update: 右上角

This commit is contained in:
easy 2023-03-03 18:17:06 +08:00
parent 9e84a7bb8c
commit fab790bbe6
7 changed files with 199 additions and 68 deletions

View File

@ -70,6 +70,10 @@ const iconKeys = [
'CaretDownOutlined',
'MinusOutlined',
'AudioOutlined',
'BellOutlined',
'UserOutlined',
'LogoutOutlined',
'ReadIconOutlined'
]
const Icon = (props: {type: string}) => {

View File

@ -1,8 +1,13 @@
<template>
<div>
<a-dropdown :trigger="['click']">
<div style="height: 48px; display: flex; display: flex">
<AIcon type="BellOutlined" @click.prevent />
<div class="notice-container">
<a-dropdown :trigger="['click']" @visible-change="visibleChange">
<div class="icon-content">
<AIcon
type="BellOutlined"
@click.prevent
style="font-size: 16px"
/>
<span class="unread" v-show="total > 0">{{ total }}</span>
</div>
<template #overlay>
<div>
@ -17,6 +22,11 @@
import { getList_api } from '@/api/account/notificationRecord';
import NoticeInfo from './NoticeInfo.vue';
import { getWebSocket } from '@/utils/websocket';
import { notification } from 'ant-design-vue';
import { changeStatus_api } from '@/api/account/notificationRecord';
import { useUserInfo } from '@/store/userInfo';
const updateCount = computed(()=>useUserInfo().$state.alarmUpdateCount);
const total = ref(0);
const list = ref<any[]>([]);
@ -26,7 +36,15 @@ const subscribeNotice = () => {
getWebSocket('notification', '/notifications', {})
?.pipe()
.subscribe((resp: any) => {
total.value += 1
total.value += 1;
notification.open({
message: 'Notification Title',
description:
'This is the content of the notification. This is the content of the notification. This is the content of the notification.',
onClick: () => {
changeStatus_api('_read', [resp.id]);
},
});
});
};
const getList = () => {
@ -44,8 +62,38 @@ const getList = () => {
})
.finally(() => (loading.value = false));
};
subscribeNotice();
getList();
watch(updateCount, () => getList());
const visibleChange = (bool: boolean) => {
bool && getList();
};
</script>
<style lang="less" scoped></style>
<style lang="less" scoped>
.notice-container {
.icon-content {
height: 48px;
display: flex;
align-items: center;
position: relative;
.unread {
position: absolute;
top: 0;
right: -12px;
min-width: 20px;
height: 20px;
padding: 0 6px;
color: #fff;
font-weight: normal;
font-size: 12px;
line-height: 20px;
white-space: nowrap;
text-align: center;
background: #ff4d4f;
border-radius: 10px;
}
}
}
</style>

View File

