LiLongLong 3 months ago
parent
commit
43e6e9e35a
  1. 1
      package.json
  2. 5
      src/main.ts
  3. 418
      src/pages/Index/History.vue
  4. 2
      src/pages/Index/Regular/Consumables.vue
  5. 12
      src/pages/Index/components/Consumables/BallGrid2.vue
  6. 30
      src/pages/Index/components/Consumables/DragAreaEx.vue
  7. 4
      src/services/Index/history.ts

1
package.json

@ -14,6 +14,7 @@
"axios": "^1.7.7",
"date-fns": "^4.1.0",
"dayjs": "^1.11.13",
"el-table-infinite-scroll": "^3.0.6",
"element-plus": "^2.8.5",
"less": "^4.2.0",
"mitt": "^3.0.1",

5
src/main.ts

@ -3,11 +3,13 @@ import './style.css'
import App from './App.vue'
import router from './router/router'
import ElementPlus from 'element-plus'
import locale from 'element-plus/es/locale/lang/zh-cn'
import 'element-plus/dist/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
// import './mock/index'
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import ElTableInfiniteScroll from "el-table-infinite-scroll";
import Confirm from './components/Confirm.vue'
const pinia = createPinia()
@ -19,5 +21,6 @@ for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component('Confirm', Confirm)
app.use(router)
app.use(pinia)
app.use(ElementPlus)
app.use(ElementPlus, { locale, zIndex: 3000 })
app.use(ElTableInfiniteScroll);
app.mount('#app')

418
src/pages/Index/History.vue

