This commit is contained in:
lingling 2025-02-19 18:12:53 +08:00
commit 1e07d77df0
15 changed files with 722 additions and 88 deletions

62
src/api/Invite.ts Normal file
View File

@ -0,0 +1,62 @@
import axios from 'axios';
import { S } from 'mockjs';
export interface LoginData {
username: string;
password: string;
}
export interface jobuser {
status: string;
code: number;
message: string;
data: {
jobuser: {
username: string; // 账号
password: string; // 密码
time: Date; // 注册时间
type: number; // 累计在线时间
}[]
};
}
export interface Black {
status: string;
code: number;
message: string;
data: Array<{}>;
}
export interface Refresh {
status: string;
code: number;
data: {
access_token: string;
refresh_token: string;
};
message: string;
}
export interface ListParams {
black?: string;
username?: string;
}
/**
*
* @param data
* @returns
*/
export function getSonId(id: string) {
return axios.post<Black>('/api/v1/Invite/getSonId', { id });
}
/**
*
* @param data
* @returns
*/
export function getDayId(id: string) {
return axios.post<Black>('/api/v1/Invite/getDayId', { id });
}

64
src/api/withdraw.ts Normal file
View File

@ -0,0 +1,64 @@
import axios from 'axios';
import { S } from 'mockjs';
export interface LoginData {
username: string;
password: string;
}
export interface jobuser {
status: string;
code: number;
message: string;
data: object;
}
export interface Black {
status: string;
code: number;
message: string;
data: {};
}
export interface Refresh {
status: string;
code: number;
data: {
access_token: string;
refresh_token: string;
};
message: string;
}
export interface ListParams {
black?: string;
username?: string;
}
/**
*
* @param data
* @returns
*/
export function lists(params: ListParams) {
return axios.post<jobuser>('/api/v1/withdraw/lists',params);
}
// /**
// *
// * @param id 拉黑
// * @returns
// */
// export function getBlack(id: string) {
// return axios.post<Black>('/api/v1/jobuser/getBlack', { id });
// }
// /**
// *
// * @param id 洗白
// * @returns
// */
// export function getOutBlack(id: string) {
// return axios.post<Black>('/api/v1/jobuser/getOutBlack', { id });
// }

View File

