Browse Source

Initial commit

master
Augcl 2 months ago
commit
7855a8cf3f
626 changed files with 64773 additions and 0 deletions
  1. +14
    -0
      .gitignore
  2. +23
    -0
      App.vue
  3. +36
    -0
      README.en.md
  4. +411
    -0
      README.md
  5. +105
    -0
      api/api.js
  6. +77
    -0
      api/http.js
  7. +215
    -0
      api/model/index.js
  8. +115
    -0
      api/model/info.js
  9. +35
    -0
      api/model/login.js
  10. +20
    -0
      api/model/vip.js
  11. +40
    -0
      common.scss
  12. +143
    -0
      components/base/navbar.vue
  13. +134
    -0
      components/base/tabbar.vue
  14. +169
    -0
      components/config/PrivacyAgreementPoup.vue
  15. +48
    -0
      components/config/configPopup.vue
  16. +122
    -0
      components/config/customerServicePopup.vue
  17. +112
    -0
      components/user/productList.vue
  18. +130
    -0
      components/userShop/userShopCommission.vue
  19. +56
    -0
      config.js
  20. +20
    -0
      index.html
  21. +45
    -0
      main.js
  22. +105
    -0
      manifest.json
  23. +47
    -0
      mixins/configList.js
  24. +78
    -0
      mixins/list.js
  25. +70
    -0
      mixins/order.js
  26. +20
    -0
      package.json
  27. +84
    -0
      pages.json
  28. +260
    -0
      pages/index/cart.vue
  29. +191
    -0
      pages/index/category.vue
  30. +353
    -0
      pages/index/center.vue
  31. +412
    -0
      pages/index/index.vue
  32. +263
    -0
      pages/index/order.vue
  33. +514
    -0
      pages_order/auth/loginAndRegisterAndForgetPassword.vue
  34. +155
    -0
      pages_order/auth/wxLogin.vue
  35. +133
    -0
      pages_order/auth/wxUserInfo.vue
  36. +244
    -0
      pages_order/components/address/addressList.vue
  37. +220
    -0
      pages_order/components/address/redactAddress.vue
  38. +77
    -0
      pages_order/components/product/submit.vue
  39. +367
    -0
      pages_order/components/product/submitUnitSelect.vue
  40. +213
    -0
      pages_order/mine/address.vue
  41. +172
    -0
      pages_order/mine/help.vue
  42. +283
    -0
      pages_order/mine/promotion.vue
  43. +109
    -0
      pages_order/mine/purse.vue
  44. +88
    -0
      pages_order/mine/runningWater.vue
  45. +567
    -0
      pages_order/order/orderDetail.vue
  46. +353
    -0
      pages_order/order/refundsOrExchange.vue
  47. +169
    -0
      pages_order/product/productDetail.vue
  48. BIN
      pages_order/static/address/icon.png
  49. BIN
      pages_order/static/address/selectIcon.png
  50. BIN
      pages_order/static/auth/headImage.png
  51. BIN
      pages_order/static/auth/wx.png
  52. BIN
      pages_order/static/help/uploading.png
  53. BIN
      pages_order/static/order/icon.png
  54. BIN
      pages_order/static/product/like.png
  55. BIN
      static/image/PrivacyAgreementPoup/icon.png
  56. BIN
      static/image/cart/1.png
  57. BIN
      static/image/cart/2.png
  58. BIN
      static/image/center/1.png
  59. BIN
      static/image/center/10.png
  60. +1
    -0
      static/image/center/11.svg
  61. BIN
      static/image/center/2.png
  62. BIN
      static/image/center/3.png
  63. BIN
      static/image/center/4.png
  64. BIN
      static/image/center/5.png
  65. BIN
      static/image/center/6.png
  66. BIN
      static/image/center/7.png
  67. BIN
      static/image/center/8.png
  68. BIN
      static/image/center/9.png
  69. BIN
      static/image/home/0.png
  70. BIN
      static/image/home/1.png
  71. BIN
      static/image/home/2.png
  72. BIN
      static/image/home/3.png
  73. BIN
      static/image/home/address-icon.png
  74. BIN
      static/image/home/arrow-icon.png
  75. BIN
      static/image/home/search-icon.png
  76. BIN
      static/image/product/favorable.png
  77. BIN
      static/image/tabbar/cart-a.png
  78. BIN
      static/image/tabbar/cart.png
  79. BIN
      static/image/tabbar/category-a.png
  80. BIN
      static/image/tabbar/category.png
  81. BIN
      static/image/tabbar/center-a.png
  82. BIN
      static/image/tabbar/center.png
  83. BIN
      static/image/tabbar/home-a.png
  84. BIN
      static/image/tabbar/home.png
  85. BIN
      static/image/tabbar/order-a.png
  86. BIN
      static/image/tabbar/order.png
  87. BIN
      static/logo.png
  88. +110
    -0
      store/store.js
  89. +10
    -0
      uni.promisify.adaptor.js
  90. +96
    -0
      uni.scss
  91. +7
    -0
      uni_modules/uv-action-sheet/changelog.md
  92. +50
    -0
      uni_modules/uv-action-sheet/components/uv-action-sheet/props.js
  93. +280
    -0
      uni_modules/uv-action-sheet/components/uv-action-sheet/uv-action-sheet.vue
  94. +92
    -0
      uni_modules/uv-action-sheet/package.json
  95. +13
    -0
      uni_modules/uv-action-sheet/readme.md
  96. +10
    -0
      uni_modules/uv-album/changelog.md
  97. +312
    -0
      uni_modules/uv-album/components/uv-album/uv-album.vue
  98. +88
    -0
      uni_modules/uv-album/package.json
  99. +21
    -0
      uni_modules/uv-album/readme.md
  100. +7
    -0
      uni_modules/uv-alert/changelog.md

+ 14
- 0
.gitignore View File

@ -0,0 +1,14 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
.idea
package-lock.json
node_modules/
unpackage/
.hbuilderx
.vite

+ 23
- 0
App.vue View File

@ -0,0 +1,23 @@
<script>
export default {
onLaunch: function() {
},
onShow: function() {
// this.$store.commit('initConfig')
},
onHide: function() {
}
}
</script>
<style>
@import url("common.scss");
/*每个页面公共css */
body{
background-color: #f7f7f7;
font-size: 30rpx;
}
.page{
padding-top: var(--window-top);
}
</style>

+ 36
- 0
README.en.md View File

@ -0,0 +1,36 @@
# uniapp项目开发模板
#### Description
{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**}
#### Software Architecture
Software architecture description
#### Installation
1. xxxx
2. xxxx
3. xxxx
#### Instructions
1. xxxx
2. xxxx
3. xxxx
#### Contribution
1. Fork the repository
2. Create Feat_xxx branch
3. Commit your code
4. Create Pull Request
#### Gitee Feature
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
4. The most valuable open source project [GVP](https://gitee.com/gvp)
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)

+ 411
- 0
README.md View File

@ -0,0 +1,411 @@
# 商城项目文档
## 项目概述
本项目是一个基于uni-app开发的商城小程序,采用Vue框架开发,集成了完整的商城功能模块。
## 目录结构
```
├── api # API接口目录
│ ├── api.js # API统一出口
│ ├── http.js # HTTP请求封装
│ └── model # 业务模块API
├── components # 公共组件
├── mixins # 混入文件
├── pages # 页面文件
├── static # 静态资源
├── store # Vuex状态管理
├── utils # 工具函数
└── uni_modules # uni-app插件模块
```
## 分包结构说明
### pages_order分包
分包是小程序优化加载性能的重要手段,pages_order作为独立分包,包含以下模块:
```
├── auth # 认证相关页面
│ ├── loginAndRegisterAndForgetPassword.vue # 登录注册
│ ├── wxLogin.vue # 微信登录
│ └── wxUserInfo.vue # 微信用户信息
├── components # 分包内公共组件
│ ├── address/ # 地址选择组件
│ ├── areaSelector/ # 区域选择器
│ └── product/ # 商品相关组件
├── home # 首页相关
│ ├── addEnterprise.vue # 添加企业
│ ├── contact.vue # 联系我们
│ ├── introduce.vue # 介绍页面
│ ├── journalism.vue # 新闻资讯
│ └── notice.vue # 公告
├── mine # 我的模块
│ ├── address.vue # 收货地址
│ ├── balance.vue # 余额
│ ├── commission.vue # 佣金
│ ├── coupon.vue # 优惠券
│ ├── memberCenter.vue # 会员中心
│ └── more... # 更多功能页面
├── order # 订单模块
│ ├── createOrder.vue # 创建订单
│ ├── orderDetail.vue # 订单详情
│ └── giftList.vue # 礼品列表
├── product # 商品模块
│ └── productDetail.vue # 商品详情
└── static # 分包静态资源
├── address/ # 地址相关图片
├── auth/ # 认证相关图片
├── coupon/ # 优惠券图片
└── more... # 其他静态资源
```
**分包特点:**
- 静态资源就近原则:分包相关的图片等静态资源存放在分包目录下,避免主包体积过大
- 模块化组织:按功能模块划分目录,便于维护和管理
- 组件复用:分包内的通用组件集中管理,提高代码复用性
## 配置文件说明
### config.js
项目核心配置文件,包含以下配置:
**1. 环境配置**
```javascript
// 当前环境
const type = 'prod'
// 环境配置
const config = {
dev: {
baseUrl: 'http://h5.xzaiyp.top/jewelry-admin',
},
prod: {
baseUrl: 'https://jewelry-admin.hhlm1688.com/jewelry-admin',
}
}
```
**2. 默认配置**
```javascript
const defaultConfig = {
// 腾讯地图Key
mapKey: 'XMBBZ-BCPCV-SXPPQ-5Y7MY-PHZXK-YFFVU',
// 阿里云OSS配置
aliOss: {
url: 'https://image.hhlm1688.com/',
config: {
region: 'oss-cn-guangzhou',
accessKeyId: '***',
accessKeySecret: '***',
bucket: 'hanhaiimage',
endpoint: 'oss-cn-shenzhen.aliyuncs.com',
}
}
}
```
**3. UI框架配置**
```javascript
uni.$uv.setConfig({
config: {
unit: 'rpx' // 设置默认单位
},
})
// UI文档地址 https://www.uvui.cn/
```
## 核心模块详解
### 1. Mixins 混入
#### 1.1 list.js - 列表数据加载混入
提供列表数据的加载、分页、下拉刷新、上拉加载更多等功能。
**主要功能:**
- 统一的分页参数处理
- 下拉刷新和上拉加载更多
- 数据加载状态管理
**使用示例:**
```javascript
// 在页面中使用list混入
import listMixin from '@/mixins/list.js'
export default {
mixins: [listMixin],
data() {
return {
// 指定API接口
mixinsListApi: 'productList'
}
}
}
```
#### 1.2 configList.js - 全局配置混入
已全局引入的配置管理混入,无需手动引入即可使用。
**主要功能:**
- 统一的分享配置
- 全局配置管理
- 用户信息关联
**配置参数:**
```javascript
// 分享配置示例
this.Gshare.title = '分享标题'
this.Gshare.path = '分享路径'
```
### 2. API 模块
#### 2.1 http.js - 请求封装
统一的HTTP请求处理,包含:
- 请求拦截器
- 响应拦截器
- 统一的错误处理
- Token管理
#### 2.2 api.js - 接口管理
统一管理API接口,支持模块化组织。API模块采用分层结构,便于维护和扩展。
**目录结构:**
```
api/
├── api.js # API统一出口
├── http.js # HTTP请求封装
└── model/ # 业务模块API
├── product.js # 商品相关接口
├── order.js # 订单相关接口
└── user.js # 用户相关接口
```
**接口定义示例:**
```javascript
// api/model/product.js
export default {
// GET请求示例
list: {
url: '/api/product/list',
method: 'GET',
loading: true // 显示加载提示
},
// POST请求示例
create: {
url: '/api/product/create',
method: 'POST',
loading: true // 显示加载提示
auth : true,//效验登录
debounce : 1000,//接口防抖,1s
limit : 500,//接口限流,0.5s
},
}
```
**调用接口示例:**
```javascript
// 第一种写法:callback方式处理响应
this.$api('product.list', {
pageNo: 1,
pageSize: 10,
categoryId: '123'
}, res => {
// 处理列表数据
})
// 第二种写法:Promise方式处理响应
this.$api('product.create', {
name: '商品名称',
price: 99.99,
description: '商品描述'
}).then(res => {
if (res.code === 200) {
// 创建成功
uni.showToast({ title: '创建成功' })
}
})
```
### 3. 公共代码
#### 3.1 工具函数 (utils)
- authorize.js: 授权处理
- pay.js: 微信网页支付相关
- utils.js: 通用工具函数
- timeUtils.js: 时间处理
- position.js: 定位与位置计算
- oss-upload: 阿里云OSS上传模块
**使用示例:**
```javascript
// 授权处理
async preservationImg(img) {
await this.$authorize('scope.writePhotosAlbum')
//在执行$authorize之后,await下面的代码都是确保授权完成的情况下执行
},
// 时间格式化
const formattedTime = this.$timeUtils.formatTime(new Date())
// 项目中在Vue集成了dayjs >>,可以直接使用
// 在utils/index中Vue.prototype.$dayjs
this.$dayjs()
// 微信网页支付调用
import { wxPay } from '@/utils/pay'
wxPay(orderData)
```
#### 3.2 公共组件
- navbar.vue: 自定义导航栏
- tabbar.vue: 底部导航栏
**使用示例:**
```html
<template>
<view>
<navbar title="商品列表" />
</view>
</template>
```
#### 3.3 OSS上传模块
**配置说明:**
项目使用阿里云OSS进行文件存储,相关配置位于config.js中:
**使用示例:**
1. 单文件上传
```javascript
export default {
methods: {
onUpload(file) {
this.$Oss.ossUpload(file.path).then(url => {
this.filePath = url
})
}
}
}
```
2. 在uv-upload组件中使用
```html
<template>
<uv-upload
:fileList="fileList"
@afterRead="afterRead"
@delete="deleteImage"
name="1"
multiple
:maxCount="maxCount"
></uv-upload>
</template>
<script>
export default {
data() {
return {
fileList: [],
maxCount: 9
}
},
methods: {
// 新增图片
afterRead(e) {
e.file.forEach(file => {
this.$Oss.ossUpload(file.url).then(url => {
this.fileList.push({
url
})
})
})
},
// 删除图片
deleteImage(e) {
this.fileList.splice(e.index, 1)
},
}
}
</script>
```
**注意事项:**
1. 上传前请确保OSS配置正确
2. 建议对上传文件大小进行限制
3. 支持的文件类型:图片、视频、文档等
4. 上传失败时会抛出异常,请做好错误处理
## 最佳实践
### 1. 列表页面开发
```javascript
// pages/product/list.vue
import listMixin from '@/mixins/list.js'
export default {
mixins: [listMixin],
data() {
return {
mixinsListApi: 'productList',
}
},
methods: {
// 分类切换
onCategoryChange(categoryId) {
this.queryParams.categoryId = categoryId
this.getData()
}
}
}
```
### 2. 详情页面开发
```javascript
// pages/product/detail.vue
import configMixin from '@/mixins/configList.js'
export default {
mixins: [configMixin],
data() {
return {
productId: '',
detail: {}
}
},
onLoad(options) {
this.productId = options.id
this.getDetail()
},
methods: {
getDetail() {
this.$api('productDetail', {
id: this.productId
}, res => {
this.detail = res.result
// 设置分享信息
this.Gshare.title = this.detail.name
this.Gshare.path = `/pages/product/detail?id=${this.productId}`
})
}
}
}
```
## 注意事项
1. 使用mixins时注意命名冲突
2. API调用建议统一使用this.$api方式
3. 页面开发建议继承相应的混入来复用通用功能
## 常见问题
1. 列表加载失败
- 检查mixinsListApi是否正确配置
- 确认网络请求是否正常
- 查看请求参数格式是否正确

+ 105
- 0
api/api.js View File

@ -0,0 +1,105 @@
import http from './http.js'
import utils from '../utils/utils.js'
let limit = {}
let debounce = {}
const models = ['login', 'index', 'vip', 'info']
const config = {
// 示例
// wxLogin : {url : '/api/wxLogin', method : 'POST',
// auth : false, showLoading : true, loadingTitle : '加载中...',
// limit : 1000
// },
getConfig : {url : '/config_common/getConfig', method : 'GET', limit : 500},
}
export function api(key, data, callback, loadingTitle) {
let req = config[key]
if (!req) {
console.error('无效key' + key);
return Promise.reject()
}
if (typeof callback == 'string') {
loadingTitle = callback
}
if (typeof data == 'function') {
callback = data
data = {}
}
// 接口限流
if (req.limit) {
let storageKey = req.url
let storage = limit[storageKey]
if (storage && new Date().getTime() - storage < req.limit) {
return Promise.reject()
}
limit[storageKey] = new Date().getTime()
}
//必须登录
if (req.auth) {
if (!uni.getStorageSync('token')) {
utils.toLogin()
console.error('需要登录', req.url)
return Promise.reject()
}
}
// 接口防抖
if(req.debounce){
let storageKey = req.url
let storage = debounce[storageKey]
if (storage) {
clearTimeout(storage)
}
debounce[storageKey] = setTimeout(() => {
clearTimeout(storage)
delete debounce[storageKey]
http.http(req.url, data, callback, req.method,
loadingTitle || req.showLoading, loadingTitle || req.loadingTitle)
}, req.debounce)
return Promise.reject()
}
return http.http(req.url, data, callback, req.method,
loadingTitle || req.showLoading, loadingTitle || req.loadingTitle)
}
function addApiModel(model, key){
for(let k in model){
if(config[`${k}`]){
console.error(`重名api------model=${key},key=${k}`);
uni.showModal({
title: `重名api`,
content: `model=${key},key=${k}`
})
continue
}
config[`${k}`] = model[k]
// config[`${key}_${k}`] = model[k]
}
}
models.forEach(key => {
addApiModel(require(`./model/${key}.js`).default, key)
})
export default api

+ 77
- 0
api/http.js View File

@ -0,0 +1,77 @@
import Vue from 'vue'
import utils from '../utils/utils.js'
import store from '../store/store.js'
function http(uri, data, callback, method = 'GET', showLoading, title) {
if(showLoading){
uni.showLoading({
title: title || '加载中...'
});
}
let reject, resolve;
let promise = new Promise((res, rej) => {
reject = rej
resolve = res
})
uni.request({
url: Vue.prototype.$config.baseUrl + uri,
data,
method: method,
header: {
'X-Access-Token': uni.getStorageSync('token'),
'Content-Type' : 'application/x-www-form-urlencoded'
},
success: (res) => {
// console.log(res,'res')
if(showLoading){
uni.hideLoading();
}
if(res.statusCode == 401 ||
res.data.message == '操作失败,token非法无效!' ||
res.data.message == '操作失败,用户不存在!'){
store.commit('logout')
console.error('登录过期');
utils.toLogin()
}
if(res.statusCode == 200 && res.data.code != 200
&& res.data.code != 902){
uni.showToast({
mask: true,
duration: 1000,
title: res.data.message,
icon:'none'
});
}
callback && callback(res.data)
resolve(res.data)
},
fail: () => {
reject('api fail')
uni.showLoading({})
setTimeout(()=>{
uni.hideLoading()
uni.showToast({icon:"none", title:"网络异常"})
}, 3000)
if(showLoading){
uni.hideLoading();
}
}
});
return promise
}
export default {
http: http,
}

+ 215
- 0
api/model/index.js View File

@ -0,0 +1,215 @@
// 首页相关接口
const api = {
// 获取首页轮播图
getRiceBanner: {
url: '/index_common/getRiceBanner',
method: 'GET',
},
// 获取首页常规产品【废弃】
// getRiceCommonProductList: {
// url: '/index_common/getRiceCommonProductList',
// method: 'GET',
// },
// 获取首页跳转图标
getRiceIconList: {
url: '/index_common/getRiceIconList',
method: 'GET',
},
// 获取首页新闻详情
getRiceNewsDetail: {
url: '/index_common/getCommonNewsDetail',
method: 'GET',
},
// 获取首页新闻列表
getRiceNewsList: {
url: '/index_common/getRiceNewsList',
method: 'GET',
},
// 获取首页公告列表
getRiceNoticeList: {
url: '/index_common/getRiceNoticeList',
method: 'GET',
},
// 获取首页商品详情
getRiceProductDetail: {
url: '/index_common/getRiceProductDetail',
method: 'GET',
},
// 获取首页体验产品
getRiceProductList: {
url: '/index_common/getRiceProductList',
method: 'GET',
},
// 查询分类接口
getCategoryList: {
url: '/index_common/getCategoryList',
method: 'GET',
},
// 新查询分类以及商品数据接口
getCategoryPidList: {
url: '/index_common/getCategoryPidList',
method: 'GET',
debounce : 250,
},
// 查询一级分类接口
getPidList: {
url: '/index_common/getCategoryPidList',
method: 'GET',
},
// 获取分类分页商品列表接口
getClassShopPageList: {
url: '/index_common/getClassShopPageList',
method: 'GET',
},
// 加入购物车
addCart: {
url: '/index_common/addCart',
method: 'GET',
auth: true,
showLoading: true,
limit : 500,
},
// 删除购物车信息
deleteCart: {
url: '/index_common/deleteCart',
method: 'DELETE',
auth: true,
showLoading: true,
},
// 修改购物车信息数量
updateCartNum: {
url: '/index_common/updateCartNum',
method: 'POST',
auth: true,
debounce: 300,
},
// 创建订单
createOrder: {
url: '/index_common/createOrder',
method: 'GET',
auth: true,
limit: 1000,
showLoading: true,
},
// 创建订单-再次支付
createOrderTwo: {
url: '/index_common/createOrderTwo',
method: 'GET',
auth: true,
limit: 1000,
showLoading: true,
},
// 多商品创建订单
createSumOrder: {
url: '/index_common/createSumOrder',
method: 'POST',
auth: true,
limit: 1000,
showLoading: true,
},
// 多商品订单再次支付
createSumOrderAgain: {
url: '/index_common/createSumOrderAgain',
method: 'POST',
auth: true,
limit: 1000,
showLoading: true,
},
// 确认收货
confirmOrder: {
url: '/index_common/confirmOrder',
method: 'GET',
auth: true,
limit: 1000,
showLoading: true,
},
// 取消订单
cancelOrder: {
url: '/index_common/cancelOrder',
method: 'GET',
auth: true,
limit: 1000,
showLoading: true,
},
// 获取首页广告列表
getRiceProductList: {
url: '/index_common/getRiceAdList',
method: 'GET',
},
// 获取首页广告列表
getRiceAdDetail: {
url: '/index_common/getRiceAdDetail',
method: 'GET',
},
//获取优惠券信息
getRiceCouponList: {
url: '/info_common/getRiceCouponList',
method: 'GET',
},
//增加或者修改合伙人申请信息
addOrUpdateCommonUser: {
url: '/index_common/addOrUpdateCommonUser',
method: 'POST',
},
//根据用户查询渠合伙人申请信息表单
getCommonUser: {
url: '/index_common/getCommonUser',
method: 'GET'
},
//提交反馈信息
addFeedback: {
url: '/info_common/addFeedback',
method: 'POST'
},
// 获取我的直接推荐间接推荐用户列表带分页
getHanHaiMemberUser: {
url: '/info_common/getHanHaiMemberUser',
method: 'GET'
},
// 获取祝福背景图
getRiceBlessing: {
url: '/index_common/getRiceBlessing',
method: 'GET'
},
// 随机获取祝福语
getRiceBlessingWords: {
url: '/index_common/getRiceBlessingWords',
method: 'GET'
},
// 根据订单标识修改订单祝福语背景
updateOrderBlessing: {
url: '/index_common/updateOrderBlessing',
method: 'POST',
auth : true,
limit : 1000,
},
// 1.收礼流程 =》点击收礼
getGiveShop: {
url: '/index_common/getGiveShop',
method: 'GET',
auth : true,
limit : 1000,
},
// 2.点击抽奖 =》抽奖
getGiveShopLottery: {
url: '/index_common/getGiveShopLottery',
method: 'GET',
auth : true,
limit : 1000,
},
// 获取我的礼品订单
getMyGiftOrder: {
url: '/index_common/getMyGiftOrder',
method: 'GET',
auth : true,
},
// 获取我的礼品订单详情
getMyGiftOrderDetail: {
url: '/index_common/getMyGiftOrderDetail',
method: 'GET',
auth : true,
},
}
export default api

