Files
jdt-admin/src/views/user/index/index.vue
Huakk ef0ca7ff27
All checks were successful
continuous-integration/drone/push Build is passing
feat(custom): \!
2024-09-06 21:05:05 +08:00

861 lines
21 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<CommonPage show-footer :title="$route.title">
<n-grid class="mb-10" x-gap="12">
<n-gi :span="24">
<div flex>
<!-- <n-card w-250>
<n-statistic label="用户总赢积分" tabular-nums>
<n-number-animation ref="numberAnimationInstRef" :from="0" :to="cardData.win" />
</n-statistic>
</n-card> -->
<n-card w-250>
<n-statistic label="用户积分(留存)" tabular-nums>
<n-number-animation ref="numberAnimationInstRef" :from="0" :to="cardData.integral" />
</n-statistic>
</n-card>
<!-- <n-card ml-10 w-250>
<n-statistic label="总豆子" tabular-nums>
<n-number-animation ref="numberAnimationInstRef" :from="0" :to="cardData.pulse" />
</n-statistic>
</n-card> -->
<n-card ml-10 w-250>
<n-statistic label="今日新增用户" tabular-nums>
<n-number-animation
ref="numberAnimationInstRef"
:from="0"
:to="cardData.today_user"
/>
</n-statistic>
</n-card>
<!-- <n-card ml-10 w-250>
<n-statistic label="用户总流水(元)" tabular-nums>
<n-number-animation
ref="numberAnimationInstRef"
:from="0"
:to="cardData.total_number"
:precision="2"
/>
</n-statistic>
</n-card> -->
<!-- <n-card ml-10 w-250>
<n-statistic label="总佣金(积分)" tabular-nums>
<n-number-animation
ref="numberAnimationInstRef"
:from="0"
:to="cardData.referee"
:precision="2"
/>
</n-statistic>
</n-card> -->
<n-card ml-10 w-250>
<n-statistic label="平台总用户" tabular-nums>
<n-number-animation
ref="numberAnimationInstRef"
:from="0"
:to="cardData.total_user"
/>
</n-statistic>
</n-card>
</div>
</n-gi>
<n-gi :span="12" mt-10 flex items-center>
<span w-100>筛选条件:</span>
<n-input-group>
<n-select
v-model:value="queryParams.selectKey"
:style="{ width: '20%' }"
:options="selectOptions"
placeholder="请选择"
/>
<n-input v-model:value="queryParams.word" :style="{ width: '30%' }" />
</n-input-group>
</n-gi>
<!-- <n-gi :span="24" mt-10>
<div>
<span>筛选状态</span>
<n-radio-group v-model:value="queryParams.status">
<n-radio-button
v-for="song in songs"
:key="song.value"
:value="song.value"
:label="song.label"
/>
</n-radio-group>
</div>
</n-gi>
<n-gi :span="24" mt-10>
<div>
<span>活动赠送</span>
<n-radio-group v-model:value="queryParams.type">
<n-radio-button
v-for="song in songs1"
:key="song.value"
:value="song.value"
:label="song.label"
/>
</n-radio-group>
</div>
</n-gi> -->
<n-gi :span="24">
<div mt-10 flex items-center>
<span w-100>时间筛选</span>
<n-date-picker
v-model:formatted-value="queryParams.time"
value-format="yyyy-MM-dd HH:mm:ss"
type="datetimerange"
clearable
/>
</div>
</n-gi>
<n-gi span="24" mt-10 flex items-center>
<n-button type="primary" @click="getList">查询</n-button>
<n-button ml-10 @click="clear">重置</n-button>
</n-gi>
</n-grid>
<n-data-table
:loading="loading"
:columns="columns"
:data="data"
:pagination="pagination"
:bordered="false"
remote
@update:sorter="handleSorterChange"
/>
<!-- 用户详情 -->
<n-drawer v-model:show="isDrawer" :width="1000" placement="right" :mask-closable="false">
<n-drawer-content title="用户详情" closable>
<div flex items-center>
<img rounded-full :src="nowRow.avatarUrl" width="70" alt="avatarUrl" />
<div ml-10>
<div>昵称{{ nowRow.nickName }}</div>
<div>电话{{ nowRow.phone }}</div>
</div>
</div>
<div mt-10 w-200 flex items-center justify-between text-center>
<div>
<div>用户积分</div>
<div text-red>{{ nowRow.integral }}</div>
</div>
<div>
<div>用户豆子</div>
<div text-red>{{ nowRow.pulse }}</div>
</div>
</div>
<n-tabs v-model:value="tabVal" type="line" animated @update-value="tabsChange">
<n-tab name="1" tab="活动订单"></n-tab>
<!-- <n-tab name="2" tab="积分订单"></n-tab>-->
<n-tab name="3" tab="豆子记录"></n-tab>
<n-tab name="4" tab="积分记录"></n-tab>
<n-tab name="5" tab="推广记录"></n-tab>
<n-tab name="6" tab="赠送记录"></n-tab>
<n-tab name="7" tab="投注记录"></n-tab>
</n-tabs>
<n-row gutter="12">
<n-col :span="12">
<div mt-10 flex items-center>
<span w-100>时间筛选</span>
<n-date-picker
v-model:formatted-value="queryData.time"
value-format="yyyy-MM-dd HH:mm:ss"
type="datetimerange"
clearable
/>
</div>
</n-col>
<n-col v-if="tabVal === '4'" :span="10">
<div mt-10 flex items-center>
<span w-100>条件筛选</span>
<n-select
v-model:value="queryData.selectKey"
:style="{ width: '30%' }"
:options="[
{
label: '取消订单',
value: 1,
},
{
label: '支付订单',
value: 2,
},
{
label: '商品赠送',
value: 3,
},
{
label: '游戏获取',
value: 5,
},
]"
placeholder="请选择类型"
/>
</div>
</n-col>
<n-col :span="4">
<div mt-10>
<n-button type="primary" @click="getTabsList">搜索</n-button>
<n-button ml-10 @click="tabsClear">重置</n-button>
</div>
</n-col>
</n-row>
<n-data-table
class="mt-5"
:columns="tabsColumns"
:loading="tabsLoading"
:data="tabsData"
:pagination="tabsPagination"
:bordered="false"
remote
/>
</n-drawer-content>
</n-drawer>
<!-- 赠送 -->
<n-modal v-model:show="showModal">
<n-card
style="width: 600px"
title="豆子赠送"
:bordered="false"
size="huge"
role="dialog"
aria-modal="true"
>
<n-form ref="formRef" :model="model" :rules="rules" label-placement="left">
<n-grid :cols="24" :x-gap="24">
<n-form-item-gi :span="24" label="豆子" path="inputValue">
<n-input-number
v-model:value="model.Number"
:default-value="0"
:min="0"
:step="100"
/>
</n-form-item-gi>
<n-form-item-gi v-perms="['/admin/gift/pulse']" :span="24">
<n-button type="primary" @click="ok">提交</n-button>
<n-button ml-10 @click="addClear">取消</n-button>
</n-form-item-gi>
</n-grid>
</n-form>
</n-card>
</n-modal>
</CommonPage>
</template>
<script setup>
import { h } from 'vue'
import api from './api'
import { NDropdown, NButton, NSwitch } from 'naive-ui'
import TheIcon from '@/components/icon/TheIcon.vue'
const loading = ref(false)
const queryParams = ref({
word: '',
selectKey: null,
status: '',
time: null,
type: '',
})
const formRef = ref(null)
const nowUserRow = ref({})
const showModal = ref(false)
const model = ref({})
const rules = {}
const queryData = ref({
time: null,
selectKey: 2,
})
const cardData = ref({
integral: 0,
pulse: 0,
today_user: 0,
total_number: 0,
total_user: 0,
win: 0,
})
const selectOptions = [
{
label: '用户昵称',
value: 0,
},
{
label: '用户电话',
value: 1,
},
]
const isDrawer = ref(false)
const columns = ref([
{
title: 'ID',
align: 'center',
key: 'ID',
},
{
title: '昵称',
align: 'center',
key: 'nickName',
},
{
title: '头像',
align: 'center',
slot: 'avatar',
render(row) {
return h('img', {
src: row.avatarUrl,
style: {
width: '30px',
height: '30px',
borderRadius: '50%',
},
})
},
},
{
title: '电话',
align: 'center',
key: 'phone',
},
{
title: '用户积分',
align: 'center',
key: 'integral',
sorter: true,
sortOrder: false,
},
// {
// title: '赢积分',
// align: 'center',
// key: 'win',
// sorter: true,
// sortOrder: false,
// },
// {
// title: '用户豆子',
// align: 'center',
// key: 'pulse',
// sorter: true,
// sortOrder: false,
// },
{
title: '用户状态',
align: 'center',
slot: 'status',
render: (row) => {
return h(
NSwitch,
{
value: row.status,
checkedValue: 1,
uncheckedValue: 2,
onUpdateValue: async (value) => {
await api.updateUserStatus({
uid: row.uid,
status: value,
})
await getList()
},
},
{}
)
},
},
{
title: '操作',
align: 'center',
slot: 'action',
render(row) {
return [
h(
NDropdown,
{
trigger: 'click',
options: [
{
label: '用户详情',
key: 1,
},
{
label: '豆子赠送',
key: 2,
},
],
onSelect: (key) => {
nowUserRow.value = { ...row }
switch (key) {
case 1:
openDrawer(nowUserRow.value)
break
case 2:
showModal.value = true
break
}
},
},
{
default: () =>
h(
NButton,
{
text: true,
iconPlacement: 'right',
},
{
default: () => '更多',
icon: () =>
h(TheIcon, {
icon: 'ant-design:down-outlined',
}),
}
),
}
),
]
},
},
])
const data = ref([])
const pagination = ref({
page: 1,
pageSize: 10,
itemCount: 0,
onChange: (page) => {
pagination.value.page = page
getList()
},
onUpdatePageSize: (pageSize) => {
pagination.value.pageSize = pageSize
pagination.value.page = 1
getList()
},
})
onMounted(() => {
getList()
})
const clear = () => {
queryParams.value = {
word: '',
selectKey: null,
status: '',
time: null,
type: '',
}
getList()
}
const getList = async () => {
loading.value = true
try {
const query_data = {
Status: queryParams.value.status,
Type: queryParams.value.type,
StartTime: queryParams.value.time === null ? '' : queryParams.value.time[0] || '',
EndTime: queryParams.value.time === null ? '' : queryParams.value.time[1] || '',
}
switch (queryParams.value.selectKey) {
case 0:
query_data['UserName'] = queryParams.value.word
break
case 1:
query_data['Phone'] = queryParams.value.word
break
}
const res = await api.getUser({
pageNum: pagination.value.page,
pageSize: pagination.value.pageSize,
...query_data,
})
data.value = res.data.data || []
pagination.value.itemCount = res.data.total_user
cardData.value.today_user = res.data.today_user
cardData.value.total_user = res.data.total_user
cardData.value.total_number = res.data.total_number
cardData.value.integral = res.data.integral
cardData.value.pulse = res.data.total_userPulse
cardData.value.referee = res.data.referee
cardData.value.win = res.data.total_userWin
} catch (error) {
$message.error(error.msg)
}
loading.value = false
}
const nowRow = ref({})
const tabVal = ref('1')
const tabsLoading = ref(false)
const tabsColumns = ref([])
const tabsData = ref([])
const tabsPagination = ref({
page: 1,
pageSize: 10,
itemCount: 0,
onChange: (page) => {
tabsPagination.value.page = page
getTabsList()
},
})
const openDrawer = (row) => {
nowRow.value = row
isDrawer.value = true
// getTabsList()
tabsChange()
}
const tabsChange = async (e = '1') => {
tabVal.value = e
tabsData.value = []
tabsColumns.value = []
if (tabVal.value === '1' || tabVal.value === '2') {
tabsColumns.value = [
{
title: '订单号',
align: 'center',
key: 'oid',
},
// {
// title: '商品封面',
// align: 'center',
// slot: 'cover',
// render: (row) => {
// return h('img', {
// src: row.cover,
// style: {
// width: '50px',
// height: '50px',
// },
// })
// },
// },
{
title: '商品名称',
align: 'center',
slot: 'goods_name',
render: (row) => {
const el = []
row.OrderGoods.forEach((item) => {
el.push(
h(
'div',
{},
{
default: () =>
`${item.Goods.name}|${item.pay_price}元或${item.pay_integral}积分|X${item.number}`,
}
)
)
})
return el
},
},
{
title: '订单总价',
align: 'center',
slot: 'number',
render: (row) => h('span', row.pay_type === 1 ? `${row.price}` : `${row.exchange}积分`),
},
{
title: '支付方式',
align: 'center',
slot: 'pay_type',
render: (row) => h('span', row.pay_type === 1 ? '微信' : '积分'),
},
{
title: '订单状态',
align: 'center',
slot: 'status',
render: (row) => {
let nameStr = ''
switch (row.status) {
case 0:
nameStr = '待付款'
break
case 1:
nameStr = '待使用'
break
case 2:
nameStr = '已使用'
break
case 3:
nameStr = '已过期'
}
return h(
'span',
{},
{
default: () => nameStr,
}
)
},
},
{
title: '下单时间',
align: 'center',
key: 'add_time',
},
]
} else if (tabVal.value === '3') {
tabsColumns.value = [
{
title: '订单号',
align: 'center',
key: 'oid',
},
{
title: '获得豆子',
align: 'center',
key: 'number',
},
{
title: '下单时间',
align: 'center',
key: 'add_time',
},
]
} else if (tabVal.value === '4') {
tabsColumns.value = [
{
title: '订单号',
align: 'center',
key: 'oid',
},
{
title: '积分',
align: 'center',
key: 'number',
},
{
title: '时间',
align: 'center',
key: 'add_time',
},
]
} else if (tabVal.value === '5') {
tabsColumns.value = [
{
title: '订单号',
align: 'center',
key: 'oid',
},
{
title: '用户昵称',
align: 'center',
key: 'nick_name',
},
{
title: '获得积分',
align: 'center',
key: 'number',
},
{
title: '时间',
align: 'center',
key: 'add_time',
},
]
} else if (tabVal.value === '6') {
tabsColumns.value = [
{
title: '获取豆子',
align: 'center',
key: 'number',
},
{
title: '类型',
align: 'center',
slot: 'type',
render: (row) => {
let nameStr = ''
switch (row.type) {
case 5:
nameStr = '注册赠送'
break
case 6:
nameStr = '签到赠送'
break
case 7:
nameStr = '主动赠送'
break
}
return h(
'span',
{},
{
default: () => nameStr,
}
)
},
},
{
title: '获得时间',
align: 'center',
key: 'add_time',
},
]
} else if (tabVal.value === '7') {
tabsColumns.value = [
// {
// title: '昵称',
// key: 'User',
// align: 'center',
// },
// {
// title: '电话',
// key: 'Phone',
// align: 'center',
// },
{
title: '期数',
key: 'Periods',
align: 'center',
},
{
title: '下注豆子',
key: 'TotalCount',
align: 'center',
sorter: true,
sortOrder: false,
},
{
title: '赢积分',
key: 'NumberSum',
align: 'center',
sorter: true,
sortOrder: false,
},
{
title: '购买号码',
key: 'PeriodsNum',
align: 'center',
},
{
title: '时间',
key: 'DrawTime',
align: 'center',
},
]
}
tabsPagination.value.page = 1
await getTabsList()
}
const getTabsList = async () => {
tabsLoading.value = true
let res
const data = {
uid: nowRow.value.uid,
pageNum: tabsPagination.value.page,
pageSize: tabsPagination.value.pageSize,
StartTime: queryData.value.time === null ? '' : queryData.value.time[0] || '',
EndTime: queryData.value.time === null ? '' : queryData.value.time[1] || '',
}
switch (tabVal.value) {
case '1':
res = await api.gethdlist(data)
break
case '2':
res = await api.getjflist(data)
break
case '3':
res = await api.getdzJllist(data)
break
case '4':
data['Type'] = queryData.value.selectKey
res = await api.getjfJllist(data)
break
case '5':
res = await api.gettgJllist(data)
break
case '6':
res = await api.getgiftJllist(data)
break
case '7':
// const betData = {
// Uid: nowRow.value.uid,
// PageNum: tabsPagination.value.page,
// PageSize: tabsPagination.value.pageSize,
// StartTime: queryData.value.time === null ? '' : queryData.value.time[0] || '',
// EndTime: queryData.value.time === null ? '' : queryData.value.time[1] || '',
// }
res = await api.getBetlist(data)
break
}
tabsData.value = res.data.data || []
tabsPagination.value.itemCount = res.data.total
tabsLoading.value = false
}
const handleSorterChange = (sorter) => {
if (!loading.value) {
loading.value = true
switch (sorter.columnKey) {
case 'integral':
columns.value[4].sortOrder = !sorter ? false : sorter.order
data.value = data.value.sort((a, b) => {
if (sorter.order === 'descend') return b.integral - a.integral
return a.integral - b.integral
})
break
case 'win':
columns.value[5].sortOrder = !sorter ? false : sorter.order
data.value = data.value.sort((a, b) => {
if (sorter.order === 'descend') return b.win - a.win
return a.win - b.win
})
break
case 'pulse':
columns.value[6].sortOrder = !sorter ? false : sorter.order
data.value = data.value.sort((a, b) => {
if (sorter.order === 'descend') return b.pulse - a.pulse
return a.pulse - b.pulse
})
break
}
loading.value = false
}
}
const tabsClear = async () => {
queryData.value = {
time: null,
selectKey: null,
}
await getTabsList()
}
const ok = () => {
formRef.value?.validate(async (errors) => {
if (!errors) {
await api.addUserPulse({
Uid: nowUserRow.value.uid,
...model.value,
})
addClear()
$message.success('提交成功')
}
})
}
const addClear = () => {
model.value = {
Number: 0,
}
showModal.value = false
getList()
}
</script>
<style lang="scss" scoped></style>