feat:样品分析

This commit is contained in:
houjunxiang
2025-11-15 18:09:04 +08:00
parent d3f6ad4bf6
commit 00e21aebd3
60 changed files with 299 additions and 200 deletions

View File

@@ -250,8 +250,7 @@ function getWeightStyle(auncel) {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
background-color: #000; background-color: rgba(31, 41, 55, 0.55);
opacity: 0.6;
color: #fff; color: #fff;
font-weight: 500; font-weight: 500;
z-index: 10; z-index: 10;

View File

@@ -12,6 +12,15 @@ const getAssayTaskList = params => {
} }
}) })
} }
const getAssayTaskPage = params => {
return request({
url: '/qms/business-assay-task/page',
method: 'GET',
params: {
...params
}
})
}
// 获取样品列表 // 获取样品列表
const getAssayTaskDataList = params => { const getAssayTaskDataList = params => {
return request({ return request({
@@ -20,6 +29,13 @@ const getAssayTaskDataList = params => {
method: 'GET' method: 'GET'
}) })
} }
const getAssayTaskDetailList = params => {
return request({
url: `/qms/business-assay-task-detail/list`,
params,
method: 'GET'
})
}
// 获取样品分析配置数据 // 获取样品分析配置数据
const getSampleAnalysisByTaskId = businessAssayTaskId => { const getSampleAnalysisByTaskId = businessAssayTaskId => {
return request({ return request({
@@ -192,6 +208,17 @@ const submitTask = params => {
}) })
} }
const taskIngredients = params => {
return request({
url: '/qms/bus/sample/analysis/issuedIngredients',
method: 'POST',
params,
custom: {
showSuccess: true
}
})
}
// 撤销提交任务明细 // 撤销提交任务明细
const redoAssayTaskDetail = params => { const redoAssayTaskDetail = params => {
return request({ return request({
@@ -230,13 +257,11 @@ const reportAssayTask = id => {
}) })
} }
const rollbackAssayTask = id => { const rollbackAssayTask = data => {
return request({ return request({
url: taskPrefix + '/rollbackAssayTask', url: '/qms/bus/sample/analysis/rollbackAnalysisSample',
method: 'POST', method: 'POST',
params: { data
id
}
}) })
} }
@@ -262,7 +287,9 @@ const queryQmsDicSampleProcessCodeList = () => {
export default { export default {
getAssayTaskList, getAssayTaskList,
getAssayTaskPage,
getAssayTaskDataList, getAssayTaskDataList,
getAssayTaskDetailList,
getSampleAnalysisByTaskId, getSampleAnalysisByTaskId,
batchSampleAndQcAnalysisByTaskId, batchSampleAndQcAnalysisByTaskId,
saveBatchSmpleAndQcAnalysis, saveBatchSmpleAndQcAnalysis,
@@ -283,5 +310,6 @@ export default {
createRollbackApply, createRollbackApply,
rollbackAssayTask, rollbackAssayTask,
submitTask, submitTask,
taskIngredients,
queryQmsDicSampleProcessCodeList queryQmsDicSampleProcessCodeList
} }

View File

@@ -7,9 +7,7 @@ export function useListData({ searchParams, api, needInitListData = false, proce
const loadingData = ref(true) const loadingData = ref(true)
const pageParams = reactive({ const pageParams = reactive({
pageNo: 1, pageNo: 1,
pageSize: 10, pageSize: 5
order: 'desc',
column: 'createTime'
}) })
const total = ref(0) const total = ref(0)
const loadStatus = ref('loadmore') const loadStatus = ref('loadmore')
@@ -21,12 +19,12 @@ export function useListData({ searchParams, api, needInitListData = false, proce
...unref(searchParams), ...unref(searchParams),
...pageParams ...pageParams
} }
let { records, total: pageTotal } = await api(params) let { list, total: pageTotal } = await api(params)
total.value = pageTotal total.value = pageTotal
if (processData) { if (processData) {
records = processData(records) list = processData(list)
} }
return records return list
} }
// 页面底部触底加载更多 // 页面底部触底加载更多

View File

@@ -21,7 +21,7 @@
{ {
"path": "pages/index/index", "path": "pages/index/index",
"style": { "style": {
"navigationBarTitleText": "系统选择" "navigationBarTitleText": "模块选择"
} }
}, },
{ {
@@ -171,6 +171,7 @@
"path": "pages/analysis/sample/sample-work-list", "path": "pages/analysis/sample/sample-work-list",
"style": { "style": {
"navigationBarTitleText": "样品分析列表", "navigationBarTitleText": "样品分析列表",
"enablePullDownRefresh": true,
"navigationStyle": "custom" "navigationStyle": "custom"
} }
}, },

View File

@@ -1,8 +1,9 @@
<template> <template>
<view> <view>
<navbar-back title="天平状态"></navbar-back> <navbar-back title="天平状态"></navbar-back>
<up-grid border :col="4"> <view class="p20">
<up-grid-item v-for="(auncel, index) in auncelList" :index="index" :key="index"> <up-grid :col="4" style="gap: 20px">
<up-grid-item v-for="(auncel, index) in auncelList" :key="index">
<view class="auncel-item" :style="{ backgroundImage: `url(${balanceBackground})` }"> <view class="auncel-item" :style="{ backgroundImage: `url(${balanceBackground})` }">
<view class="auncel-header"> <view class="auncel-header">
<view class="auncel-code">{{ auncel.deviceCode }}</view> <view class="auncel-code">{{ auncel.deviceCode }}</view>
@@ -15,9 +16,11 @@
<view v-if="auncel.isConnected === 1" class="weight-unit">{{ auncel.weightUnit }}</view> <view v-if="auncel.isConnected === 1" class="weight-unit">{{ auncel.weightUnit }}</view>
</view> </view>
</view> </view>
<view v-if="auncel.isConnected != 1" class="shade">天平断开</view>
</up-grid-item> </up-grid-item>
</up-grid> </up-grid>
</view> </view>
</view>
</template> </template>
<script setup> <script setup>
@@ -114,7 +117,7 @@ function handleDeviceStatus(res) {
if (item.id === res.deviceId) { if (item.id === res.deviceId) {
item.isConnected = res.connected item.isConnected = res.connected
if (res.connected == 0) { if (res.connected == 0) {
item.weightData = '天平断开' item.weightData = ''
item.weightUnit = '' item.weightUnit = ''
item.weightStable = 0 item.weightStable = 0
} }
@@ -135,7 +138,7 @@ function handleConnClose() {
function getWeightText(auncel) { function getWeightText(auncel) {
if (auncel.isConnected !== 1) { if (auncel.isConnected !== 1) {
return '天平断开' return ''
} }
return auncel.weightData || '' return auncel.weightData || ''
} }
@@ -175,7 +178,21 @@ function getWeightStyle(auncel) {
padding: 10px; padding: 10px;
box-sizing: border-box; box-sizing: border-box;
} }
.shade {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
justify-content: center;
align-items: center;
background-color: rgba(31, 41, 55, 0.55);
color: #fff;
font-weight: 500;
z-index: 10;
border-radius: 8px;
}
.auncel-header { .auncel-header {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@@ -1,6 +1,12 @@
<template> <template>
<view> <view>
<navbar-back title="检斤检化验系统" :autoBack="false" leftIcon="" :leftText="`您好!${userInfo.nickname}`"> <navbar-back
title="实验室管理系统【分析管理】"
titleWidth="800"
:autoBack="false"
leftIcon=""
:leftText="`您好!${userInfo.nickname}`"
>
<u-icon @click="popupShow = true" size="28" color="#FFF" name="account-fill" /> <u-icon @click="popupShow = true" size="28" color="#FFF" name="account-fill" />
</navbar-back> </navbar-back>
@@ -26,12 +32,21 @@ const popupShow = ref(false)
const isAllowAgainPrint = ref(false) const isAllowAgainPrint = ref(false)
const menuItemList = ref([ const menuItemList = ref([
// { url: '/pages/analysis/sample/sample-receive', otherConf: { icon: 'arrow-downward' }, name: '收样' }, {
{ url: '/pages/analysis/sample/sample-work-list', otherConf: { icon: 'edit-pen-fill' }, name: '样品分析' }, url: '/pages/analysis/sample/sample-work-list',
// { url: '/pages/analysis/sample/sample-report', otherConf: { icon: 'arrow-upward' }, name: '数据上报' }, otherConf: { icon: '/static/images/menus/sampleAnalysis.png' },
{ url: '/pages/analysis/sample/sample-report-search', otherConf: { icon: 'search' }, name: '待审数据' }, name: '样品分析'
// { url: '/pages/analysis/sample/sample-print', otherConf: { icon: 'file-text-fill' }, name: '单据补打' }, },
{ url: '/pages/analysis/setting/setting', otherConf: { icon: 'setting-fill' }, name: '系统设置' } {
url: '/pages/analysis/sample/sample-report-search',
otherConf: { icon: '/static/images/menus/records.png' },
name: '分析记录'
},
{
url: '/pages/analysis/auncel/auncel-status',
otherConf: { icon: '/static/images/menus/balance.png' },
name: '天平查看'
}
]) ])
// 计算属性 // 计算属性

View File

@@ -3,8 +3,11 @@
<view class="u-tab-item" :class="{ 'u-tab-item-active': active }" @tap.stop="handleClick"> <view class="u-tab-item" :class="{ 'u-tab-item-active': active }" @tap.stop="handleClick">
<u-row class="full-width"> <u-row class="full-width">
<u-col span="2" class="text-center" style="position: relative"> <u-col span="2" class="text-center" style="position: relative">
<slot name="tag" v-if="$slots.tag" :task="task"></slot>
<template v-else>
<u-icon :color="taskStyle(task)" name="tags-fill" size="40"></u-icon> <u-icon :color="taskStyle(task)" name="tags-fill" size="40"></u-icon>
<text class="seq">{{ seq }}</text> <text class="seq">{{ seq }}</text>
</template>
</u-col> </u-col>
<u-col span="10"> <u-col span="10">
<view class="fs18">{{ task.taskNo }}</view> <view class="fs18">{{ task.taskNo }}</view>

View File

@@ -1,11 +1,10 @@
<template> <template>
<view> <view>
<navbar-back :autoBack="false" title="审核查询" @leftClick="customBack"></navbar-back> <navbar-back :autoBack="false" title="分析记录" @leftClick="customBack"></navbar-back>
<u-row gutter="16"> <u-row gutter="16">
<u-col span="4"> <u-col span="4">
<view class="content-title-name"> <view class="content-title-name">
<text>任务列表</text> <text>任务列表</text>
<up-badge v-if="taskList.length > 0" class="ml5" :value="taskList.length" type="warning"></up-badge>
</view> </view>
<u-gap height="5" bg-color="#0055A2"></u-gap> <u-gap height="5" bg-color="#0055A2"></u-gap>
<scroll-view <scroll-view
@@ -14,15 +13,22 @@
scroll-with-animation scroll-with-animation
class="content-main-left" class="content-main-left"
:scroll-top="scrollTop" :scroll-top="scrollTop"
@scrolltolower="handleScrolltolower"
> >
<TaskItem <TaskItem
v-for="(task, index) in taskList" v-for="(task, index) in listData"
:key="index" :key="index"
:task="task" :task="task"
:seq="index + 1" :seq="index + 1"
:active="current === index" :active="selectedIndex === index"
@click="switchTask(index)" @click="switchTask(index)"
/> >
<template #tag="{ task }">
<u-icon v-if="task.flowStatus === 'approved'" name="/static/images/status/completed.png" size="45" />
<u-icon v-else name="/static/images/status/wait_for_audit.png" size="45" />
</template>
</TaskItem>
<up-loadmore :status="loadStatus" />
</scroll-view> </scroll-view>
</u-col> </u-col>
<u-col span="8"> <u-col span="8">
@@ -33,33 +39,30 @@
<view> <view>
<scroll-view scroll-y scroll-with-animation style="height: calc(75vh - 60px)"> <scroll-view scroll-y scroll-with-animation style="height: calc(75vh - 60px)">
<block v-for="(sample, index) in sampleList" :key="index"> <block v-for="(sample, index) in sampleList" :key="index">
<view class="p5 fs16"> <view class="sample-item">
<u-row <u-row @click="showSampleDetail(sample.id, index)">
@click="showSampleDetail(sample.id, index)" <u-col span="2" class="text-center">
:class="selectedIndex.value === index ? 'selected_Sample' : ''"
>
<u-col span="2" class="text-center" :style="sampleStyle(sample)">
<view> <view>
<text>{{ index + 1 }}</text> <text>{{ index + 1 }}</text>
</view> </view>
</u-col> </u-col>
<u-col span="5"> <u-col span="4">
<view> <view>
<text class="pl5">{{ sample.sampleAssayCode }}</text> <text class="pl5">{{ sample.sampleAssayCode }}</text>
</view> </view>
<view> <view>
<text class="pl5"> <text class="pl5">
{{ getDataSourceTypeShow(sample.dataSourceType) }}{{ sample.sampleName }} {{ sample.sampleName }}
</text> </text>
</view> </view>
</u-col> </u-col>
<u-col span="5"> <u-col span="4">
<view> <view>
<text style="padding-left: 10px">{{ sample.assayProject }}</text> <text style="padding-left: 10px" v-html="sample.assayProject"></text>
</view> </view>
</u-col> </u-col>
</u-row> </u-row>
<u-line class="p5" color="#bbb" /> <u-icon class="sample-status" name="/static/images/status/return_b.png" size="45" />
</view> </view>
</block> </block>
</scroll-view> </scroll-view>
@@ -88,20 +91,16 @@
<script setup> <script setup>
import { ref, computed, onMounted } from 'vue' import { ref, computed, onMounted } from 'vue'
import { onLoad, onBackPress } from '@dcloudio/uni-app' import { onLoad, onBackPress, onPullDownRefresh } from '@dcloudio/uni-app'
import SampleDetailPopup from '@/components/sample/sample-detail-popup.vue' import SampleDetailPopup from '@/components/sample/sample-detail-popup.vue'
import TaskItem from './components/task-item.vue' import TaskItem from './components/task-item.vue'
import nx from '@/nx' import nx from '@/nx'
import { useListData } from '@/nx/hooks/usePageListData'
import { getDataSourceTypeShow } from '../common' import { getDataSourceTypeShow } from '../common'
// 响应式数据 // 响应式数据
const scrollTop = ref(0) const scrollTop = ref(0)
const current = ref(0) const selectedIndex = ref(0)
const currentTask = ref({})
const currentTaskId = ref('')
const reviewNum = ref(0)
const selectedIndex = ref(-1)
const taskList = ref([])
const sampleList = ref([]) const sampleList = ref([])
const showDetailPopup = ref(false) const showDetailPopup = ref(false)
const detailId = ref('') const detailId = ref('')
@@ -114,56 +113,40 @@ const customBack = () => {
uni.reLaunch({ url: '/pages/analysis/index/index' }) uni.reLaunch({ url: '/pages/analysis/index/index' })
} }
const sampleStyle = sample => { function handleScrolltolower() {
if ((sample.weightSubmitStatus === 0 || sample.weightSubmitStatus === 1) && sample.reviewCount > 0) { scrollToLower()
return 'color: red'
}
if (sample.weightSubmitStatus === 2 && sample.reviewCount > 0) {
return 'color: green'
}
return ''
} }
const currentTask = computed(() => {
if (listData.value.length > 0) {
return listData.value[selectedIndex.value]
} else {
return {}
}
})
const switchTask = async index => { const switchTask = async index => {
if (index === current.value) return if (index === selectedIndex.value) return
current.value = index selectedIndex.value = index
selectedIndex.value = -1 getAssayTaskDetail(currentTask.value.id)
const task = taskList.value[index]
currentTask.value = task
getAssayTaskDetail(task.id)
} }
const showSampleDetail = (id, index) => { const showSampleDetail = (id, index) => {
selectedIndex.value = index
detailId.value = id detailId.value = id
showDetailPopup.value = true showDetailPopup.value = true
} }
const searchParams = computed(() => ({
const getAssayTask = () => {
taskList.value = []
sampleList.value = []
current.value = 0
const param = {
taskAssignStatus: 'submitted', taskAssignStatus: 'submitted',
taskAssayStatus: 'submitted', taskAssayStatus: 'submitted'
finishStatus: 'not_start', }))
flowStatus: 'not_start',
assayOper: userInfo.value.realname
}
nx.$api.assayTask.getAssayTaskList(param).then(res => {
taskList.value = res
if (taskList.value.length > 0) {
currentTask.value = taskList.value[0]
getAssayTaskDetail(currentTask.value.id)
}
})
}
const getAssayTaskDetail = businessAssayTaskId => { const { listData, scrollToLower, loadStatus, getInitData } = useListData({
reviewNum.value = 0 searchParams,
nx.$api.assayTask.getAssayTaskDataList({ businessAssayTaskId }).then(res => { api: nx.$api.assayTask.getAssayTaskPage
})
const getAssayTaskDetail = async businessAssayTaskId => {
const res = await nx.$api.assayTask.getAssayTaskDetailList({ businessAssayTaskId })
sampleList.value = res || [] sampleList.value = res || []
})
} }
const previewPDF = () => { const previewPDF = () => {
@@ -173,8 +156,11 @@ const previewPDF = () => {
} }
// 生命周期 // 生命周期
onMounted(() => { onMounted(async () => {
getAssayTask() await getInitData()
if (listData.value.length > 0) {
getAssayTaskDetail(currentTask.value.id)
}
}) })
onBackPress(() => { onBackPress(() => {
@@ -195,7 +181,16 @@ onBackPress(() => {
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
.sample-item {
position: relative;
border-bottom: 1px solid #eee;
padding: 4px;
.sample-status {
position: absolute;
top: 0;
right: 0;
}
}
.content-main-left { .content-main-left {
background-color: #f6f6f6; background-color: #f6f6f6;
} }

View File

@@ -51,7 +51,10 @@
v-for="(sample, index) in sampleDataList" v-for="(sample, index) in sampleDataList"
:key="index" :key="index"
class="u-tab-item" class="u-tab-item"
:class="currentSampleIndex === index ? 'u-tab-item-active' : ''" :class="[
currentSampleIndex === index ? 'u-tab-item-active' : '',
sample.rollbackStatus === 'in_progress' ? 'u-tab-item-disabled' : ''
]"
:data-current="index" :data-current="index"
@tap.stop="switchSample(index, false)" @tap.stop="switchSample(index, false)"
> >
@@ -60,11 +63,25 @@
<view> <view>
{{ sample.sampleCode }} {{ sample.sampleCode }}
</view> </view>
<view> {{ getDataSourceTypeShow(sample.dataSourceType) }}{{ sample.sampleName }} </view> <view> {{ sample.sampleName }} </view>
</view> </view>
</view> </view>
</scroll-view> </scroll-view>
<u-button class="btn-operation" type="primary" @click="submitTask()">数据上报</u-button> <u-button
v-if="taskIngredientsStatus === 'allow_submit'"
class="btn-operation"
type="primary"
@click="submitTask()"
>数据上报</u-button
>
<u-button
v-else
class="btn-operation"
type="primary"
:disabled="taskIngredientsStatus === 'in_progress'"
@click="handleIngredients()"
>{{ taskIngredientsStatus === 'initial' ? '下发配料' : '等待配料' }}</u-button
>
</u-col> </u-col>
<u-col span="6"> <u-col span="6">
<view class="field-name" v-html="selectedField.title" /> <view class="field-name" v-html="selectedField.title" />
@@ -248,6 +265,7 @@ const { proxy } = getCurrentInstance()
// 响应式数据定义 // 响应式数据定义
const taskId = ref('') const taskId = ref('')
const taskIngredientsStatus = ref('') //配料状态
const configReportTemplateKey = ref('') const configReportTemplateKey = ref('')
const elId = nx.$helper.uuid() const elId = nx.$helper.uuid()
const scrollTop = ref(0) //tab标题的滚动条位置 const scrollTop = ref(0) //tab标题的滚动条位置
@@ -378,9 +396,17 @@ const autoNextField = () => {
//自动切换到下一个样品 //自动切换到下一个样品
const autoNextSample = () => { const autoNextSample = () => {
if (sampleDataList.value.length <= currentSampleIndex.value + 1) return let index = currentSampleIndex.value + 1
const index = currentSampleIndex.value + 1 // 在数组范围内,且当前样品是 in_progress就继续往后找
while (index < sampleDataList.value.length) {
const sample = sampleDataList.value[index]
if (sample.rollbackStatus !== 'in_progress') {
// 找到了合法的样品,切换
switchSample(index, true) switchSample(index, true)
return
}
index++
}
} }
//手动切换样品 //手动切换样品
@@ -831,11 +857,16 @@ function handleAssayTypeChange({ index, value }) {
} }
// 获取任务数据 // 获取任务数据
async function getSampleAnalysisByTaskId() { async function getSampleAnalysisByTaskId() {
const { assayTaskAnalysisDataList, configAssayMethodProjectRangeList, businessAssayTasNo, formValue } = const {
await nx.$api.assayTask.batchSampleAndQcAnalysisByTaskId(taskId.value) assayTaskAnalysisDataList,
configAssayMethodProjectRangeList,
businessAssayTasNo,
formValue,
ingredientsStatus
} = await nx.$api.assayTask.batchSampleAndQcAnalysisByTaskId(taskId.value)
title.value = '样品分析-任务单编号:' + businessAssayTasNo title.value = '样品分析-任务单编号:' + businessAssayTasNo
taskIngredientsStatus.value = ingredientsStatus
// 处理分析数据 // 处理分析数据
assayGroups.value = assayTaskAnalysisDataList.map(group => { assayGroups.value = assayTaskAnalysisDataList.map(group => {
// 必须深拷贝 datas防止多个表格共享引用 // 必须深拷贝 datas防止多个表格共享引用
const tableData = JSON.parse(JSON.stringify(group.datas || [])) const tableData = JSON.parse(JSON.stringify(group.datas || []))
@@ -1083,29 +1114,29 @@ const auncelDoSelect = res => {
//提交任务单 //提交任务单
const submitTask = () => { const submitTask = () => {
// const params = { if (checkSampleReturning()) return
// businessAssayTaskId: taskId.value
// }
// const msg = '请确认所有样品数据都已保存,然后再提交指派单!是否继续?'
// uni.showModal({
// title: '提示',
// content: msg,
// cancelColor: '#0055A2',
// confirmColor: '#0055A2',
// success: res => {
// if (res.cancel) return
// nx.$api.assayTask.submitTask(params).then(res => {
// uni.navigateTo({
// url: '/pages/analysis/sample/sample-report-search'
// })
// })
// }
// })
const url = `/pages/analysis/sample/pdf-preview?businessAssayTaskId=${taskId.value}&reportKey=${configReportTemplateKey.value}&showConfirmBtn=true` const url = `/pages/analysis/sample/pdf-preview?businessAssayTaskId=${taskId.value}&reportKey=${configReportTemplateKey.value}&showConfirmBtn=true`
uni.navigateTo({ url }) uni.navigateTo({ url })
} }
// 配料下发
function handleIngredients() {
if (checkSampleReturning()) return
const params = {
businessAssayTaskId: businessAssayTaskId.value
}
nx.$api.assayTask.taskIngredients(params).then(res => {
getSampleAnalysisByTaskId()
})
}
function checkSampleReturning() {
const analysisSampleData = assayGroups.value.filter(t => t.value == 'analysis').tableData
const hasReturning = analysisSampleData.some(item => item.rollbackStatus === 'in_progress')
if (hasReturning) {
nx.$helper.showToast('该任务单存在退回中的样品')
return true
}
return false
}
// 生命周期 // 生命周期
const { lockOrientation } = useScreenOrientation() const { lockOrientation } = useScreenOrientation()
@@ -1242,6 +1273,11 @@ onBackPress(() => {
color: #0055a2; color: #0055a2;
font-weight: 600; font-weight: 600;
} }
.u-tab-item-disabled {
background-color: #ddd;
pointer-events: none;
opacity: 0.6;
}
.btn-operation { .btn-operation {
width: 80%; width: 80%;

View File

@@ -25,44 +25,35 @@
<u-col span="8"> <u-col span="8">
<view class="content-title-name"> <view class="content-title-name">
<text>样品列表</text> <text>样品列表</text>
<up-badge v-if="sampleList.length > 0" class="ml5" :value="sampleList.length" type="warning"></up-badge>
</view> </view>
<u-gap height="5" bg-color="#0055A2"></u-gap> <u-gap height="5" bg-color="#0055A2"></u-gap>
<view> <view>
<scroll-view scroll-y scroll-with-animation style="height: calc(75vh - 60px)"> <scroll-view scroll-y scroll-with-animation style="height: calc(75vh - 60px)">
<u-checkbox-group placement="column" v-model="checkedSampleCodes"> <u-checkbox-group placement="column" v-model="checkedSampleCodes">
<block v-for="(sample, index) in sampleList" :key="index"> <block v-for="(sample, index) in sampleList" :key="index">
<view v-if="currentTask.reviewCount === sample.reviewCount" class="p5 fs16"> <view v-if="currentTask.reviewCount === sample.reviewCount" class="sample-item">
<u-row> <u-row>
<u-col span="2" class="text-center"> <u-col span="2" class="text-center">
<u-row> <u-row>
<u-col span="6" class="text-center"> <u-col span="4" class="text-center">
<!-- v-if=" <u-checkbox :disabled="sampleDisabled(sample.rollbackStatus)" :name="sample.id"></u-checkbox>
sample.rollbackStatus !== 'running' &&
sample.rollbackStatus !== 'finished'
" -->
<u-checkbox :name="sample.id"></u-checkbox>
</u-col> </u-col>
<u-col span="6" class="text-center"> <u-col span="8" class="text-center">
<view <view
><text>{{ index + 1 }}</text></view ><text>{{ index + 1 }}</text></view
> >
</u-col> </u-col>
</u-row> </u-row>
</u-col> </u-col>
<u-col span="10" class="flex"> <u-col span="10">
<u-row> <u-row>
<u-col span="6"> <u-col span="4">
<view class="sample_desc">
<view> <view>
<view {{ sample.sampleAssayCode }}
><text class="pl10">{{ sample.sampleAssayCode }}</text></view </view>
>
<view> <view>
<text class="pl10"> {{ sample.sampleName }}
{{ getDataSourceTypeShow(sample.dataSourceType) }}{{ sample.sampleName }}
</text>
</view>
</view>
</view> </view>
</u-col> </u-col>
<u-col span="6"> <u-col span="6">
@@ -73,7 +64,12 @@
</u-row> </u-row>
</u-col> </u-col>
</u-row> </u-row>
<u-line class="p5" color="#bbb" /> <u-icon
v-if="sample.rollbackStatus"
class="sample-status"
:name="`/static/images/status/${sampleReturnIcon(sample.rollbackStatus)}.png`"
size="45"
/>
</view> </view>
</block> </block>
</u-checkbox-group> </u-checkbox-group>
@@ -111,7 +107,7 @@
<script setup> <script setup>
import { ref, computed, getCurrentInstance } from 'vue' import { ref, computed, getCurrentInstance } from 'vue'
import { onLoad, onBackPress } from '@dcloudio/uni-app' import { onLoad, onBackPress, onPullDownRefresh } from '@dcloudio/uni-app'
import nx from '@/nx' import nx from '@/nx'
import { useScreenOrientation } from '@/nx/hooks/useScreenOrientation' import { useScreenOrientation } from '@/nx/hooks/useScreenOrientation'
import { getDataSourceTypeShow } from '../common' import { getDataSourceTypeShow } from '../common'
@@ -128,6 +124,21 @@ const taskList = ref([])
const sampleList = ref([]) const sampleList = ref([])
const checkedSampleCodes = ref([]) const checkedSampleCodes = ref([])
const sampleReturnIcon = rollbackStatus => {
switch (rollbackStatus) {
case 'returned':
return 'return_returned'
case 'rejected':
return 'return_rejected'
case 'in_progress':
return 'return_in_progress'
default:
return ''
}
}
const sampleDisabled = rollbackStatus => {
return rollbackStatus === 'returned' || rollbackStatus === 'in_progress'
}
// 计算属性 // 计算属性
const userInfo = computed(() => nx.$store('user').userInfo) const userInfo = computed(() => nx.$store('user').userInfo)
@@ -135,6 +146,12 @@ const userInfo = computed(() => nx.$store('user').userInfo)
const customBack = () => { const customBack = () => {
uni.reLaunch({ url: '/pages/analysis/index/index' }) uni.reLaunch({ url: '/pages/analysis/index/index' })
} }
onPullDownRefresh(() => {
getAssayTask().finally(() => {
uni.stopPullDownRefresh()
uni.showToast({ title: '刷新成功', icon: 'none' })
})
})
const showRollbackModal = () => { const showRollbackModal = () => {
if (checkedSampleCodes.value.length === 0) { if (checkedSampleCodes.value.length === 0) {
uni.showToast({ title: '请选择要退回的样品!', icon: 'none' }) uni.showToast({ title: '请选择要退回的样品!', icon: 'none' })
@@ -149,23 +166,14 @@ const applyRollbackSample = () => {
return return
} }
showRollbackModalFlag.value = false
uni.showModal({
title: '提示',
content: '发起申请,退回已勾选的样品,是否继续?',
cancelColor: '#0055A2',
confirmColor: '#0055A2',
success: res => {
if (res.cancel) return
const data = { const data = {
remark: rollbackContent.value, backDesc: rollbackContent.value,
taskId: currentTask.value.id, idList: checkedSampleCodes.value
detailIds: checkedSampleCodes.value.join(',')
} }
nx.$api.assayTask.createRollbackApply(data).then(() => { nx.$api.assayTask.rollbackAssayTask(data).then(() => {
getAssayTaskDetail(currentTask.value.id) getAssayTaskDetail(currentTask.value.id)
}) showRollbackModalFlag.value = false
} checkedSampleCodes.value = []
}) })
} }
@@ -202,14 +210,14 @@ const switchTask = async index => {
checkedSampleCodes.value = [] checkedSampleCodes.value = []
} }
const getAssayTask = () => { const getAssayTask = async () => {
rollbackContent.value = '' rollbackContent.value = ''
const param = { const param = {
taskAssignStatus: 'submitted', taskAssignStatus: 'submitted',
taskAssayStatusList: ['not_start', 'saved'] taskAssayStatusList: ['not_start', 'saved']
// assayOper: userInfo.value.nickname // assayOper: userInfo.value.nickname
} }
nx.$api.assayTask.getAssayTaskList(param).then(res => { const res = await nx.$api.assayTask.getAssayTaskList(param)
if (res) { if (res) {
taskList.value = res taskList.value = res
if (taskList.value.length > 0) { if (taskList.value.length > 0) {
@@ -217,12 +225,11 @@ const getAssayTask = () => {
getAssayTaskDetail(currentTask.value.id) getAssayTaskDetail(currentTask.value.id)
} }
} }
})
} }
const getAssayTaskDetail = businessAssayTaskId => { const getAssayTaskDetail = businessAssayTaskId => {
sampleList.value = [] sampleList.value = []
nx.$api.assayTask.getAssayTaskDataList({ businessAssayTaskId }).then(res => { nx.$api.assayTask.getAssayTaskDetailList({ businessAssayTaskId }).then(res => {
const list = res || [] const list = res || []
list.forEach(item => (item.checked = false)) list.forEach(item => (item.checked = false))
sampleList.value = list sampleList.value = list
@@ -270,6 +277,16 @@ onBackPress(() => {
background-color: #f6f6f6; background-color: #f6f6f6;
} }
.sample-item {
position: relative;
border-bottom: 1px solid #eee;
padding: 4px;
.sample-status {
position: absolute;
top: 0;
right: 0;
}
}
.content-main-right-operation { .content-main-right-operation {
height: 50px; height: 50px;
padding-top: 10px; padding-top: 10px;
@@ -279,14 +296,4 @@ onBackPress(() => {
font-size: 18px; font-size: 18px;
width: 95%; width: 95%;
} }
.sample_desc {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
}
.sample_desc_warn {
color: red;
padding-right: 10px;
}
</style> </style>

View File

@@ -13,8 +13,8 @@ import { reactive, ref, computed, onMounted } from 'vue'
import nx from '@/nx' import nx from '@/nx'
import { useGridCol } from '@/nx/hooks/useGridCol' import { useGridCol } from '@/nx/hooks/useGridCol'
let list = reactive([ let list = reactive([
{ url: '/pages/lims/index/index', name: '设备管理系统', icon: 'device' }, { url: '/pages/lims/index/index', name: '设备管理', icon: 'device' },
{ url: '/pages/analysis/index/index', name: '化学分析系统', icon: 'analyse' } { url: '/pages/analysis/index/index', name: '分析管理', icon: 'analyse' }
]) ])
// const sysMenus = computed(() => nx.$store('user').sysMenus) // const sysMenus = computed(() => nx.$store('user').sysMenus)

View File

@@ -8,7 +8,7 @@
</view> </view>
<u-cell-group> <u-cell-group>
<u-cell icon="grid-fill" title="切换系统" :is-link="true" @click="handleTo('/pages/index/index')" /> <u-cell icon="grid-fill" title="模块选择" :is-link="true" @click="handleTo('/pages/index/index')" />
<u-cell icon="info-circle-fill" title="关于我们" :is-link="true" @click="handleTo('/pages/me/aboutMe')" /> <u-cell icon="info-circle-fill" title="关于我们" :is-link="true" @click="handleTo('/pages/me/aboutMe')" />
</u-cell-group> </u-cell-group>

View File

@@ -3,7 +3,7 @@
<view> <view>
<view class="aboutme"> <view class="aboutme">
<view class="content"> <view class="content">
<image class="logo" src="../../static/images/logo_mas.png"></image> <image class="logo" src="../../static/images/logo_lims.png"></image>
<view style="font-size: 40px; padding-top: 20px">{{ state.appName }}</view> <view style="font-size: 40px; padding-top: 20px">{{ state.appName }}</view>
<view style="padding-top: 20px; font-size: 22px">当前APP版本{{ state.appVersion }}</view> <view style="padding-top: 20px; font-size: 22px">当前APP版本{{ state.appVersion }}</view>
<rich-text :nodes="state.aboutMe"></rich-text> <rich-text :nodes="state.aboutMe"></rich-text>
@@ -23,8 +23,8 @@ import { onLoad } from '@dcloudio/uni-app'
import { reactive } from 'vue' import { reactive } from 'vue'
let state = reactive({ let state = reactive({
pageHeight: 0, pageHeight: 0,
appVersion: '1.1.0', appVersion: '1.0.0',
appName: '管理系统', appName: '实验室管理系统',
copyrightYear: '2019', copyrightYear: '2019',
aboutMe: aboutMe:
'<div style="text-align:left;font-size:20px;padding:18px"><p style="text-indent:2em;line-height:32px"></p></div>' '<div style="text-align:left;font-size:20px;padding:18px"><p style="text-indent:2em;line-height:32px"></p></div>'

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 414 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 411 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 424 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 723 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

BIN
static/images/logo_lims.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 723 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 510 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 938 KiB

BIN
static/images/status/11.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 617 B

After

Width:  |  Height:  |  Size: 537 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 976 B

After

Width:  |  Height:  |  Size: 787 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB