更新记录

1.0.2(2026-05-27)

  • 修复 <haosns-editor> 组件不显示:组件目录统一为 components/haosns-editor/
  • 移除旧目录 haosns-article-editor,避免 easycom 冲突
  • 编辑器内 markdown SDK 改为 @/ 路径引用
  • 格式弹窗显式引入 uni-popup,提升 Vue3 兼容性
  • readme 补充 easycom custom 配置与显式 import 接入方式

1.0.0(2026-05-27)

  • 首次发布
  • 组件 haosns-editor:标题、编辑 / 预览、字数统计
  • 组件 haosns-article-format-sheet:格式插入弹窗(标题、样式、列表、对齐、引用等)
  • 正文插图占位 [图片:n],配合 haosns-markdown SDK 预览与详情渲染
  • 无 uview-plus 依赖

平台兼容性

uni-app(3.7.8)

Vue2 Vue2插件版本 Vue3 Vue3插件版本 Chrome Chrome插件版本 Safari Safari插件版本 app-vue app-vue插件版本 app-nvue Android iOS 鸿蒙
1.0.0 1.0.0 1.0.0 1.0.0 1.0.0 - - - -
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
- - - - - - - - - - - -

haosns-editor

uni-app 长文 Markdown 编辑器组件,支持编辑 / 预览、格式插入、正文插图。基于原生 input / textarea,依赖 uni-popup不依赖 uview-plus

平台:uni-app · Vue2 / Vue3 · App · H5 · 小程序


依赖

按以下顺序安装到同一 uni-app 项目的 uni_modules/ 目录:

顺序 插件 说明
1 haosns-markdown 解析 SDK,必装
2 mp-html 预览 HTML 渲染
3 uni-icons uni-ui 官方组件
4 uni-popup uni-ui 官方组件
5 haosns-editor 本插件

安装方式:DCloud 插件市场「导入 HBuilderX」,或解压 zip 至 uni_modules/。完成后重新运行项目。


组件说明

标签 说明
<haosns-editor> 标题输入、编辑 / 预览切换、格式与插图工具栏
<haosns-article-format-sheet> 底部格式选择弹窗

注册方式(二选一,推荐显式 import)

方式 A · 显式 import(推荐,Vue3 最稳)

import HaosnsEditor from '@/uni_modules/haosns-editor/components/haosns-editor/haosns-editor.vue'
import HaosnsArticleFormatSheet from '@/uni_modules/haosns-editor/components/haosns-article-format-sheet/haosns-article-format-sheet.vue'

export default {
  components: { HaosnsEditor, HaosnsArticleFormatSheet }
}

方式 B · easycom

pages.json 中配置(不要只依赖 autoscan):

{
  "easycom": {
    "autoscan": true,
    "custom": {
      "^haosns-editor$": "@/uni_modules/haosns-editor/components/haosns-editor/haosns-editor.vue",
      "^haosns-article-format-sheet$": "@/uni_modules/haosns-editor/components/haosns-article-format-sheet/haosns-article-format-sheet.vue"
    }
  }
}

修改 pages.json 后需重新运行项目;若仍不显示,请改用方式 A。


快速接入

1. 发帖页

<template>
  <view class="page">
    <haosns-editor
      ref="editor"
      :title="title"
      :content="content"
      :inline-image-map="inlineImageMap"
      :inline-image-uploading="uploading"
      :format-sheet-visible="formatVisible"
      @update:title="title = $event"
      @update:content="content = $event"
      @toggle-format-sheet="formatVisible = !formatVisible"
      @request-insert-image="onInsertImage"
    />
    <haosns-article-format-sheet
      :visible="formatVisible"
      @close="formatVisible = false"
      @select="onFormatSelect"
    />
    <button @tap="submit">发布</button>
  </view>
</template>

<script>
import HaosnsEditor from '@/uni_modules/haosns-editor/components/haosns-editor/haosns-editor.vue'
import HaosnsArticleFormatSheet from '@/uni_modules/haosns-editor/components/haosns-article-format-sheet/haosns-article-format-sheet.vue'
import { embedArticleDraftInlineMap } from '@/uni_modules/haosns-markdown/js_sdk/article-markdown.js'

export default {
  components: { HaosnsEditor, HaosnsArticleFormatSheet },
  data() {
    return {
      title: '',
      content: '',
      formatVisible: false,
      inlineImageMap: {},
      imageNextId: 1,
      uploading: false
    }
  },
  methods: {
    onFormatSelect(item) {
      this.$refs.editor.insertFormatItem(item)
      this.formatVisible = false
    },
    onInsertImage() {
      if (this.uploading) return
      this.uploading = true
      const id = String(this.imageNextId++)
      uni.chooseImage({
        count: 1,
        success: (res) => {
          uni.uploadFile({
            url: 'https://api.example.com/upload', // 替换实际上传地址
            filePath: res.tempFilePaths[0],
            name: 'file',
            success: (up) => {
              const data = JSON.parse(up.data)
              const url = data.url
              this.inlineImageMap = { ...this.inlineImageMap, [id]: url }
              this.$refs.editor.insertImagePlaceholder(id)
            },
            complete: () => { this.uploading = false }
          })
        },
        fail: () => { this.uploading = false }
      })
    },
    submit() {
      if (!this.title.trim()) {
        return uni.showToast({ icon: 'none', title: '请填写标题' })
      }
      const payload = {
        title: this.title,
        content: embedArticleDraftInlineMap(this.content, this.inlineImageMap)
      }
      // 调用业务发帖接口,payload 即为提交数据
    }
  }
}
</script>

