百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 行业知识 > 正文

基于 vite2 + Vue3 写一个在线帮助文档工具

citgpt 2024-10-22 15:26 6 浏览 0 评论

提起帮助文档,想必大家都会想到 VuePress等,我也体验了一下,但是感觉和我的思路不太一样,我希望的是那种可以直接在线编辑文档,然后无需编译就可以直接发布的方式,另外可以在线写(修改)代码并且运行的效果。

VuePress 是“静态网站生成器”,需要我们自行编写文档,然后交给VuePress变成网站,VuePress 并没有提供编写环境,我知道有很多编写 Markdown 的方式,但是我还是喜欢编写、浏览合为“一体”的方式。

基于 vite2 + Vue3 写一个在线帮助文档工具

似乎没有,那么 —— 自己动手丰衣足食吧,开干!

技术栈

  • vite: ^2.7.0
  • vue: ^3.2.23
  • axios: ^0.25.0 获取json格式的配置和文档(CDN方式引入)
  • element-plus: ^2.0.2 UI库
  • nf-ui-elp": ^0.1.0 二次封装的UI库
  • @element-plus/icons-vue: ^0.2.4 图标
  • @kangc/v-md-editor:"^2.3.13 md 编辑器
  • vite-plugin-prismjs: ^0.0.8 代码高亮
  • nf-state": ^0.2.4 状态管理
  • nf-web-storage": ^0.2.3 访问 indexedDB

建立库项目(@naturefw/press-edit)实现文档的编写、浏览功能

首先使用 vite2 建立一个 Vue3 的项目:

  • 安装 elementPlus 实现页面效果;
  • 安装 v-md-editor 实现 Markdown 的编辑和显示;
  • 安装 @naturefw/storage 操作 indexedDB ,实现帮助文档的存储;
  • 安装 @naturefw/nf-state 实现状态管理;
  • 安装axios 用于加载 json文件,实现导入功能。
  • 用node写一个后端API,实现写入json文件的功能。

注意:库项目需要安装以上插件,帮助文档项目只需要安装 @naturefw/press-edit 即可。

基本功能就是这样,心急的可以先看在线演示和源码。

  • 在线演示:

在线编写帮助文档

https://nfpress.gitee.io/nf-press-edit/

  • 源码:

nf-press-edit_帮助文档的管理平台: 在线文档的编辑环境,可以编写文档。

https://gitee.com/nfpress/nf-press-edit

  • 编辑页面
  • 浏览页面

两个状态:编辑和浏览

一开始做了两个项目,分别实现编辑文档和显示文档的功能,但是后来发现,内部代码大部分是相同的,维护的时候有点麻烦,所以改为在编辑文档的项目里加入“浏览”的状态,然后设置切换的功能,这样便于内部代码的维护,以后成熟了可能会分为两个单独的项目。

编辑状态的功能

  • 菜单维护
  • 文档维护
  • 文档展示
  • 导入导出
  • 在线编写/执行代码

我喜欢在线编辑的方式,这样更省心,于是我用 el-menu 实现导航和左侧的菜单,然后加上了维护功能。使用 v-md-editor 实现 Markdown 的编辑和显示。然后用node写了一个后端API,实现保存 json文件的功能,这样就完美了。

浏览状态的功能

  • 导航
  • 菜单
  • 文档展示
  • 执行代码

就是在编辑状态的功能的基础上,去掉一些功能。或者其实可以反过来思考。

实现导航

首先参考 VuePress 设置一个json文件,用于加载和保存网站信息、导航信息。

/public/docs/.nfpress/project.json

{
  "projectId": "1000",
  "title": "nf-press-edit !",
  "description": "这是一个在线编辑、展示文档的小工具",
  "navi": [
    {
      "naviId": "1010",
      "text": "指南",
      "link": "menu"
    },
    {
      "naviId": "1020",
      "text": "组件",
      "link": "menu"
    },
    {
      "naviId": "1380",
      "text": "Gitee",
      "link": "帮助文档/nf-press-edit_帮助文档的管理平台"
    },
    {
      "naviId": "1390",
      "text": "在线演示",
      "link": "在线编写帮助文档"
    },
    {
      "naviId": "1395",
      "text": "我要提意见",
      "link": "Issues · 帮助文档/nf-press-edit_帮助文档的管理平台 - Gitee.com"
    }
  ]
}
  • projectId:项目ID,可以用于区分不同的帮助文档项目。
  • navi: 存放导航项。
  • naviId: 关联到菜单。
  • text: 导航上显示的文字。
  • link: 连接方式或链接地址。menu:表示要打开对应的菜单;URL:在新页面里打开连接。

