diff --git a/app.py b/app.py
index 8061c6c..bb9f406 100644
--- a/app.py
+++ b/app.py
@@ -15,179 +15,11 @@ st.set_page_config(
initial_sidebar_state="expanded",
)
-# ─── Custom CSS ──────────────────────────────────────────────────────────────
+# ─── Minimal CSS ─────────────────────────────────────────────────────────────
st.markdown("""
""", unsafe_allow_html=True)
@@ -300,11 +132,11 @@ def rank_crops(ph, N, P, K, rainfall, temp, pesticide, area):
# ─── Sidebar Inputs ──────────────────────────────────────────────────────────
with st.sidebar:
- st.markdown('
', unsafe_allow_html=True)
- st.markdown('', unsafe_allow_html=True)
- st.markdown("
", unsafe_allow_html=True)
+ st.header("🌾 种植决策助手")
+ st.caption("根据土壤和气候,推荐适宜作物")
+ st.divider()
- st.markdown('', unsafe_allow_html=True)
+ st.subheader("🧪 土壤参数")
col1, col2 = st.columns(2)
with col1:
ph = st.slider("pH 值", 4.0, 9.0, 6.5, 0.1)
@@ -313,18 +145,18 @@ with st.sidebar:
P = st.slider("磷 P (mg/kg)", 0, 100, 45, 5)
K = st.slider("钾 K (mg/kg)", 0, 150, 60, 5)
- st.markdown('', unsafe_allow_html=True)
+ st.subheader("🌦 气象数据")
col3, col4 = st.columns(2)
with col3:
rainfall = st.slider("降雨量 (mm/月)", 0, 400, 120, 10)
with col4:
temp = st.slider("温度 (°C)", 0, 45, 22, 1)
- st.markdown('', unsafe_allow_html=True)
+ st.subheader("🌱 种植参数")
area = st.number_input("种植面积 (公顷)", 0.1, 10000.0, 100.0, 10.0)
pesticide = st.slider("农药用量 (kg/ha)", 0, 200, 50, 5)
- st.markdown('', unsafe_allow_html=True)
+ st.subheader("🎯 目标作物")
selected_crop = st.selectbox(
"选择分析作物",
list(CROPS.keys()),
@@ -339,54 +171,22 @@ best_crop = rankings[0]
# ─── Main Layout ─────────────────────────────────────────────────────────────
-st.markdown(f"""
-
-输入土壤与气象条件,获得作物产量预测与种植建议
-""", unsafe_allow_html=True)
-
-st.markdown("
", unsafe_allow_html=True)
+st.title("🌾 种植决策助手")
+st.caption("输入土壤与气象条件,获得作物产量预测与种植建议")
# KPI row
+overall = np.mean(list(factors.values()))
k1, k2, k3, k4 = st.columns(4)
-with k1:
- st.markdown(f"""
-
-
{yph:,.0f}
-
kg / 公顷
-
{CROPS[selected_crop]['emoji']} {selected_crop} 单产
-
""", unsafe_allow_html=True)
-with k2:
- st.markdown(f"""
-
-
{ytotal/1000:,.1f}
-
吨 / 总产量
-
📦 {area:.0f} 公顷总产
-
""", unsafe_allow_html=True)
-with k3:
- overall = np.mean(list(factors.values()))
- st.markdown(f"""
-
-
{overall*100:.1f}%
-
综合适宜度
-
🎯 环境匹配指数
-
""", unsafe_allow_html=True)
-with k4:
- st.markdown(f"""
-
-
{best_crop['emoji']}
-
{best_crop['crop']} ({best_crop['score']*100:.0f}%)
-
🏆 最优推荐作物
-
""", unsafe_allow_html=True)
-
-st.markdown("
", unsafe_allow_html=True)
+k1.metric(f"{CROPS[selected_crop]['emoji']} {selected_crop} 单产", f"{yph:,.0f} kg/ha")
+k2.metric(f"📦 {area:.0f} 公顷总产", f"{ytotal/1000:,.1f} 吨")
+k3.metric("🎯 环境匹配指数", f"{overall*100:.1f}%")
+k4.metric("🏆 最优推荐作物", f"{best_crop['emoji']} {best_crop['crop']}", f"匹配度 {best_crop['score']*100:.0f}%")
# ─── Charts Row ──────────────────────────────────────────────────────────────
col_left, col_right = st.columns([3, 2])
with col_left:
- st.markdown('', unsafe_allow_html=True)
+ st.subheader("📊 影响因子雷达图")
factor_names = list(factors.keys())
factor_vals = [round(v * 100, 1) for v in factors.values()]
@@ -428,30 +228,17 @@ with col_left:
st.plotly_chart(fig_radar, use_container_width=True)
with col_right:
- st.markdown('', unsafe_allow_html=True)
+ st.subheader("🏅 作物推荐排行")
for i, r in enumerate(rankings[:4]):
rank_icons = ["🥇", "🥈", "🥉", "4️⃣"]
- bar_width = int(r['score'] * 100)
- bar_color = r['color']
- st.markdown(f"""
-
-
-
- {rank_icons[i]}
- {r['emoji']} {r['crop']}
-
-
{r['score']*100:.1f}%
-
-
-
- {r['yield_ha']:,.0f} kg/ha · 总产 {r['total_yield']/1000:,.1f} 吨
-
-
""", unsafe_allow_html=True)
+ with st.container(border=True):
+ c1, c2 = st.columns([3, 1])
+ c1.markdown(f"**{rank_icons[i]} {r['emoji']} {r['crop']}**")
+ c2.markdown(f"{r['score']*100:.1f}%
", unsafe_allow_html=True)
+ st.progress(int(r['score'] * 100), text=f"{r['yield_ha']:,.0f} kg/ha · 总产 {r['total_yield']/1000:,.1f} 吨")
# ─── Sensitivity Analysis ─────────────────────────────────────────────────────
-st.markdown('', unsafe_allow_html=True)
+st.subheader("📈 产量敏感性分析")
sa_col1, sa_col2 = st.columns(2)
@@ -504,7 +291,7 @@ with sa_col2:
st.plotly_chart(fig_R, use_container_width=True)
# ─── All Crops Comparison Bar Chart ───────────────────────────────────────────
-st.markdown('', unsafe_allow_html=True)
+st.subheader("🌐 全作物产量对比")
crop_names = [f"{r['emoji']} {r['crop']}" for r in rankings]
crop_yields = [r['yield_ha'] for r in rankings]
@@ -530,61 +317,42 @@ fig_bar.update_layout(
st.plotly_chart(fig_bar, use_container_width=True)
# ─── Advisory Panel ───────────────────────────────────────────────────────────
-st.markdown('', unsafe_allow_html=True)
+st.subheader("💡 种植建议")
adv1, adv2 = st.columns(2)
with adv1:
crop_opt = CROPS[selected_crop]["optimal"]
- advisories = []
if not (crop_opt["ph"][0] <= ph <= crop_opt["ph"][1]):
- advisories.append(("warn", f"pH {ph} 偏离 {selected_crop} 适宜范围 {crop_opt['ph']},建议{'施石灰' if ph < crop_opt['ph'][0] else '施硫磺'}调节"))
+ st.warning(f"pH {ph} 偏离 {selected_crop} 适宜范围 {crop_opt['ph']},建议{'施石灰' if ph < crop_opt['ph'][0] else '施硫磺'}调节")
else:
- advisories.append(("good", f"土壤 pH {ph} 处于 {selected_crop} 适宜范围内 ✓"))
+ st.success(f"土壤 pH {ph} 处于 {selected_crop} 适宜范围内")
if N < crop_opt["N"][0]:
- advisories.append(("warn", f"氮肥不足({N} vs 建议 {crop_opt['N'][0]}-{crop_opt['N'][1]} mg/kg),建议追施尿素"))
+ st.warning(f"氮肥不足({N} vs 建议 {crop_opt['N'][0]}-{crop_opt['N'][1]} mg/kg),建议追施尿素")
elif N > crop_opt["N"][1]:
- advisories.append(("warn", f"氮肥过量({N} mg/kg),可能造成徒长,建议减施"))
+ st.warning(f"氮肥过量({N} mg/kg),可能造成徒长,建议减施")
else:
- advisories.append(("good", f"氮肥水平 {N} mg/kg 适宜 ✓"))
+ st.success(f"氮肥水平 {N} mg/kg 适宜")
if rainfall < crop_opt["rainfall"][0]:
- advisories.append(("warn", f"降雨量不足,建议增加灌溉(缺水 {crop_opt['rainfall'][0]-rainfall} mm)"))
+ st.warning(f"降雨量不足,建议增加灌溉(缺水 {crop_opt['rainfall'][0]-rainfall} mm)")
elif rainfall > crop_opt["rainfall"][1]:
- advisories.append(("warn", f"降雨量偏多,注意防涝排水"))
+ st.warning(f"降雨量偏多,注意防涝排水")
else:
- advisories.append(("good", f"降雨量 {rainfall}mm 适合 {selected_crop} 生长 ✓"))
-
- for typ, msg in advisories:
- css_class = "alert-good" if typ == "good" else "alert-warn"
- st.markdown(f'{msg}
', unsafe_allow_html=True)
+ st.success(f"降雨量 {rainfall}mm 适合 {selected_crop} 生长")
with adv2:
- st.markdown(f"""
-
-
- 当前环境参数下适宜种植:
-
- """, unsafe_allow_html=True)
- badges = "".join([
- f'
{r["emoji"]} {r["crop"]} {r["score"]*100:.0f}%'
- for r in rankings if r['score'] > 0.6
- ])
- st.markdown(f'{badges}
', unsafe_allow_html=True)
+ suitable = [r for r in rankings if r['score'] > 0.6]
+ if suitable:
+ st.info("当前环境参数下适宜种植:" + "、".join([f"{r['emoji']} {r['crop']} ({r['score']*100:.0f}%)" for r in suitable]))
+ else:
+ st.info("当前环境参数下暂无特别适宜的作物")
- st.markdown(f"""
-
- 最优方案:{best_crop['emoji']} {best_crop['crop']}
- 预期单产:{best_crop['yield_ha']:,.0f} kg/ha
- {area:.0f}公顷总产:{best_crop['total_yield']/1000:,.1f} 吨
-
- """, unsafe_allow_html=True)
+ with st.container(border=True):
+ st.markdown(f"**最优方案:{best_crop['emoji']} {best_crop['crop']}**")
+ st.markdown(f"- 预期单产:**{best_crop['yield_ha']:,.0f} kg/ha**")
+ st.markdown(f"- {area:.0f} 公顷总产:**{best_crop['total_yield']/1000:,.1f} 吨**")
-# ─── Footer ───────────────────────────────────────────────────────────────────
-st.markdown("
", unsafe_allow_html=True)
-st.markdown("""
-
- 种植决策助手 · 基于 Cobb-Douglas 多因子产量模型 · 仅供参考
-
-""", unsafe_allow_html=True)
+st.divider()
+st.caption("种植决策助手 · 基于 Cobb-Douglas 多因子产量模型 · 仅供参考")