luojie 1 week ago
parent
commit
320524f707
32 changed files with 3639 additions and 6 deletions
  1. +8
    -0
      src/App.vue
  2. +392
    -0
      src/components/Template/BaseInfoFormPcakge.vue
  3. +163
    -0
      src/components/Template/CustomTable.vue
  4. +200
    -0
      src/components/Template/HandleFormItem.vue
  5. +258
    -0
      src/components/Template/Input.vue
  6. +43
    -0
      src/components/Template/LineLabel.vue
  7. +357
    -0
      src/components/Template/Step.vue
  8. +66
    -0
      src/components/Template/StepComponents/AddSolutionStep.vue
  9. +89
    -0
      src/components/Template/StepComponents/AutoWeighStep.vue
  10. +62
    -0
      src/components/Template/StepComponents/CentrifugeStep.vue
  11. +152
    -0
      src/components/Template/StepComponents/CoatStep.vue
  12. +71
    -0
      src/components/Template/StepComponents/ContainerStep.vue
  13. +84
    -0
      src/components/Template/StepComponents/FiltrationStep.vue
  14. +50
    -0
      src/components/Template/StepComponents/ManualWeighStep.vue
  15. +59
    -0
      src/components/Template/StepComponents/MixStep.vue
  16. +90
    -0
      src/components/Template/StepComponents/PackageStep.vue
  17. +113
    -0
      src/components/Template/StepComponents/Seal2Step.vue
  18. +114
    -0
      src/components/Template/StepComponents/Seal3Step.vue
  19. +144
    -0
      src/components/Template/StepComponents/StaticEndStep.vue
  20. +142
    -0
      src/components/Template/StepComponents/StaticStartStep.vue
  21. +147
    -0
      src/components/Template/StepComponents/TakePlateStep.vue
  22. +50
    -0
      src/components/Template/StepComponents/UltrasoundStep.vue
  23. +124
    -0
      src/components/Template/StepComponents/VortexStep.vue
  24. +33
    -0
      src/components/Template/Table.vue
  25. +3
    -1
      src/store/index.js
  26. +17
    -0
      src/store/modules/template.js
  27. +29
    -2
      src/views/business/comps/template/TemplateTable.vue
  28. +297
    -0
      src/views/business/comps/template/comps/sp/Demo.vue
  29. +263
    -0
      src/views/business/comps/template/comps/sp/SWYPFXRYPZB.vue
  30. +16
    -0
      src/views/business/comps/template/mixins/templateMixin.js
  31. +1
    -1
      src/views/business/template/list.vue
  32. +2
    -2
      vue.config.js

+ 8
- 0
src/App.vue View File

@ -450,4 +450,12 @@ export default {
}
}
}
.template-form-item {
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1);
margin-top: 24px;
padding: 24px;
}
</style>

+ 392
- 0
src/components/Template/BaseInfoFormPcakge.vue View File

@ -0,0 +1,392 @@
<template>
<div>
<div v-for="(item, index) in formConfig" :key="index">
<template v-if="item.type === 'cardItem'">
<div class="grid-container">
<div v-for="(sItem, key) in item.config" class="form-item" :class="sItem.span == 1 ? 'full-row' : ''"
:key="key">
<template v-if="sItem.type === 'input'">
<div class="form-title">{{ sItem.label }}</div>
<HandleFormItem :item="sItem" v-model="formFields[key]"
@copy="onCopy(sItem, key)" />
</template>
</div>
</div>
</template>
<template v-else-if="item.type === 'conditionItem'">
<div class="form-item ">
<div class="form-title fs-16" v-if="item.label">{{ item.label }}</div>
<div v-for="(sItem, key) in item.config" class="c-Item grid-container">
<div class="p-r-20">
<div class="form-title">{{ sItem.label }}</div>
<div class="flex ">
<HandleFormItem type="select" :item="sItem" v-model="formFields[key]"
@copy="onCopy(sItem, key)" @change="onSelectChange(key, $event)" />
</div>
</div>
<div class="p-l-20">
<div v-show="isShowOther(formFields[key])">
<div class="form-title">其他</div>
<div class="flex">
<HandleFormItem :item="getOtherItem(sItem)" v-model="formFields[sItem.otherCode]"
@copy="onCopy(sItem, key)" />
</div>
</div>
</div>
</div>
</div>
</template>
<template v-else-if="item.type === 'cellItem'">
<div class="form-item ">
<div class="form-title fs-16" v-if="item.label">{{ item.label }}</div>
<div class="grid-container gap2">
<div v-for="(sItem, key) in item.config" class="c-Item" :class="getSpanClass(sItem)"
:key="key">
<div class="form-title" v-if="sItem.label">{{ sItem.label }}</div>
<div v-if="sItem.type === 'dateTime'">
<HandleFormItem type="dateTime" :item="sItem" v-model="formFields[key]"
@copy="onCopy(sItem, key)" />
</div>
<div v-else-if="sItem.type === 'select'">
<HandleFormItem type="select" :item="sItem" v-model="formFields[key]"
@copy="onCopy(sItem, key)" @change="onSelectChange(key, $event)" />
</div>
<div v-else-if="sItem.type === 'input'">
<HandleFormItem :item="sItem" v-model="formFields[key]"
@copy="onCopy(sItem, key)" />
</div>
</div>
</div>
</div>
</template>
<template v-else-if="item.type === 'step'">
<div class="grid-container gap2">
<div v-for="(sItem, key) in item.config" class="c-Item flex item-center" :class="getSpanClass(sItem)"
:key="key">
<div class="step-form-title" v-if="sItem.label">{{ sItem.label }}</div>
<div v-if="sItem.type === 'dateTime'">
<HandleFormItem type="dateTime" :item="sItem" v-model="formFields[key]"
@copy="onCopy(sItem, key)" />
</div>
<div v-else-if="sItem.type === 'select'" class="flex flex1">
<HandleFormItem type="select" :item="sItem" style="width: auto;flex:1" v-model="formFields[key]"
@copy="onCopy(sItem, key)" @change="onSelectChange(key, $event)" />
<div v-show="isShowOther(formFields[key])" class="flex flex1">
<div class="other-title">其他</div>
<div class="flex">
<HandleFormItem class="sub-select" :item="getOtherItem(sItem)" v-model="formFields[sItem.otherCode]"
@copy="onCopy(sItem, key)" />
</div>
</div>
</div>
<div v-else-if="sItem.type === 'input'" class="flex flex1">
<HandleFormItem class="flex1" :item="sItem" v-model="formFields[key]"
@copy="onCopy(sItem, key)" />
<HandleFormItem v-if="sItem.subType === 'select'" type="select" class="sub-select" :item="getSubItem(sItem)" v-model="formFields[sItem.subKey]"
@copy="onCopy(sItem, key)" @change="onSelectChange(sItem.subKey, $event)" />
<div v-else-if="sItem.subType === 'span'">{{ formFields[sItem.subKey] }}</div>
<div class="clickable" v-else-if = "sItem.subType ==='clickable'" @click="handleClickable(sItem)">{{ formFields[sItem.subKey] }}</div>
</div>
</div>
</div>
</template>
</div>
</div>
</template>
<script>
import HandleFormItem from "./HandleFormItem.vue"
export default {
components: {
HandleFormItem
},
props: {
formConfig: {
type: Array,
value: () => [],
},
formData: {
type: Object,
value: () => ({})
}
},
data() {
return {
formFields: {},//
allFieldsConfig: {},//config,
};
},
watch: {
formData: {
immediate: true,
deep: true, // 便
handler(v) {
this.handleFormField();
}
}
},
mounted() {
this.handleFormField();
},
methods: {
handleClickable(sItem){
console.log(sItem)
this.$emit("clickable",sItem)
},
//span
getSpanClass(sItem){
const spanArr = ["full-row","","three-row"]
if(sItem.span){
return spanArr[sItem.span-1]
}
return ""
},
//
getOtherItem(sItem){
return {
label:"其他",
fillType: sItem.fillType,
}
},
getSubItem(sItem){
return {
label: sItem.label,
options: sItem.subOptions || [],
fillType: sItem.subFillType || sItem.fillType,
}
},
isShowOther(v = []) {
// v
const arr = Array.isArray(v) ? v : [v];
return arr.map(val => String(val)).includes("-1");
},
// formConfigform
handleFormField() {
const result = {};
let config = {};
const { formConfig, formData, formFields } = this;
//
formConfig.forEach((item) => {
if (item.config) {
//
config = { ...config, ...item.config }
//
Object.keys(item.config).forEach(key => {
const currentConfig = item.config[key];
let value = formData[key];
//
// if (currentConfig.type === 'select' && currentConfig.multiple) {
// if (!Array.isArray(value)) {
// //
// value = value ? [value] : [];
// }
// }
// // null/undefined
// if (value === null || value === undefined) {
// //
// if (currentConfig.type === 'select' && currentConfig.multiple) {
// value = [];
// } else {
// value = '';
// }
// }
// formFields
if (formFields[key] !== null &&
formFields[key] !== undefined &&
formFields[key] !== ''&&
typeof formFields[key] !== 'object'
) {
// 使formData
result[key] = formFields[key];
} else {
// 使formData
result[key] = value;
}
// - ""
if (currentConfig.otherCode) {
const { otherCode } = currentConfig;
result[otherCode] = formData[otherCode] || '';
config[otherCode] = { label: "其他", type: "input" }
}
if (currentConfig.subKey) {
const { subKey } = currentConfig;
result[subKey] = formData[subKey] || '';
config[subKey] = { label: currentConfig.label, type: currentConfig.subType }
}
});
// otherCode
if (item.config?.otherCode) {
config[item.config?.otherCode] = item.config?.otherCode;
}
}
});
console.log(result,"initResult")
//
this.formFields = result;
this.allFieldsConfig = config;
},
getFormData() {
const { formFields, allFieldsConfig } = this;
const { templateStatus } = this.$store.state.template;
return new Promise((resolve,reject)=>{
for (const key in formFields) {
console.log(key,formFields[key])
if (!formFields[key]) {
const o = allFieldsConfig[key];
console.log(templateStatus,o.fillType,"fill")
if (o.fillType == templateStatus) {
let prefix = "";
if (o.type === "input") {
prefix = "填写"
} else {
prefix = "选择"
}
this.$message.error(`${o.label}还未${prefix}${prefix}后再提交`);
reject(`${o.label}还未${prefix}`);
return;
}
}
}
resolve(formFields)
})
},
onInput(key, val) {
this.formFields[key] = val;
this.$emit("input", { key, value: val });
},
onSelectChange(key, val) {
//
const currentConfig = this.allFieldsConfig[key];
// //
// if (currentConfig && currentConfig.multiple) {
// //
// this.formFields[key] = Array.isArray(val) ? val : (val ? [val] : []);
// } else {
//
this.formFields[key] = val;
// }
this.$emit("select", { key, value: val });
},
//
onCopy(config, key) {
const { formFields } = this;
if (config.copyFrom) {
formFields[key] = formFields[config.copyFrom]
}
},
},
}
</script>
<style lang="scss">
.grid-container {
display: grid;
grid-template-columns: repeat(2, 1fr);
/* 默认2列 */
gap: 0 24px;
}
.gap2{
gap:0 64px;
}
.w-100 {
width: 100%;
}
.form-item {
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1);
margin-top: 24px;
padding: 24px;
border-radius: 5px 5px;
}
/* 或者使用 span 语法 */
.full-row {
grid-column: span 2;
}
.three-row {
grid-column: span 3;
}
.c-Item {
&:not(:last-child) {
margin-bottom: 16px;
}
}
.eo{
&:nth-child(even) {
padding-left: 20px;
}
&:nth-child(odd) {
padding-right: 20px;
}
}
.form-title {
margin-bottom: 12px;
font-size: 14px;
font-weight: normal;
color: #606266;
}
.step-form-title{
font-size: 14px;
font-weight: normal;
color: #606266;
width: 150px;
text-align: right;
padding-right: 10px;
}
.p-r-20{
padding-right: 20px;
}
.p-l-20{
padding-left: 20px;
}
.fs-16 {
font-size: 0.96rem;
font-weight: bold;
color: #464647
}
.flex1 {
flex: 1;
}
.flex {
display: flex;
}
.other-title{
width: 50px;
text-align: right;
margin: 0 10px;
}
.mr-24 {
margin-right: 24px;
}
.sub-select{
width: auto;
margin-left: 10px;
}
</style>

