【从前端到后端导入excel文件实现批量导入-笔记模仿芋道源码的《系统管理-用户管理-导入-批量导入》】

news2025/6/9 11:05:05

批量导入预约数据-笔记

  • 前端
    • 场馆列表
    • 后端

前端

场馆列表

该列表进入出现的是这样的,这儿是列表操作

      <el-table-column label="操作" align="center" width="220px">
        <template #default="scope">
          <el-button
            link
            type="primary"
            @click="openForm('update', scope.row.id, scope.row.name)"
            v-hasPermi="['sports:venue:update']"
          >
            编辑
          </el-button>
          <el-button
            link
            type="primary"
            @click="openImportForm('update', scope.row.id, scope.row.name, scope.row.capacity)"
            v-hasPermi="['sports:venue:update']"
          >
            批量导入预约
          </el-button>
          <el-button
            link
            type="danger"
            @click="handleDelete(scope.row.id)"
            v-hasPermi="['sports:venue:delete']"
          >
            删除
          </el-button>
        </template>
      </el-table-column>

批量导入操作在这儿

          <el-button
            link
            type="primary"
            @click="openImportForm('update', scope.row.id, scope.row.name, scope.row.capacity)"
            v-hasPermi="['sports:venue:update']"
          >
            批量导入预约
          </el-button>
  <!-- 批量导入 -->
  <UserImportForm ref="importFormRef" @success="getList" />
  import UserImportForm from './UserImportForm.vue'
  
  
  // 批量导入
	const importFormRef = ref()
	const openImportForm = (type: string, id?: number, name?: string, capacity: number) => {
  if (!importFormRef.value) {
    console.error('importFormRef 未正确绑定')
    return
  }
  	importFormRef.value.open(type, id, name)
}

这是导入的弹窗

<template>
  <Dialog v-model="dialogVisible" title="批量导入场馆预约" width="480">
    <el-form-item class="form-item">
      <el-date-picker
        v-model="bookingDates"
        type="date"
        value-format="YYYY-MM-DD"
        placeholder="请选择预约的日期"
        :disabled-date="disabledDate"
        :clearable="true"
        class="custom-date-picker"
      />
    </el-form-item>
    <el-upload
      ref="uploadRef"
      v-model:file-list="fileList"
      :action="importUrl"
      :auto-upload="false"
      :disabled="formLoading"
      :headers="uploadHeaders"
      :limit="1"
      :on-error="submitFormError"
      :on-exceed="handleExceed"
      :on-success="submitFormSuccess"
      accept=".xlsx, .xls"
      drag
      class="upload-area"
    >
      <Icon icon="ep:upload" />
      <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
      <template #tip>
        <div class="el-upload__tip text-center">
          <span>仅允许导入 xls、xlsx 格式文件。</span>
          <el-link
            :underline="false"
            style="font-size: 12px; vertical-align: baseline"
            type="primary"
            @click="importTemplate"
          >
            下载模板
          </el-link>
        </div>
      </template>
    </el-upload>
    <template #footer>
      <el-button :disabled="formLoading" type="primary" @click="submitForm" class="submit-button">
        确 定
      </el-button>
      <el-button @click="dialogVisible = false" class="cancel-button">取 消</el-button>
    </template>
  </Dialog>
</template>

<script lang="ts" setup>
import { ref, nextTick, defineExpose } from 'vue'
import axios from 'axios'
import { getAccessToken, getTenantId } from '@/utils/auth'
import * as VenueBookingApi from '@/api/sports/venuebooking'
import download from '@/utils/download'
import { getUserProfile } from '@/api/system/user/profile'
defineOptions({ name: 'VenueBookingImportForm' })

const message = useMessage()

const dialogVisible = ref(false)
const formLoading = ref(false)
const uploadRef = ref()
const importUrl =
  import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + '/sports/venue-booking/import'
const uploadHeaders = ref()
const fileList = ref([])
const updateSupport = ref(0)
const bookingDates = ref([]) // 日期范围
const venueId = ref(null) // 用于存储场馆ID
const venueName = ref('') // 用于存储场馆名称

// 用于打开导入对话框并传入参数
const open = (type: string, id: number, name: string) => {
  dialogVisible.value = true
  venueId.value = id // 设置 venueId
  venueName.value = name // 设置 venueName
  updateSupport.value = 0
  fileList.value = []
  bookingDates.value = [] // 重置日期范围
  
  resetForm()
}

defineExpose({ open })

