整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

扁平化后台管理系统,后台管理系统前端模板(html + CS

## 回答1:中文后台管理系统模板是一种为网站或应用程序开发者提供的可重复使用的界面模板。它以中文为主要语言,并具有专为后台管理任务设计的特点和功能。该模板采用HTML语言编写,可通过复制和粘贴来使用。它通常包含了常见的后台管理功能,如用户管理、权限管理、数据管理等。模板的结构和布局已经预先设计好,使开发者可以快速搭建出界面整洁、功能完善的后台管理系统。中文后台管理系统模板通常具有以下特点:1. 响应式设计:模板能够适应各种屏幕尺寸,包括桌面、平板和手机。这使得用户无论使用何种设备访问后台管理系统扁平化后台管理系统,都能获得良好的用户体验。2. 丰富的组件和布局:模板提供了丰富的界面组件和布局选项,如导航栏、表格、图表等,使开发者能够根据实际需求快速构建页面。3. 可定制性强:开发者可以根据自身需求对模板进行定制,包括更改颜色、字体、布局等。这样可以让界面更符合项目的品牌和风格。4. 支持多语言:由于是中文模板,所以它自然支持中文。但是,它也可以支持其他语言,如英语、西班牙语等,以满足全球范围的使用需求。总之,中文后台管理系统模板是一个方便快捷的工具,能够帮助开发者节省时间和精力,从而更专注地进行后台管理相关的开发工作。

### 回答2:中文后台管理系统模板html是一种用于构建中文界面的后台管理系统的HTML模板。它提供了一系列预定义的页面布局和组件,方便开发人员快速构建功能强大、易于使用的后台管理系统。中文后台管理系统模板html通常包含以下主要组件和功能:1. 导航菜单:提供多级菜单,用于导航不同的功能页面。菜单通常以树形结构呈现,可以自由添加、删除和编辑菜单项。2. 欢迎页:作为系统登录后的默认页面,展示系统的基本信息和功能模块的快速访问入口。3. 数据表格:用于展示和编辑数据的表格组件,支持数据分页、排序、筛选和批量操作等功能。4. 表单界面:提供各种表单元素,如输入框、下拉框、复选框等,用于用户输入和提交数据。5. 图表和统计:支持各种数据可视化图表和统计报表,帮助管理员了解系统的运行状况和数据分析。6. 权限管理:提供用户和角色管理功能,支持不同用户角色的权限设置和访问控制。7. 主题和样式:支持自定义的主题和样式,方便根据实际需求进行界面风格的调整。8. 响应式布局:适应不同设备和屏幕尺寸,保证后台管理系统在桌面和移动设备上的正常使用。通过使用中文后台管理系统模板html,开发人员可以省去从零开始构建后台管理系统的繁琐工作,提高开发效率和用户体验。

同时,模板也提供了丰富的功能和组件,满足不同后台管理系统的需求,并支持自定义扩展。 ### 回答3:中文后台管理系统模板HTML是一种用于构建中文后台管理系统界面的模板。这种模板通常由HTML、CSS和等前端技术组成,旨在提供一套可复用的界面元素和样式,使用户可以快速、方便地搭建自己的管理系统。中文后台管理系统模板HTML通常包括多个页面扁平化后台管理系统,如登录页面、主页、用户管理页面、数据分析页面等。每个页面都有相应的HTML结构和样式,以及与后台服务器进行数据交互的代码。这些页面之间通过导航菜单或链接相互跳转,以实现不同功能的管理和操作。在中文后台管理系统模板HTML中,常见的界面元素包括顶部导航栏、侧边菜单栏、数据表格、表单、图表等。这些元素通过CSS进行布局和样式设计,以及通过实现一些交互效果,如表单验证、数据筛选、图表展示等。中文后台管理系统模板HTML的优点是可定制性高,用户可以根据自己的需求进行灵活的修改和扩展,以满足不同的管理系统需求。它还可以提高开发效率,节省开发成本,因为用户无需从零开始设计和开发界面,只需要根据模板的结构进行相应修改即可。总之,中文后台管理系统模板HTML是一种便捷、高效的搭建中文后台管理系统界面的工具,可以帮助用户快速构建出美观、功能完善的管理系统。

一个网站都有自己的导航菜单,如:头部导航菜单,底部导航菜单,侧边栏导航菜单,wordpress网站也不例外。那么,在wordpress网站主题模板开发中,我们怎样给wordpress网站添加前台的导航菜单呢?嗯,据我多年的开发经验发现,wordpress为wordpress主题开发,主要提供了三种导航菜单的创建方式,这三种方式会创建不一样的导航功能。今天,我们就来看看第一种wordpress网站创建导航菜单的方式——基于page页面的导航菜单。这里,我们会用到wordpress提供的函数——wp_list_pages(),这是一个wordpress页面列表的函数。

我们先来看一下这个wordpress函数——wp_list_pages(),看看它的结构。

wp_list_pages($defaults);

从上面的代码中,我们可以看到,wp_list_pages()函数只有一个参数,这个参数有两种类型,可以是字符串类型,也可以是数组类型。这个我们在下面的实例中会做相应的介绍。为了方便了解这个参数的值,我们这里以数组的形式来解说一下这个参数。

参数介绍:

$defaults =array('depth' =>0, //0:显示所有页面和子页面,按层级显示;//1:只显示顶级页面;//2:显示2级页面;//-1:显示所有页面和子页面,不按层级显示;

'show_date'=>'', //是否显示创建日期

'date_format'=> get_option('date_format'),//日期格式

'child_of'=>0, //指定父页面ID号,显示这个父页面下的子页面;0表示显示所有子页面;

'exclude'=>'', //排除哪些页面

'include'=>'', //包含哪些页面

'title_li'=>'Pages', //是否显示页面列表的标题,如果不显示,设为空;这里设置标题为“Pages”

'echo'=>1, //是否打印到前台页面显示出来。1表示显示,0表示不显示,而是只获取值。

'authors'=>'', //指定特定作者创建的页面

'link_before'=> '', //链接<a>前的内容'link_after'=>'', //链接<a>后的内容

'exclude_tree'=>'', //排除父级/子级树

'sort_column'=>'menu_order', //排序方式,menu_order按后台设置;post_date按发布时间,post_modified按修改时间;

'sort_order' => 'DESC', //排序顺序,ASC顺序,DESC是倒序);

可以看到,这个wp_list_pages()函数的参数值有很多,在我们wordpress主题模板开发的实际操作中,一般只会使用其中的几个。

下面,我们通过案例来介绍wp_list_pages()函数是如何生成基于page页面的导航菜单的。我们先来看一下,wordpress网站后台都创建了哪些page单页面,如下图:


从上图中,我们可以看到,这个wordpress网站后台有6个页面,其中,“投稿”是“子页面1”和“子页面2”的父级页面。

案例1:我们在wordpress网站模板的头部添加如下代码:

$menu = array( 'depth' =>0, 'title_li'=>'页面导航菜单', 'echo'=>1, );wp_list_pages($menu);

我们再到wordpress网站的前台页面看一下效果,如下图:


我们可以看到,页面导航展示了出来,子页面按层级展示——缩进2格。

案例2:我们来修改一个参数代码,标题设置为空,添加一个排序参数,并修改一下层级参数值,代码如下:

$menu = array( 'depth' =>1, 'title_li'=>'页面导航菜单', 'echo'=>1, 'sort_order' => 'DESC','sort_column'=>'menu_order',);wp_list_pages($menu);

这时,我们再来看看wordpress网站前台页面的效果,如下图:


​我们可以看到,导航菜单的标题不见了,而且层级没有了,排序也发生了变化,按页面名称的倒序来进行排列。wp_list_pages()的参数很多,这里不做一一演示,都很简单。

案例3:wp_list_pages()函数的参数用字符串类型。

我人在开头说过,wp_list_pages()函数的参数有2种类型,可以是字符串类型,也可以是数组类型。数组类型我们在前2个案例中已经使用过了。这里,我们再来以字符串类型来做一次介绍。

这里我们拿案例的代码来演示,把数组类型的参数换成字符串的类型,代码如下:

wp_list_pages("depth=1&title=&echo=1&sort_order=DESC&sort_column=menu_order");

上面的代码中,我们用到了一个连接符&这个特殊符号,它是用来连接多个参数。中间的=这个符号,就不用解释了,是等于号。通过这句代码,我们同样达到案例2的效果。

如果想让这个基于page页面的导航菜单能在顶部横排显示,可以修改wordpress网站模板的CSS文件的代码,修改它的样式,就可以了。这里就不多说了。

这节课就介绍到这里,以上就是我的观点,如有不同观点,欢迎发表评论。同时,欢迎【点赞、分享、收藏】和【关注】我。

SM(Finite State Machines) 有限状态机,也叫有限状态自动机,是为研究有限内存的计算过程和某些语言类而抽象出的一种计算模型,它拥有有限个数量的状态,每个状态可以迁移到零个或多个状态,输入字串决定执行哪个状态的迁移。

有限状态机有什么用

代码编译器在工作时就需要通过词法分析、语法分析、语义分析来得到 AST(Abtract Syntaxt Tree) 抽象语法树。需要先词法分析拿到的所有 token 流,接着通过语法分析将 token 流进行文法校验生成语法解析树,这个过程一般有两种:

  • 边分词边生成 AST,像解析 HTML、CSS
  • 先分词生成所有 token,再来进行语法分析生成 AST,像 js

我们在前端工作中经常用到的:babel、typescript、eslint、postcss、prettier、uniapp、htmlparse、代码编辑器的语法高亮...这些其实都是基于 AST 抽象语法树来实现的,而为了得到 AST 我们需要先进行分词,而分词一个比较好的方式就是通过有限状态机来实现。

代码的本质就是字符串,分词就是把代码字符串变成一个个最小单元到不能再拆分的单词,也叫 token(注意不是咱们平时用到的登录态 token),分词器的英文 tokenizer。代码其实跟我们一篇英文文章、一首中文古诗、一个数学运算...都是一样的,我们一样可以用分词技术来拆分这些元素。

有限状态机是怎么工作的

为了理解有限状态机到底是怎么工作的,我们先来实现一个简单的减法运算分词。要求用状态机把 500-250=250 这个减法运算分词成一个数组,首先定义一共有2种状态:number-数字、operator-运算符,每一个最小的 token 只能是这两个当中的一个,代码如下

// 500-250=250
// [
//   { type: 'number', value: '500' },
//   { type: 'operator', value: '-' },
//   { type: 'number', value: '250' },
//   { type: 'operator', value: '=' },
//   { type: 'number', value: '250' }
// ]

function mathTokenizer(text) {
  // 匹配数字正则
  const numberReg = /[0-9]/
  // 匹配运算符正则
  const operatorReg = /[-=]/
  // 存储所有读取到的 token 数组
  let tokens = []
  // 当前正在读取的 token 信息
  let currentToken = {}

  // 初始状态
  function init(e) {
    if (numberReg.test(e)) {
      currentToken = { type: 'number', value: e }
      return onNumber
    } else if (operatorReg.test(e)) {
      currentToken = { type: 'operator', value: e }
      return onOperator
    }
  }

  // 读取到数字
  function onNumber(e) {
    if (numberReg.test(e)) {
      currentToken.value += e
      return onNumber
    }
    if (operatorReg.test(e)) {
      pushToken(currentToken)
      currentToken = { type: 'operator', value: e }
      return onOperator
    }
  }

  // 读取到运算符
  function onOperator(e) {
    if (numberReg.test(e)) {
      pushToken(currentToken)
      currentToken = { type: 'number', value: e }
      return onNumber
    }
    if (operatorReg.test(e)) {
      pushToken(currentToken)
      currentToken = { type: 'operator', value: e }
      return onOperator
    }
  }

  // 每次读取到完整的一个 token 后存入到数组中
  function pushToken(token) {
    tokens.push(token)
    currentToken = {}
  }

  // 遍历读取数组
  function parse(str) {
    const len = str.length
    let stateMachine = init
    for (let i = 0; i < len; i++) {
      const char = str[i]
      stateMachine = stateMachine(char)

      // 最后一个字符匹配完了要自己 pushToken
      if (i === len - 1) {
        pushToken(currentToken)
      }
    }

    return tokens
  }

  return parse(text)
}

const arr = mathTokenizer('500-250=250')
console.log(arr)

简版的 html 解析器

词法分析,生成 token 流

利用状态机来生成 token 流,为了方便理解以下示例不考虑标签属性节点、自闭合标签和一些异常情况。

我们先定义5个状态:标签开始、结束标签开始、标签名、标签结束、文本,每次读取到的内容会在这5个状态之间切换,每次读取时只要不是标签开始、结束标签开始、标签名、标签结束这4个状态的我们都当成文本来处理。

实际上我们只需要存储:开始标签、文本、结束标签这3个状态,所以定义的节点 type 分别为:startTag、text、endTag。你要按前面定义的5个状态来储存其实也是可以的,在下面生成 AST 直接忽略掉我们不需要的标签开始、标签结束这些状态信息就行了,只不过这里我们直接在分词这一步提前就给过滤了。

这里我们可以把状态机理解成一个函数,每遍历到一个字符我们都将这个字符传到函数中,而函数中可以根据这个字符来判断下一个状态是什么,再返回出去下一个状态函数就行了。

function htmlTokenizer(str){
  // 标签开始
  const tagStartReg = /</
  // 结束标签开始
  const closeTagReg = /\//
  // 标签结束
  const tagEndReg = />/
  // 标签名
  const tagNameReg = /[a-zA-Z]/

    let tokens = []
    let currentToken = {}

  // 初始状态
  function init(e) {
    if (tagStartReg.test(e)) {
      currentToken = { type: 'startTag', tagName: '' }
            return init
        }
    if (closeTagReg.test(e)) {
      currentToken = { type: 'endTag', tagName: '' }
            return onTagName
        }
    if (tagNameReg.test(e)) {
      currentToken.tagName += e
            return onTagName
        }

    // 不是上面3个状态的都当成文本节点处理
    currentToken = { type: 'text', text: e }
    return onText
  }

  // 读取到标签名
  function onTagName(e) {
    if (tagEndReg.test(e)) {
      pushToken(currentToken)
            return init
        } else {
      currentToken.tagName = (currentToken.tagName || '') + e
            return onTagName
        }
  }

  // 读取到文本
  function onText(e) {
    if (tagStartReg.test(e)) {
      pushToken(currentToken)
      currentToken = { type: 'startTag', tagName: '' }
            return init
        } else {
      currentToken.text = (currentToken.text || '') + e
            return onText
    }
  }

  // 每次读取到完整的一个 token 后存入到数组中
    function pushToken(e) {
        tokens.push(e)
        currentToken = {}
    }

  // 遍历读取数组
  function parse(chars){
    let stateMachine = init
        for (const char of chars) {
            stateMachine = stateMachine(char)
        }
        return tokens
    }

  return parse(str)
}

const tokenList = htmlTokenizer('<div>静夜思<p>锄禾日当午</p>周小黑<p>粒粒皆辛苦</p>公元一七八八年</div>')
console.log(tokenList)

语法分析,生成 AST 抽象语法树

这一步主要就怎么能把分词得到的数组转换成树形 tree 数据结构,日常开发中我们 array 转 tree 一般都是需要根据父亲 id 之类的来实现遍历生成,但是这里咱拿到的数组是没有这个父 id 的,那要怎么实现呢?

先观察数据结构,虽然是一个数组,但是这个数组其实是个类似中心对称结构的,我们暂时先忽略掉数组里的 type 为 text 的文本内容(因为这个其实我们是不能把它当成一个父节点的,它只能是某个标签的子节点),过滤掉文本后数组第1个元素和最后1个元素正好是1对,第2个元素和倒数第2个元素又是1对,我们要实现的就是把内层获取到的一对对标签不断挂载到它前面一对签的 children 属性上来实现 tree 结构。

那我们可以从数组第一项目开始遍历,然后用一个数组来模拟 stack 栈存每次遍历到的标签信息(栈的特点是先进后出,类似我们往一个桶里放东西,放在最上面的可以最先拿出来,规定数组只能使用 push 和 pop 就能模拟栈了)。

当遇到开始标签的时候就说明遇到一个新的标签了,这时就往栈里 push 进去,当遇到结束标签时就说明当前这个标签的所有信息都已经读取处理完了,那我们就可以将它从栈里弹出来,然后现在栈里最上面的一个元素其实就是当前弹出来的父标签了,直接挂载到 children 上就行了。整个过程其实主要就是理解下面2点:

  • 用栈来缓存节点:嵌套在内部的节点就可以先出栈,根节点最后出栈
  • 用引用类型对象的特点,来不断挂载节点
function htmlAst(tokenList) {
    let stack = []

  for (let i = 0; i < tokenList.length; i++) {
    const node = tokenList[i]

    // 开始标签:入栈
        if (node.type === 'startTag'){
            stack.push(node)
        }

    // 结束标签:出栈
        if (node.type === 'endTag') {
            const currentNode = stack.pop()
            const parent = stack[stack.length - 1]

            if (parent) {
                if (!parent.children) parent.children = []
        parent.children.push(currentNode)
            } else {
        const root = { type: 'document', children: [currentNode] }
                return root
            }
        }

    // 文本:加到父标签的 children 上
        if (node.type === 'text') {
            const parent = stack[stack.length - 1]
      if (!parent.children) parent.children = []
      parent.children.push(node)
        }
  }
}

然后就能拿到我们需要的 AST 语法树了,结构如下:

{
  "type": "document",
  "children": [
    {
      "type": "startTag",
      "tagName": "div",
      "children": [
        {
          "type": "text",
          "text": "静夜思"
        },
        {
          "type": "startTag",
          "tagName": "p",
          "children": [
            {
              "type": "text",
              "text": "锄禾日当午"
            }
          ]
        },
        {
          "type": "text",
          "text": "周小黑"
        },
        {
          "type": "startTag",
          "tagName": "p",
          "children": [
            {
              "type": "text",
              "text": "粒粒皆辛苦"
            }
          ]
        },
        {
          "type": "text",
          "text": "公元一七八八年"
        }
      ]
    }
  ]
}

理解了状态机就如给你按上了一双翅膀,不管给你任何一段字符任容,都可以通过状态机来拆分成我们想要的结构,理解了上面这些再去看 vue 里的模板编译,你就能知道它到底是怎么加进去那些语法糖的了。还比如小程序中的富文本解析,特定平台的小程序实际上是不能识别浏览器里的 html 的,那我们就需要先将 html 通过状态机转成 AST,然后再按照小程序的语法来进行特定的转换。