tuqin
2026-10-30 b4cf81d9f299e55472bc2e2b7165a66da8211760
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
<template>
  <div class="app-container">
    <el-tabs v-model="activeTab" type="border-card">
      <!-- ==================== Tab1: 检测项目 ==================== -->
      <el-tab-pane label="检测项目库" name="testItem">
        <div class="search_form">
          <div>
            <span class="search_title">项目编号:</span>
            <el-input v-model="itemSearch.itemCode" style="width: 180px" placeholder="请输入" clearable @change="loadItem" />
            <span class="search_title" style="margin-left: 16px">项目名称:</span>
            <el-input v-model="itemSearch.itemName" style="width: 180px" placeholder="请输入" clearable @change="loadItem" />
            <span class="search_title" style="margin-left: 16px">类型:</span>
            <el-select v-model="itemSearch.itemType" style="width: 140px" placeholder="全部" clearable @change="loadItem">
              <el-option label="化学分析" value="chemical" /><el-option label="物理测试" value="physical" />
              <el-option label="微生物" value="microbial" /><el-option label="其它" value="other" />
            </el-select>
            <el-button type="primary" style="margin-left: 10px" @click="loadItem">搜索</el-button>
          </div>
          <div>
            <el-button type="primary" @click="openItemForm('add')">新增项目</el-button>
            <el-button type="danger" plain @click="delItem">删除</el-button>
          </div>
        </div>
        <PIMTable
          rowKey="id"
          :column="itemColumns"
          :tableData="itemData"
          :page="itemPage"
          :isSelection="true"
          :tableLoading="itemLoading"
          @selection-change="(v) => (itemSelected = v)"
          @pagination="paginationItem"
        />
      </el-tab-pane>
 
      <!-- ==================== Tab2: 计量单位 ==================== -->
      <el-tab-pane label="计量单位库" name="unit">
        <div class="search_form">
          <div>
            <span class="search_title">单位编码:</span>
            <el-input v-model="unitSearch.unitCode" style="width: 160px" placeholder="请输入" clearable @change="loadUnit" />
            <span class="search_title" style="margin-left: 16px">单位名称:</span>
            <el-input v-model="unitSearch.unitName" style="width: 160px" placeholder="请输入" clearable @change="loadUnit" />
            <span class="search_title" style="margin-left: 16px">类型:</span>
            <el-select v-model="unitSearch.unitType" style="width: 150px" placeholder="全部" clearable @change="loadUnit">
              <el-option label="重量" value="weight" /><el-option label="体积" value="volume" />
              <el-option label="长度" value="length" /><el-option label="温度" value="temperature" />
              <el-option label="浓度" value="concentration" /><el-option label="计数" value="count" />
            </el-select>
            <el-button type="primary" style="margin-left: 10px" @click="loadUnit">搜索</el-button>
          </div>
          <div>
            <el-button type="primary" @click="openUnitForm('add')">新增单位</el-button>
            <el-button type="danger" plain @click="delUnit">删除</el-button>
          </div>
        </div>
        <PIMTable
          rowKey="id"
          :column="unitColumns"
          :tableData="unitData"
          :page="unitPage"
          :isSelection="true"
          :tableLoading="unitLoading"
          @selection-change="(v) => (unitSelected = v)"
          @pagination="paginationUnit"
        />
      </el-tab-pane>
 
      <!-- ==================== Tab3: 判定标准 ==================== -->
      <el-tab-pane label="判定标准库" name="standard">
        <div class="search_form">
          <div>
            <span class="search_title">标准编号:</span>
            <el-input v-model="stdSearch.standardCode" style="width: 180px" placeholder="请输入" clearable @change="loadStd" />
            <span class="search_title" style="margin-left: 16px">标准名称:</span>
            <el-input v-model="stdSearch.standardName" style="width: 180px" placeholder="请输入" clearable @change="loadStd" />
            <el-button type="primary" style="margin-left: 10px" @click="loadStd">搜索</el-button>
          </div>
          <div>
            <el-button type="primary" @click="openStdForm('add')">新增标准</el-button>
            <el-button type="danger" plain @click="delStd">删除</el-button>
          </div>
        </div>
        <PIMTable
          rowKey="id"
          :column="stdColumns"
          :tableData="stdData"
          :page="stdPage"
          :isSelection="true"
          :tableLoading="stdLoading"
          @selection-change="(v) => (stdSelected = v)"
          @pagination="paginationStd"
        />
      </el-tab-pane>
 
      <!-- ==================== Tab4: 系统参数 ==================== -->
      <el-tab-pane label="系统参数" name="setting">
        <div class="search_form">
          <div>
            <span class="search_title">分类:</span>
            <el-select v-model="setSearch.category" style="width: 150px" placeholder="全部" clearable @change="loadSetting">
              <el-option label="采集" value="collect" /><el-option label="清洗" value="clean" />
              <el-option label="预警" value="alert" /><el-option label="安全" value="security" />
              <el-option label="样品" value="sample" /><el-option label="试剂" value="reagent" /><el-option label="界面" value="ui" />
            </el-select>
            <span class="search_title" style="margin-left: 16px">参数名:</span>
            <el-input v-model="setSearch.settingName" style="width: 180px" placeholder="请输入" clearable @change="loadSetting" />
            <el-button type="primary" style="margin-left: 10px" @click="loadSetting">搜索</el-button>
          </div>
          <div>
            <el-button type="primary" @click="openSetForm('add')">新增参数</el-button>
            <el-button @click="loadSetting">刷新</el-button>
          </div>
        </div>
        <PIMTable rowKey="id" :column="setColumns" :tableData="setData" :page="setPage"
          :tableLoading="setLoading" @pagination="paginationSet" />
      </el-tab-pane>
 
      <!-- ==================== Tab5: 审计日志 ==================== -->
      <el-tab-pane label="审计日志" name="auditLog">
        <div class="search_form">
          <div>
            <span class="search_title">模块:</span>
            <el-input v-model="auditSearch.module" style="width: 150px" placeholder="如 样品管理" clearable @change="loadAudit" />
            <span class="search_title" style="margin-left: 16px">动作:</span>
            <el-select v-model="auditSearch.action" style="width: 150px" placeholder="全部" clearable @change="loadAudit">
              <el-option label="新增" value="create" /><el-option label="修改" value="update" />
              <el-option label="删除" value="delete" /><el-option label="出库" value="stock_out" />
              <el-option label="入库" value="stock_in" /><el-option label="分配资源" value="assign_resource" />
              <el-option label="处理告警" value="handle_alert" />
            </el-select>
            <span class="search_title" style="margin-left: 16px">操作人:</span>
            <el-input v-model="auditSearch.operator" style="width: 140px" placeholder="请输入" clearable @change="loadAudit" />
            <el-button type="primary" style="margin-left: 10px" @click="loadAudit">搜索</el-button>
          </div>
          <div>
            <el-button @click="loadAudit">刷新</el-button>
          </div>
        </div>
        <PIMTable rowKey="id" :column="auditColumns" :tableData="auditData" :page="auditPage"
          :tableLoading="auditLoading" @pagination="paginationAudit" />
      </el-tab-pane>
    </el-tabs>
 
    <!-- ==================== 检测项目表单 ==================== -->
    <el-dialog v-model="itemDialogVisible" :title="itemOpType === 'add' ? '新增检测项目' : '编辑检测项目'" width="70%">
      <el-form ref="itemFormRef" :model="itemForm" label-position="top" :rules="itemRules">
        <el-row :gutter="24">
          <el-col :span="12"><el-form-item label="项目编号" prop="itemCode"><el-input v-model="itemForm.itemCode" placeholder="如 PH、COD、SS" /></el-form-item></el-col>
          <el-col :span="12"><el-form-item label="项目名称" prop="itemName"><el-input v-model="itemForm.itemName" placeholder="如 pH值、化学需氧量" /></el-form-item></el-col>
          <el-col :span="12">
            <el-form-item label="类型">
              <el-select v-model="itemForm.itemType" style="width: 100%">
                <el-option label="化学分析" value="chemical" /><el-option label="物理测试" value="physical" />
                <el-option label="微生物" value="microbial" /><el-option label="其它" value="other" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12"><el-form-item label="计量单位"><el-input v-model="itemForm.unit" placeholder="如 mg/L、%" /></el-form-item></el-col>
          <el-col :span="12"><el-form-item label="检测方法"><el-input v-model="itemForm.method" placeholder="如 玻璃电极法" /></el-form-item></el-col>
          <el-col :span="12"><el-form-item label="执行标准"><el-input v-model="itemForm.standard" placeholder="如 GB/T 6920-1986" /></el-form-item></el-col>
          <el-col :span="12"><el-form-item label="合格下限"><el-input-number v-model="itemForm.limitMin" :precision="4" style="width: 100%" /></el-form-item></el-col>
          <el-col :span="12"><el-form-item label="合格上限"><el-input-number v-model="itemForm.limitMax" :precision="4" style="width: 100%" /></el-form-item></el-col>
          <el-col :span="24"><el-form-item label="备注"><el-input v-model="itemForm.remark" type="textarea" :rows="2" /></el-form-item></el-col>
        </el-row>
      </el-form>
      <template #footer>
        <el-button @click="itemDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitItem">确定</el-button>
      </template>
    </el-dialog>
 
    <!-- ==================== 计量单位表单 ==================== -->
    <el-dialog v-model="unitDialogVisible" :title="unitOpType === 'add' ? '新增计量单位' : '编辑计量单位'" width="600px">
      <el-form ref="unitFormRef" :model="unitForm" label-position="top" :rules="unitRules">
        <el-row :gutter="16">
          <el-col :span="12"><el-form-item label="单位编码" prop="unitCode"><el-input v-model="unitForm.unitCode" placeholder="如 mg" /></el-form-item></el-col>
          <el-col :span="12"><el-form-item label="单位名称" prop="unitName"><el-input v-model="unitForm.unitName" placeholder="如 毫克" /></el-form-item></el-col>
          <el-col :span="12">
            <el-form-item label="类型">
              <el-select v-model="unitForm.unitType" style="width: 100%">
                <el-option label="重量" value="weight" /><el-option label="体积" value="volume" />
                <el-option label="长度" value="length" /><el-option label="温度" value="temperature" />
                <el-option label="浓度" value="concentration" /><el-option label="计数" value="count" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12"><el-form-item label="基准单位"><el-input v-model="unitForm.baseUnit" placeholder="如 g(重量基准)" /></el-form-item></el-col>
          <el-col :span="12"><el-form-item label="换算系数"><el-input-number v-model="unitForm.conversionRate" :precision="6" :min="0" style="width: 100%" /></el-form-item></el-col>
          <el-col :span="24"><el-form-item label="备注"><el-input v-model="unitForm.remark" type="textarea" :rows="2" /></el-form-item></el-col>
        </el-row>
      </el-form>
      <template #footer>
        <el-button @click="unitDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitUnit">确定</el-button>
      </template>
    </el-dialog>
 
    <!-- ==================== 判定标准表单 ==================== -->
    <el-dialog v-model="stdDialogVisible" :title="stdOpType === 'add' ? '新增判定标准' : '编辑判定标准'" width="70%">
      <el-form ref="stdFormRef" :model="stdForm" label-position="top" :rules="stdRules">
        <el-row :gutter="24">
          <el-col :span="12"><el-form-item label="标准编号" prop="standardCode"><el-input v-model="stdForm.standardCode" placeholder="请输入" /></el-form-item></el-col>
          <el-col :span="12"><el-form-item label="标准名称" prop="standardName"><el-input v-model="stdForm.standardName" placeholder="请输入" /></el-form-item></el-col>
          <el-col :span="12">
            <el-form-item label="关联检测项目">
              <el-select v-model="stdForm.itemId" style="width: 100%" placeholder="请选择" filterable clearable @change="onStdItemChange">
                <el-option v-for="i in itemOptions" :key="i.id" :label="`${i.itemName}(${i.itemCode})`" :value="i.id" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="判定类型">
              <el-select v-model="stdForm.standardType" style="width: 100%">
                <el-option label="区间(下限~上限)" value="range" /><el-option label="不低于" value="min" />
                <el-option label="不高于" value="max" /><el-option label="等于" value="equal" />
                <el-option label="目标值±偏差" value="target" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="8"><el-form-item label="下限"><el-input-number v-model="stdForm.minValue" :precision="4" style="width: 100%" /></el-form-item></el-col>
          <el-col :span="8"><el-form-item label="上限"><el-input-number v-model="stdForm.maxValue" :precision="4" style="width: 100%" /></el-form-item></el-col>
          <el-col :span="8"><el-form-item label="单位"><el-input v-model="stdForm.unit" placeholder="如 mg/L" /></el-form-item></el-col>
          <el-col :span="8"><el-form-item label="目标值"><el-input-number v-model="stdForm.targetValue" :precision="4" style="width: 100%" /></el-form-item></el-col>
          <el-col :span="8"><el-form-item label="允许偏差"><el-input-number v-model="stdForm.deviation" :precision="4" style="width: 100%" /></el-form-item></el-col>
          <el-col :span="8"><el-form-item label="依据标准"><el-input v-model="stdForm.sourceStandard" placeholder="如 GB 5749-2022" /></el-form-item></el-col>
          <el-col :span="24"><el-form-item label="备注"><el-input v-model="stdForm.remark" type="textarea" :rows="2" /></el-form-item></el-col>
        </el-row>
      </el-form>
      <template #footer>
        <el-button @click="stdDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitStd">确定</el-button>
      </template>
    </el-dialog>
  </div>
 
    <!-- ===== 系统参数编辑 ===== -->
    <el-dialog v-model="setDialogVisible" :title="setOpType === 'add' ? '新增参数' : '编辑参数'" width="600px">
      <el-form :model="setForm" label-position="top">
        <el-row :gutter="16">
          <el-col :span="12"><el-form-item label="分类"><el-input v-model="setForm.category" placeholder="collect/clean/alert/security" /></el-form-item></el-col>
          <el-col :span="12"><el-form-item label="参数键"><el-input v-model="setForm.settingKey" /></el-form-item></el-col>
          <el-col :span="12"><el-form-item label="参数名"><el-input v-model="setForm.settingName" /></el-form-item></el-col>
          <el-col :span="12"><el-form-item label="参数值"><el-input v-model="setForm.settingValue" /></el-form-item></el-col>
          <el-col :span="12">
            <el-form-item label="值类型">
              <el-select v-model="setForm.valueType" style="width: 100%">
                <el-option label="字符串" value="string" /><el-option label="数字" value="number" />
                <el-option label="布尔" value="boolean" /><el-option label="下拉" value="select" />
                <el-option label="JSON" value="json" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12"><el-form-item label="默认值"><el-input v-model="setForm.defaultValue" /></el-form-item></el-col>
          <el-col :span="24"><el-form-item label="说明"><el-input v-model="setForm.description" type="textarea" :rows="2" /></el-form-item></el-col>
        </el-row>
      </el-form>
      <template #footer>
        <el-button @click="setDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitSet">确定</el-button>
      </template>
    </el-dialog>
