Skip to content

fafalong/element-ui-cms

 
 

Repository files navigation

测试

测试地址

web 网站

CMS

正式地址

web 网站

CMS

本地直接使用 token 登录

http://localhost:8001/?token=

目录

.
├── README.md     # 文档
├── dist    # 生产部署文件
├── docs    # 示例部署文件
├── public
│   ├── index.html    # 入口页的HTML模板文件
│   └── favicon.ico
├── server    # local-web-server配置文件
│   ├── build.example.js    # 编译示例文件
│   ├── config.mock.js    # mock接口
│   ├── config.prod.example.js    # 示例
│   ├── config.prod.js    # 生产
│   └── mocks    # mock数据
├── src
│   ├── api    # 接口
│   ├── assets
│   │   ├── css
│   │   └── img
│   ├── components    # 组件
│   │   ├── common   # 公共组件
│   │   ├── layouts   # 布局组件
│   │   └── pages   # 页面组件
│   ├── config    # 项目配置数据,常量
│   ├── i18n    # 国际化
│   │   ├── en
│   │   ├── index.js
│   │   └── zh-CN
│   ├── App.vue    # 根组件
│   ├── main.js    # 入口js
│   ├── mixins    # 混入
│   ├── router    # 路由
│   ├── store
│   │   ├── index.js    # vuex主文件
│   │   └── modules    # vuex模块
│   └── utils    # 工具方法文件
│       ├── init    # 初始化组件和插件
│       ├── load-component    # 加载组件方法
│       └── index.js
├── tool # 软件工具相关安装文件
├── vue.config.js    # vue cli项目配置文件
├── jsconfig.json    # 用于VSCodeIDE智能提示的配置文件
├── apiary.apib     # 接口文档
├── package.json
└── README.md