+ 163
- 0
src/components/Template/CustomTable.vue View File

@ -0,0 +1,163 @@
<template>
<div class="custom-table-wrapper">
<div class="custom-table-header">
<div class="custom-table-row">
<div
v-for="(col, index) in columns"
:key="index"
class="custom-table-cell header-cell"
:style="{ width: col.width ? col.width + 'px' : 'auto' }"
>
<div>{{ col.label }}</div>
<template v-if="col.showSelect&& col.options">
<select v-model="col.selected" @change="onHeaderSelectChange(index, $event)">
<option value="" disabled>{{ col.placeholder || '请选择' }}</option>
<option
v-for="opt in col.options"
:key="opt.value"
:value="opt.value"
>
{{ opt.label }}
</option>
</select>
</template>
</div>
</div>
</div>
<div class="custom-table-body">
<div
v-for="(row, rowIndex) in dataSource"
:key="rowIndex"
class="custometable-row"
>
<div
v-for="(col, colIndex) in columns"
:key="colIndex"
class="custom-table-cell body-cell"
:style="{ width: col.width ? col.width + 'px' : 'auto' }"
>
{{ row[col.prop] }}
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'CustomTable',
props: {
columns: {
type: Array,
required: true,
//
// [
// { label: '', prop: 'name' },
// { label: '', prop: 'status', type: 'select', options: [{value:1,label:''},...], selected: null }
// ]
},
dataSource: {
type: Array,
required: true
}
},
methods: {
onHeaderSelectChange(colIndex, event) {
this.$emit('header-select-change', colIndex, event.target.value);
}
}
};
</script>
<style scoped>
.custom-table-wrapper {
border: 1px solid #ebeef5;
border-radius: 4px;
overflow: hidden;
font-size: 14px;
color: #606266;
}
/* 表头 */
.custom-table-header {
background-color: #f5f7fa;
border-bottom: 1px solid #ebeef5;
white-space: nowrap;
display: block;
}
.custom-table-body {
max-height: 300px; /* 可根据需要调整或由父组件控制 */
}
/* 共同行样式 */
.custom-table-row {
display: table;
width: 100%;
table-layout: fixed;
}
.custometable-row {
display: table;
width: 100%;
table-layout: fixed;
}
/* 单元格 */
.custom-table-cell {
display: table-cell;
padding: 12px 10px;
text-align: left;
vertical-align: middle;
border-right: 1px solid #ebeef5;
box-sizing: border-box;
}
.custom-table-cell:last-child {
border-right: none;
}
.header-cell {
font-weight: bold;
color: #909399;
background-color: #f5f7fa;
}
.body-cell {
color: #606266;
background-color: #fff;
}
/* select 样式(模仿 Element UI) */
.header-cell select {
width: 100%;
padding: 4px 8px;
border: 1px solid #dcdfe6;
border-radius: 4px;
outline: none;
background-color: #fff;
font-size: 13px;
color: #606266;
appearance: none; /* 隐藏默认箭头(可选) */
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6,9 12,15 18,9'%3e%3c/polyline%3e%3c/svg%3e");
background-repeat: no-repeat;
background-position: right 8px center;
background-size: 14px;
padding-right: 28px;
}
/* 滚动容器:如果整体宽度超限,显示横向滚动条 */
.custom-table-wrapper {
display: flex;
flex-direction: column;
max-width: 100%; /* 父容器决定宽度 */
overflow-x: auto;
}
.custom-table-header,
.custom-table-body {
min-width: 100%;
}
</style>

+ 200
- 0
src/components/Template/HandleFormItem.vue View File

@ -0,0 +1,200 @@
<template>
<div class="flex w-100">
<div class="flex1 flex">
<el-input v-if="type === 'input'" :maxlength="item.maxlength || 30" :disabled="getDisabled()"
:class="item.fillType | getFillType"
:placeholder="item.placeholder ? item.placeholder : ('请输入' + item.label)"
v-model="inputValue" />
<el-select v-else-if="type === 'select'" class="flex1"
:multiple = "item.multiple"
:class="item.fillType | getFillType"
v-model="inputValue"
:disabled="getDisabled()"
:placeholder="item.placeholder ? item.placeholder : ('请选择' + item.label)"
@change = "onSelectChange"
>
<el-option v-for="op in item.options" :key="op.value" :label="op.label"
:value="op.value">
</el-option>
</el-select>
<el-date-picker
v-else-if="type === 'dateTime'" class="flex1"
:class="item.fillType | getFillType"
v-model="inputValue"
:disabled="getDisabled()"
format="yyyy/MM/DD HH:mm:ss" value-format="yyyy/MM/DD HH:mm:ss"
:placeholder="item.placeholder ? item.placeholder : ('请选择' + item.label)">
</el-date-picker>
</div>
<!-- qc才能操作 -->
<div class="handle-row" v-if="isShowHandle()">
<i class="el-icon-question"></i>
<el-checkbox></el-checkbox>
<span @click="onCopy">复制</span>
<span>稽查轨迹</span>
</div>
</div>
</template>
<script>
export default {
props: {
type: {//form input/select
type:String,
default:"input"
},
item: {
type: Object,
default: () => {
return {
placeholder: "",
maxlength: 30,
label: "",
disabled: false,
}
}
},
// v-model
value: {
type: [String, Number,Array],
default: ''
},
},
data() {
return {
inputValue: this.value
}
},
watch: {
value(newVal) {
console.log(newVal,"value")
this.inputValue = newVal
},
inputValue(newVal) {
if(this.type === "input"){
this.$emit('input', newVal)
}else{
this.$emit('change', newVal)
}
}
},
filters: {
getFillType(type) {
const typeObj = {
actFill: "orange-border",//
green: "green-border",
preFill: "blue-border",//
}
return typeObj[type] || ""
},
},
methods: {
//
isShowHandle(){
const {fillType} = this.item;
const {templateStatus} = this.$store.state.template;
//qc
return (templateStatus === "qc" || templateStatus === "actFill")&&fillType === "actFill";
},
//
getDisabled(){
const {item} = this;
const {fillType} = item;
if(item.hasOwnProperty("disabled")){
return item.disabled
}else{
const {templateStatus} = this.$store.state.template;
if(fillType === "actFill"){//fillTypeactFill
return templateStatus !== "actFill"
}else if(fillType === "preFill"){//fillTypepreFill
return templateStatus !== "preFill"
}else{
return true
}
}
},
onCopy() {
this.$emit("copy")
},
onBlur(val){
this.$emit("blur",val)
},
onSelectChange(val){
this.$emit("change",val)
},
},
}
</script>
<style lang="scss">
.flex {
display: flex;
align-items: center;
}
.flex1 {
flex: 1;
}
.handle-row {
margin-left: 10px;
}
.w-100{
width: 100%;
}
.orange-border {
input {
border-color: #f9c588;
&:focus {
border-color: #f9c588;
}
&:hover {
border-color: #f9c588;
}
&:disabled {
border-color: #f9c588 !important;
}
}
}
.green-border {
input {
border-color: green;
&:focus {
border-color: green;
}
&:hover {
border-color: green;
}
&:disabled {
border-color: green !important;
}
}
}
.blue-border {
input {
border-color: #4ea2ff;
&:focus {
border-color: #4ea2ff;
}
&:hover {
border-color: #4ea2ff;
}
&:disabled {
border-color: #4ea2ff !important;
}
}
}
</style>

+ 258
- 0
src/components/Template/Input.vue View File

@ -0,0 +1,258 @@
<template>
<el-input
v-model="inputValue"
:maxlength="maxLength"
:disabled="disabled"
:readonly="readonly"
:clearable="clearable"
:show-password="showPassword"
:placeholder="placeholder || ('请输入' + label)"
:class="borderClass"
@blur="onBlur"
@focus="onFocus"
@change="onChange"
@clear="onClear"
@keyup.enter="onEnter"
/>
</template>
<script>
export default {
name: 'CustomInput',
props: {
// v-model
value: {
type: [String, Number],
default: ''
},
//
label: {
type: String,
default: ''
},
//
maxLength: {
type: Number,
default: null
},
//
disabled: {
type: Boolean,
default: false
},
//
readonly: {
type: Boolean,
default: false
},
//
clearable: {
type: Boolean,
default: true
},
//
showPassword: {
type: Boolean,
default: false
},
//
placeholder: {
type: String,
default: ''
},
//
fillType: {
type: String,
default: '',
validator: value => {
return ['', 'actFill', 'green', 'preFill'].includes(value)
}
},
//
size: {
type: String,
default: 'default',
validator: value => {
return ['large', 'default', 'small'].includes(value)
}
}
},
data() {
return {
inputValue: this.value
}
},
computed: {
borderClass() {
const typeMap = {
actFill: 'orange-border',
green: 'green-border',
preFill: 'blue-border'
}
return typeMap[this.fillType] || ''
}
},
watch: {
value(newVal) {
this.inputValue = newVal
},
inputValue(newVal) {
this.$emit('input', newVal)
}
},
methods: {
onBlur(event) {
this.$emit('blur', event)
},
onFocus(event) {
this.$emit('focus', event)
},
onChange(value) {
this.$emit('change', value)
},
onClear() {
this.$emit('clear')
},
onEnter(event) {
this.$emit('enter', event)
},
//
clear() {
this.inputValue = ''
this.$emit('input', '')
this.$emit('clear')
},
//
focus() {
this.$el.querySelector('input').focus()
},
//
blur() {
this.$el.querySelector('input').blur()
}
},
filters: {
getFillType(type) {
const typeMap = {
actFill: 'orange-border',
green: 'green-border',
preFill: 'blue-border'
}
return typeMap[type] || ''
}
}
}
</script>
<style lang="scss">
//
:deep(.el-input) {
.el-input__inner {
transition: border-color 0.2s ease, box-shadow 0.2s ease;
&:focus {
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
}
}
}
//
.orange-border {
:deep(.el-input__inner) {
border-color: #f9c588;
&:focus {
border-color: #f9c588;
box-shadow: 0 0 0 2px rgba(249, 197, 136, 0.2);
}
&:hover {
border-color: #f9c588;
}
}
}
// 绿
.green-border {
:deep(.el-input__inner) {
border-color: #67c23a;
&:focus {
border-color: #67c23a;
box-shadow: 0 0 0 2px rgba(103, 194, 58, 0.2);
}
&:hover {
border-color: #67c23a;
}
}
}
//
.blue-border {
:deep(.el-input__inner) {
border-color: #409eff;
&:focus {
border-color: #409eff;
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
}
&:hover {
border-color: #409eff;
}
}
}
//
:deep(.el-input.is-disabled) {
.el-input__inner {
background-color: #f5f7fa;
border-color: #e4e7ed;
color: #c0c4cc;
}
}
//
:deep(.el-input.is-readonly) {
.el-input__inner {
background-color: #fafafa;
cursor: not-allowed;
}
}
//
:deep(.el-input__clear) {
color: #909399;
transition: color 0.2s ease;
&:hover {
color: #409eff;
}
}
//
:deep(.el-input__password) {
color: #909399;
transition: color 0.2s ease;
&:hover {
color: #409eff;
}
}
//
:deep(.el-input--large) {
.el-input__inner {
height: 40px;
font-size: 16px;
}
}
:deep(.el-input--small) {
.el-input__inner {
height: 32px;
font-size: 13px;
}
}
</style>

