Compare commits

..

5 Commits

Author SHA1 Message Date
41795c7c77 dify更新 2025-12-05 15:33:06 +08:00
1f243f977a aiagent icon样式修改 2025-12-04 15:31:45 +08:00
ed4780d6c2 修改样式 2025-12-04 14:23:49 +08:00
5b3b55cf10 导航栏特殊列 2025-12-04 11:00:20 +08:00
c37cbe0054 样式部分修改 2025-12-04 10:44:46 +08:00
17 changed files with 185 additions and 111 deletions

Binary file not shown.

View File

@@ -1502,7 +1502,7 @@ networks:
# create a network between sandbox, api and ssrf_proxy, and can not access outside. # create a network between sandbox, api and ssrf_proxy, and can not access outside.
ssrf_proxy_network: ssrf_proxy_network:
driver: bridge driver: bridge
internal: true # 修改为false以允许访问外部网络如192.168.0.64 internal: true
milvus: milvus:
driver: bridge driver: bridge
opensearch-net: opensearch-net:

View File

@@ -1,58 +0,0 @@
# Please do not directly edit this file. Instead, modify the .env variables related to NGINX configuration.
server {
listen 80;
server_name _;
location /console/api {
proxy_pass http://api:5001;
include proxy.conf;
}
location /api {
proxy_pass http://api:5001;
include proxy.conf;
}
location /v1 {
proxy_pass http://api:5001;
include proxy.conf;
}
location /files {
proxy_pass http://api:5001;
include proxy.conf;
}
location /explore {
proxy_pass http://web:3000;
include proxy.conf;
}
location /e/ {
proxy_pass http://plugin_daemon:5002;
proxy_set_header Dify-Hook-Url $scheme://$host$request_uri;
include proxy.conf;
}
location / {
proxy_pass http://web:3000;
include proxy.conf;
}
location /mcp {
proxy_pass http://api:5001;
include proxy.conf;
}
location /triggers {
proxy_pass http://api:5001;
include proxy.conf;
}
# placeholder for acme challenge location
# placeholder for https config defined in https.conf.template
}

View File

