chore: 移除 frontend/app 私有代码,仅保留 .gitkeep

该目录为私有移动端项目,不应开源。从 git 跟踪中移除并加入 .gitignore。
This commit is contained in:
zhangtao
2026-08-06 00:42:08 +08:00
parent 5ff25fe28c
commit a4dff8c897
186 changed files with 0 additions and 37525 deletions
@@ -1,202 +0,0 @@
---
name: create-wot-ui-theme
description: '为 wot-ui 生成单文件主题 SCSS,并在用户明确要求接入时追加 App.vue 的 `@use`。当用户要做品牌主题、语义变量落地、单文件主题接入时使用。'
argument-hint: '主题名、主题风格、主色阶、自定义范围、是否需要接入 App.vue'
---
# Create Wot-ui Theme Skill
这个 skill 用于在 `wot-ui` 项目中生成“单文件主题 SCSS”方案。它约束主题文件在 `src/themes/styles` 下完成语义变量定义与挂载,`App.vue` 只负责 `@use` 引入,不扩展成 light/dark 双文件结构,也不改造 `uni_modules/wot-ui/styles/theme/index.scss`
## 适用场景
- 用户明确要求为 `wot-ui` 生成品牌主题、业务主题或定制语义变量主题。
- 用户希望新增 `src/themes/styles/{主题名}.scss`,并把挂载逻辑也收进主题文件。
- 用户要求 `App.vue` 只保留 `@use './themes/styles/{主题名}.scss' as {主题名};` 这一类引入。
- 用户强调不要生成 dark 主题文件、不要拆成双文件主题结构、不要改内置主题入口。
## 不适用场景
- 用户只是想知道 `wd-config-provider` 或普通 CSS 变量怎么用,这种情况优先使用 `wot-ui-v2` skill。
- 用户要做 light/dark 双主题、动态换肤系统、运行时 token 注入,这不属于本 skill 的目标结构。
- 当前仓库没有实际业务 `src/App.vue` 或主题目录时,不要臆造文件;应基于用户目标项目路径执行,或先向用户确认目标工程位置。
## 核心约束
- 只生成一个主题文件:`src/themes/styles/{主题名}.scss`
- 主题文件内必须同时包含:
- 一个 `@mixin {主题名}-theme-vars`
- `page``.wot-theme-{主题名}``.wot-theme-{主题名} .wd-root-portal` 的挂载选择器
- `App.vue` 只负责 `@use` 引入,不重复写挂载选择器。
- 所有语义变量都必须填写固定色值或 `transparent`,不要写成 `var(--wot-xxx)`
- 不生成 `dark.scss`,不修改 `uni_modules/wot-ui/styles/theme/index.scss`
- 如果用户没有明确要求接入 `App.vue`,默认只生成主题文件并给出应追加的 `@use` 语句。
## 执行前确认
生成前优先确认这些信息;缺少时先问清楚再动手:
1. 主题名称,例如 `antd``ocean``forest`
2. 主题风格描述,例如品牌化、偏中性、偏高对比
3. 主色 10 阶是否全部自定义
4. `danger``success``warning` 是否沿用默认值
5. 文本、边框、填充、反馈态是否需要整体调性调整
6. 是否需要同步接入 `src/App.vue`
## 推荐流程
1. 先查看项目里是否已存在 `src/themes/styles/*.scss``src/App.vue`,确认接入位置和现有顺序。
2. 若用户未给足主题信息,先收集主题名、风格和 token 调整范围。
3. 创建 `src/themes/styles/{主题名}.scss`,按本 skill 的完整模板输出全部语义变量。
4. 如用户明确要求接入,再在 `src/App.vue``<style lang="scss">` 中只追加 `@use './themes/styles/{主题名}.scss' as {主题名};`
5. 检查变量是否完整、值是否全为固定色值、挂载选择器是否在主题文件内。
## 主题文件要求
### 结构要求
- 只保留一个 mixin,命名为 `{主题名}-theme-vars`
- 注释风格尽量贴近 `antd.scss` 这种语义分组写法
- 挂载选择器直接写在同一文件末尾
- mixin 调用形式固定为 `@include {主题名}-theme-vars();`
### App.vue 要求
- 只追加 `@use './themes/styles/{主题名}.scss' as {主题名};`
- 不在 `App.vue` 里重复写 `page, .wot-theme-{主题名}` 这些挂载块
- 如果已有其他主题 `@use`,保持原有顺序和已有块不被破坏,只追加当前主题
## 完整变量模板
```scss
@mixin {主题名}-theme-vars {
/* {主题描述} semantic tokens */
/* Primary */
--wot-primary-1: #F5F8FFFF;
--wot-primary-2: #E5EDFFFF;
--wot-primary-3: #B8CFFFFF;
--wot-primary-4: #7CA4FFFF;
--wot-primary-5: #4480FFFF;
--wot-primary-6: #1C64FDFF;
--wot-primary-7: #164ED1FF;
--wot-primary-8: #1341ADFF;
--wot-primary-9: #0F3285FF;
--wot-primary-10: #0A235CFF;
/* Danger */
--wot-danger-main: #F14646FF;
--wot-danger-hover: #FB7C7CFF;
--wot-danger-clicked: #DC2C2CFF;
--wot-danger-disabled: #FFC9C9FF;
--wot-danger-particular: #FFE3E3FF;
--wot-danger-surface: #FFF5F5FF;
/* Success */
--wot-success-main: #12B886FF;
--wot-success-hover: #59CDAAFF;
--wot-success-clicked: #0F956CFF;
--wot-success-disabled: #B8EADBFF;
--wot-success-particular: #E7F8F3FF;
--wot-success-surface: #F3FBF9FF;
/* Warning */
--wot-warning-main: #F57F00FF;
--wot-warning-hover: #FFA94DFF;
--wot-warning-clicked: #D05706FF;
--wot-warning-disabled: #FFD8A8FF;
--wot-warning-particular: #FFE8CCFF;
--wot-warning-surface: #FFF6EBFF;
/* Text */
--wot-text-main: #1D1F29FF;
--wot-text-secondary: #4E5369FF;
--wot-text-auxiliary: #868A9CFF;
--wot-text-disabled: #C9CBD4FF;
--wot-text-placeholder: #A9ACB8FF;
--wot-text-white: #FFFFFFFF;
/* Icon */
--wot-icon-main: #1D1F29FF;
--wot-icon-secondary: #4E5369FF;
--wot-icon-auxiliary: #868A9CFF;
--wot-icon-disabled: #C9CBD4FF;
--wot-icon-placeholder: #A9ACB8FF;
--wot-icon-white: #FFFFFFFF;
/* Border */
--wot-border-extra-strong: #868A9CFF;
--wot-border-strong: #C9CBD4FF;
--wot-border-main: #E5E6EBFF;
--wot-border-light: #F2F3F5FF;
--wot-border-white: #FFFFFFFF;
--wot-border-zero: transparent;
/* Filled */
--wot-filled-extra-strong: #C9CBD4FF;
--wot-filled-strong: #E5E6EBFF;
--wot-filled-content: #F2F3F5FF;
--wot-filled-bottom: #F7F8FAFF;
--wot-filled-oppo: #FFFFFFFF;
--wot-filled-zero: transparent;
/* Divider */
--wot-divider-main: #00000014;
--wot-divider-light: #0000000A;
--wot-divider-strong: #00000026;
--wot-divider-white: #FFFFFFFF;
/* Feedback */
--wot-feedback-hover: #0000000A;
--wot-feedback-active: #00000014;
--wot-feedback-accent: #1C64FD14;
/* Opacity filled */
--wot-opacfilled-tooltip-toast-cover: #000000BF;
--wot-opacfilled-main-cover: #0000008C;
--wot-opacfilled-light-cover: #0000004D;
/* Picker view mask */
--wot-picker-view-mask-start-color: #FFFFFFD9;
--wot-picker-view-mask-end-color: #FFFFFF33;
/* Classify application */
--wot-classifyapplication-yellow-background: #FFFAF1FF;
--wot-classifyapplication-yellow-border: #FDD78CFF;
--wot-classifyapplication-yellow-content: #FAAD14FF;
--wot-classifyapplication-Cyan-background: #F4FBFDFF;
--wot-classifyapplication-Cyan-border: #BDEAF1FF;
--wot-classifyapplication-Cyan-content: #22B8CFFF;
--wot-classifyapplication-Purple-background: #F9F8FFFF;
--wot-classifyapplication-Purple-border: #D0BFFFFF;
--wot-classifyapplication-Purple-content: #8059F3FF;
--wot-classifyapplication-Grape-background: #FBF6FDFF;
--wot-classifyapplication-Grape-border: #EEBEFAFF;
--wot-classifyapplication-Grape-content: #AE3EC9FF;
--wot-classifyapplication-Pink-background: #FFF0F6FF;
--wot-classifyapplication-Pink-border: #FCC2D7FF;
--wot-classifyapplication-Pink-content: #FF357CFF;
}
page,
.wot-theme-{主题名},
.wot-theme-{主题名} .wd-root-portal {
@include {主题名}-theme-vars();
}
```
## 输出检查清单
- [ ] 主题文件位于 `src/themes/styles/{主题名}.scss`
- [ ] 文件包含一个 `@mixin {主题名}-theme-vars`
- [ ] 文件内完成 `page``.wot-theme-{主题名}``.wot-theme-{主题名} .wd-root-portal` 挂载
- [ ] 包含主色、功能色、文字、图标、边框、填充、分割线、反馈、透明填充、Picker View 遮罩、分类色全部变量
- [ ] 所有值都是固定色值或 `transparent`
- [ ] `App.vue` 中若接入,只追加 `@use './themes/styles/{主题名}.scss' as {主题名};`
- [ ] mixin 调用形式为 `@include {主题名}-theme-vars();`
## 回答与实现规则
- 用户未明确要求接入时,不要主动改 `App.vue`
- 用户未明确要求时,不要顺手生成 dark 主题、变量映射层或额外目录结构
- 生成代码时优先保持最小改动,不破坏现有主题文件顺序、选择器范围和项目风格
- 如果发现项目里已有不同主题体系,先说明差异,再确认是否仍按本单文件结构落地
@@ -1,43 +0,0 @@
---
name: starter-cleaner
description: 将 wot-starter v2 模板精简为最小可开发状态,移除文档、演示分包、生成文件和 monorepo 配置,并同步清理相关 Vite 与 package.json 配置。用户要求“清理模板”“移除示例”“生成最小模板”“精简 wot-starter v2”时使用。
---
# Starter Cleaner
使用随技能提供的确定性脚本清理 `wot-starter` v2,保留基础页面、Wot UI、`uni-echarts``echarts` 能力。
## 执行流程
1. 在项目根目录运行 `git status --short`,确认并告知用户未提交改动会被保留,但待清理目录中的改动会随目录一起删除。
2. 如果用户尚未明确要求执行清理,先说明这是破坏性操作并取得确认。
3. 先预览清理计划:
```sh
node .agents/skills/starter-cleaner/scripts/clean.js --dry-run
```
4. 确认目标是 `wot-starter` v2 后执行。脚本会删除旧的 `src/pages.json`,然后受控运行 `pnpm dev:h5`,等新的最小 `src/pages.json` 生成后退出 dev server
```sh
node .agents/skills/starter-cleaner/scripts/clean.js
```
5. 执行 `pnpm install --lockfile-only` 更新锁文件,再运行 `pnpm type-check` 和 `pnpm lint`。如果依赖不可用,至少检查 `git diff --check` 和清理后的 diff,并将未运行的验证明确告知用户。
## 清理范围
脚本执行以下操作,且支持重复运行:
- 删除 `docs/`、`src/subPages/`、`src/subEcharts/`、`src/subAsyncEcharts/`。
- 删除旧的 `src/pages.json`,再通过 `pnpm dev:h5` 触发 `vite-plugin-uni-pages` 重新生成最小页面配置。
- 删除 `pnpm-workspace.yaml`,将项目从文档 workspace 恢复为单包项目。
- 从 `vite.config.ts` 的 `UniHelperPages` 配置中移除上述三个示例分包入口。
- 从 `package.json` 中移除 `docs:*` 与 `lint:docs` 脚本。
- 保留 `uni-echarts`、`echarts` 依赖和相关 Vite 配置,方便业务继续使用图表。
## 安全约束
- 不直接拼接或扩展删除路径;仅使用脚本内的固定清单。
- 不在非 `wot-starter` v2 项目中运行。仅在用户明确确认目标项目后使用 `--force` 跳过项目身份检查。
- 不用 `--force` 绕过未提交改动提示;该参数只用于项目身份检查。
@@ -1,5 +0,0 @@
# eslint-disable yaml/plain-scalar
interface:
display_name: 'Wot Starter Cleaner'
short_description: '将 wot-starter v2 清理为最小可开发模板'
default_prompt: '使用 $starter-cleaner 将当前 wot-starter v2 模板精简至最小状态。'
@@ -1,229 +0,0 @@
import { spawn } from 'node:child_process'
import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
const scriptDirectory = path.dirname(fileURLToPath(import.meta.url))
const dryRun = process.argv.includes('--dry-run')
const force = process.argv.includes('--force')
const pathsToRemove = [
'docs',
'src/subPages',
'src/subEcharts',
'src/subAsyncEcharts',
'src/pages.json',
'pnpm-workspace.yaml',
]
const subPackagePaths = [
'src/subPages',
'src/subEcharts',
'src/subAsyncEcharts',
]
function findProjectRoot(directory) {
const packageJsonPath = path.join(directory, 'package.json')
if (fs.existsSync(packageJsonPath)) {
return directory
}
const parentDirectory = path.dirname(directory)
if (parentDirectory === directory) {
throw new Error('找不到项目根目录(未发现 package.json')
}
return findProjectRoot(parentDirectory)
}
function readPackageJson(packageJsonPath) {
return JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
}
function assertWotStarterV2(packageJson) {
const isWotStarter = packageJson.name === 'wot-starter'
const isV2 = String(packageJson.version ?? '').startsWith('2.')
if (!force && (!isWotStarter || !isV2)) {
throw new Error(
`目标项目不是 wot-starter v2name=${packageJson.name ?? 'unknown'}, version=${packageJson.version ?? 'unknown'})。确认目标无误后可使用 --force。`,
)
}
}
function removePath(projectRoot, relativePath) {
const targetPath = path.join(projectRoot, relativePath)
if (!fs.existsSync(targetPath)) {
console.log(`跳过(不存在):${relativePath}`)
return false
}
console.log(`${dryRun ? '计划删除' : '删除'}${relativePath}`)
if (!dryRun) {
fs.rmSync(targetPath, { recursive: true, force: true })
}
return true
}
function updateTextFile(filePath, transform) {
if (!fs.existsSync(filePath)) {
console.log(`跳过(不存在):${path.basename(filePath)}`)
return false
}
const originalContent = fs.readFileSync(filePath, 'utf8')
const nextContent = transform(originalContent)
if (nextContent === originalContent) {
console.log(`跳过(无需修改):${path.basename(filePath)}`)
return false
}
console.log(`${dryRun ? '计划修改' : '修改'}${path.basename(filePath)}`)
if (!dryRun) {
fs.writeFileSync(filePath, nextContent, 'utf8')
}
return true
}
function cleanViteConfig(projectRoot) {
const viteConfigPath = path.join(projectRoot, 'vite.config.ts')
updateTextFile(viteConfigPath, (content) => {
const lines = content.split('\n').filter((line) => {
return !subPackagePaths.some((subPackagePath) => {
return line.includes(`'${subPackagePath}'`) || line.includes(`"${subPackagePath}"`)
})
})
return lines.join('\n').replace(/subPackages:\s*\[\s*\],/g, 'subPackages: [],')
})
}
function cleanPackageJson(projectRoot, packageJson) {
const scriptNames = Object.keys(packageJson.scripts ?? {})
const scriptsToRemove = scriptNames.filter(name => name.startsWith('docs:') || name === 'lint:docs')
if (scriptsToRemove.length === 0) {
console.log('跳过(无需修改):package.json')
return
}
for (const scriptName of scriptsToRemove) {
delete packageJson.scripts[scriptName]
}
console.log(`${dryRun ? '计划修改' : '修改'}package.json(移除 ${scriptsToRemove.join(', ')}`)
if (!dryRun) {
fs.writeFileSync(
path.join(projectRoot, 'package.json'),
`${JSON.stringify(packageJson, null, 2)}\n`,
'utf8',
)
}
}
function sleep(milliseconds) {
return new Promise(resolve => setTimeout(resolve, milliseconds))
}
function isGeneratedPagesReady(projectRoot) {
const pagesJsonPath = path.join(projectRoot, 'src/pages.json')
if (!fs.existsSync(pagesJsonPath)) {
return false
}
const content = fs.readFileSync(pagesJsonPath, 'utf8')
return content.includes('"pages"') && !subPackagePaths.some(subPackagePath => content.includes(subPackagePath))
}
function stopProcess(childProcess) {
if (childProcess.exitCode !== null || childProcess.signalCode !== null) {
return
}
if (process.platform === 'win32') {
childProcess.kill('SIGTERM')
return
}
try {
process.kill(-childProcess.pid, 'SIGTERM')
}
catch {
childProcess.kill('SIGTERM')
}
}
async function regeneratePagesJson(projectRoot) {
console.log('生成:src/pages.jsonpnpm dev:h5')
const childProcess = spawn('pnpm', ['dev:h5'], {
cwd: projectRoot,
detached: process.platform !== 'win32',
env: {
...process.env,
BROWSER: 'none',
FORCE_COLOR: '0',
},
stdio: ['ignore', 'pipe', 'pipe'],
})
let output = ''
childProcess.stdout.on('data', (chunk) => {
output += chunk.toString()
})
childProcess.stderr.on('data', (chunk) => {
output += chunk.toString()
})
const timeoutAt = Date.now() + 30000
try {
while (Date.now() < timeoutAt) {
if (isGeneratedPagesReady(projectRoot)) {
console.log('已生成:src/pages.json')
return
}
if (childProcess.exitCode !== null) {
break
}
await sleep(250)
}
throw new Error(`未能通过 pnpm dev:h5 生成 src/pages.json。\n${output.trim()}`)
}
finally {
stopProcess(childProcess)
}
}
async function main() {
const projectRoot = findProjectRoot(scriptDirectory)
const packageJsonPath = path.join(projectRoot, 'package.json')
const packageJson = readPackageJson(packageJsonPath)
assertWotStarterV2(packageJson)
console.log(`${dryRun ? '预览清理' : '开始清理'}${projectRoot}`)
for (const relativePath of pathsToRemove) {
removePath(projectRoot, relativePath)
}
cleanViteConfig(projectRoot)
cleanPackageJson(projectRoot, packageJson)
if (!dryRun) {
await regeneratePagesJson(projectRoot)
}
console.log(dryRun ? '预览完成,未修改任何文件。' : '清理完成。请运行 pnpm install --lockfile-only 更新锁文件。')
}
main().catch((error) => {
console.error(error.message)
process.exitCode = 1
})
@@ -1,73 +0,0 @@
---
name: wot-ui-cli
description: '回答、使用、调试 @wot-ui/cli 时使用。关键词:wot、@wot-ui/cli、CLI、MCP、doctor、usage、lint、list、info、doc、demo、token、changelog、extract、wot mcp。适用于命令查询、参数说明、MCP 接入、本地调试、数据提取与 open-wot 仓库维护。'
argument-hint: '命令名、参数、MCP 场景、调试问题或数据提取需求'
---
# Wot UI CLI Skill
这个 skill 用于让 Agent 在处理 `@wot-ui/cli` 本身相关的问题时,优先基于本仓库 README 与实际命令能力回答,而不是把它误当成纯组件库文档。
## 适用场景
- 用户询问 `wot` 命令怎么用。
- 用户需要区分 `list``info``doc``demo``token``changelog``doctor``usage``lint``mcp``extract` 的用途。
- 用户要接入 MCP Server,或需要 `wot mcp` 的配置与调试方法。
- 用户要在本仓库中调试 `@wot-ui/cli`、验证构建产物、重新提取数据。
- 用户的问题本质上是 open-wot 仓库维护问题,而不是单纯的 wot-ui 组件使用问题。
## 适用范围
- 关注对象是 `@wot-ui/cli` 这个工具包,以及仓库 `open-wot` 的开发维护流程。
- 重点覆盖命令能力、通用参数、MCP、离线数据来源、提取流程、本地调试和发布包边界。
- 如果任务是生成 `wd-*` 页面代码、解释组件 props 或给出主题定制方案,应优先使用 `wot-ui-v2` skill。
## 推荐流程
1. 先确认用户是在问 CLI 工具本身,还是在借 CLI 查询组件知识。
2. 如果是命令使用问题,优先按命令类别回答:组件知识、项目分析、MCP、数据提取、仓库开发。
3. 如果是仓库维护问题,优先给出本仓库里的实际调试命令,而不是泛泛而谈。
4. 如果涉及组件内容本身,可引导或切换到 `wot-ui-v2` skill。
## 命令分组
### 组件知识查询
- `wot list`
- `wot info <Component>`
- `wot doc <Component>`
- `wot demo <Component> [name]`
- `wot token [Component]`
- `wot changelog [version] [component]`
### 项目分析
- `wot doctor [dir]`
- `wot usage [dir]`
- `wot lint [dir]`
### MCP
- `wot mcp`
### 数据提取与仓库维护
- `pnpm extract:cli --wot-dir ../wot-ui --output data/v2.json`
- `pnpm extract:clone`
- `pnpm exec tsx src/index.ts <command>`
- `pnpm build`
- `node dist/index.mjs <command>`
## 工作规则
- 包名是 `@wot-ui/cli`,实际可执行命令是 `wot`
- 回答命令问题时,优先用仓库 README 中已承诺的行为和参数,不臆造未声明子命令。
- 回答本地调试问题时,优先给源码入口:`pnpm exec tsx src/index.ts ...`
- 回答构建产物问题时,再给 `node dist/index.mjs ...`
- 回答 MCP 问题时,要说明 `wot mcp` 走 stdio,终端无交互输出通常是正常现象。
- 回答提取逻辑问题时,要说明数据主要来自上游 `wot-ui/wot-ui` 的 markdown 与 SCSS 源码。
- 当用户问的是组件知识但入口是 CLI,也要保留“这是通过 CLI 查询组件知识”这一层语义。
## 参考资料
- [Wot UI CLI 概览](./references/overview.md)
@@ -1,195 +0,0 @@
# Wot UI CLI Overview
本文件根据本仓库 README 整理,目标是让 Agent 快速理解 `@wot-ui/cli` 的能力边界、命令分组、MCP 接入方式、开发调试路径与数据来源。
## Package Identity
- 包名:`@wot-ui/cli`
- 仓库:open-wot
- 可执行命令:`wot`
- 核心定位:wot-ui 的 AI 工具链仓库,提供 CLI、MCP Server、离线组件知识库与数据提取脚本。
## Repository Positioning
- 面向 wot-ui v2 的组件知识查询工具。
- 面向本地项目的组件使用分析与 lint 工具。
- 面向 AI 客户端的 MCP stdio 服务。
- 面向仓库维护者的数据提取与同步工作流。
## Core Capabilities
### Component Knowledge
- `list`:列出可用组件。
- `info <Component>`:查看 props、events、slots、CSS 变量。
- `doc <Component>`:输出组件 markdown 文档。
- `demo <Component> [name]`:查看 demo 列表或指定 demo 源码。
- `token [Component]`:查看组件 CSS 变量与默认值。
- `changelog [version] [component]`:查看版本更新记录。
### Project Analysis
- `doctor [dir]`:检查项目依赖、运行环境与基础集成情况。
- `usage [dir]`:统计 `.vue` 文件中的 `wd-*` 使用情况。
- `lint [dir]`:检查未知组件、空按钮等规则。
### MCP Server
- `mcp`:启动 MCP stdio server。
## Typical User Flows
### Query Component Knowledge Through CLI
常用顺序:
1. `wot list`
2. `wot info Button`
3. `wot demo Button basic`
4. `wot doc Button`
5. `wot token Button`
### Analyze A Local Project
常用顺序:
1. `wot doctor ./my-project`
2. `wot usage ./my-project`
3. `wot lint ./my-project`
### Run MCP In A Client
典型配置:
```json
{
"mcpServers": {
"wot-ui": {
"command": "wot",
"args": ["mcp"]
}
}
}
```
当前 README 明确列出的 MCP tools 有:
- `wot_list`
- `wot_info`
- `wot_doc`
- `wot_demo`
- `wot_token`
- `wot_changelog`
- `wot_lint`
## Common Flags
多数查询命令支持:
- `--format text`
- `--format json`
- `--version v2`
## Install And Run
### Global Install
```bash
npm install -g @wot-ui/cli
```
安装后直接用 `wot`
### Source Mode In This Repo
```bash
pnpm exec tsx src/index.ts list
pnpm exec tsx src/index.ts info Button
pnpm exec tsx src/index.ts mcp
```
适合本地调试源码,不依赖全局安装。
### Built Artifact Mode
```bash
pnpm build
node dist/index.mjs list
```
适合验证构建产物行为。
## MCP Operational Notes
- `wot mcp` 走 stdio。
- 终端里没有交互输出通常是正常现象。
- 若要调试 tool 与 prompt 调用过程,建议配合 MCP Inspector 或编辑器内置 MCP 客户端。
## Data Source And Extraction
当前版本聚焦 `wot-ui v2`
离线数据主要提取自上游 `wot-ui/wot-ui` 的:
- `docs/component/*.md`
- `docs/guide/changelog.md`
- `src/uni_modules/wot-ui/components/*/index.scss`
重新生成数据有两种方式:
### Use A Local Wot UI Repo
```bash
pnpm extract:cli --wot-dir ../wot-ui --output data/v2.json
```
### Clone Latest Upstream And Extract
```bash
pnpm extract:clone
```
## Repo Layout
- `src`CLI、MCP 与项目分析源码。
- `data`:离线组件元数据。
- `scripts`:提取脚本。
- `skills`:面向 Agent 的技能说明。
- `test`:根包测试。
## Local Development Commands
### Environment
- Node.js `>= 20`
- pnpm `10.x`
### Install
```bash
pnpm install
```
### Common Validation
```bash
pnpm lint
pnpm test:all
pnpm build:all
pnpm typecheck:all
```
### Package-Level Commands
```bash
pnpm build
pnpm test
pnpm typecheck
```
## Agent Guidance
- 如果用户问的是命令怎么用,按“命令组 + 示例命令 + 输出用途”来回答。
- 如果用户问的是仓库维护或调试,优先给本仓库中的真实命令和目录。
- 如果用户问的是组件本身怎么写页面,不要停留在 CLI 层,应切换到 `wot-ui-v2` skill。
- 不要把 `wot` 命令和 `@wot-ui/ui` 组件库 API 混为一谈。
@@ -1,153 +0,0 @@
---
name: "wot-ui-unocss-preset-guide"
description: "指导安装、配置并使用 @wot-ui/unocss-preset。Invoke when 用户询问该预设的接入、配置、使用示例或常见问题排查。"
---
# Wot UnoCSS Preset 使用指南
## 适用场景
当用户询问以下内容时,优先使用本 Skill:
- 如何安装 `@wot-ui/unocss-preset`
- 如何在 `unocss.config.ts` 配置 `presetWot`
- `prefix``preflight``baseTokens` 怎么用
- 为什么类名不生效、自动补全不出现、CI 与本地结果不一致
目标是给出可直接复制的最佳实践,帮助用户快速完成接入并稳定运行。
## 最小安装步骤
```bash
pnpm add -D unocss
pnpm add @wot-ui/unocss-preset
```
## 推荐配置(完整示例)
```ts
import { presetWot } from '@wot-ui/unocss-preset'
import { defineConfig } from 'unocss'
export default defineConfig({
presets: [
presetWot({
prefix: 'wot',
preflight: true,
baseTokens: false,
}),
],
})
```
## 配置项说明
- `prefix`:工具类前缀,默认 `wot`。示例:`wot-text-primary``wot-m-main`
- `preflight`:是否注入 wot-ui CSS 变量,默认 `true`
- `baseTokens`:是否开放基础色板和原始 token 类名,默认 `false`
## 常用类名示例
- 颜色:`wot-text-primary``wot-bg-danger-surface``wot-border-border-main`
- 间距:`wot-m-main``wot-gap-tight``wot-gap-x-loose`
- 内边距:`wot-p-main``wot-px-tight``wot-pb-loose`
- 圆角:`wot-rounded-md``wot-rounded-full`
- 字重:`wot-font-medium``wot-font-semibold`
- 排版:`wot-text-body-main``wot-text-title-large`
- 透明度:`wot-opacity-disabled`
- 描边:`wot-border-stroke-main`
## 上手示例(可复制)
### 1) 一个“卡片”示例(uni-app / Vue
```vue
<template>
<view class="wot-bg-filled-oppo wot-rounded-2xl wot-p-super-loose wot-border-border-main wot-border-stroke-main">
<text class="wot-text-title-large wot-text-text-main wot-font-semibold">
Wot UnoCSS Preset
</text>
<view class="wot-mt-tight">
<text class="wot-text-body-main wot-text-text-secondary">
wot-text-body-main + wot-text-text-secondary
</text>
</view>
<view class="wot-mt-loose wot-bg-primary wot-rounded-full wot-px-main wot-py-extra-tight">
<text class="wot-text-label-large wot-text-text-white wot-font-semibold">
wot-bg-primary
</text>
</view>
</view>
</template>
```
### 2) 间距/布局示例(常见组合)
- 外边距:`wot-mt-tight``wot-mt-main``wot-mt-super-loose`
- 内边距:`wot-p-loose``wot-px-main``wot-py-extra-tight`
- 横向/纵向组合:`wot-mx-main``wot-my-loose`
- gap`wot-gap-tight``wot-gap-x-main``wot-gap-y-loose`
### 3) 语义色示例(背景/文字/边框)
- 背景:`wot-bg-primary``wot-bg-success-surface``wot-bg-warning-surface``wot-bg-danger-surface`
- 文字:`wot-text-text-main``wot-text-text-secondary``wot-text-primary``wot-text-success-main`
- 边框:`wot-border-border-main``wot-border-success-main``wot-border-warning-main``wot-border-danger-main`
### 4) 暗黑模式示例
预设会输出暗色变量选择器 `.wot-theme-dark ...`,你只需要在根节点加 class
```vue
<template>
<view :class="dark ? 'wot-theme-dark' : ''">
<view class="wot-bg-filled-oppo wot-p-main wot-rounded-lg">
<text class="wot-text-text-main">当前主题{{ dark ? 'Dark' : 'Light' }}</text>
</view>
</view>
</template>
```
### 5) 打开 baseTokens(可选)
当你希望使用基础色板/原始 token 时启用:
```ts
presetWot({
baseTokens: true,
})
```
启用后会额外提供类似 `wot-base-black` 这类 token(用于 `theme.colors` 与颜色规则匹配)。
## 轻量提醒
- 历史项目若仍使用 `w-` 前缀,可通过 `prefix: 'w'` 兼容。
- 若 playground/子包构建异常,优先检查是否:
- 使用包名导入(如 `@wot-ui/unocss-preset`
- 在依赖主包产物的场景下先执行主包构建
- 若需要同步上游 wot-ui 变量,可使用:
- `pnpm generate:css-vars:clone`
## 常见问题排查
1. 类名不生效
- 确认项目已启用 UnoCSS 且 `presetWot()` 已加入 `presets`
- 确认类名前缀与配置一致(默认 `wot-`)。
2. VS Code 没有自动补全
- 确认安装 UnoCSS 官方扩展。
- 确认编辑器能定位到项目 `unocss.config.ts`
- 确认类名写在扩展可扫描的文件类型内。
3. CI 能复现但本地不复现(或反过来)
- 对齐 Node/pnpm 版本与锁文件。
- 对齐执行顺序(例如先构建主包,再构建依赖主包产物的 playground)。
## 响应风格要求
- 输出简洁、可执行,优先给“可复制配置 + 最短排查路径”。
- 当用户目标明确时,先给结论和配置,再补充原因说明。
- 不擅自引入本项目未使用的额外工具链或复杂抽象。
@@ -1,46 +0,0 @@
---
name: wot-ui-v2
description: '回答、生成、重构、排查 wot-ui v2 相关代码时使用。关键词:wot-ui、uni-app、Vue3、wd-、ConfigProvider、useToast、useDialog、Form、Popup、theme、llms-full。适用于组件选型、API 查询、示例页面生成、主题定制、常见坑排查。'
argument-hint: '组件名、页面场景、问题描述或主题定制需求'
---
# Wot UI V2 Skill
这个 skill 用于让 Agent 在处理 wot-ui v2 相关任务时,优先采用组件库既有能力、遵守 uni-app 场景限制,并结合本仓库提供的 `wot` CLI 查询离线知识。
## 适用场景
- 用户询问某个 `wd-*` 组件的基础用法、属性、事件、插槽或样式变量。
- 需要生成或重构 `uni-app + Vue 3 + TypeScript` 的 wot-ui 页面或组件代码。
- 需要在 `ConfigProvider`、主题变量、暗黑模式、国际化、反馈类 hooks、表单等场景下给出正确做法。
- 需要排查文档中常见的 `Toast``Dialog``Popup``Tabs``Slider`、样式覆盖问题。
- 用户只是泛化地提到主题定制,但还没有明确要求按“单文件主题 SCSS + App.vue 只 `@use`”的结构生成时。
## 推荐流程
1. 先用本仓库 CLI 或 MCP 工具查组件知识。
2. 再根据项目实际安装方式决定导入路径与集成方式。
3. 优先复用现成的 `wd-*` 组件、hooks、主题变量与组合模式,不要退化成原生标签堆砌。
4. 如果问题涉及约束或坑位,再查阅 [参考知识](./references/overview.md)。
5. 如果用户明确要求生成 `src/themes/styles/{主题名}.scss` 单文件主题,并把挂载逻辑收进主题文件,优先切换到 `create-wot-ui-theme` skill。
## 查询顺序
1. `wot list` 找组件名。
2. `wot info <Component>` 看 props、events、slots、CSS 变量。
3. `wot demo <Component>` 看 demo 名称或具体 demo 代码。
4. `wot doc <Component>` 看完整 markdown 文档。
5. `wot token <Component>` 看主题变量。
## 工作规则
- 默认把 wot-ui 视为 `uni-app + Vue 3 + TypeScript` 组件库。
- 写页面时优先输出 `script setup` 风格。
- 反馈类能力如 `useToast``useDialog``useNotify``useImagePreview``useVideoPreview`,除了 hook 调用外,通常还需要页面内显式声明对应组件实例。
- 文档里经常出现 `@/uni_modules/wot-ui` 导入路径;如果用户项目采用 npm 安装,应切换成 `@wot-ui/ui`
- 主题定制优先走 `ConfigProvider` 和 CSS 变量,不优先建议深度覆盖内部类名。
- 生成代码时尽量沿用组件库文档里的命名和交互模式,例如 `v-model:visible``before-confirm``confirm``change``custom-class``custom-style`
## 参考资料
- [Wot UI V2 概览](./references/overview.md)
@@ -1,85 +0,0 @@
# Wot UI V2 Overview
本文件根据 wot-ui v2 的 `llms-full.txt` 与本仓库现有 CLI 工作流提炼,目标是帮助 Agent 快速掌握适合生成代码与回答问题的高价值知识,而不是逐字复制官方文档。
## Product Positioning
- Wot UI v2 是面向 `uni-app``Vue 3 + TypeScript` 组件库。
- 覆盖微信小程序、支付宝小程序、钉钉小程序、H5、APP 等平台。
- 组件命名统一为 `wd-*`
- 组件库强调 AI 友好、主题定制、暗黑模式、国际化与跨端一致性。
## Installation And Integration
- npm 安装:`pnpm add @wot-ui/ui`
- 使用前需要安装 `sass`
- `uni_modules` 安装模式天然支持 easycom 自动引入。
- npm 安装模式通常需要配置 vite resolver 或 easycom。
- CLI 项目在 npm 模式下可在 `tsconfig.json` 中加入 `@wot-ui/ui/global` 以增强全局组件类型提示。
## Import Rules
- npm 安装项目:组合式函数、类型和工具优先从 `@wot-ui/ui` 导入。
- `uni_modules` 安装项目:文档中的 `@/uni_modules/wot-ui` 路径通常可直接使用。
- 官方文档示例很多基于 `uni_modules` 路径,回答时要按用户项目实际安装方式转换。
## High Value Conventions
- 反馈类组件不能依赖全局挂载。页面内通常需要显式写出 `wd-toast``wd-dialog``wd-notify``wd-image-preview``wd-video-preview` 等实例。
- `useToast``useDialog``useNotify``useQueue` 等 hooks 基于 `provide/inject`,应在 `setup` 中调用。
- 页面内如果存在多个 `wd-dialog``wd-toast`,需要通过 `selector` 区分,否则可能出现实例冲突或重复弹出。
- 自定义组件中如果要覆盖 wot-ui 内部样式,小程序环境通常需要把组件配置为 `styleIsolation: 'shared'`
-`Popup``ActionSheet``DropDownItem` 等延迟渲染弹层里使用 `Slider``Tabs` 等依赖尺寸计算的组件时,打开后应调用实例方法重新初始化,例如 `initSlider()``updateLineStyle()`
## Theme And Styling
- 主题定制优先走 CSS 变量。
- Design Token 分三层:基础变量、语义变量、组件变量。
- 局部或全局主题可通过 `wd-config-provider``theme``theme-vars` 控制。
- 深色模式通过 `wd-config-provider theme="dark"` 开启。
- 更推荐覆盖语义变量或组件变量,不推荐优先依赖深层 class 选择器覆盖。
## Common UI Patterns
- 表单场景优先组合 `wd-form``wd-form-item``wd-input``wd-textarea``wd-picker``wd-calendar``wd-select-picker`
- 弹层类场景优先使用 `wd-popup``wd-dialog``wd-action-sheet``wd-tooltip``wd-popover`
- 反馈类场景优先使用 `useToast``useDialog``useNotify`,不要直接手写临时弹层。
- 列表和展示类场景优先考虑 `wd-cell``wd-card``wd-tag``wd-badge``wd-empty``wd-loadmore``wd-skeleton`
- 导航与布局类场景优先考虑 `wd-navbar``wd-tabs``wd-tabbar``wd-sidebar``wd-row``wd-col``wd-gap`
## Interaction Patterns
- 大量组件采用 `v-model``v-model:visible` 控制状态。
- 表单和选择类组件普遍提供 `confirm``change``close` 等事件。
- 反馈组件常通过 hook 返回方法对象,例如 `toast.success()``dialog.confirm()`
- `Popover``Tooltip``SwipeAction` 等场景常与 `useQueue().closeOutside()` 配合,实现点击外部关闭。
## Component Selection Hints
- 需要主操作按钮时用 `wd-button`,不要先写原生 `button`
- 需要列表入口、设置页、表单容器时优先用 `wd-cell``wd-cell-group`
- 需要轻量提示时优先用 `useToast`;需要确认交互时优先用 `useDialog`
- 需要单选或多选弹层时优先用 `wd-select-picker``wd-picker``wd-cascader`
- 需要统一主题或暗黑模式时优先用 `wd-config-provider`
## Common Pitfalls
- `Toast``Dialog` 等函数式调用没有效果,先检查页面里是否声明了对应组件实例。
- 同一个页面里多个无 `selector` 的反馈组件可能相互干扰。
- npm 模式使用国际化时,开发态可能需要在 Vite 的 `optimizeDeps.exclude` 中排除 `@wot-ui/ui`
- 文档里的导入路径和项目实际安装方式不一致时,回答要主动修正。
- 在弹层中直接渲染依赖尺寸测量的组件时,初始化时机往往比 API 本身更关键。
## AI Response Heuristics
- 回答基础用法时,优先给最小可运行模板,再补充常用 props。
- 生成页面时,优先给完整的 `template + script setup + style` 结构。
- 如果项目已使用 wot-ui,不要建议换用其他 UI 库。
- 如果 wot-ui 已有现成组件,就不要用原生结构重复造轮子。
- 如果用户只问某个组件,优先给该组件最常见 3 到 5 个用法,不要把整份文档全部展开。
## Repo-Specific Workflow
- 在本仓库中,优先使用 `wot list``wot info``wot doc``wot demo``wot token` 获取组件知识。
- 当仓库数据与线上文档不一致时,以用户目标为准,并明确指出仓库离线数据可能需要重新提取。
- 若要补数据或修提取逻辑,关注 `scripts/extract.ts``data/v2.json``src/data/*` 与相应命令实现。
-9
View File
@@ -1,9 +0,0 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
-26
View File
@@ -1,26 +0,0 @@
# 开发环境变量
# 在开发模式下使用 (npm run dev)
# API 基础 URL - 开发环境
# VITE_API_BASE_URL=http://localhost:8001
VITE_API_BASE_URL=https://service.fastapiadmin.com
# 环境名称
VITE_ENV_NAME=development
# 项目名称
VITE_APP_TITLE=fastapiadmin
# 代理前缀
VITE_APP_BASE_API=/api/v1
# 应用端口
VITE_APP_PORT=5180
# 超时时间
VITE_TIMEOUT=10000
# WebSocket 服务器的 URL,不配置默认关闭 WebSocket
# VITE_APP_WS_ENDPOINT= ws://localhost:5180/ws
VITE_APP_WS_ENDPOINT = wss://service.fastapiadmin.com
-23
View File
@@ -1,23 +0,0 @@
# 生产环境变量
# 在生产构建时使用 (npm run build)
# API 基础 URL - 生产环境
VITE_API_BASE_URL=https://service.fastapiadmin.com
# 环境名称
VITE_ENV_NAME=production
# 项目名称
VITE_APP_TITLE=fastapiadmin
# 代理前缀
VITE_APP_BASE_API=/api/v1
# 应用端口
VITE_APP_PORT=5180
# 超时时间
VITE_TIMEOUT=10000
# WebSocket 服务器的 URL,不配置默认关闭 WebSocket
VITE_APP_WS_ENDPOINT= ws://localhost:5180/ws
-8
View File
@@ -1,8 +0,0 @@
# 测试/预发布环境变量
# 在测试环境构建时使用
# API 基础 URL - 测试环境
VITE_API_BASE_URL=https://staging-api.yourapp.com
# 环境名称
VITE_ENV_NAME=staging
-70
View File
@@ -1,70 +0,0 @@
{
"disableEmoji": false,
"list": ["test", "feat", "fix", "chore", "docs", "refactor", "style", "ci", "perf", "release", "revert", "build"],
"maxMessageLength": 64,
"minMessageLength": 3,
"questions": ["type", "scope", "subject", "body", "breaking", "issues", "lerna"],
"scopes": [],
"types": {
"chore": {
"description": "Chore | 构建/工程依赖/工具",
"emoji": "🚀",
"value": "chore"
},
"ci": {
"description": "Continuous Integration | CI 配置",
"emoji": "👷",
"value": "ci"
},
"docs": {
"description": "Documentation | 文档",
"emoji": "✏️ ",
"value": "docs"
},
"feat": {
"description": "Features | 新功能",
"emoji": "✨",
"value": "feat"
},
"fix": {
"description": "Bug Fixes | Bug 修复",
"emoji": "🐛",
"value": "fix"
},
"perf": {
"description": "Performance Improvements | 性能优化",
"emoji": "⚡",
"value": "perf"
},
"refactor": {
"description": "Code Refactoring | 代码重构",
"emoji": "♻️ ",
"value": "refactor"
},
"release": {
"description": "Create a release commit | 发版提交",
"emoji": "🏹",
"value": "release"
},
"style": {
"description": "Styles | 风格",
"emoji": "💄",
"value": "style"
},
"revert": {
"description": "Revert | 回退",
"emoji": "⏪",
"value": "revert"
},
"build": {
"description": "Build System | 打包构建",
"emoji": "📦",
"value": "build"
},
"test": {
"description": "Tests | 测试",
"emoji": "✅",
"value": "test"
}
}
}
-1
View File
@@ -1 +0,0 @@
custom: ['https://github.com/Moonofweisheng/sponsors']
-87
View File
@@ -1,87 +0,0 @@
name: 向 Wot Starter 反馈 Bug
description: 创建一个 Issue 描述你遇到的问题。
title: '[Bug 上报] 请在此填写标题'
labels: ['🐞bug: need confirm']
body:
- type: markdown
attributes:
value: |
在向我们提交 Bug 报告前,请优先使用以下方式尝试解决问题:
- 在组件文档 [wot-starter](https://starter.wot-ui.cn/) 确认使用方法是否正确
- 尝试在 [Issue](https://github.com/wot-ui/wot-starter/issues) 列表中搜索相同问题
- type: input
id: version
attributes:
label: Wot Starter 版本号
description: 你正在使用的模板版本号(请填写 package.json 里版本)
placeholder: 例如:0.1.1
validations:
required: true
- type: dropdown
id: platform
attributes:
label: 平台
multiple: true
description: 选择对应的平台
options:
- h5
- 微信小程序
- 支付宝小程序
- APP
- 钉钉小程序
- 其他小程序
validations:
required: true
- type: input
id: reproduce
attributes:
label: 复现Demo地址(如不提供,将被直接关闭)
description: |
我们需要你提供一个最小重现demo,以便于我们帮你排查问题。你可以通过 fork 本项目,快速创建一个 wot-ui 项目,并添加相关复现逻辑来提供。不要随便填写一个东西,这会导致你的 issue 被直接关闭。即使在你看来问题很容易复现,也请认真对待,因为一个完整复现demo可以大大提高我们排查问题的效率。
validations:
required: true
- type: textarea
id: reproduce-steps
attributes:
label: 重现步骤
description: |
请提供一个最简洁清晰的重现步骤,方便我们快速重现问题。
validations:
required: true
- type: textarea
id: expected
attributes:
label: 期望的结果是什么?
validations:
required: true
- type: textarea
id: actually-happening
attributes:
label: 实际的结果是什么?
validations:
required: true
- type: textarea
id: uni-app
attributes:
label: 环境信息
description: |
在这里填写你的环境信息
- 发行平台: [如 微信小程序、H5平台、App等]
- 操作系统 [如 iOS 12.1.2、Android 7.0]
- HBuilderX版本 [如使用HBuilderX,则需提供 HBuilderX 版本号]
- uni-app版本 [如使用Vue-cli创建/运行项目,则提供`npm run info`的运行结果]
- 设备信息 [如 iPhone8 Plus]
- type: textarea
id: extra
attributes:
label: 其他补充信息
description: |
根据你的分析,出现这个问题的原因可能在哪里,或者你认为可能产生关联的信息:比如 Vue 版本、vite 版本、Node 版本、采用哪种自动引入方案等,或者进行了哪些配置,使用了哪些插件等信息。
@@ -1,33 +0,0 @@
name: 向 Wot Starter 提出新功能需求
description: 创建一个 Issue 描述一下你的功能需求。
title: '[新功能需求] 请在此填写标题'
labels: ['feature: need confirm']
body:
- type: markdown
attributes:
value: |
在提交功能需求前,请注意:
- 确认这是一个通用功能,并且无法通过现有的 API 或 Slot 实现。
- 尝试在 [Issue](https://github.com/wot-ui/wot-starter/issues)列表中搜索,并且没有发现同样的需求。
- 请确保描述清楚你的需求,以便其他开发者更好地理解你的需求。
- type: textarea
id: description
attributes:
label: 这个功能解决了什么问题?
description: 请尽可能详细地说明这个功能的使用场景。
validations:
required: true
- type: textarea
id: api
attributes:
label: 你期望的 API 是什么样子的?
description: 描述一下这个新功能的 API,并提供一些代码示例。
placeholder: |
```xml
<wd-interesting some-prop="xxx" />
```
validations:
required: true
-43
View File
@@ -1,43 +0,0 @@
<!-- (将"[ ]"更新为"[x]"以勾选一个框) -->
### 🤔 这个 PR 的性质是?(至少选择一个)
- [ ] 日常 bug 修复
- [ ] 新特性提交
- [ ] 站点、文档改进
- [ ] 演示代码改进
- [ ] 组件样式/交互改进
- [ ] TypeScript 定义更新
- [ ] CI/CD 改进
- [ ] 包体积优化
- [ ] 性能优化
- [ ] 功能增强
- [ ] 国际化改进
- [ ] 代码重构
- [ ] 代码风格优化
- [ ] 测试用例
- [ ] 分支合并
- [ ] 其他改动(是关于什么的改动?)
### 🔗 相关 Issue
<!--
1. 描述相关需求的来源,如相关的 issue 讨论链接。
-->
### 💡 需求背景和解决方案
<!--
1. 要解决的具体问题。
2. 列出最终的 API 实现和用法。
3. 涉及UI/交互变动需要有截图或 GIF。
-->
### ☑️ 请求合并前的自查清单
⚠️ 请自检并全部**勾选全部选项**。⚠️
- [ ] 文档已补充或无须补充
- [ ] 代码演示已提供或无须提供
- [ ] TypeScript 定义已补充或无须补充
-119
View File
@@ -1,119 +0,0 @@
name: Build and Test
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
build-and-test:
runs-on: windows-latest
strategy:
matrix:
node-version: [20.x]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9.9.0
run_install: false
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Setup pnpm cache
uses: actions/cache@v4
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies (workspace recursive)
run: pnpm -r install --frozen-lockfile
- name: Test WeChat Mini Program dev mode
shell: bash
run: |
echo "Testing WeChat Mini Program dev mode startup..."
timeout 30s pnpm dev:mp-weixin || true
echo "✅ WeChat Mini Program dev mode test completed"
- name: Test H5 dev mode
shell: bash
run: |
echo "Testing H5 dev mode startup..."
timeout 30s pnpm dev:h5 || true
echo "✅ H5 dev mode test completed"
- name: Test iOS dev mode
shell: bash
run: |
echo "Testing iOS dev mode startup..."
timeout 30s pnpm dev:app-ios || true
echo "✅ iOS dev mode test completed"
- name: Build WeChat Mini Program
run: pnpm build:mp-weixin
- name: Build H5
run: pnpm build:h5
- name: Build iOS
run: pnpm build:app-ios
- name: Upload WeChat Mini Program artifacts
uses: actions/upload-artifact@v4
with:
name: wechat-miniprogram-build
path: dist/build/mp-weixin/
retention-days: 7
- name: Upload H5 artifacts
uses: actions/upload-artifact@v4
with:
name: h5-build
path: dist/build/h5/
retention-days: 7
- name: Upload iOS artifacts
uses: actions/upload-artifact@v4
with:
name: ios-build
path: dist/build/app-ios/
retention-days: 7
- name: Build summary
run: |
echo "## Build and Test Summary" >> $GITHUB_STEP_SUMMARY
echo "### Development Mode Tests" >> $GITHUB_STEP_SUMMARY
echo "✅ WeChat Mini Program dev mode test completed" >> $GITHUB_STEP_SUMMARY
echo "✅ H5 dev mode test completed" >> $GITHUB_STEP_SUMMARY
echo "✅ iOS dev mode test completed" >> $GITHUB_STEP_SUMMARY
echo "### Build Results" >> $GITHUB_STEP_SUMMARY
echo "✅ WeChat Mini Program build completed" >> $GITHUB_STEP_SUMMARY
echo "✅ H5 build completed" >> $GITHUB_STEP_SUMMARY
echo "✅ iOS build completed" >> $GITHUB_STEP_SUMMARY
echo "📦 All artifacts uploaded successfully" >> $GITHUB_STEP_SUMMARY
- name: Build Docs (VitePress)
run: pnpm -C docs run docs:build
- name: Upload Docs artifacts
uses: actions/upload-artifact@v4
with:
name: docs-build
path: docs/.vitepress/dist/
retention-days: 7
-54
View File
@@ -1,54 +0,0 @@
name: Release
on:
push:
tags:
- 'v*'
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20.x]
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9.9.0
run_install: false
- name: Create Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Generate GitHub Changelog
run: pnpx changelogithub
continue-on-error: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Release summary
run: |
echo "## Release Summary" >> $GITHUB_STEP_SUMMARY
echo "### Version: ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
echo "### Release Status" >> $GITHUB_STEP_SUMMARY
echo "✅ Release created successfully" >> $GITHUB_STEP_SUMMARY
-21
View File
@@ -1,21 +0,0 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
*.local
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
-4
View File
@@ -1,4 +0,0 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx commitlint --edit $1
-4
View File
@@ -1,4 +0,0 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged --allow-empty $1
-4
View File
@@ -1,4 +0,0 @@
strict-peer-dependencies=false
auto-install-peers=true
shamefully-hoist=true
ignore-workspace-root-check=true
-1
View File
@@ -1 +0,0 @@
22
-64
View File
@@ -1,64 +0,0 @@
{
"header": "# 更新日志 \n\n",
"types": [{
"type": "feat",
"section": "✨ Features | 新功能",
"hidden": false
},
{
"type": "fix",
"section": "🐛 Bug Fixes | Bug 修复",
"hidden": false
},
{
"type": "init",
"section": "🎉 Init | 初始化",
"hidden": true
},
{
"type": "docs",
"section": "✏️ Documentation | 文档",
"hidden": false
},
{
"type": "style",
"section": "💄 Styles | 风格",
"hidden": true
},
{
"type": "refactor",
"section": "♻️ Code Refactoring | 代码重构",
"hidden": true
},
{
"type": "perf",
"section": "⚡ Performance Improvements | 性能优化",
"hidden": false
},
{
"type": "test",
"section": "✅ Tests | 测试",
"hidden": true
},
{
"type": "revert",
"section": "⏪ Revert | 回退",
"hidden": true
},
{
"type": "build",
"section": "📦‍ Build System | 打包构建",
"hidden": true
},
{
"type": "chore",
"section": "🚀 Chore | 构建/工程依赖/工具",
"hidden": true
},
{
"type": "ci",
"section": "👷 Continuous Integration | CI 配置",
"hidden": true
}
]
}
-158
View File
@@ -1,158 +0,0 @@
# 更新日志
## [2.0.0](https://github.com/wot-ui/wot-starter/compare/v1.5.0...v2.0.0) (2026-04-21)
### ✨ Features | 新功能
* ✨ 集成 @wot-ui/vitepress-theme ([341f935](https://github.com/wot-ui/wot-starter/commit/341f935fef0da676c7f002506b51662baff09025))
* ✨ 支持 wot-ui v2 ([874455f](https://github.com/wot-ui/wot-starter/commit/874455f80abdd97d8ef11b4624d169ac3020a100))
### ✏️ Documentation | 文档
* ✏️ 更新全局反馈组件文档,增加使用前提和示例 ([8866c45](https://github.com/wot-ui/wot-starter/commit/8866c459a0b33d933a1cbfecaacb4d3673a85351))
### 🐛 Bug Fixes | Bug 修复
* 🐛 更新 banner URLs 为 wot-starter-v2-banner.json ([aa773d4](https://github.com/wot-ui/wot-starter/commit/aa773d409db38c041ba0e32b68602dc55f3fd2ec))
## [1.5.0](https://github.com/wot-ui/wot-starter/compare/v1.4.0...v1.5.0) (2026-04-07)
### ✨ Features | 新功能
* ✨ add wot-ui skill ([2b9b664](https://github.com/wot-ui/wot-starter/commit/2b9b664a087ca25fba26bcde22d1337a8d00309d))
### ✏️ Documentation | 文档
* ✏️ add wot-ui skill docs ([1c15e03](https://github.com/wot-ui/wot-starter/commit/1c15e03c47a1e6cdc3eb0e53cd65b41ef2dadf01))
## [1.4.0](https://github.com/wot-ui/wot-starter/compare/v1.3.2...v1.4.0) (2026-01-25)
### ✏️ Documentation | 文档
* ✏️ remove gitee-vote-2025 ([ed32556](https://github.com/wot-ui/wot-starter/commit/ed32556db46d7922cde1a60d1efc32bfb1c87d63))
### 🐛 Bug Fixes | Bug 修复
* 🐛 仅在微信小程序端开启 optimization 修复运行到支付宝小程序报错的问题 ([420aff4](https://github.com/wot-ui/wot-starter/commit/420aff484878ff88934b913d9aa84916a36c2de8))
### ✨ Features | 新功能
* ✨ 添加基于本项目实际使用场景的 Agent Skills ([f2a58f7](https://github.com/wot-ui/wot-starter/commit/f2a58f748c8758b4a083fa7181862dc1ad97e303))
* ✨ 新增清理演示页面提供精简模板的 skill starter-cleaner ([8b4c4c4](https://github.com/wot-ui/wot-starter/commit/8b4c4c4d0c9abdee1616484a435444c9ae4ce000))
### [1.3.2](https://github.com/wot-ui/wot-starter/compare/v1.3.1...v1.3.2) (2026-01-07)
### ✨ Features | 新功能
* ✨ 升级 @uni-ku/bundle-optimizer 至 2.0 并处理相关迁移配置 ([e0ab94c](https://github.com/wot-ui/wot-starter/commit/e0ab94cfd7e9971b52c929de5d89e7c5ab11eb3a))
### [1.3.1](https://github.com/wot-ui/wot-starter/compare/v1.3.0...v1.3.1) (2026-01-05)
### ✨ Features | 新功能
* ✨ 更新 @wot-ui/router 以修复 route 类型问题和 afterEach多次触发的问题 ([fd4f585](https://github.com/wot-ui/wot-starter/commit/fd4f58524bc580a3f3cc249a9447d7c3d0c556d5))
## [1.3.0](https://github.com/wot-ui/wot-starter/compare/v1.2.2...v1.3.0) (2026-01-04)
### ✨ Features | 新功能
* ✨ 更新 wot-ui 到 v1.14.0 版本 ([4fd2532](https://github.com/wot-ui/wot-starter/commit/4fd25328ba5c3d3e7ea202919071cd8fb98ffd74))
### [1.2.2](https://github.com/wot-ui/wot-starter/compare/v1.2.1...v1.2.2) (2025-12-29)
### ✨ Features | 新功能
* ✨ 替换 uni-mini-router 为 @wot-ui/router ([171f054](https://github.com/wot-ui/wot-starter/commit/171f054ac7bb0976ee26edcbf4028c80cc4387a2))
### ✏️ Documentation | 文档
* ✏️ 调整路由文档和演示demo ([73a9cf3](https://github.com/wot-ui/wot-starter/commit/73a9cf36b5b770a556e2ea74be9c5c21602ff661))
* ✏️ 更新 readme ([6bf2618](https://github.com/wot-ui/wot-starter/commit/6bf261833a6a8485c749c0123e3e8ba7b306156e))
* ✏️ 更新分包调整后demo的地址 ([5e358ec](https://github.com/wot-ui/wot-starter/commit/5e358ec27997c5437043ec3137cfbd4513154fd4))
* ✏️ 更新文档首页介绍内容 ([3a1a0a4](https://github.com/wot-ui/wot-starter/commit/3a1a0a409effcc730064d1acc1edc64fe58fd7d1))
* ✏️ 添加 PageSpy 远程的教程 ([f29a53c](https://github.com/wot-ui/wot-starter/commit/f29a53cd01cdc80c45e858e409f1564cf7716f73))
* ✏️ add about me ([046897a](https://github.com/wot-ui/wot-starter/commit/046897a862e402fd8ebbd993cac525de5e830f25))
* ✏️ add gitee vote 2025 ([14af7a0](https://github.com/wot-ui/wot-starter/commit/14af7a04cf1e26772782f0cad0d426e220dba40a))
### [1.2.1](https://github.com/wot-ui/wot-starter/compare/v1.2.0...v1.2.1) (2025-12-04)
### 🐛 Bug Fixes | Bug 修复
* **manualTheme:** 修复跟随系统自动切换主题失效的问题 ([380c702](https://github.com/wot-ui/wot-starter/commit/380c7026eeb37a12e9a2866b18bd70880efdfecc))
### ✨ Features | 新功能
* **index:** 首页设置中新增"跟随系统"按钮 ([d031788](https://github.com/wot-ui/wot-starter/commit/d031788d4c9b31a7d030f17856f69f2d177eb1b8))
* **logo:** 更新logo ([550caa2](https://github.com/wot-ui/wot-starter/commit/550caa243e423969745d62c3872dfae14976409a))
### ✏️ Documentation | 文档
* ✏️ 更新 logo ([0e57c45](https://github.com/wot-ui/wot-starter/commit/0e57c45b94284320d5fdc3be6c082faf12ddcbad))
* ✏️ 首页添加 uni-ku 插件入口 ([a5b05c9](https://github.com/wot-ui/wot-starter/commit/a5b05c9091f5396c27c556ed23eed87b7f06fef0))
* ✏️ 文档增加显示版本号 ([10b7078](https://github.com/wot-ui/wot-starter/commit/10b707810529af53bd2a85fca6624807b5b2d9ec))
* ✏️ 移动非主包必需示例页面到分包中 ([#43](https://github.com/wot-ui/wot-starter/issues/43)) ([3d7a076](https://github.com/wot-ui/wot-starter/commit/3d7a07619cf4b84c26a91b5028b2635bcc6d44ff)), closes [#35](https://github.com/wot-ui/wot-starter/issues/35)
* ✏️ update logo ([6b9e4f9](https://github.com/wot-ui/wot-starter/commit/6b9e4f9e66d7be10b1e678b46472c9e44a270fc3))
## [1.2.0](https://github.com/wot-ui/wot-starter/compare/v1.1.0...v1.2.0) (2025-11-26)
### ✨ Features | 新功能
* ✨ 合并模板与文档项目开发便利性优化 ([#41](https://github.com/wot-ui/wot-starter/issues/41)) ([646d215](https://github.com/wot-ui/wot-starter/commit/646d2158c96dcf83518ed22bc27cc8e20f2ed0d2)), closes [#35](https://github.com/wot-ui/wot-starter/issues/35)
* ✨ 支持 esm 并更新 unocss 和 [@uni-helper](https://github.com/uni-helper) 插件 ([#39](https://github.com/wot-ui/wot-starter/issues/39)) ([f433b49](https://github.com/wot-ui/wot-starter/commit/f433b49023c572488254b18584a4dbca0ba66336))
### ✏️ Documentation | 文档
* ✏️ 添加 vite base ([b6f5cf8](https://github.com/wot-ui/wot-starter/commit/b6f5cf83084d0cc04bc94c9714722a7a4c9e6327))
* ✏️ 增加更新日志入口 ([7037d8f](https://github.com/wot-ui/wot-starter/commit/7037d8f10a9aa0274d5cc5d7afc8978342f96f1a))
## [1.1.0](https://github.com/wot-ui/wot-starter/compare/v1.0.0...v1.1.0) (2025-11-12)
### ✨ Features | 新功能
* ✨ 添加 uni_modules 插件引入示例 ([8493761](https://github.com/wot-ui/wot-starter/commit/8493761ad6ea4e6478d3b7764b43b813e5178e86))
* ✨ 支持 harmony next 自定义 tabbar ([f71e8ba](https://github.com/wot-ui/wot-starter/commit/f71e8ba62504a4c0b79d02e61979b52e1f538e59))
## 1.0.0 (2025-10-28)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复分包路由未注册的问题 ([3da843b](https://github.com/wot-ui/wot-starter/commit/3da843ba33bf62d2a8032dabf3061b2ce87e46a9))
### ✏️ Documentation | 文档
* ✏️ 更新 README ([a630274](https://github.com/wot-ui/wot-starter/commit/a63027496f9f75e8106437bf7e4285164a7f91b1))
* ✏️ 更新README ([abde3bc](https://github.com/wot-ui/wot-starter/commit/abde3bca57cbee293d0751dd6f273366425e2474))
* ✏️ 添加分包示例 ([809b65b](https://github.com/wot-ui/wot-starter/commit/809b65b8384029d9ed2c7807709023d65bf6bb4c))
* **README:** ✏️ 更新 vitesse-uni-app 项目链接 ([#11](https://github.com/wot-ui/wot-starter/issues/11)) ([6f1585a](https://github.com/wot-ui/wot-starter/commit/6f1585a6da97a9aeed4071125e6b618f30a50bb7))
### ✨ Features | 新功能
* ✨ 全局反馈组件兼容支付宝小程序 ([7f04d43](https://github.com/wot-ui/wot-starter/commit/7f04d43d44b6eaedabcf32d6c5842b056a0ac8ba))
* ✨ 新增主题切换示例 ([#4](https://github.com/wot-ui/wot-starter/issues/4)) ([c39e756](https://github.com/wot-ui/wot-starter/commit/c39e756821b08ba9934c88f5576d6eabda8fd449))
* ✨ 引入 @uni-ku/root 解决使用 page-meta 和根组件的问题 ([989e9fd](https://github.com/wot-ui/wot-starter/commit/989e9fd05a9c5a3b103608c941e5e30040a19f32))
* ✨ 引入 uni-echarts 支持图表功能,增加分包异步化示例 ([f933a61](https://github.com/wot-ui/wot-starter/commit/f933a6143d5fe02783ade63c669001245970756e))
* ✨ 引入 vite-plugin-uni-pages 的 definePage 宏,优化开发体验 ([498df4f](https://github.com/wot-ui/wot-starter/commit/498df4f26b1a84e8e91827178167ff853ae1f1a9))
* 升级 uni-echarts 版本 ([#17](https://github.com/wot-ui/wot-starter/issues/17)) ([af94964](https://github.com/wot-ui/wot-starter/commit/af9496440e440afae589277c12999644ccfffe3e))
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2023-PRESENT KeJun
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-243
View File
@@ -1,243 +0,0 @@
<div align="center">
<p align="center">
<img src="src/static//logo.png" width="200" />
</p>
<h1 align="center">
FastApp
<sup style="background-color: #28a745; color: white; padding: 2px 6px; border-radius: 3px; font-size: 0.4em; vertical-align: super; margin-left: 5px;">v3.1.0</sup>
</h1>
<p align="center">
基于 uni-app + Vue 3 + TypeScript 的现代化移动端跨平台开发模板
</p>
<p align="center">
<img src="https://img.shields.io/badge/Vue-3.5.22-green.svg" alt="Vue">
<img src="https://img.shields.io/badge/TypeScript-5.9.2-blue.svg" alt="TypeScript">
<img src="https://img.shields.io/badge/uni--app-3.0.0-orange.svg" alt="uni-app">
<img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License">
</p>
</div>
> **与仓库根文档的关系**:项目总览、一键前后端启动、演示账号、Docker 部署等请以 [根目录 README.md](../../README.md) 为准;**本文档**侧重 `frontend/app/` 移动端开发说明。
## 项目介绍
FastApp 是 FastapiAdmin 项目的移动端应用,基于 uni-app 框架开发,支持一套代码多端运行。采用 Vue 3 + TypeScript + Vite 等现代化技术栈,集成了完善的代码规范和开发工具链,为开发者提供开箱即用的移动端开发解决方案。
## 特性
- ⚡️ [Vue 3](https://github.com/vuejs/core), [Vite](https://github.com/vitejs/vite), [pnpm](https://pnpm.io/), [esbuild](https://github.com/evanw/esbuild) - 就是快!
- 🐂 [Wot UI V2](https://github.com/wot-ui/wot-ui) - 一个轻量、美观、AI友好的 uni-app 组件库
- 🚦 [@wot-ui/router](https://github.com/wot-ui/my-uni) - 适用于uni-app&vue3的轻量级路由库
- 🔄 [Uni Mini CI](https://github.com/Moonofweisheng/uni-mini-ci) - 一个小程序端持续集成的插件
- 🌐 [Alova](https://alova.js.org/zh-CN/) - 极致高效的请求工具集
- 🆒 [Uni Ku](https://uni-ku.js.org/) - 非常酷的 uni-app 插件库
- 📊 [Uni Echarts](https://uni-echarts.xiaohe.ink/) - 适用于 uni-app 的 Apache ECharts 组件
- 🎨 [UnoCSS](https://github.com/unocss/unocss) - 高性能且极具灵活性的即时原子化 CSS 引擎
- 😃 [各种图标集为你所用](https://github.com/antfu/unocss/tree/main/packages/preset-icons)
- 🔥 使用 [新的 `<script setup>` 语法](https://github.com/vuejs/rfcs/pull/227)
- 📥 [API 自动加载](https://github.com/antfu/unplugin-auto-import) - 直接使用 Composition API 无需引入
- 🦾 [TypeScript](https://www.typescriptlang.org/) & [ESLint](https://eslint.org/) - 保证代码质量
## 项目结构
```bash
src/
├── api/ # API 接口定义
├── components/ # 公共组件
│ ├── DateQuery/ # 日期查询组件
│ └── Picker/ # 选择器组件
├── composables/ # 组合式函数
├── constants/ # 常量定义
├── enums/ # 枚举定义
├── http/ # HTTP 请求相关
│ ├── adapters/ # 请求适配器
│ ├── tools/ # 工具函数
│ └── types.ts # 类型定义
├── layouts/ # 布局组件
├── pages/ # 页面目录
│ ├── index/ # 首页
│ ├── login/ # 登录页
│ ├── work/ # 工作台
│ └── mine/ # 个人中心
├── router/ # 路由配置
├── store/ # 状态管理
├── types/ # TypeScript 类型
├── utils/ # 工具函数
├── App.vue # 应用根组件
├── main.ts # 应用入口文件
├── manifest.json # 应用配置文件
├── pages.json # 页面路由配置
└── theme.json # 主题配置
```
## 在线演示
- 📱 移动端:[https://service.fastapiadmin.com/app](https://service.fastapiadmin.com/app)
- 📖 在线文档:[https://service.fastapiadmin.com/](https://service.fastapiadmin.com/)
## 快速开始
### 环境要求
- **Node.js** >= 20
- **pnpm** >= 9
### 安装与运行
```bash
cd frontend/app
pnpm install
pnpm run dev:h5 # 启动 H5 开发服务器
pnpm run build:h5 # 构建 H5 应用
```
### 其他命令
```bash
pnpm run lint:eslint # ESLint 检查并自动修复
pnpm run lint:prettier # Prettier 格式化
pnpm run lint:stylelint # Stylelint 检查样式
pnpm run type-check # TypeScript 类型检查
```
## 截图
| 登录 | 首页 | 个人中心 |
| ---- | ---- | -------- |
| ![移动端登录](../../web/public/app_login.png) | ![移动端首页](../../web/public/app_home.png) | ![移动端个人中心](../../web/public/app_mine.png) |
## 鸣谢
- [uni-app](https://uniapp.dcloud.net.cn/) - 跨平台应用开发框架
- [Vue 3](https://cn.vuejs.org/) - 渐进式 JavaScript 框架
- [Vite](https://cn.vitejs.dev/) - 下一代前端构建工具
- [uni-helper](https://github.com/uni-helper) - 感谢 uni-helper 团队为 uni-app 开发体验优化做出的贡献。
- [vitesse-uni-app](https://github.com/uni-helper/vitesse-uni-app) - 感谢 vitesse-uni-app 提供的快速起手项目。
- [uni-ku](https://uni-ku.js.org/) - 感谢 uni-ku 团队为 uni-app 插件生态做出的贡献。
- [wot-ui-intellisense](https://github.com/wot-ui/wot-ui-intellisense) - wot-ui vscode 代码提示插件
- [awesome-uni-app](https://github.com/uni-helper/awesome-uni-app) - 多端统一开发框架 uni-app 优秀开发资源汇总
- [create-uni](https://github.com/uni-helper/create-uni) - 快速创建 uni-app 项目
- [wot-starter-retail](https://github.com/Moonofweisheng/wot-starter-retail) - 基于 wot-ui 的 uni-app 零售行业模板
- [uni-mini-ci](https://github.com/Moonofweisheng/uni-mini-ci) - 一个 uni-app 小程序端构建后支持 CI(持续集成)的插件
- [@wot-ui/router](https://github.com/wot-ui/my-uni) - 一个基于 vue3 和 Typescript 的轻量级 uni-app 路由库
- [uni-ku-root](https://github.com/uni-ku/root) - 一个模拟 App.vue 原有能力的根组件插件
- [uni-echarts](https://uni-echarts.xiaohe.ink/) - 适用于 uni-app 的 Apache ECharts 组件
## 许可证
本项目采用 [MIT](LICENSE) 许可证。
[![Star History Chart](https://api.star-history.com/svg?repos=FastapiAdmin/FastapiAdmin&type=Date)](https://star-history.com/#FastapiAdmin/FastapiAdmin&Date)
---
**如果这个项目对你有帮助,请给一个 ⭐ Star**
Made with ❤️ by FastApp Team
## wot-UI 组件库
基础组件:
wd-button 按钮
wd-icon 图标
wd-text 文本
Layout 布局:wd-row、wd-col提供了 24列 栅格,通过在 wd-col 上设置 span 属性,通过计算当前内容所占百分比进行分栏
wd-cell-group、wd-cell 单元格
wd-fab 悬浮按钮
wd-transition 过渡动画
wd-resize 监听元素尺寸变化
wd-config-provider 组件Wot全局配置
wd-root-portal 根节点传送,是否从页面中脱离出来,用于解决各种 fixed 失效问题,主要用于制作弹窗、弹出层等。
导航类组件:
wd-navbar 导航栏
wd-tabbar、wd-tabbar-item 标签栏
wd-tabs 标签页
wd-segmented 分段控制器
wd-sidebar、wd-sidebar-item 侧边栏
wd-pagination 分页组件
wd-index-bar、wd-index-anchor 索引栏
wd-backtop 回到顶部
录入类组件:
wd-form、wd-form-item 表单
wd-input 输入框
wd-textarea 文本域
wd-password-input 密码输入框
wd-keyboard 键盘输入框
wd-input-number 数字输入框
wd-search 搜索框
wd-checkbox-group、wd-checkbox 复选框
wd-radio-group、wd-radio 单选框
wd-switch 开关按钮
wd-rate 评分
wd-slider 滑块
wd-picker 选择器
wd-picker-view 选择器视图
wd-select-picker 单复选选择器
wd-cascader 级联选择器
wd-calendar 日历选择器
wd-calendar-view 日历面板
wd-datetime-picker 日期时间选择器
wd-upload 图片、视频和文件上传组件
wd-signature 签名组件
wd-slide-verify 滑动验证组件
反馈组件:
wd-popup 弹出层,用于展示弹窗、信息提示等内容。
wd-overlay 遮罩层,用于在弹出层显示时,遮挡背景,防止用户操作。
wd-dialog 弹出对话框,常用于消息提示、操作确认和输入收集,支持函数式调用
wd-action-sheet 操作表单,从底部弹出的动作菜单面板
wd-drop-menu wd-drop-menu-item 下拉菜单
wd-popover 气泡,常用于展示提示信息或菜单操作
wd-tooltip 文字提示,用于展示简短提示信息,支持多方向定位、受控显隐、自定义内容和动态更新位置
wd-floating-panel 浮动在页面底部的面板,用户可以通过上下拖动秒板来浏览内容,常用于地图导航
wd-loading 加载中组件,用于在异步操作进行中显示加载状态,防止用户操作
wd-progress 进度条,用于展示任务完成进度
wd-circle 圆形进度条,用于展示任务完成进度,支持自定义进度条颜色、进度条宽度、进度条高度等
wd-toast 轻提示,轻提示组件,用于消息通知、加载提示和操作结果反馈,支持组件挂载点配合 useToast() 进行函数式调用。
wd-notify 消息通知,用于在页面顶部展示通知信息。
wd-notice-bar 通知栏,用于在页面顶部展示通知信息,支持自定义内容和样式
wd-swipe-action 滑动操作,用于在列表项上添加滑动操作按钮,支持自定义按钮内容和样式
wd-sort-button 排序按钮,用于在列表项上添加排序按钮,支持自定义按钮内容和样式
wd-empty 空状态组件,用于展示无数据或无结果的情况,一般用于兜底占位展示
wd-count-down 倒计时组件,用于实时展示倒计时数值,支持毫秒级渲染与手动控制
wd-count-to 数字滚动组件,用于展示数值变化,支持自定义滚动速度、滚动时间等
展示类组件:
wd-avatar 头像, 用来代表用户或事物,支持图片、文本或图标展示
wd-badge 徽标,用于展示未读消息、未完成任务等数量,支持自定义数量、颜色、位置等
wd-tag 标签,用于展示分类、状态、标签等信息,支持自定义标签内容、样式、位置等
wd-card 卡片,用于展示信息、操作、内容等,支持自定义卡片内容、样式、位置等
wd-divider 分割线,用于分隔不同内容区域,支持自定义分割线内容、样式、位置等
wd-gap 间距组件,用于在元素之间添加间距,支持自定义间距大小、方向等
wd-grid 宫格,用于在页面上创建网格布局,支持自定义网格列数、网格间距等
wd-collapse、wd-collapse-item 折叠面板,将一组内容放置在多个折叠面板中,点击面板标题可展开或收起内容
wd-steps、wd-step 步骤条,用于引导用户按照流程完成任务,或向用户展示当前所处的步骤状态
wd-sticky 粘性组件,用于在页面滚动时保持元素在顶部或底部,不被内容遮挡
wd-skeleton 骨架屏组件,用于在数据加载中展示占位符,防止用户操作
wd-loadmore 加载更多组件,用于在列表底部添加加载更多按钮,点击后加载更多数据
wd-img 增强版图片组件,支持填充模式、懒加载、加载态/失败态插槽,以及点击预览。
wd-image-preview 图片预览组件,用于在点击图片时预览图片,支持自定义预览位置、预览大小等。
wd-video-preview 视频预览组件,用于在点击视频时预览视频,支持自定义预览位置、预览大小等。
wd-img-cropper 图片剪裁组件,用于图片裁剪,支持拖拽、缩放、旋转等操作
wd-swiper 轮播图组件,用于展示图片、视频等内容,支持自动播放、手动切换、循环播放等
wd-table wd-table-column 用于展示多条结构类似的数据,支持固定列、排序、合并单元格与虚拟滚动等能力
wd-watermark 在页面或组件上添加指定的图片或文字,可用于版权保护、品牌宣传等场景
wd-curtain 幕帘组件,用于在页面上创建一个透明的遮罩层,一般用于公告类图片弹窗展示
组合式api
useUpload:用于处理文件上传和选择相关的逻辑
useCountDown:用于处理倒计时相关的逻辑
useToast:用于处理轻提示相关的逻辑
useDialog:用于处理弹窗相关的逻辑,useDialog 用于函数式调用 wd-dialog,支持 alert、confirm、prompt、show 和 close
useImagePreview:用于处理图片预览相关的逻辑,useImagePreview 用于函数式调用 wd-image-preview,支持自定义预览位置、预览大小等。
useVideoPreview:用于处理视频预览相关的逻辑,useVideoPreview 用于函数式调用 wd-video-preview,支持自定义预览位置、预览大小等。
useConfigProvider:用于处理全局配置相关的逻辑,useConfigProvider 用于函数式调用 wd-config-provider,支持自定义全局配置项,用于在 JS 逻辑中注入全局配置(如主题变量),解决在微信小程序等环境中,由于组件渲染机制限制(如原生插槽作用域隔离)或使用 root-portal 导致无法获取父级 ConfigProvider 配置的问题。
提示:需要和 ConfigProvider 组件配合使用,使用 ConfigProvider 组件包裹你的组件。用于解决小程序端依赖注入的限制,导致部分场景下无法获取父级 ConfigProvider 配置的问题。
-12
View File
@@ -1,12 +0,0 @@
/*
* @Author: weisheng
* @Date: 2024-10-29 20:12:08
* @LastEditTime: 2024-10-29 20:12:20
* @LastEditors: weisheng
* @Description:
* @FilePath: \salary-calculator\commitlint.config.js
* 记得注释
*/
export default {
extends: ['@commitlint/config-conventional'],
}
-32
View File
@@ -1,32 +0,0 @@
.DS_Store
coverage/
node_modules/
unpackage/
dist/
lib/
website/
.temp
.cache
.vitepress/cache
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.project
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*
.history
tags.json
attributes.json
web-types.json
-252
View File
@@ -1,252 +0,0 @@
import fsp from 'node:fs/promises'
import { fileURLToPath, URL } from 'node:url'
import UnoCSS from '@unocss/vite'
import { createWotVitePressConfig } from '@wot-ui/vitepress-theme/config'
import packageJson from '../../package.json'
// https://vitepress.dev/reference/site-config
function copyDemoPlugin() {
return {
name: 'copy-demo-to-vitepress-dist',
apply: 'build' as const,
async closeBundle() {
const srcRoot = fileURLToPath(new URL('../../dist/build/h5', import.meta.url))
const destRoot = fileURLToPath(new URL('./dist/demo', import.meta.url))
try {
await fsp.rm(destRoot, { recursive: true, force: true })
}
catch {}
await fsp.mkdir(destRoot, { recursive: true })
try {
await fsp.cp(srcRoot, destRoot, { recursive: true })
}
catch {}
},
}
}
function copyChangelogPlugin() {
return {
name: 'copy-changelog-to-guide',
async configResolved() {
const src = fileURLToPath(new URL('../../CHANGELOG.md', import.meta.url))
const guideDir = fileURLToPath(new URL('../guide', import.meta.url))
const dest = fileURLToPath(new URL('../guide/changelog.md', import.meta.url))
await fsp.mkdir(guideDir, { recursive: true })
await fsp.copyFile(src, dest)
},
async buildStart() {
const src = fileURLToPath(new URL('../../CHANGELOG.md', import.meta.url))
const guideDir = fileURLToPath(new URL('../guide', import.meta.url))
const dest = fileURLToPath(new URL('../guide/changelog.md', import.meta.url))
await fsp.mkdir(guideDir, { recursive: true })
await fsp.copyFile(src, dest)
},
}
}
const guideGroups = [
{
text: '基础',
items: [
{ text: '介绍', link: '/guide/introduction' },
{ text: '起步', link: '/guide/installation' },
{ text: '更新日志', link: '/guide/changelog' },
],
},
{
text: '开发',
items: [
{ text: 'Uni Helper 插件', link: '/guide/uni-helper' },
{ text: 'Wot UI', link: '/guide/wot-ui' },
{ text: 'UnoCSS 样式', link: '/guide/styling' },
{ text: '图标使用', link: '/guide/icons' },
{ text: '暗黑模式', link: '/guide/dark-mode' },
{ text: '国际化', link: '/guide/i18n' },
{ text: '路由管理', link: '/guide/router' },
{ text: '网络请求', link: '/guide/request' },
{ text: '状态管理', link: '/guide/state-management' },
{ text: '全局反馈组件', link: '/guide/feedback' },
],
},
{
text: '工程化',
items: [
{ text: '自定义 Tabbar', link: '/guide/tabbar' },
{ text: '分包优化', link: '/guide/bundle-optimizer' },
{ text: '虚拟根组件', link: '/guide/uni-ku-root' },
{ text: 'Echarts 图表', link: '/guide/uni-echarts' },
{ text: '部署', link: '/guide/deployment' },
{
text: 'UnoCSS 预设',
link: '/guide/unocss-preset',
},
{ text: '远程调试', link: 'https://blog.wot-ui.cn/uni-app/pagespy.html' },
],
},
{
text: 'AI',
items: [
{
text: 'LLMs.txt',
link: '/guide/llms-txt',
},
{
text: 'CLI',
link: '/guide/open-wot',
},
{
text: 'Skills',
link: '/guide/skills',
},
],
},
]
const ecosystemNavItems = [
{
text: '官方生态',
items: [
{ text: 'Wot UI', link: 'https://wot-ui.cn/' },
{ text: 'Wot Starter 演示', link: 'https://starter.wot-ui.cn/demo/#/' },
{ text: '@wot-ui/router', link: 'https://my-uni.wot-ui.cn/' },
{ text: '@wot-ui/cli', link: 'https://github.com/wot-ui/open-wot' },
{ text: '@wot-ui/unocss-preset', link: 'https://github.com/wot-ui/unocss-preset' },
{ text: 'VS Code 插件', link: 'https://marketplace.visualstudio.com/items?itemName=wot-ui.wot-ui-intellisense' },
{ text: 'Wot Starter Retail', link: 'https://github.com/wot-ui/wot-starter-retail' },
],
},
{
text: '开发资源',
items: [
{ text: 'Uni Helper', link: 'https://uni-helper.cn/' },
{ text: 'uni-ku', link: 'https://uni-ku.js.org/' },
{ text: 'uni-mini-ci', link: 'https://github.com/Moonofweisheng/uni-mini-ci' },
{ text: 'Alova', link: 'https://alova.js.org/zh-CN/' },
{ text: 'uni-echarts', link: 'https://uni-echarts.xiaohe.ink/' },
],
},
]
const supportNavItems = [
{ text: '🥤一杯咖啡', link: 'https://wot-ui.cn/reward/reward' },
{ text: '关于作者', link: 'https://blog.wot-ui.cn/about' },
]
const versionNavItems = [
{ text: 'v1', link: 'https://starter-v1.wot-ui.cn' },
{ text: '更新日志', link: '/guide/changelog.html' },
]
const guideSidebar = [
...guideGroups,
]
export default createWotVitePressConfig({
lang: 'zh-CN',
title: 'Wot Starter',
description: '⚡️ 基于 vitesse-uni-app 由 vite & uni-app 驱动的、深度整合 Wot UI 组件库的快速启动模板',
head: [
['link', { rel: 'icon', href: '/favicon.ico' }],
['meta', { name: 'algolia-site-verification', content: '223BF8314C40C6AE' }],
['script', {}, `
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?45a448dc275714ac7c6e31b0f284124e";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
`],
],
vite: {
plugins: [
UnoCSS(),
copyDemoPlugin(),
copyChangelogPlugin(),
],
server: {
host: '0.0.0.0',
port: 5174,
},
ssr: {
noExternal: ['@wot-ui/vitepress-theme'],
},
optimizeDeps: {
exclude: ['@wot-ui/vitepress-theme'],
},
},
markdown: {
componentLinks: false,
scssVars: false,
versionBadge: true,
virtualVersionData: {
docsRoot: fileURLToPath(new URL('../', import.meta.url)),
},
},
features: {
llms: {
ignoreFiles: ['index.md', 'README.md', 'guide/consultation.md'],
domain: import.meta.env?.VITE_WEB_SITE_BASE_URL || 'https://starter.wot-ui.cn',
},
compression: {
verbose: true,
disable: false,
threshold: 10240,
algorithm: 'gzip',
ext: '.gz',
},
},
themeConfig: {
logo: '/logo.svg',
lastUpdated: {
text: '最后更新',
},
editLink: {
pattern: 'https://github.com/wot-ui/wot-starter/edit/v2/docs/:path',
text: '为此页提供修改建议',
},
socialLinks: [
{ icon: 'github', link: 'https://github.com/wot-ui/wot-starter/tree/v2' },
{ icon: { svg: '<svg t="1692699544299" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4184" width="200" height="200"><path d="M512 1024C230.4 1024 0 793.6 0 512S230.4 0 512 0s512 230.4 512 512-230.4 512-512 512z m259.2-569.6H480c-12.8 0-25.6 12.8-25.6 25.6v64c0 12.8 12.8 25.6 25.6 25.6h176c12.8 0 25.6 12.8 25.6 25.6v12.8c0 41.6-35.2 76.8-76.8 76.8h-240c-12.8 0-25.6-12.8-25.6-25.6V416c0-41.6 35.2-76.8 76.8-76.8h355.2c12.8 0 25.6-12.8 25.6-25.6v-64c0-12.8-12.8-25.6-25.6-25.6H416c-105.6 0-188.8 86.4-188.8 188.8V768c0 12.8 12.8 25.6 25.6 25.6h374.4c92.8 0 169.6-76.8 169.6-169.6v-144c0-12.8-12.8-25.6-25.6-25.6z" fill="#6D6D72" p-id="4185"></path></svg>' }, link: 'https://gitee.com/wot-ui/wot-starter', ariaLabel: 'Gitee' },
{ icon: { svg: '<svg t="1758594913114" class="icon" viewBox="0 0 1316 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5329" width="200" height="200"><path d="M643.181714 247.698286l154.916572-123.172572L643.181714 0.256 643.072 0l-154.660571 124.269714 154.660571 123.245715 0.109714 0.182857z m0 388.461714h0.109715l399.579428-315.245714-108.361143-87.04-291.218285 229.888h-0.146286l-0.109714 0.146285L351.817143 234.093714l-108.251429 87.04 399.433143 315.136 0.146286-0.146285z m-0.146285 215.552l0.146285-0.146286 534.893715-422.034285 108.397714 87.04-243.309714 192L643.145143 1024 10.422857 525.056 0 516.754286l108.251429-86.893715L643.035429 851.748571z" fill="#1E80FF" p-id="5330"></path></svg>' }, link: 'https://juejin.cn/user/26044011388510/posts' },
],
search: {
provider: 'algolia',
options: {
appId: 'ITS8LMWRYB',
apiKey: '259280bc7bfdf1686586ed7680c68a4c',
indexName: 'wot_demo_docs_netlify_app_its8lmwryb_pages',
},
},
footer: {
message: `Released under the MIT License.`,
copyright: 'Copyright © 2025-present Wot UI Team and contributors',
},
nav: [
{ text: '首页', link: '/' },
{
text: '指南',
activeMatch: '/guide/',
items: guideGroups,
},
{
text: '生态',
items: ecosystemNavItems,
},
{
text: '支持',
activeMatch: '/guide/consultation',
items: supportNavItems,
},
{
text: packageJson.version,
items: versionNavItems,
},
],
sidebar: {
'/guide/': guideSidebar,
},
},
})
@@ -1,39 +0,0 @@
/*
* @Author: weisheng
* @Date: 2026-04-20 14:08:09
* @LastEditTime: 2026-04-21 19:48:59
* @LastEditors: weisheng
* @Description:
* @FilePath: /wot-starter/docs/.vitepress/theme/index.ts
* 记得注释
*/
import { createWotVitePressTheme } from '@wot-ui/vitepress-theme'
export default createWotVitePressTheme({
analytics: {
trackBaiduRoute: true,
},
demoIframe: {
// assetBase: '/wxqrcode',
enabled: false,
excludePatterns: ['/guide/skills', '/guide/open-wot', '/guide/llms-txt', '/guide/unocss-preset', '/guide/wot-ui', '/guide/uni-helper', '/guide/bundle-optimizer', '/guide/changelog', '/guide/deployment', '/guide/i18n', '/guide/introduction'],
routePatterns: ['/guide'],
},
banner: {
urls: ['https://sponsor.wot-ui.cn/wot-starter-v2-banner.json', 'https://wot-sponsors.pages.dev/wot-starter-v2-banner.json'],
},
specialSponsor: {
enabled: false,
urls: [],
},
ads: {
wwadsId: '372',
// urls: ['https://sponsor.wot-ui.cn/ads.json', 'https://wot-sponsors.pages.dev/ads.json'],
},
team: {
urls: ['https://sponsor.wot-ui.cn/team.json', 'https://wot-sponsors.pages.dev/team.json'],
},
friendly: {
urls: ['https://sponsor.wot-ui.cn/friendly.json', 'https://wot-sponsors.pages.dev/friendly.json'],
},
})
-8
View File
@@ -1,8 +0,0 @@
/// <reference types="vitepress/client" />
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<Record<string, never>, Record<string, never>, any>
export default component
}
-316
View File
@@ -1,316 +0,0 @@
# 分包优化
本项目基于 [@uni-ku/bundle-optimizer](https://github.com/uni-ku/bundle-optimizer) 分包优化插件,实现 uni-app Vue3 项目分包优化插件,解决小程序主包体积超限问题。
:::tip 提示
本章节基于 @uni-ku/bundle-optimizer@2.x 版本编写,1.x 版本请参考 [@uni-ku/bundle-optimizer](https://github.com/uni-ku/bundle-optimizer) 文档处理。
:::
## 插件简介
### 为什么需要这个插件?
uni-app Vue3vite 构建)官方为了"简化配置",移除了 Vue2(webpack 构建)中内置的分包优化逻辑。这导致所有第三方库、公共组件、工具函数全部打进 `common/vendor.js`,主包体积瞬间超限,无法满足微信小程序 2 MB 限制。
**@uni-ku/bundle-optimizer** 把官方砍掉的「自动拆包」能力补了回来,并提供:
- **分包优化**:自动将公共依赖抽离到主包,各分包仅保留自用代码
- **模块异步跨包调用**:使用 `import()` 语法异步引用模块
- **组件异步跨包引用**:通过 `componentPlaceholder` 配置实现
### 功能特性
| 功能 | 说明 |
|---|---|
| 分包优化 | 自动将公共依赖抽离到主包,控制主包体积 |
| 模块异步跨包调用 | 允许使用 `import()` 语法,异步引用 JS/TS 模块 |
| 组件异步跨包引用 | 通过 `componentPlaceholder` 配置,实现组件异步跨包引用 |
### 适用范围
> ⚠️ **暂时不支持 App 平台**
适用于 uni-app CLI 或 HBuilderX 创建的 Vue3 项目
---
## 快速上手
> 本项目已集成 @uni-ku/bundle-optimizer 插件,无需额外安装。
### 1. 安装插件
```bash
pnpm add -D @uni-ku/bundle-optimizer
```
### 2. 配置 vite.config.ts
```ts
import { defineConfig } from 'vite'
import uni from '@dcloudio/vite-plugin-uni'
import Optimization from '@uni-ku/bundle-optimizer'
export default defineConfig({
plugins: [
uni(),
Optimization({
logger: false, // 是否输出日志
}),
],
})
```
### 3. 开启微信小程序分包优化
`manifest.json``manifest.config.ts` 中配置:
```json
{
"mp-weixin": {
"optimization": {
"subPackages": true
}
}
}
```
或使用 `@uni-helper/vite-plugin-uni-manifest`
```ts
// manifest.config.ts
import { defineManifestConfig } from '@uni-helper/vite-plugin-uni-manifest'
export default defineManifestConfig({
'mp-weixin': {
optimization: {
subPackages: true,
},
},
})
```
### 4. (可选)添加 TypeScript 类型支持
`tsconfig.json` 中添加:
```json
{
"compilerOptions": {
"types": ["@uni-ku/bundle-optimizer/client"]
}
}
```
或在入口文件顶部添加:
```ts
/// <reference types="@uni-ku/bundle-optimizer/client" />
```
---
## 使用示例
### 模块异步跨包调用
使用 ESM 原生异步导入语法 `import()` 来实现模块的异步引入:
```ts
// 异步引入 JS/TS 模块
import('@/pages-sub-pkg/utils/encrypt.ts').then((mod) => {
mod?.aesEncrypt('hello')
})
// 或使用 async/await
const mod = await import('@/pages-sub-pkg/utils/encrypt.ts')
mod?.aesEncrypt('hello')
```
> ⚠️ **注意**:不要使用 `import('./Comp.vue').then(...)` 动态导入 Vue 文件,这会导致组件/页面空白,与分包优化逻辑冲突。
### 组件异步跨包引用
通过 `componentPlaceholder` 配置实现组件异步跨包引用:
#### 方式一:使用 `<script setup>`(推荐)
```vue
<script setup lang="ts">
import Chart from '@/pages-sub-echarts/chart.vue'
defineOptions({
componentPlaceholder: {
Chart: 'view',
},
})
</script>
<template>
<Chart />
</template>
```
#### 方式二:使用选项式 API
```vue
<script>
import Chart from '@/pages-sub-echarts/chart.vue'
export default {
components: { Chart },
componentPlaceholder: {
Chart: 'view',
},
}
</script>
<template>
<Chart />
</template>
```
> 💡 **提示**`componentPlaceholder` 的值通常填写 `'view'` 即可。
---
## 本项目中的使用
### 配置说明
本项目已完整接入 `@uni-ku/bundle-optimizer` 插件,配置如下:
#### vite.config.ts 配置
```ts
import Optimization from '@uni-ku/bundle-optimizer'
export default defineConfig({
plugins: [
// ... 其他插件
Optimization({
logger: false,
}),
],
})
```
#### manifest.config.ts 配置
```ts
export default defineManifestConfig({
'mp-weixin': {
optimization: {
subPackages: true,
},
},
})
```
#### 分包配置
`vite.config.ts` 中配置了三个分包:
```ts
UniHelperPages({
subPackages: [
'src/subPages',
'src/subEcharts',
'src/subAsyncEcharts',
],
})
```
### 实际使用示例
#### 异步组件跨包引用示例
`src/subAsyncEcharts/asyncEcharts/index.vue` 中,展示了如何异步引用来自 `src/subEcharts` 分包的图表组件:
```vue
<script setup lang="ts">
import BarChart from '@/subEcharts/echarts/components/BarChart.vue'
import DonutChart from '@/subEcharts/echarts/components/DonutChart.vue'
import FunnelChart from '@/subEcharts/echarts/components/FunnelChart.vue'
import GaugeChart from '@/subEcharts/echarts/components/GaugeChart.vue'
import LineChart from '@/subEcharts/echarts/components/LineChart.vue'
import LiquidFillChart from '@/subEcharts/echarts/components/LiquidFillChart.vue'
import MiniLineChart from '@/subEcharts/echarts/components/MiniLineChart.vue'
import PieChart from '@/subEcharts/echarts/components/PieChart.vue'
import RadarChart from '@/subEcharts/echarts/components/RadarChart.vue'
import ScatterChart from '@/subEcharts/echarts/components/ScatterChart.vue'
import StackedBarChart from '@/subEcharts/echarts/components/StackedBarChart.vue'
defineOptions({
componentPlaceholder: {
BarChart: 'view',
DonutChart: 'view',
FunnelChart: 'view',
GaugeChart: 'view',
LineChart: 'view',
LiquidFillChart: 'view',
MiniLineChart: 'view',
PieChart: 'view',
RadarChart: 'view',
ScatterChart: 'view',
StackedBarChart: 'view',
},
})
</script>
<template>
<view class="bg-gray-50 p-5">
<view class="mb-5 rounded-3 bg-white p-5 shadow-sm">
<view class="mb-5 text-center text-base text-gray-800 font-medium">
饼图示例
</view>
<PieChart />
</view>
<!-- 更多图表... -->
</view>
</template>
```
这个示例展示了:
1.`subEcharts` 分包异步引用多个图表组件
2. 使用 `componentPlaceholder` 配置所有异步组件
3. 在模板中正常使用这些组件
### 验证效果
构建项目并使用微信开发者工具查看主包大小:
```bash
pnpm build:mp-weixin
```
使用微信开发者工具的「构建分析」功能对比主包大小,可以看到分包优化带来的体积缩减效果。
---
## 常见问题
### Q: 主包体积没有变化?
A: 检查 `manifest.json``manifest.config.ts` 中是否开启了 `mp-weixin.optimization.subPackages: true`
### Q: 组件或页面空白?
A: 可能使用了 `import('./Comp.vue').then(...)` 动态导入 Vue 文件,请改用 `componentPlaceholder` 配置式方案。
### Q: 如何配置异步组件?
A: 使用 `componentPlaceholder` 配置,通常填 `'view'` 即可。
### Q: 支持 App 平台吗?
A: 暂不支持 App 平台,未来是否支持未知。
### Q: 为什么要使用原生 `import()`
A: 降低学习成本,提供更好的 IDE 类型支持,并使代码更符合标准。
---
## 参考资料
- [@uni-ku/bundle-optimizer 官方文档](https://github.com/uni-ku/bundle-optimizer)
- [微信小程序分包异步加载](https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages/async.html)
-158
View File
@@ -1,158 +0,0 @@
# 更新日志
## [2.0.0](https://github.com/wot-ui/wot-starter/compare/v1.5.0...v2.0.0) (2026-04-21)
### ✨ Features | 新功能
* ✨ 集成 @wot-ui/vitepress-theme ([341f935](https://github.com/wot-ui/wot-starter/commit/341f935fef0da676c7f002506b51662baff09025))
* ✨ 支持 wot-ui v2 ([874455f](https://github.com/wot-ui/wot-starter/commit/874455f80abdd97d8ef11b4624d169ac3020a100))
### ✏️ Documentation | 文档
* ✏️ 更新全局反馈组件文档,增加使用前提和示例 ([8866c45](https://github.com/wot-ui/wot-starter/commit/8866c459a0b33d933a1cbfecaacb4d3673a85351))
### 🐛 Bug Fixes | Bug 修复
* 🐛 更新 banner URLs 为 wot-starter-v2-banner.json ([aa773d4](https://github.com/wot-ui/wot-starter/commit/aa773d409db38c041ba0e32b68602dc55f3fd2ec))
## [1.5.0](https://github.com/wot-ui/wot-starter/compare/v1.4.0...v1.5.0) (2026-04-07)
### ✨ Features | 新功能
* ✨ add wot-ui skill ([2b9b664](https://github.com/wot-ui/wot-starter/commit/2b9b664a087ca25fba26bcde22d1337a8d00309d))
### ✏️ Documentation | 文档
* ✏️ add wot-ui skill docs ([1c15e03](https://github.com/wot-ui/wot-starter/commit/1c15e03c47a1e6cdc3eb0e53cd65b41ef2dadf01))
## [1.4.0](https://github.com/wot-ui/wot-starter/compare/v1.3.2...v1.4.0) (2026-01-25)
### ✏️ Documentation | 文档
* ✏️ remove gitee-vote-2025 ([ed32556](https://github.com/wot-ui/wot-starter/commit/ed32556db46d7922cde1a60d1efc32bfb1c87d63))
### 🐛 Bug Fixes | Bug 修复
* 🐛 仅在微信小程序端开启 optimization 修复运行到支付宝小程序报错的问题 ([420aff4](https://github.com/wot-ui/wot-starter/commit/420aff484878ff88934b913d9aa84916a36c2de8))
### ✨ Features | 新功能
* ✨ 添加基于本项目实际使用场景的 Agent Skills ([f2a58f7](https://github.com/wot-ui/wot-starter/commit/f2a58f748c8758b4a083fa7181862dc1ad97e303))
* ✨ 新增清理演示页面提供精简模板的 skill starter-cleaner ([8b4c4c4](https://github.com/wot-ui/wot-starter/commit/8b4c4c4d0c9abdee1616484a435444c9ae4ce000))
### [1.3.2](https://github.com/wot-ui/wot-starter/compare/v1.3.1...v1.3.2) (2026-01-07)
### ✨ Features | 新功能
* ✨ 升级 @uni-ku/bundle-optimizer 至 2.0 并处理相关迁移配置 ([e0ab94c](https://github.com/wot-ui/wot-starter/commit/e0ab94cfd7e9971b52c929de5d89e7c5ab11eb3a))
### [1.3.1](https://github.com/wot-ui/wot-starter/compare/v1.3.0...v1.3.1) (2026-01-05)
### ✨ Features | 新功能
* ✨ 更新 @wot-ui/router 以修复 route 类型问题和 afterEach多次触发的问题 ([fd4f585](https://github.com/wot-ui/wot-starter/commit/fd4f58524bc580a3f3cc249a9447d7c3d0c556d5))
## [1.3.0](https://github.com/wot-ui/wot-starter/compare/v1.2.2...v1.3.0) (2026-01-04)
### ✨ Features | 新功能
* ✨ 更新 wot-ui 到 v1.14.0 版本 ([4fd2532](https://github.com/wot-ui/wot-starter/commit/4fd25328ba5c3d3e7ea202919071cd8fb98ffd74))
### [1.2.2](https://github.com/wot-ui/wot-starter/compare/v1.2.1...v1.2.2) (2025-12-29)
### ✨ Features | 新功能
* ✨ 替换 uni-mini-router 为 @wot-ui/router ([171f054](https://github.com/wot-ui/wot-starter/commit/171f054ac7bb0976ee26edcbf4028c80cc4387a2))
### ✏️ Documentation | 文档
* ✏️ 调整路由文档和演示demo ([73a9cf3](https://github.com/wot-ui/wot-starter/commit/73a9cf36b5b770a556e2ea74be9c5c21602ff661))
* ✏️ 更新 readme ([6bf2618](https://github.com/wot-ui/wot-starter/commit/6bf261833a6a8485c749c0123e3e8ba7b306156e))
* ✏️ 更新分包调整后demo的地址 ([5e358ec](https://github.com/wot-ui/wot-starter/commit/5e358ec27997c5437043ec3137cfbd4513154fd4))
* ✏️ 更新文档首页介绍内容 ([3a1a0a4](https://github.com/wot-ui/wot-starter/commit/3a1a0a409effcc730064d1acc1edc64fe58fd7d1))
* ✏️ 添加 PageSpy 远程的教程 ([f29a53c](https://github.com/wot-ui/wot-starter/commit/f29a53cd01cdc80c45e858e409f1564cf7716f73))
* ✏️ add about me ([046897a](https://github.com/wot-ui/wot-starter/commit/046897a862e402fd8ebbd993cac525de5e830f25))
* ✏️ add gitee vote 2025 ([14af7a0](https://github.com/wot-ui/wot-starter/commit/14af7a04cf1e26772782f0cad0d426e220dba40a))
### [1.2.1](https://github.com/wot-ui/wot-starter/compare/v1.2.0...v1.2.1) (2025-12-04)
### 🐛 Bug Fixes | Bug 修复
* **manualTheme:** 修复跟随系统自动切换主题失效的问题 ([380c702](https://github.com/wot-ui/wot-starter/commit/380c7026eeb37a12e9a2866b18bd70880efdfecc))
### ✨ Features | 新功能
* **index:** 首页设置中新增"跟随系统"按钮 ([d031788](https://github.com/wot-ui/wot-starter/commit/d031788d4c9b31a7d030f17856f69f2d177eb1b8))
* **logo:** 更新logo ([550caa2](https://github.com/wot-ui/wot-starter/commit/550caa243e423969745d62c3872dfae14976409a))
### ✏️ Documentation | 文档
* ✏️ 更新 logo ([0e57c45](https://github.com/wot-ui/wot-starter/commit/0e57c45b94284320d5fdc3be6c082faf12ddcbad))
* ✏️ 首页添加 uni-ku 插件入口 ([a5b05c9](https://github.com/wot-ui/wot-starter/commit/a5b05c9091f5396c27c556ed23eed87b7f06fef0))
* ✏️ 文档增加显示版本号 ([10b7078](https://github.com/wot-ui/wot-starter/commit/10b707810529af53bd2a85fca6624807b5b2d9ec))
* ✏️ 移动非主包必需示例页面到分包中 ([#43](https://github.com/wot-ui/wot-starter/issues/43)) ([3d7a076](https://github.com/wot-ui/wot-starter/commit/3d7a07619cf4b84c26a91b5028b2635bcc6d44ff)), closes [#35](https://github.com/wot-ui/wot-starter/issues/35)
* ✏️ update logo ([6b9e4f9](https://github.com/wot-ui/wot-starter/commit/6b9e4f9e66d7be10b1e678b46472c9e44a270fc3))
## [1.2.0](https://github.com/wot-ui/wot-starter/compare/v1.1.0...v1.2.0) (2025-11-26)
### ✨ Features | 新功能
* ✨ 合并模板与文档项目开发便利性优化 ([#41](https://github.com/wot-ui/wot-starter/issues/41)) ([646d215](https://github.com/wot-ui/wot-starter/commit/646d2158c96dcf83518ed22bc27cc8e20f2ed0d2)), closes [#35](https://github.com/wot-ui/wot-starter/issues/35)
* ✨ 支持 esm 并更新 unocss 和 [@uni-helper](https://github.com/uni-helper) 插件 ([#39](https://github.com/wot-ui/wot-starter/issues/39)) ([f433b49](https://github.com/wot-ui/wot-starter/commit/f433b49023c572488254b18584a4dbca0ba66336))
### ✏️ Documentation | 文档
* ✏️ 添加 vite base ([b6f5cf8](https://github.com/wot-ui/wot-starter/commit/b6f5cf83084d0cc04bc94c9714722a7a4c9e6327))
* ✏️ 增加更新日志入口 ([7037d8f](https://github.com/wot-ui/wot-starter/commit/7037d8f10a9aa0274d5cc5d7afc8978342f96f1a))
## [1.1.0](https://github.com/wot-ui/wot-starter/compare/v1.0.0...v1.1.0) (2025-11-12)
### ✨ Features | 新功能
* ✨ 添加 uni_modules 插件引入示例 ([8493761](https://github.com/wot-ui/wot-starter/commit/8493761ad6ea4e6478d3b7764b43b813e5178e86))
* ✨ 支持 harmony next 自定义 tabbar ([f71e8ba](https://github.com/wot-ui/wot-starter/commit/f71e8ba62504a4c0b79d02e61979b52e1f538e59))
## 1.0.0 (2025-10-28)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复分包路由未注册的问题 ([3da843b](https://github.com/wot-ui/wot-starter/commit/3da843ba33bf62d2a8032dabf3061b2ce87e46a9))
### ✏️ Documentation | 文档
* ✏️ 更新 README ([a630274](https://github.com/wot-ui/wot-starter/commit/a63027496f9f75e8106437bf7e4285164a7f91b1))
* ✏️ 更新README ([abde3bc](https://github.com/wot-ui/wot-starter/commit/abde3bca57cbee293d0751dd6f273366425e2474))
* ✏️ 添加分包示例 ([809b65b](https://github.com/wot-ui/wot-starter/commit/809b65b8384029d9ed2c7807709023d65bf6bb4c))
* **README:** ✏️ 更新 vitesse-uni-app 项目链接 ([#11](https://github.com/wot-ui/wot-starter/issues/11)) ([6f1585a](https://github.com/wot-ui/wot-starter/commit/6f1585a6da97a9aeed4071125e6b618f30a50bb7))
### ✨ Features | 新功能
* ✨ 全局反馈组件兼容支付宝小程序 ([7f04d43](https://github.com/wot-ui/wot-starter/commit/7f04d43d44b6eaedabcf32d6c5842b056a0ac8ba))
* ✨ 新增主题切换示例 ([#4](https://github.com/wot-ui/wot-starter/issues/4)) ([c39e756](https://github.com/wot-ui/wot-starter/commit/c39e756821b08ba9934c88f5576d6eabda8fd449))
* ✨ 引入 @uni-ku/root 解决使用 page-meta 和根组件的问题 ([989e9fd](https://github.com/wot-ui/wot-starter/commit/989e9fd05a9c5a3b103608c941e5e30040a19f32))
* ✨ 引入 uni-echarts 支持图表功能,增加分包异步化示例 ([f933a61](https://github.com/wot-ui/wot-starter/commit/f933a6143d5fe02783ade63c669001245970756e))
* ✨ 引入 vite-plugin-uni-pages 的 definePage 宏,优化开发体验 ([498df4f](https://github.com/wot-ui/wot-starter/commit/498df4f26b1a84e8e91827178167ff853ae1f1a9))
* 升级 uni-echarts 版本 ([#17](https://github.com/wot-ui/wot-starter/issues/17)) ([af94964](https://github.com/wot-ui/wot-starter/commit/af9496440e440afae589277c12999644ccfffe3e))
-402
View File
@@ -1,402 +0,0 @@
---
iframeFormatter: pages/index/index
---
# 暗黑模式
## 什么是暗黑模式
暗黑模式(Dark Mode),也被称为夜间模式或深色模式,是一种使用深色背景和浅色文字的界面显示模式。
### 主要特点
- **护眼体验**: 在光线较暗的环境下减少眼部疲劳
- **节省电量**: 在 OLED 屏幕设备上能够显著节省电池消耗
- **视觉美观**: 提供现代化的视觉体验和专业感
## 使用指南
本章节将介绍在 uni-app 项目中实现h5和微信小程序的暗黑模式功能的方案。你可以结合本文内容并参考 uni-app 官方的 [DarkMode 适配指南](https://uniapp.dcloud.net.cn/tutorial/darkmode.html) 来完成适配。
:::tip 提示
App端的暗黑模式适配,请参考 [App 端暗黑模式适配](https://uniapp.dcloud.net.cn/tutorial/darkmode.html#app-plus)完成,本章节暂不涉及。
:::
## 适配组成
暗黑模式的完整适配包含以下几个核心部分:
- **uni-app 平台配置**: 开启 [DarkMode](https://uniapp.dcloud.net.cn/tutorial/darkmode.html) 官方支持
- **UI 组件适配**: [Wot UI](https://wot-ui.cn/component/config-provider.html#%E6%B7%B1%E8%89%B2%E6%A8%A1%E5%BC%8F) 组件库的暗黑模式支持
- **样式系统适配**: [UnoCSS](https://unocss.dev/presets/mini#dark-mode) 暗黑模式工具类
## uni-app 平台配置
uni-app 提供了官方的暗黑模式配置方案,通过 `manifest.json``theme.json` 实现平台级的主题支持,其指南已经比较详细,本章节将简单介绍一下,具体可以参考官方文档。
> 📖 **详细文档**: [uni-app 暗黑模式适配指南](https://uniapp.dcloud.net.cn/tutorial/darkmode.html#get-theme)
### 配置步骤
1. **在 `manifest.config.ts` 中开启暗黑模式**
```json
// H5 配置
"h5": {
"darkmode": true,
"themeLocation": "theme.json"
}
// 微信小程序配置
"mp-weixin": {
"darkmode": true,
"themeLocation": "theme.json"
}
```
2. **创建 `theme.json` 主题变量文件**
```json
{
"light": {
"navBgColor": "#f8f8f8",
"navTxtStyle": "black",
"bgColor": "#ffffff",
"tabBgColor": "#ffffff",
"tabSelectedColor": "#0165FF"
},
"dark": {
"navBgColor": "#000000",
"navTxtStyle": "white",
"bgColor": "#000000",
"tabBgColor": "#1a1a1a",
"tabSelectedColor": "#0165FF"
}
}
```
3. **在 `pages.config.ts` 中引用主题变量**
```json
{
"globalStyle": {
"navigationBarBackgroundColor": "@navBgColor",
"navigationBarTextStyle": "@navTxtStyle",
"backgroundColor": "@bgColor"
},
"tabBar": {
"backgroundColor": "@tabBgColor",
"selectedColor": "@tabSelectedColor"
}
}
```
### 获取当前主题
```javascript
// 获取系统主题信息
const systemInfo = uni.getSystemInfoSync()
console.log('当前主题:', systemInfo.theme) // 'light' 或 'dark'
// 监听主题变化
uni.onThemeChange((res) => {
console.log('主题已切换到:', res.theme)
})
```
### CSS 媒体查询适配
```css
/* 默认样式 */
.some-background {
background: white;
}
/* 暗黑模式样式 */
@media (prefers-color-scheme: dark) {
.some-background {
background: #1b1b1b;
}
}
```
## 主题管理 API
本项目提供了两种主题管理方案,**推荐优先使用自动暗黑模式方案**:
> 📝 **项目说明**: 本演示项目为了展示完整的主题管理功能,默认使用了 `useManualTheme()`。在实际项目中,建议根据需求选择合适的方案,大多数情况下使用 `useTheme()` 即可满足需求。
### 🌙 自动暗黑模式 - `useTheme()` ⭐ 推荐
**适用场景:**
- 大多数应用的首选方案
- 只需要系统主题适应的应用
- 追求简洁和用户体验的应用
**功能特性:**
- ✅ 自动跟随系统主题
- ✅ 导航栏颜色通过 theme.json 自动处理
- ✅ 轻量级,性能优秀
- ✅ 用户体验一致
```vue
<script setup>
import { useTheme } from '@/composables/useTheme'
const { theme, isDark, themeVars } = useTheme()
</script>
<template>
<wd-config-provider :theme="theme" :theme-vars="themeVars">
<view :class="{ 'dark-mode': isDark }">
<text>当前主题: {{ theme }}</text>
</view>
</wd-config-provider>
</template>
```
### 🎨 手动主题管理 - `useManualTheme()`
**适用场景:**
- 需要用户手动控制主题的特殊应用
- 需要主题色自定义功能的应用
- 需要完整主题管理功能的复杂应用
**功能特性:**
- ✅ 手动切换暗黑模式
- ✅ 主题色选择(6种预设颜色)
- ✅ 跟随系统主题
- ✅ 自动同步导航栏颜色
- ✅ 持久化用户设置
> 💡 **建议**:除非有特殊需求,否则推荐使用 `useTheme()` 自动暗黑模式方案,它能提供更好的用户体验和性能表现。
```vue
<script setup>
import { useManualTheme } from '@/composables/useManualTheme'
const {
theme,
isDark,
toggleTheme,
openThemeColorPicker,
currentThemeColor,
themeVars
} = useManualTheme()
</script>
<template>
<wd-config-provider :theme="theme" :theme-vars="themeVars">
<view :class="{ 'dark-mode': isDark }">
<wd-button @click="toggleTheme">
切换主题
</wd-button>
<wd-button @click="openThemeColorPicker">
选择主题色
</wd-button>
</view>
</wd-config-provider>
</template>
```
## UI 组件适配 (Wot UI)
[Wot Design Uni](https://wot-ui.cn/) 组件库原生支持暗黑模式,通过 `wd-config-provider` 组件可以轻松开启全局暗黑模式支持。
### 全局配置
```vue
<!-- App.vue -->
<script setup>
// 根据需求选择合适的主题管理方案
import { useTheme } from '@/composables/useTheme' // 简化版
// 或者
// import { useManualTheme } from '@/composables/useManualTheme' // 完整版
const { theme, themeVars } = useTheme()
</script>
<template>
<wd-config-provider :theme="theme" :theme-vars="themeVars">
<!-- 你的应用内容 -->
</wd-config-provider>
</template>
```
### 组件级配置
```vue
<!-- 单个页面或组件 -->
<template>
<wd-config-provider theme="dark">
<wd-button type="primary">
暗黑模式按钮
</wd-button>
<wd-cell title="暗黑模式单元格" />
</wd-config-provider>
</template>
```
> 📖 **详细文档**: [Wot UI 暗黑模式配置](https://wot-ui.cn/component/config-provider.html#%E6%B7%B1%E8%89%B2%E6%A8%A1%E5%BC%8F)
## 样式系统适配 (UnoCSS)
### UnoCSS dark 前缀
如果你使用 [UnoCSS](https://unocss.dev/presets/mini#dark-mode),可以使用 `dark:` 前缀来实现暗黑模式样式。
```html
<view class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
内容区域
</view>
```
### 样式适配方案
```html
<!-- 使用 UnoCSS dark 前缀 -->
<view class="bg-white dark:bg-[var(--wot-dark-background)]
text-gray-800 dark:text-[var(--wot-dark-color)]">
自动适配暗黑模式的内容
</view>
<!-- 使用主题变量 -->
<view class="bg-[var(--wot-bg-color)] text-[var(--wot-text-color)]">
使用主题变量的内容
</view>
```
### 最佳实践
```html
<!-- ✅ 推荐:结合官方配置和自定义样式 -->
<view class="bg-white dark:bg-[var(--wot-dark-background2)]">
<!-- ❌ 不推荐:硬编码颜色 -->
<view style="background: #000; color: #fff;">
```
## 移除暗黑模式功能
如果你的项目不需要暗黑模式功能,可以按照以下步骤完全移除相关代码:
### 1. 移除官方配置
```json
// src/manifest.config.ts
{
"h5": {
// "darkmode": true, // 删除这行
// "themeLocation": "theme.json" // 删除这行
},
"mp-weixin": {
// "darkmode": true, // 删除这行
// "themeLocation": "theme.json" // 删除这行
}
}
```
### 2. 删除主题配置文件
```bash
# 删除主题配置文件
rm src/theme.json
```
### 3. 简化主题管理逻辑
```typescript
// src/composables/useTheme.ts - 简化版本(仅保留系统主题跟随)
export function useTheme() {
const currentThemeColor = ref(themeColorOptions[0])
// 只保留主题色功能,移除明暗模式切换逻辑
function selectThemeColor(option: ThemeColorOption) {
currentThemeColor.value = option
}
return {
currentThemeColor: computed(() => currentThemeColor.value),
themeColorOptions,
selectThemeColor,
}
}
// 如果需要完全移除主题功能,删除以下文件:
// - src/composables/useTheme.ts
// - src/composables/useManualTheme.ts
// - src/store/themeStore.ts
// - src/store/manualThemeStore.ts
```
### 4. 清理样式代码
```css
/* 移除暗黑模式相关样式 */
.container {
background: white;
color: black;
/* 移除:dark:bg-gray-900 dark:text-white */
}
/* 移除媒体查询 */
/*
@media (prefers-color-scheme: dark) {
.container {
background: #1a1a1a;
color: white;
}
}
*/
```
### 5. 更新组件使用
```vue
<script setup>
// 移除主题相关的响应式数据
// const { theme, isDark } = useTheme() // 删除这行
// 移除主题监听
// watch(theme, (newTheme) => { ... }) // 删除这块
</script>
<template>
<!-- 移除暗黑模式相关的条件渲染和样式 -->
<view class="bg-white text-gray-900">
<!-- 移除dark:bg-gray-900 dark:text-white -->
内容区域
</view>
</template>
```
### 6. 完全移除(可选)
如果要完全移除主题功能:
```bash
# 删除主题相关文件
rm src/composables/useTheme.ts
rm src/composables/useManualTheme.ts
rm src/store/themeStore.ts
rm src/store/manualThemeStore.ts
# 从 pages.json 中移除主题变量引用
# 将 @navBgColor 等变量替换为具体颜色值
```
```json
// pages.json - 替换主题变量
{
"globalStyle": {
"navigationBarBackgroundColor": "#ffffff", // 替换 @navBgColor
"navigationBarTextStyle": "black", // 替换 @navTxtStyle
"backgroundColor": "#f8f8f8" // 替换 @bgColor
}
}
```
移除后,你的应用将只使用固定的明亮主题,减少代码复杂度和包体积。
> 📖 **了解更多**: [uni-app 暗黑模式适配指南](https://uniapp.dcloud.net.cn/tutorial/darkmode.html)
-40
View File
@@ -1,40 +0,0 @@
# 部署
对于 uni-app 来说,部署即打包和发行。
## Web
使用下面的命令来打包:
```bash
pnpm build:h5
```
产物位于 `dist/build/h5`, 就像传统 SPA 一样部署即可。
## 小程序
以微信小程序为例,使用下面的命令来打包:
```bash
pnpm build:mp-weixin
```
产物位于 `dist/build/mp-weixin`, 使用微信开发者工具上传即可。
::: tip
如果想自动上传到微信小程序,可直接使用 [uni-mini-ci](https://www.npmjs.com/package/uni-mini-ci),或参考 [这篇文章](https://juejin.cn/post/7272316909051346959) 自行配置。
:::
要发行其他小程序,执行 `pnpm build:mp-<platform>`打包,并使用对应开发者工具上传即可,具体可查看 `package.json``scripts` 部分。
## APP
### 离线打包
- [android](https://nativesupport.dcloud.net.cn/AppDocs/usesdk/android.html)
- [ios](https://nativesupport.dcloud.net.cn/AppDocs/usesdk/ios.html)
::: warning
你仍然可以使用 HBuilderX 提供的“安心”打包功能,但是由于这种方式强依赖 HBuilderX,故不做推荐。
:::
-441
View File
@@ -1,441 +0,0 @@
---
title: 全局反馈组件
iframe: true
iframeFormatter: subPages/feedback/index
---
# 全局反馈组件
本项目基于 Pinia 和 Wot UI 封装了三类可全局调用的反馈能力:GlobalLoading、GlobalToast、GlobalDialog。它们适合在网络请求中间件、路由导航守卫以及其他不方便直接依赖页面内 hook 实例的场景中使用。
:::tip 提示
Wot UI 原生提供了 useToast、useDialog 等函数式能力,但调用侧通常仍要依赖对应组件实例。这里的全局反馈组件通过 Pinia 保存状态,并在组件内部监听状态后再调用 wd-toast 或 wd-dialog,因此可以把触发逻辑放到更靠近业务流程的位置,例如请求拦截器和路由守卫。
:::
## 使用前提
使用这套全局反馈封装前,需要保证页面树中已经挂载以下组件实例:
- GlobalLoading
- GlobalToast
- GlobalDialog
本项目通过组件自动导入支持直接使用这些组件;业务侧只需要调用对应的 composable
```vue
<template>
<GlobalLoading />
<GlobalToast />
<GlobalDialog />
<slot />
</template>
```
三个组件都会记录触发时所在页面路径,并且仅在相同页面内展示,避免切页后在错误页面继续显示旧反馈。
## 全局加载
### 概述
GlobalLoading 基于 Wot UI 的 wd-toast 封装,适合配合 axios、alova 等请求流程显示全局加载状态。
### 组件特性
- 基于 wd-toast 实现
- 默认不自动关闭
- 默认显示遮罩,防止重复操作
- 自动记录当前页面路径,只在触发页面展示
### 使用
```ts
import { useGlobalLoading } from '@/composables/useGlobalLoading'
const loading = useGlobalLoading()
```
### API
#### loading(option)
显示加载状态。
```ts
loading.loading('加载中...')
loading.loading({
msg: '数据加载中',
cover: true,
})
```
默认会合并以下配置:
- iconName: loading
- duration: 0
- cover: true
- position: middle
- show: true
#### close()
关闭加载状态。
```ts
loading.close()
```
### 参数说明
#### ToastOptions
| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| msg | string | - | 加载文案 |
| iconName | string | loading | 图标名称 |
| duration | number | 0 | 持续时间,0 表示不自动关闭 |
| cover | boolean | true | 是否显示遮罩 |
| position | string | middle | 显示位置:top \| middle \| bottom |
| show | boolean | true | 是否显示,内部状态字段 |
#### 参数形式
支持两种调用方式:
1. 字符串:作为 msg 使用
2. 对象:传入完整 ToastOptions
### 示例
```ts
const { loading, close } = useGlobalLoading()
async function fetchData() {
try {
loading('正在加载数据...')
await api.getData()
}
finally {
close()
}
}
```
## 全局提示
### 概述
GlobalToast 基于 Wot UI 的 wd-toast 封装,提供统一的全局轻提示能力。
### 组件特性
- 基于 wd-toast 实现
- 支持 success、error、info、warning 四种快捷调用
- 支持自定义位置、时长、图标和遮罩
- 自动记录当前页面路径,只在触发页面展示
### 使用
```ts
import { useGlobalToast } from '@/composables/useGlobalToast'
const toast = useGlobalToast()
```
### API
#### show(option)
显示普通提示。
```ts
toast.show('这是一条提示信息')
toast.show({
msg: '自定义提示',
duration: 3000,
position: 'top',
})
```
默认配置:
- duration: 2000
- show: false
- 调用时自动补齐 show: true
- 未传 position 时默认使用 middle
#### success(option)
成功提示,默认附带 success 图标,duration 为 1500。
```ts
toast.success('操作成功')
```
#### error(option)
错误提示,默认附带 error 图标,direction 为 vertical。
```ts
toast.error('操作失败')
```
#### info(option)
信息提示,默认附带 info 图标。
```ts
toast.info('这是一条信息')
```
#### warning(option)
警告提示,默认附带 warning 图标。
```ts
toast.warning('警告信息')
```
#### close()
手动关闭当前提示。
```ts
toast.close()
```
### 参数说明
#### ToastOptions
| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| msg | string | - | 提示内容 |
| duration | number | 2000 | 持续时间,0表示不自动关闭 |
| position | string | middle | 显示位置:top \| middle \| bottom |
| iconName | string | - | 图标名称 |
| direction | string | - | 布局方向:horizontal \| vertical |
| cover | boolean | false | 是否显示遮罩 |
| show | boolean | true | 是否显示,内部状态字段 |
#### 参数形式
所有方法都支持两种调用方式:
1. 字符串:作为 msg 使用
2. 对象:传入完整 ToastOptions
## 全局弹窗
### 概述
GlobalDialog 基于 Wot UI 的 wd-dialog 封装,用于统一处理提醒、确认和输入等交互流程。
### 组件特性
- 基于 wd-dialog 实现
- 支持 alert、confirm、prompt 三种模式
- 支持 success、fail 回调
- 自动记录当前页面路径,只在触发页面展示
- 内部统一设置取消按钮和确认按钮为非圆角
### 使用
```ts
import { useGlobalDialog } from '@/composables/useGlobalDialog'
const dialog = useGlobalDialog()
```
### API
#### show(option)
显示通用弹窗。
```ts
dialog.show({
title: '提示',
msg: '这是一条消息',
success: (res) => console.log('成功', res),
fail: (res) => console.log('失败', res),
})
dialog.show('简单提示')
```
注意:当参数为字符串时,实际会作为 title 使用。
#### alert(option)
显示提醒弹窗,只显示确认按钮。
```ts
dialog.alert('操作完成')
dialog.alert({
title: '提醒',
msg: '请注意查看结果',
})
```
#### confirm(option)
显示确认弹窗,自动开启取消按钮。
```ts
dialog.confirm('确定要删除吗?')
dialog.confirm({
title: '确认删除',
msg: '删除后不可恢复,确定要删除吗?',
success: (res) => {
if (res.action === 'confirm') {
console.log('用户确认删除')
}
},
fail: (res) => {
console.log('用户取消删除')
},
})
```
#### prompt(option)
显示输入弹窗,自动开启取消按钮。
```ts
dialog.prompt('请输入您的姓名')
dialog.prompt({
title: '输入信息',
msg: '请输入新的名称',
inputValue: '默认值',
inputPlaceholder: '请输入内容',
success: (res) => {
if (res.action === 'confirm') {
console.log('用户输入:', res.value)
}
},
})
```
#### close()
手动关闭弹窗。
```ts
dialog.close()
```
### 参数说明
#### GlobalDialogOptions
GlobalDialogOptions 基于 Wot UI 的 DialogOptions 扩展,并额外支持 success、fail 回调。
| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| title | string | - | 弹窗标题 |
| msg | string | - | 弹窗内容 |
| type | string | - | 弹窗类型:alert \| confirm \| prompt |
| showCancelButton | boolean | 自动设置 | alert 为 falseconfirm 和 prompt 为 true |
| inputValue | string | - | prompt 模式下的输入框默认值 |
| inputPlaceholder | string | - | prompt 模式下的输入框占位文案 |
| success | Function | - | 点击确认后的回调 |
| fail | Function | - | 取消或关闭后的回调 |
| confirmButtonText | string | 取决于 wd-dialog 默认值 | 确认按钮文本 |
| cancelButtonText | string | 取决于 wd-dialog 默认值 | 取消按钮文本 |
#### DialogResult
回调参数会透传 wd-dialog 的结果对象,常用字段如下:
| 参数 | 类型 | 说明 |
|------|------|------|
| action | string | 用户操作:confirm \| cancel |
| value | string | 输入框的值,仅 prompt 模式可用 |
#### 参数形式
所有方法都支持两种调用方式:
1. 字符串:作为 title 使用
2. 对象:传入完整 GlobalDialogOptions
### 示例
```ts
const { confirm, alert, prompt } = useGlobalDialog()
const { success, warning } = useGlobalToast()
alert({
title: '重要提醒',
msg: '这是一个重要提醒',
})
confirm({
title: '确认操作',
msg: '确定继续吗?',
success: (res) => {
if (res.action === 'confirm') {
success('继续执行')
}
},
})
prompt({
title: '输入信息',
msg: '请输入您的姓名',
success: (res) => {
if (res.action === 'confirm' && String(res.value || '').trim()) {
success(`您好,${res.value}`)
}
else {
warning('输入不能为空')
}
},
})
```
## 典型场景
### 请求中间件中显示全局加载
```ts
const globalLoading = useGlobalLoading()
globalLoading.loading('请求中...')
try {
await request()
}
finally {
globalLoading.close()
}
```
### 路由守卫中显示确认弹窗
```ts
const { confirm } = useGlobalDialog()
confirm({
title: '离开当前页面',
msg: '表单尚未保存,确定离开吗?',
success: (res) => {
if (res.action === 'confirm') {
// 继续跳转
}
},
})
```
## 注意事项
1. GlobalLoading 和 GlobalToast 底层都调用 wd-toast,但使用了不同 selector,不会互相覆盖。
2. GlobalDialog 使用 wd-dialog,内容字段应使用 msg,而不是 message。
3. 三个 composable 都会记录 currentPage,仅在触发时所在页面展示反馈。
4. 使用 duration: 0 的提示或加载时,需要手动调用 close()。
5. 支付宝小程序场景下组件内部做了兼容处理,业务侧无需额外处理。
-650
View File
@@ -1,650 +0,0 @@
# 国际化
本章节将系统化地「拆解」uni-app 项目中的国际化实现流程,从环境搭建到实战应用,手把手教你使用 [vue-i18n](https://vue-i18n.intlify.dev/) 结合vscode插件 [i18n-ally](https://github.com/lokalise/i18n-ally) 与 [Wot UI V2](https://github.com/wot-ui/wot-ui) 构建灵活高效的多语言支持系统,包含了各类平台(H5、小程序、App)的适配要点与优化策略。准备好了吗?Let's go! 不对,应该是「出发吧」...哦等等,这不就是国际化的意义所在吗?😉
> 注意:本项目是基于 `Vue3` 和 `WotUI` 的 uni-app cli 框架开发的,如果你使用的是 Vue2,请参考 uni-app [文档](https://uniapp.dcloud.net.cn/tutorial/i18n.html#vue%E7%95%8C%E9%9D%A2%E5%92%8Cjs%E5%86%85%E5%AE%B9%E7%9A%84%E5%9B%BD%E9%99%85%E5%8C%96) 进行相应调整。
## 1. 安装和配置vue-i18n
### 1.1 安装依赖
首先,我们需要安装vue-i18n
```bash
# 使用npm
npm install vue-i18n@9.1.9
# 或者使用yarn
yarn add vue-i18n@9.1.9
# 或者使用pnpm
pnpm add vue-i18n@9.1.9
```
> 注意:根据[uni-app官方文档](https://link.juejin.cn?target=https%3A%2F%2Funiapp.dcloud.net.cn%2Ftutorial%2Fi18n.html%23vue%25E7%2595%258C%25E9%259D%25A2%25E5%2592%258Cjs%25E5%2586%2585%25E5%25AE%25B9%25E7%259A%2584%25E5%259B%25BD%25E9%2599%2585%25E5%258C%2596 "https://uniapp.dcloud.net.cn/tutorial/i18n.html#vue%E7%95%8C%E9%9D%A2%E5%92%8Cjs%E5%86%85%E5%AE%B9%E7%9A%84%E5%9B%BD%E9%99%85%E5%8C%96")建议,Vue3项目需要安装vue-i18n的固定版本9.1.9,和uni-app内部使用的vue-i18n保持一致。
### 1.2 创建i18n实例
在项目中创建一个专门的目录来存放国际化相关的文件,例如`src/locale`
```typescript
// src/locale/index.ts
import { createI18n } from 'vue-i18n'
import zhCN from './zh-CN.json'
import enUS from './en-US.json'
import Locale from '@wot-ui/locale/locale'
import WotEnUS from '@wot-ui/locale/locale/lang/en-US'
Locale.add({ 'en-US': WotEnUS })
const messages = {
'zh-CN': {
...zhCN
},
'en-US': {
...enUS
}
}
// 创建i18n实例
const i18n = createI18n({
locale: uni.getStorageSync('currentLang') || 'zh-CN', // 默认语言
fallbackLocale: 'zh-CN', // 回退语言
messages, // 语言包
legacy: false // 启用Composition API模式
})
// 同步组件库语言
Locale.use(i18n.global.locale.value)
uni.setLocale(i18n.global.locale.value)
export default i18n
```
### 1.3 在main.ts中注册i18n
```typescript
// src/main.ts
import { createApp } from 'vue'
import App from './App.vue'
import i18n from './locale'
const app = createApp(App)
app.use(i18n)
app.mount('#app')
```
## 2\. 语言文件的组织结构
### 2.1 基本结构
语言文件通常以JSON格式存储,每种语言一个文件:
```bash
src/locale/
├── index.ts # i18n配置和实例
├── zh-CN.json # 中文语言包
└── en-US.json # 英文语言包
```
### 2.2 语言文件内容
语言文件是键值对的集合,键是唯一标识符,值是对应语言的文本:
```json
// zh-CN.json
{
"hello": "你好",
"welcome": "欢迎使用",
"button": "按钮"
}
// en-US.json
{
"hello": "Hello",
"welcome": "Welcome to use",
"button": "Button"
}
```
### 2.3 嵌套结构
对于复杂应用,可以使用嵌套结构组织语言文件:
```json
// zh-CN.json
{
"common": {
"confirm": "确认",
"cancel": "取消"
},
"home": {
"title": "首页",
"welcome": "欢迎回来"
}
}
```
## 3\. 使用useI18nSync钩子实现多语言切换
在项目中,我们实现了一个`useI18nSync`钩子来同步应用和组件库的语言设置:
```typescript
// src/hooks/useI18nSync.ts
import { computed, onBeforeMount } from 'vue'
import { Locale } from '@wot-ui/locale/locale'
import i18n from '../locale'
const SUPPORTED_LOCALES = [
'zh-CN',
'en-US',
]
function setLocale(locale: string, syncComponentLib: boolean = true) {
if (!SUPPORTED_LOCALES.includes(locale)) {
console.warn(`不支持的语言: ${locale},将使用默认语言 zh-CN`)
locale = 'zh-CN'
}
uni.setLocale(locale)
i18n.global.locale.value = locale
uni.setStorageSync('currentLang', locale)
if (syncComponentLib) {
Locale.use(locale)
}
return locale
}
function initLocale(defaultLocale: string, syncComponentLib: boolean) {
const storedLocale = uni.getStorageSync('currentLang') || defaultLocale
setLocale(storedLocale, syncComponentLib)
}
interface I18nSyncOptions {
/** 是否同步组件库语言设置 */
syncComponentLib?: boolean
/** 默认语言 */
defaultLocale?: string
}
/**
* 国际化同步hook
* @param options 配置选项
* @returns 国际化相关方法和状态
*/
export function useI18nSync(options?: I18nSyncOptions) {
const { syncComponentLib = true, defaultLocale = 'zh-CN' } = options || {}
const currentLang = computed(() => i18n.global.locale.value)
onBeforeMount(() => {
initLocale(defaultLocale, syncComponentLib)
})
return {
currentLang,
setLocale: (locale: string) => setLocale(locale, syncComponentLib),
supportedLocales: SUPPORTED_LOCALES
}
}
```
### 3.1 在App.vue中初始化语言
```vue
<script setup lang="ts">
import { useI18nSync } from './hooks/useI18nSync'
// 初始化国际化设置
const { currentLang, setLocale } = useI18nSync()
</script>
```
### 3.2 实现语言切换功能
```vue
<template>
<view class="language-switcher">
<view class="current-lang">{{ $t('dangQianYuYan') }}: {{ currentLang }}</view>
<wd-button @click="switchLanguage('zh-CN')">中文</wd-button>
<wd-button @click="switchLanguage('en-US')">English</wd-button>
</view>
</template>
<script setup lang="ts">
import { useI18nSync } from '../hooks/useI18nSync'
const { currentLang, setLocale } = useI18nSync()
function switchLanguage(locale: string) {
setLocale(locale)
}
</script>
```
## 4\. 在组件中使用国际化文本
### 4.1 使用Composition API
在Vue3的Composition API中使用i18n
```vue
<template>
<view class="page">
<view class="title">{{ t('hello') }}</view>
<view class="content">{{ t('welcome') }}</view>
<wd-button>{{ t('button') }}</wd-button>
</view>
</template>
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
</script>
```
### 4.2 使用模板语法
直接在模板中使用$t函数:
```vue
<template>
<view class="page">
<view class="title">{{ $t('hello') }}</view>
<view class="content">{{ $t('welcome') }}</view>
<wd-button>{{ $t('button') }}</wd-button>
</view>
</template>
```
### 4.3 动态计算属性
使用computed使内容响应语言变化:
```vue
<script setup lang="ts">
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
// 使用computed使list响应语言变化
const list = computed(() => [
{
id: 'widget',
name: t('ji-chu'),
pages: [
{
id: 'button',
name: t('button-an-niu')
},
// 其他项...
]
}
])
</script>
```
## 5\. 处理动态内容的国际化
### 5.1 平台限制与解决方案
根据uni-app官方文档,由于运行平台限制,**目前在小程序和App端不支持插值方式定义国际化**。这意味着以下方式在小程序和App端无法正常工作:
```typescript
// 这种方式在小程序和App端不支持
t('hello', { name: '小明' }) // 使用命名参数
t('hello', ['小明']) // 使用数组参数
```
为了解决这个问题,我们需要使用自定义的插值方法来处理带参数的翻译。
### 5.2 带参数的翻译
在小程序和App端,我们需要使用特殊的占位符格式和自定义插值方法来处理包含变量的文本:
```typescript
// 在语言文件中定义带占位符的文本
// zh-CN.json
{
"greeting": "你好,{0}",
"welcome": "欢迎{0}来到{1}"
}
// 使用时传入参数
t('greeting', ['小明'])
// 输出:你好,小明!
t('welcome', ['小明', 'wot-ui'])
// 输出:欢迎小明来到 wot-ui
```
> 注意:我们使用`{0}`, `{1}`这样的数字索引占位符,而不是使用命名参数如`{name}`。这是因为小程序和App端不支持命名参数的插值方式。
### 5.3 实现插值工具函数
```typescript
// src/locale/utils.ts
/**
* 替换字符串中的占位符
* @param template 模板字符串,如 "Hello {0}, welcome to {1}"
* @param values 要替换的值数组
* @returns 替换后的字符串
*/
export function interpolateTemplate(template: string, values: any[]): string {
return template.replace(/{(\d+)}/g, (_, index) => values[index] ?? '')
}
```
### 5.4 扩展t函数支持数组参数
由于小程序和App端的限制,我们需要扩展vue-i18n的t函数,使其能够处理数组参数并应用我们的插值方法:
```typescript
// src/locale/index.ts
import { createI18n } from 'vue-i18n'
import zhCN from './zh-CN.json'
import enUS from './en-US.json'
import { interpolateTemplate } from './utils'
// 创建i18n实例
const i18n = createI18n({
locale: 'zh-CN',
fallbackLocale: 'zh-CN',
messages: {
'zh-CN': zhCN,
'en-US': enUS
},
legacy: false
})
// 扩展t函数,支持数组参数插值
// 这是解决小程序和App端不支持插值方式的关键步骤
const originalT = i18n.global.t
i18n.global.t = ((key: string | number, param1?: any, param2?: any) => {
const result = originalT(key, param1, param2)
// 检测是否传入了数组参数,如果是则使用我们的插值方法处理
if (Array.isArray(param1)) {
return interpolateTemplate(result, param1)
}
return result
}) as typeof i18n.global.t
export default i18n
```
这种扩展方式的优点是:
1. 保持了与vue-i18n原有API的兼容性
2. 在小程序和App端也能使用类似的参数传递方式
3. 统一了不同平台的国际化使用体验
> 需要注意的是我们仅实现了数组参数的插值,如果需要支持更多参数类型,可以进一步扩展。
### 5.5 使用示例
```vue
<template>
<!-- 在模板中使用 -->
<view>{{ $t('greeting', [username]) }}</view>
<view>{{ $t('welcome', [username, '@wot-ui/ui']) }}</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const username = ref('小明')
// 在JS/TS代码中使用
const message = t('greeting', [username.value])
</script>
```
> 注意:这种方法在H5、App和小程序等所有平台上都能正常工作,因为我们使用了自定义的插值方法来处理参数,绕过了小程序和App端的限制。
## 6\. 组件库的国际化
WotUI组件库本身支持国际化,我们只需要参考[国际化](https://wot-ui.cn/guide/locale.html)文档进行配置即可。
### 6.1 同步应用和组件库的语言
使用`useI18nSync`钩子可以同步应用和组件库的语言设置:
```typescript
// 同步组件库语言设置
if (syncComponentLib) {
Locale.use(locale)
}
```
## 7\. pages.json的国际化
> 注意:建议仔细阅读uni-app[国际化文档](https://link.juejin.cn?target=https%3A%2F%2Funiapp.dcloud.net.cn%2Ftutorial%2Fi18n.html%23vue%25E7%2595%258C%25E9%259D%25A2%25E5%2592%258Cjs%25E5%2586%2585%25E5%25AE%25B9%25E7%259A%2584%25E5%259B%25BD%25E9%2599%2585%25E5%258C%2596 "https://uniapp.dcloud.net.cn/tutorial/i18n.html#vue%E7%95%8C%E9%9D%A2%E5%92%8Cjs%E5%86%85%E5%AE%B9%E7%9A%84%E5%9B%BD%E9%99%85%E5%8C%96")。
### 7.1 页面标题国际化
在uni-app中,pages.json中的页面标题可以通过占位符实现国际化:
```json
// pages.json
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "%index-title%"
}
}
]
}
```
### 7.2 语言文件配置
在语言文件中添加对应的翻译:
```json
// zh-CN.json
{
"index-title": "首页"
}
// en-US.json
{
"index-title": "Home"
}
```
### 7.3 平台差异处理
- **H5平台**:直接支持占位符方式
- **其他平台**:可以使用自定义Tabbar和Navbar实现
> 注意:小程序下不支持这种国际化方案,也可以使用设置tabbar和navigationbar的API来设置文字。或者废弃原生tabbar和navigationbar,使用自定义方式,详情参考[uni-app官方文档](https://link.juejin.cn?target=https%3A%2F%2Funiapp.dcloud.net.cn%2Ftutorial%2Fi18n.html%23pages "https://uniapp.dcloud.net.cn/tutorial/i18n.html#pages")
## 8\. i18n-ally插件
### 8.1 插件概述
i18n-ally是VSCode的一款国际化插件,提供以下核心功能:
- 自动检测项目中的硬编码文本
- 一键提取待翻译内容
- 多语言文件管理
- 翻译辅助工具集成
### 8.2 安装与配置
1. **安装插件**
- 在VSCode扩展市场搜索并安装`i18n-ally`
- 安装完成后重启VSCode
2. **基本配置**
- 在项目根目录创建`.vscode/settings.json`文件
- 添加以下基础配置:
```json
// .vscode/settings.json
{
"i18n-ally.sourceLanguage": "zh-CN", // 项目的​​原始语言(基准语言)​​,通常是开发时使用的默认语言(如 en 或 zh-CN)。
"i18n-ally.displayLanguage": "zh-CN", // 插件​​界面中显示的语言​​(如悬浮提示、侧边栏预览等),方便开发者查看翻译结果。
"i18n-ally.localesPaths": ["src/locale"],
"i18n-ally.keystyle": "flat",
"i18n-ally.sortKeys": true
}
```
![i18n-ally 设置示例](https://blog.wot-ui.cn/images/image_1_c3b9d87b.png)
### 8.3 核心功能详解
#### 8.3.1 自动检测
- 扫描范围:HTML标签内容、Vue模板、JS/TS代码
- 支持配置检测规则:
```json
"i18n-ally.extract.parsers.html": {
"attributes": ["text", "title", "alt", "placeholder", "label", "aria-label"],
"ignoredTags": ["script", "style"],
"vBind": true,
"inlineText": true
}
```
![i18n-ally 检测规则示例](https://blog.wot-ui.cn/images/image_2_0c8f8aa3.png)
#### 8.3.2 一键提取
- 自动生成翻译键
- 保持语言文件结构一致
- 支持批量处理
![i18n-ally 一键提取示例](https://blog.wot-ui.cn/images/image_3_00d5556e.png)
> 注意:提取的翻译键会自动添加到语言文件中,无需手动添加,但是批量提取的翻译键值会丢失插值参数,例如:`哈哈哈${232}`应当生成为`t('hahaha', [232])`,但实际上生成的是`t('ha-ha-ha-232-0')`,可以选择手动添加插值参数,或者结合[8.4.1 重构模板](#841-重构模板)进行优化。
#### 8.3.3 翻译辅助
i18n-ally内置翻译API支持,这里我们使用百度翻译API作为示例:
1. **注册账号**:访问[百度翻译开放平台](https://link.juejin.cn?target=https%3A%2F%2Ffanyi-api.baidu.com%2F "https://fanyi-api.baidu.com/")注册账号
2. **创建应用**:获取APP ID和密钥
3. **配置插件**
```json
"i18n-ally.translate.engines": ["baidu"],
"i18n-ally.translate.baidu.appid": "YOUR_APP_ID",
"i18n-ally.translate.baidu.key": "YOUR_SECRET_KEY"
```
![i18n-ally 翻译配置示例](https://blog.wot-ui.cn/images/image_4_b160ce47.png)
### 8.4 高级配置
#### 8.4.1 重构模板
```json
"i18n-ally.refactor.templates": [
{
"source": "html-inline",
"template": "{{ $t('{key}'{args}) }}"
},
{
"source": "html-attribute",
"template": "$t('{key}'{args})"
}
]
```
重构模板后,提取的翻译键会自动添加插值参数,例如:`哈哈哈${232}${111}`会生成为携带插值参数的格式`t('hahaha', 232, 111)`,不过`vue-i18n``t`方法不支持这种格式,所以我们需要再展t函数,支持可变参数,并将其作为数组参数插值。
```typescript
// src/locale/index.ts
import { createI18n } from 'vue-i18n'
import zhCN from './zh-CN.json'
import enUS from './en-US.json'
import Locale from '@wot-ui/locale/locale'
import WotEnUS from '@wot-ui/locale/locale/lang/en-US'
Locale.add({ 'en-US': WotEnUS })
const messages = {
'zh-CN': {
...zhCN
},
'en-US': {
...enUS
}
}
// 创建i18n实例
const i18n = createI18n({
locale: uni.getStorageSync('currentLang') || 'zh-CN',
fallbackLocale: 'zh-CN',
messages,
legacy: false
})
Locale.use(i18n.global.locale.value)
uni.setLocale(i18n.global.locale.value)
const originalT = i18n.global.t
i18n.global.t = ((key: string | number, ...args: any[]) => {
/**
* 替换字符串中的占位符
* @param template 模板字符串,如 "Hello {0}, welcome to {1}"
* @param values 要替换的值数组
* @returns 替换后的字符串
*/
function interpolateTemplate(template: string, values: any[]): string {
return template.replace(/{(\d+)}/g, (_, index) => values[index] ?? '')
}
// 处理对象参数场景: t(key, {key1: value1, key2: value2})
if (args.length === 1 && typeof args[0] === 'object' && !Array.isArray(args[0])) {
const result = originalT(key, ...args)
return result
}
// 处理数组参数场景: t(key, [arg1, arg2])
if (args.length === 1 && Array.isArray(args[0])) {
const result = originalT(key, args[0])
return interpolateTemplate(result, args[0])
}
// 处理可变参数场景: t(key, arg1, arg2, ...)
if (args.length > 1 && args.every((arg) => typeof arg !== 'object')) {
return interpolateTemplate(originalT(key, args), args)
}
// 处理默认场景: t(key) 或 t(key, defaultMessage) 或 t(key, plural) 等
const result = originalT(key, ...args)
return result
}) as typeof i18n.global.t
export default i18n
```
#### 8.4.2 忽略规则
```json
"i18n-ally.extract.ignored": [
"特定文本",
"正则表达式",
]
```
## 总结
至此,我们完成了国际化配置,并使用 `i18n-ally` 插件实现了对 Vue 组件的自动提取和翻译。你可以按照本章节在 wot-starter 中集成并实现国际化功能。
## 参考资料
- [Vue i18n官方文档](https://vue-i18n.intlify.dev/)
- [uni-app官方文档](https://uniapp.dcloud.net.cn/tutorial/i18n.html#vue%E7%95%8C%E9%9D%A2%E5%92%8Cjs%E5%86%85%E5%AE%B9%E7%9A%84%E5%9B%BD%E9%99%85%E5%8C%96)
- [wot-ui官方文档](https://wot-ui.cn/)
- [i18n-ally官方文档](https://github.com/lokalise/i18n-ally)
-64
View File
@@ -1,64 +0,0 @@
---
title: 图标使用
iframe: true
iframeFormatter: subPages/icon/index
---
# 图标使用
你可以使用多种方案来在项目中使用图标。一般情况下,可以直接使用 UI 组件库内置的图标,也可以使用更加灵活的 Iconify 图标集,以下是我们推荐的一些实践方案。
## WotUI 内置图标
最简单的图标使用方式是使用 [WotUI](https://wot-ui.cn/component/icon.html) 的内置图标。
```html
<!-- 基础用法 -->
<wd-icon name="star" size="20px" color="#f59e0b" />
<!-- 在按钮中使用 -->
<wd-button icon="add" type="primary">添加</wd-button>
<!-- 主题色适配 -->
<wd-icon name="home" size="24px" color="var(--wot-color-theme)" />
```
## Iconify 图标集
如果你需要更多图标选择,可以使用 [Iconify](https://iconify.design/) 图标集配合 [UnoCSS](https://unocss.dev/) 使用,我们已经默认集成了 [Carbon](https://icones.js.org/collection/carbon) 图标集,也可以在 [Icones](https://icones.js.org/) 中搜索你需要的图标。
```html
<!-- 基础用法 -->
<text class="i-carbon:star text-xl text-yellow-500"></text>
<!-- 响应式大小 -->
<text class="i-carbon:home text-sm md:text-lg lg:text-xl"></text>
<!-- 暗黑模式适配 -->
<text class="i-carbon:favorite text-gray-600 dark:text-white"></text>
```
### 常用 Carbon 图标
```html
<!-- 系统图标 -->
<div class="i-carbon:add"></div> <!-- 添加 -->
<div class="i-carbon:close"></div> <!-- 关闭 -->
<div class="i-carbon:checkmark"></div> <!-- 确认 -->
<div class="i-carbon:arrow-right"></div> <!-- 右箭头 -->
<!-- 功能图标 -->
<div class="i-carbon:home"></div> <!-- 首页 -->
<div class="i-carbon:search"></div> <!-- 搜索 -->
<div class="i-carbon:user"></div> <!-- 用户 -->
<div class="i-carbon:settings"></div> <!-- 设置 -->
<!-- 状态图标 -->
<div class="i-carbon:star"></div> <!-- 星级 -->
<div class="i-carbon:favorite"></div> <!-- 收藏 -->
<div class="i-carbon:warning"></div> <!-- 警告 -->
<div class="i-carbon:error"></div> <!-- 错误 -->
```
> 📖 **了解更多**: [Carbon 图标集](https://icones.js.org/collection/carbon) | [Iconify 官网](https://iconify.design/) | [Icones](https://icones.js.org/)
-128
View File
@@ -1,128 +0,0 @@
---
title: 起步
iframe: true
iframeFormatter: ''
---
# 起步
你可以直接使用在线编辑器快速试用,或者使用终端在本地开始使用。
## 在线试用
您可以使用在线编辑器在浏览器中开始试用:
- [StackBlitz](https://stackblitz.com/github/wot-ui/wot-starter)
- [GitHub Template](https://github.com/wot-ui/wot-starter/generate)
## 本地使用
### 前置依赖
- **Node.js** - `>= 20.19.0 || >= 22.12.0 || >= 24.0.0`
- **文本编辑器** - 推荐使用 [VS Code](https://code.visualstudio.com/) 并使用 [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) 插件
- **终端** - 为了运行 `uni` 命令,Windows 推荐 Git BashLinux 和 macOS 推荐 zsh
::: details 最佳实践
- **Node.js**: 总是使用偶数版本 (即 [LTS 版本](https://nodejs.org/en/about/previous-releases),例如 20, 22)
- **Volar**: 推荐启用 [接管模式 takeover mode](https://cn.vuejs.org/guide/typescript/overview#volar-takeover-mode)
:::
打开终端,然后使用以下命令:
::: code-group
```bash [create-uni]
pnpm create uni <project-name> -t wot-starter-v2
```
```bash [degit]
pnpx degit wot-ui/wot-starter#v2 <project-name>
```
```bash [giget]
pnpx giget gh:wot-ui/wot-starter#v2 <project-name>
```
:::
在 VS Code 中打开项目文件夹:
```bash
code <project-name>
```
安装依赖:
::: code-group
```bash [pnpm]
pnpm install
```
```bash [yarn]
npx rimraf pnpm-lock.yaml
yarn install
```
```bash [npm]
npx rimraf pnpm-lock.yaml
npm install
```
```bash [bun]
npx rimraf pnpm-lock.yaml
bun install
```
:::
## 开发
你可以使用 `dev` 命令直接启动 `h5` 模式的开发服务器
::: code-group
```bash [pnpm]
pnpm dev
```
```bash [yarn]
yarn dev
```
```bash [npm]
npm dev
```
```bash [bun]
bun dev
```
:::
### 跨端开发
同样使用 `dev` 命令,不同的是你需要使用冒号并跟着你要开发的平台标识。
::: code-group
```bash [pnpm]
pnpm dev:<platform>
```
```bash [yarn]
yarn dev:<platform>
```
```bash [npm]
npm dev:<platform>
```
```bash [bun]
bun dev:<platform>
```
:::
-43
View File
@@ -1,43 +0,0 @@
# 介绍
Wot Starter 是一个基于 [vitesse-uni-app](https://github.com/uni-helper/vitesse-uni-app) 深度整合 [Wot UI V2](https://github.com/wot-ui/wot-ui) 组件库的快速启动模板,采用直观且可扩展的方式创建类型安全、高性能和生产级的跨端应用。你可以直接开始编写 `.vue` 文件,而无需从头开始配置。
## 为什么
`uni-app` 背后的公司 DCloudio 选择创建自己的生态,比如 HBuilderX、uni_modules 等。这部分工作对部分开发者来说意义非凡,他们可以轻松上手并享受社区提供的一切资源。
但是,`uni-app` 社区生态远不如 npm 生态繁荣,我们常常需要求助于 npm 生态来实现部分需求,而 `uni-app` 的黑盒性阻碍了这一点。
vitesse-uni-app 充分拥抱开放生态,比如 VS Code 和 npm,希望能带给你更好的体验。
同样的,基于以上前提,我们选择基于 vitesse-uni-app 深度整合 WotUI 为使用 WotUI 组件库的开发者提供一个拥有更好体验的快速启动模板。
当然,如果你希望使用一个相对纯净的启动模板, [vitesse-uni-app](https://github.com/uni-helper/vitesse-uni-app) 是一个绝佳选择。此外,仍可以使用 [create-uni](https://github.com/uni-helper/create-uni) 自行搭建启动模板。
## 主要依赖
vitess-uni-app 主要由以下开源包组成:
- 核心:[@uni-helper](https://uni-helper.cn/)
- 引擎:[uni-app](https://github.com/dcloudio/uni-app)
- 打包器:[Vite](http://vite.dev/)
- CSS 样式:[UnoCSS](https://unocss.dev/)
- 代码质量:[ESLint](https://github.com/uni-helper/eslint-config) 和 [TypeScript](https://www.typescriptlang.org/)
wot-starter 在以上开源包的基础上引入了以下开源包:
- 组件库:[Wot UI V2](https://github.com/wot-ui/wot-ui)
- CI/CD[uni-mini-ci](https://github.com/Moonofweisheng/uni-mini-ci)
- 路由:[@wot-ui/router](https://github.com/wot-ui/my-uni)
- 图表库:[uni-echarts](https://github.com/xiaohe0601/uni-echarts)
- 网络请求:[Alova](https://github.com/alovajs/alova)
- Pinia[Pinia ](https://pinia.vuejs.org/zh/)
## 鸣谢
- [uni-helper](https://github.com/uni-helper) - 感谢 uni-helper 团队为 uni-app 开发体验优化做出的贡献。
- [vitesse-uni-app](https://github.com/uni-helper/vitesse-uni-app) - 感谢 vitesse-uni-app 提供的快速起手项目。
## 开源协议
本项目基于 [MIT](https://zh.wikipedia.org/wiki/MIT%E8%A8%B1%E5%8F%AF%E8%AD%89) 协议,请自由地享受和参与开源。
-43
View File
@@ -1,43 +0,0 @@
---
version: New
---
# LLMs.txt
[llms.txt](https://llmstxt.org/) 是一个专为大型语言模型设计的文本文件,类似 robots.txt,但目标不同。robots.txt 告诉搜索引擎爬虫哪些页面可以爬取,而 llms.txt 为 AI 工具提供网站内容的结构化信息,帮助它们更好地理解和索引组件库文档、示例和最佳实践。
## 可用资源
我们也提供 2 个 `llms.txt` 路由来帮助 AI 工具访问文档:
- [llms.txt](https://wot-ui.cn/llms.txt) - 包含所有组件及其文档链接的结构化概览
- [llms-full.txt](https://wot-ui.cn/llms-full.txt) - 提供包含实现细节和示例的完整文档
## 在 AI 工具中使用
### Cursor
在 Cursor 中找到 `Indexing & Docs` 设置,并将 `llms.txt` 添加到 `Docs` 中,使用 `@Docs` 功能将 llms.txt 文件包含到项目中。
[详细了解 Cursor 中的 @Docs 功能](https://cursor.com/docs/agent/tools/search)
### TRAE
在 TRAE 中找到 `上下文/文档集` 设置,并将 `llms.txt` 添加到 `文档集` 中,使用 `#Docs` 功能将 llms.txt 文件包含到项目中。
[详细了解 TRAE 中的 #Docs 功能](https://docs.trae.ai/ide/number-sign)
### 其他工具
任何支持 `llms.txt` 标准,或支持通过 URL 摄取文档的工具,都可以使用我们提供的 llms.txt 文件。你可以将它加入工具的 `文档集``rules` 或知识库配置中,帮助 AI 更好地理解 Wot UI 组件库。
### context7
如果不使用 llms.txt,也可以通过 [context7](https://github.com/upstash/context7) 直接读取组件库文档。
[详细了解 context7](https://github.com/upstash/context7)
## 延伸阅读
- [Skills](/guide/skills)
- [llms.txt:让 AI 更好地理解你的文档](https://juejin.cn/post/7500981295105015847)
-87
View File
@@ -1,87 +0,0 @@
---
version: New
---
# CLI
我们在 [Open Wot](https://github.com/wot-ui/open-wot) 中维护了Wot UI 的 AI 工具链仓库,其中对外发布的核心包为 [@wot-ui/cli](https://www.npmjs.com/package/@wot-ui/cli)。它提供命令行工具、MCP Server、离线组件知识库与数据提取脚本,用于把 wot-ui v2 的组件知识接入编辑器、AI Agent 和本地工程分析流程。
## 亮点
- 完全离线:组件 Props、事件、CSS 变量、Demo、Changelog 等元数据随包安装,无需网络请求
- Agent 友好:多数命令支持 `--format json`,适合被工具/脚本消费
- 项目分析:提供 `doctor / usage / lint` 诊断与统计能力
- MCP 集成:通过 `wot mcp` 以 stdio 方式提供 tools,便于集成到支持 MCP 的客户端
## 安装
```bash
npm install -g @wot-ui/cli
```
安装完成后可直接使用 `wot` 命令。
## 快速开始
```bash
wot list
wot info Button
wot demo Button basic
wot doc Button
wot token Button
wot changelog
```
## 命令速查
### 组件知识
- `wot list`:列出可用的 wot-ui 组件
- `wot info <Component>`:查看组件 props、events、slots、CSS 变量
- `wot doc <Component>`:输出组件 Markdown 文档
- `wot demo <Component> [name]`:查看 demo 列表或输出指定 demo 源码
- `wot token [Component]`:查看组件 CSS 变量与默认值
- `wot changelog [version] [component]`:查看版本更新记录
### 项目分析
- `wot doctor [dir]`:检查项目依赖、运行环境与基础集成情况
- `wot usage [dir]`:统计 `.vue` 文件中的 `<wd-*>` 使用情况
- `wot lint [dir]`:检查未知组件、空按钮等规则
### MCP Server
启动 MCP Server
```bash
wot mcp
```
在支持 MCP 的客户端中添加配置(示例):
```json
{
"mcpServers": {
"wot-ui": {
"command": "wot",
"args": ["mcp"]
}
}
}
```
当前 MCP Server 提供的 tools 包括:
- `wot_list`
- `wot_info`
- `wot_doc`
- `wot_demo`
- `wot_token`
- `wot_changelog`
- `wot_lint`
## 通用参数
多数查询命令支持以下参数:
- `--format text|json`
- `--version v2`
-173
View File
@@ -1,173 +0,0 @@
---
title: 网络请求
iframe: true
iframeFormatter: subPages/request/index
---
# 网络请求
你可以使用你喜欢的第三方请求库来获取数据。一般情况下,直接使用 `uni-app` 内置方法即可,以下是我们推荐的一些实践方案,本项目内置的请求库是 [alova](#alova) ,可以接着向下看。
## `uni-app` 内置方法
`uni-app` 提供了 [uni.request](https://uniapp.dcloud.net.cn/api/request/request.html)、[uni.uploadFile](https://uniapp.dcloud.net.cn/api/request/network-file.html#uploadfile)、[uni.downloadFile](https://uniapp.dcloud.net.cn/api/request/network-file.html#downloadfile)、[WebSocket](https://uniapp.dcloud.net.cn/api/request/websocket.html) 等支持。一般情况下,你可以直接使用它们。
## axios
如果你更喜欢 [axios](https://github.com/axios/axios) 及其相关生态,你可以使用 [@uni-helper/axios-adapter](https://github.com/uni-helper/axios-adapter)。它是专为 `uni-app` 打造的 `axios` 适配器,支持全平台!
```ts
import axios from 'axios'
import { createUniAppAxiosAdapter } from '@uni-helper/axios-adapter'
axios.defaults.adapter = createUniAppAxiosAdapter()
const { data, isFinished } = useAxios('/user?ID=12345')
```
## @uni-helper/uni-network
[@uni-helper/uni-network](https://github.com/uni-helper/uni-network) 是一个为 `uni-app` 打造的基于 `Promise` 的 HTTP 客户端,灵感和代码绝大部分源于 `axios@0.27.2``@uni-helper/uni-network` 在底层做了 `uni-app` 适配,体积更小,TypeScript 类型更贴近 `uni-app`
```ts
import { un } from '@uni-helper/uni-network'
try {
const response = await un.get('/user?ID=12345')
console.log(response)
}
catch (error) {
console.error(error)
}
```
## alova
[Alova](https://github.com/alovajs/alova) 是一个极致高效的请求工具集,本项目已集成此方案,无需额外安装。
:::danger ⚠️ 非常重要
我们通过[@alova/wormhole](https://alova.js.org/zh-CN/tutorial/getting-started/extension-integration) 完成了编辑器扩展集成,集成 alova 的编辑器扩展可以让它展现出它更强大的力量。
- 自动生成请求代码和响应数据类型,在 js 项目中也能体验对接口数据的智能提示。
- 将 api 文档嵌入代码中,带你体验边查边用 API 的效果。
- 定时更新 api 并主动通知前端开发,不再依赖服务端开发人员通知。
但是此方案在部分小程序平台存在`globalThis`报错问题,请参考[#657](https://github.com/alovajs/alova/issues/657),本项目通过 `AutoImport` 临时处理。
:::
### 特性
- 简单易用,[观看视频](https://alova.js.org/video-tutorial)5分钟上手。
- 完美兼容你最喜欢的技术栈。
- 20+ 高性能的业务模块,帮助你快速开发性能更好的应用。
- 更先进的 openAPI 解决方案,在代码中和API信息高效交互。
- 请求共享和响应缓存,提升应用性能。
- 类型安全。
### 有什么不同吗?
`@tanstack/react-query``swrjs``ahooks``useRequest` 等库不同,alova 旨在让API集成变得非常轻松高效,还能保持更高效的数据交互,为用户带来更流畅的体验。
> 您还可以查看 [与其他请求库的比较](https://alova.js.org/about/comparison) 以详细了解 alova 的不同之处。
### 基础使用
```typescript
// 定义 API
import { createAlova } from 'alova'
import { uniappAdapter } from '@alova/adapter-uniapp'
const alova = createAlova({
baseURL: 'https://api.example.com',
...uniappAdapter(),
responded: response => response.data
})
// 定义请求方法
const getUserInfo = (id: string) => alova.Get(`/user/${id}`)
const updateUser = (data: UserInfo) => alova.Post('/user', data)
```
### useRequest Hook
```typescript
// 使用 useRequest 发送请求
const {
data, // 响应数据
loading, // 加载状态
error, // 错误信息
send, // 手动发送请求
onSuccess, // 成功回调
onError, // 错误回调
} = useRequest(getUserInfo('123'), {
immediate: true, // 立即发送请求
})
// 监听请求状态
onSuccess((data) => {
console.log('请求成功:', data)
})
onError((error) => {
console.error('请求失败:', error)
})
```
### 高级特性
```typescript
// 请求去重
const { data } = useRequest(getUserInfo('123'), {
shareRequest: true // 相同请求自动去重
})
// 响应缓存
const { data } = useRequest(getUserInfo('123'), {
cacheFor: 300000 // 缓存5分钟
})
// 分页请求
const {
data: list,
page,
pageSize,
total,
isLastPage,
loading,
loadMore,
loadPrev,
refresh
} = usePagination(
(page, pageSize) => getApiList({ page, pageSize }),
{
initialPage: 1,
initialPageSize: 10
}
)
```
### 移除 alova
如果你不需要使用 alova 作为请求库,可以按照以下步骤将其从项目中移除:
1. **卸载依赖包**
```bash
npm uninstall alova @alova/adapter-uniapp @alova/mock @alova/shared @alova/wormhole
# 或者使用 pnpm
pnpm remove alova @alova/adapter-uniapp @alova/mock @alova/shared @alova/wormhole
```
2. **删除相关配置文件**
- 删除 `src/api/` 目录下的 alova 相关配置文件
- 移除项目中引入 alova 的代码
- 移除`alova.config.ts`文件
3. **替换为其他请求方案**
- 可以选择上述提到的 [axios](#axios)、[@uni-helper/uni-network](#uni-helperuni-network) 或直接使用 [uni-app 内置方法](#uni-app-内置方法)
4. **更新相关引用**
- 查找并替换项目中所有使用 `useRequest`、`usePagination` 等 alova hooks 的地方
- 更新对应的导入语句
> 💡 **提示**: 移除前建议先备份项目,确保不会影响现有功能。
> 📖 **了解更多**: [Alova 官方文档](https://alova.js.org/zh-CN/)
-74
View File
@@ -1,74 +0,0 @@
---
title: 路由
iframe: true
iframeFormatter: subPages/router/index
---
# 路由管理
[uni-app](https://uniapp.dcloud.net.cn/tutorial/page.html#%E8%B7%AF%E7%94%B1) 页面路由为框架统一管理,开发者需要在 `pages.json` 里配置每个路由页面的路径及页面样式。类似小程序在 `app.json` 中配置页面路由一样。所以 `uni-app` 的路由用法与 `Vue Router` 不同,不过我们可以引入类似插件实现类似 `Vue Router` 的开发体验,例如 `@wot-ui/router`
[@wot-ui/router](https://my-uni.wot-ui.cn/) 是专为 uni-app 设计的轻量级路由库,它提供了类似`Vue Router`的API和功能,可以帮助开发者实现在uni-app中进行路由跳转、传参、拦截等常用操作。
:::tip 提示
`@wot-ui/router`的目标是基于小程序平台,将uni-app路由相关的API对齐Vue Router,而并非提供完全的Vue Router,`uni-app` [路由](https://uniapp.dcloud.net.cn/api/router.html)中存在的限制,使用`@wot-ui/router`仍将存在。
:::
## 核心特性
- **📝 编程式导航**: 支持多种导航方式
- **🔄 参数传递**: 支持 params 和 query 参数
- **🛡️ 导航守卫**: 完整的导航守卫机制
- **📊 路由信息**: 获取当前路由状态
- **🎯 类型安全**: 完整的 TypeScript 支持
#### 基础用法
```typescript
// 获取路由实例
const router = useRouter()
const route = useRoute()
// 字符串路径跳转
router.push('/pages/detail/index')
// 对象路径跳转
router.push({ path: '/pages/detail/index' })
// 命名路由跳转
router.push({ name: 'detail' })
// 带参数跳转
router.push({
name: 'detail',
params: { id: '123' }
})
// 带查询参数跳转
router.push({
path: '/pages/detail/index',
query: { tab: 'info' }
})
```
#### 导航守卫
```typescript
// 全局前置守卫
router.beforeEach((to, from, next) => {
// 检查用户权限
if (to.meta.requiresAuth && !isLoggedIn()) {
next({ name: 'login' })
} else {
next()
}
})
// 全局后置钩子
router.afterEach((to, from) => {
// 页面跳转完成后的处理
console.log(`${from.path} 跳转到 ${to.path}`)
})
```
> 📖 **了解更多**: [@wot-ui/router 文档](https://my-uni.wot-ui.cn/)
-34
View File
@@ -1,34 +0,0 @@
---
version: New
---
# Skills
[Skills](https://agentskills.io/what-are-skills) 是 AI 的“超能力模板”,是一套完整的、可复用的、能解决特定问题的方案,我们专为 AI Agent(如 Trae, Cursor, Cline 等)设计了 Wot UI 相关的 Skills,以帮助 AI 更加准确、高效地处理 `wot-ui` 相关的开发任务。
## 🎯 内置技能
我们提供以下 AI 技能,可供 AI 智能体根据任务需求加载:
| Skill | 描述 | 适用场景 | 入口 |
| --- | --- | --- | --- |
| `wot-ui-v2` | 处理 wot-ui v2 组件库日常开发的核心技能。 | 组件选型、API 查询、生成 Vue3 + uni-app 页面代码、排查常见组件坑位(如 Toast, Dialog 挂载等)。 | [skills/wot-ui-v2/SKILL.md](https://github.com/wot-ui/open-wot/tree/main/skills/wot-ui-v2/SKILL.md) |
| `wot-ui-cli` | 专门用于回答、使用和调试 `@wot-ui/cli` 工具本身的技能。 | 查询 CLI 命令用法(list, info, doc 等)、配置 MCP Server、本地调试 CLI 源码、执行离线数据提取。 | [skills/wot-ui-cli/SKILL.md](https://github.com/wot-ui/open-wot/tree/main/skills/wot-ui-cli/SKILL.md) |
| `wot-ui-unocss-preset-guide` | 指导安装、配置并使用 `@wot-ui/unocss-preset`。 | 预设接入、`unocss.config.ts` 配置(如 `presetWot`)、`prefix/preflight/baseTokens` 使用示例、类名不生效/自动补全不出现等问题排查。 | [skills/wot-ui-unocss-preset-guide/SKILL.md](https://github.com/wot-ui/open-wot/tree/main/skills/wot-ui-unocss-preset-guide/SKILL.md) |
| `create-wot-ui-theme` | 生成 wot-ui 单文件主题 SCSS 的专项技能。 | 需要为 wot-ui 定制品牌主题,且要求遵循“单文件包含 mixin 和挂载选择器、App.vue 仅作 `@use` 引入”的约束时使用。 | [skills/create-wot-ui-theme/SKILL.md](https://github.com/wot-ui/open-wot/tree/main/skills/create-wot-ui-theme/SKILL.md) |
| `starter-cleaner` | 将 wot-starter v2 模板精简为最小可开发状态的清理技能。 | 初始化业务项目前移除文档、演示分包、生成文件和 monorepo 配置;同步收敛 `vite.config.ts``package.json`,并重新生成最小 `src/pages.json` / `src/uni-pages.d.ts`。 | [.agents/skills/starter-cleaner/SKILL.md](https://github.com/wot-ui/wot-starter/tree/v2/.agents/skills/starter-cleaner/SKILL.md) |
## 安装
推荐使用脚本安装 Skills,可以根据实际需求选择安装项:
```sh
pnpx skills add wot-ui/open-wot
# or
npx skills add wot-ui/open-wot
```
## 延伸阅读
- [llms.txt](/guide/llms-txt)
- [Agent Skills、Rules、Prompt、MCP,一文把它们理清楚了](https://juejin.cn/post/7599268297201958950)
-195
View File
@@ -1,195 +0,0 @@
---
title: 状态管理
iframe: true
iframeFormatter: subPages/pinia/index
---
# 状态管理
得益于组合式方法(Composition API),管理状态非常简单,本项目已经集成了 [Pinia](#pinia) ,并提供了简易集成方案,可以直接使用。
## Pinia
[Pinia](https://pinia.vuejs.org/zh/) 是 `Vue` 官方最新推荐的状态管理库,本项目已集成此方案,无需额外安装。
```shell
pnpm install pinia
```
安装依赖后,需要做基本设置。
:::code-group
```ts [main.ts]
import { createSSRApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const pinia = createPinia()
pinia.use(persistPlugin)
export function createApp() {
const app = createSSRApp(App).use(pinia);
return {
app,
}
}
```
```ts
import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
function increment() {
count.value++
}
return { count, increment }
})
```
```vue
<script setup lang="ts">
import { useCounterStore } from '../../stores/counter'
const counterStore = useCounterStore()
counterStore.count // 0
counterStore.increment()
counterStore.count // 1
</script>
```
:::
### 持久化配置
本项目添加了一个简易插件实现 Pinia 持久化,默认持久化所有 Store 数据,如需排除某些数据,可以在 [persist](https://github.com/wot-ui/wot-starter/blob/main/src/store/persist.ts) 中添加排除名单:
```typescript
// src/store/persist.ts
...
export function persistPlugin(context: PiniaPluginContext) {
// 传入排除列表(store 名单)
persist(context, ['temp'])
}
...
```
> 也可以使用 [pinia-plugin-persistedstate](https://github.com/prazdevs/pinia-plugin-persistedstate) 等 `Pinia` 持久化插件,如果你使用过 `vuex-persistedstate` 想必对它会很熟悉,值得注意的是 `uni-app` 的 [storage](https://uniapp.dcloud.net.cn/api/storage/storage.html) 与传统web并非一致,所以需要手动配置该插件使用`uni-app` 的 `storage`。
## 简单状态管理
你可以直接使用 `Vue` 提供的 `ref` 或 `reactive` 方法来做简单状态管理。
### ref
::: code-group
```ts
// 全局状态
const globalCount = ref(1)
export function useCount() {
// 本地状态
const localCount = ref(1)
function increment() {
globalCount.value++
localCount.value++
}
return {
globalCount,
localCount,
increment
}
}
```
```vue
<script setup lang="ts">
// 自动导入
const { globalCount, localCount, increment } = useCount()
</script>
<template>
<button @click="increment()">
{{ globalCount }}
{{ localCount }}
</button>
</template>
```
:::
### reactive
::: code-group
```ts
export const countStore = reactive({
count: 0,
increment() {
this.count++
}
})
```
```vue
<template>
<!-- 自动导入 -->
<button @click="countStore.increment()">
{{ countStore.count }}
</button>
</template>
```
:::
::: tip
以上例子修改自 Vue 文档的 [用响应式 API 做简单状态管理](https://cn.vuejs.org/guide/scaling-up/state-management.html#simple-state-management-with-reactivity-api)。
:::
## VueUse
你也可以使用 VueUse 提供的 `createGlobalState` 进行状态管理,你还可以配合 `useStorage` 做数据持久。
:::code-group
```ts
export const useAuth = createGlobalState(() => {
const token = useStorage('token', '', uniStorage)
const isLogin = computed(() => !!token.value)
const login = (_token: string) => {
token.value = _token
}
const logout = () => {
token.value = ''
}
return {
token,
isLogin,
login,
logout,
}
})
```
```ts
// storage adapter
export const uniStorage = {
getItem(key: string) {
return uni.getStorageSync(key) || null
},
setItem(key: string, value: any) {
return uni.setStorageSync(key, value)
},
removeItem(key: string) {
return uni.removeStorageSync(key)
},
}
```
:::
::: warning
如果你正在使用 VueUse v10 并遇到了问题,请查看 [dcloudio/uni-app#4604](https://github.com/dcloudio/uni-app/issues/4604) 获取解决方案。
:::
-40
View File
@@ -1,40 +0,0 @@
---
title: UnoCSS 样式
iframe: true
iframeFormatter: subPages/styles/index
---
# 样式
模板基于 `UnoCSS` 提供主要样式支持。当然,你可以结合 CSS 预处理器、组件库等使用。
## UnoCSS
[UnoCSS](https://unocss.dev/) 是按需使用的原子 CSS 引擎,提供了良好的样式支持。
模板内置了 [@uni-helper/unocss-preset-uni](https://github.com/uni-helper/unocss-preset-uni),它在底层使用 [unocss-applet](https://github.com/unocss-applet/unocss-applet) 来兼容不同平台,并提供了按平台编写样式的能力。
```html
<!-- 只在 H5 编译时生成 mx-auto 类 -->
<view class='uni-h5:mx-auto'></view>
<!-- 只在 APP 编译时生成 mx-auto 类 -->
<view class='uni-app:mx-auto'></view>
<!-- 只在小程序编译时生成 mx-auto 类 -->
<view class='uni-mp:mx-auto'></view>
<!-- 只在微信小程序编译时生成 mx-auto 类 -->
<view class='uni-weixin:mx-auto'></view>
<!-- 只在支付宝小程序编译时生成 mx-auto 类 -->
<view class='uni-mp-alipay:mx-auto'></view>
```
## CSS 预处理器
你可以参考 [Vite 文档 CSS 预处理器](https://cn.vitejs.dev/guide/features.html#css-pre-processors),了解相关使用方法。
## 单文件组件样式
你可以参考 [Vue 文档单文件组件 CSS 功能](https://cn.vuejs.org/api/sfc-css-features.html),了解相关使用方法。
## 组件库
除了 Wot UI 组件库,你还可以查看 [uni-helper/awesome-uni-app] 整理的 [组件库](https://github.com/uni-helper/awesome-uni-app#ui-%E7%BB%84%E4%BB%B6%E5%BA%93),当然最好不要看,如果你非要看,那么选一个你心动的即可~
-307
View File
@@ -1,307 +0,0 @@
---
title: 自定义 Tabbar
iframe: true
iframeFormatter: ''
---
# 自定义 Tabbar
本项目基于 [Wot UI](https://wot-ui.cn/) 的 `wd-tabbar` 组件,提供自定义 Tabbar 的实现。
## 实现原理
项目的自定义 Tabbar 主要由以下三个部分组成:
1. **配置文件** (`pages.config.ts`) - 启用自定义 Tabbar 并配置基础信息
2. **组件实现** (`src/layouts/tabbar.vue`) - 自定义 Tabbar 的视图层
3. **状态管理** (`src/composables/useTabbar.ts`) - Tabbar 的逻辑和状态管理
当前实现中,`tabbar.vue` 会在 APP 端调用 `uni.hideTabBar()` 隐藏原生 Tabbar,并通过 `router.pushTab` 切换页面。
## 添加 Tabbar 项
### 1. 修改配置文件
`pages.config.ts` 中的 `tabBar.list` 数组中添加新的页面路径:
```typescript
tabBar: {
custom: true,
// ... 其他配置
list: [{
pagePath: 'pages/index/index',
}, {
pagePath: 'pages/about/index',
}, {
// 添加新的tabbar项
pagePath: 'pages/new-page/index',
}],
}
```
### 2. 更新状态管理
`src/composables/useTabbar.ts` 中的 `tabbarItems` 数组中添加对应的配置:
```typescript
const tabbarItems = ref<TabbarItem[]>([
{ name: 'home', active: true, title: '首页', icon: 'home' },
{ name: 'about', active: false, title: '关于', icon: 'user' },
// 添加新的tabbar项
{ name: 'new-page', active: false, title: '新页面', icon: 'star' },
])
```
### 3. TabbarItem 接口说明
```typescript
interface TabbarItem {
name: string // 页面名称,对应路由name
value?: number // 徽标数值,不传表示不显示徽标
active: boolean // 是否为当前激活项
title: string // 显示标题
icon: string // 图标名称
}
```
## 配置图标
### 图标来源
项目使用 [Wot UI](https://wot-ui.cn/) 的内置图标库。你可以通过以下方式查看可用图标:
1. 访问 [Wot UI 图标文档](https://wot-ui.cn/component/icon.html)
2. 查看所有可用的图标名称
### 修改图标
`src/composables/useTabbar.ts` 中修改对应项的 `icon` 字段:
```typescript
const tabbarItems = ref<TabbarItem[]>([
{ name: 'home', active: true, title: '首页', icon: 'home' },
{ name: 'about', active: false, title: '关于', icon: 'user' },
])
```
### 常用图标示例
```typescript
// 常用的tabbar图标
'home' // 首页
'user' // 用户/关于
'shopping-bag' // 购物
'star' // 收藏
'setting' // 设置
'message' // 消息
'search' // 搜索
'calendar' // 日历
```
### 使用自定义图标
如果内置图标不能满足需求,你可以使用 `wd-tabbar-item``icon` 插槽来自定义图标。
#### 修改 tabbar.vue 组件
`src/layouts/tabbar.vue` 中使用插槽自定义图标:
```vue
<template>
<wd-tabbar
:model-value="activeTabbar.name"
placeholder
bordered
safe-area-inset-bottom
fixed
@change="handleTabbarChange"
>
<wd-tabbar-item
v-for="(item, index) in tabbarList"
:key="index"
:name="item.name"
:value="getTabbarItemValue(item.name)"
:title="item.title"
>
<!-- 使用 icon 插槽自定义图标 -->
<template #icon="{ active }">
<image
:src="active ? item.activeIcon : item.inactiveIcon"
class="custom-icon"
/>
</template>
</wd-tabbar-item>
</wd-tabbar>
</template>
<style>
.custom-icon {
width: 22px;
height: 22px;
}
</style>
```
#### 更新 TabbarItem 接口
```typescript
interface TabbarItem {
name: string // 页面名称,对应路由name
value?: number // 徽标数值,不传表示不显示徽标
active: boolean // 是否为当前激活项
title: string // 显示标题
icon: string // 图标名称(使用内置图标时)
activeIcon?: string // 激活状态自定义图标路径
inactiveIcon?: string // 未激活状态自定义图标路径
}
```
#### 配置自定义图标
```typescript
const tabbarItems = ref<TabbarItem[]>([
{
name: 'home',
active: true,
title: '首页',
icon: 'home',
activeIcon: '/static/icons/home-active.png',
inactiveIcon: '/static/icons/home.png'
},
{
name: 'about',
active: false,
title: '关于',
icon: 'user',
activeIcon: '/static/icons/about-active.png',
inactiveIcon: '/static/icons/about.png'
},
])
```
> 📖 **了解更多**: 查看 [wd-tabbar 组件文档](https://wot-ui.cn/component/tabbar.html) 了解更多自定义选项和插槽用法。
## 徽标配置
自定义 Tabbar 支持显示徽标,通过 `setTabbarItem` 方法为 Tabbar 项设置徽标数值。
### 显示徽标
使用 `setTabbarItem` 方法为 Tabbar 项设置徽标数值:
```typescript
const { setTabbarItem } = useTabbar()
// 为 'about' 页面设置徽标数值为 5
setTabbarItem('about', 5)
```
### 清除徽标
当前 `setTabbarItem` 的类型签名是 `setTabbarItem(name: string, value: number)`,文档示例只演示数字徽标。
如果你希望支持显式清除(例如传 `undefined``null`),建议先将 `useTabbar.ts` 中的签名改为 `value?: number``value: number | null`,再在业务中调用。
### 在组件中使用
```vue
<script setup>
const { setTabbarItem } = useTabbar()
// 模拟收到新消息
function onNewMessage() {
setTabbarItem('about', 3) // 显示3条未读消息
}
// 消息已读
function onMessageRead() {
// 当前实现没有“清除徽标”的独立方法,可按业务约定设置数字值
setTabbarItem('about', 1)
}
</script>
```
## 取消自定义 Tabbar
如果你想恢复使用 uni-app 原生 Tabbar,按以下步骤操作:
### 1. 修改配置文件
`pages.config.ts` 中修改 `tabBar` 配置:
```typescript
tabBar: {
custom: false, // 改为 false 或直接删除此行
color: '#7A7E83',
selectedColor: '#3cc51f',
backgroundColor: '#ffffff',
borderStyle: 'black',
list: [{
pagePath: 'pages/index/index',
text: '首页',
iconPath: '/static/icon/home.png', // 需要提供图标文件
selectedIconPath: '/static/icon/home-active.png'
}, {
pagePath: 'pages/about/index',
text: '关于',
iconPath: '/static/icon/about.png',
selectedIconPath: '/static/icon/about-active.png'
}],
}
```
### 2. 准备图标资源
原生 Tabbar 需要提供图标文件,在 `static/icon/` 目录下放置相应的图标:
- 未选中状态图标:`home.png``about.png`
- 选中状态图标:`home-active.png``about-active.png`
### 3. 移除自定义组件
可以选择删除或注释以下文件:
- `src/layouts/tabbar.vue`
- `src/composables/useTabbar.ts`
### 4. 更新布局
如果使用了自定义 Tabbar 布局,需要相应调整页面布局文件。
## 注意事项
1. **平台兼容性**: 自定义 Tabbar 在所有平台都能正常工作,但在 APP 端会自动隐藏原生 Tabbar
2. **页面路由**: 确保 `pages.config.ts` 中的 `pagePath` 与实际页面文件路径一致
3. **路由名称**: `tabbarItems` 中的 `name` 需要与目标页面路由名一致,否则 `router.pushTab({ name })` 无法正确跳转
4. **徽标更新**: 徽标状态是响应式的,可以实时更新
5. **主题支持**: 自定义 Tabbar 完全支持明暗主题切换
## API 参考
### useTabbar()
返回的方法和属性:
```typescript
const {
tabbarList, // 计算属性:Tabbar项列表
activeTabbar, // 计算属性:当前激活的Tabbar项
getTabbarItemValue, // 方法:获取指定项的徽标值
setTabbarItem, // 方法:设置指定项的徽标值
setTabbarItemActive, // 方法:设置指定项为激活状态
} = useTabbar()
```
### 方法详解
```typescript
// 获取徽标值
getTabbarItemValue(name: string): number | undefined
// 设置徽标值
setTabbarItem(name: string, value: number): void
// 设置激活状态
setTabbarItemActive(name: string): void
```
通过以上配置,你可以灵活地管理项目中的自定义 Tabbar,满足各种业务需求。
-460
View File
@@ -1,460 +0,0 @@
---
title: uni-echarts
iframe: true
iframeFormatter: subEcharts/echarts/index
---
# Echarts
在移动端跨平台开发中,数据可视化是一个常见需求。而 ECharts 作为百度开源的强大图表库,在 Web 端有着广泛的应用,我们在技术栈选择的时候往往倾向于选择这种应用广泛,解决方案完善的库。
但在 uni-app 中直接使用 ECharts 会遇到各种兼容性问题,特别是在小程序端。幸运的是,有很多库可以帮助我们在 `uni-app` 中使用 `Echarts`,例如 `uni-echarts``lime-echart` 等插件,为我们提供了相应的解决方案。
本章节中,我们将会在 wot-starter 中,探索 uni-app 接入 `Echarts` 的方案,并针对小程序,对其超级庞大的体积进行优化。
## 为什么选择 uni-echarts
[uni-echarts](https://github.com/xiaohe0601/uni-echarts) 是一个适用于 uni-app 的 Apache ECharts 组件(仅支持Vue 3),具有以下优势:
- 🚀 **快速上手**:与 [Vue ECharts](https://github.com/ecomfe/vue-echarts) 近乎一致的使用体验
- 📱 **多端兼容**:支持 H5、小程序、APP 等多个平台
- 📦 **支持 easycom**:无需手动导入,开箱即用
-**TypeScript 支持**:完整的类型定义
- 🍳 **免费商用**:基于 MIT 许可协议
基于以上,选择使用 `uni-echarts` 作为我们的图表库,当然也可以选择 [lime-echart](https://ext.dcloud.net.cn/plugin?id=4899) 。
## 安装和配置
### 1. 安装依赖
首先安装必要的依赖包:
```bash
pnpm add echarts uni-echarts
# 或者
npm install echarts uni-echarts
```
在我们的项目中,`package.json` 已经包含了这些依赖:
```json
{
"dependencies": {
"echarts": "^6.0.0",
"uni-echarts": "^1.1.2"
}
}
```
### 2. Vite 配置
`vite.config.ts` 中添加必要的配置:
```typescript
import { defineConfig } from 'vite'
import Uni from '@dcloudio/vite-plugin-uni'
import UniHelperComponents from '@uni-helper/vite-plugin-uni-components'
import { UniEchartsResolver } from 'uni-echarts/resolver'
export default defineConfig({
optimizeDeps: {
exclude:['@wot-ui/ui', 'uni-echarts'] : [],
},
plugins: [
// 组件自动导入
UniHelperComponents({
resolvers: [UniEchartsResolver()],
dts: 'src/components.d.ts',
}),
Uni(),
],
})
```
这样配置后,`uni-echarts` 组件就可以在项目中自动导入使用了,更多信息参见 [Uni ECharts 快速开始](https://uni-echarts.xiaohe.ink/guide/getting-started)。
## 基础使用示例
### 创建一个柱状图组件
让我们以项目中的 `BarChart.vue` 为例,看看如何创建一个基础的柱状图:
```vue
<script setup lang="ts">
import { BarChart } from 'echarts/charts'
import { DatasetComponent, GridComponent, LegendComponent, TooltipComponent } from 'echarts/components'
import * as echarts from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers'
import { provideEcharts } from 'uni-echarts/shared'
// 🚨 重要:由于 npm 插件编译机制问题,需要手动提供 echarts 实例
provideEcharts(echarts)
// 注册需要的组件
echarts.use([
GridComponent,
LegendComponent,
TooltipComponent,
DatasetComponent,
BarChart,
CanvasRenderer,
])
// 图表配置
const option = ref({
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
},
legend: {
data: ['销售额', '利润'],
top: 30,
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true,
},
xAxis: {
type: 'category',
data: ['1月', '2月', '3月', '4月', '5月', '6月'],
},
yAxis: {
type: 'value',
},
series: [
{
name: '销售额',
type: 'bar',
data: [120, 200, 150, 80, 70, 110],
itemStyle: {
color: '#5470c6',
},
},
{
name: '利润',
type: 'bar',
data: [20, 40, 30, 15, 12, 22],
itemStyle: {
color: '#91cc75',
},
},
],
})
</script>
<template>
<uni-echarts custom-class="h-300px" :option="option" />
</template>
```
### 关键要点说明
1. **provideEcharts(echarts)**:这是使用 uni-echarts 的关键步骤,必须在每个组件中调用
2. **按需导入**:只导入需要的图表类型和组件,减小打包体积
3. **echarts.use()**:注册导入的组件
4. **uni-echarts 组件**:使用 `<uni-echarts>` 标签渲染图表
## 更多图表类型
更多图表类型见 [Echarts](https://echarts.apache.org/examples/zh/index.html) 和 [Uni ECharts](https://uni-echarts.xiaohe.ink/examples/basic),当然你也可以使用 AI 工具帮助你编写想要的图表配置,它非常善于处理这个事情。
## 高级功能:分包优化与异步加载
引入 `Echarts` 后,体积暴增 800KB ,怎么办?
有办法,我们曾在 [Vue3 uni-app 主包 2 MB 危机?1 个插件 10 分钟瘦身](https://mp.weixin.qq.com/s/nnmu91kclQHnE-1TAn11Tg) 一文中介绍过 `@uni-ku/bundle-optimizer`,它是解决微信小程序超包的利器,我们现在使用它的分包优化和分包异步化能力,来优化引入 `Echarts` 后暴增的小程序体积。
这是我们的项目结构,在 `subEcharts` 分包中实现 `Echarts` 相关组件,在 `subAsyncEcharts` 分包中演示分包异步化效果:
```text
src/
├── pages/ # 主包页面
├── subEcharts/ # ECharts 组件分包
│ └── echarts/
│ └── components/
├── subAsyncEcharts/ # 异步 ECharts 演示分包
│ └── asyncEcharts/
└── subPages/ # 其他功能分包
```
### 安装和配置
#### 1. 安装依赖
```bash
pnpm add -D @uni-ku/bundle-optimizer
# 或者
npm install -D @uni-ku/bundle-optimizer
```
在我们的项目中,`package.json` 已经包含了这个依赖:
```json
{
"devDependencies": {
"@uni-ku/bundle-optimizer": "1.3.15-beta.2"
}
}
```
#### 2. Vite 配置
`vite.config.ts` 中配置插件:
```typescript
import { defineConfig } from 'vite'
import Uni from '@dcloudio/vite-plugin-uni'
import Optimization from '@uni-ku/bundle-optimizer'
export default defineConfig({
plugins: [
Uni(),
// 分包优化插件
Optimization({
logger: true, // 开启日志输出
}),
],
})
```
#### 3. 小程序分包配置
`manifest.json` 中开启分包优化:
```json
{
"mp-weixin": {
"optimization": {
"subPackages": true
}
}
}
```
如果你使用了 `@uni-helper/vite-plugin-uni-manifest` 插件,那么需要在 `manifest.config.ts` 中开启分包优化:
```ts
export default defineManifestConfig({
'mp-weixin': {
optimization: {
subPackages: true,
},
},
})
```
配置完成后,重新构建,我们会发现主包少了 200+KB ,还剩 500KB 在主包中,可以期待 `@uni-ku/bundle-optimizer` 未来可以传送组件到分包中,到时会把大部分构建产物都打包进入分包中。
> 这里配合 lime-echart 的话,应该可以将 echarts.min.js 完全放入分包,各位可以自行探索。
### 跨分包异步组件引用
在我们的项目中,`subAsyncEcharts` 分包可以异步引用 `subEcharts` 分包中的组件:
```vue
<!-- src/subAsyncEcharts/asyncEcharts/index.vue -->
<script setup lang="ts">
// 跨分包异步导入组件
import BarChart from '@/subEcharts/echarts/components/BarChart.vue?async'
import DonutChart from '@/subEcharts/echarts/components/DonutChart.vue?async'
import FunnelChart from '@/subEcharts/echarts/components/FunnelChart.vue?async'
import GaugeChart from '@/subEcharts/echarts/components/GaugeChart.vue?async'
import LineChart from '@/subEcharts/echarts/components/LineChart.vue?async'
import LiquidFillChart from '@/subEcharts/echarts/components/LiquidFillChart.vue?async'
import MiniLineChart from '@/subEcharts/echarts/components/MiniLineChart.vue?async'
import PieChart from '@/subEcharts/echarts/components/PieChart.vue?async'
import RadarChart from '@/subEcharts/echarts/components/RadarChart.vue?async'
import ScatterChart from '@/subEcharts/echarts/components/ScatterChart.vue?async'
import StackedBarChart from '@/subEcharts/echarts/components/StackedBarChart.vue?async'
</script>
```
更多信息参见 [@uni-ku/bundle-optimizer](https://github.com/uni-ku/bundle-optimizer)。
## 注意事项和最佳实践
### 1. 使用 npm 方式安装必须调用 provideEcharts
在每个使用 ECharts 的组件中,都必须调用 `provideEcharts(echarts)`
```javascript
import * as echarts from 'echarts/core'
import { provideEcharts } from 'uni-echarts/shared'
// 🚨 这一行是必须的
provideEcharts(echarts)
```
### 2. 按需导入组件
为了减小打包体积,建议按需导入需要的图表类型和组件:
```javascript
// 只导入需要的图表类型
import { BarChart, LineChart, PieChart } from 'echarts/charts'
// 只导入需要的组件
import { GridComponent, TooltipComponent, LegendComponent } from 'echarts/components'
// 导入渲染器
import { CanvasRenderer } from 'echarts/renderers'
```
### 3. 设置图表尺寸
使用 `custom-class` 属性设置图表容器的尺寸:
```vue
<template>
<!-- 使用 UnoCSS/Tailwind 类名 -->
<uni-echarts custom-class="h-300px w-full" :option="option" />
<!-- 或者使用自定义 CSS -->
<uni-echarts custom-class="chart-container" :option="option" />
</template>
<style>
.chart-container {
width: 100%;
height: 300px;
}
</style>
```
### 4. 响应式数据更新
当需要动态更新图表数据时,直接修改 `option` 对象即可:
```javascript
const option = ref({
// 初始配置
})
// 更新数据
function updateData() {
option.value.series[0].data = [/* 新数据 */]
}
```
### 5. 主题定制
可以通过 `provideEchartsTheme` 来设置自定义主题:
```javascript
import { provideEcharts, provideEchartsTheme } from 'uni-echarts/shared'
import * as echarts from 'echarts/core'
provideEcharts(echarts)
// 设置自定义主题
provideEchartsTheme({
color: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de'],
backgroundColor: 'transparent',
// 更多主题配置...
})
```
## 卸载步骤
如果你不再需要在项目中使用 ECharts,可以按照以下步骤完全卸载相关依赖和配置:
### 1. 卸载依赖包
首先卸载 ECharts 相关的依赖包:
```bash
pnpm remove echarts uni-echarts
```
### 2. 清理 Vite 配置
`vite.config.ts` 中移除 ECharts 相关的配置:
```typescript
import { defineConfig } from 'vite'
import Uni from '@dcloudio/vite-plugin-uni'
import UniHelperManifest from '@uni-helper/vite-plugin-uni-manifest'
import UniHelperPages from '@uni-helper/vite-plugin-uni-pages'
import UniHelperLayouts from '@uni-helper/vite-plugin-uni-layouts'
import UniHelperComponents from '@uni-helper/vite-plugin-uni-components'
import AutoImport from 'unplugin-auto-import/vite'
import { WotResolver } from '@uni-helper/vite-plugin-uni-components/resolvers'
import { UniEchartsResolver } from 'uni-echarts/resolver' // [!code --]
import UniKuRoot from '@uni-ku/root'
export default async () => {
const UnoCSS = (await import('unocss/vite')).default
return defineConfig({
optimizeDeps: {
exclude: ['@wot-ui/ui', 'uni-echarts'] : [], // [!code --]
exclude: ['@wot-ui/ui',] : [], // [!code ++]
},
plugins: [
UniHelperManifest(),
UniHelperPages({
dts: 'src/uni-pages.d.ts',
subPackages: [
'src/subPages',
],
exclude: ['**/components/**/*.*'],
}),
UniHelperLayouts(),
UniHelperComponents({
resolvers: [WotResolver(), UniEchartsResolver()], // [!code --]
resolvers: [WotResolver()], // [!code ++]
dts: 'src/components.d.ts',
dirs: ['src/components', 'src/business'],
directoryAsNamespace: true,
}),
UniKuRoot(),
Uni(),
// https://github.com/uni-ku/bundle-optimizer
Optimization({
logger: true,
}),
AutoImport({
imports: ['vue', '@vueuse/core', 'pinia', 'uni-app', {
from: '@wot-ui/router',
imports: ['createRouter', 'useRouter', 'useRoute'],
}, {
from: '@wot-ui/ui',
imports: ['useToast', 'useMessage', 'useNotify', 'CommonUtil'],
}, {
from: 'alova/client',
imports: ['usePagination', 'useRequest'],
}],
dts: 'src/auto-imports.d.ts',
dirs: ['src/composables', 'src/store', 'src/utils', 'src/api'],
vueTemplate: true,
}),
UnoCSS(),
],
})
}
```
### 3. 删除相关文件和目录
删除项目中与 ECharts 相关的文件和目录:
```bash
# 删除 ECharts 组件分包目录
rm -rf src/subEcharts/
# 删除异步 ECharts 演示分包目录
rm -rf src/subAsyncEcharts/
```
完成以上步骤后,你的项目就完全移除了 ECharts 相关的依赖和配置,项目体积也会相应减小。
## 总结
我们在 `wot-starter` 中 使用 `uni-echarts` 结合 `@uni-ku/bundle-optimizer``uni-app` 开发者提供了一个完整的高性能 ECharts 解决方案。通过合理的配置和规范的使用方式,我们可以在各个平台上实现丰富的数据可视化效果,同时保证应用的性能和用户体验。
## 参考资源
- [uni-echarts 官方文档](https://uni-echarts.xiaohe.ink)
- [@uni-ku/bundle-optimizer](https://github.com/uni-ku/bundle-optimizer)
- [ECharts 官方文档](https://echarts.apache.org/zh/index.html)
- [lime-echart](https://ext.dcloud.net.cn/plugin?id=4899)
-160
View File
@@ -1,160 +0,0 @@
# Uni Helper 插件
Uni Helper 是一个旨在增强 uni-app 系列产品的开发体验为爱发电的非官方组织。作为靠爱发电的非官方项目,Uni Helper 提供了打包工具插件支持、编辑器扩展支持、NPM 包等并尽力维护它们,而他们提供的众多插件组成了本项目的核心插件库。
## Components 组件
大多数组件都是用户界面的可重用部分,如按钮和菜单。
得益于 [@uni-helper/vite-plugin-uni-components](https://github.com/uni-helper/vite-plugin-uni-components),组件将自动注册到全局,你不需要显式导入它们。只需要在 `src/components` 目录下创建组件,然后直接使用即可。
:::code-group
```vue [src/pages/index.vue]
<template>
<div>
<h1>欢迎使用 vitesse-uni-app </h1>
<AppAlert>
这个组件会自动导入
</AppAlert>
</div>
</template>
```
```vue [src/components/AppAlert.vue]
<template>
<span>
<slot />
</span>
</template>
```
:::
## Pages 页面
通过组合使用组件,我们可以得到展示给用户的页面。
得益于 [@uni-helper/vite-plugin-uni-pages](https://github.com/uni-helper/vite-plugin-uni-pages),约定式路由(文件路由)的实现轻而易举。`src/pages` 目录下的每个文件都代表着一个路由。要创建新页面,只需要在这个目录里新增 `.vue` 文件。
:::code-group
```vue [src/pages/index.vue]
<template>
<div>
<h1>欢迎使用 vitesse-uni-app </h1>
<AppAlert>
这个组件会自动导入
</AppAlert>
</div>
</template>
```
```vue [src/pages/about.vue]
<template>
<section>
<p>通过 `/pages/about` 来访问这个页面</p>
</section>
</template>
```
[@uni-helper/vite-plugin-uni-pages](https://github.com/uni-helper/vite-plugin-uni-pages) 也支持配置排除指定目录的页面(例如组件目录),相对于 dir 和 subPackages,我们在`vite.config.ts`中已经做了相应的配置排除了`components`目录:
```ts
// vite.config.ts
...
UniHelperPages({
dts: 'src/uni-pages.d.ts',
subPackages: [
'src/subPages',
],
/**
* 排除的页面,相对于 dir 和 subPackages
* @default []
*/
exclude: ['**/components/**/*.*'],
})
...
```
:::
## Layouts 布局
布局可以用来创建通用界面(如页眉和页脚显示)的包装器,不同的页面可能需要不同的布局。布局是使用 `Vue` 的插槽功能实现的。
得益于 [@uni-helper/vite-plugin-uni-layouts](https://github.com/uni-helper/vite-plugin-uni-layouts),你可以轻松地切换不同的布局。
`src/layouts/default.vue` 文件将作为默认布局。
:::code-group
```vue [src/layouts/default.vue]
<template>
<div>
<AppHeader />
<!-- src/pages/index.vue 和 src/pages/about.vue 内容展示 -->
<slot />
<AppFooter />
</div>
</template>
```
```vue [src/pages/index.vue]
<template>
<div>
<h1>欢迎使用 vitesse-uni-app </h1>
<AppAlert>
这个组件会自动导入
</AppAlert>
</div>
</template>
```
```vue [src/pages/about.vue]
<template>
<section>
<p>通过 `/pages/about` 来访问这个页面</p>
</section>
</template>
```
:::
在页面文件内设置 `route` 代码块可以指定自定义布局。
```vue [src/pages/index.vue]
<route lang="json">
{
"layout": "custom"
}
</route>
```
## manifest 应用配置
`manifest.json` 文件是应用的配置文件,用于指定应用的名称、图标、权限等。
得益于 [@uni-helper/vite-plugin-uni-manifest](https://github.com/uni-helper/vite-plugin-uni-manifest),你可以使用 `TypeScript` 编写 `uni-app` 的 `manifest.json`。
```ts
// vite.config.ts
import Uni from '@dcloudio/vite-plugin-uni'
import UniManifest from '@uni-helper/vite-plugin-uni-manifest'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [UniManifest(), Uni()],
})
```
创建 `manifest.config.(ts|mts|cts|js|cjs|mjs|json)`, 然后用 `TypeScript` 编写你的 `manifest.json`。
``` ts
// manifest.config.ts
import { defineManifestConfig } from '@uni-helper/vite-plugin-uni-manifest'
export default defineManifestConfig({
// code here...
})
```
在 [这里](https://github.com/uni-helper/vite-plugin-uni-manifest/blob/main/playground/manifest.config.ts),你可以找到 `uni-app` 默认的 Vite-TS 模版的 `manifest.json` 是如何用 TypeScript 编写的。
-199
View File
@@ -1,199 +0,0 @@
---
title: uni-ku/root
iframe: true
iframeFormatter: subPages/uni-ku-root/index
---
# @uni-ku/root
在传统的 UniApp 开发中,由于框架限制,无法像标准 Vue 应用那样使用全局根组件来管理公共状态和组件,这给开发带来了诸多不便。
为了解决这个问题,我们引入了 [@uni-ku/root](https://github.com/uni-ku/root) 插件,它通过 Vite 模拟出虚拟根组件,让 UniApp 项目也能享受到类似 Vue 标准应用的开发体验,也可以解决 `@uni-helper/vite-plugin-uni-layouts` 插件无法使用微信小程序 `page-meta` 的问题。
## 🎯 @uni-ku/root 是什么?
@uni-ku/root 借助 Vite 模拟出虚拟根组件(支持SFC的App.vue),解决 uniapp 无法使用公共组件问题。
### 🎏 支持
- 自定义虚拟根组件文件命名(App.ku.vue文件命名支持更换)
- 更高灵活度的获取虚拟根组件实例(获取KuRootView的Ref)
- 自动提取PageMeta到页面顶层(自动提升小程序PageMeta[用于阻止滚动穿透]组件)
## 🚀 接入步骤和配置
### 📦 安装
```bash
pnpm add -D @uni-ku/root
yarn add -D @uni-ku/root
npm install -D @uni-ku/root
```
### 🚀 Vite 配置
`vite.config.ts` 中引入并配置 UniKuRoot 插件:
```typescript
import { defineConfig } from 'vite'
import Uni from '@dcloudio/vite-plugin-uni'
import UniHelperManifest from '@uni-helper/vite-plugin-uni-manifest'
import UniHelperPages from '@uni-helper/vite-plugin-uni-pages'
import UniHelperLayouts from '@uni-helper/vite-plugin-uni-layouts'
import UniHelperComponents from '@uni-helper/vite-plugin-uni-components'
import AutoImport from 'unplugin-auto-import/vite'
import { WotResolver } from '@uni-helper/vite-plugin-uni-components/resolvers'
import UniKuRoot from '@uni-ku/root'
// https://vitejs.dev/config/
export default async () => {
const UnoCSS = (await import('unocss/vite')).default
return defineConfig({
plugins: [
// https://github.com/uni-helper/vite-plugin-uni-manifest
UniHelperManifest(),
// https://github.com/uni-helper/vite-plugin-uni-pages
UniHelperPages({
dts: 'src/uni-pages.d.ts',
subPackages: [
'src/subPages',
],
/**
* 排除的页面,相对于 dir 和 subPackages
* @default []
*/
exclude: ['**/components/**/*.*'],
}),
// https://github.com/uni-helper/vite-plugin-uni-layouts
UniHelperLayouts(),
// https://github.com/uni-helper/vite-plugin-uni-components
UniHelperComponents({
resolvers: [WotResolver()],
dts: 'src/components.d.ts',
dirs: ['src/components', 'src/business'],
directoryAsNamespace: true,
}),
// https://github.com/uni-ku/root
UniKuRoot(),
Uni(),
// https://github.com/antfu/unocss
// see unocss.config.ts for config
UnoCSS(),
],
})
}
```
**重要提示**UniKuRoot 插件必须放在 `Uni()` 插件之前,如果存在修改 pages.json 的插件和 `Layout` 插件,需要将 UniKuRoot 放在它们之后。
### 🎉 创建虚拟根组件
`src/App.ku.vue` 中创建虚拟根组件:
> 注意 `App.ku.vue` 中暂时无法编写样式全局生效,所以我们可以将样式写到 `App.vue` 中
```html
<script setup lang="ts">
const { themeVars, theme } = useManualTheme()
</script>
<template>
<wd-config-provider :theme-vars="themeVars" :theme="theme" :custom-class="`page-wraper ${theme}`">
<ku-root-view />
<wd-notify />
<wd-message-box />
<wd-toast />
<global-loading />
<global-toast />
<global-message />
<!-- #ifdef MP-WEIXIN -->
<privacy-popup />
<!-- #endif -->
</wd-config-provider>
</template>
```
## 💡 代码示例和使用方法
### 1. 在页面中使用全局 Toast
```html
<!-- src/pages/index/index.vue -->
<script setup lang="ts">
const globalToast = useGlobalToast()
function showSuccess() {
globalToast.success('操作成功!')
}
function showError() {
globalToast.error('操作失败!')
}
</script>
<template>
<view>
<button @click="showSuccess">显示成功提示</button>
<button @click="showError">显示错误提示</button>
</view>
</template>
```
### 2. PageMeta 自动提升示例
在页面中使用 PageMeta 组件,会自动提升到页面顶层:
```html
<!-- src/pages/uni-ku-root/index.vue -->
<script setup lang="ts">
definePage({
name: 'root',
style: {
navigationBarTitleText: 'uni-ku/root',
},
})
const show = ref<boolean>(false)
</script>
<template>
<page-meta :page-style="`overflow:${show ? 'hidden' : 'visible'};`" />
<view class="min-h-200vh">
<demo-block title="锁定滚动" transparent>
<wd-cell-group border>
<wd-cell title="锁定滚动" is-link @click="show = !show" />
</wd-cell-group>
</demo-block>
<wd-popup
v-model="show"
lock-scroll
position="bottom"
closable
:safe-area-inset-bottom="true"
custom-style="height: 200px;"
@close="show = false"
/>
</view>
</template>
```
## 🎉 总结
通过接入 `@uni-ku/root`wot-starter 项目成功实现了:
- ✅ 全局组件的统一管理
- ✅ 主题配置的全局应用
- ✅ 更好的代码组织结构
- ✅ 接近标准 Vue 应用的开发体验
- ✅ 完美支持 Wot UI 组件库
这个方案不仅解决了 UniApp 开发中的痛点,还可以解决 `@uni-helper/vite-plugin-uni-layouts` 插件无法使用微信小程序 `page-meta` 的问题,一举多得,美哉!
## 🔗 相关链接
- [@uni-ku/root GitHub](https://github.com/uni-ku/root)
- [Wot UI](https://wot-ui.cn)
- [Wot Starter](https://starter.wot-ui.cn)
-247
View File
@@ -1,247 +0,0 @@
# UnoCSS 预设
[@wot-ui/unocss-preset](https://github.com/wot-ui/unocss-preset) 是我们提供的 `UnoCSS` 预设,用来把 `wot-ui` 的设计 token 和主题变量映射成可直接使用的原子类。
接入后,你可以直接在模板中使用 `wot-` 前缀类名完成颜色、间距、圆角、字重、排版、透明度和描边等样式编排,而不需要手动维护一套额外的 CSS 变量映射。
```html
<view class="wot-bg-filled-oppo wot-rounded-xl wot-p-loose">
<text class="wot-text-title-large wot-text-text-main wot-font-semibold">
Wot UI UnoCSS Preset
</text>
</view>
```
## 适用场景
如果你满足以下任一场景,推荐使用 `@wot-ui/unocss-preset`
- 项目已经接入 `UnoCSS`
- 希望直接复用 `wot-ui` 的设计 token 与主题变量
- 希望通过原子类快速搭建 `uni-app` / `Vue` 页面样式
## 它提供了什么
这个预设主要提供三部分能力:
| 能力 | 说明 |
| --- | --- |
| `theme` | 把 `wot-ui` 的语义色和基础色映射到 `UnoCSS theme`,供颜色类规则使用 |
| `rules` | 生成 `wot-text-*``wot-bg-*``wot-m-*``wot-p-*``wot-rounded-*` 等原子类规则 |
| `preflights` | 自动注入 `wot-ui` 的 CSS 变量,确保这些原子类能拿到正确的变量值 |
默认情况下,类名前缀为 `wot-`,例如:
- `wot-text-primary`
- `wot-bg-danger-surface`
- `wot-m-main`
- `wot-rounded-md`
- `wot-text-body-main`
## 安装
::: code-group
```bash [npm]
npm i -D unocss
npm i @wot-ui/unocss-preset
```
```bash [yarn]
yarn add -D unocss
yarn add @wot-ui/unocss-preset
```
```bash [pnpm]
pnpm add -D unocss
pnpm add @wot-ui/unocss-preset
```
:::
## 使用
在项目根目录创建或更新 `unocss.config.ts`
```ts
import { presetWot } from '@wot-ui/unocss-preset'
import { defineConfig } from 'unocss'
export default defineConfig({
presets: [
presetWot(),
],
})
```
完成配置后,就可以直接在模板中使用 `wot-` 前缀原子类:
```vue
<template>
<view class="wot-bg-filled-oppo wot-rounded-lg wot-p-main">
<view class="wot-text-title-large wot-text-text-main">标题</view>
<view class="wot-mt-tight wot-text-body-main wot-text-text-secondary">
使用 wot-ui 设计 token 快速组织页面样式
</view>
</view>
</template>
```
## 配置项
你可以通过 `presetWot()` 传入配置项来自定义行为:
```ts
presetWot({
prefix: 'wot',
preflight: true,
baseTokens: false,
})
```
| 配置项 | 默认值 | 说明 | 示例 |
| --- | --- | --- | --- |
| `prefix` | `wot` | 工具类前缀 | `wot-text-primary`、`wot-m-main` |
| `preflight` | `true` | 是否自动注入 `wot-ui` CSS 变量 | `presetWot({ preflight: true })` |
| `baseTokens` | `false` | 是否开放基础色板和原始 token 类名 | `presetWot({ baseTokens: true })` |
### prefix
用于控制原子类前缀。默认是 `wot`,对应生成的类名格式为 `wot-*`。
```ts
presetWot({
prefix: 'wot',
})
```
### preflight
开启后会自动注入 `wot-ui` 相关 CSS 变量,通常推荐保持默认值 `true`。如果关闭它,需要你自行确保这些变量已在项目中可用,否则类名可能存在但样式不生效。
### baseTokens
默认情况下,预设主要暴露语义化 token。开启 `baseTokens` 后,还会额外开放基础色板和原始 token 类名,适合需要更细粒度控制的场景。
## 支持的规则
| 规则类型 | 前缀模式 | 示例 |
| --- | --- | --- |
| 颜色 | `wot-text-*` / `wot-bg-*` / `wot-border-*` | `wot-text-primary`、`wot-bg-danger-surface`、`wot-border-border-main` |
| 间距 | `wot-m-*` / `wot-gap-*` | `wot-m-main`、`wot-gap-tight`、`wot-gap-x-loose` |
| 内边距 | `wot-p-*` | `wot-p-main`、`wot-px-tight`、`wot-pb-loose` |
| 圆角 | `wot-rounded-*` | `wot-rounded-md`、`wot-rounded-full` |
| 字重 | `wot-font-*` | `wot-font-medium`、`wot-font-semibold` |
| 排版 | `wot-text-*` | `wot-text-body-main`、`wot-text-title-large` |
| 透明度 | `wot-opacity-*` | `wot-opacity-disabled` |
| 描边 | `wot-border-stroke-*` | `wot-border-stroke-main` |
## 可用变量值
以下为各类原子类支持的主要变量值,使用时将它们拼接到对应规则后即可。
### 颜色类
| 项目 | 内容 |
| --- | --- |
| 适用前缀 | `wot-text-*`、`wot-bg-*`、`wot-border-*` |
| 主色 | `primary`、`primary-1` ~ `primary-10` |
| 危险色 | `danger`、`danger-main`、`danger-hover`、`danger-clicked`、`danger-disabled`、`danger-particular`、`danger-surface` |
| 成功色 | `success`、`success-main`、`success-hover`、`success-clicked`、`success-disabled`、`success-particular`、`success-surface` |
| 警告色 | `warning`、`warning-main`、`warning-hover`、`warning-clicked`、`warning-disabled`、`warning-particular`、`warning-surface` |
| 文字色 | `text-main`、`text-secondary`、`text-auxiliary`、`text-disabled`、`text-placeholder`、`text-white` |
| 图标色 | `icon-main`、`icon-secondary`、`icon-auxiliary`、`icon-disabled`、`icon-placeholder`、`icon-white` |
| 边框色 | `border-extra-strong`、`border-strong`、`border-main`、`border-light`、`border-white`、`border-zero` |
| 填充色 | `filled-extra-strong`、`filled-strong`、`filled-content`、`filled-bottom`、`filled-oppo`、`filled-zero` |
| 分割线 | `divider-main`、`divider-light`、`divider-strong`、`divider-white` |
| 反馈色 | `feedback-hover`、`feedback-active`、`feedback-accent` |
| 半透明填充 | `opacfilled-tooltip-toast-cover`、`opacfilled-main-cover`、`opacfilled-light-cover` |
| `Picker View Mask` | `picker-view-mask-start`、`picker-view-mask-end` |
| 分类色 | `classify-yellow-bg`、`classify-yellow-border`、`classify-yellow-content`、`classify-cyan-bg`、`classify-cyan-border`、`classify-cyan-content`、`classify-purple-bg`、`classify-purple-border`、`classify-purple-content`、`classify-grape-bg`、`classify-grape-border`、`classify-grape-content`、`classify-pink-bg`、`classify-pink-border`、`classify-pink-content` |
| 示例 | `wot-text-primary`、`wot-bg-filled-oppo`、`wot-border-border-main`、`wot-bg-classify-purple-content` |
### 间距类
| 项目 | 内容 |
| --- | --- |
| 适用前缀 | `wot-m-*`、`wot-mx-*`、`wot-my-*`、`wot-mt-*`、`wot-mr-*`、`wot-mb-*`、`wot-ml-*`、`wot-gap-*`、`wot-gap-x-*`、`wot-gap-y-*` |
| 可用值 | `zero`、`ultra-tight`、`super-tight`、`extra-tight`、`tight`、`main`、`loose`、`extra-loose`、`super-loose`、`ultra-loose`、`spacious`、`extra-spacious`、`super-spacious`、`ultra-spacious` |
| 示例 | `wot-m-main`、`wot-mt-tight`、`wot-gap-x-loose` |
### 内边距类
| 项目 | 内容 |
| --- | --- |
| 适用前缀 | `wot-p-*`、`wot-px-*`、`wot-py-*`、`wot-pt-*`、`wot-pr-*`、`wot-pb-*`、`wot-pl-*` |
| 可用值 | `zero`、`ultra-tight`、`super-tight`、`extra-tight`、`tight`、`main`、`loose`、`extra-loose`、`super-loose`、`ultra-loose`、`spacious`、`extra-spacious`、`super-spacious`、`ultra-spacious` |
| 示例 | `wot-p-main`、`wot-px-tight`、`wot-pb-loose` |
### 圆角类
| 项目 | 内容 |
| --- | --- |
| 适用前缀 | `wot-rounded-*` |
| 可用值 | `zero`、`sm`、`md`、`lg`、`xl`、`2xl`、`3xl`、`full` |
| 示例 | `wot-rounded-md`、`wot-rounded-full` |
### 字重类
| 项目 | 内容 |
| --- | --- |
| 适用前缀 | `wot-font-*` |
| 可用值 | `ultra-light`、`thin`、`light`、`regular`、`medium`、`semibold`、`bold` |
| 示例 | `wot-font-medium`、`wot-font-semibold` |
### 排版类
| 项目 | 内容 |
| --- | --- |
| 适用前缀 | `wot-text-*` |
| Title | `title-main`、`title-large`、`title-extra-large` |
| Body | `body-main`、`body-large`、`body-extra-large`、`body-super-large`、`body-ultra-large` |
| Label | `label-super-small`、`label-extra-small`、`label-small`、`label-main`、`label-large` |
| 示例 | `wot-text-body-main`、`wot-text-title-large`、`wot-text-label-large` |
### 透明度类
| 项目 | 内容 |
| --- | --- |
| 适用前缀 | `wot-opacity-*` |
| 可用值 | `disabled`、`dimmer`、`overlay`、`main`、`backdrop` |
| 示例 | `wot-opacity-disabled`、`wot-opacity-main` |
### 描边类
| 项目 | 内容 |
| --- | --- |
| 适用前缀 | `wot-border-stroke-*` |
| 可用值 | `zero`、`light`、`main`、`bold` |
| 示例 | `wot-border-stroke-main`、`wot-border-stroke-bold` |
### 开启 `baseTokens` 后可用
当 `baseTokens: true` 时,会额外开放基础色板与原始 token。
| 项目 | 内容 |
| --- | --- |
| 基础色 | `base-black`、`base-white`、`base-transparent` |
| 色阶家族 | `blue-*`、`lightblue-*`、`pink-*`、`red-*`、`volcano-*`、`orange-*`、`yellow-*`、`green-*`、`cyan-*`、`purple-*`、`grape-*`、`coolgrey-*`、`neutralgrey-*`、`warmgrey-*` |
| 色阶范围 | 每个家族支持 `1` ~ `10` |
| 额外透明色 | 非 `grey` 家族额外支持 `*-opac` |
| 透明阶 | `opac-1_02`、`opac-2_04`、`opac-3_08`、`opac-4_15`、`opac-5_20`、`opac-6_30`、`opac-7_45`、`opac-7_55`、`opac-8_65`、`opac-9_75`、`opac-10_85` |
| 白色透明阶 | `opacwhite-1_02`、`opacwhite-2_04`、`opacwhite-3_08`、`opacwhite-4_15`、`opacwhite-5_20`、`opacwhite-6_30`、`opacwhite-7_45`、`opacwhite-7_55`、`opacwhite-8_65`、`opacwhite-9_75`、`opacwhite-10_85` |
| 示例 | `wot-bg-base-black`、`wot-text-blue-6`、`wot-border-opac-3_08` |
## 导出内容
包默认导出 `presetWot`,并额外导出以下 token maps,便于业务侧复用:
- `SEMANTIC_COLOR_MAP`
- `BASE_COLOR_MAP`
- `SPACING_MAP`
- `PADDING_MAP`
- `RADIUS_MAP`
- `FONT_WEIGHT_MAP`
- `TYPOGRAPHY_MAP`
- `OPACITY_MAP`
- `STROKE_MAP`
-71
View File
@@ -1,71 +0,0 @@
# Wot UI
Wot UI 是一个轻量、美观、AI友好的 uni-app 组件库,提供80+高质量组件,支持暗黑模式、国际化和自定义主题。总之就是好用,爱用。它是本模板项目的组件库,当然本项目就是为它而生的。Wot UI 与 Wot Starter 团队高度重合,共享开发资源,快来使用吧。
## 快速上手
请查看[快速上手](https://wot-ui.cn/guide/quick-use.html)文档。
## 扫码体验
<div style="display:flex;gap:24px">
<div style="display: inline-block;">
<img style="width: 150px; height: 150px;" src="https://wot-ui.cn/wx.jpg" alt="微信小程序二维码" />
<div style="text-align: center;">微信扫码</div>
</div>
<div style="display: inline-block;">
<img style="width: 150px; height: 150px;" src="https://wot-ui.cn/alipay.png" alt="支付宝小程序二维码" />
<div style="text-align: center;">支付宝扫码</div>
</div>
<div style="display: inline-block;">
<img style="width: 150px; height: 150px;" src="https://wot-ui.cn/h5.png" alt="H5 演示二维码" />
<div style="text-align: center;">浏览器扫码</div>
</div>
</div>
## ✨ 特性
- 🎯 多平台覆盖,支持 微信小程序、支付宝小程序、钉钉小程序、H5、APP 等.
- 🚀 80+ 个高质量组件,覆盖移动端主流场景.
- 💪 使用 Typescript 构建,提供良好的组件类型系统.
- 🤖 提供 AI 友好的设计系统.
- 🌍 支持国际化,内置 15 种语言包.
- 📖 提供丰富的文档和组件示例.
- 🎨 支持修改 CSS 变量实现主题定制.
- 🍭 支持暗黑模式
## 赞助我们
如果您认为 Wot UI 帮助到了您的开发工作,您可以选择[赞助](https://wot-ui.cn/reward/reward.html)我们,赞助无门槛,哪怕是一杯柠檬水也好。
捐赠后您的昵称、留言等将会展示在[捐赠榜单](https://wot-ui.cn/reward/donor.html)中。
## 生态
| 分类 | 项目 | 描述 |
| --- | --- | --- |
| 官方生态 | [wot-starter](https://starter.wot-ui.cn/) | Wot UI 官方快速起手项目 |
| 官方生态 | [@wot-ui/router](https://my-uni.wot-ui.cn/) | Wot UI 官方路由与工程能力扩展 |
| 官方生态 | [@wot-ui/cli](https://github.com/wot-ui/open-wot) | Wot UI 官方 AI 工具链与 CLI |
| 官方生态 | [@wot-ui/unocss-preset](https://github.com/wot-ui/unocss-preset) | Wot UI 官方 UnoCSS 预设 |
| 官方生态 | [VS Code 插件](https://marketplace.visualstudio.com/items?itemName=wot-ui.wot-ui-intellisense) | Wot UI 官方 VS Code 代码提示插件 |
| 官方生态 | [小程序 CI 工具](https://github.com/Moonofweisheng/uni-mini-ci) | Wot UI 官方推荐的小程序 CI 工具 |
| 官方生态 | [wot-starter-retail](https://github.com/wot-ui/wot-starter-retail) | Wot UI 官方零售行业模板方案 |
| 开发资源 | [awesome-uni-app](https://github.com/uni-helper/awesome-uni-app) | 多端统一开发框架 uni-app 优秀开发资源汇总 |
| 开发资源 | [create-uni](https://github.com/uni-helper/create-uni) | 快速创建 uni-app 项目 |
| 开发资源 | [uni-ku](https://github.com/uni-ku) | uni-app 生态扩展与工具集合 |
| 开发资源 | [uni-echarts](https://uni-echarts.xiaohe.ink/) | uni-app 图表组件与接入方案 |
| 模板方案 | [vitesse-uni-app](https://vitesse-docs.netlify.app/) | 现代化 uni-app 基础模板 |
| 模板方案 | [unibest](https://unibest.tech/) | 功能完善的 uni-app 开发模板 |
## 鸣谢
- [wot-design](https://github.com/jd-ftf/wot-design-mini) - 感谢 wot-design 团队多年来的不断维护,让 wot-ui 能够站在巨人的肩膀上。
- [uni-helper](https://github.com/uni-helper) - 感谢 uni-helper 团队提供的 uni-app 工具库,让 wot-ui 能够更方便地使用。
- [捐赠者](https://wot-ui.cn/reward/donor.html) - 感谢所有捐赠者,是你们的捐赠让 wot-ui 能够更好地发展。
## 开源协议
本项目基于 [MIT](https://zh.wikipedia.org/wiki/MIT%E8%A8%B1%E5%8F%AF%E8%AD%89) 协议,请自由地享受和参与开源。
-64
View File
@@ -1,64 +0,0 @@
---
layout: home
hero:
name: "Wot Starter"
text: "飞一般体验的 \nuni-app 模板"
tagline: 基于 vitesse-uni-app 深度整合 Wot UI,背靠 Uni Helper、Wot UI 团队,告别 HBuilderX,拥抱现代前端开发工具链,让你拥有飞一般的开发体验
image:
src: /logo.svg
alt: Wot UI
actions:
- theme: brand
text: 快速开始
link: guide/installation
- theme: brand
text: 关于作者
link: https://blog.wot-ui.cn/about
- theme: alt
text: 查看演示
link: https://starter.wot-ui.cn/demo/#/
- theme: brand
text: 🥤一杯咖啡
link: https://wot-ui.cn/reward/reward
features:
- title: Wot UI
details: 高颜值、轻量化组件库
icon: ⚡️
link: "https://wot-ui.cn/"
- title: Uni Helper 插件
details: 核心插件库,极大提升了 uni-app 的开发体验
icon: 📦
link: "https://uni-helper.cn/"
- title: Uni Ku 插件
details: 非常酷的 uni-app 插件库
icon: 🆒
link: "https://uni-ku.js.org/"
- title: Uni ECharts
details: 适用于 uni-app 的 Apache ECharts 组件
icon: 📊
link: "https://uni-echarts.xiaohe.ink"
- title: Alova
details: 极致高效的请求工具集
icon: 🌐
link: "https://alova.js.org/zh-CN/"
- title: 摸鱼路由库
details: 轻量级 uni-app 路由库
icon: 🚦
link: "https://my-uni.wot-ui.cn"
- title: uni-mini-ci
details: 小程序持续集成的插件
icon: 🔄
link: "https://github.com/Moonofweisheng/uni-mini-ci"
- title: 原子化 CSS
details: 高性能且极具灵活性的即时原子化 CSS 引擎
icon: 🎨
link: "https://github.com/uni-helper/unocss-preset-uni"
- title: 100000+ 图标
details: 各种图标集为你所用
icon: 😃
link: "https://icones.js.org/"
footer: false
---
-30
View File
@@ -1,30 +0,0 @@
{
"name": "docs",
"version": "1.0.0",
"packageManager": "pnpm@9.9.0",
"description": "基于vitesse-uni-app的深度整合 Wot UI 组件库的快速起手项目",
"author": "",
"license": "MIT",
"keywords": [],
"scripts": {
"docs:dev": "vitepress dev",
"docs:build": "vitepress build",
"docs:preview": "vitepress preview",
"type-check": "tsc --noEmit"
},
"dependencies": {
"@vueuse/core": "^13.4.0",
"@wot-ui/vitepress-theme": "^2.0.0",
"axios": "^1.13.1",
"dayjs": "^1.11.20",
"vue": "^3.5.17"
},
"devDependencies": {
"@iconify-json/carbon": "^1.2.10",
"@types/node": "^24.0.4",
"@unocss/vite": "^66.3.2",
"typescript": "^5.8.3",
"unocss": "^66.3.2",
"vitepress": "2.0.0-alpha.17"
}
}
-4
View File
@@ -1,4 +0,0 @@
/llms-full.md /llms-full.txt 200!
/llms-full.txt /llms-full.txt 200!
/llms.md /llms.txt 200!
/llms.txt /llms.txt 200!
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

-5
View File
@@ -1,5 +0,0 @@
<svg width="280" height="280" viewBox="0 0 280 280" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M27.5 57.4742C27.5 54.2794 31.0606 52.3738 33.7188 54.146L70.7188 78.8133C71.8316 79.5552 72.5 80.8041 72.5 82.1415V137.86C72.5 139.197 71.8316 140.446 70.7188 141.188L33.7188 165.854C31.0605 167.626 27.5 165.721 27.5 162.526V57.4742Z" fill="#12B886"/>
<path d="M160.719 63.8136C161.832 64.5554 162.5 65.8043 162.5 67.1417V152.86C162.5 154.198 161.832 155.447 160.719 156.188L119.281 183.813C118.168 184.554 117.5 185.803 117.5 187.141V197.861C117.5 199.199 116.832 200.448 115.719 201.189L78.7187 225.855C76.0605 227.627 72.5 225.722 72.5 222.527V187.143C72.5 185.805 73.1684 184.556 74.2811 183.815L115.719 156.188C116.832 155.447 117.5 154.198 117.5 152.86V42.4743C117.5 39.2794 121.061 37.3739 123.719 39.1461L160.719 63.8136Z" fill="#1C64FD"/>
<path d="M250.956 53.8C251.93 54.5578 252.5 55.723 252.5 56.9574V163.045C252.5 164.279 251.93 165.444 250.956 166.202L209.571 198.389C208.578 199.161 208.006 200.355 208.027 201.613L208.464 227.817C208.487 229.178 207.816 230.457 206.683 231.212L169.596 255.936C166.963 257.692 163.431 255.839 163.378 252.675L162.537 202.185C162.514 200.824 163.185 199.545 164.317 198.79L205.719 171.188C206.832 170.446 207.5 169.197 207.5 167.859V28.1788C207.5 24.8502 211.329 22.9778 213.956 25.0214L250.956 53.8Z" fill="#1CB2FD"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

-39
View File
@@ -1,39 +0,0 @@
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"target": "ES2020",
"jsx": "preserve",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"baseUrl": ".",
"module": "ESNext",
"moduleResolution": "bundler",
"paths": {
"@/*": ["./*"],
"~/*": ["./.vitepress/theme/*"]
},
"resolveJsonModule": true,
"types": ["node", "vitepress/client"],
"allowImportingTsExtensions": true,
"strict": true,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"noEmit": true,
"isolatedModules": true,
"skipLibCheck": true
},
"include": [
".vitepress/**/*",
"**/*.vue",
"**/*.ts",
"**/*.mts",
"**/*.tsx",
"**/*.md",
"env.d.ts"
],
"exclude": [
"node_modules",
".vitepress/dist",
".vitepress/cache"
]
}
-76
View File
@@ -1,76 +0,0 @@
import {
defineConfig,
presetAttributify,
presetIcons,
presetTypography,
presetUno,
transformerDirectives,
transformerVariantGroup,
} from 'unocss'
export default defineConfig({
shortcuts: [
// 快捷方式(移除响应式相关的快捷方式)
['btn', 'px-4 py-1 rounded inline-block bg-teal-600 text-white cursor-pointer hover:bg-teal-700 disabled:cursor-default disabled:bg-gray-600 disabled:opacity-50'],
['btn-primary', 'bg-blue-500 hover:bg-blue-600 text-white'],
['btn-secondary', 'bg-gray-500 hover:bg-gray-600 text-white'],
['icon-btn', 'text-[0.9em] inline-block cursor-pointer select-none opacity-75 transition duration-200 ease-in-out hover:opacity-100 hover:text-teal-600 !outline-none'],
['card', 'bg-[var(--wot-filled-oppo)] rounded-lg shadow-md p-6 border border-gray-200'],
['card-dark', 'bg-gray-800 rounded-lg shadow-md p-6 border border-gray-700'],
['grid-simple', 'grid grid-cols-3 gap-6'],
['feature-card', 'bg-[var(--wot-filled-oppo)] dark:bg-gray-800 p-6 rounded-lg shadow-md border border-gray-200 dark:border-gray-700'],
['custom-container', 'max-w-screen-xl mx-auto px-4'],
['custom-container-sm', 'max-w-screen-md mx-auto px-4'],
],
presets: [
presetUno({
// 禁用响应式断点
dark: 'media', // 保留暗色模式
}),
presetAttributify(),
presetIcons({
scale: 1.2,
warn: true,
}),
presetTypography(),
],
// 排除响应式相关的规则
blocklist: [
// 屏蔽响应式断点相关的类
/^(sm|md|lg|xl|2xl):/,
// 屏蔽网格布局相关的类
/^col-span-/,
/^col-start-/,
/^col-end-/,
// 屏蔽 flexbox 响应式类
/^(sm|md|lg|xl|2xl):flex/,
/^(sm|md|lg|xl|2xl):grid/,
// 屏蔽响应式间距类
/^(sm|md|lg|xl|2xl):(m|p)[xytrbl]?-/,
// 屏蔽响应式宽高类
/^(sm|md|lg|xl|2xl):(w|h)-/,
// 屏蔽容器类,避免与 VitePress 冲突
'container',
],
transformers: [
transformerDirectives(),
transformerVariantGroup(),
],
theme: {
colors: {
primary: {
50: '#eff6ff',
100: '#dbeafe',
200: '#bfdbfe',
300: '#93c5fd',
400: '#60a5fa',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
800: '#1e40af',
900: '#1e3a8a',
950: '#172554',
},
},
},
})
-17
View File
@@ -1,17 +0,0 @@
import uni from '@uni-helper/eslint-config'
export default uni(
{
unocss: true,
rules: {
'no-console': 'off',
'eslint-comments/no-unlimited-disable': 'off',
},
ignores: [
'src/uni_modules/**/*',
'docs/.vitepress/dist',
'docs/.vitepress/cache',
'**/*.md',
],
},
)
-21
View File
@@ -1,21 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<link rel="icon" href="static/favicon.ico">
<script>
const coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)')
|| CSS.supports('top: constant(a)'))
document.write(
`<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0${
coverSupport ? ', viewport-fit=cover' : ''}" />`)
</script>
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
-126
View File
@@ -1,126 +0,0 @@
/*
* @Author: weisheng
* @Date: 2025-08-28 20:59:43
* @LastEditTime: 2025-11-17 14:28:09
* @LastEditors: weisheng
* @Description:
* @FilePath: /wot-starter/manifest.config.ts
* 记得注释
*/
import { defineManifestConfig } from '@uni-helper/vite-plugin-uni-manifest'
export default defineManifestConfig({
'name': 'wot-starter',
'appid': '__UNI__1208592',
'description': '',
'versionName': '1.0.0',
'versionCode': '100',
'transformPx': false,
/* 5+App特有相关 */
'app-plus': {
usingComponents: true,
nvueStyleCompiler: 'uni-app',
compilerVersion: 3,
splashscreen: {
alwaysShowBeforeRender: true,
waiting: true,
autoclose: true,
delay: 0,
},
/* 模块配置 */
modules: {},
/* 应用发布信息 */
distribute: {
/* android打包配置 */
android: {
permissions: [
'<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>',
'<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>',
'<uses-permission android:name="android.permission.VIBRATE"/>',
'<uses-permission android:name="android.permission.READ_LOGS"/>',
'<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>',
'<uses-feature android:name="android.hardware.camera.autofocus"/>',
'<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>',
'<uses-permission android:name="android.permission.CAMERA"/>',
'<uses-permission android:name="android.permission.GET_ACCOUNTS"/>',
'<uses-permission android:name="android.permission.READ_PHONE_STATE"/>',
'<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>',
'<uses-permission android:name="android.permission.WAKE_LOCK"/>',
'<uses-permission android:name="android.permission.FLASHLIGHT"/>',
'<uses-feature android:name="android.hardware.camera"/>',
'<uses-permission android:name="android.permission.WRITE_SETTINGS"/>',
],
},
/* ios打包配置 */
ios: {},
/* SDK配置 */
sdkConfigs: {},
},
},
/* 快应用特有相关 */
'quickapp': {},
/* 小程序特有相关 */
'mp-weixin': {
optimization: {
subPackages: true,
},
// TODO: 替换为实际的微信小程序 appid(在微信公众平台 → 开发管理 → 开发设置 中获取)
appid: 'wx0000000000000000',
setting: {
urlCheck: false,
// 压缩代码
minified: true,
// 启用 ES6 转 ES5
es6: true,
// 启用样式补全
postcss: true,
// 启用自动注入 wxss 文件
minifyWXML: true,
},
usingComponents: true,
darkmode: true,
themeLocation: 'theme.json',
// 微信小程序权限配置
permission: {
// 保存图片到相册
'scope.writePhotosAlbum': {
desc: '用于保存分享海报到相册',
},
// 获取用户位置(如需要)
'scope.userLocation': {
desc: '用于获取您的地理位置信息',
},
},
// 接口权限声明(微信小程序隐私合规)
requiredPrivateInfos: [
'getLocation',
],
// 订阅消息模板配置(在微信公众平台 → 订阅消息 中配置后填入)
// subscribeMessage: {
// tmplIds: ['', '', ''],
// },
},
'app-harmony': {},
'mp-harmony': {},
'mp-alipay': {
usingComponents: true,
compileOptions: {
globalObjectMode: 'enable',
treeShaking: true,
},
},
'mp-baidu': {
usingComponents: true,
},
'mp-toutiao': {
usingComponents: true,
},
'h5': {
darkmode: true,
themeLocation: 'theme.json',
},
'uniStatistics': {
enable: false,
},
'vueVersion': '3',
})
-148
View File
@@ -1,148 +0,0 @@
{
"name": "fastapp",
"type": "module",
"version": "3.1.0",
"private": true,
"packageManager": "pnpm@9.9.0",
"license": "MIT",
"engines": {
"node": ">=20.19.0 || >=22.12.0 || >=24.0.0"
},
"scripts": {
"dev": "uni",
"dev:app": "uni -p app",
"dev:app-android": "uni -p app-android",
"dev:app-ios": "uni -p app-ios",
"dev:custom": "uni -p",
"dev:h5": "uni",
"dev:h5:ssr": "uni --ssr",
"dev:h5:development": "uni --mode development",
"dev:h5:staging": "uni --mode staging",
"dev:h5:production": "uni --mode production",
"dev:mp-alipay": "uni -p mp-alipay",
"dev:mp-baidu": "uni -p mp-baidu",
"dev:mp-kuaishou": "uni -p mp-kuaishou",
"dev:mp-lark": "uni -p mp-lark",
"dev:mp-qq": "uni -p mp-qq",
"dev:mp-toutiao": "uni -p mp-toutiao",
"dev:mp-weixin": "uni -p mp-weixin",
"dev:quickapp-webview": "uni -p quickapp-webview",
"dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
"dev:quickapp-webview-union": "uni -p quickapp-webview-union",
"build": "uni build",
"build:app": "uni build -p app",
"build:app-android": "uni build -p app-android",
"build:app-ios": "uni build -p app-ios",
"build:custom": "uni build -p",
"build:h5": "uni build",
"build:h5:ssr": "uni build --ssr",
"build:h5:development": "uni build --mode development",
"build:h5:staging": "uni build --mode staging",
"build:h5:production": "uni build --mode production",
"build:mp-alipay": "uni build -p mp-alipay",
"build:mp-baidu": "uni build -p mp-baidu",
"build:mp-kuaishou": "uni build -p mp-kuaishou",
"build:mp-lark": "uni build -p mp-lark",
"build:mp-qq": "uni build -p mp-qq",
"build:mp-toutiao": "uni build -p mp-toutiao",
"build:mp-weixin": "uni build -p mp-weixin",
"build:quickapp-webview": "uni build -p quickapp-webview",
"build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
"build:quickapp-webview-union": "uni build -p quickapp-webview-union",
"type-check": "vue-tsc --noEmit",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"lint:docs": "eslint docs --ext .js,.ts,.vue,.mts,.cts --ignore-pattern '**/*.md'",
"prepare": "cd ../.. && husky install frontend/app/.husky",
"commit": "git-cz",
"release-major": "npm version major",
"release-minor": "npm version minor",
"release-patch": "npm version patch",
"docs:dev": "pnpm -F docs docs:dev",
"docs:build": "pnpm run build:h5 && pnpm -F docs docs:build",
"docs:preview": "pnpm -F docs docs:preview"
},
"dependencies": {
"@alova/adapter-uniapp": "^2.0.16",
"@alova/shared": "^1.3.2",
"@dcloudio/uni-app": "3.0.0-5010520260709002",
"@dcloudio/uni-app-harmony": "3.0.0-5010520260709002",
"@dcloudio/uni-app-plus": "3.0.0-5010520260709002",
"@dcloudio/uni-components": "3.0.0-5010520260709002",
"@dcloudio/uni-h5": "3.0.0-5010520260709002",
"@dcloudio/uni-mp-alipay": "3.0.0-5010520260709002",
"@dcloudio/uni-mp-baidu": "3.0.0-5010520260709002",
"@dcloudio/uni-mp-harmony": "3.0.0-5010520260709002",
"@dcloudio/uni-mp-jd": "3.0.0-5010520260709002",
"@dcloudio/uni-mp-kuaishou": "3.0.0-5010520260709002",
"@dcloudio/uni-mp-lark": "3.0.0-5010520260709002",
"@dcloudio/uni-mp-qq": "3.0.0-5010520260709002",
"@dcloudio/uni-mp-toutiao": "3.0.0-5010520260709002",
"@dcloudio/uni-mp-weixin": "3.0.0-5010520260709002",
"@dcloudio/uni-mp-xhs": "3.0.0-5010520260709002",
"@dcloudio/uni-quickapp-webview": "3.0.0-5010520260709002",
"@vueuse/core": "^11.0.3",
"@wot-ui/router": "^1.1.2",
"@wot-ui/ui": "^2.2.0",
"@wot-ui/unocss-preset": "0.0.1-beta.4",
"alova": "^3.5.1",
"echarts": "^6.0.0",
"pinia": "^2.3.1",
"uni-echarts": "^2.2.5",
"vue": "~3.4.38",
"vue-i18n": "9.1.9"
},
"devDependencies": {
"@alova/wormhole": "^1.5.0",
"@commitlint/cli": "^19.5.0",
"@commitlint/config-conventional": "^19.5.0",
"@dcloudio/types": "3.4.31",
"@dcloudio/uni-cli-shared": "3.0.0-5010520260709002",
"@dcloudio/uni-stacktracey": "3.0.0-5010520260709002",
"@dcloudio/uni-vue-devtools": "3.0.0-4020420240722002",
"@dcloudio/vite-plugin-uni": "3.0.0-5010520260709002",
"@iconify-json/carbon": "^1.1.37",
"@mini-types/alipay": "^3.0.14",
"@types/node": "^20.16.2",
"@uni-helper/eslint-config": "^0.5.0",
"@uni-helper/plugin-uni": "^0.1.0",
"@uni-helper/uni-env": "^0.2.0",
"@uni-helper/uni-types": "^1.1.0",
"@uni-helper/unocss-preset-uni": "^0.2.11",
"@uni-helper/vite-plugin-uni-components": "^0.2.6",
"@uni-helper/vite-plugin-uni-layouts": "^0.1.11",
"@uni-helper/vite-plugin-uni-manifest": "^0.2.12",
"@uni-helper/vite-plugin-uni-pages": "^0.3.23",
"@uni-helper/volar-service-uni-pages": "^0.3.24",
"@uni-ku/bundle-optimizer": "^2.1.0",
"@uni-ku/root": "^1.4.1",
"@unocss/eslint-config": "^66.5.6",
"@vue/runtime-core": "^3.4.38",
"@vue/tsconfig": "^0.5.1",
"commitizen": "^4.3.1",
"cz-conventional-changelog": "^3.3.0",
"eslint": "^9.39.1",
"git-cz": "^4.9.0",
"husky": "^8.0.3",
"lint-staged": "^15.2.9",
"miniprogram-api-typings": "^3.12.3",
"sass": "^1.99.0",
"standard-version": "^9.5.0",
"typescript": "~5.5.4",
"unocss": "66.0.0",
"unplugin-auto-import": "^0.18.2",
"vite": "^5.2.8",
"vue-tsc": "^2.0.29"
},
"pnpm": {
"overrides": {
"unconfig": "7.3.2"
},
"patchedDependencies": {
"vue-i18n": "patches/vue-i18n.patch"
}
},
"lint-staged": {
"*": "eslint --fix"
}
}
-54
View File
@@ -1,54 +0,0 @@
/*
* @Author: weisheng
* @Date: 2025-06-23 22:23:05
* @LastEditTime: 2025-06-27 13:04:54
* @LastEditors: weisheng
* @Description:
* @FilePath: /wot-starter/pages.config.ts
* 记得注释
*/
import { defineUniPages } from '@uni-helper/vite-plugin-uni-pages'
export default defineUniPages({
pages: [],
globalStyle: {
// 导航栏配置
navigationBarBackgroundColor: '@navBgColor',
navigationBarTextStyle: '@navTxtStyle',
navigationBarTitleText: 'Wot Starter',
// 页面背景配置
backgroundColor: '@bgColor',
backgroundTextStyle: '@bgTxtStyle',
backgroundColorTop: '@bgColorTop',
backgroundColorBottom: '@bgColorBottom',
// 下拉刷新配置
enablePullDownRefresh: false,
onReachBottomDistance: 50,
// 动画配置
animationType: 'pop-in',
animationDuration: 300,
},
tabBar: {
custom: true,
// #ifdef MP-ALIPAY
customize: true,
// 暂时不生效。4.71.2025061206-alpha已修复:https://uniapp.dcloud.net.cn/release-note-alpha.html#_4-71-2025061206-alpha,我们等正式版发布后更新。
overlay: true,
// #endif
height: '0',
color: '@tabColor',
selectedColor: '@tabSelectedColor',
backgroundColor: '@tabBgColor',
borderStyle: '@tabBorderStyle',
list: [{
pagePath: 'pages/index/index',
}, {
pagePath: 'pages/work/index',
}, {
pagePath: 'pages/mine/index',
}],
},
})
-35
View File
@@ -1,35 +0,0 @@
diff --git a/dist/vue-i18n.esm-bundler.js b/dist/vue-i18n.esm-bundler.js
index 7dc04f5024352d5900c70cd9b58a12dfe74400d3..076959735fe10f663ea4a5249534699a055610b2 100644
--- a/dist/vue-i18n.esm-bundler.js
+++ b/dist/vue-i18n.esm-bundler.js
@@ -23,30 +23,18 @@ const VERSION = '9.1.9';
* istanbul-ignore-next
*/
function initFeatureFlags() {
- let needWarn = false;
if (typeof __VUE_I18N_FULL_INSTALL__ !== 'boolean') {
- needWarn = true;
getGlobalThis().__VUE_I18N_FULL_INSTALL__ = true;
}
if (typeof __VUE_I18N_LEGACY_API__ !== 'boolean') {
- needWarn = true;
getGlobalThis().__VUE_I18N_LEGACY_API__ = true;
}
if (typeof __VUE_I18N_PROD_DEVTOOLS__ !== 'boolean') {
- needWarn = true;
getGlobalThis().__VUE_I18N_PROD_DEVTOOLS__ = false;
}
if (typeof __INTLIFY_PROD_DEVTOOLS__ !== 'boolean') {
getGlobalThis().__INTLIFY_PROD_DEVTOOLS__ = false;
}
- if ((process.env.NODE_ENV !== 'production') && typeof true === 'boolean') {
- needWarn = true;
- }
- if ((process.env.NODE_ENV !== 'production') && needWarn) {
- console.warn(`You are running the esm-bundler build of vue-i18n. It is recommended to ` +
- `configure your bundler to explicitly replace feature flag globals ` +
- `with boolean literals to get proper tree-shaking in the final bundle.`);
- }
}
const warnMessages = {
-16647
View File
File diff suppressed because it is too large Load Diff
-4
View File
@@ -1,4 +0,0 @@
packages:
- docs
- packages/*
- apps/*
-3
View File
@@ -1,3 +0,0 @@
{
"extends": ["github>uni-helper/renovate-config"]
}
-25
View File
@@ -1,25 +0,0 @@
{
"version": 1,
"skills": {
"create-wot-ui-theme": {
"source": "wot-ui/open-wot",
"sourceType": "github",
"computedHash": "0557819ce03183457bd196af213996963b5713641bf76546306d826501bab188"
},
"wot-ui-cli": {
"source": "wot-ui/open-wot",
"sourceType": "github",
"computedHash": "b2e370a30ff8f1e31c4b1a0ac91d16928d6fc90e68f02913e659986c94d88b0c"
},
"wot-ui-unocss-preset-guide": {
"source": "wot-ui/open-wot",
"sourceType": "github",
"computedHash": "bf1f6f752d0fef15063b56df11a902ff110ae177d867cef76b953ef237c05a70"
},
"wot-ui-v2": {
"source": "wot-ui/open-wot",
"sourceType": "github",
"computedHash": "207723b8a0e23cb5657ce9dae012ab92c0d6397212d56b07fafbdbc1ba9c3c2e"
}
}
}
-25
View File
@@ -1,25 +0,0 @@
<script setup lang="ts">
import type { ConfigProviderProps } from '@wot-ui/ui/components/wd-config-provider/types'
const { themeVars, theme } = useManualTheme()
const buttonConfig: ConfigProviderProps['button'] = {
size: 'large',
}
</script>
<template>
<wd-config-provider :theme-vars="themeVars" :theme="theme" :button="buttonConfig" :custom-class="`page-wraper ${theme}`">
<ku-root-view />
<wd-notify />
<wd-dialog />
<wd-toast />
<global-loading />
<global-toast />
<global-message />
<global-dialog />
<!-- #ifdef MP-WEIXIN -->
<privacy-popup />
<!-- #endif -->
</wd-config-provider>
</template>
-31
View File
@@ -1,31 +0,0 @@
<script setup lang="ts">
onLaunch(() => {})
</script>
<style lang="scss">
@use '@wot-ui/ui/styles/theme/index.scss' as *;
.page-wraper {
min-height: calc(100vh - var(--window-top));
box-sizing: border-box;
background: var(--wot-filled-content);
}
/* ===== 暗色模式全局品牌增强(一处生效所有页面) =====
极光氛围:页面背景叠加品牌光晕(顶部主色蓝 + 底部紫),卡片叠加微弱蓝光。
仅作用于暗色模式(.wot-theme-dark 挂在 wd-config-provider 根上),
亮色模式完全保持 wot 默认,不改动任何布局结构。 */
.wot-theme-dark .page-wraper {
background-color: var(--wot-filled-content);
background-image:
radial-gradient(ellipse 120% 45% at 50% -12%, rgba(79, 140, 255, 0.12), transparent 65%),
radial-gradient(ellipse 80% 30% at 100% 105%, rgba(139, 92, 246, 0.08), transparent 70%);
background-repeat: no-repeat;
}
/* 卡片微光:在 wot 的 filled-oppo 底色上叠极淡的蓝紫光晕,增强玻璃拟态层次 */
.wot-theme-dark .wot-bg-filled-oppo {
background-color: var(--wot-filled-oppo);
background-image: radial-gradient(ellipse 90% 55% at 85% -25%, rgba(79, 140, 255, 0.05), transparent 60%);
background-repeat: no-repeat;
}
</style>
-86
View File
@@ -1,86 +0,0 @@
/**
* AI 聊天 API 模块(对应后端 module_ai 插件)
*/
import { http } from '@/http'
const AI_BASE = '/ai/chat'
export const ChatAPI = {
/** 获取会话列表 */
getSessions(params?: Record<string, any>): Promise<PageResult<ChatSession>> {
return http.Get(`${AI_BASE}/list`, params)
},
/** 创建会话 */
createSession(title?: string): Promise<ChatSession> {
return http.Post(`${AI_BASE}/create`, { title: title || '新对话' })
},
/** 获取会话详情(含消息列表) */
getDetail(sessionId: number): Promise<{ messages: ChatMessage[] }> {
return http.Get(`${AI_BASE}/detail/${sessionId}`)
},
/** 更新会话标题 */
updateSession(sessionId: number, data: Record<string, any>): Promise<void> {
return http.Put(`${AI_BASE}/update/${sessionId}`, data)
},
/** 删除会话 */
removeSession(ids: number[]): Promise<void> {
return http.Delete(`${AI_BASE}/delete`, { ids: JSON.stringify(ids) })
},
/** 发送消息 (非流式) */
sendMessage(sessionId: number, content: string): Promise<ChatMessage> {
// silent:聊天页将错误内联展示为 AI 消息,避免与全局 toast 重复提示
return http.Post(`${AI_BASE}/ai-chat`, { session_id: sessionId, content }, { meta: { silent: true } })
},
/** 获取 AI 模型配置列表 */
getModels(): Promise<AIModelConfig[]> {
return http.Get(`${AI_BASE}/model`)
},
/** 新增 AI 模型配置 */
createModel(data: AIModelForm): Promise<AIModelConfig> {
return http.Post(`${AI_BASE}/model`, data)
},
/** 更新 AI 模型配置 */
updateModel(configId: number, data: AIModelForm): Promise<AIModelConfig> {
return http.Put(`${AI_BASE}/model/${configId}`, data)
},
/** 删除 AI 模型配置 */
deleteModel(configId: number): Promise<void> {
return http.Delete(`${AI_BASE}/model/${configId}`)
},
/** 切换激活的 AI 模型配置 */
activateModel(configId: number): Promise<void> {
return http.Post(`${AI_BASE}/model/${configId}/activate`)
},
}
/* ==================== 类型定义 ==================== */
export interface ChatSession {
id: number
session_name?: string
title?: string
created_at?: number
}
export interface ChatMessage {
id?: string
role: string
content: string
created_at?: number
time?: string
}
export interface AIModelForm {
name: string
base_url: string
api_key: string
model_id: string
temperature?: number
}
export interface AIModelConfig extends AIModelForm {
config_id: number
is_active?: boolean
created_time?: string
updated_time?: string
}
@@ -1,39 +0,0 @@
import { http } from '@/http'
const MONITOR_BASE = '/monitor'
/**
* 运营大盘 API
* 与 web 端 module_monitor/dashboard.ts 对齐
*/
export const DashboardAPI = {
getStats(): Promise<DashboardStats> {
return http.Get(`${MONITOR_BASE}/online/stats`)
},
}
export interface RecentLoginItem {
username: string
status: number
login_time: string
login_ip?: string
login_location?: string
}
export interface DashboardStats {
online_users: number
total_users: number
today_login_count: number
today_unique_users: number
week_user_created: number
login_trend: LoginTrendItem[]
recent_logins: RecentLoginItem[]
}
/** 登录趋势(按天聚合,近7天) */
export interface LoginTrendItem {
day: string
logins: number
unique_users: number
new_users: number
}
@@ -1,71 +0,0 @@
import { http } from '@/http'
const MONITOR_BASE = '/monitor'
/**
* 服务器监控 API
* 与 web 端 module_monitor/server.ts 对齐(补全完整字段)
*/
export const ServerAPI = {
getInfo(): Promise<ServerInfo> {
return http.Get(`${MONITOR_BASE}/server/info`)
},
}
export interface CpuInfo {
/** 逻辑核心数 */
cpu_num: number
/** user 占用百分比 */
used: number
/** system 占用百分比 */
sys: number
/** idle 百分比 */
free: number
}
export interface MemoryInfo {
total: string
used: string
free: string
usage: number
}
export interface DiskInfo {
dir_name: string
sys_type_name: string
type_name: string
total: string
used: string
free: string
/** 使用率百分比 */
usage: number
}
export interface SysInfo {
computer_ip: string
computer_name: string
os_arch: string
os_name: string
user_dir: string
}
export interface PyInfo {
name: string
version: string
start_time: string
run_time: string
home: string
memory_total: string
memory_used: string
memory_free: string
/** 进程内存占用率百分比(rss / available */
memory_usage: number
}
export interface ServerInfo {
cpu?: CpuInfo
mem?: MemoryInfo
sys?: SysInfo
py?: PyInfo
disks?: DiskInfo[]
}
-176
View File
@@ -1,176 +0,0 @@
import { http } from '@/http'
import { ContentTypeEnum } from '@/http/tools/enum'
const AUTH_BASE_URL = '/system/auth'
/** 方案提供方 */
export type OAuthProvider = 'wechat' | 'qq' | 'github' | 'gitee'
/**
* 认证 API
* 与 web 端 module_system/auth.ts 对齐(完整字段定义)
*/
const AuthAPI = {
/**
* 登录
* @param body 登录表单数据
* @returns 登录结果
*/
login(body: LoginFormData): Promise<LoginResult> {
return http.Post(`${AUTH_BASE_URL}/login`, body, {
headers: {
'Content-Type': ContentTypeEnum.FORM_URLENCODED,
},
meta: { ignoreAuth: true },
})
},
/**
* 刷新令牌
* @param body 刷新令牌请求体
* @returns 新的访问令牌
*/
refreshToken(body: RefreshToekenBody): Promise<LoginResult> {
// silent:刷新失败由 onAuthRequired 的刷新处理器统一跳转登录,无需全局 toast
return http.Post(`${AUTH_BASE_URL}/token/refresh`, body, { meta: { ignoreAuth: true, silent: true } })
},
/**
* 获取验证码
* @returns 验证码信息
*/
getCaptcha(): Promise<CaptchaInfo> {
// 添加随机参数防止缓存
const timestamp = new Date().getTime()
return http.Get(`${AUTH_BASE_URL}/captcha/get?timestamp=${timestamp}`, { meta: { ignoreAuth: true } })
},
/**
* 登出
* 后端 logout 接口 body 为纯字符串(JWT 原文,Annotated[str, Body]),
* 需显式 JSON.stringify 使请求体成为合法 JSON 字符串(uni.request 对字符串原样发送)
* @param token 访问令牌
*/
logout(token: string): Promise<void> {
return http.Post(`${AUTH_BASE_URL}/logout`, JSON.stringify(token))
},
/**
* 获取第三方 OAuth 登录跳转 URL
* @param provider oauth 提供商: wechat / qq / github / gitee
* @returns 跳转 URL
*/
getOAuthLoginUrl(provider: OAuthProvider): Promise<{ url: string }> {
return http.Get(`${AUTH_BASE_URL}/oauth/${provider}/login`, { meta: { ignoreAuth: true } })
},
/**
* 滑块验证码完成
* 后端仅标记 captcha_key 状态为 verified,不校验 x 坐标值(x 为占位字段)
* @param data 验证数据
* @param data.captcha_key 验证码 key
* @param data.x 滑块 x 坐标(占位,后端未使用)
* @returns 验证结果 { captcha_key, verified }
*/
completeSliderCaptcha(data: { captcha_key: string, x: number }): Promise<{ captcha_key: string, verified: boolean }> {
return http.Post(`${AUTH_BASE_URL}/captcha/slider/complete`, data, { meta: { ignoreAuth: true } })
},
/**
* 微信小程序登录
* 前端通过 uni.login 获取 code,后端调用 code2Session 换取 openid 后返回 JWT
* @param data 微信登录数据
* @param data.code uni.login 返回的 code
* @param data.nickname 用户昵称(可选,来自 getUserProfile
* @param data.avatar 头像 URL(可选)
* @returns JWT 登录结果
*/
wxLogin(data: WxLoginData): Promise<LoginResult> {
return http.Post(`${AUTH_BASE_URL}/wx-login`, data, { meta: { ignoreAuth: true } })
},
/**
* 微信小程序手机号快速登录
* 用户点击<button open-type="getPhoneNumber">后,回调 e.detail.code 发送给后端
* 后端通过 getuserphonenumber API 直接获取手机号(2023+ 新方案,无需 AES 解密)
* @param data 手机号登录数据
* @param data.code getPhoneNumber 回调返回的动态令牌 code
* @returns JWT 登录结果
*/
wxPhoneLogin(data: WxPhoneLoginData): Promise<LoginResult> {
return http.Post(`${AUTH_BASE_URL}/wx-phone-login`, data, { meta: { ignoreAuth: true } })
},
/**
* 生成小程序码
* 调用后端接口,后端通过微信 getWXACodeUnlimit API 生成无限制小程序码
* @param data 生成参数
* @param data.scene 场景参数(最大32字符,如 invite_123
* @param data.page 小程序页面路径(可选,默认主页)
* @param data.width 图片宽度(px,默认 430
* @returns 包含 base64 图片 URL 的结果
*/
generateWxQrCode(data: WxQrCodeParams): Promise<WxQrCodeResult> {
return http.Post(`${AUTH_BASE_URL}/wx-qrcode/generate`, data)
},
}
export default AuthAPI
/** 登录表单数据 */
export interface LoginFormData {
username: string
password: string
captcha_key?: string
captcha?: string
remember?: boolean
login_type?: string
}
/** 刷新令牌请求体 */
export interface RefreshToekenBody {
refresh_token: string
}
/** JWT 响应 */
export interface LoginResult {
access_token: string
refresh_token: string
token_type: string
expires_in: number
}
/** 验证码信息(滑块模式:img_base 为空字符串) */
export interface CaptchaInfo {
enable: boolean
key: string
img_base: string
}
/** 微信小程序登录数据 */
export interface WxLoginData {
code: string
nickname?: string
avatar?: string
}
/** 微信手机号登录数据(2023+ 新方案:仅传 code */
export interface WxPhoneLoginData {
code: string
}
/** 小程序码生成参数 */
export interface WxQrCodeParams {
/** 场景值(最大32字符,如 invite_123 */
scene: string
/** 目标页面路径(不带 /,如 pages/index/index),为空则默认主页 */
page?: string
/** 宽度(px),默认 430,范围 280-1280 */
width?: number
}
/** 小程序码生成结果 */
export interface WxQrCodeResult {
/** 小程序码图片 URLdata:image/png;base64,... */
url: string
}
@@ -1,42 +0,0 @@
import type { BatchSetStatus } from './user'
import { http } from '@/http'
const SYSTEM_BASE = '/system'
/**
* 公告管理 API
* 与 web 端 module_system/notice.ts 对齐(完整字段定义)
*/
export const NoticeAPI = {
getPage(params?: Record<string, any>): Promise<PageResult<NoticeItem>> {
return http.Get(`${SYSTEM_BASE}/notice/list`, params)
},
getDetail(id: number): Promise<NoticeItem> {
return http.Get(`${SYSTEM_BASE}/notice/detail/${id}`)
},
create(data: NoticeForm): Promise<NoticeItem> {
return http.Post(`${SYSTEM_BASE}/notice/create`, data)
},
update(id: number, data: NoticeForm): Promise<NoticeItem> {
return http.Put(`${SYSTEM_BASE}/notice/update/${id}`, data)
},
remove(ids: number[]): Promise<void> {
return http.Delete(`${SYSTEM_BASE}/notice/delete`, { ids: JSON.stringify(ids) })
},
batchStatus(data: BatchSetStatus): Promise<void> {
return http.Patch(`${SYSTEM_BASE}/notice/status/batch`, data)
},
getAvailable(): Promise<NoticeItem[]> {
return http.Get(`${SYSTEM_BASE}/notice/available`)
},
}
export interface NoticeForm extends BaseFormType {
notice_title?: string
notice_type?: string
notice_content?: string
status?: number
description?: string
}
export interface NoticeItem extends BaseType, NoticeForm {}
@@ -1,34 +0,0 @@
import { http } from '@/http'
const PARAM_BASE_URL = '/system/param'
/** 系统参数项(与后端 ParamsOutSchema 对齐) */
export interface ParamsItem {
id: number
config_name: string
config_key: string
config_value: string | null
config_type: boolean
status: number
description?: string | null
created_time?: string
update_time?: string
}
/** 系统参数管理 API */
const ParamsAPI = {
/**
* 获取初始化缓存参数(系统配置列表)
*
* 免认证接口:登录页(无 token)也会调用,需标记 meta.ignoreAuth 跳过鉴权注入
*
* @returns 系统配置项列表,config_key 唯一标识
*/
getInitConfig(): Promise<ParamsItem[]> {
return http.Get(`${PARAM_BASE_URL}/info`, {
meta: { ignoreAuth: true },
})
},
}
export default ParamsAPI
@@ -1,70 +0,0 @@
import { http } from '@/http'
const SYSTEM_BASE = '/system'
/**
* 工单管理 API
* 与 web 端 module_system/ticket.ts 对齐(完整字段定义)
*/
export const TicketAPI = {
getPage(params?: Record<string, any>): Promise<PageResult<TicketItem>> {
return http.Get(`${SYSTEM_BASE}/ticket/list`, params)
},
getDetail(id: number): Promise<TicketItem> {
return http.Get(`${SYSTEM_BASE}/ticket/detail/${id}`)
},
create(data: TicketForm): Promise<TicketItem> {
return http.Post(`${SYSTEM_BASE}/ticket/create`, data)
},
update(id: number, data: Record<string, any>): Promise<TicketItem> {
return http.Put(`${SYSTEM_BASE}/ticket/update/${id}`, data)
},
remove(ids: number[]): Promise<void> {
return http.Delete(`${SYSTEM_BASE}/ticket/delete`, { ids: JSON.stringify(ids) })
},
batch(data: { ids: number[], status: number, assigned_id?: number }): Promise<void> {
return http.Put(`${SYSTEM_BASE}/ticket/batch`, data)
},
exportTickets(params?: Record<string, any>): Promise<unknown> {
return http.Post(`${SYSTEM_BASE}/ticket/export`, params)
},
getComments(ticketId: number, params?: Record<string, any>): Promise<PageResult<TicketComment>> {
return http.Get(`${SYSTEM_BASE}/ticket/${ticketId}/comments`, params)
},
createComment(ticketId: number, data: { content: string }): Promise<TicketComment> {
return http.Post(`${SYSTEM_BASE}/ticket/${ticketId}/comments`, data)
},
}
export interface TicketForm extends BaseFormType {
title: string
ticket_content?: string
summary?: string
ticket_type: string
images?: string
reply?: string
assigned_id?: number
status?: number
description?: string
}
export interface TicketItem extends BaseType {
title?: string
ticket_content?: string
summary?: string
ticket_type?: string
status?: string | number
assigned_id?: number
assigned_by?: CommonType
images?: string
reply?: string
description?: string
}
export interface TicketComment extends BaseType {
ticket_id?: number
user_id?: number
username?: string
content?: string
created_by_name?: string
}
-331
View File
@@ -1,331 +0,0 @@
import { http } from '@/http'
const USER_BASE_URL = '/system/user'
/**
* 用户管理 API
* 与 web 端 module_system/user.ts 对齐(完整字段定义)
*/
const UserAPI = {
/**
* 个人中心用户信息
*
* @returns 登录用户昵称、头像信息,包括角色和权限
*/
getCurrentUserInfo(): Promise<UserInfo> {
return http.Get(`${USER_BASE_URL}/current/info`)
},
/**
* 当前用户头像上传
*
* @param body 上传参数
* @param body.filePath 本地临时文件路径(uni.chooseImage 选择结果)
* @param body.name 上传字段名,后端约定为 file
* @returns uni.uploadFile 成功回调(statusCode + data 响应体字符串,需调用方解析)
*/
uploadCurrentUserAvatar(body: { filePath: string, name?: string }): Promise<{ statusCode: number, data: string }> {
return http.Post(`${USER_BASE_URL}/current/avatar/upload`, body, { requestType: 'upload' })
},
/**
* 修改个人中心用户信息
*
* @param body
* @returns 修改后的用户信息
*/
updateCurrentUserInfo(body: UserProfileForm): Promise<UserInfo> {
return http.Put(`${USER_BASE_URL}/current/info/update`, body)
},
/**
* 修改个人中心用户密码
*
* @param body
* @returns 修改后的用户信息
*/
changeCurrentUserPassword(body: PasswordChangeForm): Promise<void> {
return http.Put(`${USER_BASE_URL}/current/password/change`, body)
},
/**
* 注册用户(公开接口)
*
* @param body 注册参数
* @param body.username 用户名(字母开头,3-32 位)
* @param body.password 密码(6-128 位)
* @param body.name 昵称(可选)
* @returns 注册结果
*/
registerUser(body: RegisterForm): Promise<void> {
return http.Post(`${USER_BASE_URL}/register`, body, { meta: { ignoreAuth: true } })
},
/**
* 忘记密码(公开接口)
*
* @param body 重置参数
* @param body.username 用户名(字母开头,3-32 位)
* @param body.new_password 新密码(6-128 位)
* @returns 重置结果
*/
forgetPassword(body: ForgetPasswordForm): Promise<void> {
return http.Post(`${USER_BASE_URL}/password/forget`, body, { meta: { ignoreAuth: true } })
},
/**
* 重置用户密码
*
* @param id 用户ID
* @param body 新密码
* @param body.password 新密码
*/
resetPassword(id: number, body: { password: string }): Promise<void> {
return http.Put(`${USER_BASE_URL}/password/reset/${id}`, body)
},
/**
* 批量修改用户状态
*
* @param body 批量操作参数
* @param body.ids 用户ID列表
* @param body.status 目标状态
*/
batchStatus(body: BatchSetStatus): Promise<void> {
return http.Patch(`${USER_BASE_URL}/status/batch`, body)
},
/**
* 导出用户
*
* @param params 导出参数
*/
exportUsers(params?: Record<string, any>): Promise<unknown> {
return http.Post(`${USER_BASE_URL}/export`, params)
},
/**
* 获取用户导入模板
*/
getImportTemplate(): Promise<unknown> {
return http.Get(`${USER_BASE_URL}/import/template`)
},
/**
* 导入用户数据
*
* @param body 导入数据
*/
importData(body: Record<string, any>): Promise<void> {
return http.Post(`${USER_BASE_URL}/import/data`, body)
},
/**
* 获取用户分页列表
*
* @param queryParams 查询参数
*/
getUserPage(queryParams: UserPageQuery): Promise<PageResult<UserInfo>> {
return http.Get(`${USER_BASE_URL}/list`, queryParams)
},
/**
* 获取用户表单详情
*
* @param userId 用户ID
* @returns 用户表单详情
*/
getUserDetail(userId: number): Promise<UserForm> {
return http.Get(`${USER_BASE_URL}/detail/${userId}`)
},
/**
* 添加用户
*
* @param body 用户表单数据
*/
addUser(body: UserForm): Promise<void> {
return http.Post(`${USER_BASE_URL}/create`, body)
},
/**
* 修改用户
*
* @param body 用户表单数据
*/
updateUser(body: UserForm): Promise<void> {
return http.Put(`${USER_BASE_URL}/update`, body)
},
/**
* 删除用户
*
* @param ids 用户ID数组
*/
deleteUser(ids: number[]): Promise<void> {
return http.Delete(`${USER_BASE_URL}/delete`, ids)
},
}
export default UserAPI
/* 忘记密码表单(与后端 UserForgetPasswordSchema 一致,confirmPassword 为前端校验字段不提交) */
export interface ForgetPasswordForm {
username: string
new_password: string
}
/* 注册表单 */
export interface RegisterForm {
username: string
password: string
name?: string
}
/* 分页查询表单 */
export interface UserPageQuery extends PageQuery {
username?: string
name?: string
mobile?: string
email?: string
dept_id?: number
status?: number
start_time?: string
end_time?: string
}
/* 搜索选择器数据类型 */
export interface searchSelectDataType {
name?: string
status?: number
}
/* 用户表单 */
export interface UserForm extends BaseFormType {
username?: string
name?: string
dept_id?: number
dept_name?: string
role_ids?: number[]
role_names?: string[]
position_ids?: number[]
position_names?: string[]
password?: string
gender?: number
email?: string
mobile?: string
avatar?: string
is_superuser?: boolean
status?: number
description?: string
}
/* 登录用户信息 */
export interface UserInfo extends BaseType {
username?: string
name?: string
avatar?: string
email?: string
mobile?: string
gender?: string
password?: string
menus?: MenuTable[]
dept?: deptTreeType
dept_id?: deptTreeType['id']
dept_name?: deptTreeType['name']
roles?: roleSelectorType[]
role_names?: roleSelectorType['name'][]
role_ids?: roleSelectorType['id'][]
positions?: positionSelectorType[]
position_names?: positionSelectorType['name'][]
position_ids?: positionSelectorType['id'][]
is_superuser?: boolean
last_login?: string
gitee_login?: string
github_login?: string
wx_login?: string
qq_login?: string
status?: number
description?: string
}
/* 菜单表 */
export interface MenuTable extends BaseType {
name?: string
type?: number
icon?: string
order?: number
permission?: string
route_name?: string
route_path?: string
component_path?: string
redirect?: string
parent_id?: number
parent_name?: string
keep_alive?: boolean
hidden?: boolean
always_show?: boolean
title?: string
params?: { key: string, value: string }[]
affix?: boolean
status?: number
description?: string
children?: MenuTable[]
}
/* 部门树 */
export interface deptTreeType {
id?: number
name?: string
parent_id?: number
children?: deptTreeType[]
}
/* 角色选择器 */
export interface roleSelectorType {
id?: number
name?: string
status?: number
description?: string
}
/* 职位选择器 */
export interface positionSelectorType {
id?: number
name?: string
status?: number
description?: string
}
/* 个人中心用户信息表单 */
export interface UserProfileForm extends BaseFormType {
name?: string
gender?: string
mobile?: string
email?: string
username?: string
dept_name?: string
positions?: positionSelectorType[]
roles?: roleSelectorType[]
avatar?: string
created_time?: string
}
/* 修改密码表单 */
export interface PasswordChangeForm {
old_password: string
new_password: string
confirm_password: string
}
/* 重置密码表单 */
export interface ResetPasswordForm {
id: number
password: string
}
/* 批量设置状态 */
export interface BatchSetStatus {
ids: number[]
status: number
}
-714
View File
@@ -1,714 +0,0 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
export {}
declare global {
const CommonUtil: typeof import('./utils/compat')['CommonUtil']
const EffectScope: typeof import('vue')['EffectScope']
const Storage: typeof import('./utils/storage')['Storage']
const TEMPLATE_IDS: typeof import('./composables/useSubscribeMessage')['TEMPLATE_IDS']
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
const computed: typeof import('vue')['computed']
const computedAsync: typeof import('@vueuse/core')['computedAsync']
const computedEager: typeof import('@vueuse/core')['computedEager']
const computedInject: typeof import('@vueuse/core')['computedInject']
const computedWithControl: typeof import('@vueuse/core')['computedWithControl']
const controlledComputed: typeof import('@vueuse/core')['controlledComputed']
const controlledRef: typeof import('@vueuse/core')['controlledRef']
const createApp: typeof import('vue')['createApp']
const createEventHook: typeof import('@vueuse/core')['createEventHook']
const createGlobalState: typeof import('@vueuse/core')['createGlobalState']
const createInjectionState: typeof import('@vueuse/core')['createInjectionState']
const createPinia: typeof import('pinia')['createPinia']
const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn']
const createReusableTemplate: typeof import('@vueuse/core')['createReusableTemplate']
const createRouter: typeof import('@wot-ui/router')['createRouter']
const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable']
const createTemplatePromise: typeof import('@vueuse/core')['createTemplatePromise']
const createUnrefFn: typeof import('@vueuse/core')['createUnrefFn']
const customRef: typeof import('vue')['customRef']
const debouncedRef: typeof import('@vueuse/core')['debouncedRef']
const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch']
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: typeof import('vue')['defineComponent']
const defineStore: typeof import('pinia')['defineStore']
const eagerComputed: typeof import('@vueuse/core')['eagerComputed']
const effectScope: typeof import('vue')['effectScope']
const extendRef: typeof import('@vueuse/core')['extendRef']
const getActivePinia: typeof import('pinia')['getActivePinia']
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentPath: typeof import('./utils/index')['getCurrentPath']
const getCurrentScope: typeof import('vue')['getCurrentScope']
const getSystemTheme: typeof import('./utils/systemTheme')['getSystemTheme']
const getTicketStats: typeof import('./composables/useCachedRequest')['getTicketStats']
const h: typeof import('vue')['h']
const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch']
const initializeThemeOnce: typeof import('./utils/systemTheme')['initializeThemeOnce']
const inject: typeof import('vue')['inject']
const injectLocal: typeof import('@vueuse/core')['injectLocal']
const isDefined: typeof import('@vueuse/core')['isDefined']
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 makeDestructurable: typeof import('@vueuse/core')['makeDestructurable']
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']
const onAddToFavorites: typeof import('@dcloudio/uni-app')['onAddToFavorites']
const onBackPress: typeof import('@dcloudio/uni-app')['onBackPress']
const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
const onClickOutside: typeof import('@vueuse/core')['onClickOutside']
const onDeactivated: typeof import('vue')['onDeactivated']
const onError: typeof import('@dcloudio/uni-app')['onError']
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
const onHide: typeof import('@dcloudio/uni-app')['onHide']
const onKeyStroke: typeof import('@vueuse/core')['onKeyStroke']
const onLaunch: typeof import('@dcloudio/uni-app')['onLaunch']
const onLoad: typeof import('@dcloudio/uni-app')['onLoad']
const onLongPress: typeof import('@vueuse/core')['onLongPress']
const onMounted: typeof import('vue')['onMounted']
const onNavigationBarButtonTap: typeof import('@dcloudio/uni-app')['onNavigationBarButtonTap']
const onNavigationBarSearchInputChanged: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputChanged']
const onNavigationBarSearchInputClicked: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputClicked']
const onNavigationBarSearchInputConfirmed: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputConfirmed']
const onNavigationBarSearchInputFocusChanged: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputFocusChanged']
const onPageNotFound: typeof import('@dcloudio/uni-app')['onPageNotFound']
const onPageScroll: typeof import('@dcloudio/uni-app')['onPageScroll']
const onPullDownRefresh: typeof import('@dcloudio/uni-app')['onPullDownRefresh']
const onReachBottom: typeof import('@dcloudio/uni-app')['onReachBottom']
const onReady: typeof import('@dcloudio/uni-app')['onReady']
const onRenderTracked: typeof import('vue')['onRenderTracked']
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
const onResize: typeof import('@dcloudio/uni-app')['onResize']
const onScopeDispose: typeof import('vue')['onScopeDispose']
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
const onShareAppMessage: typeof import('@dcloudio/uni-app')['onShareAppMessage']
const onShareTimeline: typeof import('@dcloudio/uni-app')['onShareTimeline']
const onShow: typeof import('@dcloudio/uni-app')['onShow']
const onStartTyping: typeof import('@vueuse/core')['onStartTyping']
const onTabItemTap: typeof import('@dcloudio/uni-app')['onTabItemTap']
const onThemeChange: typeof import('@dcloudio/uni-app')['onThemeChange']
const onUnhandledRejection: typeof import('@dcloudio/uni-app')['onUnhandledRejection']
const onUnload: typeof import('@dcloudio/uni-app')['onUnload']
const onUnmounted: typeof import('vue')['onUnmounted']
const onUpdated: typeof import('vue')['onUpdated']
const pausableWatch: typeof import('@vueuse/core')['pausableWatch']
const persistPlugin: typeof import('./store/persist')['persistPlugin']
const provide: typeof import('vue')['provide']
const provideLocal: typeof import('@vueuse/core')['provideLocal']
const reactify: typeof import('@vueuse/core')['reactify']
const reactifyObject: typeof import('@vueuse/core')['reactifyObject']
const reactive: typeof import('vue')['reactive']
const reactiveComputed: typeof import('@vueuse/core')['reactiveComputed']
const reactiveOmit: typeof import('@vueuse/core')['reactiveOmit']
const reactivePick: typeof import('@vueuse/core')['reactivePick']
const readonly: typeof import('vue')['readonly']
const ref: typeof import('vue')['ref']
const refAutoReset: typeof import('@vueuse/core')['refAutoReset']
const refDebounced: typeof import('@vueuse/core')['refDebounced']
const refDefault: typeof import('@vueuse/core')['refDefault']
const refThrottled: typeof import('@vueuse/core')['refThrottled']
const refWithControl: typeof import('@vueuse/core')['refWithControl']
const resolveComponent: typeof import('vue')['resolveComponent']
const resolveRef: typeof import('@vueuse/core')['resolveRef']
const resolveUnref: typeof import('@vueuse/core')['resolveUnref']
const setActivePinia: typeof import('pinia')['setActivePinia']
const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
const storeToRefs: typeof import('pinia')['storeToRefs']
const subscribeSystemThemeChange: typeof import('./utils/systemTheme')['subscribeSystemThemeChange']
const syncRef: typeof import('@vueuse/core')['syncRef']
const syncRefs: typeof import('@vueuse/core')['syncRefs']
const templateRef: typeof import('@vueuse/core')['templateRef']
const themeColorOptions: typeof import('./composables/useManualTheme')['themeColorOptions']
const throttledRef: typeof import('@vueuse/core')['throttledRef']
const throttledWatch: typeof import('@vueuse/core')['throttledWatch']
const toLoginPage: typeof import('./utils/toLoginPage')['toLoginPage']
const toRaw: typeof import('vue')['toRaw']
const toReactive: typeof import('@vueuse/core')['toReactive']
const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs']
const toValue: typeof import('vue')['toValue']
const triggerRef: typeof import('vue')['triggerRef']
const tryOnBeforeMount: typeof import('@vueuse/core')['tryOnBeforeMount']
const tryOnBeforeUnmount: typeof import('@vueuse/core')['tryOnBeforeUnmount']
const tryOnMounted: typeof import('@vueuse/core')['tryOnMounted']
const tryOnScopeDispose: typeof import('@vueuse/core')['tryOnScopeDispose']
const tryOnUnmounted: typeof import('@vueuse/core')['tryOnUnmounted']
const unref: typeof import('vue')['unref']
const unrefElement: typeof import('@vueuse/core')['unrefElement']
const until: typeof import('@vueuse/core')['until']
const useActiveElement: typeof import('@vueuse/core')['useActiveElement']
const useAiChat: typeof import('./composables/useAiChat')['useAiChat']
const useAnimate: typeof import('@vueuse/core')['useAnimate']
const useArrayDifference: typeof import('@vueuse/core')['useArrayDifference']
const useArrayEvery: typeof import('@vueuse/core')['useArrayEvery']
const useArrayFilter: typeof import('@vueuse/core')['useArrayFilter']
const useArrayFind: typeof import('@vueuse/core')['useArrayFind']
const useArrayFindIndex: typeof import('@vueuse/core')['useArrayFindIndex']
const useArrayFindLast: typeof import('@vueuse/core')['useArrayFindLast']
const useArrayIncludes: typeof import('@vueuse/core')['useArrayIncludes']
const useArrayJoin: typeof import('@vueuse/core')['useArrayJoin']
const useArrayMap: typeof import('@vueuse/core')['useArrayMap']
const useArrayReduce: typeof import('@vueuse/core')['useArrayReduce']
const useArraySome: typeof import('@vueuse/core')['useArraySome']
const useArrayUnique: typeof import('@vueuse/core')['useArrayUnique']
const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue']
const useAsyncState: typeof import('@vueuse/core')['useAsyncState']
const useAttrs: typeof import('vue')['useAttrs']
const useBase64: typeof import('@vueuse/core')['useBase64']
const useBattery: typeof import('@vueuse/core')['useBattery']
const useBluetooth: typeof import('@vueuse/core')['useBluetooth']
const useBreakpoints: typeof import('@vueuse/core')['useBreakpoints']
const useBroadcastChannel: typeof import('@vueuse/core')['useBroadcastChannel']
const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation']
const useCached: typeof import('@vueuse/core')['useCached']
const useCachedRequest: typeof import('./composables/useCachedRequest')['useCachedRequest']
const useClipboard: typeof import('@vueuse/core')['useClipboard']
const useClipboardItems: typeof import('@vueuse/core')['useClipboardItems']
const useCloned: typeof import('@vueuse/core')['useCloned']
const useColorMode: typeof import('@vueuse/core')['useColorMode']
const useConfigStore: typeof import('./store/configStore')['useConfigStore']
const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog']
const useCounter: typeof import('@vueuse/core')['useCounter']
const useCssModule: typeof import('vue')['useCssModule']
const useCssVar: typeof import('@vueuse/core')['useCssVar']
const useCssVars: typeof import('vue')['useCssVars']
const useCurrentElement: typeof import('@vueuse/core')['useCurrentElement']
const useCycleList: typeof import('@vueuse/core')['useCycleList']
const useDark: typeof import('@vueuse/core')['useDark']
const useDateFormat: typeof import('@vueuse/core')['useDateFormat']
const useDebounce: typeof import('@vueuse/core')['useDebounce']
const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn']
const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory']
const useDeviceMotion: typeof import('@vueuse/core')['useDeviceMotion']
const useDeviceOrientation: typeof import('@vueuse/core')['useDeviceOrientation']
const useDevicePixelRatio: typeof import('@vueuse/core')['useDevicePixelRatio']
const useDevicesList: typeof import('@vueuse/core')['useDevicesList']
const useDialog: typeof import('@wot-ui/ui')['useDialog']
const useDisplayMedia: typeof import('@vueuse/core')['useDisplayMedia']
const useDocumentVisibility: typeof import('@vueuse/core')['useDocumentVisibility']
const useDraggable: typeof import('@vueuse/core')['useDraggable']
const useDropZone: typeof import('@vueuse/core')['useDropZone']
const useElementBounding: typeof import('@vueuse/core')['useElementBounding']
const useElementByPoint: typeof import('@vueuse/core')['useElementByPoint']
const useElementHover: typeof import('@vueuse/core')['useElementHover']
const useElementSize: typeof import('@vueuse/core')['useElementSize']
const useElementVisibility: typeof import('@vueuse/core')['useElementVisibility']
const useEventBus: typeof import('@vueuse/core')['useEventBus']
const useEventListener: typeof import('@vueuse/core')['useEventListener']
const useEventSource: typeof import('@vueuse/core')['useEventSource']
const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper']
const useFavicon: typeof import('@vueuse/core')['useFavicon']
const useFetch: typeof import('@vueuse/core')['useFetch']
const useFileDialog: typeof import('@vueuse/core')['useFileDialog']
const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess']
const useFocus: typeof import('@vueuse/core')['useFocus']
const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin']
const useFps: typeof import('@vueuse/core')['useFps']
const useFullscreen: typeof import('@vueuse/core')['useFullscreen']
const useGamepad: typeof import('@vueuse/core')['useGamepad']
const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
const useGlobalDialog: typeof import('./composables/useGlobalDialog')['useGlobalDialog']
const useGlobalLoading: typeof import('./composables/useGlobalLoading')['useGlobalLoading']
const useGlobalMessage: typeof import('./composables/useGlobalMessage')['useGlobalMessage']
const useGlobalToast: typeof import('./composables/useGlobalToast')['useGlobalToast']
const useIdle: typeof import('@vueuse/core')['useIdle']
const useImage: typeof import('@vueuse/core')['useImage']
const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll']
const useIntersectionObserver: typeof import('@vueuse/core')['useIntersectionObserver']
const useInterval: typeof import('@vueuse/core')['useInterval']
const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn']
const useKeyModifier: typeof import('@vueuse/core')['useKeyModifier']
const useLastChanged: typeof import('@vueuse/core')['useLastChanged']
const useListPage: typeof import('./composables/useListPage')['useListPage']
const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage']
const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys']
const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory']
const useManualTheme: typeof import('./composables/useManualTheme')['useManualTheme']
const useManualThemeStore: typeof import('./store/manualThemeStore')['useManualThemeStore']
const useMediaControls: typeof import('@vueuse/core')['useMediaControls']
const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery']
const useMemoize: typeof import('@vueuse/core')['useMemoize']
const useMemory: typeof import('@vueuse/core')['useMemory']
const useMessage: typeof import('@wot-ui/ui')['useMessage']
const useMounted: typeof import('@vueuse/core')['useMounted']
const useMouse: typeof import('@vueuse/core')['useMouse']
const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement']
const useMousePressed: typeof import('@vueuse/core')['useMousePressed']
const useMutationObserver: typeof import('@vueuse/core')['useMutationObserver']
const useNavigatorLanguage: typeof import('@vueuse/core')['useNavigatorLanguage']
const useNetwork: typeof import('@vueuse/core')['useNetwork']
const useNotify: typeof import('@wot-ui/ui')['useNotify']
const useNow: typeof import('@vueuse/core')['useNow']
const useObjectUrl: typeof import('@vueuse/core')['useObjectUrl']
const useOffsetPagination: typeof import('@vueuse/core')['useOffsetPagination']
const useOnline: typeof import('@vueuse/core')['useOnline']
const usePageLeave: typeof import('@vueuse/core')['usePageLeave']
const usePagination: typeof import('alova/client')['usePagination']
const useParallax: typeof import('@vueuse/core')['useParallax']
const useParentElement: typeof import('@vueuse/core')['useParentElement']
const usePerformanceObserver: typeof import('@vueuse/core')['usePerformanceObserver']
const usePermission: typeof import('@vueuse/core')['usePermission']
const usePointer: typeof import('@vueuse/core')['usePointer']
const usePointerLock: typeof import('@vueuse/core')['usePointerLock']
const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe']
const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme']
const usePreferredContrast: typeof import('@vueuse/core')['usePreferredContrast']
const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark']
const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages']
const usePreferredReducedMotion: typeof import('@vueuse/core')['usePreferredReducedMotion']
const usePrevious: typeof import('@vueuse/core')['usePrevious']
const useRafFn: typeof import('@vueuse/core')['useRafFn']
const useRefHistory: typeof import('@vueuse/core')['useRefHistory']
const useRequest: typeof import('alova/client')['useRequest']
const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver']
const useRoute: typeof import('@wot-ui/router')['useRoute']
const useRouter: typeof import('@wot-ui/router')['useRouter']
const useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation']
const useScreenSafeArea: typeof import('@vueuse/core')['useScreenSafeArea']
const useScriptTag: typeof import('@vueuse/core')['useScriptTag']
const useScroll: typeof import('@vueuse/core')['useScroll']
const useScrollLock: typeof import('@vueuse/core')['useScrollLock']
const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage']
const useShare: typeof import('./composables/useShare')['useShare']
const useSharePoster: typeof import('./composables/useSharePoster')['useSharePoster']
const useSlots: typeof import('vue')['useSlots']
const useSorted: typeof import('@vueuse/core')['useSorted']
const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition']
const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis']
const useStepper: typeof import('@vueuse/core')['useStepper']
const useStorage: typeof import('@vueuse/core')['useStorage']
const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync']
const useStyleTag: typeof import('@vueuse/core')['useStyleTag']
const useSubscribeMessage: typeof import('./composables/useSubscribeMessage')['useSubscribeMessage']
const useSupported: typeof import('@vueuse/core')['useSupported']
const useSwipe: typeof import('@vueuse/core')['useSwipe']
const useTabbar: typeof import('./composables/useTabbar')['useTabbar']
const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList']
const useTextDirection: typeof import('@vueuse/core')['useTextDirection']
const useTextSelection: typeof import('@vueuse/core')['useTextSelection']
const useTextareaAutosize: typeof import('@vueuse/core')['useTextareaAutosize']
const useTheme: typeof import('./composables/useTheme')['useTheme']
const useThemeStore: typeof import('./store/themeStore')['useThemeStore']
const useThrottle: typeof import('@vueuse/core')['useThrottle']
const useThrottleFn: typeof import('@vueuse/core')['useThrottleFn']
const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory']
const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo']
const useTimeout: typeof import('@vueuse/core')['useTimeout']
const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn']
const useTimeoutPoll: typeof import('@vueuse/core')['useTimeoutPoll']
const useTimestamp: typeof import('@vueuse/core')['useTimestamp']
const useTitle: typeof import('@vueuse/core')['useTitle']
const useToNumber: typeof import('@vueuse/core')['useToNumber']
const useToString: typeof import('@vueuse/core')['useToString']
const useToast: typeof import('@wot-ui/ui')['useToast']
const useToggle: typeof import('@vueuse/core')['useToggle']
const useTransition: typeof import('@vueuse/core')['useTransition']
const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams']
const useUserMedia: typeof import('@vueuse/core')['useUserMedia']
const useUserStore: typeof import('./store/userStore')['useUserStore']
const useVModel: typeof import('@vueuse/core')['useVModel']
const useVModels: typeof import('@vueuse/core')['useVModels']
const useVibrate: typeof import('@vueuse/core')['useVibrate']
const useVirtualList: typeof import('@vueuse/core')['useVirtualList']
const useWakeLock: typeof import('@vueuse/core')['useWakeLock']
const useWatermark: typeof import('./composables/useWatermark')['useWatermark']
const useWebNotification: typeof import('@vueuse/core')['useWebNotification']
const useWebSocket: typeof import('@vueuse/core')['useWebSocket']
const useWebWorker: typeof import('@vueuse/core')['useWebWorker']
const useWebWorkerFn: typeof import('@vueuse/core')['useWebWorkerFn']
const useWindowFocus: typeof import('@vueuse/core')['useWindowFocus']
const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll']
const useWindowSize: typeof import('@vueuse/core')['useWindowSize']
const useWxLogin: typeof import('./composables/useWxLogin')['useWxLogin']
const watch: typeof import('vue')['watch']
const watchArray: typeof import('@vueuse/core')['watchArray']
const watchAtMost: typeof import('@vueuse/core')['watchAtMost']
const watchDebounced: typeof import('@vueuse/core')['watchDebounced']
const watchDeep: typeof import('@vueuse/core')['watchDeep']
const watchEffect: typeof import('vue')['watchEffect']
const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable']
const watchImmediate: typeof import('@vueuse/core')['watchImmediate']
const watchOnce: typeof import('@vueuse/core')['watchOnce']
const watchPausable: typeof import('@vueuse/core')['watchPausable']
const watchPostEffect: typeof import('vue')['watchPostEffect']
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
const watchThrottled: typeof import('@vueuse/core')['watchThrottled']
const watchTriggerable: typeof import('@vueuse/core')['watchTriggerable']
const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter']
const whenever: typeof import('@vueuse/core')['whenever']
}
// for type re-export
declare global {
// @ts-ignore
export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, 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<typeof import('./utils/compat')['CommonUtil']>
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
readonly Storage: UnwrapRef<typeof import('./utils/storage')['Storage']>
readonly TEMPLATE_IDS: UnwrapRef<typeof import('./composables/useSubscribeMessage')['TEMPLATE_IDS']>
readonly acceptHMRUpdate: UnwrapRef<typeof import('pinia')['acceptHMRUpdate']>
readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']>
readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']>
readonly computed: UnwrapRef<typeof import('vue')['computed']>
readonly computedAsync: UnwrapRef<typeof import('@vueuse/core')['computedAsync']>
readonly computedEager: UnwrapRef<typeof import('@vueuse/core')['computedEager']>
readonly computedInject: UnwrapRef<typeof import('@vueuse/core')['computedInject']>
readonly computedWithControl: UnwrapRef<typeof import('@vueuse/core')['computedWithControl']>
readonly controlledComputed: UnwrapRef<typeof import('@vueuse/core')['controlledComputed']>
readonly controlledRef: UnwrapRef<typeof import('@vueuse/core')['controlledRef']>
readonly createApp: UnwrapRef<typeof import('vue')['createApp']>
readonly createEventHook: UnwrapRef<typeof import('@vueuse/core')['createEventHook']>
readonly createGlobalState: UnwrapRef<typeof import('@vueuse/core')['createGlobalState']>
readonly createInjectionState: UnwrapRef<typeof import('@vueuse/core')['createInjectionState']>
readonly createPinia: UnwrapRef<typeof import('pinia')['createPinia']>
readonly createReactiveFn: UnwrapRef<typeof import('@vueuse/core')['createReactiveFn']>
readonly createReusableTemplate: UnwrapRef<typeof import('@vueuse/core')['createReusableTemplate']>
readonly createRouter: UnwrapRef<typeof import('@wot-ui/router')['createRouter']>
readonly createSharedComposable: UnwrapRef<typeof import('@vueuse/core')['createSharedComposable']>
readonly createTemplatePromise: UnwrapRef<typeof import('@vueuse/core')['createTemplatePromise']>
readonly createUnrefFn: UnwrapRef<typeof import('@vueuse/core')['createUnrefFn']>
readonly customRef: UnwrapRef<typeof import('vue')['customRef']>
readonly debouncedRef: UnwrapRef<typeof import('@vueuse/core')['debouncedRef']>
readonly debouncedWatch: UnwrapRef<typeof import('@vueuse/core')['debouncedWatch']>
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
readonly defineStore: UnwrapRef<typeof import('pinia')['defineStore']>
readonly eagerComputed: UnwrapRef<typeof import('@vueuse/core')['eagerComputed']>
readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
readonly extendRef: UnwrapRef<typeof import('@vueuse/core')['extendRef']>
readonly getActivePinia: UnwrapRef<typeof import('pinia')['getActivePinia']>
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
readonly getCurrentPath: UnwrapRef<typeof import('./utils/index')['getCurrentPath']>
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
readonly getSystemTheme: UnwrapRef<typeof import('./utils/systemTheme')['getSystemTheme']>
readonly getTicketStats: UnwrapRef<typeof import('./composables/useCachedRequest')['getTicketStats']>
readonly h: UnwrapRef<typeof import('vue')['h']>
readonly ignorableWatch: UnwrapRef<typeof import('@vueuse/core')['ignorableWatch']>
readonly initializeThemeOnce: UnwrapRef<typeof import('./utils/systemTheme')['initializeThemeOnce']>
readonly inject: UnwrapRef<typeof import('vue')['inject']>
readonly injectLocal: UnwrapRef<typeof import('@vueuse/core')['injectLocal']>
readonly isDefined: UnwrapRef<typeof import('@vueuse/core')['isDefined']>
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
readonly makeDestructurable: UnwrapRef<typeof import('@vueuse/core')['makeDestructurable']>
readonly mapActions: UnwrapRef<typeof import('pinia')['mapActions']>
readonly mapGetters: UnwrapRef<typeof import('pinia')['mapGetters']>
readonly mapState: UnwrapRef<typeof import('pinia')['mapState']>
readonly mapStores: UnwrapRef<typeof import('pinia')['mapStores']>
readonly mapWritableState: UnwrapRef<typeof import('pinia')['mapWritableState']>
readonly markRaw: UnwrapRef<typeof import('vue')['markRaw']>
readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']>
readonly onActivated: UnwrapRef<typeof import('vue')['onActivated']>
readonly onAddToFavorites: UnwrapRef<typeof import('@dcloudio/uni-app')['onAddToFavorites']>
readonly onBackPress: UnwrapRef<typeof import('@dcloudio/uni-app')['onBackPress']>
readonly onBeforeMount: UnwrapRef<typeof import('vue')['onBeforeMount']>
readonly onBeforeUnmount: UnwrapRef<typeof import('vue')['onBeforeUnmount']>
readonly onBeforeUpdate: UnwrapRef<typeof import('vue')['onBeforeUpdate']>
readonly onClickOutside: UnwrapRef<typeof import('@vueuse/core')['onClickOutside']>
readonly onDeactivated: UnwrapRef<typeof import('vue')['onDeactivated']>
readonly onError: UnwrapRef<typeof import('@dcloudio/uni-app')['onError']>
readonly onErrorCaptured: UnwrapRef<typeof import('vue')['onErrorCaptured']>
readonly onHide: UnwrapRef<typeof import('@dcloudio/uni-app')['onHide']>
readonly onKeyStroke: UnwrapRef<typeof import('@vueuse/core')['onKeyStroke']>
readonly onLaunch: UnwrapRef<typeof import('@dcloudio/uni-app')['onLaunch']>
readonly onLoad: UnwrapRef<typeof import('@dcloudio/uni-app')['onLoad']>
readonly onLongPress: UnwrapRef<typeof import('@vueuse/core')['onLongPress']>
readonly onMounted: UnwrapRef<typeof import('vue')['onMounted']>
readonly onNavigationBarButtonTap: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarButtonTap']>
readonly onNavigationBarSearchInputChanged: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputChanged']>
readonly onNavigationBarSearchInputClicked: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputClicked']>
readonly onNavigationBarSearchInputConfirmed: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputConfirmed']>
readonly onNavigationBarSearchInputFocusChanged: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputFocusChanged']>
readonly onPageNotFound: UnwrapRef<typeof import('@dcloudio/uni-app')['onPageNotFound']>
readonly onPageScroll: UnwrapRef<typeof import('@dcloudio/uni-app')['onPageScroll']>
readonly onPullDownRefresh: UnwrapRef<typeof import('@dcloudio/uni-app')['onPullDownRefresh']>
readonly onReachBottom: UnwrapRef<typeof import('@dcloudio/uni-app')['onReachBottom']>
readonly onReady: UnwrapRef<typeof import('@dcloudio/uni-app')['onReady']>
readonly onRenderTracked: UnwrapRef<typeof import('vue')['onRenderTracked']>
readonly onRenderTriggered: UnwrapRef<typeof import('vue')['onRenderTriggered']>
readonly onResize: UnwrapRef<typeof import('@dcloudio/uni-app')['onResize']>
readonly onScopeDispose: UnwrapRef<typeof import('vue')['onScopeDispose']>
readonly onServerPrefetch: UnwrapRef<typeof import('vue')['onServerPrefetch']>
readonly onShareAppMessage: UnwrapRef<typeof import('@dcloudio/uni-app')['onShareAppMessage']>
readonly onShareTimeline: UnwrapRef<typeof import('@dcloudio/uni-app')['onShareTimeline']>
readonly onShow: UnwrapRef<typeof import('@dcloudio/uni-app')['onShow']>
readonly onStartTyping: UnwrapRef<typeof import('@vueuse/core')['onStartTyping']>
readonly onTabItemTap: UnwrapRef<typeof import('@dcloudio/uni-app')['onTabItemTap']>
readonly onThemeChange: UnwrapRef<typeof import('@dcloudio/uni-app')['onThemeChange']>
readonly onUnhandledRejection: UnwrapRef<typeof import('@dcloudio/uni-app')['onUnhandledRejection']>
readonly onUnload: UnwrapRef<typeof import('@dcloudio/uni-app')['onUnload']>
readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
readonly pausableWatch: UnwrapRef<typeof import('@vueuse/core')['pausableWatch']>
readonly persistPlugin: UnwrapRef<typeof import('./store/persist')['persistPlugin']>
readonly provide: UnwrapRef<typeof import('vue')['provide']>
readonly provideLocal: UnwrapRef<typeof import('@vueuse/core')['provideLocal']>
readonly reactify: UnwrapRef<typeof import('@vueuse/core')['reactify']>
readonly reactifyObject: UnwrapRef<typeof import('@vueuse/core')['reactifyObject']>
readonly reactive: UnwrapRef<typeof import('vue')['reactive']>
readonly reactiveComputed: UnwrapRef<typeof import('@vueuse/core')['reactiveComputed']>
readonly reactiveOmit: UnwrapRef<typeof import('@vueuse/core')['reactiveOmit']>
readonly reactivePick: UnwrapRef<typeof import('@vueuse/core')['reactivePick']>
readonly readonly: UnwrapRef<typeof import('vue')['readonly']>
readonly ref: UnwrapRef<typeof import('vue')['ref']>
readonly refAutoReset: UnwrapRef<typeof import('@vueuse/core')['refAutoReset']>
readonly refDebounced: UnwrapRef<typeof import('@vueuse/core')['refDebounced']>
readonly refDefault: UnwrapRef<typeof import('@vueuse/core')['refDefault']>
readonly refThrottled: UnwrapRef<typeof import('@vueuse/core')['refThrottled']>
readonly refWithControl: UnwrapRef<typeof import('@vueuse/core')['refWithControl']>
readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']>
readonly resolveRef: UnwrapRef<typeof import('@vueuse/core')['resolveRef']>
readonly resolveUnref: UnwrapRef<typeof import('@vueuse/core')['resolveUnref']>
readonly setActivePinia: UnwrapRef<typeof import('pinia')['setActivePinia']>
readonly setMapStoreSuffix: UnwrapRef<typeof import('pinia')['setMapStoreSuffix']>
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
readonly shallowRef: UnwrapRef<typeof import('vue')['shallowRef']>
readonly storeToRefs: UnwrapRef<typeof import('pinia')['storeToRefs']>
readonly subscribeSystemThemeChange: UnwrapRef<typeof import('./utils/systemTheme')['subscribeSystemThemeChange']>
readonly syncRef: UnwrapRef<typeof import('@vueuse/core')['syncRef']>
readonly syncRefs: UnwrapRef<typeof import('@vueuse/core')['syncRefs']>
readonly templateRef: UnwrapRef<typeof import('@vueuse/core')['templateRef']>
readonly themeColorOptions: UnwrapRef<typeof import('./composables/useManualTheme')['themeColorOptions']>
readonly throttledRef: UnwrapRef<typeof import('@vueuse/core')['throttledRef']>
readonly throttledWatch: UnwrapRef<typeof import('@vueuse/core')['throttledWatch']>
readonly toLoginPage: UnwrapRef<typeof import('./utils/toLoginPage')['toLoginPage']>
readonly toRaw: UnwrapRef<typeof import('vue')['toRaw']>
readonly toReactive: UnwrapRef<typeof import('@vueuse/core')['toReactive']>
readonly toRef: UnwrapRef<typeof import('vue')['toRef']>
readonly toRefs: UnwrapRef<typeof import('vue')['toRefs']>
readonly toValue: UnwrapRef<typeof import('vue')['toValue']>
readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']>
readonly tryOnBeforeMount: UnwrapRef<typeof import('@vueuse/core')['tryOnBeforeMount']>
readonly tryOnBeforeUnmount: UnwrapRef<typeof import('@vueuse/core')['tryOnBeforeUnmount']>
readonly tryOnMounted: UnwrapRef<typeof import('@vueuse/core')['tryOnMounted']>
readonly tryOnScopeDispose: UnwrapRef<typeof import('@vueuse/core')['tryOnScopeDispose']>
readonly tryOnUnmounted: UnwrapRef<typeof import('@vueuse/core')['tryOnUnmounted']>
readonly unref: UnwrapRef<typeof import('vue')['unref']>
readonly unrefElement: UnwrapRef<typeof import('@vueuse/core')['unrefElement']>
readonly until: UnwrapRef<typeof import('@vueuse/core')['until']>
readonly useActiveElement: UnwrapRef<typeof import('@vueuse/core')['useActiveElement']>
readonly useAiChat: UnwrapRef<typeof import('./composables/useAiChat')['useAiChat']>
readonly useAnimate: UnwrapRef<typeof import('@vueuse/core')['useAnimate']>
readonly useArrayDifference: UnwrapRef<typeof import('@vueuse/core')['useArrayDifference']>
readonly useArrayEvery: UnwrapRef<typeof import('@vueuse/core')['useArrayEvery']>
readonly useArrayFilter: UnwrapRef<typeof import('@vueuse/core')['useArrayFilter']>
readonly useArrayFind: UnwrapRef<typeof import('@vueuse/core')['useArrayFind']>
readonly useArrayFindIndex: UnwrapRef<typeof import('@vueuse/core')['useArrayFindIndex']>
readonly useArrayFindLast: UnwrapRef<typeof import('@vueuse/core')['useArrayFindLast']>
readonly useArrayIncludes: UnwrapRef<typeof import('@vueuse/core')['useArrayIncludes']>
readonly useArrayJoin: UnwrapRef<typeof import('@vueuse/core')['useArrayJoin']>
readonly useArrayMap: UnwrapRef<typeof import('@vueuse/core')['useArrayMap']>
readonly useArrayReduce: UnwrapRef<typeof import('@vueuse/core')['useArrayReduce']>
readonly useArraySome: UnwrapRef<typeof import('@vueuse/core')['useArraySome']>
readonly useArrayUnique: UnwrapRef<typeof import('@vueuse/core')['useArrayUnique']>
readonly useAsyncQueue: UnwrapRef<typeof import('@vueuse/core')['useAsyncQueue']>
readonly useAsyncState: UnwrapRef<typeof import('@vueuse/core')['useAsyncState']>
readonly useAttrs: UnwrapRef<typeof import('vue')['useAttrs']>
readonly useBase64: UnwrapRef<typeof import('@vueuse/core')['useBase64']>
readonly useBattery: UnwrapRef<typeof import('@vueuse/core')['useBattery']>
readonly useBluetooth: UnwrapRef<typeof import('@vueuse/core')['useBluetooth']>
readonly useBreakpoints: UnwrapRef<typeof import('@vueuse/core')['useBreakpoints']>
readonly useBroadcastChannel: UnwrapRef<typeof import('@vueuse/core')['useBroadcastChannel']>
readonly useBrowserLocation: UnwrapRef<typeof import('@vueuse/core')['useBrowserLocation']>
readonly useCached: UnwrapRef<typeof import('@vueuse/core')['useCached']>
readonly useCachedRequest: UnwrapRef<typeof import('./composables/useCachedRequest')['useCachedRequest']>
readonly useClipboard: UnwrapRef<typeof import('@vueuse/core')['useClipboard']>
readonly useClipboardItems: UnwrapRef<typeof import('@vueuse/core')['useClipboardItems']>
readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']>
readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']>
readonly useConfigStore: UnwrapRef<typeof import('./store/configStore')['useConfigStore']>
readonly useConfirmDialog: UnwrapRef<typeof import('@vueuse/core')['useConfirmDialog']>
readonly useCounter: UnwrapRef<typeof import('@vueuse/core')['useCounter']>
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
readonly useCssVar: UnwrapRef<typeof import('@vueuse/core')['useCssVar']>
readonly useCssVars: UnwrapRef<typeof import('vue')['useCssVars']>
readonly useCurrentElement: UnwrapRef<typeof import('@vueuse/core')['useCurrentElement']>
readonly useCycleList: UnwrapRef<typeof import('@vueuse/core')['useCycleList']>
readonly useDark: UnwrapRef<typeof import('@vueuse/core')['useDark']>
readonly useDateFormat: UnwrapRef<typeof import('@vueuse/core')['useDateFormat']>
readonly useDebounce: UnwrapRef<typeof import('@vueuse/core')['useDebounce']>
readonly useDebounceFn: UnwrapRef<typeof import('@vueuse/core')['useDebounceFn']>
readonly useDebouncedRefHistory: UnwrapRef<typeof import('@vueuse/core')['useDebouncedRefHistory']>
readonly useDeviceMotion: UnwrapRef<typeof import('@vueuse/core')['useDeviceMotion']>
readonly useDeviceOrientation: UnwrapRef<typeof import('@vueuse/core')['useDeviceOrientation']>
readonly useDevicePixelRatio: UnwrapRef<typeof import('@vueuse/core')['useDevicePixelRatio']>
readonly useDevicesList: UnwrapRef<typeof import('@vueuse/core')['useDevicesList']>
readonly useDialog: UnwrapRef<typeof import('@wot-ui/ui')['useDialog']>
readonly useDisplayMedia: UnwrapRef<typeof import('@vueuse/core')['useDisplayMedia']>
readonly useDocumentVisibility: UnwrapRef<typeof import('@vueuse/core')['useDocumentVisibility']>
readonly useDraggable: UnwrapRef<typeof import('@vueuse/core')['useDraggable']>
readonly useDropZone: UnwrapRef<typeof import('@vueuse/core')['useDropZone']>
readonly useElementBounding: UnwrapRef<typeof import('@vueuse/core')['useElementBounding']>
readonly useElementByPoint: UnwrapRef<typeof import('@vueuse/core')['useElementByPoint']>
readonly useElementHover: UnwrapRef<typeof import('@vueuse/core')['useElementHover']>
readonly useElementSize: UnwrapRef<typeof import('@vueuse/core')['useElementSize']>
readonly useElementVisibility: UnwrapRef<typeof import('@vueuse/core')['useElementVisibility']>
readonly useEventBus: UnwrapRef<typeof import('@vueuse/core')['useEventBus']>
readonly useEventListener: UnwrapRef<typeof import('@vueuse/core')['useEventListener']>
readonly useEventSource: UnwrapRef<typeof import('@vueuse/core')['useEventSource']>
readonly useEyeDropper: UnwrapRef<typeof import('@vueuse/core')['useEyeDropper']>
readonly useFavicon: UnwrapRef<typeof import('@vueuse/core')['useFavicon']>
readonly useFetch: UnwrapRef<typeof import('@vueuse/core')['useFetch']>
readonly useFileDialog: UnwrapRef<typeof import('@vueuse/core')['useFileDialog']>
readonly useFileSystemAccess: UnwrapRef<typeof import('@vueuse/core')['useFileSystemAccess']>
readonly useFocus: UnwrapRef<typeof import('@vueuse/core')['useFocus']>
readonly useFocusWithin: UnwrapRef<typeof import('@vueuse/core')['useFocusWithin']>
readonly useFps: UnwrapRef<typeof import('@vueuse/core')['useFps']>
readonly useFullscreen: UnwrapRef<typeof import('@vueuse/core')['useFullscreen']>
readonly useGamepad: UnwrapRef<typeof import('@vueuse/core')['useGamepad']>
readonly useGeolocation: UnwrapRef<typeof import('@vueuse/core')['useGeolocation']>
readonly useGlobalDialog: UnwrapRef<typeof import('./composables/useGlobalDialog')['useGlobalDialog']>
readonly useGlobalLoading: UnwrapRef<typeof import('./composables/useGlobalLoading')['useGlobalLoading']>
readonly useGlobalMessage: UnwrapRef<typeof import('./composables/useGlobalMessage')['useGlobalMessage']>
readonly useGlobalToast: UnwrapRef<typeof import('./composables/useGlobalToast')['useGlobalToast']>
readonly useIdle: UnwrapRef<typeof import('@vueuse/core')['useIdle']>
readonly useImage: UnwrapRef<typeof import('@vueuse/core')['useImage']>
readonly useInfiniteScroll: UnwrapRef<typeof import('@vueuse/core')['useInfiniteScroll']>
readonly useIntersectionObserver: UnwrapRef<typeof import('@vueuse/core')['useIntersectionObserver']>
readonly useInterval: UnwrapRef<typeof import('@vueuse/core')['useInterval']>
readonly useIntervalFn: UnwrapRef<typeof import('@vueuse/core')['useIntervalFn']>
readonly useKeyModifier: UnwrapRef<typeof import('@vueuse/core')['useKeyModifier']>
readonly useLastChanged: UnwrapRef<typeof import('@vueuse/core')['useLastChanged']>
readonly useListPage: UnwrapRef<typeof import('./composables/useListPage')['useListPage']>
readonly useLocalStorage: UnwrapRef<typeof import('@vueuse/core')['useLocalStorage']>
readonly useMagicKeys: UnwrapRef<typeof import('@vueuse/core')['useMagicKeys']>
readonly useManualRefHistory: UnwrapRef<typeof import('@vueuse/core')['useManualRefHistory']>
readonly useManualTheme: UnwrapRef<typeof import('./composables/useManualTheme')['useManualTheme']>
readonly useManualThemeStore: UnwrapRef<typeof import('./store/manualThemeStore')['useManualThemeStore']>
readonly useMediaControls: UnwrapRef<typeof import('@vueuse/core')['useMediaControls']>
readonly useMediaQuery: UnwrapRef<typeof import('@vueuse/core')['useMediaQuery']>
readonly useMemoize: UnwrapRef<typeof import('@vueuse/core')['useMemoize']>
readonly useMemory: UnwrapRef<typeof import('@vueuse/core')['useMemory']>
readonly useMounted: UnwrapRef<typeof import('@vueuse/core')['useMounted']>
readonly useMouse: UnwrapRef<typeof import('@vueuse/core')['useMouse']>
readonly useMouseInElement: UnwrapRef<typeof import('@vueuse/core')['useMouseInElement']>
readonly useMousePressed: UnwrapRef<typeof import('@vueuse/core')['useMousePressed']>
readonly useMutationObserver: UnwrapRef<typeof import('@vueuse/core')['useMutationObserver']>
readonly useNavigatorLanguage: UnwrapRef<typeof import('@vueuse/core')['useNavigatorLanguage']>
readonly useNetwork: UnwrapRef<typeof import('@vueuse/core')['useNetwork']>
readonly useNotify: UnwrapRef<typeof import('@wot-ui/ui')['useNotify']>
readonly useNow: UnwrapRef<typeof import('@vueuse/core')['useNow']>
readonly useObjectUrl: UnwrapRef<typeof import('@vueuse/core')['useObjectUrl']>
readonly useOffsetPagination: UnwrapRef<typeof import('@vueuse/core')['useOffsetPagination']>
readonly useOnline: UnwrapRef<typeof import('@vueuse/core')['useOnline']>
readonly usePageLeave: UnwrapRef<typeof import('@vueuse/core')['usePageLeave']>
readonly usePagination: UnwrapRef<typeof import('alova/client')['usePagination']>
readonly useParallax: UnwrapRef<typeof import('@vueuse/core')['useParallax']>
readonly useParentElement: UnwrapRef<typeof import('@vueuse/core')['useParentElement']>
readonly usePerformanceObserver: UnwrapRef<typeof import('@vueuse/core')['usePerformanceObserver']>
readonly usePermission: UnwrapRef<typeof import('@vueuse/core')['usePermission']>
readonly usePointer: UnwrapRef<typeof import('@vueuse/core')['usePointer']>
readonly usePointerLock: UnwrapRef<typeof import('@vueuse/core')['usePointerLock']>
readonly usePointerSwipe: UnwrapRef<typeof import('@vueuse/core')['usePointerSwipe']>
readonly usePreferredColorScheme: UnwrapRef<typeof import('@vueuse/core')['usePreferredColorScheme']>
readonly usePreferredContrast: UnwrapRef<typeof import('@vueuse/core')['usePreferredContrast']>
readonly usePreferredDark: UnwrapRef<typeof import('@vueuse/core')['usePreferredDark']>
readonly usePreferredLanguages: UnwrapRef<typeof import('@vueuse/core')['usePreferredLanguages']>
readonly usePreferredReducedMotion: UnwrapRef<typeof import('@vueuse/core')['usePreferredReducedMotion']>
readonly usePrevious: UnwrapRef<typeof import('@vueuse/core')['usePrevious']>
readonly useRafFn: UnwrapRef<typeof import('@vueuse/core')['useRafFn']>
readonly useRefHistory: UnwrapRef<typeof import('@vueuse/core')['useRefHistory']>
readonly useRequest: UnwrapRef<typeof import('alova/client')['useRequest']>
readonly useResizeObserver: UnwrapRef<typeof import('@vueuse/core')['useResizeObserver']>
readonly useRoute: UnwrapRef<typeof import('@wot-ui/router')['useRoute']>
readonly useRouter: UnwrapRef<typeof import('@wot-ui/router')['useRouter']>
readonly useScreenOrientation: UnwrapRef<typeof import('@vueuse/core')['useScreenOrientation']>
readonly useScreenSafeArea: UnwrapRef<typeof import('@vueuse/core')['useScreenSafeArea']>
readonly useScriptTag: UnwrapRef<typeof import('@vueuse/core')['useScriptTag']>
readonly useScroll: UnwrapRef<typeof import('@vueuse/core')['useScroll']>
readonly useScrollLock: UnwrapRef<typeof import('@vueuse/core')['useScrollLock']>
readonly useSessionStorage: UnwrapRef<typeof import('@vueuse/core')['useSessionStorage']>
readonly useShare: UnwrapRef<typeof import('./composables/useShare')['useShare']>
readonly useSharePoster: UnwrapRef<typeof import('./composables/useSharePoster')['useSharePoster']>
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
readonly useSorted: UnwrapRef<typeof import('@vueuse/core')['useSorted']>
readonly useSpeechRecognition: UnwrapRef<typeof import('@vueuse/core')['useSpeechRecognition']>
readonly useSpeechSynthesis: UnwrapRef<typeof import('@vueuse/core')['useSpeechSynthesis']>
readonly useStepper: UnwrapRef<typeof import('@vueuse/core')['useStepper']>
readonly useStorage: UnwrapRef<typeof import('@vueuse/core')['useStorage']>
readonly useStorageAsync: UnwrapRef<typeof import('@vueuse/core')['useStorageAsync']>
readonly useStyleTag: UnwrapRef<typeof import('@vueuse/core')['useStyleTag']>
readonly useSubscribeMessage: UnwrapRef<typeof import('./composables/useSubscribeMessage')['useSubscribeMessage']>
readonly useSupported: UnwrapRef<typeof import('@vueuse/core')['useSupported']>
readonly useSwipe: UnwrapRef<typeof import('@vueuse/core')['useSwipe']>
readonly useTabbar: UnwrapRef<typeof import('./composables/useTabbar')['useTabbar']>
readonly useTemplateRefsList: UnwrapRef<typeof import('@vueuse/core')['useTemplateRefsList']>
readonly useTextDirection: UnwrapRef<typeof import('@vueuse/core')['useTextDirection']>
readonly useTextSelection: UnwrapRef<typeof import('@vueuse/core')['useTextSelection']>
readonly useTextareaAutosize: UnwrapRef<typeof import('@vueuse/core')['useTextareaAutosize']>
readonly useTheme: UnwrapRef<typeof import('./composables/useTheme')['useTheme']>
readonly useThemeStore: UnwrapRef<typeof import('./store/themeStore')['useThemeStore']>
readonly useThrottle: UnwrapRef<typeof import('@vueuse/core')['useThrottle']>
readonly useThrottleFn: UnwrapRef<typeof import('@vueuse/core')['useThrottleFn']>
readonly useThrottledRefHistory: UnwrapRef<typeof import('@vueuse/core')['useThrottledRefHistory']>
readonly useTimeAgo: UnwrapRef<typeof import('@vueuse/core')['useTimeAgo']>
readonly useTimeout: UnwrapRef<typeof import('@vueuse/core')['useTimeout']>
readonly useTimeoutFn: UnwrapRef<typeof import('@vueuse/core')['useTimeoutFn']>
readonly useTimeoutPoll: UnwrapRef<typeof import('@vueuse/core')['useTimeoutPoll']>
readonly useTimestamp: UnwrapRef<typeof import('@vueuse/core')['useTimestamp']>
readonly useTitle: UnwrapRef<typeof import('@vueuse/core')['useTitle']>
readonly useToNumber: UnwrapRef<typeof import('@vueuse/core')['useToNumber']>
readonly useToString: UnwrapRef<typeof import('@vueuse/core')['useToString']>
readonly useToast: UnwrapRef<typeof import('@wot-ui/ui')['useToast']>
readonly useToggle: UnwrapRef<typeof import('@vueuse/core')['useToggle']>
readonly useTransition: UnwrapRef<typeof import('@vueuse/core')['useTransition']>
readonly useUrlSearchParams: UnwrapRef<typeof import('@vueuse/core')['useUrlSearchParams']>
readonly useUserMedia: UnwrapRef<typeof import('@vueuse/core')['useUserMedia']>
readonly useUserStore: UnwrapRef<typeof import('./store/userStore')['useUserStore']>
readonly useVModel: UnwrapRef<typeof import('@vueuse/core')['useVModel']>
readonly useVModels: UnwrapRef<typeof import('@vueuse/core')['useVModels']>
readonly useVibrate: UnwrapRef<typeof import('@vueuse/core')['useVibrate']>
readonly useVirtualList: UnwrapRef<typeof import('@vueuse/core')['useVirtualList']>
readonly useWakeLock: UnwrapRef<typeof import('@vueuse/core')['useWakeLock']>
readonly useWatermark: UnwrapRef<typeof import('./composables/useWatermark')['useWatermark']>
readonly useWebNotification: UnwrapRef<typeof import('@vueuse/core')['useWebNotification']>
readonly useWebSocket: UnwrapRef<typeof import('@vueuse/core')['useWebSocket']>
readonly useWebWorker: UnwrapRef<typeof import('@vueuse/core')['useWebWorker']>
readonly useWebWorkerFn: UnwrapRef<typeof import('@vueuse/core')['useWebWorkerFn']>
readonly useWindowFocus: UnwrapRef<typeof import('@vueuse/core')['useWindowFocus']>
readonly useWindowScroll: UnwrapRef<typeof import('@vueuse/core')['useWindowScroll']>
readonly useWindowSize: UnwrapRef<typeof import('@vueuse/core')['useWindowSize']>
readonly useWxLogin: UnwrapRef<typeof import('./composables/useWxLogin')['useWxLogin']>
readonly watch: UnwrapRef<typeof import('vue')['watch']>
readonly watchArray: UnwrapRef<typeof import('@vueuse/core')['watchArray']>
readonly watchAtMost: UnwrapRef<typeof import('@vueuse/core')['watchAtMost']>
readonly watchDebounced: UnwrapRef<typeof import('@vueuse/core')['watchDebounced']>
readonly watchDeep: UnwrapRef<typeof import('@vueuse/core')['watchDeep']>
readonly watchEffect: UnwrapRef<typeof import('vue')['watchEffect']>
readonly watchIgnorable: UnwrapRef<typeof import('@vueuse/core')['watchIgnorable']>
readonly watchImmediate: UnwrapRef<typeof import('@vueuse/core')['watchImmediate']>
readonly watchOnce: UnwrapRef<typeof import('@vueuse/core')['watchOnce']>
readonly watchPausable: UnwrapRef<typeof import('@vueuse/core')['watchPausable']>
readonly watchPostEffect: UnwrapRef<typeof import('vue')['watchPostEffect']>
readonly watchSyncEffect: UnwrapRef<typeof import('vue')['watchSyncEffect']>
readonly watchThrottled: UnwrapRef<typeof import('@vueuse/core')['watchThrottled']>
readonly watchTriggerable: UnwrapRef<typeof import('@vueuse/core')['watchTriggerable']>
readonly watchWithFilter: UnwrapRef<typeof import('@vueuse/core')['watchWithFilter']>
readonly whenever: UnwrapRef<typeof import('@vueuse/core')['whenever']>
}
}
-74
View File
@@ -1,74 +0,0 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// Generated by vite-plugin-uni-components
// Read more: https://github.com/vuejs/core/pull/3399
export {}
declare module 'vue' {
export interface GlobalComponents {
GlobalDialog: typeof import('./components/GlobalDialog.vue')['default']
GlobalLoading: typeof import('./components/GlobalLoading.vue')['default']
GlobalMessage: typeof import('./components/GlobalMessage.vue')['default']
GlobalToast: typeof import('./components/GlobalToast.vue')['default']
SkeletonPage: typeof import('./components/SkeletonPage.vue')['default']
StatusBadge: typeof import('./components/StatusBadge.vue')['default']
UniEcharts: typeof import('uni-echarts')['default']
WdActionSheet: typeof import('@wot-ui/ui/components/wd-action-sheet/wd-action-sheet.vue')['default']
WdAvatar: typeof import('@wot-ui/ui/components/wd-avatar/wd-avatar.vue')['default']
WdBacktop: typeof import('@wot-ui/ui/components/wd-backtop/wd-backtop.vue')['default']
WdBadge: typeof import('@wot-ui/ui/components/wd-badge/wd-badge.vue')['default']
WdButton: typeof import('@wot-ui/ui/components/wd-button/wd-button.vue')['default']
WdCell: typeof import('@wot-ui/ui/components/wd-cell/wd-cell.vue')['default']
WdCellGroup: typeof import('@wot-ui/ui/components/wd-cell-group/wd-cell-group.vue')['default']
WdCheckbox: typeof import('@wot-ui/ui/components/wd-checkbox/wd-checkbox.vue')['default']
WdCol: typeof import('@wot-ui/ui/components/wd-col/wd-col.vue')['default']
WdCollapse: typeof import('@wot-ui/ui/components/wd-collapse/wd-collapse.vue')['default']
WdCollapseItem: typeof import('@wot-ui/ui/components/wd-collapse-item/wd-collapse-item.vue')['default']
WdConfigProvider: typeof import('@wot-ui/ui/components/wd-config-provider/wd-config-provider.vue')['default']
WdCountTo: typeof import('@wot-ui/ui/components/wd-count-to/wd-count-to.vue')['default']
WdDatetimePicker: typeof import('@wot-ui/ui/components/wd-datetime-picker/wd-datetime-picker.vue')['default']
WdDialog: typeof import('@wot-ui/ui/components/wd-dialog/wd-dialog.vue')['default']
WdDivider: typeof import('@wot-ui/ui/components/wd-divider/wd-divider.vue')['default']
WdDropMenu: typeof import('@wot-ui/ui/components/wd-drop-menu/wd-drop-menu.vue')['default']
WdDropMenuItem: typeof import('@wot-ui/ui/components/wd-drop-menu-item/wd-drop-menu-item.vue')['default']
WdEmpty: typeof import('@wot-ui/ui/components/wd-empty/wd-empty.vue')['default']
WdFab: typeof import('@wot-ui/ui/components/wd-fab/wd-fab.vue')['default']
WdForm: typeof import('@wot-ui/ui/components/wd-form/wd-form.vue')['default']
WdFormItem: typeof import('@wot-ui/ui/components/wd-form-item/wd-form-item.vue')['default']
WdGap: typeof import('@wot-ui/ui/components/wd-gap/wd-gap.vue')['default']
WdGrid: typeof import('@wot-ui/ui/components/wd-grid/wd-grid.vue')['default']
WdGridItem: typeof import('@wot-ui/ui/components/wd-grid-item/wd-grid-item.vue')['default']
WdIcon: typeof import('@wot-ui/ui/components/wd-icon/wd-icon.vue')['default']
WdImagePreview: typeof import('@wot-ui/ui/components/wd-image-preview/wd-image-preview.vue')['default']
WdImg: typeof import('@wot-ui/ui/components/wd-img/wd-img.vue')['default']
WdInput: typeof import('@wot-ui/ui/components/wd-input/wd-input.vue')['default']
WdLoading: typeof import('@wot-ui/ui/components/wd-loading/wd-loading.vue')['default']
WdNavbar: typeof import('@wot-ui/ui/components/wd-navbar/wd-navbar.vue')['default']
WdNoticeBar: typeof import('@wot-ui/ui/components/wd-notice-bar/wd-notice-bar.vue')['default']
WdNotify: typeof import('@wot-ui/ui/components/wd-notify/wd-notify.vue')['default']
WdPagination: typeof import('@wot-ui/ui/components/wd-pagination/wd-pagination.vue')['default']
WdPicker: typeof import('@wot-ui/ui/components/wd-picker/wd-picker.vue')['default']
WdPopup: typeof import('@wot-ui/ui/components/wd-popup/wd-popup.vue')['default']
WdRadio: typeof import('@wot-ui/ui/components/wd-radio/wd-radio.vue')['default']
WdRadioGroup: typeof import('@wot-ui/ui/components/wd-radio-group/wd-radio-group.vue')['default']
WdRow: typeof import('@wot-ui/ui/components/wd-row/wd-row.vue')['default']
WdSearch: typeof import('@wot-ui/ui/components/wd-search/wd-search.vue')['default']
WdSkeleton: typeof import('@wot-ui/ui/components/wd-skeleton/wd-skeleton.vue')['default']
WdSlider: typeof import('@wot-ui/ui/components/wd-slider/wd-slider.vue')['default']
WdSlideVerify: typeof import('@wot-ui/ui/components/wd-slide-verify/wd-slide-verify.vue')['default']
WdStep: typeof import('@wot-ui/ui/components/wd-step/wd-step.vue')['default']
WdSteps: typeof import('@wot-ui/ui/components/wd-steps/wd-steps.vue')['default']
WdSwipeAction: typeof import('@wot-ui/ui/components/wd-swipe-action/wd-swipe-action.vue')['default']
WdSwiper: typeof import('@wot-ui/ui/components/wd-swiper/wd-swiper.vue')['default']
WdSwitch: typeof import('@wot-ui/ui/components/wd-switch/wd-switch.vue')['default']
WdTab: typeof import('@wot-ui/ui/components/wd-tab/wd-tab.vue')['default']
WdTabbar: typeof import('@wot-ui/ui/components/wd-tabbar/wd-tabbar.vue')['default']
WdTabbarItem: typeof import('@wot-ui/ui/components/wd-tabbar-item/wd-tabbar-item.vue')['default']
WdTabs: typeof import('@wot-ui/ui/components/wd-tabs/wd-tabs.vue')['default']
WdTag: typeof import('@wot-ui/ui/components/wd-tag/wd-tag.vue')['default']
WdTextarea: typeof import('@wot-ui/ui/components/wd-textarea/wd-textarea.vue')['default']
WdToast: typeof import('@wot-ui/ui/components/wd-toast/wd-toast.vue')['default']
WdUpload: typeof import('@wot-ui/ui/components/wd-upload/wd-upload.vue')['default']
}
}
@@ -1,64 +0,0 @@
<!--
* @Author: weisheng
* @Date: 2025-09-02 09:42:36
* @LastEditTime: 2026-04-08 18:36:24
* @LastEditors: weisheng
* @Description:
* @FilePath: /wot-starter/src/components/GlobalDialog.vue
* 记得注释
-->
<script lang="ts" setup>
import { deepClone, isFunction } from '@wot-ui/ui/common/util'
const { dialogOptions, currentPage } = storeToRefs(useGlobalDialog())
const dialog = useDialog('globalDialog')
const currentPath = getCurrentPath()
// #ifdef MP-ALIPAY
const hackAlipayVisible = ref(false)
nextTick(() => {
hackAlipayVisible.value = true
})
// #endif
watch(() => dialogOptions.value, (newVal) => {
if (newVal) {
if (currentPage.value === currentPath) {
const option = deepClone(newVal)
dialog.show(option).then((res) => {
if (isFunction(option.success)) {
option.success(res)
}
}).catch((err) => {
if (isFunction(option.fail)) {
option.fail(err)
}
})
}
}
else {
dialog.close()
}
})
</script>
<script lang="ts">
export default {
options: {
virtualHost: true,
addGlobalClass: true,
styleIsolation: 'shared',
},
}
</script>
<template>
<!-- #ifdef MP-ALIPAY -->
<wd-dialog v-if="hackAlipayVisible" selector="globalDialog" />
<!-- #endif -->
<!-- #ifndef MP-ALIPAY -->
<wd-dialog selector="globalDialog" />
<!-- #endif -->
</template>
@@ -1,46 +0,0 @@
<script lang="ts" setup>
const { loadingOptions, currentPage } = storeToRefs(useGlobalLoading())
const { close: closeGlobalLoading } = useGlobalLoading()
const loading = useToast('globalLoading')
const currentPath = getCurrentPath()
// #ifdef MP-ALIPAY
const hackAlipayVisible = ref(false)
nextTick(() => {
hackAlipayVisible.value = true
})
// #endif
watch(() => loadingOptions.value, (newVal) => {
if (newVal && newVal.show) {
if (currentPage.value === currentPath) {
loading.loading(loadingOptions.value)
}
}
else {
loading.close()
}
})
</script>
<script lang="ts">
export default {
options: {
virtualHost: true,
addGlobalClass: true,
styleIsolation: 'shared',
},
}
</script>
<template>
<!-- #ifdef MP-ALIPAY -->
<wd-toast v-if="hackAlipayVisible" selector="globalLoading" :closed="closeGlobalLoading" />
<!-- #endif -->
<!-- #ifndef MP-ALIPAY -->
<wd-toast selector="globalLoading" :closed="closeGlobalLoading" />
<!-- #endif -->
</template>
@@ -1,40 +0,0 @@
<script lang="ts" setup>
import type { GlobalMessageOptions } from '@/composables/useGlobalMessage'
import { useGlobalMessage } from '@/composables/useGlobalMessage'
const { messageOptions, currentPage } = storeToRefs(useGlobalMessage())
const currentPath = getCurrentPath()
watch(() => messageOptions.value, (newVal) => {
if (newVal && currentPage.value === currentPath) {
const option: GlobalMessageOptions = { ...newVal }
uni.showModal({
title: option.title || '',
content: option.content || '',
showCancel: option.showCancel ?? (option.type === 'confirm'),
confirmText: option.confirmText || '确定',
cancelText: option.cancelText || '取消',
success: (res) => {
option.success?.({ confirm: res.confirm, cancel: res.cancel })
},
fail: (err) => {
option.fail?.(err)
},
})
}
})
</script>
<script lang="ts">
export default {
options: {
virtualHost: true,
addGlobalClass: true,
styleIsolation: 'shared',
},
}
</script>
<template>
<view />
</template>
@@ -1,46 +0,0 @@
<script lang="ts" setup>
const { toastOptions, currentPage } = storeToRefs(useGlobalToast())
const { close: closeGlobalToast } = useGlobalToast()
const toast = useToast('globalToast')
const currentPath = getCurrentPath()
// #ifdef MP-ALIPAY
const hackAlipayVisible = ref(false)
nextTick(() => {
hackAlipayVisible.value = true
})
// #endif
watch(() => toastOptions.value, (newVal) => {
if (newVal && newVal.show) {
if (currentPage.value === currentPath) {
toast.show(toastOptions.value)
}
}
else {
toast.close()
}
})
</script>
<script lang="ts">
export default {
options: {
virtualHost: true,
addGlobalClass: true,
styleIsolation: 'shared',
},
}
</script>
<template>
<!-- #ifdef MP-ALIPAY -->
<wd-toast v-if="hackAlipayVisible" selector="globalToast" :closed="closeGlobalToast" />
<!-- #endif -->
<!-- #ifndef MP-ALIPAY -->
<wd-toast selector="globalToast" :closed="closeGlobalToast" />
<!-- #endif -->
</template>
@@ -1,44 +0,0 @@
<script setup lang="ts">
/**
* 页面骨架屏组件
* 在数据加载时展示占位内容,提升感知性能
* 基于 wd-skeleton 封装
*/
const props = withDefaults(defineProps<{
/** 显示行数(列表行) */
rows?: number
/** 是否显示搜索栏骨架 */
search?: boolean
/** 是否显示操作栏骨架 */
action?: boolean
}>(), {
rows: 5,
})
/** 搜索栏占位行 */
const searchRowCol = [{ type: 'rect' as const, height: '64rpx', borderRadius: '12rpx' }]
/** 列表行占位:圆形头像 + 文本块 + 右侧徽章 */
const listRowCol = [
[
{ type: 'circle' as const, size: '64rpx' },
{ type: 'rect' as const, width: '200rpx', height: '28rpx', marginLeft: '24rpx' },
{ type: 'rect' as const, width: '80rpx', height: '40rpx', borderRadius: '999rpx' },
],
]
</script>
<template>
<view class="p-sm">
<!-- 搜索栏骨架 -->
<view v-if="search" class="admin-card mb-md p-md">
<wd-skeleton :row-col="searchRowCol" :loading="true" animation="gradient" />
</view>
<!-- 多行列表骨架 -->
<view class="admin-card p-sm">
<view v-for="i in props.rows" :key="i" class="gap-md p-md">
<wd-skeleton :row-col="listRowCol" :loading="true" animation="gradient" />
</view>
</view>
</view>
</template>
-123
View File
@@ -1,123 +0,0 @@
<script setup lang="ts">
/**
* 状态徽标组件
* 用于统一展示启用/禁用/草稿/发布等状态
*/
withDefaults(defineProps<{
status?: string | boolean | number
/** 自定义映射: { '0': 'enabled', '1': 'disabled', true: 'enabled', false: 'disabled' } */
map?: Record<string, string>
/** 自定义标签文本 */
label?: string
/** 仅显示圆点 */
dot?: boolean
}>(), {
status: '',
label: '',
dot: false,
})
const statusMap: Record<string, { label: string, cls: string }> = {
enabled: { label: '启用', cls: 'status-badge--enabled' },
disabled: { label: '禁用', cls: 'status-badge--disabled' },
draft: { label: '草稿', cls: 'status-badge--draft' },
published: { label: '发布', cls: 'status-badge--primary' },
archived: { label: '归档', cls: 'status-badge--disabled' },
active: { label: '活跃', cls: 'status-badge--primary' },
success: { label: '成功', cls: 'status-badge--enabled' },
failed: { label: '失败', cls: 'status-badge--danger' },
pending: { label: '待处理', cls: 'status-badge--draft' },
processing: { label: '处理中', cls: 'status-badge--primary' },
completed: { label: '已完成', cls: 'status-badge--enabled' },
closed: { label: '已关闭', cls: 'status-badge--disabled' },
deprecated: { label: '已废弃', cls: 'status-badge--danger' },
expired: { label: '过期', cls: 'status-badge--danger' },
}
function resolve(input: string | boolean | number): { label: string, cls: string } {
if (input === true || input === 'true' || input === '1' || input === 1 || input === '0' || input === 0 || input === false || input === 'false') {
// 后端规范: 0=启用, 1=禁用
const isEnabled = input === 0 || input === '0' || input === true || input === 'true'
return isEnabled
? { label: '启用', cls: 'status-badge--enabled' }
: { label: '禁用', cls: 'status-badge--disabled' }
}
return statusMap[String(input)] || { label: String(input), cls: 'status-badge--disabled' }
}
</script>
<template>
<text v-if="dot" class="status-badge--dot" :class="resolve(status).cls" />
<text v-else class="status-badge" :class="resolve(status).cls">
<text class="status-badge--dot" :class="resolve(status).cls" />
{{ label || resolve(status).label }}
</text>
</template>
<style lang="scss">
/* 状态徽标全局样式(非 scoped,供页面复用类名)
* 颜色使用 wot-ui 语义变量,自动适配亮/暗主题 */
.status-badge {
display: inline-flex;
align-items: center;
gap: 6rpx;
padding: 4rpx 14rpx;
border-radius: 999rpx;
font-size: 20rpx;
line-height: 1.4;
}
.status-badge--dot {
display: inline-block;
width: 10rpx;
height: 10rpx;
border-radius: 50%;
flex-shrink: 0;
background: currentColor;
}
/* 各状态仅定义文字色,背景色由带外层 .status-badge 的组合规则提供,
* 使纯圆点模式只显示纯色圆点 */
.status-badge--enabled {
color: var(--wot-success-main);
}
.status-badge.status-badge--enabled {
background: var(--wot-success-surface);
}
.status-badge--disabled {
color: var(--wot-text-auxiliary);
}
.status-badge.status-badge--disabled {
background: var(--wot-filled-content);
}
.status-badge--primary {
color: var(--wot-primary-6);
}
.status-badge.status-badge--primary {
background: var(--wot-primary-1);
}
.status-badge--danger {
color: var(--wot-danger-main);
}
.status-badge.status-badge--danger {
background: var(--wot-danger-surface);
}
/* failed 为 danger 的别名(menus 页菜单类型使用) */
.status-badge--failed {
color: var(--wot-danger-main);
}
.status-badge.status-badge--failed {
background: var(--wot-danger-surface);
}
.status-badge--draft {
color: var(--wot-text-secondary);
}
.status-badge.status-badge--draft {
background: var(--wot-filled-content);
}
</style>
-168
View File
@@ -1,168 +0,0 @@
/*
* @Author: weisheng
* @Date: 2025-09-02 09:42:36
* @LastEditTime: 2026-04-10 11:01:37
* @LastEditors: weisheng
* @Description:
* @FilePath: /wot-starter/src/composables/types/theme.ts
* 记得注释
*/
import type { ConfigProviderThemeVars } from '@wot-ui/ui'
export type PrimaryShadeKey
= | 'primary1'
| 'primary2'
| 'primary3'
| 'primary4'
| 'primary5'
| 'primary6'
| 'primary7'
| 'primary8'
| 'primary9'
| 'primary10'
export type ThemePrimaryShades = Record<PrimaryShadeKey, string>
/**
* 主题色选项接口
*/
export interface ThemeColorOption {
name: string
value: string
// 主色 6,用于列表和当前主题色圆点展示
primary: string
// 完整主色阶,用于注入 ConfigProvider 主题变量
primaryShades: ThemePrimaryShades
}
/**
* 主题类型
*/
export type ThemeMode = 'light' | 'dark'
/**
* 主题状态接口
*/
export interface ThemeState {
theme: ThemeMode
followSystem: boolean
hasUserSet: boolean
currentThemeColor: ThemeColorOption
themeVars: ConfigProviderThemeVars
}
/**
* 系统主题状态接口(简化版)
*/
export interface SystemThemeState {
theme: ThemeMode
themeVars: ConfigProviderThemeVars
}
/**
* 预定义的主题色选项
*/
export const themeColorOptions: ThemeColorOption[] = [
{
name: '默认蓝',
value: 'blue',
primary: '#1C64FD',
primaryShades: {
primary1: '#F5F8FF',
primary2: '#E5EDFF',
primary3: '#B8CFFF',
primary4: '#7CA4FF',
primary5: '#4480FF',
primary6: '#1C64FD',
primary7: '#164ED1',
primary8: '#1341AD',
primary9: '#0F3285',
primary10: '#0A235C',
},
},
{
name: '活力橙',
value: 'orange',
primary: '#FF7D00',
primaryShades: {
primary1: '#FFF7F0',
primary2: '#FFEAD6',
primary3: '#FFD0A8',
primary4: '#FFB06E',
primary5: '#FF9338',
primary6: '#FF7D00',
primary7: '#D96800',
primary8: '#B35600',
primary9: '#8A4200',
primary10: '#5F2D00',
},
},
{
name: '薄荷绿',
value: 'green',
primary: '#07C160',
primaryShades: {
primary1: '#F1FCF6',
primary2: '#DCF7E8',
primary3: '#B4EFD0',
primary4: '#7FE2AF',
primary5: '#42D28A',
primary6: '#07C160',
primary7: '#049F4F',
primary8: '#028241',
primary9: '#016532',
primary10: '#014825',
},
},
{
name: '樱花粉',
value: 'pink',
primary: '#FF69B4',
primaryShades: {
primary1: '#FFF2F9',
primary2: '#FFE2F1',
primary3: '#FFC2E3',
primary4: '#FF99D1',
primary5: '#FF7FC2',
primary6: '#FF69B4',
primary7: '#E84E9F',
primary8: '#CC3F8A',
primary9: '#A9336F',
primary10: '#7A2450',
},
},
{
name: '紫罗兰',
value: 'purple',
primary: '#8A2BE2',
primaryShades: {
primary1: '#F7F2FF',
primary2: '#EEE2FF',
primary3: '#D9BDFF',
primary4: '#BC8DFF',
primary5: '#A05EFF',
primary6: '#8A2BE2',
primary7: '#7423BF',
primary8: '#5E1D9C',
primary9: '#491679',
primary10: '#321056',
},
},
{
name: '朱砂红',
value: 'red',
primary: '#FF4757',
primaryShades: {
primary1: '#FFF3F4',
primary2: '#FFE3E5',
primary3: '#FFC0C6',
primary4: '#FF909A',
primary5: '#FF6672',
primary6: '#FF4757',
primary7: '#DB3445',
primary8: '#B7293A',
primary9: '#931E2E',
primary10: '#6F1421',
},
},
]
-116
View File
@@ -1,116 +0,0 @@
import { ref } from 'vue'
import { useUserStore } from '@/store/userStore'
export interface AiChatStreamHandlers {
/** 收到内容分片(追加到当前 AI 消息) */
onChunk?: (text: string) => void
/** 生成结束([DONE] / [STOPPED] */
onDone?: () => void
/** 连接或生成错误 */
onError?: (message: string) => void
}
/** 构建 AI 对话 WebSocket 地址:优先 VITE_APP_WS_ENDPOINT,否则从 API 域名推导(http→ws */
function buildChatWsUrl(): string {
const userStore = useUserStore()
const token = userStore.getAccessToken() || ''
let wsBase = import.meta.env.VITE_APP_WS_ENDPOINT || ''
if (!wsBase) {
const apiBase = import.meta.env.VITE_API_BASE_URL || ''
wsBase = apiBase.replace(/^http/, 'ws')
}
const apiPrefix = import.meta.env.VITE_APP_BASE_API || '/api/v1'
// token 走 query(小程序 WebSocket 不支持自定义 subprotocol,后端已兼容 ?token=
return `${wsBase}${apiPrefix}/ai/chat/ws?token=${encodeURIComponent(token)}`
}
/**
* AI 对话流式输出(WebSocket
* 协议(对应后端 /ai/chat/ws):
* - 发送:{"message": "...", "session_id": "..."} | {"action": "stop"}
* - 接收:内容分片(纯文本)→ [DONE] 结束 / [STOPPED] 停止确认
*/
export function useAiChat() {
const isStreaming = ref(false)
let socketTask: ReturnType<typeof uni.connectSocket> | null = null
let pendingOpen: Promise<void> | null = null
let resolveOpen: (() => void) | null = null
let closedByUser = false
let handlers: AiChatStreamHandlers = {}
function ensureConnected(): Promise<void> {
if (socketTask)
return Promise.resolve()
closedByUser = false
pendingOpen = new Promise<void>((resolve) => {
resolveOpen = resolve
})
const task = uni.connectSocket({ url: buildChatWsUrl(), complete: () => {} })
socketTask = task
task.onOpen(() => resolveOpen?.())
task.onMessage((res) => {
const text = typeof res.data === 'string' ? res.data : ''
if (text === '[DONE]' || text === '[STOPPED]') {
isStreaming.value = false
handlers.onDone?.()
}
else if (text) {
handlers.onChunk?.(text)
}
})
task.onError((err) => {
resolveOpen?.()
isStreaming.value = false
handlers.onError?.(err?.errMsg || 'WebSocket 连接失败')
})
task.onClose(() => {
socketTask = null
if (!closedByUser) {
isStreaming.value = false
handlers.onError?.('连接已断开,请重试')
}
})
return pendingOpen
}
/** 发送一条流式对话消息(自动建立/复用连接) */
async function sendMessage(
payload: { message: string, session_id?: string | null },
handler: AiChatStreamHandlers,
): Promise<void> {
handlers = handler
isStreaming.value = true
try {
await ensureConnected()
socketTask?.send({
data: JSON.stringify({ message: payload.message, session_id: payload.session_id || undefined }),
})
}
catch {
isStreaming.value = false
handlers.onError?.('连接失败,请稍后重试')
}
}
/** 停止当前生成(后端停止后返回 [STOPPED]) */
function stop() {
if (!isStreaming.value)
return
try {
socketTask?.send({ data: JSON.stringify({ action: 'stop' }) })
}
catch { /* 忽略发送失败 */ }
}
/** 关闭连接(页面卸载时调用) */
function close() {
closedByUser = true
isStreaming.value = false
if (socketTask) {
socketTask.close({ code: 1000, reason: 'page unload' })
socketTask = null
}
}
return { isStreaming, sendMessage, stop, close }
}
@@ -1,129 +0,0 @@
import { TicketAPI } from '@/api/module_system/ticket'
/**
* 缓存的请求结果
*/
interface CachedData<T> {
data: T
timestamp: number
}
/**
* 请求中标记(防重复并发请求)
*/
const pendingRequests = new Map<string, Promise<any>>()
/**
* 本地缓存存储
*/
const cacheStore = new Map<string, CachedData<any>>()
/** 默认缓存有效期(30秒) */
const DEFAULT_TTL = 30_000
/**
* 带缓存的请求 composable
*
* 解决多个页面重复请求同一接口的问题(如 work 和 mine 页面都调用工单统计)。
* 提供本地内存缓存 + 并发去重 + TTL 过期机制。
*
* @example
* ```ts
* const { getCached } = useCachedRequest()
*
* // 多页面共享缓存,30秒内不重复请求
* const stats = await getCached('ticket-stats', () => fetchTicketStats(), 30_000)
* ```
*/
export function useCachedRequest() {
/**
* 获取带缓存的数据
*
* @param key 缓存键
* @param fetcher 数据获取函数
* @param ttl 缓存有效期(毫秒),默认 30 秒
* @returns 数据
*/
async function getCached<T>(key: string, fetcher: () => Promise<T>, ttl = DEFAULT_TTL): Promise<T> {
// 检查缓存是否有效
const cached = cacheStore.get(key)
if (cached && Date.now() - cached.timestamp < ttl) {
return cached.data as T
}
// 检查是否有进行中的相同请求
const pending = pendingRequests.get(key)
if (pending) {
return pending as Promise<T>
}
// 发起新请求
const promise = fetcher()
.then((data) => {
cacheStore.set(key, { data, timestamp: Date.now() })
pendingRequests.delete(key)
return data
})
.catch((error) => {
pendingRequests.delete(key)
throw error
})
pendingRequests.set(key, promise)
return promise
}
/** 强制刷新缓存 */
async function refresh<T>(key: string, fetcher: () => Promise<T>): Promise<T> {
cacheStore.delete(key)
return getCached(key, fetcher, 0)
}
/** 清除指定缓存 */
function invalidate(key: string): void {
cacheStore.delete(key)
}
/** 清除所有缓存 */
function clearAll(): void {
cacheStore.clear()
}
return {
getCached,
refresh,
invalidate,
clearAll,
}
}
/**
* 工单统计缓存(work 和 mine 页面共享)
*/
export interface TicketStats {
pending: number
processing: number
done: number
}
/**
* 获取工单统计(带缓存,多页面共享)
*
* 缓存键 'ticket-stats'TTL 30 秒。
* work 页面和 mine 页面切换时不会重复请求。
*/
export async function getTicketStats(): Promise<TicketStats> {
const { getCached } = useCachedRequest()
return getCached('ticket-stats', async () => {
const [pending, processing, done] = await Promise.allSettled([
TicketAPI.getPage({ page_no: 1, page_size: 1, status: '0' }),
TicketAPI.getPage({ page_no: 1, page_size: 1, status: '1' }),
TicketAPI.getPage({ page_no: 1, page_size: 1, status: '2' }),
])
return {
pending: pending.status === 'fulfilled' ? (pending.value.total || 0) : 0,
processing: processing.status === 'fulfilled' ? (processing.value.total || 0) : 0,
done: done.status === 'fulfilled' ? (done.value.total || 0) : 0,
}
})
}
@@ -1,106 +0,0 @@
import type { DialogOptions, DialogResult } from '@wot-ui/ui/components/wd-dialog/types'
import { defineStore } from 'pinia'
export type GlobalDialogOptions = DialogOptions & {
success?: (res: DialogResult) => void
fail?: (res: DialogResult) => void
}
interface GlobalDialog {
dialogOptions: GlobalDialogOptions | null
currentPage: string
}
type DialogType = NonNullable<DialogOptions['type']>
function isButtonPropsObject(value: unknown): value is Record<string, any> {
return value !== null && CommonUtil.isObj(value)
}
function normalizeButtonProps(props: unknown, text?: string) {
if (props === null) {
return null
}
if (isButtonPropsObject(props)) {
return {
...props,
...(text ? { text } : {}),
}
}
if (CommonUtil.isString(props) || text) {
return {
text: text || props,
}
}
if (props === undefined) {
return {}
}
return props
}
function withDefaultTypeOptions(option: GlobalDialogOptions, type?: DialogType): GlobalDialogOptions {
const next: GlobalDialogOptions = {
...option,
...(type ? { type } : {}),
}
if (next.showCancelButton === undefined) {
if (next.type === 'alert') {
next.showCancelButton = false
}
else if (next.type === 'confirm' || next.type === 'prompt') {
next.showCancelButton = true
}
}
return next
}
function normalizeDialogOptions(option: GlobalDialogOptions, type?: DialogType): GlobalDialogOptions {
const next = withDefaultTypeOptions(option, type)
next.confirmButtonProps = normalizeButtonProps(next.confirmButtonProps, next.confirmButtonText) as DialogOptions['confirmButtonProps']
if (next.showCancelButton === false) {
next.cancelButtonProps = null
}
else if (next.showCancelButton === true || next.cancelButtonProps !== undefined || next.cancelButtonText) {
next.cancelButtonProps = normalizeButtonProps(next.cancelButtonProps, next.cancelButtonText) as DialogOptions['cancelButtonProps']
}
return next
}
function normalizeOption(option: GlobalDialogOptions | string, type?: DialogType): GlobalDialogOptions {
return normalizeDialogOptions(CommonUtil.isString(option) ? { title: option } : option, type)
}
export const useGlobalDialog = defineStore('global-Dialog', {
state: (): GlobalDialog => ({
dialogOptions: null,
currentPage: '',
}),
actions: {
show(option: GlobalDialogOptions | string, type?: DialogType) {
this.currentPage = getCurrentPath()
this.dialogOptions = normalizeOption(option, type)
},
alert(option: GlobalDialogOptions | string) {
this.show(option, 'alert')
},
confirm(option: GlobalDialogOptions | string) {
this.show(option, 'confirm')
},
prompt(option: GlobalDialogOptions | string) {
this.show(option, 'prompt')
},
close() {
this.dialogOptions = null
this.currentPage = ''
},
},
})
@@ -1,45 +0,0 @@
/*
* @Author: weisheng
* @Date: 2025-09-25 20:32:22
* @LastEditTime: 2026-04-07 14:06:20
* @LastEditors: weisheng
* @Description:
* @FilePath: /wot-starter/src/composables/useGlobalLoading.ts
* 记得注释
*/
import type { ToastOptions } from '@wot-ui/ui/components/wd-toast/types'
import { defineStore } from 'pinia'
interface GlobalLoading {
loadingOptions: ToastOptions
currentPage: string
}
const defaultOptions: ToastOptions = {
show: false,
}
export const useGlobalLoading = defineStore('global-loading', {
state: (): GlobalLoading => ({
loadingOptions: defaultOptions,
currentPage: '',
}),
getters: {},
actions: {
// 加载提示
loading(option: ToastOptions | string) {
this.currentPage = getCurrentPath()
this.loadingOptions = CommonUtil.deepMerge({
iconName: 'loading',
duration: 0,
cover: true,
position: 'middle',
show: true,
}, typeof option === 'string' ? { msg: option } : option) as ToastOptions
},
// 关闭Toast
close() {
this.loadingOptions = defaultOptions
this.currentPage = ''
},
},
})
@@ -1,32 +0,0 @@
import { defineStore } from 'pinia'
export interface GlobalMessageOptions {
title?: string
content?: string
type?: 'alert' | 'confirm'
showCancel?: boolean
confirmText?: string
cancelText?: string
success?: (res: { confirm: boolean, cancel: boolean }) => void
fail?: (err: unknown) => void
}
/**
* 全局消息 storeGlobalMessage 组件监听 messageOptions
* 仅当触发页面与 currentPage 一致时展示,避免跨页误弹
*/
export const useGlobalMessage = defineStore('global-message', {
state: () => ({
messageOptions: null as GlobalMessageOptions | null,
currentPage: '',
}),
actions: {
show(options: GlobalMessageOptions) {
this.currentPage = getCurrentPath()
this.messageOptions = options
},
close() {
this.messageOptions = null
},
},
})
@@ -1,61 +0,0 @@
import type { ToastOptions } from '@wot-ui/ui/components/wd-toast/types'
import { defineStore } from 'pinia'
interface GlobalToast {
toastOptions: ToastOptions
currentPage: string
}
const defaultOptions: ToastOptions = {
duration: 2000,
show: false,
}
export const useGlobalToast = defineStore('global-toast', {
state: (): GlobalToast => ({
toastOptions: defaultOptions,
currentPage: '',
}),
getters: {},
actions: {
// 打开Toast
show(option: ToastOptions | string) {
this.currentPage = getCurrentPath()
const options = CommonUtil.deepMerge(defaultOptions, typeof option === 'string' ? { msg: option } : option) as ToastOptions
this.toastOptions = CommonUtil.deepMerge(options, {
show: true,
position: options.position || 'middle',
}) as ToastOptions
},
// 成功提示
success(option: ToastOptions | string) {
this.show(CommonUtil.deepMerge({
iconName: 'success',
duration: 1500,
}, typeof option === 'string' ? { msg: option } : option) as ToastOptions)
},
// 关闭提示
error(option: ToastOptions | string) {
this.show(CommonUtil.deepMerge({
iconName: 'error',
direction: 'vertical',
}, typeof option === 'string' ? { msg: option } : option) as ToastOptions)
},
// 常规提示
info(option: ToastOptions | string) {
this.show(CommonUtil.deepMerge({
iconName: 'info',
}, typeof option === 'string' ? { msg: option } : option) as ToastOptions)
},
// 警告提示
warning(option: ToastOptions | string) {
this.show(CommonUtil.deepMerge({
iconName: 'warning',
}, typeof option === 'string' ? { msg: option } : option) as ToastOptions)
},
// 关闭Toast
close() {
this.toastOptions = defaultOptions
this.currentPage = ''
},
},
})
-102
View File
@@ -1,102 +0,0 @@
import type { Method } from 'alova'
import { usePagination } from 'alova/client'
import { computed, ref } from 'vue'
export interface ListPageParams {
page_no: number
page_size: number
}
export interface ListPageOptions<T> {
fetcher: (params: ListPageParams) => Promise<PageResult<T>>
pageSize?: number
onError?: (error: unknown) => void
}
/**
* 通用列表分页逻辑(基于 alova usePagination 封装)
* 底层使用 alova 状态管理,统一暴露 list/total/loading/error 三态与翻页动作
* 说明:fetcher 运行时返回的是 alova Methodhttp 层 get/post 均为 Method 实例,Method extends Promise),
* 这里仅做类型桥接,请求发送与错误处理完全交给 alova hook 管理
*/
export function useListPage<T>(options: ListPageOptions<T>) {
const { fetcher, pageSize = 10, onError } = options
const pageParams = ref<ListPageParams>({ page_no: 1, page_size: pageSize })
const {
data,
total,
loading,
error,
send,
onError: onPageError,
} = usePagination<any, T[], any>(
(pageNo: number, pageSizeNo: number) =>
fetcher({ page_no: pageNo, page_size: pageSizeNo }) as unknown as Method,
{
initialPage: 1,
initialPageSize: pageSize,
// 由页面手动触发(onLoad/搜索/翻页),禁用自动监听与预加载
immediate: false,
watchingStates: [],
preloadNextPage: false,
preloadPreviousPage: false,
// 列表页总是请求最新数据,禁用 alova 响应缓存
force: true,
// 响应已由 http 层 responded 解包为业务结构 { list, total }
data: res => (res as PageResult<T>).list ?? [],
total: res => (res as PageResult<T>).total ?? 0,
},
)
onPageError(({ error: e }) => {
onError?.(e)
})
/** 加载当前页数据 */
async function loadData() {
try {
await send(pageParams.value.page_no, pageParams.value.page_size)
}
catch {
// 错误已由 onPageError → onError 统一处理,避免未捕获 Promise 告警
}
finally {
// 收起下拉刷新指示器(onPullDownRefresh → loadData 场景;非刷新场景调用无害)
uni.stopPullDownRefresh()
}
}
/** 上一页 */
async function loadPrev() {
if (pageParams.value.page_no <= 1)
return
pageParams.value.page_no -= 1
await loadData()
}
/** 下一页 */
async function loadNext() {
pageParams.value.page_no += 1
await loadData()
}
/** 跳回第一页(搜索/重置时使用) */
async function toFirst() {
pageParams.value.page_no = 1
await loadData()
}
return {
list: computed<T[]>(() => data.value ?? []),
total: computed<number>(() => total.value ?? 0),
loading,
error,
pageParams,
loadData,
loadPrev,
loadNext,
toFirst,
}
}
@@ -1,135 +0,0 @@
import type { ThemeColorOption, ThemeMode } from '@/composables/types/theme'
import { themeColorOptions } from '@/composables/types/theme'
import { initializeThemeOnce, subscribeSystemThemeChange } from '@/utils/systemTheme'
/**
* 完整版主题管理组合式API
*
* 功能特性:
* - 支持手动切换暗黑模式
* - 支持主题色选择
* - 支持跟随系统主题
* - 自动同步导航栏颜色
* - 持久化用户设置
*
* 适用场景:
* - 需要用户手动控制主题的应用
* - 需要主题色自定义的应用
* - 需要完整主题管理功能的复杂应用
*
* @example
* ```vue
* <script setup>
* import { useManualTheme } from '@/composables/useManualTheme'
*
* const {
* theme,
* isDark,
* toggleTheme,
* openThemeColorPicker,
* currentThemeColor,
* themeVars
* } = useManualTheme()
* </script>
*
* <template>
* <wd-config-provider :theme="theme" :theme-vars="themeVars">
* <view :class="{ 'dark-mode': isDark }">
* <wd-button @click="toggleTheme">切换主题</wd-button>
* <wd-button @click="openThemeColorPicker">选择主题色</wd-button>
* </view>
* </wd-config-provider>
* </template>
* ```
*/
export function useManualTheme() {
const store = useManualThemeStore()
const showThemeColorSheet = ref(false)
let stopThemeChangeListener: (() => void) | undefined
/**
* 切换暗黑模式
* @param mode 指定主题模式,不传则自动切换
* @param isFollowSystem 是否跟随系统
*/
function toggleTheme(mode?: ThemeMode, isFollowSystem: boolean = false) {
store.toggleTheme(mode, isFollowSystem)
}
/**
* 打开主题色选择器
*/
function openThemeColorPicker() {
showThemeColorSheet.value = true
}
/**
* 关闭主题色选择器
*/
function closeThemeColorPicker() {
showThemeColorSheet.value = false
}
/**
* 选择主题色
* @param option 主题色选项
*/
function selectThemeColor(option: ThemeColorOption) {
store.setCurrentThemeColor(option)
closeThemeColorPicker()
}
/**
* 初始化主题
*/
function initTheme() {
store.initTheme()
}
// 组件挂载前初始化主题
onBeforeMount(() => {
initializeThemeOnce(store, initTheme)
stopThemeChangeListener = subscribeSystemThemeChange(store, (res) => {
if (store.followSystem) {
store.toggleTheme(res.theme, true)
}
})
})
// 页面显示时更新导航栏颜色,确保每次切换页面时导航栏颜色都是正确的
onShow(() => {
store.setNavigationBarColor()
})
// 组件卸载时清理监听
onUnmounted(() => {
stopThemeChangeListener?.()
stopThemeChangeListener = undefined
})
return {
// 状态
theme: computed(() => store.theme),
isDark: computed(() => store.isDark),
followSystem: computed(() => store.followSystem),
hasUserSet: computed(() => store.hasUserSet),
currentThemeColor: computed(() => store.currentThemeColor),
themeVars: computed(() => store.themeVars),
showThemeColorSheet,
// 常量
themeColorOptions,
// 方法
initTheme,
toggleTheme,
setFollowSystem: store.setFollowSystem,
openThemeColorPicker,
closeThemeColorPicker,
selectThemeColor,
}
}
// 导出类型和常量供外部使用
export type { ThemeColorOption, ThemeMode }
export { themeColorOptions }

Some files were not shown because too many files have changed in this diff Show More