+ 43
- 0
src/components/Template/LineLabel.vue View File

@ -0,0 +1,43 @@
<template>
<div class="content-title">
<div class="line"></div>
<div class="subtitle"> {{$attrs.label}}</div>
</div>
</template>
<script>
export default {
}
</script>
<style lang="scss" scoped>
.content-title {
width: 100%;
background: #f9f9ff;
font-size: 0.96rem;
font-weight: bold;
padding-left: 10px;
height: 40px;
line-height: 40px;
display: flex;
justify-content: flex-start;
text-align: left;
.line {
width: 2px;
float: left;
height: 16px;
margin-top: 12px;
margin-right: 8px;
border-left: #3178ff 3px solid;
}
.subtitle {
height: 40px;
line-height: 40px;
color: #464647 !important;
}
}
</style>

+ 357
- 0
src/components/Template/Step.vue View File

@ -0,0 +1,357 @@
<template>
<div class="step-container">
<el-button v-if = "isShowAddStep()" type="primary" @click="addStep" icon="el-icon-plus">添加步骤</el-button>
<div class="step-list">
<div
v-for="(step, index) in steps"
:key="step.id"
class="step-item"
>
<div class="step-content">
<span class="step-title">步骤{{ index + 1 }}</span>
<el-select
class = "step-type-select"
v-model="step.type"
@change="onTypeChange(index)"
placeholder="请选择"
>
<el-option
v-for="option in stepTypes"
:key="option.value"
:label="option.label"
:value="option.value"
></el-option>
</el-select>
<!-- 根据步骤类型显示对应的表单 -->
<component :is="getStepComponent(step.type)"
:step-data="step.formData"
@update="onFormUpdate(index, $event)">
</component>
<div class="step-header-item">
<el-button
type="text"
@click="removeStep(index)"
icon="el-icon-delete"
class="delete-btn"
></el-button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import ContainerStep from './StepComponents/ContainerStep.vue'
import CentrifugeStep from './StepComponents/CentrifugeStep.vue'
import AutoWeighStep from './StepComponents/AutoWeighStep.vue'
import ManualWeighStep from './StepComponents/ManualWeighStep.vue'
import AddSolutionStep from './StepComponents/AddSolutionStep.vue'
import MixStep from './StepComponents/MixStep.vue'
import UltrasoundStep from './StepComponents/UltrasoundStep.vue'
import FiltrationStep from './StepComponents/FiltrationStep.vue'
import PackageStep from './StepComponents/PackageStep.vue'
import Seal2Step from './StepComponents/Seal2Step.vue'
import Seal3Step from './StepComponents/Seal3Step.vue'
import VortexStep from './StepComponents/VortexStep.vue'
import CoatStep from './StepComponents/CoatStep.vue'
import StaticStartStep from './StepComponents/StaticStartStep.vue'
import StaticEndStep from './StepComponents/StaticEndStep.vue'
import TakePlateStep from './StepComponents/TakePlateStep.vue'
export default {
name: 'Step',
props: {
value: {
type: Array,
default: () => []
}
},
data() {
return {
stepTypes: [
{ label: '容器选择', value: 'container' },
// { label: '', value: 'centrifuge' },
// { label: '', value: 'autoWeigh' },
// { label: '', value: 'manualWeigh' },
// { label: '', value: 'addSolution' },
// { label: '', value: 'mix' },
// { label: '', value: 'ultrasound' },
// { label: '', value: 'filtration' },
// { label: '', value: 'package' },
// { label: '2', value: 'seal2' },
// { label: '3', value: 'seal3' },
// { label: '', value: 'vortex' },
// { label: '', value: 'coat' },
// { label: '', value: 'staticStart' },
// { label: '', value: 'staticEnd' },
// { label: '', value: 'takePlate' }
],
steps: [],
stepId: 1,
componentMap: null
}
},
computed: {
stepComponentMap() {
if (!this.componentMap) {
this.componentMap = {
'container': 'ContainerStep',
'centrifuge': 'CentrifugeStep',
'autoWeigh': 'AutoWeighStep',
'manualWeigh': 'ManualWeighStep',
'addSolution': 'AddSolutionStep',
'mix': 'MixStep',
'ultrasound': 'UltrasoundStep',
'filtration': 'FiltrationStep',
'package': 'PackageStep',
'seal2': 'Seal2Step',
'seal3': 'Seal3Step',
'vortex': 'VortexStep',
'coat': 'CoatStep',
'staticStart': 'StaticStartStep',
'staticEnd': 'StaticEndStep',
'takePlate': 'TakePlateStep'
}
}
return this.componentMap
}
},
components: {
ContainerStep,
CentrifugeStep,
AutoWeighStep,
ManualWeighStep,
AddSolutionStep,
MixStep,
UltrasoundStep,
FiltrationStep,
PackageStep,
Seal2Step,
Seal3Step,
VortexStep,
CoatStep,
StaticStartStep,
StaticEndStep,
TakePlateStep
},
created() {
//
if (this.value && this.value.length > 0) {
this.steps = this.value.map((step) => ({
id: this.stepId++,
type: step.type || '',
formData: step.formData || {}
}))
} else {
//
this.addStep()
}
},
watch: {
steps: {
handler(newVal) {
this.$emit('input', newVal.map(step => ({
type: step.type,
formData: step.formData
})))
},
deep: true
}
},
methods: {
isShowAddStep() {
const {templateStatus} = this.$store.state.template
return templateStatus === 'preFill';
},
addStep() {
try {
this.steps.push({
id: this.stepId++,
type: '',
formData: {}
})
this.$emit('step-added', this.steps.length)
} catch (error) {
console.error('添加步骤失败:', error)
this.$message.error('添加步骤失败,请重试')
}
},
removeStep(index) {
if (this.steps.length > 1) {
const removedStep = this.steps.splice(index, 1)[0]
this.$emit('step-removed', { index, step: removedStep, remaining: this.steps.length })
} else {
this.$message.warning('至少需要保留一个步骤')
}
},
onTypeChange(index) {
//
const oldType = this.steps[index].type
this.$set(this.steps[index], 'formData', {})
//
this.$emit('step-type-changed', {
index,
newType: this.steps[index].type,
oldType
})
},
onFormUpdate(stepIndex, formData) {
this.steps[stepIndex].formData = formData
},
getStepComponent(type) {
// 使
return this.stepComponentMap[type]
},
//
getFormData() {
return new Promise((resolve, reject) => {
const stepData = this.steps.map(step => ({
type: step.type,
...step.formData
}))
resolve(stepData)
})
},
//
setStepData(data) {
if (Array.isArray(data)) {
this.steps = data.map(step => ({
id: this.stepId++,
type: step.type || '',
formData: step.formData || {}
}))
}
},
//
resetSteps() {
this.steps = [{
id: this.stepId++,
type: '',
formData: {}
}]
this.$emit('steps-reset')
},
//
getStepDataByIndex(index) {
if (index >= 0 && index < this.steps.length) {
return {
type: this.steps[index].type,
formData: this.steps[index].formData
}
}
return null
},
//
validateSteps() {
const errors = []
this.steps.forEach((step, index) => {
if (!step.type) {
errors.push(`步骤 ${index + 1}: 请选择步骤类型`)
}
//
})
return {
isValid: errors.length === 0,
errors
}
},
//
importSteps(stepDataArray) {
if (Array.isArray(stepDataArray)) {
this.steps = stepDataArray.map((step, index) => ({
id: this.stepId++,
type: step.type || '',
formData: step.formData || {}
}))
this.$emit('steps-imported', this.steps.length)
}
},
//
getStepStatistics() {
const stats = {
total: this.steps.length,
byType: {},
filled: 0
}
this.steps.forEach(step => {
//
if (step.type) {
stats.byType[step.type] = (stats.byType[step.type] || 0) + 1
}
//
if (step.type && Object.keys(step.formData).length > 0) {
stats.filled++
}
})
return stats
}
},
}
</script>
<style lang="scss" scoped>
.step-container {
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
margin-top: 24px;
padding: 24px;
border-radius: 5px 5px;
.step-header {
margin-bottom: 20px;
padding: 15px;
background: #f5f7fa;
border-radius: 6px;
}
.step-list {
.step-item {
margin-bottom: 20px;
border-radius: 6px;
overflow: hidden;
.step-title {
margin-right: 10px;
}
.step-type-select{
width: 200px;
margin-right: 10px;
}
.delete-btn {
color: #f56c6c;
&:hover {
color: #f78989;
}
&:disabled {
color: #c0c4cc;
}
}
.step-content {
padding: 20px;
display: flex;
align-items: center;
}
}
}
}
</style>

+ 66
- 0
src/components/Template/StepComponents/AddSolutionStep.vue View File

@ -0,0 +1,66 @@
<template>
<div class="add-solution-step">
<el-form label-width="120px">
<el-form-item label="溶液名称">
<el-input v-model="formData.solutionName" placeholder="请输入溶液名称"></el-input>
</el-form-item>
<el-form-item label="加入体积">
<div style="display: flex; gap: 10px;">
<el-input v-model="formData.volume" placeholder="体积"></el-input>
<el-select v-model="formData.unit" style="width: 80px;">
<el-option label="μl" value="μl"></el-option>
<el-option label="ml" value="ml"></el-option>
<el-option label="l" value="l"></el-option>
</el-select>
</div>
</el-form-item>
<el-form-item label="浓度">
<el-input v-model="formData.concentration" placeholder="请输入浓度"></el-input>
</el-form-item>
<el-form-item label="温度(℃)">
<el-input v-model="formData.temperature" placeholder="请输入温度"></el-input>
</el-form-item>
<el-form-item label="加入速度">
<el-select v-model="formData.additionSpeed" placeholder="请选择加入速度">
<el-option label="慢速" value="slow"></el-option>
<el-option label="中速" value="medium"></el-option>
<el-option label="快速" value="fast"></el-option>
</el-select>
</el-form-item>
<el-form-item label="备注">
<el-input v-model="formData.notes" type="textarea" :rows="2"></el-input>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: 'AddSolutionStep',
props: ['stepData'],
data() {
return {
formData: {
solutionName: '',
volume: '',
unit: 'ml',
concentration: '',
temperature: '',
additionSpeed: '',
notes: ''
}
}
},
created() {
this.formData = { ...this.formData, ...this.stepData }
},
watch: {
formData: {
handler(newVal) {
this.$emit('update', newVal)
},
deep: true
}
}
}
</script>

+ 89
- 0
src/components/Template/StepComponents/AutoWeighStep.vue View File

