做 LikeShop 二开的人都有一个共同的痛点:拿到源码后觉得哪里都能改,结果改完一处,升级时冲突一片。根据 LikeShop 官方二开规范和多份实践文档,核心原则只有一条------核心链路文件尽量不动,扩展逻辑新建文件承载。但现实是,有些文件你绕不开,它们就是二开需求最集中的地方。
下面梳理出二开中修改频率最高的 5 个文件,每个都给出现场级的改前/改后对比,并说明为什么这样改能兼顾功能实现与升级兼容。

1. `style/variables.scss` ------ 主题色一改,全站生效

为什么最常改:客户第一句话往往是"把主题色换成我们的品牌色"。如果颜色硬编码在几十个页面里,全局搜索替换就是灾难。LikeShop 的 UniApp 端已经内置了 SCSS 变量机制,正确做法是只改变量文件,不动页面。
改前(页面里硬编码):
```scss
/ pages/index/index.vue 或 goods-card.vue 里 /
.price {
color: #E63946; // 每个用到价格色的地方都写一遍
}
.btn-primary {
background: #FF6B35; // 主题色散落各处
}
```
改后(统一引用变量):
```scss
/ style/variables.scss /
$primary-color: #1A73E8; // 改成客户品牌蓝
$price-color: #E63946;
$border-radius-base: 12rpx;
/组件或页面中 /
.price {
color: $price-color;
}
.btn-primary {
background: $primary-color;
}
```
要点:`uni.scss` 中已全局引入变量文件,页面和组件直接使用即可,无需逐文件 import。换主题时只动 `variables.scss` 一个文件,所有引用 `$primary-color` 的按钮、高亮文字、选中状态同步变化。
2. `components/business/goods-card/goods-card.vue` ------ 商品卡片是复用之王

为什么最常改:商品卡片出现在首页瀑布流、分类页、搜索结果页、活动页。如果每个页面各写一遍,改一次样式要改四五处。LikeShop 标准版已经提供了业务组件层,但客户往往需要调整展示字段(比如加上"销量"或"标签"),改动集中在这个文件里,影响面可控。
改前(Props 较少,展示形态单一):
```vue
<!-- components/business/goods-card/goods-card.vue -->
<template>
<view class="goods-card" @click="goDetail">
<image :src="goods.image" class="goods-img" />
<view class="goods-info">
<text class="goods-name">{{ goods.name }}</text>
<text class="price">{{ goods.price }}</text>
</view>
</view>
</template>
<script>
export default {
props: {
goods: { type: Object, default: () => ({}) }
},
methods: {
goDetail() {
uni.navigateTo({ url: `/pages/goods/detail?id=${this.goods.id}` });
}
}
};
</script>
```
改后(新增 Props,扩展展示字段,不改原有逻辑):
```vue
<template>
<view class="goods-card" @click="goDetail">
<image :src="goods.image" class="goods-img" />
<view class="goods-info">
<text class="goods-name">{{ goods.name }}</text>
<!-- 新增:销量展示,由 showSales 控制 -->
<text v-if="showSales" class="goods-sales">已售 {{ goods.sales_sum }}</text>
<view class="price-row">
<text class="price">{{ goods.price }}</text>
<!-- 新增:标签位 -->
<text v-if="goods.tag" class="goods-tag">{{ goods.tag }}</text>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
goods: { type: Object, default: () => ({}) },
// 新增 Props,默认 false,不影响旧页面
showSales: { type: Boolean, default: false }
},
// methods 不变
};
</script>
```
要点:新增展示需求优先加 Props,不要直接改组件内部已有逻辑。旧页面不传 `showSales` 时表现与之前完全一致,新页面按需开启。
3. `app/shopapi/logic/OrderLogic.php` ------ 订单扩展的"雷区"

为什么最常改:订单支付成功后的附加动作(发短信、推企业微信、同步 ERP)是二开高频需求。很多人的第一反应是在 `paySuccess()` 末尾塞几行代码。功能能跑,但官方一升级,这个文件就没法直接合并了。
改前(直接修改核心 Logic,植入扩展逻辑):
```php
// app/shopapi/logic/OrderLogic.php
public static function paySuccess($orderId)
{
// ... 原有核心逻辑:更新订单状态、记录支付日志、触发库存扣减 ...
// ❌ 直接塞入二开逻辑
order = OrderModel::get(orderId);
user = UserModel::get(order'user_id');
self::sendEnterpriseWechatNotify(user\['nickname'\], order'order_sn');
return true;
}
```
改后(核心文件只加一行调用,扩展逻辑独立成类):
```php
// app/shopapi/logic/OrderLogic.php
public static function paySuccess($orderId)
{
// ... 原有核心逻辑保持不变 ...
// ✅ 只加一行调用,扩展逻辑在独立文件中
\app\extra\logic\OrderExtendLogic::afterPaySuccess($orderId);
return true;
}
```
```php
// app/extra/logic/OrderExtendLogic.php (新建文件,物理隔离)
namespace app\extra\logic;
class OrderExtendLogic
{
public static function afterPaySuccess(int $orderId): void
{
try {
order = \\app\\common\\model\\Order::get(orderId);
user = \\app\\common\\model\\User::get(order'user_id');
// 企业微信推送逻辑
self::pushWechat(user\['nickname'\], order'order_sn');
} catch (\Exception $e) {
// 扩展失败不影响支付主流程
\think\facade\Log::error("支付后扩展失败:{$e->getMessage()}");
}
}
}
```
要点:官方升级时 `OrderLogic.php` 的合并冲突只有一行调用需要处理;扩展逻辑全部在 `app/extra/` 下,完全不受升级影响。同时扩展逻辑必须 try-catch 包裹,不能因为推送失败导致支付流程异常。
4. `app/shopapi/logic/GoodsLogic.php` ------ SKU 与库存联动的实际落点

