Files
PartsInquiry/frontend/pages/my/index.vue
2025-09-24 20:35:15 +08:00

286 lines
12 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="me">
<view v-if="!isLoggedIn" class="card login">
<view class="login-title">登录/注册以同步数据</view>
<button class="login-btn" type="primary" @click="goLogin">登录</button>
<button class="login-btn minor" @click="goRegister">注册</button>
</view>
<view class="card user">
<image class="avatar" :src="avatarUrl" mode="aspectFill" @error="onAvatarError" />
<view class="meta">
<text class="name">{{ shopName }}</text>
<text class="phone">{{ mobileDisplay }}</text>
<text class="role">老板</text>
</view>
</view>
<!-- VIP 卡片置于会员与订单分组上方 -->
<view class="card vip" :class="{ active: vipIsVip }">
<view class="vip-row">
<text class="vip-badge">{{ vipIsVip ? 'VIP' : '非VIP' }}</text>
<text class="vip-title">会员状态</text>
</view>
<view class="vip-meta">
<view class="item">
<text class="label">开始</text>
<text class="value">{{ vipStartDisplay }}</text>
</view>
<view class="item">
<text class="label">结束</text>
<text class="value">{{ vipEndDisplay }}</text>
</view>
</view>
</view>
<view class="group">
<view class="group-title">会员与订单</view>
<view class="cell" @click="goVip">
<text>VIP会员</text>
<text class="arrow"></text>
</view>
<view class="cell" @click="goMyOrders">
<text>我的订单</text>
<text class="arrow"></text>
</view>
</view>
<view class="group">
<view class="group-title">基础管理</view>
<view class="cell" @click="goSupplier">
<text>供应商管理</text>
<text class="arrow"></text>
</view>
<view class="cell" @click="goCustomer">
<text>客户管理</text>
<text class="arrow"></text>
</view>
<view class="cell" @click="goCustomerQuote">
<text>客户报价</text>
<text class="arrow"></text>
</view>
<view class="cell" @click="goShop">
<text>店铺管理</text>
<text class="arrow"></text>
</view>
</view>
<view class="group">
<view class="group-title">设置中心</view>
<view class="cell" @click="editProfile">
<text>账号与安全</text>
<text class="desc">修改头像姓名密码</text>
<text class="arrow"></text>
</view>
<view class="cell" @click="goProductSettings">
<text>商品设置</text>
<text class="arrow"></text>
</view>
<view class="cell" @click="goSystemParams">
<text>系统参数</text>
<text class="desc">低价提示默认收款单行折扣等</text>
<text class="arrow"></text>
</view>
<view class="cell" @click="goAbout">
<text>关于与协议</text>
<text class="arrow"></text>
</view>
<view v-if="isLoggedIn" class="cell danger" @click="logout">
<text>退出登录</text>
</view>
</view>
</view>
</template>
<script>
import { get } from '../../common/http.js'
export default {
data() {
return {
avatarUrl: '/static/logo.png',
shopName: '未登录',
mobile: '',
pendingJsCode: '',
logging: false,
vipIsVip: false,
vipStart: '',
vipEnd: ''
}
},
onShow() {
this.fetchProfile()
this.loadVipFromStorage()
try {
if (uni.getStorageSync('TOKEN')) {
// 已登录时刷新资料并隐藏登录卡片
this.$forceUpdate && this.$forceUpdate()
}
} catch(e) {}
},
computed: {
isLoggedIn() { try { return !!uni.getStorageSync('TOKEN') } catch(e){ return false } },
mobileDisplay() {
const m = String(this.mobile || '')
return m.length === 11 ? m.slice(0,3) + '****' + m.slice(7) : (m || '未绑定手机号')
},
vipStartDisplay() { return this.formatDisplay(this.vipStart) },
vipEndDisplay() { return this.formatDisplay(this.vipEnd) }
},
methods: {
// 登录相关方法已移除
async fetchProfile() {
// 未登录则不触发任何用户/店铺接口,也不加载本地用户字段
const hasToken = (() => { try { return !!uni.getStorageSync('TOKEN') } catch(e){ return false } })()
if (!hasToken) {
this.shopName = '未登录'
this.avatarUrl = '/static/logo.png'
this.mobile = ''
return
}
// 已登录:拉取概览以确认在线状态,并回填本地用户信息
try { await get('/api/dashboard/overview') } catch(e) {}
try {
const storeName = uni.getStorageSync('SHOP_NAME') || ''
const avatar = uni.getStorageSync('USER_AVATAR') || ''
const phone = uni.getStorageSync('USER_MOBILE') || ''
if (storeName) this.shopName = storeName
if (avatar) this.avatarUrl = avatar
this.mobile = phone
} catch(e) {}
},
loadVipFromStorage() {
try {
const isVip = String(uni.getStorageSync('USER_VIP_IS_VIP') || 'false').toLowerCase() === 'true'
const start = uni.getStorageSync('USER_VIP_START') || ''
const end = uni.getStorageSync('USER_VIP_END') || ''
this.vipIsVip = isVip
this.vipStart = start
this.vipEnd = end
} catch(e) {}
},
formatDisplay(value) {
if (!value) return '-'
const s = String(value)
// 简单规范化:只保留到分钟
const m = s.match(/^(\d{4}-\d{2}-\d{2})([ T](\d{2}:\d{2}))/)
if (m) return `${m[1]} ${m[3]}`
return s
},
startLogin() {
if (this.logging) return
this.logging = true
const tryOnce = async () => ({})
uni.login({ provider: 'weixin', success: async (res) => {
this.pendingJsCode = res.code || ''
if (!this.pendingJsCode) { this.logging = false; return uni.showToast({ title: '获取登录code失败', icon: 'none' }) }
try {
await tryOnce()
} catch(e) {
const msg = (e && e.message) || ''
if (msg.includes('40163') || msg.toLowerCase().includes('been used')) {
// 40163换新 code 再试一次
uni.login({ provider: 'weixin', success: async (r2) => {
const fresh = r2.code || ''
if (!fresh) { this.logging = false; return }
try {
await tryOnce()
} finally { this.logging = false }
} })
return
}
} finally {
this.logging = false
}
}, fail: () => { this.logging = false; uni.showToast({ title: '微信登录失败', icon: 'none' }) } })
},
goLogin(){ uni.navigateTo({ url: '/pages/auth/login' }) },
goRegister(){ uni.navigateTo({ url: '/pages/auth/register' }) },
onGetPhoneNumber(e) {
if (this.logging) return
this.logging = true
const phoneCode = ''
// 为避免 40163code been used此处重新获取一次 jsCode
uni.login({ provider: 'weixin', success: (res) => {
const jsCode = res.code || ''
if (!jsCode) { this.logging = false; return uni.showToast({ title: '获取登录code失败', icon: 'none' }) }
Promise.resolve().finally(() => { this.logging = false })
}, fail: () => { this.logging = false; uni.showToast({ title: '微信登录失败', icon: 'none' }) } })
},
goSmsLogin(){ uni.navigateTo({ url: '/pages/my/sms-login' }) },
onAvatarError() {
this.avatarUrl = '/static/logo.png'
},
goVip() { uni.navigateTo({ url: '/pages/my/vip' }) },
goMyOrders() { uni.switchTab({ url: '/pages/detail/index' }) },
goSupplier() { uni.navigateTo({ url: '/pages/supplier/select' }) },
goCustomer() { uni.navigateTo({ url: '/pages/customer/select' }) },
goCustomerQuote() { uni.showToast({ title: '客户报价(开发中)', icon: 'none' }) },
goShop() { uni.showToast({ title: '店铺管理(开发中)', icon: 'none' }) },
editProfile() { uni.showToast({ title: '账号与安全(开发中)', icon: 'none' }) },
goProductSettings() { uni.navigateTo({ url: '/pages/product/settings' }) },
goSystemParams() { uni.showToast({ title: '系统参数(开发中)', icon: 'none' }) },
goAbout() { uni.navigateTo({ url: '/pages/my/about' }) },
logout() {
try {
uni.removeStorageSync('TOKEN')
uni.removeStorageSync('LOGINED')
uni.removeStorageSync('LOGIN_PHONE')
uni.removeStorageSync('DEFAULT_USER_ID')
uni.setStorageSync('ENABLE_DEFAULT_USER', 'false')
uni.removeStorageSync('USER_AVATAR')
uni.removeStorageSync('USER_NAME')
uni.removeStorageSync('USER_MOBILE')
uni.removeStorageSync('SHOP_NAME')
uni.showToast({ title: '已清理本地信息', icon: 'none' })
setTimeout(() => { uni.reLaunch({ url: '/pages/index/index' }) }, 300)
} catch(e) { uni.reLaunch({ url: '/pages/index/index' }) }
}
}
}
</script>
<style lang="scss">
.me { padding: 24rpx; }
.card.login { display: flex; flex-direction: column; gap: 16rpx; padding: 22rpx; background: $uni-bg-color-grey; border-radius: 16rpx; margin-bottom: 24rpx; }
.login-title { font-size: 28rpx; font-weight: 700; }
.login-btn { }
.login-btn.minor { background: $uni-bg-color-hover; color: $uni-text-color; }
.hint { font-size: 22rpx; color: $uni-text-color-grey; }
.card.user { display: flex; gap: 18rpx; padding: 22rpx; background: $uni-bg-color-grey; border-radius: 16rpx; box-shadow: 0 6rpx 16rpx rgba(0,0,0,0.16); align-items: center; }
.avatar { width: 120rpx; height: 120rpx; border-radius: 60rpx; background: $uni-bg-color-hover; }
.meta { display: flex; flex-direction: column; gap: 6rpx; }
.name { font-size: 34rpx; font-weight: 700; color: $uni-text-color; }
.phone { font-size: 26rpx; color: $uni-text-color-grey; }
.role { font-size: 22rpx; color: $uni-text-color-grey; }
/* VIP 卡片样式 */
.card.vip { margin-top: 24rpx; padding: 22rpx; background: $uni-bg-color-grey; border-radius: 16rpx; box-shadow: 0 6rpx 16rpx rgba(0,0,0,0.12); }
.card.vip.active { border: 1rpx solid rgba(255, 208, 0, 0.6); background-image: radial-gradient(60% 60% at 80% 0%, rgba(255, 214, 0, 0.08), transparent 60%); }
.vip-row { display: flex; align-items: center; gap: 12rpx; margin-bottom: 10rpx; }
.vip-badge { background: #f1c40f; color: #111; font-weight: 800; padding: 2rpx 10rpx; border-radius: 8rpx; font-size: 22rpx; }
.vip-title { font-size: 28rpx; font-weight: 700; color: $uni-text-color; }
.vip-meta { display: grid; grid-template-columns: 1fr 1fr; gap: 8rpx 16rpx; }
.vip-meta .item { display: flex; align-items: center; gap: 10rpx; }
.vip-meta .label { width: 80rpx; color: $uni-text-color-grey; font-size: 24rpx; }
.vip-meta .value { color: $uni-text-color; font-size: 26rpx; word-break: break-all; }
.group { margin-top: 24rpx; background: $uni-bg-color-grey; border-radius: 16rpx; overflow: hidden; }
.group-title { padding: 18rpx 22rpx; font-size: 26rpx; color: $uni-text-color-grey; background: $uni-bg-color-hover; }
.cell { display: flex; align-items: center; padding: 26rpx 22rpx; border-top: 1rpx solid $uni-border-color; color: $uni-text-color; }
.cell .desc { margin-left: auto; margin-right: 8rpx; font-size: 22rpx; color: $uni-text-color-grey; }
.cell .arrow { margin-left: auto; color: #99a2b3; }
.cell.danger { color: #dd524d; justify-content: center; font-weight: 700; }
/* 简易对话框样式 */
.dialog-mask { position: fixed; left: 0; right: 0; top: 0; bottom: 0; background: rgba(0,0,0,0.45); display: flex; align-items: center; justify-content: center; z-index: 999; }
.dialog { width: 600rpx; background: #fff; border-radius: 16rpx; padding: 24rpx; box-shadow: 0 8rpx 24rpx rgba(0,0,0,0.2); }
.dialog-title { font-size: 30rpx; font-weight: 700; margin-bottom: 16rpx; }
.dialog-input { width: 100%; height: 72rpx; padding: 0 16rpx; border: 1rpx solid $uni-border-color; border-radius: 10rpx; background: #fff; color: $uni-text-color; }
.dialog-actions { display: flex; gap: 16rpx; margin-top: 18rpx; justify-content: flex-end; }
.dialog-btn { padding: 16rpx 22rpx; border-radius: 10rpx; }
.dialog-btn.cancel { background: $uni-bg-color-hover; color: $uni-text-color; }
.dialog-btn.confirm { background: #2979ff; color: #fff; }
</style>