@ -0,0 +1,89 @@
<!-- 自动称重步骤表单 -->
<template>
<div class="flex">
<div class="form-label">选择</div>
<HandleFormItem
class = "w-120"
:item="fieldConfigs.notes"
v-model="formData.notes"
/>
</div>
</template>
<script>
import HandleFormItem from '../HandleFormItem.vue'
import Input from '../Input.vue'
export default {
name: 'AutoWeighStep',
components: {
HandleFormItem,
Input
},
props: ['stepData'],
data() {
return {
formData: {
targetWeight: '123',
tolerance: '',
sampleName: '',
containerWeight: '',
notes: ''
},
//
fieldConfigs: {
targetWeight: {
label: '目标重量',
placeholder: '请输入目标重量',
maxlength: 50
},
tolerance: {
label: '允许误差',
placeholder: '请输入允许误差',
maxlength: 20
},
sampleName: {
label: '样品名称',
placeholder: '请输入样品名称',
maxlength: 100
},
containerWeight: {
label: '容器重量',
placeholder: '请输入容器重量',
maxlength: 20
},
notes: {
label: '备注',
placeholder: '请输入备注信息',
maxlength: 500
}
}
}
},
created() {
//
if (this.stepData) {
this.formData = { ...this.formData, ...this.stepData }
}
},
watch: {
formData: {
handler(newVal) {
this.$emit('update', newVal)
},
deep: true
}
},
methods: {
onFieldChange(field, value) {
this.formData[field] = value
},
}
}
</script>
<style lang="scss" scoped>
.w-120{
width: 120px;
}
</style>

+ 62
- 0
src/components/Template/StepComponents/CentrifugeStep.vue View File

@ -0,0 +1,62 @@
<template>
<div class="centrifuge-step">
<el-form label-width="120px">
<el-form-item label="转速(rpm)">
<el-input v-model="formData.speed" placeholder="请输入转速"></el-input>
</el-form-item>
<el-form-item label="时间(min)">
<el-input v-model="formData.time" placeholder="请输入时间"></el-input>
</el-form-item>
<el-form-item label="温度(℃)">
<el-input v-model="formData.temperature" placeholder="请输入温度"></el-input>
</el-form-item>
<el-form-item label="加速度">
<el-select v-model="formData.acceleration" placeholder="请选择加速度">
<el-option label="慢" value="slow"></el-option>
<el-option label="中" value="medium"></el-option>
<el-option label="快" value="fast"></el-option>
</el-select>
</el-form-item>
<el-form-item label="减速度">
<el-select v-model="formData.deceleration" placeholder="请选择减速度">
<el-option label="慢" value="slow"></el-option>
<el-option label="中" value="medium"></el-option>
<el-option label="快" value="fast"></el-option>
</el-select>
</el-form-item>
<el-form-item label="备注">
<el-input v-model="formData.notes" type="textarea" :rows="2"></el-input>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: 'CentrifugeStep',
props: ['stepData'],
data() {
return {
formData: {
speed: '',
time: '',
temperature: '',
acceleration: '',
deceleration: '',
notes: ''
}
}
},
created() {
this.formData = { ...this.formData, ...this.stepData }
},
watch: {
formData: {
handler(newVal) {
this.$emit('update', newVal)
},
deep: true
}
}
}
</script>

+ 152
- 0
src/components/Template/StepComponents/CoatStep.vue View File

@ -0,0 +1,152 @@
<template>
<div class="coat-step">
<el-form label-width="120px">
<el-form-item label="包被物质">
<el-input v-model="formData.coatMaterial" placeholder="请输入包被物质名称"></el-input>
</el-form-item>
<el-form-item label="包被浓度">
<el-input v-model="formData.concentration" placeholder="请输入浓度值">
<el-select v-model="formData.concentrationUnit" slot="append" style="width: 80px;">
<el-option label="mg/ml" value="mg/ml"></el-option>
<el-option label="μg/ml" value="μg/ml"></el-option>
<el-option label="ng/ml" value="ng/ml"></el-option>
<el-option label="%" value="%"></el-option>
</el-select>
</el-input>
</el-form-item>
<el-form-item label="包被体积">
<el-input v-model="formData.volume" placeholder="请输入体积">
<el-select v-model="formData.unit" slot="append" style="width: 80px;">
<el-option label="ml" value="ml"></el-option>
<el-option label="μl" value="μl"></el-option>
<el-option label="l" value="l"></el-option>
</el-select>
</el-input>
</el-form-item>
<el-form-item label="包被时间">
<el-input v-model="formData.time" placeholder="请输入时间">
<el-select v-model="formData.timeUnit" slot="append" style="width: 80px;">
<el-option label="min" value="min"></el-option>
<el-option label="h" value="h"></el-option>
<el-option label="d" value="d"></el-option>
</el-select>
</el-input>
</el-form-item>
<el-form-item label="温度条件">
<el-input v-model="formData.temperature" placeholder="请输入温度">
<el-select v-model="formData.temperatureUnit" slot="append" style="width: 60px;">
<el-option label="℃" value="℃"></el-option>
<el-option label="℉" value="℉"></el-option>
</el-select>
</el-input>
</el-form-item>
<el-form-item label="pH值">
<el-input-number
v-model="formData.pH"
:min="1"
:max="14"
:step="0.1"
:precision="1"
placeholder="请输入pH值"
></el-input-number>
</el-form-item>
<el-form-item label="容器类型">
<el-select v-model="formData.containerType" placeholder="请选择容器类型">
<el-option label="微孔板" value="microplate"></el-option>
<el-option label="培养皿" value="petriDish"></el-option>
<el-option label="载玻片" value="slide"></el-option>
<el-option label="试管" value="testTube"></el-option>
</el-select>
</el-form-item>
<el-form-item label="包被方法">
<el-radio-group v-model="formData.method">
<el-radio label="passive">被动吸附</el-radio>
<el-radio label="active">共价结合</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="洗涤步骤">
<el-checkbox-group v-model="formData.washSteps">
<el-checkbox label="包被前洗涤">包被前洗涤</el-checkbox>
<el-checkbox label="包被后洗涤">包被后洗涤</el-checkbox>
<el-checkbox label="封闭前洗涤">封闭前洗涤</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="封闭处理">
<el-switch v-model="formData.blocking"></el-switch>
</el-form-item>
<el-form-item v-if="formData.blocking" label="封闭剂">
<el-input v-model="formData.blockingAgent" placeholder="请输入封闭剂名称"></el-input>
</el-form-item>
<el-form-item v-if="formData.blocking" label="封闭时间">
<el-input v-model="formData.blockingTime" placeholder="请输入封闭时间">
<el-select v-model="formData.blockingTimeUnit" slot="append" style="width: 80px;">
<el-option label="min" value="min"></el-option>
<el-option label="h" value="h"></el-option>
</el-select>
</el-input>
</el-form-item>
<el-form-item label="备注">
<el-input
v-model="formData.notes"
type="textarea"
:rows="3"
placeholder="请输入备注信息"
></el-input>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: 'CoatStep',
props: ['stepData'],
data() {
return {
formData: {
coatMaterial: '',
concentration: '',
concentrationUnit: 'mg/ml',
volume: '',
unit: 'ml',
time: '',
timeUnit: 'h',
temperature: '',
temperatureUnit: '℃',
pH: '',
containerType: '',
method: 'passive',
washSteps: [],
blocking: false,
blockingAgent: '',
blockingTime: '',
blockingTimeUnit: 'min',
notes: ''
}
}
},
created() {
this.formData = { ...this.formData, ...this.stepData }
},
watch: {
formData: {
handler(newVal) {
this.$emit('update', newVal)
},
deep: true
}
}
}
</script>

+ 71
- 0
src/components/Template/StepComponents/ContainerStep.vue View File

@ -0,0 +1,71 @@
<!-- 容器步骤表单 -->
<template>
<div class="flex items-center">
<div>容器选择</div>
<HandleFormItem class="w-auto" type="select" :item="fieldConfigs.containerType"
@change="onContainerTypeChange"
v-model="formData.containerType" />
</div>
</template>
<script>
import HandleFormItem from '../HandleFormItem.vue'
export default {
name: 'ContainerStep',
components: {
HandleFormItem
},
props: ['stepData'],
data() {
return {
formData: {
containerType: '',
},
//
fieldConfigs: {
containerType: {
label: '容器选择',
placeholder: '请选择',
fillType:"actFill",
options: [
{ label: '离心管', value: 'centrifugeTube' },
{ label: '试管', value: 'testTube' },
{ label: '烧杯', value: 'beaker' },
{ label: '容量瓶', value: 'volumetricFlask' },
{ label: '培养皿', value: 'petriDish' }
]
}
}
}
},
created() {
this.formData = { ...this.formData, ...this.stepData }
},
watch: {
formData: {
handler(newVal) {
console.log(newVal,"newVal")
this.$emit('update', newVal)
},
deep: true
}
},
methods: {
onContainerTypeChange(val) {
this.formData.containerType = val
}
}
}
</script>
<style lang="scss" scoped>
.flex {
display: flex;
}
.items-center {
align-items: center;
}
.w-auto {
width: auto;
}
</style>

+ 84
- 0
src/components/Template/StepComponents/FiltrationStep.vue View File

@ -0,0 +1,84 @@
<template>
<div class="filtration-step">
<el-form label-width="120px">
<el-form-item label="滤膜类型">
<el-select v-model="formData.filterType" placeholder="请选择滤膜类型">
<el-option label="0.22μm" value="0.22"></el-option>
<el-option label="0.45μm" value="0.45"></el-option>
<el-option label="0.8μm" value="0.8"></el-option>
<el-option label="1.2μm" value="1.2"></el-option>
</el-select>
</el-form-item>
<el-form-item label="压力">
<el-input v-model="formData.pressure" placeholder="请输入压力值">
<el-select v-model="formData.pressureUnit" slot="append" style="width: 80px;">
<el-option label="kPa" value="kPa"></el-option>
<el-option label="bar" value="bar"></el-option>
<el-option label="psi" value="psi"></el-option>
</el-select>
</el-input>
</el-form-item>
<el-form-item label="过滤时间">
<el-input v-model="formData.time" placeholder="请输入时间">
<el-select v-model="formData.timeUnit" slot="append" style="width: 80px;">
<el-option label="min" value="min"></el-option>
<el-option label="s" value="s"></el-option>
<el-option label="h" value="h"></el-option>
</el-select>
</el-input>
</el-form-item>
<el-form-item label="温度">
<el-input v-model="formData.temperature" placeholder="请输入温度">
<el-select v-model="formData.temperatureUnit" slot="append" style="width: 60px;">
<el-option label="℃" value="℃"></el-option>
<el-option label="℉" value="℉"></el-option>
</el-select>
</el-input>
</el-form-item>
<el-form-item label="备注">
<el-input
v-model="formData.notes"
type="textarea"
:rows="3"
placeholder="请输入备注信息"
></el-input>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: 'FiltrationStep',
props: ['stepData'],
data() {
return {
formData: {
filterType: '',
pressure: '',
pressureUnit: 'kPa',
time: '',
timeUnit: 'min',
temperature: '',
temperatureUnit: '℃',
notes: ''
}
}
},
created() {
this.formData = { ...this.formData, ...this.stepData }
},
watch: {
formData: {
handler(newVal) {
this.$emit('update', newVal)
},
deep: true
}
}
}
</script>

+ 50
- 0
src/components/Template/StepComponents/ManualWeighStep.vue View File

@ -0,0 +1,50 @@
<template>
<div class="manual-weigh-step">
<el-form label-width="120px">
<el-form-item label="实际重量(g)">
<el-input v-model="formData.actualWeight" placeholder="请输入实际重量"></el-input>
</el-form-item>
<el-form-item label="目标重量(g)">
<el-input v-model="formData.targetWeight" placeholder="请输入目标重量"></el-input>
</el-form-item>
<el-form-item label="差值(g)">
<el-input v-model="formData.difference" placeholder="自动计算"></el-input>
</el-form-item>
<el-form-item label="样品名称">
<el-input v-model="formData.sampleName" placeholder="请输入样品名称"></el-input>
</el-form-item>
<el-form-item label="备注">
<el-input v-model="formData.notes" type="textarea" :rows="2"></el-input>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: 'ManualWeighStep',
props: ['stepData'],
data() {
return {
formData: {
actualWeight: '',
targetWeight: '',
difference: '',
sampleName: '',
notes: ''
}
}
},
created() {
this.formData = { ...this.formData, ...this.stepData }
},
watch: {
formData: {
handler(newVal) {
this.$emit('update', newVal)
},
deep: true
}
}
}
</script>

