浏览代码

feat(规则模型): 添加模型条件和关键词选择功能

- 新增模型条件表单验证和关键词选择功能
- 优化模型条件编辑和删除逻辑
- 添加模型名称和规则时效的必填项提示
- 修复模型列表选择和条件保存的bug
闪电 8 月之前
父节点
当前提交
1554681ae1
共有 2 个文件被更改,包括 397 次插入93 次删除
  1. 0 0
      .trae/rules/project_rules.md
  2. 397 93
      src/views/rules/models.vue

+ 0 - 0
.trae/rules/project_rules.md


+ 397 - 93
src/views/rules/models.vue

15
         <div class="mb-4">
15
         <div class="mb-4">
16
           <div class="relative">
16
           <div class="relative">
17
             <input type="text" v-model="searchQuery" placeholder="搜索模型"
17
             <input type="text" v-model="searchQuery" placeholder="搜索模型"
18
-              class="w-full pl-10 pr-4 py-2 text-sm border rounded-lg focus:outline-none focus:border-blue-500" @keydown.enter="doSearch"/>
18
+              class="w-full pl-10 pr-4 py-2 text-sm border rounded-lg focus:outline-none focus:border-blue-500"
19
+              @keydown.enter="doSearch" />
19
             <el-icon class="absolute left-3 top-2.5 text-gray-400" style="position: absolute;">
20
             <el-icon class="absolute left-3 top-2.5 text-gray-400" style="position: absolute;">
20
               <Search />
21
               <Search />
21
             </el-icon>
22
             </el-icon>
23
         </div>
24
         </div>
24
         <div v-if="modelList.length" class="h-[calc(100vh-180px)] overflow-y-auto" ref="cardListRef" @scroll="doScroll">
25
         <div v-if="modelList.length" class="h-[calc(100vh-180px)] overflow-y-auto" ref="cardListRef" @scroll="doScroll">
25
           <div v-for="(model, index) in modelList" :key="index" :class="[
26
           <div v-for="(model, index) in modelList" :key="index" :class="[
26
-              'p-3 mb-2 rounded-lg cursor-pointer relative group',
27
-              selectedModel === index
28
-                ? 'bg-blue-50 border border-blue-500'
29
-                : 'hover:bg-gray-50',
30
-            ]" @click="selectModel(index)">
27
+            'p-3 mb-2 rounded-lg cursor-pointer relative group',
28
+            selectedModel === index
29
+              ? 'bg-blue-50 border border-blue-500'
30
+              : 'hover:bg-gray-50',
31
+          ]" @click="selectModel(index)">
31
             <div class="text-sm font-medium">{{ model.ruleName }}</div>
32
             <div class="text-sm font-medium">{{ model.ruleName }}</div>
32
             <div class="text-xs text-gray-500 mt-1">
33
             <div class="text-xs text-gray-500 mt-1">
33
               最后更新: {{ model.updatedAt }}
34
               最后更新: {{ model.updatedAt }}
40
               </el-icon>
41
               </el-icon>
41
               <template #dropdown>
42
               <template #dropdown>
42
                 <el-dropdown-menu>
43
                 <el-dropdown-menu>
43
-                  <el-dropdown-item style="color: red;">删除</el-dropdown-item>
44
+                  <el-dropdown-item style="color: red;" @click="deleteModel(model)">删除</el-dropdown-item>
44
                   <!-- <el-dropdown-item>The Action 2st</el-dropdown-item>
45
                   <!-- <el-dropdown-item>The Action 2st</el-dropdown-item>
45
                   <el-dropdown-item>The Action 3st</el-dropdown-item> -->
46
                   <el-dropdown-item>The Action 3st</el-dropdown-item> -->
46
                 </el-dropdown-menu>
47
                 </el-dropdown-menu>
56
       <!-- 右侧添加条件抽屉 -->
57
       <!-- 右侧添加条件抽屉 -->
57
       <div class="flex-1 overflow-hidden">
58
       <div class="flex-1 overflow-hidden">
58
         <el-drawer v-model="showConditionDrawer" title="添加条件" size="40%">
59
         <el-drawer v-model="showConditionDrawer" title="添加条件" size="40%">
