Преглед на файлове

refactor(规则词库): 重构词库管理界面,优化词条添加和删除逻辑

- 移除批量导入模块,将其功能整合到词条抽屉中
- 新增输入框动态切换功能,优化词条添加体验
- 改进词条删除逻辑,支持从服务器删除数据
- 优化模板下载功能,支持动态生成文件名
闪电 преди 8 месеца
родител
ревизия
4d77fb2d77
променени са 1 файла, в които са добавени 128 реда и са изтрити 69 реда
  1. 128 69
      src/views/rules/words.vue

+ 128 - 69
src/views/rules/words.vue

@@ -40,33 +40,6 @@
40 40
       </div>
41 41
     </div>
42 42
 
43
-    <!-- 下部:批量导入 -->
44
-    <div class="border-t mt-4">
45
-      <div class="bg-white rounded-lg shadow p-4">
46
-        <h2 class="text-lg font-semibold mb-4">批量导入</h2>
47
-        <div class="flex space-x-4">
48
-          <el-upload action="" :file-list="fileList" :before-upload="beforeUpload" class="flex-1">
49
-            <el-button type="primary"><el-icon>
50
-                <Upload />
51
-              </el-icon>上传文件</el-button>
52
-            <template #tip>
53
-              <div class="el-upload__tip">
54
-                <span v-if="fileList.length > 0">{{ fileList[0].name }}</span>
55
-              </div>
56
-            </template>
57
-          </el-upload>
58
-          <a href="/template.xlsx" download class="el-button el-button--primary">
59
-            <el-icon>
60
-              <Download />
61
-            </el-icon>下载模板
62
-          </a>
63
-          <el-button type="success" @click="importWords"><el-icon>
64
-              <UploadFilled />
65
-            </el-icon>导入词库</el-button>
66
-        </div>
67
-      </div>
68
-    </div>
69
-
70 43
     <!-- 词条抽屉 -->
71 44
     <el-drawer v-model="drawerVisible" title="词条列表" size="40%">
72 45
       <div class="p-4">
@@ -85,38 +58,71 @@
85 58
           </el-icon>
86 59
         </div>
87 60
         <div class="flex flex-wrap gap-2">
88
-          <el-tag class="m-1 cursor-pointer" @click="addNewWord">
61
+          <!-- <el-tag class="m-1 cursor-pointer" @click="addNewWord">
89 62
             <el-icon>
90 63
               <Plus />
91 64
             </el-icon> 添加
92
-          </el-tag>
65
+          </el-tag> -->
66
+          <el-input v-if="inputVisible" ref="InputRef" v-model="inputValue" class="w-20" size="small"
67
+            @keyup.enter="handleInputConfirm" @blur="handleInputConfirm" style="width: 60px;"/>
68
+          <el-button v-else class="m-1" size="small" @click="showInput">
69
+            + 添加
70
+          </el-button>
93 71
           <el-tag v-for="(word, idx) in filteredWords" :key="idx" class="m-1" closable @close="removeWord(idx)">
94
-            {{ word }}
72
+            {{ word.name }}
95 73
           </el-tag>
96 74
         </div>
97 75
       </div>
76
+
77
+      <div class="absolute bottom-0 left-0 right-0 p-4 bg-white border-t flex justify-between">
78
+        <el-button @click="downloadTemplate"><el-icon>
79
+            <Download />
80
+          </el-icon>下载模板</el-button>
81
+
82
+        <el-upload style="text-align: center;" accept=".xlsx" v-model:file-list="fileList" :headers="headers"
83
+          :data="upLoadParams" :show-file-list="false" class="upload-demo"
84
+          :action="uploadURL + wordCategories[currentCategoryIndex]?.id" :on-success="handleExcelSuccess">
85
+          <el-button type="success"><el-icon>
86
+              <UploadFilled />
87
+            </el-icon>导入词库</el-button>
88
+        </el-upload>
89
+        <!-- <el-button type="success" @click="handleFileUpload"><el-icon><UploadFilled /></el-icon>导入词库</el-button> -->
90
+      </div>
98 91
     </el-drawer>
99 92
   </div>
100 93
 </template>
101 94
 
102 95
 <script lang="ts" setup name="RuleWord">
