使用 Ctrl + F 快速搜索代码片段
UnoCSS 常用配置
ts
// uno.config.ts
export default defineConfig({
rules: [['fade-up-animation', { animation: 'fade-up 0.6s 0.3s backwards' }]],
shortcuts: [
{ 'flex-center': 'flex items-center justify-center' },
{ 'flex-between': 'flex items-center justify-between' },
],
theme: {
// phone:hidden 表示 @media (min-width: 640px)
// lt-phone:hidden 表示 @media (max-width: 639.9px)
// at-pad:hidden 表示 @media (min-width: 768px) and (max-width: 1023.9px)
breakpoints: {
phone: '640px',
pad: '768px',
notebook: '1024px',
desktop: '1280px',
tv: '1536px',
},
},
presets: [
// 默认预设
presetUno(),
// 开启属性模式
presetAttributify(),
// 图标预设 https://icones.js.org/collection/ri
presetIcons({
scale: 1.2,
autoInstall: false,
extraProperties: {
display: 'inline-block',
'vertical-align': 'middle',
},
}),
],
});UniApp 请求封装
ts
import { isHttp } from './utils';
type Data<T> = {
// 后端返回的通用响应结构
code: number;
data: T;
message: string;
};
export const http = <T>(options: UniApp.RequestOptions) => {
options.url = isHttp(options.url) ? options.url : `${import.meta.env.VITE_BASE_URL}${options.url}`;
return new Promise<Data<T>>((resolve, reject) => {
uni.request({
// 拦截器配置内容
...options,
success(res) {
// 成功响应
handleResponse(res, resolve, reject);
},
fail(err) {
handleError(err, reject);
},
});
});
};
// resolve和reject不返回任何值,但通知promise更改状态
const handleResponse = <T>(
res: UniApp.RequestSuccessCallbackResult,
resolve: (value: Data<T>) => void,
reject: (reason?: any) => void,
) => {
const { data: result, statusCode } = res;
// 分离了验证状态码逻辑
if (isSuccessStatusCode(statusCode)) {
resolve(result as Data<T>);
// 登录失败
} else if (statusCode === 401) {
// ...清除用户登录信息
// 跳转至登录页面
uni.navigateTo({ url: '/login' });
reject(res);
} else {
// 分离了报错状态码逻辑
showErrorToast(res.data as Data<T>);
reject(res);
}
};
const handleError = (err: UniApp.GeneralCallbackResult, reject: (reason?: any) => void) => {
uni.showToast({
icon: 'none',
title: '网络可能开小差了~',
});
reject(err);
};
const isSuccessStatusCode = (code: number) => {
return code >= 200 && code < 300;
};
const showErrorToast = <T>(data: Data<T>) => {
uni.showToast({
icon: 'none',
title: data.message || '请求错误',
});
};ts
import { http } from './request';
import type { ExamplePageParams, ExamplePageResult } from './type';
/**
* 获取驿站列表
* @returns
*/
export function getExampleList(data?: ExamplePageParams) {
return http<ExamplePageResult>({
method: 'GET',
url: '/example/page',
data,
});
}ts
/**
* 判断字符串是否 http:// 或者 https 开头
*/
export function isHttp(str: string): boolean {
return /^https?:\/\//.test(str);
}ts
export interface PageParam {
pagesize?: number;
page?: number;
}
export interface PageResult<T> {
list: T[];
total: number;
}
export interface ExamplePageParams extends PageParam {
/**
* 关键词
*/
keyword?: string;
}
export interface ExamplePageItem {
label: string;
value: string;
}
export type ExamplePageResult = PageResult<Required<ExamplePageItem>>;CSS 骨架屏
在需要骨架屏的元素上添加 loading="true" 属性即可,如 vue 可以使用 v-bind:loading="loading"
css
*[loading='true'] > div:not([loading='true']) {
background-image: linear-gradient(90deg, #f0f2f5 25%, #e6e8eb 37%, #f0f2f5 63%) !important;
background-size: 400% 100% !important;
animation: skeleton-loading 1.4s infinite ease !important;
border: none !important;
min-height: 30px;
}
*[loading='true'] > div:not([loading='true']) > * {
display: none !important;
}
@keyframes skeleton-loading {
0% {
background-position: 100% 50%;
}
100% {
background-position: 0 50%;
}
}