质控录入按项目

This commit is contained in:
wyuu 2026-01-14 17:38:50 +08:00
parent be3703bd7a
commit ce27de7ecc
6 changed files with 328 additions and 60 deletions

View File

@ -110,7 +110,31 @@ export function queryValXmdh(data?: Object) {
// 查询质控品录入
export function getqcval(data?: Object) {
return request({
url: '/qc/getqcval',
url: '/qc/getqcvalitem',
method: 'get',
params: data
})
}
// 保存质控数据(当天所有项目)
export function saveqcvalmonth(data?: Object) {
return request({
url: '/qc/saveqcvalmonth',
method: 'post',
data
})
}
// 获取质控项目
export function queryXmItem(data?: Object) {
return request({
url: '/qc/queryitem',
method: 'get',
params: data
})
}
// 查询质控数据(单项目一个月)
export function getqcvalmonth(data?: Object) {
return request({
url: '/qc/getqcvalmonth',
method: 'get',
params: data
})

View File

@ -30,7 +30,7 @@
<vxe-column v-bind="column" v-if="column.field">
<!-- 自定义列模板 -->
<template v-if="column.slotName" #default="scope">
<slot :name="column.slotName" :row="scope.row"></slot>
<slot :name="column.slotName" :row="scope.row" :$index="scope.$rowIndex"></slot>
</template>
</vxe-column>
</template>
@ -168,9 +168,11 @@ onMounted(() => {
// 监听数据变化时重置当前行
watch(() => props.tableData, (newVal) => {
// 自定义索引
props.tableData.forEach((item: any, index) => {
item._index = index + 1
})
if (newVal) {
props.tableData.forEach((item: any, index) => {
item._index = index + 1
})
}
currentRow.value = null
})

45
src/types/Object.d.ts vendored
View File

@ -163,3 +163,48 @@ export class ymTableDataItem {
txtresult: string;
cp_bz: string;
}
// 定义原始数据项的接口
interface OriginalItem {
zkph: string | null;
zkbz: string | null;
cs: number;
xmdh: string;
sd: string | null;
qccv: number | null;
xmmc: string;
cv: string | null;
zkjg: string | null;
yq: string | null;
ld: string | null;
zkrq: string | null;
}
// 定义原始数据的接口
interface OriginalData {
cs1: OriginalItem[];
cs2: OriginalItem[];
cs3: OriginalItem[];
cs4: OriginalItem[];
cs5: OriginalItem[];
cs6: OriginalItem[];
cs7: OriginalItem[];
}
// 定义最终结果的接口
interface ResultItem {
zkph: number;
yq: string | undefined;
zkrq: string;
xmdh: string;
xmmc: string;
sd: string;
zkbz: string;
cs1: string;
cs2: string;
cs3: string;
cs4: string;
cs5: string;
cs6: string;
cs7: string;
}

View File

@ -16,9 +16,9 @@
<span class="tips">靶值列表</span> &nbsp;
<el-button type="primary" plain @click="dateClick">复制第一个开始日期到每个项目</el-button>
<el-button type="primary" plain icon="Check" @click="handleSave">保存</el-button>
<el-button type="primary" plain @click="">复制靶值</el-button>
<el-button type="primary" plain @click="">新增靶值</el-button>
<el-button type="danger" plain @click="">删除靶值</el-button>
<el-button type="primary" plain @click="copyBzHandle">复制靶值</el-button>
<el-button type="primary" plain @click="addBzHandle">新增靶值</el-button>
<el-button type="danger" plain @click="delBzHandle">删除靶值</el-button>
<el-button type="primary" plain @click="">计算</el-button>
</el-col>
</el-row>
@ -66,11 +66,14 @@
style="width:100%" />
</template>
<template #ff="{ row }">
<SelectTable v-model:data="row.ff" :tableData="dictData.MD" placeholder="" class="full-width-input" />
<SelectTable v-if="row.show" v-model:data="row.ff" :tableData="dictData.MD" placeholder=""
class="full-width-input" />
<span class="text_show" v-else @click="row.show = true">{{ formatDict(row.ff, 'MD') }}</span>
</template>
<template #sjph="{ row }">
<SelectTable v-model:data="row.sjph" :tableData="dictData.LOT" value="label" objKey="label" placeholder=""
class="full-width-input" />
<SelectTable v-if="row.show" v-model:data="row.sjph" :tableData="dictData.LOT" value="label"
objKey="label" placeholder="" class="full-width-input" />
<span class="text_show" v-else @click="row.show = true">{{ row.sjph }}</span>
</template>
<template #qccv="{ row }">
<el-input v-model="row.qccv" class="full-width-input" />
@ -350,16 +353,46 @@ const getBatchList = () => {
queryBatch({ yq: yq.value }).then((res: any) => {
loading.value = false
tableData.value = res.data || []
getXmList(tableData.value[0])
if (tableData.value.length) {
getXmList(tableData.value[0])
} else {
topTableData.value = []
bottomTableData.value = []
}
})
}
const getXmList = (row: any) => {
queryBatchXm({ yq: yq.value, zkpph: row.zkpph, zkph: row.zkph }).then((res: any) => {
res.data.forEach((item: any) => { item.show = false })
topTableData.value = res.data
rowClick(topTableData.value[0])
if (topTableData.value.length) {
rowClick(topTableData.value[0])
}
})
}
const copyBzHandle = () => {
}
const addBzHandle = () => {
topTableData.value.forEach((item: any) => {
item.zkbz = ''
item.sd = ''
item.cv = ''
item.bc = ''
item.ksrq = ''
item.stopdate = ''
item.ff = ''
item.sjph = ''
item.qccv = ''
})
}
const delBzHandle = () => {
ElMessageBox.confirm('是否确认删除选中数据?', '提示', {
type: 'warning'
}).then(() => {
})
}
const handleSave = () => { }
watch(yq, (val) => {
@ -390,6 +423,12 @@ watch(yq, (val) => {
}
}
.text_show {
display: block;
width: 100%;
height: 1.875rem;
}
.mytable-style {
:deep(.el-input__prefix) {
display: none;

View File

@ -12,14 +12,14 @@
<el-row :gutter="20">
<el-col :span="4">
<el-form-item label="日期:">
<el-date-picker v-model="queryParams.zkrq" type="date" value-format="YYYY-MM-DD" style="width:100%" />
<el-date-picker v-model="queryParams.zkrq" type="month" value-format="YYYY-MM" style="width:100%"
@change="getXmInfo" />
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label="项目:">
<el-select v-model="queryParams.xmdh" placeholder="请选择" style="width:100%">
<el-option />
</el-select>
<SelectTable v-model:data="queryParams.xmdh" :tableData="xmList" placeholder=""
@get-data-value="getXmInfo" />
</el-form-item>
</el-col>
<el-col :span="1.5">
@ -33,15 +33,15 @@
</el-form>
</div>
<el-tabs v-model="activeName" type="card" class="tabs_box">
<el-tab-pane v-for="tab in tabList" :key="tab.key" :label="tab.label" :name="tab.key" />
<el-tabs v-model="activeName" type="card" class="tabs_box" @tab-click="tabClick">
<el-tab-pane v-for="tab in zkps" :key="tab" :label="`质控${tab}`" :name="tab" />
</el-tabs>
<div class="table-box">
<CustomVxeTable class="mytable-style" ref="tableRef" :table-data="tableData" :columns="columns"
:scrollYConfig="{ enabled: true, rSize: 30, height: '100%', }" :cell-style="cellStyleHd">
<template #xmdh="{ row }">
{{ row.xmdh }} {{ row.xmmc }}
<template #zkrq="{ row, $index }">
{{ $index + 1 }}
</template>
<template #cs1="{ row }">
<el-input v-model="row.cs1" class="full-width-input" />
@ -70,25 +70,26 @@
</template>
<script setup lang="ts">
import { getqcval } from '@/api/liswork/qualityControl/index'
import { getqcvalmonth, queryXmItem } from '@/api/liswork/qualityControl/index'
import dayjs from 'dayjs'
import { ResultItem, OriginalData, OriginalItem } from '@/types/index'
import { queryValue } from '@/api/liswork/xtwh/ComOpt'
const yq = defineModel<string>()
const activeName = ref('1')
const activeName = ref(1)
const queryParams = ref({
xmdh: '',
zkrq: ref(dayjs().format('YYYY-MM-DD')),
zkrq: ref(dayjs().format('YYYY-MM')),
})
const zkps = ref(1)
const tabList = [
{ key: '1', label: '质控1' },
{ key: '2', label: '质控2' }
]
const tableData = ref([])
const tableData = ref<ResultItem[]>([])
const columns = ref([
{ field: 'xmdh', title: '日期', resizable: true, width: 200 },
{ field: 'zkrq', title: '日期', resizable: true, width: 50, slotName: 'zkrq', align: 'center' },
{ field: 'cs1', title: '次数1', resizable: true, slotName: 'cs1' },
{ field: 'cs2', title: '次数2', resizable: true, slotName: 'cs2' },
{ field: 'cs3', title: '次数3', resizable: true, slotName: 'cs3' },
@ -97,7 +98,7 @@ const columns = ref([
{ field: 'cs6', title: '次数6', resizable: true, slotName: 'cs6' },
{ field: 'cs7', title: '次数7', resizable: true, slotName: 'cs7' },
{ field: 'zkbz', title: '靶数', resizable: true, align: 'center' },
{ field: 'qcusepos', title: 'SD', resizable: true, align: 'center' },
{ field: 'sd', title: 'SD', resizable: true, align: 'center' },
])
const cellStyleHd = ({ row, column }: {
row: any;
@ -110,33 +111,114 @@ const cellStyleHd = ({ row, column }: {
{ min: 2, max: 3, color: '#ffc0c0 !important' } // 2~3:粉色
]
}
const targetField = styleRules.columnMap[column.title];
const targetField = styleRules.columnMap[column.field];
if (!targetField || row.qcusepos === 0) return {};
const deviation = Math.abs(row.zkbz - row[targetField]) / row.qcusepos;
if (!targetField || row.zkbz === 0) return;
if (!row[targetField]) return;
const deviation = Math.abs(row.zkbz - row[targetField]) / row.sd;
for (const rule of styleRules.colorRules) {
if (deviation > rule.min && (!rule.max || deviation < rule.max)) {
return { background: rule.color };
return { background: rule.color, color: '#fff !important' };
}
}
return {};
}
const tabClick = (tab: { paneName: number }) => {
activeName.value = tab.paneName
getXmInfo()
}
const xmList = ref([])
const getXmList = () => {
getqcval({ yq: yq.value, ...queryParams.value }).then((res: any) => {
tableData.value = res.data
queryXmItem({ yq: yq.value }).then((res: any) => {
xmList.value = res.data.map((item: any) => {
return {
label: item.xmmc,
value: item.xmdh,
}
})
})
}
const getXmInfo = () => {
getqcvalmonth({ yq: yq.value, ...queryParams.value, zkph: activeName.value }).then((res: any) => {
const mergedResult = mergeDataByXmdh(res.data);
tableData.value = mergedResult
})
}
// 合并数据
const mergeDataByXmdh = (rawData: OriginalData): ResultItem[] => {
const allItems: OriginalItem[] = [
...rawData.cs1,
...rawData.cs2,
...rawData.cs3,
...rawData.cs4,
...rawData.cs5,
...rawData.cs6,
...rawData.cs7
];
// 以zkrq为键分组
const grouped: Record<string, OriginalItem[]> = {};
allItems.forEach(item => {
if (item.zkrq != null && item.zkrq !== '') {
if (!grouped[item.zkrq]) {
grouped[item.zkrq] = [];
}
grouped[item.zkrq].push(item);
}
});
const result: ResultItem[] = [];
Object.keys(grouped).forEach(zkrq => {
const items = grouped[zkrq];
// 取第一个项目的基础信息(xmmc、sd、zkbz)
const baseItem = items[0];
const resultItem: ResultItem = {
zkrq,
xmdh: baseItem.xmdh || '',
yq: yq.value,
zkph: activeName.value,
xmmc: baseItem.xmmc || '',
sd: baseItem.sd || '',
zkbz: baseItem.zkbz || '',
cs1: '',
cs2: '',
cs3: '',
cs4: '',
cs5: '',
cs6: '',
cs7: ''
};
// 填充各次数的zkjg值
items.forEach(item => {
const csKey = `cs${item.cs}` as keyof ResultItem;
if (resultItem.hasOwnProperty(csKey)) {
(resultItem as any)[csKey] = item.zkjg || '';
}
});
result.push(resultItem);
});
return result;
}
const handleSave = () => { }
watch(yq, (val) => {
if (val) {
// getXmList()
queryValue({ yq: yq.value, xxdh: 'QCSAMPLECNT' }).then((res: any) => {
zkps.value = Number(res.data) || 1
})
getXmList()
}
}, { immediate: true })
</script>

View File

@ -25,13 +25,14 @@
</el-form>
</div>
<el-tabs v-model="activeName" type="card" class="tabs_box">
<el-tab-pane v-for="tab in tabList" :key="tab.key" :label="tab.label" :name="tab.key" />
<el-tabs v-model="activeName" type="card" class="tabs_box" @tab-click="tabClick">
<el-tab-pane v-for="tab in zkps" :key="tab" :label="`质控${tab}`" :name="tab" />
</el-tabs>
<div class="table-box">
<CustomVxeTable class="mytable-style" ref="tableRef" :table-data="tableData" :columns="columns"
:scrollYConfig="{ enabled: true, rSize: 30, height: '100%', }" :cell-style="cellStyleHd">
:scrollYConfig="{ enabled: true, rSize: 30, height: '100%', }" :cell-style="cellStyleHd"
:highlightCurrentRow="false">
<template #xmdh="{ row }">
{{ row.xmdh }} {{ row.xmmc }}
</template>
@ -62,19 +63,20 @@
</template>
<script setup lang="ts">
import { getqcval } from '@/api/liswork/qualityControl/index'
import { getqcval, saveqcvalmonth } from '@/api/liswork/qualityControl/index'
import { queryValue } from '@/api/liswork/xtwh/ComOpt'
import dayjs from 'dayjs'
import { color } from 'echarts';
import { ElMessage } from 'element-plus';
import { ResultItem, OriginalData, OriginalItem } from '@/types/index'
const yq = defineModel<string>()
const activeName = ref('1')
const activeName = ref(1)
const zkrq = ref(dayjs().format('YYYY-MM-DD'))
const tabList = [
{ key: '1', label: '质控1' },
{ key: '2', label: '质控2' }
]
const zkps = ref(1)
const tableData = ref([])
const tableData = ref<ResultItem[]>([])
const columns = ref([
{ field: 'xmdh', title: '质控项目', resizable: true, slotName: 'xmdh', width: 200 },
{ field: 'cs1', title: '次数1', resizable: true, slotName: 'cs1' },
@ -85,7 +87,7 @@ const columns = ref([
{ field: 'cs6', title: '次数6', resizable: true, slotName: 'cs6' },
{ field: 'cs7', title: '次数7', resizable: true, slotName: 'cs7' },
{ field: 'zkbz', title: '靶数', resizable: true, align: 'center' },
{ field: 'qcusepos', title: 'SD', resizable: true, align: 'center' },
{ field: 'sd', title: 'SD', resizable: true, align: 'center' },
])
const cellStyleHd = ({ row, column }: {
row: any;
@ -98,34 +100,108 @@ const cellStyleHd = ({ row, column }: {
{ min: 2, max: 3, color: '#ffc0c0 !important' } // 2~3:粉色
]
}
const targetField = styleRules.columnMap[column.title];
const targetField = styleRules.columnMap[column.field];
if (!targetField || row.qcusepos === 0) return {};
const deviation = Math.abs(row.zkbz - row[targetField]) / row.qcusepos;
if (!targetField || row.zkbz === 0) return;
if (!row[targetField]) return;
const deviation = Math.abs(row.zkbz - row[targetField]) / row.sd;
for (const rule of styleRules.colorRules) {
if (deviation > rule.min && (!rule.max || deviation < rule.max)) {
return { background: rule.color };
return { background: rule.color, color: '#fff !important' };
}
}
return {};
}
const getXmList = () => {
getqcval({ yq: yq.value, zkrq: zkrq.value }).then((res: any) => {
tableData.value = res.data
})
const tabClick = (tab: { paneName: number }) => {
activeName.value = tab.paneName
getXmList()
}
const handleSave = () => { }
const getXmList = () => {
getqcval({ yq: yq.value, zkrq: zkrq.value, zkph: activeName.value }).then((res: any) => {
const mergedResult = mergeDataByXmdh(res.data);
tableData.value = mergedResult
})
}
// 合并数据
const mergeDataByXmdh = (rawData: OriginalData): ResultItem[] => {
const allItems: OriginalItem[] = [
...rawData.cs1,
...rawData.cs2,
...rawData.cs3,
...rawData.cs4,
...rawData.cs5,
...rawData.cs6,
...rawData.cs7
];
// 以xmdh为键分组
const grouped: Record<string, OriginalItem[]> = {};
allItems.forEach(item => {
if (!grouped[item.xmdh]) {
grouped[item.xmdh] = [];
}
grouped[item.xmdh].push(item);
});
const result: ResultItem[] = [];
Object.keys(grouped).forEach(xmdh => {
const items = grouped[xmdh];
// 取第一个项目的基础信息(xmmc、sd、zkbz)
const baseItem = items[0];
const resultItem: ResultItem = {
xmdh,
yq: yq.value,
zkrq: zkrq.value,
zkph: activeName.value,
xmmc: baseItem.xmmc || '',
sd: baseItem.sd || '',
zkbz: baseItem.zkbz || '',
cs1: '',
cs2: '',
cs3: '',
cs4: '',
cs5: '',
cs6: '',
cs7: ''
};
// 填充各次数的zkjg值
items.forEach(item => {
const csKey = `cs${item.cs}` as keyof ResultItem;
if (resultItem.hasOwnProperty(csKey)) {
(resultItem as any)[csKey] = item.zkjg || '';
}
});
result.push(resultItem);
});
return result;
}
const handleSave = () => {
saveqcvalmonth(tableData.value).then((res: any) => {
if (res.code == 0) {
getXmList()
ElMessage.success('保存成功')
}
})
}
watch(yq, (val) => {
if (val) {
getXmList()
queryValue({ yq: yq.value, xxdh: 'QCSAMPLECNT' }).then((res: any) => {
zkps.value = Number(res.data) || 1
})
}
}, { immediate: true })
</script>