diff --git a/app.json b/app.json
index 2aaa0f5..ba39e7b 100644
--- a/app.json
+++ b/app.json
@@ -12,6 +12,8 @@
"pages/plant-detail/growth-record/index",
"pages/wiki/detail/index",
"pages/wiki/identify/index",
+ "pages/wiki/chat/index",
+ "pages/wiki/chat/history/index",
"pages/profile/identify-history/index",
"pages/profile/badges/index",
"pages/profile/badges/level-detail/index",
diff --git a/pages/community/index.wxml b/pages/community/index.wxml
index db294ed..ded0cbb 100644
--- a/pages/community/index.wxml
+++ b/pages/community/index.wxml
@@ -135,11 +135,16 @@
-
-
+
+
+ 💬
- 暂无相关动态
+ 暂无相关动态
快来发布第一条动态吧
+
+
+ 发布动态
+
diff --git a/pages/community/index.wxss b/pages/community/index.wxss
index 9bcdf05..c07155f 100644
--- a/pages/community/index.wxss
+++ b/pages/community/index.wxss
@@ -276,31 +276,75 @@ page {
flex-direction: column;
align-items: center;
justify-content: center;
- padding: 120rpx 40rpx;
- color: #999;
+ padding: 100rpx 40rpx 60rpx;
}
-.empty-icon {
- width: 160rpx;
- height: 160rpx;
- background: #f5f5f5;
- border-radius: 50%;
+.empty-scene {
+ position: relative;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 32rpx;
}
-.empty-text {
+.empty-glow {
+ position: absolute;
+ width: 240rpx;
+ height: 240rpx;
+ border-radius: 50%;
+ background: radial-gradient(circle, rgba(85,139,47,0.12) 0%, transparent 70%);
+ animation: emptyPulse 3s ease-in-out infinite;
+}
+
+@keyframes emptyPulse {
+ 0%, 100% { transform: scale(1); opacity: 0.7; }
+ 50% { transform: scale(1.1); opacity: 1; }
+}
+
+.empty-emoji {
+ font-size: 96rpx;
+ position: relative;
+ z-index: 2;
+}
+
+.anim-breathe {
+ animation: breathe 3s ease-in-out infinite;
+}
+
+@keyframes breathe {
+ 0%, 100% { transform: scale(1) translateY(0); }
+ 50% { transform: scale(1.05) translateY(-8rpx); }
+}
+
+.empty-title {
font-size: 32rpx;
- font-weight: 600;
- color: #666;
- margin-bottom: 12rpx;
+ font-weight: 700;
+ color: #558B2F;
+ margin-bottom: 8rpx;
}
.empty-hint {
font-size: 26rpx;
- color: #999;
+ color: #90A4AE;
+ margin-bottom: 36rpx;
+}
+
+.empty-cta {
+ display: flex;
+ align-items: center;
+ gap: 8rpx;
+ padding: 16rpx 40rpx;
+ border-radius: 40rpx;
+ background: linear-gradient(135deg, #558B2F, #7CB342);
+ color: #fff;
+ font-size: 28rpx;
+ font-weight: 600;
+ box-shadow: 0 6rpx 20rpx rgba(85,139,47,0.3);
+ transition: all 0.15s;
+}
+
+.empty-cta:active {
+ transform: scale(0.95);
}
/* Floating Action Button */
@@ -308,23 +352,23 @@ page {
position: fixed;
right: 40rpx;
bottom: 60rpx;
- background: #558B2F;
+ background: rgba(85, 139, 47, 0.92);
+ backdrop-filter: blur(12px);
color: white;
- padding: 24rpx 40rpx;
- border-radius: 60rpx;
+ padding: 20rpx 36rpx;
+ border-radius: 48rpx;
display: flex;
align-items: center;
- gap: 12rpx;
- box-shadow: 0 12rpx 32rpx rgba(85, 139, 47, 0.4);
+ gap: 10rpx;
+ box-shadow: 0 8rpx 28rpx rgba(85, 139, 47, 0.35);
z-index: 100;
- font-size: 28rpx;
+ font-size: 26rpx;
font-weight: 700;
transition: all 0.2s ease;
}
.floating-add-btn:active {
transform: scale(0.92);
- box-shadow: 0 4rpx 16rpx rgba(85, 139, 47, 0.2);
}
/* WeChat Style Action Container */
diff --git a/pages/garden/index.js b/pages/garden/index.js
index 4820d8f..c3ba937 100644
--- a/pages/garden/index.js
+++ b/pages/garden/index.js
@@ -14,6 +14,7 @@ Page({
total: 0,
isLastPage: false,
isLoading: false,
+ isRefreshing: false,
scrollTop: 0
},
@@ -34,13 +35,23 @@ Page({
this.loadPlants(true);
},
- // Pull to refresh
+ // Pull to refresh (page-level)
onPullDownRefresh() {
this.loadPlants(true).then(() => {
wx.stopPullDownRefresh();
});
},
+ // Pull to refresh (scroll-view)
+ onRefresh() {
+ this.setData({ isRefreshing: true });
+ this.loadPlants(true).then(() => {
+ this.setData({ isRefreshing: false });
+ }).catch(() => {
+ this.setData({ isRefreshing: false });
+ });
+ },
+
// Infinite scroll
onReachBottom() {
if (!this.data.isLastPage && !this.data.isLoading) {
diff --git a/pages/garden/index.wxml b/pages/garden/index.wxml
index e0fb37d..2d9c077 100644
--- a/pages/garden/index.wxml
+++ b/pages/garden/index.wxml
@@ -17,7 +17,9 @@
-
+
+ 🌿🌸🍀🌺
+
共养护 {{total}} 盆植物
@@ -45,7 +47,7 @@
-
+
@@ -55,6 +57,7 @@
width="100%"
height="100%"
t-class="uploaded-img"
+ lazy
/>
{{item.daysPlanted}}天
diff --git a/pages/garden/index.wxss b/pages/garden/index.wxss
index 0770adc..4861b06 100644
--- a/pages/garden/index.wxss
+++ b/pages/garden/index.wxss
@@ -83,9 +83,19 @@
flex-shrink: 0;
}
-.garden-banner {
+.garden-banner-bg {
width: 100%;
height: 100%;
+ background: linear-gradient(135deg, #558B2F 0%, #7CB342 40%, #AED581 100%);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.banner-deco {
+ font-size: 56rpx;
+ letter-spacing: 24rpx;
+ opacity: 0.4;
}
.banner-overlay {
@@ -207,23 +217,23 @@
position: fixed;
right: 40rpx;
bottom: 60rpx;
- background: #558B2F;
+ background: rgba(85, 139, 47, 0.92);
+ backdrop-filter: blur(12px);
color: white;
- padding: 24rpx 40rpx;
- border-radius: 60rpx;
+ padding: 20rpx 36rpx;
+ border-radius: 48rpx;
display: flex;
align-items: center;
- gap: 12rpx;
- box-shadow: 0 12rpx 32rpx rgba(85, 139, 47, 0.4);
+ gap: 10rpx;
+ box-shadow: 0 8rpx 28rpx rgba(85, 139, 47, 0.35);
z-index: 1000;
- font-size: 28rpx;
+ font-size: 26rpx;
font-weight: 700;
transition: all 0.2s ease;
}
.floating-add-btn:active {
transform: scale(0.92);
- box-shadow: 0 4rpx 16rpx rgba(85, 139, 47, 0.2);
}
/* List Footer */
.list-footer {
diff --git a/pages/profile/index.wxml b/pages/profile/index.wxml
index 5d97c12..2bbe1c0 100644
--- a/pages/profile/index.wxml
+++ b/pages/profile/index.wxml
@@ -69,7 +69,9 @@
-
+
-
+
-
+
@@ -101,7 +107,9 @@
-
+
diff --git a/pages/wiki/chat/history/index.js b/pages/wiki/chat/history/index.js
new file mode 100644
index 0000000..1073cb8
--- /dev/null
+++ b/pages/wiki/chat/history/index.js
@@ -0,0 +1,113 @@
+// pages/wiki/chat/history/index.js
+import request from '../../../../utils/request';
+
+Page({
+ data: {
+ list: [],
+ total: 0,
+ current: 1,
+ pageSize: 15,
+ loading: false,
+ hasMore: true,
+ showClearDialog: false,
+ },
+
+ onLoad() {
+ this.fetchHistory(true);
+ },
+
+ fetchHistory(reset = false) {
+ if (this.data.loading) return;
+ if (!reset && !this.data.hasMore) return;
+
+ const current = reset ? 1 : this.data.current;
+ this.setData({ loading: true });
+
+ request.get('/plant/chat/history', { current, pageSize: this.data.pageSize })
+ .then(res => {
+ const items = (res.list || []).map(item => ({
+ ...item,
+ answerPreview: (item.answer || '').substring(0, 80) + ((item.answer || '').length > 80 ? '...' : ''),
+ }));
+ const total = res.total || 0;
+
+ if (reset) {
+ this.setData({
+ list: items,
+ total,
+ current: 2,
+ hasMore: items.length < total,
+ loading: false,
+ });
+ } else {
+ const old = this.data.list;
+ const update = {};
+ items.forEach((item, i) => {
+ update[`list[${old.length + i}]`] = item;
+ });
+ update.current = current + 1;
+ update.hasMore = (old.length + items.length) < total;
+ update.loading = false;
+ update.total = total;
+ this.setData(update);
+ }
+ })
+ .catch(() => {
+ this.setData({ loading: false });
+ });
+ },
+
+ loadMore() {
+ this.fetchHistory(false);
+ },
+
+ onTapItem(e) {
+ const item = e.currentTarget.dataset.item;
+ // Navigate to chat page with prefilled Q&A
+ wx.navigateTo({
+ url: '/pages/wiki/chat/index?fromHistory=1',
+ success(res) {
+ res.eventChannel.emit('historyData', {
+ question: item.question,
+ answer: item.answer,
+ });
+ },
+ });
+ },
+
+ onDeleteItem(e) {
+ const id = e.currentTarget.dataset.id;
+ wx.showModal({
+ title: '删除记录',
+ content: '确定删除这条问答记录吗?',
+ success: (res) => {
+ if (res.confirm) {
+ request.post('/plant/chat/history/delete', { id }).then(() => {
+ wx.showToast({ title: '已删除', icon: 'success' });
+ this.fetchHistory(true);
+ });
+ }
+ },
+ });
+ },
+
+ onClearAll() {
+ this.setData({ showClearDialog: true });
+ },
+
+ closeClearDialog() {
+ this.setData({ showClearDialog: false });
+ },
+
+ doClearAll() {
+ this.setData({ showClearDialog: false });
+ request.post('/plant/chat/history/clear').then(() => {
+ wx.showToast({ title: '已清空', icon: 'success' });
+ this.setData({ list: [], total: 0, hasMore: false });
+ });
+ },
+
+ goToChat() {
+ wx.navigateBack();
+ },
+});
diff --git a/pages/wiki/chat/history/index.json b/pages/wiki/chat/history/index.json
new file mode 100644
index 0000000..3f36a76
--- /dev/null
+++ b/pages/wiki/chat/history/index.json
@@ -0,0 +1,12 @@
+{
+ "navigationBarTitleText": "问答历史",
+ "navigationBarBackgroundColor": "#558B2F",
+ "navigationBarTextStyle": "white",
+ "usingComponents": {
+ "t-icon": "tdesign-miniprogram/icon/icon",
+ "t-loading": "tdesign-miniprogram/loading/loading",
+ "t-empty": "tdesign-miniprogram/empty/empty",
+ "t-dialog": "tdesign-miniprogram/dialog/dialog",
+ "t-swipe-cell": "tdesign-miniprogram/swipe-cell/swipe-cell"
+ }
+}
diff --git a/pages/wiki/chat/history/index.wxml b/pages/wiki/chat/history/index.wxml
new file mode 100644
index 0000000..cdb117f
--- /dev/null
+++ b/pages/wiki/chat/history/index.wxml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+ Q
+ {{item.question}}
+
+
+ A
+ {{item.answerPreview}}
+
+
+
+
+
+
+
+
+
+
+ 📝
+ 暂无问答记录
+ 去和AI助手聊聊吧
+
+ 开始提问
+
+
+
+
+
+
+
+
diff --git a/pages/wiki/chat/history/index.wxss b/pages/wiki/chat/history/index.wxss
new file mode 100644
index 0000000..bcfcea8
--- /dev/null
+++ b/pages/wiki/chat/history/index.wxss
@@ -0,0 +1,195 @@
+/** pages/wiki/chat/history/index.wxss **/
+.history-page {
+ height: 100vh;
+ background: linear-gradient(180deg, #EEF3E5 0%, #F4F6F0 100%);
+}
+
+.history-scroll {
+ height: 100%;
+ padding: 24rpx 28rpx;
+}
+
+.header-bar {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 24rpx;
+ padding: 0 8rpx;
+}
+
+.header-count {
+ font-size: 26rpx;
+ color: #78909C;
+}
+
+.clear-btn {
+ display: flex;
+ align-items: center;
+ gap: 6rpx;
+ font-size: 26rpx;
+ color: #EF4444;
+ font-weight: 600;
+ padding: 8rpx 16rpx;
+ border-radius: 16rpx;
+}
+
+.clear-btn:active {
+ background: rgba(239,68,68,0.08);
+}
+
+/* Card */
+.history-card {
+ background: rgba(255,255,255,0.92);
+ backdrop-filter: blur(8px);
+ border-radius: 24rpx;
+ padding: 24rpx 24rpx 20rpx;
+ margin-bottom: 16rpx;
+ box-shadow: 0 2rpx 12rpx rgba(85,139,47,0.05);
+ border: 1rpx solid rgba(85,139,47,0.04);
+ transition: all 0.15s;
+ animation: cardIn 0.3s ease-out;
+}
+
+@keyframes cardIn {
+ from { opacity: 0; transform: translateY(12rpx); }
+ to { opacity: 1; transform: translateY(0); }
+}
+
+.history-card:active {
+ transform: scale(0.98);
+ background: #FAFDF7;
+}
+
+.card-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 16rpx;
+}
+
+.card-time {
+ font-size: 22rpx;
+ color: #9CA3AF;
+}
+
+.card-del {
+ padding: 8rpx;
+ margin: -8rpx;
+}
+
+.card-question {
+ display: flex;
+ gap: 12rpx;
+ margin-bottom: 12rpx;
+}
+
+.q-label {
+ width: 40rpx;
+ height: 40rpx;
+ border-radius: 10rpx;
+ background: linear-gradient(135deg, #558B2F, #7CB342);
+ color: #fff;
+ font-size: 24rpx;
+ font-weight: 700;
+ text-align: center;
+ line-height: 40rpx;
+ flex-shrink: 0;
+}
+
+.q-text {
+ font-size: 30rpx;
+ font-weight: 600;
+ color: #1F2937;
+ line-height: 1.5;
+ flex: 1;
+}
+
+.card-answer {
+ display: flex;
+ gap: 12rpx;
+}
+
+.a-label {
+ width: 40rpx;
+ height: 40rpx;
+ border-radius: 10rpx;
+ background: #E8F5E9;
+ color: #2E7D32;
+ font-size: 24rpx;
+ font-weight: 700;
+ text-align: center;
+ line-height: 40rpx;
+ flex-shrink: 0;
+}
+
+.a-text {
+ font-size: 26rpx;
+ color: #6B7280;
+ line-height: 1.6;
+ flex: 1;
+ display: -webkit-box;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+}
+
+.card-arrow {
+ display: flex;
+ align-items: center;
+ flex-shrink: 0;
+ margin-left: auto;
+ padding-left: 8rpx;
+}
+
+/* Footer */
+.footer {
+ padding: 32rpx;
+ display: flex;
+ justify-content: center;
+}
+
+.no-more {
+ font-size: 24rpx;
+ color: #CCC;
+}
+
+/* Empty */
+.empty-wrap {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 120rpx 0;
+}
+
+.empty-icon {
+ font-size: 96rpx;
+ margin-bottom: 24rpx;
+}
+
+.empty-text {
+ font-size: 32rpx;
+ font-weight: 600;
+ color: #9CA3AF;
+ margin-bottom: 8rpx;
+}
+
+.empty-sub {
+ font-size: 26rpx;
+ color: #CCC;
+ margin-bottom: 32rpx;
+}
+
+.empty-cta {
+ padding: 16rpx 48rpx;
+ border-radius: 40rpx;
+ background: linear-gradient(135deg, #558B2F, #7CB342);
+ color: #fff;
+ font-size: 28rpx;
+ font-weight: 600;
+ box-shadow: 0 6rpx 20rpx rgba(85,139,47,0.3);
+ transition: all 0.15s;
+}
+
+.empty-cta:active {
+ transform: scale(0.95);
+}
diff --git a/pages/wiki/chat/index.js b/pages/wiki/chat/index.js
new file mode 100644
index 0000000..dab7c83
--- /dev/null
+++ b/pages/wiki/chat/index.js
@@ -0,0 +1,145 @@
+// pages/wiki/chat/index.js
+
+Page({
+ data: {
+ messages: [],
+ inputValue: '',
+ isTyping: false,
+ scrollAnchor: '',
+ _counter: 0,
+ },
+
+ onLoad(options) {
+ if (options && options.fromHistory === '1') {
+ const channel = this.getOpenerEventChannel();
+ channel.on('historyData', (data) => {
+ const msgs = [
+ { id: 'h1', role: 'user', content: data.question },
+ { id: 'h2', role: 'ai', content: this._cleanMd(data.answer) },
+ ];
+ this.setData({ messages: msgs, _counter: 2 }, () => this.scrollToBottom());
+ });
+ } else if (options && options.prefillQuestion) {
+ const q = decodeURIComponent(options.prefillQuestion);
+ this.setData({ inputValue: q }, () => this.onSend());
+ }
+ },
+
+ goToHistory() {
+ wx.navigateTo({ url: '/pages/wiki/chat/history/index' });
+ },
+
+ onQuickAsk(e) {
+ const query = e.currentTarget.dataset.q;
+ this.setData({ inputValue: query }, () => this.onSend());
+ },
+
+ onInput(e) {
+ this.setData({ inputValue: e.detail.value });
+ },
+
+ onSend() {
+ const query = this.data.inputValue.trim();
+ if (!query || this.data.isTyping) return;
+
+ const uid = 'u' + (++this.data._counter);
+ const aid = 'a' + (++this.data._counter);
+ const len = this.data.messages.length;
+
+ // Push user msg + empty AI msg at once
+ this.setData({
+ [`messages[${len}]`]: { id: uid, role: 'user', content: query },
+ [`messages[${len + 1}]`]: { id: aid, role: 'ai', content: '' },
+ inputValue: '',
+ isTyping: true,
+ }, () => {
+ this.scrollToBottom();
+ this._streamRequest(query, aid);
+ });
+ },
+
+ _streamRequest(query, aiMsgId) {
+ const token = wx.getStorageSync('token');
+ const baseUrl = 'http://192.168.0.184:8889';
+ const url = `${baseUrl}/plant/chat/stream?query=${encodeURIComponent(query)}`;
+ let fullText = '';
+
+ const task = wx.request({
+ url,
+ method: 'GET',
+ enableChunked: true,
+ header: {
+ 'Authorization': `Bearer ${token}`,
+ 'Accept': 'text/event-stream',
+ },
+ success: () => {
+ this.setData({ isTyping: false });
+ this.scrollToBottom();
+ },
+ fail: () => {
+ this._updateAiMsg(aiMsgId, '网络连接失败,请稍后重试');
+ this.setData({ isTyping: false });
+ },
+ });
+
+ task.onChunkReceived((res) => {
+ const text = this._decode(res.data);
+ const lines = text.split('\n');
+
+ for (const line of lines) {
+ if (!line.startsWith('data: ')) continue;
+ const chunk = line.substring(6);
+
+ if (chunk === '[DONE]') {
+ this.setData({ isTyping: false });
+ return;
+ }
+ if (chunk.startsWith('[ERROR]')) {
+ fullText += '\n⚠️ ' + (chunk.substring(7) || '服务异常');
+ this._updateAiMsg(aiMsgId, fullText);
+ this.setData({ isTyping: false });
+ return;
+ }
+
+ fullText += chunk;
+ this._updateAiMsg(aiMsgId, fullText);
+ }
+ this.scrollToBottom();
+ });
+ },
+
+ _updateAiMsg(id, content) {
+ const idx = this.data.messages.findIndex(m => m.id === id);
+ if (idx !== -1) {
+ this.setData({ [`messages[${idx}].content`]: this._cleanMd(content) });
+ }
+ },
+
+ // Strip residual markdown symbols for clean display
+ _cleanMd(text) {
+ return text
+ .replace(/^#{1,6}\s*/gm, '') // ### headers
+ .replace(/\*\*(.+?)\*\*/g, '【$1】') // **bold** → 【bold】
+ .replace(/\*(.+?)\*/g, '$1') // *italic*
+ .replace(/^[\-\*]\s+/gm, '· ') // - list → · list
+ .replace(/^\d+\.\s+/gm, (m) => m) // keep numbered lists
+ .replace(/`([^`]+)`/g, '$1') // `code`
+ .replace(/^---+$/gm, '————') // --- → ————
+ .replace(/\n{3,}/g, '\n\n'); // collapse blank lines
+ },
+
+ _decode(buffer) {
+ try {
+ return new TextDecoder('utf-8').decode(new Uint8Array(buffer));
+ } catch (e) {
+ const bytes = new Uint8Array(buffer);
+ let s = '';
+ for (let i = 0; i < bytes.length; i++) s += String.fromCharCode(bytes[i]);
+ try { return decodeURIComponent(escape(s)); } catch (_) { return s; }
+ }
+ },
+
+ scrollToBottom() {
+ this.setData({ scrollAnchor: 'scroll-bottom' });
+ },
+});
diff --git a/pages/wiki/chat/index.json b/pages/wiki/chat/index.json
new file mode 100644
index 0000000..35faae8
--- /dev/null
+++ b/pages/wiki/chat/index.json
@@ -0,0 +1,9 @@
+{
+ "navigationBarTitleText": "植物AI助手",
+ "navigationBarBackgroundColor": "#558B2F",
+ "navigationBarTextStyle": "white",
+ "usingComponents": {
+ "t-icon": "tdesign-miniprogram/icon/icon",
+ "t-loading": "tdesign-miniprogram/loading/loading"
+ }
+}
diff --git a/pages/wiki/chat/index.wxml b/pages/wiki/chat/index.wxml
new file mode 100644
index 0000000..052cd17
--- /dev/null
+++ b/pages/wiki/chat/index.wxml
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+ 🌿
+ 植物AI百科
+ 基于知识库的智能问答助手
+
+
+ 查看问答历史
+
+
+
+ 🌱
+ 龟背竹怎么养护?
+
+
+ 🏠
+ 哪些植物适合室内?
+
+
+ 💧
+ 多肉浇水注意什么?
+
+
+ 🍂
+ 叶子发黄怎么办?
+
+
+
+
+
+
+
+
+
+ 🌱
+
+
+
+
+
+
+
+
+
+
+ 思考中...
+
+ {{item.content}}
+
+ {{item.content}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/wiki/chat/index.wxss b/pages/wiki/chat/index.wxss
new file mode 100644
index 0000000..6f744b4
--- /dev/null
+++ b/pages/wiki/chat/index.wxss
@@ -0,0 +1,265 @@
+/** pages/wiki/chat/index.wxss **/
+.chat-page {
+ height: 100vh;
+ display: flex;
+ flex-direction: column;
+ background: linear-gradient(180deg, #EEF3E5 0%, #F4F6F0 35%, #F4F6F0 100%);
+}
+
+.chat-messages {
+ flex: 1;
+ padding: 20rpx 24rpx;
+ overflow-y: hidden;
+}
+
+/* ── Welcome ── */
+.welcome {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 40rpx 20rpx 0;
+ position: relative;
+}
+
+.welcome-glow {
+ position: absolute;
+ top: -60rpx;
+ width: 480rpx;
+ height: 480rpx;
+ border-radius: 50%;
+ background: radial-gradient(circle, rgba(85,139,47,0.15) 0%, rgba(85,139,47,0.04) 50%, transparent 75%);
+ pointer-events: none;
+ animation: glowPulse 4s ease-in-out infinite;
+}
+
+@keyframes glowPulse {
+ 0%, 100% { transform: scale(1); opacity: 0.8; }
+ 50% { transform: scale(1.08); opacity: 1; }
+}
+
+.welcome-icon { font-size: 88rpx; margin-bottom: 12rpx; position: relative; }
+
+.anim-float { animation: floatUp 3s ease-in-out infinite; }
+
+@keyframes floatUp {
+ 0%, 100% { transform: translateY(0); }
+ 50% { transform: translateY(-12rpx); }
+}
+
+.welcome-title {
+ font-size: 42rpx;
+ font-weight: 800;
+ color: #2E7D32;
+ margin-bottom: 8rpx;
+ letter-spacing: 2rpx;
+}
+
+.welcome-sub {
+ font-size: 24rpx;
+ color: #90A4AE;
+ margin-bottom: 28rpx;
+}
+
+.history-entry {
+ display: flex;
+ align-items: center;
+ gap: 8rpx;
+ padding: 12rpx 24rpx;
+ border-radius: 32rpx;
+ background: rgba(255,255,255,0.85);
+ backdrop-filter: blur(10px);
+ box-shadow: 0 2rpx 12rpx rgba(85,139,47,0.08);
+ font-size: 24rpx;
+ font-weight: 600;
+ color: #558B2F;
+ margin-bottom: 32rpx;
+ transition: all 0.15s;
+}
+
+.history-entry:active {
+ transform: scale(0.96);
+ background: #F0F7EB;
+}
+
+/* Quick Ask Grid */
+.quick-grid {
+ width: 100%;
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 16rpx;
+}
+
+.quick-card {
+ background: rgba(255,255,255,0.9);
+ backdrop-filter: blur(10px);
+ border-radius: 24rpx;
+ padding: 24rpx 20rpx;
+ display: flex;
+ flex-direction: column;
+ gap: 10rpx;
+ box-shadow: 0 2rpx 16rpx rgba(85,139,47,0.06);
+ border: 1rpx solid rgba(85,139,47,0.06);
+ transition: all 0.15s;
+}
+
+.quick-card:active {
+ transform: scale(0.96);
+ background: #F0F7EB;
+ border-color: rgba(85,139,47,0.15);
+}
+
+.qc-emoji { font-size: 40rpx; }
+
+.qc-text {
+ font-size: 25rpx;
+ font-weight: 600;
+ color: #374151;
+ line-height: 1.45;
+}
+
+/* ── Message Row ── */
+.msg-row {
+ display: flex;
+ align-items: flex-start;
+ margin-bottom: 24rpx;
+ gap: 12rpx;
+ animation: fadeSlideIn 0.25s ease-out;
+}
+
+@keyframes fadeSlideIn {
+ from { opacity: 0; transform: translateY(16rpx); }
+ to { opacity: 1; transform: translateY(0); }
+}
+
+.msg-row.user { flex-direction: row-reverse; }
+
+.ai-avatar-wrap {
+ width: 60rpx;
+ height: 60rpx;
+ border-radius: 50%;
+ background: linear-gradient(135deg, #E8F5E9, #C8E6C9);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+ box-shadow: 0 4rpx 12rpx rgba(85,139,47,0.12);
+}
+
+.ai-avatar-emoji { font-size: 32rpx; }
+
+/* ── Bubbles ── */
+.msg-bubble {
+ max-width: 78%;
+ padding: 22rpx 26rpx;
+ border-radius: 24rpx;
+ word-break: break-word;
+}
+
+.msg-bubble.ai {
+ max-width: 88%;
+ background: rgba(255,255,255,0.92);
+ backdrop-filter: blur(8px);
+ border-top-left-radius: 6rpx;
+ box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.04);
+}
+
+.msg-bubble.user {
+ background: linear-gradient(135deg, #558B2F, #7CB342);
+ border-top-right-radius: 6rpx;
+ box-shadow: 0 4rpx 16rpx rgba(85,139,47,0.2);
+}
+
+.msg-text {
+ font-size: 29rpx;
+ line-height: 1.7;
+ color: #fff;
+}
+
+.ai-text {
+ font-size: 29rpx;
+ line-height: 1.85;
+ color: #1F2937;
+ white-space: pre-wrap;
+}
+
+/* ── Typing ── */
+.typing-wrap {
+ display: flex;
+ align-items: center;
+ gap: 12rpx;
+}
+
+.typing-dots {
+ display: flex;
+ gap: 8rpx;
+ align-items: center;
+}
+
+.dot {
+ width: 12rpx;
+ height: 12rpx;
+ border-radius: 50%;
+ background: #A5D6A7;
+ animation: bounce 1.4s infinite ease-in-out;
+}
+
+.dot:nth-child(2) { animation-delay: 0.16s; }
+.dot:nth-child(3) { animation-delay: 0.32s; }
+
+@keyframes bounce {
+ 0%, 80%, 100% { transform: scale(0.5); opacity: 0.4; }
+ 40% { transform: scale(1); opacity: 1; }
+}
+
+.typing-label {
+ font-size: 22rpx;
+ color: #90A4AE;
+ font-weight: 500;
+}
+
+/* ── Input ── */
+.input-area {
+ background: rgba(255,255,255,0.95);
+ backdrop-filter: blur(16px);
+ border-top: 1rpx solid rgba(0,0,0,0.03);
+ padding: 14rpx 24rpx 0;
+}
+
+.input-row {
+ display: flex;
+ align-items: center;
+ gap: 14rpx;
+}
+
+.chat-input {
+ flex: 1;
+ height: 78rpx;
+ background: #F0F4E8;
+ border-radius: 40rpx;
+ padding: 0 28rpx;
+ font-size: 28rpx;
+ color: #1F2937;
+}
+
+.send-btn {
+ width: 74rpx;
+ height: 74rpx;
+ border-radius: 50%;
+ background: #D1D5DB;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+ transition: all 0.25s;
+}
+
+.send-btn.active {
+ background: linear-gradient(135deg, #558B2F, #7CB342);
+ box-shadow: 0 4rpx 20rpx rgba(85,139,47,0.35);
+}
+
+.send-btn:active { transform: scale(0.85); }
+
+.safe-bottom {
+ height: calc(14rpx + env(safe-area-inset-bottom));
+}
diff --git a/pages/wiki/detail/index.js b/pages/wiki/detail/index.js
index b14650c..2f516ab 100644
--- a/pages/wiki/detail/index.js
+++ b/pages/wiki/detail/index.js
@@ -142,5 +142,12 @@ Page({
title: `植物百科 - ${this.data.plant.name}`,
path: `/pages/wiki/detail/index?id=${this.data.plant.id}`
};
+ },
+
+ askAiAboutPlant() {
+ const name = this.data.plant ? this.data.plant.name : '';
+ wx.navigateTo({
+ url: `/pages/wiki/chat/index?prefillQuestion=${encodeURIComponent(name + '怎么养护?')}`
+ });
}
});
diff --git a/pages/wiki/detail/index.wxml b/pages/wiki/detail/index.wxml
index 19fb07d..e46400a 100644
--- a/pages/wiki/detail/index.wxml
+++ b/pages/wiki/detail/index.wxml
@@ -195,9 +195,14 @@
-
+
+
+
+ 🤖
+ 问 AI
+
diff --git a/pages/wiki/detail/index.wxss b/pages/wiki/detail/index.wxss
index 555069e..4ab948c 100644
--- a/pages/wiki/detail/index.wxss
+++ b/pages/wiki/detail/index.wxss
@@ -286,3 +286,27 @@ page {
transform: scale(0.9);
transition: transform 0.1s;
}
+
+/* Ask AI FAB */
+.ask-ai-fab {
+ position: fixed;
+ right: 32rpx;
+ bottom: 48rpx;
+ background: linear-gradient(135deg, rgba(21,101,192,0.92), rgba(25,118,210,0.92));
+ backdrop-filter: blur(12px);
+ color: #fff;
+ padding: 20rpx 32rpx;
+ border-radius: 48rpx;
+ display: flex;
+ align-items: center;
+ gap: 10rpx;
+ font-size: 26rpx;
+ font-weight: 700;
+ box-shadow: 0 8rpx 28rpx rgba(21,101,192,0.3);
+ z-index: 100;
+ transition: all 0.2s;
+}
+
+.ask-ai-fab:active { transform: scale(0.92); }
+
+.fab-emoji { font-size: 32rpx; line-height: 1; }
diff --git a/pages/wiki/index.js b/pages/wiki/index.js
index 72a1629..782acbf 100644
--- a/pages/wiki/index.js
+++ b/pages/wiki/index.js
@@ -21,7 +21,8 @@ Page({
scrollTop: 0,
// Modal State
- showIdentifyModal: false
+ showIdentifyModal: false,
+ isRefreshing: false
},
onLoad() {
@@ -40,6 +41,13 @@ Page({
this.setData({ scrollTop: Math.random() * 0.01 });
},
+ onRefresh() {
+ this.setData({ isRefreshing: true });
+ this.fetchWikiList(true).finally(() => {
+ this.setData({ isRefreshing: false });
+ });
+ },
+
// Fetch categories from API
fetchCategories() {
request.get('/wiki-class/list').then(res => {
@@ -52,8 +60,8 @@ Page({
// Fetch wiki list from API
fetchWikiList(reset = false) {
- if (this.data.isLoading) return;
- if (!reset && !this.data.hasMore) return;
+ if (this.data.isLoading) return Promise.resolve();
+ if (!reset && !this.data.hasMore) return Promise.resolve();
const current = reset ? 1 : this.data.current;
@@ -75,7 +83,7 @@ Page({
params.classId = [this.data.activeCategory];
}
- request.post('/wiki/page', params).then(res => {
+ return request.post('/wiki/page', params).then(res => {
const data = res || {};
const list = data.list || [];
const total = data.total || 0;
@@ -207,6 +215,10 @@ Page({
openIdentifyModal() { this.setData({ showIdentifyModal: true }); },
+ goToAiChat() {
+ wx.navigateTo({ url: '/pages/wiki/chat/index' });
+ },
+
onPopupVisibleChange(e) {
this.setData({
showIdentifyModal: e.detail.visible
diff --git a/pages/wiki/index.wxml b/pages/wiki/index.wxml
index 1701be4..45c6400 100644
--- a/pages/wiki/index.wxml
+++ b/pages/wiki/index.wxml
@@ -17,6 +17,9 @@
enhanced
show-scrollbar="{{false}}"
scroll-top="{{scrollTop}}"
+ refresher-enabled="{{true}}"
+ bindrefresherrefresh="onRefresh"
+ refresher-triggered="{{isRefreshing}}"
>
@@ -111,13 +114,9 @@
-
+
-
-
- 植物识别
-
@@ -147,4 +146,16 @@
+
+
+
+
+ 🤖
+ AI问答
+
+
+
+ 植物识别
+
+
diff --git a/pages/wiki/index.wxss b/pages/wiki/index.wxss
index d7066b7..c88df79 100644
--- a/pages/wiki/index.wxss
+++ b/pages/wiki/index.wxss
@@ -79,11 +79,12 @@
}
.category-item.active {
- background: #558B2F;
+ background: linear-gradient(135deg, #558B2F, #689F38);
color: #fff;
font-weight: 700;
- box-shadow: 0 8rpx 20rpx rgba(85, 139, 47, 0.3);
+ box-shadow: 0 6rpx 20rpx rgba(85, 139, 47, 0.3);
border-color: #558B2F;
+ transform: scale(1.02);
}
.wiki-list {
@@ -179,28 +180,49 @@
font-size: 28rpx;
}
-/* Floating Action Button */
-.floating-add-btn {
+/* Floating Action Buttons */
+.floating-btns {
position: fixed;
- right: 40rpx;
- bottom: 60rpx;
- background: #558B2F;
- color: white;
- padding: 24rpx 40rpx;
- border-radius: 60rpx;
+ bottom: 48rpx;
+ left: 50%;
+ transform: translateX(-50%);
display: flex;
- align-items: center;
- gap: 12rpx;
- box-shadow: 0 12rpx 32rpx rgba(85, 139, 47, 0.4);
- z-index: 1000;
- font-size: 28rpx;
- font-weight: 700;
- transition: all 0.2s ease;
+ flex-direction: row;
+ gap: 24rpx;
+ z-index: 11600;
}
-.floating-add-btn:active {
+.floating-btn {
+ color: white;
+ padding: 18rpx 28rpx;
+ border-radius: 48rpx;
+ display: flex;
+ align-items: center;
+ gap: 8rpx;
+ font-size: 26rpx;
+ font-weight: 700;
+ white-space: nowrap;
+ transition: all 0.2s ease;
+ backdrop-filter: blur(12px);
+}
+
+.floating-btn:active {
transform: scale(0.92);
- box-shadow: 0 4rpx 16rpx rgba(85, 139, 47, 0.2);
+}
+
+.scan-btn {
+ background: rgba(85, 139, 47, 0.92);
+ box-shadow: 0 8rpx 28rpx rgba(85, 139, 47, 0.35);
+}
+
+.chat-btn {
+ background: linear-gradient(135deg, rgba(21,101,192,0.92), rgba(25,118,210,0.92));
+ box-shadow: 0 8rpx 28rpx rgba(21, 101, 192, 0.3);
+}
+
+.btn-emoji {
+ font-size: 32rpx;
+ line-height: 1;
}
/* Popup Styles */
diff --git a/project.private.config.json b/project.private.config.json
index 1a50a2c..e1ffeca 100644
--- a/project.private.config.json
+++ b/project.private.config.json
@@ -3,7 +3,7 @@
"projectname": "plant-mp",
"condition": {},
"setting": {
- "urlCheck": true,
+ "urlCheck": false,
"coverView": true,
"lazyloadPlaceholderEnable": false,
"skylineRenderEnable": false,
diff --git a/utils/request.js b/utils/request.js
index bb6108a..ec4f0e2 100644
--- a/utils/request.js
+++ b/utils/request.js
@@ -270,8 +270,8 @@ class WxRequest {
// Initialize with default instance
const request = new WxRequest({
- //baseUrl: 'http://192.168.0.184:8889',
- baseUrl: 'https://go.sundynix.cn/api',
+ baseUrl: 'http://192.168.0.184:8889',
+ //baseUrl: 'https://go.sundynix.cn/api',
header: {
'Content-Type': 'application/json'
}