自定义实现项目进度图

news2024/11/24 19:21:59

 目录

0.背景

1. 演示效果

2.实现原理

2.1 处理表头数据(日期对应的是星期几)

2.2 获取项目数据

2.3 合并单元格

3.源码


0.背景

目遇到一个展示项目进度的需求,类似甘特图的效果,不同的是,每一个项目都有计划和实际两种,然后点击可以进行交互等。其实有很多可以拿来的甘特图组件,眼花缭乱的。但是因为我们需要自定义拓展,还是有些受限,干脆用table自己写了一个组件。

1. 演示效果

2.实现原理

选择两个日期,处理日期区间,渲染表头,获取数据后,根据不同的状态进行渲染即可。

用到的无非就是table的固定列和合并单元格。

2.1 处理表头数据(日期对应的是星期几)

 用到了moment.js,根据所选择的日期区间,所处间隔天数,再根据日期进行当前日期是周几的运算,得出表头数据。如下图所示👇

getDaysWeek() {
      this.dateRange = [];
      const diffDays = moment(this.month[1]).diff(moment(this.month[0]), 'days') + 1;
      this.tableWidth = 800 + 100 * diffDays;
      const weekDays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
      const dayOfWeek = moment(this.month[0]).day();
      for (let i = 0; i < diffDays; i++) {
        const day = moment(this.month[0]).add(i, 'days').format('YYYY-MM-DD');
        const week = weekDays[(dayOfWeek + i) % 7]
        this.dateRange.push({day: day, week: week})
      }
    }

2.2 获取项目数据

项目数据的格式如下,因为一个项目分了两条数据,所以两条数据为一组。如下所示

self.planData = [
            {
              startTime: '2024-10-02', // 开始时间
              endTime: '2024-10-05', // 结束时间
              entrustName: '一个大的项目名称', // 委托名称
              planId: '1', // 项目id
              planName: '测试项目1', // 项目名称
              showName: '计划中', // 展示名称
              status: 0 // 状态:0:计划;1:执行中;2:已完成
            }, {
              endTime: "2024-10-04",
              entrustName: "一个大的项目名称",
              planId: "1",
              planName: "测试项目1",
              showName: "赶工完成",
              startTime: "2024-10-02",
              status: 2
            },{
              endTime: '2024-10-15',
              entrustName: '一个小的项目名称',
              planId: '2',
              planName: '测试项目2',
              showName: '计划中',
              startTime: '2024-10-09',
              status: 0
            }, {
              endTime: "",
              entrustName: "一个小的项目名称",
              planId: "2",
              planName: "测试项目2",
              showName: "实际执行",
              startTime: "2024-10-10",
              status: 1
            }
          ];

2.3 合并单元格

根据项目的开始结束日期范围,进行单元格合并。如下图红色标注所示

handleColspanData() {
      const self = this;
      let colIndex = 0;
      self.mergeColumnsData = [];
      self.planData.forEach((item, index) => {
        colIndex = index % 2 === 0 ? 6 : 3;
        self.dateRange.forEach((child, childIndex) => {
          if (this.isBetweenCheck(item, child.day)) {
            // 判断mergeColumnsData是否存在?存在则替换,反之插入
            let id = index % 2 === 0 ? 'p-' + item.planId : 'r-' + item.planId;
            let targetIndex = self.mergeColumnsData.findIndex(col => col.id === id);
            if (targetIndex !== -1) {
              self.mergeColumnsData[targetIndex].colCount = self.mergeColumnsData[targetIndex].colCount + 1
            } else {
              self.mergeColumnsData.push({
                id: id,
                rowIndex: index,
                colIndex: colIndex + childIndex,
                colCount: 1
              });
            }
          }
        })
      });
      self.mergeColumns('gantt-table', self.mergeColumnsData);
    },