+ 59
- 0
src/components/Template/StepComponents/MixStep.vue View File

@ -0,0 +1,59 @@
<template>
<div class="mix-step">
<el-form label-width="120px">
<el-form-item label="混合方式">
<el-select v-model="formData.method" placeholder="请选择混合方式">
<el-option label="涡旋混匀" value="vortex"></el-option>
<el-option label="颠倒混匀" value="invert"></el-option>
<el-option label="移液器吹打" value="pipette"></el-option>
<el-option label="磁力搅拌" value="magnetic"></el-option>
</el-select>
</el-form-item>
<el-form-item label="混合时间(s)">
<el-input v-model="formData.time" placeholder="请输入混合时间"></el-input>
</el-form-item>
<el-form-item label="混合速度">
<el-select v-model="formData.speed" placeholder="请选择混合速度">
<el-option label="低速" value="low"></el-option>
<el-option label="中速" value="medium"></el-option>
<el-option label="高速" value="high"></el-option>
</el-select>
</el-form-item>
<el-form-item label="温度(℃)">
<el-input v-model="formData.temperature" placeholder="请输入温度"></el-input>
</el-form-item>
<el-form-item label="备注">
<el-input v-model="formData.notes" type="textarea" :rows="2"></el-input>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: 'MixStep',
props: ['stepData'],
data() {
return {
formData: {
method: '',
time: '',
speed: '',
temperature: '',
notes: ''
}
}
},
created() {
this.formData = { ...this.formData, ...this.stepData }
},
watch: {
formData: {
handler(newVal) {
this.$emit('update', newVal)
},
deep: true
}
}
}
</script>

+ 90
- 0
src/components/Template/StepComponents/PackageStep.vue View File

@ -0,0 +1,90 @@
<template>
<div class="package-step">
<el-form label-width="120px">
<el-form-item label="分装容器">
<el-select v-model="formData.containerType" placeholder="请选择分装容器">
<el-option label="离心管" value="centrifugeTube"></el-option>
<el-option label="试管" value="testTube"></el-option>
<el-option label="样品瓶" value="vial"></el-option>
<el-option label="微孔板" value="microplate"></el-option>
</el-select>
</el-form-item>
<el-form-item label="分装数量">
<el-input-number
v-model="formData.quantity"
:min="1"
:max="100"
:step="1"
></el-input-number>
</el-form-item>
<el-form-item label="每份体积">
<el-input v-model="formData.volume" placeholder="请输入体积">
<el-select v-model="formData.unit" slot="append" style="width: 80px;">
<el-option label="ml" value="ml"></el-option>
<el-option label="μl" value="μl"></el-option>
<el-option label="l" value="l"></el-option>
</el-select>
</el-input>
</el-form-item>
<el-form-item label="分装方式">
<el-radio-group v-model="formData.method">
<el-radio label="manual">手动分装</el-radio>
<el-radio label="auto">自动分装</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="温度要求">
<el-input v-model="formData.temperature" placeholder="请输入温度">
<el-select v-model="formData.temperatureUnit" slot="append" style="width: 60px;">
<el-option label="℃" value="℃"></el-option>
<el-option label="℉" value="℉"></el-option>
</el-select>
</el-input>
</el-form-item>
<el-form-item label="备注">
<el-input
v-model="formData.notes"
type="textarea"
:rows="3"
placeholder="请输入备注信息"
></el-input>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: 'PackageStep',
props: ['stepData'],
data() {
return {
formData: {
containerType: '',
quantity: 1,
volume: '',
unit: 'ml',
method: 'manual',
temperature: '',
temperatureUnit: '℃',
notes: ''
}
}
},
created() {
this.formData = { ...this.formData, ...this.stepData }
},
watch: {
formData: {
handler(newVal) {
this.$emit('update', newVal)
},
deep: true
}
}
}
</script>

+ 113
- 0
src/components/Template/StepComponents/Seal2Step.vue View File

@ -0,0 +1,113 @@
<template>
<div class="seal2-step">
<el-form label-width="120px">
<el-form-item label="封装方式">
<el-select v-model="formData.method" placeholder="请选择封装方式">
<el-option label="热封" value="heat"></el-option>
<el-option label="机械封" value="mechanical"></el-option>
<el-option label="封膜" value="membrane"></el-option>
<el-option label="盖子封" value="cap"></el-option>
</el-select>
</el-form-item>
<el-form-item label="密封材料">
<el-select v-model="formData.material" placeholder="请选择密封材料">
<el-option label="铝箔" value="aluminum"></el-option>
<el-option label="塑料膜" value="plastic"></el-option>
<el-option label="橡胶塞" value="rubber"></el-option>
<el-option label="螺旋盖" value="screwCap"></el-option>
</el-select>
</el-form-item>
<el-form-item label="压力参数">
<el-input v-model="formData.pressure" placeholder="请输入压力值">
<el-select v-model="formData.pressureUnit" slot="append" style="width: 80px;">
<el-option label="kPa" value="kPa"></el-option>
<el-option label="bar" value="bar"></el-option>
<el-option label="psi" value="psi"></el-option>
</el-select>
</el-input>
</el-form-item>
<el-form-item label="温度参数">
<el-input v-model="formData.temperature" placeholder="请输入温度">
<el-select v-model="formData.temperatureUnit" slot="append" style="width: 60px;">
<el-option label="℃" value="℃"></el-option>
<el-option label="℉" value="℉"></el-option>
</el-select>
</el-input>
</el-form-item>
<el-form-item label="时间参数">
<el-input v-model="formData.time" placeholder="请输入时间">
<el-select v-model="formData.timeUnit" slot="append" style="width: 80px;">
<el-option label="s" value="s"></el-option>
<el-option label="min" value="min"></el-option>
<el-option label="h" value="h"></el-option>
</el-select>
</el-input>
</el-form-item>
<el-form-item label="封管数量">
<el-input-number
v-model="formData.tubeCount"
:min="2"
:max="2"
:step="1"
></el-input-number>
</el-form-item>
<el-form-item label="质量检查">
<el-checkbox-group v-model="formData.qualityChecks">
<el-checkbox label="密封性检查">密封性检查</el-checkbox>
<el-checkbox label="外观检查">外观检查</el-checkbox>
<el-checkbox label="标识检查">标识检查</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="备注">
<el-input
v-model="formData.notes"
type="textarea"
:rows="3"
placeholder="请输入备注信息"
></el-input>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: 'Seal2Step',
props: ['stepData'],
data() {
return {
formData: {
method: '',
material: '',
pressure: '',
pressureUnit: 'kPa',
temperature: '',
temperatureUnit: '℃',
time: '',
timeUnit: 's',
tubeCount: 2,
qualityChecks: [],
notes: ''
}
}
},
created() {
this.formData = { ...this.formData, ...this.stepData }
},
watch: {
formData: {
handler(newVal) {
this.$emit('update', newVal)
},
deep: true
}
}
}
</script>

+ 114
- 0
src/components/Template/StepComponents/Seal3Step.vue View File

@ -0,0 +1,114 @@
<template>
<div class="seal3-step">
<el-form label-width="120px">
<el-form-item label="封装方式">
<el-select v-model="formData.method" placeholder="请选择封装方式">
<el-option label="热封" value="heat"></el-option>
<el-option label="机械封" value="mechanical"></el-option>
<el-option label="封膜" value="membrane"></el-option>
<el-option label="盖子封" value="cap"></el-option>
</el-select>
</el-form-item>
<el-form-item label="密封材料">
<el-select v-model="formData.material" placeholder="请选择密封材料">
<el-option label="铝箔" value="aluminum"></el-option>
<el-option label="塑料膜" value="plastic"></el-option>
<el-option label="橡胶塞" value="rubber"></el-option>
<el-option label="螺旋盖" value="screwCap"></el-option>
</el-select>
</el-form-item>
<el-form-item label="压力参数">
<el-input v-model="formData.pressure" placeholder="请输入压力值">
<el-select v-model="formData.pressureUnit" slot="append" style="width: 80px;">
<el-option label="kPa" value="kPa"></el-option>
<el-option label="bar" value="bar"></el-option>
<el-option label="psi" value="psi"></el-option>
</el-select>
</el-input>
</el-form-item>
<el-form-item label="温度参数">
<el-input v-model="formData.temperature" placeholder="请输入温度">
<el-select v-model="formData.temperatureUnit" slot="append" style="width: 60px;">
<el-option label="℃" value="℃"></el-option>
<el-option label="℉" value="℉"></el-option>
</el-select>
</el-input>
</el-form-item>
<el-form-item label="时间参数">
<el-input v-model="formData.time" placeholder="请输入时间">
<el-select v-model="formData.timeUnit" slot="append" style="width: 80px;">
<el-option label="s" value="s"></el-option>
<el-option label="min" value="min"></el-option>
<el-option label="h" value="h"></el-option>
</el-select>
</el-input>
</el-form-item>
<el-form-item label="封管数量">
<el-input-number
v-model="formData.tubeCount"
:min="3"
:max="3"
:step="1"
></el-input-number>
</el-form-item>
<el-form-item label="质量检查">
<el-checkbox-group v-model="formData.qualityChecks">
<el-checkbox label="密封性检查">密封性检查</el-checkbox>
<el-checkbox label="外观检查">外观检查</el-checkbox>
<el-checkbox label="标识检查">标识检查</el-checkbox>
<el-checkbox label="批次检查">批次检查</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="备注">
<el-input
v-model="formData.notes"
type="textarea"
:rows="3"
placeholder="请输入备注信息"
></el-input>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: 'Seal3Step',
props: ['stepData'],
data() {
return {
formData: {
method: '',
material: '',
pressure: '',
pressureUnit: 'kPa',
temperature: '',
temperatureUnit: '℃',
time: '',
timeUnit: 's',
tubeCount: 3,
qualityChecks: [],
notes: ''
}
}
},
created() {
this.formData = { ...this.formData, ...this.stepData }
},
watch: {
formData: {
handler(newVal) {
this.$emit('update', newVal)
},
deep: true
}
}
}
</script>

+ 144
- 0
src/components/Template/StepComponents/StaticEndStep.vue View File