+ 115
- 0
api/model/info.js View File

@ -0,0 +1,115 @@
// 个人相关接口
const api = {
// 充值
recharge: {
url: '/info_common/withdraw',
method: 'GET',
auth: true,
limit: 1000,
showLoading: true,
},
// 提现
withdraw: {
url: '/info_common/withdraw',
method: 'GET',
auth: true,
limit: 1000,
showLoading: true,
},
// 获取地址列表带分页
getAddressPageList: {
url: '/info_common/getAddressPageList',
method: 'GET',
auth: true,
},
// 增加或修改地址信息
addOrUpdateAddress: {
url: '/info_common/addOrUpdateAddress',
method: 'POST',
limit: 500,
auth: true,
showLoading: true,
},
// 删除地址
deleteAddress: {
url: '/info_common/deleteAddress',
method: 'GET',
limit: 500,
auth: true,
showLoading: true,
},
// 修改默认地址
updateDefaultAddress: {
url: '/info_common/updateDefaultAddress',
method: 'GET',
auth: true,
limit: 1000,
},
// 获取粉丝列表带分页
getFansPageList: {
url: '/info_common/getFansPageList',
method: 'GET',
auth: true,
},
// 获取相关介绍
getInfoIntroduce: {
url: '/info_common/getInfoIntroduce',
method: 'GET',
auth: true,
},
// 获取个人邀请码
getInviteCode: {
url: '/info_common/getInviteCode',
method: 'GET',
auth: true,
},
// 获取订单列表带分页
getOrderPageList: {
url: '/info_common/getOrderPageList',
method: 'GET',
auth: true,
},
// 获取订单详情
getOrderDetail: {
url: '/info_common/getOrderDetail',
method: 'GET',
auth: true,
},
// 获取流水记录带分页
getWaterPageList: {
url: '/info_common/getWaterPageList',
method: 'GET',
auth: true,
},
// 获取相关介绍
getInfoIntroduce: {
url: '/info_common/getInfoIntroduce',
method: 'GET',
},
// 获取相关介绍详情
getInfoIntroduceDetail: {
url: '/info_common/getRiceNewsDetail',
method: 'GET',
},
// 查询个人信息相关
getRiceInfo: {
url: '/info_common/getRiceInfo',
method: 'GET',
limit: 500,
},
// 获取购物车信息列表带分页
getCartPageList: {
url: '/info_common/getCartPageList',
method: 'GET',
},
// 领取新人优惠券
getRiceCoupon: {
url: '/info_common/getRiceCoupon',
method: 'GET',
limit: 500,
auth: true,
},
}
export default api

+ 35
- 0
api/model/login.js View File

@ -0,0 +1,35 @@
// 登录相关接口
const api = {
// 微信登录接口
wxLogin: {
url: '/login_common/appletLogin',
method: 'GET',
limit : 500,
showLoading : true,
},
// 获取绑定手机号码
bindPhone: {
url: '/login_common/bindPhone',
method: 'GET',
auth: true,
},
// 修改个人信息接口
updateInfo: {
url: '/info_common/updateInfo',
method: 'POST',
auth: true,
limit : 500,
showLoading : true,
},
// 获取个人信息
getInfo: {
url: '/info_common/getInfo',
method: 'GET',
auth: true,
},
}
export default api

+ 20
- 0
api/model/vip.js View File

@ -0,0 +1,20 @@
// vip相关接口
const api = {
// 获取会员权益列表
getRiceVipList: {
url: '/index_common/getVipInfoList',
method: 'GET',
},
// 申请成为会员
applyRiceVip: {
url: '/rice_vip/applyRiceVip',
method: 'POST',
limit : 500,
auth : true,
showLoading : true,
},
}
export default api

+ 40
- 0
common.scss View File

@ -0,0 +1,40 @@
// 文字益处处理
.text-ellipsis{
overflow:hidden; //超出的文本隐藏
text-overflow:ellipsis; //溢出用省略号显示
white-space:nowrap; //溢出不换行
}
.text-ellipsis-2{
overflow: hidden;
text-overflow: ellipsis;
display:-webkit-box; //作为弹性伸缩盒子模型显示
-webkit-box-orient:vertical; //设置伸缩盒子的子元素排列方式--从上到下垂直排列
-webkit-line-clamp:2; //显示的行
}
.text-ellipsis-3{
overflow: hidden;
text-overflow: ellipsis;
display:-webkit-box; //作为弹性伸缩盒子模型显示
-webkit-box-orient:vertical; //设置伸缩盒子的子元素排列方式--从上到下垂直排列
-webkit-line-clamp:3; //显示的行
}
.share{
padding: 0;
margin: 0;
background-color: #fff;
display: flex !important;
flex-direction: column !important;
justify-content: center !important;
align-items: center !important;
font-size: 26rpx;
}
.share::after{
border: none;
padding: 0;
margin: 0;
width: 0;
height: 0;
}

+ 143
- 0
components/base/navbar.vue View File

@ -0,0 +1,143 @@
<template>
<!-- <view class="navbar"
:style="{backgroundColor : bgColor}"> -->
<view class="title"
:style="{backgroundColor : bgColor,color}">
<view class="left">
<uv-icon name="home"
v-if="leftClick && length == 1"
@click="toHome"
:color="color" size="46rpx"></uv-icon>
<uv-icon name="arrow-left"
v-else-if="leftClick"
@click="$emit('leftClick')"
:color="color" size="46rpx"></uv-icon>
</view>
<view>{{ title }}</view>
<view class="icon">
<uv-icon name="search"
v-if="isSearch"
:color="color" size="58rpx"></uv-icon>
<uv-icon name="plus-circle" :color="color"
v-if="isPlus"
@click="plusCircleShow = true"
size="46rpx" style="margin-left: 30rpx;"></uv-icon>
<view v-if="moreClick" style="margin-left: 30rpx;">
<uv-icon name="more-dot-fill" :color="color"
v-if="!moreText"
@click="moreClick()"
size="46rpx"></uv-icon>
<view v-else @click="moreClick"
style="font-weight: 400;font-size: 30rpx;">
{{ moreText }}
</view>
</view>
</view>
</view>
<!-- </view> -->
</template>
<script>
export default {
name:"navbar",
props : {
title : {
type : String,
default : ''
},
leftClick : {
type : Boolean,
},
moreClick : {
type : Function,
},
isSearch : {
type : Boolean,
default : false,
},
isPlus : {
type : Boolean,
default : false,
},
moreText : {
},
bgColor : {
default : '#fff'
},
color : {
default : '#333'
}
},
created() {
},
beforeDestroy() {
},
data() {
return {
length : getCurrentPages().length
};
},
methods : {
toHome(){
if(this.length != 1){
return
}
uni.reLaunch({
url: '/pages/index/index'
})
}
}
}
</script>
<style lang="scss" scoped>
// .navbar{
// width: 100%;
// height: 120rpx;
// padding-top: var(--status-bar-height);
// }
.title{
position: sticky;
top: 0;
left: 0;
padding-top: calc(var(--status-bar-height) + 20rpx);
width: 100%;
height: 100rpx;
background-color: #fff;
display: flex;
justify-content: center;
font-size: 32rpx;
align-items: center;
z-index: 999;
.left{
position: absolute;
left: 40rpx;
display: flex;
justify-content: flex-start;
}
.icon{
position: absolute;
right: 40rpx;
display: flex;
justify-content: flex-end;
}
}
@keyframes fade-in {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
</style>

+ 134
- 0
components/base/tabbar.vue View File

@ -0,0 +1,134 @@
<template>
<view class="tabbar-box">
<view class="tabbar">
<view :class="{ 'tabbar-active' : select == item.key}" v-for="(item, index) in list" :key="index"
v-if="!item.isNotShop || !userShop" @click="toPath(item, index)" class="tabbar-item">
<view class="tabbar-icon">
<image :src="select == item.key ?
item.selectedIconPath :
item.iconPath" class="tabbar-icon-image" mode="aspectFill"></image>
</view>
<view class="tabbar-title">
{{ item.title }}
</view>
</view>
</view>
</view>
</template>
<script>
import {
mapGetters
} from 'vuex'
export default {
name: "tabbar",
props: ['select'],
computed: {
...mapGetters(['userShop']),
},
data() {
return {
list: [{
"selectedIconPath": "/static/image/tabbar/home-a.png",
"iconPath": "/static/image/tabbar/home.png",
"pagePath": "/pages/index/index",
"title": "首页",
key: 'home',
},
{
"selectedIconPath": "/static/image/tabbar/category-a.png",
"iconPath": "/static/image/tabbar/category.png",
"pagePath": "/pages/index/category",
"title": "商品列表",
key: 'category',
},
{
"selectedIconPath": "/static/image/tabbar/order-a.png",
"iconPath": "/static/image/tabbar/order.png",
"pagePath": "/pages/index/order",
"title": "订单",
key: 'order',
},
{
"selectedIconPath": "/static/image/tabbar/cart-a.png",
"iconPath": "/static/image/tabbar/cart.png",
"pagePath": "/pages/index/cart",
"title": "购物车",
key: 'cart',
},
{
"selectedIconPath": "/static/image/tabbar/center-a.png",
"iconPath": "/static/image/tabbar/center.png",
"pagePath": "/pages/index/center",
"title": "我的",
key: 'center',
}
]
};
},
methods: {
toPath(item, index) {
if (item.key == this.select) {
return
}
uni.reLaunch({
url: item.pagePath
})
},
}
}
</script>
<style scoped lang="scss">
.tabbar-box {
height: 120rpx;
padding-bottom: env(safe-area-inset-bottom);
.tabbar {
position: fixed;
width: 750rpx;
background-color: #fff;
display: flex;
justify-content: center;
align-items: center;
flex-direction: row;
height: 120rpx;
padding-bottom: env(safe-area-inset-bottom);
z-index: 999999;
bottom: 0;
left: 0;
color: #BCBCBC;
.tabbar-item {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.tabbar-icon {
width: 54rpx;
height: 54rpx;
.tabbar-icon-image {
width: 54rpx;
height: 54rpx;
}
}
.tabbar-title {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
font-size: 23rpx;
line-height: 35rpx;
}
}
.tabbar-active {
color: $uni-color !important;
}
}
}
</style>

+ 169
- 0
components/config/PrivacyAgreementPoup.vue View File

@ -0,0 +1,169 @@
<template>
<uv-popup ref="popup" z-index="99999" :closeOnClickOverlay="false" :customStyle="{ backgroundColor: 'transparent' }">
<view class="privacyPopup">
<view class="icon">
<image src="/static/image/PrivacyAgreementPoup/icon.png"
mode=""></image>
</view>
<view class="title">
<view>协议与隐私政策</view>
</view>
<view class="content_pri">
<view class="text">
欢迎来到酒店布草!我们根据最新的法律法规监管政策要求更新了用户协议隐私政策,请您认真阅读
</view>
</view>
<view class="config">
<uv-checkbox-group v-model="checkboxValue" shape="circle">
<view class="content">
<view style="display: flex;">
<!-- <uv-checkbox size="30rpx" :name="1"></uv-checkbox> -->
同意<text @click="goToPrivacy">酒店布草隐私政策</text>
</view>
<view class="">
以及<text @click="goToPrivacy">用户协议</text>
</view>
</view>
</uv-checkbox-group>
</view>
<view class="pri_btn">
<button class="confuse_btn" @click="confusePrivacy">拒绝</button>
<button
class="confirm_btn" id="agree-btn"
open-type="agreePrivacyAuthorization"
@agreeprivacyauthorization="handleAgreePrivacyAuthorization">同意</button>
</view>
</view>
</uv-popup>
</template>
<script>
export default {
name: 'PrivacyAgreementPoup',
data() {
return {
resolvePrivacyAuthorization: {},
checkboxValue : false
}
},
mounted() {
if(wx.getPrivacySetting){
wx.getPrivacySetting({
success: res => {
console.log(res)
if (res.needAuthorization) {
//
this.init()
}
},
fail: () => {}
})
}
},
methods: {
//
init(resolve) {
this.$refs.popup.open('center')
this.resolvePrivacyAuthorization = resolve
},
//
goToPrivacy() {
wx.openPrivacyContract({
success: () => {
console.log('打开成功');
}, //
fail: () => {
uni.showToast({
title: '打开失败,稍后重试',
icon: 'none'
})
} //
})
},
//
confusePrivacy() {
this.$refs.popup.close()
// this.resolvePrivacyAuthorization({
// event: 'disagree'
// })
},
//
handleAgreePrivacyAuthorization() {
// id
// this.resolvePrivacyAuthorization({
// buttonId: 'agree-btn',
// event: 'agree'
// })
this.$refs.popup.close()
}
}
}
</script>
<style lang="scss" scoped>
.privacyPopup {
width: 90%;
margin: 0rpx auto;
background: white;
border-radius: 20rpx;
box-sizing: border-box;
padding: 40rpx 30rpx;
.icon{
display: flex;
justify-content: center;
align-items: center;
padding-bottom: 30rpx;
image{
width: 90rpx;
height: 90rpx;
}
}
.title {
text-align: center;
font-size: 36rpx;
}
.content_pri {
padding: 30rpx 0rpx;
font-size: 28rpx;
}
.config {
font-size: 28rpx;
text-align: center;
line-height: 40rpx;
margin-bottom: 30rpx;
text {
color: $uni-color;
}
.content{
display: flex;
}
}
.pri_btn {
display: flex;
.confuse_btn{
background-color: #F2F2F2;
color: #BDBDBD;
}
button {
margin: 10rpx;
flex: 1;
background: $uni-color;
outline: none;
color: white;
font-size: 30rpx;
}
}
}
</style>

+ 48
- 0
components/config/configPopup.vue View File

@ -0,0 +1,48 @@
<template>
<view class="configPopup">
<uv-popup ref="popup" :round="30" :customStyle="{height: '50vh'}">
<view class="content">
<uv-parse :content="content"></uv-parse>
</view>
</uv-popup>
</view>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'configPoup',
data() {
return {
content : ''
}
},
onShow(){
},
methods: {
//
open(key){
this.content = this.configList[key]
this.$refs.popup.open('bottom');
}
},
computed : {
...mapState(['configList'])
}
}
</script>
<style lang="scss" scoped>
.configPopup {
.content{
padding: 30rpx 20rpx;
overflow: scroll;
height: 100%;
box-sizing: border-box;
}
}
</style>

+ 122
- 0
components/config/customerServicePopup.vue View File

@ -0,0 +1,122 @@
<template>
<!-- 联系客服弹框 -->
<uv-popup ref="popup"
:safeAreaInsetBottom="false"
:round="30">
<view class="warp">
<view class="rect" @tap.stop>
<view class="title">联系{{ bTitle || title }}</view>
<view class="center">确定拨打{{ bTitle || title }}电话?</view>
<view class="bottom">
<view class="btn1"
@click="close">
取消
</view>
<view class="btn2"
@click="confirm">
确定
</view>
</view>
</view>
</view>
</uv-popup>
</template>
<script>
export default {
data() {
return {
phone:'',
title : '客服',
bPhone : '',
bTitle : '',
}
},
mounted() {
this.getCustomPhone()
},
methods: {
getCustomPhone(){
this.$api('customUser', {}, res => {
this.phone = res.result.phone
})
},
open(phone, title) {
this.bPhone = phone || this.phone
this.bTitle = title || this.title
this.$refs.popup.open()
},
close() {
this.$refs.popup.close()
},
//
confirm() {
this.$refs.popup.close()
uni.makePhoneCall({
phoneNumber: this.bPhone || this.phone,
success() {
console.log('安卓拨打成功');
},
fail() {
console.log('安卓拨打失败');
}
})
},
}
}
</script>
<style scoped lang="scss">
.warp {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
.rect {
width: 600rpx;
height: 300rpx;
background-color: #fff;
border-radius: 20rpx;
overflow: hidden;
.title {
padding: 10rpx 0 0 15rpx;
background-color: $uni-color;
color: #FFF;
text-align: left;
width: 100%;
height: 18%;
font-size: 36rpx;
}
.center {
height: 40%;
display: flex;
justify-content: center;
align-items: center;
font-size: 36rpx;
}
.bottom {
display: flex;
justify-content: center;
gap: 50rpx;
view{
height: 60rpx;
line-height: 60rpx;
padding: 0 50rpx;
border-radius: 30rpx;
}
.btn1{
background-color: #fff;
}
.btn2{
background-color: $uni-color;
color: #fff;
}
}
}
</style>

+ 112
- 0
components/user/productList.vue View File

@ -0,0 +1,112 @@
<template>
<view class="list">
<view class="item"
v-for="(item, index) in 10"
@click="$utils.navigateTo('/pages_order/product/productDetail?id=123')"
:key="index">
<image
class="image"
src="https://img95.699pic.com/photo/50058/1378.jpg_wh860.jpg" mode=""></image>
<view class="info">
<view class="title">
桌布租赁
</view>
<view class="price">
<text>58</text>/
</view>
<view class="favorable">
<view class="t">
限时优惠
</view>
<view class="p">
48
</view>
</view>
<view class="num">
已售卖5000+
</view>
</view>
<view class="btn">
<uv-icon name="shopping-cart"
color="#fff"></uv-icon>
</view>
</view>
</view>
</template>
<script>
export default {
name:"productList",
data() {
return {
};
},
methods : {
},
}
</script>
<style scoped lang="scss">
.list{
display: flex;
flex-wrap: wrap;
.item{
position: relative;
width: 300rpx;
padding: 20rpx;
background-color: #fff;
border-radius: 20rpx;
margin-top: 20rpx;
&:nth-child(odd){
margin-right: 20rpx;
}
.image{
width: 300rpx;
height: 250rpx;
border-radius: 20rpx;
}
.info{
font-size: 26rpx;
.title{
font-size: 30rpx;
}
.price{
color: #D03F25;
margin-top: 6rpx;
text{
font-size: 34rpx;
font-weight: 900;
}
}
.favorable{
display: flex;
background-image: url(/static/image/product/favorable.png);
background-size: 100% 100%;
width: fit-content;
padding: 5rpx 10rpx;
font-size: 18rpx;
margin-top: 6rpx;
.p{
color: #fff;
margin-left: 10rpx;
}
}
.num{
margin-top: 6rpx;
font-size: 22rpx;
color: #888;
}
}
.btn{
position: absolute;
right: 20rpx;
bottom: 20rpx;
padding: 10rpx;
border-radius: 50%;
background-color: $uni-color;
}
}
}
</style>

+ 130
- 0
components/userShop/userShopCommission.vue View File

@ -0,0 +1,130 @@
<template>
<view class="commission">
<image src="/static/image/center/10.png" mode=""></image>
<view class="price">
<view class="title">
总佣金
</view>
<view class="num">
7890.34
</view>
</view>
<view class="font-menu"
v-if="purse">
<view @click="toRunningWater(index)"
v-for="(item, index) in list"
:key="index">{{ item.name }}</view>
</view>
<view class="btn"
v-if="!purse"
@click="toPurse">
提现
</view>
</view>
</template>
<script>
export default {
name: "userShopCommission",
props : {
purse : {
default : false,
},
},
data() {
return {
list : [
{
name : '余额记录',
},
{
name : '提现记录',
},
{
name : '佣金记录',
},
],
};
},
methods : {
//
toPurse(){
uni.navigateTo({
url:'/pages_order/mine/purse'
})
},
//
toRunningWater(index){
uni.navigateTo({
url:'/pages_order/mine/runningWater?status=' + index
})
},
}
}
</script>
<style scoped lang="scss">
.commission {
width: 700rpx;
height: 300rpx;
position: relative;
margin: 20rpx auto;
color: #fff;
image {
width: 700rpx;
height: 300rpx;
position: absolute;
border-radius: 20rpx;
}
.price {
position: absolute;
left: 50rpx;
top: 80rpx;
font-weight: 900;
.title {
font-size: 32rpx;
}
.num {
font-size: 44rpx;
margin-top: 20rpx;
}
}
.font-menu {
font-size: 24rpx;
font-family: PingFang SC, PingFang SC-Regular;
font-weight: 500;
text-align: center;
color: #ffffff;
line-height: 24rpx;
width: 710rpx;
position: absolute;
left: 0;
bottom: 25rpx;
display: flex;
view{
width: 160rpx;
}
}
.btn {
position: absolute;
right: 50rpx;
bottom: 50rpx;
background-color: #FDC440;
width: 160rpx;
height: 60rpx;
display: flex;
justify-content: center;
align-items: center;
border-radius: 30rpx;
}
}
</style>

+ 56
- 0
config.js View File

@ -0,0 +1,56 @@
import Vue from 'vue'
import api from '@/api/api.js'
import utils from './utils/utils.js'
import uvUI from '@/uni_modules/uv-ui-tools'
Vue.use(uvUI);
// 当前环境
const type = 'dev'
// 环境配置
const config = {
dev : {
baseUrl : 'http://www.gcosc.fun:82',
},
prod : {
baseUrl : 'http://xxx.xxx.xxx/xxx',
}
}
// 默认配置
const defaultConfig = {
mapKey : 'XMBBZ-BCPCV-SXPPQ-5Y7MY-PHZXK-YFFVU',
aliOss: {
url: 'https://image.hhlm1688.com/',
config: {
//桶的地址
region: 'oss-cn-guangzhou',
//id
accessKeyId: 'LTAI5tQSs47izVy8DLVdwUU9',
//密钥
accessKeySecret: 'qHI7C3PaXYZySr84HTToviC71AYlFq',
//桶的名字
bucket: 'hanhaiimage',
endpoint: 'oss-cn-shenzhen.aliyuncs.com',
}
},
}
uni.$uv.setConfig({
// 修改$uv.config对象的属性
config: {
// 修改默认单位为rpx,相当于执行 uni.$uv.config.unit = 'rpx'
unit: 'rpx'
},
})
Vue.prototype.$config = utils.deepMergeObject(defaultConfig, config[type])
Vue.prototype.$api = api
export default Vue.prototype.$config

+ 20
- 0
index.html View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<script>
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
CSS.supports('top: constant(a)'))
document.write(
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/main.js"></script>
</body>
</html>

+ 45
- 0
main.js View File

@ -0,0 +1,45 @@
import App from './App'
// #ifndef VUE3
import Vue from 'vue'
import './uni.promisify.adaptor'
Vue.config.productionTip = false
App.mpType = 'app'
import store from '@/store/store'
import './config'
import './utils/index.js'
import mixinConfigList from '@/mixins/configList.js'
Vue.mixin(mixinConfigList)
//组件注册
import configPopup from '@/components/config/configPopup.vue'
import navbar from '@/components/base/navbar.vue'
Vue.component('configPopup',configPopup)
Vue.component('navbar',navbar)
const app = new Vue({
...App,
store,
})
app.$mount()
// #endif
// #ifdef VUE3
import {
createSSRApp
} from 'vue'
export function createApp() {
const app = createSSRApp(App)
return {
app
}
}
// #endif

+ 105
- 0
manifest.json View File

@ -0,0 +1,105 @@
{
"name" : "unapp模板",
"appid" : "__UNI__197A38F",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
/* */
"modules" : {},
/* */
"distribute" : {
/* android */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
/* ios */
"ios" : {},
/* SDK */
"sdkConfigs" : {
"maps" : {}
}
}
},
/* */
"quickapp" : {},
/* */
"mp-weixin" : {
"appid" : "wxe7ae8cbe1673834c",
"setting" : {
"urlCheck" : false
},
"usingComponents" : true,
"permission" : {
"scope.userLocation" : {
"desc" : "你的位置信息将用于小程序位置接口的效果展示"
},
"scope.userFuzzyLocation" : {
"desc" : "你的位置信息将用于小程序位置接口的效果展示"
}
},
"requiredPrivateInfos" : [ "chooseLocation", "getLocation" ]
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "2",
"h5" : {
"sdkConfigs" : {
"maps" : {
"qqmap" : {
"key" : "XMBBZ-BCPCV-SXPPQ-5Y7MY-PHZXK-YFFVU"
}
}
},
"devServer" : {
"https" : false,
"proxy" : {
"/ws/geocoder/v1/" : {
"target" : "https://apis.map.qq.com",
"changeOrigin" : true
},
"/ws/location/v1/" : {
"target" : "https://apis.map.qq.com",
"changeOrigin" : true
}
}
}
}
}

+ 47
- 0
mixins/configList.js View File

@ -0,0 +1,47 @@
import { mapState } from 'vuex'
export default {
data() {
return {
// 默认的全局分享内容
Gshare: {
// title: '三只青蛙',
path: '/pages/index/index', // 全局分享的路径,比如 首页
// imageUrl: '/static/image/login/logo.png', // 全局分享的图片(可本地可网络)
}
}
},
computed: {
...mapState(['configList', 'userInfo', 'riceInfo']),
},
// 定义全局分享
// 1.发送给朋友
onShareAppMessage(res) {
let o = {
title : this.configList.logo_name,
...this.Gshare,
}
if(this.userInfo.id){
if(this.Gshare.path.includes('?')){
o.path += '&shareId=' + this.userInfo.id
}else{
o.path += '?shareId=' + this.userInfo.id
}
}
return o
},
//2.分享到朋友圈
onShareTimeline(res) {
let o = {
...this.Gshare,
title : this.configList.logo_name,
}
if(this.userInfo.id){
o.path = this.Gshare.path + '?shareId=' + this.userInfo.id
}
return o
},
methods: {
}
}

+ 78
- 0
mixins/list.js View File

@ -0,0 +1,78 @@
/**
* 处理查询参数
* @param {Object} self - 组件实例
* @param {Object} queryParams - 额外的查询参数
* @returns {Object} 合并后的查询参数
*/
function query(self, queryParams){
// 深度合并对象
return self.$utils.deepMergeObject(
self.$utils.deepMergeObject(self.queryParams,
(self.beforeGetData && self.beforeGetData()) || {}),
queryParams)
}
/**
* 列表数据加载混入
* 提供列表数据的加载分页下拉刷新上拉加载更多等功能
*/
export default {
data() {
return {
queryParams: {
pageNo: 1,
pageSize: 10,
},
total : 0,
list : [],
}
},
// 下拉刷新
onPullDownRefresh() {
this.getData()
},
// 上拉加载更多
onReachBottom() {
this.loadMoreData()
},
// 页面显示时加载数据
onShow() {
this.getData()
},
methods: {
/**
* 获取列表数据
* @param {Object} queryParams - 查询参数
* @returns {Promise} 返回Promise对象
*/
getData(queryParams){
return new Promise((success, error) => {
if(!this.mixinsListApi){
return console.error('mixinsListApi 缺失');
}
this.$api(this.mixinsListApi,
query(this, queryParams), res => {
uni.stopPullDownRefresh()
if(res.code == 200){
success(res.result)
// 更新列表数据
this[this.mixinsListKey || 'list'] = res.result.records || res.result
// 更新总数
this.total = res.result.total || res.result.length
// 调用数据加载完成的回调
this.getDataThen && this.getDataThen(res.result.records, res.result.total, res.result)
}
})
})
},
/**
* 加载更多数据
*/
loadMoreData(){
if(this.queryParams.pageSize < this.total){
this.queryParams.pageSize += 10
this.getData()
}
},
}
}

+ 70
- 0
mixins/order.js View File

@ -0,0 +1,70 @@
export default {
data() {
return {
}
},
computed: {
},
methods: {
// 立即支付
toPayOrder(item){
let api = ''
// if([0, 1].includes(item.shopState)){
// api = 'createOrderTwo'
// }else{
api = 'createSumOrderAgain'
// }
this.$api(api, {
orderId : item.id,
addressId : item.addressId
}, res => {
if(res.code == 200){
uni.requestPaymentWxPay(res)
.then(res => {
uni.showToast({
title: '支付成功',
icon: 'none'
})
this.getData()
}).catch(n => {
this.getData()
})
}
})
},
// 确认收货
confirmOrder(item){
uni.showModal({
title: '您收到货了吗?',
success : e => {
if(e.confirm){
this.$api('confirmOrder', {
orderId : item.id,
}, res => {
this.getData()
})
}
}
})
},
// 取消订单
cancelOrder(item){
uni.showModal({
title: '确认取消订单吗?',
success : e => {
if(e.confirm){
this.$api('cancelOrder', {
orderId : item.id,
}, res => {
this.getData()
})
}
}
})
},
}
}

+ 20
- 0
package.json View File

@ -0,0 +1,20 @@
{
"name": "uniapp-project-templates",
"version": "1.0.0",
"description": "{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**}",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://gitee.com/huliyong/uniapp-project-templates.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"ali-oss": "^6.21.0",
"dayjs": "^1.11.12"
}
}