mergeColumns(tableId, mergeInfo) {
      const table = document.getElementById(tableId);
      if (!table) return;
      // 获取tbody元素
      const tbody = table.tBodies[0];
      if (!tbody) return;
      mergeInfo.forEach(info => {
        const startRow = info.rowIndex;
        const startCol = info.colIndex;
        const colCount = info.colCount || 1; // 默认合并1列
        // 获取需要合并的起始行
        const rowToMerge = tbody.rows[startRow];
        if (!rowToMerge) return;
        // 合并列
        if (colCount > 1) {
          const cellToMerge = rowToMerge.cells[startCol];
          if (!cellToMerge) return;
          cellToMerge.colSpan = colCount;
          // 删除后续的单元格
          for (let i = 1; i < colCount; i++) {
            const cell = rowToMerge.cells[startCol + i];
            if (!cell) break;
            cell.style.display = 'none'; // 隐藏单元格而不是删除
            // 标记为稍后删除
            cell.classList.add('remove-later');
          }
        }
      });
      document.querySelectorAll('.remove-later').forEach(cell => cell.remove());
    },

3.源码

直接铁源码吧

<template>
  <div class="lcdp_axe_main  projectProgress">
    <div class="search-row">
      <div>
        <span class="ins_format default">时间范围</span>
        <el-date-picker
          size="mini"
          v-model="month"
          :clearable="false"
          type="daterange"
          value-format="yyyy-MM-dd"
          start-placeholder="开始日期"
          end-placeholder="结束日期"
          @change="changeDate">
        </el-date-picker>
      </div>
    </div>
    <div class="status-box">
      <p class="title">
        项目进度
      </p>
      <p>
        <span>状态:</span>
        <span class="green-point"></span>
        <span>计划</span>
        <span class="blue-point"></span>
        <span>已完成</span>
        <span class="orange-point"></span>
        <span>进行中</span>
      </p>
    </div>
    <div class="gantt-box">
      <table id="gantt-table" :key="tabKey" :style="'width:' + tableWidth +'px'" cellspacing="0">
        <thead>
        <tr>
          <th style="width:100px" rowspan="2">
            序号
          </th>
          <th style="width:200px" rowspan="2">
            委托名称
          </th>
          <th style="width: 200px" rowspan="2">
            项目名称
          </th>
          <th style="width: 300px" colspan="3">
            时间节点
          </th>
          <th :colspan="dateRange.length">
            项目进度
          </th>
        </tr>
        <tr>
          <th>
            执行
          </th>
          <th>
            开始
          </th>
          <th>
            结束
          </th>
          <th style="width: 100px" v-for="(item, index) in dateRange" :key="index">
            {{ item.day.slice(5) }}
            <br/>
            {{ item.week }}
          </th>
        </tr>
        </thead>
        <tbody>
        <tr v-for="(item, index) in planData" :key="index">
          <td v-if="index%2===0" rowspan="2">{{ index / 2 + 1 }}</td>
          <td v-if="index%2===0" rowspan="2">{{ item.entrustName }}</td>
          <td v-if="index%2===0" rowspan="2">{{ item.planName }}</td>
          <td>{{ index % 2 === 0 ? '计划' : '实际' }}</td>
          <td>{{ item.startTime }}</td>
          <td>{{ item.endTime }}</td>
          <td v-for="(cell, ind) in dateRange" :key="'cell' + ind"
              :ref="index%2===0?'p-':'r-'+item.planId+'-' + cell.day">
            <span :class="{'planBar': item.status === 0}"
                  @click="granttCick(item,index)"
                  v-if="item.status === 0&&isBetweenCheck(item,cell.day)">{{ item.showName }}</span>
            <span :class="{'doingBar': item.status === 1}"
                  @click="granttCick(item,index)"
                  v-if="item.status === 1&&isBetweenCheck(item,cell.day)">{{ item.showName }}</span>
            <span :class="{'completedBar': item.status === 2}"
                  @click="granttCick(item,index)"
                  v-if="item.status === 2&&isBetweenCheck(item,cell.day)">{{ item.showName }}</span>
          </td>
        </tr>
        </tbody>
      </table>
    </div>
    <el-dialog
      title="计划名称"
      :close-on-click-modal="false"
      :visible.sync="dialogVisible"
      width="30%">
      <el-form ref="planForm" :rules="rules" size="mini" :model="planForm" label-width="80px">
        <el-form-item label="计划名称" prop="showName">
          <el-input v-model="planForm.showName"></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
    <el-button size="mini" @click="dialogVisible = false">取 消</el-button>
    <el-button size="mini" type="primary" @click="save">确 定</el-button>
  </span>
    </el-dialog>
  </div>
