代码提交-3-13

This commit is contained in:
2026-03-13 14:32:24 +08:00
parent 3ca2451b2f
commit ae4aede94d
27 changed files with 2285 additions and 517 deletions

View File

@@ -7,24 +7,84 @@
</template>
</uni-section>
<!-- 汇总图表 -->
<qiun-data-charts type="ring" :opts="opts" :chartData="chartData" :errorShow="false" :tapLegend="false" />
<!-- 统计数据卡片 -->
<uni-row :width="730" class="stats">
<uni-col :span="6"><view class="stat"><text class="stat-label">总打卡</text><text class="stat-value">{{ stats.total }}</text></view></uni-col>
<uni-col :span="6"><view class="stat"><text class="stat-label">活跃天</text><text class="stat-value">{{ stats.activeDays }}</text></view></uni-col>
<uni-col :span="6"><view class="stat"><text class="stat-label">平均/</text><text class="stat-value">{{ stats.avgPerDay }}</text></view></uni-col>
<uni-col :span="6"><view class="stat"><text class="stat-label">最长连打</text><text class="stat-value">{{ stats.maxStreak }}</text></view></uni-col>
</uni-row>
<!-- 统计概览 -->
<uni-section class="mb-10" title="统计概览" type="line" />
<view class="charts">
<view class="chart-box">
<qiun-data-charts type="column" :opts="optsBar" :chartData="chartWeek" :errorShow="false" />
</view>
<view class="chart-box">
<qiun-data-charts type="column" :opts="optsBar" :chartData="chartHour" :errorShow="false" />
</view>
</view>
<!-- 打卡数据卡片样式与异常巡检一致 -->
<uni-card v-if="summary.clockIn > 0 || latestRecord" :title="'本月总打卡:' + (summary.clockIn || 0)" :thumbnail="avatar" @click="onSummaryClick">
<uni-row class="demo-uni-row" :width="nvueWidth">
<!-- 左侧信息 -->
<uni-col :span="16">
<view>
<text class="uni-body">打卡状态{{ summary.clockState || '-' }}</text>
</view>
<view v-if="latestRecord">
<text class="uni-body">最近时间{{ formatDate(latestRecord.inspectionTime) }}</text>
</view>
<view v-if="latestRecord">
<text class="uni-body">巡检点{{ latestRecord.inspectionPoint || '-' }}</text>
</view>
<view v-if="latestRecord">
<text class="uni-body">巡检人{{ latestRecord.inspectorUser || '-' }}</text>
</view>
</uni-col>
<!-- 右侧缩略图 -->
<uni-col :span="8" v-if="latestRecord">
<view class="thumbs" v-if="firstThreeImages(latestImages).length">
<image v-for="(img,i) in firstThreeImages(latestImages)" :key="i" :src="imageUrl(img)" mode="aspectFill" class="thumb" @click.stop="previewImages(latestImages, i)" />
</view>
<view class="tag-view">
<uni-tag :inverted="true" :circle="true" text="已打卡" size="small" />
</view>
</uni-col>
</uni-row>
</uni-card>
<uni-section class="mb-10" title="打卡列表" List="info">
<template v-slot:right>
<uni-data-select style="width:70px;" :clear=false v-model="queryform.weekType" :localdata="weekList"
@change="weekTypeSelectChange"></uni-data-select>
</template>
</uni-section>
<uni-card v-for="item of cardList" :title="item.inspectionPoint" :thumbnail="avatar">
<!-- 列表卡片点击可查看详情 -->
<uni-card v-for="item of cardList" :key="item.id || item.recordId" :title="item.inspectionPoint" :thumbnail="avatar" @click="onCardClick($event, item)">
<uni-row class="demo-uni-row" :width="nvueWidth">
<uni-col :span="20">
<!-- 左侧信息 -->
<uni-col :span="16">
<view>
<text class="uni-body">
打卡时间{{item.inspectionTime}}
</text>
<text class="uni-body">打卡时间{{ formatDate(item.inspectionTime) }}</text>
</view>
<view>
<text class="uni-body">巡检人{{ item.inspectorUser || '-' }}</text>
</view>
<view v-if="item.remark">
<text class="uni-body">备注{{ item.remark }}</text>
</view>
</uni-col>
<uni-col :span="4">
<!-- 右侧缩略图 -->
<uni-col :span="8">
<view class="thumbs" v-if="firstThreeImages(item.inspectionImg || item.clockInImg || item.signImg).length">
<image v-for="(img,i) in firstThreeImages(item.inspectionImg || item.clockInImg || item.signImg)" :key="i" :src="imageUrl(img)" mode="aspectFill" class="thumb" @click.stop="previewImages(item.inspectionImg || item.clockInImg || item.signImg, i)" />
</view>
<view class="tag-view">
<uni-tag :inverted="true" :circle="true" text="已打卡" size="small" />
</view>
@@ -38,9 +98,8 @@
<script>
import {
listRecordView
} from '@/api/inspection/record.js'
import { listRecordView, getRecord } from '@/api/inspection/record.js'
import config from '@/config'
export default {
data() {
return {
@@ -50,6 +109,10 @@
// 数据可视化
inspectionRecordViewTable: [],
cardList: [],
// 汇总卡片数据
summary: { clockIn: 0, clockState: '' },
latestRecord: null,
latestImages: [],
weekList: [{
text: "全部",
value: 0
@@ -128,92 +191,97 @@
chartData: {},
//这里的 opts 是图表类型 type="ring" 的全部配置参数,您可以将此配置复制到 config-ucharts.js 文件中下标为 ['ring'] 的节点中来覆盖全局默认参数。实际应用过程中 opts 只需传入与全局默认参数中不一致的【某一个属性】即可实现同类型的图表显示不同的样式,达到页面简洁的需求。
opts: {
timing: "easeOut",
duration: 1000,
rotate: false,
rotateLock: false,
color: ["#1890FF", "#91CB74", "#FAC858", "#EE6666", "#73C0DE", "#3CA272", "#FC8452", "#9A60B4", "#ea7ccc"],
padding: [5, 5, 5, 5],
fontSize: 13,
fontColor: "#666666",
dataLabel: false,
dataPointShape: false,
dataPointShapeType: "hollow",
touchMoveLimit: 60,
enableScroll: false,
enableMarkLine: false,
legend: {
show: true,
position: "bottom",
lineHeight: 25,
float: "center",
padding: 5,
margin: 5,
backgroundColor: "rgba(0,0,0,0)",
borderColor: "rgba(0,0,0,0)",
borderWidth: 0,
fontSize: 13,
fontColor: "#666666",
hiddenColor: "#CECECE",
itemGap: 10
},
title: {
name: "已打卡",
fontSize: 15,
color: "#666666",
offsetX: 0,
offsetY: 0
},
subtitle: {
name: "30",
fontSize: 25,
color: "#fbbd08",
offsetX: 0,
offsetY: 0
},
extra: {
ring: {
ringWidth: 30,
activeOpacity: 0.5,
activeRadius: 10,
offsetAngle: 0,
labelWidth: 15,
border: false,
borderWidth: 3,
borderColor: "#FFFFFF",
centerColor: "#FFFFFF",
customRadius: 0,
linearType: "none"
},
tooltip: {
showBox: true,
showArrow: true,
showCategory: false,
borderWidth: 0,
borderRadius: 0,
borderColor: "#000000",
borderOpacity: 0.7,
bgColor: "#000000",
bgOpacity: 0.7,
gridType: "solid",
dashLength: 4,
gridColor: "#CCCCCC",
boxPadding: 3,
fontSize: 13,
lineHeight: 20,
fontColor: "#FFFFFF",
legendShow: true,
legendShape: "auto",
splitLine: true,
horizentalLine: false,
xAxisLabel: false,
yAxisLabel: false,
labelBgColor: "#FFFFFF",
labelBgOpacity: 0.7,
labelFontColor: "#666666"
}
}
}
timing: "easeOut",
duration: 1000,
rotate: false,
rotateLock: false,
color: ["#1890FF", "#91CB74", "#FAC858", "#EE6666", "#73C0DE", "#3CA272", "#FC8452", "#9A60B4", "#ea7ccc"],
padding: [5, 5, 5, 5],
fontSize: 13,
fontColor: "#666666",
dataLabel: false,
dataPointShape: false,
dataPointShapeType: "hollow",
touchMoveLimit: 60,
enableScroll: false,
enableMarkLine: false,
legend: {
show: true,
position: "bottom",
lineHeight: 25,
float: "center",
padding: 5,
margin: 5,
backgroundColor: "rgba(0,0,0,0)",
borderColor: "rgba(0,0,0,0)",
borderWidth: 0,
fontSize: 13,
fontColor: "#666666",
hiddenColor: "#CECECE",
itemGap: 10
},
title: {
name: "已打卡",
fontSize: 15,
color: "#666666",
offsetX: 0,
offsetY: 0
},
subtitle: {
name: "30",
fontSize: 25,
color: "#fbbd08",
offsetX: 0,
offsetY: 0
},
extra: {
ring: {
ringWidth: 30,
activeOpacity: 0.5,
activeRadius: 10,
offsetAngle: 0,
labelWidth: 15,
border: false,
borderWidth: 3,
borderColor: "#FFFFFF",
centerColor: "#FFFFFF",
customRadius: 0,
linearType: "none"
},
tooltip: {
showBox: true,
showArrow: true,
showCategory: false,
borderWidth: 0,
borderRadius: 0,
borderColor: "#000000",
borderOpacity: 0.7,
bgColor: "#000000",
bgOpacity: 0.7,
gridType: "solid",
dashLength: 4,
gridColor: "#CCCCCC",
boxPadding: 3,
fontSize: 13,
lineHeight: 20,
fontColor: "#FFFFFF",
legendShow: true,
legendShape: "auto",
splitLine: true,
horizentalLine: false,
xAxisLabel: false,
yAxisLabel: false,
labelBgColor: "#FFFFFF",
labelBgOpacity: 0.7,
labelFontColor: "#666666"
}
}
},
// 新增:统计与柱状图数据
stats: { total: 0, activeDays: 0, avgPerDay: 0, maxStreak: 0 },
optsBar: { legend: { show: false }, xAxis: { disableGrid: true }, yAxis: { disableGrid: false } },
chartWeek: { categories: [], series: [{ name: '按周几', data: [] }] },
chartHour: { categories: [], series: [{ name: '按时段', data: [] }] },
};
},
methods: {
@@ -223,6 +291,12 @@
this.queryform.month = currentMonth;
listRecordView(this.queryform).then(res => {
let data = res.data
// 汇总卡片数据
this.summary.clockIn = data.clockIn || 0
this.summary.clockState = data.clockState || ''
this.latestRecord = (data.inspectionRecordTables && data.inspectionRecordTables.length) ? data.inspectionRecordTables[0] : null
this.latestImages = this.latestRecord ? (this.latestRecord.inspectionImg || this.latestRecord.clockInImg || this.latestRecord.signImg || []) : []
// 图表数据
if (data.clockIn != 0) {
let obj = [{
"name": data.clockState,
@@ -232,7 +306,11 @@
} else {
this.inspectionRecordViewTable = []
}
this.cardList = data.inspectionRecordTables
this.cardList = data.inspectionRecordTables || []
// 新增:构建统计与柱状图
this.buildWeekChart(this.cardList)
this.buildHourChart(this.cardList)
this.buildStats(this.cardList)
})
this.getServerData()
},
@@ -278,9 +356,83 @@
this.chartData = JSON.parse(JSON.stringify(res));
}, 1000);
},
// 统一图片数组处理(支持字符串或数组)
firstThreeImages(imgs) {
if (!imgs) return []
if (Array.isArray(imgs)) return imgs.slice(0,3)
if (typeof imgs === 'string') return imgs.split(',').filter(Boolean).slice(0,3)
return []
},
imageUrl(path) {
if (!path) return ''
if (/^https?:\/\//.test(path)) return path
return `${config.baseUrl}${path.startsWith('/') ? path : '/' + path}`
},
previewImages(imgs, index=0) {
const list = Array.isArray(imgs) ? imgs : (typeof imgs === 'string' ? imgs.split(',').filter(Boolean) : [])
if (!list.length) return
uni.previewImage({
current: index,
urls: list.map(this.imageUrl)
})
},
formatDate(dateStr) {
if (!dateStr) return '-'
const d = new Date(dateStr)
if (isNaN(d.getTime())) return dateStr
const pad = n => (n<10?('0'+n):n)
return `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
},
onCardClick(e, item) {
this.goDetail(item)
},
onSummaryClick() {
// 若有最近一条记录点击汇总卡片也进入其详情
if (this.latestRecord) {
this.goDetail(this.latestRecord)
}
},
goDetail(item) {
const id = item.id || item.recordId
if (!id) {
uni.showToast({ title: '记录ID缺失', icon: 'none' })
return
}
uni.navigateTo({ url: `/pages/work/inspection/checkInDetail/index?id=${id}` })
},
// 新增统计构建方法
buildWeekChart(rows) {
const names = ['周一','周二','周三','周四','周五','周六','周日']
const counts = Array(7).fill(0)
(rows || []).forEach(r => { const d = new Date(r.inspectionTime); if (!isNaN(d)) { const idx = (d.getDay()+6)%7; counts[idx]++ } })
this.chartWeek = { categories: names, series: [{ name: '按周几', data: counts }] }
},
buildHourChart(rows) {
const buckets = Array(24).fill(0)
(rows || []).forEach(r => { const d = new Date(r.inspectionTime); if (!isNaN(d)) { buckets[d.getHours()]++ } })
const labels = buckets.map((_,h)=> (h<10?('0'+h):h)+':00')
this.chartHour = { categories: labels, series: [{ name: '按时段', data: buckets }] }
},
buildStats(rows) {
const list = rows || []
const total = list.length
const daySet = new Set(list.map(r => this.formatDate(r.inspectionTime).slice(0,10)))
const activeDays = daySet.size
const avgPerDay = activeDays ? (total/activeDays).toFixed(2) : 0
// 计算最长连续天数
const days = Array.from(daySet).sort()
let maxStreak = 0, cur = 0, prev = null
const toDate = s => new Date(s+'T00:00:00')
days.forEach(s => { if (!prev) { cur=1; } else { const diff=(toDate(s)-toDate(prev))/(24*3600*1000); cur = diff===1 ? cur+1 : 1 } maxStreak = Math.max(maxStreak, cur); prev = s })
this.stats = { total, activeDays, avgPerDay, maxStreak }
}
},
created() {
this.getList()
},
onShow() {
// 返回页面时自动刷新列表,保持数据最新
this.getList()
}
};
</script>
@@ -304,6 +456,19 @@
/* 请根据实际需求修改父元素尺寸,组件自动识别宽高 */
.charts-box {
width: 100%;
height: 300px;
height: auto;
}
.thumbs { display:flex; gap:6px; justify-content:flex-end; }
.thumb { width: 60px; height: 60px; border-radius: 4px; background:#f5f5f5; }
.tag-view { margin-top: 8px; display:flex; justify-content:flex-end; }
/* 新增:统计与图表布局样式 */
.stats { margin: 10px 0; }
.stat { background:#fff; border-radius:8px; padding:10px; display:flex; flex-direction:column; align-items:center }
.stat-label { color:#666; font-size:12px }
.stat-value { color:#1677ff; font-weight:bold; font-size:18px }
.charts { display: grid; grid-template-columns: 1fr; gap: 12px; }
@media (min-width: 380px) { .charts { grid-template-columns: 1fr 1fr; } }
.chart-box { background: #fff; border-radius: 8px; padding: 6px; }
</style>