Middle-题目108:79. Word Search

本文介绍了一个二维矩阵中查找特定单词的算法实现。采用深度优先搜索(DFS)策略,从矩阵中每个可能的起点开始,递归地寻找目标单词。文章提供了Java代码示例,并详细解释了如何避免重复访问同一单元格。

题目原文:
Given a 2D board and a word, find if the word exists in the grid.
The word can be constructed from letters of sequentially adjacent cell, where “adjacent” cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once.
For example,
Given board =
[
[‘A’,’B’,’C’,’E’],
[‘S’,’F’,’C’,’S’],
[‘A’,’D’,’E’,’E’]
]
word = “ABCCED”, -> returns true,
word = “SEE”, -> returns true,
word = “ABCB”, -> returns false.
题目大意:
给出一个矩阵,和一个单词,判断单词是否能由矩阵中一条路径连接出来。
题目分析:
一个简单的dfs,教科书式的dfs写法即可,搜到字母错误或者搜到数组边缘则退出,否则每次向四个方向搜索,注意这里的dfs是有返回值的,返回该条件下是否有解。
源码:(language:java)

public class Solution {
    public boolean exist(char[][] board, String word) {
        int row = board.length;
        if(row==0)
            return false;
        int col = board[0].length;
        boolean[][] visited = new boolean[row][col];
        for(int i = 0;i<row;i++) {
            for(int j=0;j<col;j++) {            
                if(word.charAt(0) == board[i][j]) {
                    if(dfs(board, i, j, visited, word, 0))
                        return true;  
                }
            }
        }
        return false;
    }

    private boolean dfs(char[][] board, int i, int j, boolean[][] visited, String word, int index) {
        if(index == word.length())
            return true;
        else if(i<0 || i>board.length-1 || j<0 || j>board[0].length-1 || word.charAt(index)!=board[i][j] || visited[i][j])
            return false;
        else {
            visited[i][j]=true;
            boolean result = dfs(board, i-1, j, visited, word, index+1) || dfs(board, i+1, j, visited, word, index+1) || dfs(board, i, j-1, visited, word, index+1) || dfs(board, i, j+1, visited, word, index+1);
            visited[i][j] = false;
            return result;
        }

    }
}

成绩:
13ms,beats 58.16%,众数14ms,14.58%
cmershen的碎碎念:
本题基本上是一个教科书式的dfs,区别是dfs带有返回值判断是否有解。

