-
+
+ {
+ // 子级栏目
+ childCategory && childCategory.map((item, index) => {
+ return (
+
Taro.navigateTo({url: `./list?id=${item.navigationId}`})}
+ >
+ {/* 标题 */}
+
+ {item.title}
+
+
+ )
+ })
+ }
{
// 终极文章列表
list.map((item, index) => {
@@ -80,7 +121,8 @@ const Index = () => {
})
}
- >
+
+
)
}
export default Index
diff --git a/src/honor/list.config.ts b/src/honor/list.config.ts
new file mode 100644
index 0000000..a0adf57
--- /dev/null
+++ b/src/honor/list.config.ts
@@ -0,0 +1,5 @@
+export default definePageConfig({
+ navigationBarTitleText: '光荣榜',
+ navigationBarBackgroundColor: '#d32f2f',
+ navigationBarTextStyle: 'white'
+})
diff --git a/src/honor/list.scss b/src/honor/list.scss
new file mode 100644
index 0000000..1345fe3
--- /dev/null
+++ b/src/honor/list.scss
@@ -0,0 +1,186 @@
+.veteran-page {
+ min-height: 100vh;
+ background: linear-gradient(135deg, #d32f2f 0%, #b71c1c 100%);
+
+ .hero-section {
+ position: relative;
+ padding: 40px 20px 60px;
+ background: linear-gradient(135deg, #d32f2f 0%, #b71c1c 100%);
+ overflow: hidden;
+
+ .hero-content {
+ position: relative;
+ z-index: 2;
+ text-align: center;
+
+ .hero-title {
+ font-size: 28px;
+ font-weight: bold;
+ color: #fff;
+ margin-bottom: 20px;
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
+ }
+
+ .hero-subtitle {
+ font-size: 14px;
+ line-height: 1.6;
+ color: rgba(255, 255, 255, 0.9);
+ margin: 0 auto;
+ max-width: 320px;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
+ }
+ }
+
+ .hero-decoration {
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: 100%;
+ height: 100%;
+ pointer-events: none;
+
+ .decoration-circle {
+ position: absolute;
+ top: -50px;
+ right: -50px;
+ width: 150px;
+ height: 150px;
+ border: 2px solid rgba(255, 255, 255, 0.1);
+ border-radius: 50%;
+ animation: rotate 20s linear infinite;
+ }
+
+ .decoration-star {
+ position: absolute;
+ bottom: 20px;
+ right: 30px;
+ width: 0;
+ height: 0;
+ border-left: 15px solid transparent;
+ border-right: 15px solid transparent;
+ border-bottom: 10px solid rgba(255, 255, 255, 0.1);
+ transform: rotate(35deg);
+
+ &::before {
+ content: '';
+ position: absolute;
+ left: -15px;
+ top: 3px;
+ width: 0;
+ height: 0;
+ border-left: 15px solid transparent;
+ border-right: 15px solid transparent;
+ border-bottom: 10px solid rgba(255, 255, 255, 0.1);
+ transform: rotate(-70deg);
+ }
+ }
+ }
+ }
+
+ .veteran-list {
+ padding: 20px 15px;
+
+ .veteran-card {
+ background: #fff;
+ border-radius: 12px;
+ margin-bottom: 15px;
+ padding: 20px;
+ display: flex;
+ align-items: flex-start;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+ border: 2px solid #d32f2f;
+ transition: all 0.3s ease;
+ cursor: pointer;
+
+ &:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
+ }
+
+ .veteran-avatar {
+ flex-shrink: 0;
+ margin-right: 15px;
+
+ .avatar-img {
+ width: 60px;
+ height: 60px;
+ border-radius: 50%;
+ object-fit: cover;
+ border: 3px solid #d32f2f;
+ background: #f5f5f5;
+ }
+ }
+
+ .veteran-info {
+ flex: 1;
+
+ .veteran-name {
+ font-size: 18px;
+ font-weight: bold;
+ color: #d32f2f;
+ margin: 0 0 8px 0;
+ }
+
+ .veteran-description {
+ font-size: 13px;
+ line-height: 1.5;
+ color: #666;
+ text-align: justify;
+ }
+ }
+ }
+ }
+}
+
+@keyframes rotate {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+/* 响应式设计 */
+@media (max-width: 375px) {
+ .veteran-page {
+ .hero-section {
+ padding: 30px 15px 50px;
+
+ .hero-content {
+ .hero-title {
+ font-size: 24px;
+ }
+
+ .hero-subtitle {
+ font-size: 13px;
+ }
+ }
+ }
+
+ .veteran-list {
+ padding: 15px 10px;
+
+ .veteran-card {
+ padding: 15px;
+
+ .veteran-avatar {
+ .avatar-img {
+ width: 50px;
+ height: 50px;
+ }
+ }
+
+ .veteran-info {
+ .veteran-name {
+ font-size: 16px;
+ }
+
+ .veteran-description {
+ font-size: 12px;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/honor/list.tsx b/src/honor/list.tsx
new file mode 100644
index 0000000..b7dfbc7
--- /dev/null
+++ b/src/honor/list.tsx
@@ -0,0 +1,123 @@
+import {useEffect, useState} from "react";
+import {pageCmsArticle} from "@/api/cms/cmsArticle";
+import {CmsArticle} from "@/api/cms/cmsArticle/model";
+import Taro from '@tarojs/taro'
+import {useRouter} from '@tarojs/taro'
+import {Image} from '@nutui/nutui-react-taro'
+import {getCmsNavigation} from "@/api/cms/cmsNavigation";
+import {CmsNavigation} from "@/api/cms/cmsNavigation/model";
+
+/**
+ * 文章终极列表
+ * @constructor
+ */
+const List = () => {
+ const {params} = useRouter();
+ const [navigation, setNavigation] = useState
()
+ const [list, setList] = useState([])
+
+ const reload = async () => {
+ // 获取栏目ID
+ const categoryId = Number(params.id);
+ // 当前栏目信息
+ const navs = await getCmsNavigation(categoryId);
+ // 终极新闻列表
+ const articles = await pageCmsArticle({categoryId});
+
+ // 当前栏目信息
+ if (navs) {
+ setNavigation(navs);
+ }
+ // 新闻列表
+ if (articles) {
+ setList(articles?.list || [])
+ }
+ }
+
+ useEffect(() => {
+ reload()
+ }, [])
+
+ return (
+
+
+
+
+
+
+ {/* 标题 */}
+
+ {navigation?.categoryName}
+
+
+
+
+
+ {
+ // 终极文章列表
+ list.map((item, index) => {
+ return (
+
Taro.navigateTo({url: `./detail?id=${item.articleId}`})}
+ >
+ {
+ // 图片容器
+ item.image && (
+
+

+
+ )
+ }
+ {/* 标题 */}
+
+
{item.title}
+
+ {item.comments || '暂无'}
+
+
+
+ )
+ })
+ }
+
+
+
+ )
+}
+export default List
diff --git a/src/pages/ai/index.config.ts b/src/pages/ai/index.config.ts
new file mode 100644
index 0000000..5a4c284
--- /dev/null
+++ b/src/pages/ai/index.config.ts
@@ -0,0 +1,3 @@
+export default definePageConfig({
+ navigationBarTitleText: 'AI问答'
+})
diff --git a/src/pages/ai/index.scss b/src/pages/ai/index.scss
new file mode 100644
index 0000000..c9028af
--- /dev/null
+++ b/src/pages/ai/index.scss
@@ -0,0 +1,289 @@
+.ai-chat {
+ height: 94vh;
+ display: flex;
+ flex-direction: column;
+ background-color: #f5f5f5;
+
+ .chat-header {
+ background: linear-gradient(135deg, #a6ea66 0%, #ead1ff 100%);
+ color: white;
+ padding: 16px;
+ text-align: center;
+ font-size: 18px;
+ font-weight: bold;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ }
+
+ .chat-container {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ }
+
+ .messages-container {
+ flex: 1;
+ padding: 16px;
+ overflow-y: auto;
+ scroll-behavior: smooth;
+
+ &::-webkit-scrollbar {
+ width: 4px;
+ }
+
+ &::-webkit-scrollbar-track {
+ background: transparent;
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: rgba(0, 0, 0, 0.2);
+ border-radius: 2px;
+ }
+ }
+
+ .message {
+ margin-bottom: 16px;
+ display: flex;
+ align-items: flex-start;
+ animation: fadeInUp 0.3s ease-out;
+
+ &.user {
+ flex-direction: row-reverse;
+
+ .message-content {
+ background: linear-gradient(135deg, #ff3535 0%, #FF0000 100%);
+ color: white;
+ margin-left: 0;
+ margin-right: 12px;
+ }
+ }
+
+ &.ai {
+ flex-direction: row;
+
+ .message-content {
+ background: white;
+ color: #333;
+ margin-left: 12px;
+ margin-right: 0;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ }
+ }
+
+ .avatar {
+ width: 40px;
+ height: 40px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 18px;
+ flex-shrink: 0;
+
+ &.user-avatar {
+ background: linear-gradient(135deg, #df2626 0%, #d10a0a 100%);
+ color: white;
+ }
+
+ &.ai-avatar {
+ background: linear-gradient(135deg, #ff6b6b 0%, #feca57 100%);
+ color: white;
+ }
+ }
+
+ .message-content {
+ max-width: 70%;
+ padding: 12px 16px;
+ border-radius: 18px;
+ word-wrap: break-word;
+ line-height: 1.5;
+ font-size: 14px;
+ position: relative;
+
+ .typing-indicator {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+
+ .dot {
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background-color: #999;
+ animation: typing 1.4s infinite ease-in-out;
+
+ &:nth-child(1) { animation-delay: -0.32s; }
+ &:nth-child(2) { animation-delay: -0.16s; }
+ &:nth-child(3) { animation-delay: 0s; }
+ }
+ }
+ }
+ }
+
+ .input-container {
+ background: white;
+ padding: 16px;
+ border-top: 1px solid #e0e0e0;
+ display: flex;
+ align-items: flex-end;
+ gap: 12px;
+ box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1);
+
+ .input-wrapper {
+ flex: 1;
+ position: relative;
+ }
+
+ .message-input {
+ width: 100%;
+ min-height: 40px;
+ max-height: 120px;
+ padding: 10px 16px;
+ border: 1px solid #e0e0e0;
+ border-radius: 20px;
+ font-size: 14px;
+ line-height: 1.5;
+ resize: none;
+ outline: none;
+ background: #f9f9f9;
+ transition: all 0.3s ease;
+
+ &:focus {
+ border-color: #ffc6c6;
+ background: white;
+ box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.1);
+ }
+
+ &::placeholder {
+ color: #999;
+ }
+ }
+
+ .send-button {
+ width: 40px;
+ height: 40px;
+ border-radius: 50%;
+ background: linear-gradient(135deg, #ff0000 0%, #af1403 100%);
+ border: none;
+ color: white;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ flex-shrink: 0;
+
+ &:hover {
+ transform: scale(1.05);
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
+ }
+
+ &:active {
+ transform: scale(0.95);
+ }
+
+ &:disabled {
+ background: #ccc;
+ cursor: not-allowed;
+ transform: none;
+ box-shadow: none;
+ }
+ }
+ }
+
+ .quick-questions {
+ padding: 16px;
+ background: white;
+ border-top: 1px solid #e0e0e0;
+
+ .quick-title {
+ font-size: 14px;
+ color: #666;
+ margin-bottom: 12px;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ }
+
+ .questions-list {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+
+ .question-item {
+ padding: 12px 16px;
+ background: #f9f9f9;
+ border: 1px solid #e0e0e0;
+ border-radius: 12px;
+ font-size: 14px;
+ color: #333;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ text-align: left;
+
+ &:hover {
+ background: #667eea;
+ color: white;
+ border-color: #667eea;
+ transform: translateY(-1px);
+ box-shadow: 0 2px 8px rgba(102, 126, 234, 0.2);
+ }
+
+ &:active {
+ transform: translateY(0);
+ }
+ }
+ }
+ }
+
+ .empty-state {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 40px 20px;
+ text-align: center;
+
+ .empty-icon {
+ font-size: 64px;
+ margin-bottom: 16px;
+ opacity: 0.5;
+ }
+
+ .empty-title {
+ font-size: 18px;
+ font-weight: bold;
+ color: #333;
+ margin-bottom: 8px;
+ }
+
+ .empty-desc {
+ font-size: 14px;
+ color: #666;
+ line-height: 1.5;
+ }
+ }
+}
+
+@keyframes fadeInUp {
+ from {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes typing {
+ 0%, 80%, 100% {
+ transform: scale(0);
+ opacity: 0.5;
+ }
+ 40% {
+ transform: scale(1);
+ opacity: 1;
+ }
+}
diff --git a/src/pages/ai/index.tsx b/src/pages/ai/index.tsx
new file mode 100644
index 0000000..c4c6657
--- /dev/null
+++ b/src/pages/ai/index.tsx
@@ -0,0 +1,269 @@
+import { useEffect, useState, useRef } from "react";
+import { View, Textarea } from '@tarojs/components';
+// import Toast from '@tarojs/taro';
+import Taro from '@tarojs/taro';
+import { Button } from '@nutui/nutui-react-taro';
+import { User, ArrowUp } from '@nutui/icons-react-taro';
+import { sendAiMessage, stopAiMessage } from '@/api/ai';
+import { createWebSocket } from '@/utils/websocket';
+import './index.scss';
+import {APP_API_URL} from "@/utils/server";
+
+// 消息类型
+interface Message {
+ id?: string;
+ type?: 'user' | 'ai';
+ query?: string;
+ timestamp?: number;
+ isTyping?: boolean;
+ user?: string;
+ responseMode?: string;
+}
+
+/**
+ * AI问答页面
+ */
+const AiChat = () => {
+ const [messages, setMessages] = useState([]);
+ const [inputValue, setInputValue] = useState('');
+ const [isLoading, setIsLoading] = useState(false);
+ const [currentTaskId, setCurrentTaskId] = useState('');
+ const messagesEndRef = useRef(null);
+ const wsRef = useRef(null);
+
+ // 快捷问题
+ const quickQuestions = [
+ "当兵的注意事项?",
+ "男兵应征报名对象:",
+ "当兵公务员考试?",
+ "请问您还有什么问题吗?"
+ ];
+
+ // 滚动到底部
+ const scrollToBottom = () => {
+ setTimeout(() => {
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
+ }, 100);
+ };
+
+ // 初始化WebSocket连接
+ const initWebSocket = () => {
+ const wsUrl = APP_API_URL.replace('https', 'ws') + '/chat/' + Taro.getStorageSync('UserId') || 'token';
+ // const wsUrl = 'ws://127.0.0.1:9000/chat/test'
+ wsRef.current = createWebSocket(wsUrl);
+
+ wsRef.current.onMessage((data: any) => {
+ console.log('收到WebSocket消息:', data);
+
+ if (data.answer) {
+ if (data.answer === '__END__') {
+ // 消息结束,移除typing状态
+ setMessages(prev => prev.map(msg =>
+ msg.isTyping ? { ...msg, isTyping: false } : msg
+ ));
+ setIsLoading(false);
+ setCurrentTaskId('');
+ } else {
+ // 更新AI回复消息
+ setMessages(prev => {
+ const lastMessage = prev[prev.length - 1];
+ if (lastMessage && lastMessage.type === 'ai' && lastMessage.isTyping) {
+ // 更新最后一条AI消息
+ return prev.map((msg, index) =>
+ index === prev.length - 1
+ ? { ...msg, query: msg.query + data.answer }
+ : msg
+ );
+ } else {
+ // 创建新的AI消息
+ return [...prev, {
+ id: Date.now().toString(),
+ type: 'ai',
+ query: data.answer,
+ timestamp: Date.now(),
+ isTyping: true
+ }];
+ }
+ });
+
+ if (data.taskId) {
+ setCurrentTaskId(data.taskId);
+ }
+ }
+ scrollToBottom();
+ }
+ });
+
+ wsRef.current.onOpen(() => {
+ console.log('WebSocket连接成功');
+ });
+
+ wsRef.current.onError((error: any) => {
+ console.error('WebSocket连接错误:', error);
+ // Toast.showToast({ title: '连接失败,请检查网络', icon: 'error'});
+ });
+
+ wsRef.current.connect().catch((error: any) => {
+ console.error('WebSocket连接失败:', error);
+ });
+ };
+
+ useEffect(() => {
+ initWebSocket();
+ Taro.hideTabBar();
+ return () => {
+ if (wsRef.current) {
+ wsRef.current.close();
+ }
+ };
+ }, []);
+
+ useEffect(() => {
+ scrollToBottom();
+ }, [messages]);
+
+ // 发送消息
+ const handleSendMessage = async (content: string) => {
+ if (!content.trim() || isLoading) return;
+
+ const userMessage: Message = {
+ id: Date.now().toString(),
+ type: 'user',
+ query: content.trim(),
+ timestamp: Date.now(),
+ user: `${Taro.getStorageSync('UserId')}`
+ };
+ setMessages(prev => [...prev, userMessage]);
+ setInputValue('');
+ setIsLoading(true);
+
+ try {
+ await sendAiMessage({
+ query: content.trim(),
+ user: `${Taro.getStorageSync('UserId')}`,
+ responseMode: 'streaming',
+ });
+ } catch (error) {
+ console.error('发送消息失败:', error);
+ // Toast.showToast({ title: '发送失败,请重试', icon: 'error'});
+ setIsLoading(false);
+ }
+ };
+
+ // 停止AI回复
+ const handleStopMessage = async () => {
+ if (currentTaskId) {
+ try {
+ await stopAiMessage({ taskId: currentTaskId });
+ setIsLoading(false);
+ setCurrentTaskId('');
+ setMessages(prev => prev.map(msg =>
+ msg.isTyping ? { ...msg, isTyping: false } : msg
+ ));
+ } catch (error) {
+ console.error('停止消息失败:', error);
+ }
+ }
+ };
+
+ // 处理快捷问题点击
+ const handleQuickQuestion = (question: string) => {
+ console.log(question,'qqq')
+ handleSendMessage(question);
+ };
+
+ return (
+
+ {/**/}
+ {/* AI智能问答*/}
+ {/**/}
+
+
+
+ {messages.length === 0 ? (
+
+ 🤖
+ 您好!我是AI助手
+
+ 我可以为您解答各种问题,请输入您想了解的内容
+
+
+ ) : (
+ messages.map((message) => (
+
+
+ {message.type === 'user' ? : '🤖'}
+
+
+ {message.isTyping ? (
+
+
+
+
+
+ ) : (
+ message.query
+ )}
+
+
+ ))
+ )}
+
+
+
+ {messages.length === 0 && (
+
+
+ 💡 您可以问我:
+
+
+ {quickQuestions.map((question, index) => (
+ handleQuickQuestion(question)}
+ >
+ {question}
+
+ ))}
+
+
+ )}
+
+
+
+
+
+ {isLoading ? (
+ }
+ >
+
+ ) : (
+
+ )}
+
+
+
+ );
+};
+
+export default AiChat;
diff --git a/src/pages/index/Banner.tsx b/src/pages/index/Banner.tsx
index 9f8d9ab..16ff88c 100644
--- a/src/pages/index/Banner.tsx
+++ b/src/pages/index/Banner.tsx
@@ -10,7 +10,6 @@ const MyPage = () => {
const reload = () => {
pageCmsAd({keywords: '幻灯片'}).then(data => {
- console.log(data,'幻灯片')
if(data && data?.count > 0){
const cmsAd = data.list[0];
setHeight(cmsAd.height)
diff --git a/src/pages/index/Menu.tsx b/src/pages/index/Menu.tsx
index 08556e8..32a2720 100644
--- a/src/pages/index/Menu.tsx
+++ b/src/pages/index/Menu.tsx
@@ -10,8 +10,6 @@ const Page = () => {
const reload = () => {
getSiteInfo().then(res => {
- console.log(res);
- console.log(res.topNavs, 'top');
setNavItems(res.topNavs || []);
})
};
@@ -21,19 +19,21 @@ const Page = () => {
}, [])
return (
-
-
- {
- navItems.map((item) => (
-
Taro.navigateTo({url: `/${item.model}/index?id=${item.navigationId}`})}>
-
- {item?.title}
-
- ))
- }
+ <>
+
+
+ {
+ navItems?.map((item) => (
+
Taro.navigateTo({url: `/${item.model}/index?id=${item.navigationId}`})}>
+
+ {item?.title}
+
+ ))
+ }
+
-
+ >
)
}
export default Page
diff --git a/src/pages/user/components/UserCard.tsx b/src/pages/user/components/UserCard.tsx
index a88c097..48c9b01 100644
--- a/src/pages/user/components/UserCard.tsx
+++ b/src/pages/user/components/UserCard.tsx
@@ -1,4 +1,4 @@
-import {Avatar, Tag, Space} from '@nutui/nutui-react-taro'
+import {Avatar} from '@nutui/nutui-react-taro'
import {useEffect, useState} from "react";
import {User} from "@/api/system/user/model";
import navTo from "@/utils/common";
@@ -15,9 +15,9 @@ function UserCard() {
}, []);
const reload = async () => {
- setIsLogin(true)
- if(Taro.getStorageSync('UserId')){
+ if (Taro.getStorageSync('UserId')) {
setUserInfo(await getUserInfo())
+ setIsLogin(true)
}
};
@@ -35,7 +35,7 @@ function UserCard() {
}}
>
-
+
{
IsLogin && (
@@ -43,24 +43,16 @@ function UserCard() {
}
{
!IsLogin && (
-
+
Taro.navigateTo({url: '/passport/sms-login'})}
+ src={userInfo?.avatar} shape="round"/>
)
}
-
- {IsLogin ? (
-
-
- {'注册用户'}
-
- {/*{*/}
- {/* userInfo?.organizationName && (*/}
- {/* */}
- {/* {userInfo?.organizationName}
*/}
- {/* */}
- {/* )*/}
- {/*}*/}
-
- ) : ''}
+
+ {!IsLogin ? (
+
{'点击登录'}
+ ) :
+
{userInfo?.mobile}
+ }
{
maxLength={11}
value={formData.phone}
onChange={(value) => setFormData({...formData, phone: value})}
- style={{backgroundColor: '#ffffff', borderRadius: '8px'}}
+ style={{backgroundColor: '#f3f3f3', borderRadius: '8px'}}
/>
@@ -173,7 +173,7 @@ const SmsLogin = () => {
maxLength={6}
value={formData.code}
onChange={(value) => setFormData({...formData, code: value})}
- style={{ backgroundColor: '#ffffff', borderRadius: '8px'}}
+ style={{ backgroundColor: '#f3f3f3', borderRadius: '8px', marginRight: '16px'}}
/>