+ 84
- 0
pages.json View File

@ -0,0 +1,84 @@
{
"pages": [{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/index/order",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/index/category",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/index/center",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/index/cart",
"style": {
"navigationBarTitleText": ""
}
}
],
"preloadRule": {
"pages/index/index": {
"network": "all",
"packages": ["pages_order"]
}
},
"subPackages": [{
"root": "pages_order",
"pages": [{
"path": "order/orderDetail"
},
{
"path": "mine/purse"
},
{
"path": "mine/runningWater"
},
{
"path": "mine/address"
},
{
"path": "product/productDetail"
},
{
"path": "order/refundsOrExchange"
},
{
"path": "auth/wxLogin"
},
{
"path": "auth/wxUserInfo"
},
{
"path": "auth/loginAndRegisterAndForgetPassword"
},
{
"path": "mine/help"
},
{
"path": "mine/promotion"
}
]
}],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "酒店桌布",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8",
"navigationStyle": "custom"
},
"uniIdRouter": {}
}

+ 260
- 0
pages/index/cart.vue View File

@ -0,0 +1,260 @@
<template>
<view class="page">
<navbar/>
<view class="user">
<uv-checkbox-group
shape="circle"
v-model="checkboxValue">
<uv-swipe-action>
<view
v-for="(item, index) in list"
:key="index">
<view style="margin-top: 20rpx;"></view>
<uv-swipe-action-item
:options="options">
<view class="item">
<view class="checkbox">
<uv-checkbox
:name="item.id"
activeColor="#FA5A0A"
size="40rpx"
icon-size="35rpx"
></uv-checkbox>
</view>
<image
class="image"
src="https://img95.699pic.com/photo/50058/1378.jpg_wh860.jpg"
mode=""></image>
<view class="info">
<view class="title">
<view class="">
{{ item.title }}
</view>
<view class="">
<uv-number-box v-model="item.num"
@change="valChange"></uv-number-box>
</view>
</view>
<view class="unit">
规格{{ item.unit }}
<uv-icon name="arrow-down"></uv-icon>
</view>
<view class="price">
<text>{{ item.price }}</text>
</view>
</view>
</view>
</uv-swipe-action-item>
</view>
</uv-swipe-action>
</uv-checkbox-group>
<view class="action">
<view class="icon">
<image src="/static/image/cart/1.png" mode=""></image>
<view class="num">
{{ checkboxValue.length }}
</view>
</view>
<view class="price">
<view class="count">
合计
<view class="">
<text>{{ totalPrice }}</text>
</view>
</view>
<view class="text">
{{ checkboxValue.length }}已享受更低优惠
</view>
</view>
<view class="btn">
去结算
</view>
</view>
</view>
<tabber select="cart" />
</view>
</template>
<script>
import tabber from '@/components/base/tabbar.vue'
export default {
components: {
tabber,
},
data() {
return {
value : 0,
checkboxValue : [],
options: [
{
text: '删除',
style: {
backgroundColor: '#FA5A0A'
}
},
],
list : [
{
id : 1,
title : '桌布租赁',
num : 1,
price : 299,
unit : '120*40*75【桌子尺寸】',
},
{
id : 2,
title : '桌布租赁',
num : 1,
price : 299,
unit : '120*40*75【桌子尺寸】',
},
],
}
},
computed: {
totalPrice(){
if (!this.checkboxValue.length) {
return 0
}
let price = 0
this.list.forEach(n => {
if(this.checkboxValue.includes(n.id)){
price += n.price * n.num
}
})
return price
},
},
methods: {
valChange(){
},
}
}
</script>
<style scoped lang="scss">
.page {
padding-bottom: 200rpx;
/deep/ .uv-swipe-action{
width: 100%;
}
}
.user {
.item{
background-color: #fff;
display: flex;
padding: 30rpx;
.checkbox{
display: flex;
justify-content: center;
align-items: center;
}
.image{
width: 200rpx;
height: 200rpx;
border-radius: 20rpx;
}
.info{
flex: 1;
.title{
display: flex;
padding: 10rpx 20rpx;
justify-content: space-between;
}
.unit{
font-size: 24rpx;
padding: 10rpx 20rpx;
color: #717171;
display: flex;
align-items: center;
}
.price{
color: $uni-color;
font-size: 28rpx;
padding: 10rpx 20rpx;
text{
font-size: 36rpx;
font-weight: 900;
}
}
}
}
.action{
width: 700rpx;
position: fixed;
bottom: 220rpx;
left: 25rpx;
background-color: #fff;
height: 100rpx;
border-radius: 50rpx;
box-shadow: 0 0 6rpx 6rpx #00000010;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
z-index: 999;
.icon{
position: relative;
width: 80rpx;
height: 80rpx;
margin: 0 20rpx;
image{
width: 80rpx;
height: 80rpx;
}
.num{
position: absolute;
right: 10rpx;
top: 0rpx;
background-color: $uni-color;
color: #fff;
font-size: 18rpx;
border-radius: 50%;
height: 30rpx;
width: 30rpx;
display: flex;
justify-content: center;
align-items: center;
}
}
.price{
.count{
display: flex;
font-size: 26rpx;
align-items: center;
view{
color: $uni-color;
margin-left: 10rpx;
text{
font-size: 32rpx;
font-weight: 900;
}
}
}
.text{
font-size: 20rpx;
color: #717171;
}
}
.btn{
margin-left: auto;
background-color: $uni-color;
height: 100%;
padding: 0 50rpx;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
}
}
}
</style>

+ 191
- 0
pages/index/category.vue View File

@ -0,0 +1,191 @@
<template>
<view class="page">
<navbar/>
<view class="category">
<uv-vtabs
:chain="chain"
:list="category"
height="1000px"
:barItemBadgeStyle="{right:'20px',top:'12px'}"
@change="change">
<uv-vtabs-item>
<view class="category-title">
租赁
</view>
<view class="list">
<view class="item" v-for="(item,index) in list" :key="index"
@click="$utils.navigateTo('/pages_order/product/productDetail?id=123')">
<view class="item-image">
<image
:src="item.image"
mode="aspectFill"></image>
</view>
<view class="item-unit">
<text class="text">{{item.unit}}</text>
</view>
</view>
</view>
</uv-vtabs-item>
</uv-vtabs>
</view>
<tabber select="category" />
</view>
</template>
<script>
import tabber from '@/components/base/tabbar.vue'
export default {
components: {
tabber,
},
data() {
return {
category: [
{
name : '桌布'
},
{
name : '桌布'
},
{
name : '桌布'
},
],
list : [
{
unit : '100*50*60',
image : 'https://img95.699pic.com/photo/50058/1378.jpg_wh860.jpg',
},
{
unit : '100*50*60',
image : 'https://img95.699pic.com/photo/50058/1378.jpg_wh860.jpg',
},
{
unit : '100*50*60',
image : 'https://img95.699pic.com/photo/50058/1378.jpg_wh860.jpg',
},
{
unit : '100*50*60',
image : 'https://img95.699pic.com/photo/50058/1378.jpg_wh860.jpg',
},
{
unit : '100*50*60',
image : 'https://img95.699pic.com/photo/50058/1378.jpg_wh860.jpg',
},
{
unit : '100*50*60',
image : 'https://img95.699pic.com/photo/50058/1378.jpg_wh860.jpg',
},
{
unit : '100*50*60',
image : 'https://img95.699pic.com/photo/50058/1378.jpg_wh860.jpg',
},
{
unit : '100*50*60',
image : 'https://img95.699pic.com/photo/50058/1378.jpg_wh860.jpg',
},
{
unit : '100*50*60',
image : 'https://img95.699pic.com/photo/50058/1378.jpg_wh860.jpg',
},
],
chain: false,
value: 0
}
},
computed: {
list2() {
const _list = this.list[this.value]?.childrens;
return _list ? _list : [];
}
},
onReady() {
},
methods: {
change(index) {
console.log('选项改变:', index)
this.value = index;
}
}
}
</script>
<style scoped lang="scss">
.page{
/deep/ .uv-vtabs{
height: calc(100vh - 360rpx) !important;
}
/deep/ .uv-vtabs__bar{
height: calc(100vh - 360rpx) !important;
}
/deep/ .uv-vtabs__content{
height: calc(100vh - 360rpx) !important;
}
}
.category {
font-size: 30rpx;
color: #333;
.category-title{
position: relative;
display: flex;
justify-content: center;
align-items: center;
height: 120rpx;
&::before,
&::after {
position: absolute;
top: 50%;
content: '';
width: 10%;
border-top: 2rpx solid black;
}
&::before {
left: 25%;
}
&::after {
right: 25%;
}
}
.list{
display: flex;
flex-wrap: wrap;
margin: 0 auto;
width: 490rpx;
.item {
padding: 10rpx 20rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-bottom: 20rpx;
.item-image {
width: 120rpx;
height: 120rpx;
image{
height: 100%;
width: 100%;
border-radius: 50%;
}
}
.item-unit {
font-size: 24rpx;
margin-top: 15rpx;
color: #555;
}
}
.gap {
padding: 0 30rpx;
}
}
}
</style>

+ 353
- 0
pages/index/center.vue View File

