wujingjing
2025-04-14 77b15609b62c9bcd80fcdfd65f134a06252920b9
src/layout/component/sidebar/SidebarOther.vue
@@ -27,12 +27,33 @@
            </el-tooltip>
            <div class="agent_line"></div>
            <div class="cursor-pointer m-0" @click="toggleShowExitLogin" v-if="isLoginStatus" ref="toggleExitLoginBtnRef">
            <div class="cursor-pointer m-0 relative" @click="toggleShowExitLogin" v-if="isLoginStatus" ref="toggleExitLoginBtnRef">
               <div class="nav__profile">
                  <span class="use_name">{{ firstUserCharacter }}</span>
               </div>
               <div class="isShow_Profile" v-show="isShowExitLogin">
                  <div class="exit" @click="feedbackClick"><i class="ywifont ywicon-youxiang"></i> 用户反馈</div>
                  <el-popover placement="right" :width="200" trigger="hover" popper-class="set-theme">
                     <template #reference>
                        <div class="exit"><i class="ywifont ywicon-yingyongzhongxin"></i> 界面主题</div>
                     </template>
                     <template #default>
                        <div class="px-0 m-0 relative">
                           <div class="set-theme-content">
                              <div
                                 v-for="(theme, index) in THEME_LIST"
                                 :key="index"
                                 :style="{ backgroundColor: theme.bgColor }"
                                 class="theme-item flex items-center justify-center text-[#fff] text-[12px] px-1 py-0"
                                 @click="changeTheme(theme.themeCss)"
                              >
                                 {{ theme.name }}主题
                              </div>
                           </div>
                        </div>
                     </template>
                  </el-popover>
                  <div class="exit" @click="informationClick"><i class="ywifont ywicon-wode"></i> 我的信息</div>
                  <!-- <div class="exit" @click="feedbackClick"><i class="ywifont ywicon-youxiang"></i> 用户反馈</div> -->
                  <div class="exit" @click="logoutClick"><i class="ywifont ywicon-tuichu"></i> 退出登录</div>
               </div>
            </div>
@@ -41,18 +62,23 @@
                  <span class="use_name">登</span>
               </div>
            </div>
            <div class="agent_line mt-4"></div>
            <OtherPlatform />
         </div>
      </div>
      <div class="nav_history_list bg-[#1c1e1d]" v-show="isShowHistoryChatRoom" ref="historyChatRoomRef">
      <div class="nav_history_list bg-[var(--color-bg-side)]" v-show="isShowHistoryChatRoom" ref="historyChatRoomRef">
         <div class="flex flex-col flex-auto w-[210.98px] rounded-t-lg box-border relative opacity-100 overflow-y-auto h100 p-[12px]">
            <div class="group flex-0 relative w100 h-[34px] bg-[#2b2c30]">
            <div class="group flex-0 relative w100 h-[34px] bg-[var(--color-bg-base-exr)]">
               <el-input clearable v-model="queryParams.title" placeholder="搜索" class="set-input">
                  <template #prefix>
                     <el-icon><search /></el-icon>
                     <el-icon>
                        <search />
                     </el-icon>
                  </template>
               </el-input>
               <div
                  class="absolute hidden top-[100%] w-[84px] z-[1001] left-0 group-hover:block overflow-hidden rounded-md text-sm text-gray-500 bg-[#fff] py-1.5"
                  class="absolute hidden top-[100%] w-[84px] z-[1001] left-0 group-hover:block overflow-hidden rounded-md text-gray-500 bg-[#fff] py-1.5"
               >
                  <div
                     class="w100 relative hover:bg-[#e6f1ff]"
@@ -72,16 +98,16 @@
            <div class="flex-auto text-[#ccc] flex flex-col items-center mt-6 overflow-y-auto" ref="chatRoomRef">
               <div
                  :class="{ 'bg-[#41424a]': item.id === activeRoomId }"
                  :class="{ 'text-btn-base bg-[var(--color-bg-base)]': item.id === activeRoomId }"
                  class="group flex items-center w-full h-10 rounded-md cursor-pointer px-2 py-2 flex-0"
                  v-for="(item, index) in queryData"
                  :key="index"
                  @click="roomClick(item)"
               >
                  <div class="ywifont ywicon-xiaoxi1 flex-0 mr-2.5"></div>
                  <div class="flex-auto text-ellipsis text-nowrap text-sm group-hover:text-[#0084ff]">{{ item.title }}</div>
                  <div class="flex-auto text-ellipsis text-nowrap group-hover:text-btn-base">{{ item.title }}</div>
                  <div class="text-gray-100 flex items-center space-x-2 ml-1">
                     <div class="ywifont invisible ywicon-bianji group-hover:visible" @click.stop="editChat(item)"></div>
                     <div class="ywifont invisible ywicon-bianji group-hover:visible !" @click="editChat(item)"></div>
                     <el-popconfirm title="确定删除聊天记录?" @confirm.stop="confirmDeleteChatRoom(item)" width="180">
                        <template #reference>
                           <div class="ywifont invisible ywicon-shanchu3 group-hover:visible"></div>
