fix:移动和pc自适应菜单
This commit is contained in:
@@ -103,25 +103,25 @@ setupMixMenuContext();
|
||||
:sider-visible="siderVisible"
|
||||
:sider-width="siderWidth"
|
||||
:sider-collapsed-width="siderCollapsedWidth"
|
||||
:footer-visible="themeStore.footer.visible"
|
||||
:footer-height="themeStore.footer.height"
|
||||
:fixed-footer="themeStore.footer.fixed"
|
||||
:right-footer="themeStore.footer.right"
|
||||
:footer-visible="appStore.isMobile && themeStore.footer.visible"
|
||||
:footer-height="themeStore.footer.height"
|
||||
:fixed-footer="themeStore.footer.fixed"
|
||||
:right-footer="themeStore.footer.right"
|
||||
>
|
||||
<template #header>
|
||||
<GlobalHeader v-bind="headerProps" />
|
||||
</template>
|
||||
<template #tab>
|
||||
<GlobalTab />
|
||||
</template>
|
||||
<template #sider>
|
||||
<GlobalSider />
|
||||
</template>
|
||||
<GlobalContent />
|
||||
<ThemeDrawer />
|
||||
<template #footer>
|
||||
<GlobalFooter />
|
||||
</template>
|
||||
<template #header>
|
||||
<GlobalHeader v-bind="headerProps" />
|
||||
</template>
|
||||
<template #tab>
|
||||
<GlobalTab />
|
||||
</template>
|
||||
<template #sider>
|
||||
<GlobalSider />
|
||||
</template>
|
||||
<GlobalContent />
|
||||
<ThemeDrawer />
|
||||
<template #footer>
|
||||
<GlobalFooter v-if="appStore.isMobile" /> <!-- 修改这里 -->
|
||||
</template>
|
||||
</AdminLayout>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import {ProfileOutlined,UserOutlined,HomeOutlined} from "@ant-design/icons-vue";
|
||||
import {useRouterPush} from "@/hooks/common";
|
||||
|
||||
const { routerPushByKey } = useRouterPush();
|
||||
defineOptions({
|
||||
name: 'GlobalFooter'
|
||||
});
|
||||
@@ -6,10 +10,34 @@ defineOptions({
|
||||
|
||||
<template>
|
||||
<DarkModeContainer class="h-full flex-center">
|
||||
<a href="#" target="_blank" rel="noopener noreferrer">
|
||||
Copyright © 2024 WANFi
|
||||
</a>
|
||||
<div class="flex-item">
|
||||
<ButtonIcon class="text-icon-large" @click="routerPushByKey('home')">
|
||||
<HomeOutlined />
|
||||
</ButtonIcon>
|
||||
</div>
|
||||
<div class="flex-item">
|
||||
<ButtonIcon>
|
||||
<ProfileOutlined class="text-icon-large" @click="routerPushByKey('billing_billservice')"/>
|
||||
</ButtonIcon>
|
||||
</div>
|
||||
<div class="flex-item">
|
||||
<ButtonIcon class="text-icon-large" @click="routerPushByKey('user-info/usercard')">
|
||||
<UserOutlined />
|
||||
</ButtonIcon>
|
||||
</div>
|
||||
</DarkModeContainer>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
<style scoped>
|
||||
.flex-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.flex-item {
|
||||
flex-basis: 33.33%; /* 每个子元素占据三分之一的宽度 */
|
||||
display: flex;
|
||||
justify-content: center; /* 在各自的空间内居中 */
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -52,10 +52,16 @@ const headerMenus = computed(() => {
|
||||
<DarkModeContainer class="h-full flex-y-center shadow-header">
|
||||
<GlobalLogo v-if="showLogo" class="h-full" :style="{ width: themeStore.sider.width + 'px' }" />
|
||||
<HorizontalMenu v-if="showMenu" mode="horizontal" :menus="headerMenus" class="px-12px" />
|
||||
<div v-else class="h-full flex-y-center flex-1-hidden">
|
||||
|
||||
<!-- 只在非移动端显示菜单按钮 -->
|
||||
<div v-if="!appStore.isMobile" class="h-full flex-y-center flex-1-hidden">
|
||||
<MenuToggler v-if="showMenuToggler" :collapsed="appStore.siderCollapse" @click="appStore.toggleSiderCollapse" />
|
||||
<GlobalBreadcrumb v-if="!appStore.isMobile" class="ml-12px" />
|
||||
<GlobalBreadcrumb class="ml-12px" />
|
||||
</div>
|
||||
|
||||
<!-- 在移动端时使用这个来保持布局 -->
|
||||
<div v-else class="flex-1"></div>
|
||||
|
||||
<div class="h-full flex-y-center justify-end">
|
||||
<LangSwitch :lang="appStore.locale" :lang-options="appStore.localeOptions" @change-lang="appStore.changeLocale" />
|
||||
<FullScreen v-if="!appStore.isMobile" :full="isFullscreen" @click="toggle" />
|
||||
|
||||
@@ -10,7 +10,7 @@ export const themeSettings: App.Theme.ThemeSetting = {
|
||||
tab: {visible: false, cache: true, height: 44, mode: "chrome"},
|
||||
fixedHeaderAndTab: true,
|
||||
sider: {inverted: false, width: 220, collapsedWidth: 64, mixWidth: 90, mixCollapsedWidth: 64, mixChildMenuWidth: 200},
|
||||
footer: {visible: true, fixed: false, height: 36, right: true}
|
||||
footer: {visible: true, fixed: false, height: 50, right: true}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,18 +2,18 @@
|
||||
import HeaderBanner from './modules/header-banner.vue';
|
||||
import CardData from './modules/card-data.vue';
|
||||
import LineChart from './modules/line-chart.vue';
|
||||
import PieChart from './modules/pie-chart.vue';
|
||||
import ProjectNews from './modules/project-news.vue';
|
||||
import CreativityBanner from './modules/creativity-banner.vue';
|
||||
// import PieChart from './modules/pie-chart.vue';
|
||||
// import ProjectNews from './modules/project-news.vue';
|
||||
// import CreativityBanner from './modules/creativity-banner.vue';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ASpace direction="vertical" :size="16">
|
||||
<HeaderBanner />
|
||||
<LineChart />
|
||||
<CardData />
|
||||
<!-- <ARow :gutter="[16, 16]">-->
|
||||
<!-- <ACol :span="24" :lg="14">-->
|
||||
<!-- <LineChart />-->
|
||||
<!-- </ACol>-->
|
||||
<!-- <ACol :span="24" :lg="10">-->
|
||||
<!-- <PieChart />-->
|
||||
|
||||
@@ -1,151 +1,365 @@
|
||||
<script setup lang="ts">
|
||||
import { watch } from 'vue';
|
||||
import { $t } from '@/locales';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import { useEcharts } from '@/hooks/common/echarts';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { fetchPackageList, submitPackageOrder } from '@/service/api/auth';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'LineChart'
|
||||
const { t } = useI18n();
|
||||
|
||||
interface PackageOption {
|
||||
id: string;
|
||||
packageName: string;
|
||||
price: number;
|
||||
clientNum: number;
|
||||
traffic: number;
|
||||
trafficDisplay: string;
|
||||
isRecommended?: boolean;
|
||||
promotion?: string;
|
||||
periodNum: number;
|
||||
periodType: number;
|
||||
validityPeriod: string;
|
||||
}
|
||||
|
||||
// 添加有效期类型枚举
|
||||
const PERIOD_TYPE = {
|
||||
HOUR: 0,
|
||||
DAY: 1,
|
||||
MONTH: 2,
|
||||
YEAR: 3
|
||||
} as const;
|
||||
|
||||
// 添加有效期单位映射
|
||||
const PERIOD_UNIT = {
|
||||
[PERIOD_TYPE.HOUR]: '小时',
|
||||
[PERIOD_TYPE.DAY]: '天',
|
||||
[PERIOD_TYPE.MONTH]: '月',
|
||||
[PERIOD_TYPE.YEAR]: '年'
|
||||
} as const;
|
||||
|
||||
// 格式化有效期显示
|
||||
const formatValidityPeriod = (num: number, type: number): string => {
|
||||
const unit = PERIOD_UNIT[type as keyof typeof PERIOD_UNIT] || '未知';
|
||||
return `${num}${unit}`;
|
||||
};
|
||||
|
||||
// 流量单位转换函数
|
||||
const formatTraffic = (trafficKB: number): string => {
|
||||
const KB_TO_MB = 1024;
|
||||
const KB_TO_GB = 1024 * 1024;
|
||||
const KB_TO_TB = 1024 * 1024 * 1024;
|
||||
|
||||
if (trafficKB >= KB_TO_TB) {
|
||||
// KB -> TB (除以 1024^3)
|
||||
return `${(trafficKB / KB_TO_TB).toFixed(2)}TB`;
|
||||
}
|
||||
if (trafficKB >= KB_TO_GB) {
|
||||
// KB -> GB (除以 1024^2)
|
||||
return `${(trafficKB / KB_TO_GB).toFixed(2)}GB`;
|
||||
}
|
||||
if (trafficKB >= KB_TO_MB) {
|
||||
// KB -> MB (除以 1024)
|
||||
return `${(trafficKB / KB_TO_MB).toFixed(2)}MB`;
|
||||
}
|
||||
// 小于1MB的情况,保持KB单位
|
||||
return `${trafficKB.toFixed(2)}KB`;
|
||||
};
|
||||
|
||||
const packageOptions = ref<PackageOption[]>([]);
|
||||
const selectedPackage = ref<PackageOption>({
|
||||
id: '1',
|
||||
packageName: '',
|
||||
price: 0,
|
||||
clientNum: 0,
|
||||
traffic: 0,
|
||||
trafficDisplay: '0GB',
|
||||
isRecommended: false,
|
||||
promotion: '',
|
||||
periodNum: 0,
|
||||
periodType: PERIOD_TYPE.MONTH,
|
||||
validityPeriod: '0月'
|
||||
});
|
||||
|
||||
const appStore = useAppStore();
|
||||
const fetchPackages = async () => {
|
||||
try {
|
||||
const response = await fetchPackageList();
|
||||
if (response.data && Array.isArray(response.data)) {
|
||||
packageOptions.value = response.data.map(pkg => ({
|
||||
id: pkg.id,
|
||||
packageName: pkg.packageName,
|
||||
price: parseFloat(pkg.price),
|
||||
clientNum: Number(pkg.clientNum),
|
||||
traffic: Number(pkg.traffic),
|
||||
trafficDisplay: formatTraffic(Number(pkg.traffic)),
|
||||
isRecommended: pkg.isRecommended || false,
|
||||
promotion: pkg.promotion || '',
|
||||
periodNum: Number(pkg.periodNum),
|
||||
periodType: Number(pkg.periodType),
|
||||
validityPeriod: formatValidityPeriod(Number(pkg.periodNum), Number(pkg.periodType))
|
||||
}));
|
||||
|
||||
const { domRef, updateOptions } = useEcharts(() => ({
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
label: {
|
||||
backgroundColor: '#6a7985'
|
||||
if (packageOptions.value.length > 0) {
|
||||
selectedPackage.value = packageOptions.value[0];
|
||||
}
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: [$t('page.home.downloadCount'), $t('page.home.registerCount')]
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: [] as string[]
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
color: '#8e9dff',
|
||||
name: $t('page.home.downloadCount'),
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
stack: 'Total',
|
||||
areaStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0.25,
|
||||
color: '#8e9dff'
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: '#fff'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
data: [] as number[]
|
||||
},
|
||||
{
|
||||
color: '#26deca',
|
||||
name: $t('page.home.registerCount'),
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
stack: 'Total',
|
||||
areaStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0.25,
|
||||
color: '#26deca'
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: '#fff'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
data: []
|
||||
}
|
||||
]
|
||||
}));
|
||||
|
||||
async function mockData() {
|
||||
await new Promise(resolve => {
|
||||
setTimeout(resolve, 1000);
|
||||
});
|
||||
|
||||
updateOptions(opts => {
|
||||
opts.xAxis.data = ['06:00', '08:00', '10:00', '12:00', '14:00', '16:00', '18:00', '20:00', '22:00', '24:00'];
|
||||
opts.series[0].data = [4623, 6145, 6268, 6411, 1890, 4251, 2978, 3880, 3606, 4311];
|
||||
opts.series[1].data = [2208, 2016, 2916, 4512, 8281, 2008, 1963, 2367, 2956, 678];
|
||||
|
||||
return opts;
|
||||
});
|
||||
}
|
||||
|
||||
function updateLocale() {
|
||||
updateOptions((opts, factory) => {
|
||||
const originOpts = factory();
|
||||
|
||||
opts.legend.data = originOpts.legend.data;
|
||||
opts.series[0].name = originOpts.series[0].name;
|
||||
opts.series[1].name = originOpts.series[1].name;
|
||||
|
||||
return opts;
|
||||
});
|
||||
}
|
||||
|
||||
async function init() {
|
||||
mockData();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => appStore.locale,
|
||||
() => {
|
||||
updateLocale();
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch packages:', error);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
// init
|
||||
init();
|
||||
const selectPackage = (option: PackageOption) => {
|
||||
selectedPackage.value = option;
|
||||
};
|
||||
|
||||
// 添加办理套餐的方法
|
||||
const handleSubmitOrder = async () => {
|
||||
try {
|
||||
await submitPackageOrder(selectedPackage.value.id);
|
||||
message.success('套餐办理成功!');
|
||||
} catch (error) {
|
||||
message.error('套餐办理失败,请重试!');
|
||||
console.error('Failed to submit order:', error);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
await fetchPackages();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ACard :bordered="false" class="card-wrapper">
|
||||
<div ref="domRef" class="h-360px overflow-hidden"></div>
|
||||
</ACard>
|
||||
<div class="package-container">
|
||||
<!-- 顶部价格展示 -->
|
||||
<div class="price-header">
|
||||
<div class="price">
|
||||
<span class="currency">¥</span>
|
||||
<span class="amount">{{ selectedPackage.price }}</span>
|
||||
<span class="period">/月</span>
|
||||
</div>
|
||||
<div class="subtitle">{{ selectedPackage.packageName }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 套餐选项 -->
|
||||
<div class="package-options">
|
||||
<h3 class="section-title">{{ t('page.setmeal.changablelevel') }}</h3>
|
||||
<div class="options-grid">
|
||||
<div
|
||||
v-for="option in packageOptions"
|
||||
:key="option.id"
|
||||
:class="[
|
||||
'option-card',
|
||||
{
|
||||
recommended: option.isRecommended,
|
||||
selected: selectedPackage.id === option.id
|
||||
}
|
||||
]"
|
||||
@click="selectPackage(option)"
|
||||
>
|
||||
<div v-if="option.isRecommended" class="recommended-tag">
|
||||
{{ t('page.setmeal.highlyrecommended') }}
|
||||
</div>
|
||||
<div class="package-name">{{ option.packageName }}</div>
|
||||
<div class="price">¥{{ option.price }}</div>
|
||||
<div class="traffic">{{ option.trafficDisplay }}</div>
|
||||
<div class="device-count">{{ option.clientNum }}台设备</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 套餐详情 -->
|
||||
<div class="package-details">
|
||||
<h3 class="section-title">{{ t('page.setmeal.mealdetail') }}</h3>
|
||||
<div class="details-list">
|
||||
<div class="detail-item">
|
||||
<div class="label">套餐名称</div>
|
||||
<div class="value">{{ selectedPackage.packageName }}</div>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<div class="label">{{ t('page.setmeal.GeneralPurposeTraffic') }}</div>
|
||||
<div class="value">{{ selectedPackage.trafficDisplay }},当月有效</div>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<div class="label">设备数量</div>
|
||||
<div class="value">最多{{ selectedPackage.clientNum }}台设备同时在线</div>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<div class="label">有效期限</div>
|
||||
<div class="value">{{ selectedPackage.validityPeriod }}</div>
|
||||
</div>
|
||||
<div class="bottom-bar">
|
||||
<button
|
||||
class="btn-primary"
|
||||
@click="handleSubmitOrder"
|
||||
:disabled="!selectedPackage.id"
|
||||
>
|
||||
{{ t('page.setmeal.Applynow') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
<style scoped>
|
||||
.package-container {
|
||||
min-height: 100vh;
|
||||
padding: 16px 16px 80px;
|
||||
}
|
||||
|
||||
.price-header {
|
||||
background: #fff1f0;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.price {
|
||||
color: #ff4d4f;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.currency {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.amount {
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.period {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 16px;
|
||||
color: #333;
|
||||
position: relative;
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
.section-title::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 4px;
|
||||
height: 16px;
|
||||
background: #1890ff;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.options-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.option-card {
|
||||
background: white;
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
border: 1px solid #e8e8e8;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.option-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.option-card.selected {
|
||||
border-color: #ff4d4f;
|
||||
background: #fff1f0;
|
||||
}
|
||||
|
||||
.recommended-tag {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: #ff4d4f;
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 0 0 8px 0;
|
||||
}
|
||||
|
||||
.package-details {
|
||||
background: white;
|
||||
padding: 16px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
display: flex;
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.detail-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.label {
|
||||
width: 80px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.value {
|
||||
flex: 1;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.value.highlight {
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
.bottom-bar {
|
||||
padding: 12px 16px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #ff4d4f;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 32px;
|
||||
border-radius: 24px;
|
||||
font-size: 16px;
|
||||
width: 90%;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.package-name {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 8px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.traffic {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.device-count {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-top: 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { useAuthStore } from '@/store/modules/auth';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { computed } from 'vue';
|
||||
import { UserOutlined, LockOutlined, SafetyCertificateOutlined, MobileOutlined, RightOutlined} from '@ant-design/icons-vue';
|
||||
import { UserOutlined, LockOutlined, SafetyCertificateOutlined, MobileOutlined, RightOutlined, LinkOutlined, ApiOutlined, CalendarOutlined} from '@ant-design/icons-vue';
|
||||
import { useRouterPush } from '@/hooks/common/router';
|
||||
import {useI18n} from "vue-i18n";
|
||||
|
||||
@@ -42,7 +42,22 @@ const menuItems = [
|
||||
icon: MobileOutlined,
|
||||
title: t('page.usercard.deviceconsole'),
|
||||
path: 'device'
|
||||
}
|
||||
},
|
||||
{
|
||||
icon: LinkOutlined,
|
||||
title: t('page.usercard.access'),
|
||||
path: 'access'
|
||||
},
|
||||
{
|
||||
icon: ApiOutlined,
|
||||
title: t('page.usercard.records'),
|
||||
path: 'records'
|
||||
},
|
||||
{
|
||||
icon: CalendarOutlined,
|
||||
title: t('page.usercard.cdrlrecords'),
|
||||
path: 'cdrlrecords'
|
||||
},
|
||||
];
|
||||
|
||||
const handleMenuClick = (path: string) => {
|
||||
|
||||
Reference in New Issue
Block a user