有下述代码,其中代码1、代码2和代码3是java代码,其中上传文件接口如下代码1中的uploadInstitution()方法,在代码4中,修改一下uploadInstitution()方法(允许多个附件上传),请修改代码4和代码5中的附件上传的功能: ### 代码1:LearnController.java ```java package com.zhiruan.business.learn.controller; import com.zhiruan.business.learn.service.LearnService; import com.zhiruan.core.entity.business.learn.Learn; import com.zhiruan.core.web.controller.BaseController; import com.zhiruan.core.web.domain.AjaxResult; import com.zhiruan.core.web.page.TableDataInfo; import com.zhiruan.log.annotation.Log; import com.zhiruan.log.enums.BusinessType; import com.zhiruan.security.annotation.RequiresPermissions; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import java.util.List; /** * @Author: HZH * @ClassName: LearnController * @Description TODO * @version: 1.0v * @DATE: 2025/9/19 23:37 */ @RestController @RequestMapping("/learn") public class LearnController extends BaseController { @Resource LearnService learnService; /** * 01-制度学习/业务学习-上传 */ @RequiresPermissions({"learn:institution:upload","learn:business:upload"}) @Log(title = "制度学习", businessType = BusinessType.INSERT) @PostMapping("/institution/upload") public AjaxResult uploadInstitution(@RequestParam("file") MultipartFile[] files, Long[] learnIds){ learnService.uploadLearnFile(files, learnIds); return success(); } } ``` ### 代码2:LearnService.java ```java package com.zhiruan.business.learn.service; import com.baomidou.mybatisplus.extension.service.IService; import com.zhiruan.core.entity.business.file.FileInfo; import com.zhiruan.core.entity.business.learn.Learn; import org.springframework.web.multipart.MultipartFile; import java.util.List; /** * @Author: HZH * @ClassName: LearnService * @Description TODO * @version: 1.0v * @DATE: 2025/9/19 23:39 */ public interface LearnService extends IService<Learn> { boolean uploadLearnFile(MultipartFile[] files, Long[] learnIds); } ``` ### 代码3:LearnServiceImpl.java ```java /** * 批量上传 * @param files 上传的文件数组 * @param learnIds 关联的学习ID数组 * @return */ @Override @Transactional(rollbackFor = Exception.class) public boolean uploadLearnFile(MultipartFile[] files, Long[] learnIds) { // 校验文件数组是否为空 if (Objects.isNull(files) || files.length == 0) { log.error("uploadLearnFile: 上传的文件数组为空"); throw new ServiceException("附件上传失败:未选择任何文件"); } // 校验关联的学习ID数组 if (Objects.isNull(learnIds) || learnIds.length == 0) { log.error("uploadLearnFile: 关联的学习ID数组为空"); throw new ServiceException("附件上传失败:未指定关联的学习记录"); } // 遍历每个上传的文件 for (MultipartFile file : files) { // 校验单个文件是否为空 if (Objects.isNull(file) || file.isEmpty()) { log.warn("uploadLearnFile: 跳过空文件"); continue; // 跳过空文件,继续处理其他文件 } // 每个文件关联所有learnIds for (Long learnId : learnIds) { FileInfo fileInfo = FileInfo.builder() .linkId(learnId) // 绑定当前学习记录ID .fileClazz("6") // 保持原业务标识 .build(); // 调用文件服务保存单个文件 iFileService.addFile(file, fileInfo); } } return true; // 原代码返回false可能是笔误,成功应返回true } ``` ### 代码4:src\api\learn\institution\institution.js ```js import request from '@/utils/request' // 制度学习-查询列表 export function list(query){ return request({ url: '/business/learn/institution/list', method: 'get', params: query }) } // 制度学习-新增 export function addInstitution(data){ return request({ url: '/business/learn/institution/add', method: 'post', data: data }) } // 制度学习-确认 export function delUser(learnId) { return request({ url: '/business/learn/institution/confirm/' + learnId, method: 'get' }) } // 制度学习-详情 export function getInstitutionById(learnId) { return request({ url: '/business/learn/institution/getById/' + learnId, method: 'get' }) } // 制度学习-编辑 export function editInstitution(data) { return request({ url: '/business/learn/institution/edit', method: 'put', data: data }) } // 制度学习-删除 export function removeInstitution(learnId) { return request({ url: '/business/learn/institution/remove/' + learnId, method: 'delete' }) } // 制度学习-上传附件 export function uploadInstitution(file, learnIds) { const formData = new FormData(); // 添加文件参数,与Java接口的@RequestParam("file")对应 formData.append('file', file); // 添加learnIds数组参数,后端需要接收Long[]类型 if (learnIds && learnIds.length) { learnIds.forEach(id => { formData.append('learnIds', id); }); } return request({ url: '/business/learn/institution/upload', method: 'post', data: formData, // 上传文件需要指定Content-Type为multipart/form-data headers: { 'Content-Type': 'multipart/form-data' } }); } // 查看附件详情 export function getInstitutionFile(learnId, clazz) { return request({ url: `/business/learn/institution/getFile/${learnId}/${clazz}`, method: 'get' }); } ``` ### 代码5:src\views\learn\business\index.vue ```vue <template> <div class="app-container"> <!-- 搜索表单 --> <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="80px" > <el-form-item label="行政区划" prop="areaId"> <el-tree-select v-model="queryParams.areaId" :data="deptOptions" :props="{ value: 'deptId', label: 'deptName', children: 'children' }" value-key="deptId" placeholder="请选择行政区划" clearable style="width: 240px" @change="handleQuery" :check-strictly="true" /> </el-form-item> <el-form-item label="所在部门" prop="deptId"> <el-tree-select v-model="queryParams.deptId" :data="deptOptions" :props="{ value: 'deptId', label: 'deptName', children: 'children' }" value-key="deptId" placeholder="请选择所在部门" clearable style="width: 240px" @change="handleQuery" :check-strictly="true" /> </el-form-item> <el-form-item label="制度名称" prop="learnName"> <el-input v-model="queryParams.learnName" placeholder="请输入制度名称" clearable style="width: 240px" @change="handleQuery" @keyup.enter="handleQuery" /> </el-form-item> <el-form-item> <el-button type="primary" icon="Search" @click="handleQuery" >搜索</el-button > <el-button icon="Refresh" @click="resetQuery">重置</el-button> </el-form-item> </el-form> <!-- 操作按钮 --> <el-row :gutter="10" class="mb8"> <el-col :span="1.5"> <el-button type="primary" plain icon="Upload" @click="handleUpload" v-hasPermi="['learn:business:upload']" :disabled="multiple" >上传</el-button > </el-col> <el-col :span="1.5"> <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['learn:business:add']" >新增</el-button > </el-col> <el-col :span="1.5"> <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['learn:business:remove']" >删除</el-button > </el-col> <el-col :span="1.5"> <el-button type="warning" plain icon="Check" :disabled="isAdmin || !canBatchConfirm" @click="handleConfirm" v-hasPermi="['learn:business:confirm']" >学习</el-button > </el-col> <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" ></right-toolbar> </el-row> <!-- 数据表格 --> <el-table v-loading="loading" :data="institutionList" @selection-change="handleSelectionChange" > <el-table-column type="selection" width="55" align="center" /> <el-table-column label="序号" align="center" prop="learnId" width="60" /> <el-table-column label="行政区划" align="center" prop="areaName" :show-overflow-tooltip="true" /> <el-table-column label="所在部门" align="center" prop="deptName" :show-overflow-tooltip="true" /> <el-table-column label="制度名称" align="center" prop="learnName" :show-overflow-tooltip="true" /> <el-table-column label="是否生效" align="center" prop="isEnable"> <template #default="scope"> <el-tag :type="scope.row.isEnable === '1' ? 'success' : 'info'"> {{ scope.row.isEnable === "1" ? "已生效" : "未生效" }} </el-tag> </template> </el-table-column> <el-table-column label="上传时间" align="center" prop="enableTime" width="180" > <template #default="scope"> <span>{{ scope.row.enableTime ? parseTime(scope.row.enableTime) : "-" }}</span> </template> </el-table-column> <!-- 角色相关的动态列 --> <template v-if="isAdminTypeRole"> <el-table-column label="学习人数" align="center" prop="learnNumber" width="120" /> <el-table-column label="未学习人数" align="center" prop="unLearnNumber" width="120" /> </template> <template v-else> <el-table-column label="是否学习" align="center" prop="isConfirm"> <template #default="scope"> <el-tag :type="scope.row.isConfirm === '1' ? 'success' : 'warning'"> {{ scope.row.isConfirm === "1" ? "已学习" : "未学习" }} </el-tag> </template> </el-table-column> <el-table-column label="学习时间" align="center" prop="learnTime" width="180" > <template #default="scope"> <span>{{ scope.row.learnTime ? parseTime(scope.row.learnTime) : "-" }}</span> </template> </el-table-column> </template> <el-table-column label="操作" align="center" width="360" class-name="small-padding fixed-width" > <template #default="scope"> <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['learn:business:edit']" >编辑</el-button > <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['learn:business:remove']" >删除</el-button > <el-button link type="primary" icon="Check" @click="handleConfirm(scope.row)" v-hasPermi="['learn:business:confirm']" :disabled="isAdmin || scope.row.isConfirm === '1'" > 学习 </el-button> <el-button link type="primary" icon="Document" @click="handleViewFiles(scope.row.learnId)" v-hasPermi="['learn:business:viewFile']" > 查看附件 </el-button> </template> </el-table-column> </el-table> <!-- 分页 --> <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <!-- 新增/编辑对话框 --> <el-dialog :title="title" v-model="open" width="500px" append-to-body> <el-form ref="institutionRef" :model="form" :rules="rules" label-width="100px" > <el-form-item label="行政区划" prop="areaId"> <el-tree-select v-model="form.areaId" :data="deptOptions" :props="{ value: 'deptId', label: 'deptName', children: 'children', }" value-key="deptId" placeholder="请选择行政区划" clearable :check-strictly="true" /> </el-form-item> <el-form-item label="所在部门" prop="deptId"> <el-tree-select v-model="form.deptId" :data="deptOptions" :props="{ value: 'deptId', label: 'deptName', children: 'children', }" value-key="deptId" placeholder="请选择所在部门" clearable :check-strictly="true" /> </el-form-item> <el-form-item label="制度名称" prop="learnName"> <el-input v-model="form.learnName" placeholder="请输入制度名称" /> </el-form-item> <el-form-item label="是否生效" prop="isEnable"> <el-radio-group v-model="form.isEnable"> <el-radio label="0">未生效</el-radio> <el-radio label="1">已生效</el-radio> </el-radio-group> </el-form-item> </el-form> <template #footer> <div class="dialog-footer"> <el-button type="primary" @click="submitForm">确 定</el-button> <el-button @click="cancel">取 消</el-button> </div> </template> </el-dialog> <!-- 上传对话框 --> <el-dialog title="上传制度文件" v-model="uploadOpen" width="680px" append-to-body class="custom-dialog" > <el-form ref="uploadRef" :model="uploadForm" label-width="100px"> <!-- 选择制度:每行5个,显示前3个字,超出用 tooltip 展示完整名称 --> <el-form-item label="选择制度" prop="selectedItems"> <div class="institution-grid"> <el-tooltip v-for="item in selectedInstitutions" :key="item.learnId" effect="dark" :content="item.learnName" placement="top" > <div class="institution-cell"> <el-checkbox v-model="uploadForm.selectedItems" :label="item.learnId" class="institution-btn" > {{ shortName(item.learnName) }} </el-checkbox> </div> </el-tooltip> </div> </el-form-item> <el-form-item label="上传文件" prop="files"> <el-upload class="upload-area" drag action="" :on-change="handleFileChange" :auto-upload="false" :file-list="fileList" accept=".doc,.docx,.pdf,.txt,.jpg,.jpeg,.png,.gif,.html" :multiple="true" > <el-icon class="el-icon--upload"><UploadFilled /></el-icon> <div class="el-upload__text"> 将文件拖拽到此处,或 点击选择文件 </div> <template #tip> <div class="upload-tip"> 支持 .pdf, .jpg, .jpeg, .png, .gif 格式文件<br /> 单个文件大小不超过 <b>1MB</b> </div> </template> </el-upload> </el-form-item> </el-form> <template #footer> <div class="dialog-footer"> <el-button type="primary" @click="submitUpload">上传</el-button> <el-button @click="cancelUpload">取 消</el-button> </div> </template> </el-dialog> <!-- 附件查看对话框 --> <el-dialog title="附件详情" v-model="fileDialogOpen" width="850px" append-to-body class="custom-dialog" > <div v-loading="fileLoading"> <el-empty v-if="fileList.length === 0" description="暂无附件" ></el-empty> <el-table v-else :data="fileList" border stripe style="width: 100%"> <!-- 文件名 --> <el-table-column label="文件名" prop="fileName" min-width="200" show-overflow-tooltip align="center" /> <!-- 文件内容:区分图片/非图片 --> <el-table-column label="文件预览" prop="fileUrl" min-width="300" align="center" > <template #default="scope"> <!-- 图片类型 --> <template v-if="/\.(jpg|jpeg|png|gif)$/i.test(scope.row.fileUrl)"> <el-image :src="scope.row.fileUrl" :preview-src-list="[scope.row.fileUrl]" preview-teleported fit="cover" style=" width: 80px; height: 80px; border-radius: 6px; cursor: pointer; " /> </template> <!-- PDF 文件 --> <template v-else-if="/\.pdf$/i.test(scope.row.fileUrl)"> <a :href="scope.row.fileUrl" target="_blank"> <el-icon size="32" color="#E34133"><Document /></el-icon> </a> </template> <!-- Word 文件 --> <template v-else-if="/\.(doc|docx)$/i.test(scope.row.fileUrl)"> <a :href="scope.row.fileUrl" target="_blank"> <el-icon size="32" color="#2A5699"><DocumentAdd /></el-icon> </a> </template> <!-- 其他文件 --> <template v-else> <a :href="scope.row.fileUrl" target="_blank"> <el-icon size="32" color="#606266"><Files /></el-icon> </a> </template> </template> </el-table-column> <!-- 上传人 --> <el-table-column label="创建人用户" prop="userName" align="center" width="140" /> <!-- 上传时间 --> <el-table-column label="上传时间" prop="createTime" align="center" width="160" > <template #default="scope"> <span>{{ scope.row.createTime ? parseTime(scope.row.createTime, "{y}-{m}-{d} {h}:{i}:{s}") : "-" }}</span> </template> </el-table-column> </el-table> </div> <template #footer> <div class="dialog-footer"> <el-button type="primary" @click="fileDialogOpen = false" >关 闭</el-button > </div> </template> </el-dialog> <!-- 制度确认对话框 --> <el-dialog :title="'制度确认'" v-model="confirmDialogVisible" width="600px" center append-to-body :close-on-click-modal="false" :close-on-press-escape="false" > <div class="text-center mb-4"> 本人已阅读该项制度并承诺在职期间遵守本项制度; </div> <template #footer> <div class="dialog-footer flex justify-center space-x-4"> <el-button type="primary" @click="handleSign">签字</el-button> <el-button @click="confirmDialogVisible = false">返回</el-button> </div> </template> </el-dialog> </div> </template> <script setup name="InstitutionLearn"> import { ref, reactive, toRefs, getCurrentInstance, computed } from "vue"; import { UploadFilled } from "@element-plus/icons-vue"; import { list, addInstitution, getInstitutionById, editInstitution, removeInstitution, delUser as confirmInstitution, uploadInstitution, getInstitutionFile, } from "@/api/learn/institution/institution"; import { getInfo } from "@/api/login"; import { listDept } from "@/api/system/dept"; // 获取当前组件实例 const { proxy } = getCurrentInstance(); // 用户角色相关 const userRoles = ref([]); const isAdminTypeRole = computed(() => { // 检查是否包含管理员类型角色 const adminRoles = ["admin", "deptLead", "opManager"]; return userRoles.value.some((role) => adminRoles.includes(role)); }); // 新增:判断当前用户是否为超级管理员(admin角色) const isAdmin = computed(() => { return userRoles.value.includes("admin"); }); // 部门数据(同时用于行政区划和所在部门) const deptOptions = ref([]); // 表格数据 const institutionList = ref([]); const loading = ref(true); const total = ref(0); const showSearch = ref(true); const open = ref(false); const uploadOpen = ref(false); const fileDialogOpen = ref(false); const title = ref(""); // 选中项 const ids = ref([]); const single = ref(true); const multiple = ref(true); const selectedInstitutions = ref([]); // 选中的制度列表,用于上传附件 // 制度确认对话框相关 const confirmDialogVisible = ref(false); const currentConfirmIds = ref([]); // 新增:判断是否可以进行批量确认(基于选中项的isConfirm状态) const canBatchConfirm = computed(() => { if (ids.value.length === 0) return false; const selectedItems = institutionList.value.filter((item) => ids.value.includes(item.learnId) ); return selectedItems.every((item) => item.isConfirm === "0"); }); // 文件相关 const fileList = ref([]); const currentLearnId = ref(""); // 当前查看附件的制度ID const fileLoading = ref(false); // 上传表单数据 const uploadForm = reactive({ selectedItems: [], // 选中的制度ID }); // 表单数据 const data = reactive({ form: { learnId: undefined, areaId: undefined, deptId: undefined, learnName: undefined, isEnable: "0", // 默认未生效 areaName: undefined, // 用于显示 deptName: undefined, // 用于显示 }, queryParams: { pageNum: 1, pageSize: 10, areaId: undefined, deptId: undefined, learnName: undefined, isEnable: undefined, learnClazz: 2, }, rules: { areaId: [{ required: true, message: "行政区划不能为空", trigger: "blur" }], deptId: [{ required: true, message: "所在部门不能为空", trigger: "blur" }], learnName: [ { required: true, message: "制度名称不能为空", trigger: "blur" }, ], }, }); const { form, queryParams, rules } = toRefs(data); /** 将制度名称仅显示前三个字符(中文/英文都适用) */ function shortName(name = "") { if (!name) return ""; // 如果长度小于等于3,直接返回;否则返回前三个字符(不加省略号,根据你的要求) return name.length <= 3 ? name : name.slice(0, 3); } /** 获取部门列表数据 - 同时用于行政区划和所在部门 */ function getDeptList() { listDept() .then((response) => { // 处理树形结构,保留所有层级 deptOptions.value = proxy.handleTree(response.data, "deptId"); }) .catch((error) => { console.error("获取部门列表失败:", error); }); } /** 获取用户信息和角色 */ function getUserInfo() { getInfo() .then((response) => { // 存储用户角色 userRoles.value = response.roles || []; // 获取部门数据 getDeptList(); // 获取数据列表 getList(); }) .catch((error) => { console.error("获取用户信息失败:", error); loading.value = false; }); } /** 查询制度学习列表 */ function getList() { loading.value = true; list(queryParams.value) .then((response) => { institutionList.value = response.rows || []; total.value = response.total || 0; loading.value = false; }) .catch((error) => { console.error("获取数据列表失败:", error); loading.value = false; }); } /** 取消按钮 */ function cancel() { open.value = false; reset(); } /** 取消上传 */ function cancelUpload() { uploadOpen.value = false; fileList.value = []; uploadForm.selectedItems = []; } /** 表单重置 */ function reset() { form.value = { learnId: undefined, areaId: undefined, deptId: undefined, learnName: undefined, isEnable: "0", areaName: undefined, deptName: undefined, }; proxy.resetForm("institutionRef"); } /** 搜索按钮操作 */ function handleQuery() { queryParams.value.pageNum = 1; // 搜索时重置为第一页 getList(); } /** 重置按钮操作 */ function resetQuery() { proxy.resetForm("queryRef"); // 清空查询参数 queryParams.value = { pageNum: 1, pageSize: 10, areaId: undefined, deptId: undefined, learnName: undefined, isEnable: undefined, learnClazz: 2 }; handleQuery(); } /** 新增按钮操作 */ function handleAdd() { reset(); open.value = true; title.value = "新增制度学习"; } /** 上传按钮操作 */ function handleUpload() { // 打开弹窗前,强制清空上一次的文件列表和选中状态 fileList.value = []; // 清空文件列表 uploadForm.selectedItems = []; // 清空选中的制度ID // 存储当前选中的制度 selectedInstitutions.value = institutionList.value.filter((item) => ids.value.includes(item.learnId) ); uploadForm.selectedItems = [...ids.value]; uploadOpen.value = true; } /** 文件变化处理 */ function handleFileChange(file, newFileList) { fileList.value = newFileList; } /** 提交上传 */ function submitUpload() { if (fileList.value.length === 0) { proxy.$modal.msgError("请选择文件"); return; } if (uploadForm.selectedItems.length === 0) { proxy.$modal.msgError("请选择要关联的制度"); return; } // 处理多文件上传 const uploadPromises = fileList.value.map((file) => { return uploadInstitution(file.raw, uploadForm.selectedItems); }); loading.value = true; Promise.all(uploadPromises) .then(() => { proxy.$modal.msgSuccess("文件上传成功"); cancelUpload(); getList(); }) .catch((error) => { proxy.$modal.msgError( "文件上传失败: " + (error.msg || error.message || "未知错误") ); }) .finally(() => { loading.value = false; }); } /** 多选框选中数据 */ function handleSelectionChange(selection) { ids.value = selection.map((item) => item.learnId); single.value = selection.length !== 1; multiple.value = !selection.length; } /** 修改按钮操作 */ function handleUpdate(row) { reset(); const learnId = row ? row.learnId : ids.value[0]; getInstitutionById(learnId).then((response) => { form.value = { ...response.data, areaId: response.data.areaId, deptId: response.data.deptId, }; open.value = true; title.value = "编辑制度学习"; }); } /** 提交按钮 */ function submitForm() { proxy.$refs["institutionRef"].validate((valid) => { if (valid) { // 提交前获取部门名称用于显示 const areaItem = findDeptById(deptOptions.value, form.value.areaId); const deptItem = findDeptById(deptOptions.value, form.value.deptId); const submitData = { ...form.value, areaName: areaItem ? areaItem.deptName : "", deptName: deptItem ? deptItem.deptName : "", learnClazz: 2 }; if (form.value.learnId !== undefined) { editInstitution(submitData).then((response) => { proxy.$modal.msgSuccess("修改成功"); open.value = false; getList(); }); } else { addInstitution(submitData).then((response) => { proxy.$modal.msgSuccess("新增成功"); open.value = false; getList(); }); } } }); } /** 递归查找部门 - 支持所有层级查找 */ function findDeptById(deptList, deptId) { for (const dept of deptList) { if (dept.deptId === deptId) { return dept; } if (dept.children && dept.children.length > 0) { const found = findDeptById(dept.children, deptId); if (found) return found; } } return null; } /** 删除按钮操作 */ function handleDelete(row) { const learnIds = row.learnId || ids.value; proxy.$modal .confirm("是否确认删除选中的" + learnIds.length + "条数据?") .then(function () { return removeInstitution(learnIds); }) .then(() => { getList(); proxy.$modal.msgSuccess("删除成功"); }) .catch(() => {}); } /** 确认按钮操作 - 显示自定义制度确认弹窗 */ function handleConfirm(row) { // 新增:超级管理员直接禁止操作 if (isAdmin.value) { proxy.$modal.msgWarning("超级管理员不允许执行确认操作"); return; } const learnIds = row.learnId || ids.value; if (learnIds.length === 0) { proxy.$modal.msgWarning("请选择要确认的数据"); return; } // 获取选中项详情 const selectedItems = row ? [row] : institutionList.value.filter((item) => learnIds.includes(item.learnId)); // 检查是否存在已确认数据(isConfirm=1) const hasConfirmed = selectedItems.some((item) => item.isConfirm === "1"); if (hasConfirmed) { proxy.$modal.msgWarning("存在已确认的数据,不可重复确认"); return; } // 显示自定义确认弹窗,并记录要确认的ID currentConfirmIds.value = learnIds; confirmDialogVisible.value = true; } /** 处理签字确认操作 */ function handleSign() { // 1. 统一将数据处理为数组(兼容单条/多条) let learnIds = currentConfirmIds.value; // 如果不是数组(单条数据场景),包装成数组 if (!Array.isArray(learnIds)) { learnIds = [learnIds]; } // 校验数据有效性 if (!learnIds || learnIds.length === 0) { proxy.$modal.msgWarning("请选择要确认的数据"); return; } // 2. 转为逗号分隔字符串(适配后端Long[]解析) const batchIds = learnIds.join(","); // 3. 调用接口 loading.value = true; confirmInstitution(batchIds) .then(() => { confirmDialogVisible.value = false; getList(); proxy.$modal.msgSuccess("确认成功"); }) .catch((error) => { proxy.$modal.msgError( "确认失败: " + (error.msg || error.message || "未知错误") ); }) .finally(() => { loading.value = false; }); } /** 查看附件 */ function handleViewFiles(learnId) { currentLearnId.value = learnId; fileDialogOpen.value = true; fileLoading.value = true; // 调用接口获取附件列表,clazz固定为6 getInstitutionFile(learnId, "6") .then((response) => { fileList.value = response.data || []; fileLoading.value = false; }) .catch((error) => { console.error("获取附件列表失败:", error); proxy.$modal.msgError("获取附件失败"); fileLoading.value = false; }); } /** 下载文件 */ function downloadFile(file) { // 这里实现文件下载逻辑,根据实际接口调整 proxy.download( `/business/learn/institution/download/${file.fileId}`, {}, file.fileName ); } // 初始化时加载数据 getUserInfo(); </script> <style scoped> .text-red { color: #ff4d4f; } .text-gray-500 { color: #909399; font-size: 12px; } /* 上传区样式 */ .upload-area { width: 100%; border: 2px dashed #d9d9d9; border-radius: 8px; background: #fafafa; } .upload-tip { margin-top: 6px; font-size: 12px; color: #666; line-height: 1.5; } .custom-dialog ::v-deep(.el-dialog__header) { font-weight: bold; font-size: 16px; } /* --- 选择制度:使用 Grid,保证每行固定 5 个并对齐 --- */ .institution-grid { display: grid; grid-template-columns: repeat(5, 1fr); /* 固定 5 列 */ gap: 8px 10px; /* 行间距8px,列间距10px */ align-items: center; margin-top: 0px; } /* 每个单元格,水平垂直居中 */ .institution-cell { display: flex; align-items: center; justify-content: center; } /* checkbox 占满单元格,内容居中,显示前三个字 */ .institution-btn { display: flex; align-items: center; justify-content: center; width: 100%; height: 32px; padding: 2px 6px; box-sizing: border-box; border-radius: 6px; background: #f5f7fa; border: 1px solid #dcdfe6; font-size: 13px; color: #333; overflow: hidden; white-space: nowrap; text-overflow: clip; /* 不显示省略号,只显示前三个字符由 shortName 控制 */ } /* 调整内部复选框图标与文字间距 */ .institution-btn .el-checkbox__inner { margin-right: 6px; } /* 悬浮效果 */ .institution-btn:hover { background: #ecf5ff; border-color: #409eff; } /* 超长文本显示省略号 */ .ellipsis { display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: middle; } /* 文件链接样式 */ .file-link { color: #409eff; text-decoration: none; word-break: break-all; } .file-link:hover { text-decoration: underline; } .ellipsis-text { display: inline-block; max-width: 200px; /* 或者根据实际UI调整 */ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; vertical-align: middle; } </style> ```
09-29
### `.middle-wrap` 样式分析 #### `.middle-wrap>div` 该选择器用于直接子元素为 `<div>` 的 `.middle-wrap` 元素,定义了以下样式: - `padding-left: 0; padding-right: 10px;`:设置左侧内边距为 0,右侧为 10px,使得内容在左侧对齐,右侧留出空间[^3]。 - `color: #fff;`:文本颜色设置为白色。 - `text-align: center;`:文本内容水平居中显示。 - `height: 100px;`:固定高度为 100px。 ```css .middle-wrap>div { padding-left: 0; padding-right: 10px; color: #fff; text-align: center; height: 100px; } ``` #### `.middle-wrap>div>div` 该选择器针对 `.middle-wrap` 下的二级 `<div>` 元素,设置了固定高度为 100px,确保内部元素在垂直方向上占据相同空间,可能用于对齐或布局控制。 ```css .middle-wrap>div>div { height: 100px; } ``` #### `.middle-wrap h4` 该选择器作用于 `.middle-wrap` 中的 `<h4>` 标签,去除了默认的外边距和内边距,并设置了字体大小为 22px,字体粗细为正常,用于统一标题样式。 ```css .middle-wrap h4 { margin: 0; padding: 5px 0; font-size: 22px; font-weight: normal; } ``` #### `.middle-wrap a` 该选择器用于 `.middle-wrap` 中的链接元素,将光标样式设置为指针,提示用户该元素可点击。 ```css .middle-wrap a { cursor: pointer; } ``` #### `.middle-wrap h4:before` 该伪元素在 `<h4>` 标签前插入一个空白内容,设置为 `inline-block` 显示模式,宽度和高度均为 38px,右侧外边距为 10px,通常用于添加图标或装饰性元素。 ```css .middle-wrap h4:before { content: ""; display: inline-block; width: 38px; height: 38px; margin-right: 10px; } ``` #### `.middle-wrap h4 span` 该选择器作用于 `<h4>` 标签内的 `<span>` 元素,设置其为相对定位,顶部偏移 -6px,用于微调内部文本或图标的垂直位置。 ```css .middle-wrap h4 span { position: relative; top: -6px; } ``` #### `.middle-wrap p` 该选择器用于 `.middle-wrap` 中的段落 `<p>` 元素,设置字体大小为 40px,字体加粗,无外边距,行高为 40px,确保文本在垂直方向上居中显示。 ```css .middle-wrap p { font-size: 40px; font-weight: bold; margin: 0; line-height: 40px; } ``` #### `.middle-wrap h5` 该选择器作用于 `.middle-wrap` 中的 `<h5>` 标签,去除默认外边距,设置内边距为上下 20px 和 5px,行高为 20px,透明度为 0.75,字体大小为 14px,用于副标题或说明性文本。 ```css .middle-wrap h5 { margin: 0; padding: 20px 0 5px; line-height: 20px; opacity: 0.75; font-size: 14px; } ``` ### 功能总结 这些样式定义了一个具有固定高度和视觉层次的容器 `.middle-wrap`,其中包含多个子元素,如标题、段落和链接,每个元素都有明确的布局和视觉表现。整体设计旨在创建一个视觉上统一、内容居中且易于阅读的区块,适用于卡片式布局或信息展示区域。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值