Jshop小程序商城:PHP开源DIY可视化编辑器实战指南
简介Jshop小程序商城源代码是一套面向中小企业及开发者的一站式移动电商解决方案支持微信小程序、支付宝小程序、APP、公众号与H5多端统一管理适用于快速搭建自有品牌线上商城或二次开发定制化电商系统。资源包为RAR格式大小108.18MB包含后台ThinkPHP5.1框架核心代码、小程序前端源码、API接口层及基础数据库结构文件类型涵盖PHP后端逻辑、WXML/WXSS/JS小程序页面组件、SQL初始化脚本等结构清晰、模块解耦便于理解全栈电商架构与DIY可视化功能实现原理。已有249人学习下载适合具备PHP和小程序基础的中阶开发者深入研读源码、复用业务模块、调试多端适配逻辑或基于其低门槛特性开展教学演示与项目实训。1. 这不是又一个“套模板改logo”的小程序商城Jshop 的 DIY 可视化编辑器真能拖拽出首页且后端 PHP 模块全开源可改上周帮某高校实验室搭一个校园二手书流转小程序原计划用现成 SaaS 工具——结果发现所有标榜“可视化装修”的平台前端页面能拖拽但一进后台就卡死轮播图 Banner 配置项藏在三级菜单里商品分类排序要手写 JSON更别说想加个“按学院筛选”的自定义字段。最后翻到 Jshop 小程序商城源码包解压打开admin/目录下那个叫diy-editor的文件夹直接把pages/index/index.json拖进编辑器三分钟加了个带学院下拉的搜索栏组件保存后小程序真就实时渲染出来了。这不是演示视频是我在本地php -S起的服务上亲眼看到的。它用 PHP非 Node.js 或 Java做核心业务层微信小程序端完全独立前后端彻底分离所有 DIY 页面结构、组件数据、样式配置都存 MySQL不依赖任何闭源云服务。适合需要深度定制商品流、营销规则、权限体系的中小团队——你得愿意碰 PHP 代码但不用从零写支付对接。2. 技术栈与工程结构PHP 8.1 ThinkPHP 6.1 为基座小程序端走标准 WXML/WXSSDIY 编辑器是独立 Vue 3 应用Jshop 并非“PHP 写后台 小程序套壳”那种松散组合。它的三层结构非常清晰后端 API 层ThinkPHP 6.1、管理后台Vue 2 Element UI、DIY 可视化编辑器Vue 3 TypeScript Vite。这种分法让每个模块职责单一也意味着你改首页装修逻辑不会误伤订单状态机。2.1 后端核心ThinkPHP 6.1 构建的 RESTful API 服务项目根目录下的app/是业务主干。app/api/controller/下全是控制器比如GoodsController.php处理商品列表、详情、搜索OrderController.php管订单创建、支付回调、发货。所有接口统一返回json无 HTML 渲染符合小程序调用习惯。关键点在于所有数据库操作都封装在app/common/model/对应的 Model 类中例如app/common/model/Goods.php定义了商品表字段、关联分类、SKU 规格等而不是在 Controller 里拼 SQL。这让你加一个“是否校内认证商品”字段时只需在Goods.php的$schema数组里加is_campus_verified tinyint再在迁移文件里补上字段Controller 和小程序端都不用动。// app/common/model/Goods.php 片段 protected $schema [ id bigint, title varchar, price decimal, is_campus_verified tinyint, // ← 新增字段类型明确 created_time datetime, ];提示ThinkPHP 6.1 的模型自动验证和事件钩子如afterInsert在这里被大量使用。比如商品上架时自动触发库存同步、生成静态 SKU 列表逻辑都写在 Model 的event方法里而非散落在 Controller 中。2.2 DIY 可视化编辑器一个跑在浏览器里的“页面组装工厂”真正让 Jshop 区别于其他 PHP 商城的是public/static/admin/diy-editor/这个目录。它不是一个嵌入 iframe 的简单配置页而是一个完整的 Vue 3 单页应用SPA通过vite build打包后部署在管理后台的/diy路由下。编辑器本身不处理业务逻辑只负责三件事加载当前页面的 JSON 结构如pages/index/index.json提供组件面板轮播、商品列表、富文本、自定义表单等拖拽后生成带component_id和props的节点树将修改后的 JSON 结构 POST 到/api/diy/save接口由后端存入jshop_diy_page表。这个设计的关键在于JSON 结构是纯声明式描述不包含任何 PHP 逻辑或 JS 函数体。例如一个商品列表组件的 JSON 片段长这样{ id: comp_001, type: goods-list, props: { title: 校内热卖, category_id: 5, limit: 8, show_price: true, show_sales: false }, style: { padding: 10px 0, background: #fff } }小程序端拿到这个 JSON 后用wx:for动态渲染对应组件——这意味着你新增一个“学院公告”组件只需在小程序端写好components/college-notice/index.wxml后端api/diy/render接口返回 JSON 时带上type: college-notice编辑器就能识别并拖拽使用。组件能力边界由小程序端实现编辑器只管组装。2.3 小程序端WXML 驱动JSON 配置驱动无硬编码页面小程序代码放在miniprogram/目录结构标准pages/下是各页面components/下是可复用组件utils/是工具函数。重点看pages/index/index.js它不写任何商品列表渲染逻辑而是调用getDiyPageData()从后端拉取pages/index/index.json然后遍历components数组用this.selectComponent动态加载对应组件实例。// miniprogram/pages/index/index.js 片段 onLoad() { this.getDiyPageData(index).then(res { this.setData({ diyComponents: res.data.components }); }); }, // WXML 中对应 block wx:for{{diyComponents}} wx:keyid component is{{components/ item.type /index}} >mysql -u root -p -e CREATE DATABASE jshop CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;然后导入database/jshop.sql。注意该 SQL 文件包含 37 张表其中jshop_diy_page存储 DIY 页面 JSON、jshop_goods商品主表、jshop_user用户表是核心。导入后检查jshop_config表确认site_url值为http://localhost:8000这是后续小程序请求后端的域名。提示SQL 文件末尾有INSERT INTO jshop_admin (username, password, ...)初始化管理员账号密码是明文123456首次登录后务必在后台修改。3.2 步骤二配置 Nginx 虚拟主机关键路径别写错Jshop 的入口文件是public/index.php不是根目录下的index.php。Nginx 配置必须指向public/子目录并开启 PATH_INFO 支持ThinkPHP 6.1 路由依赖它。以下是我的jshop.confserver { listen 8000; server_name localhost; root /path/to/jshop/public; # ← 必须是 public 目录不是项目根目录 index index.php; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; # PHP-FPM 地址 fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; include fastcgi_params; } }配置完 reload Nginx访问http://localhost:8000应显示 ThinkPHP 默认欢迎页证明 PHP 环境通了。3.3 步骤三启动管理后台与 DIY 编辑器管理后台地址是http://localhost:8000/admin默认账号admin/123456。登录后点击左侧菜单「DIY 可视化装修」→「首页」即可进入编辑器。此时浏览器控制台若报Failed to fetch大概率是跨域问题——因为编辑器是 Vue SPA运行在http://localhost:3000Vite 开发服务器而后端 API 在http://localhost:8000。解决方法在public/static/admin/diy-editor/vite.config.ts中配置代理// vite.config.ts export default defineConfig({ server: { proxy: { /api: { target: http://localhost:8000, // ← 指向你的 Nginx changeOrigin: true, rewrite: (path) path.replace(/^\/api/, ) } } } })然后在diy-editor/目录下执行npm run dev编辑器就会从http://localhost:3000启动并将/api/diy/*请求代理到http://localhost:8000。3.4 步骤四小程序端真机调试非模拟器小程序代码在miniprogram/用微信开发者工具打开该目录。关键配置在project.config.json中的setting→urlCheck设为false关闭合法域名校验并在app.js的App()全局对象里确认apiBase指向http://localhost:8000/api。注意iOS 真机调试时手机和电脑必须在同一局域网且apiBase要改成电脑局域网 IP如http://192.168.1.100:8000/api不能用localhost。安卓真机则可直接用localhost开发者工具已做端口映射。4. 避坑指南五个血泪经验总结专治“明明按文档做了却跑不通”这类 PHP 小程序商城最常翻车的地方往往藏在环境细节和配置耦合里。以下是我在三台不同配置的开发机上反复验证过的典型问题按出现频率排序。4.1 现象Nginx 访问http://localhost:8000/admin返回 404但http://localhost:8000能显示 ThinkPHP 欢迎页原因Nginx 的root指向了项目根目录而非public/子目录。ThinkPHP 6.1 的入口文件index.php必须在 Web 根目录下否则路由无法解析/admin这样的 PATH_INFO。解决严格检查 Nginx 配置中的root指令确保其值为/your/path/to/jshop/public不是/your/path/to/jshop。可通过ls /your/path/to/jshop/public/index.php确认文件存在。4.2 现象DIY 编辑器里拖拽组件后点“保存”控制台报401 Unauthorized原因编辑器前端未携带管理员登录态。Jshop 的/api/diy/*接口受AuthMiddleware中间件保护要求请求头带Authorization: Bearer {token}而 Vite 开发服务器默认不转发 Cookie。解决在diy-editor/src/utils/request.ts的 axios 实例中显式开启withCredentials: true// diy-editor/src/utils/request.ts const service axios.create({ baseURL: /api, timeout: 10000, withCredentials: true // ← 关键让浏览器自动带上 Cookie });同时确保 Nginx 的Access-Control-Allow-Credentials响应头为true已在jshop.conf的location ~ \.php$块中配置。4.3 现象小程序端首页空白控制台报Cannot find module components/goods-list/index原因miniprogram/app.json中的usingComponents未注册该组件或组件路径大小写错误Windows 不敏感Linux 敏感。Jshop 的组件路径约定是全小写加短横线如goods-list而非GoodsList。解决打开miniprogram/app.json检查usingComponents是否包含usingComponents: { goods-list: /components/goods-list/index }并确认miniprogram/components/goods-list/index.wxml文件真实存在且文件名是小写。4.4 现象商品图片上传后管理后台显示正常但小程序端图片 404原因图片存储路径配置不一致。Jshop 默认将图片存到public/uploads/但小程序请求时拼的是http://localhost:8000/uploads/xxx.jpg而 Nginx 未对该路径做静态文件服务配置。解决在jshop.conf的server块中添加location ^~ /uploads/ { alias /path/to/jshop/public/uploads/; expires 30d; }注意alias末尾的/必须有且路径要绝对。4.5 现象微信支付回调失败日志显示signature verification failed原因config/wechat.php中的keyAPI 密钥与微信商户平台设置的不一致或mch_id商户号填错。Jshop 使用的是微信官方 v2 版签名算法对参数顺序、空格、换行极其敏感。解决登录微信商户平台 → 「账户中心」→ 「API安全」→ 复制「API密钥」粘贴到config/wechat.php的key your_32_char_key_here。切勿手动输入用复制粘贴。同时确认mch_id是 10 位纯数字无空格。5. 核心模块拆解从 DIY 页面 JSON 到小程序真实渲染的完整链路含可复用的组件扩展模板理解 Jshop 的核心不在于看懂全部 37 张表而在于搞清“一个 DIY 页面如何从 JSON 变成用户看到的首页”。这条链路横跨后端、编辑器、小程序三方我把它拆成四个关键环节并给出可直接复用的组件扩展代码。5.1 环节一后端DiyController.php如何读取并校验 JSON当小程序请求GET /api/diy/page?nameindex时app/api/controller/DiyController.php的page()方法被调用。它不做任何渲染只做三件事查库、校验、返回。// app/api/controller/DiyController.php public function page() { $name $this-request-param(name, index); // 1. 从 jshop_diy_page 表查出对应 JSON 字符串 $page Db::name(diy_page)-where(name, $name)-find(); if (!$page) { return json([code 404, msg 页面不存在]); } // 2. JSON 解析并校验结构防注入 $data json_decode($page[content], true); if (json_last_error() ! JSON_ERROR_NONE || !isset($data[components])) { return json([code 500, msg 页面内容格式错误]); } // 3. 返回标准化结构含版本号便于前端缓存 return json([ code 200, data [ version $page[updated_time], components $data[components] ] ]); }注意$page[content]是原始 JSON 字符串json_decode后得到 PHP 数组。这里没有做任何组件逻辑处理纯粹是“管道工”角色——确保数据安全、结构正确、快速吐出。5.2 环节二DIY 编辑器如何动态注册新组件假设你要加一个“校内活动倒计时”组件。编辑器端需两步在diy-editor/src/components/下新建Countdown.vue定义 props如endTime、title在diy-editor/src/config/components.ts中注册它// diy-editor/src/config/components.ts export const COMPONENTS [ { id: countdown, name: 校内活动倒计时, icon: icon-time, category: 营销, props: [ { key: title, label: 标题, type: string, default: 活动即将开始 }, { key: endTime, label: 结束时间, type: datetime, default: } ] }, // ... 其他组件 ]注册后编辑器左侧组件面板就会出现该组件拖拽即生成带type: countdown的 JSON 节点。5.3 环节三小程序端如何动态加载并传递 props这是最精妙的一环。小程序不预编译所有组件而是运行时根据 JSON 的type字段决定加载哪个。miniprogram/components/下需新建countdown/目录结构如下countdown/ ├── index.wxml ← 模板用 {{props.title}} 绑定 ├── index.wxss ← 样式 ├── index.js ← 逻辑接收 props 并计算倒计时 └── index.json ← 声明 component关键在index.js的properties定义// miniprogram/components/countdown/index.js Component({ properties: { props: { type: Object, value: {} } }, data: { timeLeft: 00:00:00 }, lifetimes: { attached() { this.countdownTimer setInterval(() { const now Date.now(); const end new Date(this.data.props.endTime).getTime(); const diff Math.max(0, end - now); this.setData({ timeLeft: this.formatTime(diff) }); }, 1000); } }, methods: { formatTime(ms) { const d Math.floor(ms / (1000 * 60 * 60 * 24)); const h Math.floor((ms % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); const m Math.floor((ms % (1000 * 60 * 60)) / (1000 * 60)); const s Math.floor((ms % (1000 * 60)) / 1000); return ${h.toString().padStart(2,0)}:${m.toString().padStart(2,0)}:${s.toString().padStart(2,0)}; } } })WXML 中直接用{{props.title}}和{{timeLeft}}无需任何额外绑定——因为properties已声明props为 Object 类型小程序框架自动完成数据映射。5.4 环节四后端如何为新组件提供专属 API以“学院筛选”为例如果“校内活动倒计时”只需前端逻辑那“学院筛选”就需要后端支撑。比如你想在商品列表组件里加一个学院下拉后端需提供学院列表接口// app/api/controller/CollegeController.php ?php namespace app\api\controller; use think\Controller; class CollegeController extends Controller { public function list() { // 直接查 jshop_college 表需提前建好 $colleges db(college)-field(id,name)-select(); return json([code 200, data $colleges]); } }然后在小程序components/goods-list/index.js的onLoad里调用wx.request({ url: /api/college/list })拿到学院列表后渲染下拉框。所有业务扩展都遵循这个模式前端提需求 → 后端加 Controller → 小程序调用 → DIY 编辑器暴露配置项。从那以后我每次接到“加个新功能”的需求第一反应不是翻文档而是打开app/api/controller/新建一个 Controller 文件写好接口再顺藤摸瓜去改小程序组件和编辑器配置。这套 PHP JSON 小程序的三角闭环比任何 SaaS 平台都透明、可控。希望帮到你。本文还有配套的精品资源点击获取