12 changed files with 398 additions and 108 deletions
-
3envs/.env.development
-
3envs/.env.production
-
1package.json
-
5src/api/index.js
-
37src/api/loading.js
-
89src/api/server.js
-
37src/api/upload.js
-
19src/pages/index/index.vue
-
5src/styles/iconfonts.scss
-
101src/styles/methods.scss
-
106src/uni.scss
-
100src/utils/polish.js
@ -1,4 +1,5 @@ |
|||
# 测试环境配置文件 |
|||
# 接口地址 |
|||
VITE_API_BASE=https://api.test.com |
|||
VITE_API_BASE=https://test.ouxuanzhineng.cn |
|||
# 头条小程序 appid |
|||
VITE_TOUTIAO_APPID=ttc690b02d599a28ee01 |
@ -1,4 +1,5 @@ |
|||
# 生成环境配置文件 |
|||
# 接口地址 |
|||
VITE_API_BASE=https://api.formal.com |
|||
VITE_API_BASE=https://miniapp.ouxuanzhineng.cn |
|||
# 头条小程序 appid |
|||
VITE_TOUTIAO_APPID=ttc690b02d599a28ee01-p |
@ -0,0 +1,5 @@ |
|||
import server from './server'; |
|||
|
|||
// 公共
|
|||
export const login = params => server.post({ url: '/Admino/AdminUser/login', ...params }); // 登录
|
|||
export const stadiumFind = params => server.post({ url: '/stadium/find', ...params }); // 登录
|
@ -0,0 +1,37 @@ |
|||
class Loading { |
|||
times = 0 |
|||
timer = null |
|||
// https://developers.weixin.qq.com/community/develop/doc/0008e440e6cb58d4050a4b7e451c00?_at=1619083932616
|
|||
// showLoading 与 hideLoading 尽量成对出现,不然会报错
|
|||
// => (in promise) MiniProgramError{"errMsg":"hideLoading:fail toast can't be found"}Object
|
|||
isLoading = false |
|||
show(loading) { |
|||
if (loading === false) return // 如果传入的 loading 属性为 false,则不处理
|
|||
clearTimeout(this.timer) // 如果有多个请求同时进行,则用最后请求的 loading
|
|||
|
|||
this.times++ |
|||
this.timer = setTimeout(() => { |
|||
this.isLoading = true; |
|||
uni.showLoading({ |
|||
title: loading ?? '加载中', |
|||
mask: true |
|||
}) |
|||
}, 200) // 设定延迟,如果请求超过 200ms 才显示 loading
|
|||
} |
|||
|
|||
hide(loading) { |
|||
if (loading === false) return |
|||
|
|||
this.times-- |
|||
if (this.times <= 0) { |
|||
clearTimeout(this.timer) |
|||
if(this.isLoading){ |
|||
uni.hideLoading(); |
|||
this.isLoading = false; |
|||
} |
|||
this.times = 0 |
|||
} |
|||
} |
|||
} |
|||
|
|||
export default Loading |
@ -0,0 +1,89 @@ |
|||
// 引入 uni-ajax 模块
|
|||
// https://uniajax.ponjs.com/
|
|||
import ajax from 'uni-ajax'; |
|||
import Loading from './loading' |
|||
import { showModal, routeTo } from '@/utils/polish'; |
|||
const loading = new Loading(); |
|||
|
|||
// 定义 pending 状态的 Promise,用于避免进入 catch 回调
|
|||
const pending = new Promise(() => {}) |
|||
|
|||
// 创建请求实例
|
|||
const server = ajax.create({ |
|||
// 初始配置
|
|||
baseURL: __API__, |
|||
loading: 'loading...', |
|||
}) |
|||
|
|||
// 添加请求拦截器
|
|||
server.interceptors.request.use( |
|||
config => { |
|||
let _token = uni.getStorageSync('token'); |
|||
if(_token)config.data.ustoken = _token; |
|||
loading.show(config.loading); |
|||
return config; |
|||
}, |
|||
error => { |
|||
console.error('server.interceptors.request error -> ', error); |
|||
return Promise.reject(error); |
|||
} |
|||
) |
|||
|
|||
// 添加响应拦截器
|
|||
server.interceptors.response.use( |
|||
response => { |
|||
loading.hide(response.config.loading); |
|||
if(response?.data?.code === 0)return formatResponse(response); |
|||
console.error(`get ${response?.config?.url} err -> `, response) |
|||
// return errorHandle({
|
|||
// Catch: response?.config?.catch || false,
|
|||
// content: response?.data?.smsg ?? '请求失败',
|
|||
// data: response,
|
|||
// success: () => {
|
|||
// // 登录超时
|
|||
// if(response?.data?.error === '201')loginHandle();
|
|||
// if(response?.data?.error === '704'&&response?.data?.smsg === 'ustoken不能为空')loginHandle();
|
|||
// }
|
|||
// })
|
|||
}, |
|||
error => { |
|||
loading.hide(error.config.loading); |
|||
console.error('server.interceptors.response error -> ', error); |
|||
// return errorHandle({
|
|||
// Catch: error?.config?.catch || false,
|
|||
// // 404 拦截 data 会存在错误信息
|
|||
// content: error?.data ?? error?.errMsg ?? '请求失败',
|
|||
// data: error,
|
|||
// })
|
|||
} |
|||
) |
|||
|
|||
function formatResponse(response){ |
|||
let _data = response?.data?.data || null; |
|||
|
|||
return _data; |
|||
} |
|||
|
|||
/** |
|||
* 在有返回错误的拦截器里返回 pending 状态的 Promise |
|||
* |
|||
* 传值 config.catch 判断是否需要返回错误,如果是 true 返回错误信息,否则不返回。 |
|||
*/ |
|||
function errorHandle({ Catch = true, content = '', data = {}, success }) { |
|||
console.error(`get ${data?.config?.url} err -> `, data) |
|||
showModal({ |
|||
content: content || '请求失败', |
|||
success: success |
|||
}) |
|||
|
|||
return Catch ? Promise.reject(data) : pending; |
|||
} |
|||
|
|||
function loginHandle() { |
|||
uni.removeStorageSync('token'); |
|||
routeTo(`/pages/login/index`, 'rL'); |
|||
} |
|||
|
|||
|
|||
// 导出 create 创建后的实例
|
|||
export default server; |
@ -0,0 +1,37 @@ |
|||
/** |
|||
* @param { Object } options |
|||
* @param { String } options.url 请求地址 |
|||
* @param { String } options.api 请求接口 |
|||
* @param { String } options.filePath 文件路径 |
|||
* @param { Function } options.onProgressCallBack 上传进度回调 |
|||
* @param { Object } options.formData 请求参数 |
|||
* @returns { Promise } |
|||
* |
|||
* */ |
|||
export const uploadFile = ({ url = __API__, api = '/Admino/AdminUser/uploadImgs', filePath, onProgress, formData = {} }) => { |
|||
return new Promise((rs,rj)=>{ |
|||
let uploadTask = wx.uploadFile({ |
|||
url: url + api, |
|||
filePath, |
|||
formData, |
|||
name:'file', |
|||
success: res=>{ |
|||
if (res.statusCode >= 200 && res.statusCode < 300) { |
|||
let _json = res?.data || '' |
|||
let _data = JSON.parse(_json); |
|||
if(_data?.scode){ |
|||
rs(_data?.data); |
|||
}else{ |
|||
rj(_data) |
|||
} |
|||
|
|||
} else { |
|||
rj(res) |
|||
} |
|||
}, |
|||
fail: rj |
|||
}) |
|||
|
|||
uploadTask.onProgressUpdate(onProgress) |
|||
}) |
|||
} |
@ -0,0 +1,5 @@ |
|||
// iconfont 圆圈白勾成功 () |
|||
@mixin circleSuccess { |
|||
font-family: 'circleSuccess'; |
|||
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAALgAAsAAAAABqwAAAKRAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACCcAqBJIE5ATYCJAMICwYABCAFhGcHMxv4BcgekiSBOox5QEIkAShQPHytoe/v7YVZIWoiB6jiSGV8HMhwna3ylUBCd25yLrOx2ixQVN+NkAtJ2y+nGSl0wOqFHPB5QMXkyO+mdp/Mok//czm+uwLaIB/A25PG2tSmAwuwlx1GYy/CXkwN0yu/wgBeI9BgURbPnaKKBhBwx1IdhPAmqoMsPe5kkQX+3CHLMFwn1AydmsUXJek5waf/fvyzHP4kZUFkHd0WCmD3Zyl7FI/VMMU3T8sVwN0SShSYBWTirDtwwEs0Ubw0GLwNw9ws+FlWbqsXh9YI9teJ1CYwBLnPxNP8R01NIMfaJcDJpCak/iB2vOnuTjFHmu/vmRONt7fM4YabG29bU8NpZeHY7eb7+vvkh+8iNVw07OasuIekLkc7M/XOtDu6LiDLsVL9dqbLcdHwPNf93EJP+8d6aVpK6U9mOQ3PRfmu3ZbY//vOL+9IqpYzdmwAKhF68FY8/c/9PPwfs7sDV379GzP4fja474XDTNSd0BD8vQYvs6oi80RSRkdqIFma5EtokG4qwtLhbaqy8y4joU7nvZpqDWZQqDOMZPI0lBotQ406q9BgRuHsRp1MXiLXY9wYgNBmEZIWX6HQ5gTJ5Hso9fiEGm3+ocFFNC/XaDz4fIJYigTIDUKZN3SsFNQTrUNir8qSOm8TIrYM3TA2KqYcLkY6InOMsPvEOEoxxMTQYBHdDamqAU1iOBFPoyRKzbToaNz0kSje0EA+AmFRiADiDIJkPIMOW3xa/L06iKiXikUGIk0BwiZD4lCsKDEDkGKpPqjkQVax9RHFoSgMwggIaaAi3glRqQyQ2dzJCeFRUaQJPlOaaImHh0qjtpdrf4/DKhCqFLJJof5KQrooGloDAAAAAA==') format('woff2'); |
|||
} |
@ -0,0 +1,101 @@ |
|||
@charset "UTF-8"; |
|||
/* 水平flex */ |
|||
@mixin ctf($justtify: flex-start){ |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: $justtify; |
|||
} |
|||
|
|||
/* 文字样式 */ |
|||
@mixin flcw($size: 28upx, $height: 40upx, $color: #333, $weight: 400){ |
|||
font-size: $size; |
|||
line-height: $height; |
|||
color: $color; |
|||
font-weight: $weight; |
|||
} |
|||
|
|||
@mixin tHide($line: 1) { |
|||
display: -webkit-box; |
|||
word-break: break-all; |
|||
text-overflow: ellipsis; |
|||
overflow: hidden; |
|||
-webkit-box-orient: vertical; |
|||
-webkit-line-clamp:$line; |
|||
} |
|||
|
|||
@mixin isPd($height: 0){ |
|||
padding-bottom: $height; |
|||
padding-bottom: calc( $height + constant(safe-area-inset-bottom)); /* 兼容 iOS < 11.2 */ |
|||
padding-bottom: calc( $height + env(safe-area-inset-bottom)); /* 兼容 iOS >= 11.2 */ |
|||
} |
|||
|
|||
@mixin fBot{ |
|||
position: fixed; |
|||
left: 0; |
|||
bottom: 0; |
|||
width: 100%; |
|||
padding: 10upx 20upx; |
|||
@include isPd(10upx); |
|||
} |
|||
|
|||
@mixin cirBtn($color: #FF873D){ |
|||
text-align: center; |
|||
border-radius: 56upx; |
|||
background: $color; |
|||
@include flcw(32upx, 92upx, #fff, 500); |
|||
} |
|||
|
|||
@mixin clearBtn{ |
|||
margin: 0; |
|||
padding: 0; |
|||
line-height: 0; |
|||
background-color: transparent; |
|||
border-radius: 0; |
|||
&::after{ |
|||
position: unset !important; |
|||
border: unset; |
|||
} |
|||
} |
|||
|
|||
@mixin FixedLineBtn($color: #FF873D) { |
|||
position: fixed; |
|||
left: 0; |
|||
bottom: 0; |
|||
width: 100%; |
|||
padding-top: 18upx; |
|||
padding-bottom: 20upx; |
|||
padding-bottom: calc( 20upx + constant(safe-area-inset-bottom)); /* 兼容 iOS < 11.2 */ |
|||
padding-bottom: calc( 20upx + env(safe-area-inset-bottom)); /* 兼容 iOS >= 11.2 */ |
|||
border-top: 2upx solid #D8D8D8; |
|||
background-color: #fff; |
|||
>view{ |
|||
margin: 0 auto; |
|||
width: 702upx; |
|||
height: 88upx; |
|||
line-height: 88upx; |
|||
text-align: center; |
|||
font-size: 32upx; |
|||
border-radius: 44upx; |
|||
color: #fff; |
|||
background-color: $color; |
|||
} |
|||
} |
|||
|
|||
@mixin lineBtn($color: #FF873D){ |
|||
margin: 0 auto; |
|||
width: 702upx; |
|||
text-align: center; |
|||
border-radius: 44upx; |
|||
background-color: $color; |
|||
@include flcw(32upx, 88upx, #fff, 500); |
|||
} |
|||
|
|||
@mixin fixedMask { |
|||
position: fixed; |
|||
left: 0; |
|||
top: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
width: 100%; |
|||
background-color: rgba(0, 0, 0, .5); |
|||
} |
@ -1,103 +1,5 @@ |
|||
/* uniapp 全局样式 */ |
|||
$mColor: #009874; |
|||
|
|||
/* 水平flex */ |
|||
@mixin ctf($justtify: flex-start){ |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: $justtify; |
|||
} |
|||
|
|||
/* 文字样式 */ |
|||
@mixin flcw($size: 28upx, $height: 40upx, $color: #333, $weight: 400){ |
|||
font-size: $size; |
|||
line-height: $height; |
|||
color: $color; |
|||
font-weight: $weight; |
|||
} |
|||
|
|||
@mixin tHide($line: 1) { |
|||
display: -webkit-box; |
|||
word-break: break-all; |
|||
text-overflow: ellipsis; |
|||
overflow: hidden; |
|||
-webkit-box-orient: vertical; |
|||
-webkit-line-clamp:$line; |
|||
} |
|||
|
|||
@mixin isPd($height: 0){ |
|||
padding-bottom: $height; |
|||
padding-bottom: calc( $height + constant(safe-area-inset-bottom)); /* 兼容 iOS < 11.2 */ |
|||
padding-bottom: calc( $height + env(safe-area-inset-bottom)); /* 兼容 iOS >= 11.2 */ |
|||
} |
|||
|
|||
@mixin fBot{ |
|||
position: fixed; |
|||
left: 0; |
|||
bottom: 0; |
|||
width: 100%; |
|||
padding: 10upx 20upx; |
|||
@include isPd(10upx); |
|||
} |
|||
@use '@/styles/methods.scss' as *; |
|||
@use '@/styles/iconfonts.scss' as *; |
|||
|
|||
@mixin cirBtn($color: #FF873D){ |
|||
text-align: center; |
|||
border-radius: 56upx; |
|||
background: $color; |
|||
@include flcw(32upx, 92upx, #fff, 500); |
|||
} |
|||
|
|||
@mixin clearBtn{ |
|||
margin: 0; |
|||
padding: 0; |
|||
line-height: 0; |
|||
background-color: transparent; |
|||
border-radius: 0; |
|||
&::after{ |
|||
position: unset !important; |
|||
border: unset; |
|||
} |
|||
} |
|||
|
|||
@mixin FixedLineBtn($color: #FF873D) { |
|||
position: fixed; |
|||
left: 0; |
|||
bottom: 0; |
|||
width: 100%; |
|||
padding-top: 18upx; |
|||
padding-bottom: 20upx; |
|||
padding-bottom: calc( 20upx + constant(safe-area-inset-bottom)); /* 兼容 iOS < 11.2 */ |
|||
padding-bottom: calc( 20upx + env(safe-area-inset-bottom)); /* 兼容 iOS >= 11.2 */ |
|||
border-top: 2upx solid #D8D8D8; |
|||
background-color: #fff; |
|||
>view{ |
|||
margin: 0 auto; |
|||
width: 702upx; |
|||
height: 88upx; |
|||
line-height: 88upx; |
|||
text-align: center; |
|||
font-size: 32upx; |
|||
border-radius: 44upx; |
|||
color: #fff; |
|||
background-color: $color; |
|||
} |
|||
} |
|||
|
|||
@mixin lineBtn($color: #FF873D){ |
|||
margin: 0 auto; |
|||
width: 702upx; |
|||
text-align: center; |
|||
border-radius: 44upx; |
|||
background-color: $color; |
|||
@include flcw(32upx, 88upx, #fff, 500); |
|||
} |
|||
|
|||
@mixin fixedMask { |
|||
position: fixed; |
|||
left: 0; |
|||
top: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
width: 100%; |
|||
background-color: rgba(0, 0, 0, .5); |
|||
} |
|||
/* uniapp 全局样式 */ |
|||
$mColor: skyblue; |
@ -0,0 +1,100 @@ |
|||
// 路由跳转
|
|||
export const routeTo = (url = '', type = 'nT', delta = 1) => { |
|||
if(!url)return uni.navigateBack({ delta }); |
|||
switch(type){ |
|||
case 'nT': uni.navigateTo({ url, fail: _=> uni.switchTab({ url }) }); |
|||
break |
|||
case 'rT': uni.redirectTo({ url, fail: _=> uni.switchTab({ url }) }); |
|||
break |
|||
case 'rL': uni.reLaunch({ url, fail: _=> uni.switchTab({ url }) }); |
|||
break |
|||
case 'sT': uni.switchTab({ url, fail: _=> uni.navigateTo({ url }) }); |
|||
break |
|||
case 'nB': uni.navigateBack({ delta }); |
|||
break |
|||
default: uni.navigateBack({ delta: 1 }); |
|||
break |
|||
} |
|||
} |
|||
|
|||
export const showModal = ({ |
|||
title = '提示', |
|||
content = '', |
|||
showCancel = false, |
|||
cancelText = '取消', |
|||
confirmText = '确定', |
|||
confirmColor = '#10367A', |
|||
success, |
|||
fail, |
|||
complete |
|||
}) => { |
|||
uni.showModal({ |
|||
title, |
|||
content, |
|||
showCancel, |
|||
cancelText, |
|||
confirmColor, |
|||
confirmText, |
|||
success, |
|||
fail, |
|||
complete |
|||
}) |
|||
} |
|||
|
|||
export function showNone(txt, duration=1500){ |
|||
uni.showToast({ |
|||
mask: true, |
|||
title: txt, |
|||
icon: 'none', |
|||
duration, |
|||
}) |
|||
} |
|||
|
|||
export function debounce(func, wait, immediate) { |
|||
let timeout, args, context, timestamp, result; |
|||
const later = function() { |
|||
// 据上一次触发时间间隔
|
|||
const last = +new Date() - timestamp; |
|||
// 上次被包装函数被调用时间间隔last小于设定时间间隔wait
|
|||
if (last < wait && last > 0) { |
|||
timeout = setTimeout(later, wait - last); |
|||
} else { |
|||
timeout = null; |
|||
// 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
|
|||
if (!immediate) { |
|||
result = func.apply(context, args); |
|||
if (!timeout) context = args = null; |
|||
} |
|||
} |
|||
} |
|||
return function(...args) { |
|||
context = this; |
|||
timestamp = +new Date(); |
|||
const callNow = immediate && !timeout; |
|||
// 如果延时不存在,重新设定延时
|
|||
if (!timeout) timeout = setTimeout(later, wait); |
|||
if (callNow) { |
|||
result = func.apply(context, args); |
|||
context = args = null; |
|||
} |
|||
return result; |
|||
} |
|||
} |
|||
|
|||
export function jsonStr(data){ |
|||
return encodeURIComponent(JSON.stringify(data)) |
|||
} |
|||
|
|||
export function jsonPar(json){ |
|||
return JSON.parse(decodeURIComponent(decodeURIComponent(json))) |
|||
} |
|||
|
|||
|
|||
export default { |
|||
routeTo, |
|||
showModal, |
|||
showNone, |
|||
debounce, |
|||
jsonStr, |
|||
jsonPar |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue