mirror of
https://github.com/ccmjga/zhilu-admin
synced 2026-04-14 05:00:21 +00:00
avatar fix
This commit is contained in:
@@ -4,17 +4,18 @@
|
|||||||
ref="chatContainer">
|
ref="chatContainer">
|
||||||
<div class="flex flex-col gap-y-5 flex-1 pb-2">
|
<div class="flex flex-col gap-y-5 flex-1 pb-2">
|
||||||
<li v-for="chatElement in messages" :key="chatElement.content"
|
<li v-for="chatElement in messages" :key="chatElement.content"
|
||||||
:class="['flex items-start gap-2.5', chatElement.isUser ? 'flex-row-reverse max-w-full break-words' : 'flex-row']">
|
:class="['flex items-start gap-2.5', chatElement.isUser ? 'flex-row-reverse' : 'flex-row']">
|
||||||
<img class="w-8 h-8 rounded-full" :src="chatElement.isUser ? '/java.svg' : '/trump.jpg'" alt="avatar">
|
<Avatar :src="chatElement.isUser ? user.avatar : '/trump.jpg'" size="sm"
|
||||||
|
:alt="chatElement.isUser ? '用户头像' : 'AI头像'" />
|
||||||
<div
|
<div
|
||||||
:class="['flex flex-col leading-1.5 p-4 border-gray-200 rounded-e-xl rounded-es-xl ', chatElement.isUser ? 'bg-blue-100' : 'bg-gray-100']">
|
:class="['flex flex-col leading-1.5 p-4 border-gray-200 rounded-e-xl rounded-es-xl max-w-[calc(100%-40px)]', chatElement.isUser ? 'bg-blue-100' : 'bg-gray-100']">
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
<span class="text-sm font-semibold text-gray-900 ">{{ chatElement.username }}</span>
|
<span class="text-sm font-semibold text-gray-900 ">{{ chatElement.username }}</span>
|
||||||
<LoadingIcon :textColor="'text-gray-900'"
|
<LoadingIcon :textColor="'text-gray-900'"
|
||||||
v-if="isLoading && !chatElement.isUser && chatElement.content === ''" />
|
v-if="isLoading && !chatElement.isUser && chatElement.content === ''" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="markdown-content markdown-body text-base font-normal py-2.5 text-gray-900 "
|
<div class="markdown-content markdown-body text-base font-normal py-2.5 text-gray-900 break-words"
|
||||||
v-html="renderMarkdown(chatElement.content)">
|
v-html="renderMarkdown(chatElement.content)">
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
@@ -90,6 +91,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import Avatar from "@/components/Avatar.vue";
|
||||||
import InputButton from "@/components/InputButton.vue";
|
import InputButton from "@/components/InputButton.vue";
|
||||||
import UserDeleteModal from "@/components/PopupModal.vue";
|
import UserDeleteModal from "@/components/PopupModal.vue";
|
||||||
import DepartmentDeleteModal from "@/components/PopupModal.vue";
|
import DepartmentDeleteModal from "@/components/PopupModal.vue";
|
||||||
|
|||||||
43
frontend/src/components/Avatar.vue
Normal file
43
frontend/src/components/Avatar.vue
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<template>
|
||||||
|
<div class="rounded-full border border-gray-200 flex items-center justify-center overflow-hidden flex-shrink-0"
|
||||||
|
:class="sizeClass">
|
||||||
|
<img v-if="processedSrc" :src="processedSrc" class="w-full h-full object-cover" :alt="alt">
|
||||||
|
<div v-else class="w-full h-full bg-gray-100"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { getUserAvatarUrl } from "@/utils/avatarUtil";
|
||||||
|
import { computed } from "vue";
|
||||||
|
|
||||||
|
const {
|
||||||
|
src = "",
|
||||||
|
alt = "用户头像",
|
||||||
|
size = "md"
|
||||||
|
} = defineProps<{
|
||||||
|
src?: string;
|
||||||
|
alt?: string;
|
||||||
|
size?: "sm" | "md" | "lg";
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const sizeClass = computed(() => {
|
||||||
|
switch (size) {
|
||||||
|
case "sm":
|
||||||
|
return "w-8 h-8";
|
||||||
|
case "lg":
|
||||||
|
return "w-12 h-12";
|
||||||
|
default:
|
||||||
|
return "w-10 h-10";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const processedSrc = computed(() => {
|
||||||
|
if (!src) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (src === "/trump.jpg") {
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
return getUserAvatarUrl(src);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -39,10 +39,7 @@
|
|||||||
<button type="button" id="dropdown-button" class="flex text-sm rounded-full cursor-pointer"
|
<button type="button" id="dropdown-button" class="flex text-sm rounded-full cursor-pointer"
|
||||||
aria-expanded="false" data-dropdown-toggle="dropdown-user">
|
aria-expanded="false" data-dropdown-toggle="dropdown-user">
|
||||||
<span class="sr-only">打开用户菜单</span>
|
<span class="sr-only">打开用户菜单</span>
|
||||||
<div
|
<Avatar :src="user.avatar" size="sm" />
|
||||||
class="w-8 h-8 rounded-full border border-gray-200 flex items-center justify-center overflow-hidden">
|
|
||||||
<img v-if="user.avatar" :src="getUserAvatarUrl(user.avatar)" class="object-cover">
|
|
||||||
</div>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="z-50 hidden my-4 text-base list-none bg-white divide-y divide-gray-100 rounded-sm shadow-sm "
|
<div class="z-50 hidden my-4 text-base list-none bg-white divide-y divide-gray-100 rounded-sm shadow-sm "
|
||||||
@@ -97,8 +94,8 @@ import { onMounted, ref } from "vue";
|
|||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import useUserAuth from "../composables/auth/useUserAuth";
|
import useUserAuth from "../composables/auth/useUserAuth";
|
||||||
import { RouteName, RoutePath } from "../router/constants";
|
import { RouteName, RoutePath } from "../router/constants";
|
||||||
|
import Avatar from "./Avatar.vue";
|
||||||
import AiChatIcon from "./icons/AiChatIcon.vue";
|
import AiChatIcon from "./icons/AiChatIcon.vue";
|
||||||
import { getUserAvatarUrl } from "@/utils/avatarUtil";
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
changeAssistantVisible: () => void;
|
changeAssistantVisible: () => void;
|
||||||
|
|||||||
@@ -22,9 +22,7 @@
|
|||||||
<div v-for="user in users" :key="user.id" class="p-4 bg-white rounded-lg shadow">
|
<div v-for="user in users" :key="user.id" class="p-4 bg-white rounded-lg shadow">
|
||||||
<div class="flex justify-between items-start mb-3">
|
<div class="flex justify-between items-start mb-3">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<div class="w-8 h-8 rounded-full border border-gray-200 flex items-center justify-center overflow-hidden">
|
<Avatar :src="user.avatar" />
|
||||||
<img v-if="user.avatar" :src="getUserAvatarUrl(user.avatar)" class="object-cover">
|
|
||||||
</div>
|
|
||||||
<div class="font-medium text-gray-900">{{ user.username }}</div>
|
<div class="font-medium text-gray-900">{{ user.username }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
@@ -90,9 +88,7 @@
|
|||||||
<SortIcon :sortField="getSortField(field)" />
|
<SortIcon :sortField="getSortField(field)" />
|
||||||
</template>
|
</template>
|
||||||
<template #avatar="{ item }">
|
<template #avatar="{ item }">
|
||||||
<div class="w-10 h-10 rounded-full border border-gray-200 flex items-center justify-center overflow-hidden">
|
<Avatar :src="item.avatar" />
|
||||||
<img v-if="item.avatar" :src="getUserAvatarUrl(item.avatar)" class="object-cover">
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
<template #createTime="{ item }">
|
<template #createTime="{ item }">
|
||||||
{{ dayjs(item.createTime).format("llll") }}
|
{{ dayjs(item.createTime).format("llll") }}
|
||||||
@@ -162,8 +158,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import Avatar from "@/components/Avatar.vue";
|
||||||
import Breadcrumbs from "@/components/Breadcrumbs.vue";
|
import Breadcrumbs from "@/components/Breadcrumbs.vue";
|
||||||
|
|
||||||
import UserDeleteModal from "@/components/PopupModal.vue";
|
import UserDeleteModal from "@/components/PopupModal.vue";
|
||||||
import SortIcon from "@/components/SortIcon.vue";
|
import SortIcon from "@/components/SortIcon.vue";
|
||||||
import TableButton from "@/components/TableButton.vue";
|
import TableButton from "@/components/TableButton.vue";
|
||||||
@@ -180,7 +176,6 @@ import useUserDelete from "@/composables/user/useUserDelete";
|
|||||||
import { useUserQuery } from "@/composables/user/useUserQuery";
|
import { useUserQuery } from "@/composables/user/useUserQuery";
|
||||||
import { RouteName } from "@/router/constants";
|
import { RouteName } from "@/router/constants";
|
||||||
import type { UserUpsertSubmitModel } from "@/types/user";
|
import type { UserUpsertSubmitModel } from "@/types/user";
|
||||||
import { getUserAvatarUrl } from "@/utils/avatarUtil";
|
|
||||||
import { dayjs, formatDate } from "@/utils/dateUtil";
|
import { dayjs, formatDate } from "@/utils/dateUtil";
|
||||||
import { Modal, type ModalInterface, initFlowbite } from "flowbite";
|
import { Modal, type ModalInterface, initFlowbite } from "flowbite";
|
||||||
import { nextTick, onMounted, reactive, ref } from "vue";
|
import { nextTick, onMounted, reactive, ref } from "vue";
|
||||||
|
|||||||
Reference in New Issue
Block a user