index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706
  1. <script setup>
  2. import {
  3. reactive,
  4. ref
  5. } from 'vue';
  6. import {
  7. onLoad,
  8. onUnload,
  9. onPullDownRefresh,
  10. onReachBottom,
  11. onPageScroll
  12. } from "@dcloudio/uni-app";
  13. import {
  14. getProjectInfoList,
  15. getAmtCount
  16. } from "@/api/work/projectInfo.js";
  17. import {
  18. addFocus,
  19. cancelFocus
  20. } from "@/api/work/focus.js";
  21. function backToBefore() {
  22. uni.reLaunch({
  23. url: "/pages/index"
  24. });
  25. };
  26. let title = ref('项目台账')
  27. let scrollTop = ref(0)
  28. let loading = ref(true)
  29. // 统计
  30. let countShow = ref(false);
  31. let firstGetCount = true;
  32. let countList = ref([{
  33. title: "年度计划投资(万元)",
  34. key: "sumYearAmt",
  35. value: 0,
  36. color: ""
  37. }, {
  38. title: "全年占比",
  39. key: "yearRt",
  40. value: 0,
  41. color: "",
  42. isRate: true,
  43. }, {
  44. title: "当月完成金额(万元)",
  45. key: "sumMonthAmtSj",
  46. value: 0,
  47. color: "count-special-value"
  48. }, {
  49. title: "当月占比",
  50. key: "monthRt",
  51. value: 0,
  52. color: "count-special-value",
  53. isRate: true,
  54. }]);
  55. const getAmtCountValue = paramsOrginal => {
  56. getAmtCount(paramsOrginal).then(res => {
  57. firstGetCount = false;
  58. for (let i in countList.value) {
  59. countList.value[i].value = res.data[countList.value[i].key] ?? 0;
  60. }
  61. })
  62. };
  63. let orderBy = reactive({
  64. text: "amt",
  65. amtTotalStatus: null,
  66. yearAmtStatus: null
  67. })
  68. const changeOrderByStatus = type => {
  69. // 改变状态
  70. if (type === "amt") {
  71. orderBy.yearAmtStatus = null;
  72. orderBy.text = "amt";
  73. if (orderBy.amtTotalStatus === "desc") {
  74. orderBy.amtTotalStatus = "asc"
  75. } else if (orderBy.amtTotalStatus === "asc") {
  76. orderBy.amtTotalStatus = null
  77. } else {
  78. orderBy.amtTotalStatus = "desc"
  79. }
  80. } else {
  81. orderBy.amtTotalStatus = null;
  82. orderBy.text = "year";
  83. if (orderBy.yearAmtStatus === "desc") {
  84. orderBy.yearAmtStatus = "asc"
  85. } else if (orderBy.yearAmtStatus === "asc") {
  86. orderBy.yearAmtStatus = null
  87. } else {
  88. orderBy.yearAmtStatus = "desc"
  89. }
  90. }
  91. searchInfo.value.pageNo = 1;
  92. projectList.value = [];
  93. listTotal.value = 0;
  94. moreListFlag = true;
  95. getList()
  96. }
  97. // 参数
  98. let searchInfo = ref({
  99. pageNo: 1,
  100. pageSize: 10,
  101. beginDateStart: null,
  102. beginDateEnd: null,
  103. })
  104. // 触底加载flag
  105. let moreListFlag = true
  106. // 获取列表
  107. let projectList = ref([]);
  108. let listTotal = ref(0);
  109. function getList() {
  110. if (searchInfo.value.pageNo == 1) {
  111. loading.value = true
  112. }
  113. let params = Object.assign({
  114. orderBy: orderBy.text === "amt" ? "amtTotal" : "yearAmt",
  115. orderType: orderBy.text === "amt" ? orderBy.amtTotalStatus : orderBy.yearAmtStatus
  116. }, searchInfo.value)
  117. if (countShow.value && firstGetCount) getAmtCountValue(params);
  118. getProjectInfoList(params).then(res => {
  119. loading.value = false
  120. projectList.value = projectList.value.concat(res.data.list);
  121. listTotal.value = res.data.total;
  122. if (res.data.total == searchInfo.value.pageNo * searchInfo.value.pageSize - (10 - res.data.list
  123. .length)) moreListFlag = false;
  124. }).catch(() => {
  125. loading.value = false
  126. })
  127. }
  128. function goToDetail(id, subName) {
  129. uni.navigateTo({
  130. url: `/pages/projectInfo/detail/index?id=${id}&subName=${subName}`
  131. })
  132. }
  133. function goToPage(url) {
  134. uni.navigateTo({
  135. url
  136. })
  137. }
  138. function goToReport(type, subId, subName) {
  139. if (type === 'wtdb') {
  140. uni.navigateTo({
  141. url: `/pages/problemSupervision/index?type=${type}&subId=${subId}&subName=${subName}`
  142. })
  143. } else if (type === 'qtldjbm') {
  144. uni.navigateTo({
  145. url: `/pages/leadersList/index?type=${type}&subId=${subId}&subName=${subName}`
  146. })
  147. } else if (type === 'xcyx') {
  148. uni.navigateTo({
  149. url: `/pages/projectImageAndVideo/index?type=${type}&subId=${subId}&subName=${subName}`
  150. })
  151. } else if (type === 'more') {
  152. uni.navigateTo({
  153. url: `/pages/projectBtnList/index?type=${type}&subId=${subId}&subName=${subName}`
  154. })
  155. } else {
  156. uni.navigateTo({
  157. url: `/pages/projectInfo/report/index?type=${type}&subId=${subId}&subName=${subName}`
  158. })
  159. }
  160. }
  161. // 折叠/展开
  162. const changeFoldItem = (status, id) => {
  163. let item = projectList.value.find(item => item.id === id);
  164. item.unfold = status;
  165. }
  166. // 收藏/取消
  167. function changeFocus(id, status) {
  168. let item = projectList.value.find(item => item.id === id);
  169. if (status) {
  170. cancelFocus({
  171. subId: id
  172. }).then(res => {
  173. if (res.code === 200) {
  174. item.isAttention = 0;
  175. }
  176. }).catch(() => {
  177. uni.showToast({
  178. title: "更改收藏状态失败。",
  179. icon: "none",
  180. duration: 2000
  181. })
  182. })
  183. } else {
  184. addFocus({
  185. subId: id
  186. }).then(res => {
  187. if (res.code === 200) {
  188. item.isAttention = 1;
  189. }
  190. }).catch(() => {
  191. uni.showToast({
  192. title: "更改收藏状态失败。",
  193. icon: "none",
  194. duration: 2000
  195. })
  196. })
  197. }
  198. }
  199. let seachFalg = ref(true)
  200. function searchClick() {
  201. goToPage('/pages/projectInfo/search/index')
  202. }
  203. onLoad((options) => {
  204. countShow.value = options.type === "home";
  205. if (options.name === "储备项目") countShow.value = false;
  206. if (options?.type == 'home') {
  207. title.value = `项目台账(${options?.name})`
  208. seachFalg.value = false
  209. searchInfo.value = Object.assign(searchInfo.value, options)
  210. searchInfo.value.beginDateStart = `${options?.year}/01/01`;
  211. searchInfo.value.beginDateEnd = `${options?.year}/12/31`;
  212. getList();
  213. } else if (options?.type !== 'all') {
  214. seachFalg.value = false
  215. searchInfo.value = Object.assign(searchInfo.value, options)
  216. searchInfo.value.beginDateStart = `${options?.year}/01/01`;
  217. searchInfo.value.beginDateEnd = `${options?.year}/12/31`;
  218. getList();
  219. } else {
  220. let year = new Date().getFullYear();
  221. searchInfo.value.beginDateStart = `${year}/01/01`;
  222. searchInfo.value.beginDateEnd = `${year}/12/31`;
  223. uni.$on('projectInfoSearch', resolve => {
  224. searchInfo.value = Object.assign(searchInfo.value, resolve);
  225. searchInfo.value.pageNo = 1;
  226. projectList.value = [];
  227. listTotal.value = 0;
  228. moreListFlag = true;
  229. getList();
  230. });
  231. getList();
  232. }
  233. });
  234. onUnload(() => {
  235. uni.$off('projectInfoSearch');
  236. })
  237. onPageScroll((e) => {
  238. scrollTop.value = e.scrollTop
  239. })
  240. onPullDownRefresh(() => {
  241. searchInfo.value.pageNo = 1;
  242. projectList.value = [];
  243. moreListFlag = true;
  244. firstGetCount = true;
  245. try {
  246. getList();
  247. } finally {
  248. uni.stopPullDownRefresh()
  249. }
  250. })
  251. onReachBottom(() => {
  252. if (!moreListFlag) {
  253. return uni.showToast({
  254. title: "已经到底了。",
  255. icon: "none",
  256. duration: 2000
  257. })
  258. }
  259. searchInfo.value.pageNo++;
  260. getList();
  261. })
  262. </script>
  263. <template>
  264. <view class="container">
  265. <page-title @searchClick='searchClick' :showSearch='seachFalg'>{{title}}</page-title>
  266. <view class="cards-list">
  267. <view class="count-value" v-if="countShow">
  268. <view class="count-item" v-for="(item,index) in countList" :key="index">
  269. <view class="count-item-value" :class="item.color">{{item.value}}{{item.isRate ? "%" : ""}}</view>
  270. <view class="count-item-description">{{item.title}}</view>
  271. </view>
  272. </view>
  273. <view class="order-by">
  274. <view class="order-by-item">
  275. 金额
  276. <view class="order-by-icon" :class="orderBy.amtTotalStatus" @click="changeOrderByStatus('amt')"></view>
  277. </view>
  278. <view class="order-by-item">
  279. 年度计划
  280. <view class="order-by-icon" :class="orderBy.yearAmtStatus" @click="changeOrderByStatus('year')"></view>
  281. </view>
  282. </view>
  283. <view v-for="(item,index) in projectList" :key="index">
  284. <!-- 未折叠卡片 -->
  285. <view class="card" v-if="item.unfold">
  286. <!-- 项目名称 -->
  287. <view class="card-item">
  288. <view class="card-item-name">项目名称</view>
  289. <view class="card-item-content">{{item.subName ?? "--"}}</view>
  290. </view>
  291. <!-- 总投资(万元) -->
  292. <view class="card-item">
  293. <view class="card-item-name">总投资(万元)</view>
  294. <view class="card-item-content">{{item.amtTotal ?? "--"}}</view>
  295. </view>
  296. <!-- 年度计划投资-申报单位(万元) -->
  297. <view class="card-item">
  298. <view class="card-item-name">年度计划投资-申报单位(万元)</view>
  299. <view class="card-item-content">{{item.yearAmt ?? "--"}}</view>
  300. </view>
  301. <!-- 已完成投资(万元)-->
  302. <view class="card-item">
  303. <view class="card-item-name">已完成投资(万元)</view>
  304. <view class="card-item-content">{{item.yearAmtSj ?? "--"}}</view>
  305. </view>
  306. <!-- 当前状态 -->
  307. <view class="card-item">
  308. <view class="card-item-name">当前状态</view>
  309. <view class="card-item-content">{{item.status || "--"}}</view>
  310. </view>
  311. <!-- 功能按钮 -->
  312. <view class="card-item">
  313. <!-- 项目查看按钮(特殊) -->
  314. <view class="card-btn fat-btn special-btn" @click="goToDetail(item.id,item.subName)"
  315. v-if="item.usersub == 1">
  316. 项目查看</view>
  317. <!-- 项目查看按钮 -->
  318. <view class="card-btn fat-btn project-btn" @click="goToDetail(item.id,item.subName)" v-else>项目查看
  319. </view>
  320. <!-- 关注按钮 -->
  321. <view class="card-btn fat-btn focus-btn" @click="changeFocus(item.id,item.isAttention)"
  322. v-if="!item.isAttention">关注</view>
  323. <view class="card-btn fat-btn focus-btn-no" @click="changeFocus(item.id,item.isAttention)" v-else>
  324. 取消关注</view>
  325. </view>
  326. <!-- 周月年报按钮 -->
  327. <view class="card-item bottom-item">
  328. <view class="card-btn thin-btn report-btn" @click="goToReport('weekly',item.id,item.subName)">周报
  329. </view>
  330. <view class="card-btn thin-btn report-btn" @click="goToReport('monthly',item.id,item.subName)">月报
  331. </view>
  332. <view class="card-btn thin-btn more-btn" @click="goToReport('more',item.id,item.subName)">更多操作
  333. </view>
  334. </view>
  335. <!-- 编号 -->
  336. <view class="card-fold-option"
  337. :class="item.status_fgw === '2' ? 'card-fold-red' :item.status_fgw === '1' ?'card-fold-yellow':''">
  338. <view class="card-fold-count">{{index + 1}} / {{listTotal}}</view>
  339. <view class="card-fold-center" @click.stop="changeFoldItem(false,item.id)">
  340. <view class="card-fold-btn card-unfold-btn"></view>
  341. </view>
  342. <view class="card-fold-chaos"></view>
  343. </view>
  344. </view>
  345. <!-- 折叠卡片 -->
  346. <view class="card-fold" v-else>
  347. {{item.subName ?? "--"}}
  348. <view class="card-fold-option"
  349. :class="item.status_fgw === '2' ? 'card-fold-red' :item.status_fgw === '1' ?'card-fold-yellow':''">
  350. <view class="card-fold-count">{{index + 1}} / {{listTotal}}</view>
  351. <view class="card-fold-center" @click.stop="changeFoldItem(true, item.id)">
  352. <view class="card-fold-btn"></view>
  353. </view>
  354. <view class="card-fold-chaos"></view>
  355. </view>
  356. </view>
  357. </view>
  358. <empty-show v-if="projectList.length===0"></empty-show>
  359. </view>
  360. <u-back-top :scroll-top="scrollTop"></u-back-top>
  361. <u-loading-page :loading="loading"></u-loading-page>
  362. </view>
  363. </template>
  364. <style lang="scss" scoped>
  365. .count-value {
  366. display: flex;
  367. flex-wrap: wrap;
  368. width: 100%;
  369. height: 200rpx;
  370. margin-bottom: 20rpx;
  371. background: linear-gradient(180deg, #FFFFFF 2%, #A5C6FF 100%);
  372. border-radius: 40rpx 40rpx 40rpx 40rpx;
  373. opacity: 1;
  374. border: 4rpx solid #fff;
  375. .count-item {
  376. display: flex;
  377. flex-direction: column;
  378. justify-content: center;
  379. width: 50%;
  380. height: 50%;
  381. text-align: center;
  382. .count-item-value {
  383. margin-bottom: 8rpx;
  384. font-size: 34rpx;
  385. font-weight: 500;
  386. color: #FF4000;
  387. }
  388. .count-special-value {
  389. color: #330DDF;
  390. }
  391. .count-item-description {
  392. font-size: 24rpx;
  393. color: #343437;
  394. }
  395. }
  396. }
  397. .order-by {
  398. display: flex;
  399. align-items: center;
  400. justify-content: space-between;
  401. width: 100%;
  402. height: 106rpx;
  403. margin-bottom: 34rpx;
  404. padding: 0 40rpx;
  405. border-radius: 40rpx;
  406. background: #FFF;
  407. .order-by-item {
  408. flex: 1;
  409. display: flex;
  410. justify-content: center;
  411. align-items: center;
  412. font-size: 32rpx;
  413. color: #343437;
  414. .order-by-icon {
  415. width: 22rpx;
  416. height: 46rpx;
  417. margin-left: 24rpx;
  418. background-image: url("@/static/orderBy-none.png");
  419. background-size: 100% 100%;
  420. }
  421. .desc {
  422. background-image: url("@/static/orderBy-desc.png");
  423. }
  424. .asc {
  425. background-image: url("@/static/orderBy-asc.png");
  426. }
  427. }
  428. }
  429. .card-box {
  430. padding: 0 36rpx 36rpx 36rpx;
  431. border-radius: 28rpx 28rpx 28rpx 28rpx;
  432. border: 2rpx solid #C2C9D4;
  433. }
  434. .project-btn {
  435. width: 48% !important;
  436. background: #1869F6;
  437. }
  438. .focus-btn {
  439. width: 48% !important;
  440. background-color: #fff;
  441. border-radius: 16rpx 16rpx 16rpx 16rpx;
  442. border: 3rpx solid #1869F6;
  443. color: #1869F6;
  444. }
  445. .focus-btn-no {
  446. width: 48% !important;
  447. background-color: #fff;
  448. border-radius: 16rpx 16rpx 16rpx 16rpx;
  449. border: 3rpx solid #FF2D2D;
  450. color: #FF2D2D;
  451. }
  452. .lamp {
  453. display: flex;
  454. justify-content: center;
  455. align-items: center;
  456. image {
  457. width: 72rpx;
  458. height: 72rpx;
  459. margin-right: 20rpx;
  460. }
  461. text {
  462. font-size: 32rpx;
  463. }
  464. }
  465. .card-box2 {
  466. padding: 0 0 36rpx 0;
  467. border-radius: 28rpx 28rpx 28rpx 28rpx;
  468. }
  469. .focusText {
  470. font-size: 12rpx;
  471. margin-right: 12rpx;
  472. }
  473. .card {
  474. padding-top: 16rpx;
  475. overflow: hidden;
  476. }
  477. .light-item {
  478. margin-bottom: 32rpx;
  479. }
  480. .card-light {
  481. display: flex;
  482. align-items: center;
  483. justify-content: center;
  484. }
  485. .card-light-bottom {
  486. width: 122rpx;
  487. height: 44rpx;
  488. margin: auto 0;
  489. background-size: 100% 100%;
  490. }
  491. .light-red {
  492. background-image: url('@/static/icon-light-red.png');
  493. }
  494. .light-yellow {
  495. background-image: url('@/static/icon-light-yellow.png');
  496. }
  497. .light-green {
  498. background-image: url('@/static/icon-light-green.png');
  499. }
  500. .focus {
  501. width: 46rpx;
  502. height: 40rpx;
  503. background-image: url("@/static/focus-off.png");
  504. background-size: 100% 100%;
  505. }
  506. .focus-on {
  507. background-image: url("@/static/focus-on.png");
  508. }
  509. .more-btn {
  510. background-color: #fff;
  511. color: #1869F6;
  512. font-size: 32rpx;
  513. }
  514. .special-btn {
  515. width: 48% !important;
  516. background: linear-gradient(225deg, #2428F1 0%, #12C8C2 94%);
  517. }
  518. .bottom-item {
  519. display: flex;
  520. flex-wrap: wrap;
  521. justify-content: space-between;
  522. align-content: flex-start;
  523. row-gap: 20rpx;
  524. margin-bottom: 64rpx;
  525. }
  526. .card-value {
  527. position: absolute;
  528. bottom: 0;
  529. right: 50%;
  530. transform: translate(50%);
  531. width: 255rpx;
  532. height: 68rpx;
  533. padding-right: 22rpx;
  534. text-align: center;
  535. line-height: 68rpx;
  536. color: #1869F6;
  537. font-size: 32rpx;
  538. background: linear-gradient(90deg, rgba(211, 227, 255, 0) 0%, rgba(178, 206, 255, 0.5548) 54%, rgba(219, 232, 255, 0) 100%);
  539. }
  540. .card-fold {
  541. position: relative;
  542. width: 100%;
  543. min-height: 152rpx;
  544. margin-bottom: 20rpx;
  545. padding: 24rpx 30rpx 52rpx;
  546. box-sizing: border-box;
  547. background: #FFFFFF;
  548. border-radius: 40rpx;
  549. overflow: hidden;
  550. }
  551. .card-fold-option {
  552. position: absolute;
  553. display: flex;
  554. justify-content: space-between;
  555. align-items: center;
  556. left: 0;
  557. bottom: 0;
  558. width: 100%;
  559. height: 38rpx;
  560. padding: 0 40rpx;
  561. box-sizing: border-box;
  562. background: linear-gradient(270deg, #CADDFF 4%, rgba(219, 232, 255, 0) 100%);
  563. z-index: 999;
  564. .card-fold-count {
  565. flex: 1;
  566. font-size: 28rpx;
  567. color: #1869F6;
  568. }
  569. .card-fold-center {
  570. flex: 1;
  571. .card-fold-btn {
  572. width: 32rpx;
  573. height: 20rpx;
  574. margin: 0 auto;
  575. background-image: url("@/static/icon-fold.png");
  576. background-size: 100% 100%;
  577. }
  578. .card-unfold-btn {
  579. transform: rotate(180deg);
  580. }
  581. }
  582. .card-fold-chaos {
  583. flex: 1;
  584. }
  585. }
  586. .card-fold-red {
  587. background: linear-gradient(270deg, #FF8080 0%, rgba(219, 232, 255, 0) 100%);
  588. .card-fold-count {
  589. color: #FF0000;
  590. }
  591. .card-fold-center {
  592. .card-fold-btn {
  593. background-image: url("@/static/icon-fold-red.png");
  594. }
  595. }
  596. }
  597. .card-fold-yellow {
  598. background: linear-gradient(270deg, #FFAA00 4%, rgba(219, 232, 255, 0) 100%);
  599. .card-fold-count {
  600. color: #E19703;
  601. }
  602. .card-fold-center {
  603. .card-fold-btn {
  604. background-image: url("@/static/icon-fold-yellow.png");
  605. }
  606. }
  607. }
  608. </style>