feat(管理员中心): 静态页面样式完成

This commit is contained in:
hzj 2025-11-25 18:53:06 +08:00
parent 1e6f494e30
commit 537da15942
6 changed files with 359 additions and 338 deletions

BIN
src/assets/icon/addUser.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 913 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 531 B

BIN
src/assets/icon/detail.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 B

BIN
src/assets/icon/list.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 B

View File

@ -115,6 +115,20 @@ export function gotoRoom(roomId) {
} }
} }
/**
* 前往app页面
*/
export function gotoAppPage(pageName) {
if (!roomId) return
try {
console.log(`pageName:${pageName}`)
// 直接使用Flutter通道
window.FlutterPageControl.postMessage(`pageName`)
} catch (error) {
console.error('前往APP页面失败:', error)
}
}
/** /**
* 退出APP * 退出APP
*/ */

View File

@ -1,368 +1,384 @@
<template> <template>
<div class="fullPage"> <div class="fullPage">
<div class="bg"> <div class="bg gradient-background-circles">
<MobileHeader title="Admin Center" :isHomePage="true"> <!-- 顶部导航 -->
<template v-slot:extraFunction> <GeneralHeader
<div style="color: rgba(187, 146, 255, 1) !important; font-weight: 600" @click="send"> :isHomePage="true"
Send :showLanguageList="true"
</div> title="Admin Center"
</template> :backImg="imgUrl"
</MobileHeader> color="black"
<!-- 标签 --> style="box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); z-index: 999"
/>
<!-- 主要内容 -->
<div <div
style=" style="
padding: 16px;
display: flex; display: flex;
justify-content: space-around; flex-direction: column;
border-bottom: 1px solid #cccccc; gap: 12px;
background-color: transparent;
position: relative;
" "
> >
<!-- 用户信息卡片 -->
<div <div
v-for="(item, index) in tabList" style="
:key="index" background-color: white;
style="font-size: 18px; padding: 10px 0; flex: 1; text-align: center" border-radius: 12px;
class="tab-item" box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
:class="activeIndex == index ? 'tabName-active' : 'tabName'"
ref="tabItems"
@click="setActiveTab(index)"
>
{{ item.name }}
</div>
<!-- 下划线元素 -->
<div
style="width: 15px; height: 3px; background-color: #bb92ff"
:style="underlineStyle"
></div>
</div>
<!-- BD列表 --> padding: 16px;
<div style="padding: 10px" v-show="activeIndex == 0"> border: 1px solid #e5e7eb;
<div
v-for="(info, BDIndex) in BDList"
:key="BDIndex"
style="
box-shadow: 0 0 4px 0 #00000066;
padding: 10px;
border-radius: 10px;
margin-bottom: 10px;
transition: all 0.3s ease-out;
"
@click="showUserInfo(BDIndex)"
>
<!-- 基本信息 -->
<div style="display: flex; justify-content: space-between">
<div style="display: flex; align-items: center">
<img
:src="info.userProfile.userAvatar"
style="width: 36px; height: 36px; border-radius: 50%"
/>
<div style="margin-left: 8px">
<div style="font-weight: 700">{{ info.userProfile.userNickname }}</div>
<div style="font-weight: 700; color: rgba(0, 0, 0, 0.4)">
ID:{{ info.userProfile.account }}
</div>
</div>
</div>
<div style="display: flex; align-items: center">
<img
src="../../assets/icon/arrow.png"
alt=""
style="width: 24px; transition: transform 0.5s"
:class="{ rotated: BDIndex == selectedBDIndex }"
/>
</div>
</div>
<!-- 展示信息 -->
<transition name="info-fade">
<div style="display: flex" v-show="BDIndex == selectedBDIndex">
<div style="flex: 1">
<div style="font-weight: 500; color: rgba(0, 0, 0, 0.4)">Host</div>
<div style="font-weight: 600; color: rgba(0, 0, 0, 0.8)">
{{ info.allAnchorCount || 0 }}
</div>
</div>
<div style="flex: 1">
<div style="font-weight: 500; color: rgba(0, 0, 0, 0.4)">Agency</div>
<div style="font-weight: 600; color: rgba(0, 0, 0, 0.8)">
{{ info.agentCount || 0 }}
</div>
</div>
</div>
</transition>
</div>
<!-- 触发加载功能 -->
<div ref="BDloadmore" v-if="showBDLoading"></div>
</div>
<!-- agency列表 --> display: flex;
<div style="padding: 10px" v-show="activeIndex == 1"> flex-direction: column;
<div gap: 10px;
style="
box-shadow: 0 0 4px 0 #00000066;
padding: 10px;
border-radius: 10px;
margin-bottom: 10px;
transition: all 0.3s ease-out;
" "
> >
<div style="font-weight: 500; color: rgba(0, 0, 0, 0.4)"> <!-- 标题 -->
Total recharge for the month: <div style="font-weight: 600">My Leader:</div>
</div>
<div style="font-weight: 600; color: rgba(0, 0, 0, 0.8)"> <!-- 用户信息 -->
${{ totalRechargeDetail[0]?.totalAmount || 0 }} <div style="display: flex; align-items: center; gap: 4px">
</div> <!-- 头像 -->
<div style="font-weight: 500; color: rgba(0, 0, 0, 0.4)">Total recharge last month:</div> <div
<div style="font-weight: 600; color: rgba(0, 0, 0, 0.8)"> style="
${{ totalRechargeDetail[1]?.totalAmount || 0 }} width: 15%;
</div> align-self: stretch;
</div> display: flex;
<div align-items: center;
v-for="(agency, aIndex) in agencyList" justify-content: center;
:key="aIndex" "
style=" >
box-shadow: 0 0 4px 0 #00000066;
padding: 10px;
border-radius: 10px;
margin-bottom: 10px;
transition: all 0.3s ease-out;
"
@click="showAgencyInfo(aIndex)"
>
<!-- 基本信息 -->
<div style="display: flex; justify-content: space-between">
<div style="display: flex; align-items: center">
<img :src="agency.userAvatar" alt="" style="width: 36px; border-radius: 50%" />
<div style="margin-left: 8px">
<div style="font-weight: 700">{{ agency.name }}</div>
<div style="font-weight: 700; color: rgba(0, 0, 0, 0.4)">
ID:{{ agency.account }}
</div>
</div>
</div>
<div style="display: flex; align-items: center">
<img <img
src="../../assets/icon/arrow.png" :src="''"
alt=""
style=" style="
width: 24px; display: block;
transition: transform 0.5s cubic-bezier(0.68, -0.55, 0.27, 1.55); width: 100%;
aspect-ratio: 1/1;
object-fit: cover;
border-radius: 50%;
" "
:class="{ rotated: aIndex == selectedAgencyIndex }" @error="defaultAvatarUrl"
/> />
</div> </div>
</div>
<!-- 展示信息 --> <!-- 基本信息 -->
<transition name="info-fade"> <div
<div style="display: flex" v-show="aIndex == selectedAgencyIndex"> style="
<div style="flex: 1"> flex: 1;
<div style="font-weight: 500; color: rgba(0, 0, 0, 0.4)">This month:</div> min-width: 0;
<div style="font-weight: 600; color: rgba(0, 0, 0, 0.8)"> align-self: stretch;
${{ formatPrice(agency.thisMonth) || 0 }} display: flex;
</div> flex-direction: column;
justify-content: space-around;
gap: 4px;
"
>
<div
style="
font-weight: 700;
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
"
>
User1
</div> </div>
<div style="flex: 1"> <div style="color: rgba(0, 0, 0, 0.4); font-size: 0.8em; font-weight: 500">
<div style="font-weight: 500; color: rgba(0, 0, 0, 0.4)">Last month:</div> ID:1234567890
<div style="font-weight: 600; color: rgba(0, 0, 0, 0.8)">
${{ formatPrice(agency.lastMonth) || 0 }}
</div>
</div> </div>
</div> </div>
</transition> </div>
</div>
<!-- 角色模块 -->
<div
style="
background-color: white;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
padding: 16px;
border: 1px solid #e5e7eb;
display: flex;
flex-direction: column;
gap: 10px;
"
>
<!-- 标题 -->
<div style="font-weight: 600; color: #1f2937">My Role:</div>
<!-- 角色身份 -->
<div style="display: flex; justify-content: space-between; align-items: center">
<div style="font-size: 1.1em; font-weight: 600">Admin</div>
<img
src="../../assets/icon/detail.png"
alt=""
style="display: block; width: 1.5em"
class="flipImg"
/>
</div>
<!-- 分割线 -->
<div style="border-bottom: 1px solid #e6e6e6"></div>
<!-- 已完成任务 -->
<div style="display: flex; justify-content: space-between; align-items: center">
<div style="font-weight: 600">Task completion status:</div>
<div style="font-weight: 600">0</div>
</div>
<div style="display: flex; flex-direction: column; gap: 10px">
<div style="font-weight: 500">2025.11.1-2025.11.16</div>
<!-- 任务列表与完成状态 -->
<div
v-for="value in 5"
style="display: flex; justify-content: space-between; align-items: center"
>
<div style="color: rgba(0, 0, 0, 0.4); font-size: 0.8em; font-weight: 500">
Mic usage time:
</div>
<div style="color: rgba(0, 0, 0, 0.4); font-size: 0.8em; font-weight: 500">
0/20h
</div>
</div>
</div>
</div>
<!-- 本期团队收入 -->
<div
style="
background-color: white;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
padding: 16px;
border: 1px solid #e5e7eb;
display: flex;
flex-direction: column;
gap: 10px;
"
>
<!-- 总收入 -->
<div style="display: flex; justify-content: space-between; align-items: center">
<div style="font-weight: 600">Team earnings this period: $3000</div>
<img
src="../../assets/icon/arrowBlack.png"
alt=""
style="display: block; width: 1.5em"
class="flipImg"
/>
</div>
<!-- 我的BD Leader团队 -->
<div style="display: flex; flex-direction: column; gap: 10px">
<div style="display: flex; justify-content: space-between; align-items: center">
<div style="font-weight: 600">My BD Leader Teams(3)</div>
<div style="align-self: stretch; display: flex; align-items: center; gap: 4px">
<img
src="../../assets/icon/addUser.png"
alt=""
style="display: block; width: 1.6em"
class="flipImg"
/>
<img
src="../../assets/icon/list.png"
alt=""
style="display: block; width: 1.6em"
class="flipImg"
/>
</div>
</div>
<div style="color: rgba(0, 0, 0, 0.4); font-size: 0.8em; font-weight: 500">
Total income:$1500
</div>
<!-- 分割线 -->
<div style="border-bottom: 1px solid #e6e6e6"></div>
</div>
<!-- 我的BD团队 -->
<div style="display: flex; flex-direction: column; gap: 10px">
<div style="display: flex; justify-content: space-between; align-items: center">
<div style="font-weight: 600">My BD Teams(5)</div>
<div style="align-self: stretch; display: flex; align-items: center; gap: 4px">
<img
src="../../assets/icon/addUser.png"
alt=""
style="display: block; width: 1.6em"
class="flipImg"
/>
<img
src="../../assets/icon/list.png"
alt=""
style="display: block; width: 1.6em"
class="flipImg"
/>
</div>
</div>
<div style="color: rgba(0, 0, 0, 0.4); font-size: 0.8em; font-weight: 500">
Total income:$1500
</div>
<!-- 分割线 -->
<div style="border-bottom: 1px solid #e6e6e6"></div>
</div>
<!-- 我的Agency团队 -->
<div style="display: flex; flex-direction: column; gap: 10px">
<div style="display: flex; justify-content: space-between; align-items: center">
<div style="font-weight: 600">My Agency Teams(2)</div>
<div style="align-self: stretch; display: flex; align-items: center; gap: 4px">
<img
src="../../assets/icon/addUser.png"
alt=""
style="display: block; width: 1.6em"
class="flipImg"
/>
<img
src="../../assets/icon/list.png"
alt=""
style="display: block; width: 1.6em"
class="flipImg"
/>
</div>
</div>
<div style="color: rgba(0, 0, 0, 0.4); font-size: 0.8em; font-weight: 500">
Total income:$1500
</div>
</div>
</div>
<!-- 赠送礼物 -->
<div
style="
background-color: white;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
padding: 16px;
border: 1px solid #e5e7eb;
display: flex;
justify-content: space-between;
gap: 10px;
"
@click="send"
>
<div style="font-weight: 600">Send Welcome Gift</div>
<img
src="../../assets/icon/arrowBlack.png"
alt=""
style="display: block; width: 1.5em"
class="flipImg"
/>
</div>
<!-- 前往用户编辑 -->
<div
style="
background-color: white;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
padding: 16px;
border: 1px solid #e5e7eb;
display: flex;
justify-content: space-between;
gap: 10px;
"
@click="gotoAppPage('editingUser')"
>
<div style="font-weight: 600">User Editing</div>
<img
src="../../assets/icon/arrowBlack.png"
alt=""
style="display: block; width: 1.5em"
class="flipImg"
/>
</div>
<!-- 前往房间编辑 -->
<div
style="
background-color: white;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
padding: 16px;
border: 1px solid #e5e7eb;
display: flex;
justify-content: space-between;
gap: 10px;
"
@click="gotoAppPage('editingRoom')"
>
<div style="font-weight: 600">Room Editing</div>
<img
src="../../assets/icon/arrowBlack.png"
alt=""
style="display: block; width: 1.5em"
class="flipImg"
/>
</div> </div>
<!-- 触发加载功能 -->
<div ref="Agloadmore" v-if="showAgLoading"></div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { onMounted, ref, computed } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { usePageInitializationWithConfig } from '@/utils/pageConfig.js' import { useI18n } from 'vue-i18n'
import { getUserId } from '@/utils/userStore.js' import { setDocumentDirection } from '@/locales/i18n'
import { useThrottle } from '@/utils/useDebounce' import { gotoAppPage } from '@/utils/appBridge.js'
import { getBDList, getAgencyList, getAgencyTotalRecharge } from '@/api/userInfo' import GeneralHeader from '@/components/GeneralHeader.vue'
import MobileHeader from '@/components/MobileHeader.vue' const imgUrl = new URL('/src/assets/icon/arrowBackBlack.png', import.meta.url).href
const { t, locale } = useI18n()
const router = useRouter() const router = useRouter()
const tabList = ref([ //
{ locale.value && setDocumentDirection(locale.value)
id: 1,
name: 'BD',
},
{
id: 2,
name: 'Recharge Agency',
},
])
const activeIndex = ref(0) const defaultAvatarUrl = (e) => {
const tabItems = ref([]) // DOM console.log('头像资源出错')
e.target.onerror = null //
// e.target.src = new URL('/src/assets/images/WeeklyStar/defaultAvatar.png', import.meta.url).href
const setActiveTab = (index) => {
if (activeIndex.value != index) {
console.log('切换标签')
activeIndex.value = index
selectedBDIndex.value = -1
selectedAgencyIndex.value = -1
}
} }
// 线
const underlineStyle = computed(() => {
console.log('计算属性')
if (tabItems.value.length === 0) return {} //
const activeTab = tabItems.value[activeIndex.value]
console.log('选中元素的左边距', activeTab.offsetLeft)
return {
position: 'absolute',
left: `${activeTab.offsetLeft + activeTab.offsetWidth / 2 - 15 / 2}px`,
bottom: '0',
transition: 'left 0.3s ease', //
}
})
const selectedBDIndex = ref(-1) //BD
const showUserInfo = (index) => {
selectedBDIndex.value = selectedBDIndex.value != index ? index : -1
} //BD
const selectedAgencyIndex = ref(-1) //agency
const showAgencyInfo = (index) => {
selectedAgencyIndex.value = selectedAgencyIndex.value != index ? index : -1
} //agency
const send = async () => { const send = async () => {
router.push({ path: '/item-distribution' }) router.push({ path: '/item-distribution' })
} }
//
usePageInitializationWithConfig('ADMIN_CENTER')
const BDList = ref([]) // BD
const BDCurrent = ref(1) //BD
const showBDLoading = ref(true) //
const totalRechargeDetail = ref([]) //
const agencyList = ref([]) //
const agencyCurrent = ref(1) //
const showAgLoading = ref(true) //
onMounted(async () => {
if (tabItems.value.length > 0) {
underlineStyle.value //
}
getBDs() // BD
getAgencise() //
getAgencyMonthsRecharge() //
//
if (BDloadmore.value) {
observer.observe(BDloadmore.value)
}
//
if (Agloadmore.value) {
observer.observe(Agloadmore.value)
}
})
// BD
const getBDs = async () => {
let data = {
userId: getUserId(),
cursor: BDCurrent.value,
limit: 20,
}
const resBDList = await getBDList(data)
console.log('resBDList:', resBDList)
if (resBDList.status && resBDList.body) {
BDList.value.push(...resBDList.body.records)
if (resBDList.body.total <= resBDList.body.current * resBDList.body.size) {
showBDLoading.value = false
}
}
}
//
const getAgencyMonthsRecharge = async () => {
const resTotalRecharge = await getAgencyTotalRecharge()
console.log('resTotalRecharge:', resTotalRecharge)
if (resTotalRecharge.status && resTotalRecharge.body) {
totalRechargeDetail.value = resTotalRecharge.body
}
}
//
const getAgencise = async () => {
let data = {
cursor: agencyCurrent.value,
limit: 20,
}
const resagencyList = await getAgencyList(data)
console.log('resagencyList:', resagencyList)
if (resagencyList.status && resagencyList.body) {
agencyList.value.push(...resagencyList.body.records)
if (resagencyList.body.total <= resagencyList.body.current * resagencyList.body.size) {
showAgLoading.value = false
}
}
}
// div
const BDloadmore = ref(null)
const Agloadmore = ref(null)
const debouceGetBDList = useThrottle(getBDs, 1000)
const debouceGetAgList = useThrottle(getAgencise, 1000)
// IntersectionObserver
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.intersectionRatio > 0) {
console.log('监控')
//
if (entry.target == BDloadmore.value && BDList.value.length != 0) {
BDCurrent.value++
debouceGetBDList()
}
if (entry.target == Agloadmore.value && agencyList.value.length != 0) {
agencyCurrent.value++
debouceGetAgList()
}
}
})
})
const formatPrice = (value) => {
if (value && value != 0) {
return Number(value).toFixed(2)
}
return 0
}
</script> </script>
<style scoped> <style scoped>
* { * {
color: black; color: rgba(0, 0, 0, 0.8);
font-family: 'SF Pro'; font-family: 'SF Pro Text';
} }
.fullPage { .fullPage {
background-color: #ffffff;
min-height: 100vh;
position: relative; position: relative;
max-height: 100vh;
overflow-y: auto;
background-color: #ffffff;
}
.fullPage::-webkit-scrollbar {
display: none;
} }
.bg { .bg {
@ -373,30 +389,21 @@ const formatPrice = (value) => {
background-repeat: no-repeat; background-repeat: no-repeat;
} }
.tabName { @media screen and (max-width: 360px) {
color: #00000066; * {
font-size: 10px;
}
} }
.tabName-active { @media screen and (min-width: 360px) {
color: black; * {
font-weight: 500; font-size: 12px;
}
} }
.info-fade-enter-from, @media screen and (min-width: 768px) {
.info-fade-leave-to { * {
transform: translateY(-10px); font-size: 24px;
opacity: 0; }
}
.info-fade-enter-active {
transition: all 0.5s ease-out;
}
.info-fade-leave-active {
transition: all 0.5s cubic-bezier(1, 0.5, 0.8, 1);
}
.rotated {
transform: rotate(90deg);
} }
</style> </style>