feat(data-input): 新增输入指标管理
- 输入指标改为数据库管理并绑定到药品数据
This commit is contained in:
parent
6252553aee
commit
8632c1c606
@ -16,7 +16,7 @@
|
||||
"@tiptap/vue-3": "^2.10.4",
|
||||
"ant-design-vue": "^4.2.6",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"axios": "^1.7.9",
|
||||
"axios": "^1.8.2",
|
||||
"dayjs": "^1.11.13",
|
||||
"js-md5": "^0.8.3",
|
||||
"pinia": "^2.3.0",
|
||||
|
@ -1,179 +1,194 @@
|
||||
<template>
|
||||
<div class="calc-container">
|
||||
<div class="setting-container">
|
||||
<div class="grid list-container grid-cols-2 gap-4 py-2">
|
||||
<div class="input-item">
|
||||
<div class="title">体重</div>
|
||||
<div class="input">
|
||||
<input type="number" v-model="commonValues.weight" placeholder="单位(kg)"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-item">
|
||||
<div class="title">24h尿素</div>
|
||||
<div class="input">
|
||||
<input type="number" v-model="commonValues.niaosu" placeholder="单位(mmol)"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="calc-container">
|
||||
<div class="setting-container">
|
||||
<div class="grid list-container grid-cols-2 gap-4 py-2">
|
||||
<div class="input-item">
|
||||
<div class="title">体重</div>
|
||||
<div class="input">
|
||||
<input type="number" v-model="commonValues.weight" placeholder="单位(kg)"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-item">
|
||||
<div class="title">24h尿素</div>
|
||||
<div class="input">
|
||||
<input type="number" v-model="commonValues.niaosu" placeholder="单位(mmol)"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Tabs>
|
||||
<TabPane key="gut" tab="肠内制剂">
|
||||
<div class="grid list-container grid-cols-2 gap-4 py-2 setting-input-scroller">
|
||||
<div class="input-item" v-for="(it,key) in settingList.filter(s=>s.type=='gut')" :key="key">
|
||||
<div class="title">{{ it.title }}</div>
|
||||
<div class="input">
|
||||
<input type="number" v-model="it.value" :placeholder="it.placeholder"/>
|
||||
<span class="unit">{{ it.unit }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<Tabs>
|
||||
<TabPane key="gut" tab="肠内制剂">
|
||||
<div class="grid list-container grid-cols-2 gap-4 py-2 setting-input-scroller">
|
||||
<div class="input-item" v-for="(it,key) in settingList.filter(s=>s.type=='gut')" :key="key">
|
||||
<div class="title">{{ it.title }}</div>
|
||||
<div class="input">
|
||||
<input type="number" v-model="it.value" :placeholder="it.placeholder"/>
|
||||
<span class="unit">{{ it.unit }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TabPane>
|
||||
<TabPane key="vein" tab="静脉制剂">
|
||||
<div class="grid list-container grid-cols-2 gap-4 py-2 setting-input-scroller">
|
||||
<div class="input-item" v-for="(it,key) in settingList.filter(s=>s.type=='vein')" :key="key">
|
||||
<div class="title">{{ it.title }}</div>
|
||||
<div class="input">
|
||||
<input type="number" v-model="it.value" :placeholder="it.placeholder"/>
|
||||
<span class="unit">{{ it.unit }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</div>
|
||||
<div class="text-center py-4">
|
||||
<Button type="primary" @click="calcResult">测试计算</Button>
|
||||
</div>
|
||||
<ResultModal v-model:result="testResultValue" />
|
||||
</div>
|
||||
</TabPane>
|
||||
<TabPane key="vein" tab="静脉制剂">
|
||||
<div class="grid list-container grid-cols-2 gap-4 py-2 setting-input-scroller">
|
||||
<div class="input-item" v-for="(it,key) in settingList.filter(s=>s.type=='vein')" :key="key">
|
||||
<div class="title">{{ it.title }}</div>
|
||||
<div class="input">
|
||||
<input type="number" v-model="it.value" :placeholder="it.placeholder"/>
|
||||
<span class="unit">{{ it.unit }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</div>
|
||||
<div class="text-center py-4">
|
||||
<Button type="primary" @click="calcResult">测试计算</Button>
|
||||
</div>
|
||||
<ResultModal v-model:result="testResultValue"/>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {onMounted,defineComponent, ref} from "vue";
|
||||
import {onMounted, defineComponent, ref} from "vue";
|
||||
import {TabPane, Tabs, Button, Modal} from "ant-design-vue";
|
||||
import {inputProducts} from "@/pages/result/test/data-input.ts";
|
||||
// import {inputProducts} from "@/pages/result/test/data-input.ts";
|
||||
import ResultModal from "@/pages/result/components/result-modal.vue";
|
||||
import {expressionList} from "@/service/api/result.ts";
|
||||
import {parseExpression} from "@/core/string.ts";
|
||||
import {getAllProduct} from "@/service/use-result-vars.ts";
|
||||
import {getAllProductInput} from "@/service/api/product.ts";
|
||||
|
||||
const commonValues = ref({
|
||||
weight: '',
|
||||
niaosu: '',
|
||||
weight: '',
|
||||
niaosu: '',
|
||||
})
|
||||
|
||||
defineComponent({
|
||||
name: 'Calculation'
|
||||
name: 'Calculation'
|
||||
})
|
||||
const ResultExpressionList = ref<ResultExpression[]>([]);
|
||||
const settingList = ref<SettingItem[]>([])
|
||||
const testResultValue = ref<ResultItem[]>([])
|
||||
const productValues = getAllProduct();
|
||||
|
||||
onMounted(()=>{
|
||||
expressionList().then((res) => {
|
||||
ResultExpressionList.value = res.list;
|
||||
});
|
||||
const setting: SettingItem[] = []
|
||||
inputProducts.forEach(it => {
|
||||
setting.push({
|
||||
title: it.name,
|
||||
key: it.key,
|
||||
value: '',
|
||||
placeholder: it.placeholder, // + (it.unit?`(${it.unit})`:'')
|
||||
type: it.type,
|
||||
unit: it.unit,
|
||||
})
|
||||
if (it.addition) {
|
||||
setting.push({
|
||||
title: it.name,
|
||||
key: it.addition.key,
|
||||
value: '',
|
||||
placeholder: it.addition.placeholder, // + (it.unit?`(${it.unit})`:'')
|
||||
type: it.type,
|
||||
unit: it.addition.unit,
|
||||
})
|
||||
}
|
||||
})
|
||||
settingList.value = setting
|
||||
onMounted(() => {
|
||||
expressionList().then((res) => {
|
||||
ResultExpressionList.value = res.list;
|
||||
});
|
||||
initInputList().then(() => {
|
||||
console.log("init input setting success")
|
||||
})
|
||||
})
|
||||
function runCode(expression: string, vars: any) {
|
||||
return new Function('vars', `with(vars) { return ${expression};}`)(vars)
|
||||
}
|
||||
function getResultValues(){
|
||||
// 结果集
|
||||
const result:{
|
||||
[key: string]: number
|
||||
} = {};
|
||||
const {niaosu,weight} = commonValues.value
|
||||
const inputs: { [key: string]: number }= {
|
||||
niaosu: niaosu ?Number(niaosu): 0,
|
||||
weight: weight ?Number(weight): 0,
|
||||
|
||||
|
||||
async function initInputList() {
|
||||
|
||||
const setting: SettingItem[] = []
|
||||
const inputProducts = await getAllProductInput();
|
||||
inputProducts.forEach(it => {
|
||||
setting.push({
|
||||
title: it.name,
|
||||
key: it.key,
|
||||
value: '',
|
||||
placeholder: it.placeholder, // + (it.unit?`(${it.unit})`:'')
|
||||
type: it.type,
|
||||
unit: it.unit,
|
||||
})
|
||||
if (it.addition) {
|
||||
setting.push({
|
||||
title: it.name,
|
||||
key: it.addition.key,
|
||||
value: '',
|
||||
placeholder: it.addition.placeholder, // + (it.unit?`(${it.unit})`:'')
|
||||
type: it.type,
|
||||
unit: it.addition.unit,
|
||||
})
|
||||
}
|
||||
settingList.value.forEach(it=>{
|
||||
inputs[it.key] = it.value ? Number(it.value) : 0
|
||||
})
|
||||
ResultExpressionList.value.forEach(it=>{
|
||||
const expression = parseExpression(it.expression);
|
||||
if(expression.length == 0) return;
|
||||
try{
|
||||
const value = runCode(expression, {
|
||||
...(productValues.value),
|
||||
...result,
|
||||
input: inputs,
|
||||
})
|
||||
result[it.key] = value;
|
||||
}catch (e) {
|
||||
Modal.warning({
|
||||
title: '提示',
|
||||
content: `计算出错:${(e as Error).message}`
|
||||
})
|
||||
console.error('getResultValues',e,expression)
|
||||
}
|
||||
})
|
||||
return result;
|
||||
})
|
||||
settingList.value = setting
|
||||
}
|
||||
|
||||
function calcResult(){
|
||||
const result = getResultValues();
|
||||
console.log('result',result)
|
||||
const resultList:ResultItem[] = [];
|
||||
ResultExpressionList.value.forEach(it=>{
|
||||
resultList.push({
|
||||
title:it.title,
|
||||
key:it.key,
|
||||
value: result[it.key] || 0
|
||||
})
|
||||
function runCode(expression: string, vars: any) {
|
||||
return new Function('vars', `with(vars) { return ${expression};}`)(vars)
|
||||
}
|
||||
|
||||
function getResultValues() {
|
||||
// 结果集
|
||||
const result: {
|
||||
[key: string]: number
|
||||
} = {};
|
||||
const {niaosu, weight} = commonValues.value
|
||||
const inputs: { [key: string]: number } = {
|
||||
niaosu: niaosu ? Number(niaosu) : 0,
|
||||
weight: weight ? Number(weight) : 0,
|
||||
}
|
||||
settingList.value.forEach(it => {
|
||||
inputs[it.key] = it.value ? Number(it.value) : 0
|
||||
})
|
||||
ResultExpressionList.value.forEach(it => {
|
||||
const expression = parseExpression(it.expression);
|
||||
if (expression.length == 0) return;
|
||||
try {
|
||||
const value = runCode(expression, {
|
||||
...(productValues.value),
|
||||
...result,
|
||||
input: inputs,
|
||||
})
|
||||
result[it.key] = value;
|
||||
} catch (e) {
|
||||
Modal.warning({
|
||||
title: '提示',
|
||||
content: `计算出错:${(e as Error).message}`
|
||||
})
|
||||
console.error('getResultValues', e, expression)
|
||||
}
|
||||
})
|
||||
return result;
|
||||
}
|
||||
|
||||
function calcResult() {
|
||||
const result = getResultValues();
|
||||
console.log('result', result)
|
||||
const resultList: ResultItem[] = [];
|
||||
ResultExpressionList.value.forEach(it => {
|
||||
resultList.push({
|
||||
title: it.title,
|
||||
key: it.key,
|
||||
value: result[it.key] || 0
|
||||
})
|
||||
console.log(resultList);
|
||||
testResultValue.value = resultList;
|
||||
})
|
||||
console.log(resultList);
|
||||
testResultValue.value = resultList;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.calc-container{
|
||||
max-width: 90%;
|
||||
margin: auto;
|
||||
width: 600px;
|
||||
.calc-container {
|
||||
max-width: 90%;
|
||||
margin: auto;
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
.list-container {
|
||||
margin-right: -4px;
|
||||
padding-right: 4px;
|
||||
margin-right: -4px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.setting-input-scroller {
|
||||
max-height: calc(100vh - 200px);
|
||||
overflow: auto;
|
||||
max-height: calc(100vh - 200px);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.setting-container {
|
||||
.input {
|
||||
@apply w-full relative border rounded mt-1 flex items-center bg-white overflow-hidden;
|
||||
}
|
||||
.input {
|
||||
@apply w-full relative border rounded mt-1 flex items-center bg-white overflow-hidden;
|
||||
}
|
||||
|
||||
input {
|
||||
@apply flex-1 outline-0 block ml-2 py-1 mr-1;
|
||||
}
|
||||
input {
|
||||
@apply flex-1 outline-0 block ml-2 py-1 mr-1;
|
||||
}
|
||||
|
||||
.unit {
|
||||
@apply absolute inset-y-0 right-0 px-2 py-1 bg-gray-100 block h-full text-center;
|
||||
min-width: 34px;
|
||||
}
|
||||
.unit {
|
||||
@apply absolute inset-y-0 right-0 px-2 py-1 bg-gray-100 block h-full text-center;
|
||||
min-width: 34px;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -65,7 +65,7 @@ onUnmounted(() => {
|
||||
editor.destroy()
|
||||
})
|
||||
</script>
|
||||
<style>
|
||||
<style lang="scss">
|
||||
.tiptap {
|
||||
}
|
||||
.tiptap{
|
||||
|
@ -204,7 +204,7 @@ function handleHideModal() {
|
||||
.menu-item {}
|
||||
|
||||
.menu-icon {
|
||||
font-size: 26px;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.app-main-container {
|
||||
|
13
src/pages/input/index.vue
Normal file
13
src/pages/input/index.vue
Normal file
@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Hello World</h1>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
h1 {
|
||||
|
||||
}
|
||||
</style>
|
@ -13,9 +13,11 @@ import { ProductCols } from "@/service/api/product";
|
||||
import { getList, deleteProduct } from "@/service/api/product";
|
||||
import useRequest from "@/service/useRequest";
|
||||
import EditModal from "./edit-modal.vue"
|
||||
import ProductInputModal from "./input/index.vue"
|
||||
|
||||
|
||||
const editProduct = ref<Partial<ProductInfoModel>>();
|
||||
const productInput = ref<Partial<ProductInfoModel>>();
|
||||
// const formState = ref<{name?:string;category?:string}>({
|
||||
// })
|
||||
const searchParams = ref<ProductSearchParam>({
|
||||
@ -90,14 +92,16 @@ function handleSearch(){
|
||||
<th>制剂别名</th>
|
||||
<th>类型</th>
|
||||
<th v-for="th in ProductCols">{{ th.name }}</th>
|
||||
<th width="100" class="text-center">计量单位</th>
|
||||
<th width="150" class="text-center">操作</th>
|
||||
<th width="100">计量单位</th>
|
||||
<th width="180">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(tr, rowIndex) in allDataList?.list" :key="rowIndex">
|
||||
<td>{{ tr.name }}</td>
|
||||
<td>{{ tr.alias }}</td>
|
||||
<td>
|
||||
<span :title="tr.alias">{{ tr.alias }}</span>
|
||||
</td>
|
||||
<td>{{ tr.category == 'gut'?'肠内制剂':'静脉制剂' }}</td>
|
||||
<td v-for="th in ProductCols">{{ (tr as any)[th.alias] || 'NULL' }}</td>
|
||||
|
||||
@ -105,6 +109,7 @@ function handleSearch(){
|
||||
<td>
|
||||
<Space :size="20">
|
||||
<a @click="editProduct = tr">编辑</a>
|
||||
<a @click="productInput = tr">输入指标</a>
|
||||
<a @click="handleDelete(tr.id)">删除</a>
|
||||
</Space>
|
||||
</td>
|
||||
@ -122,6 +127,7 @@ function handleSearch(){
|
||||
</div>
|
||||
</div>
|
||||
<EditModal v-if="!!editProduct" v-model="editProduct" @saved="refresh" />
|
||||
<ProductInputModal v-if="!!productInput" v-model="productInput" />
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss"></style>
|
||||
|
161
src/pages/product/input/index.vue
Normal file
161
src/pages/product/input/index.vue
Normal file
@ -0,0 +1,161 @@
|
||||
<template>
|
||||
<Modal
|
||||
:width="640" :open="!saveData" :footer="null" @cancel="handleCancel" :mask-closable="false"
|
||||
:destroy-on-close="true" title="营养制剂输入指标"
|
||||
>
|
||||
|
||||
<div class="result-table mt-4">
|
||||
<Spin :spinning="loading">
|
||||
<table class="table border">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>指标名称</th>
|
||||
<th>指标Key</th>
|
||||
<th width="120">输入提示</th>
|
||||
<th>单位</th>
|
||||
<th width="120">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(tr, rowIndex) in allDataList?.list" :key="rowIndex">
|
||||
<td>{{ tr.name }}</td>
|
||||
<td>{{ tr.key }}</td>
|
||||
<td>{{ tr.placeholder }}</td>
|
||||
<td>{{ tr.unit }}</td>
|
||||
<td>
|
||||
<Space :size="20">
|
||||
<a @click="saveData = tr">编辑</a>
|
||||
<a @click="handleDelete(tr.id)">删除</a>
|
||||
</Space>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div v-if="!allDataList || allDataList?.total == 0" style="padding:30px;">
|
||||
<Empty description="暂无数据"/>
|
||||
</div>
|
||||
</Spin>
|
||||
<div class="text-center mt-4">
|
||||
<Button type="primary" @click="handleCreateNew">添加指标</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
<Modal
|
||||
:open="!!saveData" :width="640" :footer="null" :destroy-on-close="true" title="营养制剂输入指标"
|
||||
:mask-closable="false" @cancel="saveData=undefined">
|
||||
<div>
|
||||
<Form
|
||||
@finish="handleSave"
|
||||
autocomplete="off"
|
||||
:model="saveData"
|
||||
:label-col="{ span: 6 }"
|
||||
:wrapper-col="{ span: 16 }"
|
||||
>
|
||||
<div class="py-8 px-10">
|
||||
<div class="form-item-container">
|
||||
<Form.Item label="指标名称" name="name"
|
||||
:rules="[{ required: true, message: '请输入指标名称' }]">
|
||||
<Input placeholder="请输入指标名称" v-model:value="saveData.name"/>
|
||||
</Form.Item>
|
||||
<Form.Item label="指标Key" name="key" :rules="[{ required: true, message: '请输入指标Key' }]">
|
||||
<Input placeholder="请输入指标Key" v-model:value="saveData.key"/>
|
||||
</Form.Item>
|
||||
<Form.Item label="输入提示" name="placeholder">
|
||||
<Input placeholder="请输入指标提示,留空默认使用:入液量(ml)" v-model:value="saveData.placeholder"/>
|
||||
</Form.Item>
|
||||
<Form.Item label="单位" name="unit">
|
||||
<Input placeholder="请输入指标单位,留空默认使用:ml" v-model:value="saveData.unit"/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<Space :size="20">
|
||||
<Button type="default" @click="saveData = undefined">取消</Button>
|
||||
<Button :loading="saveLoading" type="primary" html-type="submit">保存</Button>
|
||||
</Space>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {Empty, Modal, Space, Spin, Button, Form, Input, message} from "ant-design-vue";
|
||||
import {onMounted, ref} from "vue";
|
||||
import {
|
||||
getProductDataInput,
|
||||
saveProductDataInput,
|
||||
deleteProductDataInput,
|
||||
} from "@/service/api/product.ts";
|
||||
import useRequest from "@/service/useRequest.ts";
|
||||
|
||||
const editData = defineModel<Partial<ProductInfoModel>>();
|
||||
const saveData = ref<Partial<ProductDataInputModel>>()
|
||||
const { loading:saveLoading, runAsync, error } = useRequest(saveProductDataInput, {
|
||||
manual: true
|
||||
})
|
||||
const {
|
||||
data: allDataList,
|
||||
loading,
|
||||
run,
|
||||
refresh
|
||||
} = useRequest(() => getProductDataInput(editData.value!.id), {manual: true})
|
||||
|
||||
function handleCancel() {
|
||||
editData.value = undefined;
|
||||
}
|
||||
|
||||
function handleCreateNew() {
|
||||
saveData.value = {
|
||||
product_id: editData.value!.id,
|
||||
key: '',
|
||||
name: '',
|
||||
placeholder: '',
|
||||
unit: '',
|
||||
}
|
||||
}
|
||||
function deleteById(id: number) {
|
||||
deleteProductDataInput(id).then(() => {
|
||||
message.success('删除成功')
|
||||
refresh()
|
||||
})
|
||||
}
|
||||
|
||||
function handleDelete(id: number) {
|
||||
Modal.confirm({
|
||||
title: '删除营养制剂数据',
|
||||
content:'删除此数据后可能导致系统无法正常计算结果或导致系统异常,请确定要删除该数据吗?',
|
||||
cancelText: '再想想',
|
||||
okText: '删除',
|
||||
okType: 'danger',
|
||||
onOk: () => {
|
||||
deleteById(id)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function handleSave() {
|
||||
saveData.value = {
|
||||
...saveData.value,
|
||||
created_at: undefined,
|
||||
updated_at: undefined,
|
||||
status: undefined,
|
||||
};
|
||||
if(!saveData.value.placeholder){
|
||||
saveData.value.placeholder = '入液量(ml)'
|
||||
}
|
||||
if(!saveData.value.unit){
|
||||
saveData.value.unit = 'ml'
|
||||
}
|
||||
runAsync(saveData.value).then(() => {
|
||||
refresh()
|
||||
saveData.value = undefined;
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
run()
|
||||
})
|
||||
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
</style>
|
@ -1,157 +1,164 @@
|
||||
<template>
|
||||
<Modal :open="visible" width="600px" @cancel="visible = false" :maskClosable="false" :closable="false"
|
||||
title="设置测试数据">
|
||||
<div class="setting-container">
|
||||
<div class="grid list-container grid-cols-2 gap-4 py-2 max-h-[400px] overflow-auto">
|
||||
<div class="input-item">
|
||||
<div class="title">体重</div>
|
||||
<div class="input">
|
||||
<input type="number" v-model="commonValues.weight" placeholder="单位(kg)"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-item">
|
||||
<div class="title">24h尿素</div>
|
||||
<div class="input">
|
||||
<input type="number" v-model="commonValues.niaosu" placeholder="单位(mmol)"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Tabs>
|
||||
<TabPane key="gut" tab="肠内制剂">
|
||||
<div class="grid list-container grid-cols-2 gap-4 py-2 max-h-[400px] overflow-auto">
|
||||
<div class="input-item" v-for="(it,key) in settingList.filter(s=>s.type=='gut')" :key="key">
|
||||
<div class="title">{{ it.title }}</div>
|
||||
<div class="input">
|
||||
<input type="number" v-model="it.value" :placeholder="it.placeholder"/>
|
||||
<span class="unit">{{ it.unit }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TabPane>
|
||||
<TabPane key="vein" tab="静脉制剂">
|
||||
<div class="grid list-container grid-cols-2 gap-4 py-2 max-h-[400px] overflow-auto">
|
||||
<div class="input-item" v-for="(it,key) in settingList.filter(s=>s.type=='vein')" :key="key">
|
||||
<div class="title">{{ it.title }}</div>
|
||||
<div class="input">
|
||||
<input type="number" v-model="it.value" :placeholder="it.placeholder"/>
|
||||
<span class="unit">{{ it.unit }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
<Modal :open="visible" width="600px" @cancel="visible = false" :maskClosable="false" :closable="false"
|
||||
title="设置测试数据">
|
||||
<div class="setting-container">
|
||||
<div class="grid list-container grid-cols-2 gap-4 py-2 max-h-[400px] overflow-auto">
|
||||
<div class="input-item">
|
||||
<div class="title">体重</div>
|
||||
<div class="input">
|
||||
<input type="number" v-model="commonValues.weight" placeholder="单位(kg)"/>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="flex justify-center mt-8">
|
||||
<Button key="submit" type="primary" @click="handleOk">确定</Button>
|
||||
<div class="input-item">
|
||||
<div class="title">24h尿素</div>
|
||||
<div class="input">
|
||||
<input type="number" v-model="commonValues.niaosu" placeholder="单位(mmol)"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Tabs>
|
||||
<TabPane key="gut" tab="肠内制剂">
|
||||
<div class="grid list-container grid-cols-2 gap-4 py-2 max-h-[400px] overflow-auto">
|
||||
<div class="input-item" v-for="(it,key) in settingList.filter(s=>s.type=='gut')" :key="key">
|
||||
<div class="title">{{ it.title }}</div>
|
||||
<div class="input">
|
||||
<input type="number" v-model="it.value" :placeholder="it.placeholder"/>
|
||||
<span class="unit">{{ it.unit }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Modal>
|
||||
</div>
|
||||
</TabPane>
|
||||
<TabPane key="vein" tab="静脉制剂">
|
||||
<div class="grid list-container grid-cols-2 gap-4 py-2 max-h-[400px] overflow-auto">
|
||||
<div class="input-item" v-for="(it,key) in settingList.filter(s=>s.type=='vein')" :key="key">
|
||||
<div class="title">{{ it.title }}</div>
|
||||
<div class="input">
|
||||
<input type="number" v-model="it.value" :placeholder="it.placeholder"/>
|
||||
<span class="unit">{{ it.unit }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="flex justify-center mt-8">
|
||||
<Button key="submit" type="primary" @click="handleOk">确定</Button>
|
||||
</div>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {Modal, Button, Tabs, TabPane} from 'ant-design-vue'
|
||||
import {defineComponent, onMounted, ref} from "vue";
|
||||
import {inputProducts} from "@/pages/result/test/data-input.ts";
|
||||
import {defineComponent, onMounted, ref} from "vue";
|
||||
// import {inputProducts} from "@/pages/result/test/data-input.ts";
|
||||
import {getAllProductInput} from "@/service/api/product.ts";
|
||||
|
||||
type InputValuesType = {
|
||||
[key: string]: number;
|
||||
[key: string]: number;
|
||||
}
|
||||
const inputValues = defineModel('inputValues')
|
||||
const commonValues = ref({
|
||||
weight: '',
|
||||
niaosu: '',
|
||||
weight: '',
|
||||
niaosu: '',
|
||||
})
|
||||
const visible = defineModel('visible', {type: Boolean})
|
||||
|
||||
defineComponent({
|
||||
name: 'InputSetting'
|
||||
name: 'InputSetting'
|
||||
})
|
||||
const settingList = ref<SettingItem[]>([])
|
||||
|
||||
onMounted(() => {
|
||||
const storeValues = (() => {
|
||||
const content = localStorage.getItem('yy_input_setting')
|
||||
try {
|
||||
if (!content) {
|
||||
return null;
|
||||
}
|
||||
return JSON.parse(content)
|
||||
} catch (e) {
|
||||
}
|
||||
return null;
|
||||
})();
|
||||
if (storeValues && Object.keys(storeValues).length > 0) {
|
||||
inputValues.value = storeValues
|
||||
commonValues.value = {
|
||||
weight: storeValues['weight'] && storeValues['weight'] !== 0 ? storeValues['weight'] : '',
|
||||
niaosu: storeValues['niaosu'] && storeValues['niaosu'] !== 0 ? storeValues['niaosu'] : '',
|
||||
}
|
||||
}
|
||||
const setting: SettingItem[] = []
|
||||
inputProducts.forEach(it => {
|
||||
setting.push({
|
||||
title: it.name,
|
||||
key: it.key,
|
||||
value: storeValues && storeValues[it.key] !== 0 ? storeValues[it.key] : '',
|
||||
placeholder: it.placeholder, // + (it.unit?`(${it.unit})`:'')
|
||||
type: it.type,
|
||||
unit: it.unit,
|
||||
})
|
||||
if (it.addition) {
|
||||
setting.push({
|
||||
title: it.name,
|
||||
key: it.addition.key,
|
||||
value: storeValues && storeValues[it.addition.key] !== 0 ? storeValues[it.addition.key] : '',
|
||||
placeholder: it.addition.placeholder, // + (it.unit?`(${it.unit})`:'')
|
||||
type: it.type,
|
||||
unit: it.addition.unit,
|
||||
})
|
||||
}
|
||||
})
|
||||
const lines:string[] = []
|
||||
|
||||
lines.push(`weight:体重`)
|
||||
lines.push(`niaosu:24h尿素`)
|
||||
setting.forEach(it => {
|
||||
lines.push(`${it.key}:${it.title}`)
|
||||
})
|
||||
console.log(lines.join("\n"));
|
||||
settingList.value = setting
|
||||
|
||||
initInputList().then(() => {
|
||||
console.log('init input setting success')
|
||||
})
|
||||
})
|
||||
|
||||
function handleOk() {
|
||||
const values: InputValuesType = {
|
||||
weight: Number(commonValues.value.weight || '0'),
|
||||
niaosu: Number(commonValues.value.niaosu || '0'),
|
||||
async function initInputList() {
|
||||
const storeValues = (() => {
|
||||
const content = localStorage.getItem('yy_input_setting')
|
||||
try {
|
||||
if (!content) {
|
||||
return null;
|
||||
}
|
||||
return JSON.parse(content)
|
||||
} catch (e) {
|
||||
}
|
||||
settingList.value.forEach(it => {
|
||||
values[it.key] = it.value ? Number(it.value) : 0
|
||||
return null;
|
||||
})();
|
||||
if (storeValues && Object.keys(storeValues).length > 0) {
|
||||
inputValues.value = storeValues
|
||||
commonValues.value = {
|
||||
weight: storeValues['weight'] && storeValues['weight'] !== 0 ? storeValues['weight'] : '',
|
||||
niaosu: storeValues['niaosu'] && storeValues['niaosu'] !== 0 ? storeValues['niaosu'] : '',
|
||||
}
|
||||
}
|
||||
const inputProducts = await getAllProductInput();
|
||||
const setting: SettingItem[] = []
|
||||
inputProducts.forEach(it => {
|
||||
setting.push({
|
||||
title: it.name,
|
||||
key: it.key,
|
||||
value: storeValues && storeValues[it.key] !== 0 ? storeValues[it.key] : '',
|
||||
placeholder: it.placeholder, // + (it.unit?`(${it.unit})`:'')
|
||||
type: it.type,
|
||||
unit: it.unit,
|
||||
})
|
||||
localStorage.setItem('yy_input_setting', JSON.stringify(values))
|
||||
visible.value = false
|
||||
inputValues.value = values;
|
||||
// if (it.addition) {
|
||||
// setting.push({
|
||||
// title: it.name,
|
||||
// key: it.addition.key,
|
||||
// value: storeValues && storeValues[it.addition.key] !== 0 ? storeValues[it.addition.key] : '',
|
||||
// placeholder: it.addition.placeholder, // + (it.unit?`(${it.unit})`:'')
|
||||
// type: it.type,
|
||||
// unit: it.addition.unit,
|
||||
// })
|
||||
// }
|
||||
})
|
||||
const lines: string[] = []
|
||||
|
||||
lines.push(`weight:体重`)
|
||||
lines.push(`niaosu:24h尿素`)
|
||||
setting.forEach(it => {
|
||||
lines.push(`${it.key}:${it.title}`)
|
||||
})
|
||||
// console.log(lines.join("\n"));
|
||||
settingList.value = setting
|
||||
}
|
||||
|
||||
function handleOk() {
|
||||
const values: InputValuesType = {
|
||||
weight: Number(commonValues.value.weight || '0'),
|
||||
niaosu: Number(commonValues.value.niaosu || '0'),
|
||||
}
|
||||
settingList.value.forEach(it => {
|
||||
values[it.key] = it.value ? Number(it.value) : 0
|
||||
})
|
||||
localStorage.setItem('yy_input_setting', JSON.stringify(values))
|
||||
visible.value = false
|
||||
inputValues.value = values;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.list-container {
|
||||
margin-right: -4px;
|
||||
padding-right: 4px;
|
||||
margin-right: -4px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.setting-container {
|
||||
.input {
|
||||
@apply w-full border rounded mt-1 flex items-center;
|
||||
}
|
||||
.input {
|
||||
@apply w-full border rounded mt-1 flex items-center;
|
||||
}
|
||||
|
||||
input {
|
||||
@apply flex-1 outline-0 block ml-2 py-1 mr-1;
|
||||
}
|
||||
input {
|
||||
@apply flex-1 outline-0 block ml-2 py-1 mr-1;
|
||||
}
|
||||
|
||||
.unit {
|
||||
@apply px-2 py-1 bg-gray-200 block h-full text-center;
|
||||
min-width: 34px;
|
||||
}
|
||||
.unit {
|
||||
@apply px-2 py-1 bg-gray-200 block h-full text-center;
|
||||
min-width: 34px;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -59,28 +59,28 @@ function handleSearch() {
|
||||
<div class="data-fields-container">
|
||||
<PageHeader title="登录账号管理" description="* 超级管理员: 拥有最高权限,可使用全部管理功能。管理员:普通管理权限,不可新建、删除、编辑账号。" />
|
||||
<div class="search-form">
|
||||
<Space :size="20">
|
||||
<Space class="form-item">
|
||||
<div class="grid xl:flex lg:grid-cols-4 md:grid-cols-2 grid-cols-1 w-full gap-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<span>姓名</span>
|
||||
<Input class="search-item" placeholder="请输入姓名" v-model:value="searchParams.nickname" />
|
||||
</Space>
|
||||
<Space class="form-item">
|
||||
<Input class="search-item flex-1" placeholder="请输入姓名" v-model:value="searchParams.nickname" />
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span>账号</span>
|
||||
<Input class="search-item" placeholder="请输入账号" v-model:value="searchParams.account" />
|
||||
</Space>
|
||||
<Space class="form-item">
|
||||
<Input class="search-item flex-1" placeholder="请输入账号" v-model:value="searchParams.account" />
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span>类型</span>
|
||||
<Select class="search-item" v-model:value="searchParams.role" style="width: 200px;">
|
||||
<Select class="search-item flex-1" v-model:value="searchParams.role" style="width: 200px;">
|
||||
<SelectOption v-for="(it, opIndex) in AllRoleList" :key="opIndex" :value="it.value">{{ it.label
|
||||
}}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
</Space>
|
||||
<Space class="form-item" :size="15">
|
||||
</div>
|
||||
<div class="flex items-center gap-2" :size="15">
|
||||
<Button :icon="h(SearchOutlined)" type="primary" @click="handleSearch">查询</Button>
|
||||
<Button v-if="userInfo?.role == 'root'" :icon="h(PlusOutlined)" class="btn-info" @click="editUser = { id: 0 }">新增</Button>
|
||||
</Space>
|
||||
</Space>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-result-table">
|
||||
<div>
|
||||
|
@ -70,7 +70,7 @@ function onValuesChange() {
|
||||
<Form.Item label="密码" name="password" :rules="passwordRules">
|
||||
<Input placeholder="请输入6-12位密码" type="password" v-model:value="userData.password" />
|
||||
<template #extra v-if="editUser?.id">
|
||||
<div>留空为不修改密码</div>
|
||||
<div>留空不修改密码</div>
|
||||
</template>
|
||||
</Form.Item>
|
||||
|
||||
|
@ -32,13 +32,13 @@ export const routes:RouteRecordRaw[] = [
|
||||
component: () => import('./pages/result/index.vue')
|
||||
},
|
||||
// {
|
||||
// path: 'ttt',
|
||||
// name: 'ttt',
|
||||
// path: 'input',
|
||||
// name: 'input',
|
||||
// meta: {
|
||||
// title: '输出计算',
|
||||
// icon:'icon-calculator'
|
||||
// title: '输入指标',
|
||||
// icon:'icon-add'
|
||||
// },
|
||||
// component: () => import('./pages/T.vue')
|
||||
// component: () => import('./pages/input/index.vue')
|
||||
// },
|
||||
{
|
||||
path: 'product-data',
|
||||
|
@ -18,6 +18,17 @@ export async function getList(param: ProductSearchParam) {
|
||||
await sleep(200);
|
||||
return await post<DataList<ProductInfoModel>>(`/product/all`, param);
|
||||
}
|
||||
export async function getProductDataInput(product_id: number) {
|
||||
await sleep(200);
|
||||
return await post<DataList<ProductDataInputModel>>(`/input/all`, { product_id });
|
||||
}
|
||||
export async function saveProductDataInput(data: Partial<ProductDataInputModel>) {
|
||||
await sleep(200);
|
||||
return await post<DataList<ProductDataInputModel>>(`/input/save`, data);
|
||||
}
|
||||
export async function deleteProductDataInput(id: number) {
|
||||
return await post<DataList<ProductDataInputModel>>(`/input/delete`, { id });
|
||||
}
|
||||
|
||||
export function deleteProduct(id: number) {
|
||||
return post(`/product/delete`, { id })
|
||||
@ -26,3 +37,9 @@ export async function saveProduct(params: Partial<ProductInfoModel>) {
|
||||
await sleep(200);
|
||||
return await post(`/product/save`, params);
|
||||
}
|
||||
|
||||
|
||||
export async function getAllProductInput(){
|
||||
const result = await post<DataList<ProductDataInputModel>>(`/input/all`)
|
||||
return result.list;
|
||||
}
|
@ -14,58 +14,6 @@ type InputValue = {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
{
|
||||
"weight": 50,
|
||||
"niaosu": 12,
|
||||
"ruineng": 10,
|
||||
"ruidai": 0,
|
||||
"ruixian": 0,
|
||||
"ruigao": 0,
|
||||
"ruisu": 0,
|
||||
"nengquanli": 0,
|
||||
"kangquanli": 0,
|
||||
"kangquangan": 0,
|
||||
"baipuli": 0,
|
||||
"ailunduo": 0,
|
||||
"weiwo": 0,
|
||||
"jiaweiti": 0,
|
||||
"yilijia": 0,
|
||||
"ruiyixi": 0,
|
||||
"nengquansu": 0,
|
||||
"ansu": 0,
|
||||
"quanansu": 0,
|
||||
"yilijia100": 0,
|
||||
"shui": 0,
|
||||
"anjisuan_ruye": 0,
|
||||
"yilijia100_ruye": 0,
|
||||
"quanansu_ruye": 0,
|
||||
"ansu_ruye": 0,
|
||||
"nengquansu_ruye": 0,
|
||||
"ruqingdanbailiang": 0,
|
||||
"ruianji": 0,
|
||||
"quanyingda": 0,
|
||||
"likawen": 0,
|
||||
"lefanming85": 0,
|
||||
"lefanming114": 0,
|
||||
"anjisuan": 0,
|
||||
"litai": 0,
|
||||
"lineng": 0,
|
||||
"liwen": 0,
|
||||
"youwen": 0,
|
||||
"yingtuolipite20": 0,
|
||||
"yingtuolipite30": 0,
|
||||
"putaotangluhuana5": 0,
|
||||
"putaotang5": 0,
|
||||
"putaotang10": 0,
|
||||
"putaotang50": 0,
|
||||
"luhuana09": 0,
|
||||
"luhuana10": 0,
|
||||
"geliefusi": 0,
|
||||
"luhuajia": 0,
|
||||
"baidanbai": 0
|
||||
}
|
||||
*/
|
||||
const inputList: InputValue[] = [
|
||||
{name: '体重', key: 'weight',},
|
||||
{name: '24h尿素', key: 'niaosu',},
|
||||
|
@ -54,7 +54,12 @@
|
||||
.overflow-auto{
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
th,td{
|
||||
text-align: left;
|
||||
}
|
||||
.text-center{
|
||||
text-align: center;
|
||||
}
|
||||
[type=reset],
|
||||
[type=submit],
|
||||
button,
|
||||
@ -210,12 +215,12 @@ img {
|
||||
}
|
||||
.menu-text{
|
||||
display: block;
|
||||
@include media-breakpoint-down(lg) {
|
||||
font-size: 12px;;
|
||||
}
|
||||
@include media-breakpoint-down(md) {
|
||||
display: none;
|
||||
}
|
||||
//@include media-breakpoint-down(lg) {
|
||||
// font-size: 12px;;
|
||||
//}
|
||||
//@include media-breakpoint-down(md) {
|
||||
// display: none;
|
||||
//}
|
||||
}
|
||||
|
||||
.search-form {
|
||||
@ -261,11 +266,11 @@ img {
|
||||
min-width: 0;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
text-align: left;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: middle;
|
||||
border-bottom: 1px solid #e8e8ec;
|
||||
padding: 15px 16px;
|
||||
padding: 15px 10px;
|
||||
}
|
||||
|
||||
th {
|
||||
|
25
src/types/product.d.ts
vendored
25
src/types/product.d.ts
vendored
@ -25,6 +25,28 @@ declare type ProductInfoModel = {
|
||||
updated_at: Date | string;
|
||||
status: number;
|
||||
}
|
||||
interface ProductDataInputModel {
|
||||
id: number;
|
||||
/**
|
||||
* 产品ID(ProductInfoModel.id)
|
||||
*/
|
||||
product_id: number;
|
||||
/**
|
||||
* 输入名称
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* 输入项对应变量名
|
||||
*/
|
||||
key: string;
|
||||
type?: string;
|
||||
placeholder: string;
|
||||
unit: string;
|
||||
|
||||
created_at: Date | string;
|
||||
updated_at: Date | string;
|
||||
status: number;
|
||||
}
|
||||
|
||||
declare type ResultExpression = {
|
||||
id: number;
|
||||
@ -63,4 +85,5 @@ interface ResultItem {
|
||||
title:string;
|
||||
key:string;
|
||||
value:number;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ const themeConfig = {
|
||||
export default {
|
||||
content: [
|
||||
'./index.html',
|
||||
'./src/**/*.{mjs,js,ts,jsx,tsx,html,vue}'
|
||||
'./src/**/*.{mjs,js,ts,jsx,tsx,html,vue,scss}'
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
|
@ -6,7 +6,7 @@ console.log(
|
||||
['NODE', 'NODE_ENV', 'npm_execpath', 'OS'].map(key => (process.env[key]))
|
||||
)
|
||||
const devServer = {
|
||||
default: 'http://localhost:3001',
|
||||
default: 'http://localhost:23002',
|
||||
test: 'http://43.136.175.109:50001'
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user