然后做一个组件,用 el-menu 绑定数据渲染出来即可实现导航效果。

/lib/navi/navi.vue

<el-menu
    :default-active="activeIndex2"
    class="el-menu-demo"
    mode="horizontal"
    v-bind="$attrs"
    :background-color="backgroundColor"
    @select="handleSelect"
  >
    <el-menu-item
      v-for="(item, index) in naviList"
      :key="index"
      :index="item.naviId"
    >
      {{item.text}}
    </el-menu-item>
  </el-menu>

可以是多级的导航,暂时没有实现在线维护功能。


  import { ref } from 'vue'
  import { ElMenu, ElMenuItem } from 'element-plus'
  import { state } from '@naturefw/nf-state'
   
  const props = defineProps({
    'background-color': { // 默认背景色
      type: String,
      default: '#ece5d9'
    },
    itemProps: Object
  })

  // 获取状态和导航内容
  const { current, naviList } = state
  // 激活第一个导航项
  const activeIndex2 = ref(naviList[0].naviId)
  
  const handleSelect = (key, keyPath) => {
    const navi = naviList.find((item) => item.naviId === key)
    if (navi.link === 'menu') {
      // 打开菜单
      current.naviId = key
    } else {
      // 打开连接
      window.open(Navi Link, '_blank')
    }
  }
  • @naturefw/nf-state

自己写的一个轻量级状态管理,可以当做大号 reactive 来使用,通过状态管理加载 project.json 然后绑定渲染。

  • naviList

导航列表,由状态管理加载。

  • current

当前激活的各种信息,比如“current.naviId”表示激活的导航项。

实现菜单

和导航类似,只是需要增加两个功能:n级分组和维护。

首先参考 VuePress 设置一个json文件,保存菜单信息。

/public/docs/.nfpress/menu.json

[
  {
    "naviId": "1010",
    "menus": [
      {
        "menuId": "110100",
        "text": "介绍",
        "description": "描述",
        "icon": "FolderOpened",
        "children": []
      },
      {
        "menuId": "111100",
        "text": "快速上手",
        "description": "描述",
        "icon": "FolderOpened",
        "children": [
          {
            "menuId": 111120,
            "text": "编辑文档项目",
            "description": "",
            "icon": "UserFilled",
            "children": []
          },
          {
            "menuId": 111130,
            "text": "展示文档项目",
            "description": "",
            "icon": "UserFilled"
          }
        ]
      } 
    ],
    "ver": 1.6
  },
  {
    "naviId": "1020",
    "menus": [
      {
        "menuId": "21000",
        "text": "导航(docNavi)",
        "description": "描述",
        "icon": "Star",
        "children": []
      } 
    ],
    "ver": 1.5
  }
]
  • naviId: 关联导航项ID,可以是数字,也可以是其他字符。需要和导航项ID对应。
  • menus: 导航项对应的菜单项集合。
  • menuId: 菜单项ID,关联一个文档,可以是数字或者英文。
  • text: 菜单项名称。
  • description: 描述,考虑以后用于查询。
  • icon: 菜单使用的图标名称。
  • children: 子菜单项目,没有的话可以去掉。
  • ver: 版本号,便于更新文档。

然后用 el-menu 绑定数据渲染,因为要实现n级分组,所以做一个递归组件实现n级菜单的效果。


实现n级分组菜单

做一个递归组件实现n级分组的功能:

/lib/menu/menu-sub-edit.vue