@ -36,6 +36,8 @@ import localeJobuser from '@/views/jobuser/locale/zh-CN';
import localeJUserblack from '@/views/userblack/locale/zh-CN';
import localeDictionary from '@/views/dictionary/locale/zh-CN';
import localeWithdraw from '@/views/withdraw/locale/zh-CN';
export default {
'menu.dashboard': '仪表盘',
'menu.server.dashboard': '仪表盘-服务端',
@ -58,6 +60,7 @@ export default {
'menu.volunteerservicecorps':'志愿队',
'menu.jobuser':'用户',
"menu.dictionary":'客服',
"menu.withdraw": '提现管理',
...localeSettings,
...localeMessageBox,
...localeLogin,
@ -84,5 +87,6 @@ export default {
...localevolunteerservicecorps,
...localeJobuser,
...localeJUserblack,
...localeDictionary
...localeDictionary,
...localeWithdraw
};

View File

@ -0,0 +1,28 @@
import { DEFAULT_LAYOUT } from '../base';
import { AppRouteRecordRaw } from '../types';
const JOBUSER: AppRouteRecordRaw = {
path: '/withdraw',
name: 'withdraw',
component: DEFAULT_LAYOUT,
meta: {
locale: 'menu.withdraw',
requiresAuth: true,
icon: 'icon-user',
order: 0,
},
children: [
{
path: 'withdraws',
name: 'withdraws',
component: () => import('@/views/withdraw/index.vue'),
meta: {
locale: 'menu.withdraw.list',
requiresAuth: true,
roles: ['*'],
},
},
],
};
export default JOBUSER;

View File

@ -10,8 +10,8 @@
100 积分 = 5 PKR -->
<h2 style="text-align: center;">100积分</h2>
<a-form :model="form" auto-label-width @submit="handleSubmit">
<a-form-item field="USDT" label="USDT">
<a-input v-model="form.USDT" placeholder="请输入对应的USDT汇率" />
<a-form-item field="BDT" label="BDT">
<a-input v-model="form.USDT" placeholder="请输入对应的BDT汇率" />
</a-form-item>
<a-form-item field="NGN" label="NGN">
<a-input v-model="form.NGN" placeholder="请输入对应的NGN汇率" />

View File

@ -1,87 +1,85 @@
<template>
<div class="container">
<Breadcrumb :items="['menu.dashboard', 'menu.dashboard.monitor']" />
<div class="layout">
<div class="layout-left-side">
<ChatPanel />
</div>
<div class="layout-content">
<a-space :size="16" direction="vertical" fill>
<Studio />
<DataStatistic />
</a-space>
</div>
<div class="layout-right-side">
<a-space :size="16" direction="vertical" fill>
<StudioStatus />
<QuickOperation />
<StudioInformation />
</a-space>
</div>
</div>
</div>
<div ref="chartContainer" style="width: 100%; height: 600px;"></div>
</template>
<script lang="ts" setup>
import ChatPanel from './components/chat-panel.vue';
import Studio from './components/studio.vue';
import DataStatistic from './components/data-statistic.vue';
import StudioStatus from './components/studio-status.vue';
import QuickOperation from './components/quick-operation.vue';
import StudioInformation from './components/studio-information.vue';
<script>
import { defineComponent, ref, onMounted } from "vue";
import * as echarts from "echarts";
export default defineComponent({
name: "FamilyTreeChart",
setup() {
const chartContainer = ref(null);
//
const chartOption = {
tooltip: {
trigger: 'item',
formatter: '{b}'
},
series: [
{
type: 'tree', // 使
layout: 'tree', // 'radial' 'tree'
roam: true, // ,
orient: 'vertical',//
initialTreeDepth: -1, //
data: [
{
name: '祖父1',
symbolSize: 40,
children: [
{
name: '父亲',
symbolSize: 30,
children: [
{
name: '我',
symbolSize: 20,
children: [
{ name: '妻子', symbolSize: 20 }
]
}
]
},
{
name: '母亲',
symbolSize: 30,
}
]
}
],
itemStyle: {
color: '#63b1f2', //
},
label: {
position: 'top',
verticalAlign: 'middle',
align: 'center',
fontSize: 12,
},
lineStyle: {
color: '#aaa',
width: 2,
curveness: 0.3,
}
}
]
};
//
onMounted(() => {
const myChart = echarts.init(chartContainer.value);
myChart.setOption(chartOption);
});
return {
chartContainer, // chartContainer
};
}
});
</script>
<script lang="ts">
export default {
name: 'Monitor',
};
</script>
<style scoped lang="less">
.container {
padding: 0 20px 20px 20px;
}
.layout {
display: flex;
&-left-side {
flex-basis: 300px;
}
&-content {
flex: 1;
padding: 0 16px;
}
&-right-side {
flex-basis: 280px;
}
}
</style>
<style lang="less" scoped>
// responsive
@media (max-width: @screen-lg) {
.layout {
flex-wrap: wrap;
&-left-side {
flex: 1;
flex-basis: 100%;
margin-bottom: 16px;
}
&-content {
flex: none;
flex-basis: 100%;
padding: 0;
order: -1;
margin-bottom: 16px;
}
&-right-side {
flex-basis: 100%;
}
}
}
<style scoped>
/* 样式可根据需要自定义 */
</style>

View File

@ -25,6 +25,9 @@
<a-form-item field="service_user_url" label="人工客服">
<a-input v-model="form.service_user_url" placeholder="请输入对应的地址" />
</a-form-item>
<a-form-item field="points" label="积分设置(大于多少人工客服)">
<a-input v-model="form.points" placeholder="请输入积分" />
</a-form-item>
<a-form-item>
<a-button html-type="submit">保存</a-button>
</a-form-item>
@ -45,7 +48,9 @@ const form = reactive({
service_2:"",
service_3:"",
service_url:"",
service_user_url:""
service_user_url:"",
points:""
});
const handleSubmit=()=>{
setAll(form)

View File

@ -0,0 +1,93 @@
<template>
<div ref="chartContainer" style="width: 800px; height: 550px;"></div>
</template>
<script lang="ts" setup>
import { defineComponent, ref, onMounted } from "vue";
import * as echarts from "echarts";
import { getSonId, getDayId } from '@/api/Invite';
import { reactive } from "vue";
const chartContainer = ref(null);
const props = defineProps({
id: Number,
});
const data = reactive( {
name: '',
symbolSize:40,
children: []
});
//
const chartOption = {
tooltip: {
trigger: 'item',
formatter: '{b}'
},
series: [
{
type: 'tree', // 使
layout: 'tree', // 'radial' 'tree'
roam: true, // ,
orient: 'vertical',//
initialTreeDepth: -1, //
data: [
data
],
itemStyle: {
color: '#63b1f2', //
},
label: {
position: 'top',
verticalAlign: 'middle',
align: 'center',
fontSize: 12,
},
lineStyle: {
color: '#aaa',
width: 2,
curveness: 0.3,
}
}
]
};
//
onMounted(() => {
getSonId(props.id).then(res => {
data.name = props.id || '';
if(res.data!=null){
res.data.forEach((element: any) => {
let item = {
name:element.invitename,
symbolSize:20,
children: []
}
getSonId(element.invitename).then(res1 => {
if(res1.data!=null){
res1.data.forEach((element1: any) => {
let item1 = {
name: element1.invitename,
symbolSize:20,
children: []
};
item.children.push(item1);
})
data.children.push(item);
const myChart = echarts.init(chartContainer.value);
myChart.setOption(chartOption);
}
})
});
}
})
});
</script>
<style scoped>
/* 样式可根据需要自定义 */
</style>

View File

@ -8,6 +8,7 @@
<a-table-column title="用户id" data-index="id"></a-table-column>
<a-table-column title="用户名称" data-index="username"></a-table-column>
<a-table-column title="用户积分" data-index="money"></a-table-column>
<a-table-column title="上级代理" data-index="agent"></a-table-column>
<a-table-column title="累计在线时长/分" data-index="time"></a-table-column>
<a-table-column title="注册时间" data-index="created_at"></a-table-column>
<a-table-column title="是否封禁" data-index="status"><template #cell="{ record }">
@ -27,12 +28,15 @@
</a-table>
</a-card>
<!-- 团队关系展示 -->
<a-modal v-model:visible="modal_visible" width="auto">
<a-modal v-model:visible="modal_visible" width="800px">
<template #title>
团队关系展示
</template>
<showgroup :id="show_group_id"></showgroup>
</a-modal>
<showgroup :id="show_group_id">
</showgroup>
<FamilyTreeChart :id="show_group_id" v-if="modal_visible"></FamilyTreeChart>
</a-modal>
</div>
</template>
@ -43,6 +47,7 @@ import { Modal } from '@arco-design/web-vue';
import { Message } from '@arco-design/web-vue';
import showgroup from './components/showgroup.vue';
import FamilyTreeChart from './components/FamilyTreeChart.vue';
interface DataItem {
id: number;

View File

@ -0,0 +1,93 @@
<template>
<div ref="chartContainer" style="width: 800px; height: 550px;"></div>
</template>
<script lang="ts" setup>
import { defineComponent, ref, onMounted } from "vue";
import * as echarts from "echarts";
import { getSonId, getDayId } from '@/api/Invite';
import { reactive } from "vue";
const chartContainer = ref(null);
const props = defineProps({
id: Number,
});
const data = reactive( {
name: '',
symbolSize:40,
children: []
});
//
const chartOption = {
tooltip: {
trigger: 'item',
formatter: '{b}'
},
series: [
{
type: 'tree', // 使
layout: 'tree', // 'radial' 'tree'
roam: true, // ,
orient: 'vertical',//
initialTreeDepth: -1, //
data: [
data
],
itemStyle: {
color: '#63b1f2', //
},
label: {
position: 'top',
verticalAlign: 'middle',
align: 'center',
fontSize: 12,
},
lineStyle: {
color: '#aaa',
width: 2,
curveness: 0.3,
}
}
]
};
//
onMounted(() => {
getSonId(props.id).then(res => {
data.name = props.id || '';
if(res.data!=null){
res.data.forEach((element: any) => {
let item = {
name:element.invitename,
symbolSize:20,
children: []
}
getSonId(element.invitename).then(res1 => {
if(res1.data!=null){
res1.data.forEach((element1: any) => {
let item1 = {
name: element1.invitename,
symbolSize:20,
children: []
};
item.children.push(item1);
})
data.children.push(item);
const myChart = echarts.init(chartContainer.value);
myChart.setOption(chartOption);
}
})
});
}
})
});
</script>
<style scoped>
/* 样式可根据需要自定义 */
</style>

View File

@ -0,0 +1,35 @@
<template>
<a-col class="banner">
<a-col :span="8">
<a-typography-title :heading="5" style="margin-top: 0">
{{ $t('workplace.welcome') }} {{ userInfo.name }}
</a-typography-title>
</a-col>
<a-divider class="panel-border" />
</a-col>
</template>
<script lang="ts" setup>
import { computed } from 'vue';
import { useUserStore } from '@/store';
const userStore = useUserStore();
const userInfo = computed(() => {
return {
name: userStore.name,
};
});
</script>
<style scoped lang="less">
.banner {
width: 100%;
padding: 20px 20px 0 20px;
background-color: var(--color-bg-2);
border-radius: 4px 4px 0 0;
}
:deep(.arco-icon-home) {
margin-right: 6px;
}
</style>

View File

@ -0,0 +1,39 @@
<template>
<div class="group">
<a-card>
父亲
</a-card>
</div>
<div class="group">
<a-card>
</a-card>
</div>
<div class="group">
<a-card>
</a-card>
<a-card>
</a-card>
</div>
<div class="group">
<a-card>
</a-card>
</div>
</template>
<script lang="ts" setup>
import { computed } from 'vue';
const props = defineProps({
id: Number,
});
</script>
<style scoped lang="less">
.group {
display: flex;
justify-content: space-between;
}
</style>

View File

@ -0,0 +1,198 @@
<template>
<div class="container">
<Breadcrumb :items="['menu.withdraw', 'menu.withdraw.list']" />
<a-card class="general-card" :title="$t('menu.withdraw.list')">
<a-table :data="data">
<template #columns>
<a-table-column title="用户id" data-index="user_id"></a-table-column>
<a-table-column title="用户名称" data-index="username"></a-table-column>
<a-table-column title="转账金额" data-index="money"></a-table-column>
<a-table-column title="状态" data-index="status" filterable="filters: [{
text: 'London',
value: 'London',
}, {
text: 'Paris',
value: 'Paris',
},],
filter: (value, row) => row.address.includes(value),"></a-table-column>
<a-table-column title="操作时间" data-index="createtime2"></a-table-column>
<a-table-column title="操作">
<template #cell="{ record }">
<a-button v-if="record.status == '申请中'" @click="showConfirm(record.id)">审批</a-button>
</template>
</a-table-column>
</template>
</a-table>
</a-card>
</div>
</template>
<script lang="ts" setup>
import { computed, ref, reactive, watch, nextTick, onMounted } from 'vue';
import { lists } from '@/api/withdraw';
import { Modal } from '@arco-design/web-vue';
import { Message } from '@arco-design/web-vue';
const columns = [
{
title: 'Name',
dataIndex: 'name',
},
{
title: 'Salary',
dataIndex: 'salary',
},
{
title: 'Address',
dataIndex: 'address',
},
{
title: 'Email',
dataIndex: 'email',
},
];
interface DataItem {
user_id: string;
username: string;
money: Date;
status: string;
createtime2: string;
}
let data = ref<DataItem[]>([])
//
const modal_visible=ref(false)
//id
const show_group_id=ref(0)
const show_group=(id:number)=>{
show_group_id.value=id
modal_visible.value=true
}
const showConfirm = (id: string) => {
Modal.confirm({
title: '操作提醒',
content: '是否同意该用户的提现申请',
okText: '同意',
cancelText: '拒绝',
onOk: () => handleBlock(id),
onCancel: () => Message.info('取消操作'),
});
};
const handleBlock = (id: string) => {
// getBlack(id).then(res => {
// console.log(res.status)
// if (res.status == 'success') {
// getAll();
// Message.success('')
// } else {
// Message.error('')
// }
// })
};
const queryParams = ref({
black: null,
username: null
});
const getAll = () => {
lists(queryParams.value).then(res => {
data.value = res.data;
})
};
onMounted(async () => {
getAll()
})
</script>
<style lang="less" scoped>
.container {
padding: 0 20px 20px 20px;
}
.left-side {
flex: 1;
overflow: auto;
}
.right-side {
width: 280px;
margin-left: 16px;
}
.panel {
background-color: var(--color-bg-2);
border-radius: 4px;
overflow: auto;
}
:deep(.panel-border) {
margin-bottom: 0;
border-bottom: 1px solid rgb(var(--gray-2));
}
.moduler-wrap {
border-radius: 4px;
background-color: var(--color-bg-2);
:deep(.text) {
font-size: 12px;
text-align: center;
color: rgb(var(--gray-8));
}
:deep(.wrapper) {
margin-bottom: 8px;
text-align: center;
cursor: pointer;
&:last-child {
.text {
margin-bottom: 0;
}
}
&:hover {
.icon {
color: rgb(var(--arcoblue-6));
background-color: #e8f3ff;
}
.text {
color: rgb(var(--arcoblue-6));
}
}
}
:deep(.icon) {
display: inline-block;
width: 32px;
height: 32px;
margin-bottom: 4px;
color: rgb(var(--dark-gray-1));
line-height: 32px;
font-size: 16px;
text-align: center;
background-color: rgb(var(--gray-1));
border-radius: 4px;
}
}
</style>
<style lang="less" scoped>
// responsive
.mobile {
.container {
display: block;
}
.right-side {
// display: none;
width: 100%;
margin-left: 0;
margin-top: 16px;
}
}
</style>

View File

@ -0,0 +1,5 @@
export default {
'menu.profile.basic': 'User List',
'menu.withdraw.list': '提现记录',
};

View File

@ -0,0 +1,5 @@
export default {
'menu.profile.basic': 'User List',
'menu.withdraw.list': '提现记录',
};