farmInfoGroup.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. <template>
  2. <div class="farm-info-group">
  3. <!-- 顶部标题栏 -->
  4. <div class="info-header">
  5. <div class="back-btn" v-if="!farmList.length" @click="handleBack">返回</div>
  6. <div class="title">
  7. <img class="title-icon" src="@/assets/images/common/chart-icon.png" alt="" />
  8. <span class="title-text">{{ farmList.length ? "农场列表" : "荔博园" }}</span>
  9. </div>
  10. </div>
  11. <!-- 主要内容区域 -->
  12. <div class="info-content" :style="{ height: !farmList.length ? 'calc(100% - 168px)' : 'calc(100% - 50px)' }">
  13. <div class="farm-info" v-for="item in farmList" :key="item" @click="handleFarmInfo(item)">
  14. <div class="lz-icon">
  15. <img src="@/assets/images/warningHome/lz-icon.png" alt="" />
  16. </div>
  17. <!-- 右侧信息区域 -->
  18. <div class="info-right">
  19. <div class="info-item farm-code">
  20. <span class="info-label">{{ !farmList.length ? '农场编号:GZ86770' : item.farmName }}</span>
  21. <div class="tags">
  22. <span class="tag">荔枝</span>
  23. <span class="tag">井岗红糯</span>
  24. </div>
  25. </div>
  26. <div class="info-item">
  27. <span class="info-label">农场面积:</span>
  28. <span class="info-value">{{ item.plantArea && item.plantArea.toFixed(2) || 0 }}亩</span>
  29. </div>
  30. <div class="info-item">
  31. <span class="info-label">农场位置:</span>
  32. <span class="info-value">广东省广州市***</span>
  33. </div>
  34. <div class="info-item">
  35. <span class="info-label">权属人:</span>
  36. <span class="info-value">张扬</span>
  37. </div>
  38. </div>
  39. </div>
  40. <template v-if="!farmList.length">
  41. <div class="tabs">
  42. <div
  43. v-for="tab in tabs"
  44. :key="tab.key"
  45. class="tab-item"
  46. :class="{ active: activeTab === tab.key }"
  47. @click="activeTab = tab.key"
  48. >
  49. {{ tab.label }}
  50. </div>
  51. </div>
  52. <div class="tab-content">
  53. <div class="timeline" v-if="activeTab === 'crop'">
  54. <div class="timeline-item" v-for="item in timelineList" :key="item.id">
  55. <div class="timeline-left">
  56. <div class="dot"></div>
  57. <div class="line"></div>
  58. </div>
  59. <div class="timeline-right">
  60. <div class="date">{{ item.date }}</div>
  61. <div class="text">{{ item.text }}</div>
  62. </div>
  63. </div>
  64. </div>
  65. <div v-else class="perception-content">
  66. <div class="perception-tabs">
  67. <div
  68. v-for="tab in perceptionTabs"
  69. :key="tab.key"
  70. class="perception-tab"
  71. :class="{ 'perception-tab--active': perceptionActive === tab.key }"
  72. @click="perceptionActive = tab.key"
  73. >
  74. <span class="label">{{ tab.label }}</span>
  75. <div class="underline"></div>
  76. </div>
  77. </div>
  78. <div class="perception-wrap">
  79. <equipment v-if="perceptionActive === 'device'"></equipment>
  80. <crop v-else></crop>
  81. </div>
  82. </div>
  83. </div>
  84. </template>
  85. </div>
  86. </div>
  87. </template>
  88. <script setup>
  89. import { ref ,onMounted} from "vue";
  90. import equipment from "./equipment.vue";
  91. import crop from "./crop.vue";
  92. onMounted(() => {
  93. fetchFarmList();
  94. });
  95. const fetchFarmList = () => {
  96. VE_API.warning.fetchFarmList().then(res => {
  97. if (res.code === 0 && res.data && res.data.length > 0) {
  98. farmList.value = res.data;
  99. } else {
  100. farmList.value = [];
  101. }
  102. });
  103. };
  104. // 底部 tabs 配置
  105. const tabs = [
  106. { key: "crop", label: "作物档案" },
  107. { key: "perception", label: "感知记录" },
  108. ];
  109. // 感知类型 tabs
  110. const perceptionTabs = [
  111. { key: "device", label: "设备感知" },
  112. { key: "crop", label: "作物感知" },
  113. ];
  114. const activeTab = ref("crop"); // 默认选中感知记录
  115. const perceptionActive = ref("device"); // 默认选中设备感知
  116. // 时间轴示例数据(后续可替换为接口数据)
  117. const timelineList = ref([
  118. { id: 1, date: "8/1", text: "水稻进入拔节期,水稻进入拔节期" },
  119. { id: 2, date: "8/1", text: "水稻进入拔节期,水稻进入拔节期" },
  120. { id: 3, date: "8/1", text: "水稻进入拔节期,水稻进入拔节期" },
  121. { id: 4, date: "8/1", text: "水稻进入拔节期,水稻进入拔节期" },
  122. { id: 5, date: "8/1", text: "水稻进入拔节期,水稻进入拔节期" },
  123. ]);
  124. const farmList = ref([]);
  125. const handleBack = () => {
  126. farmList.value = [
  127. { id: 1, name: "荔博园", area: "58亩", position: "广东省广州市***", owner: "张扬" },
  128. { id: 2, name: "荔博园", area: "58亩", position: "广东省广州市***", owner: "张扬" },
  129. { id: 3, name: "荔博园", area: "58亩", position: "广东省广州市***", owner: "张扬" },
  130. { id: 4, name: "荔博园", area: "58亩", position: "广东省广州市***", owner: "张扬" },
  131. { id: 5, name: "荔博园", area: "58亩", position: "广东省广州市***", owner: "张扬" },
  132. ];
  133. };
  134. const handleCropArchive = () => {
  135. // 作物档案逻辑
  136. };
  137. const handlePerceptionRecord = () => {
  138. // 感知记录逻辑
  139. };
  140. const handleFarmInfo = (item) => {
  141. farmList.value = [item];
  142. };
  143. </script>
  144. <style lang="scss" scoped>
  145. .farm-info-group {
  146. position: absolute;
  147. top: 0px;
  148. right: 0;
  149. width: 376px;
  150. height: 100%;
  151. background: #232323;
  152. border-radius: 4px;
  153. box-sizing: border-box;
  154. border: 1px solid rgba(255, 255, 255, 0.16);
  155. .info-header {
  156. display: flex;
  157. align-items: center;
  158. padding: 6px 14px;
  159. border-bottom: 1px solid rgba(255, 255, 255, 0.16);
  160. .back-btn {
  161. cursor: pointer;
  162. color: rgba(255, 255, 255, 0.55);
  163. }
  164. .title {
  165. flex: 1;
  166. display: flex;
  167. align-items: center;
  168. justify-content: center;
  169. gap: 8px;
  170. font-size: 18px;
  171. .title-icon {
  172. width: 19px;
  173. height: 13px;
  174. }
  175. }
  176. }
  177. .info-content {
  178. padding: 5px 10px;
  179. height: calc(100% - 168px);
  180. overflow-y: auto;
  181. .farm-info + .farm-info {
  182. margin-top: 12px;
  183. }
  184. .farm-info {
  185. display: flex;
  186. align-items: flex-start;
  187. gap: 12px;
  188. .lz-icon {
  189. border-radius: 6px;
  190. background: rgba(70, 70, 70, 0.26);
  191. img {
  192. width: 65px;
  193. height: 65px;
  194. padding: 7px;
  195. }
  196. }
  197. .info-right {
  198. font-size: 12px;
  199. color: #9f9f9f;
  200. .info-item {
  201. display: flex;
  202. align-items: center;
  203. line-height: 18px;
  204. gap: 6px;
  205. cursor: pointer;
  206. &.farm-code {
  207. font-weight: 700;
  208. font-size: 16px;
  209. margin-bottom: 4px;
  210. color: #ffffff;
  211. }
  212. .tags {
  213. display: flex;
  214. gap: 4px;
  215. margin-left: auto;
  216. .tag {
  217. padding: 1px 6px;
  218. border: 1px solid #ffd489;
  219. border-radius: 2px;
  220. color: #ffd489;
  221. font-size: 12px;
  222. font-weight: 400;
  223. }
  224. }
  225. }
  226. }
  227. }
  228. .tabs {
  229. display: flex;
  230. gap: 10px;
  231. margin-top: 12px;
  232. .tab-item {
  233. padding: 6px 32px;
  234. text-align: center;
  235. border-radius: 4px;
  236. cursor: pointer;
  237. color: #ffffff;
  238. background: rgba(166, 166, 166, 0.08);
  239. border: 1px solid transparent;
  240. &.active {
  241. color: #ffd489;
  242. background: rgba(255, 212, 137, 0.2);
  243. border: 1px solid #ffd489;
  244. }
  245. }
  246. }
  247. .tab-content {
  248. margin: 12px 0;
  249. height: calc(100% - 24px);
  250. overflow-y: auto;
  251. .timeline {
  252. padding-right: 12px;
  253. .timeline-item {
  254. display: flex;
  255. align-items: flex-start;
  256. font-size: 14px;
  257. color: #ffffff;
  258. line-height: 22px;
  259. & + .timeline-item {
  260. margin-top: 24px;
  261. }
  262. .timeline-left {
  263. width: 24px;
  264. display: flex;
  265. flex-direction: column;
  266. align-items: center;
  267. .dot {
  268. width: 6px;
  269. height: 6px;
  270. border-radius: 50%;
  271. background: #ffd489;
  272. margin-top: 6px;
  273. }
  274. .line {
  275. border-left: 1px dashed rgba(255, 212, 137, 0.6);
  276. margin-top: 4px;
  277. height: 40px;
  278. }
  279. }
  280. .timeline-right {
  281. padding-left: 8px;
  282. .date {
  283. color: #ffd489;
  284. font-weight: 500;
  285. margin-bottom: 2px;
  286. }
  287. .text {
  288. color: #d7d7d7;
  289. }
  290. }
  291. }
  292. }
  293. .perception-content {
  294. height: 100%;
  295. .perception-tabs {
  296. display: flex;
  297. align-items: flex-end;
  298. gap: 20px;
  299. margin-bottom: 12px;
  300. .perception-tab {
  301. color: #ffffff;
  302. cursor: pointer;
  303. .underline {
  304. margin-top: 6px;
  305. width: 56px;
  306. height: 2px;
  307. background: transparent;
  308. border-radius: 2px;
  309. }
  310. &.perception-tab--active {
  311. color: #ffd489;
  312. .underline {
  313. background: #ffd489;
  314. }
  315. }
  316. }
  317. }
  318. .perception-wrap {
  319. height: calc(100% - 40px);
  320. overflow-y: auto;
  321. }
  322. }
  323. }
  324. }
  325. }
  326. </style>