@ -0,0 +1,144 @@
<template>
<div class="static-end-step">
<el-form label-width="120px">
<el-form-item label="实际时间">
<el-input v-model="formData.actualDuration" placeholder="请输入实际静置时间">
<el-select v-model="formData.actualDurationUnit" slot="append" style="width: 80px;">
<el-option label="min" value="min"></el-option>
<el-option label="h" value="h"></el-option>
<el-option label="d" value="d"></el-option>
</el-select>
</el-input>
</el-form-item>
<el-form-item label="结束时间">
<el-date-picker
v-model="formData.endTime"
type="datetime"
placeholder="请选择结束时间"
format="yyyy-MM-dd HH:mm"
value-format="yyyy-MM-dd HH:mm:ss"
></el-date-picker>
</el-form-item>
<el-form-item label="实际温度">
<el-input v-model="formData.actualTemperature" placeholder="请输入实际温度">
<el-select v-model="formData.actualTemperatureUnit" slot="append" style="width: 60px;">
<el-option label="℃" value="℃"></el-option>
<el-option label="℉" value="℉"></el-option>
</el-select>
</el-input>
</el-form-item>
<el-form-item label="样品状态">
<el-select v-model="formData.sampleState" placeholder="请选择样品状态">
<el-option label="正常" value="normal"></el-option>
<el-option label="异常" value="abnormal"></el-option>
<el-option label="有变化" value="changed"></el-option>
<el-option label="无变化" value="unchanged"></el-option>
</el-select>
</el-form-item>
<el-form-item label="观察结果">
<el-checkbox-group v-model="formData.observationResults">
<el-checkbox label="颜色变化">颜色变化</el-checkbox>
<el-checkbox label="沉淀形成">沉淀形成</el-checkbox>
<el-checkbox label="相分离">相分离</el-checkbox>
<el-checkbox label="结晶">结晶</el-checkbox>
<el-checkbox label="无异常">无异常</el-checkbox>
<el-checkbox label="其他">其他</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="容器状态">
<el-select v-model="formData.finalContainerState" placeholder="请选择最终容器状态">
<el-option label="密封" value="sealed"></el-option>
<el-option label="半密封" value="semiSealed"></el-option>
<el-option label="开放" value="open"></el-option>
</el-select>
</el-form-item>
<el-form-item label="后续处理">
<el-checkbox-group v-model="formData.nextSteps">
<el-checkbox label="直接进行下一步">直接进行下一步</el-checkbox>
<el-checkbox label="需要预处理">需要预处理</el-checkbox>
<el-checkbox label="需要检测">需要检测</el-checkbox>
<el-checkbox label="需要储存">需要储存</el-checkbox>
<el-checkbox label="需要转移">需要转移</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="处理结果">
<el-radio-group v-model="formData.processResult">
<el-radio label="success">静置成功</el-radio>
<el-radio label="partial">部分成功</el-radio>
<el-radio label="failed">静置失败</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="formData.processResult !== 'success'" label="失败原因">
<el-input v-model="formData.failureReason" placeholder="请输入失败原因"></el-input>
</el-form-item>
<el-form-item label="质量评估">
<el-rate v-model="formData.qualityScore" show-text></el-rate>
</el-form-item>
<el-form-item label="最终观察">
<el-input
v-model="formData.finalObservation"
type="textarea"
:rows="3"
placeholder="请输入最终观察结果"
></el-input>
</el-form-item>
<el-form-item label="备注">
<el-input
v-model="formData.notes"
type="textarea"
:rows="3"
placeholder="请输入备注信息"
></el-input>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: 'StaticEndStep',
props: ['stepData'],
data() {
return {
formData: {
actualDuration: '',
actualDurationUnit: 'h',
endTime: '',
actualTemperature: '',
actualTemperatureUnit: '℃',
sampleState: '',
observationResults: [],
finalContainerState: '',
nextSteps: [],
processResult: 'success',
failureReason: '',
qualityScore: 5,
finalObservation: '',
notes: ''
}
}
},
created() {
this.formData = { ...this.formData, ...this.stepData }
},
watch: {
formData: {
handler(newVal) {
this.$emit('update', newVal)
},
deep: true
}
}
}
</script>

+ 142
- 0
src/components/Template/StepComponents/StaticStartStep.vue View File

@ -0,0 +1,142 @@
<template>
<div class="static-start-step">
<el-form label-width="120px">
<el-form-item label="静置目的">
<el-input v-model="formData.purpose" placeholder="请输入静置目的"></el-input>
</el-form-item>
<el-form-item label="预计时间">
<el-input v-model="formData.duration" placeholder="请输入预计时间">
<el-select v-model="formData.durationUnit" slot="append" style="width: 80px;">
<el-option label="min" value="min"></el-option>
<el-option label="h" value="h"></el-option>
<el-option label="d" value="d"></el-option>
</el-select>
</el-input>
</el-form-item>
<el-form-item label="温度要求">
<el-input v-model="formData.temperature" placeholder="请输入温度">
<el-select v-model="formData.temperatureUnit" slot="append" style="width: 60px;">
<el-option label="℃" value="℃"></el-option>
<el-option label="℉" value="℉"></el-option>
</el-select>
</el-input>
</el-form-item>
<el-form-item label="容器状态">
<el-select v-model="formData.containerState" placeholder="请选择容器状态">
<el-option label="密封" value="sealed"></el-option>
<el-option label="半密封" value="semiSealed"></el-option>
<el-option label="开放" value="open"></el-option>
</el-select>
</el-form-item>
<el-form-item label="环境条件">
<el-checkbox-group v-model="formData.environmentConditions">
<el-checkbox label="避光">避光</el-checkbox>
<el-checkbox label="恒温">恒温</el-checkbox>
<el-checkbox label="静置">静置</el-checkbox>
<el-checkbox label="无震动">无震动</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="静置位置">
<el-select v-model="formData.position" placeholder="请选择静置位置">
<el-option label="室温" value="roomTemp"></el-option>
<el-option label="冷藏" value="refrigerated"></el-option>
<el-option label="冷冻" value="frozen"></el-option>
<el-option label="恒温箱" value="incubator"></el-option>
<el-option label="水浴" value="waterBath"></el-option>
</el-select>
</el-form-item>
<el-form-item label="容器类型">
<el-select v-model="formData.containerType" placeholder="请选择容器类型">
<el-option label="离心管" value="centrifugeTube"></el-option>
<el-option label="试管" value="testTube"></el-option>
<el-option label="烧杯" value="beaker"></el-option>
<el-option label="培养皿" value="petriDish"></el-option>
<el-option label="容量瓶" value="volumetricFlask"></el-option>
</el-select>
</el-form-item>
<el-form-item label="体积范围">
<el-input v-model="formData.volume" placeholder="请输入体积">
<el-select v-model="formData.volumeUnit" slot="append" style="width: 80px;">
<el-option label="ml" value="ml"></el-option>
<el-option label="μl" value="μl"></el-option>
<el-option label="l" value="l"></el-option>
</el-select>
</el-input>
</el-form-item>
<el-form-item label="开始时间">
<el-date-picker
v-model="formData.startTime"
type="datetime"
placeholder="请选择开始时间"
format="yyyy-MM-dd HH:mm"
value-format="yyyy-MM-dd HH:mm:ss"
></el-date-picker>
</el-form-item>
<el-form-item label="观察记录">
<el-checkbox-group v-model="formData.observationRecords">
<el-checkbox label="颜色变化">颜色变化</el-checkbox>
<el-checkbox label="沉淀形成">沉淀形成</el-checkbox>
<el-checkbox label="相分离">相分离</el-checkbox>
<el-checkbox label="结晶">结晶</el-checkbox>
<el-checkbox label="其他">其他</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="备注">
<el-input
v-model="formData.notes"
type="textarea"
:rows="3"
placeholder="请输入备注信息"
></el-input>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: 'StaticStartStep',
props: ['stepData'],
data() {
return {
formData: {
purpose: '',
duration: '',
durationUnit: 'h',
temperature: '',
temperatureUnit: '℃',
containerState: '',
environmentConditions: [],
position: '',
containerType: '',
volume: '',
volumeUnit: 'ml',
startTime: '',
observationRecords: [],
notes: ''
}
}
},
created() {
this.formData = { ...this.formData, ...this.stepData }
},
watch: {
formData: {
handler(newVal) {
this.$emit('update', newVal)
},
deep: true
}
}
}
</script>

+ 147
- 0
src/components/Template/StepComponents/TakePlateStep.vue View File

@ -0,0 +1,147 @@
<template>
<div class="take-plate-step">
<el-form label-width="120px">
<el-form-item label="取板目的">
<el-input v-model="formData.purpose" placeholder="请输入取板目的"></el-input>
</el-form-item>
<el-form-item label="微孔板类型">
<el-select v-model="formData.plateType" placeholder="请选择微孔板类型">
<el-option label="96孔板" value="96well"></el-option>
<el-option label="48孔板" value="48well"></el-option>
<el-option label="24孔板" value="24well"></el-option>
<el-option label="12孔板" value="12well"></el-option>
<el-option label="6孔板" value="6well"></el-option>
<el-option label="384孔板" value="384well"></el-option>
</el-select>
</el-form-item>
<el-form-item label="取板位置">
<el-select v-model="formData.location" placeholder="请选择取板位置">
<el-option label="培养箱" value="incubator"></el-option>
<el-option label="冷藏" value="refrigerator"></el-option>
<el-option label="室温" value="roomTemp"></el-option>
<el-option label="水浴" value="waterBath"></el-option>
<el-option label="摇床" value="shaker"></el-option>
</el-select>
</el-form-item>
<el-form-item label="取板时间">
<el-date-picker
v-model="formData.takeTime"
type="datetime"
placeholder="请选择取板时间"
format="yyyy-MM-dd HH:mm"
value-format="yyyy-MM-dd HH:mm:ss"
></el-date-picker>
</el-form-item>
<el-form-item label="板子状态">
<el-select v-model="formData.plateState" placeholder="请选择板子状态">
<el-option label="正常" value="normal"></el-option>
<el-option label="有污染" value="contaminated"></el-option>
<el-option label="干燥" value="dry"></el-option>
<el-option label="湿润" value="wet"></el-option>
<el-option label="有沉淀" value="precipitate"></el-option>
<el-option label="有结晶" value="crystal"></el-option>
</el-select>
</el-form-item>
<el-form-item label="处理要求">
<el-checkbox-group v-model="formData.processRequirements">
<el-checkbox label="立即处理">立即处理</el-checkbox>
<el-checkbox label="室温放置">室温放置</el-checkbox>
<el-checkbox label="冷藏保存">冷藏保存</el-checkbox>
<el-checkbox label="冷冻保存">冷冻保存</el-checkbox>
<el-checkbox label="避光保存">避光保存</el-checkbox>
<el-checkbox label="需要检测">需要检测</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="取板数量">
<el-input-number
v-model="formData.quantity"
:min="1"
:max="10"
:step="1"
></el-input-number>
</el-form-item>
<el-form-item label="操作者">
<el-input v-model="formData.operator" placeholder="请输入操作者姓名"></el-input>
</el-form-item>
<el-form-item label="后续处理">
<el-radio-group v-model="formData.nextAction">
<el-radio label="immediate">立即处理</el-radio>
<el-radio label="delayed">延迟处理</el-radio>
<el-radio label="storage">储存待处理</el-radio>
<el-radio label="discard">丢弃</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="质量检查">
<el-checkbox-group v-model="formData.qualityChecks">
<el-checkbox label="外观检查">外观检查</el-checkbox>
<el-checkbox label="标识检查">标识检查</el-checkbox>
<el-checkbox label="完整性检查">完整性检查</el-checkbox>
<el-checkbox label="污染检查">污染检查</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="特殊要求">
<el-input
v-model="formData.specialRequirements"
type="textarea"
:rows="3"
placeholder="请输入特殊要求或注意事项"
></el-input>
</el-form-item>
<el-form-item label="备注">
<el-input
v-model="formData.notes"
type="textarea"
:rows="3"
placeholder="请输入备注信息"
></el-input>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: 'TakePlateStep',
props: ['stepData'],
data() {
return {
formData: {
purpose: '',
plateType: '',
location: '',
takeTime: '',
plateState: '',
processRequirements: [],
quantity: 1,
operator: '',
nextAction: 'immediate',
qualityChecks: [],
specialRequirements: '',
notes: ''
}
}
},
created() {
this.formData = { ...this.formData, ...this.stepData }
},
watch: {
formData: {
handler(newVal) {
this.$emit('update', newVal)
},
deep: true
}
}
}
</script>

