warningDetail.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. <template>
  2. <div class="warning-detail">
  3. <custom-header name="查看详情" :isClose="route.query.miniJson ? true : false"></custom-header>
  4. <div class="article-content" :class="{ 'is-link': isLink }">
  5. <div class="article-header">
  6. <div class="title">{{ warningDetail.title }}</div>
  7. <div class="author-info">
  8. <el-avatar :size="16" :src="warningDetail.icon" />
  9. <span class="author-name">{{ warningDetail.name }}</span>
  10. <span class="qa-date">{{
  11. warningDetail?.createTime && warningDetail?.createTime?.slice(0, 10)
  12. }}</span>
  13. </div>
  14. </div>
  15. <div class="article-image" v-if="showImage">
  16. <img :src="warningDetail?.media && warningDetail?.media[0]" alt="荔枝开花图片" />
  17. </div>
  18. <div class="article-box" v-if="pageParams.questTitle">
  19. <div class="box-top">
  20. <img class="icon" src="@/assets/img/home/ask-icon.png" alt="" />
  21. <div class="title">{{ pageParams.questTitle }}</div>
  22. </div>
  23. <div class="answer-text" v-if="pageParams.answer != null">
  24. <span v-if="pageParams.answer == 0">答:暂未出现{{ pageParams.expectedRisk }}</span>
  25. <template v-else>
  26. <div>答:{{ pageParams.occurrenceTime }}已出现{{ pageParams.expectedRisk }}</div>
  27. <div class="photo-img-wrap">
  28. <photo-provider :photo-closable="true">
  29. <photo-consumer
  30. v-for="(src, index) in pageParams.imagePaths"
  31. intro="风险照片"
  32. :key="index"
  33. :src="base_img_url2 + src"
  34. >
  35. <div class="photo-img">
  36. <img :src="base_img_url2 + src + resize_300" />
  37. </div>
  38. </photo-consumer>
  39. </photo-provider>
  40. </div>
  41. </template>
  42. </div>
  43. <div v-else class="box-bottom" :style="{ justifyContent: !isLink ? 'center' : 'flex-end' }">
  44. <template v-if="!isLink">
  45. <div v-has-permission="'农事规划'" class="edit-btn" @click="handleEditQuestion">编辑问题</div>
  46. </template>
  47. <template v-else>
  48. <div class="edit-btn" @click="handleAnswerClick(0)">否</div>
  49. <div class="edit-btn primary-btn" @click="handleAnswerClick(1)">是</div>
  50. </template>
  51. </div>
  52. </div>
  53. <div class="photo-img-wrap-container" v-if="cropAlbum.length">
  54. <div class="photo-img-wrap-title">
  55. <div class="title-text">
  56. <span>作物相册</span>
  57. <span class="count">({{ cropAlbum.length }})</span>
  58. </div>
  59. <span class="more" @click="handleSeeMore">查看更多</span>
  60. </div>
  61. <div class="photo-img-wrap">
  62. <photo-provider :photo-closable="true">
  63. <photo-consumer
  64. v-for="(src, index) in cropAlbum"
  65. intro="执行照片"
  66. :key="index"
  67. :src="base_img_url2 + src.filename"
  68. >
  69. <div class="photo-img">
  70. <img :src="base_img_url2 + src.filename + resize_300" />
  71. </div>
  72. </photo-consumer>
  73. </photo-provider>
  74. </div>
  75. </div>
  76. <div class="article-text">
  77. <span v-html="warningDetail.content"></span>
  78. </div>
  79. <div class="custom-bottom-fixed-btns" v-if="!isLink">
  80. <div class="bottom-btn primary-btn" @click="showShareSheet = true">转发</div>
  81. </div>
  82. </div>
  83. </div>
  84. <active-upload-popup ref="activeUploadPopupRef" @handleUploadSuccess="handleUploadSuccess"></active-upload-popup>
  85. <fn-share-sheet v-model:show="showShareSheet" :options="shareOptions" @select="handleShareSelect" />
  86. <Popup v-model:show="showQuestionPopup" class="copy-plan-popup" round closeable :close-on-click-overlay="false">
  87. <div class="copy-plan-content">
  88. <div class="label">问题描述</div>
  89. <el-input
  90. v-model="interactionQuestion"
  91. size="large"
  92. placeholder="请输入问题描述"
  93. type="textarea"
  94. :rows="3"
  95. maxlength="35"
  96. show-word-limit
  97. class="copy-plan-input"
  98. />
  99. </div>
  100. <div class="copy-plan-footer">
  101. <div class="btn btn-cancel" @click="showQuestionPopup = false">取消修改</div>
  102. <div class="btn btn-confirm" @click="handleEdit">确认修改</div>
  103. </div>
  104. </Popup>
  105. </template>
  106. <script setup>
  107. import customHeader from "@/components/customHeader.vue";
  108. import { ref, onActivated, computed } from "vue";
  109. import { base_img_url2, resize_300 } from "@/api/config";
  110. import FnShareSheet from "@/components/pageComponents/FnShareSheet.vue";
  111. import activeUploadPopup from "@/components/popup/activeUploadPopup.vue";
  112. import { useRoute, useRouter } from "vue-router";
  113. import { ElMessage } from "element-plus";
  114. import { Popup } from "vant";
  115. import wx from "weixin-js-sdk";
  116. const showQuestionPopup = ref(false);
  117. const handleEdit = () => {
  118. VE_API.monitor
  119. .updateFarmWorkArrange({
  120. id:pageParams.value.arrangeId,
  121. interactionQuestion: interactionQuestion.value,
  122. })
  123. .then((res) => {
  124. if (res.code === 0) {
  125. ElMessage.success("修改成功");
  126. showQuestionPopup.value = false;
  127. getAnswer();
  128. getWarningDetail();
  129. } else {
  130. ElMessage.error(res.message || "修改失败");
  131. }
  132. })
  133. .catch(() => {
  134. ElMessage.error("修改失败,请重试");
  135. })
  136. .finally(() => {
  137. showQuestionPopup.value = false;
  138. });
  139. };
  140. const interactionQuestion = ref("");
  141. const route = useRoute();
  142. const router = useRouter();
  143. const showShareSheet = ref(false);
  144. const shareOptions = ref([{ name: "微信", icon: "wechat", type: "wechat" }]);
  145. const handleShareSelect = () => {
  146. const query = {
  147. askInfo: { title: "提醒客户", content: "是否分享该提醒给好友" },
  148. shareText: warningDetail.value.title,
  149. targetUrl: `warning_detail`,
  150. paramsPage: JSON.stringify(route.query),
  151. imageUrl: warningDetail.value.media[0],
  152. };
  153. wx.miniProgram.navigateTo({
  154. url: `/pages/subPages/share_page/index?pageParams=${JSON.stringify(query)}&type=sharePage`,
  155. });
  156. };
  157. const isLink = ref(true);
  158. const warningDetail = ref({});
  159. const showImage = ref(false);
  160. const pageParams = ref({});
  161. onActivated(() => {
  162. showImage.value = route.query.showImage === "true" ? true : false;
  163. isLink.value = localStorage.getItem("SET_USER_CUR_ROLE") == 2 ? false : true;
  164. // isLink.value = true;
  165. if (route.query.miniJson) {
  166. const miniJson = JSON.parse(route.query.miniJson);
  167. pageParams.value = JSON.parse(miniJson.paramsPage);
  168. } else {
  169. pageParams.value = route.query;
  170. }
  171. getAnswer();
  172. // 获取图片数据
  173. getFarmPhoto();
  174. getWarningDetail();
  175. });
  176. const getAnswer = () => {
  177. VE_API.farm
  178. .getFarmWorkArrangeDetail({
  179. id: pageParams.value.arrangeId,
  180. })
  181. .then((res) => {
  182. if (res.code === 0) {
  183. pageParams.value.answer = res.data.answer;
  184. pageParams.value.expectedRisk = res.data.expectedRisk;
  185. pageParams.value.imagePaths = res.data.imagePaths;
  186. pageParams.value.occurrenceTime = res.data.occurrenceTime;
  187. pageParams.value.questTitle = res.data.interactionQuestion;
  188. }
  189. });
  190. };
  191. const getWarningDetail = () => {
  192. const params = {
  193. id: pageParams.value.id,
  194. };
  195. VE_API.home.warningDetail(params).then((res) => {
  196. warningDetail.value = res.data || {};
  197. });
  198. };
  199. const activeUploadPopupRef = ref(null);
  200. const handleAnswerClick = (value) => {
  201. if (value) {
  202. activeUploadPopupRef.value.showPopup({
  203. gardenIdVal: pageParams.value.farmId,
  204. problemTitleVal: pageParams.value.questTitle,
  205. typeVal: "question",
  206. arrangeIdVal: route.query.arrangeId,
  207. });
  208. } else {
  209. handleAnswer(0);
  210. }
  211. };
  212. const uploadParams = ref({});
  213. const handleUploadSuccess = (data) => {
  214. uploadParams.value = {
  215. occurrenceTime: data.executeDate,
  216. imagePaths: data.imagePaths,
  217. };
  218. handleAnswer(1);
  219. };
  220. const handleAnswer = (value) => {
  221. VE_API.home
  222. .answerQuestion({
  223. ...uploadParams.value,
  224. arrangeId: pageParams.value.arrangeId,
  225. answer: value,
  226. farmId: pageParams.value.farmId,
  227. })
  228. .then((res) => {
  229. if (res.code === 0) {
  230. ElMessage.success("回答成功");
  231. getAnswer();
  232. }
  233. });
  234. };
  235. const cropAlbum = ref([]);
  236. // 获取作物相册的三张照片(参考 patrolPhoto 的获取逻辑)
  237. const getFarmPhoto = async () => {
  238. try {
  239. // 先获取有图片的日期列表
  240. const dateParams = {
  241. farmId: pageParams.value.farmId,
  242. pageIndex: 0,
  243. limit: 10,
  244. };
  245. const { data: dateData } = await VE_API.farm.findHasImagesDate(dateParams);
  246. const dateList = Array.isArray(dateData) ? dateData : [];
  247. if (!dateList.length) {
  248. cropAlbum.value = [];
  249. return;
  250. }
  251. const result = [];
  252. let dateIndex = 0;
  253. // 按日期依次取图片,直到凑够 3 张或没有更多日期
  254. while (result.length < 3 && dateIndex < dateList.length) {
  255. const currentDate = dateList[dateIndex];
  256. const imgParams = {
  257. farmId: pageParams.value.farmId,
  258. date: currentDate,
  259. };
  260. const { data } = await VE_API.farm.getImageInfo(imgParams);
  261. const images = data && Array.isArray(data.images) ? data.images : [];
  262. images.forEach((item) => {
  263. if (result.length < 3) {
  264. // 统一存成 filename 字段,供模板使用
  265. result.push({
  266. ...item,
  267. filename: item.resFilename || item.filename,
  268. });
  269. }
  270. });
  271. dateIndex += 1;
  272. }
  273. cropAlbum.value = result;
  274. } catch (e) {
  275. console.error("获取农场相册失败", e);
  276. cropAlbum.value = [];
  277. }
  278. };
  279. const handleSeeMore = () => {
  280. router.push(`/farm_photo?farmId=${pageParams.value.farmId}`);
  281. };
  282. const handleEditQuestion = () => {
  283. interactionQuestion.value = pageParams.value.questTitle;
  284. showQuestionPopup.value = true;
  285. };
  286. </script>
  287. <style scoped lang="scss">
  288. .warning-detail {
  289. position: relative;
  290. width: 100%;
  291. height: 100vh;
  292. .article-content {
  293. padding: 8px 16px;
  294. overflow-y: auto;
  295. height: calc(100% - 40px - 60px);
  296. box-sizing: border-box;
  297. &.is-link {
  298. height: calc(100% - 40px);
  299. }
  300. .article-header {
  301. .title {
  302. font-size: 18px;
  303. font-weight: bold;
  304. margin-bottom: 6px;
  305. text-align: left;
  306. }
  307. .author-info {
  308. display: flex;
  309. align-items: center;
  310. gap: 10px;
  311. padding-bottom: 12px;
  312. border-bottom: 1px solid #f5f5f5;
  313. .author-name,
  314. .qa-date {
  315. font-size: 14px;
  316. color: #666;
  317. font-weight: normal;
  318. }
  319. }
  320. }
  321. .article-image {
  322. width: 100%;
  323. margin: 12px 0 20px 0;
  324. img {
  325. width: 100%;
  326. height: 175px;
  327. border-radius: 5px;
  328. object-fit: cover;
  329. }
  330. }
  331. .article-text {
  332. ::v-deep {
  333. img {
  334. width: 100%;
  335. height: 175px;
  336. border-radius: 5px;
  337. object-fit: cover;
  338. }
  339. }
  340. }
  341. .photo-img-wrap-container {
  342. padding: 10px;
  343. border-radius: 5px;
  344. border: 1px solid rgba(233, 233, 233, 0.5);
  345. .photo-img-wrap-title {
  346. display: flex;
  347. align-items: center;
  348. justify-content: space-between;
  349. font-size: 16px;
  350. .title-text {
  351. display: flex;
  352. align-items: center;
  353. gap: 4px;
  354. }
  355. .more {
  356. color: rgba(0, 0, 0, 0.6);
  357. font-size: 12px;
  358. }
  359. }
  360. }
  361. .photo-img-wrap {
  362. display: flex;
  363. flex-wrap: wrap;
  364. gap: 10px;
  365. margin-top: 8px;
  366. ::v-deep {
  367. .PhotoConsumer {
  368. width: 31%;
  369. height: 92px;
  370. }
  371. }
  372. .photo-img {
  373. width: 100%;
  374. height: 100%;
  375. position: relative;
  376. box-sizing: border-box;
  377. border: 2px solid transparent;
  378. border-radius: 8px;
  379. overflow: hidden;
  380. img {
  381. width: 100%;
  382. height: 100%;
  383. border-radius: 8px;
  384. object-fit: cover;
  385. }
  386. }
  387. }
  388. .article-box {
  389. margin-bottom: 12px;
  390. border-radius: 8px;
  391. border: 1px solid #2199f8;
  392. padding: 10px;
  393. .box-top {
  394. display: flex;
  395. align-items: center;
  396. gap: 8px;
  397. .icon {
  398. width: 20px;
  399. height: 20px;
  400. }
  401. .title {
  402. font-size: 16px;
  403. font-weight: 600;
  404. }
  405. }
  406. .answer-text {
  407. color: #333333;
  408. margin-top: 6px;
  409. }
  410. .box-bottom {
  411. display: flex;
  412. justify-content: flex-end;
  413. gap: 10px;
  414. .edit-btn {
  415. padding: 7px 20px;
  416. background: rgba(33, 153, 248, 0.1);
  417. color: #2199f8;
  418. border-radius: 25px;
  419. font-size: 12px;
  420. text-align: center;
  421. margin-top: 10px;
  422. &.primary-btn {
  423. background: #2199f8;
  424. color: #fff;
  425. }
  426. }
  427. }
  428. }
  429. }
  430. .custom-bottom-fixed-btns {
  431. justify-content: center;
  432. .primary-btn {
  433. padding: 10px 34px;
  434. }
  435. }
  436. }
  437. .copy-plan-popup {
  438. width: 100%;
  439. padding: 50px 12px 20px 12px;
  440. .copy-plan-content {
  441. display: flex;
  442. gap: 12px;
  443. .label {
  444. font-size: 16px;
  445. font-weight: 500;
  446. }
  447. .copy-plan-input {
  448. width: calc(100% - 80px);
  449. }
  450. }
  451. .copy-plan-footer {
  452. display: flex;
  453. gap: 12px;
  454. margin-top: 20px;
  455. .btn {
  456. flex: 1;
  457. color: #666666;
  458. border: 1px solid #999999;
  459. border-radius: 25px;
  460. padding: 10px 0;
  461. font-size: 16px;
  462. text-align: center;
  463. &.btn-confirm {
  464. color: #fff;
  465. border: 1px solid #2199f8;
  466. background: #2199f8;
  467. }
  468. }
  469. }
  470. }
  471. </style>