</template>

<script>
import moment from 'moment';

export default {
  name: "projectProgress",
  data() {
    return {
      tabKey: Math.random(),
      month: [moment().format('YYYY-MM') + '-01', moment().format('YYYY-MM') + '-' + moment().daysInMonth()],
      dateRange: [],
      planData: [],
      tableWidth: 800,
      mergeColumnsData: [],
      dialogVisible: false,
      isPlanFlag: true,
      planForm: {},
      rules: {
        showName: {required: true, message: '请输入计划名称', trigger: 'change'}
      }
    }
  },
  async mounted() {
    await this.getDaysWeek();
    await this.getTaskProgressList();
  },
  methods: {
    async getTaskProgressList() {
      const self = this;
      let params = {
        planStartTime: self.month[0],
        planEndTime: self.month[1]
      }
      // self.planData = [];
      self.planData = [
        {
          endTime: '2024-10-05',
          entrustName: '一个大的项目名称',
          planId: '1',
          planName: '测试项目1',
          showName: '计划中',
          startTime: '2024-10-02',
          status: 0
        }, {
          endTime: "2024-10-04",
          entrustName: "一个大的项目名称",
          planId: "1",
          planName: "测试项目1",
          showName: "赶工完成",
          startTime: "2024-10-02",
          status: 2
        },{
          endTime: '2024-10-15',
          entrustName: '一个小的项目名称',
          planId: '2',
          planName: '测试项目2',
          showName: '计划中',
          startTime: '2024-10-09',
          status: 0
        }, {
          endTime: "",
          entrustName: "一个小的项目名称",
          planId: "2",
          planName: "测试项目2",
          showName: "实际执行",
          startTime: "2024-10-10",
          status: 1
        }
      ];
      this.$nextTick(() => {
        this.handleColspanData();
      })
    },
    changeDate() {
      this.tabKey = Math.random();
      this.getDaysWeek();
      this.$nextTick(() => {
        this.getTaskProgressList();
      });
    },
    granttCick(data, index) {
      this.isPlanFlag = index % 2 === 0 ? true : false;
      this.planForm = JSON.parse(JSON.stringify(data));
      this.dialogVisible = true;
    },
    save() {
      const self = this;
      this.$refs.planForm.validate((valid) => {
        if (valid) {
          let params = {
            planId: self.planForm.planId,
          }
          if (self.isPlanFlag) {
            params.showName = self.planForm.showName;
          } else {
            params.actualName = self.planForm.showName;
          }
          setShowName(params).then(res => {
            if (res.code === 10000) {
              self.getTaskProgressList();
            } else {
            }
          }).catch(() => {
          })
          self.dialogVisible = false;
        } else {
          return false;
        }
      });
    },
    handleColspanData() {
      const self = this;
      let colIndex = 0;
      self.mergeColumnsData = [];
      self.planData.forEach((item, index) => {
        colIndex = index % 2 === 0 ? 6 : 3;
        self.dateRange.forEach((child, childIndex) => {
          if (this.isBetweenCheck(item, child.day)) {
            // 判断mergeColumnsData是否存在?存在则替换,反之插入
            let id = index % 2 === 0 ? 'p-' + item.planId : 'r-' + item.planId;
            let targetIndex = self.mergeColumnsData.findIndex(col => col.id === id);
            if (targetIndex !== -1) {
              self.mergeColumnsData[targetIndex].colCount = self.mergeColumnsData[targetIndex].colCount + 1
            } else {
              self.mergeColumnsData.push({
                id: id,
                rowIndex: index,
                colIndex: colIndex + childIndex,
                colCount: 1
              });
            }
          }
        })
      });
      self.mergeColumns('gantt-table', self.mergeColumnsData);
    },
    isBetweenCheck(data, current) {
      let startTime = moment(data.startTime);
      let endTime = moment(data.endTime === '' ? this.month[1] : data.endTime);
      let dateToCheck = moment(current);
      const isWithinRange = dateToCheck.isBetween(startTime, endTime, null, '[]');
      return isWithinRange;
    },
    mergeColumns(tableId, mergeInfo) {
      const table = document.getElementById(tableId);
      if (!table) return;
      // 获取tbody元素
      const tbody = table.tBodies[0];
      if (!tbody) return;
      mergeInfo.forEach(info => {
        const startRow = info.rowIndex;
        const startCol = info.colIndex;
        const colCount = info.colCount || 1; // 默认合并1列
        // 获取需要合并的起始行
        const rowToMerge = tbody.rows[startRow];
        if (!rowToMerge) return;
        // 合并列
        if (colCount > 1) {
          const cellToMerge = rowToMerge.cells[startCol];
          if (!cellToMerge) return;
          cellToMerge.colSpan = colCount;
          // 删除后续的单元格
          for (let i = 1; i < colCount; i++) {
            const cell = rowToMerge.cells[startCol + i];
            if (!cell) break;
            cell.style.display = 'none'; // 隐藏单元格而不是删除
            // 标记为稍后删除
            cell.classList.add('remove-later');
          }
        }
      });
      document.querySelectorAll('.remove-later').forEach(cell => cell.remove());
    },
    getDaysWeek() {
      this.dateRange = [];
      const diffDays = moment(this.month[1]).diff(moment(this.month[0]), 'days') + 1;
      this.tableWidth = 800 + 100 * diffDays;
      const weekDays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
      const dayOfWeek = moment(this.month[0]).day();
      for (let i = 0; i < diffDays; i++) {
        const day = moment(this.month[0]).add(i, 'days').format('YYYY-MM-DD');
        const week = weekDays[(dayOfWeek + i) % 7]
        this.dateRange.push({day: day, week: week})
      }
    }
  }
}
</script>