@ -0,0 +1,353 @@
<template>
<view class="page">
<navbar title="个人中心" />
<view class="head">
<view class="headImage">
<image src="" mode=""></image>
</view>
<view class="info">
<view class="name">
倾心.
</view>
<!-- <view class="vip">
VIP1
</view> -->
<view class="tips">
今天是您来的的第32天
</view>
</view>
<!-- <view class="headBtn" @click="headBtn">
角色切换
</view> -->
<view class="setting">
<uv-icon name="setting" size="40rpx"></uv-icon>
</view>
</view>
<!-- 水洗店 -->
<view class="userShop" v-if="userShop">
<userShopCommission />
<view class="userList">
<view class="title">
我的用户
</view>
<view class="list">
<view class="item" v-for="(item, index) in 20" :key="index">
<view class="name">
客户王生
</view>
<view class="num">
剩余水洗布198
</view>
</view>
</view>
</view>
</view>
<!-- 酒店 -->
<view class="user" v-else>
<view class="line">
<view class="item">
<view class="image">
<image src="/static/image/center/1.png" mode=""></image>
</view>
<view class="">
余额3000
</view>
</view>
<view class="item">
<view class="image">
<image src="/static/image/center/4.png" mode=""></image>
</view>
<view class="">
押金30000
</view>
</view>
</view>
<view class="line grid">
<view class="title">
常用功能
</view>
<uv-grid :col="4" :border="false">
<uv-grid-item @click="$utils.navigateTo('/pages_order/mine/address')">
<image class="image" src="/static/image/center/7.png" mode=""></image>
<text class="grid-text">地址管理</text>
</uv-grid-item>
<uv-grid-item @click="$utils.redirectTo('/index/order')">
<image class="image" src="/static/image/center/8.png" mode=""></image>
<text class="grid-text">订单管理</text>
</uv-grid-item>
<uv-grid-item @click="$utils.navigateTo('/pages_order/order/refundsOrExchange?index='+0)">
<image class="image" src="/static/image/center/5.png" mode=""></image>
<text class="grid-text">换货</text>
</uv-grid-item>
<uv-grid-item @click="$utils.navigateTo('/pages_order/order/refundsOrExchange?index='+1)">
<image class="image" src="/static/image/center/7.png" mode=""></image>
<text class="grid-text">退货</text>
</uv-grid-item>
</uv-grid>
</view>
<view class="line grid">
<uv-grid :col="4" :border="false">
<uv-grid-item @click="contactUs">
<image class="image" src="/static/image/center/9.png" mode=""></image>
<text class="grid-text">联系客服</text>
</uv-grid-item>
<uv-grid-item>
<image class="image" src="/static/image/center/6.png" mode=""></image>
<text class="grid-text">我的租赁</text>
</uv-grid-item>
<uv-grid-item @click="$utils.redirectTo('/index/cart')">
<image class="image" src="/static/image/center/7.png" mode=""></image>
<text class="grid-text">租赁车</text>
</uv-grid-item>
<uv-grid-item
@click="$utils.navigateTo('/pages_order/auth/loginAndRegisterAndForgetPassword?index='+2)">
<image class="image" src="/static/image/center/7.png" mode=""></image>
<text class="grid-text">申请成为水洗店</text>
</uv-grid-item>
</uv-grid>
</view>
</view>
<tabber select="center" />
</view>
</template>
<script>
import tabber from '@/components/base/tabbar.vue'
import {
mapGetters
} from 'vuex'
import userShopCommission from '@/components/userShop/userShopCommission.vue'
export default {
components: {
tabber,
userShopCommission,
},
computed: {
...mapGetters(['userShop']),
},
data() {
return {
}
},
methods: {
headBtn() {
let self = this
uni.showModal({
title: '演示切换角色之后的效果',
success(res) {
if (res.confirm) {
self.$store.state.shop = !self.$store.state.shop
}
}
})
},
}
}
</script>
<style scoped lang="scss">
.page {
.warp {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
.rect {
width: 600rpx;
height: 300rpx;
background-color: #fff;
border-radius: 20rpx;
overflow: hidden;
.title {
padding: 10rpx 0 0 15rpx;
background-color: #fd5100;
color: #FFF;
text-align: left;
width: 100%;
height: 18%;
font-size: 36rpx;
}
.center {
height: 40%;
display: flex;
justify-content: center;
align-items: center;
font-size: 36rpx;
}
.bottom {
display: flex;
justify-content: center;
gap: 50rpx;
}
}
}
image {
width: 100%;
height: 100%;
}
.head {
display: flex;
background-color: #fff;
padding: 40rpx 20rpx;
align-items: center;
position: relative;
.headImage {
width: 120rpx;
height: 120rpx;
background-image: url(/static/image/center/3.png);
background-size: 100% 100%;
overflow: hidden;
border-radius: 50%;
margin-right: 40rpx;
}
.info {
font-size: 28rpx;
.vip {
background-color: #FCCC92;
color: #FA6239;
width: 100rpx;
display: flex;
justify-content: center;
align-items: center;
height: 40rpx;
border-radius: 20rpx;
margin-top: 20rpx;
}
.name {
font-size: 32rpx;
}
.tips {
font-size: 26rpx;
color: #ABABAB;
}
}
.headBtn {
margin-left: auto;
padding: 15rpx 20rpx;
background-color: $uni-color;
color: #fff;
border-radius: 20rpx;
margin-top: 50rpx;
}
.setting {
position: absolute;
right: 50rpx;
top: 50rpx;
}
}
.userShop {
.userList {
.title {
font-size: 32rpx;
font-weight: 900;
padding: 20rpx;
}
.list {
display: flex;
flex-wrap: wrap;
.item {
width: 270rpx;
margin: 20rpx;
display: flex;
flex-direction: column;
padding: 40rpx 30rpx;
background-color: #fff;
border-radius: 30rpx;
line-height: 60rpx;
.name {}
.num {
color: $uni-color;
font-weight: 600;
font-size: 28rpx;
}
}
}
}
}
.user {
.line {
display: flex;
background-color: #fff;
margin-top: 20rpx;
padding: 20rpx 0;
.item {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
padding: 20rpx 0;
&:nth-child(1) {
border-right: 1px solid #00000013;
}
.image {
width: 100rpx;
height: 70rpx;
margin-right: 20rpx;
}
}
}
.grid {
flex-direction: column;
font-size: 26rpx;
padding: 20rpx;
.title {
margin-bottom: 30rpx;
font-size: 28rpx;
font-weight: 600;
}
.image {
width: 70rpx;
height: 70rpx;
margin-bottom: 10rpx;
}
text {
text-align: center;
width: 120rpx;
}
}
}
</style>

+ 412
- 0
pages/index/index.vue View File

@ -0,0 +1,412 @@
<template>
<view class="page">
<navbar/>
<view class="search">
<view @click="showSelectArea" class="left-area">
<image src="@/static/image/home/address-icon.png"></image>
<view class="area">{{ area }}</view>
<image src="@/static/image/home/arrow-icon.png" mode="aspectFit"></image>
<view class="parting-line">|</view>
</view>
<view class="center-area">
<image
style="margin-right: 20rpx;"
src="@/static/image/home/search-icon.png"></image>
<input v-model="queryParams.title"
placeholder="桌布租赁" />
</view>
<!-- <view class="right-area">
<view @click="searchAddress" class="search-button">
搜索
</view>
</view> -->
</view>
<view class="swipe">
<uv-swiper
:list="bannerList"
indicator
height="320rpx"
keyName="url"></uv-swiper>
</view>
<!-- 水洗店 -->
<view class="userShop"
v-if="userShop">
<view class="list">
<view class="item"
v-for="(item, index) in 4"
:key="index">
<view class="">
<view class="">
我的客户
</view>
<view class="num">
{{ 30 }}
</view>
</view>
<view class="">
<image :src="`/static/image/home/${index}.png`" mode=""></image>
</view>
</view>
</view>
</view>
<!-- 酒店 -->
<view class="user"
v-else>
<uv-notice-bar
fontSize="28rpx"
:text="text"></uv-notice-bar>
<view class="shop">
<image
class="image"
src="https://img95.699pic.com/photo/50058/1378.jpg_wh860.jpg"
mode=""></image>
<view class="shopInfo">
<view class="title">
HOUS水洗店
</view>
<view class="tags">
<view class="tag">
桌布水洗
</view>
<view class="tag">
桌布租赁
</view>
</view>
<view class="time">
9:00-18:00
</view>
<view class="address">
长沙市天心区桂花坪街道231号
</view>
</view>
<view class="btns">
<view class="btn"
@click="$utils.navigateTo('/pages_order/auth/wxUserInfo')">
我要水洗
</view>
</view>
<view class="tips">
关联门店主信门店
</view>
</view>
<view class="productList">
<productList/>
</view>
</view>
<!-- <selectArea ref="selectArea" @close="closeAreaPro" @select="selectArea"></selectArea> -->
<PrivacyAgreementPoup/>
<tabber select="home"/>
</view>
</template>
<script>
import PrivacyAgreementPoup from '@/components/config/PrivacyAgreementPoup.vue'
import Position from '@/utils/position.js'
import tabber from '@/components/base/tabbar.vue'
import productList from '@/components/user/productList.vue'
import { mapGetters } from 'vuex'
// import selectArea from '../../components/selectArea.vue';
export default {
components : {
tabber,
productList,
PrivacyAgreementPoup,
},
data() {
return {
area: '长沙',
text : '长沙市刘师傅在服务过程中客户投诉“服务过程中有不文明的行为”.....',
queryParams: {
pageNo: 1,
pageSize: 10,
title: ''
},
bannerList: [
{
url: 'https://cdn.uviewui.com/uview/swiper/swiper3.png',
},
{
url: 'https://cdn.uviewui.com/uview/swiper/swiper2.png',
},
{
url: 'https://cdn.uviewui.com/uview/swiper/swiper3.png',
},
],
productList: [],
}
},
computed : {
...mapGetters(['userShop']),
},
methods: {
//
showSelectArea() {
// this.$refs.selectArea.open()
},
//
searchAddress() {
Position.getLocation(res => {
Position.selectAddress(res.longitude, res.latitude, success => {
let address = this.extractProvinceAndCity(success)
this.queryParams.title = address.city
})
})
},
//()
extractProvinceAndCity(res) { //()
if (!res.address && res.name) { //
return {
province: '',
city: res.name
};
}
if (res.address) { //
// 使
const regex = /(?<province>[\u4e00-\u9fa5]+?省)(?<city>[\u4e00-\u9fa5]+?(?:市|自治州|盟|地区))/;
const match = res.address.match(regex);
if (match) { //
return {
province: match.groups.province,
city: match.groups.city
};
}
}
return { //
province: '',
city: ''
}
},
}
}
</script>
<style scoped lang="scss">
.search {
height: 82rpx;
width: 710rpx;
background: #FFFFFF;
margin: 20rpx auto;
border-radius: 41rpx;
box-sizing: border-box;
padding: 0 15rpx;
display: flex;
align-items: center;
// justify-content: space-between;
.left-area,
.center-area {
display: flex;
align-items: center;
}
.left-area {
max-width: 160rpx;
image {
flex-shrink: 0;
width: 26rpx;
height: 26rpx;
}
.area {
font-size: 24rpx;
display: -webkit-box;
-webkit-line-clamp: 2;
/* 限制显示两行 */
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
color: #292929;
}
.parting-line {
flex-shrink: 0;
font-size: 26rpx;
color: #ccc;
margin: 0rpx 5rpx;
}
}
.center-area {
display: flex;
flex-wrap: nowrap;
align-items: center;
width: calc(100% - 290rpx);
margin-left: 30rpx;
image {
width: 26rpx;
height: 26rpx;
}
.van-field {
background-color: transparent;
box-sizing: border-box;
height: 82rpx;
line-height: 82rpx;
width: calc(100% - 30rpx);
padding: 0rpx 10rpx 0rpx 0rpx;
input {
height: 82rpx;
font-size: 60rpx;
}
}
}
.right-area {
.search-button {
background: #60BDA2;
height: 60rpx;
width: 130rpx;
font-size: 26rpx;
border-radius: 35rpx;
color: white;
display: flex;
align-items: center;
justify-content: center;
}
}
}
.swipe{
overflow: hidden;
border-radius: 20rpx;
margin: 20rpx;
}
.page{
& /deep/ .uv-icon__icon{
font-size: 30rpx !important;
}
//
.userShop{
.list{
display: flex;
flex-wrap: wrap;
.item{
display: flex;
justify-content: center;
align-items: center;
width: 330rpx;
margin: 20rpx;
padding: 30rpx 0;
color: #FFFFFF;
line-height: 50rpx;
border-radius: 20rpx;
font-size: 28rpx;
.num{
font-size: 38rpx;
font-weight: 900;
}
image{
width: 110rpx;
height: 110rpx;
margin-left: 20rpx;
}
&:nth-child(1){
background: #F07A77;
}
&:nth-child(2){
background: #F48B4E;
}
&:nth-child(3){
background: #6487E1;
}
&:nth-child(4){
background: #61B7E6;
}
}
}
}
//
.user{
padding: 20rpx;
.shop{
position: relative;
display: flex;
align-items: center;
justify-content: center;
background-color: #fff;
margin-top: 20rpx;
border-radius: 20rpx;
padding: 20rpx;
overflow: hidden;
.image{
width: 180rpx;
height: 180rpx;
margin-right: 20rpx;
border-radius: 20rpx;
}
.shopInfo{
font-size: 22rpx;
.title{
font-size: 30rpx;
}
.tags{
display: flex;
.tag{
padding: 4rpx 6rpx;
border: 1rpx solid #FFAC6E;
color: #FFAC6E;
margin-right: 10rpx;
margin-top: 10rpx;
font-size: 18rpx;
}
}
.time{
margin-top: 10rpx;
display: flex;
}
.address{
margin-top: 10rpx;
display: flex;
}
}
.btns{
margin-left: auto;
display: flex;
align-items: center;
justify-content: center;
.btn{
background-color: $uni-color;
color: #fff;
box-shadow: 0 0 5rpx 5rpx #FFAC6E;
padding: 10rpx 20rpx;
flex-shrink: 0;
border-radius: 35rpx;
}
}
.tips{
position: absolute;
top: 0;
right: 0;
font-size: 24rpx;
color: #FFAC6E;
background-color: #FEF5EE;
padding: 10rpx 20rpx;
border-radius: 10rpx;
}
}
}
}
</style>

+ 263
- 0
pages/index/order.vue View File

@ -0,0 +1,263 @@
<template>
<view class="page">
<navbar
title="订单中心"
/>
<uv-tabs :list="tabs"
:activeStyle="{color : '#FD5100', fontWeight : 600}"
lineColor="#FD5100"
lineHeight="8rpx"
lineWidth="50rpx"
@click="clickTabs"></uv-tabs>
<view v-if="orderList.length > 0" class="list">
<view class="item"
v-for="(item, index) in orderList.records"
@click="toOrderDetail(item.id)"
:key="index">
<view class="top">
<view class="service">
<text>{{item.projectId_dictText}}</text>
<text>{{item.type_dictText}}</text>
</view>
<view class="status">
<text> {{item.state_dictText}}</text>
</view>
</view>
<view class="content">
<view class="left">
<image mode="aspectFill" :src="item.image"></image>
</view>
<view class="right">
<view class="text-hidden-1">
客户姓名{{item.name}}
</view>
<view class="text-hidden-1">
产品规格{{item.unit}}
</view>
<view class="text-hidden-1">
租赁地址{{item.address}}
</view>
<!-- <view class="text-hidden-1">
总计时间{{item.useTime}}分钟
</view> -->
</view>
</view>
<view class="bottom">
<view class="price">
总价格<text class="num">{{item.money}}</text>
</view>
<view class="b1">
查看物流
</view>
<!-- <view @click.stop="toPayOrder(item)" class="b2" v-if="item.state == 0">
立即付款
</view>
<view class="b1" @click.stop="moreOrder(item.projectId,toPlaceorder)" v-if="item.state == 3">
再来一单
</view>
<view class="b2" @click.stop="toEvaluate(item.id,item.projectId,item.technicianId)" v-if="item.state == 3">
立即评价
</view>
<view class="b2" @click.stop="moreOrder(item.projectId,toPlaceorder)" v-if="item.state == 4">
再来一单
</view> -->
</view>
</view>
</view>
<tabber select="order"/>
</view>
</template>
<script>
import tabber from '@/components/base/tabbar.vue'
import { mapGetters } from 'vuex'
export default {
components : {
tabber,
},
computed : {
...mapGetters(['userShop']),
},
data() {
return {
tabs: [{
name: '全部'
},
{
name: '租赁押金'
},
{
name: '水洗租赁'
},
{
name: '破损换货'
},
{
name: '退货退款'
}
],
queryParams: {
pageNo: 1,
pageSize: 10
},
// orderList: [
// {
// money : 99.99,
// address : '广广C32802',
// name : '**',
// phone : '150*****091',
// unit : '120*40*75',
// image : 'https://img95.699pic.com/photo/50058/1378.jpg_wh860.jpg',
// state_dictText : '',
// }
// ], //
orderList : {
records : [],
total : 0,
},
state : -1,
}
},
onShow() {
this.orderPage()
},
//
onReachBottom() {
if(this.queryParams.pageSize < this.orderList.total){
this.queryParams.pageSize += 10
this.orderPage()
}
},
methods: {
orderPage(){
let queryParams = {
...this.queryParams,
}
if(this.state != -1){
queryParams.state = this.state
}
this.$api('orderPage', queryParams, res => {
if(res.code == 200){
this.orderList = res.result
}
})
},
//tab
clickTabs(index) {
if (index == 0) {
this.state = -1;
} else {
this.state = index - 1;
}
this.queryParams.pageSize = 10
this.orderPage()
},
//
toOrderDetail(id) {
uni.navigateTo({
url: '/pages_order/order/orderDetail?id=' + id
})
},
getOrderList(){
},
}
}
</script>
<style scoped lang="scss">
.page{
}
.list {
.item {
width: calc(100% - 40rpx);
background-color: #fff;
margin: 20rpx;
box-sizing: border-box;
border-radius: 16rpx;
padding: 30rpx;
.top {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 30rpx;
.service {}
.status {
font-size: 26rpx;
font-weight: 600;
}
}
.content {
display: flex;
margin: 10rpx 0;
.left {
width: 150rpx;
height: 150rpx;
border-radius: 10rpx;
image {
width: 150rpx;
height: 150rpx;
border-radius: 10rpx;
}
}
.right {
width: calc(100% - 160rpx);
color: #777;
font-size: 24rpx;
padding-left: 20rpx;
line-height: 40rpx;
background-color: #F8F8F8;
}
}
.bottom {
display: flex;
justify-content: space-between;
font-size: 25rpx;
.price {
font-weight: 900;
text {
color: #ff780099;
font-size: 30rpx;
}
}
.b1 {
border: 1px solid #777;
color: #777;
box-sizing: border-box;
}
.b2 {
background: linear-gradient(178deg, #4FD3BC, #60C285);
color: #fff;
}
view {
margin: 12rpx;
border-radius: 28rpx;
padding: 8rpx 28rpx;
margin-bottom: 0;
}
}
}
}
</style>

+ 514
- 0
pages_order/auth/loginAndRegisterAndForgetPassword.vue View File

@ -0,0 +1,514 @@
<template>
<view class="refundsOrExchange">
<navbar :title="titleList[titleIndex]" leftClick
@leftClick="$utils.redirectTo('/pages/index/index')" />
<view class="frame">
<!-- 注册 -->
<view class='forgetPassword' v-if='titleIndex == 0'>
<!-- 标题 -->
<view class="title">{{titleList[titleIndex]}}</view>
<!-- 表单 -->
<view class="form1">
<view class="userName">
<uv-input v-model="form1.username" placeholder="请输入手机号" border="surround" shape='circle'
clearable :customStyle="{ backgroundColor: '#f6f6f6'}"></uv-input>
</view>
<view class="code">
<view class="left">
<uv-input v-model="form1.captcha" placeholder="请输入验证码" border="surround" shape='circle'
clearable :customStyle="{ backgroundColor: '#f6f6f6'}"></uv-input>
</view>
<view class="right">
<view>
<uv-toast ref="toast"></uv-toast>
<uv-code :seconds="seconds" @end="end" @start="start" ref="code"
@change="codeChange"></uv-code>
<uv-button @tap="getCode" iconSize='10rpx' color='#1f1c39'
shape='circle'>{{tips}}</uv-button>
</view>
</view>
</view>
<view class="password1">
<uv-input v-model="form1.password" placeholder="设置您的新密码(6到50个字符)" password clearable
border="surround" shape='circle' :customStyle="{ backgroundColor: '#f6f6f6'}"></uv-input>
</view>
<view class="password2">
<uv-input v-model="form1.newPassword" placeholder="重新确认密码" password clearable border="surround"
shape='circle' :customStyle="{ backgroundColor: '#f6f6f6'}"></uv-input>
</view>
</view>
<view>
<uv-checkbox-group v-model="checkboxValue" shape="circle">
<view class="content">
<view style="display: flex;">
<uv-checkbox size="30rpx" :name="1"></uv-checkbox>
请你阅读并同意我们的<span style="color: #fd5100"
@click="$refs.popup.open('getPrivacyPolicy')">隐私条款</span><span
style="color: #fd5100"
@click="$refs.popup.open('getUserAgreement')">服务协议</span>
</view>
</view>
</uv-checkbox-group>
</view>
</view>
<!-- 登录 -->
<view class="loginRegister" v-if='titleIndex==1'>
<!-- 标题 -->
<view class="title">{{titleList[titleIndex]}}</view>
<!-- 头像 -->
<view class="userIamge">
<view>
<img src="/static/image/center/11.svg" alt="" style="width: 100%;height: 100%;">
</view>
</view>
<!-- 用户名&密码&隐私条款 -->
<view class="form">
<view>
<uv-input v-model="form.username" placeholder="请输入账号" border="surround" shape='circle' clearable
:customStyle="{ backgroundColor: '#f6f6f6'}"></uv-input>
</view>
<view>
<uv-input v-model="form.password" password placeholder="请输入密码" border="surround" shape='circle'
clearable :customStyle="{ backgroundColor: '#f6f6f6'}"></uv-input>
</view>
<view>
<uv-checkbox-group v-model="checkboxValue" shape="circle">
<view class="content">
<view style="display: flex;">
<uv-checkbox size="30rpx" :name="1"></uv-checkbox>
请你阅读并同意我们的<span style="color: #fd5100"
@click="$refs.popup.open('yszc')">隐私条款</span><span
style="color: #fd5100"
@click="$refs.popup.open('fwxy')">服务协议</span>
</view>
</view>
</uv-checkbox-group>
</view>
</view>
</view>
<!-- 忘记密码 -->
<view class='forgetPassword' v-if='titleIndex == 2'>
<!-- 标题 -->
<view class="title">{{titleList[titleIndex]}}</view>
<!-- 表单 -->
<view class="form1">
<view class="userName">
<uv-input v-model="form1.username" placeholder="请输入手机号" border="surround" shape='circle'
clearable :customStyle="{ backgroundColor: '#f6f6f6'}"></uv-input>
</view>
<view class="code">
<view class="left">
<uv-input v-model="form1.captcha" placeholder="请输入验证码" border="surround" shape='circle'
clearable :customStyle="{ backgroundColor: '#f6f6f6'}"></uv-input>
</view>
<view class="right">
<view>
<uv-toast ref="toast"></uv-toast>
<uv-code :seconds="seconds" @end="end" @start="start" ref="code"
@change="codeChange"></uv-code>
<uv-button @tap="getCode" iconSize='10rpx' color='#1f1c39'
shape='circle'>{{tips}}</uv-button>
</view>
</view>
</view>
<view class="password1">
<uv-input v-model="form1.password" placeholder="设置您的新密码(6到50个字符)" password clearable
border="surround" shape='circle' :customStyle="{ backgroundColor: '#f6f6f6'}"></uv-input>
</view>
<view class="password2">
<uv-input v-model="form1.newPassword" placeholder="重新确认密码" password clearable border="surround"
shape='circle' :customStyle="{ backgroundColor: '#f6f6f6'}"></uv-input>
</view>
</view>
</view>
<!-- 按钮 -->
<view class="btn" @click="submit">
<button class='a'>{{titleList[titleIndex]}}</button>
</view>
<!-- tab -->
<view class="bottomTab">
<span :class="titleIndex==0 ? 'tabbarItemActive' : 'tabbarItemNoActive'"
@click='changePage(0)'>注册账号</span>
<span style="color: #9c9fa4">|</span>
<span :class="titleIndex==1 ? 'tabbarItemActive' : 'tabbarItemNoActive'"
@click='changePage(1)'>账号登录</span>
<span style="color: #9c9fa4">|</span>
<span :class="titleIndex==2 ? 'tabbarItemActive' : 'tabbarItemNoActive'"
@click='changePage(2)'>忘记密码</span>
</view>
</view>
<configPopup ref="popup"></configPopup>
</view>
</template>
<script>
import configPopup from '@/components/config/configPopup.vue';
export default {
components: {
configPopup
},
onLoad(option) {
this.titleIndex = option.index || 1
},
data() {
return {
titleIndex: 0,
titleList: ['注册', '登录', '重置密码'],
checkboxValue: [],
form: {
username: '',//19330214982
password: '',//1234567
loginModel: 0,
captcha: '',
},
form1: {
username: '',
captcha: '',
password: '',
newPassword: '',
},
tips: '获取验证码',
seconds: 60,
}
},
methods: {
submit() {
if (!this.checkboxValue.length && this.titleIndex != 2) {
return uni.showToast({
title: '请先同意隐私协议',
icon: 'none'
})
}
if (this.titleIndex == 0) {
//
this.register()
} else if (this.titleIndex == 1) {
//
this.login()
} else {
//
this.updatePassword()
}
},
//
login(){
if (this.$utils.verificationAll(this.form, {
username: '请输入账号',
password: '请输入密码',
})) {
return
}
if (!this.$utils.verificationPhone(this.form.username)) {
return uni.showToast({
title: '请输入合法的手机号',
icon: 'none'
})
}
//
this.$store.commit('login', this.form)
},
//
register(){
if (this.$utils.verificationAll(this.form1, {
username: '请输入账号',
captcha : '请输入验证码',
password: '请输入密码',
newPassword: '请确认密码',
})) {
return
}
if (!this.$utils.verificationPhone(this.form1.username)) {
return uni.showToast({
title: '请输入合法的手机号',
icon: 'none'
})
}
if (this.form1.password != this.form1.newPassword) {
return uni.showToast({
title: '密码与确认密码不一致',
icon: 'none'
})
}
this.$api('registerUser', this.form1, res => {
this.form1 = {}
if (res.code == 200) {
this.titleIndex = 1
uni.showToast({
title: '注册成功,请登录!',
icon: 'none'
})
}
})
},
updatePassword(){
if (this.$utils.verificationAll(this.form1, {
username: '请输入账号',
captcha : '请输入验证码',
password: '请输入新密码',
newPassword: '请确认密码',
})) {
return
}
if (!this.$utils.verificationPhone(this.form1.username)) {
return uni.showToast({
title: '请输入合法的手机号',
icon: 'none'
})
}
if (this.form1.password != this.form1.newPassword) {
return uni.showToast({
title: '密码与确认密码不一致',
icon: 'none'
})
}
this.$api('newPassword', this.form1, res => {
this.form1 = {}
if (res.code == 200) {
this.titleIndex = 1
uni.showToast({
title: '修改成功,请登录!',
icon: 'none'
})
}
})
},
//
changePage(index) {
this.titleIndex = index
},
confirm() {
},
codeChange(text) {
this.tips = text;
},
getCode() {
if (this.$refs.code.canGetCode) {
if (!this.$utils.verificationPhone(this.form1.username)) {
return uni.showToast({
title: '请输入合法的手机号',
icon: 'none'
})
}
uni.showLoading({
title: '正在获取验证码'
})
this.$api('sendSms', {
username: this.form1.username,
}, res => {
if (res.code == 200) {
uni.hideLoading();
// this.start()
uni.$uv.toast('验证码已发送');
//
this.$refs.code.start();
}
})
} else {
uni.$uv.toast('请勿重复发送');
}
},
end() {
// uni.$uv.toast('');
},
start() {
// uni.$uv.toast('');
}
}
}
</script>
<style lang="scss" scoped>
* {
box-sizing: border-box;
}
.content{
font-size: 22rpx;
margin: 0 auto;
}
.refundsOrExchange {
background-color: #FFF;
height: 100vh;
.frame {
background-color: #FFF;
.loginRegister {
display: flex;
flex-direction: column;
gap: 40rpx;
padding-bottom: 50rpx;
.title {
display: flex;
justify-content: center;
align-items: flex-end;
height: 10vh;
color: #000;
font-size: 40rpx;
font-weight: 700;
}
.userIamge {
display: flex;
justify-content: center;
height: 10vh;
>view:nth-of-type(1) {
width: 25%;
height: 100%;
border-radius: 50%;
overflow: hidden;
}
}
.form {
height: 12vh;
>view:nth-of-type(1) {
padding: 20rpx 100rpx;
}
>view:nth-of-type(2) {
padding: 0 100rpx;
}
>view:nth-of-type(3) {
display: flex;
padding: 30rpx 100rpx 0 100rpx;
font-size: 22rpx;
}
}
}
.btn {
// height: 5vh;
display: flex;
justify-content: center;
margin: 90rpx 0 0 0;
.a {
display: flex;
justify-content: center;
align-items: center;
width: 70%;
height: 80rpx;
color: #FFF;
background-color: $uni-color;
box-shadow: 0 0 4rpx 4rpx rgba($uni-color, 0.2);
border-radius: 100rpx;
font-size: 30rpx;
}
}
.bottomTab {
display: flex;
justify-content: space-between;
height: 10vh;
padding: 0 80rpx;
margin-top: 30rpx;
.tabbarItemActive {
color: $uni-color;
}
.tabbarItemNoActive {
color: #9c9fa4;
}
}
.forgetPassword {
padding: 100rpx 40rpx 0 40rpx;
.title {
display: flex;
justify-content: center;
align-items: flex-end;
height: 10vh;
color: #000;
font-size: 40rpx;
font-weight: 700;
}
.form1 {
display: flex;
flex-direction: column;
gap: 30rpx;
margin-top: 20rpx;
padding: 20rpx 80rpx;
.userName {
// padding: 20rpx 100 rpx;
}
.code {
display: flex;
width: 100%;
.left {
width: 55%;
}
.right {
width: 45%;
height: 100%;
>view:nth-of-type(1) {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
}
}
}
.password1 {}
.password2 {}
}
}
}
}
</style>

+ 155
- 0
pages_order/auth/wxLogin.vue View File

@ -0,0 +1,155 @@
<template>
<view class="login">
<view class="logo">
<!-- <image src="/static/image/login/logo.png" mode=""></image> -->
</view>
<view class="title">
欢迎使用酒店桌布租赁平台
</view>
<view class="btn mt"
@click="wxLogin">
<view class="icon">
<image src="../static/auth/wx.png" mode=""></image>
</view>
<view class="">
微信授权登录
</view>
</view>
<!-- <view class="btn b2">
使用短信验证登录
</view> -->
<view class="btn b2"
@click="qux">
取消登录
</view>
<view class="config">
<uv-checkbox-group
v-model="checkboxValue"
shape="circle">
<view class="content">
<view
style="display: flex;">
<uv-checkbox
size="40rpx"
icon-size="30rpx"
activeColor="#FD5100"
:name="1"
></uv-checkbox>
阅读并同意我们的<text @click="$refs.popup.open('getPrivacyPolicy')">服务协议与隐私条款</text>
</view>
<view class="">
以及<text @click="$refs.popup.open('getUserAgreement')">个人信息保护指引</text>
</view>
</view>
</uv-checkbox-group>
</view>
<configPopup ref="popup"></configPopup>
</view>
</template>
<script>
export default {
name : 'Login',
data() {
return {
checkboxValue : []
}
},
methods: {
wxLogin(){
if(!this.checkboxValue.length){
return uni.showToast({
title: '请先同意隐私协议',
icon:'none'
})
}
this.$store.commit('login')
},
qux(){
uni.reLaunch({
url: '/pages/index/index'
})
},
}
}
</script>
<style scoped lang="scss">
.login{
display: flex;
justify-content: center;
align-items: center;
height: 80vh;
flex-direction: column;
position: relative;
.logo{
height: 140rpx;
width: 140rpx;
background-color: #ddd;
border-radius: 30rpx;
image{
height: 140rpx;
width: 140rpx;
border-radius: 30rpx;
}
margin-bottom: 20rpx;
}
.title{
position: relative;
font-weight: 900;
font-size: 45rpx;
&::after{
content: '';
position: absolute;
left: 0;
top: 100%;
display: block;
height: 8rpx;
width: 210rpx;
background: linear-gradient(to right,$uni-color, #fff);
}
}
.btn{
width: 80%;
height: 100rpx;
background-color: $uni-color;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
margin: 20rpx 0;
border-radius: 20rpx;
.icon{
margin-right: 10rpx;
image{
width: 40rpx;
height: 35rpx;
}
}
}
.b2{
background-color: rgba($uni-color, 0.2);
color: $uni-color;
}
.mt{
margin-top: 200rpx;
}
.config{
position: absolute;
bottom: 0;
font-size: 24rpx;
text-align: center;
line-height: 40rpx;
text{
color: $uni-color;
}
}
}
</style>

+ 133
- 0
pages_order/auth/wxUserInfo.vue View File

@ -0,0 +1,133 @@
<template>
<view class="login">
<view class="title">
酒店桌布租赁平台
</view>
<view class="title">
申请获取你的头像昵称
</view>
<button class="chooseAvatar" open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
<view class="line">
<view class="">
头像
</view>
<view class="">
<image :src="userInfo.headImage" v-if="userInfo.headImage" style="width: 60rpx;height: 60rpx;"
mode=""></image>
<image src="../static/auth/headImage.png" v-else style="width: 50rpx;height: 50rpx;" mode=""></image>
</view>
</view>
</button>
<view class="line">
<view class="">
昵称
</view>
<view class="">
<input type="nickname" placeholder="请输入昵称" style="text-align: right;" id="nickName"
v-model="userInfo.nickName" />
</view>
</view>
<view class="btn" @click="submit">
确认
</view>
</view>
</template>
<script>
export default {
data() {
return {
userInfo: {
headImage: '',
nickName: '',
}
};
},
onShow() {},
computed: {},
methods: {
onChooseAvatar(res) {
let self = this
self.$Oss.ossUpload(res.target.avatarUrl)
.then(url => {
self.userInfo.headImage = url
})
},
submit() {
let self = this
uni.createSelectorQuery().in(this)
.select("#nickName")
.fields({
properties: ["value"],
})
.exec((res) => {
const nickName = res?.[0]?.value
self.userInfo.nickName = nickName
if (self.$utils.verificationAll(self.userInfo, {
headImage: '请选择头像',
nickName: '请填写昵称',
})) {
return
}
self.$api('updateInfo', self.userInfo, res => {
if (res.code == 200) {
uni.switchTab({
url:'/pages/index/index'
})
}
})
})
},
}
}
</script>
<style lang="scss" scoped>
.login {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 80vh;
.title {
line-height: 45rpx;
font-weight: 900;
}
.line {
display: flex;
justify-content: space-between;
align-items: center;
width: 80%;
border-bottom: 1px solid #00000023;
padding: 30rpx 0;
margin: 0 auto;
}
.chooseAvatar {
width: 100%;
padding: 0;
margin: 0;
margin-top: 10vh;
border: none;
}
.btn {
// background: $uni-linear-gradient-btn-color;
background: $uni-color;
color: #fff;
width: 80%;
padding: 20rpx 0;
text-align: center;
border-radius: 15rpx;
margin-top: 10vh;
}
}
</style>

+ 244
- 0
pages_order/components/address/addressList.vue View File

@ -0,0 +1,244 @@
<template>
<scroll-view
scroll-y="true"
:style="{height: height}"
@scrolltolower="moreAddress">
<uv-radio-group v-model="selectAddress"
@change="editDefault"
v-if="addressList.length > 0">
<view v-for="item in addressList" :key="item.id" class="address-item">
<view class="address-item-top"
@click="select(item)">
<view class="img-box">
<image src="../../static/address/icon.png" mode="aspectFill"></image>
</view>
<view class="address-info">
<view class="user-info">
<text class="user-name">{{ item.name }}</text>
<text class="user-phone">{{ item.phone }}</text>
<text v-if="item.defaultFlag == 1" class="is-default">默认</text>
</view>
<view class="address-detail">
{{ item.address + " " + item.addressDetail }}
</view>
</view>
</view>
<view class="controls"
v-if="controls">
<view class="default-checkbox">
<uv-radio
:name="item.id"
label-disabled
size="30rpx"
icon-size="30rpx">
默认地址
</uv-radio>
</view>
<view class="edit-btn">
<uv-icon name="edit-pen"></uv-icon>
<text @click="editAddress(item)" class="control-title">编辑</text>
</view>
<view class="del-btn">
<uv-icon name="trash"></uv-icon>
<text class="control-title" @click="deleteAddress(item.id)">删除</text>
</view>
</view>
</view>
</uv-radio-group>
<view
style="padding: 100rpx 0;"
v-else>
<uv-empty
mode="history"
textSize="28rpx"
iconSize="100rpx"/>
</view>
</scroll-view>
</template>
<script>
export default {
props : {
controls : {
default : false,
type : Boolean,
},
height : {
default : 'calc(90vh - 180rpx)'
}
},
data() {
return {
selectAddress : 0,
queryParams: {
pageNo: 1,
pageSize: 10,
},
addressList: [],
total : 0,
}
},
methods: {
//
getAddressList() {
return new Promise((success, fail) => {
this.$api('addressPage', this.queryParams, res => {
if (res.code == 200) {
this.addressList = res.result.records || [];
this.total = res.result.total || 0;
res.result.records.forEach(n => { //
if (n.defaultFlag == 1) {
this.selectAddress = n.id
}
})
success(res.result)
}
})
})
},
//
moreAddress(){
if(this.queryParams.pageSize > this.total){
return
}
this.queryParams.pageSize += 10
this.getAddressList()
},
//
deleteAddress(e){
this.$emit('deleteAddress', e)
},
//
editAddress(e){
this.$emit('editAddress', e)
},
//
editDefault(e){
this.$emit('editDefault', e)
},
//
select(e){
this.$emit('select', e)
},
}
}
</script>
<style scoped lang="scss">
.address-item {
background: white;
border-radius: 20rpx;
overflow: hidden;
margin-bottom: 20rpx;
padding: 15rpx 15rpx 0rpx 15rpx;
width: 680rpx;
.address-item-top {
border-bottom: 1px dashed #D3D1D1;
display: flex;
align-items: center;
padding: 0rpx 0rpx 15rpx 0rpx;
.img-box {
width: 120rpx;
height: 120rpx;
image {
width: 75%;
height: 75%;
display: block;
margin: 12.5% auto;
}
}
.address-info {
width: calc(100% - 120rpx);
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
.user-info {
display: flex;
align-items: center;
text {
display: block;
line-height: 40rpx;
margin-right: 20rpx;
}
.user-name,
.user-phone {
font-size: 30rpx;
}
.is-default {
display: flex;
align-items: center;
justify-content: center;
background: #FEB773;
color: white;
width: 80rpx;
height: 35rpx;
border-radius: 20rpx;
font-size: 22rpx;
}
}
.address-detail {
color: #4a4a4a;
font-size: 26rpx;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
text-overflow: ellipsis;
}
}
}
.controls {
display: flex;
align-items: center;
justify-content: space-between;
align-items: center;
font-size: 26rpx;
padding: 15rpx 15rpx 25rpx 15rpx;
.default-checkbox {
display: flex;
text {
margin-left: 8rpx;
}
}
.control-title {
height: 30rpx;
line-height: 30rpx;
color: #666666;
}
view {
display: flex;
align-items: center;
}
image {
width: 23rpx;
height: 23rpx;
vertical-align: middle;
margin-right: 8rpx;
}
}
}
</style>

+ 220
- 0
pages_order/components/address/redactAddress.vue View File

@ -0,0 +1,220 @@
<template>
<uv-popup round="40rpx" ref="addressPopup" :customStyle="{ height: 'auto' , width : '100%' , padding : '20rpx'}">
<view class="redact-address">
<view class="redact-address-title">{{title}}</view>
<uv-form label-width="210rpx" :model="addressDetail" ref="form">
<uv-form-item label="联系人" prop="name">
<uv-input v-model="addressDetail.name" placeholder="请输入联系人姓名" border="none">
</uv-input>
</uv-form-item>
<uv-form-item label="手机号" prop="phone">
<uv-input v-model="addressDetail.phone" placeholder="请输入手机号" border="none">
</uv-input>
</uv-form-item>
<uv-form-item label="所在地区" prop="address">
<uv-input v-model="addressDetail.address" placeholder="请选择所在地区" border="none">
</uv-input>
<template #right>
<view style="padding-right: 40rpx;color: #FBAB32;" @click.stop="selectAddr">
<image src="../../static/address/selectIcon.png" mode="aspectFit"></image>定位
</view>
</template>
</uv-form-item>
<uv-form-item label="详细地址" prop="addressDetail">
<uv-input v-model="addressDetail.addressDetail" placeholder="请输入详细地址" border="none">
</uv-input>
</uv-form-item>
</uv-form>
<view @click="onSubmit" class="save">{{ addressDetail.id ? '修改地址' : '新增地址'}}</view>
</view>
</uv-popup>
</template>
<script>
import Position from '@/utils/position.js'
export default {
data() {
return {
addressDetail: {}
}
},
props: {
title: {
type: String,
default: '新增地址'
}
},
methods: {
open(addressDetail) {
this.addressDetail = addressDetail
this.$refs.addressPopup.open('bottom')
},
close(){
this.$refs.addressPopup.close()
},
//
onSubmit() {
let isOk = this.parameterVerification(this.addressDetail)
if (isOk && !isOk.auth) {
return uni.showToast({
icon: 'none',
title: isOk.title,
'zIndex': 10000
})
}
this.$emit('saveOrUpdate', this.addressDetail)
},
//
parameterVerification(addressDetaila) {
let {
name,
phone,
address,
addressDetail
} = addressDetaila
if (name.trim() == '') {
return {
title: '请填写联系人',
auth: false
}
} else if (phone.trim() == '') {
return {
title: '请填写手机号',
auth: false
}
} else if (address.trim() == '') {
return {
title: '请填写所在地区',
auth: false
}
} else if (addressDetail.trim() == '') {
return {
title: '请填写详细地址',
auth: false
}
} else if (phone.trim() != '') {
if (!this.$utils.verificationPhone(phone)) {
return {
title: '手机号格式不合法',
auth: false
}
}
}
return {
title: '验证通过',
auth: true
}
},
//
selectAddr() {
Position.getLocation(res => {
Position.selectAddress(res.longitude, res.latitude, success => {
this.setAddress(success)
})
})
},
//
setAddress(res) {
//
this.addressDetail.latitude = res.latitude
this.addressDetail.longitude = res.longitude
if (!res.address && res.name) { //
return this.addressDetail.address = res.name
}
if (res.address || res.name) {
return this.addressDetail.address = res.address + res.name
}
this.addressDetail.address = '' //
},
}
}
</script>
<style lang="scss" scoped>
.redact-address {
box-sizing: border-box;
.redact-address-title {
height: 80rpx;
line-height: 80rpx;
font-size: 30rpx;
color: #333333;
font-weight: 600;
}
.save {
display: flex;
align-items: center;
justify-content: center;
width: 90%;
height: 80rpx;
border-radius: 40rpx;
color: white;
font-size: 28rpx;
margin: 0rpx auto;
background: $uni-color;
margin-top: 150rpx;
}
image {
width: 25rpx;
height: 25rpx;
}
//
.uv-form {
padding: 30rpx 0rpx;
}
&::v-deep .uv-cell {
padding: 0rpx 0rpx;
font-size: 26rpx;
&::after {
border: none !important;
}
.uv-field__label {
display: flex;
align-items: center;
height: 80rpx;
}
.uv-field__control,
.uv-field__right-icon {
height: 80rpx;
font-size: 26rpx;
border-bottom: 2rpx solid #cbc8c8;
}
.uv-field__right-icon {
display: flex;
align-items: center;
height: 78rpx;
color: #5FCC9F;
}
.uv-cell__value {
height: 120rpx;
}
}
&::v-deep .uv-field__error-message {
color: #5AC796;
font-size: 20rpx;
margin-top: 10rpx;
}
}
</style>

+ 77
- 0
pages_order/components/product/submit.vue View File

@ -0,0 +1,77 @@
<template>
<view class="submit">
<view class=""
@click="$emit('share')">
<uv-icon
size="40rpx"
name="share-square"></uv-icon>
<view class="">
分享
</view>
</view>
<view class=""
@click="$utils.navigateTo('/index/cart')">
<uv-icon
size="40rpx"
name="shopping-cart"></uv-icon>
<view class="">
购物车
</view>
</view>
<view class="btn"
@click="$emit('submit')">
{{ submiitTitle }}
</view>
</view>
</template>
<script>
export default {
name:"submit",
props : {
submiitTitle : {
default : '立即租赁',
type : String,
}
},
data() {
return {
}
},
methods: {
}
}
</script>
<style lang="scss" scoped>
.submit{
position: fixed;
bottom: 0;
left: 0;
width: 100vw;
background-color: #fff;
height: 100rpx;
display: flex;
justify-content: center;
align-items: center;
font-size: 24rpx;
.btn{
background: $uni-color;
width: 600rpx;
height: 80rpx;
color: #fff;
border-radius: 40rpx;
font-size: 28rpx;
}
view{
width: 100rpx;
margin: 0 10rpx;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
}
</style>

+ 367
- 0
pages_order/components/product/submitUnitSelect.vue View File

@ -0,0 +1,367 @@
<template>
<uv-popup ref="popup"
:round="30"
bgColor="#f7f7f7">
<view class="content">
<!-- 地址 -->
<view class="address"
@click="openAddress">
<image src="../../static/address/selectIcon.png" mode=""></image>
<view class="">
{{ address.name }}
</view>
<view class="">
{{ address.addressDetail }}
</view>
<view class="icon">
<uv-icon
size="30rpx"
name="arrow-right"></uv-icon>
</view>
</view>
<!-- 商品信息和数量 -->
<view class="submit-info">
<view class="title">
桌布租赁
</view>
<view class="box">
<image
class="image"
:src="unit.pic"
mode=""></image>
<view class="info">
<view class="price">
<text>{{ unit.depositPrice }}</text>
</view>
<view class="unit">
请选择规格
</view>
<view class="">
<uv-number-box v-model="num"></uv-number-box>
</view>
</view>
</view>
</view>
<!-- 规格 -->
<view class="submit-unit">
<view class="title">
规格选择
</view>
<view class="list">
<view :class="{act : unitIndex == index}"
v-for="(item, index) in detail.hotelGoodsSkuList"
@click="selectUnit(item, index)"
:key="index">
{{ item.title }}
</view>
</view>
</view>
<!-- 费用明细 -->
<view class="expense-detail">
<view class="title">
费用明细
</view>
<view class="detail">
押金{{ unit.depositPrice }}
</view>
</view>
<!-- 提交按钮 -->
<view class="submit-btn">
<view class="l"
@click="addCart">
加入租赁车
</view>
<view class="r"
@click="orderPay">
{{ submiitTitle }}
</view>
</view>
</view>
<uv-popup ref="addressPopup" :round="30">
<addressList
ref="addressList"
height="60vh"
@select="selectAddress"
/>
</uv-popup>
</uv-popup>
</template>
<script>
import addressList from '../address/addressList.vue'
export default {
components : {
addressList,
},
props : {
submiitTitle : {
default : '立即租赁',
type : String,
},
detail : {
default : {}
}
},
data() {
return {
unitIndex : 0,
address : {
name : '请选择联系人',
addressDetail : '',
},
num : 1,
unit : {},
addressTotal : 0,
}
},
methods: {
//
open(){
this.$refs.popup.open('bottom')
if(!this.unit.id){
this.selectUnit(this.detail.hotelGoodsSkuList[0], 0)
}
//
this.$refs.addressList.getAddressList().then(res => {
this.addressTotal = res.total
if(this.addressTotal != 0){
this.address = res.records[0]
}
})
},
//
close(){
this.$refs.popup.close()
},
//
openAddress(){
if (this.addressTotal == 0) {
this.$refs.popup.close()
return uni.navigateTo({
url: '/pages_order/mine/address?type=back'
})
}
this.$refs.addressPopup.open('bottom')
},
//
selectAddress(e){
this.address = e
this.$refs.addressPopup.close()
},
//
selectUnit(item, index){
this.unit = item
this.unitIndex = index
},
addCart(){
this.$api('cartAdd', {
id : this.detail.id,
skuId : this.unit.id,
}, res => {
if(res.code == 200){
uni.showToast({
title: '添加成功',
});
this.$refs.popup.close()
}
})
},
orderPay(){
let data = {
id : this.detail.id,//id
skuId : this.unit.id,//id
addressId : this.address.id,//id
sku : this.unit.title,//
num : this.num,
}
if(this.$utils.verificationAll(data, {
skuId : '请选择规格',
addressId : '请选择地址',
})){
return
}
this.$api('orderPay', data, res => {
if(res.code == 200){
uni.redirectTo({
url: '/pages/index/order'
})
// uni.requestPayment({
// provider: 'wxpay', //
// timeStamp: res.result.timeStamp, //
// nonceStr: res.result.nonceStr, //
// package: res.result.packageValue,
// signType: res.result.signType, //
// paySign: res.result.paySign, //
// success: function (res) {
// console.log('',res);
// uni.redirectTo({
// url: '/pages/index/order'
// })
// },
// fail: function (err) {
// console.log('',err);
// uni.showToast({
// icon:'none',
// title:""
// })
// }
// });
}
})
},
}
}
</script>
<style scoped lang="scss">
.content{
max-height: 80vh;
overflow: hidden;
overflow-y: auto;
.address{
display: flex;
padding: 20rpx;
background-color: #fff;
image{
width: 30rpx;
height: 30rpx;
margin: 20rpx;
}
view{
margin: 20rpx;
overflow:hidden; //
text-overflow:ellipsis; //
white-space:nowrap; //
}
.icon{
margin-left: auto;
}
}
.submit-info{
background-color: #fff;
padding: 30rpx;
margin-top: 20rpx;
.title{
font-size: 30rpx;
padding: 10rpx;
font-weight: 600;
}
.box{
display: flex;
margin-top: 10rpx;
.image{
width: 200rpx;
height: 200rpx;
border-radius: 20rpx;
margin-right: 20rpx;
}
.info{
flex: 1;
.unit{
font-size: 24rpx;
padding: 10rpx 20rpx;
color: #717171;
display: flex;
align-items: center;
}
.price{
color: $uni-color;
font-size: 28rpx;
padding: 10rpx 20rpx;
text{
font-size: 36rpx;
font-weight: 900;
}
}
}
}
}
.submit-unit{
padding: 30rpx;
background-color: #fff;
.title{
font-size: 28rpx;
font-weight: 600;
}
.list{
display: flex;
flex-wrap: wrap;
font-size: 22rpx;
.act{
color: $uni-color;
border: 1px solid $uni-color;
background-color: #F9E7DE;
}
view{
border-radius: 15rpx;
width: 320rpx;
background-color: #F3F3F3;
border: 1px solid #F3F3F3;
margin: 10rpx;
display: flex;
justify-content: center;
padding: 15rpx 0;
}
}
}
.expense-detail{
padding: 30rpx;
background-color: #fff;
font-size: 28rpx;
.title{
font-weight: 600;
}
.detail{
background-color: #F6F6F6;
color: #717171;
margin: 10rpx 0;
padding: 10rpx 20rpx;
}
}
.submit-btn{
width: 600rpx;
height: 80rpx;
color: #fff;
border-radius: 40rpx;
font-size: 28rpx;
margin: 20rpx auto;
display: flex;
justify-content: center;
align-items: center;
border: 1rpx solid $uni-color;
overflow: hidden;
.l{
flex: 1;
display: flex;
justify-content: center;
align-items: center;
color: $uni-color;
}
.r{
background: $uni-color;
flex: 1;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
}
}
</style>

+ 213
- 0
pages_order/mine/address.vue View File

@ -0,0 +1,213 @@
<template>
<view class="address">
<navbar title="地址管理" leftClick @leftClick="leftClick" />
<view class="address-list">
<addressList
controls
ref="addressList"
@deleteAddress="deleteAddress"
@editAddress="editAddress"
@editDefault="editDefault"/>
</view>
<redactAddress
ref="addressPopup"
:addressDetail="addressDetail"
@saveOrUpdate="saveOrUpdate"
:title="title"></redactAddress>
<view class="add-btn">
<view @click="addBtn" class="btn">
新增地址
</view>
</view>
</view>
</template>
<script>
import redactAddress from '../components/address/redactAddress.vue'
import addressList from '../components/address/addressList.vue'
export default {
components: {
redactAddress,
addressList
},
data() {
return {
title: '新增地址',
type : '',
}
},
onLoad(args) {
this.type = args.type
if(this.type == 'back'){
this.addBtn()
}
},
onShow() {
this.getAddressList()
},
methods: {
//
getAddressList() {
this.$refs.addressList.getAddressList()
},
//
editAddress(address) {
this.$refs.addressPopup.open({...address})
},
//
leftClick() {
uni.navigateBack(-1)
},
//
saveOrUpdate(addressDetail) {
let data = {
name: addressDetail.name,
phone: addressDetail.phone,
address: addressDetail.address,
addressDetail: addressDetail.addressDetail,
defaultId: addressDetail.defaultId || '0',
latitude: addressDetail.latitude,
longitude: addressDetail.longitude
}
if (addressDetail.id) {
data.id = addressDetail.id
}
this.$api(data.id ? 'addressEdit' : 'addressAdd', data, res => {
if (res.code == 200) {
this.$refs.addressPopup.close()
this.getAddressList()
if(this.type == 'back'){
uni.navigateBack(-1)
}
uni.showToast({
title: '操作成功',
icon: 'none'
})
}
})
},
//
editDefault(id) {
this.$api('addressDefault', {
id: id,
}, res => {
if (res.code == 200) {
this.$refs.addressPopup.close()
uni.showToast({
title: '操作成功',
icon: 'none'
})
this.getAddressList()
}
})
},
//
deleteAddress(id) {
let self = this
uni.showModal({
title: '删除地址',
content: '确认删除此地址?删除后数据不可恢复',
success(e) {
if(e.confirm){
self.$api('addressDelete', {
id
}, res => {
if (res.code == 200) {
uni.showToast({
title: '删除成功',
icon: 'none'
})
self.getAddressList()
}
})
}
}
})
},
//
addBtn() {
this.title = '新增地址'
this.$refs.addressPopup.open({ //
name: '',
phone: '',
address: '',
addressDetail: '',
defaultId: '',
latitude: '',
longitude: ''
})
},
}
}
</script>
<style lang="scss" scoped>
.address {
width: 750rpx;
margin: 0rpx auto;
background: #F5F5F5;
box-sizing: border-box;
min-height: 100vh;
.address-list {
padding: 40rpx 20rpx 120rpx 20rpx;
}
.add-btn {
position: fixed;
display: flex;
justify-content: center;
align-items: center;
left: 0;
bottom: 0;
width: 750rpx;
height: 100rpx;
background: white;
.btn {
display: flex;
align-items: center;
justify-content: center;
width: 85%;
height: 80rpx;
border-radius: 40rpx;
color: white;
text-align: center;
font-size: 28rpx;
background: $uni-color;
}
}
}
@media all and (min-width: 961px) {
.add-btn {
left: 50% !important;
transform: translateX(-50%);
}
}
//
:deep(.uni-system-choose-location) {
z-index: 99999 !important;
}
</style>

+ 172
- 0
pages_order/mine/help.vue View File

@ -0,0 +1,172 @@
<template>
<!-- 帮助与反馈 -->
<view class="help">
<navbar title="帮助与反馈" leftClick @leftClick="$utils.navigateBack" />
<view class="help-box">
<view>
<view class="help-issue">
<text>问题和意见</text>
<text style="color: #BD3624;">*</text>
</view>
<uv-textarea v-model="form.question" :count="true" border="none" height="400"
placeholder="请把发现的问题提交给我们,感谢您的参与(必填)"
:text-style="{color:'#BCB7B7',fontSize:'28rpx'}" />
</view>
<view>
<view class="help-issue">
<text>问题截图</text>
<!-- <text style="color: #BD3624;">*</text> -->
</view>
<view class="help-screenshot">
<uv-upload :fileList="fileList" multiple :maxCount="3" width="180rpx"
height="180rpx" multiple @afterRead="afterRead" @delete="deleteImage">
<image src="../static/help/uploading.png" mode="aspectFill"
style="width: 180rpx;height: 180rpx;" />
</uv-upload>
</view>
</view>
<view>
<view class="help-issue">
<text>联系方式</text>
<text style="color: #BD3624;">*</text>
</view>
<view class="form-sheet-cell">
<view>
联系姓名
</view>
<input placeholder="请输入联系姓名" v-model="form.name" />
</view>
<view class="form-sheet-cell">
<view>
联系电话
</view>
<input placeholder="请输入联系电话" v-model="form.phone" />
</view>
</view>
<view class="help-button">
<!-- <view>历史提交</view> -->
<view @click="submit">确认</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
form : {
question : '',
phone : '',
name : '',
image : '',
},
fileList: []
}
},
onLoad(args) {
},
methods: {
deleteImage(e){
this.fileList.splice(e.index, 1)
},
afterRead(e){
let self = this
e.file.forEach(file => {
self.$Oss.ossUpload(file.url).then(url => {
self.fileList.push({
url
})
})
})
},
submit(){
let data = JSON.parse(JSON.stringify(this.form))
if(this.$utils.verificationAll(this.form, {
question : '请输入你的问题和意见',//
// image : '',//
name : '请输入姓名',//
phone : '请输入手机号码',//
})){
return
}
data.image = this.fileList.map((item) => item.url).join(",")
this.$api('addAdvice', data, res => {
if(res.code == 200){
uni.showToast({
title: res.message,
icon:'none'
})
setTimeout(uni.navigateBack, 800, -1)
}
})
},
}
}
</script>
<style scoped lang="scss">
.help {
.help-box {
width: 92%;
margin-left: 4%;
.help-issue {
margin: 20rpx;
font-size: 28rpx;
font-weight: 600;
color: #333333;
}
.help-screenshot {
display: flex;
align-items: center;
background-color: #fff;
padding: 20rpx;
}
.form-sheet-cell{
display: flex;
background-color: #fff;
padding: 20rpx 30rpx;
font-size: 24rpx;
align-items: center;
view{
width: 150rpx;
}
}
.help-button {
display: flex;
justify-content: center;
font-size: 24rpx;
flex-shrink: 0;
margin-top: 60rpx;
view {
padding: 14rpx 120rpx;
border-radius: 38rpx;
}
view:nth-child(1) {
background: $uni-color;
color: #fff;
}
view:nth-child(2) {
color: #FFFDF6;
background-color: #C83741;
}
}
}
}
</style>

+ 283
- 0
pages_order/mine/promotion.vue View File

@ -0,0 +1,283 @@
<template>
<view class="promotion">
<navbar title="二维码"
bgColor="#A3D250"
color="#fff"
leftClick @leftClick="$utils.navigateBack" />
<view class="promotion-card">
<!-- <view class="user-info"> -->
<!-- <image class="image" :src="userInfo.headImage" mode="widthFix"></image> -->
<!-- <image class="image" src="../../static/logo.png" mode="widthFix"></image> -->
<!-- <view class="user-name">{{ userInfo.nickName }}</view> -->
<!-- <view class="user-name">湖南第一深情</view> -->
<!-- </view> -->
<!-- <view class="invitation-code-img">
<image
style="width: 400rpx;"
:src="imagePath" mode="widthFix"></image>
</view> -->
<image style="width: 100%;" :src="imagePath" mode="widthFix"></image>
<!-- <view class="invitation-code">加油站: {{ title }}</view> -->
<canvas id="myCanvas" type="2d" canvas-id="firstCanvas1"></canvas>
</view>
<view class="btns">
<view class="btn"
@click="preservationImg(imagePath)">保存</view>
<!-- <view class="btn">立即邀请</view> -->
</view>
</view>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'Promotion',
computed: {
...mapState(['userInfo']),
},
data() {
return {
url: '',
title: '123123',
baseUrl: 'https://dianpin-img.xzaiyp.top/',
canvas: {},
imagePath: '',
}
},
onShow() {
this.getQrCode()
this.$store.commit('getUserInfo')
},
methods: {
getQrCode() {
this.$api('getInviteCode', res => {
if (res.code == 200) {
this.url = res.result.url
this.title = res.result.name
this.draw()
}
})
},
draw() {
uni.showLoading({
title: "拼命绘画中..."
})
wx.createSelectorQuery()
.select('#myCanvas') // canvasid
.fields({
node: true,
size: true
})
.exec((res) => {
const canvas = res[0].node
//
const ctx = canvas.getContext('2d')
// Canvas
const width = res[0].width
const height = res[0].height
//
const dpr = wx.getWindowInfo().pixelRatio
//dpr
// dpr 2 4
// 3 6
let Ratio = dpr * 2
console.log("bug", dpr)
canvas.width = width * dpr
canvas.height = height * dpr
this.canvas = canvas
ctx.scale(dpr, dpr)
ctx.clearRect(0, 0, width, height)
ctx.fillStyle = '#fff'
ctx.fillRect(0, 0, canvas.width, canvas.height)
//
// const image = canvas.createImage()
// image.onload = () => {
// ctx.drawImage(image, 30, 18, 40, 40)
// }
// image.src = '/public/img/wechar_1.png'
// image.src = this.userInfo.headImage
ctx.fillStyle = 'black'
ctx.font = '22px PingFangSC-regular';
let s = this.title || '加油站'
ctx.fillText(s, canvas.width / Ratio - s.length * 11, 50);
//
const coderImage = canvas.createImage()
coderImage.src = this.baseUrl + this.url
coderImage.onload = () => {
ctx.drawImage(coderImage,
canvas.width / Ratio - 240 / 2, 100, 240, 240)
}
//
setTimeout(() => {
wx.canvasToTempFilePath({
x: 0,
y: 0,
width: this.canvas.width,
height: this.canvas.height,
canvas,
success: (res) => {
var tempFilePath = res.tempFilePath;
this.imagePath = tempFilePath
uni.hideLoading()
}
});
}, 600);
})
},
back() {
uni.navigateBack(-1)
},
preservationImg(img) {
let that = this
uni.authorize({
/* scope.writePhotosAlbum 类型是保存到相册 */
scope: 'scope.writePhotosAlbum',
success() {
/* 已授权进入 */
/* 保存图片到相册方法方法 */
that.imgApi(img);
},
complete(res) {
/* 判断如果没有授权就打开设置选项让用户重新授权 */
uni.getSetting({
success(res) {
if (!res.authSetting['scope.writePhotosAlbum']) {
/* 打开设置的方法 */
that.openInstall();
}
}
});
}
});
},
imgApi(image) {
/* 获取图片的信息 */
uni.getImageInfo({
src: image,
success: function(image) {
/* 保存图片到手机相册 */
uni.saveImageToPhotosAlbum({
filePath: image.path,
success: function() {
uni.showModal({
title: '保存成功',
content: '图片已成功保存到相册',
showCancel: false
});
},
complete(res) {
console.log(res);
}
});
}
});
},
}
}
</script>
<style lang="scss" scoped>
.promotion {
width: 100%;
height: 100vh;
background-color: $uni-color;
.promotion-card {
width: 90%;
margin: 100rpx auto 0rpx auto;
box-shadow: 0rpx 0rpx 15rpx rgba(0, 0, 0, .2);
border-radius: 15rpx;
padding: 40rpx 30rpx;
box-sizing: border-box;
background-color: #fff;
.user-info {
display: flex;
align-items: center;
.image {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
margin-right: 10rpx;
}
.user-name {
font-size: 30rpx;
}
}
.invitation-code-img {
display: flex;
align-items: center;
justify-content: center;
margin: 100rpx 0rpx;
.image {
width: 30%;
border-radius: 50%;
}
}
.invitation-code {
text-align: center;
color: #818181;
font-size: 28rpx;
}
}
.btns {
position: fixed;
left: 0;
bottom: 0;
width: 100%;
height: 120rpx;
display: flex;
background: #fff;
font-size: 28rpx;
color: $uni-color;
.btn {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
&:nth-child(2) {
background: orange;
}
}
}
}
#myCanvas {
position: fixed;
left: 100%;
/* visibility: hidden */
/* visibility: hidden; */
/* margin-top: 100rpx; */
margin: 68rpx auto;
width: 750rpx;
height: 750rpx;
/* line-height: 20px; */
background-color: rgba(255, 255, 255, 1);
text-align: center;
}
</style>

