|  | @@ -1,14 +1,16 @@
 | 
	
		
			
				|  |  |  <script setup lang="ts">
 | 
	
		
			
				|  |  |  /**
 | 
	
		
			
				|  |  | - * @Name: keyword-manage-table.vue
 | 
	
		
			
				|  |  | + * @Name: root-word-manage-table.vue
 | 
	
		
			
				|  |  |   * @Description: 关键词管理表格
 | 
	
		
			
				|  |  |   * @Author: Cheney
 | 
	
		
			
				|  |  |   */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import { nextTick, onMounted, reactive, ref } from 'vue';
 | 
	
		
			
				|  |  | -import { Plus } from '@element-plus/icons-vue';
 | 
	
		
			
				|  |  | +import { Plus, Search, Upload } from '@element-plus/icons-vue';
 | 
	
		
			
				|  |  |  import * as api from '../api';
 | 
	
		
			
				|  |  | -import { ElMessage, FormInstance, FormRules } from 'element-plus';
 | 
	
		
			
				|  |  | +import { uploadFile } from '../api';
 | 
	
		
			
				|  |  | +import type { UploadInstance, UploadRawFile } from 'element-plus';
 | 
	
		
			
				|  |  | +import { ElMessage, FormInstance, FormRules, genFileId } from 'element-plus';
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  interface DataItem {
 | 
	
		
			
				|  |  |    id: number;
 | 
	
	
		
			
				|  | @@ -36,7 +38,6 @@ const total = ref(0);
 | 
	
		
			
				|  |  |  const currentPage = ref(1);
 | 
	
		
			
				|  |  |  const pageSize = ref(10);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -// const searchTermTypeSelect = ref('positive');
 | 
	
		
			
				|  |  |  const searchTermInpRef = ref();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  const dialogVisible = ref(false);
 | 
	
	
		
			
				|  | @@ -51,6 +52,10 @@ const rules = reactive<FormRules<typeof ruleForm>>({
 | 
	
		
			
				|  |  |    searchTermType: [{ required: true, validator: checkSearchTermType, trigger: 'blur' }],
 | 
	
		
			
				|  |  |  });
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +const searchTermTypeFilter = ref(' ');
 | 
	
		
			
				|  |  | +const searchTermFilter = ref('');
 | 
	
		
			
				|  |  | +const upload = ref<UploadInstance>();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  onMounted(() => {
 | 
	
		
			
				|  |  |    fetchSearchTermList();
 | 
	
		
			
				|  |  |  });
 | 
	
	
		
			
				|  | @@ -104,14 +109,19 @@ async function fetchSearchTermList() {
 | 
	
		
			
				|  |  |    const query = {
 | 
	
		
			
				|  |  |      page: currentPage.value,
 | 
	
		
			
				|  |  |      limit: pageSize.value,
 | 
	
		
			
				|  |  | -    searchTerm: '', // TODO: 暂时写死
 | 
	
		
			
				|  |  | +    searchTerm: searchTermFilter.value,
 | 
	
		
			
				|  |  | +    searchTerm_type: searchTermTypeFilter.value,
 | 
	
		
			
				|  |  |    };
 | 
	
		
			
				|  |  |    try {
 | 
	
		
			
				|  |  |      const response = await api.getSearchTermList(query);
 | 
	
		
			
				|  |  |      total.value = response.total;
 | 
	
		
			
				|  |  |      const responseData: DataItem[] = response.data;
 | 
	
		
			
				|  |  | -    tableData.value = responseData.map((item) => ({ ...item, isEditing: false, popConfirmVisible: false, tempSearchTermType: item.searchTerm_type }));
 | 
	
		
			
				|  |  | -    console.log('tableData.value ', tableData.value );
 | 
	
		
			
				|  |  | +    tableData.value = responseData.map((item) => ({
 | 
	
		
			
				|  |  | +      ...item,
 | 
	
		
			
				|  |  | +      isEditing: false,
 | 
	
		
			
				|  |  | +      popConfirmVisible: false,
 | 
	
		
			
				|  |  | +      tempSearchTermType: item.searchTerm_type,
 | 
	
		
			
				|  |  | +    }));
 | 
	
		
			
				|  |  |    } catch (error) {
 | 
	
		
			
				|  |  |      console.log('error:', error);
 | 
	
		
			
				|  |  |    } finally {
 | 
	
	
		
			
				|  | @@ -142,6 +152,10 @@ async function updateSearchTerm(row: any) {
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * 更新搜索词类型
 | 
	
		
			
				|  |  | + * @param row
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  |  async function updateSearchTermType(row: any) {
 | 
	
		
			
				|  |  |    const data = {
 | 
	
		
			
				|  |  |      searchTerm: row.searchTerm,
 | 
	
	
		
			
				|  | @@ -166,10 +180,14 @@ function showPopConfirm(row: any) {
 | 
	
		
			
				|  |  |    row.popConfirmVisible = true;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * 取消修改, 并恢复搜索词类型的值
 | 
	
		
			
				|  |  | + * @param row 行数据
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  |  function cancelUpdate(row: any) {
 | 
	
		
			
				|  |  |    row.popConfirmVisible = false;
 | 
	
		
			
				|  |  | -  row.searchTerm_type = row.tempSearchTermType; // 恢复原值
 | 
	
		
			
				|  |  | -  ElMessage.warning({ message: '已取消修改', plain: true})
 | 
	
		
			
				|  |  | +  row.searchTerm_type = row.tempSearchTermType;
 | 
	
		
			
				|  |  | +  ElMessage.warning({ message: '已取消修改', plain: true });
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /**
 | 
	
	
		
			
				|  | @@ -256,6 +274,41 @@ function resetForm(formEl: FormInstance | undefined) {
 | 
	
		
			
				|  |  |    dialogVisible.value = false;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +// async function handleFileChange(file: any) {
 | 
	
		
			
				|  |  | +//   if (file.raw) {
 | 
	
		
			
				|  |  | +//     await handleUpload(file.raw);
 | 
	
		
			
				|  |  | +//   }
 | 
	
		
			
				|  |  | +// }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// async function handleUpload(uploadFile: any) {
 | 
	
		
			
				|  |  | +//   try {
 | 
	
		
			
				|  |  | +//     const response = await api.uploadFile(uploadFile);
 | 
	
		
			
				|  |  | +//     handleResponse(response);
 | 
	
		
			
				|  |  | +//   } catch (error) {
 | 
	
		
			
				|  |  | +//     console.error('error:', error);
 | 
	
		
			
				|  |  | +//   }
 | 
	
		
			
				|  |  | +// }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +async function handleCustomUpload(uploadRequest: any) {
 | 
	
		
			
				|  |  | +  try {
 | 
	
		
			
				|  |  | +    const { file } = uploadRequest;
 | 
	
		
			
				|  |  | +    const response = await uploadFile(file);
 | 
	
		
			
				|  |  | +    handleResponse(response);
 | 
	
		
			
				|  |  | +    uploadRequest.onSuccess(response); // 通知 el-upload 上传成功
 | 
	
		
			
				|  |  | +  } catch (error) {
 | 
	
		
			
				|  |  | +    console.error('error:', error);
 | 
	
		
			
				|  |  | +    uploadRequest.onError(error); // 通知 el-upload 上传失败
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +function handleExceed(files: any) {
 | 
	
		
			
				|  |  | +  upload.value!.clearFiles();
 | 
	
		
			
				|  |  | +  const file = files[0] as UploadRawFile;
 | 
	
		
			
				|  |  | +  file.uid = genFileId();
 | 
	
		
			
				|  |  | +  upload.value!.handleStart(file);
 | 
	
		
			
				|  |  | +  upload.value!.submit();
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  /**
 | 
	
		
			
				|  |  |   * 统一处理响应
 | 
	
		
			
				|  |  |   * @param response 后端返回的响应
 | 
	
	
		
			
				|  | @@ -271,59 +324,99 @@ function handleResponse(response: any) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  <template>
 | 
	
		
			
				|  |  |    <div>
 | 
	
		
			
				|  |  | -    <el-card v-loading="tableLoading">
 | 
	
		
			
				|  |  | -      <el-button plain type="primary" @click="handleDialogVisible" class="mb-2">
 | 
	
		
			
				|  |  | -        <el-icon>
 | 
	
		
			
				|  |  | -          <Plus />
 | 
	
		
			
				|  |  | -        </el-icon>
 | 
	
		
			
				|  |  | -        添加关键词
 | 
	
		
			
				|  |  | -      </el-button>
 | 
	
		
			
				|  |  | -      <el-table :data="tableData" stripe max-height="530" style="width: 100%">
 | 
	
		
			
				|  |  | -        <el-table-column fixed="left" prop="add_date" label="添加日期" width="180" sortable />
 | 
	
		
			
				|  |  | -        <el-table-column prop="searchTerm" label="关键词" sortable>
 | 
	
		
			
				|  |  | -          <template #default="{ row }">
 | 
	
		
			
				|  |  | -            <el-input ref="searchTermInpRef" v-if="row.isEditing" v-model="row.searchTerm" @change="updateSearchTerm(row)" />
 | 
	
		
			
				|  |  | -            <span class="font-bold" v-else>{{ row.searchTerm }}</span>
 | 
	
		
			
				|  |  | -          </template>
 | 
	
		
			
				|  |  | -        </el-table-column>
 | 
	
		
			
				|  |  | -        <el-table-column prop="searchTerm_type" label="关键词类型" sortable>
 | 
	
		
			
				|  |  | -          <template #default="{ row }">
 | 
	
		
			
				|  |  | -            <el-popconfirm
 | 
	
		
			
				|  |  | -              title="确定修改吗?"
 | 
	
		
			
				|  |  | -              @confirm="updateSearchTermType(row)"
 | 
	
		
			
				|  |  | -              @cancel="cancelUpdate(row)"
 | 
	
		
			
				|  |  | -              :visible="row.popConfirmVisible">
 | 
	
		
			
				|  |  | -              <template #reference>
 | 
	
		
			
				|  |  | -                <el-select v-model="row.searchTerm_type" @change="showPopConfirm(row)" style="width: 150px">
 | 
	
		
			
				|  |  | -                  <el-option label="positive" value="positive" />
 | 
	
		
			
				|  |  | -                  <el-option label="negative" value="negative" />
 | 
	
		
			
				|  |  | -                </el-select>
 | 
	
		
			
				|  |  | +    <el-card v-loading="tableLoading" body-style="background-color: #f6f7fb;">
 | 
	
		
			
				|  |  | +      <div class="flex justify-between gap-3.5 pb-3.5">
 | 
	
		
			
				|  |  | +        <div class="flex gap-7">
 | 
	
		
			
				|  |  | +          <div>
 | 
	
		
			
				|  |  | +            <span class="font-bold mr-2" style="color: #303133">词根:</span>
 | 
	
		
			
				|  |  | +            <el-input
 | 
	
		
			
				|  |  | +              v-model="searchTermFilter"
 | 
	
		
			
				|  |  | +              placeholder="请输入词根"
 | 
	
		
			
				|  |  | +              clearable
 | 
	
		
			
				|  |  | +              @change="fetchSearchTermList"
 | 
	
		
			
				|  |  | +              :prefix-icon="Search"
 | 
	
		
			
				|  |  | +              style="width: 240px"></el-input>
 | 
	
		
			
				|  |  | +          </div>
 | 
	
		
			
				|  |  | +          <div>
 | 
	
		
			
				|  |  | +            <span class="font-bold mr-2" style="color: #303133">搜索条件:</span>
 | 
	
		
			
				|  |  | +            <el-select v-model="searchTermTypeFilter" style="width: 200px" @change="fetchSearchTermList">
 | 
	
		
			
				|  |  | +              <el-option label="全部" value=" " />
 | 
	
		
			
				|  |  | +              <el-option label="positive" value="positive" />
 | 
	
		
			
				|  |  | +              <el-option label="negative" value="negative" />
 | 
	
		
			
				|  |  | +            </el-select>
 | 
	
		
			
				|  |  | +          </div>
 | 
	
		
			
				|  |  | +        </div>
 | 
	
		
			
				|  |  | +        <div class="flex gap-3.5">
 | 
	
		
			
				|  |  | +          <el-button plain type="primary" @click="handleDialogVisible">
 | 
	
		
			
				|  |  | +            <el-icon>
 | 
	
		
			
				|  |  | +              <Plus />
 | 
	
		
			
				|  |  | +            </el-icon>
 | 
	
		
			
				|  |  | +            添加关键词
 | 
	
		
			
				|  |  | +          </el-button>
 | 
	
		
			
				|  |  | +          <div style="height: 72px">
 | 
	
		
			
				|  |  | +            <el-upload
 | 
	
		
			
				|  |  | +              ref="upload"
 | 
	
		
			
				|  |  | +              action="#"
 | 
	
		
			
				|  |  | +              :limit="1"
 | 
	
		
			
				|  |  | +              :auto-upload="true"
 | 
	
		
			
				|  |  | +              :on-exceed="handleExceed"
 | 
	
		
			
				|  |  | +              :http-request="handleCustomUpload">
 | 
	
		
			
				|  |  | +              <template #trigger>
 | 
	
		
			
				|  |  | +                <el-button plain round type="warning" :icon="Upload">上传文件</el-button>
 | 
	
		
			
				|  |  |                </template>
 | 
	
		
			
				|  |  | -            </el-popconfirm>
 | 
	
		
			
				|  |  | -          </template>
 | 
	
		
			
				|  |  | -        </el-table-column>
 | 
	
		
			
				|  |  | -        <el-table-column fixed="right" label="操作" width="120">
 | 
	
		
			
				|  |  | -          <template #default="{ row }">
 | 
	
		
			
				|  |  | -            <el-button link type="primary" size="small" @click="handleClick(row)" v-if="!row.isEditing"> 编辑</el-button>
 | 
	
		
			
				|  |  | -            <el-button link type="primary" size="small" @click="handleClick(row)" v-else> 取消</el-button>
 | 
	
		
			
				|  |  | -            <el-popconfirm title="确定删除吗?" @confirm="handleDelete(row)">
 | 
	
		
			
				|  |  | -              <template #reference>
 | 
	
		
			
				|  |  | -                <el-button link type="danger" size="small">删除</el-button>
 | 
	
		
			
				|  |  | -              </template>
 | 
	
		
			
				|  |  | -            </el-popconfirm>
 | 
	
		
			
				|  |  | -          </template>
 | 
	
		
			
				|  |  | -        </el-table-column>
 | 
	
		
			
				|  |  | -      </el-table>
 | 
	
		
			
				|  |  | -      <div class="mt-3.5 flex justify-end">
 | 
	
		
			
				|  |  | -        <el-pagination
 | 
	
		
			
				|  |  | -          v-model:current-page="currentPage"
 | 
	
		
			
				|  |  | -          v-model:page-size="pageSize"
 | 
	
		
			
				|  |  | -          :page-sizes="[10, 20, 30, 50]"
 | 
	
		
			
				|  |  | -          layout="sizes, prev, pager, next"
 | 
	
		
			
				|  |  | -          :total="total"
 | 
	
		
			
				|  |  | -          @size-change="handleSizeChange"
 | 
	
		
			
				|  |  | -          @current-change="handleCurrentChange" />
 | 
	
		
			
				|  |  | +            </el-upload>
 | 
	
		
			
				|  |  | +          </div>
 | 
	
		
			
				|  |  | +        </div>
 | 
	
		
			
				|  |  |        </div>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      <el-card shadow="never">
 | 
	
		
			
				|  |  | +        <el-table :data="tableData" stripe max-height="530" style="width: 100%">
 | 
	
		
			
				|  |  | +          <el-table-column fixed="left" prop="add_date" label="添加日期" width="180" sortable />
 | 
	
		
			
				|  |  | +          <el-table-column prop="searchTerm" label="词根" sortable>
 | 
	
		
			
				|  |  | +            <template #default="{ row }">
 | 
	
		
			
				|  |  | +              <el-input ref="searchTermInpRef" v-if="row.isEditing" v-model="row.searchTerm" @change="updateSearchTerm(row)" />
 | 
	
		
			
				|  |  | +              <span class="font-bold" v-else>{{ row.searchTerm }}</span>
 | 
	
		
			
				|  |  | +            </template>
 | 
	
		
			
				|  |  | +          </el-table-column>
 | 
	
		
			
				|  |  | +          <el-table-column prop="searchTerm_type" label="词根类型" sortable>
 | 
	
		
			
				|  |  | +            <template #default="{ row }">
 | 
	
		
			
				|  |  | +              <el-popconfirm
 | 
	
		
			
				|  |  | +                title="确定修改吗?"
 | 
	
		
			
				|  |  | +                @confirm="updateSearchTermType(row)"
 | 
	
		
			
				|  |  | +                @cancel="cancelUpdate(row)"
 | 
	
		
			
				|  |  | +                :visible="row.popConfirmVisible">
 | 
	
		
			
				|  |  | +                <template #reference>
 | 
	
		
			
				|  |  | +                  <el-select v-model="row.searchTerm_type" @change="showPopConfirm(row)" style="width: 150px">
 | 
	
		
			
				|  |  | +                    <el-option label="positive" value="positive" />
 | 
	
		
			
				|  |  | +                    <el-option label="negative" value="negative" />
 | 
	
		
			
				|  |  | +                  </el-select>
 | 
	
		
			
				|  |  | +                </template>
 | 
	
		
			
				|  |  | +              </el-popconfirm>
 | 
	
		
			
				|  |  | +            </template>
 | 
	
		
			
				|  |  | +          </el-table-column>
 | 
	
		
			
				|  |  | +          <el-table-column fixed="right" label="操作" width="120">
 | 
	
		
			
				|  |  | +            <template #default="{ row }">
 | 
	
		
			
				|  |  | +              <el-button link type="primary" size="small" @click="handleClick(row)" v-if="!row.isEditing"> 编辑</el-button>
 | 
	
		
			
				|  |  | +              <el-button link type="primary" size="small" @click="handleClick(row)" v-else> 取消</el-button>
 | 
	
		
			
				|  |  | +              <el-popconfirm title="确定删除吗?" @confirm="handleDelete(row)">
 | 
	
		
			
				|  |  | +                <template #reference>
 | 
	
		
			
				|  |  | +                  <el-button link type="danger" size="small">删除</el-button>
 | 
	
		
			
				|  |  | +                </template>
 | 
	
		
			
				|  |  | +              </el-popconfirm>
 | 
	
		
			
				|  |  | +            </template>
 | 
	
		
			
				|  |  | +          </el-table-column>
 | 
	
		
			
				|  |  | +        </el-table>
 | 
	
		
			
				|  |  | +        <div class="mt-3.5 flex justify-end">
 | 
	
		
			
				|  |  | +          <el-pagination
 | 
	
		
			
				|  |  | +            v-model:current-page="currentPage"
 | 
	
		
			
				|  |  | +            v-model:page-size="pageSize"
 | 
	
		
			
				|  |  | +            :page-sizes="[10, 20, 30, 50]"
 | 
	
		
			
				|  |  | +            layout="sizes, prev, pager, next"
 | 
	
		
			
				|  |  | +            :total="total"
 | 
	
		
			
				|  |  | +            @size-change="handleSizeChange"
 | 
	
		
			
				|  |  | +            @current-change="handleCurrentChange" />
 | 
	
		
			
				|  |  | +        </div>
 | 
	
		
			
				|  |  | +      </el-card>
 | 
	
		
			
				|  |  |      </el-card>
 | 
	
		
			
				|  |  |    </div>
 | 
	
		
			
				|  |  |    <!-- 添加关键词弹窗 -->
 |