auditDetail.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. <template>
  2. <view class="page_body">
  3. <!-- 头部 -->
  4. <nav-bar height="96" title="任务详情" :onlyTitle="true" :showHeaderborder="true" @GoBack="GoBack"></nav-bar>
  5. <!-- 吸顶 -->
  6. <uv-sticky style="top:98rpx">
  7. <view class="search_tabs" v-if="state.isSticky">
  8. <scroll-view class="scroll_tabs" scroll-x="true" scroll-left="0" :show-scrollbar="false">
  9. <view :class="['tab_item',{'selected':item.value==state.tabsSelected}]" v-for="item in state.tabList"
  10. :key="item.value" @click="SelectTabsValue(item.value)">
  11. {{item.label}}
  12. </view>
  13. </scroll-view>
  14. </view>
  15. </uv-sticky>
  16. <view class="scroll_view_box">
  17. <scroll-view
  18. class="scroll_view_y"
  19. :scroll-top="state.scrollTop"
  20. scroll-y="true"
  21. :refresher-enabled="false"
  22. :enable-back-to-top="true"
  23. :show-scrollbar="false"
  24. :scroll-with-animation="true"
  25. refresher-default-style="none"
  26. refresher-background="#F8F9FD"
  27. :refresher-triggered="state.isRefreshing"
  28. @scrolltolower="OnLoadMore"
  29. @refresherrefresh="OnRefresh"
  30. @scroll="OnScroll">
  31. <view class="refresh_loading" v-if="state.isRefreshing"><uni-load-more status="loading" /></view>
  32. <view class="page_content padding_btm">
  33. <view class="task_title">{{state?.details?.taskName}}</view>
  34. <view class="task_class_collect">
  35. <text class="class_item">{{state?.details?.departmentName}}</text>
  36. <text class="class_item">{{state?.details?.materialCategoryName}}</text>
  37. <text class="class_item">{{state?.details?.createBy}}</text>
  38. </view>
  39. <view class="task_time">
  40. <view class="time_day">
  41. <text class="time_item">{{state?.details?.startTime}}</text>
  42. <text class="time_item">至</text>
  43. <text class="time_item">{{state?.details?.endTime}}</text>
  44. <text class="time_item" v-if="remainingDays(state?.details?.endTime) > 0">剩余{{remainingDays(state?.details?.endTime)}}天</text>
  45. </view>
  46. <view :class="['task_status', statusClassMap[state?.details?.taskStatus]]">
  47. {{taskStatusMap[state?.details?.taskStatus]}}
  48. </view>
  49. </view>
  50. <view :class="['task_desc',{'over':!state.isExpand}]">
  51. 采集要求:{{state?.details?.collectionRequire}}
  52. </view>
  53. <view class="task_desc_all">
  54. 采集要求:{{state?.details?.collectionRequire}}
  55. </view>
  56. <view class="expand_btn" v-if="state.showExpandBtn" @click="toggleExpand"><text
  57. class="expand_text">{{!state.isExpand?'展开':'收起'}}</text><uni-icons class="expand_icon"
  58. :type="!state.isExpand?'down':'up'"></uni-icons></view>
  59. </view>
  60. <!-- 间隔 -->
  61. <view class="row_gap"></view>
  62. <!-- 吸顶 -->
  63. <!-- <uv-sticky style="top:98rpx"> -->
  64. <view class="search_tabs">
  65. <scroll-view class="scroll_tabs" scroll-x="true" scroll-left="0" :show-scrollbar="false">
  66. <view :class="['tab_item',{'selected':item.value==state.tabsSelected}]" v-for="item in state.tabList"
  67. :key="item.value" @click="SelectTabsValue(item.value)">
  68. {{item.label}}
  69. </view>
  70. </scroll-view>
  71. </view>
  72. <!-- </uv-sticky> -->
  73. <view class="page_content page_content_data">
  74. <view class="total">共 {{state.pageList?.length || 0}} 人</view>
  75. <view class="list_item" v-for="item in state.pageList" :Key="item.id">
  76. <view class="item_name_file">
  77. <view class="name_file">
  78. <text class="name">{{item.targetName}}</text>
  79. <text class="file_num">文件数量:{{item?.taskTargetFileVos?.length || 0}}</text>
  80. </view>
  81. <view class="btn" @click="handleAudit(item)">{{state.tabsSelected>0?'查看':'审核'}}</view>
  82. </view>
  83. <view class="item_info">
  84. <text class="info_text">{{item.showInfo}}</text>
  85. <text class="info_text">{{item?.taskTargetFileVos?.[item.taskTargetFileVos.length -1]?.createTime}}</text>
  86. </view>
  87. </view>
  88. <view class="no_data" v-if="!state.isLoading && state.pageList.length == 0">
  89. <image class="no_data_img" src="@/static/image/bg/no_data.png"></image>
  90. <text class="no_data_text">暂无数据</text>
  91. </view>
  92. </view>
  93. <uni-load-more v-if="state.pageList.length > 0" :status="state.loadStatus" />
  94. </scroll-view>
  95. </view>
  96. </view>
  97. </template>
  98. <script setup>
  99. import uniIcons from '@/uni_modules/uni-icons/components/uni-icons/uni-icons.vue';
  100. import uvSticky from '@/uni_modules/uv-sticky/components/uv-sticky/uv-sticky.vue'; //吸顶
  101. import navBar from '@/components/navBar.vue';
  102. import { remainingDays } from '@/common/common.js';
  103. import {getReviewTaskList} from '@/reqApi/notification.js';
  104. import {
  105. onLoad,
  106. onShow
  107. } from '@dcloudio/uni-app';
  108. import {
  109. reactive,
  110. ref,
  111. nextTick,
  112. getCurrentInstance,
  113. onMounted,
  114. onUnmounted
  115. } from 'vue';
  116. const state = reactive({
  117. taskId: '', //任务id
  118. tabsSelected: 0, //默认选中tab值
  119. tabList: [{
  120. label: '待审核',
  121. value: 0
  122. }, {
  123. label: '已审核',
  124. value: 1
  125. }, {
  126. label: '已驳回',
  127. value: 2
  128. }], //tab列表
  129. showExpandBtn: false, //是否显示展开按钮
  130. isExpand: false, //是否展开
  131. pageSize: 50,
  132. pageNum: 1,
  133. scrollTop: 0,
  134. oldScrollTop: 0,
  135. isRefreshing: false, //设置当前下拉刷新状态,true 表示下拉刷新已经被触发,false 表示下拉刷新未被触发
  136. isLoading: true, //加载中
  137. noMoreData: false, //没有更多数据了
  138. loadStatus: 'more', //loading状态
  139. details: {}, //详情信息
  140. pageList: [], //列表数据
  141. searchBoxTop:0,//搜索框距离顶部的高度
  142. isSticky:false,//是否吸顶
  143. });
  144. const statusClassMap = {
  145. 1: 'status_published',
  146. 2: 'status_closed'
  147. };
  148. const taskStatusMap = {
  149. 1: '进行中',
  150. 2: '已结束'
  151. };
  152. onShow(()=>{
  153. const isLoadReviewMaterialDetailList = uni.getStorageSync('isLoadReviewMaterialDetailList');
  154. if(isLoadReviewMaterialDetailList){//审核驳回通过返回刷新列表
  155. OnLoadData(true);
  156. }
  157. })
  158. onLoad((option) => {
  159. state.taskId = option.taskId;
  160. })
  161. onMounted(() => {
  162. state.details = uni.getStorageSync('collectReviewDetailInfo') || {};
  163. OnLoadData(true,true); // 数据加载完成后触发 checkTextOverflow
  164. });
  165. onUnmounted(() => {
  166. // uni.removeStorageSync('isLoadReviewMaterialDetailList');//移除缓存
  167. uni.removeStorageSync('collectReviewDetailInfo');//移除缓存
  168. })
  169. const checkTextOverflow = () => {
  170. nextTick(() => {
  171. const instance = getCurrentInstance();
  172. // 安全保护
  173. if (!instance || !instance.proxy) {
  174. // 备用方案:全局查询(要求元素 class 唯一)
  175. let taskDescAllHeight = 0,
  176. taskDescHeight = 0;
  177. const query = uni.createSelectorQuery();
  178. query.select('.task_desc_all').boundingClientRect((rect) => {
  179. taskDescAllHeight = rect?.height || 0;
  180. });
  181. query.select('.task_desc').boundingClientRect((rect) => {
  182. taskDescHeight = rect?.height || 0;
  183. });
  184. query.select('.page_content_data').boundingClientRect((rect) => {
  185. state.searchBoxTop = rect?.top || 0;
  186. });
  187. query.exec(() => {
  188. // 所有回调执行完毕后触发
  189. state.showExpandBtn = taskDescAllHeight > taskDescHeight + 1;
  190. });
  191. return;
  192. }
  193. let taskDescAllHeight = 0,
  194. taskDescHeight = 0;
  195. const query = uni.createSelectorQuery().in(instance.proxy);
  196. query.select('.task_desc_all').boundingClientRect((rect) => {
  197. taskDescAllHeight = rect?.height || 0;
  198. });
  199. query.select('.task_desc').boundingClientRect((rect) => {
  200. taskDescHeight = rect?.height || 0;
  201. });
  202. query.select('.page_content_data').boundingClientRect((rect) => {
  203. state.searchBoxTop = rect?.top || 0;
  204. });
  205. query.exec(() => {
  206. // 所有回调执行完毕后触发
  207. state.showExpandBtn = taskDescAllHeight > taskDescHeight + 1;
  208. });
  209. });
  210. }
  211. // 展开收起切换
  212. const toggleExpand = () => {
  213. state.isExpand = !state.isExpand;
  214. }
  215. //切换tab
  216. const SelectTabsValue = (val) => {
  217. state.tabsSelected = val;
  218. OnLoadData(true);
  219. }
  220. //搜索
  221. const HandleSearchInput = (val) => {
  222. state.searchWord = val;
  223. OnLoadData(true);
  224. }
  225. const ToSafeArr = (val) => {
  226. if (Array.isArray(val)) return val
  227. return val ? [val] : []
  228. }
  229. /*
  230. *详情
  231. * initPage:初始分页 从第一开始
  232. * initData:初始加载
  233. */
  234. const OnLoadData = (initPage,initData = false) => {
  235. // uni.showLoading({
  236. // title: '加载中'
  237. // });
  238. if (initPage) state.pageNum = 1;//如果是下拉刷新、重新查询 默认加载第一页
  239. state.isLoading = true;
  240. state.loadStatus = 'loading';
  241. getReviewTaskList({
  242. taskId: state.taskId,
  243. reviewStatus: state.tabsSelected,
  244. pageNum: state.pageNum,
  245. pageSize: state.pageSize
  246. }).then(res=>{
  247. if(res.code == 200){
  248. const total = Number(res?.data?.reviewInfoVoPageResult?.total) || 0;//总数
  249. const pages = Math.ceil(total / state.pageSize);//总页数
  250. const recordsData = res?.data?.reviewInfoVoPageResult?.records || [];
  251. state.pageList = initPage ? recordsData : [...state.pageList,...recordsData];
  252. state.noMoreData = state.pageNum == pages;
  253. state.pageNum++;
  254. if(initData){
  255. checkTextOverflow();
  256. }
  257. }
  258. }).finally(()=>{
  259. // uni.hideLoading();
  260. state.isLoading = false;
  261. state.isRefreshing = false;//下拉刷新未被触发
  262. if(state.noMoreData){
  263. state.loadStatus = 'no-more';
  264. }else{
  265. state.loadStatus = 'more';
  266. }
  267. //返回到页面顶部
  268. if(initPage){
  269. GoTop();
  270. }
  271. })
  272. }
  273. // 自定义下拉刷新被触发 下拉刷新
  274. const OnRefresh = () => {
  275. state.isRefreshing = true; //下拉刷新已经被触发
  276. OnLoadData(true);
  277. }
  278. //滚动到底部/右边 触发上拉刷新
  279. const OnLoadMore = () => {
  280. if (state.isLoading || state.noMoreData) return;
  281. OnLoadData(false);
  282. }
  283. // 滚动时触发
  284. const OnScroll = (e) => {
  285. const scrollTop = e.detail.scrollTop;
  286. if(scrollTop > state.searchBoxTop){
  287. state.isSticky = true;
  288. }else{
  289. state.isSticky = false;
  290. }
  291. state.oldScrollTop = scrollTop;
  292. }
  293. //返回到顶部
  294. const GoTop = () => {
  295. // 解决view层不同步的问题
  296. state.scrollTop = state.oldScrollTop;
  297. nextTick(() => {
  298. state.scrollTop = 0
  299. });
  300. }
  301. //去审核
  302. const handleAudit = (item) => {
  303. uni.setStorageSync('reviewMaterialDetailData', item);//详情数据
  304. uni.navigateTo({
  305. url: `/pages/materialCollection/reviewMaterial/auditResult?taskStatus=${state?.details.taskStatus}`
  306. });
  307. };
  308. //返回
  309. const GoBack = () => {
  310. uni.navigateBack({
  311. delta: 1
  312. });
  313. }
  314. </script>
  315. <style scoped lang="scss">
  316. .page_body{
  317. height: 100%;
  318. min-height: auto;
  319. .scroll_view_box{
  320. width: 100%;
  321. display: flex;
  322. flex-direction: column;
  323. height: calc(100% - 96rpx);
  324. }
  325. .scroll_view_y {
  326. height: 100%;
  327. .refresh_loading {
  328. padding: 10rpx 0;
  329. }
  330. }
  331. }
  332. .page_content {
  333. &.padding_btm {
  334. padding-bottom: 24rpx;
  335. .task_title {
  336. font-weight: 500;
  337. font-size: 32rpx;
  338. color: #333333;
  339. margin-top: 24rpx;
  340. }
  341. .task_class_collect {
  342. display: inline-flex;
  343. align-items: center;
  344. flex-wrap: wrap;
  345. width: 100%;
  346. margin-top: 16rpx;
  347. gap: 16rpx;
  348. .class_item {
  349. font-weight: 400;
  350. font-size: 24rpx;
  351. color: #999999;
  352. }
  353. }
  354. .task_time {
  355. display: flex;
  356. align-items: flex-start;
  357. width: 100%;
  358. margin-top: 16rpx;
  359. .time_day {
  360. display: inline-flex;
  361. align-items: center;
  362. flex-wrap: wrap;
  363. flex: 1;
  364. .time_item {
  365. font-weight: 400;
  366. font-size: 24rpx;
  367. line-height: 48rpx;
  368. color: #999999;
  369. &:nth-child(2) {
  370. margin: 0 8rpx;
  371. }
  372. &:nth-child(4) {
  373. color: #2E64FA;
  374. margin-left: 16rpx;
  375. }
  376. }
  377. }
  378. .task_status {
  379. display: inline-flex;
  380. align-items: center;
  381. height: 48rpx;
  382. padding: 0 12rpx;
  383. border-radius: 8rpx;
  384. font-weight: 400;
  385. font-size: 24rpx;
  386. &.status_pending {
  387. background: rgba(46, 100, 250, 0.1);
  388. color: #2E64FA;
  389. }
  390. &.status_published {
  391. background: rgba(82, 196, 26, 0.1);
  392. color: #2BC644;
  393. }
  394. &.status_closed {
  395. background: #6666661a;
  396. color: #666666;
  397. }
  398. }
  399. }
  400. .task_desc {
  401. margin-top: 24rpx;
  402. display: flex;
  403. width: 100%;
  404. padding-top: 24rpx;
  405. border-top: 2rpx solid #F3F3F3;
  406. font-weight: 400;
  407. font-size: 28rpx;
  408. color: #999999;
  409. line-height: 44rpx;
  410. box-sizing: border-box;
  411. position: relative;
  412. &.over {
  413. max-height: 168rpx;
  414. overflow: hidden;
  415. transition: all 0.25s;
  416. display: -webkit-box;
  417. -webkit-line-clamp: 3;
  418. -webkit-box-orient: vertical;
  419. }
  420. }
  421. .task_desc_all {
  422. display: flex;
  423. width: 100%;
  424. padding-top: 24rpx;
  425. border-top: 2rpx solid #F3F3F3;
  426. font-weight: 400;
  427. font-size: 28rpx;
  428. color: #999999;
  429. line-height: 44rpx;
  430. box-sizing: border-box;
  431. position: absolute;
  432. left: -9999px;
  433. top: 0;
  434. visibility: hidden; // 不可见但占据空间(不影响布局,因为absolute)
  435. pointer-events: none;
  436. }
  437. .expand_btn {
  438. width: 100%;
  439. height: 40rpx;
  440. background-color: #FFFFFF;
  441. display: flex;
  442. justify-content: flex-end;
  443. align-items: center;
  444. .expand_text {
  445. font-weight: 400;
  446. font-size: 28rpx;
  447. color: #2E64FA;
  448. margin-right: 8rpx;
  449. }
  450. .expand_icon {
  451. font-size: 32rpx !important;
  452. color: #2E64FA !important;
  453. }
  454. }
  455. }
  456. }
  457. .row_gap {
  458. background-color: #F8F9FD;
  459. width: 100%;
  460. height: 24rpx;
  461. }
  462. :deep(.search_tabs){
  463. display: flex;
  464. width: 100%;
  465. flex-direction: column;
  466. background-color: #FFFFFF;
  467. padding: 24rpx;
  468. border-bottom: 2rpx solid #F3F3F3;
  469. }
  470. .scroll_tabs {
  471. margin-top:0;
  472. padding: 0;
  473. background-color: #FFFFFF;
  474. }
  475. .total {
  476. width: 100%;
  477. display: flex;
  478. font-weight: 400;
  479. font-size: 28rpx;
  480. color: #333333;
  481. padding: 24rpx 0;
  482. border-bottom: 2rpx solid #F3F3F3;
  483. }
  484. .list_item {
  485. display: flex;
  486. flex-direction: column;
  487. width: 100%;
  488. padding: 24rpx 0;
  489. border-bottom: 2rpx solid #F3F3F3;
  490. .item_name_file{
  491. display: flex;
  492. width: 100%;
  493. align-items: center;
  494. gap: 20rpx;
  495. .name_file{
  496. flex:1;
  497. gap: 20rpx;
  498. display: flex;
  499. align-items: center;
  500. .name{
  501. font-weight: 500;
  502. font-size: 32rpx;
  503. color: #333333;
  504. }
  505. .file_num{
  506. font-weight: 400;
  507. font-size: 24rpx;
  508. color: #2E64FA;
  509. }
  510. }
  511. .btn{
  512. width: 104rpx;
  513. height: 64rpx;
  514. border-radius: 8rpx;
  515. border: 2rpx solid #2E64FA;
  516. font-weight: 400;
  517. font-size: 28rpx;
  518. color: #2E64FA;
  519. display: inline-flex;
  520. align-items: center;
  521. justify-content: center;
  522. }
  523. }
  524. .item_info{
  525. margin-top: 16rpx;
  526. display: flex;
  527. gap: 16rpx;
  528. align-items: center;
  529. flex-wrap: wrap;
  530. .info_text{
  531. font-weight: 400;
  532. font-size: 24rpx;
  533. color: #999999;
  534. flex-shrink: 0;
  535. }
  536. }
  537. }
  538. .no_data{
  539. margin-top: 24rpx;
  540. }
  541. </style>