59
-          <el-form :model="conditionForm" label-width="100px">
60
-            <el-form-item label="条件分类">
60
+          <el-form :model="conditionForm" :rules="formRules" label-width="100px" ref="conditionFormRef">
61
+            <el-form-item label="条件分类" prop="type">
61
               <el-select v-model="conditionForm.type" placeholder="请选择条件分类">
62
               <el-select v-model="conditionForm.type" placeholder="请选择条件分类">
62
-                <el-option label="基础检测" value="基础检测" />
63
-                <el-option label="通话质量检测" value="通话质量检测" />
63
+                <el-option v-for="item in conditionTypes" :key="item.name" :label="item.name" :value="item.name" />
64
               </el-select>
64
               </el-select>
65
             </el-form-item>
65
             </el-form-item>
66
-            <el-form-item label="条件名称">
66
+            <el-form-item label="条件名称" prop="name">
67
               <el-select v-model="conditionForm.name" placeholder="请选择条件名称">
67
               <el-select v-model="conditionForm.name" placeholder="请选择条件名称">
68
-                <el-option label="开场白" value="开场白" />
69
-                <el-option label="结束语" value="结束语" />
70
-                <el-option label="音量" value="音量" />
68
+                <el-option v-for="item in conditionOptions" :key="item" :label="item" :value="item" />
71
               </el-select>
69
               </el-select>
72
             </el-form-item>
70
             </el-form-item>
73
-            <el-form-item label="开场白">
74
-              <div v-for="(greeting, index) in conditionForm.greetings" :key="index" class="mb-2">
75
-                <div class="flex items-center">
76
-                  <el-input v-model="conditionForm.greetings[index]" :placeholder="`请输入开场白${index + 1}`" class="flex-1" style="width: 500px;"/>
77
-                  <el-button type="danger" @click="removeGreeting(index)" :icon="Delete" circle class="ml-2" />
78
-                  <el-button v-if="index === conditionForm.greetings.length - 1" @click="addGreeting" :icon="Plus" circle class="ml-2" />
79
-                </div>
80
-              </div>
81
-              <!-- <el-button type="primary" @click="addGreeting">添加开场白</el-button> -->
71
+
72
+            <el-form-item label="关键词" prop="keywords">
73
+              <el-select v-model="conditionForm.keywords" multiple filterable placeholder="请选择关键词"
74
+                style="width: 500px;">
75
+                <el-option v-for="item in keywordOptions" :key="item.id" :label="item.term" :value="item.id" />
76
+              </el-select>
82
             </el-form-item>
77
             </el-form-item>
83
-            <el-form-item label="分值">
78
+            <el-form-item label="分值" prop="score">
84
               <div class="flex items-center">
79
               <div class="flex items-center">
85
-                <el-slider v-model="conditionForm.score" :max="100" class="flex-1 mr-4" style="width: 400px;"/>
80
+                <el-slider v-model="conditionForm.score" :max="100" class="flex-1 mr-4" style="width: 400px;" />
86
                 <el-input-number v-model="conditionForm.score" :min="0" :max="100" />
81
                 <el-input-number v-model="conditionForm.score" :min="0" :max="100" />
87
               </div>
82
               </div>
88
             </el-form-item>
83
             </el-form-item>
89
-            <el-form-item label="加减类型">
84
+            <el-form-item label="加减类型" prop="operation">
90
               <el-select v-model="conditionForm.operation" placeholder="请选择加减类型">
85
               <el-select v-model="conditionForm.operation" placeholder="请选择加减类型">
91
-                <el-option label="加分项" value="加分项" />
92
-                <el-option label="减分项" value="减分项" />
86
+                <el-option label="加分项" value="0" />
87
+                <el-option label="减分项" value="1" />
93
               </el-select>
88
               </el-select>
94
             </el-form-item>
89
             </el-form-item>
90
+            <el-form-item label="加减分频次" prop="frequency">
91
+              <el-input v-model="conditionForm.frequency" style="max-width: 600px" placeholder="请输入数字,0表示仅一次">
92
+                <template #prepend>每出现</template>
93
+                <template #append>
94
+                  {{ `${conditionForm.operation === '1' ? '减' : '加'}${conditionForm.score || 0}分`
95
+                  }}
96
+                </template>
97
+              </el-input>
98
+            </el-form-item>
99
+            <el-form-item v-if="conditionForm.name === '音量'" label="正常音量">
100
+              <el-slider v-model="conditionForm.volumeRange" range :min="100" :max="500" style="width: 400px;"
101
+                :marks="volumeMarks" />
102
+            </el-form-item>
103
+            <el-form-item v-if="conditionForm.name === '语速'" label="正常语速">
104
+              <el-slider v-model="conditionForm.speedRange" range :min="100" :max="500" style="width: 400px;"
105
+                :marks="speedMarks" />
106
+            </el-form-item>
95
             <el-form-item label="是否预警">
