123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463 |
- <script setup lang="ts">
- /**
- * @Name: root-word-manage-table.vue
- * @Description: 词根管理表格
- * @Author: Cheney
- */
- import { nextTick, onMounted, reactive, ref } from 'vue';
- import { Plus, Search, Upload } from '@element-plus/icons-vue';
- import * as api from '../api';
- 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;
- 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字段
- popConfirmVisible: boolean; // 添加popConfirmVisible字段
- tempSearchTermType: string; // 添加tempSearchTermType字段
- }
- 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 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' }],
- });
- const searchTermTypeFilter = ref('all');
- const searchTermFilter = ref('');
- const upload = ref<UploadInstance>();
- 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();
- } 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.error('==Error==', error);
- }
- }
- /**
- * 获取关键词列表数据
- */
- async function fetchSearchTermList() {
- tableLoading.value = true;
- const query = {
- page: currentPage.value,
- limit: pageSize.value,
- 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,
- }));
- } catch (error) {
- console.error('==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.error('==Error==', error);
- }
- }
- /**
- * 更新搜索词类型
- * @param row
- */
- async function updateSearchTermType(row: any) {
- 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.error('==Error==', error);
- } finally {
- row.popConfirmVisible = false;
- }
- }
- 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 });
- }
- /**
- * 切换编辑状态, 并自动获取焦点
- * @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();
- });
- }
- function resetForm(formEl: FormInstance | undefined) {
- if (!formEl) return;
- formEl.resetFields();
- dialogVisible.value = false;
- }
- /**
- * 上传文件
- * @param uploadRequest 上传请求
- */
- async function handleCustomUpload(uploadRequest: any) {
- try {
- const { file } = uploadRequest;
- const response = await api.uploadFile(file);
- handleResponse(response);
- uploadRequest.onSuccess(response); // 通知 el-upload 上传成功
- } catch (error) {
- console.log('==Error==', error);
- uploadRequest.onError(error);
- }
- }
- /**
- * @description 替换文件并上传
- * @param files 文件列表
- */
- 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 后端返回的响应
- */
- function handleResponse(response: any) {
- if (response.code === SUCCESS_CODE) {
- ElMessage.success({ message: response.msg, plain: true });
- } else if (response.code === WARNING_CODE) {
- ElMessage.warning({ message: response.msg, plain: true });
- } else {
- ElMessage.error({ message: response.msg, plain: true });
- }
- }
- </script>
- <template>
- <div>
- <el-card v-loading="tableLoading" body-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>
- </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-select>
- </div>
- </div>
- <div class="flex gap-3.5">
- <el-button plain type="primary" @click="handleDialogVisible">
- <el-icon>
- <Plus />
- </el-icon>
- 添加词根
- </el-button>
- <!-- 想要不页面不跳动可以加72的高度 -->
- <div>
- <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-upload>
- </div>
- </div>
- </el-card>
- <!-- 表格 -->
- <el-card shadow="hover" style="border: none">
- <div style="height: 535px; overflow: auto">
- <el-table :data="tableData" stripe style="width: 100%">
- <el-table-column fixed prop="add_date" label="添加日期" width="180" sortable>
- <template #header>
- <el-icon style="top: 2px; margin-right: 3px">
- <Calendar />
- </el-icon>
- <span>添加日期</span>
- </template>
- <template #default="{ row }">
- <span class="font-medium">{{ row.add_date }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="searchTerm" label="词根" sortable>
- <template #header>
- <el-icon style="top: 2px; right: 2px">
- <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>
- </template>
- </el-table-column>
- <el-table-column prop="searchTerm_type" label="词根类型" sortable>
- <template #header>
- <el-icon style="top: 2px; right: 2px">
- <Coin />
- </el-icon>
- <span>词根类型</span>
- </template>
- <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 #header>
- <el-icon style="top: 2px; margin-right: 5px">
- <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-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>
- <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>
- <!-- 添加词根弹窗 -->
- <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>
- <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>
|