+ 50
- 0
src/components/Template/StepComponents/UltrasoundStep.vue View File

@ -0,0 +1,50 @@
<template>
<div class="ultrasound-step">
<el-form label-width="120px">
<el-form-item label="功率(%)">
<el-input v-model="formData.power" placeholder="请输入功率百分比"></el-input>
</el-form-item>
<el-form-item label="时间(min)">
<el-input v-model="formData.time" placeholder="请输入超声时间"></el-input>
</el-form-item>
<el-form-item label="温度(℃)">
<el-input v-model="formData.temperature" placeholder="请输入温度"></el-input>
</el-form-item>
<el-form-item label="频率(kHz)">
<el-input v-model="formData.frequency" placeholder="请输入频率"></el-input>
</el-form-item>
<el-form-item label="备注">
<el-input v-model="formData.notes" type="textarea" :rows="2"></el-input>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: 'UltrasoundStep',
props: ['stepData'],
data() {
return {
formData: {
power: '',
time: '',
temperature: '',
frequency: '',
notes: ''
}
}
},
created() {
this.formData = { ...this.formData, ...this.stepData }
},
watch: {
formData: {
handler(newVal) {
this.$emit('update', newVal)
},
deep: true
}
}
}
</script>

+ 124
- 0
src/components/Template/StepComponents/VortexStep.vue View File

@ -0,0 +1,124 @@
<template>
<div class="vortex-step">
<el-form label-width="120px">
<el-form-item label="混匀方式">
<el-select v-model="formData.method" placeholder="请选择混匀方式">
<el-option label="漩涡混匀" value="vortex"></el-option>
<el-option label="摇床混匀" value="shaker"></el-option>
<el-option label="磁力搅拌" value="magnetic"></el-option>
<el-option label="机械搅拌" value="mechanical"></el-option>
</el-select>
</el-form-item>
<el-form-item label="混匀时间">
<el-input v-model="formData.time" placeholder="请输入时间">
<el-select v-model="formData.timeUnit" slot="append" style="width: 80px;">
<el-option label="s" value="s"></el-option>
<el-option label="min" value="min"></el-option>
<el-option label="h" value="h"></el-option>
</el-select>
</el-input>
</el-form-item>
<el-form-item label="转速设置">
<el-input v-model="formData.speed" placeholder="请输入转速">
<el-select v-model="formData.speedUnit" slot="append" style="width: 80px;">
<el-option label="rpm" value="rpm"></el-option>
<el-option label="Hz" value="Hz"></el-option>
</el-select>
</el-input>
</el-form-item>
<el-form-item label="体积范围">
<el-input v-model="formData.volume" placeholder="请输入体积">
<el-select v-model="formData.unit" slot="append" style="width: 80px;">
<el-option label="ml" value="ml"></el-option>
<el-option label="μl" value="μl"></el-option>
<el-option label="l" value="l"></el-option>
</el-select>
</el-input>
</el-form-item>
<el-form-item label="温度控制">
<el-input v-model="formData.temperature" placeholder="请输入温度">
<el-select v-model="formData.temperatureUnit" slot="append" style="width: 60px;">
<el-option label="℃" value="℃"></el-option>
<el-option label="℉" value="℉"></el-option>
</el-select>
</el-input>
</el-form-item>
<el-form-item label="容器类型">
<el-select v-model="formData.containerType" placeholder="请选择容器类型">
<el-option label="离心管" value="centrifugeTube"></el-option>
<el-option label="试管" value="testTube"></el-option>
<el-option label="烧杯" value="beaker"></el-option>
<el-option label="培养皿" value="petriDish"></el-option>
</el-select>
</el-form-item>
<el-form-item label="混匀模式">
<el-radio-group v-model="formData.mode">
<el-radio label="continuous">连续混匀</el-radio>
<el-radio label="interval">间歇混匀</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="formData.mode === 'interval'" label="间歇时间">
<el-input v-model="formData.intervalTime" placeholder="请输入间歇时间">
<el-select v-model="formData.intervalTimeUnit" slot="append" style="width: 80px;">
<el-option label="s" value="s"></el-option>
<el-option label="min" value="min"></el-option>
</el-select>
</el-input>
</el-form-item>
<el-form-item label="备注">
<el-input
v-model="formData.notes"
type="textarea"
:rows="3"
placeholder="请输入备注信息"
></el-input>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: 'VortexStep',
props: ['stepData'],
data() {
return {
formData: {
method: '',
time: '',
timeUnit: 's',
speed: '',
speedUnit: 'rpm',
volume: '',
unit: 'ml',
temperature: '',
temperatureUnit: '℃',
containerType: '',
mode: 'continuous',
intervalTime: '',
intervalTimeUnit: 's',
notes: ''
}
}
},
created() {
this.formData = { ...this.formData, ...this.stepData }
},
watch: {
formData: {
handler(newVal) {
this.$emit('update', newVal)
},
deep: true
}
}
}
</script>

+ 33
- 0
src/components/Template/Table.vue View File

@ -0,0 +1,33 @@
<template>
<el-table
:data="dataSource"
>
<el-table-column
v-for="(item) in columns"
:prop="item.prop"
:key = "item.prop"
:label="item.label">
</el-table-column>
</el-table>
</template>
<script>
export default {
props:{
//[{label:"",prop:"name"}]
columns:{
type:Array,
default:[],
},
dataSource:{
type:Array,
default:[],
}
}
}
</script>
<style lang="scss" scoped>
</style>

+ 3
- 1
src/store/index.js View File

@ -7,6 +7,7 @@ import tagsView from './modules/tagsView'
import permission from './modules/permission'
import settings from './modules/settings'
import getters from './getters'
import template from './modules/template'
Vue.use(Vuex)
@ -17,7 +18,8 @@ const store = new Vuex.Store({
user,
tagsView,
permission,
settings
settings,
template
},
getters
})

+ 17
- 0
src/store/modules/template.js View File

@ -0,0 +1,17 @@
const template = {
namespaced: true,
state: {
templateStatus:"",//模板状态 "actFill"(实际填报) "preFill"(预填) ”qc“(qc) "detail"(详情)
},
mutations: {
SET_TEMPLATE_STATUS: (state, status) => {
state.templateStatus = status
}
},
actions: {
setTemplateStatus({ commit }, status) {
commit('SET_TEMPLATE_STATUS', status)
}
}
}
export default template

+ 29
- 2
src/views/business/comps/template/TemplateTable.vue View File