107
             <el-form-item label="是否预警">
96
               <el-switch v-model="conditionForm.warning" />
108
               <el-switch v-model="conditionForm.warning" />
97
             </el-form-item>
109
             </el-form-item>
98
             <el-form-item label="是否致命">
110
             <el-form-item label="是否致命">
99
-              <el-switch v-model="conditionForm.warning" />
111
+              <el-switch v-model="conditionForm.fatal" />
100
             </el-form-item>
112
             </el-form-item>
101
           </el-form>
113
           </el-form>
102
           <div class="flex justify-end mt-4">
114
           <div class="flex justify-end mt-4">
104
             <el-button type="primary" @click="saveCondition">保存</el-button>
116
             <el-button type="primary" @click="saveCondition">保存</el-button>
105
           </div>
117
           </div>
106
         </el-drawer>
118
         </el-drawer>
107
-        <div class="bg-white rounded-lg shadow-sm p-6 h-full overflow-y-auto">
119
+        <div v-if="selectedModel >= 0" class="bg-white rounded-lg shadow-sm p-6 h-full overflow-y-auto">
108
           <!-- 操作按钮 -->
120
           <!-- 操作按钮 -->
109
           <div class="absolute mt-6 right-8">
121
           <div class="absolute mt-6 right-8">
110
-            <el-button @click="cancel">取消</el-button>
111
-            <el-button type="primary" @click="save">保存</el-button>
122
+            <!-- <el-button @click="cancel">取消</el-button> -->
123
+            <el-button type="primary" @click="submit">保存</el-button>
112
           </div>
124
           </div>
113
           <!-- 基本信息 -->
125
           <!-- 基本信息 -->
114
           <div class="mb-6">
126
           <div class="mb-6">
118
             </div>
130
             </div>
119
             <div class="flex items-center gap-6 mb-4">
131
             <div class="flex items-center gap-6 mb-4">
120
               <div class="flex-1">
132
               <div class="flex-1">
121
-                <label class="block text-sm mb-2">模型名称</label>
122
-                <el-input v-model="currentModel.ruleName" placeholder="请输入模型名称" />
133
+                <label class="block text-sm mb-2"><span class="text-red-500">*</span>模型名称</label>
134
+                <el-input v-model="currentModel.ruleName" placeholder="请输入模型名称" clearable @blur="changeRuleName"/>
123
               </div>
135
               </div>
124
               <div class="flex-1">
136
               <div class="flex-1">
125
-                <label class="block text-sm mb-2">规则时效</label>
137
+                <label class="block text-sm mb-2"><span class="text-red-500">*</span>规则时效</label>
126
                 <el-date-picker v-model="currentModel.effectiveDate" type="daterange" range-separator="至"
138
                 <el-date-picker v-model="currentModel.effectiveDate" type="daterange" range-separator="至"
127
-                  start-placeholder="开始日期" end-placeholder="结束日期" class="w-full" />
139
+                  start-placeholder="开始日期" end-placeholder="结束日期" class="w-full" clearable/>
128
               </div>
140
               </div>
129
             </div>
141
             </div>
130
           </div>
142
           </div>
143
+
131
           <!-- 模型条件 -->
144
           <!-- 模型条件 -->
132
           <div>
145
           <div>
133
             <div class="flex items-center justify-between mb-4">
146
             <div class="flex items-center justify-between mb-4">
134
-              <h2 class="text-lg font-medium">模型条件</h2>
147
+              <h2 class="text-lg font-medium"><span class="text-red-500">*</span>模型条件</h2>
135
               <el-button type="primary" @click="addCondition">
148
               <el-button type="primary" @click="addCondition">
136
                 <el-icon class="mr-1">
149
                 <el-icon class="mr-1">
137
                   <Plus />
150
                   <Plus />
141
             <el-table :data="currentModel.conditions" stripe>
