plan.vue 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769
  1. <template>
  2. <div class="plan-page">
  3. <custom-header
  4. :name="headerTitle"
  5. isGoBack
  6. @goback="goback"
  7. :isClose="route.query.headerTitle ? true : false"
  8. ></custom-header>
  9. <div class="plan-content">
  10. <div class="plan-content-header" v-if="pageType === 'plant'">
  11. <el-select
  12. class="select-item"
  13. v-model="specieValue"
  14. placeholder="选择品类"
  15. @change="() => getListMySchemes('left')"
  16. >
  17. <el-option
  18. v-for="item in options"
  19. :key="item.id"
  20. :label="item.name"
  21. :value="item.defaultContainerId"
  22. />
  23. </el-select>
  24. <tab-list
  25. class="tabs-list"
  26. type="light"
  27. v-model="active"
  28. :tabs="tabs"
  29. :scrollType="scrollType"
  30. @change="handleTabChange"
  31. />
  32. </div>
  33. <div class="tip-box">
  34. <Highlight
  35. :keywords="['关注农事/托管农事', '取消关注']"
  36. source-string="提示:关注农事/托管农事 会触发农情互动,并且为您推送农事,取消关注 则不会为您推送任何与该农事相关的内容"
  37. />
  38. </div>
  39. <div
  40. class="timeline-wrap"
  41. :class="{
  42. 'timeline-container-plant-wrap': pageType == 'plant',
  43. 'timeline-container-no-permission-wrap': !hasPlanPermission,
  44. 'no-default-plan-wrap':
  45. active !== tabs[0]?.id && pageType == 'plant' && tabs[currentTabIndex]?.enabled == 0,
  46. }"
  47. >
  48. <farm-work-plan-timeline
  49. :pageType="pageType"
  50. :farmId="route.query.farmId"
  51. :containerId="containerIdData"
  52. @row-click="handleRowClick"
  53. :disableClick="!hasPlanPermission || active === tabs[0]?.id"
  54. :isStandard="active === tabs[0]?.id"
  55. :schemeId="route.query.schemeId || active"
  56. ref="farmWorkPlanTimelineRef"
  57. />
  58. </div>
  59. </div>
  60. <div
  61. class="custom-bottom-fixed-btns"
  62. :class="{ center: active === tabs[0]?.id && pageType == 'plant' }"
  63. v-has-permission="'农事规划'"
  64. >
  65. <div class="bottom-btn-group-wrap">
  66. <div
  67. class="bottom-btn-group"
  68. :class="{ 'justify-center': active === tabs[0]?.id && pageType == 'plant' }"
  69. >
  70. <div
  71. class="bottom-btn secondary-btn"
  72. @click="handlePhenologySetting"
  73. v-show="active !== tabs[0]?.id"
  74. >
  75. 物候期设置
  76. </div>
  77. <div class="bottom-btn secondary-btn" v-if="pageType === 'plant'" @click="openCopyPlanPopup">
  78. {{ active === tabs[0]?.id ? "复制方案" : "方案设置" }}
  79. </div>
  80. </div>
  81. <div class="bottom-btn primary-btn" @click="addNewTask" v-show="active !== tabs[0]?.id">新增农事</div>
  82. </div>
  83. <template v-if="active !== tabs[0]?.id && pageType == 'plant' && tabs[currentTabIndex]?.enabled == 0">
  84. <div class="bottom-btn-divider"></div>
  85. <div class="bottom-btn primary-btn submit-btn" @click="handleSubmitPlan">提交方案</div>
  86. </template>
  87. </div>
  88. </div>
  89. <!-- 农事信息弹窗 -->
  90. <detail-dialog ref="detailDialogRef" @triggerFarmWork="triggerFarmWork"></detail-dialog>
  91. <!-- 复制方案弹窗 -->
  92. <Popup v-model:show="showCopyPlan" class="copy-plan-popup" round closeable :close-on-click-overlay="false">
  93. <div class="copy-plan-content">
  94. <div class="label">{{ active === tabs[0]?.id ? "复制为" : "方案名称" }}</div>
  95. <el-input v-model="copyPlanName" size="large" placeholder="请输入方案名称" class="copy-plan-input" />
  96. </div>
  97. <div class="copy-plan-footer">
  98. <div class="btn btn-cancel" @click="handleCancelCopyPlan">
  99. {{ active === tabs[0]?.id ? "取消复制" : "删除方案" }}
  100. </div>
  101. <div class="btn btn-confirm" @click="handleConfirmCopyPlan">
  102. {{ active === tabs[0]?.id ? "确定复制" : "确定设置" }}
  103. </div>
  104. </div>
  105. </Popup>
  106. <!-- 物候期设置弹窗 -->
  107. <Popup
  108. v-model:show="showPhenologySetting"
  109. class="copy-plan-popup phenology-popup"
  110. round
  111. closeable
  112. :close-on-click-overlay="false"
  113. >
  114. <div class="phenology-header">物候期时间设置</div>
  115. <div class="phenology-list">
  116. <div class="phenology-item" v-for="(item, index) in mergedReproductiveList" :key="item.id || index">
  117. <div class="item-label">
  118. <span class="label-text">{{ item.name }}</span>
  119. <span>起始时间</span>
  120. </div>
  121. <div class="item-value">
  122. <el-date-picker
  123. style="width: 100%"
  124. size="large"
  125. value-format="YYYY-MM-DD"
  126. v-model="item.startDate"
  127. :clearable="false"
  128. :editable="false"
  129. type="date"
  130. placeholder="选择日期"
  131. @change="(date) => handleStartDateChange(date, index)"
  132. />
  133. </div>
  134. </div>
  135. <div class="phenology-footer-tip">
  136. <span>注:</span>
  137. <span class="text">请从上往下按照时间顺序填写日期</span>
  138. </div>
  139. </div>
  140. <div class="phenology-footer" @click="handleConfirmPhenologySetting">确认设置</div>
  141. </Popup>
  142. <tip-popup
  143. v-model:show="showTipPopup"
  144. type="warning"
  145. text2="还未完善此方案不可用"
  146. :highlightText="highlightText"
  147. buttonText="我知道了"
  148. @confirm="handleBtn"
  149. />
  150. </template>
  151. <script setup>
  152. import { ref, onMounted, computed, watch } from "vue";
  153. import { Popup, Highlight } from "vant";
  154. import customHeader from "@/components/customHeader.vue";
  155. import tabList from "@/components/pageComponents/TabList.vue";
  156. import FarmWorkPlanTimeline from "@/components/pageComponents/FarmWorkPlanTimeline.vue";
  157. import { useRouter, useRoute } from "vue-router";
  158. import detailDialog from "@/components/detailDialog.vue";
  159. import eventBus from "@/api/eventBus";
  160. import tipPopup from "@/components/popup/tipPopup.vue";
  161. import { ElMessage, ElMessageBox } from "element-plus";
  162. const router = useRouter();
  163. const route = useRoute();
  164. const showTipPopup = ref(false);
  165. const highlightText = ref("");
  166. const handleBtn = () => {
  167. showTipPopup.value = false;
  168. setTimeout(() => {
  169. router.go(-1);
  170. }, 10);
  171. };
  172. const goback = () => {
  173. if (
  174. tabs.value[currentTabIndex.value]?.enabled == 0 &&
  175. pageType.value === "plant" &&
  176. active.value !== tabs.value[0]?.id
  177. ) {
  178. highlightText.value = tabs.value[currentTabIndex.value]?.name;
  179. showTipPopup.value = true;
  180. } else {
  181. router.go(-1);
  182. }
  183. };
  184. const userInfoStr = localStorage.getItem("localUserInfo");
  185. const userInfo = userInfoStr ? JSON.parse(userInfoStr) : {};
  186. // 检查是否有"农事规划"权限
  187. const hasPlanPermission = computed(() => {
  188. try {
  189. const permissions = userInfo?.agriculturalPermissions || [];
  190. return permissions.includes("农事规划");
  191. } catch (error) {
  192. console.error("解析用户信息失败:", error);
  193. return false;
  194. }
  195. });
  196. const pageType = ref("");
  197. const headerTitle = ref("");
  198. onMounted(() => {
  199. pageType.value = route.query.pageType || "";
  200. headerTitle.value = route.query.headerTitle || (pageType.value === "plant" ? "种植方案" : "农事规划");
  201. if (!pageType.value) {
  202. getFarmWorkPlanForPhenology();
  203. } else {
  204. getSpecieList();
  205. }
  206. });
  207. // 获取品类列表
  208. const specieValue = ref(null);
  209. const options = ref([]);
  210. const getSpecieList = () => {
  211. VE_API.farm.fetchSpecieList({ agriculturalId: userInfo?.agriculturalId }).then(({ data }) => {
  212. if (data && data.length) {
  213. options.value = data || [];
  214. let schemeType = "left"; // 默认使用left
  215. if (sessionStorage.getItem("specieValue")) {
  216. specieValue.value = sessionStorage.getItem("specieValue");
  217. currentTabIndex.value = sessionStorage.getItem("currentTabIndex");
  218. sessionStorage.removeItem("specieValue");
  219. sessionStorage.removeItem("currentTabIndex");
  220. // 判断恢复的specieValue是否是最后一项
  221. const currentIndex = data.findIndex(item => item.defaultContainerId === specieValue.value);
  222. if (currentIndex === data.length - 1) {
  223. schemeType = "right";
  224. }
  225. } else {
  226. specieValue.value = data[0]?.defaultContainerId;
  227. }
  228. getListMySchemes(schemeType);
  229. }
  230. });
  231. };
  232. // 获取方案列表
  233. const active = ref(null);
  234. const tabs = ref([]);
  235. // 控制标签滚动方向:'left' | 'right' | 'auto' | ''
  236. const scrollType = ref("auto");
  237. const containerIdData = ref(null);
  238. const getListMySchemes = (type = "auto") => {
  239. VE_API.home.listMySchemes({ containerId: specieValue.value }).then(({ data }) => {
  240. tabs.value = data || [];
  241. containerIdData.value = tabs.value[0]?.containerId;
  242. if (sessionStorage.getItem("active")) {
  243. active.value = sessionStorage.getItem("active");
  244. sessionStorage.removeItem("active");
  245. } else {
  246. if (type === "right") {
  247. active.value = data[data.length - 1].id;
  248. } else if (type === "left") {
  249. active.value = data[0].id;
  250. } else {
  251. if(currentTab.value) {
  252. currentTab.value = data.filter((item) => item.id === currentTab.value?.id)?.[0];
  253. } else {
  254. currentTab.value = data[0];
  255. }
  256. copyPlanName.value = currentTab.value.name;
  257. }
  258. }
  259. scrollType.value = type;
  260. getFarmWorkPlanForPhenology();
  261. });
  262. };
  263. const currentTab = ref(null);
  264. const currentTabIndex = ref(0);
  265. const handleTabChange = (id, item, index) => {
  266. active.value = id;
  267. currentTab.value = item;
  268. currentTabIndex.value = index;
  269. getFarmWorkPlanForPhenology();
  270. };
  271. const mergedReproductiveList = ref([]);
  272. const containerSpaceTimeId = ref(null);
  273. const schemeId = ref(null);
  274. const getPhenologyList = async () => {
  275. const params = {
  276. containerSpaceTimeId: containerSpaceTimeId.value,
  277. agriculturalId: userInfo?.agriculturalId,
  278. farmId: route.query.farmId,
  279. };
  280. const res = await VE_API.monitor.listPhenology(params);
  281. if (res.code === 0) {
  282. // 将intervalDaysArr合并到mergedReproductiveList中
  283. if (intervalDaysArr.value.length > 0 && res.data.length > 0) {
  284. mergedReproductiveList.value = res.data.map((item, index) => {
  285. return {
  286. ...item,
  287. intervalDays: intervalDaysArr.value[index],
  288. };
  289. });
  290. }
  291. }
  292. };
  293. const farmWorkIds = ref([]);
  294. const intervalDaysArr = ref([]);
  295. // 获取农事规划数据以获取 containerSpaceTimeId
  296. const getFarmWorkPlanForPhenology = async () => {
  297. try {
  298. const { data, code } = await VE_API.monitor.farmWorkPlan({
  299. containerId: specieValue.value,
  300. farmId: route.query.farmId,
  301. schemeId: active.value,
  302. });
  303. if (code === 0 && data?.phenologyList?.[0]?.containerSpaceTimeId) {
  304. containerSpaceTimeId.value = data.phenologyList[0].containerSpaceTimeId;
  305. schemeId.value = data.schemeId;
  306. // 收集所有farmWorkId
  307. farmWorkIds.value = [];
  308. intervalDaysArr.value = [];
  309. data.phenologyList.forEach((phenology) => {
  310. intervalDaysArr.value.push(phenology.intervalDays);
  311. if (Array.isArray(phenology.reproductiveList)) {
  312. phenology.reproductiveList.forEach((reproductive) => {
  313. if (Array.isArray(reproductive.farmWorkArrangeList)) {
  314. reproductive.farmWorkArrangeList.forEach((farmWork) => {
  315. if (farmWork.farmWorkId && farmWork.isFollow !== 0) {
  316. farmWorkIds.value.push(farmWork.farmWorkId);
  317. }
  318. });
  319. }
  320. });
  321. }
  322. });
  323. await getPhenologyList(containerSpaceTimeId.value);
  324. }
  325. } catch (error) {
  326. console.error("获取农事规划数据失败:", error);
  327. }
  328. };
  329. // 复制方案弹窗相关
  330. const showCopyPlan = ref(false);
  331. const showPhenologySetting = ref(false);
  332. const copyPlanName = ref("");
  333. const openCopyPlanPopup = () => {
  334. copyPlanName.value = "";
  335. showCopyPlan.value = true;
  336. if (active.value !== tabs.value[0]?.id) {
  337. copyPlanName.value = currentTab.value.name;
  338. }
  339. };
  340. // 物候期设置弹窗
  341. const handlePhenologySetting = () => {
  342. showPhenologySetting.value = true;
  343. };
  344. /**
  345. * 处理物候期开始时间变化
  346. * 当修改某个物候期的开始时间时,自动更新后续所有物候期的开始时间
  347. */
  348. const handleStartDateChange = (date, currentIndex) => {
  349. if (!date || !mergedReproductiveList.value || mergedReproductiveList.value.length === 0) {
  350. return;
  351. }
  352. // 从当前修改的物候期开始,更新后续所有物候期的开始时间
  353. for (let i = currentIndex; i < mergedReproductiveList.value.length - 1; i++) {
  354. const currentItem = mergedReproductiveList.value[i];
  355. const nextItem = mergedReproductiveList.value[i + 1];
  356. // 获取当前物候期的间隔天数
  357. const intervalDays = currentItem.intervalDays || 0;
  358. if (intervalDays > 0 && currentItem.startDate) {
  359. // 将日期字符串转换为时间戳(毫秒)
  360. const currentStartDateTimestamp = new Date(currentItem.startDate).getTime();
  361. // 在时间戳基础上加上间隔天数(转换为毫秒:天数 * 24小时 * 60分钟 * 60秒 * 1000毫秒)
  362. const nextStartDateTimestamp = currentStartDateTimestamp + (intervalDays * 24 * 60 * 60 * 1000);
  363. // 将时间戳转换回日期对象,然后格式化为 YYYY-MM-DD 格式
  364. const nextStartDate = new Date(nextStartDateTimestamp);
  365. const year = nextStartDate.getFullYear();
  366. const month = String(nextStartDate.getMonth() + 1).padStart(2, '0');
  367. const day = String(nextStartDate.getDate()).padStart(2, '0');
  368. const nextStartDateStr = `${year}-${month}-${day}`;
  369. // 更新下一个物候期的开始时间
  370. nextItem.startDate = nextStartDateStr;
  371. }
  372. }
  373. };
  374. /**
  375. * 确认物候期设置
  376. */
  377. const farmWorkPlanTimelineRef = ref(null);
  378. const handleConfirmPhenologySetting = async () => {
  379. const params = {
  380. farmId: route.query.farmId,
  381. agriculturalId: userInfo?.agriculturalId,
  382. items: mergedReproductiveList.value.map((item) => ({
  383. phenologyId: item.id,
  384. startDate: item.startDate,
  385. })),
  386. };
  387. const res = await VE_API.monitor.batchSaveFarmPhenologyTime(params);
  388. if (res.code === 0) {
  389. ElMessage.success("设置成功");
  390. showPhenologySetting.value = false;
  391. getFarmWorkPlanForPhenology();
  392. farmWorkPlanTimelineRef.value.updateFarmWorkPlan();
  393. }
  394. };
  395. // 取消复制方案
  396. const handleCancelCopyPlan = () => {
  397. if (active.value === tabs.value[0]?.id) {
  398. showCopyPlan.value = false;
  399. } else {
  400. ElMessageBox.confirm("确定要删除该方案吗?", "提示", {
  401. confirmButtonText: "确定",
  402. cancelButtonText: "取消",
  403. type: "warning",
  404. }).then(() => {
  405. VE_API.monitor
  406. .deleteScheme({
  407. schemeId: active.value,
  408. })
  409. .then(({ code }) => {
  410. if (code === 0) {
  411. showCopyPlan.value = false;
  412. ElMessage.success("删除成功");
  413. getListMySchemes("left");
  414. }
  415. });
  416. });
  417. }
  418. };
  419. // 确定复制方案
  420. const handleConfirmCopyPlan = () => {
  421. if (!copyPlanName.value.trim()) {
  422. ElMessage.warning("请输入方案名称");
  423. return;
  424. }
  425. if (active.value === tabs.value[0]?.id) {
  426. VE_API.monitor
  427. .copyScheme({
  428. containerId: specieValue.value,
  429. sourceSchemeId: active.value,
  430. schemeName: copyPlanName.value,
  431. })
  432. .then(({ code, data }) => {
  433. if (code === 0) {
  434. showCopyPlan.value = false;
  435. ElMessage.success("复制成功");
  436. getListMySchemes("right");
  437. currentTab.value = data;
  438. currentTabIndex.value = (tabs.value.length - 1) + 1;
  439. }
  440. });
  441. } else {
  442. VE_API.monitor
  443. .renameScheme({
  444. schemeId: active.value,
  445. name: copyPlanName.value,
  446. })
  447. .then(({ code }) => {
  448. if (code === 0) {
  449. showCopyPlan.value = false;
  450. ElMessage.success("修改成功");
  451. getListMySchemes("auto");
  452. }
  453. });
  454. }
  455. };
  456. // 验证药肥报价信息并返回结果
  457. // 返回: { allTrue: boolean, invalidIds: string[] }
  458. const validatePesticideFertilizerQuotes = async () => {
  459. const { data, code } = await VE_API.monitor.batchValidatePesticideFertilizerQuotes({
  460. ids: farmWorkIds.value,
  461. schemeId: active.value,
  462. });
  463. if (code !== 0 || !data) {
  464. return { allTrue: false, invalidIds: [] };
  465. }
  466. // 判断所有值是否都为true
  467. const allTrue = Object.values(data).every((value) => value === true);
  468. // 收集所有不为true的ID
  469. const invalidIds = Object.keys(data).filter((key) => data[key] !== true);
  470. return { allTrue, invalidIds };
  471. };
  472. // 提交方案
  473. const handleSubmitPlan = async () => {
  474. // 调用验证方法,传入所有ids
  475. if (farmWorkIds.value.length > 0) {
  476. const { allTrue } = await validatePesticideFertilizerQuotes();
  477. if (allTrue) {
  478. VE_API.monitor
  479. .enableScheme({
  480. schemeId: active.value,
  481. })
  482. .then(({ code }) => {
  483. if (code === 0) {
  484. ElMessage.success("提交成功");
  485. if(route.query.headerTitle) {
  486. router.replace({
  487. path: "/home",
  488. });
  489. }else{
  490. getListMySchemes("auto");
  491. }
  492. }
  493. });
  494. } else {
  495. ElMessage.warning("当前方案有未完善报价信息的农事,请先完善报价信息");
  496. }
  497. }
  498. };
  499. const savePlanPageInfo = () => {
  500. if(route.query.pageType !== 'plant') {
  501. return;
  502. }
  503. sessionStorage.setItem("specieValue", specieValue.value);
  504. sessionStorage.setItem("active", active.value);
  505. sessionStorage.setItem("currentTabIndex", currentTabIndex.value);
  506. };
  507. // 新增农事
  508. const addNewTask = () => {
  509. savePlanPageInfo();
  510. router.push({
  511. path: "/add_work",
  512. query: {
  513. containerSpaceTimeId: containerSpaceTimeId.value,
  514. schemeId: route.query.schemeId || schemeId.value,
  515. },
  516. });
  517. };
  518. const triggerFarmWork = () => {
  519. eventBus.emit("activeUpload:show", {
  520. gardenIdVal: route.query.farmId,
  521. problemTitleVal: "请选择您出现" + curFarmObj.value.farmWorkName + "的时间",
  522. arrangeIdVal: curFarmObj.value.id,
  523. needExecutorVal: true,
  524. });
  525. };
  526. const curFarmObj = ref({});
  527. const handleRowClick = (item) => {
  528. curFarmObj.value = item;
  529. savePlanPageInfo();
  530. sessionStorage.setItem("farmWorkAndArrangeIds", JSON.stringify(item.invalidArr));
  531. const enabled = tabs.value[currentTabIndex.value]?.enabled;
  532. router.push({
  533. path: "/modify",
  534. query: {
  535. id: item.id,
  536. noPrice: pageType.value === "plant" ? true : false,
  537. farmId: route.query.farmId,
  538. farmWorkId: item.farmWorkId,
  539. containerSpaceTimeId: item.containerSpaceTimeId,
  540. isDefault: active.value === tabs.value[0]?.id,
  541. enabled: enabled,
  542. isEdit: item.isEdit,
  543. },
  544. });
  545. };
  546. </script>
  547. <style scoped lang="scss">
  548. .plan-page {
  549. width: 100%;
  550. height: 100vh;
  551. background: #f5f7fb;
  552. .plan-content {
  553. .plan-content-header {
  554. display: flex;
  555. align-items: center;
  556. gap: 12px;
  557. margin: 10px 0 10px 12px;
  558. .select-item {
  559. width: 82px;
  560. ::v-deep {
  561. .el-select__wrapper {
  562. box-shadow: none;
  563. border-radius: 25px;
  564. border: 0.5px solid rgba(153, 153, 153, 0.5);
  565. }
  566. }
  567. }
  568. .tabs-list {
  569. width: calc(100% - 94px);
  570. margin-right: 8px;
  571. }
  572. }
  573. .tip-box {
  574. padding: 5px 10px;
  575. background: rgba(33, 153, 248, 0.1);
  576. border-radius: 5px;
  577. font-size: 12px;
  578. margin: 8px 10px;
  579. color: #444;
  580. }
  581. .timeline-wrap {
  582. height: calc(100vh - 90px - 80px);
  583. padding: 0 10px 0 4px;
  584. &.timeline-container-plant-wrap {
  585. height: calc(100vh - 90px - 85px - 38px);
  586. }
  587. &.no-default-plan-wrap {
  588. height: calc(100vh - 90px - 85px - 100px);
  589. }
  590. // 没有权限时,底部按钮不显示,高度增加 73px
  591. &.timeline-container-no-permission-wrap {
  592. height: calc(100vh - 90px - 60px);
  593. }
  594. }
  595. }
  596. // 控制区域样式
  597. .custom-bottom-fixed-btns {
  598. flex-direction: column;
  599. .bottom-btn-group-wrap {
  600. display: flex;
  601. gap: 12px;
  602. width: 100%;
  603. justify-content: space-between;
  604. .bottom-btn-group {
  605. display: flex;
  606. gap: 12px;
  607. &.justify-center {
  608. justify-content: center;
  609. width: 100%;
  610. }
  611. }
  612. }
  613. &.center {
  614. justify-content: center;
  615. .bottom-btn {
  616. padding: 10px 45px;
  617. }
  618. }
  619. .bottom-btn-divider {
  620. width: calc(100% + 24px);
  621. height: 1px;
  622. background: #f0f0f0;
  623. margin: 10px -12px;
  624. }
  625. .submit-btn {
  626. padding: 10px 83px;
  627. }
  628. }
  629. }
  630. .copy-plan-popup {
  631. width: 100%;
  632. padding: 50px 12px 20px 12px;
  633. &::before {
  634. content: "";
  635. position: absolute;
  636. top: 0;
  637. left: 0;
  638. width: 100%;
  639. height: 136px;
  640. pointer-events: none;
  641. background: url("@/assets/img/monitor/popup-header-bg.png") no-repeat center center / 100% 100%;
  642. }
  643. .copy-plan-content {
  644. display: flex;
  645. align-items: center;
  646. gap: 12px;
  647. .label {
  648. font-size: 16px;
  649. font-weight: 500;
  650. }
  651. .copy-plan-input {
  652. width: calc(100% - 80px);
  653. }
  654. }
  655. .copy-plan-footer {
  656. display: flex;
  657. gap: 12px;
  658. margin-top: 20px;
  659. .btn {
  660. flex: 1;
  661. color: #666666;
  662. border: 1px solid #999999;
  663. border-radius: 25px;
  664. padding: 10px 0;
  665. font-size: 16px;
  666. text-align: center;
  667. &.btn-confirm {
  668. color: #fff;
  669. border: 1px solid #2199f8;
  670. background: #2199f8;
  671. }
  672. }
  673. }
  674. }
  675. .phenology-popup {
  676. padding: 28px 20px 20px;
  677. .phenology-header {
  678. font-size: 24px;
  679. text-align: center;
  680. margin-bottom: 20px;
  681. font-family: "PangMenZhengDao";
  682. }
  683. .phenology-list {
  684. width: 100%;
  685. .phenology-item {
  686. width: 100%;
  687. display: flex;
  688. align-items: center;
  689. justify-content: space-between;
  690. .item-label {
  691. display: flex;
  692. align-items: center;
  693. gap: 4px;
  694. font-size: 15px;
  695. color: rgba(0, 0, 0, 0.4);
  696. .label-text {
  697. color: #000;
  698. font-size: 16px;
  699. font-weight: 500;
  700. }
  701. }
  702. .item-value {
  703. width: calc(100% - 156px);
  704. }
  705. }
  706. .phenology-item + .phenology-item {
  707. margin-top: 10px;
  708. }
  709. }
  710. .phenology-footer-tip {
  711. margin-top: 20px;
  712. text-align: center;
  713. .text{
  714. color: rgba(0, 0, 0, 0.4);
  715. }
  716. }
  717. .phenology-footer {
  718. width: 100%;
  719. text-align: center;
  720. font-size: 16px;
  721. margin-top: 10px;
  722. color: #fff;
  723. background: #2199f8;
  724. border-radius: 25px;
  725. padding: 10px 0;
  726. }
  727. }
  728. </style>