Quellcode durchsuchen

农技模块(AI问答、农业知识)

jiuling vor 11 Monaten
Ursprung
Commit
6b16793a5a
50 geänderte Dateien mit 4059 neuen und 49 gelöschten Zeilen
  1. 20 0
      api/services/chart.js
  2. 161 0
      api/services/knowledge.js
  3. 6 5
      config/api.js
  4. 29 0
      package-lock.json
  5. 2 1
      package.json
  6. 27 0
      pages.json
  7. 165 0
      pages/chart/index.vue
  8. 812 0
      pages/knowledge/ai-chat/index.vue
  9. 1097 0
      pages/knowledge/article-editor.vue
  10. 999 0
      pages/knowledge/detail.vue
  11. 625 33
      pages/knowledge/index.vue
  12. 5 2
      pages/login/index.vue
  13. 1 2
      pages/user/index.vue
  14. BIN
      static/icons/.DS_Store
  15. BIN
      static/icons/ai.png
  16. BIN
      static/icons/chat.png
  17. BIN
      static/icons/chat_off.png
  18. BIN
      static/icons/yidu.png
  19. 1 0
      static/images/agriculture/backup.svg
  20. 6 0
      static/images/agriculture/rice-field1.svg
  21. 6 0
      static/images/agriculture/rice-field2.svg
  22. 6 0
      static/images/agriculture/rice-field3.svg
  23. 6 0
      static/images/agriculture/rice-field4.svg
  24. 6 0
      static/images/agriculture/rice-field5.svg
  25. 0 0
      static/images/article-cover.jpg
  26. 0 0
      static/images/chat-bg-pattern.png
  27. 21 0
      static/images/chat-bg-pattern.svg
  28. BIN
      static/images/empty.png
  29. BIN
      static/images/icon.png
  30. BIN
      static/images/logo.png
  31. 0 0
      static/images/pattern-bg.png
  32. 1 0
      static/images/rice-farming/aerial-rice-field.jpg
  33. BIN
      static/images/rice-farming/rice-field1.jpg
  34. BIN
      static/images/rice-farming/rice-field2.jpg
  35. BIN
      static/images/rice-farming/rice-field3.jpg
  36. BIN
      static/images/rice-farming/rice-field4.jpg
  37. BIN
      static/images/rice-farming/rice-field5.jpg
  38. 1 0
      static/images/rice-farming/rice-harvest.jpg
  39. 1 0
      static/images/rice-farming/rice-seedlings.jpg
  40. 1 0
      static/images/rice-farming/terraced-rice-field.jpg
  41. 0 0
      static/images/rice-field1.jpg
  42. 0 0
      static/images/rice-field2.jpg
  43. 0 0
      static/images/rice-seedling.jpg
  44. 0 0
      static/images/robot-avatar.png
  45. 31 0
      static/images/robot-avatar.svg
  46. 0 0
      static/images/tmp/test.txt
  47. 0 0
      static/images/user-avatar.png
  48. 17 0
      static/images/user-avatar.svg
  49. 0 0
      static/images/video-cover.jpg
  50. 6 6
      utils/filters.js

+ 20 - 0
api/services/chart.js

@@ -0,0 +1,20 @@
+import {
+	http,
+	Method
+} from '@/utils/request.js';
+  // 使用storage模块的方法设置登录状态为false
+import storage from "@/utils/storage.js";
+const request = http.request;
+
+/**
+ * 上传用户头像和昵称
+ * @param params
+ */
+export function chartStream(params) {
+  return http.request({
+    url: "dify/chart/stream",
+    method: Method.POST,
+    // needToken: true,
+    data:params,
+  });
+}

+ 161 - 0
api/services/knowledge.js

@@ -0,0 +1,161 @@
+import {
+	http,
+	Method
+} from '@/utils/request.js';
+  // 使用storage模块的方法设置登录状态为false
+import storage from "@/utils/storage.js";
+const request = http.request;
+
+/**
+ * 获取农技知识列表
+ * @param params
+ */
+export function getTechList(params) {
+  return http.request({
+    url: "uniapp/knowledge/tech",
+    method: Method.GET,
+    // needToken: true,
+    data:params,
+  });
+}
+/**
+ * 获取政策解读列表
+ * @param params
+ */
+export function getPolicyList(params) {
+  return http.request({
+    url: "uniapp/knowledge/policy",
+    method: Method.GET,
+    // needToken: true,
+    data:params,
+  });
+}
+
+/**
+ * 获取知识文章详情
+ * @param id 文章ID
+ */
+export function getArticleDetail(id) {
+  return http.request({
+    url: `uniapp/knowledge/${id}`,
+    method: Method.GET,
+    // needToken: true,
+    headers: {
+      'Accept': 'application/json'
+    }
+  });
+}
+
+/**
+ * 获取文章相关图片
+ * @param articleId 文章ID
+ */
+export function getArticleImages(articleId) {
+  return http.request({
+    url: `uniapp/knowledge/images/${articleId}`,
+    method: Method.GET,
+  });
+}
+
+/**
+ * 获取轮播图列表
+ */
+export function getCarouselImages() {
+  return http.request({
+    url: "uniapp/knowledge/carousel",
+    method: Method.GET,
+  });
+}
+
+/**
+ * 创建文章
+ * @param article 文章数据
+ */
+export function createArticle(article) {
+  return http.request({
+    url: "uniapp/knowledge",
+    method: Method.POST,
+    // needToken: true,
+    data: article,
+    headers: {
+      'Content-Type': 'application/json'
+    }
+  });
+}
+
+/**
+ * 更新文章
+ * @param article 文章数据
+ */
+export function updateArticle(article) {
+  return http.request({
+    url: "uniapp/knowledge",
+    method: Method.PUT,
+    // needToken: true,
+    data: article,
+    headers: {
+      'Content-Type': 'application/json'
+    }
+  });
+}
+
+/**
+ * 上传文件
+ * @param formData 表单数据
+ */
+export function uploadFile(formData) {
+  return http.request({
+    url: "uniapp/file/upload",
+    method: Method.POST,
+    needToken: true,
+    data: formData,
+  });
+}
+
+/**
+ * 点赞文章
+ * @param id 文章ID
+ */
+export function likeArticle(id) {
+  return http.request({
+    url: `uniapp/knowledge/like/${id}`,
+    method: Method.POST,
+    needToken: true,
+  });
+}
+
+/**
+ * 取消点赞文章
+ * @param id 文章ID
+ */
+export function unlikeArticle(id) {
+  return http.request({
+    url: `uniapp/knowledge/like/${id}`,
+    method: Method.DELETE,
+    needToken: true,
+  });
+}
+
+/**
+ * 收藏文章
+ * @param id 文章ID
+ */
+export function favoriteArticle(id) {
+  return http.request({
+    url: `uniapp/knowledge/favorite/${id}`,
+    method: Method.POST,
+    needToken: true,
+  });
+}
+
+/**
+ * 取消收藏文章
+ * @param id 文章ID
+ */
+export function unfavoriteArticle(id) {
+  return http.request({
+    url: `uniapp/knowledge/favorite/${id}`,
+    method: Method.DELETE,
+    needToken: true,
+  });
+}

+ 6 - 5
config/api.js

@@ -4,16 +4,17 @@
  */
 // 开发环境
 const dev = {
-  im: "https://im-api.pickmall.cn",
+  // im: "https://im-api.pickmall.cn",
+  dify: "http://localhost:9203",
   common: "https://common-api.pickmall.cn",
-  buyer: "http://localhost:9203",
+  buyer: "http://localhost:8080",
   // common: "http://192.168.0.113:8890",
   // buyer: "http://192.168.0.113:8888",
   // im: "http://192.168.0.113:8885",
 };
 // 生产环境
 const prod = {
-  im: "https://im-api.pickmall.cn",
+  im: "http://121.4.16.100",
   common: "https://common-api.pickmall.cn",
   buyer: "http://localhost:8080",
 };
@@ -28,12 +29,12 @@ if (process.env.NODE_ENV == "development") {
 }
 //微信小程序,app的打包方式建议为生产环境,所以这块直接条件编译赋值
 // #ifdef MP-WEIXIN || APP-PLUS
-api = prod;
+// api = prod;
 // #endif
 
 // api.buyer += "/buyer";
 api.common += "/common";
-api.im += "/im";
+// api.im += "/im";
 export default {
   ...api,
 };

+ 29 - 0
package-lock.json

@@ -0,0 +1,29 @@
+{
+  "name": "nongxiaoyu_back",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "dependencies": {
+        "event-source-polyfill": "^1.0.31",
+        "uview-ui": "^2.0.36"
+      }
+    },
+    "node_modules/.pnpm/uview-ui@2.0.38": {
+      "name": "uview-ui",
+      "version": "2.0.38",
+      "engines": {
+        "HBuilderX": "^3.1.0"
+      }
+    },
+    "node_modules/event-source-polyfill": {
+      "version": "1.0.31",
+      "resolved": "https://registry.npmmirror.com/event-source-polyfill/-/event-source-polyfill-1.0.31.tgz",
+      "integrity": "sha512-4IJSItgS/41IxN5UVAVuAyczwZF7ZIEsM1XAoUzIHA6A+xzusEZUutdXz2Nr+MQPLxfTiCvqE79/C8HT8fKFvA=="
+    },
+    "node_modules/uview-ui": {
+      "resolved": "node_modules/.pnpm/uview-ui@2.0.38",
+      "link": true
+    }
+  }
+}

+ 2 - 1
package.json

@@ -1,5 +1,6 @@
 {
   "dependencies": {
-    "uview-ui": "^3.2.0"
+    "event-source-polyfill": "^1.0.31",
+    "uview-ui": "^2.0.36"
   }
 }

+ 27 - 0
pages.json

@@ -38,6 +38,33 @@
         "navigationBarTitleText": "农业知识"
       }
     },
