This commit is contained in:
yziiy
2026-01-22 18:49:36 +08:00
parent 062f1c5dd4
commit aa5328d784
6 changed files with 316 additions and 154 deletions

View File

@@ -128,4 +128,12 @@ export const bandFamilyUser = data => {
return http.request<Result>("post", "/adminapi/User/cancel_contract", {
data
});
}
// 用户关系列表
export const userRelationList = params => {
return http.request<Result>(
"get",
"/adminapi/User/user_relation_list",
{ params }
);
}

View File

@@ -308,4 +308,12 @@ export const getSingeSongList = params => {
"/adminapi/SingerSong/songList",
{ params }
);
};
// 获取房间主持列表
export const getRoomHostList = params => {
return http.request<Result>(
"get",
"/adminapi/Room/room_host_list",
{ params }
);
};

View File

@@ -1,7 +1,7 @@
export const URL = "https://yushengapi.qxyushen.top";
// export const URL = "https://yushengapi.qxyushen.top";
// 预测版
// export const URL = "https://vsyusheng.qxhs.xyz";
// 测试
// export const URL = "https://test.vespa.qxyushen.top";
export const URL = "https://test.vespa.qxyushen.top";
// 声网appId 在这里换
export const appIdBySw = "02f7339ec98947deaeab173599891932";

View File

