feat 项目列表
This commit is contained in:
@@ -1,17 +1,51 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { Project } from '@/service/project'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
item: Project
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const statusMap: Record<number, string> = {
|
||||||
|
0: '未完成',
|
||||||
|
1: '已完成',
|
||||||
|
2: '作业中',
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusText = computed(() => statusMap[props.item.status] || '未知')
|
||||||
|
|
||||||
|
const durationText = computed(() => formatDuration(props.item.total_duration))
|
||||||
|
|
||||||
|
const areaText = computed(() => formatArea(props.item.total_compaction_area))
|
||||||
|
|
||||||
|
const passText = computed(() => `${props.item.total_pass_count ?? 0}遍`)
|
||||||
|
|
||||||
|
function formatDuration(value?: number) {
|
||||||
|
const hours = value ?? 0
|
||||||
|
const text = Number.isInteger(hours) ? String(hours) : hours.toFixed(1)
|
||||||
|
return `${text}h`
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatArea(value?: number) {
|
||||||
|
const area = value ?? 0
|
||||||
|
const text = Number.isInteger(area) ? String(area) : String(area)
|
||||||
|
return `${text}m²`
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view class="project-item">
|
<view class="project-item">
|
||||||
<view class="project-info">
|
<view class="project-info">
|
||||||
<view class="avatar" />
|
<view class="avatar" />
|
||||||
<view class="info">
|
<view class="info">
|
||||||
<text class="name">
|
<text class="name">
|
||||||
G6高速改扩建项目
|
{{ item.project_name }}
|
||||||
</text>
|
</text>
|
||||||
<text class="sub">
|
<text class="sub">
|
||||||
K1+200-K1+800
|
{{ item.stake_range || '-' }}
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="status-tag">
|
<view class="status-tag" :class="`status-${item.status}`">
|
||||||
作业中
|
{{ statusText }}
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="project-params">
|
<view class="project-params">
|
||||||
@@ -20,7 +54,7 @@
|
|||||||
作业时长
|
作业时长
|
||||||
</text>
|
</text>
|
||||||
<text class="param-value">
|
<text class="param-value">
|
||||||
8h
|
{{ durationText }}
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="param">
|
<view class="param">
|
||||||
@@ -28,7 +62,7 @@
|
|||||||
压实面积
|
压实面积
|
||||||
</text>
|
</text>
|
||||||
<text class="param-value">
|
<text class="param-value">
|
||||||
28.5m²
|
{{ areaText }}
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="param">
|
<view class="param">
|
||||||
@@ -36,11 +70,11 @@
|
|||||||
碾压遍数
|
碾压遍数
|
||||||
</text>
|
</text>
|
||||||
<text class="param-value">
|
<text class="param-value">
|
||||||
10遍
|
{{ passText }}
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="project-actions">
|
<view v-if="item.status !== 1" class="project-actions">
|
||||||
<wd-button size="large" round>
|
<wd-button size="large" round>
|
||||||
开始项目
|
开始项目
|
||||||
</wd-button>
|
</wd-button>
|
||||||
@@ -55,6 +89,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.project-item + .project-item {
|
||||||
|
margin-top: 16px;
|
||||||
|
padding-top: 16px;
|
||||||
|
border-top: 1px solid #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
.project-info {
|
.project-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -89,12 +129,22 @@
|
|||||||
|
|
||||||
.status-tag {
|
.status-tag {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #52c41a;
|
color: #faad14;
|
||||||
padding: 2px 8px;
|
padding: 2px 8px;
|
||||||
background-color: #f6ffed;
|
background-color: #fffbe6;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status-tag.status-2 {
|
||||||
|
color: #52c41a;
|
||||||
|
background-color: #f6ffed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-tag.status-1 {
|
||||||
|
color: #999999;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
.project-params {
|
.project-params {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|||||||
@@ -1,9 +1,31 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import type { Project } from '@/service/project'
|
||||||
|
import { onMounted } from 'vue'
|
||||||
|
import { useDbStore } from '@/store/modules/db'
|
||||||
|
|
||||||
|
const db = useDbStore()
|
||||||
|
|
||||||
|
/** 首页只展示最近几条 */
|
||||||
|
const list = computed(() => (db.project || []).slice(0, 2))
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
if (!db.project?.length)
|
||||||
|
await db.loadAll()
|
||||||
|
})
|
||||||
|
|
||||||
function goToProjectList() {
|
function goToProjectList() {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages/project/index',
|
url: '/pages/project/index',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function goToDetail(item: Project) {
|
||||||
|
if (!item.id)
|
||||||
|
return
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/project/index?id=${item.id}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -17,7 +39,15 @@ function goToProjectList() {
|
|||||||
</wd-button>
|
</wd-button>
|
||||||
</view>
|
</view>
|
||||||
<view class="card-body">
|
<view class="card-body">
|
||||||
<home-list />
|
<view v-if="!list.length" class="empty-tip">
|
||||||
|
暂无项目
|
||||||
|
</view>
|
||||||
|
<home-list
|
||||||
|
v-for="item in list"
|
||||||
|
:key="item.id"
|
||||||
|
:item="item"
|
||||||
|
@tap="goToDetail(item)"
|
||||||
|
/>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@@ -47,4 +77,11 @@ function goToProjectList() {
|
|||||||
.card-body {
|
.card-body {
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.empty-tip {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #999999;
|
||||||
|
text-align: center;
|
||||||
|
padding: 16px 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -10,18 +10,18 @@ defineProps<{
|
|||||||
|
|
||||||
function getStatusText(status: number) {
|
function getStatusText(status: number) {
|
||||||
const map: Record<number, string> = {
|
const map: Record<number, string> = {
|
||||||
0: '作业中',
|
0: '未完成',
|
||||||
1: '暂停中',
|
1: '已结束',
|
||||||
2: '已结束',
|
2: '进行中',
|
||||||
}
|
}
|
||||||
return map[status] || '未知'
|
return map[status] || '未知'
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStatusColor(status: number) {
|
function getStatusColor(status: number) {
|
||||||
const map: Record<number, string> = {
|
const map: Record<number, string> = {
|
||||||
0: '#52c41a',
|
0: '#faad14',
|
||||||
1: '#faad14',
|
1: '#999999',
|
||||||
2: '#999999',
|
2: '#52c41a',
|
||||||
}
|
}
|
||||||
return map[status] || '#999999'
|
return map[status] || '#999999'
|
||||||
}
|
}
|
||||||
@@ -79,7 +79,7 @@ function getStatusColor(status: number) {
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 操作按钮 -->
|
||||||
<view v-if="status === 0 || status === 1" class="btn-row">
|
<view v-if="status !== 1" class="btn-row">
|
||||||
<wd-button size="large" type="primary" round>
|
<wd-button size="large" type="primary" round>
|
||||||
开始项目
|
开始项目
|
||||||
</wd-button>
|
</wd-button>
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import type { Project } from '@/service/project'
|
||||||
|
import { computed, onMounted, ref } from 'vue'
|
||||||
|
import { useDbStore } from '@/store/modules/db'
|
||||||
|
|
||||||
definePage({
|
definePage({
|
||||||
layout: false,
|
layout: false,
|
||||||
style: {
|
style: {
|
||||||
@@ -6,32 +10,62 @@ definePage({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const search = ref('')
|
const db = useDbStore()
|
||||||
const activeTab = ref(0)
|
|
||||||
const tabs = ['全部', '进行中', '已结束']
|
|
||||||
|
|
||||||
const projects = [
|
const search = ref('')
|
||||||
{
|
const activeTab = ref(0) // 0全部 1作业中 2已结束
|
||||||
projectName: '徐工XS65H #01',
|
const tabs = ['全部', '作业中', '已结束']
|
||||||
stakeRange: 'K1+200-K1+800',
|
|
||||||
status: 0,
|
const projects = computed(() => {
|
||||||
workTime: '8h',
|
const keyword = search.value.trim().toLowerCase()
|
||||||
compactArea: '28.5m²',
|
|
||||||
rollerCount: '10遍',
|
return db.project
|
||||||
},
|
.filter((item) => {
|
||||||
{
|
if (activeTab.value === 1 && item.status === 1)
|
||||||
projectName: '徐工XS65H #02',
|
return false
|
||||||
stakeRange: 'K2+100-K2+600',
|
if (activeTab.value === 2 && item.status !== 1)
|
||||||
status: 2,
|
return false
|
||||||
workTime: '5h',
|
if (keyword) {
|
||||||
compactArea: '15.2m²',
|
const name = (item.project_name || '').toLowerCase()
|
||||||
rollerCount: '6遍',
|
const stake = (item.stake_range || '').toLowerCase()
|
||||||
},
|
if (!name.includes(keyword) && !stake.includes(keyword))
|
||||||
]
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
.map(mapProjectItem)
|
||||||
|
})
|
||||||
|
|
||||||
|
function mapProjectItem(item: Project) {
|
||||||
|
return {
|
||||||
|
id: item.id,
|
||||||
|
projectName: item.project_name,
|
||||||
|
stakeRange: item.stake_range || '-',
|
||||||
|
status: item.status,
|
||||||
|
workTime: formatDuration(item.total_duration),
|
||||||
|
compactArea: formatArea(item.total_compaction_area),
|
||||||
|
rollerCount: `${item.total_pass_count ?? 0}遍`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDuration(value?: number) {
|
||||||
|
const hours = value ?? 0
|
||||||
|
const text = Number.isInteger(hours) ? String(hours) : hours.toFixed(1)
|
||||||
|
return `${text}h`
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatArea(value?: number) {
|
||||||
|
return `${value ?? 0}m²`
|
||||||
|
}
|
||||||
|
|
||||||
function handleCreate() {
|
function handleCreate() {
|
||||||
console.log('点击了新建项目')
|
console.log('点击了新建项目')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
if (!db.project.length)
|
||||||
|
await db.loadAll()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -63,9 +97,12 @@ function handleCreate() {
|
|||||||
|
|
||||||
<!-- 项目列表 -->
|
<!-- 项目列表 -->
|
||||||
<view class="project-list">
|
<view class="project-list">
|
||||||
|
<view v-if="!projects.length" class="empty-tip">
|
||||||
|
暂无项目
|
||||||
|
</view>
|
||||||
<project-item
|
<project-item
|
||||||
v-for="(item, index) in projects"
|
v-for="item in projects"
|
||||||
:key="index"
|
:key="item.id"
|
||||||
:project-name="item.projectName"
|
:project-name="item.projectName"
|
||||||
:stake-range="item.stakeRange"
|
:stake-range="item.stakeRange"
|
||||||
:status="item.status"
|
:status="item.status"
|
||||||
@@ -126,4 +163,11 @@ function handleCreate() {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.empty-tip {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #999;
|
||||||
|
text-align: center;
|
||||||
|
padding: 24px 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ const tableSqls = [
|
|||||||
|
|
||||||
/** 当前代码期望的各表字段(用于检测旧库结构) */
|
/** 当前代码期望的各表字段(用于检测旧库结构) */
|
||||||
const expectedColumns: Record<string, string[]> = {
|
const expectedColumns: Record<string, string[]> = {
|
||||||
project: ['id', 'project_name', 'stake_range', 'total_area', 'status', 'created_at', 'updated_at'],
|
project: ['id', 'project_name', 'stake_range', 'total_area', 'start_time', 'end_time', 'total_duration', 'total_compaction_area', 'total_pass_count', 'status', 'created_at', 'updated_at'],
|
||||||
work_record: ['id', 'project_id', 'start_time', 'end_time', 'compaction_area', 'pass_count', 'created_at'],
|
work_record: ['id', 'project_id', 'start_time', 'end_time', 'duration', 'compaction_area', 'pass_count', 'exception_count', 'created_at'],
|
||||||
rtk_track: ['id', 'work_record_id', 'latitude', 'longitude', 'speed', 'heading', 'steel_wheel_temp', 'compaction_value', 'created_at'],
|
rtk_track: ['id', 'work_record_id', 'latitude', 'longitude', 'speed', 'heading', 'steel_wheel_temp', 'compaction_value', 'created_at'],
|
||||||
warning_record: ['id', 'project_id', 'device_name', 'exception_type', 'exception_level', 'occurred_at', 'site_description', 'disposal_record', 'disposal_type', 'is_read', 'created_at'],
|
warning_record: ['id', 'project_id', 'device_name', 'exception_type', 'exception_level', 'occurred_at', 'site_description', 'disposal_record', 'disposal_type', 'is_read', 'created_at'],
|
||||||
history_report: ['id', 'project_id', 'report_name', 'device_id', 'construction_area', 'start_time', 'end_time', 'duration', 'compaction_area', 'pass_count', 'qualified_rate', 'avg_speed', 'avg_paving_temp', 'avg_compaction', 'exception_count', 'created_at'],
|
history_report: ['id', 'project_id', 'report_name', 'device_id', 'construction_area', 'start_time', 'end_time', 'duration', 'compaction_area', 'pass_count', 'qualified_rate', 'avg_speed', 'avg_paving_temp', 'avg_compaction', 'exception_count', 'created_at'],
|
||||||
|
|||||||
@@ -7,6 +7,11 @@ export const projectTableSql = `CREATE TABLE IF NOT EXISTS project (
|
|||||||
project_name TEXT NOT NULL,
|
project_name TEXT NOT NULL,
|
||||||
stake_range TEXT,
|
stake_range TEXT,
|
||||||
total_area REAL,
|
total_area REAL,
|
||||||
|
start_time DATETIME,
|
||||||
|
end_time DATETIME,
|
||||||
|
total_duration REAL DEFAULT 0,
|
||||||
|
total_compaction_area REAL DEFAULT 0,
|
||||||
|
total_pass_count INTEGER DEFAULT 0,
|
||||||
status INTEGER DEFAULT 0,
|
status INTEGER DEFAULT 0,
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
@@ -17,7 +22,12 @@ export interface Project {
|
|||||||
project_name: string // 项目名称,必填
|
project_name: string // 项目名称,必填
|
||||||
stake_range: string // 项目范围,可选
|
stake_range: string // 项目范围,可选
|
||||||
total_area: number // 项目总面积,可选
|
total_area: number // 项目总面积,可选
|
||||||
status: 0 | 1 | 2 // 项目状态,0:未完成,1:已完成,默认0,. 进行中2
|
start_time?: string // 项目开始时间
|
||||||
|
end_time?: string // 项目结束时间
|
||||||
|
total_duration?: number // 目前作业进行总时长,默认 0
|
||||||
|
total_compaction_area?: number // 当前总共的压实面积,默认 0
|
||||||
|
total_pass_count?: number // 当前总共的碾压遍数,默认 0
|
||||||
|
status: 0 | 1 | 2 // 项目状态,0:未完成,1:已完成,2:作业中, 默认0
|
||||||
created_at?: string // 创建时间,默认当前时间
|
created_at?: string // 创建时间,默认当前时间
|
||||||
updated_at?: string // 更新时间,默认当前时间
|
updated_at?: string // 更新时间,默认当前时间
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import DB from '@/utils/sqlite'
|
import DB from '@/utils/sqlite'
|
||||||
|
import { insert as insertWorkRecord } from './workRecord'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 插入项目假数据(3条)
|
* 插入项目假数据(3条)
|
||||||
@@ -33,7 +34,8 @@ async function seedProject() {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 插入作业记录假数据(3条)
|
* 插入作业记录假数据(3条)
|
||||||
* 字段:project_id / start_time / end_time / compaction_area / pass_count
|
* 走 workRecord.insert,事务同步累加 project 汇总字段
|
||||||
|
* 字段:project_id / start_time / end_time / duration / compaction_area / pass_count
|
||||||
*/
|
*/
|
||||||
async function seedWorkRecord() {
|
async function seedWorkRecord() {
|
||||||
const data = [
|
const data = [
|
||||||
@@ -41,6 +43,7 @@ async function seedWorkRecord() {
|
|||||||
project_id: 1,
|
project_id: 1,
|
||||||
start_time: '2026-09-10 08:00:00',
|
start_time: '2026-09-10 08:00:00',
|
||||||
end_time: '2026-09-10 12:00:00',
|
end_time: '2026-09-10 12:00:00',
|
||||||
|
duration: 4,
|
||||||
compaction_area: 1250.5,
|
compaction_area: 1250.5,
|
||||||
pass_count: 6,
|
pass_count: 6,
|
||||||
},
|
},
|
||||||
@@ -48,6 +51,7 @@ async function seedWorkRecord() {
|
|||||||
project_id: 1,
|
project_id: 1,
|
||||||
start_time: '2026-09-10 13:00:00',
|
start_time: '2026-09-10 13:00:00',
|
||||||
end_time: '2026-09-10 17:30:00',
|
end_time: '2026-09-10 17:30:00',
|
||||||
|
duration: 4.5,
|
||||||
compaction_area: 1680.0,
|
compaction_area: 1680.0,
|
||||||
pass_count: 8,
|
pass_count: 8,
|
||||||
},
|
},
|
||||||
@@ -55,13 +59,14 @@ async function seedWorkRecord() {
|
|||||||
project_id: 2,
|
project_id: 2,
|
||||||
start_time: '2026-09-09 07:30:00',
|
start_time: '2026-09-09 07:30:00',
|
||||||
end_time: '2026-09-09 11:30:00',
|
end_time: '2026-09-09 11:30:00',
|
||||||
|
duration: 4,
|
||||||
compaction_area: 980.3,
|
compaction_area: 980.3,
|
||||||
pass_count: 5,
|
pass_count: 5,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
for (const item of data) {
|
for (const item of data) {
|
||||||
await DB.insert('work_record', item)
|
await insertWorkRecord(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// 工作记录表
|
// 当日工作记录表
|
||||||
import DB from '@/utils/sqlite'
|
import DB from '@/utils/sqlite'
|
||||||
|
|
||||||
const TABLE = 'work_record'
|
const TABLE = 'work_record'
|
||||||
@@ -8,8 +8,10 @@ export const workRecordTableSql = `CREATE TABLE IF NOT EXISTS work_record (
|
|||||||
project_id INTEGER,
|
project_id INTEGER,
|
||||||
start_time DATETIME,
|
start_time DATETIME,
|
||||||
end_time DATETIME,
|
end_time DATETIME,
|
||||||
|
duration REAL,
|
||||||
compaction_area REAL,
|
compaction_area REAL,
|
||||||
pass_count INTEGER,
|
pass_count INTEGER,
|
||||||
|
exception_count INTEGER DEFAULT 0,
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
FOREIGN KEY (project_id) REFERENCES project(id)
|
FOREIGN KEY (project_id) REFERENCES project(id)
|
||||||
)`
|
)`
|
||||||
@@ -19,11 +21,57 @@ export interface WorkRecord {
|
|||||||
project_id: number // 所属项目 ID,关联 project.id
|
project_id: number // 所属项目 ID,关联 project.id
|
||||||
start_time?: string // 作业开始时间
|
start_time?: string // 作业开始时间
|
||||||
end_time?: string // 作业结束时间
|
end_time?: string // 作业结束时间
|
||||||
|
duration?: number // 作业时长
|
||||||
compaction_area?: number // 压实面积
|
compaction_area?: number // 压实面积
|
||||||
pass_count?: number // 碾压遍数
|
pass_count?: number // 碾压遍数
|
||||||
|
exception_count?: number // 异常次数,默认 0
|
||||||
created_at?: string // 创建时间,默认当前时间
|
created_at?: string // 创建时间,默认当前时间
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function escapeValue(value: any): string {
|
||||||
|
if (value === null || value === undefined)
|
||||||
|
return 'NULL'
|
||||||
|
if (typeof value === 'number')
|
||||||
|
return String(value)
|
||||||
|
if (typeof value === 'boolean')
|
||||||
|
return value ? '1' : '0'
|
||||||
|
return `'${String(value).replace(/'/g, '\'\'')}'`
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildInsertSql(data: Record<string, any>): string {
|
||||||
|
const keys = Object.keys(data)
|
||||||
|
const values = keys.map(k => escapeValue(data[k]))
|
||||||
|
return `INSERT INTO ${TABLE} (${keys.join(', ')}) VALUES (${values.join(', ')})`
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildUpdateSql(data: Record<string, any>, where: string): string {
|
||||||
|
const sets = Object.keys(data).map(k => `${k} = ${escapeValue(data[k])}`)
|
||||||
|
return `UPDATE ${TABLE} SET ${sets.join(', ')} WHERE ${where}`
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 按增量回写项目汇总字段(时长 / 压实面积 / 碾压遍数) */
|
||||||
|
function buildProjectDeltaSql(
|
||||||
|
projectId: number,
|
||||||
|
durationDelta: number,
|
||||||
|
compactionDelta: number,
|
||||||
|
passDelta: number,
|
||||||
|
): string | null {
|
||||||
|
if (durationDelta === 0 && compactionDelta === 0 && passDelta === 0)
|
||||||
|
return null
|
||||||
|
|
||||||
|
const now = escapeValue(new Date().toISOString())
|
||||||
|
return `UPDATE project SET
|
||||||
|
total_duration = COALESCE(total_duration, 0) + (${durationDelta}),
|
||||||
|
total_compaction_area = COALESCE(total_compaction_area, 0) + (${compactionDelta}),
|
||||||
|
total_pass_count = COALESCE(total_pass_count, 0) + (${passDelta}),
|
||||||
|
updated_at = ${now}
|
||||||
|
WHERE id = ${projectId}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function num(value: number | undefined | null): number {
|
||||||
|
return value ?? 0
|
||||||
|
}
|
||||||
|
|
||||||
/** 查询全部作业记录 */
|
/** 查询全部作业记录 */
|
||||||
export async function selectAll(): Promise<WorkRecord[]> {
|
export async function selectAll(): Promise<WorkRecord[]> {
|
||||||
return DB.select(TABLE)
|
return DB.select(TABLE)
|
||||||
@@ -39,17 +87,85 @@ export async function selectByProjectId(projectId: number): Promise<WorkRecord[]
|
|||||||
return DB.selectByField(TABLE, 'project_id', projectId)
|
return DB.selectByField(TABLE, 'project_id', projectId)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 新增作业记录 */
|
/** 新增作业记录,并事务累加项目汇总 */
|
||||||
export async function insert(data: WorkRecord): Promise<any> {
|
export async function insert(data: WorkRecord): Promise<void> {
|
||||||
return DB.insert(TABLE, data)
|
const sqls = [buildInsertSql(data)]
|
||||||
|
const projectSql = buildProjectDeltaSql(
|
||||||
|
data.project_id,
|
||||||
|
num(data.duration),
|
||||||
|
num(data.compaction_area),
|
||||||
|
num(data.pass_count),
|
||||||
|
)
|
||||||
|
if (projectSql)
|
||||||
|
sqls.push(projectSql)
|
||||||
|
await DB.transaction(sqls)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 更新作业记录 */
|
/** 更新作业记录,并事务按差值回写项目汇总 */
|
||||||
export async function update(id: number, data: Partial<WorkRecord>): Promise<any> {
|
export async function update(id: number, data: Partial<WorkRecord>): Promise<void> {
|
||||||
return DB.update(TABLE, data, `id = ${id}`)
|
const old = await selectById(id)
|
||||||
|
if (!old)
|
||||||
|
throw new Error(`作业记录不存在: ${id}`)
|
||||||
|
|
||||||
|
const sqls = [buildUpdateSql(data, `id = ${id}`)]
|
||||||
|
const oldProjectId = old.project_id
|
||||||
|
const newProjectId = data.project_id ?? oldProjectId
|
||||||
|
|
||||||
|
if (newProjectId !== oldProjectId) {
|
||||||
|
const removeSql = buildProjectDeltaSql(
|
||||||
|
oldProjectId,
|
||||||
|
-num(old.duration),
|
||||||
|
-num(old.compaction_area),
|
||||||
|
-num(old.pass_count),
|
||||||
|
)
|
||||||
|
const addSql = buildProjectDeltaSql(
|
||||||
|
newProjectId,
|
||||||
|
data.duration !== undefined ? num(data.duration) : num(old.duration),
|
||||||
|
data.compaction_area !== undefined ? num(data.compaction_area) : num(old.compaction_area),
|
||||||
|
data.pass_count !== undefined ? num(data.pass_count) : num(old.pass_count),
|
||||||
|
)
|
||||||
|
if (removeSql)
|
||||||
|
sqls.push(removeSql)
|
||||||
|
if (addSql)
|
||||||
|
sqls.push(addSql)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const durationDelta = data.duration !== undefined
|
||||||
|
? num(data.duration) - num(old.duration)
|
||||||
|
: 0
|
||||||
|
const compactionDelta = data.compaction_area !== undefined
|
||||||
|
? num(data.compaction_area) - num(old.compaction_area)
|
||||||
|
: 0
|
||||||
|
const passDelta = data.pass_count !== undefined
|
||||||
|
? num(data.pass_count) - num(old.pass_count)
|
||||||
|
: 0
|
||||||
|
const projectSql = buildProjectDeltaSql(
|
||||||
|
oldProjectId,
|
||||||
|
durationDelta,
|
||||||
|
compactionDelta,
|
||||||
|
passDelta,
|
||||||
|
)
|
||||||
|
if (projectSql)
|
||||||
|
sqls.push(projectSql)
|
||||||
|
}
|
||||||
|
|
||||||
|
await DB.transaction(sqls)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 删除作业记录 */
|
/** 删除作业记录,并事务扣减项目汇总 */
|
||||||
export async function remove(id: number): Promise<any> {
|
export async function remove(id: number): Promise<void> {
|
||||||
return DB.delete(TABLE, `id = ${id}`)
|
const old = await selectById(id)
|
||||||
|
if (!old)
|
||||||
|
return
|
||||||
|
|
||||||
|
const sqls = [`DELETE FROM ${TABLE} WHERE id = ${id}`]
|
||||||
|
const projectSql = buildProjectDeltaSql(
|
||||||
|
old.project_id,
|
||||||
|
-num(old.duration),
|
||||||
|
-num(old.compaction_area),
|
||||||
|
-num(old.pass_count),
|
||||||
|
)
|
||||||
|
if (projectSql)
|
||||||
|
sqls.push(projectSql)
|
||||||
|
await DB.transaction(sqls)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user