<style lang="scss" scoped>
.lcdp_axe_main {
  overflow: auto;
  width: calc(100% - 20px);
  height: calc(100% - 20px);
  position: relative;
  top: 10px;
  left: 10px;
  right: 10px;
  bottom: 10px;
  display: block;
  padding: 12px;
  background: #ffffff;

  .search-row {
    height: 40px;
    margin-bottom: 10px;
    display: flex;
    justify-content: space-between;
    border-bottom: 1px solid var(--c-borderColor);;

    .ins_format {
      margin-right: 10px;
    }
  }

  .status-box {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0 20px;
    height: 40px;

    .title {
      font-weight: bold;
      padding-left: 5px;
      border-left: 5px solid var(--c-themeColor);
    }

    .green-point, .blue-point, .orange-point {
      display: inline-flex;
      width: 15px;
      height: 15px;
      border-radius: 50%;
      margin-left: 20px;
      margin-right: 5px;
    }

    .green-point {
      background: #00b57b;
    }

    .blue-point {
      background: #18aaf1;
    }

    .orange-point {
      background: #f19418;
    }
  }

  .gantt-box {
    min-width: 100%;
    height: calc(100% - 90px);
    overflow: auto;

    #gantt-table {
      table-layout: fixed;
      min-width: 100%;

      td, th {
        border: 1px solid #d5d9dc;
        height: 45px;
        text-align: center;
        color: var(--c-mainTxtColor);
      }

      thead tr:nth-child(1),
      tbody tr:nth-child(odd) {
        td:nth-child(1),
        th:nth-child(1) {
          position: sticky;
          left: 0;
          z-index: 1;
          background: #f7fbff;
        }

        td:nth-child(2),
        th:nth-child(2) {
          position: sticky;
          left: 100px;
          z-index: 1;
          background: #f7fbff;
        }

        td:nth-child(3),
        th:nth-child(3) {
          position: sticky;
          left: 300px;
          z-index: 1;
          background: #f7fbff;
        }

        td:nth-child(4),
        th:nth-child(4) {
          position: sticky;
          left: 500px;
          z-index: 1;
          background: #f7fbff;
        }

        td:nth-child(5) {
          position: sticky;
          left: 600px;
          z-index: 1;
          background: #f7fbff;
        }

        td:nth-child(6) {
          position: sticky;
          left: 700px;
          z-index: 1;
          background: #f7fbff;
        }
      }

      tbody tr:nth-child(even) {
        td:nth-child(1) {
          position: sticky;
          left: 500px;
          z-index: 1;
          background: #f7fbff;
        }

        td:nth-child(2) {
          position: sticky;
          left: 600px;
          z-index: 1;
          background: #f7fbff;
        }

        td:nth-child(3) {
          position: sticky;
          left: 700px;
          z-index: 1;
          background: #f7fbff;
        }
      }

      thead tr:nth-child(2) {
        th:nth-child(1) {
          position: sticky;
          left: 500px;
          z-index: 1;
          background-color: lightpink;
        }

        th:nth-child(2) {
          position: sticky;
          left: 600px;
          z-index: 1;
          background-color: lightpink;
        }

        th:nth-child(3) {
          position: sticky;
          left: 700px;
          z-index: 1;
          background-color: lightpink;
        }
      }

      thead tr:nth-child(1) {
        th:nth-child(1),
        th:nth-child(2),
        th:nth-child(3),
        th:nth-child(4) {
          position: sticky;
          background: #f7fbff;
          z-index: 2;
          top: 0px;
        }

        th {
          position: sticky;
          background: #f7fbff;
          font-size: 16px;
          font-weight: bold;
          z-index: 1;
          top: 0px;
        }
      }

      thead tr:nth-child(2) {
        th:nth-child(1),
        th:nth-child(2),
        th:nth-child(3) {
          position: sticky;
          z-index: 2;
          background: #f7fbff;
          top: 45px;
        }

        th {
          position: sticky;
          z-index: 1;
          background: #f7fbff;
          top: 45px;
        }
      }

      tbody tr:nth-child(odd) td {
        border-top: 2px solid var(--c-normalTxtColor);
      }

      .planBar {
        display: inline-block;
        width: 100%;
        height: 60%;
        background: #00b57b;
        background-image: repeating-linear-gradient(
            45deg,
            hsla(0, 0%, 100%, 0.1),
            hsla(0, 0%, 100%, 0.1) 15px,
            transparent 0,
            transparent 30px
        );
      }

      .doingBar {
        display: inline-block;
        width: 100%;
        height: 60%;
        background: #f19418;
        background-image: repeating-linear-gradient(
            45deg,
            hsla(0, 0%, 100%, 0.1),
            hsla(0, 0%, 100%, 0.1) 15px,
            transparent 0,
            transparent 30px
        );
      }

      .completedBar {
        display: inline-block;
        width: 100%;
        height: 60%;
        background: #18aaf1;
        background-image: repeating-linear-gradient(
            45deg,
            hsla(0, 0%, 100%, 0.1),
            hsla(0, 0%, 100%, 0.1) 15px,
            transparent 0,
            transparent 30px
        );
      }

      span {
        color: #ffffff;
        font-weight: bold;
        font-size: 18px;
        cursor: pointer;
      }

      span:hover {
        box-shadow: 0 0 5px 5px #eaeaea;
        background-image: url("~@/assets/img/icon-edit.png");
        background-repeat: no-repeat;
        background-size: auto 80%;
        background-position: 10px center;
      }
    }
  }
}
</style>

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

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

