彻底掌握md-editor-v3任务列表:从勾选到自定义事件全攻略

彻底掌握md-editor-v3任务列表:从勾选到自定义事件全攻略

你是否遇到这些任务列表痛点?

  • 勾选状态变更无法触发业务逻辑
  • 无法获取勾选操作的行号与内容
  • 预览模式下勾选状态同步困难
  • 自定义样式与默认行为冲突

本文将通过10个实战步骤+7段核心代码,教你全面掌控任务列表的勾选事件,实现状态监听、数据同步、业务联动的全流程自定义。

任务列表事件机制解析

核心事件原理

md-editor-v3通过事件总线(Event Bus)实现任务状态变更通知,核心事件为TASK_STATE_CHANGED,定义于static/event-name.ts

export const TASK_STATE_CHANGED = 'taskStateChanged';

事件触发流程: mermaid

事件参数详解

参数名类型描述示例
lineNumbernumber任务所在行号(从1开始)3
targetValuestring修改后的行内容"- [x] 完成文档编写"

基础实现:监听任务状态变更

1. 设置编辑器唯一标识

<template>
  <MdEditor 
    v-model="content" 
    id="my-editor"  <!-- 必须设置唯一ID用于事件总线 -->
  />
</template>

2. 引入事件总线与事件常量

import bus from 'md-editor-v3/dist/utils/event-bus';
import { TASK_STATE_CHANGED } from 'md-editor-v3/dist/static/event-name';

3. 注册事件监听器

onMounted(() => {
  // 注册事件监听
  bus.on('my-editor', {
    name: TASK_STATE_CHANGED,
    callback: handleTaskChange
  });
});

// 事件处理函数
const handleTaskChange = (lineNumber, targetValue) => {
  console.log(`第${lineNumber}行任务状态变更为:`, targetValue);
  // 业务逻辑处理...
};

4. 组件卸载时清理事件

onBeforeUnmount(() => {
  // 清除指定编辑器的所有事件监听
  bus.clear('my-editor');
});

高级实战:实现复杂业务场景

场景1:任务状态持久化

const handleTaskChange = async (lineNumber, targetValue) => {
  // 解析任务内容与状态
  const isChecked = targetValue.includes('[x]');
  const taskContent = targetValue.replace(/- \[x\] /, '').replace(/- \[\s\] /, '');
  
  // 调用API保存状态
  await api.saveTaskStatus({
    line: lineNumber,
    content: taskContent,
    completed: isChecked,
    documentId: 'doc-123'
  });
};

场景2:跨组件任务状态同步

// 父组件中
const taskStates = ref<Record<number, boolean>>({});

const handleTaskChange = (lineNumber, targetValue) => {
  const isChecked = targetValue.includes('[x]');
  taskStates.value[lineNumber] = isChecked;
  
  // 通知其他组件
  emits('task-updated', { lineNumber, isChecked });
};

场景3:预览模式下的状态修改

预览模式(previewOnly)下需要特殊处理:

<template>
  <MdEditor 
    v-model="content" 
    :previewOnly="true"
    @change="handleContentChange"  <!-- 预览模式下通过change事件同步 -->
  />
</template>

<script setup>
const handleContentChange = (newContent) => {
  // 解析所有任务状态
  const lines = newContent.split('\n');
  const tasks = lines
    .map((line, index) => ({
      line: index + 1,
      content: line,
      completed: line.includes('- [x]')
    }))
    .filter(item => item.content.startsWith('- [') && item.content.includes('] '));
  
  console.log('所有任务状态:', tasks);
};
</script>

任务列表渲染与样式自定义

默认任务列表结构

<!-- 渲染后的HTML结构 -->
<div class="task-list-item enabled" data-line="2">
  <input class="task-list-item-checkbox" type="checkbox" checked>
  <label class="task-list-item-label">完成文档编写</label>
</div>

自定义勾选框样式

/* 自定义勾选框样式 */
.md-editor-v3 .task-list-item-checkbox {
  width: 18px;
  height: 18px;
  margin-right: 8px;
  accent-color: #42b983; /* Vue绿色主题 */
  border-radius: 4px;
}

/* 暗黑模式适配 */
.md-editor-v3-dark .task-list-item-checkbox {
  accent-color: #359e75;
}

常见问题解决方案

Q1: 事件不触发怎么办?

mermaid

Q2: 如何获取任务列表的完整数据?

const parseTasks = (content) => {
  return content.split('\n')
    .map((line, index) => ({
      lineNumber: index + 1,
      text: line.replace(/- \[(x| )\] /, ''),
      completed: line.includes('- [x]'),
      original: line
    }))
    .filter(item => /^- \[(x| )\] /.test(item.original));
};

// 使用示例
const tasks = parseTasks(content);
console.log('解析后的任务列表:', tasks);

Q3: 如何禁止特定任务的勾选?

const handleTaskChange = (lineNumber, targetValue) => {
  // 禁止第2行任务修改
  if (lineNumber === 2) {
    // 恢复原始状态
    const lines = content.value.split('\n');
    lines[lineNumber - 1] = lines[lineNumber - 1].includes('[x]') 
      ? '- [x] 禁止修改的任务' 
      : '- [ ] 禁止修改的任务';
    content.value = lines.join('\n');
    
    alert('该任务禁止修改状态');
  }
};