</template>
 
<script setup>
import { ref, reactive, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { listTestItem, getTestItem, addTestItem, updateTestItem, delTestItem } from "@/api/lims/testItem";
import { listUnit, getUnit, addUnit, updateUnit, delUnit as apiDelUnit } from "@/api/lims/unit";
import {
  listJudgmentStandard, getJudgmentStandard, addJudgmentStandard, updateJudgmentStandard, delJudgmentStandard,
} from "@/api/lims/judgmentStandard";
import { listSetting, getSetting, addSetting, updateSetting, delSetting } from "@/api/lims/setting";
import { listAuditLog } from "@/api/lims/auditLog";
 
const activeTab = ref("testItem");
const itemOptions = ref([]);
 
/* ==================== 检测项目 ==================== */
const itemSearch = ref({});
const itemData = ref([]);
const itemSelected = ref([]);
const itemLoading = ref(false);
const itemPage = reactive({ pageNum: 1, pageSize: 10, total: 0 });
 
const itemColumns = ref([
  { label: "项目编号", prop: "itemCode", width: 130 },
  { label: "项目名称", prop: "itemName", width: 200 },
  { label: "类型", prop: "itemType", width: 110 },
  { label: "单位", prop: "unit", width: 90 },
  { label: "检测方法", prop: "method", width: 200 },
  { label: "执行标准", prop: "standard", width: 160 },
  { label: "下限", prop: "limitMin", width: 90 },
  { label: "上限", prop: "limitMax", width: 90 },
  {
    dataType: "action", label: "操作", align: "center", fixed: "right", width: 140,
    operation: [
      { name: "编辑", type: "text", clickFun: (row) => openItemForm("edit", row) },
      { name: "删除", type: "text", clickFun: (row) => doDelItem([row.id]) },
    ],
  },
]);
 
const loadItem = () => {
  itemLoading.value = true;
  listTestItem({ ...itemSearch.value, ...itemPage }).then((res) => {
    itemLoading.value = false;
    itemData.value = res.data.records || [];
    itemPage.total = res.data.total || 0;
  }).catch(() => (itemLoading.value = false));
};
const paginationItem = (p) => { itemPage.pageNum = p.pageNum; itemPage.pageSize = p.pageSize; loadItem(); };
const loadItemOptions = () => { listTestItem({ pageNum: 1, pageSize: 500 }).then((res) => { itemOptions.value = res.data.records || []; }); };
 
const itemDialogVisible = ref(false);
const itemOpType = ref("add");
const itemFormRef = ref(null);
const itemForm = ref({});
const itemRules = {
  itemCode: [{ required: true, message: "请输入项目编号", trigger: "blur" }],
  itemName: [{ required: true, message: "请输入项目名称", trigger: "blur" }],
};
const openItemForm = (type, row) => {
  itemOpType.value = type;
  itemForm.value = { itemType: "chemical" };
  if (type === "edit") getTestItem(row.id).then((res) => { itemForm.value = { ...res.data }; });
  itemDialogVisible.value = true;
};
const submitItem = () => {
  itemFormRef.value.validate((valid) => {
    if (!valid) return;
    const fn = itemOpType.value === "add" ? addTestItem : updateTestItem;
    fn(itemForm.value).then(() => {
      ElMessage.success("保存成功");
      itemDialogVisible.value = false;
      loadItem();
      loadItemOptions();
    });
  });
};
const doDelItem = (ids) => {
  ElMessageBox.confirm(`确认删除选中的 ${ids.length} 条记录?`, "提示", { type: "warning" })
    .then(() => delTestItem(ids).then(() => { ElMessage.success("删除成功"); loadItem(); loadItemOptions(); }))
    .catch(() => {});
};
const delItem = () => {
  const ids = itemSelected.value.map((r) => r.id);
  if (!ids.length) return ElMessage.warning("请先选择记录");
  doDelItem(ids);
};
 
/* ==================== 计量单位 ==================== */
const unitSearch = ref({});
const unitData = ref([]);
const unitSelected = ref([]);
const unitLoading = ref(false);
const unitPage = reactive({ pageNum: 1, pageSize: 10, total: 0 });
 
const unitColumns = ref([
  { label: "单位编码", prop: "unitCode", width: 130 },
  { label: "单位名称", prop: "unitName", width: 160 },
  { label: "类型", prop: "unitType", width: 130 },
  { label: "基准单位", prop: "baseUnit", width: 130 },
  { label: "换算系数", prop: "conversionRate", width: 130 },
  {
    dataType: "action", label: "操作", align: "center", fixed: "right", width: 140,
    operation: [
      { name: "编辑", type: "text", clickFun: (row) => openUnitForm("edit", row) },
      { name: "删除", type: "text", clickFun: (row) => doDelUnit([row.id]) },
    ],
  },
]);
 
const loadUnit = () => {
  unitLoading.value = true;
  listUnit({ ...unitSearch.value, ...unitPage }).then((res) => {
    unitLoading.value = false;
    unitData.value = res.data.records || [];
    unitPage.total = res.data.total || 0;
  }).catch(() => (unitLoading.value = false));
};
const paginationUnit = (p) => { unitPage.pageNum = p.pageNum; unitPage.pageSize = p.pageSize; loadUnit(); };
 
const unitDialogVisible = ref(false);
const unitOpType = ref("add");
const unitFormRef = ref(null);
const unitForm = ref({});
const unitRules = {
  unitCode: [{ required: true, message: "请输入单位编码", trigger: "blur" }],
  unitName: [{ required: true, message: "请输入单位名称", trigger: "blur" }],
};
const openUnitForm = (type, row) => {
  unitOpType.value = type;
  unitForm.value = { unitType: "weight", conversionRate: 1 };
  if (type === "edit") getUnit(row.id).then((res) => { unitForm.value = { ...res.data }; });
  unitDialogVisible.value = true;
};
const submitUnit = () => {
  unitFormRef.value.validate((valid) => {
    if (!valid) return;
    const fn = unitOpType.value === "add" ? addUnit : updateUnit;
    fn(unitForm.value).then(() => {
      ElMessage.success("保存成功");
      unitDialogVisible.value = false;
      loadUnit();
    });
  });
};
const doDelUnit = (ids) => {
  ElMessageBox.confirm(`确认删除选中的 ${ids.length} 条记录?`, "提示", { type: "warning" })
    .then(() => apiDelUnit(ids).then(() => { ElMessage.success("删除成功"); loadUnit(); }))
    .catch(() => {});
};
const delUnit = () => {
  const ids = unitSelected.value.map((r) => r.id);
  if (!ids.length) return ElMessage.warning("请先选择记录");
  doDelUnit(ids);
};
 
/* ==================== 判定标准 ==================== */
const stdSearch = ref({});
const stdData = ref([]);
const stdSelected = ref([]);
const stdLoading = ref(false);
const stdPage = reactive({ pageNum: 1, pageSize: 10, total: 0 });
 
const stdColumns = ref([
  { label: "标准编号", prop: "standardCode", width: 140 },
  { label: "标准名称", prop: "standardName", width: 200 },
  { label: "检测项目", prop: "itemName", width: 160 },
  { label: "判定类型", prop: "standardType", width: 120 },
  { label: "下限", prop: "minValue", width: 100 },
  { label: "上限", prop: "maxValue", width: 100 },
  { label: "单位", prop: "unit", width: 90 },
  { label: "依据标准", prop: "sourceStandard", width: 160 },
  {
    dataType: "action", label: "操作", align: "center", fixed: "right", width: 140,
    operation: [
      { name: "编辑", type: "text", clickFun: (row) => openStdForm("edit", row) },
      { name: "删除", type: "text", clickFun: (row) => doDelStd([row.id]) },
    ],
  },
]);
 
const loadStd = () => {
  stdLoading.value = true;
  listJudgmentStandard({ ...stdSearch.value, ...stdPage }).then((res) => {
    stdLoading.value = false;
    stdData.value = res.data.records || [];
    stdPage.total = res.data.total || 0;
  }).catch(() => (stdLoading.value = false));
};
const paginationStd = (p) => { stdPage.pageNum = p.pageNum; stdPage.pageSize = p.pageSize; loadStd(); };
 
const stdDialogVisible = ref(false);
const stdOpType = ref("add");
const stdFormRef = ref(null);
const stdForm = ref({});
const stdRules = {
  standardCode: [{ required: true, message: "请输入标准编号", trigger: "blur" }],
  standardName: [{ required: true, message: "请输入标准名称", trigger: "blur" }],
};
const openStdForm = (type, row) => {
  stdOpType.value = type;
  stdForm.value = { standardType: "range" };
  if (type === "edit") getJudgmentStandard(row.id).then((res) => { stdForm.value = { ...res.data }; });
  stdDialogVisible.value = true;
};
const onStdItemChange = (id) => {
  const it = itemOptions.value.find((i) => i.id === id);
  if (it) {
    stdForm.value.itemName = it.itemName;
    if (!stdForm.value.unit) stdForm.value.unit = it.unit;
  }
};
const submitStd = () => {
  stdFormRef.value.validate((valid) => {
    if (!valid) return;
    const fn = stdOpType.value === "add" ? addJudgmentStandard : updateJudgmentStandard;
    fn(stdForm.value).then(() => {
      ElMessage.success("保存成功");
      stdDialogVisible.value = false;
      loadStd();
    });
  });
};
const doDelStd = (ids) => {
  ElMessageBox.confirm(`确认删除选中的 ${ids.length} 条记录?`, "提示", { type: "warning" })
    .then(() => delJudgmentStandard(ids).then(() => { ElMessage.success("删除成功"); loadStd(); }))
    .catch(() => {});
};
const delStd = () => {
  const ids = stdSelected.value.map((r) => r.id);
  if (!ids.length) return ElMessage.warning("请先选择记录");
  doDelStd(ids);
};
 
// ==================== ⑨ 系统参数 ====================
const setSearch = ref({});
const setData = ref([]);
const setLoading = ref(false);
const setPage = reactive({ pageNum: 1, pageSize: 10, total: 0 });
const setColumns = ref([
  { label: "分类", prop: "category", width: 100 },
  { label: "参数键", prop: "settingKey", width: 220 },
  { label: "参数名", prop: "settingName", width: 160 },
  { label: "当前值", prop: "settingValue", width: 140 },
  { label: "默认值", prop: "defaultValue", width: 110 },
  { label: "说明", prop: "description", showOverflowTooltip: true },
  {
    dataType: "action", label: "操作", align: "center", fixed: "right", width: 100,
    operation: [{ name: "编辑", type: "text", clickFun: (row) => openSetForm("edit", row) }],
  },
]);
const loadSetting = () => {
  setLoading.value = true;
  listSetting({ ...setSearch.value, ...setPage }).then((res) => {
    setLoading.value = false;
    setData.value = res.data.records || [];
    setPage.total = res.data.total || 0;
  }).catch(() => (setLoading.value = false));
};
const paginationSet = (p) => { setPage.pageNum = p.pageNum; setPage.pageSize = p.pageSize; loadSetting(); };
const setDialogVisible = ref(false);
const setOpType = ref("add");
const setForm = ref({});
const openSetForm = (type, row) => {
  setOpType.value = type;
  setForm.value = type === "edit" ? { ...row } : { valueType: "string", editable: "1", sortNo: 100 };
  setDialogVisible.value = true;
};
const submitSet = () => {
  const fn = setOpType.value === "add" ? addSetting : updateSetting;
  fn(setForm.value).then(() => {
    ElMessage.success("保存成功");
    setDialogVisible.value = false;
    loadSetting();
  });
};
 
// ==================== ⑧ 审计日志 ====================
const auditSearch = ref({});
const auditData = ref([]);
const auditLoading = ref(false);
const auditPage = reactive({ pageNum: 1, pageSize: 20, total: 0 });
const auditColumns = ref([
  { label: "时间", prop: "operateTime", width: 160 },
  { label: "模块", prop: "module", width: 110 },
  { label: "动作", prop: "action", width: 130 },
  { label: "业务编号", prop: "bizCode", width: 140 },
  { label: "说明", prop: "description", showOverflowTooltip: true },
  { label: "字段", prop: "fieldName", width: 100 },
  { label: "旧值", prop: "oldValue", width: 100 },
  { label: "新值", prop: "newValue", width: 100 },
  { label: "操作人", prop: "operator", width: 100 },
  { label: "IP", prop: "operatorIp", width: 130 },
  { label: "结果", prop: "execResult", width: 80 },
]);
const loadAudit = () => {
  auditLoading.value = true;
  listAuditLog({ ...auditSearch.value, ...auditPage }).then((res) => {
    auditLoading.value = false;
    auditData.value = res.data.records || [];
    auditPage.total = res.data.total || 0;
  }).catch(() => (auditLoading.value = false));
};
const paginationAudit = (p) => { auditPage.pageNum = p.pageNum; auditPage.pageSize = p.pageSize; loadAudit(); };
 
onMounted(() => {
  loadSetting();
  loadAudit();
  loadItem();
  loadItemOptions();
  loadUnit();
  loadStd();
});
</script>
 
<style scoped>
.search_form {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 12px;
  flex-wrap: wrap;
  gap: 8px;
}
.search_title {
  font-size: 14px;
  color: #606266;
}
</style>