@ -1,18 +1,29 @@
<template>
<div class="notice-info-container">
<img v-if="props.data.length" :src="NoData" alt="" />
<a-tabs v-else :activeKey="'default'">
<a-tab-pane key="default" tab="未读消息">
<ul class="list">
<li class="list-item" v-for="item in props.data" @click="read(item.id)">
<h5>{{ item.topicName }}</h5>
<p>{{ item.message }}</p>
</li>
</ul>
<div class="btns">
<span @click="read()">当前标记为已读</span>
<span @click="jumpPage('account/NotificationRecord')">查看更多</span>
</div>
<a-tabs :activeKey="'default'">
<a-tab-pane key="default" tab="未读消息">
<div class="no-data" v-if="props.data.length === 0">
<img src="https://gw.alipayobjects.com/zos/rmsportal/sAuJeJzSKbUmHfBQRzmZ.svg" alt="" />
</div>
<div v-else class="content">
<ul class="list">
<li
class="list-item"
v-for="item in props.data"
@click="read(item.id)"
>
<h5>{{ item.topicName }}</h5>
<p>{{ item.message }}</p>
</li>
</ul>
<div class="btns">
<span @click="read()">当前标记为已读</span>
<span @click="jumpPage('account/NotificationRecord')"
>查看更多</span
>
</div>
</div>
</a-tab-pane>
</a-tabs>
</div>
@ -21,22 +32,19 @@
<script setup lang="ts">
import { changeStatus_api } from '@/api/account/notificationRecord';
import { useMenuStore } from '@/store/menu';
import NoData from './nodata.svg';
const emits = defineEmits(['onAction'])
const emits = defineEmits(['onAction']);
const props = defineProps<{
data: any[];
}>();
const { jumpPage } = useMenuStore();
const read = (id?:string)=>{
const ids = id ? [id]: props.data.map(item=>item.id)
changeStatus_api('_read',ids).then(resp=>{
if(resp.status === 200) emits('onAction')
})
}
const read = (id?: string) => {
const ids = id ? [id] : props.data.map((item) => item.id);
changeStatus_api('_read', ids).then((resp:any) => {
if (resp.status === 200) emits('onAction');
});
};
</script>
<style lang="less" scoped>
@ -52,22 +60,61 @@ const read = (id?:string)=>{
justify-content: center;
}
.list {
.list-item {
border-bottom: 1px solid #f0f0f0;
padding: 24px;
.no-data {
width: 100%;
padding: 73px 0 88px;
color: rgba(0, 0, 0, 0.45);
text-align: center;
img {
height: 76px;
}
}
.btns {
display: flex;
span {
display: block;
width: 50%;
text-align: center;
cursor: pointer;
&:first-child{
border-right: 1px solid #f0f0f0;
.content {
.list {
max-height: 400px;
overflow: auto;
padding: 0;
margin: 0;
&::-webkit-scrollbar {
//
display: none;
}
.list-item {
padding: 12px 24px;
list-style: none;
border-bottom: 1px solid #f0f0f0;
cursor: pointer;
h5 {
color: rgba(0, 0, 0, 0.85);
font-size: 14px;
font-weight: normal;
}
p {
font-size: 12px;
color: rgba(0, 0, 0, 0.45);
}
&:hover{
background: #f0f5ff;
}
}
}
.btns {
display: flex;
height: 46px;
line-height: 46px;
span {
display: block;
width: 50%;
text-align: center;
cursor: pointer;
&:first-child {
border-right: 1px solid #f0f0f0;
}
}
}
}

View File

@ -11,12 +11,12 @@
</div>
<template #overlay>
<a-menu>
<a-menu-item @click="jumpPage('account/center')" style="width: 160px;">
<AIcon type="UserOutlined" />
<a-menu-item @click="push('/account/center')" style="width: 160px;">
<AIcon type="UserOutlined" style="margin-right: 8px;" />
<span>个人中心</span>
</a-menu-item>
<a-menu-item @click="logOut">
<AIcon type="LogoutOutlined" />
<AIcon type="LogoutOutlined" style="margin-right: 8px;" />
<span>退出登录</span>
</a-menu-item>
</a-menu>
@ -27,17 +27,17 @@
<script setup lang="ts">
import { loginout_api } from '@/api/login';
import { useMenuStore } from '@/store/menu';
import { useUserInfo } from '@/store/userInfo';
const {push} = useRouter();
const userInfo = useUserInfo().$state.userInfos as any;
const { jumpPage } = useMenuStore();
const logOut = () => {
loginout_api().then(() => {
localStorage.clear();
jumpPage('user/login');
push('/user/login');
});
};
</script>

View File

@ -20,7 +20,9 @@ export const useUserInfo = defineStore('userInfo', {
token: '',
user: {},
},
alarmUpdateCount: 0
}),
actions: {
login(userInfo: any) {
const username = userInfo.userName.trim();
@ -49,6 +51,9 @@ export const useUserInfo = defineStore('userInfo', {
}
}).catch(() => rej())
})
},
updateAlarm(){
this.alarmUpdateCount += 1
}
},
});