+	{
+	  "path": "pages/knowledge/detail",
+	  "style": {
+	    "navigationBarTitleText": "农业知识",
+	    "h5": {
+	      "navigationStyle": "custom"
+	    },
+	    "app-plus": {
+	      "titleNView": {
+	        "buttons": [{
+	          "type": "back",
+	          "background": "transparent"
+	        }]
+	      }
+	    },
+	    "mp-weixin": {
+	      "navigationBarBackgroundColor": "#ffffff",
+	      "navigationBarTextStyle": "black"
+	    }
+	  }
+	},
+	{
+	  "path": "pages/knowledge/ai-chat/index",
+	  "style": {
+	    "navigationBarTitleText": "问农小禹"
+	  }
+	},
     {
       "path": "pages/user/index",
       "style": {

+ 165 - 0
pages/chart/index.vue

@@ -0,0 +1,165 @@
+<template>
+  <view class="chat-page">
+    <scroll-view scroll-y="true" class="chat-container" :scroll-into-view="scrollIntoView" scroll-with-animation>
+      <view v-for="(msg, index) in messages" :key="index" :id="'msg-' + index" class="message" :class="msg.role">
+        <text>{{ msg.content }}</text>
+      </view>
+    </scroll-view>
+
+    <view class="input-bar">
+      <input v-model="inputText" :disabled="loading" class="chat-input" placeholder="请输入你的问题..." @confirm="sendMessage" />
+      <button :disabled="loading || !inputText.trim()" @click="sendMessage" class="send-button">发送</button>
+    </view>
+  </view>
+</template>
+
+<script>
+	import {
+		chartStream
+	} from '@/api/services/chart.js';
+export default {
+  data() {
+    return {
+      inputText: '',
+      messages: [],
+      loading: false,
+      scrollIntoView: ''
+    };
+  },
+  methods: {
+    sendMessage() {
+      const content = this.inputText.trim();
+      if (!content || this.loading) return;
+
+      this.messages.push({ role: 'user', content });
+      this.inputText = '';
+      this.scrollToBottom();
+
+      const aiMsg = { role: 'ai', content: '' };
+      this.messages.push(aiMsg);
+      this.loading = true;
+      this.scrollToBottom();
+
+      const requestBody = {
+        query: content,
+        user: 'test_user_123' // 实际项目中应为真实用户ID
+      };
+
+      uni.request({
+        url: 'http://localhost:9203/dify/chat/stream', // ⚠️ 换成你自己的后端
+        method: 'POST',
+        header: {
+          'Content-Type': 'application/json'
+        },
+        data: requestBody,
+        success: (res) => {
+          const reader = res.data.getReader?.();
+          const decoder = new TextDecoder();
+          let buffer = '';
+
+          const readStream = () => {
+            reader.read().then(({ done, value }) => {
+              if (done) {
+                this.loading = false;
+                return;
+              }
+
+              buffer += decoder.decode(value, { stream: true });
+              const events = buffer.split('\n\n');
+              buffer = events.pop(); // 保留未完成部分
+
+              events.forEach(evt => {
+                if (!evt.trim()) return;
+                const lines = evt.split('\n');
+                let eventData = '';
+
+                lines.forEach(line => {
+                  if (line.startsWith('data:')) {
+                    eventData = line.replace(/^data:\s*/, '');
+                  }
+                });
+
+                try {
+                  const json = JSON.parse(eventData);
+                  if (json.event === 'message') {
+                    aiMsg.content += json.answer || '';
+                    this.scrollToBottom();
+                  } else if (json.event === 'message_end') {
+                    this.loading = false;
+                  }
+                } catch (e) {
+                  console.warn('解析流数据失败:', e);
+                }
+              });
+
+              readStream();
+            });
+          };
+
+          readStream();
+        },
+        fail: (err) => {
+          this.loading = false;
+          aiMsg.content = '[请求失败: ' + err.errMsg + ']';
+        }
+      });
+    },
+    scrollToBottom() {
+      this.$nextTick(() => {
+        this.scrollIntoView = 'msg-' + (this.messages.length - 1);
+      });
+    }
+  }
+};
+</script>
+
+<style scoped>
+.chat-page {
+  display: flex;
+  flex-direction: column;
+  height: 100vh;
+  background-color: #f3f3f3;
+}
+.chat-container {
+  flex: 1;
+  padding: 20rpx;
+  overflow-y: auto;
+  background-color: #f3f3f3;
+}
+.message {
+  margin-bottom: 20rpx;
+  padding: 20rpx;
+  border-radius: 20rpx;
+  max-width: 80%;
+  word-break: break-word;
+}
+.message.user {
+  align-self: flex-end;
+  background-color: #dfffd6;
+}
+.message.ai {
+  align-self: flex-start;
+  background-color: #ffffff;
+}
+.input-bar {
+  display: flex;
+  align-items: center;
+  padding: 10rpx;
+  border-top: 1px solid #ddd;
+  background-color: #fff;
+}
+.chat-input {
+  flex: 1;
+  padding: 10rpx;
+  border: 1px solid #ccc;
+  border-radius: 10rpx;
+}
+.send-button {
+  margin-left: 10rpx;
+  background-color: #4caf50;
+  color: white;
+  border: none;
+  border-radius: 10rpx;
+  padding: 10rpx 20rpx;
+}
+</style>

+ 812 - 0
pages/knowledge/ai-chat/index.vue

@@ -0,0 +1,812 @@
+<template>
+	<view class="container">
+		<!-- 聊天记录区域 -->
+		<scroll-view class="chat-container" scroll-y :scroll-top="scrollTop" :scroll-with-animation="true"
+			@scroll="onScroll" :style="{ 
+        height: `calc(100vh - ${inputHeight}px)`,
+        marginTop: '0'
+      }">
+			<view class="chat-list">
+				<view v-for="(message, index) in chatMessages" :key="index" class="message-item"
+					:class="{ 'message-ai': message.sender === 'ai', 'message-user': message.sender === 'user' }">
+					<!-- AI消息 -->
+					<template v-if="message.sender === 'ai'">
+						<view class="avatar-container">
+							<image class="avatar" src="/static/icons/ai.png" mode="aspectFill"></image>
+						</view>
+						<view class="message-content">
+							<view class="message-bubble ai-bubble"
+								:class="{'typing': message.isTyping, 'welcome': message.isWelcome || index === 0}">
+								<view v-if="message.isTyping" class="typing-indicator">
+									<view class="typing-dot"></view>
+									<view class="typing-dot"></view>
+									<view class="typing-dot"></view>
+								</view>
+								<text v-else class="message-text"
+									:class="{'highlight': containsKeywords(message.content)}">
+									{{ message.content }}
+								</text>
+							</view>
+							<text class="message-time">{{ message.time }}</text>
+						</view>
+					</template>
+
+					<!-- 用户消息 -->
+					<template v-else>
+						<view class="message-content user-content">
+							<text class="message-time">{{ message.time }}</text>
+							<view class="message-bubble user-bubble">
+								<text class="message-text">{{ message.content }}</text>
+							</view>
+						</view>
+						<view class="avatar-container">
+							<image class="avatar" src="/static/images/user-avatar.svg" mode="aspectFill"></image>
+						</view>
+					</template>
+				</view>
+			</view>
+		</scroll-view>
+
+		<!-- 底部输入区 -->
+		<view class="input-container" :style="{ paddingBottom: `${isIOS ? safeAreaBottom : 20}rpx` }">
+			<!-- 问题建议区 -->
+			<scroll-view v-if="chatMessages.length <= 3 && !inputMessage" class="suggested-questions" scroll-x>
+				<view v-for="(question, index) in suggestedQuestions" :key="index" class="question-chip"
+					@click="useQuestion(question)">
+					<text>{{ question }}</text>
+				</view>
+			</scroll-view>
+
+			<view class="input-wrapper">
+				<textarea class="message-input" v-model="inputMessage" placeholder="请输入您的问题..." :disabled="isProcessing"
+					auto-height :maxlength="300" :style="{ maxHeight: '120rpx' }" @focus="onInputFocus"
+					@confirm="submitQuestion" />
+				<view class="send-button" :class="{ 'disabled': !inputMessage.trim() || isProcessing }"
+					@click="submitQuestion" hover-class="button-hover">
+					<image class="send-icon-image"
+						:src="inputMessage.trim() && !isProcessing ? '/static/icons/chat.png' : '/static/icons/chat_off.png'"
+						mode="aspectFit"></image>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import api from "@/config/api.js";
+	import storage from "@/utils/storage.js";
+	export default {
+		data() {
+			return {
+				inputMessage: '', // 输入框消息
+				chatMessages: [{
+					sender: 'ai',
+					content: '您好!我是农小禹,您的智能农业助手🌱 我可以帮您解答农业种植、病虫害防治、农产品管理等方面的问题。有什么可以帮助您的吗?',
+					time: this.getFormattedTime(new Date()),
+					timestamp: Date.now(),
+					isWelcome: true
+				}],
+				scrollTop: 0,
+				inputHeight: 110,
+				isProcessing: false,
+				requestTask: null,
+				currentTypingMessage: null, // 当前正在输入的消息索引
+				suggestedQuestions: [
+					'水稻插秧后如何管理?',
+					'果树夏季修剪技巧?',
+					'如何防治蔬菜常见病虫害?',
+					'农药使用注意事项?',
+					'有机肥和化肥怎么搭配使用?'
+				],
+				statusBarHeight: 20,
+				safeAreaBottom: 34,
+				isIOS: false,
+				typingInterval: 100, // 打字速度配置
+				typingTimers: [] // 用于存储定时器
+			}
+		},
+		// 设置页面标题
+		onNavigationBarButtonTap(e) {
+			console.log("导航栏按钮点击:", e);
+		},
+		created() {
+			this.debouncedSubmitQuestion = this.debounce(this.submitQuestion, 300)
+		},
+		mounted() {
+			// 获取系统信息
+			const systemInfo = uni.getSystemInfoSync();
+			this.statusBarHeight = systemInfo.statusBarHeight || 20;
+			this.isIOS = systemInfo.platform === 'ios';
+			this.safeAreaBottom = systemInfo.safeAreaInsets ? (systemInfo.safeAreaInsets.bottom || 0) : 0;
+
+			// 初始化消息
+			this.initMessages();
+
+			// 滚动到底部
+			this.$nextTick(() => {
+				this.scrollToBottom();
+			});
+		},
+		methods: {
+			// 处理消息发送的方法
+			makeStreamRequest(message) {
+				if (this.requestTask) {
+					this.requestTask.abort();
+				}
+
+				const url = api.dify + '/dify/chat/stream';
+
+				// 添加一个临时的"正在输入"消息
+				const typingMessageIndex = this.chatMessages.push({
+					sender: 'ai',
+					content: '',
+					time: this.getCurrentTime(),
+					isTyping: true
+				}) - 1;
+
+				this.currentTypingMessage = typingMessageIndex;
+
+				this.requestTask = uni.request({
+					url: url,
+					method: 'POST',
+					data: message,
+					responseType: 'text',
+					header: {
+						'content-type': 'application/json'
+					},
+					success: (res) => {
+						if (res.data) {
+							const lines = res.data.split('\n');
+							this.processSSELines(lines, typingMessageIndex);
+						}
+					},
+					fail: (err) => {
+						console.error('请求失败:', err);
+						this.handleError(err);
+						// 移除"正在输入"消息
+						if (this.currentTypingMessage !== null) {
+							this.chatMessages.splice(this.currentTypingMessage, 1);
+						}
+					},
+					complete: () => {
+						this.requestTask = null;
+						this.isProcessing = false;
+						this.scrollToBottom();
+					}
+				});
+			},
+
+			// 处理 SSE 数据行
+			processSSELines(lines, typingMessageIndex) {
+				let accumulatedText = this.chatMessages[typingMessageIndex]?.content || '';
+
+				lines.forEach(line => {
+					if (!line.trim()) return;
+
+					if (line.startsWith('data:')) {
+						try {
+							const jsonData = JSON.parse(line.slice(5).trim());
+							if (jsonData.eventType === 'AGENT_MESSAGE' && jsonData.answer) {
+								accumulatedText += jsonData.answer;
+								// 使用新的打字机效果更新消息内容
+								this.typewriterEffect(accumulatedText, (text) => {
+									this.$set(this.chatMessages[typingMessageIndex], 'content', text);
+									// 滚动到底部,确保用户看到最新内容
+									this.scrollToBottom();
+								});
+							} else if (jsonData.eventType === 'MESSAGE_END') {
+								// 消息结束时停止打字效果
+								this.currentTypingMessage = null;
+								this.$set(this.chatMessages[typingMessageIndex], 'isTyping', false);
+								// 确保完整内容显示
+							}
+						} catch (e) {
+							console.error('解析数据出错:', e);
+						}
+					}
+				});
+			},
+
+			// 实现打字机效果的方法
+			typewriterEffect(text, callback) {
+				let index = 0;
+				const length = text.length;
+				const interval = 50; // 打字间隔时间
+
+				// 清除之前的打字机效果定时器
+				this.typingTimers.forEach(timer => clearTimeout(timer));
+				this.typingTimers = [];
+
+				const type = () => {
+					if (index <= length) {
+						callback(text.slice(0, index));
+						index++;
+						const timer = setTimeout(type, interval);
+						this.typingTimers.push(timer);
+					}
+				};
+
+				type();
+			},
+
+			// 发送消息
+			submitQuestion() {
+				if (!storage.getHasLogin()) {
+					// 使用 uni.showModal 显示登录提示框
+					uni.showModal({
+						title: '提示',
+						content: '您还未登录,请先登录',
+						confirmText: '去登录',
+						cancelText: '取消',
+						success: function(res) {
+							if (res.confirm) {
+								// 点击确定按钮,跳转到登录页面
+								uni.navigateTo({
+									url: '/pages/login/index'
+								});
+							}
+						},
+					});
+					return;
+				}
+				if (!this.inputMessage.trim() || this.isProcessing) return;
+
+				this.isProcessing = true;
+
+				// 添加用户消息
+				this.chatMessages.push({
+					sender: 'user',
+					content: this.inputMessage,
+					time: this.getCurrentTime()
+				});
+
+				const message = {
+					query: this.inputMessage,
+					user: 'user_' + Date.now()
+				};
+
+				this.inputMessage = '';
+				this.scrollToBottom();
+				this.makeStreamRequest(message);
+			},
+
+			// 添加错误处理方法
+			handleError(error) {
+				let errorMessage = '发生错误'
+				if (error.errMsg) {
+					errorMessage = error.errMsg
+				} else if (error.message) {
+					errorMessage = error.message
+				}
+
+				uni.showToast({
+					title: errorMessage,
+					icon: 'none',
+					duration: 2000
+				})
+			},
+
+			// 添加防抖函数
+			debounce(func, wait) {
+				let timeout
+				return (...args) => {
+					clearTimeout(timeout)
+					timeout = setTimeout(() => {
+						func.apply(this, args)
+					}, wait)
+				}
+			},
+			onInputFocus() {
+				// 输入框获取焦点时,确保滚动到底部
+				this.$nextTick(() => {
+					this.scrollToBottom();
+				});
+			},
+			// 滚动到底部
+			scrollToBottom() {
+				this.$nextTick(() => {
+					const query = uni.createSelectorQuery().in(this)
+					query.select('.chat-list').boundingClientRect(data => {
+						if (data) {
+							this.scrollTop = data.height + 1000
+						}
+					}).exec()
+				})
+			},
+
+			// 错误处理
+			handleError(error) {
+				let errorMessage = '发生错误'
+				if (error.errMsg) {
+					errorMessage = error.errMsg
+				} else if (error.message) {
+					errorMessage = error.message
+				}
+
+				uni.showToast({
+					title: errorMessage,
+					icon: 'none',
+					duration: 2000
+				})
+			},
+			onScroll(e) {
+				// 可以添加滚动事件处理
+			},
+			getCurrentTime() {
+				return this.getFormattedTime(new Date());
+			},
+			getFormattedTime(date) {
+				const hours = date.getHours().toString().padStart(2, '0');
+				const minutes = date.getMinutes().toString().padStart(2, '0');
+				return `${hours}:${minutes}`;
+			},
+			/*    submitQuestion() {
+			      if (!this.inputMessage.trim() || this.isProcessing) return;
+			      
+			      this.isProcessing = true;
+			      
+			      // 添加用户消息
+			      this.chatMessages.push({
+			        sender: 'user',
+			        content: this.inputMessage,
+			        time: this.getCurrentTime()
+			      });
+			      
+			      const userQuestion = this.inputMessage;
+			      this.inputMessage = '';
+			      
+			      // 滚动到底部
+			      this.scrollToBottom();
+			      
+			      // 先添加一个"正在输入"的消息
+			      this.chatMessages.push({
+			        sender: 'ai',
+			        content: '正在思考...',
+			        time: this.getCurrentTime(),
+			        isTyping: true
+			      });
+			      
+			      // 模拟AI回复(实际项目中替换为API调用)
+			      setTimeout(() => {
+			        // 删除"正在输入"消息
+			        this.chatMessages.pop();
+			        
+			        // 模拟回复
+			        let aiResponse = "感谢您的提问!作为您的农技助理,我很高兴能帮助您解决农业相关问题。您询问的内容我已收到,我们团队正在研究适合的解决方案。";
+			        
+			        if (userQuestion.includes('水稻')) {
+			          aiResponse = "水稻种植需要注意水肥管理和病虫害防治。目前南方地区水稻插秧时间已到,建议您选择抗病性强的品种,如'中优84'。插秧后7天开始浅水促根,分蘖期保持3-5cm水层。";
+			        } else if (userQuestion.includes('蔬菜')) {
+			          aiResponse = "夏季蔬菜种植需注意遮阳和水分管理。建议种植耐热品种如空心菜、苋菜、茄子等。可以采用遮阳网减少强光照射,早晚浇水避开高温时段。";
+			        } else if (userQuestion.includes('果树')) {
+			          aiResponse = "果树现在应注意夏季修剪和病虫害防治。柑橘类果树可以进行夏季修剪,去除徒长枝和内膛枝。同时注意柑橘黄龙病和炭疽病的预防,建议定期喷施药剂保护。";
+			        }
+			        
+			        this.chatMessages.push({
+			          sender: 'ai',
+			          content: aiResponse,
+			          time: this.getCurrentTime()
+			        });
+			        
+			        this.scrollToBottom();
+			        this.isProcessing = false;
+			      }, 2000);
+			    }, */
+			useQuestion(question) {
+				this.inputMessage = question;
+			},
+			containsKeywords(text) {
+				const keywords = ['水稻', '小麦', '玉米', '病虫害', '农药', '化肥', '有机肥', '种植技术'];
+				return keywords.some(keyword => text.includes(keyword));
+			},
+			formatMessage(text) {
+				// 将文本中的换行符转换为<br>标签
+				return text.replace(/\n/g, '<br>');
+			},
+			initMessages() {
+				// 确保消息有时间戳
+				this.chatMessages.forEach(msg => {
+					if (!msg.timestamp) {
+						msg.timestamp = new Date().getTime();
+					}
+				});
+
+				// 按时间排序
+				this.chatMessages.sort((a, b) => a.timestamp - b.timestamp);
+			},
+			showDateSeparator(index) {
+				// 判断是否需要显示日期分割线
+				if (index === 0) return true;
+
+				const currentMsg = this.chatMessages[index];
+				const prevMsg = this.chatMessages[index - 1];
+
+				// 如果两条消息相隔超过30分钟,或者是不同日期,显示日期分割线
+				return this.isDifferentDay(currentMsg.timestamp, prevMsg.timestamp) ||
+					(currentMsg.timestamp - prevMsg.timestamp > 30 * 60 * 1000);
+			},
+			isDifferentDay(timestamp1, timestamp2) {
+				const date1 = new Date(timestamp1);
+				const date2 = new Date(timestamp2);
+
+				return date1.getDate() !== date2.getDate() ||
+					date1.getMonth() !== date2.getMonth() ||
+					date1.getFullYear() !== date2.getFullYear();
+			},
+			formatDateSeparator(timestamp) {
+				const now = new Date();
+				const msgDate = new Date(timestamp);
+
+				// 今天
+				if (this.isSameDay(msgDate, now)) {
+					return '今天 ' + this.getFormattedTime(msgDate);
+				}
+
+				// 昨天
+				const yesterday = new Date(now);
+				yesterday.setDate(now.getDate() - 1);
+				if (this.isSameDay(msgDate, yesterday)) {
+					return '昨天 ' + this.getFormattedTime(msgDate);
+				}
+
+				// 一周内
+				const oneWeekAgo = new Date(now);
+				oneWeekAgo.setDate(now.getDate() - 7);
+				if (msgDate >= oneWeekAgo) {
+					const weekdays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
+					return weekdays[msgDate.getDay()] + ' ' + this.getFormattedTime(msgDate);
+				}
+
+				// 其他日期
+				return msgDate.getFullYear() + '年' + (msgDate.getMonth() + 1) + '月' + msgDate.getDate() + '日 ' +
+					this
+					.getFormattedTime(msgDate);
+			},
+			isSameDay(date1, date2) {
+				return date1.getDate() === date2.getDate() &&
+					date1.getMonth() === date2.getMonth() &&
+					date1.getFullYear() === date2.getFullYear();
+			}
+		},
+		// 组件销毁时清理资源
+		beforeDestroy() {
+			// 清理所有打字机效果的定时器
+			this.typingTimers.forEach(timer => clearTimeout(timer));
+			// 清理请求
+			if (this.requestTask) {
+				this.requestTask.abort();
+				this.requestTask = null;
+			}
+		}
+	}
+</script>
+
+<style>
+	/* 容器样式 */
+	.container {
+		position: relative;
+		min-height: 100vh;
+		background-color: #f5f5f5;
+		overflow: hidden;
+		/* 防止内容溢出 */
+	}
+
+	/* 聊天容器 */
+	.chat-container {
+		padding: 20rpx 30rpx;
+		box-sizing: border-box;
+		background-color: #f8f8f8;
+		background-image: url('/static/images/chat-bg-pattern.png');
+		background-size: 300rpx;
+		background-blend-mode: overlay;
+		background-opacity: 0.05;
+		-webkit-overflow-scrolling: touch;
+		/* 增强iOS滚动体验 */
+	}
+
+	.chat-list {
+		padding-bottom: 30rpx;
+	}
+
+	/* 消息项 */
+	.message-item {
+		display: flex;
+		margin-bottom: 30rpx;
+		position: relative;
+	}
+
+	.message-ai {
+		justify-content: flex-start;
+	}
+
+	.message-user {
+		justify-content: flex-end;
+	}
+
+	/* 头像 */
+	.avatar-container {
+		width: 90rpx;
+		height: 90rpx;
+		flex-shrink: 0;
+	}
+
+	.avatar {
+		width: 90rpx;
+		height: 90rpx;
+		border-radius: 50%;
+		background-color: #e0e0e0;
+		box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
+		object-fit: cover;
+	}
+
+	/* 消息内容 */
+	.message-content {
+		max-width: 70%;
+		margin: 0 20rpx;
+		display: flex;
+		flex-direction: column;
+	}
+
+	.user-content {
+		align-items: flex-end;
+	}
+
+	.message-bubble {
+		padding: 24rpx;
+		border-radius: 24rpx;
+		position: relative;
+		margin-bottom: 10rpx;
+		word-wrap: break-word;
+		min-width: 80rpx;
+		box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
+		transition: all 0.3s ease;
+		max-width: 100%;
+	}
+
+	.ai-bubble {
+		background-color: #e8f5e9;
+		border-top-left-radius: 4rpx;
+	}
+
+	.user-bubble {
+		background-color: #e3f2fd;
+		border-top-right-radius: 4rpx;
+	}
+
+	.message-text {
+		font-size: 28rpx;
+		color: #333;
+		line-height: 1.5;
+		word-break: break-all;
+	}
+
+	.message-time {
+		font-size: 22rpx;
+		color: #999;
+	}
+
+	/* 输入区域 */
+	.input-container {
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		right: 0;
+		background-color: #fff;
+		padding: 20rpx 30rpx;
+		box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.1);
+		display: flex;
+		flex-direction: column;
+		z-index: 10;
+	}
+
+	.input-wrapper {
+		display: flex;
+		align-items: flex-end;
+	}
+
+	.message-input {
+		flex: 1;
+		min-height: 70rpx;
+		max-height: 120rpx;
+		border-radius: 35rpx;
+		background-color: #f5f5f5;
+		padding: 15rpx 30rpx;
+		font-size: 28rpx;
+		color: #333;
+		border: 1rpx solid #e0e0e0;
+		line-height: 1.4;
+	}
+
+	.send-button {
+		margin-left: 16rpx;
+		width: 76rpx;
+		height: 76rpx;
+		border-radius: 50%;
+		background-color: transparent;
+		background-image: none;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		transition: all 0.2s ease;
+		position: relative;
+		align-self: center;
+	}
+
+	.send-button.disabled {
+		background-color: transparent;
+		background-image: none;
+		opacity: 1;
+	}
+
+	.button-hover {
+		transform: scale(0.95);
+	}
+
+	@keyframes pulse {
+		0% {
+			transform: scale(1);
+		}
+
+		50% {
+			transform: scale(0.95);
+		}
+
+		100% {
+			transform: scale(1);
+		}
+	}
+
+	.send-button:active:not(.disabled) {
+		animation: pulse 0.3s ease-in-out;
+	}
+
+	/* 删除或注释掉之前的样式 */
+	.send-icon {
+		display: none;
+	}
+
+	.send-icon:before {
+		display: none;
+	}
+
+	.send-icon-text {
+		display: none;
+	}
+
+	/* 推荐问题区域 */
+	.suggested-questions {
+		display: flex;
+		white-space: nowrap;
+		margin-bottom: 15rpx;
+		padding: 5rpx 0;
+	}
+
+	.question-chip {
+		display: inline-block;
+		padding: 12rpx 20rpx;
+		margin-right: 15rpx;
+		background-color: #e8f5e9;
+		color: #4CAF50;
+		font-size: 24rpx;
+		border-radius: 30rpx;
+		border: 1rpx solid #a5d6a7;
+	}
+
+	/* AI正在输入的样式 */
+	.message-bubble.ai-bubble.typing {
+		background-color: #f0f0f0;
+	}
+
+	.typing-indicator {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		height: 40rpx;
+		padding: 0 20rpx;
+	}
+
+	.typing-dot {
+		width: 10rpx;
+		height: 10rpx;
+		margin: 0 5rpx;
+		background-color: #4CAF50;
+		border-radius: 50%;
+		opacity: 0.5;
+		animation: typingAnimation 1.4s infinite both;
+	}
+
+	.typing-dot:nth-child(2) {
+		animation-delay: 0.2s;
+	}
+
+	.typing-dot:nth-child(3) {
+		animation-delay: 0.4s;
+	}
+
+	@keyframes typingAnimation {
+		0% {
+			opacity: 0.3;
+			transform: translateY(0);
+		}
+
+		50% {
+			opacity: 1;
+			transform: translateY(-5rpx);
+		}
+
+		100% {
+			opacity: 0.3;
+			transform: translateY(0);
+		}
+	}
+
+	/* 关键词高亮 */
+	.message-text.highlight {
+		color: #2E7D32;
+		font-weight: 500;
+	}
+
+	/* 欢迎消息特殊样式 */
+	.welcome {
+		background-color: #e3f2fd !important;
+		border-left: none !important;
+		border-radius: 24rpx !important;
+	}
+
+	/* 日期分割线样式 */
+	.date-separator {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		margin: 20rpx 0;
+	}
+
+	.date-separator text {
+		background-color: rgba(0, 0, 0, 0.1);
+		color: #666;
+		font-size: 24rpx;
+		padding: 4rpx 20rpx;
+		border-radius: 20rpx;
+	}
+
+	/* 纸飞机图标 */
+	.plane-svg {
+		display: none;
+	}
+
+	.send-icon-image {
+		width: 76rpx;
+		height: 76rpx;
+	}
+
+	.material-icon {
+		font-family: 'Material Icons';
+		font-weight: normal;
+		font-style: normal;
+		font-size: 48rpx;
+		line-height: 1;
+		letter-spacing: normal;
+		text-transform: none;
+		display: inline-block;
+		white-space: nowrap;
+		word-wrap: normal;
+		direction: ltr;
+		-webkit-font-smoothing: antialiased;
+		color: white;
+	}
+
+	/* 删除不需要的导航栏样式 */
+	.custom-navbar {
+		display: none;
+	}
+
+	.navbar-bg,
+	.navbar-content,
+	.navbar-left,
+	.navbar-title,
+	.navbar-right,
+	.back-icon,
+	.arrow-left {
+		display: none;
+	}
+</style>