<template v-for="(item, index) in subMenu">
    <!--树枝-->
    <template v-if="item.children && item.children.length > 0">
      <el-sub-menu 
        :key="item.menuId + '_' + index"
        :index="item.menuId"
        style="vertical-align: middle;"
      >
        <template #title>
          <div style="display:inline;width: 100%;">
            <component
              :is="$icon[item.icon]"
              style="width: 1.5em; height: 1.5em; margin-right: 8px;vertical-align: middle;"
            >
            </component>
            <span>{{item.text}}</span>
          </div>
        </template>
        <!--递归子菜单-->
        <my-sub-menu2
          :subMenu="item.children"
          :dialogAddInfo="dialogAddInfo"
          :dialogModInfo="dialogModInfo"
        />
      </el-sub-menu>
    </template>
    <!--树叶-->
    <el-menu-item v-else
      :index="item.menuId"
      :key="item.menuId + 'son_' + index"
    >
      <template #title>
        <div style="display:inline;width: 100%;">
          <span style="float: left;">
            <component
              :is="$icon[item.icon]"
              style="width: 1.5em; height: 1.5em; margin-right: 8px;vertical-align: middle;"
            >
            </component>
            <span >{{item.text}}</span>
          </span>
        </div>
      </template>
    </el-menu-item>
  </template>
  import { ElMenuItem, ElSubMenu } from 'element-plus'
  // 展示子菜单 - 递归
  import mySubMenu2 from './menu-sub.vue'

  const props = defineProps({
    subMenu: Array, // 要显示的菜单,可以n级
    dialogAddInfo: Object, // 添加菜单
    dialogModInfo: Object // 修改菜单
  })
  • subMenu 要显示的子菜单项
  • dialogAddInfo 添加菜单的信息
  • dialogModInfo 修改菜单的信息


实现菜单的维护功能

这个就比较简单了,做个表单实现菜单的增删改即可,篇幅有限跳过。

实现 Markdown 的编辑

使用 v-md-editor 实现 Markdown 的编辑和展示,首先该插件非常好用,其次支持VuePress的主题。

建立 /lib/md/md-edit.vue 实现编辑 Markdown 的功能:

  <v-md-editor
    :toolbar="toolbar"
    left-toolbar="undo redo clear | tip emoji code | h bold italic strikethrough quote | ul ol table hr | link image  | save | customToolbar"
    :include-level="[1, 2, 3, 4]"
    v-model="current.docInfo.md"
    :height="editHeight + 'px'"
    @save="mySave"
  >
  </v-md-editor>


 import { watch,ref  } from 'vue'
  import { ElMessage, ElRadioGroup, ElRadioButton } from 'element-plus'
  import mdController from '../service/md.js'
  
  // 状态
  import { state } from '@naturefw/nf-state'

  // 获取当前激活的信息
  const current = state.current
  // 文档的加载和保存
  const { loadDocById, saveDoc } = mdController()
  
  // 可见的高度
  const editHeight = document.documentElement.clientHeight - 200

  // 单击 保存 按钮,实现保存功能
  const mySave = (text, html) => {
    saveDoc(current)
  }
  // 定时保存
  let timeout = null
  let isSaved = true
  const timeSave = () => {
    if (isSaved) {
      // 保存过了,重新计时
      isSaved = false
    } else {
      return // 有计时,退出
    }

    timeout = setTimeout(() => {
      // 保存文档
      saveDoc(current).then(() => {
        ElMessage({
          message: '自动保存文档成功!',
          type: 'success',
        })
      })
      isSaved = true
    }, 10000)
  }

  // 定时保存文档
  watch(() => current.docInfo.md, () => {
    timeSave()
  })

  // 根据激活的菜单项,加载对应的文档
  watch( () => current.menuId, async (id) => {
    const ver = current.ver
    loadDocById(id, ver).then((res) => {
      // 找到了文档
      Object.assign(current.docInfo, res)
    }).catch((res) => {
      // 没有文档
      Object.assign(current.docInfo, res)
    })
  })
  • mdController 实现文档的增删改查的controller
  • timeSave 定时保存文档,避免忘记点保存按钮

是不是挺简单的。


实现在线编写代码并且运行的功能

因为是基于Vue3建立的项目,而且也是为了写vue3相关的帮助文档,那么就有一个很实用的要求:在线写代码并且可以运行

