|
|
@@ -1,242 +1,262 @@
|
|
|
<template>
|
|
|
<view class="container">
|
|
|
- <!-- 用户信息区域 -->
|
|
|
- <view class="user-info">
|
|
|
- <button class="avatar-button" open-type="chooseAvatar" @chooseavatar="handleChooseAvatar" @click="handleWxLogin">
|
|
|
+ <!-- 顶部用户信息 -->
|
|
|
+ <view class="header">
|
|
|
+ <button class="avatar-wrapper" open-type="chooseAvatar" @chooseavatar="handleChooseAvatar" @click="handleWxLogin">
|
|
|
<image class="avatar" :src="userInfo.avatarUrl || '/static/image/M.png'" mode="aspectFill"></image>
|
|
|
</button>
|
|
|
- <view class="nickname">{{ userInfo.nickName || '未登录' }}</view>
|
|
|
+ <text class="username">{{ userStore.userInfo?.username || '未登录' }}</text>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 数据统计 -->
|
|
|
+ <view class="stats">
|
|
|
+ <view class="stat-item">
|
|
|
+ <text class="value">{{ inventoryCount }}</text>
|
|
|
+ <text class="label">当前库存</text>
|
|
|
+ </view>
|
|
|
+ <view class="stat-item">
|
|
|
+ <text class="value">{{ pendingOrders }}</text>
|
|
|
+ <text class="label">待处理订单</text>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
|
|
|
<!-- 功能入口 -->
|
|
|
<view class="menu-list">
|
|
|
- <navigator url="/pages/order/index" class="menu-item">
|
|
|
- <image class="icon" src="/static/icons/order.png"></image>
|
|
|
- <text>企业订单</text>
|
|
|
- <image class="arrow" src="/static/icons/arrow-right.png"></image>
|
|
|
- </navigator>
|
|
|
<navigator url="/pages/inventory/index" class="menu-item">
|
|
|
<image class="icon" src="/static/icons/inventory.png"></image>
|
|
|
- <text>库存管理</text>
|
|
|
+ <text class="title">库存管理</text>
|
|
|
+ <view class="badge">{{ inventoryPending }}项需处理</view>
|
|
|
<image class="arrow" src="/static/icons/arrow-right.png"></image>
|
|
|
</navigator>
|
|
|
- <navigator url="/pages/settings/index" class="menu-item">
|
|
|
- <image class="icon" src="/static/icons/settings_light.png"></image>
|
|
|
- <text>设置</text>
|
|
|
+
|
|
|
+ <navigator url="/pages/order/index" class="menu-item">
|
|
|
+ <image class="icon" src="/static/icons/order.png"></image>
|
|
|
+ <text class="title">企业订单</text>
|
|
|
+ <view class="badge">{{ orderPending }}条新订单</view>
|
|
|
<image class="arrow" src="/static/icons/arrow-right.png"></image>
|
|
|
</navigator>
|
|
|
</view>
|
|
|
+
|
|
|
+ <!-- 退出登录 -->
|
|
|
<view class="logout-container">
|
|
|
- <button class="logout-button" @click="handleLogout">退出登录</button>
|
|
|
+ <button class="logout-btn" @click="handleLogout">退出登录</button>
|
|
|
</view>
|
|
|
</view>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
-import { ref, onMounted } from 'vue'
|
|
|
+import { ref, computed } from 'vue'
|
|
|
import { useUserStore } from '@/stores/user'
|
|
|
-import type { WxLoginResponse } from '@/api/types'
|
|
|
-import { wxLogin, saveUserInfo, wxDecrypt } from '@/api'
|
|
|
+import { wxLogin } from '@/api'
|
|
|
+
|
|
|
+interface UserInventory {
|
|
|
+ status: string
|
|
|
+}
|
|
|
+
|
|
|
+interface UserOrder {
|
|
|
+ status: string
|
|
|
+}
|
|
|
|
|
|
const userStore = useUserStore()
|
|
|
|
|
|
+// 用户信息
|
|
|
const userInfo = ref({
|
|
|
- avatarUrl: '',
|
|
|
- nickName: '',
|
|
|
+ avatarUrl: userStore.userInfo?.avatarUrl || '',
|
|
|
+})
|
|
|
+
|
|
|
+// 类型安全的计算属性
|
|
|
+const inventoryCount = computed(() => (userStore as any).inventory?.length || 0)
|
|
|
+const pendingOrders = computed(() => {
|
|
|
+ const orders = ((userStore as any).orders as UserOrder[]) || []
|
|
|
+ return orders.filter((o) => o.status === 'pending').length
|
|
|
+})
|
|
|
+const inventoryPending = computed(() => {
|
|
|
+ const inventory = ((userStore as any).inventory as UserInventory[]) || []
|
|
|
+ return inventory.filter((i) => i.status === 'low').length
|
|
|
+})
|
|
|
+const orderPending = computed(() => {
|
|
|
+ const orders = ((userStore as any).orders as UserOrder[]) || []
|
|
|
+ return orders.filter((o) => o.status === 'new').length
|
|
|
})
|
|
|
|
|
|
// 微信登录
|
|
|
const handleWxLogin = () => {
|
|
|
- uni.showLoading({
|
|
|
- title: '登录中...',
|
|
|
- mask: true,
|
|
|
- })
|
|
|
+ uni.showLoading({ title: '登录中...', mask: true })
|
|
|
|
|
|
uni.login({
|
|
|
provider: 'weixin',
|
|
|
- success: async (loginRes) => {
|
|
|
+ success: async (res) => {
|
|
|
try {
|
|
|
- // 获取code
|
|
|
- const code = loginRes.code as string
|
|
|
- // 获取系统登录token
|
|
|
- const token = userStore.token
|
|
|
- if (!token) {
|
|
|
- uni.showToast({
|
|
|
- title: '请先登录系统',
|
|
|
- icon: 'none',
|
|
|
- })
|
|
|
- uni.reLaunch({
|
|
|
- url: '/pages/login/index',
|
|
|
- })
|
|
|
+ const code = res.code
|
|
|
+ if (!userStore.token) {
|
|
|
+ uni.showToast({ title: '请先登录系统', icon: 'none' })
|
|
|
+ uni.reLaunch({ url: '/pages/login/index' })
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- // 调用后端接口获取openid,传递系统token
|
|
|
const result = await wxLogin(code)
|
|
|
- console.log('result:', result)
|
|
|
- if (result.openid) {
|
|
|
- // 已通过handleUserAuth获取用户信息
|
|
|
+ if (result.session_key) {
|
|
|
userStore.setSessionKey(result.session_key)
|
|
|
- //await getUserProfile()
|
|
|
}
|
|
|
} catch (err) {
|
|
|
uni.hideLoading()
|
|
|
- uni.showToast({
|
|
|
- title: '登录失败',
|
|
|
- icon: 'none',
|
|
|
- })
|
|
|
- console.error('wxLogin error:', err)
|
|
|
+ uni.showToast({ title: '登录失败', icon: 'none' })
|
|
|
+ console.error('微信登录失败:', err)
|
|
|
}
|
|
|
},
|
|
|
fail: (err: any) => {
|
|
|
uni.hideLoading()
|
|
|
- uni.showToast({
|
|
|
- title: '微信登录失败',
|
|
|
- icon: 'none',
|
|
|
- })
|
|
|
- console.error('wxLogin fail:', err)
|
|
|
+ uni.showToast({ title: '授权失败', icon: 'none' })
|
|
|
+ console.error('微信登录失败:', err)
|
|
|
},
|
|
|
})
|
|
|
}
|
|
|
|
|
|
-// 处理选择头像
|
|
|
-const handleChooseAvatar = (e: any) => {
|
|
|
- const avatarUrl = e.detail.avatarUrl
|
|
|
- userInfo.value.avatarUrl = avatarUrl
|
|
|
- userStore.setUserInfo({
|
|
|
- ...userStore.userInfo,
|
|
|
- avatarUrl: avatarUrl,
|
|
|
- })
|
|
|
- uni.showToast({
|
|
|
- title: '头像更新成功',
|
|
|
- icon: 'success',
|
|
|
- })
|
|
|
+// 处理头像选择
|
|
|
+const handleChooseAvatar = async (e: { detail: { avatarUrl: string } }) => {
|
|
|
+ try {
|
|
|
+ const avatarUrl = e.detail.avatarUrl
|
|
|
+ userInfo.value.avatarUrl = avatarUrl
|
|
|
+ userStore.setUserInfo({
|
|
|
+ ...userStore.userInfo,
|
|
|
+ avatarUrl,
|
|
|
+ })
|
|
|
+ } catch (err) {
|
|
|
+ console.error('头像更新失败:', err)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// 退出登录
|
|
|
const handleLogout = () => {
|
|
|
- uni.showModal({
|
|
|
- title: '提示',
|
|
|
- content: '确定要退出登录吗?',
|
|
|
- success: (res) => {
|
|
|
- if (res.confirm) {
|
|
|
- // 清除token
|
|
|
- userStore.clearToken()
|
|
|
- // 跳转到登录页
|
|
|
- uni.reLaunch({
|
|
|
- url: '/pages/login/index',
|
|
|
- })
|
|
|
- }
|
|
|
- },
|
|
|
- })
|
|
|
+ userStore.clearToken()
|
|
|
+ uni.reLaunch({ url: '/pages/login/index' })
|
|
|
}
|
|
|
-
|
|
|
-// 页面加载时检查授权状态
|
|
|
-onMounted(() => {
|
|
|
- //checkAuth()
|
|
|
-})
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
-// 导入全局变量
|
|
|
-@use '~@/styles/variables.scss' as *;
|
|
|
-
|
|
|
-// 页面容器
|
|
|
.container {
|
|
|
- padding: 20rpx;
|
|
|
+ padding: 30rpx;
|
|
|
+ background: #f8f8f8;
|
|
|
+ min-height: 100vh;
|
|
|
+}
|
|
|
|
|
|
- // 用户信息区域
|
|
|
- .user-info {
|
|
|
+.header {
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 24rpx;
|
|
|
+ padding: 40rpx 0;
|
|
|
+ margin-bottom: 30rpx;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ .avatar-wrapper {
|
|
|
+ width: 160rpx;
|
|
|
+ height: 160rpx;
|
|
|
+ border-radius: 50%;
|
|
|
+ border: 4rpx solid #f1f1f1;
|
|
|
+ overflow: hidden;
|
|
|
+
|
|
|
+ .avatar {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .username {
|
|
|
+ font-size: 36rpx;
|
|
|
+ color: #333;
|
|
|
+ font-weight: 500;
|
|
|
+ margin-top: 20rpx;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.stats {
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 24rpx;
|
|
|
+ padding: 30rpx 0;
|
|
|
+ margin-bottom: 30rpx;
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-around;
|
|
|
+
|
|
|
+ .stat-item {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
align-items: center;
|
|
|
- padding: 40rpx;
|
|
|
- background: $white;
|
|
|
- border-radius: 16rpx;
|
|
|
- margin-bottom: 20rpx;
|
|
|
|
|
|
- .avatar {
|
|
|
- position: absolute;
|
|
|
- z-index: 5;
|
|
|
- width: 120rpx;
|
|
|
- height: 120rpx;
|
|
|
- left: 0;
|
|
|
- border-radius: 50%;
|
|
|
- cursor: pointer;
|
|
|
- transition: transform 0.2s ease;
|
|
|
- &:active {
|
|
|
- transform: scale(0.95);
|
|
|
- }
|
|
|
+ .value {
|
|
|
+ font-size: 48rpx;
|
|
|
+ color: #007aff;
|
|
|
+ font-weight: bold;
|
|
|
+ margin-bottom: 10rpx;
|
|
|
}
|
|
|
|
|
|
- .avatar-button {
|
|
|
- position: relative;
|
|
|
- top: 10%;
|
|
|
- width: 120rpx;
|
|
|
- height: 120rpx;
|
|
|
- border: none;
|
|
|
- outline: none;
|
|
|
- background: transparent;
|
|
|
- cursor: pointer;
|
|
|
- z-index: 10;
|
|
|
- border-radius: 50%;
|
|
|
- overflow: hidden;
|
|
|
- padding: 0;
|
|
|
- }
|
|
|
- .nickname {
|
|
|
- margin-top: 20rpx;
|
|
|
- font-size: 36rpx;
|
|
|
- color: $gray-333;
|
|
|
- font-weight: 500;
|
|
|
+ .label {
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #666;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// 菜单列表
|
|
|
.menu-list {
|
|
|
- background: $white;
|
|
|
- border-radius: 16rpx;
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 24rpx;
|
|
|
+ padding: 0 30rpx;
|
|
|
|
|
|
.menu-item {
|
|
|
- @include flex-center;
|
|
|
- padding: 30rpx;
|
|
|
- border-bottom: 1rpx solid $gray-f5;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 30rpx 0;
|
|
|
+ border-bottom: 1rpx solid #eee;
|
|
|
|
|
|
&:last-child {
|
|
|
border-bottom: none;
|
|
|
}
|
|
|
|
|
|
.icon {
|
|
|
- width: 40rpx;
|
|
|
- height: 40rpx;
|
|
|
+ width: 48rpx;
|
|
|
+ height: 48rpx;
|
|
|
+ margin-right: 20rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .title {
|
|
|
+ flex: 1;
|
|
|
+ font-size: 32rpx;
|
|
|
+ color: #333;
|
|
|
+ }
|
|
|
+
|
|
|
+ .badge {
|
|
|
+ background: #ff5a5f;
|
|
|
+ color: #fff;
|
|
|
+ font-size: 24rpx;
|
|
|
+ padding: 6rpx 16rpx;
|
|
|
+ border-radius: 24rpx;
|
|
|
margin-right: 20rpx;
|
|
|
}
|
|
|
|
|
|
.arrow {
|
|
|
- width: 24rpx;
|
|
|
- height: 24rpx;
|
|
|
- margin-left: auto;
|
|
|
+ width: 32rpx;
|
|
|
+ height: 32rpx;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
.logout-container {
|
|
|
- position: fixed;
|
|
|
- bottom: 40rpx;
|
|
|
- left: 50%;
|
|
|
- transform: translateX(-50%);
|
|
|
- width: 90%;
|
|
|
-}
|
|
|
+ margin-top: 60rpx;
|
|
|
+ padding: 0 30rpx;
|
|
|
|
|
|
-.logout-button {
|
|
|
- width: 100%;
|
|
|
- height: 80rpx;
|
|
|
- background-color: $gray-ccc;
|
|
|
- color: $gray-333;
|
|
|
- border-radius: 8rpx;
|
|
|
- font-size: 32rpx;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- box-shadow: 0 4rpx 8rpx rgba(0, 0, 0, 0.1);
|
|
|
+ .logout-btn {
|
|
|
+ width: 100%;
|
|
|
+ height: 88rpx;
|
|
|
+ line-height: 88rpx;
|
|
|
+ background: #ff5a5f;
|
|
|
+ color: #fff;
|
|
|
+ font-size: 32rpx;
|
|
|
+ border-radius: 48rpx;
|
|
|
+ border: none;
|
|
|
+
|
|
|
+ &:active {
|
|
|
+ opacity: 0.9;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|