diff --git a/.data/content/contents.sqlite b/.data/content/contents.sqlite deleted file mode 100644 index 9bfc441..0000000 Binary files a/.data/content/contents.sqlite and /dev/null differ diff --git a/.env b/.env new file mode 100644 index 0000000..c38fa3c --- /dev/null +++ b/.env @@ -0,0 +1,13 @@ +# API 基础路径 +VITE_APP_BASE_URL=/api + +# 请求头配置 +VITE_REQUEST_HEADER_CHANNEL_KEY=pc + +VITE_REQUEST_HEADER_TOKEN_KEY=Authorization + + # 默认语言 +VITE_APP_DEFAULT_LANG=zh-CN + +# 禁用 OXC 原生绑定(解决 Windows 安全策略问题) +OXC_DISABLE_NATIVE=1 diff --git a/.gitkeep b/.gitkeep new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/.gitkeep @@ -0,0 +1 @@ + diff --git a/.npmrc b/.npmrc index b22698a..7dc60b0 100644 --- a/.npmrc +++ b/.npmrc @@ -8,3 +8,5 @@ legacy-peer-deps=true node-linker=hoisted verify-store-integrity=false auto-install-peers=true +# Skip oxc native bindings due to security policy +public-hoist-pattern[]=@oxc-* diff --git a/AI_CONTEXT.md b/AI_CONTEXT.md new file mode 100644 index 0000000..f1bf5fe --- /dev/null +++ b/AI_CONTEXT.md @@ -0,0 +1,196 @@ +# AI 架构上下文 + +## 项目概览 + +这是一个基于 Nuxt 4 和 Element Plus 的现代化管理系统前端项目,使用 TypeScript 开发,旨在提供高效、美观、可扩展的管理界面。 + +### 技术栈 +- **前端框架**:Nuxt 4 +- **UI 库**:Element Plus +- **开发语言**:TypeScript +- **状态管理**:Pinia +- **国际化**:Vue I18n +- **样式方案**:SCSS + UnoCSS +- **图标库**:Nuxt Icons +- **构建工具**:Vite + +## 目录结构 + +``` +web/ +├── app/ # 主应用目录 +│ ├── api/ # API 接口定义 +│ ├── apps/ # 功能模块目录 +│ │ └── cms/ # CMS 功能模块 +│ ├── assets/ # 静态资源 +│ │ ├── icons/ # 图标文件 +│ │ ├── images/ # 图片文件 +│ │ └── scss/ # SCSS 样式文件 +│ ├── components/ # Vue 组件 +│ ├── composables/ # 组合式函数 +│ ├── lang/ # 国际化语言文件 +│ ├── layouts/ # 布局组件 +│ ├── pages/ # 页面组件 +│ ├── plugins/ # 插件 +│ ├── stores/ # Pinia 状态管理 +│ ├── types/ # TypeScript 类型定义 +│ ├── utils/ # 工具函数 +│ ├── app.vue # 根组件 +│ └── router.options.ts # 路由配置 +├── public/ # 公共静态资源 +├── .gitignore # Git 忽略文件 +├── nuxt.config.ts # Nuxt 配置 +├── package.json # 项目依赖和脚本 +├── tsconfig.json # TypeScript 配置 +└── uno.config.ts # UnoCSS 配置 +``` + +## 核心功能 + +### 认证系统 +- **登录/注册**:完整的用户认证流程 +- **验证码**:集成短信验证码和图形验证码 +- **路由守卫**:保护需要认证的路由 + +### 会员中心 +- **个人资料**:用户信息管理和编辑 +- **专属布局**:独立的会员中心布局 + +### 国际化 +- **多语言支持**:内置中文和英文 +- **动态切换**:实时语言切换功能 + +### 主题系统 +- **深色模式**:支持浅色/深色主题切换 +- **主题定制**:基于 SCSS 的灵活主题配置 + +### 路由系统 +- **动态路由**:从功能模块自动加载路由 +- **布局路由**:基于路由元信息的智能布局切换 + +## 技术架构 + +### 前端架构 +- **MVVM 模式**:基于 Vue 3 的响应式数据绑定 +- **组件化开发**:模块化、可复用的组件设计 +- **组合式 API**:使用 Vue 3 的组合式 API 进行状态管理和逻辑复用 +- **路由管理**:基于 Vue Router 的完整路由系统 +- **状态管理**:使用 Pinia 进行高效的全局状态管理 + +### 构建系统 +- **Vite**:现代化的开发服务器和构建工具 +- **ESLint**:确保代码质量和一致性 +- **TypeScript**:提供类型安全保障 +- **SCSS**:强大的 CSS 预处理器 +- **UnoCSS**:高性能的原子化 CSS 框架 + +### 部署方案 +- **Docker**:支持容器化部署 +- **Nitro**:Nuxt 的服务器引擎,支持 SSR/SSG 等多种渲染模式 + +## 核心 API 和服务 + +### API 接口 +- **认证接口**:`app/api/auth.ts` - 处理登录、注册等认证相关请求 +- **会员接口**:`app/api/member.ts` - 处理会员相关请求 +- **系统接口**:`app/api/system.ts` - 处理系统相关请求 + +### 工具函数 +- **请求工具**:`app/utils/request.ts` - 封装 HTTP 请求 +- **存储工具**:`app/utils/storage.ts` - 封装本地存储 +- **路由工具**:`app/utils/router.ts` - 路由相关工具函数 +- **验证工具**:`app/utils/validate.ts` - 表单验证工具 +- **语言工具**:`app/utils/language.ts` - 语言相关工具函数 +- **通用工具**:`app/utils/common.ts` - 通用工具函数 +- **深色模式工具**:`app/utils/dark.ts` - 深色模式相关工具函数 + +### 状态管理 +- **应用状态**:`app/stores/app.ts` - 应用全局状态 +- **配置状态**:`app/stores/config.ts` - 系统配置状态 +- **全局状态**:`app/stores/globals.ts` - 全局共享状态 +- **会员状态**:`app/stores/member.ts` - 会员相关状态 +- **个人中心状态**:`app/stores/personal-center.ts` - 个人中心相关状态 +- **系统状态**:`app/stores/system.ts` - 系统相关状态 + +## 配置信息 + +### Nuxt 配置 +- **模块集成**:Element Plus、Pinia、VueUse、UnoCSS 等 +- **CSS 配置**:SCSS 和 UnoCSS +- **路由配置**:自定义路由,支持动态加载功能模块路由 +- **服务器代理**:配置 API 代理,解决跨域问题 + +### 依赖管理 +- **包管理工具**:pnpm +- **核心依赖**:Element Plus、Vue I18n、Pinia、NProgress 等 +- **开发依赖**:TypeScript、ESLint、Sass 等 + +## 关键文件和模块 + +### 路由配置 +- `app/router.options.ts`:自定义路由配置,支持从功能模块自动加载路由 +- `app/pages/routes.ts`:主应用路由定义 +- `app/apps/cms/pages/routes.ts`:CMS 功能模块路由定义 + +### 布局组件 +- `app/layouts/default.vue`:默认布局 +- `app/layouts/member.vue`:会员中心布局 +- `app/layouts/container.vue`:容器布局 + +### 页面组件 +- `app/pages/index.vue`:首页 +- `app/pages/member/profile.vue`:会员个人资料页 +- `app/apps/cms/pages/index.vue`:CMS 首页 + +### 核心组件 +- `app/components/login-dialog/`:登录/注册对话框 +- `app/components/sms-code/`:短信验证码组件 +- `app/components/icon.vue`:图标组件 + +### 组合式函数 +- `app/composables/login.ts`:登录相关逻辑 +- `app/composables/captcha.ts`:验证码相关逻辑 +- `app/composables/lang.ts`:语言相关逻辑 +- `app/composables/send-sms.ts`:发送短信相关逻辑 + +## 开发规范 + +### 代码风格 +- 使用 TypeScript 进行类型检查 +- 使用 ESLint 进行代码质量检查 +- 遵循 Vue 3 的最佳实践 + +### 命名规范 +- **组件名**:使用 PascalCase(如 `LoginDialog`) +- **文件名**:使用 kebab-case(如 `login-dialog.vue`) +- **变量名**:使用 camelCase(如 `userInfo`) +- **常量名**:使用 UPPER_CASE(如 `API_BASE_URL`) + +### 目录结构规范 +- 功能模块放在 `app/apps/` 目录下 +- 通用组件放在 `app/components/` 目录下 +- 页面组件放在 `app/pages/` 目录下 +- 工具函数放在 `app/utils/` 目录下 + +## 未来扩展 + +### 功能扩展 +- 支持更多功能模块的集成 +- 增强国际化支持,添加更多语言 +- 提供更多主题选项和自定义能力 + +### 性能优化 +- 实现更细粒度的代码分割和懒加载 +- 优化静态资源加载 +- 进一步优化构建流程 + +### 安全增强 +- 加强认证和授权机制 +- 防止 XSS 和 CSRF 攻击 +- 实现敏感数据加密 + +## 总结 + +本项目是一个现代化的前端应用,基于 Nuxt 4 和 Element Plus 构建,使用 TypeScript 确保类型安全,采用组件化、模块化的开发方式。项目支持国际化、深色模式等现代前端特性,结构清晰,代码组织合理,具有良好的可扩展性和可维护性。 + +通过使用 Nuxt 4 的强大功能和 Element Plus 的丰富组件,项目能够快速构建出美观、高效的管理系统界面,同时为未来的功能扩展和性能优化提供了坚实的基础。项目使用 pnpm 作为包管理工具,提高了依赖安装和管理的效率。 \ No newline at end of file diff --git a/README.en.md b/README.en.md index 97c85c3..efde29e 100644 --- a/README.en.md +++ b/README.en.md @@ -1,36 +1,92 @@ # Madong-Nuxt -#### Description -MadongAdmin的Nuxt-Web工程,Nuxt4.x+TypeScript+Vite+Pinia+Element Plus +## Project Introduction -#### Software Architecture -Software architecture description +MadongAdmin's Nuxt-Web project, a modern admin system frontend built with Nuxt 4.x, TypeScript, Vite, Pinia, and Element Plus. -#### Installation +## Tech Stack -1. xxxx -2. xxxx -3. xxxx +- **Frontend Framework**: Nuxt 4 +- **UI Library**: Element Plus +- **Development Language**: TypeScript +- **State Management**: Pinia +- **Internationalization**: Vue I18n +- **Styling Solution**: SCSS + UnoCSS +- **Icon Library**: Nuxt Icons +- **Build Tool**: Vite -#### Instructions +## Core Features -1. xxxx -2. xxxx -3. xxxx +- **Authentication System**: Login/Register, CAPTCHA, Route Guard +- **Member Center**: Personal profile management +- **Internationalization**: Multi-language support (Chinese/English) +- **Theme System**: Light/Dark mode switching +- **Routing System**: Dynamic routes, Layout routes -#### Contribution +## Directory Structure + +``` +web/ +├── app/ # Main application directory +│ ├── api/ # API interface definitions +│ ├── apps/ # Feature modules directory +│ ├── assets/ # Static assets +│ ├── components/ # Vue components +│ ├── composables/ # Composables +│ ├── lang/ # Internationalization language files +│ ├── layouts/ # Layout components +│ ├── pages/ # Page components +│ ├── plugins/ # Plugins +│ ├── stores/ # Pinia state management +│ ├── types/ # TypeScript type definitions +│ ├── utils/ # Utility functions +│ ├── app.vue # Root component +│ └── router.options.ts # Route configuration +├── public/ # Public static resources +├── .gitignore # Git ignore file +├── nuxt.config.ts # Nuxt configuration +├── package.json # Project dependencies and scripts +├── tsconfig.json # TypeScript configuration +└── uno.config.ts # UnoCSS configuration +``` + +## Installation + +1. Clone the repository + ```bash + git clone + cd web + ``` + +2. Install dependencies + ```bash + pnpm install + ``` + +3. Start development server + ```bash + pnpm run dev + ``` + +4. Build for production + ```bash + pnpm run build + ``` + +## Usage + +1. Development mode: `pnpm run dev` - Start development server with hot reload +2. Build for production: `pnpm run build` - Build production version +3. Preview production: `pnpm run preview` - Preview production build +4. Code linting: `pnpm run lint` - Run ESLint to check code quality + +## Contribution 1. Fork the repository 2. Create Feat_xxx branch 3. Commit your code 4. Create Pull Request +## License -#### Gitee Feature - -1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md -2. Gitee blog [blog.gitee.com](https://blog.gitee.com) -3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) -4. The most valuable open source project [GVP](https://gitee.com/gvp) -5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) -6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) +[MIT](LICENSE) diff --git a/README.md b/README.md index 3b46c31..fbce263 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,92 @@ # Madong-Nuxt -#### 介绍 -MadongAdmin的Nuxt-Web工程,Nuxt4.x+TypeScript+Vite+Pinia+Element Plus +## 项目介绍 -#### 软件架构 -软件架构说明 +MadongAdmin的Nuxt-Web工程,基于Nuxt 4.x、TypeScript、Vite、Pinia和Element Plus构建的现代化管理系统前端。 +## 技术栈 -#### 安装教程 +- **前端框架**:Nuxt 4 +- **UI 库**:Element Plus +- **开发语言**:TypeScript +- **状态管理**:Pinia +- **国际化**:Vue I18n +- **样式方案**:SCSS + UnoCSS +- **图标库**:Nuxt Icons +- **构建工具**:Vite -1. xxxx -2. xxxx -3. xxxx +## 核心功能 -#### 使用说明 +- **认证系统**:登录/注册、验证码、路由守卫 +- **会员中心**:个人资料管理 +- **国际化**:多语言支持(中文/英文) +- **主题系统**:深色/浅色模式切换 +- **路由系统**:动态路由、布局路由 -1. xxxx -2. xxxx -3. xxxx +## 目录结构 -#### 参与贡献 +``` +web/ +├── app/ # 主应用目录 +│ ├── api/ # API 接口定义 +│ ├── apps/ # 功能模块目录 +│ ├── assets/ # 静态资源 +│ ├── components/ # Vue 组件 +│ ├── composables/ # 组合式函数 +│ ├── lang/ # 国际化语言文件 +│ ├── layouts/ # 布局组件 +│ ├── pages/ # 页面组件 +│ ├── plugins/ # 插件 +│ ├── stores/ # Pinia 状态管理 +│ ├── types/ # TypeScript 类型定义 +│ ├── utils/ # 工具函数 +│ ├── app.vue # 根组件 +│ └── router.options.ts # 路由配置 +├── public/ # 公共静态资源 +├── .gitignore # Git 忽略文件 +├── nuxt.config.ts # Nuxt 配置 +├── package.json # 项目依赖和脚本 +├── tsconfig.json # TypeScript 配置 +└── uno.config.ts # UnoCSS 配置 +``` + +## 安装教程 + +1. 克隆仓库 + ```bash + git clone <仓库地址> + cd web + ``` + +2. 安装依赖 + ```bash + pnpm install + ``` + +3. 启动开发服务器 + ```bash + pnpm run dev + ``` + +4. 构建生产版本 + ```bash + pnpm run build + ``` + +## 使用说明 + +1. 开发模式:`pnpm run dev` - 启动开发服务器,支持热更新 +2. 构建生产:`pnpm run build` - 构建生产版本 +3. 预览生产:`pnpm run preview` - 预览生产构建结果 +4. 代码检查:`pnpm run lint` - 运行 ESLint 检查代码质量 + +## 参与贡献 1. Fork 本仓库 2. 新建 Feat_xxx 分支 3. 提交代码 4. 新建 Pull Request +## 许可证 -#### 特技 - -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 -5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) +[MIT](LICENSE) diff --git a/app/api/auth.ts b/app/api/auth.ts deleted file mode 100644 index cb968f5..0000000 --- a/app/api/auth.ts +++ /dev/null @@ -1,87 +0,0 @@ - -/** - * 用户登录认证 - */ -export function authenticateUser(data: AnyObject) { - return request.post('/auth/login', data) -} - -/** - * 手机验证码登录 - */ -export function authenticateWithMobile(data: AnyObject) { - return request.post('/auth/login/mobile', data) -} - -/** - * 获取登录配置信息 - */ -export function getLoginConfig() { - return request.get('/auth/config') -} - -/** - * 用户退出登录 - */ -export function logoutUser() { - return request.post('/auth/logout') -} - -/** - * 用户注册 - */ -export function registerUser(data: AnyObject) { - const url = data.pid ? `/auth/register?pid=${data.pid}` : '/auth/register' - return request.post(url, data) -} - -/** - * 手机号注册 - */ -export function registerWithMobile(data: AnyObject) { - const url = data.pid ? `/auth/register/mobile?pid=${data.pid}` : '/auth/register/mobile' - return request.post(url, data) -} - -/** - * 微信授权登录 - */ -export function authenticateWithWechat(data: AnyObject) { - return request.post('/auth/wechat', data) -} - -/** - * 微信小程序登录 - */ -export function authenticateWithWeapp(data: AnyObject) { - return request.post('/auth/weapp', data) -} - -/** - * 绑定手机号 - */ -export function bindMobile(data: AnyObject) { - const url = data.pid ? `/auth/bind?pid=${data.pid}` : '/auth/bind' - return request.post(url, data) -} - -/** - * 生成微信扫码登录二维码 - */ -export function generateWechatQrCode() { - return request.post('/auth/wechat/scan') -} - -/** - * 检查微信扫码状态 - */ -export function checkWechatScanStatus(data: AnyObject) { - return request.get('/auth/wechat/scan/status', data) -} - -/** - * 检查微信登录可用性 - */ -export function checkWechatAvailability() { - return request.get('/auth/wechat/availability') -} \ No newline at end of file diff --git a/app/api/auth/index.ts b/app/api/auth/index.ts new file mode 100644 index 0000000..9756425 --- /dev/null +++ b/app/api/auth/index.ts @@ -0,0 +1,188 @@ +import request from '~/utils/request' + +/** + * 用户登录认证 + */ +export function authenticateUser(data: { + username: string + password: string +}): Promise { + return request.post('/auth/login', data) +} + +/** + * 手机验证码登录 + */ +export function authenticateWithMobile(data: { + mobile: string + code: string + key: string +}): Promise { + return request.post('/auth/login/mobile', data) +} + +/** + * 用户退出登录 + */ +export function logoutUser(): Promise { + return request.post('/auth/logout') +} + +/** + * 用户注册 + */ +export function registerUser(data: { + username: string + email: string + password: string + confirm_password: string + pid?: string +}): Promise { + const url = data.pid ? `/auth/register?pid=${data.pid}` : '/auth/register' + return request.post(url, data) +} + +/** + * 手机号注册 + */ +export function registerWithMobile(data: { + mobile: string + code: string + key: string + pid?: string +}): Promise { + const url = data.pid ? `/auth/register/mobile?pid=${data.pid}` : '/auth/register/mobile' + return request.post(url, data) +} + +/** + * 微信授权登录 + */ +export function authenticateWithWechat(data: any): Promise { + return request.post('/auth/wechat', data) +} + +/** + * 微信小程序登录 + */ +export function authenticateWithWeapp(data: any): Promise { + return request.post('/auth/weapp', data) +} + +/** + * 绑定手机号 + */ +export function bindMobile(data: { + mobile: string + code: string + pid?: string +}): Promise { + const url = data.pid ? `/auth/bind?pid=${data.pid}` : '/auth/bind' + return request.post(url, data) +} + +/** + * 生成微信扫码登录二维码 + */ +export function generateWechatQrCode(): Promise { + return request.post('/auth/wechat/scan') +} + +/** + * 检查微信扫码状态 + */ +export function checkWechatScanStatus(data: { scene_id: string }): Promise { + return request.get('/auth/wechat/scan/status', data) +} + +/** + * 检查微信登录可用性 + */ +export function checkWechatAvailability(): Promise { + return request.get('/auth/wechat/availability') +} + +/** + * 获取第三方绑定列表 + */ +export function getThirdPartyList(): Promise { + return request.get('/auth/third-party/list') +} + +/** + * 绑定第三方账号 + */ +export function bindThirdParty(data: { platform: number }): Promise { + return request.post('/auth/third-party/bind', data) +} + +/** + * 解绑第三方账号 + */ +export function unbindThirdParty(platform: number): Promise { + return request.delete(`/auth/third-party/unbind/${platform}`) +} + +/** + * 生成第三方绑定二维码 + */ +export function generateBindQrCode(platform: number): Promise { + return request.post('/auth/third-party/qr-code', { platform }) +} + +/** + * 检查绑定二维码状态 + */ +export function checkBindQrStatus(sceneId: string): Promise { + return request.get('/auth/third-party/qr-status', { scene_id: sceneId }) +} + +/** + * 验证邮箱(第一步:验证邮箱和验证码) + */ +export function verifyEmail(data: { + email: string + code: string +}): Promise { + return request.post('/auth/verify-email', data) +} + +/** + * 发送邮箱验证码 + */ +export function sendEmailCode(data: { + email: string + captcha_key: string + captcha_code: string +}): Promise { + return request.post('/auth/send-email-code', data) +} + +/** + * 忘记密码 - 重置密码(第二步:设置新密码) + */ +export function forgetPassword(data: { + email: string + code: string + new_password: string + confirm_password: string +}): Promise { + return request.post('/auth/forget-password', data) +} + +/** + * 刷新 Token + */ +export function refreshToken(refresh_token: string): Promise<{ + code: number + msg: string + data?: { + access_token: string + refresh_token: string + } +}> { + return request.post('/auth/refresh', { refresh_token }, { + // 不带 token 请求,避免 401 循环 + headers: {} + } as any) +} diff --git a/app/api/auth/types.ts b/app/api/auth/types.ts new file mode 100644 index 0000000..03d1862 --- /dev/null +++ b/app/api/auth/types.ts @@ -0,0 +1,99 @@ +/** + * 认证模块相关类型定义 + */ + +/** + * 登录数据 + */ +export interface LoginData { + username?: string + password?: string + phone?: string + verify_code?: string + pid?: string +} + +/** + * 注册数据 + */ +export interface RegisterData { + username?: string + password?: string + phone?: string + verify_code?: string + pid?: string +} + +/** + * 绑定手机号数据 + */ +export interface BindMobileData { + mobile: string + code: string + pid?: string +} + +/** + * 用户信息(登录/注册后返回) + */ +export interface UserInfo { + id: number + username: string + nickname?: string + avatar?: string + phone?: string + email?: string + token: string +} + +/** + * 登录配置 + */ +export interface LoginConfig { + enable_username_login: boolean + enable_mobile_login: boolean + enable_wechat_login: boolean + enable_weapp_login: boolean + enable_register: boolean +} + +/** + * 扫码状态 + */ +export enum ScanStatus { + WAITING = 'waiting', + SCANNED = 'scanned', + CONFIRMED = 'confirmed', + EXPIRED = 'expired', + CANCELED = 'canceled' +} + +/** + * 第三方平台类型 + */ +export enum ThirdPartyPlatform { + WECHAT = 1, + QQ = 2, + ALIPAY = 3 +} + +/** + * 第三方绑定信息 + */ +export interface ThirdPartyBinding { + platform: number + platform_name: string + nickname?: string + avatar?: string + bind_time: number +} + +/** + * 扫码登录信息 + */ +export interface QrCodeLoginInfo { + scene_id: string + qrcode_url: string + expires_in: number +} + diff --git a/app/api/member.ts b/app/api/member.ts deleted file mode 100644 index 9cdc9d9..0000000 --- a/app/api/member.ts +++ /dev/null @@ -1,55 +0,0 @@ -/** - * 获取会员基本信息 - */ -export function getMemberProfile() { - return request.get('/member/profile') -} - -/** - * 更新会员信息 - */ -export function updateMemberInfo(data: AnyObject) { - return request.put(`/member/profile/${data.field}`, data) -} - -/** - * 获取积分流水记录 - */ -export function getPointTransactions(data: AnyObject) { - return request.get('/member/points/transactions', data) -} - -/** - * 获取会员积分总额 - */ -export function getMemberPointsTotal() { - return request.get('/member/points/total') -} - -/** - * 获取余额流水记录 - */ -export function getBalanceTransactions(data: AnyObject) { - return request.get('/member/balance/transactions', data) -} - -/** - * 获取所有余额流水记录 - */ -export function getAllBalanceTransactions(data: AnyObject) { - return request.get('/member/balance/transactions/all', data) -} - -/** - * 绑定会员手机号 - */ -export function bindMemberMobile(data: AnyObject) { - return request.put('/member/mobile', data) -} - -/** - * 获取会员等级信息 - */ -export function getMemberLevel() { - return request.get('/member/level') -} \ No newline at end of file diff --git a/app/api/member/index.ts b/app/api/member/index.ts new file mode 100644 index 0000000..d949d3d --- /dev/null +++ b/app/api/member/index.ts @@ -0,0 +1,189 @@ +import request from '~/utils/request' +import type { + MemberInfo, + Address, + PointTransactionsResponse, + BalanceTransactionsResponse, + SignCalendar, + SignStatistics, + MemberLevel, + SignStatus +} from './types' + +/** + * 获取会员基本信息 + */ +export function getMemberProfile(): Promise { + return request.get('/member/user-info') +} + +/** + * 更新会员信息 + */ +export function updateMemberInfo(data: Partial): Promise { + return request.put('/member/user/update', data) +} + +/** + * 获取积分流水记录 + */ +export function getPointTransactions(data: { + page?: number + page_size?: number +}): Promise { + return request.get('/member/points/record', data) +} + +/** + * 获取会员积分总额 + */ +export function getMemberPointsTotal(): Promise { + return request.get('/member/points/total') +} + +/** + * 获取余额流水记录 + */ +export function getBalanceTransactions(data: { + page?: number + page_size?: number +}): Promise { + return request.get('/member/balance/record', data) +} + +/** + * 获取所有余额流水记录 + */ +export function getAllBalanceTransactions(data?: { + page?: number + page_size?: number +}): Promise { + return request.get('/member/balance/all', data) +} + +/** + * 获取会员等级信息 + */ +export function getMemberLevel(): Promise { + return request.get('/member/points/level') +} + +/** + * 会员签到 + */ +export function memberSign(): Promise { + return request.post('/member/points/sign') +} + +/** + * 执行签到 + */ +export function signIn(): Promise { + return request.post('/member/sign') +} + +/** + * 获取签到状态 + */ +export function getSignStatus(): Promise { + return request.get('/member/sign/status') +} + +/** + * 获取签到日历 + */ +export function getSignCalendar(year: number, month: number): Promise { + return request.get('/member/sign/calendar', { year, month }) +} + +/** + * 获取签到统计 + */ +export function getSignStatistics(type: string = 'month'): Promise { + return request.get('/member/sign/statistics', { type }) +} + +/** + * 修改密码 + */ +export function changePassword(data: { + old_password: string + new_password: string + confirm_password: string +}): Promise { + return request.put('/member/user/change-password', data) +} + +/** + * 获取地址列表 + */ +export function getAddressList(): Promise { + return request.get('/member/user/address/list') +} + +/** + * 创建地址 + */ +export function createAddress(data: Partial
): Promise
{ + return request.post('/member/user/address/create', data) +} + +/** + * 更新地址 + */ +export function updateAddress(id: number, data: Partial
): Promise { + return request.put(`/member/user/address/update/${id}`, data) +} + +/** + * 删除地址 + */ +export function deleteAddress(id: number): Promise { + return request.delete(`/member/user/address/delete/${id}`) +} + +/** + * 获取验证码 + */ +export function getVerificationCode(data: { type: string; phone: string }): Promise { + return request.post('/member/user/send-code', data) +} + +/** + * 修改手机号码 + */ +export function updateMemberMobile(data: { + oldMobile: string + newMobile: string + code: string +}): Promise { + return request.put('/member/user/update-phone', { + old_phone: data.oldMobile, + new_phone: data.newMobile, + verify_code: data.code + }) +} + +/** + * 修改邮箱 + */ +export function updateMemberEmail(data: { email: string; code: string }): Promise { + return request.put('/member/user/update-email', { + email: data.email, + verify_code: data.code + }) +} + +/** + * 上传头像 + */ +export function uploadAvatar(file: File): Promise { + const formData = new FormData() + formData.append('file', file) + return request.post('/member/user/avatar', formData, { + headers: { + 'Content-Type': 'multipart/form-data' + } as any + }) +} + diff --git a/app/api/member/types.ts b/app/api/member/types.ts new file mode 100644 index 0000000..33f5b4c --- /dev/null +++ b/app/api/member/types.ts @@ -0,0 +1,122 @@ +/** + * 会员模块相关类型定义 + */ + +/** + * 会员基本信息 + */ +export interface MemberInfo { + id: number + username: string + nickname?: string + avatar?: string + phone?: string + email?: string + points: number + balance: number + level: number + level_name?: string + created_at: number + updated_at: number +} + +/** + * 地址 + */ +export interface Address { + id: number + user_id: number + consignee: string + phone: string + province: string + city: string + district: string + address: string + postal_code?: string + is_default: number + created_at: number + updated_at: number +} + +/** + * 积分流水记录 + */ +export interface PointTransaction { + id: number + user_id: number + type: string + amount: number + balance: number + description: string + created_at: number +} + +/** + * 积分流水响应 + */ +export interface PointTransactionsResponse { + total: number + list: PointTransaction[] +} + +/** + * 余额流水记录 + */ +export interface BalanceTransaction { + id: number + user_id: number + type: string + amount: number + balance: number + description: string + created_at: number +} + +/** + * 余额流水响应 + */ +export interface BalanceTransactionsResponse { + total: number + list: BalanceTransaction[] +} + +/** + * 签到日历 + */ +export interface SignCalendar { + year: number + month: number + signed_dates: number[] + total_signed: number + total_can_sign: number +} + +/** + * 签到统计 + */ +export interface SignStatistics { + total_signed: number + continuous_signed: number + this_month_signed: number + today_signed: boolean +} + +/** + * 会员等级信息 + */ +export interface MemberLevel { + level: number + level_name: string + points: number + next_level_points?: number +} + +/** + * 签到状态 + */ +export interface SignStatus { + today_signed: boolean + continuous_days: number + can_sign: boolean +} + diff --git a/app/api/site/index.ts b/app/api/site/index.ts new file mode 100644 index 0000000..2848c4f --- /dev/null +++ b/app/api/site/index.ts @@ -0,0 +1,47 @@ +import request from '~/utils/request' +import type { LinkItem, AdvertisementItem, NavigationItem, SiteData } from './types' + +/** + * 获取友情链接列表 + */ +export function getLinks(params?: { type?: string }): Promise { + return request.get('/site/link', params) +} + +/** + * 获取所有友情链接 + */ +export function getAllLinks(): Promise { + return request.get('/site/link/all') +} + +/** + * 获取广告位信息 + */ +export function getAdvertisements(params?: { type?: string }): Promise { + return request.get('/site/ads', params) +} + +/** + * 获取导航菜单列表 + */ +export function getNavigationList(): Promise { + return request.get('/site/nav') +} + +/** + * 获取站点首页数据(广告、导航、友情链接等) + */ +export function getSiteData(): Promise { + return request.get('/site/site-data') +} + +/** + * 获取 Web 分组所有配置 + * 一次性获取前台站点设置、搜索配置、统计分析配置等 + */ +export function getWebGroupConfigs(): Promise> { + return request.get('/system/config/group/web') +} + + diff --git a/app/api/site/types.ts b/app/api/site/types.ts new file mode 100644 index 0000000..1f7b3bf --- /dev/null +++ b/app/api/site/types.ts @@ -0,0 +1,82 @@ +/** + * 站点首页模块相关类型定义 + */ + +/** + * 友情链接项 + */ +export interface LinkItem { + id: number + name: string + url: string + logo?: string + description?: string + category?: string + sort: number + target?: string + click_count?: number + enabled: number + created_at: number + updated_at: number + deleted_at: number | null +} + +/** + * 广告项 + */ +export interface AdvertisementItem { + id: number + title: string + link: string + image: string + description?: string + sort: number + enabled: number + start_time: number + end_time: number + created_at: number + updated_at: number + deleted_at: number | null +} + +/** + * 导航菜单项 + */ +export interface NavigationItem { + id: number + title: string + url: string + icon?: string + code: string | null + sort: number + enabled: number + is_show: number +} + +/** + * 站点首页数据 + */ +export interface SiteData { + advertisements: AdvertisementItem[] + navigation: NavigationItem[] + links: LinkItem[] +} + +/** + * 搜索配置项 + */ +export interface SearchConfig { + keyword: string // 搜索引擎关键字 + keyword_enabled: number // 是否启用关键字搜索 (0/1) +} + +/** + * 统计分析配置项 + */ +export interface AnalyticsConfig { + baidu_tongji_code: string // 百度统计代码 + baidu_tongji_enabled: number // 是否启用百度统计 (0/1) +} + + + diff --git a/app/api/system.ts b/app/api/system.ts deleted file mode 100644 index 495f9a9..0000000 --- a/app/api/system.ts +++ /dev/null @@ -1,97 +0,0 @@ -/** - * 获取验证码图片 - */ -export function getCaptchaImage() { - return request.get('/system/captcha', { time: Date.now() }) -} - -/** - * 获取微信授权码 - */ -export function getWechatAuthCode(data: AnyObject) { - return request.get('/system/wechat/auth-code', data) -} - -/** - * 同步微信用户信息 - */ -export function syncWechatUserInfo(data: AnyObject) { - return request.get('/system/wechat/sync', data) -} - -/** - * 获取协议内容 - */ -export function getAgreementContent(key: string) { - return request.get(`/system/agreement/${key}`) -} - -/** - * 重置用户密码 - */ -export function resetUserPassword(data: AnyObject) { - return request.post('/system/password/reset', data) -} - -/** - * 发送短信验证码 - */ -export function sendSmsVerificationCode(data: AnyObject) { - return request.post(`/system/sms/${data.type}`, data) -} - -/** - * 获取微信 JSSDK 配置 - */ -export function getWechatJssdkConfig(data: AnyObject) { - return request.get('/system/wechat/jssdk-config', data) -} - -/** - * 获取远程图片 - */ -export function fetchRemoteImage(data: AnyObject) { - return request.post('/system/image/fetch', data) -} - -/** - * 获取 Base64 格式图片 - */ -export function fetchBase64Image(data: AnyObject) { - return request.post('/system/image/base64', data) -} - -/** - * 获取版权信息 - */ -export function getCopyrightInfo() { - return request.get('/system/copyright') -} - -/** - * 获取站点信息 - */ -export function getSiteInformation() { - return request.get('/system/site') -} - -/** - * 获取广告位信息 - */ -export function getAdvertisementInfo(params: Record) { - return request.get('/system/web/advertisements', params, { showErrorMessage: false }) -} - -/** - * 获取导航菜单列表 - */ -export function getNavigationList() { - return request.get('/system/web/navigation') -} - -/** - * 获取友情链接列表 - */ -export function getFriendlyLinks() { - return request.get('/system/web/friendly-links') -} \ No newline at end of file diff --git a/app/api/system/index.ts b/app/api/system/index.ts new file mode 100644 index 0000000..d190582 --- /dev/null +++ b/app/api/system/index.ts @@ -0,0 +1,64 @@ +import request from '~/utils/request' +import type { SiteConfig, AgreementType, SmsType } from './types' + +/** + * 按分组获取配置 + */ +export function getConfigByGroup(groupCode: string, options?: { + enabled_only?: boolean + with_metadata?: boolean +}) { + return request.get(`/system/config/group/${groupCode}`, options) +} + +/** + * 按 code 获取配置 + */ +export function getConfigByCode(code: string, options?: { group_code?: string}) { + return request.get(`/system/config/code/${code}`, options) +} + + +/** + * 获取协议内容 + */ +export function getAgreementContent(key: AgreementType): Promise { + return request.get(`/agreement/${key}`) +} + +/** + * 发送短信验证码 + */ +export function sendSmsVerificationCode(data: { + type: SmsType + phone: string + [key: string]: any +}): Promise { + return request.post(`/sms/${data.type}`, data) +} + +/** + * 获取验证码图片 + */ +export function getCaptchaImage() { + return request.get('/captcha', { time: Date.now() }, { + showSuccessMessage: false, + returnFullResponse: true + }) +} + + +/** + * 获取远程图片 + */ +export function fetchRemoteImage(data: { url: string }): Promise { + return request.post('/image/fetch', data) +} + +/** + * 获取 Base64 格式图片 + */ +export function fetchBase64Image(data: { url: string }): Promise { + return request.post('/image/base64', data) +} + diff --git a/app/api/system/types.ts b/app/api/system/types.ts new file mode 100644 index 0000000..b99508c --- /dev/null +++ b/app/api/system/types.ts @@ -0,0 +1,41 @@ +/** + * 系统配置相关类型定义 + */ + +/** + * 配置项 + */ +export interface ConfigItem { + id: number + group_code: string + code: string + name: string + content: Record + is_sys: number + enabled: number + created_at: number + updated_at: number + remark?: string +} + +/** + * 站点设置配置 + */ +export interface SiteConfig { + copyright: string + icp: string + icp_url: string + network_security: string + network_security_url: string +} + +/** + * 协议类型 + */ +export type AgreementType = 'user' | 'privacy' | 'service' + +/** + * 短信/邮箱验证码类型 + */ +export type SmsType = 'register' | 'login' | 'reset_password' | 'reset_password_email' | 'change_phone' | 'change_email' + diff --git a/app/app.vue b/app/app.vue index bee6165..0a3ea27 100644 --- a/app/app.vue +++ b/app/app.vue @@ -1,26 +1,57 @@ diff --git a/app/apps/.gitkeep b/app/apps/.gitkeep new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/app/apps/.gitkeep @@ -0,0 +1 @@ + diff --git a/app/assets/css/uno-reset.css b/app/assets/css/uno-reset.css new file mode 100644 index 0000000..c913a53 --- /dev/null +++ b/app/assets/css/uno-reset.css @@ -0,0 +1,387 @@ +/* +1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) +2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) +2. [UnoCSS]: allow to override the default border color with css var `--un-default-border-color` +*/ + +*, +::before, +::after { + box-sizing: border-box; /* 1 */ + border-width: 0; /* 2 */ + border-style: solid; /* 2 */ + border-color: var(--un-default-border-color, #e5e7eb); /* 2 */ +} + +::before, +::after { + --un-content: ''; +} + +/* +1. Use a consistent sensible line-height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +3. Use a more readable tab size. +4. Use the user's configured `sans` font-family by default. +5. Use the user's configured `sans` font-feature-settings by default. +6. Use the user's configured `sans` font-variation-settings by default. +7. Disable tap highlights on iOS. +*/ + +html, +:host { + line-height: 1.5; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ + -moz-tab-size: 4; /* 3 */ + tab-size: 4; /* 3 */ + font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; /* 4 */ + font-feature-settings: normal; /* 5 */ + font-variation-settings: normal; /* 6 */ + -webkit-tap-highlight-color: transparent; /* 7 */ +} + +/* +1. Remove the margin in all browsers. +2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. +*/ + +body { + margin: 0; /* 1 */ + line-height: inherit; /* 2 */ +} + +/* +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +3. Ensure horizontal rules are visible by default. +*/ + +hr { + height: 0; /* 1 */ + color: inherit; /* 2 */ + border-top-width: 1px; /* 3 */ +} + +/* +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +abbr:where([title]) { + text-decoration: underline dotted; +} + +/* +Remove the default font size and weight for headings. +*/ + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +/* +Reset links to optimize for opt-in styling instead of opt-out. +*/ + +a { + color: inherit; + text-decoration: inherit; +} + +/* +Add the correct font weight in Edge and Safari. +*/ + +b, +strong { + font-weight: bolder; +} + +/* +1. Use the user's configured `mono` font-family by default. +2. Use the user's configured `mono` font-feature-settings by default. +3. Use the user's configured `mono` font-variation-settings by default. +4. Correct the odd `em` font sizing in all browsers. +*/ + +code, +kbd, +samp, +pre { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; /* 1 */ + font-feature-settings: normal; /* 2 */ + font-variation-settings: normal; /* 3 */ + font-size: 1em; /* 4 */ +} + +/* +Add the correct font size in all browsers. +*/ + +small { + font-size: 80%; +} + +/* +Prevent `sub` and `sup` elements from affecting the line height in all browsers. +*/ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +3. Remove gaps between table borders by default. +*/ + +table { + text-indent: 0; /* 1 */ + border-color: inherit; /* 2 */ + border-collapse: collapse; /* 3 */ +} + +/* +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +3. Remove default padding in all browsers. +*/ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; /* 1 */ + font-feature-settings: inherit; /* 1 */ + font-variation-settings: inherit; /* 1 */ + font-size: 100%; /* 1 */ + font-weight: inherit; /* 1 */ + line-height: inherit; /* 1 */ + color: inherit; /* 1 */ + margin: 0; /* 2 */ + padding: 0; /* 3 */ +} + +/* +Remove the inheritance of text transform in Edge and Firefox. +*/ + +button, +select { + text-transform: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Remove default button styles. +*/ + +button, +[type='button'], +[type='reset'], +[type='submit'] { + -webkit-appearance: button; /* 1 */ + background-color: transparent; /* 2 */ + background-image: none; /* 2 */ +} + +/* +Use the modern Firefox focus style for all focusable elements. +*/ + +:-moz-focusring { + outline: auto; +} + +/* +Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) +*/ + +:-moz-ui-invalid { + box-shadow: none; +} + +/* +Add the correct vertical alignment in Chrome and Firefox. +*/ + +progress { + vertical-align: baseline; +} + +/* +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/* +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +[type='search'] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/* +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to `inherit` in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* +Add the correct display in Chrome and Safari. +*/ + +summary { + display: list-item; +} + +/* +Removes the default spacing for appropriate elements. +*/ + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +fieldset { + margin: 0; + padding: 0; +} + +legend { + padding: 0; +} + +ol, +ul, +menu { + list-style: none; + margin: 0; + padding: 0; +} + +dialog { + padding: 0; +} + +/* +Prevent resizing textareas horizontally by default. +*/ + +textarea { + resize: vertical; +} + +/* +1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) +2. Set the default placeholder color to the user's configured gray 400 color. +*/ + +input::placeholder, +textarea::placeholder { + opacity: 1; /* 1 */ + color: #9ca3af; /* 2 */ +} + +/* +Set the default cursor for buttons. +*/ + +button, +[role="button"] { + cursor: pointer; +} + +/* +Make sure disabled buttons don't get the pointer cursor. +*/ + +:disabled { + cursor: default; +} + +/* +1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) +2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) + This can trigger a poorly considered lint error in some tools but is included by design. +*/ + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; /* 1 */ + vertical-align: middle; /* 2 */ +} + +/* +Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) +*/ + +img, +video { + max-width: 100%; + height: auto; +} + +/* +Make elements with the HTML hidden attribute stay hidden by default. +*/ + +[hidden]:where(:not([hidden="until-found"])) { + display: none; +} diff --git a/app/components/advertisement/index.vue b/app/components/advertisement/index.vue new file mode 100644 index 0000000..d19a0a8 --- /dev/null +++ b/app/components/advertisement/index.vue @@ -0,0 +1,400 @@ + + + + + diff --git a/app/components/advertisement/modes/float.vue b/app/components/advertisement/modes/float.vue new file mode 100644 index 0000000..93264b5 --- /dev/null +++ b/app/components/advertisement/modes/float.vue @@ -0,0 +1,137 @@ + + + + + diff --git a/app/components/advertisement/modes/index.ts b/app/components/advertisement/modes/index.ts new file mode 100644 index 0000000..aff7ce9 --- /dev/null +++ b/app/components/advertisement/modes/index.ts @@ -0,0 +1,26 @@ +import type { Component } from 'vue' + +// 内置模式 +import SimpleMode from './simple.vue' +import FloatMode from './float.vue' + +// 动态加载自定义模式 +const customModes: Record = { + simple: SimpleMode, + float: FloatMode +} + +// 注册自定义模式 +export const registerMode = (name: string, component: Component) => { + customModes[name] = component +} + +// 获取模式组件 +export const getModeComponent = (modeName: string): Component | undefined => { + return customModes[modeName] +} + +// 获取所有可用模式 +export const getAvailableModes = (): string[] => { + return Object.keys(customModes) +} diff --git a/app/components/advertisement/modes/simple.vue b/app/components/advertisement/modes/simple.vue new file mode 100644 index 0000000..774704c --- /dev/null +++ b/app/components/advertisement/modes/simple.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/app/components/baidu-analytics/index.vue b/app/components/baidu-analytics/index.vue new file mode 100644 index 0000000..a6ec208 --- /dev/null +++ b/app/components/baidu-analytics/index.vue @@ -0,0 +1,77 @@ + + + + + diff --git a/app/components/icon.vue b/app/components/icon.vue deleted file mode 100644 index 198ffb7..0000000 --- a/app/components/icon.vue +++ /dev/null @@ -1,56 +0,0 @@ - \ No newline at end of file diff --git a/app/components/icon/icon-selector.vue b/app/components/icon/icon-selector.vue new file mode 100644 index 0000000..8dc4395 --- /dev/null +++ b/app/components/icon/icon-selector.vue @@ -0,0 +1,299 @@ + + + + + diff --git a/app/components/icon/icon-usage.md b/app/components/icon/icon-usage.md new file mode 100644 index 0000000..3d2d80c --- /dev/null +++ b/app/components/icon/icon-usage.md @@ -0,0 +1,141 @@ +# 图标组件使用说明 + +## 功能特性 + +- 支持多种图标库:Ant Design、Element Plus、Carbon、IC、Lucide、Ri 等 +- 支持自定义图标 +- 支持图标选择器组件 +- 支持离线模式 + +## 安装依赖 + +```bash +pnpm add @iconify/vue @iconify/json +``` + +## 使用方式 + +### 1. 基础图标组件 (Iconify) + +```vue + + + +``` + +### 2. 自定义尺寸和颜色 + +```vue + +``` + +### 3. 图标选择器 (IconSelector) + +```vue + + + +``` + +### 4. 监听图标选择事件 + +```vue + + + +``` + +## 支持的图标库 + +| 图标库 | 前缀 | 示例 | +|--------|------|------| +| Ant Design | `ant-design` | `ant-design:home-outlined` | +| Element Plus | `ep` | `ep:home-filled` | +| Carbon | `carbon` | `carbon:home` | +| IC | `ic` | `ic:baseline-home` | +| Lucide | `lucide` | `lucide:home` | +| Ri | `ri` | `ri:home-line` | + +## 自定义添加图标库 + +### 1. 安装图标库 + +```bash +pnpm add @iconify-json/your-icon-library +``` + +### 2. 在 load-icon.ts 中添加 + +```typescript +import YourIcons from '@iconify/json/json/your-icon-library.json' +addCollection(YourIcons) +``` + +### 3. 在 icon-selector.vue 中添加 + +```typescript +const iconifyPrefixs = ['ant-design', 'carbon', 'ic', 'lucide', 'ep', 'ri', 'your-icon-library'] +``` + +## 配置说明 + +### 离线模式 + +在 `.env` 文件中配置: + +```env +VITE_APP_ICON_OFFLINE=true +VITE_APP_ICONIFY_PREFIX=ant-design,carbon,ic,lucide,ep,ri +``` + +### 图标选择器配置 + +| 属性 | 类型 | 默认值 | 说明 | +|------|------|--------|------| +| modelValue | string | '' | v-model 绑定的图标值 | +| text | string | '图标选择器' | 显示文本 | +| width | string | '200px' | 组件宽度 | +| size | string | 'default' | 组件大小:large/default/small | +| disabled | boolean | false | 是否禁用 | + +## 注意事项 + +1. 图标名称格式:`图标库名称:图标名称` +2. 离线模式下需要预先加载所有图标数据 +3. 图标选择器支持搜索功能 +4. 图标选择器支持分页显示 + +## 示例代码 + +完整示例请参考项目中的使用场景。 diff --git a/app/components/icon/iconify.vue b/app/components/icon/iconify.vue new file mode 100644 index 0000000..ad99b50 --- /dev/null +++ b/app/components/icon/iconify.vue @@ -0,0 +1,49 @@ + + + + + diff --git a/app/components/icon/index.ts b/app/components/icon/index.ts new file mode 100644 index 0000000..1682be9 --- /dev/null +++ b/app/components/icon/index.ts @@ -0,0 +1,3 @@ +export { default as Icon } from './iconify.vue' +export { default as IconSelector } from './icon-selector.vue' +export { default } from './iconify.vue' diff --git a/app/components/login-dialog/forget-password.vue b/app/components/login-dialog/forget-password.vue new file mode 100644 index 0000000..6c98a29 --- /dev/null +++ b/app/components/login-dialog/forget-password.vue @@ -0,0 +1,565 @@ + + + + + diff --git a/app/components/login-dialog/index.vue b/app/components/login-dialog/index.vue index 4fe2093..dd2670b 100644 --- a/app/components/login-dialog/index.vue +++ b/app/components/login-dialog/index.vue @@ -11,6 +11,7 @@ > + @@ -18,6 +19,8 @@ import { ref, computed, watch } from "vue"; import login from "./login.vue"; import register from "./register.vue"; +import forgetPassword from "./forget-password.vue"; +import { useMemberStore } from '~/stores/member'; const type = ref("login"); diff --git a/app/components/login-dialog/login.vue b/app/components/login-dialog/login.vue index c4e64a6..48c8a7b 100644 --- a/app/components/login-dialog/login.vue +++ b/app/components/login-dialog/login.vue @@ -5,7 +5,7 @@