个人感觉这个功能还是很实用的,我知道有第三方网站提供了这种功能,但是网速有点慢,另外有一种大炮打蚊子的感觉,我只需要实现简单的代码演示。

于是我基于 vue 的 defineAsyncComponent 写了一个简单版的在线编写代码且运行的功能:

/lib/runCode/run.vue

<div style="padding: 5px; border: 1px solid #ccc!important;">
    <async-comp></async-comp>
  </div>
  import {
    defineAsyncComponent,
    ref, reactive,...
    // 其他常用的vue内置指令
  } from 'vue'

  // 使用 eval编译js代码
  const mysetup = `
    (function setup () {
      {{code}}
    })
  `
  
  // 通过属性传入需要运行的代码和模板
  const props = defineProps({
    code: {
      type: Object,
      default: () => {
        return {
          js: '',
          template: '',
          style: ''
        }
      }
    }
  })

  const code = props.code

  // 使用 defineAsyncComponent 让代码运行起来
  const AsyncComp = defineAsyncComponent(
    () => new Promise((resolve, reject) => {
        resolve({
          template: code.template, // 设置模板
          style: [code.style], // 大概是样式设置,但是好像没啥效果
          setup: (props, ctx) => {
            const tmpJs = code.js // 获取js代码
            let fun = null // 转换后的函数
            try {
              if (tmpJs)
                fun = eval(mysetup.replace('{{code}}', tmpJs)) // 用 eval 把 字符串 变成 函数
            } catch (error) {
              console.error('转换出现异常:', error)
            }

            const re = typeof fun === 'function' ? fun : () => {}

            return {
              ...re(props, ctx) // 运行函数,解构返回对象
            }
          }
        })
      })
  )
  • defineAsyncComponent

实用 defineAsyncComponent 加载组件,需要设置三个部分:模板、setup和style。

  • template: 字符串形式,可以直接传入
  • setup: js代码,可以用eval的方式进行动态编译。
  • style: 可以设置样式。

这样即可让在线编写的代码运行起来,当然功能有限,只能用于一些简单的代码演示。


导出

以上这些功能都是基于 indexedDB 进行的,想要发布的话,需要先导出为json文件。

因为浏览器里不能直接写文件,所以需要使用折中的方式:

  • 复制粘贴
  • 下载
  • 导出


复制粘贴

这个简单,用文本域显示json即可。

下载

使用 chrome 浏览器提供的下载功能下载文件。

 const uri = 'data:text/json;charset=utf-8,\ufeff' + encodeURIComponent(show.navi)

  //通过创建a标签实现
  var link = document.createElement("a")
  link.href = uri
  //对下载的文件命名
  link.download = fileName
  document.body.appendChild(link)
  link.click()
  document.body.removeChild(link)

以上介绍的是内部原理,如果只是想简单使用的话,可以跳过,直接看下面的介绍。


用后端写文件

以上两种都不太方便,于是用node做了个简单的后端API,用于实现写入json文件的功能。

代码放在了 api文件夹里,可以使用 ```yarn api```运行。当然需要在 package.json 里做一下设置。

 "scripts": {
    "dev": "vite",
    "build": "vite build --mode project",
    "lib": "vite build --mode lib",
    "serve": "vite preview",
    "api": "node api/server.js"
  },

实现一个帮助文档的项目

上面介绍的是库项目的基本原理,我们要做帮助文档的时候,并不需要那么复杂。

使用 vite2 建立一个vue3的项目,然后安装 @naturefw/press-edit,使用提供的组件即可方便的实现。

main.js

首先需要在 main.js 里面做一些设置。

import { createApp } from 'vue'
import App from './App.vue'

// 设置 axios 的 baseUrl
const baseUrl = (document.location.host.includes('.gitee.io')) ?
  '/doc-ui-core/' :  '/'

// 轻量级状态
// 设置 indexedDB 数据库,存放文档的各种信息。
import { setupIndexedDB, setupStore } from '@naturefw/press-edit'
// 初始化 indexedDB 数据库
setupIndexedDB(baseUrl)
  