相关文章

MySQL:基于Spring监听Binlog日志

binlog的三种模式 MySQL 的二进制日志&#xff08;binlog&#xff09;有三种不同的格式&#xff0c;通常被称为 binlog 模式。这三种模式分别是 Statement 模式、Row 模式和Mixed 模式。 Statement 模式&#xff1a; 在 Statement 模式下&#xff0c;MySQL 记录每个会更改数…

代码随想录训练营Day30 | 491.递增子序列 | 46.全排列 | 47.全排列 II

学习文档&#xff1a;代码随想录 (programmercarl.com) 学习视频&#xff1a;代码随想录算法公开课 | 最强算法公开课 | 代码随想录 (programmercarl.com) Leetcode 491. 非递减子序列 题目描述 给你一个整数数组 nums &#xff0c;找出并返回所有该数组中不同的递增子序列…

AI赋能!0基础小白自媒体创业,成功率提升90%的秘诀?

本文背景 好多小伙伴也想写公众号文章&#xff0c;但是自己又没有写过&#xff0c;不知道如何开始。 今天分享个小方法&#xff0c;就算是写作新手&#xff0c;也能靠 AI 快速上手&#xff0c;写出好内容&#xff01; 一起来看看怎么用 AI 工具 助力写作&#xff0c;提高效率&a…