View File

@ -54,18 +54,6 @@
}"
>
<AIcon type="ReadIconOutlined" />
<!-- <svg
width="1em"
height="1em"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 18H6L2 22V2C2 2 2.9 2 4 2H20C21.1 2 22 2 22 2V11H20V4H4V16H12V18ZM23 14.34L21.59 12.93L17.35 17.17L15.23 15.05L13.82 16.46L17.34 20L23 14.34Z"
fill="currentColor"
/>
</svg> -->
</PermissionButton>
<PermissionButton
type="link"
@ -80,7 +68,11 @@
</template>
</j-pro-table>
<ViewDialog v-if="viewVisible" v-model:visible="viewVisible" :data="viewItem" />
<ViewDialog
v-if="viewVisible"
v-model:visible="viewVisible"
:data="viewItem"
/>
</div>
</page-container>
</template>
@ -97,7 +89,10 @@ import { optionItem } from '@/views/rule-engine/Scene/typings';
import { dictItemType } from '@/views/system/DataSource/typing';
import moment from 'moment';
import { message } from 'ant-design-vue';
import NoticeCp from '@/components/Layout/components/Notice.vue';
import { useUserInfo } from '@/store/userInfo';
const { updateAlarm } = useUserInfo();
const columns = [
{
title: '类型',
@ -181,6 +176,7 @@ const table = {
if (resp.status === 200) {
message.success('操作成功!');
table.refresh();
updateAlarm();
}
});
},

View File

