Pārlūkot izejas kodu

feat:添加中/英切换功能

wangsisi 2 nedēļas atpakaļ
vecāks
revīzija
1cbbfabb12
72 mainītis faili ar 2394 papildinājumiem un 765 dzēšanām
  1. 3 1
      package.json
  2. 267 0
      scripts/build-flat-en.js
  3. 32 0
      scripts/extract-chinese.js
  4. 78 0
      scripts/wrap-vue-i18n.js
  5. 16 8
      src/App.vue
  6. BIN
      src/assets/img/common/yw.png
  7. 6 6
      src/components/album_compoents/albumCarouselItem.vue
  8. 5 3
      src/components/album_compoents/albumDrawBox.vue
  9. 13 13
      src/components/album_compoents/detailDailog.vue
  10. 4 4
      src/components/chatWindow.vue
  11. 11 11
      src/components/detailDialog.vue
  12. 30 14
      src/components/gardenList.vue
  13. 3 3
      src/components/pageComponents/FarmWorkPlanTimeline.vue
  14. 3 1
      src/components/pageComponents/FnShareSheet.vue
  15. 18 2
      src/components/pageComponents/GrowthStageTimeline.vue
  16. 1 1
      src/components/pageComponents/locationSearch.vue
  17. 8 6
      src/components/popup/UploadProgressPopup.vue
  18. 5 3
      src/components/popup/agriExecutePopup.vue
  19. 5 5
      src/components/popup/confirmDrawTypePopup.vue
  20. 8 8
      src/components/popup/droneConsultPopup.vue
  21. 18 18
      src/components/popup/farmInfoPopup.vue
  22. 13 11
      src/components/popup/interactPopup.vue
  23. 5 3
      src/components/popup/reviewUploadPopup.vue
  24. 4 2
      src/components/popup/selectRegionPopup.vue
  25. 6 4
      src/components/popup/startInteractPopup.vue
  26. 6 4
      src/components/popup/uploadTips.vue
  27. 16 14
      src/components/recordItem.vue
  28. 1 1
      src/components/upload.vue
  29. 15 13
      src/components/weatherInfo.vue
  30. 486 0
      src/i18n/flat-en.js
  31. 69 0
      src/i18n/index.js
  32. 298 0
      src/i18n/messages.js
  33. 162 0
      src/i18n/recordDetails-messages.js
  34. 15 0
      src/i18n/recordTextMap.js
  35. 2 0
      src/main.js
  36. 38 12
      src/plugins/axios.js
  37. 10 0
      src/plugins/i18n.js
  38. 31 0
      src/store/modules/locale/index.js
  39. 3 1
      src/utils/back/DialogModel1.vue
  40. 17 15
      src/views/old_mini/achievement_report/index.vue
  41. 58 108
      src/views/old_mini/agri_file/components/fileFloat.vue
  42. 15 13
      src/views/old_mini/agri_file/index.vue
  43. 3 1
      src/views/old_mini/agri_record/components/mapInfo.vue
  44. 48 10
      src/views/old_mini/agri_record/index.vue
  45. 12 10
      src/views/old_mini/agri_record/subPages/prescriptionPage.vue
  46. 37 35
      src/views/old_mini/agri_record/subPages/statusDetail.vue
  47. 5 5
      src/views/old_mini/chat_frame/consult.vue
  48. 10 8
      src/views/old_mini/create_farm/editMap.vue
  49. 18 16
      src/views/old_mini/create_farm/index.vue
  50. 8 8
      src/views/old_mini/create_farm/selectCrop.vue
  51. 10 8
      src/views/old_mini/growth_report/adjustPopup.vue
  52. 4 2
      src/views/old_mini/growth_report/historyRiskReport.vue
  53. 74 42
      src/views/old_mini/growth_report/index.vue
  54. 11 11
      src/views/old_mini/home/components/farmInfoPopup.vue
  55. 11 11
      src/views/old_mini/interaction/index.vue
  56. 4 2
      src/views/old_mini/interactionList/components/morePopup.vue
  57. 7 5
      src/views/old_mini/interactionList/confirmArea.vue
  58. 11 9
      src/views/old_mini/interactionList/drawRegion.vue
  59. 7 5
      src/views/old_mini/interactionList/drawRegion1.vue
  60. 23 23
      src/views/old_mini/interactionList/index.vue
  61. 6 6
      src/views/old_mini/monitor/index.vue
  62. 11 9
      src/views/old_mini/monitor/subPages/agriculturalDetail.vue
  63. 5 5
      src/views/old_mini/monitor/subPages/darwArea.vue
  64. 25 23
      src/views/old_mini/monitor/subPages/farmInfo.vue
  65. 10 10
      src/views/old_mini/monitor/subPages/plan.vue
  66. 6 6
      src/views/old_mini/monitor/subPages/reviewResults.vue
  67. 4 4
      src/views/old_mini/monitor/subPages/soilPopup.vue
  68. 165 132
      src/views/old_mini/recordDetails/index.vue
  69. 11 11
      src/views/old_mini/recordDetails/mapManage.vue
  70. 16 14
      src/views/old_mini/work_detail/components/executePopup.vue
  71. 3 1
      src/views/old_mini/work_detail/components/mapInfo.vue
  72. 25 25
      src/views/old_mini/work_detail/index.vue

+ 3 - 1
package.json

@@ -8,7 +8,9 @@
     "servepro": "vue-cli-service serve --mode pro",
     "build": "vue-cli-service build --mode pro",
     "build2": "vue-cli-service build --mode pro",