STM32(十八):实时时钟

时间戳 Unix 时间戳&#xff08;Unix Timestamp&#xff09;定义为从UTC/GMT的1970年1月1日0时0分0秒开始所经过的秒数&#xff0c;不考虑闰秒。 时间戳存储在一个秒计数器中&#xff0c;秒计数器为32位/64位的整型变量。 世界上所有时区的秒计数器相同&#xff0c;不同时…

一次性使用病毒采样管-保存运输呼吸道 肠道等多种病毒样本的有力工具!

远离了新冠病毒却又来了流感病毒、手足口、猴痘病毒、诺如病毒等多种病毒&#xff0c;对于众多病毒的检测诊断&#xff0c;确保病毒样本的高效采集、安全保存和准确运输是至关重要的。为了满足这一需求&#xff0c;一次性使用病毒采样管应运而生。 在研究、医学诊断和疫情监测…

10.12 标准IO

练习&#xff1a;针对fscanf和fprintf练习 有如下结构体&#xff1a; typedef struct Student { char name[20]; int id; float chinese;//语文成绩 float math; float english; float physical; float chemical; float biological; }stu_t; *Pstu_t//声明学生结构体类型 在栈区…

逆向思维的力量:Prolog在游戏编程中的应用与代码实践

在主流游戏开发语言如C++、Python和Unity统治的今天,Prolog作为一种基于逻辑编程的语言,似乎与游戏开发不太沾边。然而,Prolog的逻辑推理机制和简洁的语法在解决复杂逻辑问题上有着独特的优势,尤其是在人工智能(AI)决策和路径规划等领域。通过Prolog,我们可以以极简的代…

【C++】C++的引用

一.引用 1.引用的概念和定义 引用不是新定义⼀个变量&#xff0c;而是给已存在变量取了⼀个别名&#xff0c;编译器不会为引用变量开辟内存空间&#xff0c;它和它引用的变量共用同⼀块内存空间。 类型& 引用别名 引用对象; 2.引用的特征 a.引用在定义时必须初始化 …

从数据到结论:ChatGPT如何帮助你完成复杂的数据分析?

AIPaperGPT&#xff0c;论文写作神器~ https://www.aipapergpt.com/ 在撰写学术论文时&#xff0c;数据分析往往是最具挑战性的部分之一。无论是定量研究还是定性研究&#xff0c;如何有效分析数据、得出合理结论都是关键所在。 ChatGPT的优势 1. 快速处理大量数据 在面对大…

Qt-窗口对话框QColorDialog的使用(52)

目录 描述 常用方法 使用 描述 颜⾊对话框的功能是允许⽤⼾选择颜⾊。继承⾃ QDialog 类。颜⾊对话框如下图⽰&#xff1a; 常用方法 1、QColorDialog (QWidget *parent nullptr) //创建对象的同时设置⽗对象 2、QColorDialog(const QColor &initial, QWidget *paren…

2024开放原子开源生态大会 | 麒麟信安携手openEuler共建开源生态,共塑产业未来

