Browse Source

feat: 新增二期相关界面

sunxiao 1 week ago
parent
commit
fb2b7a552a

+ 14 - 0
.env.test

@@ -0,0 +1,14 @@
+# 页面标题
+VITE_APP_TITLE = 智能国标化验室数据平台
+
+# 生产环境配置
+VITE_APP_ENV = 'production'
+
+# 管理系统/生产环境
+VITE_APP_BASE_API = 'http://192.168.40.21:8889'
+
+# 是否在打包时开启压缩,支持 gzip 和 brotli
+VITE_BUILD_COMPRESS = gzip
+
+VITE_APP_BASE_TEST = http://10.0.0.28:8080/
+VITE_APP_BASE_PROD = http://192.168.9.54:8080/

+ 3 - 3
package.json

@@ -1,14 +1,14 @@
 {
   "name": "ruoyi",
   "version": "3.8.8",
-  "description": "若依管理系统",
-  "author": "若依",
+  "description": "智能国标化验室数据平台",
+  "author": "sunXiao",
   "license": "MIT",
   "type": "module",
   "scripts": {
     "dev": "vite",
     "build:prod": "vite build --mode production",
-    "build:test": "vite build --mode development",
+    "build:test": "vite build --mode test",
     "preview": "vite preview",
     "deploy:prod": "node deploy/index.js --prod",
     "deploy:test": "node deploy/index.js --test"

+ 3 - 2
src/api/client/manage.js

@@ -55,10 +55,11 @@ export function getAllWaterFactoryList(params) {
 }
 
 // 设备管理 - list - 全部水厂
-export function getAllDeviceList() {
+export function getAllDeviceList(params) {
   return request({
     url: '/business/device/all',
-    method: 'get'
+    method: 'get',
+    params
   })
 }
 

+ 3 - 3
src/views/client/device/index.vue

@@ -110,8 +110,8 @@ const onChangeTag= (row) => {
       cancelButtonText: '取消',
       type: 'warning',
     }
-  ).then(() => {
-    putDevice({ deviceId: row.deviceId, deviceStatus: row.deviceStatus == 0 ? 1 : 0 });
+  ).then(async() => {
+    await putDevice({ deviceId: row.deviceId, deviceStatus: row.deviceStatus == 0 ? 1 : 0 });
     initPageData();
     proxy.$modal.msgSuccess("状态变更成功");
   }).catch(() => {})
@@ -200,7 +200,7 @@ onMounted(() => {
       <div class="flex justify-between items-center mb-[10px]">
         <el-button type="primary" @click="dialogVisible = true" :loading="loading" :icon="CirclePlus">新增设备</el-button>
       </div>
-      <el-table :data="tableData" style="width: 100%" v-loading="tableLoading" row-key="id" border>
+      <el-table :data="tableData" style="width: 100%" v-loading="loading" row-key="id" border>
         <el-table-column prop="deviceName" label="设备名称" width="200" fixed/>
         <el-table-column prop="deviceSn" label="设备SN" width="180" />
         <el-table-column prop="deviceModel" label="设备型号" width="180"/>

+ 99 - 23
src/views/configuration/flow/index.vue

@@ -1,6 +1,6 @@
 <script setup>
 import { CirclePlus } from '@element-plus/icons-vue'
-
+import { ElMessageBox } from 'element-plus'
 import { getWorkFlowList, getAllWaterFactoryList, getPositionList, getAssayList, putWorkFlow, postWorkFlow, delWorkFlow } from '@/api/configuration'
 import { getAllDeviceList} from '@/api/client/manage'
 
@@ -20,6 +20,8 @@ const deviceOptions = ref([]);
 const pointOptions = ref([]);
 const assayOptions = ref([]);
 
+const assayItems = ref([]);
+
 const formData = ref({});
 const formRef = ref(null);
 
@@ -30,7 +32,24 @@ const rules = {
   code: { required: true, message: '请输入化验流程编号', trigger: 'blur' },
   deviceId: { required: true, message: '请选择所属设备', trigger: 'blur' },
   totalSteps: { required: true, message: '化验总步数', trigger: 'blur' },
-  items: { type: 'array', required: true, message: '请添加化验内容', trigger: 'blur' }
+  items: { validator: (rule, any, callback) => {
+    if ( !assayItems.value.length ) {
+      callback(new Error('请添加化验内容'))
+    }
+    callback();
+  }, trigger: 'blur'}
+}
+
+// 设备 - list
+const initDeviceOptions = (deviceWorks) => {
+  deviceOptions.value = [];
+  getAllDeviceList({ deviceWorks }).then(({ data }) => deviceOptions.value = data)
+}
+
+// 点位 - list
+const initPositonOptions = (params = {}) => {
+  pointOptions.value = [];
+  getPositionList({ pageNum: 1, pageSize: 10000, ...params }).then(({ rows }) => pointOptions.value = rows)
 }
 
 const initPageData = async () => {
@@ -42,9 +61,9 @@ const initPageData = async () => {
     ...val,
     items: val.items.map(item => ({
       ...item,
-      itemId: item.id,
+      itemId: item.itemId,
       itemName: item.assayItem?.name,
-      positionId: item.position?.id,
+      positionId: item.positionId,
       positionName: item.position?.name,
       workflowId: val.id
     }))
@@ -72,6 +91,9 @@ const handleReset = () => {
 // 编辑流程
 const handleTableEdit = (row) => {
   formData.value = { ...row };
+  assayItems.value = [...row.items ];
+  initDeviceOptions(row?.organization?.id);
+  initPositonOptions({deviceId: row?.bizDevice.deviceId});
   dialogVisible.value = true;
 }
 
@@ -80,7 +102,8 @@ const handleDialogConfirm = async () => {
   if (!formRef.value) return
   await formRef.value.validate(async(valid, fields) => {
     if (valid) {
-      formData.value.id ? await putWorkFlow(formData.value) : await postWorkFlow(formData.value);
+      const parmas = { ...formData.value, items: assayItems.value };
+      formData.value.id ? await putWorkFlow(parmas) : await postWorkFlow(parmas);
       formRef.value.resetFields();
       dialogVisible.value = false;
       proxy.$modal.msgSuccess("操作成功");
@@ -94,19 +117,27 @@ const handleDialogConfirm = async () => {
 // Dialog - 重置表单
 const handleDialogReset = () => {
   formData.value = {};
+  deviceOptions.value = [];
+  assayItems.value = [];
   formRef.value.resetFields();
 }
 
 // Dialog - 新增行
 const handleAddRow = () => {
-  const { itemId, positionId, id: workflowId } = formData.value;
-  if ( !itemId ) {
-    return proxy.$modal.msgError("请先选择化验项目");
+  const { organizationId, deviceId, itemId, positionId, id: workflowId } = formData.value;
+  if (!organizationId) {
+    return proxy.$modal.msgError("请先选择所属水厂");
+  }
+  if (!deviceId) {
+    return proxy.$modal.msgError("请先选择所属设备");
   }
   if ( !positionId ) {
     return proxy.$modal.msgError("请先选择取样点位");
   }
-  formRef.value.validate("items");
+  if ( !itemId ) {
+    return proxy.$modal.msgError("请先选择化验项目");
+  }
+  formRef.value.clearValidate("items");
   const params = {
     itemId,
     itemName: assayOptions.value.find(({ id }) => id === itemId).name,
@@ -114,8 +145,7 @@ const handleAddRow = () => {
     positionName: pointOptions.value.find(({ id }) => id === positionId).name,
     workflowId
   }
-  if ( !formData.value.items?.length ) formData.value.items = [];
-  formData.value.items.push(params);
+  assayItems.value.push(params);
 }
 
 // Table - 删除
@@ -128,18 +158,63 @@ const handleDelete = async ({ id }) => {
 
 // Tag - 删除行
 const handleCloseTag = (index) => {
-  formData.value.items.splice(index, 1);
+  assayItems.value.splice(index, 1);
+}
+
+// Dialog - 选择水厂 - change
+const onWaterFactorySelectChange = (deviceWorks) => {
+  if ( assayItems.value.length > 0 ) {
+    ElMessageBox.confirm(
+      '切换水厂后将清空化验内容,是否切换?',
+      '提示',
+      {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      }
+    ).then(() => {
+      formData.value.positionId = '';
+      formData.value.itemId = '';
+      assayItems.value = [];
+      formData.value.deviceId = '';
+      pointOptions.value = [];
+      initDeviceOptions(deviceWorks);
+    })
+  } else {
+    assayItems.value = [];
+    formData.value.deviceId = '';
+    initDeviceOptions(deviceWorks);
+  }
+}
+
+// Dialog - 选择设备 - change
+const onDeviceSelectChange = (deviceId) => {
+  if ( assayItems.value?.length > 0 ) {
+    ElMessageBox.confirm(
+      '切换设备后将清空化验内容,是否切换?',
+      '提示',
+      {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      }
+    ).then(() => {
+      formData.value.positionId = '';
+      formData.value.itemId = '';
+      assayItems.value = [];
+      initPositonOptions({ deviceId })
+    })
+  } else {
+    assayItems.value = [];
+    initPositonOptions({ deviceId })
+  }
 }
 
 onMounted(async () => {
   // 水厂
   getAllWaterFactoryList().then(({ data }) => waterFactoryOptions.value = data)
-  // 设备
-  getAllDeviceList().then(({ data }) => deviceOptions.value = data)
   // 化验项目
   getAssayList({ pageNum: 1, pageSize: 9999 }).then(({ rows }) => assayOptions.value = rows)
-  // 点位
-  getPositionList({ pageNum: 1, pageSize: 10000 }).then(({ rows }) => pointOptions.value = rows)
   // 初始化数据
   initPageData();
 })
@@ -233,7 +308,7 @@ onMounted(async () => {
           </el-col>
           <el-col :span="11">
             <el-form-item label="所属水厂" prop="organizationId">
-              <el-select v-model="formData.organizationId" placeholder="请选择所属水厂" filterable clearable>
+              <el-select v-model="formData.organizationId" placeholder="请选择所属水厂" filterable clearable @change="onWaterFactorySelectChange">
                 <el-option v-for="item in waterFactoryOptions" :key="item.id" :label="item.name" :value="item.id" />
               </el-select>
             </el-form-item>
@@ -245,7 +320,7 @@ onMounted(async () => {
           </el-col>
           <el-col :span="11">
             <el-form-item label="所属设备" prop="deviceId">
-              <el-select v-model="formData.deviceId" placeholder="请选所属设备" filterable clearable>
+              <el-select v-model="formData.deviceId" placeholder="请选所属设备" filterable clearable @change="onDeviceSelectChange">
                 <el-option v-for="item in deviceOptions" :key="item.deviceId" :label="item.deviceName" :value="item.deviceId" />
               </el-select>
             </el-form-item>
@@ -262,13 +337,14 @@ onMounted(async () => {
         <el-form-item label="化验内容" prop="items">
           <el-row class="w-full" :gutter="20">
             <el-col :span="8">
-              <el-select v-model="formData.itemId" placeholder="请选择化验项目" filterable clearable>
-                <el-option v-for="item in assayOptions" :key="item.id" :label="item.name" :value="item.id" />
+              <el-select v-model="formData.positionId" placeholder="请选择取样点位" filterable clearable>
+                <el-option v-for="item in pointOptions" :key="item.id" :label="item.name" :value="item.id" />
               </el-select>
             </el-col>
             <el-col :span="8">
-              <el-select v-model="formData.positionId" placeholder="请选择取样点位" filterable clearable>
-                <el-option v-for="item in pointOptions" :key="item.id" :label="item.name" :value="item.id" />
+              {{ formData.itemId }}
+              <el-select v-model="formData.itemId" placeholder="请选择化验项目" filterable clearable>
+                <el-option v-for="item in assayOptions" :key="item.id" :label="item.name" :value="item.id" />
               </el-select>
             </el-col>
             <el-col :span="8">
@@ -276,7 +352,7 @@ onMounted(async () => {
             </el-col>
           </el-row>
           <div class="w-full min-h-[100px] px-[10px] py-[10px] mt-[30px] bg-[#f3f5f9] space-x-[10px]">
-            <el-tag v-for="item, index in formData.items" :key="index" closable type="primary" @close="handleCloseTag(index)">
+            <el-tag v-for="item, index in assayItems" :key="index" closable type="primary" @close="handleCloseTag(index)">
               <span>{{ item.itemName }}({{ item.positionName }})</span>
             </el-tag>
           </div>

+ 20 - 6
src/views/configuration/point/index.vue

@@ -1,6 +1,5 @@
 <script setup>
-import { CirclePlus, Edit, Search, Share, Upload } from '@element-plus/icons-vue'
-import { ElMessage, ElMessageBox } from 'element-plus'
+import { CirclePlus } from '@element-plus/icons-vue'
 import { getPositionList, getAllWaterFactoryList, postPosition, putPosition, delPosition } from '@/api/configuration'
 import { getAllDeviceList} from '@/api/client/manage'
 
@@ -30,9 +29,17 @@ const rules = {
   code: { required: true, message: '请输入取样点位编号', trigger: 'blur' }
 }
 
+
+// 设备 - list
+const initDeviceOptions = (deviceWorks) => {
+  deviceOptions.value = [];
+  getAllDeviceList({ deviceWorks }).then(({ data }) => deviceOptions.value = data)
+}
+
 // Table - 编辑
 const handleEdit = (row) => {
   formData.value = { ...row };
+  initDeviceOptions(row.organization.id);
   dialogVisible.value = true;
 }
 
@@ -43,6 +50,12 @@ const handleDelete = async ({ id }) => {
   initPageData();
 }
 
+// Dialog - 选择水厂 - change
+const onWaterFactorySelectChange = (deviceWorks) => {
+  formData.value.deviceId = '';
+  initDeviceOptions(deviceWorks);
+}
+
 const initPageData = async () => {
   loading.value = true;
 
@@ -93,13 +106,14 @@ const handleDialogConfirm = async () => {
 
 // Dialog - 重置表单
 const handleDialogReset = () => {
-  formRef.value.resetFields();
   formData.value = {};
+  formRef.value.resetFields();
+  deviceOptions.value = [];
 }
 
 onMounted(() => {
   getAllWaterFactoryList().then(({ data }) => waterFactoryOptions.value = data);
-  getAllDeviceList().then(({ data }) => deviceOptions.value = data);
+  
   initPageData();
 })
 </script>
@@ -140,7 +154,7 @@ onMounted(() => {
       <div class="flex justify-between items-center mb-[10px]">
         <el-button type="primary" @click="dialogVisible = true" :loading="loading" :icon="CirclePlus">新增点位</el-button>
       </div>
-      <el-table :data="tableData" style="width: 100%" v-loading="tableLoading" row-key="id">
+      <el-table :data="tableData" style="width: 100%" v-loading="loading" row-key="id">
         <el-table-column prop="organization.name" label="所属水厂" width="220" fixed/>
         <el-table-column prop="bizDevice.deviceName" label="所属设备SN" />
         <el-table-column prop="name" label="取样点位名称"/>
@@ -173,7 +187,7 @@ onMounted(() => {
         <el-row justify="space-between">
           <el-col :span="24">
             <el-form-item label="所属水厂" prop="organizationId">
-              <el-select v-model="formData.organizationId" placeholder="请选择所属水厂" filterable clearable>
+              <el-select v-model="formData.organizationId" placeholder="请选择所属水厂" filterable clearable @change="onWaterFactorySelectChange">
                 <el-option v-for="item in waterFactoryOptions" :key="item.id" :label="item.name" :value="item.id" />
               </el-select>
             </el-form-item>

+ 12 - 7
src/views/configuration/sample/index.vue

@@ -1,6 +1,5 @@
 <script setup>
 import { CirclePlus } from '@element-plus/icons-vue'
-import { ElMessage, ElMessageBox } from 'element-plus'
 import { getSampleList, getAllWaterFactoryList, postSample, putSample, getAssayList, delSample, delWorkFlow } from '@/api/configuration'
 
 const { proxy } = getCurrentInstance();
@@ -25,14 +24,16 @@ const rules = {
   deviceWorks: {required: true, message: '请选择所属水厂', trigger: 'blur' },
   assayItem: {required: true, message: '请选择化验项目', trigger: 'blur' },
   resultValue: {required: true, message: '请输入质控值', trigger: 'blur' },
-  // remark: {required: true, message: '请输入浮动率', trigger: 'blur' },
+  // lowValue: {required: true, message: '请输入浮动率', trigger: 'blur' },
+  highValue: {required: true, message: '请输入上限值', trigger: 'blur' },
+  lowValue: {required: true, message: '请输入下限值', trigger: 'blur' },
   time: [
     { type: 'array', required: true, message: '请选择有效日期', trigger: 'change', }
   ]
 }
 
 const formatNumber = (num) => {
-  return (num == 0 || num) ? Number(num).toFixed(2) : '';
+  return (num == 0 || num) ? Number(Number(num).toFixed(2)) : '';
 }
 
 const handleInpChange = () => {
@@ -40,6 +41,8 @@ const handleInpChange = () => {
   const isExistsValue = !!(resultValue && remark);
   formData.value.highValue = isExistsValue ? formatNumber(resultValue + ( resultValue * (remark / 100) )) : highValue;
   formData.value.lowValue = isExistsValue ?  formatNumber(resultValue - ( resultValue * (remark / 100) )) : lowValue;
+  formRef.value.validateField("highValue");
+  formRef.value.validateField("lowValue");
 }
 
 const initPageData = async () => {
@@ -53,6 +56,8 @@ const initPageData = async () => {
       ...item,
       lowValue: formatNumber(item.lowValue),
       highValue: formatNumber(item.highValue),
+      remark: item.remark ? Number(item.remark) : 0,
+      remark: item.remark ? Number(item.remark) : 0
     })
   })
 
@@ -207,14 +212,14 @@ onMounted(() => {
           </el-col>
           <el-col :span="24">
             <el-form-item label="质控值" prop="resultValue">
-              <el-input-number v-model="formData.resultValue" :min="1" style="width: 320px;" placeholder="请输入质控值" @change="handleInpChange"/> 
+              <el-input-number v-model="formData.resultValue" style="width: 320px;" placeholder="请输入质控值" @blur="handleInpChange"/> 
             </el-form-item>
           </el-col>
           <el-col :span="24">
             <el-form-item label="浮动率" prop="remark">
               <div>
                 <div class="space-x-[10px]">
-                  <el-input-number v-model="formData.remark" :min="0" style="width: 320px;" placeholder="请输入浮动率" @change="handleInpChange"/> 
+                  <el-input-number v-model="formData.remark" style="width: 320px;" placeholder="请输入浮动率" @blur="handleInpChange"/> 
                   <span>%</span>
                 </div>
                 <span class="text-[#999] text-[12px]">根据浮动率,自动计算上限值和下限值。四舍五入保留2为小数。</span>
@@ -223,12 +228,12 @@ onMounted(() => {
           </el-col>
           <el-col :span="24">
             <el-form-item label="上限值" prop="highValue">
-              <el-input-number v-model="formData.highValue" :min="1" style="width: 320px;" placeholder="请输入上限值"/> 
+              <el-input-number v-model="formData.highValue" style="width: 320px;" placeholder="请输入上限值"/> 
             </el-form-item>
           </el-col>
           <el-col :span="24">
             <el-form-item label="下限值" prop="lowValue">
-              <el-input-number v-model="formData.lowValue" :min="1" style="width: 320px;" placeholder="请输入上限值"/> 
+              <el-input-number v-model="formData.lowValue" style="width: 320px;" placeholder="请输入上限值"/> 
             </el-form-item>
           </el-col>
           <el-col :span="24">

+ 1 - 1
vite.config.js

@@ -11,7 +11,7 @@ export default defineConfig(({ mode, command }) => {
     // 部署生产环境和开发环境下的URL。
     // 默认情况下,vite 会假设你的应用是被部署在一个域名的根路径上
     // 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。
-    base: VITE_APP_ENV === 'production' ? '' : '',
+    base: VITE_APP_ENV === 'production' ? '/data/' : '',
     plugins: createVitePlugins(env, command === 'build'),
     resolve: {
       // https://cn.vitejs.dev/config/#resolve-alias