yuanmingze пре 2 месеци
родитељ
комит
8e05d489b9

+ 2 - 2
src/pages-mine/compliance-evaluation/ability-test.vue

@@ -78,12 +78,12 @@ import type {
   QuizItem,
   QuizPltDetailResponse,
   QuizPltSubmitParams,
-} from '@/services/modules/mine/compliance-evaluation'
+} from '@/services/modules/mine/complianceEvaluation'
 import {
   getQuizPltAvailApi,
   getQuizPltDetailsApi,
   quizTestResultCreateApi,
-} from '@/services/modules/mine/compliance-evaluation'
+} from '@/services/modules/mine/complianceEvaluation'
 
 interface PageQuery {
   fromHome?: string

+ 2 - 2
src/pages-mine/compliance-evaluation/index.vue

@@ -60,8 +60,8 @@ import { computed, ref } from 'vue'
 
 import { onLoad } from '@dcloudio/uni-app'
 
-import type { AbilityTestResult } from '@/services/modules/mine/compliance-evaluation'
-import { getQuizPltTestResultApi } from '@/services/modules/mine/compliance-evaluation'
+import type { AbilityTestResult } from '@/services/modules/mine/complianceEvaluation'
+import { getQuizPltTestResultApi } from '@/services/modules/mine/complianceEvaluation'
 
 interface PageQuery {
   fromHome?: string

+ 2 - 2
src/pages-mine/points-rule/index.vue

@@ -101,11 +101,11 @@ import type { DictItem } from '@/services/modules/common/type'
 import {
   getTaskTypeDeptListApi,
   getTaskTypeListByRuleIdApi,
-} from '@/services/modules/mine/points-rule'
+} from '@/services/modules/mine/pointsRule'
 import type {
   TaskTypeDeptListResponse,
   TaskTypeListByRuleIdResponse,
-} from '@/services/modules/mine/points-rule/type'
+} from '@/services/modules/mine/pointsRule/type'
 
 type TaskRuleItem = TaskTypeListByRuleIdResponse[string][number]
 

+ 587 - 0
src/pages-task/completed-record/index.vue