// UI库
import ElementPlus from 'element-plus'
// import 'element-plus/lib/theme-chalk/index.css'
// import 'dayjs/locale/zh-cn'
import zhCn from 'element-plus/es/locale/lang/zh-cn'

// 二次封装
import { nfElementPlus } from '@naturefw/ui-elp'
// 设置icon
import installIcon from './icon/index.js'

// 设置 Markdown 的配置函数
import setMarkDown from './main-md.js'

// 主题
import vuepressTheme from '@kangc/v-md-editor/lib/theme/vuepress.js'

const {
  VueMarkdownEditor, // Markdown 的编辑器
  VMdPreview // Markdown 的浏览器
} = setMarkDown(vuepressTheme)

const app = createApp(App)
app.config.globalProperties.$ELEMENT = {
  locale: zhCn,
  size: 'small'
}

app.use(setupStore) // 状态管理
  .use(nfElementPlus) // 二次封装的组件
  .use(installIcon) // 注册全局图标
  .use(ElementPlus, { locale: zhCn, size: 'small' }) // UI库
  .use(VueMarkdownEditor) // markDown编辑器
  .use(VMdPreview) // markDown 显示
  .mount('#app')
  • baseUrl: 根据发布平台的情况进行设置,比如这里需要设置为:“/doc-ui-core/”
  • setupIndexedDB: 初始化 indexedDB 数据库
  • setupStore: 设置状态
  • element-plus:element-plus 可以不挂载,但是css需要 import 进来,这里采用CDN的方式引入。
  • nfElementPlus: 二次封装的组件,便于实现增删改查。
  • setMarkDown: 加载 v-md-editor ,以及需要的插件。
  • vuepressTheme: 设置主题。

设置 Markdown

因为 v-md-editor 相关设置比较多,所以设置了一个单独文件进行管理:

/src/main-md.js

// Markdown 编辑器
import VueMarkdownEditor from '@kangc/v-md-editor'
import '@kangc/v-md-editor/lib/style/base-editor.css'
// 在这里引入,不被识别?
// import vuepressTheme from '@kangc/v-md-editor/lib/theme/vuepress.js'
import '@kangc/v-md-editor/lib/theme/style/vuepress.css'

// 代码高亮
import Prism from 'prismjs'


// emoji
import createEmojiPlugin from '@kangc/v-md-editor/lib/plugins/emoji/index'
import '@kangc/v-md-editor/lib/plugins/emoji/emoji.css'

// 流程图
// import createMermaidPlugin from '@kangc/v-md-editor/lib/plugins/mermaid/cdn'
// import '@kangc/v-md-editor/lib/plugins/mermaid/mermaid.css'

// todoList
import createTodoListPlugin from '@kangc/v-md-editor/lib/plugins/todo-list/index'
import '@kangc/v-md-editor/lib/plugins/todo-list/todo-list.css'

// 代码行号
import createLineNumbertPlugin from '@kangc/v-md-editor/lib/plugins/line-number/index';

// 高亮代码行
import createHighlightLinesPlugin from '@kangc/v-md-editor/lib/plugins/highlight-lines/index'
import '@kangc/v-md-editor/lib/plugins/highlight-lines/highlight-lines.css'

// 复制代码
import createCopyCodePlugin from '@kangc/v-md-editor/lib/plugins/copy-code/index'
import '@kangc/v-md-editor/lib/plugins/copy-code/copy-code.css'


// markdown 显示器
import VMdPreview from '@kangc/v-md-editor/lib/preview'
// import '@kangc/v-md-editor/lib/style/preview.css'


/**
 * 设置 Markdown 编辑器 和浏览器
 * @param {*} vuepressTheme 
 * @returns 
 */