// 限制选择日期的函数(例如,不能选择过去的日期)
const disabledDate = (date: Date) => {
  return date.getTime() < Date.now() // 禁止选择过去的日期
}

// 提交表单
const submitForm = async () => {
  if (!validateForm()) return

  uploadHeaders.value = {
    Authorization: 'Bearer ' + getAccessToken(),
    'tenant-id': getTenantId()
  }
  formLoading.value = true

  // 创建 FormData 对象,包含文件和其他参数
  const formData = new FormData()
  formData.append('file', fileList.value[0].raw)
  formData.append('venueId', venueId.value) // 场馆 ID
  formData.append('venueName', venueName.value) // 场馆名称
  formData.append('bookingTime', bookingDates.value) // 预约日期
  formData.append('updateSupport', updateSupport.value ? 'true' : 'false')
 // 获取用户资料并添加到 formData 中

  // 发起请求
  try {
    const res = await getUserProfile();  // 假设 getUserProfile 是异步函数
    formData.append('agent', res.nickname);  // 将 nickname 作为 agent 添加到 FormData 中
    formData.append('agentId', res.id);
    const response = await axios.post(importUrl, formData, {
      headers: uploadHeaders.value
    })

    // 检查接口返回的 response 格式,如果是包含 code 的结构
    if (response.data && response.data.code === 0) {
      // 上传成功
      message.alertSuccess('批量导入成功!!')
      submitFormSuccess(response.data)
    } else {
      // 上传失败,显示错误信息
      submitFormError(response.data)
    }
  } catch (error) {
    submitFormError(error) // 请求失败
  }
}

// 失败回调
const submitFormError = (error: any): void => {
  if (error && error.msg) {
    message.error(error.msg || '上传失败,请重新上传!')
  } else {
    message.error('上传失败,请重新上传!')
  }
  formLoading.value = false
}

// 成功回调
const submitFormSuccess = (response: any) => {
  if (response.code !== 0) {
    message.error(response.msg || '上传失败')
    formLoading.value = false
    return
  }
  // 完成后恢复状态
  formLoading.value = false
  dialogVisible.value = false
}

// 重置表单
const resetForm = async (): Promise<void> => {
  formLoading.value = false
  await nextTick()
  uploadRef.value?.clearFiles()
}

// 处理文件超出限制
const handleExceed = (): void => {
  message.error('最多只能上传一个文件!')
}

// 下载模板
const importTemplate = async () => {
  const res = await VenueBookingApi.importUserTemplate()
  download.excel(res, '用户导入模版.xls')
}

// 验证表单输入
const validateForm = () => {
  if (!fileList.value.length) {
    message.error('请上传文件')
    return false
  }

  if (!bookingDates.value || bookingDates.value.length === 0) {
    message.error('请先选择预约日期')
    return false
  }

  return true
}
</script>

<style scoped>
.form-item {
  margin-left: 110px;
}
</style>

// 下载用户导入模板
export const importUserTemplate = () => {
  return request.download({ url: '/sports/venue-booking/get-import-template' })
}

下载用户模板插件

const download0 = (data: Blob, fileName: string, mineType: string) => {
  // 创建 blob
  const blob = new Blob([data], { type: mineType })
  // 创建 href 超链接,点击进行下载
  window.URL = window.URL || window.webkitURL
  const href = URL.createObjectURL(blob)
  const downA = document.createElement('a')
  downA.href = href
  downA.download = fileName
  downA.click()
  // 销毁超连接
  window.URL.revokeObjectURL(href)
}

const download = {
  // 下载 Excel 方法
  excel: (data: Blob, fileName: string) => {
    download0(data, fileName, 'application/vnd.ms-excel')
  },
  // 下载 Word 方法
  word: (data: Blob, fileName: string) => {
    download0(data, fileName, 'application/msword')
  },
  // 下载 Zip 方法
  zip: (data: Blob, fileName: string) => {
    download0(data, fileName, 'application/zip')
  },
  // 下载 Html 方法
  html: (data: Blob, fileName: string) => {
    download0(data, fileName, 'text/html')
  },
  // 下载 Markdown 方法
  markdown: (data: Blob, fileName: string) => {
    download0(data, fileName, 'text/markdown')
  },
  // 下载图片(允许跨域)
  image: ({
    url,
    canvasWidth,
    canvasHeight,
    drawWithImageSize = true
  }: {
    url: string
    canvasWidth?: number // 指定画布宽度
    canvasHeight?: number // 指定画布高度
    drawWithImageSize?: boolean // 将图片绘制在画布上时带上图片的宽高值, 默认是要带上的
  }) => {
    const image = new Image()
    // image.setAttribute('crossOrigin', 'anonymous')
    image.src = url
    image.onload = () => {
      const canvas = document.createElement('canvas')
      canvas.width = canvasWidth || image.width
      canvas.height = canvasHeight || image.height
      const ctx = canvas.getContext('2d') as CanvasRenderingContext2D
      ctx?.clearRect(0, 0, canvas.width, canvas.height)
      if (drawWithImageSize) {
        ctx.drawImage(image, 0, 0, image.width, image.height)
      } else {
        ctx.drawImage(image, 0, 0)
      }
      const url = canvas.toDataURL('image/png')
      const a = document.createElement('a')
      a.href = url
      a.download = 'image.png'
      a.click()
    }
  }
}

