Files
MrleeAdmin/frontend/app/pages/index/index.vue
T

642 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="home-page" :class="`theme-${themeMode}`">
<view class="home-hero">
<view>
<text class="hero-title">扫码工作台</text>
<text class="hero-subtitle">高效完成生产扫码与库存管理</text>
</view>
<view class="hero-action">
<u-icon name="bell-fill" color="#1677ff" size="38" />
</view>
</view>
<view class="user-card" @tap="openProfile">
<view class="avatar">
<u-icon name="account-fill" color="#ffffff" size="54" />
</view>
<view class="user-info">
<text class="user-account">{{ member.displayName }}</text>
<text class="user-name">用户名:{{ member.username }}</text>
</view>
<view class="user-plan-pill" :class="{ disabled: !member.enabled }">{{ member.levelName }}</view>
</view>
<view class="menu-panel">
<view class="workspace-header">
<text class="workspace-title">工作区</text>
</view>
<view v-for="(section, sectionIndex) in sections" :key="section.title" class="menu-group" :class="`menu-group-${sectionIndex}`">
<view class="menu-grid">
<view v-for="item in section.items" :key="item.title" class="menu-item" hover-class="menu-item--active" @tap="handleMenuTap(item)">
<view class="icon-wrap" :style="{ backgroundColor: item.bgColor }">
<u-icon :name="item.icon" color="#ffffff" size="42" />
</view>
<text class="menu-title">{{ item.title }}</text>
<text v-if="item.desc" class="menu-desc">{{ item.desc }}</text>
</view>
</view>
</view>
</view>
<view class="chart-panel">
<view class="chart-header">
<view>
<text class="chart-title">今日数据</text>
<text class="chart-subtitle">扫码与异常概览</text>
</view>
<view class="chart-badge">实时</view>
</view>
<view class="summary-grid">
<view v-for="item in summaries" :key="item.label" class="summary-card">
<text class="summary-value">{{ item.value }}</text>
<text class="summary-label">{{ item.label }}</text>
</view>
</view>
<view class="trend-card">
<view class="chart-section-title">扫码趋势</view>
<view class="bar-chart">
<view v-for="item in trendData" :key="item.label" class="bar-item">
<view class="bar-track">
<view class="bar-fill" :style="{ height: item.percent + '%', backgroundColor: item.color }"></view>
</view>
<text class="bar-label">{{ item.label }}</text>
</view>
</view>
</view>
<view class="progress-card">
<view class="chart-section-title">异常处理</view>
<view v-for="item in exceptionData" :key="item.label" class="progress-row">
<view class="progress-info">
<text class="progress-label">{{ item.label }}</text>
<text class="progress-value">{{ item.value }}%</text>
</view>
<view class="progress-track">
<view class="progress-fill" :style="{ width: item.value + '%', backgroundColor: item.color }"></view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import { getUserInfo } from '@/api/member.js'
export default {
data() {
return {
member: {
displayName: '未登录',
username: '-',
levelName: '请先登录',
enabled: true
},
summaries: [
{ label: '订单扫码', value: '128' },
{ label: '整机扫码', value: '86' },
{ label: '异常待处理', value: '12' }
],
trendData: [
{ label: '09', percent: 42, color: '#1677ff' },
{ label: '10', percent: 68, color: '#35bfa3' },
{ label: '11', percent: 52, color: '#1677ff' },
{ label: '12', percent: 76, color: '#35bfa3' },
{ label: '13', percent: 45, color: '#1677ff' },
{ label: '14', percent: 88, color: '#35bfa3' }
],
exceptionData: [
{ label: '已处理', value: 72, color: '#19be6b' },
{ label: '待复核', value: 46, color: '#f7b733' },
{ label: '待处理', value: 28, color: '#e91f3f' }
],
sections: [
{
title: '扫码',
items: [
{ title: '订单模式', icon: 'order', bgColor: '#e91f3f', url: '/pages/entry/order' },
{ title: '一单多机', icon: 'grid-fill', bgColor: '#6b70e8', url: '/pages/entry/multi-machine' },
{ title: '整机模式', icon: 'scan', bgColor: '#1e9df2', url: '/pages/entry/machine' },
{ title: 'UTS 扫码原型', icon: 'scan', bgColor: '#1677ff', url: '/pages/scan/prototype' },
{ title: '异常处理', icon: 'warning-fill', bgColor: '#f7b733', url: '/pages/exception/index' },
{ title: '维修处理', icon: 'setting-fill', bgColor: '#35bfa3', url: '/pages/repair/index' }
]
},
{
title: '查询',
items: [
{ title: '查询', icon: 'search', bgColor: '#ef6a62', url: '/pages/query/index' },
{ title: '统计', icon: 'grid-fill', bgColor: '#1eb2e8', url: '/pages/statistics/index' },
{ title: '设置', icon: 'setting-fill', bgColor: '#9f86e8', url: '/pages/settings/index' }
]
}
]
}
},
onShow() {
this.loadMemberInfo()
},
methods: {
async loadMemberInfo() {
if (!this.isLoggedIn()) {
this.member = {
displayName: '未登录',
username: '-',
levelName: '请先登录',
enabled: true
}
return
}
try {
const profile = await getUserInfo()
uni.setStorageSync('scanapp_user', profile)
this.member = {
displayName: profile.nickname || profile.email || profile.username || '-',
username: profile.username || profile.email || '-',
levelName: profile.level_name || (profile.level && profile.level.name) || (Number(profile.enabled) === 0 ? '账号已停用' : '会员用户'),
enabled: Number(profile.enabled) !== 0
}
} catch (error) {
console.error('[scanapp] 获取首页会员信息失败', error)
}
},
isLoggedIn() {
return Boolean(uni.getStorageSync('scanapp_token'))
},
requireLogin(redirectUrl) {
if (this.isLoggedIn()) {
uni.navigateTo({
url: redirectUrl
})
return
}
uni.showModal({
title: '需要登录',
content: '登录后可访问个人资料',
confirmText: '去登录',
success: (res) => {
if (res.confirm) {
uni.navigateTo({
url: `/pages/auth/login?redirect=${encodeURIComponent(redirectUrl)}`
})
}
}
})
},
openProfile() {
this.requireLogin('/pages/profile/index')
},
handleMenuTap(item) {
if (item.url) {
uni.navigateTo({
url: item.url
})
return
}
uni.showToast({
title: `${item.title}功能待接入`,
icon: 'none'
})
}
}
}
</script>
<style lang="scss" scoped>
.home-page {
min-height: 100vh;
padding: 34rpx 24rpx 48rpx;
box-sizing: border-box;
background: linear-gradient(180deg, #eaf4ff 0%, #f4f6fb 34%, #f4f6fb 100%);
}
.home-hero {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12rpx 8rpx 30rpx;
}
.hero-title,
.hero-subtitle {
display: block;
}
.hero-title {
color: #17233d;
font-size: 38rpx;
font-weight: 800;
line-height: 1.35;
}
.hero-subtitle {
margin-top: 8rpx;
color: #7d8798;
font-size: 22rpx;
line-height: 1.4;
}
.hero-action {
display: flex;
align-items: center;
justify-content: center;
width: 72rpx;
height: 72rpx;
border-radius: 24rpx;
background: rgba(255, 255, 255, 0.9);
box-shadow: 0 12rpx 32rpx rgba(23, 35, 61, 0.08);
}
.user-card {
position: relative;
overflow: hidden;
display: flex;
align-items: center;
padding: 36rpx 28rpx;
border: 1rpx solid rgba(255, 255, 255, 0.72);
border-radius: 28rpx;
background: linear-gradient(135deg, #ffffff 0%, #f8fbff 100%);
box-shadow: 0 18rpx 44rpx rgba(23, 35, 61, 0.08);
}
.user-card::after {
position: absolute;
top: -80rpx;
right: -70rpx;
width: 180rpx;
height: 180rpx;
border-radius: 50%;
background: rgba(22, 119, 255, 0.08);
content: '';
}
.avatar {
display: flex;
align-items: center;
justify-content: center;
width: 72rpx;
height: 72rpx;
margin-right: 24rpx;
border-radius: 50%;
background: linear-gradient(135deg, #35bfa3 0%, #1677ff 100%);
box-shadow: 0 12rpx 28rpx rgba(53, 191, 163, 0.28);
}
.user-info {
display: flex;
flex: 1;
min-width: 0;
flex-direction: column;
}
.user-account {
color: #111111;
font-size: 32rpx;
font-weight: 700;
line-height: 1.35;
}
.user-name {
margin-top: 8rpx;
color: #8b8f98;
font-size: 22rpx;
line-height: 1.35;
}
.user-plan-pill {
flex: none;
margin-left: 20rpx;
padding: 6rpx 18rpx;
border-radius: 999rpx;
background: rgba(25, 190, 107, 0.1);
color: #19be6b;
font-size: 20rpx;
font-weight: 600;
line-height: 1.4;
}
.user-plan-pill.disabled {
background: rgba(233, 31, 63, 0.1);
color: #e91f3f;
}
.menu-panel {
margin-top: 34rpx;
overflow: hidden;
padding: 18rpx;
border: 1rpx solid rgba(255, 255, 255, 0.72);
border-radius: 28rpx;
background: #ffffff;
box-shadow: 0 16rpx 42rpx rgba(23, 35, 61, 0.06);
}
.workspace-header {
padding: 6rpx 8rpx 20rpx;
}
.workspace-title {
color: #17233d;
font-size: 28rpx;
font-weight: 700;
line-height: 1.35;
}
.menu-group {
overflow: hidden;
border-radius: 24rpx;
}
.menu-group + .menu-group {
margin-top: 18rpx;
}
.menu-group-0 {
background: linear-gradient(135deg, rgba(22, 119, 255, 0.08) 0%, rgba(233, 31, 63, 0.05) 100%);
}
.menu-group-1 {
background: linear-gradient(135deg, rgba(25, 190, 107, 0.08) 0%, rgba(159, 134, 232, 0.06) 100%);
}
.menu-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
.menu-item {
display: flex;
align-items: center;
justify-content: center;
min-height: 196rpx;
padding: 26rpx 10rpx;
box-sizing: border-box;
border-right: 1rpx solid #edf0f5;
border-bottom: 1rpx solid #edf0f5;
flex-direction: column;
}
.menu-item--active {
background: #f8fbff;
}
.menu-item:nth-child(3n) {
border-right: 0;
}
.menu-item:nth-last-child(-n + 3) {
border-bottom: 0;
}
.icon-wrap {
display: flex;
align-items: center;
justify-content: center;
width: 64rpx;
height: 64rpx;
margin-bottom: 28rpx;
border-radius: 50%;
box-shadow: 0 10rpx 24rpx rgba(23, 35, 61, 0.12);
}
.menu-title {
color: #111111;
font-size: 28rpx;
line-height: 1.35;
text-align: center;
}
.menu-desc {
margin-top: 4rpx;
color: #9a9fa8;
font-size: 20rpx;
line-height: 1.25;
text-align: center;
}
.chart-panel {
margin-top: 34rpx;
padding: 28rpx;
border: 1rpx solid rgba(255, 255, 255, 0.72);
border-radius: 28rpx;
background: #ffffff;
box-shadow: 0 16rpx 42rpx rgba(23, 35, 61, 0.06);
}
.chart-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 24rpx;
}
.chart-title,
.chart-subtitle {
display: block;
}
.chart-title {
color: #17233d;
font-size: 28rpx;
font-weight: 700;
line-height: 1.35;
}
.chart-subtitle {
margin-top: 6rpx;
color: #8a94a6;
font-size: 20rpx;
line-height: 1.4;
}
.chart-badge {
padding: 8rpx 18rpx;
border-radius: 999rpx;
background: rgba(22, 119, 255, 0.1);
color: #1677ff;
font-size: 20rpx;
font-weight: 600;
}
.summary-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16rpx;
margin-bottom: 22rpx;
}
.summary-card {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 112rpx;
border-radius: 22rpx;
background: #f8fbff;
}
.summary-value {
color: #17233d;
font-size: 32rpx;
font-weight: 800;
line-height: 1.3;
}
.summary-label {
margin-top: 8rpx;
color: #8a94a6;
font-size: 20rpx;
line-height: 1.3;
}
.trend-card,
.progress-card {
padding: 24rpx;
border-radius: 24rpx;
background: #f8fbff;
}
.progress-card {
margin-top: 20rpx;
}
.chart-section-title {
margin-bottom: 22rpx;
color: #17233d;
font-size: 26rpx;
font-weight: 700;
}
.bar-chart {
display: flex;
align-items: flex-end;
justify-content: space-between;
height: 210rpx;
}
.bar-item {
display: flex;
align-items: center;
flex: 1;
flex-direction: column;
}
.bar-track {
display: flex;
align-items: flex-end;
width: 28rpx;
height: 156rpx;
overflow: hidden;
border-radius: 999rpx;
background: #eaf0f8;
}
.bar-fill {
width: 100%;
border-radius: 999rpx;
}
.bar-label {
margin-top: 14rpx;
color: #8a94a6;
font-size: 20rpx;
}
.progress-row + .progress-row {
margin-top: 20rpx;
}
.progress-info {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10rpx;
}
.progress-label,
.progress-value {
font-size: 22rpx;
line-height: 1.35;
}
.progress-label {
color: #5f6b7a;
}
.progress-value {
color: #17233d;
font-weight: 700;
}
.progress-track {
height: 14rpx;
overflow: hidden;
border-radius: 999rpx;
background: #eaf0f8;
}
.progress-fill {
height: 100%;
border-radius: 999rpx;
}
.theme-dark.home-page {
background: linear-gradient(180deg, #111827 0%, #151923 34%, #151923 100%);
}
.theme-dark .hero-action {
background: #222936;
box-shadow: none;
}
.theme-dark .user-card,
.theme-dark .menu-panel,
.theme-dark .chart-panel {
border-color: #364152;
background: #222936;
box-shadow: none;
}
.theme-dark .menu-group-0,
.theme-dark .menu-group-1 {
background: #1c2431;
}
.theme-dark .menu-item {
border-color: #364152;
}
.theme-dark .menu-item--active,
.theme-dark .summary-card,
.theme-dark .trend-card,
.theme-dark .progress-card {
background: #1c2431;
}
.theme-dark .user-account,
.theme-dark .menu-title,
.theme-dark .workspace-title,
.theme-dark .chart-title,
.theme-dark .chart-section-title,
.theme-dark .summary-value,
.theme-dark .progress-value {
color: #f2f5fa;
}
.theme-dark .hero-subtitle,
.theme-dark .user-name,
.theme-dark .menu-desc,
.theme-dark .chart-subtitle,
.theme-dark .summary-label,
.theme-dark .bar-label,
.theme-dark .progress-label {
color: #aeb9ca;
}
.theme-dark .bar-track,
.theme-dark .progress-track {
background: #364152;
}
</style>