|  | @@ -0,0 +1,281 @@
 | 
											
												
													
														|  | 
 |  | +<script lang="ts" setup>/**
 | 
											
												
													
														|  | 
 |  | + * @Name: createKeyword.vue
 | 
											
												
													
														|  | 
 |  | + * @Description: 谷歌关键词添加
 | 
											
												
													
														|  | 
 |  | + * @Author: xinyan
 | 
											
												
													
														|  | 
 |  | + */
 | 
											
												
													
														|  | 
 |  | +import { deleteKeyword, getKeyword, postCreateKeyword, UpdateKeyword } from '/@/views/googleTrends/api';
 | 
											
												
													
														|  | 
 |  | +import { Delete, Plus } from '@element-plus/icons-vue';
 | 
											
												
													
														|  | 
 |  | +import { ref } from 'vue';
 | 
											
												
													
														|  | 
 |  | +import { VxeGridInstance } from 'vxe-table';
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +const emit = defineEmits([ 'updateKeyword' ]);
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +const keywordInput = ref('');
 | 
											
												
													
														|  | 
 |  | +const btnLoading = ref(false);
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +const isCancel = ref(false); // 是否取消编辑
 | 
											
												
													
														|  | 
 |  | +const isEditing = ref(false); // 是否正在编辑
 | 
											
												
													
														|  | 
 |  | +const keywordList = ref([]); // 关键词列表
 | 
											
												
													
														|  | 
 |  | +const xGrid = ref<VxeGridInstance>();
 | 
											
												
													
														|  | 
 |  | +const gridOptions = reactive({
 | 
											
												
													
														|  | 
 |  | +  border: 'inner',
 | 
											
												
													
														|  | 
 |  | +  keepSource: true,
 | 
											
												
													
														|  | 
 |  | +  loading: false,
 | 
											
												
													
														|  | 
 |  | +  height: 555,
 | 
											
												
													
														|  | 
 |  | +  columnConfig: {
 | 
											
												
													
														|  | 
 |  | +    resizable: true
 | 
											
												
													
														|  | 
 |  | +  },
 | 
											
												
													
														|  | 
 |  | +  rowConfig: {
 | 
											
												
													
														|  | 
 |  | +    isHover: true,
 | 
											
												
													
														|  | 
 |  | +    height: 38,
 | 
											
												
													
														|  | 
 |  | +  },
 | 
											
												
													
														|  | 
 |  | +  editConfig: {
 | 
											
												
													
														|  | 
 |  | +    trigger: 'manual',
 | 
											
												
													
														|  | 
 |  | +    showIcon: false,
 | 
											
												
													
														|  | 
 |  | +    // autoFocus: true,
 | 
											
												
													
														|  | 
 |  | +    autoClear: false,
 | 
											
												
													
														|  | 
 |  | +  },
 | 
											
												
													
														|  | 
 |  | +  pagerConfig: {
 | 
											
												
													
														|  | 
 |  | +    enabled: true,
 | 
											
												
													
														|  | 
 |  | +    total: 20,
 | 
											
												
													
														|  | 
 |  | +    currentPage: 1,
 | 
											
												
													
														|  | 
 |  | +    pageSize: 20,
 | 
											
												
													
														|  | 
 |  | +    pageSizes: [10, 20, 30],
 | 
											
												
													
														|  | 
 |  | +  },
 | 
											
												
													
														|  | 
 |  | +  columns: [
 | 
											
												
													
														|  | 
 |  | +    { field: 'keyword', title: '关键词', editRender: { name: 'input' }, slots: { edit: 'keyword_edit' } },
 | 
											
												
													
														|  | 
 |  | +    { field: 'insert_time', title: '添加时间' },
 | 
											
												
													
														|  | 
 |  | +    { title: '操作', width: 120, slots: { default: 'operate' }, align: 'center' },
 | 
											
												
													
														|  | 
 |  | +  ],
 | 
											
												
													
														|  | 
 |  | +  data: []
 | 
											
												
													
														|  | 
 |  | +});
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +const gridEvents = {
 | 
											
												
													
														|  | 
 |  | +  pageChange({ currentPage, pageSize }) {
 | 
											
												
													
														|  | 
 |  | +    if (gridOptions.pagerConfig) {
 | 
											
												
													
														|  | 
 |  | +      gridOptions.pagerConfig.currentPage = currentPage;
 | 
											
												
													
														|  | 
 |  | +      gridOptions.pagerConfig.pageSize = pageSize;
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +    getList();
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +};
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +const hasActiveEditRow = (row) => {
 | 
											
												
													
														|  | 
 |  | +  const $grid = xGrid.value;
 | 
											
												
													
														|  | 
 |  | +  if ($grid) {
 | 
											
												
													
														|  | 
 |  | +    return $grid.isEditByRow(row);
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +  return false;
 | 
											
												
													
														|  | 
 |  | +};
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +function editRowEvent(row) {
 | 
											
												
													
														|  | 
 |  | +  isCancel.value = false;
 | 
											
												
													
														|  | 
 |  | +  isEditing.value = true;
 | 
											
												
													
														|  | 
 |  | +  const $grid = xGrid.value;
 | 
											
												
													
														|  | 
 |  | +  if ($grid) {
 | 
											
												
													
														|  | 
 |  | +    $grid.setEditRow(row);
 | 
											
												
													
														|  | 
 |  | +    row.original_keyword = row.keyword;
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +function handelEditClosed({ row }) {
 | 
											
												
													
														|  | 
 |  | +  isEditing.value = false;
 | 
											
												
													
														|  | 
 |  | +  if (isCancel.value)return;
 | 
											
												
													
														|  | 
 |  | +  if (row.original_keyword !== row.keyword){
 | 
											
												
													
														|  | 
 |  | +    updateKeyword(row);
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +async function getList() {
 | 
											
												
													
														|  | 
 |  | +  gridOptions.loading = true;
 | 
											
												
													
														|  | 
 |  | +  try {
 | 
											
												
													
														|  | 
 |  | +    const resp = await getKeyword({
 | 
											
												
													
														|  | 
 |  | +      page: gridOptions.pagerConfig.currentPage,
 | 
											
												
													
														|  | 
 |  | +      limit: gridOptions.pagerConfig.pageSize,
 | 
											
												
													
														|  | 
 |  | +    });
 | 
											
												
													
														|  | 
 |  | +    gridOptions.data = resp.data;
 | 
											
												
													
														|  | 
 |  | +    keywordList.value = resp.data.map(item => item.keyword);
 | 
											
												
													
														|  | 
 |  | +    emit('updateKeyword',keywordList.value);
 | 
											
												
													
														|  | 
 |  | +    gridOptions.pagerConfig.total = resp.total;
 | 
											
												
													
														|  | 
 |  | +    gridOptions.loading = false;
 | 
											
												
													
														|  | 
 |  | +  } catch (error) {
 | 
											
												
													
														|  | 
 |  | +    console.log(error);
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +async function handleAdd() {
 | 
											
												
													
														|  | 
 |  | +  const keywordsInput = keywordInput.value.trim();
 | 
											
												
													
														|  | 
 |  | +  if (!keywordsInput) {
 | 
											
												
													
														|  | 
 |  | +    return;
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +  // 按行拆分关键词,并过滤掉空行
 | 
											
												
													
														|  | 
 |  | +  const keywords = keywordsInput.split('\n').map(keyword => keyword.trim()).filter(keyword => keyword.length > 0);
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  try {
 | 
											
												
													
														|  | 
 |  | +    btnLoading.value = true;
 | 
											
												
													
														|  | 
 |  | +    const resp = await postCreateKeyword({ keyword: keywords.join(',') });
 | 
											
												
													
														|  | 
 |  | +    if (resp.code === 2000) {
 | 
											
												
													
														|  | 
 |  | +      ElMessage.success('关键词添加成功');
 | 
											
												
													
														|  | 
 |  | +      keywordInput.value = ''; // 清空输入框
 | 
											
												
													
														|  | 
 |  | +      await getList();
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +  } catch (error) {
 | 
											
												
													
														|  | 
 |  | +    ElMessage.error('添加关键词失败,请重试!'); // 提示失败消息
 | 
											
												
													
														|  | 
 |  | +  } finally {
 | 
											
												
													
														|  | 
 |  | +    btnLoading.value = false;
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +async function updateKeyword(row) {
 | 
											
												
													
														|  | 
 |  | +  try {
 | 
											
												
													
														|  | 
 |  | +    gridOptions.loading = true;
 | 
											
												
													
														|  | 
 |  | +    const resp = await UpdateKeyword({
 | 
											
												
													
														|  | 
 |  | +      id: row.id,
 | 
											
												
													
														|  | 
 |  | +      new_keyword: row.keyword
 | 
											
												
													
														|  | 
 |  | +    });
 | 
											
												
													
														|  | 
 |  | +    if (resp.code === 2000) {
 | 
											
												
													
														|  | 
 |  | +      await getList();
 | 
											
												
													
														|  | 
 |  | +      ElMessage.success('关键词更新成功');
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +  } catch (error) {
 | 
											
												
													
														|  | 
 |  | +    ElMessage.error('更新关键词失败,请重试!'); // 提示失败消息
 | 
											
												
													
														|  | 
 |  | +  } finally {
 | 
											
												
													
														|  | 
 |  | +    gridOptions.loading = false;
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +function cancel(row) {
 | 
											
												
													
														|  | 
 |  | +  isCancel.value = true;
 | 
											
												
													
														|  | 
 |  | +  const $grid = xGrid.value;
 | 
											
												
													
														|  | 
 |  | +  if ($grid) {
 | 
											
												
													
														|  | 
 |  | +    $grid.clearEdit().then(() => {
 | 
											
												
													
														|  | 
 |  | +      $grid.revertData(row)
 | 
											
												
													
														|  | 
 |  | +    })
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +async function handleDelete(row) {
 | 
											
												
													
														|  | 
 |  | +  try {
 | 
											
												
													
														|  | 
 |  | +    gridOptions.loading = true;
 | 
											
												
													
														|  | 
 |  | +    const resp = await deleteKeyword({ keyword: row.keyword });
 | 
											
												
													
														|  | 
 |  | +    if (resp.code === 2000) {
 | 
											
												
													
														|  | 
 |  | +      ElMessage.success('关键词删除成功');
 | 
											
												
													
														|  | 
 |  | +      await getList();
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +  } catch (error) {
 | 
											
												
													
														|  | 
 |  | +    ElMessage.error('删除关键词失败,请重试!'); // 提示失败消息
 | 
											
												
													
														|  | 
 |  | +  } finally {
 | 
											
												
													
														|  | 
 |  | +    gridOptions.loading = false;
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +function handleClear() {
 | 
											
												
													
														|  | 
 |  | +  keywordInput.value = '';
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +const cellStyle = () => {
 | 
											
												
													
														|  | 
 |  | +  return {
 | 
											
												
													
														|  | 
 |  | +    fontSize: '13px',
 | 
											
												
													
														|  | 
 |  | +    fontWeight: '500',
 | 
											
												
													
														|  | 
 |  | +  };
 | 
											
												
													
														|  | 
 |  | +};
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +const headerCellStyle = () => {
 | 
											
												
													
														|  | 
 |  | +  return {
 | 
											
												
													
														|  | 
 |  | +    fontSize: '13px',
 | 
											
												
													
														|  | 
 |  | +    backgroundColor: '#f0f1f3',
 | 
											
												
													
														|  | 
 |  | +    height: 10,
 | 
											
												
													
														|  | 
 |  | +  };
 | 
											
												
													
														|  | 
 |  | +};
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +onMounted(() => {
 | 
											
												
													
														|  | 
 |  | +  getList();
 | 
											
												
													
														|  | 
 |  | +});
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +</script>
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +<template>
 | 
											
												
													
														|  | 
 |  | +  <el-card class="mx-2 my-5">
 | 
											
												
													
														|  | 
 |  | +    <div class="my-4 mx-1.5" style="font-size: 18px;font-weight: bold;color: #464646">谷歌关键词添加</div>
 | 
											
												
													
														|  | 
 |  | +    <div class="mx-2">
 | 
											
												
													
														|  | 
 |  | +      <el-row :gutter="20">
 | 
											
												
													
														|  | 
 |  | +        <el-col :span="8" class="input-section">
 | 
											
												
													
														|  | 
 |  | +          <div class="input-container">
 | 
											
												
													
														|  | 
 |  | +            <el-input
 | 
											
												
													
														|  | 
 |  | +                v-model="keywordInput"
 | 
											
												
													
														|  | 
 |  | +                :rows="26"
 | 
											
												
													
														|  | 
 |  | +                class="textarea"
 | 
											
												
													
														|  | 
 |  | +                placeholder="添加谷歌趋势关键词,一行一个关键词......(关键词添加上限200个)"
 | 
											
												
													
														|  | 
 |  | +                type="textarea"
 | 
											
												
													
														|  | 
 |  | +            />
 | 
											
												
													
														|  | 
 |  | +            <!-- 按钮组 -->
 | 
											
												
													
														|  | 
 |  | +            <div class="button-group">
 | 
											
												
													
														|  | 
 |  | +              <el-popconfirm
 | 
											
												
													
														|  | 
 |  | +                :icon="InfoFilled"
 | 
											
												
													
														|  | 
 |  | +                icon-color="#626AEF"
 | 
											
												
													
														|  | 
 |  | +                title="确认清空关键词?"
 | 
											
												
													
														|  | 
 |  | +                width="220"
 | 
											
												
													
														|  | 
 |  | +                @confirm="handleClear"
 | 
											
												
													
														|  | 
 |  | +            >
 | 
											
												
													
														|  | 
 |  | +              <template #reference>
 | 
											
												
													
														|  | 
 |  | +                <el-button :icon="Delete" bg size="small" text type="danger">清空</el-button>
 | 
											
												
													
														|  | 
 |  | +              </template>
 | 
											
												
													
														|  | 
 |  | +            </el-popconfirm>
 | 
											
												
													
														|  | 
 |  | +              <el-button :icon="Plus" :loading="btnLoading" bg size="small" text type="primary" @click="handleAdd">添加
 | 
											
												
													
														|  | 
 |  | +              </el-button>
 | 
											
												
													
														|  | 
 |  | +            </div>
 | 
											
												
													
														|  | 
 |  | +          </div>
 | 
											
												
													
														|  | 
 |  | +        </el-col>
 | 
											
												
													
														|  | 
 |  | +        <el-col :span="16">
 | 
											
												
													
														|  | 
 |  | +          <el-card body-style="padding: 0" shadow="never">
 | 
											
												
													
														|  | 
 |  | +            <vxe-grid ref="xGrid" :cell-style="cellStyle" :header-cell-style="headerCellStyle" show-overflow
 | 
											
												
													
														|  | 
 |  | +                      v-bind="gridOptions" v-on="gridEvents" @edit-closed="handelEditClosed">
 | 
											
												
													
														|  | 
 |  | +              <template #operate="{ row }">
 | 
											
												
													
														|  | 
 |  | +                <template v-if="hasActiveEditRow(row)">
 | 
											
												
													
														|  | 
 |  | +                  <el-button link size="small" @click="cancel(row)">取消</el-button>
 | 
											
												
													
														|  | 
 |  | +                  <el-button link size="small" type="warning" @click="updateKeyword(row)">保存</el-button>
 | 
											
												
													
														|  | 
 |  | +                </template>
 | 
											
												
													
														|  | 
 |  | +                <template v-else>
 | 
											
												
													
														|  | 
 |  | +                  <el-button link size="small" type="primary" @click="editRowEvent(row)">编辑</el-button>
 | 
											
												
													
														|  | 
 |  | +                  <el-popconfirm
 | 
											
												
													
														|  | 
 |  | +                      :icon="InfoFilled"
 | 
											
												
													
														|  | 
 |  | +                      icon-color="#626AEF"
 | 
											
												
													
														|  | 
 |  | +                      title="确认删除关键词?"
 | 
											
												
													
														|  | 
 |  | +                      width="220"
 | 
											
												
													
														|  | 
 |  | +                      @confirm="handleDelete(row)"
 | 
											
												
													
														|  | 
 |  | +                  >
 | 
											
												
													
														|  | 
 |  | +                    <template #reference>
 | 
											
												
													
														|  | 
 |  | +                      <el-button :disabled="isEditing" link size="small" type="danger">删除</el-button>
 | 
											
												
													
														|  | 
 |  | +                    </template>
 | 
											
												
													
														|  | 
 |  | +                  </el-popconfirm>
 | 
											
												
													
														|  | 
 |  | +                </template>
 | 
											
												
													
														|  | 
 |  | +              </template>
 | 
											
												
													
														|  | 
 |  | +              <template #keyword_edit="{ row }">
 | 
											
												
													
														|  | 
 |  | +                <el-input v-model="row.keyword"/>
 | 
											
												
													
														|  | 
 |  | +              </template>
 | 
											
												
													
														|  | 
 |  | +            </vxe-grid>
 | 
											
												
													
														|  | 
 |  | +          </el-card>
 | 
											
												
													
														|  | 
 |  | +        </el-col>
 | 
											
												
													
														|  | 
 |  | +      </el-row>
 | 
											
												
													
														|  | 
 |  | +    </div>
 | 
											
												
													
														|  | 
 |  | +  </el-card>
 | 
											
												
													
														|  | 
 |  | +</template>
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +<style scoped>
 | 
											
												
													
														|  | 
 |  | +.input-container {
 | 
											
												
													
														|  | 
 |  | +  position: relative;
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +.textarea {
 | 
											
												
													
														|  | 
 |  | +  width: 100%;
 | 
											
												
													
														|  | 
 |  | +  padding-bottom: 40px; /* 为按钮腾出空间 */
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +.button-group {
 | 
											
												
													
														|  | 
 |  | +  position: absolute;
 | 
											
												
													
														|  | 
 |  | +  bottom: 50px;
 | 
											
												
													
														|  | 
 |  | +  right: 10px;
 | 
											
												
													
														|  | 
 |  | +  /* display: flex; */
 | 
											
												
													
														|  | 
 |  | +  /* gap: 5px; */
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +</style>
 |