为什么最常改:客户常提"库存要和小程序、H5、线下门店打通"或"增加库存预警"。商品详情的 SKU 计算和库存校验逻辑集中在 `GoodsLogic` 中,二开时需要在读取 SKU 和扣减库存的关键节点插入渠道判断或预警检查。
改前(库存扣减直接走原逻辑,无渠道区分):
```php
// app/shopapi/logic/GoodsLogic.php 中库存扣减相关方法
public static function deductStock(skuId, num)
{
sku = GoodsSkuModel::where('id', skuId)->lock(true)->find();
if (sku\['stock'\] \< num) {
throw new \Exception('库存不足');
}
sku-\>stock = sku->stock - $num;
$sku->save();
return true;
}
```
改后(保留原逻辑,在扣减前插入渠道库存检查):
```php
public static function deductStock(skuId, num, $channel = 'default')
{
sku = GoodsSkuModel::where('id', skuId)->lock(true)->find();
if (sku\['stock'\] \< num) {
throw new \Exception('库存不足');
}
// ✅ 新增:多渠道库存检查(扩展逻辑独立方法,不破坏原有扣减)
if ($channel !== 'default') {
channelStock = \\app\\extra\\service\\ChannelStockService::check(skuId, channel, num);
if (!$channelStock) {
throw new \Exception('该渠道库存不足');
}
\app\extra\service\ChannelStockService::deduct(skuId, channel, $num);
}
// 原有扣减逻辑保持不变
sku-\>stock = sku->stock - $num;
$sku->save();
return true;
}
```
要点:方法签名增加了可选参数 `$channel`,默认值保证旧调用方不受影响。真正的多渠道库存逻辑放在 `app/extra/service/` 下,`GoodsLogic` 只负责在正确的时机调用。
5. `uniapp/pages/user/user.vue`(个人中心页)------ 菜单项增删改的集中地
为什么最常改:几乎每个二开项目都会动个人中心:加一个"我的推广"入口、改菜单图标、调整订单状态栏、隐藏某些客户不需要的模块。这个文件是页面层,本来就可以自由改,但问题在于**不要把它改成上千行的意大利面**。
改前(菜单项写死在页面模板里,状态判断也堆在页面中):
```vue
<!-- pages/user/user.vue -->
<template>
<view class="user-page">
<!-- 订单状态栏,四个图标写死 -->
<view class="order-nav">
<view class="nav-item" @click="goOrder(0)">待付款</view>
<view class="nav-item" @click="goOrder(1)">待发货</view>
<view class="nav-item" @click="goOrder(2)">待收货</view>
<view class="nav-item" @click="goOrder(3)">待评价</view>
</view>
<!-- 菜单列表,每一项手写 -->
<view class="menu-list">
<view class="menu-item" @click="goPage('/pages/user/address')">收货地址</view>
<view class="menu-item" @click="goPage('/pages/user/coupon')">优惠券</view>
<view class="menu-item" @click="goPage('/pages/user/collect')">我的收藏</view>
</view>
</view>
</template>
```
改后(菜单数据抽到 data 中,用循环渲染,新增入口只加一行数据):
```vue
<template>
<view class="user-page">
<view class="order-nav">
<view
v-for="item in orderNav"
:key="item.status"
class="nav-item"
@click="goOrder(item.status)"
>
{{ item.label }}
</view>
</view>
<view class="menu-list">
<view
v-for="item in menuList"
:key="item.path"
class="menu-item"
@click="goPage(item.path)"
>
{{ item.label }}
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
orderNav: [
{ label: '待付款', status: 0 },
{ label: '待发货', status: 1 },
{ label: '待收货', status: 2 },
{ label: '待评价', status: 3 }
],
menuList: [
{ label: '收货地址', path: '/pages/user/address' },
{ label: '优惠券', path: '/pages/user/coupon' },
{ label: '我的收藏', path: '/pages/user/collect' },
// ✅ 新增入口只需加这一行
{ label: '我的推广', path: '/pages/user/promotion' }
]
};
},
methods: {
goOrder(status) {
uni.navigateTo({ url: `/pages/order/list?status=${status}` });
},
goPage(path) {
uni.navigateTo({ url: path });
}
}
};
</script>
```
要点:页面层可以改,但用数据驱动代替手写模板。以后加菜单项只改 `menuList` 数组,不需要在 template 里复制粘贴一堆 `<view>`。如果某个菜单模块逻辑复杂(比如分销中心有自己的数据请求),应该抽成独立的页面区块组件,而不是全堆在 `user.vue` 里。
改文件不如改"位置"
这 5 个文件之所以最常被改,是因为它们分别对应了二开中最普遍的五类需求:换皮、商品展示、订单钩子、库存扩展、个人中心入口。

但真正让二开项目能持续维护的,不是"改得少",而是改对位置:
| 文件 | 正确改法 | 错误改法 |
| `variables.scss` | 只改变量值 | 页面里硬编码颜色 |
| `goods-card.vue` | 加 Props,旧行为不变 | 直接改组件内部逻辑 |
| `OrderLogic.php` | 加一行调用,扩展独立成类 | 在方法体里塞业务代码 |
| `GoodsLogic.php` | 加可选参数,扩展逻辑外置 | 重写整个扣减方法 |
| `user.vue` | 数据驱动菜单 | 模板里手写每一项 |
按照 LikeShop 官方二开白皮书的说法:新增能力 > 修改原逻辑,组合扩展 > 覆盖替换。做到这一点,官方发新版本时你才敢点"合并"。
"本文基于 LikeShop 单商户标准版 v3.5编写,多商户版逻辑类似但需注意 shop_id 隔离"。