154
             <el-table :data="currentModel.conditions" stripe>
142
               <el-table-column prop="type" label="条件分类" width="140" />
155
               <el-table-column prop="type" label="条件分类" width="140" />
143
               <el-table-column prop="name" label="条件名称" />
156
               <el-table-column prop="name" label="条件名称" />
144
-              <el-table-column prop="operation" label="加减分项" width="140" />
145
-              <el-table-column prop="warning" label="是否预警" width="140" />
146
-              <el-table-column prop="fatal" label="是否致命" width="140" />
147
-              <el-table-column prop="frequency" label="加减分频次" width="140" />
157
+              <el-table-column prop="operation" label="加减分项" width="140">
158
+                <template #default="{ row }">
159
+                  <span :class="row.operation? 'text-red-500' : 'text-gray-500'">
160
+                    {{ row.operation? '减分项' : '加分项' }}
161
+                  </span>
162
+                </template>
163
+              </el-table-column>
164
+              <el-table-column prop="warning" label="是否预警" width="140">
165
+                <template #default="{ row }">
166
+                  <span :class="row.warning ? 'text-red-500' : 'text-gray-500'">
167
+                    {{ row.warning ? '预警' : '非预警' }}
168
+                  </span>
169
+                </template>
170
+              </el-table-column>
171
+              <el-table-column prop="fatal" label="是否致命" width="140">
172
+                <template #default="{ row }">
173
+                  <span :class="row.fatal ? 'text-red-500' : 'text-gray-500'">
174
+                    {{ row.f ? '致命' : '非致命' }}
175
+                  </span>
176
+                </template>
177
+              </el-table-column>
178
+              <el-table-column prop="frequency" label="加减分频次" width="140">
179
+                <template #default="{ row }">
180
+                  <span>
181
+                    {{ row.frequency ? `每出现${row.frequency}次${row.operation === '1' ? '减' : '加'}${row.score || 0}分` :
182
+                    `出现多次仅${row.operation === '1' ? '减' : '加'}${row.score || 0}分`
183
+                    }}
184
+                  </span>
185
+                </template>
186
+              </el-table-column>
148
               <el-table-column prop="score" label="分值" width="100" />
187
               <el-table-column prop="score" label="分值" width="100" />
149
               <el-table-column label="操作" fixed="right" width="120">
188
               <el-table-column label="操作" fixed="right" width="120">
150
-                <template #default="{ row }">
189
+                <template #default="{ row, $index }">
151
                   <el-button type="primary" :icon="Edit" circle class="mr-2" @click="editCondition(row)" />
190
                   <el-button type="primary" :icon="Edit" circle class="mr-2" @click="editCondition(row)" />
152
-                  <el-button type="danger" :icon="Delete" circle @click="deleteCondition(row)" />
191
+                  <el-button type="danger" :icon="Delete" circle @click="deleteCondition($index, row)" />
153
                 </template>
192
                 </template>
154
               </el-table-column>
193
               </el-table-column>
155
             </el-table>
194
             </el-table>
156
-            <!-- <div class="mt-4 text-sm text-gray-500">
157
-              <span>共 {{ currentModel.conditions.length }} 条</span>
158
-            </div> -->
159
           </div>
195
           </div>
160
 
196
 
161
-          
197
+
198
+        </div>
199
+
200
+        <div v-else class="h-[calc(100vh-180px)]">
201
+          <el-empty :image-size="200" description="请选择模型"/>
162
         </div>
202
         </div>
163
 
203
 
164
       </div>
204
       </div>
166
   </div>
206
   </div>
167
 </template>
207
 </template>
168
 <script lang="ts" setup name="RuleModel">
208
 <script lang="ts" setup name="RuleModel">
169
-import { ref, onMounted } from 'vue';
209
+import { ref, onMounted, watch, nextTick } from 'vue';
170
 import { Edit, Delete, Search, Plus, MoreFilled } from '@element-plus/icons-vue';
210
 import { Edit, Delete, Search, Plus, MoreFilled } from '@element-plus/icons-vue';
171
 import moment from 'moment';
211
 import moment from 'moment';
172
 import { createPageData, getPageListData, editPageData, deletePageData } from '@/api/main/system/system';
212
 import { createPageData, getPageListData, editPageData, deletePageData } from '@/api/main/system/system';