@@ -303,6 +303,7 @@ INSERT INTO `tb_sys_menu` VALUES
('402', 'menu_learning_records', '学习记录', 'menu_user_center', '/user-center/learning-records', 'user/user-center/LearningRecordsView', NULL, 1, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0), ('402', 'menu_learning_records', '学习记录', 'menu_user_center', '/user-center/learning-records', 'user/user-center/LearningRecordsView', NULL, 1, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0),
('403', 'menu_my_favorites', '我的收藏', 'menu_user_center', '/user-center/favorites', 'user/user-center/MyFavoritesView', NULL, 2, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0), ('403', 'menu_my_favorites', '我的收藏', 'menu_user_center', '/user-center/favorites', 'user/user-center/MyFavoritesView', NULL, 2, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0),
('404', 'menu_my_achievements', '我的成就', 'menu_user_center', '/user-center/achievements', 'user/user-center/MyAchievementsView', NULL, 3, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0), ('404', 'menu_my_achievements', '我的成就', 'menu_user_center', '/user-center/achievements', 'user/user-center/MyAchievementsView', NULL, 3, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0),
('650', 'menu_user_message_center', '消息中心', 'menu_user_center', '/user-center/message', 'user/message/MyMessageListView', NULL, 4, 0, 'NavigationLayout', '1', NULL, '2025-11-13 10:00:00', '2025-11-13 10:00:00', NULL, 0),
('500', 'menu_profile', '账号中心', 'menu_user_dropdown', '/profile', 'user/user-center/UserCenterLayout', NULL, 5, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0), ('500', 'menu_profile', '账号中心', 'menu_user_dropdown', '/profile', 'user/user-center/UserCenterLayout', NULL, 5, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0),
('501', 'menu_personal_info', '个人信息', 'menu_profile', '/profile/personal-info', 'user/user-center/profile/PersonalInfoView', NULL, 1, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), ('501', 'menu_personal_info', '个人信息', 'menu_profile', '/profile/personal-info', 'user/user-center/profile/PersonalInfoView', NULL, 1, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
('502', 'menu_account_settings', '账号设置', 'menu_profile', '/profile/account-settings', 'user/user-center/profile/AccountSettingsView', NULL, 2, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), ('502', 'menu_account_settings', '账号设置', 'menu_profile', '/profile/account-settings', 'user/user-center/profile/AccountSettingsView', NULL, 2, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
@@ -344,9 +345,8 @@ INSERT INTO `tb_sys_menu` VALUES
('8004', 'menu_admin_system_task', '系统定时任务配置', 'menu_admin_crontab_manage', '/admin/manage/crontab/system-task', 'admin/manage/crontab/SystemTaskView', NULL, 4, 0, 'SidebarLayout', '1', NULL, '2025-11-25 13:45:00', '2025-11-25 13:45:00', NULL, 0), ('8004', 'menu_admin_system_task', '系统定时任务配置', 'menu_admin_crontab_manage', '/admin/manage/crontab/system-task', 'admin/manage/crontab/SystemTaskView', NULL, 4, 0, 'SidebarLayout', '1', NULL, '2025-11-25 13:45:00', '2025-11-25 13:45:00', NULL, 0),
-- 消息通知模块菜单 (9000-9999) -- 消息通知模块菜单 (9000-9999)
('9001', 'menu_admin_message_manage', '消息管理', NULL, '/admin/manage/message', 'admin/manage/message/MessageManageView', 'admin/notice.svg', 9, 0, 'SidebarLayout', '1', NULL, '2025-11-13 10:00:00', '2025-11-13 10:00:00', NULL, 0), ('9001', 'menu_admin_message_manage', '消息管理', NULL, '/admin/manage/message', 'admin/manage/message/MessageManageView', 'admin/notice.svg', 9, 0, 'SidebarLayout', '1', NULL, '2025-11-13 10:00:00', '2025-11-13 10:00:00', NULL, 0),
-- 用户端消息中心菜单 (650-699) -- 用户端消息详情菜单 (651)
('650', 'menu_user_message_center', '消息中心', NULL, '/user/message', 'user/message/MyMessageListView', NULL, 7, 1, 'NavigationLayout', '1', NULL, '2025-11-13 10:00:00', '2025-11-13 10:00:00', NULL, 0), ('651', 'menu_user_message_detail', '消息详情', 'menu_user_message_center', '/user-center/message/detail/:messageID', 'user/message/MyMessageDetailView', NULL, 1, 3, 'NavigationLayout', '1', NULL, '2025-11-13 10:00:00', '2025-11-13 10:00:00', NULL, 0);
('651', 'menu_user_message_detail', '消息详情', 'menu_user_message_center', '/user/message/detail/:messageID', 'user/message/MyMessageDetailView', NULL, 1, 3, 'NavigationLayout', '1', NULL, '2025-11-13 10:00:00', '2025-11-13 10:00:00', NULL, 0);
-- 插入菜单权限关联数据 -- 插入菜单权限关联数据
-- ============================================ -- ============================================

View File

@@ -1,10 +1,4 @@
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 10C0 4.47715 4.47715 0 10 0H38C43.5229 0 48 4.47715 48 10V38C48 43.5229 43.5228 48 38 48H10C4.47715 48 0 43.5228 0 38V10Z" fill="#E7000B"/> <rect width="80" height="80" rx="40" fill="#C62828"/>
<path d="M24 20V16H20" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M39.816 41.372H23.44V18.786H56.514V41.372H41.518C43.7873 44.3467 45.2593 46.3247 45.934 47.306L41.472 50.388C39.3253 47.2293 37.7153 44.9447 36.642 43.534L39.816 41.372ZM37.424 27.664V23.524H28.868V27.664H37.424ZM42.622 27.664H51.086V23.524H42.622V27.664ZM37.424 32.356H28.868V36.45H37.424V32.356ZM42.622 32.356V36.45H51.086V32.356H42.622ZM55.364 43.028C56.2227 44.3773 57.2807 46.1407 58.538 48.318C59.826 50.4953 60.884 52.3353 61.712 53.838L56.836 56.552C56.008 54.9267 54.996 53.056 53.8 50.94C52.604 48.7933 51.5767 47.0147 50.718 45.604L55.364 43.028ZM46.486 51.676C46.578 50.756 46.624 49.2687 46.624 47.214L51.73 48.272C51.73 49.2533 51.5767 51.17 51.27 54.022C51.1473 55.31 50.856 56.2913 50.396 56.966C49.9667 57.6407 49.292 58.116 48.372 58.392C47.4827 58.6987 46.1793 58.8827 44.462 58.944C42.4993 59.0053 40.9967 59.036 39.954 59.036C38.942 59.036 37.424 59.0053 35.4 58.944C33.3147 58.852 31.7967 58.392 30.846 57.564C29.926 56.736 29.466 55.402 29.466 53.562V43.488H34.756V51.492C34.756 52.32 34.94 52.9027 35.308 53.24C35.676 53.5773 36.2893 53.7767 37.148 53.838C37.884 53.8687 39.0033 53.884 40.506 53.884C41.978 53.884 43.082 53.8687 43.818 53.838C44.7993 53.8073 45.474 53.6233 45.842 53.286C46.21 52.9487 46.4247 52.412 46.486 51.676ZM18.472 55.08C19.3613 53.4853 20.2047 51.6913 21.002 49.698C21.83 47.7047 22.4893 45.742 22.98 43.81L28.178 45.006C27.5953 46.9687 26.89 49.0387 26.062 51.216C25.2647 53.3627 24.4367 55.2793 23.578 56.966L18.472 55.08Z" fill="white"/>
<path d="M30 20H18C16.8954 20 16 20.8954 16 22V30C16 31.1046 16.8954 32 18 32H30C31.1046 32 32 31.1046 32 30V22C32 20.8954 31.1046 20 30 20Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14 26H16" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M32 26H34" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M27 25V27" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M21 25V27" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1004 B

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -1,5 +1,4 @@
<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="80" height="80" rx="40" fill="#C62828"/> <rect width="80" height="80" rx="40" fill="#C62828"/>
<path d="M44.3335 20.4997C44.5794 20.4997 44.8244 20.5052 45.0679 20.5153C44.5924 21.8662 44.3335 23.3192 44.3335 24.8327C44.3335 32.0124 50.1538 37.8327 57.3335 37.8327C58.8471 37.8327 60.301 37.5739 61.6519 37.0983C61.662 37.3417 61.6665 37.5869 61.6665 37.8327C61.6665 47.4056 53.9064 55.1666 44.3335 55.1667V62.7497C33.5002 58.4163 18.3335 51.916 18.3335 37.8327C18.3337 28.26 26.0938 20.4998 35.6665 20.4997H44.3335ZM24.8335 41.444H29.8892V33.4997H24.8335V41.444ZM32.7778 33.4997V41.444H37.8335V33.4997H32.7778ZM56.314 16.859C56.6964 15.936 57.9708 15.936 58.353 16.859L58.9019 18.1833C59.8379 20.4428 61.5836 22.247 63.7788 23.2233L65.3325 23.9147C66.2225 24.3106 66.2226 25.6058 65.3325 26.0016L63.687 26.7331C61.5468 27.685 59.8316 29.4256 58.8794 31.611L58.3452 32.8366C57.9543 33.7336 56.713 33.7335 56.3218 32.8366L55.7876 31.611C54.8353 29.4256 53.1202 27.685 50.98 26.7331L49.3345 26.0016C48.4446 25.6058 48.4446 24.3105 49.3345 23.9147L50.8882 23.2233C53.0833 22.2469 54.8294 20.4428 55.7651 18.1833L56.314 16.859Z" fill="white"/> <path d="M39.816 41.372H23.44V18.786H56.514V41.372H41.518C43.7873 44.3467 45.2593 46.3247 45.934 47.306L41.472 50.388C39.3253 47.2293 37.7153 44.9447 36.642 43.534L39.816 41.372ZM37.424 27.664V23.524H28.868V27.664H37.424ZM42.622 27.664H51.086V23.524H42.622V27.664ZM37.424 32.356H28.868V36.45H37.424V32.356ZM42.622 32.356V36.45H51.086V32.356H42.622ZM55.364 43.028C56.2227 44.3773 57.2807 46.1407 58.538 48.318C59.826 50.4953 60.884 52.3353 61.712 53.838L56.836 56.552C56.008 54.9267 54.996 53.056 53.8 50.94C52.604 48.7933 51.5767 47.0147 50.718 45.604L55.364 43.028ZM46.486 51.676C46.578 50.756 46.624 49.2687 46.624 47.214L51.73 48.272C51.73 49.2533 51.5767 51.17 51.27 54.022C51.1473 55.31 50.856 56.2913 50.396 56.966C49.9667 57.6407 49.292 58.116 48.372 58.392C47.4827 58.6987 46.1793 58.8827 44.462 58.944C42.4993 59.0053 40.9967 59.036 39.954 59.036C38.942 59.036 37.424 59.0053 35.4 58.944C33.3147 58.852 31.7967 58.392 30.846 57.564C29.926 56.736 29.466 55.402 29.466 53.562V43.488H34.756V51.492C34.756 52.32 34.94 52.9027 35.308 53.24C35.676 53.5773 36.2893 53.7767 37.148 53.838C37.884 53.8687 39.0033 53.884 40.506 53.884C41.978 53.884 43.082 53.8687 43.818 53.838C44.7993 53.8073 45.474 53.6233 45.842 53.286C46.21 52.9487 46.4247 52.412 46.486 51.676ZM18.472 55.08C19.3613 53.4853 20.2047 51.6913 21.002 49.698C21.83 47.7047 22.4893 45.742 22.98 43.81L28.178 45.006C27.5953 46.9687 26.89 49.0387 26.062 51.216C25.2647 53.3627 24.4367 55.2793 23.578 56.966L18.472 55.08Z" fill="white"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,10 @@
<svg width="77" height="77" viewBox="0 0 77 77" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="77" height="77" rx="6" fill="white"/>
<path d="M49.1296 16.4629C50.3467 16.463 51.3328 17.45 51.3328 18.667V45.1113H60.1482V53.9258C60.1482 57.5771 57.1882 60.5371 53.5369 60.5371H22.6853C19.0341 60.5371 16.074 57.5771 16.074 53.9258V18.667C16.074 17.4499 17.061 16.4629 18.2781 16.4629H49.1296ZM51.3328 53.9258C51.3328 55.1429 52.3198 56.1298 53.5369 56.1299C54.754 56.1299 55.741 55.1429 55.741 53.9258V49.5186H51.3328V53.9258ZM32.9167 38.917L28.1667 34.167L25.0007 37.334L32.9167 45.25L44.0007 34.167L40.8337 31L32.9167 38.917Z" fill="url(#paint0_linear_1534_4780)"/>
<defs>
<linearGradient id="paint0_linear_1534_4780" x1="19.4445" y1="16.7219" x2="51.7222" y2="60.2775" gradientUnits="userSpaceOnUse">
<stop stop-color="#82B7FD"/>
<stop offset="1" stop-color="#2F6AFF"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 918 B

View File

@@ -0,0 +1,9 @@
<svg width="53" height="53" viewBox="0 0 53 53" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M41.8702 48.4813H11.0183C7.36713 48.4813 4.40723 45.5215 4.40723 41.8702V6.61093C4.40723 5.39387 5.39387 4.40723 6.61093 4.40723H37.4628C38.6799 4.40723 39.6665 5.39387 39.6665 6.61093V33.0554H48.4813V41.8702C48.4813 45.5215 45.5215 48.4813 41.8702 48.4813ZM39.6665 37.4628V41.8702C39.6665 43.0873 40.6531 44.0739 41.8702 44.0739C43.0873 44.0739 44.0739 43.0873 44.0739 41.8702V37.4628H39.6665ZM13.222 15.4257V19.8332H30.8517V15.4257H13.222ZM13.222 24.2406V28.648H30.8517V24.2406H13.222ZM13.222 33.0554V37.4628H24.2406V33.0554H13.222Z" fill="url(#paint0_linear_1534_4772)"/>
<defs>
<linearGradient id="paint0_linear_1534_4772" x1="7.77772" y1="4.66623" x2="40.0555" y2="48.2218" gradientUnits="userSpaceOnUse">
<stop stop-color="#FD9082"/>
<stop offset="1" stop-color="#FD6D78"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 918 B

View File

@@ -8,10 +8,17 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter, useRoute } from 'vue-router';
const home = ref<boolean>(false); const home = ref<boolean>(false);
const router = useRouter(); const router = useRouter();
const route = useRoute();
// 组件加载时根据当前路由判断是否在管理页
onMounted(() => {
// 如果当前路径以 /admin 开头,说明在管理页,应该显示"前往用户页"
home.value = route.path.startsWith('/admin');
});
function changeHome(){ function changeHome(){
if(home.value){ if(home.value){

View File

@@ -11,7 +11,7 @@
<div class="notice-dropdown" :class="{ 'show': dropdownVisible }" :style="dropdownStyle" <div class="notice-dropdown" :class="{ 'show': dropdownVisible }" :style="dropdownStyle"
v-if="dropdownVisible" @mouseenter="openDropdown" @mouseleave="closeDropdown"> v-if="dropdownVisible" @mouseenter="openDropdown" @mouseleave="closeDropdown">
<div class="notice-header"> <div class="notice-header">
<h3>通知</h3> <h3>消息通知</h3>
<span class="notice-count" v-if="unreadCount > 0">{{ unreadCount }}条未读</span> <span class="notice-count" v-if="unreadCount > 0">{{ unreadCount }}条未读</span>
</div> </div>
<div class="notice-list" v-loading="loading"> <div class="notice-list" v-loading="loading">
@@ -195,7 +195,7 @@ async function handleNoticeClick(item: MessageUserVO) {
} }
function viewAll() { function viewAll() {
router.push('/user/message'); router.push('/user-center/message');
forceCloseDropdown(); forceCloseDropdown();
} }

View File

@@ -43,6 +43,18 @@
</div> </div>
</Teleport> </Teleport>
</div> </div>
<!-- 特殊标签专题报告和思政案例 -->
<div
v-for="tag in specialTags"
:key="tag.tagID"
class="nav-item special-tag"
:class="{ active: isTagActive(tag) }"
>
<div class="nav-link" @click="handleTagClick(tag)">
<span>{{ tag.name }}</span>
</div>
</div>
</div> </div>
<!-- 右侧用户区域 --> <!-- 右侧用户区域 -->
<div class="nav-right"> <div class="nav-right">
@@ -56,11 +68,13 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed } from 'vue'; import { ref, computed, onMounted } from 'vue';
import { useRouter, useRoute } from 'vue-router'; import { useRouter, useRoute } from 'vue-router';
import { useStore } from 'vuex'; import { useStore } from 'vuex';
import type { SysMenu } from '@/types'; import type { SysMenu } from '@/types';
import { MenuType } from '@/types/enums'; import { MenuType } from '@/types/enums';
import type { Tag } from '@/types/resource';
import { resourceTagApi } from '@/apis/resource';
// @ts-ignore - Vue 3.5 组件导入兼容性 // @ts-ignore - Vue 3.5 组件导入兼容性
import {UserDropdown, Search, Notice} from '@/components/base'; import {UserDropdown, Search, Notice} from '@/components/base';
const router = useRouter(); const router = useRouter();
@@ -70,11 +84,32 @@ const store = useStore();
const activeDropdown = ref<string | null>(null); const activeDropdown = ref<string | null>(null);
const searchKeyword = ref(''); const searchKeyword = ref('');
const dropdownPositions = ref<Record<string, { left: number; top: number; width: number }>>({}); const dropdownPositions = ref<Record<string, { left: number; top: number; width: number }>>({});
const specialTags = ref<Tag[]>([]); // 特殊标签:专题报告和思政案例
// 获取所有菜单 // 获取所有菜单
const allMenus = computed(() => store.getters['auth/menuTree']); const allMenus = computed(() => store.getters['auth/menuTree']);
const userInfo = computed(() => store.getters['auth/user']); const userInfo = computed(() => store.getters['auth/user']);
// 加载特殊标签
onMounted(async () => {
await loadSpecialTags();
});
// 加载特殊标签(专题报告和思政案例)
async function loadSpecialTags() {
try {
const res = await resourceTagApi.getTagsByType(1); // 1 = 文章分类标签
if (res.success && res.dataList) {
// 只获取专题报告(tag005)和思政案例(tag006)
specialTags.value = res.dataList.filter((tag: Tag) =>
tag.tagID === 'tag_article_005' || tag.tagID === 'tag_article_006'
);
}
} catch (error) {
console.error('加载特殊标签失败:', error);
}
}
// 获取第一层的导航菜单MenuType.NAVIGATION过滤掉用户相关菜单 // 获取第一层的导航菜单MenuType.NAVIGATION过滤掉用户相关菜单
const navigationMenus = computed(() => { const navigationMenus = computed(() => {
const menus = allMenus.value.filter((menu: SysMenu) => { const menus = allMenus.value.filter((menu: SysMenu) => {
@@ -116,6 +151,15 @@ function getNavigationChildren(menu: SysMenu): SysMenu[] {
function isActive(menu: SysMenu): boolean { function isActive(menu: SysMenu): boolean {
if (!menu.url) return false; if (!menu.url) return false;
// 特殊处理:如果是资源中心菜单,且 URL 中有特殊标签的 tagID则不激活
if (menu.url === '/resource-center' && route.path === '/resource-center') {
const tagID = route.query.tagID as string;
// 如果 tagID 是特殊标签(专题报告或思政案例),则不激活资源中心菜单
if (tagID === 'tag_article_005' || tagID === 'tag_article_006') {
return false;
}
}
// 精确匹配 // 精确匹配
if (route.path === menu.url) return true; if (route.path === menu.url) return true;
@@ -206,6 +250,20 @@ function handleNavClick(menu: SysMenu) {
} }
} }
// 处理特殊标签点击
function handleTagClick(tag: Tag) {
// 导航到资源中心,并传递 tagID 参数
router.push({
path: '/resource-center',
query: { tagID: tag.tagID }
});
}
// 判断标签是否激活
function isTagActive(tag: Tag): boolean {
return route.path === '/resource-center' && route.query.tagID === tag.tagID;
}
// 处理搜索 // 处理搜索
function handleSearch(keyword: string) { function handleSearch(keyword: string) {
if (keyword && keyword.trim()) { if (keyword && keyword.trim()) {

View File

@@ -105,7 +105,7 @@
<div class="message-avatar"> <div class="message-avatar">
<div class="avatar-circle ai-avatar"> <div class="avatar-circle ai-avatar">
<img v-if="agentAvatarUrl" :src="agentAvatarUrl" alt="AI助手" class="ai-avatar-img" /> <img v-if="agentAvatarUrl" :src="agentAvatarUrl" alt="AI助手" class="ai-avatar-img" />
<span v-else>🤖</span> <img v-else src="@/assets/imgs/assistant.svg" alt="AI助手" class="ai-avatar-img" />
</div> </div>
</div> </div>
<div class="message-content"> <div class="message-content">
@@ -1142,6 +1142,7 @@ onUnmounted(() => {
.ai-agent { .ai-agent {
position: fixed; position: fixed;
z-index: 50; z-index: 50;
transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
&.expanded { &.expanded {
top: 50%; top: 50%;

View File

@@ -499,7 +499,7 @@ onMounted(() => {
height: 80%; height: 80%;
max-width: 1142px; max-width: 1142px;
margin: auto auto; margin: auto auto;
box-shadow: 0px 4px 30px 0px rgba(176, 196, 225, 0.25); box-shadow: 0px 4px 30px 0px rgba(176, 196, 225, 0.5);
border-radius: 30px; border-radius: 30px;
overflow: hidden; overflow: hidden;
justify-content: center; justify-content: center;

View File

@@ -517,7 +517,7 @@ onUnmounted(() => {
height: 80%; height: 80%;
max-width: 1142px; max-width: 1142px;
margin: auto auto; margin: auto auto;
box-shadow: 0px 4px 30px 0px rgba(176, 196, 225, 0.25); box-shadow: 0px 4px 30px 0px rgba(176, 196, 225, 0.5);
border-radius: 30px; border-radius: 30px;
overflow: hidden; overflow: hidden;
justify-content: center; justify-content: center;

View File

@@ -311,12 +311,14 @@ onMounted(() => {
padding: 20px; padding: 20px;
.header { .header {
max-width: 900px; // max-width: 900px;
width: 90%;
margin: 0 auto 20px; margin: 0 auto 20px;
} }
.detail-container { .detail-container {
max-width: 900px; // max-width: 900px;
width: 90%;
margin: 0 auto; margin: 0 auto;
.message-card { .message-card {

View File

@@ -5,9 +5,9 @@
:category-name="currentCategoryName" :category-name="currentCategoryName"
:show-article-mode="showArticle" :show-article-mode="showArticle"
/> />
<div class="search-wrapper"> <!-- <div class="search-wrapper">
<Search @search="handleSearch" /> <Search @search="handleSearch" />
</div> </div> -->
<div class="content-wrapper"> <div class="content-wrapper">
<div class="content-container"> <div class="content-container">
<ResourceSideBar <ResourceSideBar
@@ -37,11 +37,15 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'; import { ref, onMounted, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { ResourceSideBar, ResourceList, ResourceArticle } from './components'; import { ResourceSideBar, ResourceList, ResourceArticle } from './components';
import { Search, CenterHead } from '@/components/base'; import { Search, CenterHead } from '@/components/base';
import type { Resource, Tag } from '@/types/resource'; import type { Resource, Tag } from '@/types/resource';
import { resourceApi } from '@/apis/resource'; import { resourceApi, resourceTagApi } from '@/apis/resource';
const route = useRoute();
const router = useRouter();
const showArticle = ref(false); const showArticle = ref(false);
const currentCategoryId = ref('tag_article_001'); const currentCategoryId = ref('tag_article_001');
@@ -51,16 +55,59 @@ const searchKeyword = ref('');
const resourceListRef = ref(); const resourceListRef = ref();
const resourceList = ref<Resource[]>([]); const resourceList = ref<Resource[]>([]);
// 组件加载时检查 URL 参数
onMounted(async () => {
const tagID = route.query.tagID as string;
if (tagID) {
await loadTagInfo(tagID);
}
});
// 监听路由参数变化
watch(() => route.query.tagID, async (newTagID) => {
if (newTagID && typeof newTagID === 'string') {
await loadTagInfo(newTagID);
}
});
// 加载标签信息
async function loadTagInfo(tagID: string) {
try {
const res = await resourceTagApi.getTagsByType(1); // 1 = 文章分类标签
if (res.success && res.dataList) {
const tag = res.dataList.find((t: Tag) => t.tagID === tagID);
if (tag) {
currentCategoryId.value = tag.tagID || '';
currentCategoryName.value = tag.name || '';
searchKeyword.value = '';
showArticle.value = false;
}
}
} catch (error) {
console.error('加载标签信息失败:', error);
}
}
function handleCategoryChange(category: Tag) { function handleCategoryChange(category: Tag) {
currentCategoryId.value = category.tagID || category.id || ''; currentCategoryId.value = category.tagID || category.id || '';
currentCategoryName.value = category.name || ''; currentCategoryName.value = category.name || '';
searchKeyword.value = ''; searchKeyword.value = '';
showArticle.value = false; showArticle.value = false;
// 清除 URL 中的 tagID 参数,确保 URL 与实际显示的分类一致
if (route.query.tagID) {
router.replace({ path: route.path, query: {} });
}
} }
function handleSearch(keyword: string) { function handleSearch(keyword: string) {
searchKeyword.value = keyword; searchKeyword.value = keyword;
showArticle.value = false; showArticle.value = false;
// 清除 URL 中的 tagID 参数
if (route.query.tagID) {
router.replace({ path: route.path, query: {} });
}
} }
function handleListUpdated(list: Resource[]) { function handleListUpdated(list: Resource[]) {

View File

@@ -6,19 +6,6 @@
<!-- 任务进度 --> <!-- 任务进度 -->
<h2 class="section-title">任务进度</h2> <h2 class="section-title">任务进度</h2>
<div class="progress-card"> <div class="progress-card">
<div class="progress-info">
<div class="progress-header">
<span class="progress-text">当前学习进度{{ completedCount }}/{{ totalCount }}</span>
<!-- <span class="progress-percent">{{ progressPercent }}%</span> -->
<!-- <span class="progress-level">{{ userLevel }}</span> -->
</div>
<div class="progress-bar-container">
<div class="progress-bar">
<div class="progress-fill" :style="{ width: progressPercent + '%' }"></div>
</div>
</div>
</div>
<div class="task-stats"> <div class="task-stats">
<div class="stat-card pending"> <div class="stat-card pending">
<div class="stat-content"> <div class="stat-content">
@@ -26,7 +13,7 @@
<div class="stat-number">{{ pendingCount }}</div> <div class="stat-number">{{ pendingCount }}</div>
</div> </div>
<div class="stat-icon pending-icon"> <div class="stat-icon pending-icon">
<el-icon><DocumentCopy /></el-icon> <el-icon><img src="@/assets/imgs/learningtask.svg" /></el-icon>
</div> </div>
</div> </div>
@@ -36,7 +23,20 @@
<div class="stat-number">{{ completedCount }}</div> <div class="stat-number">{{ completedCount }}</div>
</div> </div>
<div class="stat-icon completed-icon"> <div class="stat-icon completed-icon">
<el-icon><DocumentChecked /></el-icon> <el-icon><img src="@/assets/imgs/finishtask.svg" /></el-icon>
</div>
</div>
</div>
<div class="progress-info">
<div class="progress-header">
<span class="progress-text">当前学习进度{{ completedCount }}/{{ totalCount }}</span>
<!-- <span class="progress-percent">{{ progressPercent }}%</span> -->
<!-- <span class="progress-level">{{ userLevel }}</span> -->
</div>
<div class="progress-bar-container">
<div class="progress-bar">
<div class="progress-fill" :style="{ width: progressPercent + '%' }"></div>
</div> </div>
</div> </div>
</div> </div>
@@ -310,8 +310,10 @@ function getDeadlineStatus(task: TaskItemVO): { show: boolean; text: string; typ
box-shadow: 0 17px 22.4px rgba(164, 182, 199, 0.1); box-shadow: 0 17px 22.4px rgba(164, 182, 199, 0.1);
.progress-info { .progress-info {
margin-bottom: 40px; margin-top: 20px;
border-radius: 10px;
background: #F7F8F9;
padding: 10px;
.progress-header { .progress-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@@ -338,16 +340,17 @@ function getDeadlineStatus(task: TaskItemVO): { show: boolean; text: string; typ
} }
.progress-bar-container { .progress-bar-container {
// margin: 20px;
.progress-bar { .progress-bar {
width: 100%; width: 100%;
height: 12px; height: 12px;
background: #F7F8F9; background: #ffffff;
border-radius: 27px; border-radius: 27px;
overflow: hidden; overflow: hidden;
.progress-fill { .progress-fill {
height: 100%; height: 100%;
background: linear-gradient(143deg, #FD9082 1%, #FD6D78 99%); background: linear-gradient(143deg, #3D3D3D 1%, #3D3D3D 99%);
border-radius: 27px; border-radius: 27px;
transition: width 0.3s ease; transition: width 0.3s ease;
} }
@@ -370,6 +373,8 @@ function getDeadlineStatus(task: TaskItemVO): { show: boolean; text: string; typ
align-items: center; align-items: center;
&.pending { &.pending {
background: linear-gradient(45deg, #ffffff 0%, #FFF1F0 100%);
.stat-icon { .stat-icon {
background: #FFF5F4; background: #FFF5F4;
@@ -384,7 +389,7 @@ function getDeadlineStatus(task: TaskItemVO): { show: boolean; text: string; typ
} }
&.completed { &.completed {
background: #FBFDFF; background: linear-gradient(45deg, #ffffff 0%, #F0FAFF 100%);
.stat-icon { .stat-icon {
background: #EFF8FF; background: #EFF8FF;
@@ -548,7 +553,7 @@ function getDeadlineStatus(task: TaskItemVO): { show: boolean; text: string; typ
// 学习进度 // 学习进度
.task-progress { .task-progress {
margin-bottom: auto; margin-top: auto;
padding-bottom: 20px; padding-bottom: 20px;
.progress-header { .progress-header {
@@ -593,7 +598,7 @@ function getDeadlineStatus(task: TaskItemVO): { show: boolean; text: string; typ
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
gap: 20px; gap: 20px;
margin-top: auto; // margin-top: auto;
.task-time { .task-time {
display: flex; display: flex;