性能优化与最佳实践

事件防抖处理

import { debounce } from 'lodash-es';

// 防抖处理,避免快速勾选时频繁触发
const debouncedHandleTaskChange = debounce((lineNumber, targetValue) => {
  // 实际业务处理
}, 300);

// 注册防抖后的处理函数
bus.on('my-editor', {
  name: TASK_STATE_CHANGED,
  callback: debouncedHandleTaskChange
});

批量任务操作

// 全选/取消全选功能
const toggleAllTasks = (checked) => {
  const lines = content.value.split('\n');
  const newLines = lines.map(line => {
    if (/^- \[(x| )\] /.test(line)) {
      return checked ? line.replace('- [ ] ', '- [x] ') : line.replace('- [x] ', '- [ ] ');
    }
    return line;
  });
  content.value = newLines.join('\n');
};

任务状态可视化

<template>
  <div class="task-stats">
    <div class="progress-bar">
      <div 
        class="progress" 
        :style="{ width: `${(completedCount / totalCount) * 100}%` }"
      ></div>
    </div>
    <p>{{ completedCount }}/{{ totalCount }} 任务已完成</p>
  </div>
</template>

<script setup>
const completedCount = computed(() => {
  return (content.value.match(/- \[x\]/g) || []).length;
});

const totalCount = computed(() => {
  return (content.value.match(/- \[(x| )\]/g) || []).length;
});
</script>

完整示例代码

<template>
  <div class="task-editor-demo">
    <MdEditor 
      v-model="content" 
      id="task-editor"
      :toolbars="['bold', 'italic', 'task']"
      :preview="true"
      height="500px"
    />
    
    <div class="task-log">
      <h3>操作日志</h3>
      <ul>
        <li v-for="log in logs" :key="log.id">
          {{ new Date(log.time).toLocaleTimeString() }} - {{ log.message }}
        </li>
      </ul>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, computed } from 'vue';
import MdEditor from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';
import bus from 'md-editor-v3/dist/utils/event-bus';
import { TASK_STATE_CHANGED } from 'md-editor-v3/dist/static/event-name';

const content = ref(`# 任务列表示例

- [ ] 学习事件总线机制
- [ ] 实现任务状态监听
- [ ] 完成自定义业务逻辑
- [x] 初始化编辑器
`);

const logs = ref<Array<{ id: number; time: number; message: string }>>([]);
let logId = 1;

// 解析任务状态
const taskStats = computed(() => {
  const lines = content.value.split('\n');
  const tasks = lines.filter(line => /^- \[(x| )\] /.test(line));
  const completed = tasks.filter(line => line.includes('- [x]')).length;
  
  return {
    total: tasks.length,
    completed,
    progress: tasks.length ? Math.round((completed / tasks.length) * 100) : 0
  };
});

// 任务变更处理函数
const handleTaskChange = (lineNumber: number, targetValue: string) => {
  const isChecked = targetValue.includes('[x]');
  const taskText = targetValue.replace(/- \[x\] /, '').replace(/- \[\s\] /, '');
  
  // 添加操作日志
  logs.value.unshift({
    id: logId++,
    time: Date.now(),
    message: `任务 "${taskText}" 已${isChecked ? '完成' : '取消'} (行: ${lineNumber})`
  });
  
  // 仅保留最近10条日志
  if (logs.value.length > 10) {
    logs.value.pop();
  }
  
  // 业务逻辑:当所有任务完成时提示
  if (isChecked && taskStats.value.completed === taskStats.value.total) {
    alert('恭喜!所有任务已完成!');
  }
};

onMounted(() => {
  // 注册事件监听
  bus.on('task-editor', {
    name: TASK_STATE_CHANGED,
    callback: handleTaskChange
  });
});

onBeforeUnmount(() => {
  // 清理事件监听
  bus.clear('task-editor');
});
</script>

<style scoped>
.task-editor-demo {
  max-width: 800px;
  margin: 0 auto;
  padding: 20px;
}

.task-log {
  margin-top: 20px;
  padding: 15px;
  background: #f5f5f5;
  border-radius: 8px;
}

.task-log h3 {
  margin-top: 0;
  color: #333;
}

.task-log ul {
  list-style: none;
  padding: 0;
  margin: 0;
}

.task-log li {
  padding: 8px 0;
  border-bottom: 1px solid #eee;
  font-size: 14px;
  color: #666;
}

.task-log li:last-child {
  border-bottom: none;
}
</style>

总结与进阶展望

通过本文学习,你已掌握: ✅ 任务列表事件监听的完整流程 ✅ 3种核心业务场景的实现方案 ✅ 5个常见问题的解决方案 ✅ 性能优化与最佳实践

进阶方向

  1. 实现任务拖拽排序与状态联动
  2. 结合Vuex/Pinia实现全局任务状态管理
  3. 开发任务完成动画与交互反馈
  4. 集成第三方任务管理API(如Trello)

希望本文能帮助你彻底掌握md-editor-v3的任务列表功能。如有任何问题,欢迎在评论区留言讨论。记得收藏本文,以便后续开发查阅!

下一篇预告:《md-editor-v3高级插件开发指南》

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值