Ver Fonte

feat: 新增关键词-关键词管理页面

WanGxC há 11 meses atrás
pai
commit
e83bc0954d

+ 34 - 0
src/views/keyword/keywordManage/api.ts

@@ -0,0 +1,34 @@
+import { request } from '/@/utils/service';
+
+const apiPrefix = '/api/searchterm/';
+
+export function getSearchTermList(query: any) {
+  return request({
+    url: apiPrefix + 'topsearchtermroot/',
+    method: 'GET',
+    params: query,
+  });
+}
+
+export function postCreateSearchTerm(body: any) {
+  return request({
+    url: apiPrefix + 'topsearchtermroot/',
+    method: 'POST',
+    data: body,
+  });
+}
+
+export function deleteSearchTerm(body: any) {
+  return request({
+    url: apiPrefix + `topsearchtermrootdelete/${body}/`,
+    method: 'POST',
+  });
+}
+
+export function postUpdateSearchTerm(body: any) {
+  return request({
+    url: apiPrefix + `topsearchtermrootupdate/${body.id}/`,
+    method: 'POST',
+    data: body.data,
+  });
+}

+ 328 - 0
src/views/keyword/keywordManage/components/keyword-manage-table.vue

@@ -0,0 +1,328 @@
+<script setup lang="ts">
+/**
+ * @Name: keyword-manage-table.vue
+ * @Description: 关键词管理表格
+ * @Author: Cheney
+ */
+
+import { nextTick, onMounted, reactive, ref } from 'vue';
+import { Plus } from '@element-plus/icons-vue';
+import * as api from '../api';
+import { ElMessage, FormInstance, FormRules } from 'element-plus';
+
+interface DataItem {
+  id: number;
+  modifier_name: string;
+  creator_name: string;
+  create_datetime: string;
+  update_datetime: string;
+  description: string | null;
+  modifier: string;
+  searchTerm: string;
+  searchTerm_type: string;
+  add_date: string;
+  creator: number;
+  isEditing: boolean; // 添加isEditing字段
+}
+
+const SUCCESS_CODE = 2000;
+const currentDate = new Date().toISOString().split('T')[0];
+
+const tableLoading = ref(false);
+const tableData = ref<DataItem[]>([]);
+const total = ref(0);
+const currentPage = ref(1);
+const pageSize = ref(10);
+
+// const searchTermTypeSelect = ref('positive');
+const searchTermInpRef = ref();
+
+const dialogVisible = ref(false);
+const formSearchTermInpRef = ref();
+const ruleFormRef = ref<FormInstance>();
+const ruleForm = reactive({
+  searchTerm: '',
+  searchTermType: 'positive',
+});
+const rules = reactive<FormRules<typeof ruleForm>>({
+  searchTerm: [{ required: true, validator: checkSearchTerm, trigger: 'blur' }],
+  searchTermType: [{ required: true, validator: checkSearchTermType, trigger: 'blur' }],
+});
+
+onMounted(() => {
+  fetchSearchTermList();
+});
+
+/**
+ * 添加关键词
+ */
+async function addSearchTerm() {
+  const body = {
+    searchTerm: ruleForm.searchTerm,
+    searchTerm_type: ruleForm.searchTermType,
+    add_date: currentDate, // 使用当前日期代替硬编码日期
+  };
+  try {
+    const response = await api.postCreateSearchTerm(body);
+    handleResponse(response);
+    if (response.code === SUCCESS_CODE) {
+      await fetchSearchTermList();
+    } else {
+      ElMessage.error('添加失败');
+    }
+  } catch (error) {
+    console.error('error:', error);
+  }
+}
+
+/**
+ * 删除行数据
+ * @param row 行数据
+ */
+async function handleDelete(row: any) {
+  try {
+    const response = await api.deleteSearchTerm(row.id);
+    handleResponse(response);
+    if (response.code === SUCCESS_CODE) {
+      tableData.value = tableData.value.filter((item) => item.id !== row.id);
+      await fetchSearchTermList();
+    } else {
+      ElMessage.error('删除失败');
+    }
+  } catch (error) {
+    console.log('error:', error);
+  }
+}
+
+/**
+ * 获取关键词列表数据
+ */
+async function fetchSearchTermList() {
+  tableLoading.value = true;
+  const query = {
+    page: currentPage.value,
+    limit: pageSize.value,
+    searchTerm: '', // TODO: 暂时写死
+  };
+  try {
+    const response = await api.getSearchTermList(query);
+    total.value = response.total;
+    const responseData: DataItem[] = response.data;
+    tableData.value = responseData.map((item) => ({ ...item, isEditing: false }));
+  } catch (error) {
+    console.log('error:', error);
+  } finally {
+    tableLoading.value = false;
+  }
+}
+
+/**
+ * 更新关键词
+ * @param row 修改行数据
+ */
+async function updateSearchTerm(row: any) {
+  row.isEditing = false;
+  const data = {
+    searchTerm: row.searchTerm,
+    searchTerm_type: row.searchTerm_type,
+  };
+  try {
+    const response = await api.postUpdateSearchTerm({ data, id: row.id });
+    handleResponse(response);
+    if (response.code === SUCCESS_CODE) {
+      await fetchSearchTermList();
+    } else {
+      ElMessage.error('更新失败');
+    }
+  } catch (error) {
+    console.log('error:', error);
+  }
+}
+
+async function updateSearchTermType(row: any, newValue: string) {
+  const data = {
+    searchTerm: row.searchTerm,
+    searchTerm_type: newValue,
+  };
+  try {
+    const response = await api.postUpdateSearchTerm({ data, id: row.id });
+    handleResponse(response);
+    if (response.code === SUCCESS_CODE) {
+      await fetchSearchTermList();
+    } else {
+      ElMessage.error('更新失败');
+    }
+  } catch (error) {
+    console.log('error:', error);
+  }
+}
+
+/**
+ * 切换编辑状态, 并自动获取焦点
+ * @param row 行数据
+ */
+function handleClick(row: any) {
+  row.isEditing = !row.isEditing;
+  nextTick(() => {
+    searchTermInpRef.value.focus();
+  });
+}
+
+/**
+ * 切换页码
+ * @param newPage 新页码
+ */
+async function handleCurrentChange(newPage: number) {
+  currentPage.value = newPage;
+  await fetchSearchTermList();
+}
+
+/**
+ * 切换每页条数
+ * @param newSize 新每页条数
+ */
+async function handleSizeChange(newSize: number) {
+  currentPage.value = 1;
+  pageSize.value = newSize;
+  await fetchSearchTermList();
+}
+
+/**
+ * 关闭对话框时清空表单
+ * @param done
+ */
+function handleClose(done: Function) {
+  if (ruleFormRef.value) ruleFormRef.value.resetFields();
+  done();
+}
+
+/**
+ * 打开对话框, 并自动获取焦点
+ */
+function handleDialogVisible() {
+  dialogVisible.value = true;
+  setTimeout(() => {
+    formSearchTermInpRef.value.focus();
+  }, 100);
+}
+
+function checkSearchTerm(rule: any, value: any, callback: any) {
+  if (!value) {
+    return callback(new Error('请输入关键词'));
+  } else {
+    callback();
+  }
+}
+
+function checkSearchTermType(rule: any, value: any, callback: any) {
+  if (!value) {
+    callback(new Error('请选择关键词类型'));
+  } else {
+    callback();
+  }
+}
+
+function submitForm(formEl: FormInstance | undefined) {
+  if (!formEl) return;
+  formEl.validate((valid) => {
+    if (valid) {
+      dialogVisible.value = false;
+      addSearchTerm();
+      formEl.resetFields();
+    } else {
+      console.log('error submit!');
+    }
+  });
+}
+
+function resetForm(formEl: FormInstance | undefined) {
+  if (!formEl) return;
+  formEl.resetFields();
+  dialogVisible.value = false;
+}
+
+/**
+ * 统一处理响应
+ * @param response 后端返回的响应
+ */
+function handleResponse(response: any) {
+  if (response.code === SUCCESS_CODE) {
+    ElMessage.success(response.msg);
+  } else {
+    ElMessage.error(response.msg || '请联系管理员');
+  }
+}
+</script>
+
+<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 height="530" style="width: 100%">
+        <el-table-column fixed="left" prop="add_date" label="添加日期" width="180" />
+        <el-table-column prop="searchTerm" label="关键词">
+          <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="关键词类型">
+          <template #default="{ row }">
+            <el-select v-model="row.searchTerm_type" @change="(value) => updateSearchTermType(row, value)">
+              <el-option label="positive" value="positive" />
+              <el-option label="negative" value="negative" />
+            </el-select>
+          </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-button link type="danger" size="small" @click="handleDelete(row)">删除</el-button>
+          </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>
+  </div>
+
+  <el-dialog v-model="dialogVisible" title="添加关键词" width="500" :before-close="handleClose">
+    <el-form ref="ruleFormRef" style="max-width: 600px" :model="ruleForm" status-icon :rules="rules" label-width="auto">
+      <el-form-item label="关键词" prop="searchTerm">
+        <el-input ref="formSearchTermInpRef" v-model="ruleForm.searchTerm" />
+      </el-form-item>
+      <el-form-item label="关键词类型" prop="searchTermType">
+        <el-select v-model="ruleForm.searchTermType">
+          <el-option label="positive" value="positive" />
+          <el-option label="negative" value="negative" />
+        </el-select>
+      </el-form-item>
+      <!--<el-form-item>-->
+      <!--  <el-button type="primary" @click="submitForm(ruleFormRef)"> Submit</el-button>-->
+      <!--  <el-button @click="resetForm(ruleFormRef)">Reset</el-button>-->
+      <!--</el-form-item>-->
+    </el-form>
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button @click="resetForm(ruleFormRef)">取消</el-button>
+        <el-button type="primary" @click="submitForm(ruleFormRef)"> 确定</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<style scoped></style>

+ 19 - 0
src/views/keyword/keywordManage/index.vue

@@ -0,0 +1,19 @@
+<script setup lang="ts">
+/**
+ * @Name: index.vue
+ * @Description: 关键词管理首页
+ * @Author: Cheney
+ */
+
+import KeywordManageTable from './components/keyword-manage-table.vue';
+</script>
+
+<template>
+  <div class="pt-1.5 pr-2.5">
+    <KeywordManageTable />
+  </div>
+</template>
+
+<style scoped>
+
+</style>