vr-shopxo-plugin/docs/02_FRONTEND_CUSTOMIZATION.md

472 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# shopxo-uniapp 前端编译与自定义
> 调研时间2026-04-14
> 源码位置:`council-research/shopxo-eval/.worktrees/shopxo-evaluator/shopxo-uniapp-src/`
> 官方仓库https://gitee.com/zongzhige/shopxo-uniapp
---
## 一、项目概述
shopxo-uniapp 是 ShopXO 官方出品的 uni-app 前端,支持:
- ✅ 微信小程序MP-WEIXIN
- ✅ QQ 小程序MP-QQ
- ✅ 百度小程序MP-BAIDU
- ✅ 支付宝小程序MP-ALIPAY
- ✅ 抖音/头条小程序MP-TOUTIAO
- ✅ 快手小程序MP-KUAISHOU
- ✅ H5
- ✅ APPiOS/Android
**README 原文**> 已支持小程序微信、QQ、百度、支付宝、头条&抖音、快手)+ H5 + APP
---
## 二、编译到微信小程序
### 2.1 编译工具
**HBuilderX**uni-app 官方 IDE
### 2.2 编译步骤
1. 用 HBuilderX 导入 `shopxo-uniapp-src` 项目
2. 修改 `App.vue` 中的接口地址:
```javascript
// globalData 配置
request_url: 'https://your-shopxo-domain.com/', // 你的 ShopXO 域名
static_url: 'https://your-shopxo-domain.com/static/'
```
3. 修改 `manifest.json` 中的 AppID微信小程序配置
```json
{
"mp-weixin": {
"appid": "wx YOUR APPID",
"setting": {
"urlCheck": false
}
}
}
```
4. 顶部菜单 → **发行** → **微信小程序**
5. 用微信开发者工具打开发行目录
### 2.3 条件编译指令
`.vue` 文件和 `pages.json` 中使用:
```vue
<!-- #ifdef MP-WEIXIN || MP-BAIDU || MP-QQ || MP-KUAISHOU || H5 || APP -->
<view class="top-nav">微信/百度/QQ/快手/H5/APP 专属内容</view>
<!-- #endif -->
<!-- #ifdef MP-ALIPAY -->
<view>支付宝专属内容</view>
<!-- #endif -->
<!-- #ifndef H5 -->
<!-- H5 平台小程序专属内容 -->
<uni-icons type="scan" @tap="scan_event"></uni-icons>
<!-- #endif -->
```
`pages.json` 中:
```json
{
"path": "pages/goods-detail/goods-detail",
"style": {
"// #ifdef MP-WEIXIN": "",
"navigationStyle": "custom",
"// #endif": "",
}
}
```
---
## 三、pages.json 路由与全局组件
### 3.1 核心页面
```json
{
"pages": [
{ "path": "pages/index/index" }, // 首页(含 DIY
{ "path": "pages/goods-category/goods-category" }, // 分类
{ "path": "pages/cart/cart" }, // 购物车
{ "path": "pages/user/user" } // 用户中心
],
"subPackages": [
{
"root": "pages/diy", // DIY 页面
"pages": [{ "path": "diy" }]
},
{
"root": "pages/goods-detail", // 商品详情
"pages": [{ "path": "goods-detail" }]
},
{
"root": "pages/goods-search", // 搜索
"pages": [{ "path": "goods-search" }]
},
{
"root": "pages/user-order-detail", // 订单详情
"pages": [{ "path": "user-order-detail" }]
}
]
}
```
### 3.2 全局注册的自定义组件
`pages/index/index``style.usingComponents` 中全局注册:
```json
{
"component-diy": "/pages/diy/components/diy/diy",
"component-form-input": "/pages/form-input/components/form-input/form-input",
"component-layout": "/pages/design/components/layout/layout",
"component-goods-comments": "/pages/goods-detail/components/goods-comments/goods-comments",
"component-coupon-card": "/pages/plugins/coupon/components/coupon-card/coupon-card",
"component-form-input-base": "/pages/form-input/components/form-input/form-input-base"
}
```
其他页面按需引入。
---
## 四、商品详情页改造
### 4.1 商品详情页位置
`pages/goods-detail/goods-detail.vue`
### 4.2 添加票务条件渲染
`<template>` 中添加:
```vue
<template>
<view :class="theme_view">
<!-- 顶部导航始终显示-->
<view class="page ...">
<top-nav></top-nav>
<!-- 票务商品完全自定义页面结构 -->
<block v-if="goods && goods.item_type === 'ticket'">
<ticket-header :goods="goods"></ticket-header>
<ticket-seat-selector
:goods-id="goods.id"
:spec-data="goods_spec_data"
@select="on_seat_select"
></ticket-seat-selector>
<ticket-info :goods="goods"></ticket-info>
<ticket-attendee-form
:max-count="selected_seats.length"
@submit="on_attendee_submit"
></ticket-attendee-form>
<ticket-purchase-bar
:goods="goods"
:selected-seats="selected_seats"
:attendees="attendees"
@buy="ticket_buy"
></ticket-purchase-bar>
</block>
<!-- 普通商品:原有完整 UI -->
<block v-else>
<goods-photo :photos="goods_photo"></goods-photo>
<goods-price :goods="goods"></goods-price>
<goods-spec-select
v-if="goods.is_exist_many_spec == 1"
:spec-data="goods_spec_data"
></goods-spec-select>
<goods-buy-nav :buy-button="buy_button"></goods-buy-nav>
<goods-tabs :tabs="tabs"></goods-tabs>
</block>
</view>
</view>
</template>
```
### 4.3 后端配合(可选)
如果需要完全不同的页面结构,可以:
**方案 A插件钩子替换**
-`plugins_view_goods_detail_base_sku_top` 注入跳转按钮
- 点击后用 `uni.reLaunch` 跳转到 `pages/ticket-buy/ticket-buy`
**方案 B修改 Goods.php 控制器1 行)**
```php
// app/index/controller/Goods.php Index() 方法
if($goods['item_type'] == 'ticket') {
return MyView('/goods/ticket_detail'); // 自定义模板
}
return MyView();
```
**推荐方案 A**(通过插件机制,不修改核心代码)。
---
## 五、新建票务专属页面
### 5.1 在 pages.json 中添加路由
```json
{
"subPackages": [
{
"root": "pages/ticket-buy",
"pages": [
{
"path": "ticket-buy",
"style": {
"navigationBarTitleText": "选择座位",
"navigationStyle": "custom"
}
}
]
},
{
"root": "pages/ticket-verify",
"pages": [
{
"path": "ticket-verify",
"style": {
"navigationBarTitleText": "票务核销",
"enablePullDownRefresh": false
}
}
]
},
{
"root": "pages/ticket-wallet",
"pages": [
{
"path": "ticket-wallet",
"style": {
"navigationBarTitleText": "我的票夹"
}
}
]
}
]
}
```
### 5.2 页面目录结构
```
pages/ticket-buy/
├── ticket-buy.vue # 选座 + 购票主流程
└── components/
├── seat-selector.vue # 座位选择器
├── attendee-form.vue # 观演人表单
└── purchase-bar.vue # 购买栏
pages/ticket-wallet/
├── ticket-wallet.vue # 票夹主页面
└── components/
└── ticket-card.vue # 电子票卡片(含 QR 码)
pages/ticket-verify/
└── ticket-verify.vue # B 端核销页面(参考 realstore/check.vue
```
---
## 六、uni.scanCode 扫码能力
### 6.1 基础用法
```javascript
// 扫码
uni.scanCode({
onlyFromCamera: true, // 只允许相机扫码
success: (res) => {
console.log('扫码结果:', res.result);
// res.result: 扫码内容
// res.scanType: 二维码类型QR_CODE 等)
},
fail: (err) => {
console.error('扫码失败', err);
}
});
```
### 6.2 在 H5 平台的处理
```vue
<!-- #ifndef H5 -->
<uni-icons type="scan" size="56rpx" @tap="scan_event"></uni-icons>
<!-- #endif -->
<!-- H5 平台提示手动输入 -->
<!-- #ifdef H5 -->
<view class="cr-grey text-size-sm" @tap="show_manual_input">
请输入核销码
</view>
<!-- #endif -->
```
### 6.3 参考实现
ShopXO 的 `realstore/check/check.vue` 已完整实现:
- 扫码触发 → 自动提交验证
- 手动输入兼容
- 成功/失败状态显示
- 连续扫描(核销后清空输入框)
---
## 七、QR 码展示
### 7.1 后端生成 QR 码图片 URL
```javascript
// 生成票务 QR 码 URL
function getTicketQrUrl(ticketId) {
const baseUrl = app.globalData.request_url;
const ticketData = JSON.stringify({
id: ticketId,
exp: Date.now() + 30 * 86400 * 1000 // 30天有效期
});
const encoded = encodeURIComponent(
uni.base64ToString(
uni.stringToBase64(ticketData)
)
);
return `${baseUrl}?s=index/qrcode/index&content=${encoded}&size=8&level=H`;
}
```
### 7.2 在页面中展示
```vue
<template>
<view class="ticket-card">
<image :src="qrCodeUrl" mode="aspectFit" class="qr-image" />
<view class="ticket-code">{{ ticket.code }}</view>
</view>
</template>
```
---
## 八、manifest.json 关键配置
### 8.1 多端配置
```json
{
"name": "ShopXO",
"appid": "__UNI__50E3C11",
"description": "ShopXO开源商城...",
"transformPx": false,
"app-plus": {
"usingComponents": true,
"nvueCompiler": "uni-app",
"distribute": {
"android": { ... },
"ios": { ... },
"mp-weixin": {
"appid": "wx YOUR APPID",
"setting": {
"urlCheck": false,
"es6": true,
"minified": true
},
"usingComponents": true
}
}
}
}
```
### 8.2 权限配置(微信小程序)
```json
"mp-weixin": {
"permission": {
"scope.userLocation": {
"desc": "用于场馆定位"
}
}
}
```
---
## 九、自定义组件注册
### 9.1 全局注册(所有页面可用)
`pages/index/index.vue``style.usingComponents` 中添加:
```json
"component-ticket-seat": "/pages/ticket-buy/components/seat-selector/seat-selector",
"component-ticket-card": "/pages/ticket-wallet/components/ticket-card/ticket-card",
"component-attendee-form": "/pages/ticket-buy/components/attendee-form/attendee-form"
```
### 9.2 页面级注册
在单个页面的 `pages.json` 中:
```json
{
"path": "pages/ticket-wallet/ticket-wallet",
"style": {
"usingComponents": {
"component-ticket-card": "/pages/ticket-wallet/components/ticket-card/ticket-card"
}
}
}
```
---
## 十、ShopXO API 调用
### 10.1 请求封装
ShopXO uni-app 使用全局封装的请求方法:
```javascript
uni.request({
url: app.globalData.get_request_url('action', 'controller', 'module'),
method: 'POST',
data: { ... },
success: (res) => {
if (res.data.code == 0) {
// 成功
} else {
// 失败
}
}
});
```
### 10.2 插件 API 路径
对于插件的 API
```javascript
// 格式get_request_url(action, controller, plugins_name)
const url = app.globalData.get_request_url(
'verify', // action
'adminverify', // controller
'vrticket' // 插件名apps 目录下的目录名)
);
```
### 10.3 鉴权
请求自动带上登录态:
```javascript
// 已在全局封装,每次请求自动附加:
// Header: Authorization / Token
// 或者通过 Session/Cookie
```