ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ThinkPHP6+Arco Design Vue:CMS前后端分离开发指南

ThinkPHP6+Arco Design Vue:CMS前后端分离开发指南 简介一套面向PHP与Vue.js开发者的CMS管理系统源码采用ThinkPHP6提供后端接口与数据层支持前端基于Arco Design Vue组件库构建适合需要快速搭建内容管理后台或学习前后端分离架构的中高级开发者。压缩包共122个文件主要包含32个Vue组件、31个JavaScript脚本、29个PHP文件以及SQL、样式、配置文件等辅助内容整体约193KB目录结构清晰可对应前端src、后端app与静态资源分层查看已有1121人学习下载。通过源码可了解ThinkPHP6的MVC分层、ORM模型与路由配置方式同时观察Arco Design Vue在表格、表单、布局等后台界面中的实际用法以及Vuex状态管理和ECMAScript新特性在真实项目中的落地。压缩包内还包含环境配置示例、依赖锁文件、SQL脚本与说明文档便于本地快速还原项目并开展二次开发。1. 基于 ThinkPHP6 和 Arco Design Vue 的 CMS 源码为什么这套组合值得拆开看如果你手里刚好有一份“基于 ThinkPHP6 和 Arco Design Vue 开发的一套CMS管理系统源码.zip”不要急着解压丢到 Web 目录里就跑先想清楚这件事这套技术栈里ThinkPHP6 承担的是内容模型、权限控制、API 输出Arco Design Vue 承担的是后台的交互界面和状态管理两者通过 JSON 接口完成前后端分离。ThinkPHP6 的多应用模式让后台管理端和 API 端可以拆成独立的应用Arco Design Vue 则把表格、表单、菜单、弹窗这些 CMS 高频组件直接封装好比自己从零拿 Element 拼要快得多。这篇文章不假设你下载过某个特定压缩包而是按这套技术栈最常见的交付形态讲清楚从环境准备、接口设计到前端对接的完整路径。新手能照着跑通熟练工能排查掉大部分隐藏配置问题。2. 解压源码包后的前 30 分钟环境检查、依赖安装与首屏启动很多 CMS 源码跑不起来问题不在代码而在 PHP 扩展、Node 版本、伪静态规则和.env配置这四样东西上。拿到源码.zip先把它还原成两个独立工程backend/是 ThinkPHP6 服务端frontend/是 Arco Design Vue 前端。下面按顺序做环境初始化。2.1 检查后端运行环境PHP 7.4 是底线ThinkPHP6 要求 PHP 7.2.5 以上但 CMS 系统里经常用到注解路由、枚举、撮合composer包的版本实际部署建议不低于 PHP 7.4推荐 8.1。打开终端先确认php -v composer --version命令输出里重点看PHP 8.1.x和Composer version 2.x如果composer提示没有安装先装 Composer 再继续。后端还需要三个常见扩展用php -m查看缺少其中一个时很多源码会直接报“类不存在”扩展作用检查方式pdo_mysql数据库连接php -mmbstring多字节字符串处理php -mcurl远程请求与第三方接口php -m这套 CMS 如果是从 ThinkPHP6 官方脚手架扩展而来还会用到topthink/think-multi-app、topthink/think-migration之类的包这些由 Composer 统一安装不需要手动下载。2.2 初始化 ThinkPHP6 后端composer install 与 .env 配置解压源码包后进入后端目录执行cd backend composer install cp .env.example .env php think runcomposer install会依据composer.json生成vendor/依赖目录如果这一步报错绝大多数情况是 PHP 版本过低或缺少某个扩展。.env文件里保存数据库账号、密钥、调试开关CMS 源码通常不会把真实数据库配置提交到压缩包里所以要先复制一份出来改。php think run是 ThinkPHP6 自带的开发服务器命令默认监听127.0.0.1:8000浏览器访问http://127.0.0.1:8000能看到前端页面或接口返回说明后端已经活了。.env里最容易改错的是APP_DEBUG和DATABASE_*我建议生产环境把APP_DEBUG设置为false否则数据库报错信息会直接暴露给访问者。2.3 初始化前端npm install 与 vite dev server前端目录一般是frontend/这套 CMS 大概率基于 Vite Vue3执行cd frontend npm install npm run devnpm install会把package.json里声明的依赖装进node_modules源码包里通常不携带这个目录所以必须执行。如果网络条件不好导致部分依赖安装慢可以临时换用镜像源npm config set registry https://registry.npmmirror.com npm install安装完成后npm run dev会启动 Vite 开发服务器默认端口是5173浏览器打开后如果页面白屏打开控制台看VITE相关报错常见原因包括 Node 版本过低Vite 4 需要 Node 14.18Vite 5 需要 Node 18。2.4 联调必备前端代理和后端跨域配置前后端分离的 CMS 在开发环境最常见的坑是跨域。Vue 页面跑在http://localhost:5173后端接口在http://127.0.0.1:8000浏览器会拦截非箭头域的请求。解决办法不是在后端拼命加跨域头而是在 Vite 里配置代理。在frontend/vite.config.js里写import { defineConfig } from vite import vue from vitejs/plugin-vue export default defineConfig({ plugins: [vue()], server: { port: 5173, host: 0.0.0.0, proxy: { /api: { target: http://127.0.0.1:8000, changeOrigin: true } } } })这段配置的意思是前端所有以/api开头的请求Vite 都会转发到8000端口的后端服务。/api之外的请求走前端本地开发服务器。这样做的好处是生产环境不需要 CORS 跨域后端只需在路由里统一处理/api前缀即可。CMS 源码里如果后端路由本身不带/api前缀通常会在入口文件或路由配置里显式定义保持前后端路径一致很重要。3. ThinkPHP6 多应用模式从 URL 设计到 SQL 注入防护CMS 的接口设计直接决定前端好不好接。ThinkPHP6 默认是单应用模式单应用下控制器层级很浅一旦接入后台管理、API、H5 端整个目录会乱成一团。所以这套 CMS 采用多应用模式来拆分后台和管理前端是很常规的做法。3.1 多应用模式的目录结构与应用绑定在本地刚解压的源码里如果app目录下面有好几个子目录而不是直接放控制器说明启用了多应用模式。这依赖topthink/think-multi-app这个扩展包。安装这个包后可以创建app/api目录作为接口应用创建app/admin目录作为后台管理应用。一个常见的目录切分方式app/ ├── api/ │ ├── controller/ │ │ ├── Article.php │ │ └── User.php │ └── route/ │ └── app.php ├── admin/ │ ├── controller/ │ │ └── Login.php │ └── route/ │ └── app.php └── common/ └── model/api应用负责给前端提供数据接口admin应用负责 CMS 后台的登录、内容录入、权限管理。二者通过同一个数据库和不同的控制器逻辑来协作不需要额外写大量重复代码。3.2 多应用模式下的 URL 生成和路由参数多应用模式下最直接的 URL 形式是http://域名/api/article/read/id/12这种写法比较长而且会在前端拼接时出现视图路径混乱的问题。CMS 源码里更推荐用路由来固定规则。在app/api/route/app.php中定义资源路由use think\facade\Route; Route::get(article/:id, article/read) -pattern([id \d]) -name(api_article_read);这个路由的含义是前端访问/api/article/12时会映射到app\api\controller\Article类的read方法并且id参数被限定为纯数字非数字请求会被拒绝或直接 404。ThinkPHP6 多应用模式下Route::get的第二个参数如果是article/read它实际解析的是当前应用下的控制器也就是app\api\controller\Article::read。这里建议在路由定义时把参数约束加上尤其是 CMS 内容里的文章 ID、分类 ID 等整型字段避免用户传idabc打乱查询。前端 Vue 路由里对应的路径通常是/article/:id这里vue路由参数的$route.params.id会拿到 URL 里那一段数字后续在 axios 请求里拼进地址。前后端路由参数名保持一致能少排查很多问题。3.3 用查询构造器防 SQL 注入CMS 里最常见的翻车点CMS 系统的列表页、搜索页、标签页都涉及动态查询最容易出现 SQL 注入的位置就是用户输入的搜索关键字。ThinkPHP6 的查询构造器默认提供参数绑定直接使用静态方法传值即可use think\facade\Db; $keyword input(get.keyword, , trim); $list Db::name(article) -where(title, like, % . $keyword . %) -order(id, desc) -paginate(10);where的三个参数写法会把$keyword作为预处理参数传给 PDO不会拼进 SQL 语句里。只要不写-whereRaw()或-where()的字符串拼接形式就能避开最典型的 SQL 注入。哪些情况需要格外小心写法是否安全说明Db::name(article)-where(id, $id)安全参数自动绑定Db::name(article)-where(id $id)危险字符串拼接Db::name(article)-whereRaw(title like %$kw%)危险未做白名单过滤时Db::query(select * from article where id . $id)危险完全暴露如果源码里大量出现上面的危险写法建议不要直接拿到生产环境。在 ThinkPHP6 中修复方式也很简单把字符串条件拆成查询构造器的键值对形式或把whereRaw里的动态值改成:name绑定Db::name(article) -whereRaw(title like :title, [title %{$keyword}%]) -select();这样keyword就算包含引号、OR 11等片段也会被当作普通字符串值处理。3.4 权限中间件与接口状态码约定CMS 后台接口不能裸奔登录鉴别应该放在中间件里统一处理。ThinkPHP6 里创建中间件后在app/api/middleware.php注册return [ auth app\api\middleware\AuthCheck::class, ];然后在需要鉴权的路由组里引用Route::group(admin, function () { Route::get(category/list, category/list); Route::post(article/save, article/save); })-middleware(auth);中间件代码里校验请求头中的 Token如果无效则统一返回 JSONnamespace app\api\middleware; use think\Request; use think\Response; class AuthCheck { public function handle(Request $request, \Closure $next) { $token $request-header(Authorization); if (!$token || strlen($token) 20) { return Response::create([code 401, msg 登录已过期], json, 200); } return $next($request); } }这里的返回值为 HTTP 200但业务状态码是 401。CMS 前端拦截器拿到code 401后跳转登录页不会触发浏览器层面的跨域错误。接口统一返回{code, msg, data}这种结构是整个前后端协作的基础源码包里的控制器方法最好全部遵循这个约定否则前端封装会比较痛苦。4. Arco Design Vue 前端搭建 CMS 后台的可视化界面后端接口准备好之后前端的工作是“把内容操作界面做出来”。Arco Design Vue 是字节跳动开源的企业级 Vue3 组件库和 Ant Design Vue 相比它在表格、抽屉、权限布局上更轻快暗色主题和项目管理类组件也更全面。4.1 安装 Arco Design Vue 并完成基本配置在frontend/目录初始化 Vue3 工程后安装 Arconpm install arco-design/web-vue npm install arco-design/web-vue/es/icon在main.js中加入完整引用import { createApp } from vue import App from ./App.vue import ArcoVue from arco-design/web-vue import ArcoVueIcon from arco-design/web-vue/es/icon import arco-design/web-vue/dist/arco.css const app createApp(App) app.use(ArcoVue) app.use(ArcoVueIcon) app.mount(#app)这里使用的是完整引入对所有组件一次性注册开发方便但体积较大。CMS 后台并不要求首屏加载特别极致所以优先保证开发效率。如果后续要优化再手动按需引入Arco 提供了unplugin-vue-components插件来按需自动加载读者可以完全掌握两者切换的方法。安装并运行npm run dev后浏览器任意页面里写一个a-button就能验证组件库是否生效。4.2 vue-router 下搭建后台布局和动态菜单CMS 后台常见的布局是左侧菜单、顶部面包屑、右侧内容区。在App.vue或者根组件里用 Arco 的布局组件组合template a-layout styleheight: 100vh a-layout-sider collapsible a-menu :selected-keys[currentRoute] a-menu-item keydashboard template #iconicon-dashboard //template 系统概览 /a-menu-item a-menu-item keyarticle template #iconicon-file //template 文章管理 /a-menu-item /a-menu /a-layout-sider a-layout a-layout-headerCMS 管理系统/a-layout-header a-layout-content stylepadding: 16px router-view / /a-layout-content /a-layout /a-layout /template菜单和vue-router的路由表不强绑定可以在路由对象里添加meta.title和meta.icon再由菜单组件读取。动态菜单的逻辑则是在登录后请求后端返回的menuList渲染到a-menu中。这里需要处理vue路由参数的刷新问题比如编辑文章的路由是/article/edit/:id刷新页面时组件拿到route.params.id再请求详情所以菜单尽量不要使用:default-open-keys这种静态值要使用selected-keys配合$route.path动态计算。4.3 axios 请求封装统一处理 Token、超时和业务码前后端分离的 CMS 中axios 请求封装是必不可少的一层。建议在frontend/src/utils/request.js中建立实例import axios from axios import { Message } from arco-design/web-vue import router from /router const request axios.create({ baseURL: /api, timeout: 10000 }) request.interceptors.request.use( (config) { const token localStorage.getItem(cms_token) if (token) { config.headers.Authorization token } return config }, (error) Promise.reject(error) ) request.interceptors.response.use( (response) { const { code, msg, data } response.data if (code ! 0) { Message.error(msg || 请求失败) if (code 401) { router.replace(/login) } return Promise.reject(new Error(msg)) } return data }, (error) { Message.error(网络异常请稍后重试) return Promise.reject(error) } ) export default request注意baseURL: /api配合 Vite 代理开发环境下所有请求都指向后端如果后端某个接口路径不是/api开头需要调整axios.get(/xxx)的完整路径。CMS 列表页的请求通常需要携带page、pageSize、关键词等参数统一放在params对象里后端按 ThinkPHP6 的分页参数名page和list_rows或自定义参数接收即可。4.4 表格分页和内容编辑页的最小实现内容列表是 CMS 的高频场景。Arco 的a-table加上pagination属性就够用了但必须正确处理分页事件template a-table :dataarticleList :paginationpagination page-changeloadList row-keyid template #columns a-table-column titleID>npm install hls.js在 Vue 组件中实现播放器template video refvideoRef controls classvideo-player/video /template script setup import { ref, onMounted } from vue import Hls from hls.js const props defineProps({ src: { type: String, required: true } }) const videoRef ref(null) onMounted(() { if (Hls.isSupported()) { const hls new Hls() hls.loadSource(props.src) hls.attachMedia(videoRef.value) } else { videoRef.value.src props.src } }) /scriptHls.isSupported()是判断浏览器是否支持 MSE支持则用 HLS 解析 m3u8不支持时直接把切片地址交给原生播放器。这里如果后端返回的 m3u8 地址要求携带 Token 参数需要在请求头或 URL 查询参数上加上鉴权信息否则播放会 403。5. 最后 20 分钟的验证与加固让这套 CMS 源码体面地上线当接口和页面都能跑起来时CMS 源码真正剩下的工作是检查几个不起眼但直接影响安全与体验的点。这里给出一组可以直接照着做的验证命令和检查项。第一件事是验证后端是否完整执行了参数绑定。打开app/api/controller/Article.php搜索所有where、whereRaw、Db::query如果出现字符串拼接用查询构造器重写。然后在前端列表页的搜索框输入 or 11 --看是否返回全量数据如果返回了说明仍有 SQL 注入风险必须修掉。第二件事是检查后端调试模式。确认.env中APP_DEBUG false然后重启php think run随意访问一个不存在路径的接口看返回 JSON 中是否携带堆栈、SQL 语句或文件绝对路径。如果这些信息暴露了说明.env修改后没有重启进程或者项目根目录的runtime缓存未清理cd backend php think clear第三件事情是接管前端的路由权限。CMS 后台登录后如果用户直接访问/admin/user必须在vue-router的beforeEach里判断 Token 和非当前用户角色否则就会出现“前端菜单隐藏了但 URL 能直接打开”的问题。在src/router/index.js中加入router.beforeEach((to, from, next) { const token localStorage.getItem(cms_token) if (to.meta.requiresAuth !token) { next(/login) } else { next() } })最后把前端的构建产物交给 Nginx 或后端静态目录。执行npm run build后dist目录里的index.html引用的是绝对路径还是相对路径直接影响部署在二级目录时能不能加载资源。如果部署在域名根目录则不用改如果要部署在https://example.com/cms/这个子目录需要把 Vite 的base设置为/cms/再重新打包。这里最容易被忽略花十分钟确认完才不算白跑一遍源码。验证完这四步CMS 系统的基础安全边界和部署路径基本就稳了。剩下的功能调整比如富文本图片上传、模板消息推送都是在这套已经跑通的骨架上做增量开发。本文还有配套的精品资源点击获取
返回列表