@@ -104,15 +130,7 @@
            </div>
         </template>
      </el-dialog>
      <!-- <el-dialog v-model="userRenameVisible" title="重命名" width="500" :before-close="handleCloseRename" class="el-dialog-rename">
         <el-input v-model="userRenameText" :rows="8" type="textarea" />
         <template #footer>
            <div class="dialog-footer">
               <el-button @click="handleCloseRename">取消</el-button>
               <el-button type="primary" @click="confirmRename"> 确 定 </el-button>
            </div>
         </template>
      </el-dialog> -->
      <UserInformation v-model="userInformationVisible" />
   </div>
</template>
@@ -120,9 +138,12 @@
import { onClickOutside } from '@vueuse/core';
import { ElMessage, ElMessageBox } from 'element-plus';
import moment from 'moment';
import { computed, onMounted, ref, watch, watchEffect } from 'vue';
import QRCode from 'qrcodejs2-fixes';
import { computed, nextTick, onMounted, ref, watch, watchEffect } from 'vue';
import type { ChatRoomItem } from './components/types';
import { DeleteHistoryGroups } from '/@/api/ai/chat';
import UserInformation from './components/UserInformation.vue';
import { DeleteHistoryGroups, setHistoryGroupTitle } from '/@/api/ai/chat';
import { SERVE_URL } from '/@/constants';
import { useSearch } from '/@/hooks/useSearch';
import { DateFilter, dateFilterMap } from '/@/model/types/date';
import {
@@ -133,7 +154,9 @@
   isSharePage,
   isShowLogin,
   newChatRoomClick,
   selectFirstRoom,
} from '/@/stores/chatRoom';
import OtherPlatform from './components/OtherPlatform.vue';
import emitter from '/@/utils/mitt';
import { accessSessionKey, userNameKey } from '/@/utils/request';
import { gotoRoute } from '/@/utils/route';
@@ -155,8 +178,6 @@
const queryParams = ref({
   title: '',
});
const userRenameVisible = ref(false);
const userRenameText = ref('');
const historyChatRoomClick = () => {
   isShowHistoryChatRoom.value = true;
};
@@ -186,10 +207,16 @@
      inputValue: room.title,
      inputErrorMessage: '请输入新的名称',
   })
      .then((value) => {
         const foundIndex = chatRoomList.value.findIndex((item) => item.id === activeRoomId.value);
         chatRoomList.value[foundIndex].title = userRenameText.value;
         userRenameVisible.value = false;
      .then(async (edit) => {
         const res = await setHistoryGroupTitle({
            history_group_id: room.id,
            title: edit.value,
         });
         if (res.json_ok) {
            const foundIndex = chatRoomList.value.findIndex((item) => item.id === activeRoomId.value);
            chatRoomList.value[foundIndex].title = edit.value;
            chatRoomRef.value.firstElementChild?.scrollIntoView();
         }
      })
      .catch(({ value }) => {
         ElMessage({
@@ -204,7 +231,7 @@
      isShowHistoryChatRoom.value = false;
   },
   {
      ignore: ['.el-message-box'],
      ignore: ['.el-message-box', '.el-popper'],
   }
);
//#endregion
@@ -234,6 +261,33 @@
};
//#endregion
//#region ====================== 界面主题 ======================
const THEME_LIST = ref([
   { name: '默认', themeCss: 'theme-default', bgColor: '#1c1e1d' },
   { name: '粉色', themeCss: 'theme-pink', bgColor: '#ec4899' },
   { name: '蓝色', themeCss: 'theme-blue', bgColor: '#3b82f6' },
]);
const changeTheme = (theme = '') => {
   const classList = document.documentElement.classList;
   // 移除所有主题类
   THEME_LIST.value.forEach((item) => {
      if (item.themeCss !== 'theme-default') {
         classList.remove(item.themeCss);
      }
      // 如果提供了主题,则添加它
      if (theme && theme !== 'theme-default') {
         classList.add(theme);
      }
   });
};
//#endregion
//#region ====================== 我的信息 ======================
const userInformationVisible = ref(false);
const informationClick = () => {
   userInformationVisible.value = true;
};
//#endregion
//#region ====================== 显示/退出登录 ======================
//登录
const openLoginDlg = async () => {
@@ -254,9 +308,15 @@
const toggleExitLoginBtnRef = ref<HTMLDivElement>(null);
onClickOutside(toggleExitLoginBtnRef, () => {
   isShowExitLogin.value = false;
});
onClickOutside(
   toggleExitLoginBtnRef,
   () => {
      isShowExitLogin.value = false;
   },
   {
      ignore: ['.set-theme'],
   }
);
//#endregion
//#region ====================== 日期筛选 ======================
const activeDateFilter = ref<DateFilter>(DateFilter.All);
@@ -286,6 +346,24 @@
   }
});
//#endregion
//#region ====================== 扫码手机端下载 ======================
const setPhoneQRCode = ref<HTMLElement | null>(null);
// 初始化生成二维码
const initQrcode = () => {
   let currentTime = new Date().getTime();
   const url = `${SERVE_URL}ai_html/views/mobileDownload/index.html?v=${currentTime}`;
   nextTick(() => {
      (<HTMLElement>setPhoneQRCode.value).innerHTML = '';
      new QRCode(setPhoneQRCode.value, {
         text: url,
         width: 126,
         height: 126,
         colorDark: '#000000',
         colorLight: '#ffffff',
      });
   });
};
//#endregion
//#region ====================== 搜索聊天室 ======================
const { query, queryData } = useSearch(filteredChatRoomList, queryParams);
const debounceQuery = debounce(query);
@@ -301,6 +379,7 @@
   userName.value = Local.get(userNameKey);
});
onMounted(async () => {
   selectFirstRoom();
   emitter.on('openLoginDlg', () => {
      if (isShowLogin.value || isLoginStatus.value) return;
      openLoginDlg();
@@ -308,6 +387,7 @@
   emitter.on('logout', () => {
      logoutClick();
   });
   initQrcode();
});
</script>
@@ -318,10 +398,12 @@
   height: 100%;
   position: relative;
   transition: width 0.2s ease;
   .layoutNav__item___1y99z {
      position: absolute;
      top: 0;
      z-index: 101;
      .nav__content {
         width: 60px;
         display: flex;
@@ -330,6 +412,7 @@
         padding: 14px 0 1px;
         transition: all 0.3s ease;
         transform: all ease 0.3s;
         .agent_img {
            width: 28px;
            height: 28px;
@@ -338,6 +421,7 @@
            margin-bottom: 16px;
         }
         .agent_line {
            width: 24px;
            height: 1px;
@@ -346,24 +430,29 @@
            margin-bottom: 16px;
            cursor: pointer;
         }
         .nav__chat {
            width: 40px;
            height: 40px;
            border-radius: 12px;
            margin-bottom: 10px;
            cursor: pointer;
            &:hover {
               background-color: #41424a;
               background-color: var(--color-bg-base);
            }
            .nav__chat-icon {
               background-position: 8px 8px;
               font-size: 25px;
               line-height: 40px;
               text-align: center;
               color: #fff;
               &:hover {
                  color: #0084ff;
               }
               .chat_img {
                  display: inline-flex;
                  align-items: center;
@@ -388,12 +477,13 @@
            justify-content: center;
            -webkit-box-align: center;
            align-items: center;
            .use_name {
               width: 30px;
               height: 30px;
               border-radius: 50%;
               overflow: hidden;
               background-color: #1d86ff;
               background-color: var(--color-bg-avatar);
               display: flex;
               align-items: center;
               justify-content: center;
@@ -402,6 +492,7 @@
         }
      }
   }
   .isShow_Profile {
      color: rgba(0, 0, 0, 0.87);
      border-radius: 8px;
@@ -415,23 +506,41 @@
      min-height: 50px;
      max-width: calc(100% - 32px);
      outline: 0px;
      max-height: calc(100% - 96px);
      // max-height: 100%;
      opacity: 1;
      transform: none;
      transition: opacity 274ms cubic-bezier(0.4, 0, 0.2, 1), transform 182ms cubic-bezier(0.4, 0, 0.2, 1);
      // top: 105px;
      top: 204px;
      left: 72px;
      top: 0;
      left: 60px;
      transform-origin: 0px 181px;
      .exit {
         height: 44px;
         padding: 16px 12px;
         gap: 8px;
         cursor: pointer;
         &:hover {
            background-color: rgba(0, 0, 0, 0.04);
         }
      }
   }
}
.set-theme-content {
   width: 200px;
   height: 124px;
   display: flex;
   border-radius: 8px;
   gap: 4px;
   overflow: visible;
   .theme-item {
      position: relative;
      flex-shrink: 0;
      flex-grow: 0;
      cursor: pointer;
      border-radius: 5px;
      overflow: hidden;
   }
}
.nav_history_list {
@@ -447,6 +556,7 @@
   opacity: 1;
   transition: opacity 0.2s;
}
.set-input {
   :deep(.el-input__wrapper) {
      width: 100%;
@@ -466,18 +576,22 @@
      transition: color 0.2s ease-in-out;
      box-shadow: unset;
   }
   :deep(.el-input__inner) {
      &::placeholder {
         color: white;
      }
      color: white;
   }
}
::-webkit-scrollbar {
   height: 0;
   width: 0;
   color: transparent;
}
.expand-sidebar {
   width: 20px;
   height: 48px;