export default function setMarkDown (vuepressTheme) {

  // 设置 vuePress 主题
  VueMarkdownEditor.use(vuepressTheme,
    {
      Prism,
      extend(md) {
        // md为 markdown-it 实例,可以在此处进行修改配置,并使用 plugin 进行语法扩展
        // md.set(option).use(plugin);
      },
    }
  )
  
  // 预览
  VMdPreview.use(vuepressTheme,
    {
      Prism,
      extend(md) {
        // md为 markdown-it 实例,可以在此处进行修改配置,并使用 plugin 进行语法扩展
        // md.set(option).use(plugin);
      },
    }
  )
  
  // emoji
  VueMarkdownEditor.use(createEmojiPlugin())
  // 流程图
  // VueMarkdownEditor.use(createMermaidPlugin())
  // todoList
  VueMarkdownEditor.use(createTodoListPlugin())
  // 代码行号
  VueMarkdownEditor.use(createLineNumbertPlugin())
  // 高亮代码行
  VueMarkdownEditor.use(createHighlightLinesPlugin())
  // 复制代码
  VueMarkdownEditor.use(createCopyCodePlugin())
  

  // 预览的插件
  VMdPreview.use(createEmojiPlugin())
  VMdPreview.use(createTodoListPlugin())
  VMdPreview.use(createLineNumbertPlugin())
  VMdPreview.use(createHighlightLinesPlugin())
  VMdPreview.use(createCopyCodePlugin())
  
  return {
    VueMarkdownEditor,
    VMdPreview
  }

}


不多介绍了,可以根据需要选择插件。

布局

在App.vue文件里面进行整体布局

 <el-container>
    <el-header>
      <!--导航-->
      <div style="float: left;">
        <!--写网站logo、标题等-->
        <h1>nf-press</h1>
      </div>
      <div style="float: right;min-width: 100px;height: 60px;padding-top: 13px;">
        <!--写网站logo、标题等-->
        <el-switch v-model="$state.current.isView" v-bind="itemProps"></el-switch>
      </div>
      <div style="float: right;min-width: 600px;height: 60px;">
        <!--网站导航-->
        <doc-navi ></doc-navi>
      </div>
    </el-header>
    <el-container>
      <!--左侧边栏-->
      <el-aside width="330px">
        <!--菜单-->
        <doc-menu ></doc-menu>
      </el-aside>
      <el-main>
        <!--文档区域-->
        <component
          :is="docControl[$state.current.isView]"
        />
      </el-main>
    </el-container>
  </el-container>
  import { reactive, defineAsyncComponent } from 'vue'
  import { ElHeader, ElContainer ,ElAside, ElMain } from 'element-plus'
  import { docMenu, docNavi, config } from '@naturefw/press-edit' // 菜单 导航
  import docView from './views/doc.vue' // 显示文档

  // 加载菜单子控件
  const docControl = {
    true: docView,
    false: defineAsyncComponent(() => import('./views/main.vue')) // 修改文档
  }

  const itemProps = reactive({
    'inline-prompt': true,
    'active-text': '看',
    'inactive-text': '写',
    'active-color': '#378FEB',
    'inactive-color': '#EA9712'
  })


  • $state:全局状态,$state.current.isView 设置是否是浏览状态。
  • doc-navi:导航组件
  • doc-menu:菜单组件
  • docControl:根据状态选择加载显示组件或者编辑组件的字典。

这种方式虽然有点麻烦,但是比较灵活,可以根据需要进行各种灵活设置,比如添加版权信息、备案信息、广告等内容。

导航、菜单、编辑和浏览

直接使用组件实现,比较简单不搬运了,直接看源码即可。

打包发布与版本管理

需要打包的情况分为两种:第一次打包、修改代码(非在线编辑的代码)后打包。

如果只是文档内容有变化的话,只需要直接上传json文件即可,不需要再次打包。

内置了一个简单的版本管理功能,可以通过 ver.json文件里的版本号实现更新功能。

相关推荐

外贸网站建设要多少钱?外贸网站建设周期要多久

大家好,我是【无锡柠萌网络lemon56.com】的小美,今天分享:外贸网站建设要多少钱?外贸网站建设周期要多久您关注的问题·FAQ外贸网站建设中遇到的常见问题,让您少走弯路,提高效率!...

500块搭建一个可以卖货的跨境电商独立站,包括服务器和域名吗?

用500元搭建一个可以卖货的跨境电商独立站,虽然有一定挑战性,但通过精打细算和选择合适的工具和服务,仍然是有可能的。以下是一些建议,帮助你在预算范围内实现这个目标,包括服务器和域名的选择。...

