286 lines
12 KiB
Vue
286 lines
12 KiB
Vue
<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 = ''
|
||
// 为避免 40163(code 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>
|
||
|
||
|