完善OSS/CDN,私密OSS,商家入驻,客服

This commit is contained in:
Daniel Kou 2026-06-05 14:47:48 +08:00
parent 5fc340a47a
commit fa8d4cc38d
24 changed files with 3771 additions and 501 deletions

View File

@ -2,6 +2,7 @@ export const apiArr = {
getCateList: "/api/v2/wechat/merchant-cate-crud/list", //商家分类列表
getMerchantList: "/api/v2/wechat/merchant-info-crud/page", //商家列表
getHomeBanner: "/api/v2/wechat/home-banner-region-crud/page", //获取首页banner及其广告
getHomeBannerMulti: "/api/v2/wechat/home-banner-region-crud/multi", //按广告位批量获取首页广告(一次返回多个 ad_position
getButtonNum:"/api/v2/wechat/nav-display-crud/info",//获取首页button的行数 数量
getHomeButton:"/api/v2/wechat/home-button-region-crud/page", //获取首页button

View File

@ -3,6 +3,7 @@ export const apiArr = {
getMerchantList:"/api/v2/wechat/merchant-info-crud/page",//商家列表
createComment:"/api/v2/wechat/merchant-evaluation-crud/creat",//创建用户评价
getMerchantInfo:"/api/v2/wechat/merchant-info-crud/info",//获取商家信息详情
getShopActivityList:"/api/v2/wechat/commodity/shop-activity/list",//好店详情页-店铺活动商品列表
getMerchantComment:"/api/v2/wechat/merchant-evaluation-crud/page",//获取商家评价
merChantCommentLike:"/api/v2/wechat/merchant-evaluation-like-crud/creat",//点赞
merChantCommentUnlike:"/api/v2/wechat/merchant-evaluation-like-crud/del",//取消点赞
@ -26,4 +27,5 @@ export const apiArr = {
createStore:"/api/v2/wechat/store-info-crud/creat",//门店信息创建
ocrRecognize:"/api/v1/wechat/oss/ocr-recognize",//证件 OCR 自动识别
};

View File

@ -0,0 +1,84 @@
# 首页 Banner / 广告位 跳转现状梳理
> 状态现状分析仅梳理代码未改。日期2026-06-03
> 范围:小程序 `pages/index/index.vue` 首页各广告位的点击跳转逻辑,对照后端配置。
---
## 一、后端是怎么配置跳转的
每条 banner/广告(表 `home_banner_region`)、金刚位(表 `home_button_region`)都有统一的跳转配置字段,核心是 **`promotion_type`(推广类型)**
| promotion_type | 含义 | 配套字段 |
|---|---|---|
| 1 | 无跳转 | — |
| 2 | 选择活动 | 关联 `advertisement_goods`(按 banner id |
| 3 | 跳转本程序页面 | `link_url` |
| 4 | 跳转其他小程序 | `appid` + `link_url` |
| 5 | 选择商品 | `advertisement_goods[].goods_id` |
- 后台创建/编辑接口(`api/admin/v2/crud_home_banner_region.go`)用 `promotion_type` + `required-if` 校验对应字段,例如 type=4 必填 appid、type=3/4 必填 link_url。
- `home_button_region`(金刚位)字段相同,也有 `promotion_type` / `link_url` / `appid`
- type=2/5 的落地:`/packages/advertising/index/index?id=<banner.id>` 是“广告聚合落地页”,传 banner 自身 id 进去,页面内部拉该 banner 关联的 `advertisement_goods` 商品列表展示,并可进入 `goodsDetail`
---
## 二、前端实际怎么跳的(问题所在)
首页有 4 个不同的点击处理函数各管一片,**几乎都没读 `promotion_type`**
### 1. `headerServerClick`(金刚位 tabList、homeLeftList 用)
- 文件:`pages/index/index.vue`(搜 `headerServerClick(e)`
- 只看 `link_url``appid`**完全忽略 `promotion_type`**。
- 逻辑:无 `link_url` → 弹“此功能暂未开通”;有 `appid` → 跳小程序;否则当本程序路径 `NavgateTo`
- **后果**type=2活动、type=5商品的配置它不认 → 落到“没 link_url”分支 → 弹“暂未开通”。
### 2. `toAdvertisingView`(广告横幅 serverLeft、serverRight[0] 用)
- 文件:`pages/index/index.vue:357` 附近
- **写死**跳 `/packages/advertising/index/index?id=xxx`,不看 `promotion_type`,一律进广告落地页。
### 3. `goToPurify`serverRight 非首个用,模板 `index.vue:101`
- 文件:`pages/index/index.vue:363` 附近
- **彻底写死**:固定跳净水小程序 `appId=wx77b22c0a0018e580`path/appId 全硬编码,传入的 `item` 没用到,与后台配置零关系。
- 模板:`@tap="index === 0 ? toAdvertisingView(serverRightList) : goToPurify(item)"` —— serverRight 第一个跳广告页,其余全跳净水小程序。
### 4. `headerServerClick2`(中部 serverItem / homeRightList1,2 用)
- 文件:`pages/index/index.vue`(搜 `headerServerClick2(e)`
- 不跳外链,只按 `title` 匹配商家分类做筛选,或固定跳本地生活页。**注意:这一处可能是“按分类筛选”的有意设计,不一定算 bug。**
### 5. 顶部轮播 Banner位置 1模板 `index.vue:68`
- 点击事件被**注释掉了**`<!-- <swiper-item ... @click="headerServerClick(item)"> -->`),顶部 banner 现在点击无反应。
---
## 三、根因
后台用统一的 `promotion_type` 模型配跳转,前端却是早期“按位置分别写死”的处理函数拼起来的:既没统一读 `promotion_type`,部分位置还硬编码了目标(广告页、净水小程序)。所以出现“很多跳转和后台配置不符”——不是数据错,是前端没按配置解析。
---
## 四、建议修法(未实施,待决策)
做一个统一跳转函数 `handleAdJump(item)`,严格按 `promotion_type` 分发:
```
switch promotion_type:
1: 不跳
2: NavgateTo('/packages/advertising/index/index?id=' + item.id) // 活动
3: link_url 跳本程序页面(兼容 shopEnter 特例 / H5 / APP webview
4: navigateToMiniProgram(appid, link_url) // 其他小程序
5: NavgateTo('/packages/advertising/index/index?id=' + item.id) // 商品(同活动,落地页内展示)
其他: 弹“暂未开通”
```
然后把首页所有广告位的 `@click`/`@tap` 都换成 `handleAdJump(item)`
- 顶部轮播 banner取消注释`handleAdJump`
- 金刚位 tabList、homeLeftList`headerServerClick``handleAdJump`
- serverLeft、serverRight`toAdvertisingView` / `goToPurify``handleAdJump`
### 实施前需确认的点
1. `headerServerClick2`(中部 serverItem 按 title 筛分类)是否要保留分类筛选语义?还是也改成按 `promotion_type` 跳?
2. 净水小程序跳转:是想**保留写死**,还是改由后台某条 banner 配 `type=4 + 该 appid` 来控制(更灵活,去掉硬编码)?
3. type=2/5 是否都统一进广告落地页 `/packages/advertising/index/index?id=banner.id`(当前分析结论是“是”)。
> 相关:`promotion_type` 字段定义见 `huichang-server/internal/model/entity/home_banner_region.go``home_button_region.go`;广告落地页 `uniapp-ZHSQ/packages/advertising/index/index.vue`

839
package-lock.json generated
View File

@ -1,7 +1,7 @@
{
"name": "支付键盘、数字键盘、付款键盘、密码键盘",
"version": "1.0.1",
"lockfileVersion": 3,
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
@ -1168,5 +1168,842 @@
"node": ">=0.4"
}
}
},
"dependencies": {
"@babel/helper-string-parser": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="
},
"@babel/helper-validator-identifier": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
"integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="
},
"@babel/parser": {
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz",
"integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==",
"requires": {
"@babel/types": "^7.28.4"
}
},
"@babel/types": {
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz",
"integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==",
"requires": {
"@babel/helper-string-parser": "^7.27.1",
"@babel/helper-validator-identifier": "^7.27.1"
}
},
"@jridgewell/sourcemap-codec": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="
},
"@vue/compiler-core": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.21.tgz",
"integrity": "sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==",
"requires": {
"@babel/parser": "^7.28.3",
"@vue/shared": "3.5.21",
"entities": "^4.5.0",
"estree-walker": "^2.0.2",
"source-map-js": "^1.2.1"
}
},
"@vue/compiler-dom": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.21.tgz",
"integrity": "sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==",
"requires": {
"@vue/compiler-core": "3.5.21",
"@vue/shared": "3.5.21"
}
},
"@vue/compiler-sfc": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.21.tgz",
"integrity": "sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==",
"requires": {
"@babel/parser": "^7.28.3",
"@vue/compiler-core": "3.5.21",
"@vue/compiler-dom": "3.5.21",
"@vue/compiler-ssr": "3.5.21",
"@vue/shared": "3.5.21",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.18",
"postcss": "^8.5.6",
"source-map-js": "^1.2.1"
}
},
"@vue/compiler-ssr": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.21.tgz",
"integrity": "sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==",
"requires": {
"@vue/compiler-dom": "3.5.21",
"@vue/shared": "3.5.21"
}
},
"@vue/reactivity": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.21.tgz",
"integrity": "sha512-3ah7sa+Cwr9iiYEERt9JfZKPw4A2UlbY8RbbnH2mGCE8NwHkhmlZt2VsH0oDA3P08X3jJd29ohBDtX+TbD9AsA==",
"requires": {
"@vue/shared": "3.5.21"
}
},
"@vue/runtime-core": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.21.tgz",
"integrity": "sha512-+DplQlRS4MXfIf9gfD1BOJpk5RSyGgGXD/R+cumhe8jdjUcq/qlxDawQlSI8hCKupBlvM+3eS1se5xW+SuNAwA==",
"requires": {
"@vue/reactivity": "3.5.21",
"@vue/shared": "3.5.21"
}
},
"@vue/runtime-dom": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.21.tgz",
"integrity": "sha512-3M2DZsOFwM5qI15wrMmNF5RJe1+ARijt2HM3TbzBbPSuBHOQpoidE+Pa+XEaVN+czbHf81ETRoG1ltztP2em8w==",
"requires": {
"@vue/reactivity": "3.5.21",
"@vue/runtime-core": "3.5.21",
"@vue/shared": "3.5.21",
"csstype": "^3.1.3"
}
},
"@vue/server-renderer": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.21.tgz",
"integrity": "sha512-qr8AqgD3DJPJcGvLcJKQo2tAc8OnXRcfxhOJCPF+fcfn5bBGz7VCcO7t+qETOPxpWK1mgysXvVT/j+xWaHeMWA==",
"requires": {
"@vue/compiler-ssr": "3.5.21",
"@vue/shared": "3.5.21"
}
},
"@vue/shared": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.21.tgz",
"integrity": "sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw=="
},
"abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
"requires": {
"event-target-shim": "^5.0.0"
}
},
"async-limiter": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
},
"balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
},
"bl": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
"requires": {
"buffer": "^5.5.0",
"inherits": "^2.0.4",
"readable-stream": "^3.4.0"
},
"dependencies": {
"buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"requires": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
}
}
},
"brace-expansion": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"buffer": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
"requires": {
"base64-js": "^1.3.1",
"ieee754": "^1.2.1"
}
},
"buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
},
"callback-stream": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/callback-stream/-/callback-stream-1.1.0.tgz",
"integrity": "sha512-sAZ9kODla+mGACBZ1IpTCAisKoGnv6PykW7fPk1LrM+mMepE18Yz0515yoVcrZy7dQsTUp3uZLQ/9Sx1RnLoHw==",
"requires": {
"inherits": "^2.0.1",
"readable-stream": "> 1.0.0 < 3.0.0"
}
},
"commist": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/commist/-/commist-1.1.0.tgz",
"integrity": "sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg==",
"requires": {
"leven": "^2.1.0",
"minimist": "^1.1.0"
}
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
"concat-stream": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
"integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
"requires": {
"buffer-from": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^2.2.2",
"typedarray": "^0.0.6"
}
},
"core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
},
"csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
},
"d": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz",
"integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==",
"requires": {
"es5-ext": "^0.10.64",
"type": "^2.7.2"
}
},
"debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"requires": {
"ms": "^2.1.3"
}
},
"duplexify": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
"integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
"requires": {
"end-of-stream": "^1.0.0",
"inherits": "^2.0.1",
"readable-stream": "^2.0.0",
"stream-shift": "^1.0.0"
}
},
"end-of-stream": {
"version": "1.4.5",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
"integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
"requires": {
"once": "^1.4.0"
}
},
"entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="
},
"es5-ext": {
"version": "0.10.64",
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz",
"integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==",
"requires": {
"es6-iterator": "^2.0.3",
"es6-symbol": "^3.1.3",
"esniff": "^2.0.1",
"next-tick": "^1.1.0"
}
},
"es6-iterator": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
"integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
"requires": {
"d": "1",
"es5-ext": "^0.10.35",
"es6-symbol": "^3.1.1"
}
},
"es6-map": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz",
"integrity": "sha512-mz3UqCh0uPCIqsw1SSAkB/p0rOzF/M0V++vyN7JqlPtSW/VsYgQBvVvqMLmfBuyMzTpLnNqi6JmcSizs4jy19A==",
"requires": {
"d": "1",
"es5-ext": "~0.10.14",
"es6-iterator": "~2.0.1",
"es6-set": "~0.1.5",
"es6-symbol": "~3.1.1",
"event-emitter": "~0.3.5"
}
},
"es6-set": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.6.tgz",
"integrity": "sha512-TE3LgGLDIBX332jq3ypv6bcOpkLO0AslAQo7p2VqX/1N46YNsvIWgvjojjSEnWEGWMhr1qUbYeTSir5J6mFHOw==",
"requires": {
"d": "^1.0.1",
"es5-ext": "^0.10.62",
"es6-iterator": "~2.0.3",
"es6-symbol": "^3.1.3",
"event-emitter": "^0.3.5",
"type": "^2.7.2"
}
},
"es6-symbol": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz",
"integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==",
"requires": {
"d": "^1.0.2",
"ext": "^1.7.0"
}
},
"esniff": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz",
"integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==",
"requires": {
"d": "^1.0.1",
"es5-ext": "^0.10.62",
"event-emitter": "^0.3.5",
"type": "^2.7.2"
}
},
"estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
},
"event-emitter": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
"integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==",
"requires": {
"d": "1",
"es5-ext": "~0.10.14"
}
},
"event-target-shim": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="
},
"ext": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz",
"integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==",
"requires": {
"type": "^2.7.2"
}
},
"extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
},
"glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"glob-parent": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
"integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==",
"requires": {
"is-glob": "^3.1.0",
"path-dirname": "^1.0.0"
}
},
"glob-stream": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz",
"integrity": "sha512-uMbLGAP3S2aDOHUDfdoYcdIePUCfysbAd0IAoWVZbeGU/oNQ8asHVSshLDJUPWxfzj8zsCG7/XeHPHTtow0nsw==",
"requires": {
"extend": "^3.0.0",
"glob": "^7.1.1",
"glob-parent": "^3.1.0",
"is-negated-glob": "^1.0.0",
"ordered-read-streams": "^1.0.0",
"pumpify": "^1.3.5",
"readable-stream": "^2.1.5",
"remove-trailing-separator": "^1.0.1",
"to-absolute-glob": "^2.0.0",
"unique-stream": "^2.0.2"
}
},
"help-me": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/help-me/-/help-me-1.1.0.tgz",
"integrity": "sha512-P/IZ8yOMne3SCTHbVY429NZ67B/2bVQlcYGZh2iPPbdLrEQ/qY5aGChn0YTDmt7Sb4IKRI51fypItav+lNl76w==",
"requires": {
"callback-stream": "^1.0.2",
"glob-stream": "^6.1.0",
"through2": "^2.0.1",
"xtend": "^4.0.0"
}
},
"ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"requires": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"is-absolute": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz",
"integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==",
"requires": {
"is-relative": "^1.0.0",
"is-windows": "^1.0.1"
}
},
"is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="
},
"is-glob": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
"integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==",
"requires": {
"is-extglob": "^2.1.0"
}
},
"is-negated-glob": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz",
"integrity": "sha512-czXVVn/QEmgvej1f50BZ648vUI+em0xqMq2Sn+QncCLN4zj1UAxlT+kw/6ggQTOaZPd1HqKQGEqbpQVtJucWug=="
},
"is-relative": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
"integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==",
"requires": {
"is-unc-path": "^1.0.0"
}
},
"is-unc-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
"integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==",
"requires": {
"unc-path-regex": "^0.1.2"
}
},
"is-windows": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
"integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="
},
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
},
"json-stable-stringify-without-jsonify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="
},
"leven": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz",
"integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA=="
},
"magic-string": {
"version": "0.30.19",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz",
"integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==",
"requires": {
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
"minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="
},
"mqtt": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/mqtt/-/mqtt-3.0.0.tgz",
"integrity": "sha512-0nKV6MAc1ibKZwaZQUTb3iIdT4NVpj541BsYrqrGBcQdQ7Jd0MnZD1/6/nj1UFdGTboK9ZEUXvkCu2nPCugHFA==",
"requires": {
"base64-js": "^1.3.0",
"commist": "^1.0.0",
"concat-stream": "^1.6.2",
"end-of-stream": "^1.4.1",
"es6-map": "^0.1.5",
"help-me": "^1.0.1",
"inherits": "^2.0.3",
"minimist": "^1.2.0",
"mqtt-packet": "^6.0.0",
"pump": "^3.0.0",
"readable-stream": "^2.3.6",
"reinterval": "^1.1.0",
"split2": "^3.1.0",
"websocket-stream": "^5.1.2",
"xtend": "^4.0.1"
}
},
"mqtt-packet": {
"version": "6.10.0",
"resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-6.10.0.tgz",
"integrity": "sha512-ja8+mFKIHdB1Tpl6vac+sktqy3gA8t9Mduom1BA75cI+R9AHnZOiaBQwpGiWnaVJLDGRdNhQmFaAqd7tkKSMGA==",
"requires": {
"bl": "^4.0.2",
"debug": "^4.1.1",
"process-nextick-args": "^2.0.1"
}
},
"ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="
},
"next-tick": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"requires": {
"wrappy": "1"
}
},
"ordered-read-streams": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz",
"integrity": "sha512-Z87aSjx3r5c0ZB7bcJqIgIRX5bxR7A4aSzvIbaxd0oTkWBCOoKfuGHiKj60CHVUgg1Phm5yMZzBdt8XqRs73Mw==",
"requires": {
"readable-stream": "^2.0.1"
}
},
"path-dirname": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
"integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q=="
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="
},
"picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
},
"postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"requires": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
}
},
"process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"pump": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
"integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
"requires": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"pumpify": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
"integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
"requires": {
"duplexify": "^3.6.0",
"inherits": "^2.0.3",
"pump": "^2.0.0"
},
"dependencies": {
"pump": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
"integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
"requires": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
}
}
},
"readable-stream": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"reinterval": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz",
"integrity": "sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ=="
},
"remove-trailing-separator": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
"integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw=="
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
},
"split2": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz",
"integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==",
"requires": {
"readable-stream": "^3.0.0"
},
"dependencies": {
"readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
}
}
},
"stream-shift": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz",
"integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ=="
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
}
},
"through2": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
"integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
"requires": {
"readable-stream": "~2.3.6",
"xtend": "~4.0.1"
}
},
"through2-filter": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz",
"integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==",
"requires": {
"through2": "~2.0.0",
"xtend": "~4.0.0"
}
},
"to-absolute-glob": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz",
"integrity": "sha512-rtwLUQEwT8ZeKQbyFJyomBRYXyE16U5VKuy0ftxLMK/PZb2fkOsg5r9kHdauuVDbsNdIBoC/HCthpidamQFXYA==",
"requires": {
"is-absolute": "^1.0.0",
"is-negated-glob": "^1.0.0"
}
},
"type": {
"version": "2.7.3",
"resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz",
"integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ=="
},
"typedarray": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
},
"ultron": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
"integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og=="
},
"unc-path-regex": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
"integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg=="
},
"unique-stream": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz",
"integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==",
"requires": {
"json-stable-stringify-without-jsonify": "^1.0.1",
"through2-filter": "^3.0.0"
}
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"vue": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.21.tgz",
"integrity": "sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==",
"requires": {
"@vue/compiler-dom": "3.5.21",
"@vue/compiler-sfc": "3.5.21",
"@vue/runtime-dom": "3.5.21",
"@vue/server-renderer": "3.5.21",
"@vue/shared": "3.5.21"
}
},
"websocket-stream": {
"version": "5.5.2",
"resolved": "https://registry.npmjs.org/websocket-stream/-/websocket-stream-5.5.2.tgz",
"integrity": "sha512-8z49MKIHbGk3C4HtuHWDtYX8mYej1wWabjthC/RupM9ngeukU4IWoM46dgth1UOS/T4/IqgEdCDJuMe2039OQQ==",
"requires": {
"duplexify": "^3.5.1",
"inherits": "^2.0.1",
"readable-stream": "^2.3.3",
"safe-buffer": "^5.1.2",
"ws": "^3.2.0",
"xtend": "^4.0.0"
}
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"ws": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
"integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
"requires": {
"async-limiter": "~1.0.0",
"safe-buffer": "~5.1.0",
"ultron": "~1.1.0"
}
},
"xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
}
}
}