-    "lint": "vue-cli-service lint"
+    "lint": "vue-cli-service lint",
+    "i18n:build": "node scripts/build-flat-en.js",
+    "i18n:wrap": "node scripts/wrap-vue-i18n.js"
   },
   "dependencies": {
     "@element-plus/icons-vue": "^2.0.6",

+ 267 - 0
scripts/build-flat-en.js

@@ -0,0 +1,267 @@
+const fs = require("fs");
+const path = require("path");
+
+const SRC = path.join(__dirname, "../src");
+const OUT = path.join(__dirname, "../src/i18n/flat-en.js");
+const SKIP = /copy\.vue$|weatherInfo copy|flat-en\.js|messages\.js/i;
+const CHINESE_RE = /[\u4e00-\u9fff][\u4e00-\u9fff\d,。、?!:;""''()【】\-\s%%·]*/g;
+
+const messagesPath = path.join(__dirname, "../src/i18n/messages.js");
+const messages = new Function(`${fs.readFileSync(messagesPath, "utf8").replace("export default", "return ")}`)();
+
+const PHRASES = [
+    ["长势报告", "Growth Report"],
+    ["农情档案", "Crop Archives"],
+    ["农事规划", "Farm Planning"],
+    ["有味溯源", "Traceability"],
+    ["荔枝长势报告", "Lychee Growth Report"],
+    ["作物长势报告", "Crop Growth Report"],
+    ["气象风险", "Weather Risks"],
+    ["农事建议", "Farming Advice"],
+    ["巡园重点", "Patrol Priorities"],
+    ["未来7-15天气象风险", "7–15 Day Weather Risks"],
+    ["未来七天天气", "7-Day Forecast"],
+    ["农场列表", "Farm List"],
+    ["示范农场", "Demo Farm"],
+    ["专属数字农场,种好卖好", "Your Digital Farm — Grow Better, Sell Better"],
+    ["点击解锁一键溯源增产", "Tap to Unlock Traceability & Yield Boost"],
+    ["您的农场已绑定成功", "Farm Linked Successfully"],
+    ["点击展开更多", "Show More"],
+    ["点击收起", "Collapse"],
+    ["暂无地址", "No Address"],
+    ["干旱预警", "Drought Alert"],
+    ["设为默认农场", "Set as Default"],
+    ["新增农场", "Add Farm"],
+    ["当前农场", "Current Farm"],
+    ["农场信息维护", "Farm Profile"],
+    ["查看全部农场列表,并可创建农场", "View All Farms and Create New Ones"],
+    ["背景描述:", "Background: "],
+    ["对策建议:", "Recommendations: "],
+    ["点击解锁", "Unlock"],
+    ["请求错误", "Bad Request"],
+    ["未授权,请登录", "Unauthorized. Please log in"],
+    ["没有权限,拒绝访问", "Forbidden"],
+    ["请求地址出错", "Invalid URL"],
+    ["请求超时", "Request Timeout"],
+    ["服务器内部错误", "Internal Server Error"],
+    ["服务未实现", "Not Implemented"],
+    ["网关错误", "Bad Gateway"],
+    ["服务不可用", "Service Unavailable"],
+    ["网关超时", "Gateway Timeout"],
+    ["HTTP版本不受支持", "HTTP Version Not Supported"],
+    ["农事详情", "Farm Work Details"],
+    ["农事凭证", "Farm Work Certificate"],
+    ["执行区域", "Execution Area"],
+    ["已认证", "Verified"],
+    ["农事目的", "Purpose"],
+    ["注意事项", "Notes"],
+    ["药物处方", "Prescription"],
+    ["执行方式", "Execution Method"],
+    ["异常记录", "Exception Log"],
+    ["转发给执行人员", "Forward to Executor"],
+    ["确认上传", "Confirm Upload"],
+    ["确认信息", "Confirm"],
+    ["转发记录", "Forward Record"],
+    ["物候不整齐?", "Uneven Phenology?"],
+    ["发现异常,拍照记录", "Report Issue with Photo"],
+    ["当前现状:", "Current Status:"],
+    ["点击上传照片", "Upload Photo"],
+    ["点击勾画新发生区域", "Draw New Area"],
+    ["暂未到达进程", "Stage Not Reached"],
+    ["邀请勾画", "Invite to Draw"],
+    ["区域名称", "Area Name"],
+    ["物候时间", "Phenology Period"],
+    ["多选", "Multi-select"],
+    ["调整pH值", "Adjust pH"],
+    ["物候期时间设置", "Phenology Schedule"],
+    ["起始时间", "Start Time"],
+    ["请从上往下按照时间顺序填写日期", "Enter dates in chronological order from top to bottom"],
+    ["确认设置", "Confirm"],
+    ["上传中", "Uploading"],
+    ["上传失败", "Upload Failed"],
+    ["上传成功", "Upload Successful"],
+    ["加载中", "Loading"],
+    ["暂无数据", "No Data"],
+    ["请稍候", "Please Wait"],
+    ["操作成功", "Success"],
+    ["操作失败", "Failed"],
+    ["网络错误", "Network Error"],
+    ["请求失败", "Request Failed"],
+    ["登录", "Log In"],
+    ["退出登录", "Log Out"],
+    ["确定", "OK"],
+    ["取消", "Cancel"],
+    ["确认", "Confirm"],
+    ["提交", "Submit"],
+    ["保存", "Save"],
+    ["删除", "Delete"],
+    ["编辑", "Edit"],
+    ["返回", "Back"],
+    ["搜索", "Search"],
+    ["搜索农场", "Search Farms"],
+    ["选择地区", "Select Region"],
+    ["选择品类", "Select Category"],
+    ["请选择", "Please Select"],
+    ["请输入", "Please Enter"],
+    ["展开更多", "Expand"],
+    ["收起", "Collapse"],
+    ["详情", "Details"],
+    ["查看", "View"],
+    ["关闭", "Close"],
+    ["下一步", "Next"],
+    ["上一步", "Previous"],
+    ["完成", "Done"],
+    ["全部", "All"],
+    ["更多", "More"],
+    ["首页", "Home"],
+    ["个人中心", "Profile"],
+    ["农事服务", "Farm Services"],
+    ["用户管理", "User Management"],
+    ["历史风险报告", "Historical Risk Reports"],
+    ["校准物候期", "Calibrate Phenology"],
+    ["进程互动", "Progress Check"],
+    ["长势互动", "Growth Check"],
+    ["病虫互动", "Pest Check"],
+    ["病虫风险", "Pest & Disease Risk"],
+    ["阴雨寡照风险", "Rain & Low-Light Risk"],
+    ["根外追肥", "Foliar Feeding"],
+    ["虫害防治", "Pest Control"],
+    ["种植档案管理", "Planting Records"],
+    ["邀请关注", "Invite to Follow"],
+];
+
+const WORDS = {
+    请: "Please ",
+    选择: "Select ",
+    输入: "Enter ",
+    暂无: "No ",
+    当前: "Current ",
+    未来: "Future ",
+    查看: "View ",
+    点击: "Tap ",
+    上传: "Upload ",
+    下载: "Download ",
+    搜索: "Search ",
+    筛选: "Filter ",
+    排序: "Sort ",
+    新增: "Add ",
+    编辑: "Edit ",
+    删除: "Delete ",
+    保存: "Save ",
+    提交: "Submit ",
+    确认: "Confirm ",
+    取消: "Cancel ",
+    返回: "Back ",
+    关闭: "Close ",
+    展开: "Expand ",
+    收起: "Collapse ",
+    成功: " Success",
+    失败: " Failed",
+    错误: " Error",
+    警告: " Warning",
+    提示: " Notice",
+    农场: "Farm",
+    果园: "Orchard",
+    地块: "Plot",
+    品种: "Variety",
+    作物: "Crop",
+    荔枝: "Lychee",
+    水稻: "Rice",
+    农事: "Farm Work",
+    天气: "Weather",
+    风险: " Risk",
+    报告: " Report",
+    档案: " Archives",
+    规划: " Planning",
+    溯源: " Traceability",
+    互动: " Interaction",
+    巡园: "Patrol ",
+    物候期: "Phenological Stage",
+    生育期: "Reproductive Stage",
+    膨果期: "Fruit Expansion Stage",
+    晴天: "Sunny",
+    周日: "Sun",
+    周一: "Mon",
+    周二: "Tue",
+    周三: "Wed",
+    周四: "Thu",
+    周五: "Fri",
+    周六: "Sat",
+};
+
+function pairZhEn(zhTree, enTree, out = {}) {
+    Object.keys(zhTree || {}).forEach((k) => {
+        if (zhTree[k] && typeof zhTree[k] === "object" && !Array.isArray(zhTree[k])) {
+            pairZhEn(zhTree[k], (enTree || {})[k], out);
+        } else if (typeof zhTree[k] === "string" && typeof (enTree || {})[k] === "string") {
+            out[zhTree[k]] = enTree[k];
+        }
+    });
+    return out;
+}
+
+function extractTemplateChinese(filePath) {
+    const content = fs.readFileSync(filePath, "utf8");
+    const templateMatch = content.match(/<template>([\s\S]*?)<\/template>/);
+    if (!templateMatch) return [];
+    return (templateMatch[1].match(CHINESE_RE) || [])
+        .map((s) => s.trim().replace(/\s+/g, " "))
+        .filter((s) => s.length >= 2 && s.length <= 120 && /[\u4e00-\u9fff]/.test(s));
+}
+
+function walk(dir, files = []) {
+    for (const name of fs.readdirSync(dir)) {
+        const p = path.join(dir, name);
+        if (fs.statSync(p).isDirectory()) {
+            if (name !== "node_modules") walk(p, files);
+        } else if (name.endsWith(".vue") && !SKIP.test(p)) files.push(p);
+    }
+    return files;
+}
+
+function autoTranslate(zh, cache) {
+    if (cache[zh]) return cache[zh];
+    for (const [key, val] of PHRASES) {
+        if (zh === key || zh.includes(key)) {
+            cache[zh] = zh.split(key).join(val);
+            if (cache[zh] !== zh) return cache[zh];
+        }
+    }
+    let result = zh;
+    const keys = Object.keys(WORDS).sort((a, b) => b.length - a.length);
+    keys.forEach((w) => {
+        result = result.split(w).join(WORDS[w]);
+    });
+    result = result.replace(/\s+/g, " ").trim();
+    if (/[\u4e00-\u9fff]/.test(result)) {
+        result = `[${result}]`;
+    }
+    cache[zh] = result;
+    return result;
+}
+
+const map = pairZhEn(messages.zh, messages.en);
+const cache = { ...map };
+
+for (const file of walk(SRC)) {
+    extractTemplateChinese(file).forEach((s) => {
+        if (!map[s]) map[s] = null;
+    });
+}
+
+Object.keys(map).forEach((zh) => {
+    if (!map[zh] || /[\u4e00-\u9fff]/.test(map[zh])) {
+        map[zh] = autoTranslate(zh, cache);
+    }
+});
+
+const esc = (s) => s.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
+const lines = ["export default {"];
+Object.keys(map)
+    .sort()
+    .forEach((zh) => {
+        lines.push(`  '${esc(zh)}': '${esc(map[zh])}',`);
+    });
+lines.push("};");
+fs.writeFileSync(OUT, lines.join("\n"));
+console.log("Wrote", Object.keys(map).length, "entries");

+ 32 - 0
scripts/extract-chinese.js

@@ -0,0 +1,32 @@
+const fs = require("fs");
+const path = require("path");
+
+const SRC = path.join(__dirname, "../src");
+const SKIP = /copy\.vue$|weatherInfo copy/i;
+const CHINESE_RE = /[\u4e00-\u9fff][\u4e00-\u9fff\d,。、?!:;""''()【】\-\s%%·]*/g;
+
+function walk(dir, files = []) {
+    for (const name of fs.readdirSync(dir)) {
+        const p = path.join(dir, name);
+        const stat = fs.statSync(p);
+        if (stat.isDirectory()) {
+            if (name !== "node_modules") walk(p, files);
+        } else if (/\.(vue|js)$/.test(name) && !SKIP.test(p)) {
+            files.push(p);
+        }
+    }
+    return files;
+}
+
+const set = new Set();
+for (const file of walk(SRC)) {
+    const content = fs.readFileSync(file, "utf8");
+    const matches = content.match(CHINESE_RE) || [];
+    matches.forEach((m) => {
+        const s = m.trim();
+        if (s.length >= 2 && /[\u4e00-\u9fff]/.test(s)) set.add(s);
+    });
+}
+
+const list = [...set].sort((a, b) => b.length - a.length);
+console.log(JSON.stringify(list, null, 2));

+ 78 - 0
scripts/wrap-vue-i18n.js

@@ -0,0 +1,78 @@
+const fs = require("fs");
+const path = require("path");
+
+const ROOT = path.join(__dirname, "../src");
+const SKIP = /copy\.vue$|weatherInfo copy/i;
+const TEXT_IN_TAG = />([^<>{}\n]+)</g;
+const ATTR_RE = /(?<![:\w$])(text|title|label|name|placeholder|message|confirm-button-text|cancel-button-text)=["']([^"']*[\u4e00-\u9fff][^"']*)["']/g;
+
+function walk(dir, files = []) {
+    for (const name of fs.readdirSync(dir)) {
+        const p = path.join(dir, name);
+        if (fs.statSync(p).isDirectory()) {
+            if (!["node_modules"].includes(name)) walk(p, files);
+        } else if (name.endsWith(".vue") && !SKIP.test(p)) files.push(p);
+    }
+    return files;
+}
+
+function hasChinese(s) {
+    return /[\u4e00-\u9fff]/.test(s);
+}
+
+function escapeStr(s) {
+    return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
+}
+
+function alreadyWrapped(text) {
+    return text.includes("{{") || text.includes("$t(") || text.includes("t(");
+}
+
+function wrapTemplate(content) {
+    let changed = false;
+    let out = content.replace(TEXT_IN_TAG, (full, text) => {
+        const trimmed = text.trim();
+        if (!trimmed || !hasChinese(trimmed) || alreadyWrapped(trimmed)) return full;
+        if (/^[\s\d.,:;+\-/%]+$/.test(trimmed)) return full;
+        changed = true;
+        return `>{{ $t('${escapeStr(trimmed)}') }}<`;
+    });
+
+    out = out.replace(ATTR_RE, (full, attr, val) => {
+        changed = true;
+        return `:${attr}="$t('${escapeStr(val)}')"`;
+    });
+
+    return { out, changed };
+}
+
+function processVueFile(content) {
+    const open = content.indexOf("<template>");
+    const close = content.lastIndexOf("</template>");
+    if (open === -1 || close === -1 || close <= open) {
+        return { content, changed: false };
+    }
+
+    const before = content.slice(0, open + "<template>".length);
+    const templateBody = content.slice(open + "<template>".length, close);
+    const after = content.slice(close);
+
+    const { out, changed } = wrapTemplate(templateBody);
+    if (!changed) return { content, changed: false };
+    return { content: before + out + after, changed: true };
+}
+
+let total = 0;
+for (const file of walk(ROOT)) {
+    const original = fs.readFileSync(file, "utf8");
+    const { content, changed } = processVueFile(original);
+    if (!changed) continue;
+    if (content.length < original.length * 0.7) {
+        console.warn("SKIP (suspicious shrink):", path.relative(ROOT, file));
+        continue;
+    }
+    fs.writeFileSync(file, content);
+    total++;
+    console.log("Updated:", path.relative(ROOT, file));
+}
+console.log("Done. Updated", total, "files");

+ 16 - 8
src/App.vue

@@ -12,6 +12,7 @@
     <component :is="Component" />
   </keep-alive>
 </router-view> -->
+    <el-config-provider :locale="elementLocale">
     <router-view v-slot="{ Component }">
         <keep-alive v-if="route.meta && route.meta.keepAlive">
             <component :is="Component" />
@@ -25,7 +26,7 @@
         <!-- 托管农户:首页、农情档案、农事记录 -->
         <!-- <template v-if="userType == 2"> -->
             <tabbar-item replace to="/growth_report">
-                <span>长势报告</span>
+                <span>{{ t("tabbar.growthReport") }}</span>
                 <template #icon="props">
                     <img
                         :src="
@@ -37,7 +38,7 @@
                 </template>
             </tabbar-item>
             <tabbar-item replace to="/agri_file">
-                <span>农情档案</span>
+                <span>{{ t("tabbar.agriFile") }}</span>
                 <template #icon="props">
                     <img
                         :src="
@@ -49,7 +50,7 @@
                 </template>
             </tabbar-item>
             <tabbar-item replace to="/agri_record">
-                <span>农事规划</span>
+                <span>{{ t("tabbar.agriRecord") }}</span>
                 <template #icon="props">
                     <img
                         :src="
@@ -61,7 +62,7 @@
                 </template>
             </tabbar-item>
             <tabbar-item replace to="/youwei_trace">
-                <span>有味溯源</span>
+                <span>{{ t("tabbar.trace") }}</span>
                 <template #icon="props">
                     <img
                         :src="
@@ -77,7 +78,7 @@
         <!-- 普通农户:首页、农事服务、用户管理、个人中心(保留原逻辑) -->
         <!-- <template v-else-if="userType == 1">
             <tabbar-item replace to="/home" v-if="curRole == 0 || curRole == 2">
-                <span>首页</span>
+                <span>{{ $t('首页') }}</span>
                 <template #icon="props">
                     <img
                         :src="
@@ -89,7 +90,7 @@
                 </template>
             </tabbar-item>
             <tabbar-item replace to="/task_condition">
-                <span>农事服务</span>
+                <span>{{ $t('农事服务') }}</span>
                 <template #icon="props">
                     <img
                         :src="
@@ -101,7 +102,7 @@
                 </template>
             </tabbar-item>
             <tabbar-item replace to="/user">
-                <span>用户管理</span>
+                <span>{{ $t('用户管理') }}</span>
                 <template #icon="props">
                     <img
                         :src="
@@ -113,7 +114,7 @@
                 </template>
             </tabbar-item>
             <tabbar-item replace to="/mine">
-                <span>个人中心</span>
+                <span>{{ $t('个人中心') }}</span>
                 <template #icon="props">
                     <img
                         :src="
@@ -129,6 +130,7 @@
 
     <!-- 开启底部安全区适配 -->
     <number-keyboard safe-area-inset-bottom />
+    </el-config-provider>
 </template>
 
 <script setup>
@@ -138,7 +140,13 @@ import { nextTick, watch, onMounted, ref, computed } from "vue";
 import { useRoute, useRouter } from "vue-router";
 import { useStore } from "vuex";
 import { NH, NZ, EXPERT } from "@/common/user_role";
+import zhCn from "element-plus/es/locale/lang/zh-cn";
+import en from "element-plus/es/locale/lang/en";
+import { useI18n } from "@/i18n";
+
+const { t, locale } = useI18n();
 const store = useStore();
+const elementLocale = computed(() => (locale.value === "en" ? en : zhCn));
 const route = useRoute();
 const router = useRouter();
 

BIN
src/assets/img/common/yw.png


+ 6 - 6
src/components/album_compoents/albumCarouselItem.vue

@@ -126,7 +126,7 @@
                     </div>
                     <div class="carousel-img-mask" v-if="isAchievementImgs && imgData?.reCheckText">
                         <div class="mask-content">
-                            <div class="review-effect-text">复核成效</div>
+                            <div class="review-effect-text">{{ $t('复核成效') }}</div>
                             <div class="review-effect-content">
                                 <span class="review-effect-content-text">{{ imgData?.reCheckText }}</span>
                             </div>
@@ -170,7 +170,7 @@
                 </div>
                 <div class="carousel-img-mask" v-if="isAchievementImgs && imgData?.reCheckText">
                     <div class="mask-content">
-                        <div class="review-effect-text">复核成效</div>
+                        <div class="review-effect-text">{{ $t('复核成效') }}</div>
                         <div class="review-effect-content">
                             <span class="review-effect-content-text">{{ imgData?.reCheckText }}</span>
                         </div>
@@ -186,21 +186,21 @@
             <!-- 底部操作按钮 -->
             <div class="bottom-actions" @click.stop="showPopup = false">
                 <div class="action-buttons">
-                    <div class="action-btn text-btn">&lt;&lt;长按图片保存或转发&gt;&gt;</div>
+                    <div class="action-btn text-btn">{{ $t('&lt;&lt;长按图片保存或转发&gt;&gt;') }}</div>
                     <!-- <div class="action-btn green-btn" @click.stop="handleWechat">
                         <div class="icon-circle">
                             <img src="@/assets/img/home/wechat.png" alt="" />
                         </div>
-                        <span class="btn-label">微信</span>
+                        <span class="btn-label">{{ $t('微信') }}</span>
                     </div>
                     <div class="action-btn orange-btn" @click.stop="handleSaveImage">
                         <div class="icon-circle">
                             <el-icon :size="24"><Download /></el-icon>
                         </div>
-                        <span class="btn-label">保存图片</span>
+                        <span class="btn-label">{{ $t('保存图片') }}</span>
                     </div> -->
                 </div>
-                <div class="cancel-btn" @click="handleCancel">取消</div>
+                <div class="cancel-btn" @click="handleCancel">{{ $t('取消') }}</div>
             </div>
         </popup>
     </div>

+ 5 - 3
src/components/album_compoents/albumDrawBox.vue

@@ -32,21 +32,23 @@
                     <div class="icon-circle">
                         <img src="@/assets/img/home/wechat.png" alt="" />
                     </div>
-                    <span class="btn-label">微信</span>
+                    <span class="btn-label">{{ t('微信') }}</span>
                 </div>
                 <div class="action-btn orange-btn" @click.stop="handleSaveImage">
                     <div class="icon-circle">
                         <el-icon :size="24"><Download /></el-icon>
                     </div>
-                    <span class="btn-label">保存图片</span>
+                    <span class="btn-label">{{ t('保存图片') }}</span>
                 </div> -->
             </div>
-            <div class="cancel-btn" @click="handleCancel">取消</div>
+            <div class="cancel-btn" @click="handleCancel">{{ t('取消') }}</div>
         </div>
     </popup>
 </template>
 
 <script setup>
+import { useI18n } from "@/i18n";
+const { t } = useI18n();
 import { Popup } from "vant";
 import { ref, onMounted, onBeforeUnmount, defineProps } from "vue";
 import { base_img_url2 } from "@/api/config";

+ 13 - 13
src/components/album_compoents/detailDailog.vue

@@ -10,37 +10,37 @@
                         <div class="detail-title">{{ dialogData.farmWorkName }}</div>
                         <div class="detail-desc-box">
                             <!-- <div class="desc-item" v-if="dialogData?.conditionList && dialogData.conditionList.length">
-                                <span class="item-name">触发条件</span>
+                                <span class="item-name">{{ $t('触发条件') }}</span>
                                 {{ dialogData.condition || dialogData.conditionList[0].name + dialogData.conditionList[0].value }}
                             </div> -->
                             <div class="desc-item">
-                                <span class="item-name">农事编号</span>
+                                <span class="item-name">{{ $t('农事编号') }}</span>
                                 {{ dialogData.code }}
                             </div>
                             <div class="desc-item">
-                                <span class="item-name">推荐时间</span>
+                                <span class="item-name">{{ $t('推荐时间') }}</span>
                                 {{ dialogData.executeDate }}
                             </div>
                             <!-- <div class="desc-item">
-                                <span class="item-name">农事宗旨</span>
+                                <span class="item-name">{{ $t('农事宗旨') }}</span>
                                 {{ dialogData.purpose || dialogData.condition }}
                             </div> -->
                             <div class="desc-item">
-                                <div class="item-name">药物处方</div>
+                                <div class="item-name">{{ $t('药物处方') }}</div>
                                 <div class="item-table">
                                     <el-table :data="pesticideFertilizers" style="width: 100%" :header-cell-style="{background: '#F5F5F5'}">
-                                        <el-table-column prop="pesticideFertilizerCode" label="类型" width="62">
+                                        <el-table-column prop="pesticideFertilizerCode" :label="$t('类型')" width="62">
                                             <template #default="scope">
                                                 {{scope.row.typeName}}
                                             </template>
                                         </el-table-column>
-                                        <el-table-column prop="defaultName" label="名称" />
-                                        <!-- <el-table-column prop="ratio" label="配比" width="50">
+                                        <el-table-column prop="defaultName" :label="$t('名称')" />
+                                        <!-- <el-table-column prop="ratio" :label="$t('配比')" width="50">
                                             <template #default="scope">
                                                 {{scope.row.ratio ? scope.row.ratio : "--"}}
                                             </template>
                                         </el-table-column> -->
-                                        <el-table-column prop="ratio" label="方式" width="62">
+                                        <el-table-column prop="ratio" :label="$t('方式')" width="62">
                                             <template #default="scope">
                                                 {{scope.row.executeStyle ? (scope.row.executeStyle === 2 ? "无人机" : "人工") : "--"}}
                                             </template>
@@ -62,16 +62,16 @@
 
             <div class="close-btn" v-show="dialogData.isBtn" @click.stop="winDialogVisible=false">
                     <!-- <el-icon size="32" color="#fff"><CircleCloseFilled /></el-icon> -->
-                    <el-button v-show="dialogData.typeStr === 'export'" type="primary" class="one-btn" @click.stop="toPage"> 邀请专家诊断 </el-button>
-                    <el-button v-show="dialogData.typeStr === 'activate'" type="primary" class="one-btn" @click.stop="handleAct"> 立即激活专家农事 </el-button>
-                    <!-- <el-button type="primary" class="one-btn" @click.stop="toShare"> 立即分享 </el-button> -->
+                    <el-button v-show="dialogData.typeStr === 'export'" type="primary" class="one-btn" @click.stop="toPage">{{ $t('邀请专家诊断') }}</el-button>
+                    <el-button v-show="dialogData.typeStr === 'activate'" type="primary" class="one-btn" @click.stop="handleAct">{{ $t('立即激活专家农事') }}</el-button>
+                    <!-- <el-button type="primary" class="one-btn" @click.stop="toShare">{{ $t('立即分享') }}</el-button> -->
             </div>
         </div>
         <template #footer>
             <div class="dialog-footer">
                 <div class="close-btn">
                     <!-- <el-icon size="32" color="#fff"><CircleCloseFilled /></el-icon> -->
-                    <!-- <el-button type="primary" class="one-btn" @click="toShare"> 立即分享 </el-button> -->
+                    <!-- <el-button type="primary" class="one-btn" @click="toShare">{{ $t('立即分享') }}</el-button> -->
                 </div>
             </div>
         </template>

+ 4 - 4
src/components/chatWindow.vue

@@ -51,7 +51,7 @@
                          <!-- 对话样式消息 -->
                         <div v-if="msg.messageType === 'card'" class="card-message" @click="handleCardClick(msg)">
                             <template v-if="(msg.cardType || msg.content.cardType) === 'quotation'">
-                                <div class="card-title">向您发送了一张 服务报价单</div>
+                                <div class="card-title">{{ $t('向您发送了一张 服务报价单') }}</div>
                                 <img src="https://birdseye-img.sysuimars.com/temp/price.png" alt="" />
                             </template>
                             <template v-else>
@@ -102,7 +102,7 @@
                         <!-- 对话样式消息 -->
                         <div v-if="msg.messageType === 'card'" class="card-message" @click="handleCardClick(msg)">
                             <template v-if="(msg.cardType || msg.content.cardType) === 'quotation'">
-                                <div class="card-title">向您发送了一张 服务报价单</div>
+                                <div class="card-title">{{ $t('向您发送了一张 服务报价单') }}</div>
                                 <img src="https://birdseye-img.sysuimars.com/temp/price.png" alt="" />
                             </template>
                             <template v-else>
@@ -158,8 +158,8 @@
                 <input type="file" ref="fileInput" accept="image/*" style="display: none" @change="handleImageUpload" />
             </div>
 
-            <input type="text" v-model="inputMessage" placeholder="请输入你想说的话~" @keyup.enter="sendTextMessage" />
-            <div class="send" @click="sendTextMessage">发送</div>
+            <input type="text" v-model="inputMessage" :placeholder="$t('请输入你想说的话~')" @keyup.enter="sendTextMessage" />
+            <div class="send" @click="sendTextMessage">{{ $t('发送') }}</div>
 
             <!-- 录音指示器 -->
             <div v-if="isRecording" class="recording-indicator">

+ 11 - 11
src/components/detailDialog.vue

@@ -18,31 +18,31 @@
                         <div class="detail-title">{{ dialogData.name }}</div>
                         <div class="detail-desc-box">
                             <!-- <div class="desc-item van-ellipsis" v-if="dialogData?.conditionList && dialogData.conditionList.length">
-                                <span class="item-name">触发条件</span>
+                                <span class="item-name">{{ $t('触发条件') }}</span>
                                 {{
                                     dialogData.condition ||
                                     dialogData.conditionList[0].name + dialogData.conditionList[0].value
                                 }}
                             </div> -->
                             <div class="desc-item van-ellipsis">
-                                <span class="item-name">农事编号</span>
+                                <span class="item-name">{{ $t('农事编号') }}</span>
                                 {{ dialogData.code }}
                             </div>
                             <div class="desc-item">
-                                <span class="item-name">推荐时间</span>
+                                <span class="item-name">{{ $t('推荐时间') }}</span>
                                 {{ dialogData.executeDate }}
                             </div>
                             <div class="desc-item">
                                 <div class="item-name">药物处方
-                                    <span class="no-prescription" v-if="!hasPrescription">暂无处方</span>
+                                    <span class="no-prescription" v-if="!hasPrescription">{{ $t('暂无处方') }}</span>
                                 </div>
                                 <div class="item-table" v-if="hasPrescription">
                                     <div class="table">
                                         <div class="th">
-                                            <div class="td">类型</div>
-                                            <div class="td width">名称</div>
-                                            <div class="td">配比</div>
-                                            <div class="td">方式</div>
+                                            <div class="td">{{ $t('类型') }}</div>
+                                            <div class="td width">{{ $t('名称') }}</div>
+                                            <div class="td">{{ $t('配比') }}</div>
+                                            <div class="td">{{ $t('方式') }}</div>
                                         </div>
                                         <div
                                             class="tr"
@@ -52,7 +52,7 @@
                                             <div class="td">{{ subP.typeName }}</div>
                                             <div class="td width">{{ subP.name }}</div>
                                             <div class="td">{{ subP.ratio }}</div>
-                                            <div class="td">人工</div>
+                                            <div class="td">{{ $t('人工') }}</div>
                                         </div>
                                     </div>
                                 </div>
@@ -77,7 +77,7 @@
             <div class="dialog-footer">
                 <div class="close-btn">
                     <!-- <el-icon size="32" color="#fff"><CircleCloseFilled /></el-icon> -->
-                    <!-- <el-button type="primary" class="one-btn" @click="toShare"> 立即分享 </el-button> -->
+                    <!-- <el-button type="primary" class="one-btn" @click="toShare">{{ $t('立即分享') }}</el-button> -->
                 </div>
             </div>
         </template>
@@ -89,7 +89,7 @@
         </div>
         <div class="no-popup-title">
             <span>{{ curRole == 0 ? "好的,感谢您的配合" : "农事下发成功" }}</span>
-            <div class="no-popup-title-sub" v-if="curRole == 0">请您耐心等待 农事确认</div>
+            <div class="no-popup-title-sub" v-if="curRole == 0">{{ $t('请您耐心等待 农事确认') }}</div>
         </div>
         <div class="no-popup-btn" @click="handleNoPopupBtn">{{ curRole == 0 ? "我知道了" : "我知道了" }}</div>
     </Popup>

+ 30 - 14
src/components/gardenList.vue

@@ -12,18 +12,18 @@
                     <div class="garden-top-row">
                         <span class="garden-name van-ellipsis">{{ item.name }}</span>
                         <div class="warning-tag">
-                            <span>{{ item.warningText }}</span>
+                            <span>{{ t("common.droughtWarning") }}</span>
                             <el-icon size="10"><ArrowRightBold /></el-icon>
                         </div>
                     </div>
-                    <div class="garden-address van-ellipsis">{{ item.address }}</div>
+                    <div class="garden-address van-ellipsis">{{ item.rawAddress || t("common.noAddress") }}</div>
                 </div>
                 <div
                     v-if="item.isCurrent"
                     class="action-btn action-btn-current"
                     type="button"
                 >
-                    当前农场
+                    {{ t("garden.currentFarm") }}
                 </div>
                 <div
                     v-else
@@ -31,7 +31,7 @@
                     type="button"
                     @click.stop="handleSelectFarm(item)"
                 >
-                    设为默认农场
+                    {{ t("garden.setDefaultFarm") }}
                 </div>
             </div>
 
@@ -53,14 +53,16 @@
 
     <!-- 新增农场 -->
     <div class="garden-btn fixed-btn" @click="handleAddFarm">
-        <span class="add-garden-btn-text">新增农场</span>
+        <span class="add-garden-btn-text">{{ t("garden.addFarm") }}</span>
     </div>
 </template>
 
 <script setup>
 import { onActivated, ref, watch } from "vue";
 import { useRouter } from "vue-router";
+import { useI18n } from "@/i18n";
 
+const { t, locale } = useI18n();
 const router = useRouter();
 
 const props = defineProps({
@@ -74,15 +76,30 @@ const emit = defineEmits(["selectGarden", "loaded"]);
 
 const gardenList = ref([]);
 
+const STAT_CONFIG = [
+    { titleKey: "statFarm11", labelKey: "statSomeRisk", type: "danger" },
+    { titleKey: "statFarm22", labelKey: "statStandardFarm", type: "danger" },
+    { titleKey: "statFarm22", labelKey: "statStandardControl", type: "safe" },
+];
+
 function mapStats() {
-    // 这里先保留静态展示,后续可替换为接口字段映射
-    return [
-        { title: "农事11", label: "某某风险", type: "danger" },
-        { title: "农事22", label: "标准农事", type: "danger" },
-        { title: "农事22", label: "标准防治", type: "safe" }
-    ];
+    return STAT_CONFIG.map(({ titleKey, labelKey, type }) => ({
+        title: t(`garden.${titleKey}`),
+        label: t(`garden.${labelKey}`),
+        type,
+    }));
 }
 
+function refreshGardenStats() {
+    if (!gardenList.value.length) return;
+    gardenList.value = gardenList.value.map((farm) => ({
+        ...farm,
+        stats: mapStats(),
+    }));
+}
+
+watch(locale, refreshGardenStats);
+
 function saveSelectedFarm(farm) {
     localStorage.setItem("selectedFarmId", farm.id);
     localStorage.setItem("selectedFarmName", farm.name || "");
@@ -140,9 +157,8 @@ function normalizeFarmList(data) {
         ...farm,
         id: farm.farm_id,
         name: farm.farm_name,
-        warningText: "干旱预警",
         wkt: farm.geom_wkt,
-        address: farm.farm_address || farm.city || "暂无地址",
+        rawAddress: farm.farm_address || farm.city || "",
         stats: mapStats(),
         isCurrent: false
     }));
@@ -327,7 +343,7 @@ defineExpose({
 }
 
 .stat-title {
-    font-size: 16px;
+    font-size: 15px;
     line-height: 22px;
 }
 

+ 3 - 3
src/components/pageComponents/FarmWorkPlanTimeline.vue

@@ -12,7 +12,7 @@
             </div>
             <div v-for="(p, idx) in phenologyList" :key="`phenology-${uniqueTimestamp}-${idx}`" class="phenology-bar">
                 <div class="start-time">
-                    <div class="time-text">起始时间</div>
+                    <div class="time-text">{{ $t('起始时间') }}</div>
                     <span>{{ p.startDate }}</span>
                 </div>
                 <div class="phenology-title" v-if="p.reproductiveList[0]?.phenologyName === getNextPhenologyName(idx, 0)">
@@ -51,12 +51,12 @@
                                     v-if="!disableClick"
                                     :class="shouldShowIncompleteStatus(fw.farmWorkId) ? 'link-warning' : 'edit-link'"
                                     @click.stop="handleEdit(fw)"
-                                    >点击编辑</span
+                                    >{{ $t('点击编辑') }}</span
                                 >
                             </div>
                             <div class="card-status" v-if="shouldShowIncompleteStatus(fw.farmWorkId)">
                                 <el-icon color="#FF953D" size="13"><WarningFilled /></el-icon>
-                                <span>未完善</span>
+                                <span>{{ $t('未完善') }}</span>
                             </div>
                         </div>
                     </div>

+ 3 - 1
src/components/pageComponents/FnShareSheet.vue

@@ -2,13 +2,15 @@
     <share-sheet
         teleport="#app"
         v-model:show="internalShow"
-        title="立即分享给好友"
+        :title="t('立即分享给好友')"
         :options="options"
         @select="handleSelect"
     />
 </template>
 
 <script setup>
+import { useI18n } from "@/i18n";
+const { t } = useI18n();
 import { ShareSheet } from "vant";
 import { computed } from "vue";
 

+ 18 - 2
src/components/pageComponents/GrowthStageTimeline.vue

@@ -54,7 +54,7 @@
                                     class="growth-stage-timeline__tooltip"
                                     :style="tooltipBubbleStyle"
                                 >
-                                    {{ tooltipText }}
+                                    {{ displayTooltipText }}
                                 </div>
                                 <div
                                     class="growth-stage-timeline__tooltip-caret"
@@ -115,6 +115,7 @@ import {
     onBeforeUnmount,
     nextTick,
 } from "vue";
+import { useI18n } from "@/i18n";
 
 /**
  * @typedef {Object} GrowthStageItem
@@ -147,7 +148,22 @@ const props = defineProps({
     },
 });
 
-const emit = defineEmits(["update:modelValue", "change", "scrollSettled"]);
+const emit = defineEmits(["update:modelValue", "change", "scrollSettled", "locale-change"]);
+
+const { t, locale } = useI18n();
+
+const DEFAULT_TOOLTIP_ZH = "此为预估进程,请左右移动进行校准!";
+
+const displayTooltipText = computed(() => {
+    if (!props.tooltipText || props.tooltipText === DEFAULT_TOOLTIP_ZH) {
+        return t("growthStageTimeline.tooltipHint");
+    }
+    return t(props.tooltipText);
+});
+
+watch(locale, () => {
+    emit("locale-change");
+});
 
 const scrollRef = ref(null);
 const trackRef = ref(null);

+ 1 - 1
src/components/pageComponents/locationSearch.vue

@@ -1,5 +1,5 @@
 <template>
-    <el-select class="location-search" v-model="locationVal" filterable remote clearable reserve-keyword placeholder="搜索位置"
+    <el-select class="location-search" v-model="locationVal" filterable remote clearable reserve-keyword :placeholder="$t('搜索位置')"
         :remote-method="remoteMethod" :loading="loading" @change="handleSearchRes" popper-class="location-search-popper"
         :class="customClass" @clear="handleClear">
         <el-option v-for="(item, index) in locationOptions.list" :key="index" :label="item.title"

+ 8 - 6
src/components/popup/UploadProgressPopup.vue

@@ -2,27 +2,29 @@
     <popup :show="show" round :close-on-click-overlay="false" class="upload-progress-popup"
         @update:show="emit('update:show', $event)">
         <slot name="header"></slot>
-        <div class="upload-box" v-loading="popupImageUploadLoading" element-loading-text="上传中...">
+        <div class="upload-box" v-loading="popupImageUploadLoading" element-loading-:text="t('上传中...')">
             <div class="box-header">
                 <div class="upload-title">
-                    <span>上传照片</span>
-                    <span class="optional">(可选)</span>
+                    <span>{{ t('上传照片') }}</span>
+                    <span class="optional">{{ t('(可选)') }}</span>
                 </div>
-                <div class="ai-btn">AI 智能分析</div>
+                <div class="ai-btn">{{ t('AI 智能分析') }}</div>
             </div>
             <upload ref="uploadRef" :maxCount="10" :initImgArr="initImgArr" @handleUpload="onHandleUpload">
             </upload>
-            <!-- <div class="upload-result">AI识别结果:该病为该病为该病为该病为病为该病为病为该病为</div> -->
+            <!-- <div class="upload-result">{{ t('AI识别结果:该病为该病为该病为该病为病为该病为病为该病为') }}</div> -->
         </div>
         <slot name="footer"></slot>
         <div class="upload-action-btns">
-            <div class="cancel-btn" @click="emit('cancel')">取消</div>
+            <div class="cancel-btn" @click="emit('cancel')">{{ t('取消') }}</div>
             <div class="confirm-btn" @click="emit('confirm')">{{ confirmText }}</div>
         </div>
     </popup>
 </template>
 
 <script setup>
+import { useI18n } from "@/i18n";
+const { t } = useI18n();
 import { ref } from 'vue';
 import { Popup } from 'vant';
 import upload from '@/components/upload.vue';

+ 5 - 3
src/components/popup/agriExecutePopup.vue

@@ -7,11 +7,11 @@
                 <!-- <img class="expert-logo" :src="popupData?.expertAvatar" alt="" /> -->
                 <div class="expert-info">
                     <!-- <span>{{ popupData?.expertName || "韦帮稳" }}专家</span> -->
-                    <span>飞鸟</span>
-                    <span class="invite-text">邀请您进行农情互动</span>
+                    <span>{{ t('飞鸟') }}</span>
+                    <span class="invite-text">{{ t('邀请您进行农情互动') }}</span>
                 </div>
             </div>
-            <div class="popup-tips">为了更好校准您的农事执行时间</div>
+            <div class="popup-tips">{{ t('为了更好校准您的农事执行时间') }}</div>
 
 
             <!-- 标题 -->
@@ -39,6 +39,8 @@
 </template>
 
 <script setup>
+import { useI18n } from "@/i18n";
+const { t } = useI18n();
 import { Popup } from "vant";
 import { ref } from "vue";
 import { useRouter } from "vue-router";

+ 5 - 5
src/components/popup/confirmDrawTypePopup.vue

@@ -1,6 +1,6 @@
 <template>
     <popup v-model:show="showValue" round class="confirm-draw-type-popup" teleport="body">
-        <div class="popup-title">请确认勾画类型</div>
+        <div class="popup-title">{{ $t('请确认勾画类型') }}</div>
         <div class="type-list">
             <div
                 v-for="item in typeOptions"
@@ -14,7 +14,7 @@
         </div>
 
         <template v-if="selectedType !== 'DORMANCY'">
-            <div class="popup-sub-title">请确认 具体勾画类别</div>
+            <div class="popup-sub-title">{{ $t('请确认 具体勾画类别') }}</div>
             <div class="category-list">
                 <div
                     v-for="(item, index) in currentCategoryOptions"
@@ -29,9 +29,9 @@
         </template>
 
         <textarea v-if="selectedType !== 'ABNORMAL'" v-model="remark" class="remark-input" maxlength="200"
-            placeholder="添加备注"></textarea>
+            :placeholder="$t('添加备注')"></textarea>
 
-        <div class="confirm-btn" @click="handleConfirm">确认</div>
+        <div class="confirm-btn" @click="handleConfirm">{{ $t('确认') }}</div>
     </popup>
 
     <!-- 异常问题区:点击确认后弹出的照片上传进度弹窗 -->
@@ -48,7 +48,7 @@
                 @handleUpload="handleUploadSuccess"
             />
         </div>
-        <textarea v-model="remark" class="remark-input" maxlength="200" placeholder="添加备注"></textarea>
+        <textarea v-model="remark" class="remark-input" maxlength="200" :placeholder="$t('添加备注')"></textarea>
         <div class="confirm-btn" :class="{ 'confirm-btn-loading': confirmUploadLoading }" @click="handleConfirmUpload">
             {{ confirmUploadLoading ? '上传中...' : '确认上传' }}
         </div>

+ 8 - 8
src/components/popup/droneConsultPopup.vue

@@ -3,26 +3,26 @@
         <div class="popup-content">
             <!-- 无人机咨询:飞鸟购买 -->
             <template v-if="type === 'droneConsult'">
-                <div class="top-text">未监测您果园有配套的无人机,您可咨询飞鸟购买</div>
+                <div class="top-text">{{ $t('未监测您果园有配套的无人机,您可咨询飞鸟购买') }}</div>
                 <div class="code-wrap">
                     <img class="code-img" src="@/assets/img/home/qrcode.png" alt="">
                 </div>
-                <div class="hint-bubble">长按可添加客服,咨询相关问题</div>
-                <div class="action-btn" @click="handleCopy">一键复制微信号</div>
+                <div class="hint-bubble">{{ $t('长按可添加客服,咨询相关问题') }}</div>
+                <div class="action-btn" @click="handleCopy">{{ $t('一键复制微信号') }}</div>
             </template>
             <!-- 地形/建模航线 -->
             <template v-else-if="type === 'terrainRoute'">
                 <div class="top-text">
-                    <div>需要获取精细的地形信息才可以规划航</div>
-                    <div>线,点击获取建模航线</div>
+                    <div>{{ $t('需要获取精细的地形信息才可以规划航') }}</div>
+                    <div>{{ $t('线,点击获取建模航线') }}</div>
                 </div>
-                <div class="action-btn" @click="handleConfirm">立即获取</div>
+                <div class="action-btn" @click="handleConfirm">{{ $t('立即获取') }}</div>
             </template>
             <!-- 航线已生成(兼容图片 UI:中间区域不展示,仅文案 + 我知道了) -->
             <template v-else-if="type === 'terrainRouteSuccess'">
-                <div class="top-text">果园三维建模航线已经生成,航线已经推送到遥控器上,请您按照规划的航线起飞即可</div>
+                <div class="top-text">{{ $t('果园三维建模航线已经生成,航线已经推送到遥控器上,请您按照规划的航线起飞即可') }}</div>
                 <div class="video-wrap"></div>
-                <div class="action-btn" @click="handleConfirm">我知道了</div>
+                <div class="action-btn" @click="handleConfirm">{{ $t('我知道了') }}</div>
             </template>
         </div>
     </popup>

+ 18 - 18
src/components/popup/farmInfoPopup.vue

@@ -9,20 +9,20 @@
         <!-- 第一步:农场信息填写 -->
         <template v-if="currentStep === 1">
             <!-- 标题 -->
-            <div class="popup-title">完善以下信息,生成智能种植方案</div>
+            <div class="popup-title">{{ $t('完善以下信息,生成智能种植方案') }}</div>
 
             <!-- 表单区域 -->
             <el-form ref="formRef" :model="formData" :rules="rules" class="farm-form">
                 <!-- 农场位置 -->
-                <el-form-item label="农场位置" prop="address">
-                    <el-input v-model="formData.address" placeholder="请输入农场位置" clearable />
+                <el-form-item :label="$t('农场位置')" prop="address">
+                    <el-input v-model="formData.address" :placeholder="$t('请输入农场位置')" clearable />
                 </el-form-item>
 
                 <!-- 农场品种 -->
-                <el-form-item label="农场品种" prop="speciesId">
+                <el-form-item :label="$t('农场品种')" prop="speciesId">
                     <el-select
                         v-model="formData.speciesId"
-                        placeholder="请选择"
+                        :placeholder="$t('请选择')"
                         @change="handleSpecieChange"
                     >
                         <el-option
@@ -35,12 +35,12 @@
                 </el-form-item>
 
                 <!-- 农场品类 -->
-                <el-form-item label="农场品类" prop="typeIds">
+                <el-form-item :label="$t('农场品类')" prop="typeIds">
                     <el-select
                         v-model="formData.typeIds"
                         multiple
                         collapse-tags
-                        placeholder="请选择"
+                        :placeholder="$t('请选择')"
                         :disabled="!formData.speciesId"
                         @change="handleFruitsChange"
                     >
@@ -54,19 +54,19 @@
                 </el-form-item>
 
                 <!-- 农场亩数 -->
-                <el-form-item label="农场亩数" prop="mu">
-                    <el-input v-model="formData.mu" placeholder="请输入" type="number">
+                <el-form-item :label="$t('农场亩数')" prop="mu">
+                    <el-input v-model="formData.mu" :placeholder="$t('请输入')" type="number">
                         <template #suffix>
-                            <span class="unit">亩</span>
+                            <span class="unit">{{ $t('') }}</span>
                         </template>
                     </el-input>
                 </el-form-item>
 
                 <!-- 农场名称 -->
-                <el-form-item label="农场名称" prop="name">
+                <el-form-item :label="$t('农场名称')" prop="name">
                     <el-input
                         v-model="formData.name"
-                        placeholder="请输入农场名称"
+                        :placeholder="$t('请输入农场名称')"
                         clearable
                         @input="handleNameInput"
                     />
@@ -77,18 +77,18 @@
         <!-- 第二步:物候期起始时间填写 -->
         <template v-else>
             <!-- 提示文字 -->
-            <div class="popup-title">为了精准匹配种植方案,完善物候信息</div>
+            <div class="popup-title">{{ $t('为了精准匹配种植方案,完善物候信息') }}</div>
 
             <!-- 物候期表单 -->
             <el-form ref="phenologyFormRef" :model="phenologyData" label-width="92px" :rules="phenologyRules" class="farm-form">
                 <!-- 物候期选择器 -->
                 <el-form-item
-                    label="当下物候期"
+                    :label="$t('当下物候期')"
                     prop="phenologyId"
                 >
                     <el-select
                         v-model="phenologyData.phenologyId"
-                        placeholder="选择物候期"
+                        :placeholder="$t('选择物候期')"
                         style="width: 100%"
                         @change="handlePhenologyChange"
                     >
@@ -103,13 +103,13 @@
 
                 <!-- 日期选择器 -->
                 <el-form-item
-                    label="起始时间"
+                    :label="$t('起始时间')"
                     prop="phenologyStartDate"
                 >
                     <el-date-picker
                         v-model="phenologyData.phenologyStartDate"
                         type="date"
-                        placeholder="选择时间"
+                        :placeholder="$t('选择时间')"
                         format="YYYY-MM-DD"
                         value-format="YYYY-MM-DD"
                         style="width: 100%"
@@ -119,7 +119,7 @@
         </template>
 
         <!-- 确认按钮 -->
-        <div class="btn-confirm" @click="handleConfirm">确认信息</div>
+        <div class="btn-confirm" @click="handleConfirm">{{ $t('确认信息') }}</div>
     </popup>
 </template>
 

+ 13 - 11
src/components/popup/interactPopup.vue

@@ -14,7 +14,7 @@
                         <el-select
                             v-model="formData.phenologyId"
                             size="large"
-                            placeholder="请选择物候期"
+                            :placeholder="t('请选择物候期')"
                             @change="handlePhenologyChange"
                             :editable="false"
                         >
@@ -27,7 +27,7 @@
                         </el-select>
                     </div>
                     <div class="select-item">
-                        <el-select v-model="formData.reproductiveId" size="large" placeholder="请选择" :editable="false">
+                        <el-select v-model="formData.reproductiveId" size="large" :placeholder="t('请选择')" :editable="false">
                             <el-option
                                 v-for="item in reproductiveList"
                                 :key="item.id"
@@ -50,7 +50,7 @@
                         style="width: 100%"
                         disabled
                         type="date"
-                        placeholder="请选择日期"
+                        :placeholder="t('请选择日期')"
                         :editable="false"
                     />
                 </div>
@@ -66,13 +66,13 @@
                     :rows="3"
                     maxlength="35"
                     show-word-limit
-                    placeholder="请设置互动问题"
+                    :placeholder="t('请设置互动问题')"
                     class="question-textarea"
                 />
             </div>
         </div>
         <div class="interact-buttons">
-            <div class="btn-delete" @click="handleCancelInteract">取消修改</div>
+            <div class="btn-delete" @click="handleCancelInteract">{{ t('取消修改') }}</div>
             <div class="btn-save" :class="{ disabled: isSaving }" @click="handleSaveInteract">
                 {{ saveButtonText }}
             </div>
@@ -81,6 +81,8 @@
 </template>
 
 <script setup>
+import { useI18n } from "@/i18n";
+const { t } = useI18n();
 import { Popup } from "vant";
 import { ref, computed } from "vue";
 import { ElMessage, ElMessageBox } from "element-plus";
@@ -124,19 +126,19 @@ const resetInteractData = () => {
 // 验证函数
 const validateInteractForm = () => {
     if (!formData.value.phenologyId) {
-        ElMessage.warning("请选择互动阶段");
+        ElMessage.warning({ message: t('请选择互动阶段'), type: 'warning' });
         return false;
     }
     if (!formData.value.reproductiveId) {
-        ElMessage.warning("请选择互动阶段");
+        ElMessage.warning({ message: t('请选择互动阶段'), type: 'warning' });
         return false;
     }
     if (!formData.value.interactionTime) {
-        ElMessage.warning("请选择互动时间");
+        ElMessage.warning({ message: t('请选择互动时间'), type: 'warning' });
         return false;
     }
     if (!formData.value.interactionQuestion?.trim()) {
-        ElMessage.warning("请设置互动问题");
+        ElMessage.warning({ message: t('请设置互动问题'), type: 'warning' });
         return false;
     }
     return true;
@@ -218,7 +220,7 @@ const handleSaveInteract = () => {
         .updateFarmWorkArrange(paramsObj)
         .then((res) => {
             if (res.code === 0) {
-                ElMessage.success("保存成功");
+                ElMessage.success({ message: t('保存成功'), type: 'success' });
                 show.value = false;
                 emit("handleSaveSuccess", paramsObj);
             } else {
@@ -227,7 +229,7 @@ const handleSaveInteract = () => {
         })
         .catch((error) => {
             console.error("保存互动设置失败:", error);
-            ElMessage.error("保存失败,请重试");
+            ElMessage.error({ message: t('保存失败,请重试'), type: 'error' });
         })
         .finally(() => {
             isSaving.value = false;

+ 5 - 3
src/components/popup/reviewUploadPopup.vue

@@ -7,13 +7,15 @@
                 <img class="example" src="@/assets/img/home/plus.png" alt="" />
             </upload>
             <div class="upload-footer">
-                <div class="btn" @click="handleUploadConfirm">确认上传</div>
+                <div class="btn" @click="handleUploadConfirm">{{ t('确认上传') }}</div>
             </div>
         </div>
     </popup>
 </template>
 
 <script setup>
+import { useI18n } from "@/i18n";
+const { t } = useI18n();
 import { ref, watch } from "vue";
 import { Popup } from "vant";
 import upload from "@/components/upload";
@@ -69,7 +71,7 @@ function handleUpload({ imgArr }) {
 // 确认上传处理函数
 const handleUploadConfirm = () => {
     if (images.value.length === 0) {
-        ElMessage.warning("请上传照片");
+        ElMessage.warning({ message: t('请上传照片'), type: 'warning' });
         return;
     }
     if(props.recordId){
@@ -79,7 +81,7 @@ const handleUploadConfirm = () => {
         };
         VE_API.monitor.addReviewImg(params).then((res) => {
             if (res.code === 0) {
-                ElMessage.success("上传成功");
+                ElMessage.success({ message: t('上传成功'), type: 'success' });
                 show.value = false;
                 emit("success");
             }

+ 4 - 2
src/components/popup/selectRegionPopup.vue

@@ -8,13 +8,15 @@
             <div class="sub-title">{{ subTitle }}</div>
         </div>
         <div class="actions">
-            <div class="btn btn-ghost" @click="handleSkip">跳过</div>
-            <div class="btn btn-primary" @click="handleConfirm">去勾选</div>
+            <div class="btn btn-ghost" @click="handleSkip">{{ t('跳过') }}</div>
+            <div class="btn btn-primary" @click="handleConfirm">{{ t('去勾选') }}</div>
         </div>
     </popup>
 </template>
 
 <script setup>
+import { useI18n } from "@/i18n";
+const { t } = useI18n();
 import { Popup } from "vant";
 import { computed, ref, watch, onMounted, onBeforeUnmount, nextTick } from "vue";
 import DrawRegionMap from "@/views/old_mini/interactionList/map/drawRegionMap.js";

+ 6 - 4
src/components/popup/startInteractPopup.vue

@@ -14,15 +14,15 @@
                 <!-- <el-avatar :size="26" :src="popupData?.expertAvatar" /> -->
                 <div class="expert-info">
                     <!-- <span>{{ popupData?.expertName || "韦帮稳" }}专家</span> -->
-                    <span>飞鸟</span>
-                    <span class="invite-text">邀请您进行农情互动</span>
+                    <span>{{ t('飞鸟') }}</span>
+                    <span class="invite-text">{{ t('邀请您进行农情互动') }}</span>
                 </div>
             </div>
-            <div class="popup-tips">为了将农事方案与您的品种匹配,请您录入品种信息</div>
+            <div class="popup-tips">{{ t('为了将农事方案与您的品种匹配,请您录入品种信息') }}</div>
 
 
             <!-- 标题 -->
-            <div class="popup-title">农情互动</div>
+            <div class="popup-title">{{ t('农情互动') }}</div>
 
             <!-- 异常信息区域 -->
             <div class="abnormal-info">
@@ -43,6 +43,8 @@
 </template>
 
 <script setup>
+import { useI18n } from "@/i18n";
+const { t } = useI18n();
 import { Popup } from "vant";
 import { ref } from "vue";
 import { useRouter } from "vue-router";

+ 6 - 4
src/components/popup/uploadTips.vue

@@ -1,18 +1,18 @@
 <template>
     <popup v-model:show="showValue" round class="upload-tips-popup" teleport="body">
-        <div class="title">上传提示</div>
+        <div class="title">{{ t('上传提示') }}</div>
         <div class="tips-box">
             <Highlight class="tips-text" source-string="请使用 水印相机 APP 进行拍摄照片照片需要显示 时间和位置 水印"
                 :keywords="['水印相机 APP', '时间和位置']" />
         </div>
         <div class="img-box">
             <img src="@/assets/img/monitor/upload-tip-img.png" alt="">
-            <div class="img-text">示例图片</div>
+            <div class="img-text">{{ t('示例图片') }}</div>
             <div class="img-info">
                 <span class="img-time">14:33</span>
                 <div class="info-text">
-                    <span>2026.03.11 星期三</span>
-                    <span class="address">广州市从化区江埔街九里步</span>
+                    <span>{{ t('2026.03.11 星期三') }}</span>
+                    <span class="address">{{ t('广州市从化区江埔街九里步') }}</span>
                 </div>
             </div>
         </div>
@@ -20,6 +20,8 @@
 </template>
 
 <script setup>
+import { useI18n } from "@/i18n";
+const { t } = useI18n();
 import { Popup, Highlight } from "vant";
 import { computed } from "vue";
 

+ 16 - 14
src/components/recordItem.vue

@@ -40,11 +40,11 @@
                     "
                 >
                     <!-- <div class="image-wrapper" v-if="currentImageUrl">
-                        <span class="image-label">执行照片</span>
+                        <span class="image-label">{{ t('执行照片') }}</span>
                         <img :src="currentImageUrl" alt="" />
                     </div> -->
                     <div class="image-wrapper">
-                        <span class="image-label">执行照片</span>
+                        <span class="image-label">{{ t('执行照片') }}</span>
                         <img
                             v-if="recordItemData.executeEvidence?.[0]"
                             :src="base_img_url2 + recordItemData.executeEvidence?.[0]"
@@ -53,7 +53,7 @@
                         <img v-else :src="base_img_url2 + recordItemData.executeEvidenceList?.[0]" alt="" />
                     </div>
                     <div class="image-wrapper">
-                        <span class="image-label">执行照片</span>
+                        <span class="image-label">{{ t('执行照片') }}</span>
                         <img
                             v-if="recordItemData.executeEvidence?.[1]"
                             :src="base_img_url2 + recordItemData.executeEvidence?.[1]"
@@ -76,7 +76,7 @@
                 class="info-item recipe-name"
                 v-if="onlyRecipeName && recordItemData?.prescriptionList && recordItemData?.prescriptionList.length"
             >
-                <span class="name-text">药物处方:</span>
+                <span class="name-text">{{ t('药物处方:') }}</span>
                 <div class="rescription info-val">
                     <span v-for="(fertilizer, fertilizerI) in recordItemData.prescriptionList" :key="fertilizerI">
                         <span v-for="(pest, pestI) in fertilizer.pesticideFertilizerList" :key="'sub' + pestI">
@@ -91,14 +91,14 @@
                             </span>
                         </span>
                     </span>
-                    <span v-if="!recordItemData.prescriptionList || !recordItemData.prescriptionList?.length || recordItemData.prescriptionList[0]?.pesticideFertilizerList?.length === 0">无处方</span>
+                    <span v-if="!recordItemData.prescriptionList || !recordItemData.prescriptionList?.length || recordItemData.prescriptionList[0]?.pesticideFertilizerList?.length === 0">{{ t('无处方') }}</span>
                 </div>
             </div>
             <div
                 class="info-item recipe-name"
                 v-if="onlyRecipeName && recordItemData?.prescription && recordItemData?.prescription?.id != null"
             >
-                <span class="name-text">药物处方:</span>
+                <span class="name-text">{{ t('药物处方:') }}</span>
                 <div class="rescription info-val" v-if="recordItemData?.prescription?.pesticideFertilizerList.length > 0">
                     <span
                         v-for="(pest, pestI) in recordItemData.prescription.pesticideFertilizerList"
@@ -124,10 +124,10 @@
                 <div class="info-table">
                     <div class="table">
                         <div class="th">
-                            <div class="td">类型</div>
-                            <div class="td width">名称</div>
-                            <div class="td">配比</div>
-                            <div class="td">方式</div>
+                            <div class="td">{{ t('类型') }}</div>
+                            <div class="td width">{{ t('名称') }}</div>
+                            <div class="td">{{ t('配比') }}</div>
+                            <div class="td">{{ t('方式') }}</div>
                         </div>
                         <div>
                             <div
@@ -139,14 +139,14 @@
                                 <div class="td">{{ subP.typeName }}</div>
                                 <div class="td width">{{ subP.name }}</div>
                                 <div class="td">{{ subP.ratio }}</div>
-                                <div class="td">人工</div>
+                                <div class="td">{{ t('人工') }}</div>
                             </div>
                         </div>
                         <!-- <div class="tr">
-                            <div class="td width">80%代森锰锌</div>
-                            <div class="td">山德生</div>
+                            <div class="td width">{{ t('80%代森锰锌') }}</div>
+                            <div class="td">{{ t('山德生') }}</div>
                             <div class="td">1:2000</div>
-                            <div class="td">人工</div>
+                            <div class="td">{{ t('人工') }}</div>
                         </div> -->
                     </div>
                 </div>
@@ -173,6 +173,8 @@
 </template>
 
 <script setup>
+import { useI18n } from "@/i18n";
+const { t } = useI18n();
 import { ref, computed, watch } from "vue";
 import { base_img_url2 } from "@/api/config";
 const emit = defineEmits(["titleRightClick"]);

+ 1 - 1
src/components/upload.vue

@@ -7,7 +7,7 @@
       <el-icon class="icon" size="16">
         <Warning />
       </el-icon>
-      <span>上传照片可精准预测最佳病虫防治时间</span>
+      <span>{{ $t('上传照片可精准预测最佳病虫防治时间') }}</span>
     </div>
     <div class="upload-content">
       <img v-if="exampleImg" @click="showExample(null)" class="example" src="@/assets/img/home/example-4.png" alt="" />

+ 15 - 13
src/components/weatherInfo.vue

@@ -21,13 +21,13 @@
                             <img class="current-icon"
                                 :src="activeGarden === 'list' ? require('@/assets/img/common/menu-active.png') : require('@/assets/img/common/menu.png')"
                                 alt="">
-                            <span class="current-name">农场列表</span>
+                            <span class="current-name">{{ t("weather.farmList") }}</span>
                         </div>
 
                         <div class="mask-wrap" v-if="props.showTabMask" @click="emit('closeTabMask')">
                             <div class="mask-content">
                                 <div class="mask-text">
-                                    查看全部农场列表,并可创建农场
+                                    {{ t("weather.farmListGuide") }}
                                 </div>
                             </div>
                         </div>
@@ -35,7 +35,7 @@
                 </div>
 
                 <div class="address-select flex-center farm-name" v-else>
-                    示范农场
+                    {{ t("common.demoFarm") }}
                 </div>
                 <div class="farm-l" v-show="activeGarden === 'current' && hasWeather">
                     <div class="temperature flex-center">
@@ -50,7 +50,7 @@
                                 <span>{{ currentDateText }}</span>
                                 <span class="temperature-text-date">{{ currentWeekText }}</span>
                                 <span v-show="!isExpanded" class="temperature-text-more" @click="toggleExpand">
-                                    展开更多
+                                    {{ t("weather.expandMore") }}
                                 </span>
                             </div>
                         </div>
@@ -59,7 +59,7 @@
                     <div class="button-wrap" v-if="hasFarm">
                         <div class="button-item" @click="handleFarmInfo">
                             <img class="button-pre" src="@/assets/img/common/info.png" alt="">
-                            农场信息维护
+                            {{ t("weather.farmInfoMaintain") }}
                         </div>
                         <!-- <div class="button-item">
                             <img class="button-pre" src="@/assets/img/common/idea.png" alt="">
@@ -76,22 +76,22 @@
         </div>
         <div class="weather-chart-container" v-show="activeGarden === 'current' && hasWeather">
             <div class="weather-chart-title">
-                <span>未来七天天气</span>
-                <div class="weather-chart-title-more" @click="toggleExpand">收起</div>
+                <span>{{ t("weather.future7Days") }}</span>
+                <div class="weather-chart-title-more" @click="toggleExpand">{{ t("weather.collapse") }}</div>
             </div>
             <weather-chart class="weather-chart" :weather-data="weatherData"></weather-chart>
         </div>
         <!-- 农场筛选 -->
         <div class="farm-filter" v-show="activeGarden === 'list'">
             <div class="filter-l">
-                <el-input v-model="searchFarm" style="width: 96px" placeholder="搜索农场" :prefix-icon="Search" />
+                <el-input v-model="searchFarm" style="width: 96px" :placeholder="t('weather.searchFarm')" :prefix-icon="Search" />
             </div>
             <div class="filter-r">
-                <el-select v-model="regionVal" placeholder="选择地区" style="width: 92px">
+                <el-select v-model="regionVal" :placeholder="t('weather.selectRegion')" style="width: 92px">
                     <el-option v-for="item in regionOptions" :key="item.value" :label="item.label"
                         :value="item.value" />
                 </el-select>
-                <el-select v-model="typeVal" placeholder="选择品类" style="width: 92px">
+                <el-select v-model="typeVal" :placeholder="t('weather.selectCategory')" style="width: 92px">
                     <el-option v-for="item in typeOptions" :key="item.value" :label="item.label" :value="item.value" />
                 </el-select>
             </div>
@@ -107,6 +107,9 @@ import { useRouter } from "vue-router";
 import { useStore } from "vuex";
 import { Search } from '@element-plus/icons-vue';
 import { convertPointToArray } from "@/utils/index";
+import { useI18n } from "@/i18n";
+
+const { t } = useI18n();
 const store = useStore();
 
 const props = defineProps({
@@ -231,7 +234,7 @@ function getWeatherData() {
             const today = data.daily[0];
             currentWeather.value = {
                 temp: today.tempMax || today.tempMin || 26,
-                text: today.textDay || "晴天",
+                text: today.textDay || t("weather.sunny"),
                 iconDay: today.iconDay,
                 fxDate: today.fxDate
             };
@@ -260,8 +263,7 @@ const currentWeekText = computed(() => {
         return "";
     }
 
-    const weekMap = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"];
-    return weekMap[date.getDay()];
+    return t(`week.${date.getDay()}`);
 });
 
 

+ 486 - 0
src/i18n/flat-en.js

@@ -0,0 +1,486 @@
+export default {
+  '一键复制微信号\'': '[一键复制微信号\']',
+  '上传中': 'Uploading',
+  '上传执行照片邀请码\'': '[Upload 执行照片邀Please 码\']',
+  '上传提示\'': 'Upload Notice\'',
+  '上传照片': '[Upload 照片]',
+  '上传照片\'': '[Upload 照片\']',
+  '上传照片可精准预测最佳病虫防治时间\'': '[Upload 照片可精准预测最佳病虫防治时间\']',
+  '下发专家\'': '[下发专家\']',
+  '下发农事"': '[下发Farm Work"]',
+  '专家': '[专家]',
+  '专属数字农场,种好卖好': 'Your digital farm — grow better, sell better',
+  '为了将农事方案与您的品种匹配,请您录入品种信息\'': '[为了将Farm Work方案与您的Variety匹配,Please 您录入Variety信息\']',
+  '为了更好校准您的农事执行时间\'': '[为了更好校准您的Farm Work执行时间\']',
+  '之前\'': '[之前\']',
+  '二级\'': '[二级\']',
+  '二维码弹窗 --': '[二维码弹窗 --]',
+  '互动主题:': '[Interaction主题:]',
+  '亩\'': '[亩\']',
+  '人工\'': '[人工\']',
+  '代森锰锌\'': '[代森锰锌\']',
+  '以上表型进入该阶段\'': '[以上表型进入该阶段\']',
+  '伴随间歇性小雨或雾天。\'': '[伴随间歇性小雨或雾天。\']',
+  '作业农事\'': '[作业Farm Work\']',
+  '作业周期\'': '[作业周期\']',
+  '作业面积\'': '[作业面积\']',
+  '作物块(每个农场一块) --': '[Crop块(每个Farm一块) --]',
+  '作物档案 --': 'Crop Archives --',
+  '作物档案:外层可滚动,滚动位置单独缓存以便路由': '[Crop Archives:外层可滚动,滚动位置单独缓存以便路由]',
+  '作物长势\'': '[Crop长势\']',
+  '作物长势报告': 'Crop Growth Report',
+  '使用功效\'': '[使用功效\']',
+  '使用设备\'': '[使用设备\']',
+  '保存图片\'': 'Save图片\'',
+  '修改地址\'': '[修改地址\']',
+  '全区"': '[全区"]',
+  '全部': 'All',
+  '全部日期\'': 'All日期\'',
+  '六成': '60%',
+  '六成\'': '[六成\']',
+  '关注农事': '[关注Farm Work]',
+  '其他\'': '[其他\']',
+  '内容卡片 --': '[内容卡片 --]',
+  '内置标题模板 --': '[内置标题模板 --]',
+  '农事凭证\'': '[Farm Work凭证\']',
+  '农事名称\'': '[Farm Work名称\']',
+  '农事宗旨\'': '[Farm Work宗旨\']',
+  '农事建议': 'Farming advice',
+  '农事建议\'': 'Farming Advice\'',
+  '农事执行图片"': '[Farm Work执行图片"]',
+  '农事方案\'': '[Farm Work方案\']',
+  '农事时间\'': '[Farm Work时间\']',
+  '农事目的\'': '[Farm Work目的\']',
+  '农事组信息 --': '[Farm Work组信息 --]',
+  '农事编号\'': '[Farm Work编号\']',
+  '农事编号:': '[Farm Work编号:]',
+  '农事规划\'"': 'Farm Planning\'"',
+  '农事详情\'': '农事Details\'',
+  '农场亩数 --': '[Farm亩数 --]',
+  '农场亩数\'': '[Farm亩数\']',
+  '农场位置 --': '[Farm位置 --]',
+  '农场位置\'': '[Farm位置\']',
+  '农场位置:\'': '[Farm位置:\']',
+  '农场信息维护': 'Farm profile',
+  '农场列表': 'Farm list',
+  '农场列表 --': 'Farm List --',
+  '农场列表引导 --': 'Farm List引导 --',
+  '农场名称\'': '[Farm名称\']',
+  '农场名称:\'': '[Farm名称:\']',
+  '农场品种 --': 'FarmVariety --',
+  '农场品种\'': 'FarmVariety\'',
+  '农场品类 --': '[Farm品类 --]',
+  '农场品类\'': '[Farm品类\']',
+  '农场基本信息\'': '[Farm基本信息\']',
+  '农场筛选 --': 'FarmFilter --',
+  '农场详情\'': '农场Details\'',
+  '农场面积\'': '[Farm面积\']',
+  '农场面积:\'': '[Farm面积:\']',
+  '农情互动\'': '[农情 Interaction\']',
+  '农情档案\'': 'Crop Archives\'',
+  '农情照片"': '[农情照片"]',
+  '农情照片\'': '[农情照片\']',
+  '农情研判:\'': '[农情研判:\']',
+  '农情详情\'': '农情Details\'',
+  '分区管理\'': '[分区管理\']',
+  '创建 --': '[创建 --]',
+  '创建农场"': '[创建Farm"]',
+  '创建农场\'"': '[创建Farm\'"]',
+  '删除地块\'': 'Delete地块\'',
+  '加入下方指导群可解锁': '[加入下方指导群可解锁]',
+  '加入冼老师种植群\'': '[加入冼老师种植群\']',
+  '加载中': 'Loading',
+  '勾画区域\'': '[勾画区域\']',
+  '勾画区域\'"': '[勾画区域\'"]',
+  '勾选地块\'"': '[勾选Plot\'"]',
+  '勾选地块获得农场面积\'': '[勾选Plot获得Farm面积\']',
+  '区域勾选\'': '[区域勾选\']',
+  '单亩用量\'': '[单亩用量\']',
+  '历史风险\'': '[历史 Risk\']',
+  '历史风险报告': 'Historical Risk Reports',
+  '历史风险报告\'': 'Historical Risk Reports\'',
+  '历史高发风险\'': '[历史高发 Risk\']',
+  '去勾选\'': '[去勾选\']',
+  '发起需求"': '[发起需求"]',
+  '取消': 'Cancel',
+  '取消\'': 'Cancel\'',
+  '取消修改\'': 'Cancel修改\'',
+  '取消关注\'': 'Cancel关注\'',
+  '取消勾选\'': 'Cancel勾选\'',
+  '可选': '[可选]',
+  '右箭头 --': '[右箭头 --]',
+  '名称\'': '[名称\']',
+  '周一': 'Mon',
+  '周三': 'Wed',
+  '周二': 'Tue',
+  '周五': 'Fri',
+  '周六': 'Sat',
+  '周四': 'Thu',
+  '周日': 'Sun',
+  '咨询专家\'': '[咨询专家\']',
+  '品种选择 --': 'VarietySelect --',
+  '四大种植报告生成成功 --': '[四大种植 Report生成 Success --]',
+  '图片"': '[图片"]',
+  '图片列表 --': '[图片列表 --]',
+  '图片区域 --': '[图片区域 --]',
+  '图片消息 --': '[图片消息 --]',
+  '土壤性质\'': '[土壤性质\']',
+  '土壤改良\'': '[土壤改良\']',
+  '土壤改良报告': '[土壤改良 Report]',
+  '地图 --': '[地图 --]',
+  '地形': '[地形]',
+  '基本信息\'': '[基本信息\']',
+  '增加品种': '[增加Variety]',
+  '壤土\'': '[壤土\']',
+  '处于{stage}': 'in {stage}',
+  '处于为 分蘖初期\'': '[处于为 分蘖初期\']',
+  '处方卡片 --': '[处方卡片 --]',
+  '处方详情 --': '处方Details --',
+  '处方详情\'': '处方Details\'',
+  '复制方案"': '[复制方案"]',
+  '复核\'"': '[复核\'"]',
+  '复核成效\'': '[复核成效\']',
+  '复核照片\'': '[复核照片\']',
+  '天\'': '[天\']',
+  '天了': '[天了]',
+  '天后\'': '[天后\']',
+  '天气 --': 'Weather --',
+  '天气遮罩 --': '[Weather遮罩 --]',
+  '头部区域 --': '[头部区域 --]',
+  '如果不确定是否发生,直接上传照片即可\'': '如果不OK是否发生,直接上传照片即可\'',
+  '完善以下信息,生成智能种植方案\'': '[完善以下信息,生成智能种植方案\']',
+  '完善作物品种以及物候信息,精准匹配种植方案,实现精细化管理': '[完善CropVariety以及物候信息,精准匹配种植方案,实现精细化管理]',
+  '对方消息 --': '[对方消息 --]',
+  '对策建议:': 'Recommendations: ',
+  '对话样式消息 --': '[对话样式消息 --]',
+  '小麦\'': '[小麦\']',
+  '展开\'': 'Expand \'',
+  '展开更多': 'Expand',
+  '展开详细操作指南\'': '[Expand 详细操作指南\']',
+  '山德生\'': '[山德生\']',
+  '巡园建议:\'': '[Patrol 建议:\']',
+  '巡园提醒:': '[Patrol 提醒:]',
+  '巡园要点:\'': '[Patrol 要点:\']',
+  '巡园重点': 'Patrol priorities',
+  '左箭头 --': '[左箭头 --]',
+  '已全部生成\'': '已All生成\'',
+  '已关注"': '[已关注"]',
+  '已发起需求"': '[已发起需求"]',
+  '已完成\'': '已Done\'',
+  '已激活\'': '[已激活\']',
+  '已认证成功': '[已认证 Success]',
+  '带锁 --': '[带锁 --]',
+  '干旱预警': 'Drought alert',
+  '广州市从化区江埔街九里步\'': '[广州市从化区江埔街九里步\']',
+  '底部操作按钮 --': '[底部操作按钮 --]',
+  '建模航线提示弹窗(复用': '[建模航线 Notice弹窗(复用]',
+  '建议勾选\'': '[建议勾选\']',
+  '开始采集': '[开始采集]',
+  '异常信息区域 --': '[异常信息区域 --]',
+  '当前': 'Current',
+  '当前农场': 'Current farm',
+  '当前日期:2025': '[Current 日期:2025]',
+  '当前日期:{date}': 'Current date: {date}',
+  '当前膨果期对病虫害高度敏感,易受害。经过飞鸟智慧大脑计算病虫风险:病虫胁迫指数处于中等水平,存在一定的病虫害发生风险,局部区域可能出现病害流行或虫害爆发,需加强监测预警,及时巡园': 'During fruit expansion, crops are highly sensitive to pests and diseases. Feiniao AI assessment: moderate pest stress index. Local outbreaks are possible — strengthen monitoring and patrols.',
+  '当前荔枝处于果实膨大期,近期存在强降水和强对流天气频发的情况,且当前识别到阴雨气象风险较高3级,累计阴雨天数达12天、日均湿度约91%。连续阴雨导致光照不足,月累计日照时数较常年偏少17%,叶片光合效率下降,碳水化合物合成减少"': '当前荔枝处于果实膨大期,近期存在强降水和强对流天气频发的情况,且当前识别到阴雨Weather Risks较高3级,累计阴雨天数达12天、日均湿度约91%。连续阴雨导致光照不足,月累计日照时数较常年偏少17%,叶片光合效率下降,碳水化合物合成减少"',
+  '当前荔枝处于果期,预测田间病虫害气象风险2级,近期湿度达88%、日均气温达30': '当前荔枝处于果期,预测田间病虫害Weather Risks2级,近期湿度达88%、日均气温达30',
+  '当季作物:\'': '[当季Crop:\']',
+  '待记录\'': '[待记录\']',
+  '微信\'': '[微信\']',
+  '您的农场已绑定成功': 'Your farm has been linked successfully',
+  '您的四大种植报告\'': '[您的四大种植 Report\']',
+  '成果报告\'': '[成果 Report\']',
+  '我\'': '[我\']',
+  '我已执行 --': '[我已执行 --]',
+  '我已执行\'': '[我已执行\']',
+  '托管农事 会触发农情互动,并且为您推送农事,取消关注 则不会为您推送任何与该农事相关的内容"': '托管农事 会触发农情互动,并且为您推送农事,Cancel关注 则不会为您推送任何与该农事相关的内容"',
+  '托管农事"': '[托管Farm Work"]',
+  '托管农事\'': '[托管Farm Work\']',
+  '托管农户:首页、农情档案、农事记录 --': '托管农户:首页、Crop Archives、农事记录 --',
+  '执行区域': '[执行区域]',
+  '执行区域 --': '[执行区域 --]',
+  '执行区域\'': '[执行区域\']',
+  '执行区域地图弹窗 --': '[执行区域地图弹窗 --]',
+  '执行操作\'': '[执行操作\']',
+  '执行方式\'': '[执行方式\']',
+  '执行日期\'': '[执行日期\']',
+  '执行时间\'': '[执行时间\']',
+  '执行时间已经过去': '[执行时间已经过去]',
+  '执行时间已过去 7 天\'': '[执行时间已过去 7 天\']',
+  '执行时间:': '[执行时间:]',
+  '执行最佳': '[执行最佳]',
+  '执行档案 --': '[执行 Archives --]',
+  '执行档案\'': '[执行 Archives\']',
+  '执行照片\'': '[执行照片\']',
+  '执行码\'': '[执行码\']',
+  '执行组织': '[执行组织]',
+  '执行轨迹 --': '[执行轨迹 --]',
+  '执行轨迹\'': '[执行轨迹\']',
+  '执行,执行时间需巡园校准': '[执行,执行时间需Patrol 校准]',
+  '拖动手柄 --': '[拖动手柄 --]',
+  '按钮区域 --': '[按钮区域 --]',
+  '按钮样式 --': '[按钮样式 --]',
+  '推荐时间\'': '[推荐时间\']',
+  '推荐时间:': '[推荐时间:]',
+  '提交成功\'': 'Submit成功\'',
+  '提交方案\'': 'Submit方案\'',
+  '提交有误\'': 'Submit有误\'',
+  '提示:关注农事': '[Notice:关注Farm Work]',
+  '搜索位置\'': 'Search位置\'',
+  '搜索农场': 'Search farms',
+  '搜索品类\'': 'Search品类\'',
+  '搜索框 --': 'Search框 --',
+  '操作提示:拖动圆点,即可调整地块边界\'': '[操作 Notice:拖动圆点,即可调整Plot边界\']',
+  '收起': 'Collapse',
+  '收起\'': 'Collapse\'',
+  '文本消息 --': '[文本消息 --]',
+  '新增农事\'': 'Add Farm Work\'',
+  '新增农场': 'Add farm',
+  '新增农场 --': 'Add Farm --',
+  '新增品种': 'Add Variety',
+  '新增品种\'': 'Add Variety\'',
+  '新增用户"': '[Add 用户"]',
+  '新增用户\'': '[Add 用户\']',
+  '新建管理分区\'': '[新建管理分区\']',
+  '方式\'': '[方式\']',
+  '方案设置"': '[方案设置"]',
+  '施用方式\'': '[施用方式\']',
+  '无人机咨询:飞鸟购买 --': '[无人机咨询:飞鸟购买 --]',
+  '无处方\'': '[无处方\']',
+  '时间和位置\'': '[时间和位置\']',
+  '星期三\'': '[星期三\']',
+  '是否有10%的荔枝发现蒂蛀虫为害特征?': 'Do 10% of lychee trees show litchi stem borer damage?',
+  '是否有60%的荔枝进入果实转色期?': 'Have 60% of lychee trees entered fruit color-change stage?',
+  '是是否有10%的荔枝出现生理落果或裂果症状': 'Do 10% of lychee trees show physiological drop or fruit cracking?',
+  '晴天': 'Sunny',
+  '智能分析\'': '[智能分析\']',
+  '暂无内容\'': '[No 内容\']',
+  '暂无地址': 'No address',
+  '暂无处方': '[No 处方]',
+  '暂无处方\'': '[No 处方\']',
+  '暂无巡园提醒"': '[No Patrol 提醒"]',
+  '暂无提示"': 'No Notice"',
+  '暂无数据"': 'No Data"',
+  '暂无数据\'': 'No Data\'',
+  '暂无记录\'': '[No 记录\']',
+  '暂未检测到工人执行\'': '[暂未检测到工人执行\']',
+  '暂未检测到拍照\'': '[暂未检测到拍照\']',
+  '有效期:2025': '[有效期:2025]',
+  '有机质\'': '[有机质\']',
+  '服务农场': '[服务Farm]',
+  '服务详情模式的内容 --': '服务Details模式的内容 --',
+  '未上传状态内容 --': '[未Upload 状态内容 --]',
+  '未完善\'': '[未完善\']',
+  '未执行\'': '[未执行\']',
+  '未来7-15天气象风险': '7–15 day weather risks',
+  '未来七天天气': '7-day forecast',
+  '未激活\'': '[未激活\']',
+  '未监测您果园有配套的无人机,您可咨询飞鸟购买\'': '[未监测您Orchard有配套的无人机,您可咨询飞鸟购买\']',
+  '果实是否开始由扁圆变圆、横径明显增大?\'': '[果实是否开始由扁圆变圆、横径明显增大?\']',
+  '果实由扁圆形开始横向增宽,果皮青绿色有光泽,果实横径明显增大,果肩逐渐饱满。 巡园要点:重点观察树冠外围中上部结果枝,检查果实形状变化和横径大小,统计进入快速膨大的果实比例。拍照记录果实膨大状态,上传存档。"': '[果实由扁圆形开始横向增宽,果皮青绿色有光泽,果实横径明显增大,果肩逐渐饱满。 Patrol 要点:重点观察树冠外围中上部结果枝,检查果实形状变化和横径大小,统计进入快速膨大的果实比例。拍照记录果实膨大状态,Upload 存档。"]',
+  '果径是否显著小于正常果,且果皮失去光泽、呈现暗哑状态?\'': '[果径是否显著小于正常果,且果皮失去光泽、呈现暗哑状态?\']',
+  '柑橘\'': '[柑橘\']',
+  '查看互动\'': 'View互动\'',
+  '查看全部农场列表,并可创建农场': 'View all farms and create new ones',
+  '查看农事\'': 'View农事\'',
+  '查看区域\'': 'View区域\'',
+  '查看更多\'': 'View更多\'',
+  '查看详情\'': '查看Details\'',
+  '标题 --': '[标题 --]',
+  '标题区域 --': '[标题区域 --]',
+  '校准物候期 --': 'Calibrate Phenology --',
+  '校准物候期)\'': 'Calibrate Phenology)\'',
+  '根外追肥': 'Foliar feeding',
+  '桂味': '[桂味]',
+  '桂味\'': '[桂味\']',
+  '桂味-上市时间:06': '[桂味-上市时间:06]',
+  '梢期杀虫\'': '[梢期杀虫\']',
+  '橙色状态头部 --': '[橙色状态头部 --]',
+  '此二维码为': '[此二维码为]',
+  '每一段农事 --': '[每一段Farm Work --]',
+  '每日农情打卡,享一对一专家指导\'': '[每日农情打卡,享一对一专家指导\']',
+  '气象风险': 'Weather risks',
+  '气象风险\'"': 'Weather Risks\'"',
+  '水印相机': '[水印相机]',
+  '水稻\'': 'Rice\'',
+  '没有更多了\'': '没有More了\'',
+  '注意事项\'': '[注意事项\']',
+  '添加"': '[添加"]',
+  '潜在危害:田间湿度过高极易诱发霜疫霉病、炭疽病等真菌病害,病菌侵染幼果后导致腐烂、脱落。影响幼果正常发育和果皮着色,可能造成裂果、烂果。\'': '[潜在危害:田间湿度过高极易诱发霜疫霉病、炭疽病等真菌病害,病菌侵染幼果后导致腐烂、脱落。影响幼果正常发育和果皮着色,可能造成裂果、烂果。\']',
+  '点击展开更多': 'Show more',
+  '点击收起': 'Collapse',
+  '点击新建管理分区\'': '[Tap 新建管理分区\']',
+  '点击查看\'': '点击View\'',
+  '点击编辑\'': '点击Edit\'',
+  '点击解锁': 'Unlock',
+  '点击解锁\'': 'Unlock\'',
+  '点击解锁一键溯源增产': 'Tap to unlock traceability & yield boost',
+  '点击解锁一键溯源增产\'': 'Tap to Unlock Traceability & Yield Boost\'',
+  '点击选择作物"': 'Tap Select Crop"',
+  '点样式 --': '[点样式 --]',
+  '物候期设置': '[Phenological Stage设置]',
+  '物候风险': '[物候 Risk]',
+  '生成成果报告\'': '[生成成果 Report\']',
+  '生育期背景:同一': '[Reproductive Stage背景:同一]',
+  '由于系统审核,您照片拍摄位置与农场相差超 3 公里,请在农场现场使用水印相机重新拍摄上传,谢谢配合\'': '[由于系统审核,您照片拍摄位置与Farm相差超 3 公里,Please 在Farm现场使用水印相机重新拍摄Upload ,谢谢配合\']',
+  '由于系统审核,某些照片拍摄位置与农场相差超 3 公里,请在农场现场使用水印相机重新拍摄上传,谢谢配合': '[由于系统审核,某些照片拍摄位置与Farm相差超 3 公里,Please 在Farm现场使用水印相机重新拍摄Upload ,谢谢配合]',
+  '界定标准:\'': '[界定标准:\']',
+  '界定标准:{ratio} 以上表型进入该阶段': 'Criterion: over {ratio} of plants show this stage',
+  '病害': '[病害]',
+  '病害"': '[病害"]',
+  '病虫互动': 'Pest check',
+  '病虫害列表 --': '[病虫害列表 --]',
+  '病虫害态势监控\'': '[病虫害态势监控\']',
+  '病虫害态势监控\'"': '[病虫害态势监控\'"]',
+  '病虫风险': 'Pest & disease risk',
+  '的果园情况,请查看': '的果园情况,请View',
+  '的物候期\'': '[的Phenological Stage\']',
+  '的生育期\'': '[的Reproductive Stage\']',
+  '确认': 'Confirm',
+  '确认\'': 'Confirm\'',
+  '确认上传\'': 'Confirm上传\'',
+  '确认修改"': 'Confirm修改"',
+  '确认农场区域\'': 'Confirm农场区域\'',
+  '确认区域\'': 'Confirm区域\'',
+  '确认执行时间 --': 'Confirm执行时间 --',
+  '示例图片\'': '[示例图片\']',
+  '示例照片\'': '[示例照片\']',
+  '示例照片轮播组件 --': '[示例照片轮播组件 --]',
+  '示范农场': 'Demo Farm',
+  '种植作物\'': '[种植Crop\']',
+  '种植区域': '[种植区域]',
+  '种植建议\'': '[种植建议\']',
+  '种植建议报告': '[种植建议 Report]',
+  '种植档案管理': 'Planting Records',
+  '种植类别\'': '[种植类别\']',
+  '科普知识:\'': '[科普知识:\']',
+  '稍后执行"': '[稍后执行"]',
+  '立即分享\'': '[立即分享\']',
+  '立即分享给好友\'': '[立即分享给好友\']',
+  '立即创建"': '[立即创建"]',
+  '立即获取': '[立即获取]',
+  '第一步:农场信息填写 --': '[第一步:Farm信息填写 --]',
+  '类型\'': '[类型\']',
+  '类型切换 --': '[类型切换 --]',
+  '精准施治,智慧护航\'': '[精准施治,智慧护航\']',
+  '组件:天气 --': '[组件:Weather --]',
+  '组合照片(用于生成合成图片) --': '[组合照片(用于生成合成图片) --]',
+  '经过飞鸟智慧大脑计气象风险:阴雨寡照胁迫指数处于中等水平,存在一定的植株缺素风险,局部区域可能会出现长势异常现象,需加强监测预警,及时巡园': 'Feiniao AI weather assessment: moderate rain/low-light stress. Nutrient deficiency may occur in some areas with abnormal growth — strengthen monitoring and patrols.',
+  '绿色标签 --': '[绿色标签 --]',
+  '编辑信息\'': 'Edit信息\'',
+  '编辑农场"': 'Edit农场"',
+  '编辑农场\'': 'Edit农场\'',
+  '群 --': '[群 --]',
+  '聊天消息区域 --': '[聊天消息区域 --]',
+  '背景描述:': 'Background: ',
+  '膨果期': 'Fruit expansion stage',
+  '自定义内容插槽 --': '[自定义内容插槽 --]',
+  '自定义标题插槽 --': '[自定义标题插槽 --]',
+  '航线已生成) --': '[航线已生成) --]',
+  '节点': '[节点]',
+  '荔枝': 'Lychee',
+  '荔枝\'': 'Lychee\'',
+  '荔枝膨果期遇持续阴雨寡照,需及时排水防渍,重点根外喷施磷酸二氢钾并配合补充钙硼肥,以快速恢复树势、促进膨果壮果。': 'During prolonged rain and low light in fruit expansion, improve drainage. Apply potassium dihydrogen phosphate foliar spray with calcium and boron to restore vigor and promote fruit sizing.',
+  '荔枝长势报告': 'Lychee Growth Report',
+  '药物处方': '[药物处方]',
+  '药物处方\'': '[药物处方\']',
+  '药物处方:': '[药物处方:]',
+  '药物处方:\'': '[药物处方:\']',
+  '药肥名称\'': '[药肥名称\']',
+  '药肥处方\'': '[药肥处方\']',
+  '药肥类型\'': '[药肥类型\']',
+  '药肥配比\'': '[药肥配比\']',
+  '蒂蛀虫、荔枝蝽蟓、炭疽病、霜疫霉病\'': '[蒂蛀虫、Lychee蝽蟓、炭疽病、霜疫霉病\']',
+  '虫害': '[虫害]',
+  '虫害防治': 'Pest control',
+  '表单区域 --': '[表单区域 --]',
+  '表型特征:\'': '[表型特征:\']',
+  '触发农事"': '[触发Farm Work"]',
+  '触发时间为今天\'': '[触发时间为今天\']',
+  '触发时间已过\'': '[触发时间已过\']',
+  '触发条件\'': '[触发条件\']',
+  '设为默认农场': 'Set as default',
+  '识别结果:该病为该病为该病为该病为病为该病为病为该病为\'': '[识别结果:该病为该病为该病为该病为病为该病为病为该病为\']',
+  '该农场\'': '[该Farm\']',
+  '该页面正在升级,敬请期待': '[该页面正在升级,敬Please 期待]',
+  '语音消息 --': '[语音消息 --]',
+  '请上传农事执行现场照片及所用药肥凭证照片\'': '[Please Upload Farm Work执行现场照片及所用药肥凭证照片\']',
+  '请使用 水印相机': '[Please 使用 水印相机]',
+  '请确认 具体勾画类别\'': '请Confirm 具体勾画类别\'',
+  '请确认勾画类型\'': '请Confirm勾画类型\'',
+  '请设置互动问题': '[Please 设置 Interaction问题]',
+  '请设置互动问题\'': '[Please 设置 Interaction问题\']',
+  '请输入\'': 'Please Enter\'',
+  '请输入农场位置\'': 'Please Enter农场位置\'',
+  '请输入农场的真实面积\' "': 'Please Enter农场的真实面积\' "',
+  '请输入农资机构名称\'': 'Please Enter农资机构名称\'',
+  '请输入您的农场名称\'': 'Please Enter您的农场名称\'',
+  '请输入病虫害名称\'': 'Please Enter病虫害名称\'',
+  '请选择\'': 'Please Select\'',
+  '请选择下次提醒时间': 'Please Select下次提醒时间',
+  '请选择互动时间': 'Please Select互动时间',
+  '请选择互动阶段': 'Please Select互动阶段',
+  '请选择实际执行时间': 'Please Select实际执行时间',
+  '请选择异常类型\'': 'Please Select异常类型\'',
+  '请选择当前\'': 'Please Select当前\'',
+  '请选择当前{species}的物候期': 'Select phenological stage for {species}',
+  '请选择当前{species}的生育期': 'Select reproductive stage for {species}',
+  '请选择您的土壤类型\'': 'Please Select您的土壤类型\'',
+  '请选择日期\'': 'Please Select日期\'',
+  '请选择物候期': 'Please select phenological stage',
+  '请选择物候期\'': 'Please Select物候期\'',
+  '请选择生育期': 'Please select reproductive stage',
+  '请选择类型\'': 'Please Select类型\'',
+  '质地\'': '[质地\']',
+  '起始时间\'': '[起始时间\']',
+  '距离执行时间还差': '[距离执行时间还差]',
+  '跳过\'': '[跳过\']',
+  '轨道:横线': '[轨道:横线]',
+  '转发\'': '[转发\']',
+  '转发处方\'': '[转发处方\']',
+  '转发执行二维码\'': '[转发执行二维码\']',
+  '转发操作指南\'': '[转发操作指南\']',
+  '过往种植:\'': '[过往种植:\']',
+  '返回后恢复 --': 'Back后恢复 --',
+  '这是': '[这是]',
+  '进程互动': 'Progress check',
+  '进行拍摄照片照片需要显示 时间和位置 水印"': '[进行拍摄照片照片需要显示 时间和位置 水印"]',
+  '连续多列合并为一格,标题与描述跨列居中 --': '[连续多列合并为一格,标题与描述跨列居中 --]',
+  '选择作物\'': 'Select Crop\'',
+  '选择品种\'': 'Select Variety\'',
+  '选择品类': 'Category',
+  '选择品类\'': 'Select Category\'',
+  '选择地区': 'Region',
+  '通过叶面喷施针对性杀虫剂,快速压低蒂蛀虫种群数量,保护果实免受蛀害。': 'Targeted foliar insecticides can quickly reduce litchi stem borer populations and protect fruit from damage.',
+  '邀请关注 --': 'Invite to Follow --',
+  '邀请关注\'': 'Invite to Follow\'',
+  '邀请您进行农情互动\'': '[邀Please 您进行农情 Interaction\']',
+  '邀请拍照\'': '[邀Please 拍照\']',
+  '配比\'': '[配比\']',
+  '重点巡视低洼地、排水不畅的粘重土壤果园,以及树冠中下部及内膛果实。观察果实膨大进度,对比不同方位果穗大小差异;观察叶片萎蔫程度和黄化部位。拔根检查根系状态': '[重点巡视低洼地、排水不畅的粘重土壤Orchard,以及树冠中下部及内膛果实。观察果实膨大进度,对比不同方位果穗大小差异;观察叶片萎蔫程度和黄化部位。拔根检查根系状态]',
+  '重置区域\'': '[重置区域\']',
+  '长势互动': 'Growth check',
+  '长势异常态势跟踪\'': '[长势异常态势跟踪\']',
+  '长势异常态势跟踪\'"': '[长势异常态势跟踪\'"]',
+  '长势报告"': 'Growth Report"',
+  '长按二维码保存或转发': '长按二维码Save或转发',
+  '长按可添加客服,咨询相关问题\'': '[长按可添加客服,咨询相关问题\']',
+  '长按图片保存或转发': '长按图片Save或转发',
+  '阴雨寡照风险': 'Rain & low-light risk',
+  '阶段文案': '[阶段文案]',
+  '韦帮稳"': '[韦帮稳"]',
+  '顶部状态 --': '[顶部状态 --]',
+  '顶部说明 --': '[顶部说明 --]',
+  '预警等级:黄色预警(中等风险),建议在未来48小时内启动预防措施。\'': '[预警等级:黄色预警(中等 Risk),建议在Future 48小时内启动预防措施。\']',
+  '预计': '[预计]',
+  '预计报价:': '[预计报价:]',
+  '预计未来7天平均相对湿度': '[预计Future 7天平均相对湿度]',
+  '预计激活时间': '[预计激活时间]',
+  '风险描述风险描述风险描述风险描述风险': '[Risk描述 Risk描述 Risk描述 Risk描述 Risk]',
+  '飞鸟\'': '[飞鸟\']',
+  '飞鸟种植助手\'"': '[飞鸟种植助手\'"]',
+  '高发区域:\'': '[高发区域:\']',
+  '高湿环境利于蒂蛀虫等害虫繁殖,成虫活动增加,增加后期虫果率。\'': '[高湿环境利于蒂蛀虫等害虫繁殖,成虫活动增加,增加后期虫果率。\']',
+  '高湿风险:\'': '[高湿 Risk:\']',
+  '默认内容 --': '[默认内容 --]',
+};

+ 69 - 0
src/i18n/index.js

@@ -0,0 +1,69 @@
+import { computed } from "vue";
+import { useStore } from "vuex";
+import messages from "./messages";
+import flatEn from "./flat-en";
+
+function getNested(obj, path) {
+    return path.split(".").reduce((acc, key) => (acc != null ? acc[key] : undefined), obj);
+}
+
+function hasChinese(str) {
+    return typeof str === "string" && /[\u4e00-\u9fff]/.test(str);
+}
+
+function interpolate(text, params = {}) {
+    if (!text || typeof text !== "string") return text;
+    return text.replace(/\{(\w+)\}/g, (_, key) => (params[key] != null ? params[key] : `{${key}}`));
+}
+
+function pairZhEn(zhTree, enTree, out = {}) {
+    Object.keys(zhTree || {}).forEach((k) => {
+        if (zhTree[k] && typeof zhTree[k] === "object" && !Array.isArray(zhTree[k])) {
+            pairZhEn(zhTree[k], (enTree || {})[k], out);
+        } else if (typeof zhTree[k] === "string" && typeof (enTree || {})[k] === "string") {
+            out[zhTree[k]] = enTree[k];
+        }
+    });
+    return out;
+}
+
+const nestedEnMap = pairZhEn(messages.zh, messages.en);
+
+export function translate(key, params = {}, locale) {
+    const lang = locale || "zh";
+
+    if (lang === "zh") {
+        if (key && key.includes(".") && !hasChinese(key)) {
+            const nested = getNested(messages.zh, key);
+            if (nested != null) return interpolate(nested, params);
+        }
+        return interpolate(key, params);
+    }
+
+    if (hasChinese(key)) {
+        const en =
+            flatEn[key] ||
+            nestedEnMap[key] ||
+            key;
+        return interpolate(en, params);
+    }
+
+    const text =
+        getNested(messages.en, key) ??
+        getNested(messages.zh, key) ??
+        flatEn[key] ??
+        nestedEnMap[key] ??
+        key;
+    return interpolate(text, params);
+}
+
+export function useI18n() {
+    const store = useStore();
+    const locale = computed(() => store.state.locale.locale);
+
+    const t = (key, params = {}) => translate(key, params, locale.value);
+
+    const toggleLocale = () => store.dispatch("locale/toggleLocale");
+
+    return { locale, t, toggleLocale };
+}

+ 298 - 0
src/i18n/messages.js

@@ -0,0 +1,298 @@
+import { recordDetailsZh, recordDetailsEn } from "./recordDetails-messages";
+
+export default {
+    zh: {
+        tabbar: {
+            growthReport: "长势报告",
+            agriFile: "农情档案",
+            agriRecord: "农事规划",
+            trace: "有味溯源",
+        },
+        common: {
+            current: "当前",
+            demoFarm: "示范农场",
+            expandMore: "点击展开更多",
+            collapse: "点击收起",
+            backgroundDesc: "背景描述:",
+            suggestion: "对策建议:",
+            unlock: "点击解锁",
+            noAddress: "暂无地址",
+            droughtWarning: "干旱预警",
+            cancel: "取消",
+            confirm: "确认",
+        },
+        week: {
+            0: "周日",
+            1: "周一",
+            2: "周二",
+            3: "周三",
+            4: "周四",
+            5: "周五",
+            6: "周六",
+        },
+        weather: {
+            farmList: "农场列表",
+            farmListGuide: "查看全部农场列表,并可创建农场",
+            expandMore: "展开更多",
+            collapse: "收起",
+            farmInfoMaintain: "农场信息维护",
+            future7Days: "未来七天天气",
+            searchFarm: "搜索农场",
+            selectRegion: "选择地区",
+            selectCategory: "选择品类",
+            sunny: "晴天",
+        },
+        garden: {
+            currentFarm: "当前农场",
+            setDefaultFarm: "设为默认农场",
+            addFarm: "新增农场",
+            statFarm11: "农事11",
+            statFarm22: "农事22",
+            statSomeRisk: "某某风险",
+            statStandardFarm: "标准农事",
+            statStandardControl: "标准防治",
+        },
+        growthStageTimeline: {
+            tooltipHint: "此为预估进程,请左右移动进行校准!",
+        },
+        growthReport: {
+            title: "荔枝长势报告",
+            cropTitle: "作物长势报告",
+            weatherRisk: "气象风险",
+            cropLychee: "荔枝",
+            phenologyFruitExpansion: "膨果期",
+            inStage: "处于{stage}",
+            futureWeatherRisk: "未来7-15天气象风险",
+            farmAdvice: "农事建议",
+            patrolFocus: "巡园重点",
+            bindSuccess: "您的农场已绑定成功",
+            lockTitle: "专属数字农场,种好卖好",
+            lockSub: "点击解锁一键溯源增产",
+            risk: {
+                pest: {
+                    title: "病虫风险",
+                    desc: "当前膨果期对病虫害高度敏感,易受害。经过飞鸟智慧大脑计算病虫风险:病虫胁迫指数处于中等水平,存在一定的病虫害发生风险,局部区域可能出现病害流行或虫害爆发,需加强监测预警,及时巡园",
+                },
+                rain: {
+                    title: "阴雨寡照风险",
+                    desc: "经过飞鸟智慧大脑计气象风险:阴雨寡照胁迫指数处于中等水平,存在一定的植株缺素风险,局部区域可能会出现长势异常现象,需加强监测预警,及时巡园",
+                },
+            },
+            advice: {
+                foliar: {
+                    title: "根外追肥",
+                    desc: "荔枝膨果期遇持续阴雨寡照,需及时排水防渍,重点根外喷施磷酸二氢钾并配合补充钙硼肥,以快速恢复树势、促进膨果壮果。",
+                },
+                pestControl: {
+                    title: "虫害防治",
+                    desc: "通过叶面喷施针对性杀虫剂,快速压低蒂蛀虫种群数量,保护果实免受蛀害。",
+                },
+            },
+            patrol: {
+                process: {
+                    title: "进程互动",
+                    desc: "是否有60%的荔枝进入果实转色期?",
+                },
+                growth: {
+                    title: "长势互动",
+                    desc: "是是否有10%的荔枝出现生理落果或裂果症状",
+                },
+                pest: {
+                    title: "病虫互动",
+                    desc: "是否有10%的荔枝发现蒂蛀虫为害特征?",
+                },
+            },
+        },
+        adjustPopup: {
+            title: "当前日期:{date}",
+            selectPhenology: "请选择当前{species}的物候期",
+            selectReproductive: "请选择当前{species}的生育期",
+            standard: "界定标准:{ratio} 以上表型进入该阶段",
+            ratio: "六成",
+            selectPhenologyToast: "请选择物候期",
+            selectReproductiveToast: "请选择生育期",
+        },
+        agriFile: {
+            legendZone: "管理分区",
+            legendGrowth: "长势异常",
+            legendPest: "病虫害异常",
+            tabPhenology: "物候记录",
+            tabAbnormal: "异常记录",
+            tabFarming: "农事记录",
+            recordFruitExpand: "果园出现果实迅速膨大,占比",
+            recordFruitSet: "果园出现谢花坐果,占比",
+            recordFullBloom: "果园进入盛花期",
+            recordPanicle: "果园出现花穗抽生",
+            recordStemBorerFruit: "果园出现蒂蛀虫为害果,占比",
+            recordCalciumCrack: "果园出现缺钙裂果症状,占比",
+            recordStinkBug: "果园出现荔枝蝽象若虫为害",
+            recordFlowerNoFruit: "果园出现花而不实现象",
+            recordFertilize: "执行了膨果壮果追肥农事",
+            recordStemBorerControl: "执行了蒂蛀虫防治农事",
+            recordFlowerFruitProtect: "执行了保花保果药剂喷施农事",
+            recordPreFlowerPest: "执行了花前病虫综合防治农事",
+        },
+        recordDetails: recordDetailsZh,
+        agriRecord: {
+            farmDetail: "农场详情",
+            level2: "二级",
+            pendingRecord: "待记录",
+            growthWorkName: "长势异常态势跟踪",
+            growthReason: "缺锌导致生长缓慢",
+            growthIssue: "是否有10%的荔枝出现落果裂果?",
+            pestWorkName: "病虫害态势监控",
+            pestReason: "病虫害影响作物生长",
+            pestIssue: "是否有10%的荔枝发现蒂蛀虫?",
+            phenologyWorkName: "物候跟踪记录",
+            phenologyReason: "预计到达病虫防控关键期",
+            phenologyIssue: "是否有60%的荔枝进入转色期",
+        },
+    },
+    en: {
+        tabbar: {
+            growthReport: "Growth Report",
+            agriFile: "Crop Archives",
+            agriRecord: "Farm Planning",
+            trace: "Traceability",
+        },
+        common: {
+            current: "Current",
+            demoFarm: "Demo Farm",
+            expandMore: "Show more",
+            collapse: "Collapse",
+            backgroundDesc: "Background: ",
+            suggestion: "Recommendations: ",
+            unlock: "Unlock",
+            noAddress: "No address",
+            droughtWarning: "Drought alert",
+            cancel: "Cancel",
+            confirm: "Confirm",
+        },
+        week: {
+            0: "Sun",
+            1: "Mon",
+            2: "Tue",
+            3: "Wed",
+            4: "Thu",
+            5: "Fri",
+            6: "Sat",
+        },
+        weather: {
+            farmList: "Farm list",
+            farmListGuide: "View all farms and create new ones",
+            expandMore: "Expand",
+            collapse: "Collapse",
+            farmInfoMaintain: "Farm profile",
+            future7Days: "7-day forecast",
+            searchFarm: "Search farms",
+            selectRegion: "Region",
+            selectCategory: "Category",
+            sunny: "Sunny",
+        },
+        garden: {
+            currentFarm: "Current farm",
+            setDefaultFarm: "Set as default",
+            addFarm: "Add farm",
+            statFarm11: "Farm Work 11",
+            statFarm22: "Farm Work 22",
+            statSomeRisk: "Risk Alert",
+            statStandardFarm: "Standard Farm Work",
+            statStandardControl: "Standard Control",
+        },
+        growthStageTimeline: {
+            tooltipHint: "This is an estimated progress. Drag left or right to calibrate.",
+        },
+        growthReport: {
+            title: "Lychee Growth Report",
+            cropTitle: "Crop Growth Report",
+            weatherRisk: "Weather risks",
+            cropLychee: "Lychee",
+            phenologyFruitExpansion: "Fruit expansion stage",
+            inStage: "in {stage}",
+            futureWeatherRisk: "7–15 day weather risks",
+            farmAdvice: "Farming advice",
+            patrolFocus: "Patrol priorities",
+            bindSuccess: "Your farm has been linked successfully",
+            lockTitle: "Your digital farm — grow better, sell better",
+            lockSub: "Tap to unlock traceability & yield boost",
+            risk: {
+                pest: {
+                    title: "Pest & disease risk",
+                    desc: "During fruit expansion, crops are highly sensitive to pests and diseases. Feiniao AI assessment: moderate pest stress index. Local outbreaks are possible — strengthen monitoring and patrols.",
+                },
+                rain: {
+                    title: "Rain & low-light risk",
+                    desc: "Feiniao AI weather assessment: moderate rain/low-light stress. Nutrient deficiency may occur in some areas with abnormal growth — strengthen monitoring and patrols.",
+                },
+            },
+            advice: {
+                foliar: {
+                    title: "Foliar feeding",
+                    desc: "During prolonged rain and low light in fruit expansion, improve drainage. Apply potassium dihydrogen phosphate foliar spray with calcium and boron to restore vigor and promote fruit sizing.",
+                },
+                pestControl: {
+                    title: "Pest control",
+                    desc: "Targeted foliar insecticides can quickly reduce litchi stem borer populations and protect fruit from damage.",
+                },
+            },
+            patrol: {
+                process: {
+                    title: "Progress check",
+                    desc: "Have 60% of lychee trees entered fruit color-change stage?",
+                },
+                growth: {
+                    title: "Growth check",
+                    desc: "Do 10% of lychee trees show physiological drop or fruit cracking?",
+                },
+                pest: {
+                    title: "Pest check",
+                    desc: "Do 10% of lychee trees show litchi stem borer damage?",
+                },
+            },
+        },
+        adjustPopup: {
+            title: "Current date: {date}",
+            selectPhenology: "Select phenological stage for {species}",
+            selectReproductive: "Select reproductive stage for {species}",
+            standard: "Criterion: over {ratio} of plants show this stage",
+            ratio: "60%",
+            selectPhenologyToast: "Please select phenological stage",
+            selectReproductiveToast: "Please select reproductive stage",
+        },
+        agriFile: {
+            legendZone: "Management Zone",
+            legendGrowth: "Growth Abnormality",
+            legendPest: "Pest & Disease",
+            tabPhenology: "Phenology",
+            tabAbnormal: "Abnormalities",
+            tabFarming: "Farm Work",
+            recordFruitExpand: "Rapid fruit expansion in orchard, ",
+            recordFruitSet: "Flower drop and fruit set in orchard, ",
+            recordFullBloom: "Orchard in full bloom",
+            recordPanicle: "Panicle emergence in orchard",
+            recordStemBorerFruit: "Stem borer damage on fruit in orchard, ",
+            recordCalciumCrack: "Calcium deficiency fruit cracking in orchard, ",
+            recordStinkBug: "Lychee stink bug nymph damage in orchard",
+            recordFlowerNoFruit: "Flowering without fruit set in orchard",
+            recordFertilize: "Fruit expansion fertilization applied",
+            recordStemBorerControl: "Stem borer control applied",
+            recordFlowerFruitProtect: "Flower and fruit protection spray applied",
+            recordPreFlowerPest: "Pre-bloom pest control applied",
+        },
+        recordDetails: recordDetailsEn,
+        agriRecord: {
+            farmDetail: "Farm Details",
+            level2: "Level 2",
+            pendingRecord: "Pending",
+            growthWorkName: "Growth Abnormality Tracking",
+            growthReason: "Slow growth due to zinc deficiency",
+            growthIssue: "Do 10% of lychee trees show fruit drop or cracking?",
+            pestWorkName: "Pest & Disease Monitoring",
+            pestReason: "Pests and diseases affecting crop growth",
+            pestIssue: "Do 10% of lychee trees show litchi stem borer?",
+            phenologyWorkName: "Phenology Tracking",
+            phenologyReason: "Expected critical pest control period",
+            phenologyIssue: "Have 60% of lychee trees entered color-change stage?",
+        },
+    },
+};

+ 162 - 0
src/i18n/recordDetails-messages.js

@@ -0,0 +1,162 @@
+/** 农事记录详情页文案 */
+export const recordDetailsZh = {
+    workName: "农事名称",
+    expand: "展开",
+    collapse: "收起",
+    scienceLabel: "科普知识:",
+    phenotypeLabel: "表型特征:",
+    highRiskLabel: "高发区域:",
+    farmAnalysisLabel: "农情研判:",
+    patrolLabel: "巡园要点:",
+    growthQuestion: "果径是否显著小于正常果,且果皮失去光泽、呈现暗哑状态?",
+    pestQuestion: "蒂蛀虫、荔枝蝽蟓、炭疽病、霜疫霉病",
+    phenologyQuestion: "果实是否开始由扁圆变圆、横径明显增大?",
+    growthScience:
+        "当前荔枝处于果实膨大期,近期存在强降水和强对流天气频发的情况,且当前识别到阴雨气象风险较高3级,累计阴雨天数达12天、日均湿度约91%。连续阴雨导致光照不足,月累计日照时数较常年偏少17%,叶片光合效率下降,碳水化合物合成减少",
+    growthPhenotype:
+        "果实膨大速度慢,果皮颜色黄绿、光泽度差,严重时果面出现细微水裂纹;萎蔫时叶片下垂卷曲;缺氮时老叶均匀黄化;缺钾时老叶叶缘焦枯;缺镁时老叶叶肉黄化、叶脉仍绿;缺钙时果实易裂果;缺硼时果实发育不良、果皮木栓化;缺锌时果实大小不均;缺铁时新叶黄白失绿",
+    growthHighRisk:
+        "重点巡视低洼地、排水不畅的粘重土壤果园,以及树冠中下部及内膛果实。观察果实膨大进度,对比不同方位果穗大小差异;观察叶片萎蔫程度和黄化部位。拔根检查根系状态——砂土看根系是否偏白不烂,黏土看是否发软发臭,酸性土看根尖是否膨大变黑,盐碱土看是否被泥浆包裹,重金属土看是否发黑有异味,荔枝土看根尖是否变黑腐臭。根系受损时先排水松土养根,暂不施根肥,叶面维持,等新根萌发后再施根肥。发现异常症状后请拍照上传,及时存档。",
+    pestScience:
+        "当前荔枝处于果期,预测田间病虫害气象风险2级,近期湿度达88%、日均气温达30℃,该环境条件十分有利于害虫活动和病原菌侵染。果期是荔枝产量形成的关键阶段,幼果皮层薄嫩、抗逆性弱,一旦遭受蒂蛀虫、荔枝蝽象、霜疫霉病、炭疽病等主要病虫为害,将直接导致落果、烂果,严重影响产量和品质。需抓紧巡园,同步做好虫情、病情的动态监测,防范风险升级。",
+    phenologyAnalysis:
+        "膨果期果实开始横向生长,由扁圆形逐渐转为圆球形,是果肉细胞快速分裂膨大和干物质积累的关键阶段,氮和钾需求双高。氮促进果肉细胞分裂膨大,钾促进光合产物转运和糖分积累。此时养分供应不足会导致果实膨大受阻、单果重偏低,需及时根部追肥保障果实发育。",
+    phenologyPhenotype:
+        "果实由扁圆形开始横向增宽,果皮青绿色有光泽,果实横径明显增大,果肩逐渐饱满。\n巡园要点:重点观察树冠外围中上部结果枝,检查果实形状变化和横径大小,统计进入快速膨大的果实比例。拍照记录果实膨大状态,上传存档。",
+    tabFruitBlocked: "膨果受阻",
+    tabSlowGrowth: "长势缓慢",
+    tabVigorous: "植株旺长",
+    tabWilting: "叶片萎蔫",
+    drawTip: "点击勾画新发生区域",
+    notReached: "暂未到达进程",
+    confirmUpload: "确认上传",
+    keyAssessment: "关键防治需肥期评估:",
+    phenologyIssue: "是否有60%的荔枝进入膨果期?",
+    currentStatus: "当前现状:当前{label}进入到{period}",
+    uploadPhoto: "点击上传照片",
+    confirmInfo: "确认信息",
+    abnormalBanner: "发现异常,拍照记录",
+    abnormalBannerDesc: "系统为您智能匹配农事方案",
+    abnormalRecord: "异常记录",
+    abnormalCount: "有多少植株出现了异常?",
+    inputPlaceholder: "请输入",
+    patrolDesc: "巡园描述",
+    inputDescPlaceholder: "请输入描述",
+    uploadPhotoFirst: "请先上传照片",
+    confirmSuccess: "确认成功",
+    pestDisease: "病害",
+    pestInsect: "虫害",
+    catFungal: "真菌类",
+    catBorer: "钻蛀类",
+    catVirus: "病毒类",
+    catOther: "其它",
+    catChewing: "啃食类",
+    catMasticate: "咀嚼式",
+    detDownyMildew: "霜疫霉病",
+    detAnthracnose: "炭疽病",
+    detBlast: "叶瘟",
+    detSheathBlight: "纹枯病",
+    detStinkBug: "荔枝蝽象",
+    detRiceBorer: "二化螟",
+    detStemBorer: "蒂蛀虫",
+    pestT1_downy:
+        "果实感病后出现水渍状褪绿斑块,湿度大时表面产生白色霜状霉层,后病部变褐软腐,果实腐烂脱落。膨大期至转色期果实感病后病斑扩展迅速,果肉褐变腐烂,有酸臭味,病果易脱落。",
+    pestT2_downy:
+        "重点巡查果园低洼积水处、密蔽通风不良处的果穗,观察果实表面有无水渍状褪绿斑块,清晨或雨后湿度大时查看病斑表面有无白色霜状霉层,用手轻捏感病果实检查是否软腐,闻有无酸臭味。发现病斑后请拍照上传,及时存档,便于记录发生程度与防控节点。",
+    pestT1_anthracnose:
+        "果实感病后出现暗褐色水渍状斑点,后扩展为圆形或不规则形黑褐色凹陷病斑,边缘明显,湿度大时病斑表面出现粉红色黏稠孢子堆。病斑深入果肉导致腐烂,病果干缩后挂在枝头不脱落形成僵果。",
+    pestT2_anthracnose:
+        "重点巡查树冠内膛及下部枝条的果穗,观察果实表面有无暗褐色至黑褐色凹陷病斑,病斑边缘是否清晰,在湿度大的清晨查看病斑表面有无粉红色黏稠物。注意与霜疫霉病区分:炭疽病病斑凹陷干缩,霜疫霉病病部湿软腐烂。发现病斑后请拍照上传,及时存档,便于记录发生程度与防控节点。",
+    pestT1_stinkbug:
+        "成虫和若虫刺吸幼果和嫩梢汁液,受害果实出现褐色坏死斑点,果实发育受阻、果形不正,严重时幼果大量脱落。成虫体长约25mm、黄褐色盾形,受惊时释放臭液;若虫体色鲜红至橙红,群集为害。",
+    pestT2_stinkbug:
+        "重点巡查树冠中上部外围果穗集中处,观察果实和果梗上是否有黄褐色盾形成虫或红色群集若虫。轻摇枝条看是否有成虫受惊飞逃,同时闻是否有刺激性臭味。发现为害状及活虫后请拍照上传,及时存档,便于记录发生程度与防控节点。",
+    pestT1_stemborer:
+        "成虫为小型蛾类,幼虫钻蛀果实,从果蒂附近蛀入果心,蛀食果核和果肉,造成果实内部充满虫粪。受害果实外表初期无明显异常,后期果蒂周围变黑、果实提前着色变红脱落(\"红果\"现象),落果剖开可见幼虫及虫粪。",
+    pestT2_stemborer:
+        "重点巡查树冠内膛和下垂枝的果穗,注意捡拾地面落果,剖开检查果核周围有无幼虫和虫粪。观察树上果实有无果蒂变黑、提前转红的异常果。成虫调查时,在晨昏成虫活动期用手掌用力拍打树冠内膛的枝干,观察是否有小蛾受惊飞出,以此判断成虫发生密度。关键防治节点需通过收集落果观察蛹的羽化进度来确定打药时机。发现为害状及活虫后请拍照上传,及时存档,便于记录发生程度与防控节点。",
+    pestT1_riceborer: "幼虫钻蛀稻茎基部,造成枯心苗,受害株心叶变黄枯死,茎秆基部有圆形蛀孔及虫粪排出,轻拔枯心苗易折断,断口可见幼虫或虫道",
+    pestT2_riceborer:
+        "重点巡查田间分散出现的枯心团或枯心带,沿行间逐株检查心叶是否变黄萎蔫。拔取枯心株,用刀片或指甲剥开茎秆基部,检查内部有无淡褐色或灰白色幼虫、虫粪及蛀道。发现为害状及活虫后请拍照上传,及时存档,便于记录发生程度与防控节点",
+};
+
+export const recordDetailsEn = {
+    workName: "Farm Work",
+    expand: "Expand",
+    collapse: "Collapse",
+    scienceLabel: "Science: ",
+    phenotypeLabel: "Phenotype: ",
+    highRiskLabel: "High-risk areas: ",
+    farmAnalysisLabel: "Assessment: ",
+    patrolLabel: "Patrol points: ",
+    growthQuestion:
+        "Is fruit diameter significantly smaller than normal, with dull, lackluster skin?",
+    pestQuestion: "Stem borer, lychee stink bug, anthracnose, downy mildew",
+    phenologyQuestion: "Are fruits changing from flat-round to round with clearly increased equatorial diameter?",
+    growthScience:
+        "Lychee is in fruit expansion. Recent heavy rain and convection weather are frequent; rainy weather risk is level 3 with 12 rainy days and ~91% humidity. Low light reduced monthly sunshine by 17% vs normal, lowering photosynthesis and carbohydrate synthesis.",
+    growthPhenotype:
+        "Slow fruit expansion, yellow-green dull skin, fine cracks when severe; wilting leaves curl down; N deficiency yellows old leaves evenly; K burns leaf margins; Mg yellows leaf blade; Ca causes cracking; B poor fruit set; Zn uneven fruit size; Fe bleaches new leaves.",
+    growthHighRisk:
+        "Patrol low-lying poorly drained orchards and inner-canopy fruit. Compare fruit size by aspect; check wilting and chlorosis. Root checks by soil type. After root damage: drain, loosen soil, foliar care first, root fertilizer after new roots. Photograph and archive abnormalities.",
+    pestScience:
+        "Lychee in fruit stage; field pest weather risk level 2; humidity 88%, ~30°C favor pests and pathogens. Fruit stage is critical; thin skin is vulnerable to stem borer, stink bug, downy mildew, anthracnose — causing drop and rot. Intensify scouting and monitoring.",
+    phenologyAnalysis:
+        "Fruit expands horizontally from flat-round toward round; rapid cell division and dry matter accumulation; high N and K demand. Insufficient nutrients limit sizing — apply root fertilizer timely.",
+    phenologyPhenotype:
+        "Fruit widens horizontally, glossy green skin, shoulders fill out.\nPatrol: outer upper fruiting branches; track shape and equatorial diameter; photo and upload.",
+    tabFruitBlocked: "Expansion blocked",
+    tabSlowGrowth: "Slow growth",
+    tabVigorous: "Vigorous growth",
+    tabWilting: "Leaf wilting",
+    drawTip: "Tap to draw new affected area",
+    notReached: "Stage not reached",
+    confirmUpload: "Confirm upload",
+    keyAssessment: "Key nutrition & control assessment: ",
+    phenologyIssue: "Have 60% of lychee trees entered fruit expansion?",
+    currentStatus: "Status: {label} entered {period}",
+    uploadPhoto: "Upload photos",
+    confirmInfo: "Confirm",
+    abnormalBanner: "Report abnormality with photos",
+    abnormalBannerDesc: "Smart farm work recommendations for you",
+    abnormalRecord: "Abnormal log",
+    abnormalCount: "What % of plants show abnormality?",
+    inputPlaceholder: "Enter",
+    patrolDesc: "Patrol notes",
+    inputDescPlaceholder: "Enter description",
+    uploadPhotoFirst: "Please upload photos first",
+    confirmSuccess: "Confirmed",
+    pestDisease: "Disease",
+    pestInsect: "Insect",
+    catFungal: "Fungal",
+    catBorer: "Borer",
+    catVirus: "Viral",
+    catOther: "Other",
+    catChewing: "Chewing",
+    catMasticate: "Chewing type",
+    detDownyMildew: "Downy mildew",
+    detAnthracnose: "Anthracnose",
+    detBlast: "Blast",
+    detSheathBlight: "Sheath blight",
+    detStinkBug: "Lychee stink bug",
+    detRiceBorer: "Rice stem borer",
+    detStemBorer: "Litchi stem borer",
+    pestT1_downy:
+        "Infected fruit shows water-soaked light-green patches; white downy mold in high humidity, then brown soft rot and drop. Rapid spread in expansion to color-change stage.",
+    pestT2_downy:
+        "Patrol low, humid, poorly ventilated clusters; check water-soaked patches and morning mold; squeeze fruit for soft rot and sour smell. Photograph and archive.",
+    pestT1_anthracnose:
+        "Dark brown water-soaked spots expand to sunken anthracnose lesions with pink spore masses in humidity; flesh rots; mummified fruit may remain on tree.",
+    pestT2_anthracnose:
+        "Patrol inner and lower clusters for sunken dark lesions; distinguish from downy mildew (soft rot vs sunken dry lesions). Photograph and archive.",
+    pestT1_stinkbug:
+        "Adults and nymphs suck young fruit and shoots; brown necrotic spots, misshapen fruit, heavy drop. Adults ~25 mm tan shield-shaped; nymphs orange-red in groups.",
+    pestT2_stinkbug:
+        "Patrol upper outer fruiting zones for shield bugs or red nymph masses; shake branches and note odor. Photograph and archive live insects and damage.",
+    pestT1_stemborer:
+        "Small moth larvae bore from stem end into seed cavity; frass inside fruit; late red premature fruit drop.",
+    pestT2_stemborer:
+        "Patrol inner canopy and hanging clusters; collect dropped fruit and split for larvae/frass; check stem-end blackening. Photograph and archive.",
+    pestT1_riceborer: "Larvae bore rice stems at base causing dead hearts with bore holes and frass.",
+    pestT2_riceborer: "Patrol dead-heart patches; split stem bases for larvae, frass, and tunnels. Photograph and archive.",
+};

+ 15 - 0
src/i18n/recordTextMap.js

@@ -0,0 +1,15 @@
+/** 农情/档案/记录详情共用的中文 record 字段 → i18n key */
+export const RECORD_KEY_MAP = {
+    "果园出现果实迅速膨大,占比": "agriFile.recordFruitExpand",
+    "果园出现谢花坐果,占比": "agriFile.recordFruitSet",
+    "果园进入盛花期": "agriFile.recordFullBloom",
+    "果园出现花穗抽生": "agriFile.recordPanicle",
+    "果园出现蒂蛀虫为害果,占比": "agriFile.recordStemBorerFruit",
+    "果园出现缺钙裂果症状,占比": "agriFile.recordCalciumCrack",
+    "果园出现荔枝蝽象若虫为害": "agriFile.recordStinkBug",
+    "果园出现花而不实现象": "agriFile.recordFlowerNoFruit",
+    "执行了膨果壮果追肥农事": "agriFile.recordFertilize",
+    "执行了蒂蛀虫防治农事": "agriFile.recordStemBorerControl",
+    "执行了保花保果药剂喷施农事": "agriFile.recordFlowerFruitProtect",
+    "执行了花前病虫综合防治农事": "agriFile.recordPreFlowerPest",
+};

+ 2 - 0
src/main.js

@@ -15,8 +15,10 @@ import router from "@/router";
 import vue3PhotoPreview from 'vue3-photo-preview';
 import 'vue3-photo-preview/dist/index.css';
 import App from './App.vue'
+import i18nPlugin from "@/plugins/i18n";
 
 const app = createApp(App);
+app.use(i18nPlugin)
 app.use(mock)
     .use(elementIcon)
     .use(installElementPlus)

+ 38 - 12
src/plugins/axios.js

@@ -12,14 +12,16 @@
 import axios from "axios";
 import Qs from "qs";
 import NProgress from "nprogress";
-import {igSuccessUrl} from "../api/config";
+import { igSuccessUrl, base_new_url } from "../api/config";
 import { SET_TOKEN } from "@/store/modules/app/type";
+import { translate } from "@/i18n";
 
 // Full config:  https://github.com/axios/axios#request-config
 // axios.defaults.baseURL = process.env.baseURL || process.env.apiUrl || '';
 // axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
 // axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
 const install = (app, { router, store, opt }) => {
+    const $t = (key, params) => translate(key, params, store?.state?.locale?.locale || "zh");
     let config = {
         Global: true,
         // baseURL: process.env.baseURL || process.env.apiUrl || ""
@@ -56,6 +58,7 @@ const install = (app, { router, store, opt }) => {
             const token = store.getters.token;
             token && (config.headers.token = token);
             config.headers.appId = "wx153ff06f5e0c7a8e"
+            attachApiLang(config);
             // Do something before request is sent
             return config;
         },
@@ -110,41 +113,41 @@ const install = (app, { router, store, opt }) => {
                 let message = "";
                 switch (error.response.status) {
                     case 400:
-                        message = "请求错误";
+                        message = $t("请求错误");
                         break;
                     case 401: {
-                        message = "未授权,请登录";
+                        message = $t("未授权,请登录");
                         router.replace({
                             name: "dev_login",
                         });
                         break;
                     }
                     case 403:
-                        message = "没有权限,拒绝访问";
+                        message = $t("没有权限,拒绝访问");
                         break;
                     case 404:
-                        message = `请求地址出错`;
+                        message = $t("请求地址出错");
                         break;
                     case 408:
-                        message = "请求超时";
+                        message = $t("请求超时");
                         break;
                     case 500:
-                        message = "服务器内部错误";
+                        message = $t("服务器内部错误");
                         break;
                     case 501:
-                        message = "服务未实现";
+                        message = $t("服务未实现");
                         break;
                     case 502:
-                        message = "网关错误";
+                        message = $t("网关错误");
                         break;
                     case 503:
-                        message = "服务不可用";
+                        message = $t("服务不可用");
                         break;
                     case 504:
-                        message = "网关超时";
+                        message = $t("网关超时");
                         break;
                     case 505:
-                        message = "HTTP版本不受支持";
+                        message = $t("HTTP版本不受支持");
                         break;
                     default:
                         break;
@@ -173,6 +176,29 @@ const install = (app, { router, store, opt }) => {
         return false;
     }
 
+    /** 为 base_new_url 接口附加 lang(中文为空,英文为 en) */
+    function attachApiLang(config) {
+        const url = config.url || "";
+        if (!url.startsWith(base_new_url)) return;
+
+        const lang = store.getters["locale/apiLang"] ?? "";
+        const method = (config.method || "get").toLowerCase();
+
+        if (method === "get" || method === "delete") {
+            config.params = { ...(config.params || {}), lang };
+            return;
+        }
+
+        const data = config.data;
+        if (data instanceof FormData) {
+            data.append("lang", lang);
+        } else if (data != null && typeof data === "object" && !Array.isArray(data)) {
+            config.data = { ...data, lang };
+        } else {
+            config.data = { lang };
+        }
+    }
+
 
     const method = {
         post: (url, p, config) => {

+ 10 - 0
src/plugins/i18n.js

@@ -0,0 +1,10 @@
+import { translate } from "@/i18n";
+import store from "@/store";
+
+export default {
+    install(app) {
+        app.config.globalProperties.$store = store;
+        app.config.globalProperties.$t = (key, params) =>
+            translate(key, params, store.state.locale?.locale || "zh");
+    },
+};

+ 31 - 0
src/store/modules/locale/index.js

@@ -0,0 +1,31 @@
+const LOCALE_KEY = "app_locale";
+
+export default {
+    namespaced: true,
+    state: {
+        locale: localStorage.getItem(LOCALE_KEY) || "zh",
+    },
+    getters: {
+        /** base_new_url 接口 lang 参数:中文为空,英文为 en */
+        apiLang: (state) => (state.locale === "en" ? "en" : ""),
+    },
+    mutations: {
+        SET_LOCALE(state, locale) {
+            state.locale = locale;
+            localStorage.setItem(LOCALE_KEY, locale);
+        },
+        TOGGLE_LOCALE(state) {
+            const next = state.locale === "zh" ? "en" : "zh";
+            state.locale = next;
+            localStorage.setItem(LOCALE_KEY, next);
+        },
+    },
+    actions: {
+        toggleLocale({ commit }) {
+            commit("TOGGLE_LOCALE");
+        },
+        setLocale({ commit }, locale) {
+            commit("SET_LOCALE", locale);
+        },
+    },
+};

+ 3 - 1
src/utils/back/DialogModel1.vue

@@ -9,7 +9,7 @@
   >
     <div class="dialog-box">
       <div class="title">
-        <div class="name">农事方案</div>
+        <div class="name">{{ t('农事方案') }}</div>
         <div class="close cursor-pointer" @click="closeDialog"></div>
       </div>
       <div class="my-body"></div>
@@ -18,6 +18,8 @@
 </template>
 
 <script setup>
+import { useI18n } from "@/i18n";
+const { t } = useI18n();
 import {reactive, ref, toRefs, computed, nextTick, onMounted} from "vue";
 import {useStore} from "vuex";
 import {WKT} from "ol/format";

+ 17 - 15
src/views/old_mini/achievement_report/index.vue

@@ -1,13 +1,13 @@
 <template>
     <div class="achievement-report-page">
-        <custom-header name="生成成果报告"></custom-header>
+        <custom-header :name="$t('生成成果报告')"></custom-header>
         <div class="report-content-wrap" v-loading="loading">
             <div class="report-content" ref="reportDom">
                 <img src="@/assets/img/home/qrcode.png" alt="" class="code-icon" />
                 <div class="report-header">
                     <img class="header-book" src="@/assets/img/home/book.png" alt="" />
                     <div class="time-tag">{{ workItem?.executeDate }}</div>
-                    <div class="report-title">成果报告</div>
+                    <div class="report-title">{{ t('成果报告') }}</div>
                     <div class="report-info">
                         <div class="info-item">
                             <img class="info-icon" :src="workItem?.executorIcon || 'https://birdseye-img.sysuimars.com/dinggou-mini/defalut-icon.png'" alt="" />
@@ -23,39 +23,39 @@
                 <div class="report-box">
                     <div class="report-box-item">
                         <div class="item-content">{{ workItem?.farmWorkName || "--" }}</div>
-                        <div class="item-title">作业农事</div>
+                        <div class="item-title">{{ t('作业农事') }}</div>
                     </div>
                     <div class="report-box-item">
                         <div class="item-content">{{ formatArea(workItem?.area) || "--" }}</div>
-                        <div class="item-title">作业面积</div>
+                        <div class="item-title">{{ t('作业面积') }}</div>
                     </div>
                     <div class="report-box-item">
                         <div class="item-content">{{ reportData?.workCycle ? (reportData?.workCycle + '天') : "--" }}</div>
-                        <div class="item-title">作业周期</div>
+                        <div class="item-title">{{ t('作业周期') }}</div>
                     </div>
                     <div class="report-box-item">
                         <div class="item-content">{{ reportData?.deviceName || "--" }}</div>
-                        <div class="item-title">使用设备</div>
+                        <div class="item-title">{{ t('使用设备') }}</div>
                     </div>
                 </div>
 
                 <!-- <div class="report-box">
-                    <div class="box-title">精准施治,智慧护航</div>
+                    <div class="box-title">{{ t('精准施治,智慧护航') }}</div>
                     <div class="box-text">
                         {{ reportData?.resultInfo || "--" }}
                     </div>
                 </div> -->
 
                 <div class="report-excute" v-for="(item, index) in workItem?.executeEvidence" :key="index">
-                    <div class="tag-label">执行照片</div>
+                    <div class="tag-label">{{ t('执行照片') }}</div>
                     <album-draw-box :key="paramsPage.id" :isShowNum="0" :imgData="workItem" :photo="item" :current="index" :index="index" :length="workItem?.executeEvidence?.length"></album-draw-box>
                 </div>
                 <div class="report-excute" v-for="(item, index) in workItem?.reviewImage" :key="index">
-                    <div class="tag-label">复核照片</div>
+                    <div class="tag-label">{{ t('复核照片') }}</div>
                     <album-draw-box :key="paramsPage.id+'复核'" :isShowNum="0" :isAchievementImgs="true" :imgData="workItem" :photo="item" :current="index" :index="index" :length="workItem?.reviewImage?.length"></album-draw-box>
                 </div>
                 <!-- <div class="report-excute" v-for="(item, index) in workItem?.reviewImage" :key="index">
-                    <div class="tag-label">复核照片</div>
+                    <div class="tag-label">{{ t('复核照片') }}</div>
                     <album-draw-box :isShowNum="0" :photo="item" :current="index" :index="'review'+index" :length="workItem?.reviewImage?.length"></album-draw-box>
                 </div> -->
                 <!-- <div class="report-excute">
@@ -68,8 +68,8 @@
             </div>
 
             <div class="bottom-btn">
-                <div class="btn-item second" @click="handleDownload">保存图片</div>
-                <div class="btn-item primay" @click="handleDownload">转发</div>
+                <div class="btn-item second" @click="handleDownload">{{ t('保存图片') }}</div>
+                <div class="btn-item primay" @click="handleDownload">{{ t('转发') }}</div>
             </div>
         </div>
 
@@ -77,7 +77,7 @@
         <div class="review-hide-box">
             <div class="review-image" ref="reviewComboRef">
                 <div class="review-mask">
-                    <div class="review-text">复核成效</div>
+                    <div class="review-text">{{ t('复核成效') }}</div>
                     <div class="review-content">
                         {{ workItem?.reCheckText }}
                     </div>
@@ -86,7 +86,7 @@
                     <img src="@/assets/img/home/vs.png" alt="" />
                 </div>
                 <div class="review-image-item" v-if="workItem?.executeEvidence?.length">
-                    <div class="review-image-item-title">复核照片</div>
+                    <div class="review-image-item-title">{{ t('复核照片') }}</div>
                     <img
                         class="review-image-item-img left-img"
                         :src="leftCoverImg"
@@ -116,12 +116,14 @@
                     &lt;&lt;长按图片保存或转发&gt;&gt;
                 </div>
             </div>
-            <div class="cancel-btn" @click="handleCancel">取消</div>
+            <div class="cancel-btn" @click="handleCancel">{{ t('取消') }}</div>
         </div>
     </popup>
 </template>
 
 <script setup>
+import { useI18n } from "@/i18n";
+const { t } = useI18n();
 import CustomHeader from "@/components/customHeader.vue";
 import AlbumCarousel from "@/components/album_compoents/albumCarousel";
 import { ref, onActivated, onDeactivated, onUnmounted, nextTick, watch } from "vue";

+ 58 - 108
src/views/old_mini/agri_file/components/fileFloat.vue

@@ -1,24 +1,24 @@
 <template>
-    <!-- <div class="add-btn">点击新建管理分区</div> -->
+    <!-- <div class="add-btn">{{ t('点击新建管理分区') }}</div> -->
     <floating-panel class="file-float-panel" :class="{ 'custom-panel': height === anchors[0] }" v-model:height="height"
         :anchors="anchors">
         <div class="file-float-content">
             <div class="float-tabs">
                 <div class="tab-active-bg" :style="activeBgStyle"></div>
-                <div v-for="(item, index) in floatTabLabels" :key="index" class="tab-item"
+                <div v-for="(item, index) in floatTabLabels" :key="item.value" class="tab-item"
                     @click="changeTab(item, index)" :class="{ 'tab-item-active': activeTab === index }">
                     {{ item.title }}
                 </div>
             </div>
 
             <div class="tab-content-group" v-show="height !== anchors[0]">
-                <!-- <div class="tab-loading" v-if="isCurrentTabLoading">加载中...</div>
-                <div class="tab-empty" v-else-if="currentList.length === 0">暂无记录</div> -->
-                <div class="tab-content-item" v-for="item in currentList" :key="item.id">
+                <!-- <div class="tab-loading" v-if="isCurrentTabLoading">{{ t('加载中...') }}</div>
+                <div class="tab-empty" v-else-if="currentList.length === 0">{{ t('暂无记录') }}</div> -->
+                <div class="tab-content-item" v-for="item in displayList" :key="item.id">
                     <div class="time-tag">{{ item.time }}</div>
                     <div class="item-info">
-                        {{ item.record }} <span class="blue-text">{{ item.ratio }}{{ (floatTabLabels[activeTab].value !==
-                            'farming' && item.ratio.length) ? '%' : '' }}</span>
+                        {{ item.recordText }}
+                        <span class="blue-text">{{ item.ratio }}{{ item.showRatio ? '%' : '' }}</span>
                     </div>
                 </div>
             </div>
@@ -27,11 +27,14 @@
 </template>
 
 <script setup>
-
+import { useI18n } from "@/i18n";
+import { RECORD_KEY_MAP } from "@/i18n/recordTextMap";
 import { FloatingPanel } from 'vant';
-import { computed, ref, onMounted } from 'vue';
+import { computed, ref, onMounted, watch } from 'vue';
 
+const { t, locale } = useI18n();
 const emit = defineEmits(['map-farm-record']);
+
 const anchors = [
     130,
     Math.round(0.4 * window.innerHeight),
@@ -39,23 +42,28 @@ const anchors = [
 ];
 const height = ref(anchors[0]);
 
-const floatTabLabels = [{
-    title: "物候记录",
-    value: "phenology",
-}, {
-    title: "异常记录",
-    value: "abnormal",
-}, {
-    title: "农事记录",
-    value: "farming",
-}];
+const floatTabLabels = computed(() => [
+    { title: t("agriFile.tabPhenology"), value: "phenology" },
+    { title: t("agriFile.tabAbnormal"), value: "abnormal" },
+    { title: t("agriFile.tabFarming"), value: "farming" },
+]);
 
 const currentList = ref([]);
 const activeTab = ref(0);
+const activeTabValue = computed(() => floatTabLabels.value[activeTab.value]?.value || "phenology");
+
+const displayList = computed(() =>
+    currentList.value.map((item) => ({
+        ...item,
+        recordText: t(RECORD_KEY_MAP[item.record] || item.record),
+        showRatio: activeTabValue.value !== "farming" && String(item.ratio ?? "").length > 0,
+    }))
+);
+
 const syncMapFarmRecord = () => {
-    emit('map-farm-record', {
+    emit("map-farm-record", {
         polygonWkt: mapPolygonData.value,
-        records: currentList.value,
+        records: displayList.value,
     });
 };
 
@@ -69,6 +77,9 @@ const activeBgStyle = computed(() => ({
     transform: `translateX(${activeTab.value * 100}%)`,
 }));
 
+watch(locale, () => {
+    syncMapFarmRecord();
+});
 
 onMounted(() => {
     getFarmRecord();
@@ -77,103 +88,40 @@ onMounted(() => {
 const mapPolygonData = ref("");
 const farmRecordData = ref({});
 const getFarmRecord = async () => {
-    // const res = await VE_API.monitor.getFarmRecord();
     const res = {
-        "code": 200,
-        "data": {
-            "abnormal": [
-                {
-                    "id": 2,
-                    "ratio": "5",
-                    "record": "果园出现蒂蛀虫为害果,占比",
-                    "time": "05-12"
-                },
-                {
-                    "id": 3,
-                    "ratio": "8",
-                    "record": "果园出现缺钙裂果症状,占比",
-                    "time": "05-12"
-                },
-                {
-                    "id": 4,
-                    "ratio": "",
-                    "record": "果园出现荔枝蝽象若虫为害",
-                    "time": "05-03"
-                },
-                {
-                    "id": 1,
-                    "ratio": "",
-                    "record": "果园出现花而不实现象",
-                    "time": "04-15"
-                }
+        code: 200,
+        data: {
+            abnormal: [
+                { id: 2, ratio: "5", record: "果园出现蒂蛀虫为害果,占比", time: "05-12" },
+                { id: 3, ratio: "8", record: "果园出现缺钙裂果症状,占比", time: "05-12" },
+                { id: 4, ratio: "", record: "果园出现荔枝蝽象若虫为害", time: "05-03" },
+                { id: 1, ratio: "", record: "果园出现花而不实现象", time: "04-15" },
             ],
-            "farming": [
-                {
-                    "id": 2,
-                    "ratio": "",
-                    "record": "执行了膨果壮果追肥农事",
-                    "time": "05-07"
-                },
-                {
-                    "id": 3,
-                    "ratio": "",
-                    "record": "执行了蒂蛀虫防治农事",
-                    "time": "05-03"
-                },
-                {
-                    "id": 4,
-                    "ratio": "",
-                    "record": "执行了保花保果药剂喷施农事",
-                    "time": "04-10"
-                },
-                {
-                    "id": 1,
-                    "ratio": "",
-                    "record": "执行了花前病虫综合防治农事",
-                    "time": "03-15"
-                }
+            farming: [
+                { id: 2, ratio: "", record: "执行了膨果壮果追肥农事", time: "05-07" },
+                { id: 3, ratio: "", record: "执行了蒂蛀虫防治农事", time: "05-03" },
+                { id: 4, ratio: "", record: "执行了保花保果药剂喷施农事", time: "04-10" },
+                { id: 1, ratio: "", record: "执行了花前病虫综合防治农事", time: "03-15" },
             ],
-            "phenology": [
-                {
-                    "id": 2,
-                    "ratio": "65",
-                    "record": "果园出现果实迅速膨大,占比",
-                    "time": "05-10"
-                },
-                {
-                    "id": 3,
-                    "ratio": "80",
-                    "record": "果园出现谢花坐果,占比",
-                    "time": "04-25"
-                },
-                {
-                    "id": 4,
-                    "ratio": "",
-                    "record": "果园进入盛花期",
-                    "time": "03-20"
-                },
-                {
-                    "id": 1,
-                    "ratio": "100",
-                    "record": "果园出现花穗抽生",
-                    "time": "02-15"
-                }
+            phenology: [
+                { id: 2, ratio: "65", record: "果园出现果实迅速膨大,占比", time: "05-10" },
+                { id: 3, ratio: "80", record: "果园出现谢花坐果,占比", time: "04-25" },
+                { id: 4, ratio: "", record: "果园进入盛花期", time: "03-20" },
+                { id: 1, ratio: "100", record: "果园出现花穗抽生", time: "02-15" },
             ],
-            "polygon":[
+            polygon: [
                 "MULTIPOLYGON (((110.48938292603424 21.417825047316228, 110.4899730323636 21.417990023279287, 110.49068052543606 21.417796493784067, 110.49084867401376 21.41706679240906, 110.49043306149144 21.415388479246303, 110.48927822974991 21.415515383833224, 110.48873888525532 21.416276811355146, 110.48938292603424 21.417825047316228)))",
                 "MULTIPOLYGON (((110.48996192821238 21.41538768609246, 110.49012614274784 21.415411734511736, 110.4903759544273 21.415328199567455, 110.4905258604706 21.415064079395847, 110.4906400745989 21.41417654044062, 110.49084470824545 21.413217617655164, 110.49126349338252 21.41276314060309, 110.49141339942582 21.412484743665402, 110.49060914160583 21.412625131864786, 110.49000713797136 21.41311768029294, 110.48996192821238 21.41538768609246)))",
                 "MULTIPOLYGON (((110.49144762747073 21.417923243516555, 110.49146967053326 21.417909231135127, 110.49165526849174 21.41746823769546, 110.49183769383541 21.416946342581525, 110.49196777103703 21.416824196916536, 110.49210815923641 21.416644944187453, 110.4923865561741 21.416283266114533, 110.49282437699918 21.415975522491067, 110.49311308493458 21.415770095690846, 110.49327568143673 21.415638432181936, 110.49335023788149 21.41553373589761, 110.49344541632172 21.41515064267577, 110.49345255470479 21.414922214419164, 110.49277207842925 21.414636431237852, 110.49198459250442 21.414455575677493, 110.49168240095673 21.414377846617867, 110.49167605572734 21.414614206411272, 110.49148411253958 21.414625310562485, 110.4913199297302 21.414685590241334, 110.4913104118861 21.414970332408416, 110.49127789258574 21.415159896135265, 110.49120730190918 21.415339148864177, 110.49111212346895 21.41546288083657, 110.49093366389366 21.415717483164258, 110.49088924728812 21.415978430721168, 110.49090193774674 21.416193375365253, 110.49094714750589 21.41630679633994, 110.49102963548751 21.41653443144287, 110.49111767554473 21.4168675559838, 110.49114940169142 21.41712136515764, 110.49114860853774 21.41767498641832, 110.4913286544205 21.41780585677361, 110.49144762747073 21.417923243516555)))",
-                // "POLYGON ((110.48760606287757 21.42397019891643, 110.4878409330982 21.423999557694007, 110.48775285676547 21.423823405028536, 110.48760606287757 21.42397019891643))"
-            ]
-        }
-    }
+            ],
+        },
+    };
     if (res.code === 200) {
         mapPolygonData.value = res.data.polygon;
         farmRecordData.value = res.data || {};
-        changeTab(floatTabLabels[0], 0);
+        changeTab(floatTabLabels.value[0], 0);
     }
-}
-
+};
 </script>
 
 <style lang="scss" scoped>
@@ -282,6 +230,8 @@ const getFarmRecord = async () => {
                 height: 21px;
                 line-height: 21px;
                 padding: 0 6px;
+                width: 46px;
+                box-sizing: border-box;
             }
 
             .item-info {
@@ -295,4 +245,4 @@ const getFarmRecord = async () => {
         }
     }
 }
-</style>
+</style>

+ 15 - 13
src/views/old_mini/agri_file/index.vue

@@ -9,10 +9,10 @@
             <template #types-content>
                 <div class="type-tabs">
                     <div class="type-item" @click="changeType('荔枝')"
-                        :class="{ 'type-item-active': activeType === '荔枝' }">荔枝</div>
-                    <!-- <div class="type-item" @click="changeType('水稻')" :class="{ 'type-item-active': activeType === '水稻' }">水稻</div> -->
-                    <!-- <div class="type-item" @click="changeType('柑橘')" :class="{ 'type-item-active': activeType === '柑橘' }">柑橘</div> -->
-                    <!-- <div class="type-item" @click="changeType('小麦')" :class="{ 'type-item-active': activeType === '小麦' }">小麦</div> -->
+                        :class="{ 'type-item-active': activeType === '荔枝' }">{{ $t('荔枝') }}</div>
+                    <!-- <div class="type-item" @click="changeType('水稻')" :class="{ 'type-item-active': activeType === '水稻' }">{{ $t('水稻') }}</div> -->
+                    <!-- <div class="type-item" @click="changeType('柑橘')" :class="{ 'type-item-active': activeType === '柑橘' }">{{ $t('柑橘') }}</div> -->
+                    <!-- <div class="type-item" @click="changeType('小麦')" :class="{ 'type-item-active': activeType === '小麦' }">{{ $t('小麦') }}</div> -->
                 </div>
             </template>
         </weather-info>
@@ -24,7 +24,7 @@
 
         <div class="file-content" v-show="activeGardenTab === 'current'">
             <div class="map-legend">
-                <div v-for="item in mapLegendItems" :key="item.label" class="map-legend__item">
+                <div v-for="item in mapLegendItems" :key="item.key" class="map-legend__item">
                     <span class="map-legend__dot" :class="item.dotClass"></span>
                     <span class="map-legend__text">{{ item.label }}</span>
                 </div>
@@ -43,7 +43,9 @@ import weatherInfo from "@/components/weatherInfo.vue";
 import gardenList from "@/components/gardenList.vue";
 import fileFloat from "./components/fileFloat.vue";
 import FileMap from "./fileMap.js";
+import { useI18n } from "@/i18n";
 
+const { t } = useI18n();
 const store = useStore();
 const route = useRoute();
 const tabBarHeight = computed(() => store.state.home.tabBarHeight);
@@ -56,16 +58,16 @@ const gardenListRef = ref(null);
 const activeGardenTab = ref("current");
 const activeType = ref("荔枝");
 
-const mapLegendItems = [
-    { label: "管理分区", dotClass: "map-legend__dot--zone" },
-    { label: "长势异常", dotClass: "map-legend__dot--growth" },
-    { label: "病虫害异常", dotClass: "map-legend__dot--pest" },
-];
+const mapLegendItems = computed(() => [
+    { key: "zone", label: t("agriFile.legendZone"), dotClass: "map-legend__dot--zone" },
+    { key: "growth", label: t("agriFile.legendGrowth"), dotClass: "map-legend__dot--growth" },
+    { key: "pest", label: t("agriFile.legendPest"), dotClass: "map-legend__dot--pest" },
+]);
 
 const fileMap = new FileMap();
 
 const onFarmRecordMap = ({ polygonWkt, records }) => {
-    fileMap.setFarmRecordOverlay?.(polygonWkt, records);
+    // fileMap.setFarmRecordOverlay?.(polygonWkt, records);
 };
 
 const weatherExpanded = (isExpandedValue) => {
@@ -168,8 +170,8 @@ onActivated(() => {
             display: flex;
             align-items: center;
             justify-content: center;
-            gap: 10px;
-            padding: 3px 10px;
+            gap: 6px;
+            padding: 3px 5px;
             background: rgba(255, 255, 255, 0.69);
             border-radius: 25px;
         }

+ 3 - 1
src/views/old_mini/agri_record/components/mapInfo.vue

@@ -1,6 +1,6 @@
 <template>
     <div class="map-info">
-        <!-- <div class="popup-title" v-if="showTitle">执行区域</div> -->
+        <!-- <div class="popup-title" v-if="showTitle">{{ t('执行区域') }}</div> -->
         <div class="map-box">
             <div class="map" ref="mapContainer"></div>
         </div>
@@ -8,6 +8,8 @@
 </template>
 
 <script setup>
+import { useI18n } from "@/i18n";
+const { t } = useI18n();
 import { ref, nextTick, onMounted, watch } from "vue";
 import IndexMap from "@/views/old_mini/home/map/index.js";
 import AreaMap from "./areaMap.js";

+ 48 - 10
src/views/old_mini/agri_record/index.vue

@@ -1,5 +1,5 @@
 <template>
-    <custom-header v-if="isHeaderShow" name="农场详情"></custom-header>
+    <custom-header v-if="isHeaderShow" :name="t('agriRecord.farmDetail')"></custom-header>
     <div class="monitor-index" :style="{ height: `calc(100vh - ${tabBarHeight}px)` }">
         <!-- 天气遮罩 -->
         <div class="weather-mask" v-show="isExpanded" @click="handleMaskClick"></div>
@@ -16,13 +16,13 @@
         <div ref="archivesScrollAreaRef" class="archives-time-line" v-show="activeGardenTab === 'current'">
             <div class="trend-monitor-list">
                 <div class="trend-monitor-card" @click="handleTrendMonitorCardClick(item)"
-                    v-for="(item, index) in trendMonitorMockList" :key="index">
+                    v-for="(item, index) in displayTrendMonitorList" :key="index">
                     <div class="card-header">
                         <div class="header-left">
                             <span class="title">{{ item?.first_work?.work_name }}</span>
-                            <span class="level-tag">二级</span>
+                            <span class="level-tag">{{ t('agriRecord.level2') }}</span>
                         </div>
-                        <div class="status-tag" v-if="item?.risk_level">待记录</div>
+                        <div class="status-tag" v-if="item?.risk_level">{{ t('agriRecord.pendingRecord') }}</div>
                     </div>
                     <div class="card-row">
                         <div class="reason-text">{{ item?.first_work?.work_reason_short }}</div>
@@ -43,6 +43,26 @@
 </template>
 
 <script setup>
+import { useI18n } from "@/i18n";
+const { t, locale } = useI18n();
+
+const WORK_I18N_KEYS = {
+    growth: {
+        workName: "agriRecord.growthWorkName",
+        reason: "agriRecord.growthReason",
+        issue: "agriRecord.growthIssue",
+    },
+    pest: {
+        workName: "agriRecord.pestWorkName",
+        reason: "agriRecord.pestReason",
+        issue: "agriRecord.pestIssue",
+    },
+    phenology: {
+        workName: "agriRecord.phenologyWorkName",
+        reason: "agriRecord.phenologyReason",
+        issue: "agriRecord.phenologyIssue",
+    },
+};
 import customHeader from "@/components/customHeader.vue";
 import { ref, computed, onActivated, onDeactivated, watch, nextTick } from "vue";
 import { useStore } from "vuex";
@@ -89,6 +109,23 @@ const selectedGardenId = ref(null);
 const gardenListRef = ref(null);
 const activeGardenTab = ref('current');
 const trendMonitorMockList = ref([]);
+
+const displayTrendMonitorList = computed(() =>
+    trendMonitorMockList.value.map((item) => {
+        const keys = WORK_I18N_KEYS[item.workType];
+        if (!keys || !item.first_work) return item;
+        return {
+            ...item,
+            first_work: {
+                ...item.first_work,
+                work_name: t(keys.workName),
+                work_reason_short: t(keys.reason),
+                interaction_issue: t(keys.issue),
+            },
+        };
+    })
+);
+
 const changeGardenTab = (tab) => {
     activeGardenTab.value = tab;
 };
@@ -131,11 +168,12 @@ const getFarmRiskAndTracking = async () => {
         }
     }
     if (res.code === 200) {
-        trendMonitorMockList.value.push(res.data?.growth_abnormal_tracking);
-        trendMonitorMockList.value.push(res.data?.pest_risk_assessment);
-        if (res.data?.phenology_tracking) {
-            trendMonitorMockList.value.push(res.data?.phenology_tracking);
-        }
+        const growth = res.data?.growth_abnormal_tracking;
+        const pest = res.data?.pest_risk_assessment;
+        const phenology = res.data?.phenology_tracking;
+        if (growth) trendMonitorMockList.value.push({ ...growth, workType: "growth" });
+        if (pest) trendMonitorMockList.value.push({ ...pest, workType: "pest" });
+        if (phenology) trendMonitorMockList.value.push({ ...phenology, workType: "phenology" });
     }
 }
 
@@ -227,7 +265,7 @@ const handleTrendMonitorCardClick = (item) => {
     blockArchivesScrollSaveOnce.value = true;
     router.push({
         path: "/record_details",
-        query: { workId: item.work_id, type: item.first_work.work_name },
+        query: { workId: item.work_id, type: item.workType },
     });
 };
 </script>

+ 12 - 10
src/views/old_mini/agri_record/subPages/prescriptionPage.vue

@@ -1,6 +1,6 @@
 <template>
     <div class="prescription-page">
-        <custom-header name="处方详情" isClose :showClose="false" @goback="handleClose"></custom-header>
+        <custom-header :name="$t('处方详情')" isClose :showClose="false" @goback="handleClose"></custom-header>
 
         <div class="prescription-content">
             <div class="card-box content-card">
@@ -13,15 +13,15 @@
 
                 <div class="card-info">
                     <div class="info-item">
-                        <span class="info-label">下发专家</span>
+                        <span class="info-label">{{ t('下发专家') }}</span>
                         <span class="info-value">{{ prescriptionData.expert }}</span>
                     </div>
                     <div class="info-item">
-                        <span class="info-label">执行时间</span>
+                        <span class="info-label">{{ t('执行时间') }}</span>
                         <span class="info-value">{{ prescriptionData.executeTime }}</span>
                     </div>
                     <div class="info-item">
-                        <span class="info-label">施用方式</span>
+                        <span class="info-label">{{ t('施用方式') }}</span>
                         <span class="info-value">{{ prescriptionData.applyMethod }}</span>
                     </div>
                 </div>
@@ -37,11 +37,11 @@
 
                 <!-- <div class="table-item">
                     <div class="form-item">
-                        <div class="item-name">施用方式</div>
+                        <div class="item-name">{{ t('施用方式') }}</div>
                         <div class="item-text">{{ detailData?.usageMode || '其他' }}</div>
                     </div>
                     <div class="form-item">
-                        <div class="item-name">执行方式</div>
+                        <div class="item-name">{{ t('执行方式') }}</div>
                         <div class="item-text">
                             {{ quotationData?.executionMethodName }}
                         </div>
@@ -51,16 +51,16 @@
                     v-if="detailData?.prescriptionList?.length && detailData?.prescriptionList[0]?.pesticideFertilizerList?.length > 0">
                     <div class="new-title">
                         <div class="title-1">
-                            <div class="table-name">药肥类型</div>
+                            <div class="table-name">{{ t('药肥类型') }}</div>
                         </div>
                         <div class="title-2">
-                            <div class="table-name">药肥名称</div>
+                            <div class="table-name">{{ t('药肥名称') }}</div>
                         </div>
                         <div class="title-4">
-                            <div class="table-name">药肥配比</div>
+                            <div class="table-name">{{ t('药肥配比') }}</div>
                         </div>
                         <div class="title-5">
-                            <div class="table-name">单亩用量</div>
+                            <div class="table-name">{{ t('单亩用量') }}</div>
                         </div>
                     </div>
                     <div class="new-table-wrap"
@@ -103,6 +103,8 @@
 </template>
 
 <script setup>
+import { useI18n } from "@/i18n";
+const { t } = useI18n();
 import { ref, onMounted } from "vue";
 import { useRouter, useRoute } from "vue-router";
 import customHeader from "@/components/customHeader.vue";

+ 37 - 35
src/views/old_mini/agri_record/subPages/statusDetail.vue

@@ -1,25 +1,25 @@
 <template>
     <div class="status-detail">
-        <custom-header name="农事详情" isGoBack @goback="handleClose"></custom-header>
+        <custom-header :name="$t('农事详情')" isGoBack @goback="handleClose"></custom-header>
 
         <div class="detail-content" :class="{ 'has-group': isLocked }">
             <!-- 橙色状态头部 -->
             <div class="status-header" :class="'status-' + status">
                 <!-- <div class="status-left" v-if="status === -1">
                     <div class="status-text">
-                        <span>未执行</span>
+                        <span>{{ t('未执行') }}</span>
                     </div>
-                    <div class="status-sub-text">执行时间已过去 7 天</div>
+                    <div class="status-sub-text">{{ t('执行时间已过去 7 天') }}</div>
                 </div> -->
                 <div class="status-left" v-if="status === 5">
                     <div class="status-text">
-                        <span>已完成</span>
+                        <span>{{ t('已完成') }}</span>
                     </div>
                     <div class="status-sub-text">执行时间:{{ detailData?.detail?.executeDate }}</div>
                 </div>
                  <div class="status-left" v-else-if="status === 0 || !status">
                      <div class="status-text">
-                         <span>未激活</span>
+                         <span>{{ t('未激活') }}</span>
                      </div>
                      <div class="status-sub-text">
                          预计激活时间 {{ detailData?.activateTime ? formatDate(detailData.activateTime) : "" }}
@@ -27,11 +27,11 @@
                  </div>
                 <div class="status-left" v-else>
                     <div class="status-text">
-                        <span>已激活</span>
+                        <span>{{ t('已激活') }}</span>
                     </div>
                     <div class="status-sub-text" v-if="daysDiff > 0">距离执行时间还差 {{ daysDiff }} 天</div>
-                    <div class="status-sub-text" v-else-if="daysDiff === 0">触发时间为今天</div>
-                    <div class="status-sub-text" v-else>触发时间已过</div>
+                    <div class="status-sub-text" v-else-if="daysDiff === 0">{{ t('触发时间为今天') }}</div>
+                    <div class="status-sub-text" v-else>{{ t('触发时间已过') }}</div>
                 </div>
             </div>
 
@@ -43,20 +43,20 @@
                             <div class="card-title">{{ detailData?.detail?.farmWorkName }}</div>
                             <div class="type-tag">{{ typeMap[detailData?.detail?.farmWorkType || 1] }}</div>
                         </div>
-                        <!-- <div class="forward-link" @click="handleForward">转发处方</div> -->
+                        <!-- <div class="forward-link" @click="handleForward">{{ t('转发处方') }}</div> -->
                     </div>
 
                     <div class="card-info">
                         <div class="info-item">
-                            <span class="info-label">下发专家</span>
+                            <span class="info-label">{{ t('下发专家') }}</span>
                             <span class="info-value">{{ detailData?.expertNameFromFarmBasicInfo }}</span>
                         </div>
                         <div class="info-item">
-                            <span class="info-label">执行时间</span>
+                            <span class="info-label">{{ t('执行时间') }}</span>
                             <span class="info-value">{{ detailData?.detail?.executeDate }}</span>
                         </div>
                         <div class="info-item">
-                            <span class="info-label">农事建议</span>
+                            <span class="info-label">{{ t('农事建议') }}</span>
                             <span class="info-value">{{ detailData?.detail?.purpose }}</span>
                         </div>
                     </div>
@@ -78,23 +78,23 @@
                             v-show="showPrescription && detailData?.detail?.prescriptionList?.length && detailData?.detail?.prescriptionList[0]?.pesticideFertilizerList?.length > 0">
 
                             <div class="info-item pb-8">
-                                <span class="info-label">施用方式</span>
+                                <span class="info-label">{{ t('施用方式') }}</span>
                                 <span class="info-value">{{ detailData?.detail?.usageMode }}</span>
                             </div>
 
                             <div class="new-wrap-inner">
                                 <div class="new-title">
                                     <div class="title-1">
-                                        <div class="table-name">药肥类型</div>
+                                        <div class="table-name">{{ t('药肥类型') }}</div>
                                     </div>
                                     <div class="title-2">
-                                        <div class="table-name">药肥名称</div>
+                                        <div class="table-name">{{ t('药肥名称') }}</div>
                                     </div>
                                     <div class="title-4">
-                                        <div class="table-name">药肥配比</div>
+                                        <div class="table-name">{{ t('药肥配比') }}</div>
                                     </div>
                                     <div class="title-5">
-                                        <div class="table-name">单亩用量</div>
+                                        <div class="table-name">{{ t('单亩用量') }}</div>
                                     </div>
                                 </div>
                                 <div class="new-table-wrap"
@@ -142,9 +142,9 @@
                 <div class="card-box action-video">
                     <div class="card-header">
                         <div class="card-title-wrap">
-                            <div class="card-title">执行操作</div>
+                            <div class="card-title">{{ t('执行操作') }}</div>
                         </div>
-                        <!-- <div class="forward-link" @click="handleForward">转发操作指南</div> -->
+                        <!-- <div class="forward-link" @click="handleForward">{{ t('转发操作指南') }}</div> -->
                     </div>
                     <div class="video-wrap">
                         <photo-provider :photo-closable="true" v-if="guideSrc">
@@ -173,32 +173,32 @@
                 <!-- <div class="card-box execution-file">
                     <div class="card-header">
                         <div class="card-title-wrap">
-                            <div class="card-title">执行档案</div>
+                            <div class="card-title">{{ t('执行档案') }}</div>
                         </div>
-                        <div class="forward-link" @click="handleShowQrCodePopup(1)">邀请拍照</div>
+                        <div class="forward-link" @click="handleShowQrCodePopup(1)">{{ t('邀请拍照') }}</div>
                     </div>
                     <div class="exe-upload">
                         <upload :maxCount="10" ref="uploadRef" exampleImg class="upload-wrap" @handleUpload="handleUpload">
                             <img class="example" src="@/assets/img/home/plus.png" alt="" />
                         </upload>
                     </div>
-                    <div class="no-text">暂未检测到拍照</div>
+                    <div class="no-text">{{ t('暂未检测到拍照') }}</div>
                 </div> -->
 
                 <!-- 执行轨迹 -->
                 <!-- <div class="card-box execution-file">
                     <div class="card-header">
                         <div class="card-title-wrap">
-                            <div class="card-title">执行轨迹</div>
+                            <div class="card-title">{{ t('执行轨迹') }}</div>
                         </div>
-                        <div class="forward-link" @click="handleShowQrCodePopup(2)">转发执行二维码</div>
+                        <div class="forward-link" @click="handleShowQrCodePopup(2)">{{ t('转发执行二维码') }}</div>
                     </div>
                     <div class="exe-upload">
                         <upload :maxCount="10" :key="2" ref="uploadRef2" exampleImg class="upload-wrap" @handleUpload="handleUpload2">
                             <img class="example" src="@/assets/img/home/plus.png" alt="" />
                         </upload>
                     </div>
-                    <div class="no-text">暂未检测到工人执行</div>
+                    <div class="no-text">{{ t('暂未检测到工人执行') }}</div>
                 </div> -->
             </div>
 
@@ -207,8 +207,8 @@
                 <div class="group-left">
                     <el-avatar :size="34" src="https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png" />
                     <div class="group-name">
-                        <div class="name-text">加入冼老师种植群</div>
-                        <div class="group-desc">每日农情打卡,享一对一专家指导</div>
+                        <div class="name-text">{{ t('加入冼老师种植群') }}</div>
+                        <div class="group-desc">{{ t('每日农情打卡,享一对一专家指导') }}</div>
                     </div>
                 </div>
                 <div class="group-right">
@@ -224,7 +224,7 @@
 
              <!-- 我已执行 -->
              <!-- <div class="bottom-btn-wrap">
-                <div class="bottom-btn" @click="selectExecuteTime">我已执行</div>
+                <div class="bottom-btn" @click="selectExecuteTime">{{ t('我已执行') }}</div>
              </div> -->
         </div>
 
@@ -237,12 +237,12 @@
                 <div class="text-wrap">
                     <div class="qr-code-text">
                         此二维码为
-                        <span v-if="qrCodeType === 1"><span class="work-name">梢期杀虫</span> 上传执行照片邀请码</span>
-                        <span v-if="qrCodeType === 2"><span class="work-name">梢期杀虫</span> 执行码</span>
+                        <span v-if="qrCodeType === 1"><span class="work-name">{{ t('梢期杀虫') }}</span>{{ t('上传执行照片邀请码') }}</span>
+                        <span v-if="qrCodeType === 2"><span class="work-name">{{ t('梢期杀虫') }}</span>{{ t('执行码') }}</span>
                     </div>
-                    <div class="qr-code-text">有效期:2025.07.15之前</div>
+                    <div class="qr-code-text">{{ t('有效期:2025.07.15之前') }}</div>
                 </div>
-                <div class="code-tips">&lt;&lt;长按二维码保存或转发&gt;&gt;</div>
+                <div class="code-tips">{{ t('&lt;&lt;长按二维码保存或转发&gt;&gt;') }}</div>
             </div>
         </popup>
 
@@ -256,10 +256,12 @@
     </div>
 
     <!-- 确认执行时间 -->
-    <calendar v-model:show="showCalendar" title="执行日期" @confirm="onConfirmExecuteTime" :min-date="minDate" :max-date="maxDate" />
+    <calendar v-model:show="showCalendar" :title="t('执行日期')" @confirm="onConfirmExecuteTime" :min-date="minDate" :max-date="maxDate" />
 </template>
 
 <script setup>
+import { useI18n } from "@/i18n";
+const { t } = useI18n();
 import { useRouter, useRoute } from "vue-router";
 import { showSuccessToast } from 'vant';
 import { ElMessage } from "element-plus";
@@ -332,8 +334,8 @@ const handleForward = async () => {
     try {
         if (navigator.clipboard && navigator.clipboard.writeText) {
             await navigator.clipboard.writeText(url);
-            // showSuccessToast('复制成功');
-            ElMessage.success('复制成功');
+            // showSuccessToast(t('复制成功')));
+            ElMessage.success({ message: t('复制成功'), type: 'success' });
         } else {
             // 兼容不支持 clipboard API 的环境
             const textarea = document.createElement("textarea");

+ 5 - 5
src/views/old_mini/chat_frame/consult.vue

@@ -1,6 +1,6 @@
 <template>
     <div class="consult">
-        <custom-header name="咨询专家"></custom-header>
+        <custom-header :name="$t('咨询专家')"></custom-header>
         <div class="consult-content">
             <!-- 聊天消息区域 -->
             <div class="chat-messages" ref="messagesContainer">
@@ -50,7 +50,7 @@
                                     <!-- 否则当作普通字符串 URL 使用 -->
                                     <img v-else :src="handleImgUrl(msg.content.coverUrl)" alt="">
                                 </div>
-                                <div class="btn-detail" @click="handleDetailClick">查看详情</div>
+                                <div class="btn-detail" @click="handleDetailClick">{{ $t('查看详情') }}</div>
                             </div>
                         </div>
                     </template>
@@ -96,7 +96,7 @@
                                     <!-- 否则当作普通字符串 URL 使用 -->
                                     <img v-else :src="handleImgUrl(msg.content.coverUrl)" alt="">
                                 </div>
-                                <div class="btn-detail" @click="handleDetailClick">查看详情</div>
+                                <div class="btn-detail" @click="handleDetailClick">{{ $t('查看详情') }}</div>
                             </div>
                         </div>
                         <!-- <div class="avatar avatar-r">{{ msg.senderName.charAt(0) }}</div> -->
@@ -114,8 +114,8 @@
                     <input type="file" ref="fileInput" accept="image/*" style="display: none" @change="handleImageUpload" />
                 </div> -->
 
-                <input type="text" v-model="inputMessage" placeholder="请输入你想说的话~" @keyup.enter="sendTextMessage" />
-                <div class="send" @click="sendTextMessage">发送</div>
+                <input type="text" v-model="inputMessage" :placeholder="$t('请输入你想说的话~')" @keyup.enter="sendTextMessage" />
+                <div class="send" @click="sendTextMessage">{{ $t('发送') }}</div>
             </div>
 
             <!-- 图片预览模态框 -->

+ 10 - 8
src/views/old_mini/create_farm/editMap.vue

@@ -1,8 +1,8 @@
 <template>
     <div class="edit-map">
-        <custom-header name="编辑农场" :isGoBack="true" @goback="backgToCreate"></custom-header>
+        <custom-header :name="$t('编辑农场')" :isGoBack="true" @goback="backgToCreate"></custom-header>
         <div class="edit-map-content">
-            <div class="edit-map-tip">操作提示:拖动圆点,即可调整地块边界</div>
+            <div class="edit-map-tip">{{ t('操作提示:拖动圆点,即可调整地块边界') }}</div>
             <div class="map-container" ref="mapContainer"></div>
             <div class="edit-map-footer">
                 <div class="footer-back" @click="goBack">
@@ -13,12 +13,12 @@
                         <div class="address-title">{{ pointName }}</div>
                         <div class="address-detail">{{ pointAddress }}</div>
                     </div>
-                    <div class="address-btn" @click="goBack">修改地址</div>
+                    <div class="address-btn" @click="goBack">{{ t('修改地址') }}</div>
                 </div>
                 <div class="edit-map-footer-btn">
-                    <div class="btn-delete" @click="deletePolygon">删除地块</div>
-                    <div class="btn-cancel" @click="goBack">取消</div>
-                    <div class="btn-confirm" @click="confirm">确认</div>
+                    <div class="btn-delete" @click="deletePolygon">{{ t('删除地块') }}</div>
+                    <div class="btn-cancel" @click="goBack">{{ t('取消') }}</div>
+                    <div class="btn-confirm" @click="confirm">{{ t('确认') }}</div>
                 </div>
             </div>
         </div>
@@ -26,6 +26,8 @@
 </template>
 
 <script setup>
+import { useI18n } from "@/i18n";
+const { t } = useI18n();
 import customHeader from "@/components/customHeader.vue";
 import { ref, onMounted, onActivated, onDeactivated } from "vue";
 import EditMap from "./map/editMap.js";
@@ -63,7 +65,7 @@ onMounted(() => {
     
     // 设置绘制限制回调
     editMap.setDrawLimitCallback(() => {
-        ElMessage.warning("请先删除当前地块再重新勾画");
+        ElMessage.warning({ message: t('请先删除当前地块再重新勾画'), type: 'warning' });
     });
     // editMap.setAreaGeometry([{ featureWkt: mapData.wkt }]);
 });
@@ -121,7 +123,7 @@ const deletePolygon = () => {
         }
     ).then(() => {
         editMap.deleteCurrentPolygon();
-        ElMessage.success("地块已删除");
+        ElMessage.success({ message: t('地块已删除'), type: 'success' });
     }).catch(() => {
         // 用户取消删除,不做任何操作
     });

+ 18 - 16
src/views/old_mini/create_farm/index.vue

@@ -7,7 +7,7 @@
         <div class="farm-content">
             <div class="top-mask"></div>
             <div class="farm-filter">
-                <el-select v-model="locationVal" filterable remote reserve-keyword placeholder="搜索位置"
+                <el-select v-model="locationVal" filterable remote reserve-keyword :placeholder="t('搜索位置')"
                     :remote-method="remoteMethod" :loading="loading" @change="handleSearchRes"
                     popper-class="location-search-popper">
                     <el-option v-for="(item, index) in locationOptions.list" :key="index" :label="item.title"
@@ -29,36 +29,36 @@
                         <div class="create-content">
                             <div class="create-from">
                                 <el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" class="demo-ruleForm">
-                                    <el-form-item label="农场位置" prop="farm_address">
+                                    <el-form-item :label="t('农场位置')" prop="farm_address">
                                         <div class="position-wrap">
-                                            <el-input placeholder="农场位置" readonly v-model="ruleForm.farm_address"
+                                            <el-input :placeholder="t('农场位置')" readonly v-model="ruleForm.farm_address"
                                                 autocomplete="off" />
                                         </div>
                                     </el-form-item>
-                                    <el-form-item label="种植作物" required>
+                                    <el-form-item :label="t('种植作物')" required>
                                         <div class="select-wrap">
                                             <div class="species-entry" @click="handleGoSelectCrop">
                                                 <span class="species-entry__text">{{ selectedCropSummaryText || "点击选择作物" }}</span>
                                             </div>
                                         </div>
                                     </el-form-item>
-                                    <el-form-item label="农场面积" prop="farm_area">
+                                    <el-form-item :label="t('农场面积')" prop="farm_area">
                                         <div class="area-box">
                                             <el-input :placeholder="isFromEditMap ? '勾选地块获得农场面积' : '请输入农场的真实面积'
                                                 " v-model="ruleForm.farm_area" :readonly="isFromEditMap" type="text"
                                                 autocomplete="off" style="width: fit-content" @input="handleMianjiInput"
                                                 @keypress="handleMianjiKeypress" />
-                                            <div class="unit">亩</div>
+                                            <div class="unit">{{ t('') }}</div>
                                         </div>
                                     </el-form-item>
-                                    <el-form-item label="农场名称" prop="farm_name">
-                                        <el-input placeholder="请输入您的农场名称" v-model="ruleForm.farm_name"
+                                    <el-form-item :label="t('农场名称')" prop="farm_name">
+                                        <el-input :placeholder="t('请输入您的农场名称')" v-model="ruleForm.farm_name"
                                             autocomplete="off" @input="handleFarmNameInput" />
                                     </el-form-item>
                                 </el-form>
                             </div>
                             <div class="create-btn">
-                                <div class="btn-item sencond-btn" @click="resetForm(ruleFormRef)">取消</div>
+                                <div class="btn-item sencond-btn" @click="resetForm(ruleFormRef)">{{ t('取消') }}</div>
                                 <div class="btn-item primary-btn" @click="submitForm(ruleFormRef)">
                                     {{
                                         paramsType === "client"
@@ -80,20 +80,22 @@
             :close-on-click-overlay="false">
             <div class="planting-report-popup__inner">
                 <img class="icon" src="@/assets/img/home/right.png" alt="" />
-                <div class="title">您的四大种植报告<br />已全部生成</div>
+                <div class="title">{{ t('您的四大种植报告') }}<br />{{ t('已全部生成') }}</div>
                 <div class="links">
-                    <span class="link">(作物长势报告)</span>
-                    <span class="link">(历史风险报告)</span>
-                    <span class="link">(土壤改良报告)</span>
-                    <span class="link">(种植建议报告)</span>
+                    <span class="link">{{ t('(作物长势报告)') }}</span>
+                    <span class="link">{{ t('(历史风险报告)') }}</span>
+                    <span class="link">{{ t('(土壤改良报告)') }}</span>
+                    <span class="link">{{ t('(种植建议报告)') }}</span>
                 </div>
-                <div class="btn" @click="handlePlantingReportView">点击查看</div>
+                <div class="btn" @click="handlePlantingReportView">{{ t('点击查看') }}</div>
             </div>
         </Popup>
     </div>
 </template>
 
 <script setup>
+import { useI18n } from "@/i18n";
+const { t } = useI18n();
 import customHeader from "@/components/customHeader.vue";
 import IndexMap from "./map/index.js";
 import { useRoute, useRouter } from "vue-router";
@@ -428,7 +430,7 @@ const submitForm = (formEl) => {
         if (valid) {
             const cropItems = parseSelectedCropItems();
             if (!cropItems) {
-                ElMessage.warning("请先点击「种植作物」选择作物品类");
+                ElMessage.warning({ message: t('请先点击「种植作物」选择作物品类'), type: 'warning' });
                 return;
             }
             const params = {

+ 8 - 8
src/views/old_mini/create_farm/selectCrop.vue

@@ -1,6 +1,6 @@
 <template>
     <div class="select-crop-page">
-        <custom-header name="选择作物"></custom-header>
+        <custom-header :name="$t('选择作物')"></custom-header>
         <div class="page-body">
             <div class="top-tabs">
                 <div v-for="(m, mi) in majorTabList" :key="m.key" class="tab-item"
@@ -17,7 +17,7 @@
                                 <span class="title-bar"></span>
                                 <span>{{ m.label }}</span>
                             </div>
-                            <el-input v-model="searchMap[majorBlockKey(m.key)]" class="search-wrap" placeholder="搜索品类">
+                            <el-input v-model="searchMap[majorBlockKey(m.key)]" class="search-wrap" :placeholder="$t('搜索品类')">
                                 <template #prefix>
                                     <el-icon>
                                         <Search />
@@ -46,11 +46,11 @@
         </div>
 
         <div class="custom-bottom-fixed-btns">
-            <div class="bottom-btn primary-btn" @click="handleConfirm">确认</div>
+            <div class="bottom-btn primary-btn" @click="handleConfirm">{{ $t('确认') }}</div>
         </div>
     </div>
     <popup v-model:show="showPopup" class="crop-popup" round>
-        <div class="popup-title">请选择具体品种</div>
+        <div class="popup-title">{{ $t('请选择具体品种') }}</div>
         <div class="popup-body">
             <div v-if="popupVarietyDetail" class="category-section">
                 <div class="section-header">
@@ -58,7 +58,7 @@
                         <span class="title-bar"></span>
                         <span>{{ popupVarietyDetail.varietyName }}</span>
                     </div>
-                    <el-input v-model="popupSearchKeyword" class="search-wrap" placeholder="搜索品种">
+                    <el-input v-model="popupSearchKeyword" class="search-wrap" :placeholder="$t('搜索品种')">
                         <template #prefix>
                             <el-icon>
                                 <Search />
@@ -77,10 +77,10 @@
                     </div>
                 </div>
             </div>
-            <div v-if="shouldShowPopupSwitch" class="switch-btn" @click="handleSwitch">换一换</div>
+            <div v-if="shouldShowPopupSwitch" class="switch-btn" @click="handleSwitch">{{ $t('换一换') }}</div>
             <div class="popup-actions">
-                <div class="action-btn cancel-btn" @click="clearPopupSelection">取消选中</div>
-                <div class="action-btn confirm-btn" @click="handlePopupConfirm">确认</div>
+                <div class="action-btn cancel-btn" @click="clearPopupSelection">{{ $t('取消选中') }}</div>
+                <div class="action-btn confirm-btn" @click="handlePopupConfirm">{{ $t('确认') }}</div>
             </div>
         </div>
     </popup>

+ 10 - 8
src/views/old_mini/growth_report/adjustPopup.vue

@@ -1,9 +1,9 @@
 <template>
     <Popup class="adjust-popup" teleport="body" :z-index="9999" round v-model:show="show" closeable>
-        <div class="popup-title">当前日期:2025.04.15</div>
+        <div class="popup-title">{{ t('当前日期:2025.04.15') }}</div>
         <div class="adjust-content">
             <div class="adjust-item">
-                <div class="item-title">请选择当前<span class="text-light">桂味</span>的物候期</div>
+                <div class="item-title">{{ t('请选择当前') }}<span class="text-light">{{ t('桂味') }}</span>{{ t('的物候期') }}</div>
                 <div class="item-checkbox">
                     <div class="tag-group add-tag-group">
                         <div class="tag-item" :class="{ selected: item.selected }" @click="handleSelect(idx)"
@@ -14,8 +14,8 @@
                 </div>
             </div>
             <div class="adjust-item">
-                <div class="item-title">请选择当前<span class="text-light">桂味</span>的生育期</div>
-                <div class="sub-title">界定标准:<span class="text-bold">六成</span> 以上表型进入该阶段</div>
+                <div class="item-title">{{ t('请选择当前') }}<span class="text-light">{{ t('桂味') }}</span>{{ t('的生育期') }}</div>
+                <div class="sub-title">{{ t('界定标准:') }}<span class="text-bold">{{ t('六成') }}</span>{{ t('以上表型进入该阶段') }}</div>
                 <div class="item-checkbox">
                     <div class="tag-group add-tag-group child-tag-group">
                         <div class="tag-item" :class="{ selected: item.selected }" @click="handleSelectChild(idx)"
@@ -28,13 +28,15 @@
             </div>
         </div>
         <div class="popup-button">
-            <div class="button-item cancel" @click="close">取消</div>
-            <div class="button-item confirm" @click="handleConfirm">确认</div>
+            <div class="button-item cancel" @click="close">{{ t('取消') }}</div>
+            <div class="button-item confirm" @click="handleConfirm">{{ t('确认') }}</div>
         </div>
     </Popup>
 </template>
 
 <script setup>
+import { useI18n } from "@/i18n";
+const { t } = useI18n();
 import { ref } from 'vue';
 import { Popup, showToast } from "vant";
 
@@ -52,11 +54,11 @@ const handleConfirm = () => {
     const hasPeriod = periodList.value.some((item) => item.selected);
     const hasChild = childList.value.some((item) => item.selected);
     if (!hasPeriod) {
-        showToast("请选择物候期");
+        showToast(t('请选择物候期'));
         return;
     }
     if (!hasChild) {
-        showToast("请选择生育期");
+        showToast(t('请选择生育期'));
         return;
     }
     close();

+ 4 - 2
src/views/old_mini/growth_report/historyRiskReport.vue

@@ -1,14 +1,16 @@
 <template>
     <div class="history-risk-report-page">
-        <custom-header name="历史风险报告" isGoBack @goback="handleGoBack"></custom-header>
+        <custom-header :name="$t('历史风险报告')" isGoBack @goback="handleGoBack"></custom-header>
         <div v-loading="loading" class="history-risk-report-content">
             <div v-if="reportContent" class="rich-text" v-html="reportContent"></div>
-            <div v-else-if="!loading" class="empty-text">暂无内容</div>
+            <div v-else-if="!loading" class="empty-text">{{ t('暂无内容') }}</div>
         </div>
     </div>
 </template>
 
 <script setup>
+import { useI18n } from "@/i18n";
+const { t } = useI18n();
 import { onActivated, onMounted, ref } from "vue";
 import { useRouter } from "vue-router";
 import customHeader from "@/components/customHeader.vue";

+ 74 - 42
src/views/old_mini/growth_report/index.vue

@@ -13,7 +13,7 @@
             <!-- <div class="invite-follow" v-if="currentFarmName && activeGardenTab === 'current'">
                 <div class="invite-content">
                     <icon name="share" />
-                    <span>邀请关注</span>
+                    <span>{{ t('邀请关注') }}</span>
                 </div>
             </div> -->
         </div>
@@ -30,7 +30,7 @@
                 <span class="risk-report-icon">
                     <i></i>
                 </span>
-                <span class="risk-report-text">历史风险报告</span>
+                <span class="risk-report-text">{{ t('历史风险报告') }}</span>
             </div> -->
 
             <div class="report-content has-report" :style="{ minHeight: `calc(100vh - ${tabBarHeight}px)` }">
@@ -39,10 +39,10 @@
 
                 <div class="report-header">
                     <!-- <div class="type-tabs report-tabs">
-                        <div class="type-item" :class="{ 'type-item-active': activeReportIndex === 0 }">作物长势</div>
-                        <div class="type-item" :class="{ 'type-item-active': activeReportIndex === 1 }">历史风险</div>
-                        <div class="type-item" :class="{ 'type-item-active': activeReportIndex === 2 }">土壤改良</div>
-                        <div class="type-item" :class="{ 'type-item-active': activeReportIndex === 3 }">种植建议</div>
+                        <div class="type-item" :class="{ 'type-item-active': activeReportIndex === 0 }">{{ t('作物长势') }}</div>
+                        <div class="type-item" :class="{ 'type-item-active': activeReportIndex === 1 }">{{ t('历史风险') }}</div>
+                        <div class="type-item" :class="{ 'type-item-active': activeReportIndex === 2 }">{{ t('土壤改良') }}</div>
+                        <div class="type-item" :class="{ 'type-item-active': activeReportIndex === 3 }">{{ t('种植建议') }}</div>
                     </div> -->
                     <div class="type-tabs" v-if="subjectData.length > 1">
                         <div
@@ -56,12 +56,12 @@
                         class="subject-toggle"
                         @click="typeTabsExpanded = !typeTabsExpanded"
                     >
-                        {{ typeTabsExpanded ? "点击收起" : "点击展开更多" }}
+                        {{ typeTabsExpanded ? t("common.collapse") : t("common.expandMore") }}
                     </div>
                     </div>
 
                     <div class="time-tag">{{ workItems?.[0]?.reportDate || new Date().toISOString().split('T')[0] }}</div>
-                    <div class="report-title">荔枝长势报告</div>
+                    <div class="report-title report-title-toggle" @click="toggleLocale">{{ t("growthReport.title") }}</div>
                     <div class="report-info">
                         <div class="info-item">
                             <img class="info-icon" src="@/assets/img/home/farm.png" alt="" />
@@ -71,14 +71,16 @@
                 </div>
 
                 <div class="report-box">
-                    <div class="box-title">气象风险</div>
+                    <div class="box-title">{{ t("growthReport.weatherRisk") }}</div>
                     <div class="box-text">
                         <div class="box-bg">
                             <!-- <div class="types-info">
-                                当前 <span class="text-bold">水稻</span> 处于为  分蘖初期<span class="text-link" @click="handleAdjustPopup">(校准物候期)</span>
+                                当前 <span class="text-bold">{{ t('水稻') }}</span>{{ t('处于为  分蘖初期') }}<span class="text-link" @click="handleAdjustPopup">{{ t('(校准物候期)') }}</span>
                             </div> -->
                             <div class="types-info">
-                                当前 <span class="text-bold">荔枝</span> 处于膨果期
+                                {{ t("common.current") }}
+                                <span class="text-bold">{{ t("growthReport.cropLychee") }}</span>
+                                {{ t("growthReport.inStage", { stage: t("growthReport.phenologyFruitExpansion") }) }}
                             </div>
                             <div class="tp-img">
                                 <img src="@/assets/img/common/tp-1.png" alt="">
@@ -95,7 +97,7 @@
                                     <div class="title-line"></div>
                                     <div class="title-block"></div>
                                 </div>
-                                <div>未来7-15天气象风险</div>
+                                <div>{{ t("growthReport.futureWeatherRisk") }}</div>
                                 <div class="title-l">
                                     <div class="title-block"></div>
                                     <div class="title-line title-line-right"></div>
@@ -111,7 +113,7 @@
                 </div>
 
                 <div class="report-box">
-                    <div class="box-title">农事建议</div>
+                    <div class="box-title">{{ t("growthReport.farmAdvice") }}</div>
                     <div class="box-text">
                         <div class="warning-part" v-for="(part, partI) in adviceList" :key="partI">
                             <div class="report-part">
@@ -119,7 +121,7 @@
                                     <div class="part-title">{{ part.title }}</div>
                                     <!-- <div class="part-link">
                                         <el-icon class="part-link-icon"><Link /></el-icon>
-                                        <div class="text-link">查看农事</div>
+                                        <div class="text-link">{{ t('查看农事') }}</div>
                                     </div> -->
                                 </div>
                                 <div class="part-text">{{ part.description }}</div>
@@ -128,7 +130,7 @@
                     </div>
                 </div>
                 <div class="report-box">
-                    <div class="box-title">巡园重点</div>
+                    <div class="box-title">{{ t("growthReport.patrolFocus") }}</div>
                     <div class="box-text">
                         <div class="warning-part" v-for="(part, partI) in patrolList" :key="partI">
                             <div class="report-part">
@@ -136,7 +138,7 @@
                                     <div class="part-title">{{ part.title }}</div>
                                     <!-- <div class="part-link">
                                         <el-icon class="part-link-icon"><Link /></el-icon>
-                                        <div class="text-link">查看互动</div>
+                                        <div class="text-link">{{ t('查看互动') }}</div>
                                     </div> -->
                                 </div>
                                 <div class="part-text">{{ part.description }}</div>
@@ -149,11 +151,11 @@
                     <div class="box-title">{{ work?.title }}</div>
                     <div class="box-text">
                         <div class="box-bg" v-show="work?.backgroundDesc">
-                            <span class="box-subtitle">背景描述:</span>
+                            <span class="box-subtitle">{{ t("common.backgroundDesc") }}</span>
                             <div class="pre-text">{{ work?.backgroundDesc }}</div>
                         </div>
                         <div class="box-advice" v-show="work?.suggestion">
-                            <span class="box-subtitle">对策建议:</span>
+                            <span class="box-subtitle">{{ t("common.suggestion") }}</span>
                             <div class="pre-text">{{ work?.suggestion }}</div>
                         </div>
                         <div class="box-sum pre-text" v-show="work?.summary">{{ work?.summary }}</div>
@@ -175,11 +177,11 @@
                 <div class="report-header" :class="{ 'no-farm': !currentFarmName }">
                     <!-- <img class="header-book" src="@/assets/img/home/book.png" alt="" /> -->
                     <div class="time-tag">{{ new Date().toISOString().split('T')[0] }}</div>
-                    <div class="report-title" @click="handleAddFarm">作物长势报告</div>
+                    <div class="report-title report-title-toggle" @click="toggleLocale">{{ t("growthReport.cropTitle") }}</div>
                     <div class="report-info pb-4">
                         <div class="info-item">
                             <img class="info-icon" src="@/assets/img/home/farm.png" alt="" />
-                            <span class="info-text">示范农场</span>
+                            <span class="info-text">{{ t("common.demoFarm") }}</span>
                         </div>
                     </div>
                 </div>
@@ -191,11 +193,11 @@
                     <img @click="handleLockClick" src="@/assets/img/home/lock-blue.png" alt=""
                         class="has-click lock-img-item" />
                     <div class="lock-text">
-                        专属数字农场,种好卖好
-                        <div>点击解锁一键溯源增产</div>
+                        {{ t("growthReport.lockTitle") }}
+                        <div>{{ t("growthReport.lockSub") }}</div>
                     </div>
 
-                    <div @click="handleLockClick" class="lock-btn has-click">点击解锁</div>
+                    <div @click="handleLockClick" class="lock-btn has-click">{{ t("common.unlock") }}</div>
                 </div>
 
                 <div class="lock-bg"></div>
@@ -205,7 +207,7 @@
         <!-- 农场列表引导 -->
         <div class="mask-wrap" @click="closeTabMask" v-if="showTabMask"></div>
 
-        <tip-popup v-model:show="showBindSuccess" type="success" text="您的农场已绑定成功" hideBtn />
+        <tip-popup v-model:show="showBindSuccess" type="success" :text="t('growthReport.bindSuccess')" hideBtn />
 
         <start-interact-popup ref="startInteractPopupRef" />
 
@@ -228,7 +230,9 @@ import startInteractPopup from "@/components/popup/startInteractPopup.vue";
 import agriExecutePopup from "@/components/popup/agriExecutePopup.vue";
 import gardenList from "@/components/gardenList.vue";
 import adjustPopup from "./adjustPopup.vue";
+import { useI18n } from "@/i18n";
 
+const { t, toggleLocale: dispatchToggleLocale } = useI18n();
 const store = useStore();
 const tabBarHeight = computed(() => store.state.home.tabBarHeight);
 
@@ -240,40 +244,40 @@ const workItems = ref([]);
 const swipeRef = ref(null);
 
 //
-const riskList = ref([
+const riskList = computed(() => [
     {
-        title: '病虫风险',
-        description: '当前膨果期对病虫害高度敏感,易受害。经过飞鸟智慧大脑计算病虫风险:病虫胁迫指数处于中等水平,存在一定的病虫害发生风险,局部区域可能出现病害流行或虫害爆发,需加强监测预警,及时巡园'
+        title: t("growthReport.risk.pest.title"),
+        description: t("growthReport.risk.pest.desc"),
     },
     {
-        title: '阴雨寡照风险',
-        description: '经过飞鸟智慧大脑计气象风险:阴雨寡照胁迫指数处于中等水平,存在一定的植株缺素风险,局部区域可能会出现长势异常现象,需加强监测预警,及时巡园'
+        title: t("growthReport.risk.rain.title"),
+        description: t("growthReport.risk.rain.desc"),
     },
 ]);
 
-const adviceList = ref([
+const adviceList = computed(() => [
     {
-        title: '根外追肥',
-        description: '荔枝膨果期遇持续阴雨寡照,需及时排水防渍,重点根外喷施磷酸二氢钾并配合补充钙硼肥,以快速恢复树势、促进膨果壮果。'
+        title: t("growthReport.advice.foliar.title"),
+        description: t("growthReport.advice.foliar.desc"),
     },
     {
-        title: '虫害防治',
-        description: '通过叶面喷施针对性杀虫剂,快速压低蒂蛀虫种群数量,保护果实免受蛀害。'
+        title: t("growthReport.advice.pestControl.title"),
+        description: t("growthReport.advice.pestControl.desc"),
     },
 ]);
 
-const patrolList = ref([
+const patrolList = computed(() => [
     {
-        title: '进程互动',
-        description: '是否有60%的荔枝进入果实转色期?'
+        title: t("growthReport.patrol.process.title"),
+        description: t("growthReport.patrol.process.desc"),
     },
     {
-        title: '长势互动',
-        description: '是是否有10%的荔枝出现生理落果或裂果症状'
+        title: t("growthReport.patrol.growth.title"),
+        description: t("growthReport.patrol.growth.desc"),
     },
     {
-        title: '病虫互动',
-        description: '是否有10%的荔枝发现蒂蛀虫为害特征?'
+        title: t("growthReport.patrol.pest.title"),
+        description: t("growthReport.patrol.pest.desc"),
     },
 ]);
 //
@@ -553,6 +557,29 @@ const getRegions = async () => {
     });
 }
 
+/** 切换语言后,用新 lang 参数重新请求页面数据 */
+const reloadPageDataAfterLocaleChange = () => {
+    gardenListRef.value?.refreshFarmList?.();
+
+    const subjectId = paramsPage.value.subjectId || localStorage.getItem("selectedFarmId");
+    if (subjectId) {
+        getSubjectData(subjectId);
+    }
+
+    if (paramsPage.value.farmId) {
+        getTodayPatrolFocus();
+        getPendingFarmWork();
+        getDetail();
+    }
+
+    startInteractPopupRef.value?.getPhenologyInitOrConfirmStatus?.();
+};
+
+const toggleLocale = async () => {
+    await dispatchToggleLocale();
+    reloadPageDataAfterLocaleChange();
+};
+
 // 清理数据的函数
 const clearData = () => {
     workItems.value = [];
@@ -913,6 +940,11 @@ onUnmounted(() => {
                 font-family: "PangMenZhengDao";
                 font-size: 34px;
                 line-height: 38px;
+
+                &.report-title-toggle {
+                    cursor: pointer;
+                    user-select: none;
+                }
                 color: #000000;
             }
 
@@ -1136,7 +1168,7 @@ onUnmounted(() => {
                             display: flex;
                             align-items: center;
                             .title-line {
-                                width: 76px;
+                                width: 68px;
                                 height: 1px;
                                 background: linear-gradient(90deg, rgba(118, 118, 118, 0) 0%, rgba(118, 118, 118, 0.4) 100%);
                                 &.title-line-right {

+ 11 - 11
src/views/old_mini/home/components/farmInfoPopup.vue

@@ -1,19 +1,19 @@
 <template>
     <Popup v-model:show="show" :overlay-style="{ zIndex: 9999 }" teleport="body" class="farm-info-popup" closeable>
         <div class="popup-content-box">
-            <div class="popup-title">基本信息</div>
+            <div class="popup-title">{{ $t('基本信息') }}</div>
             <div class="popup-content">
                 <!-- <div class="map-box">
                     <div class="map" ref="mapContainer"></div>
                 </div> -->
                 <cell-group inset class="cell-group">
-                    <field v-model="farmInfo.subjectName" label="农场名称" />
-                    <field readonly label="农场面积">
+                    <field v-model="farmInfo.subjectName" :label="$t('农场名称')" />
+                    <field readonly :label="$t('农场面积')">
                         <template #input>
                             <span>{{ farmInfo.farmArea }}亩</span>
                         </template>
                     </field>
-                    <field readonly label="种植作物" class="crop-field">
+                    <field readonly :label="$t('种植作物')" class="crop-field">
                         <template #input>
                             <template v-if="farmInfo.regionList && farmInfo.regionList.length">
                                 <template v-for="(item, index) in farmInfo.regionList" :key="index">
@@ -26,21 +26,21 @@
                             </template>
                         </template>
                     </field>
-                    <field v-model="farmInfo.contactName" label="联系人" />
+                    <field v-model="farmInfo.contactName" :label="$t('联系人')" />
                     <field
                         ref="contactPhoneFieldRef"
                         v-model="farmInfo.contactPhone"
-                        label="联系电话"
+                        :label="$t('联系电话')"
                         type="tel"
                         maxlength="11"
                         :rules="contactPhoneRules"
                     />
-                    <field class="address-field" v-model="farmInfo.farmAddress" readonly label="农场位置" />
-                    <!-- <field label="基地类别" class="base-type-field">
+                    <field class="address-field" v-model="farmInfo.farmAddress" readonly :label="$t('农场位置')" />
+                    <!-- <field :label="$t('基地类别')" class="base-type-field">
                         <template #input>
                             <el-select
                                 v-model="farmInfo.baseType"
-                                placeholder="请选择"
+                                :placeholder="$t('请选择')"
                                 class="base-type-select"
                                 autocomplete="off"
                                 teleported
@@ -58,8 +58,8 @@
                 </cell-group>
             </div>
             <div class="popup-footer">
-                <div class="footer-btn no-btn" @click="handleCancel">取消</div>
-                <div class="footer-btn yes-btn" @click="handleEdit">确认修改</div>
+                <div class="footer-btn no-btn" @click="handleCancel">{{ $t('取消') }}</div>
+                <div class="footer-btn yes-btn" @click="handleEdit">{{ $t('确认修改') }}</div>
             </div>
         </div>
     </Popup>

+ 11 - 11
src/views/old_mini/interaction/index.vue

@@ -1,6 +1,6 @@
 <template>
     <div class="interaction-page">
-        <custom-header name="农情互动" bgColor="#f2f4f5" />
+        <custom-header :name="$t('农情互动')" bgColor="#f2f4f5" />
         <div class="interaction-content">
             <!-- 顶部说明 -->
             <div class="intro-card">
@@ -18,7 +18,7 @@
                 <div v-for="(region, regionIndex) in crop.regions" :key="regionIndex" class="variety-card">
                     <div class="field-row">
                         <div class="field-value variety-select-wrap" v-if="farmData.status === 'INIT' || isAddVariety">
-                            <el-select filterable v-model="region.regionId" class="variety-input" placeholder="选择品种">
+                            <el-select filterable v-model="region.regionId" class="variety-input" :placeholder="$t('选择品种')">
                                 <el-option v-for="(item, index) in crop.typeIdItems" :key="index" :label="item.name"
                                     :value="item.id"
                                     :disabled="isVarietyOptionDisabled(crop, region, item.id) || item.selected === 1" />
@@ -42,10 +42,10 @@
                     </div>
 
                     <div class="field-row">
-                        <div class="field-label">当下物候期</div>
+                        <div class="field-label">{{ $t('当下物候期') }}</div>
                         <div class="field-value">
                             <el-select
-                            v-model="region.phenologyId" @change="handlePhenologyChange(crop, region)" class="select-input" placeholder="选择物候期">
+                            v-model="region.phenologyId" @change="handlePhenologyChange(crop, region)" class="select-input" :placeholder="$t('选择物候期')">
                                 <el-option v-for="(item, index) in crop.phenologyOptions" :key="index"
                                     :label="item.phenologyName" :value="item.phenologyId" />
                             </el-select>
@@ -61,7 +61,7 @@
                                 v-model="region.phenologyStartDate"
                                 class="date-picker"
                                 type="date"
-                                placeholder="选择时间"
+                                :placeholder="$t('选择时间')"
                                 format="YYYY-MM-DD"
                                 value-format="YYYY-MM-DD"
                                 :disabled-date="(time) => disabledFutureDate(time, crop, region)"
@@ -80,24 +80,24 @@
         </div>
     </div>
 
-    <tip-popup v-model:show="showTipPopup" type="success" text="您的农情报告已生成" text2="请查看" buttonText="点击查看"
+    <tip-popup v-model:show="showTipPopup" type="success" :text="$t('您的农情报告已生成')" text2="请查看" buttonText="点击查看"
         @confirm="handleTipConfirm" />
 
 
     <popup class="add-tag-popup" :z-index="2500" round v-model:show="showAddPopup" @update:show="handlePopupClose">
         <div class="tag-item">
-            <div class="popup-title">新增品种名称</div>
-            <el-input autofocus class="popup-input" v-model="newVarietyName" placeholder="请输入品种名称" size="large" />
+            <div class="popup-title">{{ $t('新增品种名称') }}</div>
+            <el-input autofocus class="popup-input" v-model="newVarietyName" :placeholder="$t('请输入品种名称')" size="large" />
         </div>
         <div class="popup-button">
-            <div class="cancel" @click="handleCancelAdd">取消</div>
-            <div @click="handleAddVariety">确认</div>
+            <div class="cancel" @click="handleCancelAdd">{{ $t('取消') }}</div>
+            <div @click="handleAddVariety">{{ $t('确认') }}</div>
         </div>
     </popup>
 
     <!-- 勾选区域引导弹窗 -->
     <select-region-popup v-model:show="showSelectRegionPopup" :image="selectRegionImage" :title="selectRegionTitle"
-        sub-title="精准匹配农情信息,高效管理分区" @confirm="handleGoSelectRegion" @skip="handleSkipSelectRegion" />
+        sub-:title="$t('精准匹配农情信息,高效管理分区')" @confirm="handleGoSelectRegion" @skip="handleSkipSelectRegion" />
 </template>
 
 <script setup>

+ 4 - 2
src/views/old_mini/interactionList/components/morePopup.vue

@@ -10,7 +10,7 @@
                     v-model="keyword"
                     class="search-input"
                     type="text"
-                    placeholder="请输入病虫害名称"
+                    :placeholder="t('请输入病虫害名称')"
                 />
             </div> -->
 
@@ -52,7 +52,7 @@
                     </div>
                     <div class="card-name">{{ item.exampleImageAnnotation || "病害" }}</div>
                 </div>
-                <div v-if="!filteredList.length" class="empty-text">暂无数据</div>
+                <div v-if="!filteredList.length" class="empty-text">{{ t('暂无数据') }}</div>
             </div>
         </div>
     </popup>
@@ -67,6 +67,8 @@
 </template>
 
 <script setup>
+import { useI18n } from "@/i18n";
+const { t } = useI18n();
 import { ref, computed } from "vue";
 import { Popup } from "vant";
 import { Search } from "@element-plus/icons-vue";

+ 7 - 5
src/views/old_mini/interactionList/confirmArea.vue

@@ -1,8 +1,8 @@
 <template>
     <div class="edit-map">
-        <custom-header name="确认农场区域"></custom-header>
+        <custom-header :name="$t('确认农场区域')"></custom-header>
         <div class="edit-map-content">
-            <div class="edit-map-tip">操作提示:拖动圆点,即可调整地块边界</div>
+            <div class="edit-map-tip">{{ t('操作提示:拖动圆点,即可调整地块边界') }}</div>
             <div class="map-container" ref="mapContainer"></div>
             <div class="edit-map-footer">
                 <div class="footer-back" @click="goBack">
@@ -13,11 +13,11 @@
                         <div class="address-title">111</div>
                         <div class="address-detail">2222</div>
                     </div>
-                    <div class="address-btn" @click="goBack">修改地址</div>
+                    <div class="address-btn" @click="goBack">{{ t('修改地址') }}</div>
                 </div>
                 <div class="edit-map-footer-btn">
-                    <div class="btn-cancel" @click="goBack">取消</div>
-                    <div class="btn-confirm" @click="confirm">确认</div>
+                    <div class="btn-cancel" @click="goBack">{{ t('取消') }}</div>
+                    <div class="btn-confirm" @click="confirm">{{ t('确认') }}</div>
                 </div>
             </div>
         </div>
@@ -29,6 +29,8 @@
 </template>
 
 <script setup>
+import { useI18n } from "@/i18n";
+const { t } = useI18n();
 import customHeader from "@/components/customHeader.vue";
 import DroneConsultPopup from "@/components/popup/droneConsultPopup.vue";
 import { ref, onMounted, onActivated, onDeactivated } from "vue";

+ 11 - 9
src/views/old_mini/interactionList/drawRegion.vue

@@ -14,16 +14,16 @@
             </div>
         </div>
         <div class="edit-map-content">
-            <div class="edit-map-tip" v-if="!viewOnly">操作提示:拖动圆点,即可调整地块边界</div>
+            <div class="edit-map-tip" v-if="!viewOnly">{{ t('操作提示:拖动圆点,即可调整地块边界') }}</div>
             <div class="map-container" ref="mapContainer"></div>
             <div class="edit-map-footer" :style="{ 'bottom': activeRegionType !== 'DORMANCY' ? '85px' : '59px' }">
                 <div class="footer-back" @click="goBack">
                     <img class="back-icon" src="@/assets/img/home/go-back.png" alt="" />
                 </div>
                 <div class="edit-map-footer-btn" v-if="!viewOnly">
-                    <div class="btn-delete" @click="deletePolygon">删除地块</div>
-                    <!-- <div class="btn-cancel" @click="goBack">取消</div> -->
-                    <div class="btn-confirm" @click="confirm">确认区域</div>
+                    <div class="btn-delete" @click="deletePolygon">{{ t('删除地块') }}</div>
+                    <!-- <div class="btn-cancel" @click="goBack">{{ t('取消') }}</div> -->
+                    <div class="btn-confirm" @click="confirm">{{ t('确认区域') }}</div>
                 </div>
             </div>
         </div>
@@ -31,18 +31,20 @@
         <popup v-model:show="showAbnormalTypePopup" round closeable class="abnormal-popup"
             :close-on-click-overlay="true">
             <div class="abnormal-popup-content">
-                <div class="abnormal-popup-title">请选择异常类型</div>
-                <el-select v-model="selectedAbnormalType" class="abnormal-type-select" placeholder="请选择类型" size="large">
+                <div class="abnormal-popup-title">{{ t('请选择异常类型') }}</div>
+                <el-select v-model="selectedAbnormalType" class="abnormal-type-select" :placeholder="t('请选择类型')" size="large">
                     <el-option v-for="item in abnormalTypeOptions" :key="item.value" :label="item.name"
                         :value="item.value" />
                 </el-select>
-                <div class="abnormal-popup-confirm" @click="handleConfirmUpload">确认上传</div>
+                <div class="abnormal-popup-confirm" @click="handleConfirmUpload">{{ t('确认上传') }}</div>
             </div>
         </popup>
     </div>
 </template>
 
 <script setup>
+import { useI18n } from "@/i18n";
+const { t } = useI18n();
 import customHeader from "@/components/customHeader.vue";
 import { ref, computed, onMounted, onActivated, onDeactivated } from "vue";
 import { Popup } from "vant";
@@ -576,7 +578,7 @@ const deletePolygon = () => {
         if (drawRegionMap.kmap && drawRegionMap.kmap.polygonLayer && drawRegionMap.kmap.polygonLayer.source) {
             drawRegionMap.kmap.polygonLayer.source.clear();
         }
-        ElMessage.success("地块已删除");
+        ElMessage.success({ message: t('地块已删除'), type: 'success' });
     }).catch(() => {
         // 用户取消删除,不做任何操作
     });
@@ -649,7 +651,7 @@ const saveAndBack = () => {
 
 const handleConfirmUpload = () => {
     if (!selectedAbnormalType.value) {
-        ElMessage.warning("请选择异常类型");
+        ElMessage.warning({ message: t('请选择异常类型'), type: 'warning' });
         return;
     }
     showAbnormalTypePopup.value = false;

+ 7 - 5
src/views/old_mini/interactionList/drawRegion1.vue

@@ -2,16 +2,16 @@
     <div class="edit-map">
         <custom-header :name="viewOnly ? '查看区域' : '勾画区域'"></custom-header>
         <div class="edit-map-content">
-            <div class="edit-map-tip" v-if="!viewOnly">操作提示:拖动圆点,即可调整地块边界</div>
+            <div class="edit-map-tip" v-if="!viewOnly">{{ t('操作提示:拖动圆点,即可调整地块边界') }}</div>
             <div class="map-container" ref="mapContainer"></div>
             <div class="edit-map-footer">
                 <div class="footer-back" @click="goBack">
                     <img class="back-icon" src="@/assets/img/home/go-back.png" alt="" />
                 </div>
                 <div class="edit-map-footer-btn" v-if="!viewOnly">
-                    <div class="btn-delete" @click="deletePolygon">删除地块</div>
-                    <div class="btn-cancel" @click="goBack">取消</div>
-                    <div class="btn-confirm" @click="confirm">确认</div>
+                    <div class="btn-delete" @click="deletePolygon">{{ t('删除地块') }}</div>
+                    <div class="btn-cancel" @click="goBack">{{ t('取消') }}</div>
+                    <div class="btn-confirm" @click="confirm">{{ t('确认') }}</div>
                 </div>
             </div>
         </div>
@@ -19,6 +19,8 @@
 </template>
 
 <script setup>
+import { useI18n } from "@/i18n";
+const { t } = useI18n();
 import customHeader from "@/components/customHeader.vue";
 import { ref, computed, onMounted, onActivated, onDeactivated } from "vue";
 import DrawRegionMap from "./map/drawRegionMap.js";
@@ -133,7 +135,7 @@ const deletePolygon = () => {
         }
     ).then(() => {
         drawRegionMap.deleteCurrentPolygon();
-        ElMessage.success("地块已删除");
+        ElMessage.success({ message: t('地块已删除'), type: 'success' });
         sessionStorage.removeItem("drawRegionPolygonData");
     }).catch(() => {
         // 用户取消删除,不做任何操作

+ 23 - 23
src/views/old_mini/interactionList/index.vue

@@ -1,5 +1,5 @@
 <template>
-    <custom-header v-if="!showClose" name="农情互动" bgColor="#f2f4f5"></custom-header>
+    <custom-header v-if="!showClose" :name="$t('农情互动')" bgColor="#f2f4f5"></custom-header>
     <div class="interaction-list-wrapper" :class="{ 'show-close': showClose }">
         <div class="interaction-card" v-if="showClose">
             <img v-if="listData[0]?.coverUrl" :src="listData[0]?.coverUrl" alt="">
@@ -9,7 +9,7 @@
             </div>
         </div>
         <div class="interaction-list" ref="interactionListRef">
-            <!-- <div class="tips-text">由于系统审核,您照片拍摄位置与农场相差超 3 公里,请在农场现场使用水印相机重新拍摄上传,谢谢配合</div> -->
+            <!-- <div class="tips-text">{{ $t('由于系统审核,您照片拍摄位置与农场相差超 3 公里,请在农场现场使用水印相机重新拍摄上传,谢谢配合') }}</div> -->
             <div class="list-item" v-for="(item, index) in listData" :key="item.id || index"
                 :class="{ 'uploaded-item': item.questionStatus !== 3 }">
                 <!-- 标题区域 -->
@@ -24,14 +24,14 @@
                                 urgentType[item.urgent] }}</div>
                         <!-- <div class="location-error">
                             <el-icon size="16"><InfoFilled /></el-icon>
-                            <span>提交有误</span>
+                            <span>{{ $t('提交有误') }}</span>
                         </div> -->
                     </div>
                     <div class="upload-status" v-show="item.questionStatus !== 3">
                         <el-icon class="status-icon">
                             <SuccessFilled />
                         </el-icon>
-                        <span class="status-text">提交成功</span>
+                        <span class="status-text">{{ $t('提交成功') }}</span>
                     </div>
                 </div>
 
@@ -39,14 +39,14 @@
                 <div class="uploaded-content" v-show="item.questionStatus === 3 || item.expanded">
                     <div class="content-wrapper">
                         <span>{{ item.reason }}</span>
-                        <text-ellipsis class="item-desc" rows="0" :content="item.remark" expand-text="展开"
-                            collapse-text="收起" />
-                        <div class="tip-box">如果不确定是否发生,直接上传照片即可</div>
+                        <text-ellipsis class="item-desc" rows="0" :content="item.remark" expand-:text="$t('展开')"
+                            collapse-:text="$t('收起')" />
+                        <div class="tip-box">{{ $t('如果不确定是否发生,直接上传照片即可') }}</div>
                         <div class="example-wrapper">
                             <div class="example-header">
-                                <div>示例照片</div>
+                                <div>{{ $t('示例照片') }}</div>
                                 <div class="more" v-if="item.exampleImageWithAnnotations.length > 3"
-                                    @click="openMorePopup(item)">查看更多</div>
+                                    @click="openMorePopup(item)">{{ $t('查看更多') }}</div>
                             </div>
                             <div class="example-list" v-if="item.exampleImageWithAnnotations.length > 0">
                                 <div class="image-item-wrapper"
@@ -91,7 +91,7 @@
                                 <el-icon>
                                     <Plus />
                                 </el-icon>
-                                <span>继续上传照片</span>
+                                <span>{{ $t('继续上传照片') }}</span>
                             </div>
                         </uploader>
                     </div>
@@ -104,7 +104,7 @@
                             <el-icon>
                                 <Plus />
                             </el-icon>
-                            <span>点击上传照片</span>
+                            <span>{{ $t('点击上传照片') }}</span>
                         </div>
                     </uploader>
 
@@ -133,7 +133,7 @@
 
                     <!-- 输入框 -->
                     <div class="input-wrapper" v-if="item.questionStatus === 3 || item.replyText">
-                        <el-input v-model="item.replyText" :disabled="item.questionStatus !== 3" placeholder="请输入您咨询的问题"
+                        <el-input v-model="item.replyText" :disabled="item.questionStatus !== 3" :placeholder="$t('请输入您咨询的问题')"
                             clearable />
                     </div>
 
@@ -189,14 +189,14 @@
                     </div>
                 </div>
             </div>
-            <div class="empty-data" v-if="!loading && listData.length === 0">暂无数据</div>
+            <div class="empty-data" v-if="!loading && listData.length === 0">{{ $t('暂无数据') }}</div>
         </div>
     </div>
 
     <!-- <div class="custom-bottom-fixed-btns" :class="{ 'center-btn': false }">
-        <div class="bottom-btn secondary-btn" @click="handleShowDroneConsultPopup">获取自动飞行航线</div>
-        <div class="bottom-btn secondary-btn">邀请农情互动</div>
-        <div class="bottom-btn primary-btn" @click="handleSubmitAll">一键提交</div>
+        <div class="bottom-btn secondary-btn" @click="handleShowDroneConsultPopup">{{ $t('获取自动飞行航线') }}</div>
+        <div class="bottom-btn secondary-btn">{{ $t('邀请农情互动') }}</div>
+        <div class="bottom-btn primary-btn" @click="handleSubmitAll">{{ $t('一键提交') }}</div>
     </div> -->
 
     <!-- 农场信息完善弹窗 -->
@@ -211,13 +211,13 @@
 
     <popup v-model:show="showUploadProgressPopup" round :close-on-click-overlay="false" class="upload-progress-popup">
         <div class="upload-progress-title">
-            <span>照片上传进度</span>
+            <span>{{ $t('照片上传进度') }}</span>
             <el-progress class="upload-progress" :percentage="uploadPercentage" :stroke-width="10" :format="format" />
         </div>
         <div
             class="upload-box"
             v-loading="popupImageUploadLoading"
-            element-loading-text="上传中..."
+            element-loading-:text="$t('上传中...')"
         >
             <!-- 把已经上传成功的图片传给 upload 组件做回显,同时保持原有上传事件不变 -->
             <upload ref="uploadRef" :maxCount="10" :initImgArr="initImgArr" @handleUpload="handleUploadSuccess">
@@ -228,23 +228,23 @@
                 v-for="(q, qIdx) in (currentItem?.questionList?.length ? currentItem.questionList : (currentItem?.question ? [currentItem.question] : []))"
                 :key="qIdx">
                 <span class="label-text">{{ q }}</span>
-                <el-input class="label-input" v-model="currentItem.answerValues[qIdx]" placeholder="请输入" type="number">
+                <el-input class="label-input" v-model="currentItem.answerValues[qIdx]" :placeholder="$t('请输入')" type="number">
                     <template #suffix>
                         <span class="unit">{{ currentItem.indicators[qIdx]?.unit ?? currentItem.indicators[0]?.unit ??
                             '%' }}</span>
                     </template>
                 </el-input>
             </div>
-            <el-input class="input-item" v-model="currentItem.replyText" placeholder="请输入您咨询的问题" clearable />
+            <el-input class="input-item" v-model="currentItem.replyText" :placeholder="$t('请输入您咨询的问题')" clearable />
         </div>
         <template v-if="currentItem.interactionTypeId != 1">
-            <div class="region-tips">勾画异常发生区域,精准匹配专属农事方案</div>
+            <div class="region-tips">{{ $t('勾画异常发生区域,精准匹配专属农事方案') }}</div>
             <div class="region-map" ref="mapContainer" @click="handleDrawRegion(currentItem)">
-                <div class="region-map-text">点击勾画异常发生区域</div>
+                <div class="region-map-text">{{ $t('点击勾画异常发生区域') }}</div>
             </div>
         </template>
         <div class="upload-action-btns">
-            <div class="cancel-btn" @click="handleCancelUploadPopup">取消</div>
+            <div class="cancel-btn" @click="handleCancelUploadPopup">{{ $t('取消') }}</div>
             <div class="confirm-btn" :class="{ 'confirm-btn-loading': confirmUploadLoading }" @click="handleConfirmUpload">
                 {{ confirmUploadLoading ? '上传中...' : '确认上传' }}
             </div>

+ 6 - 6
src/views/old_mini/monitor/index.vue

@@ -1,5 +1,5 @@
 <template>
-    <custom-header v-if="isHeaderShow" name="农场详情"></custom-header>
+    <custom-header v-if="isHeaderShow" :name="$t('农场详情')"></custom-header>
     <div class="monitor-index" :style="{ height: `calc(100vh - ${tabBarHeight}px)` }">
         <!-- 天气遮罩 -->
         <div class="weather-mask" v-show="isExpanded" @click="handleMaskClick"></div>
@@ -15,17 +15,17 @@
         <!-- 作物档案 -->
         <div class="archives-time-line" :class="{ 'no-top': !varietyTabs.length && !gardenId }" v-show="activeGardenTab === 'current'">
             <div class="archives-time-line-header">
-                <div class="line-title">农情档案</div>
+                <div class="line-title">{{ $t('农情档案') }}</div>
                 <div class="header-right">
                     <div class="add-variety-btn" v-if="varietyTabs.length > 0" @click="handleAddVariety">
-                        <span>分区管理</span>
+                        <span>{{ $t('分区管理') }}</span>
                     </div>
                     <el-date-picker
                         style="width: 100px"
                         :editable="false"
                         v-model="date"
                         type="year"
-                        placeholder="全部日期"
+                        :placeholder="$t('全部日期')"
                         :disabled-date="disabledYearDate"
                     />
                 </div>
@@ -48,9 +48,9 @@
                     <img src="@/assets/img/home/lock-blue.png" alt="" class="lock-img-item" />
                     <div class="lock-text">
                         专属数字农场,种好卖好
-                        <div>点击解锁一键溯源增产</div>
+                        <div>{{ $t('点击解锁一键溯源增产') }}</div>
                     </div>
-                    <div class="lock-btn">点击解锁</div>
+                    <div class="lock-btn">{{ $t('点击解锁') }}</div>
                 </div>
                 <img class="example-img" src="@/assets/img/monitor/example.png" alt="">
             </template>

+ 11 - 9
src/views/old_mini/monitor/subPages/agriculturalDetail.vue

@@ -1,11 +1,11 @@
 <template>
     <div class="agricultural-detail">
-        <custom-header name="农事详情"></custom-header>
+        <custom-header :name="$t('农事详情')"></custom-header>
         <div class="detail-content">
             <div class="card-wrap" v-if="route.query.title !== '气象风险'">
                 <div class="card-box photo-card">
                     <div class="card-title">
-                        <span>农情照片</span>
+                        <span>{{ t('农情照片') }}</span>
                         <span class="date">{{ imgInfo.samplingTime }}</span>
                     </div>
                     <div class="ratio-tip" :class="archiveColorObj[route.query.title]">
@@ -23,25 +23,25 @@
                     </div>
                 </div>
                 <div class="card-box region-card">
-                    <div class="card-title">区域勾选</div>
+                    <div class="card-title">{{ t('区域勾选') }}</div>
                     <div class="map-container" ref="mapContainer"></div>
                 </div>
             </div>
             <div class="card-wrap" v-else>
                 <div class="card-box risk-card">
                     <div class="card-title">
-                        <span>农情详情</span>
+                        <span>{{ t('农情详情') }}</span>
                         <span class="date">2025.05.06</span>
                     </div>
                     <img class="risk-image" src="" alt="">
                     <div class="risk-content">
                         <p class="risk-highlight">
-                            <span class="risk-title">高湿风险:</span>
-                            <span class="risk-text">预计未来7天平均相对湿度≥85%,伴随间歇性小雨或雾天。</span>
+                            <span class="risk-title">{{ t('高湿风险:') }}</span>
+                            <span class="risk-text">{{ t('预计未来7天平均相对湿度≥85%,伴随间歇性小雨或雾天。') }}</span>
                         </p>
-                        <p>潜在危害:田间湿度过高极易诱发霜疫霉病、炭疽病等真菌病害,病菌侵染幼果后导致腐烂、脱落。影响幼果正常发育和果皮着色,可能造成裂果、烂果。</p>
-                        <p>高湿环境利于蒂蛀虫等害虫繁殖,成虫活动增加,增加后期虫果率。</p>
-                        <div class="warning-tip">预警等级:黄色预警(中等风险),建议在未来48小时内启动预防措施。</div>
+                        <p>{{ t('潜在危害:田间湿度过高极易诱发霜疫霉病、炭疽病等真菌病害,病菌侵染幼果后导致腐烂、脱落。影响幼果正常发育和果皮着色,可能造成裂果、烂果。') }}</p>
+                        <p>{{ t('高湿环境利于蒂蛀虫等害虫繁殖,成虫活动增加,增加后期虫果率。') }}</p>
+                        <div class="warning-tip">{{ t('预警等级:黄色预警(中等风险),建议在未来48小时内启动预防措施。') }}</div>
                     </div>
                 </div>
             </div>
@@ -50,6 +50,8 @@
 </template>
 
 <script setup>
+import { useI18n } from "@/i18n";
+const { t } = useI18n();
 import { useRoute } from "vue-router";
 import { ref, onActivated, computed } from "vue";
 import customHeader from "@/components/customHeader.vue";

+ 5 - 5
src/views/old_mini/monitor/subPages/darwArea.vue

@@ -15,7 +15,7 @@
             </div>
         </div>
         <div class="edit-map-content">
-            <div class="edit-map-tip" v-if="!viewOnly">操作提示:拖动圆点,即可调整地块边界</div>
+            <div class="edit-map-tip" v-if="!viewOnly">{{ $t('操作提示:拖动圆点,即可调整地块边界') }}</div>
             <div class="map-container" ref="mapContainer"></div>
             <div class="edit-map-footer">
                 <div v-if="viewOnly" class="footer-back" @click="deletePolygon">
@@ -23,14 +23,14 @@
                 </div>
                 <div class="edit-map-footer-btn" :class="{ 'confirm-btn-box': viewOnly }">
                     <template v-if="!viewOnly">
-                        <div class="btn-reset" @click="resetPolygon">重置区域</div>
-                        <div class="btn-confirm" @click="openConfirmDrawTypePopup">确认区域</div>
+                        <div class="btn-reset" @click="resetPolygon">{{ $t('重置区域') }}</div>
+                        <div class="btn-confirm" @click="openConfirmDrawTypePopup">{{ $t('确认区域') }}</div>
                     </template>
-                    <div v-else class="btn-confirm" @click="handleEditRegion">编辑区域</div>
+                    <div v-else class="btn-confirm" @click="handleEditRegion">{{ $t('编辑区域') }}</div>
                 </div>
             </div>
         </div>
-        <tip-popup v-model:show="showTipPopup" type="success" text="您的农情报告已生成" text2="请查看" buttonText="点击查看"
+        <tip-popup v-model:show="showTipPopup" type="success" :text="$t('您的农情报告已生成')" text2="请查看" buttonText="点击查看"
             @confirm="handleTipConfirm" />
 
         <confirm-draw-type-popup ref="confirmDrawTypePopupRef" @confirm="handleConfirmDrawType" />

+ 25 - 23
src/views/old_mini/monitor/subPages/farmInfo.vue

@@ -1,42 +1,42 @@
 <template>
-    <custom-header name="农场基本信息" bgColor="#f2f3f5"></custom-header>
+    <custom-header :name="$t('农场基本信息')" bgColor="#f2f3f5"></custom-header>
     <div class="farm-details-page">
         <div class="info-box info-card">
             <div class="section-header">
-                <div class="line-title">基本信息</div>
+                <div class="line-title">{{ t('基本信息') }}</div>
                 <div class="edit-btn-box">
-                    <div class="edit-btn" @click="handleAddVariety">新增品种</div>
-                    <div class="edit-btn" @click="handleEditFarmInfo">编辑信息</div>
+                    <div class="edit-btn" @click="handleAddVariety">{{ t('新增品种') }}</div>
+                    <div class="edit-btn" @click="handleEditFarmInfo">{{ t('编辑信息') }}</div>
                 </div>
             </div>
             <div class="info-list">
                 <div class="info-row">
-                    <span class="info-label">农场名称:</span>
+                    <span class="info-label">{{ t('农场名称:') }}</span>
                     <span class="info-value">{{ farmInfo.subjectName }}</span>
                 </div>
                 <div class="info-row">
-                    <span class="info-label">农场面积:</span>
+                    <span class="info-label">{{ t('农场面积:') }}</span>
                     <span class="info-value">{{ farmInfo.farmArea }}亩</span>
                 </div>
                 <div class="info-row">
-                    <span class="info-label">农场位置:</span>
+                    <span class="info-label">{{ t('农场位置:') }}</span>
                     <span class="info-value">{{ farmInfo.farmAddress }}</span>
                 </div>
             </div>
         </div>
         <div class="info-box info-card">
             <div class="section-header">
-                <div class="line-title">土壤性质</div>
-                <div class="edit-btn" @click="handleEditSoil">编辑信息</div>
+                <div class="line-title">{{ t('土壤性质') }}</div>
+                <div class="edit-btn" @click="handleEditSoil">{{ t('编辑信息') }}</div>
             </div>
             <div class="info-list grid-list">
                 <div class="grid-item">
-                    <div class="grid-value">壤土</div>
-                    <div class="grid-name">质地</div>
+                    <div class="grid-value">{{ t('壤土') }}</div>
+                    <div class="grid-name">{{ t('质地') }}</div>
                 </div>
                 <div class="grid-item">
                     <div class="grid-value">1%</div>
-                    <div class="grid-name">有机质</div>
+                    <div class="grid-name">{{ t('有机质') }}</div>
                 </div>
                 <div class="grid-item">
                     <div class="grid-value">5.5</div>
@@ -46,29 +46,29 @@
         </div>
         <div class="info-box info-card">
             <div class="section-header">
-                <div class="line-title">历史高发风险</div>
-                <div class="edit-btn only-text" @click="handleEditFarmFacility">查看更多<el-icon><ArrowRight /></el-icon></div>
+                <div class="line-title">{{ t('历史高发风险') }}</div>
+                <div class="edit-btn only-text" @click="handleEditFarmFacility">{{ t('查看更多') }}<el-icon><ArrowRight /></el-icon></div>
             </div>
             <div class="info-list">
-                <span class="text-label">物候风险:</span> 风险描述风险描述风险描述风险描述风险
+                <span class="text-label">{{ t('物候风险:') }}</span> 风险描述风险描述风险描述风险描述风险
             </div>
         </div>
         <div class="info-box info-card">
             <div class="section-header">
-                <div class="line-title">种植类别</div>
+                <div class="line-title">{{ t('种植类别') }}</div>
             </div>
             <div class="info-list">
                 <div class="info-row">
-                    <span class="info-label">过往种植:</span>
+                    <span class="info-label">{{ t('过往种植:') }}</span>
                     <div class="info-value crop-tags">
                         <span v-for="crop in farmInfo.regionList" :key="crop.regionId" class="crop-tag dark-tag">
                             {{ crop.regionName }}
                         </span>
-                        <span class="crop-tag border-tag" @click="handleAddVariety">+新增品种</span>
+                        <span class="crop-tag border-tag" @click="handleAddVariety">{{ t('+新增品种') }}</span>
                     </div>
                 </div>
                 <div class="info-row line-break">
-                    <div class="info-label">当季作物:</div>
+                    <div class="info-label">{{ t('当季作物:') }}</div>
                     <div class="season-box">
                         <div class="info-value crop-tags">
                             <span class="crop-tag">
@@ -76,8 +76,8 @@
                             </span>
                         </div>
                         <div class="season-content">
-                            <div class="season-item">桂味-上市时间:06.08</div>
-                            <div class="season-item">桂味-上市时间:06.08</div>
+                            <div class="season-item">{{ t('桂味-上市时间:06.08') }}</div>
+                            <div class="season-item">{{ t('桂味-上市时间:06.08') }}</div>
                         </div>
                     </div>
                     <div class="season-box">
@@ -87,8 +87,8 @@
                             </span>
                         </div>
                         <div class="season-content">
-                            <div class="season-item">桂味-上市时间:06.08</div>
-                            <div class="season-item">桂味-上市时间:06.08</div>
+                            <div class="season-item">{{ t('桂味-上市时间:06.08') }}</div>
+                            <div class="season-item">{{ t('桂味-上市时间:06.08') }}</div>
                         </div>
                     </div>
                 </div>
@@ -101,6 +101,8 @@
 </template>
 
 <script setup>
+import { useI18n } from "@/i18n";
+const { t } = useI18n();
 import { ref, onMounted } from "vue";
 import customHeader from "@/components/customHeader.vue";
 import { useRouter, useRoute } from "vue-router";

+ 10 - 10
src/views/old_mini/monitor/subPages/plan.vue

@@ -11,7 +11,7 @@
                 <el-select
                     class="select-item"
                     v-model="specieValue"
-                    placeholder="选择品类"
+                    :placeholder="$t('选择品类')"
                     @change="() => getListMySchemes('left')"
                 >
                     <el-option
@@ -78,11 +78,11 @@
                         {{ active === tabs[0]?.id ? "复制方案" : "方案设置" }}
                     </div>
                 </div>
-                <div class="bottom-btn primary-btn" @click="addNewTask" v-show="active !== tabs[0]?.id">新增农事</div>
+                <div class="bottom-btn primary-btn" @click="addNewTask" v-show="active !== tabs[0]?.id">{{ $t('新增农事') }}</div>
             </div>
             <template v-if="active !== tabs[0]?.id && pageType == 'plant' && tabs[currentTabIndex]?.enabled == 0">
                 <div class="bottom-btn-divider"></div>
-                <div class="bottom-btn primary-btn submit-btn" @click="handleSubmitPlan">提交方案</div>
+                <div class="bottom-btn primary-btn submit-btn" @click="handleSubmitPlan">{{ $t('提交方案') }}</div>
             </template>
         </div>
     </div>
@@ -92,7 +92,7 @@
     <Popup v-model:show="showCopyPlan" class="copy-plan-popup" round closeable :close-on-click-overlay="false">
         <div class="copy-plan-content">
             <div class="label">{{ active === tabs[0]?.id ? "复制为" : "方案名称" }}</div>
-            <el-input v-model="copyPlanName" size="large" placeholder="请输入方案名称" class="copy-plan-input" />
+            <el-input v-model="copyPlanName" size="large" :placeholder="$t('请输入方案名称')" class="copy-plan-input" />
         </div>
         <div class="copy-plan-footer">
             <div class="btn btn-cancel" @click="handleCancelCopyPlan">
@@ -112,12 +112,12 @@
         closeable
         :close-on-click-overlay="false"
     >
-        <div class="phenology-header">物候期时间设置</div>
+        <div class="phenology-header">{{ $t('物候期时间设置') }}</div>
         <div class="phenology-list">
             <div class="phenology-item" v-for="(item, index) in mergedReproductiveList" :key="item.id || index">
                 <div class="item-label">
                     <span class="label-text">{{ item.name }}</span>
-                    <span>起始时间</span>
+                    <span>{{ $t('起始时间') }}</span>
                 </div>
                 <div class="item-value">
                     <el-date-picker
@@ -128,17 +128,17 @@
                         :clearable="false"
                         :editable="false"
                         type="date"
-                        placeholder="选择日期"
+                        :placeholder="$t('选择日期')"
                         @change="(date) => handleStartDateChange(date, index)"
                     />
                 </div>
             </div>
             <div class="phenology-footer-tip">
-                <span>注:</span>
-                <span class="text">请从上往下按照时间顺序填写日期</span>
+                <span>{{ $t('注:') }}</span>
+                <span class="text">{{ $t('请从上往下按照时间顺序填写日期') }}</span>
             </div>
         </div>
-        <div class="phenology-footer" @click="handleConfirmPhenologySetting">确认设置</div>
+        <div class="phenology-footer" @click="handleConfirmPhenologySetting">{{ $t('确认设置') }}</div>
     </Popup>
 
     <tip-popup

+ 6 - 6
src/views/old_mini/monitor/subPages/reviewResults.vue

@@ -1,5 +1,5 @@
 <template>
-    <custom-header name="复核成效"></custom-header>
+    <custom-header :name="$t('复核成效')"></custom-header>
     <div class="farm-dynamics">
         <div class="task-content">
             <div class="expert-content">
@@ -8,8 +8,8 @@
                         v-model:loading="loading"
                         :finished="finished"
                         :offset="100"
-                        loading-text="加载中..."
-                        finished-text="没有更多了"
+                        loading-:text="$t('加载中...')"
+                        finished-:text="$t('没有更多了')"
                         @load="onLoad"
                     >
                         <div v-for="(section, index) in contentData" :key="index" class="content-section">
@@ -26,7 +26,7 @@
                             >
                                 <template #footer>
                                     <div class="action-group" v-if="!section.reviewImage.length">
-                                        <div class="action-l">查看详情</div>
+                                        <div class="action-l">{{ $t('查看详情') }}</div>
                                         <div class="action-r">
                                             <div
                                                 class="action-item warning-item"
@@ -57,8 +57,8 @@
     <!-- 需求发送成功弹窗 -->
     <popup v-model:show="showApplyPopup" round class="apply-popup">
         <img class="check-icon" src="@/assets/img/home/right.png" alt="" />
-        <div class="apply-text">需求发送成功</div>
-        <div class="apply-btn" @click="showApplyPopup = false">我知道了</div>
+        <div class="apply-text">{{ $t('需求发送成功') }}</div>
+        <div class="apply-btn" @click="showApplyPopup = false">{{ $t('我知道了') }}</div>
     </popup>
 
     <!-- 上传照片弹窗 -->

+ 4 - 4
src/views/old_mini/monitor/subPages/soilPopup.vue

@@ -1,7 +1,7 @@
 <template>
     <Popup class="soil-popup" round v-model:show="show">
         <div class="soil-content">
-            <div class="section-title">请选择您的土壤类型</div>
+            <div class="section-title">{{ $t('请选择您的土壤类型') }}</div>
             <div class="slider-wrap">
                 <Slider
                     v-model="soilIndex"
@@ -40,7 +40,7 @@
                 </div>
             </div>
 
-            <div class="section-title ph-title">调整pH值</div>
+            <div class="section-title ph-title">{{ $t('调整pH值') }}</div>
             <div class="slider-wrap">
                 <Slider
                     v-model="phIndex"
@@ -80,8 +80,8 @@
             </div>
         </div>
         <div class="popup-button">
-            <div class="button-item cancel" @click="close">取消</div>
-            <div class="button-item confirm" @click="handleConfirm">确认信息</div>
+            <div class="button-item cancel" @click="close">{{ $t('取消') }}</div>
+            <div class="button-item confirm" @click="handleConfirm">{{ $t('确认信息') }}</div>
         </div>
     </Popup>
 </template>

+ 165 - 132
src/views/old_mini/recordDetails/index.vue

@@ -1,25 +1,25 @@
 <template>
     <div class="record-wrap">
-        <custom-header name="农事名称"></custom-header>
+        <custom-header :name="t('recordDetails.workName')"></custom-header>
         <div class="record-content">
-            <div class="record-header" v-if="route.query.type == '长势异常态势跟踪'">
-                <span>长势异常态势跟踪</span>
-                <div class="question">果径是否显著小于正常果,且果皮失去光泽、呈现暗哑状态?</div>
+            <div class="record-header" v-if="recordType === 'growth'">
+                <span>{{ t('agriRecord.growthWorkName') }}</span>
+                <div class="question">{{ t('recordDetails.growthQuestion') }}</div>
             </div>
-            <div class="record-header" v-else-if="route.query.type == '病虫害态势监控'">
-                <span>病虫害态势监控</span>
-                <div class="question">蒂蛀虫、荔枝蝽蟓、炭疽病、霜疫霉病</div>
+            <div class="record-header" v-else-if="recordType === 'pest'">
+                <span>{{ t('agriRecord.pestWorkName') }}</span>
+                <div class="question">{{ t('recordDetails.pestQuestion') }}</div>
             </div>
             <div class="record-header" v-else>
-                <span>{{ workDetail.interaction_type }}</span>
-                <div class="question">果实是否开始由扁圆变圆、横径明显增大?</div>
+                <span>{{ t('agriRecord.phenologyWorkName') }}</span>
+                <div class="question">{{ t('recordDetails.phenologyQuestion') }}</div>
             </div>
             <div class="record-body">
-                <div class="card-wrap" v-if="route.query.type == '长势异常态势跟踪'">
+                <div class="card-wrap" v-if="recordType === 'growth'">
                     <div class="card-item">
-                        <span class="item-label">科普知识:</span>
-                        <text-ellipsis class="item-value" expand-text="展开" collapse-text="收起" rows="3"
-                            content="当前荔枝处于果实膨大期,近期存在强降水和强对流天气频发的情况,且当前识别到阴雨气象风险较高3级,累计阴雨天数达12天、日均湿度约91%。连续阴雨导致光照不足,月累计日照时数较常年偏少17%,叶片光合效率下降,碳水化合物合成减少" />
+                        <span class="item-label">{{ t('recordDetails.scienceLabel') }}</span>
+                        <text-ellipsis class="item-value" :expand-text="expandCollapse.expand" :collapse-text="expandCollapse.collapse" rows="3"
+                            :content="t('recordDetails.growthScience')" />
                     </div>
                     <div class="tabs-list" v-if="!showMap">
                         <div class="item-tab" :class="{ 'item-tab--active': activeTab1 === index }"
@@ -27,21 +27,21 @@
                         </div>
                     </div>
                     <div class="card-item">
-                        <span class="item-label">表型特征:</span>
-                        <text-ellipsis class="item-value" expand-text="展开" collapse-text="收起" rows="3"
-                            content="果实膨大速度慢,果皮颜色黄绿、光泽度差,严重时果面出现细微水裂纹;萎蔫时叶片下垂卷曲;缺氮时老叶均匀黄化;缺钾时老叶叶缘焦枯;缺镁时老叶叶肉黄化、叶脉仍绿;缺钙时果实易裂果;缺硼时果实发育不良、果皮木栓化;缺锌时果实大小不均;缺铁时新叶黄白失绿" />
+                        <span class="item-label">{{ t('recordDetails.phenotypeLabel') }}</span>
+                        <text-ellipsis class="item-value" :expand-text="expandCollapse.expand" :collapse-text="expandCollapse.collapse" rows="3"
+                            :content="t('recordDetails.growthPhenotype')" />
                     </div>
                     <div class="card-item">
-                        <span class="item-label">高发区域:</span>
-                        <text-ellipsis class="item-value" expand-text="展开" collapse-text="收起" rows="3"
-                            content="重点巡视低洼地、排水不畅的粘重土壤果园,以及树冠中下部及内膛果实。观察果实膨大进度,对比不同方位果穗大小差异;观察叶片萎蔫程度和黄化部位。拔根检查根系状态——砂土看根系是否偏白不烂,黏土看是否发软发臭,酸性土看根尖是否膨大变黑,盐碱土看是否被泥浆包裹,重金属土看是否发黑有异味,荔枝土看根尖是否变黑腐臭。根系受损时先排水松土养根,暂不施根肥,叶面维持,等新根萌发后再施根肥。发现异常症状后请拍照上传,及时存档。" />
+                        <span class="item-label">{{ t('recordDetails.highRiskLabel') }}</span>
+                        <text-ellipsis class="item-value" :expand-text="expandCollapse.expand" :collapse-text="expandCollapse.collapse" rows="3"
+                            :content="t('recordDetails.growthHighRisk')" />
                     </div>
                 </div>
-                <div class="card-wrap" v-else-if="route.query.type == '病虫害态势监控'">
+                <div class="card-wrap" v-else-if="recordType === 'pest'">
                     <div class="card-item">
-                        <span class="item-label">科普知识:</span>
-                        <text-ellipsis class="item-value" expand-text="展开" collapse-text="收起" rows="3"
-                            content="当前荔枝处于果期,预测田间病虫害气象风险2级,近期湿度达88%、日均气温达30℃,该环境条件十分有利于害虫活动和病原菌侵染。果期是荔枝产量形成的关键阶段,幼果皮层薄嫩、抗逆性弱,一旦遭受蒂蛀虫、荔枝蝽象、霜疫霉病、炭疽病等主要病虫为害,将直接导致落果、烂果,严重影响产量和品质。需抓紧巡园,同步做好虫情、病情的动态监测,防范风险升级。" />
+                        <span class="item-label">{{ t('recordDetails.scienceLabel') }}</span>
+                        <text-ellipsis class="item-value" :expand-text="expandCollapse.expand" :collapse-text="expandCollapse.collapse" rows="3"
+                            :content="t('recordDetails.pestScience')" />
                     </div>
                     <div class="pest-classify-picker">
                         <div class="pest-classify-picker__row pest-classify-picker__row--top">
@@ -64,32 +64,31 @@
                         </div>
                     </div>
                     <div class="card-item">
-                        <span class="item-label">表型特征:</span>
-                        <text-ellipsis class="item-value" expand-text="展开" collapse-text="收起" rows="3"
-                            :content="text1" />
+                        <span class="item-label">{{ t('recordDetails.phenotypeLabel') }}</span>
+                        <text-ellipsis class="item-value" :expand-text="expandCollapse.expand" :collapse-text="expandCollapse.collapse" rows="3"
+                            :content="pestText1Content" />
                     </div>
                     <div class="card-item">
-                        <span class="item-label">高发区域:</span>
-                        <text-ellipsis class="item-value" expand-text="展开" collapse-text="收起" rows="3"
-                            :content="text2" />
+                        <span class="item-label">{{ t('recordDetails.highRiskLabel') }}</span>
+                        <text-ellipsis class="item-value" :expand-text="expandCollapse.expand" :collapse-text="expandCollapse.collapse" rows="3"
+                            :content="pestText2Content" />
                     </div>
                 </div>
                 <div class="card-wrap" v-else>
                     <div class="card-item">
-                        <span class="item-label">农情研判:</span>
-                        <text-ellipsis class="item-value" expand-text="展开" collapse-text="收起" rows="3"
-                            content="膨果期果实开始横向生长,由扁圆形逐渐转为圆球形,是果肉细胞快速分裂膨大和干物质积累的关键阶段,氮和钾需求双高。氮促进果肉细胞分裂膨大,钾促进光合产物转运和糖分积累。此时养分供应不足会导致果实膨大受阻、单果重偏低,需及时根部追肥保障果实发育。" />
+                        <span class="item-label">{{ t('recordDetails.farmAnalysisLabel') }}</span>
+                        <text-ellipsis class="item-value" :expand-text="expandCollapse.expand" :collapse-text="expandCollapse.collapse" rows="3"
+                            :content="t('recordDetails.phenologyAnalysis')" />
                     </div>
                     <div class="card-item">
-                        <span class="item-label">巡园要点:</span>
-                        <text-ellipsis class="item-value" expand-text="展开" collapse-text="收起" rows="3"
+                        <span class="item-label">{{ t('recordDetails.patrolLabel') }}</span>
+                        <text-ellipsis class="item-value" :expand-text="expandCollapse.expand" :collapse-text="expandCollapse.collapse" rows="3"
                             :content="workDetail.inspection_keypoints" />
                     </div>
                     <div class="card-item">
-                        <span class="item-label">表型特征:</span>
-                        <text-ellipsis class="item-value" expand-text="展开" collapse-text="收起" rows="3"
-                            content="果实由扁圆形开始横向增宽,果皮青绿色有光泽,果实横径明显增大,果肩逐渐饱满。
-巡园要点:重点观察树冠外围中上部结果枝,检查果实形状变化和横径大小,统计进入快速膨大的果实比例。拍照记录果实膨大状态,上传存档。" />
+                        <span class="item-label">{{ t('recordDetails.phenotypeLabel') }}</span>
+                        <text-ellipsis class="item-value" :expand-text="expandCollapse.expand" :collapse-text="expandCollapse.collapse" rows="3"
+                            :content="t('recordDetails.phenologyPhenotype')" />
                     </div>
                 </div>
                 <!-- <div class="tabs-list" v-if="!showMap">
@@ -109,23 +108,24 @@
                         </div>
                         <div class="my-map">
                             <div class="map-container" ref="mapContainer">
-                                <div class="tip">点击勾画新发生区域</div>
+                                <div class="tip">{{ t('recordDetails.drawTip') }}</div>
                             </div>
                             <div class="confirm-btn-wrap">
-                                <div class="cancel-btn">暂未到达进程</div>
-                                <div class="confirm-btn">确认上传</div>
+                                <div class="cancel-btn">{{ t('recordDetails.notReached') }}</div>
+                                <div class="confirm-btn">{{ t('recordDetails.confirmUpload') }}</div>
                             </div>
                         </div>
                     </div>
-                    <div v-if="!showMap && route.query.type == '物候跟踪记录'" class="border-wrap no-map-wrap">
+                    <div v-if="!showMap && recordType === 'phenology'" class="border-wrap no-map-wrap">
                         <div class="question-info">
-                            <span class="title">关键防治需肥期评估:</span>
-                            <span class="content">是否有60%的荔枝进入膨果期?                            </span>
-                            <div class="current-status">当前现状:当前{{ curStage.label }}进入到{{ curStage.periodTitle }}</div>
+                            <span class="title">{{ t('recordDetails.keyAssessment') }}</span>
+                            <span class="content">{{ t('recordDetails.phenologyIssue') }}</span>
+                            <div class="current-status">{{ currentStatusText }}</div>
                         </div>
                         <div class="time-line">
                             <GrowthStageTimeline v-model="growthStageIndex" :stages="growthStages"
-                                @scroll-settled="onStageScrollSettled" />
+                                @scroll-settled="onStageScrollSettled"
+                                @locale-change="getFindPhenologyInfo" />
                             <!-- <GrowthStageTimeline v-model="growthStageIndex" :stages="growthStages" /> -->
                         </div>
                         <div class="confirm-btn-wrap">
@@ -135,26 +135,26 @@
                                     <el-icon>
                                         <Plus />
                                     </el-icon>
-                                    <span>点击上传照片</span>
+                                    <span>{{ t('recordDetails.uploadPhoto') }}</span>
                                 </div>
                             </uploader>
-                            <div class="confirm-btn" @click="hanldeSubmit">确认信息</div>
+                            <div class="confirm-btn" @click="hanldeSubmit">{{ t('recordDetails.confirmInfo') }}</div>
                         </div>
                     </div>
                     <div class="phenology-track-section">
-                        <PhenologyTrackTimelineItem v-for="(row, idx) in phenologyTrackList" :key="idx" :date="row.date"
+                        <PhenologyTrackTimelineItem v-for="(row, idx) in displayPhenologyTrackList" :key="idx" :date="row.date"
                             :content="row.content" :images="row.images" />
                     </div>
                 </div>
             </div>
         </div>
         <!-- <div class="custom-bottom-fixed-btns">
-            <div class="bottom-btn secondary-btn">转发记录</div>
+            <div class="bottom-btn secondary-btn">{{ $t('转发记录') }}</div>
         </div> -->
 
         <!-- <div class="phenology-tip-banner">
             <div class="banner__left">
-                <div class="banner__title">物候不整齐?</div>
+                <div class="banner__title">{{ $t('物候不整齐?') }}</div>
                 <span class="banner__desc">
                     如果区域长势不同,会降低病虫害防治功效,
                     建议根据长势拆分区域,进行分区精细管理,
@@ -166,23 +166,23 @@
             </div>
         </div> -->
 
-        <div class="phenology-tip-banner" v-if="route.query.type != '物候跟踪记录'">
+        <div class="phenology-tip-banner" v-if="recordType !== 'phenology'">
             <div class="banner__left">
                 <div class="banner__title">
                     <el-icon size="17">
                         <WarningFilled />
                     </el-icon>
-                    <span>发现异常,拍照记录</span>
+                    <span>{{ t('recordDetails.abnormalBanner') }}</span>
                 </div>
                 <span class="banner__desc">
-                    系统为您智能匹配农事方案
+                    {{ t('recordDetails.abnormalBannerDesc') }}
                 </span>
             </div>
             <!-- <div class="banner__btn" @click="goPartitionManage">
                 分区管理
             </div> -->
             <div class="banner__btn" @click="handleAbnormalRecord">
-                异常记录
+                {{ t('recordDetails.abnormalRecord') }}
             </div>
         </div>
 
@@ -191,8 +191,8 @@
             @cancel="handleCancelUploadPopup" @confirm="handleConfirmUpload" @handleUpload="handleUploadSuccess">
             <template #header>
                 <div class="upload-progress-title">
-                    <span class="label">当前现状:</span>
-                    <span class="value">60% 进入红黄叶进程</span>
+                    <span class="label">{{ $t('当前现状:') }}</span>
+                    <span class="value">{{ $t('60% 进入红黄叶进程') }}</span>
                 </div>
             </template>
         </UploadProgressPopup> -->
@@ -203,8 +203,8 @@
             <template #header>
                 <div class="upload-form">
                     <div class="form-item aaa">
-                        <div class="item-label" style="font-size: 14px;color: #5A5A5A;">有多少植株出现了异常?</div>
-                        <el-input v-model="formData.regionName" size="large" placeholder="请输入">
+                        <div class="item-label" style="font-size: 14px;color: #5A5A5A;">{{ t('recordDetails.abnormalCount') }}</div>
+                        <el-input v-model="formData.regionName" size="large" :placeholder="t('recordDetails.inputPlaceholder')">
                             <template #suffix>
                                 %
                             </template>
@@ -215,11 +215,11 @@
             <template #footer>
                 <div class="upload-form">
                     <div class="form-item">
-                        <div class="item-label">巡园描述</div>
-                        <el-input v-model="formData.regionName" size="large" placeholder="请输入描述" />
+                        <div class="item-label">{{ t('recordDetails.patrolDesc') }}</div>
+                        <el-input v-model="formData.regionName" size="large" :placeholder="t('recordDetails.inputDescPlaceholder')" />
                     </div>
                     <div class="map-container" ref="mapContainer">
-                        <div class="tip">点击勾画新发生区域</div>
+                        <div class="tip">{{ t('recordDetails.drawTip') }}</div>
                     </div>
                 </div>
             </template>
@@ -232,8 +232,10 @@ import customHeader from "@/components/customHeader.vue";
 import PhenologyTrackTimelineItem from "@/components/pageComponents/PhenologyTrackTimelineItem.vue";
 import GrowthStageTimeline from "@/components/pageComponents/GrowthStageTimeline.vue";
 import UploadProgressPopup from "@/components/popup/UploadProgressPopup.vue";
-import { ref, onMounted, watch, nextTick } from 'vue';
+import { ref, onMounted, watch, nextTick, computed } from 'vue';
 import { useRouter, useRoute } from 'vue-router';
+import { useI18n } from '@/i18n';
+import { RECORD_KEY_MAP } from '@/i18n/recordTextMap';
 import { Uploader, TextEllipsis } from "vant";
 import { ElMessage } from "element-plus";
 import UploadFile from "@/utils/upliadFile";
@@ -243,6 +245,24 @@ import { Plus } from "@element-plus/icons-vue";
 
 const router = useRouter();
 const route = useRoute();
+const { t } = useI18n();
+
+const TYPE_LEGACY_MAP = {
+    长势异常态势跟踪: 'growth',
+    病虫害态势监控: 'pest',
+    物候跟踪记录: 'phenology',
+};
+
+const recordType = computed(() => {
+    const type = route.query.type;
+    return TYPE_LEGACY_MAP[type] || type || 'phenology';
+});
+
+const expandCollapse = computed(() => ({
+    expand: t('recordDetails.expand'),
+    collapse: t('recordDetails.collapse'),
+}));
+
 function goPartitionManage() {
     router.push({ name: 'MapManage' });
 }
@@ -251,65 +271,72 @@ const showMap = ref(false);
 
 /** 病虫害态势监控:分类选择(接入接口后可绑定 workDetail) */
 const pestTopIndex = ref(0);
-const pestTopLabels = ['病害', '虫害'];
 const pestCategoryIndex = ref(0);
 const pestDetailIndex = ref(0);
-const pestCategoryLabels = ref(['真菌类', '钻蛀类', '病毒类', '其它']);
-const pestDetailLabels = ref(['霜疫霉病', '炭疽病', '叶瘟', '纹枯病']);
+const pestDetailKeys = ref(['detDownyMildew', 'detAnthracnose', 'detBlast', 'detSheathBlight']);
+
+const pestTopLabels = computed(() => [t('recordDetails.pestDisease'), t('recordDetails.pestInsect')]);
+
+const pestCategoryLabels = computed(() => {
+    if (pestTopIndex.value === 0) {
+        return ['catFungal', 'catBorer', 'catVirus', 'catOther'].map((k) => t(`recordDetails.${k}`));
+    }
+    return ['catChewing', 'catMasticate', 'catBorer', 'catOther'].map((k) => t(`recordDetails.${k}`));
+});
+
+const pestDetailLabels = computed(() =>
+    pestDetailKeys.value.map((k) => t(`recordDetails.${k}`))
+);
+
+const pestText1Content = computed(() => {
+    const top = pestTopIndex.value;
+    const cat = pestCategoryIndex.value;
+    const det = pestDetailIndex.value;
+    if (top === 0) {
+        return det === 0 ? t('recordDetails.pestT1_downy') : t('recordDetails.pestT1_anthracnose');
+    }
+    if (cat === 0) return t('recordDetails.pestT1_stinkbug');
+    if (cat !== 0 && det !== 0) return t('recordDetails.pestT1_stemborer');
+    return t('recordDetails.pestT1_riceborer');
+});
+
+const pestText2Content = computed(() => {
+    const top = pestTopIndex.value;
+    const cat = pestCategoryIndex.value;
+    const det = pestDetailIndex.value;
+    if (top === 0) {
+        return det === 0 ? t('recordDetails.pestT2_downy') : t('recordDetails.pestT2_anthracnose');
+    }
+    if (cat === 0) return t('recordDetails.pestT2_stinkbug');
+    if (cat !== 0 && det !== 0) return t('recordDetails.pestT2_stemborer');
+    return t('recordDetails.pestT2_riceborer');
+});
 
-const text1 = ref('果实感病后出现水渍状褪绿斑块,湿度大时表面产生白色霜状霉层,后病部变褐软腐,果实腐烂脱落。膨大期至转色期果实感病后病斑扩展迅速,果肉褐变腐烂,有酸臭味,病果易脱落。')
-const text2 = ref('重点巡查果园低洼积水处、密蔽通风不良处的果穗,观察果实表面有无水渍状褪绿斑块,清晨或雨后湿度大时查看病斑表面有无白色霜状霉层,用手轻捏感病果实检查是否软腐,闻有无酸臭味。发现病斑后请拍照上传,及时存档,便于记录发生程度与防控节点。')
 const handlePestTopClick = (index) => {
     pestTopIndex.value = index;
-    handlePestCategoryClick(0)
+    pestCategoryIndex.value = 0;
+    pestDetailIndex.value = 0;
     if (index === 0) {
-        pestCategoryLabels.value = ['真菌类', '钻蛀类', '病毒类', '其它'];
-        pestDetailLabels.value = ['霜疫霉病', '炭疽病', '叶瘟', '纹枯病'];
+        pestDetailKeys.value = ['detDownyMildew', 'detAnthracnose', 'detBlast', 'detSheathBlight'];
     } else {
-        pestCategoryLabels.value = ['啃食类', '咀嚼式', '钻蛀类', '其它'];
-        pestDetailLabels.value = ['荔枝蝽象'];
+        pestDetailKeys.value = ['detStinkBug'];
     }
 };
 
 const handlePestCategoryClick = (index) => {
     pestCategoryIndex.value = index;
-    handlePestDetailClick(0)
-    if (index === 0) {
-        if (pestTopIndex.value == 1) {
-            pestDetailLabels.value = ['荔枝蝽象'];
-        } else {
-            pestDetailLabels.value = ['霜疫霉病', '炭疽病', '叶瘟', '纹枯病'];
-        }
+    pestDetailIndex.value = 0;
+    if (pestTopIndex.value === 1) {
+        pestDetailKeys.value = index === 0 ? ['detStinkBug'] : ['detRiceBorer'];
+    } else if (index !== 0) {
+        pestDetailKeys.value = ['detStemBorer'];
     } else {
-        if (pestTopIndex.value == 1) {
-            pestDetailLabels.value = ['二化螟'];
-        } else {
-            text1.value = '成虫为小型蛾类,幼虫钻蛀果实,从果蒂附近蛀入果心,蛀食果核和果肉,造成果实内部充满虫粪。受害果实外表初期无明显异常,后期果蒂周围变黑、果实提前着色变红脱落("红果"现象),落果剖开可见幼虫及虫粪。'
-            text2.value = '重点巡查树冠内膛和下垂枝的果穗,注意捡拾地面落果,剖开检查果核周围有无幼虫和虫粪。观察树上果实有无果蒂变黑、提前转红的异常果。成虫调查时,在晨昏成虫活动期用手掌用力拍打树冠内膛的枝干,观察是否有小蛾受惊飞出,以此判断成虫发生密度。关键防治节点需通过收集落果观察蛹的羽化进度来确定打药时机。发现为害状及活虫后请拍照上传,及时存档,便于记录发生程度与防控节点。'
-            pestDetailLabels.value = ['蒂蛀虫'];
-        }
+        pestDetailKeys.value = ['detDownyMildew', 'detAnthracnose', 'detBlast', 'detSheathBlight'];
     }
 };
 
 const handlePestDetailClick = (index) => {
     pestDetailIndex.value = index;
-    if (pestTopIndex.value === 0) {
-        if (index === 0) {
-            text1.value = '果实感病后出现水渍状褪绿斑块,湿度大时表面产生白色霜状霉层,后病部变褐软腐,果实腐烂脱落。膨大期至转色期果实感病后病斑扩展迅速,果肉褐变腐烂,有酸臭味,病果易脱落。';
-            text2.value = '重点巡查果园低洼积水处、密蔽通风不良处的果穗,观察果实表面有无水渍状褪绿斑块,清晨或雨后湿度大时查看病斑表面有无白色霜状霉层,用手轻捏感病果实检查是否软腐,闻有无酸臭味。发现病斑后请拍照上传,及时存档,便于记录发生程度与防控节点。';
-        } else {
-            text1.value = '果实感病后出现暗褐色水渍状斑点,后扩展为圆形或不规则形黑褐色凹陷病斑,边缘明显,湿度大时病斑表面出现粉红色黏稠孢子堆。病斑深入果肉导致腐烂,病果干缩后挂在枝头不脱落形成僵果。';
-            text2.value = '重点巡查树冠内膛及下部枝条的果穗,观察果实表面有无暗褐色至黑褐色凹陷病斑,病斑边缘是否清晰,在湿度大的清晨查看病斑表面有无粉红色黏稠物。注意与霜疫霉病区分:炭疽病病斑凹陷干缩,霜疫霉病病部湿软腐烂。发现病斑后请拍照上传,及时存档,便于记录发生程度与防控节点。';
-        }
-    }else {
-        if (pestCategoryIndex.value === 0) {
-            text1.value = '成虫和若虫刺吸幼果和嫩梢汁液,受害果实出现褐色坏死斑点,果实发育受阻、果形不正,严重时幼果大量脱落。成虫体长约25mm、黄褐色盾形,受惊时释放臭液;若虫体色鲜红至橙红,群集为害。'
-            text2.value = '重点巡查树冠中上部外围果穗集中处,观察果实和果梗上是否有黄褐色盾形成虫或红色群集若虫。轻摇枝条看是否有成虫受惊飞逃,同时闻是否有刺激性臭味。发现为害状及活虫后请拍照上传,及时存档,便于记录发生程度与防控节点。'
-        } else {
-            text1.value = '幼虫钻蛀稻茎基部,造成枯心苗,受害株心叶变黄枯死,茎秆基部有圆形蛀孔及虫粪排出,轻拔枯心苗易折断,断口可见幼虫或虫道'
-            text2.value = '重点巡查田间分散出现的枯心团或枯心带,沿行间逐株检查心叶是否变黄萎蔫。拔取枯心株,用刀片或指甲剥开茎秆基部,检查内部有无淡褐色或灰白色幼虫、虫粪及蛀道。发现为害状及活虫后请拍照上传,及时存档,便于记录发生程度与防控节点'
-        }
-    }
 };
 
 const indexMap = new IndexMap();
@@ -324,14 +351,21 @@ const tabsList = ref([
     { label: '分区四', value: 3 },
 ]);
 
-const activeTab1 = ref(0)
-const tabsList1 = ref([
-    { label: '膨果受阻', value: 0 },
-    { label: '长势缓慢', value: 1 },
-    { label: '植株旺长', value: 2 },
-    { label: '叶片萎蔫', value: 3 },
+const activeTab1 = ref(0);
+const tabsList1 = computed(() => [
+    { label: t('recordDetails.tabFruitBlocked'), value: 0 },
+    { label: t('recordDetails.tabSlowGrowth'), value: 1 },
+    { label: t('recordDetails.tabVigorous'), value: 2 },
+    { label: t('recordDetails.tabWilting'), value: 3 },
 ]);
 
+const currentStatusText = computed(() =>
+    t('recordDetails.currentStatus', {
+        label: curStage.value?.label || '',
+        period: curStage.value?.periodTitle || '',
+    })
+);
+
 const formData = ref({
     regionName: '',
 });
@@ -385,7 +419,7 @@ const hanldeSubmit = () => {
     }
     VE_API.monitor.farmPhenologyAdjust(params).then(res => {
         if (res.code === 200) {
-            ElMessage.success('确认成功')
+            ElMessage.success(t('recordDetails.confirmSuccess'))
         }
     })
 }
@@ -430,7 +464,7 @@ const handleCancelUploadPopup = () => {
 
 const handleConfirmUpload = () => {
     if (!uploadData.value || uploadData.value.length === 0) {
-        ElMessage.warning("请先上传照片");
+        ElMessage.warning(t('recordDetails.uploadPhotoFirst'));
         return;
     }
     showUploadProgressPopup.value = false;
@@ -438,8 +472,22 @@ const handleConfirmUpload = () => {
 
 const handleUploadClick = () => { };
 
-/** 物候跟踪时间轴示例数据,接入接口后可替换 */
-const phenologyTrackList = ref([]);
+/** 物候跟踪时间轴原始数据,接入接口后可替换 */
+const phenologyTrackRawList = ref([]);
+
+const formatTrackItemContent = (item) => {
+    const recordText = t(RECORD_KEY_MAP[item.record] || item.record);
+    const ratio = String(item.ratio ?? "").trim();
+    return ratio ? `${recordText}${ratio}%` : recordText;
+};
+
+const displayPhenologyTrackList = computed(() =>
+    phenologyTrackRawList.value.map((item) => ({
+        date: item.time,
+        content: formatTrackItemContent(item),
+        images: item.images || [],
+    }))
+);
 // const phenologyTrackList = ref([
 //     { date: '04/18', content: '有 60%已经来花了', images: [] },
 //     {
@@ -592,24 +640,9 @@ const getFarmRecord = async () => {
             ]
         }
     }
-    if (res.code === 200 && res.data?.abnormal.length) {
-        if(route.query.type == '物候跟踪记录'){
-            phenologyTrackList.value = res.data.phenology.map(item => {
-                return {
-                    date: item.time,
-                    content: item.record + item.ratio + '%',
-                    images: []
-                }
-            })
-        }else{
-            phenologyTrackList.value = res.data.abnormal.map(item => {
-                return {
-                    date: item.time,
-                    content: item.record + item.ratio + '%',
-                    images: []
-                }
-            })
-        }
+    if (res.code === 200 && res.data?.abnormal?.length) {
+        phenologyTrackRawList.value =
+            recordType.value === "phenology" ? res.data.phenology || [] : res.data.abnormal || [];
     }
 }
 

+ 11 - 11
src/views/old_mini/recordDetails/mapManage.vue

@@ -1,41 +1,41 @@
 <template>
     <div class="map-manage">
-        <custom-header name="勾画区域" />
+        <custom-header :name="$t('勾画区域')" />
         <div class="map-manage-content">
             <locationSearch class="location-search" @change="handleLocationChange"></locationSearch>
             <div class="map-container" ref="mapContainer"></div>
-            <div class="new-region-btn" v-show="!drawingEnabled" @click="onStartRegionDrawing">新建管理分区</div>
+            <div class="new-region-btn" v-show="!drawingEnabled" @click="onStartRegionDrawing">{{ $t('新建管理分区') }}</div>
             <div class="map-icon" v-show="drawingEnabled" @click="handleMapIconClick">
                 <img src="@/assets/img/map/map-icon.png" alt="">
             </div>
         </div>
         <div class="custom-bottom-fixed-btns" :style="{ justifyContent: drawingEnabled ? 'space-between' : 'center' }">
             <template v-if="drawingEnabled">
-                <div class="bottom-btn secondary-btn" @click="handleClearDraw">取消勾选</div>
-                <div class="bottom-btn primary-btn" @click="handleConfirmDraw">确认区域</div>
+                <div class="bottom-btn secondary-btn" @click="handleClearDraw">{{ $t('取消勾选') }}</div>
+                <div class="bottom-btn primary-btn" @click="handleConfirmDraw">{{ $t('确认区域') }}</div>
             </template>
-            <div v-else class="bottom-btn secondary-btn">邀请勾画</div>
+            <div v-else class="bottom-btn secondary-btn">{{ $t('邀请勾画') }}</div>
         </div>
 
         <UploadProgressPopup ref="uploadProgressPopupRef" v-model:show="showUploadProgressPopup"
             :popup-image-upload-loading="popupImageUploadLoading" :init-img-arr="initImgArr"
             @cancel="handleCancelUploadPopup" @confirm="handleConfirmUpload" @handleUpload="handleUploadSuccess"
-            confirm-text="点击上传">
+            confirm-:text="$t('点击上传')">
             <template #header>
                 <div class="upload-form">
                     <div class="form-item">
-                        <div class="item-label">区域名称</div>
-                        <el-input v-model="formData.regionName" size="large" placeholder="请输入区域名称" />
+                        <div class="item-label">{{ $t('区域名称') }}</div>
+                        <el-input v-model="formData.regionName" size="large" :placeholder="$t('请输入区域名称')" />
                     </div>
                     <div class="form-item">
-                        <div class="item-label">品种 <span class="optional">(多选)</span></div>
-                        <el-select v-model="value" placeholder="选择品种" size="large">
+                        <div class="item-label">{{ $t('品种') }}<span class="optional">{{ $t('(多选)') }}</span></div>
+                        <el-select v-model="value" :placeholder="$t('选择品种')" size="large">
                             <el-option v-for="item in options" :key="item.value" :label="item.label"
                                 :value="item.value" />
                         </el-select>
                     </div>
                     <div class="form-item">
-                        <div class="item-label">物候时间</div>
+                        <div class="item-label">{{ $t('物候时间') }}</div>
                         <GrowthStageTimeline v-model="growthStageIndex" :stages="growthStages" />
                     </div>
                 </div>

+ 16 - 14
src/views/old_mini/work_detail/components/executePopup.vue

@@ -7,13 +7,13 @@
                 </div>
                 <div class="time-input">
                     <el-date-picker format="YYYY-MM-DD" value-format="YYYY-MM-DD" v-model="executeTime"
-                        :disabled-date="disabledDate" size="large" style="width: 100%" type="date" placeholder="请选择日期"
+                        :disabled-date="disabledDate" size="large" style="width: 100%" type="date" :placeholder="t('请选择日期')"
                         :editable="false" />
                 </div>
             </div>
             <div class="upload-wrap" :class="{ 'upload-cont': fileList.length }">
-                <div class="name">农事凭证</div>
-                <div class="sub-name">请上传农事执行现场照片及所用药肥凭证照片</div>
+                <div class="name">{{ t('农事凭证') }}</div>
+                <div class="sub-name">{{ t('请上传农事执行现场照片及所用药肥凭证照片') }}</div>
                 
                 <uploader class="uploader" v-model="fileList" multiple :max-count="5" :after-read="afterRead" @delete="handleDelete"
                     @click="handleClick('rg')">
@@ -21,11 +21,11 @@
                     <img class="plus" src="@/assets/img/home/plus.png" alt="" />
                 </uploader>
                 <div class="service-input">
-                    <el-input v-model="serviceInput" placeholder="请输入农资机构名称" />
+                    <el-input v-model="serviceInput" :placeholder="t('请输入农资机构名称')" />
                 </div>
             </div>
             <div class="button-wrap">
-                <div @click="closeTask" class="button primary">确认上传</div>
+                <div @click="closeTask" class="button primary">{{ t('确认上传') }}</div>
             </div>
         </div>
     </popup>
@@ -37,10 +37,10 @@
             </div>
             <div class="time-number-input">
                 <el-input-number v-model="remindTime" :min="1" :max="30" :step="1" size="large" style="width: 100%" />
-                <span class="time-unit">天后</span>
+                <span class="time-unit">{{ t('天后') }}</span>
             </div>
             <div class="button-wrap">
-                <div @click="handleRemind" class="button primary">确认</div>
+                <div @click="handleRemind" class="button primary">{{ t('确认') }}</div>
             </div>
         </div>
     </popup>
@@ -48,6 +48,8 @@
 </template>
 
 <script setup>
+import { useI18n } from "@/i18n";
+const { t } = useI18n();
 import { Popup, Uploader } from "vant";
 import { ref } from "vue";
 import { ElMessage } from "element-plus";
@@ -98,7 +100,7 @@ const afterRead = (file) => {
         }).catch(() => {
             item.status = 'failed';
             item.message = '上传失败';
-            ElMessage.error('图片上传失败,请稍后再试!')
+            ElMessage.error({ message: t('图片上传失败,请稍后再试!'), type: 'error' });
         });
     });
 };
@@ -115,9 +117,9 @@ function handleDelete(file) {
 
 function closeTask() {
     // stepIndex.value = 2
-    if (!fileArr.value.length) return ElMessage.warning('请上传农事凭证')
-    if (!executeTime.value) return ElMessage.warning('请选择实际执行时间')
-    if (!serviceInput.value) return ElMessage.warning('请输入农资机构名称')
+    if (!fileArr.value.length) return ElMessage.warning({ message: t('请上传农事凭证'), type: 'warning' });
+    if (!executeTime.value) return ElMessage.warning({ message: t('请选择实际执行时间'), type: 'warning' });
+    if (!serviceInput.value) return ElMessage.warning({ message: t('请输入农资机构名称'), type: 'warning' });
 
     const params = {
         recordId: recordId.value,
@@ -128,7 +130,7 @@ function closeTask() {
     console.log("params", params);
     VE_API.report.addExecuteImgAndComplete(params).then((res) => {
         if (res.code === 0) {
-            ElMessage.success('上传成功')
+            ElMessage.success({ message: t('上传成功'), type: 'success' });
             showValue.value = false;
             setTimeout(() => {
                 emit('executeSuccess');
@@ -137,7 +139,7 @@ function closeTask() {
             ElMessage.error(res.msg)
         }
     }).catch((err) => {
-        ElMessage.error('操作失败')
+        ElMessage.error({ message: t('操作失败'), type: 'error' });
     })
 }
 
@@ -158,7 +160,7 @@ function showRemindPopup() {
 }
 
 function handleRemind() {
-    if (!remindTime.value) return ElMessage.warning('请选择下次提醒时间')
+    if (!remindTime.value) return ElMessage.warning({ message: t('请选择下次提醒时间'), type: 'warning' });
     showRemindValue.value = false;
 }
 

+ 3 - 1
src/views/old_mini/work_detail/components/mapInfo.vue

@@ -1,6 +1,6 @@
 <template>
     <div class="map-info">
-        <!-- <div class="popup-title" v-if="showTitle">执行区域</div> -->
+        <!-- <div class="popup-title" v-if="showTitle">{{ t('执行区域') }}</div> -->
         <div class="map-box">
             <div class="map" ref="mapContainer"></div>
         </div>
@@ -8,6 +8,8 @@
 </template>
 
 <script setup>
+import { useI18n } from "@/i18n";
+const { t } = useI18n();
 import { ref, nextTick, onMounted, watch } from "vue";
 import AreaMap from "./areaMap.js";
 

+ 25 - 25
src/views/old_mini/work_detail/index.vue

@@ -1,6 +1,6 @@
 <template>
     <div class="work-detail">
-        <custom-header name="农事详情" v-if="!miniJson?.hideDraw" :showClose="false" isGoBack @goback="handleBack" />
+        <custom-header :name="$t('农事详情')" v-if="!miniJson?.hideDraw" :showClose="false" isGoBack @goback="handleBack" />
 
         <div class="work-detail-content">
             <!-- 顶部状态 -->
@@ -39,33 +39,33 @@
 
                         <div class="stage-info">
                             <div class="form-item">
-                                <div class="item-name">农事目的</div>
+                                <div class="item-name">{{ $t('农事目的') }}</div>
                                 <div class="item-text">
                                     {{ prescription.purpose || prescription.purposeName || "--" }}
                                 </div>
                             </div>
                             <div class="form-item">
-                                <div class="item-name">农事时间</div>
+                                <div class="item-name">{{ $t('农事时间') }}</div>
                                 <div class="item-text">
                                     {{ detail.executeDate || "--" }}
                                 </div>
                             </div>
                             <div class="form-item">
-                                <div class="item-name">执行区域</div>
+                                <div class="item-name">{{ $t('执行区域') }}</div>
                                 <div class="item-text light-text area-text">
                                     {{ detail?.executionRegion?.regionName }}种植区域
-                                    <div class="area-btn" v-if="detail?.executionRegion?.regionRange" @click="handleViewArea">查看区域</div>
-                                    <div class="area-btn area-btn-right" @click="toDraw" v-if="!detail?.executionRegion?.regionRange && !miniJson?.hideDraw">建议勾选<el-icon><ArrowRight /></el-icon></div>
+                                    <div class="area-btn" v-if="detail?.executionRegion?.regionRange" @click="handleViewArea">{{ $t('查看区域') }}</div>
+                                    <div class="area-btn area-btn-right" @click="toDraw" v-if="!detail?.executionRegion?.regionRange && !miniJson?.hideDraw">{{ $t('建议勾选') }}<el-icon><ArrowRight /></el-icon></div>
                                 </div>
                             </div>
                             <div class="form-item">
-                                <div class="item-name">注意事项</div>
+                                <div class="item-name">{{ $t('注意事项') }}</div>
                                 <div class="item-text">
                                     {{ detail.remark || "--" }}
                                 </div>
                             </div>
                             <div class="form-item" v-if="hasAnyAvailableExecutionMethod(prescription)">
-                                <div class="item-name">药肥处方</div>
+                                <div class="item-name">{{ $t('药肥处方') }}</div>
                             </div>
                         </div>
 
@@ -81,9 +81,9 @@
                             v-if="prescription.pesticideList && prescription.pesticideList.length && hasAnyAvailableExecutionMethod(prescription)">
                             <div class="prescription-table">
                                 <div class="table-header">
-                                    <div class="col col-type">使用功效</div>
-                                    <div class="col col-name">药肥名称</div>
-                                    <div class="col col-ratio">药肥配比</div>
+                                    <div class="col col-type">{{ $t('使用功效') }}</div>
+                                    <div class="col col-name">{{ $t('药肥名称') }}</div>
+                                    <div class="col col-ratio">{{ $t('药肥配比') }}</div>
                                 </div>
 
                                 <div v-for="(item, i) in prescription.pesticideList" :key="i" class="table-row">
@@ -112,7 +112,7 @@
 
 
 <div class="work-info photo-box" v-if="prescription.cropAlbum && prescription.cropAlbum.length">
-    <div class="photo-title">农事凭证</div>
+    <div class="photo-title">{{ $t('农事凭证') }}</div>
     <div class="tips-text" v-if="detail?.imageAuditRejectReason">{{ detail?.imageAuditRejectReason }}</div>
     <div class="photo-sub-title" v-if="info?.appType === 1">来自于 {{ detail?.executorOrganizationName || "--" }}</div>
     <div class="photo-img-wrap">
@@ -125,7 +125,7 @@
                         <el-icon size="24" color="#FF953D">
                             <WarningFilled />
                         </el-icon>
-                        <div class="fail-icon-text">审核失败</div>
+                        <div class="fail-icon-text">{{ $t('审核失败') }}</div>
                     </div>
                 </div>
             </photo-consumer>
@@ -138,7 +138,7 @@
                 <div class="box-wrap stage-card">
                     <div class="work-info">
                         <div class="map-box">
-                            <div class="map-title">执行区域</div>
+                            <div class="map-title">{{ $t('执行区域') }}</div>
                             <div class="map-container" ref="mapContainer"></div>
                         </div>
                         <div class="area-list">
@@ -146,10 +146,10 @@
                                 <div class="area-l">
                                     权属田块1:{{ farmData.work_time }}
                                     <span class="area-tag"
-                                        style="background: rgba(55, 193, 27, 0.1); color: #37C11B;">已认证</span>
-                                    <!-- <span class="area-tag">未激活</span> -->
+                                        style="background: rgba(55, 193, 27, 0.1); color: #37C11B;">{{ $t('已认证') }}</span>
+                                    <!-- <span class="area-tag">{{ $t('未激活') }}</span> -->
                                 </div>
-                                <!-- <div class="area-r">我已完成</div> -->
+                                <!-- <div class="area-r">{{ $t('我已完成') }}</div> -->
                             </div>
                         </div>
                         <!-- <div class="ecological-plant-card">
@@ -172,19 +172,19 @@
                 <div class="box-wrap stage-card">
                     <div class="work-info">
                         <div class="info-item">
-                            <div class="info-title"><span class="title-block"></span>农事目的</div>
+                            <div class="info-title"><span class="title-block"></span>{{ $t('农事目的') }}</div>
                             <div class="info-value">{{ farmData.work_purpose }}</div>
                         </div>
                         <div class="info-item">
-                            <div class="info-title"><span class="title-block"></span>注意事项</div>
+                            <div class="info-title"><span class="title-block"></span>{{ $t('注意事项') }}</div>
                             <div class="info-value">{{ farmData.precautions }}</div>
                         </div>
                         <div class="info-item">
-                            <div class="info-title"><span class="title-block"></span>药物处方</div>
+                            <div class="info-title"><span class="title-block"></span>{{ $t('药物处方') }}</div>
                             <div class="info-value">{{ farmData.drug_prescription }}</div>
                         </div>
                         <div class="info-item">
-                            <div class="info-title"><span class="title-block"></span>执行方式</div>
+                            <div class="info-title"><span class="title-block"></span>{{ $t('执行方式') }}</div>
                             <div class="info-value">{{ farmData.execution_method }}</div>
                         </div>
                     </div>
@@ -196,9 +196,9 @@
                             <el-icon size="16" color="#FF953D"><WarningFilled /></el-icon>
                             存在继发危害需巡园
                         </div>
-                        <div class="warning-text">文案文案文案文案文案</div>
+                        <div class="warning-text">{{ $t('文案文案文案文案文案') }}</div>
                     </div>
-                    <div class="warning-r">异常记录</div>
+                    <div class="warning-r">{{ $t('异常记录') }}</div>
                 </div> -->
 
                 <!-- 底部按钮 -->
@@ -209,9 +209,9 @@
                 </div>
 
                 <div class="fixed-btn-wrap execute-action">
-                    <div class="action-item second" v-if="!miniJson?.hideDraw" @click="handleConvert">转发给执行人员</div>
+                    <div class="action-item second" v-if="!miniJson?.hideDraw" @click="handleConvert">{{ $t('转发给执行人员') }}</div>
                 </div> -->
-                <!-- <div class="action-item primary" @click="handleExecute">溯源认证</div> -->
+                <!-- <div class="action-item primary" @click="handleExecute">{{ $t('溯源认证') }}</div> -->
             </div>
         </div>
         <ExecutePopup ref="executePopupRef" @executeSuccess="getDetail" />