213
+import { ElMessage, ElMessageBox } from 'element-plus';
173
 
214
 
174
 const searchQuery = ref('');
215
 const searchQuery = ref('');
175
-const selectedModel = ref(0);
216
+const selectedModel = ref(-1);
176
 const currentPage = ref(1);
217
 const currentPage = ref(1);
177
 const pageSize = 10;
218
 const pageSize = 10;
178
 const cardListRef = ref(null);
219
 const cardListRef = ref(null);
179
 const modelList: any = ref([]);
220
 const modelList: any = ref([]);
221
+const volumeMarks = {
222
+  260: '标准',
223
+}
224
+const speedMarks = {
225
+  260: '标准',
226
+}
180
 const currentModel: any = ref({
227
 const currentModel: any = ref({
181
   id: 0,
228
   id: 0,
182
   ruleName: '',
229
   ruleName: '',
185
   conditions: [],
232
   conditions: [],
186
 });
233
 });
187
 const selectModel = (index: number) => {
234
 const selectModel = (index: number) => {
235
+  if (!modelList.value?.length) return;
188
   selectedModel.value = index;
236
   selectedModel.value = index;
237
+  currentModel.value = modelList.value[index];
238
+  currentModel.value.conditions = [];
239
+  if (currentModel.value.startTime && currentModel.value.endTime) {
240
+    currentModel.value.effectiveDate = [
241
+      moment(currentModel.value.startTime),
242
+      moment(currentModel.value.endTime)
243
+    ];
244
+  }
245
+
246
+  getCases();
189
 };
247
 };
248
+
249
+// 获取条件
250
+const getCases = async () => {
251
+  if (selectedModel.value < 0) return;
252
+  // 获取模型案例逻辑
253
+  const res = await getPageListData(`/quality/scoringCriteria`, {
254
+    rulesId: currentModel.value.id,
255
+  });
256
+  if (res?.data?.length) {
257
+    console.log('模型案例', res.data)
258
+    currentModel.value.conditions = res.data.map((item: any) => {
259
+      return {
260
+        id: item.id,
261
+        rulesId: item.rulesId,
262
+        type: item.conditionCategory,
263
+        name: item.conditionName,
264
+        score: item.score,
265
+        frequency: item.frequency,
266
+        keywords: item.content,
267
+        operation: item.scoreType + '',
268
+        warning: item.isAlert === 1,
269
+        fatal: item.isCritical === 1,
270
+        speedRange: [item.minValue, item.maxValue],
271
+        volumeRange: [item.minValue, item.maxValue],
272
+      }
273
+    });
274
+  }
275
+}
276
+
190
 const showConditionDrawer = ref(false);
277
 const showConditionDrawer = ref(false);
191
 
278
 
192
 const addGreeting = () => {
279
 const addGreeting = () => {
197
   conditionForm.value.greetings.splice(index, 1);
284
   conditionForm.value.greetings.splice(index, 1);
198
 };
285
 };
199
 
286
 
200
-const getModelCases = async () => {
287
+const getModelCases = async (name = '') => {
288
+  if (conditionTypes.value?.length) return;
201
   // 获取模型案例逻辑
289
   // 获取模型案例逻辑
202
   const res = await getPageListData(`/quality/qcresult/getInspectionRulesType`, {});
290
   const res = await getPageListData(`/quality/qcresult/getInspectionRulesType`, {});
203
   if (res?.data?.length) {
291
   if (res?.data?.length) {
204
     console.log('模型案例', res.data)
292
     console.log('模型案例', res.data)
293
+    conditionTypes.value = res.data;
294
+    if (conditionTypes.value.length) {
295
+      const selectedType = conditionTypes.value.find(item => name ? item.name === res.data[0].name : item.name === name);
296
+      conditionOptions.value = selectedType ? selectedType.Condition : [];
297
+    }
298
+    
299
+  }
300
+
301
+}
302
+
303
+const getWords = async () => {
304
+  if (keywordOptions.value?.length) return;
305
+  // 获取模型案例逻辑
306
+  const res = await getPageListData(`/quality/searchLexicon`, {
307
+    type: 1,
308
+  });
309
+  if (res?.data?.length) {
310
+    keywordOptions.value = res.data;
205
   }
311
   }
206
-  
207
 }
312
 }