103
-import { onMounted, ref } from 'vue'
96
+import { nextTick, onMounted, ref } from 'vue'
104 97
 import { ElMessage, ElMessageBox } from 'element-plus'
105 98
 import { createPageData, getPageListData, editPageData, deletePageData } from '@/api/main/system/system'
106 99
 
100
+import { getToken } from '@/utils/auth';
107 101
 
108
-const wordCategories = ref([
102
+const downLoadUrl = new URL('@/assets/downLoad/word_template.xlsx', import.meta.url).href
103
+
104
+const wordCategories: any = ref([
109 105
   // { name: '敏感词', count: 12 },
110 106
   // { name: '违禁词', count: 8 },
111 107
   // { name: '广告词', count: 5 },
112 108
 ])
113 109
 
114 110
 const drawerVisible = ref(false)
115
-const currentWords = ref<string[]>([])
111
+const currentWords = ref([])
116 112
 const currentCategoryIndex = ref(0)
117 113
 const fileList = ref<File[]>([])
118 114
 const searchQuery = ref('')
119
-const filteredWords = ref<string[]>([])
115
+const filteredWords: any = ref<string[]>([])
116
+const inputVisible = ref(false)
117
+const inputValue = ref('')
118
+const InputRef = ref(null)
119
+
120
+const headers = ref({
121
+  Authorization: 'Bearer ' + getToken(),
122
+});
123
+
124
+const upLoadParams = ref({});
125
+const uploadURL = ref(import.meta.env.VITE_APP_BASE_API + '/quality/searchLexicon/importExcel/');
120 126
 
121 127
 const filterWords = () => {
122 128
   if (!searchQuery.value) {
@@ -124,45 +130,47 @@ const filterWords = () => {
124 130
     return
125 131
   }
126 132
   filteredWords.value = currentWords.value.filter(word =>
127
-    word.toLowerCase().includes(searchQuery.value.toLowerCase())
133
+    word.name.toLowerCase().includes(searchQuery.value.toLowerCase())
128 134
   )
129 135
 }
130 136
 
131
-const addNewWord = () => {
132
-  const newWord = prompt('请输入新词条')
133
-  if (newWord) {
134
-    currentWords.value.push(newWord)
135
-    filterWords()
136
-  }
137
-}
138
-
139 137
 const removeWord = (index: number) => {
140
-  currentWords.value.splice(index, 1)
141
-  filterWords()
138
+  const info = filteredWords.value[index];
139
+  deletePageData(`/quality/searchLexicon/${info.id}`).then(({ state }) => {
140
+    if (state === 'success') {
141
+      ElMessage.success('删除成功');
142
+      filteredWords.value.splice(index, 1);
143
+      const currentIndex = currentWords.value.findIndex(item => item.id === info.id);
144
+      console.log(currentIndex, currentWords.value, info.id)
145
+      if (currentIndex !== -1) {
146
+        currentWords.value.splice(currentIndex, 1);
147
+      }
148
+
149
+      wordCategories.value[currentCategoryIndex.value].count--;
150
+      filterWords();
151
+    }
152
+  })
153
+  // currentWords.value.splice(index, 1);
154
+  // filterWords()
142 155
 }
143 156
 
144 157
 const showWordDrawer = (index: number) => {
145 158
   currentCategoryIndex.value = index
146 159
   // 模拟获取词条数据
147
-  currentWords.value = Array.from({ length: wordCategories.value[index].count }, (_, i) =>
148
-    `${wordCategories.value[index].name}-${i + 1}`
149
-  )
150
-  filteredWords.value = [...currentWords.value]
160
+  // currentWords.value = Array.from({ length: wordCategories.value[index].count }, (_, i) =>
161
+  //   `${wordCategories.value[index].name}-${i + 1}`
162
+  // )
163
+  // filteredWords.value = [...currentWords.value]
164
+  getList(wordCategories.value[index].id)
151 165
   drawerVisible.value = true
152 166
 }
153 167
 
154
-const beforeUpload = (file: File) => {
155
-  console.log('上传文件:', file)
156
-  fileList.value = [file]
157
-  return false
158
-}
159
-
160 168
 const downloadTemplate = () => {
161
-  console.log('下载模板')
162
-}
163
-
164
-const importWords = () => {
165
-  console.log('导入词库')
169
+  const fileName = `${wordCategories.value[currentCategoryIndex.value].name}_模板.xlsx`
170
+  const link = document.createElement('a')
171
+  link.href = downLoadUrl
172
+  link.download = fileName
173
+  link.click()
166 174
 }
167 175
 
168 176
 const handleDelete = (index: number) => {
@@ -188,11 +196,10 @@ const handleEdit = async (index: number) => {
188 196
       cancelButtonText: '取消',
189 197
       inputValue: originalName,
190 198
       inputPattern: /^\S+$/,
191
-      inputErrorMessage: '词库名称不能为空'
199
+      inputErrorMessage: '词库名称不能为空,且为中文'
192 200
     })
193 201
 
194 202
     if (!value.trim() || value === originalName) return
195
-
196 203
     const params = {
197 204
       id: wordCategories.value[index].id, // 假设接口需要ID参数
198 205
       term: value.trim(),
@@ -207,10 +214,11 @@ const handleEdit = async (index: number) => {
207 214
         type: 'success',
208 215
         message: '修改成功',
209 216
       });
210
-      wordCategories.value.splice(index, 1, {
211
-        ...wordCategories.value[index],
212
-        name: value.trim()
213
-      });
217
+      wordCategories.value[index].name = value.trim()
218
+      // wordCategories.value.splice(index, 1, {
219
+      //   ...wordCategories.value[index],
220
+      //   name: value.trim()
221
+      // });
214 222
     }
215 223
   } catch (error) {
216 224
     // 用户取消输入
@@ -248,15 +256,66 @@ const handleAddCategory = async () => {
248 256
   }
249 257
 }
250 258
 
251
-const getList = () => {
252
-  getPageListData('/quality/searchLexicon', { type: 1, pageNum: 1, pageSize: 10000 }).then((res) => {
259
+const getList = (category = 0) => {
260
+  const params = {
261
+    type: 1,
262
+    pageNum: 1,
263
+    pageSize: 10000,
264
+    category
265
+  };
266
+  if (category) params.type = 2;
267
+  getPageListData('/quality/searchLexicon', params).then((res) => {
253 268
     console.log(res);
254 269
     if (res.state === 'success') {
255
-      wordCategories.value = res.data.map((item: any) => ({ name: item.term, count: item.count, id: item.id }));
270
+      if (!category) wordCategories.value = res.data.map((item: any) => ({ name: item.term, count: item.count, id: item.id }));
271
+      else {
272
+        currentWords.value = res.data.map((item: any) => ({ name: item.term, count: item.count, id: item.id }));
273
+        filterWords();
274
+      }
256 275
     }
257 276
   });
258 277
 }
259 278
 
279
+const handleExcelSuccess = (response, uploadFile, uploadFiles) => {
280
+  // 提示导入成功
281
+  getList();
282
+  // getPlanpatient()
283
+};
284
+
285
+const showInput = () => {
286
+  inputVisible.value = true
287
+  inputValue.value = ''
288
+  nextTick(() => {
289
+    InputRef.value!.input!.focus()
290
+  })
291
+}
292
+
293
+const handleInputConfirm = () => {
294
+  inputVisible.value = false
295
+  const name = inputValue.value.trim()
296
+  if (name) {
297
+    // currentWords.value.push(inputValue.value)
298
+    
299
+    // filterWords()
300
+    const params = { term: name, type: 2, category: wordCategories.value[currentCategoryIndex.value].id };
301
+
302
+    createPageData('/quality/searchLexicon', params).then((res) => {
303
+      console.log(res);
304
+      if (res.state ==='success') {
305
+        ElMessage({
306
+          type:'success',
307
+          message: '添加成功',
308
+        });
309
+        const info = { name: res.data.term, id: res.data.id };
310
+        filteredWords.value = [info, ...filteredWords.value];
311
+        currentWords.value = [info,...currentWords.value];
312
+        wordCategories.value[currentCategoryIndex.value].count++;
313
+        filterWords();
314
+      }
315
+    })
316
+  }
317
+}
318
+
260 319
 onMounted(() => {
261 320
   getList();
262 321
 })