+ 109
- 0
pages_order/mine/purse.vue View File

@ -0,0 +1,109 @@
<template>
<view class="purse">
<navbar title="立即提现" leftClick @leftClick="$utils.navigateBack" />
<!-- 水洗店 -->
<view class="userShop">
<userShopCommission purse />
</view>
<view class="from-body">
<view>我要提现</view>
<view class="from-line">
<input placeholder="请输入提现金额" />
</view>
<view class="from-line">
<input placeholder="请输入姓名" />
</view>
<view class="from-line">
<input placeholder="请输入开户行" />
</view>
<view class="from-line">
<input placeholder="请输入银行卡卡号" />
</view>
<view class="mt56">提现说明</view>
<view style="line-height: 45rpx; font-size: 24rpx;color: #666666;" v-html="notice">
</view>
<!-- <p>1本次提现必须通过银行卡提现暂不支持其他途径</p>
<p>2如若遇到24小时提现未到账请联系客服</p> -->
</view>
<view class="b-fiexd">
<view class="button-submit">提交</view>
</view>
</view>
</template>
<script>
import userShopCommission from '@/components/userShop/userShopCommission.vue'
export default {
components: {
userShopCommission,
},
data() {
return {
notice : ''
}
},
methods: {
}
}
</script>
<style scoped lang="scss">
.purse{
min-height: 100vh;
background-color: #ffffff;
.from-body {
padding: 40rpx 20rpx;
font-size: 28rpx;
font-family: PingFang SC, PingFang SC-Bold;
font-weight: 700;
text-align: left;
color: #333333;
line-height: 40px;
padding-bottom: 160rpx;
.from-line {
margin-top: 40rpx;
}
input {
width: 612rpx;
height: 90rpx;
line-height: 90rpx;
background: #F5F5F5;
border-radius: 46rpx;
padding: 0 50rpx;
font-size: 28rpx;
font-family: PingFang SC, PingFang SC-Regular;
font-weight: 400;
text-align: left;
color: #333;
}
}
.button-submit {
width: 596rpx;
height: 90rpx;
line-height: 90rpx;
background: $uni-color;
border-radius: 46rpx;
margin: 20rpx auto;
font-size: 28rpx;
font-family: PingFang SC, PingFang SC-Regular;
font-weight: 400;
text-align: center;
color: #ffffff;
}
}
</style>