9月25日-27日&#xff0c;由开放原子开源基金会主办的2024开放原子开源生态大会在北京开幕&#xff0c;大会以“开源赋能产业&#xff0c;生态共筑未来”为主题。工业和信息化部党组书记、部长金壮龙&#xff0c;北京市委副书记、市长殷勇&#xff0c;工业和信息化部总经济师、…

Chromium HTML Tags与c++接口对应关系分析

一、HTML 标签(HTML Tags) <a> <head> <img>等等这些标签在c中的接口是如何定义和查找的呢&#xff1f; 更多标签参考&#xff1a; HTML <a> target 属性 (w3school.com.cn) 二、html_tag_names.json5 (third_party\blink\renderer\core\html\htm…

QD1-P10 HTML 超链接标签(a)下篇

本节学习&#xff1a;HTML a 标签实现大纲跳转&#xff08;锚点&#xff09;。 本节视频 www.bilibili.com/video/BV1n64y1U7oj?p10 本节实现大纲&#xff08;锚点&#xff09;跳转 ​​ 完整 HTML 代码 <!DOCTYPE html> <html><head><meta charset&…

python爬虫--某动漫信息采集

python爬虫--tx动漫 一、采集主页信息二、采集详情页信息三、代码供参考一、采集主页信息 略。 二、采集详情页信息 如上图所示,使用xpath提取详情页的标题、作者、评分、人气、评论人数等数据。 三、代码供参考 import csv import time import random import requests fr…

弧光保护装置的应用

长期以来&#xff0c;人们对中低压母线的保护一直不够重视&#xff0c;没有保护来快速切除配电站所内部发生的故障&#xff0c;往往使故障发展扩大造成开关设备被严重烧毁&#xff0c;有的甚至发展成“火烧连营”的事故。同时&#xff0c;变电站的变压器由于遭受外部短路电流冲…

【harmonyOS开发笔记3】ArkTS中数组的使用

数组的定义 数组&#xff1a;是一个容器&#xff0c;可以存储多个数据 定义数组的格式&#xff1a; let 数组名: 类型[] [数据1&#xff0c; 数据2&#xff0c; ] 示例&#xff1a;let names: string[] [小明, 小红] // 数组 let 数组名: 类型[] [数据1, 数据2, ] let …

【大模型实战教程】Qwen2.5 PyTorch模型微调入门实战

1 引言 Qwen2.5是Qwen大型语言模型系列的最新成果。对于Qwen2.5&#xff0c;通义千问团队发布了从0.5到720亿参数不等的基础语言模型及指令调优语言模型。Qwen2.5相比Qwen2带来了以下改进&#xff1a; 显著增加知识量&#xff0c;在编程与数学领域的能力得到极大提升&#xf…

四种Agent落地方案:Coze、AutoGen、Langchain、llama-index

前言 1. Coze 优势&#xff1a; 模块化设计&#xff1a; 允许开发者灵活地添加或替换组件&#xff0c;适应不同的应用需求。 易于集成&#xff1a; 支持与多种外部系统和服务的集成&#xff0c;方便与现有业务系统对接。 用户友好&#xff1a; 提供图形化界面和命令行工具&…

讯飞与腾讯云:Android 语音识别服务对比选择

目录 一、讯飞语音识别 1.1 讯飞语音识别介绍 1.1.1 功能特点 1.1.2 优势 1.2 接入流程 1.2.1 注册账号并创建应用 1.2.2 下载SDK等相关资料 1.2.3 导入SDK 1.2.4 添加用户权限 1.2.5 初始化讯飞SDK 1.2.6 初始化语音识别对象 1.2.7 显示结果 二、腾讯云语音识别 …

数字人直播带货操作详解,必看!

作为人工智能和直播带货两大热门行业的结合体&#xff0c;数字人直播带货一经出现就引发了众多商家的关注&#xff0c;许多创业者更是因此对数字人直播这一创业项目产生了浓厚的兴趣&#xff0c;连带着对数字人直播带货怎么操作等问题的讨论也日渐热烈了起来。 本期&#xff0…