index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. <template>
  2. <hc-sys id="app-sys" class="h-100vh hc-task-page" :isNavBar="false">
  3. <view id="hc-task-nav-bar" class="hc-task-nav-bar">
  4. <view class="task-nav-bar">
  5. <view class="segmented-bar">
  6. <template v-for="item in taskTypeData">
  7. <view class="task-tab-item"
  8. :class="item.key === taskType?'task-cur':''"
  9. @click="taskTypeChange(item)"
  10. >{{item.name}}</view>
  11. </template>
  12. </view>
  13. <view class="more-bar" v-if="taskType === 1 && taskList.length > 0" @click="moreBarClick">
  14. <text class="i-ri-more-2-fill icon"/>
  15. </view>
  16. </view>
  17. <view class="controls-bar-box">
  18. <view class="controls-bar">
  19. <view class="left">
  20. <text class="i-ri-filter-fill icon" :class="isControlsFilter?'cur':''" @click="dateFilterClick"/>
  21. <text class="i-ri-filter-off-fill icon" @click="dateFilterClear"/>
  22. </view>
  23. <view class="right">
  24. <text class="i-ri-sort-desc icon" :class="searchForm.ordType === 1?'cur':''" @click="changeOrdType(1)"/>
  25. <text class="i-ri-sort-asc icon" :class="searchForm.ordType === 2?'cur':''" @click="changeOrdType(2)"/>
  26. </view>
  27. </view>
  28. <view class="controls-filter" v-if="isControlsFilter">
  29. <view class="search-form-date">
  30. <picker class="search-date-input" mode="date" :value="searchForm.startTime" @change="startDateChange">
  31. <view class="content">
  32. <view class="text">{{searchForm.startTime??'点此选择开始日期'}}</view>
  33. <text class="i-ri-close-circle-line icon" v-if="searchForm.startTime" @click.stop="startDateClear"/>
  34. </view>
  35. </picker>
  36. </view>
  37. <view class="search-form-date">
  38. <picker class="search-date-input" mode="date" :value="searchForm.endTime" @change="endDateChange">
  39. <view class="content">
  40. <view class="text">{{searchForm.endTime??'点此选择结束日期'}}</view>
  41. <text class="i-ri-close-circle-line icon" v-if="searchForm.endTime" @click.stop="endDateClear"/>
  42. </view>
  43. </picker>
  44. </view>
  45. <button class="search-form-btn" type="primary" size="mini" @click="searchClick">查询</button>
  46. </view>
  47. </view>
  48. </view>
  49. <!--下拉刷新区域-->
  50. <z-paging ref="pageRef" :style="pagingStyle" v-model="taskList" @query="getTaskList">
  51. <uni-card v-for="item in taskList" padding="0" :class="item.check?'is-check':''" @click="taskItemClick(item)">
  52. <view class="py-3 text-30 text-black">{{item.taskName}}</view>
  53. <view slot="actions" class="card-actions no-border">
  54. <view class="card-actions-item">
  55. <view class="item-icon-check" v-if="showCheck">
  56. <uni-icons type="checkbox-filled" size="26" color="#ee5b20" v-if="item.check"/>
  57. <uni-icons type="checkbox" size="26" color="#9a9a9a" v-else/>
  58. </view>
  59. <text>{{item.startTime}}提交的申请</text>
  60. </view>
  61. <view class="card-actions-item">
  62. <uni-icons type="calendar" size="18" color="#EE5B20"/>
  63. <text class="card-actions-item-text" style="color: #EE5B20;">审批</text>
  64. </view>
  65. </view>
  66. </uni-card>
  67. </z-paging>
  68. <!--底部操作区域 -->
  69. <view id="hc-bottom-bar" class="hc-bottom-bar"/>
  70. <view id="hc-bottom-btn-bar" class="hc-bottom-btn-bar">
  71. <view class="show-check-tabbars" v-if="showCheck">
  72. <view class="check-bar">
  73. <view class="check-box" @click="allCheckClick">
  74. <text class="text">全选</text>
  75. <uni-icons type="checkbox-filled" size="26" color="#ee5b20" v-if="isAllCheck"/>
  76. <uni-icons type="checkbox" size="26" color="#9a9a9a" v-else/>
  77. </view>
  78. <view class="text-box">
  79. <text class="text">共勾选</text>
  80. <text class="text" style="color: #ee5b20;">{{itemCheckIndex}}</text>
  81. <text class="text">条任务</text>
  82. </view>
  83. </view>
  84. <view class="btn-bar">
  85. <button class="check-btn" size="mini" @click="batchApproval">批量审批</button>
  86. <button class="check-btn" size="mini" @click="cancelTaskClick">批量废除</button>
  87. <button class="check-btn cancel" size="mini" @click="cancelCheckClick">取消操作</button>
  88. </view>
  89. </view>
  90. </view>
  91. </hc-sys>
  92. </template>
  93. <script setup>
  94. import {getCurrentInstance, ref} from "vue";
  95. import mainApi from '~api/tasks/data';
  96. import {onShow, onReady} from '@dcloudio/uni-app'
  97. import {errorToast, querySelect, successToast} from "@/utils/tools";
  98. import {arrToKey, getArrValue, getObjValue} from "js-fast-way";
  99. import {useAppStore} from "@/store";
  100. //初始变量
  101. const store = useAppStore()
  102. const projectId = ref(store.projectId);
  103. const contractId = ref(store.contractId);
  104. const instance = getCurrentInstance().proxy
  105. const isNodes = ref(false)
  106. const pageRef = ref(null)
  107. onReady(() => {
  108. setPagingStyle()
  109. isNodes.value = true
  110. })
  111. onShow(() => {
  112. projectId.value = store.projectId
  113. contractId.value = store.contractId
  114. if (isNodes.value) {
  115. searchClick()
  116. }
  117. })
  118. //内容区域
  119. const pagingStyle = ref({
  120. position: 'relative',
  121. width: '100%',
  122. bottom: 0,
  123. })
  124. const setPagingStyle = async () => {
  125. const {height: appHeight} = await querySelect(instance, 'app-sys')
  126. const {height: navHeight} = await querySelect(instance, 'hc-task-nav-bar')
  127. const {height: bottomHeight} = await querySelect(instance, 'hc-bottom-bar')
  128. const {height: bottomBtnHeight} = await querySelect(instance, 'hc-bottom-btn-bar')
  129. // #ifdef H5
  130. let content = navHeight + bottomHeight + bottomBtnHeight
  131. pagingStyle.value.height = (appHeight - content) + 'px'
  132. // #endif
  133. // #ifdef APP-PLUS
  134. const {screenHeight, safeArea} = uni.getWindowInfo()
  135. content = navHeight + (screenHeight - safeArea.bottom) + bottomBtnHeight
  136. pagingStyle.value.height = (screenHeight - content) + 'px'
  137. // #endif
  138. }
  139. //顶部类型切换
  140. const taskType = ref(1)
  141. const taskTypeData = [
  142. {key: 1, name: '我的审批'},
  143. {key: 2, name: '我发起的'},
  144. {key: 3, name: '已办结的'}
  145. ]
  146. const taskTypeChange = ({key}) => {
  147. taskType.value = key
  148. searchClick()
  149. }
  150. //批量操作
  151. const showCheck = ref(false)
  152. const moreBarClick = () => {
  153. showCheck.value = !showCheck.value
  154. setPagingStyle()
  155. }
  156. //日期选择器弹出框
  157. const isControlsFilter = ref(false)
  158. const dateFilterClick = () => {
  159. isControlsFilter.value = !isControlsFilter.value
  160. setPagingStyle()
  161. }
  162. const dateFilterClear = () => {
  163. isControlsFilter.value = false
  164. searchForm.value.startTime = null
  165. searchForm.value.endTime = null
  166. setPagingStyle()
  167. searchClick()
  168. }
  169. //开启日期选择完毕
  170. const startDateChange = (e) => {
  171. const val = e.detail.value, endTime = searchForm.value.endTime
  172. if (endTime && val && val > endTime) {
  173. errorToast('开始日期不能大于结束日期', 2000)
  174. return false
  175. } else {
  176. searchForm.value.startTime = val
  177. }
  178. }
  179. const startDateClear = () => {
  180. searchForm.value.startTime = null
  181. }
  182. //结束日期选择完毕
  183. const endDateChange = (e) => {
  184. const val = e.detail.value, startTime = searchForm.value.startTime
  185. if (startTime && val && val < startTime) {
  186. errorToast('结束日期不能小于开始日期', 2000)
  187. return false
  188. } else {
  189. searchForm.value.endTime = val
  190. }
  191. }
  192. const endDateClear = () => {
  193. searchForm.value.endTime = null
  194. }
  195. //条件搜索
  196. const searchClick = () => {
  197. pageRef.value?.reload()
  198. }
  199. //排序切换
  200. const changeOrdType = (type) => {
  201. searchForm.value.ordType = type
  202. searchClick()
  203. }
  204. //搜索表单
  205. const searchForm = ref({
  206. startTime: null, endTime: null, ordType: 1
  207. })
  208. //获取任务列表数据
  209. const taskList = ref([])
  210. const getTaskList = async (pageNo, pageSize) => {
  211. let task_type = taskType.value, res = {};
  212. uni.showLoading({title: '获取数据中...', mask: true});
  213. if (task_type === 1) {
  214. const { data } = await mainApi.queryUserToDoTaskList({
  215. projectId: projectId.value,
  216. contractId: contractId.value,
  217. ...searchForm.value,
  218. current: pageNo,
  219. size: pageSize,
  220. })
  221. res = getObjValue(data)
  222. } else if (task_type === 2) {
  223. const { data } = await mainApi.queryUserStartFlow({
  224. projectId: projectId.value,
  225. contractId: contractId.value,
  226. ...searchForm.value,
  227. current: pageNo,
  228. size: pageSize,
  229. })
  230. res = getObjValue(data)
  231. } else if (task_type === 3) {
  232. const { data } = await mainApi.queryUserDoneTaskList({
  233. projectId: projectId.value,
  234. contractId: contractId.value,
  235. ...searchForm.value,
  236. current: pageNo,
  237. size: pageSize,
  238. })
  239. res = getObjValue(data)
  240. }
  241. uni.hideLoading();
  242. isNodes.value = true
  243. pageRef.value?.complete(getArrValue(res?.records));
  244. if (isAllCheck.value) {
  245. isAllCheck.value = itemCheckIndex.value === taskList.value.length
  246. }
  247. }
  248. //任务被点击
  249. const itemCheckIndex = ref(0)
  250. const taskItemClick = (item) => {
  251. if (showCheck.value) {
  252. const check = !item.check, list = taskList.value
  253. if (check) {
  254. itemCheckIndex.value++
  255. } else {
  256. itemCheckIndex.value--
  257. }
  258. item.check = check
  259. isAllCheck.value = list.length === itemCheckIndex.value
  260. } else {
  261. toTaskDetail([item])
  262. }
  263. }
  264. //全选
  265. const isAllCheck = ref(false)
  266. const allCheckClick = () => {
  267. uni.showLoading({title: '处理中...', mask: true});
  268. const list = taskList.value, isCheck = !isAllCheck.value
  269. for (let i = 0; i < list.length; i++) {
  270. list[i].check = isCheck;
  271. }
  272. itemCheckIndex.value = isCheck ? list.length : 0
  273. isAllCheck.value = isCheck
  274. uni.hideLoading();
  275. }
  276. //取消操作
  277. const cancelCheckClick = () => {
  278. const list = taskList.value
  279. for (let i = 0; i < list.length; i++) {
  280. list[i].check = false;
  281. }
  282. itemCheckIndex.value = 0
  283. showCheck.value = false
  284. setPagingStyle()
  285. }
  286. //批量审批
  287. const batchApproval = () => {
  288. let rows = taskList.value.filter((item) => {
  289. return item.check
  290. })
  291. if (rows.length <= 0) {
  292. errorToast('请选择需要审批的任务')
  293. return false
  294. }
  295. //路由跳转
  296. toTaskDetail(rows)
  297. }
  298. //跳转任务详情
  299. const toTaskDetail = (rows) => {
  300. const node = encodeURIComponent(JSON.stringify({
  301. rows: rows,
  302. isTask: taskType.value === 1
  303. }))
  304. uni.navigateTo({
  305. url: '/pages/task/detail?node=' + node,
  306. events: {
  307. finish: () => {
  308. searchClick();
  309. }
  310. }
  311. });
  312. }
  313. //批量废除
  314. const popupRef = ref(null)
  315. const argument = ref('')
  316. const cancelTaskList = ref([])
  317. const cancelTaskClick = () => {
  318. cancelTaskList.value = []
  319. let rows = taskList.value.filter((item) => {
  320. return item.check
  321. })
  322. if (rows.length <= 0) {
  323. errorToast('请选择需要废除的任务')
  324. return false
  325. }
  326. cancelTaskList.value = rows
  327. argument.value = ''
  328. popupRef.value?.open()
  329. }
  330. //确认废除
  331. const confirmRepeal = () => {
  332. if (!argument.value) {
  333. errorToast('请先填写废除理由')
  334. return false
  335. }
  336. batchCompleteApprovalTaskApi(cancelTaskList.value)
  337. }
  338. //批量废除接口
  339. const batchCompleteApprovalTaskApi = async (rows) => {
  340. uni.showLoading({title: '批量废除中...', mask: true});
  341. let taskIds = arrToKey(rows, 'taskId', ',')
  342. let approvalType = arrToKey(rows, 'approvalType', ',')
  343. let formDataId = arrToKey(rows, 'formDataId', ',')
  344. let parallelProcessInstanceIds = arrToKey(rows, 'parallelProcessInstanceId', ',')
  345. const {error, code, msg} = await mainApi.batchCompleteApprovalTask({
  346. flag: 'NO',
  347. comment: argument.value,
  348. taskIds: taskIds,
  349. approvalType: approvalType,
  350. formDataId: formDataId,
  351. parallelProcessInstanceIds: parallelProcessInstanceIds,
  352. })
  353. uni.hideLoading();
  354. if (!error && code === 200) {
  355. successToast('废除成功')
  356. cancelClick()
  357. searchClick()
  358. } else {
  359. errorToast(`废除失败:${msg}`)
  360. }
  361. }
  362. //取消废除
  363. const cancelClick = () => {
  364. popupRef.value?.close()
  365. argument.value = ''
  366. }
  367. </script>
  368. <style lang="scss" scoped>
  369. page {
  370. background: #EFEFF4;
  371. height: 100%;
  372. }
  373. </style>
  374. <style lang="scss">
  375. @import "@/style/task/index.scss";
  376. </style>