fix: improve admin center mobile taps
This commit is contained in:
parent
08ae81d5a8
commit
ef5af2ac27
@ -15,7 +15,7 @@
|
||||
>
|
||||
<!-- 返回键 -->
|
||||
<div style="width: 1.5em; display: flex; align-items: center">
|
||||
<button v-if="showBack" class="back-btn" @click="handleBack">
|
||||
<button v-if="showBack" class="back-btn" v-touch-tap="handleBack">
|
||||
<img
|
||||
v-smart-img
|
||||
class="flipImg"
|
||||
@ -43,7 +43,7 @@
|
||||
|
||||
<!-- 右侧按钮 -->
|
||||
<!-- 帮助按钮 -->
|
||||
<button v-if="showHelp" class="help-btn" style="width: 1.5em" @click="$emit('help')">
|
||||
<button v-if="showHelp" class="help-btn" style="width: 1.5em" v-touch-tap="emitHelp">
|
||||
<img v-smart-img :src="helpImg" alt="" style="width: 100%; display: block" />
|
||||
</button>
|
||||
|
||||
@ -56,7 +56,7 @@
|
||||
position: relative;
|
||||
color: rgba(187, 146, 255, 1) !important;
|
||||
"
|
||||
@click="showLang"
|
||||
v-touch-tap="showLang"
|
||||
>
|
||||
<div style="font-weight: bold">{{ currentLangName }}</div>
|
||||
<transition name="slide-fade">
|
||||
@ -64,7 +64,7 @@
|
||||
<div
|
||||
style="padding: 8px; color: black !important; font-weight: 590"
|
||||
v-for="lang in langStore.langList"
|
||||
@click.stop="handleLanguageChange(lang)"
|
||||
v-touch-tap.stop="() => handleLanguageChange(lang)"
|
||||
>
|
||||
{{ lang.value }}
|
||||
</div>
|
||||
@ -147,7 +147,7 @@ const props = defineProps({
|
||||
})
|
||||
|
||||
// 定义emits
|
||||
defineEmits(['help'])
|
||||
const emit = defineEmits(['help'])
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
@ -178,6 +178,10 @@ const handleLanguageChange = (item) => {
|
||||
// 立即更新visibleList状态
|
||||
}
|
||||
|
||||
const emitHelp = () => {
|
||||
emit('help')
|
||||
}
|
||||
|
||||
// 检测是否在APP环境中
|
||||
const isInAppEnvironment = ref(false)
|
||||
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
<div
|
||||
v-show="maskLayerShow"
|
||||
class="mask-layer"
|
||||
@click.stop
|
||||
@mousedown.stop
|
||||
@mouseup.stop
|
||||
@pointerdown.stop
|
||||
@ -12,7 +11,7 @@
|
||||
@touchmove.prevent
|
||||
@wheel.prevent
|
||||
>
|
||||
<div class="mask-layer__content" @touchmove.stop>
|
||||
<div class="mask-layer__content" @touchmove.stop v-touch-tap.self.stop="emitClose">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
@ -31,6 +30,11 @@ const props = defineProps({
|
||||
default: true,
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(['close'])
|
||||
|
||||
const emitClose = () => {
|
||||
emit('close')
|
||||
}
|
||||
|
||||
let lockCount = 0
|
||||
let lockedScrollY = 0
|
||||
|
||||
@ -8,6 +8,7 @@ import { installRuntimeSecurity } from './utils/runtimeSecurity.js'
|
||||
import { applyPerformanceModeClass } from './utils/performanceMode.js'
|
||||
import { versionChecker } from './utils/versionChecker.js'
|
||||
import { smartImage } from './directives/smartImage.js'
|
||||
import { vTouchTap } from './utils/touchTap.js'
|
||||
import { createPinia } from 'pinia'
|
||||
import { createApp } from 'vue'
|
||||
import { useLangStore } from '@/stores/lang'
|
||||
@ -28,6 +29,7 @@ pinia.use(piniaPluginPersistedstate)
|
||||
|
||||
const app = createApp(App)
|
||||
app.directive('smart-img', smartImage)
|
||||
app.directive('touch-tap', vTouchTap)
|
||||
app.component('LoadingContent', LoadingContent)
|
||||
|
||||
app.use(router)
|
||||
|
||||
167
src/utils/touchTap.js
Normal file
167
src/utils/touchTap.js
Normal file
@ -0,0 +1,167 @@
|
||||
const TAP_MOVE_LIMIT = 10
|
||||
const DUPLICATE_EVENT_LIMIT = 700
|
||||
const FOCUSABLE_SELECTOR =
|
||||
'a,button,input,textarea,select,[tabindex],[role="button"],[contenteditable="true"]'
|
||||
|
||||
const getEventPoint = (event) => {
|
||||
if (event.changedTouches?.length) return event.changedTouches[0]
|
||||
if (event.touches?.length) return event.touches[0]
|
||||
return event
|
||||
}
|
||||
|
||||
const triggerBinding = (el, event) => {
|
||||
const binding = el.__touchTapBinding
|
||||
if (!binding) return
|
||||
|
||||
if (binding.modifiers.self && event.target !== el) {
|
||||
return
|
||||
}
|
||||
|
||||
if (binding.modifiers.stop) {
|
||||
event.stopPropagation()
|
||||
}
|
||||
if (binding.modifiers.prevent) {
|
||||
event.preventDefault()
|
||||
}
|
||||
|
||||
if (typeof binding.value === 'function') {
|
||||
binding.value(event)
|
||||
}
|
||||
}
|
||||
|
||||
export const vTouchTap = {
|
||||
mounted(el, binding) {
|
||||
el.__touchTapBinding = binding
|
||||
|
||||
let startX = 0
|
||||
let startY = 0
|
||||
let moved = false
|
||||
let active = false
|
||||
let lastTouchTime = 0
|
||||
let lastActivationTime = 0
|
||||
|
||||
if (!el.matches(FOCUSABLE_SELECTOR)) {
|
||||
el.setAttribute('role', 'button')
|
||||
el.setAttribute('tabindex', '0')
|
||||
el.__touchTapAddedA11y = true
|
||||
}
|
||||
|
||||
const activate = (event) => {
|
||||
lastActivationTime = Date.now()
|
||||
triggerBinding(el, event)
|
||||
}
|
||||
|
||||
// Pointer Events 同时覆盖移动端触摸和桌面鼠标;移动阈值用于避免滚动列表时误触。
|
||||
const onStart = (event) => {
|
||||
if (el.__touchTapBinding?.modifiers.self && event.target !== el) {
|
||||
active = false
|
||||
return
|
||||
}
|
||||
if (event.button !== undefined && event.button !== 0) return
|
||||
|
||||
const point = getEventPoint(event)
|
||||
startX = point.clientX || 0
|
||||
startY = point.clientY || 0
|
||||
moved = false
|
||||
active = true
|
||||
}
|
||||
|
||||
const onMove = (event) => {
|
||||
if (!active) return
|
||||
|
||||
const point = getEventPoint(event)
|
||||
const deltaX = Math.abs((point.clientX || 0) - startX)
|
||||
const deltaY = Math.abs((point.clientY || 0) - startY)
|
||||
if (deltaX > TAP_MOVE_LIMIT || deltaY > TAP_MOVE_LIMIT) {
|
||||
moved = true
|
||||
}
|
||||
}
|
||||
|
||||
const onEnd = (event) => {
|
||||
if (!active) return
|
||||
|
||||
const point = getEventPoint(event)
|
||||
const deltaX = Math.abs((point.clientX || 0) - startX)
|
||||
const deltaY = Math.abs((point.clientY || 0) - startY)
|
||||
active = false
|
||||
|
||||
if (moved || deltaX > TAP_MOVE_LIMIT || deltaY > TAP_MOVE_LIMIT) return
|
||||
activate(event)
|
||||
}
|
||||
|
||||
const onTouchEnd = (event) => {
|
||||
lastTouchTime = Date.now()
|
||||
onEnd(event)
|
||||
}
|
||||
|
||||
const onMouseEnd = (event) => {
|
||||
if (Date.now() - lastTouchTime < DUPLICATE_EVENT_LIMIT) return
|
||||
onEnd(event)
|
||||
}
|
||||
|
||||
const onClick = (event) => {
|
||||
if (event.detail !== 0) return
|
||||
if (Date.now() - lastActivationTime < DUPLICATE_EVENT_LIMIT) return
|
||||
activate(event)
|
||||
}
|
||||
|
||||
const onKeydown = (event) => {
|
||||
if (event.key !== 'Enter' && event.key !== ' ') return
|
||||
if (event.key === ' ') {
|
||||
event.preventDefault()
|
||||
}
|
||||
activate(event)
|
||||
}
|
||||
|
||||
const onCancel = () => {
|
||||
active = false
|
||||
moved = false
|
||||
}
|
||||
|
||||
const hasPointerEvent = typeof window !== 'undefined' && typeof window.PointerEvent === 'function'
|
||||
const pointerOrTouchEvents = hasPointerEvent
|
||||
? [
|
||||
['pointerdown', onStart, { passive: true }],
|
||||
['pointermove', onMove, { passive: true }],
|
||||
['pointerup', onEnd, { passive: false }],
|
||||
['pointercancel', onCancel, { passive: true }],
|
||||
]
|
||||
: [
|
||||
['touchstart', onStart, { passive: true }],
|
||||
['touchmove', onMove, { passive: true }],
|
||||
['touchend', onTouchEnd, { passive: false }],
|
||||
['touchcancel', onCancel, { passive: true }],
|
||||
['mousedown', onStart, { passive: true }],
|
||||
['mousemove', onMove, { passive: true }],
|
||||
['mouseup', onMouseEnd, { passive: false }],
|
||||
]
|
||||
const events = [
|
||||
...pointerOrTouchEvents,
|
||||
['click', onClick, { passive: false }],
|
||||
['keydown', onKeydown, { passive: false }],
|
||||
]
|
||||
|
||||
events.forEach(([name, handler, options]) => {
|
||||
el.addEventListener(name, handler, options)
|
||||
})
|
||||
|
||||
el.__touchTapCleanup = () => {
|
||||
events.forEach(([name, handler, options]) => {
|
||||
el.removeEventListener(name, handler, options)
|
||||
})
|
||||
}
|
||||
},
|
||||
updated(el, binding) {
|
||||
el.__touchTapBinding = binding
|
||||
},
|
||||
beforeUnmount(el) {
|
||||
el.__touchTapCleanup?.()
|
||||
delete el.__touchTapBinding
|
||||
delete el.__touchTapCleanup
|
||||
if (el.__touchTapAddedA11y) {
|
||||
el.removeAttribute('role')
|
||||
el.removeAttribute('tabindex')
|
||||
delete el.__touchTapAddedA11y
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -1,7 +1,6 @@
|
||||
<template>
|
||||
<div class="fullPage">
|
||||
<div class="distribution-page">
|
||||
<div class="bg">
|
||||
<!-- 顶部导航 -->
|
||||
<GeneralHeader
|
||||
:showLanguageList="true"
|
||||
:title="t('item_distribution')"
|
||||
@ -9,141 +8,61 @@
|
||||
style="box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); z-index: 999"
|
||||
/>
|
||||
|
||||
<!-- 标签 -->
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
|
||||
background-color: transparent;
|
||||
position: relative;
|
||||
"
|
||||
>
|
||||
<div
|
||||
v-for="(item, index) in tabList"
|
||||
:key="index"
|
||||
style="font-size: 1.3em; padding: 10px 0; flex: 1; text-align: center"
|
||||
class="tab-item"
|
||||
:class="activeIndex == index ? 'tabName-active' : 'tabName'"
|
||||
ref="tabItems"
|
||||
@click="setActiveTab(index)"
|
||||
>
|
||||
{{ t(item.name) }}
|
||||
</div>
|
||||
<!-- 下划线元素 -->
|
||||
<div
|
||||
style="width: 15px; height: 3px; background-color: #bb92ff"
|
||||
:style="underlineStyle"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<!-- 商品列表 -->
|
||||
<div
|
||||
style="
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 12px;
|
||||
padding: 15px;
|
||||
"
|
||||
>
|
||||
<div v-for="(product, index) in productList" :key="index" @click="showSendDialog(product)">
|
||||
<main class="distribution-content">
|
||||
<div class="tabs">
|
||||
<div
|
||||
style="
|
||||
background-color: white;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0px 0px 3px 1px rgba(0, 0, 0, 0.2);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
"
|
||||
v-for="(item, index) in tabList"
|
||||
:key="index"
|
||||
class="tab-item"
|
||||
:class="activeIndex == index ? 'tabName-active' : 'tabName'"
|
||||
ref="tabItems"
|
||||
v-touch-tap="() => setActiveTab(index)"
|
||||
>
|
||||
<img :src="product.cover" alt="" style="width: 60%; margin: 10px 0" />
|
||||
<button
|
||||
style="
|
||||
background-color: #bb92ff;
|
||||
color: white;
|
||||
margin-bottom: 10px;
|
||||
border: 0;
|
||||
border-radius: 20px;
|
||||
padding: 5px 10px;
|
||||
"
|
||||
>
|
||||
{{ t(item.name) }}
|
||||
</div>
|
||||
<div class="tab-underline" :style="underlineStyle"></div>
|
||||
</div>
|
||||
|
||||
<div class="product-grid">
|
||||
<div
|
||||
v-for="(product, index) in productList"
|
||||
:key="index"
|
||||
class="product-card"
|
||||
v-touch-tap="() => showSendDialog(product)"
|
||||
>
|
||||
<img :src="product.cover" alt="" class="product-cover" />
|
||||
<button class="send-pill" type="button">
|
||||
{{ t('send') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 弹窗 -->
|
||||
<!-- 遮罩层 -->
|
||||
<maskLayer :maskLayerShow="dialogshow" @click="closedPopup">
|
||||
<div
|
||||
style="
|
||||
padding: 20px;
|
||||
background-color: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
border-radius: 20px;
|
||||
margin: 20% 5%;
|
||||
"
|
||||
@click.stop
|
||||
>
|
||||
<img :src="selectedGoods.cover" alt="" style="width: 60%; margin: 5px 0" />
|
||||
<div style="font-weight: 600; margin: 10px 0">
|
||||
<span style="color: #00000080; margin-right: 10px">{{ t('validity') }}:</span>
|
||||
<span style="font-weight: 700; color: #000">{{ validityPeriod }}{{ t('day') }}</span>
|
||||
<maskLayer :maskLayerShow="dialogshow" @close="closedPopup">
|
||||
<div class="send-dialog" @touchend.stop @pointerup.stop>
|
||||
<img :src="selectedGoods.cover" alt="" class="dialog-cover" />
|
||||
<div class="validity-row">
|
||||
<span>{{ t('validity') }}:</span>
|
||||
<strong>{{ validityPeriod }}{{ t('day') }}</strong>
|
||||
</div>
|
||||
<div class="send-form">
|
||||
<input
|
||||
type="text"
|
||||
:placeholder="t('enter_user_id_placeholder')"
|
||||
class="user-input"
|
||||
v-model="targetUserId"
|
||||
/>
|
||||
<button
|
||||
class="confirm-btn"
|
||||
type="button"
|
||||
:style="{ backgroundColor: confirmClick ? '#bb92ff' : '#00000080' }"
|
||||
v-touch-tap="sendGoods"
|
||||
>
|
||||
{{ t('confirm') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style="
|
||||
width: 90%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
:placeholder="t('enter_user_id_placeholder')"
|
||||
style="
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
align-self: stretch;
|
||||
|
||||
border-radius: 8px;
|
||||
border: 0;
|
||||
outline: none;
|
||||
background-color: #f1f1f1b2;
|
||||
padding: 8px;
|
||||
margin-right: 4px;
|
||||
font-size: 1em;
|
||||
font-weight: 590;
|
||||
"
|
||||
v-model="targetUserId"
|
||||
/>
|
||||
<button
|
||||
style="
|
||||
width: auto;
|
||||
min-width: 0;
|
||||
align-self: stretch;
|
||||
|
||||
border-radius: 8px;
|
||||
border: 0;
|
||||
padding: 8px;
|
||||
color: white;
|
||||
font-size: 1em;
|
||||
font-weight: 590;
|
||||
"
|
||||
:style="{ backgroundColor: confirmClick ? '#bb92ff' : '#00000080' }"
|
||||
@click="sendGoods"
|
||||
>
|
||||
{{ t('confirm') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</maskLayer>
|
||||
</maskLayer>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -208,11 +127,12 @@ const underlineStyle = computed(() => {
|
||||
|
||||
if (tabItems.value.length === 0) return {} //没有这个元素
|
||||
const activeTab = tabItems.value[activeIndex.value]
|
||||
const underlineWidth = 24
|
||||
console.log('选中元素的左边距', activeTab.offsetLeft)
|
||||
|
||||
return {
|
||||
position: 'absolute',
|
||||
left: `${activeTab.offsetLeft + activeTab.offsetWidth / 2 - 15 / 2}px`,
|
||||
left: `${activeTab.offsetLeft + activeTab.offsetWidth / 2 - underlineWidth / 2}px`,
|
||||
bottom: '0',
|
||||
transition: 'left 0.3s ease', // 平滑过渡
|
||||
}
|
||||
@ -341,12 +261,18 @@ onMounted(() => {
|
||||
<style scoped>
|
||||
* {
|
||||
color: black;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.fullPage {
|
||||
background-color: #ffffff;
|
||||
min-height: 100vh;
|
||||
.distribution-page {
|
||||
background-color: #f5f7fb;
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.distribution-page::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bg {
|
||||
@ -357,24 +283,176 @@ onMounted(() => {
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.distribution-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
padding: 14px 14px 28px;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
position: relative;
|
||||
padding: 6px;
|
||||
border-radius: 18px;
|
||||
background: rgba(255, 255, 255, 0.86);
|
||||
box-shadow: 0 10px 26px rgba(37, 45, 65, 0.07);
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
min-height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10px 8px;
|
||||
font-size: 1.1em;
|
||||
font-weight: 800;
|
||||
text-align: center;
|
||||
border-radius: 14px;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
.tabName {
|
||||
color: #00000066;
|
||||
color: rgba(31, 41, 55, 0.48);
|
||||
}
|
||||
|
||||
.tabName-active {
|
||||
color: black;
|
||||
font-weight: 500;
|
||||
color: #111827;
|
||||
background: #f4f2ff;
|
||||
}
|
||||
|
||||
.tab-underline {
|
||||
width: 24px;
|
||||
height: 4px;
|
||||
border-radius: 999px;
|
||||
background-color: #bb92ff;
|
||||
}
|
||||
|
||||
.product-grid {
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.product-card {
|
||||
min-width: 0;
|
||||
min-height: 170px;
|
||||
padding: 14px 10px 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border: 1px solid rgba(212, 218, 232, 0.9);
|
||||
border-radius: 18px;
|
||||
background-color: rgba(255, 255, 255, 0.96);
|
||||
box-shadow: 0 10px 26px rgba(37, 45, 65, 0.08);
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
.product-cover {
|
||||
display: block;
|
||||
width: 68%;
|
||||
max-height: 112px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.send-pill,
|
||||
.confirm-btn {
|
||||
border: 0;
|
||||
color: white;
|
||||
background-color: #bb92ff;
|
||||
font-weight: 800;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
.send-pill {
|
||||
min-width: 76px;
|
||||
min-height: 36px;
|
||||
padding: 8px 14px;
|
||||
border-radius: 999px;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.send-dialog {
|
||||
margin: 18vh 5% 0;
|
||||
padding: 22px 18px;
|
||||
background-color: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
border-radius: 22px;
|
||||
box-shadow: 0 18px 45px rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
|
||||
.dialog-cover {
|
||||
display: block;
|
||||
width: 62%;
|
||||
max-height: 190px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.validity-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin: 14px 0;
|
||||
font-size: 1.08em;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.validity-row span {
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.validity-row strong {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.send-form {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.user-input {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
border-radius: 12px;
|
||||
border: 0;
|
||||
outline: none;
|
||||
background-color: #f1f1f1b2;
|
||||
padding: 12px;
|
||||
font-size: 1.05em;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.confirm-btn {
|
||||
min-width: 86px;
|
||||
border-radius: 12px;
|
||||
padding: 0 14px;
|
||||
font-size: 1.05em;
|
||||
}
|
||||
|
||||
.product-card:active,
|
||||
.send-pill:active,
|
||||
.confirm-btn:active,
|
||||
.tab-item:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 360px) {
|
||||
* {
|
||||
font-size: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 360px) {
|
||||
* {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -155,7 +155,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 更多按钮 -->
|
||||
<div class="moreBt" @click="incomeShow(AgencyInfo)">
|
||||
<div class="moreBt" v-touch-tap="() => incomeShow(AgencyInfo)">
|
||||
{{ t('more') }}
|
||||
</div>
|
||||
</div>
|
||||
@ -163,7 +163,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 遮罩层 -->
|
||||
<maskLayer :maskLayerShow="maskLayerShow" @click="closedPopup">
|
||||
<maskLayer :maskLayerShow="maskLayerShow" @close="closedPopup">
|
||||
<div style="position: fixed; bottom: 0; width: 100%">
|
||||
<!-- 某个Agency的收益 -->
|
||||
<div
|
||||
@ -178,7 +178,8 @@
|
||||
padding: 10px 12px 30px;
|
||||
"
|
||||
class="overflow-y-auto"
|
||||
@click.stop
|
||||
@touchend.stop
|
||||
@pointerup.stop
|
||||
>
|
||||
<div style="display: flex; justify-content: center; align-items: center">
|
||||
<div style="margin: 0; font-weight: 600; color: #333">
|
||||
@ -262,7 +263,8 @@
|
||||
padding: 10px 12px 30px;
|
||||
"
|
||||
class="overflow-y-auto"
|
||||
@click.stop
|
||||
@touchend.stop
|
||||
@pointerup.stop
|
||||
>
|
||||
<div style="font-weight: 600">{{ incomeDetailsItem.settlementPeriod }}</div>
|
||||
<div style="display: flex; justify-content: space-between">
|
||||
@ -463,6 +465,8 @@ onMounted(() => {
|
||||
overflow-y: auto;
|
||||
background-color: #ffffff;
|
||||
background-image: url(../../assets/images/Azizi/secondBg.png);
|
||||
background-size: 100% auto;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.fullPage::-webkit-scrollbar {
|
||||
@ -482,13 +486,19 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.moreBt {
|
||||
font-size: 0.8em;
|
||||
|
||||
min-width: 56px;
|
||||
min-height: 30px;
|
||||
padding: 6px 10px;
|
||||
border-radius: 999px;
|
||||
background: #f4f0ff;
|
||||
position: absolute;
|
||||
bottom: 4%;
|
||||
right: 4%;
|
||||
color: #bb92ff;
|
||||
font-weight: 500;
|
||||
font-size: 0.95em;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
[dir='rtl'] .moreBt {
|
||||
@ -498,13 +508,13 @@ onMounted(() => {
|
||||
|
||||
@media screen and (max-width: 360px) {
|
||||
* {
|
||||
font-size: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 360px) {
|
||||
* {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -155,7 +155,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 更多按钮 -->
|
||||
<div class="moreBt" @click="incomeShow(BDLeaderInfo)">
|
||||
<div class="moreBt" v-touch-tap="() => incomeShow(BDLeaderInfo)">
|
||||
{{ t('more') }}
|
||||
</div>
|
||||
</div>
|
||||
@ -163,7 +163,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 遮罩层 -->
|
||||
<maskLayer :maskLayerShow="maskLayerShow" @click="closedPopup">
|
||||
<maskLayer :maskLayerShow="maskLayerShow" @close="closedPopup">
|
||||
<div style="position: fixed; bottom: 0; width: 100%">
|
||||
<!-- 某个BDLeader的收益 -->
|
||||
<div
|
||||
@ -178,7 +178,8 @@
|
||||
padding: 10px 12px 30px;
|
||||
"
|
||||
class="overflow-y-auto"
|
||||
@click.stop
|
||||
@touchend.stop
|
||||
@pointerup.stop
|
||||
>
|
||||
<div style="display: flex; justify-content: center; align-items: center">
|
||||
<div style="margin: 0; font-weight: 600; color: #333">
|
||||
@ -264,7 +265,8 @@
|
||||
padding: 10px 12px 30px;
|
||||
"
|
||||
class="overflow-y-auto"
|
||||
@click.stop
|
||||
@touchend.stop
|
||||
@pointerup.stop
|
||||
>
|
||||
<div style="font-weight: 600">{{ incomeDetailsItem.settlementPeriod }}</div>
|
||||
<div style="display: flex; justify-content: space-between">
|
||||
@ -465,6 +467,8 @@ onMounted(() => {
|
||||
overflow-y: auto;
|
||||
background-color: #ffffff;
|
||||
background-image: url(../../assets/images/Azizi/secondBg.png);
|
||||
background-size: 100% auto;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.fullPage::-webkit-scrollbar {
|
||||
@ -484,24 +488,30 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.moreBt {
|
||||
font-size: 0.8em;
|
||||
|
||||
min-width: 56px;
|
||||
min-height: 30px;
|
||||
padding: 6px 10px;
|
||||
border-radius: 999px;
|
||||
background: #f4f0ff;
|
||||
position: absolute;
|
||||
bottom: 4%;
|
||||
right: 4%;
|
||||
color: #bb92ff;
|
||||
font-weight: 500;
|
||||
font-size: 0.95em;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 360px) {
|
||||
* {
|
||||
font-size: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 360px) {
|
||||
* {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -153,7 +153,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 更多按钮 -->
|
||||
<div class="moreBt" @click="incomeShow(BDInfo)">
|
||||
<div class="moreBt" v-touch-tap="() => incomeShow(BDInfo)">
|
||||
{{ t('more') }}
|
||||
</div>
|
||||
</div>
|
||||
@ -161,7 +161,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 遮罩层 -->
|
||||
<maskLayer :maskLayerShow="maskLayerShow" @click="closedPopup">
|
||||
<maskLayer :maskLayerShow="maskLayerShow" @close="closedPopup">
|
||||
<div style="position: fixed; bottom: 0; width: 100%">
|
||||
<!-- 某个BD的收益 -->
|
||||
<div
|
||||
@ -176,7 +176,8 @@
|
||||
padding: 10px 12px 30px;
|
||||
"
|
||||
class="overflow-y-auto"
|
||||
@click.stop
|
||||
@touchend.stop
|
||||
@pointerup.stop
|
||||
>
|
||||
<div style="display: flex; justify-content: center; align-items: center">
|
||||
<div style="margin: 0; font-weight: 600; color: #333">
|
||||
@ -259,7 +260,8 @@
|
||||
padding: 10px 12px 30px;
|
||||
"
|
||||
class="overflow-y-auto"
|
||||
@click.stop
|
||||
@touchend.stop
|
||||
@pointerup.stop
|
||||
>
|
||||
<div style="font-weight: 600">{{ incomeDetailsItem.settlementPeriod }}</div>
|
||||
<div style="display: flex; justify-content: space-between">
|
||||
@ -460,6 +462,8 @@ onMounted(() => {
|
||||
overflow-y: auto;
|
||||
background-color: #ffffff;
|
||||
background-image: url(../../assets/images/Azizi/secondBg.png);
|
||||
background-size: 100% auto;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.fullPage::-webkit-scrollbar {
|
||||
@ -479,13 +483,19 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.moreBt {
|
||||
font-size: 0.8em;
|
||||
|
||||
min-width: 56px;
|
||||
min-height: 30px;
|
||||
padding: 6px 10px;
|
||||
border-radius: 999px;
|
||||
background: #f4f0ff;
|
||||
position: absolute;
|
||||
bottom: 4%;
|
||||
right: 4%;
|
||||
color: #bb92ff;
|
||||
font-weight: 500;
|
||||
font-size: 0.95em;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
[dir='rtl'] .moreBt {
|
||||
@ -495,13 +505,13 @@ onMounted(() => {
|
||||
|
||||
@media screen and (max-width: 360px) {
|
||||
* {
|
||||
font-size: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 360px) {
|
||||
* {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
<template>
|
||||
<div class="fullPage">
|
||||
<div class="recharge-page">
|
||||
<div class="bg">
|
||||
<!-- 顶部导航 -->
|
||||
<GeneralHeader
|
||||
:showLanguageList="true"
|
||||
:title="t('my_linked_recharge_agent')"
|
||||
@ -9,154 +8,59 @@
|
||||
style="box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); z-index: 999"
|
||||
/>
|
||||
|
||||
<!-- 主要内容 -->
|
||||
<div
|
||||
style="
|
||||
padding: 12px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
"
|
||||
>
|
||||
<!-- 这个月总充值 -->
|
||||
<div
|
||||
style="
|
||||
background-color: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
|
||||
padding: 12px;
|
||||
border: 1px solid #e5e7eb;
|
||||
"
|
||||
>
|
||||
<div style="font: 0.8em; font-weight: 500; color: rgba(0, 0, 0, 0.4)">
|
||||
{{ t('my_total_income') }}
|
||||
<main class="recharge-content">
|
||||
<section class="summary-card">
|
||||
<div class="metric-block">
|
||||
<div class="metric-label">{{ t('my_total_income') }}</div>
|
||||
<div class="metric-value">${{ totalRechargeDetail?.totalIncome || 0 }}</div>
|
||||
</div>
|
||||
<div style="font: 0.9em; font-weight: 600; color: rgba(0, 0, 0, 0.8)">
|
||||
${{ totalRechargeDetail?.totalIncome || 0 }}
|
||||
<div class="metric-block">
|
||||
<div class="metric-label">{{ t('this_period_recharge') }}</div>
|
||||
<div class="metric-value">${{ totalRechargeDetail?.currentMonthRecharge || 0 }}</div>
|
||||
</div>
|
||||
<div style="font: 0.8em; font-weight: 500; color: rgba(0, 0, 0, 0.4)">
|
||||
{{ t('this_period_recharge') }}
|
||||
</div>
|
||||
<div style="font: 0.9em; font-weight: 600; color: rgba(0, 0, 0, 0.8)">
|
||||
${{ totalRechargeDetail?.currentMonthRecharge || 0 }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div style="font-weight: 700">{{ t('recharge_agency_list') }}</div>
|
||||
<div class="list-title">{{ t('recharge_agency_list') }}</div>
|
||||
|
||||
<div
|
||||
<section
|
||||
v-for="(agency, aIndex) in agencyList"
|
||||
:key="aIndex"
|
||||
style="
|
||||
background-color: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
|
||||
padding: 12px;
|
||||
border: 1px solid #e5e7eb;
|
||||
|
||||
margin-bottom: 10px;
|
||||
transition: all 0.3s ease-out;
|
||||
"
|
||||
@click="showAgencyInfo(aIndex)"
|
||||
class="agency-card"
|
||||
:class="{ expanded: aIndex == selectedAgencyIndex }"
|
||||
v-touch-tap="() => showAgencyInfo(aIndex)"
|
||||
>
|
||||
<div style="width: 100%; display: flex; justify-content: space-between">
|
||||
<!-- 基本信息 -->
|
||||
<div
|
||||
style="
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
align-self: stretch;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
gap: 4px;
|
||||
"
|
||||
>
|
||||
<img
|
||||
:src="agency.userAvatar"
|
||||
alt=""
|
||||
style="display: block; object-fit: cover; width: 3em; border-radius: 50%"
|
||||
/>
|
||||
<div
|
||||
style="
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
gap: 4px;
|
||||
"
|
||||
>
|
||||
<div
|
||||
style="
|
||||
font-weight: 700;
|
||||
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
"
|
||||
>
|
||||
{{ agency.userNickname }}
|
||||
</div>
|
||||
<div style="font-weight: 700; color: rgba(0, 0, 0, 0.4)">
|
||||
{{ t('user_id_prefix') }}{{ agency.account }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="agency-main">
|
||||
<div class="agency-profile">
|
||||
<img :src="agency.userAvatar" alt="" class="agency-avatar" />
|
||||
<div class="agency-meta">
|
||||
<div class="agency-name">{{ agency.userNickname }}</div>
|
||||
<div class="agency-id">{{ t('user_id_prefix') }}{{ agency.account }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 展开按钮 -->
|
||||
<div
|
||||
style="
|
||||
width: auto;
|
||||
min-width: 0;
|
||||
align-self: stretch;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
"
|
||||
>
|
||||
</div>
|
||||
<div class="expand-icon">
|
||||
<img
|
||||
src="../../assets/icon/Azizi/arrow.png"
|
||||
alt=""
|
||||
style="
|
||||
width: 24px;
|
||||
transition: transform 0.5s cubic-bezier(0.68, -0.55, 0.27, 1.55);
|
||||
"
|
||||
:class="{ rotated: aIndex == selectedAgencyIndex }"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 展示信息 -->
|
||||
<transition name="info-fade">
|
||||
<div style="display: flex" v-show="aIndex == selectedAgencyIndex">
|
||||
<div style="flex: 1">
|
||||
<div style="font-weight: 500; color: rgba(0, 0, 0, 0.4)">
|
||||
{{ t('this_month') }}:
|
||||
</div>
|
||||
<div style="font-weight: 600; color: rgba(0, 0, 0, 0.8)">
|
||||
${{ formatPrice(agency.thisMonth) || 0 }}
|
||||
</div>
|
||||
<div class="agency-detail" v-show="aIndex == selectedAgencyIndex">
|
||||
<div class="detail-metric">
|
||||
<div class="metric-label">{{ t('this_month') }}:</div>
|
||||
<div class="detail-value">${{ formatPrice(agency.thisMonth) || 0 }}</div>
|
||||
</div>
|
||||
<div style="flex: 1">
|
||||
<div style="font-weight: 500; color: rgba(0, 0, 0, 0.4)">
|
||||
{{ t('last_month') }}:
|
||||
</div>
|
||||
<div style="font-weight: 600; color: rgba(0, 0, 0, 0.8)">
|
||||
${{ formatPrice(agency.lastMonth) || 0 }}
|
||||
</div>
|
||||
<div class="detail-metric">
|
||||
<div class="metric-label">{{ t('last_month') }}:</div>
|
||||
<div class="detail-value">${{ formatPrice(agency.lastMonth) || 0 }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
<!-- 触发加载功能 -->
|
||||
</section>
|
||||
<div ref="Agloadmore" v-if="showAgLoading"></div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -251,12 +155,18 @@ const formatPrice = (value) => {
|
||||
<style scoped>
|
||||
* {
|
||||
color: black;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.fullPage {
|
||||
background-color: #ffffff;
|
||||
min-height: 100vh;
|
||||
.recharge-page {
|
||||
background-color: #f5f7fb;
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.recharge-page::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bg {
|
||||
@ -267,13 +177,148 @@ const formatPrice = (value) => {
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.tabName {
|
||||
color: #00000066;
|
||||
.recharge-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
padding: 16px 14px 28px;
|
||||
}
|
||||
|
||||
.tabName-active {
|
||||
color: black;
|
||||
font-weight: 500;
|
||||
.summary-card,
|
||||
.agency-card {
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
border: 1px solid rgba(212, 218, 232, 0.9);
|
||||
border-radius: 18px;
|
||||
box-shadow: 0 10px 26px rgba(37, 45, 65, 0.08);
|
||||
}
|
||||
|
||||
.summary-card {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.metric-block {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.metric-label {
|
||||
color: rgba(31, 41, 55, 0.55);
|
||||
font-size: 1em;
|
||||
font-weight: 700;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
margin-top: 5px;
|
||||
color: #111827;
|
||||
font-size: 1.35em;
|
||||
font-weight: 800;
|
||||
line-height: 1.2;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.list-title {
|
||||
color: #111827;
|
||||
font-size: 1.2em;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.agency-card {
|
||||
padding: 14px;
|
||||
transition:
|
||||
transform 0.2s ease,
|
||||
box-shadow 0.2s ease;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
.agency-card.expanded {
|
||||
box-shadow: 0 14px 32px rgba(37, 45, 65, 0.12);
|
||||
}
|
||||
|
||||
.agency-main,
|
||||
.agency-profile,
|
||||
.expand-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.agency-main {
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.agency-profile {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.agency-avatar {
|
||||
display: block;
|
||||
width: 54px;
|
||||
height: 54px;
|
||||
flex: 0 0 54px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
background: #eef2f7;
|
||||
}
|
||||
|
||||
.agency-meta {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.agency-name {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: #111827;
|
||||
font-size: 1.12em;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.agency-id {
|
||||
margin-top: 4px;
|
||||
color: rgba(31, 41, 55, 0.5);
|
||||
font-size: 0.98em;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.expand-icon {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
justify-content: center;
|
||||
border-radius: 12px;
|
||||
background: #f4f2ff;
|
||||
}
|
||||
|
||||
.expand-icon img {
|
||||
display: block;
|
||||
width: 22px;
|
||||
transition: transform 0.5s cubic-bezier(0.68, -0.55, 0.27, 1.55);
|
||||
}
|
||||
|
||||
.agency-detail {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
margin-top: 14px;
|
||||
padding-top: 14px;
|
||||
border-top: 1px solid rgba(229, 231, 235, 0.9);
|
||||
}
|
||||
|
||||
.detail-metric {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
margin-top: 4px;
|
||||
color: rgba(31, 41, 55, 0.9);
|
||||
font-size: 1.08em;
|
||||
font-weight: 800;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.info-fade-enter-from,
|
||||
@ -296,13 +341,13 @@ const formatPrice = (value) => {
|
||||
|
||||
@media screen and (max-width: 360px) {
|
||||
* {
|
||||
font-size: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 360px) {
|
||||
* {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
>
|
||||
<slot name="content"></slot>
|
||||
</div>
|
||||
<div class="moreBt" @click="btMore">{{ t('more') }}</div>
|
||||
<div class="moreBt" v-touch-tap="btMore">{{ t('more') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -119,12 +119,19 @@ const getTypeText = (type) => {
|
||||
}
|
||||
|
||||
.moreBt {
|
||||
font-size: 0.8em;
|
||||
min-width: 56px;
|
||||
min-height: 30px;
|
||||
padding: 6px 10px;
|
||||
border-radius: 999px;
|
||||
background: #f4f0ff;
|
||||
font-size: 0.95em;
|
||||
position: absolute;
|
||||
bottom: 4%;
|
||||
right: 4%;
|
||||
color: #bb92ff;
|
||||
font-weight: 500;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
.status {
|
||||
@ -154,13 +161,13 @@ const getTypeText = (type) => {
|
||||
|
||||
@media screen and (max-width: 360px) {
|
||||
* {
|
||||
font-size: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 360px) {
|
||||
* {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
<template>
|
||||
<div class="fullPage">
|
||||
<div class="bg gradient-background-circles">
|
||||
<!-- 顶部导航 -->
|
||||
<div class="admin-page">
|
||||
<div class="admin-shell gradient-background-circles">
|
||||
<GeneralHeader
|
||||
:isHomePage="true"
|
||||
:showLanguageList="true"
|
||||
@ -10,415 +9,215 @@
|
||||
style="box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); z-index: 999"
|
||||
/>
|
||||
|
||||
<!-- 主要内容 -->
|
||||
<div
|
||||
style="
|
||||
padding: 16px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
"
|
||||
>
|
||||
<!-- 用户信息卡片 -->
|
||||
<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">{{ t('my_leader') }}</div>
|
||||
|
||||
<!-- 用户信息 -->
|
||||
<div style="display: flex; align-items: center; gap: 4px">
|
||||
<!-- 头像 -->
|
||||
<div
|
||||
style="
|
||||
width: 3.5em;
|
||||
align-self: stretch;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
"
|
||||
>
|
||||
<main class="admin-content">
|
||||
<section class="leader-panel">
|
||||
<div class="section-kicker">{{ t('my_leader') }}</div>
|
||||
<div class="leader-card">
|
||||
<div class="leader-avatar">
|
||||
<img
|
||||
:src="userInfo.userAvatar || ''"
|
||||
style="
|
||||
display: block;
|
||||
width: 100%;
|
||||
aspect-ratio: 1/1;
|
||||
object-fit: cover;
|
||||
border-radius: 50%;
|
||||
"
|
||||
class="avatar-image"
|
||||
alt=""
|
||||
@error="handleAvatarImageError"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 基本信息 -->
|
||||
<div
|
||||
style="
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
align-self: stretch;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
gap: 4px;
|
||||
"
|
||||
>
|
||||
<div
|
||||
style="
|
||||
font-weight: 700;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
"
|
||||
>
|
||||
{{ userInfo.userNickname || '-' }}
|
||||
</div>
|
||||
<div style="color: rgba(0, 0, 0, 0.4); font-size: 0.8em; font-weight: 500">
|
||||
<div class="leader-meta">
|
||||
<div class="leader-name">{{ userInfo.userNickname || '-' }}</div>
|
||||
<div class="muted-text">
|
||||
{{ t('user_id_prefix') }} {{ userInfo.account || '-' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 角色模块 -->
|
||||
<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: 4px;
|
||||
"
|
||||
>
|
||||
<!-- 标题 -->
|
||||
<div style="font-weight: 600; color: #1f2937">{{ t('my_role') }}</div>
|
||||
|
||||
<!-- 角色身份 -->
|
||||
<div style="display: flex; justify-content: space-between; align-items: center">
|
||||
<div style="font-size: 1.1em; font-weight: 600">{{ t('admin') }}</div>
|
||||
<img
|
||||
src="../../assets/icon/Azizi/detail.png"
|
||||
alt=""
|
||||
style="display: block; width: 1.5em"
|
||||
class="flipImg"
|
||||
@click="gotoPolicy"
|
||||
/>
|
||||
<section class="admin-card role-card">
|
||||
<div class="section-title-row">
|
||||
<div>
|
||||
<div class="section-kicker">{{ t('my_role') }}</div>
|
||||
<div class="section-title">{{ t('admin') }}</div>
|
||||
</div>
|
||||
<button class="icon-action" type="button" v-touch-tap="gotoPolicy">
|
||||
<img src="../../assets/icon/Azizi/detail.png" alt="" class="icon-image flipImg" />
|
||||
</button>
|
||||
</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">{{ t('task_completion_status') }}</div>
|
||||
<div style="font-weight: 600">({{ taskData.completedTaskCount || 0 }})</div>
|
||||
<div class="task-summary">
|
||||
<div>{{ t('task_completion_status') }}</div>
|
||||
<strong>({{ taskData.completedTaskCount || 0 }})</strong>
|
||||
</div>
|
||||
|
||||
<div v-if="taskData.tasks" style="display: flex; flex-direction: column; gap: 4px">
|
||||
<div style="font-weight: 500">
|
||||
<div v-if="taskData.tasks" class="task-list">
|
||||
<div class="task-period">
|
||||
{{ taskData.cycleStartDate }}-{{ taskData.cycleEndDate }}
|
||||
</div>
|
||||
<!-- 任务列表与完成状态 -->
|
||||
<div
|
||||
v-for="task in taskData.tasks"
|
||||
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">
|
||||
{{ taskName(task) }}
|
||||
</div>
|
||||
<div style="color: rgba(0, 0, 0, 0.4); font-size: 0.8em; font-weight: 500">
|
||||
{{ taskCompletedText(task) }}
|
||||
</div>
|
||||
<div v-for="task in taskData.tasks" :key="task.taskType" class="task-row">
|
||||
<span>{{ taskName(task) }}</span>
|
||||
<strong>{{ taskCompletedText(task) }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 本期团队收入 -->
|
||||
<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: 4px;
|
||||
"
|
||||
>
|
||||
<!-- 总收入 -->
|
||||
<div style="display: flex; justify-content: space-between; align-items: center">
|
||||
<!-- 基本信息 -->
|
||||
<div style="display: flex; flex-direction: column; gap: 4px">
|
||||
<div style="font-weight: 600">
|
||||
<section class="admin-card income-card">
|
||||
<div class="income-summary">
|
||||
<div>
|
||||
<div class="income-title">
|
||||
{{ t('team_earnings_this_period') }} ${{ teamSummary.teamEarningsThisPeriod || 0 }}
|
||||
</div>
|
||||
|
||||
<div style="color: rgba(0, 0, 0, 0.4); font-size: 0.8em; font-weight: 500">
|
||||
<div class="muted-text">
|
||||
{{ t('available_salaries') }} ${{ availableIncome?.availableBalance || 0 }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 跳转按钮 -->
|
||||
<img
|
||||
src="../../assets/icon/Azizi/arrowBlack.png"
|
||||
alt=""
|
||||
style="display: block; width: 2em"
|
||||
class="flipImg"
|
||||
@click="gotoIncome"
|
||||
/>
|
||||
<button class="round-action" type="button" v-touch-tap="gotoIncome">
|
||||
<img src="../../assets/icon/Azizi/arrowBlack.png" alt="" class="flipImg" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 分割线 -->
|
||||
<div style="border-bottom: 1px solid #e6e6e6"></div>
|
||||
|
||||
<!-- 我的BD Leader团队 -->
|
||||
<div style="display: flex; justify-content: space-between; align-items: center">
|
||||
<!-- 基本信息 -->
|
||||
<div style="display: flex; flex-direction: column; gap: 4px">
|
||||
<div style="font-weight: 600">
|
||||
{{ t('my_bd_leader_teams') }}({{ teamSummary.bdLeaderTeams?.count || 0 }})
|
||||
<div class="team-list">
|
||||
<div class="team-row">
|
||||
<div class="team-copy">
|
||||
<div class="team-title">
|
||||
{{ t('my_bd_leader_teams') }}({{ teamSummary.bdLeaderTeams?.count || 0 }})
|
||||
</div>
|
||||
<div class="muted-text">
|
||||
{{ t('total_income') }} ${{ teamSummary.bdLeaderTeams?.totalIncome || 0 }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="color: rgba(0, 0, 0, 0.4); font-size: 0.8em; font-weight: 500">
|
||||
{{ t('total_income') }} ${{ teamSummary.bdLeaderTeams?.totalIncome || 0 }}
|
||||
<div class="team-actions">
|
||||
<button
|
||||
class="square-action"
|
||||
type="button"
|
||||
v-touch-tap="() => gotoPage('/invite-bd-leader')"
|
||||
>
|
||||
<img src="../../assets/icon/Azizi/addUser.png" alt="" class="flipImg" />
|
||||
</button>
|
||||
<button
|
||||
class="square-action"
|
||||
type="button"
|
||||
v-touch-tap="() => gotoPage('/my-BDLeader-teams')"
|
||||
>
|
||||
<img src="../../assets/icon/Azizi/list.png" alt="" class="flipImg" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 跳转按钮 -->
|
||||
<div style="align-self: stretch; display: flex; align-items: center; gap: 4px">
|
||||
<img
|
||||
src="../../assets/icon/Azizi/addUser.png"
|
||||
alt=""
|
||||
style="display: block; width: 1.6em"
|
||||
class="flipImg"
|
||||
@click="gotoPage('/invite-bd-leader')"
|
||||
/>
|
||||
<img
|
||||
src="../../assets/icon/Azizi/list.png"
|
||||
alt=""
|
||||
style="display: block; width: 1.6em"
|
||||
class="flipImg"
|
||||
@click="gotoPage('/my-BDLeader-teams')"
|
||||
/>
|
||||
<div class="team-row">
|
||||
<div class="team-copy">
|
||||
<div class="team-title">
|
||||
{{ t('my_bd_teams') }}({{ teamSummary.bdTeams?.count || 0 }})
|
||||
</div>
|
||||
<div class="muted-text">
|
||||
{{ t('total_income') }} ${{ teamSummary.bdTeams?.totalIncome || 0 }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="team-actions">
|
||||
<button
|
||||
class="square-action"
|
||||
type="button"
|
||||
v-touch-tap="() => gotoPage('/invite-bd')"
|
||||
>
|
||||
<img src="../../assets/icon/Azizi/addUser.png" alt="" class="flipImg" />
|
||||
</button>
|
||||
<button
|
||||
class="square-action"
|
||||
type="button"
|
||||
v-touch-tap="() => gotoPage('/my-BD-teams')"
|
||||
>
|
||||
<img src="../../assets/icon/Azizi/list.png" alt="" class="flipImg" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="team-row">
|
||||
<div class="team-copy">
|
||||
<div class="team-title">
|
||||
{{ t('my_agency_teams') }}({{ teamSummary.agencyTeams?.count || 0 }})
|
||||
</div>
|
||||
<div class="muted-text">
|
||||
{{ t('total_income') }} ${{ teamSummary.agencyTeams?.totalIncome || 0 }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="team-actions">
|
||||
<button
|
||||
class="square-action"
|
||||
type="button"
|
||||
v-touch-tap="() => gotoPage('/invite-agency')"
|
||||
>
|
||||
<img src="../../assets/icon/Azizi/addUser.png" alt="" class="flipImg" />
|
||||
</button>
|
||||
<button
|
||||
class="square-action"
|
||||
type="button"
|
||||
v-touch-tap="() => gotoPage('/my-agency-teams')"
|
||||
>
|
||||
<img src="../../assets/icon/Azizi/list.png" alt="" class="flipImg" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="team-row">
|
||||
<div class="team-copy">
|
||||
<div class="team-title">
|
||||
{{ t('number_of_recharge_agents_invited') }}({{
|
||||
teamSummary.rechargeTeams?.count || 0
|
||||
}})
|
||||
</div>
|
||||
<div class="muted-text">
|
||||
{{ t('total_recharge') }}: ${{ teamSummary.rechargeTeams?.totalIncome || 0 }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="team-actions">
|
||||
<button
|
||||
class="square-action"
|
||||
type="button"
|
||||
v-touch-tap="() => gotoPage('/invite-recharge-agency')"
|
||||
>
|
||||
<img src="../../assets/icon/Azizi/addUser.png" alt="" class="flipImg" />
|
||||
</button>
|
||||
<button
|
||||
class="square-action"
|
||||
type="button"
|
||||
v-touch-tap="() => gotoPage('/my-recharge-agency')"
|
||||
>
|
||||
<img src="../../assets/icon/Azizi/list.png" alt="" class="flipImg" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 分割线 -->
|
||||
<div style="border-bottom: 1px solid #e6e6e6"></div>
|
||||
<section class="quick-actions">
|
||||
<button class="quick-action" type="button" v-touch-tap="send">
|
||||
<span>{{ t('send_welcome_gift') }}</span>
|
||||
<img src="../../assets/icon/Azizi/arrowBlack.png" alt="" class="flipImg" />
|
||||
</button>
|
||||
|
||||
<!-- 我的BD团队 -->
|
||||
<div style="display: flex; justify-content: space-between; align-items: center">
|
||||
<!-- 基本信息 -->
|
||||
<div style="display: flex; flex-direction: column; gap: 4px">
|
||||
<div style="font-weight: 600">
|
||||
{{ t('my_bd_teams') }}({{ teamSummary.bdTeams?.count || 0 }})
|
||||
</div>
|
||||
<button
|
||||
v-if="teamPermissionCheck.hasUserEditingPermission"
|
||||
class="quick-action"
|
||||
type="button"
|
||||
v-touch-tap="() => gotoAppPage('editingUser')"
|
||||
>
|
||||
<span>{{ t('user_editing') }}</span>
|
||||
<img src="../../assets/icon/Azizi/arrowBlack.png" alt="" class="flipImg" />
|
||||
</button>
|
||||
|
||||
<div style="color: rgba(0, 0, 0, 0.4); font-size: 0.8em; font-weight: 500">
|
||||
{{ t('total_income') }} ${{ teamSummary.bdTeams?.totalIncome || 0 }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 跳转按钮 -->
|
||||
<div style="align-self: stretch; display: flex; align-items: center; gap: 4px">
|
||||
<img
|
||||
src="../../assets/icon/Azizi/addUser.png"
|
||||
alt=""
|
||||
style="display: block; width: 1.6em"
|
||||
class="flipImg"
|
||||
@click="gotoPage('/invite-bd')"
|
||||
/>
|
||||
<img
|
||||
src="../../assets/icon/Azizi/list.png"
|
||||
alt=""
|
||||
style="display: block; width: 1.6em"
|
||||
class="flipImg"
|
||||
@click="gotoPage('/my-BD-teams')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分割线 -->
|
||||
<div style="border-bottom: 1px solid #e6e6e6"></div>
|
||||
|
||||
<!-- 我的Agency团队 -->
|
||||
<div style="display: flex; justify-content: space-between; align-items: center">
|
||||
<!-- 基本信息 -->
|
||||
<div style="display: flex; flex-direction: column; gap: 4px">
|
||||
<div style="font-weight: 600">
|
||||
{{ t('my_agency_teams') }}({{ teamSummary.agencyTeams?.count || 0 }})
|
||||
</div>
|
||||
|
||||
<div style="color: rgba(0, 0, 0, 0.4); font-size: 0.8em; font-weight: 500">
|
||||
{{ t('total_income') }} ${{ teamSummary.agencyTeams?.totalIncome || 0 }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 跳转按钮 -->
|
||||
<div style="align-self: stretch; display: flex; align-items: center; gap: 4px">
|
||||
<img
|
||||
src="../../assets/icon/Azizi/addUser.png"
|
||||
alt=""
|
||||
style="display: block; width: 1.6em"
|
||||
class="flipImg"
|
||||
@click="gotoPage('/invite-agency')"
|
||||
/>
|
||||
<img
|
||||
src="../../assets/icon/Azizi/list.png"
|
||||
alt=""
|
||||
style="display: block; width: 1.6em"
|
||||
class="flipImg"
|
||||
@click="gotoPage('/my-agency-teams')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分割线 -->
|
||||
<div style="border-bottom: 1px solid #e6e6e6"></div>
|
||||
|
||||
<!-- 我的Recharge Agency团队 -->
|
||||
<div style="display: flex; justify-content: space-between; align-items: center">
|
||||
<!-- 基本信息 -->
|
||||
<div style="display: flex; flex-direction: column; gap: 4px">
|
||||
<div style="font-weight: 600">
|
||||
{{ t('number_of_recharge_agents_invited') }}({{
|
||||
teamSummary.rechargeTeams?.count || 0
|
||||
}})
|
||||
</div>
|
||||
|
||||
<div style="color: rgba(0, 0, 0, 0.4); font-size: 0.8em; font-weight: 500">
|
||||
{{ t('total_recharge') }}: ${{ teamSummary.rechargeTeams?.totalIncome || 0 }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 跳转按钮 -->
|
||||
<div style="align-self: stretch; display: flex; align-items: center; gap: 4px">
|
||||
<img
|
||||
src="../../assets/icon/Azizi/addUser.png"
|
||||
alt=""
|
||||
style="display: block; width: 1.6em"
|
||||
class="flipImg"
|
||||
@click="gotoPage('/invite-recharge-agency')"
|
||||
/>
|
||||
<img
|
||||
src="../../assets/icon/Azizi/list.png"
|
||||
alt=""
|
||||
style="display: block; width: 1.6em"
|
||||
class="flipImg"
|
||||
@click="gotoPage('/my-recharge-agency')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分割线 -->
|
||||
<div style="border-bottom: 1px solid #e6e6e6"></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: 4px;
|
||||
"
|
||||
@click="send"
|
||||
>
|
||||
<div style="font-weight: 600">{{ t('send_welcome_gift') }}</div>
|
||||
<img
|
||||
src="../../assets/icon/Azizi/arrowBlack.png"
|
||||
alt=""
|
||||
style="display: block; width: 1.5em"
|
||||
class="flipImg"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 前往用户编辑 -->
|
||||
<div
|
||||
v-if="teamPermissionCheck.hasUserEditingPermission"
|
||||
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: 4px;
|
||||
"
|
||||
@click="gotoAppPage('editingUser')"
|
||||
>
|
||||
<div style="font-weight: 600">{{ t('user_editing') }}</div>
|
||||
<img
|
||||
src="../../assets/icon/Azizi/arrowBlack.png"
|
||||
alt=""
|
||||
style="display: block; width: 1.5em"
|
||||
class="flipImg"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 前往房间编辑 -->
|
||||
<div
|
||||
v-if="teamPermissionCheck.hasRoomEditingPermission"
|
||||
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: 4px;
|
||||
"
|
||||
@click="gotoAppPage('editingRoom')"
|
||||
>
|
||||
<div style="font-weight: 600">{{ t('room_editing') }}</div>
|
||||
<img
|
||||
src="../../assets/icon/Azizi/arrowBlack.png"
|
||||
alt=""
|
||||
style="display: block; width: 1.5em"
|
||||
class="flipImg"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
v-if="teamPermissionCheck.hasRoomEditingPermission"
|
||||
class="quick-action"
|
||||
type="button"
|
||||
v-touch-tap="() => gotoAppPage('editingRoom')"
|
||||
>
|
||||
<span>{{ t('room_editing') }}</span>
|
||||
<img src="../../assets/icon/Azizi/arrowBlack.png" alt="" class="flipImg" />
|
||||
</button>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { setDocumentDirection } from '@/locales/i18n'
|
||||
@ -563,21 +362,21 @@ onMounted(() => {
|
||||
<style scoped>
|
||||
* {
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.fullPage {
|
||||
.admin-page {
|
||||
position: relative;
|
||||
|
||||
max-height: 100vh;
|
||||
height: 100vh;
|
||||
overflow-y: auto;
|
||||
background-color: #ffffff;
|
||||
background: #f5f7fb;
|
||||
}
|
||||
|
||||
.fullPage::-webkit-scrollbar {
|
||||
.admin-page::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bg {
|
||||
.admin-shell {
|
||||
width: 100vw;
|
||||
min-height: 100vh;
|
||||
background-image: url(../../assets/images/Azizi/secondBg.png);
|
||||
@ -585,15 +384,259 @@ onMounted(() => {
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.admin-content {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
padding: 18px 16px 26px;
|
||||
}
|
||||
|
||||
.admin-card,
|
||||
.leader-panel,
|
||||
.quick-action {
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
border: 1px solid rgba(212, 218, 232, 0.9);
|
||||
border-radius: 18px;
|
||||
box-shadow: 0 10px 26px rgba(37, 45, 65, 0.08);
|
||||
}
|
||||
|
||||
.leader-panel {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.leader-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.leader-avatar {
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
flex: 0 0 68px;
|
||||
padding: 3px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #bb92ff, #6fb1ff);
|
||||
}
|
||||
|
||||
.avatar-image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
background: #eef2f7;
|
||||
}
|
||||
|
||||
.leader-meta {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.leader-name {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 1.35em;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.section-kicker {
|
||||
color: rgba(31, 41, 55, 0.58);
|
||||
font-size: 0.98em;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.section-title-row,
|
||||
.income-summary,
|
||||
.team-row,
|
||||
.quick-action {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
margin-top: 3px;
|
||||
color: #111827;
|
||||
font-size: 1.32em;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.role-card,
|
||||
.income-card {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.icon-action,
|
||||
.round-action,
|
||||
.square-action,
|
||||
.quick-action {
|
||||
border: 0;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
.icon-action,
|
||||
.round-action,
|
||||
.square-action {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 0 0 auto;
|
||||
background: #f4f2ff;
|
||||
}
|
||||
|
||||
.icon-action {
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
.icon-image {
|
||||
display: block;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
.task-summary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 14px;
|
||||
padding: 12px 0;
|
||||
border-top: 1px solid rgba(229, 231, 235, 0.9);
|
||||
border-bottom: 1px solid rgba(229, 231, 235, 0.9);
|
||||
font-size: 1.08em;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.task-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding-top: 12px;
|
||||
}
|
||||
|
||||
.task-period {
|
||||
font-size: 1.02em;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.task-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
color: rgba(31, 41, 55, 0.62);
|
||||
font-size: 0.98em;
|
||||
font-weight: 650;
|
||||
}
|
||||
|
||||
.income-title {
|
||||
color: #111827;
|
||||
font-size: 1.2em;
|
||||
font-weight: 800;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.muted-text {
|
||||
color: rgba(31, 41, 55, 0.56);
|
||||
font-size: 1em;
|
||||
font-weight: 650;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.round-action {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.round-action img,
|
||||
.square-action img,
|
||||
.quick-action img {
|
||||
display: block;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
.team-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: 14px;
|
||||
border-top: 1px solid rgba(229, 231, 235, 0.9);
|
||||
}
|
||||
|
||||
.team-row {
|
||||
min-height: 72px;
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid rgba(229, 231, 235, 0.78);
|
||||
}
|
||||
|
||||
.team-row:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.team-copy {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.team-title {
|
||||
overflow-wrap: anywhere;
|
||||
color: #1f2937;
|
||||
font-size: 1.08em;
|
||||
font-weight: 800;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.team-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.square-action {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 13px;
|
||||
}
|
||||
|
||||
.quick-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.quick-action {
|
||||
width: 100%;
|
||||
min-height: 58px;
|
||||
padding: 0 16px;
|
||||
font-size: 1.08em;
|
||||
font-weight: 800;
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
.quick-action:active,
|
||||
.icon-action:active,
|
||||
.round-action:active,
|
||||
.square-action:active {
|
||||
transform: scale(0.97);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 360px) {
|
||||
* {
|
||||
font-size: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 360px) {
|
||||
* {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user