mirror of
https://gitee.com/yudaocode/yudao-mall-uniapp.git
synced 2025-12-30 01:32:26 +00:00
合并初始化
This commit is contained in:
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
unpackage/*
|
||||
node_modules/*
|
||||
.idea/*
|
||||
deploy.sh
|
||||
.hbuilderx/
|
||||
.vscode/
|
||||
**/.DS_Store
|
||||
.env
|
||||
yarn.lock
|
||||
package-lock.json
|
||||
*.keystore
|
||||
pnpm-lock.yaml
|
||||
6
.prettierignore
Normal file
6
.prettierignore
Normal file
@@ -0,0 +1,6 @@
|
||||
/unpackage/*
|
||||
/node_modules/**
|
||||
/uni_modules/**
|
||||
/public/*
|
||||
**/*.svg
|
||||
**/*.sh
|
||||
10
.prettierrc
Normal file
10
.prettierrc
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"printWidth": 100,
|
||||
"semi": true,
|
||||
"vueIndentScriptAndStyle": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"proseWrap": "never",
|
||||
"htmlWhitespaceSensitivity": "strict",
|
||||
"endOfLine": "auto"
|
||||
}
|
||||
46
App.vue
Normal file
46
App.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<script setup>
|
||||
import {
|
||||
onLaunch,
|
||||
onShow,
|
||||
onError
|
||||
} from '@dcloudio/uni-app';
|
||||
import {
|
||||
ShoproInit
|
||||
} from './sheep';
|
||||
|
||||
onLaunch(() => {
|
||||
// 隐藏原生导航栏 使用自定义底部导航
|
||||
uni.hideTabBar();
|
||||
|
||||
// 加载Shopro底层依赖
|
||||
ShoproInit();
|
||||
});
|
||||
|
||||
onError((err) => {
|
||||
console.log('AppOnError:', err);
|
||||
});
|
||||
|
||||
onShow((options) => {
|
||||
// #ifdef APP-PLUS
|
||||
// 获取urlSchemes参数
|
||||
const args = plus.runtime.arguments;
|
||||
if (args) {}
|
||||
|
||||
// 获取剪贴板
|
||||
uni.getClipboardData({
|
||||
success: (res) => {},
|
||||
});
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
// 确认收货回调结果
|
||||
console.log(options, 'options');
|
||||
// #endif
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '@/sheep/scss/index.scss';
|
||||
</style>
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 lidongtony
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
190
README.md
Normal file
190
README.md
Normal file
@@ -0,0 +1,190 @@
|
||||
## 简介
|
||||
|
||||

|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://gitee.com/sheepjs/shopro-uniapp.git)
|
||||
[](https://gitee.com/sheepjs/shopro-uniapp.git)
|
||||
[](https://gitee.com/sheepjs/shopro-uniapp.git)
|
||||
[](https://gitee.com/sheepjs/shopro-uniapp.git)
|
||||
|
||||
[官方网站](https://www.shopro.top/) | [H5 演示](http://shopro.sheepjs.com/) | [管理系统](https://shopro.sheepjs.com/admin/) | [问题反馈](https://gitee.com/sheepjs/shopro-uniapp/issues)
|
||||
|
||||
</div>
|
||||
|
||||
## 特性
|
||||
|
||||

|
||||
|
||||
- **支持主题色+自定义头部导航+自定义底部导航**
|
||||
- **内含沉浸式头部、通用头部导航示例,支持后端自定义配置底部导航背景和样式**
|
||||
- **店铺装修组件(轮播、标题栏、优惠券、商品组、宫格导航、列表导航+广告魔方+富文本、搜索栏等众多组件)**
|
||||
- **内置微信公众号分享 jssdk+微信小程序分享卡片+微信 App 分享+海报分享统一封装**
|
||||
- **内置微信公众号登录+微信小程序手机号登录+微信 App 开放平台登录+账号密码登录+iOS 登录统一封装**
|
||||
- **内置余额支付+微信公众号 jssdk 支付+微信小程序支付+微信 App 支付+支付宝网页支付+支付宝 App 支付统一封装**
|
||||
- **支持第三方 cdn 图片资源地址,并支持阿里云、腾讯云、七牛云图片缩放参数**
|
||||
- **严格适配多终端场景并支持 App 审核上架**
|
||||
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **前端技术栈:uni-app、ES6、Vue3、Vite、Pinia;**
|
||||
## 安装
|
||||
|
||||
```bash
|
||||
# 1.克隆项目
|
||||
$ git clone https://gitee.com/sheepjs/shopro-uniapp.git
|
||||
```
|
||||
|
||||
```bash
|
||||
# 2.拷贝env示例配置文件 重命名为.env
|
||||
$ cd shopro-uniapp
|
||||
$ cp env .env
|
||||
```
|
||||
|
||||
```bash
|
||||
# 3.安装依赖 (需安装nodejs环境, 使用npm国内镜像)
|
||||
$ npm install --registry=https://registry.npmmirror.com
|
||||
```
|
||||
|
||||
```bash
|
||||
# 4.使用HbuilderX 运行...
|
||||
```
|
||||
|
||||
## 体验
|
||||
|
||||

|
||||
|
||||
客户端演示地址:[https://shopro.sheepjs.com](https://shopro.sheepjs.com)
|
||||
|
||||
演示账号: shopro
|
||||
|
||||
演示密码: a123456
|
||||
|
||||
管理端演示地址:[https://shopro.sheepjs.com/admin/](https://shopro.sheepjs.com/admin/)
|
||||
|
||||
演示账号: shopro
|
||||
|
||||
演示密码: 123456
|
||||
|
||||
_(注意:演示环境已屏蔽管理权限和相关操作)_
|
||||
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
├── pages // 页面
|
||||
│ ├── index // 入口页面
|
||||
│ ├── user // 用户相关
|
||||
│ ├── public // 公共页面
|
||||
│ ├── activity // 活动页面
|
||||
│ ├── app // 积分、签到页面
|
||||
│ ├── chat // 客服页面
|
||||
│ ├── commission // 分销页面
|
||||
│ ├── coupon // 优惠券页面
|
||||
│ ├── goods // 商品页面
|
||||
│ ├── order // 订单页面
|
||||
│ ├── pay // 支付页面
|
||||
├── sheep // 底层依赖/工具库
|
||||
│ ├── api // 服务端接口
|
||||
│ ├── components // 自定义功能组件
|
||||
│ ├── config // 配置文件
|
||||
│ ├── helper // 助手函数
|
||||
│ ├── hooks // vue-hooks
|
||||
│ ├── libs // 自定义依赖
|
||||
│ ├── platform // 第三方平台登录、分享、支付
|
||||
│ ├── request // 请求类库
|
||||
│ ├── router // 自定义路由跳转
|
||||
│ ├── scss // 主样式库
|
||||
│ ├── store // pinia状态管理模块
|
||||
│ ├── ui // 自定义UI组件
|
||||
│ ├── url // cdn图片地址格式化
|
||||
│ ├── validate // 通用验证器
|
||||
│ ├── index.js // Shopro入口文件
|
||||
├── uni_modules // dcloud第三方插件
|
||||
|
||||
```
|
||||
|
||||
|
||||
## 更新
|
||||
|
||||
### 近期计划
|
||||
|
||||
- [ ] Typescript 重构;
|
||||
|
||||
### V1.8.3 更新简介 2023/10/25
|
||||
1. 对接微信小程序发货管理
|
||||
2. 修复路由模式为history时,微信公众号使用微信登录时跳转白屏bug
|
||||
|
||||
### V1.8.2 更新简介 2023/09/4
|
||||
1. 添加 图片热区组件
|
||||
2. 添加 商品评论商家回复功能
|
||||
3. 优化 购物车性能
|
||||
4. 优化 搜索组件
|
||||
5. 优化 动态添加直播组件
|
||||
6. 优化 轮播图组件
|
||||
7. 优化 微信小程序订阅消息提醒时机
|
||||
8. 优化 移动小程序端客服bug
|
||||
9. 优化 h5支付拉起微信或者支付宝客户端时,支付单查询过早的问题
|
||||
10. 优化 标题栏组件
|
||||
11. 优化 二级分类组件
|
||||
12. 优化 规格弹框,手动输入数量无法改变数量问题
|
||||
13. 优化 绑定手机号
|
||||
14. 重构 瀑布流商品
|
||||
15. 重构 小程序快捷登录
|
||||
16. 海报图片协议转换,自动识别https协议
|
||||
17. 升级依赖版本
|
||||
|
||||
### V1.8.1 更新简介 2023/03/18
|
||||
|
||||
1. 优化搜索组件
|
||||
|
||||
2. 添加多端直播组件,动态加载直播插件
|
||||
|
||||
3. 添加多种配送方式(货到付款、手动发货)
|
||||
|
||||
4. 添加发货内容详情展示
|
||||
|
||||
5. 优化`radio`点击效果bug
|
||||
|
||||
6. 商品轮播图添加视频播放
|
||||
|
||||
6. 修复部分页面样式显示问题
|
||||
|
||||
|
||||
### V1.8.0 更新简介 2023/02/07
|
||||
|
||||
1. 引入`luch-request`,替换`libs`中的`request`
|
||||
|
||||
2. 兼容`HbulderX`版本更新小程序端`v-bind`无法使用多层对象的问题
|
||||
|
||||
3. 优化分页数据相关页面代码
|
||||
|
||||
4. 富文本渲染组件使用`mp-html`替换原`su-parse`
|
||||
|
||||
5. 修复阶梯拼团弹框点击规格自动关闭问题
|
||||
|
||||
6. 自定义页面头部添加返回按钮及快捷菜单
|
||||
|
||||
7. 优化筛选时间可以任意选择时间问题(改为只能筛选当天及以前)
|
||||
|
||||
8. 修复部分页面样式显示问题
|
||||
|
||||
### V1.7.1 更新简介 2022/12/09
|
||||
|
||||
1. 更新插件市场忽略文件问题
|
||||
|
||||
2. 更改客服聊天图片样式问题
|
||||
|
||||
### V1.5 更新简介 2022/12/07
|
||||
|
||||
- [x] 服务保障icon 变形问题;
|
||||
- [x] 确认订单 可用优惠券逻辑修改;
|
||||
- [x] `su-image`组件中`customStyle`添加`width`属性;
|
||||
|
||||
---
|
||||
|
||||
**<p align="center">如果您觉得我们的开源项目很有帮助,请点击 :star: Star(https://gitee.com/sheepjs/shopro-uniapp.git) 支持 SheepJS 开源团队:heart:</p>**
|
||||
|
||||
---
|
||||
3
androidPrivacy.json
Normal file
3
androidPrivacy.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"prompt" : "template"
|
||||
}
|
||||
17
index.html
Normal file
17
index.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
|
||||
/>
|
||||
<title></title>
|
||||
<!--preload-links-->
|
||||
<!--app-context-->
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"><!--app-html--></div>
|
||||
<script type="module" src="/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
9
jsconfig.json
Normal file
9
jsconfig.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"jsx": "preserve",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
15
main.js
Normal file
15
main.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import App from './App';
|
||||
import { createSSRApp } from 'vue';
|
||||
import { setupPinia } from './sheep/store';
|
||||
|
||||
|
||||
export function createApp() {
|
||||
|
||||
const app = createSSRApp(App);
|
||||
|
||||
setupPinia(app);
|
||||
|
||||
return {
|
||||
app,
|
||||
};
|
||||
}
|
||||
240
manifest.json
Normal file
240
manifest.json
Normal file
@@ -0,0 +1,240 @@
|
||||
{
|
||||
"name": "星品",
|
||||
"appid": "__UNI__082C0BA",
|
||||
"description": "Shopro是由SheepJS团队开发,使用Uniapp+Vue3技术驱动的在线商城系统,内含诸多功能与丰富的活动,期待您的使用和反馈。",
|
||||
"versionName": "1.8.3",
|
||||
"versionCode": 183,
|
||||
"transformPx": false,
|
||||
"app-plus": {
|
||||
"usingComponents": true,
|
||||
"nvueCompiler": "uni-app",
|
||||
"nvueStyleCompiler": "uni-app",
|
||||
"compilerVersion": 3,
|
||||
"nvueLaunchMode": "fast",
|
||||
"splashscreen": {
|
||||
"alwaysShowBeforeRender": true,
|
||||
"waiting": true,
|
||||
"autoclose": true,
|
||||
"delay": 0
|
||||
},
|
||||
"safearea": {
|
||||
"bottom": {
|
||||
"offset": "none"
|
||||
}
|
||||
},
|
||||
"modules": {
|
||||
"Payment": {},
|
||||
"Share": {},
|
||||
"VideoPlayer": {},
|
||||
"OAuth": {}
|
||||
},
|
||||
"distribute": {
|
||||
"android": {
|
||||
"permissions": [
|
||||
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
|
||||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_MOCK_LOCATION\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CALL_PHONE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
||||
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.GET_TASKS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.INTERNET\"/>",
|
||||
"<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_CONTACTS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_SMS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.RECEIVE_BOOT_COMPLETED\"/>",
|
||||
"<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
|
||||
"<uses-permission android:name=\"android.permission.SEND_SMS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.SYSTEM_ALERT_WINDOW\"/>",
|
||||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_CONTACTS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_SMS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.RECEIVE_USER_PRESENT\"/>"
|
||||
],
|
||||
"minSdkVersion": 21,
|
||||
"schemes": "shopro"
|
||||
},
|
||||
"ios": {
|
||||
"urlschemewhitelist": [
|
||||
"baidumap",
|
||||
"iosamap"
|
||||
],
|
||||
"dSYMs": false,
|
||||
"privacyDescription": {
|
||||
"NSPhotoLibraryUsageDescription": "需要同意访问您的相册选取图片才能完善该条目",
|
||||
"NSPhotoLibraryAddUsageDescription": "需要同意访问您的相册才能保存该图片",
|
||||
"NSCameraUsageDescription": "需要同意访问您的摄像头拍摄照片才能完善该条目",
|
||||
"NSUserTrackingUsageDescription": "开启追踪并不会获取您在其它站点的隐私信息,该行为仅用于标识设备,保障服务安全和提升浏览体验"
|
||||
},
|
||||
"urltypes": "shopro",
|
||||
"capabilities": {
|
||||
"entitlements": {
|
||||
"com.apple.developer.associated-domains": [
|
||||
"applinks:shopro.sheepjs.com"
|
||||
]
|
||||
}
|
||||
},
|
||||
"idfa": true
|
||||
},
|
||||
"sdkConfigs": {
|
||||
"speech": {
|
||||
"ifly": {}
|
||||
},
|
||||
"ad": {},
|
||||
"oauth": {
|
||||
"apple": {},
|
||||
"weixin": {
|
||||
"appid": "wxae7a0c156da9383b",
|
||||
"UniversalLinks": "https://shopro.sheepjs.com/uni-universallinks/__UNI__082C0BA/"
|
||||
}
|
||||
},
|
||||
"payment": {
|
||||
"weixin": {
|
||||
"__platform__": [
|
||||
"ios",
|
||||
"android"
|
||||
],
|
||||
"appid": "wxae7a0c156da9383b",
|
||||
"UniversalLinks": "https://shopro.sheepjs.com/uni-universallinks/__UNI__082C0BA/"
|
||||
},
|
||||
"alipay": {
|
||||
"__platform__": [
|
||||
"ios",
|
||||
"android"
|
||||
]
|
||||
}
|
||||
},
|
||||
"share": {
|
||||
"weixin": {
|
||||
"appid": "wxae7a0c156da9383b",
|
||||
"UniversalLinks": "https://shopro.sheepjs.com/uni-universallinks/__UNI__082C0BA/"
|
||||
}
|
||||
}
|
||||
},
|
||||
"orientation": [
|
||||
"portrait-primary"
|
||||
],
|
||||
"splashscreen": {
|
||||
"androidStyle": "common",
|
||||
"iosStyle": "common",
|
||||
"useOriginalMsgbox": true
|
||||
},
|
||||
"icons": {
|
||||
"android": {
|
||||
"hdpi": "unpackage/res/icons/72x72.png",
|
||||
"xhdpi": "unpackage/res/icons/96x96.png",
|
||||
"xxhdpi": "unpackage/res/icons/144x144.png",
|
||||
"xxxhdpi": "unpackage/res/icons/192x192.png"
|
||||
},
|
||||
"ios": {
|
||||
"appstore": "unpackage/res/icons/1024x1024.png",
|
||||
"ipad": {
|
||||
"app": "unpackage/res/icons/76x76.png",
|
||||
"app@2x": "unpackage/res/icons/152x152.png",
|
||||
"notification": "unpackage/res/icons/20x20.png",
|
||||
"notification@2x": "unpackage/res/icons/40x40.png",
|
||||
"proapp@2x": "unpackage/res/icons/167x167.png",
|
||||
"settings": "unpackage/res/icons/29x29.png",
|
||||
"settings@2x": "unpackage/res/icons/58x58.png",
|
||||
"spotlight": "unpackage/res/icons/40x40.png",
|
||||
"spotlight@2x": "unpackage/res/icons/80x80.png"
|
||||
},
|
||||
"iphone": {
|
||||
"app@2x": "unpackage/res/icons/120x120.png",
|
||||
"app@3x": "unpackage/res/icons/180x180.png",
|
||||
"notification@2x": "unpackage/res/icons/40x40.png",
|
||||
"notification@3x": "unpackage/res/icons/60x60.png",
|
||||
"settings@2x": "unpackage/res/icons/58x58.png",
|
||||
"settings@3x": "unpackage/res/icons/87x87.png",
|
||||
"spotlight@2x": "unpackage/res/icons/80x80.png",
|
||||
"spotlight@3x": "unpackage/res/icons/120x120.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"quickapp": {},
|
||||
"quickapp-native": {
|
||||
"icon": "/static/logo.png",
|
||||
"package": "com.example.demo",
|
||||
"features": [
|
||||
{
|
||||
"name": "system.clipboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"quickapp-webview": {
|
||||
"icon": "/static/logo.png",
|
||||
"package": "com.example.demo",
|
||||
"minPlatformVersion": 1070,
|
||||
"versionName": "1.0.0",
|
||||
"versionCode": 100
|
||||
},
|
||||
"mp-weixin": {
|
||||
"appid": "wx43051b2afa4ed3d0",
|
||||
"setting": {
|
||||
"urlCheck": false,
|
||||
"minified": true,
|
||||
"postcss": true
|
||||
},
|
||||
"optimization": {
|
||||
"subPackages": true
|
||||
},
|
||||
"plugins": {},
|
||||
"lazyCodeLoading": "requiredComponents",
|
||||
"usingComponents": {},
|
||||
"permission": {},
|
||||
"requiredPrivateInfos": [
|
||||
"chooseAddress"
|
||||
]
|
||||
},
|
||||
"mp-alipay": {
|
||||
"usingComponents": true
|
||||
},
|
||||
"mp-baidu": {
|
||||
"usingComponents": true
|
||||
},
|
||||
"mp-toutiao": {
|
||||
"usingComponents": true
|
||||
},
|
||||
"mp-jd": {
|
||||
"usingComponents": true
|
||||
},
|
||||
"h5": {
|
||||
"template": "index.html",
|
||||
"router": {
|
||||
"mode": "hash",
|
||||
"base": "./"
|
||||
},
|
||||
"sdkConfigs": {
|
||||
"maps": {}
|
||||
},
|
||||
"async": {
|
||||
"timeout": 20000
|
||||
},
|
||||
"title": "星品购",
|
||||
"optimization": {
|
||||
"treeShaking": {
|
||||
"enable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"vueVersion": "3",
|
||||
"_spaceID": "192b4892-5452-4e1d-9f09-eee1ece40639",
|
||||
"locale": "zh-Hans",
|
||||
"fallbackLocale": "zh-Hans"
|
||||
}
|
||||
105
package.json
Normal file
105
package.json
Normal file
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"id": "shopro",
|
||||
"name": "shopro",
|
||||
"displayName": "星品购",
|
||||
"version": "1.0.1",
|
||||
"description": "Shopro-B2C商城,一套代码,同时发行到iOS、Android、H5、微信小程序多个平台,请使用手机扫码快速体验强大功能",
|
||||
"scripts": {
|
||||
"prettier": "prettier --write \"{pages,sheep}/**/*.{js,json,tsx,css,less,scss,vue,html,md}\""
|
||||
},
|
||||
"repository": "https://github.com/sheepjs/shop.git",
|
||||
"keywords": [
|
||||
"商城",
|
||||
"B2C",
|
||||
"shopro",
|
||||
"商城模板"
|
||||
],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/sheepjs/shop/issues"
|
||||
},
|
||||
"homepage": "https://github.com/dcloudio/hello-uniapp#readme",
|
||||
"dcloudext": {
|
||||
"category": [
|
||||
"前端页面模板",
|
||||
"uni-app前端项目模板"
|
||||
],
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": ""
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "u",
|
||||
"aliyun": "u"
|
||||
},
|
||||
"client": {
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "u"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "y",
|
||||
"IE": "y",
|
||||
"Edge": "y",
|
||||
"Firefox": "y",
|
||||
"Safari": "y"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "u",
|
||||
"百度": "u",
|
||||
"字节跳动": "u",
|
||||
"QQ": "u",
|
||||
"京东": "u"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
},
|
||||
"Vue": {
|
||||
"vue2": "u",
|
||||
"vue3": "y"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@hyoga/uni-socket.io": "^1.0.1",
|
||||
"dayjs": "^1.11.7",
|
||||
"lodash": "^4.17.21",
|
||||
"luch-request": "^3.0.8",
|
||||
"pinia": "^2.0.33",
|
||||
"pinia-plugin-persist-uni": "^1.2.0",
|
||||
"qs-canvas": "^1.0.11",
|
||||
"weixin-js-sdk": "^1.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "^2.8.7",
|
||||
"vconsole": "^3.15.0"
|
||||
}
|
||||
}
|
||||
763
pages.json
Normal file
763
pages.json
Normal file
@@ -0,0 +1,763 @@
|
||||
{
|
||||
"easycom": {
|
||||
"autoscan": true,
|
||||
"custom": {
|
||||
"^s-(.*)": "@/sheep/components/s-$1/s-$1.vue",
|
||||
"^su-(.*)": "@/sheep/ui/su-$1/su-$1.vue"
|
||||
}
|
||||
},
|
||||
"pages": [
|
||||
{
|
||||
"path": "pages/index/index",
|
||||
"aliasPath": "/",
|
||||
"style": {
|
||||
"navigationBarTitleText": "首页",
|
||||
"enablePullDownRefresh": true
|
||||
},
|
||||
"meta": {
|
||||
"auth": false,
|
||||
"sync": true,
|
||||
"title": "首页",
|
||||
"group": "商城"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/index/user",
|
||||
"style": {
|
||||
"navigationBarTitleText": "个人中心",
|
||||
"enablePullDownRefresh": true
|
||||
},
|
||||
"meta": {
|
||||
"sync": true,
|
||||
"title": "个人中心",
|
||||
"group": "商城"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/index/category",
|
||||
"style": {
|
||||
"navigationBarTitleText": "商品分类"
|
||||
},
|
||||
"meta": {
|
||||
"sync": true,
|
||||
"title": "商品分类",
|
||||
"group": "商城"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/index/cart",
|
||||
"style": {
|
||||
"navigationBarTitleText": "购物车"
|
||||
},
|
||||
"meta": {
|
||||
"sync": true,
|
||||
"title": "购物车",
|
||||
"group": "商城"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/index/login",
|
||||
"style": {
|
||||
"navigationBarTitleText": "登录"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/index/search",
|
||||
"style": {
|
||||
"navigationBarTitleText": "搜索"
|
||||
},
|
||||
"meta": {
|
||||
"sync": true,
|
||||
"title": "搜索",
|
||||
"group": "商城"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/index/page",
|
||||
"style": {
|
||||
"navigationBarTitleText": ""
|
||||
},
|
||||
"meta": {
|
||||
"auth": false,
|
||||
"sync": true,
|
||||
"title": "自定义页面",
|
||||
"group": "商城"
|
||||
}
|
||||
}
|
||||
],
|
||||
"subPackages": [
|
||||
{
|
||||
"root": "pages/goods",
|
||||
"pages": [
|
||||
{
|
||||
"path": "index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "商品详情"
|
||||
},
|
||||
"meta": {
|
||||
"sync": true,
|
||||
"title": "普通商品",
|
||||
"group": "商品"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "groupon",
|
||||
"style": {
|
||||
"navigationBarTitleText": "拼团商品"
|
||||
},
|
||||
"meta": {
|
||||
"sync": true,
|
||||
"title": "拼团商品",
|
||||
"group": "商品"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "seckill",
|
||||
"style": {
|
||||
"navigationBarTitleText": "秒杀商品"
|
||||
},
|
||||
"meta": {
|
||||
"sync": true,
|
||||
"title": "秒杀商品",
|
||||
"group": "商品"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "score",
|
||||
"style": {
|
||||
"navigationBarTitleText": "积分商品"
|
||||
},
|
||||
"meta": {
|
||||
"sync": true,
|
||||
"title": "积分商品",
|
||||
"group": "商品"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "商品列表"
|
||||
},
|
||||
"meta": {
|
||||
"sync": true,
|
||||
"title": "商品列表",
|
||||
"group": "商品"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "comment/add",
|
||||
"style": {
|
||||
"navigationBarTitleText": "评价商品"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "comment/list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "商品评价"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/order",
|
||||
"pages": [
|
||||
{
|
||||
"path": "detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "订单详情"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"title": "订单详情"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "confirm",
|
||||
"style": {
|
||||
"navigationBarTitleText": "确认订单"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"title": "确认订单"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的订单",
|
||||
"enablePullDownRefresh": true
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"sync": true,
|
||||
"title": "用户订单",
|
||||
"group": "订单中心"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "invoice",
|
||||
"style": {
|
||||
"navigationBarTitleText": "发票详情"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"title": "发票详情"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "dispatch/content",
|
||||
"style": {
|
||||
"navigationBarTitleText": "发货内容"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"title": "发货内容"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "aftersale/apply",
|
||||
"style": {
|
||||
"navigationBarTitleText": "申请售后"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"title": "申请售后"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "aftersale/list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "售后列表"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"sync": true,
|
||||
"title": "售后订单",
|
||||
"group": "订单中心"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "aftersale/detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "售后详情"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"title": "售后详情"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "aftersale/log",
|
||||
"style": {
|
||||
"navigationBarTitleText": "售后进度"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"title": "售后进度"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "express/log",
|
||||
"style": {
|
||||
"navigationBarTitleText": "物流轨迹"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"title": "物流轨迹"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "express/list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "订单包裹"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"title": "订单包裹"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/user",
|
||||
"pages": [
|
||||
{
|
||||
"path": "info",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的信息"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"sync": true,
|
||||
"title": "用户信息",
|
||||
"group": "用户中心"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "goods-collect",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的收藏"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"sync": true,
|
||||
"title": "商品收藏",
|
||||
"group": "用户中心"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "goods-log",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的足迹"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"sync": true,
|
||||
"title": "浏览记录",
|
||||
"group": "用户中心"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "address/list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "收货地址"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"sync": true,
|
||||
"title": "地址管理",
|
||||
"group": "用户中心"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "address/edit",
|
||||
"style": {
|
||||
"navigationBarTitleText": "编辑地址"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"title": "编辑地址"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "invoice/list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "发票管理"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"sync": true,
|
||||
"title": "发票管理",
|
||||
"group": "用户中心"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "invoice/edit",
|
||||
"style": {
|
||||
"navigationBarTitleText": "编辑发票"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"title": "编辑发票"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "wallet/money",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的余额"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"sync": true,
|
||||
"title": "用户余额",
|
||||
"group": "用户中心"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "wallet/commission",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的佣金"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"sync": true,
|
||||
"title": "用户佣金",
|
||||
"group": "分销中心"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "wallet/score",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的积分"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"sync": true,
|
||||
"title": "用户积分",
|
||||
"group": "用户中心"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/commission",
|
||||
"pages": [
|
||||
{
|
||||
"path": "index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "分销"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"sync": true,
|
||||
"title": "分销中心",
|
||||
"group": "分销商城"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "apply",
|
||||
"style": {
|
||||
"navigationBarTitleText": "申请分销商"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"sync": true,
|
||||
"title": "申请分销商",
|
||||
"group": "分销商城"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "goods",
|
||||
"style": {
|
||||
"navigationBarTitleText": "推广商品"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"sync": true,
|
||||
"title": "推广商品",
|
||||
"group": "分销商城"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "order",
|
||||
"style": {
|
||||
"navigationBarTitleText": "分销订单"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"sync": true,
|
||||
"title": "分销订单",
|
||||
"group": "分销商城"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "share-log",
|
||||
"style": {
|
||||
"navigationBarTitleText": "分享记录"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"sync": true,
|
||||
"title": "分享记录",
|
||||
"group": "分销商城"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "team",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的团队"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"sync": true,
|
||||
"title": "我的团队",
|
||||
"group": "分销商城"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/app",
|
||||
"pages": [
|
||||
{
|
||||
"path": "sign",
|
||||
"style": {
|
||||
"navigationBarTitleText": "签到中心"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"sync": true,
|
||||
"title": "签到中心",
|
||||
"group": "应用"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "score-shop",
|
||||
"style": {
|
||||
"navigationBarTitleText": "积分商城"
|
||||
},
|
||||
"meta": {
|
||||
"auth": false,
|
||||
"sync": true,
|
||||
"title": "积分商城",
|
||||
"group": "应用"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/public",
|
||||
"pages": [
|
||||
{
|
||||
"path": "setting",
|
||||
"style": {
|
||||
"navigationBarTitleText": "系统设置"
|
||||
},
|
||||
"meta": {
|
||||
"sync": true,
|
||||
"title": "系统设置",
|
||||
"group": "通用"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "feedback",
|
||||
"style": {
|
||||
"navigationBarTitleText": "问题反馈"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"sync": true,
|
||||
"title": "问题反馈",
|
||||
"group": "通用"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "richtext",
|
||||
"style": {
|
||||
"navigationBarTitleText": "富文本"
|
||||
},
|
||||
"meta": {
|
||||
"sync": true,
|
||||
"title": "富文本",
|
||||
"group": "通用"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "faq",
|
||||
"style": {
|
||||
"navigationBarTitleText": "常见问题"
|
||||
},
|
||||
"meta": {
|
||||
"sync": true,
|
||||
"title": "常见问题",
|
||||
"group": "通用"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "error",
|
||||
"style": {
|
||||
"navigationBarTitleText": "错误页面"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "webview",
|
||||
"style": {
|
||||
"navigationBarTitleText": ""
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/coupon",
|
||||
"pages": [
|
||||
{
|
||||
"path": "list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "领券中心"
|
||||
},
|
||||
"meta": {
|
||||
"sync": true,
|
||||
"title": "领券中心",
|
||||
"group": "优惠券"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "优惠券"
|
||||
},
|
||||
"meta": {
|
||||
"auth": false,
|
||||
"sync": true,
|
||||
"title": "优惠券详情",
|
||||
"group": "优惠券"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/chat",
|
||||
"pages": [
|
||||
{
|
||||
"path": "index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "客服"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"sync": true,
|
||||
"title": "客服",
|
||||
"group": "客服"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/pay",
|
||||
"pages": [
|
||||
{
|
||||
"path": "index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "收银台"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "result",
|
||||
"style": {
|
||||
"navigationBarTitleText": "支付结果"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "recharge",
|
||||
"style": {
|
||||
"navigationBarTitleText": "充值余额"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"sync": true,
|
||||
"title": "充值余额",
|
||||
"group": "支付"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "recharge-log",
|
||||
"style": {
|
||||
"navigationBarTitleText": "充值记录"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"sync": true,
|
||||
"title": "充值记录",
|
||||
"group": "支付"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "withdraw",
|
||||
"style": {
|
||||
"navigationBarTitleText": "申请提现"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"sync": true,
|
||||
"title": "申请提现",
|
||||
"group": "支付"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "withdraw-log",
|
||||
"style": {
|
||||
"navigationBarTitleText": "提现记录"
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"sync": true,
|
||||
"title": "提现记录",
|
||||
"group": "支付"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/activity",
|
||||
"pages": [
|
||||
{
|
||||
"path": "groupon/detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "拼团详情"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "groupon/order",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的拼团",
|
||||
"enablePullDownRefresh": true
|
||||
},
|
||||
"meta": {
|
||||
"auth": true,
|
||||
"sync": true,
|
||||
"title": "拼团订单",
|
||||
"group": "营销活动"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "营销商品"
|
||||
},
|
||||
"meta": {
|
||||
"sync": true,
|
||||
"title": "营销商品",
|
||||
"group": "营销活动"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "groupon/list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "拼团活动"
|
||||
},
|
||||
"meta": {
|
||||
"sync": true,
|
||||
"title": "拼团活动",
|
||||
"group": "营销活动"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "seckill/list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "秒杀活动"
|
||||
},
|
||||
"meta": {
|
||||
"sync": true,
|
||||
"title": "秒杀活动",
|
||||
"group": "营销活动"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
"navigationBarTextStyle": "black",
|
||||
"navigationBarTitleText": "星品购",
|
||||
"navigationBarBackgroundColor": "#FFFFFF",
|
||||
"backgroundColor": "#FFFFFF",
|
||||
"navigationStyle": "custom"
|
||||
},
|
||||
"tabBar": {
|
||||
"list": [
|
||||
{
|
||||
"pagePath": "pages/index/index"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/index/cart"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/index/user"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
507
pages/activity/groupon/detail.vue
Normal file
507
pages/activity/groupon/detail.vue
Normal file
@@ -0,0 +1,507 @@
|
||||
<template>
|
||||
<s-layout title="拼团详情" class="detail-wrap" :navbar="state.data && !state.loading ? 'inner': 'normal'" :onShareAppMessage="shareInfo">
|
||||
<view v-if="state.loading"></view>
|
||||
<view v-if="state.data && !state.loading">
|
||||
<view
|
||||
class="recharge-box"
|
||||
v-if="state.data.goods"
|
||||
:style="[
|
||||
{
|
||||
marginTop: '-' + Number(statusBarHeight + 88) + 'rpx',
|
||||
paddingTop: Number(statusBarHeight + 108) + 'rpx',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<s-goods-item
|
||||
class="goods-box"
|
||||
:img="state.data.goods.image"
|
||||
:title="state.data.goods.title"
|
||||
:price="state.data.goods.price[0]"
|
||||
priceColor="#E1212B"
|
||||
@tap="
|
||||
sheep.$router.go('/pages/goods/groupon', {
|
||||
id: state.data.goods.id,
|
||||
activity_id: state.data.goods.activity.id,
|
||||
})
|
||||
"
|
||||
:style="[{ top: Number(statusBarHeight + 108) + 'rpx' }]"
|
||||
>
|
||||
<template #groupon>
|
||||
<view class="ss-flex">
|
||||
<view class="sales-title">{{ state.data.num }}人团</view>
|
||||
<view class="num-title ss-m-l-20">已拼{{ state.data.goods.sales }}件</view>
|
||||
</view>
|
||||
</template>
|
||||
</s-goods-item>
|
||||
</view>
|
||||
<view class="countdown-box detail-card ss-p-t-44 ss-flex-col ss-col-center">
|
||||
<view v-if="state.data.status === 'finish' || state.data.status === 'finish_fictitious'">
|
||||
<view v-if="state.data.my">
|
||||
<view class="countdown-title ss-flex">
|
||||
<text class="cicon-check-round"></text>
|
||||
恭喜您~拼团成功
|
||||
</view>
|
||||
</view>
|
||||
<view v-else>
|
||||
<view class="countdown-title ss-flex">
|
||||
<text class="cicon-info"></text>
|
||||
抱歉~该团已满员
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="state.data.status === 'invalid'">
|
||||
<view class="countdown-title ss-flex">
|
||||
<text class="cicon-info"></text>
|
||||
{{ state.data.my ? '拼团超时,已自动退款' : '该团已解散' }}
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="state.data.status === 'ing'">
|
||||
<!-- TODO: 拼团进行中+活动结束-->
|
||||
<view v-if="state.data.activity_status === 'ended'">
|
||||
<view class="countdown-title ss-flex">
|
||||
<text class="cicon-info"></text>
|
||||
拼团已结束,请关注下次活动
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="countdown-title ss-flex" v-if="state.data.activity_status === 'ing'">
|
||||
还差
|
||||
<view class="num">{{ state.data.num - state.data.current_num }}人</view>
|
||||
拼团成功
|
||||
<view class="ss-flex countdown-time">
|
||||
<view class="countdown-h ss-flex ss-row-center">{{ endTime.h }}</view>
|
||||
<view class="ss-m-x-4">:</view>
|
||||
<view class="countdown-num ss-flex ss-row-center">
|
||||
{{ endTime.m }}
|
||||
</view>
|
||||
<view class="ss-m-x-4">:</view>
|
||||
<view class="countdown-num ss-flex ss-row-center">
|
||||
{{ endTime.s }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="ss-m-t-60 ss-flex ss-flex-wrap ss-row-center">
|
||||
<view
|
||||
class="header-avatar ss-m-r-24 ss-m-b-20"
|
||||
v-for="item in state.data.groupon_logs"
|
||||
:key="item.id"
|
||||
>
|
||||
<image :src="sheep.$url.cdn(item.avatar)" class="avatar-img"></image>
|
||||
<view
|
||||
class="header-tag ss-flex ss-col-center ss-row-center"
|
||||
v-if="item.is_leader == '1'"
|
||||
>
|
||||
团长
|
||||
</view>
|
||||
</view>
|
||||
<view class="default-avatar ss-m-r-24 ss-m-b-20" v-for="item in state.number" :key="item">
|
||||
<image
|
||||
:src="sheep.$url.static('/static/img/shop/avatar/unknown.png')"
|
||||
class="avatar-img"
|
||||
></image>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
class="detail-cell-wrap ss-flex ss-col-center ss-row-between"
|
||||
v-if="state.data.activity?.richtext_id > 0"
|
||||
@tap="
|
||||
sheep.$router.go('/pages/public/richtext', {
|
||||
id: state.data.activity.richtext_id,
|
||||
title: state.data.activity.richtext_title,
|
||||
})
|
||||
"
|
||||
>
|
||||
<view class="label-text">玩法</view>
|
||||
<view class="ss-flex">
|
||||
<view class="cell-content ss-line-1 ss-flex-1">
|
||||
{{ state.data.activity?.richtext_title }}
|
||||
</view>
|
||||
<button class="ss-reset-button">
|
||||
<text class="_icon-forward right-forwrad-icon"></text>
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
v-if="
|
||||
state.data.status == 'finish' ||
|
||||
state.data.status == 'finish_fictitious' ||
|
||||
state.data.status == 'invalid'
|
||||
"
|
||||
class="ss-m-t-40 ss-flex ss-row-center"
|
||||
>
|
||||
<button
|
||||
class="ss-reset-button order-btn"
|
||||
v-if="state.data.my"
|
||||
@tap="onDetail(state.data.my.order_id)"
|
||||
>
|
||||
查看订单
|
||||
</button>
|
||||
<button class="ss-reset-button join-btn" v-else @tap="onCreateGroupon"> 我要开团 </button>
|
||||
</view>
|
||||
<view v-if="state.data.status === 'ing'" class="ss-m-t-40 ss-flex ss-row-center">
|
||||
<view v-if="state.data.activity_status === 'ended'">
|
||||
<button
|
||||
class="ss-reset-button join-btn"
|
||||
v-if="state.data.my"
|
||||
@tap="onDetail(state.data.my.order_id)"
|
||||
>
|
||||
查看订单
|
||||
</button>
|
||||
<button
|
||||
class="ss-reset-button disabled-btn"
|
||||
v-else
|
||||
disabled
|
||||
@tap="onDetail(state.data.my.order_id)"
|
||||
>
|
||||
去参团
|
||||
</button>
|
||||
</view>
|
||||
<view v-else class="ss-flex ss-row-center">
|
||||
<view v-if="state.data.my">
|
||||
<button
|
||||
class="ss-reset-button join-btn"
|
||||
:disabled="state.data.activity_status === 'ing' && endTime.ms <= 0"
|
||||
@tap="onShare"
|
||||
>
|
||||
邀请好友来拼团
|
||||
</button>
|
||||
</view>
|
||||
<view v-else>
|
||||
<button
|
||||
class="ss-reset-button join-btn"
|
||||
:disabled="state.data.activity_status === 'ing' && endTime.ms <= 0"
|
||||
@tap="onJoinGroupon()"
|
||||
>
|
||||
立即参团
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="state.data.goods">
|
||||
<s-select-groupon-sku
|
||||
:show="state.showSelectSku"
|
||||
:goodsInfo="state.data.goods"
|
||||
:grouponAction="state.grouponAction"
|
||||
:grouponNum="state.grouponNum"
|
||||
@buy="onBuy"
|
||||
@change="onSkuChange"
|
||||
@close="state.showSelectSku = false"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<s-empty v-if="!state.data && !state.loading" icon="/static/goods-empty.png"> </s-empty>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, reactive } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { useDurationTime } from '@/sheep/hooks/useGoods';
|
||||
import { showShareModal } from '@/sheep/hooks/useModal';
|
||||
import { isEmpty } from 'lodash';
|
||||
|
||||
const headerBg = sheep.$url.css('/static/img/shop/user/withdraw_bg.png');
|
||||
const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
|
||||
const state = reactive({
|
||||
data: {},
|
||||
loading: true,
|
||||
grouponAction: 'create',
|
||||
showSelectSku: false,
|
||||
grouponNum: 0,
|
||||
number: 0,
|
||||
});
|
||||
|
||||
const shareInfo = computed(() => {
|
||||
if (isEmpty(state.data)) return {};
|
||||
return sheep.$platform.share.getShareInfo(
|
||||
{
|
||||
title: state.data.goods.title,
|
||||
image: sheep.$url.cdn(state.data.goods.image),
|
||||
desc: state.data.goods.subtitle,
|
||||
params: {
|
||||
page: '5',
|
||||
query: state.data.id,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'groupon', // 邀请拼团海报
|
||||
title: state.data.goods.title, // 商品标题
|
||||
image: sheep.$url.cdn(state.data.goods.image), // 商品主图
|
||||
price: state.data.goods.price[0], // 商品价格
|
||||
original_price: state.data.goods.original_price, // 商品原价
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
// 订单详情
|
||||
function onDetail(orderId) {
|
||||
sheep.$router.go('/pages/order/detail', {
|
||||
id: orderId,
|
||||
});
|
||||
}
|
||||
|
||||
//去开团
|
||||
function onCreateGroupon() {
|
||||
state.grouponAction = 'create';
|
||||
state.grouponId = 0;
|
||||
state.showSelectSku = true;
|
||||
}
|
||||
|
||||
// 规格变更
|
||||
function onSkuChange(e) {
|
||||
state.selectedSkuPrice = e;
|
||||
}
|
||||
|
||||
// 立即参团
|
||||
function onJoinGroupon() {
|
||||
state.grouponAction = 'join';
|
||||
state.grouponId = state.data.id;
|
||||
state.grouponNum = state.data.num;
|
||||
state.showSelectSku = true;
|
||||
}
|
||||
|
||||
// 立即购买
|
||||
function onBuy(e) {
|
||||
sheep.$router.go('/pages/order/confirm', {
|
||||
data: JSON.stringify({
|
||||
order_type: 'goods',
|
||||
buy_type: 'groupon',
|
||||
activity_id: state.data.activity.id,
|
||||
groupon_id: state.grouponId,
|
||||
groupon_num: state.grouponNum,
|
||||
goods_list: [
|
||||
{
|
||||
goods_id: e.goods_id,
|
||||
goods_num: e.goods_num,
|
||||
goods_sku_price_id: e.id,
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
const endTime = computed(() => {
|
||||
return useDurationTime(state.data.expire_time);
|
||||
});
|
||||
|
||||
// 获取拼团团队详情
|
||||
async function getGrouponDetail(id) {
|
||||
const { error, data } = await sheep.$api.activity.grouponDetail(id);
|
||||
if (error === 0) {
|
||||
state.data = data;
|
||||
let number = Number(state.data.num - state.data.current_num);
|
||||
state.number = number > 0 ? number : 0;
|
||||
} else {
|
||||
state.data = null;
|
||||
}
|
||||
state.loading = false;
|
||||
}
|
||||
|
||||
function onShare() {
|
||||
showShareModal();
|
||||
}
|
||||
|
||||
onLoad((options) => {
|
||||
getGrouponDetail(options.id);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.recharge-box {
|
||||
position: relative;
|
||||
margin-bottom: 120rpx;
|
||||
background: v-bind(headerBg) center/750rpx 100%
|
||||
no-repeat,
|
||||
linear-gradient(115deg, #f44739 0%, #ff6600 100%);
|
||||
border-radius: 0 0 5% 5%;
|
||||
height: 100rpx;
|
||||
|
||||
.goods-box {
|
||||
width: 710rpx;
|
||||
border-radius: 20rpx;
|
||||
position: absolute;
|
||||
left: 20rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.sales-title {
|
||||
height: 32rpx;
|
||||
background: rgba(#ffe0e2, 0.29);
|
||||
border-radius: 16rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
padding: 6rpx 20rpx;
|
||||
color: #f7979c;
|
||||
}
|
||||
|
||||
.num-title {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
|
||||
.countdown-time {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #383a46;
|
||||
.countdown-h {
|
||||
font-size: 24rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
padding: 0 4rpx;
|
||||
margin-left: 16rpx;
|
||||
height: 40rpx;
|
||||
background: linear-gradient(90deg, #ff6000 0%, #fe832a 100%);
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
.countdown-num {
|
||||
font-size: 24rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
background: linear-gradient(90deg, #ff6000 0%, #fe832a 100%);
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.countdown-box {
|
||||
// height: 364rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 10rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
.countdown-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
|
||||
.cicon-check-round {
|
||||
color: #42b111;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.cicon-info {
|
||||
color: #d71e08;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.num {
|
||||
color: #ff6000;
|
||||
}
|
||||
}
|
||||
|
||||
.header-avatar {
|
||||
width: 86rpx;
|
||||
height: 86rpx;
|
||||
background: #ececec;
|
||||
border-radius: 50%;
|
||||
border: 4rpx solid #edc36c;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
|
||||
.avatar-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.header-tag {
|
||||
width: 72rpx;
|
||||
height: 36rpx;
|
||||
font-size: 24rpx;
|
||||
line-height: nor;
|
||||
background: linear-gradient(132deg, #f3dfb1, #f3dfb1, #ecbe60);
|
||||
border-radius: 16rpx;
|
||||
position: absolute;
|
||||
left: 4rpx;
|
||||
top: -36rpx;
|
||||
}
|
||||
}
|
||||
.default-avatar {
|
||||
width: 86rpx;
|
||||
height: 86rpx;
|
||||
background: #ececec;
|
||||
border-radius: 50%;
|
||||
.avatar-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 86rpx;
|
||||
height: 86rpx;
|
||||
background: #ececec;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
.order-btn {
|
||||
width: 668rpx;
|
||||
height: 70rpx;
|
||||
border: 2rpx solid #dfdfdf;
|
||||
border-radius: 35rpx;
|
||||
color: #999999;
|
||||
font-weight: 500;
|
||||
font-size: 26rpx;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.disabled-btn {
|
||||
width: 668rpx;
|
||||
height: 70rpx;
|
||||
background: #dddddd;
|
||||
border-radius: 35rpx;
|
||||
color: #999999;
|
||||
font-weight: 500;
|
||||
font-size: 28rpx;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.join-btn {
|
||||
width: 668rpx;
|
||||
height: 70rpx;
|
||||
background: linear-gradient(90deg, #ff6000 0%, #fe832a 100%);
|
||||
box-shadow: 0px 8rpx 6rpx 0px rgba(255, 104, 4, 0.22);
|
||||
border-radius: 35rpx;
|
||||
color: #fff;
|
||||
font-weight: 500;
|
||||
font-size: 28rpx;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.detail-cell-wrap {
|
||||
width: 100%;
|
||||
padding: 10rpx 20rpx;
|
||||
box-sizing: border-box;
|
||||
border-top: 2rpx solid #dfdfdf;
|
||||
background-color: #fff;
|
||||
// min-height: 60rpx;
|
||||
|
||||
.label-text {
|
||||
font-size: 28rpx;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.cell-content {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: $dark-6;
|
||||
}
|
||||
|
||||
.right-forwrad-icon {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: $dark-9;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
254
pages/activity/groupon/list.vue
Normal file
254
pages/activity/groupon/list.vue
Normal file
@@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<s-layout navbar="inner" :bgStyle="{ color: '#FE832A' }">
|
||||
<view
|
||||
class="page-bg"
|
||||
:style="[{ marginTop: '-' + Number(statusBarHeight + 88) + 'rpx' }]"
|
||||
></view>
|
||||
<view class="list-content">
|
||||
<view class="content-header ss-flex-col ss-col-center ss-row-center">
|
||||
<view class="content-header-title ss-m-b-22 ss-flex ss-row-center">
|
||||
<view>{{ state.activityInfo.title }}</view>
|
||||
<!-- <view class="more">更多</view> -->
|
||||
</view>
|
||||
<view class="content-header-box ss-flex ss-row-center">
|
||||
<view class="countdown-box ss-flex" v-if="endTime?.ms > 0 && state.activityInfo">
|
||||
<view class="countdown-title ss-m-r-12">距结束</view>
|
||||
<view class="ss-flex countdown-time">
|
||||
<view class="ss-flex countdown-h">{{ endTime.h }}</view>
|
||||
<view class="ss-m-x-4">:</view>
|
||||
<view class="countdown-num ss-flex ss-row-center">{{ endTime.m }}</view>
|
||||
<view class="ss-m-x-4">:</view>
|
||||
<view class="countdown-num ss-flex ss-row-center">{{ endTime.s }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="" v-if="endTime?.ms < 0 && state.activityInfo"> 活动已结束 </view>
|
||||
</view>
|
||||
</view>
|
||||
<scroll-view
|
||||
class="scroll-box"
|
||||
:style="{ height: pageHeight + 'rpx' }"
|
||||
scroll-y="true"
|
||||
:scroll-with-animation="false"
|
||||
:enable-back-to-top="true"
|
||||
>
|
||||
<view class="goods-box ss-m-b-20" v-for="item in state.pagination.data" :key="item.id">
|
||||
<s-goods-column
|
||||
class=""
|
||||
size="lg"
|
||||
:data="item"
|
||||
:grouponTag="true"
|
||||
@click="
|
||||
sheep.$router.go('/pages/goods/groupon', {
|
||||
id: item.id,
|
||||
activity_id: state.activityId,
|
||||
})
|
||||
"
|
||||
>
|
||||
<template v-slot:cart>
|
||||
<button class="ss-reset-button cart-btn">去拼团</button>
|
||||
</template>
|
||||
</s-goods-column>
|
||||
</view>
|
||||
<uni-load-more
|
||||
v-if="state.pagination.total > 0"
|
||||
:status="state.loadStatus"
|
||||
:content-text="{
|
||||
contentdown: '上拉加载更多',
|
||||
}"
|
||||
@tap="loadmore"
|
||||
/>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
<script setup>
|
||||
import { reactive, computed } from 'vue';
|
||||
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
|
||||
import sheep from '@/sheep';
|
||||
import _ from 'lodash';
|
||||
import { useDurationTime } from '@/sheep/hooks/useGoods';
|
||||
|
||||
const { screenHeight, safeAreaInsets, screenWidth, safeArea } = sheep.$platform.device;
|
||||
const sys_navBar = sheep.$platform.navbar;
|
||||
const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
|
||||
const pageHeight =
|
||||
(safeArea.height + safeAreaInsets.bottom) * 2 + statusBarHeight - sys_navBar - 350;
|
||||
const headerBg = sheep.$url.css('/static/img/shop/goods/groupon-header.png');
|
||||
|
||||
const state = reactive({
|
||||
activityId: 0,
|
||||
pagination: {
|
||||
data: [],
|
||||
current_page: 1,
|
||||
total: 1,
|
||||
last_page: 1,
|
||||
},
|
||||
loadStatus: '',
|
||||
activityInfo: {},
|
||||
});
|
||||
// 倒计时
|
||||
const endTime = computed(() => {
|
||||
if (state.activityInfo.end_time) {
|
||||
return useDurationTime(state.activityInfo.end_time);
|
||||
}
|
||||
});
|
||||
|
||||
async function getList(activityId, page = 1, list_rows = 4) {
|
||||
state.loadStatus = 'loading';
|
||||
const res = await sheep.$api.goods.activityList({
|
||||
list_rows,
|
||||
activity_id: activityId,
|
||||
page,
|
||||
});
|
||||
if (res.error === 0) {
|
||||
if (page >= 2) {
|
||||
let couponList = _.concat(state.pagination.data, res.data.data);
|
||||
state.pagination = {
|
||||
...res.data,
|
||||
data: couponList,
|
||||
};
|
||||
} else {
|
||||
state.pagination = res.data;
|
||||
}
|
||||
if (state.pagination.current_page < state.pagination.last_page) {
|
||||
state.loadStatus = 'more';
|
||||
} else {
|
||||
state.loadStatus = 'noMore';
|
||||
}
|
||||
}
|
||||
}
|
||||
async function getActivity(id) {}
|
||||
// 加载更多
|
||||
function loadmore() {
|
||||
if (state.loadStatus !== 'noMore') {
|
||||
getList(state.activityId, state.pagination.current_page + 1);
|
||||
}
|
||||
}
|
||||
// 上拉加载更多
|
||||
onReachBottom(() => {
|
||||
loadmore();
|
||||
});
|
||||
onLoad(async (options) => {
|
||||
if (!options.id) {
|
||||
state.activityInfo = null;
|
||||
return;
|
||||
}
|
||||
state.activityId = options.id;
|
||||
getList(state.activityId);
|
||||
const { error, data } = await sheep.$api.activity.activity(options.id);
|
||||
if (error === 0) {
|
||||
state.activityInfo = data;
|
||||
} else {
|
||||
state.activityInfo = null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.page-bg {
|
||||
width: 100%;
|
||||
height: 458rpx;
|
||||
margin-top: -88rpx;
|
||||
background: v-bind(headerBg) no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
.list-content {
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
margin: -190rpx 20rpx 0 20rpx;
|
||||
background: #fff;
|
||||
border-radius: 20rpx 20rpx 0 0;
|
||||
.content-header {
|
||||
width: 100%;
|
||||
border-radius: 20rpx 20rpx 0 0;
|
||||
height: 150rpx;
|
||||
background: linear-gradient(180deg, #fff4f7, #ffe4d1);
|
||||
.content-header-title {
|
||||
width: 100%;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
color: #ff2923;
|
||||
line-height: 30rpx;
|
||||
position: relative;
|
||||
.more {
|
||||
position: absolute;
|
||||
right: 30rpx;
|
||||
top: 0;
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #999999;
|
||||
line-height: 30rpx;
|
||||
}
|
||||
}
|
||||
.content-header-box {
|
||||
width: 678rpx;
|
||||
height: 64rpx;
|
||||
background: rgba($color: #fff, $alpha: 0.66);
|
||||
border-radius: 32px;
|
||||
.num {
|
||||
font-size: 24rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
color: #f51c11;
|
||||
line-height: 30rpx;
|
||||
}
|
||||
.title {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
font-family: OPPOSANS;
|
||||
color: #333;
|
||||
line-height: 30rpx;
|
||||
}
|
||||
.countdown-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
line-height: 28rpx;
|
||||
}
|
||||
|
||||
.countdown-time {
|
||||
font-size: 28rpx;
|
||||
color: rgba(#ed3c30, 0.23);
|
||||
.countdown-h {
|
||||
font-size: 24rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
padding: 0 4rpx;
|
||||
height: 40rpx;
|
||||
background: rgba(#ed3c30, 0.23);
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
.countdown-num {
|
||||
font-size: 24rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
background: rgba(#ed3c30, 0.23);
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.scroll-box {
|
||||
height: 900rpx;
|
||||
.goods-box {
|
||||
position: relative;
|
||||
.cart-btn {
|
||||
position: absolute;
|
||||
bottom: 10rpx;
|
||||
right: 20rpx;
|
||||
z-index: 11;
|
||||
height: 50rpx;
|
||||
line-height: 50rpx;
|
||||
padding: 0 20rpx;
|
||||
border-radius: 25rpx;
|
||||
font-size: 24rpx;
|
||||
color: #fff;
|
||||
background: linear-gradient(90deg, #ff6600 0%, #fe832a 100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
298
pages/activity/groupon/order.vue
Normal file
298
pages/activity/groupon/order.vue
Normal file
@@ -0,0 +1,298 @@
|
||||
<!-- 页面 -->
|
||||
<template>
|
||||
<s-layout title="我的拼团">
|
||||
<su-sticky bgColor="#fff">
|
||||
<su-tabs
|
||||
:list="tabMaps"
|
||||
:scrollable="false"
|
||||
@change="onTabsChange"
|
||||
:current="state.currentTab"
|
||||
></su-tabs>
|
||||
</su-sticky>
|
||||
<s-empty v-if="state.pagination.total === 0" icon="/static/goods-empty.png"> </s-empty>
|
||||
<view v-if="state.pagination.total > 0">
|
||||
<view
|
||||
class="order-list-card-box bg-white ss-r-10 ss-m-t-14 ss-m-20"
|
||||
v-for="order in state.pagination.data"
|
||||
:key="order.id"
|
||||
>
|
||||
<view class="order-card-header ss-flex ss-col-center ss-row-between ss-p-x-20">
|
||||
<view class="order-no">订单号:{{ order.my.order.order_sn }}</view>
|
||||
<view
|
||||
class="ss-font-26"
|
||||
:class="
|
||||
order.status === 'ing'
|
||||
? 'warning-color'
|
||||
: order.status === 'invalid'
|
||||
? 'danger-color'
|
||||
: 'success-color'
|
||||
"
|
||||
>{{ order.status_text }}</view
|
||||
>
|
||||
</view>
|
||||
<view class="border-bottom">
|
||||
<s-goods-item
|
||||
:img="order.goods.image"
|
||||
:title="order.goods.title"
|
||||
:price="order.goods.price[0]"
|
||||
priceColor="#E1212B"
|
||||
radius="20"
|
||||
>
|
||||
<template #groupon>
|
||||
<view class="ss-flex">
|
||||
<view class="sales-title"> {{ order.num }}人团 </view>
|
||||
<!-- <view class="num-title ss-m-l-20"> 已拼{{ order.goods.sales }}件 </view> -->
|
||||
</view>
|
||||
</template>
|
||||
</s-goods-item>
|
||||
</view>
|
||||
<view class="order-card-footer ss-flex ss-row-right ss-p-x-20">
|
||||
<button
|
||||
class="detail-btn ss-reset-button"
|
||||
@tap="sheep.$router.go('/pages/order/detail', { id: order.my.order_id })"
|
||||
>
|
||||
订单详情
|
||||
</button>
|
||||
<button
|
||||
class="tool-btn ss-reset-button"
|
||||
:class="{ 'ui-BG-Main-Gradient': order.status === 'ing' }"
|
||||
@tap="sheep.$router.go('/pages/activity/groupon/detail', { id: order.id })"
|
||||
>
|
||||
{{ order.status === 'ing' ? '邀请拼团' : '拼团详情' }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<uni-load-more
|
||||
v-if="state.pagination.total > 0"
|
||||
:status="state.loadStatus"
|
||||
:content-text="{
|
||||
contentdown: '上拉加载更多',
|
||||
}"
|
||||
@tap="loadmore"
|
||||
/>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, reactive } from 'vue';
|
||||
import { onLoad, onReachBottom, onPullDownRefresh } from '@dcloudio/uni-app';
|
||||
import sheep from '@/sheep';
|
||||
import _ from 'lodash';
|
||||
|
||||
// 数据
|
||||
const state = reactive({
|
||||
currentTab: 0,
|
||||
pagination: {
|
||||
data: [],
|
||||
current_page: 1,
|
||||
total: 1,
|
||||
last_page: 1,
|
||||
},
|
||||
loadStatus: '',
|
||||
deleteOrderId: 0,
|
||||
});
|
||||
|
||||
const tabMaps = [
|
||||
{
|
||||
name: '全部',
|
||||
value: 'all',
|
||||
},
|
||||
{
|
||||
name: '进行中',
|
||||
value: 'ing',
|
||||
},
|
||||
{
|
||||
name: '拼团成功',
|
||||
value: 'finish',
|
||||
},
|
||||
{
|
||||
name: '拼团失败',
|
||||
value: 'invalid',
|
||||
},
|
||||
];
|
||||
|
||||
// 切换选项卡
|
||||
function onTabsChange(e) {
|
||||
state.pagination = {
|
||||
data: [],
|
||||
current_page: 1,
|
||||
total: 1,
|
||||
last_page: 1,
|
||||
};
|
||||
state.currentTab = e.index;
|
||||
getGrouponList();
|
||||
}
|
||||
|
||||
// 订单详情
|
||||
function onDetail(orderSN) {
|
||||
sheep.$router.go('/pages/order/detail', {
|
||||
orderSN,
|
||||
});
|
||||
}
|
||||
|
||||
// 继续支付
|
||||
function onPay(orderSN) {
|
||||
sheep.$router.go('/pages/pay/index', {
|
||||
orderSN,
|
||||
});
|
||||
}
|
||||
|
||||
// 评价
|
||||
function onComment(orderSN) {
|
||||
sheep.$router.go('/pages/order/comment/add', {
|
||||
orderSN,
|
||||
});
|
||||
}
|
||||
|
||||
// 确认收货
|
||||
async function onConfirm(orderId) {
|
||||
const { error, data } = await sheep.$api.order.confirm(orderId);
|
||||
if (error === 0) {
|
||||
let index = state.pagination.data.findIndex((order) => order.id === orderId);
|
||||
state.pagination.data[index] = data;
|
||||
}
|
||||
}
|
||||
|
||||
// 取消订单
|
||||
async function onCancel(orderId) {
|
||||
const { error, data } = await sheep.$api.order.cancel(orderId);
|
||||
if (error === 0) {
|
||||
let index = state.pagination.data.findIndex((order) => order.id === orderId);
|
||||
state.pagination.data[index] = data;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取订单列表
|
||||
async function getGrouponList(page = 1, list_rows = 5) {
|
||||
state.loadStatus = 'loading';
|
||||
let res = await sheep.$api.activity.myGroupon({
|
||||
type: tabMaps[state.currentTab].value,
|
||||
});
|
||||
if (res.error === 0) {
|
||||
if (page >= 2) {
|
||||
let orderList = _.concat(state.pagination.data, res.data.data);
|
||||
state.pagination = {
|
||||
...res.data,
|
||||
data: orderList,
|
||||
};
|
||||
} else {
|
||||
state.pagination = res.data;
|
||||
}
|
||||
|
||||
if (state.pagination.current_page < state.pagination.last_page) {
|
||||
state.loadStatus = 'more';
|
||||
} else {
|
||||
state.loadStatus = 'noMore';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onLoad((options) => {
|
||||
if (options.type) {
|
||||
state.currentTab = options.type;
|
||||
}
|
||||
getGrouponList();
|
||||
});
|
||||
|
||||
// 加载更多
|
||||
function loadmore() {
|
||||
if (state.loadStatus !== 'noMore') {
|
||||
getGrouponList(state.pagination.current_page + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 上拉加载更多
|
||||
onReachBottom(() => {
|
||||
loadmore();
|
||||
});
|
||||
//下拉刷新
|
||||
onPullDownRefresh(() => {
|
||||
getGrouponList();
|
||||
setTimeout(function () {
|
||||
uni.stopPullDownRefresh();
|
||||
}, 800);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.swiper-box {
|
||||
flex: 1;
|
||||
|
||||
.swiper-item {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.order-list-card-box {
|
||||
.order-card-header {
|
||||
height: 80rpx;
|
||||
|
||||
.order-no {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.order-card-footer {
|
||||
height: 100rpx;
|
||||
|
||||
.detail-btn {
|
||||
width: 210rpx;
|
||||
height: 66rpx;
|
||||
border: 2rpx solid #dfdfdf;
|
||||
border-radius: 33rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
color: #999999;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
.tool-btn {
|
||||
width: 210rpx;
|
||||
height: 66rpx;
|
||||
border-radius: 33rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
margin-right: 20rpx;
|
||||
background: #f6f6f6;
|
||||
}
|
||||
|
||||
.invite-btn {
|
||||
width: 210rpx;
|
||||
height: 66rpx;
|
||||
background: linear-gradient(90deg, #fe832a, #ff6600);
|
||||
box-shadow: 0px 8rpx 6rpx 0px rgba(255, 104, 4, 0.22);
|
||||
border-radius: 33rpx;
|
||||
color: #fff;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sales-title {
|
||||
height: 32rpx;
|
||||
background: rgba(#ffe0e2, 0.29);
|
||||
border-radius: 16rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
padding: 6rpx 20rpx;
|
||||
color: #f7979c;
|
||||
}
|
||||
|
||||
.num-title {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #999999;
|
||||
}
|
||||
.warning-color {
|
||||
color: #faad14;
|
||||
}
|
||||
.danger-color {
|
||||
color: #ff3000;
|
||||
}
|
||||
.success-color {
|
||||
color: #52c41a;
|
||||
}
|
||||
</style>
|
||||
191
pages/activity/index.vue
Normal file
191
pages/activity/index.vue
Normal file
@@ -0,0 +1,191 @@
|
||||
<template>
|
||||
<s-layout class="activity-wrap" :title="state.activityInfo.title">
|
||||
<su-sticky bgColor="#fff">
|
||||
<view class="ss-flex ss-col-top tip-box">
|
||||
<view class="type-text ss-flex ss-row-center">{{ state.activityInfo.type_text }}:</view>
|
||||
<view class="ss-flex-1">
|
||||
<view class="tip-content" v-for="item in state.activityInfo.texts" :key="item">
|
||||
{{ item }}
|
||||
</view>
|
||||
</view>
|
||||
<image class="activity-left-image" src="/static/activity-left.png" />
|
||||
<image class="activity-right-image" src="/static/activity-right.png" />
|
||||
</view>
|
||||
</su-sticky>
|
||||
|
||||
<view class="ss-flex ss-flex-wrap ss-p-x-20 ss-m-t-20 ss-col-top">
|
||||
<view class="goods-list-box">
|
||||
<view class="left-list" v-for="item in state.leftGoodsList" :key="item.id">
|
||||
<s-goods-column
|
||||
class="goods-md-box"
|
||||
size="md"
|
||||
:data="item"
|
||||
@click="sheep.$router.go('/pages/goods/index', { id: item.id })"
|
||||
@getHeight="mountMasonry($event, 'left')"
|
||||
>
|
||||
<template v-slot:cart>
|
||||
<button class="ss-reset-button cart-btn"> </button>
|
||||
</template>
|
||||
</s-goods-column>
|
||||
</view>
|
||||
</view>
|
||||
<view class="goods-list-box">
|
||||
<view class="right-list" v-for="item in state.rightGoodsList" :key="item.id">
|
||||
<s-goods-column
|
||||
class="goods-md-box"
|
||||
size="md"
|
||||
:data="item"
|
||||
@click="sheep.$router.go('/pages/goods/index', { id: item.id })"
|
||||
@getHeight="mountMasonry($event, 'right')"
|
||||
>
|
||||
<template v-slot:cart>
|
||||
<button class="ss-reset-button cart-btn"> </button>
|
||||
</template>
|
||||
</s-goods-column>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<uni-load-more
|
||||
v-if="state.pagination.total > 0"
|
||||
:status="state.loadStatus"
|
||||
:content-text="{
|
||||
contentdown: '上拉加载更多',
|
||||
}"
|
||||
@tap="loadmore"
|
||||
/>
|
||||
</s-layout>
|
||||
</template>
|
||||
<script setup>
|
||||
import { reactive } from 'vue';
|
||||
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
|
||||
import sheep from '@/sheep';
|
||||
import _ from 'lodash';
|
||||
|
||||
const state = reactive({
|
||||
pagination: {
|
||||
data: [],
|
||||
current_page: 1,
|
||||
total: 1,
|
||||
last_page: 1,
|
||||
},
|
||||
loadStatus: '',
|
||||
leftGoodsList: [],
|
||||
rightGoodsList: [],
|
||||
activityId: 0,
|
||||
activityInfo: {},
|
||||
});
|
||||
// 加载瀑布流
|
||||
let count = 0;
|
||||
let leftHeight = 0;
|
||||
let rightHeight = 0;
|
||||
|
||||
function mountMasonry(height = 0, where = 'left') {
|
||||
if (!state.pagination.data[count]) return;
|
||||
|
||||
if (where === 'left') {
|
||||
leftHeight += height;
|
||||
} else {
|
||||
rightHeight += height;
|
||||
}
|
||||
if (leftHeight <= rightHeight) {
|
||||
state.leftGoodsList.push(state.pagination.data[count]);
|
||||
} else {
|
||||
state.rightGoodsList.push(state.pagination.data[count]);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
async function getList(activityId, page = 1, list_rows = 6) {
|
||||
state.loadStatus = 'loading';
|
||||
const res = await sheep.$api.goods.activityList({
|
||||
list_rows,
|
||||
activity_id: activityId,
|
||||
page,
|
||||
});
|
||||
if (res.error === 0) {
|
||||
if (page >= 2) {
|
||||
let couponList = _.concat(state.pagination.data, res.data.data);
|
||||
state.pagination = {
|
||||
...res.data,
|
||||
data: couponList,
|
||||
};
|
||||
} else {
|
||||
state.pagination = res.data;
|
||||
}
|
||||
mountMasonry();
|
||||
if (state.pagination.current_page < state.pagination.last_page) {
|
||||
state.loadStatus = 'more';
|
||||
} else {
|
||||
state.loadStatus = 'noMore';
|
||||
}
|
||||
}
|
||||
}
|
||||
async function getActivity(id) {
|
||||
const { error, data } = await sheep.$api.activity.activity(id);
|
||||
if (error === 0) {
|
||||
state.activityInfo = data;
|
||||
}
|
||||
}
|
||||
// 加载更多
|
||||
function loadmore() {
|
||||
if (state.loadStatus !== 'noMore') {
|
||||
getList(state.activityId, state.pagination.current_page + 1);
|
||||
}
|
||||
}
|
||||
// 上拉加载更多
|
||||
onReachBottom(() => {
|
||||
loadmore();
|
||||
});
|
||||
onLoad((options) => {
|
||||
state.activityId = options.activityId;
|
||||
getList(state.activityId);
|
||||
getActivity(state.activityId);
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.goods-list-box {
|
||||
width: 50%;
|
||||
box-sizing: border-box;
|
||||
.left-list {
|
||||
margin-right: 10rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
.right-list {
|
||||
margin-left: 10rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
}
|
||||
.tip-box {
|
||||
background: #fff0e7;
|
||||
padding: 20rpx;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
.activity-left-image {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 58rpx;
|
||||
height: 36rpx;
|
||||
}
|
||||
.activity-right-image {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 72rpx;
|
||||
height: 50rpx;
|
||||
}
|
||||
.type-text {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #ff6000;
|
||||
line-height: 42rpx;
|
||||
}
|
||||
.tip-content {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #ff6000;
|
||||
line-height: 42rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
249
pages/activity/seckill/list.vue
Normal file
249
pages/activity/seckill/list.vue
Normal file
@@ -0,0 +1,249 @@
|
||||
<template>
|
||||
<s-layout navbar="inner" :bgStyle="{ color: 'rgb(245,28,19)' }">
|
||||
<view
|
||||
class="page-bg"
|
||||
:style="[{ marginTop: '-' + Number(statusBarHeight + 88) + 'rpx' }]"
|
||||
></view>
|
||||
<view class="list-content">
|
||||
<view class="content-header ss-flex-col ss-col-center ss-row-center">
|
||||
<view class="content-header-title ss-m-b-22 ss-flex ss-row-center">
|
||||
<view>{{ state.activityInfo.title }}</view>
|
||||
<!-- <view class="more">更多</view> -->
|
||||
</view>
|
||||
<view class="content-header-box ss-flex ss-row-center">
|
||||
<view class="countdown-box ss-flex" v-if="endTime?.ms > 0 && state.activityInfo">
|
||||
<view class="countdown-title ss-m-r-12">距结束</view>
|
||||
<view class="ss-flex countdown-time">
|
||||
<view class="ss-flex countdown-h">{{ endTime.h }}</view>
|
||||
<view class="ss-m-x-4">:</view>
|
||||
<view class="countdown-num ss-flex ss-row-center">{{ endTime.m }}</view>
|
||||
<view class="ss-m-x-4">:</view>
|
||||
<view class="countdown-num ss-flex ss-row-center">{{ endTime.s }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="" v-if="endTime?.ms < 0 && state.activityInfo"> 活动已结束 </view>
|
||||
</view>
|
||||
</view>
|
||||
<scroll-view
|
||||
class="scroll-box"
|
||||
:style="{ height: pageHeight + 'rpx' }"
|
||||
scroll-y="true"
|
||||
:scroll-with-animation="false"
|
||||
:enable-back-to-top="true"
|
||||
>
|
||||
<view class="goods-box ss-m-b-20" v-for="item in state.pagination.data" :key="item.id">
|
||||
<s-goods-column
|
||||
class=""
|
||||
size="lg"
|
||||
:data="item"
|
||||
:seckillTag="true"
|
||||
@click="
|
||||
sheep.$router.go('/pages/goods/seckill', {
|
||||
id: item.id,
|
||||
activity_id: state.activityId,
|
||||
})
|
||||
"
|
||||
>
|
||||
<template v-slot:cart>
|
||||
<button class="ss-reset-button cart-btn">去抢购</button>
|
||||
</template>
|
||||
</s-goods-column>
|
||||
</view>
|
||||
<uni-load-more
|
||||
v-if="state.pagination.total > 0"
|
||||
:status="state.loadStatus"
|
||||
:content-text="{
|
||||
contentdown: '上拉加载更多',
|
||||
}"
|
||||
@tap="loadmore"
|
||||
/>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
<script setup>
|
||||
import { reactive, computed } from 'vue';
|
||||
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
|
||||
import sheep from '@/sheep';
|
||||
import _ from 'lodash';
|
||||
import { useDurationTime } from '@/sheep/hooks/useGoods';
|
||||
|
||||
const { screenHeight, safeAreaInsets, screenWidth, safeArea } = sheep.$platform.device;
|
||||
const sys_navBar = sheep.$platform.navbar;
|
||||
const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
|
||||
const pageHeight =
|
||||
(safeArea.height + safeAreaInsets.bottom) * 2 + statusBarHeight - sys_navBar - 350;
|
||||
const headerBg = sheep.$url.css('/static/img/shop/goods/seckill-header.png');
|
||||
|
||||
const state = reactive({
|
||||
activityId: 0,
|
||||
pagination: {
|
||||
data: [],
|
||||
current_page: 1,
|
||||
total: 1,
|
||||
last_page: 1,
|
||||
},
|
||||
loadStatus: '',
|
||||
activityInfo: {},
|
||||
});
|
||||
// 倒计时
|
||||
const endTime = computed(() => {
|
||||
if (state.activityInfo.end_time) {
|
||||
return useDurationTime(state.activityInfo.end_time);
|
||||
}
|
||||
});
|
||||
|
||||
async function getList(activityId, page = 1, list_rows = 4) {
|
||||
state.loadStatus = 'loading';
|
||||
const res = await sheep.$api.goods.activityList({
|
||||
list_rows,
|
||||
activity_id: activityId,
|
||||
page,
|
||||
});
|
||||
if (res.error === 0) {
|
||||
let couponList = _.concat(state.pagination.data, res.data.data);
|
||||
state.pagination = {
|
||||
...res.data,
|
||||
data: couponList,
|
||||
};
|
||||
if (state.pagination.current_page < state.pagination.last_page) {
|
||||
state.loadStatus = 'more';
|
||||
} else {
|
||||
state.loadStatus = 'noMore';
|
||||
}
|
||||
}
|
||||
}
|
||||
async function getActivity(id) {}
|
||||
// 加载更多
|
||||
function loadmore() {
|
||||
if (state.loadStatus !== 'noMore') {
|
||||
getList(state.activityId, state.pagination.current_page + 1);
|
||||
}
|
||||
}
|
||||
// 上拉加载更多
|
||||
onReachBottom(() => {
|
||||
loadmore();
|
||||
});
|
||||
onLoad(async (options) => {
|
||||
if (!options.id) {
|
||||
state.activityInfo = null;
|
||||
return;
|
||||
}
|
||||
state.activityId = options.id;
|
||||
getList(state.activityId);
|
||||
const { error, data } = await sheep.$api.activity.activity(options.id);
|
||||
if (error === 0) {
|
||||
state.activityInfo = data;
|
||||
} else {
|
||||
state.activityInfo = null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.page-bg {
|
||||
width: 100%;
|
||||
height: 458rpx;
|
||||
background: v-bind(headerBg) no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
.list-content {
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
margin: -190rpx 20rpx 0 20rpx;
|
||||
background: #fff;
|
||||
border-radius: 20rpx 20rpx 0 0;
|
||||
.content-header {
|
||||
width: 100%;
|
||||
border-radius: 20rpx 20rpx 0 0;
|
||||
height: 150rpx;
|
||||
background: linear-gradient(180deg, #fff4f7, #ffe6ec);
|
||||
.content-header-title {
|
||||
width: 100%;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
color: #ff2923;
|
||||
line-height: 30rpx;
|
||||
position: relative;
|
||||
.more {
|
||||
position: absolute;
|
||||
right: 30rpx;
|
||||
top: 0;
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #999999;
|
||||
line-height: 30rpx;
|
||||
}
|
||||
}
|
||||
.content-header-box {
|
||||
width: 678rpx;
|
||||
height: 64rpx;
|
||||
background: rgba($color: #fff, $alpha: 0.66);
|
||||
border-radius: 32px;
|
||||
.num {
|
||||
font-size: 24rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
color: #f51c11;
|
||||
line-height: 30rpx;
|
||||
}
|
||||
.title {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
font-family: OPPOSANS;
|
||||
color: #333;
|
||||
line-height: 30rpx;
|
||||
}
|
||||
.countdown-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
line-height: 28rpx;
|
||||
}
|
||||
|
||||
.countdown-time {
|
||||
font-size: 28rpx;
|
||||
color: rgba(#ed3c30, 0.23);
|
||||
.countdown-h {
|
||||
font-size: 24rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
padding: 0 4rpx;
|
||||
height: 40rpx;
|
||||
background: rgba(#ed3c30, 0.23);
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
.countdown-num {
|
||||
font-size: 24rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
background: rgba(#ed3c30, 0.23);
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.scroll-box {
|
||||
height: 900rpx;
|
||||
.goods-box {
|
||||
position: relative;
|
||||
.cart-btn {
|
||||
position: absolute;
|
||||
bottom: 10rpx;
|
||||
right: 20rpx;
|
||||
z-index: 11;
|
||||
height: 50rpx;
|
||||
line-height: 50rpx;
|
||||
padding: 0 20rpx;
|
||||
border-radius: 25rpx;
|
||||
font-size: 24rpx;
|
||||
color: #fff;
|
||||
background: linear-gradient(90deg, #ff6600 0%, #fe832a 100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
77
pages/app/score-shop.vue
Normal file
77
pages/app/score-shop.vue
Normal file
@@ -0,0 +1,77 @@
|
||||
<!-- 页面 -->
|
||||
<template>
|
||||
<s-layout title="积分商城">
|
||||
<view class="ss-p-20">
|
||||
<view v-for="item in state.pagination.data" :key="item.id" class="ss-m-b-20">
|
||||
<s-score-card
|
||||
size="sl"
|
||||
:data="item"
|
||||
priceColor="#FF3000"
|
||||
@tap="sheep.$router.go('/pages/goods/score', { id: item.id })"
|
||||
></s-score-card>
|
||||
</view>
|
||||
</view>
|
||||
<s-empty
|
||||
v-if="state.pagination.total === 0"
|
||||
icon="/static/goods-empty.png"
|
||||
text="暂无积分商品"
|
||||
></s-empty>
|
||||
<uni-load-more
|
||||
v-if="state.pagination.total > 0"
|
||||
:status="state.loadStatus"
|
||||
:content-text="{
|
||||
contentdown: '上拉加载更多',
|
||||
}"
|
||||
@tap="loadmore"
|
||||
/>
|
||||
</s-layout>
|
||||
</template>
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
|
||||
import { reactive } from 'vue';
|
||||
import _ from 'lodash';
|
||||
|
||||
const state = reactive({
|
||||
pagination: {
|
||||
data: [],
|
||||
current_page: 1,
|
||||
total: 1,
|
||||
last_page: 1,
|
||||
},
|
||||
loadStatus: '',
|
||||
});
|
||||
async function getData(page = 1, list_rows = 5) {
|
||||
state.loadStatus = 'loading';
|
||||
let res = await sheep.$api.app.scoreShop.list({
|
||||
list_rows,
|
||||
page,
|
||||
});
|
||||
if (res.error === 0) {
|
||||
let couponlist = _.concat(state.pagination.data, res.data.data);
|
||||
state.pagination = {
|
||||
...res.data,
|
||||
data: couponlist,
|
||||
};
|
||||
if (state.pagination.current_page < state.pagination.last_page) {
|
||||
state.loadStatus = 'more';
|
||||
} else {
|
||||
state.loadStatus = 'noMore';
|
||||
}
|
||||
}
|
||||
}
|
||||
// 加载更多
|
||||
function loadmore() {
|
||||
if (state.loadStatus !== 'noMore') {
|
||||
getData(state.pagination.current_page + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 上拉加载更多
|
||||
onReachBottom(() => {
|
||||
loadmore();
|
||||
});
|
||||
onLoad(() => {
|
||||
getData();
|
||||
});
|
||||
</script>
|
||||
512
pages/app/sign.vue
Normal file
512
pages/app/sign.vue
Normal file
@@ -0,0 +1,512 @@
|
||||
<!-- 页面 -->
|
||||
<template>
|
||||
<s-layout title="签到有礼">
|
||||
<view v-if="state.loading"></view>
|
||||
<view class="sign-wrap" v-else-if="state.data && !state.loading">
|
||||
<!-- 签到日历 -->
|
||||
<view class="content-box calendar">
|
||||
<view class="sign-everyday ss-flex ss-col-center ss-row-between ss-p-x-30">
|
||||
<text class="sign-everyday-title">签到日历</text>
|
||||
<view class="sign-num-box">
|
||||
已连续签到
|
||||
<text class="sign-num">{{ state.continue_days }}</text>
|
||||
天
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 切换年月 -->
|
||||
<view class="bar ss-flex ss-col-center ss-row-center">
|
||||
<view class="previous" @tap="handleCalendar(0)"><text class="cicon-back"></text></view>
|
||||
<view class="date ss-m-x-20">{{ state.cur_year || '--' }} 年 {{ state.cur_month || '--' }} 月</view>
|
||||
<view class="next" @tap="handleCalendar(1)"><text class="cicon-forward"></text></view>
|
||||
</view>
|
||||
|
||||
<!-- 显示星期 -->
|
||||
<view class="week ss-flex">
|
||||
<view class="week-item ss-flex ss-row-center" v-for="(item, index) in state.weeks_ch" :key="index">
|
||||
{{ item.title }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 日历表 -->
|
||||
<view class="myDateTable">
|
||||
<view v-for="(item, j) in state.data.days" :key="j" class="dateCell ss-flex ss-row-center ss-col-center">
|
||||
<!-- 空格 -->
|
||||
<view class="ss-flex ss-row-center ss-col-center">
|
||||
<text :decode="true"> </text>
|
||||
</view>
|
||||
<view>
|
||||
<!-- 已签到日期 -->
|
||||
<view v-if="item.is_sign" class="is-sign ss-flex ss-row-center">
|
||||
<view class="is-sign-num">{{ item.day < 10 ? '0' + item.day : item.day }}</view>
|
||||
<image class="is-sign-image" :src="sheep.$url.static('/static/img/shop/app/correct.png')">
|
||||
</image>
|
||||
</view>
|
||||
<!-- 未签到日期 -->
|
||||
<view class="is-sign ss-flex ss-row-center" v-if="item.is_replenish == 1"
|
||||
@tap="onShowRetroactive(item.date)">
|
||||
<view class="cell-num">{{ item.day < 10 ? '0' + item.day : item.day }}</view>
|
||||
<text class="cicon-title"></text>
|
||||
</view>
|
||||
<view class="is-sign ss-flex ss-row-center" v-if="item.is_replenish == 0 && !item.is_sign">
|
||||
<view class="cell-num">{{ item.day < 10 ? '0' + item.day : item.day }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 签到按钮 -->
|
||||
<view class="ss-flex ss-col-center ss-row-center sign-box ss-m-y-40">
|
||||
<button class="ss-reset-button sign-btn" v-if="state.isSign === 0" @tap="onSign">签到</button>
|
||||
<button class="ss-reset-button already-btn" v-if="state.isSign === 1" disabled>已签到</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bg-white ss-m-t-16 ss-p-t-30 ss-p-b-60 ss-p-x-40">
|
||||
<view class="activity-title ss-m-b-30">签到说明</view>
|
||||
<view class="activity-des">
|
||||
1、每日签到固定 {{ state.data.rules.everyday }} 积分
|
||||
<text v-if="state.data.rules.is_inc == '1'">
|
||||
,次日递增奖励 {{ state.data.rules.inc_num }} 积分,直到
|
||||
{{ state.data.rules.until_day }} 天之后不再增加
|
||||
</text>
|
||||
</view>
|
||||
<view class="activity-des" v-if="state.data.rules.discounts?.length > 0">
|
||||
2、<text class="" v-for="i in state.data.rules.discounts" :key="i">
|
||||
连续签到 {{ i.full }} 天,奖励 {{ i.value }} 积分;
|
||||
</text>
|
||||
</view>
|
||||
<view class="activity-des" v-if="state.data.rules.is_replenish == '1'">
|
||||
{{ state.data.rules.discounts?.length > 0 ? '3' : '2' }}、用户在
|
||||
{{ state.data.rules.replenish_limit }} 天内,可补签
|
||||
{{ state.data.rules.replenish_days }} 天,每次补签消耗
|
||||
{{ state.data.rules.replenish_num }}积分
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<s-empty v-else-if="!state.data && !state.loading" icon="/static/data-empty.png" text="签到活动还未开始">
|
||||
</s-empty>
|
||||
<su-popup :show="state.showModel" type="center" round="10" :isMaskClick="false">
|
||||
<view class="model-box ss-flex-col">
|
||||
<view class="ss-m-t-56 ss-flex-col ss-col-center">
|
||||
<text class="cicon-check-round"></text>
|
||||
<view class="score-title">{{ state.signin.score }}积分</view>
|
||||
<view class="model-title ss-flex ss-col-center ss-m-t-22 ss-m-b-30">
|
||||
已连续打卡{{ state.continue_days }}天
|
||||
</view>
|
||||
</view>
|
||||
<view class="model-bg ss-flex-col ss-col-center ss-row-right">
|
||||
<view class="title ss-m-b-64">签到成功</view>
|
||||
<view class="ss-m-b-40">
|
||||
<button class="ss-reset-button confirm-btn" @tap="onConfirm">确认</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</su-popup>
|
||||
<su-popup :show="state.showRetroactive" type="center" round="10" :isMaskClick="false">
|
||||
<view class="model-box ss-flex-col">
|
||||
<view class="ss-m-t-56 ss-flex-col ss-col-center">
|
||||
<text class="cicon-check-round"></text>
|
||||
<view class="score-title">消耗{{ state.data?.rules.replenish_num }}积分</view>
|
||||
<view class="model-title ss-flex ss-col-center ss-m-t-22 ss-m-b-30">
|
||||
已连续打卡{{ state.continue_days }}天
|
||||
</view>
|
||||
</view>
|
||||
<view class="model-bg ss-flex-col ss-col-center ss-row-right">
|
||||
<view class="title ss-m-b-64">确认补签</view>
|
||||
<view class="ss-m-b-40 ss-flex">
|
||||
<button class="ss-reset-button cancel-btn" @tap="state.showRetroactive = false">取消</button>
|
||||
<button class="ss-reset-button confirm-btn" @tap="onRetroactive">确认</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</su-popup>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad, onReady } from '@dcloudio/uni-app';
|
||||
import { computed, reactive } from 'vue';
|
||||
|
||||
const headerBg = sheep.$url.css('/static/img/shop/app/sign.png');
|
||||
|
||||
const state = reactive({
|
||||
data: {
|
||||
days: [], //日历
|
||||
rules: {}, //规则
|
||||
},
|
||||
cur_year: 0, //当前选的年
|
||||
cur_month: 0, //当前选的月
|
||||
cur_day: 0, //当前选择的天
|
||||
weeks_ch: [
|
||||
{
|
||||
title: '日',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
title: '一',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
title: '二',
|
||||
value: '2',
|
||||
},
|
||||
{
|
||||
title: '三',
|
||||
value: '3',
|
||||
},
|
||||
{
|
||||
title: '四',
|
||||
value: '4',
|
||||
},
|
||||
{
|
||||
title: '五',
|
||||
value: '5',
|
||||
},
|
||||
{
|
||||
title: '六',
|
||||
value: '6',
|
||||
},
|
||||
], //星期
|
||||
showModel: false, //签到弹框
|
||||
continue_days: 0, //连续签到天数
|
||||
signin: {}, // 签到
|
||||
showRetroactive: false, //补签弹框
|
||||
date: '', //补签选中日期
|
||||
isSign: 0, //今天是否签到
|
||||
loading: true,
|
||||
});
|
||||
async function onSign() {
|
||||
const { error, data } = await sheep.$api.activity.signAdd();
|
||||
if (error === 0) {
|
||||
state.showModel = true;
|
||||
state.signin = data;
|
||||
// getData();
|
||||
}
|
||||
}
|
||||
|
||||
function onShowRetroactive(e) {
|
||||
state.showRetroactive = true;
|
||||
state.date = e;
|
||||
}
|
||||
//签到确认刷新页面
|
||||
function onConfirm() {
|
||||
state.showModel = false;
|
||||
getData();
|
||||
}
|
||||
//补签
|
||||
async function onRetroactive() {
|
||||
const { error, data } = await sheep.$api.activity.replenish({
|
||||
date: state.date,
|
||||
});
|
||||
if (error === 0) {
|
||||
state.showRetroactive = false;
|
||||
getData();
|
||||
}
|
||||
}
|
||||
|
||||
async function getData(mouth) {
|
||||
const { error, data } = await sheep.$api.activity.signList(mouth);
|
||||
if (error === 0) {
|
||||
state.data = data;
|
||||
} else {
|
||||
state.data = null;
|
||||
}
|
||||
state.loading = false;
|
||||
if (state.data) {
|
||||
state.data.days.forEach((i, index) => {
|
||||
if (index < i.week) {
|
||||
index++;
|
||||
var obj = {
|
||||
day: null,
|
||||
is_sign: false,
|
||||
};
|
||||
state.data.days.unshift(obj);
|
||||
}
|
||||
if (index == 1) {
|
||||
let arr = i.date.split('-');
|
||||
state.cur_year = arr[0];
|
||||
state.cur_month = arr[1];
|
||||
}
|
||||
});
|
||||
if (state.data.days[0].day == null) {
|
||||
state.data.days.forEach((i, index) => {
|
||||
if (i.current == 'today') {
|
||||
state.isSign = i.is_sign;
|
||||
}
|
||||
});
|
||||
}
|
||||
state.continue_days = data.continue_days;
|
||||
}
|
||||
}
|
||||
|
||||
onReady(() => {
|
||||
getData();
|
||||
});
|
||||
|
||||
// 切换控制年月,上一个月,下一个月
|
||||
const handleCalendar = (type) => {
|
||||
const cur_year = parseInt(state.cur_year);
|
||||
const cur_month = parseInt(state.cur_month);
|
||||
var newMonth;
|
||||
var newYear = cur_year;
|
||||
if (type === 0) {
|
||||
//上个月
|
||||
newMonth = cur_month - 1;
|
||||
if (newMonth < 1) {
|
||||
newYear = cur_year - 1;
|
||||
newMonth = 12;
|
||||
} else if (newMonth < 10) {
|
||||
newMonth = '0' + newMonth;
|
||||
}
|
||||
} else {
|
||||
newMonth = cur_month + 1;
|
||||
if (newMonth > 12) {
|
||||
newYear = cur_year + 1;
|
||||
newMonth = 1;
|
||||
} else if (newMonth < 10) {
|
||||
newMonth = '0' + newMonth;
|
||||
}
|
||||
}
|
||||
getData({
|
||||
month: newYear + '-' + newMonth,
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.header-box {
|
||||
border-top: 2rpx solid rgba(#dfdfdf, 0.5);
|
||||
}
|
||||
|
||||
// 日历
|
||||
.calendar {
|
||||
background: #fff;
|
||||
|
||||
.sign-everyday {
|
||||
height: 100rpx;
|
||||
background: rgba(255, 255, 255, 1);
|
||||
border: 2rpx solid rgba(223, 223, 223, 0.4);
|
||||
|
||||
.sign-everyday-title {
|
||||
font-size: 32rpx;
|
||||
color: rgba(51, 51, 51, 1);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.sign-num-box {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: rgba(153, 153, 153, 1);
|
||||
|
||||
.sign-num {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #ff6000;
|
||||
padding: 0 10rpx;
|
||||
font-family: OPPOSANS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 年月日
|
||||
.bar {
|
||||
height: 100rpx;
|
||||
|
||||
.date {
|
||||
font-size: 30rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.cicon-back {
|
||||
margin-top: 6rpx;
|
||||
font-size: 30rpx;
|
||||
color: #c4c4c4;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.cicon-forward {
|
||||
margin-top: 6rpx;
|
||||
font-size: 30rpx;
|
||||
color: #c4c4c4;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
// 星期
|
||||
.week {
|
||||
.week-item {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: rgba(153, 153, 153, 1);
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 日历表
|
||||
.myDateTable {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.dateCell {
|
||||
width: calc(750rpx / 7);
|
||||
height: 80rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
color: rgba(51, 51, 51, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.is-sign {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
position: relative;
|
||||
|
||||
.is-sign-num {
|
||||
font-size: 24rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.is-sign-image {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.cell-num {
|
||||
font-size: 24rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.cicon-title {
|
||||
position: absolute;
|
||||
right: -10rpx;
|
||||
top: -6rpx;
|
||||
font-size: 20rpx;
|
||||
color: red;
|
||||
}
|
||||
|
||||
// 签到按钮
|
||||
.sign-box {
|
||||
height: 140rpx;
|
||||
width: 100%;
|
||||
|
||||
.sign-btn {
|
||||
width: 710rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 35rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 0.2em 0.5em rgba(#ff6000, 0.4);
|
||||
background: linear-gradient(90deg, #ff6000, #fe832a);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.already-btn {
|
||||
width: 710rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 35rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.model-box {
|
||||
width: 520rpx;
|
||||
// height: 590rpx;
|
||||
background: linear-gradient(177deg, #ff6000 0%, #fe832a 100%);
|
||||
// background: linear-gradient(177deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
border-radius: 10rpx;
|
||||
|
||||
.cicon-check-round {
|
||||
font-size: 70rpx;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.score-title {
|
||||
font-size: 34rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
color: #fcff00;
|
||||
}
|
||||
|
||||
.model-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.model-bg {
|
||||
width: 520rpx;
|
||||
height: 344rpx;
|
||||
background-size: 100% 100%;
|
||||
background-image: v-bind(headerBg);
|
||||
background-repeat: no-repeat;
|
||||
border-radius: 0 0 10rpx 10rpx;
|
||||
|
||||
.title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
// color: var(--ui-BG-Main);
|
||||
color: #ff6000;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
width: 220rpx;
|
||||
height: 70rpx;
|
||||
border: 2rpx solid #ff6000;
|
||||
border-radius: 35rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #ff6000;
|
||||
line-height: normal;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.confirm-btn {
|
||||
width: 220rpx;
|
||||
height: 70rpx;
|
||||
background: linear-gradient(90deg, #ff6000, #fe832a);
|
||||
box-shadow: 0 0.2em 0.5em rgba(#ff6000, 0.4);
|
||||
border-radius: 35rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//签到说明
|
||||
.activity-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.activity-des {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #666666;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
</style>
|
||||
63
pages/chat/components/goods.vue
Normal file
63
pages/chat/components/goods.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<view class="goods ss-flex">
|
||||
<image class="image" :src="sheep.$url.cdn(goodsData.image)" mode="aspectFill"> </image>
|
||||
<view class="ss-flex-1">
|
||||
<view class="title ss-line-2">
|
||||
{{ goodsData.title }}
|
||||
</view>
|
||||
<view v-if="goodsData.subtitle" class="subtitle ss-line-1">
|
||||
{{ goodsData.subtitle }}
|
||||
</view>
|
||||
<view class="price ss-m-t-8">
|
||||
¥{{ isArray(goodsData.price) ? goodsData.price[0] : goodsData.price }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { isArray } from 'lodash';
|
||||
|
||||
const props = defineProps({
|
||||
goodsData: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goods {
|
||||
background: #fff;
|
||||
padding: 20rpx;
|
||||
border-radius: 12rpx;
|
||||
|
||||
.image {
|
||||
width: 116rpx;
|
||||
height: 116rpx;
|
||||
flex-shrink: 0;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.title {
|
||||
height: 64rpx;
|
||||
line-height: 32rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.price {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #ff3000;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
122
pages/chat/components/order.vue
Normal file
122
pages/chat/components/order.vue
Normal file
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<view class="order">
|
||||
<view class="top ss-flex ss-row-between">
|
||||
<span>{{ orderData.order_sn }}</span>
|
||||
<span>{{ orderData.create_time.split(' ')[1] }}</span>
|
||||
</view>
|
||||
<template v-if="from != 'msg'">
|
||||
<view class="bottom ss-flex" v-for="item in orderData.items" :key="item">
|
||||
<image class="image" :src="sheep.$url.cdn(item.goods_image)" mode="aspectFill"> </image>
|
||||
<view class="ss-flex-1">
|
||||
<view class="title ss-line-2">
|
||||
{{ item.goods_title }}
|
||||
</view>
|
||||
<view v-if="item.goods_num" class="num ss-m-b-10"> 数量:{{ item.goods_num }} </view>
|
||||
<view class="ss-flex ss-row-between ss-m-t-8">
|
||||
<span class="price">¥{{ item.goods_price }}</span>
|
||||
<span class="status">{{ orderData.status_text }}</span>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<template v-else>
|
||||
<view class="bottom ss-flex" v-for="item in [orderData.items[0]]" :key="item">
|
||||
<image class="image" :src="sheep.$url.cdn(item.goods_image)" mode="aspectFill"> </image>
|
||||
<view class="ss-flex-1">
|
||||
<view class="title title-1 ss-line-1">
|
||||
{{ item.goods_title }}
|
||||
</view>
|
||||
<view class="order-total ss-flex ss-row-between ss-m-t-8">
|
||||
<span>共{{ orderData.items.length }}件商品</span>
|
||||
<span>合计 ¥{{ orderData.pay_fee }}</span>
|
||||
</view>
|
||||
<view class="ss-flex ss-row-right ss-m-t-8">
|
||||
<span class="status">{{ orderData.status_text }}</span>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
|
||||
const props = defineProps({
|
||||
from: String,
|
||||
orderData: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.order {
|
||||
background: #fff;
|
||||
padding: 20rpx;
|
||||
border-radius: 12rpx;
|
||||
|
||||
.top {
|
||||
line-height: 40rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #999;
|
||||
border-bottom: 1px solid rgba(223, 223, 223, 0.5);
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.bottom {
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
&:last-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.image {
|
||||
flex-shrink: 0;
|
||||
width: 116rpx;
|
||||
height: 116rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.title {
|
||||
height: 64rpx;
|
||||
line-height: 32rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
|
||||
&.title-1 {
|
||||
height: 32rpx;
|
||||
width: 300rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.num {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.price {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #ff3000;
|
||||
}
|
||||
|
||||
.status {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: var(--ui-BG-Main);
|
||||
}
|
||||
|
||||
.order-total {
|
||||
line-height: 28rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
151
pages/chat/components/select-popup.vue
Normal file
151
pages/chat/components/select-popup.vue
Normal file
@@ -0,0 +1,151 @@
|
||||
<template>
|
||||
<su-popup :show="show" showClose round="10" backgroundColor="#eee" @close="emits('close')">
|
||||
<view class="select-popup">
|
||||
<view class="title">
|
||||
<span>{{ mode == 'goods' ? '我的浏览' : '我的订单' }}</span>
|
||||
</view>
|
||||
<scroll-view
|
||||
class="scroll-box"
|
||||
scroll-y="true"
|
||||
:scroll-with-animation="true"
|
||||
:show-scrollbar="false"
|
||||
@scrolltolower="loadmore"
|
||||
>
|
||||
<view
|
||||
class="item"
|
||||
v-for="item in state.pagination.data"
|
||||
:key="item"
|
||||
@tap="emits('select', { type: mode, data: item })"
|
||||
>
|
||||
<template v-if="mode == 'goods'">
|
||||
<GoodsItem :goodsData="item.goods" />
|
||||
</template>
|
||||
<template v-if="mode == 'order'">
|
||||
<OrderItem :orderData="item" />
|
||||
</template>
|
||||
</view>
|
||||
<uni-load-more :status="state.loadStatus" :content-text="{ contentdown: '上拉加载更多' }" />
|
||||
</scroll-view>
|
||||
</view>
|
||||
</su-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, watch } from 'vue';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import sheep from '@/sheep';
|
||||
import _ from 'lodash';
|
||||
import GoodsItem from './goods.vue';
|
||||
import OrderItem from './order.vue';
|
||||
|
||||
const emits = defineEmits(['select', 'close']);
|
||||
const props = defineProps({
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'goods',
|
||||
},
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.mode,
|
||||
() => {
|
||||
state.pagination.data = [];
|
||||
if (props.mode) {
|
||||
getList(state.pagination.page);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const state = reactive({
|
||||
loadStatus: '',
|
||||
pagination: {
|
||||
data: [],
|
||||
current_page: 1,
|
||||
total: 1,
|
||||
last_page: 1,
|
||||
},
|
||||
});
|
||||
|
||||
async function getList(page, list_rows = 5) {
|
||||
state.loadStatus = 'loading';
|
||||
const res =
|
||||
props.mode == 'goods'
|
||||
? await sheep.$api.user.view.list({
|
||||
page,
|
||||
list_rows,
|
||||
})
|
||||
: await sheep.$api.order.list({
|
||||
page,
|
||||
list_rows,
|
||||
});
|
||||
let orderList = _.concat(state.pagination.data, res.data.data);
|
||||
state.pagination = {
|
||||
...res.data,
|
||||
data: orderList,
|
||||
};
|
||||
if (state.pagination.current_page < state.pagination.last_page) {
|
||||
state.loadStatus = 'more';
|
||||
} else {
|
||||
state.loadStatus = 'noMore';
|
||||
}
|
||||
}
|
||||
|
||||
function loadmore() {
|
||||
if (state.loadStatus !== 'noMore') {
|
||||
getList(state.pagination.current_page + 1);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.select-popup {
|
||||
max-height: 600rpx;
|
||||
|
||||
.title {
|
||||
height: 100rpx;
|
||||
line-height: 100rpx;
|
||||
padding: 0 26rpx;
|
||||
background: #fff;
|
||||
border-radius: 20rpx 20rpx 0 0;
|
||||
|
||||
span {
|
||||
font-size: 32rpx;
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: -15px;
|
||||
background: var(--ui-BG-Main);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-box {
|
||||
height: 500rpx;
|
||||
}
|
||||
|
||||
.item {
|
||||
background: #fff;
|
||||
margin: 26rpx 26rpx 0;
|
||||
border-radius: 20rpx;
|
||||
|
||||
:deep() {
|
||||
.image {
|
||||
width: 140rpx;
|
||||
height: 140rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
58
pages/chat/emoji.js
Normal file
58
pages/chat/emoji.js
Normal file
@@ -0,0 +1,58 @@
|
||||
export const emojiList = [
|
||||
{ name: '[笑掉牙]', file: 'xiaodiaoya.png' },
|
||||
{ name: '[可爱]', file: 'keai.png' },
|
||||
{ name: '[冷酷]', file: 'lengku.png' },
|
||||
{ name: '[闭嘴]', file: 'bizui.png' },
|
||||
{ name: '[生气]', file: 'shengqi.png' },
|
||||
{ name: '[惊恐]', file: 'jingkong.png' },
|
||||
{ name: '[瞌睡]', file: 'keshui.png' },
|
||||
{ name: '[大笑]', file: 'daxiao.png' },
|
||||
{ name: '[爱心]', file: 'aixin.png' },
|
||||
{ name: '[坏笑]', file: 'huaixiao.png' },
|
||||
{ name: '[飞吻]', file: 'feiwen.png' },
|
||||
{ name: '[疑问]', file: 'yiwen.png' },
|
||||
{ name: '[开心]', file: 'kaixin.png' },
|
||||
{ name: '[发呆]', file: 'fadai.png' },
|
||||
{ name: '[流泪]', file: 'liulei.png' },
|
||||
{ name: '[汗颜]', file: 'hanyan.png' },
|
||||
{ name: '[惊悚]', file: 'jingshu.png' },
|
||||
{ name: '[困~]', file: 'kun.png' },
|
||||
{ name: '[心碎]', file: 'xinsui.png' },
|
||||
{ name: '[天使]', file: 'tianshi.png' },
|
||||
{ name: '[晕]', file: 'yun.png' },
|
||||
{ name: '[啊]', file: 'a.png' },
|
||||
{ name: '[愤怒]', file: 'fennu.png' },
|
||||
{ name: '[睡着]', file: 'shuizhuo.png' },
|
||||
{ name: '[面无表情]', file: 'mianwubiaoqing.png' },
|
||||
{ name: '[难过]', file: 'nanguo.png' },
|
||||
{ name: '[犯困]', file: 'fankun.png' },
|
||||
{ name: '[好吃]', file: 'haochi.png' },
|
||||
{ name: '[呕吐]', file: 'outu.png' },
|
||||
{ name: '[龇牙]', file: 'ziya.png' },
|
||||
{ name: '[懵比]', file: 'mengbi.png' },
|
||||
{ name: '[白眼]', file: 'baiyan.png' },
|
||||
{ name: '[饿死]', file: 'esi.png' },
|
||||
{ name: '[凶]', file: 'xiong.png' },
|
||||
{ name: '[感冒]', file: 'ganmao.png' },
|
||||
{ name: '[流汗]', file: 'liuhan.png' },
|
||||
{ name: '[笑哭]', file: 'xiaoku.png' },
|
||||
{ name: '[流口水]', file: 'liukoushui.png' },
|
||||
{ name: '[尴尬]', file: 'ganga.png' },
|
||||
{ name: '[惊讶]', file: 'jingya.png' },
|
||||
{ name: '[大惊]', file: 'dajing.png' },
|
||||
{ name: '[不好意思]', file: 'buhaoyisi.png' },
|
||||
{ name: '[大闹]', file: 'danao.png' },
|
||||
{ name: '[不可思议]', file: 'bukesiyi.png' },
|
||||
{ name: '[爱你]', file: 'aini.png' },
|
||||
{ name: '[红心]', file: 'hongxin.png' },
|
||||
{ name: '[点赞]', file: 'dianzan.png' },
|
||||
{ name: '[恶魔]', file: 'emo.png' },
|
||||
];
|
||||
|
||||
export let emojiPage = {};
|
||||
emojiList.forEach((item, index) => {
|
||||
if (!emojiPage[Math.floor(index / 30) + 1]) {
|
||||
emojiPage[Math.floor(index / 30) + 1] = [];
|
||||
}
|
||||
emojiPage[Math.floor(index / 30) + 1].push(item);
|
||||
});
|
||||
870
pages/chat/index.vue
Normal file
870
pages/chat/index.vue
Normal file
@@ -0,0 +1,870 @@
|
||||
<template>
|
||||
<s-layout class="chat-wrap" title="客服" navbar="inner">
|
||||
<div class="status">
|
||||
{{ socketState.isConnect ? customerServiceInfo.title : '网络已断开,请检查网络后刷新重试' }}
|
||||
</div>
|
||||
<div class="page-bg" :style="{ height: sys_navBar + 'px' }"></div>
|
||||
<view class="chat-box" :style="{ height: pageHeight + 'px' }">
|
||||
<scroll-view
|
||||
:style="{ height: pageHeight + 'px' }"
|
||||
scroll-y="true"
|
||||
:scroll-with-animation="false"
|
||||
:enable-back-to-top="true"
|
||||
:scroll-into-view="chat.scrollInto"
|
||||
>
|
||||
<button
|
||||
class="loadmore-btn ss-reset-button"
|
||||
v-if="
|
||||
chatList.length &&
|
||||
chatHistoryPagination.lastPage > 1 &&
|
||||
loadingMap[chatHistoryPagination.loadStatus].title
|
||||
"
|
||||
@click="onLoadMore"
|
||||
>
|
||||
{{ loadingMap[chatHistoryPagination.loadStatus].title }}
|
||||
<i
|
||||
class="loadmore-icon sa-m-l-6"
|
||||
:class="loadingMap[chatHistoryPagination.loadStatus].icon"
|
||||
></i>
|
||||
</button>
|
||||
<view class="message-item ss-flex-col" v-for="(item, index) in chatList" :key="index">
|
||||
<view class="ss-flex ss-row-center ss-col-center">
|
||||
<!-- 日期 -->
|
||||
<view v-if="item.from !== 'system' && showTime(item, index)" class="date-message">
|
||||
{{ formatTime(item.date) }}
|
||||
</view>
|
||||
<!-- 系统消息 -->
|
||||
<view v-if="item.from === 'system'" class="system-message">
|
||||
{{ item.content.text }}
|
||||
</view>
|
||||
</view>
|
||||
<!-- 常见问题 -->
|
||||
<view v-if="item.mode === 'template' && item.content.list.length" class="template-wrap">
|
||||
<view class="title">猜你想问</view>
|
||||
<view
|
||||
class="item"
|
||||
v-for="(item, index) in item.content.list"
|
||||
:key="index"
|
||||
@click="onTemplateList(item)"
|
||||
>
|
||||
* {{ item.title }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
v-if="
|
||||
(item.from === 'customer_service' && item.mode !== 'template') ||
|
||||
item.from === 'customer'
|
||||
"
|
||||
class="ss-flex ss-col-top"
|
||||
:class="[
|
||||
item.from === 'customer_service'
|
||||
? `ss-row-left`
|
||||
: item.from === 'customer'
|
||||
? `ss-row-right`
|
||||
: '',
|
||||
]"
|
||||
>
|
||||
<!-- 客服头像 -->
|
||||
<image
|
||||
v-show="item.from === 'customer_service'"
|
||||
class="chat-avatar ss-m-r-24"
|
||||
:src="
|
||||
sheep.$url.cdn(item?.sender?.avatar) ||
|
||||
sheep.$url.static('/static/img/shop/chat/default.png')
|
||||
"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
|
||||
<!-- 发送状态 -->
|
||||
<span
|
||||
v-if="
|
||||
item.from === 'customer' &&
|
||||
index == chatData.chatList.length - 1 &&
|
||||
chatData.isSendSucces !== 0
|
||||
"
|
||||
class="send-status"
|
||||
>
|
||||
<image
|
||||
v-if="chatData.isSendSucces == -1"
|
||||
class="loading"
|
||||
:src="sheep.$url.static('/static/img/shop/chat/loading.png')"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
<!-- <image
|
||||
v-if="chatData.isSendSucces == 1"
|
||||
class="warning"
|
||||
:src="sheep.$url.static('/static/img/shop/chat/warning.png')"
|
||||
mode="aspectFill"
|
||||
@click="onAgainSendMessage(item)"
|
||||
></image> -->
|
||||
</span>
|
||||
|
||||
<!-- 内容 -->
|
||||
<template v-if="item.mode === 'text'">
|
||||
<view class="message-box" :class="[item.from]">
|
||||
<div
|
||||
class="message-text ss-flex ss-flex-wrap"
|
||||
@click="onRichtext"
|
||||
v-html="replaceEmoji(item.content.text)"
|
||||
></div>
|
||||
</view>
|
||||
</template>
|
||||
<template v-if="item.mode === 'image'">
|
||||
<view class="message-box" :class="[item.from]" :style="{ width: '200rpx' }">
|
||||
<su-image
|
||||
class="message-img"
|
||||
isPreview
|
||||
:previewList="[sheep.$url.cdn(item.content.url)]"
|
||||
:current="0"
|
||||
:src="sheep.$url.cdn(item.content.url)"
|
||||
:height="200"
|
||||
:width="200"
|
||||
mode="aspectFill"
|
||||
></su-image>
|
||||
</view>
|
||||
</template>
|
||||
<template v-if="item.mode === 'goods'">
|
||||
<GoodsItem
|
||||
:goodsData="item.content.item"
|
||||
@tap="
|
||||
sheep.$router.go('/pages/goods/index', {
|
||||
id: item.content.item.id,
|
||||
})
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="item.mode === 'order'">
|
||||
<OrderItem
|
||||
from="msg"
|
||||
:orderData="item.content.item"
|
||||
@tap="
|
||||
sheep.$router.go('/pages/order/detail', {
|
||||
id: item.content.item.id,
|
||||
})
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
<!-- user头像 -->
|
||||
<image
|
||||
v-show="item.from === 'customer'"
|
||||
class="chat-avatar ss-m-l-24"
|
||||
:src="sheep.$url.cdn(customerUserInfo.avatar)"
|
||||
mode="aspectFill"
|
||||
>
|
||||
</image>
|
||||
</view>
|
||||
</view>
|
||||
<view id="scrollBottom"></view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<su-fixed bottom>
|
||||
<view class="send-wrap ss-flex">
|
||||
<view class="left ss-flex ss-flex-1">
|
||||
<uni-easyinput
|
||||
class="ss-flex-1 ss-p-l-22"
|
||||
:inputBorder="false"
|
||||
:clearable="false"
|
||||
v-model="chat.msg"
|
||||
placeholder="请输入你要咨询的问题"
|
||||
></uni-easyinput>
|
||||
</view>
|
||||
<text class="sicon-basic bq" @tap.stop="onTools('emoji')"></text>
|
||||
<text
|
||||
v-if="!chat.msg"
|
||||
class="sicon-edit"
|
||||
:class="{ 'is-active': chat.toolsMode == 'tools' }"
|
||||
@tap.stop="onTools('tools')"
|
||||
></text>
|
||||
<button v-if="chat.msg" class="ss-reset-button send-btn" @tap="onSendMessage">
|
||||
发送
|
||||
</button>
|
||||
</view>
|
||||
</su-fixed>
|
||||
<su-popup
|
||||
:show="chat.showTools"
|
||||
@close="
|
||||
chat.showTools = false;
|
||||
chat.toolsMode = '';
|
||||
"
|
||||
>
|
||||
<view class="ss-modal-box ss-flex-col">
|
||||
<view class="send-wrap ss-flex">
|
||||
<view class="left ss-flex ss-flex-1">
|
||||
<uni-easyinput
|
||||
class="ss-flex-1 ss-p-l-22"
|
||||
:inputBorder="false"
|
||||
:clearable="false"
|
||||
v-model="chat.msg"
|
||||
placeholder="请输入你要咨询的问题"
|
||||
></uni-easyinput>
|
||||
</view>
|
||||
<text class="sicon-basic bq" @tap.stop="onTools('emoji')"></text>
|
||||
<text></text>
|
||||
<text
|
||||
v-if="!chat.msg"
|
||||
class="sicon-edit"
|
||||
:class="{ 'is-active': chat.toolsMode == 'tools' }"
|
||||
@tap.stop="onTools('tools')"
|
||||
></text>
|
||||
<button v-if="chat.msg" class="ss-reset-button send-btn" @tap="onSendMessage">
|
||||
发送
|
||||
</button>
|
||||
</view>
|
||||
<view class="content ss-flex ss-flex-1">
|
||||
<template v-if="chat.toolsMode == 'emoji'">
|
||||
<swiper
|
||||
class="emoji-swiper"
|
||||
:indicator-dots="true"
|
||||
circular
|
||||
indicator-active-color="#7063D2"
|
||||
indicator-color="rgba(235, 231, 255, 1)"
|
||||
:autoplay="false"
|
||||
:interval="3000"
|
||||
:duration="1000"
|
||||
>
|
||||
<swiper-item v-for="emoji in emojiPage" :key="emoji">
|
||||
<view class="ss-flex ss-flex-wrap">
|
||||
<template v-for="item in emoji" :key="item">
|
||||
<image
|
||||
class="emoji-img"
|
||||
:src="sheep.$url.cdn(`/static/img/chat/emoji/${item.file}`)"
|
||||
@tap="onEmoji(item)"
|
||||
>
|
||||
</image>
|
||||
</template>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</template>
|
||||
<template v-else>
|
||||
<view class="image">
|
||||
<s-uploader
|
||||
file-mediatype="image"
|
||||
:imageStyles="{ width: 50, height: 50, border: false }"
|
||||
@select="onSelect({ type: 'image', data: $event })"
|
||||
>
|
||||
<image
|
||||
class="icon"
|
||||
:src="sheep.$url.static('/static/img/shop/chat/image.png')"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
</s-uploader>
|
||||
<view>图片</view>
|
||||
</view>
|
||||
<view class="goods" @tap="onShowSelect('goods')">
|
||||
<image
|
||||
class="icon"
|
||||
:src="sheep.$url.static('/static/img/shop/chat/goods.png')"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
<view>商品</view>
|
||||
</view>
|
||||
<view class="order" @tap="onShowSelect('order')">
|
||||
<image
|
||||
class="icon"
|
||||
:src="sheep.$url.static('/static/img/shop/chat/order.png')"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
<view>订单</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</su-popup>
|
||||
|
||||
<SelectPopup
|
||||
:mode="chat.selectMode"
|
||||
:show="chat.showSelect"
|
||||
@select="onSelect"
|
||||
@close="chat.showSelect = false"
|
||||
/>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { computed, reactive, toRefs } from 'vue';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { emojiList, emojiPage } from './emoji.js';
|
||||
import SelectPopup from './components/select-popup.vue';
|
||||
import GoodsItem from './components/goods.vue';
|
||||
import OrderItem from './components/order.vue';
|
||||
import { useChatWebSocket } from './socket';
|
||||
|
||||
const {
|
||||
socketInit,
|
||||
state: chatData,
|
||||
socketSendMsg,
|
||||
formatChatInput,
|
||||
socketHistoryList,
|
||||
onDrop,
|
||||
onPaste,
|
||||
getFocus,
|
||||
// upload,
|
||||
getUserToken,
|
||||
// socketTest,
|
||||
showTime,
|
||||
formatTime,
|
||||
} = useChatWebSocket();
|
||||
const chatList = toRefs(chatData).chatList;
|
||||
const customerServiceInfo = toRefs(chatData).customerServerInfo;
|
||||
const chatHistoryPagination = toRefs(chatData).chatHistoryPagination;
|
||||
const customerUserInfo = toRefs(chatData).customerUserInfo;
|
||||
const socketState = toRefs(chatData).socketState;
|
||||
|
||||
const sys_navBar = sheep.$platform.navbar;
|
||||
const chatConfig = computed(() => sheep.$store('app').chat);
|
||||
|
||||
const { screenHeight, safeAreaInsets, safeArea, screenWidth } = sheep.$platform.device;
|
||||
const pageHeight = safeArea.height - 44 - 35 - 50;
|
||||
|
||||
const chatStatus = {
|
||||
online: {
|
||||
text: '在线',
|
||||
colorVariate: '#46c55f',
|
||||
},
|
||||
offline: {
|
||||
text: '离线',
|
||||
colorVariate: '#b5b5b5',
|
||||
},
|
||||
busy: {
|
||||
text: '忙碌',
|
||||
colorVariate: '#ff0e1b',
|
||||
},
|
||||
};
|
||||
|
||||
// 加载更多
|
||||
const loadingMap = {
|
||||
loadmore: {
|
||||
title: '查看更多',
|
||||
icon: 'el-icon-d-arrow-left',
|
||||
},
|
||||
nomore: {
|
||||
title: '没有更多了',
|
||||
icon: '',
|
||||
},
|
||||
loading: {
|
||||
title: '加载中... ',
|
||||
icon: 'el-icon-loading',
|
||||
},
|
||||
};
|
||||
const onLoadMore = () => {
|
||||
chatHistoryPagination.value.page < chatHistoryPagination.value.lastPage && socketHistoryList();
|
||||
};
|
||||
|
||||
const chat = reactive({
|
||||
msg: '',
|
||||
scrollInto: '',
|
||||
|
||||
showTools: false,
|
||||
toolsMode: '',
|
||||
|
||||
showSelect: false,
|
||||
selectMode: '',
|
||||
chatStyle: {
|
||||
mode: 'inner',
|
||||
color: '#F8270F',
|
||||
type: 'color',
|
||||
alwaysShow: 1,
|
||||
src: '',
|
||||
list: {},
|
||||
},
|
||||
});
|
||||
|
||||
// 点击工具栏开关
|
||||
function onTools(mode) {
|
||||
if (!socketState.value.isConnect) {
|
||||
sheep.$helper.toast(socketState.value.tip || '您已掉线!请返回重试');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!chat.toolsMode || chat.toolsMode === mode) {
|
||||
chat.showTools = !chat.showTools;
|
||||
}
|
||||
chat.toolsMode = mode;
|
||||
if (!chat.showTools) {
|
||||
chat.toolsMode = '';
|
||||
}
|
||||
}
|
||||
|
||||
function onShowSelect(mode) {
|
||||
chat.showTools = false;
|
||||
chat.showSelect = true;
|
||||
chat.selectMode = mode;
|
||||
}
|
||||
|
||||
async function onSelect({ type, data }) {
|
||||
let msg = '';
|
||||
switch (type) {
|
||||
case 'image':
|
||||
const { path, fullurl } = await sheep.$api.app.upload(data.tempFiles[0].path, 'default');
|
||||
msg = {
|
||||
from: 'customer',
|
||||
mode: 'image',
|
||||
date: new Date().getTime(),
|
||||
content: {
|
||||
url: fullurl,
|
||||
path: path,
|
||||
},
|
||||
};
|
||||
break;
|
||||
case 'goods':
|
||||
msg = {
|
||||
from: 'customer',
|
||||
mode: 'goods',
|
||||
date: new Date().getTime(),
|
||||
content: {
|
||||
item: {
|
||||
id: data.goods.id,
|
||||
title: data.goods.title,
|
||||
image: data.goods.image,
|
||||
price: data.goods.price,
|
||||
stock: data.goods.stock,
|
||||
},
|
||||
},
|
||||
};
|
||||
break;
|
||||
case 'order':
|
||||
msg = {
|
||||
from: 'customer',
|
||||
mode: 'order',
|
||||
date: new Date().getTime(),
|
||||
content: {
|
||||
item: {
|
||||
id: data.id,
|
||||
order_sn: data.order_sn,
|
||||
create_time: data.create_time,
|
||||
pay_fee: data.pay_fee,
|
||||
items: data.items.filter((item) => ({
|
||||
goods_id: item.goods_id,
|
||||
goods_title: item.goods_title,
|
||||
goods_image: item.goods_image,
|
||||
goods_price: item.goods_price,
|
||||
})),
|
||||
status_text: data.status_text,
|
||||
},
|
||||
},
|
||||
};
|
||||
break;
|
||||
}
|
||||
if (msg) {
|
||||
socketSendMsg(msg, () => {
|
||||
scrollBottom();
|
||||
});
|
||||
// scrollBottom();
|
||||
chat.showTools = false;
|
||||
chat.showSelect = false;
|
||||
chat.selectMode = '';
|
||||
}
|
||||
}
|
||||
|
||||
function onAgainSendMessage(item) {
|
||||
if (!socketState.value.isConnect) {
|
||||
sheep.$helper.toast(socketState.value.tip || '您已掉线!请返回重试');
|
||||
return;
|
||||
}
|
||||
if (!item) return;
|
||||
const data = {
|
||||
from: 'customer',
|
||||
mode: 'text',
|
||||
date: new Date().getTime(),
|
||||
content: item.content,
|
||||
};
|
||||
socketSendMsg(data, () => {
|
||||
scrollBottom();
|
||||
});
|
||||
}
|
||||
|
||||
function onSendMessage() {
|
||||
if (!socketState.value.isConnect) {
|
||||
sheep.$helper.toast(socketState.value.tip || '您已掉线!请返回重试');
|
||||
return;
|
||||
}
|
||||
if (!chat.msg) return;
|
||||
const data = {
|
||||
from: 'customer',
|
||||
mode: 'text',
|
||||
date: new Date().getTime(),
|
||||
content: {
|
||||
text: chat.msg,
|
||||
},
|
||||
};
|
||||
socketSendMsg(data, () => {
|
||||
scrollBottom();
|
||||
});
|
||||
chat.showTools = false;
|
||||
// scrollBottom();
|
||||
setTimeout(() => {
|
||||
chat.msg = '';
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// 点击猜你想问
|
||||
function onTemplateList(e) {
|
||||
if (!socketState.value.isConnect) {
|
||||
sheep.$helper.toast(socketState.value.tip || '您已掉线!请返回重试');
|
||||
return;
|
||||
}
|
||||
const data = {
|
||||
from: 'customer',
|
||||
mode: 'text',
|
||||
date: new Date().getTime(),
|
||||
content: {
|
||||
text: e.title,
|
||||
},
|
||||
customData: {
|
||||
question_id: e.id,
|
||||
},
|
||||
};
|
||||
socketSendMsg(data, () => {
|
||||
scrollBottom();
|
||||
});
|
||||
// scrollBottom();
|
||||
}
|
||||
|
||||
function onEmoji(item) {
|
||||
chat.msg += item.name;
|
||||
}
|
||||
|
||||
function selEmojiFile(name) {
|
||||
for (let index in emojiList) {
|
||||
if (emojiList[index].name === name) {
|
||||
return emojiList[index].file;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function replaceEmoji(data) {
|
||||
let newData = data;
|
||||
if (typeof newData !== 'object') {
|
||||
let reg = /\[(.+?)\]/g; // [] 中括号
|
||||
let zhEmojiName = newData.match(reg);
|
||||
if (zhEmojiName) {
|
||||
zhEmojiName.forEach((item) => {
|
||||
let emojiFile = selEmojiFile(item);
|
||||
newData = newData.replace(
|
||||
item,
|
||||
`<img class="chat-img" style="width: 24px;height: 24px;margin: 0 3px;" src="${sheep.$url.cdn(
|
||||
'/static/img/chat/emoji/' + emojiFile,
|
||||
)}"/>`,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
return newData;
|
||||
}
|
||||
|
||||
function scrollBottom() {
|
||||
let timeout = null;
|
||||
chat.scrollInto = '';
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => {
|
||||
chat.scrollInto = 'scrollBottom';
|
||||
}, 100);
|
||||
}
|
||||
|
||||
onLoad(async () => {
|
||||
const { error } = await getUserToken();
|
||||
if (error === 0) {
|
||||
socketInit(chatConfig.value, () => {
|
||||
scrollBottom();
|
||||
});
|
||||
} else {
|
||||
socketState.value.isConnect = false;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-bg {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
background-size: 750rpx 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.chat-wrap {
|
||||
// :deep() {
|
||||
// .ui-navbar-box {
|
||||
// background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
// }
|
||||
// }
|
||||
|
||||
.status {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
z-index: 3;
|
||||
height: 70rpx;
|
||||
padding: 0 30rpx;
|
||||
background: var(--ui-BG-Main-opacity-1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 30rpx;
|
||||
font-weight: 400;
|
||||
color: var(--ui-BG-Main);
|
||||
}
|
||||
|
||||
.chat-box {
|
||||
padding: 0 20rpx 0;
|
||||
|
||||
.loadmore-btn {
|
||||
width: 98%;
|
||||
height: 40px;
|
||||
font-size: 12px;
|
||||
color: #8c8c8c;
|
||||
|
||||
.loadmore-icon {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
|
||||
.message-item {
|
||||
margin-bottom: 33rpx;
|
||||
}
|
||||
|
||||
.date-message,
|
||||
.system-message {
|
||||
width: fit-content;
|
||||
border-radius: 12rpx;
|
||||
padding: 8rpx 16rpx;
|
||||
margin-bottom: 16rpx;
|
||||
background-color: var(--ui-BG-3);
|
||||
color: #999;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.chat-avatar {
|
||||
width: 70rpx;
|
||||
height: 70rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.send-status {
|
||||
color: #333;
|
||||
height: 80rpx;
|
||||
margin-right: 8rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.loading {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
-webkit-animation: rotating 2s linear infinite;
|
||||
animation: rotating 2s linear infinite;
|
||||
|
||||
@-webkit-keyframes rotating {
|
||||
0% {
|
||||
transform: rotateZ(0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotateZ(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotating {
|
||||
0% {
|
||||
transform: rotateZ(0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotateZ(360deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.warning {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
color: #ff3000;
|
||||
}
|
||||
}
|
||||
|
||||
.message-box {
|
||||
max-width: 50%;
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
// max-width: 500rpx;
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
word-wrap: break-word;
|
||||
padding: 20rpx;
|
||||
border-radius: 10rpx;
|
||||
color: #fff;
|
||||
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
|
||||
&.customer_service {
|
||||
background: #fff;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
:deep() {
|
||||
.imgred {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.imgred,
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep() {
|
||||
.goods,
|
||||
.order {
|
||||
max-width: 500rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.message-img {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
|
||||
.template-wrap {
|
||||
// width: 100%;
|
||||
padding: 20rpx 24rpx;
|
||||
background: #fff;
|
||||
border-radius: 10rpx;
|
||||
|
||||
.title {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 29rpx;
|
||||
}
|
||||
|
||||
.item {
|
||||
font-size: 24rpx;
|
||||
color: var(--ui-BG-Main);
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
&:last-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.error-img {
|
||||
width: 400rpx;
|
||||
height: 400rpx;
|
||||
}
|
||||
|
||||
#scrollBottom {
|
||||
height: 120rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.send-wrap {
|
||||
padding: 18rpx 20rpx;
|
||||
background: #fff;
|
||||
|
||||
.left {
|
||||
height: 64rpx;
|
||||
border-radius: 32rpx;
|
||||
background: var(--ui-BG-1);
|
||||
}
|
||||
|
||||
.bq {
|
||||
font-size: 50rpx;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
|
||||
.sicon-edit {
|
||||
font-size: 50rpx;
|
||||
margin-left: 10rpx;
|
||||
transform: rotate(0deg);
|
||||
transition: all linear 0.2s;
|
||||
|
||||
&.is-active {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
}
|
||||
|
||||
.send-btn {
|
||||
width: 100rpx;
|
||||
height: 60rpx;
|
||||
line-height: 60rpx;
|
||||
border-radius: 30rpx;
|
||||
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
font-size: 26rpx;
|
||||
color: #fff;
|
||||
margin-left: 11rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
align-content: space-around;
|
||||
border-top: 1px solid #dfdfdf;
|
||||
padding: 20rpx 0 0;
|
||||
|
||||
.emoji-swiper {
|
||||
width: 100%;
|
||||
height: 280rpx;
|
||||
padding: 0 20rpx;
|
||||
|
||||
.emoji-img {
|
||||
width: 50rpx;
|
||||
height: 50rpx;
|
||||
display: inline-block;
|
||||
margin: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.image,
|
||||
.goods,
|
||||
.order {
|
||||
width: 33.3%;
|
||||
height: 280rpx;
|
||||
text-align: center;
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.icon {
|
||||
width: 50rpx;
|
||||
height: 50rpx;
|
||||
margin-bottom: 21rpx;
|
||||
}
|
||||
}
|
||||
|
||||
:deep() {
|
||||
.uni-file-picker__container {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.file-picker__box {
|
||||
display: none;
|
||||
|
||||
&:last-of-type {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
.chat-img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin: 0 3px;
|
||||
}
|
||||
.full-img {
|
||||
object-fit: cover;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
</style>
|
||||
821
pages/chat/socket.js
Normal file
821
pages/chat/socket.js
Normal file
@@ -0,0 +1,821 @@
|
||||
import { reactive, ref, unref } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import chat from '@/sheep/api/chat';
|
||||
import dayjs from 'dayjs';
|
||||
import io from '@hyoga/uni-socket.io';
|
||||
|
||||
export function useChatWebSocket(socketConfig) {
|
||||
let SocketIo = null;
|
||||
|
||||
// chat状态数据
|
||||
const state = reactive({
|
||||
chatDotNum: 0, //总状态红点
|
||||
chatList: [], //会话信息
|
||||
customerUserInfo: {}, //用户信息
|
||||
customerServerInfo: {
|
||||
//客服信息
|
||||
title: '连接中...',
|
||||
state: 'connecting',
|
||||
avatar: null,
|
||||
nickname: '',
|
||||
},
|
||||
socketState: {
|
||||
isConnect: true, //是否连接成功
|
||||
isConnecting: false, //重连中,不允许新的socket开启。
|
||||
tip: '',
|
||||
},
|
||||
chatHistoryPagination: {
|
||||
page: 0, //当前页
|
||||
list_rows: 10, //每页条数
|
||||
last_id: 0, //最后条ID
|
||||
lastPage: 0, //总共多少页
|
||||
loadStatus: 'loadmore', //loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态
|
||||
},
|
||||
templateChatList: [], //猜你想问
|
||||
|
||||
chatConfig: {}, // 配置信息
|
||||
|
||||
isSendSucces: -1, // 是否发送成功 -1=发送中|0=发送成功|1发送失败
|
||||
});
|
||||
|
||||
/**
|
||||
* 连接初始化
|
||||
* @param {Object} config - 配置信息
|
||||
* @param {Function} callBack -回调函数,有新消息接入,保持底部
|
||||
*/
|
||||
const socketInit = (config, callBack) => {
|
||||
state.chatConfig = config;
|
||||
if (SocketIo && SocketIo.connected) return; // 如果socket已经连接,返回false
|
||||
if (state.socketState.isConnecting) return; // 重连中,返回false
|
||||
|
||||
// 启动初始化
|
||||
SocketIo = io(config.chat_domain, {
|
||||
reconnection: true, // 默认 true 是否断线重连
|
||||
reconnectionAttempts: 5, // 默认无限次 断线尝试次数
|
||||
reconnectionDelay: 1000, // 默认 1000,进行下一次重连的间隔。
|
||||
reconnectionDelayMax: 5000, // 默认 5000, 重新连接等待的最长时间 默认 5000
|
||||
randomizationFactor: 0.5, // 默认 0.5 [0-1],随机重连延迟时间
|
||||
timeout: 20000, // 默认 20s
|
||||
transports: ['websocket', 'polling'], // websocket | polling,
|
||||
...config,
|
||||
});
|
||||
|
||||
// 监听连接
|
||||
SocketIo.on('connect', async (res) => {
|
||||
socketReset(callBack);
|
||||
// socket连接
|
||||
// 用户登录
|
||||
// 顾客登录
|
||||
console.log('socket:connect');
|
||||
});
|
||||
// 监听消息
|
||||
SocketIo.on('message', (res) => {
|
||||
if (res.error === 0) {
|
||||
const { message, sender } = res.data;
|
||||
state.chatList.push(formatMessage(res.data.message));
|
||||
|
||||
// 告诉父级页面
|
||||
// window.parent.postMessage({
|
||||
// chatDotNum: ++state.chatDotNum
|
||||
// })
|
||||
callBack && callBack();
|
||||
}
|
||||
});
|
||||
// 监听客服接入成功
|
||||
SocketIo.on('customer_service_access', (res) => {
|
||||
if (res.error === 0) {
|
||||
editCustomerServerInfo({
|
||||
title: res.data.customer_service.name,
|
||||
state: 'online',
|
||||
avatar: res.data.customer_service.avatar,
|
||||
});
|
||||
state.chatList.push(formatMessage(res.data.message));
|
||||
// callBack && callBack()
|
||||
}
|
||||
});
|
||||
// 监听排队等待
|
||||
SocketIo.on('waiting_queue', (res) => {
|
||||
if (res.error === 0) {
|
||||
editCustomerServerInfo({
|
||||
title: res.data.title,
|
||||
state: 'waiting',
|
||||
avatar: '',
|
||||
});
|
||||
// callBack && callBack()
|
||||
}
|
||||
});
|
||||
// 监听没有客服在线
|
||||
SocketIo.on('no_customer_service', (res) => {
|
||||
if (res.error === 0) {
|
||||
editCustomerServerInfo({
|
||||
title: '暂无客服在线...',
|
||||
state: 'waiting',
|
||||
avatar: '',
|
||||
});
|
||||
}
|
||||
state.chatList.push(formatMessage(res.data.message));
|
||||
// callBack && callBack()
|
||||
});
|
||||
// 监听客服上线
|
||||
SocketIo.on('customer_service_online', (res) => {
|
||||
if (res.error === 0) {
|
||||
editCustomerServerInfo({
|
||||
title: res.data.customer_service.name,
|
||||
state: 'online',
|
||||
avatar: res.data.customer_service.avatar,
|
||||
});
|
||||
}
|
||||
});
|
||||
// 监听客服下线
|
||||
SocketIo.on('customer_service_offline', (res) => {
|
||||
if (res.error === 0) {
|
||||
editCustomerServerInfo({
|
||||
title: res.data.customer_service.name,
|
||||
state: 'offline',
|
||||
avatar: res.data.customer_service.avatar,
|
||||
});
|
||||
}
|
||||
});
|
||||
// 监听客服忙碌
|
||||
SocketIo.on('customer_service_busy', (res) => {
|
||||
if (res.error === 0) {
|
||||
editCustomerServerInfo({
|
||||
title: res.data.customer_service.name,
|
||||
state: 'busy',
|
||||
avatar: res.data.customer_service.avatar,
|
||||
});
|
||||
}
|
||||
});
|
||||
// 监听客服断开链接
|
||||
SocketIo.on('customer_service_break', (res) => {
|
||||
if (res.error === 0) {
|
||||
editCustomerServerInfo({
|
||||
title: '客服服务结束',
|
||||
state: 'offline',
|
||||
avatar: '',
|
||||
});
|
||||
state.socketState.isConnect = false;
|
||||
state.socketState.tip = '当前服务已结束';
|
||||
}
|
||||
state.chatList.push(formatMessage(res.data.message));
|
||||
// callBack && callBack()
|
||||
});
|
||||
// 监听自定义错误 custom_error
|
||||
SocketIo.on('custom_error', (error) => {
|
||||
editCustomerServerInfo({
|
||||
title: error.msg,
|
||||
state: 'offline',
|
||||
avatar: '',
|
||||
});
|
||||
console.log('custom_error:', error);
|
||||
});
|
||||
// 监听错误 error
|
||||
SocketIo.on('error', (error) => {
|
||||
console.log('error:', error);
|
||||
});
|
||||
// 重连失败 connect_error
|
||||
SocketIo.on('connect_error', (error) => {
|
||||
console.log('connect_error');
|
||||
});
|
||||
// 连接上,但无反应 connect_timeout
|
||||
SocketIo.on('connect_timeout', (error) => {
|
||||
console.log(error, 'connect_timeout');
|
||||
});
|
||||
// 服务进程销毁 disconnect
|
||||
SocketIo.on('disconnect', (error) => {
|
||||
console.log(error, 'disconnect');
|
||||
});
|
||||
// 服务重启重连上reconnect
|
||||
SocketIo.on('reconnect', (error) => {
|
||||
console.log(error, 'reconnect');
|
||||
});
|
||||
// 开始重连reconnect_attempt
|
||||
SocketIo.on('reconnect_attempt', (error) => {
|
||||
state.socketState.isConnect = false;
|
||||
state.socketState.isConnecting = true;
|
||||
editCustomerServerInfo({
|
||||
title: `重连中,第${error}次尝试...`,
|
||||
state: 'waiting',
|
||||
avatar: '',
|
||||
});
|
||||
console.log(error, 'reconnect_attempt');
|
||||
});
|
||||
// 重新连接中reconnecting
|
||||
SocketIo.on('reconnecting', (error) => {
|
||||
console.log(error, 'reconnecting');
|
||||
});
|
||||
// 重新连接错误reconnect_error
|
||||
SocketIo.on('reconnect_error', (error) => {
|
||||
console.log('reconnect_error');
|
||||
});
|
||||
// 重新连接失败reconnect_failed
|
||||
SocketIo.on('reconnect_failed', (error) => {
|
||||
state.socketState.isConnecting = false;
|
||||
editCustomerServerInfo({
|
||||
title: `重连失败,请刷新重试~`,
|
||||
state: 'waiting',
|
||||
avatar: '',
|
||||
});
|
||||
console.log(error, 'reconnect_failed');
|
||||
|
||||
// setTimeout(() => {
|
||||
state.isSendSucces = 1;
|
||||
// }, 500)
|
||||
});
|
||||
};
|
||||
|
||||
// 重置socket
|
||||
const socketReset = (callBack) => {
|
||||
state.chatList = [];
|
||||
state.chatHistoryList = [];
|
||||
state.chatHistoryPagination = {
|
||||
page: 0,
|
||||
per_page: 10,
|
||||
last_id: 0,
|
||||
totalPage: 0,
|
||||
};
|
||||
socketConnection(callBack); // 连接
|
||||
};
|
||||
|
||||
// 退出连接
|
||||
const socketClose = () => {
|
||||
SocketIo.emit('customer_logout', {}, (res) => {
|
||||
console.log('socket:退出', res);
|
||||
});
|
||||
};
|
||||
|
||||
// 测试事件
|
||||
const socketTest = () => {
|
||||
SocketIo.emit('test', {}, (res) => {
|
||||
console.log('test:test', res);
|
||||
});
|
||||
};
|
||||
|
||||
// 发送消息
|
||||
const socketSendMsg = (data, sendMsgCallBack) => {
|
||||
state.isSendSucces = -1;
|
||||
state.chatList.push(data);
|
||||
sendMsgCallBack && sendMsgCallBack();
|
||||
SocketIo.emit(
|
||||
'message',
|
||||
{
|
||||
message: formatInput(data),
|
||||
...data.customData,
|
||||
},
|
||||
(res) => {
|
||||
// setTimeout(() => {
|
||||
state.isSendSucces = res.error;
|
||||
// }, 500)
|
||||
|
||||
// console.log(res, 'socket:send');
|
||||
// sendMsgCallBack && sendMsgCallBack()
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
// 连接socket,存入sessionId
|
||||
const socketConnection = (callBack) => {
|
||||
SocketIo.emit(
|
||||
'connection',
|
||||
{
|
||||
auth: 'user',
|
||||
token: uni.getStorageSync('socketUserToken') || '',
|
||||
session_id: uni.getStorageSync('socketSessionId') || '',
|
||||
},
|
||||
(res) => {
|
||||
if (res.error === 0) {
|
||||
socketCustomerLogin(callBack);
|
||||
uni.setStorageSync('socketSessionId', res.data.session_id);
|
||||
// uni.getStorageSync('socketUserToken') && socketLogin(uni.getStorageSync(
|
||||
// 'socketUserToken')) // 如果有用户token,绑定
|
||||
state.customerUserInfo = res.data.chat_user;
|
||||
state.socketState.isConnect = true;
|
||||
} else {
|
||||
editCustomerServerInfo({
|
||||
title: `服务器异常!`,
|
||||
state: 'waiting',
|
||||
avatar: '',
|
||||
});
|
||||
state.socketState.isConnect = false;
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
// 用户id,获取token
|
||||
const getUserToken = async (id) => {
|
||||
const res = await chat.unifiedToken();
|
||||
if (res.error === 0) {
|
||||
uni.setStorageSync('socketUserToken', res.data.token);
|
||||
// SocketIo && SocketIo.connected && socketLogin(res.data.token)
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
// 用户登录
|
||||
const socketLogin = (token) => {
|
||||
SocketIo.emit(
|
||||
'login',
|
||||
{
|
||||
token: token,
|
||||
},
|
||||
(res) => {
|
||||
console.log(res, 'socket:login');
|
||||
state.customerUserInfo = res.data.chat_user;
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
// 顾客登录
|
||||
const socketCustomerLogin = (callBack) => {
|
||||
SocketIo.emit(
|
||||
'customer_login',
|
||||
{
|
||||
room_id: state.chatConfig.room_id,
|
||||
},
|
||||
(res) => {
|
||||
state.templateChatList = res.data.questions.length ? res.data.questions : [];
|
||||
state.chatList.push({
|
||||
from: 'customer_service', // 用户customer右 | 顾客customer_service左 | 系统system中间
|
||||
mode: 'template', // goods,order,image,text,system
|
||||
date: new Date().getTime(), //时间
|
||||
content: {
|
||||
//内容
|
||||
list: state.templateChatList,
|
||||
},
|
||||
});
|
||||
res.error === 0 && socketHistoryList(callBack);
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
// 获取历史消息
|
||||
const socketHistoryList = (historyCallBack) => {
|
||||
state.chatHistoryPagination.loadStatus = 'loading';
|
||||
state.chatHistoryPagination.page += 1;
|
||||
SocketIo.emit('messages', state.chatHistoryPagination, (res) => {
|
||||
if (res.error === 0) {
|
||||
state.chatHistoryPagination.total = res.data.messages.total;
|
||||
state.chatHistoryPagination.lastPage = res.data.messages.last_page;
|
||||
state.chatHistoryPagination.page = res.data.messages.current_page;
|
||||
res.data.messages.data.forEach((item) => {
|
||||
item.message_type && state.chatList.unshift(formatMessage(item));
|
||||
});
|
||||
state.chatHistoryPagination.loadStatus =
|
||||
state.chatHistoryPagination.page < state.chatHistoryPagination.lastPage
|
||||
? 'loadmore'
|
||||
: 'nomore';
|
||||
if (state.chatHistoryPagination.last_id == 0) {
|
||||
state.chatHistoryPagination.last_id = res.data.messages.data.length
|
||||
? res.data.messages.data[0].id
|
||||
: 0;
|
||||
}
|
||||
state.chatHistoryPagination.page === 1 && historyCallBack && historyCallBack();
|
||||
}
|
||||
|
||||
// 历史记录之后,猜你想问
|
||||
// state.chatList.push({
|
||||
// from: 'customer_service', // 用户customer右 | 顾客customer_service左 | 系统system中间
|
||||
// mode: 'template', // goods,order,image,text,system
|
||||
// date: new Date().getTime(), //时间
|
||||
// content: { //内容
|
||||
// list: state.templateChatList
|
||||
// }
|
||||
// })
|
||||
});
|
||||
};
|
||||
|
||||
// 修改客服信息
|
||||
const editCustomerServerInfo = (data) => {
|
||||
state.customerServerInfo = {
|
||||
...state.customerServerInfo,
|
||||
...data,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* ================
|
||||
* 工具函数 ↓
|
||||
* ===============
|
||||
*/
|
||||
|
||||
/**
|
||||
* 是否显示时间
|
||||
* @param {*} item - 数据
|
||||
* @param {*} index - 索引
|
||||
*/
|
||||
const showTime = (item, index) => {
|
||||
if (unref(state.chatList)[index + 1]) {
|
||||
let dateString = dayjs(unref(state.chatList)[index + 1].date).fromNow();
|
||||
if (dateString === dayjs(unref(item).date).fromNow()) {
|
||||
return false;
|
||||
} else {
|
||||
dateString = dayjs(unref(item).date).fromNow();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* 格式化时间
|
||||
* @param {*} time - 时间戳
|
||||
*/
|
||||
const formatTime = (time) => {
|
||||
let diffTime = new Date().getTime() - time;
|
||||
if (diffTime > 28 * 24 * 60 * 1000) {
|
||||
return dayjs(time).format('MM/DD HH:mm');
|
||||
}
|
||||
if (diffTime > 360 * 28 * 24 * 60 * 1000) {
|
||||
return dayjs(time).format('YYYY/MM/DD HH:mm');
|
||||
}
|
||||
return dayjs(time).fromNow();
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取焦点
|
||||
* @param {*} virtualNode - 节点信息 ref
|
||||
*/
|
||||
const getFocus = (virtualNode) => {
|
||||
if (window.getSelection) {
|
||||
let chatInput = unref(virtualNode);
|
||||
chatInput.focus();
|
||||
let range = window.getSelection();
|
||||
range.selectAllChildren(chatInput);
|
||||
range.collapseToEnd();
|
||||
} else if (document.selection) {
|
||||
let range = document.selection.createRange();
|
||||
range.moveToElementText(chatInput);
|
||||
range.collapse(false);
|
||||
range.select();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
* @param {Blob} file -文件数据流
|
||||
* @return {path,fullPath}
|
||||
*/
|
||||
|
||||
const upload = (name, file) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let data = new FormData();
|
||||
data.append('file', file, name);
|
||||
data.append('group', 'chat');
|
||||
ajax({
|
||||
url: '/upload',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
data,
|
||||
success: function (res) {
|
||||
resolve(res);
|
||||
},
|
||||
error: function (err) {
|
||||
reject(err);
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 粘贴到输入框
|
||||
* @param {*} e - 粘贴内容
|
||||
* @param {*} uploadHttp - 上传图片地址
|
||||
*/
|
||||
const onPaste = async (e) => {
|
||||
let paste = e.clipboardData || window.clipboardData;
|
||||
let filesArr = Array.from(paste.files);
|
||||
filesArr.forEach(async (child) => {
|
||||
if (child && child.type.includes('image')) {
|
||||
e.preventDefault(); //阻止默认
|
||||
let file = child;
|
||||
const img = await readImg(file);
|
||||
const blob = await compressImg(img, file.type);
|
||||
const { data } = await upload(file.name, blob);
|
||||
let image = `<img class="full-url" src='${data.fullurl}'>`;
|
||||
document.execCommand('insertHTML', false, image);
|
||||
} else {
|
||||
document.execCommand('insertHTML', false, paste.getData('text'));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 拖拽到输入框
|
||||
* @param {*} e - 粘贴内容
|
||||
* @param {*} uploadHttp - 上传图片地址
|
||||
*/
|
||||
const onDrop = async (e) => {
|
||||
e.preventDefault(); //阻止默认
|
||||
let filesArr = Array.from(e.dataTransfer.files);
|
||||
filesArr.forEach(async (child) => {
|
||||
if (child && child.type.includes('image')) {
|
||||
let file = child;
|
||||
const img = await readImg(file);
|
||||
const blob = await compressImg(img, file.type);
|
||||
const { data } = await upload(file.name, blob);
|
||||
let image = `<img class="full-url" src='${data.fullurl}' >`;
|
||||
document.execCommand('insertHTML', false, image);
|
||||
} else {
|
||||
ElMessage({
|
||||
message: '禁止拖拽非图片资源',
|
||||
type: 'warning',
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 解析富文本输入框内容
|
||||
* @param {*} virtualNode -节点信息
|
||||
* @param {Function} formatInputCallBack - cb 回调
|
||||
*/
|
||||
const formatChatInput = (virtualNode, formatInputCallBack) => {
|
||||
let res = '';
|
||||
let elemArr = Array.from(virtualNode.childNodes);
|
||||
elemArr.forEach((child, index) => {
|
||||
if (child.nodeName === '#text') {
|
||||
//如果为文本节点
|
||||
res += child.nodeValue;
|
||||
if (
|
||||
//文本节点的后面是图片,并且不是emoji,分开发送。输入框中的图片和文本表情分开。
|
||||
elemArr[index + 1] &&
|
||||
elemArr[index + 1].nodeName === 'IMG' &&
|
||||
elemArr[index + 1] &&
|
||||
elemArr[index + 1].name !== 'emoji'
|
||||
) {
|
||||
const data = {
|
||||
from: 'customer',
|
||||
mode: 'text',
|
||||
date: new Date().getTime(),
|
||||
content: {
|
||||
text: filterXSS(res),
|
||||
},
|
||||
};
|
||||
formatInputCallBack && formatInputCallBack(data);
|
||||
res = '';
|
||||
}
|
||||
} else if (child.nodeName === 'BR') {
|
||||
res += '<br/>';
|
||||
} else if (child.nodeName === 'IMG') {
|
||||
// 有emjio 和 一般图片
|
||||
// 图片解析后直接发送,不跟文字表情一组
|
||||
if (child.name !== 'emoji') {
|
||||
let srcReg = /src=[\'\']?([^\'\']*)[\'\']?/i;
|
||||
let src = child.outerHTML.match(srcReg);
|
||||
const data = {
|
||||
from: 'customer',
|
||||
mode: 'image',
|
||||
date: new Date().getTime(),
|
||||
content: {
|
||||
url: src[1],
|
||||
path: src[1].replace(/http:\/\/[^\/]*/, ''),
|
||||
},
|
||||
};
|
||||
formatInputCallBack && formatInputCallBack(data);
|
||||
} else {
|
||||
// 非表情图片跟文字一起发送
|
||||
res += child.outerHTML;
|
||||
}
|
||||
} else if (child.nodeName === 'DIV') {
|
||||
res += `<div style='width:200px; white-space: nowrap;'>${child.outerHTML}</div>`;
|
||||
}
|
||||
});
|
||||
if (res) {
|
||||
const data = {
|
||||
from: 'customer',
|
||||
mode: 'text',
|
||||
date: new Date().getTime(),
|
||||
content: {
|
||||
text: filterXSS(res),
|
||||
},
|
||||
};
|
||||
formatInputCallBack && formatInputCallBack(data);
|
||||
}
|
||||
unref(virtualNode).innerHTML = '';
|
||||
};
|
||||
|
||||
/**
|
||||
* 状态回调
|
||||
* @param {*} res -接口返回数据
|
||||
*/
|
||||
const callBackNotice = (res) => {
|
||||
ElNotification({
|
||||
title: 'socket',
|
||||
message: res.msg,
|
||||
showClose: true,
|
||||
type: res.error === 0 ? 'success' : 'warning',
|
||||
duration: 1200,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 格式化发送信息
|
||||
* @param {Object} message
|
||||
* @returns obj - 消息对象
|
||||
*/
|
||||
const formatInput = (message) => {
|
||||
let obj = {};
|
||||
switch (message.mode) {
|
||||
case 'text':
|
||||
obj = {
|
||||
message_type: 'text',
|
||||
message: message.content.text,
|
||||
};
|
||||
break;
|
||||
case 'image':
|
||||
obj = {
|
||||
message_type: 'image',
|
||||
message: message.content.path,
|
||||
};
|
||||
break;
|
||||
case 'goods':
|
||||
obj = {
|
||||
message_type: 'goods',
|
||||
message: message.content.item,
|
||||
};
|
||||
break;
|
||||
case 'order':
|
||||
obj = {
|
||||
message_type: 'order',
|
||||
message: message.content.item,
|
||||
};
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
/**
|
||||
* 格式化接收信息
|
||||
* @param {*} message
|
||||
* @returns obj - 消息对象
|
||||
*/
|
||||
const formatMessage = (message) => {
|
||||
let obj = {};
|
||||
switch (message.message_type) {
|
||||
case 'system':
|
||||
obj = {
|
||||
from: 'system', // 用户customer左 | 顾客customer_service右 | 系统system中间
|
||||
mode: 'system', // goods,order,image,text,system
|
||||
date: message.create_time * 1000, //时间
|
||||
content: {
|
||||
//内容
|
||||
text: message.message,
|
||||
},
|
||||
};
|
||||
break;
|
||||
case 'text':
|
||||
obj = {
|
||||
from: message.sender_identify,
|
||||
mode: message.message_type,
|
||||
date: message.create_time * 1000, //时间
|
||||
sender: message.sender,
|
||||
content: {
|
||||
text: message.message,
|
||||
messageId: message.id,
|
||||
},
|
||||
};
|
||||
break;
|
||||
case 'image':
|
||||
obj = {
|
||||
from: message.sender_identify,
|
||||
mode: message.message_type,
|
||||
date: message.create_time * 1000, //时间
|
||||
sender: message.sender,
|
||||
content: {
|
||||
url: sheep.$url.cdn(message.message),
|
||||
messageId: message.id,
|
||||
},
|
||||
};
|
||||
break;
|
||||
case 'goods':
|
||||
obj = {
|
||||
from: message.sender_identify,
|
||||
mode: message.message_type,
|
||||
date: message.create_time * 1000, //时间
|
||||
sender: message.sender,
|
||||
content: {
|
||||
item: message.message,
|
||||
messageId: message.id,
|
||||
},
|
||||
};
|
||||
break;
|
||||
case 'order':
|
||||
obj = {
|
||||
from: message.sender_identify,
|
||||
mode: message.message_type,
|
||||
date: message.create_time * 1000, //时间
|
||||
sender: message.sender,
|
||||
content: {
|
||||
item: message.message,
|
||||
messageId: message.id,
|
||||
},
|
||||
};
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
|
||||
/**
|
||||
* file 转换为 img
|
||||
* @param {*} file - file 文件
|
||||
* @returns img - img标签
|
||||
*/
|
||||
const readImg = (file) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (e) {
|
||||
img.src = e.target.result;
|
||||
};
|
||||
reader.onerror = function (e) {
|
||||
reject(e);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
img.onload = function () {
|
||||
resolve(img);
|
||||
};
|
||||
img.onerror = function (e) {
|
||||
reject(e);
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 压缩图片
|
||||
*@param img -被压缩的img对象
|
||||
* @param type -压缩后转换的文件类型
|
||||
* @param mx -触发压缩的图片最大宽度限制
|
||||
* @param mh -触发压缩的图片最大高度限制
|
||||
* @returns blob - 文件流
|
||||
*/
|
||||
const compressImg = (img, type = 'image/jpeg', mx = 1000, mh = 1000, quality = 1) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const canvas = document.createElement('canvas');
|
||||
const context = canvas.getContext('2d');
|
||||
const { width: originWidth, height: originHeight } = img;
|
||||
// 最大尺寸限制
|
||||
const maxWidth = mx;
|
||||
const maxHeight = mh;
|
||||
// 目标尺寸
|
||||
let targetWidth = originWidth;
|
||||
let targetHeight = originHeight;
|
||||
if (originWidth > maxWidth || originHeight > maxHeight) {
|
||||
if (originWidth / originHeight > 1) {
|
||||
// 宽图片
|
||||
targetWidth = maxWidth;
|
||||
targetHeight = Math.round(maxWidth * (originHeight / originWidth));
|
||||
} else {
|
||||
// 高图片
|
||||
targetHeight = maxHeight;
|
||||
targetWidth = Math.round(maxHeight * (originWidth / originHeight));
|
||||
}
|
||||
}
|
||||
canvas.width = targetWidth;
|
||||
canvas.height = targetHeight;
|
||||
context.clearRect(0, 0, targetWidth, targetHeight);
|
||||
// 图片绘制
|
||||
context.drawImage(img, 0, 0, targetWidth, targetHeight);
|
||||
canvas.toBlob(
|
||||
function (blob) {
|
||||
resolve(blob);
|
||||
},
|
||||
type,
|
||||
quality,
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
compressImg,
|
||||
readImg,
|
||||
formatMessage,
|
||||
formatInput,
|
||||
callBackNotice,
|
||||
|
||||
socketInit,
|
||||
socketSendMsg,
|
||||
socketClose,
|
||||
socketHistoryList,
|
||||
|
||||
getFocus,
|
||||
formatChatInput,
|
||||
onDrop,
|
||||
onPaste,
|
||||
upload,
|
||||
|
||||
getUserToken,
|
||||
|
||||
state,
|
||||
|
||||
socketTest,
|
||||
|
||||
showTime,
|
||||
formatTime,
|
||||
};
|
||||
}
|
||||
290
pages/commission/apply.vue
Normal file
290
pages/commission/apply.vue
Normal file
@@ -0,0 +1,290 @@
|
||||
<!-- 申请分销商 -->
|
||||
<template>
|
||||
<s-layout title="申请分销商" class="apply-wrap" navbar="inner">
|
||||
<s-empty
|
||||
v-if="state.error === 1"
|
||||
paddingTop="0"
|
||||
icon="/static/comment-empty.png"
|
||||
text="未开启分销商申请"
|
||||
></s-empty>
|
||||
|
||||
<view v-if="state.error === 0" class="distribution-apply-wrap">
|
||||
<view class="apply-header">
|
||||
<view class="header-box ss-flex">
|
||||
<image
|
||||
class="bg-img"
|
||||
:src="sheep.$url.cdn(state.background)"
|
||||
mode="widthFix"
|
||||
@load="onImgLoad"
|
||||
></image>
|
||||
<view class="heaer-title">申请分销商</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="apply-box bg-white" :style="{ marginTop: state.imgHeight + 'rpx' }">
|
||||
<uni-forms
|
||||
label-width="200"
|
||||
:model="state.model"
|
||||
:rules="state.rules"
|
||||
border
|
||||
class="form-box"
|
||||
>
|
||||
<view class="item-box">
|
||||
<uni-forms-item
|
||||
v-for="(item, index) in state.formList"
|
||||
:key="index"
|
||||
:label="item.name"
|
||||
:required="true"
|
||||
:label-position="item.type == 'image' ? 'top' : 'left'"
|
||||
>
|
||||
<uni-easyinput
|
||||
v-if="item.type !== 'image'"
|
||||
:inputBorder="false"
|
||||
:type="item.type"
|
||||
:styles="{ disableColor: '#fff' }"
|
||||
placeholderStyle="color:#BBBBBB;font-size:28rpx;line-height:normal"
|
||||
v-model="item.value"
|
||||
:placeholder="`请填写${item.name}`"
|
||||
/>
|
||||
<s-uploader
|
||||
v-if="item.type === 'image'"
|
||||
v-model:url="item.value"
|
||||
fileMediatype="image"
|
||||
limit="1"
|
||||
mode="grid"
|
||||
:imageStyles="{ width: '168rpx', height: '168rpx' }"
|
||||
class="file-picker"
|
||||
/>
|
||||
</uni-forms-item>
|
||||
</view>
|
||||
</uni-forms>
|
||||
<label class="ss-flex ss-m-t-20" v-if="state.protocol?.status == 1" @tap="onChange">
|
||||
<radio
|
||||
:checked="state.isAgree"
|
||||
color="var(--ui-BG-Main)"
|
||||
style="transform: scale(0.6)"
|
||||
@tap.stop="onChange"
|
||||
/>
|
||||
<view class="agreement-text ss-flex">
|
||||
<view class="ss-m-r-4">勾选代表同意</view>
|
||||
<view
|
||||
class="tcp-text"
|
||||
@tap.stop="
|
||||
sheep.$router.go('/pages/public/richtext', {
|
||||
id: state.protocol.id,
|
||||
title: state.protocol.title,
|
||||
})
|
||||
"
|
||||
>
|
||||
《{{ state.protocol.title }}》
|
||||
</view>
|
||||
</view>
|
||||
</label>
|
||||
<su-fixed bottom placeholder>
|
||||
<view class="submit-box ss-flex ss-row-center ss-p-30">
|
||||
<button class="submit-btn ss-reset-button ui-BG-Main ui-Shadow-Main" @tap="submit">
|
||||
{{ submitText }}
|
||||
</button>
|
||||
</view>
|
||||
</su-fixed>
|
||||
</view>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { computed, reactive } from 'vue';
|
||||
import { isEmpty } from 'lodash';
|
||||
|
||||
const state = reactive({
|
||||
error: -1,
|
||||
status: '-',
|
||||
config: {},
|
||||
isAgree: false,
|
||||
formList: [],
|
||||
protocol: {},
|
||||
applyInfo: [],
|
||||
background: '',
|
||||
imgHeight: 400,
|
||||
});
|
||||
const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
|
||||
|
||||
//勾选协议
|
||||
function onChange() {
|
||||
state.isAgree = !state.isAgree;
|
||||
}
|
||||
|
||||
const submitText = computed(() => {
|
||||
if (state.status === 'normal') return '修改信息';
|
||||
if (state.status === 'needinfo') return '提交审核';
|
||||
if (state.status === 'reject') return '重新提交';
|
||||
return '';
|
||||
});
|
||||
|
||||
async function getAgentForm() {
|
||||
const { error, data } = await sheep.$api.commission.form();
|
||||
state.error = error;
|
||||
if (error === 0) {
|
||||
state.status = data.status;
|
||||
state.background = data.background;
|
||||
state.formList = data.form;
|
||||
state.applyInfo = data.applyInfo;
|
||||
state.protocol = data.protocol;
|
||||
|
||||
if (data.protocol.status != 1) {
|
||||
state.isAgree = true;
|
||||
}
|
||||
mergeFormList();
|
||||
}
|
||||
}
|
||||
function onImgLoad(e) {
|
||||
state.imgHeight = (e.detail.height / e.detail.width) * 750 - 88 - statusBarHeight;
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
if (!state.isAgree) {
|
||||
sheep.$helper.toast('请同意申请协议');
|
||||
return;
|
||||
}
|
||||
|
||||
const validate = state.formList.every((item) => {
|
||||
if (isEmpty(item.value)) {
|
||||
if (item.type !== 'image') {
|
||||
sheep.$helper.toast(`请填写${item.name}`);
|
||||
} else {
|
||||
sheep.$helper.toast(`请上传${item.name}`);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!validate) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { error } = await sheep.$api.commission.apply({
|
||||
data: state.formList,
|
||||
});
|
||||
if (error === 0) {
|
||||
sheep.$router.back();
|
||||
}
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
getAgentForm();
|
||||
});
|
||||
|
||||
// 初始化formData
|
||||
function mergeFormList() {
|
||||
state.formList.forEach((form) => {
|
||||
const apply = state.applyInfo.find(
|
||||
(info) => info.type === form.type && info.name === form.name,
|
||||
);
|
||||
if (typeof apply !== 'undefined') form.value = apply.value;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep() {
|
||||
.uni-forms-item__label .label-text {
|
||||
font-size: 28rpx !important;
|
||||
color: #333333 !important;
|
||||
line-height: normal !important;
|
||||
}
|
||||
|
||||
.file-picker__progress {
|
||||
height: 0 !important;
|
||||
}
|
||||
|
||||
.uni-list-item__content-title {
|
||||
font-size: 28rpx !important;
|
||||
color: #333333 !important;
|
||||
line-height: normal !important;
|
||||
}
|
||||
|
||||
.uni-icons {
|
||||
font-size: 40rpx !important;
|
||||
}
|
||||
|
||||
.is-disabled {
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
|
||||
.distribution-apply-wrap {
|
||||
// height: 100vh;
|
||||
// width: 100vw;
|
||||
// position: absolute;
|
||||
// left: 0;
|
||||
// top: 0;
|
||||
// background-color: #fff;
|
||||
// overflow-y: auto;
|
||||
|
||||
.submit-btn {
|
||||
width: 690px;
|
||||
height: 86rpx;
|
||||
border-radius: 43rpx;
|
||||
}
|
||||
.apply-header {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
.header-box {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
.bg-img {
|
||||
width: 750rpx;
|
||||
}
|
||||
|
||||
.heaer-title {
|
||||
position: absolute;
|
||||
left: 30rpx;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
font-size: 50rpx;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
z-index: 11;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
width: 51rpx;
|
||||
height: 8rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 4rpx;
|
||||
position: absolute;
|
||||
z-index: 12;
|
||||
bottom: -20rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.apply-box {
|
||||
padding: 0 40rpx;
|
||||
|
||||
.item-box {
|
||||
border-bottom: 2rpx solid #eee;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.agreement-text {
|
||||
font-size: 24rpx;
|
||||
color: #c4c4c4;
|
||||
line-height: normal;
|
||||
|
||||
.tcp-text {
|
||||
color: var(--ui-BG-Main);
|
||||
}
|
||||
}
|
||||
|
||||
.card-image {
|
||||
width: 140rpx;
|
||||
height: 140rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
</style>
|
||||
108
pages/commission/components/account-info.vue
Normal file
108
pages/commission/components/account-info.vue
Normal file
@@ -0,0 +1,108 @@
|
||||
<!-- 账户 -->
|
||||
<template>
|
||||
<view class="account-card">
|
||||
<view class="account-card-box">
|
||||
<view class="ss-flex ss-row-between card-box-header">
|
||||
<view class="ss-flex">
|
||||
<view class="header-title ss-m-r-16">账户信息</view>
|
||||
<button
|
||||
class="ss-reset-button look-btn ss-flex"
|
||||
@tap="state.showMoney = !state.showMoney"
|
||||
>
|
||||
<uni-icons
|
||||
:type="state.showMoney ? 'eye-filled' : 'eye-slash-filled'"
|
||||
color="#A57A55"
|
||||
size="20"
|
||||
></uni-icons>
|
||||
</button>
|
||||
</view>
|
||||
<view class="ss-flex" @tap="sheep.$router.go('/pages/user/wallet/commission')">
|
||||
<view class="header-title ss-m-r-4">查看明细</view>
|
||||
<text class="cicon-play-arrow"></text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 收益 -->
|
||||
<view class="card-content ss-flex">
|
||||
<view class="ss-flex-1 ss-flex-col ss-col-center">
|
||||
<view class="item-title">总收益(元)</view>
|
||||
<view class="item-detail">
|
||||
{{ state.showMoney ? agentInfo.total_income || '0.00' : '***' }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="ss-flex-1 ss-flex-col ss-col-center">
|
||||
<view class="item-title">我的佣金(元)</view>
|
||||
<view class="item-detail">
|
||||
{{ state.showMoney ? userInfo.commission || '0.00' : '***' }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="ss-flex-1 ss-flex-col ss-col-center">
|
||||
<view class="item-title">我的消费(元)</view>
|
||||
<view class="item-detail">
|
||||
{{ state.showMoney ? userInfo.total_consume || '0.00' : '***' }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { computed, reactive } from 'vue';
|
||||
|
||||
const userInfo = computed(() => sheep.$store('user').userInfo);
|
||||
const agentInfo = computed(() => sheep.$store('user').agentInfo);
|
||||
|
||||
const state = reactive({
|
||||
showMoney: false,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.account-card {
|
||||
width: 694rpx;
|
||||
margin: 0 auto;
|
||||
padding: 2rpx;
|
||||
background: linear-gradient(180deg, #ffffff 0.88%, #fff9ec 100%);
|
||||
border-radius: 12rpx;
|
||||
z-index: 3;
|
||||
position: relative;
|
||||
.account-card-box {
|
||||
background: #ffefd6;
|
||||
.card-box-header {
|
||||
padding: 0 30rpx;
|
||||
height: 72rpx;
|
||||
box-shadow: 0px 2px 6px #f2debe;
|
||||
.header-title {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #a17545;
|
||||
line-height: 30rpx;
|
||||
}
|
||||
.cicon-play-arrow {
|
||||
color: #a17545;
|
||||
font-size: 24rpx;
|
||||
line-height: 30rpx;
|
||||
}
|
||||
}
|
||||
.card-content {
|
||||
height: 190rpx;
|
||||
background: #fdfae9;
|
||||
.item-title {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #cba67e;
|
||||
line-height: 30rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
.item-detail {
|
||||
font-size: 36rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: bold;
|
||||
color: #692e04;
|
||||
line-height: 30rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
184
pages/commission/components/commission-auth.vue
Normal file
184
pages/commission/components/commission-auth.vue
Normal file
@@ -0,0 +1,184 @@
|
||||
<!-- 页面 -->
|
||||
<template>
|
||||
<su-popup
|
||||
:show="state.show"
|
||||
type="center"
|
||||
round="10"
|
||||
@close="state.show = false"
|
||||
:isMaskClick="false"
|
||||
maskBackgroundColor="rgba(0, 0, 0, 0.7)"
|
||||
>
|
||||
<view class="notice-box">
|
||||
<view class="img-wrap">
|
||||
<image
|
||||
class="notice-img"
|
||||
:src="sheep.$url.static(state.event.image)"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
</view>
|
||||
<view class="notice-title">{{ state.event.title }}</view>
|
||||
<view class="notice-detail">{{ state.event.subtitle }}</view>
|
||||
<button
|
||||
class="ss-reset-button notice-btn ui-Shadow-Main ui-BG-Main-Gradient"
|
||||
@tap="onTap(state.event.action)"
|
||||
>
|
||||
{{ state.event.button }}
|
||||
</button>
|
||||
<button class="ss-reset-button back-btn" @tap="sheep.$router.back()"> 返回 </button>
|
||||
</view>
|
||||
</su-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { reactive, watch } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
error: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
const emits = defineEmits(['getAgentInfo']);
|
||||
const state = reactive({
|
||||
event: {},
|
||||
show: false,
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.error,
|
||||
(error) => {
|
||||
if (error !== 0 && error !== 100) {
|
||||
state.event = eventMap[error];
|
||||
state.show = true;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
async function onTap(eventName) {
|
||||
switch (eventName) {
|
||||
case 'back': // 返回
|
||||
sheep.$router.back();
|
||||
break;
|
||||
case 'apply': // 需提交资料
|
||||
sheep.$router.go('/pages/commission/apply');
|
||||
break;
|
||||
case 'reApply': // 直接重新申请
|
||||
let { error } = await sheep.$api.commission.apply();
|
||||
if (error === 0) {
|
||||
emits('getAgentInfo');
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
const eventMap = {
|
||||
// 关闭
|
||||
101: {
|
||||
image: '/static/img/shop/commission/close.png',
|
||||
title: '分销中心已关闭',
|
||||
subtitle: '该功能暂不可用',
|
||||
button: '知道了',
|
||||
action: 'back',
|
||||
},
|
||||
// 禁用
|
||||
102: {
|
||||
image: '/static/img/shop/commission/forbidden.png',
|
||||
title: '账户已被禁用',
|
||||
subtitle: '该功能暂不可用',
|
||||
button: '知道了',
|
||||
action: 'back',
|
||||
},
|
||||
// 补充信息
|
||||
103: {
|
||||
image: '/static/img/shop/commission/apply.png',
|
||||
title: '待完善信息',
|
||||
subtitle: '请补充您的信息后提交审核',
|
||||
button: '完善信息',
|
||||
action: 'apply',
|
||||
},
|
||||
// 审核中
|
||||
104: {
|
||||
image: '/static/img/shop/commission/pending.png',
|
||||
title: '正在审核中',
|
||||
subtitle: '请耐心等候结果',
|
||||
button: '知道了',
|
||||
action: 'back',
|
||||
},
|
||||
// 重新提交
|
||||
105: {
|
||||
image: '/static/img/shop/commission/reject.png',
|
||||
title: '抱歉!您的申请信息未通过',
|
||||
subtitle: '请尝试修改后重新提交',
|
||||
button: '重新申请',
|
||||
action: 'apply',
|
||||
},
|
||||
// 直接重新申请
|
||||
106: {
|
||||
image: '/static/img/shop/commission/reject.png',
|
||||
title: '抱歉!您的申请未通过',
|
||||
subtitle: '请尝试重新申请',
|
||||
button: '重新申请',
|
||||
action: 'reApply',
|
||||
},
|
||||
// 冻结
|
||||
107: {
|
||||
image: '/static/img/shop/commission/freeze.png',
|
||||
title: '抱歉!您的账户已被冻结',
|
||||
subtitle: '如有疑问请联系客服',
|
||||
button: '联系客服',
|
||||
action: 'chat',
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.notice-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #fff;
|
||||
width: 612rpx;
|
||||
min-height: 658rpx;
|
||||
background: #ffffff;
|
||||
padding: 30rpx;
|
||||
border-radius: 20rpx;
|
||||
.img-wrap {
|
||||
margin-bottom: 50rpx;
|
||||
.notice-img {
|
||||
width: 180rpx;
|
||||
height: 170rpx;
|
||||
}
|
||||
}
|
||||
.notice-title {
|
||||
font-size: 35rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
.notice-detail {
|
||||
font-size: 28rpx;
|
||||
font-weight: 400;
|
||||
color: #999999;
|
||||
line-height: 36rpx;
|
||||
margin-bottom: 50rpx;
|
||||
}
|
||||
.notice-btn {
|
||||
width: 492rpx;
|
||||
line-height: 70rpx;
|
||||
border-radius: 35rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
.back-btn {
|
||||
width: 492rpx;
|
||||
line-height: 70rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: var(--ui-BG-Main-gradient);
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
173
pages/commission/components/commission-condition.vue
Normal file
173
pages/commission/components/commission-condition.vue
Normal file
@@ -0,0 +1,173 @@
|
||||
<template>
|
||||
<su-popup
|
||||
:show="state.show"
|
||||
type="bottom"
|
||||
round="10"
|
||||
:isMaskClick="false"
|
||||
:backgroundImage="sheep.$url.css('/static/img/shop/commission/become-agent.png')"
|
||||
@close="show = false"
|
||||
backgroundColor="var(--ui-BG-Main)"
|
||||
>
|
||||
<view class="model-box ss-flex ss-row-center">
|
||||
<view class="content">
|
||||
<scroll-view
|
||||
class="scroll-box"
|
||||
scroll-y="true"
|
||||
:scroll-with-animation="true"
|
||||
:show-scrollbar="false"
|
||||
>
|
||||
<view v-if="errorData.type === 'goods'">
|
||||
<view class="item-box ss-m-b-20" v-for="item in errorData.value" :key="item.id">
|
||||
<s-goods-item :title="item.title" :img="item.image" :price="item.price[0]" priceColor="#E1212B" @tap="sheep.$router.go('/pages/goods/index', { id: item.id })">
|
||||
<template #groupon>
|
||||
<view class="item-box-subtitle">{{ item.subtitle }}</view>
|
||||
</template>
|
||||
</s-goods-item>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<s-goods-item
|
||||
title="累计消费满"
|
||||
price=""
|
||||
:img="sheep.$url.static('/static/img/shop/commission/consume.png')"
|
||||
v-else-if="errorData.type === 'consume'"
|
||||
>
|
||||
<template #groupon>
|
||||
<view class="ss-flex">
|
||||
<view class="progress-box ss-flex">
|
||||
<view
|
||||
class="progerss-active"
|
||||
:style="{
|
||||
width: state.percent < 10 ? '10%' : state.percent + '%',
|
||||
}"
|
||||
></view>
|
||||
</view>
|
||||
<view class="progress-title ss-m-l-10">{{ errorData.value }}元</view>
|
||||
</view>
|
||||
<view class="progress-title ss-m-t-20">{{ userInfo.total_consume }}元</view>
|
||||
</template>
|
||||
</s-goods-item>
|
||||
</scroll-view>
|
||||
<view class="content-des" v-if="errorData.type === 'goods'"
|
||||
>* 购买指定商品即可成为分销商</view
|
||||
>
|
||||
<view class="content-des" v-else-if="errorData.type === 'consume'"
|
||||
>* 满足累计消费即可成为分销商</view
|
||||
>
|
||||
</view>
|
||||
<button class="ss-reset-button go-btn ui-BG-Main-Gradient" @tap="sheep.$router.back()">
|
||||
返回
|
||||
</button>
|
||||
</view>
|
||||
</su-popup>
|
||||
</template>
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { computed, reactive, watch } from 'vue';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
|
||||
const props = defineProps({
|
||||
error: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
errorData: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
});
|
||||
|
||||
const userInfo = computed(() => sheep.$store('user').userInfo);
|
||||
|
||||
const state = reactive({
|
||||
percent: computed(() => {
|
||||
if (props.errorData.type !== 'consume') {
|
||||
return 0;
|
||||
}
|
||||
let percent = (userInfo.value.total_consume / props.errorData.value) * 100;
|
||||
return parseInt(percent);
|
||||
}),
|
||||
show: false,
|
||||
money: '',
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.error,
|
||||
(error) => {
|
||||
if (error == 100) {
|
||||
state.show = true;
|
||||
}
|
||||
},
|
||||
);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
:deep() {
|
||||
.ss-goods-item-warp {
|
||||
background-color: #f8f8f8 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.progress-title {
|
||||
font-size: 20rpx;
|
||||
font-weight: 500;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.progress-box {
|
||||
flex: 1;
|
||||
height: 18rpx;
|
||||
background: #e7e7e7;
|
||||
border-radius: 9rpx;
|
||||
}
|
||||
|
||||
.progerss-active {
|
||||
height: 24rpx;
|
||||
background: linear-gradient(90deg, #ff6000 0%, #fe832a 100%);
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.model-box {
|
||||
padding: 140rpx 40rpx 60rpx 40rpx;
|
||||
height: 916rpx;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
|
||||
.content {
|
||||
height: 720rpx;
|
||||
width: 612rpx;
|
||||
padding-top: 30rpx;
|
||||
// background-color: #fff;
|
||||
box-sizing: border-box;
|
||||
|
||||
.content-des {
|
||||
margin-top: 20rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #999999;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-box {
|
||||
height: 620rpx;
|
||||
}
|
||||
.item-box-subtitle {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #999999;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.go-btn {
|
||||
width: 600rpx;
|
||||
height: 70rpx;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
bottom: 120rpx;
|
||||
border-radius: 35rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
126
pages/commission/components/commission-info.vue
Normal file
126
pages/commission/components/commission-info.vue
Normal file
@@ -0,0 +1,126 @@
|
||||
<!-- 分销商信息 -->
|
||||
<template>
|
||||
<!-- 用户资料 -->
|
||||
<view class="user-card ss-flex ss-col-bottom">
|
||||
<view class="card-top ss-flex ss-row-between">
|
||||
<view class="ss-flex">
|
||||
<view class="head-img-box">
|
||||
<image class="head-img" :src="sheep.$url.cdn(userInfo.avatar)" mode="aspectFill"></image>
|
||||
</view>
|
||||
<view class="ss-flex-col">
|
||||
<view class="user-name">{{ userInfo.nickname }}</view>
|
||||
<view class="user-info-box ss-flex">
|
||||
<view class="tag-box ss-flex" v-if="agentInfo.level_info">
|
||||
<image
|
||||
v-if="agentInfo.level_info?.image"
|
||||
class="tag-img"
|
||||
:src="sheep.$url.cdn(agentInfo.level_info?.image)"
|
||||
mode="aspectFill"
|
||||
>
|
||||
</image>
|
||||
<text class="tag-title">{{ agentInfo.level_info?.name }}</text>
|
||||
</view>
|
||||
<view class="ss-iconfont uicon-arrow-right" style="color: #fff; font-size: 28rpx">
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { computed, reactive } from 'vue';
|
||||
|
||||
const userInfo = computed(() => sheep.$store('user').userInfo);
|
||||
const agentInfo = computed(() => sheep.$store('user').agentInfo);
|
||||
const headerBg = sheep.$url.css('/static/img/shop/commission/background.png');
|
||||
|
||||
const state = reactive({
|
||||
showMoney: false,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// 用户资料卡片
|
||||
.user-card {
|
||||
width: 690rpx;
|
||||
height: 192rpx;
|
||||
margin: -88rpx 20rpx 0 20rpx;
|
||||
padding-top: 88rpx;
|
||||
background: v-bind(headerBg) no-repeat;
|
||||
background-size: 100% 100%;
|
||||
|
||||
.head-img-box {
|
||||
margin-right: 20rpx;
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
background: #fce0ad;
|
||||
|
||||
.head-img {
|
||||
width: 92rpx;
|
||||
height: 92rpx;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
}
|
||||
|
||||
.card-top {
|
||||
box-sizing: border-box;
|
||||
padding-bottom: 34rpx;
|
||||
.user-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #692e04;
|
||||
line-height: 30rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.log-btn {
|
||||
width: 84rpx;
|
||||
height: 42rpx;
|
||||
border: 2rpx solid rgba(#ffffff, 0.33);
|
||||
border-radius: 21rpx;
|
||||
font-size: 22rpx;
|
||||
font-weight: 400;
|
||||
color: #ffffff;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.look-btn {
|
||||
color: #fff;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.user-info-box {
|
||||
.tag-box {
|
||||
background: #ff6000;
|
||||
border-radius: 18rpx;
|
||||
line-height: 36rpx;
|
||||
|
||||
.tag-img {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
border-radius: 50%;
|
||||
margin-left: -2rpx;
|
||||
}
|
||||
|
||||
.tag-title {
|
||||
font-size: 24rpx;
|
||||
padding: 0 10rpx;
|
||||
font-weight: 500;
|
||||
line-height: 36rpx;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
184
pages/commission/components/commission-log.vue
Normal file
184
pages/commission/components/commission-log.vue
Normal file
@@ -0,0 +1,184 @@
|
||||
<!-- 分销明细 -->
|
||||
<template>
|
||||
<view class="distribution-log-wrap">
|
||||
<view class="header-box">
|
||||
<image class="header-bg" :src="sheep.$url.static('/static/img/shop/commission/title2.png')" />
|
||||
<view class="ss-flex header-title">
|
||||
<view class="title">实时动态</view>
|
||||
<text class="cicon-forward"></text>
|
||||
</view>
|
||||
</view>
|
||||
<scroll-view
|
||||
scroll-y="true"
|
||||
@scrolltolower="loadmore"
|
||||
class="scroll-box log-scroll"
|
||||
scroll-with-animation="true"
|
||||
>
|
||||
<view v-if="state.pagination.data">
|
||||
<view
|
||||
class="log-item-box ss-flex ss-row-between"
|
||||
v-for="item in state.pagination.data"
|
||||
:key="item.id"
|
||||
>
|
||||
<view class="log-item-wrap">
|
||||
<view class="log-item ss-flex ss-ellipsis-1 ss-col-center">
|
||||
<view class="ss-flex ss-col-center">
|
||||
<image
|
||||
v-if="item.oper_type === 'user'"
|
||||
class="log-img"
|
||||
:src="sheep.$url.cdn(item.oper?.avatar)"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
<image
|
||||
v-else-if="item.oper_type === 'admin'"
|
||||
class="log-img"
|
||||
:src="sheep.$url.static('/static/img/shop/avatar/default_user.png')"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
<image
|
||||
v-else
|
||||
class="log-img"
|
||||
:src="sheep.$url.static('/static/img/shop/avatar/notice.png')"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
</view>
|
||||
<view class="log-text ss-ellipsis-1">{{ item.remark }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<text class="log-time">{{ dayjs(item.create_time).fromNow() }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<uni-load-more
|
||||
v-if="state.pagination.total > 0"
|
||||
:status="state.loadStatus"
|
||||
color="#333333"
|
||||
@tap="loadmore"
|
||||
/>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { computed, reactive } from 'vue';
|
||||
import _ from 'lodash';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const state = reactive({
|
||||
loadStatus: '',
|
||||
pagination: {
|
||||
data: [],
|
||||
current_page: 1,
|
||||
total: 1,
|
||||
last_page: 1,
|
||||
},
|
||||
});
|
||||
|
||||
async function getLog(page = 1) {
|
||||
const res = await sheep.$api.commission.log({
|
||||
page,
|
||||
});
|
||||
if (res.error === 0) {
|
||||
let list = _.concat(state.pagination.data, res.data.data);
|
||||
state.pagination = {
|
||||
...res.data,
|
||||
data: list,
|
||||
};
|
||||
if (state.pagination.current_page < state.pagination.last_page) {
|
||||
state.loadStatus = 'more';
|
||||
} else {
|
||||
state.loadStatus = 'noMore';
|
||||
}
|
||||
}
|
||||
}
|
||||
getLog();
|
||||
|
||||
// 加载更多
|
||||
function loadmore() {
|
||||
if (state.loadStatus !== 'noMore') {
|
||||
getLog(state.pagination.current_page + 1);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.distribution-log-wrap {
|
||||
width: 690rpx;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 20rpx;
|
||||
border-radius: 12rpx;
|
||||
z-index: 3;
|
||||
position: relative;
|
||||
.header-box {
|
||||
width: 690rpx;
|
||||
height: 76rpx;
|
||||
position: relative;
|
||||
.header-bg {
|
||||
width: 690rpx;
|
||||
height: 76rpx;
|
||||
}
|
||||
.header-title {
|
||||
position: absolute;
|
||||
left: 20rpx;
|
||||
top: 24rpx;
|
||||
}
|
||||
.title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
line-height: 30rpx;
|
||||
}
|
||||
.cicon-forward {
|
||||
font-size: 30rpx;
|
||||
font-weight: 400;
|
||||
color: #ffffff;
|
||||
line-height: 30rpx;
|
||||
}
|
||||
}
|
||||
.log-scroll {
|
||||
height: 600rpx;
|
||||
background: #fdfae9;
|
||||
padding: 10rpx 20rpx 0;
|
||||
box-sizing: border-box;
|
||||
border-radius: 0 0 12rpx 12rpx;
|
||||
|
||||
.log-item-box {
|
||||
margin-bottom: 20rpx;
|
||||
.log-time {
|
||||
// margin-left: 30rpx;
|
||||
text-align: right;
|
||||
font-size: 24rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 400;
|
||||
color: #c4c4c4;
|
||||
}
|
||||
}
|
||||
|
||||
.loadmore-wrap {
|
||||
// line-height: 80rpx;
|
||||
}
|
||||
|
||||
.log-item {
|
||||
// background: rgba(#ffffff, 0.2);
|
||||
border-radius: 24rpx;
|
||||
padding: 6rpx 20rpx 6rpx 12rpx;
|
||||
|
||||
.log-img {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.log-text {
|
||||
max-width: 480rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
153
pages/commission/components/commission-menu.vue
Normal file
153
pages/commission/components/commission-menu.vue
Normal file
@@ -0,0 +1,153 @@
|
||||
<!-- 分销商菜单栏 -->
|
||||
<template>
|
||||
<view class="menu-box ss-flex-col">
|
||||
<view class="header-box">
|
||||
<image class="header-bg" :src="sheep.$url.static('/static/img/shop/commission/title1.png')" />
|
||||
<view class="ss-flex header-title">
|
||||
<view class="title">功能专区</view>
|
||||
<text class="cicon-forward"></text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="menu-list ss-flex ss-flex-wrap">
|
||||
<view
|
||||
v-for="(item, index) in state.menuList"
|
||||
:key="index"
|
||||
class="item-box ss-flex-col ss-col-center"
|
||||
@tap="sheep.$router.go(item.path)"
|
||||
>
|
||||
<image
|
||||
class="menu-icon ss-m-b-10"
|
||||
:src="sheep.$url.static(item.img)"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
<view>{{ item.title }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- <uni-grid :column="4" :showBorder="false" :highlight="false">
|
||||
<uni-grid-item
|
||||
v-for="(item, index) in state.menuList"
|
||||
:index="index"
|
||||
:key="index"
|
||||
@tap="sheep.$router.go(item.path)"
|
||||
>
|
||||
<view class="grid-item-box ss-flex ss-flex-col ss-row-center ss-col-center">
|
||||
<image
|
||||
class="menu-icon ss-m-b-10"
|
||||
:src="sheep.$url.static(item.img)"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
<text class="menu-title">{{ item.title }}</text>
|
||||
</view>
|
||||
</uni-grid-item>
|
||||
</uni-grid> -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { computed, reactive } from 'vue';
|
||||
|
||||
const state = reactive({
|
||||
menuList: [
|
||||
{
|
||||
img: '/static/img/shop/commission/commission_icon1.png',
|
||||
title: '我的团队',
|
||||
path: '/pages/commission/team',
|
||||
},
|
||||
{
|
||||
img: '/static/img/shop/commission/commission_icon2.png',
|
||||
title: '佣金明细',
|
||||
path: '/pages/user/wallet/commission',
|
||||
},
|
||||
{
|
||||
img: '/static/img/shop/commission/commission_icon3.png',
|
||||
title: '分销订单',
|
||||
path: '/pages/commission/order',
|
||||
},
|
||||
{
|
||||
img: '/static/img/shop/commission/commission_icon4.png',
|
||||
title: '推广商品',
|
||||
path: '/pages/commission/goods',
|
||||
},
|
||||
{
|
||||
img: '/static/img/shop/commission/commission_icon5.png',
|
||||
title: '我的资料',
|
||||
path: '/pages/commission/apply',
|
||||
isAgentFrom: true,
|
||||
},
|
||||
{
|
||||
img: '/static/img/shop/commission/commission_icon7.png',
|
||||
title: '邀请海报',
|
||||
path: 'action:showShareModal',
|
||||
},
|
||||
{
|
||||
img: '/static/img/shop/commission/commission_icon8.png',
|
||||
title: '分享记录',
|
||||
path: '/pages/commission/share-log',
|
||||
},
|
||||
],
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.menu-box {
|
||||
margin: 0 auto;
|
||||
width: 690rpx;
|
||||
margin-bottom: 20rpx;
|
||||
margin-top: 20rpx;
|
||||
border-radius: 12rpx;
|
||||
z-index: 3;
|
||||
position: relative;
|
||||
}
|
||||
.header-box {
|
||||
width: 690rpx;
|
||||
height: 76rpx;
|
||||
position: relative;
|
||||
.header-bg {
|
||||
width: 690rpx;
|
||||
height: 76rpx;
|
||||
}
|
||||
.header-title {
|
||||
position: absolute;
|
||||
left: 20rpx;
|
||||
top: 24rpx;
|
||||
}
|
||||
.title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
line-height: 30rpx;
|
||||
}
|
||||
.cicon-forward {
|
||||
font-size: 30rpx;
|
||||
font-weight: 400;
|
||||
color: #ffffff;
|
||||
line-height: 30rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-list {
|
||||
padding: 50rpx 0 10rpx 0;
|
||||
background: #fdfae9;
|
||||
border-radius: 0 0 12rpx 12rpx;
|
||||
}
|
||||
.item-box {
|
||||
width: 25%;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
width: 68rpx;
|
||||
height: 68rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.menu-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
137
pages/commission/goods.vue
Normal file
137
pages/commission/goods.vue
Normal file
@@ -0,0 +1,137 @@
|
||||
<!-- 页面 -->
|
||||
<template>
|
||||
<s-layout title="推广商品" :onShareAppMessage="state.shareInfo">
|
||||
<view class="goods-item ss-m-20" v-for="item in state.pagination.data" :key="item.id">
|
||||
<s-goods-item
|
||||
size="lg"
|
||||
:img="item.image"
|
||||
:title="item.title"
|
||||
:subTitle="item.subtitle"
|
||||
:price="item.price[0]"
|
||||
:originPrice="item.original_price"
|
||||
priceColor="#333"
|
||||
@tap="sheep.$router.go('/pages/goods/index', { id: item.id })"
|
||||
>
|
||||
<template #rightBottom>
|
||||
<view class="ss-flex ss-row-between">
|
||||
<view class="commission-num">预计佣金:¥{{ item.commission }}</view>
|
||||
<button
|
||||
class="ss-reset-button share-btn ui-BG-Main-Gradient"
|
||||
@tap.stop="onShareGoods(item)"
|
||||
>
|
||||
分享赚
|
||||
</button>
|
||||
</view>
|
||||
</template>
|
||||
</s-goods-item>
|
||||
</view>
|
||||
<s-empty
|
||||
v-if="state.pagination.total === 0"
|
||||
icon="/static/goods-empty.png"
|
||||
text="暂无推广商品"
|
||||
></s-empty>
|
||||
<!-- 加载更多 -->
|
||||
<uni-load-more
|
||||
v-if="state.pagination.total > 0"
|
||||
:status="state.loadStatus"
|
||||
:content-text="{
|
||||
contentdown: '上拉加载更多',
|
||||
}"
|
||||
@tap="loadmore"
|
||||
/>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import $share from '@/sheep/platform/share';
|
||||
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
|
||||
import { computed, reactive } from 'vue';
|
||||
import _ from 'lodash';
|
||||
import { showShareModal } from '@/sheep/hooks/useModal';
|
||||
|
||||
const state = reactive({
|
||||
pagination: {
|
||||
data: [],
|
||||
current_page: 1,
|
||||
total: 1,
|
||||
last_page: 1,
|
||||
},
|
||||
loadStatus: '',
|
||||
shareInfo: {},
|
||||
});
|
||||
|
||||
function onShareGoods(goodsInfo) {
|
||||
state.shareInfo = $share.getShareInfo(
|
||||
{
|
||||
title: goodsInfo.title,
|
||||
image: sheep.$url.cdn(goodsInfo.image),
|
||||
desc: goodsInfo.subtitle,
|
||||
params: {
|
||||
page: '2',
|
||||
query: goodsInfo.id,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'goods', // 商品海报
|
||||
title: goodsInfo.title, // 商品标题
|
||||
image: sheep.$url.cdn(goodsInfo.image), // 商品主图
|
||||
price: goodsInfo.price[0], // 商品价格
|
||||
original_price: goodsInfo.original_price, // 商品原价
|
||||
},
|
||||
);
|
||||
showShareModal();
|
||||
}
|
||||
|
||||
async function getGoodsList(page = 1, list_rows = 8) {
|
||||
state.loadStatus = 'loading';
|
||||
let res = await sheep.$api.commission.goods({
|
||||
list_rows,
|
||||
page,
|
||||
});
|
||||
if (res.error === 0) {
|
||||
let orderList = _.concat(state.pagination.data, res.data.data);
|
||||
state.pagination = {
|
||||
...res.data,
|
||||
data: orderList,
|
||||
};
|
||||
if (state.pagination.current_page < state.pagination.last_page) {
|
||||
state.loadStatus = 'more';
|
||||
} else {
|
||||
state.loadStatus = 'noMore';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onLoad(async () => {
|
||||
getGoodsList();
|
||||
});
|
||||
|
||||
// 加载更多
|
||||
function loadmore() {
|
||||
if (state.loadStatus !== 'noMore') {
|
||||
getGoodsList(state.pagination.current_page + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 上拉加载更多
|
||||
onReachBottom(() => {
|
||||
loadmore();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goods-item {
|
||||
.commission-num {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: $red;
|
||||
}
|
||||
|
||||
.share-btn {
|
||||
width: 120rpx;
|
||||
height: 50rpx;
|
||||
border-radius: 25rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
61
pages/commission/index.vue
Normal file
61
pages/commission/index.vue
Normal file
@@ -0,0 +1,61 @@
|
||||
<!-- 分销中心 -->
|
||||
<template>
|
||||
<s-layout navbar="inner" class="index-wrap" title="分销中心" :bgStyle="bgStyle" onShareAppMessage>
|
||||
<!-- 分销商信息 -->
|
||||
<commission-info />
|
||||
<!-- 账户信息 -->
|
||||
<account-info />
|
||||
<!-- 菜单栏 -->
|
||||
<commission-menu />
|
||||
<!-- 分销记录 -->
|
||||
<commission-log />
|
||||
<!-- 弹框 -->
|
||||
<commission-condition :error="state.error" :errorData="state.errorData" />
|
||||
|
||||
<!-- 权限 -->
|
||||
<commission-auth :error="state.error" @getAgentInfo="getAgentInfo" />
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { onShow } from '@dcloudio/uni-app';
|
||||
import { computed, reactive } from 'vue';
|
||||
import commissionInfo from './components/commission-info.vue';
|
||||
import accountInfo from './components/account-info.vue';
|
||||
import commissionLog from './components/commission-log.vue';
|
||||
import commissionMenu from './components/commission-menu.vue';
|
||||
import commissionAuth from './components/commission-auth.vue';
|
||||
import commissionCondition from './components/commission-condition.vue';
|
||||
|
||||
const state = reactive({
|
||||
error: 0,
|
||||
errorData: {},
|
||||
config: {
|
||||
background: '/storage/default/20220704/29ac76a3c9d0d983200d612e45a052ca.png',
|
||||
},
|
||||
});
|
||||
|
||||
const agentInfo = computed(() => sheep.$store('user').agentInfo);
|
||||
|
||||
const bgStyle = {
|
||||
color: '#F7D598',
|
||||
};
|
||||
|
||||
async function getAgentInfo() {
|
||||
const { error, data } = await sheep.$store('user').getAgentInfo();
|
||||
if (error !== 0) {
|
||||
state.error = error;
|
||||
state.errorData = data;
|
||||
}
|
||||
}
|
||||
onShow(() => {
|
||||
getAgentInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.page-main) {
|
||||
background-size: 100% 100% !important;
|
||||
}
|
||||
</style>
|
||||
417
pages/commission/order.vue
Normal file
417
pages/commission/order.vue
Normal file
@@ -0,0 +1,417 @@
|
||||
<!-- 分销订单 -->
|
||||
<template>
|
||||
<s-layout title="分销订单" :class="state.scrollTop ? 'order-warp' : ''" navbar="inner">
|
||||
<view
|
||||
class="header-box"
|
||||
:style="[
|
||||
{
|
||||
marginTop: '-' + Number(statusBarHeight + 88) + 'rpx',
|
||||
paddingTop: Number(statusBarHeight + 108) + 'rpx',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<!-- 团队数据总览 -->
|
||||
<view class="team-data-box ss-flex ss-col-center ss-row-between">
|
||||
<view class="data-card">
|
||||
<view class="total-item">
|
||||
<view class="item-title">团队订单数量(单)</view>
|
||||
<view class="total-num">
|
||||
{{ state.agentInfo.child_order_count_all || 0 }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="category-item ss-flex">
|
||||
<view class="ss-flex-1">
|
||||
<view class="item-title">一级订单</view>
|
||||
<view class="category-num">
|
||||
{{ state.agentInfo.child_order_count_1 || 0 }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="ss-flex-1">
|
||||
<view class="item-title">二级订单</view>
|
||||
<view class="category-num">
|
||||
{{ state.agentInfo.child_order_count_2 || 0 }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="data-card">
|
||||
<view class="total-item">
|
||||
<view class="item-title">团队订单金额(元)</view>
|
||||
<view class="total-num">
|
||||
{{ state.agentInfo.child_order_money_all || '0.00' }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="category-item ss-flex">
|
||||
<view class="ss-flex-1">
|
||||
<view class="item-title">一级订单</view>
|
||||
<view class="category-num">
|
||||
{{ state.agentInfo.child_order_money_1 || '0.00' }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="ss-flex-1">
|
||||
<view class="item-title">二级订单</view>
|
||||
<view class="category-num">
|
||||
{{ state.agentInfo.child_order_money_2 || '0.00' }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 自购 -->
|
||||
<view class="direct-box ss-flex ss-row-between">
|
||||
<view class="direct-item">
|
||||
<view class="item-title">自购分销订单数量(单)</view>
|
||||
<view class="item-value">
|
||||
{{ state.agentInfo.child_order_count_0 || 0 }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="direct-item">
|
||||
<view class="item-title">自购分销订单金额(元)</view>
|
||||
<view class="item-value">
|
||||
{{ state.agentInfo.child_order_money_0 || '0.00' }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- tab -->
|
||||
<su-sticky bgColor="#fff">
|
||||
<su-tabs
|
||||
:list="tabMaps"
|
||||
:scrollable="false"
|
||||
:current="state.currentTab"
|
||||
@change="onTabsChange"
|
||||
>
|
||||
</su-tabs>
|
||||
</su-sticky>
|
||||
|
||||
<!-- 订单 -->
|
||||
<view class="order-box">
|
||||
<view class="order-item" v-for="item in state.pagination.data" :key="item">
|
||||
<view class="order-header">
|
||||
<view class="no-box ss-flex ss-col-center ss-row-between">
|
||||
<text class="order-code">订单编号:{{ item.order.order_sn }}</text>
|
||||
<text class="order-state">{{ item.order_item.status_text }}</text>
|
||||
</view>
|
||||
<view class="order-from ss-flex ss-col-center ss-row-between">
|
||||
<view class="from-user ss-flex ss-col-center">
|
||||
<text>下单人:</text>
|
||||
<image class="user-avatar" :src="sheep.$url.cdn(item.buyer.avatar)" mode="aspectFill">
|
||||
</image>
|
||||
<text class="user-name">{{ item.buyer.nickname }}</text>
|
||||
</view>
|
||||
<view class="order-time">{{ item.create_time }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<s-goods-item
|
||||
class="border-bottom"
|
||||
:img="item.order_item.goods_image"
|
||||
:title="item.order_item.goods_title"
|
||||
:skuText="item.order_item.goods_sku_text"
|
||||
:price="item.order_item.goods_price"
|
||||
:num="item.order_item.goods_num"
|
||||
>
|
||||
<template #rightBottom>
|
||||
<view class="ss-flex commission-box ss-row-between ss-m-t-10">
|
||||
<view class="ss-flex">
|
||||
<text class="name">佣金:</text>
|
||||
<text class="commission-num">{{ item.rewards[0]?.commission }}</text>
|
||||
</view>
|
||||
<view class="order-status">
|
||||
{{ item.commission_order_status_text }}
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</s-goods-item>
|
||||
</view>
|
||||
<!-- 数据为空 -->
|
||||
<s-empty v-if="state.pagination.total === 0" icon="/static/order-empty.png" text="暂无订单">
|
||||
</s-empty>
|
||||
<!-- 加载更多 -->
|
||||
<uni-load-more
|
||||
v-if="state.pagination.total > 0"
|
||||
:status="state.loadStatus"
|
||||
:content-text="{
|
||||
contentdown: '上拉加载更多',
|
||||
}"
|
||||
@tap="loadmore"
|
||||
/>
|
||||
</view>
|
||||
<!-- </view> -->
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
|
||||
import { computed, reactive } from 'vue';
|
||||
import _ from 'lodash';
|
||||
import { onPageScroll } from '@dcloudio/uni-app';
|
||||
|
||||
const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
|
||||
const headerBg = sheep.$url.css('/static/img/shop/user/withdraw_bg.png');
|
||||
onPageScroll((e) => {
|
||||
if (e.scrollTop > 100) {
|
||||
state.scrollTop = false;
|
||||
} else {
|
||||
state.scrollTop = true;
|
||||
}
|
||||
});
|
||||
|
||||
const state = reactive({
|
||||
pagination: {
|
||||
data: [],
|
||||
current_page: 1,
|
||||
total: 1,
|
||||
last_page: 1,
|
||||
},
|
||||
loadStatus: '',
|
||||
currentTab: 0,
|
||||
agentInfo: {},
|
||||
scrollTop: false,
|
||||
});
|
||||
|
||||
const tabMaps = [
|
||||
{
|
||||
name: '全部',
|
||||
value: 'all',
|
||||
},
|
||||
// {
|
||||
// name: '不计入',
|
||||
// value: 'no'
|
||||
// },
|
||||
{
|
||||
name: '已计入',
|
||||
value: 'yes',
|
||||
},
|
||||
{
|
||||
name: '已扣除',
|
||||
value: 'back',
|
||||
},
|
||||
{
|
||||
name: '已取消',
|
||||
value: 'cancel',
|
||||
},
|
||||
];
|
||||
// 切换选项卡
|
||||
function onTabsChange(e) {
|
||||
state.pagination = {
|
||||
data: [],
|
||||
current_page: 1,
|
||||
total: 1,
|
||||
last_page: 1,
|
||||
};
|
||||
state.currentTab = e.index;
|
||||
getOrderList();
|
||||
}
|
||||
|
||||
// 获取订单列表
|
||||
async function getOrderList(page = 1, list_rows = 5) {
|
||||
state.loadStatus = 'loading';
|
||||
let res = await sheep.$api.commission.order({
|
||||
type: tabMaps[state.currentTab].value,
|
||||
list_rows,
|
||||
page,
|
||||
});
|
||||
if (res.error === 0) {
|
||||
let orderList = _.concat(state.pagination.data, res.data.data);
|
||||
state.pagination = {
|
||||
...res.data,
|
||||
data: orderList,
|
||||
};
|
||||
if (state.pagination.current_page < state.pagination.last_page) {
|
||||
state.loadStatus = 'more';
|
||||
} else {
|
||||
state.loadStatus = 'noMore';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getAgentInfo() {
|
||||
const { error, data, msg } = await sheep.$api.commission.agent();
|
||||
if (error === 0) {
|
||||
state.agentInfo = data;
|
||||
}
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
getAgentInfo();
|
||||
getOrderList();
|
||||
});
|
||||
|
||||
// 加载更多
|
||||
function loadmore() {
|
||||
if (state.loadStatus !== 'noMore') {
|
||||
getOrderList(state.pagination.current_page + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 上拉加载更多
|
||||
onReachBottom(() => {
|
||||
loadmore();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.header-box {
|
||||
box-sizing: border-box;
|
||||
padding: 0 20rpx 20rpx 20rpx;
|
||||
width: 750rpx;
|
||||
background: v-bind(headerBg) no-repeat,
|
||||
linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
background-size: 750rpx 100%;
|
||||
// 团队信息总览
|
||||
.team-data-box {
|
||||
.data-card {
|
||||
width: 305rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 20rpx;
|
||||
padding: 20rpx;
|
||||
|
||||
.total-item {
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
.item-title {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #999999;
|
||||
line-height: normal;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.total-num {
|
||||
font-size: 38rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
font-family: OPPOSANS;
|
||||
}
|
||||
}
|
||||
|
||||
.category-num {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
font-family: OPPOSANS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 直推
|
||||
.direct-box {
|
||||
margin-top: 20rpx;
|
||||
|
||||
.direct-item {
|
||||
width: 340rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 20rpx;
|
||||
padding: 20rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
.item-title {
|
||||
font-size: 22rpx;
|
||||
font-weight: 500;
|
||||
color: #999999;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.item-value {
|
||||
font-size: 38rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
font-family: OPPOSANS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 订单
|
||||
.order-box {
|
||||
.order-item {
|
||||
background: #ffffff;
|
||||
border-radius: 10rpx;
|
||||
margin: 20rpx;
|
||||
|
||||
.order-footer {
|
||||
padding: 20rpx;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.order-header {
|
||||
.no-box {
|
||||
padding: 20rpx;
|
||||
|
||||
.order-code {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.order-state {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: var(--ui-BG-Main);
|
||||
}
|
||||
}
|
||||
|
||||
.order-from {
|
||||
padding: 20rpx;
|
||||
|
||||
.from-user {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #666666;
|
||||
|
||||
.user-avatar {
|
||||
width: 26rpx;
|
||||
height: 26rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
|
||||
.order-time {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.commission-box {
|
||||
.name {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
|
||||
.commission-num {
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
color: $red;
|
||||
font-family: OPPOSANS;
|
||||
|
||||
&::before {
|
||||
content: '¥';
|
||||
font-size: 22rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.order-status {
|
||||
line-height: 30rpx;
|
||||
padding: 0 10rpx;
|
||||
border-radius: 30rpx;
|
||||
margin-left: 20rpx;
|
||||
font-size: 24rpx;
|
||||
color: var(--ui-BG-Main);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
173
pages/commission/share-log.vue
Normal file
173
pages/commission/share-log.vue
Normal file
@@ -0,0 +1,173 @@
|
||||
<!-- 分销记录 -->
|
||||
<template>
|
||||
<s-layout title="分享记录">
|
||||
<view class="distraction-share-wrap">
|
||||
<view class="share-log-box">
|
||||
<!-- 分享记录列表 -->
|
||||
<view class="log-list ss-flex" v-for="item in state.pagination.data" :key="item.id">
|
||||
<view class="log-avatar-wrap">
|
||||
<image
|
||||
class="log-avatar"
|
||||
:src="sheep.$url.cdn(item.user?.avatar)"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
</view>
|
||||
|
||||
<view class="item-right">
|
||||
<view class="name">{{ item.user?.nickname }}</view>
|
||||
<view class="content ss-flex">
|
||||
<view v-if="item.ext?.image" class="content-img-wrap">
|
||||
<image class="content-img" :src="sheep.$url.cdn(item.ext?.image)" mode="aspectFill">
|
||||
</image>
|
||||
</view>
|
||||
|
||||
<view v-if="item.ext?.memo" class="content-text">
|
||||
{{ item.ext?.memo }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="item-bottom ss-flex ss-row-between">
|
||||
<view class="from-text"></view>
|
||||
<view class="time">{{ dayjs(item.create_time).fromNow() }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<s-empty
|
||||
v-if="state.pagination.total === 0"
|
||||
icon="/static/data-empty.png"
|
||||
text="暂无分享记录"
|
||||
>
|
||||
</s-empty>
|
||||
<!-- 加载更多 -->
|
||||
<uni-load-more
|
||||
v-if="state.pagination.total > 0"
|
||||
:status="state.loadStatus"
|
||||
:content-text="{
|
||||
contentdown: '上拉加载更多',
|
||||
}"
|
||||
@tap="loadmore"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { computed, reactive } from 'vue';
|
||||
import _ from 'lodash';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const state = reactive({
|
||||
pagination: {
|
||||
data: [],
|
||||
current_page: 1,
|
||||
total: 1,
|
||||
last_page: 1,
|
||||
},
|
||||
loadStatus: '',
|
||||
});
|
||||
|
||||
async function getShareLog(page = 1, list_rows = 8) {
|
||||
state.loadStatus = 'loading';
|
||||
let res = await sheep.$api.user.share.list({
|
||||
list_rows,
|
||||
page,
|
||||
});
|
||||
if (res.error === 0) {
|
||||
let orderList = _.concat(state.pagination.data, res.data.data);
|
||||
state.pagination = {
|
||||
...res.data,
|
||||
data: orderList,
|
||||
};
|
||||
if (state.pagination.current_page < state.pagination.last_page) {
|
||||
state.loadStatus = 'more';
|
||||
} else {
|
||||
state.loadStatus = 'noMore';
|
||||
}
|
||||
}
|
||||
}
|
||||
// 加载更多
|
||||
function loadmore() {
|
||||
if (state.loadStatus !== 'noMore') {
|
||||
getShareLog(state.pagination.current_page + 1);
|
||||
}
|
||||
}
|
||||
onLoad(async () => {
|
||||
getShareLog();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.share-log-box {
|
||||
// 分享记录列表
|
||||
.log-list {
|
||||
background-color: #fff;
|
||||
padding: 30rpx;
|
||||
margin: 10rpx 0;
|
||||
align-items: flex-start;
|
||||
|
||||
.log-avatar-wrap {
|
||||
margin-right: 14rpx;
|
||||
|
||||
.log-avatar {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.item-right {
|
||||
flex: 1;
|
||||
|
||||
.name {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #7f7aa5;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.content {
|
||||
background: rgba(241, 241, 241, 0.46);
|
||||
border-radius: 2rpx;
|
||||
padding: 10rpx;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.content-img-wrap {
|
||||
margin-right: 16rpx;
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
|
||||
.content-img {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.content-text {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
|
||||
.item-bottom {
|
||||
width: 100%;
|
||||
|
||||
.time {
|
||||
font-size: 22rpx;
|
||||
font-weight: 500;
|
||||
color: #c8c8c8;
|
||||
}
|
||||
|
||||
.from-text {
|
||||
font-size: 22rpx;
|
||||
font-weight: 500;
|
||||
color: #c8c8c8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
257
pages/commission/team.vue
Normal file
257
pages/commission/team.vue
Normal file
@@ -0,0 +1,257 @@
|
||||
<!-- 页面 -->
|
||||
<template>
|
||||
<s-layout title="我的团队" :class="state.scrollTop ? 'team-wrap' : ''" navbar="inner">
|
||||
<view
|
||||
class="header-box"
|
||||
:style="[
|
||||
{
|
||||
marginTop: '-' + Number(statusBarHeight + 88) + 'rpx',
|
||||
paddingTop: Number(statusBarHeight + 108) + 'rpx',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<!-- 推荐人 -->
|
||||
<view v-if="userInfo.parent_user" class="referrer-box ss-flex ss-col-center">
|
||||
推荐人:
|
||||
<image
|
||||
class="referrer-avatar ss-m-r-10"
|
||||
:src="sheep.$url.cdn(userInfo.parent_user.avatar)"
|
||||
mode="aspectFill"
|
||||
>
|
||||
</image>
|
||||
{{ userInfo.parent_user.nickname }}
|
||||
</view>
|
||||
<!-- 团队数据总览 -->
|
||||
<view class="team-data-box ss-flex ss-col-center ss-row-between">
|
||||
<view class="data-card">
|
||||
<view class="total-item">
|
||||
<view class="item-title">团队总人数(人)</view>
|
||||
<view class="total-num">{{ agentInfo.child_user_count_all || 0 }}</view>
|
||||
</view>
|
||||
<view class="category-item ss-flex">
|
||||
<view class="ss-flex-1">
|
||||
<view class="item-title">一级成员</view>
|
||||
<view class="category-num">{{ agentInfo.child_user_count_1 || 0 }}</view>
|
||||
</view>
|
||||
<view class="ss-flex-1">
|
||||
<view class="item-title">二级成员</view>
|
||||
<view class="category-num">{{ agentInfo.child_user_count_2 || 0 }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="data-card">
|
||||
<view class="total-item">
|
||||
<view class="item-title">团队分销商人数(人)</view>
|
||||
<view class="total-num">{{ agentInfo.child_agent_count_all || 0 }}</view>
|
||||
</view>
|
||||
<view class="category-item ss-flex">
|
||||
<view class="ss-flex-1">
|
||||
<view class="item-title">一级分销商</view>
|
||||
<view class="category-num">{{ agentInfo.child_agent_count_1 || 0 }}</view>
|
||||
</view>
|
||||
<view class="ss-flex-1">
|
||||
<view class="item-title">二级分销商</view>
|
||||
<view class="category-num">{{ agentInfo.child_agent_count_2 || 0 }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="list-box">
|
||||
<uni-list :border="false">
|
||||
<uni-list-chat
|
||||
v-for="item in state.pagination.data"
|
||||
:key="item.id"
|
||||
:avatar-circle="true"
|
||||
:title="item.nickname"
|
||||
:avatar="sheep.$url.cdn(item.avatar)"
|
||||
:note="filterUserNum(item.agent?.child_user_count_1)"
|
||||
>
|
||||
<view class="chat-custom-right">
|
||||
<view v-if="item.agent?.level_info" class="tag-box ss-flex ss-col-center">
|
||||
<image
|
||||
class="tag-img"
|
||||
:src="sheep.$url.cdn(item.agent.level_info.image)"
|
||||
mode="aspectFill"
|
||||
>
|
||||
</image>
|
||||
<text class="tag-title">{{ item.agent.level_info.name }}</text>
|
||||
</view>
|
||||
|
||||
<text class="time-text">{{ item.create_time }}</text>
|
||||
</view>
|
||||
</uni-list-chat>
|
||||
</uni-list>
|
||||
</view>
|
||||
<s-empty v-if="state.pagination.total === 0" icon="/static/data-empty.png" text="暂无团队信息">
|
||||
</s-empty>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
|
||||
import { computed, reactive } from 'vue';
|
||||
import _ from 'lodash';
|
||||
import { onPageScroll } from '@dcloudio/uni-app';
|
||||
|
||||
const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
|
||||
const agentInfo = computed(() => sheep.$store('user').agentInfo);
|
||||
const userInfo = computed(() => sheep.$store('user').userInfo);
|
||||
const headerBg = sheep.$url.css('/static/img/shop/user/withdraw_bg.png');
|
||||
|
||||
onPageScroll((e) => {
|
||||
if (e.scrollTop > 100) {
|
||||
state.scrollTop = false;
|
||||
} else {
|
||||
state.scrollTop = true;
|
||||
}
|
||||
});
|
||||
const state = reactive({
|
||||
pagination: {
|
||||
data: [],
|
||||
current_page: 1,
|
||||
total: 1,
|
||||
last_page: 1,
|
||||
},
|
||||
loadStatus: '',
|
||||
});
|
||||
|
||||
function filterUserNum(num) {
|
||||
if (_.isNil(num)) {
|
||||
return '';
|
||||
}
|
||||
return `下级团队${num}人`;
|
||||
}
|
||||
|
||||
async function getTeamList(page = 1, list_rows = 8) {
|
||||
state.loadStatus = 'loading';
|
||||
let res = await sheep.$api.commission.team({
|
||||
list_rows,
|
||||
page,
|
||||
});
|
||||
if (res.error === 0) {
|
||||
let orderList = _.concat(state.pagination.data, res.data.data);
|
||||
state.pagination = {
|
||||
...res.data,
|
||||
data: orderList,
|
||||
};
|
||||
if (state.pagination.current_page < state.pagination.last_page) {
|
||||
state.loadStatus = 'more';
|
||||
} else {
|
||||
state.loadStatus = 'noMore';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onLoad(async () => {
|
||||
getTeamList();
|
||||
});
|
||||
|
||||
// 加载更多
|
||||
function loadmore() {
|
||||
if (state.loadStatus !== 'noMore') {
|
||||
getTeamList(state.pagination.current_page + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 上拉加载更多
|
||||
onReachBottom(() => {
|
||||
loadmore();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.header-box {
|
||||
box-sizing: border-box;
|
||||
padding: 0 20rpx 20rpx 20rpx;
|
||||
width: 750rpx;
|
||||
z-index: 3;
|
||||
position: relative;
|
||||
background: v-bind(headerBg) no-repeat,
|
||||
linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
background-size: 750rpx 100%;
|
||||
// 团队信息总览
|
||||
.team-data-box {
|
||||
.data-card {
|
||||
width: 305rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 20rpx;
|
||||
padding: 20rpx;
|
||||
|
||||
.item-title {
|
||||
font-size: 22rpx;
|
||||
font-weight: 500;
|
||||
color: #999999;
|
||||
line-height: 30rpx;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.total-item {
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.total-num {
|
||||
font-size: 38rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
font-family: OPPOSANS;
|
||||
}
|
||||
|
||||
.category-num {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
font-family: OPPOSANS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.list-box {
|
||||
z-index: 3;
|
||||
position: relative;
|
||||
}
|
||||
.chat-custom-right {
|
||||
.time-text {
|
||||
font-size: 22rpx;
|
||||
font-weight: 400;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.tag-box {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 21rpx;
|
||||
line-height: 30rpx;
|
||||
padding: 5rpx 10rpx;
|
||||
width: 140rpx;
|
||||
|
||||
.tag-img {
|
||||
width: 34rpx;
|
||||
height: 34rpx;
|
||||
margin-right: 6rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.tag-title {
|
||||
font-size: 18rpx;
|
||||
font-weight: 500;
|
||||
color: rgba(255, 255, 255, 1);
|
||||
line-height: 20rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 推荐人
|
||||
.referrer-box {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
padding: 20rpx;
|
||||
|
||||
.referrer-avatar {
|
||||
width: 34rpx;
|
||||
height: 34rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
372
pages/coupon/detail.vue
Normal file
372
pages/coupon/detail.vue
Normal file
@@ -0,0 +1,372 @@
|
||||
<!-- 优惠券详情 -->
|
||||
<template>
|
||||
<s-layout title="优惠券详情">
|
||||
<view class="bg-white">
|
||||
<!-- 详情卡片 -->
|
||||
<view class="detail-wrap ss-p-20">
|
||||
<view class="detail-box">
|
||||
<view class="tag-box ss-flex ss-col-center ss-row-center">
|
||||
<image
|
||||
class="tag-image"
|
||||
:src="sheep.$url.static('/static/img/shop/app/coupon_icon.png')"
|
||||
mode="aspectFit"
|
||||
></image>
|
||||
</view>
|
||||
<view class="top ss-flex-col ss-col-center">
|
||||
<view class="title ss-m-t-50 ss-m-b-20 ss-m-x-20">{{ state.list.name }}</view>
|
||||
<view class="subtitle ss-m-b-50">{{ state.list.amount_text }}</view>
|
||||
<button
|
||||
class="ss-reset-button ss-m-b-30"
|
||||
:class="
|
||||
state.list.get_status == 'can_get' || state.list.get_status == 'can_use'
|
||||
? 'use-btn'
|
||||
: 'disable-btn'
|
||||
"
|
||||
:disabled="
|
||||
(state.list.get_status != 'can_get' && state.list.get_status != 'can_use') ||
|
||||
state.userCouponId
|
||||
"
|
||||
@click="getCoupon"
|
||||
>
|
||||
{{ state.list.get_status_text }}
|
||||
</button>
|
||||
<view
|
||||
class="time ss-m-y-30"
|
||||
v-if="
|
||||
state.list.get_status == 'can_get' ||
|
||||
state.list.get_status == 'cannot_get' ||
|
||||
state.list.get_status == 'get_over'
|
||||
"
|
||||
>
|
||||
领取时间:{{ state.list.get_start_time }}至{{ state.list.get_end_time }}
|
||||
</view>
|
||||
<view class="time ss-m-y-30" v-else>
|
||||
有效期:{{ state.list.use_start_time }}至{{ state.list.use_end_time }}
|
||||
</view>
|
||||
<view class="coupon-line ss-m-t-14"></view>
|
||||
</view>
|
||||
<view class="bottom">
|
||||
<view class="type ss-flex ss-col-center ss-row-between ss-p-x-30">
|
||||
<view>优惠券类型</view>
|
||||
<view>{{ state.list.type_text }}</view>
|
||||
</view>
|
||||
<uni-collapse>
|
||||
<uni-collapse-item title="优惠券说明" v-if="state.list.description">
|
||||
<view class="content ss-p-b-20">
|
||||
<text class="des ss-p-l-30">{{ state.list.description }}</text>
|
||||
</view>
|
||||
</uni-collapse-item>
|
||||
</uni-collapse>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 适用商品 -->
|
||||
<view
|
||||
class="all-user ss-flex ss-row-center ss-col-center"
|
||||
v-if="state.list.use_scope == 'all_use'"
|
||||
>
|
||||
{{ state.list.use_scope_text }}
|
||||
</view>
|
||||
|
||||
<su-sticky v-else bgColor="#fff">
|
||||
<view class="goods-title ss-p-20">{{ state.list.use_scope_text }}</view>
|
||||
<su-tabs
|
||||
:scrollable="true"
|
||||
:list="state.tabMaps"
|
||||
@change="onTabsChange"
|
||||
:current="state.currentTab"
|
||||
v-if="state.list.use_scope == 'category'"
|
||||
></su-tabs>
|
||||
</su-sticky>
|
||||
<view v-if="state.list.use_scope == 'goods' || state.list.use_scope == 'disabled_goods'">
|
||||
<view v-for="(item, index) in state.list.items_value" :key="index">
|
||||
<s-goods-column
|
||||
class="ss-m-20"
|
||||
size="lg"
|
||||
:data="item"
|
||||
:titleColor="props.goodsFieldsStyle?.title?.color"
|
||||
:subTitleColor="props.goodsFieldsStyle?.subtitle?.color"
|
||||
@click="sheep.$router.go('/pages/goods/index', { id: item.id })"
|
||||
:goodsFields="{
|
||||
title: { show: true },
|
||||
subtitle: { show: true },
|
||||
price: { show: true },
|
||||
original_price: { show: true },
|
||||
sales: { show: true },
|
||||
stock: { show: false },
|
||||
}"
|
||||
:buttonShow="state.list.use_scope != 'disabled_goods'"
|
||||
></s-goods-column>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="state.list.use_scope == 'category'">
|
||||
<view v-for="(item, index) in state.pagination.data" :key="index">
|
||||
<s-goods-column
|
||||
class="ss-m-20"
|
||||
size="lg"
|
||||
:data="item"
|
||||
:titleColor="props.goodsFieldsStyle?.title?.color"
|
||||
:subTitleColor="props.goodsFieldsStyle?.subtitle?.color"
|
||||
@click="sheep.$router.go('/pages/goods/index', { id: item.id })"
|
||||
:goodsFields="{
|
||||
title: { show: true },
|
||||
subtitle: { show: true },
|
||||
price: { show: true },
|
||||
original_price: { show: true },
|
||||
sales: { show: true },
|
||||
stock: { show: false },
|
||||
}"
|
||||
:buttonShow="state.list.use_scope != 'disabled_goods'"
|
||||
></s-goods-column>
|
||||
</view>
|
||||
</view>
|
||||
<uni-load-more
|
||||
v-if="state.pagination.total > 0 && state.list.use_scope == 'category'"
|
||||
:status="state.loadStatus"
|
||||
:content-text="{
|
||||
contentdown: '上拉加载更多',
|
||||
}"
|
||||
@tap="loadmore"
|
||||
/>
|
||||
<s-empty
|
||||
v-if="state.list.use_scope == 'category' && state.pagination.total === 0"
|
||||
paddingTop="0"
|
||||
icon="/static/soldout-empty.png"
|
||||
text="暂无商品"
|
||||
>
|
||||
</s-empty>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
|
||||
import { reactive } from 'vue';
|
||||
import _ from 'lodash';
|
||||
|
||||
const pagination = {
|
||||
data: [],
|
||||
current_page: 1,
|
||||
total: 1,
|
||||
last_page: 1,
|
||||
};
|
||||
const state = reactive({
|
||||
list: {},
|
||||
couponId: 0,
|
||||
userCouponId: 0,
|
||||
pagination: {
|
||||
data: [],
|
||||
current_page: 1,
|
||||
total: 1,
|
||||
last_page: 1,
|
||||
},
|
||||
tabMaps: [],
|
||||
loadStatus: '',
|
||||
categoryId: 0,
|
||||
});
|
||||
|
||||
// 接收参数
|
||||
const props = defineProps({
|
||||
includes: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
list: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
goodsFieldsStyle: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
buyData: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
});
|
||||
|
||||
function onTabsChange(e) {
|
||||
state.pagination = pagination;
|
||||
state.currentTab = e.index;
|
||||
state.categoryId = e.value;
|
||||
getGoodsList(state.categoryId);
|
||||
}
|
||||
async function getGoodsList(categoryId, page = 1, list_rows = 5) {
|
||||
state.loadStatus = 'loading';
|
||||
const res = await sheep.$api.goods.list({
|
||||
category_id: categoryId,
|
||||
list_rows,
|
||||
page,
|
||||
is_category_deep: false,
|
||||
});
|
||||
if (res.error === 0) {
|
||||
let couponlist = _.concat(state.pagination.data, res.data.data);
|
||||
state.pagination = {
|
||||
...res.data,
|
||||
data: couponlist,
|
||||
};
|
||||
if (state.pagination.current_page < state.pagination.last_page) {
|
||||
state.loadStatus = 'more';
|
||||
} else {
|
||||
state.loadStatus = 'noMore';
|
||||
}
|
||||
}
|
||||
}
|
||||
async function getCoupon() {
|
||||
const { error, msg } = await sheep.$api.coupon.get(state.couponId);
|
||||
if (error === 0) {
|
||||
uni.showToast({
|
||||
title: msg,
|
||||
});
|
||||
setTimeout(() => {
|
||||
getCouponContent(state.couponId, state.userCouponId);
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
async function getCouponContent(id, c) {
|
||||
const { data } = await sheep.$api.coupon.detail(id, c);
|
||||
state.list = data;
|
||||
data.items_value.forEach((i) => {
|
||||
state.tabMaps.push({ name: i.name, value: i.id });
|
||||
});
|
||||
state.pagination = pagination;
|
||||
if (state.list.use_scope == 'category') {
|
||||
getGoodsList(state.tabMaps[0].value);
|
||||
}
|
||||
}
|
||||
// 加载更多
|
||||
function loadmore() {
|
||||
if (state.loadStatus !== 'noMore') {
|
||||
getGoodsList(state.categoryId, state.pagination.current_page + 1);
|
||||
}
|
||||
}
|
||||
onLoad((options) => {
|
||||
state.couponId = options.id;
|
||||
state.userCouponId = options.user_coupon_id;
|
||||
getCouponContent(state.couponId, state.userCouponId);
|
||||
});
|
||||
// 上拉加载更多
|
||||
onReachBottom(() => {
|
||||
loadmore();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goods-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.detail-wrap {
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
var(--ui-BG-Main),
|
||||
var(--ui-BG-Main-gradient),
|
||||
var(--ui-BG-Main),
|
||||
#fff
|
||||
);
|
||||
}
|
||||
|
||||
.detail-box {
|
||||
// background-color: var(--ui-BG);
|
||||
border-radius: 6rpx;
|
||||
position: relative;
|
||||
margin-top: 100rpx;
|
||||
.tag-box {
|
||||
width: 140rpx;
|
||||
height: 140rpx;
|
||||
background: var(--ui-BG);
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
top: -70rpx;
|
||||
left: 50%;
|
||||
z-index: 6;
|
||||
transform: translateX(-50%);
|
||||
|
||||
.tag-image {
|
||||
width: 104rpx;
|
||||
height: 104rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.top {
|
||||
background-color: #fff;
|
||||
border-radius: 20rpx 20rpx 0 0;
|
||||
-webkit-mask: radial-gradient(circle at 16rpx 100%, #0000 16rpx, red 0) -16rpx;
|
||||
padding: 110rpx 0 0 0;
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
|
||||
.title {
|
||||
font-size: 40rpx;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.use-btn {
|
||||
width: 386rpx;
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
border-radius: 40rpx;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
.disable-btn {
|
||||
width: 386rpx;
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
background: #e5e5e5;
|
||||
border-radius: 40rpx;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.coupon-line {
|
||||
width: 95%;
|
||||
border-bottom: 2rpx solid #eeeeee;
|
||||
}
|
||||
}
|
||||
|
||||
.bottom {
|
||||
background-color: #fff;
|
||||
border-radius: 0 0 20rpx 20rpx;
|
||||
-webkit-mask: radial-gradient(circle at 16rpx 0%, #0000 16rpx, red 0) -16rpx;
|
||||
padding: 40rpx 30rpx;
|
||||
|
||||
.type {
|
||||
height: 96rpx;
|
||||
border-bottom: 2rpx solid #eeeeee;
|
||||
}
|
||||
}
|
||||
|
||||
.des {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #666666;
|
||||
}
|
||||
}
|
||||
|
||||
.all-user {
|
||||
width: 100%;
|
||||
height: 300rpx;
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
</style>
|
||||
261
pages/coupon/list.vue
Normal file
261
pages/coupon/list.vue
Normal file
@@ -0,0 +1,261 @@
|
||||
<!-- 优惠券中心 -->
|
||||
<template>
|
||||
<s-layout title="优惠券" :bgStyle="{ color: '#f2f2f2' }">
|
||||
<su-sticky bgColor="#fff">
|
||||
<su-tabs :list="tabMaps" :scrollable="false" @change="onTabsChange" :current="state.currentTab"></su-tabs>
|
||||
</su-sticky>
|
||||
<s-empty v-if="state.pagination.total === 0" icon="/static/coupon-empty.png" text="暂无优惠券"></s-empty>
|
||||
<template v-if="state.currentTab == '0'">
|
||||
<view v-for="item in state.pagination.list" :key="item.id">
|
||||
<s-coupon-list :data="item">
|
||||
<!-- @tap="
|
||||
sheep.$router.go('/pages/coupon/detail', {
|
||||
id: item.id,
|
||||
})
|
||||
" -->
|
||||
<template #default>
|
||||
<button class="ss-reset-button card-btn ss-flex ss-row-center ss-col-center"
|
||||
:class="item.get_status != 'can_get' ? 'border-btn' : ''" @click.stop="getBuy(item.id)"
|
||||
:disabled="item.get_status != 'can_get'">
|
||||
<!-- {{ item.status_text }} -->
|
||||
{{item.status_text|| '立即使用' }}
|
||||
</button>
|
||||
</template>
|
||||
</s-coupon-list>
|
||||
</view>
|
||||
</template>
|
||||
<template v-else>
|
||||
<view v-for="item in state.pagination.list" :key="item.id">
|
||||
<s-coupon-list :data="item" type="user">
|
||||
<!-- @tap="
|
||||
sheep.$router.go('/pages/coupon/detail', {
|
||||
id: item.id,
|
||||
})
|
||||
" -->
|
||||
<template #default>
|
||||
<button class="ss-reset-button card-btn ss-flex ss-row-center ss-col-center" :class="
|
||||
item.status == 'can_get' || item.status == 'can_use'
|
||||
? ''
|
||||
: item.status == 'used' || item.status == 'expired'
|
||||
? 'disabled-btn'
|
||||
: 'border-btn'
|
||||
" :disabled="item.status != 'can_get' && item.status != 'can_use'" @click.stop="
|
||||
sheep.$router.go('/pages/coupon/detail', {
|
||||
id: item.coupon_id,
|
||||
user_coupon_id: item.id,
|
||||
})
|
||||
">
|
||||
<!-- {{ item.status_text }} -->
|
||||
{{item.status_text|| '立即使用' }}
|
||||
</button>
|
||||
</template>
|
||||
</s-coupon-list>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- <uni-load-more v-if="state.pagination.total > 0" :status="state.loadStatus" :content-text="{
|
||||
contentdown: '上拉加载更多',
|
||||
}" @tap="loadmore" /> -->
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import {
|
||||
onLoad,
|
||||
onReachBottom
|
||||
} from '@dcloudio/uni-app';
|
||||
import {
|
||||
computed,
|
||||
reactive
|
||||
} from 'vue';
|
||||
import _ from 'lodash';
|
||||
|
||||
const pagination = {
|
||||
data: [],
|
||||
current_page: 1,
|
||||
total: 1,
|
||||
last_page: 1,
|
||||
};
|
||||
// 数据
|
||||
const state = reactive({
|
||||
currentTab: 0,
|
||||
pagination: {
|
||||
data: [],
|
||||
current_page: 1,
|
||||
total: 1,
|
||||
last_page: 1,
|
||||
},
|
||||
loadStatus: '',
|
||||
type: '1',
|
||||
});
|
||||
|
||||
const tabMaps = [
|
||||
// {
|
||||
// name: '领券中心',
|
||||
// value: 'all',
|
||||
// },
|
||||
{
|
||||
name: '已领取',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
name: '已使用',
|
||||
value: '2',
|
||||
},
|
||||
{
|
||||
name: '已失效',
|
||||
value: '3',
|
||||
},
|
||||
];
|
||||
|
||||
function onTabsChange(e) {
|
||||
state.pagination = pagination
|
||||
state.currentTab = e.index;
|
||||
state.type = e.value;
|
||||
// if (state.currentTab == 0) {
|
||||
// getData();
|
||||
// } else {
|
||||
getCoupon();
|
||||
// }
|
||||
}
|
||||
async function getData(page = 1, list_rows = 5) {
|
||||
state.loadStatus = 'loading';
|
||||
const res = await sheep.$api.coupon.list({
|
||||
list_rows,
|
||||
page
|
||||
});
|
||||
if (res.error === 0) {
|
||||
let couponlist = _.concat(state.pagination.data, res.data.data);
|
||||
state.pagination = {
|
||||
...res.data,
|
||||
data: couponlist,
|
||||
};
|
||||
if (state.pagination.current_page < state.pagination.last_page) {
|
||||
state.loadStatus = 'more';
|
||||
} else {
|
||||
state.loadStatus = 'noMore';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getCoupon(page = 1, list_rows = 5) {
|
||||
state.loadStatus = 'loading';
|
||||
let res = await sheep.$api.coupon.userCoupon({
|
||||
status: state.type,
|
||||
pageSize: list_rows,
|
||||
pageNo: page
|
||||
});
|
||||
if (res.code === 0) {
|
||||
// 拦截修改数据
|
||||
let obj = {
|
||||
1: '可用',
|
||||
2: '已用',
|
||||
3: '过期'
|
||||
}
|
||||
res.data.list = res.data.list.map(item => {
|
||||
return {
|
||||
...item,
|
||||
enough: (item.usePrice / 100).toFixed(2),
|
||||
amount: (item.discountPrice / 100).toFixed(2),
|
||||
use_start_time: sheep.$helper.timeFormat(item.validStartTime, 'yyyy-mm-dd hh:MM:ss'),
|
||||
use_end_time: sheep.$helper.timeFormat(item.validEndTime, 'yyyy-mm-dd hh:MM:ss'),
|
||||
status_text: obj[item.status]
|
||||
}
|
||||
});
|
||||
if (page >= 2) {
|
||||
let couponlist = _.concat(state.pagination.data, res.data.list);
|
||||
|
||||
state.pagination = {
|
||||
...res.data,
|
||||
data: couponlist,
|
||||
};
|
||||
console.log(state.pagination, '拿到的优惠券数据');
|
||||
} else {
|
||||
state.pagination = res.data;
|
||||
console.log(state.pagination, '拿到的优惠券数据');
|
||||
}
|
||||
// if (state.pagination.current_page < state.pagination.last_page) {
|
||||
// state.loadStatus = 'more';
|
||||
// } else {
|
||||
// state.loadStatus = 'noMore';
|
||||
// }
|
||||
}
|
||||
}
|
||||
async function getBuy(id) {
|
||||
const {
|
||||
error,
|
||||
msg
|
||||
} = await sheep.$api.coupon.get(id);
|
||||
if (error === 0) {
|
||||
uni.showToast({
|
||||
title: msg,
|
||||
});
|
||||
setTimeout(() => {
|
||||
state.pagination = pagination
|
||||
getData();
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
// 加载更多
|
||||
function loadmore() {
|
||||
if (state.loadStatus !== 'noMore') {
|
||||
if (state.currentTab == 0) {
|
||||
getData(state.pagination.current_page + 1);
|
||||
} else {
|
||||
getCoupon(state.pagination.current_page + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
onLoad((Option) => {
|
||||
// if (Option.type === 'all' || !Option.type) {
|
||||
// getData();
|
||||
// } else {
|
||||
// state.type = Option.type;
|
||||
// Option.type === 'geted' ?
|
||||
// () :
|
||||
// Option.type === 'used' ?
|
||||
// (state.currentTab = 1 && state.type = 2) :
|
||||
// (state.currentTab = 2 && state.type = 3);
|
||||
|
||||
if (Option.type == 'geted') {
|
||||
state.currentTab = 0
|
||||
state.type = 1
|
||||
} else if (Option.type == 'used') {
|
||||
state.currentTab = 1
|
||||
state.type = 2
|
||||
} else {
|
||||
state.currentTab = 2
|
||||
state.type = 3
|
||||
}
|
||||
getCoupon();
|
||||
// }
|
||||
});
|
||||
onReachBottom(() => {
|
||||
loadmore();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.card-btn {
|
||||
// width: 144rpx;
|
||||
padding: 0 16rpx;
|
||||
height: 50rpx;
|
||||
border-radius: 40rpx;
|
||||
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
color: #ffffff;
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.border-btn {
|
||||
background: linear-gradient(90deg, var(--ui-BG-Main-opacity-4), var(--ui-BG-Main-light));
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.disabled-btn {
|
||||
background: #cccccc;
|
||||
background-color: #cccccc !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
</style>
|
||||
209
pages/goods/comment/add.vue
Normal file
209
pages/goods/comment/add.vue
Normal file
@@ -0,0 +1,209 @@
|
||||
<!-- 评价 -->
|
||||
<template>
|
||||
<s-layout title="评价">
|
||||
<view>
|
||||
<view v-for="(item, index) in state.orderInfo.items" :key="item.id">
|
||||
<view v-if="item.btns.includes('comment')">
|
||||
<view class="commont-from-wrap">
|
||||
<!-- 评价商品 -->
|
||||
<s-goods-item :img="item.goods_image" :title="item.goods_title" :skuText="item.goods_sku_text"
|
||||
:price="item.goods_price" :num="item.goods_num"></s-goods-item>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<!-- 评分 -->
|
||||
<view class="star-box ss-flex ss-col-center">
|
||||
<view class="star-title ss-m-r-40">
|
||||
<!-- {{ rateMap[state.commentList[index].level] }} -->
|
||||
商品质量
|
||||
</view>
|
||||
<uni-rate v-model="state.commentList[index].level" />
|
||||
</view>
|
||||
<view class="star-box ss-flex ss-col-center">
|
||||
<view class="star-title ss-m-r-40">
|
||||
<!-- {{ rateMap[state.commentList[index].level] }} -->
|
||||
服务态度
|
||||
</view>
|
||||
<uni-rate v-model="state.commentList[index].level2" />
|
||||
</view>
|
||||
<!-- 评价 -->
|
||||
<view class="area-box">
|
||||
<uni-easyinput :inputBorder="false" type="textarea" maxlength="120" autoHeight
|
||||
v-model="state.commentList[index].content"
|
||||
placeholder="宝贝满足你的期待吗?说说你的使用心得,分享给想买的他们吧~"></uni-easyinput>
|
||||
|
||||
<view class="img-box">
|
||||
<s-uploader v-model:url="state.commentList[index].images" fileMediatype="image"
|
||||
limit="9" mode="grid" :imageStyles="{ width: '168rpx', height: '168rpx' }" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<su-fixed bottom placeholder>
|
||||
<view class="foot_box ss-flex ss-row-center ss-col-center">
|
||||
<button class="ss-reset-button post-btn ui-BG-Main-Gradient ui-Shadow-Main" @tap="onSubmit">
|
||||
发布
|
||||
</button>
|
||||
</view>
|
||||
</su-fixed>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import {
|
||||
onLoad
|
||||
} from '@dcloudio/uni-app';
|
||||
import {
|
||||
computed,
|
||||
reactive
|
||||
} from 'vue';
|
||||
|
||||
const state = reactive({
|
||||
orderInfo: {},
|
||||
commentList: [],
|
||||
orderId: null
|
||||
});
|
||||
|
||||
const rateMap = {
|
||||
1: '糟糕',
|
||||
2: '差评',
|
||||
3: '一般',
|
||||
4: '良好',
|
||||
5: '好评',
|
||||
};
|
||||
|
||||
async function onSubmit() {
|
||||
// 对接商品评价
|
||||
// console.log(state.orderInfo);
|
||||
// return;
|
||||
let obj = {
|
||||
anonymous: false,
|
||||
benefitScores: state.commentList[0].level2,
|
||||
content: state.commentList[0].content,
|
||||
descriptionScores: state.commentList[0].level,
|
||||
orderItemId: state.commentList[0].item_id,
|
||||
picUrls: 'https://t7.baidu.com/it/u=2531125946,3055766435&fm=193&f=GIF'
|
||||
}
|
||||
const {
|
||||
code
|
||||
} = await sheep.$api.order.comment(obj);
|
||||
if (code === 0) {
|
||||
sheep.$router.back();
|
||||
}
|
||||
}
|
||||
|
||||
onLoad(async (options) => {
|
||||
let id = '';
|
||||
if (options.orderSN) {
|
||||
id = options.orderSN;
|
||||
}
|
||||
if (options.id) {
|
||||
id = options.id;
|
||||
}
|
||||
if (options.orderId) {
|
||||
state.orderId = options.orderId
|
||||
}
|
||||
|
||||
const res = await sheep.$api.order.detail(id);
|
||||
if (res.code === 0) {
|
||||
let obj = {
|
||||
10: ['待发货', '等待买家付款', ["apply_refund"]],
|
||||
30: ['待评价', '等待买家评价', ["express", "comment"]]
|
||||
}
|
||||
|
||||
res.data.status_text = obj[res.data.status][0];
|
||||
res.data.status_desc = obj[res.data.status][1];
|
||||
res.data.btns = obj[res.data.status][2];
|
||||
res.data.address = {
|
||||
province_name: res.data.receiverAreaName.split(' ')[0],
|
||||
district_name: res.data.receiverAreaName.split(' ')[2],
|
||||
city_name: res.data.receiverAreaName.split(' ')[1],
|
||||
address: res.data.receiverDetailAddress,
|
||||
consignee: res.data.receiverName,
|
||||
mobile: res.data.receiverMobile,
|
||||
}
|
||||
res.data.pay_fee = res.data.payPrice / 100
|
||||
res.data.create_time = sheep.$helper.timeFormat(res.data.createTime, 'yyyy-mm-dd hh:MM:ss')
|
||||
res.data.order_sn = res.data.no
|
||||
res.data.id = res.data.id
|
||||
res.data.goods_amount = res.data.totalPrice / 100
|
||||
res.data.dispatch_amount = res.data.deliveryPrice / 100
|
||||
res.data.pay_types_text = res.data.payChannelName.split(',')
|
||||
res.data.items = res.data.items.map(ite => {
|
||||
return {
|
||||
...ite,
|
||||
btns: obj[res.data.status][2],
|
||||
goods_title: ite.spuName,
|
||||
goods_num: ite.count,
|
||||
goods_price: ite.price / 100,
|
||||
goods_image: ite.picUrl,
|
||||
goods_sku_text: ite.properties.reduce((it0, it1) => it0 + it1.valueName + ' ', '')
|
||||
}
|
||||
})
|
||||
if (res.data.btns.includes('comment')) {
|
||||
state.orderInfo = res.data;
|
||||
state.orderInfo.items.forEach((item) => {
|
||||
if (item.btns.includes('comment')) {
|
||||
state.commentList.push({
|
||||
item_id: item.id,
|
||||
level: 5,
|
||||
content: '',
|
||||
images: [],
|
||||
});
|
||||
}
|
||||
});
|
||||
console.log(state.orderInfo.items, '循环')
|
||||
return;
|
||||
}
|
||||
}
|
||||
sheep.$helper.toast('无待评价订单');
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// 评价商品
|
||||
.goods-card {
|
||||
margin: 10rpx 0;
|
||||
padding: 20rpx;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
// 评论,选择图片
|
||||
.form-item {
|
||||
background: #fff;
|
||||
|
||||
.star-box {
|
||||
height: 100rpx;
|
||||
padding: 0 25rpx;
|
||||
}
|
||||
|
||||
.star-title {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.area-box {
|
||||
width: 690rpx;
|
||||
min-height: 306rpx;
|
||||
background: rgba(249, 250, 251, 1);
|
||||
border-radius: 20rpx;
|
||||
padding: 28rpx;
|
||||
margin: auto;
|
||||
|
||||
.img-box {
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.post-btn {
|
||||
width: 690rpx;
|
||||
line-height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
color: rgba(#fff, 0.9);
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
</style>
|
||||
167
pages/goods/comment/list.vue
Normal file
167
pages/goods/comment/list.vue
Normal file
@@ -0,0 +1,167 @@
|
||||
<!-- 商品评论的分页 -->
|
||||
<template>
|
||||
<s-layout title="全部评价">
|
||||
<su-tabs
|
||||
:list="state.type"
|
||||
:scrollable="false"
|
||||
@change="onTabsChange"
|
||||
:current="state.currentTab"
|
||||
/>
|
||||
<!-- 评论列表 -->
|
||||
<view class="ss-m-t-20">
|
||||
<view class="list-item" v-for="item in state.pagination.list" :key="item">
|
||||
<comment-item :item="item" />
|
||||
</view>
|
||||
</view>
|
||||
<s-empty v-if="state.pagination.total === 0" text="暂无数据" icon="/static/data-empty.png" />
|
||||
<!-- 下拉 -->
|
||||
<uni-load-more
|
||||
v-if="state.pagination.total > 0"
|
||||
:status="state.loadStatus"
|
||||
:content-text="{
|
||||
contentdown: '上拉加载更多',
|
||||
}"
|
||||
@tap="loadMore"
|
||||
/>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import CommentApi from '@/sheep/api/product/comment';
|
||||
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
|
||||
import { reactive } from 'vue';
|
||||
import _ from 'lodash';
|
||||
import commentItem from '../components/detail/comment-item.vue';
|
||||
|
||||
const state = reactive({
|
||||
id: 0, // 商品 SPU 编号
|
||||
type: [
|
||||
{ type: 0, name: '全部' },
|
||||
{ type: 1, name: '好评' },
|
||||
{ type: 2, name: '中评' },
|
||||
{ type: 3, name: '差评' },
|
||||
],
|
||||
currentTab: 0, // 选中的 TAB
|
||||
loadStatus: '',
|
||||
pagination: {
|
||||
list: [],
|
||||
total: 0,
|
||||
pageNo: 1,
|
||||
pageSize: 1,
|
||||
},
|
||||
});
|
||||
|
||||
// 切换选项卡
|
||||
function onTabsChange(e) {
|
||||
state.currentTab = e.index;
|
||||
// 加载列表
|
||||
state.pagination.pageNo = 1;
|
||||
state.pagination.list = [];
|
||||
state.pagination.total = 0;
|
||||
getList();
|
||||
}
|
||||
|
||||
async function getList() {
|
||||
// 加载列表
|
||||
state.loadStatus = 'loading';
|
||||
let res = await CommentApi.getCommentPage(
|
||||
state.id,
|
||||
state.pagination.pageNo,
|
||||
state.pagination.pageSize,
|
||||
state.type[state.currentTab].type,
|
||||
);
|
||||
if (res.code !== 0) {
|
||||
return;
|
||||
}
|
||||
// 合并列表
|
||||
state.pagination.list = _.concat(state.pagination.list, res.data.list);
|
||||
state.pagination.total = res.data.total;
|
||||
state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
|
||||
}
|
||||
|
||||
// 加载更多
|
||||
function loadMore() {
|
||||
if (state.loadStatus === 'noMore') {
|
||||
return;
|
||||
}
|
||||
state.pagination.pageNo++;
|
||||
getList();
|
||||
}
|
||||
|
||||
onLoad((options) => {
|
||||
state.id = options.id;
|
||||
getList();
|
||||
});
|
||||
|
||||
// 上拉加载更多
|
||||
onReachBottom(() => {
|
||||
loadMore();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.list-item {
|
||||
padding: 32rpx 30rpx 20rpx 20rpx;
|
||||
background: #fff;
|
||||
|
||||
.avatar {
|
||||
width: 52rpx;
|
||||
height: 52rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.nickname {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.create-time {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #c4c4c4;
|
||||
}
|
||||
|
||||
.content-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
color: #666666;
|
||||
line-height: 42rpx;
|
||||
}
|
||||
|
||||
.content-img {
|
||||
width: 174rpx;
|
||||
height: 174rpx;
|
||||
}
|
||||
|
||||
.cicon-info-o {
|
||||
font-size: 26rpx;
|
||||
color: #c4c4c4;
|
||||
}
|
||||
|
||||
.foot-title {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-box {
|
||||
width: 100%;
|
||||
height: 120rpx;
|
||||
background: #fff;
|
||||
border-top: 2rpx solid #eee;
|
||||
}
|
||||
|
||||
.tab-btn {
|
||||
width: 130rpx;
|
||||
height: 62rpx;
|
||||
background: #eeeeee;
|
||||
border-radius: 31rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 400;
|
||||
color: #999999;
|
||||
border: 1px solid #e5e5e5;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
</style>
|
||||
94
pages/goods/components/detail/comment-item.vue
Normal file
94
pages/goods/components/detail/comment-item.vue
Normal file
@@ -0,0 +1,94 @@
|
||||
<!-- 商品评论项 -->
|
||||
<template>
|
||||
<view>
|
||||
<!-- 用户评论 -->
|
||||
<view class="user ss-flex ss-m-b-14">
|
||||
<view class="ss-m-r-20 ss-flex">
|
||||
<image class="avatar" :src="item.userAvatar"></image>
|
||||
</view>
|
||||
<view class="nickname ss-m-r-20">{{ item.userNickname }}</view>
|
||||
<view class="">
|
||||
<uni-rate :readonly="true" v-model="item.scores" size="18" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="content"> {{ item.content }} </view>
|
||||
<view class="ss-m-t-24" v-if="item.picUrls?.length">
|
||||
<scroll-view class="scroll-box" scroll-x scroll-anchoring>
|
||||
<view class="ss-flex">
|
||||
<view v-for="(picUrl, index) in item.picUrls" :key="picUrl" class="ss-m-r-10">
|
||||
<su-image
|
||||
class="content-img"
|
||||
isPreview
|
||||
:previewList="item.picUrls"
|
||||
:current="index"
|
||||
:src="picUrl"
|
||||
:height="120"
|
||||
:width="120"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<!-- 商家回复 -->
|
||||
<view class="ss-m-t-20 reply-box" v-if="item.replyTime">
|
||||
<view class="reply-title">商家回复:</view>
|
||||
<view class="reply-content">{{ item.replyContent }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
item: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.avatar {
|
||||
width: 52rpx;
|
||||
height: 52rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.nickname {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 636rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.reply-box {
|
||||
position: relative;
|
||||
background: #f8f8f8;
|
||||
border-radius: 8rpx;
|
||||
padding: 16rpx;
|
||||
}
|
||||
|
||||
.reply-title {
|
||||
position: absolute;
|
||||
left: 16rpx;
|
||||
top: 16rpx;
|
||||
font-weight: 400;
|
||||
font-size: 26rpx;
|
||||
line-height: 40rpx;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.reply-content {
|
||||
text-indent: 128rpx;
|
||||
font-weight: 400;
|
||||
font-size: 26rpx;
|
||||
line-height: 40rpx;
|
||||
color: #333333;
|
||||
}
|
||||
</style>
|
||||
101
pages/goods/components/detail/detail-activity-tip.vue
Normal file
101
pages/goods/components/detail/detail-activity-tip.vue
Normal file
@@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<su-fixed bottom placeholder :val="44">
|
||||
<view>
|
||||
<view v-for="activity in props.activityList" :key="activity.id">
|
||||
<!-- TODO 芋艿:拼团 -->
|
||||
<view
|
||||
class="activity-box ss-p-x-38 ss-flex ss-row-between ss-col-center"
|
||||
:class="activity.type === 1 ? 'seckill-box' : 'groupon-box'"
|
||||
>
|
||||
<view class="activity-title ss-flex">
|
||||
<view class="ss-m-r-16">
|
||||
<image
|
||||
v-if="activity.type === 1"
|
||||
:src="sheep.$url.static('/static/img/shop/goods/seckill-icon.png')"
|
||||
class="activity-icon"
|
||||
/>
|
||||
<!-- TODO 芋艿:拼团 -->
|
||||
<image
|
||||
v-else-if="activity.type === 3"
|
||||
:src="sheep.$url.static('/static/img/shop/goods/groupon-icon.png')"
|
||||
class="activity-icon"
|
||||
/>
|
||||
</view>
|
||||
<view>该商品正在参与{{ activity.name }}活动</view>
|
||||
</view>
|
||||
<button class="ss-reset-button activity-go" @tap="onActivity(activity)"> GO </button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</su-fixed>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
|
||||
// TODO 芋艿:这里要迁移下;
|
||||
const seckillBg = sheep.$url.css('/static/img/shop/goods/seckill-tip-bg.png');
|
||||
const grouponBg = sheep.$url.css('/static/img/shop/goods/groupon-tip-bg.png');
|
||||
|
||||
const props = defineProps({
|
||||
activityList: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function onActivity(activity) {
|
||||
const type = activity.type;
|
||||
const typePath = type === 1 ? 'seckill' :
|
||||
type === 2 ? 'TODO 拼团' : 'groupon';
|
||||
sheep.$router.go(`/pages/goods/${typePath}`, {
|
||||
id: activity.spuId,
|
||||
activity_id: activity.id,
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.activity-box {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 10rpx;
|
||||
|
||||
.activity-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
line-height: 42rpx;
|
||||
|
||||
.activity-icon {
|
||||
width: 38rpx;
|
||||
height: 38rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.activity-go {
|
||||
width: 70rpx;
|
||||
height: 32rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
font-weight: 500;
|
||||
color: #ff6000;
|
||||
font-size: 24rpx;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
|
||||
//秒杀卡片
|
||||
.seckill-box {
|
||||
background: v-bind(seckillBg) no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.groupon-box {
|
||||
background: v-bind(grouponBg) no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
</style>
|
||||
115
pages/goods/components/detail/detail-cell-params.vue
Normal file
115
pages/goods/components/detail/detail-cell-params.vue
Normal file
@@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<view>
|
||||
<detail-cell
|
||||
v-if="modelValue.length > 0"
|
||||
label="参数"
|
||||
:value="state.paramsTitle"
|
||||
@click="state.show = true"
|
||||
></detail-cell>
|
||||
<su-popup :show="state.show" round="10" :showClose="true" @close="close">
|
||||
<view class="ss-modal-box bg-white">
|
||||
<view class="modal-header">产品参数</view>
|
||||
<scroll-view
|
||||
class="modal-content ss-p-t-50"
|
||||
scroll-y="true"
|
||||
:scroll-with-animation="true"
|
||||
:show-scrollbar="false"
|
||||
@touchmove.stop
|
||||
>
|
||||
<view class="sale-item ss-flex ss-col-top" v-for="item in modelValue" :key="item.title">
|
||||
<view class="item-title">{{ item.title }}</view>
|
||||
<view class="item-value">{{ item.content }}</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="modal-footer ss-flex ss-row-center ss-m-b-20">
|
||||
<button class="ss-reset-button save-btn ui-Shadow-Main" @tap="state.show = false"
|
||||
>确定</button
|
||||
>
|
||||
</view>
|
||||
</view>
|
||||
</su-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, computed } from 'vue';
|
||||
|
||||
import detailCell from './detail-cell.vue';
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
});
|
||||
const state = reactive({
|
||||
show: false,
|
||||
paramsTitle: computed(() => {
|
||||
let titleArray = [];
|
||||
props.modelValue.map((item) => {
|
||||
titleArray.push(item.title);
|
||||
});
|
||||
return titleArray.join(' · ');
|
||||
}),
|
||||
});
|
||||
|
||||
function close() {
|
||||
state.show = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ss-modal-box {
|
||||
border-radius: 30rpx 30rpx 0 0;
|
||||
max-height: 730rpx;
|
||||
|
||||
.modal-header {
|
||||
position: relative;
|
||||
padding: 30rpx 20rpx 40rpx;
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
padding: 0 30rpx;
|
||||
max-height: 500rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
.sale-item {
|
||||
margin-bottom: 24rpx;
|
||||
padding-bottom: 24rpx;
|
||||
border-bottom: 2rpx solid rgba(#dfdfdf, 0.4);
|
||||
|
||||
.item-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
line-height: 42rpx;
|
||||
width: 120rpx;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.item-value {
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
color: $dark-9;
|
||||
line-height: 42rpx;
|
||||
flex: 1;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
height: 120rpx;
|
||||
|
||||
.save-btn {
|
||||
width: 710rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
121
pages/goods/components/detail/detail-cell-service.vue
Normal file
121
pages/goods/components/detail/detail-cell-service.vue
Normal file
@@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<view>
|
||||
<detail-cell
|
||||
v-if="modelValue.length > 0"
|
||||
label="保障"
|
||||
:value="state.paramsTitle"
|
||||
@click="state.show = true"
|
||||
>
|
||||
</detail-cell>
|
||||
<su-popup :show="state.show" round="10" :showClose="true" @close="state.show = false">
|
||||
<view class="ss-modal-box">
|
||||
<view class="modal-header">服务保障</view>
|
||||
<scroll-view
|
||||
class="modal-content"
|
||||
scroll-y="true"
|
||||
:scroll-with-animation="true"
|
||||
:show-scrollbar="false"
|
||||
@touchmove.stop
|
||||
>
|
||||
<view class="sale-item ss-flex ss-col-top" v-for="item in modelValue" :key="item.id">
|
||||
<image
|
||||
class="title-icon ss-m-r-14"
|
||||
:src="sheep.$url.cdn(item.image)"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
<view class="title-box">
|
||||
<view class="item-title ss-m-b-20">{{ item.name }}</view>
|
||||
<view class="item-value">{{ item.description }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="modal-footer ss-flex ss-row-center ss-m-b-20">
|
||||
<button class="ss-reset-button save-btn ui-Shadow-Main" @tap="state.show = false"
|
||||
>确定</button
|
||||
>
|
||||
</view>
|
||||
</view>
|
||||
</su-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, computed } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import detailCell from './detail-cell.vue';
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
});
|
||||
|
||||
const state = reactive({
|
||||
show: false,
|
||||
paramsTitle: computed(() => {
|
||||
let nameArray = [];
|
||||
props.modelValue.map((item) => {
|
||||
nameArray.push(item.name);
|
||||
});
|
||||
return nameArray.join(' · ');
|
||||
}),
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ss-modal-box {
|
||||
border-radius: 30rpx 30rpx 0 0;
|
||||
max-height: 730rpx;
|
||||
|
||||
.modal-header {
|
||||
position: relative;
|
||||
padding: 30rpx 20rpx 40rpx;
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
padding: 0 30rpx;
|
||||
max-height: 500rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
.sale-item {
|
||||
margin-bottom: 44rpx;
|
||||
|
||||
.title-icon {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
}
|
||||
.title-box{
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.item-value {
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
color: $dark-9;
|
||||
line-height: 42rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
height: 120rpx;
|
||||
background-color: #fff;
|
||||
|
||||
.save-btn {
|
||||
width: 710rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
31
pages/goods/components/detail/detail-cell-sku.vue
Normal file
31
pages/goods/components/detail/detail-cell-sku.vue
Normal file
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<!-- SKU 选择的提示框 -->
|
||||
<detail-cell label="选择" :value="value" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import detailCell from './detail-cell.vue';
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
sku: {
|
||||
type: Object
|
||||
}
|
||||
});
|
||||
const value = computed(() => {
|
||||
if (!props.sku.id) {
|
||||
return '请选择商品规格';
|
||||
}
|
||||
let str = '';
|
||||
props.sku.properties.forEach(property => {
|
||||
str += property.propertyName + ':' + property.valueName + ' ';
|
||||
});
|
||||
return str;
|
||||
});
|
||||
</script>
|
||||
60
pages/goods/components/detail/detail-cell.vue
Normal file
60
pages/goods/components/detail/detail-cell.vue
Normal file
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<view class="detail-cell-wrap ss-flex ss-col-center ss-row-between" @tap="onClick">
|
||||
<view class="label-text">{{ label }}</view>
|
||||
<view class="cell-content ss-line-1 ss-flex-1">{{ value }}</view>
|
||||
<button class="ss-reset-button">
|
||||
<text class="_icon-forward right-forwrad-icon"></text>
|
||||
</button>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 详情 cell
|
||||
*
|
||||
*/
|
||||
|
||||
const props = defineProps({
|
||||
label: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['click']);
|
||||
|
||||
// 点击
|
||||
const onClick = () => {
|
||||
emits('click');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.detail-cell-wrap {
|
||||
padding: 10rpx 20rpx;
|
||||
// min-height: 60rpx;
|
||||
|
||||
.label-text {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: $dark-9;
|
||||
margin-right: 38rpx;
|
||||
}
|
||||
|
||||
.cell-content {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: $dark-6;
|
||||
}
|
||||
|
||||
.right-forwrad-icon {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: $dark-9;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
106
pages/goods/components/detail/detail-comment-card.vue
Normal file
106
pages/goods/components/detail/detail-comment-card.vue
Normal file
@@ -0,0 +1,106 @@
|
||||
<!-- 商品评论的卡片 -->
|
||||
<template>
|
||||
<view class="detail-comment-card bg-white">
|
||||
<view class="card-header ss-flex ss-col-center ss-row-between ss-p-b-30">
|
||||
<view class="ss-flex ss-col-center">
|
||||
<view class="line"></view>
|
||||
<view class="title ss-m-l-20 ss-m-r-10">评价</view>
|
||||
<view class="des">({{ state.total }})</view>
|
||||
</view>
|
||||
<view
|
||||
class="ss-flex ss-col-center"
|
||||
@tap="sheep.$router.go('/pages/goods/comment/list', { id: goodsId })"
|
||||
v-if="state.commentList.length > 0"
|
||||
>
|
||||
<button class="ss-reset-button more-btn">查看全部</button>
|
||||
<text class="cicon-forward" />
|
||||
</view>
|
||||
</view>
|
||||
<!-- 评论列表 -->
|
||||
<view class="card-content">
|
||||
<view class="comment-box ss-p-y-30" v-for="item in state.commentList" :key="item.id">
|
||||
<comment-item :item="item" />
|
||||
</view>
|
||||
<s-empty
|
||||
v-if="state.commentList.length === 0"
|
||||
paddingTop="0"
|
||||
icon="/static/comment-empty.png"
|
||||
text="期待您的第一个评价"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, onBeforeMount } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import CommentApi from '@/sheep/api/product/comment';
|
||||
import commentItem from './comment-item.vue';
|
||||
|
||||
const props = defineProps({
|
||||
goodsId: {
|
||||
type: [Number, String],
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const state = reactive({
|
||||
commentList: [], // 评论列表,只展示最近的 3 条
|
||||
total: 0, // 总评论数
|
||||
});
|
||||
|
||||
async function getComment(id) {
|
||||
const { data } = await CommentApi.getCommentPage(id, 1, 3, 0);
|
||||
state.commentList = data.list;
|
||||
state.total = data.total;
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
getComment(props.goodsId);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.detail-comment-card {
|
||||
margin: 0 20rpx 20rpx 20rpx;
|
||||
padding: 20rpx 20rpx 0 20rpx;
|
||||
.card-header {
|
||||
.line {
|
||||
width: 6rpx;
|
||||
height: 30rpx;
|
||||
background: linear-gradient(180deg, var(--ui-BG-Main) 0%, var(--ui-BG-Main-gradient) 100%);
|
||||
border-radius: 3rpx;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.des {
|
||||
font-size: 24rpx;
|
||||
color: $dark-9;
|
||||
}
|
||||
|
||||
.more-btn {
|
||||
font-size: 24rpx;
|
||||
color: var(--ui-BG-Main);
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.cicon-forward {
|
||||
font-size: 24rpx;
|
||||
line-height: normal;
|
||||
color: var(--ui-BG-Main);
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
.comment-box {
|
||||
border-bottom: 2rpx solid #eeeeee;
|
||||
&:last-child {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
51
pages/goods/components/detail/detail-content-card.vue
Normal file
51
pages/goods/components/detail/detail-content-card.vue
Normal file
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<view class="detail-content-card bg-white ss-m-x-20 ss-p-t-20">
|
||||
<view class="card-header ss-flex ss-col-center ss-m-b-30 ss-m-l-20">
|
||||
<view class="line"></view>
|
||||
<view class="title ss-m-l-20 ss-m-r-20">详情</view>
|
||||
</view>
|
||||
<view class="card-content">
|
||||
<mp-html :content="content"></mp-html>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
const { safeAreaInsets } = sheep.$platform.device;
|
||||
|
||||
const props = defineProps({
|
||||
content: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.detail-content-card {
|
||||
.card-header {
|
||||
.line {
|
||||
width: 6rpx;
|
||||
height: 30rpx;
|
||||
background: linear-gradient(180deg, var(--ui-BG-Main) 0%, var(--ui-BG-Main-gradient) 100%);
|
||||
border-radius: 3rpx;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.des {
|
||||
font-size: 24rpx;
|
||||
color: $dark-9;
|
||||
}
|
||||
|
||||
.more-btn {
|
||||
font-size: 24rpx;
|
||||
color: var(--ui-BG-Main);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
255
pages/goods/components/detail/detail-navbar.vue
Normal file
255
pages/goods/components/detail/detail-navbar.vue
Normal file
@@ -0,0 +1,255 @@
|
||||
<template>
|
||||
<su-fixed alway :bgStyles="{ background: '#fff' }" :val="0" noNav opacity :placeholder="false">
|
||||
<su-status-bar />
|
||||
<view
|
||||
class="ui-bar ss-flex ss-col-center ss-row-between ss-p-x-20"
|
||||
:style="[{ height: sys_navBar - sys_statusBar + 'px' }]"
|
||||
>
|
||||
<!-- 左 -->
|
||||
<view class="icon-box ss-flex">
|
||||
<view class="icon-button icon-button-left ss-flex ss-row-center" @tap="onClickLeft">
|
||||
<text class="sicon-back" v-if="hasHistory" />
|
||||
<text class="sicon-home" v-else />
|
||||
</view>
|
||||
<view class="line"></view>
|
||||
<view class="icon-button icon-button-right ss-flex ss-row-center" @tap="onClickRight">
|
||||
<text class="sicon-more" />
|
||||
</view>
|
||||
</view>
|
||||
<!-- 中 -->
|
||||
<view class="detail-tab-card ss-flex-1" :style="[{ opacity: state.tabOpacityVal }]">
|
||||
<view class="tab-box ss-flex ss-col-center ss-row-around">
|
||||
<view
|
||||
class="tab-item ss-flex-1 ss-flex ss-row-center ss-col-center"
|
||||
v-for="item in state.tabList"
|
||||
:key="item.value"
|
||||
@tap="onTab(item)"
|
||||
>
|
||||
<view class="tab-title" :class="state.curTab === item.value ? 'cur-tab-title' : ''">
|
||||
{{ item.label }}
|
||||
</view>
|
||||
<view v-show="state.curTab === item.value" class="tab-line"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- #ifdef MP -->
|
||||
<view :style="[capsuleStyle]"></view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</su-fixed>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive } from 'vue';
|
||||
import { onPageScroll } from '@dcloudio/uni-app';
|
||||
import sheep from '@/sheep';
|
||||
import throttle from '@/sheep/helper/throttle.js';
|
||||
import { showMenuTools, closeMenuTools } from '@/sheep/hooks/useModal';
|
||||
|
||||
const sys_statusBar = sheep.$platform.device.statusBarHeight;
|
||||
const sys_navBar = sheep.$platform.navbar;
|
||||
const capsuleStyle = {
|
||||
width: sheep.$platform.capsule.width + 'px',
|
||||
height: sheep.$platform.capsule.height + 'px',
|
||||
};
|
||||
|
||||
const state = reactive({
|
||||
tabOpacityVal: 0,
|
||||
curTab: 'goods',
|
||||
tabList: [
|
||||
{
|
||||
label: '商品',
|
||||
value: 'goods',
|
||||
to: 'detail-swiper-selector',
|
||||
},
|
||||
{
|
||||
label: '评价',
|
||||
value: 'comment',
|
||||
to: 'detail-comment-selector',
|
||||
},
|
||||
{
|
||||
label: '详情',
|
||||
value: 'detail',
|
||||
to: 'detail-content-selector',
|
||||
},
|
||||
],
|
||||
});
|
||||
const emits = defineEmits(['clickLeft']);
|
||||
const hasHistory = sheep.$router.hasHistory();
|
||||
|
||||
function onClickLeft() {
|
||||
if (hasHistory) {
|
||||
sheep.$router.back();
|
||||
} else {
|
||||
sheep.$router.go('/pages/index/index');
|
||||
}
|
||||
emits('clickLeft');
|
||||
}
|
||||
|
||||
function onClickRight() {
|
||||
showMenuTools();
|
||||
}
|
||||
|
||||
let commentCard = {
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
};
|
||||
|
||||
function getCommentCardNode() {
|
||||
return new Promise((res, rej) => {
|
||||
uni.createSelectorQuery()
|
||||
.select('.detail-comment-selector')
|
||||
.boundingClientRect((data) => {
|
||||
if (data) {
|
||||
commentCard.top = data.top;
|
||||
commentCard.bottom = data.top + data.height;
|
||||
res(data);
|
||||
} else {
|
||||
res(null);
|
||||
}
|
||||
})
|
||||
.exec();
|
||||
});
|
||||
}
|
||||
|
||||
function onTab(tab) {
|
||||
let scrollTop = 0;
|
||||
if (tab.value === 'comment') {
|
||||
scrollTop = commentCard.top - sys_navBar + 1;
|
||||
} else if (tab.value === 'detail') {
|
||||
scrollTop = commentCard.bottom - sys_navBar + 1;
|
||||
}
|
||||
uni.pageScrollTo({
|
||||
scrollTop,
|
||||
duration: 200,
|
||||
});
|
||||
}
|
||||
|
||||
onPageScroll((e) => {
|
||||
state.tabOpacityVal = e.scrollTop > sheep.$platform.navbar ? 1 : e.scrollTop * 0.01;
|
||||
if (commentCard.top === 0) {
|
||||
throttle(() => {
|
||||
getCommentCardNode();
|
||||
}, 50);
|
||||
}
|
||||
|
||||
if (e.scrollTop < commentCard.top - sys_navBar) {
|
||||
state.curTab = 'goods';
|
||||
} else if (
|
||||
e.scrollTop >= commentCard.top - sys_navBar &&
|
||||
e.scrollTop <= commentCard.bottom - sys_navBar
|
||||
) {
|
||||
state.curTab = 'comment';
|
||||
} else {
|
||||
state.curTab = 'detail';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.icon-box {
|
||||
box-shadow: 0px 0px 4rpx rgba(51, 51, 51, 0.08), 0px 4rpx 6rpx 2rpx rgba(102, 102, 102, 0.12);
|
||||
border-radius: 30rpx;
|
||||
width: 134rpx;
|
||||
height: 56rpx;
|
||||
margin-left: 8rpx;
|
||||
border: 1px solid rgba(#fff, 0.4);
|
||||
.line {
|
||||
width: 2rpx;
|
||||
height: 24rpx;
|
||||
background: #e5e5e7;
|
||||
}
|
||||
.sicon-back {
|
||||
font-size: 32rpx;
|
||||
color: #000;
|
||||
}
|
||||
.sicon-home {
|
||||
font-size: 32rpx;
|
||||
color: #000;
|
||||
}
|
||||
.sicon-more {
|
||||
font-size: 32rpx;
|
||||
color: #000;
|
||||
}
|
||||
.icon-button {
|
||||
width: 67rpx;
|
||||
height: 56rpx;
|
||||
&-left:hover {
|
||||
background: rgba(0, 0, 0, 0.16);
|
||||
border-radius: 30rpx 0px 0px 30rpx;
|
||||
}
|
||||
&-right:hover {
|
||||
background: rgba(0, 0, 0, 0.16);
|
||||
border-radius: 0px 30rpx 30rpx 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.left-box {
|
||||
position: relative;
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
.circle {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
background: rgba(#fff, 0.6);
|
||||
border: 1rpx solid #ebebeb;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
z-index: -1;
|
||||
}
|
||||
}
|
||||
.right {
|
||||
position: relative;
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
.circle {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
background: rgba(#ffffff, 0.6);
|
||||
border: 1rpx solid #ebebeb;
|
||||
box-sizing: border-box;
|
||||
border-radius: 50%;
|
||||
z-index: -1;
|
||||
}
|
||||
}
|
||||
.detail-tab-card {
|
||||
width: 50%;
|
||||
.tab-item {
|
||||
height: 80rpx;
|
||||
position: relative;
|
||||
z-index: 11;
|
||||
|
||||
.tab-title {
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.cur-tab-title {
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
|
||||
.tab-line {
|
||||
width: 60rpx;
|
||||
height: 6rpx;
|
||||
border-radius: 6rpx;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
bottom: 10rpx;
|
||||
background-color: var(--ui-BG-Main);
|
||||
z-index: 12;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
39
pages/goods/components/detail/detail-progress.vue
Normal file
39
pages/goods/components/detail/detail-progress.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<view class="ss-flex ss-col-center">
|
||||
<view class="progress-title ss-m-r-10"> 已抢{{ percent }}% </view>
|
||||
<view class="progress-box ss-flex ss-col-center">
|
||||
<view class="progerss-active" :style="{ width: percent < 10 ? '10%' : percent + '%' }">
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
percent: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.progress-title {
|
||||
font-size: 20rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.progress-box {
|
||||
width: 168rpx;
|
||||
height: 18rpx;
|
||||
background: #f6f6f6;
|
||||
border-radius: 9rpx;
|
||||
}
|
||||
|
||||
.progerss-active {
|
||||
height: 24rpx;
|
||||
background: linear-gradient(86deg, #f60600, #d00500);
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
</style>
|
||||
177
pages/goods/components/detail/detail-skeleton.vue
Normal file
177
pages/goods/components/detail/detail-skeleton.vue
Normal file
@@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<view
|
||||
class="skeleton-wrap"
|
||||
:class="['theme-' + sys.mode, 'main-' + sys.theme, 'font-' + sys.fontSize]"
|
||||
>
|
||||
<view class="skeleton-banner"></view>
|
||||
<view class="container-box">
|
||||
<view class="container-box-strip title ss-m-b-58"></view>
|
||||
<view class="container-box-strip ss-m-b-20"></view>
|
||||
<view class="container-box-strip ss-m-b-20"></view>
|
||||
<view class="container-box-strip w-364"></view>
|
||||
</view>
|
||||
<view class="container-box">
|
||||
<view class="ss-flex ss-row-between ss-m-b-34">
|
||||
<view class="container-box-strip w-380"></view>
|
||||
<view class="circle"></view>
|
||||
</view>
|
||||
<view class="ss-flex ss-row-between ss-m-b-34">
|
||||
<view class="container-box-strip w-556"></view>
|
||||
<view class="circle"></view>
|
||||
</view>
|
||||
<view class="ss-flex ss-row-between">
|
||||
<view class="container-box-strip w-556"></view>
|
||||
<view class="circle"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="container-box">
|
||||
<view class="container-box-strip w-198 ss-m-b-42"></view>
|
||||
<view class="ss-flex">
|
||||
<view class="circle ss-m-r-12"></view>
|
||||
<view class="container-box-strip w-252"></view>
|
||||
</view>
|
||||
</view>
|
||||
<su-fixed bottom placeholder bg="bg-white">
|
||||
<view class="ui-tabbar-box">
|
||||
<view class="foot ss-flex ss-col-center">
|
||||
<view class="ss-m-r-54 ss-m-l-32">
|
||||
<view class="rec ss-m-b-8"></view>
|
||||
<view class="oval"></view>
|
||||
</view>
|
||||
<view class="ss-m-r-54">
|
||||
<view class="rec ss-m-b-8"></view>
|
||||
<view class="oval"></view>
|
||||
</view>
|
||||
<view class="ss-m-r-50">
|
||||
<view class="rec ss-m-b-8"></view>
|
||||
<view class="oval"></view>
|
||||
</view>
|
||||
<button class="ss-reset-button add-btn ui-Shadow-Main"></button>
|
||||
<button class="ss-reset-button buy-btn ui-Shadow-Main"></button>
|
||||
</view>
|
||||
</view>
|
||||
</su-fixed>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
|
||||
const sys = computed(() => sheep.$store('sys'));
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@keyframes loading {
|
||||
0% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.skeleton-wrap {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
|
||||
.skeleton-banner {
|
||||
width: 100%;
|
||||
height: calc(100vh - 882rpx);
|
||||
background: #f4f4f4;
|
||||
}
|
||||
|
||||
.container-box {
|
||||
padding: 24rpx 18rpx;
|
||||
margin: 14rpx 20rpx;
|
||||
background: var(--ui-BG);
|
||||
animation: loading 1.4s ease infinite;
|
||||
|
||||
.container-box-strip {
|
||||
height: 40rpx;
|
||||
background: #f3f3f1;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.title {
|
||||
width: 470rpx;
|
||||
height: 50rpx;
|
||||
border-radius: 25rpx;
|
||||
}
|
||||
|
||||
.w-364 {
|
||||
width: 364rpx;
|
||||
}
|
||||
|
||||
.w-380 {
|
||||
width: 380rpx;
|
||||
}
|
||||
|
||||
.w-556 {
|
||||
width: 556rpx;
|
||||
}
|
||||
|
||||
.w-198 {
|
||||
width: 198rpx;
|
||||
}
|
||||
|
||||
.w-252 {
|
||||
width: 252rpx;
|
||||
}
|
||||
|
||||
.circle {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
background: #f3f3f1;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
.ui-tabbar-box {
|
||||
box-shadow: 0px -6px 10px 0px rgba(51, 51, 51, 0.2);
|
||||
}
|
||||
|
||||
.foot {
|
||||
height: 100rpx;
|
||||
background: var(--ui-BG);
|
||||
.rec {
|
||||
width: 38rpx;
|
||||
height: 38rpx;
|
||||
background: #f3f3f1;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.oval {
|
||||
width: 38rpx;
|
||||
height: 22rpx;
|
||||
background: #f3f3f1;
|
||||
border-radius: 11rpx;
|
||||
}
|
||||
.add-btn {
|
||||
width: 214rpx;
|
||||
height: 72rpx;
|
||||
font-weight: 500;
|
||||
font-size: 28rpx;
|
||||
border-radius: 40rpx 0 0 40rpx;
|
||||
background-color: var(--ui-BG-Main-light);
|
||||
color: var(--ui-BG-Main);
|
||||
}
|
||||
|
||||
.buy-btn {
|
||||
width: 214rpx;
|
||||
height: 72rpx;
|
||||
font-weight: 500;
|
||||
font-size: 28rpx;
|
||||
|
||||
border-radius: 0 40rpx 40rpx 0;
|
||||
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
171
pages/goods/components/detail/detail-tabbar.vue
Normal file
171
pages/goods/components/detail/detail-tabbar.vue
Normal file
@@ -0,0 +1,171 @@
|
||||
<template>
|
||||
<su-fixed bottom placeholder bg="bg-white">
|
||||
<view class="ui-tabbar-box">
|
||||
<view class="ui-tabbar ss-flex ss-col-center ss-row-between">
|
||||
<view
|
||||
v-if="collectIcon"
|
||||
class="detail-tabbar-item ss-flex ss-flex-col ss-row-center ss-col-center"
|
||||
@tap="onFavorite"
|
||||
>
|
||||
<block v-if="modelValue.favorite">
|
||||
<image
|
||||
class="item-icon"
|
||||
:src="sheep.$url.static('/static/img/shop/goods/collect_1.gif')"
|
||||
mode="aspectFit"
|
||||
></image>
|
||||
<view class="item-title">已收藏</view>
|
||||
</block>
|
||||
<block v-else>
|
||||
<image
|
||||
class="item-icon"
|
||||
:src="sheep.$url.static('/static/img/shop/goods/collect_0.png')"
|
||||
mode="aspectFit"
|
||||
></image>
|
||||
<view class="item-title">收藏</view>
|
||||
</block>
|
||||
</view>
|
||||
<view
|
||||
v-if="serviceIcon"
|
||||
class="detail-tabbar-item ss-flex ss-flex-col ss-row-center ss-col-center"
|
||||
@tap="onChat"
|
||||
>
|
||||
<image
|
||||
class="item-icon"
|
||||
:src="sheep.$url.static('/static/img/shop/goods/message.png')"
|
||||
mode="aspectFit"
|
||||
></image>
|
||||
<view class="item-title">客服</view>
|
||||
</view>
|
||||
<view
|
||||
v-if="shareIcon"
|
||||
class="detail-tabbar-item ss-flex ss-flex-col ss-row-center ss-col-center"
|
||||
@tap="showShareModal"
|
||||
>
|
||||
<image
|
||||
class="item-icon"
|
||||
:src="sheep.$url.static('/static/img/shop/goods/share.png')"
|
||||
mode="aspectFit"
|
||||
></image>
|
||||
<view class="item-title">分享</view>
|
||||
</view>
|
||||
<slot></slot>
|
||||
</view>
|
||||
</view>
|
||||
</su-fixed>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
*
|
||||
* 底部导航
|
||||
*
|
||||
* @property {String} bg - 背景颜色Class
|
||||
* @property {String} ui - 自定义样式Class
|
||||
* @property {Boolean} noFixed - 是否定位
|
||||
* @property {Boolean} topRadius - 上圆角
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
import { computed, reactive } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import { showShareModal } from '@/sheep/hooks/useModal';
|
||||
|
||||
// 数据
|
||||
const state = reactive({});
|
||||
|
||||
// 接收参数
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
bg: {
|
||||
type: String,
|
||||
default: 'bg-white',
|
||||
},
|
||||
bgStyles: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
ui: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
|
||||
noFixed: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
topRadius: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
collectIcon: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
serviceIcon: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
shareIcon: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
const elStyles = computed(() => {
|
||||
return {
|
||||
'border-top-left-radius': props.topRadius + 'rpx',
|
||||
'border-top-right-radius': props.topRadius + 'rpx',
|
||||
overflow: 'hidden',
|
||||
};
|
||||
});
|
||||
|
||||
const tabbarheight = (e) => {
|
||||
uni.setStorageSync('tabbar', e);
|
||||
};
|
||||
async function onFavorite() {
|
||||
const { error } = await sheep.$api.user.favorite.do(props.modelValue.id);
|
||||
if (error === 0) {
|
||||
if (props.modelValue.favorite) {
|
||||
props.modelValue.favorite = 0;
|
||||
} else {
|
||||
props.modelValue.favorite = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const onChat = () => {
|
||||
sheep.$router.go('/pages/chat/index', {
|
||||
id: props.modelValue.id,
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ui-tabbar-box {
|
||||
box-shadow: 0px -6px 10px 0px rgba(51, 51, 51, 0.2);
|
||||
}
|
||||
.ui-tabbar {
|
||||
display: flex;
|
||||
height: 50px;
|
||||
background: #fff;
|
||||
|
||||
.detail-tabbar-item {
|
||||
width: 100rpx;
|
||||
|
||||
.item-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-size: 20rpx;
|
||||
font-weight: 500;
|
||||
line-height: 20rpx;
|
||||
margin-top: 12rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
137
pages/goods/components/groupon/groupon-card-list.vue
Normal file
137
pages/goods/components/groupon/groupon-card-list.vue
Normal file
@@ -0,0 +1,137 @@
|
||||
<template>
|
||||
<view v-if="state.list.length > 0" class="groupon-list detail-card ss-p-x-20">
|
||||
<view class="join-activity ss-flex ss-row-between ss-m-t-30">
|
||||
<view class="">已有{{ modelValue.sales }}人参与活动</view>
|
||||
<text class="cicon-forward"></text>
|
||||
</view>
|
||||
<view
|
||||
v-for="(item, index) in state.list"
|
||||
@tap="sheep.$router.go('/pages/activity/groupon/detail', { id: item.id })"
|
||||
:key="index"
|
||||
class="ss-m-t-40 ss-flex ss-row-between border-bottom ss-p-b-30"
|
||||
>
|
||||
<view class="ss-flex ss-col-center">
|
||||
<image :src="sheep.$url.cdn(item.leader.avatar)" class="user-avatar"></image>
|
||||
<view class="user-nickname ss-m-l-20 ss-line-1">{{ item.leader.nickname }}</view>
|
||||
</view>
|
||||
<view class="ss-flex ss-col-center">
|
||||
<view class="ss-flex-col ss-col-bottom ss-m-r-20">
|
||||
<view class="title ss-flex ss-m-b-14">
|
||||
还差
|
||||
<view class="num">{{ item.num - item.current_num }}人</view>
|
||||
成团
|
||||
</view>
|
||||
<view class="end-time">{{ endTime(item.expire_time) }}</view>
|
||||
</view>
|
||||
<view class="">
|
||||
<button class="ss-reset-button go-btn" @tap.stop="onJoinGroupon(item)"> 去参团 </button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import { useDurationTime } from '@/sheep/hooks/useGoods';
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
});
|
||||
const state = reactive({
|
||||
list: [],
|
||||
});
|
||||
const emits = defineEmits(['join']);
|
||||
|
||||
function onJoinGroupon(groupon) {
|
||||
emits('join', groupon);
|
||||
}
|
||||
|
||||
// 倒计时
|
||||
function endTime(time) {
|
||||
const durationTime = useDurationTime(time);
|
||||
|
||||
if (durationTime.ms <= 0) {
|
||||
return '该团已解散';
|
||||
}
|
||||
|
||||
let timeText = '剩余 ';
|
||||
timeText += `${durationTime.h}时`;
|
||||
timeText += `${durationTime.m}分`;
|
||||
timeText += `${durationTime.s}秒`;
|
||||
return timeText;
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
const { data } = await sheep.$api.activity.getGrouponList({
|
||||
goods_id: props.modelValue.id,
|
||||
activity_id: props.modelValue.activity.id,
|
||||
});
|
||||
state.list = data.data;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.detail-card {
|
||||
background-color: $white;
|
||||
margin: 14rpx 20rpx;
|
||||
border-radius: 10rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
.groupon-list {
|
||||
.join-activity {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #999999;
|
||||
|
||||
.cicon-forward {
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
background: #ececec;
|
||||
border-radius: 60rpx;
|
||||
}
|
||||
|
||||
.user-nickname {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
width: 160rpx;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #666666;
|
||||
|
||||
.num {
|
||||
color: #ff6000;
|
||||
}
|
||||
}
|
||||
|
||||
.end-time {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.go-btn {
|
||||
width: 140rpx;
|
||||
height: 60rpx;
|
||||
background: linear-gradient(90deg, #ff6000 0%, #fe832a 100%);
|
||||
border-radius: 30rpx;
|
||||
color: #fff;
|
||||
font-weight: 500;
|
||||
font-size: 26rpx;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
103
pages/goods/components/list/list-goods-card.vue
Normal file
103
pages/goods/components/list/list-goods-card.vue
Normal file
@@ -0,0 +1,103 @@
|
||||
<!-- 页面 -->
|
||||
<template>
|
||||
<view class="list-goods-card ss-flex-col" @tap="onClick">
|
||||
<view class="md-img-box">
|
||||
<image class="goods-img md-img-box" :src="sheep.$url.cdn(img)" mode="aspectFill"></image>
|
||||
</view>
|
||||
<view class="md-goods-content ss-flex-col ss-row-around">
|
||||
<view class="md-goods-title ss-line-2 ss-m-x-20 ss-m-t-6 ss-m-b-16">{{ title }}</view>
|
||||
<view class="md-goods-subtitle ss-line-1 ss-p-y-10 ss-p-20">{{ subTitle }}</view>
|
||||
<view class="ss-flex ss-col-center ss-row-between ss-m-b-16 ss-m-x-20">
|
||||
<view class="md-goods-price text-price">{{ price }}</view>
|
||||
<view class="goods-origin-price text-price">{{ originPrice }}</view>
|
||||
<view class="sales-text">已售{{ sales }}件</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { computed, reactive } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
img: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
subTitle: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
price: {
|
||||
type: [String, Number],
|
||||
default: '',
|
||||
},
|
||||
originPrice: {
|
||||
type: [String, Number],
|
||||
default: '',
|
||||
},
|
||||
sales: {
|
||||
type: [String, Number],
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
const emits = defineEmits(['click']);
|
||||
const onClick = () => {
|
||||
emits('click');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goods-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.sales-text {
|
||||
font-size: 20rpx;
|
||||
color: #c4c4c4;
|
||||
}
|
||||
|
||||
.goods-origin-price {
|
||||
font-size: 20rpx;
|
||||
color: #c4c4c4;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.list-goods-card {
|
||||
overflow: hidden;
|
||||
width: 344rpx;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
background-color: $white;
|
||||
box-shadow: 0 0 20rpx 4rpx rgba(199, 199, 199, 0.22);
|
||||
border-radius: 20rpx;
|
||||
|
||||
.md-img-box {
|
||||
width: 344rpx;
|
||||
height: 344rpx;
|
||||
}
|
||||
|
||||
.md-goods-title {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
}
|
||||
.md-goods-subtitle {
|
||||
background-color: var(--ui-BG-Main-tag);
|
||||
color: var(--ui-BG-Main);
|
||||
font-size: 20rpx;
|
||||
}
|
||||
|
||||
.md-goods-price {
|
||||
font-size: 30rpx;
|
||||
color: $red;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
92
pages/goods/components/list/list-navbar.vue
Normal file
92
pages/goods/components/list/list-navbar.vue
Normal file
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<su-fixed
|
||||
alway
|
||||
:bgStyles="{ background: '#fff' }"
|
||||
:val="0"
|
||||
noNav
|
||||
:opacity="false"
|
||||
placeholder
|
||||
index="10090"
|
||||
>
|
||||
<su-status-bar />
|
||||
<view
|
||||
class="ui-bar ss-flex ss-col-center ss-row-between ss-p-x-20"
|
||||
:style="[{ height: sys_navBar - sys_statusBar + 'px' }]"
|
||||
>
|
||||
<!-- 左 -->
|
||||
<view class="left-box">
|
||||
<text
|
||||
class="_icon-back back-icon"
|
||||
@tap="toBack"
|
||||
:style="[{ color: state.iconColor }]"
|
||||
></text>
|
||||
</view>
|
||||
<!-- 中 -->
|
||||
<uni-search-bar
|
||||
class="ss-flex-1"
|
||||
radius="33"
|
||||
:placeholder="placeholder"
|
||||
cancelButton="none"
|
||||
:focus="true"
|
||||
v-model="state.searchVal"
|
||||
@confirm="onSearch"
|
||||
/>
|
||||
<!-- 右 -->
|
||||
<view class="right">
|
||||
<text class="sicon-more" :style="[{ color: state.iconColor }]" @tap="showMenuTools" />
|
||||
</view>
|
||||
<!-- #ifdef MP -->
|
||||
<view :style="[capsuleStyle]"></view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</su-fixed>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import { showMenuTools } from '@/sheep/hooks/useModal';
|
||||
|
||||
const sys_statusBar = sheep.$platform.device.statusBarHeight;
|
||||
const sys_navBar = sheep.$platform.navbar;
|
||||
const capsuleStyle = {
|
||||
width: sheep.$platform.capsule.width + 'px',
|
||||
height: sheep.$platform.capsule.height + 'px',
|
||||
margin: '0 ' + (sheep.$platform.device.windowWidth - sheep.$platform.capsule.right) + 'px',
|
||||
};
|
||||
|
||||
const state = reactive({
|
||||
iconColor: '#000',
|
||||
searchVal: '',
|
||||
});
|
||||
|
||||
const props = defineProps({
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '搜索内容',
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['searchConfirm']);
|
||||
|
||||
// 返回
|
||||
const toBack = () => {
|
||||
sheep.$router.back();
|
||||
};
|
||||
|
||||
// 搜索
|
||||
const onSearch = () => {
|
||||
emits('searchConfirm', state.searchVal);
|
||||
};
|
||||
|
||||
const onTab = (item) => {};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.back-icon {
|
||||
font-size: 40rpx;
|
||||
}
|
||||
.sicon-more {
|
||||
font-size: 48rpx;
|
||||
}
|
||||
</style>
|
||||
597
pages/goods/groupon.vue
Normal file
597
pages/goods/groupon.vue
Normal file
@@ -0,0 +1,597 @@
|
||||
<template>
|
||||
<s-layout :onShareAppMessage="shareInfo" navbar="goods">
|
||||
<!-- 标题栏 -->
|
||||
<detailNavbar />
|
||||
<!-- 骨架屏 -->
|
||||
<detailSkeleton v-if="state.skeletonLoading" />
|
||||
<!-- 空置页 -->
|
||||
<s-empty
|
||||
v-else-if="
|
||||
state.goodsInfo === null ||
|
||||
!['groupon', 'groupon_ladder'].includes(state.goodsInfo.activity_type)
|
||||
"
|
||||
text="活动不存在或已结束"
|
||||
icon="/static/soldout-empty.png"
|
||||
showAction
|
||||
actionText="返回上一页"
|
||||
@clickAction="sheep.$router.back()"
|
||||
/>
|
||||
<block v-else>
|
||||
<view class="detail-swiper-selector">
|
||||
<!-- 商品图轮播 -->
|
||||
<su-swiper
|
||||
class="ss-m-b-14"
|
||||
isPreview
|
||||
:list="state.goodsSwiper"
|
||||
dotStyle="tag"
|
||||
imageMode="widthFix"
|
||||
dotCur="bg-mask-40"
|
||||
:seizeHeight="750"
|
||||
/>
|
||||
|
||||
<!-- 价格+标题 -->
|
||||
<view class="title-card detail-card ss-m-y-14 ss-m-x-20 ss-p-x-20 ss-p-y-34">
|
||||
<view class="ss-flex ss-row-between ss-m-b-60">
|
||||
<view>
|
||||
<view class="price-box ss-flex ss-col-bottom ss-m-b-18">
|
||||
<view class="price-text ss-m-r-16">
|
||||
{{ goodsPrice }}
|
||||
</view>
|
||||
<view class="tig ss-flex ss-col-center">
|
||||
<view class="tig-icon ss-flex ss-col-center ss-row-center">
|
||||
<view class="groupon-tag">
|
||||
<image
|
||||
:src="sheep.$url.static('/static/img/shop/goods/groupon-tag.png')"
|
||||
></image>
|
||||
</view>
|
||||
</view>
|
||||
<view class="tig-title">拼团价</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="ss-flex ss-row-between">
|
||||
<view
|
||||
class="origin-price ss-flex ss-col-center"
|
||||
v-if="state.goodsInfo.original_price"
|
||||
>
|
||||
单买价:
|
||||
<view class="origin-price-text">
|
||||
{{ state.goodsInfo.original_goods_price[0] || state.goodsInfo.original_price }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="countdown-box" v-if="endTime.ms > 0">
|
||||
<view class="countdown-title ss-m-b-20">距结束仅剩</view>
|
||||
<view class="ss-flex countdown-time">
|
||||
<view class="ss-flex countdown-h">{{ endTime.h }}</view>
|
||||
<view class="ss-m-x-4">:</view>
|
||||
<view class="countdown-num ss-flex ss-row-center">{{ endTime.m }}</view>
|
||||
<view class="ss-m-x-4">:</view>
|
||||
<view class="countdown-num ss-flex ss-row-center">{{ endTime.s }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="countdown-title" v-else> 活动已结束 </view>
|
||||
</view>
|
||||
|
||||
<view class="title-text ss-line-2 ss-m-b-6">{{ state.goodsInfo.title }}</view>
|
||||
<view class="subtitle-text ss-line-1">{{ state.goodsInfo.subtitle }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 功能卡片 -->
|
||||
<view class="detail-cell-card detail-card ss-flex-col">
|
||||
<!-- 规格 -->
|
||||
<detail-cell-sku
|
||||
v-model="state.selectedSkuPrice.goods_sku_text"
|
||||
:skus="state.goodsInfo.skus"
|
||||
@tap="state.showSelectSku = true"
|
||||
/>
|
||||
<!-- 服务 -->
|
||||
<detail-cell-service v-model="state.goodsInfo.service" />
|
||||
<!-- 参数 -->
|
||||
<detail-cell-params v-model="state.goodsInfo.params" />
|
||||
<!-- 玩法 -->
|
||||
<detail-cell
|
||||
v-if="state.goodsInfo.activity.richtext_id > 0"
|
||||
label="玩法"
|
||||
:value="state.goodsInfo.activity.richtext_title"
|
||||
@click="
|
||||
sheep.$router.go('/pages/public/richtext', {
|
||||
id: state.goodsInfo.activity.richtext_id,
|
||||
title: state.goodsInfo.activity.richtext_title,
|
||||
})
|
||||
"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 参团列表 -->
|
||||
<groupon-card-list
|
||||
v-if="state.goodsInfo.activity.rules.is_team_card === '1'"
|
||||
v-model="state.goodsInfo"
|
||||
@join="onJoinGroupon"
|
||||
/>
|
||||
|
||||
<!-- 规格与数量弹框 -->
|
||||
<s-select-groupon-sku
|
||||
:show="state.showSelectSku"
|
||||
:goodsInfo="state.goodsInfo"
|
||||
:grouponAction="state.grouponAction"
|
||||
:grouponNum="state.grouponNum"
|
||||
@buy="onBuy"
|
||||
@ladder="onLadder"
|
||||
@change="onSkuChange"
|
||||
@close="onSkuClose"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 评价 -->
|
||||
<detail-comment-card class="detail-comment-selector" :goodsId="state.goodsId" />
|
||||
<!-- 详情 -->
|
||||
<detail-content-card class="detail-content-selector" :content="state.goodsInfo.content" />
|
||||
|
||||
<!-- 商品tabbar -->
|
||||
<!-- TODO: 已售罄、预热 判断 设计-->
|
||||
<detail-tabbar v-model="state.goodsInfo">
|
||||
<view class="buy-box ss-flex ss-col-center ss-p-r-20">
|
||||
<button
|
||||
v-if="state.goodsInfo.activity.rules.is_alone == 1"
|
||||
class="ss-reset-button origin-price-btn ss-flex-col"
|
||||
@tap="sheep.$router.go('/pages/goods/index', { id: state.goodsInfo.id })"
|
||||
>
|
||||
<view class="btn-price">{{
|
||||
state.goodsInfo.original_goods_price[0] || state.goodsInfo.original_price
|
||||
}}</view>
|
||||
<view>原价购买</view>
|
||||
</button>
|
||||
<button v-else class="ss-reset-button origin-price-btn ss-flex-col">
|
||||
<view class="btn-title">{{
|
||||
state.grouponNum == 0 ? '阶梯团' : state.grouponNum + '人团'
|
||||
}}</view>
|
||||
</button>
|
||||
<button
|
||||
class="ss-reset-button btn-tox ss-flex-col"
|
||||
@tap="onCreateGroupon"
|
||||
:class="
|
||||
state.goodsInfo.activity.status === 'ing' && state.goodsInfo.stock != 0
|
||||
? 'check-btn-box'
|
||||
: 'disabled-btn-box'
|
||||
"
|
||||
:disabled="state.goodsInfo.stock === 0 || state.goodsInfo.activity.status != 'ing'"
|
||||
>
|
||||
<view class="btn-price">{{ goodsPrice }}</view>
|
||||
<view v-if="state.goodsInfo.activity.status === 'ing'">
|
||||
<view v-if="state.goodsInfo.stock === 0">已售罄</view>
|
||||
<view v-else>立即开团</view>
|
||||
</view>
|
||||
<view v-else>{{ state.goodsInfo.activity.status_text }}</view>
|
||||
</button>
|
||||
</view>
|
||||
</detail-tabbar>
|
||||
</block>
|
||||
<!-- 轮播 -->
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, getCurrentInstance, computed, ref } from 'vue';
|
||||
import { onLoad, onPageScroll } from '@dcloudio/uni-app';
|
||||
import sheep from '@/sheep';
|
||||
import { isEmpty } from 'lodash';
|
||||
import detailNavbar from './components/detail/detail-navbar.vue';
|
||||
import detailCell from './components/detail/detail-cell.vue';
|
||||
import detailCellSku from './components/detail/detail-cell-sku.vue';
|
||||
import detailCellService from './components/detail/detail-cell-service.vue';
|
||||
import detailCellParams from './components/detail/detail-cell-params.vue';
|
||||
import detailTabbar from './components/detail/detail-tabbar.vue';
|
||||
import detailSkeleton from './components/detail/detail-skeleton.vue';
|
||||
import detailCommentCard from './components/detail/detail-comment-card.vue';
|
||||
import detailContentCard from './components/detail/detail-content-card.vue';
|
||||
import grouponCardList from './components/groupon/groupon-card-list.vue';
|
||||
import { useDurationTime, formatPrice, formatGoodsSwiper } from '@/sheep/hooks/useGoods';
|
||||
|
||||
|
||||
const headerBg = sheep.$url.css('/static/img/shop/goods/groupon-bg.png');
|
||||
const btnBg = sheep.$url.css('/static/img/shop/goods/groupon-btn.png');
|
||||
const disabledBtnBg = sheep.$url.css(
|
||||
'/static/img/shop/goods/activity-btn-disabled.png',
|
||||
);
|
||||
const seckillBg = sheep.$url.css('/static/img/shop/goods/seckill-tip-bg.png');
|
||||
const grouponBg = sheep.$url.css('/static/img/shop/goods/groupon-tip-bg.png');
|
||||
|
||||
onPageScroll(() => {});
|
||||
const state = reactive({
|
||||
skeletonLoading: true, // 骨架屏
|
||||
goodsId: 0, // 商品ID
|
||||
goodsInfo: {}, // 商品信息
|
||||
goodsSwiper: [], // 商品轮播图
|
||||
showSelectSku: false, // 显示规格弹框
|
||||
selectedSkuPrice: {}, // 选中的规格价格
|
||||
grouponId: 0, // 团购ID
|
||||
grouponType: '', // 团购类型
|
||||
grouponNum: 0, // 团购人数
|
||||
grouponAction: 'create', // 团购操作
|
||||
});
|
||||
|
||||
// 商品主价格
|
||||
const goodsPrice = computed(() => {
|
||||
if (isEmpty(state.selectedSkuPrice)) {
|
||||
return formatPrice(state.goodsInfo.price);
|
||||
}
|
||||
if(state.grouponNum === 0 && state.grouponType === 'groupon_ladder') {
|
||||
return formatPrice(state.goodsInfo.price)
|
||||
}
|
||||
if (state.grouponType === 'groupon') {
|
||||
return state.selectedSkuPrice.groupon_price;
|
||||
}
|
||||
if (state.grouponType === 'groupon_ladder') {
|
||||
return state.selectedSkuPrice.ladder_price;
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
// 倒计时
|
||||
const endTime = computed(() => {
|
||||
return useDurationTime(state.goodsInfo.activity.end_time);
|
||||
});
|
||||
|
||||
// 规格变更
|
||||
function onSkuChange(e) {
|
||||
state.selectedSkuPrice = e;
|
||||
}
|
||||
|
||||
// 阶梯变更
|
||||
function onLadder(e) {
|
||||
state.showSelectSku = false;
|
||||
state.grouponNum = e
|
||||
setTimeout(() => {
|
||||
state.showSelectSku = true;
|
||||
}, 80);
|
||||
}
|
||||
|
||||
function onSkuClose() {
|
||||
state.showSelectSku = false;
|
||||
}
|
||||
|
||||
// 发起拼团
|
||||
function onCreateGroupon() {
|
||||
state.grouponAction = 'create';
|
||||
state.grouponId = 0;
|
||||
state.showSelectSku = true;
|
||||
}
|
||||
|
||||
// 点击参团
|
||||
function onJoinGroupon(groupon) {
|
||||
state.grouponAction = 'join';
|
||||
state.grouponId = groupon.id;
|
||||
state.grouponNum = groupon.num;
|
||||
state.showSelectSku = true;
|
||||
}
|
||||
|
||||
// 立即购买
|
||||
function onBuy(e) {
|
||||
sheep.$router.go('/pages/order/confirm', {
|
||||
data: JSON.stringify({
|
||||
order_type: 'goods',
|
||||
buy_type: 'groupon',
|
||||
activity_id: state.goodsInfo.activity.id,
|
||||
groupon_id: state.grouponId,
|
||||
groupon_num: state.grouponNum,
|
||||
goods_list: [
|
||||
{
|
||||
goods_id: e.goods_id,
|
||||
goods_num: e.goods_num,
|
||||
goods_sku_price_id: e.id,
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
const shareInfo = computed(() => {
|
||||
if (isEmpty(state.goodsInfo?.activity)) return {};
|
||||
return sheep.$platform.share.getShareInfo(
|
||||
{
|
||||
title: state.goodsInfo.title,
|
||||
image: sheep.$url.cdn(state.goodsInfo.image),
|
||||
params: {
|
||||
page: '3',
|
||||
query: state.goodsInfo.id + ',' + state.goodsInfo.activity.id,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'goods', // 商品海报
|
||||
title: state.goodsInfo.title, // 商品标题
|
||||
image: sheep.$url.cdn(state.goodsInfo.image), // 商品主图
|
||||
price: state.goodsInfo.price[0], // 商品价格
|
||||
original_price: state.goodsInfo.original_price, // 商品原价
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
onLoad(async (options) => {
|
||||
// 非法参数
|
||||
if (!options.id) {
|
||||
state.goodsInfo = null;
|
||||
return;
|
||||
}
|
||||
state.goodsId = options.id;
|
||||
// 加载商品信息
|
||||
const { error, data } = await sheep.$api.goods.detail(options.id, {
|
||||
activity_id: options.activity_id,
|
||||
});
|
||||
// 关闭骨架屏
|
||||
state.skeletonLoading = false;
|
||||
if (error === 0) {
|
||||
state.goodsInfo = data;
|
||||
state.grouponType = state.goodsInfo.activity_type;
|
||||
if (state.grouponType === 'groupon') {
|
||||
state.grouponNum = state.goodsInfo.activity.rules.team_num;
|
||||
}
|
||||
state.goodsSwiper = formatGoodsSwiper(state.goodsInfo.images);
|
||||
} else {
|
||||
// 未找到商品
|
||||
state.goodsInfo = null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.detail-card {
|
||||
background-color: $white;
|
||||
margin: 14rpx 20rpx;
|
||||
border-radius: 10rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// 价格标题卡片
|
||||
.title-card {
|
||||
width: 710rpx;
|
||||
box-sizing: border-box;
|
||||
// height: 320rpx;
|
||||
background-size: 100% 100%;
|
||||
border-radius: 10rpx;
|
||||
background-image: v-bind(headerBg);
|
||||
background-repeat: no-repeat;
|
||||
|
||||
.price-box {
|
||||
.price-text {
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
line-height: normal;
|
||||
font-family: OPPOSANS;
|
||||
|
||||
&::before {
|
||||
content: '¥';
|
||||
font-size: 30rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
.origin-price {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #fff;
|
||||
opacity: 0.7;
|
||||
|
||||
.origin-price-text {
|
||||
text-decoration: line-through;
|
||||
|
||||
font-family: OPPOSANS;
|
||||
|
||||
&::before {
|
||||
content: '¥';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tig {
|
||||
border: 2rpx solid #ffffff;
|
||||
border-radius: 4rpx;
|
||||
width: 126rpx;
|
||||
height: 38rpx;
|
||||
|
||||
.tig-icon {
|
||||
margin-left: -2rpx;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 4rpx 0 0 4rpx;
|
||||
|
||||
.groupon-tag {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.tig-title {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
line-height: normal;
|
||||
color: #ffffff;
|
||||
width: 86rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.countdown-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.countdown-time {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
.countdown-h {
|
||||
font-size: 24rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
padding: 0 4rpx;
|
||||
height: 40rpx;
|
||||
background: rgba(#000000, 0.1);
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
.countdown-num {
|
||||
font-size: 24rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
background: rgba(#000000, 0.1);
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
line-height: 42rpx;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.subtitle-text {
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
color: #ffffff;
|
||||
line-height: 42rpx;
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
// 购买
|
||||
.buy-box {
|
||||
.disabled-btn-box[disabled] {
|
||||
background-color: transparent;
|
||||
}
|
||||
.check-btn-box {
|
||||
width: 248rpx;
|
||||
height: 80rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
margin-left: -36rpx;
|
||||
background-image: v-bind(btnBg);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
color: #ffffff;
|
||||
line-height: normal;
|
||||
border-radius: 0px 40rpx 40rpx 0px;
|
||||
}
|
||||
.disabled-btn-box {
|
||||
width: 248rpx;
|
||||
height: 80rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
margin-left: -36rpx;
|
||||
background-image: v-bind(disabledBtnBg);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
color: #999999;
|
||||
line-height: normal;
|
||||
border-radius: 0px 40rpx 40rpx 0px;
|
||||
}
|
||||
|
||||
.origin-price-btn {
|
||||
width: 236rpx;
|
||||
height: 80rpx;
|
||||
background: rgba(#ff5651, 0.1);
|
||||
color: #ff6000;
|
||||
border-radius: 40rpx 0px 0px 40rpx;
|
||||
line-height: normal;
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
|
||||
.btn-title {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
.btn-price {
|
||||
font-family: OPPOSANS;
|
||||
|
||||
&::before {
|
||||
content: '¥';
|
||||
}
|
||||
}
|
||||
.more-item-box {
|
||||
.more-item {
|
||||
width: 156rpx;
|
||||
height: 58rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #999999;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
.more-item-hover {
|
||||
background: rgba(#ffefe5, 0.32);
|
||||
color: #ff6000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//秒杀卡片
|
||||
.seckill-box {
|
||||
background: v-bind(seckillBg)
|
||||
no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.groupon-box {
|
||||
background: v-bind(grouponBg)
|
||||
no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
//活动卡片
|
||||
.activity-box {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 10rpx;
|
||||
|
||||
.activity-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
line-height: 42rpx;
|
||||
|
||||
.activity-icon {
|
||||
width: 38rpx;
|
||||
height: 38rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.activity-go {
|
||||
width: 70rpx;
|
||||
height: 32rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
font-weight: 500;
|
||||
color: #ff6000;
|
||||
font-size: 24rpx;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.model-box {
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
384
pages/goods/index.vue
Normal file
384
pages/goods/index.vue
Normal file
@@ -0,0 +1,384 @@
|
||||
<template>
|
||||
<view>
|
||||
<s-layout :onShareAppMessage="shareInfo" navbar="goods">
|
||||
<!-- 标题栏 -->
|
||||
<detailNavbar />
|
||||
|
||||
<!-- 骨架屏 -->
|
||||
<detailSkeleton v-if="state.skeletonLoading" />
|
||||
<!-- 下架/售罄提醒 -->
|
||||
<s-empty v-else-if="state.goodsInfo === null" text="商品不存在或已下架" icon="/static/soldout-empty.png" showAction
|
||||
actionText="再逛逛" actionUrl="/pages/goods/list" />
|
||||
<block v-else>
|
||||
<view class="detail-swiper-selector">
|
||||
<!-- 商品轮播图 -->
|
||||
<su-swiper class="ss-m-b-14" isPreview :list="formatGoodsSwiper(state.goodsInfo.sliderPicUrls)"
|
||||
dotStyle="tag" imageMode="widthFix" dotCur="bg-mask-40" :seizeHeight="750" />
|
||||
|
||||
<!-- 价格+标题 -->
|
||||
<view class="title-card detail-card ss-p-y-40 ss-p-x-20">
|
||||
<view class="ss-flex ss-row-between ss-col-center ss-m-b-26">
|
||||
<view class="price-box ss-flex ss-col-bottom">
|
||||
<view class="price-text ss-m-r-16">
|
||||
{{ fen2yuan(state.selectedSku.price || state.goodsInfo.price) }}
|
||||
</view>
|
||||
<view class="origin-price-text" v-if="state.goodsInfo.marketPrice > 0">
|
||||
{{ fen2yuan(state.selectedSku.marketPrice || state.goodsInfo.marketPrice) }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="sales-text">
|
||||
{{ formatSales('exact', state.goodsInfo.salesCount) }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="discounts-box ss-flex ss-row-between ss-m-b-28">
|
||||
<!-- 满减送/限时折扣活动的提示 TODO 芋艿:promos 未写 -->
|
||||
<div class="tag-content">
|
||||
<view class="tag-box ss-flex">
|
||||
<view class="tag ss-m-r-10" v-for="promos in state.goodsInfo.promos" :key="promos.id" @tap="onActivity">
|
||||
{{ promos.title }}
|
||||
</view>
|
||||
</view>
|
||||
</div>
|
||||
|
||||
<!-- 优惠劵 -->
|
||||
<view class="get-coupon-box ss-flex ss-col-center ss-m-l-20" @tap="state.showModel = true"
|
||||
v-if="state.couponInfo.length">
|
||||
<view class="discounts-title ss-m-r-8">领券</view>
|
||||
<text class="cicon-forward"></text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="title-text ss-line-2 ss-m-b-6">{{ state.goodsInfo.name }}</view>
|
||||
<view class="subtitle-text ss-line-1">{{ state.goodsInfo.introduction }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 功能卡片 -->
|
||||
<view class="detail-cell-card detail-card ss-flex-col">
|
||||
<detail-cell-sku v-model="state.selectedSku.goods_sku_text" :sku="state.selectedSku"
|
||||
@tap="state.showSelectSku = true" />
|
||||
<!-- TODO 芋艿:可能暂时不考虑使用 -->
|
||||
<detail-cell-service v-if="state.goodsInfo.service" v-model="state.goodsInfo.service" />
|
||||
<detail-cell-params v-if="state.goodsInfo.params" v-model="state.goodsInfo.params" />
|
||||
</view>
|
||||
|
||||
<!-- 规格与数量弹框 -->
|
||||
<s-select-sku :goodsInfo="state.goodsInfo" :show="state.showSelectSku" @addCart="onAddCart"
|
||||
@buy="onBuy" @change="onSkuChange" @close="state.showSelectSku = false" />
|
||||
</view>
|
||||
|
||||
<!-- 评价 -->
|
||||
<detail-comment-card class="detail-comment-selector" :goodsId="state.goodsId" />
|
||||
<!-- 详情 -->
|
||||
<detail-content-card class="detail-content-selector" :content="state.goodsInfo.description" />
|
||||
|
||||
<!-- 活动跳转:拼团/秒杀/砍价活动 -->
|
||||
<detail-activity-tip v-if="state.activityList.length > 0" :activity-list="state.activityList" />
|
||||
|
||||
<!-- 详情 tabbar -->
|
||||
<detail-tabbar v-model="state.goodsInfo">
|
||||
<view class="buy-box ss-flex ss-col-center ss-p-r-20" v-if="state.goodsInfo.stock > 0">
|
||||
<button class="ss-reset-button add-btn ui-Shadow-Main" @tap="state.showSelectSku = true">
|
||||
加入购物车
|
||||
</button>
|
||||
<button class="ss-reset-button buy-btn ui-Shadow-Main" @tap="state.showSelectSku = true">
|
||||
立即购买
|
||||
</button>
|
||||
</view>
|
||||
<view class="buy-box ss-flex ss-col-center ss-p-r-20" v-else>
|
||||
<button class="ss-reset-button disabled-btn" disabled> 已售罄 </button>
|
||||
</view>
|
||||
</detail-tabbar>
|
||||
|
||||
<!-- 优惠劵弹窗 -->
|
||||
<s-coupon-get v-model="state.couponInfo" :show="state.showModel" @close="state.showModel = false"
|
||||
@get="onGet" />
|
||||
|
||||
<!-- 满减送/限时折扣活动弹窗 -->
|
||||
<s-activity-pop v-model="state.activityInfo" :show="state.showActivityModel"
|
||||
@close="state.showActivityModel = false" />
|
||||
</block>
|
||||
</s-layout>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, computed } from 'vue';
|
||||
import { onLoad, onPageScroll } from '@dcloudio/uni-app';
|
||||
import sheep from '@/sheep';
|
||||
import CouponApi from '@/sheep/api/promotion/coupon';
|
||||
import ActivityApi from '@/sheep/api/promotion/activity';
|
||||
import { formatSales, formatGoodsSwiper, fen2yuan, } from '@/sheep/hooks/useGoods';
|
||||
import detailNavbar from './components/detail/detail-navbar.vue';
|
||||
import detailCellSku from './components/detail/detail-cell-sku.vue';
|
||||
import detailCellService from './components/detail/detail-cell-service.vue';
|
||||
import detailCellParams from './components/detail/detail-cell-params.vue';
|
||||
import detailTabbar from './components/detail/detail-tabbar.vue';
|
||||
import detailSkeleton from './components/detail/detail-skeleton.vue';
|
||||
import detailCommentCard from './components/detail/detail-comment-card.vue';
|
||||
import detailContentCard from './components/detail/detail-content-card.vue';
|
||||
import detailActivityTip from './components/detail/detail-activity-tip.vue';
|
||||
import { isEmpty } from 'lodash';
|
||||
|
||||
onPageScroll(() => {});
|
||||
|
||||
const state = reactive({
|
||||
goodsId: 0,
|
||||
skeletonLoading: true, // SPU 加载中
|
||||
goodsInfo: {}, // SPU 信息
|
||||
showSelectSku: false, // 是否展示 SKU 选择弹窗
|
||||
selectedSku: {}, // 选中的 SKU
|
||||
showModel: false, // 是否展示 Coupon 优惠劵的弹窗
|
||||
couponInfo: [], // 可领取的 Coupon 优惠劵的列表
|
||||
showActivityModel: false, // 【满减送/限时折扣】是否展示 Activity 营销活动的弹窗
|
||||
activityInfo: [], // 【满减送/限时折扣】可参与的 Activity 营销活动的列表
|
||||
activityList: [], // 【秒杀/拼团/砍价】可参与的 Activity 营销活动的列表
|
||||
});
|
||||
|
||||
// 规格变更
|
||||
function onSkuChange(e) {
|
||||
state.selectedSku = e;
|
||||
}
|
||||
|
||||
// 添加购物车 TODO 芋艿:待测试
|
||||
function onAddCart(e) {
|
||||
sheep.$store('cart').add(e);
|
||||
}
|
||||
|
||||
// 立即购买 TODO 芋艿:待测试
|
||||
function onBuy(e) {
|
||||
sheep.$router.go('/pages/order/confirm', {
|
||||
data: JSON.stringify({
|
||||
order_type: 'goods',
|
||||
goods_list: [{
|
||||
goods_id: e.goods_id,
|
||||
goods_num: e.goods_num,
|
||||
goods_sku_price_id: e.id,
|
||||
}, ],
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
// 营销活动 TODO 芋艿:待测试
|
||||
function onActivity() {
|
||||
state.activityInfo = state.goodsInfo.promos;
|
||||
state.showActivityModel = true;
|
||||
}
|
||||
|
||||
// 立即领取 TODO 芋艿:待测试
|
||||
async function onGet(id) {
|
||||
const {
|
||||
error,
|
||||
msg
|
||||
} = await sheep.$api.coupon.get(id);
|
||||
if (error === 0) {
|
||||
uni.showToast({
|
||||
title: msg,
|
||||
});
|
||||
setTimeout(() => {
|
||||
getCoupon();
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO 芋艿:待测试
|
||||
const shareInfo = computed(() => {
|
||||
if (isEmpty(state.goodsInfo)) return {};
|
||||
return sheep.$platform.share.getShareInfo({
|
||||
title: state.goodsInfo.name,
|
||||
image: sheep.$url.cdn(state.goodsInfo.image),
|
||||
desc: state.goodsInfo.subtitle,
|
||||
params: {
|
||||
page: '2',
|
||||
query: state.goodsInfo.id,
|
||||
},
|
||||
}, {
|
||||
type: 'goods', // 商品海报
|
||||
title: state.goodsInfo.name, // 商品标题
|
||||
image: sheep.$url.cdn(state.goodsInfo.image), // 商品主图
|
||||
price: state.goodsInfo.price[0], // 商品价格
|
||||
original_price: state.goodsInfo.original_price, // 商品原价
|
||||
}, );
|
||||
});
|
||||
|
||||
onLoad(async (options) => {
|
||||
// 非法参数
|
||||
if (!options.id) {
|
||||
state.goodsInfo = null;
|
||||
return;
|
||||
}
|
||||
state.goodsId = options.id;
|
||||
// 1. 加载商品信息
|
||||
sheep.$api.goods.detail(state.goodsId).then((res) => {
|
||||
// 未找到商品
|
||||
if (res.code !== 0 || !res.data) {
|
||||
state.goodsInfo = null;
|
||||
return;
|
||||
}
|
||||
// 加载到商品
|
||||
state.skeletonLoading = false;
|
||||
state.goodsInfo = res.data;
|
||||
});
|
||||
|
||||
// 2. 加载优惠劵信息
|
||||
CouponApi.getCouponTemplateList(state.goodsId,2, 10).then((res) => {
|
||||
if (res.code !== 0) {
|
||||
return;
|
||||
}
|
||||
state.couponInfo = res.data;
|
||||
});
|
||||
|
||||
// 3. 加载营销活动信息
|
||||
ActivityApi.getActivityListBySpuId(state.goodsId).then((res) => {
|
||||
if (res.code !== 0) {
|
||||
return;
|
||||
}
|
||||
state.activityList = res.data;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.detail-card {
|
||||
background-color: #ffff;
|
||||
margin: 14rpx 20rpx;
|
||||
border-radius: 10rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// 价格标题卡片
|
||||
.title-card {
|
||||
.price-box {
|
||||
.price-text {
|
||||
font-size: 42rpx;
|
||||
font-weight: 500;
|
||||
color: #ff3000;
|
||||
line-height: 30rpx;
|
||||
font-family: OPPOSANS;
|
||||
|
||||
&::before {
|
||||
content: '¥';
|
||||
font-size: 30rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.origin-price-text {
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
text-decoration: line-through;
|
||||
color: $gray-c;
|
||||
font-family: OPPOSANS;
|
||||
|
||||
&::before {
|
||||
content: '¥';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sales-text {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: $gray-c;
|
||||
}
|
||||
|
||||
.discounts-box {
|
||||
.tag-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tag-box {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.tag {
|
||||
flex-shrink: 0;
|
||||
padding: 4rpx 10rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
border-radius: 4rpx;
|
||||
color: var(--ui-BG-Main);
|
||||
background: var(--ui-BG-Main-tag);
|
||||
}
|
||||
|
||||
.discounts-title {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: var(--ui-BG-Main);
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.cicon-forward {
|
||||
color: var(--ui-BG-Main);
|
||||
font-size: 24rpx;
|
||||
line-height: normal;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
line-height: 42rpx;
|
||||
}
|
||||
|
||||
.subtitle-text {
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
color: $dark-9;
|
||||
line-height: 42rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 购买
|
||||
.buy-box {
|
||||
.add-btn {
|
||||
width: 214rpx;
|
||||
height: 72rpx;
|
||||
font-weight: 500;
|
||||
font-size: 28rpx;
|
||||
border-radius: 40rpx 0 0 40rpx;
|
||||
background-color: var(--ui-BG-Main-light);
|
||||
color: var(--ui-BG-Main);
|
||||
}
|
||||
|
||||
.buy-btn {
|
||||
width: 214rpx;
|
||||
height: 72rpx;
|
||||
font-weight: 500;
|
||||
font-size: 28rpx;
|
||||
|
||||
border-radius: 0 40rpx 40rpx 0;
|
||||
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
color: $white;
|
||||
}
|
||||
|
||||
.disabled-btn {
|
||||
width: 428rpx;
|
||||
height: 72rpx;
|
||||
border-radius: 40rpx;
|
||||
background: #999999;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
.model-box {
|
||||
height: 60vh;
|
||||
|
||||
.model-content {
|
||||
height: 56vh;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
383
pages/goods/list.vue
Normal file
383
pages/goods/list.vue
Normal file
@@ -0,0 +1,383 @@
|
||||
<template>
|
||||
<s-layout
|
||||
navbar="normal"
|
||||
:leftWidth="0"
|
||||
:rightWidth="0"
|
||||
tools="search"
|
||||
:defaultSearch="state.keyword"
|
||||
@search="onSearch"
|
||||
>
|
||||
<!-- 筛选 -->
|
||||
<su-sticky bgColor="#fff">
|
||||
<view class="ss-flex">
|
||||
<view class="ss-flex-1">
|
||||
<su-tabs
|
||||
:list="state.tabList"
|
||||
:scrollable="false"
|
||||
@change="onTabsChange"
|
||||
:current="state.currentTab"
|
||||
></su-tabs>
|
||||
</view>
|
||||
<view class="list-icon" @tap="state.iconStatus = !state.iconStatus">
|
||||
<text v-if="state.iconStatus" class="sicon-goods-list"></text>
|
||||
<text v-else class="sicon-goods-card"></text>
|
||||
</view>
|
||||
</view>
|
||||
</su-sticky>
|
||||
|
||||
<!-- 弹窗 -->
|
||||
<su-popup
|
||||
:show="state.showFilter"
|
||||
type="top"
|
||||
round="10"
|
||||
:space="sys_navBar + 38"
|
||||
backgroundColor="#F6F6F6"
|
||||
:zIndex="10"
|
||||
@close="state.showFilter = false"
|
||||
>
|
||||
<view class="filter-list-box">
|
||||
<view
|
||||
class="filter-item"
|
||||
v-for="(item, index) in state.tabList[state.currentTab].list"
|
||||
:key="item.value"
|
||||
:class="[{ 'filter-item-active': index == state.curFilter }]"
|
||||
@tap="onFilterItem(index)"
|
||||
>
|
||||
{{ item.label }}
|
||||
</view>
|
||||
</view>
|
||||
</su-popup>
|
||||
<view v-if="state.iconStatus && state.pagination.total > 0" class="goods-list ss-m-t-20">
|
||||
<view
|
||||
class="ss-p-l-20 ss-p-r-20 ss-m-b-20"
|
||||
v-for="item in state.pagination.data"
|
||||
:key="item.id"
|
||||
>
|
||||
<s-goods-column
|
||||
class=""
|
||||
size="lg"
|
||||
:data="item"
|
||||
:topRadius="10"
|
||||
:bottomRadius="10"
|
||||
@click="sheep.$router.go('/pages/goods/index', { id: item.id })"
|
||||
></s-goods-column>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
v-if="!state.iconStatus && state.pagination.total > 0"
|
||||
class="ss-flex ss-flex-wrap ss-p-x-20 ss-m-t-20 ss-col-top"
|
||||
>
|
||||
<view class="goods-list-box">
|
||||
<view class="left-list" v-for="item in state.leftGoodsList" :key="item.id">
|
||||
<s-goods-column
|
||||
class="goods-md-box"
|
||||
size="md"
|
||||
:data="item"
|
||||
:topRadius="10"
|
||||
:bottomRadius="10"
|
||||
@click="sheep.$router.go('/pages/goods/index', { id: item.id })"
|
||||
@getHeight="mountMasonry($event, 'left')"
|
||||
>
|
||||
<template v-slot:cart>
|
||||
<button class="ss-reset-button cart-btn"> </button>
|
||||
</template>
|
||||
</s-goods-column>
|
||||
</view>
|
||||
</view>
|
||||
<view class="goods-list-box">
|
||||
<view class="right-list" v-for="item in state.rightGoodsList" :key="item.id">
|
||||
<s-goods-column
|
||||
class="goods-md-box"
|
||||
size="md"
|
||||
:topRadius="10"
|
||||
:bottomRadius="10"
|
||||
:data="item"
|
||||
@click="sheep.$router.go('/pages/goods/index', { id: item.id })"
|
||||
@getHeight="mountMasonry($event, 'right')"
|
||||
>
|
||||
<template v-slot:cart>
|
||||
<button class="ss-reset-button cart-btn"> </button>
|
||||
</template>
|
||||
</s-goods-column>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<uni-load-more
|
||||
v-if="state.pagination.total > 0"
|
||||
:status="state.loadStatus"
|
||||
:content-text="{
|
||||
contentdown: '上拉加载更多',
|
||||
}"
|
||||
@tap="loadmore"
|
||||
/>
|
||||
<s-empty v-if="state.pagination.total === 0" icon="/static/soldout-empty.png" text="暂无商品">
|
||||
</s-empty>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive } from 'vue';
|
||||
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
|
||||
import sheep from '@/sheep';
|
||||
import _ from 'lodash';
|
||||
|
||||
const sys_navBar = sheep.$platform.navbar;
|
||||
const emits = defineEmits(['close', 'change']);
|
||||
|
||||
const pagination = {
|
||||
data: [],
|
||||
current_page: 1,
|
||||
total: 1,
|
||||
last_page: 1,
|
||||
};
|
||||
const state = reactive({
|
||||
pagination: {
|
||||
data: [],
|
||||
current_page: 1,
|
||||
total: 1,
|
||||
last_page: 1,
|
||||
},
|
||||
// currentSort: 'weigh',
|
||||
// currentOrder: 'desc',
|
||||
currentTab: 0,
|
||||
filterParams: {},
|
||||
curFilter: 0,
|
||||
showFilter: false,
|
||||
iconStatus: false,
|
||||
categoryId: 0,
|
||||
tabList: [
|
||||
{
|
||||
name: '综合推荐',
|
||||
// value: '',
|
||||
list: [
|
||||
{
|
||||
label: '综合推荐',
|
||||
// sort: '',
|
||||
// order: true,
|
||||
},
|
||||
{
|
||||
label: '价格升序',
|
||||
sort: 'price',
|
||||
order: true,
|
||||
},
|
||||
{
|
||||
label: '价格降序',
|
||||
sort: 'price',
|
||||
order: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: '销量',
|
||||
// value: 'salesCount',
|
||||
},
|
||||
{
|
||||
name: '新品优先',
|
||||
// value: 'create_time',
|
||||
},
|
||||
],
|
||||
loadStatus: '',
|
||||
keyword: '',
|
||||
leftGoodsList: [],
|
||||
rightGoodsList: [],
|
||||
});
|
||||
|
||||
// 加载瀑布流
|
||||
let count = 0;
|
||||
let leftHeight = 0;
|
||||
let rightHeight = 0;
|
||||
|
||||
function mountMasonry(height = 0, where = 'left') {
|
||||
if (!state.pagination.data[count]) return;
|
||||
|
||||
if (where === 'left') {
|
||||
leftHeight += height;
|
||||
} else {
|
||||
rightHeight += height;
|
||||
}
|
||||
if (leftHeight <= rightHeight) {
|
||||
state.leftGoodsList.push(state.pagination.data[count]);
|
||||
} else {
|
||||
state.rightGoodsList.push(state.pagination.data[count]);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
function emptyList() {
|
||||
state.pagination = pagination
|
||||
state.leftGoodsList = [];
|
||||
state.rightGoodsList = [];
|
||||
count = 0;
|
||||
leftHeight = 0;
|
||||
rightHeight = 0;
|
||||
}
|
||||
//搜索
|
||||
function onSearch(e) {
|
||||
state.keyword = e;
|
||||
emptyList();
|
||||
getList(state.currentSort, state.currentOrder, state.categoryId, e);
|
||||
}
|
||||
|
||||
// 点击
|
||||
function onTabsChange(e) {
|
||||
if (state.tabList[e.index].list) {
|
||||
state.currentTab = e.index;
|
||||
state.showFilter = !state.showFilter;
|
||||
return;
|
||||
} else {
|
||||
state.showFilter = false;
|
||||
}
|
||||
if (e.index === state.currentTab) {
|
||||
return;
|
||||
} else {
|
||||
state.currentTab = e.index;
|
||||
}
|
||||
emptyList();
|
||||
|
||||
getList(e.value, state.currentOrder, state.categoryId, state.keyword);
|
||||
}
|
||||
|
||||
// 点击筛选项
|
||||
const onFilterItem = (val) => {
|
||||
console.log(val)
|
||||
if (
|
||||
state.currentSort === state.tabList[0].list[val].sort &&
|
||||
state.currentOrder === state.tabList[0].list[val].order
|
||||
) {
|
||||
state.showFilter = false;
|
||||
return;
|
||||
}
|
||||
state.curFilter = val;
|
||||
state.tabList[0].name = state.tabList[0].list[val].label;
|
||||
state.currentSort = state.tabList[0].list[val].sort;
|
||||
state.currentOrder = state.tabList[0].list[val].order;
|
||||
emptyList();
|
||||
getList(state.currentSort, state.currentOrder, state.categoryId, state.keyword);
|
||||
state.showFilter = false;
|
||||
};
|
||||
|
||||
async function getList(Sort, Order, categoryId, keyword, page = 1, list_rows = 6) {
|
||||
state.loadStatus = 'loading';
|
||||
const res = await sheep.$api.goods.list({
|
||||
sortField: Sort,
|
||||
sortAsc: Order,
|
||||
category_id: !keyword ? categoryId : '',
|
||||
pageSize:list_rows,
|
||||
keyword: keyword,
|
||||
pageNo:page,
|
||||
});
|
||||
if (res.code === 0) {
|
||||
let couponList = _.concat(state.pagination.data, res.data.list);
|
||||
state.pagination = {
|
||||
...res.data,
|
||||
data: couponList,
|
||||
};
|
||||
mountMasonry();
|
||||
if (state.pagination.current_page < state.pagination.last_page) {
|
||||
state.loadStatus = 'more';
|
||||
} else {
|
||||
state.loadStatus = 'noMore';
|
||||
}
|
||||
}
|
||||
}
|
||||
// 加载更多
|
||||
function loadmore() {
|
||||
if (state.loadStatus !== 'noMore') {
|
||||
getList(
|
||||
state.currentSort,
|
||||
state.currentOrder,
|
||||
state.categoryId,
|
||||
state.keyword,
|
||||
state.pagination.current_page + 1,
|
||||
);
|
||||
}
|
||||
}
|
||||
onLoad((options) => {
|
||||
state.categoryId = options.categoryId;
|
||||
state.keyword = options.keyword;
|
||||
getList(state.currentSort, state.currentOrder, state.categoryId, state.keyword);
|
||||
});
|
||||
// 上拉加载更多
|
||||
onReachBottom(() => {
|
||||
loadmore();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goods-list-box {
|
||||
width: 50%;
|
||||
box-sizing: border-box;
|
||||
.left-list {
|
||||
margin-right: 10rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
.right-list {
|
||||
margin-left: 10rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
}
|
||||
.goods-box {
|
||||
&:nth-last-of-type(1) {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
&:nth-child(2n) {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
.list-icon {
|
||||
width: 80rpx;
|
||||
.sicon-goods-card {
|
||||
font-size: 40rpx;
|
||||
}
|
||||
.sicon-goods-list {
|
||||
font-size: 40rpx;
|
||||
}
|
||||
}
|
||||
.goods-card {
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
.list-filter-tabs {
|
||||
background-color: #fff;
|
||||
}
|
||||
.filter-list-box {
|
||||
padding: 28rpx 52rpx;
|
||||
.filter-item {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
line-height: normal;
|
||||
margin-bottom: 24rpx;
|
||||
&:nth-last-child(1) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
.filter-item-active {
|
||||
color: var(--ui-BG-Main);
|
||||
}
|
||||
}
|
||||
.tab-item {
|
||||
height: 50px;
|
||||
position: relative;
|
||||
z-index: 11;
|
||||
|
||||
.tab-title {
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.cur-tab-title {
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
|
||||
.tab-line {
|
||||
width: 60rpx;
|
||||
height: 6rpx;
|
||||
border-radius: 6rpx;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
bottom: 10rpx;
|
||||
background-color: var(--ui-BG-Main);
|
||||
z-index: 12;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
368
pages/goods/score.vue
Normal file
368
pages/goods/score.vue
Normal file
@@ -0,0 +1,368 @@
|
||||
<template>
|
||||
<view>
|
||||
<s-layout :onShareAppMessage="state.shareInfo" navbar="goods">
|
||||
<!-- 标题栏 -->
|
||||
<detailNavbar />
|
||||
<detailSkeleton v-if="state.skeletonLoading" />
|
||||
<!-- 空置页 -->
|
||||
|
||||
<s-empty
|
||||
v-else-if="state.goodsInfo === null"
|
||||
text="商品不存在或已下架"
|
||||
icon="/static/soldout-empty.png"
|
||||
showAction
|
||||
actionText="再逛逛"
|
||||
actionUrl="/pages/goods/list"
|
||||
/>
|
||||
<block v-else>
|
||||
<!-- 商品轮播图 -->
|
||||
<su-swiper
|
||||
class="ss-m-b-14 detail-swiper-selector"
|
||||
isPreview
|
||||
:list="state.goodsSwiper"
|
||||
dotStyle="tag"
|
||||
imageMode="widthFix"
|
||||
dotCur="bg-mask-40"
|
||||
:seizeHeight="750"
|
||||
/>
|
||||
|
||||
<!-- 价格+标题 -->
|
||||
<view class="title-card detail-card ss-p-y-40 ss-p-x-20">
|
||||
<view class="ss-flex ss-row-between ss-col-center ss-m-b-18">
|
||||
<view class="price-box ss-flex ss-col-bottom">
|
||||
<view v-if="goodsPrice.price > 0" class="price-text"> ¥{{ goodsPrice.price }} </view>
|
||||
<text v-if="goodsPrice.price > 0 && goodsPrice.score > 0">+</text>
|
||||
<image
|
||||
:src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
|
||||
class="score-img"
|
||||
></image>
|
||||
<view class="score-text ss-m-r-16">
|
||||
{{ goodsPrice.score }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="sales-text">
|
||||
{{ formatExchange(state.goodsInfo.sales_show_type, state.goodsInfo.sales) }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="origin-price-text ss-m-b-60" v-if="state.goodsInfo.original_price">
|
||||
原价:¥{{ state.selectedSkuPrice.original_price || state.goodsInfo.original_price }}
|
||||
</view>
|
||||
<view class="title-text ss-line-2 ss-m-b-6">{{ state.goodsInfo.title }}</view>
|
||||
<view class="subtitle-text ss-line-1">{{ state.goodsInfo.subtitle }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 功能卡片 -->
|
||||
<view class="detail-cell-card detail-card ss-flex-col">
|
||||
<detail-cell-sku
|
||||
v-model="state.selectedSkuPrice.goods_sku_text"
|
||||
:skus="state.goodsInfo.skus"
|
||||
@tap="state.showSelectSku = true"
|
||||
/>
|
||||
<detail-cell-service v-model="state.goodsInfo.service" />
|
||||
<detail-cell-params v-model="state.goodsInfo.params" />
|
||||
</view>
|
||||
<!-- 规格与数量弹框 -->
|
||||
<s-select-sku
|
||||
:goodsInfo="state.goodsInfo"
|
||||
:show="state.showSelectSku"
|
||||
:isScore="true"
|
||||
@addCart="onAddCart"
|
||||
@buy="onBuy"
|
||||
@change="onSkuChange"
|
||||
@close="state.showSelectSku = false"
|
||||
/>
|
||||
|
||||
<!-- 评价 -->
|
||||
<view class="detail-comment-selector">
|
||||
<detail-comment-card :goodsId="state.goodsId" />
|
||||
</view>
|
||||
|
||||
<!-- 详情 -->
|
||||
<view class="detail-content-selector"></view>
|
||||
<detail-content-card :content="state.goodsInfo.content" />
|
||||
|
||||
<!-- 详情tabbar -->
|
||||
<detail-tabbar v-model="state.goodsInfo" :shareIcon="false" :collectIcon="false">
|
||||
<!-- TODO: 缺货中 已售罄 判断 设计-->
|
||||
<view class="buy-box ss-flex ss-col-center ss-p-r-20" v-if="state.goodsInfo.stock > 0">
|
||||
<button class="ss-reset-button buy-btn" @tap="state.showSelectSku = true">
|
||||
立即兑换
|
||||
</button>
|
||||
</view>
|
||||
<view class="buy-box ss-flex ss-col-center ss-p-r-20" v-else>
|
||||
<button class="ss-reset-button disabled-btn" disabled> 已兑完 </button>
|
||||
</view>
|
||||
</detail-tabbar>
|
||||
</block>
|
||||
</s-layout>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, computed } from 'vue';
|
||||
import { onLoad, onPageScroll } from '@dcloudio/uni-app';
|
||||
import sheep from '@/sheep';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { formatExchange, formatGoodsSwiper } from '@/sheep/hooks/useGoods';
|
||||
import detailNavbar from './components/detail/detail-navbar.vue';
|
||||
import detailCellSku from './components/detail/detail-cell-sku.vue';
|
||||
import detailCellService from './components/detail/detail-cell-service.vue';
|
||||
import detailCellParams from './components/detail/detail-cell-params.vue';
|
||||
import detailTabbar from './components/detail/detail-tabbar.vue';
|
||||
import detailSkeleton from './components/detail/detail-skeleton.vue';
|
||||
import detailCommentCard from './components/detail/detail-comment-card.vue';
|
||||
import detailContentCard from './components/detail/detail-content-card.vue';
|
||||
|
||||
const headerBg = sheep.$url.css('/static/img/shop/goods/score-bg.png');
|
||||
const seckillBg = sheep.$url.css('/static/img/shop/goods/seckill-tip-bg.png');
|
||||
const grouponBg = sheep.$url.css('/static/img/shop/goods/seckill-tip-bg.png');
|
||||
|
||||
onPageScroll(() => {});
|
||||
|
||||
const state = reactive({
|
||||
goodsId: 0,
|
||||
skeletonLoading: true,
|
||||
goodsInfo: {},
|
||||
showSelectSku: false,
|
||||
goodsSwiper: [],
|
||||
selectedSkuPrice: {},
|
||||
shareInfo: {},
|
||||
showModel: false,
|
||||
total: 0,
|
||||
couponInfo: [],
|
||||
});
|
||||
|
||||
const goodsPrice = computed(() => {
|
||||
let price, score;
|
||||
if (isEmpty(state.selectedSkuPrice)) {
|
||||
price = state.goodsInfo.price[0];
|
||||
score = state.goodsInfo.score || 0;
|
||||
} else {
|
||||
price = state.selectedSkuPrice.price;
|
||||
score = state.selectedSkuPrice.score || 0;
|
||||
}
|
||||
return { price, score };
|
||||
});
|
||||
|
||||
// 规格变更
|
||||
function onSkuChange(e) {
|
||||
state.selectedSkuPrice = e;
|
||||
}
|
||||
// 格式化价格
|
||||
function formatPrice(e) {
|
||||
if (Number(e[0]) > 0) {
|
||||
return e.length === 1 ? e[0] : e.join('~');
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
// 添加购物车
|
||||
function onAddCart(e) {
|
||||
sheep.$store('cart').add(e);
|
||||
}
|
||||
// 立即购买
|
||||
function onBuy(e) {
|
||||
sheep.$router.go('/pages/order/confirm', {
|
||||
data: JSON.stringify({
|
||||
order_type: 'score',
|
||||
goods_list: [
|
||||
{
|
||||
goods_id: e.goods_id,
|
||||
goods_num: e.goods_num,
|
||||
goods_sku_price_id: e.id,
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
onLoad((options) => {
|
||||
// 非法参数
|
||||
if (!options.id) {
|
||||
state.goodsInfo = null;
|
||||
return;
|
||||
}
|
||||
state.goodsId = options.id;
|
||||
// 加载商品信息
|
||||
sheep.$api.app.scoreShop.detail(state.goodsId).then((res) => {
|
||||
state.skeletonLoading = false;
|
||||
if (res.error === 0) {
|
||||
state.goodsInfo = res.data;
|
||||
state.goodsSwiper = formatGoodsSwiper(state.goodsInfo.images);
|
||||
} else {
|
||||
// 未找到商品
|
||||
state.goodsInfo = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.detail-card {
|
||||
background-color: #ffff;
|
||||
margin: 14rpx 20rpx;
|
||||
border-radius: 10rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// 价格标题卡片
|
||||
.title-card {
|
||||
width: 710rpx;
|
||||
box-sizing: border-box;
|
||||
background-size: 100% 100%;
|
||||
border-radius: 10rpx;
|
||||
background-image: v-bind(headerBg);
|
||||
background-repeat: no-repeat;
|
||||
.price-box {
|
||||
.score-img {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
margin: 0 4rpx;
|
||||
}
|
||||
.score-text {
|
||||
font-size: 42rpx;
|
||||
font-weight: 500;
|
||||
color: #ff3000;
|
||||
line-height: 36rpx;
|
||||
font-family: OPPOSANS;
|
||||
}
|
||||
.price-text {
|
||||
font-size: 42rpx;
|
||||
font-weight: 500;
|
||||
color: #ff3000;
|
||||
line-height: 36rpx;
|
||||
font-family: OPPOSANS;
|
||||
}
|
||||
}
|
||||
.origin-price-text {
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
text-decoration: line-through;
|
||||
color: $gray-c;
|
||||
font-family: OPPOSANS;
|
||||
}
|
||||
|
||||
.sales-text {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: $gray-c;
|
||||
}
|
||||
|
||||
.discounts-box {
|
||||
.discounts-tag {
|
||||
padding: 4rpx 10rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
border-radius: 4rpx;
|
||||
color: var(--ui-BG-Main);
|
||||
// background: rgba(#2aae67, 0.05);
|
||||
background: var(--ui-BG-Main-tag);
|
||||
}
|
||||
|
||||
.discounts-title {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: var(--ui-BG-Main);
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.cicon-forward {
|
||||
color: var(--ui-BG-Main);
|
||||
font-size: 24rpx;
|
||||
line-height: normal;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
line-height: 42rpx;
|
||||
}
|
||||
|
||||
.subtitle-text {
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
color: $dark-9;
|
||||
line-height: 42rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 购买
|
||||
.buy-box {
|
||||
.buy-btn {
|
||||
width: 630rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
color: $white;
|
||||
}
|
||||
.disabled-btn {
|
||||
width: 630rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
background: #999999;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
//秒杀卡片
|
||||
.seckill-box {
|
||||
background: v-bind(seckillBg) no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.groupon-box {
|
||||
background: v-bind(grouponBg) no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
//活动卡片
|
||||
.activity-box {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 10rpx;
|
||||
|
||||
.activity-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
line-height: 42rpx;
|
||||
|
||||
.activity-icon {
|
||||
width: 38rpx;
|
||||
height: 38rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.activity-go {
|
||||
width: 70rpx;
|
||||
height: 32rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
font-weight: 500;
|
||||
color: #ff6000;
|
||||
font-size: 24rpx;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.model-box {
|
||||
height: 60vh;
|
||||
.model-content {
|
||||
height: 56vh;
|
||||
}
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
534
pages/goods/seckill.vue
Normal file
534
pages/goods/seckill.vue
Normal file
@@ -0,0 +1,534 @@
|
||||
<template>
|
||||
<s-layout :onShareAppMessage="shareInfo" navbar="goods">
|
||||
<!-- 标题栏 -->
|
||||
<detailNavbar />
|
||||
<!-- 骨架屏 -->
|
||||
<detailSkeleton v-if="state.skeletonLoading" />
|
||||
<!-- 空置页 -->
|
||||
<s-empty
|
||||
v-else-if="state.goodsInfo === null || state.goodsInfo.activity_type !== 'seckill'"
|
||||
text="活动不存在或已结束"
|
||||
icon="/static/soldout-empty.png"
|
||||
showAction
|
||||
actionText="再逛逛"
|
||||
actionUrl="/pages/goods/list"
|
||||
/>
|
||||
<block v-else>
|
||||
<view class="detail-swiper-selector">
|
||||
<!-- 轮播 -->
|
||||
<su-swiper
|
||||
class="ss-m-b-14"
|
||||
isPreview
|
||||
:list="state.goodsSwiper"
|
||||
dotStyle="tag"
|
||||
imageMode="widthFix"
|
||||
dotCur="bg-mask-40"
|
||||
:seizeHeight="750"
|
||||
/>
|
||||
|
||||
<!-- 价格+标题 -->
|
||||
<view class="title-card ss-m-y-14 ss-m-x-20 ss-p-x-20 ss-p-y-34">
|
||||
<view class="price-box ss-flex ss-row-between ss-m-b-18">
|
||||
<view class="ss-flex">
|
||||
<view class="price-text ss-m-r-16">
|
||||
{{ state.selectedSkuPrice.price || formatPrice(state.goodsInfo.price) }}
|
||||
</view>
|
||||
<view class="tig ss-flex ss-col-center">
|
||||
<view class="tig-icon ss-flex ss-col-center ss-row-center">
|
||||
<text class="cicon-alarm"></text>
|
||||
</view>
|
||||
<view class="tig-title">秒杀价</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="countdown-box" v-if="endTime.ms > 0">
|
||||
<view class="countdown-title ss-m-b-20">距结束仅剩</view>
|
||||
<view class="ss-flex countdown-time">
|
||||
<view class="ss-flex countdown-h">{{ endTime.h }}</view>
|
||||
<view class="ss-m-x-4">:</view>
|
||||
<view class="countdown-num ss-flex ss-row-center">{{ endTime.m }}</view>
|
||||
<view class="ss-m-x-4">:</view>
|
||||
<view class="countdown-num ss-flex ss-row-center">{{ endTime.s }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="countdown-title" v-else> 活动已结束 </view>
|
||||
</view>
|
||||
<view class="ss-flex ss-row-between ss-m-b-60">
|
||||
<view class="origin-price ss-flex ss-col-center" v-if="state.goodsInfo.original_price">
|
||||
原价
|
||||
<view class="origin-price-text">
|
||||
{{ state.selectedSkuPrice.original_price || state.goodsInfo.original_price }}
|
||||
</view>
|
||||
</view>
|
||||
<detail-progress :percent="state.percent" />
|
||||
</view>
|
||||
|
||||
<view class="title-text ss-line-2 ss-m-b-6">{{ state.goodsInfo.title }}</view>
|
||||
<view class="subtitle-text ss-line-1">{{ state.goodsInfo.subtitle }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 功能卡片 -->
|
||||
<view class="detail-cell-card detail-card ss-flex-col">
|
||||
<detail-cell-sku
|
||||
v-model="state.selectedSkuPrice.goods_sku_text"
|
||||
:skus="state.goodsInfo.skus"
|
||||
@tap="state.showSelectSku = true"
|
||||
/>
|
||||
<detail-cell-service v-model="state.goodsInfo.service" />
|
||||
<detail-cell-params v-model="state.goodsInfo.params" />
|
||||
</view>
|
||||
<!-- 规格与数量弹框 -->
|
||||
<s-select-seckill-sku
|
||||
v-model="state.goodsInfo"
|
||||
:show="state.showSelectSku"
|
||||
@buy="onBuy"
|
||||
@change="onSkuChange"
|
||||
@close="state.showSelectSku = false"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 评价 -->
|
||||
<detail-comment-card class="detail-comment-selector" :goodsId="state.goodsId" />
|
||||
<!-- 详情 -->
|
||||
<detail-content-card class="detail-content-selector" :content="state.goodsInfo.content" />
|
||||
|
||||
<!-- 详情tabbar -->
|
||||
<detail-tabbar v-model="state.goodsInfo">
|
||||
<!-- TODO: 缺货中 已售罄 判断 设计-->
|
||||
<view class="buy-box ss-flex ss-col-center ss-p-r-20">
|
||||
<button
|
||||
class="ss-reset-button origin-price-btn ss-flex-col"
|
||||
v-if="state.goodsInfo.original_price"
|
||||
@tap="sheep.$router.go('/pages/goods/index', { id: state.goodsInfo.id })"
|
||||
>
|
||||
<view>
|
||||
<view class="btn-price">{{ state.goodsInfo.original_price }}</view>
|
||||
<view>原价购买</view>
|
||||
</view>
|
||||
</button>
|
||||
<button v-else class="ss-reset-button origin-price-btn ss-flex-col">
|
||||
<view
|
||||
class="no-original"
|
||||
:class="
|
||||
state.goodsInfo.stock === 0 || state.goodsInfo.activity.status != 'ing' ? '' : ''
|
||||
"
|
||||
>秒杀价</view
|
||||
>
|
||||
</button>
|
||||
<button
|
||||
class="ss-reset-button btn-box ss-flex-col"
|
||||
@tap="state.showSelectSku = true"
|
||||
:class="
|
||||
state.goodsInfo.activity.status === 'ing' && state.goodsInfo.stock != 0
|
||||
? 'check-btn-box'
|
||||
: 'disabled-btn-box'
|
||||
"
|
||||
:disabled="state.goodsInfo.stock === 0 || state.goodsInfo.activity.status != 'ing'"
|
||||
>
|
||||
<view class="btn-price">{{ state.goodsInfo.price[0] }}</view>
|
||||
<view v-if="state.goodsInfo.activity.status === 'ing'">
|
||||
<view v-if="state.goodsInfo.stock === 0">已售罄</view>
|
||||
<view v-else>立即秒杀</view>
|
||||
</view>
|
||||
<view v-else>{{ state.goodsInfo.activity.status_text }}</view>
|
||||
</button>
|
||||
</view>
|
||||
</detail-tabbar>
|
||||
</block>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, computed } from 'vue';
|
||||
import { onLoad, onPageScroll } from '@dcloudio/uni-app';
|
||||
import sheep from '@/sheep';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { useDurationTime, formatGoodsSwiper, formatPrice } from '@/sheep/hooks/useGoods';
|
||||
import detailNavbar from './components/detail/detail-navbar.vue';
|
||||
import detailCellSku from './components/detail/detail-cell-sku.vue';
|
||||
import detailCellService from './components/detail/detail-cell-service.vue';
|
||||
import detailCellParams from './components/detail/detail-cell-params.vue';
|
||||
import detailTabbar from './components/detail/detail-tabbar.vue';
|
||||
import detailSkeleton from './components/detail/detail-skeleton.vue';
|
||||
import detailCommentCard from './components/detail/detail-comment-card.vue';
|
||||
import detailContentCard from './components/detail/detail-content-card.vue';
|
||||
import detailProgress from './components/detail/detail-progress.vue';
|
||||
|
||||
const headerBg = sheep.$url.css('/static/img/shop/goods/seckill-bg.png');
|
||||
const btnBg = sheep.$url.css('/static/img/shop/goods/seckill-btn.png');
|
||||
const disabledBtnBg = sheep.$url.css(
|
||||
'/static/img/shop/goods/activity-btn-disabled.png',
|
||||
);
|
||||
const seckillBg = sheep.$url.css('/static/img/shop/goods/seckill-tip-bg.png');
|
||||
const grouponBg = sheep.$url.css('/static/img/shop/goods/groupon-tip-bg.png');
|
||||
|
||||
onPageScroll(() => {});
|
||||
const state = reactive({
|
||||
goodsId: 0,
|
||||
skeletonLoading: true,
|
||||
goodsInfo: {},
|
||||
showSelectSku: false,
|
||||
goodsSwiper: [],
|
||||
selectedSkuPrice: {},
|
||||
showModel: false,
|
||||
total: 0,
|
||||
percent: 0,
|
||||
price: '',
|
||||
});
|
||||
|
||||
// 倒计时
|
||||
const endTime = computed(() => {
|
||||
return useDurationTime(state.goodsInfo.activity.end_time);
|
||||
});
|
||||
|
||||
// 规格变更
|
||||
function onSkuChange(e) {
|
||||
state.selectedSkuPrice = e;
|
||||
}
|
||||
|
||||
// 立即购买
|
||||
function onBuy(e) {
|
||||
sheep.$router.go('/pages/order/confirm', {
|
||||
data: JSON.stringify({
|
||||
order_type: 'goods',
|
||||
buy_type: 'seckill',
|
||||
activity_id: state.goodsInfo.activity.id,
|
||||
goods_list: [
|
||||
{
|
||||
goods_id: e.goods_id,
|
||||
goods_num: e.goods_num,
|
||||
goods_sku_price_id: e.id,
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
const shareInfo = computed(() => {
|
||||
if (isEmpty(state.goodsInfo?.activity)) return {};
|
||||
return sheep.$platform.share.getShareInfo(
|
||||
{
|
||||
title: state.goodsInfo.title,
|
||||
image: sheep.$url.cdn(state.goodsInfo.image),
|
||||
params: {
|
||||
page: '4',
|
||||
query: state.goodsInfo.id + ',' + state.goodsInfo.activity.id,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'goods', // 商品海报
|
||||
title: state.goodsInfo.title, // 商品标题
|
||||
image: sheep.$url.cdn(state.goodsInfo.image), // 商品主图
|
||||
price: state.goodsInfo.price[0], // 商品价格
|
||||
original_price: state.goodsInfo.original_price, // 商品原价
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
onLoad((options) => {
|
||||
// 非法参数
|
||||
if (!options.id) {
|
||||
state.goodsInfo = null;
|
||||
return;
|
||||
}
|
||||
state.goodsId = options.id;
|
||||
// 加载商品信息
|
||||
sheep.$api.goods
|
||||
.detail(options.id, {
|
||||
activity_id: options.activity_id,
|
||||
})
|
||||
.then((res) => {
|
||||
state.skeletonLoading = false;
|
||||
if (res.error === 0) {
|
||||
state.goodsInfo = res.data;
|
||||
state.percent =
|
||||
state.goodsInfo.stock + state.goodsInfo.sales > 0
|
||||
? (
|
||||
(state.goodsInfo.sales / (state.goodsInfo.sales + state.goodsInfo.stock)) *
|
||||
100
|
||||
).toFixed(2)
|
||||
: 0;
|
||||
state.percent = Number(state.percent);
|
||||
state.goodsSwiper = formatGoodsSwiper(state.goodsInfo.images);
|
||||
} else {
|
||||
// 未找到商品
|
||||
state.goodsInfo = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.disabled-btn-box[disabled] {
|
||||
background-color: transparent;
|
||||
}
|
||||
.detail-card {
|
||||
background-color: $white;
|
||||
margin: 14rpx 20rpx;
|
||||
border-radius: 10rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// 价格标题卡片
|
||||
.title-card {
|
||||
width: 710rpx;
|
||||
box-sizing: border-box;
|
||||
// height: 320rpx;
|
||||
background-size: 100% 100%;
|
||||
border-radius: 10rpx;
|
||||
background-image: v-bind(headerBg);
|
||||
background-repeat: no-repeat;
|
||||
|
||||
.price-box {
|
||||
.price-text {
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
line-height: normal;
|
||||
font-family: OPPOSANS;
|
||||
|
||||
&::before {
|
||||
content: '¥';
|
||||
font-size: 30rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.origin-price {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #fff;
|
||||
opacity: 0.7;
|
||||
|
||||
.origin-price-text {
|
||||
text-decoration: line-through;
|
||||
|
||||
font-family: OPPOSANS;
|
||||
|
||||
&::before {
|
||||
content: '¥';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tig {
|
||||
border: 2rpx solid #ffffff;
|
||||
border-radius: 4rpx;
|
||||
width: 126rpx;
|
||||
height: 38rpx;
|
||||
|
||||
.tig-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
margin-left: -2rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 4rpx 0 0 4rpx;
|
||||
|
||||
.cicon-alarm {
|
||||
font-size: 32rpx;
|
||||
color: #fc6e6f;
|
||||
}
|
||||
}
|
||||
|
||||
.tig-title {
|
||||
width: 86rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
line-height: normal;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.countdown-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.countdown-time {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
.countdown-h {
|
||||
font-size: 24rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
padding: 0 4rpx;
|
||||
height: 40rpx;
|
||||
background: rgba(#000000, 0.1);
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
.countdown-num {
|
||||
font-size: 24rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
background: rgba(#000000, 0.1);
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.discounts-box {
|
||||
.discounts-tag {
|
||||
padding: 4rpx 10rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
border-radius: 4rpx;
|
||||
color: var(--ui-BG-Main);
|
||||
// background: rgba(#2aae67, 0.05);
|
||||
background: var(--ui-BG-Main-tag);
|
||||
}
|
||||
|
||||
.discounts-title {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: var(--ui-BG-Main);
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.cicon-forward {
|
||||
color: var(--ui-BG-Main);
|
||||
font-size: 24rpx;
|
||||
line-height: normal;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
line-height: 42rpx;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.subtitle-text {
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
color: #ffffff;
|
||||
line-height: 42rpx;
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
// 购买
|
||||
.buy-box {
|
||||
.check-btn-box {
|
||||
width: 248rpx;
|
||||
height: 80rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
margin-left: -36rpx;
|
||||
background-image: v-bind(btnBg);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
color: #ffffff;
|
||||
line-height: normal;
|
||||
border-radius: 0px 40rpx 40rpx 0px;
|
||||
}
|
||||
|
||||
.disabled-btn-box {
|
||||
width: 248rpx;
|
||||
height: 80rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
margin-left: -36rpx;
|
||||
background-image: v-bind(disabledBtnBg);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
color: #999999;
|
||||
line-height: normal;
|
||||
border-radius: 0px 40rpx 40rpx 0px;
|
||||
}
|
||||
.btn-price {
|
||||
font-family: OPPOSANS;
|
||||
|
||||
&::before {
|
||||
content: '¥';
|
||||
}
|
||||
}
|
||||
|
||||
.origin-price-btn {
|
||||
width: 236rpx;
|
||||
height: 80rpx;
|
||||
background: rgba(#ff5651, 0.1);
|
||||
color: #ff6000;
|
||||
border-radius: 40rpx 0px 0px 40rpx;
|
||||
line-height: normal;
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
.no-original {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.btn-title {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//秒杀卡片
|
||||
.seckill-box {
|
||||
background: v-bind(seckillBg) no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.groupon-box {
|
||||
background: v-bind(grouponBg) no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
//活动卡片
|
||||
.activity-box {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 10rpx;
|
||||
|
||||
.activity-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
line-height: 42rpx;
|
||||
|
||||
.activity-icon {
|
||||
width: 38rpx;
|
||||
height: 38rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.activity-go {
|
||||
width: 70rpx;
|
||||
height: 32rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
font-weight: 500;
|
||||
color: #ff6000;
|
||||
font-size: 24rpx;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.model-box {
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
200
pages/index/cart.vue
Normal file
200
pages/index/cart.vue
Normal file
@@ -0,0 +1,200 @@
|
||||
<template>
|
||||
<s-layout title="购物车" tabbar="/pages/index/cart" :bgStyle="{ color: '#fff' }">
|
||||
<s-empty v-if="state.list.length === 0" text="购物车空空如也,快去逛逛吧~" icon="/static/cart-empty.png" />
|
||||
|
||||
<!-- 头部 -->
|
||||
<view class="cart-box ss-flex ss-flex-col ss-row-between" v-if="state.list.length">
|
||||
<view class="cart-header ss-flex ss-col-center ss-row-between ss-p-x-30">
|
||||
<view class="header-left ss-flex ss-col-center ss-font-26">
|
||||
共
|
||||
<text class="goods-number ui-TC-Main ss-flex">{{ state.list.length }}</text>
|
||||
件商品
|
||||
</view>
|
||||
<view class="header-right">
|
||||
<button v-if="state.editMode" class="ss-reset-button" @tap="state.editMode = false">
|
||||
取消
|
||||
</button>
|
||||
<button v-else class="ss-reset-button ui-TC-Main" @tap="state.editMode = true">
|
||||
编辑
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 内容 -->
|
||||
<view class="cart-content ss-flex-1 ss-p-x-30 ss-m-b-40">
|
||||
<view class="goods-box ss-r-10 ss-m-b-14" v-for="item in state.list" :key="item.id">
|
||||
<view class="ss-flex ss-col-center">
|
||||
<label class="check-box ss-flex ss-col-center ss-p-l-10" @tap="onSelectSingle(item.id)">
|
||||
<radio :checked="state.selectedIds.includes(item.id)" color="var(--ui-BG-Main)"
|
||||
style="transform: scale(0.8)" @tap.stop="onSelectSingle(item.id)" />
|
||||
</label>
|
||||
<s-goods-item :title="item.spu.name" :img="item.spu.picUrl || item.goods.image"
|
||||
:price="item.sku.price/100"
|
||||
:skuText="item.sku.properties.length>1? item.sku.properties.reduce((items2,items)=>items2.valueName+' '+items.valueName):item.sku.properties[0].valueName"
|
||||
priceColor="#FF3000" :titleWidth="400">
|
||||
<template v-if="!state.editMode" v-slot:tool>
|
||||
<su-number-box :min="0" :max="item.sku.stock" :step="1" v-model="item.count"
|
||||
@change="onNumberChange($event, item)"></su-number-box>
|
||||
</template>
|
||||
</s-goods-item>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 底部 -->
|
||||
<su-fixed bottom :val="48" placeholder v-if="state.list.length > 0" :isInset="false">
|
||||
<view class="cart-footer ss-flex ss-col-center ss-row-between ss-p-x-30 border-bottom">
|
||||
<view class="footer-left ss-flex ss-col-center">
|
||||
<label class="check-box ss-flex ss-col-center ss-p-r-30" @tap="onSelectAll">
|
||||
<radio :checked="state.isAllSelected" color="var(--ui-BG-Main)"
|
||||
style="transform: scale(0.8)" @tap.stop="onSelectAll" />
|
||||
<view class="ss-m-l-8"> 全选 </view>
|
||||
</label>
|
||||
<text>合计:</text>
|
||||
<view class="text-price price-text">
|
||||
{{ state.totalPriceSelected }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="footer-right">
|
||||
<button v-if="state.editMode" class="ss-reset-button ui-BG-Main-Gradient pay-btn ui-Shadow-Main"
|
||||
@tap="onDelete">
|
||||
删除
|
||||
</button>
|
||||
<button v-else class="ss-reset-button ui-BG-Main-Gradient pay-btn ui-Shadow-Main"
|
||||
@tap="onConfirm">
|
||||
去结算
|
||||
{{ state.selectedIds?.length ? `(${state.selectedIds.length})` : '' }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</su-fixed>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import {
|
||||
computed,
|
||||
reactive,
|
||||
unref
|
||||
} from 'vue';
|
||||
|
||||
const sys_navBar = sheep.$platform.navbar;
|
||||
const cart = sheep.$store('cart');
|
||||
|
||||
const state = reactive({
|
||||
editMode: false,
|
||||
list: computed(() => cart.list),
|
||||
selectedList: [],
|
||||
selectedIds: computed(() => cart.selectedIds),
|
||||
isAllSelected: computed(() => cart.isAllSelected),
|
||||
totalPriceSelected: computed(() => cart.totalPriceSelected),
|
||||
});
|
||||
// 单选选中
|
||||
function onSelectSingle(id) {
|
||||
console.log('单选')
|
||||
cart.selectSingle(id);
|
||||
}
|
||||
// 全选
|
||||
function onSelectAll() {
|
||||
cart.selectAll(!state.isAllSelected);
|
||||
}
|
||||
|
||||
// 结算
|
||||
function onConfirm() {
|
||||
let items = []
|
||||
let goods_list = [];
|
||||
state.selectedList = state.list.filter((item) => state.selectedIds.includes(item.id));
|
||||
state.selectedList.map((item) => {
|
||||
console.log(item, '便利');
|
||||
// 此处前端做出修改
|
||||
items.push({
|
||||
skuId: item.sku.id,
|
||||
count: item.count,
|
||||
cartId: item.id,
|
||||
})
|
||||
goods_list.push({
|
||||
// goods_id: item.goods_id,
|
||||
goods_id: item.spu.id,
|
||||
// goods_num: item.goods_num,
|
||||
goods_num: item.count,
|
||||
// 商品价格id真没有
|
||||
// goods_sku_price_id: item.goods_sku_price_id,
|
||||
});
|
||||
});
|
||||
// return;
|
||||
if (goods_list.length === 0) {
|
||||
sheep.$helper.toast('请选择商品');
|
||||
return;
|
||||
}
|
||||
sheep.$router.go('/pages/order/confirm', {
|
||||
data: JSON.stringify({
|
||||
// order_type: 'goods',
|
||||
// goods_list,
|
||||
items,
|
||||
// from: 'cart',
|
||||
deliveryType: 1,
|
||||
pointStatus: false,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
function onNumberChange(e, cartItem) {
|
||||
if (e === 0) {
|
||||
cart.delete(cartItem.id);
|
||||
return;
|
||||
}
|
||||
if (cartItem.goods_num === e) return;
|
||||
cartItem.goods_num = e;
|
||||
cart.update({
|
||||
goods_id: cartItem.id,
|
||||
goods_num: e,
|
||||
goods_sku_price_id: cartItem.goods_sku_price_id,
|
||||
});
|
||||
}
|
||||
async function onDelete() {
|
||||
cart.delete(state.selectedIds);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.ui-fixed) {
|
||||
height: 72rpx;
|
||||
}
|
||||
|
||||
.cart-box {
|
||||
width: 100%;
|
||||
|
||||
.cart-header {
|
||||
height: 70rpx;
|
||||
background-color: #f6f6f6;
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: v-bind('sys_navBar') rpx;
|
||||
z-index: 1000;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.cart-footer {
|
||||
height: 100rpx;
|
||||
background-color: #fff;
|
||||
|
||||
.pay-btn {
|
||||
width: 180rpx;
|
||||
height: 70rpx;
|
||||
font-size: 28rpx;
|
||||
line-height: 28rpx;
|
||||
font-weight: 500;
|
||||
border-radius: 40rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.cart-content {
|
||||
margin-top: 70rpx;
|
||||
|
||||
.goods-box {
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
236
pages/index/category.vue
Normal file
236
pages/index/category.vue
Normal file
@@ -0,0 +1,236 @@
|
||||
<!-- 商品分类列表 -->
|
||||
<template>
|
||||
<s-layout title="分类" tabbar="/pages/index/category" :bgStyle="{ color: '#fff' }">
|
||||
<view class="s-category">
|
||||
<view class="three-level-wrap ss-flex ss-col-top" :style="[{ height: pageHeight + 'px' }]">
|
||||
<!-- 商品分类(左) -->
|
||||
<scroll-view class="side-menu-wrap" scroll-y :style="[{ height: pageHeight + 'px' }]">
|
||||
<view
|
||||
class="menu-item ss-flex"
|
||||
v-for="(item, index) in state.categoryList"
|
||||
:key="item.id"
|
||||
:class="[{ 'menu-item-active': index === state.activeMenu }]"
|
||||
@tap="onMenu(index)"
|
||||
>
|
||||
<view class="menu-title ss-line-1">
|
||||
{{ item.name }}
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<!-- 商品分类(右) -->
|
||||
<scroll-view
|
||||
class="goods-list-box"
|
||||
scroll-y
|
||||
:style="[{ height: pageHeight + 'px' }]"
|
||||
v-if="state.categoryList?.length"
|
||||
>
|
||||
<image
|
||||
v-if="state.categoryList[state.activeMenu].picUrl"
|
||||
class="banner-img"
|
||||
:src="sheep.$url.cdn(state.categoryList[state.activeMenu].picUrl)"
|
||||
mode="widthFix"
|
||||
/>
|
||||
<first-one v-if="state.style === 'first_one'" :pagination="state.pagination" />
|
||||
<first-two v-if="state.style === 'first_two'" :pagination="state.pagination" />
|
||||
<second-one
|
||||
v-if="state.style === 'second_one'"
|
||||
:data="state.categoryList"
|
||||
:activeMenu="state.activeMenu"
|
||||
/>
|
||||
<uni-load-more
|
||||
v-if="
|
||||
(state.style === 'first_one' || state.style === 'first_two') &&
|
||||
state.pagination.total > 0
|
||||
"
|
||||
:status="state.loadStatus"
|
||||
:content-text="{
|
||||
contentdown: '点击查看更多',
|
||||
}"
|
||||
@tap="loadMore"
|
||||
/>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import secondOne from './components/second-one.vue';
|
||||
import firstOne from './components/first-one.vue';
|
||||
import firstTwo from './components/first-two.vue';
|
||||
import sheep from '@/sheep';
|
||||
import CategoryApi from '@/sheep/api/product/category';
|
||||
import SpuApi from '@/sheep/api/product/spu';
|
||||
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
|
||||
import { computed, reactive } from 'vue';
|
||||
import _ from 'lodash';
|
||||
import { handleTree } from '@/sheep/util';
|
||||
|
||||
const state = reactive({
|
||||
style: 'second_one', // first_one(一级 - 样式一), first_two(二级 - 样式二), second_one(二级)
|
||||
categoryList: [], // 商品分类树
|
||||
activeMenu: 0, // 选中的一级菜单,在 categoryList 的下标
|
||||
|
||||
pagination: {
|
||||
// 商品分页
|
||||
list: [], // 商品列表
|
||||
total: [], // 商品总数
|
||||
pageNo: 1,
|
||||
pageSize: 6,
|
||||
},
|
||||
loadStatus: '',
|
||||
});
|
||||
|
||||
const { safeArea } = sheep.$platform.device;
|
||||
const pageHeight = computed(() => safeArea.height - 44 - 50);
|
||||
|
||||
// 加载商品分类
|
||||
async function getList() {
|
||||
const { code, data } = await CategoryApi.getCategoryList();
|
||||
if (code !== 0) {
|
||||
return;
|
||||
}
|
||||
state.categoryList = handleTree(data);
|
||||
}
|
||||
|
||||
// 选中菜单
|
||||
const onMenu = (val) => {
|
||||
state.activeMenu = val;
|
||||
if (state.style === 'first_one' || state.style === 'first_two') {
|
||||
state.pagination.pageNo = 1;
|
||||
state.pagination.list = [];
|
||||
state.pagination.total = 0;
|
||||
getGoodsList();
|
||||
}
|
||||
};
|
||||
|
||||
// 加载商品列表
|
||||
async function getGoodsList() {
|
||||
// 加载列表
|
||||
state.loadStatus = 'loading';
|
||||
const res = await SpuApi.getSpuPage({
|
||||
categoryId: state.categoryList[state.activeMenu].id,
|
||||
pageNo: state.pagination.pageNo,
|
||||
pageSize: state.pagination.pageSize,
|
||||
});
|
||||
if (res.code !== 0) {
|
||||
return;
|
||||
}
|
||||
// 合并列表
|
||||
state.pagination.list = _.concat(state.pagination.list, res.data.list);
|
||||
state.pagination.total = res.data.total;
|
||||
state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
|
||||
}
|
||||
|
||||
// 加载更多商品
|
||||
function loadMore() {
|
||||
if (state.loadStatus === 'noMore') {
|
||||
return;
|
||||
}
|
||||
state.pagination.pageNo++;
|
||||
getGoodsList();
|
||||
}
|
||||
|
||||
onLoad(async () => {
|
||||
await getList();
|
||||
// 如果是 first 风格,需要加载商品分页
|
||||
if (state.style === 'first_one' || state.style === 'first_two') {
|
||||
onMenu(0);
|
||||
}
|
||||
});
|
||||
|
||||
onReachBottom(() => {
|
||||
loadMore();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.s-category {
|
||||
:deep() {
|
||||
.side-menu-wrap {
|
||||
width: 200rpx;
|
||||
height: 100%;
|
||||
padding-left: 12rpx;
|
||||
background-color: #f6f6f6;
|
||||
|
||||
.menu-item {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
position: relative;
|
||||
transition: all linear 0.2s;
|
||||
|
||||
.menu-title {
|
||||
line-height: 32rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 400;
|
||||
color: #333;
|
||||
margin-left: 28rpx;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
width: 64rpx;
|
||||
height: 12rpx;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
var(--ui-BG-Main-gradient),
|
||||
var(--ui-BG-Main-light)
|
||||
) !important;
|
||||
position: absolute;
|
||||
left: -64rpx;
|
||||
bottom: 0;
|
||||
z-index: -1;
|
||||
transition: all linear 0.2s;
|
||||
}
|
||||
}
|
||||
|
||||
&.menu-item-active {
|
||||
background-color: #fff;
|
||||
border-radius: 20rpx 0 0 20rpx;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: -20rpx;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
background: radial-gradient(circle at 0 100%, transparent 20rpx, #fff 0);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -20rpx;
|
||||
right: 0;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
background: radial-gradient(circle at 0% 0%, transparent 20rpx, #fff 0);
|
||||
}
|
||||
|
||||
.menu-title {
|
||||
font-weight: 600;
|
||||
|
||||
&::before {
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.goods-list-box {
|
||||
background-color: #fff;
|
||||
width: calc(100vw - 100px);
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.banner-img {
|
||||
width: calc(100vw - 130px);
|
||||
border-radius: 5px;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
26
pages/index/components/first-one.vue
Normal file
26
pages/index/components/first-one.vue
Normal file
@@ -0,0 +1,26 @@
|
||||
<!-- 分类展示:first-one 风格 -->
|
||||
<template>
|
||||
<view class="ss-flex-col">
|
||||
<view class="goods-box" v-for="item in pagination.list" :key="item.id">
|
||||
<s-goods-column
|
||||
size="sl"
|
||||
:data="item"
|
||||
@click="sheep.$router.go('/pages/goods/index', { id: item.id })"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
|
||||
const props = defineProps({
|
||||
pagination: Object,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goods-box {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
66
pages/index/components/first-two.vue
Normal file
66
pages/index/components/first-two.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<!-- 分类展示:first-two 风格 -->
|
||||
<template>
|
||||
<view>
|
||||
<view class="ss-flex flex-wrap">
|
||||
<view class="goods-box" v-for="item in pagination?.list" :key="item.id">
|
||||
<view @click="sheep.$router.go('/pages/goods/index', { id: item.id })">
|
||||
<view class="goods-img">
|
||||
<image class="goods-img" :src="item.picUrl" mode="aspectFit" />
|
||||
</view>
|
||||
<view class="goods-content">
|
||||
<view class="goods-title ss-line-1 ss-m-b-28">{{ item.title }}</view>
|
||||
<view class="goods-price">¥{{ fen2yuan(item.price) }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { fen2yuan } from '@/sheep/hooks/useGoods';
|
||||
|
||||
const props = defineProps({
|
||||
pagination: Object,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goods-box {
|
||||
width: calc((100% - 20rpx) / 2);
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.goods-img {
|
||||
width: 100%;
|
||||
height: 246rpx;
|
||||
border-radius: 10rpx 10rpx 0px 0px;
|
||||
}
|
||||
|
||||
.goods-content {
|
||||
width: 100%;
|
||||
background: #ffffff;
|
||||
box-shadow: 0px 0px 20rpx 4rpx rgba(199, 199, 199, 0.22);
|
||||
padding: 20rpx 0 32rpx 16rpx;
|
||||
box-sizing: border-box;
|
||||
border-radius: 0 0 10rpx 10rpx;
|
||||
|
||||
.goods-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.goods-price {
|
||||
font-size: 24rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
color: #e1212b;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(2n + 1) {
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
80
pages/index/components/second-one.vue
Normal file
80
pages/index/components/second-one.vue
Normal file
@@ -0,0 +1,80 @@
|
||||
<!-- 分类展示:second-one 风格 -->
|
||||
<template>
|
||||
<view>
|
||||
<!-- 一级分类的名字 -->
|
||||
<view class="title-box ss-flex ss-col-center ss-row-center ss-p-b-30">
|
||||
<view class="title-line-left" />
|
||||
<view class="title-text ss-p-x-20">{{ props.data[activeMenu].name }}</view>
|
||||
<view class="title-line-right" />
|
||||
</view>
|
||||
<!-- 二级分类的名字 -->
|
||||
<view class="goods-item-box ss-flex ss-flex-wrap ss-p-b-20">
|
||||
<view
|
||||
class="goods-item"
|
||||
v-for="item in props.data[activeMenu].children"
|
||||
:key="item.id"
|
||||
@tap="
|
||||
sheep.$router.go('/pages/goods/list', {
|
||||
categoryId: item.id,
|
||||
})
|
||||
"
|
||||
>
|
||||
<image class="goods-img" :src="item.picUrl" mode="aspectFill" />
|
||||
<view class="ss-p-10">
|
||||
<view class="goods-title ss-line-1">{{ item.name }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
activeMenu: [Number, String],
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.title-box {
|
||||
.title-line-left,
|
||||
.title-line-right {
|
||||
width: 15px;
|
||||
height: 1px;
|
||||
background: #d2d2d2;
|
||||
}
|
||||
}
|
||||
|
||||
.goods-item {
|
||||
width: calc((100% - 20px) / 3);
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&:nth-of-type(3n) {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.goods-img {
|
||||
width: calc((100vw - 140px) / 3);
|
||||
height: calc((100vw - 140px) / 3);
|
||||
}
|
||||
|
||||
.goods-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
line-height: 40rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.goods-price {
|
||||
color: $red;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
90
pages/index/index.vue
Normal file
90
pages/index/index.vue
Normal file
@@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<view v-if="template">
|
||||
<s-layout title="首页" navbar="custom" tabbar="/pages/index/index" :bgStyle="template.page"
|
||||
:navbarStyle="template.style?.navbar" onShareAppMessage>
|
||||
<s-block v-for="(item, index) in template.components" :key="index" :styles="item.property.style">
|
||||
<s-block-item :type="item.id" :data="item.property" :styles="item.property.style" />
|
||||
</s-block>
|
||||
</s-layout>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
computed
|
||||
} from 'vue';
|
||||
import {
|
||||
onLoad,
|
||||
onPageScroll,
|
||||
onPullDownRefresh
|
||||
} from '@dcloudio/uni-app';
|
||||
import sheep from '@/sheep';
|
||||
import $share from '@/sheep/platform/share';
|
||||
import index2Api from '@/sheep/api/index2';
|
||||
// 隐藏原生tabBar
|
||||
uni.hideTabBar();
|
||||
|
||||
const template = computed(() => sheep.$store('app').template?.home);
|
||||
// 在此处拦截改变一下首页轮播图 此处先写死后期复活 放到启动函数里
|
||||
// (async function() {
|
||||
// console.log('原代码首页定制化数据',template)
|
||||
// let {
|
||||
// data
|
||||
// } = await index2Api.decorate();
|
||||
// console.log('首页导航配置化过高无法兼容',JSON.parse(data[1].value))
|
||||
// 改变首页底部数据 但是没有通过数组id获取商品数据接口
|
||||
// let {
|
||||
// data: datas
|
||||
// } = await index2Api.spids();
|
||||
// template.value.data[9].data.goodsIds = datas.list.map(item => item.id);
|
||||
// template.value.data[0].data.list = JSON.parse(data[0].value).map(item => {
|
||||
// return {
|
||||
// src: item.picUrl,
|
||||
// url: item.url,
|
||||
// title: item.name,
|
||||
// type: "image"
|
||||
// }
|
||||
// })
|
||||
// }())
|
||||
|
||||
|
||||
onLoad((options) => {
|
||||
// #ifdef MP
|
||||
// 小程序识别二维码
|
||||
if (options.scene) {
|
||||
const sceneParams = decodeURIComponent(options.scene).split('=');
|
||||
options[sceneParams[0]] = sceneParams[1];
|
||||
}
|
||||
// #endif
|
||||
|
||||
// 预览模板
|
||||
if (options.templateId) {
|
||||
sheep.$store('app').init(options.templateId);
|
||||
}
|
||||
|
||||
// 解析分享信息
|
||||
if (options.spm) {
|
||||
$share.decryptSpm(options.spm);
|
||||
}
|
||||
|
||||
// 进入指定页面(完整页面路径)
|
||||
if (options.page) {
|
||||
sheep.$router.go(decodeURIComponent(options.page));
|
||||
}
|
||||
|
||||
// TODO 芋艿:测试接口的调用
|
||||
sheep.$api.app.test();
|
||||
});
|
||||
|
||||
// 下拉刷新
|
||||
onPullDownRefresh(() => {
|
||||
sheep.$store('app').init();
|
||||
setTimeout(function() {
|
||||
uni.stopPullDownRefresh();
|
||||
}, 800);
|
||||
});
|
||||
|
||||
onPageScroll(() => {});
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
39
pages/index/login.vue
Normal file
39
pages/index/login.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<!-- 空登陆页 -->
|
||||
<view></view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { isEmpty } from 'lodash';
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||
|
||||
onLoad(async (options) => {
|
||||
// #ifdef H5
|
||||
let event = '';
|
||||
if (options.login_code) {
|
||||
event = 'login';
|
||||
const { error } = await sheep.$platform.useProvider().login(options.login_code);
|
||||
if (error === 0) {
|
||||
sheep.$store('user').getInfo();
|
||||
}
|
||||
}
|
||||
if (options.bind_code) {
|
||||
event = 'bind';
|
||||
const { error } = await sheep.$platform.useProvider().bind(options.bind_code);
|
||||
}
|
||||
|
||||
// 检测H5登录回调
|
||||
let returnUrl = uni.getStorageSync('returnUrl');
|
||||
if (returnUrl) {
|
||||
uni.removeStorage('returnUrl');
|
||||
location.replace(returnUrl);
|
||||
} else {
|
||||
uni.switchTab({
|
||||
url: '/',
|
||||
});
|
||||
}
|
||||
|
||||
// #endif
|
||||
});
|
||||
</script>
|
||||
52
pages/index/page.vue
Normal file
52
pages/index/page.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<s-layout
|
||||
:title="page.name"
|
||||
navbar="custom"
|
||||
:bgStyle="page.style?.background"
|
||||
:navbarStyle="page.style?.navbar"
|
||||
onShareAppMessage
|
||||
showLeftButton
|
||||
>
|
||||
<s-block v-for="(item, index) in page.list" :key="index" :styles="item.style">
|
||||
<s-block-item :type="item.type" :data="item.data" :styles="item.style" />
|
||||
</s-block>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, reactive } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad, onPageScroll } from '@dcloudio/uni-app';
|
||||
|
||||
const page = reactive({
|
||||
name: '',
|
||||
list: [],
|
||||
style: {},
|
||||
});
|
||||
onLoad(async (options) => {
|
||||
let id;
|
||||
|
||||
if (options.id) {
|
||||
id = options.id;
|
||||
}
|
||||
|
||||
// #ifdef MP
|
||||
// 小程序预览自定义页面
|
||||
if (options.scene) {
|
||||
const sceneParams = decodeURIComponent(options.scene).split('=');
|
||||
id = sceneParams[1];
|
||||
}
|
||||
// #endif
|
||||
|
||||
const { error, data } = await sheep.$api.app.page(id);
|
||||
if (error === 0) {
|
||||
page.name = data.name;
|
||||
page.list = data.diypage?.page?.data;
|
||||
page.style = data.diypage?.page?.style;
|
||||
}
|
||||
});
|
||||
|
||||
onPageScroll(() => {});
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
113
pages/index/search.vue
Normal file
113
pages/index/search.vue
Normal file
@@ -0,0 +1,113 @@
|
||||
<template>
|
||||
<s-layout class="set-wrap" title="搜索" :bgStyle="{ color: '#FFF' }">
|
||||
<view class="ss-p-x-24">
|
||||
<view class="ss-flex ss-col-center">
|
||||
<uni-search-bar
|
||||
class="ss-flex-1"
|
||||
radius="33"
|
||||
placeholder="请输入关键字"
|
||||
cancelButton="none"
|
||||
:focus="true"
|
||||
@confirm="onSearch($event.value)"
|
||||
/>
|
||||
</view>
|
||||
<view class="ss-flex ss-row-between ss-col-center">
|
||||
<view class="serach-history">搜索历史</view>
|
||||
<button class="clean-history ss-reset-button" @tap="onDelete"> 清除搜索历史 </button>
|
||||
</view>
|
||||
<view class="ss-flex ss-col-center ss-row-left ss-flex-wrap">
|
||||
<button
|
||||
class="history-btn ss-reset-button"
|
||||
@tap="onSearch(item)"
|
||||
v-for="(item, index) in state.historyList"
|
||||
:key="index"
|
||||
>
|
||||
{{ item }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
const state = reactive({
|
||||
historyList: [],
|
||||
});
|
||||
|
||||
// 搜索
|
||||
function onSearch(keyword) {
|
||||
if (!keyword) return;
|
||||
saveSearchHistory(keyword);
|
||||
sheep.$router.go('/pages/goods/list', { keyword });
|
||||
}
|
||||
|
||||
// 保存搜索历史
|
||||
function saveSearchHistory(keyword) {
|
||||
// 如果关键词在搜索历史中,则把此关键词先移除
|
||||
if (state.historyList.includes(keyword)) {
|
||||
state.historyList.splice(state.historyList.indexOf(keyword), 1);
|
||||
}
|
||||
// 置顶关键词
|
||||
state.historyList.unshift(keyword);
|
||||
|
||||
// 最多保留10条记录
|
||||
if (state.historyList.length >= 10) {
|
||||
state.historyList.length = 10;
|
||||
}
|
||||
uni.setStorageSync('searchHistory', state.historyList);
|
||||
}
|
||||
|
||||
function onDelete() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确认清除搜索历史吗?',
|
||||
success: function (res) {
|
||||
if (res.confirm) {
|
||||
state.historyTag = [];
|
||||
uni.removeStorageSync('searchHistory');
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
onLoad(() => {
|
||||
state.historyList = uni.getStorageSync('searchHistory') || [];
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.serach-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.uni-searchbar {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.serach-history {
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.clean-history {
|
||||
font-weight: 500;
|
||||
color: #999999;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.history-btn {
|
||||
padding: 0 38rpx;
|
||||
height: 60rpx;
|
||||
background: #f5f6f8;
|
||||
border-radius: 30rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
max-width: 690rpx;
|
||||
margin: 0 20rpx 20rpx 0;
|
||||
}
|
||||
</style>
|
||||
41
pages/index/user.vue
Normal file
41
pages/index/user.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<s-layout
|
||||
title="我的"
|
||||
tabbar="/pages/index/user"
|
||||
navbar="custom"
|
||||
:bgStyle="template.page"
|
||||
:navbarStyle="template.style?.navbar"
|
||||
onShareAppMessage
|
||||
>
|
||||
<s-block v-for="(item, index) in template.components" :key="index" :styles="item.property.style">
|
||||
<s-block-item :type="item.id" :data="item.property" :styles="item.property.style" />
|
||||
</s-block>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { onShow, onPageScroll, onPullDownRefresh } from '@dcloudio/uni-app';
|
||||
import sheep from '@/sheep';
|
||||
|
||||
// 隐藏原生tabBar
|
||||
uni.hideTabBar();
|
||||
|
||||
const template = computed(() => sheep.$store('app').template.user);
|
||||
const isLogin = computed(() => sheep.$store('user').isLogin);
|
||||
|
||||
onShow(() => {
|
||||
sheep.$store('user').updateUserData();
|
||||
});
|
||||
|
||||
onPullDownRefresh(() => {
|
||||
sheep.$store('user').updateUserData();
|
||||
setTimeout(function () {
|
||||
uni.stopPullDownRefresh();
|
||||
}, 800);
|
||||
});
|
||||
|
||||
onPageScroll(() => {});
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
318
pages/order/aftersale/apply.vue
Normal file
318
pages/order/aftersale/apply.vue
Normal file
@@ -0,0 +1,318 @@
|
||||
<!-- 订单详情 -->
|
||||
<template>
|
||||
<s-layout title="申请售后">
|
||||
<!-- 售后商品 -->
|
||||
<view class="goods-box">
|
||||
<s-goods-item :img="state.goodsItem.goods_image" :title="state.goodsItem.goods_title"
|
||||
:skuText="state.goodsItem.goods_sku_text" :price="state.goodsItem.goods_price"
|
||||
:num="state.goodsItem.goods_num"></s-goods-item>
|
||||
</view>
|
||||
|
||||
<uni-forms ref="form" v-model="formData" :rules="rules" label-position="top">
|
||||
<!-- 售后类型 -->
|
||||
<view class="refund-item">
|
||||
<view class="item-title ss-m-b-20">售后类型</view>
|
||||
<view class="ss-flex-col">
|
||||
<radio-group @change="onRefundChange">
|
||||
<label class="ss-flex ss-col-center ss-p-y-10" v-for="(item, index) in state.refundTypeList" :key="index">
|
||||
<radio :checked="formData.type === item.value" color="var(--ui-BG-Main)" style="transform: scale(0.8)"
|
||||
:value="item.value" />
|
||||
<view class="item-value ss-m-l-8">{{ item.text }}</view>
|
||||
</label>
|
||||
</radio-group>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 申请原因 -->
|
||||
<view class="refund-item ss-flex ss-col-center ss-row-between" @tap="state.showModal = true">
|
||||
<text class="item-title">申请原因</text>
|
||||
<view class="ss-flex refund-cause ss-col-center">
|
||||
<text class="ss-m-r-20" v-if="formData.reason">{{ formData.reason }}</text>
|
||||
<text class="ss-m-r-20" v-else>请选择申请原因~</text>
|
||||
<!-- <text class="ss-iconfont _icon-forward" style="color: #666"></text> -->
|
||||
<text class="cicon-forward" style="height: 28rpx"></text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="refund-item u-m-b-20">
|
||||
<view class="item-title ss-m-b-20">联系方式</view>
|
||||
<view class="input-box u-flex">
|
||||
<uni-easyinput :inputBorder="false" type="number" v-model="formData.mobile" placeholder="请输入您的联系电话"
|
||||
paddingLeft="10" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 留言 -->
|
||||
<view class="refund-item">
|
||||
<view class="item-title ss-m-b-20">相关描述</view>
|
||||
<view class="describe-box">
|
||||
<uni-easyinput :inputBorder="false" class="describe-content" type="textarea" maxlength="120" autoHeight
|
||||
v-model="formData.content" placeholder="客官~请描述您遇到的问题,建议上传照片"></uni-easyinput>
|
||||
<view class="upload-img">
|
||||
<s-uploader v-model:url="formData.images" fileMediatype="image" limit="9" mode="grid"
|
||||
:imageStyles="{ width: '168rpx', height: '168rpx' }" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</uni-forms>
|
||||
<!-- 底部按钮 -->
|
||||
<su-fixed bottom placeholder>
|
||||
<view class="foot-wrap">
|
||||
<view class="foot_box ss-flex ss-col-center ss-row-between ss-p-x-30">
|
||||
<button class="ss-reset-button contcat-btn" @tap="sheep.$router.go('/pages/chat/index')">联系客服</button>
|
||||
<button class="ss-reset-button ui-BG-Main-Gradient sub-btn" @tap="submit">提交</button>
|
||||
</view>
|
||||
</view>
|
||||
</su-fixed>
|
||||
<!-- 申请原因弹窗 -->
|
||||
|
||||
<su-popup :show="state.showModal" round="10" :showClose="true" @close="state.showModal = false">
|
||||
<view class="modal-box page_box">
|
||||
<view class="modal-head item-title head_box ss-flex ss-row-center ss-col-center">申请原因</view>
|
||||
<view class="modal-content content_box">
|
||||
<radio-group @change="onChange">
|
||||
<label class="radio ss-flex ss-col-center" v-for="item in state.refundReasonList" :key="item.value">
|
||||
<view class="ss-flex-1 ss-p-20">{{ item.title }}</view>
|
||||
<radio :value="item.value" color="var(--ui-BG-Main)" :checked="item.value === state.currentValue" />
|
||||
</label>
|
||||
</radio-group>
|
||||
</view>
|
||||
<view class="modal-foot foot_box ss-flex ss-row-center ss-col-center">
|
||||
<button class="ss-reset-button close-btn ui-BG-Main-Gradient" @tap="onReason">确定</button>
|
||||
</view>
|
||||
</view>
|
||||
</su-popup>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { reactive, ref, unref } from 'vue';
|
||||
const form = ref(null);
|
||||
const state = reactive({
|
||||
showModal: false,
|
||||
currentValue: 0,
|
||||
goodsItem: {},
|
||||
reasonText: '',
|
||||
//售后类型
|
||||
refundTypeList: [
|
||||
{
|
||||
text: '仅退款',
|
||||
value: 'refund',
|
||||
},
|
||||
{
|
||||
text: '退/换货',
|
||||
value: 'return',
|
||||
},
|
||||
{
|
||||
text: '其他',
|
||||
value: 'other',
|
||||
},
|
||||
],
|
||||
refundReasonList: [
|
||||
{
|
||||
value: '1',
|
||||
title: '卖家发错货了',
|
||||
},
|
||||
{
|
||||
value: '2',
|
||||
title: '退运费',
|
||||
},
|
||||
{
|
||||
value: '3',
|
||||
title: '大小/重量与商品描述不符',
|
||||
},
|
||||
{
|
||||
value: '4',
|
||||
title: '生产日期/保质期与商品描述不符',
|
||||
},
|
||||
{
|
||||
value: '5',
|
||||
title: '质量问题',
|
||||
},
|
||||
{
|
||||
value: '6',
|
||||
title: '我不想要了',
|
||||
},
|
||||
],
|
||||
});
|
||||
const formData = reactive({
|
||||
type: '',
|
||||
reason: '',
|
||||
mobile: '',
|
||||
content: '',
|
||||
images: [],
|
||||
});
|
||||
const rules = reactive({});
|
||||
|
||||
// 提交表单
|
||||
async function submit() {
|
||||
// #ifdef MP
|
||||
sheep.$platform.useProvider('wechat').subscribeMessage('order_aftersale_change');
|
||||
// #endif
|
||||
let data = {
|
||||
...formData,
|
||||
order_id: state.goodsItem.order_id,
|
||||
order_item_id: state.goodsItem.id,
|
||||
};
|
||||
const res = await sheep.$api.order.aftersale.apply(data);
|
||||
if (res.error === 0) {
|
||||
uni.showToast({
|
||||
title: res.msg,
|
||||
});
|
||||
sheep.$router.go('/pages/order/aftersale/list');
|
||||
}
|
||||
}
|
||||
|
||||
//选择售后类型
|
||||
function onRefundChange(e) {
|
||||
formData.type = e.detail.value;
|
||||
}
|
||||
|
||||
//选择申请原因
|
||||
function onChange(e) {
|
||||
state.currentValue = e.detail.value;
|
||||
state.refundReasonList.forEach((item) => {
|
||||
if (item.value === e.detail.value) {
|
||||
state.reasonText = item.title;
|
||||
}
|
||||
});
|
||||
}
|
||||
//确定
|
||||
function onReason() {
|
||||
formData.reason = state.reasonText;
|
||||
state.showModal = false;
|
||||
}
|
||||
|
||||
function onTitle(e, title) {
|
||||
state.currentValue = e;
|
||||
state.reasonText = title;
|
||||
}
|
||||
onLoad((options) => {
|
||||
state.goodsItem = JSON.parse(options.item);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.item-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
color: rgba(51, 51, 51, 1);
|
||||
// margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
// 售后项目
|
||||
.refund-item {
|
||||
background-color: #fff;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
padding: 30rpx;
|
||||
|
||||
&:last-child {
|
||||
border: none;
|
||||
}
|
||||
|
||||
// 留言
|
||||
.describe-box {
|
||||
width: 690rpx;
|
||||
background: rgba(249, 250, 251, 1);
|
||||
padding: 30rpx;
|
||||
box-sizing: border-box;
|
||||
border-radius: 20rpx;
|
||||
|
||||
.describe-content {
|
||||
height: 200rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
// 联系方式
|
||||
.input-box {
|
||||
height: 84rpx;
|
||||
background: rgba(249, 250, 251, 1);
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.goods-box {
|
||||
background: #fff;
|
||||
padding: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.foot-wrap {
|
||||
height: 100rpx;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.foot_box {
|
||||
height: 100rpx;
|
||||
background-color: #fff;
|
||||
|
||||
.sub-btn {
|
||||
width: 336rpx;
|
||||
line-height: 74rpx;
|
||||
border-radius: 38rpx;
|
||||
color: rgba(#fff, 0.9);
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.contcat-btn {
|
||||
width: 336rpx;
|
||||
line-height: 74rpx;
|
||||
background: rgba(238, 238, 238, 1);
|
||||
border-radius: 38rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 400;
|
||||
color: rgba(51, 51, 51, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.modal-box {
|
||||
width: 750rpx;
|
||||
// height: 680rpx;
|
||||
border-radius: 30rpx 30rpx 0 0;
|
||||
background: #fff;
|
||||
|
||||
.modal-head {
|
||||
height: 100rpx;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.modal-foot {
|
||||
.close-btn {
|
||||
width: 710rpx;
|
||||
line-height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
color: rgba(#fff, 0.9);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.success-box {
|
||||
width: 600rpx;
|
||||
padding: 90rpx 0 64rpx 0;
|
||||
|
||||
.cicon-check-round {
|
||||
font-size: 96rpx;
|
||||
color: #04b750;
|
||||
}
|
||||
|
||||
.success-title {
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.success-btn {
|
||||
width: 492rpx;
|
||||
height: 70rpx;
|
||||
background: linear-gradient(90deg, var(--ui-BG-Main-gradient), var(--ui-BG-Main));
|
||||
border-radius: 35rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
355
pages/order/aftersale/detail.vue
Normal file
355
pages/order/aftersale/detail.vue
Normal file
@@ -0,0 +1,355 @@
|
||||
<!-- 售后详情 -->
|
||||
<template>
|
||||
<s-layout title="售后详情" :navbar="!isEmpty(state.info) && state.loading ? 'inner' : 'normal'">
|
||||
<view class="content_box" v-if="!isEmpty(state.info) && state.loading">
|
||||
<!-- 步骤条 -->
|
||||
<!-- 这个没找到替换方案 -->
|
||||
<view class="steps-box ss-flex" :style="[
|
||||
{
|
||||
marginTop: '-' + Number(statusBarHeight + 88) + 'rpx',
|
||||
paddingTop: Number(statusBarHeight + 88) + 'rpx',
|
||||
},
|
||||
]">
|
||||
<!-- <uni-steps :options="state.list" :active="state.active" active-color="#fff" /> -->
|
||||
<view class="ss-flex">
|
||||
<view class="steps-item" v-for="(item, index) in state.list" :key="index">
|
||||
<view class="ss-flex">
|
||||
<text class="sicon-circleclose" v-if="
|
||||
(state.list.length - 1 == index && state.info.aftersale_status === -2) ||
|
||||
(state.list.length - 1 == index && state.info.aftersale_status === -1)
|
||||
"></text>
|
||||
<text class="sicon-circlecheck" v-else
|
||||
:class="state.active >= index ? 'activity-color' : 'info-color'"></text>
|
||||
|
||||
<view v-if="state.list.length - 1 != index" class="line"
|
||||
:class="state.active >= index ? 'activity-bg' : 'info-bg'"></view>
|
||||
</view>
|
||||
<view class="steps-item-title" :class="state.active >= index ? 'activity-color' : 'info-color'">
|
||||
{{ item.title }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 服务状态 -->
|
||||
<!-- <view class="status-box ss-flex ss-col-center ss-row-between ss-m-x-20"
|
||||
@tap="sheep.$router.go('/pages/order/aftersale/log', { id: state.aftersaleId })">
|
||||
<view class="">
|
||||
<view class="status-text">{{ state.info.aftersale_status_desc }}</view>
|
||||
<view class="status-time">{{ state.info.update_time }}</view>
|
||||
</view>
|
||||
<text class="ss-iconfont _icon-forward" style="color: #666"></text>
|
||||
</view> -->
|
||||
|
||||
<!-- 退款金额 -->
|
||||
<view class="aftersale-money ss-flex ss-col-center ss-row-between">
|
||||
<view class="aftersale-money--title">退款总额</view>
|
||||
<view class="aftersale-money--num">¥{{ state.info.refundPrice/100 }}</view>
|
||||
</view>
|
||||
<!-- 服务商品 -->
|
||||
<view class="order-shop">
|
||||
<!-- <s-goods-item :title="state.info.goods_title" :price="state.info.goods_price"
|
||||
:img="state.info.goods_image" priceColor="#333333" :titleWidth="480"
|
||||
:skuText="state.info.goods_sku_text" :num="state.info.goods_num"></s-goods-item> -->
|
||||
<s-goods-item :img=" state.info.picUrl" :title=" state.info.spuName" priceColor="#333333"
|
||||
:titleWidth="480" :skuText=" state.info.properties.reduce((a,b)=>a+b.valueName+' ','')"
|
||||
:price=" state.info.refundPrice/100" :num=" state.info.count"></s-goods-item>
|
||||
</view>
|
||||
|
||||
<!-- 服务内容 -->
|
||||
<view class="aftersale-content">
|
||||
<view class="aftersale-item ss-flex ss-col-center">
|
||||
<view class="item-title">服务单号:</view>
|
||||
<view class="item-content ss-m-r-16">{{ state.info.no }}</view>
|
||||
<button class="ss-reset-button copy-btn" @tap="onCopy">复制</button>
|
||||
</view>
|
||||
<view class="aftersale-item ss-flex ss-col-center">
|
||||
<view class="item-title">申请时间:</view>
|
||||
<view class="item-content">
|
||||
{{ sheep.$helper.timeFormat(state.info.createTime, 'yyyy-mm-dd hh:MM:ss') }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="aftersale-item ss-flex ss-col-center">
|
||||
<view class="item-title">售后类型:</view>
|
||||
<view class="item-content">{{ status2[state.info.way] }}</view>
|
||||
</view>
|
||||
<view class="aftersale-item ss-flex ss-col-center">
|
||||
<view class="item-title">申请原因:</view>
|
||||
<view class="item-content">{{ state.info.applyReason }}</view>
|
||||
</view>
|
||||
<view class="aftersale-item ss-flex ss-col-center">
|
||||
<view class="item-title">相关描述:</view>
|
||||
<view class="item-content">{{ state.info.applyDescription }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<s-empty v-if="isEmpty(state.info) && state.loading" icon="/static/order-empty.png" text="暂无该订单售后详情" />
|
||||
<!-- <su-fixed bottom placeholder bg="bg-white" v-if="!isEmpty(state.info)">
|
||||
<view class="foot_box">
|
||||
<button class="ss-reset-button btn" v-if="state.info.btns?.includes('cancel')"
|
||||
@tap="onApply(state.info.id)">取消申请</button>
|
||||
<button class="ss-reset-button btn" v-if="state.info.btns?.includes('delete')"
|
||||
@tap="onDelete(state.info.id)">删除</button>
|
||||
<button class="ss-reset-button contcat-btn btn"
|
||||
@tap="sheep.$router.go('/pages/chat/index')">联系客服</button>
|
||||
</view>
|
||||
</su-fixed> -->
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import {
|
||||
onLoad
|
||||
} from '@dcloudio/uni-app';
|
||||
import {
|
||||
reactive
|
||||
} from 'vue';
|
||||
import {
|
||||
isEmpty
|
||||
} from 'lodash';
|
||||
|
||||
const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
|
||||
const headerBg = sheep.$url.css('/static/img/shop/order/order_bg.png');
|
||||
const state = reactive({
|
||||
active: 0,
|
||||
aftersaleId: 0,
|
||||
info: {},
|
||||
list: [{
|
||||
title: '提交申请',
|
||||
},
|
||||
{
|
||||
title: '处理中',
|
||||
},
|
||||
],
|
||||
loading: false,
|
||||
});
|
||||
|
||||
const status2 = {
|
||||
10: '仅退款',
|
||||
20: '退货退款'
|
||||
}
|
||||
|
||||
function onApply(orderId) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要取消此申请吗?',
|
||||
success: async function(res) {
|
||||
if (res.confirm) {
|
||||
const {
|
||||
error
|
||||
} = await sheep.$api.order.aftersale.cancel(orderId);
|
||||
if (error === 0) {
|
||||
getDetail(state.aftersaleId);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function onDelete(orderId) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要删除吗?',
|
||||
success: async function(res) {
|
||||
if (res.confirm) {
|
||||
const {
|
||||
error
|
||||
} = await sheep.$api.order.aftersale.delete(orderId);
|
||||
if (error === 0) {
|
||||
sheep.$router.back();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
const onCopy = () => {
|
||||
sheep.$helper.copyText(state.info.aftersale_sn);
|
||||
};
|
||||
async function getDetail(id) {
|
||||
const {
|
||||
code,
|
||||
data
|
||||
} = await sheep.$api.order.aftersale.detail(id);
|
||||
state.loading = true;
|
||||
if (code === 0) {
|
||||
state.info = data;
|
||||
if (state.info.aftersale_status === -2 || state.info.aftersale_status === -1) {
|
||||
state.list.push({
|
||||
title: state.info.aftersale_status_text
|
||||
});
|
||||
state.active = 2;
|
||||
} else {
|
||||
state.list.push({
|
||||
title: '完成'
|
||||
});
|
||||
state.active = state.info.aftersale_status;
|
||||
}
|
||||
} else {
|
||||
state.info = null;
|
||||
}
|
||||
}
|
||||
onLoad((options) => {
|
||||
state.aftersaleId = options.id;
|
||||
getDetail(options.id);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// 步骤条
|
||||
.steps-box {
|
||||
width: 100%;
|
||||
height: 190rpx;
|
||||
background: v-bind(headerBg) no-repeat,
|
||||
linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
background-size: 750rpx 100%;
|
||||
padding-left: 72rpx;
|
||||
|
||||
.steps-item {
|
||||
.sicon-circleclose {
|
||||
font-size: 24rpx;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.sicon-circlecheck {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.steps-item-title {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
margin-top: 16rpx;
|
||||
margin-left: -36rpx;
|
||||
width: 100rpx;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.activity-color {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.info-color {
|
||||
color: rgba(#fff, 0.4);
|
||||
}
|
||||
|
||||
.activity-bg {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.info-bg {
|
||||
background: rgba(#fff, 0.4);
|
||||
}
|
||||
|
||||
.line {
|
||||
width: 270rpx;
|
||||
height: 4rpx;
|
||||
}
|
||||
|
||||
// 服务状态
|
||||
.status-box {
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
background-color: #fff;
|
||||
border-radius: 20rpx 20rpx 0px 0px;
|
||||
padding: 20rpx;
|
||||
margin-top: -20rpx;
|
||||
|
||||
.status-text {
|
||||
font-size: 28rpx;
|
||||
|
||||
font-weight: 500;
|
||||
color: rgba(51, 51, 51, 1);
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.status-time {
|
||||
font-size: 24rpx;
|
||||
|
||||
font-weight: 400;
|
||||
color: rgba(153, 153, 153, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 退款金额
|
||||
.aftersale-money {
|
||||
background-color: #fff;
|
||||
height: 98rpx;
|
||||
padding: 0 20rpx;
|
||||
margin: 20rpx;
|
||||
|
||||
.aftersale-money--title {
|
||||
font-size: 28rpx;
|
||||
|
||||
font-weight: 500;
|
||||
color: rgba(51, 51, 51, 1);
|
||||
}
|
||||
|
||||
.aftersale-money--num {
|
||||
font-size: 28rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
color: #ff3000;
|
||||
}
|
||||
}
|
||||
|
||||
// order-shop
|
||||
.order-shop {
|
||||
padding: 20rpx;
|
||||
background-color: #fff;
|
||||
margin: 0 20rpx 20rpx 20rpx;
|
||||
}
|
||||
|
||||
// 服务内容
|
||||
.aftersale-content {
|
||||
background-color: #fff;
|
||||
padding: 20rpx;
|
||||
margin: 0 20rpx;
|
||||
|
||||
.aftersale-item {
|
||||
height: 60rpx;
|
||||
|
||||
.copy-btn {
|
||||
background: #eeeeee;
|
||||
color: #333;
|
||||
border-radius: 20rpx;
|
||||
width: 75rpx;
|
||||
height: 40rpx;
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
color: #999;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.item-content {
|
||||
color: #333;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 底部功能
|
||||
.foot_box {
|
||||
height: 100rpx;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
|
||||
.btn {
|
||||
width: 160rpx;
|
||||
line-height: 60rpx;
|
||||
background: rgba(238, 238, 238, 1);
|
||||
border-radius: 30rpx;
|
||||
padding: 0;
|
||||
margin-right: 20rpx;
|
||||
font-size: 26rpx;
|
||||
|
||||
font-weight: 400;
|
||||
color: rgba(51, 51, 51, 1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
238
pages/order/aftersale/list.vue
Normal file
238
pages/order/aftersale/list.vue
Normal file
@@ -0,0 +1,238 @@
|
||||
<!-- 售后列表 -->
|
||||
<template>
|
||||
<s-layout title="售后列表">
|
||||
<!-- tab -->
|
||||
<su-sticky bgColor="#fff">
|
||||
<su-tabs :list="tabMaps" :scrollable="false" @change="onTabsChange" :current="state.currentTab"></su-tabs>
|
||||
</su-sticky>
|
||||
<s-empty v-if="state.pagination.total === 0" icon="/static/data-empty.png" text="暂无数据">
|
||||
</s-empty>
|
||||
<!-- 列表 -->
|
||||
<view v-if="state.pagination.total > 0">
|
||||
<view class="list-box ss-m-y-20" v-for="order in state.pagination.data" :key="order.id"
|
||||
@tap="sheep.$router.go('/pages/order/aftersale/detail', { id: order.id })">
|
||||
<view class="order-head ss-flex ss-col-center ss-row-between">
|
||||
<text class="no">服务单号:{{ order.no }}</text>
|
||||
<text class="state">{{ status[order.status] }}</text>
|
||||
</view>
|
||||
<s-goods-item :img="order.picUrl" :title="order.spuName"
|
||||
:skuText="order.properties.reduce((a,b)=>a+b.valueName+' ','')" :price="order.refundPrice/100"
|
||||
:num="order.count"></s-goods-item>
|
||||
<view class="apply-box ss-flex ss-col-center ss-row-between border-bottom ss-p-x-20">
|
||||
<view class="ss-flex ss-col-center">
|
||||
<!-- 此处需修改 -->
|
||||
<view class="title ss-m-r-20">{{ status2[order.way] }}</view>
|
||||
<!-- <view class="value">{{ order.aftersale_status_desc }}</view> -->
|
||||
<view class="value">{{ order.applyReason }}</view>
|
||||
</view>
|
||||
<text class="_icon-forward"></text>
|
||||
</view>
|
||||
<!-- <view class="tool-btn-box ss-flex ss-col-center ss-row-right ss-p-r-20">
|
||||
<view>
|
||||
<button class="ss-reset-button tool-btn" @tap.stop="onApply(order.id)"
|
||||
v-if="order.btns.includes('cancel')">取消申请</button>
|
||||
</view>
|
||||
<view>
|
||||
<button class="ss-reset-button tool-btn" @tap.stop="onDelete(order.id)"
|
||||
v-if="order.btns.includes('delete')">删除</button>
|
||||
</view>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
<uni-load-more v-if="state.pagination.total > 0" :status="state.loadStatus" :content-text="{
|
||||
contentdown: '上拉加载更多',
|
||||
}" @tap="loadmore" />
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import {
|
||||
onLoad,
|
||||
onReachBottom
|
||||
} from '@dcloudio/uni-app';
|
||||
import {
|
||||
computed,
|
||||
reactive
|
||||
} from 'vue';
|
||||
import _ from 'lodash';
|
||||
|
||||
const pagination = {
|
||||
data: [],
|
||||
current_page: 1,
|
||||
total: 1,
|
||||
last_page: 1,
|
||||
};
|
||||
const state = reactive({
|
||||
currentTab: 0,
|
||||
showApply: false,
|
||||
pagination: {
|
||||
data: [],
|
||||
current_page: 1,
|
||||
total: 1,
|
||||
last_page: 1,
|
||||
},
|
||||
loadStatus: '',
|
||||
});
|
||||
// 字典需要登录 尚未接入 先用固定值代替
|
||||
const status = {
|
||||
10: '申请售后',
|
||||
20: '商品待退货',
|
||||
30: '商家待收货',
|
||||
40: '等待退款',
|
||||
50: '退款成功',
|
||||
61: '买家取消',
|
||||
62: '商家拒绝',
|
||||
63: '商家拒收货'
|
||||
}
|
||||
const status2 = {
|
||||
10: '仅退款',
|
||||
20: '退货退款'
|
||||
}
|
||||
const tabMaps = [{
|
||||
name: '全部',
|
||||
value: 'all',
|
||||
},
|
||||
// {
|
||||
// name: '申请中',
|
||||
// value: 'nooper',
|
||||
// },
|
||||
// {
|
||||
// name: '处理中',
|
||||
// value: 'ing',
|
||||
// },
|
||||
// {
|
||||
// name: '已完成',
|
||||
// value: 'completed',
|
||||
// },
|
||||
// {
|
||||
// name: '已拒绝',
|
||||
// value: 'refuse',
|
||||
// },
|
||||
];
|
||||
// 切换选项卡
|
||||
function onTabsChange(e) {
|
||||
state.pagination = pagination
|
||||
state.currentTab = e.index;
|
||||
getOrderList();
|
||||
}
|
||||
|
||||
// 获取售后列表
|
||||
async function getOrderList(page = 1, list_rows = 5) {
|
||||
pagination.current_page = page;
|
||||
state.loadStatus = 'loading';
|
||||
let res = await sheep.$api.order.aftersale.list({
|
||||
// type: tabMaps[state.currentTab].value,
|
||||
pageSize: list_rows,
|
||||
pageNo: page,
|
||||
});
|
||||
console.log(res, '未处理前售后列表数据')
|
||||
if (res.code === 0) {
|
||||
let orderList = _.concat(state.pagination.data, res.data.list);
|
||||
|
||||
state.pagination = {
|
||||
total: res.data.total,
|
||||
...res.data,
|
||||
data: orderList,
|
||||
};
|
||||
console.log(state.pagination, '售后订单数据')
|
||||
// if (state.pagination.current_page < state.pagination.last_page) {
|
||||
state.loadStatus = 'more';
|
||||
// } else {
|
||||
// state.loadStatus = 'noMore';
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
function onApply(orderId) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要取消此申请吗?',
|
||||
success: async function(res) {
|
||||
if (res.confirm) {
|
||||
const {
|
||||
error
|
||||
} = await sheep.$api.order.aftersale.cancel(orderId);
|
||||
if (error === 0) {
|
||||
state.pagination = pagination
|
||||
getOrderList();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function onDelete(orderId) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要删除吗?',
|
||||
success: async function(res) {
|
||||
if (res.confirm) {
|
||||
const {
|
||||
error
|
||||
} = await sheep.$api.order.aftersale.delete(orderId);
|
||||
if (error === 0) {
|
||||
state.pagination = pagination
|
||||
getOrderList();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
onLoad(async (options) => {
|
||||
if (options.type) {
|
||||
state.currentTab = options.type;
|
||||
}
|
||||
getOrderList();
|
||||
});
|
||||
|
||||
// 加载更多
|
||||
function loadmore() {
|
||||
// if (state.loadStatus !== 'noMore') {
|
||||
getOrderList(pagination.current_page + 1);
|
||||
// }
|
||||
}
|
||||
|
||||
// 上拉加载更多
|
||||
onReachBottom(() => {
|
||||
loadmore();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.list-box {
|
||||
background-color: #fff;
|
||||
|
||||
.order-head {
|
||||
padding: 0 25rpx;
|
||||
height: 77rpx;
|
||||
}
|
||||
|
||||
.apply-box {
|
||||
height: 82rpx;
|
||||
|
||||
.title {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 22rpx;
|
||||
color: $dark-6;
|
||||
}
|
||||
}
|
||||
|
||||
.tool-btn-box {
|
||||
height: 100rpx;
|
||||
|
||||
.tool-btn {
|
||||
width: 160rpx;
|
||||
height: 60rpx;
|
||||
background: #f6f6f6;
|
||||
border-radius: 30rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
99
pages/order/aftersale/log-item.vue
Normal file
99
pages/order/aftersale/log-item.vue
Normal file
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<view class="log-item ss-flex">
|
||||
<view class="log-icon ss-flex-col ss-col-center ss-m-r-20">
|
||||
<text class="cicon-title" :class="index === 0 ? 'activity-color' : ''"></text>
|
||||
<view v-if="data.length - 1 != index" class="line"></view>
|
||||
</view>
|
||||
<view>
|
||||
<view class="text">{{ item.log_type_text }}</view>
|
||||
<mp-html class="richtext" :content="item.content"></mp-html>
|
||||
<view class="" v-if="item.images?.length">
|
||||
<scroll-view class="scroll-box" scroll-x scroll-anchoring>
|
||||
<view class="ss-flex">
|
||||
<view v-for="i in item.images" :key="i" class="ss-m-r-20">
|
||||
<su-image
|
||||
class="content-img"
|
||||
isPreview
|
||||
:previewList="state.commentImages"
|
||||
:current="index"
|
||||
:src="i"
|
||||
:height="120"
|
||||
:width="120"
|
||||
mode="aspectFit"
|
||||
></su-image>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<view class="date">{{ item.create_time }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { reactive } from 'vue';
|
||||
const props = defineProps({
|
||||
item: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
});
|
||||
const state = reactive({
|
||||
commentImages: [],
|
||||
});
|
||||
props.item.images?.forEach((i) => {
|
||||
state.commentImages.push(sheep.$url.cdn(i));
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.log-item {
|
||||
align-items: stretch;
|
||||
}
|
||||
.log-icon {
|
||||
height: inherit;
|
||||
.cicon-title {
|
||||
font-size: 30rpx;
|
||||
color: #dfdfdf;
|
||||
}
|
||||
.activity-color {
|
||||
color: #60bd45;
|
||||
}
|
||||
.line {
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
background: #dfdfdf;
|
||||
}
|
||||
}
|
||||
.text {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
}
|
||||
.richtext {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #999999;
|
||||
margin: 20rpx 0 0 0;
|
||||
}
|
||||
.content-img {
|
||||
margin-top: 20rpx;
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
}
|
||||
.date {
|
||||
margin-top: 20rpx;
|
||||
font-size: 24rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 400;
|
||||
color: #999999;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
</style>
|
||||
54
pages/order/aftersale/log.vue
Normal file
54
pages/order/aftersale/log.vue
Normal file
@@ -0,0 +1,54 @@
|
||||
<!-- 售后进度 -->
|
||||
<template>
|
||||
<s-layout title="售后进度">
|
||||
<view class="log-box">
|
||||
<view v-for="(item, index) in state.info" :key="item.title">
|
||||
<log-item :item="item" :index="index" :data="state.info"></log-item>
|
||||
</view>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { computed, reactive } from 'vue';
|
||||
import logItem from './log-item.vue';
|
||||
|
||||
const state = reactive({
|
||||
active: 1,
|
||||
list: [
|
||||
{
|
||||
title: '买家下单',
|
||||
desc: '2018-11-11',
|
||||
},
|
||||
{
|
||||
title: '卖家发货',
|
||||
desc: '2018-11-12',
|
||||
},
|
||||
{
|
||||
title: '买家签收',
|
||||
desc: '2018-11-13',
|
||||
},
|
||||
{
|
||||
title: '交易完成',
|
||||
desc: '2018-11-14',
|
||||
},
|
||||
],
|
||||
});
|
||||
async function getDetail(id) {
|
||||
const { data } = await sheep.$api.order.aftersale.detail(id);
|
||||
state.info = data.logs;
|
||||
}
|
||||
onLoad((options) => {
|
||||
state.aftersaleId = options.id;
|
||||
getDetail(options.id);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.log-box {
|
||||
padding: 24rpx 24rpx 24rpx 40rpx;
|
||||
background-color: #fff;
|
||||
}
|
||||
</style>
|
||||
414
pages/order/confirm.vue
Normal file
414
pages/order/confirm.vue
Normal file
@@ -0,0 +1,414 @@
|
||||
<template>
|
||||
<s-layout title="确认订单">
|
||||
<!-- v-if="state.orderInfo.need_address === 1" -->
|
||||
<!-- 这个判断先删除 -->
|
||||
<view class="bg-white address-box ss-m-b-14 ss-r-b-10" @tap="onSelectAddress">
|
||||
<s-address-item :item="state.addressInfo" :hasBorderBottom="false">
|
||||
<view class="ss-rest-button"><text class="_icon-forward"></text></view>
|
||||
</s-address-item>
|
||||
</view>
|
||||
<view class="order-card-box ss-m-b-14">
|
||||
<s-goods-item v-for="item in state.orderInfo.goods_list" :key="item.goods_id"
|
||||
:img="item.current_sku_price.image || item.goods.image" :title="item.goods.title"
|
||||
:skuText="item.current_sku_price?.goods_sku_text" :price="item.current_sku_price.price"
|
||||
:num="item.goods_num" marginBottom="10">
|
||||
<template #top>
|
||||
<view class="order-item ss-flex ss-col-center ss-row-between ss-p-x-20 bg-white">
|
||||
<view class="item-title">配送方式</view>
|
||||
<view class="ss-flex ss-col-center">
|
||||
<text class="item-value">{{ item.dispatch_type_text }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</s-goods-item>
|
||||
|
||||
<view class="order-item ss-flex ss-col-center ss-row-between ss-p-x-20 bg-white ss-r-10">
|
||||
<view class="item-title">订单备注</view>
|
||||
<view class="ss-flex ss-col-center">
|
||||
<uni-easyinput maxlength="20" placeholder="建议留言前先与商家沟通" v-model="state.orderPayload.remark"
|
||||
:inputBorder="false" :clearable="false"></uni-easyinput>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 合计 -->
|
||||
<view class="bg-white total-card-box ss-p-20 ss-m-b-14 ss-r-10">
|
||||
<view class="total-box-content border-bottom">
|
||||
<view class="order-item ss-flex ss-col-center ss-row-between">
|
||||
<view class="item-title">商品金额</view>
|
||||
<view class="ss-flex ss-col-center">
|
||||
<text class="item-value ss-m-r-24">¥{{ state.orderInfo.goods_amount }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="order-item ss-flex ss-col-center ss-row-between"
|
||||
v-if="state.orderPayload.order_type === 'score'">
|
||||
<view class="item-title">扣除积分</view>
|
||||
<view class="ss-flex ss-col-center">
|
||||
<image :src="sheep.$url.static('/static/img/shop/goods/score1.svg')" class="score-img"></image>
|
||||
<text class="item-value ss-m-r-24">{{ state.orderInfo.score_amount }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="order-item ss-flex ss-col-center ss-row-between">
|
||||
<view class="item-title">运费</view>
|
||||
<view class="ss-flex ss-col-center">
|
||||
<text class="item-value ss-m-r-24">+¥{{ state.orderInfo.dispatch_amount }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="order-item ss-flex ss-col-center ss-row-between"
|
||||
v-if="state.orderPayload.order_type != 'score'">
|
||||
<!-- <view v-if="state.orderInfo.coupon_discount_fee > 0" class="order-item ss-flex ss-col-center ss-row-between"> -->
|
||||
<view class="item-title">优惠券</view>
|
||||
<view class="ss-flex ss-col-center" @tap="state.showCoupon = true">
|
||||
<text class="item-value text-red"
|
||||
v-if="state.orderPayload.coupon_id">-¥{{ state.orderInfo.coupon_discount_fee }}</text>
|
||||
<text class="item-value"
|
||||
:class="state.couponInfo.can_use?.length > 0 ? 'text-red' : 'text-disabled'" v-else>{{
|
||||
state.couponInfo.can_use?.length > 0
|
||||
? state.couponInfo.can_use?.length + '张可用'
|
||||
: '暂无可用优惠券'
|
||||
}}</text>
|
||||
|
||||
<text class="_icon-forward item-icon"></text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="order-item ss-flex ss-col-center ss-row-between"
|
||||
v-if="state.orderInfo.promo_infos?.length">
|
||||
<!-- <view v-if="state.orderInfo.promo_discount_fee > 0" class="order-item ss-flex ss-col-center ss-row-between"> -->
|
||||
<view class="item-title">活动优惠</view>
|
||||
<view class="ss-flex ss-col-center" @tap="state.showDiscount = true">
|
||||
<text class="item-value text-red"> -¥{{ state.orderInfo.promo_discount_fee }} </text>
|
||||
<text class="_icon-forward item-icon"></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="total-box-footer ss-font-28 ss-flex ss-row-right ss-col-center ss-m-r-28">
|
||||
<view class="total-num ss-m-r-20">共{{ state.totalNumber }}件</view>
|
||||
<view>合计:</view>
|
||||
<view class="total-num text-red"> ¥{{ state.orderInfo.pay_fee }} </view>
|
||||
<view class="ss-flex" v-if="state.orderPayload.order_type === 'score'">
|
||||
<view class="total-num ss-font-30 text-red ss-m-l-4"> + </view>
|
||||
<image :src="sheep.$url.static('/static/img/shop/goods/score1.svg')" class="score-img"></image>
|
||||
<view class="total-num ss-font-30 text-red">{{ state.orderInfo.score_amount }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 发票 -->
|
||||
<view class="bg-white ss-p-20 ss-r-20">
|
||||
<view class="order-item ss-flex ss-col-center ss-row-between">
|
||||
<view class="item-title">发票申请</view>
|
||||
<view class="ss-flex ss-col-center" @tap="onSelectInvoice">
|
||||
<text class="item-value">{{ state.invoiceInfo.name || '无需开具发票' }}</text>
|
||||
<text class="_icon-forward item-icon"></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 选择优惠券弹框 -->
|
||||
<s-coupon-select v-model="state.couponInfo" :show="state.showCoupon" @confirm="onSelectCoupon"
|
||||
@close="state.showCoupon = false" />
|
||||
<!-- 满额折扣弹框 -->
|
||||
<s-discount-list v-model="state.orderInfo" :show="state.showDiscount" @close="state.showDiscount = false" />
|
||||
<!-- 底部 -->
|
||||
<su-fixed bottom :opacity="false" bg="bg-white" placeholder :noFixed="false" :index="200">
|
||||
<view class="footer-box border-top ss-flex ss-row-between ss-p-x-20 ss-col-center">
|
||||
<view class="total-box-footer ss-flex ss-col-center">
|
||||
<view class="total-num ss-font-30 text-red"> ¥{{ state.orderInfo.pay_fee }} </view>
|
||||
<view v-if="state.orderPayload.order_type === 'score'" class="ss-flex">
|
||||
<view class="total-num ss-font-30 text-red ss-m-l-4">+</view>
|
||||
<image :src="sheep.$url.static('/static/img/shop/goods/score1.svg')" class="score-img"></image>
|
||||
<view class="total-num ss-font-30 text-red">{{ state.orderInfo.score_amount }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<button class="ss-reset-button ui-BG-Main-Gradient ss-r-40 submit-btn ui-Shadow-Main" @tap="onConfirm">
|
||||
{{ exchangeNow ? '立即兑换' : '提交订单' }}
|
||||
</button>
|
||||
</view>
|
||||
</su-fixed>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
reactive,
|
||||
computed
|
||||
} from 'vue';
|
||||
import {
|
||||
onLoad,
|
||||
onPageScroll,
|
||||
onShow
|
||||
} from '@dcloudio/uni-app';
|
||||
import sheep from '@/sheep';
|
||||
import {
|
||||
isEmpty
|
||||
} from 'lodash';
|
||||
|
||||
const state = reactive({
|
||||
orderPayload: {},
|
||||
orderInfo: {},
|
||||
addressInfo: {},
|
||||
invoiceInfo: {},
|
||||
totalNumber: 0,
|
||||
showCoupon: false,
|
||||
couponInfo: [],
|
||||
showDiscount: false,
|
||||
});
|
||||
|
||||
// 立即兑换(立即兑换无需跳转收银台)
|
||||
const exchangeNow = computed(
|
||||
() => state.orderPayload.order_type === 'score' && state.orderInfo.pay_fee == 0,
|
||||
);
|
||||
|
||||
// 选择地址
|
||||
function onSelectAddress() {
|
||||
uni.$once('SELECT_ADDRESS', (e) => {
|
||||
changeConsignee(e.addressInfo);
|
||||
});
|
||||
sheep.$router.go('/pages/user/address/list');
|
||||
}
|
||||
|
||||
// 更改收货人地址&计算订单信息
|
||||
async function changeConsignee(addressInfo = {}) {
|
||||
if (isEmpty(addressInfo)) {
|
||||
const {
|
||||
code,
|
||||
data
|
||||
} = await sheep.$api.user.address.default();
|
||||
console.log(data, '默认收货地址');
|
||||
if (code === 0 && !isEmpty(data)) {
|
||||
console.log('执行赋值')
|
||||
addressInfo = data;
|
||||
}
|
||||
}
|
||||
if (!isEmpty(addressInfo)) {
|
||||
state.addressInfo = addressInfo;
|
||||
state.orderPayload.address_id = state.addressInfo.id;
|
||||
}
|
||||
getOrderInfo();
|
||||
}
|
||||
|
||||
// 选择优惠券
|
||||
async function onSelectCoupon(e) {
|
||||
state.orderPayload.coupon_id = e || 0;
|
||||
getOrderInfo();
|
||||
state.showCoupon = false;
|
||||
}
|
||||
|
||||
// 选择发票信息
|
||||
function onSelectInvoice() {
|
||||
uni.$once('SELECT_INVOICE', (e) => {
|
||||
state.invoiceInfo = e.invoiceInfo;
|
||||
state.orderPayload.invoice_id = e.invoiceInfo.id || 0;
|
||||
});
|
||||
sheep.$router.go('/pages/user/invoice/list');
|
||||
}
|
||||
|
||||
// 提交订单/立即兑换
|
||||
function onConfirm() {
|
||||
if (!state.orderPayload.address_id && state.orderInfo.need_address === 1) {
|
||||
sheep.$helper.toast('请选择收货地址');
|
||||
return;
|
||||
}
|
||||
|
||||
if (exchangeNow.value) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定使用积分立即兑换?',
|
||||
cancelText: '再想想',
|
||||
success: async function(res) {
|
||||
if (res.confirm) {
|
||||
submitOrder();
|
||||
}
|
||||
},
|
||||
});
|
||||
} else {
|
||||
submitOrder();
|
||||
}
|
||||
}
|
||||
|
||||
// 创建订单&跳转
|
||||
async function submitOrder() {
|
||||
const {
|
||||
error,
|
||||
data
|
||||
} = await sheep.$api.order.create(state.orderPayload);
|
||||
if (error === 0) {
|
||||
// 更新购物车列表
|
||||
if (state.orderPayload.from === 'cart') {
|
||||
sheep.$store('cart').getList();
|
||||
}
|
||||
if (exchangeNow.value) {
|
||||
sheep.$router.redirect('/pages/pay/result', {
|
||||
orderSN: data.order_sn,
|
||||
});
|
||||
} else {
|
||||
sheep.$router.redirect('/pages/pay/index', {
|
||||
orderSN: data.order_sn,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 检查库存&计算订单价格
|
||||
async function getOrderInfo() {
|
||||
console.log(state.orderPayload, '计算价格传参')
|
||||
// let {code, data} = await sheep.$api.order.calc(state.orderPayload);
|
||||
// let data = await sheep.$api.order.calc(state.orderPayload);
|
||||
console.log(state.orderPayload.items)
|
||||
let data = await sheep.$api.order.calc({
|
||||
deliveryType: 1,
|
||||
pointStatus: false,
|
||||
items: state.orderPayload.items
|
||||
});
|
||||
console.log(data, '修改后的获取订单详细数据')
|
||||
return;
|
||||
if (error === 0) {
|
||||
state.totalNumber = 0;
|
||||
state.orderInfo = data;
|
||||
state.orderInfo.goods_list.forEach((item) => {
|
||||
state.totalNumber += item.goods_num;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 获取可用优惠券
|
||||
async function getCoupons() {
|
||||
const {
|
||||
error,
|
||||
data
|
||||
} = await sheep.$api.order.coupons(state.orderPayload);
|
||||
if (error === 0) {
|
||||
state.couponInfo = data;
|
||||
}
|
||||
}
|
||||
|
||||
onLoad(async (options) => {
|
||||
console.log(options)
|
||||
if (options.data) {
|
||||
state.orderPayload = JSON.parse(options.data);
|
||||
changeConsignee();
|
||||
if (state.orderPayload.order_type !== 'score') {
|
||||
getCoupons();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep() {
|
||||
.uni-input-wrapper {
|
||||
width: 320rpx;
|
||||
}
|
||||
|
||||
.uni-easyinput__content-input {
|
||||
font-size: 28rpx;
|
||||
height: 72rpx;
|
||||
text-align: right !important;
|
||||
padding-right: 0 !important;
|
||||
|
||||
.uni-input-input {
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
font-size: 26rpx;
|
||||
height: 32rpx;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.uni-easyinput__content {
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: right !important;
|
||||
}
|
||||
}
|
||||
|
||||
.score-img {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
margin: 0 4rpx;
|
||||
}
|
||||
|
||||
.order-item {
|
||||
height: 80rpx;
|
||||
|
||||
.item-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.item-value {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
font-family: OPPOSANS;
|
||||
}
|
||||
|
||||
.text-disabled {
|
||||
color: #bbbbbb;
|
||||
}
|
||||
|
||||
.item-icon {
|
||||
color: $dark-9;
|
||||
}
|
||||
|
||||
.remark-input {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.item-placeholder {
|
||||
color: $dark-9;
|
||||
font-size: 26rpx;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
.total-box-footer {
|
||||
height: 90rpx;
|
||||
|
||||
.total-num {
|
||||
color: #333333;
|
||||
font-family: OPPOSANS;
|
||||
}
|
||||
}
|
||||
|
||||
.footer-box {
|
||||
height: 100rpx;
|
||||
|
||||
.submit-btn {
|
||||
width: 240rpx;
|
||||
height: 70rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
|
||||
.goto-pay-text {
|
||||
line-height: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
width: 240rpx;
|
||||
height: 80rpx;
|
||||
font-size: 26rpx;
|
||||
background-color: #e5e5e5;
|
||||
color: $dark-9;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 28rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.cicon-checkbox {
|
||||
font-size: 36rpx;
|
||||
color: var(--ui-BG-Main);
|
||||
}
|
||||
|
||||
.cicon-box {
|
||||
font-size: 36rpx;
|
||||
color: #999999;
|
||||
}
|
||||
</style>
|
||||
692
pages/order/detail.vue
Normal file
692
pages/order/detail.vue
Normal file
@@ -0,0 +1,692 @@
|
||||
<!-- 订单详情 -->
|
||||
<template>
|
||||
<s-layout title="订单详情" class="index-wrap" navbar="inner">
|
||||
<!-- 订单状态 -->
|
||||
<view class="state-box ss-flex-col ss-col-center ss-row-right" :style="[
|
||||
{
|
||||
marginTop: '-' + Number(statusBarHeight + 88) + 'rpx',
|
||||
paddingTop: Number(statusBarHeight + 88) + 'rpx',
|
||||
},
|
||||
]">
|
||||
<view class="ss-flex ss-m-t-32 ss-m-b-20">
|
||||
<image v-if="
|
||||
state.orderInfo.status_code == 'unpaid' ||
|
||||
state.orderInfo.status_code == 'nosend' ||
|
||||
state.orderInfo.status_code == 'nocomment'
|
||||
" class="state-img" :src="sheep.$url.static('/static/img/shop/order/order_loading.png')">
|
||||
</image>
|
||||
<image v-if="
|
||||
state.orderInfo.status_code == 'completed' ||
|
||||
state.orderInfo.status_code == 'refund_agree'
|
||||
" class="state-img" :src="sheep.$url.static('/static/img/shop/order/order_success.png')">
|
||||
</image>
|
||||
<image v-if="state.orderInfo.status_code == 'cancel' || state.orderInfo.status_code == 'closed'"
|
||||
class="state-img" :src="sheep.$url.static('/static/img/shop/order/order_close.png')">
|
||||
</image>
|
||||
<image v-if="state.orderInfo.status_code == 'noget'" class="state-img"
|
||||
:src="sheep.$url.static('/static/img/shop/order/order_express.png')">
|
||||
</image>
|
||||
<view class="ss-font-30">{{ state.orderInfo.status_text }}</view>
|
||||
</view>
|
||||
<view class="ss-font-26 ss-m-x-20 ss-m-b-70">{{ state.orderInfo.status_desc }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 收货地址 -->
|
||||
<view class="order-address-box" v-if="state.orderInfo.address">
|
||||
<view class="ss-flex ss-col-center">
|
||||
<text class="address-username">
|
||||
{{ state.orderInfo.address.consignee }}
|
||||
</text>
|
||||
<text class="address-phone">{{ state.orderInfo.address.mobile }}</text>
|
||||
</view>
|
||||
<view class="address-detail">{{ addressText }}</view>
|
||||
</view>
|
||||
|
||||
<view class="detail-goods" :style="[{ marginTop: state.orderInfo.address ? '0' : '-40rpx' }]">
|
||||
<!-- 订单信息 -->
|
||||
<view class="order-list" v-for="item in state.orderInfo.items" :key="item.goods_id">
|
||||
<view class="order-card">
|
||||
<s-goods-item @tap="onGoodsDetail(item.goods_id)" :img="item.goods_image" :title="item.goods_title"
|
||||
:skuText="item.goods_sku_text" :price="item.goods_price" :score="state.orderInfo.score_amount"
|
||||
:num="item.goods_num">
|
||||
<!-- <template #top>
|
||||
<view class="order-item ss-flex ss-col-center ss-row-between ss-p-x-20 bg-white">
|
||||
<view class="item-title">配送方式</view>
|
||||
<view class="ss-flex ss-col-center">
|
||||
<text class="item-value ss-m-r-20">{{ item.dispatch_type_text }}</text>
|
||||
<button class="ss-reset-button copy-btn" @tap="onDetail(item)" v-if="
|
||||
(item.dispatch_type === 'autosend' || item.dispatch_type === 'custom') &&
|
||||
item.dispatch_status !== 0
|
||||
">详情</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<template #tool>
|
||||
<view class="ss-flex">
|
||||
<button class="ss-reset-button apply-btn" v-if="item.btns.includes('aftersale')"
|
||||
@tap.stop="
|
||||
sheep.$router.go('/pages/order/aftersale/apply', {
|
||||
item: JSON.stringify(item),
|
||||
})
|
||||
">
|
||||
申请售后
|
||||
</button>
|
||||
<button class="ss-reset-button apply-btn" v-if="item.btns.includes('re_aftersale')"
|
||||
@tap.stop="
|
||||
sheep.$router.go('/pages/order/aftersale/apply', {
|
||||
item: JSON.stringify(item),
|
||||
})
|
||||
">
|
||||
重新售后
|
||||
</button>
|
||||
|
||||
<button class="ss-reset-button apply-btn" v-if="item.btns.includes('aftersale_info')"
|
||||
@tap.stop="
|
||||
sheep.$router.go('/pages/order/aftersale/detail', {
|
||||
id: item.ext.aftersale_id,
|
||||
})
|
||||
">
|
||||
售后详情
|
||||
</button>
|
||||
<button class="ss-reset-button apply-btn" v-if="item.btns.includes('buy_again')"
|
||||
@tap.stop="
|
||||
sheep.$router.go('/pages/goods/index', {
|
||||
id: item.goods_id,
|
||||
})
|
||||
">
|
||||
再次购买
|
||||
</button>
|
||||
</view>
|
||||
</template>
|
||||
<template #priceSuffix>
|
||||
<button class="ss-reset-button tag-btn" v-if="item.status_text">
|
||||
{{ item.status_text }}
|
||||
</button>
|
||||
</template> -->
|
||||
</s-goods-item>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 订单信息 -->
|
||||
<view class="notice-box">
|
||||
<view class="notice-box__content">
|
||||
<view class="notice-item--center">
|
||||
<view class="ss-flex ss-flex-1">
|
||||
<text class="title">订单编号:</text>
|
||||
<text class="detail">{{ state.orderInfo.order_sn }}</text>
|
||||
</view>
|
||||
<button class="ss-reset-button copy-btn" @tap="onCopy">复制</button>
|
||||
</view>
|
||||
<view class="notice-item">
|
||||
<text class="title">下单时间:</text>
|
||||
<text class="detail">{{ state.orderInfo.create_time }}</text>
|
||||
</view>
|
||||
<view class="notice-item" v-if="state.orderInfo.paid_time">
|
||||
<text class="title">支付时间:</text>
|
||||
<text class="detail">{{ state.orderInfo.paid_time || '-' }}</text>
|
||||
</view>
|
||||
<view class="notice-item">
|
||||
<text class="title">支付方式:</text>
|
||||
<text class="detail">{{ state.orderInfo.pay_types_text?.join(',') || '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 价格信息 -->
|
||||
<view class="order-price-box">
|
||||
<view class="notice-item ss-flex ss-row-between">
|
||||
<text class="title">商品总额</text>
|
||||
<view class="ss-flex">
|
||||
<text class="detail"
|
||||
v-if="Number(state.orderInfo.goods_amount) > 0">¥{{ state.orderInfo.goods_amount }}</text>
|
||||
<view v-if="state.orderInfo.score_amount && Number(state.orderInfo.goods_amount) > 0"
|
||||
class="detail">+</view>
|
||||
<view class="price-text ss-flex ss-col-center" v-if="state.orderInfo.score_amount">
|
||||
<image :src="sheep.$url.static('/static/img/shop/goods/score1.svg')" class="score-img"></image>
|
||||
<view class="detail">{{ state.orderInfo.score_amount }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="notice-item ss-flex ss-row-between">
|
||||
<text class="title">运费</text>
|
||||
<text class="detail">¥{{ state.orderInfo.dispatch_amount }}</text>
|
||||
</view>
|
||||
<view class="notice-item ss-flex ss-row-between" v-if="state.orderInfo.total_discount_fee > 0">
|
||||
<text class="title">优惠金额</text>
|
||||
<text class="detail">¥{{ state.orderInfo.total_discount_fee }}</text>
|
||||
</view>
|
||||
<view class="notice-item all-rpice-item ss-flex ss-m-t-20">
|
||||
<text class="title">{{
|
||||
['paid', 'completed'].includes(state.orderInfo.status) ? '已付款' : '需付款'
|
||||
}}</text>
|
||||
<text class="detail all-price"
|
||||
v-if="Number(state.orderInfo.pay_fee) > 0">¥{{ state.orderInfo.pay_fee }}</text>
|
||||
<view v-if="
|
||||
state.orderInfo.score_amount &&
|
||||
Number(state.orderInfo.pay_fee) > 0 &&
|
||||
['paid', 'completed'].includes(state.orderInfo.status)
|
||||
" class="detail all-price">+</view>
|
||||
<view class="price-text ss-flex ss-col-center" v-if="
|
||||
state.orderInfo.score_amount && ['paid', 'completed'].includes(state.orderInfo.status)
|
||||
">
|
||||
<image :src="sheep.$url.static('/static/img/shop/goods/score1.svg')" class="score-img"></image>
|
||||
<view class="detail all-price">{{ state.orderInfo.score_amount }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="notice-item all-rpice-item ss-flex ss-m-t-20" v-if="refundFee > 0">
|
||||
<text class="title">已退款</text>
|
||||
<text class="detail all-price">¥{{ refundFee.toFixed(2) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部按钮 -->
|
||||
<!-- TODO: 查看物流、等待成团、评价完后返回页面没刷新页面 -->
|
||||
<su-fixed bottom placeholder bg="bg-white" v-if="state.orderInfo.btns?.length">
|
||||
<view class="footer-box ss-flex ss-col-center ss-row-right">
|
||||
<button class="ss-reset-button cancel-btn" v-if="state.orderInfo.btns?.includes('cancel')"
|
||||
@tap="onCancel(state.orderInfo.id)">取消订单</button>
|
||||
<button class="ss-reset-button pay-btn ui-BG-Main-Gradient" v-if="state.orderInfo.btns?.includes('pay')"
|
||||
@tap="onPay(state.orderInfo.order_sn)">继续支付</button>
|
||||
<button class="ss-reset-button cancel-btn" v-if="state.orderInfo.btns?.includes('apply_refund')"
|
||||
@tap="onRefund(state.orderInfo.id)">申请退款</button>
|
||||
<button class="ss-reset-button cancel-btn" v-if="state.orderInfo.btns?.includes('groupon')" @tap="
|
||||
sheep.$router.go('/pages/activity/groupon/detail', {
|
||||
id: state.orderInfo.ext.groupon_id,
|
||||
})
|
||||
">
|
||||
{{ state.orderInfo.status_code === 'groupon_ing' ? '邀请拼团' : '拼团详情' }}
|
||||
</button>
|
||||
<button class="ss-reset-button cancel-btn" v-if="state.orderInfo.btns?.includes('express')"
|
||||
@tap="onExpress(state.orderInfo.id)">查看物流</button>
|
||||
<button class="ss-reset-button cancel-btn" v-if="state.orderInfo.btns?.includes('confirm')"
|
||||
@tap="onConfirm(state.orderInfo.id)">确认收货</button>
|
||||
<button class="ss-reset-button cancel-btn" v-if="state.orderInfo.btns?.includes('comment')"
|
||||
@tap="onComment(state.orderInfo.id,state.orderInfo)">评价晒单</button>
|
||||
<button v-if="state.orderInfo.btns?.includes('invoice')" class="ss-reset-button cancel-btn"
|
||||
@tap.stop="onOrderInvoice(state.orderInfo.invoice?.id)">
|
||||
查看发票
|
||||
</button>
|
||||
<button v-if="state.orderInfo.btns?.includes('re_apply_refund')" class="ss-reset-button cancel-btn"
|
||||
@tap.stop="onRefund(state.orderInfo.id)">
|
||||
重新退款
|
||||
</button>
|
||||
</view>
|
||||
</su-fixed>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import {
|
||||
onLoad
|
||||
} from '@dcloudio/uni-app';
|
||||
import {
|
||||
computed,
|
||||
reactive
|
||||
} from 'vue';
|
||||
import {
|
||||
isEmpty
|
||||
} from 'lodash';
|
||||
|
||||
const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
|
||||
const headerBg = sheep.$url.css('/static/img/shop/order/order_bg.png');
|
||||
const tradeManaged = computed(() => sheep.$store('app').has_wechat_trade_managed);
|
||||
|
||||
const state = reactive({
|
||||
orderInfo: {},
|
||||
merchantTradeNo: '', // 商户订单号
|
||||
comeinType: '', // 进入订单详情的来源类型
|
||||
});
|
||||
|
||||
const addressText = computed(() => {
|
||||
let data = state.orderInfo.address;
|
||||
if (data) {
|
||||
return `${data.province_name} ${data.city_name} ${data.district_name} ${data.address}`;
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
// 复制
|
||||
const onCopy = () => {
|
||||
sheep.$helper.copyText(state.orderInfo.order_sn);
|
||||
};
|
||||
//退款总额
|
||||
const refundFee = computed(() => {
|
||||
let refundFee = 0;
|
||||
state.orderInfo.items?.forEach((i) => {
|
||||
refundFee += Number(i.refund_fee);
|
||||
});
|
||||
return refundFee;
|
||||
});
|
||||
// 去支付
|
||||
function onPay(orderSN) {
|
||||
sheep.$router.go('/pages/pay/index', {
|
||||
orderSN,
|
||||
});
|
||||
}
|
||||
|
||||
function onGoodsDetail(id) {
|
||||
sheep.$router.go('/pages/goods/index', {
|
||||
id
|
||||
});
|
||||
}
|
||||
|
||||
// 取消订单
|
||||
async function onCancel(orderId) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要取消订单吗?',
|
||||
success: async function(res) {
|
||||
if (res.confirm) {
|
||||
const {
|
||||
error,
|
||||
data
|
||||
} = await sheep.$api.order.cancel(orderId);
|
||||
if (error === 0) {
|
||||
getOrderDetail(data.order_sn);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// 申请退款
|
||||
async function onRefund(orderId) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要申请退款吗?',
|
||||
success: async function(res) {
|
||||
if (res.confirm) {
|
||||
const {
|
||||
error,
|
||||
data
|
||||
} = await sheep.$api.order.applyRefund(orderId);
|
||||
if (error === 0) {
|
||||
getOrderDetail(data.order_sn);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// 查看物流
|
||||
async function onExpress(orderId) {
|
||||
sheep.$router.go('/pages/order/express/list', {
|
||||
orderId,
|
||||
});
|
||||
}
|
||||
|
||||
//确认收货
|
||||
async function onConfirm(orderId, ignore = false) {
|
||||
// 需开启确认收货组件
|
||||
// todo:
|
||||
// 1.怎么检测是否开启了发货组件功能?如果没有开启的话就不能在这里return出去
|
||||
// 2.如果开启了走mpConfirm方法,需要在App.vue的show方法中拿到确认收货结果
|
||||
let isOpenBusinessView = true;
|
||||
if (
|
||||
sheep.$platform.name === 'WechatMiniProgram' &&
|
||||
!isEmpty(state.orderInfo.wechat_extra_data) &&
|
||||
isOpenBusinessView &&
|
||||
!ignore
|
||||
) {
|
||||
mpConfirm(orderId);
|
||||
return;
|
||||
}
|
||||
|
||||
// 正常的确认收货流程
|
||||
const {
|
||||
error,
|
||||
data
|
||||
} = await sheep.$api.order.confirm(orderId);
|
||||
if (error === 0) {
|
||||
getOrderDetail(data.order_sn);
|
||||
}
|
||||
}
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
// 小程序确认收货组件
|
||||
function mpConfirm(orderId) {
|
||||
if (!wx.openBusinessView) {
|
||||
sheep.$helper.toast(`请升级微信版本`);
|
||||
return;
|
||||
}
|
||||
wx.openBusinessView({
|
||||
businessType: 'weappOrderConfirm',
|
||||
extraData: {
|
||||
merchant_trade_no: state.orderInfo.wechat_extra_data.merchant_trade_no,
|
||||
transaction_id: state.orderInfo.wechat_extra_data.transaction_id,
|
||||
},
|
||||
success(response) {
|
||||
console.log('success:', response);
|
||||
if (response.errMsg === 'openBusinessView:ok') {
|
||||
if (response.extraData.status === 'success') {
|
||||
onConfirm(orderId, true);
|
||||
}
|
||||
}
|
||||
},
|
||||
fail(error) {
|
||||
console.log('error:', error);
|
||||
},
|
||||
complete(result) {
|
||||
console.log('result:', result);
|
||||
},
|
||||
});
|
||||
}
|
||||
// #endif
|
||||
|
||||
// 查看发票
|
||||
function onOrderInvoice(invoiceId) {
|
||||
sheep.$router.go('/pages/order/invoice', {
|
||||
invoiceId,
|
||||
});
|
||||
}
|
||||
|
||||
// 配送方式详情
|
||||
function onDetail(item) {
|
||||
sheep.$router.go('/pages/order/dispatch/content', {
|
||||
id: item.order_id,
|
||||
item_id: item.id,
|
||||
});
|
||||
}
|
||||
|
||||
// 评价
|
||||
function onComment(orderSN, orderId) {
|
||||
console.log(orderId);
|
||||
// return;
|
||||
uni.$once('SELECT_INVOICE', (e) => {
|
||||
state.invoiceInfo = e.invoiceInfo;
|
||||
});
|
||||
sheep.$router.go('/pages/goods/comment/add', {
|
||||
orderSN,
|
||||
orderId
|
||||
});
|
||||
}
|
||||
async function getOrderDetail(id) {
|
||||
// 对详情数据进行适配
|
||||
let res = {};
|
||||
if (state.comeinType === 'wechat') {
|
||||
res = await sheep.$api.order.detail(id, {
|
||||
merchant_trade_no: state.merchantTradeNo,
|
||||
});
|
||||
} else {
|
||||
res = await sheep.$api.order.detail(id);
|
||||
}
|
||||
console.log(res, '我的订单详情数据');
|
||||
if (res.code === 0) {
|
||||
let obj = {
|
||||
10: ['待发货', '等待买家付款', ["apply_refund"]],
|
||||
30: ['待评价', '等待买家评价', ["express", "comment"]]
|
||||
}
|
||||
res.data.status_text = obj[res.data.status][0];
|
||||
res.data.status_desc = obj[res.data.status][1];
|
||||
res.data.btns = obj[res.data.status][2];
|
||||
res.data.address = {
|
||||
province_name: res.data.receiverAreaName.split(' ')[0],
|
||||
district_name: res.data.receiverAreaName.split(' ')[2],
|
||||
city_name: res.data.receiverAreaName.split(' ')[1],
|
||||
address: res.data.receiverDetailAddress,
|
||||
consignee: res.data.receiverName,
|
||||
mobile: res.data.receiverMobile,
|
||||
}
|
||||
res.data.pay_fee = res.data.payPrice / 100
|
||||
res.data.create_time = sheep.$helper.timeFormat(res.data.createTime, 'yyyy-mm-dd hh:MM:ss')
|
||||
res.data.order_sn = res.data.no
|
||||
res.data.goods_amount = res.data.totalPrice / 100
|
||||
res.data.dispatch_amount = res.data.deliveryPrice / 100
|
||||
res.data.pay_types_text = res.data.payChannelName.split(',')
|
||||
res.data.items = res.data.items.map(ite => {
|
||||
|
||||
return {
|
||||
...ite,
|
||||
goods_title: ite.spuName,
|
||||
goods_num: ite.count,
|
||||
goods_price: ite.price / 100,
|
||||
goods_image: ite.picUrl,
|
||||
goods_sku_text: ite.properties.reduce((it0, it1) => it0 + it1.valueName + ' ', '')
|
||||
}
|
||||
})
|
||||
state.orderInfo = res.data;
|
||||
console.log(state.orderInfo, '修改后数据')
|
||||
} else {
|
||||
sheep.$router.back();
|
||||
}
|
||||
}
|
||||
|
||||
onLoad(async (options) => {
|
||||
let id = 0;
|
||||
if (options.orderSN) {
|
||||
id = options.orderSN;
|
||||
}
|
||||
if (options.id) {
|
||||
id = options.id;
|
||||
}
|
||||
state.comeinType = options.comein_type;
|
||||
if (state.comeinType === 'wechat') {
|
||||
state.merchantTradeNo = options.merchant_trade_no;
|
||||
}
|
||||
getOrderDetail(id);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.score-img {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
margin: 0 4rpx;
|
||||
}
|
||||
|
||||
.apply-btn {
|
||||
width: 140rpx;
|
||||
height: 50rpx;
|
||||
border-radius: 25rpx;
|
||||
font-size: 24rpx;
|
||||
border: 2rpx solid #dcdcdc;
|
||||
line-height: normal;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
.state-box {
|
||||
color: rgba(#fff, 0.9);
|
||||
width: 100%;
|
||||
background: v-bind(headerBg) no-repeat,
|
||||
linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
background-size: 750rpx 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
.state-img {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.order-address-box {
|
||||
background-color: #fff;
|
||||
border-radius: 10rpx;
|
||||
margin: -50rpx 20rpx 16rpx 20rpx;
|
||||
padding: 44rpx 34rpx 42rpx 20rpx;
|
||||
font-size: 30rpx;
|
||||
box-sizing: border-box;
|
||||
font-weight: 500;
|
||||
color: rgba(51, 51, 51, 1);
|
||||
|
||||
.address-username {
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.address-detail {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: rgba(153, 153, 153, 1);
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-goods {
|
||||
border-radius: 10rpx;
|
||||
margin: 0 20rpx 20rpx 20rpx;
|
||||
|
||||
.order-list {
|
||||
margin-bottom: 20rpx;
|
||||
background-color: #fff;
|
||||
|
||||
.order-card {
|
||||
padding: 20rpx 0;
|
||||
|
||||
.order-sku {
|
||||
font-size: 24rpx;
|
||||
|
||||
font-weight: 400;
|
||||
color: rgba(153, 153, 153, 1);
|
||||
width: 450rpx;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.order-num {
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.tag-btn {
|
||||
margin-left: 16rpx;
|
||||
font-size: 24rpx;
|
||||
height: 36rpx;
|
||||
color: var(--ui-BG-Main);
|
||||
border: 2rpx solid var(--ui-BG-Main);
|
||||
border-radius: 14rpx;
|
||||
padding: 0 4rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 订单信息。
|
||||
.notice-box {
|
||||
background: #fff;
|
||||
border-radius: 10rpx;
|
||||
margin: 0 20rpx 20rpx 20rpx;
|
||||
|
||||
.notice-box__head {
|
||||
font-size: 30rpx;
|
||||
|
||||
font-weight: 500;
|
||||
color: rgba(51, 51, 51, 1);
|
||||
line-height: 80rpx;
|
||||
border-bottom: 1rpx solid #dfdfdf;
|
||||
padding: 0 25rpx;
|
||||
}
|
||||
|
||||
.notice-box__content {
|
||||
padding: 20rpx;
|
||||
|
||||
.self-pickup-box {
|
||||
width: 100%;
|
||||
|
||||
.self-pickup--img {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
margin: 40rpx 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notice-item,
|
||||
.notice-item--center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: normal;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.title {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.detail {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
width: 100rpx;
|
||||
line-height: 50rpx;
|
||||
border-radius: 25rpx;
|
||||
padding: 0;
|
||||
background: rgba(238, 238, 238, 1);
|
||||
font-size: 22rpx;
|
||||
font-weight: 400;
|
||||
color: rgba(51, 51, 51, 1);
|
||||
}
|
||||
|
||||
// 订单价格信息
|
||||
.order-price-box {
|
||||
background-color: #fff;
|
||||
border-radius: 10rpx;
|
||||
padding: 20rpx;
|
||||
margin: 0 20rpx 20rpx 20rpx;
|
||||
|
||||
.notice-item {
|
||||
line-height: 70rpx;
|
||||
|
||||
.title {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.detail {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-family: OPPOSANS;
|
||||
}
|
||||
}
|
||||
|
||||
.all-rpice-item {
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
|
||||
.title {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.all-price {
|
||||
font-size: 26rpx;
|
||||
font-family: OPPOSANS;
|
||||
line-height: normal;
|
||||
color: $red;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 底部
|
||||
.footer-box {
|
||||
height: 100rpx;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
border-radius: 10rpx;
|
||||
padding-right: 20rpx;
|
||||
|
||||
.cancel-btn {
|
||||
width: 160rpx;
|
||||
height: 60rpx;
|
||||
background: #eeeeee;
|
||||
border-radius: 30rpx;
|
||||
margin-right: 20rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.pay-btn {
|
||||
width: 160rpx;
|
||||
height: 60rpx;
|
||||
font-size: 26rpx;
|
||||
border-radius: 30rpx;
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
84
pages/order/dispatch/content.vue
Normal file
84
pages/order/dispatch/content.vue
Normal file
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<s-layout title="发货内容">
|
||||
<view class="order-card ss-m-x-20 ss-r-20">
|
||||
<s-goods-item
|
||||
:img="state.data.goods_image"
|
||||
:title="state.data.goods_title"
|
||||
:skuText="state.data.goods_sku_text"
|
||||
:price="state.data.goods_price"
|
||||
:num="state.data.goods_num"
|
||||
radius="20"
|
||||
>
|
||||
<template #priceSuffix>
|
||||
<button class="ss-reset-button tag-btn" v-if="state.data.status_text">
|
||||
{{ state.data.status_text }}
|
||||
</button>
|
||||
</template>
|
||||
</s-goods-item>
|
||||
</view>
|
||||
<view class="bg-white ss-p-20 ss-m-x-20 ss-r-20">
|
||||
<view class="title ss-m-b-26">发货信息</view>
|
||||
<view v-if="state.data.ext?.dispatch_content_type === 'params'">
|
||||
<view class="desc ss-m-b-20" v-for="item in state.data.ext.dispatch_content" :key="item">
|
||||
{{ item.title }}: {{ item.content }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="desc" v-else>{{ state.data.ext?.dispatch_content }}</view>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
<script setup>
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { reactive } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
|
||||
const state = reactive({
|
||||
data: [],
|
||||
});
|
||||
async function getDetail(id, item_id) {
|
||||
const { error, data } = await sheep.$api.order.itemDetail(id,item_id);
|
||||
if (error === 0) {
|
||||
state.data = data;
|
||||
}
|
||||
}
|
||||
onLoad((options) => {
|
||||
getDetail(options.id, options.item_id);
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.order-card {
|
||||
padding: 20rpx 0;
|
||||
|
||||
.order-sku {
|
||||
font-size: 24rpx;
|
||||
|
||||
font-weight: 400;
|
||||
color: rgba(153, 153, 153, 1);
|
||||
width: 450rpx;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.order-num {
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
}
|
||||
.tag-btn {
|
||||
margin-left: 16rpx;
|
||||
font-size: 24rpx;
|
||||
height: 36rpx;
|
||||
color: var(--ui-BG-Main);
|
||||
border: 2rpx solid var(--ui-BG-Main);
|
||||
border-radius: 14rpx;
|
||||
padding: 0 4rpx;
|
||||
}
|
||||
}
|
||||
.title {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
.desc {
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
color: #333333;
|
||||
}
|
||||
</style>
|
||||
104
pages/order/express/list.vue
Normal file
104
pages/order/express/list.vue
Normal file
@@ -0,0 +1,104 @@
|
||||
<!-- 物流包裹-->
|
||||
<template>
|
||||
<s-layout title="物流包裹">
|
||||
<view class="express-wrap">
|
||||
<su-sticky bgColor="#FFE2B6">
|
||||
<view class="header ss-flex ss-p-l-24">{{ state.list.length }}个包裹已派送</view>
|
||||
</su-sticky>
|
||||
<view
|
||||
class="express-box"
|
||||
v-for="item in state.list"
|
||||
:key="item.type"
|
||||
@tap="sheep.$router.go('/pages/order/express/log', { id: item.id, orderId: state.orderId })"
|
||||
>
|
||||
<view class="express-box-header ss-flex ss-row-between">
|
||||
<view class="express-box-header-type">{{ item.status_text }}</view>
|
||||
<view class="express-box-header-num">{{
|
||||
item.express_name + ' : ' + item.express_no
|
||||
}}</view>
|
||||
</view>
|
||||
<view class="express-box-content">
|
||||
<view class="content-address">{{ item.logs[0]?.content }}</view>
|
||||
<view class="" v-if="item.items?.length">
|
||||
<scroll-view class="scroll-box" scroll-x scroll-anchoring>
|
||||
<view class="ss-flex">
|
||||
<view v-for="i in item.items" :key="i" class="ss-m-r-20"
|
||||
><image class="content-img" :src="sheep.$url.static(i.goods_image)" />
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="express-box-foot">共{{ item.items.length }}件商品</view>
|
||||
</view>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { computed, reactive } from 'vue';
|
||||
const state = reactive({
|
||||
list: [],
|
||||
orderId: '',
|
||||
});
|
||||
async function getExpressList(id) {
|
||||
const { data } = await sheep.$api.order.express(id, '');
|
||||
state.list = data;
|
||||
}
|
||||
onLoad((Option) => {
|
||||
state.orderId = Option.orderId;
|
||||
getExpressList(state.orderId);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.header {
|
||||
height: 84rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
color: #a8700d;
|
||||
}
|
||||
.express-box {
|
||||
background: #fff;
|
||||
padding-bottom: 30rpx;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 20rpx;
|
||||
.express-box-header {
|
||||
height: 76rpx;
|
||||
padding: 0 24rpx;
|
||||
border-bottom: 2rpx solid rgba(#dfdfdf, 0.5);
|
||||
.express-box-header-type {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #999;
|
||||
}
|
||||
.express-box-header-num {
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
.express-box-content {
|
||||
padding: 20rpx 24rpx;
|
||||
.content-address {
|
||||
font-size: 28rpx;
|
||||
font-weight: 400;
|
||||
color: #333333;
|
||||
line-height: normal;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
.content-img {
|
||||
width: 180rpx;
|
||||
height: 180rpx;
|
||||
}
|
||||
}
|
||||
.express-box-foot {
|
||||
padding: 0 24rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
174
pages/order/express/log.vue
Normal file
174
pages/order/express/log.vue
Normal file
@@ -0,0 +1,174 @@
|
||||
<!-- 物流追踪 -->
|
||||
<template>
|
||||
<s-layout title="物流追踪">
|
||||
<view class="log-wrap">
|
||||
<view class="log-card ss-flex ss-m-20 ss-r-10" v-if="goodsImages.length > 0">
|
||||
<uni-swiper-dot :info="goodsImages" :current="state.current" mode="round">
|
||||
<swiper class="swiper-box" @change="change">
|
||||
<swiper-item v-for="(item, index) in goodsImages" :key="index">
|
||||
<image class="log-card-img" :src="sheep.$url.static(item.image)"></image>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</uni-swiper-dot>
|
||||
|
||||
<view class="log-card-msg">
|
||||
<view class="ss-flex ss-m-b-8">
|
||||
<view>物流状态:</view>
|
||||
<view class="warning-color">{{ state.info.status_text }}</view>
|
||||
</view>
|
||||
<view class="ss-m-b-8">快递单号:{{ state.info.express_no }}</view>
|
||||
<view>快递公司:{{ state.info.express_name }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="log-content ss-m-20 ss-r-10">
|
||||
<view
|
||||
class="log-content-box ss-flex"
|
||||
v-for="(item, index) in state.info.logs"
|
||||
:key="item.title"
|
||||
>
|
||||
<view class="log-icon ss-flex-col ss-col-center ss-m-r-20">
|
||||
<text
|
||||
v-if="state.info.logs[index].status === state.info.logs[index - 1]?.status"
|
||||
class="cicon-title"
|
||||
></text>
|
||||
<text
|
||||
v-if="state.info.logs[index].status != state.info.logs[index - 1]?.status"
|
||||
:class="[
|
||||
index === 0 ? 'activity-color' : 'info-color',
|
||||
item.status === 'transport'
|
||||
? 'sicon-transport'
|
||||
: item.status === 'delivery'
|
||||
? 'sicon-delivery'
|
||||
: item.status === 'collect'
|
||||
? 'sicon-a-collectmaterials'
|
||||
: item.status === 'fail' || item.status === 'back' || item.status === 'refuse'
|
||||
? 'sicon-circleclose'
|
||||
: item.status === 'signfor'
|
||||
? 'sicon-circlecheck'
|
||||
: 'sicon-warning-outline',
|
||||
]"
|
||||
></text>
|
||||
<view v-if="state.info.logs.length - 1 != index" class="line"></view>
|
||||
</view>
|
||||
<view class="log-content-msg">
|
||||
<view
|
||||
v-if="
|
||||
item.status_text &&
|
||||
state.info.logs[index].status != state.info.logs[index - 1]?.status
|
||||
"
|
||||
class="log-msg-title ss-m-b-20"
|
||||
>{{ item.status_text }}</view
|
||||
>
|
||||
<view class="log-msg-desc ss-m-b-16">{{ item.content }}</view>
|
||||
<view class="log-msg-date ss-m-b-40">{{ item.change_date }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { computed, reactive } from 'vue';
|
||||
|
||||
const state = reactive({
|
||||
info: [],
|
||||
current: 0,
|
||||
});
|
||||
const goodsImages = computed(() => {
|
||||
let array = [];
|
||||
if (state.info.items) {
|
||||
state.info.items.forEach((item) => {
|
||||
array.push({
|
||||
image: item.goods_image,
|
||||
});
|
||||
});
|
||||
}
|
||||
return array;
|
||||
});
|
||||
function change(e) {
|
||||
state.current = e.detail.current;
|
||||
}
|
||||
async function getExpressdetail(id, orderId) {
|
||||
const { data } = await sheep.$api.order.express(id, orderId);
|
||||
state.info = data;
|
||||
}
|
||||
onLoad((Option) => {
|
||||
getExpressdetail(Option.id, Option.orderId);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.swiper-box {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
}
|
||||
.log-card {
|
||||
border-top: 2rpx solid rgba(#dfdfdf, 0.5);
|
||||
padding: 20rpx;
|
||||
background: #fff;
|
||||
margin-bottom: 20rpx;
|
||||
.log-card-img {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
.log-card-msg {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
width: 490rpx;
|
||||
color: #333333;
|
||||
.warning-color {
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
.log-content {
|
||||
padding: 34rpx 20rpx 0rpx 20rpx;
|
||||
background: #fff;
|
||||
.log-content-box {
|
||||
align-items: stretch;
|
||||
}
|
||||
.log-icon {
|
||||
height: inherit;
|
||||
.cicon-title {
|
||||
color: #ccc;
|
||||
font-size: 40rpx;
|
||||
}
|
||||
.activity-color {
|
||||
color: #f0c785;
|
||||
font-size: 40rpx;
|
||||
}
|
||||
.info-color {
|
||||
color: #ccc;
|
||||
font-size: 40rpx;
|
||||
}
|
||||
.line {
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
background: #d8d8d8;
|
||||
}
|
||||
}
|
||||
|
||||
.log-content-msg {
|
||||
.log-msg-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
.log-msg-desc {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #333333;
|
||||
line-height: 36rpx;
|
||||
}
|
||||
.log-msg-date {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
329
pages/order/invoice.vue
Normal file
329
pages/order/invoice.vue
Normal file
@@ -0,0 +1,329 @@
|
||||
<!-- 订单详情 -->
|
||||
<template>
|
||||
<s-layout title="发票详情" class="invoice-wrap" navbar="inner">
|
||||
<view
|
||||
class="invoice-heard ss-flex-col ss-row-right ss-col-center"
|
||||
:style="[
|
||||
{
|
||||
marginTop: '-' + Number(statusBarHeight + 88) + 'rpx',
|
||||
paddingTop: Number(statusBarHeight + 88) + 'rpx',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<view class="ss-flex ss-m-t-32 ss-m-b-32">
|
||||
<text
|
||||
class="sicon-warning-line"
|
||||
v-if="state.data.status === 'waiting' || state.data.status === 'unpaid'"
|
||||
></text>
|
||||
<text class="sicon-check-line" v-if="state.data.status === 'finish'"></text>
|
||||
<view class="invoice-heard-title">{{ state.data.status_text }}</view>
|
||||
</view>
|
||||
<view class="ss-flex ss-m-b-52">
|
||||
<view class="ss-m-r-20 invoice-heard-desc">预计可开发票金额:</view>
|
||||
<view class="invoice-heard-price">¥{{ state.data.amount }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="invoice-content ss-flex-col ss-col-center">
|
||||
<view class="ss-m-t-50 ss-m-b-42 invoice-content-title">增值税电子普通发票</view>
|
||||
<view class="ss-flex ss-m-b-64">
|
||||
<view v-for="(item, index) in state.info" :key="item.title">
|
||||
<view class="log-icon ss-flex">
|
||||
<text class="sicon-circlecheck" v-if="statusNum >= index"></text>
|
||||
<text class="sicon-unchecked" v-else></text>
|
||||
<view
|
||||
v-if="state.info.length - 1 != index"
|
||||
class="line"
|
||||
:class="statusNum >= index ? 'activity-color' : ''"
|
||||
></view>
|
||||
</view>
|
||||
<view class="log-title">{{ item.title }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="invoice-content-list ss-flex ss-row-between ss-col-top">
|
||||
<view class="">
|
||||
<view class="ss-flex">
|
||||
<view class="list-title">发票类型</view>
|
||||
<view class="list-desc">{{ state.data.type_text }}</view>
|
||||
</view>
|
||||
<view class="ss-flex">
|
||||
<view class="list-title">发票抬头</view>
|
||||
<view class="list-desc">{{ state.data.name }}</view>
|
||||
</view>
|
||||
<view class="ss-flex" v-if="state.data.type === 'company'">
|
||||
<view class="list-title">发票税号</view>
|
||||
<view class="list-desc">{{ state.data.tax_no }}</view>
|
||||
</view>
|
||||
<view class="ss-flex" v-if="state.data.status === 'finish'">
|
||||
<view class="list-title">实开金额</view>
|
||||
<view class="list-desc">¥{{ state.data.invoice_amount }}</view>
|
||||
</view>
|
||||
<view class="ss-flex" v-if="state.data.status === 'finish'">
|
||||
<view class="list-title">开票时间</view>
|
||||
<view class="list-desc">{{ state.data.finish_time }}</view>
|
||||
</view>
|
||||
<view class="ss-flex">
|
||||
<view class="list-title">申请时间</view>
|
||||
<view class="list-desc">{{ state.data.create_time }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
class="invoice-content-img ss-flex-col ss-col-center"
|
||||
v-if="state.data.status === 'finish'"
|
||||
>
|
||||
<su-image
|
||||
class="invoice-img"
|
||||
isPreview
|
||||
:previewList="state.jointImage"
|
||||
:current="0"
|
||||
:src="sheep.$url.static('/static/img/shop/order/invoice_thumb.png')"
|
||||
:height="110"
|
||||
mode="scaleToFill"
|
||||
v-if="state.jointImage[0].substr(-4) != '.pdf'"
|
||||
></su-image>
|
||||
<!-- TODO: 发票为多个pdf时 -->
|
||||
<view v-if="state.jointImage[0].substr(-4) == '.pdf'" @tap="onInvoice">
|
||||
<image
|
||||
:src="sheep.$url.static('/static/img/shop/order/invoice_thumb.png')"
|
||||
class="invoice-img"
|
||||
></image>
|
||||
</view>
|
||||
<view class="invoice-img-num">共{{ state.numImage }}张</view>
|
||||
<view class="invoice-img-title">点击预览发票</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="invoice-order ss-m-t-20">
|
||||
<view class="goods-box" v-for="item in state.data.order_items" :key="item.id">
|
||||
<s-goods-item
|
||||
:img="item.goods_image"
|
||||
:title="item.goods_title"
|
||||
:skuText="item.goods_sku_text"
|
||||
:price="item.goods_price"
|
||||
:num="item.goods_num"
|
||||
/>
|
||||
</view>
|
||||
<view class="invoice-order-list">
|
||||
<view class="ss-flex">
|
||||
<view class="list-title">订单状态</view>
|
||||
<view class="list-desc">{{ state.data.order?.status_text }}</view>
|
||||
</view>
|
||||
<view class="ss-flex">
|
||||
<view class="list-title">订单编号</view>
|
||||
<view class="list-desc">{{ state.data.order?.order_sn }}</view>
|
||||
</view>
|
||||
<view class="ss-flex">
|
||||
<view class="list-title">下单时间</view>
|
||||
<view class="list-desc">{{ state.data.order?.create_time }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { computed, reactive } from 'vue';
|
||||
|
||||
const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
|
||||
const headerBg = sheep.$url.css('/static/img/shop/order/invoice_bg.png');
|
||||
const state = reactive({
|
||||
info: [
|
||||
{
|
||||
title: '订单提交',
|
||||
},
|
||||
{
|
||||
title: '等待开票',
|
||||
},
|
||||
{
|
||||
title: '开票完成',
|
||||
},
|
||||
],
|
||||
data: {},
|
||||
jointImage: [],
|
||||
numImage: 0,
|
||||
});
|
||||
const statusNum = computed(() => {
|
||||
if (state.data.status === 'finish') {
|
||||
return 2;
|
||||
} else if (state.data.status === 'waiting') {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
function onInvoice() {
|
||||
// #ifdef H5
|
||||
window.open(state.jointImage);
|
||||
// #endif
|
||||
// #ifdef MP || APP-PLUS
|
||||
uni.downloadFile({
|
||||
url: state.jointImage[0],
|
||||
success: function (res) {
|
||||
var filePath = res.tempFilePath;
|
||||
uni.openDocument({
|
||||
filePath: filePath,
|
||||
showMenu: true,
|
||||
success: function (res) {
|
||||
console.log('打开文档成功');
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
// #endif
|
||||
}
|
||||
async function getInvoiceDetail(id) {
|
||||
const { data } = await sheep.$api.order.invoice(id);
|
||||
state.data = data;
|
||||
state.data.download_urls?.forEach((i, index) => {
|
||||
state.numImage = index + 1;
|
||||
if (i.substr(-4) != '.pdf') {
|
||||
state.jointImage.push(sheep.$url.static(i));
|
||||
} else {
|
||||
state.jointImage.push(sheep.$url.static(i));
|
||||
}
|
||||
});
|
||||
}
|
||||
onLoad((options) => {
|
||||
getInvoiceDetail(options.invoiceId);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.invoice-heard {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background: v-bind(headerBg) no-repeat,
|
||||
linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
background-size: 750rpx 100%;
|
||||
.sicon-warning-line {
|
||||
color: #fff;
|
||||
font-size: 34rpx;
|
||||
}
|
||||
.sicon-check-line {
|
||||
color: #fff;
|
||||
font-size: 34rpx;
|
||||
}
|
||||
.invoice-heard-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
margin-left: 8rpx;
|
||||
line-height: normal;
|
||||
}
|
||||
.invoice-heard-desc {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
}
|
||||
.invoice-heard-price {
|
||||
font-size: 28rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
.invoice-content {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
background: #ffffff;
|
||||
border-radius: 20rpx;
|
||||
margin-top: -16rpx;
|
||||
.invoice-content-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
}
|
||||
.log-icon {
|
||||
.sicon-unchecked {
|
||||
color: #c2bec2;
|
||||
font-size: 44rpx;
|
||||
}
|
||||
.sicon-circlecheck {
|
||||
color: #e60a00;
|
||||
font-size: 44rpx;
|
||||
}
|
||||
.line {
|
||||
width: 158rpx;
|
||||
height: 6rpx;
|
||||
background: #f2f2f2;
|
||||
border: 2rpx solid #ffffff;
|
||||
}
|
||||
.activity-color {
|
||||
background: #e60a00;
|
||||
}
|
||||
}
|
||||
.log-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
margin-left: -26rpx;
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
.invoice-content-list {
|
||||
width: 100%;
|
||||
padding: 0 46rpx 0 30rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.list-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #999999;
|
||||
margin-right: 44rpx;
|
||||
margin-bottom: 36rpx;
|
||||
}
|
||||
.list-desc {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
margin-bottom: 36rpx;
|
||||
}
|
||||
.invoice-img {
|
||||
width: 200rpx;
|
||||
height: 110rpx;
|
||||
}
|
||||
.invoice-img-num {
|
||||
width: 216rpx;
|
||||
height: 40rpx;
|
||||
background: rgba(#000000, 0.45);
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
text-align: center;
|
||||
margin-top: -30rpx;
|
||||
z-index: 1;
|
||||
}
|
||||
.invoice-img-title {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
.invoice-order {
|
||||
width: 100%;
|
||||
padding-top: 30rpx;
|
||||
box-sizing: border-box;
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
.goods-box {
|
||||
border-bottom: 2rpx solid #dfdfdf;
|
||||
}
|
||||
.invoice-order-list {
|
||||
padding: 40rpx 24rpx 0 24rpx;
|
||||
.list-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #999999;
|
||||
margin-right: 44rpx;
|
||||
margin-bottom: 36rpx;
|
||||
}
|
||||
.list-desc {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
margin-bottom: 36rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
586
pages/order/list.vue
Normal file
586
pages/order/list.vue
Normal file
@@ -0,0 +1,586 @@
|
||||
<!-- 页面 -->
|
||||
<template>
|
||||
<s-layout title="我的订单">
|
||||
<su-sticky bgColor="#fff">
|
||||
<su-tabs :list="tabMaps" :scrollable="false" @change="onTabsChange" :current="state.currentTab"></su-tabs>
|
||||
</su-sticky>
|
||||
<s-empty v-if="state.pagination.total === 0" icon="/static/order-empty.png" text="暂无订单"></s-empty>
|
||||
<view v-if="state.pagination.total > 0">
|
||||
<view class="bg-white order-list-card-box ss-r-10 ss-m-t-14 ss-m-20" v-for="order in state.pagination.data"
|
||||
:key="order.id" @tap="onOrderDetail(order.id)">
|
||||
<view class="order-card-header ss-flex ss-col-center ss-row-between ss-p-x-20">
|
||||
<view class="order-no">订单号:{{ order.no }}</view>
|
||||
<view class="order-state ss-font-26" :class="formatOrderColor(order.status_code)">{{
|
||||
order.status
|
||||
}}</view>
|
||||
</view>
|
||||
<view class="border-bottom" v-for="item in order.items" :key="item.id">
|
||||
<s-goods-item :img="item.picUrl" :title="item.spuName"
|
||||
:skuText="item.properties.length>1? item.properties.reduce((items2,items)=>items2.valueName+' '+items.valueName):item.properties[0].valueName"
|
||||
:price="item.price/100" :score="order.score_amount" :num="item.count">
|
||||
<template #tool>
|
||||
<view class="ss-flex">
|
||||
<!-- <button class="ss-reset-button apply-btn" v-if="item.btns.includes('aftersale')"
|
||||
@tap.stop="
|
||||
sheep.$router.go('/pages/order/aftersale/apply', {
|
||||
item: JSON.stringify(item),
|
||||
})
|
||||
">
|
||||
申请售后
|
||||
</button>
|
||||
<button class="ss-reset-button apply-btn" v-if="item.btns.includes('re_aftersale')"
|
||||
@tap.stop="
|
||||
sheep.$router.go('/pages/order/aftersale/apply', {
|
||||
item: JSON.stringify(item),
|
||||
})
|
||||
">
|
||||
重新售后
|
||||
</button>
|
||||
|
||||
<button class="ss-reset-button apply-btn" v-if="item.btns.includes('aftersale_info')"
|
||||
@tap.stop="
|
||||
sheep.$router.go('/pages/order/aftersale/detail', {
|
||||
id: item.ext.aftersale_id,
|
||||
})
|
||||
">
|
||||
售后详情
|
||||
</button>
|
||||
<button class="ss-reset-button apply-btn" v-if="item.btns.includes('buy_again')"
|
||||
@tap.stop="
|
||||
sheep.$router.go('/pages/goods/index', {
|
||||
id: item.goods_id,
|
||||
})
|
||||
">
|
||||
再次购买
|
||||
</button> -->
|
||||
</view>
|
||||
</template>
|
||||
</s-goods-item>
|
||||
</view>
|
||||
<view class="pay-box ss-m-t-30 ss-flex ss-row-right ss-p-r-20">
|
||||
<!-- <view v-if="order.total_discount_fee > 0" class="ss-flex ss-col-center ss-m-r-8">
|
||||
<view class="discounts-title">优惠:¥</view>
|
||||
<view class="discounts-money">{{ order.total_discount_fee }}</view>
|
||||
</view> -->
|
||||
<!-- <view class="ss-flex ss-col-center ss-m-r-8">
|
||||
<view class="discounts-title">运费:¥</view>
|
||||
<view class="discounts-money">{{ order.dispatch_amount }}</view>
|
||||
</view> -->
|
||||
<view class="ss-flex ss-col-center">
|
||||
<view class="discounts-title pay-color">共{{count}}件商品,总金额:</view>
|
||||
<view class="discounts-money pay-color" v-if="Number(order.payPrice) > 0">
|
||||
¥{{ order.payPrice/100 }}</view>
|
||||
<view v-if="order.score_amount && Number(order.payPrice) > 0">+</view>
|
||||
<view class="discounts-money pay-color ss-flex ss-col-center" v-if="order.score_amount">
|
||||
<image :src="sheep.$url.static('/static/img/shop/goods/score1.svg')" class="score-img">
|
||||
</image>
|
||||
<view>{{ order.score_amount }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- :class="order.btns.length > 3 ? 'ss-row-between' : 'ss-row-right'" -->
|
||||
<view class="order-card-footer ss-flex ss-col-center ss-p-x-20">
|
||||
<!-- <su-popover>
|
||||
<button class="more-btn ss-reset-button" @click.stop>更多</button>
|
||||
<template #content>
|
||||
<view class="more-item-box">
|
||||
<view class="more-item ss-flex ss-col-center ss-reset-button">
|
||||
<view class="item-title">删除订单</view>
|
||||
</view>
|
||||
<view class="more-item ss-flex ss-col-center ss-reset-button">
|
||||
<view class="item-title">查看发票</view>
|
||||
</view>
|
||||
<view class="more-item ss-flex ss-col-center ss-reset-button">
|
||||
<view class="item-title">评价晒单</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</su-popover> -->
|
||||
<view class="ss-flex ss-col-center">
|
||||
<!-- <button v-if="order.btns.includes('groupon')" class="tool-btn ss-reset-button"
|
||||
@tap.stop="onOrderGroupon(order)">
|
||||
{{ order.status_code === 'groupon_ing' ? '邀请拼团' : '拼团详情' }}
|
||||
</button>
|
||||
<button v-if="order.btns.includes('invoice')" class="tool-btn ss-reset-button"
|
||||
@tap.stop="onOrderInvoice(order.invoice?.id)">
|
||||
查看发票
|
||||
</button>
|
||||
<button v-if="order.btns.length === 0" class="tool-btn ss-reset-button"
|
||||
@tap.stop="onOrderDetail(order.order_sn)">
|
||||
查看详情
|
||||
</button>
|
||||
|
||||
<button v-if="order.btns.includes('confirm')" class="tool-btn ss-reset-button"
|
||||
@tap.stop="onConfirm(order)">
|
||||
确认收货
|
||||
</button>
|
||||
|
||||
<button v-if="order.btns.includes('express')" class="tool-btn ss-reset-button"
|
||||
@tap.stop="onExpress(order.id)">
|
||||
查看物流
|
||||
</button>
|
||||
|
||||
<button v-if="order.btns.includes('apply_refund')" class="tool-btn ss-reset-button"
|
||||
@tap.stop="onRefund(order.id)">
|
||||
申请退款
|
||||
</button>
|
||||
<button v-if="order.btns.includes('re_apply_refund')" class="tool-btn ss-reset-button"
|
||||
@tap.stop="onRefund(order.id)">
|
||||
重新退款
|
||||
</button>
|
||||
|
||||
<button v-if="order.btns.includes('cancel')" class="tool-btn ss-reset-button"
|
||||
@tap.stop="onCancel(order.id)">
|
||||
取消订单
|
||||
</button>
|
||||
|
||||
<button v-if="order.btns.includes('comment')" class="tool-btn ss-reset-button"
|
||||
@tap.stop="onComment(order.order_sn)">
|
||||
评价晒单
|
||||
</button>
|
||||
|
||||
<button v-if="order.btns.includes('delete')" class="delete-btn ss-reset-button"
|
||||
@tap.stop="onDelete(order.id)">
|
||||
删除订单
|
||||
</button>
|
||||
|
||||
<button v-if="order.btns.includes('pay')" class="tool-btn ss-reset-button ui-BG-Main-Gradient"
|
||||
@tap.stop="onPay(order.order_sn)">
|
||||
继续支付
|
||||
</button> -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<uni-load-more v-if="state.pagination.total > 0" :status="state.loadStatus" :content-text="{
|
||||
contentdown: '上拉加载更多',
|
||||
}" @tap="loadmore" />
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
computed,
|
||||
reactive
|
||||
} from 'vue';
|
||||
import {
|
||||
onLoad,
|
||||
onReachBottom,
|
||||
onPullDownRefresh
|
||||
} from '@dcloudio/uni-app';
|
||||
import {
|
||||
formatOrderColor
|
||||
} from '@/sheep/hooks/useGoods';
|
||||
import sheep from '@/sheep';
|
||||
import _ from 'lodash';
|
||||
import {
|
||||
isEmpty
|
||||
} from 'lodash';
|
||||
|
||||
const pagination = {
|
||||
data: [],
|
||||
current_page: 1,
|
||||
total: 1,
|
||||
last_page: 1,
|
||||
};
|
||||
// 数据
|
||||
const state = reactive({
|
||||
currentTab: 0,
|
||||
pagination: {
|
||||
data: [],
|
||||
current_page: 1,
|
||||
total: 1,
|
||||
last_page: 1,
|
||||
},
|
||||
loadStatus: '',
|
||||
deleteOrderId: 0,
|
||||
error: 0,
|
||||
});
|
||||
|
||||
const tabMaps = [{
|
||||
name: '全部',
|
||||
// value: 'all',
|
||||
},
|
||||
{
|
||||
name: '待付款',
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
name: '待发货',
|
||||
value: 10,
|
||||
},
|
||||
{
|
||||
name: '待收货',
|
||||
value: 20,
|
||||
},
|
||||
{
|
||||
name: '待评价',
|
||||
value: 30,
|
||||
},
|
||||
];
|
||||
|
||||
// 切换选项卡
|
||||
function onTabsChange(e) {
|
||||
if (state.currentTab === e.index) return;
|
||||
|
||||
state.pagination = pagination;
|
||||
state.currentTab = e.index;
|
||||
|
||||
getOrderList();
|
||||
}
|
||||
|
||||
// 订单详情
|
||||
function onOrderDetail(orderSN) {
|
||||
sheep.$router.go('/pages/order/detail', {
|
||||
orderSN,
|
||||
});
|
||||
}
|
||||
|
||||
// 分享拼团
|
||||
function onOrderGroupon(order) {
|
||||
sheep.$router.go('/pages/activity/groupon/detail', {
|
||||
id: order.ext.groupon_id,
|
||||
});
|
||||
}
|
||||
|
||||
// 查看发票
|
||||
function onOrderInvoice(invoiceId) {
|
||||
sheep.$router.go('/pages/order/invoice', {
|
||||
invoiceId,
|
||||
});
|
||||
}
|
||||
|
||||
// 继续支付
|
||||
function onPay(orderSN) {
|
||||
sheep.$router.go('/pages/pay/index', {
|
||||
orderSN,
|
||||
});
|
||||
}
|
||||
|
||||
// 评价
|
||||
function onComment(orderSN) {
|
||||
sheep.$router.go('/pages/goods/comment/add', {
|
||||
orderSN,
|
||||
});
|
||||
}
|
||||
|
||||
// 确认收货
|
||||
async function onConfirm(order, ignore = false) {
|
||||
// 需开启确认收货组件
|
||||
// todo:
|
||||
// 1.怎么检测是否开启了发货组件功能?如果没有开启的话就不能在这里return出去
|
||||
// 2.如果开启了走mpConfirm方法,需要在App.vue的show方法中拿到确认收货结果
|
||||
let isOpenBusinessView = true;
|
||||
if (
|
||||
sheep.$platform.name === 'WechatMiniProgram' &&
|
||||
!isEmpty(order.wechat_extra_data) &&
|
||||
isOpenBusinessView &&
|
||||
!ignore
|
||||
) {
|
||||
mpConfirm(order);
|
||||
return;
|
||||
}
|
||||
|
||||
// 正常的确认收货流程
|
||||
|
||||
const {
|
||||
error
|
||||
} = await sheep.$api.order.confirm(order.id);
|
||||
if (error === 0) {
|
||||
state.pagination = pagination;
|
||||
getOrderList();
|
||||
}
|
||||
}
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
// 小程序确认收货组件
|
||||
function mpConfirm(order) {
|
||||
if (!wx.openBusinessView) {
|
||||
sheep.$helper.toast(`请升级微信版本`);
|
||||
return;
|
||||
}
|
||||
wx.openBusinessView({
|
||||
businessType: 'weappOrderConfirm',
|
||||
extraData: {
|
||||
merchant_id: '1481069012',
|
||||
merchant_trade_no: order.wechat_extra_data.merchant_trade_no,
|
||||
transaction_id: order.wechat_extra_data.transaction_id,
|
||||
},
|
||||
success(response) {
|
||||
console.log('success:', response);
|
||||
if (response.errMsg === 'openBusinessView:ok') {
|
||||
if (response.extraData.status === 'success') {
|
||||
onConfirm(order, true);
|
||||
}
|
||||
}
|
||||
},
|
||||
fail(error) {
|
||||
console.log('error:', error);
|
||||
},
|
||||
complete(result) {
|
||||
console.log('result:', result);
|
||||
},
|
||||
});
|
||||
}
|
||||
// #endif
|
||||
|
||||
// 查看物流
|
||||
async function onExpress(orderId) {
|
||||
sheep.$router.go('/pages/order/express/list', {
|
||||
orderId,
|
||||
});
|
||||
}
|
||||
|
||||
// 取消订单
|
||||
async function onCancel(orderId) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要取消订单吗?',
|
||||
success: async function(res) {
|
||||
if (res.confirm) {
|
||||
const {
|
||||
error,
|
||||
data
|
||||
} = await sheep.$api.order.cancel(orderId);
|
||||
if (error === 0) {
|
||||
let index = state.pagination.data.findIndex((order) => order.id === orderId);
|
||||
state.pagination.data[index] = data;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// 删除订单
|
||||
function onDelete(orderId) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要删除订单吗?',
|
||||
success: async function(res) {
|
||||
if (res.confirm) {
|
||||
const {
|
||||
error,
|
||||
data
|
||||
} = await sheep.$api.order.delete(orderId);
|
||||
if (error === 0) {
|
||||
let index = state.pagination.data.findIndex((order) => order.id === orderId);
|
||||
state.pagination.data.splice(index, 1);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// 申请退款
|
||||
async function onRefund(orderId) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要申请退款吗?',
|
||||
success: async function(res) {
|
||||
if (res.confirm) {
|
||||
// #ifdef MP
|
||||
sheep.$platform.useProvider('wechat').subscribeMessage('order_refund');
|
||||
// #endif
|
||||
const {
|
||||
error,
|
||||
data
|
||||
} = await sheep.$api.order.applyRefund(orderId);
|
||||
if (error === 0) {
|
||||
let index = state.pagination.data.findIndex((order) => order.id === orderId);
|
||||
state.pagination.data[index] = data;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// 获取订单列表
|
||||
async function getOrderList(page = 1, list_rows = 5) {
|
||||
state.loadStatus = 'loading';
|
||||
let res = await sheep.$api.order.list({
|
||||
status: tabMaps[state.currentTab].value,
|
||||
pageSize: list_rows,
|
||||
pageNo: page,
|
||||
commentStatus: tabMaps[state.currentTab].value == 30 ? false : null
|
||||
});
|
||||
state.error = res.code;
|
||||
if (res.code === 0) {
|
||||
let orderList = _.concat(state.pagination.data, res.data.list);
|
||||
state.pagination = {
|
||||
...res.data,
|
||||
data: orderList,
|
||||
};
|
||||
console.log(state.pagination)
|
||||
if (state.pagination.data.length < state.pagination.total) {
|
||||
state.loadStatus = 'more';
|
||||
} else {
|
||||
state.loadStatus = 'noMore';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onLoad(async (options) => {
|
||||
if (options.type) {
|
||||
state.currentTab = options.type;
|
||||
}
|
||||
getOrderList();
|
||||
});
|
||||
|
||||
// 加载更多
|
||||
function loadmore() {
|
||||
if (state.loadStatus !== 'noMore') {
|
||||
getOrderList(parseInt((state.pagination.data.length / 5) + 1));
|
||||
}
|
||||
}
|
||||
|
||||
// 上拉加载更多
|
||||
onReachBottom(() => {
|
||||
loadmore();
|
||||
});
|
||||
|
||||
//下拉刷新
|
||||
onPullDownRefresh(() => {
|
||||
state.pagination = pagination;
|
||||
getOrderList();
|
||||
setTimeout(function() {
|
||||
uni.stopPullDownRefresh();
|
||||
}, 800);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.score-img {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
margin: 0 4rpx;
|
||||
}
|
||||
|
||||
.tool-btn {
|
||||
width: 160rpx;
|
||||
height: 60rpx;
|
||||
background: #f6f6f6;
|
||||
font-size: 26rpx;
|
||||
border-radius: 30rpx;
|
||||
margin-right: 10rpx;
|
||||
|
||||
&:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.delete-btn {
|
||||
width: 160rpx;
|
||||
height: 56rpx;
|
||||
color: #ff3000;
|
||||
background: #fee;
|
||||
border-radius: 28rpx;
|
||||
font-size: 26rpx;
|
||||
margin-right: 10rpx;
|
||||
line-height: normal;
|
||||
|
||||
&:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.apply-btn {
|
||||
width: 140rpx;
|
||||
height: 50rpx;
|
||||
border-radius: 25rpx;
|
||||
font-size: 24rpx;
|
||||
border: 2rpx solid #dcdcdc;
|
||||
line-height: normal;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
.swiper-box {
|
||||
flex: 1;
|
||||
|
||||
.swiper-item {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.order-list-card-box {
|
||||
.order-card-header {
|
||||
height: 80rpx;
|
||||
|
||||
.order-no {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.order-state {}
|
||||
}
|
||||
|
||||
.pay-box {
|
||||
.discounts-title {
|
||||
font-size: 24rpx;
|
||||
line-height: normal;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.discounts-money {
|
||||
font-size: 24rpx;
|
||||
line-height: normal;
|
||||
color: #999;
|
||||
font-family: OPPOSANS;
|
||||
}
|
||||
|
||||
.pay-color {
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.order-card-footer {
|
||||
height: 100rpx;
|
||||
|
||||
.more-item-box {
|
||||
padding: 20rpx;
|
||||
|
||||
.more-item {
|
||||
height: 60rpx;
|
||||
|
||||
.title {
|
||||
font-size: 26rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.more-btn {
|
||||
color: $dark-9;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 154rpx;
|
||||
color: #333333;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.uni-tooltip-popup) {
|
||||
background: var(--ui-BG);
|
||||
}
|
||||
|
||||
.warning-color {
|
||||
color: #faad14;
|
||||
}
|
||||
|
||||
.danger-color {
|
||||
color: #ff3000;
|
||||
}
|
||||
|
||||
.success-color {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.info-color {
|
||||
color: #999999;
|
||||
}
|
||||
</style>
|
||||
237
pages/pay/components/account-info-modal.vue
Normal file
237
pages/pay/components/account-info-modal.vue
Normal file
@@ -0,0 +1,237 @@
|
||||
<template>
|
||||
<su-popup :show="show" class="add-bank-wrap" @close="hideModal">
|
||||
<view class="ss-modal-box bg-white ss-flex-col">
|
||||
<view class="modal-header ss-flex-col ss-col-left">
|
||||
<text v-if="props.modelValue.type === 'bank'" class="modal-title ss-m-b-20">
|
||||
绑定银行卡
|
||||
</text>
|
||||
<text v-if="props.modelValue.type === 'wechat'" class="modal-title ss-m-b-20">
|
||||
绑定微信
|
||||
</text>
|
||||
<text v-if="props.modelValue.type === 'alipay'" class="modal-title ss-m-b-20">
|
||||
绑定支付宝
|
||||
</text>
|
||||
</view>
|
||||
<view class="modal-content ss-flex-1 ss-p-b-100">
|
||||
<block v-if="props.modelValue.type === 'bank'">
|
||||
<uni-forms
|
||||
ref="form"
|
||||
:model="state.bank.model"
|
||||
:rules="state.bank.rules"
|
||||
validateTrigger="bind"
|
||||
labelWidth="160"
|
||||
labelAlign="center"
|
||||
border
|
||||
:labelStyle="{ fontWeight: 'bold' }"
|
||||
>
|
||||
<uni-forms-item name="account_name" label="持卡人">
|
||||
<uni-easyinput
|
||||
:inputBorder="false"
|
||||
placeholder="请输入持卡人"
|
||||
v-model="state.bank.model.account_name"
|
||||
/>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item name="account_header" label="开户行">
|
||||
<uni-easyinput
|
||||
:inputBorder="false"
|
||||
placeholder="请输入开户行"
|
||||
v-model="state.bank.model.account_header"
|
||||
/>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item name="account_no" label="银行卡号">
|
||||
<uni-easyinput
|
||||
type="number"
|
||||
:inputBorder="false"
|
||||
placeholder="请输入银行卡号"
|
||||
v-model="state.bank.model.account_no"
|
||||
/>
|
||||
</uni-forms-item>
|
||||
</uni-forms>
|
||||
</block>
|
||||
|
||||
<block v-if="props.modelValue.type === 'wechat'">
|
||||
<uni-forms
|
||||
ref="form"
|
||||
:model="state.wechat.model"
|
||||
:rules="state.wechat.rules"
|
||||
validateTrigger="bind"
|
||||
labelWidth="160"
|
||||
labelAlign="center"
|
||||
border
|
||||
:labelStyle="{ fontWeight: 'bold' }"
|
||||
>
|
||||
<uni-forms-item name="account_name" label="真实姓名">
|
||||
<uni-easyinput
|
||||
:inputBorder="false"
|
||||
placeholder="请输入您的真实姓名"
|
||||
v-model="state.wechat.model.account_name"
|
||||
/>
|
||||
</uni-forms-item>
|
||||
</uni-forms>
|
||||
</block>
|
||||
|
||||
<block v-if="props.modelValue.type === 'alipay'">
|
||||
<uni-forms
|
||||
ref="form"
|
||||
:model="state.alipay.model"
|
||||
:rules="state.alipay.rules"
|
||||
validateTrigger="bind"
|
||||
labelWidth="160"
|
||||
labelAlign="center"
|
||||
border
|
||||
:labelStyle="{ fontWeight: 'bold' }"
|
||||
>
|
||||
<uni-forms-item name="account_name" label="真实姓名">
|
||||
<uni-easyinput
|
||||
:inputBorder="false"
|
||||
placeholder="请输入您的真实姓名"
|
||||
v-model="state.alipay.model.account_name"
|
||||
/>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item name="account_no" label="支付宝">
|
||||
<uni-easyinput
|
||||
:inputBorder="false"
|
||||
placeholder="请输入支付宝 邮箱/手机号"
|
||||
v-model="state.alipay.model.account_no"
|
||||
/>
|
||||
</uni-forms-item>
|
||||
</uni-forms>
|
||||
</block>
|
||||
</view>
|
||||
<view class="modal-footer ss-flex ss-row-center ss-col-center">
|
||||
<button class="ss-reset-button save-btn" @tap="onSave">保存</button>
|
||||
</view>
|
||||
</view>
|
||||
</su-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, unref, watchPostEffect, watch } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import { realName, bankName, bankCode, alipayAccount } from '@/sheep/validate/form';
|
||||
|
||||
const form = ref(null);
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newValue, oldValue) => {
|
||||
setModelValue(newValue);
|
||||
},
|
||||
);
|
||||
|
||||
function setModelValue(modelValue) {
|
||||
Object.keys(state[modelValue.type].model).forEach((key) => {
|
||||
state[modelValue.type].model[key] = modelValue[key];
|
||||
});
|
||||
}
|
||||
|
||||
const emits = defineEmits(['update:modelValue', 'close']);
|
||||
// 数据
|
||||
const state = reactive({
|
||||
bank: {
|
||||
model: {
|
||||
account_name: '',
|
||||
account_header: '',
|
||||
account_no: '',
|
||||
},
|
||||
rules: {
|
||||
account_name: realName,
|
||||
account_header: bankName,
|
||||
account_no: bankCode,
|
||||
},
|
||||
},
|
||||
alipay: {
|
||||
model: {
|
||||
account_name: '',
|
||||
account_no: '',
|
||||
},
|
||||
rules: {
|
||||
account_name: realName,
|
||||
account_no: alipayAccount,
|
||||
},
|
||||
},
|
||||
wechat: {
|
||||
model: {
|
||||
account_name: '',
|
||||
},
|
||||
rules: {
|
||||
account_name: realName,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
setModelValue(props.modelValue);
|
||||
|
||||
const hideModal = () => {
|
||||
emits('close');
|
||||
};
|
||||
const onSave = async () => {
|
||||
const validate = await unref(form)
|
||||
.validate()
|
||||
.catch((error) => {
|
||||
'error: ', error;
|
||||
});
|
||||
if (!validate) return;
|
||||
let data = {
|
||||
type: props.modelValue.type,
|
||||
account_header: state[props.modelValue.type].model.account_header,
|
||||
account_name: state[props.modelValue.type].model.account_name,
|
||||
account_no: state[props.modelValue.type].model.account_no,
|
||||
};
|
||||
let res = await sheep.$api.user.account.save(data);
|
||||
if (res.error === 0) {
|
||||
emits('update:modelValue', res.data);
|
||||
hideModal();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ss-modal-box {
|
||||
border-radius: 30rpx 30rpx 0 0;
|
||||
max-height: 1000rpx;
|
||||
|
||||
.modal-header {
|
||||
position: relative;
|
||||
padding: 60rpx 40rpx 40rpx;
|
||||
|
||||
.modal-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
position: absolute;
|
||||
top: 10rpx;
|
||||
right: 20rpx;
|
||||
font-size: 46rpx;
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
height: 120rpx;
|
||||
|
||||
.save-btn {
|
||||
width: 710rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
178
pages/pay/components/account-type-select.vue
Normal file
178
pages/pay/components/account-type-select.vue
Normal file
@@ -0,0 +1,178 @@
|
||||
<template>
|
||||
<su-popup :show="show" class="ss-checkout-counter-wrap" @close="hideModal">
|
||||
<view class="ss-modal-box bg-white ss-flex-col">
|
||||
<view class="modal-header ss-flex-col ss-col-left">
|
||||
<text class="modal-title ss-m-b-20">选择提现方式</text>
|
||||
</view>
|
||||
<view class="modal-content ss-flex-1 ss-p-b-100">
|
||||
<radio-group @change="onChange">
|
||||
<label
|
||||
class="container-list ss-p-l-34 ss-p-r-24 ss-flex ss-col-center ss-row-center"
|
||||
v-for="(item, index) in typeList"
|
||||
:key="index"
|
||||
>
|
||||
<view class="container-icon ss-flex ss-m-r-20">
|
||||
<image :src="sheep.$url.static(item.icon)" />
|
||||
</view>
|
||||
<view class="ss-flex-1">{{ item.title }}</view>
|
||||
|
||||
<radio
|
||||
:value="item.value"
|
||||
color="var(--ui-BG-Main)"
|
||||
:checked="item.value === state.currentValue"
|
||||
:disabled="!methods.includes(item.value)"
|
||||
/>
|
||||
</label>
|
||||
</radio-group>
|
||||
</view>
|
||||
<view class="modal-footer ss-flex ss-row-center ss-col-center">
|
||||
<button class="ss-reset-button save-btn" @tap="onConfirm">确定</button>
|
||||
</view>
|
||||
</view>
|
||||
</su-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, onBeforeMount, nextTick } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default() {},
|
||||
},
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
methods: {
|
||||
type: Array,
|
||||
default: [],
|
||||
},
|
||||
});
|
||||
const emits = defineEmits(['update:modelValue', 'change', 'close']);
|
||||
const state = reactive({
|
||||
currentValue: '',
|
||||
});
|
||||
const typeList = [
|
||||
{
|
||||
icon: '/static/img/shop/pay/wechat.png',
|
||||
title: '微信零钱',
|
||||
value: 'wechat',
|
||||
},
|
||||
{
|
||||
icon: '/static/img/shop/pay/alipay.png',
|
||||
title: '支付宝账户',
|
||||
value: 'alipay',
|
||||
},
|
||||
{
|
||||
icon: '/static/img/shop/pay/bank.png',
|
||||
title: '银行卡转账',
|
||||
value: 'bank',
|
||||
},
|
||||
];
|
||||
const getWalletAccountInfo = async () => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
let res = await sheep.$api.user.account.info({
|
||||
type: state.currentValue,
|
||||
});
|
||||
if (res.error === 0) {
|
||||
if (!props.methods.includes(res.data.type)) {
|
||||
return;
|
||||
}
|
||||
state.currentValue = res.data.type;
|
||||
emits('update:modelValue', {
|
||||
type: res.data.type,
|
||||
account_header: res.data.account_header,
|
||||
account_name: res.data.account_name,
|
||||
account_no: res.data.account_no,
|
||||
});
|
||||
} else {
|
||||
emits('update:modelValue', {
|
||||
type: state.currentValue,
|
||||
});
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
||||
function onChange(e) {
|
||||
state.currentValue = e.detail.value;
|
||||
}
|
||||
|
||||
const onConfirm = async () => {
|
||||
if (state.currentValue === '') {
|
||||
sheep.$helper.toast('请选择提现方式');
|
||||
return;
|
||||
}
|
||||
await getWalletAccountInfo();
|
||||
emits('close');
|
||||
};
|
||||
|
||||
const hideModal = () => {
|
||||
emits('close');
|
||||
};
|
||||
|
||||
onBeforeMount(async () => {
|
||||
await getWalletAccountInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ss-modal-box {
|
||||
border-radius: 30rpx 30rpx 0 0;
|
||||
max-height: 1000rpx;
|
||||
|
||||
.modal-header {
|
||||
position: relative;
|
||||
padding: 60rpx 40rpx 40rpx;
|
||||
|
||||
.modal-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
position: absolute;
|
||||
top: 10rpx;
|
||||
right: 20rpx;
|
||||
font-size: 46rpx;
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
overflow-y: auto;
|
||||
|
||||
.container-list {
|
||||
height: 96rpx;
|
||||
border-bottom: 2rpx solid rgba(#dfdfdf, 0.5);
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
|
||||
.container-icon {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
height: 120rpx;
|
||||
|
||||
.save-btn {
|
||||
width: 710rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
356
pages/pay/index.vue
Normal file
356
pages/pay/index.vue
Normal file
@@ -0,0 +1,356 @@
|
||||
<!-- 收银台 -->
|
||||
<template>
|
||||
<s-layout title="收银台">
|
||||
<view class="bg-white ss-modal-box ss-flex-col">
|
||||
<view class="modal-header ss-flex-col ss-col-center ss-row-center">
|
||||
<view class="money-box ss-m-b-20">
|
||||
<text class="money-text">{{ state.orderInfo.pay_fee }}</text>
|
||||
</view>
|
||||
<view class="time-text">
|
||||
<text>{{ payDescText }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="modal-content ss-flex-1">
|
||||
<view class="pay-title ss-p-l-30 ss-m-y-30">选择支付方式</view>
|
||||
<radio-group @change="onTapPay">
|
||||
<label class="pay-type-item" v-for="item in state.payMethods" :key="item.title">
|
||||
<view
|
||||
class="pay-item ss-flex ss-col-center ss-row-between ss-p-x-30 border-bottom"
|
||||
:class="{ 'disabled-pay-item': item.disabled }"
|
||||
v-if="allowedPayment.includes(item.value)"
|
||||
>
|
||||
<view class="ss-flex ss-col-center">
|
||||
<image
|
||||
class="pay-icon"
|
||||
v-if="item.disabled"
|
||||
:src="sheep.$url.static('/static/img/shop/pay/cod_disabled.png')"
|
||||
mode="aspectFit"
|
||||
></image>
|
||||
<image
|
||||
class="pay-icon"
|
||||
v-else
|
||||
:src="sheep.$url.static(item.icon)"
|
||||
mode="aspectFit"
|
||||
></image>
|
||||
<text class="pay-title">{{ item.title }}</text>
|
||||
</view>
|
||||
<view class="check-box ss-flex ss-col-center ss-p-l-10">
|
||||
<view class="userInfo-money ss-m-r-10" v-if="item.value == 'money'">
|
||||
余额: {{ userInfo.money }}元
|
||||
</view>
|
||||
<view
|
||||
class="userInfo-money ss-m-r-10"
|
||||
v-if="item.value == 'offline' && item.disabled"
|
||||
>
|
||||
部分商品不支持
|
||||
</view>
|
||||
<radio
|
||||
:value="item.value"
|
||||
color="var(--ui-BG-Main)"
|
||||
style="transform: scale(0.8)"
|
||||
:disabled="item.disabled"
|
||||
:checked="state.payment === item.value"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</label>
|
||||
</radio-group>
|
||||
</view>
|
||||
<!-- 工具 -->
|
||||
<view class="modal-footer ss-flex ss-row-center ss-col-center ss-m-t-80 ss-m-b-40">
|
||||
<button v-if="state.payStatus === 0" class="ss-reset-button past-due-btn">
|
||||
检测支付环境中
|
||||
</button>
|
||||
<button v-else-if="state.payStatus === -1" class="ss-reset-button past-due-btn" disabled>
|
||||
支付已过期
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
class="ss-reset-button save-btn"
|
||||
@tap="onPay"
|
||||
:disabled="state.payStatus !== 1"
|
||||
:class="{ 'disabled-btn': state.payStatus !== 1 }"
|
||||
>
|
||||
立即支付
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, reactive } from 'vue';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import sheep from '@/sheep';
|
||||
import { useDurationTime } from '@/sheep/hooks/useGoods';
|
||||
|
||||
const userInfo = computed(() => sheep.$store('user').userInfo);
|
||||
|
||||
// 检测支付环境
|
||||
const state = reactive({
|
||||
orderType: 'goods',
|
||||
payment: '',
|
||||
orderInfo: {},
|
||||
payStatus: 0, // 0=检测支付环境, -2=未查询到支付单信息, -1=支付已过期, 1=待支付,2=订单已支付
|
||||
payMethods: [],
|
||||
});
|
||||
|
||||
const allowedPayment = computed(() => {
|
||||
if(state.orderType === 'recharge') {
|
||||
return sheep.$store('app').platform.recharge_payment
|
||||
}
|
||||
return sheep.$store('app').platform.payment
|
||||
});
|
||||
|
||||
const payMethods = [
|
||||
{
|
||||
icon: '/static/img/shop/pay/wechat.png',
|
||||
title: '微信支付',
|
||||
value: 'wechat',
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
icon: '/static/img/shop/pay/alipay.png',
|
||||
title: '支付宝支付',
|
||||
value: 'alipay',
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
icon: '/static/img/shop/pay/wallet.png',
|
||||
title: '余额支付',
|
||||
value: 'money',
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
icon: '/static/img/shop/pay/apple.png',
|
||||
title: 'Apple Pay',
|
||||
value: 'apple',
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
icon: '/static/img/shop/pay/cod.png',
|
||||
title: '货到付款',
|
||||
value: 'offline',
|
||||
disabled: false,
|
||||
},
|
||||
];
|
||||
|
||||
const onPay = () => {
|
||||
if (state.payment === '') {
|
||||
sheep.$helper.toast('请选择支付方式');
|
||||
return;
|
||||
}
|
||||
if (state.payment === 'money') {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要支付吗?',
|
||||
success: function (res) {
|
||||
if (res.confirm) {
|
||||
sheep.$platform.pay(state.payment, state.orderType, state.orderInfo.order_sn);
|
||||
}
|
||||
},
|
||||
});
|
||||
} else if (state.payment === 'offline') {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要下单吗?',
|
||||
success: function (res) {
|
||||
if (res.confirm) {
|
||||
sheep.$platform.pay(state.payment, state.orderType, state.orderInfo.order_sn);
|
||||
}
|
||||
},
|
||||
});
|
||||
} else {
|
||||
sheep.$platform.pay(state.payment, state.orderType, state.orderInfo.order_sn);
|
||||
}
|
||||
};
|
||||
|
||||
const payDescText = computed(() => {
|
||||
if (state.payStatus === 2) {
|
||||
return '该订单已支付';
|
||||
}
|
||||
if (state.payStatus === 1 && state.orderInfo.ext.expired_time !== 0) {
|
||||
const time = useDurationTime(state.orderInfo.ext.expired_time);
|
||||
if (time.ms <= 0) {
|
||||
state.payStatus = -1;
|
||||
return '';
|
||||
}
|
||||
return `剩余支付时间 ${time.h}:${time.m}:${time.s} `;
|
||||
}
|
||||
if (state.payStatus === -2) {
|
||||
return '未查询到支付单信息';
|
||||
}
|
||||
|
||||
return '';
|
||||
});
|
||||
|
||||
function checkPayStatus() {
|
||||
if (state.orderInfo.status === 'unpaid') {
|
||||
state.payStatus = 1;
|
||||
return;
|
||||
}
|
||||
if (state.orderInfo.status === 'closed') {
|
||||
state.payStatus = -1;
|
||||
return;
|
||||
}
|
||||
state.payStatus = 2;
|
||||
}
|
||||
|
||||
function onTapPay(e) {
|
||||
state.payment = e.detail.value;
|
||||
}
|
||||
|
||||
async function setRechargeOrder(id) {
|
||||
const { data, error } = await sheep.$api.trade.order(id);
|
||||
if (error === 0) {
|
||||
state.orderInfo = data;
|
||||
state.payMethods = payMethods;
|
||||
checkPayStatus();
|
||||
} else {
|
||||
state.payStatus = -2;
|
||||
}
|
||||
}
|
||||
|
||||
async function setGoodsOrder(id) {
|
||||
const { data, error } = await sheep.$api.order.detail(id);
|
||||
if (error === 0) {
|
||||
state.orderInfo = data;
|
||||
if (state.orderInfo.ext.offline_status === 'none') {
|
||||
payMethods.forEach((item, index, array) => {
|
||||
if (item.value === 'offline') {
|
||||
array.splice(index, 1);
|
||||
}
|
||||
});
|
||||
} else if (state.orderInfo.ext.offline_status === 'disabled') {
|
||||
payMethods.forEach((item) => {
|
||||
if (item.value === 'offline') {
|
||||
item.disabled = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
state.payMethods = payMethods;
|
||||
checkPayStatus();
|
||||
} else {
|
||||
state.payStatus = -2;
|
||||
}
|
||||
}
|
||||
|
||||
onLoad((options) => {
|
||||
if (
|
||||
sheep.$platform.name === 'WechatOfficialAccount' &&
|
||||
sheep.$platform.os === 'ios' &&
|
||||
!sheep.$platform.landingPage.includes('pages/pay/index')
|
||||
) {
|
||||
location.reload();
|
||||
return;
|
||||
}
|
||||
let id = '';
|
||||
if (options.orderSN) {
|
||||
id = options.orderSN;
|
||||
}
|
||||
if (options.id) {
|
||||
id = options.id;
|
||||
}
|
||||
if (options.type === 'recharge') {
|
||||
state.orderType = 'recharge';
|
||||
// 充值订单
|
||||
setRechargeOrder(id);
|
||||
} else {
|
||||
state.orderType = 'goods';
|
||||
// 商品订单
|
||||
setGoodsOrder(id);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pay-icon {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
margin-right: 26rpx;
|
||||
}
|
||||
|
||||
.ss-modal-box {
|
||||
// max-height: 1000rpx;
|
||||
|
||||
.modal-header {
|
||||
position: relative;
|
||||
padding: 60rpx 20rpx 40rpx;
|
||||
|
||||
|
||||
.money-text {
|
||||
color: $red;
|
||||
font-size: 46rpx;
|
||||
font-weight: bold;
|
||||
font-family: OPPOSANS;
|
||||
|
||||
&::before {
|
||||
content: '¥';
|
||||
font-size: 30rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.time-text {
|
||||
font-size: 26rpx;
|
||||
color: $gray-b;
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
position: absolute;
|
||||
top: 10rpx;
|
||||
right: 20rpx;
|
||||
font-size: 46rpx;
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
overflow-y: auto;
|
||||
|
||||
.pay-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.pay-tip {
|
||||
font-size: 26rpx;
|
||||
color: #bbbbbb;
|
||||
}
|
||||
|
||||
.pay-item {
|
||||
height: 86rpx;
|
||||
}
|
||||
.disabled-pay-item {
|
||||
.pay-title {
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
|
||||
.userInfo-money {
|
||||
font-size: 26rpx;
|
||||
color: #bbbbbb;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.save-btn {
|
||||
width: 710rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
color: $white;
|
||||
}
|
||||
.disabled-btn {
|
||||
background: #e5e5e5;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.past-due-btn {
|
||||
width: 710rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
background-color: #999;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
171
pages/pay/recharge-log.vue
Normal file
171
pages/pay/recharge-log.vue
Normal file
@@ -0,0 +1,171 @@
|
||||
<template>
|
||||
<s-layout class="widthdraw-log-wrap" title="充值记录">
|
||||
<!-- 记录卡片 -->
|
||||
<view class="wallet-log-box ss-p-b-30">
|
||||
<view class="log-list" v-for="item in state.pagination.data" :key="item">
|
||||
<view class="head ss-flex ss-col-center ss-row-between">
|
||||
<view class="title">充值金额</view>
|
||||
<view
|
||||
class="num"
|
||||
:class="
|
||||
item.status === -1
|
||||
? 'danger-color'
|
||||
: item.status === 2
|
||||
? 'success-color'
|
||||
: 'warning-color'
|
||||
"
|
||||
>{{ item.pay_fee }}元</view
|
||||
>
|
||||
</view>
|
||||
<view class="status-box item ss-flex ss-col-center ss-row-between">
|
||||
<view class="item-title">支付状态</view>
|
||||
<view
|
||||
class="status-text"
|
||||
:class="
|
||||
item.status === -1
|
||||
? 'danger-color'
|
||||
: item.status === 2
|
||||
? 'success-color'
|
||||
: 'warning-color'
|
||||
"
|
||||
>{{ item.status_text }}</view
|
||||
>
|
||||
</view>
|
||||
<view class="time-box item ss-flex ss-col-center ss-row-between">
|
||||
<text class="item-title">充值渠道</text>
|
||||
<view class="time ss-ellipsis-1">{{ item.platform_text }}</view>
|
||||
</view>
|
||||
<view class="time-box item ss-flex ss-col-center ss-row-between">
|
||||
<text class="item-title">充值单号</text>
|
||||
<view class="time"> {{ item.order_sn }} </view>
|
||||
</view>
|
||||
<view class="time-box item ss-flex ss-col-center ss-row-between">
|
||||
<text class="item-title">充值时间</text>
|
||||
<view class="time"> {{ item.paid_time }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<s-empty
|
||||
v-if="state.pagination.total === 0"
|
||||
icon="/static/comment-empty.png"
|
||||
text="暂无充值记录"
|
||||
></s-empty>
|
||||
<uni-load-more
|
||||
v-if="state.pagination.total > 0"
|
||||
:status="state.loadStatus"
|
||||
:content-text="{
|
||||
contentdown: '上拉加载更多',
|
||||
}"
|
||||
@tap="loadmore"
|
||||
/>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
|
||||
import _ from 'lodash';
|
||||
const state = reactive({
|
||||
currentTab: 0,
|
||||
pagination: {
|
||||
data: [],
|
||||
current_page: 1,
|
||||
total: 1,
|
||||
last_page: 1,
|
||||
},
|
||||
loadStatus: '',
|
||||
});
|
||||
async function getLogList(page = 1, list_rows = 5) {
|
||||
const res = await sheep.$api.trade.orderLog({ type: 'recharge', list_rows, page });
|
||||
if (res.error === 0) {
|
||||
let logList = _.concat(state.pagination.data, res.data.data);
|
||||
state.pagination = {
|
||||
...res.data,
|
||||
data: logList,
|
||||
};
|
||||
if (state.pagination.current_page < state.pagination.last_page) {
|
||||
state.loadStatus = 'more';
|
||||
} else {
|
||||
state.loadStatus = 'noMore';
|
||||
}
|
||||
}
|
||||
}
|
||||
// 加载更多
|
||||
function loadmore() {
|
||||
if (state.loadStatus !== 'noMore') {
|
||||
getLogList(state.pagination.current_page + 1);
|
||||
}
|
||||
}
|
||||
onLoad(() => {
|
||||
getLogList();
|
||||
});
|
||||
onReachBottom(() => {
|
||||
loadmore();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// 记录卡片
|
||||
.log-list {
|
||||
min-height: 213rpx;
|
||||
background: $white;
|
||||
margin-bottom: 10rpx;
|
||||
padding-bottom: 10rpx;
|
||||
|
||||
.head {
|
||||
padding: 0 35rpx;
|
||||
height: 80rpx;
|
||||
border-bottom: 1rpx solid $gray-e;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: $dark-3;
|
||||
}
|
||||
|
||||
.num {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.item {
|
||||
padding: 0 30rpx 10rpx;
|
||||
|
||||
.item-icon {
|
||||
color: $gray-d;
|
||||
font-size: 36rpx;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
width: 180rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #c0c0c0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.warning-color {
|
||||
color: #faad14;
|
||||
}
|
||||
.danger-color {
|
||||
color: #ff4d4f;
|
||||
}
|
||||
.success-color {
|
||||
color: #67c23a;
|
||||
}
|
||||
</style>
|
||||
250
pages/pay/recharge.vue
Normal file
250
pages/pay/recharge.vue
Normal file
@@ -0,0 +1,250 @@
|
||||
<template>
|
||||
<s-layout title="充值" class="withdraw-wrap" navbar="inner">
|
||||
<view class="wallet-num-box ss-flex ss-col-center ss-row-between" :style="[
|
||||
{
|
||||
marginTop: '-' + Number(statusBarHeight + 88) + 'rpx',
|
||||
paddingTop: Number(statusBarHeight + 108) + 'rpx',
|
||||
},
|
||||
]">
|
||||
<view class="">
|
||||
<view class="num-title">当前余额(元)</view>
|
||||
<view class="wallet-num">{{ userInfo.money }}</view>
|
||||
</view>
|
||||
<button class="ss-reset-button log-btn" @tap="sheep.$router.go('/pages/pay/recharge-log')">充值记录</button>
|
||||
</view>
|
||||
<view class="recharge-box">
|
||||
<view class="recharge-card-box" v-if="state.data.status">
|
||||
<view class="input-label ss-m-b-50">充值金额</view>
|
||||
<view class="input-box ss-flex border-bottom ss-p-b-20" v-if="state.data.custom_status">
|
||||
<view class="unit">¥</view>
|
||||
<uni-easyinput v-model="state.recharge_money" type="digit" placeholder="请输入充值金额" :inputBorder="false">
|
||||
</uni-easyinput>
|
||||
</view>
|
||||
<view class="face-value-box ss-flex ss-flex-wrap ss-m-y-40">
|
||||
<button class="ss-reset-button face-value-btn" v-for="item in state.faceValueList" :key="item.money"
|
||||
:class="[{ 'btn-active': state.recharge_money == parseFloat(item.money) }]" @tap="onCard(item.money)">
|
||||
<text class="face-value-title">{{ item.money }}</text>
|
||||
<view v-if="item.gift" class="face-value-tag">
|
||||
送{{ item.gift }}{{ state.data.gift_type == 'money' ? '元' : '积分' }}</view>
|
||||
</button>
|
||||
</view>
|
||||
<button class="ss-reset-button save-btn ui-BG-Main-Gradient ss-m-t-60 ui-Shadow-Main" @tap="onConfirm">
|
||||
确认充值
|
||||
</button>
|
||||
</view>
|
||||
<view class="" v-if="state.data.status === 0"> 关闭充值 </view>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, reactive } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
|
||||
const userInfo = computed(() => sheep.$store('user').userInfo);
|
||||
const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
|
||||
const headerBg = sheep.$url.css('/static/img/shop/user/withdraw_bg.png');
|
||||
|
||||
const state = reactive({
|
||||
recharge_money: '',
|
||||
data: {},
|
||||
faceValueList: [],
|
||||
});
|
||||
// 点击卡片
|
||||
|
||||
function onCard(e) {
|
||||
state.recharge_money = e;
|
||||
}
|
||||
async function getRechargeTabs() {
|
||||
const res = await sheep.$api.trade.rechargeRules();
|
||||
if (res.error === 0) {
|
||||
state.data = res.data;
|
||||
state.data.status = res.data.status;
|
||||
state.faceValueList = res.data.quick_amounts;
|
||||
}
|
||||
}
|
||||
|
||||
function onChange(e) {
|
||||
state.data.gift_type = e.detail.value;
|
||||
}
|
||||
|
||||
async function onConfirm() {
|
||||
const { error, data } = await sheep.$api.trade.recharge({
|
||||
recharge_money: state.recharge_money,
|
||||
});
|
||||
if (error === 0) {
|
||||
// #ifdef MP
|
||||
sheep.$platform.useProvider('wechat').subscribeMessage('money_change');
|
||||
// #endif
|
||||
sheep.$router.go('/pages/pay/index', { orderSN: data.order_sn, type: 'recharge' });
|
||||
}
|
||||
}
|
||||
onLoad(() => {
|
||||
getRechargeTabs();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep() {
|
||||
.uni-input-input {
|
||||
font-family: OPPOSANS !important;
|
||||
}
|
||||
}
|
||||
|
||||
.wallet-num-box {
|
||||
padding: 0 40rpx 80rpx;
|
||||
background: var(--ui-BG-Main) v-bind(headerBg) center/750rpx 100% no-repeat;
|
||||
border-radius: 0 0 5% 5%;
|
||||
|
||||
.num-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: $white;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.wallet-num {
|
||||
font-size: 60rpx;
|
||||
font-weight: 500;
|
||||
color: $white;
|
||||
font-family: OPPOSANS;
|
||||
}
|
||||
|
||||
.log-btn {
|
||||
width: 170rpx;
|
||||
height: 60rpx;
|
||||
line-height: 60rpx;
|
||||
border: 1rpx solid $white;
|
||||
border-radius: 30rpx;
|
||||
padding: 0;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
.recharge-box {
|
||||
position: relative;
|
||||
padding: 0 30rpx;
|
||||
margin-top: -60rpx;
|
||||
}
|
||||
|
||||
.save-btn {
|
||||
width: 620rpx;
|
||||
height: 86rpx;
|
||||
border-radius: 44rpx;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.recharge-card-box {
|
||||
width: 690rpx;
|
||||
background: var(--ui-BG);
|
||||
border-radius: 20rpx;
|
||||
padding: 30rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
.input-label {
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.unit {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 48rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.uni-easyinput__placeholder-class {
|
||||
font-size: 30rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:deep(.uni-easyinput__content-input) {
|
||||
font-size: 48rpx;
|
||||
}
|
||||
|
||||
.face-value-btn {
|
||||
width: 200rpx;
|
||||
height: 144rpx;
|
||||
border: 1px solid var(--ui-BG-Main);
|
||||
border-radius: 10rpx;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin-bottom: 15rpx;
|
||||
margin-right: 15rpx;
|
||||
|
||||
&:nth-of-type(3n) {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.face-value-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 500;
|
||||
color: var(--ui-BG-Main);
|
||||
font-family: OPPOSANS;
|
||||
|
||||
&::after {
|
||||
content: '元';
|
||||
font-size: 24rpx;
|
||||
margin-left: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.face-value-tag {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
height: 40rpx;
|
||||
line-height: 40rpx;
|
||||
background: var(--ui-BG-Main);
|
||||
opacity: 0.8;
|
||||
border-radius: 10rpx 0 20rpx 0;
|
||||
top: 0;
|
||||
left: -2rpx;
|
||||
padding: 0 16rpx;
|
||||
font-size: 22rpx;
|
||||
color: $white;
|
||||
font-family: OPPOSANS;
|
||||
}
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
content: ' ';
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--ui-BG-Main);
|
||||
opacity: 0.1;
|
||||
z-index: 0;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-active {
|
||||
z-index: 1;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
background: var(--ui-BG-Main);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.face-value-title {
|
||||
color: $white;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
font-family: OPPOSANS;
|
||||
}
|
||||
|
||||
.face-value-tag {
|
||||
background: $white;
|
||||
color: var(--ui-BG-Main);
|
||||
font-family: OPPOSANS;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
285
pages/pay/result.vue
Normal file
285
pages/pay/result.vue
Normal file
@@ -0,0 +1,285 @@
|
||||
<!-- 支付结果页面 -->
|
||||
<template>
|
||||
<s-layout title="支付结果" :bgStyle="{ color: '#FFF' }">
|
||||
<view class="pay-result-box ss-flex-col ss-row-center ss-col-center">
|
||||
<view class="pay-waiting ss-m-b-30" v-if="payResult === 'waiting'"> </view>
|
||||
<image
|
||||
class="pay-img ss-m-b-30"
|
||||
v-if="payResult === 'success'"
|
||||
:src="sheep.$url.static('/static/img/shop/order/order_pay_success.gif')"
|
||||
></image>
|
||||
<image
|
||||
class="pay-img ss-m-b-30"
|
||||
v-if="['failed', 'closed'].includes(payResult)"
|
||||
:src="sheep.$url.static('/static/img/shop/order/order_paty_fail.gif')"
|
||||
></image>
|
||||
<view class="tip-text ss-m-b-30" v-if="payResult == 'success'">{{
|
||||
state.orderInfo.pay_mode === 'offline' ? '下单成功' : '支付成功'
|
||||
}}</view>
|
||||
<view class="tip-text ss-m-b-30" v-if="payResult == 'failed'">支付失败</view>
|
||||
<view class="tip-text ss-m-b-30" v-if="payResult == 'closed'">该订单已关闭</view>
|
||||
<view class="tip-text ss-m-b-30" v-if="payResult == 'waiting'">检测支付结果...</view>
|
||||
<view class="pay-total-num ss-flex" v-if="payResult === 'success'">
|
||||
<view v-if="Number(state.orderInfo.pay_fee) > 0">¥{{ state.orderInfo.pay_fee }}</view>
|
||||
<view v-if="state.orderInfo.score_amount && Number(state.orderInfo.pay_fee) > 0">+</view>
|
||||
<view class="price-text ss-flex ss-col-center" v-if="state.orderInfo.score_amount">
|
||||
<image
|
||||
:src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
|
||||
class="score-img"
|
||||
></image>
|
||||
<view>{{ state.orderInfo.score_amount }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="btn-box ss-flex ss-row-center ss-m-t-50">
|
||||
<button class="back-btn ss-reset-button" @tap="sheep.$router.go('/pages/index/index')">
|
||||
返回首页
|
||||
</button>
|
||||
<button
|
||||
class="check-btn ss-reset-button"
|
||||
v-if="payResult === 'failed'"
|
||||
@tap="sheep.$router.redirect('/pages/pay/index', { orderSN: state.orderId })"
|
||||
>
|
||||
重新支付
|
||||
</button>
|
||||
<button class="check-btn ss-reset-button" v-if="payResult === 'success'" @tap="onOrder">
|
||||
查看订单
|
||||
</button>
|
||||
<button
|
||||
class="check-btn ss-reset-button"
|
||||
v-if="
|
||||
payResult === 'success' &&
|
||||
['groupon', 'groupon_ladder'].includes(state.orderInfo.activity_type)
|
||||
"
|
||||
@tap="sheep.$router.redirect('/pages/activity/groupon/order')"
|
||||
>
|
||||
我的拼团
|
||||
</button>
|
||||
</view>
|
||||
<!-- #ifdef MP -->
|
||||
<view class="subscribe-box ss-flex ss-m-t-44">
|
||||
<image
|
||||
class="subscribe-img"
|
||||
:src="sheep.$url.static('/static/img/shop/order/cargo.png')"
|
||||
></image>
|
||||
<view class="subscribe-title ss-m-r-48 ss-m-l-16">获取实时发货信息与订单状态</view>
|
||||
<view class="subscribe-start" @tap="subscribeMessage">立即订阅</view>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onLoad, onHide, onShow } from '@dcloudio/uni-app';
|
||||
import { reactive, computed } from 'vue';
|
||||
import { isEmpty } from 'lodash';
|
||||
import sheep from '@/sheep';
|
||||
|
||||
const state = reactive({
|
||||
orderId: 0,
|
||||
orderType: 'goods',
|
||||
result: 'unpaid', // 支付状态
|
||||
orderInfo: {}, // 订单详情
|
||||
counter: 0, // 获取结果次数
|
||||
});
|
||||
|
||||
const payResult = computed(() => {
|
||||
if (state.result === 'unpaid') {
|
||||
return 'waiting';
|
||||
}
|
||||
if (state.result === 'paid') {
|
||||
return 'success';
|
||||
}
|
||||
if (state.result === 'failed') {
|
||||
return 'failed';
|
||||
}
|
||||
|
||||
if (state.result === 'closed') {
|
||||
return 'closed';
|
||||
}
|
||||
});
|
||||
async function getOrderInfo(orderId) {
|
||||
let checkPayResult;
|
||||
state.counter++;
|
||||
if (state.orderType === 'recharge') {
|
||||
checkPayResult = sheep.$api.trade.order;
|
||||
} else {
|
||||
checkPayResult = sheep.$api.order.detail;
|
||||
}
|
||||
const { data, error } = await checkPayResult(orderId);
|
||||
if (error === 0) {
|
||||
state.orderInfo = data;
|
||||
if (state.orderInfo.status === 'closed') {
|
||||
state.result = 'closed';
|
||||
return;
|
||||
}
|
||||
if (state.orderInfo.status !== 'unpaid') {
|
||||
state.result = 'paid';
|
||||
// #ifdef MP
|
||||
subscribeMessage();
|
||||
// #endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (state.counter < 3 && state.result === 'unpaid') {
|
||||
setTimeout(() => {
|
||||
getOrderInfo(orderId);
|
||||
}, 1500);
|
||||
}
|
||||
// 超过三次检测才判断为支付失败
|
||||
if (state.counter >= 3) {
|
||||
state.result = 'failed';
|
||||
}
|
||||
}
|
||||
|
||||
function onOrder() {
|
||||
if ((state.orderType === 'recharge')) {
|
||||
sheep.$router.redirect('/pages/pay/recharge-log');
|
||||
} else {
|
||||
sheep.$router.redirect('/pages/order/list');
|
||||
}
|
||||
}
|
||||
|
||||
// #ifdef MP
|
||||
function subscribeMessage() {
|
||||
let event = ['order_dispatched'];
|
||||
if (['groupon', 'groupon_ladder'].includes(state.orderInfo.activity_type)) {
|
||||
event.push('groupon_finish');
|
||||
event.push('groupon_fail');
|
||||
}
|
||||
sheep.$platform.useProvider('wechat').subscribeMessage(event);
|
||||
}
|
||||
// #endif
|
||||
|
||||
onLoad(async (options) => {
|
||||
let id = '';
|
||||
// 支付订单号
|
||||
if (options.orderSN) {
|
||||
id = options.orderSN;
|
||||
}
|
||||
if (options.id) {
|
||||
id = options.id;
|
||||
}
|
||||
state.orderId = id;
|
||||
|
||||
if (options.orderType === 'recharge') {
|
||||
state.orderType = 'recharge';
|
||||
}
|
||||
|
||||
// 支付结果传值过来是失败,则直接显示失败界面
|
||||
if (options.payState === 'fail') {
|
||||
state.result = 'failed';
|
||||
} else {
|
||||
// 轮询三次检测订单支付结果
|
||||
getOrderInfo(state.orderId);
|
||||
}
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
if(isEmpty(state.orderInfo)) return;
|
||||
getOrderInfo(state.orderId);
|
||||
})
|
||||
|
||||
onHide(() => {
|
||||
state.result = 'unpaid';
|
||||
state.counter = 0;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@keyframes rotation {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.score-img {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
margin: 0 4rpx;
|
||||
}
|
||||
|
||||
.pay-result-box {
|
||||
padding: 60rpx 0;
|
||||
|
||||
.pay-waiting {
|
||||
margin-top: 20rpx;
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border: 10rpx solid rgb(233, 231, 231);
|
||||
border-bottom-color: rgb(204, 204, 204);
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
// -webkit-animation: rotation 1s linear infinite;
|
||||
animation: rotation 1s linear infinite;
|
||||
}
|
||||
|
||||
.pay-img {
|
||||
width: 130rpx;
|
||||
height: 130rpx;
|
||||
}
|
||||
|
||||
.tip-text {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.pay-total-num {
|
||||
font-size: 36rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
font-family: OPPOSANS;
|
||||
}
|
||||
|
||||
.btn-box {
|
||||
width: 100%;
|
||||
|
||||
.back-btn {
|
||||
width: 190rpx;
|
||||
height: 70rpx;
|
||||
font-size: 28rpx;
|
||||
border: 2rpx solid #dfdfdf;
|
||||
border-radius: 35rpx;
|
||||
font-weight: 400;
|
||||
color: #595959;
|
||||
}
|
||||
|
||||
.check-btn {
|
||||
width: 190rpx;
|
||||
height: 70rpx;
|
||||
font-size: 28rpx;
|
||||
border: 2rpx solid #dfdfdf;
|
||||
border-radius: 35rpx;
|
||||
font-weight: 400;
|
||||
color: #595959;
|
||||
margin-left: 32rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.subscribe-box {
|
||||
.subscribe-img {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
}
|
||||
|
||||
.subscribe-title {
|
||||
font-weight: 500;
|
||||
font-size: 32rpx;
|
||||
line-height: 36rpx;
|
||||
color: #434343;
|
||||
}
|
||||
|
||||
.subscribe-start {
|
||||
color: var(--ui-BG-Main);
|
||||
font-weight: 700;
|
||||
font-size: 32rpx;
|
||||
line-height: 36rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
187
pages/pay/withdraw-log.vue
Normal file
187
pages/pay/withdraw-log.vue
Normal file
@@ -0,0 +1,187 @@
|
||||
<template>
|
||||
<s-layout class="widthdraw-log-wrap" title="提现记录">
|
||||
<!-- 记录卡片 -->
|
||||
<view class="wallet-log-box ss-p-b-30">
|
||||
<view class="log-list" v-for="item in state.pagination.data" :key="item">
|
||||
<view class="head ss-flex ss-col-center ss-row-between">
|
||||
<view class="title">{{
|
||||
item.withdraw_type === 'bank'
|
||||
? '提现至银行卡'
|
||||
: item.withdraw_type === 'alipay'
|
||||
? '提现至支付宝'
|
||||
: '提现至微信'
|
||||
}}</view>
|
||||
<view
|
||||
class="num"
|
||||
:class="
|
||||
item.status === -1
|
||||
? 'danger-color'
|
||||
: item.status === 2
|
||||
? 'success-color'
|
||||
: 'warning-color'
|
||||
"
|
||||
>{{ item.amount }}元</view
|
||||
>
|
||||
</view>
|
||||
<view class="status-box item ss-flex ss-col-center ss-row-between">
|
||||
<view class="item-title">申请状态</view>
|
||||
<view
|
||||
class="status-text"
|
||||
:class="
|
||||
item.status === -1
|
||||
? 'danger-color'
|
||||
: item.status === 2
|
||||
? 'success-color'
|
||||
: 'warning-color'
|
||||
"
|
||||
>{{ item.status_text }}</view
|
||||
>
|
||||
</view>
|
||||
<view class="time-box item ss-flex ss-col-center ss-row-between">
|
||||
<text class="item-title">账户信息</text>
|
||||
<view class="time ss-ellipsis-1" v-if="item.withdraw_type === 'bank'"
|
||||
>{{ item.withdraw_info_hidden.开户行 }}[{{ item.withdraw_info_hidden.银行卡号 }}]</view
|
||||
>
|
||||
<view class="time ss-ellipsis-1" v-if="item.withdraw_type === 'alipay'">
|
||||
支付宝[{{ item.withdraw_info_hidden.支付宝账户 }}]
|
||||
</view>
|
||||
<view class="time ss-ellipsis-1" v-if="item.withdraw_type === 'wechat'">微信零钱</view>
|
||||
</view>
|
||||
<view class="time-box item ss-flex ss-col-center ss-row-between">
|
||||
<text class="item-title">提现单号</text>
|
||||
<view class="time"> {{ item.withdraw_sn }} </view>
|
||||
</view>
|
||||
<view class="time-box item ss-flex ss-col-center ss-row-between">
|
||||
<text class="item-title">手续费</text>
|
||||
<view class="time">{{ item.charge_fee }}元</view>
|
||||
</view>
|
||||
<view class="time-box item ss-flex ss-col-center ss-row-between">
|
||||
<text class="item-title">申请时间</text>
|
||||
<view class="time"> {{ item.create_time }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<s-empty
|
||||
v-if="state.pagination.total === 0"
|
||||
icon="/static/comment-empty.png"
|
||||
text="暂无提现记录"
|
||||
></s-empty>
|
||||
<uni-load-more
|
||||
v-if="state.pagination.total > 0"
|
||||
:status="state.loadStatus"
|
||||
:content-text="{
|
||||
contentdown: '上拉加载更多',
|
||||
}"
|
||||
@tap="loadmore"
|
||||
/>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
|
||||
import _ from 'lodash';
|
||||
const state = reactive({
|
||||
currentTab: 0,
|
||||
pagination: {
|
||||
data: [],
|
||||
current_page: 1,
|
||||
total: 1,
|
||||
last_page: 1,
|
||||
},
|
||||
loadStatus: '',
|
||||
});
|
||||
async function getList(page = 1, list_rows = 6) {
|
||||
const res = await sheep.$api.pay.withdraw.list({ list_rows, page });
|
||||
if (res.error === 0) {
|
||||
let logList = _.concat(state.pagination.data, res.data.data);
|
||||
state.pagination = {
|
||||
...res.data,
|
||||
data: logList,
|
||||
};
|
||||
if (state.pagination.current_page < state.pagination.last_page) {
|
||||
state.loadStatus = 'more';
|
||||
} else {
|
||||
state.loadStatus = 'noMore';
|
||||
}
|
||||
}
|
||||
}
|
||||
// 加载更多
|
||||
function loadmore() {
|
||||
if (state.loadStatus !== 'noMore') {
|
||||
getList(state.pagination.current_page + 1);
|
||||
}
|
||||
}
|
||||
onLoad(() => {
|
||||
getList();
|
||||
});
|
||||
onReachBottom(() => {
|
||||
loadmore();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// 记录卡片
|
||||
.log-list {
|
||||
min-height: 213rpx;
|
||||
background: $white;
|
||||
margin-bottom: 10rpx;
|
||||
padding-bottom: 10rpx;
|
||||
|
||||
.head {
|
||||
padding: 0 35rpx;
|
||||
height: 80rpx;
|
||||
border-bottom: 1rpx solid $gray-e;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: $dark-3;
|
||||
}
|
||||
|
||||
.num {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.item {
|
||||
padding: 0 30rpx 10rpx;
|
||||
|
||||
.item-icon {
|
||||
color: $gray-d;
|
||||
font-size: 36rpx;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
width: 180rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #c0c0c0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.warning-color {
|
||||
color: #faad14;
|
||||
}
|
||||
.danger-color {
|
||||
color: #ff4d4f;
|
||||
}
|
||||
.success-color {
|
||||
color: #67c23a;
|
||||
}
|
||||
</style>
|
||||
380
pages/pay/withdraw.vue
Normal file
380
pages/pay/withdraw.vue
Normal file
@@ -0,0 +1,380 @@
|
||||
<template>
|
||||
<s-layout title="申请提现" class="withdraw-wrap" navbar="inner">
|
||||
<!-- <view class="page-bg"></view> -->
|
||||
<view
|
||||
class="wallet-num-box ss-flex ss-col-center ss-row-between"
|
||||
:style="[
|
||||
{
|
||||
marginTop: '-' + Number(statusBarHeight + 88) + 'rpx',
|
||||
paddingTop: Number(statusBarHeight + 108) + 'rpx',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<view class="">
|
||||
<view class="num-title">可提现金额(元)</view>
|
||||
<view class="wallet-num">{{ userInfo.commission }}</view>
|
||||
</view>
|
||||
<button class="ss-reset-button log-btn" @tap="sheep.$router.go('/pages/pay/withdraw-log')"
|
||||
>提现记录</button
|
||||
>
|
||||
</view>
|
||||
<!-- 提现输入卡片-->
|
||||
<view class="draw-card">
|
||||
<view class="card-title">提现金额</view>
|
||||
<view class="input-box ss-flex ss-col-center border-bottom">
|
||||
<view class="unit">¥</view>
|
||||
<uni-easyinput
|
||||
:inputBorder="false"
|
||||
class="ss-flex-1 ss-p-l-10"
|
||||
v-model="state.amount"
|
||||
type="number"
|
||||
placeholder="请输入提现金额"
|
||||
/>
|
||||
</view>
|
||||
<view class="bank-box ss-flex ss-col-center ss-row-between ss-m-b-30">
|
||||
<view class="name">提现至</view>
|
||||
<view class="bank-list ss-flex ss-col-center" @tap="onAccountSelect(true)">
|
||||
<view v-if="!state.accountInfo.type" class="empty-text">请选择提现方式</view>
|
||||
<view v-if="state.accountInfo.type === 'wechat'" class="empty-text">微信零钱</view>
|
||||
<view v-if="state.accountInfo.type === 'alipay'" class="empty-text">支付宝账户</view>
|
||||
<view v-if="state.accountInfo.type === 'bank'" class="empty-text">银行卡转账</view>
|
||||
<text class="cicon-forward"></text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bind-box ss-flex ss-col-center ss-row-between" v-if="state.accountInfo.type">
|
||||
<view class="placeholder-text" v-if="state.accountInfo.account_name">
|
||||
{{ state.accountInfo.account_header }}|{{ state.accountInfo.account_name }}
|
||||
</view>
|
||||
<view class="placeholder-text" v-else>暂无提现账户</view>
|
||||
<button class="add-btn ss-reset-button" @tap="onAccountEdit(true)">
|
||||
{{ state.accountInfo.account_name ? '修改' : '添加' }}
|
||||
</button>
|
||||
</view>
|
||||
<button class="ss-reset-button save-btn ui-BG-Main-Gradient ui-Shadow-Main" @tap="onConfirm">
|
||||
确认提现
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<!-- 提现说明 -->
|
||||
<view class="draw-notice">
|
||||
<view class="title ss-m-b-30">提现说明</view>
|
||||
<view class="draw-list" v-for="(rule, index) in state.rulesList" :key="index">
|
||||
{{ index + 1 }}.{{ rule }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 选择提现账户 -->
|
||||
<account-type-select
|
||||
:show="state.accountSelect"
|
||||
@close="onAccountSelect(false)"
|
||||
round="10"
|
||||
v-model="state.accountInfo"
|
||||
:methods="state.rules.methods"
|
||||
/>
|
||||
<!-- 编辑账户信息 -->
|
||||
<account-info-modal
|
||||
v-if="state.accountInfo.type"
|
||||
v-model="state.accountInfo"
|
||||
:show="state.accountEdit"
|
||||
@close="onAccountEdit(false)"
|
||||
round="10"
|
||||
/>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, reactive, onBeforeMount } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import accountTypeSelect from './components/account-type-select.vue';
|
||||
import accountInfoModal from './components/account-info-modal.vue';
|
||||
import { onPageScroll } from '@dcloudio/uni-app';
|
||||
const headerBg = sheep.$url.css('/static/img/shop/user/withdraw_bg.png');
|
||||
onPageScroll(() => {});
|
||||
const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
|
||||
function filterRules(rules) {
|
||||
let list = [];
|
||||
let str1 = '';
|
||||
if (rules.min_amount > 0) {
|
||||
str1 += `最少 ${rules.min_amount}元; `;
|
||||
}
|
||||
if (rules.max_amount > 0) {
|
||||
str1 += `最多 ${rules.max_amount}元;`;
|
||||
}
|
||||
if (str1 !== '') {
|
||||
list.push('单次提现金额 ' + str1);
|
||||
}
|
||||
|
||||
if (rules.max_num > 0) {
|
||||
list.push(`每${rules.num_unit === 'day' ? '天' : '月'}最多可提现 ${rules.max_num} 次;`);
|
||||
}
|
||||
|
||||
if (rules.charge_rate_format > 0) {
|
||||
list.push(`每次收取提现手续费 ${rules.charge_rate_format}%;`);
|
||||
}
|
||||
list.push(
|
||||
`提现申请后将${rules.auto_arrival ? '自动' : '审核后'}到账, 到账结果请查收对应渠道服务通知;`,
|
||||
);
|
||||
list.push('如有疑问请及时联系客服.');
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
const userStore = sheep.$store('user');
|
||||
const userInfo = computed(() => userStore.userInfo);
|
||||
const state = reactive({
|
||||
amount: '',
|
||||
type: '',
|
||||
accountInfo: {},
|
||||
accountSelect: false,
|
||||
accountEdit: false,
|
||||
rules: {
|
||||
min_amount: 0,
|
||||
max_amount: 0,
|
||||
max_num: 0,
|
||||
num_unit: 0,
|
||||
charge_rate_format: 0,
|
||||
charge_rate: 0,
|
||||
methods: [],
|
||||
},
|
||||
rulesList: [],
|
||||
});
|
||||
|
||||
const onAccountEdit = (e) => {
|
||||
state.accountEdit = e;
|
||||
};
|
||||
|
||||
const onAccountSelect = (e) => {
|
||||
state.accountSelect = e;
|
||||
};
|
||||
|
||||
const onConfirm = async () => {
|
||||
let payload = {
|
||||
money: state.amount,
|
||||
...state.accountInfo,
|
||||
};
|
||||
|
||||
if (payload.money > userInfo.commission || payload.money <= 0) {
|
||||
sheep.$helper.toast('请输入正确的提现金额');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!payload.type) {
|
||||
sheep.$helper.toast('请选择提现方式');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!payload.account_name || !payload.account_header || !payload.account_no) {
|
||||
sheep.$helper.toast('请完善您的账户信息');
|
||||
return;
|
||||
}
|
||||
|
||||
if (sheep.$platform.name === 'H5' && payload.type === 'wechat') {
|
||||
sheep.$helper.toast('请使用微信浏览器操作');
|
||||
return;
|
||||
}
|
||||
|
||||
let { error, msg, data } = await sheep.$api.pay.withdraw.apply(payload);
|
||||
if (error === -1) {
|
||||
sheep.$platform.useProvider('wechat').bind();
|
||||
}
|
||||
if (error === 0) {
|
||||
userStore.getInfo();
|
||||
uni.showModal({
|
||||
title: '操作成功',
|
||||
content: '您的提现申请已成功提交',
|
||||
cancelText: '继续提现',
|
||||
confirmText: '查看记录',
|
||||
success: function (res) {
|
||||
res.confirm && sheep.$router.go('/pages/pay/withdraw-log');
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
async function getWithdrawRules() {
|
||||
let { error, data } = await sheep.$api.pay.withdraw.rules();
|
||||
if (error === 0) {
|
||||
state.rules = data;
|
||||
state.rulesList = filterRules(state.rules);
|
||||
}
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
getWithdrawRules();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep() {
|
||||
.uni-input-input {
|
||||
font-family: OPPOSANS !important;
|
||||
}
|
||||
}
|
||||
|
||||
.wallet-num-box {
|
||||
padding: 0 40rpx 80rpx;
|
||||
background: var(--ui-BG-Main) v-bind(headerBg) center/750rpx 100% no-repeat;
|
||||
border-radius: 0 0 5% 5%;
|
||||
|
||||
.num-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: $white;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.wallet-num {
|
||||
font-size: 60rpx;
|
||||
font-weight: 500;
|
||||
color: $white;
|
||||
font-family: OPPOSANS;
|
||||
}
|
||||
|
||||
.log-btn {
|
||||
width: 170rpx;
|
||||
height: 60rpx;
|
||||
line-height: 60rpx;
|
||||
border: 1rpx solid $white;
|
||||
border-radius: 30rpx;
|
||||
padding: 0;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
// 提现输入卡片
|
||||
.draw-card {
|
||||
background-color: $white;
|
||||
border-radius: 20rpx;
|
||||
width: 690rpx;
|
||||
min-height: 560rpx;
|
||||
margin: -60rpx 30rpx 30rpx 30rpx;
|
||||
padding: 30rpx;
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
box-sizing: border-box;
|
||||
|
||||
.card-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.bank-box {
|
||||
.name {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.bank-list {
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
font-weight: 400;
|
||||
color: $dark-9;
|
||||
}
|
||||
|
||||
.cicon-forward {
|
||||
color: $dark-9;
|
||||
}
|
||||
}
|
||||
|
||||
.input-box {
|
||||
width: 624rpx;
|
||||
height: 100rpx;
|
||||
margin-bottom: 40rpx;
|
||||
|
||||
.unit {
|
||||
font-size: 48rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.uni-easyinput__placeholder-class {
|
||||
font-size: 30rpx;
|
||||
height: 36rpx;
|
||||
}
|
||||
|
||||
:deep(.uni-easyinput__content-input) {
|
||||
font-size: 48rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.save-btn {
|
||||
width: 616rpx;
|
||||
height: 86rpx;
|
||||
line-height: 86rpx;
|
||||
border-radius: 40rpx;
|
||||
margin-top: 80rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.bind-box {
|
||||
.placeholder-text {
|
||||
font-size: 26rpx;
|
||||
color: $dark-9;
|
||||
}
|
||||
|
||||
.add-btn {
|
||||
width: 100rpx;
|
||||
height: 50rpx;
|
||||
border-radius: 25rpx;
|
||||
line-height: 50rpx;
|
||||
font-size: 22rpx;
|
||||
color: var(--ui-BG-Main);
|
||||
background-color: var(--ui-BG-Main-light);
|
||||
}
|
||||
}
|
||||
|
||||
.input-box {
|
||||
width: 624rpx;
|
||||
height: 100rpx;
|
||||
margin-bottom: 40rpx;
|
||||
|
||||
.unit {
|
||||
font-size: 48rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.uni-easyinput__placeholder-class {
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
:deep(.uni-easyinput__content-input) {
|
||||
font-size: 48rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.save-btn {
|
||||
width: 616rpx;
|
||||
height: 86rpx;
|
||||
line-height: 86rpx;
|
||||
border-radius: 40rpx;
|
||||
margin-top: 80rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 提现说明
|
||||
.draw-notice {
|
||||
width: 684rpx;
|
||||
background: #ffffff;
|
||||
border: 2rpx solid #fffaee;
|
||||
border-radius: 20rpx;
|
||||
margin: 20rpx 32rpx 0 32rpx;
|
||||
padding: 30rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
.title {
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.draw-list {
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
line-height: 46rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
57
pages/public/error.vue
Normal file
57
pages/public/error.vue
Normal file
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<view class="error-page">
|
||||
<s-empty
|
||||
v-if="errCode === 'NetworkError'"
|
||||
icon="/static/internet-empty.png"
|
||||
text="网络连接失败"
|
||||
showAction
|
||||
actionText="重新连接"
|
||||
@clickAction="onReconnect"
|
||||
buttonColor="#ff3000"
|
||||
></s-empty>
|
||||
<s-empty
|
||||
v-else-if="errCode === 'TemplateError'"
|
||||
icon="/static/internet-empty.png"
|
||||
text="未找到模板"
|
||||
showAction
|
||||
actionText="重新加载"
|
||||
@clickAction="onReconnect"
|
||||
buttonColor="#ff3000"
|
||||
></s-empty>
|
||||
<s-empty
|
||||
v-else-if="errCode !== ''"
|
||||
icon="/static/internet-empty.png"
|
||||
:text="errMsg"
|
||||
showAction
|
||||
actionText="重新加载"
|
||||
@clickAction="onReconnect"
|
||||
buttonColor="#ff3000"
|
||||
></s-empty>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { ref } from 'vue';
|
||||
import { ShoproInit } from '@/sheep';
|
||||
|
||||
const errCode = ref('');
|
||||
const errMsg = ref('');
|
||||
onLoad((options) => {
|
||||
errCode.value = options.errCode;
|
||||
errMsg.value = options.errMsg;
|
||||
});
|
||||
// 重新连接
|
||||
async function onReconnect() {
|
||||
uni.reLaunch({
|
||||
url: '/pages/index/index',
|
||||
});
|
||||
ShoproInit();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.error-page {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
110
pages/public/faq.vue
Normal file
110
pages/public/faq.vue
Normal file
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<s-layout class="set-wrap" title="常见问题" :bgStyle="{ color: '#FFF' }">
|
||||
<uni-collapse>
|
||||
<uni-collapse-item v-for="(item, index) in state.list" :key="item">
|
||||
<template v-slot:title>
|
||||
<view class="ss-flex ss-col-center header">
|
||||
<view class="ss-m-l-20 ss-m-r-20 icon">
|
||||
<view class="rectangle">
|
||||
<view class="num ss-flex ss-row-center ss-col-center">
|
||||
{{ index + 1 < 10 ? '0' + (index + 1) : index + 1 }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="triangle"> </view>
|
||||
</view>
|
||||
<view class="title ss-m-t-36 ss-m-b-36">
|
||||
{{ item.title }}
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<view class="content ss-p-l-78 ss-p-r-40 ss-p-b-50 ss-p-t-20">
|
||||
<text class="text">{{ item.content }}</text>
|
||||
</view>
|
||||
</uni-collapse-item>
|
||||
</uni-collapse>
|
||||
<s-empty
|
||||
v-if="state.list.length === 0 && !state.loading"
|
||||
text="暂无常见问题"
|
||||
icon="/static/collect-empty.png"
|
||||
/>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { reactive } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
|
||||
const state = reactive({
|
||||
list: [],
|
||||
loading: true,
|
||||
});
|
||||
|
||||
async function getFaqList() {
|
||||
const { error, data } = await sheep.$api.data.faq();
|
||||
if (error === 0) {
|
||||
state.list = data;
|
||||
state.loading = false;
|
||||
}
|
||||
}
|
||||
onLoad(() => {
|
||||
getFaqList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.header {
|
||||
.title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
line-height: 30rpx;
|
||||
max-width: 688rpx;
|
||||
}
|
||||
|
||||
.icon {
|
||||
position: relative;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
|
||||
.rectangle {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 40rpx;
|
||||
height: 36rpx;
|
||||
background: var(--ui-BG-Main);
|
||||
border-radius: 4px;
|
||||
|
||||
.num {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: var(--ui-BG);
|
||||
line-height: 32rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.triangle {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 4rpx solid transparent;
|
||||
border-right: 4rpx solid transparent;
|
||||
border-top: 8rpx solid var(--ui-BG-Main);
|
||||
position: absolute;
|
||||
left: 16rpx;
|
||||
bottom: -4rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
border-bottom: 1rpx solid #dfdfdf;
|
||||
|
||||
.text {
|
||||
font-size: 26rpx;
|
||||
color: #666666;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
226
pages/public/feedback.vue
Normal file
226
pages/public/feedback.vue
Normal file
@@ -0,0 +1,226 @@
|
||||
<template>
|
||||
<s-layout class="set-wrap" title="问题反馈">
|
||||
<uni-forms ref="form" :modelValue="state.formData" border>
|
||||
<view class="bg-white type-box ss-p-x-20 ss-p-y-30">
|
||||
<view class="title ss-m-b-44">请选择类型</view>
|
||||
<view class="ss-m-l-12">
|
||||
<radio-group @change="radioChange">
|
||||
<label
|
||||
class="ss-flex ss-col-center ss-m-b-40"
|
||||
v-for="item in state.radioList"
|
||||
:key="item.type"
|
||||
>
|
||||
<radio :value="item.type" color="var(--ui-BG-Main)" style="transform: scale(0.8)" />
|
||||
<view class="radio-subtitle">{{ item.type }}</view>
|
||||
</label>
|
||||
</radio-group>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bg-white ss-p-x-20 ss-p-y-30 ss-m-t-20">
|
||||
<view class="title ss-m-b-30"> 相关描述 </view>
|
||||
<view class="textarea">
|
||||
<uni-easyinput
|
||||
:inputBorder="false"
|
||||
type="textarea"
|
||||
v-model="state.formData.content"
|
||||
placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal"
|
||||
placeholder="客官~请描述您遇到的问题,建议上传照片"
|
||||
clearable
|
||||
></uni-easyinput>
|
||||
<s-uploader
|
||||
v-model:url="state.formData.images"
|
||||
fileMediatype="image"
|
||||
limit="9"
|
||||
mode="grid"
|
||||
:imageStyles="{ width: '168rpx', height: '168rpx' }"
|
||||
></s-uploader>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bg-white ss-p-x-20 ss-p-y-30 ss-m-t-20">
|
||||
<view class="title ss-m-b-30"> 联系方式 </view>
|
||||
<view class="mobile-box">
|
||||
<uni-easyinput
|
||||
:inputBorder="false"
|
||||
type="number"
|
||||
v-model="state.formData.phone"
|
||||
paddingLeft="10"
|
||||
placeholder="请输入您的联系电话"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</uni-forms>
|
||||
<su-fixed bottom placeholder>
|
||||
<view class="ss-flex ss-row-between ss-p-x-30 ss-p-y-10">
|
||||
<button class="kefu-btn ss-reset-button" @tap="sheep.$router.go('/pages/chat/index')">
|
||||
联系客服
|
||||
</button>
|
||||
<button class="submit-btn ss-reset-button ui-BG-Main ui-Shadow-Main" @tap="onSubmit">
|
||||
提交
|
||||
</button>
|
||||
</view>
|
||||
</su-fixed>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { computed, reactive, ref, unref } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
|
||||
const filesRef = ref(null);
|
||||
const state = reactive({
|
||||
radioList: [
|
||||
{
|
||||
type: '产品功能问题反馈',
|
||||
},
|
||||
{
|
||||
type: '建议及意见反馈',
|
||||
},
|
||||
{
|
||||
type: '投诉客服其他问题',
|
||||
},
|
||||
],
|
||||
formData: {
|
||||
content: '',
|
||||
phone: '',
|
||||
images: [],
|
||||
type: '',
|
||||
},
|
||||
imageFiles: [],
|
||||
current: 0,
|
||||
});
|
||||
|
||||
async function onSubmit() {
|
||||
if (!state.formData.type) {
|
||||
sheep.$helper.toast('请选择类型');
|
||||
return;
|
||||
}
|
||||
if (!state.formData.content) {
|
||||
sheep.$helper.toast('请描述您遇到的问题');
|
||||
return;
|
||||
}
|
||||
if (!state.formData.phone) {
|
||||
sheep.$helper.toast('请输入您的联系方式');
|
||||
return;
|
||||
}
|
||||
|
||||
const { error } = await sheep.$api.app.feedback(state.formData);
|
||||
if (error === 0) {
|
||||
sheep.$router.back();
|
||||
}
|
||||
}
|
||||
|
||||
function radioChange(e) {
|
||||
state.formData.type = e.detail.value;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.type-box {
|
||||
border-top: 2rpx solid #f9fafb;
|
||||
}
|
||||
|
||||
.uni-forms {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
:deep() {
|
||||
.uni-easyinput__placeholder-class {
|
||||
color: #bbbbbb !important;
|
||||
font-size: 28rpx !important;
|
||||
font-weight: 400 !important;
|
||||
line-height: normal !important;
|
||||
}
|
||||
|
||||
.uni-forms-item__label .label-text {
|
||||
font-size: 28rpx !important;
|
||||
color: #333333 !important;
|
||||
line-height: normal !important;
|
||||
}
|
||||
|
||||
.uni-list-item__content-title {
|
||||
font-size: 28rpx !important;
|
||||
color: #333333 !important;
|
||||
line-height: normal !important;
|
||||
}
|
||||
|
||||
.uni-easyinput__content-textarea {
|
||||
font-size: 28rpx !important;
|
||||
color: #333333 !important;
|
||||
line-height: normal !important;
|
||||
margin-top: 4rpx !important;
|
||||
padding-left: 20rpx !important;
|
||||
}
|
||||
|
||||
.uni-icons {
|
||||
font-size: 40rpx !important;
|
||||
}
|
||||
|
||||
.icon-del-box {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
top: 0;
|
||||
right: 0;
|
||||
|
||||
.icon-del {
|
||||
width: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.radio-subtitle {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
line-height: 42rpx;
|
||||
}
|
||||
|
||||
.textarea {
|
||||
min-height: 322rpx;
|
||||
background: #f9fafb;
|
||||
border-radius: 20rpx;
|
||||
padding: 20rpx;
|
||||
margin: 30rpx 20rpx 46rpx 0;
|
||||
|
||||
.area {
|
||||
height: 238rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
line-height: 50rpx;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pl-style {
|
||||
font-size: 24rpx;
|
||||
color: #b1b3c7;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-box {
|
||||
background: #f9fafb;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 334rpx;
|
||||
height: 74rpx;
|
||||
border-radius: 37rpx;
|
||||
}
|
||||
|
||||
.kefu-btn {
|
||||
width: 334rpx;
|
||||
height: 74rpx;
|
||||
border-radius: 37rpx;
|
||||
background: #eeeeee;
|
||||
color: #333333;
|
||||
}
|
||||
</style>
|
||||
47
pages/public/richtext.vue
Normal file
47
pages/public/richtext.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<s-layout class="set-wrap" :title="state.title" :bgStyle="{ color: '#FFF' }">
|
||||
<view class="ss-p-30"><mp-html class="richtext" :content="state.content"></mp-html></view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { reactive } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
|
||||
const state = reactive({
|
||||
title: '',
|
||||
content: '',
|
||||
});
|
||||
|
||||
async function getRichTextContent(id) {
|
||||
const { error, data } = await sheep.$api.data.richtext(id);
|
||||
if (error === 0) {
|
||||
state.content = data.content;
|
||||
if (state.title === '') {
|
||||
state.title = data.title;
|
||||
uni.setNavigationBarTitle({
|
||||
title: state.title,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
onLoad((options) => {
|
||||
if (options.title) {
|
||||
state.title = options.title;
|
||||
uni.setNavigationBarTitle({
|
||||
title: state.title,
|
||||
});
|
||||
}
|
||||
getRichTextContent(options.id);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.set-title {
|
||||
margin: 0 30rpx;
|
||||
}
|
||||
|
||||
.richtext {
|
||||
}
|
||||
</style>
|
||||
239
pages/public/setting.vue
Normal file
239
pages/public/setting.vue
Normal file
@@ -0,0 +1,239 @@
|
||||
<template>
|
||||
<s-layout class="set-wrap" title="系统设置" :bgStyle="{ color: '#fff' }">
|
||||
<view class="header-box ss-flex-col ss-row-center ss-col-center">
|
||||
<image
|
||||
class="logo-img ss-m-b-46"
|
||||
:src="sheep.$url.cdn(appInfo.logo)"
|
||||
mode="aspectFit"
|
||||
></image>
|
||||
<view class="name ss-m-b-24">{{ appInfo.name }}</view>
|
||||
</view>
|
||||
|
||||
<view class="container-list">
|
||||
<uni-list :border="false">
|
||||
<uni-list-item
|
||||
title="当前版本"
|
||||
:rightText="appInfo.version"
|
||||
showArrow
|
||||
clickable
|
||||
:border="false"
|
||||
class="list-border"
|
||||
@tap="onCheckUpdate"
|
||||
></uni-list-item>
|
||||
<uni-list-item
|
||||
title="本地缓存"
|
||||
:rightText="storageSize"
|
||||
showArrow
|
||||
:border="false"
|
||||
class="list-border"
|
||||
></uni-list-item>
|
||||
<uni-list-item
|
||||
title="意见反馈"
|
||||
showArrow
|
||||
clickable
|
||||
:border="false"
|
||||
class="list-border"
|
||||
@tap="sheep.$router.go('/pages/public/feedback')"
|
||||
></uni-list-item>
|
||||
<uni-list-item
|
||||
title="关于我们"
|
||||
showArrow
|
||||
clickable
|
||||
:border="false"
|
||||
class="list-border"
|
||||
@tap="
|
||||
sheep.$router.go('/pages/public/richtext', {
|
||||
id: appInfo.about_us.id,
|
||||
title: appInfo.about_us.title,
|
||||
})
|
||||
"
|
||||
></uni-list-item>
|
||||
<!-- 为了过审 只有iOS-App有注销账号功能 -->
|
||||
<uni-list-item
|
||||
v-if="isLogin && sheep.$platform.os === 'ios' && sheep.$platform.name === 'App'"
|
||||
title="注销账号"
|
||||
rightText=""
|
||||
showArrow
|
||||
clickable
|
||||
:border="false"
|
||||
class="list-border"
|
||||
@click="onLogoff"
|
||||
></uni-list-item>
|
||||
</uni-list>
|
||||
</view>
|
||||
|
||||
<view class="set-footer ss-flex-col ss-row-center ss-col-center">
|
||||
<view class="agreement-box ss-flex ss-col-center ss-m-b-40">
|
||||
<view class="ss-flex ss-col-center ss-m-b-10">
|
||||
<view
|
||||
class="tcp-text"
|
||||
@tap="
|
||||
sheep.$router.go('/pages/public/richtext', {
|
||||
id: appInfo.user_protocol.id,
|
||||
title: appInfo.user_protocol.title,
|
||||
})
|
||||
"
|
||||
>
|
||||
《{{ appInfo.user_protocol.title }}》
|
||||
</view>
|
||||
<view class="agreement-text">与</view>
|
||||
<view
|
||||
class="tcp-text"
|
||||
@tap="
|
||||
sheep.$router.go('/pages/public/richtext', {
|
||||
id: appInfo.privacy_protocol.id,
|
||||
title: appInfo.privacy_protocol.title,
|
||||
})
|
||||
"
|
||||
>
|
||||
《{{ appInfo.privacy_protocol.title }}》
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="copyright-text ss-m-b-10">{{ appInfo.copyright }}</view>
|
||||
<view class="copyright-text">{{ appInfo.copytime }}</view>
|
||||
</view>
|
||||
<su-fixed bottom placeholder>
|
||||
<view class="ss-p-x-20 ss-p-b-40">
|
||||
<button
|
||||
class="loginout-btn ss-reset-button ui-BG-Main ui-Shadow-Main"
|
||||
@tap="onLogout"
|
||||
v-if="isLogin"
|
||||
>
|
||||
退出登录
|
||||
</button>
|
||||
</view>
|
||||
</su-fixed>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { computed, reactive } from 'vue';
|
||||
|
||||
const appInfo = computed(() => sheep.$store('app').info);
|
||||
const isLogin = computed(() => sheep.$store('user').isLogin);
|
||||
const storageSize = uni.getStorageInfoSync().currentSize + 'Kb';
|
||||
const state = reactive({
|
||||
showModal: false,
|
||||
});
|
||||
|
||||
function onCheckUpdate() {
|
||||
sheep.$platform.checkUpdate();
|
||||
// 小程序初始化时已检查更新
|
||||
// H5实时更新无需检查
|
||||
// App 1.跳转应用市场更新 2.手动热更新 3.整包更新
|
||||
}
|
||||
function onLogoff() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确认注销账号?',
|
||||
success: async function (res) {
|
||||
if (res.confirm) {
|
||||
const { error } = await sheep.$api.user.logoff();
|
||||
if (error === 0) {
|
||||
sheep.$store('user').logout();
|
||||
sheep.$router.go('/pages/index/user');
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function onLogout() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确认退出账号?',
|
||||
success: async function (res) {
|
||||
if (res.confirm) {
|
||||
const result = await sheep.$store('user').logout();
|
||||
if (result) {
|
||||
sheep.$router.go('/pages/index/user');
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container-list {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.set-title {
|
||||
margin: 0 30rpx;
|
||||
}
|
||||
|
||||
.header-box {
|
||||
padding: 100rpx 0;
|
||||
|
||||
.logo-img {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 42rpx;
|
||||
font-weight: 400;
|
||||
color: $dark-3;
|
||||
}
|
||||
|
||||
.version {
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
line-height: 32rpx;
|
||||
color: $gray-b;
|
||||
}
|
||||
}
|
||||
|
||||
.set-footer {
|
||||
margin: 100rpx 0 0 0;
|
||||
|
||||
.copyright-text {
|
||||
font-size: 22rpx;
|
||||
font-weight: 500;
|
||||
color: $gray-c;
|
||||
line-height: 30rpx;
|
||||
}
|
||||
|
||||
.agreement-box {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
|
||||
.tcp-text {
|
||||
color: var(--ui-BG-Main);
|
||||
}
|
||||
|
||||
.agreement-text {
|
||||
color: $dark-9;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.loginout-btn {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.list-border {
|
||||
font-size: 28rpx;
|
||||
font-weight: 400;
|
||||
color: #333333;
|
||||
border-bottom: 2rpx solid #eeeeee;
|
||||
}
|
||||
|
||||
:deep(.uni-list-item__content-title) {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
:deep(.uni-list-item__extra-text) {
|
||||
color: #bbbbbb;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
</style>
|
||||
15
pages/public/webview.vue
Normal file
15
pages/public/webview.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<view><web-view :src="url"></web-view></view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const url = ref('');
|
||||
onLoad((options) => {
|
||||
url.value = decodeURIComponent(options.url);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
261
pages/user/address/edit.vue
Normal file
261
pages/user/address/edit.vue
Normal file
@@ -0,0 +1,261 @@
|
||||
<template>
|
||||
<s-layout :title="state.model.id ? '编辑地址' : '新增地址'">
|
||||
<uni-forms ref="addressFormRef" v-model="state.model" :rules="state.rules" validateTrigger="bind"
|
||||
labelWidth="160" labelAlign="left" border :labelStyle="{ fontWeight: 'bold' }">
|
||||
<view class="bg-white form-box ss-p-x-30">
|
||||
<uni-forms-item name="consignee" label="收货人" class="form-item">
|
||||
<uni-easyinput v-model="state.model.consignee" placeholder="请填写收货人姓名" :inputBorder="false"
|
||||
placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal" />
|
||||
</uni-forms-item>
|
||||
|
||||
<uni-forms-item name="mobile" label="手机号" class="form-item">
|
||||
<uni-easyinput v-model="state.model.mobile" type="number" placeholder="请输入手机号" :inputBorder="false"
|
||||
placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal">
|
||||
</uni-easyinput>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item name="region" label="省市区" @tap="state.showRegion = true" class="form-item">
|
||||
<uni-easyinput v-model="state.model.region" disabled :inputBorder="false"
|
||||
:styles="{ disableColor: '#fff', color: '#333' }"
|
||||
placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal"
|
||||
placeholder="请选择省市区">
|
||||
<template v-slot:right>
|
||||
<uni-icons type="right"></uni-icons>
|
||||
</template>
|
||||
</uni-easyinput>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item name="address" label="详细地址" :formItemStyle="{ alignItems: 'flex-start' }"
|
||||
:labelStyle="{ lineHeight: '5em' }" class="textarea-item">
|
||||
<uni-easyinput :inputBorder="false" type="textarea" v-model="state.model.address"
|
||||
placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal"
|
||||
placeholder="请输入详细地址" clearable></uni-easyinput>
|
||||
</uni-forms-item>
|
||||
</view>
|
||||
|
||||
<view class="ss-m-y-20 bg-white ss-p-x-30 ss-flex ss-row-between ss-col-center default-box">
|
||||
<view class="default-box-title"> 设为默认地址 </view>
|
||||
<su-switch style="transform: scale(0.8)" v-model="state.model.is_default"></su-switch>
|
||||
</view>
|
||||
</uni-forms>
|
||||
<su-fixed bottom :opacity="false" bg="" placeholder :noFixed="false" :index="10">
|
||||
<view class="footer-box ss-flex-col ss-row-between ss-p-20">
|
||||
<view class="ss-m-b-20"><button class="ss-reset-button save-btn ui-Shadow-Main"
|
||||
@tap="onSave">保存</button></view>
|
||||
<button v-if="state.model.id" class="ss-reset-button cancel-btn" @tap="onDelete">
|
||||
删除
|
||||
</button>
|
||||
</view>
|
||||
</su-fixed>
|
||||
<!-- 省市区弹窗 -->
|
||||
<su-region-picker :show="state.showRegion" @cancel="state.showRegion = false" @confirm="onRegionConfirm">
|
||||
</su-region-picker>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
computed,
|
||||
watch,
|
||||
ref,
|
||||
reactive,
|
||||
unref
|
||||
} from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import {
|
||||
onLoad,
|
||||
onPageScroll
|
||||
} from '@dcloudio/uni-app';
|
||||
import _ from 'lodash';
|
||||
import {
|
||||
consignee,
|
||||
mobile,
|
||||
address,
|
||||
region
|
||||
} from '@/sheep/validate/form';
|
||||
|
||||
const addressFormRef = ref(null);
|
||||
const state = reactive({
|
||||
showRegion: false,
|
||||
model: {
|
||||
consignee: '',
|
||||
mobile: '',
|
||||
address: '',
|
||||
is_default: false,
|
||||
region: '',
|
||||
},
|
||||
rules: {
|
||||
consignee,
|
||||
mobile,
|
||||
address,
|
||||
region,
|
||||
},
|
||||
});
|
||||
watch(
|
||||
() => state.model.province_name,
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
state.model.region =
|
||||
`${state.model.province_name}-${state.model.city_name}-${state.model.district_name}`;
|
||||
}
|
||||
}, {
|
||||
deep: true,
|
||||
},
|
||||
);
|
||||
const onRegionConfirm = (e) => {
|
||||
state.model = {
|
||||
...state.model,
|
||||
...e,
|
||||
};
|
||||
state.showRegion = false;
|
||||
};
|
||||
const getAreaData = () => {
|
||||
if (_.isEmpty(uni.getStorageSync('areaData'))) {
|
||||
sheep.$api.data.area().then((res) => {
|
||||
if (res.code === 0) {
|
||||
uni.setStorageSync('areaData', res.data);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
const onSave = async () => {
|
||||
const validate = await unref(addressFormRef)
|
||||
.validate()
|
||||
.catch((error) => {
|
||||
console.log('error: ', error);
|
||||
});
|
||||
if (!validate) return;
|
||||
|
||||
let res = null;
|
||||
if (state.model.id) {
|
||||
res = await sheep.$api.user.address.update({
|
||||
id: state.model.id,
|
||||
areaId: state.model.district_id,
|
||||
defaultStatus: state.model.is_default,
|
||||
detailAddress: state.model.address,
|
||||
mobile: state.model.mobile,
|
||||
name: state.model.consignee
|
||||
});
|
||||
} else {
|
||||
res = await sheep.$api.user.address.create({
|
||||
areaId: state.model.district_id,
|
||||
defaultStatus: state.model.is_default,
|
||||
detailAddress: state.model.address,
|
||||
mobile: state.model.mobile,
|
||||
name: state.model.consignee
|
||||
});
|
||||
}
|
||||
if (res.code === 0) {
|
||||
sheep.$router.back();
|
||||
}
|
||||
};
|
||||
|
||||
const onDelete = () => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确认删除此收货地址吗?',
|
||||
success: async function(res) {
|
||||
if (res.confirm) {
|
||||
const {
|
||||
code
|
||||
} = await sheep.$api.user.address.delete(state.model.id);
|
||||
if (code === 0) {
|
||||
sheep.$router.back();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
onLoad(async (options) => {
|
||||
getAreaData();
|
||||
if (options.id) {
|
||||
let res = await sheep.$api.user.address.detail(options.id);
|
||||
if (res.code === 0) {
|
||||
state.model = {
|
||||
...state.model,
|
||||
district_id: res.data.areaId,
|
||||
is_default: res.data.defaultStatus,
|
||||
address: res.data.detailAddress,
|
||||
mobile: res.data.mobile,
|
||||
consignee: res.data.name,
|
||||
id: res.data.id,
|
||||
province_name: res.data.areaName.split(' ')[0],
|
||||
city_name: res.data.areaName.split(' ')[1],
|
||||
district_name: res.data.areaName.split(' ')[2]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (options.data) {
|
||||
let data = JSON.parse(options.data);
|
||||
console.log(data)
|
||||
state.model = {
|
||||
...state.model,
|
||||
...data,
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep() {
|
||||
.uni-forms-item__label .label-text {
|
||||
font-size: 28rpx !important;
|
||||
color: #333333 !important;
|
||||
line-height: normal !important;
|
||||
}
|
||||
|
||||
.uni-easyinput__content-input {
|
||||
font-size: 28rpx !important;
|
||||
color: #333333 !important;
|
||||
line-height: normal !important;
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
|
||||
.uni-easyinput__content-textarea {
|
||||
font-size: 28rpx !important;
|
||||
color: #333333 !important;
|
||||
line-height: normal !important;
|
||||
margin-top: 8rpx !important;
|
||||
}
|
||||
|
||||
.uni-icons {
|
||||
font-size: 40rpx !important;
|
||||
}
|
||||
|
||||
.is-textarea-icon {
|
||||
margin-top: 22rpx;
|
||||
}
|
||||
|
||||
.is-disabled {
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
|
||||
.default-box {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
height: 100rpx;
|
||||
|
||||
.default-box-title {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.footer-box {
|
||||
.save-btn {
|
||||
width: 710rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
color: $white;
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
width: 710rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
background: var(--ui-BG);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
147
pages/user/address/list.vue
Normal file
147
pages/user/address/list.vue
Normal file
@@ -0,0 +1,147 @@
|
||||
<template>
|
||||
<s-layout title="收货地址" :bgStyle="{ color: '#FFF' }">
|
||||
<view v-if="state.list.length">
|
||||
<s-address-item hasBorderBottom v-for="item in state.list" :key="item.id" :item="item"
|
||||
@tap="onSelect(item)">
|
||||
</s-address-item>
|
||||
</view>
|
||||
|
||||
<su-fixed bottom placeholder>
|
||||
<view class="footer-box ss-flex ss-row-between ss-p-20">
|
||||
<!-- 微信小程序和微信H5 -->
|
||||
<button v-if="['WechatMiniProgram', 'WechatOfficialAccount'].includes(sheep.$platform.name)"
|
||||
@tap="importWechatAddress"
|
||||
class="border ss-reset-button sync-wxaddress ss-m-20 ss-flex ss-row-center ss-col-center">
|
||||
<text class="cicon-weixin ss-p-r-10" style="color: #09bb07; font-size: 40rpx"></text>
|
||||
导入微信地址
|
||||
</button>
|
||||
<button class="add-btn ss-reset-button ui-Shadow-Main"
|
||||
@tap="sheep.$router.go('/pages/user/address/edit')">
|
||||
新增收货地址
|
||||
</button>
|
||||
</view>
|
||||
</su-fixed>
|
||||
<s-empty v-if="state.list.length === 0 && !state.loading" text="暂无收货地址" icon="/static/data-empty.png" />
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
reactive,
|
||||
onBeforeMount
|
||||
} from 'vue';
|
||||
import {
|
||||
onShow
|
||||
} from '@dcloudio/uni-app';
|
||||
import sheep from '@/sheep';
|
||||
import {
|
||||
isEmpty
|
||||
} from 'lodash';
|
||||
|
||||
const state = reactive({
|
||||
list: [],
|
||||
loading: true,
|
||||
});
|
||||
|
||||
// 选择收货地址
|
||||
const onSelect = (addressInfo) => {
|
||||
uni.$emit('SELECT_ADDRESS', {
|
||||
addressInfo,
|
||||
});
|
||||
sheep.$router.back();
|
||||
};
|
||||
|
||||
// 导入微信地址
|
||||
function importWechatAddress() {
|
||||
let wechatAddress = {};
|
||||
// #ifdef MP
|
||||
uni.chooseAddress({
|
||||
success: (res) => {
|
||||
wechatAddress = {
|
||||
consignee: res.userName,
|
||||
mobile: res.telNumber,
|
||||
province_name: res.provinceName,
|
||||
city_name: res.cityName,
|
||||
district_name: res.countyName,
|
||||
address: res.detailInfo,
|
||||
region: '',
|
||||
is_default: false,
|
||||
};
|
||||
if (!isEmpty(wechatAddress)) {
|
||||
sheep.$router.go('/pages/user/address/edit', {
|
||||
data: JSON.stringify(wechatAddress),
|
||||
});
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log('%cuni.chooseAddress,调用失败', 'color:green;background:yellow');
|
||||
},
|
||||
});
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
sheep.$platform.useProvider('wechat').jssdk.openAddress({
|
||||
success: (res) => {
|
||||
wechatAddress = {
|
||||
consignee: res.userName,
|
||||
mobile: res.telNumber,
|
||||
province_name: res.provinceName,
|
||||
city_name: res.cityName,
|
||||
district_name: res.countryName,
|
||||
address: res.detailInfo,
|
||||
region: '',
|
||||
is_default: false,
|
||||
};
|
||||
if (!isEmpty(wechatAddress)) {
|
||||
sheep.$router.go('/pages/user/address/edit', {
|
||||
data: JSON.stringify(wechatAddress),
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
// #endif
|
||||
}
|
||||
|
||||
onShow(async () => {
|
||||
state.list = (await sheep.$api.user.address.list()).data;
|
||||
state.loading = false;
|
||||
});
|
||||
|
||||
onBeforeMount(() => {
|
||||
if (!!uni.getStorageSync('areaData')) {
|
||||
return;
|
||||
}
|
||||
// 提前加载省市区数据
|
||||
sheep.$api.data.area().then((res) => {
|
||||
if (res.error === 0) {
|
||||
uni.setStorageSync('areaData', res.data);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.footer-box {
|
||||
.add-btn {
|
||||
flex: 1;
|
||||
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
border-radius: 80rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
line-height: 80rpx;
|
||||
color: $white;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.sync-wxaddress {
|
||||
flex: 1;
|
||||
line-height: 80rpx;
|
||||
background: $white;
|
||||
border-radius: 80rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
color: $dark-6;
|
||||
margin-right: 18rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user