+ 88
- 0
pages_order/mine/runningWater.vue View File

@ -0,0 +1,88 @@
<template>
<view class="running-water">
<navbar :title="title[status]" leftClick @leftClick="leftClick" />
<view class="tab-box">
<view class="tab-box1" v-if="agentFlow && agentFlow.total">
<uv-cell center border :title="item.title"
v-for="(item, index) in agentFlow.records"
:value="x[item.type] + item.money" :label="item.createTime" />
</view>
<view
style="padding: 100rpx 0;"
v-else>
<uv-empty
mode="history"
textSize="28rpx"
iconSize="100rpx"/>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
title : ['余额记录','提现记录','佣金记录'],
agentFlow : {
total : 0,
records : [
{
type : 0,
money : 100,
createTime : '2024-04-02 20:00',
title : "佣金提现",
},
{
type : 0,
money : 100,
createTime : '2024-04-02 20:00',
title : "佣金提现",
},
{
type : 0,
money : 100,
createTime : '2024-04-02 20:00',
title : "佣金提现",
},
]
},
x : ['+', '-' , '-' , '+'],
status : 0,
}
},
onLoad(e) {
this.status = e.status
},
methods: {
leftClick() { //
uni.navigateBack(-1)
},
getAgentFlow(){ //
let type = this.status;
this.$api('getAgentFlow', { type }, res => {
if(res.code == 200){
this.agentFlow = res.result
}
})
},
}
}
</script>
<style lang="scss" scoped>
.running-water{
width: 750rpx;
background: #F5F5F5;
margin: 0 auto;
min-height: 100vh;
.tab-box{
margin: 20rpx;
background-color: #fff;
border-radius: 20rpx;
overflow: hidden;
}
}
</style>

+ 567
- 0
pages_order/order/orderDetail.vue View File