export default download

后端

用户模板下载

    @GetMapping("/get-import-template")
    @Operation(summary = "获得导入用户模板")
    public void importTemplate(HttpServletResponse response) throws IOException {
        // 手动创建导出 demo
        List<UserImportDemoExcelVO> list = Arrays.asList(
                UserImportDemoExcelVO.builder().registrant("张三").phone("15601691300").idCard("522324198805060010").build());

        // 输出
        ExcelUtils.write(response, "预约用户导入模板.xls", "预约用户列表", UserImportDemoExcelVO.class, list);
    }
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = false) // 设置 chain = false,避免用户导入有问题
public class UserImportDemoExcelVO {

//    @ExcelProperty("场馆id")
//    private Long venueId;
//
//    @ExcelProperty("场馆名称")
//    private String venueName;

    @ExcelProperty("预定人姓名")
    private String registrant;

    @ExcelProperty("预定人联系方式")
    private String phone;

    @ExcelProperty("预定人身份证号")
    private String idCard;

//    @ExcelProperty("预约时间")
//    private LocalDateTime bookingTime;

}

导入模板


    @PostMapping("/import")
    @Operation(summary = "导入场馆预约数据")
    @Parameters({
            @Parameter(name = "file", description = "Excel 文件", required = true),
            @Parameter(name = "venueId", description = "场馆 ID", required = true),
            @Parameter(name = "venueName", description = "场馆名称", required = true),
            @Parameter(name = "bookingTime", description = "预约时间", required = true),
            @Parameter(name = "agent", description = "代理预约人", required = true),
            @Parameter(name = "agentId", description = "代理id", required = true),
            @Parameter(name = "updateSupport", description = "是否支持更新,默认为 false", example = "false")
    })
    @PreAuthorize("@ss.hasPermission('sports:venue:booking:import')")
    public CommonResult<VenueBookingRespVO> importExcel(@RequestParam("file") MultipartFile file,
                                                        @RequestParam("venueId") Long venueId,
                                                        @RequestParam("agentId") Long agentId,
                                                        @RequestParam("venueName") String venueName,
                                                        @RequestParam("bookingTime") String bookingTime,
                                                        @RequestParam("agent") String agent,
                                                        @RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) throws Exception {

        // 解析 Excel 文件为对象列表
        List<UserImportExcelVO> list = ExcelUtils.read(file, UserImportExcelVO.class);

        // 调用 service 层进行导入,获取导入结果
        CommonResult<VenueBookingRespVO> result = venueBookingService.importVenueBookingList(list, venueId, venueName,agent,agentId, bookingTime, updateSupport);

        // 返回服务层的响应
        return result;
    }

@Schema(description = "管理后台 - 观赛预约记录 Response VO")
@Data
@ExcelIgnoreUnannotated
public class VenueBookingRespVO {
    @ExcelProperty("代理预定人id")
    private Long id;
    @Schema(description = "预定人")
    @ExcelProperty("预定人")
    private String registrant;
    @Schema(description = "预定人")
    @ExcelProperty("代理预定人")
    private String agent;
    @Schema(description = "代理预定人id", example = "19070")
    @ExcelProperty("代理预定人id")
    private Long agentId;

    @Schema(description = "预定人员电话号码")
    @ExcelProperty("预定人员电话号码")
    private String phone;

    @Schema(description = "预定人员身份证")
    @ExcelProperty("预定人员身份证")
    private String idCard;

    @Schema(description = "预约时间")
    @ExcelProperty("预约时间")
    private LocalDateTime bookingTime;

