From 088ff87ea34941a2b0aabc406f3b223f5e36d44b Mon Sep 17 00:00:00 2001
From: "Ray.Hao" <1490493387@qq.com>
Date: Tue, 22 Jul 2025 23:21:44 +0800
Subject: [PATCH] =?UTF-8?q?wip:=20=E4=B8=B4=E6=97=B6=E6=8F=90=E4=BA=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README-theme.md | 190 -----------
README-wechat-login.md | 609 -----------------------------------
src/composables/useTabbar.ts | 60 ++++
src/composables/useTheme.ts | 294 +++++++++--------
src/layouts/default.vue | 40 +--
src/layouts/tabbar.vue | 138 ++------
src/main.ts | 8 -
src/styles/index.scss | 2 -
src/styles/theme.scss | 190 -----------
src/types/auto-imports.d.ts | 229 ++++++++++++-
vite.config.ts | 16 +-
11 files changed, 501 insertions(+), 1275 deletions(-)
delete mode 100644 README-theme.md
delete mode 100644 README-wechat-login.md
create mode 100644 src/composables/useTabbar.ts
delete mode 100644 src/styles/theme.scss
diff --git a/README-theme.md b/README-theme.md
deleted file mode 100644
index 4493fbd..0000000
--- a/README-theme.md
+++ /dev/null
@@ -1,190 +0,0 @@
-# Wot Design Uni 主题系统使用指南
-
-## 概述
-
-本项目基于 Wot Design Uni 的 CSS 变量系统实现了完整的主题定制功能,支持:
-
-- 🌙 暗黑模式切换
-- 🎨 12 种预设主题色
-- 🎯 自定义主题色
-- 💾 持久化存储
-- 📱 多平台兼容(H5、小程序)
-
-## 核心文件
-
-### 1. 主题变量文件
-
-- `src/styles/wot-theme.scss` - Wot Design Uni CSS 变量定制
-- `src/uni.scss` - 引入主题变量文件
-
-### 2. 主题 Composable
-
-- `src/composables/useTheme.ts` - 主题状态管理和工具函数
-
-### 3. 主题设置页面
-
-- `src/pages/mine/settings/theme/index.vue` - 主题设置界面
-- `src/pages/test-wot-theme.vue` - 主题测试页面
-
-## 使用方法
-
-### 1. 在组件中使用主题
-
-```vue
-
-
-
-
-
-
- 主要按钮
-
-
- 使用主题色的文本
-
-
-
-
-
-
-```
-
-### 2. 可用的 CSS 变量
-
-#### 主题色系
-
-- `--wot-color-theme` - 主题色
-- `--wot-color-success` - 成功色
-- `--wot-color-warning` - 警告色
-- `--wot-color-danger` - 危险色
-- `--wot-color-info` - 信息色
-
-#### 文本颜色
-
-- `--wot-color-text` - 主要文本色
-- `--wot-color-text-secondary` - 次要文本色
-- `--wot-color-text-placeholder` - 占位符文本色
-
-#### 背景颜色
-
-- `--wot-color-bg` - 主背景色
-- `--wot-color-bg-light` - 浅背景色
-- `--wot-card-bg-color` - 卡片背景色
-
-#### 边框颜色
-
-- `--wot-color-border` - 边框色
-- `--wot-color-border-light` - 浅边框色
-
-### 3. 预设主题色
-
-项目提供 12 种预设主题色:
-
-```javascript
-const colorColumns = [
- { value: "#165DFF", label: "蓝色" },
- { value: "#0FC6C2", label: "青绿色" },
- { value: "#722ED1", label: "紫色" },
- { value: "#F5222D", label: "红色" },
- { value: "#FA8C16", label: "橙色" },
- { value: "#FADB14", label: "黄色" },
- { value: "#52C41A", label: "绿色" },
- { value: "#EB2F96", label: "粉色" },
- { value: "#13C2C2", label: "青色" },
- { value: "#1890FF", label: "天蓝色" },
- { value: "#CD5C5C", label: "经典红" },
- { value: "#228B22", label: "自然绿" },
-];
-```
-
-## 暗黑模式适配
-
-### 自动适配
-
-所有使用 Wot CSS 变量的组件都会自动适配暗黑模式。
-
-### 手动适配
-
-对于自定义组件,使用 CSS 变量即可:
-
-```scss
-.my-component {
- background-color: var(--wot-card-bg-color, #fff);
- color: var(--wot-color-text, #333);
- border: 1px solid var(--wot-color-border, #e5e6eb);
-}
-```
-
-## 测试页面
-
-访问 `/pages/test-wot-theme` 可以测试:
-
-- 暗黑模式切换
-- 主题色切换
-- CSS 变量效果
-- 各种 Wot 组件的主题适配
-
-## 注意事项
-
-1. **CSS 变量优先级**:使用 `var(--variable-name, fallback)` 格式提供回退值
-2. **暗黑模式检测**:通过 `[data-theme="dark"]` 选择器自动切换
-3. **持久化存储**:主题设置会自动保存到本地存储
-4. **平台兼容**:H5 和小程序环境都有相应的适配处理
-
-## 扩展主题
-
-### 添加新的主题色
-
-1. 在 `colorColumns` 数组中添加新颜色
-2. 在 `wot-theme.scss` 中添加对应的 CSS 类
-
-```scss
-.theme-custom {
- --wot-color-theme: #your-color;
-}
-```
-
-### 自定义 CSS 变量
-
-在 `wot-theme.scss` 中添加新的变量:
-
-```scss
-:root {
- --wot-custom-color: #your-color;
-}
-
-[data-theme="dark"] {
- --wot-custom-color: #your-dark-color;
-}
-```
-
-## 故障排除
-
-1. **主题色不生效**:检查是否正确引入了 `wot-theme.scss`
-2. **暗黑模式样式异常**:确认使用了正确的 CSS 变量
-3. **小程序兼容问题**:检查条件编译是否正确
-
-## 更新日志
-
-- ✅ 修复了暗黑模式切换无效的问题
-- ✅ 完善了 Wot Design Uni CSS 变量系统
-- ✅ 优化了主题设置页面的 UI 组件
-- ✅ 添加了完整的主题测试页面
diff --git a/README-wechat-login.md b/README-wechat-login.md
deleted file mode 100644
index 71f1465..0000000
--- a/README-wechat-login.md
+++ /dev/null
@@ -1,609 +0,0 @@
-# 微信登录实现指南
-
-本文档提供基于 wx-java-sdk 的微信授权登录后端实现方案,包括会话管理和接口设计。
-
-## 技术方案概述
-
-微信登录流程采用基于会话管理的方式,避免每次都从微信服务端获取授权:
-
-1. 前端通过微信 SDK 获取登录凭证(code)和手机号加密数据
-2. 后端接收凭证和加密数据,与微信服务器交互获取用户信息
-3. 后端创建或更新用户信息,并生成会话令牌(token)返回给前端
-4. 前端存储令牌,后续请求时携带令牌进行身份验证
-5. 令牌过期时,后端自动使用刷新令牌获取新的访问令牌
-
-## 后端依赖
-
-```xml
-
-
- com.github.binarywang
- weixin-java-miniapp
- 4.5.0
-
-
-
-
- io.jsonwebtoken
- jjwt-api
- 0.11.5
-
-
- io.jsonwebtoken
- jjwt-impl
- 0.11.5
- runtime
-
-
- io.jsonwebtoken
- jjwt-jackson
- 0.11.5
- runtime
-
-```
-
-## 后端配置
-
-```yaml
-# application.yml
-wx:
- miniapp:
- appid: ${WX_MINIAPP_APPID} # 微信小程序 appId
- secret: ${WX_MINIAPP_SECRET} # 微信小程序 appSecret
- token: ${WX_MINIAPP_TOKEN} # 微信小程序消息服务器配置的 token
- aesKey: ${WX_MINIAPP_AES_KEY} # 微信小程序消息服务器配置的 EncodingAESKey
- msgDataFormat: JSON # 消息格式,XML 或者 JSON
-
-# JWT 配置
-jwt:
- secret: ${JWT_SECRET_KEY} # JWT 密钥
- access-token-expiration: 86400 # 访问令牌过期时间(秒),默认1天
- refresh-token-expiration: 604800 # 刷新令牌过期时间(秒),默认7天
-```
-
-## 微信服务配置类
-
-```java
-@Configuration
-@EnableConfigurationProperties(WxMaProperties.class)
-public class WxMaConfiguration {
- private final WxMaProperties properties;
- private static final Map maServices = new HashMap<>();
-
- @Autowired
- public WxMaConfiguration(WxMaProperties properties) {
- this.properties = properties;
- }
-
- @Bean
- public WxMaService wxMaService() {
- WxMaService service = new WxMaServiceImpl();
- WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
- config.setAppid(properties.getAppid());
- config.setSecret(properties.getSecret());
- config.setToken(properties.getToken());
- config.setAesKey(properties.getAesKey());
- config.setMsgDataFormat(properties.getMsgDataFormat());
- service.setWxMaConfig(config);
- maServices.put(properties.getAppid(), service);
- return service;
- }
-
- public static WxMaService getMaService(String appid) {
- return maServices.get(appid);
- }
-}
-```
-
-## 微信用户表设计
-
-```sql
-CREATE TABLE `wx_user` (
- `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
- `open_id` varchar(128) NOT NULL COMMENT '微信开放ID',
- `union_id` varchar(128) DEFAULT NULL COMMENT '微信开放平台unionid',
- `session_key` varchar(128) DEFAULT NULL COMMENT '会话密钥',
- `nickname` varchar(64) DEFAULT NULL COMMENT '昵称',
- `avatar_url` varchar(256) DEFAULT NULL COMMENT '头像',
- `phone` varchar(32) DEFAULT NULL COMMENT '手机号',
- `gender` tinyint(1) DEFAULT NULL COMMENT '性别(0:未知 1:男 2:女)',
- `country` varchar(64) DEFAULT NULL COMMENT '国家',
- `province` varchar(64) DEFAULT NULL COMMENT '省份',
- `city` varchar(64) DEFAULT NULL COMMENT '城市',
- `language` varchar(64) DEFAULT NULL COMMENT '语言',
- `is_new_user` tinyint(1) DEFAULT '1' COMMENT '是否新用户(0:否 1:是)',
- `last_login_time` datetime DEFAULT NULL COMMENT '最后登录时间',
- `created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
- `updated_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
- PRIMARY KEY (`id`),
- UNIQUE KEY `idx_open_id` (`open_id`),
- KEY `idx_union_id` (`union_id`),
- KEY `idx_phone` (`phone`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='微信用户表';
-```
-
-## 控制器层实现
-
-```java
-@Slf4j
-@RestController
-@RequestMapping("/api/v1/auth")
-@RequiredArgsConstructor
-public class AuthController {
-
- private final WxMaService wxMaService;
- private final UserService userService;
- private final JwtService jwtService;
-
- /**
- * 微信登录(简单版)
- */
- @PostMapping("/wechat/login")
- public R wechatLogin(@RequestBody WechatLoginRequest request) {
- try {
- // 获取微信用户信息
- WxMaJscode2SessionResult sessionResult = wxMaService.jsCode2SessionInfo(request.getCode());
- String openid = sessionResult.getOpenid();
- String sessionKey = sessionResult.getSessionKey();
-
- // 查询或创建用户
- User user = userService.getOrCreateWxUser(openid, sessionKey);
-
- // 生成令牌
- String accessToken = jwtService.generateAccessToken(user.getId());
- String refreshToken = jwtService.generateRefreshToken(user.getId());
-
- // 构建返回结果
- LoginResult result = new LoginResult();
- result.setAccessToken(accessToken);
- result.setRefreshToken(refreshToken);
- result.setTokenType("Bearer");
- result.setExpiresIn(jwtService.getAccessTokenExpiration());
- result.setNewUser(user.getIsNewUser());
- result.setProfileComplete(userService.isUserProfileComplete(user));
-
- return R.ok(result);
- } catch (WxErrorException e) {
- log.error("微信登录失败", e);
- return R.fail("微信登录失败: " + e.getMessage());
- }
- }
-
- /**
- * 微信小程序手机号登录
- */
- @PostMapping("/wechat/phone-login")
- public R wechatPhoneLogin(@RequestBody WechatPhoneLoginRequest request) {
- try {
- // 获取微信用户信息
- WxMaJscode2SessionResult sessionResult = wxMaService.jsCode2SessionInfo(request.getCode());
- String openid = sessionResult.getOpenid();
- String sessionKey = sessionResult.getSessionKey();
-
- // 解密手机号
- String phoneNumber = null;
-
- // 新版本获取手机号
- if (request.getPhoneCode() != null) {
- WxMaPhoneNumberInfo phoneInfo = wxMaService.getPhoneNoInfo(request.getPhoneCode());
- phoneNumber = phoneInfo.getPhoneNumber();
- }
- // 旧版本获取手机号
- else if (request.getEncryptedData() != null && request.getIv() != null) {
- WxMaPhoneNumberInfo phoneInfo = wxMaService.getUserService()
- .getPhoneNoInfo(sessionKey, request.getEncryptedData(), request.getIv());
- phoneNumber = phoneInfo.getPhoneNumber();
- }
-
- if (phoneNumber == null) {
- return R.fail("获取手机号失败");
- }
-
- // 查询或创建用户并更新手机号
- User user = userService.getOrCreateWxUserWithPhone(openid, sessionKey, phoneNumber);
-
- // 生成令牌
- String accessToken = jwtService.generateAccessToken(user.getId());
- String refreshToken = jwtService.generateRefreshToken(user.getId());
-
- // 构建返回结果
- LoginResult result = new LoginResult();
- result.setAccessToken(accessToken);
- result.setRefreshToken(refreshToken);
- result.setTokenType("Bearer");
- result.setExpiresIn(jwtService.getAccessTokenExpiration());
- result.setNewUser(user.getIsNewUser());
- result.setProfileComplete(userService.isUserProfileComplete(user));
-
- return R.ok(result);
- } catch (WxErrorException e) {
- log.error("微信手机号登录失败", e);
- return R.fail("微信手机号登录失败: " + e.getMessage());
- }
- }
-
- /**
- * 检查会话有效性
- */
- @GetMapping("/check-session")
- public R checkSession(HttpServletRequest request) {
- String token = jwtService.getTokenFromRequest(request);
- boolean isValid = jwtService.validateToken(token);
-
- SessionValidResult result = new SessionValidResult();
- result.setValid(isValid);
-
- return R.ok(result);
- }
-
- /**
- * 刷新令牌
- */
- @PostMapping("/refresh-token")
- public R refreshToken(@RequestBody RefreshTokenRequest request) {
- try {
- String refreshToken = request.getRefreshToken();
- if (!jwtService.validateRefreshToken(refreshToken)) {
- return R.fail("刷新令牌无效或已过期");
- }
-
- Long userId = jwtService.getUserIdFromToken(refreshToken);
- String newAccessToken = jwtService.generateAccessToken(userId);
-
- RefreshTokenResult result = new RefreshTokenResult();
- result.setAccessToken(newAccessToken);
- result.setExpiresIn(jwtService.getAccessTokenExpiration());
-
- return R.ok(result);
- } catch (Exception e) {
- log.error("刷新令牌失败", e);
- return R.fail("刷新令牌失败");
- }
- }
-
- /**
- * 登出
- */
- @PostMapping("/logout")
- public R logout(HttpServletRequest request) {
- // 此处可以将token加入黑名单
- // 在真实场景中,可以将token存入Redis黑名单,并设置过期时间
- return R.ok();
- }
-}
-```
-
-## JWT服务实现
-
-```java
-@Service
-@RequiredArgsConstructor
-public class JwtService {
-
- @Value("${jwt.secret}")
- private String jwtSecret;
-
- @Value("${jwt.access-token-expiration}")
- private long accessTokenExpiration;
-
- @Value("${jwt.refresh-token-expiration}")
- private long refreshTokenExpiration;
-
- /**
- * 生成访问令牌
- */
- public String generateAccessToken(Long userId) {
- return generateToken(userId, accessTokenExpiration, "access");
- }
-
- /**
- * 生成刷新令牌
- */
- public String generateRefreshToken(Long userId) {
- return generateToken(userId, refreshTokenExpiration, "refresh");
- }
-
- /**
- * 生成令牌
- */
- private String generateToken(Long userId, long expiration, String type) {
- Date now = new Date();
- Date expiryDate = new Date(now.getTime() + expiration * 1000);
-
- return Jwts.builder()
- .setSubject(userId.toString())
- .setIssuedAt(now)
- .setExpiration(expiryDate)
- .claim("type", type)
- .signWith(getSigningKey())
- .compact();
- }
-
- /**
- * 从请求中获取令牌
- */
- public String getTokenFromRequest(HttpServletRequest request) {
- String bearerToken = request.getHeader("Authorization");
- if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
- return bearerToken.substring(7);
- }
- return null;
- }
-
- /**
- * 验证令牌
- */
- public boolean validateToken(String token) {
- try {
- Jwts.parserBuilder()
- .setSigningKey(getSigningKey())
- .build()
- .parseClaimsJws(token);
- return true;
- } catch (Exception e) {
- return false;
- }
- }
-
- /**
- * 验证刷新令牌
- */
- public boolean validateRefreshToken(String token) {
- try {
- Claims claims = Jwts.parserBuilder()
- .setSigningKey(getSigningKey())
- .build()
- .parseClaimsJws(token)
- .getBody();
-
- return "refresh".equals(claims.get("type"));
- } catch (Exception e) {
- return false;
- }
- }
-
- /**
- * 从令牌中获取用户ID
- */
- public Long getUserIdFromToken(String token) {
- Claims claims = Jwts.parserBuilder()
- .setSigningKey(getSigningKey())
- .build()
- .parseClaimsJws(token)
- .getBody();
-
- return Long.parseLong(claims.getSubject());
- }
-
- /**
- * 获取签名密钥
- */
- private Key getSigningKey() {
- byte[] keyBytes = Decoders.BASE64.decode(jwtSecret);
- return Keys.hmacShaKeyFor(keyBytes);
- }
-
- /**
- * 获取访问令牌过期时间(秒)
- */
- public long getAccessTokenExpiration() {
- return accessTokenExpiration;
- }
-}
-```
-
-## 用户服务实现
-
-```java
-@Service
-@RequiredArgsConstructor
-public class UserServiceImpl implements UserService {
-
- private final WxUserMapper wxUserMapper;
-
- /**
- * 获取或创建微信用户
- */
- @Override
- @Transactional(rollbackFor = Exception.class)
- public User getOrCreateWxUser(String openid, String sessionKey) {
- // 查询用户
- WxUser wxUser = wxUserMapper.selectByOpenId(openid);
-
- // 如果用户不存在,创建新用户
- if (wxUser == null) {
- wxUser = new WxUser();
- wxUser.setOpenId(openid);
- wxUser.setSessionKey(sessionKey);
- wxUser.setIsNewUser(true);
- wxUser.setLastLoginTime(new Date());
- wxUserMapper.insert(wxUser);
- } else {
- // 更新会话密钥和登录时间
- wxUser.setSessionKey(sessionKey);
- wxUser.setLastLoginTime(new Date());
- wxUserMapper.updateById(wxUser);
- }
-
- // 将 WxUser 转换为 User
- return convertToUser(wxUser);
- }
-
- /**
- * 获取或创建带手机号的微信用户
- */
- @Override
- @Transactional(rollbackFor = Exception.class)
- public User getOrCreateWxUserWithPhone(String openid, String sessionKey, String phone) {
- // 先检查是否有该手机号的用户
- WxUser wxUserByPhone = wxUserMapper.selectByPhone(phone);
-
- // 如果存在该手机号用户但openid不同,可能是用户换了微信号,更新openid
- if (wxUserByPhone != null && !openid.equals(wxUserByPhone.getOpenId())) {
- wxUserByPhone.setOpenId(openid);
- wxUserByPhone.setSessionKey(sessionKey);
- wxUserByPhone.setLastLoginTime(new Date());
- wxUserByPhone.setIsNewUser(false);
- wxUserMapper.updateById(wxUserByPhone);
- return convertToUser(wxUserByPhone);
- }
-
- // 查询用户
- WxUser wxUser = wxUserMapper.selectByOpenId(openid);
-
- // 如果用户不存在,创建新用户
- if (wxUser == null) {
- wxUser = new WxUser();
- wxUser.setOpenId(openid);
- wxUser.setSessionKey(sessionKey);
- wxUser.setPhone(phone);
- wxUser.setIsNewUser(true);
- wxUser.setLastLoginTime(new Date());
- wxUserMapper.insert(wxUser);
- } else {
- // 更新会话密钥、手机号和登录时间
- wxUser.setSessionKey(sessionKey);
- wxUser.setPhone(phone);
- wxUser.setLastLoginTime(new Date());
- wxUserMapper.updateById(wxUser);
- }
-
- // 将 WxUser 转换为 User
- return convertToUser(wxUser);
- }
-
- /**
- * 判断用户信息是否完整
- */
- @Override
- public boolean isUserProfileComplete(User user) {
- if (user == null) {
- return false;
- }
-
- return StringUtils.hasText(user.getNickname())
- && StringUtils.hasText(user.getAvatar())
- && StringUtils.hasText(user.getPhone());
- }
-
- /**
- * 将 WxUser 转换为 User
- */
- private User convertToUser(WxUser wxUser) {
- if (wxUser == null) {
- return null;
- }
-
- User user = new User();
- user.setId(wxUser.getId());
- user.setOpenId(wxUser.getOpenId());
- user.setUnionId(wxUser.getUnionId());
- user.setNickname(wxUser.getNickname());
- user.setAvatar(wxUser.getAvatarUrl());
- user.setPhone(wxUser.getPhone());
- user.setGender(wxUser.getGender());
- user.setIsNewUser(wxUser.getIsNewUser());
-
- return user;
- }
-}
-```
-
-## 网关过滤器实现 (可选)
-
-如果使用了Spring Cloud Gateway,可以添加JWT验证过滤器:
-
-```java
-@Component
-@RequiredArgsConstructor
-public class JwtAuthenticationFilter implements GlobalFilter {
-
- private final JwtService jwtService;
-
- @Override
- public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
- ServerHttpRequest request = exchange.getRequest();
-
- // 白名单路径,不需要token验证
- if (isWhiteListPath(request.getPath().toString())) {
- return chain.filter(exchange);
- }
-
- // 获取token
- String token = getTokenFromRequest(request);
- if (token == null) {
- return onError(exchange, "未授权", HttpStatus.UNAUTHORIZED);
- }
-
- // 验证token
- if (!jwtService.validateToken(token)) {
- return onError(exchange, "token无效或已过期", HttpStatus.UNAUTHORIZED);
- }
-
- // 获取用户ID并设置到请求头
- Long userId = jwtService.getUserIdFromToken(token);
- ServerHttpRequest mutatedRequest = request.mutate()
- .header("X-User-ID", userId.toString())
- .build();
-
- return chain.filter(exchange.mutate().request(mutatedRequest).build());
- }
-
- private boolean isWhiteListPath(String path) {
- List whiteList = Arrays.asList(
- "/api/v1/auth/login",
- "/api/v1/auth/wechat/login",
- "/api/v1/auth/wechat/mini-login",
- "/api/v1/auth/wechat/phone-login",
- "/api/v1/auth/refresh-token"
- );
-
- return whiteList.stream().anyMatch(path::startsWith);
- }
-
- private String getTokenFromRequest(ServerHttpRequest request) {
- List authHeaders = request.getHeaders().get("Authorization");
- if (authHeaders != null && !authHeaders.isEmpty()) {
- String auth = authHeaders.get(0);
- if (auth.startsWith("Bearer ")) {
- return auth.substring(7);
- }
- }
- return null;
- }
-
- private Mono onError(ServerWebExchange exchange, String message, HttpStatus status) {
- ServerHttpResponse response = exchange.getResponse();
- response.setStatusCode(status);
- response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
-
- Map result = new HashMap<>();
- result.put("code", status.value());
- result.put("message", message);
-
- byte[] bytes = new ObjectMapper().writeValueAsBytes(result);
- DataBuffer buffer = response.bufferFactory().wrap(bytes);
- return response.writeWith(Mono.just(buffer));
- }
-}
-```
-
-## 安全建议
-
-1. 生产环境中使用HTTPS保护API通信
-2. 敏感信息(如SessionKey)不要存储在前端
-3. 为JWT密钥使用足够强度的随机字符串
-4. 实现令牌黑名单机制处理注销和令牌泄露情况
-5. 考虑实现令牌自动续期机制
-6. 定期清理过期的会话记录
-7. 记录关键操作的审计日志
-
-## 测试
-
-使用Postman或其他API测试工具测试以下接口:
-
-1. `/api/v1/auth/wechat/login` - 微信简单登录
-2. `/api/v1/auth/wechat/phone-login` - 微信手机号登录
-3. `/api/v1/auth/check-session` - 检查会话有效性
-4. `/api/v1/auth/refresh-token` - 刷新令牌
-5. `/api/v1/auth/logout` - 登出
diff --git a/src/composables/useTabbar.ts b/src/composables/useTabbar.ts
new file mode 100644
index 0000000..f85d356
--- /dev/null
+++ b/src/composables/useTabbar.ts
@@ -0,0 +1,60 @@
+/*
+ * @Author: weisheng
+ * @Date: 2024-10-29 22:12:54
+ * @LastEditTime: 2025-06-25 13:33:39
+ * @LastEditors: weisheng
+ * @Description:
+ * @FilePath: /wot-demo/src/composables/useTabbar.ts
+ * 记得注释
+ */
+export interface TabbarItem {
+ name: string;
+ value: number | null;
+ active: boolean;
+ title: string;
+ icon: string;
+}
+
+const tabbarItems = ref([
+ { name: "home", value: null, active: true, title: "首页", icon: "home" },
+ { name: "mine", value: null, active: false, title: "我的", icon: "user" },
+]);
+
+export function useTabbar() {
+ const tabbarList = computed(() => tabbarItems.value);
+
+ const activeTabbar = computed(() => {
+ const item = tabbarItems.value.find((item) => item.active);
+ return item || tabbarItems.value[0];
+ });
+
+ const getTabbarItemValue = (name: string) => {
+ const item = tabbarItems.value.find((item) => item.name === name);
+ return item && item.value ? item.value : null;
+ };
+
+ const setTabbarItem = (name: string, value: number) => {
+ const tabbarItem = tabbarItems.value.find((item) => item.name === name);
+ if (tabbarItem) {
+ tabbarItem.value = value;
+ }
+ };
+
+ const setTabbarItemActive = (name: string) => {
+ tabbarItems.value.forEach((item) => {
+ if (item.name === name) {
+ item.active = true;
+ } else {
+ item.active = false;
+ }
+ });
+ };
+
+ return {
+ tabbarList,
+ activeTabbar,
+ getTabbarItemValue,
+ setTabbarItem,
+ setTabbarItemActive,
+ };
+}
diff --git a/src/composables/useTheme.ts b/src/composables/useTheme.ts
index 6618cc1..739b42d 100644
--- a/src/composables/useTheme.ts
+++ b/src/composables/useTheme.ts
@@ -1,162 +1,178 @@
-import { ref, watch, computed } from "vue";
import type { ConfigProviderThemeVars } from "wot-design-uni";
-/* 默认的主题色列表 */
-export const colorColumns = [
- { value: "#165DFF", label: "海洋蓝" },
- { value: "#1677FF", label: "天空蓝" },
- { value: "#0081FF", label: "梦幻蓝" },
- { value: "#4080FF", label: "皇家蓝" },
- { value: "#4D74FF", label: "靛蓝" },
- { value: "#0FC6C2", label: "碧波绿" },
- { value: "#722ED1", label: "魔幻紫" },
- { value: "#F5222D", label: "热情红" },
- { value: "#FA8C16", label: "活力橙" },
- { value: "#FADB14", label: "阳光黄" },
- { value: "#52C41A", label: "生机绿" },
- { value: "#EB2F96", label: "浪漫粉" },
- { value: "#13C2C2", label: "清新青" },
- { value: "#36CFC9", label: "湖水蓝" },
- { value: "#CD5C5C", label: "复古红" },
- { value: "#228B22", label: "森林绿" },
+// 定义主题色选项
+export interface ThemeColorOption {
+ name: string;
+ value: string;
+ primary: string;
+}
+
+// 预定义的主题色选项
+export const themeColorOptions: ThemeColorOption[] = [
+ { name: "默认蓝", value: "blue", primary: "#4D7FFF" },
+ { name: "活力橙", value: "orange", primary: "#FF7D00" },
+ { name: "薄荷绿", value: "green", primary: "#07C160" },
+ { name: "樱花粉", value: "pink", primary: "#FF69B4" },
+ { name: "紫罗兰", value: "purple", primary: "#8A2BE2" },
+ { name: "朱砂红", value: "red", primary: "#FF4757" },
];
-/* 存储键名 */
-const THEME_STORAGE_KEY = "app_theme_mode";
-const THEME_COLOR_STORAGE_KEY = "app_theme_color";
+export function useTheme() {
+ // 状态定义
+ const theme = ref<"light" | "dark">("light");
+ const followSystem = ref(true); // 是否跟随系统主题
+ const hasUserSet = ref(false); // 用户是否手动设置过主题
+ const currentThemeColor = ref(themeColorOptions[0]);
+ const showThemeColorSheet = ref(false);
-/* 从存储中获取主题模式 */
-const getStoredTheme = (): "light" | "dark" => {
- try {
- const stored = uni.getStorageSync(THEME_STORAGE_KEY);
- return stored === "dark" ? "dark" : "light";
- } catch {
- return "light";
- }
-};
+ const themeVars = reactive({
+ darkBackground: "#0f0f0f",
+ darkBackground2: "#1a1a1a",
+ darkBackground3: "#242424",
+ darkBackground4: "#2f2f2f",
+ darkBackground5: "#3d3d3d",
+ darkBackground6: "#4a4a4a",
+ darkBackground7: "#606060",
+ darkColor: "#ffffff",
+ darkColor2: "#e0e0e0",
+ darkColor3: "#a0a0a0",
+ colorTheme: themeColorOptions[0].primary,
+ });
-/* 从存储中获取主题色 */
-const getStoredThemeColor = (): string => {
- try {
- const stored = uni.getStorageSync(THEME_COLOR_STORAGE_KEY);
- return stored || colorColumns[0].value;
- } catch {
- return colorColumns[0].value;
- }
-};
+ // 计算属性
+ const isDark = computed(() => theme.value === "dark");
-/* 主题状态 */
-export const theme = ref<"light" | "dark">(getStoredTheme());
-export const currentThemeColor = ref(getStoredThemeColor());
-
-/* 主题变量(供 ConfigProvider 使用) */
-export const themeVars = computed(() => ({
- colorTheme: currentThemeColor.value,
- // 按钮颜色
- buttonPrimaryBgColor: currentThemeColor.value,
- buttonPrimaryColor: "#ffffff",
- // 开关颜色
- switchOnBgColor: currentThemeColor.value,
- // 其他组件颜色
- cellIconColor: currentThemeColor.value,
- tagPrimaryBgColor: currentThemeColor.value,
- tagPrimaryColor: "#ffffff",
-}));
-
-/* 应用主题到根元素 */
-const applyThemeToRoot = () => {
- // 获取根元素
- const root = document.documentElement;
- const body = document.body;
-
- // #ifdef H5
- // 应用暗黑模式
- if (theme.value === "dark") {
- root.setAttribute("data-theme", "dark");
- body.classList.add("wot-theme-dark");
- } else {
- root.removeAttribute("data-theme");
- body.classList.remove("wot-theme-dark");
+ /* 手动切换主题 */
+ function toggleTheme(mode?: "light" | "dark") {
+ theme.value = mode || (theme.value === "light" ? "dark" : "light");
+ hasUserSet.value = true; // 标记用户已手动设置
+ followSystem.value = false; // 不再跟随系统
+ setNavigationBarColor();
}
- // 应用主题色类
- // 移除所有主题色类
- root.className = root.className.replace(/theme-color-\w+/g, "").trim();
- // 添加当前主题色类
- const colorClass = `theme-color-${currentThemeColor.value.replace("#", "")}`;
- root.classList.add(colorClass);
- // #endif
-
- // #ifdef MP
- // 小程序环境下通过设置页面的 data-theme 属性
- const pages = getCurrentPages();
- if (pages.length > 0) {
- const currentPage = pages[pages.length - 1] as any;
- if (currentPage) {
- currentPage.setData?.({
- "data-theme": theme.value,
- themeColor: currentThemeColor.value,
- });
+ /* 设置是否跟随系统主题 */
+ function setFollowSystem(follow: boolean) {
+ followSystem.value = follow;
+ if (follow) {
+ hasUserSet.value = false;
+ initTheme(); // 重新获取系统主题
}
}
- // #endif
-};
-/* 监听主题模式变化 */
-watch(
- theme,
- (newTheme) => {
- uni.setStorageSync(THEME_STORAGE_KEY, newTheme);
- applyThemeToRoot();
- },
- { immediate: true }
-);
+ /* 设置导航栏颜色 */
+ function setNavigationBarColor() {
+ uni.setNavigationBarColor({
+ frontColor: theme.value === "light" ? "#000000" : "#ffffff",
+ backgroundColor: theme.value === "light" ? "#ffffff" : "#000000",
+ });
+ }
-/* 监听主题色变化 */
-watch(
- currentThemeColor,
- (newColor) => {
- uni.setStorageSync(THEME_COLOR_STORAGE_KEY, newColor);
- applyThemeToRoot();
- },
- { immediate: true }
-);
+ /* 设置主题色 */
+ function setCurrentThemeColor(color: ThemeColorOption) {
+ currentThemeColor.value = color;
+ themeVars.colorTheme = color.primary;
+ }
-/* 切换主题模式 */
-export const toggleTheme = () => {
- theme.value = theme.value === "light" ? "dark" : "light";
-};
+ /* 获取系统主题 */
+ function getSystemTheme(): "light" | "dark" {
+ try {
+ // #ifdef MP-WEIXIN
+ // 微信小程序使用 getAppBaseInfo
+ const appBaseInfo = uni.getAppBaseInfo();
+ if (appBaseInfo && appBaseInfo.theme) {
+ return appBaseInfo.theme as "light" | "dark";
+ }
+ // #endif
-/* 设置主题色 */
-export const setThemeColor = (color: string) => {
- currentThemeColor.value = color;
-};
+ // #ifndef MP-WEIXIN
+ // 其他平台使用 getSystemInfoSync
+ const systemInfo = uni.getSystemInfoSync();
+ if (systemInfo && systemInfo.theme) {
+ return systemInfo.theme as "light" | "dark";
+ }
+ // #endif
+ } catch (error) {
+ console.warn("获取系统主题失败:", error);
+ }
+ return "light"; // 默认返回 light
+ }
-/* 重置主题 */
-export const resetTheme = () => {
- theme.value = "light";
- currentThemeColor.value = colorColumns[0].value;
-};
+ /* 初始化主题 */
+ function initTheme() {
+ // 如果用户已手动设置且不跟随系统,保持当前主题
+ if (hasUserSet.value && !followSystem.value) {
+ console.log("使用用户设置的主题:", theme.value);
+ setNavigationBarColor();
+ return;
+ }
-/* 初始化主题 */
-export const initTheme = () => {
- applyThemeToRoot();
- console.log("主题初始化完成:", {
- mode: theme.value,
- color: currentThemeColor.value,
+ // 获取系统主题
+ const systemTheme = getSystemTheme();
+
+ // 如果是首次启动或跟随系统,使用系统主题
+ if (!hasUserSet.value || followSystem.value) {
+ theme.value = systemTheme;
+ if (!hasUserSet.value) {
+ followSystem.value = true;
+ console.log("首次启动,使用系统主题:", theme.value);
+ } else {
+ console.log("跟随系统主题:", theme.value);
+ }
+ }
+
+ setNavigationBarColor();
+ }
+
+ /* 打开主题色选择 */
+ function openThemeColorPicker() {
+ showThemeColorSheet.value = true;
+ }
+
+ /* 关闭主题色选择 */
+ function closeThemeColorPicker() {
+ showThemeColorSheet.value = false;
+ }
+
+ /* 选择主题色 */
+ function selectThemeColor(option: ThemeColorOption) {
+ setCurrentThemeColor(option);
+ closeThemeColorPicker();
+ }
+
+ // 检查函数是否存在的工具函数
+ const isFunction = (fn: any): boolean => typeof fn === "function";
+
+ onBeforeMount(() => {
+ initTheme();
+ if (isFunction(uni.onThemeChange)) {
+ uni.onThemeChange((res) => {
+ toggleTheme(res.theme);
+ });
+ }
+ });
+
+ onUnmounted(() => {
+ if (isFunction(uni.offThemeChange)) {
+ uni.offThemeChange((res) => {
+ toggleTheme(res.theme);
+ });
+ }
});
-};
-/* 导出主题相关的工具 */
-export const useTheme = () => {
return {
- theme,
+ theme: computed(() => theme.value),
+ isDark,
+ followSystem: computed(() => followSystem.value),
+ hasUserSet: computed(() => hasUserSet.value),
+ currentThemeColor: computed(() => currentThemeColor.value),
+ showThemeColorSheet,
themeVars,
- currentThemeColor,
- toggleTheme,
- setThemeColor,
- resetTheme,
+ themeColorOptions,
initTheme,
- colorColumns,
+ toggleTheme,
+ setFollowSystem,
+ openThemeColorPicker,
+ closeThemeColorPicker,
+ selectThemeColor,
};
-};
+}
diff --git a/src/layouts/default.vue b/src/layouts/default.vue
index b789731..a9a267a 100644
--- a/src/layouts/default.vue
+++ b/src/layouts/default.vue
@@ -1,22 +1,5 @@
-
-
-
-
+
+
-
+
diff --git a/src/layouts/tabbar.vue b/src/layouts/tabbar.vue
index 8b0aff2..048fcc7 100644
--- a/src/layouts/tabbar.vue
+++ b/src/layouts/tabbar.vue
@@ -1,31 +1,6 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/src/main.ts b/src/main.ts
index 764fe3b..9b30d79 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -5,19 +5,11 @@ import "uno.css";
import "@/styles/index.scss";
import { setupStore } from "@/store";
-// 可选:导入业务组件
-// import BusinessComponents from '@/components/business';
-
export function createApp() {
const app = createSSRApp(App);
setupStore(app);
- // 可选:全局注册业务组件
- // Object.entries(BusinessComponents).forEach(([name, component]) => {
- // app.component(name, component);
- // });
-
return {
app,
};
diff --git a/src/styles/index.scss b/src/styles/index.scss
index b0b8fc7..99a274f 100644
--- a/src/styles/index.scss
+++ b/src/styles/index.scss
@@ -1,5 +1,3 @@
-@import "./theme";
-
html,
body,
#app {
diff --git a/src/styles/theme.scss b/src/styles/theme.scss
deleted file mode 100644
index c84b48c..0000000
--- a/src/styles/theme.scss
+++ /dev/null
@@ -1,190 +0,0 @@
-/**
- * 统一主题系统
- * 简洁易懂,一个文件搞定所有主题
- */
-
-/* 亮色主题(默认) */
-:root,
-page {
- /* ===== 主题色 ===== */
- --wot-color-theme: #165dff;
- --primary-color: var(--wot-color-theme);
- --primary-color-light: #94bfff;
- --primary-color-dark: #0e3c9b;
-
- /* ===== 功能色 ===== */
- --wot-color-success: #0fc6c2;
- --wot-color-warning: #ff7d00;
- --wot-color-danger: #f5222d;
- --wot-color-info: #86909c;
-
- /* ===== 文本颜色 ===== */
- --wot-color-text: #1d2129;
- --wot-color-text-secondary: #4e5969;
- --wot-color-text-placeholder: #86909c;
- --wot-color-text-disabled: #c9cdd4;
-
- /* ===== 背景颜色 ===== */
- --wot-color-bg: #ffffff;
- --wot-color-bg-page: #f5f7fa;
- --wot-color-bg-light: #f8f9fa;
- --wot-color-bg-container: #ffffff;
-
- /* ===== 边框颜色 ===== */
- --wot-color-border: #e5e6eb;
- --wot-color-border-light: #f2f3f5;
-
- /* ===== 组件专用变量 ===== */
- --wot-card-bg-color: var(--wot-color-bg-container);
- --wot-card-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
- --wot-cell-bg-color: var(--wot-color-bg-container);
- --wot-popup-bg-color: var(--wot-color-bg-container);
- --wot-navbar-bg-color: var(--wot-color-bg-container);
- --wot-tabbar-bg-color: var(--wot-color-bg-container);
-}
-
-/* 暗黑主题 */
-[data-theme="dark"],
-[data-theme="dark"] page,
-.wot-theme-dark,
-.wot-theme-dark page {
- /* ===== 主题色(暗黑模式下稍微调亮) ===== */
- --wot-color-theme: #4080ff;
- --primary-color: var(--wot-color-theme);
- --primary-color-light: #6fa0ff;
- --primary-color-dark: #2060df;
-
- /* ===== 功能色 ===== */
- --wot-color-success: #1dd1cc;
- --wot-color-warning: #ff8c1a;
- --wot-color-danger: #ff4757;
- --wot-color-info: #9ca3af;
-
- /* ===== 文本颜色 ===== */
- --wot-color-text: #ffffff;
- --wot-color-text-secondary: #d1d5db;
- --wot-color-text-placeholder: #9ca3af;
- --wot-color-text-disabled: #6b7280;
-
- /* ===== 背景颜色 ===== */
- --wot-color-bg: #1a1a1a;
- --wot-color-bg-page: #0f0f0f;
- --wot-color-bg-light: #2a2a2a;
- --wot-color-bg-container: #1f1f1f;
-
- /* ===== 边框颜色 ===== */
- --wot-color-border: #404040;
- --wot-color-border-light: #606060;
-
- /* ===== 组件专用变量 ===== */
- --wot-card-bg-color: var(--wot-color-bg-container);
- --wot-card-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.3);
- --wot-cell-bg-color: var(--wot-color-bg-container);
- --wot-popup-bg-color: var(--wot-color-bg-container);
- --wot-navbar-bg-color: var(--wot-color-bg-container);
- --wot-tabbar-bg-color: var(--wot-color-bg-container);
-}
-
-/* 动态主题色类(这些会被 useTheme 动态应用到根元素) */
-.theme-color-165DFF {
- --wot-color-theme: #165dff;
- --primary-color: #165dff;
-}
-.theme-color-0FC6C2 {
- --wot-color-theme: #0fc6c2;
- --primary-color: #0fc6c2;
-}
-.theme-color-722ED1 {
- --wot-color-theme: #722ed1;
- --primary-color: #722ed1;
-}
-.theme-color-F5222D {
- --wot-color-theme: #f5222d;
- --primary-color: #f5222d;
-}
-.theme-color-FA8C16 {
- --wot-color-theme: #fa8c16;
- --primary-color: #fa8c16;
-}
-.theme-color-FADB14 {
- --wot-color-theme: #fadb14;
- --primary-color: #fadb14;
-}
-.theme-color-52C41A {
- --wot-color-theme: #52c41a;
- --primary-color: #52c41a;
-}
-.theme-color-EB2F96 {
- --wot-color-theme: #eb2f96;
- --primary-color: #eb2f96;
-}
-.theme-color-13C2C2 {
- --wot-color-theme: #13c2c2;
- --primary-color: #13c2c2;
-}
-.theme-color-1890FF {
- --wot-color-theme: #1890ff;
- --primary-color: #1890ff;
-}
-.theme-color-CD5C5C {
- --wot-color-theme: #cd5c5c;
- --primary-color: #cd5c5c;
-}
-.theme-color-228B22 {
- --wot-color-theme: #228b22;
- --primary-color: #228b22;
-}
-
-/* 全局基础样式 */
-page {
- color: var(--wot-color-text);
- background-color: var(--wot-color-bg-page);
- transition:
- background-color 0.3s ease,
- color 0.3s ease;
-}
-
-/* H5 环境下的 body 样式 */
-/* #ifdef H5 */
-body {
- color: var(--wot-color-text);
- background-color: var(--wot-color-bg-page);
- transition:
- background-color 0.3s ease,
- color 0.3s ease;
-}
-/* #endif */
-
-/* 通用组件样式重置 */
-view,
-text,
-button,
-input,
-textarea {
- transition:
- background-color 0.3s ease,
- color 0.3s ease,
- border-color 0.3s ease;
-}
-
-/* 确保所有 Wot 组件使用主题变量 */
-:deep(.wd-button--primary) {
- background-color: var(--wot-color-theme) !important;
- border-color: var(--wot-color-theme) !important;
-}
-
-:deep(.wd-cell) {
- background-color: var(--wot-cell-bg-color) !important;
-}
-
-:deep(.wd-navbar) {
- background-color: var(--wot-navbar-bg-color) !important;
-}
-
-:deep(.wd-tabbar) {
- background-color: var(--wot-tabbar-bg-color) !important;
-}
-
-:deep(.wd-icon) {
- color: var(--wot-color-theme) !important;
-}
diff --git a/src/types/auto-imports.d.ts b/src/types/auto-imports.d.ts
index 3e3bdcc..1551cad 100644
--- a/src/types/auto-imports.d.ts
+++ b/src/types/auto-imports.d.ts
@@ -6,22 +6,49 @@
// biome-ignore lint: disable
export {}
declare global {
+ const CommonUtil: typeof import('wot-design-uni')['CommonUtil']
const EffectScope: typeof import('vue')['EffectScope']
+ const Storage: typeof import('../utils/storage')['Storage']
+ const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
+ const applyThemeOnPageShow: typeof import('../utils/theme')['applyThemeOnPageShow']
+ const applyThemeToMiniProgram: typeof import('../utils/theme')['applyThemeToMiniProgram']
+ const auth: typeof import('../api/auth')['default']
+ const checkLogin: typeof import('../utils/auth')['checkLogin']
+ const clearAll: typeof import('../utils/storage')['clearAll']
+ const clearTokens: typeof import('../utils/auth')['clearTokens']
+ const colorColumns: typeof import('../composables/useTheme')['colorColumns']
const computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp']
+ const createPinia: typeof import('pinia')['createPinia']
+ const currentThemeColor: typeof import('../composables/useTheme')['currentThemeColor']
const customRef: typeof import('vue')['customRef']
+ const debounce: typeof import('../utils/index')['debounce']
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: typeof import('vue')['defineComponent']
+ const defineStore: typeof import('pinia')['defineStore']
const effectScope: typeof import('vue')['effectScope']
+ const file: typeof import('../api/file')['default']
+ const getAccessToken: typeof import('../utils/auth')['getAccessToken']
+ const getActivePinia: typeof import('pinia')['getActivePinia']
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope']
+ const getRefreshToken: typeof import('../utils/auth')['getRefreshToken']
+ const getToken: typeof import('../utils/storage')['getToken']
+ const getUserInfo: typeof import('../utils/storage')['getUserInfo']
const guessSerializerType: typeof import('@uni-helper/uni-use')['guessSerializerType']
const h: typeof import('vue')['h']
+ const initTheme: typeof import('../composables/useTheme')['initTheme']
const inject: typeof import('vue')['inject']
+ const isLoggedIn: typeof import('../utils/auth')['isLoggedIn']
const isProxy: typeof import('vue')['isProxy']
const isReactive: typeof import('vue')['isReactive']
const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef']
+ const mapActions: typeof import('pinia')['mapActions']
+ const mapGetters: typeof import('pinia')['mapGetters']
+ const mapState: typeof import('pinia')['mapState']
+ const mapStores: typeof import('pinia')['mapStores']
+ const mapWritableState: typeof import('pinia')['mapWritableState']
const markRaw: typeof import('vue')['markRaw']
const nextTick: typeof import('vue')['nextTick']
const onActivated: typeof import('vue')['onActivated']
@@ -65,17 +92,35 @@ declare global {
const onUpdated: typeof import('vue')['onUpdated']
const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
const provide: typeof import('vue')['provide']
+ const publicRequest: typeof import('../utils/request')['publicRequest']
const reactive: typeof import('vue')['reactive']
const readonly: typeof import('vue')['readonly']
const ref: typeof import('vue')['ref']
+ const request: typeof import('../utils/request')['default']
+ const requireLogin: typeof import('../utils/auth')['requireLogin']
+ const resetTheme: typeof import('../composables/useTheme')['resetTheme']
const resolveComponent: typeof import('vue')['resolveComponent']
+ const setAccessToken: typeof import('../utils/auth')['setAccessToken']
+ const setActivePinia: typeof import('pinia')['setActivePinia']
+ const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
+ const setRefreshToken: typeof import('../utils/auth')['setRefreshToken']
+ const setThemeColor: typeof import('../composables/useTheme')['setThemeColor']
+ const setToken: typeof import('../utils/storage')['setToken']
+ const setUserInfo: typeof import('../utils/storage')['setUserInfo']
+ const setupStore: typeof import('../store/index')['setupStore']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
+ const store: typeof import('../store/index')['store']
+ const storeToRefs: typeof import('pinia')['storeToRefs']
+ const theme: typeof import('../composables/useTheme')['theme']
+ const themeColorOptions: typeof import('../composables/useTheme')['themeColorOptions']
+ const themeVars: typeof import('../composables/useTheme')['themeVars']
const toRaw: typeof import('vue')['toRaw']
const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs']
const toValue: typeof import('vue')['toValue']
+ const toggleTheme: typeof import('../composables/useTheme')['toggleTheme']
const triggerRef: typeof import('vue')['triggerRef']
const tryOnBackPress: typeof import('@uni-helper/uni-use')['tryOnBackPress']
const tryOnHide: typeof import('@uni-helper/uni-use')['tryOnHide']
@@ -97,9 +142,11 @@ declare global {
const useInterceptor: typeof import('@uni-helper/uni-use')['useInterceptor']
const useLink: (typeof import("vue-router"))["useLink"]
const useLoading: typeof import('@uni-helper/uni-use')['useLoading']
+ const useMessage: typeof import('wot-design-uni')['useMessage']
const useModal: typeof import('@uni-helper/uni-use')['useModal']
const useModel: typeof import('vue')['useModel']
const useNetwork: typeof import('@uni-helper/uni-use')['useNetwork']
+ const useNotify: typeof import('wot-design-uni')['useNotify']
const useOnline: typeof import('@uni-helper/uni-use')['useOnline']
const usePage: typeof import('@uni-helper/uni-use')['usePage']
const usePageScroll: typeof import('@uni-helper/uni-use')['usePageScroll']
@@ -117,13 +164,20 @@ declare global {
const useSelectorQuery: typeof import('@uni-helper/uni-use')['useSelectorQuery']
const useSlots: typeof import('vue')['useSlots']
const useSocket: typeof import('@uni-helper/uni-use')['useSocket']
+ const useStomp: typeof import('../composables/useStomp')['useStomp']
const useStorage: typeof import('@uni-helper/uni-use')['useStorage']
const useStorageAsync: typeof import('@uni-helper/uni-use')['useStorageAsync']
const useStorageSync: typeof import('@uni-helper/uni-use')['useStorageSync']
+ const useTabbar: typeof import('../composables/useTabbar')['useTabbar']
const useTemplateRef: typeof import('vue')['useTemplateRef']
- const useToast: typeof import('@uni-helper/uni-use')['useToast']
+ const useTheme: typeof import('../composables/useTheme')['useTheme']
+ const useThemeStore: typeof import('../store/modules/theme.store')['useThemeStore']
+ const useToast: typeof import('wot-design-uni')['useToast']
const useUploadFile: typeof import('@uni-helper/uni-use')['useUploadFile']
+ const useUserStore: typeof import('../store/modules/user.store')['useUserStore']
const useVisible: typeof import('@uni-helper/uni-use')['useVisible']
+ const useWechat: typeof import('../composables/useWechat')['useWechat']
+ const user: typeof import('../api/user')['default']
const watch: typeof import('vue')['watch']
const watchEffect: typeof import('vue')['watchEffect']
const watchPostEffect: typeof import('vue')['watchPostEffect']
@@ -135,3 +189,176 @@ declare global {
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
import('vue')
}
+
+// for vue template auto import
+import { UnwrapRef } from 'vue'
+declare module 'vue' {
+ interface GlobalComponents {}
+ interface ComponentCustomProperties {
+ readonly CommonUtil: UnwrapRef
+ readonly EffectScope: UnwrapRef
+ readonly Storage: UnwrapRef
+ readonly acceptHMRUpdate: UnwrapRef
+ readonly applyThemeOnPageShow: UnwrapRef
+ readonly applyThemeToMiniProgram: UnwrapRef
+ readonly auth: UnwrapRef
+ readonly checkLogin: UnwrapRef
+ readonly clearAll: UnwrapRef
+ readonly clearTokens: UnwrapRef
+ readonly computed: UnwrapRef
+ readonly createApp: UnwrapRef
+ readonly createPinia: UnwrapRef
+ readonly customRef: UnwrapRef
+ readonly debounce: UnwrapRef
+ readonly defineAsyncComponent: UnwrapRef
+ readonly defineComponent: UnwrapRef
+ readonly defineStore: UnwrapRef
+ readonly effectScope: UnwrapRef
+ readonly file: UnwrapRef
+ readonly getAccessToken: UnwrapRef
+ readonly getActivePinia: UnwrapRef
+ readonly getCurrentInstance: UnwrapRef
+ readonly getCurrentScope: UnwrapRef
+ readonly getRefreshToken: UnwrapRef
+ readonly getToken: UnwrapRef
+ readonly getUserInfo: UnwrapRef
+ readonly guessSerializerType: UnwrapRef
+ readonly h: UnwrapRef
+ readonly inject: UnwrapRef
+ readonly isLoggedIn: UnwrapRef
+ readonly isProxy: UnwrapRef
+ readonly isReactive: UnwrapRef
+ readonly isReadonly: UnwrapRef
+ readonly isRef: UnwrapRef
+ readonly mapActions: UnwrapRef
+ readonly mapGetters: UnwrapRef
+ readonly mapState: UnwrapRef
+ readonly mapStores: UnwrapRef
+ readonly mapWritableState: UnwrapRef
+ readonly markRaw: UnwrapRef
+ readonly nextTick: UnwrapRef
+ readonly onActivated: UnwrapRef
+ readonly onAddToFavorites: UnwrapRef
+ readonly onBackPress: UnwrapRef
+ readonly onBeforeMount: UnwrapRef
+ readonly onBeforeUnmount: UnwrapRef
+ readonly onBeforeUpdate: UnwrapRef
+ readonly onDeactivated: UnwrapRef
+ readonly onError: UnwrapRef
+ readonly onErrorCaptured: UnwrapRef
+ readonly onHide: UnwrapRef
+ readonly onLaunch: UnwrapRef
+ readonly onLoad: UnwrapRef
+ readonly onMounted: UnwrapRef
+ readonly onNavigationBarButtonTap: UnwrapRef
+ readonly onNavigationBarSearchInputChanged: UnwrapRef
+ readonly onNavigationBarSearchInputClicked: UnwrapRef
+ readonly onNavigationBarSearchInputConfirmed: UnwrapRef
+ readonly onNavigationBarSearchInputFocusChanged: UnwrapRef
+ readonly onPageNotFound: UnwrapRef
+ readonly onPageScroll: UnwrapRef
+ readonly onPullDownRefresh: UnwrapRef
+ readonly onReachBottom: UnwrapRef
+ readonly onReady: UnwrapRef
+ readonly onRenderTracked: UnwrapRef
+ readonly onRenderTriggered: UnwrapRef
+ readonly onResize: UnwrapRef
+ readonly onScopeDispose: UnwrapRef
+ readonly onServerPrefetch: UnwrapRef
+ readonly onShareAppMessage: UnwrapRef
+ readonly onShareTimeline: UnwrapRef
+ readonly onShow: UnwrapRef
+ readonly onTabItemTap: UnwrapRef
+ readonly onThemeChange: UnwrapRef
+ readonly onUnhandledRejection: UnwrapRef
+ readonly onUnload: UnwrapRef
+ readonly onUnmounted: UnwrapRef
+ readonly onUpdated: UnwrapRef
+ readonly onWatcherCleanup: UnwrapRef
+ readonly provide: UnwrapRef
+ readonly publicRequest: UnwrapRef
+ readonly reactive: UnwrapRef
+ readonly readonly: UnwrapRef
+ readonly ref: UnwrapRef
+ readonly request: UnwrapRef
+ readonly requireLogin: UnwrapRef
+ readonly resolveComponent: UnwrapRef
+ readonly setAccessToken: UnwrapRef
+ readonly setActivePinia: UnwrapRef
+ readonly setMapStoreSuffix: UnwrapRef
+ readonly setRefreshToken: UnwrapRef
+ readonly setToken: UnwrapRef
+ readonly setUserInfo: UnwrapRef
+ readonly setupStore: UnwrapRef
+ readonly shallowReactive: UnwrapRef
+ readonly shallowReadonly: UnwrapRef
+ readonly shallowRef: UnwrapRef
+ readonly store: UnwrapRef
+ readonly storeToRefs: UnwrapRef
+ readonly themeColorOptions: UnwrapRef
+ readonly toRaw: UnwrapRef
+ readonly toRef: UnwrapRef
+ readonly toRefs: UnwrapRef
+ readonly toValue: UnwrapRef
+ readonly triggerRef: UnwrapRef
+ readonly tryOnBackPress: UnwrapRef
+ readonly tryOnHide: UnwrapRef
+ readonly tryOnInit: UnwrapRef
+ readonly tryOnLoad: UnwrapRef
+ readonly tryOnReady: UnwrapRef
+ readonly tryOnScopeDispose: UnwrapRef
+ readonly tryOnShow: UnwrapRef
+ readonly tryOnUnload: UnwrapRef
+ readonly unref: UnwrapRef
+ readonly useActionSheet: UnwrapRef
+ readonly useAttrs: UnwrapRef
+ readonly useClipboardData: UnwrapRef
+ readonly useCssModule: UnwrapRef
+ readonly useCssVars: UnwrapRef
+ readonly useDownloadFile: UnwrapRef
+ readonly useGlobalData: UnwrapRef
+ readonly useId: UnwrapRef
+ readonly useInterceptor: UnwrapRef
+ readonly useLoading: UnwrapRef
+ readonly useMessage: UnwrapRef
+ readonly useModal: UnwrapRef
+ readonly useModel: UnwrapRef
+ readonly useNetwork: UnwrapRef
+ readonly useNotify: UnwrapRef
+ readonly useOnline: UnwrapRef
+ readonly usePage: UnwrapRef
+ readonly usePageScroll: UnwrapRef
+ readonly usePages: UnwrapRef
+ readonly usePreferredDark: UnwrapRef
+ readonly usePreferredLanguage: UnwrapRef
+ readonly usePrevPage: UnwrapRef
+ readonly usePrevRoute: UnwrapRef
+ readonly useProvider: UnwrapRef
+ readonly useRequest: UnwrapRef
+ readonly useRoute: UnwrapRef
+ readonly useRouter: UnwrapRef
+ readonly useScanCode: UnwrapRef
+ readonly useScreenBrightness: UnwrapRef
+ readonly useSelectorQuery: UnwrapRef
+ readonly useSlots: UnwrapRef
+ readonly useSocket: UnwrapRef
+ readonly useStomp: UnwrapRef
+ readonly useStorage: UnwrapRef
+ readonly useStorageAsync: UnwrapRef
+ readonly useStorageSync: UnwrapRef
+ readonly useTabbar: UnwrapRef
+ readonly useTemplateRef: UnwrapRef
+ readonly useTheme: UnwrapRef
+ readonly useThemeStore: UnwrapRef
+ readonly useToast: UnwrapRef
+ readonly useUploadFile: UnwrapRef
+ readonly useUserStore: UnwrapRef
+ readonly useVisible: UnwrapRef
+ readonly useWechat: UnwrapRef
+ readonly user: UnwrapRef
+ readonly watch: UnwrapRef
+ readonly watchEffect: UnwrapRef
+ readonly watchPostEffect: UnwrapRef
+ readonly watchSyncEffect: UnwrapRef
+ }
+}
\ No newline at end of file
diff --git a/vite.config.ts b/vite.config.ts
index 05bb95e..b5748bd 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -44,11 +44,19 @@ export default defineConfig(async ({ mode }: ConfigEnv): Promise =>
}),
AutoImport({
- imports: ["vue", "uni-app", uniuseAutoImports()],
+ imports: [
+ "vue",
+ "uni-app",
+ "pinia",
+ uniuseAutoImports(),
+ {
+ from: "wot-design-uni",
+ imports: ["useToast", "useMessage", "useNotify", "CommonUtil"],
+ },
+ ],
dts: "src/types/auto-imports.d.ts", // 自动生成的类型声明文件
- eslintrc: {
- enabled: false,
- },
+ dirs: ["src/composables", "src/store", "src/utils", "src/api"],
+ vueTemplate: true,
}),
uni(),