对接uniapp端登录[C

This commit is contained in:
柳清爽
2025-03-27 16:01:29 +08:00
parent aa59dd79c1
commit 2b674966fe
15 changed files with 381 additions and 140 deletions

3
Cunkebao/.env.production Normal file
View File

@@ -0,0 +1,3 @@
# 生产环境配置
VUE_APP_BASE_API = 'https://api.cunkebao.com'
VUE_APP_ENV = 'production'

3
Cunkebao/.gitignore vendored
View File

@@ -1 +1,2 @@
node_modules
node_modules
.env

View File

@@ -1,6 +1,6 @@
# 客宝 UniApp
# 客宝 UniApp
基于uni-app框架开发的客宝移动端应用支持H5、微信小程序、App等多端部署。
基于uni-app框架开发的客宝移动端应用支持H5、微信小程序、App等多端部署。
## 项目结构

View File

@@ -3,14 +3,14 @@ import request from '@/utils/request'
/**
* 用户登录
* @param {Object} data 登录数据
* @param {string} data.username 用户名
* @param {string} data.account 账号(手机号)
* @param {string} data.password 密码
* @param {boolean} data.is_encrypted 密码是否已加密
* @param {number} data.typeId 用户类型
* @returns {Promise} 登录结果
*/
export function login(data) {
return request({
url: '/api/auth/login',
url: '/v1/auth/login',
method: 'POST',
data
})
@@ -19,13 +19,14 @@ export function login(data) {
/**
* 手机号验证码登录
* @param {Object} data 登录数据
* @param {string} data.mobile 手机号
* @param {string} data.account 手机号
* @param {string} data.code 验证码
* @param {number} data.typeId 用户类型
* @returns {Promise} 登录结果
*/
export function mobileLogin(data) {
return request({
url: '/api/auth/mobile-login',
url: '/v1/auth/mobile-login',
method: 'POST',
data
})
@@ -34,13 +35,13 @@ export function mobileLogin(data) {
/**
* 发送验证码
* @param {Object} data 数据
* @param {string} data.mobile 手机号
* @param {string} data.account 手机号
* @param {string} data.type 验证码类型(login:登录,register:注册)
* @returns {Promise} 发送结果
*/
export function sendCode(data) {
return request({
url: '/api/auth/code',
url: '/v1/auth/code',
method: 'POST',
data
})
@@ -52,7 +53,7 @@ export function sendCode(data) {
*/
export function getUserInfo() {
return request({
url: '/api/auth/info',
url: '/v1/auth/info',
method: 'GET'
})
}
@@ -63,7 +64,7 @@ export function getUserInfo() {
*/
export function refreshToken() {
return request({
url: '/api/auth/refresh',
url: '/v1/auth/refresh',
method: 'POST'
})
}
@@ -86,7 +87,7 @@ export function logout() {
*/
export function wechatLogin(data) {
return request({
url: '/api/auth/wechat-login',
url: '/v1/auth/wechat-login',
method: 'POST',
data
})
@@ -100,7 +101,7 @@ export function wechatLogin(data) {
*/
export function appleLogin(data) {
return request({
url: '/api/auth/apple-login',
url: '/v1/auth/apple-login',
method: 'POST',
data
})

View File

@@ -1,7 +1,7 @@
{
"name" : "客宝",
"name" : "客宝",
"appid" : "",
"description" : "客宝应用",
"description" : "客宝应用",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
@@ -69,6 +69,6 @@
"enable" : true
}
},
"title" : "客宝"
"title" : "客宝"
}
}

View File

@@ -1,7 +1,7 @@
{
"name": "cunkebao",
"version": "1.0.0",
"description": "客宝 - 基于 uni-app 的跨平台应用",
"description": "客宝 - 基于 uni-app 的跨平台应用",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"

View File

@@ -108,7 +108,7 @@
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "客宝",
"navigationBarTitleText": "客宝",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
},

View File

@@ -6,13 +6,13 @@
></u-navbar>
<view class="content">
<view class="title">客宝隐私政策</view>
<view class="title">客宝隐私政策</view>
<view class="date">生效日期2023年1月1日</view>
<view class="section">
<view class="section-title">引言</view>
<view class="paragraph">
客宝以下简称"我们"非常重视您的隐私和个人信息保护本隐私政策旨在向您说明我们如何收集使用存储共享和保护您的个人信息以及您享有的相关权利
客宝以下简称"我们"非常重视您的隐私和个人信息保护本隐私政策旨在向您说明我们如何收集使用存储共享和保护您的个人信息以及您享有的相关权利
</view>
<view class="paragraph">
请您在使用我们的服务前仔细阅读并了解本隐私政策的全部内容如您对本隐私政策有任何疑问可随时联系我们的客服

View File

@@ -6,26 +6,26 @@
></u-navbar>
<view class="content">
<view class="title">客宝用户协议</view>
<view class="title">客宝用户协议</view>
<view class="date">生效日期2023年1月1日</view>
<view class="section">
<view class="section-title">总则</view>
<view class="section-title">协议的接受与变更</view>
<view class="paragraph">
1.1 客宝用户协议以下简称"本协议"是您与客宝平台以下简称"我们"之间就客宝平台服务等相关事宜所订立的契约
1.1 客宝用户协议以下简称"本协议"是您与客宝平台以下简称"我们"之间就客宝平台服务等相关事宜所订立的契约
</view>
<view class="paragraph">
1.2 您应当在使用客宝平台服务之前认真阅读本协议全部内容如您对本协议有任何疑问可随时咨询我们的客服
1.2 您应当在使用客宝平台服务之前认真阅读本协议全部内容如您对本协议有任何疑问可随时咨询我们的客服
</view>
<view class="paragraph">
1.3 您点击"同意""下一步"或您使用客宝平台服务即视为您已阅读并同意签署本协议本协议自您确认同意之时起生效
1.3 您点击"同意""下一步"或您使用客宝平台服务即视为您已阅读并同意签署本协议本协议自您确认同意之时起生效
</view>
</view>
<view class="section">
<view class="section-title">账号注册与使用</view>
<view class="paragraph">
2.1 您应当保证您具有完全民事行为能力能够独立承担民事责任并独立承担使用客宝平台服务的一切法律责任
2.1 您应当保证您具有完全民事行为能力能够独立承担民事责任并独立承担使用客宝平台服务的一切法律责任
</view>
<view class="paragraph">
2.2 您注册成功后我们将给予您一个用户账号及相应的密码该用户账号和密码由您负责保管
@@ -38,7 +38,7 @@
<view class="section">
<view class="section-title">服务内容</view>
<view class="paragraph">
3.1 客宝平台服务的具体内容由我们根据实际情况提供包括但不限于信息发布交易撮合数据统计等
3.1 客宝平台服务的具体内容由我们根据实际情况提供包括但不限于信息发布交易撮合数据统计等
</view>
<view class="paragraph">
3.2 我们有权不经事先通知随时变更中断或终止部分或全部的服务
@@ -46,19 +46,19 @@
</view>
<view class="section">
<view class="section-title">用户义务</view>
<view class="section-title">用户行为规范</view>
<view class="paragraph">
4.1 您在使用客宝平台服务时必须遵守中华人民共和国相关法律法规
4.1 您在使用客宝平台服务时必须遵守中华人民共和国相关法律法规
</view>
<view class="paragraph">
4.2 您不得利用客宝平台服务从事违法违规行为包括但不限于发布违法信息侵犯他人知识产权等
4.2 您不得利用客宝平台服务从事违法违规行为包括但不限于发布违法信息侵犯他人知识产权等
</view>
</view>
<view class="section">
<view class="section-title">知识产权</view>
<view class="paragraph">
5.1 客宝平台所包含的全部智力成果包括但不限于程序源代码图标图饰图像图表文字等均受著作权法商标法专利法及其他知识产权法律法规的保护
5.1 客宝平台所包含的全部智力成果包括但不限于程序源代码图标图饰图像图表文字等均受著作权法商标法专利法及其他知识产权法律法规的保护
</view>
</view>
@@ -78,7 +78,7 @@
7.1 我们有权随时修改本协议并在修改后的协议生效前通过适当方式通知您
</view>
<view class="paragraph">
7.2 如您不同意修改后的协议可以选择停止使用客宝平台服务如您继续使用客宝平台服务则视为您已同意修改后的协议
7.2 如您不同意修改后的协议可以选择停止使用客宝平台服务如您继续使用客宝平台服务则视为您已同意修改后的协议
</view>
</view>

View File

@@ -100,6 +100,8 @@
<script>
import LineChart from '@/components/LineChart.vue'
import CustomTabBar from '@/components/CustomTabBar.vue'
import Auth from '@/utils/auth'
import { getUserInfo, logout } from '@/api/user'
export default {
components: {
@@ -119,14 +121,42 @@ export default {
{ icon: 'coupon', color: 'yellow', count: 167, label: '海报获客' },
{ icon: 'play-right', color: 'black', count: 156, label: '抖音获客' },
{ icon: 'heart', color: 'red', count: 89, label: '小红书获客' }
]
],
userInfo: null
}
},
onLoad() {
// 检查登录状态
if (!Auth.isLogin()) {
uni.reLaunch({
url: '/pages/login/index'
});
return;
}
// 获取用户信息
this.fetchUserInfo();
// 加载数据
this.loadData();
},
methods: {
// 获取用户信息
fetchUserInfo() {
// 先尝试从缓存获取
this.userInfo = Auth.getUserInfo();
// 然后从服务器获取最新信息
getUserInfo().then(res => {
if (res.code === 200) {
this.userInfo = res.data;
Auth.setUserInfo(res.data);
}
}).catch(err => {
console.error('获取用户信息失败:', err);
});
},
// 加载数据
loadData() {
// 这里可以添加API调用获取实际数据
@@ -139,6 +169,34 @@ export default {
uni.navigateTo({
url: '/pages/notification/index'
});
},
// 退出登录
handleLogout() {
uni.showModal({
title: '提示',
content: '确认退出登录吗?',
success: (res) => {
if (res.confirm) {
// 直接清除本地保存的登录信息
Auth.removeAll();
// 显示退出成功提示
uni.showToast({
title: '退出成功',
icon: 'success',
duration: 1500
});
// 跳转到登录页面
setTimeout(() => {
uni.reLaunch({
url: '/pages/login/index'
});
}, 1500);
}
}
});
}
}
}
@@ -174,6 +232,25 @@ export default {
display: flex;
color: black;
align-items: center;
.user-info {
display: flex;
align-items: center;
margin-right: 20rpx;
padding: 8rpx 20rpx;
background-color: #f5f5f5;
border-radius: 30rpx;
.user-name {
font-size: 28rpx;
color: #333;
margin-right: 8rpx;
}
}
.icon-bell {
margin-left: 10rpx;
}
}
}

View File

@@ -24,10 +24,10 @@
<!-- 手机号输入 -->
<view class="input-box">
<u--input
v-model="form.mobile"
v-model="form.account"
placeholder="+86手机号"
prefixIcon="phone"
prefixIconStyle="font-size: 52rpx;color: #909399;padding-right: 16rpx;"
prefixIconStyle="font-size: 40rpx; color: #909399; padding-right: 16rpx;"
clearable
type="number"
maxlength="11"
@@ -71,7 +71,7 @@
fontSize="30rpx"
suffixIcon="eye"
@clickSuffixIcon="showPassword = !showPassword"
suffixIconStyle="font-size: 45rpx;"
suffixIconStyle="font-size: 40rpx;"
></u--input>
</view>
@@ -101,9 +101,10 @@
<!-- 登录按钮 -->
<u-button
text="登录"
type="primary"
type="info"
:disabled="!canLogin"
@click="handleLogin"
customStyle="width: 100%; margin-top: 40rpx; height: 96rpx; border-radius: 24rpx; font-size: 32rpx; font-weight: 500;"
customStyle="width: 100%; margin-top: 40rpx; height: 96rpx; border-radius: 24rpx; font-size: 40rpx; font-weight: bold; background-color: #2563eb; color: #fff;"
></u-button>
<!-- 分割线 -->
@@ -117,13 +118,13 @@
<view class="other-login">
<!-- 微信登录 -->
<button class="wechat-btn" @click="handleWechatLogin">
<u-icon name="weixin-fill" size="56" color="#07c160" class="wechat-icon"></u-icon>
<u-icon name="weixin-fill" size="44" color="#07c160" class="wechat-icon"></u-icon>
<text>使用微信登录</text>
</button>
<!-- Apple登录 -->
<button class="apple-btn" @click="handleAppleLogin">
<u-icon name="apple-fill" size="56" color="#333333" class="apple-icon"></u-icon>
<u-icon name="apple-fill" size="44" color="#333333" class="apple-icon"></u-icon>
<text>使用 Apple 登录</text>
</button>
</view>
@@ -135,6 +136,9 @@
</template>
<script>
import { login, mobileLogin, sendCode } from '@/api/user'
import Auth from '@/utils/auth'
export default {
data() {
return {
@@ -144,9 +148,10 @@ export default {
],
current: 0,
form: {
mobile: '',
account: '',
code: '',
password: ''
password: '',
typeId: 1 // 默认账号类型为运营后台/操盘手
},
showPassword: false,
isAgree: false,
@@ -161,7 +166,7 @@ export default {
},
computed: {
isValidMobile() {
return /^1\d{10}$/.test(this.form.mobile)
return /^1\d{10}$/.test(this.form.account)
},
canLogin() {
if (!this.isAgree || !this.isValidMobile) return false
@@ -185,18 +190,48 @@ export default {
},
getCode() {
if (this.sending || !this.isValidMobile) return
this.sending = true
this.codeTips = '60s'
let seconds = 60
const timer = setInterval(() => {
seconds--
this.codeTips = `${seconds}s`
if (seconds <= 0) {
clearInterval(timer)
// 发送验证码接口调用
sendCode({
account: this.form.account,
type: 'login'
}).then(res => {
if (res.code === 200) {
// 发送成功,开始倒计时
this.sending = true
this.codeTips = '60s'
let seconds = 60
const timer = setInterval(() => {
seconds--
this.codeTips = `${seconds}s`
if (seconds <= 0) {
clearInterval(timer)
this.sending = false
this.codeTips = '发送验证码'
}
}, 1000)
// 提示用户
uni.showToast({
title: '验证码已发送',
icon: 'success'
})
} else {
// 发送失败
uni.showToast({
title: res.msg || '验证码发送失败',
icon: 'none'
})
this.sending = false
this.codeTips = '发送验证码'
}
}, 1000)
}).catch(err => {
console.error('发送验证码失败:', err)
uni.showToast({
title: '验证码发送失败,请重试',
icon: 'none'
})
this.sending = false
})
},
handleLogin() {
if (!this.canLogin) {
@@ -237,42 +272,69 @@ export default {
mask: true
})
// 模拟登录成功
setTimeout(() => {
// 根据当前登录方式选择不同的登录API
const loginAction = this.current === 0 ?
mobileLogin({
account: this.form.account,
code: this.form.code,
typeId: this.form.typeId
}) :
login({
account: this.form.account,
password: this.form.password,
typeId: this.form.typeId
});
// 调用登录接口
loginAction.then(res => {
// 隐藏加载提示
uni.hideLoading()
// 保存登录状态和用户信息
uni.setStorageSync('token', 'mock_token_' + Date.now())
uni.setStorageSync('userInfo', {
mobile: this.form.mobile,
loginTime: Date.now()
})
// 显示登录成功提示
uni.showToast({
title: '登录成功',
icon: 'success',
duration: 1500
})
// 延迟跳转到首页
setTimeout(() => {
uni.reLaunch({
url: '/pages/index/index',
success: () => {
console.log('跳转到首页成功')
},
fail: (err) => {
console.error('跳转失败:', err)
uni.showToast({
title: '跳转失败,请重试',
icon: 'none'
})
}
if (res.code === 200) {
// 登录成功保存token和用户信息
Auth.setToken(res.data.token, res.data.token_expired - Math.floor(Date.now() / 1000));
Auth.setUserInfo(res.data.member);
// 显示登录成功提示
uni.showToast({
title: '登录成功',
icon: 'success',
duration: 1500
})
}, 1500)
}, 1000)
// 延迟跳转到首页
setTimeout(() => {
uni.reLaunch({
url: '/pages/index/index',
success: () => {
console.log('跳转到首页成功')
},
fail: (err) => {
console.error('跳转失败:', err)
uni.showToast({
title: '跳转失败,请重试',
icon: 'none'
})
}
})
}, 1500)
} else {
// 登录失败
uni.showToast({
title: res.msg || '登录失败',
icon: 'none'
})
}
}).catch(err => {
// 隐藏加载提示
uni.hideLoading()
console.error('登录失败:', err)
uni.showToast({
title: '登录失败,请重试',
icon: 'none'
})
})
},
handleWechatLogin() {
console.log('微信登录')

View File

@@ -12,7 +12,7 @@
<!-- 用户信息卡片 -->
<view class="user-card">
<view class="avatar-wrap">
<template v-if="userInfo.avatar">
<template v-if="userInfo && userInfo.avatar">
<image class="avatar" :src="userInfo.avatar"></image>
</template>
<template v-else>
@@ -22,8 +22,8 @@
</template>
</view>
<view class="user-info">
<view class="username">卡若</view>
<view class="account">账号: 84675209</view>
<view class="username">{{ userInfo && userInfo.username ? userInfo.username : '未设置昵称' }}</view>
<view class="account">账号: {{ userInfo && userInfo.account ? userInfo.account : '未登录' }}</view>
<view class="edit-profile-btn" @click="editProfile">
编辑资料
</view>
@@ -74,6 +74,8 @@
<script>
import CustomTabBar from '@/components/CustomTabBar.vue'
import Auth from '@/utils/auth'
import { getUserInfo, logout } from '@/api/user'
export default {
components: {
@@ -81,28 +83,48 @@ export default {
},
data() {
return {
userInfo: {
avatar: null,
username: '卡若',
account: '84675209'
}
userInfo: null
}
},
onShow() {
// 每次显示页面时获取最新的用户信息
this.getUserInfo();
},
onLoad() {
// 检查登录状态
if (!Auth.isLogin()) {
uni.reLaunch({
url: '/pages/login/index'
});
return;
}
// 获取用户信息
this.getUserInfo();
},
methods: {
// 获取用户信息
getUserInfo() {
// 这里可以添加获取用户信息的API调用
console.log('获取用户信息');
// 示例数据实际应从API获取
this.userInfo = {
avatar: 'https://images.unsplash.com/photo-1568602471122-7832951cc4c5?w=400&h=400&auto=format&fit=crop',
username: '卡若',
account: '84675209'
};
// 先从本地缓存获取
const cachedUserInfo = Auth.getUserInfo();
if (cachedUserInfo) {
this.userInfo = cachedUserInfo;
}
// 同时从服务器获取最新信息
getUserInfo().then(res => {
if (res.code === 200) {
this.userInfo = res.data;
// 更新本地缓存
Auth.setUserInfo(res.data);
}
}).catch(err => {
console.error('获取用户信息失败:', err);
// 如果获取失败但有缓存,使用缓存数据
if (!this.userInfo) {
this.userInfo = Auth.getUserInfo();
}
});
},
// 跳转到设置页面
@@ -140,14 +162,22 @@ export default {
content: '确定要退出登录吗?',
success: (res) => {
if (res.confirm) {
// 清除登录状态
// uni.removeStorageSync('token');
// uni.removeStorageSync('userInfo');
// 直接清除本地保存的登录信息
Auth.removeAll();
// 显示退出成功提示
uni.showToast({
title: '退出成功',
icon: 'success',
duration: 1500
});
// 跳转到登录页面
uni.reLaunch({
url: '/pages/login/index'
});
setTimeout(() => {
uni.reLaunch({
url: '/pages/login/index'
});
}, 1500);
}
}
});
@@ -178,7 +208,9 @@ export default {
z-index: 999;
.title {
font-size: 40rpx;
font-size: 45rpx;
font-weight: bold;
color: #2664ec;
}
.header-icons {

View File

@@ -1,6 +1,7 @@
/**
* 认证相关工具函数
*/
import { refreshToken } from '@/api/user';
const TOKEN_KEY = 'token';
const TOKEN_EXPIRES_KEY = 'token_expires';
@@ -67,6 +68,28 @@ function removeAll() {
removeUserInfo();
}
/**
* 刷新Token
* @returns {Promise} 刷新结果
*/
function refreshTokenAsync() {
return new Promise((resolve, reject) => {
refreshToken()
.then(res => {
if (res.code === 200) {
// 更新Token
setToken(res.data.token, res.data.token_expired - Math.floor(Date.now() / 1000));
resolve(res);
} else {
reject(res);
}
})
.catch(err => {
reject(err);
});
});
}
/**
* 判断是否已登录
* @returns {boolean} 是否已登录
@@ -84,6 +107,24 @@ function isLogin() {
return nowTime < expiresTime;
}
/**
* 获取用户类型
* @returns {number} 用户类型ID
*/
function getUserType() {
const userInfo = getUserInfo();
return userInfo ? userInfo.typeId || 0 : 0;
}
/**
* 是否为管理员
* @returns {boolean} 是否为管理员
*/
function isAdmin() {
const userInfo = getUserInfo();
return userInfo ? !!userInfo.isAdmin : false;
}
export default {
setToken,
getToken,
@@ -92,5 +133,8 @@ export default {
getUserInfo,
removeUserInfo,
removeAll,
isLogin
isLogin,
refreshToken: refreshTokenAsync,
getUserType,
isAdmin
};

View File

@@ -1,9 +1,7 @@
import Auth from './auth';
// 服务器地址
const BASE_URL = process.env.NODE_ENV === 'development'
? 'http://localhost:8080'
: 'https://api.example.com';
const BASE_URL = process.env.VUE_APP_BASE_API || 'http://yishi.com';
// 请求超时时间
const TIMEOUT = 10000;
@@ -17,7 +15,7 @@ function requestInterceptor(config) {
// 获取 token
const token = uni.getStorageSync('token');
// 如果有 token则带上请求头
// 如果有 token则带上请求头 Authorization: Bearer + token
if (token) {
config.header = {
...config.header,
@@ -39,11 +37,8 @@ function requestInterceptor(config) {
function responseInterceptor(response) {
// 未登录或token失效 - 取消登录拦截
if (response.data.code === 401) {
// 只在控制台打印信息,不进行拦截
console.log('登录已过期,但不进行拦截');
console.log('登录已过期,需要重新登录');
/*
// 以下代码已注释,取消登录拦截
// 清除登录信息
Auth.removeToken();
Auth.removeUserInfo();
@@ -54,29 +49,43 @@ function responseInterceptor(response) {
});
return Promise.reject(new Error('登录已过期,请重新登录'));
*/
// 直接返回响应,不拦截
return response.data;
}
// token需要刷新 - 取消登录拦截
// token需要刷新 - 410 状态码
if (response.data.code === 410) {
// 只在控制台打印信息,不进行拦截
console.log('Token需要刷新但不进行拦截');
/*
// 以下代码已注释,取消登录拦截
// 处理token刷新逻辑这里简化处理
uni.reLaunch({
url: '/pages/login/index'
});
return Promise.reject(new Error('登录已过期,请重新登录'));
*/
// 直接返回响应,不拦截
return response.data;
// 尝试刷新 token
return Auth.refreshToken()
.then(res => {
if (res.code === 200) {
// 更新本地token
Auth.setToken(res.data.token, res.data.token_expired - Math.floor(Date.now() / 1000));
// 使用新token重试原请求
const config = response.config;
config.header.Authorization = `Bearer ${res.data.token}`;
// 重新发起请求
return request(config);
} else {
// 刷新失败,跳转到登录页
uni.reLaunch({
url: '/pages/login/index'
});
return Promise.reject(new Error('登录已过期,请重新登录'));
}
})
.catch(err => {
console.error('刷新token失败', err);
// 清除登录信息
Auth.removeToken();
Auth.removeUserInfo();
// 跳转到登录页
uni.reLaunch({
url: '/pages/login/index'
});
return Promise.reject(new Error('登录已过期,请重新登录'));
});
}
return response.data;

View File

@@ -110,9 +110,14 @@ class User extends Model
$user->lastLoginTime = time();
$user->save();
// 用手机号当做默认用户名(如果没有设置用户名)
$username = $user->username ?: $user->account;
return [
'id' => $user->id,
'username' => $username,
'account' => $user->account,
'avatar' => $user->avatar,
'isAdmin' => $user->isAdmin,
'companyId' => $user->companyId,
'typeId' => $user->typeId,
@@ -137,14 +142,21 @@ class User extends Model
return null;
}
// 用手机号当做默认用户名(如果没有设置用户名)
$username = $user->username ?: $user->account;
// 默认头像地址
$avatar = $user->avatar ?: '';
return [
'id' => $user->id,
'username' => $username,
'account' => $user->account,
'avatar' => $avatar,
'isAdmin' => $user->isAdmin,
'companyId' => $user->companyId,
'typeId' => $user->typeId,
'role' => $user->isAdmin ? 'admin' : 'user',
'permissions' => $user->isAdmin ? ['*'] : ['user']
'lastLoginIp' => $user->lastLoginIp,
'lastLoginTime' => $user->lastLoginTime
];
}