无锡网络公司设计搭建阀门网站一般要多少钱?

无锡网络公司设计搭建阀门网站一般要多少钱?这是一个涉及多方面因素的复杂问题,其费用因网站规模、功能需求、设计复杂度及后续维护等因素而异。首先,从基础成本出发,域名注册是搭建网站的第一步,费用通常在几十...

定制化网站开发大概多少钱

定制化网站开发的价格因项目的复杂性、功能需求、设计要求和开发时间等因素而异。以下是一些常见的定制化网站开发价格范围,供参考:1.简单的定制化网站开发:一般来说,一个简单的定制化网站开发项目可能需要花...

9月安卓手机性能排行榜出炉:前十名差距仅为7%

时间已经进入9月,按照安兔兔目前的安卓手机性能榜单显示,红魔9SPro+依旧力压其他热销手机成为榜首,可见游戏定位的这款手机,在性能上确实非常的出色。不过,笔者也发现了一个问题,其中前十名中得分最高...

装修公司怎么在网络平台接单?实现高效获客

装修行业正经历着深刻的变革,随着消费者需求的日益多样化与个性化,传统的线下获客方式已难以满足装修公司的业务需求。因此,装修公司必须紧跟时代步伐,充分利用网络平台实现高效获客。1、入驻装修接单平台:壹品...

企业如何利用二维码进行线上营销?

#企业如何利用二维码进行线上营销?#企业利用二维码进行线上营销,可以从以下几个方面入手:?1.设计创意二维码?:...

家电销售该怎么线上拓客

社交媒体平台:利用微信、微博、抖音等平台,定期发布家电产品的信息、使用心得、促销活动等内容。引流靠手动一定是不行的,所以一般使用点软件肯定没错,最近用了款比较冷门的APP,"里德助手Plus...

pop社交软件,脱单软件排行榜

在交友软件里面脱单是非常明确的一件事,各年龄各社交平台上的女人都是有所不同的,具体问题具体分析一下。接下来就说说又哪些比较优质的恋爱脱单软件。...

手机处理器最新跑分排行榜,你的手机什么水平?

手机处理器最新跑分排行榜,你的手机什么水平?第1名:天玑9300第2名:骁龙8Gen3第3名:A17Pro第4名:天玑9200第5名:骁龙8Gen2第6名:骁龙8Gen1...

继番茄小说后,字节再推免费网文 App“蛋花小说”和“常读小说”

据Tech星球报道,字节跳动公司近期推出了两款全新的免费网文App,分别是“蛋花小说”和“常读小说”。这两款产品的开发公司分别为湖北福瑞兴网络科技有限公司和湖北聚合润网络科技有限公司,均为字节跳动的1...

全世界最好用的AI软件排名是?

Hey小伙伴们,今天咱们来聊聊那些让人爱不释手的AI神器!在这个智能化时代,谁还没几个拿得出手的AI软件呢?别急,我这就给你盘点一波全球超火的AI软件,保证让你大开眼界!...

手机root软件哪个成功率高?手机root软件排行榜2025

作为游戏玩家,欲在手游中畅玩无阻,root权限不可或缺,可优化画质、启用辅助。2025年已至,究竟哪些手机root软件能脱颖而出?哪款软件的root成功率会独占鳌头?一、手机root软...

独立站新手教程引流篇:如何优化谷歌广告投放效果?

随着谷歌广告单价的持续上涨,如何在提升投放效果的同时,降低推广费用成为每一个独立站卖家的必修课。因此新手卖家在完成初步的广告投放流程后,最重要的就是了解一下谷歌广告优化的基础策略。设置转化跟踪即利用G...

云媒易:干货知识分享!海外推广的渠道有哪些,如何正确的选择?

越来越多的海外企业或国内的跨境商家认识到海外的网络营销的作用,开展线上外贸营销,渠道是非常的重要,那么海外的推广渠道有哪些?企业应该如何选择呢?1、社交媒体推广社交媒体推广是现在海外推广方式中最热门的...

取消回复欢迎 发表评论: