diff --git a/components/PrivateImage.vue b/components/PrivateImage.vue
new file mode 100644
index 00000000..74ddf4c5
--- /dev/null
+++ b/components/PrivateImage.vue
@@ -0,0 +1,107 @@
+
+
+
+ 加载中…
+ {{ error }}
+ 未上传
+
+
+
+
+
+
diff --git a/components/nav/nav.vue b/components/nav/nav.vue
index 3cdc2845..1648dbc1 100644
--- a/components/nav/nav.vue
+++ b/components/nav/nav.vue
@@ -167,18 +167,21 @@ export default {
// 解析path字段,提取scene参数的值
const pathParts = res.path.split('?');
if (pathParts.length > 1) {
- const queryParams = pathParts[1].split('&');
- for (const param of queryParams) {
- const [key, value] = param.split('=');
- if (key === 'scene') {
- const params = {
- id: value
- }
- uni.setStorageSync('merchantInfo', params);
- NavgateTo('/packages/localLife/detail/index')
- break;
- }
- }
+ if (pathParts[1].startsWith('scene=')){
+ const pathPart = decodeURIComponent(pathParts[1].substring(6))
+ const queryParams = pathPart.split('&');
+ for (const param of queryParams) {
+ const [key, value] = param.split('=');
+ if (key === 'id') {
+ const params = {
+ id: value
+ }
+ uni.setStorageSync('merchantInfo', params);
+ NavgateTo('/packages/localLife/detail/index')
+ break;
+ }
+ }
+ }
}
}
},
diff --git a/packages/customerService/chattingRecords/index.vue b/packages/customerService/chattingRecords/index.vue
index 55fe074c..27485d7a 100644
--- a/packages/customerService/chattingRecords/index.vue
+++ b/packages/customerService/chattingRecords/index.vue
@@ -68,9 +68,9 @@ export default {
},
getAvatarUrl(record){
if(record.client_id_one == uni.getStorageSync('openId')){
- return record.two.avatar ? picUrl + record.two.avatar : 'https://static.hshuishang.com/defaultTx.png'
+ return record.two && record.two.avatar ? picUrl + record.two.avatar : 'https://static.hshuishang.com/defaultTx.png'
}else{
- return record.one.avatar ? picUrl + record.one.avatar : 'https://static.hshuishang.com/defaultTx.png'
+ return record.one && record.one.avatar ? picUrl + record.one.avatar : 'https://static.hshuishang.com/defaultTx.png'
}
},
// 格式化时间
diff --git a/packages/customerService/index/index.vue b/packages/customerService/index/index.vue
index 08088ae5..ff67e77b 100644
--- a/packages/customerService/index/index.vue
+++ b/packages/customerService/index/index.vue
@@ -160,6 +160,7 @@ export default {
const options = {
clientId: this.selfClientId
}
+ console.log('clientId:', options.clientId)
// 添加连接状态回调
const callbacks = {
diff --git a/packages/myOrders/submitOrder/index.vue b/packages/myOrders/submitOrder/index.vue
index e69de29b..487e47d0 100644
--- a/packages/myOrders/submitOrder/index.vue
+++ b/packages/myOrders/submitOrder/index.vue
@@ -0,0 +1,16 @@
+
+
+ 提交订单
+
+
+
+
+
+
diff --git a/packages/shopEnter/auditStatus/index.vue b/packages/shopEnter/auditStatus/index.vue
index f720eb4d..bab1eeaa 100644
--- a/packages/shopEnter/auditStatus/index.vue
+++ b/packages/shopEnter/auditStatus/index.vue
@@ -100,7 +100,18 @@ export default {
}
},
onLoad(options) {
- this.itemObj = JSON.parse(options.itemObj);
+ 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() {
diff --git a/packages/shopEnter/choose/index.css b/packages/shopEnter/choose/index.css
new file mode 100644
index 00000000..8feba11e
--- /dev/null
+++ b/packages/shopEnter/choose/index.css
@@ -0,0 +1,95 @@
+page {
+ background-color: #f6f7fb;
+ min-height: 100vh;
+}
+
+.container {
+ padding: 30rpx;
+}
+
+.header {
+ padding: 40rpx 0 60rpx;
+ text-align: center;
+}
+
+.header-title {
+ font-size: 40rpx;
+ font-weight: bold;
+ color: #222222;
+ margin-bottom: 16rpx;
+}
+
+.header-desc {
+ font-size: 28rpx;
+ color: #999999;
+}
+
+.type-list {
+ margin-bottom: 40rpx;
+}
+
+.type-card {
+ display: flex;
+ align-items: center;
+ background: #FFFFFF;
+ border-radius: 20rpx;
+ padding: 40rpx 30rpx;
+ margin-bottom: 24rpx;
+ box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
+}
+
+.type-card:active {
+ opacity: 0.8;
+}
+
+.type-icon {
+ width: 100rpx;
+ height: 100rpx;
+ margin-right: 24rpx;
+ flex-shrink: 0;
+}
+
+.type-icon image {
+ width: 100%;
+ height: 100%;
+}
+
+.type-info {
+ flex: 1;
+}
+
+.type-name {
+ font-size: 32rpx;
+ font-weight: bold;
+ color: #222222;
+ margin-bottom: 10rpx;
+}
+
+.type-desc {
+ font-size: 24rpx;
+ color: #999999;
+}
+
+.type-arrow {
+ flex-shrink: 0;
+}
+
+.tips {
+ background: #FFFFFF;
+ border-radius: 20rpx;
+ padding: 30rpx;
+}
+
+.tips-title {
+ font-size: 28rpx;
+ font-weight: bold;
+ color: #222222;
+ margin-bottom: 20rpx;
+}
+
+.tips-item {
+ font-size: 24rpx;
+ color: #666666;
+ line-height: 1.8;
+ margin-bottom: 10rpx;
+}
diff --git a/packages/shopEnter/choose/index.vue b/packages/shopEnter/choose/index.vue
new file mode 100644
index 00000000..972a08e9
--- /dev/null
+++ b/packages/shopEnter/choose/index.vue
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+ 个人入驻
+ 适用于个体工商户、自然人经营者
+
+
+
+
+
+
+
+
+
+
+
+
+ 企业入驻
+ 适用于企业、公司法人
+
+
+
+
+
+
+
+
+ 入驻须知
+ 1. 个人入驻需提供身份证、银行卡等个人资质信息
+ 2. 企业入驻需提供营业执照、法人身份证、对公账户等企业资质信息
+ 3. 提交后将由平台工作人员进行审核,预计1-3个工作日
+
+
+
+
+
+
+
diff --git a/packages/shopEnter/example/index.css b/packages/shopEnter/example/index.css
new file mode 100644
index 00000000..a1a3d125
--- /dev/null
+++ b/packages/shopEnter/example/index.css
@@ -0,0 +1,66 @@
+page {
+ background-color: #f6f7fb;
+ min-height: 100vh;
+}
+
+.container {
+ padding: 30rpx;
+}
+
+.example-section {
+ background: #FFFFFF;
+ border-radius: 20rpx;
+ padding: 30rpx;
+}
+
+.title {
+ font-size: 34rpx;
+ font-weight: bold;
+ color: #222222;
+ margin-bottom: 30rpx;
+ text-align: center;
+}
+
+.sub-section {
+ margin-bottom: 40rpx;
+}
+
+.sub-title {
+ font-size: 28rpx;
+ color: #333333;
+ margin-bottom: 20rpx;
+ font-weight: bold;
+}
+
+.example-img {
+ width: 100%;
+ margin-bottom: 20rpx;
+ display: flex;
+ justify-content: center;
+}
+
+.example-img image {
+ width: 500rpx;
+ border-radius: 10rpx;
+ border: 2rpx dashed #ddd;
+}
+
+.tips {
+ background: #FFF8F6;
+ border-radius: 10rpx;
+ padding: 24rpx;
+ margin-top: 20rpx;
+}
+
+.tips-title {
+ font-size: 26rpx;
+ font-weight: bold;
+ color: #FF370B;
+ margin-bottom: 16rpx;
+}
+
+.tips-item {
+ font-size: 24rpx;
+ color: #666666;
+ line-height: 1.8;
+}
diff --git a/packages/shopEnter/example/index.vue b/packages/shopEnter/example/index.vue
new file mode 100644
index 00000000..228ff667
--- /dev/null
+++ b/packages/shopEnter/example/index.vue
@@ -0,0 +1,69 @@
+
+
+
+ 营业执照拍摄示例
+
+
+
+
+ 拍摄要求:
+ 1. 请确保营业执照在有效期内
+ 2. 照片需清晰完整,四角完整可见
+ 3. 不得有遮挡、涂改、反光
+ 4. 支持原件拍照或彩色扫描件
+
+
+
+
+ 身份证拍摄示例
+
+ 身份证正面(人像面)
+
+
+
+
+
+ 身份证反面(国徽面)
+
+
+
+
+
+ 拍摄要求:
+ 1. 请确保身份证在有效期内
+ 2. 照片需清晰完整,四角完整可见
+ 3. 不得有遮挡、涂改、反光
+ 4. 仅支持二代身份证
+
+
+
+
+
+
+
+
diff --git a/packages/shopEnter/index/index.css b/packages/shopEnter/index/index.css
index 812c372c..b62a3f5f 100644
--- a/packages/shopEnter/index/index.css
+++ b/packages/shopEnter/index/index.css
@@ -1,116 +1,420 @@
+/* ============================================================
+ * 商家入驻 - 基本信息 tab 全新样式(按设计稿 PDF 1)
+ * 主色 #FF370B 辅色 #FFF1ED 灰边 #EEE 灰字 #999 深字 #222
+ * ============================================================ */
+
page {
- background-color: #f6f7fb;
- min-height: 100vh;
- padding-bottom: 60rpx;
+ background-color: #F6F7F9;
+ min-height: 100vh;
+ padding-bottom: 200rpx;
}
.container {
- background: url(https://static.hshuishang.com/property-img-file/shopEn_apply.png) no-repeat;
- background-size: 750rpx 497rpx;
- box-sizing: border-box;
- padding-top: 290rpx;
+ padding-bottom: 40rpx;
+ box-sizing: border-box;
}
-.Msg {
- width: 710rpx;
- background: #FFFFFF;
- border-radius: 20rpx 20rpx 20rpx 20rpx;
- margin: 0 auto;
- padding: 0 20rpx;
- 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%);
}
-.row {
- padding-top: 30rpx;
- display: flex;
- justify-content: space-between;
+.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;
}
-.row_label {
- font-size: 28rpx;
- color: #999999;
- width: 180rpx;
+.step-pill.active {
+ background: #FFFFFF;
+ color: #FF370B;
+ font-weight: 600;
}
-.red {
- color: #FF370B;
- margin-left: 5rpx;
+/* ================= Tab 切换 ================= */
+.tab-bar {
+ display: flex;
+ background: #FFFFFF;
+ padding: 0 60rpx;
}
-.row_con {
- flex: 1;
- padding-bottom: 30rpx;
- border-bottom: 1rpx solid #EBEBEB;
- display: flex;
+.tab-item {
+ flex: 1;
+ text-align: center;
+ padding: 28rpx 0 22rpx;
+ font-size: 30rpx;
+ color: #222;
+ position: relative;
}
-.row_con input {
- flex: 1;
+.tab-item.active {
+ color: #FF370B;
+ font-weight: 600;
}
-.nonebor {
- border-bottom: none;
+.tab-item.active::after {
+ content: '';
+ position: absolute;
+ bottom: 8rpx;
+ left: 50%;
+ transform: translateX(-50%);
+ width: 56rpx;
+ height: 6rpx;
+ background: #FF370B;
+ border-radius: 3rpx;
}
-.row2 {
- display: flex;
- align-items: center;
+/* ================= 表单卡片 ================= */
+.form-card {
+ background: #FFFFFF;
+ margin: 20rpx 24rpx 0;
+ padding: 24rpx;
+ border-radius: 16rpx;
}
-.mt {
- margin-top: 26rpx;
- padding-top: 36rpx;
- padding-bottom: 36rpx;
+.field {
+ margin-top: 20rpx;
}
-
-
-.imgCon {
- font-size: 18rpx;
- color: #222222;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- width: 120rpx;
- height: 120rpx;
- background: #F6F7FB;
- border: 1rpx solid #D1D1D1;
- border-radius: 10rpx 10rpx 10rpx 10rpx;
+.field:first-child {
+ margin-top: 0;
}
-.imgCon image {
- width: 34rpx;
- height: 34rpx;
- margin-bottom: 8rpx;
+.field-label {
+ font-size: 26rpx;
+ color: #222;
+ margin-bottom: 14rpx;
+ display: flex;
+ align-items: center;
}
-.mt2 {
- margin-top: 30rpx;
+.field-label .star {
+ color: #FF370B;
+ margin-right: 4rpx;
+ font-size: 26rpx;
}
-.u-upload__wrap__preview {
- width: 120rpx;
- height: 120rpx;
- border-radius: 10rpx 0rpx 10rpx 10rpx !important;
+.field-input {
+ height: 80rpx;
+ border: 1rpx solid #EBEBEB;
+ border-radius: 8rpx;
+ padding: 0 20rpx;
+ display: flex;
+ align-items: center;
+ background: #FFFFFF;
}
-.u-upload__wrap__preview__image {
- width: 100% !important;
- height: 100% !important;
- object-fit: cover;
+.field-input input {
+ flex: 1;
+ font-size: 28rpx;
+ color: #222;
+ height: 80rpx;
+ line-height: 80rpx;
}
-.addBtn {
- font-size: 36rpx;
- color: #FFFFFF;
- width: 600rpx;
- height: 90rpx;
- background: linear-gradient(91deg, #FF7658 0%, #FF370B 100%);
- border-radius: 100rpx 100rpx 100rpx 100rpx;
- margin: 0 auto;
- margin-top: 58rpx;
- display: flex;
- align-items: center;
- justify-content: center;
-}
\ No newline at end of file
+.field-input input::placeholder,
+.field-input .placeholder {
+ color: #B7B7B7;
+}
+
+.field-input.disabled {
+ background: #FAFAFA;
+}
+
+.field-value {
+ flex: 1;
+ font-size: 28rpx;
+ color: #222;
+}
+
+.field-value.placeholder {
+ color: #B7B7B7;
+}
+
+.field-arrow {
+ color: #C8C8C8;
+ font-size: 24rpx;
+ margin-left: 12rpx;
+}
+
+/* ================= 上传图片块 ================= */
+.upload-section {
+ margin-top: 20rpx;
+}
+
+.upload-title {
+ font-size: 26rpx;
+ color: #222;
+ margin-bottom: 16rpx;
+ display: flex;
+ align-items: center;
+}
+
+.upload-title .star {
+ color: #FF370B;
+ margin-right: 4rpx;
+}
+
+.upload-title .count {
+ color: #999;
+ font-size: 24rpx;
+ margin-left: 8rpx;
+ font-weight: normal;
+}
+
+.upload-grid {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 16rpx;
+}
+
+/* uview u-upload 内部样式微调(让占位框跟设计稿一致) */
+.upload-section /deep/ .u-upload__wrap__preview {
+ width: 168rpx !important;
+ height: 168rpx !important;
+ border-radius: 12rpx !important;
+ overflow: hidden;
+}
+
+.upload-section /deep/ .u-upload__button {
+ width: 168rpx !important;
+ height: 168rpx !important;
+ border-radius: 12rpx !important;
+ background: #FAFAFA !important;
+ border: 1rpx dashed #D1D1D1 !important;
+}
+
+.upload-add {
+ width: 168rpx;
+ height: 168rpx;
+ background: #FAFAFA;
+ border: 1rpx dashed #D1D1D1;
+ border-radius: 12rpx;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ color: #999;
+ font-size: 22rpx;
+}
+
+.upload-add-icon {
+ font-size: 48rpx;
+ color: #C8C8C8;
+ line-height: 1;
+ margin-bottom: 6rpx;
+}
+
+/* ================= 底部红色提示框(门店信息要求) ================= */
+.tip-box {
+ margin: 20rpx 24rpx 0;
+ background: #FFF7F4;
+ border: 1rpx solid #FFD9CD;
+ border-radius: 12rpx;
+ padding: 20rpx 24rpx 24rpx;
+ display: flex;
+ gap: 12rpx;
+}
+
+.tip-box .tip-icon {
+ color: #FF370B;
+ font-size: 28rpx;
+ font-weight: bold;
+ flex-shrink: 0;
+ line-height: 40rpx;
+}
+
+.tip-box .tip-content {
+ flex: 1;
+}
+
+.tip-box .tip-title {
+ font-size: 26rpx;
+ color: #FF370B;
+ font-weight: 600;
+ margin-bottom: 8rpx;
+}
+
+.tip-box .tip-item {
+ font-size: 24rpx;
+ color: #FF370B;
+ line-height: 40rpx;
+}
+
+/* ================= 底部按钮(吸底) ================= */
+.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;
+}
+
+/* ================= 协议勾选 ================= */
+.agreement {
+ display: flex;
+ align-items: center;
+ padding: 24rpx 24rpx 0;
+}
+
+.agreement-text {
+ font-size: 24rpx;
+ color: #666;
+ margin-left: 8rpx;
+}
+
+.agreement-link {
+ color: #FF370B;
+}
+
+/* ================= 资质示例链接 ================= */
+.example-link {
+ font-size: 24rpx;
+ color: #FF370B;
+ margin-top: 10rpx;
+}
+
+/* ================= 弹层选择器(所在地区/类目/营业状态) ================= */
+.popup-mask {
+ position: fixed;
+ inset: 0;
+ background: rgba(0, 0, 0, 0.5);
+ z-index: 999;
+ display: flex;
+ align-items: flex-end;
+}
+
+.popup-sheet {
+ width: 100%;
+ background: #FFFFFF;
+ border-radius: 24rpx 24rpx 0 0;
+ max-height: 80vh;
+ display: flex;
+ flex-direction: column;
+}
+
+.popup-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 28rpx 32rpx;
+ border-bottom: 1rpx solid #F0F0F0;
+}
+
+.popup-header .pop-cancel {
+ color: #999;
+ font-size: 28rpx;
+}
+
+.popup-header .pop-title {
+ color: #222;
+ font-size: 30rpx;
+ font-weight: 600;
+}
+
+.popup-header .pop-confirm {
+ color: #FF370B;
+ font-size: 28rpx;
+}
+
+.popup-body {
+ flex: 1;
+ display: flex;
+ overflow: hidden;
+}
+
+.popup-col {
+ flex: 1;
+ overflow-y: auto;
+ padding: 12rpx 0;
+}
+
+.popup-col-item {
+ padding: 24rpx 32rpx;
+ font-size: 28rpx;
+ color: #222;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.popup-col-item.selected {
+ color: #FF370B;
+}
+
+.popup-col-item .check {
+ color: #FF370B;
+ font-size: 24rpx;
+}
+
+/* 单列选择列表(营业状态) */
+.popup-list {
+ flex: 1;
+ overflow-y: auto;
+}
+
+.popup-list-item {
+ padding: 30rpx 32rpx;
+ font-size: 30rpx;
+ color: #222;
+ text-align: center;
+ border-bottom: 1rpx solid #F5F5F5;
+}
+
+.popup-list-item.selected {
+ color: #FF370B;
+}
+
+/* ================= 类目选择搜索框 ================= */
+.popup-search {
+ margin: 16rpx 32rpx;
+ background: #F5F5F5;
+ border-radius: 8rpx;
+ padding: 14rpx 20rpx;
+ display: flex;
+ align-items: center;
+ gap: 12rpx;
+ font-size: 26rpx;
+ color: #B7B7B7;
+}
diff --git a/packages/shopEnter/index/index.vue b/packages/shopEnter/index/index.vue
index 1ea3d242..d55a7905 100644
--- a/packages/shopEnter/index/index.vue
+++ b/packages/shopEnter/index/index.vue
@@ -1,119 +1,342 @@
-
-
- 门店名称*
-
-
+
+
+ 填写信息
+ 提交审核
+ 入驻成功
+
+
+
+
+ 基本信息
+ 资质信息
+ 结算信息
+
+
+
+
+
+
+
+ *门店名称
+
+
+
+
+
+
+ *所在地区
+
+
+ {{ regionLabel || '请选择省/市/区' }}
+
+
+
+
+
+
+ *详细地址
+
+
+
+
+
+
+ *商家类目
+
+
+ {{ confirmClassify.cate_name || '选择经营品类' }}
+
+
+
+
+
+
+ *营业状态
+
+
+ {{ bizStatusLabel || '选择营业状态' }}
+
+
+
+
+
+
+ *联系人
+
+
+
+
+
+
+ *手机号
+
+
+
-
- 所在省*
-
-
-
+
+
+
+
+
+ *店内环境({{ imgList3.length }}/3张)
+
+
+
+ +
+ {{ imgList3.length }}/3
+
+
-
- 所在市*
-
-
-
+
+
+
+
+
+ *门头环境({{ imgList.length }}/3张)
+
+
+
+ +
+ {{ imgList.length }}/3
+
+
-
- 所在区*
-
-
-
-
-
-
- 详细地址*
-
-
-
-
-
- 联系人*
-
-
-
-
-
- 手机号*
-
-
-
-
-
- 商家分类*
-
-
-
-
-
-
- 银行卡号
-
-
+
+
+
+ !
+
+ 门店信息要求
+ · 门店名称需与门店招牌上的名称一致
+ · 门店品类需与门店主要经营范围的分类一致
+ · 门店地址需与门店实际经营地址一致
-
-
- 门脸照
-
-
-
-
- 上传图片
-
-
+
+
+
+
+ 统一社会信用代码*
+
+
+
+
+
+ 法人姓名*
+
+
+
+
+
+ 法人身份证号*
+
+
+
-
-
- 店内环境
-
-
-
-
- 上传图片
-
-
+
+
+ 营业执照*
+
+
+
+
+ 上传图片
+
+
+ 查看示例
+
+
+
+ {{ enterType === 'enterprise' ? '法人身份证正面' : '身份证正面' }}*
+
+
+
+
+ 上传图片
+
+
+ 查看示例
+
+
+
+ {{ enterType === 'enterprise' ? '法人身份证反面' : '身份证反面' }}*
+
+
+
+
+ 上传图片
+
+
+
+
+ 上一步
+ 下一步
+
+
-
- 营业执照*
-
-
-
-
- 上传图片
+
+
+
+
+
+ 开户人姓名
+
+
-
+
+
+ 银行卡号
+
+
+
+
+
+ 开户银行
+
+
+
+
+
+ 开户支行
+
+
+
+
+
+
+
+ 企业开户名称
+
+
+
+
+
+ 对公银行账号
+
+
+
+
+
+ 开户银行
+
+
+
+
+
+ 开户支行
+
+
+
+
+
+
+
+
+
+
+
+
+ 我已阅读并同意《商家入驻协议》
+
+
+
+ 上一步
+ 提交入驻
+
+
+
+
+
- 立即入驻
+
+
+
+
+
+
+
+
+ 下一步
+
-
-
-
-
@@ -130,10 +353,13 @@ import {
upload,
NavgateTo
} from '../../../utils';
+import { signPrivateView } from '../../../utils/uploadOSS.js';
export default {
data() {
return {
picUrl,
+ currentTab: 1,
+ enterType: 'personal',
area: "",
confirmCity: "",
confirmArea: "",
@@ -146,12 +372,26 @@ export default {
imgList5: [],
imgList6: [],
+ // 身份证照片
+ idCardFrontList: [],
+ idCardFrontPath: [],
+ idCardBackList: [],
+ idCardBackPath: [],
+
contact_name: "",
contact_phone: "",
bank_card: "",
+ bank_name: "",
+ bank_branch: "",
+ account_name: "",
store_name: "",
address: "",
+ // 企业入驻额外字段
+ credit_code: "",
+ legal_person: "",
+ legal_id_card: "",
+
confirmProv: "",
confirmCity: "",
confirmBusiness: "",
@@ -166,9 +406,208 @@ export default {
show4: false,
itemObj: {},
+ agreeProtocol: false,
+
+ // 新版弹层相关
+ showRegion: false,
+ showClassifyPop: false,
+ showBizStatus: false,
+ tempProv: {},
+ tempCity: {},
+ tempBuss: {},
+ tempClassify: {},
+ bizStatus: 0, // 1 营业中 2 即将开业 3 休息中
+ bizStatusList: [
+ { value: 1, label: '营业中' },
+ { value: 2, label: '即将开业' },
+ { value: 3, label: '休息中' },
+ ],
}
},
+ computed: {
+ regionLabel() {
+ const a = this.confirmProv && this.confirmProv.ad_name
+ const b = this.confirmCity && this.confirmCity.short_name
+ const c = this.confirmBusiness && this.confirmBusiness.short_name
+ if (!a) return ''
+ return [a, b, c].filter(Boolean).join('/')
+ },
+ bizStatusLabel() {
+ const opt = this.bizStatusList.find(o => o.value === this.bizStatus)
+ return opt ? opt.label : ''
+ },
+ },
methods: {
+ // ===== 新版弹层交互 =====
+ async openRegion() {
+ if (!this.pro || !this.pro.length) await this.getPro()
+ this.tempProv = this.confirmProv && this.confirmProv.ad_code ? this.confirmProv : {}
+ this.tempCity = this.confirmCity && this.confirmCity.ad_code ? this.confirmCity : {}
+ this.tempBuss = this.confirmBusiness && this.confirmBusiness.ad_code ? this.confirmBusiness : {}
+ if (this.tempProv.ad_code) await this.getCity(this.tempProv.ad_code)
+ if (this.tempCity.ad_code) await this.getBuss(this.tempCity.ad_code)
+ this.showRegion = true
+ },
+ async tapProv(item) {
+ this.tempProv = item
+ this.tempCity = {}
+ this.tempBuss = {}
+ this.city = []
+ this.buss = []
+ await this.getCity(item.ad_code)
+ },
+ async tapCity(item) {
+ this.tempCity = item
+ this.tempBuss = {}
+ this.buss = []
+ await this.getBuss(item.ad_code)
+ },
+ tapBuss(item) {
+ this.tempBuss = item
+ },
+ confirmRegion() {
+ if (!this.tempProv.ad_code) {
+ uni.showToast({ title: '请选择省', icon: 'none' }); return
+ }
+ if (!this.tempCity.ad_code) {
+ uni.showToast({ title: '请选择市', icon: 'none' }); return
+ }
+ if (!this.tempBuss.ad_code) {
+ uni.showToast({ title: '请选择区', icon: 'none' }); return
+ }
+ this.confirmProv = this.tempProv
+ this.confirmCity = this.tempCity
+ this.confirmBusiness = this.tempBuss
+ this.showRegion = false
+ },
+ async openClassify() {
+ if (!this.classify || !this.classify.length) await this.getClassify()
+ this.tempClassify = this.confirmClassify && this.confirmClassify.id ? this.confirmClassify : {}
+ this.showClassifyPop = true
+ },
+ confirmClassifyPop() {
+ if (!this.tempClassify.id) {
+ uni.showToast({ title: '请选择类目', icon: 'none' }); return
+ }
+ this.confirmClassify = this.tempClassify
+ this.showClassifyPop = false
+ },
+ selectBizStatus(opt) {
+ this.bizStatus = opt.value
+ this.showBizStatus = false
+ },
+
+ switchTab(tab) {
+ // 点击 Tab 切换时需要校验前面的 Tab
+ if (tab > this.currentTab) {
+ if (this.currentTab === 1 && !this.validateTab1()) return;
+ if (tab === 3 && !this.validateTab2()) return;
+ }
+ this.currentTab = tab;
+ uni.pageScrollTo({ scrollTop: 0, duration: 200 });
+ },
+ nextTab(tab) {
+ if (tab === 2) {
+ if (!this.validateTab1()) return;
+ }
+ if (tab === 3) {
+ if (!this.validateTab2()) return;
+ }
+ this.currentTab = tab;
+ uni.pageScrollTo({ scrollTop: 0, duration: 200 });
+ },
+ validateTab1() {
+ // 基本信息校验
+ if (!this.store_name) { uni.showToast({ title: '请输入门店名称', icon: 'none' }); return false; }
+ if (!this.confirmProv.ad_code) { uni.showToast({ title: '请选择所在省', icon: 'none' }); return false; }
+ if (!this.confirmCity.ad_code) { uni.showToast({ title: '请选择所在市', icon: 'none' }); return false; }
+ if (!this.confirmBusiness.ad_code) { uni.showToast({ title: '请选择所在区', icon: 'none' }); return false; }
+ if (!this.address) { uni.showToast({ title: '请输入详细地址', icon: 'none' }); return false; }
+ if (!this.contact_name) { uni.showToast({ title: '请输入联系人姓名', icon: 'none' }); return false; }
+ if (!this.contact_phone) { uni.showToast({ title: '请输入联系人手机号', icon: 'none' }); return false; }
+ if (!isPhone(this.contact_phone)) { uni.showToast({ title: '联系人手机号格式不正确', icon: 'none' }); return false; }
+ if (!this.confirmClassify.id) { uni.showToast({ title: '请选择商家分类', icon: 'none' }); return false; }
+ return true;
+ },
+ validateTab2() {
+ // 资质信息校验
+ if (this.enterType === 'enterprise') {
+ if (!this.credit_code) { uni.showToast({ title: '请输入统一社会信用代码', icon: 'none' }); return false; }
+ if (!/^[0-9A-HJ-NPQRTUWXY]{18}$/.test(this.credit_code.toUpperCase())) {
+ uni.showToast({ title: '统一社会信用代码格式不正确(18位)', icon: 'none' });
+ return false;
+ }
+ if (!this.legal_person) { uni.showToast({ title: '请输入法人姓名', icon: 'none' }); return false; }
+ if (!this.legal_id_card) { uni.showToast({ title: '请输入法人身份证号', icon: 'none' }); return false; }
+ if (!/(^\d{15}$)|(^\d{17}([0-9X])$)/.test(this.legal_id_card.toUpperCase())) {
+ uni.showToast({ title: '法人身份证号格式不正确', icon: 'none' });
+ return false;
+ }
+ }
+ if (!this.imgList6.length) { uni.showToast({ title: '请上传营业执照', icon: 'none' }); return false; }
+ if (!this.idCardFrontPath.length) { uni.showToast({ title: '请上传身份证正面照片', icon: 'none' }); return false; }
+ if (!this.idCardBackPath.length) { uni.showToast({ title: '请上传身份证反面照片', icon: 'none' }); return false; }
+ return true;
+ },
+ validateTab3() {
+ // 结算信息校验(个人 / 企业都需要银行四要素)
+ if (!this.account_name) {
+ uni.showToast({
+ title: this.enterType === 'enterprise' ? '请输入企业开户名称' : '请输入开户人姓名',
+ icon: 'none'
+ });
+ return false;
+ }
+ if (!this.bank_card) {
+ uni.showToast({
+ title: this.enterType === 'enterprise' ? '请输入对公银行账号' : '请输入银行卡号',
+ icon: 'none'
+ });
+ return false;
+ }
+ if (!/^\d{15,20}$/.test(this.bank_card)) {
+ uni.showToast({ title: '银行账号格式不正确(15-20位数字)', icon: 'none' });
+ return false;
+ }
+ if (!this.bank_name) { uni.showToast({ title: '请输入开户银行', icon: 'none' }); return false; }
+ if (!this.bank_branch) { uni.showToast({ title: '请输入开户支行', icon: 'none' }); return false; }
+ return true;
+ },
+ showExample(type) {
+ // 跳转到资质示例页
+ NavgateTo('/packages/shopEnter/example/index?type=' + type);
+ },
+
+ // 身份证正面上传(私密 bucket,不能直链回显)
+ afterReadIdFront(e) {
+ const files = Array.isArray(e.file) ? e.file : [e.file]
+ files.forEach(item => {
+ upload(item.url, res => {
+ // 私密文件:上传 UI 用本地预览,path 单独保存供提交
+ this.idCardFrontList.push({ url: item.url })
+ this.idCardFrontPath.push(res.data.path)
+ }, 'merchant_private')
+ })
+ },
+ deleteIdFront(e) {
+ this.idCardFrontList.splice(e.index, 1);
+ this.idCardFrontPath.splice(e.index, 1);
+ },
+ // 身份证反面上传(私密 bucket)
+ afterReadIdBack(e) {
+ const files = Array.isArray(e.file) ? e.file : [e.file]
+ files.forEach(item => {
+ upload(item.url, res => {
+ this.idCardBackList.push({ url: item.url })
+ this.idCardBackPath.push(res.data.path)
+ }, 'merchant_private')
+ })
+ },
+ deleteIdBack(e) {
+ this.idCardBackList.splice(e.index, 1);
+ this.idCardBackPath.splice(e.index, 1);
+ },
+
// 根据ad_code解析省市区信息
parseAdCode(adCode) {
if (!adCode) return;
@@ -207,7 +646,6 @@ export default {
this.show3 = false;
},
clickBuss(e) {
- console.log(e);
this.show3 = false;
this.confirmBusiness = e.value[0]
},
@@ -270,11 +708,13 @@ export default {
})
},
afterReadImg3(e) {
- e.file.forEach(item => {
+ // 营业执照(私密 bucket)
+ const files = Array.isArray(e.file) ? e.file : [e.file]
+ files.forEach(item => {
upload(item.url, res => {
- this.imgList5.push({ url: this.picUrl + res.data.path })
+ this.imgList5.push({ url: item.url })
this.imgList6.push(res.data.path)
- })
+ }, 'merchant_private')
})
},
deletePic2(e) {
@@ -285,101 +725,102 @@ export default {
this.imgList5.splice(e.index, 1);
this.imgList6.splice(e.index, 1);
},
+ viewProtocol() {
+ // 查看商家入驻协议
+ uni.showModal({
+ title: '商家入驻协议',
+ content: '本协议是您与平台之间关于商家入驻服务的法律协议。入驻后您需遵守平台规则,按时提供商品或服务,保证信息真实有效。平台有权对违规商家进行处罚。',
+ showCancel: false,
+ confirmText: '我知道了'
+ });
+ },
+ // 给私密 bucket 中的多张图批量签名 URL,写到目标 fileList
+ async signPrivatePaths(paths, listKey) {
+ if (!paths || !paths.length) {
+ this[listKey] = []
+ return
+ }
+ const result = []
+ for (const p of paths) {
+ try {
+ const r = await signPrivateView(p)
+ result.push({ url: r.url })
+ } catch (e) {
+ console.error('签发私密 URL 失败:', p, e)
+ result.push({ url: '' })
+ }
+ }
+ this[listKey] = result
+ },
submit() {
let that = this
- if (!that.store_name) {
- return uni.showToast({
- title: '请输入门店名称',
- duration: 2000
- });
+ if (!that.validateTab3()) return;
+ if (!that.agreeProtocol) {
+ uni.showToast({ title: '请先阅读并同意商家入驻协议', icon: 'none' });
+ return;
}
- if (!that.confirmProv.ad_code) {
- return uni.showToast({
- title: '请选择所在省',
- duration: 2000
- });
- }
- if (!that.confirmCity.ad_code) {
- return uni.showToast({
- title: '请选择所在市',
- duration: 2000
- });
- }
- if (!that.confirmBusiness.ad_code) {
- return uni.showToast({
- title: '请选择所在区',
- duration: 2000
- });
- }
- if (!that.address) {
- return uni.showToast({
- title: '请输入详细地址',
- duration: 2000
- });
- }
- if (!that.contact_name) {
- return uni.showToast({
- title: '请输入联系人姓名',
- duration: 2000
- });
- }
- if (!that.contact_phone) {
- return uni.showToast({
- title: '请输入联系人手机号',
- duration: 2000
- });
- }
- if (!that.confirmClassify.id) {
- return uni.showToast({
- title: '请选择商家分类',
- duration: 2000
- });
- }
- if (!that.imgList6.length) {
- return uni.showToast({
- title: '请上传营业执照',
- duration: 2000
- });
- }
-
let interior_photo = that.imgList4.join(",")
let facade_photo = that.imgList2.join(",")
let license_photo = that.imgList6.join(",")
- request(apiArr.createStore, "POST", {
+ let id_card_front = that.idCardFrontPath.join(",")
+ let id_card_back = that.idCardBackPath.join(",")
+
+ let params = {
+ enter_type: that.enterType === 'enterprise' ? 2 : 1,
contact_name: that.contact_name,
phone: that.contact_phone,
bank_card: that.bank_card,
+ bank_name: that.bank_name,
+ bank_branch: that.bank_branch,
+ account_name: that.account_name,
merchant_name: that.store_name,
address: that.address,
ad_code: that.confirmBusiness.ad_code,
facade_photo,
interior_photo,
license_photo,
+ id_card_front,
+ id_card_back,
merchant_cate_id: that.confirmClassify.id,
- }).then(res => {
- that.contact_name = ''
- that.contact_phone = ''
- that.bank_card = ''
- that.store_name = ''
- that.address = ''
- that.confirmProv = ''
- that.confirmCity = ''
- that.confirmBusiness = ''
- that.imgList = []
- that.imgList2 = []
- that.imgList3 = []
- that.imgList4 = []
- that.imgList5 = []
- that.imgList6 = []
- that.confirmClassify = ''
- NavgateTo("../sucess/index")
- }).catch(res => {
- if (res.message.includes("agent_nil")) {
- uni.showToast({
- title: '未找到对应的代理商信息',
- icon: 'none',
- });
+ }
+
+ if (that.enterType === 'enterprise') {
+ params.credit_code = that.credit_code;
+ params.legal_person = that.legal_person;
+ params.legal_id_card = that.legal_id_card;
+ params.account_type = 2;
+ } else {
+ params.account_type = 1;
+ }
+
+ request(apiArr.createStore, "POST", params).then(res => {
+ // 提交成功后用最新表单 + 服务端返回拼一个 itemObj 给 auditStatus 页用
+ const newItem = {
+ ...params,
+ id: res && res.id,
+ status: 1,
+ create_time: new Date().toLocaleString('sv-SE').replace('T', ' '),
+ handle_time: '',
+ merchant_code: '',
+ remark: ''
}
+ NavgateTo("../auditStatus/index?itemObj=" + encodeURIComponent(JSON.stringify(newItem)))
+ }).catch(res => {
+ 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
+ })
})
},
@@ -414,9 +855,14 @@ export default {
},
onLoad(options) {
+ // 获取入驻类型
+ if (options.enterType) {
+ this.enterType = options.enterType;
+ }
+
// 先执行数据获取方法
Promise.all([this.getPro(), this.getClassify()]).then(() => {
- // 数据获取完成后再进行赋值操作
+ // 数据获取完成后再进行赋值操作(编辑回显)
if (options.itemObj) {
this.itemObj = JSON.parse(options.itemObj)
this.store_name = this.itemObj.merchant_name
@@ -425,6 +871,18 @@ export default {
this.contact_phone = this.itemObj.phone
this.confirmClassify = this.classify.find(item => item.id == this.itemObj.merchant_cate_id)
this.bank_card = this.itemObj.bank_card
+ this.bank_name = this.itemObj.bank_name || ''
+ this.bank_branch = this.itemObj.bank_branch || ''
+ this.account_name = this.itemObj.account_name || ''
+ this.credit_code = this.itemObj.credit_code || ''
+ this.legal_person = this.itemObj.legal_person || ''
+ this.legal_id_card = this.itemObj.legal_id_card || ''
+
+ // 入驻类型回显
+ if (this.itemObj.enter_type === 2) {
+ this.enterType = 'enterprise';
+ }
+
// 解析ad_code回显省市区
if (this.itemObj.ad_code) {
this.parseAdCode(this.itemObj.ad_code);
@@ -433,8 +891,14 @@ export default {
this.imgList2 = this.itemObj.facade_photo ? this.itemObj.facade_photo.split(",") : []
this.imgList3 = this.itemObj.interior_photo ? this.itemObj.interior_photo.split(",").map(item => ({ url: this.picUrl + item })) : []
this.imgList4 = this.itemObj.interior_photo ? this.itemObj.interior_photo.split(",") : []
- this.imgList5 = this.itemObj.license_photo?.split(",").map(item => ({ url: this.picUrl + item }))
- this.imgList6 = this.itemObj.license_photo?.split(",")
+
+ // 营业执照、身份证 —— 私密 bucket,需要签名 URL 才能回显
+ this.imgList6 = this.itemObj.license_photo?.split(",").filter(Boolean) || []
+ this.idCardFrontPath = this.itemObj.id_card_front ? this.itemObj.id_card_front.split(",").filter(Boolean) : []
+ this.idCardBackPath = this.itemObj.id_card_back ? this.itemObj.id_card_back.split(",").filter(Boolean) : []
+ this.signPrivatePaths(this.imgList6, 'imgList5')
+ this.signPrivatePaths(this.idCardFrontPath, 'idCardFrontList')
+ this.signPrivatePaths(this.idCardBackPath, 'idCardBackList')
}
})
}
@@ -443,4 +907,4 @@ export default {
\ No newline at end of file
+
diff --git a/pages.json b/pages.json
index b7bac54c..3b2648a8 100644
--- a/pages.json
+++ b/pages.json
@@ -572,6 +572,13 @@
{
"root": "packages/shopEnter",
"pages": [
+ {
+ "path": "choose/index",
+ "style": {
+ "navigationBarTitleText": "商家入驻",
+ "navigationBarBackgroundColor": "#fff"
+ }
+ },
{
"path": "index/index",
"style": {
@@ -593,6 +600,13 @@
"navigationBarTitleText": "审核状态",
"navigationBarBackgroundColor": "#fff"
}
+ },
+ {
+ "path": "example/index",
+ "style": {
+ "navigationBarTitleText": "资质示例",
+ "navigationBarBackgroundColor": "#fff"
+ }
}
]
},
diff --git a/pages/index/index.vue b/pages/index/index.vue
index 20a0e4a5..068c0e89 100644
--- a/pages/index/index.vue
+++ b/pages/index/index.vue
@@ -203,7 +203,7 @@
买单返积分
-
+
点评
@@ -665,14 +665,14 @@ export default {
async headershopEnterClick() {
if (!uni.getStorageSync('userId')) {
- NavgateTo('/packages/shopEnter/index/index', { isLogin: false });
+ NavgateTo('/packages/shopEnter/choose/index', { isLogin: false });
return
}
const res = await request(apiArr2.statusQuery, "POST", {}, { silent: false });
if (res.status) {
NavgateTo('/packages/shopEnter/auditStatus/index?itemObj=' + JSON.stringify(res));
} else {
- NavgateTo('/packages/shopEnter/index/index');
+ NavgateTo('/packages/shopEnter/choose/index');
}
},
diff --git a/utils/index.js b/utils/index.js
index 7abc27e7..56f48cb1 100644
--- a/utils/index.js
+++ b/utils/index.js
@@ -3,8 +3,8 @@ const environments = {
development: {
apiUrl: "https://test.hshuishang.com",
picUrl: "https://test.hshuishang.com",
- aliyunOssUrl: "https://wechat-img-file.oss-cn-beijing.aliyuncs.com",
- staticUrl: "https://static.hshuishang.com",
+ aliyunOssUrl: "https://wechat-img-file-dev.oss-cn-beijing.aliyuncs.com",
+ staticUrl: "https://static-dev.hshuishang.com",
},
production: {
apiUrl: "https://api.hshuishang.com",
@@ -25,7 +25,7 @@ const getCurrentEnvironment = () => {
if (envVersion === "release") {
return "production"; // 正式版
} else if (envVersion === "trial") {
- return "production"; // 体验版
+ return "development"; // 体验版
} else if (envVersion === "develop") {
return "development"; // 开发版
}
@@ -54,14 +54,14 @@ const getCurrentEnvironment = () => {
};
// 获取当前环境配置
-const currentEnv = "production";//getCurrentEnvironment();
+const currentEnv = getCurrentEnvironment();
const envConfig = environments[currentEnv] || environments.production;
export const RequsetUrl = envConfig.apiUrl; // 请求地址前缀
-// export const picUrl = envConfig.picUrl; // 图片地址前缀
-export const picUrl = currentEnv==='production'?envConfig.aliyunOssUrl:envConfig.picUrl; // 图片地址前缀
-export const aliyunOssUrl = envConfig.aliyunOssUrl; // 阿里云OSS地址
-export const staticUrl = envConfig.staticUrl; // 静态资源地址
+// 公开图片域名:统一走 CDN(dev: static-dev.hshuishang.com / prod: static.hshuishang.com)
+export const picUrl = envConfig.staticUrl;
+export const aliyunOssUrl = envConfig.aliyunOssUrl; // 阿里云OSS直链地址(私密文件签名 URL 已自带 host,一般不需要直接拼)
+export const staticUrl = envConfig.staticUrl; // CDN 加速域名
/**
* 处理图片URL,根据环境自动替换
@@ -326,70 +326,26 @@ export const floatCalculate = (num1, num2, operator) => {
};
/**
- * 图片上传
- * @param {string} filename - 图片上传地址
- * @param {Function} fn - 接口回调函数
+ * 图片上传 —— 走 OSS 直传,回调形状对齐旧后端 { code, data: { path } },path 以 / 开头
+ * @param {string} filename - 本地文件路径
+ * @param {Function} fn - 回调函数
+ * @param {string} [scene] - 场景,默认 common
*/
-export const upload = (filename, fn) => {
- uni.showLoading({
- title: "上传中",
- mask: true,
- });
-
- uni.uploadFile({
- url: RequsetUrl + "/api/v1/public/upload-image",
- filePath: filename,
- name: "image",
- formData: {
- uid: uni.getStorageSync("uid"),
- },
- success: (f) => {
- uni.hideLoading();
- fn(JSON.parse(f.data));
- },
- fail: (res) => {
- uni.hideLoading();
- console.log(res);
- uni.showToast({
- title: "上传文件失败",
- icon: "none",
- });
- },
- complete: () => {},
- });
+export const upload = (filename, fn, scene = "common") => {
+ // 引入此处避免顶层循环依赖
+ const { uploadOSSCompat } = require("./uploadOSS.js");
+ uploadOSSCompat(filename, fn, scene);
};
/**
- * 视频上传
- * @param {string} filename - 图片上传地址
- * @param {Function} fn - 接口回调函数
+ * 视频上传 —— 走 OSS 直传
+ * @param {string} filename - 本地文件路径
+ * @param {Function} fn - 回调函数
+ * @param {string} [scene] - 场景,默认 video
*/
-export const uploadVideo = (filename, fn) => {
- uni.showLoading({
- title: "上传中",
- mask: true,
- });
- uni.uploadFile({
- url: RequsetUrl + "/api/v1/public/upload-video",
- filePath: filename,
- name: "file",
- formData: {
- uid: uni.getStorageSync("uid"),
- },
- success: (f) => {
- uni.hideLoading();
- fn(JSON.parse(f.data));
- },
- fail: (res) => {
- uni.hideLoading();
- console.log(res);
- uni.showToast({
- title: "上传文件失败",
- icon: "none",
- });
- },
- complete: () => {},
- });
+export const uploadVideo = (filename, fn, scene = "video") => {
+ const { uploadOSSCompat } = require("./uploadOSS.js");
+ uploadOSSCompat(filename, fn, scene);
};
/**
diff --git a/utils/uploadOSS.js b/utils/uploadOSS.js
new file mode 100644
index 00000000..d6abf9d3
--- /dev/null
+++ b/utils/uploadOSS.js
@@ -0,0 +1,150 @@
+// uniapp-ZHSQ —— OSS 直传客户端
+//
+// 用法:
+// import { uploadOSS } from '@/utils/uploadOSS'
+// const { objectKey, url } = await uploadOSS({ filePath: tempFilePath, scene: 'avatar' })
+// // objectKey: OSS 上的相对 key,存数据库
+// // url: 公开访问 URL(如果走签名 URL 显示,前端再单独处理)
+//
+// 支持的 scene 由服务端 wechatOssSceneCfg 控制,目前:
+// - avatar 用户头像 (jpg/jpeg/png/webp,<=5MB)
+// - merchant 商家入驻资料 (jpg/jpeg/png/webp/pdf,<=10MB)
+
+import { RequsetUrl, aliyunOssUrl } from "@/utils/index.js";
+
+const inferExt = (filePath) => {
+ if (!filePath) return "";
+ const idx = filePath.lastIndexOf(".");
+ if (idx < 0) return "";
+ return filePath.substring(idx + 1).toLowerCase();
+};
+
+const fetchCredential = (scene, ext) => {
+ return new Promise((resolve, reject) => {
+ uni.request({
+ url: RequsetUrl + "/api/v1/wechat/oss/credential",
+ method: "POST",
+ header: {
+ Authorization: uni.getStorageSync("ctoken") || "",
+ "Content-Type": "application/json",
+ },
+ data: { scene, ext },
+ success: (res) => {
+ if (res.statusCode !== 200) return reject(new Error("获取上传凭证失败:" + res.statusCode));
+ const body = res.data || {};
+ // 兼容服务端通用响应包装
+ const data = body.data || body;
+ if (!data || !data.host) return reject(new Error("上传凭证字段缺失"));
+ resolve(data);
+ },
+ fail: (err) => reject(err),
+ });
+ });
+};
+
+const postToOSS = (filePath, cred) => {
+ return new Promise((resolve, reject) => {
+ uni.uploadFile({
+ url: cred.host,
+ filePath,
+ name: "file", // 必须是 file,OSS PostObject 协议约定
+ formData: {
+ key: cred.object_key,
+ OSSAccessKeyId: cred.access_key_id,
+ "x-oss-security-token": cred.security_token,
+ policy: cred.policy,
+ signature: cred.signature,
+ success_action_status: "200",
+ },
+ success: (res) => {
+ // OSS 200 -> 空 body 视为成功
+ if (res.statusCode === 200 || res.statusCode === 204) {
+ resolve();
+ } else {
+ reject(new Error("OSS 上传失败:" + res.statusCode + " " + (res.data || "")));
+ }
+ },
+ fail: (err) => reject(err),
+ });
+ });
+};
+
+/**
+ * 直传 OSS。
+ * @param {Object} opts
+ * @param {string} opts.filePath 本地文件临时路径(uni.chooseImage 拿到的 path)
+ * @param {string} opts.scene 上传场景:avatar / merchant
+ * @param {string} [opts.ext] 扩展名(不含点);不传则从 filePath 推断
+ * @param {boolean} [opts.showLoading=true]
+ * @returns {Promise<{ objectKey: string, url: string }>}
+ */
+export const uploadOSS = async ({ filePath, scene, ext, showLoading = true }) => {
+ if (!filePath) throw new Error("filePath 不能为空");
+ if (!scene) throw new Error("scene 不能为空");
+
+ const finalExt = (ext || inferExt(filePath)).toLowerCase();
+ if (!finalExt) throw new Error("无法识别文件扩展名");
+
+ if (showLoading) uni.showLoading({ title: "上传中", mask: true });
+ try {
+ const cred = await fetchCredential(scene, finalExt);
+ await postToOSS(filePath, cred);
+ return {
+ objectKey: cred.object_key,
+ url: aliyunOssUrl + "/" + cred.object_key,
+ };
+ } finally {
+ if (showLoading) uni.hideLoading();
+ }
+};
+
+/**
+ * 兼容旧版 utils/index.js 中 upload(filename, fn) 的回调式 API。
+ * 内部走 OSS 直传,回调 res 形状对齐旧版(res.data.path 为以 / 开头的 object key,
+ * 调用方 picUrl + res.data.path 可以直接拼成完整 URL)。
+ *
+ * @param {string} filePath
+ * @param {Function} fn 接收 { code, msg, data: { path } }
+ * @param {string} [scene='merchant']
+ */
+export const uploadOSSCompat = (filePath, fn, scene = "merchant") => {
+ uploadOSS({ filePath, scene })
+ .then(({ objectKey }) => {
+ fn && fn({ code: 1, msg: "ok", data: { path: "/" + objectKey } });
+ })
+ .catch((err) => {
+ console.error("uploadOSSCompat 失败:", err);
+ uni.showToast({ title: err.message || "上传文件失败", icon: "none" });
+ });
+};
+
+/**
+ * 取私密文件查看 URL(小程序商家本人查看自己上传的证件 / 合同)。
+ * 服务端会校验 path 必须以 wechat/merchant_private/{当前 user_id}/ 开头。
+ *
+ * @param {string} objectKey 存数据库的 path(带或不带前导 /)
+ * @returns {Promise<{ url: string, expireSeconds: number }>}
+ */
+export const signPrivateView = (objectKey) => {
+ return new Promise((resolve, reject) => {
+ if (!objectKey) return reject(new Error("object_key 不能为空"));
+ const cleanKey = objectKey.replace(/^\/+/, "");
+ uni.request({
+ url: RequsetUrl + "/api/v1/wechat/oss/sign-private-view",
+ method: "POST",
+ header: {
+ Authorization: uni.getStorageSync("ctoken") || "",
+ "Content-Type": "application/json",
+ },
+ data: { object_key: cleanKey },
+ success: (res) => {
+ if (res.statusCode !== 200) return reject(new Error("签发失败:" + res.statusCode));
+ const body = res.data || {};
+ const data = body.data || body;
+ if (!data || !data.url) return reject(new Error("签发结果字段缺失"));
+ resolve({ url: data.url, expireSeconds: data.expire_seconds });
+ },
+ fail: (err) => reject(err),
+ });
+ });
+};