@ -1,51 +1,47 @@
<template>
<!-- 历史页面-->
<div id="history-container">
<!-- 筛选 -->
<!-- <div class="history-filter">
<div class="filter-input">
<el-input v-model="inputValue" placeholder="输入信息">
<template #prefix>
<el-icon class="el-input__icon">
<search />
</el-icon>
<div class="table-box">
<el-table
style="height: 100%"
v-loading="loading"
v-el-table-infinite-scroll="load"
:infinite-scroll-disabled="disabled"
:data="tableData"
show-overflow-tooltip
@row-click="rowClickHandle"
@selection-change="handleSelection"
header-cell-class-name="table-header"
row-class-name="table-row">
<el-table-column type="selection"></el-table-column>
<el-table-column type="index" label="序号" width="50"></el-table-column>
<el-table-column prop="creatDate" label="日期" width="170">
<template #default="scope">
{{ formatDate(scope.row.creatDate) }}
</template>
</el-input>
</div>
<div class="filter-button">
<el-button type="primary" class="search-button" @click="handleSearch" disabled>搜索</el-button>
<el-button class="reload-button" @click="handleReset">重置</el-button>
</div>
</div> -->
<!-- 表格 -->
<div class="history-table" @scroll="onScroll" ref="tableContainer">
<HistoryTable
ref="historyTableRef"
@selectItems="handleSelection"
@selectIds="handleSelectIds"
@select-row="handleSelectRow"
:tableData="tableData"
:loading="loading"
:key="tableKey"
:loadingText="loadingText"
/>
</el-table-column>
<el-table-column prop="sampleId" label="患者ID" ></el-table-column>
<el-table-column prop="sampleBloodType" label="样本类型">
<template #default="scope">
{{
settingTubeStore.bloodTypeKeyMap[scope.row.sampleBloodType].name
}}
</template>
</el-table-column>
<el-table-column prop="lotId" label="批次" ></el-table-column>
<el-table-column prop="results" label="结果">
<template #default="scope">
<div v-for="(r, idx) in scope.row.results" :key="idx">
{{ showResult(r) }}
</div>
</template>
</el-table-column>
</el-table>
</div>
<!-- 功能 -->
<div class="history-function">
<el-button type="primary" plain @click="showActionConfirm('refresh')"
>刷新</el-button
>
<el-button type="primary" plain @click="showActionConfirm('delete')"
>删除</el-button
>
<el-button type="primary" plain @click="showActionConfirm('print')"
>打印</el-button
>
<el-button type="primary" plain @click="showActionConfirm('export')"
>导出LIS</el-button
>
<div class="history-table-footer">
<el-button type="danger" size="large" :disabled="!selectedItems.length" @click="showActionConfirm('delete')">删除</el-button>
<el-button type="primary" size="large" :disabled="!selectedItems.length" @click="showActionConfirm('print')">打印</el-button>
<el-button type="primary" size="large" :disabled="!selectedItems.length" @click="showActionConfirm('export')">导出LIS</el-button>
</div>
<!-- 确认操作弹框 -->
@ -140,17 +136,17 @@
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { ref } from 'vue'
import { HistoryTable, HistoryWarn } from './components/index'
import { HistoryWarn } from './components/index'
import {
getHistoryInfo,
deleteHistoryInfo,
printHistoryInfo,
exportRecordsToLIS,
} from '../../services/Index/index'
} from '@/services'
import HistoryMessage from './components/History/HistoryMessage.vue'
import type { TableItem } from '../../types/Index'
import type { ResultItem, TableItem } from '@/types/Index'
import WarnSvg from '@/assets/Index/History/warn.svg'
import PrintSvg from '@/assets/Index/History/print.svg'
import ErrorSvg from '@/assets/Warn.svg'
@ -163,8 +159,16 @@ defineOptions({
})
const settingTubeStore = useSettingTestTubeStore()
//
const historyTableRef = ref()
const showResult = (t: ResultItem) => {
if (t.status !== 'SUCCESS') {
return '错误'
}
const unit1 = t.resultConverters[0]
return (
t.subProjName + ' ' + (t.result * unit1.A + unit1.B).toFixed(2) + ' ' + unit1.uintstr
)
}
//
const isVisible = ref<boolean>(false)
@ -235,14 +239,11 @@ const actions: Record<
const selectedIds = ref<number[]>([])
//
const handleSelection = (items: TableItem[]) => {
console.log(items)
selectedItems.value = items
}
const handleSelectIds = (ids: number[]) => {
selectedIds.value = ids
}
const rowData = ref<TableItem>()
const handleSelectRow = (item: TableItem) => {
const rowClickHandle = (item: TableItem) => {
isVisible.value = true
rowData.value = item
}
@ -252,18 +253,7 @@ const formatDate = (date: string | number | Date) => {
//
const showActionConfirm = (actionType: string) => {
if (actionType === 'refresh') {
getTableData(true)
return
}
//
if (selectedItems.value.length === 0) {
//
warnMessage.value = '请先选择项目'
warnIcon = WarnSvg
showWarn.value = true
return
}
//
if (actionType === 'delete') {
currentAction.value = {
@ -281,13 +271,36 @@ const loading = ref(false)
//
const tableData = ref<TableItem[]>([])
const currentPage = ref(1)
const pageSize = ref(20)
const pageSize = ref(25)
const total = ref(0)
const totalPage = ref(0)
const tableKey = ref(0) //
const tableContainer = ref(null as HTMLElement | null)
const hasMore = ref(true)
const loadingText = ref('加载中...')
const disabled = ref(false)
const load = () => {
if (disabled.value) return;
loading.value = true
getHistoryInfo({
pageNum: currentPage.value,
pageSize: pageSize.value,
}).then(async (res) => {
currentPage.value++;
total.value = res.data.total
totalPage.value = res.data.totalPage
tableData.value = tableData.value.concat(res.data.list)
if (currentPage.value > totalPage.value) {
disabled.value = true
}
}).catch(() => {
eMessage.error("获取数据失败")
}).finally(() => {
loading.value = false
})
}
const getTableData = async (isReset: boolean = false) => {
if (isReset) {
//
@ -332,30 +345,8 @@ const getHistoryInfoApi = async () => {
console.log('获取数据失败', error)
}
}
const onScroll = (event: any) => {
const container = event.target
const isNearBottom =
container.scrollHeight - container.scrollTop <= container.clientHeight + 100
if (isNearBottom && !loading.value) {
getTableData()
}
}
//
// const handleSearch = async () => {
// console.log('', inputValue.value)
// try {
// const res = await searchHistoryInfo(inputValue.value)
// console.log(res.data.list)
// //
// tableData.value = res.data.list as TableItem[]
// //
// currentPage.value = 1
// hasMore.value = false
// } catch (error) {
// console.error('', error)
// }
// }
//
const handleConfirm = async () => {
@ -366,14 +357,13 @@ const handleConfirm = async () => {
await handleConfirmDelete()
} else if (actionType === 'print') {
//
handlePrint()
await handlePrint()
} else if (actionType === 'export') {
//
handleExport()
await handleExport()
}
}
const handleWarnClose = () => {
getTableData()
showWarn.value = false
}
@ -385,34 +375,18 @@ const handleCancel = () => {
//
const handleConfirmDelete = async () => {
try {
//
for (const item of selectedItems.value) {
const res = await deleteHistoryInfo(item.id)
if (!res.success) {
throw new Error('删除失败')
}
}
//
const deleteIds = selectedItems.value.map((item) => item.id)
tableData.value = tableData.value.filter(
(item) => !deleteIds.includes(item.id),
)
//
if (historyTableRef.value) {
historyTableRef.value.clearSelection()
}
eMessage.success('删除成功')
//
getTableData()
} catch (error) {
eMessage.error('删除失败')
} finally {
showModal.value = false
}
deleteHistoryInfo(deleteIds.join(',')).then((res) => {
if (res.success && res.ecode === 'SUC') {
eMessage.success('删除成功')
tableData.value = tableData.value.filter(
(item) => !deleteIds.includes(item.id),
)
}else {
eMessage.error('删除失败')
}
})
}
let warnIcon: any
//
@ -425,14 +399,6 @@ const handlePrint = async () => {
warnIcon = new URL('@/assets/Index/History/success.svg', import.meta.url)
.href
showWarn.value = true
//
selectedItems.value = []
selectedIds.value = []
//
if (historyTableRef.value?.clearSelection) {
historyTableRef.value.clearSelection()
}
} else {
eMessage.error(res.message || '打印失败')
}
@ -453,14 +419,6 @@ const handleExport = async () => {
warnIcon = new URL('@/assets/Index/History/success.svg', import.meta.url)
.href
showWarn.value = true
//
selectedItems.value = []
selectedIds.value = []
//
if (historyTableRef.value?.clearSelection) {
historyTableRef.value.clearSelection()
}
} else {
eMessage.error(res.message || '导出失败')
}
@ -471,10 +429,6 @@ const handleExport = async () => {
showWarn.value = true
}
}
onMounted(() => {
getTableData()
})
</script>
<style scoped lang="less">
@ -498,176 +452,26 @@ onMounted(() => {
box-sizing: border-box;
}
width: 100%;
height: 90vh;
height: 100%;
display: flex;
flex-direction: column;
background-color: #f5f7fa;
background-color: #fff;
padding: 20px 20px 0 20px;
box-sizing: border-box;
.history-filter {
width: 100%;
height: 70px;
margin-bottom: 20px;
display: flex;
justify-content: space-between;
align-items: center;
background-color: #fff;
border-radius: 10px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
.filter-input {
flex: 1;
max-width: 800px;
.el-input {
height: 50px;
font-size: 26px;
.el-input__wrapper {
border-radius: 25px;
padding: 0 20px;
box-shadow: 0 0 0 1px #dcdfe6;
&:hover {
box-shadow: 0 0 0 1px #409eff;
}
&.is-focus {
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
}
}
.el-input__icon {
font-size: 26px;
color: #909399;
}
}
}
.filter-button {
display: flex;
.search-button,
.reload-button {
height: 50px;
border-radius: 25px;
font-size: 26px;
font-weight: 500;
transition: all 0.3s;
&:hover {
transform: translateY(-2px);
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
}
.search-button {
width: 200px;
&:disabled {
background-color: #a0cfff;
border-color: #a0cfff;
&:hover {
transform: none;
box-shadow: none;
}
}
}
.reload-button {
width: 90px;
color: #606266;
border: 1px solid #dcdfe6;
&:hover {
color: #409eff;
border-color: #c6e2ff;
background-color: #ecf5ff;
}
}
.table-box {
height: calc(100% - 80px);
.el-table {
font-size: 18px;
}
}
.history-table {
flex: 1;
overflow-y: auto; //
overflow-x: hidden; //
background-color: #fff;
border-radius: 10px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
margin: 0 20px;
// padding: 20px;
position: relative;
//
&::-webkit-scrollbar {
width: 6px;
height: 6px;
}
&::-webkit-scrollbar-thumb {
background: #c0c4cc;
border-radius: 3px;
&:hover {
background: #909399;
}
}
&::-webkit-scrollbar-track {
background: #f5f7fa;
}
////
//&::after {
// content: '';
// position: absolute;
// left: 0;
// right: 0;
// bottom: 0;
// height: 30px;
// background: linear-gradient(
// to top,
// rgba(255, 255, 255, 0.9),
// transparent
// );
// pointer-events: none;
//}
}
.history-function {
padding: 0 20px;
width: 100%;
height: 84px;
margin-top: 20px;
background-color: #fff;
box-shadow: 0 -2px 12px 0 rgba(0, 0, 0, 0.05);
.history-table-footer {
height: 80px;
display: flex;
justify-content: center;
align-items: center;
gap: 20px;
justify-content: space-around;
.el-button {
flex: 1 1 auto;
height: 50px;
border-radius: 25px;
font-size: 26px;
font-weight: 500;
transition: all 0.3s;
&:hover {
transform: translateY(-2px);
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
&.is-plain {
background-color: #fff;
&:hover {
background-color: #ecf5ff;
}
}
width: 100px;
font-size: 18px;
}
}
}
@ -690,8 +494,6 @@ onMounted(() => {
.result-success {
display: flex;
gap: 12px;
.res-unit {
}
}
.result-error {
color: red;
@ -727,4 +529,10 @@ onMounted(() => {
}
}
}
:deep(.table-header) {
height: 60px;
}
:deep(.table-row) {
height: 60px;
}
</style>

2
src/pages/Index/Regular/Consumables.vue

@ -342,7 +342,7 @@ const updateTipNum = async ({
sync: boolean
}) => {
if (tipNum < 0) {
return
tipNum = 0
}
if (deviceStore.deviceState.workState !== 'WORKING') {
consumableStore.setDisableUpdateConsumableData(true)

12
src/pages/Index/components/Consumables/BallGrid2.vue

@ -86,17 +86,19 @@ const ballStates = ref(Array.from({ length: props.total }, () => false)) // 小
//
const emit = defineEmits(['update:TipNum'])
const updateSliderVal = async (hVal, vVal) => {
const empty = parseInt(vVal) * columns + parseInt(hVal)
const updateSliderVal = async (empty) => {
if (empty< 0){
empty = 0
}
const activated = total - empty
emit('update:TipNum', activated, props.order, false)
}
const updateSliderEndVal = async (hVal, vVal) => {
const empty = parseInt(vVal) * columns + parseInt(hVal)
const activated = total - empty
emit('update:TipNum', activated, props.order, true)
// const empty = parseInt(vVal) * columns + parseInt(hVal)
// const activated = total - empty
// emit('update:TipNum', activated, props.order, true)
}
//
const gridStyle = computed(() => ({

30
src/pages/Index/components/Consumables/DragAreaEx.vue

@ -84,20 +84,30 @@ function handleDrag(event) {
if (!isDragging.value) return
const touchEvent = event.touches ? event.touches[0] : event
const deltaX = touchEvent.clientX - startX.value
const deltaY = touchEvent.clientY - startY.value
const rect = sliderTrack.value.getBoundingClientRect()
let percentX = (deltaX + startXValue.value) / rect.width
let percentY = (deltaY + startYValue.value) / rect.height
hValue.value = Math.min(1, Math.max(0, percentX)) * hTotalVal.value
vValue.value = Math.min(1, Math.max(0, percentY)) * vTotalVal.value
emit('update:sliderValue', +hValue.value.toFixed(), +vValue.value.toFixed())
const clientX = touchEvent.clientX
const clientY = touchEvent.clientY
const relativeX = clientX - rect.left
const relativeY = clientY - rect.top
const row = Math.ceil(relativeY / (rect.height / vTotalVal.value) )
const col = Math.ceil(relativeX / (rect.width /hTotalVal.value) )
const empty = (row - 1)* hTotalVal.value + col
emit('update:sliderValue', empty)
//
// let percentX = (deltaX + startXValue.value) / rect.width
// let percentY = (deltaY + startYValue.value) / rect.height
//
// hValue.value = Math.min(1, Math.max(0, percentX)) * hTotalVal.value
// vValue.value = Math.min(1, Math.max(0, percentY)) * vTotalVal.value
//
// emit('update:sliderValue', +hValue.value.toFixed(), +vValue.value.toFixed())
}
function handleEnd() {
emit('update:sliderEndValue', +hValue.value.toFixed(), +vValue.value.toFixed())
// emit('update:sliderEndValue', +hValue.value.toFixed(), +vValue.value.toFixed())
isDragging.value = false
document.removeEventListener('mousemove', handleDrag)
document.removeEventListener('touchmove', handleDrag)

4
src/services/Index/history.ts

@ -17,10 +17,10 @@ export const getHistoryInfo = async (params: paramsType) => {
//删除历史记录
export const deleteHistoryInfo = async (id: number) => {
export const deleteHistoryInfo = async (ids: string) => {
try {
const res = await apiClient.post(
`/api/v1/app/reactionResult/deleteRecord?id=${id}`,
`/api/v1/app/reactionResult/deleteRecordByIds?ids=${ids}`,
)
return res.data
} catch (error) {

Loading…
Cancel
Save