homeFloatingPanel.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. <template>
  2. <div class="home-floating-panel">
  3. <floating-panel
  4. class="floating-panel"
  5. :class="{ 'background-panel': isBackground }"
  6. v-model:height="height"
  7. :anchors="anchors"
  8. :content-draggable="false"
  9. @height-change="handleHeightChange"
  10. >
  11. <template #header>
  12. <div class="floating-panel-header" :class="{ 'file-header-active': activeTab === 0 }">
  13. <div class="tabs">
  14. <div
  15. :class="['tab-item', activeTab === index ? 'active' : '']"
  16. v-for="(tab, index) in tabs"
  17. :key="tab"
  18. @click="handleTabClick(index)"
  19. >
  20. {{ tab }}
  21. </div>
  22. </div>
  23. <div class="file-header" v-if="activeTab === 1">
  24. <div class="select-group">
  25. <el-select class="select-item" v-model="dateValue" placeholder="Select">
  26. <el-option
  27. v-for="item in dateOptions"
  28. :key="item.value"
  29. :label="item.label"
  30. :value="item.value"
  31. />
  32. </el-select>
  33. <el-select class="select-item" v-model="areaValue" placeholder="Select">
  34. <el-option
  35. v-for="item in areaOptions"
  36. :key="item.value"
  37. :label="item.label"
  38. :value="item.value"
  39. />
  40. </el-select>
  41. </div>
  42. <el-anchor
  43. :container="containerRef"
  44. class="tabs-content-group"
  45. direction="horizontal"
  46. type="default"
  47. :offset="0"
  48. @click="handleClick"
  49. >
  50. <el-anchor-link
  51. class="tabs-content-item"
  52. v-for="(tab, index) in tabsContent"
  53. :key="tab"
  54. @click="handleTabsContentClick(index)"
  55. :href="'#part' + (index + 1)"
  56. :title="tab"
  57. ></el-anchor-link>
  58. </el-anchor>
  59. </div>
  60. </div>
  61. </template>
  62. <div class="floating-panel-content" v-show="activeTab === 0">
  63. <record-task></record-task>
  64. </div>
  65. <div class="floating-panel-content" ref="cardContentRef" v-show="activeTab === 1">
  66. <div class="card-content-group" ref="containerRef" :style="{ height: `${cardContentHeight}px` }">
  67. <div class="card-content-item" id="part1">
  68. <div class="card-content-item-title">果园总览</div>
  69. <div class="card-content-item-content">
  70. <div class="card-text">果园面积共XX亩,共有XX棵生产树。</div>
  71. <div class="card-text">
  72. 本次飞巡拍摄了XX张照片,包括了XX棵树,目前为施用根部有机肥阶段,根据树体冠幅大小,果园预计施用有机肥XXkg。
  73. </div>
  74. <div class="card-text">
  75. 目前XX%的树体暂未萌动新梢,需要进行剪枝农事,提高树体光合效率与通风效率。
  76. </div>
  77. </div>
  78. </div>
  79. <div class="card-content-item" id="part2">
  80. <div class="card-content-item-title">整体园相</div>
  81. <div class="card-content-item-content">
  82. <div class="card-name">透光率</div>
  83. <div class="card-value">
  84. 透光率体现树体自身郁闭程度,当前XX%的树体透光性较差,可能造成整体减产XX%,需立即执行剪枝农事;XX%的树体透光正常,建议继续保持现有管理措施并及时巡园。
  85. </div>
  86. </div>
  87. <div class="card-content-item-content">
  88. <div class="card-name">通风率</div>
  89. <div class="card-value">
  90. 透光率体现树体自身郁闭程度,当前XX%的树体透光性较差,可能造成整体减产XX%,需立即执行剪枝农事;XX%的树体透光正常,建议继续保持现有管理措施并及时巡园。
  91. </div>
  92. <div class="map-wrap">
  93. <img class="map-img" src="@/assets/img/home/map.png" alt="" />
  94. <div class="map-text">剪枝农事地图</div>
  95. </div>
  96. </div>
  97. </div>
  98. <div class="card-content-item" id="part3">
  99. <div class="card-content-item-title">营养管理</div>
  100. <div class="card-content-item-content">
  101. <div class="card-name">根肥</div>
  102. <div class="card-value">
  103. 生产树中,树体冠幅平均表面积XX平方米,预计需要使用有机肥XXkg(或者平衡肥+尿素XXkg)用来培养健壮秋梢。如遭遇持续阴雨,可配合芸苔素增强光合作用。
  104. </div>
  105. <div class="map-wrap">
  106. <img class="map-img" src="@/assets/img/home/map.png" alt="" />
  107. <div class="map-text">根肥农事地图</div>
  108. </div>
  109. </div>
  110. </div>
  111. <div class="card-content-item" id="part4">
  112. <div class="card-content-item-title">病虫管理</div>
  113. <div class="card-content-item-content">
  114. <div class="card-value">
  115. 在以萌动新梢的树体中,2%的树体新梢小叶变为红黄色,长度10-15cm,极易被病虫啃食,预计XX月XX日果园进入虫害爆发高峰期,需要做好虫害预防工作。
  116. </div>
  117. <div class="map-wrap">
  118. <img class="map-img" src="@/assets/img/home/map.png" alt="" />
  119. <div class="map-text">防虫农事地图</div>
  120. </div>
  121. </div>
  122. </div>
  123. <div class="card-content-item" id="part5">
  124. <div class="card-content-item-title">农事记录</div>
  125. <div class="card-content-item-content">农事记录</div>
  126. </div>
  127. </div>
  128. </div>
  129. </floating-panel>
  130. <div class="expand-btn-wrap" v-show="height === defalutHeight">
  131. <span>农事任务/农事档案</span>
  132. <div class="expand-btn" @click="handleExpandBtnClick">
  133. <span>展开</span>
  134. <el-icon><ArrowUpBold /></el-icon>
  135. </div>
  136. </div>
  137. </div>
  138. </template>
  139. <script setup>
  140. import { FloatingPanel } from "vant";
  141. import { useStore } from "vuex";
  142. import { computed, ref } from "vue";
  143. import RecordTask from "./recordTask.vue";
  144. const store = useStore();
  145. // const tabBarHeight = computed(() => store.state.home.tabBarHeight);
  146. const tabBarHeight = ref(localStorage.getItem("tabBarHeight") * 1 || 50);
  147. const defalutHeight = ref(0);
  148. const anchors = ref([defalutHeight.value, 310 + tabBarHeight.value, Math.round(1 * window.innerHeight)]);
  149. const height = ref(anchors.value[1]);
  150. const containerRef = ref(null);
  151. const handleClick = (e) => {
  152. e.preventDefault();
  153. };
  154. const activeTab = ref(0);
  155. const tabs = ref(["农事任务", "农场档案"]);
  156. const handleTabClick = (index) => {
  157. activeTab.value = index;
  158. };
  159. const dateValue = ref("2025-08-28");
  160. const dateOptions = [
  161. {
  162. value: "2025-08-28",
  163. label: "2025-08-28",
  164. },
  165. ];
  166. const areaValue = ref("1");
  167. const areaOptions = [
  168. {
  169. value: "1",
  170. label: "全部区域",
  171. },
  172. ];
  173. const tabsContent = ref(["果园总览", "整体园相", "营养管理", "病虫管理", "农事记录"]);
  174. const activeTabsContent = ref(0);
  175. const handleTabsContentClick = (index) => {
  176. activeTabsContent.value = index;
  177. };
  178. const emit = defineEmits(["heightChange"]);
  179. const cardContentRef = ref(null);
  180. const isBackground = ref(false);
  181. const handleHeightChange = ({ height }) => {
  182. isBackground.value = false;
  183. if (height > anchors.value[1]) {
  184. isBackground.value = true;
  185. }
  186. if (height === anchors.value[1]) {
  187. cardContentHeight.value = 180;
  188. cardContentRef.value.scrollTo({ top: 0, behavior: "smooth" });
  189. } else if (height === anchors.value[2]) {
  190. cardContentHeight.value = Math.round(1 * window.innerHeight) - (tabBarHeight.value - 40) - 180;
  191. }
  192. emit("heightChange", height);
  193. };
  194. const cardContentHeight = ref(180);
  195. const handleExpandBtnClick = () => {
  196. cardContentHeight.value = 180;
  197. height.value = anchors.value[1];
  198. cardContentRef.value.scrollTo({ top: 0, behavior: "smooth" });
  199. emit("heightChange", anchors.value[1]);
  200. };
  201. </script>
  202. <style lang="scss" scoped>
  203. .van-floating-panel {
  204. border-radius: 0;
  205. }
  206. .floating-panel {
  207. width: 100%;
  208. background: linear-gradient(180deg, transparent 0%, #f5f7fb 14%);
  209. ::v-deep {
  210. .van-floating-panel__content {
  211. background: transparent;
  212. overflow: hidden;
  213. }
  214. }
  215. .floating-panel-header {
  216. width: calc(100% - 24px);
  217. border-radius: 14px;
  218. margin: 0 auto 10px;
  219. background: #fff;
  220. &.file-header-active{
  221. border-radius: 14px 14px 0 0;
  222. margin-bottom: 0;
  223. }
  224. .tabs {
  225. display: flex;
  226. .tab-item {
  227. flex: 1;
  228. text-align: center;
  229. font-size: 16px;
  230. color: rgba(0, 0, 0, 0.5);
  231. font-weight: 500;
  232. padding: 10px 0;
  233. &.active {
  234. color: #2199f8;
  235. border-radius: 14px 14px 0 0;
  236. background: linear-gradient(180deg, #cee5fb 0%, #fff 80%);
  237. }
  238. }
  239. }
  240. .file-header {
  241. .select-group {
  242. display: flex;
  243. justify-content: center;
  244. padding-top: 5px;
  245. .select-item {
  246. width: 115px;
  247. ::v-deep {
  248. .el-select__wrapper {
  249. text-align: center;
  250. gap: 2px;
  251. box-shadow: none;
  252. justify-content: center;
  253. }
  254. .el-select__selection {
  255. flex: none;
  256. width: fit-content;
  257. }
  258. .el-select__placeholder {
  259. position: static;
  260. transform: none;
  261. width: fit-content;
  262. color: #000;
  263. }
  264. .el-select__caret {
  265. color: #000;
  266. }
  267. }
  268. }
  269. .select-item:first-child {
  270. margin-right: 10px;
  271. }
  272. }
  273. ::v-deep{
  274. .el-anchor{
  275. --el-anchor-line-height: auto;
  276. }
  277. .el-anchor__marker{
  278. height: 0;
  279. }
  280. .el-anchor.el-anchor--horizontal .el-anchor__list{
  281. width: 100%;
  282. }
  283. .el-anchor__link.is-active{
  284. background: rgba(33, 153, 248, 0.2);
  285. }
  286. }
  287. .tabs-content-group {
  288. display: flex;
  289. justify-content: space-evenly;
  290. padding: 5px 8px;
  291. background-color: transparent;
  292. .tabs-content-item {
  293. flex: 1;
  294. font-size: 14px;
  295. color: rgba(0, 0, 0, 0.5);
  296. font-weight: 500;
  297. padding: 1px 0;
  298. border-radius: 14px;
  299. text-align: center;
  300. }
  301. }
  302. }
  303. }
  304. .floating-panel-content {
  305. width: calc(100% - 24px);
  306. height: 100%;
  307. margin: 0 auto;
  308. .card-content-group {
  309. padding-bottom: 12px;
  310. height: 100%;
  311. box-sizing: border-box;
  312. overflow: auto;
  313. .card-content-item {
  314. border-radius: 14px;
  315. padding: 12px;
  316. background: #fff;
  317. color: #171717;
  318. .card-content-item-title {
  319. font-size: 16px;
  320. font-weight: 500;
  321. text-align: center;
  322. position: relative;
  323. &::before {
  324. content: "";
  325. position: absolute;
  326. left: 50%;
  327. transform: translateX(-50%);
  328. bottom: -1px;
  329. width: 66px;
  330. height: 8px;
  331. background: rgba(33, 153, 248, 0.3);
  332. border-radius: 4px;
  333. }
  334. }
  335. .card-content-item-content {
  336. margin-top: 12px;
  337. border-radius: 8px;
  338. background: rgba(238, 238, 238, 0.3);
  339. padding: 10px;
  340. line-height: 21px;
  341. .card-name {
  342. font-weight: 500;
  343. }
  344. .card-value {
  345. font-size: 12px;
  346. color: rgba(23, 23, 23, 0.6);
  347. }
  348. .map-wrap {
  349. margin-top: 10px;
  350. .map-img {
  351. width: 100%;
  352. height: 137px;
  353. }
  354. .map-text {
  355. font-size: 12px;
  356. color: rgba(23, 23, 23, 0.6);
  357. text-align: center;
  358. margin-top: 5px;
  359. }
  360. }
  361. .card-text + .card-text {
  362. margin-top: 5px;
  363. }
  364. }
  365. }
  366. .card-content-item + .card-content-item {
  367. margin-top: 10px;
  368. }
  369. }
  370. }
  371. &.background-panel {
  372. background: #f5f7fb;
  373. .floating-panel-header {
  374. margin-top: 12px;
  375. }
  376. }
  377. }
  378. .expand-btn-wrap{
  379. position: absolute;
  380. bottom: 62px;
  381. left: 12px;
  382. width: calc(100% - 24px);
  383. background-image: linear-gradient(180deg, #d7eafc 0%, #ffffff 100%);
  384. border-radius: 14px;
  385. padding: 15px 12px;
  386. box-sizing: border-box;
  387. display: flex;
  388. align-items: center;
  389. justify-content: space-between;
  390. font-weight: 500;
  391. font-size: 15px;
  392. .expand-btn{
  393. display: flex;
  394. align-items: center;
  395. justify-content: center;
  396. gap: 4px;
  397. font-size: 13px;
  398. color: #2199f8;
  399. }
  400. }
  401. </style>