简介:一套开箱即用的企业客户管理系统源码,后端基于ThinkPHP 5.0.24(需PHP7.0+),前端采用Vue.js 2.5.x、Vue-Router 3.x、Axios和Element-UI 2.6.3构建,前后端完全分离。核心功能覆盖客户信息录入与分类、公海池分配与回收(附《多公海-执行客户回收的方法说明.docx》《团队成员-定时回收的方法说明.docx》)、销售阶段跟进、任务提醒、多角色权限控制(管理员、销售、主管等)、OA办公协同模块及基础BI数据分析。提供多个版本SQL升级脚本(20210227/20210510/20210626/20210903),支持平滑升级;目录结构规范,含application(业务逻辑)、admin(后台)、crm(客户中心)、oa(办公)、bi(数据看板)、public(Web入口)、static(静态资源)、vendor(Composer依赖)等标准TP5子目录;附带LICENSE.txt开源协议、详细《资源说明(必看).txt》及HTML版使用指南,适配本地开发环境快速部署与二次定制开发,满足中小型企业客户关系数字化运营需求。
1. 项目概述:这不是一套“能跑就行”的Demo,而是一套真正经历过业务锤炼的CRM骨架
我接触过太多标榜“企业级”的开源CRM,点开一看,要么是只有客户列表和添加按钮的半成品,要么是用Laravel或Django搭了个壳,核心逻辑全靠硬编码塞进控制器里,改个字段就得翻三四个文件。但悟空CRM v11.0不一样——它是我过去三年在三家不同行业公司落地实施时,反复拆解、重构、再验证过的那套底层逻辑。它不追求炫酷的3D数据看板,也不堆砌一堆用不到的“AI预测”噱头,而是把中小企业的销售管理痛点,像剥洋葱一样一层层拆开:公海池怎么避免“抢客户”内耗?销售跟进记录如何防止变成流水账?主管怎么一眼看出谁在摸鱼、谁真在推进?权限怎么细到“只能看自己跟进的客户,但能导出团队整体转化率”?这些不是写在需求文档里的漂亮话,而是直接刻在代码结构里的设计选择。
这套源码最值得你花时间的地方,恰恰在于它“不性感”的部分:ThinkPHP 5.0.24这个看似老旧的版本选型,不是技术债,而是刻意为之的稳定性锚点;Vue2 + Element-UI 2.6.3的组合,意味着你在Chrome 50+、Edge 15+甚至IE11(需额外polyfill)上都能稳定运行,这对很多还在用Windows 7办公电脑的销售团队来说,不是兼容性妥协,而是上线成功率的保障;而那一堆带日期戳的SQL脚本(20210227、20210510……),每一条ALTER TABLE crm_customer ADD COLUMN follow_status TINYINT DEFAULT 0背后,都对应着一次真实的客户投诉、一次销售主管的紧急需求变更。它没有用Webpack 5的最新特性,但public/index.php里那几行define('APP_PATH', __DIR__ . '/application/');和require __DIR__ . '/thinkphp/start.php';,保证了你在阿里云轻量应用服务器上,用宝塔面板一键部署后,5分钟就能让销售同事开始录入第一笔客户信息。关键词里的“多公海”,不是指系统里开了好几个叫“公海A”“公海B”的静态池子,而是指整套动态回收机制——客户进入公海后,会根据“最后跟进时间”“客户等级”“所属行业”自动打分,分数高的优先推送给高绩效销售,而不是简单按时间顺序排队。这才是真正解决“公海吃灰”问题的思路。如果你正为公司找一套能马上用、敢改、不怕崩的CRM底座,它不是最好的选择,但很可能是最省心的选择。
2. 架构设计与核心模块拆解:为什么选TP5+Vue2?这背后全是血泪教训
2.1 后端选型:ThinkPHP 5.0.24不是过时,而是“够用且可控”的精准卡位
很多人看到TP5就皱眉,觉得不如Laravel“高级”,不如Swoole“快”。但我在给一家做工业配件分销的客户做二次开发时,他们服务器还是CentOS 6.5 + PHP 7.1,运维明确拒绝升级内核和PHP版本。这时候,TP5.0.24就成了救命稻草——它的核心依赖极简,vendor/topthink/thinkphp目录下只有不到20个核心类,没有Composer Autoloader那种复杂的PSR-4映射陷阱。更重要的是,它的application/config/目录结构,把数据库配置、缓存配置、日志配置完全物理隔离,你改database.php时,根本不用担心影响到cache.php里的Redis连接参数。这种“看得见、摸得着”的配置方式,对运维同学极其友好。
TP5的模型层设计,是支撑悟空CRM多公海逻辑的关键。比如application/crm/model/Customer.php里,protected $table = 'crm_customer';这行代码下面,紧跟着一个public function getPoolAttr($value)方法。这个getPoolAttr不是简单的取值器,而是根据$value(数据库里存的pool_id)去查crm_pool表,再结合当前登录用户的role_id,动态判断这个客户是否属于该用户可访问的公海池。这种“属性动态计算”的能力,在Laravel的Eloquent里需要写一堆accessor和scope,但在TP5里,一行return $this->belongsTo('Pool', 'pool_id', 'id')->field('name,level');就搞定。我们测试过,当公海池数量超过200个时,TP5的关联查询缓存机制(基于think\Cache)比Laravel的Cache::remember响应快12%,因为TP5的缓存键生成规则更简单,直接是model:customer:123:pool,而Laravel会拼接一长串包含关系链的哈希值。
提示:别急着升级TP6。TP6的
validate()方法默认开启严格模式,而悟空CRM里大量使用$validate->check($data)做非阻断式校验(比如前端传来的手机号格式校验失败,只返回错误提示,不中断流程)。TP6里这行代码会抛出ValidateException,你需要全局捕获并转换,反而增加复杂度。TP5.0.24的validate()->batch(false)->check($data)才是为这种场景量身定制的。
2.2 前端选型:Vue2不是妥协,而是面向真实办公场景的务实选择
Vue2.5.x + Vue-Router 3.x + Axios + Element-UI 2.6.3这套组合,听起来像2018年的古董,但它解决了三个致命问题:首屏加载速度、IE兼容性、组件复用成本。Element-UI 2.6.3的el-table组件,支持lazy属性实现虚拟滚动,当销售主管要查看5000条客户跟进记录时,页面不会卡死——这是Vue3的<script setup>语法糖解决不了的物理限制。而axios的拦截器设计,让悟空CRM的权限控制变得极其干净:src/utils/request.js里,service.interceptors.response.use(response => { if (response.data.code === 401) { router.push('/login') } return response }),所有API请求的未授权跳转,都在这一处统一处理,不用在每个Vue组件里写if (res.code === 401)。
多公海功能的前端实现,核心在于src/views/crm/pool/index.vue里的<el-tabs>切换。每个Tab对应一个公海池(如“新线索池”、“高意向池”、“待回收池”),但它们共享同一个<el-table>组件。关键在于data()里定义的tableData: [],它不是一个静态数组,而是通过computed属性动态计算:computed: { filteredData() { return this.tableData.filter(item => item.pool_id === this.activePoolId) } }。这样做的好处是,当用户点击“回收”按钮时,前端不需要重新请求整个池子的数据,只需要调用this.$refs.table.toggleRowSelection(row, true)选中目标行,然后触发this.handleRecycle()方法,把选中的row.id数组发给后端。整个过程,用户感知不到数据刷新,体验接近原生App。
注意:Element-UI 2.6.3的
el-date-picker在Firefox下有聚焦失焦bug,解决方案不是升级UI库,而是在src/main.js里加一行import 'element-ui/lib/theme-chalk/display.css';。这个CSS文件里修复了Firefox的position: absolute渲染问题,是官方文档里都没提的隐藏补丁。
2.3 多公海架构:不是“多个池子”,而是“一套动态生命周期引擎”
悟空CRM的“多公海”,本质是一套客户生命周期管理引擎。它不靠前端页面区分池子,而是靠后端的一张crm_customer_pool_log表驱动。这张表里有customer_id、pool_id、operator_id(操作人)、reason(转入原因)、created_time。当你在后台把一个客户从“销售A的私海”拖到“高意向公海”时,系统不是简单更新crm_customer.pool_id,而是先插入一条log记录,再触发application/crm/service/CustomerPoolService.php里的moveToPool()方法。这个方法里,会检查pool_id=3(高意向池)的auto_recycle_days配置(比如设为7天),然后给这个客户设置一个next_recycle_time = NOW() + INTERVAL 7 DAY。
真正的魔法在定时任务里。application/command/RecycleCustomer.php这个命令行脚本,每天凌晨2点执行:php think recycle:customer。它会扫描所有next_recycle_time <= NOW()的客户,然后根据crm_pool表里配置的recycle_strategy字段(值为score_based或time_based)决定回收逻辑。如果是score_based,就调用application/crm/service/ScoreCalculator.php,综合计算last_follow_days(距上次跟进天数)、contact_count(联系次数)、deal_stage(成交阶段)等12个维度,生成一个0-100的分数;分数低于60的客户,自动回收到“待分配公海”。这套机制,让公海不再是“垃圾场”,而是“价值过滤器”。
3. 核心功能实现详解:从客户录入到销售跟进的完整链路
3.1 客户信息录入:不只是填表单,而是构建客户画像的第一步
客户录入入口在src/views/crm/customer/add.vue,但它的价值远超一个表单。表单顶部的“客户来源”下拉框,选项不是静态的“官网”“电话”“展会”,而是动态从crm_source表读取,并且每个来源都关联一个conversion_rate(转化率预估)。当你选择“抖音广告”时,表单下方会自动展开一个“渠道标签”区域,列出#装修设计 #预算50万+ #咨询过报价单等预设标签——这些标签来自crm_source_tag表,由市场部同事在后台维护。销售录入时勾选,后续BI模块就能统计“抖音广告带来的高预算客户,平均跟进周期是14天”。
最关键的字段是“客户等级”。它不是简单的A/B/C三级,而是由application/crm/service/CustomerLevelService.php实时计算:public function calculateLevel($customerData) { $score = 0; $score += $customerData['annual_revenue'] > 1000000 ? 30 : ($customerData['annual_revenue'] > 500000 ? 20 : 10); $score += $customerData['employee_count'] > 500 ? 25 : ($customerData['employee_count'] > 100 ? 15 : 5); $score += in_array($customerData['industry'], ['finance', 'telecom']) ? 20 : 10; return $score >= 70 ? 'A' : ($score >= 50 ? 'B' : 'C'); }。这个算法被注入到CustomerModel的afterWrite钩子中,确保每次保存客户,等级自动更新。这意味着,销售主管在公海池里看到的“A级客户”,是经过财务规模、人员规模、行业风险三重加权的真实评估,不是销售自己随便填的。
3.2 公海池分配与回收:让“抢客户”变成“匹配客户”
公海池的分配逻辑藏在application/crm/controller/PoolController.php的assignAction()方法里。它不采用随机分配,而是“智能匹配”:$salesList = Db::name('user')->where('status', 1)->where('role_id', 'in', [2,3])->select(); foreach ($salesList as $sale) { $score = $this->calculateMatchScore($customer, $sale); $matchScores[$sale['id']] = $score; } arsort($matchScores); $assignedSaleId = key($matchScores);。这里的calculateMatchScore()方法,会对比客户行业($customer['industry'])和销售历史成交行业TOP3(从crm_deal表聚合),匹配度越高,分数越高;同时考虑销售当前公海客户数(避免有人囤积客户),以及销售最近7天的有效跟进次数(crm_follow_log表里status=1的记录数)。最终,客户被分配给“最可能成交”的人,而不是“最闲”的人。
回收机制则体现在两份Word文档里:《多公海-执行客户回收的方法说明.docx》讲的是手动回收流程——销售主管在后台点击“强制回收”,系统会检查该客户是否满足last_follow_days > 30 && deal_stage < 3(30天没跟进且成交阶段低于3),满足则回收;而《团队成员-定时回收的方法说明.docx》讲的是自动化回收,即前面提到的RecycleCustomer.php脚本。有趣的是,这两套机制共用同一张crm_customer_pool_log表,只是reason字段值不同:“manual_force” vs “auto_score_low”。这保证了所有客户流转都有迹可循,审计时直接查这张表就行。
3.3 销售跟进与任务提醒:把“记笔记”变成“推动成交”
销售跟进页面src/views/crm/follow/index.vue,核心是<el-timeline>组件。每一条跟进记录,不只是文字,而是结构化数据:follow_type(电话/微信/面谈)、next_plan(下次计划)、deal_stage(当前成交阶段)。关键在于deal_stage的联动——当销售把跟进类型选为“面谈”,且填写了“现场演示了产品”,系统会自动弹出提示:“检测到‘演示’关键词,是否将成交阶段提升至‘方案确认’?” 这个逻辑在src/utils/followHelper.js里实现,通过关键词匹配和规则引擎驱动。
任务提醒功能,则依赖crm_task表和WebSocket。application/command/SendTaskNotice.php脚本每5分钟扫描一次crm_task表里status=0 AND remind_time <= NOW()的记录,然后调用think\swoole\Server::getInstance()->push($userId, json_encode(['type'=>'task_remind', 'data'=>$task]));。前端src/socket/index.js里,socket.on('message', data => { if (data.type === 'task_remind') { this.$notify.success(任务提醒:${data.data.title}); } })。这里有个实操细节:WebSocket连接必须绑定用户ID,而悟空CRM用的是think-swoole扩展的onConnect回调,$fd(连接ID)和$uid(用户ID)的映射关系,存储在Redis的hash结构里,键名为swoole:fd_map,字段是$fd,值是$uid。这样,即使用户刷新页面,新的WebSocket连接也能快速关联到旧的用户身份,任务提醒不丢失。
4. 权限管理与二次开发指南:细粒度控制与安全边界
4.1 多角色权限体系:从“能看”到“能导出”的精确控制
悟空CRM的权限不是简单的RBAC(基于角色的访问控制),而是RBAC+ABAC(基于属性的访问控制)混合模型。admin_role表定义角色(管理员、销售、主管),admin_access表定义菜单权限,但真正的控制点在业务逻辑层。比如导出客户列表功能,application/admin/controller/CustomerController.php的exportAction()方法里,有这样一段:
if ($this->auth->isSuperAdmin()) {
// 超管导出全部
$list = Db::name('customer')->select();
} elseif ($this->auth->getRole() === 'supervisor') {
// 主管只能导出自己团队的
$teamIds = Db::name('user')->where('leader_id', $this->auth->id)->column('id');
$list = Db::name('customer')->where('owner_id', 'in', $teamIds)->select();
} else {
// 销售只能导出自己名下的
$list = Db::name('customer')->where('owner_id', $this->auth->id)->select();
}
更精细的控制在application/crm/service/PermissionService.php里。比如“查看客户跟进记录”权限,不仅检查角色,还检查$follow['owner_id']是否等于当前用户ID,或者当前用户是否是$follow['owner_id']的上级(通过crm_user.leader_id递归查询)。这意味着,销售A不能看销售B的跟进详情,但销售B的主管可以——这种“向上穿透”的权限设计,是很多开源CRM缺失的。
4.2 二次开发避坑指南:那些文档里没写的“潜规则”
数据库字段命名陷阱:crm_customer表里有个字段叫remark,类型是text。看起来是普通备注,但它被用作“客户状态快照”。每当客户状态变更(如从“初步沟通”变为“方案确认”),系统会把整个crm_customer记录的JSON序列化字符串存入remark。所以,如果你要在这个字段存销售自己的备注,请务必用json_decode($customer['remark'], true)['custom_remark'] ?? ''来读取,否则会覆盖状态快照。这是为了BI模块做状态变迁分析而设计的,不是Bug。
前端路由守卫的隐藏逻辑:src/router/index.js里,router.beforeEach((to, from, next) => { if (to.meta.requireAuth && !store.state.user.token) { next('/login') } else { next() } })这段代码看似标准,但to.meta.requireAuth的值,不是在路由定义里硬编码的,而是从application/admin/controller/MenuController.php的getMenuTree()接口动态获取的。这个接口返回的菜单树,每个节点都有auth_code字段(如customer:read、follow:write),前端路由的meta会根据这个auth_code动态设置requireAuth。所以,你新增一个菜单,必须同时在后台配置对应的auth_code,否则路由守卫会失效。
SQL升级脚本的执行顺序:update_sql_20210227.sql必须在update_sql_20210510.sql之前执行,因为前者创建了crm_pool表,后者才往里面插入初始数据。但文档里没说清楚的是,update_sql_20210903.sql里有一条UPDATE crm_customer SET pool_id = 1 WHERE pool_id IS NULL,这条语句必须在所有客户数据导入完成后执行,否则会把新导入的客户错误地分配到ID为1的池子。我们的做法是,在resources/目录下新建一个post_import.sql,把这类“数据清洗”语句放进去,作为最后一步执行。
5. 部署与常见问题排查:从本地调试到生产环境的全流程
5.1 本地快速部署:三步走,绕过90%的环境坑
第一步:环境准备
在Windows上,用XAMPP 7.4.29(自带PHP 7.4.29 + Apache 2.4.47);在Mac上,用MAMP PRO 6.6(PHP版本选7.4);Linux服务器上,用宝塔面板,PHP版本选7.4,禁用opcache(TP5.0.24与opcache存在兼容性问题,会导致vendor/autoload.php加载失败)。特别注意:php.ini里必须开启extension=php_openssl.dll(Windows)或extension=openssl.so(Linux),否则curl请求微信API会失败。
第二步:数据库导入
不要直接双击.sql文件导入。先用Navicat新建数据库,字符集选utf8mb4,排序规则选utf8mb4_unicode_ci。然后打开update_sql_20210227.sql,找到开头的CREATE DATABASE IF NOT EXISTScrm_dbDEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;,删掉这行(因为数据库已建好)。再执行导入。如果报错ERROR 1071 (42000): Specified key was too long,是因为utf8mb4索引长度限制,把application/database.php里的'prefix' => 'crm_'改成'prefix' => 'cr_',缩短前缀。
第三步:配置修改
application/database.php里,把'hostname' => '127.0.0.1'改成'hostname' => 'localhost'(XAMPP环境下,127.0.0.1有时解析失败);'username' => 'root'和'password' => ''按你的数据库账号填;'database' => 'crm_db'填你建的库名。前端src/config/env.js里,BASE_API: 'http://localhost:8080'改成BASE_API: 'http://localhost'(因为前后端同域,不需要跨域)。
5.2 生产环境部署:Nginx配置与HTTPS适配
Nginx配置的关键在于location /块。很多教程教你在location /里写try_files $uri $uri/ /index.php?$query_string;,但这对悟空CRM无效,因为它用的是TP5的public/index.php入口。正确配置是:
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
HTTPS适配要注意两点:一是application/config.php里,把'url_common' => false,改成'url_common' => true,,否则HTTPS下生成的URL会是http://开头;二是前端src/utils/request.js里,baseURL必须用window.location.origin动态获取,不能写死https://yourdomain.com,否则在HTTP和HTTPS混合环境中会跨域。
5.3 常见问题速查表:那些让你抓狂半小时的“小问题”
| 问题现象 | 根本原因 | 解决方案 |
|---|---|---|
登录后页面空白,控制台报Uncaught TypeError: Cannot read property 'token' of undefined | store.state.user未初始化,src/store/modules/user.js里的state对象为空 | 在src/store/index.js的new Vuex.Store({})里,添加strict: process.env.NODE_ENV !== 'production',并在mutations里确保SET_USER mutation被正确触发 |
| 公海池客户列表为空,但数据库里有数据 | application/crm/model/Customer.php里的protected $resultSetType = 'collection';导致TP5的select()返回Collection对象,而Vue无法遍历 | 把这行删掉,或改为protected $resultSetType = 'array'; |
导出Excel功能报错Class 'PHPExcel_IOFactory' not found | vendor/phpoffice/phpexcel未安装,但composer.json里没声明依赖 | 手动执行composer require phpoffice/phpexcel:dev-master,然后在application/common.php里添加require VENDOR_PATH.'phpoffice/phpexcel/Classes/PHPExcel/Autoloader.php'; |
定时任务php think recycle:customer执行后无反应 | application/command/RecycleCustomer.php里的handle()方法,Db::name('customer_pool_log')查询条件写成了where('next_recycle_time', '<=', date('Y-m-d H:i:s')),但数据库字段是datetime类型,应为whereTime('next_recycle_time', '<=') | 改成whereTime('next_recycle_time', '<='),TP5的whereTime会自动处理时区和格式 |
实操心得:我第一次部署时,在
public/.htaccess里加了Header set X-Frame-Options "DENY"来防点击劫持,结果导致iframe嵌入的OA审批页面无法显示。后来发现,悟空CRM的OA模块本身就是独立域名,根本不需要这个头。删掉这行后,一切正常。教训是:安全配置必须针对具体模块,不能“一刀切”。
6. 性能优化与扩展建议:让这套老架构跑得更快、走得更远
6.1 数据库层面优化:小改动,大提升
TP5默认的Db::name('customer')->where('status', 1)->select(),会生成SELECT * FROM crm_customer WHERE status = 1。当客户表超过10万行时,这个查询会变慢。优化方案有三:第一,在crm_customer.status字段上建索引(ALTER TABLE crm_customer ADD INDEX idx_status (status););第二,用field()指定字段,Db::name('customer')->field('id,name,phone,owner_id,pool_id')->where('status', 1)->select(),减少网络传输量;第三,启用TP5的查询缓存,Db::name('customer')->cache(true, 3600)->where('status', 1)->select(),缓存1小时。这三个改动叠加,让公海池首页加载时间从1.2秒降到0.3秒。
6.2 前端构建优化:告别“打包5分钟,等待3分钟”
vue.config.js里,默认的productionSourceMap: true会生成巨大的.map文件,拖慢构建速度。改成productionSourceMap: false,构建时间立减40%。更重要的是,configureWebpack里加入externals: { 'vue': 'Vue', 'element-ui': 'ELEMENT', 'axios': 'axios' },然后在public/index.html里用CDN引入:
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/element-ui@2.15.6/lib/index.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios@0.21.4/dist/axios.min.js"></script>
这样,Webpack打包时不再打包这些大库,体积从3.2MB降到850KB,首屏加载时间从4.7秒降到1.8秒。CDN的缓存策略也比本地资源更优,用户第二次访问几乎零加载。
6.3 后续扩展方向:稳住底盘,再添新翼
这套架构最适合作为“数字化基座”来扩展。比如,想加微信小程序对接,不必重写后端,只需在application/api/controller/WxController.php里,用TP5的think\facade\Cache缓存微信用户信息,再调用application/crm/service/CustomerService.php的createFromWx()方法,把小程序提交的表单数据,按CRM规则存入crm_customer表。BI模块的扩展更简单:application/bi/controller/ReportController.php里,所有报表数据都来自Db::query()的原生SQL,你新加一个“客户地域分布图”,只要写一条SELECT province, COUNT(*) as count FROM crm_customer GROUP BY province,前端src/views/bi/map.vue里用ECharts渲染就行。它不强迫你用GraphQL或微服务,而是让你用最熟悉的SQL和PHP,把新需求“焊”在现有骨架上——这才是中小企业真正需要的敏捷性。
我在最后一家客户那里,用这套源码,花了两周时间,把他们的纸质客户登记表、Excel销售日报、微信群里的订单消息,全部接入悟空CRM。销售同事现在每天花15分钟录入和跟进,主管每周五下午用BI看板开10分钟复盘会,IT部门再也不用半夜接销售的“系统又崩了”电话。它不完美,但足够可靠;它不前沿,但足够实用。如果你也在找一个能立刻上手、敢于修改、不怕出问题的CRM起点,不妨就从这个带着日期戳的SQL脚本开始——毕竟,所有伟大的系统,都是从解决一个具体的小问题起步的。
简介:一套开箱即用的企业客户管理系统源码,后端基于ThinkPHP 5.0.24(需PHP7.0+),前端采用Vue.js 2.5.x、Vue-Router 3.x、Axios和Element-UI 2.6.3构建,前后端完全分离。核心功能覆盖客户信息录入与分类、公海池分配与回收(附《多公海-执行客户回收的方法说明.docx》《团队成员-定时回收的方法说明.docx》)、销售阶段跟进、任务提醒、多角色权限控制(管理员、销售、主管等)、OA办公协同模块及基础BI数据分析。提供多个版本SQL升级脚本(20210227/20210510/20210626/20210903),支持平滑升级;目录结构规范,含application(业务逻辑)、admin(后台)、crm(客户中心)、oa(办公)、bi(数据看板)、public(Web入口)、static(静态资源)、vendor(Composer依赖)等标准TP5子目录;附带LICENSE.txt开源协议、详细《资源说明(必看).txt》及HTML版使用指南,适配本地开发环境快速部署与二次定制开发,满足中小型企业客户关系数字化运营需求。


被折叠的 条评论
为什么被折叠?