208
 
313
 
209
-const conditionForm: any = ref({});
314
+const conditionFormRef = ref();
315
+const formRules = {
316
+  type: [{ required: true, message: '请选择条件分类', trigger: 'change' }],
317
+  name: [{ required: true, message: '请选择条件名称', trigger: 'change' }],
318
+  keywords: [{ required: true, message: '请选择关键词', trigger: 'change' }],
319
+  score: [{ required: true, message: '请设置分值', trigger: 'change' }],
320
+  operation: [{ required: true, message: '请选择加减类型', trigger: 'change' }]
321
+};
322
+const conditionForm: any = ref({
323
+  volumeRange: [250, 300],
324
+});
210
 
325
 
211
 const addCondition = async () => {
326
 const addCondition = async () => {
327
+  getModelCases();
328
+  getWords();
212
   showConditionDrawer.value = true;
329
   showConditionDrawer.value = true;
213
-
214
-  // 获取可用条件列表
215
-  await getModelCases();
216
   conditionForm.value = {
330
   conditionForm.value = {
217
     type: '',
331
     type: '',
218
     name: '',
332
     name: '',
219
-    greetings: [''] as string[],
220
     score: 0,
333
     score: 0,
221
-    operation: '',
222
-    warning: false
334
+    operation: '1',
335
+    warning: false,
336
+    fatal: false,
337
+    // min: 0,
338
+    // max: 100,
339
+    frequency: 1,
340
+    volumeRange: [250, 300],
341
+    speedRange: [250, 300],
342
+    keywords: [],
223
   };
343
   };
224
 };
344
 };
225
-const editCondition = (row: any) => {
226
-  // 编辑条件逻辑
345
+const editCondition = async (row: any) => {
346
+  console.log('editCondition', row)
347
+  await getWords();
348
+  
349
+  await getModelCases(row.type);
350
+  nextTick(() => {
351
+    showConditionDrawer.value = true;
352
+    conditionForm.value = row;
353
+    console.log('conditionForm.value', row)
354
+    conditionForm.value.keywords = row.keywords?.length ? row.keywords.split(',').map(o => Number(o)) : [];
355
+  })
356
+  
227
 };
357
 };
228
 
358
 
229
 const getPageList = async () => {
359
 const getPageList = async () => {
232
     pageNum: currentPage.value,
362
     pageNum: currentPage.value,
233
     pageSize: pageSize,
363
     pageSize: pageSize,
234
   };
364
   };
235
-  const {data} = await getPageListData(`/quality/inspectionRules`, params);
365
+  const { data } = await getPageListData(`/quality/inspectionmodel`, params);
236
   if (data?.length) {
366
   if (data?.length) {
237
     modelList.value = modelList.value.concat(data);
367
     modelList.value = modelList.value.concat(data);
368
+
369
+    if (selectedModel.value === -1 && modelList.value.length) {
370
+      // selectedModel.value = 0;
371
+      // currentModel.value = modelList.value[0];
372
+      selectModel(0);
373
+    }
238
   }
374
   }
239
 }
375
 }
240
 
376
 
261
 
397
 
262
 const addModel = () => {
398
 const addModel = () => {
263
   const time = moment().format('YYYY-MM-DD HH:mm:ss');
399
   const time = moment().format('YYYY-MM-DD HH:mm:ss');
400
+
401
+  // 判断是否存在新模型
402
+  const index = modelList.value.findIndex((item: any) => item.ruleName === '新模型');
403
+  if (index >=0) {
404
+    ElMessage.error('请先保存之前的新模型');
405
+    return;
406
+  }
264
   modelList.value.unshift({
407
   modelList.value.unshift({
265
     ruleName: '新模型',
408
     ruleName: '新模型',
266
     updatedAt: time,
409
     updatedAt: time,
267
   });
410
   });
411
+
412
+  selectedModel.value = 0;
268
   currentModel.value = {
413
   currentModel.value = {
269
     ruleName: '新模型',
414
     ruleName: '新模型',
270
-  createdAt: time,
271
-  effectiveDate: [],
272
-  conditions: [
273
-    {
274
-      type: '基础检测',
275
-      name: '开场白',
276
-      operation: '减分项',
277
-      warning: '预警',
278
-      fatal: '致命',
279
-      frequency: '触发多次加减分一次',
280
-      score: 5,
281
-    },
282
-    {
283
-      type: '基础检测',
284
-      name: '结束语',
285
-      operation: '减分项',
286
-      warning: '不预警',
287
-      fatal: '不致命',
288
-      frequency: '触发多次加减分一次',
289
-      score: 5,
290
-    },
291
-    {
292
-      type: '通话质量检测',
293
-      name: '音量',
294
-      operation: '减分项',
295
-      warning: '不预警',
296
-      fatal: '不致命',
297
-      frequency: '触发多次加减分一次',
298
-      score: 5,
299
-    },
300
-  ],
415
+    createdAt: time,
416
+    effectiveDate: [],
417
+    conditions: [
418
+      
419
+    ],
301
   }
420
   }
302
 }
421
 }
