Преглед изворни кода

✨ feat: 新增文件上传组件; 包版本升级; 若干细节更新

WanGxC пре 8 месеци
родитељ
комит
93eab7750b

+ 28 - 23
package-lock.json

@@ -63,16 +63,16 @@
 				"@types/sortablejs": "^1.15.0",
 				"@typescript-eslint/eslint-plugin": "^5.46.0",
 				"@typescript-eslint/parser": "^5.46.0",
-				"@vitejs/plugin-vue": "^4.0.0",
+				"@vitejs/plugin-vue": "^4.6.2",
 				"@vue/compiler-sfc": "^3.2.45",
 				"eslint": "^8.29.0",
 				"eslint-plugin-vue": "^9.8.0",
 				"prettier": "^2.8.1",
 				"sass": "^1.56.2",
-				"typescript": "^4.9.4",
+				"typescript": "^5.6.2",
 				"unplugin-auto-import": "^0.16.7",
 				"unplugin-vue-components": "^0.25.2",
-				"vite": "^4.0.0",
+				"vite": "^4.5.3",
 				"vite-plugin-compression": "^0.5.1",
 				"vite-plugin-vue-setup-extend": "^0.4.0",
 				"vue-eslint-parser": "^9.1.0"
@@ -4800,15 +4800,16 @@
 			}
 		},
 		"node_modules/@vitejs/plugin-vue": {
-			"version": "4.4.0",
-			"resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-4.4.0.tgz",
-			"integrity": "sha512-xdguqb+VUwiRpSg+nsc2HtbAUSGak25DXYvpQQi4RVU1Xq1uworyoH/md9Rfd8zMmPR/pSghr309QNcftUVseg==",
+			"version": "4.6.2",
+			"resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-4.6.2.tgz",
+			"integrity": "sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==",
 			"dev": true,
+			"license": "MIT",
 			"engines": {
 				"node": "^14.18.0 || >=16.0.0"
 			},
 			"peerDependencies": {
-				"vite": "^4.0.0",
+				"vite": "^4.0.0 || ^5.0.0",
 				"vue": "^3.2.25"
 			}
 		},
@@ -9357,16 +9358,16 @@
 			"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
 		},
 		"node_modules/typescript": {
-			"version": "4.9.5",
-			"resolved": "https://registry.npmmirror.com/typescript/-/typescript-4.9.5.tgz",
-			"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
+			"version": "5.6.2",
+			"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.6.2.tgz",
+			"integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==",
 			"devOptional": true,
 			"bin": {
 				"tsc": "bin/tsc",
 				"tsserver": "bin/tsserver"
 			},
 			"engines": {
-				"node": ">=4.2.0"
+				"node": ">=14.17"
 			}
 		},
 		"node_modules/ufo": {
@@ -9779,9 +9780,10 @@
 			}
 		},
 		"node_modules/vite": {
-			"version": "4.4.11",
-			"resolved": "https://registry.npmmirror.com/vite/-/vite-4.4.11.tgz",
-			"integrity": "sha512-ksNZJlkcU9b0lBwAGZGGaZHCMqHsc8OpgtoYhsQ4/I2v5cnpmmmqe5pM4nv/4Hn6G/2GhTdj0DhZh2e+Er1q5A==",
+			"version": "4.5.3",
+			"resolved": "https://registry.npmmirror.com/vite/-/vite-4.5.3.tgz",
+			"integrity": "sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==",
+			"license": "MIT",
 			"dependencies": {
 				"esbuild": "^0.18.10",
 				"postcss": "^8.4.27",
@@ -9793,6 +9795,9 @@
 			"engines": {
 				"node": "^14.18.0 || >=16.0.0"
 			},
+			"funding": {
+				"url": "https://github.com/vitejs/vite?sponsor=1"
+			},
 			"optionalDependencies": {
 				"fsevents": "~2.3.2"
 			},
@@ -14362,9 +14367,9 @@
 			}
 		},
 		"@vitejs/plugin-vue": {
-			"version": "4.4.0",
-			"resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-4.4.0.tgz",
-			"integrity": "sha512-xdguqb+VUwiRpSg+nsc2HtbAUSGak25DXYvpQQi4RVU1Xq1uworyoH/md9Rfd8zMmPR/pSghr309QNcftUVseg==",
+			"version": "4.6.2",
+			"resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-4.6.2.tgz",
+			"integrity": "sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==",
 			"dev": true,
 			"requires": {}
 		},
