Files
zgty-mas-m/pages/analysis/sample/sample-work-edit-task.vue
2025-11-11 20:51:08 +08:00

407 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view>
<navbar-back titleWidth="800" :title="title"></navbar-back>
<u-row>
<u-col span="1"></u-col>
<u-col span="10">
<scroll-view scroll-y scroll-with-animation :scroll-top="scrollTop">
<view class="form-item-my" v-for="(field, index) in formFields" :key="'field_' + index">
<template v-if="!field.hidden">
<view
class="label-my"
:style="{ fontWeight: checkFeadToDetailField(field.headToDetailField) ? 'bold' : 'normal' }"
>{{ field.label }}</view
>
<view class="content-my">
<view v-if="field.type == 'title'" class="content-title">
{{ field.value }}
</view>
<!--普通输入框-->
<u-input
v-if="field.type == 'Input'"
v-model="field.value"
clearable
:placeholder="field.placeholder"
:disabled="field.disabled || field.fillingWay == 'calculate'"
/>
<!--数字限制小数位数-->
<u-input
v-if="field.type == 'decimal'"
type="number"
clearable
@blur="event => checkDecimal(event, field)"
v-model="field.value"
:placeholder="field.placeholder"
:disabled="field.disabled || field.fillingWay == 'calculate'"
/>
<!--select-->
<view v-if="field.type == 'select'" class="x-bc select-my" @click="handleFieldClick(field)">
<text v-if="field.value">{{ field.value }}</text>
<text v-else>请选择</text>
<u-icon name="arrow-down" size="20"></u-icon>
</view>
<u-picker
v-if="field.type == 'select'"
:show="field.showPicker"
:columns="[field.options]"
keyName="displayName"
@cancel="field.showPicker = false"
@confirm="event => pickerConfirm(event, field)"
/>
<!--日期-->
<view v-if="field.type == 'date'" class="x-bc select-my" @click="handleFieldClick(field)">
<text v-if="field.value">{{ nx.$dayjs(field.value).format('YYYY-MM-DD HH:mm:ss') }}</text>
<text v-else>请选择</text>
<u-icon name="calendar-fill" size="20"></u-icon>
</view>
<u-datetime-picker
v-if="field.type == 'date'"
:show="field.showPicker"
v-model="curDate"
mode="datetime"
@cancel="field.showPicker = false"
@confirm="event => pickerConfirm(event, field)"
></u-datetime-picker>
</view>
</template>
</view>
</scroll-view>
<u-button style="width: 50%" type="primary" @click="saveHeadData">保存</u-button>
</u-col>
<u-col span="1"> </u-col>
</u-row>
<up-loading-page :loading="pageLoading"></up-loading-page>
</view>
</template>
<script setup>
import { ref, computed } from 'vue'
import nx from '@/nx'
import { onLoad } from '@dcloudio/uni-app'
import { calcRowAnalysisValue } from '@/nx/helper/calcAnalysisValue'
import tools from '@/nx/utils/tools'
const pageLoading = ref(false)
// 标题
const title = ref('编辑指派单')
// tab标题的滚动条位置
const scrollTop = ref(0)
// 当前任务样品
const currentTaskId = ref('')
// 字典选择器显示
// 当前日期(用于日期选择器)
const curDate = ref(Number(new Date()))
// 字段值对象
const formData = ref({})
// 处理后的字段结构
const formFields = ref([])
const staticFormSchema = [
{ label: '指派单号', fieldKey: 'businessAssayTaskId', hidden: true },
{ type: 'title', value: '分析原始记录单' },
{ label: '检测方法', type: 'Input', fieldKey: 'configAssayMethodName', disabled: true },
{ label: '分析人', type: 'Input', fieldKey: 'assayOperator', disabled: true },
{ label: '检测时间', type: 'date', fieldKey: 'assayTime' }
]
let dynamicFormSchema = []
// 页面加载
onLoad(param => {
if (param.currentTaskId) {
currentTaskId.value = param.currentTaskId
}
loadTaskDetail()
})
let tabs = ref([])
const dynamicFormData = ref({})
async function loadTaskDetail() {
pageLoading.value = true
const {
assayTaskAnalysisDataList,
businessAssayTasNo,
configAssayMethodName,
assayOperator,
assayTime,
formValue,
dataCollectionId,
dataCollectionKey
} = await nx.$api.assayTask.batchSampleAndQcAnalysisByTaskId(currentTaskId.value)
formData.value = {
businessAssayTaskId: currentTaskId.value,
configAssayMethodName: configAssayMethodName,
assayOperator: assayOperator,
assayTime: assayTime
}
if (formValue) {
formData.value = { ...formData.value, ...JSON.parse(formValue) }
dynamicFormData.value = JSON.parse(formValue)
}
title.value = '样品分析-任务指派单:' + businessAssayTasNo
// 查询动态字段
const dataCollectionParams = {}
if (dataCollectionId) {
dataCollectionParams.dataCollectionId = dataCollectionId
} else {
dataCollectionParams.dataCollectionKey = dataCollectionKey
}
const data = await nx.$api.assayTask.getDynamicBaseFormSchema(dataCollectionParams)
dynamicFormSchema = data.map(item => ({
label: item.fieldName,
fieldKey: item.fieldKey,
type: item.fieldType,
placeholder: '请输入'
}))
formFields.value = [...staticFormSchema, ...dynamicFormSchema]
bindFormValue()
// 处理样品分类数据
const tabGroups = assayTaskAnalysisDataList
tabs.value = tabGroups.map(group => {
// 必须深拷贝 datas防止多个表格共享引用
const tableData = JSON.parse(JSON.stringify(group.datas || []))
const columns = group.columns || []
return {
name: group.analysisType,
label: group.analysisName,
columns,
tableData,
configQCSampleMethodInfo: group.configQCSampleMethod?.configInfomation
? JSON.parse(group.configQCSampleMethod['configInfomation'])['set']
: []
}
})
pageLoading.value = false
}
// 空白样或者标样影响计算配置字段
const configQCSampleMethodInfos = computed(() => {
return tabs.value.filter(t => t.name === 'by' || t.name === 'kby').flatMap(tab => tab.configQCSampleMethodInfo)
})
// 表格数据更新后重新计算
async function updateTableDataByConfigFields() {
let needCalcTabs = []
needCalcTabs = tabs.value.filter(t => t.name !== 'by' && t.name !== 'kby')
const formData = realFormData.value
if (needCalcTabs.length === 0) return
for (const key in formData) {
for (const tab of needCalcTabs) {
const columnObj = tab.columns.find(c => {
if (c.formula) {
let FromKey = c.formula.split(':')
return FromKey[1] === key
}
return false
})
if (!columnObj) continue
tab.tableData.forEach(row => {
// 赋值配置列参与计算
row[columnObj.fieldIndex].value = formData[key]
calcRowAnalysisValue(row, columnObj, tab.columns)
})
}
}
}
// 点击字段(打开选择器)
function handleFieldClick(field) {
field.showPicker = true
}
// 判断是否需要同步到明细字段(加粗显示)
function checkFeadToDetailField(headToDetailField) {
if (headToDetailField && headToDetailField.trim() != '') {
return true
} else {
return false
}
}
// 处理小数位数补0或去除多余位数
function checkDecimal(e, field) {
if (e == '') return
const decimalCount = field.decimalPosition
const count = Number(decimalCount)
const value = field.value
const pos = value.indexOf('.') + 1
let length = value.length - pos
if (pos == 0) {
length = 0
}
while (length < count) {
if (field.value.indexOf('.') < 0) field.value += '.'
field.value = field.value + '0'
length++
}
if (count === 0) {
field.value = parseInt(field.value) + ''
} else if (length > count) {
field.value = field.value.substring(0, pos + count)
}
}
// 绑定数据(将 formData 填入字段)
function bindFormValue() {
for (const field of formFields.value) {
const fieldKey = field.fieldKey
if (fieldKey) {
const value = formData.value[fieldKey]
if (value) {
field.value = value
}
}
}
}
const realFormData = computed(() => {
const formData = {}
for (const field of formFields.value) {
if (field.fieldKey) {
formData[field.fieldKey] = field.value
}
}
return formData
})
// 实际保存逻辑
async function handleSave(change) {
let params = {}
if (change) {
// 计算样品数据
updateTableDataByConfigFields()
params = {
...realFormData.value,
formValue: JSON.stringify(getDynamicFormSchemaFormData(realFormData.value)),
assayTaskAnalysisDataList: tabs.value.map(item => ({
datas: item.tableData,
analysisType: item.name
}))
}
} else {
params = {
...realFormData.value,
formValue: JSON.stringify(getDynamicFormSchemaFormData(realFormData.value))
}
}
await nx.$api.assayTask.saveBatchSmpleAndQcAnalysis(params)
uni.redirectTo({
url: '/pages/analysis/sample/sample-work-detail?currentTaskId=' + currentTaskId.value
})
}
function getDynamicFormSchemaFormData(obj) {
const includeKeys = new Set(dynamicFormSchema.filter(item => item.fieldKey).map(item => item.fieldKey))
return Object.fromEntries(Object.entries(obj).filter(([key]) => includeKeys.has(key)))
}
// 检查动态字段值在空白样或者标样的(影响计算配置字段)中是否变化
function checkPropertyEquality() {
if (configQCSampleMethodInfos.value.length === 0) return false
for (const config of configQCSampleMethodInfos.value) {
const target = config.target
if (!target) continue
const originalValue = dynamicFormData.value[target]
const currentValue = realFormData.value[target]
console.log('checkPropertyEquality', originalValue, currentValue)
if (!looseEqual(originalValue, currentValue)) {
return true
}
}
return false
}
function looseEqual(a, b) {
const aEmpty = tools.isEmpty(a)
const bEmpty = tools.isEmpty(b)
if (aEmpty && bEmpty) {
return true // 都是空,算相等
}
if (aEmpty || bEmpty) {
return false // 一个空一个非空,不等
}
return a === b // 都非空,严格相等
}
// 保存抬头字段(带确认弹窗)
function saveHeadData() {
if (!realFormData.value['assayTime']) return nx.$helper.showToast('请选择分析时间')
if (checkPropertyEquality()) {
uni.showModal({
title: '提示',
content: '您修改的字段将影响样品分析结果,系统将重新计算',
showCancel: false,
success: res => {
if (res.confirm) {
handleSave(true)
}
}
})
} else {
handleSave(false)
}
}
/*
* 选择器组件确认事件
* event 事件默认参数
* field 当前字段
* 处理逻辑:
* field.value = 选中的值
* field.valueText = 选中的文本
* todo 关联字段在动态字段的change事件处理
* */
function pickerConfirm(event, field) {
if (field.type == 'date') {
field.value = nx.$dayjs(event.value).valueOf()
field.showPicker = false
return
}
const confLabel = field.props.label
const confValue = field.props.value
const selected = event.value[0]
const value = selected[confValue]
const displayName = selected[confLabel]
field.value = value
field.valueText = displayName
if (typeof field.change == 'function') {
field.change({ value }, selected, { formFields: formFields.value })
}
field.showPicker = false
}
</script>
<style lang="scss" scoped>
.content-title {
width: 100%;
font-size: 20px;
font-weight: 300;
}
.form-item-my {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.label-my {
width: 170px; /* 标签宽度 */
padding-right: 10px;
text-align: right;
}
.label-my sub {
font-size: 0.6em; /* 调整下标字体大小 */
vertical-align: sub; /* 调整下标垂直对齐 */
}
.content-my {
flex: 1;
padding-left: 7px;
.select-my {
color: #303133;
font-size: 15px;
padding: 6px 8px;
border: 1px solid #dcdcdc;
border-radius: 5px;
}
}
</style>