index.vue 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740
  1. <template>
  2. <div class="base-container no-events">
  3. <div class="content">
  4. <!-- <navigation @handleTab="handleTab"></navigation> -->
  5. <div class="left yes-events">
  6. <component :is="components[currentComponent]" @backHome="backHome" />
  7. </div>
  8. <div class="right yes-events">
  9. <div class="list adopt-list-wrap">
  10. <chart-box name="果树管理" arrow="" color="yellow">
  11. <el-tabs v-model="activeName" class="demo-tabs">
  12. <el-tab-pane label="果树列表" name="果树列表">
  13. <adopt-list></adopt-list>
  14. </el-tab-pane>
  15. <el-tab-pane label="待分配" name="待分配">
  16. <template #label>
  17. <span class="custom-tabs-label">
  18. <span>待分配</span>
  19. <span class="count-text" v-if="listCount">{{ listCount }}</span>
  20. </span>
  21. </template>
  22. <client-list
  23. :checkDistributeShow="checkDistributeShow"
  24. :startReloadList="startReloadList"
  25. @update:checkDistributeShow="hanldeDistributeCheck"
  26. @update:checkData="handlCheckChange"
  27. @update:listCount="updateListCount"
  28. ></client-list>
  29. </el-tab-pane>
  30. <el-tab-pane label="已分配" name="已分配">
  31. <apply-list
  32. :startReloadList="startReloadList"
  33. @update:userList="setReload"
  34. ></apply-list>
  35. </el-tab-pane>
  36. <!-- <el-tab-pane label="确认地址" name="确认地址">
  37. <address-list></address-list>
  38. </el-tab-pane> -->
  39. </el-tabs>
  40. </chart-box>
  41. </div>
  42. </div>
  43. <!-- 图例 -->
  44. <div v-if="legendArr && legendArr.length" class="map-bg map-legend yes-events">
  45. <div class="item" v-for="(legend, legendI) in legendArr" :key="legendI">
  46. <span class="legend-block" :style="{ background: legend.color }"></span>
  47. {{ legend.name }}
  48. </div>
  49. </div>
  50. <div v-else class="map-bg map-legend yes-events">
  51. <div class="item" v-show="!checkDistributeShow">
  52. <img src="@/assets/images/map/status/wry.png" alt="" />
  53. 未开放
  54. </div>
  55. <div class="item" v-show="!checkShow">
  56. <img src="@/assets/images/map/status/dry.png" alt="" />
  57. 待守护
  58. </div>
  59. <!-- <div class="item" v-show="!checkShow">
  60. <img src="@/assets/images/map/status/yry.png" alt="" />
  61. 已守护
  62. </div> -->
  63. <div class="item selected-item" v-show="checkShow || checkDistributeShow">
  64. <img src="@/assets/images/map/status/selected.png" alt="" />
  65. 已选择
  66. </div>
  67. </div>
  68. <div class="tips" v-show="checkShow">
  69. <div class="text">
  70. <span>提示:</span>请在底图上点选 <span>未开放的果树</span>,或按住 Ctrl 框选
  71. <span>开放守护的区域</span>
  72. </div>
  73. </div>
  74. <div class="tips" v-show="checkDistributeShow">
  75. <div class="text">
  76. <span>提示:</span>请在底图上点选 <span>待守护的果树</span>,或按住 Ctrl 框选
  77. <span>分配的区域</span>
  78. </div>
  79. </div>
  80. <div v-show="!checkShow && !checkDistributeShow" class="right-button yes-events" @click="hanldeCheck">
  81. 新增守护区域
  82. </div>
  83. <div v-show="checkShow" class="center-button">
  84. <div class="cancel yes-events" @click="handleCancel">取消</div>
  85. <div class="yes-events" @click="saveEdit">确认开放守护权限</div>
  86. </div>
  87. <div v-show="checkDistributeShow" class="center-button">
  88. <div class="cancel yes-events" @click="handleCancel">取消分配</div>
  89. <div class="yes-events" @click="saveDistributeEdit" :class="{'disabled': (!selectedTree.length || !selectedTels.length) || selectedTree.length < selectedTels.length}">
  90. 确认分配<span class="select-text">(已选{{selectedTree.length}}/<span class="select-total">{{ selectedTels.length }}</span>)</span>
  91. </div>
  92. </div>
  93. </div>
  94. </div>
  95. <custom-map class="bottom-map" ref="mapRef" @update:selectedTree="handleSelectedTree"></custom-map>
  96. <!-- 图片弹窗 -->
  97. <PicturePreview :imageUrl="urls" :curIndex="urlsIndex"></PicturePreview>
  98. <PdfDialog title="果园报告"></PdfDialog>
  99. </template>
  100. <script setup>
  101. import { nextTick, onMounted, onUnmounted, ref } from "vue";
  102. import config from "@/api/config.js";
  103. import timeLine from "@/components/timeLine.vue";
  104. import PicturePreview from "@/components/PicturePreview.vue";
  105. import navigation from "@/components/navigation.vue";
  106. import chartBox from "@/components/chartBox.vue";
  107. import leftTabs from "./components/leftTabs/index.vue";
  108. import treeDetail from "./components/treeDetail.vue";
  109. import adoptList from "./components/adoptList.vue";
  110. import clientList from "./components/clientList.vue";
  111. import customMap from "./homeMap.vue";
  112. import applyList from "./components/applyList.vue";
  113. import addressList from "./components/addressList.vue";
  114. import { convertPointToArray } from "@/utils/index";
  115. import { useRoute } from "vue-router";
  116. import { useStore } from "vuex";
  117. import eventBus from "@/api/eventBus";
  118. import PdfDialog from "../../components/PdfDialog";
  119. import { ElMessage } from "element-plus";
  120. let store = useStore();
  121. const components = {
  122. leftTabs,
  123. treeDetail,
  124. };
  125. const route = useRoute();
  126. const mapRef = ref(null);
  127. const activeName = ref("果树列表");
  128. const currentFarmId = store.getters.userinfo.curFarmId
  129. // const currentFarmId = sessionStorage.getItem('userinfo').curFarmId
  130. onMounted(() => {
  131. //区域切换监听事件
  132. eventBus.on("area:id", areaId);
  133. areaId({ areaId: 0, farmId: currentFarmId });
  134. // 修改单棵树信息后刷新地图数据
  135. eventBus.off("refreshMapData", getPointList);
  136. eventBus.on("refreshMapData", getPointList);
  137. if (route.query.acitveName) {
  138. activeName.value = route.query.acitveName
  139. }
  140. });
  141. onUnmounted(() => {
  142. if (mapRef.value) {
  143. mapRef.value.destroy(); // 确保在自定义地图组件中实现了destroy方法
  144. }
  145. eventBus.off("area:id", areaId);
  146. eventBus.off("changePointLegend", toggleLegend);
  147. eventBus.off("clickMapPoint", toggleLeftComponet);
  148. });
  149. const getPointList = () => {
  150. VE_API.manage_interface.fetchSampleList({ farmId: organId.value }).then(({ data }) => {
  151. if (mapRef.value) {
  152. nextTick(() => {
  153. mapRef.value.addCluster(data);
  154. // mapRef.value.initData(arr)
  155. // mapRef.value.initMap(arr, () => mapRef.value.getRegionList(organId.value))
  156. });
  157. }
  158. });
  159. };
  160. const regionData = ref([]);
  161. function getBlueRegionList() {
  162. VE_API.manage_interface.fetchRegionList({ farmId: organId.value }).then(({ data }) => {
  163. regionData.value = data;
  164. mapRef.value.initAreaMap(data);
  165. });
  166. }
  167. const checkShow = ref(false);
  168. const hanldeCheck = () => {
  169. checkShow.value = true;
  170. mapRef.value.enableBoxSelect();
  171. };
  172. // 分配果树
  173. const checkDistributeShow = ref(false);
  174. const startReloadList = ref(false);
  175. const hanldeDistributeCheck = () => {
  176. checkDistributeShow.value = true;
  177. mapRef.value.enableDistributeBoxSelect();
  178. };
  179. // 分配果树勾选值变化事件
  180. const selectedTels = ref([]);
  181. const handlCheckChange = (val) => {
  182. selectedTels.value = val;
  183. };
  184. const listCount = ref(null)
  185. function updateListCount(count) {
  186. listCount.value = count
  187. }
  188. // 地图--选树时抛出的事件
  189. const selectedTree = ref([]);
  190. const handleSelectedTree = (data) => {
  191. selectedTree.value = data;
  192. }
  193. const handleCancel = () => {
  194. checkShow.value = false;
  195. checkDistributeShow.value = false;
  196. selectedTree.value = []
  197. selectedTels.value = []
  198. // mapRef.value.clearSelection()
  199. mapRef.value.stopBoxSelect();
  200. };
  201. const saveEdit = () => {
  202. mapRef.value.saveSelect(() => {
  203. getPointList();
  204. });
  205. checkShow.value = false;
  206. checkDistributeShow.value = false;
  207. };
  208. const saveDistributeEdit = () => {
  209. if (!selectedTree.value.length || !selectedTels.value.length || selectedTree.value.length < selectedTels.value.length) {
  210. return
  211. }
  212. // mapRef.value.saveDistributeSelect(() => {
  213. // getPointList();
  214. // });
  215. const params = {
  216. farmId: currentFarmId,
  217. sampleIds: selectedTree.value,
  218. tels: selectedTels.value,
  219. };
  220. VE_API.manage_interface.saveTreeUser(params).then(({code}) => {
  221. if (code === 0) {
  222. ElMessage.success("分配成功");
  223. startReloadList.value = true; // 触发重新加载列表
  224. activeName.value = "已分配"
  225. handleCancel()
  226. return;
  227. }
  228. })
  229. };
  230. // 图例
  231. const legendArr = ref([]);
  232. const organId = ref("");
  233. const regionId = ref(null);
  234. eventBus.off("changePointLegend", toggleLegend);
  235. eventBus.on("changePointLegend", toggleLegend);
  236. function toggleLegend({ colorObj }) {
  237. legendArr.value = colorObj?.list;
  238. }
  239. //区域切换监听事件
  240. function areaId({ areaId, farmId }) {
  241. organId.value = farmId;
  242. regionId.value = areaId;
  243. // 树点位
  244. getPointList();
  245. // 分区
  246. if (!regionData.value.length) {
  247. // 全部区域
  248. getBlueRegionList();
  249. }
  250. }
  251. const urls = ref([]);
  252. const urlsIndex = ref(0);
  253. const currentComponent = ref("leftTabs");
  254. eventBus.on("clickMapPoint", toggleLeftComponet);
  255. function toggleLeftComponet() {
  256. currentComponent.value = "treeDetail";
  257. }
  258. function backHome() {
  259. currentComponent.value = "leftTabs";
  260. mapRef.value.resetCurrentTree();
  261. }
  262. function setReload() {
  263. startReloadList.value = true
  264. }
  265. </script>
  266. <style lang="scss" scoped>
  267. .base-container {
  268. width: 100%;
  269. height: 100%;
  270. position: absolute;
  271. box-sizing: border-box;
  272. z-index: 1;
  273. color: #ffffff;
  274. .content {
  275. width: 100%;
  276. height: calc(100% - 36px);
  277. display: flex;
  278. justify-content: space-between;
  279. box-sizing: border-box;
  280. .left,
  281. .right {
  282. width: 376px;
  283. height: calc(100% - 10px);
  284. margin-top: 10px;
  285. box-sizing: border-box;
  286. display: flex;
  287. position: relative;
  288. }
  289. .left {
  290. background: #fff;
  291. border-radius: 8px;
  292. margin-left: 25px;
  293. padding: 0 10px 10px;
  294. .arrow {
  295. position: absolute;
  296. right: -16px;
  297. top: calc(50% - 40px);
  298. background: #fff;
  299. width: 16px;
  300. height: 80px;
  301. line-height: 80px;
  302. border-radius: 0 5px 5px 0;
  303. text-align: center;
  304. cursor: pointer;
  305. }
  306. ::v-deep {
  307. .el-tabs__item {
  308. outline: none;
  309. }
  310. }
  311. }
  312. .right {
  313. width: 376px;
  314. margin-right: 25px;
  315. .adopt-list-wrap {
  316. ::v-deep {
  317. .el-tabs {
  318. height: 100%;
  319. }
  320. .el-tabs__content {
  321. height: calc(100% - 40px - 15px);
  322. position: static;
  323. }
  324. .el-tabs__item {
  325. color: #727272;
  326. width: 100px;
  327. }
  328. .el-tabs__active-bar {
  329. background-color: #2199f8;
  330. height: 1px;
  331. }
  332. .el-tabs__item.is-active {
  333. color: #2199f8;
  334. }
  335. .el-tabs__nav-wrap:after {
  336. height: 1px;
  337. background-color: rgba(127, 127, 127, 0.1);
  338. }
  339. .el-tabs__nav {
  340. left: 50%;
  341. transform: translateX(-50%) !important;
  342. }
  343. .el-tab-pane {
  344. height: 100%;
  345. }
  346. }
  347. .custom-tabs-label {
  348. position: relative;
  349. .count-text {
  350. position: absolute;
  351. top: -7px;
  352. right: -20px;
  353. width: 16px;
  354. height: 16px;
  355. background: #E04C4C;
  356. color: #fff;
  357. text-align: center;
  358. line-height: 16px;
  359. border-radius: 50%;
  360. font-size: 10px;
  361. }
  362. }
  363. }
  364. .list {
  365. width: 100%;
  366. height: 100%;
  367. overflow: auto;
  368. .btn-wrap {
  369. width: 100%;
  370. height: 25px;
  371. line-height: 25px;
  372. margin: 10px 0;
  373. display: flex;
  374. align-items: center;
  375. justify-content: space-between;
  376. div {
  377. width: 48%;
  378. height: 100%;
  379. color: #ffd489;
  380. border: 1px solid rgba(255, 213, 137, 0.6);
  381. border-radius: 2px;
  382. text-align: center;
  383. font-size: 12px;
  384. cursor: pointer;
  385. &.active {
  386. background: #ffd489;
  387. color: #000;
  388. }
  389. }
  390. }
  391. .img-box {
  392. width: 100%;
  393. height: calc(100% - 35px);
  394. overflow: auto;
  395. }
  396. .img-box1 {
  397. width: 100%;
  398. height: calc(100% - 10px);
  399. overflow: auto;
  400. margin-top: 10px;
  401. }
  402. .img-box2 {
  403. width: 100%;
  404. height: calc(100% - 45px);
  405. overflow: auto;
  406. margin-top: 10px;
  407. }
  408. img {
  409. width: 100%;
  410. height: auto;
  411. object-fit: cover;
  412. margin-bottom: 12px;
  413. cursor: pointer;
  414. }
  415. .mt {
  416. margin-top: -12px;
  417. }
  418. }
  419. }
  420. .overflow {
  421. overflow: auto;
  422. }
  423. .legend {
  424. position: fixed;
  425. bottom: 8px;
  426. right: 64px;
  427. height: 20px;
  428. }
  429. .map-bg {
  430. position: fixed;
  431. z-index: 2;
  432. background: rgba(35, 35, 35, 0.8);
  433. border-radius: 18px;
  434. padding: 7px 16px;
  435. left: 460px;
  436. }
  437. .tips {
  438. position: fixed;
  439. z-index: 2;
  440. top: 105px;
  441. width: 100%;
  442. display: flex;
  443. justify-content: center;
  444. .text {
  445. font-size: 20px;
  446. padding: 6px 16px;
  447. font-family: "PangMenZhengDao";
  448. background: rgba(4, 3, 0, 0.6);
  449. border-radius: 20px;
  450. span {
  451. color: #ffd489;
  452. }
  453. }
  454. }
  455. .right-button {
  456. position: fixed;
  457. z-index: 2;
  458. right: 460px;
  459. bottom: 36px;
  460. font-size: 24px;
  461. font-family: "PangMenZhengDao";
  462. padding: 16px 32px 20px 32px;
  463. cursor: pointer;
  464. background: url("@/assets/images/foster-home/btn-bg.png") no-repeat center center / 100% 100%;
  465. }
  466. .center-button {
  467. position: fixed;
  468. z-index: 2;
  469. width: 100%;
  470. display: flex;
  471. align-items: center;
  472. justify-content: center;
  473. bottom: 116px;
  474. div {
  475. cursor: pointer;
  476. width: 248px;
  477. text-align: center;
  478. padding: 15px;
  479. font-family: "PangMenZhengDao";
  480. color: #000;
  481. font-size: 20px;
  482. background: linear-gradient(0deg, #ffd887, #ed9e1e);
  483. border: 2px solid rgba(255, 255, 255, 0.61);
  484. border-radius: 12px;
  485. }
  486. .cancel {
  487. margin-right: 20px;
  488. background: #eeeeee;
  489. }
  490. .disabled {
  491. opacity: 0.6;
  492. pointer-events: none;
  493. }
  494. }
  495. .select-text {
  496. padding-left: 4px;
  497. font-size: 16px;
  498. .select-total {
  499. color: rgba(29, 29, 29, 0.5);
  500. }
  501. }
  502. .map-btn {
  503. top: 19px;
  504. cursor: pointer;
  505. }
  506. .map-legend {
  507. bottom: 36px;
  508. display: flex;
  509. align-items: center;
  510. .item {
  511. display: flex;
  512. align-items: center;
  513. font-size: 14px;
  514. img {
  515. width: 16px;
  516. margin-right: 6px;
  517. }
  518. .legend-block {
  519. width: 16px;
  520. height: 16px;
  521. box-sizing: border-box;
  522. border-radius: 50%;
  523. border: 2px solid #fff;
  524. margin-right: 6px;
  525. }
  526. }
  527. .selected-item {
  528. img {
  529. width: 24px;
  530. }
  531. }
  532. .legend-title {
  533. border-bottom: 1px solid rgba(102, 102, 102, 0.35);
  534. }
  535. .item + .item {
  536. padding-left: 12px;
  537. }
  538. }
  539. }
  540. }
  541. .bottom-map {
  542. width: 100%;
  543. height: 100%;
  544. position: absolute;
  545. z-index: 0;
  546. }
  547. .compare-start-btn {
  548. position: absolute;
  549. z-index: 2;
  550. left: 50%;
  551. transform: translateX(-50%);
  552. cursor: pointer;
  553. bottom: 106px;
  554. // right: 445px;
  555. img {
  556. height: 55px;
  557. }
  558. }
  559. </style>
  560. <style lang="less">
  561. .file-wrap {
  562. &.map-file {
  563. width: 367px;
  564. position: relative;
  565. background: url("@/assets/images/home/file-bg.png") no-repeat top center / 100% 100%;
  566. margin-left: 12px;
  567. padding: 12px;
  568. .file-title {
  569. font-size: 20px;
  570. color: #ffd489;
  571. .tag {
  572. border: 1px solid #ffd489;
  573. border-radius: 4px;
  574. font-size: 12px;
  575. display: inline-block;
  576. width: 44px;
  577. height: 20px;
  578. text-align: center;
  579. line-height: 18px;
  580. margin-left: 8px;
  581. padding: 1px 4px;
  582. }
  583. }
  584. .overview-file {
  585. padding-top: 20px;
  586. .box-title {
  587. font-size: 16px;
  588. padding-left: 13px;
  589. margin-bottom: 16px;
  590. position: relative;
  591. display: flex;
  592. justify-content: space-between;
  593. color: #fff;
  594. &::before {
  595. content: "";
  596. position: absolute;
  597. left: 0;
  598. top: 3px;
  599. width: 3px;
  600. height: 16px;
  601. background: #fff;
  602. border-radius: 11px;
  603. }
  604. }
  605. .title {
  606. color: #f3c11d;
  607. font-size: 16px;
  608. font-family: "PangMenZhengDao";
  609. margin-bottom: 20px;
  610. .big {
  611. width: 13px;
  612. height: 13px;
  613. margin: -10px 0 0 4px;
  614. }
  615. .small {
  616. width: 7px;
  617. height: 7px;
  618. margin-left: -3px;
  619. }
  620. }
  621. .base-data {
  622. background: rgba(207, 207, 207, 0.1);
  623. border-radius: 4px;
  624. padding: 6px 0;
  625. display: flex;
  626. .base-item {
  627. flex: 1;
  628. text-align: center;
  629. .label {
  630. font-size: 12px;
  631. color: #666666;
  632. }
  633. .value {
  634. padding-top: 2px;
  635. font-size: 16px;
  636. color: #ffffff;
  637. }
  638. }
  639. .base-item + .base-item {
  640. border-left: 1px solid rgba(102, 102, 102, 0.42);
  641. }
  642. }
  643. .list {
  644. margin-top: 15px;
  645. width: max-content;
  646. font-size: 14px;
  647. .list-item {
  648. color: #bbbbbb;
  649. display: flex;
  650. margin-bottom: 8px;
  651. .list-name {
  652. color: #f3c11d;
  653. margin-right: 6px;
  654. img {
  655. width: 17px;
  656. height: 13px;
  657. }
  658. }
  659. }
  660. }
  661. }
  662. .overview-file + .overview-file {
  663. margin-top: 8px;
  664. }
  665. .box-wrap {
  666. display: flex;
  667. .box-item {
  668. // flex: 1;
  669. min-width: 109px;
  670. display: flex;
  671. flex-direction: column;
  672. justify-content: center;
  673. align-items: center;
  674. padding: 6px;
  675. box-sizing: border-box;
  676. background: rgba(207, 207, 207, 0.1);
  677. border-radius: 4px;
  678. border: 1px solid rgba(207, 207, 207, 0.1);
  679. cursor: pointer;
  680. .item-name {
  681. font-size: 12px;
  682. color: #666666;
  683. width: max-content;
  684. }
  685. .item-val {
  686. font-size: 18px;
  687. color: #fff;
  688. width: max-content;
  689. padding-top: 3px;
  690. }
  691. &.active {
  692. background: rgba(255, 212, 137, 0.16);
  693. border: 1px solid #ffd489;
  694. .item-name {
  695. color: #bbbbbb;
  696. }
  697. }
  698. }
  699. .box-item + .box-item {
  700. margin-left: 8px;
  701. }
  702. }
  703. }
  704. }
  705. </style>