@@ -18025,9 +18030,9 @@
 			"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
 		},
 		"typescript": {
-			"version": "4.9.5",
-			"resolved": "https://registry.npmmirror.com/typescript/-/typescript-4.9.5.tgz",
-			"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
+			"version": "5.6.2",
+			"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.6.2.tgz",
+			"integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==",
 			"devOptional": true
 		},
 		"ufo": {
@@ -18343,9 +18348,9 @@
 			}
 		},
 		"vite": {
-			"version": "4.4.11",
-			"resolved": "https://registry.npmmirror.com/vite/-/vite-4.4.11.tgz",
-			"integrity": "sha512-ksNZJlkcU9b0lBwAGZGGaZHCMqHsc8OpgtoYhsQ4/I2v5cnpmmmqe5pM4nv/4Hn6G/2GhTdj0DhZh2e+Er1q5A==",
+			"version": "4.5.3",
+			"resolved": "https://registry.npmmirror.com/vite/-/vite-4.5.3.tgz",
+			"integrity": "sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==",
 			"requires": {
 				"esbuild": "^0.18.10",
 				"fsevents": "~2.3.2",

+ 3 - 3
package.json

@@ -63,16 +63,16 @@
 		"@types/sortablejs": "^1.15.0",
 		"@typescript-eslint/eslint-plugin": "^5.46.0",
 		"@typescript-eslint/parser": "^5.46.0",
-		"@vitejs/plugin-vue": "^4.0.0",
+		"@vitejs/plugin-vue": "^4.6.2",
 		"@vue/compiler-sfc": "^3.2.45",
 		"eslint": "^8.29.0",
 		"eslint-plugin-vue": "^9.8.0",
 		"prettier": "^2.8.1",
 		"sass": "^1.56.2",
-		"typescript": "^4.9.4",
+		"typescript": "^5.6.2",
 		"unplugin-auto-import": "^0.16.7",
 		"unplugin-vue-components": "^0.25.2",
-		"vite": "^4.0.0",
+		"vite": "^4.5.3",
 		"vite-plugin-compression": "^0.5.1",
 		"vite-plugin-vue-setup-extend": "^0.4.0",
 		"vue-eslint-parser": "^9.1.0"

+ 71 - 0
src/components/FileUploader/index.vue

@@ -0,0 +1,71 @@
+<script lang="ts" setup>
+import { ref, useAttrs } from 'vue';
+import { type ButtonProps, ElMessage, genFileId, UploadInstance, UploadRawFile } from 'element-plus';
+import { Upload } from '@element-plus/icons-vue';
+import { SUCCESS_CODE, WARNING_CODE } from '/@/utils/requestCode';
+
+
+const props = defineProps<Partial<Omit<ButtonProps, 'loading'>>>();
+const attrs = useAttrs() as any;
+
+const emit = defineEmits([ 'upload-success', 'upload-error' ]);
+
+const upload = ref<UploadInstance>();
+const loading = ref(false);
+
+async function handleCustomUpload(uploadRequest: any) {
+  try {
+    loading.value = true;
+    const { file } = uploadRequest;
+    const response = await attrs.api.uploadFile(file);
+    handleResponse(response);
+    uploadRequest.onSuccess(response);
+  } catch (error) {
+    console.log('Error==>', error);
+    uploadRequest.onError(error);
+    emit('upload-error', error);
+  } finally {
+    loading.value = false;
+  }
+}
+
+//
+function handleExceed(files: any) {
+  upload.value!.clearFiles();
+  const file = files[0] as UploadRawFile;
+  file.uid = genFileId();
+  upload.value!.handleStart(file);
+  upload.value!.submit();
+}
+
+//
+function handleResponse(response: any) {
+  if (response.code === SUCCESS_CODE) {
+    ElMessage.success({ message: response.msg, plain: true });
+    emit('upload-success', response);
+  } else if (response.code === WARNING_CODE) {
+    ElMessage.warning({ message: response.msg, plain: true });
+  } else {
+    ElMessage.error({ message: response.msg, plain: true });
+    emit('upload-error', response);
+  }
+}
+</script>
+
+<template>
+  <el-upload
+      ref="upload"
+      :auto-upload="true"
+      :http-request="handleCustomUpload"
+      :limit="1"
+      :on-exceed="handleExceed"
+      :show-file-list="false"
+      action="#"
+      v-bind="attrs">
+    <template #trigger>
+      <el-button :icon="Upload" :loading="loading" plain round type="warning" v-bind="props">
+        <slot></slot>
+      </el-button>
+    </template>
+  </el-upload>
+</template>

+ 13 - 7
src/views/demo/index.vue

@@ -1,14 +1,20 @@
 <template>
-<WeekRangePicker v-model="weekDate" />
+  <el-button :loading="loading" v-bind="props" @click="handleClick"><slot></slot></el-button>
 </template>
 
 <script lang="ts" setup>
-import WeekRangePicker from "/@/components/WeekRangePicker/index.vue";
-import { onMounted } from 'vue'
+import { ref } from 'vue';
+import type { ButtonProps } from 'element-plus';
 
-const weekDate = ref([]);
-onMounted(() => {
 
-console.log('weekDate=>', weekDate.value)
-})
+const loading = ref(false);
+const props = defineProps<Partial<Omit<ButtonProps, 'loading'>>>();
+
+function handleClick() {
+  loading.value = true;
+  console.log('props.disabled=> ', props);
+  setTimeout(() => {
+    loading.value = false;
+  }, 2000);
+}
 </script>

+ 71 - 12
src/views/featureWord/queryPage/FeatureWordTable.vue

@@ -1,19 +1,33 @@
-<script lang="ts" setup>/**
+<script lang="ts" setup>
+/**
  * @Name: FeatureWordTable.vue
  * @Description: 反查关键词表格
  * @Author: Cheney
  */
 
 import { usePagination } from '/@/utils/usePagination';
-import { inject, ref, Ref } from 'vue';
+import { inject, onBeforeMount, onBeforeUnmount, ref, Ref } from 'vue';
 import { useElTableData } from '/@/utils/useElTableData';
 import * as api from '/@/views/featureWord/queryPage/api';
+import emitter from '/@/utils/emitter';
 
 
 const { tableData, total, currentPage, pageSize, handlePageChange } = usePagination(fetchTableData);
 const filter = inject<Ref>('filter');
 const loading = ref(false);
 
+emitter.on('QueryPage-query', () => {
+  fetchTableData();
+});
+
+onBeforeMount(() => {
+  pageSize.value = 20;
+});
+
+onBeforeUnmount(() => {
+  emitter.all.clear();
+});
+
 async function fetchTableData() {
   const query = {
     search_term: filter.value.searchTerm,
@@ -24,9 +38,27 @@ async function fetchTableData() {
     page: currentPage.value,
     limit: pageSize.value
   };
-  const { success, data } = await useElTableData(api.getTableData, query, tableData, total, loading);
-  console.log('(FeatureWordTable.vue: 28)=> data', data);
-  // tableData.value = data;
+  await useElTableData(api.getFeatureWordData, query, tableData, total, loading);
+}
+
+function arraySpanMethod({ row, column, rowIndex, columnIndex }) {
+  if (columnIndex === 0) { // 只对 "特征词" 列进行处理
+    const currentWord = tableData.value[rowIndex].feature_word;
+    let rowspan = 1;
+
+    // 计算有多少连续行具有相同的 feature_word, 包括最后一行
+    while (rowIndex + rowspan < tableData.value.length &&
+    tableData.value[rowIndex + rowspan].feature_word === currentWord) {
+      rowspan++;
+    }
+
+    if (rowIndex === 0 || tableData.value[rowIndex - 1].feature_word !== currentWord) {
+      return [ rowspan, 1 ];
+    } else {
+      return [ 0, 0 ];
+    }
+  }
+  return [ 1, 1 ]; // 对其他列不做特殊处理
 }
 </script>
 
@@ -34,13 +66,40 @@ async function fetchTableData() {
   <el-card shadow="hover" style="border: none; margin-bottom: 10px">
     <el-descriptions title="/ 反查关键词 /"></el-descriptions>
     <el-card shadow="never">
-      <el-table v-loading="loading" :data="tableData" height="800" stripe style="width: 100%">
-        <el-table-column align="center" label="特征词" prop="feature_word"></el-table-column>
-        <el-table-column align="center" label="权重值" prop="weight_value"></el-table-column>
-        <el-table-column align="center" label="关键词" prop="searchTerm"></el-table-column>
-        <el-table-column align="center" label="搜索频次" prop="searchFrequencyRank"></el-table-column>
-        <el-table-column align="center" label="点击分享" prop="clickShare"></el-table-column>
-        <el-table-column align="center" label="转化分享" prop="conversionShare"></el-table-column>
+      <el-table v-loading="loading" :data="tableData" :span-method="arraySpanMethod" height="800" stripe style="width: 100%">
+        <el-table-column align="center" label="特征词" prop="feature_word">
+          <template #default="{ row }">
+            <span class="font-semibold">{{ row.feature_word }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column align="center" label="关键词" prop="searchTerm">
+          <template #default="{ row }">
+            <el-tooltip :content="row.searchTerm" :show-after="500" effect="dark" placement="top-start">
+              <span class="font-semibold line-clamp-1 text-ellipsis">{{ row.searchTerm }}</span>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+        <el-table-column align="center" label="权重值" prop="weight_value">
+          <template #default="{ row }">
+            <span class="font-semibold">{{ row.weight_value }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column align="center" label="搜索频次" prop="searchFrequencyRank">
+          <template #default="{ row }">
+            <span class="font-semibold">{{ row.searchFrequencyRank }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column align="center" label="点击分享" prop="clickShare">
+          <template #default="{ row }">
+            <span class="font-semibold">{{ row.clickShare }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column align="center" label="转化分享" prop="conversionShare">
+          <template #default="{ row }">
+            <span class="font-semibold">{{ row.conversionShare }}</span>
+          </template>
+        </el-table-column>
       </el-table>
       <div class="mt-3.5 flex justify-end">
         <el-pagination

+ 30 - 3
src/views/featureWord/queryPage/WeightTable.vue

@@ -1,9 +1,10 @@
 <script lang="ts" setup>
-import { inject, onBeforeUnmount, Ref, ref } from 'vue';
+import { inject, onBeforeMount, onBeforeUnmount, Ref, ref } from 'vue';
 import { usePagination } from '/@/utils/usePagination';
 import * as api from './api';
 import emitter from '/@/utils/emitter';
 import { useElTableData } from '/@/utils/useElTableData';
+import FileUploader from '/@/components/FileUploader/index.vue';
 
 
 const { tableData, total, currentPage, pageSize, handlePageChange } = usePagination(fetchTableData);
@@ -14,6 +15,10 @@ emitter.on('QueryPage-query', () => {
   fetchTableData();
 });
 
+onBeforeMount(() => {
+  pageSize.value = 20;
+});
+
 onBeforeUnmount(() => {
   emitter.all.clear();
 });
@@ -37,17 +42,39 @@ async function fetchTableData() {
     );
   }
 }
+
+function handleUploadSuccess() {
+  console.log('success=> ');
+}
+
+function handleUploadError() {
+  console.log('Error=> ');
+}
 </script>
 
 <template>
   <el-card shadow="hover" style="border: none; margin-bottom: 10px">
     <el-descriptions title="/ 特征词与权重 /">
       <template #extra>
-        <el-button icon="Upload" plain round type="warning">上传文件</el-button>
+        <FileUploader
+            :api="{ uploadFile: api.uploadFile }"
+            plain
+            round
+            type="warning"
+            @upload-success="handleUploadSuccess"
+            @upload-error="handleUploadError"
+        >文件上传
+        </FileUploader>
+        <!--<Demo type="warning" :disabled="false" plain>上传</Demo>-->
       </template>
     </el-descriptions>
     <el-card shadow="never">
-      <el-table v-loading="loading" :data="tableData" height="600" stripe style="width: 100%">
+      <el-table v-loading="loading" :data="tableData" height="600" style="width: 100%">
+        <el-table-column align="center" type="index" width="60">
+          <template #header>
+            <span>序号</span>
+          </template>
+        </el-table-column>
         <el-table-column align="center" label="关键词" prop="keyword">
           <template #default="{ row }">
             <span class="font-semibold">{{ row.keyword }}</span>

+ 16 - 0
src/views/featureWord/queryPage/api.ts

@@ -10,3 +10,19 @@ export function getTableData(query: any) {
     params: query,
   });
 }
+
+export function getFeatureWordData(query: any) {
+  return request({
+    url: apiPrefix + 'featureword/',
+    method: 'GET',
+    params: query,
+  });
+}
+
+export function uploadFile(body: any) {
+  return request({
+    url: apiPrefix + 'wordsresource/',
+    method: 'POST',
+    data: body,
+  });
+}

+ 12 - 13
src/views/featureWord/queryPage/index.vue

@@ -8,18 +8,19 @@
 import { Download, Search } from '@element-plus/icons-vue';
 import WeightTable from './WeightTable.vue';
 import emitter from '/@/utils/emitter';
-import { onMounted, provide, ref, watch } from 'vue';
+import { onMounted, provide, ref } from 'vue';
 import dayjs from 'dayjs';
 import _ from 'lodash';
 import enLocale from 'element-plus/es/locale/lang/en';
 import FeatureWordTable from '/@/views/featureWord/queryPage/FeatureWordTable.vue';
 
+
 const date = ref(calculateLastMonthFirstWeek());
 const dateRange = ref(date.value[0]);
 
 const filter = ref({
-  searchTerm: '',
-  marketIds: '',
+  searchTerm: 'zosi',
+  marketIds: 'ATVPDKIKX0DER',
   reportType: 'WEEKLY',
   reportDate: date
 });
@@ -68,7 +69,7 @@ function handleQuery() {
 }
 
 function handleDownload() {
-  // Implement download logic here
+
 }
 
 
@@ -88,7 +89,7 @@ function handleDownload() {
         </div>
         <div>
           <span class="font-bold mr-2" style="color: #303133">报告类型:</span>
-          <el-select v-model="filter.reportType" @change="calculateDate" style="width: 100px">
+          <el-select v-model="filter.reportType" style="width: 100px" @change="calculateDate">
             <el-option label="月度" value="MONTHLY"></el-option>
             <el-option label="周度" value="WEEKLY"></el-option>
           </el-select>
@@ -98,26 +99,24 @@ function handleDownload() {
           <el-config-provider v-if="filter.reportType === 'WEEKLY'" :locale="enLocale">
             <el-date-picker
                 v-model="dateRange"
+                :clearable="false"
+                :disabled-date="(time: Date) => time > new Date()"
                 :format="`${date[0]} To ${date[1]}`"
                 type="week"
                 value-format="YYYY-MM-DD"
-                :disabled-date="(time: Date) => time > new Date()"
-                :clearable="false"
                 @change="calculateDate"
             />
           </el-config-provider>
           <el-date-picker
               v-else
               v-model="dateRange"
-              type="month"
+              :clearable="false"
+              :disabled-date="(time: Date) => time > new Date()"
               :format="`${date[0]} To ${date[1]}`"
+              type="month"
               value-format="YYYY-MM-DD"
-              :disabled-date="(time: Date) => time > new Date()"
-              :clearable="false"
               @change="calculateDate"
           />
-          <!--<MonthRangePicker v-if="filter.reportType === 'MONTHLY'" v-model="monthDate"/>-->
-          <!--<WeekRangePicker v-else v-model="weekDate"/>-->
         </div>
       </div>
       <div class="flex gap-3.5">
@@ -126,7 +125,7 @@ function handleDownload() {
       </div>
     </el-card>
     <WeightTable/>
-    <FeatureWordTable />
+    <FeatureWordTable/>
   </div>
 </template>
 

+ 65 - 59
src/views/searchTerm/rootWordManage/components/root-word-manage-table.vue

@@ -1,4 +1,4 @@
-<script setup lang="ts">
+<script lang="ts" setup>
 /**
  * @Name: root-word-manage-table.vue
  * @Description: 词根管理表格
@@ -12,6 +12,7 @@ import type { UploadInstance, UploadRawFile } from 'element-plus';
 import { ElMessage, FormInstance, FormRules, genFileId } from 'element-plus';
 import { SUCCESS_CODE, WARNING_CODE } from '/@/utils/requestCode';
 
+
 interface DataItem {
   id: number;
   modifier_name: string;
@@ -44,11 +45,11 @@ const formSearchTermInpRef = ref();
 const ruleFormRef = ref<FormInstance>();
 const ruleForm = reactive({
   searchTerm: '',
-  searchTermType: 'positive',
+  searchTermType: 'positive'
 });
 const rules = reactive<FormRules<typeof ruleForm>>({
-  searchTerm: [{ required: true, validator: checkSearchTerm, trigger: 'blur' }],
-  searchTermType: [{ required: true, validator: checkSearchTermType, trigger: 'blur' }],
+  searchTerm: [ { required: true, validator: checkSearchTerm, trigger: 'blur' } ],
+  searchTermType: [ { required: true, validator: checkSearchTermType, trigger: 'blur' } ]
 });
 
 const searchTermTypeFilter = ref('all');
@@ -66,7 +67,7 @@ async function addSearchTerm() {
   const body = {
     searchTerm: ruleForm.searchTerm,
     searchTerm_type: ruleForm.searchTermType,
-    add_date: currentDate, // 使用当前日期代替硬编码日期
+    add_date: currentDate // 使用当前日期代替硬编码日期
   };
   try {
     const response = await api.postCreateSearchTerm(body);
@@ -105,7 +106,7 @@ async function fetchSearchTermList() {
     page: currentPage.value,
     limit: pageSize.value,
     searchTerm: searchTermFilter.value,
-    searchTerm_type: searchTermTypeFilter.value,
+    searchTerm_type: searchTermTypeFilter.value
   };
   try {
     const response = await api.getSearchTermList(query);
@@ -115,7 +116,7 @@ async function fetchSearchTermList() {
       ...item,
       isEditing: false,
       popConfirmVisible: false,
-      tempSearchTermType: item.searchTerm_type,
+      tempSearchTermType: item.searchTerm_type
     }));
   } catch (error) {
     console.error('==Error==', error);
@@ -134,7 +135,7 @@ async function updateSearchTerm(row: any) {
   row.isEditing = false;
   const data = {
     searchTerm: row.searchTerm,
-    searchTerm_type: row.searchTerm_type,
+    searchTerm_type: row.searchTerm_type
   };
   try {
     const response = await api.postUpdateSearchTerm({ data, id: row.id });
@@ -156,7 +157,7 @@ async function updateSearchTerm(row: any) {
 async function updateSearchTermType(row: any) {
   const data = {
     searchTerm: row.searchTerm,
-    searchTerm_type: row.searchTerm_type,
+    searchTerm_type: row.searchTerm_type
   };
   try {
     const response = await api.postUpdateSearchTerm({ data, id: row.id });
@@ -311,48 +312,48 @@ function handleResponse(response: any) {
 </script>
 
 <template>
-  <div class="p-2.5" v-loading="tableLoading" style="background-color: #f7f7f7">
+  <div v-loading="tableLoading" class="p-2.5" style="background-color: #f7f7f7">
     <!-- 筛选 -->
     <el-card body-class="flex justify-between gap-3.5" shadow="hover" style="border: none; margin-bottom: 10px">
       <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>
+              v-model="searchTermFilter"
+              :prefix-icon="Search"
+              clearable
+              placeholder="请输入词根"
+              style="width: 240px"
+              @change="fetchSearchTermList"></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="all" />
-            <el-option label="positive" value="positive" />
-            <el-option label="negative" value="negative" />
-            <el-option label="无词根类型" value="typeless" />
+            <el-option label="全部" value="all"/>
+            <el-option label="positive" value="positive"/>
+            <el-option label="negative" value="negative"/>
+            <el-option label="无词根类型" value="typeless"/>
           </el-select>
         </div>
       </div>
       <div class="flex gap-3.5">
         <el-button plain type="primary" @click="handleDialogVisible">
           <el-icon>
-            <Plus />
+            <Plus/>
           </el-icon>
           添加词根
         </el-button>
         <!-- 想要不页面不跳动可以加72的高度 -->
         <div>
           <el-upload
-            ref="upload"
-            action="#"
-            :limit="1"
-            :auto-upload="true"
-            :on-exceed="handleExceed"
-            :http-request="handleCustomUpload">
+              ref="upload"
+              :auto-upload="true"
+              :http-request="handleCustomUpload"
+              :limit="1"
+              :on-exceed="handleExceed"
+              action="#">
             <template #trigger>
-              <el-button plain round type="warning" :icon="Upload">批量词根上传</el-button>
+              <el-button :icon="Upload" plain round type="warning">批量词根上传</el-button>
             </template>
           </el-upload>
         </div>
@@ -362,10 +363,15 @@ function handleResponse(response: any) {
     <el-card shadow="hover" style="border: none">
       <div style="height: 800px">
         <el-table :data="tableData" height="800" stripe style="width: 100%">
-          <el-table-column fixed prop="add_date" label="添加日期" width="180" sortable>
+          <el-table-column align="center" type="index" width="60">
+            <template #header>
+              <span>序号</span>
+            </template>
+          </el-table-column>
+          <el-table-column align="center" label="添加日期" prop="add_date" sortable>
             <template #header>
               <el-icon style="top: 2px; margin-right: 3px">
-                <Calendar />
+                <Calendar/>
               </el-icon>
               <span>添加日期</span>
             </template>
@@ -373,35 +379,35 @@ function handleResponse(response: any) {
               <span class="font-medium">{{ row.add_date }}</span>
             </template>
           </el-table-column>
-          <el-table-column prop="searchTerm" label="词根" sortable>
+          <el-table-column label="词根" prop="searchTerm" sortable>
             <template #header>
               <el-icon style="top: 2px; right: 2px">
-                <Key />
+                <Key/>
               </el-icon>
               <span>词根</span>
             </template>
             <template #default="{ row }">
-              <el-input ref="searchTermInpRef" v-if="row.isEditing" v-model="row.searchTerm" @change="updateSearchTerm(row)" />
-              <span class="font-semibold" v-else>{{ row.searchTerm }}</span>
+              <el-input v-if="row.isEditing" ref="searchTermInpRef" v-model="row.searchTerm" @change="updateSearchTerm(row)"/>
+              <span v-else class="font-semibold">{{ row.searchTerm }}</span>
             </template>
           </el-table-column>
-          <el-table-column prop="searchTerm_type" label="词根类型" sortable>
+          <el-table-column label="词根类型" prop="searchTerm_type" sortable>
             <template #header>
               <el-icon style="top: 2px; right: 2px">
-                <Coin />
+                <Coin/>
               </el-icon>
               <span>词根类型</span>
             </template>
             <template #default="{ row }">
               <el-popconfirm
-                title="确定修改吗?"
-                @confirm="updateSearchTermType(row)"
-                @cancel="cancelUpdate(row)"
-                :visible="row.popConfirmVisible">
+                  :visible="row.popConfirmVisible"
+                  title="确定修改吗?"
+                  @cancel="cancelUpdate(row)"
+                  @confirm="updateSearchTermType(row)">
                 <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 v-model="row.searchTerm_type" style="width: 150px" @change="showPopConfirm(row)">
+                    <el-option label="positive" value="positive"/>
+                    <el-option label="negative" value="negative"/>
                   </el-select>
                 </template>
               </el-popconfirm>
@@ -410,16 +416,16 @@ function handleResponse(response: any) {
           <el-table-column fixed="right" label="操作" width="120">
             <template #header>
               <el-icon style="top: 2px; margin-right: 5px">
-                <EditPen />
+                <EditPen/>
               </el-icon>
               <span>操作</span>
             </template>
             <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 v-if="!row.isEditing" link size="small" type="primary" @click="handleClick(row)"> 编辑</el-button>
+              <el-button v-else link size="small" type="primary" @click="handleClick(row)"> 取消</el-button>
               <el-popconfirm title="确定删除吗?" @confirm="handleDelete(row)">
                 <template #reference>
-                  <el-button link type="danger" size="small">删除</el-button>
+                  <el-button link size="small" type="danger">删除</el-button>
                 </template>
               </el-popconfirm>
             </template>
@@ -428,27 +434,27 @@ function handleResponse(response: any) {
       </div>
       <div class="mt-3.5 flex justify-end">
         <el-pagination
-          v-model:current-page="currentPage"
-          v-model:page-size="pageSize"
-          :page-sizes="[20, 40, 50, 100]"
-          layout="total, sizes, prev, pager, next"
-          :total="total"
-          @size-change="handleSizeChange"
-          @current-change="handleCurrentChange" />
+            v-model:current-page="currentPage"
+            v-model:page-size="pageSize"
+            :page-sizes="[20, 40, 50, 100]"
+            :total="total"
+            layout="total, sizes, prev, pager, next"
+            @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-dialog v-model="dialogVisible" :before-close="handleClose" title="添加关键词" width="500">
+    <el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="auto" status-icon style="max-width: 600px">
       <el-form-item label="关键词" prop="searchTerm">
-        <el-input ref="formSearchTermInpRef" v-model="ruleForm.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-option label="positive" value="positive"/>
+          <el-option label="negative" value="negative"/>
         </el-select>
       </el-form-item>
     </el-form>

+ 17 - 17
vite.config.ts

@@ -1,30 +1,30 @@
-import vue from '@vitejs/plugin-vue'
-import { resolve } from 'path'
-import { defineConfig, loadEnv, ConfigEnv } from 'vite'
-import vueSetupExtend from 'vite-plugin-vue-setup-extend'
-import vueJsx from '@vitejs/plugin-vue-jsx'
-import AutoImport from 'unplugin-auto-import/vite'
-import compression from 'vite-plugin-compression'
-import Components from 'unplugin-vue-components/vite'
-import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
+import vue from '@vitejs/plugin-vue';
+import { resolve } from 'path';
+import { defineConfig, loadEnv, ConfigEnv } from 'vite';
+import vueSetupExtend from 'vite-plugin-vue-setup-extend';
+import vueJsx from '@vitejs/plugin-vue-jsx';
+import AutoImport from 'unplugin-auto-import/vite';
+import compression from 'vite-plugin-compression';
+import Components from 'unplugin-vue-components/vite';
+import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
 
 const pathResolve = (dir: string) => {
-  return resolve(__dirname, '.', dir)
-}
+  return resolve(__dirname, '.', dir);
+};
 
 const alias: Record<string, string> = {
   '/@': pathResolve('./src/'),
   '@views': pathResolve('./src/views'),
   'vue-i18n': 'vue-i18n/dist/vue-i18n.cjs.js',
-}
+};
 
 const viteConfig = defineConfig((mode: ConfigEnv) => {
-  const env = loadEnv(mode.mode, process.cwd())
+  const env = loadEnv(mode.mode, process.cwd());
   return {
     plugins: [
       vue(),
       vueJsx(),
-      vueSetupExtend(),
+      // vueSetupExtend(),
       AutoImport({
         imports: ['vue', 'vue-router', 'pinia'],
         // resolvers: [ElementPlusResolver()],
@@ -83,7 +83,7 @@ const viteConfig = defineConfig((mode: ConfigEnv) => {
       __INTLIFY_PROD_DEVTOOLS__: JSON.stringify(false),
       __VERSION__: JSON.stringify(process.env.npm_package_version),
     },
-  }
-})
+  };
+});
 
-export default viteConfig
+export default viteConfig;