@@ -1,8 +1,8 @@
<script setup lang="ts">
import { ref, onMounted, h } from "vue";
import ExportForm from '@/components/exportDialog/index.vue';
import ExportForm from "@/components/exportDialog/index.vue";
import { utils, writeFile } from "xlsx";
const exportFormRef = ref(null)
const exportFormRef = ref(null);
import { addDialog } from "@/components/ReDialog";
import { message } from "@/utils/message";
const props = defineProps(["userInfo"]);
@@ -10,34 +10,39 @@ import {
userLogData,
userPhotoAlbum,
familyListData,
bandFamilyUser
bandFamilyUser,
userRelationList
} from "@/api/modules/newuserList";
const userData = ref({ ...props.userInfo.user_info, ...props.userInfo.follow_num, userId: props.userInfo.userId })
const activeIndex = ref("1")
const userData = ref({
...props.userInfo.user_info,
...props.userInfo.follow_num,
userId: props.userInfo.userId
});
const activeIndex = ref("1");
const basicLable = ref([
{ label: 'ID', prop: 'user_code' },
{ label: '年龄', prop: 'age' },
{ label: '手机号码', prop: 'mobile' },
{ label: '个人简介', prop: 'profile' },
{ label: '性别', prop: 'sex' },
{ label: '标签', prop: 'tag' },
{ label: '生日', prop: 'birthday' },
{ label: '注册时间', prop: 'createtime' },
{ label: '用户来源', prop: 'system' },
{ label: '师傅(关系)', prop: 'teacher_nickname' }
])
{ label: "ID", prop: "user_code" },
{ label: "年龄", prop: "age" },
{ label: "手机号码", prop: "mobile" },
{ label: "个人简介", prop: "profile" },
{ label: "性别", prop: "sex" },
{ label: "标签", prop: "tag" },
{ label: "生日", prop: "birthday" },
{ label: "注册时间", prop: "createtime" },
{ label: "用户来源", prop: "system" },
{ label: "师傅(关系)", prop: "teacher_nickname" }
]);
const statisticsLable = ref([
{ label: '充值金额', prop: 'user_recharge_all_money' },
{ label: '金币余额', prop: 'coin' },
{ label: '钻石余额', prop: 'earnings' },
{ label: '关注', prop: 'follow_num' },
{ label: '粉丝', prop: 'fans_num' },
{ label: '邀请好友', prop: 'invited_num' },
{ label: '获得收益', prop: 'invited_earnings' },
{ label: '是否实名', prop: 'is_real' },
{ label: '真实姓名', prop: 'real_name' },
{ label: '身份证号', prop: 'card_id' }
])
{ label: "充值金额", prop: "user_recharge_all_money" },
{ label: "金币余额", prop: "coin" },
{ label: "钻石余额", prop: "earnings" },
{ label: "关注", prop: "follow_num" },
{ label: "粉丝", prop: "fans_num" },
{ label: "邀请好友", prop: "invited_num" },
{ label: "获得收益", prop: "invited_earnings" },
{ label: "是否实名", prop: "is_real" },
{ label: "真实姓名", prop: "real_name" },
{ label: "身份证号", prop: "card_id" }
]);
// 家族成员
const familyColumns = ref([
{
@@ -73,37 +78,95 @@ const familyColumns = ref([
fixed: "right",
slot: "operation"
}
])
const tableList = ref([])
const getLogData = async (type) => {
]);
// 用户关系列表
const relationColumns = ref([
{
label: "用户1",
prop: "nickname1"
},
{
label: "用户2",
prop: "nickname2"
},
{
label: "关系名称",
prop: "relation_name"
},
{
label: "添加时间",
prop: "add_time"
},
{
label: "图片",
prop: "image",
slot: "image"
},
{
label: "CP房间ID",
prop: "cp_room_id"
}
]);
const relationTableList = ref([]);
const relationPagination = ref({
total: 0,
pageSize: 10,
currentPage: 1,
background: true
});
const tableList = ref([]);
const getLogData = async type => {
const { data, code } = await userLogData({
user_id: userData.value.userId, type: type, page: pagination.value.currentPage, page_limit: pagination.value.pageSize
})
user_id: userData.value.userId,
type: type,
page: pagination.value.currentPage,
page_limit: pagination.value.pageSize
});
if (code) {
tableList.value = data.lists
pagination.value.total = data.count
pagination.value.pageSize = +data.page_limit
pagination.value.currentPage = +data.page
tableList.value = data.lists;
pagination.value.total = data.count;
pagination.value.pageSize = +data.page_limit;
pagination.value.currentPage = +data.page;
}
}
};
const getFamilyData = async () => {
pagination.value.currentPage = 1
pagination.value.pageSize = 10
tableList.value = []
pagination.value.currentPage = 1;
pagination.value.pageSize = 10;
tableList.value = [];
const { data, code } = await familyListData({
user_id: userData.value.userId, page: pagination.value.currentPage, page_limit: pagination.value.pageSize
})
user_id: userData.value.userId,
page: pagination.value.currentPage,
page_limit: pagination.value.pageSize
});
if (code) {
tableList.value = data.lists
pagination.value.total = data.count
pagination.value.pageSize = +data.page_limit
pagination.value.currentPage = +data.page
tableList.value = data.lists;
pagination.value.total = data.count;
pagination.value.pageSize = +data.page_limit;
pagination.value.currentPage = +data.page;
}
}
};
const getRelationData = async () => {
relationPagination.value.currentPage = 1;
relationPagination.value.pageSize = 10;
relationTableList.value = [];
const { data, code } = await userRelationList({
user_id: userData.value.userId,
page: relationPagination.value.currentPage,
page_limit: relationPagination.value.pageSize
});
if (code) {
relationTableList.value = data.lists || [];
relationPagination.value.total = data.count || 0;
relationPagination.value.pageSize = +data.page_limit || 10;
relationPagination.value.currentPage = +data.page || 1;
}
};
const getPhotoAlbum = async () => {
const { data, code } = await userPhotoAlbum({ user_id: userData.value.userId })
photoAlbums.value = code ? data : []
}
const { data, code } = await userPhotoAlbum({
user_id: userData.value.userId
});
photoAlbums.value = code ? data : [];
};
const pagination = ref({
total: 0,
pageSize: 10,
@@ -131,78 +194,94 @@ const dynamicColumns = ref([
label: "数量",
prop: "change_money"
}
])
]);
onMounted(() => {
getLogData(activeIndex.value == '1' ? 1 : 2)
})
const photoAlbums = ref([])
const handleClick = (tab) => {
const { name } = tab.props
activeIndex.value = name
tableList.value = []
if (['1', '2'].includes(name)) {
getLogData(activeIndex.value == '1' ? 1 : 2)
} else if (name == '3') {
getLogData(activeIndex.value === "1" ? 1 : 2);
});
const photoAlbums = ref([]);
const openAlbum = album => {
// 打开相册详情的逻辑,可以根据需求实现
console.log("打开相册:", album);
};
const handleClick = tab => {
const { name } = tab.props;
activeIndex.value = name;
tableList.value = [];
if (["1", "2"].includes(name)) {
getLogData(activeIndex.value === "1" ? 1 : 2);
} else if (name == "3") {
// 获取相册
getPhotoAlbum()
} else {
getFamilyData()
getPhotoAlbum();
} else if (name == "4") {
getFamilyData();
} else if (name == "5") {
getRelationData();
}
}
};
const handleSizeChange = (val: number) => {
pagination.value.pageSize = val;
if (['1', '2'].includes(activeIndex.value)) {
getLogData(activeIndex.value == '1' ? 1 : 2)
} else {
getFamilyData()
if (["1", "2"].includes(activeIndex.value)) {
getLogData(activeIndex.value === "1" ? 1 : 2);
} else if (activeIndex.value === "4") {
getFamilyData();
}
};
const handleCurrentChange = (val: number) => {
pagination.value.currentPage = val;
if (['1', '2'].includes(activeIndex.value)) {
getLogData(activeIndex.value == '1' ? 1 : 2)
} else {
getFamilyData()
if (["1", "2"].includes(activeIndex.value)) {
getLogData(activeIndex.value === "1" ? 1 : 2);
} else if (activeIndex.value === "4") {
getFamilyData();
}
};
//
const FamilyDelete = async (rowData) => {
const handleRelationSizeChange = (val: number) => {
relationPagination.value.pageSize = val;
getRelationData();
};
const handleRelationCurrentChange = (val: number) => {
relationPagination.value.currentPage = val;
getRelationData();
};
//
const FamilyDelete = async rowData => {
const { code } = await bandFamilyUser({ id: rowData.id });
if (code) {
message(`解绑成功`, {
type: "success"
});
getFamilyData()
getFamilyData();
}
}
const exportTable = async (activeIndex) => {
};
const exportTable = async activeIndex => {
if (tableList.value.length === 0) {
message(`暂无数据导出`, {
type: "error"
});
return
return;
}
if (['1', '2'].includes(activeIndex)) {
let exportTableList = []
if (["1", "2"].includes(activeIndex)) {
let exportTableList = [];
addDialog({
title: `导出数据`,
props: {
formInline: {
time: ''
time: ""
}
},
width: "40%",
closeOnClickModal: false,
contentRenderer: () => h(ExportForm, { ref: exportFormRef, formInline: null }),
contentRenderer: () =>
h(ExportForm, { ref: exportFormRef, formInline: null }),
beforeSure: (done, { options }) => {
const FormRef = exportFormRef.value.getRef();
const curData = options.props.formInline;
const exportData = async (formData) => {
const exportData = async formData => {
const { data, code } = await userLogData({
user_id: userData.value.userId, type: activeIndex.value == '1' ? 1 : 2, page: 1, page_limit: 10000
})
user_id: userData.value.userId,
type: activeIndex.value == "1" ? 1 : 2,
page: 1,
page_limit: 10000
});
if (code) {
exportTableList = data.lists;
const res = exportTableList.map(item => {
@@ -217,35 +296,43 @@ const exportTable = async (activeIndex) => {
titleList.push(column.label);
});
res.unshift(titleList);
console.log(titleList)
console.log(titleList);
const workSheet = utils.aoa_to_sheet(res);
const workBook = utils.book_new();
utils.book_append_sheet(workBook, workSheet, "数据报表");
writeFile(workBook, `${activeIndex.value == '1' ? '金币' : '钻石'}统计——${formData.start_time} - ${formData.end_time}.xlsx`);
writeFile(
workBook,
`${activeIndex.value == "1" ? "金币" : "钻石"}统计——${formData.start_time} - ${formData.end_time}.xlsx`
);
message("导出成功", {
type: "success"
});
done()
done();
} else {
message("获取数据失败,请重试!", {
type: "error"
});
}
}
};
FormRef.validate(valid => {
if (valid) {
if (curData.time && curData.time.length) {
exportData({ start_time: curData.time[0] || '', end_time: curData.time[1] || '' })
exportData({
start_time: curData.time[0] || "",
end_time: curData.time[1] || ""
});
}
}
});
}
});
} else {
let exportTableList = []
let exportTableList = [];
const { data, code } = await familyListData({
user_id: userData.value.userId, page: 1, page_limit: 10000
})
user_id: userData.value.userId,
page: 1,
page_limit: 10000
});
if (code) {
exportTableList = data.lists;
const res = exportTableList.map(item => {
@@ -273,102 +360,106 @@ const exportTable = async (activeIndex) => {
});
}
}
}
};
</script>
<template>
<div class="viewPage">
<div class="basicView">
<div class="left-view">
<el-image style="width: 150px; height: 150px" :src="userData.avatar" fit="cover"></el-image>
<el-image style="width: 150px; height: 150px" :src="userData.avatar" fit="cover" />
</div>
<div>
<el-descriptions title="" border :column="2">
<el-descriptions-item :label="item.label" v-for="item in basicLable">{{
userData[item.prop] }}</el-descriptions-item>
<el-descriptions-item v-for="item in basicLable" :label="item.label">{{ userData[item.prop]
}}</el-descriptions-item>
</el-descriptions>
</div>
</div>
<div style="margin-top: 20px;">
<div style="margin-top: 20px">
<el-descriptions title="统计信息" border :column="5" direction="vertical">
<el-descriptions-item :label="item.label" v-for="item in statisticsLable">{{
userData[item.prop] }}</el-descriptions-item>
<el-descriptions-item v-for="item in statisticsLable" :label="item.label">{{ userData[item.prop]
}}</el-descriptions-item>
</el-descriptions>
</div>
<div style="margin-top: 20px;">
<div style="margin-top: 20px">
<el-tabs v-model="activeIndex" @tab-click="handleClick">
<el-tab-pane label="金币收支" name="1">
<div class="mb-5" style="float: right;">
<el-button @click="exportTable('1')" type="primary">导出当前表格</el-button>
<div class="mb-5" style="float: right">
<el-button type="primary" @click="exportTable('1')">导出当前表格</el-button>
</div>
<pure-table v-if="activeIndex == 1" ref="tableRef" align-whole="center" showOverflowTooltip
<pure-table v-if="activeIndex === '1'" ref="tableRef" align-whole="center" showOverflowTooltip
table-layout="auto" :adaptiveConfig="{ offsetBottom: 108 }" :data="tableList" :columns="dynamicColumns"
:pagination="{ ...pagination }" @page-size-change="handleSizeChange"
@page-current-change="handleCurrentChange" :header-cell-style="{
background: 'var(--el-fill-color-light)',
color: 'var(--el-text-color-primary)'
}">
</pure-table>
</el-tab-pane>
<el-tab-pane label="钻石收支" name="2">
<div class="mb-5" style="float: right;">
<el-button @click="exportTable('2')" type="primary">导出当前表格</el-button>
</div>
<pure-table v-if="activeIndex == 2" ref="tableRef" align-whole="center" showOverflowTooltip
table-layout="auto" :adaptiveConfig="{ offsetBottom: 108 }" @page-size-change="handleSizeChange"
@page-current-change="handleCurrentChange" :data="tableList" :columns="dynamicColumns"
:pagination="{ ...pagination }" :header-cell-style="{
background: 'var(--el-fill-color-light)',
color: 'var(--el-text-color-primary)'
}">
</pure-table>
}" @page-size-change="handleSizeChange" @page-current-change="handleCurrentChange" />
</el-tab-pane>
<el-tab-pane label="钻石收支" name="2">
<div class="mb-5" style="float: right">
<el-button type="primary" @click="exportTable('2')">导出当前表格</el-button>
</div>
<pure-table v-if="activeIndex === '2'" ref="tableRef" align-whole="center" showOverflowTooltip
table-layout="auto" :adaptiveConfig="{ offsetBottom: 108 }" :data="tableList" :columns="dynamicColumns"
:pagination="{ ...pagination }" :header-cell-style="{
background: 'var(--el-fill-color-light)',
color: 'var(--el-text-color-primary)'
}" @page-size-change="handleSizeChange" @page-current-change="handleCurrentChange" />
</el-tab-pane>
<el-tab-pane label="ta的相册" name="3">
<template v-if="activeIndex == 3">
<template v-if="activeIndex === '3'">
<div v-if="photoAlbums.length > 0" class="albums-container">
<div v-for="(album, index) in photoAlbums" :key="index" class="album-card" @click="openAlbum(album)">
<img :src="album.image" class="album-cover" alt="Album cover">
<img :src="album.image" class="album-cover" alt="Album cover" />
<div class="album-info">
<div class="album-title">{{ album.name }}</div>
<div class="album-stats">
<span><i class="fas fa-images"></i> {{ album.img_num }} 张照片</span>
<span><i class="fas fa-images" />
{{ album.img_num }} 张照片</span>
</div>
</div>
</div>
</div>
<div v-else class="empty-album">
<i class="fas fa-images"></i>
<i class="fas fa-images" />
<h3>暂无相册</h3>
<p>该用户还没有创建任何相册</p>
</div>
</template>
</el-tab-pane>
<el-tab-pane label="家族成员" name="4">
<div class="mb-5" style="float: right;">
<el-button @click="exportTable('4')" type="primary">导出当前表格</el-button>
<div class="mb-5" style="float: right">
<el-button type="primary" @click="exportTable('4')">导出当前表格</el-button>
</div>
<pure-table v-if="activeIndex == 4" ref="tableRef" align-whole="center" showOverflowTooltip
table-layout="auto" :adaptiveConfig="{ offsetBottom: 108 }" @page-size-change="handleSizeChange"
@page-current-change="handleCurrentChange" :data="tableList" :columns="familyColumns"
<pure-table v-if="activeIndex === '4'" ref="tableRef" align-whole="center" showOverflowTooltip
table-layout="auto" :adaptiveConfig="{ offsetBottom: 108 }" :data="tableList" :columns="familyColumns"
:pagination="{ ...pagination }" :header-cell-style="{
background: 'var(--el-fill-color-light)',
color: 'var(--el-text-color-primary)'
}">
}" @page-size-change="handleSizeChange" @page-current-change="handleCurrentChange">
<template #operation="{ row }">
<el-popconfirm :title="`是否解绑${row.nickname}的家族关系`" @confirm="FamilyDelete(row)">
<template #reference>
<el-button link type="primary">
解绑
</el-button>
<el-button link type="primary"> 解绑 </el-button>
</template>
</el-popconfirm>
</template>
</pure-table>
</el-tab-pane>
<el-tab-pane label="用户关系" name="5">
<pure-table v-if="activeIndex === '5'" ref="tableRef" align-whole="center" showOverflowTooltip
table-layout="auto" :adaptiveConfig="{ offsetBottom: 108 }" :data="relationTableList"
:columns="relationColumns" :pagination="{ ...relationPagination }" :header-cell-style="{
background: 'var(--el-fill-color-light)',
color: 'var(--el-text-color-primary)'
}" @page-size-change="handleRelationSizeChange" @page-current-change="handleRelationCurrentChange">
<template #image="{ row }">
<el-image v-if="row.image" style="width: 120px; height: 30px" :src="row.image" fit="cover"
:preview-src-list="[row.image]" />
<span v-else>-</span>
</template>
</pure-table>
</el-tab-pane>
</el-tabs>
</div>
</div>
</template>
@@ -439,7 +530,7 @@ const exportTable = async (activeIndex) => {
.album-stats i {
margin-right: 5px;
color: #409EFF;
color: #409eff;
}
.empty-album {
@@ -463,4 +554,4 @@ const exportTable = async (activeIndex) => {
.empty-album p {
font-size: 16px;
}
</style>
</style>

View File

@@ -7,7 +7,8 @@ import dayjs from "dayjs";
import {
getRoomDetail,
getRoomWaterFlow,
getRoomEnterByUser
getRoomEnterByUser,
getRoomHostList
} from "@/api/modules/room";
const props = defineProps(["tableData"]);
const dataBytable = ref({ ...props.tableData });
@@ -96,7 +97,7 @@ const getAllData = async () => {
: "关闭",
is_earnings: data.is_earnings === 1 ? "是" : "否",
room_password: data.room_password === "" ? "否" : "是",
beginTime: `${data.start_time || '暂未设定'} - ${data.end_time || '暂未设定'}`
beginTime: `${data.start_time || "暂未设定"} - ${data.end_time || "暂未设定"}`
}
: null;
};
@@ -124,7 +125,7 @@ const getFlowData = async index => {
pagination.value.total = data.count;
pagination.value.currentPage = data.page;
}
} else {
} else if (index === 2) {
const { data, code } = await getRoomEnterByUser({
room_id: dataBytable.value.room_id,
page: pagination.value.currentPage,
@@ -135,6 +136,17 @@ const getFlowData = async index => {
pagination.value.total = data.count;
pagination.value.currentPage = data.page;
}
} else if (index === 4) {
const { data, code } = await getRoomHostList({
room_id: dataBytable.value.room_id,
page: pagination.value.currentPage,
page_limit: pagination.value.pageSize
});
if (code) {
flowTableList.value = data.lists;
pagination.value.total = data.count;
pagination.value.currentPage = data.page;
}
}
};
const getExportData = async index => {
@@ -196,6 +208,29 @@ const dynamicUserColumns = ref([
prop: "createtime"
}
]);
const dynamicHostColumns = ref([
{
label: "ID",
prop: "id"
},
{
label: "类型",
prop: "type_str"
},
{
label: "用户编号",
prop: "user_code"
},
{
label: "昵称",
prop: "nickname"
},
{
label: "头像",
prop: "avatar",
slot: "avatar"
}
]);
const activeName = ref("1");
const tagValue = ref(1);
const dateSearchValue = ref(getDefaultTimeRange());
@@ -215,19 +250,21 @@ const handleClick = tab => {
pagination.value.currentPage = 1;
flowTableList.value = [];
activeIndex.value = name;
if (["1", "2"].includes(name)) {
getFlowData(activeIndex.value == "1" ? 1 : 2);
if (["1", "2", "4"].includes(name)) {
getFlowData(
activeIndex.value == "1" ? 1 : activeIndex.value == "2" ? 2 : 4
);
} else {
console.log("点歌记录");
}
};
const handleSizeChange = (val: number) => {
pagination.value.pageSize = val;
getFlowData(activeIndex.value == "1" ? 1 : 2);
getFlowData(activeIndex.value == "1" ? 1 : activeIndex.value == "2" ? 2 : 4);
};
const handleCurrentChange = (val: number) => {
pagination.value.currentPage = val;
getFlowData(activeIndex.value == "1" ? 1 : 2);
getFlowData(activeIndex.value == "1" ? 1 : activeIndex.value == "2" ? 2 : 4);
};
const changeTime = val => {
// console.log(val)
@@ -282,7 +319,7 @@ const exportExcal = async () => {
<div style="width: 70%">
<el-descriptions title="" border :column="3">
<el-descriptions-item v-for="item in basicLable" :label="item.label">{{ roomDetail[item.prop]
}}</el-descriptions-item>
}}</el-descriptions-item>
</el-descriptions>
</div>
</div>
@@ -340,6 +377,18 @@ const exportExcal = async () => {
<el-tab-pane label="点歌记录" name="3">
<singView :id="dataBytable.room_id" />
</el-tab-pane>
<el-tab-pane label="房间主持列表" name="4">
<pure-table ref="tableRef" class="mt-5" align-whole="center" showOverflowTooltip table-layout="auto"
default-expand-all row-key="id" :adaptiveConfig="{ offsetBottom: 108 }" :data="flowTableList"
:columns="dynamicHostColumns" :pagination="{ ...pagination }" :header-cell-style="{
background: 'var(--el-fill-color-light)',
color: 'var(--el-text-color-primary)'
}" @page-current-change="handleCurrentChange" @page-size-change="handleSizeChange">
<template #avatar="{ row }">
<el-image style="width: 30px; height: 30px" :src="row.avatar" fit="cover" />
</template>
</pure-table>
</el-tab-pane>
</el-tabs>
</div>
</div>

View File

@@ -39,8 +39,14 @@ export default ({ mode }: ConfigEnv): UserConfigExport => {
// "Access-Control-Allow-Origin",
// "http://adminvs.qxhs.xyz"
// );
res.setHeader('Access-Control-Allow-Origin', 'http://yushenggliht.qxyushen.top');
// res.setHeader('Access-Control-Allow-Origin', 'https://test.vespa.qxyushen.top');
// res.setHeader(
// "Access-Control-Allow-Origin",
// "http://yushenggliht.qxyushen.top"
// );
res.setHeader(
"Access-Control-Allow-Origin",
"https://test.vespa.qxyushen.top"
);
res.setHeader(
"Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS"