绘制首页和配置tabbar
@ -6,143 +6,143 @@ import { loadEnv } from 'vite'
|
|||||||
|
|
||||||
// 手动解析命令行参数获取 mode
|
// 手动解析命令行参数获取 mode
|
||||||
function getMode() {
|
function getMode() {
|
||||||
const args = process.argv.slice(2)
|
const args = process.argv.slice(2)
|
||||||
const modeFlagIndex = args.findIndex(arg => arg === '--mode')
|
const modeFlagIndex = args.findIndex(arg => arg === '--mode')
|
||||||
return modeFlagIndex !== -1 ? args[modeFlagIndex + 1] : args[0] === 'build' ? 'production' : 'development' // 默认 development
|
return modeFlagIndex !== -1 ? args[modeFlagIndex + 1] : args[0] === 'build' ? 'production' : 'development' // 默认 development
|
||||||
}
|
}
|
||||||
// 获取环境变量的范例
|
// 获取环境变量的范例
|
||||||
const env = loadEnv(getMode(), path.resolve(process.cwd(), 'env'))
|
const env = loadEnv(getMode(), path.resolve(process.cwd(), 'env'))
|
||||||
const {
|
const {
|
||||||
VITE_APP_TITLE,
|
VITE_APP_TITLE,
|
||||||
VITE_UNI_APPID,
|
VITE_UNI_APPID,
|
||||||
VITE_WX_APPID,
|
VITE_WX_APPID,
|
||||||
VITE_APP_PUBLIC_BASE,
|
VITE_APP_PUBLIC_BASE,
|
||||||
VITE_FALLBACK_LOCALE,
|
VITE_FALLBACK_LOCALE,
|
||||||
} = env
|
} = env
|
||||||
|
|
||||||
export default defineManifestConfig({
|
export default defineManifestConfig({
|
||||||
'name': VITE_APP_TITLE,
|
'name': VITE_APP_TITLE,
|
||||||
'appid': VITE_UNI_APPID,
|
'appid': VITE_UNI_APPID,
|
||||||
'description': '',
|
'description': '',
|
||||||
'versionName': '1.0.0',
|
'versionName': '1.0.0',
|
||||||
'versionCode': '100',
|
'versionCode': '100',
|
||||||
'transformPx': false,
|
'transformPx': false,
|
||||||
'locale': VITE_FALLBACK_LOCALE, // 'zh-Hans'
|
'locale': VITE_FALLBACK_LOCALE, // 'zh-Hans'
|
||||||
'h5': {
|
'h5': {
|
||||||
router: {
|
router: {
|
||||||
// base: VITE_APP_PUBLIC_BASE,
|
// base: VITE_APP_PUBLIC_BASE,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
/* 5+App特有相关 */
|
/* 5+App特有相关 */
|
||||||
'app-plus': {
|
'app-plus': {
|
||||||
usingComponents: true,
|
usingComponents: true,
|
||||||
nvueStyleCompiler: 'uni-app',
|
nvueStyleCompiler: 'uni-app',
|
||||||
compilerVersion: 3,
|
compilerVersion: 3,
|
||||||
compatible: {
|
compatible: {
|
||||||
ignoreVersion: true,
|
ignoreVersion: true,
|
||||||
},
|
},
|
||||||
splashscreen: {
|
splashscreen: {
|
||||||
alwaysShowBeforeRender: true,
|
alwaysShowBeforeRender: true,
|
||||||
waiting: true,
|
waiting: true,
|
||||||
autoclose: true,
|
autoclose: true,
|
||||||
delay: 0,
|
delay: 0,
|
||||||
},
|
},
|
||||||
/* 模块配置 */
|
/* 模块配置 */
|
||||||
modules: {},
|
modules: {},
|
||||||
/* 应用发布信息 */
|
/* 应用发布信息 */
|
||||||
distribute: {
|
distribute: {
|
||||||
/* android打包配置 */
|
/* android打包配置 */
|
||||||
android: {
|
android: {
|
||||||
minSdkVersion: 30,
|
minSdkVersion: 30,
|
||||||
targetSdkVersion: 30,
|
targetSdkVersion: 30,
|
||||||
abiFilters: ['armeabi-v7a', 'arm64-v8a'],
|
abiFilters: ['armeabi-v7a', 'arm64-v8a'],
|
||||||
permissions: [
|
permissions: [
|
||||||
'<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>',
|
'<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>',
|
||||||
'<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>',
|
'<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>',
|
||||||
'<uses-permission android:name="android.permission.VIBRATE"/>',
|
'<uses-permission android:name="android.permission.VIBRATE"/>',
|
||||||
'<uses-permission android:name="android.permission.READ_LOGS"/>',
|
'<uses-permission android:name="android.permission.READ_LOGS"/>',
|
||||||
'<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>',
|
'<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>',
|
||||||
'<uses-feature android:name="android.hardware.camera.autofocus"/>',
|
'<uses-feature android:name="android.hardware.camera.autofocus"/>',
|
||||||
'<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>',
|
'<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>',
|
||||||
'<uses-permission android:name="android.permission.CAMERA"/>',
|
'<uses-permission android:name="android.permission.CAMERA"/>',
|
||||||
'<uses-permission android:name="android.permission.GET_ACCOUNTS"/>',
|
'<uses-permission android:name="android.permission.GET_ACCOUNTS"/>',
|
||||||
'<uses-permission android:name="android.permission.READ_PHONE_STATE"/>',
|
'<uses-permission android:name="android.permission.READ_PHONE_STATE"/>',
|
||||||
'<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>',
|
'<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>',
|
||||||
'<uses-permission android:name="android.permission.WAKE_LOCK"/>',
|
'<uses-permission android:name="android.permission.WAKE_LOCK"/>',
|
||||||
'<uses-permission android:name="android.permission.FLASHLIGHT"/>',
|
'<uses-permission android:name="android.permission.FLASHLIGHT"/>',
|
||||||
'<uses-feature android:name="android.hardware.camera"/>',
|
'<uses-feature android:name="android.hardware.camera"/>',
|
||||||
'<uses-permission android:name="android.permission.WRITE_SETTINGS"/>',
|
'<uses-permission android:name="android.permission.WRITE_SETTINGS"/>',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
/* ios打包配置 */
|
/* ios打包配置 */
|
||||||
ios: {},
|
ios: {},
|
||||||
/* SDK配置 */
|
/* SDK配置 */
|
||||||
sdkConfigs: {},
|
sdkConfigs: {},
|
||||||
/* 图标配置 */
|
/* 图标配置 */
|
||||||
icons: {
|
icons: {
|
||||||
android: {
|
android: {
|
||||||
hdpi: 'static/app/icons/72x72.png',
|
hdpi: 'static/app/icons/72x72.png',
|
||||||
xhdpi: 'static/app/icons/96x96.png',
|
xhdpi: 'static/app/icons/96x96.png',
|
||||||
xxhdpi: 'static/app/icons/144x144.png',
|
xxhdpi: 'static/app/icons/144x144.png',
|
||||||
xxxhdpi: 'static/app/icons/192x192.png',
|
xxxhdpi: 'static/app/icons/192x192.png',
|
||||||
},
|
},
|
||||||
ios: {
|
ios: {
|
||||||
appstore: 'static/app/icons/1024x1024.png',
|
appstore: 'static/app/icons/1024x1024.png',
|
||||||
ipad: {
|
ipad: {
|
||||||
'app': 'static/app/icons/76x76.png',
|
'app': 'static/app/icons/76x76.png',
|
||||||
'app@2x': 'static/app/icons/152x152.png',
|
'app@2x': 'static/app/icons/152x152.png',
|
||||||
'notification': 'static/app/icons/20x20.png',
|
'notification': 'static/app/icons/20x20.png',
|
||||||
'notification@2x': 'static/app/icons/40x40.png',
|
'notification@2x': 'static/app/icons/40x40.png',
|
||||||
'proapp@2x': 'static/app/icons/167x167.png',
|
'proapp@2x': 'static/app/icons/167x167.png',
|
||||||
'settings': 'static/app/icons/29x29.png',
|
'settings': 'static/app/icons/29x29.png',
|
||||||
'settings@2x': 'static/app/icons/58x58.png',
|
'settings@2x': 'static/app/icons/58x58.png',
|
||||||
'spotlight': 'static/app/icons/40x40.png',
|
'spotlight': 'static/app/icons/40x40.png',
|
||||||
'spotlight@2x': 'static/app/icons/80x80.png',
|
'spotlight@2x': 'static/app/icons/80x80.png',
|
||||||
},
|
},
|
||||||
iphone: {
|
iphone: {
|
||||||
'app@2x': 'static/app/icons/120x120.png',
|
'app@2x': 'static/app/icons/120x120.png',
|
||||||
'app@3x': 'static/app/icons/180x180.png',
|
'app@3x': 'static/app/icons/180x180.png',
|
||||||
'notification@2x': 'static/app/icons/40x40.png',
|
'notification@2x': 'static/app/icons/40x40.png',
|
||||||
'notification@3x': 'static/app/icons/60x60.png',
|
'notification@3x': 'static/app/icons/60x60.png',
|
||||||
'settings@2x': 'static/app/icons/58x58.png',
|
'settings@2x': 'static/app/icons/58x58.png',
|
||||||
'settings@3x': 'static/app/icons/87x87.png',
|
'settings@3x': 'static/app/icons/87x87.png',
|
||||||
'spotlight@2x': 'static/app/icons/80x80.png',
|
'spotlight@2x': 'static/app/icons/80x80.png',
|
||||||
'spotlight@3x': 'static/app/icons/120x120.png',
|
'spotlight@3x': 'static/app/icons/120x120.png',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
/* 快应用特有相关 */
|
/* 快应用特有相关 */
|
||||||
'quickapp': {},
|
'quickapp': {},
|
||||||
/* 小程序特有相关 */
|
/* 小程序特有相关 */
|
||||||
'mp-weixin': {
|
'mp-weixin': {
|
||||||
appid: VITE_WX_APPID,
|
appid: VITE_WX_APPID,
|
||||||
setting: {
|
setting: {
|
||||||
urlCheck: false,
|
urlCheck: false,
|
||||||
// 是否启用 ES6 转 ES5
|
// 是否启用 ES6 转 ES5
|
||||||
es6: true,
|
es6: true,
|
||||||
minified: true,
|
minified: true,
|
||||||
},
|
},
|
||||||
optimization: {
|
optimization: {
|
||||||
subPackages: true,
|
subPackages: true,
|
||||||
},
|
},
|
||||||
// styleIsolation: 'shared',
|
// styleIsolation: 'shared',
|
||||||
usingComponents: true,
|
usingComponents: true,
|
||||||
// __usePrivacyCheck__: true,
|
// __usePrivacyCheck__: true,
|
||||||
},
|
},
|
||||||
'mp-alipay': {
|
'mp-alipay': {
|
||||||
usingComponents: true,
|
usingComponents: true,
|
||||||
styleIsolation: 'shared',
|
styleIsolation: 'shared',
|
||||||
},
|
},
|
||||||
'mp-baidu': {
|
'mp-baidu': {
|
||||||
usingComponents: true,
|
usingComponents: true,
|
||||||
},
|
},
|
||||||
'mp-toutiao': {
|
'mp-toutiao': {
|
||||||
usingComponents: true,
|
usingComponents: true,
|
||||||
},
|
},
|
||||||
'uniStatistics': {
|
'uniStatistics': {
|
||||||
enable: false,
|
enable: false,
|
||||||
},
|
},
|
||||||
'vueVersion': '3',
|
'vueVersion': '3',
|
||||||
})
|
})
|
||||||
|
|||||||
@ -2,22 +2,22 @@ import { defineUniPages } from '@uni-helper/vite-plugin-uni-pages'
|
|||||||
import { tabBar } from './src/tabbar/config'
|
import { tabBar } from './src/tabbar/config'
|
||||||
|
|
||||||
export default defineUniPages({
|
export default defineUniPages({
|
||||||
globalStyle: {
|
globalStyle: {
|
||||||
navigationStyle: 'default',
|
navigationStyle: 'default',
|
||||||
navigationBarTitleText: 'unibest',
|
navigationBarTitleText: 'unibest',
|
||||||
navigationBarBackgroundColor: '#f8f8f8',
|
navigationBarBackgroundColor: '#f8f8f8',
|
||||||
navigationBarTextStyle: 'black',
|
navigationBarTextStyle: 'black',
|
||||||
backgroundColor: '#FFFFFF',
|
backgroundColor: '#FFFFFF',
|
||||||
},
|
},
|
||||||
easycom: {
|
easycom: {
|
||||||
autoscan: true,
|
autoscan: true,
|
||||||
custom: {
|
custom: {
|
||||||
'^fg-(.*)': '@/components/fg-$1/fg-$1.vue',
|
'^fg-(.*)': '@/components/fg-$1/fg-$1.vue',
|
||||||
'^wd-(.*)': 'wot-design-uni/components/wd-$1/wd-$1.vue',
|
'^wd-(.*)': 'wot-design-uni/components/wd-$1/wd-$1.vue',
|
||||||
'^(?!z-paging-refresh|z-paging-load-more)z-paging(.*)':
|
'^(?!z-paging-refresh|z-paging-load-more)z-paging(.*)':
|
||||||
'z-paging/components/z-paging$1/z-paging$1.vue',
|
'z-paging/components/z-paging$1/z-paging$1.vue',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// tabbar 的配置统一在 “./src/tabbar/config.ts” 文件中
|
// tabbar 的配置统一在 “./src/tabbar/config.ts” 文件中
|
||||||
tabBar: tabBar as any,
|
tabBar: tabBar as any,
|
||||||
})
|
})
|
||||||
|
|||||||
@ -8,153 +8,153 @@ type TImage = 'png' | 'jpg' | 'jpeg' | 'webp' | '*'
|
|||||||
type TFile = 'doc' | 'docx' | 'ppt' | 'zip' | 'xls' | 'xlsx' | 'txt' | TImage
|
type TFile = 'doc' | 'docx' | 'ppt' | 'zip' | 'xls' | 'xlsx' | 'txt' | TImage
|
||||||
|
|
||||||
interface TOptions<T extends TfileType> {
|
interface TOptions<T extends TfileType> {
|
||||||
formData?: Record<string, any>
|
formData?: Record<string, any>
|
||||||
maxSize?: number
|
maxSize?: number
|
||||||
accept?: T extends 'image' ? TImage[] : TFile[]
|
accept?: T extends 'image' ? TImage[] : TFile[]
|
||||||
fileType?: T
|
fileType?: T
|
||||||
success?: (params: any) => void
|
success?: (params: any) => void
|
||||||
error?: (err: any) => void
|
error?: (err: any) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function useUpload<T extends TfileType>(options: TOptions<T> = {} as TOptions<T>) {
|
export default function useUpload<T extends TfileType>(options: TOptions<T> = {} as TOptions<T>) {
|
||||||
const {
|
const {
|
||||||
formData = {},
|
formData = {},
|
||||||
maxSize = 5 * 1024 * 1024,
|
maxSize = 5 * 1024 * 1024,
|
||||||
accept = ['*'],
|
accept = ['*'],
|
||||||
fileType = 'image',
|
fileType = 'image',
|
||||||
success,
|
success,
|
||||||
error: onError,
|
error: onError,
|
||||||
} = options
|
} = options
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const error = ref<Error | null>(null)
|
const error = ref<Error | null>(null)
|
||||||
const data = ref<any>(null)
|
const data = ref<any>(null)
|
||||||
|
|
||||||
const handleFileChoose = ({ tempFilePath, size }: { tempFilePath: string, size: number }) => {
|
const handleFileChoose = ({ tempFilePath, size }: { tempFilePath: string, size: number }) => {
|
||||||
if (size > maxSize) {
|
if (size > maxSize) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: `文件大小不能超过 ${maxSize / 1024 / 1024}MB`,
|
title: `文件大小不能超过 ${maxSize / 1024 / 1024}MB`,
|
||||||
icon: 'none',
|
icon: 'none',
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// const fileExtension = file?.tempFiles?.name?.split('.').pop()?.toLowerCase()
|
// const fileExtension = file?.tempFiles?.name?.split('.').pop()?.toLowerCase()
|
||||||
// const isTypeValid = accept.some((type) => type === '*' || type.toLowerCase() === fileExtension)
|
// const isTypeValid = accept.some((type) => type === '*' || type.toLowerCase() === fileExtension)
|
||||||
|
|
||||||
// if (!isTypeValid) {
|
// if (!isTypeValid) {
|
||||||
// uni.showToast({
|
// uni.showToast({
|
||||||
// title: `仅支持 ${accept.join(', ')} 格式的文件`,
|
// title: `仅支持 ${accept.join(', ')} 格式的文件`,
|
||||||
// icon: 'none',
|
// icon: 'none',
|
||||||
// })
|
// })
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
|
|
||||||
loading.value = true
|
loading.value = true
|
||||||
uploadFile({
|
uploadFile({
|
||||||
tempFilePath,
|
tempFilePath,
|
||||||
formData,
|
formData,
|
||||||
onSuccess: (res) => {
|
onSuccess: (res) => {
|
||||||
const { data: _data } = JSON.parse(res)
|
const { data: _data } = JSON.parse(res)
|
||||||
data.value = _data
|
data.value = _data
|
||||||
// console.log('上传成功', res)
|
// console.log('上传成功', res)
|
||||||
success?.(_data)
|
success?.(_data)
|
||||||
},
|
},
|
||||||
onError: (err) => {
|
onError: (err) => {
|
||||||
error.value = err
|
error.value = err
|
||||||
onError?.(err)
|
onError?.(err)
|
||||||
},
|
},
|
||||||
onComplete: () => {
|
onComplete: () => {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const run = () => {
|
const run = () => {
|
||||||
// 微信小程序从基础库 2.21.0 开始, wx.chooseImage 停止维护,请使用 uni.chooseMedia 代替。
|
// 微信小程序从基础库 2.21.0 开始, wx.chooseImage 停止维护,请使用 uni.chooseMedia 代替。
|
||||||
// 微信小程序在2023年10月17日之后,使用本API需要配置隐私协议
|
// 微信小程序在2023年10月17日之后,使用本API需要配置隐私协议
|
||||||
const chooseFileOptions = {
|
const chooseFileOptions = {
|
||||||
count: 1,
|
count: 1,
|
||||||
success: (res: any) => {
|
success: (res: any) => {
|
||||||
console.log('File selected successfully:', res)
|
console.log('File selected successfully:', res)
|
||||||
// 小程序中res:{errMsg: "chooseImage:ok", tempFiles: [{fileType: "image", size: 48976, tempFilePath: "http://tmp/5iG1WpIxTaJf3ece38692a337dc06df7eb69ecb49c6b.jpeg"}]}
|
// 小程序中res:{errMsg: "chooseImage:ok", tempFiles: [{fileType: "image", size: 48976, tempFilePath: "http://tmp/5iG1WpIxTaJf3ece38692a337dc06df7eb69ecb49c6b.jpeg"}]}
|
||||||
// h5中res:{errMsg: "chooseImage:ok", tempFilePaths: "blob:http://localhost:9000/f74ab6b8-a14d-4cb6-a10d-fcf4511a0de5", tempFiles: [File]}
|
// h5中res:{errMsg: "chooseImage:ok", tempFilePaths: "blob:http://localhost:9000/f74ab6b8-a14d-4cb6-a10d-fcf4511a0de5", tempFiles: [File]}
|
||||||
// h5的File有以下字段:{name: "girl.jpeg", size: 48976, type: "image/jpeg"}
|
// h5的File有以下字段:{name: "girl.jpeg", size: 48976, type: "image/jpeg"}
|
||||||
// App中res:{errMsg: "chooseImage:ok", tempFilePaths: "file:///Users/feige/xxx/gallery/1522437259-compressed-IMG_0006.jpg", tempFiles: [File]}
|
// App中res:{errMsg: "chooseImage:ok", tempFilePaths: "file:///Users/feige/xxx/gallery/1522437259-compressed-IMG_0006.jpg", tempFiles: [File]}
|
||||||
// App的File有以下字段:{path: "file:///Users/feige/xxx/gallery/1522437259-compressed-IMG_0006.jpg", size: 48976}
|
// App的File有以下字段:{path: "file:///Users/feige/xxx/gallery/1522437259-compressed-IMG_0006.jpg", size: 48976}
|
||||||
let tempFilePath = ''
|
let tempFilePath = ''
|
||||||
let size = 0
|
let size = 0
|
||||||
// #ifdef MP-WEIXIN
|
// #ifdef MP-WEIXIN
|
||||||
tempFilePath = res.tempFiles[0].tempFilePath
|
tempFilePath = res.tempFiles[0].tempFilePath
|
||||||
size = res.tempFiles[0].size
|
size = res.tempFiles[0].size
|
||||||
// #endif
|
// #endif
|
||||||
// #ifndef MP-WEIXIN
|
// #ifndef MP-WEIXIN
|
||||||
tempFilePath = res.tempFilePaths[0]
|
tempFilePath = res.tempFilePaths[0]
|
||||||
size = res.tempFiles[0].size
|
size = res.tempFiles[0].size
|
||||||
// #endif
|
// #endif
|
||||||
handleFileChoose({ tempFilePath, size })
|
handleFileChoose({ tempFilePath, size })
|
||||||
},
|
},
|
||||||
fail: (err: any) => {
|
fail: (err: any) => {
|
||||||
console.error('File selection failed:', err)
|
console.error('File selection failed:', err)
|
||||||
error.value = err
|
error.value = err
|
||||||
onError?.(err)
|
onError?.(err)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fileType === 'image') {
|
if (fileType === 'image') {
|
||||||
// #ifdef MP-WEIXIN
|
// #ifdef MP-WEIXIN
|
||||||
uni.chooseMedia({
|
uni.chooseMedia({
|
||||||
...chooseFileOptions,
|
...chooseFileOptions,
|
||||||
mediaType: ['image'],
|
mediaType: ['image'],
|
||||||
})
|
})
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
// #ifndef MP-WEIXIN
|
// #ifndef MP-WEIXIN
|
||||||
uni.chooseImage(chooseFileOptions)
|
uni.chooseImage(chooseFileOptions)
|
||||||
// #endif
|
// #endif
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
uni.chooseFile({
|
uni.chooseFile({
|
||||||
...chooseFileOptions,
|
...chooseFileOptions,
|
||||||
type: 'all',
|
type: 'all',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { loading, error, data, run }
|
return { loading, error, data, run }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function uploadFile({
|
async function uploadFile({
|
||||||
tempFilePath,
|
tempFilePath,
|
||||||
formData,
|
formData,
|
||||||
onSuccess,
|
onSuccess,
|
||||||
onError,
|
onError,
|
||||||
onComplete,
|
onComplete,
|
||||||
}: {
|
}: {
|
||||||
tempFilePath: string
|
tempFilePath: string
|
||||||
formData: Record<string, any>
|
formData: Record<string, any>
|
||||||
onSuccess: (data: any) => void
|
onSuccess: (data: any) => void
|
||||||
onError: (err: any) => void
|
onError: (err: any) => void
|
||||||
onComplete: () => void
|
onComplete: () => void
|
||||||
}) {
|
}) {
|
||||||
uni.uploadFile({
|
uni.uploadFile({
|
||||||
url: VITE_UPLOAD_BASEURL,
|
url: VITE_UPLOAD_BASEURL,
|
||||||
filePath: tempFilePath,
|
filePath: tempFilePath,
|
||||||
name: 'file',
|
name: 'file',
|
||||||
formData,
|
formData,
|
||||||
success: (uploadFileRes) => {
|
success: (uploadFileRes) => {
|
||||||
try {
|
try {
|
||||||
const data = uploadFileRes.data
|
const data = uploadFileRes.data
|
||||||
onSuccess(data)
|
onSuccess(data)
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
onError(err)
|
onError(err)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
console.error('Upload failed:', err)
|
console.error('Upload failed:', err)
|
||||||
onError(err)
|
onError(err)
|
||||||
},
|
},
|
||||||
complete: onComplete,
|
complete: onComplete,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,103 +9,103 @@ import { ContentTypeEnum, ResultEnum, ShowMessage } from './tools/enum'
|
|||||||
|
|
||||||
// 配置动态Tag
|
// 配置动态Tag
|
||||||
export const API_DOMAINS = {
|
export const API_DOMAINS = {
|
||||||
DEFAULT: import.meta.env.VITE_SERVER_BASEURL,
|
DEFAULT: import.meta.env.VITE_SERVER_BASEURL,
|
||||||
SECONDARY: import.meta.env.VITE_API_SECONDARY_URL,
|
SECONDARY: import.meta.env.VITE_API_SECONDARY_URL,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建请求实例
|
* 创建请求实例
|
||||||
*/
|
*/
|
||||||
const { onAuthRequired, onResponseRefreshToken } = createServerTokenAuthentication<
|
const { onAuthRequired, onResponseRefreshToken } = createServerTokenAuthentication<
|
||||||
typeof VueHook,
|
typeof VueHook,
|
||||||
typeof uniappRequestAdapter
|
typeof uniappRequestAdapter
|
||||||
>({
|
>({
|
||||||
refreshTokenOnError: {
|
refreshTokenOnError: {
|
||||||
isExpired: (error) => {
|
isExpired: (error) => {
|
||||||
return error.response?.status === ResultEnum.Unauthorized
|
return error.response?.status === ResultEnum.Unauthorized
|
||||||
},
|
},
|
||||||
handler: async () => {
|
handler: async () => {
|
||||||
try {
|
try {
|
||||||
// await authLogin();
|
// await authLogin();
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
// 切换到登录页
|
// 切换到登录页
|
||||||
await uni.reLaunch({ url: '/pages/common/login/index' })
|
await uni.reLaunch({ url: '/pages/common/login/index' })
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* alova 请求实例
|
* alova 请求实例
|
||||||
*/
|
*/
|
||||||
const alovaInstance = createAlova({
|
const alovaInstance = createAlova({
|
||||||
baseURL: import.meta.env.VITE_APP_PROXY_PREFIX,
|
baseURL: import.meta.env.VITE_APP_PROXY_PREFIX,
|
||||||
...AdapterUniapp(),
|
...AdapterUniapp(),
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
statesHook: VueHook,
|
statesHook: VueHook,
|
||||||
|
|
||||||
beforeRequest: onAuthRequired((method) => {
|
beforeRequest: onAuthRequired((method) => {
|
||||||
// 设置默认 Content-Type
|
// 设置默认 Content-Type
|
||||||
method.config.headers = {
|
method.config.headers = {
|
||||||
ContentType: ContentTypeEnum.JSON,
|
ContentType: ContentTypeEnum.JSON,
|
||||||
Accept: 'application/json, text/plain, */*',
|
Accept: 'application/json, text/plain, */*',
|
||||||
...method.config.headers,
|
...method.config.headers,
|
||||||
}
|
}
|
||||||
|
|
||||||
const { config } = method
|
const { config } = method
|
||||||
const ignoreAuth = !config.meta?.ignoreAuth
|
const ignoreAuth = !config.meta?.ignoreAuth
|
||||||
console.log('ignoreAuth===>', ignoreAuth)
|
console.log('ignoreAuth===>', ignoreAuth)
|
||||||
// 处理认证信息 自行处理认证问题
|
// 处理认证信息 自行处理认证问题
|
||||||
if (ignoreAuth) {
|
if (ignoreAuth) {
|
||||||
const token = 'getToken()'
|
const token = 'getToken()'
|
||||||
if (!token) {
|
if (!token) {
|
||||||
throw new Error('[请求错误]:未登录')
|
throw new Error('[请求错误]:未登录')
|
||||||
}
|
}
|
||||||
// method.config.headers.token = token;
|
// method.config.headers.token = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理动态域名
|
// 处理动态域名
|
||||||
if (config.meta?.domain) {
|
if (config.meta?.domain) {
|
||||||
method.baseURL = config.meta.domain
|
method.baseURL = config.meta.domain
|
||||||
console.log('当前域名', method.baseURL)
|
console.log('当前域名', method.baseURL)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
responded: onResponseRefreshToken((response, method) => {
|
responded: onResponseRefreshToken((response, method) => {
|
||||||
const { config } = method
|
const { config } = method
|
||||||
const { requestType } = config
|
const { requestType } = config
|
||||||
const {
|
const {
|
||||||
statusCode,
|
statusCode,
|
||||||
data: rawData,
|
data: rawData,
|
||||||
errMsg,
|
errMsg,
|
||||||
} = response as UniNamespace.RequestSuccessCallbackResult
|
} = response as UniNamespace.RequestSuccessCallbackResult
|
||||||
|
|
||||||
// 处理特殊请求类型(上传/下载)
|
// 处理特殊请求类型(上传/下载)
|
||||||
if (requestType === 'upload' || requestType === 'download') {
|
if (requestType === 'upload' || requestType === 'download') {
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理 HTTP 状态码错误
|
// 处理 HTTP 状态码错误
|
||||||
if (statusCode !== 200) {
|
if (statusCode !== 200) {
|
||||||
const errorMessage = ShowMessage(statusCode) || `HTTP请求错误[${statusCode}]`
|
const errorMessage = ShowMessage(statusCode) || `HTTP请求错误[${statusCode}]`
|
||||||
console.error('errorMessage===>', errorMessage)
|
console.error('errorMessage===>', errorMessage)
|
||||||
toast.error(errorMessage)
|
toast.error(errorMessage)
|
||||||
throw new Error(`${errorMessage}:${errMsg}`)
|
throw new Error(`${errorMessage}:${errMsg}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理业务逻辑错误
|
// 处理业务逻辑错误
|
||||||
const { code, message, data } = rawData as IResponse
|
const { code, message, data } = rawData as IResponse
|
||||||
if (code !== ResultEnum.Success) {
|
if (code !== ResultEnum.Success) {
|
||||||
if (config.meta?.toast !== false) {
|
if (config.meta?.toast !== false) {
|
||||||
toast.warning(message)
|
toast.warning(message)
|
||||||
}
|
}
|
||||||
throw new Error(`请求错误[${code}]:${message}`)
|
throw new Error(`请求错误[${code}]:${message}`)
|
||||||
}
|
}
|
||||||
// 处理成功响应,返回业务数据
|
// 处理成功响应,返回业务数据
|
||||||
return data
|
return data
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
export const http = alovaInstance
|
export const http = alovaInstance
|
||||||
|
|||||||
142
src/http/http.ts
@ -1,47 +1,47 @@
|
|||||||
import type { CustomRequestOptions } from '@/http/types'
|
import type { CustomRequestOptions } from '@/http/types'
|
||||||
|
|
||||||
export function http<T>(options: CustomRequestOptions) {
|
export function http<T>(options: CustomRequestOptions) {
|
||||||
// 1. 返回 Promise 对象
|
// 1. 返回 Promise 对象
|
||||||
return new Promise<IResData<T>>((resolve, reject) => {
|
return new Promise<IResData<T>>((resolve, reject) => {
|
||||||
uni.request({
|
uni.request({
|
||||||
...options,
|
...options,
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
// #ifndef MP-WEIXIN
|
// #ifndef MP-WEIXIN
|
||||||
responseType: 'json',
|
responseType: 'json',
|
||||||
// #endif
|
// #endif
|
||||||
// 响应成功
|
// 响应成功
|
||||||
success(res) {
|
success(res) {
|
||||||
// 状态码 2xx,参考 axios 的设计
|
// 状态码 2xx,参考 axios 的设计
|
||||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||||
// 2.1 提取核心数据 res.data
|
// 2.1 提取核心数据 res.data
|
||||||
resolve(res.data as IResData<T>)
|
resolve(res.data as IResData<T>)
|
||||||
}
|
}
|
||||||
else if (res.statusCode === 401) {
|
else if (res.statusCode === 401) {
|
||||||
// 401错误 -> 清理用户信息,跳转到登录页
|
// 401错误 -> 清理用户信息,跳转到登录页
|
||||||
// userStore.clearUserInfo()
|
// userStore.clearUserInfo()
|
||||||
// uni.navigateTo({ url: '/pages/login/login' })
|
// uni.navigateTo({ url: '/pages/login/login' })
|
||||||
reject(res)
|
reject(res)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// 其他错误 -> 根据后端错误信息轻提示
|
// 其他错误 -> 根据后端错误信息轻提示
|
||||||
!options.hideErrorToast
|
!options.hideErrorToast
|
||||||
&& uni.showToast({
|
&& uni.showToast({
|
||||||
icon: 'none',
|
icon: 'none',
|
||||||
title: (res.data as IResData<T>).msg || '请求错误',
|
title: (res.data as IResData<T>).msg || '请求错误',
|
||||||
})
|
})
|
||||||
reject(res)
|
reject(res)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 响应失败
|
// 响应失败
|
||||||
fail(err) {
|
fail(err) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
icon: 'none',
|
icon: 'none',
|
||||||
title: '网络错误,换个网络试试',
|
title: '网络错误,换个网络试试',
|
||||||
})
|
})
|
||||||
reject(err)
|
reject(err)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,13 +52,13 @@ export function http<T>(options: CustomRequestOptions) {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function httpGet<T>(url: string, query?: Record<string, any>, header?: Record<string, any>, options?: Partial<CustomRequestOptions>) {
|
export function httpGet<T>(url: string, query?: Record<string, any>, header?: Record<string, any>, options?: Partial<CustomRequestOptions>) {
|
||||||
return http<T>({
|
return http<T>({
|
||||||
url,
|
url,
|
||||||
query,
|
query,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
header,
|
header,
|
||||||
...options,
|
...options,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,40 +70,40 @@ export function httpGet<T>(url: string, query?: Record<string, any>, header?: Re
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function httpPost<T>(url: string, data?: Record<string, any>, query?: Record<string, any>, header?: Record<string, any>, options?: Partial<CustomRequestOptions>) {
|
export function httpPost<T>(url: string, data?: Record<string, any>, query?: Record<string, any>, header?: Record<string, any>, options?: Partial<CustomRequestOptions>) {
|
||||||
return http<T>({
|
return http<T>({
|
||||||
url,
|
url,
|
||||||
query,
|
query,
|
||||||
data,
|
data,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
header,
|
header,
|
||||||
...options,
|
...options,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* PUT 请求
|
* PUT 请求
|
||||||
*/
|
*/
|
||||||
export function httpPut<T>(url: string, data?: Record<string, any>, query?: Record<string, any>, header?: Record<string, any>, options?: Partial<CustomRequestOptions>) {
|
export function httpPut<T>(url: string, data?: Record<string, any>, query?: Record<string, any>, header?: Record<string, any>, options?: Partial<CustomRequestOptions>) {
|
||||||
return http<T>({
|
return http<T>({
|
||||||
url,
|
url,
|
||||||
data,
|
data,
|
||||||
query,
|
query,
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
header,
|
header,
|
||||||
...options,
|
...options,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DELETE 请求(无请求体,仅 query)
|
* DELETE 请求(无请求体,仅 query)
|
||||||
*/
|
*/
|
||||||
export function httpDelete<T>(url: string, query?: Record<string, any>, header?: Record<string, any>, options?: Partial<CustomRequestOptions>) {
|
export function httpDelete<T>(url: string, query?: Record<string, any>, header?: Record<string, any>, options?: Partial<CustomRequestOptions>) {
|
||||||
return http<T>({
|
return http<T>({
|
||||||
url,
|
url,
|
||||||
query,
|
query,
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
header,
|
header,
|
||||||
...options,
|
...options,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
http.get = httpGet
|
http.get = httpGet
|
||||||
|
|||||||
@ -9,57 +9,57 @@ const baseUrl = getEnvBaseUrl()
|
|||||||
|
|
||||||
// 拦截器配置
|
// 拦截器配置
|
||||||
const httpInterceptor = {
|
const httpInterceptor = {
|
||||||
// 拦截前触发
|
// 拦截前触发
|
||||||
invoke(options: CustomRequestOptions) {
|
invoke(options: CustomRequestOptions) {
|
||||||
// 接口请求支持通过 query 参数配置 queryString
|
// 接口请求支持通过 query 参数配置 queryString
|
||||||
if (options.query) {
|
if (options.query) {
|
||||||
const queryStr = stringifyQuery(options.query)
|
const queryStr = stringifyQuery(options.query)
|
||||||
if (options.url.includes('?')) {
|
if (options.url.includes('?')) {
|
||||||
options.url += `&${queryStr}`
|
options.url += `&${queryStr}`
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
options.url += `?${queryStr}`
|
options.url += `?${queryStr}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 非 http 开头需拼接地址
|
// 非 http 开头需拼接地址
|
||||||
if (!options.url.startsWith('http')) {
|
if (!options.url.startsWith('http')) {
|
||||||
// #ifdef H5
|
// #ifdef H5
|
||||||
// console.log(__VITE_APP_PROXY__)
|
// console.log(__VITE_APP_PROXY__)
|
||||||
if (JSON.parse(__VITE_APP_PROXY__)) {
|
if (JSON.parse(__VITE_APP_PROXY__)) {
|
||||||
// 自动拼接代理前缀
|
// 自动拼接代理前缀
|
||||||
options.url = import.meta.env.VITE_APP_PROXY_PREFIX + options.url
|
options.url = import.meta.env.VITE_APP_PROXY_PREFIX + options.url
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
options.url = baseUrl + options.url
|
options.url = baseUrl + options.url
|
||||||
}
|
}
|
||||||
// #endif
|
// #endif
|
||||||
// 非H5正常拼接
|
// 非H5正常拼接
|
||||||
// #ifndef H5
|
// #ifndef H5
|
||||||
options.url = baseUrl + options.url
|
options.url = baseUrl + options.url
|
||||||
// #endif
|
// #endif
|
||||||
// TIPS: 如果需要对接多个后端服务,也可以在这里处理,拼接成所需要的地址
|
// TIPS: 如果需要对接多个后端服务,也可以在这里处理,拼接成所需要的地址
|
||||||
}
|
}
|
||||||
// 1. 请求超时
|
// 1. 请求超时
|
||||||
options.timeout = 10000 // 10s
|
options.timeout = 10000 // 10s
|
||||||
// 2. (可选)添加小程序端请求头标识
|
// 2. (可选)添加小程序端请求头标识
|
||||||
options.header = {
|
options.header = {
|
||||||
platform, // 可选,与 uniapp 定义的平台一致,告诉后台来源
|
platform, // 可选,与 uniapp 定义的平台一致,告诉后台来源
|
||||||
...options.header,
|
...options.header,
|
||||||
}
|
}
|
||||||
// 3. 添加 token 请求头标识
|
// 3. 添加 token 请求头标识
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const { token } = userStore.userInfo as unknown as IUserInfo
|
const { token } = userStore.userInfo as unknown as IUserInfo
|
||||||
if (token) {
|
if (token) {
|
||||||
options.header.Authorization = `Bearer ${token}`
|
options.header.Authorization = `Bearer ${token}`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export const requestInterceptor = {
|
export const requestInterceptor = {
|
||||||
install() {
|
install() {
|
||||||
// 拦截 request 请求
|
// 拦截 request 请求
|
||||||
uni.addInterceptor('request', httpInterceptor)
|
uni.addInterceptor('request', httpInterceptor)
|
||||||
// 拦截 uploadFile 文件上传
|
// 拦截 uploadFile 文件上传
|
||||||
uni.addInterceptor('uploadFile', httpInterceptor)
|
uni.addInterceptor('uploadFile', httpInterceptor)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
17
src/main.ts
@ -9,13 +9,14 @@ import '@/style/index.scss'
|
|||||||
import 'virtual:uno.css'
|
import 'virtual:uno.css'
|
||||||
|
|
||||||
export function createApp() {
|
export function createApp() {
|
||||||
const app = createSSRApp(App)
|
const app = createSSRApp(App)
|
||||||
app.use(store)
|
app.use(store)
|
||||||
app.use(routeInterceptor)
|
app.use(routeInterceptor)
|
||||||
app.use(requestInterceptor)
|
app.use(requestInterceptor)
|
||||||
app.use(VueQueryPlugin)
|
app.use(VueQueryPlugin)
|
||||||
|
app.provide('OSS', '/src/static/')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
app,
|
app,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tabBar": {
|
"tabBar": {
|
||||||
"custom": true,
|
"custom": false,
|
||||||
"color": "#999999",
|
"color": "#999999",
|
||||||
"selectedColor": "#018d71",
|
"selectedColor": "#018d71",
|
||||||
"backgroundColor": "#F8F8F8",
|
"backgroundColor": "#F8F8F8",
|
||||||
@ -27,15 +27,21 @@
|
|||||||
"list": [
|
"list": [
|
||||||
{
|
{
|
||||||
"iconPath": "static/tabbar/home.png",
|
"iconPath": "static/tabbar/home.png",
|
||||||
"selectedIconPath": "static/tabbar/homeHL.png",
|
"selectedIconPath": "static/tabbar/home_s.png",
|
||||||
"pagePath": "pages/index/index",
|
"pagePath": "pages/index/index",
|
||||||
"text": "首页"
|
"text": "首页"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"iconPath": "static/tabbar/example.png",
|
"iconPath": "static/tabbar/reserve.png",
|
||||||
"selectedIconPath": "static/tabbar/exampleHL.png",
|
"selectedIconPath": "static/tabbar/reserve_s.png",
|
||||||
"pagePath": "pages/about/about",
|
"pagePath": "pages/reserve/reserve",
|
||||||
"text": "关于"
|
"text": "预约"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"iconPath": "static/tabbar/my.png",
|
||||||
|
"selectedIconPath": "static/tabbar/my_s.png",
|
||||||
|
"pagePath": "pages/my/my",
|
||||||
|
"text": "我的"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -72,6 +78,24 @@
|
|||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "Vue Query 请求演示"
|
"navigationBarTitleText": "Vue Query 请求演示"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/my/my",
|
||||||
|
"type": "page",
|
||||||
|
"layout": "tabbar",
|
||||||
|
"style": {
|
||||||
|
"navigationStyle": "custom",
|
||||||
|
"navigationBarTitleText": "我的"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/reserve/reserve",
|
||||||
|
"type": "page",
|
||||||
|
"layout": "tabbar",
|
||||||
|
"style": {
|
||||||
|
"navigationStyle": "custom",
|
||||||
|
"navigationBarTitleText": "预约"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"subPackages": [
|
"subPackages": [
|
||||||
@ -89,4 +113,4 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1,122 +1,86 @@
|
|||||||
<!-- 使用 type="home" 属性设置首页,其他页面不需要设置,默认为page -->
|
<!-- 使用 type="home" 属性设置首页,其他页面不需要设置,默认为page -->
|
||||||
<route lang="jsonc" type="home">
|
<route lang="jsonc" type="home">{
|
||||||
{
|
"layout": "tabbar",
|
||||||
"layout": "tabbar",
|
"style": {
|
||||||
"style": {
|
// 'custom' 表示开启自定义导航栏,默认 'default'
|
||||||
// 'custom' 表示开启自定义导航栏,默认 'default'
|
"navigationStyle": "custom",
|
||||||
"navigationStyle": "custom",
|
"navigationBarTitleText": "首页"
|
||||||
"navigationBarTitleText": "首页"
|
}
|
||||||
}
|
}</route>
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
defineOptions({
|
|
||||||
name: 'Home',
|
|
||||||
})
|
|
||||||
|
|
||||||
// 获取屏幕边界到安全区域距离
|
|
||||||
let safeAreaInsets
|
|
||||||
let systemInfo
|
|
||||||
|
|
||||||
// #ifdef MP-WEIXIN
|
|
||||||
// 微信小程序使用新的API
|
|
||||||
systemInfo = uni.getWindowInfo()
|
|
||||||
safeAreaInsets = systemInfo.safeArea
|
|
||||||
? {
|
|
||||||
top: systemInfo.safeArea.top,
|
|
||||||
right: systemInfo.windowWidth - systemInfo.safeArea.right,
|
|
||||||
bottom: systemInfo.windowHeight - systemInfo.safeArea.bottom,
|
|
||||||
left: systemInfo.safeArea.left,
|
|
||||||
}
|
|
||||||
: null
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
// #ifndef MP-WEIXIN
|
|
||||||
// 其他平台继续使用uni API
|
|
||||||
systemInfo = uni.getSystemInfoSync()
|
|
||||||
safeAreaInsets = systemInfo.safeAreaInsets
|
|
||||||
// #endif
|
|
||||||
const author = ref('菲鸽')
|
|
||||||
const description = ref(
|
|
||||||
'unibest 是一个集成了多种工具和技术的 uniapp 开发模板,由 uniapp + Vue3 + Ts + Vite5 + UnoCss + VSCode 构建,模板具有代码提示、自动格式化、统一配置、代码片段等功能,并内置了许多常用的基本组件和基本功能,让你编写 uniapp 拥有 best 体验。',
|
|
||||||
)
|
|
||||||
// 测试 uni API 自动引入
|
|
||||||
onLoad(() => {
|
|
||||||
console.log('项目作者:', author.value)
|
|
||||||
})
|
|
||||||
|
|
||||||
console.log('index')
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view class="bg-white px-4 pt-2" :style="{ marginTop: `${safeAreaInsets?.top}px` }">
|
<view class="home-bg">
|
||||||
<view class="mt-10">
|
<view class="home-bg w-[100vw] fixed top-0 left-0">
|
||||||
<image src="/static/logo.svg" alt="" class="mx-auto block h-28 w-28" />
|
<wd-navbar safeAreaInsetTop :bordered="false" custom-style="background-color: transparent !important;">
|
||||||
</view>
|
<template #left>
|
||||||
<view class="mt-4 text-center text-4xl text-[#d14328]">
|
<view class="">
|
||||||
unibest
|
<view class="">上海</view>
|
||||||
</view>
|
<wd-img width="14rpx" height="9rpx" :src="`${OSS}icon/icon_arrow_down.png`" />
|
||||||
<view class="mb-8 mt-2 text-center text-2xl">
|
</view>
|
||||||
最好用的 uniapp 开发模板
|
</template>
|
||||||
</view>
|
<template #title>
|
||||||
|
<view class="search-box">
|
||||||
|
<wd-search v-model="keyword" hide-cancel placeholder-left></wd-search>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</wd-navbar>
|
||||||
|
</view>
|
||||||
|
|
||||||
<view class="m-auto mb-2 max-w-100 text-justify indent text-4">
|
<!-- 内容区:自动避让粘性导航栏高度 -->
|
||||||
{{ description }}
|
<view class="content" :style="{ paddingTop: navbarHeight + 'px' }">
|
||||||
</view>
|
<view v-for="(item, index) in 100" :key="index">123--{{ index }}</view>
|
||||||
<view class="mt-4 text-center">
|
</view>
|
||||||
作者:
|
</view>
|
||||||
<text class="text-green-500">
|
|
||||||
菲鸽
|
|
||||||
</text>
|
|
||||||
</view>
|
|
||||||
<view class="mt-4 text-center">
|
|
||||||
官网地址:
|
|
||||||
<text class="text-green-500">
|
|
||||||
https://unibest.tech
|
|
||||||
</text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- #ifdef H5 -->
|
|
||||||
<view class="mt-4 text-center">
|
|
||||||
<a href="https://unibest.tech/base/3-plugin" target="_blank" class="text-green-500">
|
|
||||||
新手请看必看章节1:
|
|
||||||
</a>
|
|
||||||
</view>
|
|
||||||
<!-- #endif -->
|
|
||||||
<!-- #ifdef MP-WEIXIN -->
|
|
||||||
<view class="mt-4 text-center">
|
|
||||||
新手请看必看章节1:
|
|
||||||
<text class="text-green-500">
|
|
||||||
https://unibest.tech/base/3-plugin
|
|
||||||
</text>
|
|
||||||
</view>
|
|
||||||
<!-- #endif -->
|
|
||||||
<!-- #ifdef H5 -->
|
|
||||||
<view class="mt-4 text-center">
|
|
||||||
<a href="https://unibest.tech/base/14-faq" target="_blank" class="text-green-500">
|
|
||||||
新手请看必看章节2:
|
|
||||||
</a>
|
|
||||||
</view>
|
|
||||||
<!-- #endif -->
|
|
||||||
<!-- #ifdef MP-WEIXIN -->
|
|
||||||
<view class="mt-4 text-center">
|
|
||||||
新手请看必看章节2:
|
|
||||||
<text class="text-green-500">
|
|
||||||
https://unibest.tech/base/14-faq
|
|
||||||
</text>
|
|
||||||
</view>
|
|
||||||
<!-- #endif -->
|
|
||||||
|
|
||||||
<view class="mt-4 text-center">
|
|
||||||
<wd-button type="primary">
|
|
||||||
UI组件按钮
|
|
||||||
</wd-button>
|
|
||||||
</view>
|
|
||||||
<view class="mt-4 text-center">
|
|
||||||
UI组件官网:<text class="text-green-500">
|
|
||||||
https://wot-design-uni.cn
|
|
||||||
</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, inject } from 'vue'
|
||||||
|
import { getNavBarHeight } from '@/utils/index'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'Home',
|
||||||
|
})
|
||||||
|
|
||||||
|
let keyword = ref('')
|
||||||
|
let navbarHeight = ref(0)
|
||||||
|
let OSS = inject('OSS') // '@/static/'
|
||||||
|
|
||||||
|
onLoad(() => {
|
||||||
|
navbarHeight.value = getNavBarHeight()
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.home-bg {
|
||||||
|
background-color: $cz-page-background;
|
||||||
|
background-image: url(#{$OSS}images/home_bg.png);
|
||||||
|
background-size: 100% auto;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-box {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
align-items: center;
|
||||||
|
--wot-search-padding: 0;
|
||||||
|
--wot-search-side-padding: 0;
|
||||||
|
|
||||||
|
:deep() {
|
||||||
|
.wd-search {
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wd-search__input {
|
||||||
|
// #ifdef MP
|
||||||
|
padding-left: 32px !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifndef MP
|
||||||
|
padding-right: 0 !important;
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
120
src/pages/my/my.vue
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
<!-- 使用 type="home" 属性设置首页,其他页面不需要设置,默认为page -->
|
||||||
|
<route lang="jsonc" type="page">{
|
||||||
|
"layout": "tabbar",
|
||||||
|
"style": {
|
||||||
|
// 'custom' 表示开启自定义导航栏,默认 'default'
|
||||||
|
"navigationStyle": "custom",
|
||||||
|
"navigationBarTitleText": "我的"
|
||||||
|
}
|
||||||
|
}</route>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="bg-white px-4 pt-2" :style="{ marginTop: `${safeAreaInsets?.top}px` }">
|
||||||
|
<view class="mt-10">
|
||||||
|
<image src="/static/logo.svg" alt="" class="mx-auto block h-28 w-28" />
|
||||||
|
</view>
|
||||||
|
<view class="mt-4 text-center text-4xl text-[#d14328]">
|
||||||
|
unibest
|
||||||
|
</view>
|
||||||
|
<view class="mb-8 mt-2 text-center text-2xl">
|
||||||
|
最好用的 uniapp 开发模板
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="m-auto mb-2 max-w-100 text-justify indent text-4">
|
||||||
|
{{ description }}
|
||||||
|
</view>
|
||||||
|
<view class="mt-4 text-center">
|
||||||
|
作者:
|
||||||
|
<text class="text-green-500">
|
||||||
|
菲鸽
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
<view class="mt-4 text-center">
|
||||||
|
官网地址:
|
||||||
|
<text class="text-green-500">
|
||||||
|
https://unibest.tech
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- #ifdef H5 -->
|
||||||
|
<view class="mt-4 text-center">
|
||||||
|
<a href="https://unibest.tech/base/3-plugin" target="_blank" class="text-green-500">
|
||||||
|
新手请看必看章节1:
|
||||||
|
</a>
|
||||||
|
</view>
|
||||||
|
<!-- #endif -->
|
||||||
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
|
<view class="mt-4 text-center">
|
||||||
|
新手请看必看章节1:
|
||||||
|
<text class="text-green-500">
|
||||||
|
https://unibest.tech/base/3-plugin
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
<!-- #endif -->
|
||||||
|
<!-- #ifdef H5 -->
|
||||||
|
<view class="mt-4 text-center">
|
||||||
|
<a href="https://unibest.tech/base/14-faq" target="_blank" class="text-green-500">
|
||||||
|
新手请看必看章节2:
|
||||||
|
</a>
|
||||||
|
</view>
|
||||||
|
<!-- #endif -->
|
||||||
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
|
<view class="mt-4 text-center">
|
||||||
|
新手请看必看章节2:
|
||||||
|
<text class="text-green-500">
|
||||||
|
https://unibest.tech/base/14-faq
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
<!-- #endif -->
|
||||||
|
|
||||||
|
<view class="mt-4 text-center">
|
||||||
|
<wd-button type="primary">
|
||||||
|
UI组件按钮
|
||||||
|
</wd-button>
|
||||||
|
</view>
|
||||||
|
<view class="mt-4 text-center">
|
||||||
|
UI组件官网:<text class="text-green-500">
|
||||||
|
https://wot-design-uni.cn
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
defineOptions({
|
||||||
|
name: 'Home',
|
||||||
|
})
|
||||||
|
|
||||||
|
// 获取屏幕边界到安全区域距离
|
||||||
|
let safeAreaInsets
|
||||||
|
let systemInfo
|
||||||
|
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
// 微信小程序使用新的API
|
||||||
|
systemInfo = uni.getWindowInfo()
|
||||||
|
safeAreaInsets = systemInfo.safeArea
|
||||||
|
? {
|
||||||
|
top: systemInfo.safeArea.top,
|
||||||
|
right: systemInfo.windowWidth - systemInfo.safeArea.right,
|
||||||
|
bottom: systemInfo.windowHeight - systemInfo.safeArea.bottom,
|
||||||
|
left: systemInfo.safeArea.left,
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifndef MP-WEIXIN
|
||||||
|
// 其他平台继续使用uni API
|
||||||
|
systemInfo = uni.getSystemInfoSync()
|
||||||
|
safeAreaInsets = systemInfo.safeAreaInsets
|
||||||
|
// #endif
|
||||||
|
const author = ref('菲鸽')
|
||||||
|
const description = ref(
|
||||||
|
'unibest 是一个集成了多种工具和技术的 uniapp 开发模板,由 uniapp + Vue3 + Ts + Vite5 + UnoCss + VSCode 构建,模板具有代码提示、自动格式化、统一配置、代码片段等功能,并内置了许多常用的基本组件和基本功能,让你编写 uniapp 拥有 best 体验。',
|
||||||
|
)
|
||||||
|
// 测试 uni API 自动引入
|
||||||
|
onLoad(() => {
|
||||||
|
console.log('项目作者:', author.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('index')
|
||||||
|
</script>
|
||||||
120
src/pages/reserve/reserve.vue
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
<!-- 使用 type="home" 属性设置首页,其他页面不需要设置,默认为page -->
|
||||||
|
<route lang="jsonc" type="page">{
|
||||||
|
"layout": "tabbar",
|
||||||
|
"style": {
|
||||||
|
// 'custom' 表示开启自定义导航栏,默认 'default'
|
||||||
|
"navigationStyle": "custom",
|
||||||
|
"navigationBarTitleText": "预约"
|
||||||
|
}
|
||||||
|
}</route>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="bg-white px-4 pt-2" :style="{ marginTop: `${safeAreaInsets?.top}px` }">
|
||||||
|
<view class="mt-10">
|
||||||
|
<image src="/static/logo.svg" alt="" class="mx-auto block h-28 w-28" />
|
||||||
|
</view>
|
||||||
|
<view class="mt-4 text-center text-4xl text-[#d14328]">
|
||||||
|
unibest
|
||||||
|
</view>
|
||||||
|
<view class="mb-8 mt-2 text-center text-2xl">
|
||||||
|
最好用的 uniapp 开发模板
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="m-auto mb-2 max-w-100 text-justify indent text-4">
|
||||||
|
{{ description }}
|
||||||
|
</view>
|
||||||
|
<view class="mt-4 text-center">
|
||||||
|
作者:
|
||||||
|
<text class="text-green-500">
|
||||||
|
菲鸽
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
<view class="mt-4 text-center">
|
||||||
|
官网地址:
|
||||||
|
<text class="text-green-500">
|
||||||
|
https://unibest.tech
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- #ifdef H5 -->
|
||||||
|
<view class="mt-4 text-center">
|
||||||
|
<a href="https://unibest.tech/base/3-plugin" target="_blank" class="text-green-500">
|
||||||
|
新手请看必看章节1:
|
||||||
|
</a>
|
||||||
|
</view>
|
||||||
|
<!-- #endif -->
|
||||||
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
|
<view class="mt-4 text-center">
|
||||||
|
新手请看必看章节1:
|
||||||
|
<text class="text-green-500">
|
||||||
|
https://unibest.tech/base/3-plugin
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
<!-- #endif -->
|
||||||
|
<!-- #ifdef H5 -->
|
||||||
|
<view class="mt-4 text-center">
|
||||||
|
<a href="https://unibest.tech/base/14-faq" target="_blank" class="text-green-500">
|
||||||
|
新手请看必看章节2:
|
||||||
|
</a>
|
||||||
|
</view>
|
||||||
|
<!-- #endif -->
|
||||||
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
|
<view class="mt-4 text-center">
|
||||||
|
新手请看必看章节2:
|
||||||
|
<text class="text-green-500">
|
||||||
|
https://unibest.tech/base/14-faq
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
<!-- #endif -->
|
||||||
|
|
||||||
|
<view class="mt-4 text-center">
|
||||||
|
<wd-button type="primary">
|
||||||
|
UI组件按钮
|
||||||
|
</wd-button>
|
||||||
|
</view>
|
||||||
|
<view class="mt-4 text-center">
|
||||||
|
UI组件官网:<text class="text-green-500">
|
||||||
|
https://wot-design-uni.cn
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
defineOptions({
|
||||||
|
name: 'Home',
|
||||||
|
})
|
||||||
|
|
||||||
|
// 获取屏幕边界到安全区域距离
|
||||||
|
let safeAreaInsets
|
||||||
|
let systemInfo
|
||||||
|
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
// 微信小程序使用新的API
|
||||||
|
systemInfo = uni.getWindowInfo()
|
||||||
|
safeAreaInsets = systemInfo.safeArea
|
||||||
|
? {
|
||||||
|
top: systemInfo.safeArea.top,
|
||||||
|
right: systemInfo.windowWidth - systemInfo.safeArea.right,
|
||||||
|
bottom: systemInfo.windowHeight - systemInfo.safeArea.bottom,
|
||||||
|
left: systemInfo.safeArea.left,
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifndef MP-WEIXIN
|
||||||
|
// 其他平台继续使用uni API
|
||||||
|
systemInfo = uni.getSystemInfoSync()
|
||||||
|
safeAreaInsets = systemInfo.safeAreaInsets
|
||||||
|
// #endif
|
||||||
|
const author = ref('菲鸽')
|
||||||
|
const description = ref(
|
||||||
|
'unibest 是一个集成了多种工具和技术的 uniapp 开发模板,由 uniapp + Vue3 + Ts + Vite5 + UnoCss + VSCode 构建,模板具有代码提示、自动格式化、统一配置、代码片段等功能,并内置了许多常用的基本组件和基本功能,让你编写 uniapp 拥有 best 体验。',
|
||||||
|
)
|
||||||
|
// 测试 uni API 自动引入
|
||||||
|
onLoad(() => {
|
||||||
|
console.log('项目作者:', author.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('index')
|
||||||
|
</script>
|
||||||
BIN
src/static/icon/icon_arrow_down.png
Normal file
|
After Width: | Height: | Size: 260 B |
BIN
src/static/images/home_bg.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 799 B |
|
Before Width: | Height: | Size: 1.4 KiB |
BIN
src/static/tabbar/home_s.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/static/tabbar/my.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
src/static/tabbar/my_s.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
BIN
src/static/tabbar/reserve.png
Normal file
|
After Width: | Height: | Size: 656 B |
BIN
src/static/tabbar/reserve_s.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
@ -3,12 +3,12 @@ import { createPersistedState } from 'pinia-plugin-persistedstate' // 数据持
|
|||||||
|
|
||||||
const store = createPinia()
|
const store = createPinia()
|
||||||
store.use(
|
store.use(
|
||||||
createPersistedState({
|
createPersistedState({
|
||||||
storage: {
|
storage: {
|
||||||
getItem: uni.getStorageSync,
|
getItem: uni.getStorageSync,
|
||||||
setItem: uni.setStorageSync,
|
setItem: uni.setStorageSync,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
export default store
|
export default store
|
||||||
|
|||||||
@ -2,110 +2,110 @@ import type { IUserInfoVo } from '@/api/types/login'
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import {
|
import {
|
||||||
getUserInfo as _getUserInfo,
|
getUserInfo as _getUserInfo,
|
||||||
login as _login,
|
login as _login,
|
||||||
logout as _logout,
|
logout as _logout,
|
||||||
wxLogin as _wxLogin,
|
wxLogin as _wxLogin,
|
||||||
getWxCode,
|
getWxCode,
|
||||||
} from '@/api/login'
|
} from '@/api/login'
|
||||||
import { toast } from '@/utils/toast'
|
import { toast } from '@/utils/toast'
|
||||||
|
|
||||||
// 初始化状态
|
// 初始化状态
|
||||||
const userInfoState: IUserInfoVo = {
|
const userInfoState: IUserInfoVo = {
|
||||||
id: 0,
|
id: 0,
|
||||||
username: '',
|
username: '',
|
||||||
avatar: '/static/images/default-avatar.png',
|
avatar: '/static/images/default-avatar.png',
|
||||||
token: '',
|
token: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useUserStore = defineStore(
|
export const useUserStore = defineStore(
|
||||||
'user',
|
'user',
|
||||||
() => {
|
() => {
|
||||||
// 定义用户信息
|
// 定义用户信息
|
||||||
const userInfo = ref<IUserInfoVo>({ ...userInfoState })
|
const userInfo = ref<IUserInfoVo>({ ...userInfoState })
|
||||||
// 设置用户信息
|
// 设置用户信息
|
||||||
const setUserInfo = (val: IUserInfoVo) => {
|
const setUserInfo = (val: IUserInfoVo) => {
|
||||||
console.log('设置用户信息', val)
|
console.log('设置用户信息', val)
|
||||||
// 若头像为空 则使用默认头像
|
// 若头像为空 则使用默认头像
|
||||||
if (!val.avatar) {
|
if (!val.avatar) {
|
||||||
val.avatar = userInfoState.avatar
|
val.avatar = userInfoState.avatar
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val.avatar = 'https://oss.laf.run/ukw0y1-site/avatar.jpg?feige'
|
val.avatar = 'https://oss.laf.run/ukw0y1-site/avatar.jpg?feige'
|
||||||
}
|
}
|
||||||
userInfo.value = val
|
userInfo.value = val
|
||||||
}
|
}
|
||||||
const setUserAvatar = (avatar: string) => {
|
const setUserAvatar = (avatar: string) => {
|
||||||
userInfo.value.avatar = avatar
|
userInfo.value.avatar = avatar
|
||||||
console.log('设置用户头像', avatar)
|
console.log('设置用户头像', avatar)
|
||||||
console.log('userInfo', userInfo.value)
|
console.log('userInfo', userInfo.value)
|
||||||
}
|
}
|
||||||
// 删除用户信息
|
// 删除用户信息
|
||||||
const removeUserInfo = () => {
|
const removeUserInfo = () => {
|
||||||
userInfo.value = { ...userInfoState }
|
userInfo.value = { ...userInfoState }
|
||||||
uni.removeStorageSync('userInfo')
|
uni.removeStorageSync('userInfo')
|
||||||
uni.removeStorageSync('token')
|
uni.removeStorageSync('token')
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 获取用户信息
|
* 获取用户信息
|
||||||
*/
|
*/
|
||||||
const getUserInfo = async () => {
|
const getUserInfo = async () => {
|
||||||
const res = await _getUserInfo()
|
const res = await _getUserInfo()
|
||||||
const userInfo = res.data
|
const userInfo = res.data
|
||||||
setUserInfo(userInfo)
|
setUserInfo(userInfo)
|
||||||
uni.setStorageSync('userInfo', userInfo)
|
uni.setStorageSync('userInfo', userInfo)
|
||||||
uni.setStorageSync('token', userInfo.token)
|
uni.setStorageSync('token', userInfo.token)
|
||||||
// TODO 这里可以增加获取用户路由的方法 根据用户的角色动态生成路由
|
// TODO 这里可以增加获取用户路由的方法 根据用户的角色动态生成路由
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 用户登录
|
* 用户登录
|
||||||
* @param credentials 登录参数
|
* @param credentials 登录参数
|
||||||
* @returns R<IUserLogin>
|
* @returns R<IUserLogin>
|
||||||
*/
|
*/
|
||||||
const login = async (credentials: {
|
const login = async (credentials: {
|
||||||
username: string
|
username: string
|
||||||
password: string
|
password: string
|
||||||
code: string
|
code: string
|
||||||
uuid: string
|
uuid: string
|
||||||
}) => {
|
}) => {
|
||||||
const res = await _login(credentials)
|
const res = await _login(credentials)
|
||||||
console.log('登录信息', res)
|
console.log('登录信息', res)
|
||||||
toast.success('登录成功')
|
toast.success('登录成功')
|
||||||
await getUserInfo()
|
await getUserInfo()
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 退出登录 并 删除用户信息
|
* 退出登录 并 删除用户信息
|
||||||
*/
|
*/
|
||||||
const logout = async () => {
|
const logout = async () => {
|
||||||
_logout()
|
_logout()
|
||||||
removeUserInfo()
|
removeUserInfo()
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 微信登录
|
* 微信登录
|
||||||
*/
|
*/
|
||||||
const wxLogin = async () => {
|
const wxLogin = async () => {
|
||||||
// 获取微信小程序登录的code
|
// 获取微信小程序登录的code
|
||||||
const data = await getWxCode()
|
const data = await getWxCode()
|
||||||
console.log('微信登录code', data)
|
console.log('微信登录code', data)
|
||||||
|
|
||||||
const res = await _wxLogin(data)
|
const res = await _wxLogin(data)
|
||||||
await getUserInfo()
|
await getUserInfo()
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
userInfo,
|
userInfo,
|
||||||
login,
|
login,
|
||||||
wxLogin,
|
wxLogin,
|
||||||
getUserInfo,
|
getUserInfo,
|
||||||
setUserAvatar,
|
setUserAvatar,
|
||||||
logout,
|
logout,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
persist: true,
|
persist: true,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@ -3,11 +3,11 @@ import type { TabBar } from '@uni-helper/vite-plugin-uni-pages'
|
|||||||
type NativeTabBarItem = TabBar['list'][0]
|
type NativeTabBarItem = TabBar['list'][0]
|
||||||
|
|
||||||
type CustomTabBarItem = (Pick<NativeTabBarItem, 'text' | 'pagePath'> & {
|
type CustomTabBarItem = (Pick<NativeTabBarItem, 'text' | 'pagePath'> & {
|
||||||
iconType: 'uniUi' | 'uiLib' | 'unocss' | 'iconfont' | 'image' // 不建议用 image 模式,需要配置2张图
|
iconType: 'uniUi' | 'uiLib' | 'unocss' | 'iconfont' | 'image' // 不建议用 image 模式,需要配置2张图
|
||||||
icon: any // 其实是 string 类型,这里是为了避免 ts 报错 (tabbar/index.vue 里面 uni-icons 那行)
|
icon: any // 其实是 string 类型,这里是为了避免 ts 报错 (tabbar/index.vue 里面 uni-icons 那行)
|
||||||
activeIcon?: string // 只有在 image 模式下才需要,传递的是高亮的图片(PS: 不建议用 image 模式)
|
activeIcon?: string // 只有在 image 模式下才需要,传递的是高亮的图片(PS: 不建议用 image 模式)
|
||||||
badge?: number | 'dot' // badge 显示一个数字或 小红点(样式可以直接在 tabbar/index.vue 里面修改)
|
badge?: number | 'dot' // badge 显示一个数字或 小红点(样式可以直接在 tabbar/index.vue 里面修改)
|
||||||
isBulge?: boolean // 是否是中间的鼓包tabbarItem
|
isBulge?: boolean // 是否是中间的鼓包tabbarItem
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -20,14 +20,14 @@ type CustomTabBarItem = (Pick<NativeTabBarItem, 'text' | 'pagePath'> & {
|
|||||||
* 温馨提示:本文件的任何代码更改了之后,都需要重新运行,否则 pages.json 不会更新导致配置不生效
|
* 温馨提示:本文件的任何代码更改了之后,都需要重新运行,否则 pages.json 不会更新导致配置不生效
|
||||||
*/
|
*/
|
||||||
export const TABBAR_MAP = {
|
export const TABBAR_MAP = {
|
||||||
NO_TABBAR: 0,
|
NO_TABBAR: 0,
|
||||||
NATIVE_TABBAR: 1,
|
NATIVE_TABBAR: 1,
|
||||||
CUSTOM_TABBAR_WITH_CACHE: 2,
|
CUSTOM_TABBAR_WITH_CACHE: 2,
|
||||||
CUSTOM_TABBAR_WITHOUT_CACHE: 3,
|
CUSTOM_TABBAR_WITHOUT_CACHE: 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: 1/3. 通过这里切换使用tabbar的策略
|
// TODO: 1/3. 通过这里切换使用tabbar的策略
|
||||||
export const selectedTabbarStrategy = TABBAR_MAP.CUSTOM_TABBAR_WITH_CACHE
|
export const selectedTabbarStrategy = TABBAR_MAP.NATIVE_TABBAR
|
||||||
|
|
||||||
// TODO: 2/3. 更新下面的 tabbar 配置
|
// TODO: 2/3. 更新下面的 tabbar 配置
|
||||||
// 如果是使用 NO_TABBAR(0),nativeTabbarList 和 customTabbarList 都不生效(里面的配置不用管)
|
// 如果是使用 NO_TABBAR(0),nativeTabbarList 和 customTabbarList 都不生效(里面的配置不用管)
|
||||||
@ -35,90 +35,102 @@ export const selectedTabbarStrategy = TABBAR_MAP.CUSTOM_TABBAR_WITH_CACHE
|
|||||||
// 如果是使用 CUSTOM_TABBAR(2,3),nativeTabbarList 不生效(里面的配置不用管)
|
// 如果是使用 CUSTOM_TABBAR(2,3),nativeTabbarList 不生效(里面的配置不用管)
|
||||||
// pagePath 是 nativeTabbarList 和 customTabbarList 的关联点,如果没有对应上,会有问题!!
|
// pagePath 是 nativeTabbarList 和 customTabbarList 的关联点,如果没有对应上,会有问题!!
|
||||||
export const nativeTabbarList: NativeTabBarItem[] = [
|
export const nativeTabbarList: NativeTabBarItem[] = [
|
||||||
{
|
{
|
||||||
iconPath: 'static/tabbar/home.png',
|
iconPath: 'static/tabbar/home.png',
|
||||||
selectedIconPath: 'static/tabbar/homeHL.png',
|
selectedIconPath: 'static/tabbar/home_s.png',
|
||||||
pagePath: 'pages/index/index',
|
pagePath: 'pages/index/index',
|
||||||
text: '首页',
|
text: '首页',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
iconPath: 'static/tabbar/example.png',
|
iconPath: 'static/tabbar/reserve.png',
|
||||||
selectedIconPath: 'static/tabbar/exampleHL.png',
|
selectedIconPath: 'static/tabbar/reserve_s.png',
|
||||||
pagePath: 'pages/about/about',
|
pagePath: 'pages/reserve/reserve',
|
||||||
text: '关于',
|
text: '预约',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
iconPath: 'static/tabbar/my.png',
|
||||||
|
selectedIconPath: 'static/tabbar/my_s.png',
|
||||||
|
pagePath: 'pages/my/my',
|
||||||
|
text: '我的',
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// iconPath: 'static/tabbar/example.png',
|
||||||
|
// selectedIconPath: 'static/tabbar/exampleHL.png',
|
||||||
|
// pagePath: 'pages/about/about',
|
||||||
|
// text: '关于',
|
||||||
|
// },
|
||||||
]
|
]
|
||||||
|
|
||||||
// pagePath 是 nativeTabbarList 和 customTabbarList 的关联点,如果没有对应上,会有问题!!
|
// pagePath 是 nativeTabbarList 和 customTabbarList 的关联点,如果没有对应上,会有问题!!
|
||||||
// 如果希望通过接口调用 customTabbarList,可以在 tabbar/index.vue 文件里面调用接口
|
// 如果希望通过接口调用 customTabbarList,可以在 tabbar/index.vue 文件里面调用接口
|
||||||
// 本文件因为需要提前编译生成 pages.json, 接口拦截还不生效,无法正常调用接口
|
// 本文件因为需要提前编译生成 pages.json, 接口拦截还不生效,无法正常调用接口
|
||||||
export const customTabbarList: CustomTabBarItem[] = [
|
export const customTabbarList: CustomTabBarItem[] = [
|
||||||
{
|
{
|
||||||
// text 和 pagePath 可以自己直接写,也可以通过索引从 nativeTabbarList 中获取
|
// text 和 pagePath 可以自己直接写,也可以通过索引从 nativeTabbarList 中获取
|
||||||
text: '首页',
|
text: '首页',
|
||||||
pagePath: 'pages/index/index', // pagePath 是两者的关联点
|
pagePath: 'pages/index/index', // pagePath 是两者的关联点
|
||||||
// 本框架内置了 uniapp 官方UI库 (uni-ui)的图标库
|
// 本框架内置了 uniapp 官方UI库 (uni-ui)的图标库
|
||||||
// 使用方式如:<uni-icons type="home" size="30"/>
|
// 使用方式如:<uni-icons type="home" size="30"/>
|
||||||
// 图标列表地址:https://uniapp.dcloud.net.cn/component/uniui/uni-icons.html
|
// 图标列表地址:https://uniapp.dcloud.net.cn/component/uniui/uni-icons.html
|
||||||
iconType: 'uniUi',
|
iconType: 'uniUi',
|
||||||
icon: 'home',
|
icon: 'home',
|
||||||
// badge: 'dot',
|
// badge: 'dot',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: nativeTabbarList[1].text,
|
text: nativeTabbarList[1].text,
|
||||||
pagePath: nativeTabbarList[1].pagePath, // pagePath 是两者的关联点
|
pagePath: nativeTabbarList[1].pagePath, // pagePath 是两者的关联点
|
||||||
// 注意 unocss 图标需要如下处理:(二选一)
|
// 注意 unocss 图标需要如下处理:(二选一)
|
||||||
// 1)在fg-tabbar.vue页面上引入一下并注释掉(见tabbar/index.vue代码第2行)
|
// 1)在fg-tabbar.vue页面上引入一下并注释掉(见tabbar/index.vue代码第2行)
|
||||||
// 2)配置到 unocss.config.ts 的 safelist 中
|
// 2)配置到 unocss.config.ts 的 safelist 中
|
||||||
iconType: 'unocss',
|
iconType: 'unocss',
|
||||||
icon: 'i-carbon-code',
|
icon: 'i-carbon-code',
|
||||||
// badge: 10,
|
// badge: 10,
|
||||||
},
|
},
|
||||||
|
|
||||||
// {
|
// {
|
||||||
// pagePath: 'pages/mine/index',
|
// pagePath: 'pages/mine/index',
|
||||||
// text: '我的',
|
// text: '我的',
|
||||||
// // 注意 iconfont 图标需要额外加上 'iconfont',如下
|
// // 注意 iconfont 图标需要额外加上 'iconfont',如下
|
||||||
// iconType: 'iconfont',
|
// iconType: 'iconfont',
|
||||||
// icon: 'iconfont icon-my',
|
// icon: 'iconfont icon-my',
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
// pagePath: 'pages/index/index',
|
// pagePath: 'pages/index/index',
|
||||||
// text: '首页',
|
// text: '首页',
|
||||||
// // 使用 ‘image’时,需要配置 icon + iconActive 2张图片(不推荐)
|
// // 使用 ‘image’时,需要配置 icon + iconActive 2张图片(不推荐)
|
||||||
// // 既然已经用了自定义tabbar了,就不建议用图片了,所以不推荐
|
// // 既然已经用了自定义tabbar了,就不建议用图片了,所以不推荐
|
||||||
// iconType: 'image',
|
// iconType: 'image',
|
||||||
// icon: '/static/tabbar/home.png',
|
// icon: '/static/tabbar/home.png',
|
||||||
// iconActive: '/static/tabbar/homeHL.png',
|
// iconActive: '/static/tabbar/homeHL.png',
|
||||||
// },
|
// },
|
||||||
]
|
]
|
||||||
|
|
||||||
// NATIVE_TABBAR(1) 和 CUSTOM_TABBAR_WITH_CACHE(2) 时,需要tabbar缓存
|
// NATIVE_TABBAR(1) 和 CUSTOM_TABBAR_WITH_CACHE(2) 时,需要tabbar缓存
|
||||||
/** 是否启用 tabbar 缓存 */
|
/** 是否启用 tabbar 缓存 */
|
||||||
export const tabbarCacheEnable
|
export const tabbarCacheEnable
|
||||||
= [TABBAR_MAP.NATIVE_TABBAR, TABBAR_MAP.CUSTOM_TABBAR_WITH_CACHE].includes(selectedTabbarStrategy)
|
= [TABBAR_MAP.NATIVE_TABBAR, TABBAR_MAP.CUSTOM_TABBAR_WITH_CACHE].includes(selectedTabbarStrategy)
|
||||||
|
|
||||||
// CUSTOM_TABBAR_WITH_CACHE(2) 和 CUSTOM_TABBAR_WITHOUT_CACHE(3) 时,启用自定义tabbar
|
// CUSTOM_TABBAR_WITH_CACHE(2) 和 CUSTOM_TABBAR_WITHOUT_CACHE(3) 时,启用自定义tabbar
|
||||||
/** 是否启用自定义 tabbar */
|
/** 是否启用自定义 tabbar */
|
||||||
export const customTabbarEnable
|
export const customTabbarEnable
|
||||||
= [TABBAR_MAP.CUSTOM_TABBAR_WITH_CACHE, TABBAR_MAP.CUSTOM_TABBAR_WITHOUT_CACHE].includes(selectedTabbarStrategy)
|
= [TABBAR_MAP.CUSTOM_TABBAR_WITH_CACHE, TABBAR_MAP.CUSTOM_TABBAR_WITHOUT_CACHE].includes(selectedTabbarStrategy)
|
||||||
|
|
||||||
// CUSTOM_TABBAR_WITH_CACHE(2)时,需要隐藏原生tabbar
|
// CUSTOM_TABBAR_WITH_CACHE(2)时,需要隐藏原生tabbar
|
||||||
/** 是否需要隐藏原生 tabbar */
|
/** 是否需要隐藏原生 tabbar */
|
||||||
export const nativeTabbarNeedHide = selectedTabbarStrategy === TABBAR_MAP.CUSTOM_TABBAR_WITH_CACHE
|
export const nativeTabbarNeedHide = selectedTabbarStrategy === TABBAR_MAP.CUSTOM_TABBAR_WITH_CACHE
|
||||||
|
|
||||||
const _tabbar: TabBar = {
|
const _tabbar: TabBar = {
|
||||||
// 只有微信小程序支持 custom。App 和 H5 不生效
|
// 只有微信小程序支持 custom。App 和 H5 不生效
|
||||||
custom: selectedTabbarStrategy === TABBAR_MAP.CUSTOM_TABBAR_WITH_CACHE,
|
custom: selectedTabbarStrategy === TABBAR_MAP.CUSTOM_TABBAR_WITH_CACHE,
|
||||||
color: '#999999',
|
color: '#999999',
|
||||||
selectedColor: '#018d71',
|
selectedColor: '#018d71',
|
||||||
backgroundColor: '#F8F8F8',
|
backgroundColor: '#F8F8F8',
|
||||||
borderStyle: 'black',
|
borderStyle: 'black',
|
||||||
height: '50px',
|
height: '50px',
|
||||||
fontSize: '10px',
|
fontSize: '10px',
|
||||||
iconWidth: '24px',
|
iconWidth: '24px',
|
||||||
spacing: '3px',
|
spacing: '3px',
|
||||||
list: nativeTabbarList as unknown as TabBar['list'],
|
list: nativeTabbarList as unknown as TabBar['list'],
|
||||||
}
|
}
|
||||||
|
|
||||||
export const tabbarList = nativeTabbarList
|
export const tabbarList = nativeTabbarList
|
||||||
|
|||||||
@ -75,3 +75,6 @@ $uni-color-subtitle: #555; // 二级标题颜色
|
|||||||
$uni-font-size-subtitle: 18px;
|
$uni-font-size-subtitle: 18px;
|
||||||
$uni-color-paragraph: #3f536e; // 文章段落颜色
|
$uni-color-paragraph: #3f536e; // 文章段落颜色
|
||||||
$uni-font-size-paragraph: 15px;
|
$uni-font-size-paragraph: 15px;
|
||||||
|
|
||||||
|
$cz-page-background: #F6F7F9; // 页面背景色
|
||||||
|
$OSS: '/src/static/';
|
||||||
|
|||||||
@ -3,11 +3,11 @@ import { tabbarList } from '@/tabbar/config'
|
|||||||
import { isMpWeixin } from './platform'
|
import { isMpWeixin } from './platform'
|
||||||
|
|
||||||
export function getLastPage() {
|
export function getLastPage() {
|
||||||
// getCurrentPages() 至少有1个元素,所以不再额外判断
|
// getCurrentPages() 至少有1个元素,所以不再额外判断
|
||||||
// const lastPage = getCurrentPages().at(-1)
|
// const lastPage = getCurrentPages().at(-1)
|
||||||
// 上面那个在低版本安卓中打包会报错,所以改用下面这个【虽然我加了 src/interceptions/prototype.ts,但依然报错】
|
// 上面那个在低版本安卓中打包会报错,所以改用下面这个【虽然我加了 src/interceptions/prototype.ts,但依然报错】
|
||||||
const pages = getCurrentPages()
|
const pages = getCurrentPages()
|
||||||
return pages[pages.length - 1]
|
return pages[pages.length - 1]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -16,25 +16,25 @@ export function getLastPage() {
|
|||||||
* redirectPath 如 '/pages/demo/base/route-interceptor'
|
* redirectPath 如 '/pages/demo/base/route-interceptor'
|
||||||
*/
|
*/
|
||||||
export function currRoute() {
|
export function currRoute() {
|
||||||
const lastPage = getLastPage()
|
const lastPage = getLastPage()
|
||||||
const currRoute = (lastPage as any).$page
|
const currRoute = (lastPage as any).$page
|
||||||
// console.log('lastPage.$page:', currRoute)
|
// console.log('lastPage.$page:', currRoute)
|
||||||
// console.log('lastPage.$page.fullpath:', currRoute.fullPath)
|
// console.log('lastPage.$page.fullpath:', currRoute.fullPath)
|
||||||
// console.log('lastPage.$page.options:', currRoute.options)
|
// console.log('lastPage.$page.options:', currRoute.options)
|
||||||
// console.log('lastPage.options:', (lastPage as any).options)
|
// console.log('lastPage.options:', (lastPage as any).options)
|
||||||
// 经过多端测试,只有 fullPath 靠谱,其他都不靠谱
|
// 经过多端测试,只有 fullPath 靠谱,其他都不靠谱
|
||||||
const { fullPath } = currRoute as { fullPath: string }
|
const { fullPath } = currRoute as { fullPath: string }
|
||||||
// console.log(fullPath)
|
// console.log(fullPath)
|
||||||
// eg: /pages/login/index?redirect=%2Fpages%2Fdemo%2Fbase%2Froute-interceptor (小程序)
|
// eg: /pages/login/index?redirect=%2Fpages%2Fdemo%2Fbase%2Froute-interceptor (小程序)
|
||||||
// eg: /pages/login/index?redirect=%2Fpages%2Froute-interceptor%2Findex%3Fname%3Dfeige%26age%3D30(h5)
|
// eg: /pages/login/index?redirect=%2Fpages%2Froute-interceptor%2Findex%3Fname%3Dfeige%26age%3D30(h5)
|
||||||
return getUrlObj(fullPath)
|
return getUrlObj(fullPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureDecodeURIComponent(url: string) {
|
function ensureDecodeURIComponent(url: string) {
|
||||||
if (url.startsWith('%')) {
|
if (url.startsWith('%')) {
|
||||||
return ensureDecodeURIComponent(decodeURIComponent(url))
|
return ensureDecodeURIComponent(decodeURIComponent(url))
|
||||||
}
|
}
|
||||||
return url
|
return url
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 解析 url 得到 path 和 query
|
* 解析 url 得到 path 和 query
|
||||||
@ -42,22 +42,22 @@ function ensureDecodeURIComponent(url: string) {
|
|||||||
* 输出: {path: /pages/login/index, query: {redirect: /pages/demo/base/route-interceptor}}
|
* 输出: {path: /pages/login/index, query: {redirect: /pages/demo/base/route-interceptor}}
|
||||||
*/
|
*/
|
||||||
export function getUrlObj(url: string) {
|
export function getUrlObj(url: string) {
|
||||||
const [path, queryStr] = url.split('?')
|
const [path, queryStr] = url.split('?')
|
||||||
// console.log(path, queryStr)
|
// console.log(path, queryStr)
|
||||||
|
|
||||||
if (!queryStr) {
|
if (!queryStr) {
|
||||||
return {
|
return {
|
||||||
path,
|
path,
|
||||||
query: {},
|
query: {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const query: Record<string, string> = {}
|
const query: Record<string, string> = {}
|
||||||
queryStr.split('&').forEach((item) => {
|
queryStr.split('&').forEach((item) => {
|
||||||
const [key, value] = item.split('=')
|
const [key, value] = item.split('=')
|
||||||
// console.log(key, value)
|
// console.log(key, value)
|
||||||
query[key] = ensureDecodeURIComponent(value) // 这里需要统一 decodeURIComponent 一下,可以兼容h5和微信y
|
query[key] = ensureDecodeURIComponent(value) // 这里需要统一 decodeURIComponent 一下,可以兼容h5和微信y
|
||||||
})
|
})
|
||||||
return { path, query }
|
return { path, query }
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 得到所有的需要登录的 pages,包括主包和分包的
|
* 得到所有的需要登录的 pages,包括主包和分包的
|
||||||
@ -65,49 +65,49 @@ export function getUrlObj(url: string) {
|
|||||||
* 如果没有传 key,则表示所有的 pages,如果传递了 key, 则表示通过 key 过滤
|
* 如果没有传 key,则表示所有的 pages,如果传递了 key, 则表示通过 key 过滤
|
||||||
*/
|
*/
|
||||||
export function getAllPages(key = 'needLogin') {
|
export function getAllPages(key = 'needLogin') {
|
||||||
// 这里处理主包
|
// 这里处理主包
|
||||||
const mainPages = pages
|
const mainPages = pages
|
||||||
.filter(page => !key || page[key])
|
.filter(page => !key || page[key])
|
||||||
.map(page => ({
|
.map(page => ({
|
||||||
...page,
|
...page,
|
||||||
path: `/${page.path}`,
|
path: `/${page.path}`,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// 这里处理分包
|
// 这里处理分包
|
||||||
const subPages: any[] = []
|
const subPages: any[] = []
|
||||||
subPackages.forEach((subPageObj) => {
|
subPackages.forEach((subPageObj) => {
|
||||||
// console.log(subPageObj)
|
// console.log(subPageObj)
|
||||||
const { root } = subPageObj
|
const { root } = subPageObj
|
||||||
|
|
||||||
subPageObj.pages
|
subPageObj.pages
|
||||||
.filter(page => !key || page[key])
|
.filter(page => !key || page[key])
|
||||||
.forEach((page: { path: string } & Record<string, any>) => {
|
.forEach((page: { path: string } & Record<string, any>) => {
|
||||||
subPages.push({
|
subPages.push({
|
||||||
...page,
|
...page,
|
||||||
path: `/${root}/${page.path}`,
|
path: `/${root}/${page.path}`,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
const result = [...mainPages, ...subPages]
|
const result = [...mainPages, ...subPages]
|
||||||
// console.log(`getAllPages by ${key} result: `, result)
|
// console.log(`getAllPages by ${key} result: `, result)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isCurrentPageTabbar() {
|
export function isCurrentPageTabbar() {
|
||||||
const routeObj = currRoute()
|
const routeObj = currRoute()
|
||||||
return tabbarList.some(item => `/${item.pagePath}` === routeObj.path)
|
return tabbarList.some(item => `/${item.pagePath}` === routeObj.path)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCurrentPageI18nKey() {
|
export function getCurrentPageI18nKey() {
|
||||||
const routeObj = currRoute()
|
const routeObj = currRoute()
|
||||||
const currPage = pages.find(page => `/${page.path}` === routeObj.path)
|
const currPage = pages.find(page => `/${page.path}` === routeObj.path)
|
||||||
if (!currPage) {
|
if (!currPage) {
|
||||||
console.warn('路由不正确')
|
console.warn('路由不正确')
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
console.log(currPage)
|
console.log(currPage)
|
||||||
console.log(currPage.style.navigationBarTitleText)
|
console.log(currPage.style.navigationBarTitleText)
|
||||||
return currPage.style.navigationBarTitleText
|
return currPage.style.navigationBarTitleText
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -126,65 +126,75 @@ export const needLoginPages: string[] = getAllPages('needLogin').map(page => pag
|
|||||||
* 根据微信小程序当前环境,判断应该获取的 baseUrl
|
* 根据微信小程序当前环境,判断应该获取的 baseUrl
|
||||||
*/
|
*/
|
||||||
export function getEnvBaseUrl() {
|
export function getEnvBaseUrl() {
|
||||||
// 请求基准地址
|
// 请求基准地址
|
||||||
let baseUrl = import.meta.env.VITE_SERVER_BASEURL
|
let baseUrl = import.meta.env.VITE_SERVER_BASEURL
|
||||||
|
|
||||||
// # 有些同学可能需要在微信小程序里面根据 develop、trial、release 分别设置上传地址,参考代码如下。
|
// # 有些同学可能需要在微信小程序里面根据 develop、trial、release 分别设置上传地址,参考代码如下。
|
||||||
const VITE_SERVER_BASEURL__WEIXIN_DEVELOP = 'https://ukw0y1.laf.run'
|
const VITE_SERVER_BASEURL__WEIXIN_DEVELOP = 'https://ukw0y1.laf.run'
|
||||||
const VITE_SERVER_BASEURL__WEIXIN_TRIAL = 'https://ukw0y1.laf.run'
|
const VITE_SERVER_BASEURL__WEIXIN_TRIAL = 'https://ukw0y1.laf.run'
|
||||||
const VITE_SERVER_BASEURL__WEIXIN_RELEASE = 'https://ukw0y1.laf.run'
|
const VITE_SERVER_BASEURL__WEIXIN_RELEASE = 'https://ukw0y1.laf.run'
|
||||||
|
|
||||||
// 微信小程序端环境区分
|
// 微信小程序端环境区分
|
||||||
if (isMpWeixin) {
|
if (isMpWeixin) {
|
||||||
const {
|
const {
|
||||||
miniProgram: { envVersion },
|
miniProgram: { envVersion },
|
||||||
} = uni.getAccountInfoSync()
|
} = uni.getAccountInfoSync()
|
||||||
|
|
||||||
switch (envVersion) {
|
switch (envVersion) {
|
||||||
case 'develop':
|
case 'develop':
|
||||||
baseUrl = VITE_SERVER_BASEURL__WEIXIN_DEVELOP || baseUrl
|
baseUrl = VITE_SERVER_BASEURL__WEIXIN_DEVELOP || baseUrl
|
||||||
break
|
break
|
||||||
case 'trial':
|
case 'trial':
|
||||||
baseUrl = VITE_SERVER_BASEURL__WEIXIN_TRIAL || baseUrl
|
baseUrl = VITE_SERVER_BASEURL__WEIXIN_TRIAL || baseUrl
|
||||||
break
|
break
|
||||||
case 'release':
|
case 'release':
|
||||||
baseUrl = VITE_SERVER_BASEURL__WEIXIN_RELEASE || baseUrl
|
baseUrl = VITE_SERVER_BASEURL__WEIXIN_RELEASE || baseUrl
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return baseUrl
|
return baseUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据微信小程序当前环境,判断应该获取的 UPLOAD_BASEURL
|
* 根据微信小程序当前环境,判断应该获取的 UPLOAD_BASEURL
|
||||||
*/
|
*/
|
||||||
export function getEnvBaseUploadUrl() {
|
export function getEnvBaseUploadUrl() {
|
||||||
// 请求基准地址
|
// 请求基准地址
|
||||||
let baseUploadUrl = import.meta.env.VITE_UPLOAD_BASEURL
|
let baseUploadUrl = import.meta.env.VITE_UPLOAD_BASEURL
|
||||||
|
|
||||||
const VITE_UPLOAD_BASEURL__WEIXIN_DEVELOP = 'https://ukw0y1.laf.run/upload'
|
const VITE_UPLOAD_BASEURL__WEIXIN_DEVELOP = 'https://ukw0y1.laf.run/upload'
|
||||||
const VITE_UPLOAD_BASEURL__WEIXIN_TRIAL = 'https://ukw0y1.laf.run/upload'
|
const VITE_UPLOAD_BASEURL__WEIXIN_TRIAL = 'https://ukw0y1.laf.run/upload'
|
||||||
const VITE_UPLOAD_BASEURL__WEIXIN_RELEASE = 'https://ukw0y1.laf.run/upload'
|
const VITE_UPLOAD_BASEURL__WEIXIN_RELEASE = 'https://ukw0y1.laf.run/upload'
|
||||||
|
|
||||||
// 微信小程序端环境区分
|
// 微信小程序端环境区分
|
||||||
if (isMpWeixin) {
|
if (isMpWeixin) {
|
||||||
const {
|
const {
|
||||||
miniProgram: { envVersion },
|
miniProgram: { envVersion },
|
||||||
} = uni.getAccountInfoSync()
|
} = uni.getAccountInfoSync()
|
||||||
|
|
||||||
switch (envVersion) {
|
switch (envVersion) {
|
||||||
case 'develop':
|
case 'develop':
|
||||||
baseUploadUrl = VITE_UPLOAD_BASEURL__WEIXIN_DEVELOP || baseUploadUrl
|
baseUploadUrl = VITE_UPLOAD_BASEURL__WEIXIN_DEVELOP || baseUploadUrl
|
||||||
break
|
break
|
||||||
case 'trial':
|
case 'trial':
|
||||||
baseUploadUrl = VITE_UPLOAD_BASEURL__WEIXIN_TRIAL || baseUploadUrl
|
baseUploadUrl = VITE_UPLOAD_BASEURL__WEIXIN_TRIAL || baseUploadUrl
|
||||||
break
|
break
|
||||||
case 'release':
|
case 'release':
|
||||||
baseUploadUrl = VITE_UPLOAD_BASEURL__WEIXIN_RELEASE || baseUploadUrl
|
baseUploadUrl = VITE_UPLOAD_BASEURL__WEIXIN_RELEASE || baseUploadUrl
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return baseUploadUrl
|
return baseUploadUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getNavBarHeight() {
|
||||||
|
const systemInfo = uni.getSystemInfoSync()
|
||||||
|
const statusBarHeight = systemInfo.statusBarHeight; // 状态栏高度(单位:px)
|
||||||
|
const titleBarHeight = 44; // 默认导航栏标题高度(iOS/Android 一般为 44px)
|
||||||
|
const navbarHeight = statusBarHeight + titleBarHeight; // 完整的导航栏高度
|
||||||
|
|
||||||
|
console.log("🚀 ~ getNavBarHeight ~ navbarHeight:", navbarHeight)
|
||||||
|
return navbarHeight
|
||||||
|
}
|
||||||
@ -20,8 +20,8 @@ import { toast } from './toast'
|
|||||||
* 上传文件的URL配置
|
* 上传文件的URL配置
|
||||||
*/
|
*/
|
||||||
export const uploadFileUrl = {
|
export const uploadFileUrl = {
|
||||||
/** 用户头像上传地址 */
|
/** 用户头像上传地址 */
|
||||||
USER_AVATAR: `${import.meta.env.VITE_SERVER_BASEURL}/user/avatar`,
|
USER_AVATAR: `${import.meta.env.VITE_SERVER_BASEURL}/user/avatar`,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -32,35 +32,35 @@ export const uploadFileUrl = {
|
|||||||
* @param options 上传选项
|
* @param options 上传选项
|
||||||
*/
|
*/
|
||||||
export function useFileUpload<T = string>(url: string, filePath: string, formData: Record<string, any> = {}, options: Omit<UploadOptions, 'sourceType' | 'sizeType' | 'count'> = {}) {
|
export function useFileUpload<T = string>(url: string, filePath: string, formData: Record<string, any> = {}, options: Omit<UploadOptions, 'sourceType' | 'sizeType' | 'count'> = {}) {
|
||||||
return useUpload<T>(
|
return useUpload<T>(
|
||||||
url,
|
url,
|
||||||
formData,
|
formData,
|
||||||
{
|
{
|
||||||
...options,
|
...options,
|
||||||
sourceType: ['album'],
|
sourceType: ['album'],
|
||||||
sizeType: ['original'],
|
sizeType: ['original'],
|
||||||
},
|
},
|
||||||
filePath,
|
filePath,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UploadOptions {
|
export interface UploadOptions {
|
||||||
/** 最大可选择的图片数量,默认为1 */
|
/** 最大可选择的图片数量,默认为1 */
|
||||||
count?: number
|
count?: number
|
||||||
/** 所选的图片的尺寸,original-原图,compressed-压缩图 */
|
/** 所选的图片的尺寸,original-原图,compressed-压缩图 */
|
||||||
sizeType?: Array<'original' | 'compressed'>
|
sizeType?: Array<'original' | 'compressed'>
|
||||||
/** 选择图片的来源,album-相册,camera-相机 */
|
/** 选择图片的来源,album-相册,camera-相机 */
|
||||||
sourceType?: Array<'album' | 'camera'>
|
sourceType?: Array<'album' | 'camera'>
|
||||||
/** 文件大小限制,单位:MB */
|
/** 文件大小限制,单位:MB */
|
||||||
maxSize?: number //
|
maxSize?: number //
|
||||||
/** 上传进度回调函数 */
|
/** 上传进度回调函数 */
|
||||||
onProgress?: (progress: number) => void
|
onProgress?: (progress: number) => void
|
||||||
/** 上传成功回调函数 */
|
/** 上传成功回调函数 */
|
||||||
onSuccess?: (res: Record<string, any>) => void
|
onSuccess?: (res: Record<string, any>) => void
|
||||||
/** 上传失败回调函数 */
|
/** 上传失败回调函数 */
|
||||||
onError?: (err: Error | UniApp.GeneralCallbackResult) => void
|
onError?: (err: Error | UniApp.GeneralCallbackResult) => void
|
||||||
/** 上传完成回调函数(无论成功失败) */
|
/** 上传完成回调函数(无论成功失败) */
|
||||||
onComplete?: () => void
|
onComplete?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -72,150 +72,150 @@ export interface UploadOptions {
|
|||||||
* @returns 上传状态和控制对象
|
* @returns 上传状态和控制对象
|
||||||
*/
|
*/
|
||||||
export function useUpload<T = string>(url: string, formData: Record<string, any> = {}, options: UploadOptions = {},
|
export function useUpload<T = string>(url: string, formData: Record<string, any> = {}, options: UploadOptions = {},
|
||||||
/** 直接传入文件路径,跳过选择器 */
|
/** 直接传入文件路径,跳过选择器 */
|
||||||
directFilePath?: string) {
|
directFilePath?: string) {
|
||||||
/** 上传中状态 */
|
/** 上传中状态 */
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
/** 上传错误状态 */
|
/** 上传错误状态 */
|
||||||
const error = ref(false)
|
const error = ref(false)
|
||||||
/** 上传成功后的响应数据 */
|
/** 上传成功后的响应数据 */
|
||||||
const data = ref<T>()
|
const data = ref<T>()
|
||||||
/** 上传进度(0-100) */
|
/** 上传进度(0-100) */
|
||||||
const progress = ref(0)
|
const progress = ref(0)
|
||||||
|
|
||||||
/** 解构上传选项,设置默认值 */
|
/** 解构上传选项,设置默认值 */
|
||||||
const {
|
const {
|
||||||
/** 最大可选择的图片数量 */
|
/** 最大可选择的图片数量 */
|
||||||
count = 1,
|
count = 1,
|
||||||
/** 所选的图片的尺寸 */
|
/** 所选的图片的尺寸 */
|
||||||
sizeType = ['original', 'compressed'],
|
sizeType = ['original', 'compressed'],
|
||||||
/** 选择图片的来源 */
|
/** 选择图片的来源 */
|
||||||
sourceType = ['album', 'camera'],
|
sourceType = ['album', 'camera'],
|
||||||
/** 文件大小限制(MB) */
|
/** 文件大小限制(MB) */
|
||||||
maxSize = 10,
|
maxSize = 10,
|
||||||
/** 进度回调 */
|
/** 进度回调 */
|
||||||
onProgress,
|
onProgress,
|
||||||
/** 成功回调 */
|
/** 成功回调 */
|
||||||
onSuccess,
|
onSuccess,
|
||||||
/** 失败回调 */
|
/** 失败回调 */
|
||||||
onError,
|
onError,
|
||||||
/** 完成回调 */
|
/** 完成回调 */
|
||||||
onComplete,
|
onComplete,
|
||||||
} = options
|
} = options
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查文件大小是否超过限制
|
* 检查文件大小是否超过限制
|
||||||
* @param size 文件大小(字节)
|
* @param size 文件大小(字节)
|
||||||
* @returns 是否通过检查
|
* @returns 是否通过检查
|
||||||
*/
|
*/
|
||||||
const checkFileSize = (size: number) => {
|
const checkFileSize = (size: number) => {
|
||||||
const sizeInMB = size / 1024 / 1024
|
const sizeInMB = size / 1024 / 1024
|
||||||
if (sizeInMB > maxSize) {
|
if (sizeInMB > maxSize) {
|
||||||
toast.warning(`文件大小不能超过${maxSize}MB`)
|
toast.warning(`文件大小不能超过${maxSize}MB`)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 触发文件选择和上传
|
* 触发文件选择和上传
|
||||||
* 根据平台使用不同的选择器:
|
* 根据平台使用不同的选择器:
|
||||||
* - 微信小程序使用 chooseMedia
|
* - 微信小程序使用 chooseMedia
|
||||||
* - 其他平台使用 chooseImage
|
* - 其他平台使用 chooseImage
|
||||||
*/
|
*/
|
||||||
const run = () => {
|
const run = () => {
|
||||||
if (directFilePath) {
|
if (directFilePath) {
|
||||||
// 直接使用传入的文件路径
|
// 直接使用传入的文件路径
|
||||||
loading.value = true
|
loading.value = true
|
||||||
progress.value = 0
|
progress.value = 0
|
||||||
uploadFile<T>({
|
uploadFile<T>({
|
||||||
url,
|
url,
|
||||||
tempFilePath: directFilePath,
|
tempFilePath: directFilePath,
|
||||||
formData,
|
formData,
|
||||||
data,
|
data,
|
||||||
error,
|
error,
|
||||||
loading,
|
loading,
|
||||||
progress,
|
progress,
|
||||||
onProgress,
|
onProgress,
|
||||||
onSuccess,
|
onSuccess,
|
||||||
onError,
|
onError,
|
||||||
onComplete,
|
onComplete,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// #ifdef MP-WEIXIN
|
// #ifdef MP-WEIXIN
|
||||||
// 微信小程序环境下使用 chooseMedia API
|
// 微信小程序环境下使用 chooseMedia API
|
||||||
uni.chooseMedia({
|
uni.chooseMedia({
|
||||||
count,
|
count,
|
||||||
mediaType: ['image'], // 仅支持图片类型
|
mediaType: ['image'], // 仅支持图片类型
|
||||||
sourceType,
|
sourceType,
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
const file = res.tempFiles[0]
|
const file = res.tempFiles[0]
|
||||||
// 检查文件大小是否符合限制
|
// 检查文件大小是否符合限制
|
||||||
if (!checkFileSize(file.size))
|
if (!checkFileSize(file.size))
|
||||||
return
|
return
|
||||||
|
|
||||||
// 开始上传
|
// 开始上传
|
||||||
loading.value = true
|
loading.value = true
|
||||||
progress.value = 0
|
progress.value = 0
|
||||||
uploadFile<T>({
|
uploadFile<T>({
|
||||||
url,
|
url,
|
||||||
tempFilePath: file.tempFilePath,
|
tempFilePath: file.tempFilePath,
|
||||||
formData,
|
formData,
|
||||||
data,
|
data,
|
||||||
error,
|
error,
|
||||||
loading,
|
loading,
|
||||||
progress,
|
progress,
|
||||||
onProgress,
|
onProgress,
|
||||||
onSuccess,
|
onSuccess,
|
||||||
onError,
|
onError,
|
||||||
onComplete,
|
onComplete,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
console.error('选择媒体文件失败:', err)
|
console.error('选择媒体文件失败:', err)
|
||||||
error.value = true
|
error.value = true
|
||||||
onError?.(err)
|
onError?.(err)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
// #ifndef MP-WEIXIN
|
// #ifndef MP-WEIXIN
|
||||||
// 非微信小程序环境下使用 chooseImage API
|
// 非微信小程序环境下使用 chooseImage API
|
||||||
uni.chooseImage({
|
uni.chooseImage({
|
||||||
count,
|
count,
|
||||||
sizeType,
|
sizeType,
|
||||||
sourceType,
|
sourceType,
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
console.log('选择图片成功:', res)
|
console.log('选择图片成功:', res)
|
||||||
|
|
||||||
// 开始上传
|
// 开始上传
|
||||||
loading.value = true
|
loading.value = true
|
||||||
progress.value = 0
|
progress.value = 0
|
||||||
uploadFile<T>({
|
uploadFile<T>({
|
||||||
url,
|
url,
|
||||||
tempFilePath: res.tempFilePaths[0],
|
tempFilePath: res.tempFilePaths[0],
|
||||||
formData,
|
formData,
|
||||||
data,
|
data,
|
||||||
error,
|
error,
|
||||||
loading,
|
loading,
|
||||||
progress,
|
progress,
|
||||||
onProgress,
|
onProgress,
|
||||||
onSuccess,
|
onSuccess,
|
||||||
onError,
|
onError,
|
||||||
onComplete,
|
onComplete,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
console.error('选择图片失败:', err)
|
console.error('选择图片失败:', err)
|
||||||
error.value = true
|
error.value = true
|
||||||
onError?.(err)
|
onError?.(err)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
// #endif
|
// #endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return { loading, error, data, progress, run }
|
return { loading, error, data, progress, run }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -223,28 +223,28 @@ export function useUpload<T = string>(url: string, formData: Record<string, any>
|
|||||||
* @template T 上传成功后返回的数据类型
|
* @template T 上传成功后返回的数据类型
|
||||||
*/
|
*/
|
||||||
interface UploadFileOptions<T> {
|
interface UploadFileOptions<T> {
|
||||||
/** 上传地址 */
|
/** 上传地址 */
|
||||||
url: string
|
url: string
|
||||||
/** 临时文件路径 */
|
/** 临时文件路径 */
|
||||||
tempFilePath: string
|
tempFilePath: string
|
||||||
/** 额外的表单数据 */
|
/** 额外的表单数据 */
|
||||||
formData: Record<string, any>
|
formData: Record<string, any>
|
||||||
/** 上传成功后的响应数据 */
|
/** 上传成功后的响应数据 */
|
||||||
data: Ref<T | undefined>
|
data: Ref<T | undefined>
|
||||||
/** 上传错误状态 */
|
/** 上传错误状态 */
|
||||||
error: Ref<boolean>
|
error: Ref<boolean>
|
||||||
/** 上传中状态 */
|
/** 上传中状态 */
|
||||||
loading: Ref<boolean>
|
loading: Ref<boolean>
|
||||||
/** 上传进度(0-100) */
|
/** 上传进度(0-100) */
|
||||||
progress: Ref<number>
|
progress: Ref<number>
|
||||||
/** 上传进度回调 */
|
/** 上传进度回调 */
|
||||||
onProgress?: (progress: number) => void
|
onProgress?: (progress: number) => void
|
||||||
/** 上传成功回调 */
|
/** 上传成功回调 */
|
||||||
onSuccess?: (res: Record<string, any>) => void
|
onSuccess?: (res: Record<string, any>) => void
|
||||||
/** 上传失败回调 */
|
/** 上传失败回调 */
|
||||||
onError?: (err: Error | UniApp.GeneralCallbackResult) => void
|
onError?: (err: Error | UniApp.GeneralCallbackResult) => void
|
||||||
/** 上传完成回调 */
|
/** 上传完成回调 */
|
||||||
onComplete?: () => void
|
onComplete?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -253,72 +253,72 @@ interface UploadFileOptions<T> {
|
|||||||
* @param options 上传选项
|
* @param options 上传选项
|
||||||
*/
|
*/
|
||||||
function uploadFile<T>({
|
function uploadFile<T>({
|
||||||
url,
|
url,
|
||||||
tempFilePath,
|
tempFilePath,
|
||||||
formData,
|
formData,
|
||||||
data,
|
data,
|
||||||
error,
|
error,
|
||||||
loading,
|
loading,
|
||||||
progress,
|
progress,
|
||||||
onProgress,
|
onProgress,
|
||||||
onSuccess,
|
onSuccess,
|
||||||
onError,
|
onError,
|
||||||
onComplete,
|
onComplete,
|
||||||
}: UploadFileOptions<T>) {
|
}: UploadFileOptions<T>) {
|
||||||
try {
|
try {
|
||||||
// 创建上传任务
|
// 创建上传任务
|
||||||
const uploadTask = uni.uploadFile({
|
const uploadTask = uni.uploadFile({
|
||||||
url,
|
url,
|
||||||
filePath: tempFilePath,
|
filePath: tempFilePath,
|
||||||
name: 'file', // 文件对应的 key
|
name: 'file', // 文件对应的 key
|
||||||
formData,
|
formData,
|
||||||
header: {
|
header: {
|
||||||
// H5环境下不需要手动设置Content-Type,让浏览器自动处理multipart格式
|
// H5环境下不需要手动设置Content-Type,让浏览器自动处理multipart格式
|
||||||
// #ifndef H5
|
// #ifndef H5
|
||||||
'Content-Type': 'multipart/form-data',
|
'Content-Type': 'multipart/form-data',
|
||||||
// #endif
|
// #endif
|
||||||
},
|
},
|
||||||
// 确保文件名称合法
|
// 确保文件名称合法
|
||||||
success: (uploadFileRes) => {
|
success: (uploadFileRes) => {
|
||||||
console.log('上传文件成功:', uploadFileRes)
|
console.log('上传文件成功:', uploadFileRes)
|
||||||
try {
|
try {
|
||||||
// 解析响应数据
|
// 解析响应数据
|
||||||
const { data: _data } = JSON.parse(uploadFileRes.data)
|
const { data: _data } = JSON.parse(uploadFileRes.data)
|
||||||
// 上传成功
|
// 上传成功
|
||||||
data.value = _data as T
|
data.value = _data as T
|
||||||
onSuccess?.(_data)
|
onSuccess?.(_data)
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
// 响应解析错误
|
// 响应解析错误
|
||||||
console.error('解析上传响应失败:', err)
|
console.error('解析上传响应失败:', err)
|
||||||
error.value = true
|
error.value = true
|
||||||
onError?.(new Error('上传响应解析失败'))
|
onError?.(new Error('上传响应解析失败'))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
// 上传请求失败
|
// 上传请求失败
|
||||||
console.error('上传文件失败:', err)
|
console.error('上传文件失败:', err)
|
||||||
error.value = true
|
error.value = true
|
||||||
onError?.(err)
|
onError?.(err)
|
||||||
},
|
},
|
||||||
complete: () => {
|
complete: () => {
|
||||||
// 无论成功失败都执行
|
// 无论成功失败都执行
|
||||||
loading.value = false
|
loading.value = false
|
||||||
onComplete?.()
|
onComplete?.()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// 监听上传进度
|
// 监听上传进度
|
||||||
uploadTask.onProgressUpdate((res) => {
|
uploadTask.onProgressUpdate((res) => {
|
||||||
progress.value = res.progress
|
progress.value = res.progress
|
||||||
onProgress?.(res.progress)
|
onProgress?.(res.progress)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
// 创建上传任务失败
|
// 创建上传任务失败
|
||||||
console.error('创建上传任务失败:', err)
|
console.error('创建上传任务失败:', err)
|
||||||
error.value = true
|
error.value = true
|
||||||
loading.value = false
|
loading.value = false
|
||||||
onError?.(new Error('创建上传任务失败'))
|
onError?.(new Error('创建上传任务失败'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
118
uno.config.ts
@ -1,66 +1,66 @@
|
|||||||
// https://www.npmjs.com/package/@uni-helper/unocss-preset-uni
|
// https://www.npmjs.com/package/@uni-helper/unocss-preset-uni
|
||||||
import { presetUni } from '@uni-helper/unocss-preset-uni'
|
import { presetUni } from '@uni-helper/unocss-preset-uni'
|
||||||
import {
|
import {
|
||||||
defineConfig,
|
defineConfig,
|
||||||
presetAttributify,
|
presetAttributify,
|
||||||
presetIcons,
|
presetIcons,
|
||||||
transformerDirectives,
|
transformerDirectives,
|
||||||
transformerVariantGroup,
|
transformerVariantGroup,
|
||||||
} from 'unocss'
|
} from 'unocss'
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
presets: [
|
presets: [
|
||||||
presetUni({
|
presetUni({
|
||||||
attributify: {
|
attributify: {
|
||||||
// prefix: 'fg-', // 如果加前缀,则需要在代码里面使用 `fg-` 前缀,如:<div fg-border="1px solid #000"></div>
|
// prefix: 'fg-', // 如果加前缀,则需要在代码里面使用 `fg-` 前缀,如:<div fg-border="1px solid #000"></div>
|
||||||
prefixedOnly: true,
|
prefixedOnly: true,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
presetIcons({
|
presetIcons({
|
||||||
scale: 1.2,
|
scale: 1.2,
|
||||||
warn: true,
|
warn: true,
|
||||||
extraProperties: {
|
extraProperties: {
|
||||||
'display': 'inline-block',
|
'display': 'inline-block',
|
||||||
'vertical-align': 'middle',
|
'vertical-align': 'middle',
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
// 支持css class属性化
|
// 支持css class属性化
|
||||||
presetAttributify(),
|
presetAttributify(),
|
||||||
],
|
],
|
||||||
transformers: [
|
transformers: [
|
||||||
// 启用指令功能:主要用于支持 @apply、@screen 和 theme() 等 CSS 指令
|
// 启用指令功能:主要用于支持 @apply、@screen 和 theme() 等 CSS 指令
|
||||||
transformerDirectives(),
|
transformerDirectives(),
|
||||||
// 启用 () 分组功能
|
// 启用 () 分组功能
|
||||||
// 支持css class组合,eg: `<div class="hover:(bg-gray-400 font-medium) font-(light mono)">测试 unocss</div>`
|
// 支持css class组合,eg: `<div class="hover:(bg-gray-400 font-medium) font-(light mono)">测试 unocss</div>`
|
||||||
transformerVariantGroup(),
|
transformerVariantGroup(),
|
||||||
],
|
],
|
||||||
shortcuts: [
|
shortcuts: [
|
||||||
{
|
{
|
||||||
center: 'flex justify-center items-center',
|
center: 'flex justify-center items-center',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
// 动态图标需要在这里配置,或者写在vue页面中注释掉
|
// 动态图标需要在这里配置,或者写在vue页面中注释掉
|
||||||
safelist: ['i-carbon-code'],
|
safelist: ['i-carbon-code'],
|
||||||
rules: [
|
rules: [
|
||||||
[
|
[
|
||||||
'p-safe',
|
'p-safe',
|
||||||
{
|
{
|
||||||
padding:
|
padding:
|
||||||
'env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left)',
|
'env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left)',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
['pt-safe', { 'padding-top': 'env(safe-area-inset-top)' }],
|
['pt-safe', { 'padding-top': 'env(safe-area-inset-top)' }],
|
||||||
['pb-safe', { 'padding-bottom': 'env(safe-area-inset-bottom)' }],
|
['pb-safe', { 'padding-bottom': 'env(safe-area-inset-bottom)' }],
|
||||||
],
|
],
|
||||||
theme: {
|
theme: {
|
||||||
colors: {
|
colors: {
|
||||||
/** 主题色,用法如: text-primary */
|
/** 主题色,用法如: text-primary */
|
||||||
primary: 'var(--wot-color-theme,#0957DE)',
|
primary: 'var(--wot-color-theme,#0957DE)',
|
||||||
},
|
},
|
||||||
fontSize: {
|
fontSize: {
|
||||||
/** 提供更小号的字体,用法如:text-2xs */
|
/** 提供更小号的字体,用法如:text-2xs */
|
||||||
'2xs': ['20rpx', '28rpx'],
|
'2xs': ['20rpx', '28rpx'],
|
||||||
'3xs': ['18rpx', '26rpx'],
|
'3xs': ['18rpx', '26rpx'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
296
vite.config.ts
@ -25,159 +25,159 @@ import ViteRestart from 'vite-plugin-restart'
|
|||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default ({ command, mode }) => {
|
export default ({ command, mode }) => {
|
||||||
// @see https://unocss.dev/
|
// @see https://unocss.dev/
|
||||||
// const UnoCSS = (await import('unocss/vite')).default
|
// const UnoCSS = (await import('unocss/vite')).default
|
||||||
// console.log(mode === process.env.NODE_ENV) // true
|
// console.log(mode === process.env.NODE_ENV) // true
|
||||||
|
|
||||||
// mode: 区分生产环境还是开发环境
|
// mode: 区分生产环境还是开发环境
|
||||||
console.log('command, mode -> ', command, mode)
|
console.log('command, mode -> ', command, mode)
|
||||||
// pnpm dev:h5 时得到 => serve development
|
// pnpm dev:h5 时得到 => serve development
|
||||||
// pnpm build:h5 时得到 => build production
|
// pnpm build:h5 时得到 => build production
|
||||||
// pnpm dev:mp-weixin 时得到 => build development (注意区别,command为build)
|
// pnpm dev:mp-weixin 时得到 => build development (注意区别,command为build)
|
||||||
// pnpm build:mp-weixin 时得到 => build production
|
// pnpm build:mp-weixin 时得到 => build production
|
||||||
// pnpm dev:app 时得到 => build development (注意区别,command为build)
|
// pnpm dev:app 时得到 => build development (注意区别,command为build)
|
||||||
// pnpm build:app 时得到 => build production
|
// pnpm build:app 时得到 => build production
|
||||||
// dev 和 build 命令可以分别使用 .env.development 和 .env.production 的环境变量
|
// dev 和 build 命令可以分别使用 .env.development 和 .env.production 的环境变量
|
||||||
|
|
||||||
const { UNI_PLATFORM } = process.env
|
const { UNI_PLATFORM } = process.env
|
||||||
console.log('UNI_PLATFORM -> ', UNI_PLATFORM) // 得到 mp-weixin, h5, app 等
|
console.log('UNI_PLATFORM -> ', UNI_PLATFORM) // 得到 mp-weixin, h5, app 等
|
||||||
|
|
||||||
const env = loadEnv(mode, path.resolve(process.cwd(), 'env'))
|
const env = loadEnv(mode, path.resolve(process.cwd(), 'env'))
|
||||||
const {
|
const {
|
||||||
VITE_APP_PORT,
|
VITE_APP_PORT,
|
||||||
VITE_SERVER_BASEURL,
|
VITE_SERVER_BASEURL,
|
||||||
VITE_DELETE_CONSOLE,
|
VITE_DELETE_CONSOLE,
|
||||||
VITE_SHOW_SOURCEMAP,
|
VITE_SHOW_SOURCEMAP,
|
||||||
VITE_APP_PUBLIC_BASE,
|
VITE_APP_PUBLIC_BASE,
|
||||||
VITE_APP_PROXY,
|
VITE_APP_PROXY,
|
||||||
VITE_APP_PROXY_PREFIX,
|
VITE_APP_PROXY_PREFIX,
|
||||||
} = env
|
} = env
|
||||||
console.log('环境变量 env -> ', env)
|
console.log('环境变量 env -> ', env)
|
||||||
|
|
||||||
return defineConfig({
|
return defineConfig({
|
||||||
envDir: './env', // 自定义env目录
|
envDir: './env', // 自定义env目录
|
||||||
base: VITE_APP_PUBLIC_BASE,
|
base: VITE_APP_PUBLIC_BASE,
|
||||||
plugins: [
|
plugins: [
|
||||||
UniPages({
|
UniPages({
|
||||||
exclude: ['**/components/**/**.*'],
|
exclude: ['**/components/**/**.*'],
|
||||||
// homePage 通过 vue 文件的 route-block 的type="home"来设定
|
// homePage 通过 vue 文件的 route-block 的type="home"来设定
|
||||||
// pages 目录为 src/pages,分包目录不能配置在pages目录下
|
// pages 目录为 src/pages,分包目录不能配置在pages目录下
|
||||||
subPackages: ['src/pages-sub'], // 是个数组,可以配置多个,但是不能为pages里面的目录
|
subPackages: ['src/pages-sub'], // 是个数组,可以配置多个,但是不能为pages里面的目录
|
||||||
dts: 'src/types/uni-pages.d.ts',
|
dts: 'src/types/uni-pages.d.ts',
|
||||||
}),
|
}),
|
||||||
UniLayouts(),
|
UniLayouts(),
|
||||||
UniPlatform(),
|
UniPlatform(),
|
||||||
UniManifest(),
|
UniManifest(),
|
||||||
// UniXXX 需要在 Uni 之前引入
|
// UniXXX 需要在 Uni 之前引入
|
||||||
{
|
{
|
||||||
// 临时解决 dcloudio 官方的 @dcloudio/uni-mp-compiler 出现的编译 BUG
|
// 临时解决 dcloudio 官方的 @dcloudio/uni-mp-compiler 出现的编译 BUG
|
||||||
// 参考 github issue: https://github.com/dcloudio/uni-app/issues/4952
|
// 参考 github issue: https://github.com/dcloudio/uni-app/issues/4952
|
||||||
// 自定义插件禁用 vite:vue 插件的 devToolsEnabled,强制编译 vue 模板时 inline 为 true
|
// 自定义插件禁用 vite:vue 插件的 devToolsEnabled,强制编译 vue 模板时 inline 为 true
|
||||||
name: 'fix-vite-plugin-vue',
|
name: 'fix-vite-plugin-vue',
|
||||||
configResolved(config) {
|
configResolved(config) {
|
||||||
const plugin = config.plugins.find(p => p.name === 'vite:vue')
|
const plugin = config.plugins.find(p => p.name === 'vite:vue')
|
||||||
if (plugin && plugin.api && plugin.api.options) {
|
if (plugin && plugin.api && plugin.api.options) {
|
||||||
plugin.api.options.devToolsEnabled = false
|
plugin.api.options.devToolsEnabled = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
UnoCSS(),
|
UnoCSS(),
|
||||||
AutoImport({
|
AutoImport({
|
||||||
imports: ['vue', 'uni-app'],
|
imports: ['vue', 'uni-app'],
|
||||||
dts: 'src/types/auto-import.d.ts',
|
dts: 'src/types/auto-import.d.ts',
|
||||||
dirs: ['src/hooks'], // 自动导入 hooks
|
dirs: ['src/hooks'], // 自动导入 hooks
|
||||||
vueTemplate: true, // default false
|
vueTemplate: true, // default false
|
||||||
}),
|
}),
|
||||||
// Optimization 插件需要 page.json 文件,故应在 UniPages 插件之后执行
|
// Optimization 插件需要 page.json 文件,故应在 UniPages 插件之后执行
|
||||||
Optimization({
|
Optimization({
|
||||||
enable: {
|
enable: {
|
||||||
'optimization': true,
|
'optimization': true,
|
||||||
'async-import': true,
|
'async-import': true,
|
||||||
'async-component': true,
|
'async-component': true,
|
||||||
},
|
},
|
||||||
dts: {
|
dts: {
|
||||||
base: 'src/types',
|
base: 'src/types',
|
||||||
},
|
},
|
||||||
logger: false,
|
logger: false,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
ViteRestart({
|
ViteRestart({
|
||||||
// 通过这个插件,在修改vite.config.js文件则不需要重新运行也生效配置
|
// 通过这个插件,在修改vite.config.js文件则不需要重新运行也生效配置
|
||||||
restart: ['vite.config.js'],
|
restart: ['vite.config.js'],
|
||||||
}),
|
}),
|
||||||
// h5环境增加 BUILD_TIME 和 BUILD_BRANCH
|
// h5环境增加 BUILD_TIME 和 BUILD_BRANCH
|
||||||
UNI_PLATFORM === 'h5' && {
|
UNI_PLATFORM === 'h5' && {
|
||||||
name: 'html-transform',
|
name: 'html-transform',
|
||||||
transformIndexHtml(html) {
|
transformIndexHtml(html) {
|
||||||
return html.replace('%BUILD_TIME%', dayjs().format('YYYY-MM-DD HH:mm:ss'))
|
return html.replace('%BUILD_TIME%', dayjs().format('YYYY-MM-DD HH:mm:ss'))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// 打包分析插件,h5 + 生产环境才弹出
|
// 打包分析插件,h5 + 生产环境才弹出
|
||||||
UNI_PLATFORM === 'h5'
|
UNI_PLATFORM === 'h5'
|
||||||
&& mode === 'production'
|
&& mode === 'production'
|
||||||
&& visualizer({
|
&& visualizer({
|
||||||
filename: './node_modules/.cache/visualizer/stats.html',
|
filename: './node_modules/.cache/visualizer/stats.html',
|
||||||
open: true,
|
open: true,
|
||||||
gzipSize: true,
|
gzipSize: true,
|
||||||
brotliSize: true,
|
brotliSize: true,
|
||||||
}),
|
}),
|
||||||
// 只有在 app 平台时才启用 copyNativeRes 插件
|
// 只有在 app 平台时才启用 copyNativeRes 插件
|
||||||
// UNI_PLATFORM === 'app' && copyNativeRes(),
|
// UNI_PLATFORM === 'app' && copyNativeRes(),
|
||||||
Components({
|
Components({
|
||||||
extensions: ['vue'],
|
extensions: ['vue'],
|
||||||
deep: true, // 是否递归扫描子目录,
|
deep: true, // 是否递归扫描子目录,
|
||||||
directoryAsNamespace: false, // 是否把目录名作为命名空间前缀,true 时组件名为 目录名+组件名,
|
directoryAsNamespace: false, // 是否把目录名作为命名空间前缀,true 时组件名为 目录名+组件名,
|
||||||
dts: 'src/types/components.d.ts', // 自动生成的组件类型声明文件路径(用于 TypeScript 支持)
|
dts: 'src/types/components.d.ts', // 自动生成的组件类型声明文件路径(用于 TypeScript 支持)
|
||||||
}),
|
}),
|
||||||
Uni(),
|
Uni(),
|
||||||
],
|
],
|
||||||
define: {
|
define: {
|
||||||
__UNI_PLATFORM__: JSON.stringify(UNI_PLATFORM),
|
__UNI_PLATFORM__: JSON.stringify(UNI_PLATFORM),
|
||||||
__VITE_APP_PROXY__: JSON.stringify(VITE_APP_PROXY),
|
__VITE_APP_PROXY__: JSON.stringify(VITE_APP_PROXY),
|
||||||
},
|
},
|
||||||
css: {
|
css: {
|
||||||
postcss: {
|
postcss: {
|
||||||
plugins: [
|
plugins: [
|
||||||
// autoprefixer({
|
// autoprefixer({
|
||||||
// // 指定目标浏览器
|
// // 指定目标浏览器
|
||||||
// overrideBrowserslist: ['> 1%', 'last 2 versions'],
|
// overrideBrowserslist: ['> 1%', 'last 2 versions'],
|
||||||
// }),
|
// }),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'@': path.join(process.cwd(), './src'),
|
'@': path.join(process.cwd(), './src'),
|
||||||
'@img': path.join(process.cwd(), './src/static/images'),
|
'@img': path.join(process.cwd(), './src/static/images'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0',
|
||||||
hmr: true,
|
hmr: true,
|
||||||
port: Number.parseInt(VITE_APP_PORT, 10),
|
port: Number.parseInt(VITE_APP_PORT, 10),
|
||||||
// 仅 H5 端生效,其他端不生效(其他端走build,不走devServer)
|
// 仅 H5 端生效,其他端不生效(其他端走build,不走devServer)
|
||||||
proxy: JSON.parse(VITE_APP_PROXY)
|
proxy: JSON.parse(VITE_APP_PROXY)
|
||||||
? {
|
? {
|
||||||
[VITE_APP_PROXY_PREFIX]: {
|
[VITE_APP_PROXY_PREFIX]: {
|
||||||
target: VITE_SERVER_BASEURL,
|
target: VITE_SERVER_BASEURL,
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: path => path.replace(new RegExp(`^${VITE_APP_PROXY_PREFIX}`), ''),
|
rewrite: path => path.replace(new RegExp(`^${VITE_APP_PROXY_PREFIX}`), ''),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
},
|
},
|
||||||
esbuild: {
|
esbuild: {
|
||||||
drop: VITE_DELETE_CONSOLE === 'true' ? ['console', 'debugger'] : ['debugger'],
|
drop: VITE_DELETE_CONSOLE === 'true' ? ['console', 'debugger'] : ['debugger'],
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
sourcemap: false,
|
sourcemap: false,
|
||||||
// 方便非h5端调试
|
// 方便非h5端调试
|
||||||
// sourcemap: VITE_SHOW_SOURCEMAP === 'true', // 默认是false
|
// sourcemap: VITE_SHOW_SOURCEMAP === 'true', // 默认是false
|
||||||
target: 'es6',
|
target: 'es6',
|
||||||
// 开发环境不用压缩
|
// 开发环境不用压缩
|
||||||
minify: mode === 'development' ? false : 'esbuild',
|
minify: mode === 'development' ? false : 'esbuild',
|
||||||
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||