@@ -0,0 +1,587 @@
+<template>
+  <view class="task-record">
+    <!-- 顶部筛选栏 -->
+    <view class="record-header">
+      <view class="record-tabs">
+        <view
+          v-for="tab in tabs"
+          :key="tab.value"
+          class="record-tabs__item"
+          :class="{ 'record-tabs__item--active': params.activeTab === tab.value }"
+          @click="handleTabChange(tab.value)"
+        >
+          {{ tab.label }}
+        </view>
+      </view>
+
+      <!-- 月份筛选 -->
+      <view class="record-date">
+        <view class="record-date__divider" />
+
+        <view v-if="!taskRuleId" class="record-date__inner" @click="openTaskRulePicker">
+          <text class="record-date__text">筛选</text>
+          <wd-icon name="caret-down" size="28rpx" />
+        </view>
+
+        <view v-else class="record-date__inner" @click="clearTaskRuleId">
+          <text class="record-date__text">{{ taskRuleIdText }}</text>
+          <wd-icon name="close-circle" size="28rpx" />
+        </view>
+      </view>
+    </view>
+
+    <view class="record-content">
+      <!-- 空状态 -->
+      <view v-if="showEmpty" class="record-empty">
+        <wd-empty icon="no-content" tip="暂时还没有任务记录哦~" />
+      </view>
+
+      <!-- 列表 -->
+      <view v-else class="record-list">
+        <view
+          v-for="item in recordList"
+          :key="item.id"
+          class="record-card"
+          :class="{ 'record-card--rejected': isRejectedRecord(item) }"
+          @click="handleOpenDetail(item.id)"
+        >
+          <view class="record-card__header">
+            <view class="record-card__time">
+              <view class="record-card__time-icon">
+                <wd-icon
+                  name="time-line"
+                  :color="isRejectedRecord(item) ? '#9ca3af' : '#3fa3f1'"
+                  size="36rpx"
+                />
+              </view>
+              <text>{{ formatDate(item.createTime) }}</text>
+            </view>
+
+            <view
+              v-if="isRejectedRecord(item)"
+              class="record-card__reason"
+              @click.stop="handleShowRejectReason(item)"
+            >
+              <text>审核不通过</text>
+
+              <view class="record-card__reason-icon">
+                <wd-icon name="exclamation-circle-fill" color="#9ca3af" size="24px"></wd-icon>
+              </view>
+            </view>
+
+            <wd-icon
+              name="right"
+              :color="isRejectedRecord(item) ? '#9ca3af' : '#3fa3f1'"
+              size="28rpx"
+            />
+          </view>
+
+          <view class="record-card__body">
+            <view class="record-card__title">
+              {{ item.taskTypeId || '-' }}
+            </view>
+
+            <view
+              class="record-card__score"
+              :class="{ 'record-card__score--disabled': isRejectedRecord(item) }"
+            >
+              +{{ formatAmount(item.score) }}积分
+            </view>
+          </view>
+        </view>
+
+        <!-- 底部加载状态 -->
+        <view class="record-load-status">
+          <text v-if="loading">努力加载中</text>
+          <text v-else-if="finished">实在没有了</text>
+          <text v-else>轻轻上拉</text>
+        </view>
+      </view>
+    </view>
+
+    <!-- 月份选择器 -->
+    <wd-datetime-picker
+      v-model:visible="taskRulePickerVisible"
+      v-model="taskRulePickerValue"
+      type="year-month"
+      @confirm="handleTaskRuleConfirm"
+    />
+
+    <!-- 拒绝原因弹窗 -->
+    <wd-popup v-model="rejectReasonPopupVisible" position="center" custom-class="reason-popup">
+      <view class="reason-dialog">
+        <view class="reason-dialog__title">拒绝原因</view>
+
+        <view class="reason-dialog__content">
+          {{ rejectReason }}
+        </view>
+
+        <view class="reason-dialog__footer">
+          <wd-button
+            size="small"
+            round
+            custom-class="reason-dialog__button"
+            @click="rejectReasonPopupVisible = false"
+          >
+            关闭
+          </wd-button>
+        </view>
+      </view>
+    </wd-popup>
+  </view>
+</template>
+
+<script setup lang="ts">
+import { computed, reactive, ref } from 'vue'
+
+import { onLoad, onReachBottom } from '@dcloudio/uni-app'
+import dayjs from 'dayjs'
+
+import { getHistoryTaskListApi } from '@/services/modules/task/completedRecord'
+import type {
+  TaskHistoryRecordListRequest,
+  TaskHistoryRecordListResponse,
+  TaskRecordItem,
+} from '@/services/modules/task/completedRecord/type'
+
+const tabs = [
+  { label: '全部', value: 0 },
+  { label: '通过', value: 1 },
+  { label: '不通过', value: 2 },
+] as const
+
+type TaskRecordTab = (typeof tabs)[number]['value']
+
+const taskStatusMap: Record<TaskRecordTab, string> = {
+  0: '',
+  1: '3',
+  2: '4',
+}
+
+const params = reactive<{
+  activeTab: TaskRecordTab
+  current: number
+  size: number
+  selectedYear: string
+  selectedMonth: string
+}>({
+  activeTab: 0,
+  current: 1,
+  size: 10,
+  selectedYear: '',
+  selectedMonth: '',
+})
+
+const loading = ref(false)
+const finished = ref(false)
+const recordList = ref<TaskRecordItem[]>([])
+
+const taskRulePickerVisible = ref(false)
+const taskRulePickerValue = ref(dayjs().startOf('month').valueOf())
+
+const rejectReasonPopupVisible = ref(false)
+const rejectReason = ref('')
+
+let requestId = 0
+
+const taskRuleId = computed(() => {
+  const { selectedYear, selectedMonth } = params
+  return selectedYear && selectedMonth ? `${selectedYear}-${selectedMonth}` : ''
+})
+
+const taskRuleIdText = computed(() => {
+  const { selectedYear, selectedMonth } = params
+  return selectedYear && selectedMonth ? `${selectedYear}年${selectedMonth}月` : '筛选'
+})
+
+const showEmpty = computed(() => {
+  return !loading.value && recordList.value.length === 0
+})
+
+const resetList = () => {
+  params.current = 1
+  finished.value = false
+  recordList.value = []
+}
+
+const buildRequestParams = (): TaskHistoryRecordListRequest => {
+  return {
+    taskRuleId: taskRuleId.value,
+    current: params.current,
+    size: params.size,
+    taskStatus: taskStatusMap[params.activeTab],
+  }
+}
+
+const refreshRecordList = () => {
+  resetList()
+  fetchRecordList({ force: true })
+}
+
+const fetchRecordList = async (options: { force?: boolean } = {}) => {
+  if (!options.force && (loading.value || finished.value)) return
+
+  loading.value = true
+
+  const currentRequestId = ++requestId
+  const requestParams = buildRequestParams()
+
+  try {
+    const res = await getHistoryTaskListApi(requestParams)
+
+    if (currentRequestId !== requestId) return
+
+    const data = (res?.data ?? {}) as Partial<TaskHistoryRecordListResponse>
+    const records = data.records ?? []
+    const total = Number(data.total ?? 0)
+
+    recordList.value = params.current === 1 ? records : recordList.value.concat(records)
+
+    finished.value = records.length < params.size || (total > 0 && recordList.value.length >= total)
+  } catch (error) {
+    console.error('获取任务记录失败:', error)
+
+    uni.showToast({
+      title: '获取列表失败',
+      icon: 'none',
+    })
+  } finally {
+    if (currentRequestId === requestId) {
+      loading.value = false
+    }
+  }
+}
+
+const loadMoreRecordList = () => {
+  if (loading.value || finished.value) return
+
+  params.current += 1
+  fetchRecordList()
+}
+
+const handleTabChange = (value: TaskRecordTab) => {
+  if (params.activeTab === value) return
+
+  params.activeTab = value
+  refreshRecordList()
+}
+
+const openTaskRulePicker = () => {
+  taskRulePickerVisible.value = true
+}
+
+const handleTaskRuleConfirm = (event: { value: string | number | Date }) => {
+  const date = dayjs(event.value)
+
+  params.selectedYear = date.format('YYYY')
+  params.selectedMonth = date.format('MM')
+
+  refreshRecordList()
+}
+
+const clearTaskRuleId = () => {
+  taskRulePickerValue.value = dayjs().startOf('month').valueOf()
+  params.selectedYear = ''
+  params.selectedMonth = ''
+
+  refreshRecordList()
+}
+
+const handleShowRejectReason = (item: TaskRecordItem) => {
+  rejectReason.value = getRejectReason(item)
+  rejectReasonPopupVisible.value = true
+}
+
+const handleOpenDetail = (id: TaskRecordItem['id']) => {
+  uni.navigateTo({
+    url: `/pages/task/task-detail/index?id=${id}`,
+  })
+}
+
+const isRejectedRecord = (item: TaskRecordItem) => {
+  return String(item.realFlag) === '1' || String(item.taskStatus) === '4'
+}
+
+const getRejectReason = (item: TaskRecordItem) => {
+  return (
+    item.approvalInfo ||
+    item.reportOneApprovalInfo ||
+    item.reportDrugApprovalInfo ||
+    item.reportSecondApprovalInfo ||
+    item.taskStatusInfo ||
+    '暂无原因'
+  )
+}
+
+const formatDate = (value: string | null) => {
+  if (!value) return '-'
+
+  const date = dayjs(value)
+  return date.isValid() ? date.format('YYYY-MM-DD') : '-'
+}
+
+const formatAmount = (value: string | number | null) => {
+  return value === null || value === undefined || value === '' ? '-' : value
+}
+
+onReachBottom(loadMoreRecordList)
+
+onLoad(() => {
+  fetchRecordList({ force: true })
+})
+</script>
+
+<style lang="scss" scoped>
+.task-record {
+  min-height: 100vh;
+  background: linear-gradient(180deg, #f3f8ff 0%, #fafafa 360rpx, #fafafa 100%);
+
+  .record-header {
+    position: fixed;
+    top: 0;
+    right: 0;
+    left: 0;
+    z-index: 99;
+    display: flex;
+    align-items: center;
+    height: 96rpx;
+    padding: 0 20rpx;
+    background: rgba(255, 255, 255, 0.96);
+    box-shadow: 0 8rpx 24rpx rgba(31, 91, 150, 0.06);
+    box-sizing: border-box;
+  }
+
+  .record-tabs {
+    display: flex;
+    flex: 1;
+    height: 100%;
+  }
+
+  .record-tabs__item {
+    position: relative;
+    flex: 1;
+    height: 100%;
+    color: #6b7280;
+    font-size: 28rpx;
+    font-weight: 400;
+    line-height: 96rpx;
+    text-align: center;
+  }
+
+  .record-tabs__item--active {
+    color: #1f2937;
+    font-weight: 700;
+
+    &::after {
+      position: absolute;
+      bottom: 10rpx;
+      left: 50%;
+      width: 44rpx;
+      height: 8rpx;
+      background: linear-gradient(90deg, #3fa3f1 0%, #68c7ff 100%);
+      border-radius: 999rpx;
+      transform: translateX(-50%);
+      content: '';
+    }
+  }
+
+  .record-date {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 230rpx;
+    height: 100%;
+    color: #333333;
+  }
+
+  .record-date__divider {
+    width: 2rpx;
+    height: 40rpx;
+    margin-right: 22rpx;
+    background: #ebebeb;
+  }
+
+  .record-date__inner {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    min-width: 160rpx;
+    height: 100%;
+  }
+
+  .record-date__text {
+    margin-right: 10rpx;
+    color: #333333;
+    font-size: 28rpx;
+    white-space: nowrap;
+  }
+
+  .record-content {
+    padding: 120rpx 28rpx 28rpx;
+  }
+
+  .record-empty {
+    padding-top: 220rpx;
+  }
+
+  .record-card {
+    position: relative;
+    margin-bottom: 24rpx;
+    overflow: hidden;
+    background: #ffffff;
+    border: 2rpx solid rgba(63, 163, 241, 0.08);
+    border-radius: 24rpx;
+    box-shadow: 0 12rpx 32rpx rgba(31, 91, 150, 0.08);
+  }
+
+  .record-card__header {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    min-height: 82rpx;
+    padding: 0 26rpx;
+    background: linear-gradient(135deg, #eaf6ff 0%, #f5fbff 100%);
+    box-sizing: border-box;
+  }
+
+  .record-card__time {
+    display: flex;
+    flex: 1;
+    align-items: center;
+    min-width: 0;
+    color: #1f2937;
+    font-size: 26rpx;
+    font-weight: 600;
+    line-height: 40rpx;
+  }
+
+  .record-card__time-icon {
+    display: flex;
+    flex-shrink: 0;
+    align-items: center;
+    justify-content: center;
+    margin-right: 14rpx;
+  }
+
+  .record-card__reason {
+    display: flex;
+    flex-shrink: 0;
+    align-items: center;
+    margin-right: 16rpx;
+    color: #9ca3af;
+    font-size: 24rpx;
+    line-height: 36rpx;
+  }
+
+  .record-card__reason-icon {
+    display: flex;
+    flex-shrink: 0;
+    align-items: center;
+    justify-content: center;
+    margin-left: 10rpx;
+  }
+
+  .record-card__body {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    min-height: 128rpx;
+    padding: 0 26rpx;
+    background: #ffffff;
+    box-sizing: border-box;
+  }
+
+  .record-card__title {
+    flex: 1;
+    min-width: 0;
+    color: #1f2937;
+    font-size: 32rpx;
+    font-weight: 600;
+    line-height: 44rpx;
+    word-break: break-all;
+  }
+
+  .record-card__score {
+    flex-shrink: 0;
+    margin-left: 24rpx;
+    color: rgb(var(--main-color-rgb));
+    font-size: 32rpx;
+    font-weight: 700;
+    line-height: 44rpx;
+  }
+
+  .record-card__score--disabled {
+    color: #9ca3af;
+  }
+
+  .record-card--rejected {
+    border-color: rgba(156, 163, 175, 0.1);
+    box-shadow: 0 10rpx 28rpx rgba(31, 41, 55, 0.05);
+
+    .record-card__header {
+      background: linear-gradient(135deg, #f3f4f6 0%, #fafafa 100%);
+    }
+
+    .record-card__time,
+    .record-card__title {
+      color: #9ca3af;
+    }
+  }
+
+  .record-load-status {
+    padding: 12rpx 0 32rpx;
+    color: #9ca3af;
+    font-size: 24rpx;
+    line-height: 36rpx;
+    text-align: center;
+  }
+}
+
+:deep(.reason-popup) {
+  overflow: visible;
+  background: transparent;
+}
+
+.reason-dialog {
+  width: 560rpx;
+  padding: 34rpx 34rpx 28rpx;
+  background: #ffffff;
+  border-radius: 24rpx;
+  box-shadow: 0 18rpx 48rpx rgba(31, 41, 55, 0.16);
+  box-sizing: border-box;
+}
+
+.reason-dialog__title {
+  color: #1f2937;
+  font-size: 32rpx;
+  font-weight: 700;
+  line-height: 44rpx;
+  text-align: center;
+}
+
+.reason-dialog__content {
+  min-height: 120rpx;
+  margin-top: 24rpx;
+  color: #4b5563;
+  font-size: 28rpx;
+  line-height: 44rpx;
+  text-align: left;
+  word-break: break-all;
+}
+
+.reason-dialog__footer {
+  display: flex;
+  justify-content: center;
+  margin-top: 32rpx;
+}
+
+:deep(.reason-dialog__button) {
+  min-width: 180rpx;
+  height: 64rpx;
+  color: #ffffff;
+  font-size: 28rpx;
+  font-weight: 600;
+  background: linear-gradient(135deg, #3fa3f1 0%, #61bef8 100%);
+  border: 0;
+}
+</style>

+ 7 - 0
src/pages.json

@@ -193,6 +193,13 @@
             "navigationBarTitleText": "我的积分包记录",
             "navigationStyle": "default"
           }
+        },
+        {
+          "path": "completed-record/index",
+          "style": {
+            "navigationBarTitleText": "任务完成记录",
+            "navigationStyle": "default"
+          }
         }
       ]
     },

+ 1 - 1
src/pages/mine/mine.config.ts

@@ -17,7 +17,7 @@ export const menuList = [
   {
     txt: '任务完成记录',
     icon: 'https://yy-cloud-oss.oss-cn-beijing.aliyuncs.com/img/rwwcjl.png',
-    path: 'task-history',
+    path: '/pages-task/completed-record/index',
   },
 ]
 

+ 0 - 0
src/services/modules/mine/compliance-evaluation/index.ts → src/services/modules/mine/complianceEvaluation/index.ts


+ 0 - 0
src/services/modules/mine/compliance-evaluation/type.d.ts → src/services/modules/mine/complianceEvaluation/type.d.ts


+ 0 - 0
src/services/modules/mine/points-rule/index.ts → src/services/modules/mine/pointsRule/index.ts


+ 0 - 0
src/services/modules/mine/points-rule/type.d.ts → src/services/modules/mine/pointsRule/type.d.ts


+ 9 - 0
src/services/modules/task/completedRecord/index.ts

@@ -0,0 +1,9 @@
+import http from '../../../index'
+import type { TaskHistoryRecordListRequest, TaskHistoryRecordListResponse } from './type'
+
+export const getHistoryTaskListApi = (params: TaskHistoryRecordListRequest) => {
+  return http.get<TaskHistoryRecordListResponse>(`/admin/api/getHistoryTaskList`, params, {
+    loading: true,
+    loadingText: '加载中...',
+  })
+}

+ 134 - 0
src/services/modules/task/completedRecord/type.d.ts

@@ -0,0 +1,134 @@
+export interface TaskHistoryRecordListRequest {
+  taskRuleId: string
+  current: number
+  size: number
+  taskStatus: string
+}
+
+export interface TaskHistoryRecordListResponse {
+  current: number
+  hitCount: boolean
+  optimizeCountSql: boolean
+  orders: unknown[]
+  pages: number
+  searchCount: boolean
+  size: number
+  total: number
+  records: TaskRecordItem[]
+}
+
+export interface TaskRecordItem {
+  id: string
+
+  actualStatus: string | null
+  actualTime: string | null
+  address: string | null
+
+  approvalInfo: string | null
+  approvalOpinion: string | null
+  approvalTime: string | null
+  approvalUserId: string | null
+  approvalUserName: string | null
+
+  area: string | null
+  auditStatusTag: string | null
+  city: string | null
+  compareResult: string | null
+
+  createTime: string
+  createUser: string | null
+
+  delFlag: string | null
+
+  deptApprovalTime: string | null
+  deptApprovalUserId: string | null
+  deptApprovalUserName: string | null
+  deptId: string | null
+  deptReviewTaskCount: number
+
+  drugEntId: string | null
+  enableFlag: string | null
+  extIds: string | null
+
+  ip2region: string | null
+
+  lookintoDate: string | null
+  lookintoTypeId: string | null
+  notifyDate: string | null
+
+  packageStatus: string | null
+  pkgSn: string | null
+
+  platAuditStatus: string | null
+  province: string | null
+
+  realFlag: string | null
+
+  relationPkgSn: string | null
+  relationScorePackageName: string | null
+
+  remoteIp: string | null
+
+  reportDrugApprovalInfo: string | null
+  reportDrugApprovalOpinion: string | null
+  reportDrugApprovalStatus: string | null
+  reportDrugId: string | null
+
+  reportOneApprovalInfo: string | null
+  reportOneApprovalOpinion: string | null
+  reportOneApprovalStatus: string | null
+  reportOneId: string | null
+
+  reportSecondApprovalInfo: string | null
+  reportSecondApprovalOpinion: string | null
+  reportSecondApprovalStatus: string | null
+  reportSecondId: string | null
+
+  reviewShow: string | null
+  reviewTaskCount: number
+
+  score: number
+
+  scorePackageDrugId: string | null
+  scorePackageId: string | null
+  scorePackageLevel1Id: string | null
+  scorePackageName: string | null
+
+  settleStatus: string | null
+
+  shareImgUrl: string | null
+  subTime: string | null
+  subToGigTime: string | null
+
+  submitStatus: string | null
+
+  taskContentId: string | null
+  taskFrom: string | null
+  taskInfoImg: string | null
+  taskNumber: string
+  taskRuleId: string | null
+  taskSettleStatus: string | null
+  taskStatus: string | null
+  taskStatusInfo: string | null
+  taskTypeId: string
+  taskTypeName: string | null
+  taskTypeParentId: string | null
+  taskUserId: string | null
+  taskUserType: string | null
+  taskUsername: string | null
+
+  tenantId: number
+  tmpId: string | null
+  type: string | null
+
+  updateTime: string | null
+  updateUser: string | null
+
+  userList: unknown[] | null
+  username: string | null
+
+  version: number
+
+  wmTaskContent: unknown | null
+  wmTaskStatusFlowList: unknown[] | null
+}