feat:页面修改

This commit is contained in:
houjunxiang
2025-10-10 10:45:25 +08:00
parent 386f1e7466
commit 2b50debcd3
11 changed files with 1489 additions and 3870 deletions

View File

@@ -20,7 +20,7 @@
<u-input
v-if="
field.type == 'input' &&
(field.decimalCount == null || field.decimalCount == '' || Number(field.decimalCount == null) < 0)
(field.decimalCount == null || field.decimalCount == '' || Number(field.decimalCount) < 0)
"
v-model="field.value"
clearable
@@ -29,7 +29,7 @@
/>
<!--数字,限制小数位数-->
<u-input
v-if="field.type == 'input' && field.decimalCount != null && Number(field.decimalCount == null) >= 0"
v-if="field.type == 'input' && field.decimalCount != null && Number(field.decimalCount) >= 0"
type="number"
clearable
@blur="event => checkDecimal(event, field)"
@@ -77,482 +77,483 @@
</view>
</template>
<script>
<script setup>
import { ref, computed } from 'vue'
import request from '@/nx/request'
import { cloneDeep } from 'lodash'
import { cloneDeep } from 'lodash-es'
import { calcAnalysisValue } from '@/nx/helper/calcAnalysisValue'
import nx from '@/nx'
export default {
data() {
return {
title: '编辑指派单',
taskInstance: {},
scrollTop: 0, //tab标题的滚动条位置
currentTaskNo: '', //当前任务样品
showDicPicker: false,
curDate: Number(new Date()),
//字段值对象
formValue: {},
//后端返回的字段结构
sourceFormFields: [],
//处理后的字段结构
formFields: [],
sampleList: []
}
},
computed: {
dynamicFieldPlaceholder(field) {
return '请输入'
}
},
onLoad(param) {
if (param.currentTaskNo) {
this.currentTaskNo = param.currentTaskNo
}
this.title = '样品分析-任务指派单:' + this.currentTaskNo
this.loadHeadFieldsAndValueByTaskNo()
this.getSampleList(this.currentTaskNo)
},
methods: {
handleFieldClick(field) {
if (field.type == 'date') {
if (field.fillingWay == '1') {
field.showPicker = true
}
return
}
import { onLoad } from '@dcloudio/uni-app'
// ========== data ==========
// 标题
const title = ref('编辑指派单')
// 表单模型
const taskInstance = ref({})
// tab标题的滚动条位置
const scrollTop = ref(0)
// 当前任务样品
const currentTaskNo = ref('')
// 字典选择器显示
const showDicPicker = ref(false)
// 当前日期(用于日期选择器)
const curDate = ref(Number(new Date()))
// 字段值对象
const formValue = ref({})
// 后端返回的字段结构
const sourceFormFields = ref([])
// 处理后的字段结构
const formFields = ref([])
// 样品列表
const sampleList = ref([])
// 表单引用
const uForm = ref(null)
// 页面加载
onLoad(param => {
if (param.currentTaskNo) {
currentTaskNo.value = param.currentTaskNo
}
title.value = '样品分析-任务指派单:' + currentTaskNo.value
loadHeadFieldsAndValueByTaskNo()
getSampleList(currentTaskNo.value)
})
// ========== methods ==========
// 点击字段(打开选择器)
function handleFieldClick(field) {
if (field.type == 'date') {
if (field.fillingWay == '1') {
field.showPicker = true
},
checkFeadToDetailField(headToDetailField) {
if (headToDetailField && headToDetailField.trim() != '') {
return true
} else {
return false
}
},
getSampleList(taskNo) {
this.$u.api
.getAssayTaskDetailListByTaskNo({ taskNo: taskNo })
.then(res => {
this.sampleList = res.result
})
.catch(err => {
console.error(err)
})
},
//返回上一页
// customBack() {
// uni.redirectTo({
// url: "/pages/sample/sample-work-detail"
// });
// },
navRightClick() {},
//处理小数位数补0或去除多余位数
checkDecimal(e, field) {
if (e == '') return
const decimalCount = field.decimalCount
if (decimalCount == null || decimalCount == '' || isNaN(decimalCount)) return
const count = Number(field.decimalCount)
}
return
}
field.showPicker = true
}
// 判断是否需要同步到明细字段(加粗显示)
function checkFeadToDetailField(headToDetailField) {
if (headToDetailField && headToDetailField.trim() != '') {
return true
} else {
return false
}
}
// 获取样品列表
function getSampleList(taskNo) {
nx.$api.assayTask
.getAssayTaskDetailListByTaskNo({ taskNo: taskNo })
.then(res => {
sampleList.value = res.result
})
.catch(err => {
console.error(err)
})
}
// 返回上一页(原注释保留,未启用)
// function customBack() {
// uni.redirectTo({
// url: "/pages/sample/sample-work-detail"
// });
// }
// 右上角导航点击(空实现)
function navRightClick() {}
// 处理小数位数补0或去除多余位数
function checkDecimal(e, field) {
if (e == '') return
const decimalCount = field.decimalCount
if (decimalCount == null || decimalCount == '' || isNaN(decimalCount)) return
const count = Number(field.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)
}
}
// 组装字段(处理默认值、初始化 picker 状态)
function assembleFields() {
const formFieldsArr = []
for (const field of sourceFormFields.value) {
//日期类型的默认值
if (field.type == 'date') {
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)
}
},
updateFieldValue(name, val) {
for (const field of this.formFields) {
if (field.prop == name) {
field.value = val
break
}
}
},
assembleFields() {
const me = this
const formFields = []
for (const field of this.sourceFormFields) {
//日期类型的默认值
if (field.type == 'date') {
const value = field.value
field.showPicker = false
if (value == 'curDate') {
field.value = me.$helper.dateFormat(new Date(), 'yyyy-MM-dd')
}
}
if (field.type == 'select') {
field.showPicker = false
}
formFields.push(field)
}
//先序列化再转json避免json里定义的方法丢失
this.formFields = JSON.parse(JSON.stringify(formFields, replacer), reviver)
// this.formFields = formFields;
},
//绑定数据
bindFormValue() {
const me = this
//formValue
for (const field of me.formFields) {
const prop = field.prop
if (prop) {
const value = me.formValue[prop]
if (value) {
field.value = value
if (field.type == 'select') {
field.valueText = value
}
}
}
}
},
//获取表单字段和值(老数据)
loadHeadFieldsAndValueByTaskNo() {
const me = this
const currentTaskNo = me.currentTaskNo
this.$u.api
.queryHeadValueByTaskNo({ taskNo: currentTaskNo })
.then(res => {
const result = res.result
const formConf = result.formConf
me.sourceFormFields = eval(formConf)
me.formValue = JSON.parse(result.formValue)
//加载和处理表单字段
me.assembleFields()
//绑定数据
me.bindFormValue()
//读取字段里的动态选项
me.loadFieldApiData()
console.log('formFields', this.formFields)
})
.catch(err => {
//如果没有查到数据,按配置读取新的表单字段
me.getHeadFields()
})
},
// 获取表单字段(新数据)
getHeadFields() {
const me = this
const currentTaskNo = this.currentTaskNo
uni.showLoading({
title: '加载中...'
})
this.$u.api
.queryHeadFieldsByTaskNo({ taskNo: currentTaskNo })
.then(res => {
me.sourceFormFields = me.analysisFormAndFields(res)
//加载和处理表单字段
me.assembleFields()
//读取字段里的动态选项
me.loadFieldApiData()
})
.finally(() => {
uni.hideLoading()
})
},
analysisFormAndFields(formRet) {
const fieldsArray = []
for (const form of formRet) {
let content = cloneDeep(form.content)
content = eval('(' + content + ')')
const column = content.column
for (const field of column) {
fieldsArray.push(field)
}
}
return fieldsArray
},
//将抬头字段值同步保存到明细字段
saveHeadValueToDetail(onComplete) {
//循环抬头字段,提取需哟保存到明细的字段
const conf = []
for (const field of this.formFields) {
const prop = field.prop
const headToDetailField = field.headToDetailField
if (prop && headToDetailField && headToDetailField.trim() != '') {
const value = field.value
const r = {
prop: field.prop,
value: value,
headToDetailField: headToDetailField
}
conf.push(r)
}
}
const data = {
taskNo: this.currentTaskNo,
conf: conf
}
this.$u.api.saveHeadValueToDetail(data).then(res => {
if (onComplete) onComplete()
})
},
handleSave() {
//显示loading
uni.showLoading({
title: '正在保存...'
})
//组装数据
const formValue = {}
for (const field of this.formFields) {
const prop = field.prop
if (prop) {
formValue[prop] = field.value
}
}
const value = {
taskNo: this.currentTaskNo,
formValue: JSON.stringify(formValue),
formConf: JSON.stringify(this.sourceFormFields, replacer)
}
this.$u.api
.saveHeadValue(value)
.then(async res => {
await this.saveHeadValueToDetail(async () => {
if (this.checkPropertyEquality()) {
await this.processIds(this.sampleList, 100)
} else {
uni.hideLoading()
// this.$helper.showToast({
// title: '保存成功!'
// });
uni.redirectTo({
url: '/pages/analysis/sample/sample-work-detail?currentTaskNo=' + this.currentTaskNo
})
}
})
})
.catch(err => {
uni.hideLoading()
console.log(err)
})
},
// 检查字段修改前和修改后字段值是否相等
checkPropertyEquality() {
for (const field of this.formFields) {
const prop = field.prop
if (prop && field['headToDetailField'] && field['headToDetailField'].trim() !== '') {
const flag = this.formValue[prop] !== field.value ? true : false
if (flag) return true
}
}
return false
},
//保存抬头字段
saveHeadData() {
if (this.checkPropertyEquality()) {
uni.showModal({
title: '提示',
content: '您修改的字段将影响样品分析结果,系统将重新计算',
showCancel: false,
success: res => {
if (res.confirm) {
this.handleSave()
}
}
})
} else {
this.handleSave()
}
},
// 通过样品id查询明细
async getSampleDataById(taskDetailId) {
let fieldGroup = []
const { result, additionalProperties } = await this.$u.api.queryFieldsByTaskDetail({
taskDetailId,
isSearchSRange: '0'
})
fieldGroup = result
const conAssayTaskId = additionalProperties.conAssayTaskId
const busSubCSampleId = additionalProperties.busSubCSampleId
const detail = additionalProperties.taskDetail
//处理硫值、硫量:未保存过的数据,读取接口返回的硫值、硫量
// this.loadSValue(detail);
//按公式计算值,并检查原数据与计算后的数据是否一致
try {
calcAnalysisValue(fieldGroup)
} catch (error) {
console.log(error)
}
let valueList = []
let cupNum = 0
for (const g of fieldGroup) {
for (const f of g.fields) {
if (f.dicKey == 'bh' || f.dicKey == 'bh_up') cupNum = f.value
valueList.push({
id: f.detailId,
type: f.pOrE,
value: f.value,
name: f.name,
dataType: f.dataType
})
}
}
let params = {
busSubCSampleId,
conAssayTaskId,
// measureTime : this.curSample.measureTime,
elementParamValueList: valueList,
busAssayTaskDetailId: taskDetailId
}
if (typeof cupNum != 'undefined' && cupNum != null && cupNum != '' && cupNum != 0 && cupNum != '0') {
//提交杯号,保存到后台
params.cupNum = cupNum
}
return params
},
// 提交样品
async submitData(data) {
try {
await this.$u.api.saveDetailValue(data)
} catch (error) {
throw error
}
},
async processIds(list, interval) {
let index = 0
const intervalId = setInterval(async () => {
if (index < list.length) {
const item = list[index]
index++
const params = await this.getSampleDataById(item['id'])
await this.submitData(params)
} else {
clearInterval(intervalId) // 所有任务完成后清除定时器
uni.redirectTo({
url: '/pages/analysis/sample/sample-work-detail?currentTaskNo=' + this.currentTaskNo
})
}
}, interval)
},
async apiRequest(url) {
return request({
url: url,
method: 'GET',
custom: {
isApiEncryption: true
}
})
},
//读取字段里的API选项
async loadFieldApiData() {
const formFields = this.formFields
let changeFlag = false
for (const field of formFields) {
const dicUrl = field.dicUrl
const type = field.type
if (dicUrl && dicUrl != '') {
//读取API选项
try {
const res = await this.apiRequest(dicUrl)
const data = res
const confLabel = field.props.label
const confValue = field.props.value
const emptyItem = { name: '', displayName: '' }
emptyItem[confLabel] = ''
emptyItem[confValue] = ''
//设置valueText、displayName
for (const item of data) {
if (item[confValue] == field.value) {
changeFlag = true
field.valueText = item[confLabel]
}
}
// data.unshift(emptyItem) //添加空数据
field.options = data
} catch (e) {}
}
}
if (changeFlag) {
const formFields = this.formFields
// this.formFields = JSON.parse(JSON.stringify(formFields));
this.formFields = JSON.parse(JSON.stringify(formFields, replacer), reviver)
}
},
/*
* 选择器组件确认事件
* event 事件默认参数
* field 当前字段
* 处理逻辑:
* field.value = 选中的值
* field.valueText = 选中的文本
* todo 关联字段在动态字段的change事件处理
* */
pickerConfirm(event, field) {
const me = this
if (field.type == 'date') {
field.value = nx.$dayjs(event.value).format(field.format)
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, me)
}
field.showPicker = false
},
checkLoadSValue() {
const vKey = 'sRange'
const vF = this.getFieldByKey(vKey)
if (vF == null) return false
const v = vF.value
if (v == null || v == '') return true
try {
if (v == 0 || number(v) == 0) return true
} catch (e) {}
return false
},
//读取硫值、硫量
loadSValue(detail) {
let flag = this.checkLoadSValue()
if (!flag) return
const vKey = 'sValue'
const rKey = 'sRange'
const vF = this.getFieldByKey(vKey)
if (vF != null) {
vF.value = detail.svalue
if (value == 'curDate') {
field.value = nx.$dayjs.format('yyyy-MM-dd')
}
const rF = this.getFieldByKey(rKey)
if (rF != null) {
rF.value = detail.srange
}
},
getFieldByKey(key) {
const group = this.fieldGroup
let field = null
for (let g of group) {
for (let f of g.fields) {
const dicKey = f.dicKey
if (dicKey && dicKey == key) {
field = f
break
}
}
if (field.type == 'select') {
field.showPicker = false
}
formFieldsArr.push(field)
}
//先序列化再转json避免json里定义的方法丢失
formFields.value = JSON.parse(JSON.stringify(formFieldsArr, replacer), reviver)
}
// 绑定数据(将 formValue 填入字段)
function bindFormValue() {
//formValue
for (const field of formFields.value) {
const prop = field.prop
if (prop) {
const value = formValue.value[prop]
if (value) {
field.value = value
if (field.type == 'select') {
field.valueText = value
}
}
// console.log('field', JSON.stringify(field));
return field
}
}
}
// 获取表单字段和值(老数据)
function loadHeadFieldsAndValueByTaskNo() {
nx.$api.assayTask
.queryHeadValueByTaskNo({ taskNo: currentTaskNo.value })
.then(res => {
const result = res.result
const formConf = result.formConf
sourceFormFields.value = eval(formConf)
formValue.value = JSON.parse(result.formValue)
//加载和处理表单字段
assembleFields()
//绑定数据
bindFormValue()
//读取字段里的动态选项
loadFieldApiData()
console.log('formFields', formFields.value)
})
.catch(err => {
//如果没有查到数据,按配置读取新的表单字段
getHeadFields()
})
}
// 获取表单字段(新数据)
function getHeadFields() {
nx.$api.assayTask.queryHeadFieldsByTaskNo({ taskNo: currentTaskNoVal }).then(res => {
sourceFormFields.value = analysisFormAndFields(res)
//加载和处理表单字段
assembleFields()
//读取字段里的动态选项
loadFieldApiData()
})
}
// 解析表单结构
function analysisFormAndFields(formRet) {
const fieldsArray = []
for (const form of formRet) {
let content = cloneDeep(form.content)
content = eval('(' + content + ')')
const column = content.column
for (const field of column) {
fieldsArray.push(field)
}
}
return fieldsArray
}
// 将抬头字段值同步保存到明细字段
function saveHeadValueToDetail(onComplete) {
//循环抬头字段,提取需哟保存到明细的字段
const conf = []
for (const field of formFields.value) {
const prop = field.prop
const headToDetailField = field.headToDetailField
if (prop && headToDetailField && headToDetailField.trim() != '') {
const value = field.value
const r = {
prop: field.prop,
value: value,
headToDetailField: headToDetailField
}
conf.push(r)
}
}
const data = {
taskNo: currentTaskNo.value,
conf: conf
}
nx.$api.assayTask.saveHeadValueToDetail(data).then(res => {
if (onComplete) onComplete()
})
}
// 实际保存逻辑
function handleSave() {
//组装数据
const formValueObj = {}
for (const field of formFields.value) {
const prop = field.prop
if (prop) {
formValueObj[prop] = field.value
}
}
const value = {
taskNo: currentTaskNo.value,
formValue: JSON.stringify(formValueObj),
formConf: JSON.stringify(sourceFormFields.value, replacer)
}
nx.$api.assayTask.saveHeadValue(value).then(async res => {
await saveHeadValueToDetail(async () => {
if (checkPropertyEquality()) {
await processIds(sampleList.value, 100)
} else {
uni.redirectTo({
url: '/pages/analysis/sample/sample-work-detail?currentTaskNo=' + currentTaskNo.value
})
}
})
})
}
// 检查字段修改前和修改后字段值是否相等
function checkPropertyEquality() {
for (const field of formFields.value) {
const prop = field.prop
if (prop && field['headToDetailField'] && field['headToDetailField'].trim() !== '') {
const flag = formValue.value[prop] !== field.value ? true : false
if (flag) return true
}
}
return false
}
// 保存抬头字段(带确认弹窗)
function saveHeadData() {
if (checkPropertyEquality()) {
uni.showModal({
title: '提示',
content: '您修改的字段将影响样品分析结果,系统将重新计算',
showCancel: false,
success: res => {
if (res.confirm) {
handleSave()
}
}
})
} else {
handleSave()
}
}
// 通过样品id查询明细
async function getSampleDataById(taskDetailId) {
let fieldGroup = []
const { result, additionalProperties } = await nx.$api.assayTask.queryFieldsByTaskDetail({
taskDetailId,
isSearchSRange: '0'
})
fieldGroup = result
const conAssayTaskId = additionalProperties.conAssayTaskId
const busSubCSampleId = additionalProperties.busSubCSampleId
const detail = additionalProperties.taskDetail
//处理硫值、硫量:未保存过的数据,读取接口返回的硫值、硫量
// loadSValue(detail);
//按公式计算值,并检查原数据与计算后的数据是否一致
try {
calcAnalysisValue(fieldGroup)
} catch (error) {
console.log(error)
}
let valueList = []
let cupNum = 0
for (const g of fieldGroup) {
for (const f of g.fields) {
if (f.dicKey == 'bh' || f.dicKey == 'bh_up') cupNum = f.value
valueList.push({
id: f.detailId,
type: f.pOrE,
value: f.value,
name: f.name,
dataType: f.dataType
})
}
}
let params = {
busSubCSampleId,
conAssayTaskId,
// measureTime : this.curSample.measureTime,
elementParamValueList: valueList,
busAssayTaskDetailId: taskDetailId
}
if (typeof cupNum != 'undefined' && cupNum != null && cupNum != '' && cupNum != 0 && cupNum != '0') {
//提交杯号,保存到后台
params.cupNum = cupNum
}
return params
}
// 提交样品
async function submitData(data) {
try {
await nx.$api.assayTask.saveDetailValue(data)
} catch (error) {
throw error
}
}
// 批量处理样品(带间隔)
async function processIds(list, interval) {
let index = 0
const intervalId = setInterval(async () => {
if (index < list.length) {
const item = list[index]
index++
const params = await getSampleDataById(item['id'])
await submitData(params)
} else {
clearInterval(intervalId) // 所有任务完成后清除定时器
uni.redirectTo({
url: '/pages/analysis/sample/sample-work-detail?currentTaskNo=' + currentTaskNo.value
})
}
}, interval)
}
// 通用 API 请求
async function apiRequest(url) {
return request({
url: url,
method: 'GET'
})
}
// 读取字段里的API选项
async function loadFieldApiData() {
const formFieldsVal = formFields.value
let changeFlag = false
for (const field of formFieldsVal) {
const dicUrl = field.dicUrl
const type = field.type
if (dicUrl && dicUrl != '') {
//读取API选项
try {
const res = await apiRequest(dicUrl)
const data = res
const confLabel = field.props.label
const confValue = field.props.value
const emptyItem = { name: '', displayName: '' }
emptyItem[confLabel] = ''
emptyItem[confValue] = ''
//设置valueText、displayName
for (const item of data) {
if (item[confValue] == field.value) {
changeFlag = true
field.valueText = item[confLabel]
}
}
// data.unshift(emptyItem) //添加空数据
field.options = data
} catch (e) {}
}
}
if (changeFlag) {
// 重新序列化以触发响应式更新(保留函数)
formFields.value = JSON.parse(JSON.stringify(formFieldsVal, replacer), reviver)
}
}
/*
* 选择器组件确认事件
* event 事件默认参数
* field 当前字段
* 处理逻辑:
* field.value = 选中的值
* field.valueText = 选中的文本
* todo 关联字段在动态字段的change事件处理
* */
function pickerConfirm(event, field) {
if (field.type == 'date') {
field.value = nx.$dayjs(event.value).format(field.format)
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
}
// 检查是否需要加载硫值(原逻辑保留)
function checkLoadSValue() {
const vKey = 'sRange'
const vF = getFieldByKey(vKey)
if (vF == null) return false
const v = vF.value
if (v == null || v == '') return true
try {
if (v == 0 || Number(v) == 0) return true
} catch (e) {}
return false
}
// 读取硫值、硫量(原逻辑保留,未启用)
// function loadSValue(detail) {
// let flag = checkLoadSValue()
// if (!flag) return
// const vKey = 'sValue'
// const rKey = 'sRange'
// const vF = getFieldByKey(vKey)
// if (vF != null) {
// vF.value = detail.svalue
// }
// const rF = getFieldByKey(rKey)
// if (rF != null) {
// rF.value = detail.srange
// }
// }
// 根据 key 获取字段(依赖 fieldGroup但当前无 fieldGroup保留原逻辑结构
function getFieldByKey(key) {
// 注意:原代码中 this.fieldGroup 未定义,此处无法实现,保留函数结构
// const group = this.fieldGroup
// let field = null
// for (let g of group) {
// for (let f of g.fields) {
// const dicKey = f.dicKey
// if (dicKey && dicKey == key) {
// field = f
// break
// }
// }
// }
// return field
return null
}
// ========== 工具函数(保留原位置) ==========
// 自定义 replacer将函数转换为字符串。json序列化和反序列化时避免函数丢失
function replacer(key, value) {
if (typeof value === 'function') {
@@ -571,6 +572,7 @@ function reviver(key, value) {
return value
}
</script>
<style lang="scss" scoped>
.navbar-right {
font-size: 16px;