@ -1,19 +1,40 @@
<template>
<div class="template-table">
<SP001 v-if="sn == 'SP001'" />
<!-- <SP001 v-if="sn == 'SP0012'" />
<SWYPFXRYPZB v-if="sn == 'SP001'" /> -->
<component ref="templateComponent" :is="getTemplateComponent()" :fillType="fillType">
</component>
</div>
</template>
<script>
//
import SP001 from './comps/sp/SP001';
import SWYPFXRYPZB from "./comps/sp/SWYPFXRYPZB.vue";
export default {
name: "TemplateTable",
components: { SP001 },
components: {
SP001,SWYPFXRYPZB
},
props: {
sn: {
type: String,
default: '',
},
fillType: {
type: String,
default: 'preFill',
}
},
computed: {
templateComponentMap() {
if (!this.componentMap) {
this.componentMap = {
'SP001': 'SWYPFXRYPZB',
}
}
return this.componentMap
}
},
watch: {
@ -31,6 +52,12 @@ export default {
mounted() {
},
methods: {
async getFormData() {
return await this.$refs.templateComponent.getFormData();
},
getTemplateComponent() {
return this.templateComponentMap[this.sn]
},
}
};
</script>

+ 297
- 0
src/views/business/comps/template/comps/sp/Demo.vue View File

@ -0,0 +1,297 @@
<!-- 生物样品分析溶液配制表 -->
<template>
<div>
<div class="detail-container">
<div class="detail-title"><img src="@/assets/images/detail-title.png">生物样品分析溶液配制表<img
src="@/assets/images/detail-title.png" /></div>
<div class="detail-content">
<div class="content">
<LineLabel label = "试验基本信息"/>
<BaseInfoFormPcakge ref = "baseInfo" :formConfig = "formConfig" :formData = "formData"/>
<LineLabel class="mt-20" label = "试验试剂信息"/>
<TableList class="mt-20" :columns = "sysjColumns" :dataSource = "dataSource"/>
<LineLabel class="mt-20" label = "仪器使用信息"/>
<TableList class="mt-20" :columns = "yqsColumns" :dataSource = "dataSource"/>
<LineLabel class="mt-20" label = "存储条件"/>
<BaseInfoFormPcakge ref = "storageCondition" :formConfig = "storageFormConfig" :formData = "formData"/>
<LineLabel class="mt-20" label = "操作步骤"/>
<div class="template-form-item">
<BaseInfoFormPcakge ref = "stepFormPackage" :formConfig = "stepFormConfig" :formData = "formData"/>
<!-- <CustomTable class="mt-20" :columns = "stepColumns" :dataSource = "stepDataSource"/> -->
</div>
<Step ref = "stepRef"></Step>
</div>
</div>
<button @click = "onSave">保存</button>
</div>
</div>
</template>
<script>
import BaseInfoFormPcakge from "@/components/Template/BaseInfoFormPcakge";
import LineLabel from "@/components/Template/LineLabel";
import TableList from "@/components/Template/Table";
import Step from "@/components/Template/Step";
import templateMixin from "../../mixins/templateMixin";
import CustomTable from '@/components/Template/CustomTable.vue'
export default {
name: "SWYPFXRYPZB",
components: { BaseInfoFormPcakge,LineLabel,TableList,Step,CustomTable },
mixins: [templateMixin],
props: {
value: {
type: {},
default: () => { },
}
},
watch: {
value: {
immediate: true,
handler(v) {
}
}
},
data() {
return {
dataSource:[{name:"名称1"}],
stepDataSource:[{
code1:"目标溶液编号1",
code1:"目标溶液编号1",
code1:"目标溶液编号1",
code1:"目标溶液编号1",
code1:"目标溶液编号1",
code1:"目标溶液编号1",
code1:"目标溶液编号1",
code1:"目标溶液编号1",
code1:"目标溶液编号1",
code2:"起始溶液编号1",
code3:"预设起始溶液体积1",
code4:"实际起始溶液体积1",
code5:"预设稀释液体积1",
code6:"实际稀释液体积1",
code7:"预设目标溶液浓度1",
code8:"实际目标溶液浓度1",
}],
stepColumns:[
{label:"目标溶液编号",prop:"code1",width: 120},
{label:"起始溶液编号",prop:"code2",width: 120},
{label:"起始溶液编号",prop:"code2",width: 120},
{label:"起始溶液编号",prop:"code2",width: 120},
{label:"起始溶液编号",prop:"code2",width: 120},
{label:"起始溶液编号",prop:"code2",width: 120},
{label:"起始溶液编号",prop:"code2",width: 120},
{label:"起始溶液编号",prop:"code2",width: 120},
{label:"起始溶液编号",prop:"code2",width: 120},
{
label:"预设起始溶液体积",prop:"code3",showSelect: true,
options: [
{ label: 'mg', value: 'mg' },
{ label: 'ng', value: 'ng' },
]
},
{
label:"实际起始溶液体积",prop:"code4",showSelect: true,width: 180,
options: [
{ label: 'mg', value: 'mg' },
{ label: 'ng', value: 'ng' },
]
},
{
label:"预设稀释液体积",prop:"code5",showSelect: true,width: 180,
options: [
{ label: 'mg', value: 'mg' },
{ label: 'ng', value: 'ng' },
]
},
{
label:"实际稀释液体积",prop:"code6",showSelect: true,width: 180,
options: [
{ label: 'mg', value: 'mg' },
{ label: 'ng', value: 'ng' },
]
},
{
label:"预设目标溶液浓度",prop:"code7",showSelect: true,width: 180,
options: [
{ label: 'mg', value: 'mg' },
{ label: 'ng', value: 'ng' },
]
},
{
label:"实际目标溶液浓度",prop:"code8",showSelect: true,width: 180,
options: [
{ label: 'mg', value: 'mg' },
{ label: 'ng', value: 'ng' },
]
},
],
sysjColumns:[
{label:"试剂名称",prop:"name"},
{label:"编号",prop:"name"},
{label:"批号",prop:"name"},
{label:"浓度/含量/纯度",prop:"name"},
{label:"来源",prop:"name"},
{label:"失效日",prop:"name"},
],
yqsColumns:[
{label:"仪器名称",prop:"name"},
{label:"仪器型号",prop:"name"},
{label:"仪器编号",prop:"name"},
{label:"下次测试/校准/检定日期",prop:"name"},
],
storageFormConfig:[
{
type:"conditionItem",
config:{
storageCondition1:{
label:"存储条件",
type:"select",
fillType:"preFill",
options:[],
otherCode:"other1",
},
}
}
],
formConfig: [
{
type:"cardItem",
config:{
name:{
label:"试验名称",
type:"input",
disabled:true,
},
code:{
label:"试验编号",
type:"input",
disabled:true,
},
methodCode:{
label:"方法编号",
type:"input",
fillType:"preFill",
copyFrom:"name",//
},
versionNum:{
label:"版本号",
type:"input",
fillType:"actFill"
},
}
},
{
type:"conditionItem",
label:"试验配制条件",
config:{
pre:{
label:"预填",
type:"select",
multiple:true,
fillType:"preFill",
options:[
{label:"白光",value:"1"},
{label:"黄光",value:"3"},
{label:"其他",value:"-1"},
],
otherCode:"other1",
},
act:{
label:"实际",
type:"select",
fillType:"actFill",
otherCode:"other2",
multiple:true,
options:[
{label:"白光",value:"1"},
{label:"黄光",value:"3"},
{label:"其他",value:"-1"},
]
}
}
},
{
type:"cellItem",
label:"配置时间",
config:{
startDate:{
label:"开始时间",
type:"dateTime",
},
endDate:{
label:"结束时间",
type:"dateTime",
},
}
}
],
stepFormConfig:[
{
type:"step",
config:{
startDate:{
label:"目标溶液名称",
type:"input",
},
endDate:{
label:"目标溶液编号",
type:"input",
subType:"span",
subKey:"targetCode1",
},
target:{
label:"目标溶液预计浓度",
type:"input",
subType:"select",
subKey:"taget1",
subOptions:[
{label:"mg",value:"mg"},
{label:"ng",value:"ng"},
],
},
target:{
label:"目标溶液预计浓度",
type:"input",
subType:"clickable",
subKey:"taget1c",
},
}
}
],
formData:{}
};
},
mounted() {
setTimeout(()=>{
this.formData = {
// code:"code1",name:"name1",act:["1","-1"],taget1:"mg",targetCode1:"123456",taget1c:""
}
},3000)
setTimeout(()=>{
this.storageFormConfig[0].config.storageCondition1.options = [
{label:"白光",value:"1"},
{label:"黄光",value:"3"},
{label:"其他",value:"-1"},
]
},4000)
},
methods: {
async onSave(){
// const result1 = await this.$refs.baseInfo.getFormData();
// const result2 = await this.$refs.storageCondition.getFormData();
// const result3 = await this.$refs.stepRef.getFormData();
const result = await this.$refs.stepFormPackage.getFormData();
console.log(result,"reee")
}
}
};
</script>
<style rel="stylesheet/scss" lang="scss">
.mt-20{
margin-top: 20px;
}
</style>

+ 263
- 0
src/views/business/comps/template/comps/sp/SWYPFXRYPZB.vue View File

@ -0,0 +1,263 @@
<!-- 生物样品分析溶液配制表 -->
<template>
<div>
<div class="detail-container">
<div class="detail-title"><img src="@/assets/images/detail-title.png">生物样品分析溶液配制表<img
src="@/assets/images/detail-title.png" /></div>
<div class="detail-content">
<div class="content">
<LineLabel label = "试验基本信息"/>
<BaseInfoFormPcakge ref = "baseInfo" :formConfig = "formConfig" :formData = "formData"/>
<LineLabel class="mt-20" label = "试验试剂信息"/>
<TableList class="mt-20" :columns = "sysjColumns" :dataSource = "dataSource"/>
<LineLabel class="mt-20" label = "仪器使用信息"/>
<TableList class="mt-20" :columns = "yqsColumns" :dataSource = "dataSource"/>
<LineLabel class="mt-20" label = "存储条件"/>
<BaseInfoFormPcakge ref = "storageCondition" :formConfig = "storageFormConfig" :formData = "formData"/>
<LineLabel class="mt-20" label = "操作步骤"/>
<div class="template-form-item">
<BaseInfoFormPcakge ref = "stepFormPackage" :formConfig = "stepFormConfig" :formData = "formData"/>
</div>
<Step ref = "stepRef"></Step>
</div>
</div>
</div>
</div>
</template>
<script>
import BaseInfoFormPcakge from "@/components/Template/BaseInfoFormPcakge";
import LineLabel from "@/components/Template/LineLabel";
import TableList from "@/components/Template/Table";
import Step from "@/components/Template/Step";
import templateMixin from "../../mixins/templateMixin";
import CustomTable from '@/components/Template/CustomTable.vue';
const mgOptions = [
{label:"mg",value:"mg"},
{label:"ng",value:"ng"},
];
const conditionOptions = [
{label:"条件1",value:"1"},
{label:"条件2",value:"2"},
{label:"条件3",value:"3"},
{label:"其他",value:"-1"},
];
export default {
name: "SWYPFXRYPZB",
components: { BaseInfoFormPcakge,LineLabel,TableList,Step,CustomTable },
mixins: [templateMixin],
props: {
value: {
type: {},
default: () => { },
},
fillType: {
type: String,
default: 'preFill',
},
},
watch: {
value: {
immediate: true,
handler(v) {
}
}
},
data() {
return {
dataSource:[{name:"名称1"}],
sysjColumns:[
{label:"试剂名称",prop:"reagentName"},
{label:"编号",prop:"reagentCode"},
{label:"批号",prop:"reagentNo"},
{label:"浓度/含量/纯度",prop:"concentration"},
{label:"来源",prop:"source"},
{label:"失效日",prop:"expireDate"},
],
yqsColumns:[
{label:"仪器名称",prop:"instrumentName"},
{label:"仪器型号",prop:"instrumentModel"},
{label:"仪器编号",prop:"instrumentCode"},
{label:"下次测试/校准/检定日期",prop:"nextTestDate"},
],
storageFormConfig:[
{
type:"conditionItem",
config:{
storageCondition1:{
label:"存储条件",
type:"select",
fillType:"preFill",
options:conditionOptions,
otherCode:"other1",
},
}
}
],
formConfig: [
{
type:"cardItem",
config:{
name:{
label:"试验名称",
type:"input",
disabled:true,
},
code:{
label:"试验编号",
type:"input",
disabled:true,
},
methodCode:{
label:"方法编号",
type:"input",
fillType:"preFill",
copyFrom:"name",//
},
versionNum:{
label:"版本号",
type:"input",
fillType:"actFill"
},
}
},
{
type:"conditionItem",
label:"试验配制条件",
config:{
pre:{
label:"预填",
type:"select",
multiple:true,
fillType:"preFill",
options:conditionOptions,
otherCode:"other1",
},
act:{
label:"实际",
type:"select",
fillType:"actFill",
otherCode:"other2",
multiple:true,
options:conditionOptions
}
}
},
{
type:"cellItem",
label:"配置时间",
config:{
startDate:{
label:"开始时间",
type:"input",
},
endDate:{
label:"结束时间",
type:"input",
},
}
}
],
stepFormConfig:[
{
type:"step",
config:{
targetName:{
label:"目标溶液名称",
type:"input",
fillType:"preFill",
},
targetCode:{
label:"目标溶液编号",
type:"input",
subType:"span",
fillType:"preFill",
subKey:"targetCode1",
},
targetPreConcentration:{
label:"目标溶液预计浓度",
type:"input",
subType:"select",
subKey:"targetPreConcentrationUnit",
fillType:"preFill",
subOptions:mgOptions,
},
targetActConcentration:{
label:"目标溶液实际浓度",
type:"input",
subType:"select",
subKey:"targetActConcentrationUnit",
fillType:"actFill",
subFillType:"preFill",
subOptions:mgOptions,
},
targetPreVolume:{
label:"目标溶液预计体积",
type:"input",
subType:"select",
subKey:"targetPreVolumeUnit",
subOptions:mgOptions,
fillType:"preFill",
},
targetActVolume:{
label:"目标溶液实际体积",
type:"input",
subType:"select",
subKey:"targetActVolumeUnit",
fillType:"actFill",
subFillType:"preFill",
subOptions:mgOptions,
},
effectivePeriod:{
label:"有效周期",
type:"input",
subType:"select",
subKey:"effectivePeriodUnit",
fillType:"preFill",
subOptions:[
{label:"小时",value:"hour"},
{label:"天",value:"day"},
],
},
expireDate:{
label:"失效日",
type:"input",
},
}
}
],
formData:{}
};
},
mounted() {
setTimeout(()=>{
this.formData = {
// code:"code1",name:"name1",act:["1","-1"],taget1:"mg",targetCode1:"123456",taget1c:""
}
},3000)
},
methods: {
async getFormData(){
const baseData = await this.$refs.baseInfo.getFormData();
const conditionData = await this.$refs.storageCondition.getFormData();
const stepData = await this.$refs.stepRef.getFormData();
const stepFormData = await this.$refs.stepFormPackage.getFormData();
return {
...baseData,
...conditionData,
...stepData,
...stepFormData,
}
},
}
};
</script>
<style rel="stylesheet/scss" lang="scss">
.mt-20{
margin-top: 20px;
}
</style>

+ 16
- 0
src/views/business/comps/template/mixins/templateMixin.js View File

@ -0,0 +1,16 @@
export default {
data() {
},
mounted() {
this.setTemplateStatus(this.fillType)
},
methods: {
setTemplateStatus(status) {
this.$store.commit('template/SET_TEMPLATE_STATUS', status)
}
},
}

+ 1
- 1
src/views/business/template/list.vue View File

@ -79,7 +79,7 @@
</el-dialog>
<el-dialog :close-on-click-modal="false" :close-on-press-escape="false" :title="$t('page.system.template.bdpz')" :visible.sync="tableDialog.visible"
width="90%" append-to-body>
<TemplateTable :sn="tableDialog.sn" />
<TemplateTable ref = "templateTable" :sn="tableDialog.sn" fillType="preFill" />
</el-dialog>
</div>
</template>

+ 2
- 2
vue.config.js View File

@ -34,8 +34,8 @@ module.exports = {
proxy: {
// detail: https://cli.vuejs.org/config/#devserver-proxy
[process.env.VUE_APP_BASE_API]: {
target: `http://localhost:8080`,
// target: `http://39.99.251.173:8080`,
// target: `http://localhost:8080`,
target: `http://39.99.251.173:8080`,
changeOrigin: true,
pathRewrite: {
['^' + process.env.VUE_APP_BASE_API]: ''

Loading…
Cancel
Save