303
 
422
 
304
 onMounted(() => {
423
 onMounted(() => {
305
   getPageList()
424
   getPageList()
306
 })
425
 })
426
+const conditionTypes = ref([
427
+  // {
428
+  //   Condition: ["开场白", "结束语"],
429
+  //   name: "基础检测"
430
+  // },
431
+  // {
432
+  //   Condition: ["清晰度", "打断客户", "答复不清", "脏话辱骂"],
433
+  //   name: "服务态度检测"
434
+  // },
435
+  // {
436
+  //   Condition: ["音量", "语速"],
437
+  //   name: "通话质量检测"
438
+  // }
439
+]);
440
+
441
+const conditionOptions = ref<string[]>([]);
442
+
443
+// 监听条件分类变化
444
+watch(() => conditionForm.value.type, (newVal) => {
445
+  const selectedType = conditionTypes.value.find(item => item.name === newVal);
446
+  conditionOptions.value = selectedType ? selectedType.Condition : [];
447
+  if (conditionForm.value.name && !conditionOptions.value.includes(conditionForm.value.name)) {
448
+    conditionForm.value.name = ''; // 重置条件名称
449
+  }
450
+});
451
+
452
+const saveCondition = async () => {
453
+  try {
454
+    await conditionFormRef.value.validate();
455
+    const condition = {
456
+      id: conditionForm.value.id || '',
457
+      type: conditionForm.value.type,
458
+      name: conditionForm.value.name,
459
+      keywords: conditionForm.value.keywords.join(','), // 将id数组转为逗号分隔的字符串
460
+      // 如果需要同时保存关键词文本,可以添加:
461
+      keywordTerms: conditionForm.value.keywords.map(
462
+        id => keywordOptions.value.find(item => item.id === id)?.term || ''
463
+      ).join(','),
464
+      operation: conditionForm.value.operation,
465
+      warning: conditionForm.value.warning,
466
+      fatal: conditionForm.value.fatal,
467
+      frequency: conditionForm.value.frequency,
468
+      score: conditionForm.value.score,
469
+    };
470
+
471
+    // 如果是编辑模式,找到原条件并更新
472
+    const existingIndex = currentModel.value.conditions.findIndex(
473
+      (item: any) =>
474
+        item.type === conditionForm.value.type &&
475
+        item.name === conditionForm.value.name
476
+    );
477
+
478
+    if (existingIndex !== -1) {
479
+      currentModel.value.conditions[existingIndex] = condition;
480
+    } else {
481
+      currentModel.value.conditions.push(condition);
482
+    }
483
+
484
+    showConditionDrawer.value = false;
485
+  } catch (error) {
486
+    console.log('表单验证失败:', error);
487
+    return;
488
+  }
489
+};
490
+const deleteCondition = async (index: number, row: any) => {
491
+  
492
+  if (row.id) {
493
+    const res: any = await deletePageData(`/quality/scoringCriteria/${row.id}`);
494
+    console.log('删除', res)
495
+    if (res.state !== 'success') return;
496
+  }
497
+console.log('deleteCondition', index, row)
498
+  if (index !== -1) {
499
+    currentModel.value.conditions.splice(index, 1);
500
+  }
501
+  ElMessage.success('删除成功');
502
+};
503
+// 关键词选项数据 - 修改为 id/term 格式
504
+const keywordOptions = ref([
505
+  // { id: 1, term: '关键词1' },
506
+  // { id: 2, term: '关键词2' },
507
+  // { id: 3, term: '关键词3' },
508
+  // { id: 4, term: '关键词4' },
509
+]);
510
+
511
+const submit = async () => {
512
+  // 提交逻辑
513
+  console.log('提交', currentModel.value)
514
+  
515
+  const params: any = {
516
+    ruleName: currentModel.value.ruleName,
517
+    // effectiveDate: currentModel.value.effectiveDate,
518
+    scoringCriteria: [],
519
+  }
520
+
521
+  if (currentModel.value.effectiveDate && currentModel.value.effectiveDate.length === 2) {
522
+    params.startTime = moment(currentModel.value.effectiveDate[0]).format('YYYY-MM-DD HH:mm:ss');
523
+    params.endTime = moment(currentModel.value.effectiveDate[1]).format('YYYY-MM-DD HH:mm:ss');
524
+  }
525
+
526
+  if (!currentModel.value.conditions?.length) {
527
+    // 提示添加
528
+    ElMessage.error('请添加模型条件');
529
+    return;
530
+  }
531
+
532
+  for (const info of currentModel.value.conditions) {
533
+    const newInfo: any = {
534
+      id: info.id,
535
+      conditionCategory: info.type,
536
+      conditionName: info.name,
537
+      operation: info.operation,
538
+      isAlert: info.warning ? 1 : 0,
539
+      isCritical: info.fatal ? 1 : 0,
540
+      // rulesId: info.rulesId,
541
+      frequency: info.frequency,
542
+      score: info.score,
543
+      content: info.keywords,
544
+      type: 1,
545
+    };
546
+    if (info.name === '音量' && info.volumeRange && info.volumeRange.length === 2) {
547
+      newInfo.minValue = info.volumeRange[0];
548
+      newInfo.maxValue = info.volumeRange[1];   
549
+    }
550
+
551
+    if (info.name === '语速' && info.speedRange && info.speedRange.length === 2) {
552
+      newInfo.minValue = info.speedRange[0];
553
+      newInfo.maxValue = info.speedRange[1];
554
+    }
555
+    params.scoringCriteria.push(newInfo)
556
+  }
557
+  console.log('提交', params)
558
+
559
+  if (currentModel.value.id) {
560
+    params.id = currentModel.value.id;
561
+    const res: any = await editPageData(`/quality/inspectionmodel`, params);
562
+    if (res?.state === 'success') {
563
+      ElMessage.success('修改成功');
564
+      currentModel.value.updatedAt = moment().format('YYYY-MM-DD HH:mm:ss');
565
+      getCases();
566
+    }
567
+  } else {
568
+    const res: any = await createPageData(`/quality/inspectionmodel`, params);
569
+    console.log('提交', res)
570
+    if (res?.state === 'success') {
571
+      ElMessage.success('提交成功');
572
+      currentModel.value.id = res?.data.id;
573
+      currentModel.value.updatedAt = moment().format('YYYY-MM-DD HH:mm:ss');
574
+      getCases();
575
+    }
576
+  }
577
+  
578
+}
579
+
580
+const changeRuleName = () => {
581
+  // 更改左侧列表的名字
582
+  const index = modelList.value.findIndex((item: any) => item.id === currentModel.value.id);
583
+  if (index >= 0) {
584
+    modelList.value[index].ruleName = currentModel.value.ruleName;
585
+  }
586
+}
587
+
588
+const deleteModel = async (model) => {
589
+
590
+  ElMessageBox.confirm('确认删除该模型吗?', '提示', {
591
+    confirmButtonText: '确定',
592
+    cancelButtonText: '取消',
593
+    type: 'warning'
594
+  }).then(async () => {
595
+    if (model.id) {
596
+      const res: any = await deletePageData(`/quality/inspectionmodel/${model.id}`);
597
+      console.log('删除', res)
598
+      if (res.state !== 'success') return;
599
+    }
600
+    const index = modelList.value.findIndex((item: any) => item.id === model.id);
601
+    if (index >= 0) {
602
+      modelList.value.splice(index, 1);
603
+      
604
+      selectModel(0);
605
+      ElMessage.success('删除成功');
606
+    }
607
+  })
608
+  
609
+}
610
+
307
 </script>
611
 </script>
308
 <style scoped>
612
 <style scoped>
309
 .el-input :deep(.el-input__wrapper) {
613
 .el-input :deep(.el-input__wrapper) {