From 2a68a3cbdc02807bc60d9445f59cc8cab2a31ac3 Mon Sep 17 00:00:00 2001 From: "liujw155@outlook.com" Date: Wed, 28 May 2025 18:28:27 +0800 Subject: [PATCH] init api logic && scss --- envs/.env.development | 3 +- envs/.env.production | 3 +- package.json | 1 + src/api/index.js | 5 +++ src/api/loading.js | 37 ++++++++++++++++ src/api/server.js | 89 ++++++++++++++++++++++++++++++++++++++ src/api/upload.js | 37 ++++++++++++++++ src/pages/index/index.vue | 19 +++++++-- src/styles/iconfonts.scss | 5 +++ src/styles/methods.scss | 101 +++++++++++++++++++++++++++++++++++++++++++ src/uni.scss | 106 ++-------------------------------------------- src/utils/polish.js | 100 +++++++++++++++++++++++++++++++++++++++++++ 12 files changed, 398 insertions(+), 108 deletions(-) create mode 100644 src/api/index.js create mode 100644 src/api/loading.js create mode 100644 src/api/server.js create mode 100644 src/api/upload.js create mode 100644 src/styles/iconfonts.scss create mode 100644 src/styles/methods.scss create mode 100644 src/utils/polish.js diff --git a/envs/.env.development b/envs/.env.development index 6233ab3..75fe5b2 100644 --- a/envs/.env.development +++ b/envs/.env.development @@ -1,4 +1,5 @@ +# 测试环境配置文件 # 接口地址 -VITE_API_BASE=https://api.test.com +VITE_API_BASE=https://test.ouxuanzhineng.cn # 头条小程序 appid VITE_TOUTIAO_APPID=ttc690b02d599a28ee01 \ No newline at end of file diff --git a/envs/.env.production b/envs/.env.production index a855f8a..c97c703 100644 --- a/envs/.env.production +++ b/envs/.env.production @@ -1,4 +1,5 @@ +# 生成环境配置文件 # 接口地址 -VITE_API_BASE=https://api.formal.com +VITE_API_BASE=https://miniapp.ouxuanzhineng.cn # 头条小程序 appid VITE_TOUTIAO_APPID=ttc690b02d599a28ee01-p \ No newline at end of file diff --git a/package.json b/package.json index 2716b4d..cdab210 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "@dcloudio/uni-mp-xhs": "3.0.0-4060420250429001", "@dcloudio/uni-quickapp-webview": "3.0.0-4060420250429001", "jsonc-parser": "^3.3.1", + "uni-ajax": "^2.5.1", "vue": "^3.4.21", "vue-i18n": "^9.1.9" }, diff --git a/src/api/index.js b/src/api/index.js new file mode 100644 index 0000000..a190c86 --- /dev/null +++ b/src/api/index.js @@ -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 }); // 登录 diff --git a/src/api/loading.js b/src/api/loading.js new file mode 100644 index 0000000..310b6d6 --- /dev/null +++ b/src/api/loading.js @@ -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 \ No newline at end of file diff --git a/src/api/server.js b/src/api/server.js new file mode 100644 index 0000000..abdd9d8 --- /dev/null +++ b/src/api/server.js @@ -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; \ No newline at end of file diff --git a/src/api/upload.js b/src/api/upload.js new file mode 100644 index 0000000..0dcf9da --- /dev/null +++ b/src/api/upload.js @@ -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) + }) +} diff --git a/src/pages/index/index.vue b/src/pages/index/index.vue index c5a437a..e3ce0d1 100644 --- a/src/pages/index/index.vue +++ b/src/pages/index/index.vue @@ -2,19 +2,30 @@ import { onLoad } from '@dcloudio/uni-app'; import { deepClone } from "@/utils"; import { accAdd } from "@/utils/calculation"; +import { stadiumFind } from "@/api"; + onLoad(() => { - console.log('deepClone', deepClone({ a: 1, b: { c: 2 } })); - console.log('accAdd', accAdd(0.1, 0.2)); + console.log('deepClone', deepClone({ a: 1, b: { c: 2 } })); + console.log('accAdd', accAdd(0.1, 0.2)); + stadiumFind({ data: { brand_id: 63 }, loading: '加载...' }) + .then(res=>{ + console.log('stadiumFind', res); + }) }); diff --git a/src/styles/iconfonts.scss b/src/styles/iconfonts.scss new file mode 100644 index 0000000..d4ebd4e --- /dev/null +++ b/src/styles/iconfonts.scss @@ -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'); +} \ No newline at end of file diff --git a/src/styles/methods.scss b/src/styles/methods.scss new file mode 100644 index 0000000..b1cd365 --- /dev/null +++ b/src/styles/methods.scss @@ -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); +} diff --git a/src/uni.scss b/src/uni.scss index 78fed93..2d36fd1 100644 --- a/src/uni.scss +++ b/src/uni.scss @@ -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; \ No newline at end of file diff --git a/src/utils/polish.js b/src/utils/polish.js new file mode 100644 index 0000000..86f6a91 --- /dev/null +++ b/src/utils/polish.js @@ -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 +} \ No newline at end of file