@ -0,0 +1,567 @@
<template>
<view>
<navbar
title="订单详情"
leftClick
@leftClick="$utils.navigateBack"
/>
<!-- 水洗店 -->
<view class=""
v-if="userShop">
<view class="controls">
<view class="title">
<image src="../static/order/icon.png" mode=""></image>
服务完成
</view>
<view class="tips">
待送回
</view>
<view class="btns">
<view class="btn1">
快递寄回
</view>
<view class="btn2">
线下配送
</view>
</view>
</view>
<view class="steps">
<uv-steps
activeColor="#FD5100"
:current="stepsCurrent" dot>
<uv-steps-item :title="item"
:key="index"
v-for="(item, index) in steps"></uv-steps-item>
</uv-steps>
</view>
</view>
<!-- 酒店和水洗店 -->
<view class="info">
<view class="flex"
style="display: flex;">
<view style="width: 8rpx;height: 30rpx;
background: #FD5100;border-radius: 6rpx;" />
<view class="head-title">服务项目</view>
</view>
<view class="flex">
<view class="server-item">
<view class="img-box">
<image :src="msgShop.image" mode="aspectFill"></image>
</view>
<view class="server-info">
<view class="server-title">
{{msgOrder.projectName}}
<!-- <view class="coupon">领券立减</view> -->
</view>
<view class="current-price">
<text class="unit"></text>{{msgOrder.money}}
</view>
<view class="sales-volume" style="margin-top: 5px;">
<view class="desc">规格{{msgOrder.unit}}</view>
</view>
<view class="time-coupon">
<!-- <view class="flex">
<image src="@/static/home/time-icon.png"></image>
<view class="time">{{msgOrder.useTime}}分钟</view>
</view> -->
<!-- <view class="sales-volume">
<image src="@/static/icons/icon1.png"></image>
<view class="desc">已售出{{msgShop.payNum}}+</view>
</view> -->
</view>
</view>
</view>
</view>
<!-- <view class="line min_tips">
<view class="head-div flex">
<view style="width: 118rpx;height: 118rpx;border-radius: 50%;overflow: hidden;">
<image style="width: 118rpx;" :src="msgTechnician.image" mode="widthFix"></image>
</view>
<view style="padding: 10rpx 34rpx;display: flex;flex-direction: column;justify-content: space-around;">
<view class="nickname">
{{msgTechnician.title}}
<view v-if="msgTechnician.isVip" class="tag">
<image src="@/static/order/s.png" mode="aspectFit"></image>
<view class="auth">官方认证</view>
</view>
</view>
<view class="days">
<van-rate v-model="msgTechnician.score" :size="10" readonly color="#ffb54c" void-icon="star"
void-color="#eee" />
<view class="">
好评{{msgTechnician.pinNum}}
</view>
</view>
</view>
</view>
<view @click="gototechnicianDetail(msgTechnician)" class="btn-x">
服务技师
</view>
</view> -->
<view class="line address">
<view class="address-top">
<!-- <view class="">
服务地址
</view> -->
<view class="copy">
<image @click="copy(msgOrder.name + ' ' + msgOrder.phone + ' ' + msgOrder.address)" src="/static/order/copy.png"></image>
</view>
</view>
<view class="addressDetail">
<view class="">{{msgOrder.name}} {{msgOrder.phone}}</view>
<view class="">{{msgOrder.address}}</view>
</view>
</view>
<view class="line">
<view class="t min_tips">
<view class="">
实付款
</view>
<view class="current-price">
{{ msgOrder.money }}
</view>
</view>
<view class="min_tips">
<view class="">
租赁费用
</view>
<view class="">
{{ msgOrder.price }}
</view>
</view>
<view class="min_tips">
<view class="">
水洗费用
</view>
<view class="">
{{ msgOrder.price}}
</view>
</view>
<view class="min_tips">
<view class="">
押金
</view>
<view class="">
{{ msgOrder.price }}
</view>
</view>
</view>
<!-- 订单信息 -->
<view class="line">
<view class="t min_tips">
<view class="">
订单信息
</view>
</view>
<view class="min_tips">
<view class="">
订单编号
</view>
<view class="">
{{msgOrder.id}}
</view>
</view>
<view class="min_tips">
<view class="">
下单时间
</view>
<view class="">
{{msgOrder.createTime}}
</view>
</view>
</view>
<!-- 下单须知 -->
<view class="line">
<view class="t min_tips">
<view class="">
下单须知
</view>
</view>
<view class="min_tips" style="line-height: 40rpx;">
{{msgShop.projectExplain}}
</view>
<view class="btns">
<view @click="clickService" class="btn">
联系客服
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
computed : {
...mapGetters(['userShop']),
},
data() {
return {
stepsCurrent : 0,
steps : [
'接单',
'检查',
'开始清洗',
'服务完成',
],
msgShop : {
money : 99.99,
image : 'https://img95.699pic.com/photo/50058/1378.jpg_wh860.jpg',
projectExplain : '1.xxxxxxxxxx xxxxxxxxxx。2.xxxxxx xxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxx xxxxxxxxxxxxxxxx。3.。',
},
msgOrder : {
money : 99.99,
address : '广东省广州市越秀区城南故事C3栋2802',
name : '李**',
phone : '150*****091',
unit : '120*40*75【桌子尺寸】',
state_dictText : '已完成',
price : 199.99,
id : '020644568964457',
createTime : '2024-01-18 15:39',
projectName : '桌布租赁'
},
}
},
methods: {
}
}
</script>
<style scoped lang="scss">
.order {
background: linear-gradient(#4899a6, #6fc6ad, #6fc6ad);
padding-bottom: 10px;
}
.controls{
margin: 20rpx;
background-color: #fff;
height: 400rpx;
display: flex;
flex-direction: column;
width: 710rpx;
border-radius: 20rpx;
justify-content: center;
align-items: center;
.title{
display: flex;
justify-content: center;
align-items: center;
font-size: 40rpx;
image{
width: 100rpx;
height: 100rpx;
margin-right: 20rpx;
}
}
.tips{
font-size: 26rpx;
color: #FD5100;
margin-top: 10rpx;
}
.btns{
margin-top: 50rpx;
display: flex;
view{
margin: 0 20rpx;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
background-color: $uni-color;
padding: 15rpx 40rpx;
border-radius: 40rpx;
}
.btn2{
background-color: #FFFFFF;
border: 1px solid #A7A7A7;
color: #A7A7A7;
}
}
}
.steps{
margin: 20rpx;
background-color: #fff;
display: flex;
flex-direction: column;
width: 710rpx;
border-radius: 20rpx;
padding: 70rpx 0;
/deep/ .uv-text__value{
font-size: 22rpx !important;
}
}
.box {
padding: 20px;
.btns {
display: flex;
justify-content: center;
align-items: center;
margin-top: 10px;
.btn {
color: #fff;
padding: 10rpx 50rpx;
background-color: #ffb300;
border-radius: 30rpx;
font-size: 25rpx;
margin-right: 10rpx;
}
.btc{
background: #ccc;
}
}
}
.info {
margin: 10px;
padding: 20rpx;
background-color: #fff;
width: calc(100% - 40px);
border-radius: 10px;
.head-title {
font-family: PingFang SC, PingFang SC-Bold;
color: #2f2e2e;
line-height: 30rpx;
margin-left: 10rpx;
}
.server-item {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
background: white;
border-radius: 15rpx;
box-sizing: border-box;
margin: 20rpx 0rpx;
width: 100%;
.img-box {
width: 150rpx;
height: 150rpx;
border-radius: 10rpx;
overflow: hidden;
image {
width: 100%;
height: 100%;
}
}
.server-info {
display: flex;
flex-direction: column;
justify-content: space-around;
width: calc(100% - 180rpx);
box-sizing: border-box;
padding: 10rpx 15rpx;
.server-title {
display: flex;
margin-bottom: 10rpx;
}
.coupon {
display: flex;
justify-content: center;
align-items: center;
background: #F29E45;
color: white;
width: 120rpx;
height: 40rpx;
border-radius: 10rpx;
margin-left: 10rpx;
font-size: 22rpx;
}
.time-coupon,
.price {
display: flex;
flex-wrap: wrap;
align-items: center;
}
.time-coupon {
margin: 10rpx 0rpx;
font-size: 26rpx;
justify-content: space-between;
width: 100%;
.flex {
justify-content: center;
align-items: center;
}
image {
width: 25rpx;
height: 25rpx;
}
.time {
color: #B8B8B8;
margin-left: 6rpx;
}
}
.sales-volume {
display: flex;
align-items: center;
color: #B8B8B8;
font-size: 24rpx;
image {
width: 25rpx;
height: 25rpx;
}
}
}
}
.address {
.address-top{
display: flex;
justify-content: space-between;
align-items: center;
image{
width: 30rpx;
height: 30rpx;
}
}
.addressDetail {
color: #777;
font-size: 22rpx;
padding: 5px 0;
}
text {
background-color: #F29E45;
padding: 8rpx 10rpx;
color: #fff;
font-size: 20rpx;
margin-left: 10px;
border-radius: 5px;
}
}
.min_tips {
font-size: 22rpx;
color: #777;
display: flex;
justify-content: space-between;
padding: 5px 0;
align-items: center;
}
.current-price {
font-size: 30rpx;
color: #FD5100;
}
.line {
border-top: 2px dotted #00000011;
padding: 20rpx 0;
.t {
padding: 5px 0;
color: #000;
font-size: 26rpx;
}
}
.head-div {
.nickname {
font-size: 30rpx;
font-weight: 600;
text-align: left;
line-height: 42rpx;
display: flex;
align-items: center;
.tag {
position: relative;
display: flex;
align-items: center;
image {
height: 45rpx;
width: 90rpx;
vertical-align: middle;
}
.auth {
position: absolute;
white-space: nowrap;
color: #FF6200;
left: 23rpx;
font-size: 17rpx;
}
}
}
.days {
font-size: 20rpx;
font-weight: 400;
text-align: left;
line-height: 56rpx;
display: flex;
align-items: center;
view {
padding-left: 5px;
}
}
}
.btn-x {
color: #6fc6ad;
border: 1px solid #6fc6ad;
padding: 10rpx 20rpx;
border-radius: 30rpx;
}
.btns {
display: flex;
justify-content: center;
.btn {
color: #6fc6ad;
border: 1px solid #6fc6ad;
padding: 10rpx 20rpx;
border-radius: 30rpx;
}
}
}
</style>

+ 353
- 0
pages_order/order/refundsOrExchange.vue View File

@ -0,0 +1,353 @@
<template>
<view class="refundsOrExchange">
<navbar :title="title[titleIndex]" leftClick @leftClick="$utils.navigateBack" />
<view class="frame">
<!-- 商品简介 -->
<view class="itme1" @click="openSpPopup">
<view class="left">
<img src="../../static/image/center/1.png" alt="" style="width: 100%;height: 100%;">
</view>
<view class="center">
<view>{{ commodity.title }}</view>
<view>{{ commodity.smallTitle }}</view>
</view>
<view class="right">×{{ commodity.total }}</view>
</view>
<!--<commoditySelect ></commoditySelect>-->
<!-- 申请类型&申请原因 -->
<view class="item2">
<view class="type">
<span>申请类型</span>
<span>退货退款</span>
</view>
<uv-line></uv-line>
<view class="reason">
<view>申请原因</view>
<view>
<uv-input placeholder="请输入申请原因" border="none" clearable></uv-input>
</view>
</view>
</view>
<!-- 退货数量&申请金额-->
<view class="item3">
<view class="type">
<span>{{ titleIndex == 0 ? '退货数量' : '换货数量' }}</span>
<span>
<uv-number-box :min="1" :max="100"></uv-number-box>
</span>
</view>
<uv-line v-if='titleIndex == 0 ? true :false'></uv-line>
<view class="reason" v-if='titleIndex == 0 ? true :false'>
<view>申请原因</view>
<view>
<uv-input disabled placeholder="$" border="none" clearable></uv-input>
</view>
</view>
</view>
<!-- 申请说明 -->
<view class="item4">
<view>申请说明(选填)</view>
<view>
<uv-input placeholder="请您详细填写申请说明" border="none" clearable></uv-input>
</view>
<view>
<uv-upload :fileList="fileList" :maxCount="5" multiple width="150rpx" height="150rpx"
@delete="deleteImage" @afterRead="afterRead" :previewFullImage="true"></uv-upload>
</view>
</view>
<!-- 联系电话 -->
<view class="item5">
<view class="phone">
<view>联系电话</view>
<view>
<uv-input placeholder="请输入联系电话" border="none" clearable></uv-input>
</view>
</view>
</view>
</view>
<!-- 底部按钮 -->
<!--商品选择-->
<uv-popup ref="spPopup" :round="30">
</uv-popup>
</view>
</template>
<script>
export default {
onLoad(option) {
this.titleIndex = option.index
},
components: {
},
data() {
return {
titleIndex: 0,
title: ['申请换货', '申请退货'],
fileList: [],
bottomBtnStyle: {
color: '#FFF',
backgroundColor: '#fd5100',
fontSize: '34rpx',
text: '提交申请',
width: '400rpx',
height: '80rpx',
borderRadius: '100rpx',
bottom: '40rpx'
},
commodityList: [{
title: '商品名称',
smallTitle: '产品规格:120*4*75【桌子尺寸】',
total: 1,
},
{
title: '商品名称1',
smallTitle: '产品规格:120*4*75【桌子尺寸】',
total: 1,
},
{
title: '商品名称2',
smallTitle: '产品规格:120*4*75【桌子尺寸】',
total: 1,
}
],
commodity: {
title: '商品名称',
smallTitle: '产品规格:120*4*75【桌子尺寸】',
total: 1,
},
}
},
mounted() {
},
methods: {
openSpPopup() {
this.$refs.spPopup.open('bottom');
},
// 退
selectCommodity(e) {
console.log(e, "selectCommodity--e")
this.commodity = e
this.$refs.spPopup.close()
},
confirm() {
console.log("===");
},
deleteImage(e) {
this.fileList.splice(e.index, 1)
},
afterRead(e) {
let self = this
e.file.forEach(file => {
self.$Oss.ossUpload(file.url).then(url => {
self.fileList.push({
url
})
})
})
},
}
}
</script>
<style lang="scss" scoped>
* {
box-sizing: border-box;
}
.refundsOrExchange {
.frame {
display: flex;
flex-direction: column;
gap: 30rpx;
width: 100%;
padding-top: 40rpx;
background-color: #f5f5f5;
.itme1 {
display: flex;
height: 200rpx;
background-color: #ffffff;
.left {
padding: 40rpx;
width: 20%;
border-radius: 10rpx;
background-color: #ffffff;
}
.center {
display: flex;
flex-direction: column;
justify-content: center;
gap: 20rpx;
width: 60%;
padding: 0rpx 0 0 20rpx;
background-color: #ffffff;
// view
>view:first-of-type {
font-size: 36rpx;
color: #333;
}
// view
>view:nth-of-type(2) {
font-size: 28rpx;
color: #666666;
}
}
.right {
display: flex;
justify-content: center;
align-items: center;
width: 10%;
color: #666666;
background-color: #ffffff;
}
}
.item2 {
width: 100vw;
.type {
display: flex;
align-items: center;
background-color: #FFF;
height: 80rpx;
padding: 0 0 0 20rpx;
>span:nth-of-type(1) {
width: 30%;
}
>span:nth-of-type(2) {
width: 70%;
}
}
.reason {
display: flex;
align-items: center;
background-color: #FFF;
height: 80rpx;
// margin: 10rpx 0 0 0;
padding: 10rpx 0 0 20rpx;
>view:nth-of-type(1) {
width: 30%;
}
>view:nth-of-type(2) {
width: 70%;
padding: 0 20rpx 0 0;
}
}
}
.item3 {
width: 100vw;
.type {
display: flex;
align-items: center;
background-color: #FFF;
height: 80rpx;
padding: 0 0 0 20rpx;
>span:nth-of-type(1) {
width: 70%;
}
>span:nth-of-type(2) {
width: 30%;
}
}
.reason {
display: flex;
align-items: center;
background-color: #FFF;
height: 80rpx;
// margin: 10rpx 0 0 0;
padding: 10rpx 0 0 20rpx;
>view:nth-of-type(1) {
width: 30%;
}
>view:nth-of-type(2) {
width: 70%;
padding: 0 20rpx 0 0;
}
}
}
.item4 {
display: flex;
flex-direction: column;
padding: 10rpx 0 0 20rpx;
background-color: #FFF;
>view:nth-of-type(1) {
background-color: #FFF;
}
>view:nth-of-type(2) {
margin: 10rpx 0 10rpx 0;
background-color: #FFF;
}
}
.item5 {
display: flex;
flex-direction: column;
padding: 0 0 0 20rpx;
background-color: #FFF;
.phone {
display: flex;
align-items: center;
background-color: #FFF;
height: 80rpx;
// margin: 10rpx 0 0 0;
padding: 10rpx 0 0 20rpx;
>view:nth-of-type(1) {
width: 30%;
}
>view:nth-of-type(2) {
width: 70%;
padding: 0 20rpx 0 0;
}
}
}
}
}
</style>

+ 169
- 0
pages_order/product/productDetail.vue View File

@ -0,0 +1,169 @@
<template>
<view class="page">
<navbar title="商品详情" leftClick @leftClick="$utils.navigateBack" />
<view class="swipe">
<uv-swiper
:list="productDetail.image.split(',')"
indicator
height="350rpx"></uv-swiper>
</view>
<view class="info">
<view class="title">
桌布租赁
</view>
<view class="info-line">
<view class="price">
<text>45.9</text>
</view>
<view class="num">
已售1000+
<image src="../static/product/like.png" mode=""></image>
</view>
</view>
<view class="tips">
<view class="tip">
专业设备
</view>
<view class="tip">
科学流程
</view>
<view class="tip">
质量保证
</view>
</view>
</view>
<view class="info-unit">
<uv-cell title="是否有桌布" isLink>
<template #icon>
<text class="text">桌布</text>
</template>
</uv-cell>
<uv-cell title="请选择规格" isLink>
<template #icon>
<text class="text">规格</text>
</template>
</uv-cell>
<uv-cell>
<template #icon>
<text>上门取件·送货上门</text>
</template>
</uv-cell>
</view>
<view class="content">
<view class="title">
商品详情
</view>
<uv-parse :content="productDetail.content"></uv-parse>
</view>
<!-- 分享和租赁按钮 -->
<submit
@submit="$refs.submitUnitSelect.open()"
@share="share"/>
<!-- 选择规格 -->
<submitUnitSelect ref="submitUnitSelect"/>
</view>
</template>
<script>
import submit from '../components/product/submit.vue'
import submitUnitSelect from '../components/product/submitUnitSelect.vue'
export default {
components : {
submit,
submitUnitSelect
},
data() {
return {
productDetail : {
image : 'https://cdn.uviewui.com/uview/swiper/swiper3.png,https://cdn.uviewui.com/uview/swiper/swiper2.png',
content : '',
}
}
},
onLoad(args) {
console.log(args);
},
methods: {
//
share(){
},
//
submit(){
}
}
}
</script>
<style scoped lang="scss">
.page{
.swipe{
}
.info{
padding: 30rpx;
background-color: #fff;
.title{
font-size: 34rpx;
font-weight: 900;
}
.info-line{
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 30rpx;
.price{
font-size: 28rpx;
color: $uni-color;
text{
font-size: 34rpx;
}
}
.num{
font-size: 24rpx;
image{
width: 24rpx;
height: 24rpx;
}
}
}
.tips{
display: flex;
font-size: 20rpx;
margin-top: 30rpx;
.tip{
margin-right: 40rpx;
}
}
}
.info-unit{
margin-top: 20rpx;
padding: 30rpx;
background-color: #fff;
/deep/ text{
font-size: 26rpx;
}
/deep/ .text{
color: #7C7C7C;
margin-right: 20rpx;
font-size: 26rpx;
}
}
.content{
background-color: #fff;
margin-top: 20rpx;
.title{
padding: 30rpx;
}
}
}
</style>

BIN
pages_order/static/address/icon.png View File

Before After
Width: 96  |  Height: 96  |  Size: 4.5 KiB

BIN
pages_order/static/address/selectIcon.png View File

Before After
Width: 28  |  Height: 36  |  Size: 1.3 KiB

BIN
pages_order/static/auth/headImage.png View File

Before After
Width: 80  |  Height: 80  |  Size: 1.4 KiB

BIN
pages_order/static/auth/wx.png View File

Before After
Width: 22  |  Height: 18  |  Size: 453 B

BIN
pages_order/static/help/uploading.png View File

Before After
Width: 216  |  Height: 216  |  Size: 7.2 KiB

BIN
pages_order/static/order/icon.png View File

Before After
Width: 82  |  Height: 82  |  Size: 3.0 KiB

BIN
pages_order/static/product/like.png View File

Before After
Width: 25  |  Height: 22  |  Size: 1.1 KiB

BIN
static/image/PrivacyAgreementPoup/icon.png View File

Before After
Width: 39  |  Height: 39  |  Size: 1007 B

BIN
static/image/cart/1.png View File

Before After
Width: 36  |  Height: 36  |  Size: 1.9 KiB

BIN
static/image/cart/2.png View File

Before After
Width: 72  |  Height: 72  |  Size: 5.4 KiB

BIN
static/image/center/1.png View File

Before After
Width: 51  |  Height: 49  |  Size: 1.2 KiB

BIN
static/image/center/10.png View File

Before After
Width: 355  |  Height: 158  |  Size: 45 KiB

+ 1
- 0
static/image/center/11.svg View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1726157567630" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6220" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32"><path d="M513.536 628.6336c101.6832 0 280.8832 38.7072 310.8864 193.7408 77.5168-77.5168 124.928-185.9584 124.928-305.0496 0-241.152-194.6624-435.8144-435.8144-435.8144S77.7216 277.0944 77.7216 517.2224c0 119.0912 47.4112 227.6352 124.928 306.0736 30.0032-154.9312 209.2032-194.6624 310.8864-194.6624z m0-439.7056c112.3328 0 204.3904 91.0336 204.3904 204.3904 0 112.3328-91.0336 204.3904-204.3904 204.3904a203.776 203.776 0 0 1-204.3904-204.3904c0.1024-112.4352 92.0576-204.3904 204.3904-204.3904z m0 0" fill="#cdcdcd" p-id="6221"></path><path d="M515.4816 1006.8992c-65.8432 0-129.8432-12.9024-189.952-38.4-58.1632-24.576-110.2848-59.8016-155.136-104.5504A485.56032 485.56032 0 0 1 65.8432 708.8128c-25.6-60.2112-38.5024-124.1088-38.5024-189.952 0-65.8432 12.9024-129.8432 38.4-189.952 24.576-58.1632 59.8016-110.2848 104.5504-155.136 44.8512-44.8512 96.9728-79.9744 155.136-104.5504C385.6384 43.6224 449.536 30.72 515.4816 30.72c65.8432 0 129.8432 12.9024 189.952 38.4 58.1632 24.576 110.2848 59.8016 155.136 104.5504 44.8512 44.8512 79.9744 96.9728 104.5504 155.136C990.6176 389.0176 1003.52 452.9152 1003.52 518.8608c0 65.8432-12.9024 129.8432-38.4 189.952-24.576 58.1632-59.8016 110.2848-104.5504 155.136a485.56032 485.56032 0 0 1-155.136 104.5504 484.39296 484.39296 0 0 1-189.952 38.4z m0-936.8576c-60.6208 0-119.3984 11.8784-174.6944 35.2256a447.76448 447.76448 0 0 0-142.6432 96.1536A444.42624 444.42624 0 0 0 101.9904 344.064a446.70976 446.70976 0 0 0-35.2256 174.6944c0 60.6208 11.8784 119.3984 35.2256 174.6944 22.6304 53.4528 54.9888 101.4784 96.1536 142.6432 41.1648 41.2672 89.1904 73.6256 142.6432 96.1536 55.296 23.3472 114.0736 35.2256 174.6944 35.2256 60.6208 0 119.3984-11.8784 174.6944-35.2256 53.4528-22.6304 101.4784-54.9888 142.6432-96.1536 41.2672-41.1648 73.6256-89.1904 96.1536-142.6432 23.3472-55.296 35.2256-114.0736 35.2256-174.6944 0-60.6208-11.8784-119.3984-35.2256-174.6944a447.76448 447.76448 0 0 0-96.1536-142.6432 444.42624 444.42624 0 0 0-142.6432-96.1536 447.44704 447.44704 0 0 0-174.6944-35.2256z m0 0" fill="#cdcdcd" p-id="6222"></path></svg>

BIN
static/image/center/2.png View File

Before After
Width: 112  |  Height: 126  |  Size: 6.1 KiB

BIN
static/image/center/3.png View File

Before After
Width: 120  |  Height: 120  |  Size: 5.9 KiB

BIN
static/image/center/4.png View File

Before After
Width: 52  |  Height: 49  |  Size: 1.7 KiB

BIN
static/image/center/5.png View File

Before After
Width: 64  |  Height: 64  |  Size: 2.8 KiB

BIN
static/image/center/6.png View File

Before After
Width: 63  |  Height: 50  |  Size: 2.1 KiB

BIN
static/image/center/7.png View File

Before After
Width: 64  |  Height: 64  |  Size: 2.4 KiB

BIN
static/image/center/8.png View File

Before After
Width: 64  |  Height: 64  |  Size: 2.5 KiB

BIN
static/image/center/9.png View File

Before After
Width: 56  |  Height: 48  |  Size: 2.1 KiB

BIN
static/image/home/0.png View File

Before After
Width: 116  |  Height: 118  |  Size: 14 KiB

BIN
static/image/home/1.png View File

Before After
Width: 117  |  Height: 117  |  Size: 13 KiB

BIN
static/image/home/2.png View File

Before After
Width: 114  |  Height: 114  |  Size: 12 KiB

BIN
static/image/home/3.png View File

Before After
Width: 114  |  Height: 114  |  Size: 11 KiB

BIN
static/image/home/address-icon.png View File

Before After
Width: 38  |  Height: 38  |  Size: 1.4 KiB

BIN
static/image/home/arrow-icon.png View File

Before After
Width: 39  |  Height: 39  |  Size: 296 B

BIN
static/image/home/search-icon.png View File

Before After
Width: 38  |  Height: 38  |  Size: 1.8 KiB

BIN
static/image/product/favorable.png View File

Before After
Width: 60  |  Height: 15  |  Size: 282 B

BIN
static/image/tabbar/cart-a.png View File

Before After
Width: 40  |  Height: 40  |  Size: 958 B

BIN
static/image/tabbar/cart.png View File

Before After
Width: 40  |  Height: 40  |  Size: 953 B

BIN
static/image/tabbar/category-a.png View File

Before After
Width: 40  |  Height: 40  |  Size: 1.1 KiB

BIN
static/image/tabbar/category.png View File

Before After
Width: 40  |  Height: 40  |  Size: 1.2 KiB

BIN
static/image/tabbar/center-a.png View File

Before After
Width: 40  |  Height: 40  |  Size: 915 B

BIN
static/image/tabbar/center.png View File

Before After
Width: 40  |  Height: 40  |  Size: 937 B

BIN
static/image/tabbar/home-a.png View File

Before After
Width: 47  |  Height: 43  |  Size: 2.2 KiB

BIN
static/image/tabbar/home.png View File

Before After
Width: 47  |  Height: 43  |  Size: 2.2 KiB

BIN
static/image/tabbar/order-a.png View File

Before After
Width: 40  |  Height: 40  |  Size: 691 B

BIN
static/image/tabbar/order.png View File

Before After
Width: 40  |  Height: 40  |  Size: 736 B

BIN
static/logo.png View File

Before After
Width: 72  |  Height: 72  |  Size: 3.9 KiB

+ 110
- 0
store/store.js View File

@ -0,0 +1,110 @@
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex); //vue的插件机制
import api from '@/api/api.js'
//Vuex.Store 构造器选项
const store = new Vuex.Store({
state: {
configList: {}, //配置列表
shop : false,//身份判断如果不需要,可以删除
userInfo : {}, //用户信息
},
getters: {
// 角色 true为水洗店 false为酒店 : 身份判断如果不需要,可以删除
userShop(state){
return state.shop
}
},
mutations: {
// 初始化配置
initConfig(state){
api('getConfig', res => {
const configList = {
...state.configList,
}
if (res.code == 200) {
res.result.forEach(n => {
configList[n.keyName] = n.keyContent;
configList[n.keyName + '_keyValue'] = n.keyValue;
});
}
state.configList = configList
uni.$emit('initConfig', state.configList)
})
// let config = ['getPrivacyPolicy', 'getUserAgreement']
// config.forEach(k => {
// api(k, res => {
// if (res.code == 200) {
// state.configList[k] = res.result
// }
// })
// })
},
// 微信登录
login(state){
uni.showLoading({
title: '登录中...'
})
uni.login({
success(res) {
if(res.errMsg != "login:ok"){
return
}
api('wxLogin', {
code : res.code
}, res => {
uni.hideLoading()
if(res.code != 200){
return
}
state.userInfo = res.result.userInfo
uni.setStorageSync('token', res.result.token)
if(!state.userInfo.nickName || !state.userInfo.headImage){
uni.navigateTo({
url: '/pages_order/auth/wxUserInfo'
})
}else{
uni.navigateBack(-1)
}
})
}
})
},
// 获取用户个人信息
getUserInfo(state){
api('getInfo', res => {
if(res.code == 200){
state.userInfo = res.result
}
})
},
// 退出登录
logout(state){
uni.showModal({
title: '确认退出登录吗',
success(r) {
if(r.confirm){
state.userInfo = {}
state.role = false
uni.removeStorageSync('token')
uni.reLaunch({
url: '/pages/index/index'
})
}
}
})
},
},
actions: {},
})
export default store

+ 10
- 0
uni.promisify.adaptor.js View File

@ -0,0 +1,10 @@
uni.addInterceptor({
returnValue (res) {
if (!(!!res && (typeof res === "object" || typeof res === "function") && typeof res.then === "function")) {
return res;
}
return new Promise((resolve, reject) => {
res.then((res) => res[0] ? reject(res[0]) : resolve(res[1]));
});
},
});

+ 96
- 0
uni.scss View File

