farmDynamics.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. <template>
  2. <div class="farm-dynamics">
  3. <tab-list v-model="activePlanIndex" :tabs="filterType" @change="handlePlanClick" />
  4. <div class="task-content">
  5. <div class="expert-content">
  6. <empty
  7. v-if="contentData.length === 0"
  8. image="https://birdseye-img.sysuimars.com/birdseye-look-mini/custom-empty-image.png"
  9. image-size="80"
  10. description="暂无数据"
  11. class="empty-state"
  12. />
  13. <div v-for="(section, index) in contentData" :key="index" class="content-section">
  14. <div class="section-id" :id="section.targetId"></div>
  15. <record-item
  16. :record-item-data="section"
  17. title-mode="default"
  18. onlyRecipeName
  19. titleRightDotText="全区"
  20. titleRightType="dot"
  21. class="recipe-item"
  22. :showFarmImage="activePlanIndex == 5 ? true : false"
  23. :content-mode="activePlanIndex == 5 ? 'serviceDetail' : ''"
  24. @click="handleClick(section)"
  25. >
  26. <template #footer>
  27. <div class="action-group">
  28. <div class="action-l">查看详情</div>
  29. <div class="action-r" v-if="section.farmWorkDetail?.flowStatus === 0 && !section.isPublic">
  30. <div class="action-item warning-item" @click.stop="handleApply(section)">
  31. 发起需求
  32. </div>
  33. <div class="action-item primary-item" @click.stop="handleConfirmComplete(section)">确认并完成</div>
  34. </div>
  35. <div class="action-r" v-else-if="section.farmWorkDetail?.flowStatus === 0 && section.isPublic">
  36. <div
  37. class="action-item second-item"
  38. @click.stop="handleOperation(section, 'cancel')"
  39. >
  40. 取消发起
  41. </div>
  42. </div>
  43. <div class="action-r" v-else-if="section.farmWorkDetail?.flowStatus === 4">
  44. <div class="action-item warning-item has-applied">已锁单</div>
  45. <div
  46. v-if="section.executeEvidence?.length"
  47. class="action-item primary-item"
  48. @click.stop="handleOperation(section, 'confirmComplete')"
  49. >
  50. 确认执行完成
  51. </div>
  52. <div
  53. v-else
  54. class="action-item primary-item"
  55. @click.stop="handleApply(section, 'remindExecute')"
  56. >
  57. 提醒对方执行
  58. </div>
  59. </div>
  60. <div class="action-r" v-else-if="activePlanIndex == 5">
  61. <!-- <div
  62. class="action-item warning-item"
  63. :class="{ 'has-applied': section.hasApplied }"
  64. @click="handleApply(section, index)"
  65. >
  66. {{ section.hasApplied ? "已发起需求" : "发起需求" }}
  67. </div> -->
  68. <div
  69. class="action-item primary-item"
  70. v-if="!section.farmWorkDetail?.reviewImage?.length"
  71. @click.stop="handleUploadPhoto(section)"
  72. >
  73. 上传照片
  74. </div>
  75. </div>
  76. </div>
  77. </template>
  78. </record-item>
  79. </div>
  80. </div>
  81. </div>
  82. </div>
  83. <!-- 需求发送成功弹窗 -->
  84. <tip-popup v-model:show="showApplyPopup" type="success" text="需求发送成功" />
  85. <!-- 发起需求成功弹窗 -->
  86. <fn-share-sheet :class="type === 'manage' ? '' : 'share-sheet'" v-model:show="showShare" @select="onSelect" :options="options" />
  87. <!-- 提醒对方执行弹窗 -->
  88. <upload-execute ref="uploadExecuteRef" onlyShare />
  89. <!-- 上传照片弹窗 -->
  90. <review-upload-popup v-model="showUpload" :record-id="sectionId" @success="getContentData" />
  91. </template>
  92. <script setup>
  93. import { ref, onActivated ,onMounted} from "vue";
  94. import recordItem from "@/components/recordItem.vue";
  95. import tabList from "@/components/pageComponents/TabList.vue";
  96. import { Popup, Empty } from "vant";
  97. import { useRouter } from "vue-router";
  98. import wx from "weixin-js-sdk";
  99. import uploadExecute from "@/views/old_mini/task_condition/components/uploadExecute.vue";
  100. import tipPopup from "@/components/popup/tipPopup.vue";
  101. import FnShareSheet from "@/components/pageComponents/FnShareSheet.vue";
  102. import { ElMessageBox, ElMessage } from "element-plus";
  103. import { base_img_url2 } from "@/api/config";
  104. import reviewUploadPopup from "@/components/popup/reviewUploadPopup.vue";
  105. const router = useRouter();
  106. const props = defineProps({
  107. type: {
  108. type: String,
  109. default: "",
  110. },
  111. active: {
  112. type: Number,
  113. default: 0,
  114. },
  115. });
  116. const handleConfirmComplete = (section) => {
  117. ElMessage.warning("该功能正在升级中,敬请期待");
  118. };
  119. const showApplyPopup = ref(false);
  120. const uploadExecuteRef = ref(null);
  121. const showShare = ref(false);
  122. const options = ref([
  123. {
  124. name: "需求大厅",
  125. icon: "https://birdseye-img.sysuimars.com/birdseye-look-mini/xuqiu-icon.png",
  126. type: "demandHall",
  127. },
  128. { name: "微信", icon: "wechat", type: "wechat" },
  129. ]);
  130. // 获取触发图片
  131. const triggerImg = ref([]);
  132. const getTriggerImg = async () => {
  133. const { data } = await VE_API.z_farm_work_record.getTriggerImg({ farmWorkRecordId: currentSection.value.id });
  134. triggerImg.value = data || [];
  135. }
  136. const onSelect = async (option) => {
  137. if (option.type === "wechat") {
  138. await getTriggerImg();
  139. const query = {
  140. askInfo: { title: "农事需求", content: "是否分享该农事需求给好友" },
  141. shareText: `发起了 ${currentSection.value.farmWorkName} 的接单需求 请查看!`,
  142. id: currentSection.value.id,
  143. farmWorkOrderId: currentSection.value.orderId,
  144. postImg: triggerImg.value.length ? base_img_url2 + triggerImg.value[triggerImg.value.length - 1].cloudFilename : ''
  145. };
  146. wx.miniProgram.navigateTo({
  147. url: `/pages/subPages/share_page/index?pageParams=${JSON.stringify(query)}&type=priceSheet`,
  148. });
  149. } else {
  150. updatePublicStatus(1);
  151. }
  152. };
  153. const updatePublicStatus = (isPublic) => {
  154. return VE_API.z_farm_work_record.updatePublicStatus({ recordId: currentSection.value.id, isPublic }).then((res) => {
  155. if (res.code === 0) {
  156. getContentData();
  157. }
  158. });
  159. };
  160. const updateFlowStatus = (targetFlowStatus) => {
  161. return VE_API.z_farm_work_record.updateFlowStatus({ id: currentSection.value.id, targetFlowStatus }).then((res) => {
  162. if (res.code === 0) {
  163. if (targetFlowStatus === 2) {
  164. showApplyPopup.value = true;
  165. }
  166. getContentData();
  167. }
  168. });
  169. };
  170. // 取消发起需求/确认执行完成
  171. const handleOperation = (section, type) => {
  172. currentSection.value = section;
  173. ElMessageBox.confirm(type === "cancel" ? "确认取消发起需求吗?" : "确认执行完成吗?", "提示", {
  174. confirmButtonText: "确认",
  175. cancelButtonText: "取消",
  176. type: "warning",
  177. })
  178. .then(() => {
  179. if (type === "cancel") {
  180. updatePublicStatus(0);
  181. }else{
  182. updateFlowStatus(5);
  183. }
  184. })
  185. .catch(() => {});
  186. };
  187. const showUpload = ref(false);
  188. const sectionId = ref(null);
  189. // 上传照片处理函数
  190. const handleUploadPhoto = ({ id }) => {
  191. showUpload.value = true;
  192. sectionId.value = id;
  193. };
  194. const filterType = ref([
  195. {
  196. title: "待确认",
  197. value: '0',
  198. },
  199. {
  200. title: "待完成",
  201. value: 4,
  202. },
  203. {
  204. title: "已完成",
  205. value: 5,
  206. },
  207. ]);
  208. const activePlanIndex = ref('0');
  209. const handlePlanClick = (value) => {
  210. activePlanIndex.value = value;
  211. getContentData();
  212. };
  213. onMounted(() => {
  214. getContentData();
  215. });
  216. onActivated(() => {
  217. if(props.active == 0){
  218. getContentData();
  219. }
  220. });
  221. const contentData = ref([]);
  222. const getContentData = async () => {
  223. const res = await VE_API.z_farm_work_record.getSimpleList({
  224. role: localStorage.getItem("SET_USER_CUR_ROLE"),
  225. flowStatus:activePlanIndex.value,
  226. });
  227. contentData.value = res.data;
  228. // 遍历数组,获取每一项的详情并合并
  229. if (res.data && res.data.length > 0) {
  230. const detailPromises = res.data.map((item) => getItemDetail(item.id));
  231. const detailResults = await Promise.all(detailPromises);
  232. // 将详情数据合并到对应的数组项中
  233. contentData.value = res.data.map((item, index) => {
  234. return {
  235. ...item,
  236. farmWorkDetail: detailResults[index][0],
  237. prescriptionList: detailResults[index][0]?.prescriptionList,
  238. reviewImage: detailResults[index][0]?.reviewImage,
  239. };
  240. });
  241. }
  242. };
  243. defineExpose({
  244. getContentData,
  245. });
  246. async function getItemDetail(id) {
  247. const { data } = await VE_API.z_farm_work_record.getDetail({ id });
  248. return data || {};
  249. }
  250. const currentSection = ref({});
  251. const handleApply = (section, type) => {
  252. currentSection.value = section;
  253. if (type === "remindExecute") {
  254. const params = {
  255. id: section.id,
  256. farmMiniUserId: section.farmWorkDetail?.users[0]?.userId,
  257. farmWorkOrderId: section?.orderId,
  258. farmId: section?.farmId,
  259. farmWorkName: section?.farmWorkName,
  260. type: "remindExecute",
  261. };
  262. uploadExecuteRef.value.showPopup(params);
  263. } else {
  264. showShare.value = true;
  265. }
  266. };
  267. const handleClick = (section) => {
  268. if (activePlanIndex.value == 5) {
  269. router.push(`/review_work?json=${JSON.stringify({ id: section.id, goBack: true })}`);
  270. } else {
  271. router.push({
  272. path: "/completed_work",
  273. query: { json: JSON.stringify({ id: section.id }) },
  274. });
  275. }
  276. };
  277. </script>
  278. <style lang="scss" scoped>
  279. .farm-dynamics {
  280. width: 100%;
  281. height: 100vh;
  282. background: #f5f7fb;
  283. .task-content {
  284. display: flex;
  285. height: calc(100% - 140px);
  286. .expert-content {
  287. width: 100%;
  288. height: 100%;
  289. overflow: auto;
  290. padding: 10px;
  291. box-sizing: border-box;
  292. .empty-state {
  293. ::v-deep .van-empty {
  294. padding: 40px 0;
  295. }
  296. }
  297. .content-section {
  298. position: relative;
  299. .section-id {
  300. position: absolute;
  301. top: 0;
  302. width: 100%;
  303. height: 1px;
  304. }
  305. .recipe-item {
  306. border: 1px solid rgba(0, 0, 0, 0.1);
  307. }
  308. }
  309. .box-title {
  310. display: flex;
  311. align-items: center;
  312. justify-content: space-between;
  313. padding-bottom: 8px;
  314. .title-l {
  315. font-size: 16px;
  316. font-weight: 600;
  317. color: #000;
  318. .parent-text {
  319. margin-left: 5px;
  320. font-size: 12px;
  321. font-weight: normal;
  322. padding: 4px 6px;
  323. border-radius: 14px;
  324. background: rgba(119, 119, 119, 0.1);
  325. }
  326. }
  327. .title-btn {
  328. width: 24px;
  329. height: 24px;
  330. border-radius: 50%;
  331. background: #2199f8;
  332. display: flex;
  333. align-items: center;
  334. justify-content: center;
  335. }
  336. .title-r {
  337. display: flex;
  338. align-items: center;
  339. color: #393939;
  340. font-size: 12px;
  341. .r-dot {
  342. width: 6px;
  343. height: 6px;
  344. border-radius: 50%;
  345. background: #393939;
  346. margin-right: 5px;
  347. }
  348. }
  349. }
  350. .content-info {
  351. padding-top: 8px;
  352. .info-line {
  353. font-size: 12px;
  354. color: #bbbbbb;
  355. margin-bottom: 8px;
  356. line-height: 1.4;
  357. .info-val {
  358. color: #666666;
  359. }
  360. }
  361. .review-title {
  362. .info-val {
  363. margin-top: 5px;
  364. }
  365. }
  366. .review-image {
  367. display: flex;
  368. align-items: center;
  369. gap: 8px;
  370. img {
  371. flex: 1;
  372. height: 82px;
  373. object-fit: cover;
  374. border-radius: 4px;
  375. }
  376. }
  377. .reminder-box {
  378. background: linear-gradient(90deg, #d9ebfc, transparent);
  379. border-radius: 4px;
  380. padding: 6px 8px;
  381. font-size: 12px;
  382. color: #2e2e2e;
  383. line-height: 1.4;
  384. margin-top: 8px;
  385. .highlight-number {
  386. color: #2199f8;
  387. font-weight: 500;
  388. }
  389. }
  390. }
  391. .action-r {
  392. color: #1d2129;
  393. }
  394. .action-group {
  395. display: flex;
  396. align-items: center;
  397. justify-content: space-between;
  398. padding-top: 8px;
  399. margin-top: 8px;
  400. border-top: 1px solid #f5f5f5;
  401. .action-l {
  402. font-size: 12px;
  403. }
  404. }
  405. .action-group {
  406. .action-r {
  407. display: flex;
  408. align-items: center;
  409. .action-item {
  410. padding: 0 11px;
  411. height: 30px;
  412. line-height: 30px;
  413. border-radius: 20px;
  414. font-size: 12px;
  415. &.second-item {
  416. background: rgba(137, 137, 137, 0.1);
  417. color: #898989;
  418. }
  419. &.primary-item {
  420. background: #2199f8;
  421. color: #fff;
  422. }
  423. &.warning-item {
  424. background: rgba(255, 131, 29, 0.1);
  425. color: #ff831d;
  426. &.has-applied {
  427. background: transparent;
  428. color: #afafaf;
  429. }
  430. }
  431. &.cancel-item {
  432. color: #676767;
  433. border: 1px solid rgba(103, 103, 103, 0.2);
  434. }
  435. }
  436. .action-item + .action-item {
  437. margin-left: 5px;
  438. }
  439. }
  440. .apply-action {
  441. justify-content: flex-end;
  442. .default-item {
  443. border: 1px solid rgba(0, 0, 0, 0.4);
  444. color: rgba(0, 0, 0, 0.4);
  445. }
  446. }
  447. }
  448. }
  449. }
  450. }
  451. </style>