3个文件已删除
11个文件已添加
18个文件已修改
1 文件已重命名
1281 ■■■■■ 已修改文件
.gitignore 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.hbuilderx/launch.json 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
BAT使用说明.md 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
components/personalAndShareCoupon/index.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
components/personalAndShareCoupon/rule.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
components/popActivity/index.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
components/popCoupon/index.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
components/popVideo/index.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
components/productWindow/index.vue 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
components/yueyangZone/index.vue 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
config/app.js 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
config/env/index.js 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
config/env/prod.js 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
config/env/sit.js 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
libs/routine.js 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manifest.json.demo 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/goods_cate/goods_cate.vue 203 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/index/index.vue 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/order_pay_status/index.vue 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/user/index.vue 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/users/order_confirm/index.vue 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/users/user_coupon/index.vue 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/zone/index.vue 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
project.config.json 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
publish-prod.bat 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
publish-sit.bat 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
scripts/manifest.js 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
scripts/publish.js 312 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
scripts/set-env.js 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
switch-prod.bat 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
switch-sit.bat 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
需要修改的配置文件.txt 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.gitignore
....@@ -2,7 +2,7 @@
22 node_modules
33 /dist
44
5
-# local env files
5
+# 本地环境文件
66 .env.local
77 .env.*.local
88
....@@ -21,4 +21,8 @@
2121 *.sw?
2222 build.sh
2323 .idea
24
-unpackage
24
+unpackage
25
+/.hbuilderx
26
+# 根目录 project.config.json 不参与 mp-weixin 编译(小程序 appid 由 scripts/publish.js 写入产物)
27
+project.config.json
28
+/manifest.json
.hbuilderx/launch.json
deleted file mode 100644
....@@ -1,24 +0,0 @@
1
-{ // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
2
- // launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数
3
- "version": "0.0",
4
- "configurations": [{
5
- "app-plus" :
6
- {
7
- "launchtype" : "remote"
8
- },
9
- "default" :
10
- {
11
- "launchtype" : "remote"
12
- },
13
- "h5" :
14
- {
15
- "launchtype" : "remote"
16
- },
17
- "mp-weixin" :
18
- {
19
- "launchtype" : "remote"
20
- },
21
- "type" : "uniCloud"
22
- }
23
- ]
24
-}
BAT使用说明.md
....@@ -0,0 +1,133 @@
1
+# 4 个 bat 使用说明
2
+
3
+项目根目录下 4 个双击即用的批处理,按用途分两类:
4
+
5
+| 我想做的事 | 用哪个 bat |
6
+| --- | --- |
7
+| 出小程序发行版,并自动在微信开发者工具里打开 | `publish-sit.bat`(测试)/ `publish-prod.bat`(生产) |
8
+| 在 HBuilderX 里「运行 → 运行到小程序模拟器」调试 | 先切环境:`switch-sit.bat` / `switch-prod.bat` |
9
+
10
+> 一句话区分:`publish-*` 产出 `unpackage/dist/build/mp-weixin` 发行产物,编译期 appid 由脚本注入,**不改仓库里的任何文件**;`switch-*` 改的是 HBuilderX 的编译输入 `manifest.json`,供 GUI 编译/调试用。
11
+
12
+---
13
+
14
+## 1. 一次性前提
15
+
16
+1. Node.js(命令行 `node -v` 可用)
17
+2. HBuilderX(提供命令行 `cli.exe`)
18
+3. 微信开发者工具,且 **设置 → 安全设置 → 服务端口 = 开启**(不开时 `cli.bat open` 会失败,脚本会降级提示你手动导入)
19
+
20
+**工具路径不需要配置**,脚本按顺序自动查找,命中后把所在目录加入本次进程的 `PATH`(便于按名字直接调用):
21
+
22
+1. 环境变量 `HBUILDERX_CLI` / `WX_DEVTOOLS_CLI`
23
+2. 系统 `PATH`
24
+3. 常见安装目录(`Program Files`、`Program Files (x86)`、用户目录、桌面、下载、各磁盘常见软件目录…)
25
+4. 磁盘浅层扫描(≤2 层,只扫本地固定磁盘,跳过系统目录)
26
+
27
+找不到时直接报错并给出办法:
28
+
29
+```bat
30
+set PATH=%PATH%;<HBuilderX 或 微信开发者工具 的安装目录>
31
+set HBUILDERX_CLI=<HBuilderX cli.exe 完整路径>
32
+set WX_DEVTOOLS_CLI=<微信开发者工具 cli.bat 完整路径>
33
+```
34
+
35
+---
36
+
37
+## 2. publish-sit.bat / publish-prod.bat
38
+
39
+双击 = 发行并自动打开。实际执行 4 步:
40
+
41
+| 步骤 | 做什么 | 具体命令 |
42
+| --- | --- | --- |
43
+| 0 | 生成 `manifest.json`(模板 `manifest.json.demo` + 该环境 appid) | 内置逻辑 |
44
+| 1 | HBuilderX CLI 发行编译 | `cli.exe publish --platform mp-weixin --project <项目根>` |
45
+| 2 | 回写产物 appid(兜底校正) | 写入 `unpackage/dist/build/mp-weixin/project.config.json` |
46
+| 3 | 微信开发者工具 CLI 打开产物 | `cli.bat open --project <产物目录>` |
47
+
48
+- 发行产物:`unpackage/dist/build/mp-weixin`
49
+- 编译日志在 **HBuilderX 控制台**里看
50
+- 任一步失败,窗口会打印 `[FAILED] ...`(退出码非 0)
51
+
52
+### 可加的参数(`%*` 原样传给 `scripts/publish.js`)
53
+
54
+| 参数 | 作用 |
55
+| --- | --- |
56
+| `--dry-run` | 只打印将要执行的命令,不做任何写操作 |
57
+| `--no-open` | 只编译,不打开微信开发者工具(此种情况不要求安装开发者工具) |
58
+| `--no-gen` | 跳过生成 `manifest.json`(要求它已存在;适合你手工维护 manifest 的场景) |
59
+| 其他参数 | 原样透传给 HBuilderX cli |
60
+
61
+用法示例(在命令行窗口里):
62
+
63
+```bat
64
+publish-sit.bat --dry-run
65
+publish-prod.bat --no-open
66
+```
67
+
68
+等价命令:`npm run release:sit` / `npm run release:prod` / `node scripts/publish.js sit`
69
+
70
+---
71
+
72
+## 3. switch-sit.bat / switch-prod.bat
73
+
74
+双击 = 按该环境重新生成 `manifest.json`(`manifest.json.demo` + 该环境 appid)。
75
+
76
+- 等价命令:`npm run env:sit` / `npm run env:prod` / `node scripts/set-env.js sit`
77
+- **切完必须做两件事**:① 在 HBuilderX 里重新编译一次;② 在微信开发者工具里重新打开该项目(appid 变更需重开)
78
+- 幂等:内容已一致时提示「无需重写」,可放心重复双击
79
+- 适用场景:在 HBuilderX GUI 里直接运行/调试(编译期 appid 取自 `manifest.json`),或需要出 App 包(顶层 DCloud appid)
80
+
81
+### 两个环境的取值
82
+
83
+| 环境 | 小程序 appid(`mp-weixin.appid`) | DCloud appid(顶层) | 接口地址 |
84
+| --- | --- | --- | --- |
85
+| sit | `wx7e443c60d08449de` | `__UNI__3D0C5FE` | `https://sit.tohours.com/crmeb/front` |
86
+| prod | `wx15d029cdf2e92a9f` | `__UNI__CE0C605` | `https://acmall.bluedeer.com.cn/crmeb/front` |
87
+
88
+数据源:`config/env/sit.js`、`config/env/prod.js`(唯一数据源,改这两处即可,两个文件键名保持一致)。
89
+
90
+---
91
+
92
+## 4. 为什么跑完 git 是干净的
93
+
94
+`manifest.json` 是**生成物**,`project.config.json` 与 `unpackage/` 已忽略:
95
+
96
+```
97
+manifest.json.demo(入库,模板) ──注入环境 appid──▶ manifest.json(已忽略,不入库)
98
+```
99
+
100
+所以发行、切环境都不会产生 git 改动,`git status` 只会有你自己改的源码。
101
+
102
+两点注意:
103
+
104
+1. 新 clone 的机器**没有 `manifest.json`,HBuilderX 打不开项目** —— 先跑一次 `switch-sit.bat`(或任意 `publish-*.bat`)把它生成出来。
105
+2. 在 HBuilderX GUI 里直接改的 manifest 配置落在**生成物**上,下次生成会被模板内容覆盖(脚本会打印警告)。需要长期保留的改动,请写进 `manifest.json.demo`。
106
+
107
+---
108
+
109
+## 5. 接口地址为什么自动对得上
110
+
111
+`config/env/index.js` 的识别顺序:手动 `OVERRIDE_ENV` > 小程序 appid 自动识别 > H5 域名识别 > 默认 prod,对外导出 `ENV_NAME` / `IS_SIT` / `IS_PROD`。
112
+
113
+也就是「用哪个 appid 编译」就等于「用哪套接口」,不会出现接口与环境错配 —— 你只需要选对 bat。
114
+
115
+---
116
+
117
+## 6. 常见问题
118
+
119
+| 现象 | 处理 |
120
+| --- | --- |
121
+| 窗口报 `'…' is not recognized as an internal or external command` 且带中文乱码 | bat 内混用 UTF-8 中文时,cmd 按代码页解析会错位。现在 4 个 bat **全部是 ASCII**,中文提示由 Node 打印。自己改 bat 时不要加中文,要加就用 Node 输出 |
122
+| `未找到 HBuilderX 命令行 cli.exe` / `未找到微信开发者工具命令行 cli.bat` | 按报错里的两种办法处理:加 `PATH` 或设对应环境变量(见第 1 节) |
123
+| 自动打开失败,提示未开启服务端口 | 开发者工具 → 设置 → 安全设置 → 打开服务端口;或手动「导入项目」选 `unpackage\dist\build\mp-weixin` |
124
+| 开发者工具里 appid 还是旧的 | appid 变更后需要在工具里**重新打开**该项目 |
125
+| `--no-gen 需要 manifest.json 已存在` | 先跑一次 `switch-*.bat`,或去掉 `--no-gen` |
126
+| `cli.exe 退出码 …` | 编译失败,看 HBuilderX 控制台里的具体报错 |
127
+
128
+---
129
+
130
+## 7. 附注
131
+
132
+- 4 个 bat 都会先 `cd /d "%~dp0"`,所以在任意目录双击或命令行调用都不会跑错位置;末尾 `pause` 方便双击后查看输出。
133
+- 微信开发者工具生成的 `project.private.config.json` 目前仍入库(未被忽略)。若想连它一起做到零噪音,可自行加入 `.gitignore` 并执行 `git rm --cached project.private.config.json`。
components/personalAndShareCoupon/index.vue
....@@ -409,7 +409,7 @@
409409 .box {
410410 width: 600rpx;
411411 padding: 90rpx 0 70rpx 0;
412
- background: url("https://ossacmall.bluedeer.com.cn/app/static/images/confirm_bg.png") center 0rpx/100% auto no-repeat;
412
+ background: url("https://ossacmall.bluedeer.com.cn/app/static/images/confirm_bg.png") center 0rpx / 100% auto no-repeat;
413413 background-color: #f5f5f5;
414414 position: absolute;
415415 left: 50%;
components/personalAndShareCoupon/rule.vue
....@@ -55,7 +55,7 @@
5555 .box {
5656 width: 600rpx;
5757 padding: 90rpx 0 70rpx 0;
58
- background: url("https://ossacmall.bluedeer.com.cn/app/static/images/confirm_bg.png") center -50rpx/100% auto no-repeat;
58
+ background: url("https://ossacmall.bluedeer.com.cn/app/static/images/confirm_bg.png") center -50rpx / 100% auto no-repeat;
5959 background-color: #f5f5f5;
6060 position: absolute;
6161 left: 50%;
components/popActivity/index.vue
....@@ -138,7 +138,7 @@
138138 .box {
139139 width: 600rpx;
140140 padding: 90rpx 0 70rpx 0;
141
- background: url("https://ossacmall.bluedeer.com.cn/app/static/images/confirm_bg.png") center 0rpx/100% auto no-repeat;
141
+ background: url("https://ossacmall.bluedeer.com.cn/app/static/images/confirm_bg.png") center 0rpx / 100% auto no-repeat;
142142 background-color: #f5f5f5;
143143 position: absolute;
144144 left: 50%;
components/popCoupon/index.vue
....@@ -322,7 +322,7 @@
322322 .box {
323323 width: 600rpx;
324324 padding: 90rpx 0 70rpx 0;
325
- background: url("https://ossacmall.bluedeer.com.cn/app/static/images/confirm_bg.png") center -120rpx/100% auto no-repeat;
325
+ background: url("https://ossacmall.bluedeer.com.cn/app/static/images/confirm_bg.png") center -120rpx / 100% auto no-repeat;
326326 background-color: #f5f5f5;
327327 position: absolute;
328328 left: 50%;
components/popVideo/index.vue
....@@ -131,7 +131,7 @@
131131
132132 .box {
133133 padding: 90rpx 0 70rpx 0;
134
- background: url("https://ossacmall.bluedeer.com.cn/app/static/images/confirm_bg.png") center 320rpx/100% auto no-repeat;
134
+ background: url("https://ossacmall.bluedeer.com.cn/app/static/images/confirm_bg.png") center 320rpx / 100% auto no-repeat;
135135 background-color: #f5f5f5;
136136 position: relative;
137137 border-radius: 14rpx;
components/productWindow/index.vue
....@@ -186,19 +186,6 @@
186186 path: url,
187187 envVersion: miniProgramInfo.envVersion == 'release' ? 'release' : 'trial',
188188 })
189
-
190
- // let params = {
191
- // custAgentNo: that.$store.getters.belong?.agentNo || '',
192
- // custAgentChnl: that.$store.getters.belong?.channel || '',
193
- // custAgentCoy: that.$store.getters.belong?.companyCode || '',
194
- // custAgentBranch: that.$store.getters.belong?.branchCode || '',
195
- // custAgentOrgNo: that.$store.getters.belong?.subCode || ''
196
- // };
197
- // let webUrl = 'https://ossacmall.bluedeer.com.cn/qr/index.html?isMall=true' + (miniProgramInfo.envVersion == 'release' ? '' : '&isTrial=true') + '&params=' + encodeURIComponent(JSON.stringify(params)) + '&pageUrl=' + encodeURIComponent(url);
198
- // if (HTTP_REQUEST_URL.indexOf('tohours') > 0) {
199
- // webUrl = webUrl.replace('https://ossacmall.bluedeer.com.cn/qr', 'https://sit.tohours.com/crmeb_web/qr/')
200
- // }
201
- // uni.navigateTo({ url: '/pages/users/web_page/index?webUel=' + encodeURIComponent(webUrl) })
202189 }
203190 if (!that.$store.getters.isLogin) {
204191 toLogin(that);
components/yueyangZone/index.vue
....@@ -9,13 +9,10 @@
99 </view>
1010 </view>
1111 <view class="points-right">
12
- <image src="https://ossacmall.bluedeer.com.cn/app/static/images/yueyang_jifen.png" class="lottery-btn" mode="widthFix"
13
- @click="goLottery"></image>
14
- </view>
15
- <view class="detail-btn" @click="goDetail">
16
- <text>消耗明细</text>
17
- </view>
18
- </view>
12
+ <image src="https://ossacmall.bluedeer.com.cn/app/static/images/yueyang_jifen.png" class="lottery-btn" mode="widthFix"
13
+ @click="goLottery"></image>
14
+ </view>
15
+ </view>
1916
2017 <!-- 优惠券兑换区 -->
2118 <view class="coupon-area">
....@@ -133,11 +130,6 @@
133130 },
134131 onShow() {},
135132 methods: {
136
- goDetail() {
137
- uni.navigateTo({
138
- url: '/pages/consumption/index?zoneId=' + this.zoneInfo.id
139
- })
140
- },
141133 goLottery() {
142134 this.$emit('goLottery')
143135 },
....@@ -322,20 +314,7 @@
322314 height: 57rpx;
323315 }
324316
325
- .detail-btn {
326
- border: 2rpx solid #fff;
327
- /* border-radius: 10rpx; */
328
- border-top-left-radius: 20rpx;
329
- border-bottom-left-radius: 20rpx;
330
- padding: 10rpx;
331
- background-color: #daf2b2;
332
- color: #1e984f;
333
- width: 50rpx;
334
- position: fixed;
335
- top: 200rpx;
336
- right: 0rpx;
337
- line-height: 1rem;
338
- }
317
+
339318
340319 .coupon-area {
341320 display: flex;
config/app.js
....@@ -7,31 +7,12 @@
77 // +----------------------------------------------------------------------
88 // | Author: CRMEB Team <admin@crmeb.com>
99 // +----------------------------------------------------------------------
10
-//移动端商城API
11
-let domain = 'https://acmall.bluedeer.com.cn/crmeb/front'
1210
13
-module.exports = {
14
- // 请求域名 格式: https://您的域名
15
- HTTP_REQUEST_URL:domain,
16
-
17
- // #ifdef MP
18
- HTTP_REQUEST_URL: domain,
19
- // #endif
20
- HTTP_REQUEST_URL:'https://acmall.bluedeer.com.cn/crmeb/front',
21
- HTTP_ADMIN_URL:'https://acmall.bluedeer.com.cn/crmeb/admin',
22
- APPID:'wx15d029cdf2e92a9f',
23
- GDP_ACCOUNT_ID:'8672f93377575ace',
24
- GDP_DATASOURCE_ID:'b01182b2d4f73e45',
25
- GDP_SERVER_URL:'https://giocollect.aviva-cofco.com.cn',
26
- GDP_DEBUG:true,
27
- THIRD_APPID:'wxa83daa550324ead8',
28
- // #ifdef H5
29
- //H5接口是浏览器地址
30
- // HTTP_REQUEST_URL: window.location.protocol+"//"+window.location.host,
31
- // http://api.java.crmeb.net:20001
32
- HTTP_REQUEST_URL:domain,
33
- // #endif
34
-
11
+// 环境相关参数(HTTP_REQUEST_URL/HTTP_ADMIN_URL/APPID/THIRD_APPID/GDP_* 等)
12
+// 统一维护在 config/env/ 下,本文件只做合并,对外导出名保持不变
13
+const env = require('./env');
14
+
15
+module.exports = Object.assign({}, env, {
3516 HEADER:{
3617 'content-type': 'application/json'
3718 },
....@@ -44,4 +25,4 @@
4425 EXPIRE:0,
4526 //分页最多显示条数
4627 LIMIT: 10
47
-};
28
+});
config/env/index.js
....@@ -0,0 +1,49 @@
1
+// +----------------------------------------------------------------------
2
+// | 环境选择入口:一套代码跑多环境
3
+// | 识别优先级:手动指定 > 小程序 appid 自动识别 > H5 域名识别 > 默认 prod
4
+// | 说明:小程序 appid 由 manifest.json -> mp-weixin.appid 决定,
5
+// | 所以“用哪个 appid 跑”就等于“用哪套接口”,不会出现接口与环境错配
6
+// +----------------------------------------------------------------------
7
+const envs = {
8
+ prod: require('./prod.js'),
9
+ sit: require('./sit.js')
10
+};
11
+
12
+// 手动强制指定环境:'' = 自动识别(推荐);也可填 'prod' 或 'sit'
13
+const OVERRIDE_ENV = '';
14
+
15
+// appid -> 环境名 映射
16
+const APPID_ENV_MAP = {};
17
+Object.keys(envs).forEach(function(key) {
18
+ APPID_ENV_MAP[envs[key].APPID] = key;
19
+});
20
+
21
+function detectEnv() {
22
+ if (OVERRIDE_ENV && envs[OVERRIDE_ENV]) return OVERRIDE_ENV;
23
+
24
+ // #ifdef MP
25
+ try {
26
+ const info = uni.getAccountInfoSync();
27
+ const appId = info && info.miniProgram && info.miniProgram.appId;
28
+ if (appId && APPID_ENV_MAP[appId]) return APPID_ENV_MAP[appId];
29
+ } catch (e) {}
30
+ // #endif
31
+
32
+ // #ifdef H5
33
+ if (typeof location !== 'undefined' && location.hostname) {
34
+ const host = location.hostname;
35
+ if (/sit|tohours|localhost|127\.0\.0\.1/.test(host)) return 'sit';
36
+ }
37
+ // #endif
38
+
39
+ return 'prod';
40
+}
41
+
42
+const ENV_NAME = detectEnv();
43
+const current = envs[ENV_NAME] || envs.prod;
44
+
45
+module.exports = Object.assign({}, current, {
46
+ ENV_NAME: ENV_NAME,
47
+ IS_PROD: ENV_NAME === 'prod',
48
+ IS_SIT: ENV_NAME === 'sit'
49
+});
config/env/prod.js
....@@ -0,0 +1,23 @@
1
+// +----------------------------------------------------------------------
2
+// | 生产环境(PROD)参数
3
+// | 注意:新增环境参数时,请同步维护 prod.js 与 sit.js,保持键名完全一致
4
+// +----------------------------------------------------------------------
5
+module.exports = {
6
+ ENV_NAME: 'prod',
7
+ // 请求域名
8
+ HTTP_REQUEST_URL: 'https://acmall.bluedeer.com.cn/crmeb/front',
9
+ HTTP_ADMIN_URL: 'https://acmall.bluedeer.com.cn/crmeb/admin',
10
+ // 微信小程序 appid(需与 manifest.json -> mp-weixin.appid 保持一致)
11
+ APPID: 'wx15d029cdf2e92a9f',
12
+ // 跳转关联小程序 appid
13
+ THIRD_APPID: 'wxa83daa550324ead8',
14
+ // GrowingIO 埋点
15
+ GDP_ACCOUNT_ID: '8672f93377575ace',
16
+ GDP_DATASOURCE_ID: 'b01182b2d4f73e45',
17
+ GDP_SERVER_URL: 'https://giocollect.aviva-cofco.com.cn',
18
+ GDP_DEBUG: true,
19
+ // 活动页/内嵌 web 资源地址(替代代码里按域名判断环境)
20
+ WEB_ACTIVITY_BASE: 'https://ossacmall.bluedeer.com.cn',
21
+ // DCloud 应用标识(需与 manifest.json -> appid 保持一致)
22
+ UNI_APPID: '__UNI__CE0C605'
23
+};
config/env/sit.js
....@@ -0,0 +1,23 @@
1
+// +----------------------------------------------------------------------
2
+// | 测试环境(SIT)参数
3
+// | 注意:新增环境参数时,请同步维护 prod.js 与 sit.js,保持键名完全一致
4
+// +----------------------------------------------------------------------
5
+module.exports = {
6
+ ENV_NAME: 'sit',
7
+ // 请求域名
8
+ HTTP_REQUEST_URL: 'https://sit.tohours.com/crmeb/front',
9
+ HTTP_ADMIN_URL: 'https://sit.tohours.com/crmeb/admin',
10
+ // 微信小程序 appid(需与 manifest.json -> mp-weixin.appid 保持一致)
11
+ APPID: 'wx7e443c60d08449de',
12
+ // 跳转关联小程序 appid
13
+ THIRD_APPID: 'wx64d2ecbbeb3eae79',
14
+ // GrowingIO 埋点
15
+ GDP_ACCOUNT_ID: '83b2522b4d9c972a',
16
+ GDP_DATASOURCE_ID: 'a1616a9a748d2c7f',
17
+ GDP_SERVER_URL: 'https://collecttest.aviva-cofco.com.cn',
18
+ GDP_DEBUG: true,
19
+ // 活动页/内嵌 web 资源地址(替代代码里按域名判断环境)
20
+ WEB_ACTIVITY_BASE: 'https://sit.tohours.com/crmeb_web',
21
+ // DCloud 应用标识(需与 manifest.json -> appid 保持一致)
22
+ UNI_APPID: '__UNI__3D0C5FE'
23
+};
libs/routine.js
....@@ -87,6 +87,7 @@
8787 uni.login({
8888 provider: provider,
8989 success(res) {
90
+ console.log(uni.login,res);
9091 if (res.code) Cache.set(STATE_R_KEY, res.code, 10800);
9192 return resolve(res.code);
9293 },
manifest.json
similarity index 98%rename from manifest.jsonrename to manifest.json.demo
....@@ -1,6 +1,6 @@
11 {
22 "name" : "CRMEB Java 商城",
3
- "appid" : "__UNI__CE0C605",
3
+ "appid" : "__UNI__3D0C5FE",
44 "description" : "CRMEB Java 商城",
55 "versionName" : "1.0.0",
66 "versionCode" : 1001,
....@@ -123,7 +123,7 @@
123123 "quickapp" : {},
124124 /* 小程序特有相关 */
125125 "mp-weixin" : {
126
- "appid" : "wx15d029cdf2e92a9f",
126
+ "appid" : null,
127127 "setting" : {
128128 "urlCheck" : true,
129129 "minified" : true,
package.json
....@@ -1,4 +1,10 @@
11 {
2
+ "scripts": {
3
+ "env:prod": "node scripts/set-env.js prod",
4
+ "env:sit": "node scripts/set-env.js sit",
5
+ "release:sit": "node scripts/publish.js sit",
6
+ "release:prod": "node scripts/publish.js prod"
7
+ },
28 "dependencies": {
39 "bignumber.js": "^9.3.1",
410 "vue-inset-loader": "^1.2.6"
pages/goods_cate/goods_cate.vue
....@@ -13,7 +13,7 @@
1313 <scroll-view scroll-y="true" scroll-with-animation='true'
1414 :style="{height: (height-280/750*windowWidth) + 'px'}" :scroll-into-view="'menu_item'+navActive">
1515 <view class='item acea-row row-center-wrapper menu_item' :id="'menu_item'+index"
16
- :class='index==navActive?"on":""' v-for="(item,index) in categoryList" :key="item._key"
16
+ :class='index==navActive?"on":""' v-for="(item,index) in categoryList" :key="index"
1717 @click='tap(index,"b"+index)'>
1818 <image v-if="item.extra" class="img" :src="item.extra" mode="aspectFill"></image>
1919 <view class="text">{{item.name}}</view>
....@@ -25,7 +25,7 @@
2525 <scroll-view :scroll-into-view="'subMenu_item'+subActive" scroll-x="true" scroll-with-animation='true'
2626 style="width: 100%;">
2727 <view class="subMenu_wrapper">
28
- <block v-for="(itemn,indexn) in categoryList[navActive].child" :key="itemn._key"><text
28
+ <block v-for="(itemn,indexn) in categoryList[navActive].child" :key="indexn"><text
2929 class="subMenu_item" :id="'subMenu_item'+indexn" :class='indexn==subActive?"on":""'
3030 @click='tapSub(indexn)'>{{itemn.name}}</text></block>
3131 </view>
....@@ -35,15 +35,15 @@
3535 :style="{top: (categoryList[navActive].show2Category==0?0:118) + 'rpx',height: (height-(categoryList[navActive].show2Category==0?0:(122/750*windowWidth))) + 'px'}">
3636 <scroll-view :scroll-top="scrollTop" class="goodlistScroll" scroll-y="true" style="height:100%;"
3737 :scroll-into-view="scrollIntoView" @scroll="handleScroll" :throttle="false">
38
- <block v-for="(aitem,aindex) in categoryList" :key="aitem._key">
39
- <view class="list" v-if="aitem.child.length>0" v-for="(sitem,sindex) in aitem.child" :key="sitem._key"
38
+ <block v-for="(aitem,aindex) in categoryList" :key="aindex">
39
+ <view class="list" v-if="aitem.child.length>0" v-for="sitem,sindex in aitem.child" :key="sindex"
4040 :style="(aindex==categoryList.length-1&&sindex==aitem.child.length-1)?('min-height:'+(height-370/750*windowWidth)+'px'):''"
4141 :id="'list'+aindex+'_'+sindex">
4242 <view class="list_title">{{sitem.name}}</view>
4343 <view class="list_wrapper cf">
4444 <view class="list_column cfl">
4545 <block style="float:left" v-if="sitem.productArr.length>0"
46
- v-for="(gitem,gindex) in sitem.productArr" :key="gitem._key">
46
+ v-for="(gitem,gindex) in sitem.productArr" :key="gindex">
4747 <GoodBlock v-if="gindex%2==0" :size="'small'" :pageName="'分类'"
4848 :moduleName="'分类商品列表'" :careBeanRate="careBeanRate" :goodsInfo="gitem">
4949 </GoodBlock>
....@@ -51,7 +51,7 @@
5151 </view>
5252 <view class="list_column cfr">
5353 <block style="float:left" v-if="sitem.productArr.length>0"
54
- v-for="(gitem,gindex) in sitem.productArr" :key="gitem._key">
54
+ v-for="(gitem,gindex) in sitem.productArr" :key="gindex">
5555 <GoodBlock v-if="gindex%2==1" :size="'small'" :pageName="'分类'"
5656 :moduleName="'分类商品列表'" :careBeanRate="careBeanRate" :goodsInfo="gitem">
5757 </GoodBlock>
....@@ -66,14 +66,14 @@
6666 <view class="list_wrapper cf">
6767 <view class="list_column cfl">
6868 <block v-if="aitem.productArr.length>0" v-for="(gitem,gindex) in aitem.productArr"
69
- :key="gitem._key">
69
+ :key="gindex">
7070 <GoodBlock v-if="gindex%2==0" :pageName="'分类'" :moduleName="'分类商品列表'"
7171 :careBeanRate="careBeanRate" :goodsInfo="gitem"></GoodBlock>
7272 </block>
7373 </view>
7474 <view class="list_column cfr">
7575 <block v-if="aitem.productArr.length>0" v-for="(gitem,gindex) in aitem.productArr"
76
- :key="gitem._key">
76
+ :key="gindex">
7777 <GoodBlock v-if="gindex%2==1" :pageName="'分类'" :moduleName="'分类商品列表'"
7878 :careBeanRate="careBeanRate" :goodsInfo="gitem"></GoodBlock>
7979 </block>
....@@ -144,7 +144,11 @@
144144 totalGetNum: 0,
145145 // 首屏优化:当前分类的待完成请求数
146146 catePending: [],
147
+ // 标记各分类是否已派发过请求,避免切换分类时重复请求
148
+ cateStarted: [],
147149 firstScreenReady: true,
150
+ // 后台加载标记:首屏渲染并关闭loading后,才开始后台加载其余分类
151
+ bgLoaded: false,
148152 // 渲染优化:非首屏分类数据缓冲队列,批量渲染减少setData次数
149153 deferredLists: [],
150154 deferTimer: null,
....@@ -162,6 +166,8 @@
162166 let that = this;
163167 that.menuTop = uni.getMenuButtonBoundingClientRect().top;
164168 that.menuHeight = uni.getMenuButtonBoundingClientRect().height;
169
+ // 尽早计算页面布局高度,避免首屏数据就绪后因 height 为 0 而白屏
170
+ that.infoScroll();
165171 if (that.$store.getters.careBeanRate) {
166172 that.$nextTick(() => {
167173 that.$set(that, 'careBeanRate', that.$store.getters.careBeanRate)
....@@ -176,6 +182,10 @@
176182 });
177183 },
178184 mounted() {},
185
+ onReady() {
186
+ // 页面渲染完成后复核一次布局尺寸(含滚动区域 scrollHeight)
187
+ this.infoScroll();
188
+ },
179189 onHide() {
180190 let that = this;
181191 if (that.$store.getters.isSystemHide) {
....@@ -339,6 +349,10 @@
339349 that.isClicking = true;
340350 that.navActive = index;
341351 that.subActive = 0;
352
+ // loading期间切到尚未发起请求的分类时,立即补发请求,避免loading卡住
353
+ if (!that.cateStarted[index]) {
354
+ that.loadCateByIndex(index);
355
+ }
342356 // loading期间切换分类,若目标分类已加载完成则立即关闭loading
343357 that.checkFirstScreen(index);
344358 that.$nextTick(() => {
....@@ -352,6 +366,10 @@
352366 let that = this;
353367 that.isClicking = true;
354368 that.subActive = index;
369
+ // 当前分类尚未发起请求时补发,避免loading卡住
370
+ if (!that.cateStarted[that.navActive]) {
371
+ that.loadCateByIndex(that.navActive);
372
+ }
355373 that.checkFirstScreen(that.navActive);
356374 that.$nextTick(() => {
357375 that.scrollIntoView = 'list' + that.navActive + '_' + that.subActive;
....@@ -391,23 +409,28 @@
391409 let that = this;
392410 that.firstScreenReady = false;
393411 that.allRequestsDone = false;
412
+ that.bgLoaded = false;
394413 that.deferredLists = [];
395414 if (that.deferTimer) {
396415 clearTimeout(that.deferTimer);
397416 that.deferTimer = null;
398417 }
418
+ // 暂不考虑传入cid的情况:一律从第一个分类开始加载首屏
419
+ that.navActive = 0;
420
+ that.subActive = 0;
399421 uni.showLoading();
400422 getCategoryList().then(res => {
401
- that.categoryList = res.data;
402
- // [P0-优化] 装配阶段为所有分类节点打 _key,供模板 :key 使用
403
- that.ensureCategoryKey(that.categoryList);
423
+ that.categoryList = res.data || [];
404424 // 重置计数,避免重复进入页面时累积
405425 that.totalGetNum = 0;
406426 that.finishNum = 0;
407427 that.catePending = [];
428
+ that.cateStarted = [];
408429 that.categoryList.forEach((item, index) => {
409
-
410430 if (item.child && item.child.length > 0) {
431
+ item.child.sort((a, b) => {
432
+ return a.sort - b.sort;
433
+ });
411434 that.totalGetNum += item.child.length;
412435 that.catePending[index] = item.child.length;
413436 } else {
....@@ -415,113 +438,50 @@
415438 that.catePending[index] = 1;
416439 }
417440 })
418
-
419
- // 先标记当前选中分类,再发起请求
420
- that.categoryList.forEach((item, index) => {
421
- if (that.optionId && that.optionId == item.id) {
422
- that.navActive = index;
423
- that.subActive = 0;
424
- }
425
- if (item.child && item.child.length > 0) {
426
- item.child.sort((a, b) => {
427
- return a.sort - b.sort;
428
- });
429
- item.child.forEach((item2, index2) => {
430
- if (that.optionId && that.optionId == item2.id) {
431
- that.navActive = index;
432
- that.subActive = index2;
433
- }
434
- })
435
- }
441
+ // 分类为空时直接关闭loading,避免一直停留在loading态
442
+ if (!that.categoryList.length) {
443
+ that.firstScreenReady = true;
444
+ that.isLoading = false;
445
+ uni.hideLoading();
446
+ return;
447
+ }
448
+ // 首屏优化:先只请求第一个分类,保证首屏数据最快返回并直接渲染
449
+ that.loadCateByIndex(that.navActive);
450
+ // 其余分类待首屏渲染、loading关闭后,由 loadRestCategory 后台错峰加载
451
+ })
452
+ },
453
+ // 按索引加载某个分类下的商品数据(同一分类只派发一次)
454
+ loadCateByIndex(index) {
455
+ let that = this;
456
+ let item = that.categoryList[index];
457
+ if (!item) return;
458
+ if (that.cateStarted[index]) return;
459
+ that.cateStarted[index] = true;
460
+ if (item.child && item.child.length > 0) {
461
+ item.child.forEach((item2, index2) => {
462
+ that.getAllProductList('isSub', index, index2);
436463 })
437
-
438
- const loadCate = (index) => {
439
- let item = that.categoryList[index];
440
- if (item.child && item.child.length > 0) {
441
- item.child.forEach((item2, index2) => {
442
- that.getAllProductList('isSub', index, index2);
443
- })
444
- } else {
445
- that.getAllProductList(false, index);
446
- }
447
- };
448
- // [P0-优化] 首屏分类(当前navActive):立即并发发起,不走任何队列,最大限度缩短首屏 loading 时长
449
- loadCate(that.navActive);
450
- // [P0-优化] 其余分类:构建任务列表,走下方并发限流池,避免一次性占满 wx.request queue 拖慢首屏
451
- let otherTasks = [];
452
- that.categoryList.forEach((it, idx) => {
453
- if (idx !== that.navActive) otherTasks.push({ index: idx });
454
- });
455
- that.runCategoryTasksWithLimit(otherTasks, 4);
456
-
457
-
458
- })
459
- },
460
- // [P0-优化] 并发限流工具:除首屏分类外,其余分类的请求经由此处发起
461
- runCategoryTasksWithLimit(tasks, limit) {
462
- let that = this;
463
- if (!tasks || !tasks.length) return Promise.resolve();
464
- const queue = tasks.slice();
465
- const executing = [];
466
- const launch = () => {
467
- while (queue.length && executing.length < limit) {
468
- const task = queue.shift();
469
- const item = that.categoryList[task.index];
470
- if (!item) continue;
471
- let promise;
472
- if (item.child && item.child.length > 0) {
473
- const subPromises = [];
474
- item.child.forEach((sub, subIdx) => {
475
- subPromises.push(that.getAllProductList('isSub', task.index, subIdx));
476
- });
477
- promise = Promise.all(subPromises);
478
- } else {
479
- promise = that.getAllProductList(false, task.index);
480
- }
481
- // 用 .catch 包一层避免单个失败拖垮整池
482
- const tracked = promise.catch(() => {});
483
- tracked.finally(() => {
484
- const idx = executing.indexOf(tracked);
485
- if (idx >= 0) executing.splice(idx, 1);
486
- }).catch(() => {});
487
- executing.push(tracked);
488
- }
489
- };
490
- const next = () => {
491
- launch();
492
- if (executing.length === 0) return Promise.resolve();
493
- return Promise.race(executing).then(next, next);
494
- };
495
- return next();
496
- },
497
- // [P0-优化] 为分类节点打 _key:非 H5 平台的 v-for :key 必须为简单变量,
498
- // 故在数据装配阶段预先在 item 上加 _key,模板用 :key="item._key" 引用
499
- ensureCategoryKey: function(list) {
500
- if (!Array.isArray(list)) return;
501
- list.forEach((item, i) => {
502
- if (!item) return;
503
- if (item.id != null) item._key = 'c-' + item.id;
504
- else if (!item._key) item._key = 'c0-' + i;
505
- if (item.child && item.child.length) {
506
- item.child.forEach((sub, j) => {
507
- if (!sub) return;
508
- if (sub.id != null) sub._key = 's-' + sub.id;
509
- else if (!sub._key) sub._key = 's0-' + i + '-' + j;
510
- });
511
- }
512
- });
513
- },
514
- // [P0-优化] 为商品节点打 _key
515
- ensureProductKey: function(list) {
516
- if (!Array.isArray(list)) return;
517
- list.forEach((g, k) => {
518
- if (!g) return;
519
- if (g.id != null && !g._key) g._key = 'p-' + g.id;
520
- else if (!g._key) g._key = 'pk-' + k;
521
- });
522
- },
523
- // 当前分类数据全部就绪后立即关闭全屏loading,不必等待所有分类
524
- checkFirstScreen(index1) {
464
+ } else {
465
+ that.getAllProductList(false, index);
466
+ }
467
+ },
468
+ // 首屏渲染并关闭loading后,后台错峰加载其余分类,避免与首屏抢请求/抢渲染
469
+ loadRestCategory() {
470
+ let that = this;
471
+ if (that.bgLoaded) return;
472
+ that.bgLoaded = true;
473
+ let delay = 0;
474
+ that.categoryList.forEach((item, index) => {
475
+ if (index == that.navActive) return;
476
+ // 每个分类错开触发,逐步在后台铺开,避免一次性铺满请求队列
477
+ delay += 50;
478
+ setTimeout(() => {
479
+ that.loadCateByIndex(index);
480
+ }, delay);
481
+ })
482
+ },
483
+ // 当前分类数据全部就绪后立即关闭全屏loading,不必等待所有分类
484
+ checkFirstScreen(index1) {
525485 let that = this;
526486 if (that.firstScreenReady || index1 != that.navActive) return;
527487 if ((that.catePending[index1] || 0) <= 0) {
....@@ -531,6 +491,8 @@
531491 that.$nextTick(() => {
532492 setTimeout(() => {
533493 uni.hideLoading();
494
+ // 首屏已渲染且loading已关闭,此时才开始后台加载其余分类
495
+ that.loadRestCategory();
534496 }, 50)
535497 })
536498 }
....@@ -600,7 +562,8 @@
600562 updateDimensions() {
601563 const query = uni.createSelectorQuery().in(this);
602564 query.select('.goodlistScroll').boundingClientRect(data => {
603
- this.scrollHeight = data.height;
565
+ // 首屏渲染前该节点可能还不存在,避免报错
566
+ if (data) this.scrollHeight = data.height;
604567 }).exec();
605568 },
606569 handleScroll(e) {
....@@ -631,8 +594,6 @@
631594 cid: isSub ? that.categoryList[index1].child[index2].id : that.categoryList[index1].id,
632595 }).then(res => {
633596 let list = res.data.list;
634
- // [P0-优化] 装配阶段为商品节点打 _key,供模板 :key 使用
635
- that.ensureProductKey(list);
636597 // 当前浏览分类即时渲染;其余分类进入缓冲队列批量渲染,
637598 // 避免几十次$set各自触发一次全量列表渲染
638599 if (index1 == that.navActive && !that.firstScreenReady) {
pages/index/index.vue
....@@ -735,26 +735,7 @@
735735 path: url,
736736 envVersion: miniProgramInfo.envVersion == 'release' ? 'release' : 'trial',
737737 })
738
-
739
- // let params={};
740
- // params['custAgentNo']=((this.$store.getters.belong && this.$store.getters.belong.agentNo) ? this.$store
741
- // .getters.belong.agentNo : '');
742
- // params['custAgentChnl']=((this.$store.getters.belong && this.$store.getters.belong.channel) ? this.$store
743
- // .getters.belong.channel : '');
744
- // params['custAgentCoy']=((this.$store.getters.belong && this.$store.getters.belong.companyCode) ? this
745
- // .$store.getters.belong.companyCode : '');
746
- // params['custAgentBranch']=((this.$store.getters.belong && this.$store.getters.belong.branchCode) ? this
747
- // .$store.getters.belong.branchCode : '');
748
- // params['custAgentOrgNo']=((this.$store.getters.belong && this.$store.getters.belong.subCode) ? this
749
- // .$store.getters.belong.subCode : '');
750
- // let webUrl='https://ossacmall.bluedeer.com.cn/qr/index.html?isMall=true'+(miniProgramInfo.envVersion == 'release' ? '' : '&isTrial=true')+'&params='+encodeURIComponent(JSON.stringify(params))+'&pageUrl='+encodeURIComponent(url);
751
- // if(HTTP_REQUEST_URL.indexOf('tohours')>0){
752
- // webUrl=webUrl.replace('https://ossacmall.bluedeer.com.cn/qr','https://sit.tohours.com/crmeb_web/qr/')
753
- // }
754
- // uni.navigateTo({
755
- // url: '/pages/users/web_page/index?webUel='+encodeURIComponent(webUrl)
756
- // })
757
-
738
+
758739 this.clickTrack('third')
759740 },
760741 goToLink(item, type) {
pages/order_pay_status/index.vue
....@@ -231,28 +231,6 @@
231231 path: url,
232232 envVersion: miniProgramInfo.envVersion == 'release' ? 'release' : 'trial',
233233 })
234
-
235
- // let params = {};
236
- // params['custAgentNo'] = ((this.$store.getters.belong && this.$store.getters.belong.agentNo) ? this.$store
237
- // .getters.belong.agentNo : '');
238
- // params['custAgentChnl'] = ((this.$store.getters.belong && this.$store.getters.belong.channel) ? this.$store
239
- // .getters.belong.channel : '');
240
- // params['custAgentCoy'] = ((this.$store.getters.belong && this.$store.getters.belong.companyCode) ? this
241
- // .$store.getters.belong.companyCode : '');
242
- // params['custAgentBranch'] = ((this.$store.getters.belong && this.$store.getters.belong.branchCode) ? this
243
- // .$store.getters.belong.branchCode : '');
244
- // params['custAgentOrgNo'] = ((this.$store.getters.belong && this.$store.getters.belong.subCode) ? this
245
- // .$store.getters.belong.subCode : '');
246
- // let webUrl = 'https://ossacmall.bluedeer.com.cn/qr/index.html?isMall=true' + (miniProgramInfo.envVersion ==
247
- // 'release' ? '' : '&isTrial=true') + '&params=' + encodeURIComponent(JSON.stringify(params)) +
248
- // '&pageUrl=' + encodeURIComponent(url);
249
- // if (HTTP_REQUEST_URL.indexOf('tohours') > 0) {
250
- // webUrl = webUrl.replace('https://ossacmall.bluedeer.com.cn/qr',
251
- // 'https://sit.tohours.com/crmeb_web/qr/')
252
- // }
253
- // uni.navigateTo({
254
- // url: '/pages/users/web_page/index?webUel=' + encodeURIComponent(webUrl)
255
- // })
256234 },
257235 goToLink(item, type) {
258236 let that = this;
pages/user/index.vue
....@@ -508,14 +508,6 @@
508508 path: url,
509509 envVersion: envVersion != 'release' ? 'trial' : 'release'
510510 })
511
-
512
- // let webUrl='https://ossacmall.bluedeer.com.cn/qr/index.html?isMall=true'+(envVersion == 'release' ? '' : '&isTrial=true')+'&params='+encodeURIComponent(JSON.stringify(params))+'&pageUrl='+encodeURIComponent(url);
513
- // if(HTTP_REQUEST_URL.indexOf('tohours')>0){
514
- // webUrl=webUrl.replace('https://ossacmall.bluedeer.com.cn/qr','https://sit.tohours.com/crmeb_web/qr/')
515
- // }
516
- // uni.navigateTo({
517
- // url: '/pages/users/web_page/index?webUel='+encodeURIComponent(webUrl)
518
- // })
519511 }
520512 if (!that.$store.getters.isLogin) {
521513 toLogin(that);
pages/users/order_confirm/index.vue
....@@ -579,31 +579,6 @@
579579 path: url,
580580 envVersion: miniProgramInfo.envVersion == 'release' ? 'release' : 'trial',
581581 })
582
-
583
- // let params = {};
584
- // params['custAgentNo'] = ((that.$store.getters.belong && that.$store.getters.belong.agentNo) ? that
585
- // .$store
586
- // .getters.belong.agentNo : '');
587
- // params['custAgentChnl'] = ((that.$store.getters.belong && that.$store.getters.belong.channel) ? that
588
- // .$store
589
- // .getters.belong.channel : '');
590
- // params['custAgentCoy'] = ((that.$store.getters.belong && that.$store.getters.belong.companyCode) ? that
591
- // .$store.getters.belong.companyCode : '');
592
- // params['custAgentBranch'] = ((that.$store.getters.belong && that.$store.getters.belong.branchCode) ?
593
- // that
594
- // .$store.getters.belong.branchCode : '');
595
- // params['custAgentOrgNo'] = ((that.$store.getters.belong && that.$store.getters.belong.subCode) ? that
596
- // .$store.getters.belong.subCode : '');
597
- // let webUrl = 'https://ossacmall.bluedeer.com.cn/qr/index.html?isMall=true' + (miniProgramInfo.envVersion == 'release' ?
598
- // '' : '&isTrial=true') + '&params=' + encodeURIComponent(JSON.stringify(params)) + '&pageUrl=' +
599
- // encodeURIComponent(url);
600
- // if (HTTP_REQUEST_URL.indexOf('tohours') > 0) {
601
- // webUrl = webUrl.replace('https://ossacmall.bluedeer.com.cn/qr',
602
- // 'https://sit.tohours.com/crmeb_web/qr/')
603
- // }
604
- // uni.navigateTo({
605
- // url: '/pages/users/web_page/index?webUel=' + encodeURIComponent(webUrl)
606
- // })
607582 }
608583 if (!that.$store.getters.isLogin) {
609584 toLogin(that);
....@@ -1730,7 +1705,7 @@
17301705 box-sizing: border-box;
17311706 padding: 25rpx 25rpx 25rpx 55rpx;
17321707 border-radius: 14rpx;
1733
- background: url("https://ossacmall.bluedeer.com.cn/app/static/images/icon_expressTips.png") 15rpx 25rpx/32rpx auto no-repeat;
1708
+ background: url("https://ossacmall.bluedeer.com.cn/app/static/images/icon_expressTips.png") 15rpx 25rpx / 32rpx auto no-repeat;
17341709 background-color: #fef7ee;
17351710 color: #8e3b1f;
17361711 font-size: 27rpx;
pages/users/user_coupon/index.vue
....@@ -289,28 +289,6 @@
289289 path: url,
290290 envVersion: miniProgramInfo.envVersion == 'release' ? 'release' : 'trial',
291291 })
292
-
293
- // let params = {};
294
- // params['custAgentNo'] = ((this.$store.getters.belong && this.$store.getters.belong.agentNo) ? this.$store
295
- // .getters.belong.agentNo : '');
296
- // params['custAgentChnl'] = ((this.$store.getters.belong && this.$store.getters.belong.channel) ? this.$store
297
- // .getters.belong.channel : '');
298
- // params['custAgentCoy'] = ((this.$store.getters.belong && this.$store.getters.belong.companyCode) ? this
299
- // .$store.getters.belong.companyCode : '');
300
- // params['custAgentBranch'] = ((this.$store.getters.belong && this.$store.getters.belong.branchCode) ? this
301
- // .$store.getters.belong.branchCode : '');
302
- // params['custAgentOrgNo'] = ((this.$store.getters.belong && this.$store.getters.belong.subCode) ? this
303
- // .$store.getters.belong.subCode : '');
304
- // let webUrl = 'https://ossacmall.bluedeer.com.cn/qr/index.html?isMall=true' + (miniProgramInfo.envVersion ==
305
- // 'release' ? '' : '&isTrial=true') + '&params=' + encodeURIComponent(JSON.stringify(params)) +
306
- // '&pageUrl=' + encodeURIComponent(url);
307
- // if (HTTP_REQUEST_URL.indexOf('tohours') > 0) {
308
- // webUrl = webUrl.replace('https://ossacmall.bluedeer.com.cn/qr',
309
- // 'https://sit.tohours.com/crmeb_web/qr/')
310
- // }
311
- // uni.navigateTo({
312
- // url: '/pages/users/web_page/index?webUel=' + encodeURIComponent(webUrl)
313
- // })
314292 },
315293 isTabLink(link) {
316294 let isTab = false;
pages/zone/index.vue
....@@ -14,6 +14,9 @@
1414 <view :style="{ height: marTop + 'px' }"></view>
1515 <yueyangZone ref=yueyangZoneRef v-if="isYueyangZone" :zoneInfo="info" :careBeanRate="careBeanRate"
1616 style="position: relative;z-index: 2;" @goLottery="goLottery" :relationKey="relationKey"></yueyangZone>
17
+ <view v-if="isYueyangZone" class="detail-btn" @click="goDetail">
18
+ <text>消耗明细</text>
19
+ </view>
1720 <view v-else>
1821 <view class="expireTime" v-if="expireTime && expireTime.length">
1922 <text>专区将在</text>
....@@ -495,7 +498,7 @@
495498
496499 <script>
497500 import {
498
- HTTP_REQUEST_URL
501
+ WEB_ACTIVITY_BASE
499502 } from '@/config/app';
500503 import {
501504 mapGetters
....@@ -1195,6 +1198,11 @@
11951198 // that.myCareBean = res2.data.totalCareBeans;
11961199 });
11971200 },
1201
+ goDetail() {
1202
+ uni.navigateTo({
1203
+ url: '/pages/consumption/index?zoneId=' + this.info.id
1204
+ })
1205
+ },
11981206 goLottery() {
11991207 let that = this;
12001208 var data = {
....@@ -1206,11 +1214,7 @@
12061214 this.addTrack('yy_marketPageClick', data);
12071215
12081216 function go() {
1209
- let webUrl = 'https://ossacmall.bluedeer.com.cn/acmall_lottery/index.html'
1210
- if (HTTP_REQUEST_URL.indexOf('tohours') > 0) {
1211
- webUrl = webUrl.replace('https://ossacmall.bluedeer.com.cn',
1212
- 'https://sit.tohours.com/crmeb_web')
1213
- }
1217
+ let webUrl = `${WEB_ACTIVITY_BASE}/acmall_lottery/index.html`
12141218 webUrl += '?token=' +
12151219 that.$store.getters.token + '&unionId=' + that.$store.getters.unionId + '&mdayId=' + that.info
12161220 .mdayId
....@@ -1840,6 +1844,21 @@
18401844 transform: translate(0, -50%);
18411845 }
18421846
1847
+.detail-btn {
1848
+ border: 2rpx solid #fff;
1849
+ border-top-left-radius: 20rpx;
1850
+ border-bottom-left-radius: 20rpx;
1851
+ padding: 10rpx;
1852
+ background-color: #daf2b2;
1853
+ color: #1e984f;
1854
+ width: 50rpx;
1855
+ position: fixed;
1856
+ top: 200rpx;
1857
+ right: 0rpx;
1858
+ line-height: 1rem;
1859
+ z-index: 99;
1860
+}
1861
+
18431862 .head_img {
18441863 width: 750rpx;
18451864 height: 422rpx;
project.config.json
deleted file mode 100644
....@@ -1,29 +0,0 @@
1
-{
2
- "appid": "wx15d029cdf2e92a9f",
3
- "compileType": "miniprogram",
4
- "libVersion": "3.1.4",
5
- "packOptions": {
6
- "ignore": [],
7
- "include": []
8
- },
9
- "setting": {
10
- "coverView": true,
11
- "es6": true,
12
- "postcss": true,
13
- "minified": true,
14
- "enhance": true,
15
- "showShadowRootInWxmlPanel": true,
16
- "packNpmRelationList": [],
17
- "babelSetting": {
18
- "ignore": [],
19
- "disablePlugins": [],
20
- "outputPath": ""
21
- },
22
- "condition": false
23
- },
24
- "condition": {},
25
- "editorSetting": {
26
- "tabIndent": "insertSpaces",
27
- "tabSize": 2
28
- }
29
-}
publish-prod.bat
....@@ -0,0 +1,12 @@
1
+@echo off
2
+chcp 65001 >nul
3
+cd /d "%~dp0"
4
+node "scripts\publish.js" prod %*
5
+if errorlevel 1 (
6
+ echo.
7
+ echo [FAILED] publish PROD failed - see output above
8
+) else (
9
+ echo.
10
+ echo [OK] PROD finished - see output above. Release dir: unpackage\dist\build\mp-weixin
11
+)
12
+pause
publish-sit.bat
....@@ -0,0 +1,12 @@
1
+@echo off
2
+chcp 65001 >nul
3
+cd /d "%~dp0"
4
+node "scripts\publish.js" sit %*
5
+if errorlevel 1 (
6
+ echo.
7
+ echo [FAILED] publish SIT failed - see output above
8
+) else (
9
+ echo.
10
+ echo [OK] SIT finished - see output above. Release dir: unpackage\dist\build\mp-weixin
11
+)
12
+pause
scripts/manifest.js
....@@ -0,0 +1,122 @@
1
+/**
2
+ * manifest.json 生成器
3
+ *
4
+ * manifest.json 是 HBuilderX 的编译输入,但内含环境相关 appid,不适合直接提交;
5
+ * 因此模板入库、生成物忽略:
6
+ *
7
+ * manifest.json.demo(入库,模板) ──注入环境 appid──▶ manifest.json(.gitignore)
8
+ *
9
+ * appid 数据源唯一:config/env/<env>.js 的 UNI_APPID(DCloud 标识)、APPID(微信小程序 appid)
10
+ * 说明:模板里 mp-weixin 的 appid 允许写成 null 或不存在,生成时会自动写入/补齐。
11
+ */
12
+const fs = require('fs');
13
+const path = require('path');
14
+
15
+const ROOT = path.resolve(__dirname, '..');
16
+const TEMPLATE = path.join(ROOT, 'manifest.json.demo');
17
+const TARGET = path.join(ROOT, 'manifest.json');
18
+
19
+function fail(msg) {
20
+ console.error('[manifest] ' + msg);
21
+ process.exit(1);
22
+}
23
+
24
+/** 只替换第一处 "appid" : <字符串或 null>,保留原有的空格/缩进风格 */
25
+function replaceAppId(text, value) {
26
+ let done = false;
27
+ const result = text.replace(/"appid"(\s*:\s*)(?:"[^"]*"|null)/, function(match, sep) {
28
+ done = true;
29
+ return '"appid"' + sep + '"' + value + '"';
30
+ });
31
+ return { text: result, done: done };
32
+}
33
+
34
+/** 注入两个 appid:顶层(DCloud 标识)+ mp-weixin 块内(微信小程序 appid) */
35
+function injectAppIds(text, env) {
36
+ // 1) 顶层 appid(第一处出现即为顶层)
37
+ const top = replaceAppId(text, env.UNI_APPID);
38
+ if (!top.done) fail('模板中未找到顶层 "appid" 字段:' + path.relative(ROOT, TEMPLATE));
39
+ text = top.text;
40
+
41
+ // 2) mp-weixin 下的 appid,只在该区块内替换第一处
42
+ const idx = text.indexOf('"mp-weixin"');
43
+ if (idx < 0) fail('模板中未找到 "mp-weixin" 配置块:' + path.relative(ROOT, TEMPLATE));
44
+ const head = text.slice(0, idx);
45
+ const body = text.slice(idx);
46
+ const scoped = replaceAppId(body, env.APPID);
47
+ if (scoped.done) return head + scoped.text;
48
+
49
+ // 3) 字段被删掉时,按同级子项缩进补一行
50
+ const m = body.match(/^("mp-weixin"\s*:\s*\{)(\r?\n)([ \t]*)/);
51
+ if (!m) fail('模板的 "mp-weixin" 块格式异常,无法写入 appid');
52
+ const nl = m[2];
53
+ const indent = m[3];
54
+ return head + m[1] + nl + indent + '"appid" : "' + env.APPID + '",' + nl + indent + body.slice(m[0].length);
55
+}
56
+
57
+/** 统计「除 appid 行以外」的差异(按行内容比较,不受行号位移影响) */
58
+function otherDiff(a, b) {
59
+ function count(text) {
60
+ const m = new Map();
61
+ text.split(/\r?\n/).forEach(function(line) {
62
+ const l = line.replace(/\s+$/, '');
63
+ if (/^\s*"appid"\s*:/.test(l.trim())) return;
64
+ m.set(l, (m.get(l) || 0) + 1);
65
+ });
66
+ return m;
67
+ }
68
+ const ma = count(a);
69
+ const mb = count(b);
70
+ const keys = new Set(Array.from(ma.keys()).concat(Array.from(mb.keys())));
71
+ let diff = 0;
72
+ const sample = [];
73
+ keys.forEach(function(k) {
74
+ const d = Math.abs((ma.get(k) || 0) - (mb.get(k) || 0));
75
+ if (!d) return;
76
+ diff += d;
77
+ if (sample.length < 3 && k.trim()) sample.push(k.trim().slice(0, 40));
78
+ });
79
+ return { count: diff, sample: sample };
80
+}
81
+
82
+/**
83
+ * 按环境生成 manifest.json
84
+ * @param {object} env config/env/<env>.js 导出的配置
85
+ * @returns {{ file: string, changed: boolean }}
86
+ */
87
+function generate(env) {
88
+ if (!fs.existsSync(TEMPLATE)) {
89
+ fail('未找到模板 ' + path.relative(ROOT, TEMPLATE) +
90
+ '\n 请把 manifest.json 的模板保存为 manifest.json.demo(需入库)后再生成');
91
+ }
92
+
93
+ const rendered = injectAppIds(fs.readFileSync(TEMPLATE, 'utf8'), env);
94
+ const exists = fs.existsSync(TARGET);
95
+
96
+ if (exists) {
97
+ const prev = fs.readFileSync(TARGET, 'utf8');
98
+ // 行尾差异(CRLF/LF)不算变化,避免无意义重写
99
+ if (prev.replace(/\r\n/g, '\n') === rendered.replace(/\r\n/g, '\n')) {
100
+ console.log('[manifest] manifest.json 已是目标内容,无需重写');
101
+ return { file: TARGET, changed: false };
102
+ }
103
+ const diffs = otherDiff(prev, rendered);
104
+ if (diffs.count) {
105
+ console.log('[manifest] 注意:现有 manifest.json 有 ' + diffs.count + ' 行内容与模板不同,本次会被模板覆盖' +
106
+ (diffs.sample.length ? '(如 ' + diffs.sample.join(' | ') + ')' : ''));
107
+ console.log('[manifest] 需要长期保留的改动请写进 manifest.json.demo');
108
+ }
109
+ }
110
+
111
+ fs.writeFileSync(TARGET, rendered, 'utf8');
112
+ console.log('[manifest] 已由 manifest.json.demo 生成 manifest.json' +
113
+ '(DCloud ' + env.UNI_APPID + ' / 小程序 ' + env.APPID + ')');
114
+ return { file: TARGET, changed: true };
115
+}
116
+
117
+module.exports = {
118
+ TEMPLATE: TEMPLATE,
119
+ TARGET: TARGET,
120
+ generate: generate,
121
+ injectAppIds: injectAppIds
122
+};
scripts/publish.js
....@@ -0,0 +1,312 @@
1
+/**
2
+ * 命令行发行(不上传)+ 自动用微信开发者工具打开产物
3
+ *
4
+ * 用法:
5
+ * node scripts/publish.js sit # 发行 SIT 版本并打开微信开发者工具
6
+ * node scripts/publish.js prod # 发行 PROD 版本并打开微信开发者工具
7
+ * node scripts/publish.js sit --no-gen # 不改 manifest.json(要求它已存在)
8
+ * node scripts/publish.js sit --no-open # 只编译,不打开微信开发者工具
9
+ * node scripts/publish.js sit --dry-run # 只打印将要执行的命令
10
+ *
11
+ * 流程:从 manifest.json.demo 生成 manifest.json(按目标环境注入 appid)→ 发行编译 →
12
+ * 校正产物 appid → 用微信开发者工具打开产物
13
+ *
14
+ * 环境值统一取自 config/env/<env>.js(唯一数据源),接口地址等运行时参数由包内按 appid 自动识别;
15
+ * manifest.json 是生成物(已 .gitignore),因此 git 永远没有环境噪音。
16
+ * 微信开发者工具打开的产物 appid = unpackage/dist/build/mp-weixin/project.config.json 的 appid
17
+ * (构建时由 manifest 生成,这里再兜底校正一次)。
18
+ *
19
+ * 外部工具(HBuilderX cli.exe、微信开发者工具 cli.bat)不写死绝对路径,按顺序自动查找:
20
+ * 1) 环境变量 HBUILDERX_CLI / WX_DEVTOOLS_CLI
21
+ * 2) 系统 PATH
22
+ * 3) 常见安装目录(Program Files、用户目录、各磁盘常见软件目录)
23
+ * 4) 磁盘根目录浅层扫描(≤2 层)
24
+ * 找到后会把其所在目录加入本次进程 PATH(便于按名字直接调用),找不到则报错并给出解决办法。
25
+ */
26
+const { spawnSync } = require('child_process');
27
+const fs = require('fs');
28
+const path = require('path');
29
+const manifest = require('./manifest');
30
+
31
+const ROOT = path.resolve(__dirname, '..');
32
+const ENV_DIR = path.join(ROOT, 'config', 'env');
33
+const DIST_DIR = path.join(ROOT, 'unpackage', 'dist', 'build', 'mp-weixin');
34
+const DIST_CONFIG = path.join(DIST_DIR, 'project.config.json');
35
+
36
+/* ------------------------- 外部工具定位 ------------------------- */
37
+
38
+// 扫描时要跳过的系统/无关目录
39
+const SKIP_DIRS = /^(\$recycle\.bin|system volume information|windows|winsxs|node_modules|\.git|\.cache|programdata)$/i;
40
+
41
+const HX_TOOL = {
42
+ envKey: 'HBUILDERX_CLI',
43
+ label: 'HBuilderX 命令行 cli.exe',
44
+ names: ['cli.exe'],
45
+ subDirs: ['HBuilderX', 'HBuilderX\\HBuilderX'],
46
+ dirPattern: /^hbuilderx/i,
47
+ ok: function(file) { return /hbuilderx/i.test(file); },
48
+ hint: 'HBuilderX 安装/解压目录下的 cli.exe(如 X:\\HBuilderX\\cli.exe)',
49
+ extra: ''
50
+};
51
+
52
+const WX_TOOL = {
53
+ envKey: 'WX_DEVTOOLS_CLI',
54
+ label: '微信开发者工具命令行 cli.bat',
55
+ names: ['cli.bat'],
56
+ subDirs: ['微信web开发者工具', '微信开发者工具', 'Tencent\\微信web开发者工具', 'Tencent\\微信开发者工具'],
57
+ dirPattern: /微信(web)?开发者工具/,
58
+ hint: '微信开发者工具安装目录下的 cli.bat(如 X:\\Program Files (x86)\\Tencent\\微信web开发者工具\\cli.bat)',
59
+ extra: ' 3) 或加 --no-open,只编译不自动打开产物\n'
60
+};
61
+
62
+function fail(msg) {
63
+ console.error('[release] ' + msg);
64
+ process.exit(1);
65
+}
66
+
67
+function uniq(list) {
68
+ return list.filter(function(v, i) { return v && list.indexOf(v) === i; });
69
+}
70
+
71
+function isFile(p) {
72
+ try { return fs.statSync(p).isFile(); } catch (e) { return false; }
73
+}
74
+
75
+function isDir(p) {
76
+ try { return fs.statSync(p).isDirectory(); } catch (e) { return false; }
77
+}
78
+
79
+/** 本地固定磁盘(C:、D: …);拿不到时退回环境变量里的盘符(结果缓存,只探测一次) */
80
+let DRIVES = null;
81
+function fixedDrives() {
82
+ if (DRIVES) return DRIVES;
83
+ const ps = spawnSync('powershell', ['-NoProfile', '-NonInteractive', '-Command',
84
+ "[System.IO.DriveInfo]::GetDrives() | Where-Object { $_.DriveType -eq 'Fixed' } | ForEach-Object { $_.Name }"
85
+ ], { encoding: 'utf8', windowsHide: true });
86
+ const fromPs = (ps.stdout || '').split(/\r?\n/)
87
+ .map(function(s) { return s.trim().replace(/\\$/, ''); })
88
+ .filter(function(s) { return /^[A-Za-z]:$/.test(s); });
89
+ const fromEnv = [process.env.SystemDrive, process.env.HOMEDRIVE];
90
+ DRIVES = uniq(fromPs.concat(fromEnv).map(function(d) { return (d || '').replace(/\\$/, ''); }));
91
+ return DRIVES;
92
+}
93
+
94
+/** 在系统 PATH 中按文件名查找,ok() 用于排除同名无关程序 */
95
+function findOnPath(names, ok) {
96
+ const dirs = (process.env.PATH || '').split(path.delimiter);
97
+ for (let i = 0; i < dirs.length; i++) {
98
+ const dir = dirs[i].replace(/^"|"$/g, '');
99
+ if (!dir) continue;
100
+ for (let j = 0; j < names.length; j++) {
101
+ const file = path.join(dir, names[j]);
102
+ if (isFile(file) && (!ok || ok(file))) return file;
103
+ }
104
+ }
105
+ return '';
106
+}
107
+
108
+/** 常见安装根目录:Program Files、用户目录、各磁盘根与常见软件目录 */
109
+function baseDirs() {
110
+ const bases = [
111
+ process.env.ProgramFiles,
112
+ process.env['ProgramFiles(x86)'],
113
+ process.env.LOCALAPPDATA,
114
+ process.env.APPDATA,
115
+ process.env.USERPROFILE,
116
+ process.env.LOCALAPPDATA && path.join(process.env.LOCALAPPDATA, 'Programs'),
117
+ process.env.USERPROFILE && path.join(process.env.USERPROFILE, 'Desktop'),
118
+ process.env.USERPROFILE && path.join(process.env.USERPROFILE, 'Downloads')
119
+ ];
120
+ fixedDrives().forEach(function(d) {
121
+ bases.push(d + '\\', d + '\\Program Files', d + '\\Program Files (x86)',
122
+ d + '\\soft', d + '\\software', d + '\\Programs', d + '\\develop');
123
+ });
124
+ return uniq(bases).filter(isDir);
125
+}
126
+
127
+/** 磁盘浅层扫描:找名字符合特征的目录(兜底手段) */
128
+function scanPlausibleDirs(pattern) {
129
+ const found = [];
130
+ fixedDrives().forEach(function(root) {
131
+ let level1 = [];
132
+ try { level1 = fs.readdirSync(root + '\\', { withFileTypes: true }); } catch (e) { return; }
133
+ level1.forEach(function(e1) {
134
+ if (!e1.isDirectory() || SKIP_DIRS.test(e1.name)) return;
135
+ const p1 = path.join(root + '\\', e1.name);
136
+ if (pattern.test(e1.name)) { found.push(p1); return; }
137
+ let level2 = [];
138
+ try { level2 = fs.readdirSync(p1, { withFileTypes: true }); } catch (e) { return; }
139
+ level2.forEach(function(e2) {
140
+ if (e2.isDirectory() && pattern.test(e2.name)) found.push(path.join(p1, e2.name));
141
+ });
142
+ });
143
+ });
144
+ return found;
145
+}
146
+
147
+/** 定位工具:返回 { file, source },找不到返回 null */
148
+function resolveTool(cfg) {
149
+ // 1) 环境变量显式指定
150
+ const fromEnv = (process.env[cfg.envKey] || '').trim();
151
+ if (fromEnv) {
152
+ if (isFile(fromEnv)) return { file: fromEnv, source: '环境变量 ' + cfg.envKey };
153
+ fail('环境变量 ' + cfg.envKey + ' 指向的文件不存在:' + fromEnv);
154
+ }
155
+
156
+ // 2) 系统 PATH
157
+ const onPath = findOnPath(cfg.names, cfg.ok);
158
+ if (onPath) return { file: onPath, source: '系统 PATH' };
159
+
160
+ // 3) 常见安装目录 + 磁盘浅层扫描
161
+ const dirs = baseDirs().concat(scanPlausibleDirs(cfg.dirPattern));
162
+ for (let i = 0; i < dirs.length; i++) {
163
+ for (let j = 0; j < cfg.subDirs.length; j++) {
164
+ for (let k = 0; k < cfg.names.length; k++) {
165
+ const file = path.join(dirs[i], cfg.subDirs[j], cfg.names[k]);
166
+ if (isFile(file)) return { file: file, source: '常见安装目录' };
167
+ }
168
+ }
169
+ }
170
+ return null;
171
+}
172
+
173
+/** 找不到工具时的报错提示 */
174
+function toolFail(cfg) {
175
+ fail('未找到 ' + cfg.label + '\n' +
176
+ ' 已查找:环境变量 ' + cfg.envKey + ' → 系统 PATH → 常见安装目录 → 磁盘浅层扫描\n' +
177
+ ' 典型位置:' + cfg.hint + '\n' +
178
+ ' 解决办法(任选其一):\n' +
179
+ ' 1) 把安装目录加入 PATH:set PATH=%PATH%;<安装目录>\n' +
180
+ ' 2) 用环境变量指定:set ' + cfg.envKey + '=<cli 完整路径>\n' +
181
+ cfg.extra);
182
+}
183
+
184
+/** 把工具所在目录加入本次进程 PATH,便于按名字直接调用 */
185
+function putOnPath(file) {
186
+ const dir = path.dirname(file);
187
+ const cur = process.env.PATH || '';
188
+ if (cur.split(path.delimiter).indexOf(dir) < 0) process.env.PATH = dir + path.delimiter + cur;
189
+}
190
+
191
+/* ------------------------- 参数解析 ------------------------- */
192
+
193
+const argv = process.argv.slice(2);
194
+const envName = (argv.find(function(a) { return a.charAt(0) !== '-'; }) || '').trim();
195
+const noGen = argv.indexOf('--no-gen') > -1;
196
+const noOpen = argv.indexOf('--no-open') > -1;
197
+const dryRun = argv.indexOf('--dry-run') > -1;
198
+
199
+const OWN_ARGS = ['--no-gen', '--no-open', '--dry-run'];
200
+// 透传给 HBuilderX cli 的额外参数(保留 "参数 值" 成对关系)
201
+const passthrough = [];
202
+let envNameSkipped = false;
203
+argv.forEach(function(a) {
204
+ if (OWN_ARGS.indexOf(a) > -1) return;
205
+ if (!envNameSkipped && a === envName) {
206
+ envNameSkipped = true;
207
+ return;
208
+ }
209
+ passthrough.push(a);
210
+});
211
+
212
+function readAppId(file) {
213
+ try {
214
+ return JSON.parse(fs.readFileSync(file, 'utf8')).appid || '';
215
+ } catch (e) {
216
+ return '';
217
+ }
218
+}
219
+
220
+/** 回写 appid(正则替换,保留原格式) */
221
+function writeAppId(file, appid) {
222
+ const prev = fs.readFileSync(file, 'utf8');
223
+ const next = prev.replace(/"appid"(\s*:\s*)"[^"]*"/, function(m, sep) {
224
+ return '"appid"' + sep + '"' + appid + '"';
225
+ });
226
+ if (next !== prev) fs.writeFileSync(file, next, 'utf8');
227
+ return readAppId(file);
228
+}
229
+
230
+function main() {
231
+ const available = fs.existsSync(ENV_DIR)
232
+ ? fs.readdirSync(ENV_DIR).filter(f => f.endsWith('.js') && f !== 'index.js').map(f => f.replace(/\.js$/, ''))
233
+ : [];
234
+
235
+ if (!envName) fail('请指定环境名,可选:' + available.join(' / ') + '\n 例:node scripts/publish.js sit');
236
+ if (available.indexOf(envName) < 0) fail('未找到 config/env/' + envName + '.js,可选:' + available.join(' / '));
237
+
238
+ const env = require(path.join(ENV_DIR, envName + '.js'));
239
+
240
+ // 定位外部工具(不写死路径)
241
+ console.log('[release] 查找 HBuilderX / 微信开发者工具…');
242
+ const hx = resolveTool(HX_TOOL);
243
+ if (!hx) toolFail(HX_TOOL);
244
+ const wx = noOpen ? null : resolveTool(WX_TOOL);
245
+ if (!noOpen && !wx) toolFail(WX_TOOL);
246
+ putOnPath(hx.file);
247
+ if (wx) putOnPath(wx.file);
248
+
249
+ // 目标编译产物由 manifest.json 决定,而 manifest.json 是生成物
250
+ if (noGen && !fs.existsSync(manifest.TARGET)) {
251
+ fail('--no-gen 需要 manifest.json 已存在,请先运行:node scripts/set-env.js ' + envName);
252
+ }
253
+
254
+ console.log('[release] 环境 : ' + envName + '(' + env.ENV_NAME + ')');
255
+ console.log('[release] 小程序appid : ' + env.APPID);
256
+ console.log('[release] 接口地址 : ' + env.HTTP_REQUEST_URL);
257
+ console.log('[release] 产物目录 : ' + DIST_DIR);
258
+ console.log('[release] HBuilderX : ' + hx.file + '(' + hx.source + ')');
259
+ if (wx) console.log('[release] 开发者工具 : ' + wx.file + '(' + wx.source + ')');
260
+ console.log('[release] manifest.json: ' + (noGen
261
+ ? '不改动(--no-gen)'
262
+ : '由 manifest.json.demo 生成(appid ' + env.APPID + ',生成物不入库)'));
263
+
264
+ const cliArgs = ['publish', '--platform', 'mp-weixin', '--project', ROOT].concat(passthrough);
265
+ // 路径含空格/中文,交给 cmd 解析更稳(--no-open 时没有微信开发者工具,不拼这条命令)
266
+ const openCmd = wx ? '"' + wx.file + '" open --project "' + DIST_DIR + '"' : '';
267
+
268
+ if (dryRun) {
269
+ console.log('[release] 0) 生成: manifest.json ← manifest.json.demo' + (noGen ? '(已指定 --no-gen,跳过)' : ''));
270
+ console.log('[release] 1) 编译: "' + hx.file + '" ' + cliArgs.join(' '));
271
+ console.log('[release] 2) 产物 appid 回写为 ' + env.APPID + ' → ' + DIST_CONFIG);
272
+ if (!noOpen) console.log('[release] 3) 打开: ' + openCmd);
273
+ console.log('[release] --dry-run,未执行');
274
+ return;
275
+ }
276
+
277
+ // 1) 生成 manifest.json(HBuilderX 的编译输入,模板:manifest.json.demo)
278
+ if (noGen) {
279
+ console.log('[release] 跳过 manifest.json 生成(--no-gen)');
280
+ } else {
281
+ manifest.generate(env);
282
+ }
283
+
284
+ // 2) 发行编译(产物:unpackage/dist/build/mp-weixin)
285
+ console.log('[release] 开始发行编译,日志见 HBuilderX 控制台…');
286
+ const build = spawnSync(hx.file, cliArgs, { cwd: ROOT, stdio: 'inherit' });
287
+ if (build.error) fail('调用 cli.exe 失败:' + build.error.message);
288
+ if (build.status !== 0) fail('cli.exe 退出码 ' + build.status + ',请查看 HBuilderX 控制台输出');
289
+
290
+ // 3) 校正产物 appid(正常应与 manifest 一致,这里兜底保证)
291
+ if (!fs.existsSync(DIST_CONFIG)) {
292
+ fail('发行产物缺少 project.config.json:' + DIST_CONFIG + '\n 请确认 HBuilderX 编译是否成功');
293
+ }
294
+ const before = readAppId(DIST_CONFIG);
295
+ const after = writeAppId(DIST_CONFIG, env.APPID);
296
+ if (!after) fail('回写产物 appid 失败:' + DIST_CONFIG);
297
+ console.log('[release] 产物 appid : ' + before + (before === after ? '(无需调整)' : ' → ' + after));
298
+
299
+ // 4) 用微信开发者工具打开产物(首次需在「设置-安全设置」中开启服务端口)
300
+ if (!noOpen) {
301
+ console.log('[release] 打开微信开发者工具…');
302
+ const open = spawnSync(openCmd, { cwd: ROOT, stdio: 'inherit', shell: true });
303
+ if (open.error || open.status !== 0) {
304
+ console.log('[release] 自动打开失败(常见原因:未开启服务端口、微信开发者工具版本较旧)');
305
+ console.log('[release] 请手动打开微信开发者工具 → 导入项目 → 选择目录:' + DIST_DIR);
306
+ }
307
+ }
308
+
309
+ console.log('[release] 完成。微信开发者工具中的项目 appid = ' + after + ',包内会自动使用 ' + env.HTTP_REQUEST_URL);
310
+}
311
+
312
+main();
scripts/set-env.js
....@@ -0,0 +1,61 @@
1
+/**
2
+ * 环境切换:按 config/env/<env>.js 从模板生成 manifest.json
3
+ *
4
+ * 用法:
5
+ * npm run env:sit 等价于 node scripts/set-env.js sit
6
+ * npm run env:prod 等价于 node scripts/set-env.js prod
7
+ *
8
+ * 说明:
9
+ * manifest.json 是 HBuilderX 的编译输入(appid 属于编译期值,无法像接口地址那样运行时切换),
10
+ * 但它含环境相关 appid,不适合直接提交,所以:
11
+ * manifest.json.demo(入库模板) --生成--> manifest.json(已 .gitignore)
12
+ * 生成后必须重新编译一次,并在微信开发者工具中重新打开项目(appid 变更需重开)。
13
+ *
14
+ * 如需在 HBuilderX 里直接编译/运行调试,先跑一次本脚本;
15
+ * 走命令行发行则直接用 scripts/publish.js(内部会自动生成)。
16
+ *
17
+ * appid 数据源唯一:config/env/<env>.js 中的 UNI_APPID(DCloud 标识)、APPID(小程序 appid)
18
+ */
19
+const fs = require('fs');
20
+const path = require('path');
21
+const manifest = require('./manifest');
22
+
23
+const ROOT = path.resolve(__dirname, '..');
24
+const ENV_DIR = path.join(ROOT, 'config', 'env');
25
+
26
+function fail(msg) {
27
+ console.error('[set-env] ' + msg);
28
+ process.exit(1);
29
+}
30
+
31
+function main() {
32
+ const envName = (process.argv[2] || '').trim();
33
+ const available = fs.existsSync(ENV_DIR)
34
+ ? fs.readdirSync(ENV_DIR).filter(f => f.endsWith('.js') && f !== 'index.js').map(f => f.replace(/\.js$/, ''))
35
+ : [];
36
+
37
+ if (!envName) {
38
+ fail('请指定环境名,可选:' + available.join(' / ') + '\n 例:npm run env:sit');
39
+ }
40
+ if (available.indexOf(envName) < 0) {
41
+ fail('未找到 config/env/' + envName + '.js,可选:' + available.join(' / '));
42
+ }
43
+
44
+ const env = require(path.join(ENV_DIR, envName + '.js'));
45
+ if (!env.UNI_APPID || !env.APPID) {
46
+ fail('config/env/' + envName + '.js 缺少 UNI_APPID 或 APPID');
47
+ }
48
+
49
+ console.log('[set-env] 切换到 ' + envName + '(' + env.ENV_NAME + ')');
50
+ console.log(' DCloud appid : ' + env.UNI_APPID);
51
+ console.log(' 小程序 appid : ' + env.APPID);
52
+ console.log(' 接口地址 : ' + env.HTTP_REQUEST_URL);
53
+
54
+ const res = manifest.generate(env);
55
+
56
+ console.log(res.changed
57
+ ? '[set-env] 完成,请在 HBuilderX 中重新编译一次,并在微信开发者工具中重新打开项目'
58
+ : '[set-env] 无需改动,当前 manifest.json 已是 ' + envName + ' 环境');
59
+}
60
+
61
+main();
switch-prod.bat
....@@ -0,0 +1,12 @@
1
+@echo off
2
+chcp 65001 >nul
3
+cd /d "%~dp0"
4
+node "scripts\set-env.js" prod
5
+if errorlevel 1 (
6
+ echo.
7
+ echo [FAILED] switch to PROD failed - see output above
8
+) else (
9
+ echo.
10
+ echo [OK] PROD manifest generated. Recompile in HBuilderX, then reopen the project in WeChat DevTools.
11
+)
12
+pause
switch-sit.bat
....@@ -0,0 +1,12 @@
1
+@echo off
2
+chcp 65001 >nul
3
+cd /d "%~dp0"
4
+node "scripts\set-env.js" sit
5
+if errorlevel 1 (
6
+ echo.
7
+ echo [FAILED] switch to SIT failed - see output above
8
+) else (
9
+ echo.
10
+ echo [OK] SIT manifest generated. Recompile in HBuilderX, then reopen the project in WeChat DevTools.
11
+)
12
+pause
需要修改的配置文件.txt
deleted file mode 100644
....@@ -1,19 +0,0 @@
1
-/***
2
-需要修改的配置文件
3
-***/
4
-
5
------app/manifest.json--------
6
- appid: wx15d029cdf2e92a9f
7
-
8
-
9
------app/config/app.js-------
10
-
11
- HTTP_REQUEST_URL:'https://acmall.bluedeer.com.cn/crmeb/front',
12
- HTTP_ADMIN_URL:'https://acmall.bluedeer.com.cn/crmeb/admin',
13
- APPID:'wx15d029cdf2e92a9f',
14
- GDP_ACCOUNT_ID:'8672f93377575ace',
15
- GDP_DATASOURCE_ID:'b01182b2d4f73e45',
16
- GDP_SERVER_URL:'https://giocollect.aviva-cofco.com.cn',
17
- GDP_DEBUG:true,
18
- THIRD_APPID:'wxa83daa550324ead8',
19
-