pages.jsonpages 数组中注册该页面路径。

2. 详情页

<template>
  <mp-html :content="bodyHtml" />
</template>

<script>
import mpHtml from '@/uni_modules/mp-html/components/mp-html/mp-html.vue'
import {
  articleMarkdownToHtml,
  applyArticleDetailGallerySplit,
  extractArticleDraftInlineMap
} from '@/uni_modules/haosns-markdown/js_sdk/article-markdown.js'

export default {
  components: { mpHtml },
  data() {
    return { detail: null }
  },
  computed: {
    bodyHtml() {
      if (!this.detail) return ''
      const { content, map } = extractArticleDraftInlineMap(this.detail.content)
      const inlineMap = map || this.buildMapFromUrls(this.detail.articleInlineImages)
      return articleMarkdownToHtml(content, inlineMap)
    }
  },
  methods: {
    buildMapFromUrls(urls) {
      const m = {}
      ;(urls || []).forEach((url, i) => { m[String(i + 1)] = url })
      return m
    },
    onLoad() {
      // 接口返回 detail 后:
      // this.detail = applyArticleDetailGallerySplit(detail)
    }
  }
}
</script>

<style>
.article-align-center { text-align: center; }
.article-align-right { text-align: right; }
</style>

3. 列表摘要(可选)

import { articleContentToListPlainText } from '@/uni_modules/haosns-markdown/js_sdk/article-markdown.js'

const summary = articleContentToListPlainText(item.content)

组件 API

haosns-editor

Props

名称 类型 默认值 说明
title String '' 标题
content String '' 正文 Markdown
maxlength Number 5000 正文字数上限
inlineImageMap Object {} 插图映射 { "1": "url" }
inlineImageUploading Boolean false 上传中,禁用插图按钮
formatSheetVisible Boolean false 格式弹窗是否打开(控制按钮高亮)
editorReadOnly Boolean false 正文只读
titlePlaceholder String 添加标题 标题占位符
contentPlaceholder String 添加正文,支持 Markdown 语法 正文占位符

Events

名称 说明
update:title 标题变更
update:content 正文变更
toggle-format-sheet 点击「格式」按钮
request-insert-image 点击「插图」,由页面实现上传逻辑
mode-change 编辑 / 预览切换,参数 'edit' \| 'preview'

Methods(通过 ref 调用)

名称 说明
insertFormatItem(item) 插入选中的格式项
insertImagePlaceholder(id) 在光标处插入 [图片:id]
switchToEdit() 切换到编辑模式

haosns-article-format-sheet

Props

名称 类型 默认值 说明
visible Boolean false 是否显示
sheetTitle String 插入格式 弹窗标题
sections Array 内置格式列表 自定义格式分区,不传则使用默认

Events

名称 说明
close 弹窗关闭
select 选中格式项,参数为 item 对象

默认格式分区可通过以下方式扩展:

import { ARTICLE_FORMAT_SECTIONS } from '@/uni_modules/haosns-editor/components/haosns-article-format-sheet/haosns-article-format-sheet.vue'

插图流程说明

  1. 用户点击「插图」→ 触发 request-insert-image
  2. 页面选择图片并上传,得到 URL
  3. 分配递增 id(如 "1", "2"),写入 inlineImageMap
  4. 调用 this.$refs.editor.insertImagePlaceholder(id)
  5. 正文中出现 [图片:1],预览与详情通过 articleMarkdownToHtml 渲染为 <img>

顶栏相册图与正文插图需在后端或详情页用 applyArticleDetailGallerySplit 区分,详见 markdown SDK 文档。


常见问题

组件不显示?

  1. 确认组件目录为 uni_modules/haosns-editor/components/haosns-editor/(1.0.2 已修复旧目录名问题)
  2. 推荐使用 readme 中的显式 import 注册组件
  3. 或在 pages.json 配置 easycom custom 规则
  4. 删除 unpackage 后重新运行项目

预览区域空白?
确认已安装 mp-html;检查 content 是否有内容。

格式弹窗无法打开?
确认已安装 uni-popup,且页面中包含 <haosns-article-format-sheet> 并绑定 visible

与 uview 能否共存?
可以。本插件不引用 uview,可在已有 uview 项目中直接使用。

Vue2 项目是否支持?
支持。组件使用 Options API,兼容 Vue2 / Vue3。

如何联系作者?
请通过 DCloud 插件市场内置的 uni-im 向作者咨询,勿在 readme、changelog 或插件简介中填写 QQ、微信等第三方联系方式。


更新日志

changelog.md

Markdown 语法与更多 SDK 方法见 haosns-markdown readme

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

暂无用户评论。