plan.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. <template>
  2. <div class="plan-page">
  3. <custom-header :name="pageType === 'plant' ? '种植方案' : '农事规划'"></custom-header>
  4. <div class="plan-content">
  5. <div class="plan-content-header" v-if="pageType === 'plant'">
  6. <el-select class="select-item" v-model="value" placeholder="选择品类">
  7. <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
  8. </el-select>
  9. <tab-list type="light" v-model="active" :tabs="tabs" @change="handleTabChange" />
  10. </div>
  11. <farm-work-plan-timeline
  12. class="timeline-container"
  13. :class="{
  14. 'timeline-container-plant': pageType == 'plant',
  15. 'timeline-container-no-permission': !hasPlanPermission
  16. }"
  17. :pageType="pageType"
  18. :farmId="route.query.farmId"
  19. @row-click="handleRowClick"
  20. @edit="handleEdit"
  21. :disableClick="!hasPlanPermission"
  22. />
  23. </div>
  24. <div class="custom-bottom-fixed-btns" v-has-permission="'农事规划'">
  25. <div class="bottom-btn-group">
  26. <div class="bottom-btn secondary-btn" @click="handlePhenologySetting">物候期设置</div>
  27. <div class="bottom-btn secondary-btn" v-if="pageType === 'plant'" @click="openCopyPlanPopup">
  28. {{ active === 1 ? "复制方案" : "方案设置" }}
  29. </div>
  30. </div>
  31. <div class="bottom-btn primary-btn" @click="addNewTask">新增农事</div>
  32. </div>
  33. </div>
  34. <!-- 农事信息弹窗 -->
  35. <detail-dialog ref="detailDialogRef" @triggerFarmWork="triggerFarmWork"></detail-dialog>
  36. <!-- 互动设置弹窗 -->
  37. <interact-popup
  38. ref="interactPopupRef"
  39. @handleSaveSuccess="getFarmWorkPlanForPhenology"
  40. @handleDeleteInteract="handleDeleteInteract"
  41. ></interact-popup>
  42. <!-- 复制方案弹窗 -->
  43. <Popup v-model:show="showCopyPlan" class="copy-plan-popup" round closeable :close-on-click-overlay="false">
  44. <div class="copy-plan-content">
  45. <div class="label">{{ active === 1 ? "复制为" : "方案名称" }}</div>
  46. <el-input v-model="copyPlanName" size="large" placeholder="请输入方案名称" class="copy-plan-input" />
  47. </div>
  48. <div class="copy-plan-footer">
  49. <div class="btn btn-cancel" @click="handleCancelCopyPlan">{{ active === 1 ? "取消复制" : "删除方案" }}</div>
  50. <div class="btn btn-confirm" @click="handleConfirmCopyPlan">
  51. {{ active === 1 ? "确定复制" : "确定设置" }}
  52. </div>
  53. </div>
  54. </Popup>
  55. <!-- 物候期设置弹窗 -->
  56. <Popup
  57. v-model:show="showPhenologySetting"
  58. class="copy-plan-popup phenology-popup"
  59. round
  60. closeable
  61. :close-on-click-overlay="false"
  62. >
  63. <div class="phenology-header">物候期时间设置</div>
  64. <div class="phenology-list">
  65. <div
  66. class="phenology-item"
  67. v-for="(item, index) in mergedReproductiveList"
  68. :key="item.id || index"
  69. >
  70. <div class="item-label">
  71. <span class="label-text">{{ item.name }}</span>
  72. <span>起始时间</span>
  73. </div>
  74. <div class="item-value">
  75. <el-date-picker
  76. style="width: 100%"
  77. size="large"
  78. value-format="YYYY-MM-DD"
  79. v-model="item.startDate"
  80. type="date"
  81. placeholder="选择日期"
  82. />
  83. </div>
  84. </div>
  85. </div>
  86. <div class="phenology-footer" @click="handleConfirmPhenologySetting">确认设置</div>
  87. </Popup>
  88. </template>
  89. <script setup>
  90. import { ref, onMounted, computed } from "vue";
  91. import { Popup } from "vant";
  92. import customHeader from "@/components/customHeader.vue";
  93. import tabList from "@/components/pageComponents/TabList.vue";
  94. import FarmWorkPlanTimeline from "@/components/pageComponents/FarmWorkPlanTimeline.vue";
  95. import { useRouter, useRoute } from "vue-router";
  96. import detailDialog from "@/components/detailDialog.vue";
  97. import eventBus from "@/api/eventBus";
  98. import interactPopup from "@/components/popup/interactPopup.vue";
  99. import { ElMessage } from "element-plus";
  100. const router = useRouter();
  101. const route = useRoute();
  102. // 检查是否有"农事规划"权限
  103. const hasPlanPermission = computed(() => {
  104. try {
  105. const userInfoStr = localStorage.getItem("localUserInfo");
  106. if (!userInfoStr) return false;
  107. const userInfo = JSON.parse(userInfoStr);
  108. const permissions = userInfo.agriculturalPermissions || [];
  109. return permissions.includes("农事规划");
  110. } catch (error) {
  111. console.error("解析用户信息失败:", error);
  112. return false;
  113. }
  114. });
  115. const active = ref(1);
  116. const tabs = ref([
  117. {
  118. id: 1,
  119. name: "标准化方案",
  120. },
  121. {
  122. id: 2,
  123. name: "全托管方案",
  124. },
  125. {
  126. id: 3,
  127. name: "半托管方案",
  128. },
  129. ]);
  130. const handleTabChange = (id, item) => {
  131. active.value = id;
  132. console.log(id, item);
  133. };
  134. const value = ref("1");
  135. const options = ref([
  136. {
  137. value: "1",
  138. label: "荔枝",
  139. },
  140. {
  141. value: "2",
  142. label: "全托管方案",
  143. },
  144. ]);
  145. const pageType = ref("");
  146. onMounted(() => {
  147. pageType.value = route.query.pageType || "";
  148. getFarmWorkPlanForPhenology();
  149. });
  150. const mergedReproductiveList = ref([]);
  151. const containerSpaceTimeId = ref(null);
  152. const getPhenologyList = async (containerSpaceTimeIdVal) => {
  153. if (!containerSpaceTimeIdVal) return;
  154. const res = await VE_API.monitor.listPhenology({
  155. containerSpaceTimeId: containerSpaceTimeIdVal,
  156. farmId: route.query.farmId,
  157. });
  158. if (res.code === 0) {
  159. mergedReproductiveList.value = res.data || [];
  160. }
  161. };
  162. // 获取农事规划数据以获取 containerSpaceTimeId
  163. const getFarmWorkPlanForPhenology = async () => {
  164. try {
  165. const { data, code } = await VE_API.monitor.farmWorkPlan({
  166. farmId: route.query.farmId,
  167. });
  168. if (code === 0 && data?.phenologyList?.[0]?.containerSpaceTimeId) {
  169. containerSpaceTimeId.value = data.phenologyList[0].containerSpaceTimeId;
  170. await getPhenologyList(containerSpaceTimeId.value);
  171. }
  172. } catch (error) {
  173. console.error("获取农事规划数据失败:", error);
  174. }
  175. };
  176. // 复制方案弹窗相关
  177. const showCopyPlan = ref(false);
  178. const showPhenologySetting = ref(false);
  179. const copyPlanName = ref("");
  180. const openCopyPlanPopup = () => {
  181. copyPlanName.value = "";
  182. showCopyPlan.value = true;
  183. };
  184. // 物候期设置弹窗
  185. const handlePhenologySetting = () => {
  186. showPhenologySetting.value = true;
  187. };
  188. /**
  189. * 确认物候期设置
  190. */
  191. const handleConfirmPhenologySetting = async () => {
  192. console.log(mergedReproductiveList.value);
  193. const params = {
  194. farmId: route.query.farmId,
  195. items: mergedReproductiveList.value.map((item) => ({
  196. phenologyId: item.id,
  197. startDate: item.startDate,
  198. })),
  199. };
  200. const res = await VE_API.monitor.batchSaveFarmPhenologyTime(params);
  201. if (res.code === 0) {
  202. ElMessage.success("设置成功");
  203. showPhenologySetting.value = false;
  204. getFarmWorkPlanForPhenology();
  205. }
  206. };
  207. // 取消复制方案
  208. const handleCancelCopyPlan = () => {
  209. showCopyPlan.value = false;
  210. };
  211. // 确定复制方案
  212. const handleConfirmCopyPlan = () => {
  213. if (!copyPlanName.value.trim()) {
  214. ElMessage.warning("请输入方案名称");
  215. return;
  216. }
  217. // TODO: 在此处调用复制方案的接口
  218. ElMessage.success("复制成功");
  219. showCopyPlan.value = false;
  220. };
  221. // 新增农事
  222. const addNewTask = () => {
  223. ElMessage.warning("该功能正在升级中,敬请期待");
  224. };
  225. const triggerFarmWork = () => {
  226. eventBus.emit("activeUpload:show", {
  227. gardenIdVal: route.query.farmId,
  228. problemTitleVal: "请选择您出现" + curFarmObj.value.farmWorkName + "的时间",
  229. arrangeIdVal: curFarmObj.value.id,
  230. needExecutorVal: true,
  231. });
  232. };
  233. const curFarmObj = ref({});
  234. const handleRowClick = (item) => {
  235. curFarmObj.value = item;
  236. router.push({
  237. path: "/modify",
  238. query: { id: item.id, farmId: route.query.farmId, farmWorkId: item.farmWorkId, containerSpaceTimeId: item.containerSpaceTimeId, agriculturalStoreId: route.query.agriculturalStoreId },
  239. });
  240. };
  241. const interactPopupRef = ref(null);
  242. const handleEdit = (item) => {
  243. console.log(item);
  244. if (interactPopupRef.value) {
  245. interactPopupRef.value.showPopup(item);
  246. }
  247. };
  248. const handleDeleteInteract = (params) => {
  249. getFarmWorkPlanForPhenology();
  250. };
  251. </script>
  252. <style scoped lang="scss">
  253. .plan-page {
  254. width: 100%;
  255. height: 100vh;
  256. background: #f5f7fb;
  257. .plan-content {
  258. padding: 12px 0;
  259. .plan-content-header {
  260. display: flex;
  261. align-items: center;
  262. gap: 12px;
  263. margin-bottom: 10px;
  264. margin-left: 12px;
  265. .select-item {
  266. width: 82px;
  267. ::v-deep {
  268. .el-select__wrapper {
  269. box-shadow: none;
  270. border-radius: 25px;
  271. border: 0.5px solid rgba(153, 153, 153, 0.5);
  272. }
  273. }
  274. }
  275. }
  276. .timeline-container {
  277. height: calc(100vh - 40px - 73px);
  278. padding: 0 12px;
  279. &.timeline-container-plant {
  280. height: calc(100vh - 40px - 73px - 38px);
  281. }
  282. &.timeline-container-plant {
  283. height: calc(100vh - 40px - 38px);
  284. }
  285. // 没有权限时,底部按钮不显示,高度增加 73px
  286. &.timeline-container-no-permission {
  287. height: calc(100vh - 40px - 58px);
  288. }
  289. }
  290. }
  291. // 控制区域样式
  292. .custom-bottom-fixed-btns {
  293. .bottom-btn-group {
  294. display: flex;
  295. gap: 12px;
  296. }
  297. }
  298. }
  299. .copy-plan-popup {
  300. width: 100%;
  301. padding: 50px 12px 20px 12px;
  302. &::before {
  303. content: "";
  304. position: absolute;
  305. top: 0;
  306. left: 0;
  307. width: 100%;
  308. height: 136px;
  309. background: url("@/assets/img/monitor/popup-header-bg.png") no-repeat center center / 100% 100%;
  310. }
  311. .copy-plan-content {
  312. display: flex;
  313. align-items: center;
  314. gap: 12px;
  315. .label {
  316. font-size: 16px;
  317. font-weight: 500;
  318. }
  319. .copy-plan-input {
  320. width: calc(100% - 80px);
  321. }
  322. }
  323. .copy-plan-footer {
  324. display: flex;
  325. gap: 12px;
  326. margin-top: 20px;
  327. .btn {
  328. flex: 1;
  329. color: #666666;
  330. border: 1px solid #999999;
  331. border-radius: 25px;
  332. padding: 10px 0;
  333. font-size: 16px;
  334. text-align: center;
  335. &.btn-confirm {
  336. color: #fff;
  337. border: 1px solid #2199f8;
  338. background: #2199f8;
  339. }
  340. }
  341. }
  342. }
  343. .phenology-popup {
  344. padding: 28px 20px 20px;
  345. .phenology-header {
  346. font-size: 24px;
  347. text-align: center;
  348. margin-bottom: 20px;
  349. font-family: "PangMenZhengDao";
  350. }
  351. .phenology-list {
  352. width: 100%;
  353. .phenology-item {
  354. width: 100%;
  355. display: flex;
  356. align-items: center;
  357. justify-content: space-between;
  358. .item-label {
  359. display: flex;
  360. align-items: center;
  361. gap: 4px;
  362. font-size: 15px;
  363. color: rgba(0, 0, 0, 0.4);
  364. .label-text {
  365. color: #000;
  366. font-size: 16px;
  367. font-weight: 500;
  368. }
  369. }
  370. .item-value {
  371. width: calc(100% - 156px);
  372. }
  373. }
  374. .phenology-item + .phenology-item {
  375. margin-top: 10px;
  376. }
  377. }
  378. .phenology-footer{
  379. width: 100%;
  380. text-align: center;
  381. font-size: 16px;
  382. margin-top: 20px;
  383. color: #fff;
  384. background: #2199f8;
  385. border-radius: 25px;
  386. padding: 10px 0;
  387. }
  388. }
  389. </style>