feat:新增用户悬浮卡片
This commit is contained in:
BIN
Yi.Bbs.Vue3/src/assets/box/gitee-icon.png
Normal file
BIN
Yi.Bbs.Vue3/src/assets/box/gitee-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.4 KiB |
BIN
Yi.Bbs.Vue3/src/assets/box/github-icon.png
Normal file
BIN
Yi.Bbs.Vue3/src/assets/box/github-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.4 KiB |
BIN
Yi.Bbs.Vue3/src/assets/box/website-icon.png
Normal file
BIN
Yi.Bbs.Vue3/src/assets/box/website-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
@@ -1,30 +1,32 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<div class="avatar-left">
|
<div class="avatar-left">
|
||||||
<el-avatar :size="props.size" :src="iconUrl" />
|
|
||||||
|
|
||||||
<div v-if="props.isSelf" class="avatar-center">
|
<div v-if="props.isSelf" class="avatar-center">
|
||||||
|
<el-avatar :size="props.size" :src="iconUrl" />
|
||||||
<div class="nick">{{ userInfo.nick }}</div>
|
<div class="nick">{{ userInfo.nick }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="!props.isSelf" class="avatar-card">
|
||||||
<div v-if="!props.isSelf">
|
<!-- 悬浮卡片 -->
|
||||||
<div class="nick" :class="{ mt_1: props.time != 'undefined' }">
|
<userInfo-card :userInfo="userInfo" :iconUrl="iconUrl" />
|
||||||
<div class="text">{{ userInfo.nick }}</div>
|
<div class="content">
|
||||||
<div class="level">
|
<div class="nick" :class="{ mt_1: props.time != 'undefined' }">
|
||||||
<el-tag round effect="light" type="success" v-if="userInfo.level">{{
|
<div class="text">{{ userInfo.nick }}</div>
|
||||||
userInfo.level
|
<div class="level">
|
||||||
}}</el-tag>
|
<el-tag round effect="light" type="success" v-if="userInfo.level"
|
||||||
|
>等级{{ userInfo.level }}</el-tag
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="status" v-if="userInfo.userLimit">
|
||||||
|
<el-tag round effect="light" :type="userInfo.userLimit.type">
|
||||||
|
{{ userInfo.userLimit.label }}
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="status" v-if="userInfo.userLimit">
|
<div class="remarks" v-if="props.time">{{ props.time }}</div>
|
||||||
<el-tag round effect="light" :type="userInfo.userLimit.type">
|
<div class="remarks">
|
||||||
{{ userInfo.userLimit.label }}
|
<slot name="bottom" />
|
||||||
</el-tag>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="remarks" v-if="props.time">{{ props.time }}</div>
|
|
||||||
<div class="remarks">
|
|
||||||
<slot name="bottom" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="info" v-if="!props.isSelf">
|
<div class="info" v-if="!props.isSelf">
|
||||||
<el-tag class="ml-2" type="warning">V8</el-tag>
|
<el-tag class="ml-2" type="warning">V8</el-tag>
|
||||||
@@ -44,6 +46,11 @@
|
|||||||
import useUserStore from "@/stores/user";
|
import useUserStore from "@/stores/user";
|
||||||
import { reactive, watch, onMounted, computed, ref } from "vue";
|
import { reactive, watch, onMounted, computed, ref } from "vue";
|
||||||
import { upload } from "@/apis/fileApi";
|
import { upload } from "@/apis/fileApi";
|
||||||
|
import useAuths from "@/hooks/useAuths";
|
||||||
|
import UserInfoCard from "./UserInfoCard/index.vue";
|
||||||
|
|
||||||
|
const { getToken } = useAuths();
|
||||||
|
const isHasToken = getToken();
|
||||||
|
|
||||||
//userInfo
|
//userInfo
|
||||||
//{icon,name,role,id},根据判断userInfo是否等于未定义,来觉得是当前登录用户信息,还是其他人信息
|
//{icon,name,role,id},根据判断userInfo是否等于未定义,来觉得是当前登录用户信息,还是其他人信息
|
||||||
@@ -99,7 +106,7 @@ const Init = () => {
|
|||||||
userInfo.nick = props.userInfo.nick;
|
userInfo.nick = props.userInfo.nick;
|
||||||
userInfo.role = props.userInfo.role;
|
userInfo.role = props.userInfo.role;
|
||||||
userInfo.id = props.userInfo.id;
|
userInfo.id = props.userInfo.id;
|
||||||
userInfo.level = "等级" + props.userInfo.level;
|
userInfo.level = props.userInfo.level;
|
||||||
userInfo.userLimit = getStatusInfo(props.userInfo.userLimit);
|
userInfo.userLimit = getStatusInfo(props.userInfo.userLimit);
|
||||||
iconUrl.value = iconUrlHandler(userInfo.icon);
|
iconUrl.value = iconUrlHandler(userInfo.icon);
|
||||||
}
|
}
|
||||||
@@ -136,21 +143,12 @@ const getStatusInfo = (type) => {
|
|||||||
return statusTypeList.filter((item) => item.value === type)[0];
|
return statusTypeList.filter((item) => item.value === type)[0];
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
.mt_1 {
|
.mt_1 {
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nick {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
font-weight: bold;
|
|
||||||
> div {
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
margin-top: 0.6rem;
|
margin-top: 0.6rem;
|
||||||
@@ -171,12 +169,27 @@ const getStatusInfo = (type) => {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar-left {
|
.avatar-left,
|
||||||
|
.avatar-card {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
.content {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
.nick {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-weight: bold;
|
||||||
|
> div {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar-center {
|
.avatar-center {
|
||||||
|
display: flex;
|
||||||
flex: 2;
|
flex: 2;
|
||||||
}
|
}
|
||||||
.el-avatar {
|
.el-avatar {
|
||||||
|
|||||||
@@ -1,52 +1,178 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="modelValue" class="userInfo-card" ref="cardRef">
|
<!-- 悬浮卡片 -->
|
||||||
<!-- 个人信息内容 -->
|
<el-popover
|
||||||
<div class="info">
|
placement="right"
|
||||||
<h2>温海靖</h2>
|
:width="300"
|
||||||
<p>XXX</p>
|
:show-arrow="false"
|
||||||
<!-- 其他个人信息... -->
|
trigger="hover"
|
||||||
|
popper-class="userCard"
|
||||||
|
v-if="!props.isSelf"
|
||||||
|
>
|
||||||
|
<template #reference>
|
||||||
|
<el-avatar :size="props.size" :src="iconUrl" />
|
||||||
|
</template>
|
||||||
|
<div class="top">
|
||||||
|
<div class="left">
|
||||||
|
<div class="image">
|
||||||
|
<img :src="iconUrl" alt="" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="right">
|
||||||
|
<div class="userInfo">
|
||||||
|
<div class="name">{{ userInfo.nick }}</div>
|
||||||
|
<div class="level">
|
||||||
|
<el-tag effect="light" type="success" v-if="userInfo.level"
|
||||||
|
>等级{{ userInfo.level }}</el-tag
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="website">
|
||||||
|
<div class="icon">
|
||||||
|
<img src="@/assets/box/github-icon.png" alt="" />
|
||||||
|
</div>
|
||||||
|
<div class="icon">
|
||||||
|
<img src="@/assets/box/website-icon.png" alt="" />
|
||||||
|
</div>
|
||||||
|
<div class="icon">
|
||||||
|
<img src="@/assets/box/gitee-icon.png" alt="" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="btn">
|
||||||
|
<el-button type="primary" icon="Plus">关注</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="line"></div>
|
||||||
|
<div class="bottom">
|
||||||
|
<div class="header">
|
||||||
|
<div class="score">积分:3005</div>
|
||||||
|
<div class="status">
|
||||||
|
<span>状态:</span>
|
||||||
|
<span> {{ getStatusInfo(userInfo.userLimit.label) }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hobby">
|
||||||
|
<span>关注:</span>
|
||||||
|
<el-tag type="info">C#</el-tag>
|
||||||
|
<el-tag type="info">前端</el-tag>
|
||||||
|
<el-tag type="info">Python</el-tag>
|
||||||
|
<el-tag type="info">算法</el-tag>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-popover>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup name="UserInfoCard">
|
||||||
import { ref, computed, nextTick, defineProps, defineEmits } from "vue";
|
import { computed, defineProps } from "vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
// 用户信息
|
||||||
type: Boolean,
|
userInfo: {
|
||||||
default: false,
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
// icon地址
|
||||||
|
iconUrl: {
|
||||||
|
type: String,
|
||||||
|
default: () => "",
|
||||||
},
|
},
|
||||||
actOnRef: {},
|
|
||||||
});
|
});
|
||||||
const emit = defineEmits("update:modelValue");
|
|
||||||
const cardRef = ref(null);
|
|
||||||
const avatarRef = computed(() => props.actOnRef);
|
|
||||||
|
|
||||||
nextTick(() => {
|
const userInfo = computed(() => props.userInfo);
|
||||||
document.addEventListener("mouseup", (e) => {
|
|
||||||
// 如果点击的是按钮 则不执行下面的操作
|
const statusTypeList = [
|
||||||
if (avatarRef.value && cardRef.value) {
|
{
|
||||||
if (cardRef.value.contains(e.target)) {
|
label: "正常",
|
||||||
return;
|
value: "Normal",
|
||||||
}
|
type: "success",
|
||||||
if (!cardRef.value.contains(e.target)) {
|
},
|
||||||
// 点击的区域不包含在弹窗区域之内就关闭弹窗
|
{
|
||||||
emit("update:modelValue", false);
|
label: "危险",
|
||||||
}
|
value: "Dangerous",
|
||||||
}
|
type: "warning",
|
||||||
});
|
},
|
||||||
});
|
{
|
||||||
|
label: "已禁止",
|
||||||
|
value: "Ban",
|
||||||
|
type: "danger",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const getStatusInfo = (type) => {
|
||||||
|
return statusTypeList.filter((item) => item.value === type)[0];
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped lang="scss">
|
||||||
.userInfo-card {
|
.userCard {
|
||||||
width: 100px;
|
.top {
|
||||||
height: 100px;
|
display: flex;
|
||||||
position: absolute;
|
width: 100%;
|
||||||
top: 0;
|
height: 100px;
|
||||||
left: 0;
|
.left {
|
||||||
background-color: pink;
|
width: 80px;
|
||||||
/* 卡片样式... */
|
.image {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.right {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex: 1;
|
||||||
|
margin-left: 20px;
|
||||||
|
.userInfo {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
.level {
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.website {
|
||||||
|
display: flex;
|
||||||
|
.icon {
|
||||||
|
cursor: pointer;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
margin-right: 10px;
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.line {
|
||||||
|
margin: 20px 0;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
}
|
||||||
|
.bottom {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-around;
|
||||||
|
width: 100%;
|
||||||
|
height: 100px;
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.status {
|
||||||
|
margin-left: 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.hobby {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
> .el-tag {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="point-box">
|
<div class="point-box">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<div class="icon"><img :src="userImageSrc" alt="" /></div>
|
<UserInfoCard :userInfo="pointsData" :iconUrl="userImageSrc" />
|
||||||
</div>
|
</div>
|
||||||
<div class="center">
|
<div class="center">
|
||||||
<div class="top">
|
<div class="top">
|
||||||
@@ -36,6 +36,7 @@
|
|||||||
|
|
||||||
<script setup name="PointsRanking">
|
<script setup name="PointsRanking">
|
||||||
import { defineProps, computed } from "vue";
|
import { defineProps, computed } from "vue";
|
||||||
|
import UserInfoCard from "@/components/UserInfoCard/index.vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
pointsData: {
|
pointsData: {
|
||||||
@@ -44,6 +45,8 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const pointsData = computed(() => props.pointsData);
|
||||||
|
|
||||||
const statusTypeList = [
|
const statusTypeList = [
|
||||||
{
|
{
|
||||||
label: "正常",
|
label: "正常",
|
||||||
@@ -66,10 +69,10 @@ const getStatusInfo = (type) => {
|
|||||||
return statusTypeList.filter((item) => item.value === type)[0];
|
return statusTypeList.filter((item) => item.value === type)[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
const userLimit = computed(() => getStatusInfo(props.pointsData.userLimit));
|
const userLimit = computed(() => getStatusInfo(pointsData.value.userLimit));
|
||||||
const userImageSrc = computed(() => {
|
const userImageSrc = computed(() => {
|
||||||
if (props.pointsData.icon) {
|
if (pointsData.value.icon) {
|
||||||
return import.meta.env.VITE_APP_BASEAPI + "/file/" + props.pointsData.icon;
|
return import.meta.env.VITE_APP_BASEAPI + "/file/" + pointsData.value.icon;
|
||||||
} else {
|
} else {
|
||||||
return "acquiesce.png";
|
return "acquiesce.png";
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user