@ -2146,7 +2146,7 @@ cli-width@^3.0.0:
resolved "https://registry.npmmirror.com/cli-width/-/cli-width-3.0.0.tgz"
integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==
clipboard@^2.0.4:
clipboard@^2.0.10, clipboard@^2.0.4:
version "2.0.11"
resolved "https://registry.jetlinks.cn/clipboard/-/clipboard-2.0.11.tgz"
integrity sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==
@ -3416,6 +3416,11 @@ highlight.js@^11.3.1:
resolved "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.7.0.tgz"
integrity sha512-1rRqesRFhMO/PRF+G86evnyJkCgaZFOI+Z6kdj15TA18funfoqJXvgPCLSf0SWq3SRfg1j3HlDs8o4s3EGq1oQ==
hls.js@^1.0.10:
version "1.3.4"
resolved "https://registry.jetlinks.cn/hls.js/-/hls.js-1.3.4.tgz#8212a3f95c3321f64a586f20e67876f3a9d09488"
integrity sha512-iFEwVqtEDk6sKotcTwtJ5OMo/nuDTk9PrpB8FI2J2WYf8EriTVfR4FaK0aNyYtwbYeRSWCXJKlz23xeREdlNYg==
homedir-polyfill@^1.0.0:
version "1.0.3"
resolved "https://registry.npmmirror.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz"
@ -3896,10 +3901,10 @@ jetlinks-store@^0.0.3:
resolved "https://registry.npmjs.org/jetlinks-store/-/jetlinks-store-0.0.3.tgz"
integrity sha512-AZf/soh1hmmwjBZ00fr1emuMEydeReaI6IBTGByQYhTmK1Zd5pQAxC7WLek2snRAn/HHDgJfVz2hjditKThl6Q==
jetlinks-ui-components@^1.0.2:
version "1.0.2"
resolved "https://registry.jetlinks.cn/jetlinks-ui-components/-/jetlinks-ui-components-1.0.2.tgz#753507e4a84dee966c65d4ae33943507bd60cc9d"
integrity sha512-LXKetf75/uqzWP4XOobm+iUWTVEeMJNnwEk+GrNftQdcDzGvUxBaoFCoS+HBH06FnE2VBbh6rEwrpDhkLSNEow==
jetlinks-ui-components@^1.0.1:
version "1.0.3"
resolved "https://registry.jetlinks.cn/jetlinks-ui-components/-/jetlinks-ui-components-1.0.3.tgz#08a35ebfa016574affcfd11204359cdccf8e139f"
integrity sha512-kA/AxzdfNy+Sl8En8raMwIh3stofgElkUuJ+oRJMpQVTGbrOk29DifRsHJJFNvtEvclmLdKZkOkthOuEdG2mnw==
dependencies:
"@vueuse/core" "^9.12.0"
ant-design-vue "^3.2.15"
@ -6554,6 +6559,11 @@ three@0.143.0:
resolved "https://registry.npmjs.org/three/-/three-0.143.0.tgz"
integrity sha512-oKcAGYHhJ46TGEuHjodo2n6TY2R6lbvrkp+feKZxqsUL/WkH7GKKaeu6RHeyb2Xjfk2dPLRKLsOP0KM2VgT8Zg==
throttle-debounce@^3.0.1:
version "3.0.1"
resolved "https://registry.jetlinks.cn/throttle-debounce/-/throttle-debounce-3.0.1.tgz#32f94d84dfa894f786c9a1f290e7a645b6a19abb"
integrity sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==
through2@^4.0.0:
version "4.0.2"
resolved "https://registry.npmmirror.com/through2/-/through2-4.0.2.tgz"
@ -6940,6 +6950,11 @@ uuid@^3.3.2:
resolved "https://registry.jetlinks.cn/uuid/-/uuid-3.4.0.tgz"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
v-clipboard3@^0.1.4:
version "0.1.4"
resolved "https://registry.jetlinks.cn/v-clipboard3/-/v-clipboard3-0.1.4.tgz#13bdd12ce9728190d70f6ebf8b71de59d88b550e"
integrity sha512-iGIXgluf2WLbT+/Z1de9kKzoK9c9aPpy+zcPlY8/fneO+NHK95QEmFx2Q9LoxeUPRemD+nOfEv1J20Ki7W0v7Q==
v8-compile-cache-lib@^3.0.1:
version "3.0.1"
resolved "https://registry.npmmirror.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz"
@ -7072,6 +7087,13 @@ vue-types@^3.0.0:
dependencies:
is-plain-object "3.0.1"
vue3-json-viewer@^2.2.2:
version "2.2.2"
resolved "https://registry.jetlinks.cn/vue3-json-viewer/-/vue3-json-viewer-2.2.2.tgz#43a512f378c602bb6b7f2a72adeaf7d15b443885"
integrity sha512-56l3XDGggnpwEqZieXsSMhNT4NhtO6d7zuSAxHo4i0UVxymyY2jRb7UMQOU1ztChKALZCAzX7DlgrsnEhxu77A==
dependencies:
clipboard "^2.0.10"
vue3-markdown-it@^1.0.10:
version "1.0.10"
resolved "https://registry.npmmirror.com/vue3-markdown-it/-/vue3-markdown-it-1.0.10.tgz"
@ -7099,7 +7121,16 @@ vue3-ts-jsoneditor@^2.7.1:
vanilla-jsoneditor "^0.7.9"
vue "^3.2.37"
vue@^3.2.25:
vue3-video-play@^1.3.1-beta.6:
version "1.3.1-beta.6"
resolved "https://registry.jetlinks.cn/vue3-video-play/-/vue3-video-play-1.3.1-beta.6.tgz#bca3f55a11053eaa37053835e4610c04d9cc509e"
integrity sha512-Olrx2/LNAds7fuor/yX9ZKT9sOcwcfTt2g2YbbCrEaAmZ5Tb0hwBr5z+/CoLwELzzRzXCHPmWWoT0Wm5W/Nwpw==
dependencies:
hls.js "^1.0.10"
throttle-debounce "^3.0.1"
vue "^3.2.2"
vue@^3.2.2, vue@^3.2.25:
version "3.2.47"
resolved "https://registry.jetlinks.cn/vue/-/vue-3.2.47.tgz#3eb736cbc606fc87038dbba6a154707c8a34cff0"
integrity sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==