no message

This commit is contained in:
kuaifan 2025-04-19 19:06:34 +08:00
parent 63c6e12aca
commit 7b49d66a8e
13 changed files with 300 additions and 7 deletions

View File

@ -267,9 +267,20 @@ class UsersController extends AbstractController
return Base::retSuccess('请求成功', $captcha);
}
/**
* @api {get} api/users/logout 06. 退出登录
*
* @apiVersion 1.0.0
* @apiGroup users
* @apiName logout
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
*/
public function logout()
{
$user = User::auth();
UserDevice::forget();
return Base::retSuccess('退出成功');
}
/**
@ -2504,6 +2515,6 @@ class UsersController extends AbstractController
}
UserDevice::forget($userDevice->id);
//
return Base::retSuccess('删除成功');
return Base::retSuccess('操作成功');
}
}

View File

@ -444,8 +444,9 @@ class User extends AbstractModel
{
$user = self::authInfo();
if (!$user) {
if (Base::token()) {
UserDevice::forget();
$token = Base::token();
if ($token) {
UserDevice::forget($token);
throw new ApiException('身份已失效,请重新登录', [], -1);
} else {
throw new ApiException('请登录后继续...', [], -1);

View File

@ -838,7 +838,7 @@ export default {
case 'logout':
$A.modalConfirm({
title: '退出登录',
content: '你确定要登出系统',
content: '你确定要登出系统',
onOk: () => {
this.$store.dispatch("logout", false)
}

View File

@ -0,0 +1,212 @@
<template>
<div class="setting-device">
<ul>
<li v-for="device in devices" :key="device.id">
<div class="icon">
<span :class="getIcon(device.detail)"></span>
</div>
<div class="info">
<div class="title">
<span class="name">{{ getName(device.detail) }}</span>
<span class="device">{{ device.detail.os }}</span>
</div>
<div class="time">
<EPopover placement="bottom-start" trigger="click">
<div class="time-popover">
<p>{{$L('登录时间')}}: {{device.created_at}}</p>
<p>{{$L('更新时间')}}: {{device.updated_at}}</p>
<p>{{$L('过期时间')}}: {{device.expired_at}}</p>
</div>
<span slot="reference">{{ device.updated_at }}</span>
</EPopover>
</div>
</div>
<div>
<span v-if="device.is_current" class="current">{{$L('当前设备')}}</span>
<Button v-else @click="onLogout(device)">{{$L('退出登录')}}</Button>
</div>
</li>
</ul>
</div>
</template>
<style lang="scss" scoped>
.setting-device {
> ul {
display: flex;
flex-direction: column;
gap: 16px;
> li {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 8px;
padding: 24px;
border-radius: 12px;
background: rgba(79, 89, 102, .04);
.icon {
align-self: flex-start;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
height: 24px;
> span {
width: 20px;
height: 20px;
}
}
.info {
flex: 1 1 auto;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
gap: 6px;
.title {
width: 100%;
font-size: 16px;
line-height: 24px;
display: flex;
flex-direction: row;
align-items: center;
gap: 2px;
justify-content: flex-start;
color: #262626;
.name {
font-weight: 500;
}
.device {
&:before {
content: "";
}
&:after {
content: "";
}
}
}
.time {
width: 100%;
font-size: 14px;
line-height: 22px;
color: #8a939d;
cursor: pointer;
}
}
.current {
color: #595959;
}
.ivu-btn {
background: #d9d9dd;
border-color: #d9d9dd;
color: #262626;
box-shadow: none;
height: 36px;
padding: 0 12px;
border-radius: 12px;
&:hover {
background: rgba(217, 217, 221, 0.8);
border-color: rgba(217, 217, 221, 0.8);
}
}
}
}
}
.time-popover {
> p {
line-height: 26px;
}
}
</style>
<script>
export default {
name: 'SettingDevice',
data() {
return {
loadIng: 0,
devices: []
};
},
mounted() {
this.getDeviceList();
},
methods: {
getDeviceList() {
this.loadIng++;
this.$store.dispatch("call", {
url: 'users/device/list',
}).then(({data}) => {
this.devices = data.list
if (typeof this.$parent.updateDeviceNum === "function") {
this.$parent.updateDeviceNum(this.devices.length);
}
}).catch(() => {
this.devices = []
}).finally(() => {
this.loadIng--;
})
},
getIcon({app_type}) {
if (/ios/i.test(app_type)) {
return 'apple';
}
else if (/android/i.test(app_type)) {
return 'android';
}
else if (/mac/i.test(app_type)) {
return 'macos';
}
else if (/win/i.test(app_type)) {
return 'window';
}
return 'web';
},
getName({app_type, browser}) {
if (/web/i.test(app_type)) {
return browser + " " + this.$L("浏览器")
}
return app_type + " " + this.$L("客户端")
},
onLogout(device) {
$A.modalConfirm({
title: '退出登录',
content: '是否在该设备上退出登录?',
loading: true,
onOk: () => {
return new Promise((resolve, reject) => {
this.$store.dispatch("call", {
url: 'users/device/logout',
data: {
id: device.id
}
}).then(({msg}) => {
resolve(msg);
this.getDeviceList();
}).catch(({msg}) => {
reject(msg);
});
})
},
});
}
}
};
</script>

View File

@ -17,7 +17,11 @@
:key="key"
:class="classNameRoute(item.path, item.divided)"
@click="toggleRoute(item.path)">
<template v-if="item.path === 'version'">
<template v-if="item.path === 'device'">
<AutoTip>{{$L(item.name)}}</AutoTip>
<span v-if="deviceNum > 0" class="op-8">{{deviceNum}}</span>
</template>
<template v-else-if="item.path === 'version'">
<AutoTip disabled>{{$L(item.name)}}</AutoTip>
<Badge v-if="!!clientNewVersion" :text="clientNewVersion"/>
</template>
@ -51,6 +55,7 @@ export default {
components: {MobileNavTitle},
data() {
return {
deviceNum: 0,
version: window.systemInfo.version
}
},
@ -61,6 +66,10 @@ export default {
}
},
activated() {
this.getDeviceNum();
},
computed: {
...mapState(['userInfo', 'userIsAdmin', 'clientNewVersion', 'systemConfig']),
@ -97,7 +106,8 @@ export default {
menu.push(...[
{path: 'version', name: '更新日志', divided: true},
{path: 'version-show', name: '版本'},
{path: 'clearCache', name: '清除缓存', divided: true},
{path: 'device', name: '登录设备', divided: true},
{path: 'clearCache', name: '清除缓存'},
{path: 'logout', name: '退出登录'},
])
return menu;
@ -224,6 +234,20 @@ export default {
})
},
getDeviceNum() {
this.$store.dispatch("call", {
url: 'users/device/count',
}).then(({data}) => {
this.updateDeviceNum(data.count)
}).catch(() => {
this.updateDeviceNum(0)
})
},
updateDeviceNum(num) {
this.deviceNum = num
},
getServerVersion() {
return new Promise((resolve, reject) => {
if (/^\d+\.\d+\.\d+$/.test(this.systemConfig.server_version)) {

View File

@ -94,6 +94,11 @@ export default [
path: 'system',
component: () => import('./pages/manage/setting/system.vue'),
},
{
name: 'manage-setting-device',
path: 'device',
component: () => import('./pages/manage/setting/device.vue'),
},
{
name: 'manage-setting-version',
path: 'version',

View File

@ -985,6 +985,11 @@ export default {
* @param appendFrom
*/
logout({state, dispatch}, appendFrom = true) {
try {
dispatch("call", "users/logout")
} catch (e) {
console.log(e);
}
dispatch("handleClearCache", {}).then(() => {
let from = ["/", "/login"].includes(window.location.pathname) ? "" : encodeURIComponent(window.location.href);
if (appendFrom === false) {

View File

@ -82,6 +82,14 @@
align-items: center;
}
.common-auto-tip {
flex: 1;
}
.op-8 {
opacity: 0.8;
}
.ivu-badge {
transform: scale(0.8);
transform-origin: right center;
@ -334,6 +342,28 @@
}
}
.setting-device {
.icon {
> span {
&.android {
background-image: url("../images/device/android.svg");
}
&.apple {
background-image: url("../images/device/apple.svg");
}
&.macos {
background-image: url("../images/device/macos.svg");
}
&.window {
background-image: url("../images/device/window.svg");
}
&.web {
background-image: url("../images/device/web.svg");
}
}
}
}
body.window-portrait {
.page-setting {
background-color: #f8f8f8;

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1024 1024"><path d="M270.1 741.7c0 23.4 19.1 42.5 42.6 42.5h48.7v120.4c0 30.5 24.5 55.4 54.6 55.4c30.2 0 54.6-24.8 54.6-55.4V784.1h85v120.4c0 30.5 24.5 55.4 54.6 55.4c30.2 0 54.6-24.8 54.6-55.4V784.1h48.7c23.5 0 42.6-19.1 42.6-42.5V346.4h-486v395.3zm357.1-600.1l44.9-65c2.6-3.8 2-8.9-1.5-11.4c-3.5-2.4-8.5-1.2-11.1 2.6l-46.6 67.6c-30.7-12.1-64.9-18.8-100.8-18.8c-35.9 0-70.1 6.7-100.8 18.8l-46.6-67.5c-2.6-3.8-7.6-5.1-11.1-2.6c-3.5 2.4-4.1 7.4-1.5 11.4l44.9 65c-71.4 33.2-121.4 96.1-127.8 169.6h486c-6.6-73.6-56.7-136.5-128-169.7zM409.5 244.1a26.9 26.9 0 1 1 26.9-26.9a26.97 26.97 0 0 1-26.9 26.9zm208.4 0a26.9 26.9 0 1 1 26.9-26.9a26.97 26.97 0 0 1-26.9 26.9zm223.4 100.7c-30.2 0-54.6 24.8-54.6 55.4v216.4c0 30.5 24.5 55.4 54.6 55.4c30.2 0 54.6-24.8 54.6-55.4V400.1c.1-30.6-24.3-55.3-54.6-55.3zm-658.6 0c-30.2 0-54.6 24.8-54.6 55.4v216.4c0 30.5 24.5 55.4 54.6 55.4c30.2 0 54.6-24.8 54.6-55.4V400.1c0-30.6-24.5-55.3-54.6-55.3z" fill="currentColor"></path></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512"><path d="M349.13 136.86c-40.32 0-57.36 19.24-85.44 19.24c-28.79 0-50.75-19.1-85.69-19.1c-34.2 0-70.67 20.88-93.83 56.45c-32.52 50.16-27 144.63 25.67 225.11c18.84 28.81 44 61.12 77 61.47h.6c28.68 0 37.2-18.78 76.67-19h.6c38.88 0 46.68 18.89 75.24 18.89h.6c33-.35 59.51-36.15 78.35-64.85c13.56-20.64 18.6-31 29-54.35c-76.19-28.92-88.43-136.93-13.08-178.34c-23-28.8-55.32-45.48-85.79-45.48z" fill="currentColor"></path><path d="M340.25 32c-24 1.63-52 16.91-68.4 36.86c-14.88 18.08-27.12 44.9-22.32 70.91h1.92c25.56 0 51.72-15.39 67-35.11c14.72-18.77 25.88-45.37 21.8-72.66z" fill="currentColor"></path></svg>

After

Width:  |  Height:  |  Size: 710 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24"><g fill="none"><path d="M4.25 3A2.25 2.25 0 0 0 2 5.25v10.5A2.25 2.25 0 0 0 4.25 18H9.5v1.25c0 .69-.56 1.25-1.25 1.25h-.5a.75.75 0 0 0 0 1.5h8.5a.75.75 0 0 0 0-1.5h-.5c-.69 0-1.25-.56-1.25-1.25V18h5.25A2.25 2.25 0 0 0 22 15.75V5.25A2.25 2.25 0 0 0 19.75 3H4.25zM13 18v1.25c0 .45.108.875.3 1.25h-2.6c.192-.375.3-.8.3-1.25V18h2zm-9.5-3.5h17v1.25a.75.75 0 0 1-.75.75H4.25a.75.75 0 0 1-.75-.75V14.5z" fill="currentColor"></path></g></svg>

After

Width:  |  Height:  |  Size: 537 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32"><g fill="none"><path d="M11 16c0-1.393.078-2.734.222-4h9.556c.144 1.266.222 2.607.222 4c0 1.393-.078 2.734-.222 4h-9.556A35.485 35.485 0 0 1 11 16zm-1.79 4A37.618 37.618 0 0 1 9 16c0-1.379.073-2.72.21-4H2.58A14.002 14.002 0 0 0 2 16c0 1.39.203 2.733.58 4h6.63zm-5.863 2h6.138c.314 1.86.771 3.547 1.344 4.978c.369.922.793 1.758 1.272 2.472A14.036 14.036 0 0 1 3.347 22zm8.168 0h8.97c-.29 1.6-.69 3.032-1.17 4.235c-.516 1.288-1.104 2.262-1.706 2.9c-.6.634-1.144.865-1.609.865c-.465 0-1.009-.231-1.609-.866c-.602-.637-1.19-1.611-1.705-2.899c-.481-1.203-.881-2.636-1.171-4.235zm11 0c-.314 1.86-.771 3.547-1.344 4.978c-.369.922-.793 1.758-1.272 2.472A14.036 14.036 0 0 0 28.653 22h-6.138zm6.905-2c.377-1.267.58-2.61.58-4c0-1.39-.203-2.733-.58-4h-6.63c.137 1.28.21 2.621.21 4s-.073 2.72-.21 4h6.63zM19.314 5.765c.481 1.203.881 2.636 1.171 4.235h-8.97c.29-1.6.69-3.032 1.17-4.235c.516-1.288 1.104-2.263 1.706-2.9c.598-.631 1.14-.863 1.604-.865h.008c.464 0 1.007.233 1.606.866c.602.636 1.19 1.611 1.705 2.899zM22.515 10h6.138a14.036 14.036 0 0 0-8.754-7.45c.479.714.903 1.55 1.272 2.472c.573 1.431 1.03 3.118 1.344 4.978zM3.347 10h6.138c.314-1.86.771-3.547 1.344-4.978c.369-.922.793-1.758 1.272-2.472A14.036 14.036 0 0 0 3.347 10z" fill="currentColor"></path></g></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512"><path d="M480 265H232v179l248 36V265z" fill="currentColor"></path><path d="M216 265H32v150l184 26.7V265z" fill="currentColor"></path><path d="M480 32L232 67.4V249h248V32z" fill="currentColor"></path><path d="M216 69.7L32 96v153h184V69.7z" fill="currentColor"></path></svg>

After

Width:  |  Height:  |  Size: 377 B