taskOnline.vue 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. <route lang="json5" type="page">
  2. {
  3. layout: 'default',
  4. style: {
  5. navigationBarTitleText: '在线录入',
  6. navigationStyle: 'custom',
  7. disableScroll: true,
  8. 'app-plus': {
  9. bounce: 'none',
  10. },
  11. },
  12. }
  13. </route>
  14. <template>
  15. <view class="task-online-container">
  16. <!-- 导航栏 -->
  17. <NavBar title="在线录入" />
  18. <!-- 查询视图 -->
  19. <QueryView ref="queryViewRef" :query-type="defaultTypeValue" @query-action="queryAction" />
  20. <!-- 筛选条件栏 -->
  21. <view class="filter-bar">
  22. <view
  23. v-for="(item, index) in rightTypeList"
  24. :key="item.id"
  25. class="filter-item"
  26. @click="handleRightType(item.id)"
  27. >
  28. <view class="checkbox-wrapper" :style="{ marginLeft: index === 0 ? '0' : '15px' }">
  29. <view class="checkbox" :class="{ 'radio-checked': rightType === item.id }">
  30. <view v-if="rightType === item.id" class="radio-inner"></view>
  31. </view>
  32. <text class="checkbox-text">{{ item.value }}</text>
  33. </view>
  34. </view>
  35. </view>
  36. <!-- 列表 -->
  37. <scroll-view class="list-scroll" scroll-y @scrolltolower="loadMore">
  38. <view v-for="item in listData" :key="item.id" class="task-cell">
  39. <TaskItem
  40. :ref="setItemRef(item.id)"
  41. :item="item"
  42. :user-id="userId"
  43. @claim-task="handleClaimTask"
  44. />
  45. </view>
  46. <view v-if="loading" class="loading-text">加载中...</view>
  47. <view v-if="!hasMore && listData.length > 0" class="no-more-text">没有更多了</view>
  48. <view v-if="!loading && listData.length === 0" class="empty-text">
  49. <text>暂无数据</text>
  50. </view>
  51. </scroll-view>
  52. </view>
  53. </template>
  54. <script lang="ts" setup>
  55. import { ref, reactive, computed, onMounted } from 'vue'
  56. import { useUserStore } from '@/store/user'
  57. import QueryView from './components/query/QueryView.vue'
  58. import TaskItem from './components/TaskItem.vue'
  59. import { useConfigStore } from '@/store/config'
  60. import { TaskOrderFuncName, requestFunc } from '@/api/ApiRouter/taskOrder'
  61. import NavBar from '@/components/NavBar/NavBar.vue'
  62. defineOptions({
  63. name: 'taskOnline',
  64. })
  65. // 状态
  66. const queryViewRef = ref<any>(null)
  67. const listData = ref<any[]>([])
  68. const loading = ref(false)
  69. const hasMore = ref(true)
  70. const params = reactive({
  71. pageNo: 1,
  72. pageSize: 10,
  73. })
  74. const rightType = ref<'claim' | 'allot' | 'unclaim' | ''>('unclaim')
  75. const rightTypeList = [
  76. { value: '全部', id: '' },
  77. { value: '已认领', id: 'claim' },
  78. { value: '已分配', id: 'allot' },
  79. { value: '待认领', id: 'unclaim' },
  80. ]
  81. const userStore = useUserStore()
  82. const userInfo = computed(() => userStore.userInfo)
  83. const userId = computed(() => userInfo.value?.id || '')
  84. const equipType = useConfigStore().getEquipType()
  85. // 默认查询参数
  86. const defaultTypeValue = computed(() => ({
  87. orderNo: '',
  88. taskStatusList: [400, 500, 510],
  89. }))
  90. const itemRefs = reactive<Record<string, any>>({})
  91. // 设置组件引用
  92. const setItemRef = (itemId: string) => (ref: any) => {
  93. if (ref) {
  94. itemRefs[itemId] = ref
  95. } else {
  96. delete itemRefs[itemId]
  97. }
  98. }
  99. // 获取查询参数
  100. const getQueryParams = () => {
  101. const queryData = queryViewRef.value?.getQueryParams() || {}
  102. const statusData = handleRightRefresh()
  103. return {
  104. ...params,
  105. ...queryData,
  106. ...statusData,
  107. }
  108. }
  109. // 获取筛选条件
  110. const handleRightRefresh = () => {
  111. const result: Record<string, any> = {}
  112. switch (rightType.value) {
  113. case 'claim':
  114. result.isClaim = true
  115. result.mainCheckerIds = userId.value
  116. break
  117. case 'allot':
  118. result.isClaim = true
  119. result.isAllot = true
  120. result.checkUserStrIds = userId.value
  121. break
  122. case 'unclaim':
  123. result.isClaim = false
  124. result.planCheckUserIds = userId.value
  125. break
  126. default:
  127. result.mainCheckerIds = userId.value
  128. result.planCheckUserIds = userId.value
  129. result.checkUserStrIds = userId.value
  130. result.managerStrIds = userId.value
  131. break
  132. }
  133. return result
  134. }
  135. // 获取列表数据
  136. const fetchList = async (refresh = false) => {
  137. if (loading.value) return
  138. params.pageNo = refresh ? 1 : params.pageNo + 1
  139. const queryData = getQueryParams()
  140. loading.value = true
  141. try {
  142. const responseResult = await requestFunc(TaskOrderFuncName.TaskEquipList, equipType, queryData)
  143. const newList = responseResult?.data?.list || []
  144. if (refresh) {
  145. listData.value = newList
  146. } else {
  147. listData.value = [...listData.value, ...newList]
  148. }
  149. hasMore.value = newList.length >= params.pageSize
  150. } catch (error) {
  151. console.error('获取列表失败:', error)
  152. } finally {
  153. loading.value = false
  154. }
  155. }
  156. // 加载更多
  157. const loadMore = () => {
  158. if (!loading.value && hasMore.value) {
  159. fetchList(false)
  160. }
  161. }
  162. // 刷新列表
  163. const refreshList = () => {
  164. params.pageNo = 1
  165. fetchList(true)
  166. }
  167. // 查询动作
  168. const queryAction = () => {
  169. refreshList()
  170. }
  171. // 切换筛选类型
  172. const handleRightType = (id: string) => {
  173. rightType.value = id as 'claim' | 'allot' | 'unclaim'
  174. refreshList()
  175. }
  176. // 设备认领/取消认领
  177. const handleClaimTask = async (id: string, isClaim: boolean) => {
  178. try {
  179. let result = null
  180. if (isClaim) {
  181. result = await requestFunc(TaskOrderFuncName.EquipmentConfirmClaim, equipType, { id })
  182. } else {
  183. result = await requestFunc(TaskOrderFuncName.EquipmentCancelClaim, equipType, { id })
  184. }
  185. if (result?.code === 0 && result?.data) {
  186. uni.showToast({ title: `${isClaim ? '认领' : '取消认领'}成功`, icon: 'success' })
  187. itemRefs[id]?.setIsClaim(isClaim)
  188. refreshList()
  189. } else {
  190. const msg = result?.msg || `${isClaim ? '设备认领' : '取消认领'}失败`
  191. uni.showToast({ title: msg, icon: 'error' })
  192. }
  193. } catch (error) {
  194. console.error('认领任务失败:', error)
  195. uni.showToast({ title: '操作失败', icon: 'error' })
  196. }
  197. }
  198. // 页面显示时刷新
  199. onMounted(() => {
  200. refreshList()
  201. })
  202. </script>
  203. <style lang="scss" scoped>
  204. .task-online-container {
  205. display: flex;
  206. flex-direction: column;
  207. height: 100vh;
  208. background-color: #f5f5f5;
  209. }
  210. .navigate-view {
  211. display: flex;
  212. align-items: center;
  213. justify-content: center;
  214. height: 44px;
  215. background-color: #fff;
  216. border-bottom: 1px solid #eee;
  217. }
  218. .navigate-title {
  219. font-size: 17px;
  220. font-weight: 500;
  221. color: #333;
  222. }
  223. .filter-bar {
  224. display: flex;
  225. flex-direction: row;
  226. flex-wrap: wrap;
  227. padding: 10px 15px;
  228. background-color: #fff;
  229. border-bottom: 1px solid #eee;
  230. }
  231. .filter-item {
  232. display: flex;
  233. flex-direction: row;
  234. align-items: center;
  235. }
  236. .checkbox-wrapper {
  237. display: flex;
  238. flex-direction: row;
  239. align-items: center;
  240. }
  241. .checkbox {
  242. display: flex;
  243. align-items: center;
  244. justify-content: center;
  245. width: 16px;
  246. height: 16px;
  247. margin-right: 6px;
  248. border: 1px solid #bbb;
  249. border-radius: 50%;
  250. }
  251. .checkbox.radio-checked {
  252. background-color: #fff;
  253. border-color: #2f8eff;
  254. }
  255. .checkbox .radio-inner {
  256. width: 8px;
  257. height: 8px;
  258. background-color: #2f8eff;
  259. border-radius: 50%;
  260. }
  261. .checkbox-text {
  262. font-size: 14px;
  263. color: #333;
  264. }
  265. .list-scroll {
  266. flex: 1;
  267. overflow: hidden;
  268. }
  269. .loading-text,
  270. .no-more-text {
  271. padding: 15px;
  272. font-size: 14px;
  273. color: #999;
  274. text-align: center;
  275. }
  276. .empty-text {
  277. display: flex;
  278. flex-direction: column;
  279. align-items: center;
  280. justify-content: center;
  281. padding: 60px 0;
  282. color: #999;
  283. font-size: 14px;
  284. }
  285. </style>