阐述

  • 本项目是基于Element UI开发的 CMS 原型,依赖 vuevue-routervuex @panhezeng/*等类库,详见 package.json

  • 本项目的理念是简单好改,专注解决开发 CMS 最基本和核心的需求,用户访问页面权限,数据列表的展示,列表查询筛选排序,列表项的增删查改等

  • 本项目主要是对本人以前开发的 CMS 的抽象封装和重构优化,参考了 Ant Design Pro 和 Vue Element Admin,和它们的最重要区别在于,当用户通过点击菜单浏览列表页面,然后进行翻页,筛选等操作后,再刷新页面,依然可以恢复刷新前的列表查询条件

  • 最佳实践,最大限度使用 Vue 的组合,插槽(slot),混入 (mixins),抽象封装可复用代码

  • 本项目 Element UI 使用按需加载方式,可以修改 src/utils/init-components/element-ui.js 文件增减组件,自定义主题则参考 Element 文档的搭配插件按需引入组件主题部分

  • 为减少编译时间,项目初始化依赖的 vue 等类库,除了 element-ui 外,都使用 externals 方法加载,具体见 vue.config.js 的 externals 和 HtmlWebpackTagsPlugin

  • 浏览器兼容见 .browserslistrc 和 vue.config.js 的 cdnAssets polyfill.min.js (实测兼容 IE11)

  • 不需要的功能可以删除相关代码,比如如果不需要国际化,可以说删除所有 VueI18n 相关代码;不需要切换菜单布局,则可以删除多余的菜单布局代码

  • 本页面国际化切换后,使用 reload 方式生效,虽然有点不太友好,但是考虑很多地方国际化不是依赖 vue i18n 模块,而是通过其他方式,还有如果服务端数据也有多语言版本,还需要重新通过接口请求数据等

  • 根据项目需求不同,团队人员技术选型不同,在权限设计上会有所不同,本 cms 原型的菜单通过后端获得,权限细化到按钮级别,API 按照后端语言 Python 的 Django 框架要求设计。如果希望菜单放到前端等,后端使用后端 PHP 框架,则需要修改相关代码,在相关功能处已经通过注释说明

  • src/config/pages/index.js 文件包含了所有页面路由权限相关配置,具体页面访问,按钮显示的逻辑,全项目搜索 actionCodes,查看相关代码

开发流程

  • 在 src/components/pages/ 目录内建立以模块名命名 PascalCase(Pascal Case 命名规范)的目录,index.vue 是该模块默认页。 比如公告 News 模块 src/components/pages/News/ ,index.vue 是公告列表页, Item.vue 是公告详情页,components/Form.vue 是添加和编辑公告的表单组件。

  • 在 src/config/pages/ 目录内建立以模块名命名 kebab-case(kebab Case 命名规范)的 js 文件,配置该模块的页面路由相关配置。具体页面配置规则请阅读 src/config/pages/index.js 内 pages 对象的注释,参考 news.js 。

  • 如果模块需要出现在导航菜单中,以下方式二选一

    • 后端实现,在账户信息接口 /api/cms/account/ 返回对象的 menu_actions 属性上设置菜单数据,菜单对象 的 path 属性值必须和页面配置对象的 path 属性值一致。

    • 前端实现,在 src/store/modules/pages/menu/ 目录内,在 index.js 或者 development.js 文件内设置菜单数据,development.js 文件内的数据在生产模式会被丢弃。

  • 注意,如果前后端都设置了同样的菜单数据,则会出现两份同样的菜单

  • 如果需要配置导航菜单的 icon,以下方式二选一

    • 前端实现, 在 src/config/pages 目录内的页面配置 js 对象中,设置 icon 属性,值为 src/assets/img/icons/svg 中的 svg 文件名。非叶子节点的菜单是没有对应页面配置的,可以通过设置叶子节点菜单的页面配置对象的 parentIcon 属性实现 icon 显示,值和 icon 属性一样,注意如果多个子菜单设置了不同 parentIcon,按菜单顺序取第一个 parentIcon,后面的无效。

    • 后端实现,设置账户信息接口返回的菜单对象的 icon 属性,要求同上。

  • 在 src/api/ 目录内建立以模块名命名 kebab-case(kebab Case 命名规范)的 js 文件,配置该模块的接口。接口 url 请到 mock 文档查看,如果是列表类的增删查改模块,api 方法可继承 common-crud.js,参考 news.js 。

  • 如果想把关联度高的模块放在一起,也可以采用父模块名目录嵌套子模块名目录或文件的形式。

  • 拿到开发需求后,拷贝粘贴 News 模块相关目录文件,改成新需求的模块名,即可快速开始。具体细节请阅读注释。请修改 index.vue 和 Form.vue 内的 import api from '@/api/news' 的 news 为 新需求模块的 api 文件名

  • vue.config.js devServer 配置 api 接口 代理

用法

  • 如需单文件上传,src 目录搜索 single-upload ,查看 SingleUpload 组件用法示例

  • 如需多文件上传,src 目录搜索 multiple-upload ,查看 MultipleUpload 组件用法示例

  • 如需文本框输入联想搜索 远程搜索单选,src 目录搜索 fetchSuggestions ,查看 el-autocomplete 组件用法示例

  • 如需远程搜索多选 tag 和创建 tag 类数据,src 目录搜索 select-remote-multiple ,查看 SelectRemoteMultiple 组件用法示例

  • 如需列表项筛选, src 目录搜索 :filters ,查看 el-table-column 组件用法示例,需要 el-table 组件 设置 @filter-change="listScope.filterChange"

  • 如需列表后端排序,src 目录搜索 sortable="custom" ,查看 el-table-column 组件用法示例,需要 el-table 组件 设置 @sort-change="listScope.sortChange"

  • 如需拖拽排序 在需要实现拖拽排序的组件内混入 src/mixins/sortable.js,src 目录搜索 sortable ,查看 sortable 用法示例

  • 如需富文本编辑器, src 目录搜索 Editor ,查看 Editor 组件用法示例

  • 基于 src/components/common/Page/List/index.vue 组件的列表页,如需给初始获取列表数据的接口请求设置 query 数据,可以使用 beforeRouteEnter 的 next 方法实现,src 目录搜索 path: to.path , 查看用法示例

  • src/config/form-rules.js src/mixins/form.js 提供常用的表单验证规则

  • element 的表单验证支持的类型和扩展使用方法见async-validator

  • 如果 element ui 的 icon 不够用,可以使用 svg-icon 组件,src 目录搜索 svg-icon ,查看用法示例。 在iconfont制作下载 icon 的 svg 文件放到 src/assets/img/icons/svg 目录,在 svg-icon 标签的 icon 属性上添上 svg 的名字即可

  • 外部组件的所有属性方法事件用法,请查看组件文档,或者直接查看 node_modules 内组件源码

注意

  • 两个以上页面级组件会用到的组件放在 src/components/common 内

  • 公共组件和 mixins 的修改要谨慎,确认是普遍性需求,并且兼容之前代码

  • 所有混入类,比如常用的 src/mixins/list.js 和 src/mixins/form-add-edit.js 所有的属性对象函数等,都可以在使用混入类的地方进行覆写或追加数据,建议开发前先仔细阅读 src/mixins 类的代码

  • 样式相关

    • 如需定制样式, 在 root 元素写上 class="目录名-模块名", 示例

    • 所有模块样式都嵌套在 root class 内, 示例 .directory-file { .content-main {} }

    • 不要使用 scoped-css,原因很多,比如父组件无法覆写子组件的 scoped-css,通过类似 v-html 方式添加的 dom,scoped-css 不起作用等

    • element ui 的部分组件在渲染后是会提取放到 body 元素下,比如 el-dialog,所以这些组件的 class name 和样式,需要特别处理,必须保证 class name 的唯一性,使用目录名-模块名-组件名的方式实现,并且不能嵌套在 root class 内

  • 如需组件名, 组件名由目录名和文件名构成, 帕斯卡命名法, 示例 name: 'DirectoryFile'

  • 列表页组件相关

    • 组件 created 方法 默认初始化列表数据,列表查询表单的重置查询,列表项筛选都通过 apiListParamsUpdate 重新请求列表数据,其他情况自行调用 fetchData 方法

    • 如需查询表单, page-list 标签写上 :api-list-params="apiListParams" 属性,并且在 page-list 元素内使用 slot,示例 表单项

    • 如需带展开功能的查询表单, page-list 标签写上 collapse-form 属性,并且在 query form slot 元素内写上子元素 展开后显示的表单项

    • 通过定义页面组件的 data,computed 可以覆写 MixinList 的 data,computed 定义, 比如 apiList 等

    • 通过页面组件的 data 定义 apiListParams 对象定义列表接口参数数据,包括列表页查询表单数据,el-table 的 filters 数据,列表页初始化时请求列表接口参数数据初始值

    • 用 apiListParamsResetExcludeKeys 排除不点击查询重置按钮重置的字段

    • 如果基于 page-list 组件的页面要实现刷新页面回写查询表单数据,则必须在 data 里面定义 apiListParams 数据,注意 apiListParams 对象只支持数字和字符串类型数据,如果查询表单有组件绑定值为 object 或 array,则必须特殊处理,具体使用示例搜索 CascaderValueLast

    • 通过页面组件的 data 定义 apiListParamExcludeKeys 数组,剔除列表请求接口的参数

    • 如果需要接管列表页点击添加或编辑按钮的行为,page-list 标签写上 header-btn-add-callback 属性 和 header-btn-edit-callback 属性,page-list-action-cell-btn 标签写上 btn-edit-callback 属性

    • 如需批量操作按钮, page-list 标签写上 header-btn-batch 属性

      • 如需增加 header 区域按钮,在 page-list 元素内使用 slot,slot 名字查看 page-list 组件内 <slot name="header 打头的代码

      • 列表元素是在 page-list 元素内写上 slot 命名为 list 的子元素

    • 在 page-list 组件内查看所有 slot 的可使用 slot 数据

  • 添加编辑页组件相关

    • 通过定义页面组件的 data,computed 可以覆写 MixinFormAddEdit 的 data,computed 定义, 比如 id, apiCreate 等

    • MixinFormAddEdit 提供了提交前拦截,不返回等机制,具体看 src/mixins/form-add-edit.js

  • 如果是在 jsx 中使用组件,需要通过 Vue.component 方式注册组件

  • 注意,用 element 组件的 Table 表格组件,用属性修改宽度样式等,需要刷新页面才能看到正式效果,热更新的方式会异常

  • 有些热更新情况下会出现的警告之类的可以忽略,正常刷新无问题

  • 富文本编辑器、单文件上传组件等自定义组件的表单项验证需要设置 :show-message="!formData.bar" 才能实现预期效果

IDE

WebStorm

Languages and Frameworks | JavaScript | Webpack,点击文件夹图标选择项目的 node_modules/@vue/cli-service/webpack.config.js 文件,实现智能跳转和提示

搜索 eslint,打钩激活

安装 prettier 插件,搜索 File Watchers,点击+,选择 prettier,添加 js 和 vue 的 watcher,可能需要翻墙下载插件,可以使用 tool 目录内的本地文件安装,和导入 watcher 官方文档

Languages and Frameworks | JavaScript ,选择 JSX

每次安装了新版本 node,需要搜索 Node.js , Node interpreter 选择最新版本 node , 勾选 Coding assistance for Node.js , Package manager 选择 npm

VSCode

配置 jsconfig.json 文件,实现智能跳转和提示

安装 prettier 插件,官方文档

配置

    "prettier.eslintIntegration": true, // 开启 eslint 支持
    "eslint.autoFixOnSave": true, // 每次保存时自动修复
    "eslint.validate": [ // 开启对.vue文件中错误的检查
        "javascript",
        "javascriptreact",
        {
            "language": "html",
            "autoFix": true
        },
        {
            "language": "vue",
            "autoFix": true
        }
    ]

Sublime Text

安装插件:SublimeLinter-eslint

修改 SublimeLinter 配置,SublimeLinter settings syntax_map

        "syntax_map": {
            //・・・
            "vue": "javascript",
            "vue component": "javascript",
            "html": "javascript",
        },

环境

  • Ubuntu 系统第一次需要执行 sudo apt-get install -y build-essential

  • 重置前端依赖环境,cd 到项目目录,删除前端依赖相关文件

    rm -rf node_modules package-lock.json yarn.lock && npm cache clean --force
  • 安装项目依赖包 ** 如果需要把老版本的全局模块安装到新版本 node,请把 nvm install node 替换为 nvm install node --reinstall-packages-from=node, 有些系统 nvm 命令需要手动添加到 bash,所以下面命令会找不到 nvm 报错中断,请查看 nvm 安装文档 **

    curl -o- https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash && export NVM_NODEJS_ORG_MIRROR=https://npm.taobao.org/mirrors/node && nvm install node && nvm use node && curl -o- https://gist.githubusercontent.com/52cik/c1de8926e20971f415dd/raw/e98cbe963748046f371a5c95161449b8b5bd321a/npm.taobao.sh | bash && npm install -g npm && npm install -g nrm && npm i
  • 本地测试 http://0.0.0.0:8001/

    npm run serve

  • 正式发版

    npm run build

  • 如果 npm run build 失败报错,可以尝试删除 node_modules 文件夹,package-lock.json yarn.lock 文件(如果有的话),再重新执行上面的命令

npm WARN deprecated nomnom babel-preset-es 可以无视,是因为项目依赖的底层包没有更新造成的,不影响项目功能

部署

  1. cd 到项目目录,执行以下命令
  curl -o- https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash && export NVM_NODEJS_ORG_MIRROR=https://npm.taobao.org/mirrors/node && nvm install node && nvm use node && curl -o- https://gist.githubusercontent.com/52cik/c1de8926e20971f415dd/raw/e98cbe963748046f371a5c95161449b8b5bd321a/npm.taobao.sh | bash && npm install -g npm && npm i && npm run build
  1. 拷贝 dist 目录下所有文件到服务器的 cms 域名映射的目录下,所有相关域名地址都指向目录下的 index.html.

nginx

location / {
  try_files $uri $uri/ /index.html;
}

Packages

No packages published

Languages

  • Vue 82.7%
  • JavaScript 16.3%
  • Other 1.0%