资讯动态

基于ThinkPHP和Vue的房产销售管理系统设计与实现

发布时间:2026/9/11 9:34:49 来源:尧图企业网站定制
做了几年房产行业的信息化项目手头这套用ThinkPHP加Vue搭起来的房屋房产销售信息管理系统算是我个人比较满意的一个作品。说实话房产销售行业的软件需求一直很特殊它不像纯电商或者纯内容管理那样有现成的模板可以套很多业务流程的细节都需要定制化开发。今天把这套系统的设计与实现思路整理出来从需求分析、技术选型到前后端核心代码再到部署上线遇到的坑一次性讲清楚希望能给正在做类似系统或者准备入行房产软件开发的同行一些参考。这个系统的核心场景很明确一个房产中介公司或者开发商销售部需要把房源、客户、带看、成交这整条业务线管起来。传统的做法是Excel加微信房源多了之后经常出现房源重复登记、客户跟进记录丢失、佣金算错这些乱七八糟的问题。这套系统就是要把这些线下流程搬到线上用ThinkPHP做后端API接口用Vue做前端页面实现房源管理、客户管理、带看记录、成交管理和数据统计这几个核心功能模块。适合有一定PHP和前端基础、想做一个完整前后端分离项目的开发者参考也适合房产行业的技术人员了解这类业务系统到底是怎么设计的。1. 项目整体设计与思路拆解1.1 房产销售业务的核心痛点在动手写代码之前我花了不少时间泡在房产门店里观察销售人员和店长的日常工作。发现他们的痛点非常集中房源信息不统一同一个房源可能被好几个业务员重复录入照片、价格、户型信息甚至对不上客户问起来销售自己都含糊。客户跟进没记录今天给哪个客户打了电话约了明天几点看房全靠业务员个人记忆人一离职客户资源跟着就带走了。带看过程无迹可循什么时候带客户去看的房看完之后客户反馈怎么样价格谈判到了哪一步管理层完全不清楚。成交数据统计滞后月底统计佣金、算业绩的时候只能靠人工翻记录又慢又容易出错。所以这套系统的第一条设计原则就是以房源为中心、以客户为主线把每一套房源的完整状态和每一个客户的跟进过程全部记录下来。第二条原则是权限分层业务员只能看自己的客户和房源店长可以看整个门店的老板可以看所有数据。这个权限设计在后端接口和前端路由两个层面都需要执行。1.2 系统模块规划与功能边界整个系统我划分了六大功能模块模块名称核心功能主要角色系统管理用户管理、角色权限、操作日志管理员房源管理房源录入、审核、上下架、条件检索业务员、店长客户管理客户登记、跟进记录、意向标签业务员带看管理预约带看、带看记录、反馈回写业务员、店长成交管理成交登记、合同信息、佣金计算店长、销售数据统计房源/客户/成交多维度统计店长、老板功能边界一定要划清楚这是我在做过多个管理类系统之后最深的体会。最开始我差点把财务功能也塞进去后来想明白了一个道理房产销售系统里涉及到钱的部分比如佣金结算、财务流水最好不要自己造轮子跟财务软件对接或者单独做模块会更稳妥。贸然扩展功能边界会让系统的复杂度和风险成倍上升后续维护也会非常痛苦。1.3 为什么选择ThinkPHP Vue这套组合选型的时候我在几个方案之间犹豫过。Spring Boot Vue功能强大但PHP团队维护起来有难度DjangoVue也不错但国内房产行业相关的PHP生态资料更多招人也更容易。最终选了ThinkPHP Vue理由很实在ThinkPHP 6.0 LTS版本稳定性好经过这么多年的迭代框架的核心代码已经非常成熟社区活跃度在国内数一数二遇到问题搜一下基本都能找到解决方案。项目用到的数据库操作、缓存、验证器这些功能ThinkPHP都内置了不需要引入太多第三方包。Vue的渐进式开发体验Vue的前端生态对中小型管理系统的适配度极高Element UI等组件库开箱即用表单、表格、弹窗这些后台管理系统的通用界面基本不用写太多重复代码。而且Vue的文档和社区资料是所有前端框架里最丰富的团队新成员上手非常快。前后端分离是必然趋势虽然ThinkPHP本身也能渲染模板但既然要做成管理后台前后端分离能更好地支撑以后可能的移动端、小程序端的接入。后端只管输出JSON前端专注交互接口定义好了两边并行开发效率比传统MVC高很多。热词里很多人都在搜“thinkphp v6.0.12lts”和“vue安装及环境配置”说明大家确实是想用这套组合做实际项目但第一步就被环境卡住了。后面我会把整个环境的搭建过程完整写一遍包括我踩过的那些细节坑。2. 开发环境准备与项目初始化2.1 本地环境搭建的版本选择这套系统的开发环境我在两台机器上分别配置过一台是Windows一台是Mac版本组合如下PHP8.0以上ThinkPHP 6对PHP 8的支持已经很完善可以用上JIT等新特性数据库MySQL 5.7或8.0推荐8.0JSON字段处理更强大Web服务器本地开发用PHP内置服务器正式环境用Nginx前端Node.js 16以上npm 8以上PHP依赖管理Composer 2.x具体安装过程这里不一步步截图了重点说几个坑。第一PHP8.0以后很多老的PHP扩展需要单独确认是否已启用比如fileinfo扩展是ThinkPHP的验证器依赖的默认没开的话会出现文件上传验证失败的诡异问题。第二Composer安装ThinkPHP的时候如果网络不稳定会超时建议先配置好Composer的中国镜像源。2.2 ThinkPHP 6后端项目的创建与配置用Composer创建项目是标准操作composer create-project topthink/think tp_house创建完成之后第一步是修改.env文件配置数据库连接APP_DEBUG true [APP] DEFAULT_TIMEZONE Asia/Shanghai [DATABASE] TYPE mysql HOSTNAME 127.0.0.1 DATABASE house_sale USERNAME root PASSWORD your_password HOSTPORT 3306 CHARSET utf8mb4 DEBUG true这里要强调一个细节ThinkPHP 6默认使用php think run启动开发服务器URL访问格式是http://localhost:8000。但前后端分离项目里前端Vue访问后端接口会涉及跨域问题。我建议开发阶段直接在后端配置跨域中间件后面会在接口章节详细讲。2.3 Vue项目的脚手架搭建前端我用的Vue 3.4版本搭配Vite构建工具。很多教程还在推Vue CLI但新项目强烈建议直接用Vite启动速度快一个量级热更新体验也好很多。npm create vitelatest house-admin -- --template vue cd house-admin npm install npm install vue-router4 pinia axios element-plus注意这里我直接一次性把项目会用到的依赖全装上了vue-router是前端路由pinia是状态管理库Vue 3官方推荐替代Vue 2时代的Vuexaxios负责HTTP请求element-plus是UI组件库。这套依赖组合是当前Vue 3项目最主流的技术栈搭配。装完之后用VS Code打开项目推荐安装Volar插件而不是老旧的VeturVolar对Vue 3的组合式API和TypeScript支持更好。热词里有人搜“vscode配置vue开发环境”重点就看两个设置一是文件关联确认.vue文件用Volar接管二是打开vue文件的自动格式化功能用Prettier统一风格不然团队协作时格式冲突会让人崩溃。3. 数据库设计与ThinkPHP后端实现3.1 核心数据表结构设计数据库设计是这类业务系统的地基我在这里花的精力最多。整个系统一共12张表核心的表有6张。首先是house房源表字段包括字段名类型说明idint主键titlevarchar(200)房源标题districtvarchar(50)所属区域addressvarchar(255)详细地址house_typevarchar(20)户型如3室2厅areadecimal(10,2)建筑面积total_pricedecimal(12,2)总价unit_pricedecimal(12,2)单价statustinyint状态1在售 2已定 3已售 4下架owner_idint业主IDis_audittinyint审核状态create_timeint录入时间这里有一个设计要点status和is_audit必须分开。之前有个同行把审核状态和出售状态混在一个字段里结果想筛选“已审核的在售房源”时发现SQL写得很别扭还容易出逻辑漏洞。另外总价和单价要同时冗余存储虽然单价可以由总价除以面积算出来但统计报表的时候每次都实时计算高并发下数据库压力很大冗余字段换查询性能在这个场景下是划算的。然后是customer客户表加了intention_level意向等级字段用1-5的整数表示在客户筛选排序时能派上大用场。follow_record跟进记录表是我特别设计的每个客户可以有多条跟进记录每条记录包含跟进方式电话、微信、到店、带看、跟进内容、下次跟进时间三个关键信息。这个设计让整个客户跟进过程有完整的时间线管理层随时能审查业务员的工作质量。带看记录表visit_record关联了房源、客户、业务员三方加上客户反馈和价格谈判情况。成交表deal_record则记录了最终的成交价、佣金比例、合同编号是后续统计的核心数据来源。3.2 创建数据表和基础模型表结构在MySQL命令行或Navicat里执行SQL建表即可这里分享一个ThinkPHP 6的模型创建技巧。数据表建好之后用命令行工具快速生成对应的模型类php think make:model House php think make:model Customer php think make:model VisitRecord生成的基础模型类文件在app\model目录下继承think\Model。我在模型层主要做了三件事第一定义表关联。比如House模型关联Owner业主信息、VisitRecord带看记录public function owner() { return $this-belongsTo(Owner::class, owner_id, id); } public function visits() { return $this-hasMany(VisitRecord::class, house_id, id); }第二定义字段类型自动转换。ThinkPHP 6支持在模型中直接指定字段类型格式化输出的时候省去很多麻烦protected $type [ area float, create_time timestamp:Y-m-d H:i, ];第三使用全局作用域做软删除和状态过滤。房源删除采用软删除机制调用destroy()方法并不会真正删除数据库记录而是在delete_time字段写入时间戳。这是为了防止业务员手滑删掉重要房源导致后续扯皮。3.3 认证机制与权限控制的实现思路登录认证用的JWT方案。ThinkPHP 6官方没有内置JWT我引入了firebase/php-jwt这个扩展包。整体认证流程是用户提交用户名密码后端验证通过后签发一个有效期为8小时的JWT令牌返回给前端前端把令牌存在localStorage里每次请求在Authorization请求头上带上后端在中间件里解析令牌获取用户信息。关键的后端验证代码是这样的public function parseToken(Request $request) { $header $request-header(Authorization); if (!$header || !preg_match(/Bearer\s(\S)/, $header, $matches)) { throw new HttpException(401, 未登录或登录已过期); } try { $decoded JWT::decode($matches[1], env(JWT_SECRET), array(HS256)); return $decoded; } catch (Exception $e) { throw new HttpException(401, Token无效或已过期); } }权限控制用了ThinkPHP的后置中间件机制自定义一个CheckAuth中间件在每个需要登录的控制器模块里注册。角色权限在数据库里做了role字段标识1是管理员2是店长3是业务员。注意权限控制不能只靠前端隐藏按钮接口层必须有校验否则懂点技术的人直接调接口就能绕过限制了。3.4 房源管理核心接口实现房源模块是系统的重头戏我实现了三个核心接口房源新增、房源列表查询带多条件筛选和房源状态变更。房源新增接口的核心逻辑public function save(Request $request) { $data $request-post(); validate(\app\validate\HouseValidate::class)-check($data); $house new House(); $house-title $data[title]; $house-district $data[district]; // 其他字段赋值 $house-status 1; $house-is_audit 0; $house-create_time time(); $house-save(); return json([code 0, msg 提交成功等待审核, data [id $house-id]]); }这里用了ThinkPHP的验证器严格校验必填字段和数据格式。这一步特别重要我见过太多系统因为后端没有做参数校验前端传了个负数面积进来后面的统计报表全是负数查问题查到崩溃。房源列表查询是多条件组合筛选的重点难点。用户可能按区域、价格区间、户型、面积、状态等多个条件组合筛选我一开始写的SQL拼接代码非常混乱后来重构封装成了查询构造器链式写法public function getList(Request $request) { $page $request-get(page, 1); $limit $request-get(limit, 10); $where []; if ($district $request-get(district)) { $where[] [district, , $district]; } if ($status $request-get(status)) { $where[] [status, , $status]; } if ($minPrice $request-get(min_price)) { $where[] [total_price, , $minPrice]; } if ($maxPrice $request-get(max_price)) { $where[] [total_price, , $maxPrice]; } $list House::with([owner]) -where($where) -order(create_time, desc) -paginate([$limit, page $page]); return json([code 0, data $list]); }ThinkPHP的paginate方法会自动处理分页参数返回的数据带上总条数和分页信息前端表格组件可以直接用。还有一个关键的SQL监听技巧热词里有人在搜“thinkphp 监听sql的代码一般添加在哪里”。如果SQL执行有问题可以在应用的app/middleware.php里注册一个全局中间件或者在AppService中监听事件。我用的办法最简单粗暴调试阶段在.env里设置DATABASE.DEBUG true然后安装tracy/tracy调试工具栏页面底部会展示执行的所有SQL语句定位慢查询和错误查询非常方便。正式环境记得关掉这个开关不然SQL语句全暴露在报错页面里有安全风险。3.5 前后端分离的跨域处理方案跨域是前后端分离项目开发阶段的第一个拦路虎。Vue项目跑在http://localhost:5173ThinkPHP接口跑在http://localhost:8000端口不同就产生了跨域。有两种解决方案。开发阶段的推荐方案是配置Vite的DevServer代理在vite.config.js里设置export default defineConfig({ server: { proxy: { /api: { target: http://localhost:8000, changeOrigin: true } } } })这样前端代码里请求/api/house/listVite开发服务器会自动转发到http://localhost:8000/api/house/list浏览器的请求源就没变跨域问题在源头就化解了。这个方案的好处是不用在后端处理CORS生产环境Nginx也可以用同样的思路处理。如果确实需要后端开启跨域可以在ThinkPHP中间件中设置响应头public function handle($request, \Closure $next) { $response $next($request); $response-header([ Access-Control-Allow-Origin *, Access-Control-Allow-Methods GET, POST, PUT, DELETE, OPTIONS, Access-Control-Allow-Headers Authorization, Content-Type, ]); return $response; }生产环境我建议用Nginx来处理跨域后端保持干净后面部署章节会讲到。4. Vue前端核心实现4.1 前端技术方案与工程结构前端项目我使用了Vue 3的组合式APIComposition API开发。热词里很多人问“vue选项式和组合式区别”我用实际项目经验来解释选项式把逻辑分散在data、methods、computed这些固定字段里组件小的时候很清晰但业务逻辑一复杂一个功能的代码会被拆散到好几个选项里维护起来很痛苦组合式API用setup语法可以把同一个业务逻辑的所有代码封装在computed、watch、methods等函数的组合里按功能域组织逻辑代码复用性也更好。做房产销售这种业务逻辑比较重的系统我强烈建议直接用组合式API。前端项目的目录结构src/ ├── api/ # 接口请求封装 ├── assets/ # 静态资源 ├── components/ # 公共组件 ├── router/ # 路由配置 ├── stores/ # Pinia状态管理 ├── views/ # 页面组件 ├── utils/ # 工具函数 ├── App.vue └── main.js4.2 Axios封装与Token处理axios封装是整个前端代码里复用率最高的部分。热词里有“vue前后端分离请求token处理”、“vue axios devserver转发”这两个问题我都遇到过这里给出完整方案。在utils/request.js中统一封装import axios from axios import { ElMessage } from element-plus import router from ../router const request axios.create({ baseURL: /api, timeout: 10000 }) // 请求拦截器注入Token request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config }) // 响应拦截器统一处理错误 request.interceptors.response.use( response { const res response.data if (res.code ! 0) { ElMessage.error(res.msg || 请求失败) return Promise.reject(new Error(res.msg)) } return res }, error { if (error.response?.status 401) { ElMessage.error(登录已过期请重新登录) localStorage.removeItem(token) router.push(/login) } else { ElMessage.error(error.message || 网络异常) } return Promise.reject(error) } ) export default request这套封装的核心价值在于第一Token的注入不需要每个页面手动写第二响应拦截器已经处理了后端统一的{code, msg, data}格式页面里拿数据直接res.data就行不用重复做错误处理第三所有地方都能拿到统一的401拦截逻辑登录过期自动跳转到登录页。Token失效的处理是我在实际运营中最常被问到的。有用户问“为什么我登录一会儿就退出了”排查发现是Token有效期设置太短。JWT的有效期一般8个小时比较合理跟实际业务场景匹配业务员早上登录下班前应该还在有效期内。但要注意刷新页面的场景Token存localStorage虽然简单但刷新页面不会丢这点比内存存储好所以最终选择存localStorage安全性方面用HTTPS部署来兜底。4.3 前端路由设计与权限控制Vue Router一共设计了四个层级的路由登录页独立出来主布局下面是首页看板、房源管理、客户管理、带看管理、成交管理、统计报表、系统设置。路由配置核心部分const routes [ { path: /login, component: () import(../views/Login.vue) }, { path: /, component: () import(../layouts/MainLayout.vue), redirect: /dashboard, children: [ { path: dashboard, component: () import(../views/Dashboard.vue), meta: { title: 首页看板 } }, { path: houses, component: () import(../views/house/HouseList.vue), meta: { title: 房源管理, roles: [admin, manager] } }, { path: customers, component: () import(../views/customer/CustomerList.vue), meta: { title: 客户管理 } }, // ... 其他路由 ] } ]这里用到了路由懒加载() import()形式首屏加载速度会快很多算是一个比较基础但非常有效的性能优化措施。前端权限控制通过路由守卫实现router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (!token to.path ! /login) { next(/login) return } if (token to.path /login) { next(/) return } // 这里还可以根据用户角色做路由过滤 next() })路由守卫有两个作用没登录的用户不管访问什么页面都会被弹回登录页同时配合后端接口的权限校验实现双重保障。4.4 房源管理页面与认证交互实现用户登录认证页面是前端的一个重点模块。登录页的核心代码template div classlogin-page el-form refloginFormRef :modelloginForm :rulesloginRules el-form-item propusername el-input v-modelloginForm.username placeholder用户名 / /el-form-item el-form-item proppassword el-input v-modelloginForm.password typepassword placeholder密码 / /el-form-item el-button typeprimary :loadingloading clickhandleLogin登录/el-button /el-form /div /template script setup import { reactive, ref } from vue import { useRouter } from vue-router import { ElMessage } from element-plus import { loginApi } from ../api/user import { useUserStore } from ../stores/user const router useRouter() const userStore useUserStore() const loginForm reactive({ username: , password: }) const loading ref(false) const handleLogin async () { loading.value true try { const res await loginApi(loginForm) localStorage.setItem(token, res.data.token) localStorage.setItem(userInfo, JSON.stringify(res.data.user)) userStore.setUser(res.data.user) ElMessage.success(登录成功) router.push(/) } finally { loading.value false } } /script这页代码看起来不难但有几个细节是踩过坑之后才总结出来的。登录按钮必须加loading状态防止用户重复点击导致重复提交登录成功之后不要忘了把用户角色信息也存下来后面路由守卫和菜单栏权限显示都要用错误提示用ElMessage统一弹出比浏览器默认alert美观很多也不会阻塞界面。房源列表页的前端实现主要用Element Plus的表格组件加载后端数据el-table :datahouseList el-table-column proptitle label房源标题 min-width200 / el-table-column propdistrict label区域 width100 / el-table-column prophouse_type label户型 width100 / el-table-column proparea label面积(㎡) width100 / el-table-column proptotal_price label总价(万) width100 / el-table-column label状态 width100 template #default{ row } el-tag :typestatusMap[row.status].type{{ statusMap[row.status].label }}/el-tag /template /el-table-column el-table-column label操作 width200 fixedright template #default{ row } el-button link typeprimary clickopenDetail(row)详情/el-button el-button link typewarning clickeditHouse(row)编辑/el-button /template /el-table-column /el-table筛选条件部分用了el-select和el-input组件点击“搜索”按钮后重新请求接口点击“重置”清空条件恢复默认列表。这里要注意前端把筛选参数绑定为响应式数据并且深拷贝一份用于重置避免重置时因为引用关系导致数据没清干净的bug。4.5 数据统计看板的ECharts集成老板和店长最关心的数据可视化我用ECharts实现。热词里有人在搜“vue element-plus 实现 echarts 箱线图boxplot 多个箱子”那类高级图表这里用不上但ECharts和Vue3的集成方法是一样的。关键是安装依赖后在组件里正确初始化图表实例script setup import * as echarts from echarts import { ref, onMounted, onBeforeUnmount } from vue import { dealStatsApi } from ../api/stats const chartRef ref(null) let chart null onMounted(async () { const res await dealStatsApi() chart echarts.init(chartRef.value) chart.setOption({ title: { text: 本月成交趋势 }, tooltip: { trigger: axis }, xAxis: { data: res.data.dates }, yAxis: {}, series: [{ type: line, data: res.data.counts, smooth: true, areaStyle: {} }] }) }) onBeforeUnmount(() { chart.dispose() }) /script有两个坑需要提一下ECharts实例在组件卸载前一定要调用dispose()销毁否则页面切换之后会报“There is a chart instance already initialized on the dom”的警告严重的会导致内存泄露图表容器必须有明确高度否则初始化之后是一片空白。这两个问题我调试后总结成了团队内部文档新同事照着做就不会踩同样的坑。5. 核心业务模块的完整实现流程5.1 房源发布与审核流转业务员提交房源时表单校验通过后房源状态为“待审核”。店长登录后进入“房源审核”菜单能看到所有待审核的房源点击通过或者驳回。驳回时店长必须填写驳回理由这个理由会通过列表里的提示组件显示给业务员看。审核状态机的流转逻辑我画在代码里用状态常量加条件判断实现const STATUS_PENDING 0; // 待审核 const STATUS_APPROVED 1; // 已通过 const STATUS_REJECTED 2; // 已驳回 if ($house-status ! 0) { throw new HttpException(400, 该房源已处理不能重复审核); }这个校验逻辑非常重要。如果后端不做这个状态判断就可能出现运营人员在页面同时打开多个窗口对同一套房源点了两次审核通过数据库里状态变成乱套的情况。加了这个判断不管前端怎么点后端都只接受第一次操作。5.2 客户跟进与带看流程闭环客户管理模块的亮点在跟进记录的设计。业务员每联系一次客户都要在跟进模块里新增一条记录包含联系方式和沟通内容。系统会自动记录操作人和时间并支持设置下次跟进时间。当天没有完成跟进记录的客户会出现在首页看板的“待跟进提醒”里。我带团队做需求分析时发现业务员普遍反感强制填写跟进记录觉得是“浪费时间”。后来在系统里加了一个功能把跟进记录和统计排行关联起来跟进次数和成交率挂钩店长开会的时候能直观看到每个人的工作量。有了数据驱动业务员才真正愿意用这个系统。带看记录的流程是业务员先选择客户、选择待看房源、预约带看时间确认带看后填写看房反馈包括客户满意程度、价格意向、竞品对比信息。反馈信息推送到该房源所属的店长工作台辅助店长判断是否需要与业主沟通议价。5.3 成交管理与佣金计算成交登记是整个交易链路的终点。核心控制点有两个房源和客户的状态变更以及佣金计算。成交登记时首先要校验房源状态是“在售”然后把房源状态改为“已售”把客户状态改为“已成交”。这两个操作必须在一个数据库事务里执行避免出现房源已售但客户还是跟进中的数据不一致。ThinkPHP的数据库事务写法Db::transaction(function () use ($data) { $house House::find($data[house_id]); if ($house-status ! 1) { throw new \Exception(房源状态异常无法成交); } $house-status 3; $house-save(); $deal new DealRecord(); $deal-house_id $data[house_id]; $deal-customer_id $data[customer_id]; $deal-deal_price $data[deal_price]; $deal-commission_rate $data[commission_rate]; $deal-commission_amount round($data[deal_price] * $data[commission_rate] / 100, 2); $deal-save(); });佣金计算这里有个细节佣金比例是按百分比存的比如1.5表示1.5%计算佣金时要除以100最后保留两位小数。如果佣金比例是跟房价阶梯挂钩的还需要在系统里配置多级费率表。我第一次实现时忽略了这个点直接写死一个比例运营人员后来提出不同等级的房源佣金不一样需求变更起来就是噩梦。所以业务系统里凡是关联“规则”的字段尽量做成可配置而不是写死在代码里。5.4 统计报表模块统计报表包含三个维度房源维度各区域房源数、价格分布、客户维度客户来源、意向等级分布、成交维度月度成交趋势、佣金排名。报表接口统一返回原始统计数据前端ECharts图表展示。举一个SQL统计的例子按区域统计在售房源数量$list House::field(district, count(id) as total) -where(status, 1) -group(district) -select();这种聚合查询在数据量大时必须建好索引我在这张表的district、status字段上都加了普通索引。数据量到几十万条的时候有没有索引查询速度差几十倍。6. 部署上线与常见问题排查6.1 前后端打包部署方案开发完成之后要上线这里分享一下生产环境的部署经验。前端打包npm run build构建完成后会生成dist目录里面是纯静态文件。我一般用Nginx直接托管这些静态文件然后配置反向代理把/api开头的请求转发到后端的PHP服务。Nginx的关键配置如下server { listen 80; server_name house.example.com; # 前端静态文件 root /var/www/house/dist; index index.html; # 处理Vue Router的history模式刷新404问题 location / { try_files $uri $uri/ /index.html; } # API反向代理到ThinkPHP location /api/ { proxy_pass http://127.0.0.1:8000/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }热词里有人在搜“nginx部署前端vue项目”和“vue项目启动后network不可用”第一个问题参考上面配置就能解决第二个问题通常是开发环境监听地址的问题Vite默认只监听localhost要局域网访问得在server.host配置0.0.0.0。后端部署的坑更多一些。ThinkPHP在Nginx环境下的伪静态配置必须正确否则访问二级路由会出现404。Nginx的配置location / { if (!-e $request_filename) { rewrite ^(.*)$ /index.php?s$1 last; } }另外PostgreSQL和MySQL的大小写敏感规则不一样如果迁移过数据库要特别小心。PHP环境里proc_open、shell_exec这类函数如果没特殊需求建议在php.ini的disable_functions里禁掉防止恶意命令执行。6.2 开发中高频问题的排查方法做一个实战类的管理系统总会遇到不少坑。我把自己开发过程中遇到的高频问题整理成一张速查表问题现象可能原因排查方向前端登录发请求Network显示红字后端跨域未允许检查CORS配置或Vite代理列表页刷新出现404Vue Router history模式没配置Nginx里加try_files表单提交显示401Token过期或未注入检查请求头重新登录中文数据在页面乱码数据库字符集不是utf8mb4修改数据表和连接的CHARSET文件上传提示“非法文件”PHPfileinfo扩展没开启在php.ini里启用扩展接口报500但日志没有记录PHP错误被隐藏打开APP_DEBUG看实时错误前端改了代码页面不变热更新失效或浏览器缓存重启DevServer强刷浏览器其中“Token过期后页面异常”是我在系统上线之后遇到的最大问题。用户挂在系统里不动8小时Token到期后用户点任何按钮都会收到401错误但前端响应拦截器里的逻辑是弹提示并跳登录页。一开始跳转的效果不稳定有时候登录页弹出来是白屏。排查发现是路由守卫和资源加载的时序问题401触发跳转时如果对应的路由组件在懒加载中尚未返回页面会短暂空白。解决方案是在跳转前先清空整个Vue黑名单缓存强制重新挂载根组件问题就消失了。6.3 系统的性能优化与后续演进系统跑了大半年几千套房源和上万条跟进记录整体响应时间还能控制在200ms以内主要得益于几点第一核心数据表的查询索引建得比较全第二后端做了查询缓存热门的筛选条件结果缓存60秒第三前端表格全部分页不做一次性全量加载第四静态资源和图片走CDN加速减轻服务器压力。后续演进方向有很多小程序端是刚需客户在外边看房的时候随时能用手机查房源房产的图片和VR看房可以接入对象的存储服务和播放器智能推荐方面根据客户带看历史推荐相似房源。热词里有人搜“vue播放m3u8”说明不少同行都在折腾房源视频和VR展示这块也是房产系统的标配功能。7. 结合开发经验的一点想法做这套系统最大的收获是明白了一个道理技术框架选型永远不是项目成功的决定因素对业务场景的理解才是最花钱的部分。ThinkPHP和Vue都是非常成熟的技术网上教程一抓一大把但真正让系统值钱的是你能不能把房源状态、客户跟进、成交佣金这套业务逻辑设计得清晰可靠能不能用代码把线下手工流程的漏洞堵住。热词里有人搜“thinkphp 3.2 版本兼容php8”这个我特别想多说一句。3.2是非常老的版本了应该是2014年左右的东西如果还在维护老项目建议尽早规划迁移。PHP 8带来的性能提升和语法改进是革命性的继续停留在老版本生态里安全漏洞和依赖兼容问题会越来越难处理。我当时就是从ThinkPHP 5.1平滑迁移到6.0的主要工作是数据库查询构造器的写法调整和验证器重构前后花了大概一周时间。最后再分享一个实用的小技巧开发这类前后端分离的管理系统接口设计要尽量语义化POST /api/house表示新增房源GET /api/house?id1表示获取房源详情PUT /api/house?id1表示更新房源DELETE /api/house?id1表示删除房源。这种RESTful风格虽然看似简单但坚持用下来前后端联调的时候可以省掉大量沟通成本。我的经验就是后端定义好接口契约文档前端按契约开发整个项目的协作效率能提升50%以上。如果你也准备做一个类似的房产销售管理系统先别急着写代码花几天时间把业务梳理清楚跟真实用户聊聊他们的工作流程画清楚状态机流转图再动手搭建工程。这套思路无论用什么技术栈都值得先走一遍。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价