+ 1097 - 0
pages/knowledge/article-editor.vue

@@ -0,0 +1,1097 @@
+<template>
+  <view class="container">
+    <!-- H5环境自定义导航栏 -->
+    <view class="h5-custom-navbar" v-if="isH5">
+      <view class="h5-navbar-left" @click="goBack">
+        <view class="h5-back-icon">
+          <view class="h5-arrow-left"></view>
+        </view>
+      </view>
+      <text class="h5-navbar-title">编辑文章</text>
+      <view class="h5-navbar-right"></view>
+    </view>
+    
+    <!-- 表单内容区 -->
+    <view class="article-container" :style="isH5 ? 'margin-top: 90rpx;' : ''">
+      <!-- 加载状态 -->
+      <view class="loading-container" v-if="loading">
+        <view class="loading-spinner"></view>
+        <text class="loading-text">加载中...</text>
+      </view>
+      
+      <!-- 编辑区域 -->
+      <block v-else>
+        <!-- 基本信息区域 -->
+        <view class="form-section">
+          <view class="form-title">基本信息</view>
+          <view class="form-item">
+            <text class="form-label">文章标题</text>
+            <input class="form-input" v-model="articleInfo.title" placeholder="请输入文章标题" />
+          </view>
+          <view class="form-item">
+            <text class="form-label">文章来源</text>
+            <input class="form-input" v-model="articleInfo.source" placeholder="请输入文章来源" />
+          </view>
+          <view class="form-item">
+            <text class="form-label">文章类型</text>
+            <radio-group class="type-group" @change="handleTypeChange">
+              <label class="type-item">
+                <radio value="tech" :checked="articleInfo.category === 'tech'" />
+                <text>农技知识</text>
+              </label>
+              <label class="type-item">
+                <radio value="policy" :checked="articleInfo.category === 'policy'" />
+                <text>政策解读</text>
+              </label>
+            </radio-group>
+          </view>
+          <view class="form-item">
+            <text class="form-label">内容类型</text>
+            <radio-group class="type-group" @change="handleContentTypeChange">
+              <label class="type-item">
+                <radio value="article" :checked="articleInfo.contentType === 'article'" />
+                <text>文章</text>
+              </label>
+              <label class="type-item">
+                <radio value="video" :checked="articleInfo.contentType === 'video'" />
+                <text>视频</text>
+              </label>
+            </radio-group>
+          </view>
+          <view class="form-item" v-if="articleInfo.contentType === 'video'">
+            <text class="form-label">视频链接</text>
+            <input class="form-input" v-model="articleInfo.videoUrl" placeholder="请输入视频链接" />
+          </view>
+          <view class="form-item" v-if="articleInfo.contentType === 'video'">
+            <text class="form-label">视频时长</text>
+            <input class="form-input" v-model="articleInfo.videoDuration" placeholder="请输入视频时长,如12:30" />
+          </view>
+          <view class="form-item">
+            <text class="form-label">文章简介</text>
+            <textarea class="form-textarea" v-model="articleInfo.description" placeholder="请输入文章简介" />
+          </view>
+          <!-- <view class="form-item">
+            <text class="form-label">缩略图</text>
+            <view class="upload-box" @click="chooseThumbnail">
+              <image v-if="articleInfo.thumbnail" :src="articleInfo.thumbnail" mode="aspectFill" class="thumbnail-preview"></image>
+              <view v-else class="upload-placeholder">
+                <text class="upload-icon">+</text>
+                <text>上传缩略图</text>
+              </view>
+            </view>
+          </view> -->
+        </view>
+        
+        <!-- 文章内容编辑区 -->
+        <view class="form-section">
+          <view class="form-title">文章内容</view>
+          
+          <!-- 文章内容构建器 -->
+          <view class="content-builder">
+            <view class="section-controls">
+              <button class="control-btn" @click="addSection('paragraph')">添加段落</button>
+              <button class="control-btn" @click="addSection('title')">添加标题</button>
+              <button class="control-btn" @click="addSection('image')">添加图片</button>
+              <button class="control-btn" @click="addSection('note')">添加提示框</button>
+            </view>
+            
+            <!-- 内容块 -->
+            <view class="content-sections">
+              <view v-for="(section, index) in contentSections" :key="index" class="content-section-item">
+                <!-- 段落 -->
+                <view v-if="section.type === 'paragraph'" class="section-edit-item">
+                  <textarea class="section-textarea" v-model="section.content" placeholder="请输入段落内容"></textarea>
+                  <view class="section-controls">
+                    <text class="delete-btn" @click="deleteSection(index)">删除</text>
+                    <text class="move-btn" @click="moveSection(index, -1)" v-if="index > 0">上移</text>
+                    <text class="move-btn" @click="moveSection(index, 1)" v-if="index < contentSections.length - 1">下移</text>
+                  </view>
+                </view>
+                
+                <!-- 标题 -->
+                <view v-if="section.type === 'title'" class="section-edit-item">
+                  <input class="section-title-input" v-model="section.content" placeholder="请输入标题"></input>
+                  <view class="section-controls">
+                    <text class="delete-btn" @click="deleteSection(index)">删除</text>
+                    <text class="move-btn" @click="moveSection(index, -1)" v-if="index > 0">上移</text>
+                    <text class="move-btn" @click="moveSection(index, 1)" v-if="index < contentSections.length - 1">下移</text>
+                  </view>
+                </view>
+                
+                <!-- 图片 -->
+                <view v-if="section.type === 'image'" class="section-edit-item">
+                  <view class="image-upload-container">
+                    <view class="upload-box" @click="chooseImage(index)">
+                      <image v-if="section.imageUrl" :src="section.imageUrl" mode="aspectFill" class="section-image-preview"></image>
+                      <view v-else class="upload-placeholder">
+                        <text class="upload-icon">+</text>
+                        <text>上传图片</text>
+                      </view>
+                    </view>
+                    <input class="image-caption-input" v-model="section.caption" placeholder="图片说明文字"></input>
+                    <input class="color-input" v-model="section.color" placeholder="颜色代码,例如: #8BC34A"></input>
+                  </view>
+                  <view class="section-controls">
+                    <text class="delete-btn" @click="deleteSection(index)">删除</text>
+                    <text class="move-btn" @click="moveSection(index, -1)" v-if="index > 0">上移</text>
+                    <text class="move-btn" @click="moveSection(index, 1)" v-if="index < contentSections.length - 1">下移</text>
+                  </view>
+                </view>
+                
+                <!-- 提示框 -->
+                <view v-if="section.type === 'note'" class="section-edit-item">
+                  <view class="note-box-edit">
+                    <input class="note-title-input" v-model="section.title" placeholder="提示框标题,例如:注意事项"></input>
+                    <textarea class="note-content-input" v-model="section.content" placeholder="提示框内容"></textarea>
+                  </view>
+                  <view class="section-controls">
+                    <text class="delete-btn" @click="deleteSection(index)">删除</text>
+                    <text class="move-btn" @click="moveSection(index, -1)" v-if="index > 0">上移</text>
+                    <text class="move-btn" @click="moveSection(index, 1)" v-if="index < contentSections.length - 1">下移</text>
+                  </view>
+                </view>
+              </view>
+            </view>
+          </view>
+        </view>
+        
+        <!-- 预览区 -->
+        <view class="preview-section">
+          <view class="form-title">内容预览</view>
+          <view class="article-content">
+            <rich-text :nodes="generatedHtml"></rich-text>
+          </view>
+        </view>
+        
+        <!-- 提交按钮 -->
+        <view class="submit-section">
+          <button class="submit-btn" @click="saveArticle">保存文章</button>
+        </view>
+      </block>
+    </view>
+    
+    <!-- 返回顶部 -->
+    <view class="back-to-top" @click="scrollToTop">
+      <view class="top-arrow"></view>
+      <text class="top-text">顶部</text>
+    </view>
+  </view>
+</template>
+
+<script>
+import { createArticle, updateArticle, getArticleDetail, uploadFile } from '@/api/services/knowledge.js';
+
+export default {
+  data() {
+    return {
+      title: "编辑文章",
+      isH5: false,
+      loading: false,
+      isEdit: false,  // 是否为编辑模式
+      id: null,  // 文章ID
+      
+      // 文章基本信息
+      articleInfo: {
+        id: '',
+        title: '',
+        description: '',
+        source: '农业技术研究院',
+        category: 'tech',  // 默认为农技知识
+        contentType: 'article',  // 默认为文章类型
+        thumbnail: '',  // 缩略图
+        videoUrl: '',  // 视频链接
+        videoDuration: '',  // 视频时长
+        isRecommend: 0,
+        status: 1
+      },
+      
+      // 内容块
+      contentSections: [],
+      
+      // 生成的HTML内容
+      generatedHtml: ''
+    }
+  },
+  
+  onLoad(options) {
+    // 检测是否在H5环境中运行
+    // #ifdef H5
+    this.isH5 = true;
+    // #endif
+    
+    // 设置导航栏标题
+    uni.setNavigationBarTitle({
+      title: '编辑文章'
+    });
+    
+    // 如果有ID参数,则为编辑模式
+    if (options && options.id) {
+      this.id = options.id;
+      this.isEdit = true;
+      this.loadArticleData();
+    }
+  },
+  
+  watch: {
+    // 监听内容区块变化,实时生成HTML
+    contentSections: {
+      handler: function() {
+        console.log("this.generateHtml()",this.generateHtml());
+        
+        this.generateHtml();
+      },
+      deep: true
+    }
+  },
+  
+  methods: {
+    // 加载文章数据(编辑模式)
+    async loadArticleData() {
+      if (!this.id) return;
+      
+      try {
+        this.loading = true;
+        const result = await getArticleDetail(this.id);
+        
+        if (result.data.code === 200 && result.data.data) {
+          const articleData = result.data.data;
+          
+          // 填充基本信息
+          this.articleInfo = {
+            id: articleData.id,
+            title: articleData.title,
+            description: articleData.description,
+            source: articleData.source,
+            category: articleData.type,
+            contentType: articleData.contentType,
+            thumbnail: articleData.image,
+            videoUrl: articleData.videoUrl,
+            videoDuration: articleData.duration,
+            isRecommend: 0,
+            status: 1
+          };
+          
+          // 解析HTML内容为内容块
+          this.parseHtmlToSections(articleData.content);
+        } else {
+          uni.showToast({
+            title: result.data.msg || '获取文章详情失败',
+            icon: 'none'
+          });
+        }
+      } catch (error) {
+        console.error('获取文章详情失败:', error);
+        uni.showToast({
+          title: '网络异常,请稍后重试',
+          icon: 'none'
+        });
+      } finally {
+        this.loading = false;
+      }
+    },
+    
+    // 解析HTML为内容块
+    parseHtmlToSections(html) {
+      if (!html) {
+        this.contentSections = [];
+        return;
+      }
+      
+      // 创建一个临时的DOM元素来解析HTML
+      const parser = new DOMParser();
+      const doc = parser.parseFromString(html, 'text/html');
+      const divs = doc.querySelectorAll('div.content-section');
+      
+      const sections = [];
+      
+      divs.forEach(div => {
+        // 处理段落
+        const paragraph = div.querySelector('p.paragraph');
+        if (paragraph) {
+          sections.push({
+            type: 'paragraph',
+            content: paragraph.textContent
+          });
+        }
+        
+        // 处理标题
+        const title = div.querySelector('h3.section-title');
+        if (title) {
+          sections.push({
+            type: 'title',
+            content: title.textContent
+          });
+        }
+        
+        // 处理图片
+        const imageDiv = div.querySelector('div.section-image');
+        if (imageDiv) {
+          const colorImage = imageDiv.querySelector('div.color-image');
+          const imageLabel = colorImage ? colorImage.querySelector('div.image-label') : null;
+          
+          sections.push({
+            type: 'image',
+            imageUrl: '', // 需要后端提供真实URL
+            caption: imageLabel ? imageLabel.textContent : '',
+            color: colorImage ? colorImage.style.backgroundColor : '#8BC34A'
+          });
+        }
+        
+        // 处理提示框
+        const noteBox = div.querySelector('div.note-box');
+        if (noteBox) {
+          const noteTitle = noteBox.querySelector('div.note-title');
+          const noteContent = noteBox.querySelector('div.note-content');
+          
+          sections.push({
+            type: 'note',
+            title: noteTitle ? noteTitle.textContent : '【注意事项】',
+            content: noteContent ? noteContent.textContent : ''
+          });
+        }
+      });
+      
+      this.contentSections = sections;
+    },
+    
+    // 类型切换处理
+    handleTypeChange(e) {
+      this.articleInfo.category = e.detail.value;
+    },
+    
+    // 内容类型切换处理
+    handleContentTypeChange(e) {
+      this.articleInfo.contentType = e.detail.value;
+    },
+    
+    // 添加内容区块
+    addSection(type) {
+      switch(type) {
+        case 'paragraph':
+          this.contentSections.push({
+            type: 'paragraph',
+            content: ''
+          });
+          break;
+        case 'title':
+          this.contentSections.push({
+            type: 'title',
+            content: ''
+          });
+          break;
+        case 'image':
+          this.contentSections.push({
+            type: 'image',
+            imageUrl: '',
+            caption: '',
+            color: '#8BC34A' // 默认颜色
+          });
+          break;
+        case 'note':
+          this.contentSections.push({
+            type: 'note',
+            title: '【注意事项】',
+            content: ''
+          });
+          break;
+      }
+    },
+    
+    // 删除区块
+    deleteSection(index) {
+      this.contentSections.splice(index, 1);
+    },
+    
+    // 移动区块
+    moveSection(index, direction) {
+      const newIndex = index + direction;
+      
+      if (newIndex >= 0 && newIndex < this.contentSections.length) {
+        const item = this.contentSections[index];
+        this.contentSections.splice(index, 1);
+        this.contentSections.splice(newIndex, 0, item);
+      }
+    },
+    
+    // 选择缩略图
+    chooseThumbnail() {
+      uni.chooseImage({
+        count: 1,
+        success: async (res) => {
+          const tempFilePath = res.tempFilePaths[0];
+		  console.log("res",res);
+		  
+          console.log("tempFilePath",tempFilePath);
+          try {
+            uni.showLoading({ title: '上传中...' });
+            // 调用上传API
+            const uploadResult = await this.uploadImage(tempFilePath);
+            
+            if (uploadResult && uploadResult.url) {
+              this.articleInfo.thumbnail = ""+ uploadResult.url;
+            } else {
+              throw new Error('上传失败');
+            }
+          } catch (error) {
+            console.error('上传图片失败:', error);
+            uni.showToast({
+              title: '上传图片失败',
+              icon: 'none'
+            });
+          } finally {
+            uni.hideLoading();
+          }
+        }
+      });
+    },
+    
+    // 选择内容图片
+    chooseImage(index) {
+      uni.chooseImage({
+        count: 1,
+        success: async (res) => {
+          const tempFilePath = res.tempFilePaths[0];
+          
+          try {
+            uni.showLoading({ title: '上传中...' });
+            // 调用上传API
+            const uploadResult = await this.uploadImage(tempFilePath);
+            
+            if (uploadResult && uploadResult.url) {
+              this.$set(this.contentSections[index], 'imageUrl', uploadResult.url);
+            } else {
+              throw new Error('上传失败');
+            }
+          } catch (error) {
+            console.error('上传图片失败:', error);
+            uni.showToast({
+              title: '上传图片失败',
+              icon: 'none'
+            });
+          } finally {
+            uni.hideLoading();
+          }
+        }
+      });
+    },
+    
+    // 上传图片
+    async uploadImage(filePath) {
+      return new Promise((resolve, reject) => {
+        uni.uploadFile({
+          url: 'http://localhost:9203/file/upload', // 替换为实际的上传API
+          filePath: filePath,
+          name: 'file',
+          success: (uploadRes) => {
+            const data = JSON.parse(uploadRes.data);
+            if (data.code === 200 && data.data) {
+              resolve(data.data);
+            } else {
+              reject(new Error(data.msg || '上传失败'));
+            }
+          },
+          fail: (error) => {
+            reject(error);
+          }
+        });
+      });
+    },
+    
+    // 生成HTML内容
+    generateHtml() {
+      let html = '';
+      
+      this.contentSections.forEach(section => {
+        switch(section.type) {
+          case 'paragraph':
+            html += `<div class="content-section">
+                      <p class="paragraph">${section.content || ''}</p>
+                     </div>`;
+            break;
+            
+          case 'title':
+            html += `<div class="content-section">
+                      <h3 class="section-title">${section.content || ''}</h3>
+                     </div>`;
+            break;
+            
+          case 'image':
+            html += `<div class="content-section">
+                      <div class="section-image">
+                        <div class="color-image" style="background-color: ${section.color || '#8BC34A'};">
+                          <div class="image-label">${section.caption || ''}</div>
+                        </div>
+                      </div>
+                     </div>`;
+            break;
+            
+          case 'note':
+            html += `<div class="content-section">
+                      <div class="note-box">
+                        <div class="note-title">${section.title || '【注意事项】'}</div>
+                        <div class="note-content">${section.content || ''}</div>
+                      </div>
+                     </div>`;
+            break;
+        }
+      });
+      
+      this.generatedHtml = html;
+      return html;
+    },
+    
+    // 验证表单
+    validateForm() {
+      if (!this.articleInfo.title) {
+        uni.showToast({
+          title: '请输入文章标题',
+          icon: 'none'
+        });
+        return false;
+      }
+      
+      if (!this.articleInfo.source) {
+        uni.showToast({
+          title: '请输入文章来源',
+          icon: 'none'
+        });
+        return false;
+      }
+      
+      if (!this.articleInfo.description) {
+        uni.showToast({
+          title: '请输入文章简介',
+          icon: 'none'
+        });
+        return false;
+      }
+      
+      // if (!this.articleInfo.thumbnail) {
+      //   uni.showToast({
+      //     title: '请上传文章缩略图',
+      //     icon: 'none'
+      //   });
+      //   return false;
+      // }
+      
+      if (this.articleInfo.contentType === 'video') {
+        if (!this.articleInfo.videoUrl) {
+          uni.showToast({
+            title: '请输入视频链接',
+            icon: 'none'
+          });
+          return false;
+        }
+      } else {
+        // 文章类型时,需要检查内容是否为空
+        if (this.contentSections.length === 0) {
+          uni.showToast({
+            title: '请添加文章内容',
+            icon: 'none'
+          });
+          return false;
+        }
+      }
+      
+      return true;
+    },
+    
+    // 保存文章
+    async saveArticle() {
+      if (!this.validateForm()) return;
+      
+      try {
+        this.loading = true;
+        
+        // 生成最终的HTML内容
+        const contentHtml = this.generateHtml();
+        console.log("contentHtml",contentHtml);
+        // 构建提交数据
+        const articleData = {
+          id: this.isEdit ? this.id : null,
+          title: this.articleInfo.title,
+          description: this.articleInfo.description,
+          content: contentHtml, // 直接传递原始HTML内容,不进行编码或转义
+          category: this.articleInfo.category,
+          thumbnail: this.articleInfo.thumbnail,
+          contentType: this.articleInfo.contentType,
+          videoUrl: this.articleInfo.videoUrl,
+          videoDuration: this.articleInfo.videoDuration,
+          source: this.articleInfo.source,
+          isRecommend: this.articleInfo.isRecommend,
+          status: this.articleInfo.status
+        };
+        
+        let result;
+        
+        if (this.isEdit) {
+          // 编辑模式
+          result = await updateArticle(articleData);
+        } else {
+          // 新建模式
+          result = await createArticle(articleData);
+        }
+        
+        if (result.data.code === 200) {
+          uni.showToast({
+            title: this.isEdit ? '更新成功' : '发布成功',
+            icon: 'success'
+          });
+          
+          // 成功后返回上一页
+          setTimeout(() => {
+            uni.navigateBack();
+          }, 1500);
+        } else {
+          uni.showToast({
+            title: result.data.msg || '保存失败',
+            icon: 'none'
+          });
+        }
+      } catch (error) {
+        console.error('保存文章失败:', error);
+        uni.showToast({
+          title: '网络异常,请稍后重试',
+          icon: 'none'
+        });
+      } finally {
+        this.loading = false;
+      }
+    },
+    
+    // 返回
+    goBack() {
+      uni.navigateBack();
+    },
+    
+    // 滚动到顶部
+    scrollToTop() {
+      uni.pageScrollTo({
+        scrollTop: 0,
+        duration: 300
+      });
+    }
+  }
+};
+</script>
+
+<style>
+/* 引入原有样式 */
+@font-face {
+  font-family: 'iconfont';
+  src: url('https://at.alicdn.com/t/font_3442238_cosd6rj55jg.ttf') format('truetype');
+}
+
+@font-face {
+  font-family: 'Material Icons';
+  font-style: normal;
+  font-weight: 400;
+  src: url(https://fonts.gstatic.com/s/materialicons/v139/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2) format('woff2');
+}
+
+.icon, .view-icon, .play-icon, .action-icon, .top-icon {
+  font-family: 'iconfont';
+}
+
+.material-icon {
+  font-family: 'Material Icons';
+  font-weight: normal;
+  font-style: normal;
+  font-size: 50rpx;
+  line-height: 1;
+  letter-spacing: normal;
+  text-transform: none;
+  display: inline-block;
+  white-space: nowrap;
+  word-wrap: normal;
+  direction: ltr;
+  -webkit-font-smoothing: antialiased;
+  color: #4CAF50;
+}
+
+/* 容器样式 */
+.container {
+  background-color: #f8f8f8;
+  min-height: 100vh;
+  position: relative;
+  padding-bottom: 120rpx;
+}
+
+/* 文章容器 */
+.article-container {
+  background-color: #fff;
+  border-radius: 0;
+  overflow: hidden;
+}
+
+/* H5导航栏样式 */
+.h5-custom-navbar {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  height: 90rpx;
+  background-color: #fff;
+  display: flex;
+  align-items: center;
+  padding: 0 30rpx;
+  z-index: 100;
+  box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
+}
+
+.h5-navbar-left {
+  width: 80rpx;
+  height: 80rpx;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.h5-back-icon {
+  font-size: 40rpx;
+  color: #333;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.h5-navbar-title {
+  flex: 1;
+  text-align: center;
+  font-size: 32rpx;
+  font-weight: bold;
+  color: #333;
+}
+
+.h5-navbar-right {
+  width: 60rpx;
+}
+
+.h5-arrow-left {
+  width: 24rpx;
+  height: 24rpx;
+  border-top: 4rpx solid #333;
+  border-left: 4rpx solid #333;
+  transform: rotate(-45deg);
+}
+
+/* 表单样式 */
+.form-section {
+  padding: 30rpx;
+  margin-bottom: 30rpx;
+  background-color: #fff;
+  border-bottom: 1rpx solid #eee;
+}
+
+.form-title {
+  font-size: 34rpx;
+  font-weight: bold;
+  color: #333;
+  margin-bottom: 30rpx;
+  padding-left: 16rpx;
+  border-left: 8rpx solid #4CAF50;
+}
+
+.form-item {
+  margin-bottom: 30rpx;
+}
+
+.form-label {
+  display: block;
+  font-size: 28rpx;
+  color: #666;
+  margin-bottom: 12rpx;
+}
+
+.form-input {
+  width: 100%;
+  height: 80rpx;
+  border: 1rpx solid #ddd;
+  border-radius: 8rpx;
+  padding: 0 20rpx;
+  font-size: 28rpx;
+  color: #333;
+  background-color: #f9f9f9;
+}
+
+.form-textarea {
+  width: 100%;
+  height: 180rpx;
+  border: 1rpx solid #ddd;
+  border-radius: 8rpx;
+  padding: 20rpx;
+  font-size: 28rpx;
+  color: #333;
+  background-color: #f9f9f9;
+}
+
+.type-group {
+  display: flex;
+  flex-direction: row;
+}
+
+.type-item {
+  margin-right: 60rpx;
+  font-size: 28rpx;
+  color: #333;
+}
+
+/* 上传框样式 */
+.upload-box {
+  width: 200rpx;
+  height: 200rpx;
+  border: 2rpx dashed #ddd;
+  border-radius: 8rpx;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  background-color: #f9f9f9;
+}
+
+.upload-placeholder {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  color: #999;
+  font-size: 24rpx;
+}
+
+.upload-icon {
+  font-size: 60rpx;
+  margin-bottom: 10rpx;
+}
+
+.thumbnail-preview {
+  width: 100%;
+  height: 100%;
+  border-radius: 8rpx;
+}
+
+/* 内容构建器样式 */
+.content-builder {
+  margin-top: 20rpx;
+}
+
+.section-controls {
+  display: flex;
+  flex-wrap: wrap;
+  margin-bottom: 30rpx;
+}
+
+.control-btn {
+  margin-right: 20rpx;
+  margin-bottom: 20rpx;
+  padding: 10rpx 24rpx;
+  background-color: #f5f5f5;
+  color: #333;
+  font-size: 26rpx;
+  border-radius: 30rpx;
+  border: 1rpx solid #e0e0e0;
+}
+
+.content-section-item {
+  margin-bottom: 30rpx;
+  border: 1rpx solid #eee;
+  border-radius: 8rpx;
+  padding: 20rpx;
+}
+
+.section-edit-item {
+  position: relative;
+}
+
+.section-textarea {
+  width: 100%;
+  min-height: 150rpx;
+  border: 1rpx solid #eee;
+  border-radius: 8rpx;
+  padding: 20rpx;
+  font-size: 28rpx;
+  color: #333;
+  margin-bottom: 20rpx;
+  background-color: #f9f9f9;
+}
+
+.section-title-input {
+  width: 100%;
+  height: 80rpx;
+  border: 1rpx solid #eee;
+  border-radius: 8rpx;
+  padding: 0 20rpx;
+  font-size: 30rpx;
+  font-weight: bold;
+  color: #333;
+  margin-bottom: 20rpx;
+  background-color: #f9f9f9;
+}
+
+.section-controls {
+  display: flex;
+  justify-content: flex-end;
+}
+
+.delete-btn, .move-btn {
+  font-size: 24rpx;
+  color: #999;
+  padding: 6rpx 16rpx;
+  margin-left: 20rpx;
+}
+
+.delete-btn {
+  color: #ff5252;
+}
+
+/* 图片上传区域 */
+.image-upload-container {
+  width: 100%;
+  margin-bottom: 20rpx;
+}
+
+.section-image-preview {
+  width: 100%;
+  height: 300rpx;
+  border-radius: 8rpx;
+}
+
+.image-caption-input {
+  width: 100%;
+  height: 70rpx;
+  border: 1rpx solid #eee;
+  border-radius: 8rpx;
+  padding: 0 20rpx;
+  font-size: 26rpx;
+  color: #333;
+  margin-top: 20rpx;
+  background-color: #f9f9f9;
+}
+
+.color-input {
+  width: 100%;
+  height: 70rpx;
+  border: 1rpx solid #eee;
+  border-radius: 8rpx;
+  padding: 0 20rpx;
+  font-size: 26rpx;
+  color: #333;
+  margin-top: 20rpx;
+  background-color: #f9f9f9;
+}
+
+/* 提示框编辑区域 */
+.note-box-edit {
+  border-left: 8rpx solid #8bc34a;
+  padding: 20rpx;
+  background-color: #f1f8e9;
+  margin-bottom: 20rpx;
+  border-radius: 8rpx;
+}
+
+.note-title-input {
+  width: 100%;
+  height: 70rpx;
+  border: 1rpx solid #e0e0e0;
+  border-radius: 8rpx;
+  padding: 0 20rpx;
+  font-size: 28rpx;
+  font-weight: bold;
+  color: #558b2f;
+  margin-bottom: 20rpx;
+  background-color: rgba(255,255,255,0.5);
+}
+
+.note-content-input {
+  width: 100%;
+  height: 150rpx;
+  border: 1rpx solid #e0e0e0;
+  border-radius: 8rpx;
+  padding: 20rpx;
+  font-size: 26rpx;
+  color: #689f38;
+  background-color: rgba(255,255,255,0.5);
+}
+
+/* 预览区域 */
+.preview-section {
+  padding: 30rpx;
+  margin-bottom: 30rpx;
+  background-color: #fff;
+  border-top: 1rpx solid #eee;
+}
+
+.article-content {
+  margin-top: 20rpx;
+  border: 1rpx dashed #ddd;
+  padding: 20rpx;
+  border-radius: 8rpx;
+}
+
+/* 提交按钮区域 */
+.submit-section {
+  padding: 20rpx 30rpx 50rpx;
+}
+
+.submit-btn {
+  width: 100%;
+  height: 90rpx;
+  line-height: 90rpx;
+  background: linear-gradient(135deg, #4CAF50, #388E3C);
+  color: #fff;
+  font-size: 32rpx;
+  font-weight: bold;
+  border-radius: 45rpx;
+  text-align: center;
+  box-shadow: 0 6rpx 16rpx rgba(76, 175, 80, 0.3);
+}
+
+/* 内容预览样式 */
+.content-section {
+  margin-bottom: 40rpx;
+}
+
+.section-title {
+  font-size: 34rpx;
+  font-weight: bold;
+  color: #2e7d32;
+  margin-bottom: 20rpx;
+  display: block;
+}
+
+.paragraph {
+  font-size: 30rpx;
+  line-height: 1.8;
+  color: #333;
+  margin-bottom: 20rpx;
+  display: block;
+}
+
+.section-image {
+  margin: 20rpx 0;
+  width: 100%;
+}
+
+.color-image {
+  width: 100%;
+  height: 300rpx;
+  border-radius: 0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  position: relative;
+  overflow: hidden;
+}
+
+.image-label {
+  color: white;
+  font-size: 32rpx;
+  font-weight: bold;
+  text-shadow: 0 2px 4px rgba(0,0,0,0.5);
+  z-index: 5;
+}
+
+.note-box {
+  background-color: #f1f8e9;
+  border-left: 8rpx solid #8bc34a;
+  padding: 20
+}
+</style>

+ 999 - 0
pages/knowledge/detail.vue

@@ -0,0 +1,999 @@
+<template>
+  <view class="container">
+    <!-- H5环境自定义导航栏 -->
+    <view class="h5-custom-navbar" v-if="isH5">
+      <view class="h5-navbar-left" @click="goBack">
+        <view class="h5-back-icon">
+          <view class="h5-arrow-left"></view>
+        </view>
+      </view>
+      <text class="h5-navbar-title">农业知识</text>
+      <view class="h5-navbar-right"></view>
+    </view>
+    
+    <!-- 文章内容区 -->
+    <view class="article-container" :style="isH5 ? 'margin-top: 90rpx;' : ''">
+      <!-- 加载状态显示 -->
+      <view class="loading-container" v-if="loading">
+        <view class="loading-spinner"></view>
+        <text class="loading-text">加载中...</text>
+      </view>
+
+      <!-- 内容区域 -->
+      <block v-else>
+        <!-- 封面图轮播 -->
+        <swiper class="cover-swiper" 
+          indicator-dots 
+          autoplay 
+          circular
+          interval="3000"
+          duration="500"
+          indicator-active-color="#4CAF50" 
+          indicator-color="rgba(255,255,255,0.5)"
+          @change="handleSwiperChange">
+          <!-- 有实际图片时展示 -->
+          <swiper-item v-for="(slide, index) in articleImages" :key="'img-' + index" v-if="articleImages.length > 0">
+            <view class="swiper-item-container" :style="{ backgroundColor: slide.color || '#4CAF50' }">
+              <image class="swiper-image" :src="slide.url" mode="aspectFill" v-if="slide.url"></image>
+              <view class="pattern-overlay"></view>
+              <view class="swiper-overlay">
+                <text class="swiper-title">{{ slide.title || articleInfo.title }}</text>
+                <text class="swiper-subtitle">{{ slide.subtitle || '' }}</text>
+              </view>
+            </view>
+          </swiper-item>
+          
+          <!-- 没有图片时使用生成的轮播图 -->
+          <swiper-item v-for="(slide, index) in carouselImages" :key="'gen-' + index" v-if="articleImages.length === 0">
+            <view class="swiper-item-container" :style="{ backgroundColor: slide.color || '#4CAF50' }">
+              <view class="pattern-overlay"></view>
+              <view class="agri-icon" v-html="slide.icon"></view>
+              <view class="swiper-overlay">
+                <text class="swiper-title">{{ slide.title || articleInfo.title }}</text>
+                <text class="swiper-subtitle">{{ slide.subtitle || '' }}</text>
+              </view>
+            </view>
+          </swiper-item>
+        </swiper>
+        <view class="swiper-counter" v-if="(articleImages.length > 0 && articleImages.length > 1) || (articleImages.length === 0 && carouselImages.length > 0)">
+          <text>{{ currentSwiperIndex + 1 }}/{{ articleImages.length > 0 ? articleImages.length : carouselImages.length }}</text>
+        </view>
+        
+        <!-- 标题及元信息 -->
+        <view class="article-header">
+          <text class="article-title">{{ articleInfo.title }}</text>
+          <view class="article-meta">
+            <text class="source">{{ articleInfo.source }}</text>
+            <text class="dot">·</text>
+            <text class="date">{{ formatDate(articleInfo.publishDate) }}</text>
+            <view class="views">
+              <!-- <text class="view-icon">&#xe614;</text> -->
+			  <image src="../../static/icons/yidu.png" mode="aspectFit"></image>
+              <text class="view-count">{{ articleInfo.viewCount }}</text>
+            </view>
+          </view>
+        </view>
+        
+        <!-- 文章正文 -->
+        <view class="article-content">
+          <!-- 如果有HTML内容则渲染HTML内容 -->
+          <rich-text v-if="articleInfo.content" :nodes="processHtmlContent(articleInfo.content)"></rich-text>
+          
+          <!-- 无内容时的备用内容 -->
+          <view v-else class="content-section">
+            <text class="paragraph">
+              暂无详细内容
+            </text>
+          </view>
+        </view>
+        
+        <!-- 视频示例 -->
+        <view class="video-container" v-if="articleInfo.contentType === 'video' && articleInfo.videoUrl">
+          <video 
+            class="article-video"
+            :src="articleInfo.videoUrl"
+			autoplay=true
+            controls
+            object-fit="contain"
+            poster=""
+          ></video>
+          <text class="video-title">{{ articleInfo.title }}</text>
+        </view>
+        
+        <!-- 视频预览 -->
+        <view class="video-container" v-else-if="articleInfo.contentType === 'video'">
+          <view class="video-poster">
+            <view class="video-color-bg" style="background-color: #33691E;">
+              <view class="video-pattern"></view>
+              <text class="video-label">视频教学</text>
+            </view>
+            <view class="play-button">
+              <view class="triangle-play"></view>
+            </view>
+            <text class="video-duration">{{ articleInfo.duration || '00:00' }}</text>
+          </view>
+          <text class="video-title">{{ articleInfo.title }}</text>
+        </view>
+      </block>
+    </view>
+    
+    <!-- 底部功能区 -->
+    <!-- <view class="footer-action" v-if="!loading">
+      <view class="action-button like" @click="handleLike">
+        <view class="action-icon-wrapper">
+          <text v-if="articleInfo.isLiked" class="material-icon">&#xe87d;</text>
+          <text v-else class="material-icon">&#xe87e;</text>
+        </view>
+        <text class="action-text">{{ articleInfo.isLiked ? '已点赞' : '点赞' }}</text>
+      </view>
+      <view class="action-button collect" @click="handleCollect">
+        <view class="action-icon-wrapper">
+          <text v-if="articleInfo.isFavorite" class="material-icon">&#xe838;</text>
+          <text v-else class="material-icon">&#xe83a;</text>
+        </view>
+        <text class="action-text">{{ articleInfo.isFavorite ? '已收藏' : '收藏' }}</text>
+      </view>
+      <view class="action-button share" @click="handleShare">
+        <view class="action-icon-wrapper">
+          <text class="material-icon">&#xe80d;</text>
+        </view>
+        <text class="action-text">分享</text>
+      </view>
+    </view> -->
+    
+    <!-- 返回顶部 -->
+    <view class="back-to-top" @click="scrollToTop">
+      <view class="top-arrow"></view>
+      <text class="top-text">顶部</text>
+    </view>
+  </view>
+</template>
+
+<script>
+import { getArticleDetail, getArticleImages, likeArticle, unlikeArticle, favoriteArticle, unfavoriteArticle } from '@/api/services/knowledge.js';
+import api from "@/config/api.js";
+export default {
+  data() {
+    return {
+	  baseUrl:api.im,
+      title: "农业知识",
+      // 文章ID
+      id: null,
+      // 文章类型
+      type: null,
+      // 加载状态
+      loading: true,
+      // 文章信息
+      articleInfo: {
+        id: '',
+        title: '',
+        source: '',
+        publishDate: '',
+        viewCount: 0,
+        content: '',
+        contentType: 'article',
+        videoUrl: '',
+        duration: '',
+        isLiked: false,
+        isFavorite: false
+      },
+      // 文章相关图片
+      articleImages: [],
+      // 轮播图(当没有真实图片时使用)
+      carouselImages: [],
+      currentSwiperIndex: 0,
+      isH5: false // 是否是H5环境
+    }
+  },
+  onLoad(options) {
+	  console.log("this...",this.baseUrl);
+	  console.log("process.env.NODE_ENV",process.env.NODE_ENV);
+    // 检测是否在H5环境中运行
+    // #ifdef H5
+    this.isH5 = true;
+    // #endif
+    
+    // 设置导航栏标题
+    uni.setNavigationBarTitle({
+      title: '农业知识'
+    });
+    
+    // 获取参数
+    this.id = options.id;
+    this.type = options.type;
+    
+    // 加载文章详情
+    this.loadArticleDetail();
+  },
+  onNavigationBarButtonTap(e) {
+    if (e.index === 0) {
+      this.goBack();
+    }
+  },
+  methods: {
+    // 加载文章详情
+    async loadArticleDetail() {
+      if (!this.id) {
+        uni.showToast({
+          title: '文章ID不能为空',
+          icon: 'none'
+        });
+        return;
+      }
+      
+      try {
+        this.loading = true;
+        const result = await getArticleDetail(this.id);
+        
+        if (result.data.code === 200 && result.data.data) {
+          // 设置文章信息
+          this.articleInfo = result.data.data;
+		  this.articleInfo.videoUrl = this.baseUrl + result.data.data.videoUrl
+          
+          // 加载文章相关图片
+          await this.loadArticleImages();
+          
+          // 如果没有相关图片,生成默认轮播图数据
+          if (this.articleImages.length === 0) {
+            this.generateCarouselImages();
+          }
+        } else {
+          uni.showToast({
+            title: result.data.msg || '获取文章详情失败',
+            icon: 'none'
+          });
+        }
+      } catch (error) {
+        console.error('获取文章详情失败:', error);
+        uni.showToast({
+          title: '网络异常,请稍后重试',
+          icon: 'none'
+        });
+      } finally {
+        this.loading = false;
+      }
+    },
+    
+    // 加载文章相关图片
+    async loadArticleImages() {
+      try {
+        const result = await getArticleImages(this.id);
+        if (result.data.code === 200 && result.data.data) {
+          this.articleImages = result.data.data;
+        } else {
+          console.log('获取文章图片失败:', result.data.msg);
+          this.articleImages = [];
+        }
+      } catch (error) {
+        console.error('获取文章图片失败:', error);
+        this.articleImages = [];
+      }
+    },
+    
+    // 生成默认轮播图数据
+    generateCarouselImages() {
+      // 如果没有内容,生成基于文章类型和内容的默认轮播图
+      const colors = ['#8BC34A', '#4CAF50', '#7CB342', '#689F38', '#33691E'];
+      const icons = [
+        '<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a10 10 0 1 0 10 10A10 10 0 0 0 12 2zm0 18a8 8 0 1 1 8-8 8 8 0 0 1-8 8z"></path><path d="M12 6a1 1 0 0 0-1 1v5a1 1 0 0 0 .55.89l4 2a1 1 0 0 0 .9-1.78L13 11.28V7a1 1 0 0 0-1-1z"></path></svg>',
+        '<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12h-4 M17 12l-3-3 M17 12l-3 3 M3 6h10 M13 6l-3-3 M13 6l-3 3 M3 18h10 M13 18l-3-3 M13 18l-3 3"></path></svg>',
+        '<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3v18h18"></path><path d="M18.5 3a2.5 2.5 0 0 1 0 5H12v6.5c0 1.38 1.12 2.5 2.5 2.5s2.5-1.12 2.5-2.5V9"></path></svg>',
+      ];
+      
+      // 根据文章类型生成不同的默认轮播图
+      let subtitles = [];
+      if (this.type === 'tech') {
+        subtitles = ['科学种植技术', '提高农作物产量', '农业技术创新', '绿色生态农业'];
+      } else {
+        subtitles = ['政策解读要点', '补贴申请指南', '农业发展方向', '乡村振兴战略'];
+      }
+      
+      // 生成3个轮播图
+      this.carouselImages = [];
+      for (let i = 0; i < 3; i++) {
+        this.carouselImages.push({
+          color: colors[i % colors.length],
+          title: this.articleInfo.title,
+          subtitle: subtitles[i % subtitles.length],
+          icon: icons[i % icons.length]
+        });
+      }
+    },
+    
+    // 格式化日期
+    formatDate(dateStr) {
+      if (!dateStr) return '';
+      
+      const date = new Date(dateStr);
+      return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
+    },
+    
+    // 返回上一页
+    goBack() {
+      uni.navigateBack();
+    },
+    
+    // 滚动到顶部
+    scrollToTop() {
+      uni.pageScrollTo({
+        scrollTop: 0,
+        duration: 300
+      });
+    },
+    
+    // 点赞/取消点赞
+    async handleLike() {
+      try {
+        if (this.articleInfo.isLiked) {
+          // 已点赞,执行取消点赞
+          const result = await unlikeArticle(this.id);
+          if (result.data.code === 200) {
+            this.articleInfo.isLiked = false;
+            uni.showToast({
+              title: '取消点赞成功',
+              icon: 'none'
+            });
+          }
+        } else {
+          // 未点赞,执行点赞
+          const result = await likeArticle(this.id);
+          if (result.data.code === 200) {
+            this.articleInfo.isLiked = true;
+            uni.showToast({
+              title: '点赞成功',
+              icon: 'none'
+            });
+          }
+        }
+      } catch (error) {
+        console.error('操作失败:', error);
+        if (error.response && error.response.status === 401) {
+          uni.showToast({
+            title: '请先登录',
+            icon: 'none'
+          });
+        } else {
+          uni.showToast({
+            title: '网络异常,请稍后重试',
+            icon: 'none'
+          });
+        }
+      }
+    },
+    
+    // 收藏/取消收藏
+    async handleCollect() {
+      try {
+        if (this.articleInfo.isFavorite) {
+          // 已收藏,执行取消收藏
+          const result = await unfavoriteArticle(this.id);
+          if (result.data.code === 200) {
+            this.articleInfo.isFavorite = false;
+            uni.showToast({
+              title: '取消收藏成功',
+              icon: 'none'
+            });
+          }
+        } else {
+          // 未收藏,执行收藏
+          const result = await favoriteArticle(this.id);
+          if (result.data.code === 200) {
+            this.articleInfo.isFavorite = true;
+            uni.showToast({
+              title: '收藏成功',
+              icon: 'none'
+            });
+          }
+        }
+      } catch (error) {
+        console.error('操作失败:', error);
+        if (error.response && error.response.status === 401) {
+          uni.showToast({
+            title: '请先登录',
+            icon: 'none'
+          });
+        } else {
+          uni.showToast({
+            title: '网络异常,请稍后重试',
+            icon: 'none'
+          });
+        }
+      }
+    },
+    
+    // 分享
+    handleShare() {
+      uni.showShareMenu({
+        withShareTicket: true,
+        menus: ['shareAppMessage', 'shareTimeline']
+      });
+    },
+    
+    // 处理轮播图切换
+    handleSwiperChange(e) {
+      this.currentSwiperIndex = e.detail.current;
+    },
+    
+    // 处理HTML内容
+    processHtmlContent(content) {
+      if (!content) return '';
+      
+      // 确保内容是字符串
+      let htmlContent = content.toString();
+      
+      // 如果内容不是HTML格式,尝试解析
+      if (!htmlContent.includes('<div class="content-section">')) {
+        try {
+          // 尝试解码可能被转义的HTML
+          htmlContent = htmlContent.replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '"').replace(/&amp;/g, '&');
+        } catch (e) {
+          console.error('HTML内容格式化失败:', e);
+        }
+      }
+      
+      return htmlContent;
+    }
+  }
+}
+</script>
+
+<style>
+/* 字体图标 */
+@font-face {
+  font-family: 'iconfont';
+  src: url('https://at.alicdn.com/t/font_3442238_cosd6rj55jg.ttf') format('truetype');
+}
+
+@font-face {
+  font-family: 'Material Icons';
+  font-style: normal;
+  font-weight: 400;
+  src: url(https://fonts.gstatic.com/s/materialicons/v139/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2) format('woff2');
+}
+
+.icon, .view-icon, .play-icon, .action-icon, .top-icon {
+  font-family: 'iconfont';
+}
+
+.material-icon {
+  font-family: 'Material Icons';
+  font-weight: normal;
+  font-style: normal;
+  font-size: 50rpx;
+  line-height: 1;
+  letter-spacing: normal;
+  text-transform: none;
+  display: inline-block;
+  white-space: nowrap;
+  word-wrap: normal;
+  direction: ltr;
+  -webkit-font-smoothing: antialiased;
+  color: #4CAF50;
+}
+
+/* 容器样式 */
+.container {
+  background-color: #f8f8f8;
+  min-height: 100vh;
+  position: relative;
+  padding-bottom: 120rpx;
+}
+
+/* 删除自定义导航栏样式 */
+.custom-navbar {
+  display: none;
+}
+
+.navbar-left, .back-icon, .navbar-title, .navbar-right, .arrow-left {
+  display: none;
+}
+
+/* 文章容器 */
+.article-container {
+  background-color: #fff;
+  border-radius: 0;
+  overflow: hidden;
+}
+
+/* 封面图轮播 */
+.cover-swiper {
+  width: 100%;
+  height: 420rpx;
+  position: relative;
+}
+
+.swiper-item-container {
+  position: relative;
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  border-radius: 0;
+}
+
+.swiper-image {
+  width: 100%;
+  height: 100%;
+  display: block;
+  object-fit: cover;
+}
+
+.swiper-overlay {
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  padding: 40rpx 30rpx;
+  background: linear-gradient(to top, rgba(0,0,0,0.7), rgba(0,0,0,0));
+  display: flex;
+  flex-direction: column;
+  justify-content: flex-end;
+  align-items: flex-start;
+  color: #fff;
+}
+
+.swiper-title {
+  font-size: 42rpx;
+  font-weight: bold;
+  margin-bottom: 10rpx;
+  text-shadow: 0 2px 4px rgba(0,0,0,0.5);
+}
+
+.swiper-subtitle {
+  font-size: 28rpx;
+  opacity: 0.9;
+  text-shadow: 0 1px 2px rgba(0,0,0,0.5);
+}
+
+/* 文章头部信息 */
+.article-header {
+  padding: 30rpx 30rpx 0;
+}
+
+.article-title {
+  font-size: 42rpx;
+  font-weight: bold;
+  color: #333;
+  line-height: 1.4;
+  margin-bottom: 20rpx;
+}
+
+.article-meta {
+  display: flex;
+  align-items: center;
+  margin-bottom: 40rpx;
+}
+
+.source {
+  font-size: 24rpx;
+  color: #666;
+}
+
+.dot {
+  margin: 0 10rpx;
+  color: #999;
+}
+
+.date {
+  font-size: 24rpx;
+  color: #999;
+}
+
+.views {
+  margin-left: auto;
+  display: flex;
+  align-items: center;
+}
+.views image {
+	padding-right: 6px;
+	width: 16px;  /* 设置图标宽度 */
+	height: 16px; /* 设置图标高度 */
+	vertical-align: middle;
+}
+
+.view-icon {
+  font-size: 24rpx;
+  color: #999;
+  margin-right: 6rpx;
+}
+
+.view-count {
+  font-size: 24rpx;
+  color: #999;
+}
+
+/* 文章内容 */
+.article-content {
+  padding: 0 30rpx 40rpx;
+}
+
+.content-section {
+  margin-bottom: 40rpx;
+}
+
+.section-title {
+  font-size: 34rpx;
+  font-weight: bold;
+  color: #2e7d32;
+  margin-bottom: 20rpx;
+  display: block;
+}
+
+.paragraph {
+  font-size: 30rpx;
+  line-height: 1.8;
+  color: #333;
+  margin-bottom: 20rpx;
+  display: block;
+}
+
+.content-image {
+  width: 100%;
+  margin: 20rpx 0;
+  border-radius: 12rpx;
+}
+
+.image-caption {
+  font-size: 24rpx;
+  color: #999;
+  text-align: center;
+  display: block;
+  margin-top: 10rpx;
+}
+
+/* 注意事项盒子 */
+.note-box {
+  background-color: #f1f8e9;
+  border-left: 8rpx solid #8bc34a;
+  padding: 20rpx;
+  border-radius: 8rpx;
+  margin: 20rpx 0;
+}
+
+.note-title {
+  font-size: 28rpx;
+  font-weight: bold;
+  color: #558b2f;
+  margin-bottom: 10rpx;
+  display: block;
+}
+
+.note-content {
+  font-size: 26rpx;
+  line-height: 1.6;
+  color: #689f38;
+  display: block;
+}
+
+/* 视频容器 */
+.video-container {
+  margin: 0 30rpx 40rpx;
+  border-radius: 0;
+  overflow: hidden;
+  background-color: #fff;
+}
+
+.article-video {
+  width: 100%;
+  height: 500rpx;
+}
+
+.video-poster {
+  position: relative;
+  width: 100%;
+  height: 380rpx;
+}
+
+.video-color-bg {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  border-radius: 0;
+}
+
+.video-pattern {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  opacity: 0.1;
+  background-image: 
+    linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, 
+    rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);
+  background-size: 50px 50px;
+}
+
+.video-label {
+  color: white;
+  font-size: 32rpx;
+  font-weight: bold;
+  text-shadow: 0 2px 4px rgba(0,0,0,0.5);
+  z-index: 5;
+}
+
+.play-button {
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  width: 120rpx;
+  height: 120rpx;
+  background-color: rgba(0, 0, 0, 0.6);
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  z-index: 10;
+  box-shadow: 0 0 20rpx rgba(0, 0, 0, 0.3);
+  border: 4rpx solid rgba(255, 255, 255, 0.8);
+}
+
+.triangle-play {
+  width: 0;
+  height: 0;
+  border-top: 26rpx solid transparent;
+  border-bottom: 26rpx solid transparent;
+  border-left: 40rpx solid #fff;
+  margin-left: 10rpx;
+}
+
+.video-duration {
+  position: absolute;
+  bottom: 20rpx;
+  right: 20rpx;
+  padding: 6rpx 12rpx;
+  background-color: rgba(0, 0, 0, 0.6);
+  color: #fff;
+  font-size: 22rpx;
+  border-radius: 4rpx;
+}
+
+.video-title {
+  font-size: 28rpx;
+  font-weight: bold;
+  color: #333;
+  padding: 20rpx;
+  display: block;
+}
+
+/* 底部功能区 */
+.footer-action {
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  height: 100rpx;
+  background-color: #fff;
+  display: flex;
+  align-items: center;
+  justify-content: space-around;
+  box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
+  z-index: 99;
+}
+
+.action-button {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  padding: 10rpx 0;
+}
+
+.action-icon-wrapper {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-bottom: 6rpx;
+}
+
+.action-text {
+  font-size: 24rpx;
+  color: #4CAF50;
+  font-weight: 500;
+}
+
+.action-button:active {
+  opacity: 0.7;
+}
+
+/* 返回顶部按钮 */
+.back-to-top {
+  position: fixed;
+  right: 30rpx;
+  bottom: 120rpx;
+  width: 100rpx;
+  height: 100rpx;
+  background-color: rgba(46, 125, 50, 0.9);
+  border-radius: 50%;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  z-index: 99;
+}
+
+.top-arrow {
+  width: 20rpx;
+  height: 20rpx;
+  border-top: 4rpx solid #fff;
+  border-left: 4rpx solid #fff;
+  transform: rotate(45deg);
+  margin-bottom: 6rpx;
+}
+
+.top-text {
+  color: #fff;
+  font-size: 22rpx;
+}
+
+.swiper-counter {
+  position: absolute;
+  top: 110rpx;
+  right: 30rpx;
+  padding: 6rpx 12rpx;
+  background-color: rgba(0, 0, 0, 0.5);
+  color: #fff;
+  font-size: 22rpx;
+  border-radius: 20rpx;
+  z-index: 10;
+}
+
+.pattern-overlay {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  opacity: 0.1;
+  background-image: 
+    linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, 
+    rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);
+  background-size: 50px 50px;
+}
+
+.agri-icon {
+  position: absolute;
+  top: 40%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  opacity: 0.8;
+  width: 120rpx;
+  height: 120rpx;
+  z-index: 5;
+}
+
+.agri-icon svg {
+  width: 100%;
+  height: 100%;
+  stroke-width: 1.5;
+}
+
+.section-image {
+  margin: 20rpx 0;
+  width: 100%;
+}
+
+.color-image {
+  width: 100%;
+  height: 300rpx;
+  border-radius: 0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  position: relative;
+  overflow: hidden;
+}
+
+.color-image::before {
+  content: "";
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background-image: 
+    linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, 
+    rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);
+  background-size: 50px 50px;
+  opacity: 0.2;
+}
+
+.image-label {
+  color: white;
+  font-size: 32rpx;
+  font-weight: bold;
+  text-shadow: 0 2px 4px rgba(0,0,0,0.5);
+  z-index: 5;
+}
+
+/* 删除浮动返回按钮样式 */
+.float-back-button, .float-back-icon {
+  display: none;
+}
+
+/* H5导航栏样式 */
+.h5-custom-navbar {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  height: 90rpx;
+  background-color: #fff;
+  display: flex;
+  align-items: center;
+  padding: 0 30rpx;
+  z-index: 100;
+  box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
+}
+
+.h5-navbar-left {
+  width: 80rpx;
+  height: 80rpx;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.h5-back-icon {
+  font-size: 40rpx;
+  color: #333;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.h5-navbar-title {
+  flex: 1;
+  text-align: center;
+  font-size: 32rpx;
+  font-weight: bold;
+  color: #333;
+}
+
+.h5-navbar-right {
+  width: 60rpx;
+}
+
+.h5-arrow-left {
+  width: 24rpx;
+  height: 24rpx;
+  border-top: 4rpx solid #333;
+  border-left: 4rpx solid #333;
+  transform: rotate(-45deg);
+}
+
+/* 加载状态 */
+.loading-container {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  height: 400rpx;
+}
+
+.loading-spinner {
+  width: 80rpx;
+  height: 80rpx;
+  border: 6rpx solid rgba(76, 175, 80, 0.2);
+  border-radius: 50%;
+  border-top: 6rpx solid #4CAF50;
+  animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+  0% { transform: rotate(0deg); }
+  100% { transform: rotate(360deg); }
+}
+
+.loading-text {
+  margin-top: 20rpx;
+  font-size: 28rpx;
+  color: #999;
+}
+</style> 

+ 625 - 33
pages/knowledge/index.vue

@@ -1,49 +1,641 @@
 <template>
-  <view class="container">
-    农业知识页面(待开发)
-    <!-- 使用iframe方式嵌入,仅在H5下有效 -->
-    <iframe
-      class="dify-iframe"
-      src="https://your-dify-server.com/embed/chat?token=xxx"
-      frameborder="0"
-    ></iframe>
+  <view class="page-container">
+    <!-- 顶部标签导航 -->
+    <view class="header-tabs">
+      <view 
+        class="tab-item" 
+        :class="{ active: currentTab === 0 }" 
+        @click="handleTabChange(0)"
+      >
+        <text>农技知识</text>
+        <view class="tab-line" :class="{ active: currentTab === 0 }"></view>
+      </view>
+      <view 
+        class="tab-item" 
+        :class="{ active: currentTab === 1 }" 
+        @click="handleTabChange(1)"
+      >
+        <text>政策解读</text>
+        <view class="tab-line" :class="{ active: currentTab === 1 }"></view>
+      </view>
+    </view>
+
+    <!-- 内容区域 -->
+    <scroll-view
+      scroll-y
+      class="content-container"
+      @scrolltolower="loadMore"
+      @refresherrefresh="onRefresh"
+      refresher-enabled
+      :refresher-triggered="isRefreshing"
+    >
+      <!-- 主内容区域 -->
+      <view class="cards-section">
+        <!-- 农技知识内容 -->
+        <block v-if="currentTab === 0">
+          <navigator 
+            class="content-card"
+            v-for="(item, index) in techList"
+            :key="index"
+            :url="`/pages/knowledge/detail?id=${item.id}&type=tech`"
+            hover-class="card-hover"
+          >
+            <view class="content-thumbnail">
+              <image 
+                :src="item.image" 
+                mode="aspectFill"
+                class="thumbnail-image"
+                lazy-load
+              />
+              <!-- 仅视频类型显示时长和播放按钮 -->
+              <template v-if="item.contentType === 'video'">
+                <view class="video-duration">{{ item.duration }}</view>
+                <view class="play-button">
+                  <view class="play-icon"></view>
+                </view>
+                <view class="content-type-tag video">视频</view>
+              </template>
+              <!-- 文章类型显示标签 -->
+              <template v-else>
+                <view class="content-type-tag article">文章</view>
+              </template>
+            </view>
+            <view class="content-info">
+              <text class="content-title">{{ item.title }}</text>
+              <view class="info-row">
+                <text class="source-name">{{ item.source }}</text>
+                <view class="view-count">
+                  <!-- <text class="iconfont icon-eye"></text> -->
+				  <image src="../../static/icons/yidu.png" mode="aspectFit"></image>
+                  <text>已读 {{ item.viewCount }}</text>
+                </view>
+              </view>
+            </view>
+          </navigator>
+        </block>
+
+        <!-- 政策解读内容 -->
+        <block v-if="currentTab === 1">
+          <navigator 
+            class="content-card"
+            v-for="(item, index) in policyList"
+            :key="index"
+            :url="`/pages/knowledge/detail?id=${item.id}&type=policy`"
+            hover-class="card-hover"
+          >
+            <view class="content-thumbnail">
+              <image 
+                :src="item.image" 
+                mode="aspectFill"
+                class="thumbnail-image"
+                lazy-load
+              />
+              <!-- 仅视频类型显示时长和播放按钮 -->
+              <template v-if="item.contentType === 'video'">
+                <view class="video-duration">{{ item.duration }}</view>
+                <view class="play-button">
+                  <view class="play-icon"></view>
+                </view>
+                <view class="content-type-tag video">视频</view>
+              </template>
+              <!-- 文章类型显示标签 -->
+              <template v-else>
+                <view class="content-type-tag article">文章</view>
+              </template>
+            </view>
+            <view class="content-info">
+              <text class="content-title">{{ item.title }}</text>
+              <view class="info-row">
+                <text class="source-name">{{ item.source }}</text>
+                <view class="view-count">
+                 <image src="../../static/icons/yidu.png" mode="aspectFit"></image>
+                  <text>已读 {{ item.viewCount }}</text>
+                </view>
+              </view>
+			  
+            </view>
+          </navigator>
+        </block>
+
+        <!-- 无数据状态 -->
+        <view class="empty-state" v-if="(currentTab === 0 && techList.length === 0) || (currentTab === 1 && policyList.length === 0)">
+          <image src="/static/images/empty.png" mode="aspectFit" class="empty-image"></image>
+          <text class="empty-text">暂无数据</text>
+        </view>
+        
+        <!-- 加载状态 -->
+        <view class="loading-state" v-if="loading">
+          <text class="loading-text">加载中...</text>
+        </view>
+        
+        <!-- 底部加载完成提示 -->
+        <view class="load-all" v-if="(currentTab === 0 && techLoadAll) || (currentTab === 1 && policyLoadAll)">
+          <text class="load-all-text">— 已经到底啦 —</text>
+        </view>
+      </view>
+
+      <!-- 底部安全区域 -->
+      <view style="height: 120rpx;"></view>
+    </scroll-view>
+
+    <!-- AI问答悬浮按钮 -->
+    <view class="assistant-btn" @click="navigateToAI" hover-class="btn-hover">
+      <image src="/static/icons/ai.png" class="assistant-icon" mode="aspectFit"></image>
+      <text class="btn-text">问农小禹</text>
+    </view>
   </view>
 </template>
 
 <script>
+import {getTechList,getPolicyList} from '@/api/services/knowledge.js';
 export default {
+  data() {
+    return {
+      // 当前标签
+      currentTab: 0,
+
+      // 农技知识列表
+      techList: [],
+      // 政策解读列表
+      policyList: [],
+
+      // 分页参数
+      techPage: 1,
+      policyPage: 1,
+      pageSize: 10,
+      
+      // 加载状态
+      loading: false,
+      isRefreshing: false,
+      
+      // 是否全部加载完成
+      techLoadAll: false,
+      policyLoadAll: false
+    }
+  },
+
   mounted() {
-    // 设置配置
-    window.difyChatbotConfig = {
-      token: 'OFaLSsPTnueswy16',
-      baseUrl: 'http://121.4.16.100'
-    };
-
-    // 注入脚本
-    const script = document.createElement('script');
-    script.src = 'http://121.4.16.100/embed.min.js';
-    script.id = 'OFaLSsPTnueswy16';
-    script.defer = true;
-    document.body.appendChild(script);
+	  console.log("process.env.NODE_ENV",process.env.NODE_ENV);
+    // 初始化数据加载
+    this.loadInitialData();
+  },
+
+  methods: {
+    // 初始化数据加载
+    loadInitialData() {
+      if (this.currentTab === 0) {
+        this.loadTechList();
+      } else {
+        this.loadPolicyList();
+      }
+    },
+    
+    // 处理Tab切换
+    handleTabChange(index) {
+      if (this.currentTab === index) {
+        return;
+      }
+      
+      this.currentTab = index;
+      
+      // 切换标签时,如果对应列表为空,则加载数据
+      if (index === 0 && this.techList.length === 0) {
+        this.loadTechList();
+      } else if (index === 1 && this.policyList.length === 0) {
+        this.loadPolicyList();
+      }
+    },
+
+    // 下拉刷新
+    async onRefresh() {
+      this.isRefreshing = true;
+      
+      if (this.currentTab === 0) {
+        this.techPage = 1;
+        this.techLoadAll = false;
+        await this.loadTechList(true);
+      } else {
+        this.policyPage = 1;
+        this.policyLoadAll = false;
+        await this.loadPolicyList(true);
+      }
+      
+      this.isRefreshing = false;
+    },
+
+    // 加载更多
+    loadMore() {
+      if (this.loading) return;
+      
+      if (this.currentTab === 0 && !this.techLoadAll) {
+        this.techPage++;
+        this.loadTechList();
+      } else if (this.currentTab === 1 && !this.policyLoadAll) {
+        this.policyPage++;
+        this.loadPolicyList();
+      }
+    },
+    
+    // 加载农技知识列表
+    async loadTechList(refresh = false) {
+      if (this.loading) return;
+      
+      this.loading = true;
+      
+      try {
+         const result = await getTechList({params: {
+            pageNum: this.techPage,
+            pageSize: this.pageSize
+          }});
+		console.log("result",result);
+        // 请求成功
+        if (result.data.code === 200 && result.data.data) {
+          const newData = result.data.data;
+          
+          // 如果是刷新或第一页,则替换列表
+          if (refresh || this.techPage === 1) {
+            this.techList = newData;
+          } else {
+            // 否则追加到列表
+            this.techList = [...this.techList, ...newData];
+          }
+          
+          // 判断是否加载完全部数据
+          if (newData.length < this.pageSize) {
+            this.techLoadAll = true;
+          }
+		  console.log("this.techList",this.techList);
+        } else {
+          // 请求返回错误
+          uni.showToast({
+            title: result.msg || '加载失败',
+            icon: 'none'
+          });
+          
+          // 如果不是第一页,则减回页码
+          if (this.techPage > 1) {
+            this.techPage--;
+          }
+        }
+      } catch (error) {
+        console.error('加载农技知识列表失败:', error);
+        uni.showToast({
+          title: '网络异常,请稍后重试',
+          icon: 'none'
+        });
+        
+        // 如果不是第一页,则减回页码
+        if (this.techPage > 1) {
+          this.techPage--;
+        }
+      } finally {
+        this.loading = false;
+      }
+    },
+    
+    // 加载政策解读列表
+    async loadPolicyList(refresh = false) {
+      if (this.loading) return;
+      
+      this.loading = true;
+      
+      try {
+        const result = await getPolicyList({
+          params: {
+            pageNum: this.policyPage,
+            pageSize: this.pageSize
+          }
+        })
+        
+        // 请求成功
+        if (result.data.code === 200 && result.data.data) {
+          const newData = result.data.data;
+          
+          // 如果是刷新或第一页,则替换列表
+          if (refresh || this.policyPage === 1) {
+            this.policyList = newData;
+          } else {
+            // 否则追加到列表
+            this.policyList = [...this.policyList, ...newData];
+          }
+          
+          // 判断是否加载完全部数据
+          if (newData.length < this.pageSize) {
+            this.policyLoadAll = true;
+          }
+        } else {
+          // 请求返回错误
+          uni.showToast({
+            title: result.msg || '加载失败',
+            icon: 'none'
+          });
+          
+          // 如果不是第一页,则减回页码
+          if (this.policyPage > 1) {
+            this.policyPage--;
+          }
+        }
+      } catch (error) {
+        console.error('加载政策解读列表失败:', error);
+        uni.showToast({
+          title: '网络异常,请稍后重试',
+          icon: 'none'
+        });
+        
+        // 如果不是第一页,则减回页码
+        if (this.policyPage > 1) {
+          this.policyPage--;
+        }
+      } finally {
+        this.loading = false;
+      }
+    },
+
+    // 跳转AI问答
+    navigateToAI() {
+      uni.navigateTo({
+        url: '/pages/knowledge/ai-chat/index'
+      })
+    }
   }
 }
 </script>
 
 <style scoped>
-.dify-iframe {
+
+
+.icon-robot:before {
+  content: "\e64b";
+}
+
+.icon-eye:before {
+  content: "\e614";
+}
+
+/* 页面容器
+.page-container {
+  min-height: 100vh;
+  background-color: #f7f8fa;
+  position: relative;
+  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
+}
+
+/* 头部标签样式 */
+.header-tabs {
+  display: flex;
+  background-color: #fff;
+  padding: 0 30rpx;
+  position: relative;
+  border-bottom: 1rpx solid #f2f2f2;
+}
+
+.tab-item {
+  position: relative;
+  padding: 20rpx 30rpx;
+  margin-right: 50rpx;
+  font-size: 30rpx;
+  color: #666;
+  transition: all 0.3s ease;
+}
+
+.tab-item.active {
+  color: #333;
+  font-weight: 600;
+}
+
+.tab-line {
+  position: absolute;
+  bottom: 0;
+  left: 50%;
+  width: 0;
+  height: 6rpx;
+  background-color: #4dc971;
+  border-radius: 3rpx;
+  transition: all 0.3s ease;
+  transform: translateX(-50%);
+}
+
+.tab-line.active {
+  width: 40rpx;
+}
+
+/* 内容区域样式 */
+.content-container {
+  height: calc(100vh - 88rpx);
+  box-sizing: border-box;
+}
+
+.cards-section {
+  padding: 12rpx;
+}
+
+/* 内容卡片通用样式 */
+.content-card {
+  background-color: #fff;
+  border-radius: 16rpx;
+  margin-bottom: 24rpx;
+  overflow: hidden;
+  transition: all 0.2s ease;
+  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
+  position: relative;
+}
+
+.card-hover {
+  transform: translateY(-2rpx);
+  box-shadow: 0 6rpx 16rpx rgba(0, 0, 0, 0.12);
+}
+
+/* 缩略图 */
+.content-thumbnail {
+  position: relative;
   width: 100%;
-  height: 100vh;
-  border: none;
+  height: 300rpx;
+  background-color: #f0f0f0;
 }
- #dify-chatbot-bubble-button {
-    background-color: #1C64F2 !important;
-  }
-  #dify-chatbot-bubble-window {
-    width: 24rem !important;
-    height: 40rem !important;
-  }
-.container {
-  padding: 40rpx;
-  font-size: 32rpx;
+
+.thumbnail-image {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+}
+
+/* 播放按钮 */
+.play-button {
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  width: 70rpx;
+  height: 70rpx;
+  background-color: rgba(0, 0, 0, 0.5);
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  z-index: 2;
+}
+
+.play-icon {
+  width: 0;
+  height: 0;
+  border-style: solid;
+  border-width: 9rpx 0 9rpx 16rpx;
+  border-color: transparent transparent transparent #ffffff;
+  margin-left: 4rpx;
+}
+
+/* 视频时长 */
+.video-duration {
+  position: absolute;
+  bottom: 10rpx;
+  right: 10rpx;
+  padding: 3rpx 8rpx;
+  background-color: rgba(0, 0, 0, 0.6);
+  color: #fff;
+  font-size: 18rpx;
+  border-radius: 10rpx;
+}
+
+/* 内容类型标签 */
+.content-type-tag {
+  position: absolute;
+  top: 10rpx;
+  left: 10rpx;
+  padding: 4rpx 10rpx;
+  font-size: 20rpx;
+  border-radius: 6rpx;
+  z-index: 2;
+}
+
+.content-type-tag.video {
+  background-color: #4dc971;
+  color: #fff;
+}
+
+.content-type-tag.article {
+  background-color: #3498db;
+  color: #fff;
+}
+
+/* 内容信息 */
+.content-info {
+  padding: 16rpx;
+  position: relative;
+}
+
+.content-title {
+  font-size: 30rpx;
+  font-weight: 600;
+  color: #333;
+  line-height: 1.4;
+  margin-bottom: 10rpx;
+  display: -webkit-box;
+  -webkit-box-orient: vertical;
+  -webkit-line-clamp: 2;
+  overflow: hidden;
+}
+
+.source-name {
+  color: #999;
+  font-size: 22rpx;
+}
+
+.info-row {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.view-count {
+  color: #c0c0c0;
+  font-size: 20rpx;
+  display: flex;
+  align-items: center;
+}
+
+.view-count image {
+  padding-right: 6px;
+  width: 16px;  /* 设置图标宽度 */
+  height: 16px; /* 设置图标高度 */
+  vertical-align: middle;
+}
+
+.view-count .iconfont {
+  margin-right: 4rpx;
+}
+
+/* 空状态 */
+.empty-state {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding: 120rpx 0;
+}
+
+.empty-image {
+  width: 180rpx;
+  height: 180rpx;
+  margin-bottom: 16rpx;
+}
+
+.empty-text {
+  font-size: 26rpx;
+  color: #999;
+}
+
+/* 加载中状态 */
+.loading-state {
+  text-align: center;
+  padding: 20rpx 0;
+}
+
+.loading-text {
+  font-size: 24rpx;
+  color: #999;
+}
+
+/* 加载全部状态 */
+.load-all {
+  text-align: center;
+  padding: 30rpx 0;
+}
+
+.load-all-text {
+  font-size: 24rpx;
+  color: #999;
+}
+
+/* AI按钮 */
+.assistant-btn {
+  position: fixed;
+  right: 30rpx;
+  bottom: 100rpx;
+  height: 80rpx;
+  border-radius: 40rpx;
+  background: linear-gradient(135deg, #42b983, #3db160);
+  display: flex;
+  align-items: center;
+  padding: 0 24rpx;
+  box-shadow: 0 6rpx 20rpx rgba(66, 185, 131, 0.35);
+  z-index: 99;
+  transition: all 0.25s ease;
+}
+
+.assistant-icon {
+  width: 68rpx;
+  height: 68rpx;
+  margin-right: 10rpx;
+}
+
+.btn-hover {
+  transform: scale(1.05);
+  box-shadow: 0 8rpx 24rpx rgba(66, 185, 131, 0.45);
+}
+
+.btn-text {
+  font-size: 28rpx;
+  color: #fff;
+  font-weight: 500;
 }
 </style>

+ 5 - 2
pages/login/index.vue

@@ -154,8 +154,11 @@
 									});
 								}else{
 									// 老用户直接跳转首页
-									wx.switchTab({
-										url: '/pages/user/index'
+									// wx.switchTab({
+									// 	url: '/pages/user/index'
+									// });
+									uni.navigateBack({
+										delta: 1,
 									});
 								}
 								

+ 1 - 2
pages/user/index.vue

@@ -19,7 +19,6 @@
 			<view class="card-title">
 				<text>我的地块</text>
 				<text class="more" @click="navigateToPlots">查看更多 ></text>
-				<text class="more" @click="refresh">测试token过期 ></text>
 			</view>
 			<view class="plot-info">
 				<view class="plot-item">
@@ -231,7 +230,7 @@
 		width: 120rpx;
 		height: 120rpx;
 		border-radius: 60rpx;
-		border: 4rpx solid #fff;
+		/* border: 4rpx solid #fff; */
 	}
 
 	.user-detail {

BIN
static/icons/.DS_Store


BIN
static/icons/ai.png


BIN
static/icons/chat.png


BIN
static/icons/chat_off.png


BIN
static/icons/yidu.png


+ 1 - 0
static/images/agriculture/backup.svg

@@ -0,0 +1 @@
+<svg width='1200' height='800' xmlns='http://www.w3.org/2000/svg'><rect width='100%' height='100%' fill='#8BC34A'/><text x='600' y='400' font-family='Arial' font-size='60' text-anchor='middle' fill='white'>水稻高产栽培技术 - 备用图片</text></svg>

+ 6 - 0
static/images/agriculture/rice-field1.svg

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="1200" height="800" xmlns="http://www.w3.org/2000/svg">
+  <rect width="100%" height="100%" fill="#8BC34A"/>
+  <text x="600" y="400" font-family="Arial" font-size="60" text-anchor="middle" fill="white">水稻高产栽培技术 - 1</text>
+  <text x="600" y="480" font-family="Arial" font-size="36" text-anchor="middle" fill="white">绿色稻田图片1</text>
+</svg> 

+ 6 - 0
static/images/agriculture/rice-field2.svg

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="1200" height="800" xmlns="http://www.w3.org/2000/svg">
+  <rect width="100%" height="100%" fill="#4CAF50"/>
+  <text x="600" y="400" font-family="Arial" font-size="60" text-anchor="middle" fill="white">水稻高产栽培技术 - 2</text>
+  <text x="600" y="480" font-family="Arial" font-size="36" text-anchor="middle" fill="white">绿色稻田图片2</text>
+</svg> 

+ 6 - 0
static/images/agriculture/rice-field3.svg

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="1200" height="800" xmlns="http://www.w3.org/2000/svg">
+  <rect width="100%" height="100%" fill="#7CB342"/>
+  <text x="600" y="400" font-family="Arial" font-size="60" text-anchor="middle" fill="white">水稻高产栽培技术 - 3</text>
+  <text x="600" y="480" font-family="Arial" font-size="36" text-anchor="middle" fill="white">绿色稻田图片3</text>
+</svg> 

+ 6 - 0
static/images/agriculture/rice-field4.svg

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="1200" height="800" xmlns="http://www.w3.org/2000/svg">
+  <rect width="100%" height="100%" fill="#689F38"/>
+  <text x="600" y="400" font-family="Arial" font-size="60" text-anchor="middle" fill="white">水稻高产栽培技术 - 4</text>
+  <text x="600" y="480" font-family="Arial" font-size="36" text-anchor="middle" fill="white">绿色稻田图片4</text>
+</svg> 

+ 6 - 0
static/images/agriculture/rice-field5.svg

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="1200" height="800" xmlns="http://www.w3.org/2000/svg">
+  <rect width="100%" height="100%" fill="#33691E"/>
+  <text x="600" y="400" font-family="Arial" font-size="60" text-anchor="middle" fill="white">水稻高产栽培技术 - 5</text>
+  <text x="600" y="480" font-family="Arial" font-size="36" text-anchor="middle" fill="white">绿色稻田图片5</text>
+</svg> 

+ 0 - 0
static/images/article-cover.jpg


+ 0 - 0
static/images/chat-bg-pattern.png


+ 21 - 0
static/images/chat-bg-pattern.svg

@@ -0,0 +1,21 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100">
+  <defs>
+    <pattern id="pattern" width="100" height="100" patternUnits="userSpaceOnUse">
+      <!-- 小麦图案 -->
+      <path d="M10,50 Q20,40 10,30 M10,30 L5,25 M10,30 L15,25" stroke="#E8F5E9" stroke-width="1" fill="none" />
+      <path d="M30,80 Q40,70 30,60 M30,60 L25,55 M30,60 L35,55" stroke="#E8F5E9" stroke-width="1" fill="none" />
+      
+      <!-- 水稻图案 -->
+      <path d="M70,20 Q80,10 90,20 M90,20 L95,15 M90,20 L85,15" stroke="#E8F5E9" stroke-width="1" fill="none" />
+      <path d="M50,90 Q60,80 70,90 M70,90 L75,85 M70,90 L65,85" stroke="#E8F5E9" stroke-width="1" fill="none" />
+      
+      <!-- 简单叶子图案 -->
+      <path d="M20,10 C25,5 30,5 35,10 C30,15 25,15 20,10 Z" fill="#E8F5E9" opacity="0.3" />
+      <path d="M60,40 C65,35 70,35 75,40 C70,45 65,45 60,40 Z" fill="#E8F5E9" opacity="0.3" />
+      <path d="M80,60 C85,55 90,55 95,60 C90,65 85,65 80,60 Z" fill="#E8F5E9" opacity="0.3" />
+      <path d="M40,90 C45,85 50,85 55,90 C50,95 45,95 40,90 Z" fill="#E8F5E9" opacity="0.3" />
+    </pattern>
+  </defs>
+  
+  <rect width="100" height="100" fill="url(#pattern)" />
+</svg> 

BIN
static/images/empty.png


BIN
static/images/icon.png


BIN
static/images/logo.png


+ 0 - 0
static/images/pattern-bg.png


+ 1 - 0
static/images/rice-farming/aerial-rice-field.jpg

@@ -0,0 +1 @@
+<html><body>404</body></html>

BIN
static/images/rice-farming/rice-field1.jpg


BIN
static/images/rice-farming/rice-field2.jpg


BIN
static/images/rice-farming/rice-field3.jpg


BIN
static/images/rice-farming/rice-field4.jpg


BIN
static/images/rice-farming/rice-field5.jpg


+ 1 - 0
static/images/rice-farming/rice-harvest.jpg

@@ -0,0 +1 @@
+<html><body>404</body></html>

+ 1 - 0
static/images/rice-farming/rice-seedlings.jpg

@@ -0,0 +1 @@
+<html><body>404</body></html>

+ 1 - 0
static/images/rice-farming/terraced-rice-field.jpg

@@ -0,0 +1 @@
+<html><body>404</body></html>

+ 0 - 0
static/images/rice-field1.jpg


+ 0 - 0
static/images/rice-field2.jpg


+ 0 - 0
static/images/rice-seedling.jpg


+ 0 - 0
static/images/robot-avatar.png


+ 31 - 0
static/images/robot-avatar.svg

@@ -0,0 +1,31 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
+  <!-- 头部 -->
+  <circle cx="50" cy="50" r="45" fill="#4CAF50" />
+  <circle cx="50" cy="50" r="40" fill="#81C784" />
+  
+  <!-- 眼睛 -->
+  <circle cx="35" cy="40" r="8" fill="white" />
+  <circle cx="65" cy="40" r="8" fill="white" />
+  <circle cx="35" cy="40" r="4" fill="#333" />
+  <circle cx="65" cy="40" r="4" fill="#333" />
+  <circle cx="36" cy="38" r="2" fill="white" />
+  <circle cx="66" cy="38" r="2" fill="white" />
+  
+  <!-- 嘴巴 -->
+  <path d="M35,65 Q50,75 65,65" stroke="#333" stroke-width="3" fill="none" />
+  
+  <!-- 天线 -->
+  <line x1="40" y1="15" x2="40" y2="25" stroke="#333" stroke-width="3" />
+  <line x1="60" y1="15" x2="60" y2="25" stroke="#333" stroke-width="3" />
+  <circle cx="40" cy="12" r="4" fill="#FF5722" />
+  <circle cx="60" cy="12" r="4" fill="#FF5722" />
+  
+  <!-- 耳朵/侧面元素 -->
+  <rect x="10" y="40" width="5" height="15" rx="2" fill="#388E3C" />
+  <rect x="85" y="40" width="5" height="15" rx="2" fill="#388E3C" />
+  
+  <!-- 小装饰 -->
+  <rect x="40" y="80" width="20" height="5" rx="2" fill="#388E3C" />
+  <circle cx="30" cy="80" r="3" fill="#81C784" />
+  <circle cx="70" cy="80" r="3" fill="#81C784" />
+</svg> 

+ 0 - 0
static/images/tmp/test.txt


+ 0 - 0
static/images/user-avatar.png


+ 17 - 0
static/images/user-avatar.svg

@@ -0,0 +1,17 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
+  <!-- 背景圆形 -->
+  <circle cx="50" cy="50" r="50" fill="#E0E0E0" />
+  
+  <!-- 人物上半身轮廓 -->
+  <circle cx="50" cy="42" r="20" fill="#9E9E9E" />
+  
+  <!-- 人物下半身轮廓 -->
+  <path d="M20,90 Q50,110 80,90 L80,85 Q50,100 20,85 Z" fill="#9E9E9E" />
+  
+  <!-- 脖子连接 -->
+  <rect x="40" y="58" width="20" height="10" fill="#9E9E9E" />
+  
+  <!-- 肩部 -->
+  <path d="M40,65 Q30,70 25,85 L30,87 Q35,75 40,70 Z" fill="#9E9E9E" />
+  <path d="M60,65 Q70,70 75,85 L70,87 Q65,75 60,70 Z" fill="#9E9E9E" />
+</svg> 

+ 0 - 0
static/images/video-cover.jpg


+ 6 - 6
utils/filters.js

@@ -2,9 +2,9 @@
 // import { getUserInfo } from "@/api/services/members.js";
 import storage from "@/utils/storage.js";
 // import Vue from "vue";
-import { getCurrentInstance } from 'vue'
+// import { getCurrentInstance } from 'vue'
 // 获取当前组件实例
-const { proxy } = getCurrentInstance()
+// const { proxy } = getCurrentInstance()
 import Foundation from "./Foundation.js";
 /**
  * 金钱单位置换  2999 --> 2,999.00
@@ -389,7 +389,7 @@ export function isLogin (val) {
 /**
  * 跳转im
  */
-export function talkIm (storeId, goodsId, id) {
+/* export function talkIm (storeId, goodsId, id) {
   if (isLogin('auth')) {
     let url = `/pages/mine/im/index?userId=${storeId}`
     if(goodsId && id) url = `/pages/mine/im/index?userId=${storeId}&goodsid=${goodsId}&skuid=${id}`
@@ -400,9 +400,9 @@ export function talkIm (storeId, goodsId, id) {
   else {
     tipsToLogin()
   }
-}
+} */
 
-export function tipsToLogin (type) {
+/* export function tipsToLogin (type) {
   if (!isLogin("auth")) {
     uni.showModal({
       title: "提示",
@@ -424,7 +424,7 @@ export function tipsToLogin (type) {
     return false;
   }
   return true;
-}
+} */
 
 /**
  * 获取用户信息并重新添加到缓存里面