|
|
@@ -0,0 +1,1012 @@
|
|
|
+<template>
|
|
|
+ <uni-navbar-lite :showLeft=true title="工分"></uni-navbar-lite>
|
|
|
+ <view class="list-page">
|
|
|
+ <!-- 搜索栏 -->
|
|
|
+ <view class="search-bar">
|
|
|
+ <view class="search-box">
|
|
|
+ <image class="search-icon" src="/static/images/workbench/list/1.png" mode="aspectFit"></image>
|
|
|
+ <input
|
|
|
+ class="search-input"
|
|
|
+ placeholder="搜索工单编码、风机编号"
|
|
|
+ v-model="searchKeyword"
|
|
|
+ @confirm="handleSearch"
|
|
|
+ />
|
|
|
+ <text v-if="searchKeyword.length > 0" class="clear-icon" @click="clearSearch">✕</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 工单评分状态筛选 -->
|
|
|
+ <!-- <view class="status-bar">
|
|
|
+ <view class="status-box">
|
|
|
+ <text
|
|
|
+ class="status-txt"
|
|
|
+ :class="{ 'stauts-sel': statusFilter === '' }"
|
|
|
+ @click="filterByStatus('')"
|
|
|
+ >
|
|
|
+ 全部
|
|
|
+ </text>
|
|
|
+ <text
|
|
|
+ class="status-txt"
|
|
|
+ :class="{ 'stauts-sel': statusFilter === 'to_self' }"
|
|
|
+ @click="filterByStatus('to_self')"
|
|
|
+ >
|
|
|
+ 待自评
|
|
|
+ </text>
|
|
|
+ <text
|
|
|
+ class="status-txt"
|
|
|
+ :class="{ 'stauts-sel': statusFilter === 'to_re' }"
|
|
|
+ @click="filterByStatus('to_re')"
|
|
|
+ >
|
|
|
+ 待复评
|
|
|
+ </text>
|
|
|
+ <text
|
|
|
+ class="status-txt"
|
|
|
+ :class="{ 'stauts-sel': statusFilter === 'to_confirm' }"
|
|
|
+ @click="filterByStatus('to_confirm')"
|
|
|
+ >
|
|
|
+ 待确认
|
|
|
+ </text>
|
|
|
+ <text
|
|
|
+ class="status-txt"
|
|
|
+ :class="{ 'stauts-sel': statusFilter === 'to_final' }"
|
|
|
+ @click="filterByStatus('to_final')"
|
|
|
+ >
|
|
|
+ 待终评
|
|
|
+ </text>
|
|
|
+ </view>
|
|
|
+ </view> -->
|
|
|
+
|
|
|
+ <!-- 工分统计 -->
|
|
|
+ <!-- <view class="stats-section">
|
|
|
+ <view class="stats-header">
|
|
|
+ <text class="stats-title">{{ monthTitle }}月度工分</text>
|
|
|
+ <view class="month-filters">
|
|
|
+ <text
|
|
|
+ class="month-filter"
|
|
|
+ :class="{ 'month-filter-sel': selectedMonth === 'prev' }"
|
|
|
+ @click="changeMonth('prev')"
|
|
|
+ >
|
|
|
+ 上月
|
|
|
+ </text>
|
|
|
+ <text
|
|
|
+ class="month-filter"
|
|
|
+ :class="{ 'month-filter-sel': selectedMonth === 'current' }"
|
|
|
+ @click="changeMonth('current')"
|
|
|
+ >
|
|
|
+ 本月
|
|
|
+ </text>
|
|
|
+ <text
|
|
|
+ class="month-filter"
|
|
|
+ :class="{ 'month-filter-sel': selectedMonth === 'custom' }"
|
|
|
+ @click="showCustomDatePicker"
|
|
|
+ >
|
|
|
+ 自定义
|
|
|
+ </text>
|
|
|
+ </view>
|
|
|
+ </view> -->
|
|
|
+
|
|
|
+ <!-- 统计数据 -->
|
|
|
+ <!-- <view class="stats-content">
|
|
|
+ <view class="total-score">
|
|
|
+ <text class="score-value">{{ totalScore }}分</text>
|
|
|
+ <text class="score-label">{{ monthTitle }}总工分</text>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="score-breakdown">
|
|
|
+ <view class="breakdown-item">
|
|
|
+ <text class="breakdown-label">维保工分</text>
|
|
|
+ <text class="breakdown-value">{{ maintenanceScore }}分</text>
|
|
|
+ </view>
|
|
|
+ <view class="breakdown-item">
|
|
|
+ <text class="breakdown-label">维修工分</text>
|
|
|
+ <text class="breakdown-value">{{ repairScore }}分</text>
|
|
|
+ </view>
|
|
|
+ <view v-if="rank !== null && totalRankingUsers !== null" class="breakdown-item">
|
|
|
+ <text class="breakdown-label">排名</text>
|
|
|
+ <text class="breakdown-value">{{ rank }}/{{ totalRankingUsers }}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view> -->
|
|
|
+
|
|
|
+ <!-- 工单评分列表 -->
|
|
|
+ <common-list
|
|
|
+ :dataList="orderList"
|
|
|
+ :loading="loading"
|
|
|
+ :refreshing="refreshing"
|
|
|
+ :hasMore="hasMore"
|
|
|
+ @refresh="handleRefresh"
|
|
|
+ @loadMore="loadMore"
|
|
|
+ @itemClick="handleItemClick"
|
|
|
+ class="list-with-padding"
|
|
|
+ >
|
|
|
+ <template #default="{ item, index }">
|
|
|
+ <view class="list-item">
|
|
|
+ <view class="item-container">
|
|
|
+ <view class="item-header">
|
|
|
+ <text class="item-title">{{ getPropertyValue(item, 'workOrderProjectNo') }}-风机编号{{ getPropertyValue(item, 'pcsDeviceName') }}的{{ getWorkOrderTypeText(getPropertyValue(item, 'orderType')) }}</text>
|
|
|
+ <!-- <text class="info-value">{{ getScoringStatus(item) }}</text> -->
|
|
|
+ <text class="status-tag" :class="getStatusClass(item)">{{ getScoringStatus(item) }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="info-row">
|
|
|
+ <view class="info-label">
|
|
|
+ <text class="text-gray">{{ getWorkOrderTypeInfo(item) }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="info-value-row">
|
|
|
+ <text v-if="getPropertyValue(item, 'score') !== ''" class="score-text">{{ formatNumber(parseFloat(getPropertyValue(item, 'score'))) }}</text>
|
|
|
+ <!-- <text class="status-text">{{ getOrderStatusText(getPropertyValue(item, 'scoreStatus')) }}</text> -->
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="info-row">
|
|
|
+ <view class="info-label">
|
|
|
+ <text class="text-gray">工作结束时间: {{ formatDate(getPropertyValue(item, 'assignTime')) }}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </template>
|
|
|
+ </common-list>
|
|
|
+
|
|
|
+ <!-- 自定义时间选择弹窗 -->
|
|
|
+ <l-popup v-model="showDatePickerPopup" position="bottom">
|
|
|
+ <view class="date-picker-popup">
|
|
|
+ <view class="popup-header">
|
|
|
+ <text class="popup-title">选择年月</text>
|
|
|
+ <view class="popup-actions">
|
|
|
+ <text class="cancel-btn" @click="closeDatePicker">取消</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="date-picker-container">
|
|
|
+ <view class="date-picker-item">
|
|
|
+ <!-- <text class="date-label">选择年月</text> -->
|
|
|
+ <view class="date-display" @click="openDatePicker">
|
|
|
+ {{ customDate != null && customDate != '' ? customDate : '请选择年月' }}
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </l-popup>
|
|
|
+
|
|
|
+ <!-- Date Picker -->
|
|
|
+ <l-popup v-model="showDatePicker" position="bottom" :z-index="1000">
|
|
|
+ <l-date-time-picker
|
|
|
+ :mode="3"
|
|
|
+ format="YYYY-MM"
|
|
|
+ :modelValue="customDate"
|
|
|
+ confirm-btn="确定"
|
|
|
+ cancel-btn="取消"
|
|
|
+ @confirm="onDateConfirm"
|
|
|
+ @cancel="showDatePicker = false">
|
|
|
+ </l-date-time-picker>
|
|
|
+ </l-popup>
|
|
|
+
|
|
|
+ <!-- 底部 TabBar -->
|
|
|
+ <!-- <custom-tabbar :current="3" /> -->
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="uts">
|
|
|
+ import { ref, computed, onMounted } from 'vue'
|
|
|
+ import { listOrderScores, getOrderScoreStatistics, listMobileOrderScores } from '@/api/score/index'
|
|
|
+ import { getDictDataByType } from '@/api/dict/index'
|
|
|
+ import type { SysDictData } from '@/types/dict'
|
|
|
+
|
|
|
+ // 数据状态
|
|
|
+ const searchKeyword = ref<string>('')
|
|
|
+ const statusFilter = ref<string>('')
|
|
|
+ const selectedMonth = ref<string>('current')
|
|
|
+ const loading = ref<boolean>(false)
|
|
|
+ const refreshing = ref<boolean>(false)
|
|
|
+ const hasMore = ref<boolean>(true)
|
|
|
+ const orderList = ref<any[]>([])
|
|
|
+ const currentPage = ref<number>(1)
|
|
|
+ const totalScore = ref<number>(0)
|
|
|
+ const maintenanceScore = ref<number>(0)
|
|
|
+ const repairScore = ref<number>(0)
|
|
|
+ const rank = ref<number | null>(null)
|
|
|
+ const totalRankingUsers = ref<number | null>(null)
|
|
|
+ const customDate = ref<string>('')
|
|
|
+
|
|
|
+ // 弹窗显示状态
|
|
|
+ const showDatePickerPopup = ref<boolean>(false)
|
|
|
+ const showDatePicker = ref<boolean>(false)
|
|
|
+
|
|
|
+ // 工单状态字典列表
|
|
|
+ const statusDictList = ref<SysDictData[]>([])
|
|
|
+ // 维保类型字典列表
|
|
|
+ const inspectionTypeDictList = ref<SysDictData[]>([])
|
|
|
+ // 检修类型字典列表
|
|
|
+ const maintenanceTypeDictList = ref<SysDictData[]>([])
|
|
|
+ const dictLoaded = ref<boolean>(false)
|
|
|
+
|
|
|
+ // 计算属性
|
|
|
+ const monthTitle = computed(() => {
|
|
|
+ switch (selectedMonth.value) {
|
|
|
+ case 'prev':
|
|
|
+ return '上月'
|
|
|
+ case 'current':
|
|
|
+ return '本月'
|
|
|
+ case 'custom':
|
|
|
+ // 显示自定义选择的年月
|
|
|
+ return (customDate.value != null && customDate.value != '') ? customDate.value : '自定义'
|
|
|
+ default:
|
|
|
+ return '本月'
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ // 打开自定义日期选择弹窗
|
|
|
+ function showCustomDatePicker() {
|
|
|
+ showDatePickerPopup.value = true
|
|
|
+ }
|
|
|
+
|
|
|
+ function closeDatePicker() {
|
|
|
+ showDatePicker.value = false
|
|
|
+ showDatePickerPopup.value = false
|
|
|
+ }
|
|
|
+
|
|
|
+ // 打开日期选择器
|
|
|
+ function openDatePicker() {
|
|
|
+ showDatePicker.value = true
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取工单评分状态字典列表
|
|
|
+ const loadStatusDictList = async (): Promise<void> => {
|
|
|
+ try {
|
|
|
+ const result = await getDictDataByType('gxt_scoring_status')
|
|
|
+ const resultObj = result as UTSJSONObject
|
|
|
+
|
|
|
+ if (resultObj['code'] == 200) {
|
|
|
+ const data = resultObj['data'] as any[]
|
|
|
+ const dictData: SysDictData[] = []
|
|
|
+
|
|
|
+ if (data != null && data.length > 0) {
|
|
|
+ for (let i = 0; i < data.length; i++) {
|
|
|
+ const item = data[i] as UTSJSONObject
|
|
|
+ // 只提取需要的字段
|
|
|
+ const dictItem: SysDictData = {
|
|
|
+ dictValue: item['dictValue'] as string | null,
|
|
|
+ dictLabel: item['dictLabel'] as string | null,
|
|
|
+ dictCode: null,
|
|
|
+ dictSort: null,
|
|
|
+ dictType: null,
|
|
|
+ cssClass: null,
|
|
|
+ listClass: null,
|
|
|
+ isDefault: null,
|
|
|
+ status: null,
|
|
|
+ default: null,
|
|
|
+ createTime: null,
|
|
|
+ remark: null
|
|
|
+ }
|
|
|
+ dictData.push(dictItem)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ statusDictList.value = dictData
|
|
|
+ }
|
|
|
+ } catch (e: any) {
|
|
|
+ console.error('获取工单评分状态字典失败:', e.message)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取维保类型字典列表
|
|
|
+ const loadInspectionTypeDictList = async (): Promise<void> => {
|
|
|
+ try {
|
|
|
+ const result = await getDictDataByType('gxt_inspection_type')
|
|
|
+ const resultObj = result as UTSJSONObject
|
|
|
+
|
|
|
+ if (resultObj['code'] == 200) {
|
|
|
+ const data = resultObj['data'] as any[]
|
|
|
+ const dictData: SysDictData[] = []
|
|
|
+
|
|
|
+ if (data != null && data.length > 0) {
|
|
|
+ for (let i = 0; i < data.length; i++) {
|
|
|
+ const item = data[i] as UTSJSONObject
|
|
|
+ // 只提取需要的字段
|
|
|
+ const dictItem: SysDictData = {
|
|
|
+ dictValue: item['dictValue'] as string | null,
|
|
|
+ dictLabel: item['dictLabel'] as string | null,
|
|
|
+ dictCode: null,
|
|
|
+ dictSort: null,
|
|
|
+ dictType: null,
|
|
|
+ cssClass: null,
|
|
|
+ listClass: null,
|
|
|
+ isDefault: null,
|
|
|
+ status: null,
|
|
|
+ default: null,
|
|
|
+ createTime: null,
|
|
|
+ remark: null
|
|
|
+ }
|
|
|
+ dictData.push(dictItem)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ inspectionTypeDictList.value = dictData
|
|
|
+ }
|
|
|
+ } catch (e: any) {
|
|
|
+ console.error('获取维保类型字典失败:', e.message)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取检修类型字典列表
|
|
|
+ const loadMaintenanceTypeDictList = async (): Promise<void> => {
|
|
|
+ try {
|
|
|
+ const result = await getDictDataByType('gxt_maintenance_type')
|
|
|
+ const resultObj = result as UTSJSONObject
|
|
|
+
|
|
|
+ if (resultObj['code'] == 200) {
|
|
|
+ const data = resultObj['data'] as any[]
|
|
|
+ const dictData: SysDictData[] = []
|
|
|
+
|
|
|
+ if (data != null && data.length > 0) {
|
|
|
+ for (let i = 0; i < data.length; i++) {
|
|
|
+ const item = data[i] as UTSJSONObject
|
|
|
+ // 只提取需要的字段
|
|
|
+ const dictItem: SysDictData = {
|
|
|
+ dictValue: item['dictValue'] as string | null,
|
|
|
+ dictLabel: item['dictLabel'] as string | null,
|
|
|
+ dictCode: null,
|
|
|
+ dictSort: null,
|
|
|
+ dictType: null,
|
|
|
+ cssClass: null,
|
|
|
+ listClass: null,
|
|
|
+ isDefault: null,
|
|
|
+ status: null,
|
|
|
+ default: null,
|
|
|
+ createTime: null,
|
|
|
+ remark: null
|
|
|
+ }
|
|
|
+ dictData.push(dictItem)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ maintenanceTypeDictList.value = dictData
|
|
|
+ }
|
|
|
+ } catch (e: any) {
|
|
|
+ console.error('获取检修类型字典失败:', e.message)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Helper function to safely extract properties from item
|
|
|
+ function getPropertyValue(item: any | null, propertyName: string): string {
|
|
|
+ if (item == null) return ''
|
|
|
+ const itemObj = item as UTSJSONObject
|
|
|
+ const value = itemObj[propertyName]
|
|
|
+ return value != null ? '' + value : ''
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取工单类型文本
|
|
|
+ function getWorkOrderTypeText(orderType: string | null): string {
|
|
|
+ if (orderType != null) {
|
|
|
+ if (orderType == "1") {
|
|
|
+ return "维修工单"
|
|
|
+ } else if (orderType == "2") {
|
|
|
+ return "维保工单"
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return ""
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取工单类型相关信息(维保类型或检修类型)
|
|
|
+ function getWorkOrderTypeInfo(item: any | null): string {
|
|
|
+ if (item == null) return ''
|
|
|
+
|
|
|
+ const orderType = getPropertyValue(item, 'orderType')
|
|
|
+
|
|
|
+ // 维保工单显示维保类型
|
|
|
+ if (orderType == "2") {
|
|
|
+ const inspectionType = getPropertyValue(item, 'inspectionType')
|
|
|
+ if (inspectionType != null && inspectionType != '') {
|
|
|
+ // 如果字典尚未加载,返回原始值
|
|
|
+ if (inspectionTypeDictList.value.length == 0) {
|
|
|
+ return inspectionType
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查找字典中对应的标签
|
|
|
+ const dictItem = inspectionTypeDictList.value.find(dict => dict.dictValue == inspectionType)
|
|
|
+ return (dictItem != null ? dictItem.dictLabel : inspectionType) as string
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 维修工单显示检修类型
|
|
|
+ else if (orderType == "1") {
|
|
|
+ const maintenanceType = getPropertyValue(item, 'maintenanceType')
|
|
|
+ if (maintenanceType != null && maintenanceType != '') {
|
|
|
+ // 如果字典尚未加载,返回原始值
|
|
|
+ if (maintenanceTypeDictList.value.length == 0) {
|
|
|
+ return maintenanceType
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查找字典中对应的标签
|
|
|
+ const dictItem = maintenanceTypeDictList.value.find(dict => dict.dictValue == maintenanceType)
|
|
|
+ return (dictItem != null ? dictItem.dictLabel : maintenanceType) as string
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return ""
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取工单状态文本
|
|
|
+ function getScoringStatus(item: any | null): string | null {
|
|
|
+ if (item == null) return ''
|
|
|
+ const rawStatus = getPropertyValue(item, 'scoringStatus')
|
|
|
+
|
|
|
+ if (rawStatus == null || rawStatus == '') return ''
|
|
|
+
|
|
|
+ // 如果字典尚未加载,返回原始值
|
|
|
+ if (!dictLoaded.value) {
|
|
|
+ return rawStatus
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查找字典中对应的标签
|
|
|
+ const dictItem = statusDictList.value.find(dict => dict.dictValue == rawStatus)
|
|
|
+ return dictItem != null ? dictItem.dictLabel : rawStatus
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取工单评分状态文本
|
|
|
+ function getOrderStatusText(status: string | null): string {
|
|
|
+ const statusMap: UTSJSONObject = {
|
|
|
+ '1': '待自评',
|
|
|
+ '2': '待复评',
|
|
|
+ '3': '待确认',
|
|
|
+ '4': '待终评'
|
|
|
+ }
|
|
|
+
|
|
|
+ if (status == null) return ''
|
|
|
+ const result = statusMap[status]
|
|
|
+ return result != null ? result as string : '未知状态'
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取统计数据
|
|
|
+ function getStatistics() {
|
|
|
+ // Convert 'current' and 'prev' values to actual date strings
|
|
|
+ let monthValue = '';
|
|
|
+ if (selectedMonth.value === 'custom') {
|
|
|
+ monthValue = customDate.value;
|
|
|
+ } else if (selectedMonth.value === 'current') {
|
|
|
+ const now = new Date();
|
|
|
+ monthValue = `${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}`;
|
|
|
+ } else if (selectedMonth.value === 'prev') {
|
|
|
+ const now = new Date();
|
|
|
+ const prevMonth = now.getMonth(); // getMonth() returns 0-11
|
|
|
+ const prevYear = prevMonth === 0 ? now.getFullYear() - 1 : now.getFullYear();
|
|
|
+ const monthStr = prevMonth === 0 ? '12' : prevMonth.toString().padStart(2, '0');
|
|
|
+ monthValue = `${prevYear}-${monthStr}`;
|
|
|
+ }
|
|
|
+
|
|
|
+ const params: UTSJSONObject = {
|
|
|
+ //scoringStatus: statusFilter.value,
|
|
|
+ month: monthValue
|
|
|
+ }
|
|
|
+
|
|
|
+ getOrderScoreStatistics(params).then((response: any) => {
|
|
|
+ const resultObj = response as UTSJSONObject
|
|
|
+ const responseData = resultObj['data'] as UTSJSONObject
|
|
|
+
|
|
|
+ if (responseData != null) {
|
|
|
+ totalScore.value = (responseData['totalScore'] != null) ? parseFloat((responseData['totalScore'] as number).toFixed(2)) : 0
|
|
|
+ maintenanceScore.value = (responseData['maintenanceScore'] != null) ? parseFloat((responseData['maintenanceScore'] as number).toFixed(2)) : 0
|
|
|
+ repairScore.value = (responseData['repairScore'] != null) ? parseFloat((responseData['repairScore'] as number).toFixed(2)) : 0
|
|
|
+ rank.value = (responseData['rank'] != null) ? responseData['rank'] as number : null
|
|
|
+ totalRankingUsers.value = (responseData['totalRankingUsers'] != null) ? responseData['totalRankingUsers'] as number : null
|
|
|
+ } else {
|
|
|
+ totalScore.value = 0
|
|
|
+ maintenanceScore.value = 0
|
|
|
+ repairScore.value = 0
|
|
|
+ rank.value = null
|
|
|
+ totalRankingUsers.value = null
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ function onDateConfirm(value: string) {
|
|
|
+ customDate.value = value
|
|
|
+ showDatePicker.value = false
|
|
|
+ showDatePickerPopup.value = false
|
|
|
+ selectedMonth.value = 'custom'
|
|
|
+ getStatistics()
|
|
|
+ }
|
|
|
+
|
|
|
+ // 方法
|
|
|
+ function loadData(isRefresh: boolean) {
|
|
|
+ const shouldRefresh = isRefresh
|
|
|
+
|
|
|
+ if (loading.value && !shouldRefresh) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ loading.value = true
|
|
|
+ if (shouldRefresh) {
|
|
|
+ currentPage.value = 1
|
|
|
+ refreshing.value = true
|
|
|
+ }
|
|
|
+
|
|
|
+ // Convert 'current' and 'prev' values to actual date strings
|
|
|
+ let monthValue = '';
|
|
|
+ if (selectedMonth.value === 'custom') {
|
|
|
+ monthValue = customDate.value;
|
|
|
+ } else if (selectedMonth.value === 'current') {
|
|
|
+ const now = new Date();
|
|
|
+ const m = now.getMonth() + 1
|
|
|
+ let mStr = m.toString()
|
|
|
+ if (m < 10) {
|
|
|
+ mStr = '0' + mStr
|
|
|
+ }
|
|
|
+ monthValue = `${now.getFullYear()}-${mStr}`;
|
|
|
+ } else if (selectedMonth.value === 'prev') {
|
|
|
+ const now = new Date();
|
|
|
+ const prevMonth = now.getMonth(); // getMonth() returns 0-11
|
|
|
+ const prevYear = prevMonth === 0 ? now.getFullYear() - 1 : now.getFullYear();
|
|
|
+ let monthStr = prevMonth === 0 ? '12' : prevMonth.toString()
|
|
|
+ if (prevMonth !== 0 && prevMonth < 10) {
|
|
|
+ monthStr = '0' + monthStr
|
|
|
+ }
|
|
|
+ monthValue = `${prevYear}-${monthStr}`;
|
|
|
+ }
|
|
|
+
|
|
|
+ const params: UTSJSONObject = {
|
|
|
+ pageNum: currentPage.value,
|
|
|
+ pageSize: 10,
|
|
|
+ keyword: searchKeyword.value,
|
|
|
+ scoringStatus: 'to_self,to_re,to_confirm,to_final',
|
|
|
+ month: monthValue
|
|
|
+ }
|
|
|
+
|
|
|
+ listMobileOrderScores(params).then((response: any) => {
|
|
|
+ // 提取响应数据
|
|
|
+ const resultObj = response as UTSJSONObject
|
|
|
+ console.log('API响应数据:', resultObj)
|
|
|
+ const responseData = resultObj['rows'] as any[]
|
|
|
+ const responseTotal = resultObj['total'] as number
|
|
|
+
|
|
|
+ if (shouldRefresh) {
|
|
|
+ orderList.value = Array.isArray(responseData) ? responseData : []
|
|
|
+ } else {
|
|
|
+ const currentRows = Array.isArray(responseData) ? responseData : []
|
|
|
+ orderList.value = [...orderList.value, ...currentRows]
|
|
|
+ }
|
|
|
+
|
|
|
+ hasMore.value = orderList.value.length < responseTotal
|
|
|
+ loading.value = false
|
|
|
+ refreshing.value = false
|
|
|
+ }).catch(() => {
|
|
|
+ loading.value = false
|
|
|
+ refreshing.value = false
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ function handleSearch() {
|
|
|
+ loadData(true)
|
|
|
+ }
|
|
|
+
|
|
|
+ function clearSearch() {
|
|
|
+ searchKeyword.value = ""
|
|
|
+ loadData(true)
|
|
|
+ }
|
|
|
+
|
|
|
+ function filterByStatus(status: string) {
|
|
|
+ statusFilter.value = status
|
|
|
+ loadData(true)
|
|
|
+ getStatistics()
|
|
|
+ }
|
|
|
+
|
|
|
+ function changeMonth(month: string) {
|
|
|
+ selectedMonth.value = month
|
|
|
+ //loadData(true)
|
|
|
+ getStatistics()
|
|
|
+ }
|
|
|
+
|
|
|
+ function loadMore() {
|
|
|
+ if (!hasMore.value || loading.value) return
|
|
|
+
|
|
|
+ currentPage.value++
|
|
|
+ loadData(false)
|
|
|
+ }
|
|
|
+
|
|
|
+ function handleRefresh() {
|
|
|
+ loadData(true)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 点击列表项
|
|
|
+ function handleItemClick(item: any | null, index: number): void {
|
|
|
+ if (item == null) return
|
|
|
+ const itemObj = item as UTSJSONObject
|
|
|
+ const id = itemObj.get('id')
|
|
|
+ const orderType = itemObj.get('orderType')
|
|
|
+ if (id != null && orderType != null) {
|
|
|
+ // 转换为字符串
|
|
|
+ const idStr = '' + id
|
|
|
+ const orderTypeStr = '' + orderType
|
|
|
+ uni.navigateTo({
|
|
|
+ url: `/pages/score/detail/index?id=${idStr}&orderType=${orderTypeStr}`
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function formatNumber(value: number | null) {
|
|
|
+ if (value === null) return '0.0'
|
|
|
+ return value.toFixed(2)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取工单评分状态样式类
|
|
|
+ function getStatusClass(item: any | null): string {
|
|
|
+ if (item == null) return ''
|
|
|
+ const rawStatus = getPropertyValue(item, 'scoringStatus')
|
|
|
+ if (rawStatus == null || rawStatus == '') return ''
|
|
|
+ return `status-${rawStatus}`
|
|
|
+ }
|
|
|
+
|
|
|
+ function formatDate(dateString: string) {
|
|
|
+ if (dateString == '' || dateString == null) return ''
|
|
|
+ const date = new Date(dateString)
|
|
|
+ const year = date.getFullYear()
|
|
|
+ const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
|
|
+ const day = date.getDate().toString().padStart(2, '0')
|
|
|
+ const hours = date.getHours().toString().padStart(2, '0')
|
|
|
+ const minutes = date.getMinutes().toString().padStart(2, '0')
|
|
|
+ return `${year}-${month}-${day} ${hours}:${minutes}`
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生命周期
|
|
|
+ onMounted(() => {
|
|
|
+ loadStatusDictList()
|
|
|
+ loadInspectionTypeDictList()
|
|
|
+ loadMaintenanceTypeDictList()
|
|
|
+ loadData(false)
|
|
|
+ getStatistics()
|
|
|
+ dictLoaded.value = true
|
|
|
+ })
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss">
|
|
|
+.list-page {
|
|
|
+ flex: 1;
|
|
|
+ background-color: #e8f0f9;
|
|
|
+ // padding-bottom: 150rpx; // 为底部 TabBar 留出空间
|
|
|
+}
|
|
|
+
|
|
|
+/* 搜索栏样式 */
|
|
|
+.search-bar {
|
|
|
+ padding: 20rpx 30rpx;
|
|
|
+ background-color: #d7eafe;
|
|
|
+}
|
|
|
+
|
|
|
+.search-box {
|
|
|
+ flex-direction: row;
|
|
|
+ align-items: center;
|
|
|
+ height: 72rpx;
|
|
|
+ padding: 0 24rpx;
|
|
|
+ background-color: #f5f5f5;
|
|
|
+ border-radius: 36rpx;
|
|
|
+
|
|
|
+ .search-icon {
|
|
|
+ width: 32rpx;
|
|
|
+ height: 32rpx;
|
|
|
+ margin-right: 12rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .search-input {
|
|
|
+ flex: 1;
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #333333;
|
|
|
+ }
|
|
|
+
|
|
|
+ .clear-icon {
|
|
|
+ margin-left: 12rpx;
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #999999;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* 工单状态筛选 */
|
|
|
+.status-bar {
|
|
|
+ padding-bottom: 10px;
|
|
|
+ padding-left: 30rpx;
|
|
|
+ background-color: #d7eafe;
|
|
|
+}
|
|
|
+
|
|
|
+.status-box {
|
|
|
+ flex-direction: row;
|
|
|
+ align-items: center;
|
|
|
+ height: 72rpx;
|
|
|
+ flex: 1;
|
|
|
+
|
|
|
+ .status-txt {
|
|
|
+ padding: 8px 12px;
|
|
|
+ text-align: center;
|
|
|
+ margin-right: 12rpx;
|
|
|
+ border-radius: 36rpx;
|
|
|
+ background-color: #fff;
|
|
|
+ font-size: 28rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .stauts-sel {
|
|
|
+ background-color: #007AFF;
|
|
|
+ color: #fff;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* 工分统计 */
|
|
|
+.stats-section {
|
|
|
+ margin: 15rpx 30rpx;
|
|
|
+ background-color: #ffffff;
|
|
|
+ border-radius: 16rpx;
|
|
|
+ padding: 20rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.stats-header {
|
|
|
+ flex-direction: row;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.stats-title {
|
|
|
+ font-size: 32rpx;
|
|
|
+ font-weight: bold;
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.month-filters {
|
|
|
+ flex-direction: row;
|
|
|
+ justify-content: flex-end;
|
|
|
+}
|
|
|
+
|
|
|
+.month-filter {
|
|
|
+ padding: 6rpx 16rpx;
|
|
|
+ margin-left: 12rpx;
|
|
|
+ font-size: 24rpx;
|
|
|
+ border-radius: 24rpx;
|
|
|
+ background-color: #f2f3f5;
|
|
|
+ color: #666;
|
|
|
+ white-space: nowrap;
|
|
|
+}
|
|
|
+
|
|
|
+.month-filter-sel {
|
|
|
+ background-color: #165dff;
|
|
|
+ color: white;
|
|
|
+}
|
|
|
+
|
|
|
+.stats-content {
|
|
|
+ margin-top: 20rpx;
|
|
|
+ flex-direction: column;
|
|
|
+}
|
|
|
+
|
|
|
+.total-score {
|
|
|
+ margin-bottom: 30rpx;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.score-label {
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #666;
|
|
|
+ display: flex;
|
|
|
+ margin-bottom: 8rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.score-value {
|
|
|
+ display: flex;
|
|
|
+ font-size: 48rpx;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #165dff;
|
|
|
+ line-height: 1.2;
|
|
|
+}
|
|
|
+
|
|
|
+.score-breakdown {
|
|
|
+ flex-direction: row;
|
|
|
+ justify-content: space-between;
|
|
|
+}
|
|
|
+
|
|
|
+.breakdown-item {
|
|
|
+ flex: 1;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.breakdown-label {
|
|
|
+ display: flex;
|
|
|
+ font-size: 32rpx;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #333;
|
|
|
+ line-height: 1.4;
|
|
|
+}
|
|
|
+
|
|
|
+.breakdown-value {
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #666;
|
|
|
+ display: flex;
|
|
|
+ margin-bottom: 8rpx;
|
|
|
+}
|
|
|
+
|
|
|
+/* 列表项样式 */
|
|
|
+.list-item {
|
|
|
+ margin: 12rpx 30rpx;
|
|
|
+ background-color: #ffffff;
|
|
|
+ border-radius: 16rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.item-container {
|
|
|
+ padding: 30rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.item-header {
|
|
|
+ flex-direction: row;
|
|
|
+ align-items: flex-start;
|
|
|
+ margin-bottom: 16rpx;
|
|
|
+
|
|
|
+ .item-title {
|
|
|
+ font-size: 30rpx;
|
|
|
+ color: #333333;
|
|
|
+ font-weight: bold;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ flex: 0 1 70%;
|
|
|
+ min-width: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .info-value {
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #999999;
|
|
|
+ margin-left: auto;
|
|
|
+ flex: 0 0 auto;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.info-row {
|
|
|
+ flex-direction: row;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ .info-label {
|
|
|
+ font-size: 26rpx;
|
|
|
+ color: #666;
|
|
|
+ }
|
|
|
+
|
|
|
+ .info-value-row {
|
|
|
+ flex-direction: row;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .score-text {
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #ff9900;
|
|
|
+ font-weight: bold;
|
|
|
+ margin-right: 20rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .status-text {
|
|
|
+ font-size: 24rpx;
|
|
|
+ padding: 4rpx 12rpx;
|
|
|
+ border-radius: 24rpx;
|
|
|
+ background-color: #f2f3f5;
|
|
|
+ color: #666;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.text-gray{
|
|
|
+ font-size: 26rpx;
|
|
|
+ color: #666;
|
|
|
+}
|
|
|
+
|
|
|
+/* 日期选择弹窗 */
|
|
|
+.date-picker-popup {
|
|
|
+ background-color: white;
|
|
|
+ border-top-left-radius: 30rpx;
|
|
|
+ border-top-right-radius: 30rpx;
|
|
|
+ padding: 40rpx;
|
|
|
+ padding-bottom: 40rpx;
|
|
|
+ margin-bottom: 150rpx; /* 提高弹窗,避免被底部导航栏遮挡 */
|
|
|
+}
|
|
|
+
|
|
|
+.popup-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 40rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.popup-title {
|
|
|
+ font-size: 34rpx;
|
|
|
+ font-weight: bold;
|
|
|
+}
|
|
|
+
|
|
|
+.popup-actions {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: row;
|
|
|
+}
|
|
|
+
|
|
|
+.cancel-btn {
|
|
|
+ color: #999;
|
|
|
+ padding: 10rpx 20rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.confirm-btn {
|
|
|
+ color: #165dff;
|
|
|
+ padding: 10rpx 20rpx;
|
|
|
+ font-weight: bold;
|
|
|
+}
|
|
|
+
|
|
|
+.date-picker-container {
|
|
|
+ padding: 20rpx 0;
|
|
|
+ padding-bottom: env(safe-area-inset-bottom); // 适配安全区域
|
|
|
+}
|
|
|
+
|
|
|
+.date-picker-item {
|
|
|
+ margin-bottom: 40rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.date-label {
|
|
|
+ display: flex;
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+ font-size: 30rpx;
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+
|
|
|
+.date-display {
|
|
|
+ width: 100%;
|
|
|
+ padding: 20rpx;
|
|
|
+ border: 2rpx solid #ddd;
|
|
|
+ border-radius: 8rpx;
|
|
|
+ font-size: 32rpx;
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+
|
|
|
+// 添加底部填充的类
|
|
|
+.list-with-padding {
|
|
|
+ padding-bottom: 40rpx;
|
|
|
+}
|
|
|
+
|
|
|
+/* 工单评分状态标签样式 */
|
|
|
+.status-tag {
|
|
|
+ padding: 8rpx 20rpx;
|
|
|
+ border-radius: 20rpx;
|
|
|
+ font-size: 24rpx;
|
|
|
+ white-space: nowrap;
|
|
|
+ margin-left: 70rpx;
|
|
|
+ border: 1rpx solid;
|
|
|
+}
|
|
|
+
|
|
|
+/* 待自评 */
|
|
|
+.status-to_self {
|
|
|
+ background-color: #fff7e6;
|
|
|
+ color: #fa8c16;
|
|
|
+ border-color: #ffd591;
|
|
|
+}
|
|
|
+
|
|
|
+/* 待复评 */
|
|
|
+.status-to_re {
|
|
|
+ background-color: #ebf5ff;
|
|
|
+ color: #409eff;
|
|
|
+ border-color: #d8ebff;
|
|
|
+}
|
|
|
+
|
|
|
+/* 待确认 */
|
|
|
+.status-to_confirm {
|
|
|
+ background-color: #f0f9eb;
|
|
|
+ color: #5cb87a;
|
|
|
+ border-color: #c2e7b0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/* 待终评 */
|
|
|
+.status-to_final {
|
|
|
+ background-color: #ebf5ff;
|
|
|
+ color: #409eff;
|
|
|
+ border-color: #d8ebff;
|
|
|
+}
|
|
|
+
|
|
|
+/* 待归档 */
|
|
|
+.status-to_archive {
|
|
|
+ background-color: #f0f9eb;
|
|
|
+ color: #5cb87a;
|
|
|
+ border-color: #c2e7b0;
|
|
|
+}
|
|
|
+
|
|
|
+/* 已归档 */
|
|
|
+.status-archived {
|
|
|
+ background-color: #f0f9eb;
|
|
|
+ color: #5cb87a;
|
|
|
+ border-color: #c2e7b0;
|
|
|
+}
|
|
|
+</style>
|
|
|
+
|