View File

@ -582,8 +582,10 @@ export default {
// 使itemObj
this.currentGG = this.info.commodity_goods_info_list[this.currentGGIndex];
// promotional_pricesales_price
this.currentGG.sales_price = this.promotional_price;
// promotional_pricesales_price
if (this.promotional_price !== "" && this.promotional_price != null) {
this.currentGG.sales_price = this.promotional_price;
}
//
this.changeImg = this.currentGG.commodity_pic[0];
@ -616,8 +618,10 @@ export default {
//
changeGG(item, index) {
this.currentGG = item;
// promotional_pricesales_price
this.currentGG.sales_price = this.promotional_price;
// promotional_pricesales_price
if (this.promotional_price !== "" && this.promotional_price != null) {
this.currentGG.sales_price = this.promotional_price;
}
this.currentGGIndex = index;
if (this.currentGG.cart_count) {
this.currentNum = this.currentGG.cart_count.count;
@ -778,7 +782,15 @@ export default {
}
},
onLoad(options) {
this.itemObj = JSON.parse(decodeURIComponent(options.item));
// URL
if (options && options.fromChat) {
let stashed
try { stashed = uni.getStorageSync('chatCardItem') } catch (e) { stashed = null }
uni.removeStorageSync('chatCardItem')
this.itemObj = stashed || JSON.parse(decodeURIComponent(options.item))
} else {
this.itemObj = JSON.parse(decodeURIComponent(options.item));
}
const meun = menuButtonInfo();
this.top = meun.top;
this.localHeight = meun.height;

View File

@ -33,9 +33,44 @@
}" class="message-item">
<image :src="message.isSelf ? userAvatar : getAvatarUrl(message)" class="message-avatar" mode="aspectFill">
</image>
<view class="message-content">
<!-- 文字 -->
<view v-if="!message.type || message.type === 1" class="message-content">
{{ message.content }}
</view>
<!-- 图片 -->
<view v-else-if="message.type === 2" class="message-media">
<image v-if="message.mediaUrl" :src="message.mediaUrl" class="chat-img" mode="aspectFit"
@tap="previewImage(message.mediaUrl)" />
<view v-else class="media-loading">图片加载中</view>
</view>
<!-- 视频 -->
<view v-else-if="message.type === 3" class="message-media">
<video v-if="message.mediaUrl" :src="message.mediaUrl" class="chat-video" controls></video>
<view v-else class="media-loading">视频加载中</view>
</view>
<!-- 商品/购物车卡片 -->
<view v-else-if="message.type === 4" class="message-card" @tap="openCardLink(message.card)">
<image v-if="message.card.pic && !message.card._picErr" :src="message.card.pic" class="card-pic"
mode="aspectFill" @error="onCardPicError(message)" />
<view v-else class="card-pic card-pic--ph">商品</view>
<view class="card-info">
<view class="card-name">{{ message.card.name }}</view>
<view class="card-price">¥{{ message.card.price }}</view>
<view class="card-tag">商品</view>
</view>
</view>
<!-- 订单卡片 -->
<view v-else-if="message.type === 5" class="message-card" @tap="openCardLink(message.card)">
<image v-if="message.card.pic && !message.card._picErr" :src="message.card.pic" class="card-pic"
mode="aspectFill" @error="onCardPicError(message)" />
<view v-else class="card-pic card-pic--ph">订单</view>
<view class="card-info">
<view class="card-name">订单 {{ message.card.order_no }}</view>
<view class="card-price">¥{{ message.card.amount }}</view>
<view class="card-tag">订单 · {{ message.card.count }}</view>
</view>
</view>
<view v-else class="message-content">{{ message.content }}</view>
</view>
</block>
</scroll-view>
@ -46,18 +81,45 @@
<textarea v-model="inputMessage" :adjust-position="true" auto-height class="message-input" cursor-spacing="10"
enable-keyboard-accessory-view="true" hold-keyboard="true" maxlength="500" placeholder="请输入消息..."
@blur="onInputBlur" @confirm="sendMessage" @focus="onInputFocus" @input="handleInput"></textarea>
<view class="plus-btn" @tap="togglePanel">
<uni-icons color="#666" size="30" type="plusempty"></uni-icons>
</view>
<button :disabled="inputMessage.trim() === ''" class="send-btn" @tap="sendMessage">
发送
</button>
</view>
<!-- 更多功能面板 -->
<view v-if="showPanel" class="more-panel">
<view class="panel-item" @tap="chooseMedia('image')">
<view class="panel-icon">🖼</view>
<text>相册图片</text>
</view>
<view class="panel-item" @tap="chooseMedia('camera')">
<view class="panel-icon">📷</view>
<text>拍摄</text>
</view>
<view class="panel-item" @tap="chooseMedia('video')">
<view class="panel-icon">🎬</view>
<text>视频</text>
</view>
<view class="panel-item" @tap="openGoodsPicker">
<view class="panel-icon">🛒</view>
<text>商品/购物车</text>
</view>
<view class="panel-item" @tap="openOrderPicker">
<view class="panel-icon">📦</view>
<text>订单</text>
</view>
</view>
</view>
</view>
</template>
<script>
import { menuButtonInfo, picUrl, request } from '@/utils'
import { menuButtonInfo, picUrl, request, NavgateTo } from '@/utils'
import mqttTool from '@/utils/mqtt'
import { apiArr } from '../../../api/customerService'
import { uploadOSS, signPrivateView } from '@/utils/uploadOSS'
export default {
data() {
@ -79,6 +141,8 @@ export default {
//
inputMessage: '',
//
showPanel: false,
//
canSend: false,
//
@ -150,6 +214,13 @@ export default {
console.log('用户头像:', this.userAvatar)
},
onShow() {
// picker /
if (!this._onPickCard) {
this._onPickCard = ({ type, card }) => {
this.publishMsg(type, JSON.stringify(card))
}
uni.$on('chat:pickCard', this._onPickCard)
}
},
methods: {
getAvatarUrl(record) {
@ -193,26 +264,46 @@ export default {
let jsMsg = msg // 使
console.log('收到消息', topic, msg)
if (jsMsg.send_client === this.selfClientId || jsMsg.receive_client === this.selfClientId) {
console.log('接收或发送人是我')
if (jsMsg.send_client === this.chatTarget.openId || jsMsg.receive_client === this.chatTarget.openId) {
console.log('接收或发送人是我的聊天对象')
this.messages.push({
content: jsMsg.content,
time: Date.now(),
isSelf: jsMsg.send_client === this.selfClientId,
isLoading: false
})
console.log('收到我的消息', this.messages)
this.scrollToView = 'msg-' + (this.messages.length - 1)
this.appendMessage(jsMsg, jsMsg.send_client === this.selfClientId)
}
}
})
},
// type/content/ URL
async appendMessage(raw, isSelf) {
const type = Number(raw.type) || 1
const item = {
type,
content: raw.content,
time: Date.now(),
isSelf,
isLoading: false,
mediaUrl: '',
card: null
}
if (type === 4 || type === 5) {
try { item.card = typeof raw.content === 'string' ? JSON.parse(raw.content) : raw.content } catch (e) { item.card = {} }
}
const idx = this.messages.push(item) - 1
this.scrollToView = 'msg-' + idx
// /content object_key
if (type === 2 || type === 3) {
try {
const r = await signPrivateView(raw.content)
this.$set(this.messages[idx], 'mediaUrl', r.url)
} catch (e) {
console.error('签发聊天媒体URL失败', e)
}
}
},
async subscribe() {
if (this.isConnected && this.client) {
this.client.subscribe('contact/message/receive_msg', { qos: 0 }, (err) => {
const topic = 'contact/message/send_msg/' + this.chatTarget.bindId // 沿 contact/message
this.client.subscribe(topic, { qos: 0 }, (err) => {
if (!err) {
console.log('订阅成功', 'contact/message/receive_msg', { qos: 0 })
console.log('订阅成功', topic, { qos: 0 })
this.connectingStatus = ''
} else {
console.log('订阅失败:', err)
@ -348,13 +439,32 @@ export default {
}
//
const formattedMessages = historyMessages.map(msg => ({
content: msg.content,
time: new Date(msg.create_time).getTime(),
times: msg.update_time,
isSelf: msg.send_client === this.selfClientId, //
isLoading: false
})).reverse(); //
const formattedMessages = historyMessages.map(msg => {
const type = Number(msg.type) || 1
const item = {
type,
content: msg.content,
time: new Date(msg.create_time).getTime(),
times: msg.update_time,
isSelf: msg.send_client === this.selfClientId,
isLoading: false,
mediaUrl: '',
card: null
}
if (type === 4 || type === 5) {
try { item.card = typeof msg.content === 'string' ? JSON.parse(msg.content) : msg.content } catch (e) { item.card = {} }
}
return item
}).reverse(); //
// / URL
formattedMessages.forEach(item => {
if (item.type === 2 || item.type === 3) {
signPrivateView(item.content).then(r => {
this.$set(item, 'mediaUrl', r.url)
}).catch(e => console.error('历史媒体签名失败', e))
}
})
console.log('格式化后的历史消息:', formattedMessages)
//
@ -441,7 +551,7 @@ export default {
}
console.log('发送消息', msgData)
this.client.publish(
'contact/message/send_msg', // 使
'contact/message/send_msg/' + this.chatTarget.bindId, //
JSON.stringify(msgData),
{ Qos: 0 },
(err) => {
@ -458,6 +568,107 @@ export default {
this.inputMessage = ''
},
// "+"
togglePanel() {
this.showPanel = !this.showPanel
},
// type: 1 2 3 4 5content /object_key/JSON
publishMsg(type, content) {
if (!this.client || !this.isConnected) {
uni.showToast({ title: '连接断开,请重试', icon: 'none' })
return
}
const msgData = {
bind_id: this.chatTarget.bindId,
send_client: this.selfClientId,
receive_client: this.chatTarget.openId,
type,
content,
receive_read_status: 2
}
this.client.publish('contact/message/send_msg/' + this.chatTarget.bindId, JSON.stringify(msgData), { Qos: 0 }, (err) => {
if (err) console.error('发送消息失败', err)
})
},
// //
chooseMedia(source) {
this.showPanel = false
if (source === 'video') {
uni.chooseVideo({
sourceType: ['album', 'camera'],
maxDuration: 60,
success: (res) => this.uploadAndSend(res.tempFilePath, 'chat_video', 3)
})
} else {
uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: source === 'camera' ? ['camera'] : ['album'],
success: (res) => {
const fp = res.tempFilePaths && res.tempFilePaths[0]
if (fp) this.uploadAndSend(fp, 'chat_image', 2)
}
})
}
},
// bucket object_key
async uploadAndSend(filePath, scene, type) {
try {
const { objectKey } = await uploadOSS({ filePath, scene, bindId: this.chatTarget.bindId })
this.publishMsg(type, objectKey)
} catch (e) {
console.error('上传失败', e)
uni.showToast({ title: '上传失败,请重试', icon: 'none' })
}
},
//
previewImage(url) {
if (url) uni.previewImage({ urls: [url], current: url })
},
// /
openCardLink(card) {
if (!card) return
// path
if (!card.page && card.path) {
NavgateTo(card.path, { isLogin: false })
return
}
if (!card.page) return
// item URL storage
const item = card.item || {}
try {
uni.setStorageSync('chatCardItem', item)
} catch (e) {
console.error('暂存卡片数据失败', e)
}
NavgateTo(card.page + '?item=' + encodeURIComponent(JSON.stringify(item)) + '&fromChat=1', { isLogin: false })
},
// -> $set
onCardPicError(message) {
if (message && message.card) {
this.$set(message.card, '_picErr', true)
}
},
// /
openGoodsPicker() {
this.showPanel = false
// / publishMsg(4, JSON)
NavgateTo('/packages/customerService/picker/index?type=goods&bindId=' + this.chatTarget.bindId, { isLogin: false })
},
//
openOrderPicker() {
this.showPanel = false
NavgateTo('/packages/customerService/picker/index?type=order&bindId=' + this.chatTarget.bindId, { isLogin: false })
},
//
handleInput() {
this.canSend = this.inputMessage.trim().length > 0
@ -580,6 +791,12 @@ export default {
clearTimeout(this.reconnectFailedTimer)
this.reconnectFailedTimer = null
}
//
if (this._onPickCard) {
uni.$off('chat:pickCard', this._onPickCard)
this._onPickCard = null
}
}
}
</script>
@ -592,4 +809,115 @@ export default {
--header-height: 80px; /* 头部高度 */
--input-height: 80px; /* 输入区域高度 */
}
/* 图片/视频消息 */
.message-media {
max-width: 60%;
}
.chat-img {
width: 320rpx;
height: 320rpx;
border-radius: 12rpx;
background: #f0f0f0;
}
.chat-video {
width: 400rpx;
height: 300rpx;
border-radius: 12rpx;
}
.media-loading {
padding: 30rpx 40rpx;
background: #fff;
border-radius: 12rpx;
color: #999;
font-size: 24rpx;
}
/* 商品/订单卡片 */
.message-card {
display: flex;
width: 520rpx;
background: #fff;
border-radius: 14rpx;
overflow: hidden;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.06);
}
.card-pic {
width: 180rpx;
height: 180rpx;
flex-shrink: 0;
background: #f5f5f5;
}
.card-pic--ph {
display: flex;
align-items: center;
justify-content: center;
color: #bbb;
font-size: 28rpx;
}
.card-info {
flex: 1;
padding: 18rpx 20rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
min-width: 0;
}
.card-name {
font-size: 28rpx;
color: #222;
line-height: 38rpx;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.card-price {
font-size: 32rpx;
color: #FF370B;
font-weight: 600;
}
.card-tag {
align-self: flex-start;
font-size: 20rpx;
color: #999;
border: 1rpx solid #eee;
border-radius: 6rpx;
padding: 0 10rpx;
}
/* "+" 按钮与更多面板 */
.plus-btn {
display: flex;
align-items: center;
justify-content: center;
width: 60rpx;
height: 60rpx;
}
.more-panel {
display: flex;
flex-wrap: wrap;
padding: 24rpx 12rpx;
background: #f7f7f7;
}
.panel-item {
width: 20%;
display: flex;
flex-direction: column;
align-items: center;
gap: 8rpx;
font-size: 22rpx;
color: #666;
margin-bottom: 16rpx;
}
.panel-icon {
width: 96rpx;
height: 96rpx;
background: #fff;
border-radius: 16rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 44rpx;
}
</style>

View File

@ -0,0 +1,165 @@
<template>
<view class="picker-page">
<!-- 订单普通/团购 两个 tab -->
<view v-if="mode === 'order'" class="tabs">
<view class="tab" :class="{ active: orderTab === 'normal' }" @tap="switchTab('normal')">普通订单</view>
<view class="tab" :class="{ active: orderTab === 'group' }" @tap="switchTab('group')">团购订单</view>
</view>
<scroll-view class="list" scroll-y>
<!-- 订单列表 -->
<block v-if="mode === 'order'">
<view v-for="(item, idx) in orderList" :key="idx" class="order-card" @tap="pickOrder(item)">
<view class="order-head">
<text class="order-no">订单号{{ item.order_no }}</text>
</view>
<view class="order-goods">
<image v-for="(g, gi) in (item.commodity_order_item_list || []).slice(0, 4)" :key="gi"
:src="g.commodity_pic" class="goods-thumb" mode="aspectFill" />
</view>
<view class="order-foot">
<text>{{ item.total_count }}</text>
<text class="amount">¥{{ item.total_amount }}</text>
<view class="send-btn">发送</view>
</view>
</view>
<view v-if="orderList.length === 0" class="empty">暂无订单</view>
</block>
<!-- 购物车列表 -->
<block v-else>
<view v-for="(g, idx) in cartGoods" :key="idx" class="goods-row" @tap="pickGoods(g)">
<image :src="g.pic" class="goods-thumb-lg" mode="aspectFill" />
<view class="goods-info">
<view class="goods-name">{{ g.name }}</view>
<view class="goods-price">¥{{ g.price }}</view>
</view>
<view class="send-btn">发送</view>
</view>
<view v-if="cartGoods.length === 0" class="empty">购物车为空</view>
</block>
</scroll-view>
</view>
</template>
<script>
import { request, picUrl } from '@/utils'
import { apiArr as shopApi } from '@/api/shop'
import { apiArr as afterSaleApi } from '@/api/afterSale'
export default {
data() {
return {
mode: 'order', // order | goods
bindId: 0,
orderTab: 'normal', // normal | group
orderList: [],
cartGoods: []
}
},
onLoad(options) {
this.mode = options.type === 'goods' ? 'goods' : 'order'
this.bindId = Number(options.bindId) || 0
uni.setNavigationBarTitle({ title: this.mode === 'goods' ? '选择购物车商品' : '选择订单' })
if (this.mode === 'goods') {
this.loadCart()
} else {
this.loadOrders()
}
},
methods: {
switchTab(t) {
this.orderTab = t
this.loadOrders()
},
// order_cate 1 2 tab
loadOrders() {
request(afterSaleApi.orderList, 'POST', {
user_id: uni.getStorageSync('userId')
}).then(res => {
const list = res.order_list || []
list.forEach(item => {
(item.commodity_order_item_list || []).forEach(g => {
g.commodity_pic = (g.commodity_pic && g.commodity_pic.startsWith('http')) ? g.commodity_pic : picUrl + g.commodity_pic
})
})
if (this.orderTab === 'group') {
this.orderList = list.filter(i => i.order_cate == 2)
} else {
this.orderList = list.filter(i => i.order_cate == 1 || i.order_cate == 4)
}
})
},
//
loadCart() {
request(shopApi.getCar, 'POST', { is_group_buy: '' }).then(res => {
const groups = [].concat(res.same_day_cart_list || [], res.normal_cart_list || [])
const goods = []
groups.forEach(sup => {
(sup.commodity_cart_and_goods_model || []).forEach(c => {
const gi = c.commodity_goods_info || {}
goods.push({
goods_id: c.goods_id,
commodity_id: c.commodity_id,
name: gi.commodity_name || gi.goods_name || '商品',
pic: (gi.commodity_pic && gi.commodity_pic.startsWith('http')) ? gi.commodity_pic : picUrl + (gi.commodity_pic || ''),
price: gi.sales_price || c.price || 0
})
})
})
this.cartGoods = goods
})
},
// -> type=5
pickOrder(item) {
const firstPic = (item.commodity_order_item_list && item.commodity_order_item_list[0] && item.commodity_order_item_list[0].commodity_pic) || ''
const card = {
order_id: item.id,
order_no: item.order_no,
amount: item.total_amount,
count: item.total_count,
pic: firstPic,
page: '/packages/myOrders/orderDetails/index', // item storage URL
item //
}
uni.$emit('chat:pickCard', { type: 5, card })
uni.navigateBack()
},
// -> type=4
pickGoods(g) {
const card = {
goods_id: g.goods_id,
commodity_id: g.commodity_id,
name: g.name,
price: g.price,
pic: g.pic,
page: '/packages/advertising/goodsDetail/index',
item: { commodity_id: g.commodity_id, id: g.goods_id, promotional_price: g.price } // goodsDetail commodity_id id promotional_price
}
uni.$emit('chat:pickCard', { type: 4, card })
uni.navigateBack()
}
}
}
</script>
<style scoped>
.picker-page { display: flex; flex-direction: column; height: 100vh; background: #f5f5f5; }
.tabs { display: flex; background: #fff; }
.tab { flex: 1; text-align: center; padding: 28rpx 0; font-size: 28rpx; color: #666; }
.tab.active { color: #FF370B; font-weight: 600; border-bottom: 4rpx solid #FF370B; }
.list { flex: 1; padding: 20rpx; box-sizing: border-box; }
.order-card { background: #fff; border-radius: 12rpx; padding: 20rpx; margin-bottom: 20rpx; }
.order-head { font-size: 24rpx; color: #999; margin-bottom: 14rpx; }
.order-goods { display: flex; gap: 12rpx; }
.goods-thumb { width: 120rpx; height: 120rpx; border-radius: 8rpx; background: #f0f0f0; }
.order-foot { display: flex; align-items: center; justify-content: flex-end; gap: 20rpx; margin-top: 16rpx; }
.order-foot .amount { color: #FF370B; font-weight: 600; }
.goods-row { display: flex; align-items: center; background: #fff; border-radius: 12rpx; padding: 16rpx; margin-bottom: 16rpx; }
.goods-thumb-lg { width: 140rpx; height: 140rpx; border-radius: 8rpx; background: #f0f0f0; flex-shrink: 0; }
.goods-info { flex: 1; margin-left: 20rpx; min-width: 0; }
.goods-name { font-size: 28rpx; color: #222; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.goods-price { font-size: 30rpx; color: #FF370B; font-weight: 600; margin-top: 12rpx; }
.send-btn { background: linear-gradient(91deg, #FF7658, #FF370B); color: #fff; font-size: 24rpx; padding: 10rpx 28rpx; border-radius: 30rpx; flex-shrink: 0; }
.empty { text-align: center; color: #999; font-size: 26rpx; padding: 80rpx 0; }
</style>

View File

@ -425,4 +425,84 @@ page {
.coupon_item_button::after {
border: none;
}
/* ============ 店铺活动商品 ============ */
.shop-activity {
padding: 24rpx;
}
.shop-activity-title {
font-size: 32rpx;
font-weight: 600;
color: #222;
padding-left: 16rpx;
border-left: 6rpx solid #FF370B;
margin-bottom: 20rpx;
}
.shop-activity-list {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.sa-goods {
display: flex;
align-items: center;
background: #fff;
border: 1rpx solid #f2f2f2;
border-radius: 12rpx;
padding: 16rpx;
}
.sa-pic {
width: 160rpx;
height: 160rpx;
border-radius: 8rpx;
flex-shrink: 0;
background: #f5f5f5;
}
.sa-info {
flex: 1;
margin-left: 20rpx;
display: flex;
flex-direction: column;
justify-content: center;
min-width: 0;
}
.sa-name {
font-size: 28rpx;
color: #222;
line-height: 38rpx;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.sa-price-row {
display: flex;
align-items: baseline;
margin-top: 16rpx;
gap: 16rpx;
}
.sa-price {
font-size: 34rpx;
font-weight: 600;
color: #FF370B;
}
.sa-unit {
font-size: 22rpx;
font-weight: normal;
}
.sa-origin {
font-size: 22rpx;
color: #999;
text-decoration: line-through;
}

View File

@ -59,6 +59,27 @@
</view>
</view>
<!-- 店铺活动商品 -->
<view class="white_container shop-activity" v-if="shopActivityList.length > 0">
<view class="shop-activity-title">店铺活动</view>
<view class="shop-activity-list">
<block v-for="(item, index) in shopActivityList" :key="index">
<view class="sa-goods" v-for="(sku, sIdx) in item.group_buy_goods_list" :key="sIdx"
@click="toShopActivityDetail(sku)">
<image class="sa-pic" :src="sku.commodity_pic || item.commodity_pic" mode="aspectFill" />
<view class="sa-info">
<view class="sa-name">{{ sku.goods_name || item.commodity_name }}</view>
<view class="sa-price-row">
<view class="sa-price">{{ sku.group_buy_price }}<text class="sa-unit" v-if="sku.goods_unit">/{{ sku.goods_unit }}</text></view>
<view class="sa-origin" v-if="sku.sales_price">单买价 {{ sku.sales_price }}</view>
</view>
</view>
</view>
</block>
</view>
</view>
<view class="white_container detail" v-for="(item, index) in commentList" :key="index">
<view class="Msg">
<view class="Msg_Tit">
@ -181,7 +202,8 @@ export default {
isDisabled: false,
coupons: [],
couponDetails: [],
showCouponPopup: false
showCouponPopup: false,
shopActivityList: [] // activity_type=2
};
},
onLoad(options) {
@ -213,6 +235,7 @@ export default {
this.flag = false
this.commentList = []
this.getCommentList()
this.getShopActivity()
});
},
onShow() {
@ -229,6 +252,46 @@ export default {
},
methods: {
// activity_type=2
getShopActivity() {
const userId = uni.getStorageSync('userId')
if (!userId) return
const params = {
user_id: userId,
merchant_id: this.info.id,
community_id: uni.getStorageSync('changeCommData') ? uni.getStorageSync('changeCommData').id : ''
}
request(apiArr.getShopActivityList, "POST", params, {}, false).then(res => {
const list = (res.list || []).map(item => {
const group_buy_goods_list = (item.group_buy_goods_list || []).map(sku => ({
...sku,
commodity_pic: picUrl + sku.commodity_pic,
quantity: 0
}))
return {
...item,
commodity_pic: picUrl + item.commodity_pic,
group_buy_goods_list
}
})
this.shopActivityList = list
}).catch(() => {
this.shopActivityList = []
})
},
// ->
toShopActivityDetail(itemObj) {
let targetItem = itemObj
if (!targetItem.group_buy_activity_info && targetItem.group_buy_goods_list && targetItem.group_buy_goods_list.length > 0) {
targetItem = targetItem.group_buy_goods_list[0]
}
if (!targetItem.group_buy_activity_info) return
const item = {
...targetItem,
groupById: targetItem.group_buy_activity_info.id
}
NavgateTo(`/packages/shop/groupPurchaseDetail/index?item=${JSON.stringify(item)}`)
},
getCouponList() {
const params = {
mch_id: uni.getStorageSync("merchantInfo").id,

View File

@ -170,7 +170,15 @@ export default {
};
},
onLoad(options) {
const item = JSON.parse(options?.item);
let item
// URL
if (options && options.fromChat) {
try { item = uni.getStorageSync('chatCardItem') } catch (e) { item = null }
uni.removeStorageSync('chatCardItem')
}
if (!item) {
item = JSON.parse(options?.item);
}
this.orderInfo = item;
//
item.order_status == "1" ? this.startCountdown() : "";

View File

@ -1,67 +1,148 @@
/* ============================================================
* 商家入驻 - 资料审核状态页
* 审核中 #FF9F0A 通过 #34C759 失败 #FF3B30 主色 #FF370B
* ============================================================ */
page {
background-color: #F6F7F9;
min-height: 100vh;
}
.container {
margin-top: 100rpx;
display: flex;
justify-content: center;
padding: 24rpx;
box-sizing: border-box;
padding-bottom: 200rpx;
}
.auditStatus {
width: 100px;
height: 100px;
margin: 0 auto;
.audit-card {
background: #FFFFFF;
border-radius: 16rpx;
padding: 60rpx 40rpx 48rpx;
display: flex;
flex-direction: column;
align-items: center;
}
.title {
font-size: 40rpx;
font-weight: bold;
margin: 10rpx 0;
text-align: center;
.audit-icon {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 28rpx;
}
.content{
font-size: 26rpx;
margin-top: 40rpx;
text-align: center;
color: #a9a9a9;
.audit-icon--ing {
background: #FFF4E0;
}
.info{
margin-top: 70rpx;
width: 500rpx;
background-color: #f6f6fa;
padding: 20rpx 30rpx;
border-radius: 20rpx;
.audit-icon--ok {
background: #E6F8EA;
}
.info view{
display: flex;
justify-content: space-between;
margin: 15rpx 0;
.audit-icon--fail {
background: #FDECEA;
}
.info_text{
color: #999999;
.audit-title {
font-size: 36rpx;
font-weight: 600;
color: #222;
margin-bottom: 16rpx;
}
.info_text2{
color: #faba5a;
.audit-desc {
font-size: 26rpx;
color: #999;
text-align: center;
line-height: 40rpx;
padding: 0 20rpx;
margin-bottom: 32rpx;
}
.btn{
margin-top: 50rpx;
border: none;
background-color: #ff4218;
color: #ffffff;
border-radius: 50rpx;
.audit-times {
width: 100%;
border-top: 1rpx solid #F2F2F2;
padding-top: 24rpx;
}
.warning{
color: #ff4218;
font-size: 27rpx;
margin: 50rpx 0;
.audit-time-row {
display: flex;
justify-content: center;
font-size: 26rpx;
color: #666;
line-height: 44rpx;
}
.warning_title{
font-size: 30rpx;
font-weight: bold;
margin-bottom: 20rpx;
}
/* 驳回原因框 */
.reject-box {
width: 100%;
background: #FFF7F4;
border: 1rpx solid #FFD9CD;
border-radius: 12rpx;
padding: 24rpx;
margin-bottom: 24rpx;
}
.reject-title {
display: flex;
align-items: center;
gap: 10rpx;
font-size: 28rpx;
font-weight: 600;
color: #FF370B;
margin-bottom: 14rpx;
}
.reject-text {
font-size: 26rpx;
color: #FF370B;
line-height: 42rpx;
white-space: pre-wrap;
}
.reject-imgs {
margin-top: 18rpx;
}
.reject-imgs-label {
font-size: 24rpx;
color: #999;
margin-bottom: 12rpx;
}
.reject-thumbs {
display: flex;
flex-wrap: wrap;
gap: 12rpx;
}
.reject-thumb {
width: 120rpx;
height: 120rpx;
border-radius: 8rpx;
border: 1rpx solid #EEE;
}
/* 底部按钮 */
.audit-footer {
position: fixed;
left: 0;
right: 0;
bottom: 0;
background: #FFFFFF;
padding: 16rpx 24rpx calc(16rpx + env(safe-area-inset-bottom));
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.04);
z-index: 99;
}
.btn-primary {
height: 88rpx;
background: linear-gradient(91deg, #FF7658 0%, #FF370B 100%);
border-radius: 44rpx;
color: #FFFFFF;
font-size: 30rpx;
display: flex;
align-items: center;
justify-content: center;
}

View File

@ -1,128 +1,141 @@
<template>
<view class="container">
<view class="auditStatusContainer" v-if="itemObj.status == 1">
<view class="header">
<view class="statusIcon">
<image src="https://static.hshuishang.com/enter_audit1.png" mode="aspectFill" class="auditStatus" />
</view>
<view class="title">
审核中
</view>
</view>
<view class="content">
<view>您的入驻申请正在审核中</view>
<view>请耐心等待工作人员处理</view>
</view>
<view class="info">
<view>
<view class="info_text">申请提交时间</view>
<view>{{ itemObj.create_time }}</view>
</view>
<view>
<view class="info_text">当前审核环节</view>
<view class="info_text2">资质验证中</view>
</view>
</view>
</view>
<view class="auditStatusContainer" v-if="itemObj.status == 2">
<view class="header">
<view class="statusIcon">
<image src="https://static.hshuishang.com/af_√.png" mode="aspectFill"
class="auditStatus" />
</view>
<view class="title">
入驻成功
</view>
</view>
<view class="content">
<view>恭喜您您的店铺已成功入驻我们平台</view>
</view>
<view class="info">
<view>
<view class="info_text">店铺名称</view>
<view>{{ itemObj.merchant_name }}</view>
</view>
<view>
<view class="info_text">入住时间</view>
<view>{{ itemObj.handle_time || '' }}</view>
</view>
<view>
<view class="info_text">店铺ID</view>
<view>{{ itemObj.merchant_code }}</view>
</view>
</view>
<!-- <view>
<button class="btn" @click="goShopManage">进入店铺管理</button>
</view> -->
</view>
<view class="auditStatusContainer" v-if="itemObj.status == 3">
<view class="header">
<view class="statusIcon">
<image src="https://static.hshuishang.com/enter_audit2.png" mode="aspectFill" class="auditStatus" />
</view>
<view class="title">
审核失败
</view>
</view>
<view class="content">
<view>很抱歉您的入驻申请未通过审核</view>
<view>请修改后重新提交</view>
</view>
<view class="info">
<view>
<view class="info_text">申请提交时间</view>
<view>{{ itemObj.create_time }}</view>
</view>
<view>
<view class="info_text">审核完成时间</view>
<view>{{ itemObj.handle_time || '' }}</view>
</view>
</view>
<view class="warning" v-if="itemObj.remark">
<view class="warning_title">审核未通过原因</view>
<view>
{{ itemObj.remark || '' }}
</view>
</view>
<view>
<button class="btn" @click="resubmit">修改并重新提交</button>
</view>
</view>
</view>
<view class="container">
<!-- 审核中 -->
<view class="audit-card" v-if="status === 1">
<view class="audit-icon audit-icon--ing">
<u-icon name="clock" color="#FF9F0A" size="56"></u-icon>
</view>
<view class="audit-title">正在审核中</view>
<view class="audit-desc">您的店铺资料已提交工作人员将在 1-3 个工作日内完成审核请耐心等待</view>
<view class="audit-times">
<view class="audit-time-row"><text>申请时间</text><text>{{ itemObj.create_time || '—' }}</text></view>
</view>
</view>
<!-- 审核通过 -->
<view class="audit-card" v-else-if="status === 2">
<view class="audit-icon audit-icon--ok">
<u-icon name="checkmark" color="#34C759" size="56"></u-icon>
</view>
<view class="audit-title">审核通过</view>
<view class="audit-desc">恭喜您您的店铺资料已同意审核</view>
<view class="audit-times">
<view class="audit-time-row"><text>申请时间</text><text>{{ itemObj.create_time || '—' }}</text></view>
<view class="audit-time-row"><text>审核时间</text><text>{{ itemObj.handle_time || '—' }}</text></view>
</view>
</view>
<!-- 审核失败 -->
<view class="audit-card" v-else-if="status === 3">
<view class="audit-icon audit-icon--fail">
<u-icon name="close" color="#FF3B30" size="56"></u-icon>
</view>
<view class="audit-title">审核失败</view>
<view class="audit-desc">很抱歉您的资料未通过审核请修改后重新提交</view>
<view class="reject-box" v-if="itemObj.remark || rejectImages.length">
<view class="reject-title">
<u-icon name="error-circle" color="#FF370B" size="28"></u-icon>
<text>驳回原因</text>
</view>
<view class="reject-text">{{ itemObj.remark || '—' }}</view>
<view class="reject-imgs" v-if="rejectImages.length">
<view class="reject-imgs-label">问题截图</view>
<view class="reject-thumbs">
<image v-for="(u, i) in rejectImages" :key="i" :src="u" class="reject-thumb"
mode="aspectFill" @click="preview(rejectImages, i)" />
</view>
</view>
</view>
<view class="audit-times">
<view class="audit-time-row"><text>申请时间</text><text>{{ itemObj.create_time || '—' }}</text></view>
<view class="audit-time-row"><text>审核时间</text><text>{{ itemObj.handle_time || '—' }}</text></view>
</view>
</view>
<view class="audit-footer" v-if="status === 3">
<view class="btn-primary" @click="resubmit">修改资料并重新提交</view>
</view>
</view>
</template>
<script>
import { request, NavgateTo } from '../../../utils/index';
import { apiArr } from '../../../api/v2Home.js'
import { NavgateTo } from '../../../utils/index';
import { signPrivateView } from '../../../utils/uploadOSS.js';
export default {
data() {
return {
itemObj: {}
}
},
onLoad(options) {
if (options && options.itemObj) {
try {
const raw = decodeURIComponent(options.itemObj)
this.itemObj = JSON.parse(raw)
} catch (e) {
console.error('解析审核状态参数失败:', e)
this.itemObj = {}
}
} else if (options && options.status) {
// status
this.itemObj = { status: Number(options.status) }
}
},
methods: {
goShopManage() {
NavgateTo('/packages/storeManagement/index/index');
},
resubmit() {
NavgateTo('/packages/shopEnter/index/index?itemObj=' + JSON.stringify(this.itemObj));
}
}
data() {
return {
itemObj: {},
rejectImages: [],
}
},
computed: {
status() {
return Number(this.itemObj.status) || 0
}
},
onLoad(options) {
if (options && options.itemObj) {
try {
this.itemObj = JSON.parse(decodeURIComponent(options.itemObj))
} catch (e) {
console.error('解析审核状态参数失败:', e)
this.itemObj = {}
}
} else if (options && options.status) {
// status
this.itemObj = { status: Number(options.status) }
}
// reject_images key
if (this.itemObj.reject_images) {
this.signRejectImages(this.itemObj.reject_images)
}
},
methods: {
async signRejectImages(paths) {
const list = (paths || '').split(',').filter(Boolean)
const result = []
for (const p of list) {
try {
const r = await signPrivateView(p)
result.push(r.url)
} catch (e) {
console.error('签发驳回截图 URL 失败:', p, e)
}
}
this.rejectImages = result
},
preview(urls, current) {
if (!urls || !urls.length) return
uni.previewImage({ urls, current: urls[current] })
},
resubmit() {
// URL
try {
uni.setStorageSync('shopEnterEditData', this.itemObj)
} catch (e) {
console.error('暂存编辑数据失败:', e)
}
NavgateTo('/packages/shopEnter/index/index?edit=1');
},
// 退/
goHome() {
uni.reLaunch({ url: '/pages/index/index' })
}
},
// /
onBackPress(e) {
// status 3
if (e && e.from === 'backbutton') {
uni.reLaunch({ url: '/pages/index/index' })
return true
}
return false
}
}
</script>
<style>
@import url("./index.css");
</style>
</style>

View File

@ -0,0 +1,235 @@
/* ============================================================
* 商家入驻 - 自定义相机取景框拍证件
* ============================================================ */
page {
background: #000;
}
.cam-page {
position: fixed;
inset: 0;
background: #000;
overflow: hidden;
}
/* 顶部导航 */
.cam-nav {
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: 20;
display: flex;
align-items: center;
justify-content: space-between;
padding: 12rpx 24rpx;
height: 88rpx;
}
.cam-nav-back {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
}
.cam-nav-title {
color: #fff;
font-size: 32rpx;
font-weight: 500;
}
.cam-nav-right {
width: 60rpx;
}
/* 相机取景 */
.cam-view {
width: 100%;
height: 100vh;
}
/* 取景框遮罩 */
.cam-overlay {
position: absolute;
inset: 0;
z-index: 10;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
pointer-events: none;
}
.cam-frame {
position: relative;
border: 2rpx dashed rgba(255, 255, 255, 0.7);
border-radius: 16rpx;
}
.frame-landscape {
width: 86vw;
height: 54vw;
}
.frame-portrait {
width: 64vw;
height: 86vw;
}
.cam-corner {
position: absolute;
width: 44rpx;
height: 44rpx;
border-color: #FF370B;
border-style: solid;
border-width: 0;
}
.cam-corner.tl {
top: -2rpx;
left: -2rpx;
border-top-width: 6rpx;
border-left-width: 6rpx;
border-top-left-radius: 16rpx;
}
.cam-corner.tr {
top: -2rpx;
right: -2rpx;
border-top-width: 6rpx;
border-right-width: 6rpx;
border-top-right-radius: 16rpx;
}
.cam-corner.bl {
bottom: -2rpx;
left: -2rpx;
border-bottom-width: 6rpx;
border-left-width: 6rpx;
border-bottom-left-radius: 16rpx;
}
.cam-corner.br {
bottom: -2rpx;
right: -2rpx;
border-bottom-width: 6rpx;
border-right-width: 6rpx;
border-bottom-right-radius: 16rpx;
}
.cam-tip {
margin-top: 36rpx;
color: #fff;
font-size: 28rpx;
text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.6);
}
/* 横版/竖版切换 */
.cam-orient {
position: absolute;
bottom: 280rpx;
left: 0;
right: 0;
z-index: 20;
display: flex;
justify-content: center;
gap: 24rpx;
}
.orient-item {
padding: 12rpx 40rpx;
border-radius: 40rpx;
background: rgba(255, 255, 255, 0.18);
color: rgba(255, 255, 255, 0.8);
font-size: 28rpx;
}
.orient-item.active {
background: #FF370B;
color: #fff;
}
/* 底部操作 */
.cam-actions {
position: absolute;
bottom: 0;
left: 0;
right: 0;
z-index: 20;
height: 240rpx;
display: flex;
align-items: center;
justify-content: space-around;
padding-bottom: env(safe-area-inset-bottom);
background: rgba(0, 0, 0, 0.3);
}
.cam-action-side {
width: 110rpx;
display: flex;
flex-direction: column;
align-items: center;
gap: 8rpx;
color: #fff;
font-size: 24rpx;
}
.cam-action-placeholder {
opacity: 0;
}
.cam-shutter {
width: 130rpx;
height: 130rpx;
border-radius: 50%;
background: #fff;
border: 8rpx solid rgba(255, 255, 255, 0.4);
box-sizing: border-box;
}
.cam-shutter:active {
transform: scale(0.94);
}
/* 预览态 */
.cam-preview {
width: 100%;
height: 100vh;
background: #000;
}
.cam-preview-actions {
position: absolute;
bottom: 0;
left: 0;
right: 0;
z-index: 20;
height: 200rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 40rpx;
padding-bottom: env(safe-area-inset-bottom);
background: rgba(0, 0, 0, 0.4);
}
.prev-btn {
width: 240rpx;
height: 88rpx;
border-radius: 44rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 30rpx;
}
.prev-retake {
background: rgba(255, 255, 255, 0.2);
color: #fff;
}
.prev-submit {
background: linear-gradient(91deg, #FF7658 0%, #FF370B 100%);
color: #fff;
}

View File

@ -0,0 +1,165 @@
<template>
<view class="cam-page">
<!-- 顶部自定义导航 -->
<view class="cam-nav" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="cam-nav-back" @click="goBack">
<u-icon name="arrow-left" color="#fff" size="40"></u-icon>
</view>
<view class="cam-nav-title">{{ title }}</view>
<view class="cam-nav-right"></view>
</view>
<!-- 拍摄态相机取景 -->
<block v-if="!captured">
<camera class="cam-view" :device-position="'back'" :flash="'auto'" @error="onCamError"></camera>
<!-- 取景框遮罩 -->
<view class="cam-overlay">
<view class="cam-frame" :class="orientation === 'landscape' ? 'frame-landscape' : 'frame-portrait'">
<view class="cam-corner tl"></view>
<view class="cam-corner tr"></view>
<view class="cam-corner bl"></view>
<view class="cam-corner br"></view>
</view>
<view class="cam-tip">{{ frameTip }}</view>
</view>
<!-- 横版/竖版切换 -->
<view class="cam-orient">
<view class="orient-item" :class="{ active: orientation === 'landscape' }" @click="orientation = 'landscape'">横版</view>
<view class="orient-item" :class="{ active: orientation === 'portrait' }" @click="orientation = 'portrait'">竖版</view>
</view>
<!-- 底部操作相册 / 拍照 -->
<view class="cam-actions">
<view class="cam-action-side" @click="pickFromAlbum">
<u-icon name="photo" color="#fff" size="44"></u-icon>
<text>相册</text>
</view>
<view class="cam-shutter" @click="takePhoto"></view>
<view class="cam-action-side cam-action-placeholder"></view>
</view>
</block>
<!-- 预览态重拍 / 提交 -->
<block v-else>
<image class="cam-preview" :src="tempPath" mode="aspectFit"></image>
<view class="cam-preview-actions">
<view class="prev-btn prev-retake" @click="retake">重拍</view>
<view class="prev-btn prev-submit" @click="confirmUse">提交</view>
</view>
</block>
</view>
</template>
<script>
export default {
data() {
return {
statusBarHeight: 20,
target: 'license', // license / idFront / idBack / industry
orientation: 'landscape',
captured: false,
tempPath: '',
cameraCtx: null,
}
},
computed: {
title() {
const map = {
license: '拍摄营业执照',
idFront: '拍摄身份证人像面',
idBack: '拍摄身份证国徽面',
industry: '拍摄行业资质',
}
return map[this.target] || '拍摄证件'
},
frameTip() {
const map = {
license: '请将营业执照完整置于取景框内',
idFront: '请将身份证人像面置于取景框内',
idBack: '请将身份证国徽面置于取景框内',
industry: '请将资质证件完整置于取景框内',
}
return map[this.target] || '请将证件完整置于取景框内'
}
},
onLoad(options) {
if (options && options.target) this.target = options.target
// /
this.orientation = (this.target === 'idFront' || this.target === 'idBack') ? 'landscape' : 'portrait'
try {
const sys = uni.getSystemInfoSync()
this.statusBarHeight = sys.statusBarHeight || 20
} catch (e) {}
},
onReady() {
// #ifndef H5
this.cameraCtx = uni.createCameraContext()
// #endif
},
methods: {
goBack() {
uni.navigateBack()
},
onCamError(e) {
console.error('相机错误:', e)
uni.showModal({
title: '无法使用相机',
content: '请检查是否已授予相机权限,或改用相册上传。',
showCancel: false
})
},
takePhoto() {
if (!this.cameraCtx) {
// H5 camera 退
this.pickFromCamera()
return
}
this.cameraCtx.takePhoto({
quality: 'high',
success: (res) => {
this.tempPath = res.tempImagePath
this.captured = true
},
fail: () => {
uni.showToast({ title: '拍照失败,请重试', icon: 'none' })
}
})
},
pickFromCamera() {
uni.chooseImage({
count: 1, sizeType: ['compressed'], sourceType: ['camera'],
success: (res) => {
const fp = res.tempFilePaths && res.tempFilePaths[0]
if (!fp) return
this.tempPath = fp
this.captured = true
}
})
},
pickFromAlbum() {
uni.chooseImage({
count: 1, sizeType: ['compressed'], sourceType: ['album'],
success: (res) => {
const fp = res.tempFilePaths && res.tempFilePaths[0]
if (!fp) return
this.tempPath = fp
this.captured = true
}
})
},
retake() {
this.captured = false
this.tempPath = ''
},
confirmUse() {
if (!this.tempPath) return
// 线 + OCR
uni.$emit('shopEnter:capture', { target: this.target, filePath: this.tempPath })
uni.navigateBack()
}
}
}
</script>
<style>
@import url("./index.css");
</style>

View File

@ -0,0 +1,182 @@
/* ============================================================
* 商家入驻 - 确认信息页预览信息
* 主色 #FF370B 背景 #F6F7F9
* ============================================================ */
page {
background-color: #F6F7F9;
min-height: 100vh;
padding-bottom: 200rpx;
}
.container {
padding-bottom: 40rpx;
box-sizing: border-box;
}
/* 顶部进度条(复用主页样式) */
.step-bar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 24rpx 30rpx 28rpx;
background: linear-gradient(91deg, #FF7658 0%, #FF370B 100%);
}
.step-pill {
flex: 1;
height: 60rpx;
margin: 0 8rpx;
border-radius: 30rpx;
background: rgba(255, 255, 255, 0.18);
display: flex;
align-items: center;
justify-content: center;
color: rgba(255, 255, 255, 0.65);
font-size: 26rpx;
}
.step-pill.active {
background: #FFFFFF;
color: #FF370B;
font-weight: 600;
}
.confirm-head {
font-size: 32rpx;
font-weight: 600;
color: #222;
padding: 28rpx 24rpx 8rpx;
}
/* 分组卡片 */
.cf-group {
background: #FFFFFF;
margin: 16rpx 24rpx 0;
border-radius: 16rpx;
padding: 8rpx 24rpx;
}
.cf-group-title {
font-size: 28rpx;
font-weight: 600;
color: #222;
padding: 20rpx 0 12rpx;
border-bottom: 1rpx solid #F2F2F2;
}
.cf-row {
display: flex;
align-items: flex-start;
justify-content: space-between;
padding: 22rpx 0;
border-bottom: 1rpx solid #F7F7F7;
}
.cf-row:last-child {
border-bottom: none;
}
.cf-k {
font-size: 26rpx;
color: #999;
flex-shrink: 0;
margin-right: 24rpx;
}
.cf-v {
font-size: 26rpx;
color: #222;
text-align: right;
flex: 1;
word-break: break-all;
}
/* 文件资料行 */
.cf-file-row {
display: flex;
align-items: flex-start;
justify-content: space-between;
padding: 22rpx 0;
border-bottom: 1rpx solid #F7F7F7;
}
.cf-file-row:last-child {
border-bottom: none;
}
.cf-thumbs {
display: flex;
flex-wrap: wrap;
gap: 12rpx;
justify-content: flex-end;
flex: 1;
margin-left: 24rpx;
}
.cf-thumb {
width: 120rpx;
height: 120rpx;
border-radius: 8rpx;
border: 1rpx solid #EEE;
}
.cf-empty {
font-size: 24rpx;
color: #B7B7B7;
}
/* 协议勾选 */
.agreement {
display: flex;
align-items: center;
padding: 24rpx 24rpx 0;
}
.agreement-text {
font-size: 24rpx;
color: #666;
margin-left: 8rpx;
}
.agreement-link {
color: #FF370B;
}
/* 底部按钮(吸底) */
.bottom-bar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
background: #FFFFFF;
padding: 16rpx 24rpx calc(16rpx + env(safe-area-inset-bottom));
display: flex;
gap: 16rpx;
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.04);
z-index: 99;
}
.btn-primary {
flex: 1;
height: 88rpx;
background: linear-gradient(91deg, #FF7658 0%, #FF370B 100%);
border-radius: 44rpx;
color: #FFFFFF;
font-size: 30rpx;
display: flex;
align-items: center;
justify-content: center;
}
.btn-secondary {
flex: 1;
height: 88rpx;
background: #FFFFFF;
border: 1rpx solid #FF370B;
border-radius: 44rpx;
color: #FF370B;
font-size: 30rpx;
display: flex;
align-items: center;
justify-content: center;
}

View File

@ -0,0 +1,209 @@
<template>
<view class="container">
<!-- 顶部进度条 -->
<view class="step-bar">
<view class="step-pill active">填写信息</view>
<view class="step-pill">提交审核</view>
<view class="step-pill">入驻成功</view>
</view>
<view class="confirm-head">请确认以下信息</view>
<!-- 基本信息 -->
<view class="cf-group">
<view class="cf-group-title">基本信息</view>
<view class="cf-row"><text class="cf-k">门店名称</text><text class="cf-v">{{ d.merchant_name || '—' }}</text></view>
<view class="cf-row"><text class="cf-k">所在地区</text><text class="cf-v">{{ d._region_label || '—' }}</text></view>
<view class="cf-row"><text class="cf-k">详细地址</text><text class="cf-v">{{ d.address || '—' }}</text></view>
<view class="cf-row"><text class="cf-k">商家类目</text><text class="cf-v">{{ d._cate_name || '—' }}</text></view>
<view class="cf-row" v-if="d._biz_status_label"><text class="cf-k">营业状态</text><text class="cf-v">{{ d._biz_status_label }}</text></view>
<view class="cf-row"><text class="cf-k">负责人姓名</text><text class="cf-v">{{ d.contact_name || '—' }}</text></view>
<view class="cf-row"><text class="cf-k">负责人手机</text><text class="cf-v">{{ d.phone || '—' }}</text></view>
</view>
<!-- 资质信息 -->
<view class="cf-group">
<view class="cf-group-title">资质信息</view>
<view class="cf-row"><text class="cf-k">执照名称</text><text class="cf-v">{{ d.license_name || '—' }}</text></view>
<view class="cf-row"><text class="cf-k">信用代码</text><text class="cf-v">{{ d.credit_code || '—' }}</text></view>
<view class="cf-row"><text class="cf-k">营业执照</text><text class="cf-v">{{ d.license_is_forever === 1 ? '永久有效' : (d.license_valid_until || '—') }}</text></view>
<view class="cf-row" v-if="d.industry_name"><text class="cf-k">证书名称</text><text class="cf-v">{{ d.industry_name }}</text></view>
<view class="cf-row" v-if="d.industry_cert_no"><text class="cf-k">证书编号</text><text class="cf-v">{{ d.industry_cert_no }}</text></view>
<view class="cf-row" v-if="d.industry_name"><text class="cf-k">资质有效期</text><text class="cf-v">{{ d.industry_is_forever === 1 ? '永久有效' : (d.industry_valid_until || '') }}</text></view>
<view class="cf-row"><text class="cf-k">法人姓名</text><text class="cf-v">{{ d.legal_person || '—' }}</text></view>
<view class="cf-row"><text class="cf-k">身份证号</text><text class="cf-v">{{ d.id_card_no || '—' }}</text></view>
<view class="cf-row"><text class="cf-k">证件有效期</text><text class="cf-v">{{ d.id_is_forever === 1 ? '永久有效' : (d.id_valid_until || '—') }}</text></view>
</view>
<!-- 结算信息 -->
<view class="cf-group">
<view class="cf-group-title">结算信息</view>
<view class="cf-row"><text class="cf-k">银行开户名</text><text class="cf-v">{{ d.account_name || '—' }}</text></view>
<view class="cf-row"><text class="cf-k">{{ d.account_type === 2 ? '对公账号' : '银行卡号' }}</text><text class="cf-v">{{ d.bank_card || '—' }}</text></view>
<view class="cf-row"><text class="cf-k">开户银行</text><text class="cf-v">{{ d.bank_name || '—' }}</text></view>
<view class="cf-row"><text class="cf-k">开户支行</text><text class="cf-v">{{ d.bank_branch || '—' }}</text></view>
<view class="cf-row" v-if="d._bank_region_label"><text class="cf-k">银行所在地</text><text class="cf-v">{{ d._bank_region_label }}</text></view>
<view class="cf-row" v-if="d.bank_union_no"><text class="cf-k">联行号</text><text class="cf-v">{{ d.bank_union_no }}</text></view>
</view>
<!-- 文件资料 -->
<view class="cf-group">
<view class="cf-group-title">文件资料</view>
<view class="cf-file-row">
<text class="cf-k">营业执照</text>
<view class="cf-thumbs">
<image v-for="(u, i) in licenseUrls" :key="i" :src="u" class="cf-thumb" mode="aspectFill" @click="preview(licenseUrls, i)" />
<text v-if="!licenseUrls.length" class="cf-empty">未上传</text>
</view>
</view>
<view class="cf-file-row">
<text class="cf-k">行业资质</text>
<view class="cf-thumbs">
<image v-for="(u, i) in industryUrls" :key="i" :src="u" class="cf-thumb" mode="aspectFill" @click="preview(industryUrls, i)" />
<text v-if="!industryUrls.length" class="cf-empty">未上传</text>
</view>
</view>
<view class="cf-file-row">
<text class="cf-k">法人证件</text>
<view class="cf-thumbs">
<image v-for="(u, i) in idUrls" :key="i" :src="u" class="cf-thumb" mode="aspectFill" @click="preview(idUrls, i)" />
<text v-if="!idUrls.length" class="cf-empty">未上传</text>
</view>
</view>
<view class="cf-file-row">
<text class="cf-k">门头环境</text>
<view class="cf-thumbs">
<image v-for="(u, i) in facadeUrls" :key="i" :src="u" class="cf-thumb" mode="aspectFill" @click="preview(facadeUrls, i)" />
<text v-if="!facadeUrls.length" class="cf-empty">未上传</text>
</view>
</view>
<view class="cf-file-row">
<text class="cf-k">店内环境</text>
<view class="cf-thumbs">
<image v-for="(u, i) in interiorUrls" :key="i" :src="u" class="cf-thumb" mode="aspectFill" @click="preview(interiorUrls, i)" />
<text v-if="!interiorUrls.length" class="cf-empty">未上传</text>
</view>
</view>
</view>
<!-- 协议勾选 -->
<view class="agreement">
<view class="agreement-check" @click="agreeProtocol = !agreeProtocol">
<u-icon :name="agreeProtocol ? 'checkmark-circle-fill' : 'checkmark-circle'" :color="agreeProtocol ? '#FF370B' : '#ccc'" size="36"></u-icon>
</view>
<view class="agreement-text">
我已阅读并同意<text class="agreement-link" @click.stop="viewProtocol">商家入驻协议</text>
</view>
</view>
<view class="bottom-bar">
<view class="btn-secondary" @click="goBack">返回修改</view>
<view class="btn-primary" @click="submit">提交审核</view>
</view>
</view>
</template>
<script>
import { apiArr } from '../../../api/v2local';
import { request, picUrl, NavgateTo } from '../../../utils';
import { signPrivateView } from '../../../utils/uploadOSS.js';
export default {
data() {
return {
picUrl,
d: {},
agreeProtocol: false,
licenseUrls: [],
industryUrls: [],
idUrls: [],
facadeUrls: [],
interiorUrls: [],
submitting: false,
}
},
onLoad(options) {
if (options && options.itemObj) {
try {
this.d = JSON.parse(decodeURIComponent(options.itemObj))
} catch (e) {
console.error('解析确认页参数失败:', e)
this.d = {}
}
}
// / bucket URL
this.facadeUrls = (this.d.facade_photo || '').split(',').filter(Boolean).map(p => this.picUrl + p)
this.interiorUrls = (this.d.interior_photo || '').split(',').filter(Boolean).map(p => this.picUrl + p)
// / / bucket
this.signList(this.d.license_photo, 'licenseUrls')
this.signList(this.d.industry_photo, 'industryUrls')
this.signList([this.d.id_card_front, this.d.id_card_back].filter(Boolean).join(','), 'idUrls')
},
methods: {
async signList(paths, key) {
const list = (paths || '').split(',').filter(Boolean)
const result = []
for (const p of list) {
try {
const r = await signPrivateView(p)
result.push(r.url)
} catch (e) {
console.error('签发私密 URL 失败:', p, e)
}
}
this[key] = result
},
preview(urls, current) {
if (!urls || !urls.length) return
uni.previewImage({ urls, current: urls[current] })
},
viewProtocol() {
uni.showModal({
title: '商家入驻协议',
content: '本协议是您与平台之间关于商家入驻服务的法律协议。入驻后您需遵守平台规则,按时提供商品或服务,保证信息真实有效。平台有权对违规商家进行处罚。',
showCancel: false,
confirmText: '我知道了'
})
},
goBack() {
uni.navigateBack()
},
submit() {
if (!this.agreeProtocol) {
uni.showToast({ title: '请先阅读并同意商家入驻协议', icon: 'none' })
return
}
if (this.submitting) return
this.submitting = true
// 线
const params = {}
Object.keys(this.d).forEach(k => {
if (!k.startsWith('_')) params[k] = this.d[k]
})
request(apiArr.createStore, "POST", params).then(res => {
// reLaunch /
const auditItem = {
status: 1,
create_time: new Date().toLocaleString('sv-SE').replace('T', ' '),
handle_time: '',
remark: ''
}
uni.reLaunch({
url: "/packages/shopEnter/auditStatus/index?itemObj=" + encodeURIComponent(JSON.stringify(auditItem))
})
}).catch(res => {
this.submitting = false
const msg = res && res.message ? res.message : '提交失败,请稍后重试'
if (msg.includes("agent_nil")) {
uni.showModal({ title: '提交失败', content: '未找到对应的代理商信息,请联系平台', showCancel: false })
return
}
uni.showModal({ title: '提交失败', content: msg, showCancel: false })
})
}
}
}
</script>
<style>
@import url("./index.css");
</style>

View File

@ -36,6 +36,19 @@
<view class="tips-item">4. 仅支持二代身份证</view>
</view>
</view>
<view class="example-section" v-if="type === 'industry'">
<view class="title">行业资质示例</view>
<view class="example-img">
<image src="/static/example/license.jpg" mode="widthFix"></image>
</view>
<view class="tips">
<view class="tips-title">拍摄要求</view>
<view class="tips-item">1. 请上传与经营品类对应的资质如食品经营许可证</view>
<view class="tips-item">2. 照片需清晰完整四角完整可见</view>
<view class="tips-item">3. 不得有遮挡涂改反光</view>
<view class="tips-item">4. 资质需在有效期内</view>
</view>
</view>
</view>
</template>
@ -55,7 +68,8 @@ export default {
//
const titleMap = {
license: '营业执照示例',
idcard: '身份证示例'
idcard: '身份证示例',
industry: '行业资质示例'
}
uni.setNavigationBarTitle({
title: titleMap[this.type] || '资质示例'

View File

@ -418,3 +418,257 @@ page {
font-size: 26rpx;
color: #B7B7B7;
}
/* ============ Tab2 资质相关 ============ */
.qual-thumb {
width: 100%;
height: 360rpx;
border-radius: 12rpx;
position: relative;
overflow: hidden;
border: 1rpx solid #EBEBEB;
}
.qual-thumb-img {
width: 100%;
height: 100%;
}
.qual-thumb-del {
position: absolute;
top: 8rpx;
right: 8rpx;
width: 44rpx;
height: 44rpx;
border-radius: 50%;
background: rgba(0, 0, 0, 0.5);
color: #fff;
font-size: 32rpx;
display: flex;
align-items: center;
justify-content: center;
line-height: 1;
}
.id-card-row {
display: flex;
gap: 20rpx;
margin-top: 12rpx;
}
.id-card-cell {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
gap: 12rpx;
}
.id-card-cell .upload-add {
width: 100%;
height: 200rpx;
}
.id-card-cell .qual-thumb {
width: 100%;
height: 200rpx;
}
.id-card-cell-label {
font-size: 24rpx;
color: #999;
}
.forever-toggle {
display: flex;
align-items: center;
gap: 10rpx;
margin-top: 12rpx;
padding-left: 4rpx;
}
.forever-toggle-text {
font-size: 24rpx;
color: #666;
}
/* ============ 有效期分段控件(永久有效 / 截止日期) ============ */
.validity-seg {
display: flex;
gap: 16rpx;
}
.validity-seg .seg-item {
flex: 1;
height: 72rpx;
border: 1rpx solid #EBEBEB;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 26rpx;
color: #666;
background: #FFFFFF;
}
.validity-seg .seg-item.active {
color: #FF370B;
border-color: #FF370B;
background: #FFF1ED;
font-weight: 600;
}
/* ============ 结算页:银行账户信息分组标题 ============ */
.section-title {
display: flex;
align-items: center;
font-size: 30rpx;
color: #222;
font-weight: 600;
padding: 28rpx 24rpx 4rpx;
}
.section-title::before {
content: '';
width: 6rpx;
height: 30rpx;
background: #FF370B;
border-radius: 3rpx;
margin-right: 12rpx;
}
/* 结算页行内底部提示 */
.row-tip {
font-size: 22rpx;
color: #86909C;
padding: 12rpx 24rpx 0;
line-height: 34rpx;
}
/* 结算页可点击选择行右侧布局 */
.row_con--select {
display: flex;
align-items: center;
justify-content: space-between;
}
.row_con .row_value {
flex: 1;
font-size: 28rpx;
color: #222;
}
.row_con .row_value.placeholder {
color: #B7B7B7;
}
/* ============ 结算页 银行账户信息表单 ============ */
.Msg {
background: #FFFFFF;
margin: 12rpx 24rpx 0;
border-radius: 16rpx;
padding: 0 24rpx;
}
.Msg .row {
display: flex;
flex-direction: column;
padding: 24rpx 0;
border-bottom: 1rpx solid #F2F2F2;
}
.Msg .row:last-child {
border-bottom: none;
}
.Msg .row_label {
font-size: 26rpx;
color: #222;
margin-bottom: 14rpx;
display: flex;
align-items: center;
}
.Msg .row_label .star {
color: #FF370B;
margin-right: 4rpx;
}
.Msg .row_con {
height: 80rpx;
border: 1rpx solid #EBEBEB;
border-radius: 8rpx;
padding: 0 20rpx;
display: flex;
align-items: center;
background: #FFFFFF;
}
.Msg .row_con input {
flex: 1;
font-size: 28rpx;
color: #222;
height: 80rpx;
line-height: 80rpx;
}
.Msg .row_con input::placeholder {
color: #B7B7B7;
}
/* 结算页底部按钮组 */
.btn-group {
display: flex;
gap: 16rpx;
padding: 40rpx 24rpx;
}
.btn-group .prevBtn {
flex: 1;
height: 88rpx;
background: #FFFFFF;
border: 1rpx solid #FF370B;
border-radius: 44rpx;
color: #FF370B;
font-size: 30rpx;
display: flex;
align-items: center;
justify-content: center;
}
.btn-group .nextBtn {
flex: 1;
height: 88rpx;
background: linear-gradient(91deg, #FF7658 0%, #FF370B 100%);
border-radius: 44rpx;
color: #FFFFFF;
font-size: 30rpx;
display: flex;
align-items: center;
justify-content: center;
}
.action-sheet {
width: 100%;
background: #F2F2F4;
padding-bottom: env(safe-area-inset-bottom);
}
.action-item {
background: #FFFFFF;
padding: 32rpx 0;
text-align: center;
font-size: 32rpx;
color: #222;
border-bottom: 1rpx solid #F0F0F0;
}
.action-item:active {
background: #F8F8F8;
}
.action-cancel {
margin-top: 12rpx;
color: #999;
border-bottom: none;
}

File diff suppressed because it is too large Load Diff

View File

@ -145,6 +145,13 @@
"navigationBarBackgroundColor": "#F9F9F9"
}
},
{
"path": "picker/index",
"style": {
"navigationBarTitleText": "选择",
"navigationBarBackgroundColor": "#fff"
}
},
{
"path": "chattingRecords/index",
"style": {
@ -586,6 +593,20 @@
"navigationBarBackgroundColor": "#fff"
}
},
{
"path": "confirm/index",
"style": {
"navigationBarTitleText": "商家入驻",
"navigationBarBackgroundColor": "#fff"
}
},
{
"path": "camera/index",
"style": {
"navigationBarTitleText": "拍摄证件",
"navigationStyle": "custom"
}
},
{
"path": "sucess/index",
"style": {
@ -597,7 +618,7 @@
{
"path": "auditStatus/index",
"style": {
"navigationBarTitleText": "审核状态",
"navigationBarTitleText": "资料审核",
"navigationBarBackgroundColor": "#fff"
}
},

View File

@ -489,6 +489,9 @@ export default {
uni.setStorageSync('location', preciseLocation);
uni.setStorageSync('ad_code', ad_info.adcode);
// TODO: ad_code
// 广1-11 getHomeXxx
// banner-region
await this.prefetchBannerGroups();
const [bannerList, serverLeft, serverRightList, homeLeftList, homeRightList, bottomList, buttonList, categoryList] = await Promise.all([
this.getHomeBanner(),//
@ -677,18 +680,45 @@ export default {
},
async getHomeBanner() {
// 广 this._bannerGroups getHomeXxx
// banner-region
async prefetchBannerGroups(positions = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) {
try {
const res = await request(apiArr2.getHomeBannerMulti, "POST", {
ad_code: Number(uni.getStorageSync('ad_code')),
ad_positions: positions,
longitude: uni.getStorageSync('location').lng,
latitude: uni.getStorageSync('location').lat,
page_size: 50
}, { silent: false });
this._bannerGroups = (res && res.groups) ? res.groups : {};
} catch (e) {
console.warn('批量拉取首页广告失败,回退逐个请求:', e);
this._bannerGroups = null; // 退
}
},
// 广退 page
async getBannerRows(position) {
if (this._bannerGroups) {
const rows = this._bannerGroups[String(position)] || [];
return rows;
}
const res = await request(apiArr2.getHomeBanner, "POST", {
ad_code: uni.getStorageSync('ad_code'),
ad_position: 1,
ad_position: position,
longitude: uni.getStorageSync('location').lng,
latitude: uni.getStorageSync('location').lat,
page_num: 1,
page_size: 10
page_size: 50
}, { silent: false });
return (res && res.rows) ? res.rows : [];
},
if (res.rows && res.rows.length) {
let filterRes = this.filterShowList(res.rows, 1);
async getHomeBanner() {
const rows = await this.getBannerRows(1);
if (rows && rows.length) {
let filterRes = this.filterShowList(rows, 1);
filterRes.forEach(item => {
item.pic_src = picUrl + item.pic_src
})
@ -699,21 +729,14 @@ export default {
},
async getServerLeft() {
const res = await request(apiArr2.getHomeBanner, "POST", {
ad_code: uni.getStorageSync('ad_code'),
ad_position: 2,
longitude: uni.getStorageSync('location').lng,
latitude: uni.getStorageSync('location').lat,
page_num: 1,
page_size: 10
}, { silent: false });
if (!res.rows || !res.rows.length) {
const rows = await this.getBannerRows(2);
if (!rows || !rows.length) {
this.serverLeftList = []
}
if (res.rows && res.rows.length) {
this.serverLeftList = res.rows
let filterRes = this.filterShowList(res?.rows, 1);
if (rows && rows.length) {
this.serverLeftList = rows
let filterRes = this.filterShowList(rows, 1);
filterRes.forEach(item => {
item.pic_src = picUrl + item.pic_src
})
@ -727,19 +750,12 @@ export default {
async getServerRight() {
const rightList = []
for (let i = 3; i < 5; i++) {
const res = await request(apiArr2.getHomeBanner, "POST", {
ad_code: uni.getStorageSync('ad_code'),
ad_position: i,
longitude: uni.getStorageSync('location').lng,
latitude: uni.getStorageSync('location').lat,
page_num: 1,
page_size: 10
}, { silent: false });
if (!res.rows || !res.rows.length) {
const rows = await this.getBannerRows(i);
if (!rows || !rows.length) {
this.serverRightList = []
}
if (res.rows && res.rows.length) {
let filterRes = this.filterShowList(res?.rows, 1);
if (rows && rows.length) {
let filterRes = this.filterShowList(rows, 1);
filterRes.forEach(item => {
item.pic_src = picUrl + item.pic_src
})
@ -750,16 +766,9 @@ export default {
},
async getHomeMidLeft() {
const res = await request(apiArr2.getHomeBanner, "POST", {
ad_code: uni.getStorageSync('ad_code'),
ad_position: 5,
longitude: uni.getStorageSync('location').lng,
latitude: uni.getStorageSync('location').lat,
page_num: 1,
page_size: 10
}, { silent: false });
if (res.rows && res.rows.length) {
let filterRes = this.filterShowList(res?.rows, 1);
const rows = await this.getBannerRows(5);
if (rows && rows.length) {
let filterRes = this.filterShowList(rows, 1);
filterRes.forEach(item => {
item.pic_src = picUrl + item.pic_src
})
@ -772,19 +781,9 @@ export default {
async getHomeMidRight() {
const rightList = []
for (let i = 6; i < 12; i++) {
const res = await request(apiArr2.getHomeBanner, "POST", {
ad_code: uni.getStorageSync('ad_code'),
ad_position: i,
longitude: uni.getStorageSync('location').lng,
latitude: uni.getStorageSync('location').lat,
page_num: 1,
page_size: 10
}, { silent: false });
if (res.rows && res.rows.length) {
// let firstItem = res.rows[0];
// firstItem.pic_src = picUrl + firstItem.pic_src;
// rightList.push(firstItem);
let filterRes = this.filterShowList(res?.rows, 1);
const rows = await this.getBannerRows(i);
if (rows && rows.length) {
let filterRes = this.filterShowList(rows, 1);
filterRes.forEach(item => {
item.pic_src = picUrl + item.pic_src
})

View File

@ -1,7 +1,7 @@
{
"appid": "wx1addb25675dd8e70",
"compileType": "miniprogram",
"libVersion": "3.8.3",
"libVersion": "3.9.1",
"packOptions": {
"ignore": [],
"include": []
@ -19,7 +19,18 @@
"disablePlugins": [],
"outputPath": ""
},
"condition": true
"condition": true,
"compileWorklet": false,
"uglifyFileName": false,
"uploadWithSourceMap": true,
"packNpmManually": false,
"minifyWXSS": true,
"minifyWXML": true,
"localPlugins": false,
"disableUseStrict": false,
"useCompilerPlugins": false,
"swc": false,
"disableSWC": true
},
"condition": {},
"editorSetting": {

View File

@ -2,7 +2,22 @@
"description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
"projectname": "uniapp-ZHSQ",
"setting": {
"compileHotReLoad": true
"compileHotReLoad": true,
"urlCheck": true,
"coverView": true,
"lazyloadPlaceholderEnable": false,
"skylineRenderEnable": false,
"preloadBackgroundData": false,
"autoAudits": false,
"useApiHook": true,
"showShadowRootInWxmlPanel": true,
"useStaticServer": false,
"useLanDebug": false,
"showES6CompileOption": false,
"checkInvalidKey": true,
"ignoreDevUnusedFiles": true,
"bigPackageSizeSupport": false
},
"libVersion": "3.9.1"
"libVersion": "3.16.0",
"condition": {}
}

View File

@ -19,8 +19,10 @@ const inferExt = (filePath) => {
return filePath.substring(idx + 1).toLowerCase();
};
const fetchCredential = (scene, ext) => {
const fetchCredential = (scene, ext, bindId) => {
return new Promise((resolve, reject) => {
const data = { scene, ext };
if (bindId) data.bind_id = bindId; // 客服聊天场景:按会话分目录
uni.request({
url: RequsetUrl + "/api/v1/wechat/oss/credential",
method: "POST",
@ -28,7 +30,7 @@ const fetchCredential = (scene, ext) => {
Authorization: uni.getStorageSync("ctoken") || "",
"Content-Type": "application/json",
},
data: { scene, ext },
data,
success: (res) => {
if (res.statusCode !== 200) return reject(new Error("获取上传凭证失败:" + res.statusCode));
const body = res.data || {};
@ -78,7 +80,7 @@ const postToOSS = (filePath, cred) => {
* @param {boolean} [opts.showLoading=true]
* @returns {Promise<{ objectKey: string, url: string }>}
*/
export const uploadOSS = async ({ filePath, scene, ext, showLoading = true }) => {
export const uploadOSS = async ({ filePath, scene, ext, bindId, showLoading = true }) => {
if (!filePath) throw new Error("filePath 不能为空");
if (!scene) throw new Error("scene 不能为空");
@ -87,7 +89,7 @@ export const uploadOSS = async ({ filePath, scene, ext, showLoading = true }) =>
if (showLoading) uni.showLoading({ title: "上传中", mask: true });
try {
const cred = await fetchCredential(scene, finalExt);
const cred = await fetchCredential(scene, finalExt, bindId);
await postToOSS(filePath, cred);
return {
objectKey: cred.object_key,