@ -0,0 +1,96 @@
/**
* 这里是uni-app内置的常用样式变量
*
* uni-app 官方扩展插件及插件市场https://ext.dcloud.net.cn上很多三方插件均使用了这些样式变量
* 如果你是插件开发者建议你使用scss预处理并在插件代码中直接使用这些变量无需 import 这个文件方便用户通过搭积木的方式开发整体风格一致的App
*
*/
/**
* 如果你是App开发者插件使用者你可以通过修改这些变量来定制自己的插件主题实现自定义主题功能
*
* 如果你的项目同样使用了scss预处理你也可以直接在你的 scss 代码中使用如下变量同时无需 import 这个文件
*/
/* 颜色变量 */
$uni-color: #FD5100;
/* 行为相关颜色 */
$uni-color-primary: #007aff;
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
/* 文字基本颜色 */
$uni-text-color:#333;//基本色
$uni-text-color-inverse:#fff;//反色
$uni-text-color-grey:#999;//辅助灰色如加载更多的提示信息
$uni-text-color-placeholder: #808080;
$uni-text-color-disable:#c0c0c0;
/* 背景颜色 */
$uni-bg-color:#ffffff;
$uni-bg-color-grey:#f8f8f8;
$uni-bg-color-hover:#f1f1f1;//点击状态颜色
$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
/* 边框颜色 */
$uni-border-color:#c8c7cc;
/* 尺寸变量 */
/* 文字尺寸 */
$uni-font-size-sm:12px;
$uni-font-size-base:14px;
$uni-font-size-lg:16px;
/* 图片尺寸 */
$uni-img-size-sm:20px;
$uni-img-size-base:26px;
$uni-img-size-lg:40px;
/* Border Radius */
$uni-border-radius-sm: 2px;
$uni-border-radius-base: 3px;
$uni-border-radius-lg: 6px;
$uni-border-radius-circle: 50%;
/* 水平间距 */
$uni-spacing-row-sm: 5px;
$uni-spacing-row-base: 10px;
$uni-spacing-row-lg: 15px;
/* 垂直间距 */
$uni-spacing-col-sm: 4px;
$uni-spacing-col-base: 8px;
$uni-spacing-col-lg: 12px;
/* 透明度 */
$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
/* 文章场景相关 */
$uni-color-title: #2C405A; // 文章标题颜色
$uni-font-size-title:20px;
$uni-color-subtitle: #555555; // 二级标题颜色
$uni-font-size-subtitle:26px;
$uni-color-paragraph: #3F536E; // 文章段落颜色
$uni-font-size-paragraph:15px;
.uni-color-btn{
border-radius: 40rpx;
padding: 20rpx;
margin: 40rpx;
background: $uni-color;
color: #fff;
text-align: center;
font-size: 28rpx;
}
.uni-uncolor-btn{
border-radius: 40rpx;
padding: 20rpx;
margin: 40rpx;
border: 1px solid $uni-color;
color: $uni-color;
text-align: center;
font-size: 28rpx;
}

+ 7
- 0
uni_modules/uv-action-sheet/changelog.md View File

@ -0,0 +1,7 @@
## 1.0.2(2023-07-02)
uv-action-sheet 由于弹出层uv-popup的修改,打开和关闭方法更改,详情参考文档:https://www.uvui.cn/components/actionSheet.html
## 1.0.1(2023-05-16)
1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
2. 优化部分功能
## 1.0.0(2023-05-10)
uv-action-sheet 底部操作菜单

+ 50
- 0
uni_modules/uv-action-sheet/components/uv-action-sheet/props.js View File

@ -0,0 +1,50 @@
export default {
props: {
// 标题,有值则显示,同时会显示关闭按钮
title: {
type: String,
default: ''
},
// 选项上方的描述信息
description: {
type: String,
default: ''
},
// 数据
actions: {
type: Array,
default: () => []
},
// 取消按钮的文字,不为空时显示按钮
cancelText: {
type: String,
default: ''
},
// 点击某个菜单项时是否关闭弹窗
closeOnClickAction: {
type: Boolean,
default: true
},
// 处理底部安全区(默认true)
safeAreaInsetBottom: {
type: Boolean,
default: true
},
// 小程序的打开方式
openType: {
type: String,
default: ''
},
// 点击遮罩是否允许关闭 (默认true)
closeOnClickOverlay: {
type: Boolean,
default: true
},
// 圆角值
round: {
type: [Boolean, String, Number],
default: 0
},
...uni.$uv?.props?.actionSheet
}
}

+ 280
- 0
uni_modules/uv-action-sheet/components/uv-action-sheet/uv-action-sheet.vue View File

@ -0,0 +1,280 @@
<template>
<uv-popup
ref="popup"
mode="bottom"
:safeAreaInsetBottom="safeAreaInsetBottom"
:round="round"
:close-on-click-overlay="closeOnClickOverlay"
@change="popupChange"
>
<view class="uv-action-sheet">
<view
class="uv-action-sheet__header"
v-if="title"
>
<text class="uv-action-sheet__header__title uv-line-1">{{title}}</text>
<view
class="uv-action-sheet__header__icon-wrap"
@tap.stop="cancel"
>
<uv-icon
name="close"
size="17"
color="#c8c9cc"
bold
></uv-icon>
</view>
</view>
<text
class="uv-action-sheet__description"
:style="[{
marginTop: `${title && description ? 0 : '18px'}`
}]"
v-if="description"
>{{description}}</text>
<slot>
<uv-line v-if="description"></uv-line>
<view class="uv-action-sheet__item-wrap">
<view v-for="(item, index) in actions" :key="index">
<!-- #ifdef MP -->
<button
class="uv-reset-button"
:openType="item.openType"
@getuserinfo="onGetUserInfo"
@contact="onContact"
@getphonenumber="onGetPhoneNumber"
@error="onError"
@launchapp="onLaunchApp"
@opensetting="onOpenSetting"
:lang="lang"
:session-from="sessionFrom"
:send-message-title="sendMessageTitle"
:send-message-path="sendMessagePath"
:send-message-img="sendMessageImg"
:show-message-card="showMessageCard"
:app-parameter="appParameter"
@tap="selectHandler(index)"
:hover-class="!item.disabled && !item.loading ? 'uv-action-sheet--hover' : ''"
>
<!-- #endif -->
<view
class="uv-action-sheet__item-wrap__item"
@tap.stop="selectHandler(index)"
:hover-class="!item.disabled && !item.loading ? 'uv-action-sheet--hover' : ''"
:hover-stay-time="150"
>
<template v-if="!item.loading">
<text
class="uv-action-sheet__item-wrap__item__name"
:style="[itemStyle(index)]"
>{{ item.name }}</text>
<text
v-if="item.subname"
class="uv-action-sheet__item-wrap__item__subname"
>{{ item.subname }}</text>
</template>
<uv-loading-icon
v-else
custom-class="van-action-sheet__loading"
size="18"
mode="circle"
/>
</view>
<!-- #ifdef MP -->
</button>
<!-- #endif -->
<uv-line v-if="index !== actions.length - 1"></uv-line>
</view>
</view>
</slot>
<uv-gap
bgColor="#eaeaec"
height="6"
v-if="cancelText"
></uv-gap>
<view hover-class="uv-action-sheet--hover">
<text
@touchmove.stop.prevent
:hover-stay-time="150"
v-if="cancelText"
class="uv-action-sheet__cancel-text"
@tap="cancel"
>{{cancelText}}</text>
</view>
</view>
</uv-popup>
</template>
<script>
import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
import button from '@/uni_modules/uv-ui-tools/libs/mixin/button.js'
import openType from '@/uni_modules/uv-ui-tools/libs/mixin/openType.js'
import props from './props.js';
/**
* ActionSheet 操作菜单
* @description 本组件用于从底部弹出一个操作菜单供用户选择并返回结果本组件功能类似于uni的uni.showActionSheetAPI配置更加灵活所有平台都表现一致
* @tutorial https://www.uvui.cn/components/actionSheet.html
* @property {Boolean} show 操作菜单是否展示 默认 false
* @property {String} title 操作菜单标题
* @property {String} description 选项上方的描述信息
* @property {Array<Object>} actions 按钮的文字数组见官方文档示例
* @property {String} cancelText 取消按钮的提示文字,不为空时显示按钮
* @property {Boolean} closeOnClickAction 点击某个菜单项时是否关闭弹窗 默认 true
* @property {Boolean} safeAreaInsetBottom 处理底部安全区 默认 true
* @property {String} openType 小程序的打开方式 (contact | launchApp | getUserInfo | openSetting getPhoneNumber error )
* @property {Boolean} closeOnClickOverlay 点击遮罩是否允许关闭 (默认 true )
* @property {String} lang 指定返回用户信息的语言zh_CN 简体中文zh_TW 繁体中文en 英文
* @property {String} sessionFrom 会话来源openType="contact"时有效
* @property {String} sendMessageTitle 会话内消息卡片标题openType="contact"时有效
* @property {String} sendMessagePath 会话内消息卡片点击跳转小程序路径openType="contact"时有效
* @property {String} sendMessageImg 会话内消息卡片图片openType="contact"时有效
* @property {Boolean} showMessageCard 是否显示会话内消息卡片设置此参数为 true用户进入客服会话会在右下角显示"可能要发送的小程序"提示用户点击后可以快速发送小程序消息openType="contact"时有效 默认 false
* @property {String} appParameter 打开 APP APP 传递的参数openType=launchApp 时有效
*
* @event {Function} select 点击ActionSheet列表项时触发
* @event {Function} close 点击取消按钮时触发
* @event {Function} getuserinfo 用户点击该按钮时会返回获取到的用户信息回调的 detail 数据与 wx.getUserInfo 返回的一致openType="getUserInfo"时有效
* @event {Function} contact 客服消息回调openType="contact"时有效
* @event {Function} getphonenumber 获取用户手机号回调openType="getPhoneNumber"时有效
* @event {Function} error 当使用开放能力时发生错误的回调openType="error"时有效
* @event {Function} launchapp 打开 APP 成功的回调openType="launchApp"时有效
* @event {Function} opensetting 在打开授权设置页后回调openType="openSetting"时有效
* @example <uv-action-sheet ref="actionSheet" :actions="list" :title="title" ></uv-action-sheet>
*/
export default {
name: "uv-action-sheet",
mixins: [openType, button, mpMixin , mixin, props],
emits: ['close', 'select'],
computed: {
//
itemStyle() {
return (index) => {
let style = {};
if (this.actions[index].color) style.color = this.actions[index].color
if (this.actions[index].fontSize) style.fontSize = this.$uv.addUnit(this.actions[index].fontSize)
//
if (this.actions[index].disabled) style.color = '#c0c4cc'
return style;
}
},
},
methods: {
open() {
this.$refs.popup.open();
},
close() {
this.$refs.popup.close();
},
popupChange(e) {
if(!e.show) this.$emit('close');
},
//
cancel() {
this.close();
},
selectHandler(index) {
const item = this.actions[index]
if (item && !item.disabled && !item.loading) {
this.$emit('select', item)
if (this.closeOnClickAction) {
this.close();
}
}
},
}
}
</script>
<style lang="scss" scoped>
$show-lines: 1;
$show-reset-button: 1;
@import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
@import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
$uv-action-sheet-reset-button-width:100% !default;
$uv-action-sheet-title-font-size: 16px !default;
$uv-action-sheet-title-padding: 12px 30px !default;
$uv-action-sheet-title-color: $uv-main-color !default;
$uv-action-sheet-header-icon-wrap-right:15px !default;
$uv-action-sheet-header-icon-wrap-top:15px !default;
$uv-action-sheet-description-font-size:13px !default;
$uv-action-sheet-description-color:14px !default;
$uv-action-sheet-description-margin: 18px 15px !default;
$uv-action-sheet-item-wrap-item-padding:15px !default;
$uv-action-sheet-item-wrap-name-font-size:16px !default;
$uv-action-sheet-item-wrap-subname-font-size:13px !default;
$uv-action-sheet-item-wrap-subname-color: #c0c4cc !default;
$uv-action-sheet-item-wrap-subname-margin-top:10px !default;
$uv-action-sheet-cancel-text-font-size:16px !default;
$uv-action-sheet-cancel-text-color:$uv-content-color !default;
$uv-action-sheet-cancel-text-font-size:15px !default;
$uv-action-sheet-cancel-text-hover-background-color:rgb(242, 243, 245) !default;
.uv-reset-button {
width: $uv-action-sheet-reset-button-width;
}
.uv-action-sheet {
text-align: center;
&__header {
position: relative;
padding: $uv-action-sheet-title-padding;
&__title {
font-size: $uv-action-sheet-title-font-size;
color: $uv-action-sheet-title-color;
font-weight: bold;
text-align: center;
}
&__icon-wrap {
position: absolute;
right: $uv-action-sheet-header-icon-wrap-right;
top: $uv-action-sheet-header-icon-wrap-top;
}
}
&__description {
font-size: $uv-action-sheet-description-font-size;
color: $uv-tips-color;
margin: $uv-action-sheet-description-margin;
text-align: center;
}
&__item-wrap {
&__item {
padding: $uv-action-sheet-item-wrap-item-padding;
@include flex;
align-items: center;
justify-content: center;
flex-direction: column;
&__name {
font-size: $uv-action-sheet-item-wrap-name-font-size;
color: $uv-main-color;
text-align: center;
}
&__subname {
font-size: $uv-action-sheet-item-wrap-subname-font-size;
color: $uv-action-sheet-item-wrap-subname-color;
margin-top: $uv-action-sheet-item-wrap-subname-margin-top;
text-align: center;
}
}
}
&__cancel-text {
font-size: $uv-action-sheet-cancel-text-font-size;
color: $uv-action-sheet-cancel-text-color;
text-align: center;
padding: $uv-action-sheet-cancel-text-font-size;
}
&--hover {
background-color: $uv-action-sheet-cancel-text-hover-background-color;
}
}
</style>

+ 92
- 0
uni_modules/uv-action-sheet/package.json View File

@ -0,0 +1,92 @@
{
"id": "uv-action-sheet",
"displayName": "uv-action-sheet 底部操作菜单 全面兼容小程序、nvue、vue2、vue3等多端",
"version": "1.0.2",
"description": "该组件用于从底部弹出一个操作菜单,供用户选择并返回结果。本组件功能类似于uni的uni.showActionSheet API,配置更加灵活,所有平台都表现一致。",
"keywords": [
"action-sheet",
"uvui",
"uv-ui",
"操作菜单",
"菜单选择"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [
"uv-ui-tools",
"uv-popup",
"uv-icon",
"uv-line",
"uv-loading-icon",
"uv-gap"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"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",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

+ 13
- 0
uni_modules/uv-action-sheet/readme.md View File

@ -0,0 +1,13 @@
## ActionSheet 操作菜单
> **组件名:uv-action-sheet**
本组件用于从底部弹出一个操作菜单,供用户选择并返回结果。
本组件功能类似于uni的uni.showActionSheet API,配置更加灵活,所有平台都表现一致。
### <a href="https://www.uvui.cn/components/actionSheet.html" target="_blank">查看文档</a>
### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a><a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

+ 10
- 0
uni_modules/uv-album/changelog.md View File

@ -0,0 +1,10 @@
## 1.0.4(2023-12-06)
1. 阻止事件冒泡处理
## 1.0.3(2023-10-23)
1. 修复报错的BUG
## 1.0.2(2023-10-23)
1. 修复设置singleSize、multipleSize、space等值带单位,存在不显示的BUG
## 1.0.1(2023-09-13)
1. 添加依赖
## 1.0.0(2023-08-30)
1. 新增uv-album相册组件

+ 312
- 0
uni_modules/uv-album/components/uv-album/uv-album.vue View File

@ -0,0 +1,312 @@
<template>
<view class="uv-album">
<view
class="uv-album__row"
ref="uv-album__row"
v-for="(arr, index) in showUrls"
:forComputedUse="albumWidth"
:key="index"
>
<view
class="uv-album__row__wrapper"
v-for="(item, index1) in arr"
:key="index1"
:style="[imageStyle(index + 1, index1 + 1)]"
@tap.stop="previewFullImage ? onPreviewTap(getSrc(item)) : ''"
>
<image
:src="getSrc(item)"
:mode="
urls.length === 1
? imageHeight > 0
? singleMode
: 'widthFix'
: multipleMode
"
:style="[
{
width: imageWidth,
height: imageHeight
}
]"
></image>
<view
v-if="
showMore &&
urls.length > rowCount * showUrls.length &&
index === showUrls.length - 1 &&
index1 === showUrls[showUrls.length - 1].length - 1
"
class="uv-album__row__wrapper__text"
>
<uv-text
:text="`+${urls.length - maxCount}`"
color="#fff"
:size="$uv.getPx(multipleSize) * 0.3"
align="center"
customStyle="justify-content: center"
></uv-text>
</view>
</view>
</view>
</view>
</template>
<script>
import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
// #ifdef APP-NVUE
// weexKPIdom
const dom = uni.requireNativePlugin('dom')
// #endif
/**
* Album 相册
* @description 本组件提供一个类似相册的功能让开发者开发起来更加得心应手减少重复的模板代码
* @tutorial https://www.uvui.cn/components/album.html
* @property {Array} urls 图片地址列表 Array<String>|Array<Object>形式
* @property {String} keyName 指定从数组的对象元素中读取哪个属性作为图片地址
* @property {String | Number} singleSize 单图时图片长边的长度 默认 180
* @property {String | Number} multipleSize 多图时图片边长 默认 70
* @property {String | Number} space 多图时图片水平和垂直之间的间隔 默认 6
* @property {String} singleMode 单图时图片缩放裁剪的模式 默认 'scaleToFill'
* @property {String} multipleMode 多图时图片缩放裁剪的模式 默认 'aspectFill'
* @property {String | Number} maxCount 取消按钮的提示文字 默认 9
* @property {Boolean} previewFullImage 是否可以预览图片 默认 true
* @property {String | Number} rowCount 每行展示图片数量如设置singleSize和multipleSize将会无效 默认 3
* @property {Boolean} showMore 超出maxCount时是否显示查看更多的提示 默认 true
*
* @event {Function} albumWidth 某些特殊的情况下需要让文字与相册的宽度相等这里事件的形式对外发送 回调参数 width
* @example <uv-album :urls="urls2" @albumWidth="width => albumWidth = width" multipleSize="68" ></uv-album>
*/
export default {
name: 'uv-album',
mixins: [mpMixin, mixin],
emits: ['albumWidth'],
props: {
// Array<String>|Array<Object>
urls: {
type: Array,
default: () => []
},
//
keyName: {
type: String,
default: ''
},
//
singleSize: {
type: [String, Number],
default: 180
},
//
multipleSize: {
type: [String, Number],
default: 70
},
//
space: {
type: [String, Number],
default: 6
},
//
singleMode: {
type: String,
default: 'scaleToFill'
},
//
multipleMode: {
type: String,
default: 'aspectFill'
},
//
maxCount: {
type: [String, Number],
default: 9
},
//
previewFullImage: {
type: Boolean,
default: true
},
// singleSizemultipleSize
rowCount: {
type: [String, Number],
default: 3
},
// maxCount
showMore: {
type: Boolean,
default: true
},
...uni.$uv?.props?.album
},
data() {
return {
//
singleWidth: 0,
//
singleHeight: 0,
//
singlePercent: 0.6
}
},
watch: {
urls: {
immediate: true,
handler(newVal) {
if (newVal.length === 1) {
this.getImageRect()
}
}
}
},
computed: {
imageStyle() {
return (index1, index2) => {
const { space, rowCount, multipleSize, urls } = this;
const rowLen = this.showUrls.length;
const allLen = this.urls.length;
const style = {
marginRight: this.$uv.addUnit(space),
marginBottom: this.$uv.addUnit(space)
}
//
if (index1 === rowLen) style.marginBottom = 0
//
if (
index2 === rowCount ||
(index1 === rowLen &&
index2 === this.showUrls[index1 - 1].length)
)
style.marginRight = 0
return style
}
},
//
showUrls() {
const arr = []
this.urls.map((item, index) => {
//
if (index + 1 <= this.maxCount) {
//
const itemIndex = Math.floor(index / this.rowCount)
//
if (!arr[itemIndex]) {
arr[itemIndex] = []
}
arr[itemIndex].push(item)
}
})
return arr
},
imageWidth() {
return this.$uv.addUnit(
this.urls.length === 1 ? this.singleWidth : this.multipleSize
)
},
imageHeight() {
return this.$uv.addUnit(
this.urls.length === 1 ? this.singleHeight : this.multipleSize
)
},
// computedurls
//
albumWidth() {
let width = 0
if (this.urls.length === 1) {
width = this.singleWidth
} else {
width =
this.showUrls[0].length * this.$uv.getPx(this.multipleSize) +
this.$uv.getPx(this.space) * (this.showUrls[0].length - 1)
}
this.$emit('albumWidth', width)
return width
}
},
methods: {
//
onPreviewTap(url) {
const urls = this.urls.map((item) => {
return this.getSrc(item)
})
uni.previewImage({
current: url,
urls
})
},
//
getSrc(item) {
return this.$uv.test.object(item) ?
(this.keyName && item[this.keyName]) || item.src :
item
},
//
// download
// (singlePercent)
getImageRect() {
const src = this.getSrc(this.urls[0])
uni.getImageInfo({
src,
success: (res) => {
//
const isHorizotal = res.width >= res.height
this.singleWidth = isHorizotal ?
this.singleSize :
(res.width / res.height) * this.$uv.getPx(this.singleSize)
this.singleHeight = !isHorizotal ?
this.singleSize :
(res.height / res.width) * this.singleWidth
},
fail: () => {
this.getComponentWidth()
}
})
},
//
async getComponentWidth() {
// dom
await this.$uv.sleep(30)
// #ifndef APP-NVUE
this.$uGetRect('.uv-album__row').then((size) => {
this.singleWidth = size.width * this.singlePercent
})
// #endif
// #ifdef APP-NVUE
// ref="uv-album__row"forthis.$refs['uv-album__row']
const ref = this.$refs['uv-album__row'][0]
ref &&
dom.getComponentRect(ref, (res) => {
this.singleWidth = res.size.width * this.singlePercent
})
// #endif
}
}
}
</script>
<style lang="scss" scoped>
@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
.uv-album {
@include flex(column);
&__row {
@include flex(row);
flex-wrap: wrap;
&__wrapper {
position: relative;
&__text {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.3);
@include flex(row);
justify-content: center;
align-items: center;
}
}
}
}
</style>

+ 88
- 0
uni_modules/uv-album/package.json View File

@ -0,0 +1,88 @@
{
"id": "uv-album",
"displayName": "uv-album 相册 全面兼容vue3+2、app、h5、小程序等多端",
"version": "1.0.4",
"description": "本组件提供一个类似相册的功能,让开发者开发起来更加得心应手,功能齐全,灵活配置可以,开箱即用。减少重复的模板代码",
"keywords": [
"album",
"uv-ui",
"uvui",
"相册",
"图片"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [
"uv-ui-tools",
"uv-text"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"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",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

+ 21
- 0
uni_modules/uv-album/readme.md View File

@ -0,0 +1,21 @@
# Album 相册
> **组件名:uv-album**
本组件提供一个类似相册的功能,让开发者开发起来更加得心应手。
功能齐全,灵活配置可以,开箱即用。减少重复的模板代码。
# <a href="https://www.uvui.cn/components/album.html" target="_blank">查看文档</a>
## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small>
### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
</a>
#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

+ 7
- 0
uni_modules/uv-alert/changelog.md View File

@ -0,0 +1,7 @@
## 1.0.2(2023-06-01)
1. 修复点击触发两次实践的BUG
## 1.0.1(2023-05-16)
1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
2. 优化部分功能
## 1.0.0(2023-05-10)
uv-alert 警告提示组件

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save