    @Schema(description = "场馆id", example = "19070")
    @ExcelProperty("场馆id")
    private Long venueId;

    @Schema(description = "场馆名称")
    @ExcelProperty("场馆名称")
    private String venueName;

    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
    private String message;

    // 带有多个字段的构造方法
    public VenueBookingRespVO(String message, String registrant, String phone, String idCard,
                              LocalDateTime bookingTime, Long venueId, String venueName, LocalDateTime createTime) {
        this.message = message;
        this.registrant = registrant;
        this.phone = phone;
        this.idCard = idCard;
        this.bookingTime = bookingTime;
        this.venueId = venueId;
        this.venueName = venueName;
        this.createTime = createTime;
    }

}

/**
 * 用户 Excel 导入 VO
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = false) // 设置 chain = false,避免用户导入有问题
public class UserImportExcelVO {

    @ExcelProperty("场馆id")
    private Long venueId;

    @ExcelProperty("场馆名称")
    private String venueName;

    @ExcelProperty("预定人姓名")
    private String registrant;

    @ExcelProperty("预定人联系方式")
    private String phone;

    @ExcelProperty("预定人身份证号")
    private String idCard;

    @ExcelProperty("预约时间")
    private LocalDateTime bookingTime;

}
    @Override
    @Transactional(rollbackFor = Exception.class)
    public CommonResult<VenueBookingRespVO> importVenueBookingList(List<UserImportExcelVO> list, Long venueId, String venueName, String agent, Long agentId, String bookingTime, Boolean updateSupport) {
        if (list == null || list.isEmpty()) {
            return CommonResult.error(400, "导入的数据不能为空!");
        }

        List<String> errorMessages = new ArrayList<>();
        LocalDateTime bookingDateTime = LocalDateTime.parse(bookingTime + "T00:00:00");

        // 身份证号和手机号验证正则表达式
        String idCardRegex = "^[1-9]\\d{5}(?:18|19|20)\\d{2}(?:0[1-9]|10|11|12)(?:0[1-9]|[1-2]\\d|30|31)\\d{3}[\\dXx]$";
        Pattern idCardPattern = Pattern.compile(idCardRegex);
        String phoneRegex = "^1[3-9]\\d{9}$";
        Pattern phonePattern = Pattern.compile(phoneRegex);

        List<VenueBookingDO> venueBookingList = new ArrayList<>();
        Set<String> idCardSet = new HashSet<>();  // 用于存储已存在的身份证号,查重

        for (int i = 0; i < list.size(); i++) {
            UserImportExcelVO excelVO = list.get(i);
            errorMessages.clear();  // 清空错误信息列表

            // 验证身份证号格式
            Matcher idCardMatcher = idCardPattern.matcher(excelVO.getIdCard());
            if (!idCardMatcher.matches()) {
                errorMessages.add("第" + (i + 1) + "条记录:身份证号格式不正确");
            }

            // 验证手机号格式
            Matcher phoneMatcher = phonePattern.matcher(excelVO.getPhone());
            if (!phoneMatcher.matches()) {
                errorMessages.add("第" + (i + 1) + "条记录:手机号格式不正确");
            }

            // 检查身份证号是否重复
            if (idCardSet.contains(excelVO.getIdCard())) {
                errorMessages.add("第" + (i + 1) + "条记录:身份数据重复");
            } else {
                idCardSet.add(excelVO.getIdCard());  // 加入已存在身份证号集合
            }

            // 如果有错误,返回错误信息并终止插入
            if (!errorMessages.isEmpty()) {
                return CommonResult.error(400, String.join(",", errorMessages));  // 使用 error 方法返回错误信息
            }

            // 对身份证号进行加密处理
            String encryptedIdCard = Sm2Util.signMd5(excelVO.getIdCard());
            excelVO.setVenueId(venueId);
            excelVO.setVenueName(venueName);
            excelVO.setBookingTime(bookingDateTime);
            excelVO.setIdCard(encryptedIdCard);

            VenueBookingDO venueBookingDO = new VenueBookingDO();
            venueBookingDO.setVenueId(excelVO.getVenueId());
            venueBookingDO.setVenueName(excelVO.getVenueName());
            venueBookingDO.setBookingTime(excelVO.getBookingTime());
            venueBookingDO.setIdCard(excelVO.getIdCard());  // 设置加密后的身份证号
            venueBookingDO.setPhone(excelVO.getPhone());
            venueBookingDO.setRegistrant(excelVO.getRegistrant());
            venueBookingDO.setAgent(agent);
            venueBookingDO.setAgentId(agentId);
            venueBookingDO.setBookingStatus(0);

            venueBookingList.add(venueBookingDO);
        }

        // 批量插入数据
        if (!venueBookingList.isEmpty()) {
            venueBookingMapper.insertBatch(venueBookingList);
        }

        // 返回成功的响应,填充所有字段
        return CommonResult.success(new VenueBookingRespVO("导入成功", "registrantData", "12345678901", "123456789012345678", bookingDateTime, venueId, venueName, LocalDateTime.now()));
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2405278.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LabVIEW音频测试分析

LabVIEW通过读取指定WAV 文件&#xff0c;实现对音频信号的播放、多维度测量分析功能&#xff0c;为音频设备研发、声学研究及质量检测提供专业工具支持。 主要功能 文件读取与播放&#xff1a;支持持续读取示例数据文件夹内的 WAV 文件&#xff0c;可实时播放音频以监听被测信…

RoseMirrorHA 双机热备全解析

在数字化时代&#xff0c;企业核心业务系统一旦瘫痪&#xff0c;每分钟可能造成数万甚至数十万的损失。想象一下&#xff0c;如果银行的交易系统突然中断&#xff0c;或者医院的挂号系统无法访问&#xff0c;会引发怎样的连锁反应&#xff1f;为了守护这些关键业务&#xff0c;…

day 18进行聚类,进而推断出每个簇的实际含义

浙大疏锦行 对聚类的结果根据具体的特征进行解释&#xff0c;进而推断出每个簇的实际含义 两种思路&#xff1a; 你最开始聚类的时候&#xff0c;就选择了你想最后用来确定簇含义的特征&#xff0c; 最开始用全部特征来聚类&#xff0c;把其余特征作为 x&#xff0c;聚类得到…

LLMs 系列科普文(6)

截止到目前&#xff0c;我们从模型预训练阶段的数据准备讲起&#xff0c;谈到了 Tokenizer、模型的结构、模型的训练&#xff0c;基础模型、预训练阶段、后训练阶段等&#xff0c;这里存在大量的术语或名词&#xff0c;也有一些奇奇怪怪或者说是看起来乱七八糟的内容。这期间跳…

serv00 ssh登录保活脚本-邮件通知版

适用于自己有服务器情况&#xff0c;ssh定时登录到serv00&#xff0c;并在登录成功后发送邮件通知 msmtp 和 mutt安装 需要安装msmtp 和 mutt这两个邮件客户端并配置&#xff0c;参考如下文章前几步是讲配置这俩客户端的&#xff0c;很简单&#xff0c;不再赘述 用Shell脚本实…

意识上传伦理前夜:我们是否在创造数字奴隶?

当韩国财阀将“数字永生”标价1亿美元准入权时&#xff0c;联合国预警的“神经种姓制度”正从科幻步入现实。某脑机接口公司用户协议中“上传意识衍生算法归公司所有”的隐藏条款&#xff0c;恰似德里达预言的当代印证&#xff1a;“当意识沦为可交易数据流&#xff0c;主体性便…

【AIGC】RAGAS评估原理及实践

【AIGC】RAGAS评估原理及实践 &#xff08;1&#xff09;准备评估数据集&#xff08;2&#xff09;开始评估2.1 加载数据集2.2 评估忠实性2.3 评估答案相关性2.4 上下文精度2.5 上下文召回率2.6 计算上下文实体召回率 RAGas&#xff08;RAG Assessment)RAG 评估的缩写&#xff…

ESP12E/F 参数对比

模式GPIO0GPIO2GPIO15描述正常启动高高低从闪存运行固件闪光模式低高低启用固件刷写 PinNameFunction1RSTReset (Active Low)2ADC (A0)Analog Input (0–1V)3EN (CH_PD)Chip Enable (Pull High for Normal Operation)4GPIO16Wake from Deep Sleep, General Purpose I/O5GPIO14S…

第二十八章 字符串与数字

第二十八章 字符串与数字 计算机程序完全就是和数据打交道。很多编程问题需要使用字符串和数字这种更小的数据来解决。 参数扩展 第七章,已经接触过参数扩展,但未进行详细说明,大多数参数扩展并不用于命令行,而是出现在脚本文件中。 如果没有什么特殊原因,把参数扩展放…

[RDK X5] MJPG编解码开发实战:从官方API到OpenWanderary库的C++/Python实现

业余时间一直在基于RDK X5搞一些小研究&#xff0c;需要基于高分辨率图像检测目标。实际落地时&#xff0c;在图像采集上遇到了个大坑。首先&#xff0c;考虑到可行性&#xff0c;我挑选了一个性价比最高的百元内摄像头&#xff0c;已确定可以在X5上使用&#xff0c;接下来就开…

aardio 简单网页自动化

WebView自动化&#xff0c;以前每次重复做网页登录、搜索这些操作时都觉得好麻烦&#xff0c;现在终于能让程序替我干活了&#xff0c;赶紧记录下这个超实用的技能&#xff01; 一、初次接触WebView WebView自动化就像给程序装了个"网页浏览器"&#xff0c;第一步得…

打卡第39天:Dataset 和 Dataloader类

知识点回顾&#xff1a; 1.Dataset类的__getitem__和__len__方法&#xff08;本质是python的特殊方法&#xff09; 2.Dataloader类 3.minist手写数据集的了解 作业&#xff1a;了解下cifar数据集&#xff0c;尝试获取其中一张图片 import torch import torch.nn as nn import…

如何做好一份优秀的技术文档:专业指南与最佳实践

如何做好一份优秀的技术文档&#xff1a;专业指南与最佳实践 技术文档是产品开发、用户支持和团队协作的核心工具。高质量的技术文档能够提升开发效率、降低维护成本并改善用户体验。本文将从实践出发&#xff0c;详细讲解如何编写专业、清晰且实用的技术文档。 &#x1f31f;…

TCP相关问题 第一篇

TCP相关问题1 1.TCP主动断开连接方为什么需要等待2MSL 如上图所示:在被动链接方调用close&#xff0c;发送FIN时进入LAST_ACK状态&#xff0c;但未收到主动连接方的ack确认&#xff0c;需要被动连接方重新发送一个FIN&#xff0c;而为什么是2MSL&#xff0c;一般认为丢失ack在…

6.Pandas 数据可视化图-1

第三章 数据可视化 文章目录 目录 第三章 数据可视化 文章目录 前言 一、数据可视化 二、使用步骤 1.pyplot 1.1引入库 1.2 设置汉字字体 1.3 数据准备 1.4 设置索引列 ​编辑 1.5 调用绘图函数 2.使用seaborn绘图 2.1 安装导入seaborn 2.2 设置背景风格 2.3 调用绘图方法 2.…

软件功能测试报告都包含哪些内容?

软件功能测试报告是软件开发生命周期中的重要文档&#xff0c;主要涵盖以下关键内容&#xff1a;    1.测试概况&#xff1a;概述测试目标、范围和方法&#xff0c;确保读者对测试背景有清晰了解。 2.测试环境&#xff1a;详细描述测试所用的硬件、软件环境&#xff0c;确保…

在Vue或React项目中使用Tailwind CSS实现暗黑模式切换:从系统适配到手动控制

在现代Web开发中&#xff0c;暗黑模式(Dark Mode)已成为提升用户体验的重要功能。本文将带你使用Tailwind CSS在React项目(Vue项目类似)中实现两种暗黑模式控制方式&#xff1a; 系统自动适配 - 根据用户设备偏好自动切换手动切换 - 通过按钮让用户自由选择 一、项目准备 使…

Linux--命令行参数和环境变量

1.命令行参数 Linux 命令行参数基础 1.1参数格式 位置参数&#xff1a;无符号&#xff0c;按顺序传递&#xff08;如 ls /home/user 中 /home/user 是位置参数&#xff09; 选项参数&#xff1a; 短选项&#xff1a;以 - 开头&#xff0c;单个字母&#xff08;如 -l 表示长格…

springboot线上教学平台

摘要&#xff1a;在社会快速发展的影响下&#xff0c;使线上教学平台的管理和运营比过去十年更加理性化。依照这一现实为基础&#xff0c;设计一个快捷而又方便的网上线上教学平台系统是一项十分重要并且有价值的事情。对于传统的线上教学平台控制模型来说&#xff0c;网上线上…

mariadb5.5.56在centos7.6环境安装

mariadb5.5.56在centos7.6环境安装 1 下载安装包 https://mariadb.org/mariadb/all-releases/#5-5 2 上传安装包的服务器 mariadb-5.5.56-linux-systemd-x86_64.tar.gz 3 解压安装包 tar -zxvf mariadb-5.5.56-linux-systemd-x86_64.tar.gz mv mariadb-5.5.56-linux-syst…