From 4c6243a27480671148554f858707ea108b7c973a Mon Sep 17 00:00:00 2001 From: tanxin Date: Thu, 29 Oct 2026 10:08:39 +0800 Subject: [PATCH] Initial commit --- .gitignore | 8 + App.uvue | 51 + README.md | 557 ++++++++ api/auth.ts | 138 ++ api/student.ts | 343 +++++ components/countdown-btn.uvue | 90 ++ components/main-tab-bar.uvue | 64 + components/qr-scanner.uvue | 149 ++ components/study-activity-chart.uvue | 29 + components/study-trend-chart.uvue | 57 + index.html | 20 + main.ts | 9 + manifest.json | 72 + package-lock.json | 60 + package.json | 16 + pages.json | 130 ++ pages/alerts/index.uvue | 68 + pages/growth/index.uvue | 75 + pages/index/index.uvue | 649 +++++++++ pages/login/forgot-password.uvue | 360 +++++ pages/login/index.uvue | 333 +++++ pages/login/verify-code.uvue | 230 +++ pages/profile/index.uvue | 159 +++ pages/profile/verify-student.uvue | 130 ++ pages/register/complete.uvue | 322 +++++ pages/register/index.uvue | 14 + pages/register/input-student.uvue | 546 +++++++ pages/register/parent-account.uvue | 496 +++++++ pages/register/scan-student.uvue | 515 +++++++ pages/study/index.uvue | 155 ++ pages/tasks/index.uvue | 85 ++ pages/today-learning/index.uvue | 147 ++ pages/vocabulary/index.uvue | 92 ++ platformConfig.json | 7 + screenshots/study-report-modules.png | Bin 0 -> 32466 bytes screenshots/vocabulary.png | Bin 0 -> 58986 bytes static/README.md | 24 + static/logo.png | Bin 0 -> 4023 bytes store/home.ts | 24 + store/registration.ts | 8 + store/user.ts | 170 +++ uni.scss | 76 + uni_modules/uni-badge-view/changelog.md | 6 + .../uni-badge-view/uni-badge-view.uvue | 287 ++++ uni_modules/uni-badge-view/package.json | 74 + uni_modules/uni-badge-view/readme.md | 34 + uni_modules/uni-collapse-x/changelog.md | 4 + .../uni-collapse-x/components/common.ts | 3 + uni_modules/uni-collapse-x/components/type.ts | 6 + .../uni-collapse-item/uni-collapse-item.uvue | 739 ++++++++++ .../components/uni-collapse/uni-collapse.uvue | 129 ++ uni_modules/uni-collapse-x/package.json | 74 + uni_modules/uni-collapse-x/readme.md | 96 ++ uni_modules/uni-combox-x/changelog.md | 6 + .../components/uni-combox/uni-combox.uvue | 660 +++++++++ uni_modules/uni-combox-x/package.json | 91 ++ uni_modules/uni-combox-x/readme.md | 122 ++ uni_modules/uni-drag-cell/changelog.md | 2 + .../uni-drag-cell/uni-drag-cell.uvue | 1263 +++++++++++++++++ uni_modules/uni-drag-cell/package.json | 73 + uni_modules/uni-drag-cell/readme.md | 331 +++++ uni_modules/uni-fab-button/changelog.md | 2 + .../uni-fab-button/uni-fab-button.uvue | 152 ++ uni_modules/uni-fab-button/package.json | 73 + uni_modules/uni-fab-button/readme.md | 14 + uni_modules/uni-icons-x/changelog.md | 4 + .../components/uni-icons/icons.uts | 169 +++ .../components/uni-icons/uni-icons.uvue | 66 + .../components/uni-icons/uniicons.ttf | Bin 0 -> 35824 bytes uni_modules/uni-icons-x/package.json | 100 ++ uni_modules/uni-icons-x/readme.md | 1 + uni_modules/uni-index-bar/changelog.md | 4 + .../uni-index-bar/uni-index-bar.uvue | 384 +++++ uni_modules/uni-index-bar/package.json | 75 + uni_modules/uni-index-bar/readme.md | 98 ++ uni_modules/uni-link-x/changelog.md | 2 + .../components/uni-link/uni-link.uvue | 362 +++++ uni_modules/uni-link-x/package.json | 76 + .../uni-link-x/pages/link-webview.uvue | 28 + uni_modules/uni-link-x/readme.md | 45 + uni_modules/uni-nav-bar-x/changelog.md | 4 + .../components/uni-nav-bar/uni-nav-bar.uvue | 361 +++++ uni_modules/uni-nav-bar-x/package.json | 73 + uni_modules/uni-nav-bar-x/readme.md | 63 + uni_modules/uni-number-box-x/changelog.md | 4 + .../uni-number-box/uni-number-box.uvue | 802 +++++++++++ uni_modules/uni-number-box-x/package.json | 74 + uni_modules/uni-number-box-x/readme.md | 58 + uni_modules/uni-rate-x/changelog.md | 4 + .../components/uni-rate/uni-rate.uvue | 624 ++++++++ uni_modules/uni-rate-x/package.json | 73 + uni_modules/uni-rate-x/readme.md | 32 + uni_modules/uni-refresh-box/changelog.md | 2 + .../uni-refresh-box/uni-refresh-box.uvue | 526 +++++++ uni_modules/uni-refresh-box/package.json | 73 + uni_modules/uni-refresh-box/readme.md | 80 ++ uni_modules/uni-tab-bar/changelog.md | 6 + .../components/uni-tab-bar/uni-tab-bar.uvue | 139 ++ .../uni-tab-content/uni-tab-content.uvue | 104 ++ .../components/uni-tab-item/uni-tab-item.uvue | 187 +++ .../uni-tab-midbutton/uni-tab-midbutton.uvue | 159 +++ .../components/uni-tab/uni-tab.uvue | 222 +++ uni_modules/uni-tab-bar/package.json | 103 ++ uni_modules/uni-tab-bar/readme.md | 93 ++ uni_modules/uni-tab-bar/todo.md | 10 + uni_modules/uni-time-format/changelog.md | 2 + .../uni-time-format/uni-time-format.uvue | 290 ++++ uni_modules/uni-time-format/package.json | 73 + uni_modules/uni-time-format/readme.md | 40 + uni_modules/uni-ui-x/changelog.md | 19 + .../components/uni-ui-x/uni-ui-x.uvue | 0 uni_modules/uni-ui-x/package.json | 106 ++ uni_modules/uni-ui-x/readme.md | 127 ++ uni_modules/uts-openSchema/changelog.md | 2 + uni_modules/uts-openSchema/package.json | 75 + uni_modules/uts-openSchema/readme.md | 57 + .../utssdk/app-android/config.json | 3 + .../utssdk/app-android/index.uts | 27 + .../utssdk/app-harmony/index.uts | 19 + .../uts-openSchema/utssdk/app-ios/config.json | 3 + .../uts-openSchema/utssdk/app-ios/index.uts | 22 + .../uts-openSchema/utssdk/interface.uts | 2 + .../uts-openSchema/utssdk/web/index.uts | 12 + utils/request.ts | 471 ++++++ 124 files changed, 17359 insertions(+) create mode 100644 .gitignore create mode 100644 App.uvue create mode 100644 README.md create mode 100644 api/auth.ts create mode 100644 api/student.ts create mode 100644 components/countdown-btn.uvue create mode 100644 components/main-tab-bar.uvue create mode 100644 components/qr-scanner.uvue create mode 100644 components/study-activity-chart.uvue create mode 100644 components/study-trend-chart.uvue create mode 100644 index.html create mode 100644 main.ts create mode 100644 manifest.json create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 pages.json create mode 100644 pages/alerts/index.uvue create mode 100644 pages/growth/index.uvue create mode 100644 pages/index/index.uvue create mode 100644 pages/login/forgot-password.uvue create mode 100644 pages/login/index.uvue create mode 100644 pages/login/verify-code.uvue create mode 100644 pages/profile/index.uvue create mode 100644 pages/profile/verify-student.uvue create mode 100644 pages/register/complete.uvue create mode 100644 pages/register/index.uvue create mode 100644 pages/register/input-student.uvue create mode 100644 pages/register/parent-account.uvue create mode 100644 pages/register/scan-student.uvue create mode 100644 pages/study/index.uvue create mode 100644 pages/tasks/index.uvue create mode 100644 pages/today-learning/index.uvue create mode 100644 pages/vocabulary/index.uvue create mode 100644 platformConfig.json create mode 100644 screenshots/study-report-modules.png create mode 100644 screenshots/vocabulary.png create mode 100644 static/README.md create mode 100644 static/logo.png create mode 100644 store/home.ts create mode 100644 store/registration.ts create mode 100644 store/user.ts create mode 100644 uni.scss create mode 100644 uni_modules/uni-badge-view/changelog.md create mode 100644 uni_modules/uni-badge-view/components/uni-badge-view/uni-badge-view.uvue create mode 100644 uni_modules/uni-badge-view/package.json create mode 100644 uni_modules/uni-badge-view/readme.md create mode 100644 uni_modules/uni-collapse-x/changelog.md create mode 100644 uni_modules/uni-collapse-x/components/common.ts create mode 100644 uni_modules/uni-collapse-x/components/type.ts create mode 100644 uni_modules/uni-collapse-x/components/uni-collapse-item/uni-collapse-item.uvue create mode 100644 uni_modules/uni-collapse-x/components/uni-collapse/uni-collapse.uvue create mode 100644 uni_modules/uni-collapse-x/package.json create mode 100644 uni_modules/uni-collapse-x/readme.md create mode 100644 uni_modules/uni-combox-x/changelog.md create mode 100644 uni_modules/uni-combox-x/components/uni-combox/uni-combox.uvue create mode 100644 uni_modules/uni-combox-x/package.json create mode 100644 uni_modules/uni-combox-x/readme.md create mode 100644 uni_modules/uni-drag-cell/changelog.md create mode 100644 uni_modules/uni-drag-cell/components/uni-drag-cell/uni-drag-cell.uvue create mode 100644 uni_modules/uni-drag-cell/package.json create mode 100644 uni_modules/uni-drag-cell/readme.md create mode 100644 uni_modules/uni-fab-button/changelog.md create mode 100644 uni_modules/uni-fab-button/components/uni-fab-button/uni-fab-button.uvue create mode 100644 uni_modules/uni-fab-button/package.json create mode 100644 uni_modules/uni-fab-button/readme.md create mode 100644 uni_modules/uni-icons-x/changelog.md create mode 100644 uni_modules/uni-icons-x/components/uni-icons/icons.uts create mode 100644 uni_modules/uni-icons-x/components/uni-icons/uni-icons.uvue create mode 100644 uni_modules/uni-icons-x/components/uni-icons/uniicons.ttf create mode 100644 uni_modules/uni-icons-x/package.json create mode 100644 uni_modules/uni-icons-x/readme.md create mode 100644 uni_modules/uni-index-bar/changelog.md create mode 100644 uni_modules/uni-index-bar/components/uni-index-bar/uni-index-bar.uvue create mode 100644 uni_modules/uni-index-bar/package.json create mode 100644 uni_modules/uni-index-bar/readme.md create mode 100644 uni_modules/uni-link-x/changelog.md create mode 100644 uni_modules/uni-link-x/components/uni-link/uni-link.uvue create mode 100644 uni_modules/uni-link-x/package.json create mode 100644 uni_modules/uni-link-x/pages/link-webview.uvue create mode 100644 uni_modules/uni-link-x/readme.md create mode 100644 uni_modules/uni-nav-bar-x/changelog.md create mode 100644 uni_modules/uni-nav-bar-x/components/uni-nav-bar/uni-nav-bar.uvue create mode 100644 uni_modules/uni-nav-bar-x/package.json create mode 100644 uni_modules/uni-nav-bar-x/readme.md create mode 100644 uni_modules/uni-number-box-x/changelog.md create mode 100644 uni_modules/uni-number-box-x/components/uni-number-box/uni-number-box.uvue create mode 100644 uni_modules/uni-number-box-x/package.json create mode 100644 uni_modules/uni-number-box-x/readme.md create mode 100644 uni_modules/uni-rate-x/changelog.md create mode 100644 uni_modules/uni-rate-x/components/uni-rate/uni-rate.uvue create mode 100644 uni_modules/uni-rate-x/package.json create mode 100644 uni_modules/uni-rate-x/readme.md create mode 100644 uni_modules/uni-refresh-box/changelog.md create mode 100644 uni_modules/uni-refresh-box/components/uni-refresh-box/uni-refresh-box.uvue create mode 100644 uni_modules/uni-refresh-box/package.json create mode 100644 uni_modules/uni-refresh-box/readme.md create mode 100644 uni_modules/uni-tab-bar/changelog.md create mode 100644 uni_modules/uni-tab-bar/components/uni-tab-bar/uni-tab-bar.uvue create mode 100644 uni_modules/uni-tab-bar/components/uni-tab-content/uni-tab-content.uvue create mode 100644 uni_modules/uni-tab-bar/components/uni-tab-item/uni-tab-item.uvue create mode 100644 uni_modules/uni-tab-bar/components/uni-tab-midbutton/uni-tab-midbutton.uvue create mode 100644 uni_modules/uni-tab-bar/components/uni-tab/uni-tab.uvue create mode 100644 uni_modules/uni-tab-bar/package.json create mode 100644 uni_modules/uni-tab-bar/readme.md create mode 100644 uni_modules/uni-tab-bar/todo.md create mode 100644 uni_modules/uni-time-format/changelog.md create mode 100644 uni_modules/uni-time-format/components/uni-time-format/uni-time-format.uvue create mode 100644 uni_modules/uni-time-format/package.json create mode 100644 uni_modules/uni-time-format/readme.md create mode 100644 uni_modules/uni-ui-x/changelog.md create mode 100644 uni_modules/uni-ui-x/components/uni-ui-x/uni-ui-x.uvue create mode 100644 uni_modules/uni-ui-x/package.json create mode 100644 uni_modules/uni-ui-x/readme.md create mode 100644 uni_modules/uts-openSchema/changelog.md create mode 100644 uni_modules/uts-openSchema/package.json create mode 100644 uni_modules/uts-openSchema/readme.md create mode 100644 uni_modules/uts-openSchema/utssdk/app-android/config.json create mode 100644 uni_modules/uts-openSchema/utssdk/app-android/index.uts create mode 100644 uni_modules/uts-openSchema/utssdk/app-harmony/index.uts create mode 100644 uni_modules/uts-openSchema/utssdk/app-ios/config.json create mode 100644 uni_modules/uts-openSchema/utssdk/app-ios/index.uts create mode 100644 uni_modules/uts-openSchema/utssdk/interface.uts create mode 100644 uni_modules/uts-openSchema/utssdk/web/index.uts create mode 100644 utils/request.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..296a331 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +node_modules/ +unpackage/ +.hbuilderx/ +.env +.env.* +*.keystore +*.jks +*.p12 diff --git a/App.uvue b/App.uvue new file mode 100644 index 0000000..0aeb3ec --- /dev/null +++ b/App.uvue @@ -0,0 +1,51 @@ + + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..8ebc414 --- /dev/null +++ b/README.md @@ -0,0 +1,557 @@ +# 爱学蝶变家长端 + +## 1. 文档用途 + +本文档是本项目的实现契约。阅读本文档后,其他 AI 应能够在不阅读原始页面代码的情况下,使用 uni-app x 重新生成结构、功能、校验、接口调用、状态共享和页面跳转基本一致的家长端应用。 + +实现或重构时必须以本文档为准,并保持以下行为不变: + +- 登录、验证码登录、找回密码和家长注册流程完整可用。 +- 注册流程最多绑定 3 名学生,至少绑定 1 名学生才能继续。 +- 学生信息不能通过 URL 参数传递。 +- Web 与 Android/iOS 的扫码实现必须区分平台。 +- 业务 API 使用统一请求封装,当前默认启用 mock 响应。 +- 表单和页面必须适配手机窄屏及底部安全区。 + +## 2. 技术基线 + +| 项目项 | 当前约定 | +| --- | --- | +| 框架 | uni-app x | +| Vue | Vue 3,` + + diff --git a/components/main-tab-bar.uvue b/components/main-tab-bar.uvue new file mode 100644 index 0000000..d083dd5 --- /dev/null +++ b/components/main-tab-bar.uvue @@ -0,0 +1,64 @@ + + + + + diff --git a/components/qr-scanner.uvue b/components/qr-scanner.uvue new file mode 100644 index 0000000..5d16463 --- /dev/null +++ b/components/qr-scanner.uvue @@ -0,0 +1,149 @@ + + + + + diff --git a/components/study-activity-chart.uvue b/components/study-activity-chart.uvue new file mode 100644 index 0000000..b4178d4 --- /dev/null +++ b/components/study-activity-chart.uvue @@ -0,0 +1,29 @@ + + + + + diff --git a/components/study-trend-chart.uvue b/components/study-trend-chart.uvue new file mode 100644 index 0000000..92fed8d --- /dev/null +++ b/components/study-trend-chart.uvue @@ -0,0 +1,57 @@ + + + + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..8793fef --- /dev/null +++ b/index.html @@ -0,0 +1,20 @@ + + + + + + + + + + +
+ + + \ No newline at end of file diff --git a/main.ts b/main.ts new file mode 100644 index 0000000..8bdcc86 --- /dev/null +++ b/main.ts @@ -0,0 +1,9 @@ +import App from './App.uvue' + +import { createSSRApp } from 'vue' +export function createApp() { + const app = createSSRApp(App) + return { + app + } +} \ No newline at end of file diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..b00b905 --- /dev/null +++ b/manifest.json @@ -0,0 +1,72 @@ +{ + "name": "爱学蝶变家长端", + "appid": "__UNI__800D1E3", + "description": "", + "versionName": "1.0.0", + "versionCode": "100", + "uni-app-x": { + "styleIsolationVersion": "2", + "vapor": true, + "vapor-default-script-lang": "ts", + "vapor-render-target": "bytecode" + }, + "quickapp": {}, + "mp-weixin": { + "appid": "", + "setting": { + "urlCheck": false + }, + "usingComponents": true + }, + "mp-alipay": { + "usingComponents": true + }, + "mp-baidu": { + "usingComponents": true + }, + "mp-toutiao": { + "usingComponents": true + }, + "uniStatistics": { + "enable": false + }, + "vueVersion": "3", + "app": { + "distribute": { + "icons": { + "android": { + "hdpi": "", + "xhdpi": "", + "xxhdpi": "", + "xxxhdpi": "" + } + } + } + }, + "app-android": { + "distribute": { + "modules": {}, + "icons": { + "hdpi": "", + "xhdpi": "", + "xxhdpi": "", + "xxxhdpi": "" + }, + "splashScreens": { + "default": {} + } + } + }, + "app-ios": { + "distribute": { + "modules": {}, + "icons": {}, + "splashScreens": {} + } + }, + "web": { + "router": { + "mode": "" + } + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..5e38367 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,60 @@ +{ + "name": "app.k12.parents", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "app.k12.parents", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@zxing/browser": "^0.2.1" + } + }, + "node_modules/@zxing/browser": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@zxing/browser/-/browser-0.2.1.tgz", + "integrity": "sha512-92pVfVDUXbc15xu9vIEmhNyqIEASMEkDF92CwT6T+7sg2EHXJRktH9CQKoQSXe51UtbNsj+Fm09H/JBWHMs/Sw==", + "license": "MIT", + "optionalDependencies": { + "@zxing/text-encoding": "^0.9.0" + }, + "peerDependencies": { + "@zxing/library": "^0.23.0" + } + }, + "node_modules/@zxing/library": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@zxing/library/-/library-0.23.0.tgz", + "integrity": "sha512-6fkkoFwP8CHxl6ugnPsj74PLJgX2iRv5zczGAyt5OBzQgxFhuhF0NCEc4t4OvSr8xAv2MRLlI0Iu9ZGDZQ2urA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "ts-custom-error": "^3.3.1" + }, + "engines": { + "node": ">= 24.0.0" + }, + "optionalDependencies": { + "@zxing/text-encoding": "~0.9.0" + } + }, + "node_modules/@zxing/text-encoding": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz", + "integrity": "sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==", + "license": "(Unlicense OR Apache-2.0)", + "optional": true + }, + "node_modules/ts-custom-error": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/ts-custom-error/-/ts-custom-error-3.3.1.tgz", + "integrity": "sha512-5OX1tzOjxWEgsr/YEUWSuPrQ00deKLh6D7OTWcvNHm12/7QPyRh8SYpyWvA4IZv8H/+GQWQEh/kwo95Q9OVW1A==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..9a3cacb --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "app.k12.parents", + "version": "1.0.0", + "description": "家长学情是基于 uni-app x Vapor 模式开发的移动端应用。当前实现家长账号的注册、密码登录、验证码登录和密码重置流程。", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "commonjs", + "dependencies": { + "@zxing/browser": "^0.2.1" + } +} diff --git a/pages.json b/pages.json new file mode 100644 index 0000000..b9b3ddc --- /dev/null +++ b/pages.json @@ -0,0 +1,130 @@ +{ + "easycom": { + "autoscan": true + }, + "pages": [ + { + "path": "pages/login/index", + "style": { + "navigationBarTitleText": "登录", + "navigationBarBackgroundColor": "#FFFFFF", + "navigationBarTextStyle": "black" + } + }, + { + "path": "pages/index/index", + "style": { + "navigationBarTitleText": "首页" + } + }, + { + "path": "pages/study/index", + "style": { + "navigationStyle": "custom" + } + }, + { + "path": "pages/vocabulary/index", + "style": { + "navigationStyle": "custom" + } + }, + { + "path": "pages/growth/index", + "style": { + "navigationStyle": "custom" + } + }, + { + "path": "pages/tasks/index", + "style": { + "navigationBarTitleText": "任务" + } + }, + { + "path": "pages/profile/index", + "style": { + "navigationBarTitleText": "我的" + } + }, + { + "path": "pages/profile/verify-student", + "style": { "navigationBarTitleText": "验证学生" } + }, + { + "path": "pages/alerts/index", + "style": { + "navigationBarTitleText": "预警信息" + } + }, + { + "path": "pages/today-learning/index", + "style": { + "navigationStyle": "custom" + } + }, + { + "path": "pages/login/verify-code", + "style": { + "navigationBarTitleText": "验证码登录", + "navigationBarBackgroundColor": "#FFFFFF", + "navigationBarTextStyle": "black" + } + }, + { + "path": "pages/login/forgot-password", + "style": { + "navigationBarTitleText": "忘记密码", + "navigationBarBackgroundColor": "#FFFFFF", + "navigationBarTextStyle": "black" + } + }, + { + "path": "pages/register/index", + "style": { + "navigationBarTitleText": "家长注册", + "navigationBarBackgroundColor": "#FFFFFF", + "navigationBarTextStyle": "black" + } + }, + { + "path": "pages/register/scan-student", + "style": { + "navigationBarTitleText": "家长注册", + "navigationBarBackgroundColor": "#FFFFFF", + "navigationBarTextStyle": "black" + } + }, + { + "path": "pages/register/input-student", + "style": { + "navigationBarTitleText": "家长注册", + "navigationBarBackgroundColor": "#FFFFFF", + "navigationBarTextStyle": "black" + } + }, + { + "path": "pages/register/parent-account", + "style": { + "navigationBarTitleText": "家长注册", + "navigationBarBackgroundColor": "#FFFFFF", + "navigationBarTextStyle": "black" + } + }, + { + "path": "pages/register/complete", + "style": { + "navigationBarTitleText": "注册完成", + "navigationBarBackgroundColor": "#FFFFFF", + "navigationBarTextStyle": "black" + } + } + ], + "globalStyle": { + "navigationBarTextStyle": "black", + "navigationBarTitleText": "家长学情", + "navigationBarBackgroundColor": "#FFFFFF", + "backgroundColor": "#F5F5F5" + }, + "uniIdRouter": {} +} diff --git a/pages/alerts/index.uvue b/pages/alerts/index.uvue new file mode 100644 index 0000000..d294c0c --- /dev/null +++ b/pages/alerts/index.uvue @@ -0,0 +1,68 @@ + + + + + diff --git a/pages/growth/index.uvue b/pages/growth/index.uvue new file mode 100644 index 0000000..8fa808f --- /dev/null +++ b/pages/growth/index.uvue @@ -0,0 +1,75 @@ + + + + + diff --git a/pages/index/index.uvue b/pages/index/index.uvue new file mode 100644 index 0000000..c320e5b --- /dev/null +++ b/pages/index/index.uvue @@ -0,0 +1,649 @@ + + + + + diff --git a/pages/login/forgot-password.uvue b/pages/login/forgot-password.uvue new file mode 100644 index 0000000..1396553 --- /dev/null +++ b/pages/login/forgot-password.uvue @@ -0,0 +1,360 @@ + + + + + diff --git a/pages/login/index.uvue b/pages/login/index.uvue new file mode 100644 index 0000000..dc4e8f8 --- /dev/null +++ b/pages/login/index.uvue @@ -0,0 +1,333 @@ + + + + + diff --git a/pages/login/verify-code.uvue b/pages/login/verify-code.uvue new file mode 100644 index 0000000..43fc28e --- /dev/null +++ b/pages/login/verify-code.uvue @@ -0,0 +1,230 @@ + + + + + diff --git a/pages/profile/index.uvue b/pages/profile/index.uvue new file mode 100644 index 0000000..24e0a6a --- /dev/null +++ b/pages/profile/index.uvue @@ -0,0 +1,159 @@ + + + + + diff --git a/pages/profile/verify-student.uvue b/pages/profile/verify-student.uvue new file mode 100644 index 0000000..c09474c --- /dev/null +++ b/pages/profile/verify-student.uvue @@ -0,0 +1,130 @@ + + + + + diff --git a/pages/register/complete.uvue b/pages/register/complete.uvue new file mode 100644 index 0000000..3da125a --- /dev/null +++ b/pages/register/complete.uvue @@ -0,0 +1,322 @@ + + + + + diff --git a/pages/register/index.uvue b/pages/register/index.uvue new file mode 100644 index 0000000..695c9ee --- /dev/null +++ b/pages/register/index.uvue @@ -0,0 +1,14 @@ + + + diff --git a/pages/register/input-student.uvue b/pages/register/input-student.uvue new file mode 100644 index 0000000..5167459 --- /dev/null +++ b/pages/register/input-student.uvue @@ -0,0 +1,546 @@ + + + + + diff --git a/pages/register/parent-account.uvue b/pages/register/parent-account.uvue new file mode 100644 index 0000000..4140214 --- /dev/null +++ b/pages/register/parent-account.uvue @@ -0,0 +1,496 @@ + + + + + diff --git a/pages/register/scan-student.uvue b/pages/register/scan-student.uvue new file mode 100644 index 0000000..8d0dd10 --- /dev/null +++ b/pages/register/scan-student.uvue @@ -0,0 +1,515 @@ + + + + + diff --git a/pages/study/index.uvue b/pages/study/index.uvue new file mode 100644 index 0000000..5ea7f0e --- /dev/null +++ b/pages/study/index.uvue @@ -0,0 +1,155 @@ + + + + + diff --git a/pages/tasks/index.uvue b/pages/tasks/index.uvue new file mode 100644 index 0000000..c7743b7 --- /dev/null +++ b/pages/tasks/index.uvue @@ -0,0 +1,85 @@ + + + + + diff --git a/pages/today-learning/index.uvue b/pages/today-learning/index.uvue new file mode 100644 index 0000000..d4c0815 --- /dev/null +++ b/pages/today-learning/index.uvue @@ -0,0 +1,147 @@ + + + + + diff --git a/pages/vocabulary/index.uvue b/pages/vocabulary/index.uvue new file mode 100644 index 0000000..8908e46 --- /dev/null +++ b/pages/vocabulary/index.uvue @@ -0,0 +1,92 @@ + + + + + diff --git a/platformConfig.json b/platformConfig.json new file mode 100644 index 0000000..896f669 --- /dev/null +++ b/platformConfig.json @@ -0,0 +1,7 @@ + +// 参考链接 https://doc.dcloud.net.cn/uni-app-x/tutorial/ls-plugin.html#setting +{ + "targets": [ + "APP-ANDROID" + ] +} \ No newline at end of file diff --git a/screenshots/study-report-modules.png b/screenshots/study-report-modules.png new file mode 100644 index 0000000000000000000000000000000000000000..ad4ac16e5fcd80719a5e0ee7d1423517229ab0ff GIT binary patch literal 32466 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 标签 + +## 其他资源 + +后续可以根据需要添加其他图片资源,如: +- 引导页图片 +- 默认头像 +- 占位图等 diff --git a/static/logo.png b/static/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b5771e209bb677e2ebd5ff766ad5ee11790f305a GIT binary patch literal 4023 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 SYs}WS&%*SCNx89m_(null) +export const homeStudentMetrics = ref(null) +export const homeStudentAlerts = ref(null) + +export function setHomeStudentProfile(profile: StudentHomeProfile) { + homeStudentProfile.value = profile +} + +export function setHomeStudentMetrics(metrics: StudentHomeMetrics) { + homeStudentMetrics.value = metrics +} + +export function setHomeStudentAlerts(alerts: StudentAlert[]) { + homeStudentAlerts.value = alerts +} + +export function clearHomeStudentProfile() { + homeStudentProfile.value = null + homeStudentMetrics.value = null + homeStudentAlerts.value = null +} diff --git a/store/registration.ts b/store/registration.ts new file mode 100644 index 0000000..42ec866 --- /dev/null +++ b/store/registration.ts @@ -0,0 +1,8 @@ +import type { StudentInfo } from '../api/student' + +// 仅在注册流程中保存待绑定学生,供扫码与手动输入页面共同使用。 +export const registrationStudents = ref([]) + +export function resetRegistrationStudents() { + registrationStudents.value.splice(0) +} diff --git a/store/user.ts b/store/user.ts new file mode 100644 index 0000000..14f720b --- /dev/null +++ b/store/user.ts @@ -0,0 +1,170 @@ +/** + * 用户状态管理 + * 使用 uni-app x 的响应式系统 + */ + +import type { UserInfo } from '../api/auth' +import { clearHomeStudentProfile } from './home' + +// 用户状态类型 +type UserState = { + userInfo: UserInfo | null + token: string + isLoggedIn: boolean + rememberPassword: boolean + rememberedPhone: string + rememberedPassword: string +} + +// 创建响应式用户状态 +const userState = reactive({ + userInfo: null, + token: '', + isLoggedIn: false, + rememberPassword: false, + rememberedPhone: '', + rememberedPassword: '' +}) + +/** + * 初始化用户状态 + * 从本地存储恢复登录状态和记住密码信息 + */ +export function initUserStore() { + // 恢复登录状态 + const token = uni.getStorageSync('token') as string + const userInfo = uni.getStorageSync('userInfo') as UserInfo | null + + if (token && userInfo) { + userState.token = token + userState.userInfo = userInfo + userState.isLoggedIn = true + } + + // 恢复记住密码信息 + const rememberInfo = uni.getStorageSync('rememberInfo') as any + if (rememberInfo && rememberInfo.remember) { + userState.rememberPassword = true + userState.rememberedPhone = rememberInfo.phone + userState.rememberedPassword = rememberInfo.password + } +} + +/** + * 设置用户信息 + */ +export function setUserInfo(userInfo: UserInfo) { + userState.userInfo = userInfo + userState.token = userInfo.token + userState.isLoggedIn = true + + // 保存到本地存储 + uni.setStorageSync('token', userInfo.token) + uni.setStorageSync('refreshToken', userInfo.refreshToken) + uni.setStorageSync('userInfo', userInfo) +} + +/** + * 保存记住密码信息 + */ +export function saveRememberInfo(phone: string, password: string, remember: boolean) { + userState.rememberPassword = remember + userState.rememberedPhone = phone + userState.rememberedPassword = password + + if (remember) { + uni.setStorageSync('rememberInfo', { + phone, + password, + remember: true + }) + } else { + uni.removeStorageSync('rememberInfo') + } +} + +/** + * 获取记住的密码信息 + */ +export function getRememberInfo(): { phone: string, password: string, remember: boolean } { + return { + phone: userState.rememberedPhone, + password: userState.rememberedPassword, + remember: userState.rememberPassword + } +} + +/** + * 清除用户状态 + */ +export function clearUserInfo() { + userState.userInfo = null + userState.token = '' + userState.isLoggedIn = false + + // 清除本地存储 + uni.removeStorageSync('token') + uni.removeStorageSync('refreshToken') + uni.removeStorageSync('userInfo') + clearHomeStudentProfile() +} + +/** + * 退出登录 + */ +export function logout() { + clearUserInfo() + + // 跳转到登录页 + uni.reLaunch({ + url: '/pages/login/index' + }) +} + +/** + * 获取用户信息 + */ +export function getUserInfo(): UserInfo | null { + return userState.userInfo +} + +/** + * 获取Token + */ +export function getToken(): string { + return userState.token +} + +/** + * 获取登录状态 + */ +export function getIsLoggedIn(): boolean { + return userState.isLoggedIn +} + +/** + * 更新Token + */ +export function updateToken(token: string, refreshToken: string) { + userState.token = token + uni.setStorageSync('token', token) + uni.setStorageSync('refreshToken', refreshToken) + + if (userState.userInfo !== null) { + userState.userInfo.token = token + userState.userInfo.refreshToken = refreshToken + uni.setStorageSync('userInfo', userState.userInfo) + } +} + +// 导出用户状态(只读) +export function useUserStore() { + return { + userInfo: computed(() => userState.userInfo), + token: computed(() => userState.token), + isLoggedIn: computed(() => userState.isLoggedIn), + rememberPassword: computed(() => userState.rememberPassword), + rememberedPhone: computed(() => userState.rememberedPhone), + rememberedPassword: computed(() => userState.rememberedPassword) + } +} diff --git a/uni.scss b/uni.scss new file mode 100644 index 0000000..b9249e9 --- /dev/null +++ b/uni.scss @@ -0,0 +1,76 @@ +/** + * 这里是uni-app内置的常用样式变量 + * + * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 + * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App + * + */ + +/** + * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能 + * + * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件 + */ + +/* 颜色变量 */ + +/* 行为相关颜色 */ +$uni-color-primary: #007aff; +$uni-color-success: #4cd964; +$uni-color-warning: #f0ad4e; +$uni-color-error: #dd524d; + +/* 文字基本颜色 */ +$uni-text-color:#333;//基本色 +$uni-text-color-inverse:#fff;//反色 +$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息 +$uni-text-color-placeholder: #808080; +$uni-text-color-disable:#c0c0c0; + +/* 背景颜色 */ +$uni-bg-color:#ffffff; +$uni-bg-color-grey:#f8f8f8; +$uni-bg-color-hover:#f1f1f1;//点击状态颜色 +$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色 + +/* 边框颜色 */ +$uni-border-color:#c8c7cc; + +/* 尺寸变量 */ + +/* 文字尺寸 */ +$uni-font-size-sm:12px; +$uni-font-size-base:14px; +$uni-font-size-lg:16px; + +/* 图片尺寸 */ +$uni-img-size-sm:20px; +$uni-img-size-base:26px; +$uni-img-size-lg:40px; + +/* Border Radius */ +$uni-border-radius-sm: 2px; +$uni-border-radius-base: 3px; +$uni-border-radius-lg: 6px; +$uni-border-radius-circle: 50%; + +/* 水平间距 */ +$uni-spacing-row-sm: 5px; +$uni-spacing-row-base: 10px; +$uni-spacing-row-lg: 15px; + +/* 垂直间距 */ +$uni-spacing-col-sm: 4px; +$uni-spacing-col-base: 8px; +$uni-spacing-col-lg: 12px; + +/* 透明度 */ +$uni-opacity-disabled: 0.3; // 组件禁用态的透明度 + +/* 文章场景相关 */ +$uni-color-title: #2C405A; // 文章标题颜色 +$uni-font-size-title:20px; +$uni-color-subtitle: #555555; // 二级标题颜色 +$uni-font-size-subtitle:26px; +$uni-color-paragraph: #3F536E; // 文章段落颜色 +$uni-font-size-paragraph:15px; diff --git a/uni_modules/uni-badge-view/changelog.md b/uni_modules/uni-badge-view/changelog.md new file mode 100644 index 0000000..ed5163c --- /dev/null +++ b/uni_modules/uni-badge-view/changelog.md @@ -0,0 +1,6 @@ +## 1.0.2(2026-05-18) +- 修复类名拼写错误 +## 1.0.1(2026-04-30) +- 更新文档 +## 1.0.0(2026-04-30) +- 支持 uni-badge-view 组件 diff --git a/uni_modules/uni-badge-view/components/uni-badge-view/uni-badge-view.uvue b/uni_modules/uni-badge-view/components/uni-badge-view/uni-badge-view.uvue new file mode 100644 index 0000000..fd1bb6c --- /dev/null +++ b/uni_modules/uni-badge-view/components/uni-badge-view/uni-badge-view.uvue @@ -0,0 +1,287 @@ + + + + + \ No newline at end of file diff --git a/uni_modules/uni-badge-view/package.json b/uni_modules/uni-badge-view/package.json new file mode 100644 index 0000000..30b9a64 --- /dev/null +++ b/uni_modules/uni-badge-view/package.json @@ -0,0 +1,74 @@ +{ + "id": "uni-badge-view", + "displayName": "uni-badge-view 数字角标", + "version": "1.0.2", + "description": "数字角标(徽章)组件,在元素周围展示消息提醒,一般用于列表、九宫格、按钮等地方。", + "isExt": true, + "extUrl": "https://ext.dcloud.net.cn/plugin?id=27844", + "keywords": [ + "badge", + "uni-ui", + "uniui", + "数字角标", + "徽章" +], + "repository": "https://gitcode.com/dcloud/uni-ui-x", + "engines": { + "HBuilderX": "^5.08", + "uni-app-x": "^5.08", + "uni-app": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "type": "component-vue", + "darkmode": "√", + "i18n": "√", + "widescreen": "√", + "npmurl": "" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "client": { + "uni-app-x": { + "web": { + "safari": "√", + "chrome": "√" + }, + "app": { + "android": "√", + "ios": "√", + "harmony": "√" + }, + "mp": { + "weixin": "√" + } + } + }, + "cloud": { + "tcb": "x", + "aliyun": "x", + "alipay": "x" + } + } + } +} diff --git a/uni_modules/uni-badge-view/readme.md b/uni_modules/uni-badge-view/readme.md new file mode 100644 index 0000000..3922376 --- /dev/null +++ b/uni_modules/uni-badge-view/readme.md @@ -0,0 +1,34 @@ +badge角标,可以用于显示红点、被红圈或其他颜色圈包裹的数字、文字。提醒用户此时有需要注意的新信息。 + +### 文件路径 +项目下有一个 /pages/uni-ui/badge/badge.uvue 页面,里面演示了badge组件的各种用法。 + +### 设计 +#### badge组件有多种用法: +1. 独立显示 + 如果没有slot内容,视为要显示独立的红点和数字,此时不是角标,就是普通的红点和圆背景数字。 + 在按钮或列表项的右侧,独立显示数字。也可以显示红点,但这种场景数字更多一些。 + +2. 作为一个容器组件,包含一个slot,在包裹内容的右上角显示一个角标view。 + badge组件包裹一个目标view时,badge组件内部的角标view的中心点位于目标view的右上角顶点。 + 此时角标view是通过transform位移到右上角的,实际内容已经超出了容器本身。 + 所以需要注意容器的父级的设置,留出超出容器的尺寸(即top和right边距需要留出角标view的高度和宽度的一半),避免被父级裁剪。 + +#### badge的text属性的用法: +在badge作为容器组件时,text属性的不同设置,可以代表不同的效果。具体见属性章节。 + +#### 样式自定义 +badge组件的badge-class和badge-style属性,直接作用在角标view上,可以修改角标view的样式。 + +角标view本质是一个text组件,可以覆盖前景、背景、位置等各种样式。 + +#### badge组件有如下属性: +- text + 类型为 string|null + * 值为"0"时,不显示角标;所以永远不会显示一个为0的角标。此时直接显示slot子内容,不包裹外层,减少层级,提高性能; + * 值为null或""时,代表一个长宽为8px的红色圆点; + * 否则显示为该文字内容的圆角红底圈套文字。 +- badge-class + 直接作用于角标view的class +- badge-style + 直接作用于角标view的style diff --git a/uni_modules/uni-collapse-x/changelog.md b/uni_modules/uni-collapse-x/changelog.md new file mode 100644 index 0000000..c1ffe2d --- /dev/null +++ b/uni_modules/uni-collapse-x/changelog.md @@ -0,0 +1,4 @@ +## 1.0.1(2026-05-14) +- 支持 content-wrap-class 定义内层容器样式 +## 1.0.0(2026-05-07) +- 支持 uni-collapse 组件 diff --git a/uni_modules/uni-collapse-x/components/common.ts b/uni_modules/uni-collapse-x/components/common.ts new file mode 100644 index 0000000..deec9a1 --- /dev/null +++ b/uni_modules/uni-collapse-x/components/common.ts @@ -0,0 +1,3 @@ +export const UNI_COLLAPSE_REGISTER_CHILD = 'uni-collapse-register-child' +export const UNI_COLLAPSE_UNREGISTER_CHILD = 'uni-collapse-unregister-child' +export const UNI_COLLAPSE_CHILD_TOGGLE = 'uni-collapse-child-toggle' diff --git a/uni_modules/uni-collapse-x/components/type.ts b/uni_modules/uni-collapse-x/components/type.ts new file mode 100644 index 0000000..7e875c7 --- /dev/null +++ b/uni_modules/uni-collapse-x/components/type.ts @@ -0,0 +1,6 @@ +export type ItemChildType = { + openCollapse?: (show: boolean) => void; + openOrClose: (show: boolean) => void; + is_open: Ref; + elId: string; +}; diff --git a/uni_modules/uni-collapse-x/components/uni-collapse-item/uni-collapse-item.uvue b/uni_modules/uni-collapse-x/components/uni-collapse-item/uni-collapse-item.uvue new file mode 100644 index 0000000..cb008c5 --- /dev/null +++ b/uni_modules/uni-collapse-x/components/uni-collapse-item/uni-collapse-item.uvue @@ -0,0 +1,739 @@ + + + + + diff --git a/uni_modules/uni-collapse-x/components/uni-collapse/uni-collapse.uvue b/uni_modules/uni-collapse-x/components/uni-collapse/uni-collapse.uvue new file mode 100644 index 0000000..c437c8f --- /dev/null +++ b/uni_modules/uni-collapse-x/components/uni-collapse/uni-collapse.uvue @@ -0,0 +1,129 @@ + + + diff --git a/uni_modules/uni-collapse-x/package.json b/uni_modules/uni-collapse-x/package.json new file mode 100644 index 0000000..10f02f1 --- /dev/null +++ b/uni_modules/uni-collapse-x/package.json @@ -0,0 +1,74 @@ +{ + "id": "uni-collapse-x", + "displayName": "uni-collapse 折叠面板", + "version": "1.0.1", + "description": "Collapse 折叠面板组件,可自定义折叠面板的展开和收起。", + "isExt": true, + "extUrl": "https://ext.dcloud.net.cn/plugin?id=27899", + "keywords": [ + "uni-ui", + "uniui", + "collapse", + "折叠面板", + "uni-app-x" +], + "engines": { + "HBuilderX": "", + "uni-app": "", + "uni-app-x": "^5.07" + }, + "repository": "https://gitcode.com/dcloud/uni-ui-x", + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "type": "component-vue", + "darkmode": "x", + "i18n": "x", + "widescreen": "x", + "npmurl": "" + }, + "uni_modules": { + "encrypt": [], + "dependencies": [], + "platforms": { + "cloud": { + "tcb": "x", + "aliyun": "x", + "alipay": "x" + }, + "client": { + "uni-app-x": { + "web": { + "safari": "√", + "chrome": "√" + }, + "app": { + "android": "√", + "ios": "√", + "harmony": "√" + }, + "mp": { + "weixin": "√" + } + } + } + } + } +} diff --git a/uni_modules/uni-collapse-x/readme.md b/uni_modules/uni-collapse-x/readme.md new file mode 100644 index 0000000..a6702ed --- /dev/null +++ b/uni_modules/uni-collapse-x/readme.md @@ -0,0 +1,96 @@ +折叠面板组件,包含: + +- `uni-collapse`:父容器 +- `uni-collapse-item`:子面板 + +一个折叠面板组件可以包含若干uni-collapse-item。每个uni-collapse-item包括顶部标题栏和通过slot传入的内容区。 + +### 基本用法 + +```html + + + 内容 1 + + + 内容 2 + + +``` + +uni-collapse-item的标题栏的自定义: +- 标题栏容器可通过 `title-wrap-class` 自定义背景等容器样式。 +- 标题栏支持title属性,可通过title-class自定义文字样式。 +- 标题展开态可通过 `title-open-class` 自定义激活文字样式。组件默认使用透明度区分状态,便于外部继续覆盖文字颜色。 +- 标题禁用态可通过 `title-disabled-class` 自定义禁用文字样式。若同时存在展开态和禁用态,以禁用态为最终状态。 +- 内容外层容器可通过 `content-wrap-class` 自定义背景等容器样式。 +- 标题栏右边默认带有箭头,可通过arrow-class自定义箭头样式。该箭头为一个view包括2个直角边的边框,然后旋转而成。 +- 箭头展开态和禁用态可分别通过 `arrow-open-class`、`arrow-disabled-class` 覆盖。组件默认也使用透明度区分状态,便于外部继续覆盖箭头颜色。 +- 如果完全不想要默认包含的title和arrow,也可以传入一个名为title的具名插槽来替换。 + +### 具名插槽标题 + +```html + + + 自定义标题内容 + +``` + +### externalClass + +```html + + 内容区域 + +``` + +### Slots + +#### uni-collapse-item + +| 插槽名 | 说明 | 插槽参数 | +| --- | --- | --- | +| default | 面板内容 | - | +| title | 自定义标题区域 | `open: boolean`、`disabled: boolean` | + +### Events + +#### uni-collapse-item + +| 事件名 | 说明 | 回调参数 | +| --- | --- | --- | +| change | 面板展开状态变化时触发 | `open: boolean` | + +### Expose + +#### uni-collapse-item + +| 方法名 | 说明 | +| --- | --- | +| openCollapse(open: boolean) | 执行展开/收起,会遵循 `disabled` 和手风琴逻辑 | +| openOrClose(open: boolean) | 直接切换内容显示状态 | + +### 暗黑模式适配 + +建议通过组件 `class` 和 externalClass 组合传入暗色样式: + +- 用组件 `class` 统一控制根节点和内容区等外层视觉。 +- 用 `title-wrap-class`、`title-class`、`title-open-class`、`content-wrap-class`、`arrow-class`、`arrow-open-class` 等 externalClass 覆盖标题区、内容外层和箭头样式。 +- 默认展开态和禁用态主要通过透明度表达,减少颜色覆盖时的交叉冲突。 + +### 注意 +- 本组件在Android上需HBuilderX 5.09+。在低版本上箭头变颜色+旋转时,在Android上会消失 diff --git a/uni_modules/uni-combox-x/changelog.md b/uni_modules/uni-combox-x/changelog.md new file mode 100644 index 0000000..68ffac8 --- /dev/null +++ b/uni_modules/uni-combox-x/changelog.md @@ -0,0 +1,6 @@ +## 1.0.2 (2026-10-08) +- 修复安卓蒸汽模式会出现内容不显示的问题 +## 1.0.1(2026-08-28) +- 修复 web 运行报错的问题 +## 1.0.0(2026-08-28) +- 新增 uni-combox 组合框组件。 diff --git a/uni_modules/uni-combox-x/components/uni-combox/uni-combox.uvue b/uni_modules/uni-combox-x/components/uni-combox/uni-combox.uvue new file mode 100644 index 0000000..ab17343 --- /dev/null +++ b/uni_modules/uni-combox-x/components/uni-combox/uni-combox.uvue @@ -0,0 +1,660 @@ + + + + + diff --git a/uni_modules/uni-combox-x/package.json b/uni_modules/uni-combox-x/package.json new file mode 100644 index 0000000..68f7aab --- /dev/null +++ b/uni_modules/uni-combox-x/package.json @@ -0,0 +1,91 @@ +{ + "id": "uni-combox-x", + "displayName": "uni-combox 组合框", + "version": "1.0.2", + "description": "Combox 组合框组件,支持默认单选和通过 input 插槽启用可输可选模式。", + "isExt": true, + "extUrl": "https://ext.dcloud.net.cn/plugin?id=29441", + "keywords": [ + "组合框", + "combox", + "uni-app-x" +], + "repository": "https://gitcode.com/dcloud/uni-ui-x", + "engines": { + "HBuilderX": "", + "uni-app": "", + "uni-app-x": "^5.31" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "type": "component-vue", + "darkmode": "√", + "i18n": "x", + "widescreen": "√", + "npmurl": "", + "vapor": "√" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "x", + "aliyun": "x", + "alipay": "x" + }, + "client": { + "uni-app-x": { + "web": { + "safari": { + "extVersion": "1.0.2", + "minVersion": "" + }, + "chrome": { + "extVersion": "1.0.2", + "minVersion": "" + } + }, + "app": { + "android": { + "extVersion": "1.0.2", + "minVersion": "" + }, + "ios": { + "extVersion": "1.0.2", + "minVersion": "" + }, + "harmony": { + "extVersion": "1.0.2", + "minVersion": "" + } + }, + "mp": { + "weixin": { + "extVersion": "1.0.2", + "minVersion": "" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-combox-x/readme.md b/uni_modules/uni-combox-x/readme.md new file mode 100644 index 0000000..9e4b24b --- /dev/null +++ b/uni_modules/uni-combox-x/readme.md @@ -0,0 +1,122 @@ +uni-combox 组合框组件。 + +默认为单选模式,类似 select 组件,只能选择不能输入。通过具名插槽 `input` 传入 input 组件后,切换为可输入、可选择的组合模式。 + +### 基本用法 + +```html + +``` + +### options + +```html + +``` + +### modelValue / v-model + +```html + +``` + +### placeholder + +```html + +``` + +### 组合模式 + +```html + + + +``` + +输入时会打开候选浮层,并按 startsWith 规则过滤候选项。点击右侧箭头打开时展示完整候选列表,并高亮当前输入匹配项。 + +### 自定义样式 + +```html + +``` + +也可以单独传入任意一个样式类属性。 + +```html + + + + + +``` + +### 事件 + +```html + +``` + +### 浮层 + +候选浮层通过 `teleport` 渲染到页面层,不受组件自身尺寸裁剪。下方空间不足且上方空间更充足时,会自动向上展开。默认最大高度为 200px。 + +候选浮层内部使用 `scroll-view`。 + +### External Classes + +| 类名 | 说明 | +| --- | --- | +| arrow-class | 右侧箭头样式 | +| options-view-class | 候选浮层外层样式 | +| option-text-class | 候选项文字样式 | +| option-text-highlight-class | 候选项高亮样式 | +| result-text-class | 单选模式结果文字样式 | + +### Props + +| 属性名 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| options | Array\ | [] | 候选字符串数组 | +| modelValue | String | '' | 当前值,支持 v-model | +| placeholder | String | '' | 占位文字 | +| arrowClass | String | '' | 右侧箭头自定义样式类 | +| optionsViewClass | String | '' | 候选浮层外层自定义样式类 | +| optionTextClass | String | '' | 候选项文字自定义样式类 | +| optionTextHighlightClass | String | '' | 候选项高亮自定义样式类 | +| resultTextClass | String | '' | 单选模式结果文字自定义样式类 | + +### Events + +| 事件名 | 说明 | 回调参数 | +| --- | --- | --- | +| update:modelValue | 值更新时触发 | value: string | +| selectchange | 从候选项选中时触发 | value: string | + +### Expose + +| 方法名 | 说明 | +| --- | --- | +| setInputValue(value: string) | 设置当前值并打开过滤候选 | +| openOptions() | 打开候选浮层 | +| closeOptions() | 关闭候选浮层 | diff --git a/uni_modules/uni-drag-cell/changelog.md b/uni_modules/uni-drag-cell/changelog.md new file mode 100644 index 0000000..6ffb5ab --- /dev/null +++ b/uni_modules/uni-drag-cell/changelog.md @@ -0,0 +1,2 @@ +## 1.0.0(2026-04-30) +- 支持 uni-drag-cell 组件 diff --git a/uni_modules/uni-drag-cell/components/uni-drag-cell/uni-drag-cell.uvue b/uni_modules/uni-drag-cell/components/uni-drag-cell/uni-drag-cell.uvue new file mode 100644 index 0000000..2ca2940 --- /dev/null +++ b/uni_modules/uni-drag-cell/components/uni-drag-cell/uni-drag-cell.uvue @@ -0,0 +1,1263 @@ + + + + + + + + + + + + diff --git a/uni_modules/uni-drag-cell/package.json b/uni_modules/uni-drag-cell/package.json new file mode 100644 index 0000000..f15d1cd --- /dev/null +++ b/uni_modules/uni-drag-cell/package.json @@ -0,0 +1,73 @@ +{ + "id": "uni-drag-cell", + "displayName": "uni-drag-cell", + "version": "1.0.0", + "description": "可拖拽排序组件", + "isExt": true, + "extUrl": "https://ext.dcloud.net.cn/plugin?id=27845", + "keywords": [ + "uni-drag-cell", + "uni-app-x", + "uni-app x", + "组件" + ], + "repository": "https://gitcode.com/dcloud/uni-ui-x", + "engines": { + "HBuilderX": "^5.08", + "uni-app-x": "^5.08", + "uni-app": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "type": "component-vue", + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "darkmode": "x", + "i18n": "x", + "widescreen": "x", + "npmurl": "" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "x", + "aliyun": "x", + "alipay": "x" + }, + "client": { + "uni-app-x": { + "web": { + "safari": "√", + "chrome": "√" + }, + "app": { + "android": "√", + "ios": "√", + "harmony": "√" + }, + "mp": { + "weixin": "√" + } + } + } + } + } +} diff --git a/uni_modules/uni-drag-cell/readme.md b/uni_modules/uni-drag-cell/readme.md new file mode 100644 index 0000000..919faaa --- /dev/null +++ b/uni_modules/uni-drag-cell/readme.md @@ -0,0 +1,331 @@ +`uni-drag-cell` 是一个基于 `uni-app x + uvue + UTS` 实现的可拖拽排序组件。 + +它适合这些场景: +- 标签排序 +- 宫格排序 +- 单列列表排序 +- 带删除能力的拖拽 +- 尾部追加“+”入口 +- 手柄拖拽排序 +- 九宫格图片选择和排序 +- 支付方式排序 + +组件通过 v-model 绑定了一个数组,通过插槽渲染绑定数组的内容,通过拖拽调整数组项的顺序。 + +组件还提供了show-close属性,显示底部删除条,拖到删除条即可删除。 + +组件还提供了show-append属性,显示尾部的添加插槽。该插槽不参与拖拽排序,始终在最后一个。(添加数组项的逻辑需自行实现) + +通过这些能力,本组件具备对数组数据排序、添加、删除的能力。 + +但本组件不适合超长虚拟列表。不具备拖动到顶部或底部后自动滚动列表的功能。 + +### 设计目标 + +这个组件的设计不是“拖一下就立刻改数组”,而是分成两个阶段: + +1. **拖拽预览阶段** + - 拖动过程中,通过 `transform` 动态调整视觉顺序 + - 其他 item 会根据碰撞结果做位移动画 + - 此时外部 `v-model` 还不会被正式改写 + +2. **拖拽提交阶段** + - 松手后,才正式修改内部数组顺序 + - 然后通过 `update:modelValue` 和 `change` 把最终结果抛给外部 + +这样做的好处是: +- 拖动过程更平滑 +- 外部响应式更新次数更少 +- 更容易支持删除条、追加入口、手柄拖拽等扩展能力 + +### 交互模式 + +组件目前支持两种进入拖拽态的方式:长按拖拽和手柄拖拽。 + +#### 1. 长按拖拽 + +默认模式。 + +- 长按某个 item 后进入拖拽态 +- 当前长按触发时间为 `350ms` +- 进入拖拽态后,item 会跟随手指/鼠标移动,并且会scale放大1.12倍。 +- 拖动过程中持续做碰撞检测,并实时预览排序结果 + +补充: +- 如果按下后移动距离超过组件内部阈值,会取消本次长按判定,避免页面滚动时误触发拖拽 + +适合: +- 标签排序 +- 宫格排序 +- 图片排序 + +#### 2. 手柄拖拽 + +当 `handleMode` 为 `true`,且同时提供 `handle` 插槽时,会启用手柄模式。 + +- 不再需要长按 +- 只有拖动手柄才会开始排序 +- 内容区本身可以继续点击、滚动、预览 + +适合: +- 设置项排序 +- 表单项排序 +- 内容区本身还有点击行为的列表 + +### 插槽 + +| 插槽名 | 插槽参数 | 说明 | +| --- | --- | --- | +| `default` | `{ item, index }` | 默认内容插槽,用于渲染每一个可排序项。组件不限制 item 的视觉形式,可以自由渲染标签、卡片、宫格、图片等内容。 | +| `append` | - | 列表末尾的固定入口。不参与拖拽排序,不会写入 `v-model`,不会被删除,适合放“新增按钮”“上传入口”“更多入口”等内容。 | +| `handle` | `{ item, index }` | 拖拽手柄插槽,仅在 `handleMode` 开启时生效。手柄由组件放在每个 item 的右上角,建议给内容区预留右侧空间,避免手柄遮挡正文。 | + +### 事件 + +| 事件名 | 回调参数 | 触发时机 | 说明 | +| --- | --- | --- | --- | +| `change` | 最新数组 | 排序完成后、删除完成后 | 最终结果事件。拖动中的视觉预览不会频繁触发 `change`。 | + +### 使用方式 + +#### 1. 基础标签排序 + +```vue + + + +``` + +适合: +- 标签拖拽 +- 轻量横向流式布局 + +说明: +- 默认使用长按触发拖拽 +- 当前长按触发时间为 `350ms` + +#### 2. 宫格排序 + +```vue + + + +``` + +适合: +- 应用图标 +- 功能入口宫格 +- 九宫格类布局 + +#### 3. 带删除条的拖拽 + +```vue + + + +``` + +交互: +- 长按进入拖拽 +- 拖到屏幕底部删除条 +- 删除条高亮后松手即删除 + +#### 4. 尾部追加一个固定入口 + +```vue + + + + +``` + +说明: +- `append` 是通用设计,不局限于图片场景 +- 拖拽排序时,`append` 不参与位置计算 + +#### 5. 九宫格图片选择 + +```vue + + + + +``` + +设计说明: +- 图片数组本身就是 `v-model` +- `append` 用来放“添加图片”入口 +- 当达到上限时,把 `showAppend` 设为 `false` + +#### 6. 手柄拖拽排序 + +```vue + + + + +``` + +说明: +- 手柄模式下,不需要长按 +- 当前实现中,手柄模式不会再把整个 item 放大 +- 这样可以避免单列场景下被父容器裁剪 + +### 组件内部设计说明 + +#### 1. 排序预览不是直接改数组 + +拖动过程中,组件内部维护的是“预览顺序”。 + +实现方式: +- 记录每个 item 的稳定 id +- 根据碰撞结果生成一份预览顺序 +- 通过 `transform` 让其他项移动到目标位置 +- 松手后再正式更新数组和 id 顺序 + +这样可以避免: +- 拖动中频繁改外部数组 +- 频繁触发父组件重渲染 +- item 身份丢失导致动画错乱 + +#### 2. 删除判定基于拖动内容区域 + +删除命中不是简单按手指坐标判断,而是优先按“拖动内容的真实边界”和“删除条边界”是否接触来判断。 + +这样做是为了避免: +- item 外层有 padding 时提前触发删除 +- 不同高度 item 在删除区表现不一致 +- web 与 app 坐标系不一致时出现明显偏移 + +#### 3. 追加项与删除条互不干扰 + +`append` 项的设计目标是“固定入口”,因此: +- 不参与碰撞排序 +- 不参与删除 +- 不写入绑定数组 + +#### 4. 手柄模式与长按模式共用同一套拖拽链路 + +也就是说: +- 排序逻辑相同 +- 删除逻辑相同 +- 只是“进入拖拽态”的入口不同 + +这样可以减少两套逻辑分叉导致的维护成本。 + +### 性能注意 + +拖拽时耗费性能的点主要有两个: + +#### 1. 拖动中的碰撞检测 + +每次 move 都会更新: +- 当前拖动偏移 +- 删除条命中状态 +- 目标悬停位置 + +这是拖拽组件的必要开销,当前实现已经把重测布局的频率控制在较低水平,只在必要时重新测量。 + +#### 2. 删除条命中时的实时矩形读取 + +当启用 `showClose` 时,拖动中会优先读取当前拖动内容的实时矩形,以保证删除命中准确。 + +这部分是“精度优先”的设计,开销可接受,但不建议把它用于超大数量、超复杂 item 的高频拖拽场景。 + +### 使用注意事项 + +#### 1. 建议 item 尺寸稳定 + +尤其是宫格模式下,建议每个 item 的宽高尽量稳定。 + +否则可能带来: +- 拖动命中感不一致 +- 排序预览观感不稳定 + +#### 2. 手柄模式下给内容区预留右侧空间 + +因为手柄是覆盖在 item 右上角的。 + +建议: +- 内容区右侧留白 +- 或者把手柄区域设计成视觉上独立的一块 + +#### 3. `append` 只是入口,不是数据项 + +这点非常重要。 + +不要指望: +- `append` 参与排序 +- `append` 进入 `v-model` +- `append` 被删除 + +如果你需要“真正的数据项”,请直接写进 `modelValue`。 + +#### 4. 外部不要在拖拽过程中频繁整体替换列表 + +建议: +- 由组件在松手后统一回传结果 +- 父组件收到结果后再更新自己的数据源 + +不建议: +- 拖动过程中再由父组件持续重建整个数组 + +#### 5. 不建议用于超大列表或虚拟列表场景 + +这个组件当前更适合: +- 小中型标签列表 +- 宫格 +- 表单排序 +- 九宫格图片选择 + +如果是超长列表、虚拟滚动列表,应该使用专门为大数据量设计的方案。 + +### 平台说明 + +#### Web + +- 支持 pointer / touch 交互 + +#### Android + +- 支持触摸拖拽 diff --git a/uni_modules/uni-fab-button/changelog.md b/uni_modules/uni-fab-button/changelog.md new file mode 100644 index 0000000..8d5b5a6 --- /dev/null +++ b/uni_modules/uni-fab-button/changelog.md @@ -0,0 +1,2 @@ +## 1.0.0(2026-04-30) +- 支持 uni-fab-button 组件 diff --git a/uni_modules/uni-fab-button/components/uni-fab-button/uni-fab-button.uvue b/uni_modules/uni-fab-button/components/uni-fab-button/uni-fab-button.uvue new file mode 100644 index 0000000..ca27cda --- /dev/null +++ b/uni_modules/uni-fab-button/components/uni-fab-button/uni-fab-button.uvue @@ -0,0 +1,152 @@ + + + diff --git a/uni_modules/uni-fab-button/package.json b/uni_modules/uni-fab-button/package.json new file mode 100644 index 0000000..a4b1644 --- /dev/null +++ b/uni_modules/uni-fab-button/package.json @@ -0,0 +1,73 @@ +{ + "id": "uni-fab-button", + "displayName": "uni-fab-button", + "version": "1.0.0", + "description": "uni-fab-button", + "isExt": true, + "extUrl": "https://ext.dcloud.net.cn/plugin?id=27846", + "keywords": [ + "uni-fab-button", + "uni-app-x", + "ui", + "组件" + ], + "repository": "https://gitcode.com/dcloud/uni-ui-x", + "engines": { + "HBuilderX": "^5.07", + "uni-app-x": "^5.07", + "uni-app": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "type": "component-vue", + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "darkmode": "x", + "i18n": "x", + "widescreen": "x", + "npmurl": "" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "x", + "aliyun": "x", + "alipay": "x" + }, + "client": { + "uni-app-x": { + "web": { + "safari": "√", + "chrome": "√" + }, + "app": { + "android": "√", + "ios": "√", + "harmony": "√" + }, + "mp": { + "weixin": "√" + } + } + } + } + } +} diff --git a/uni_modules/uni-fab-button/readme.md b/uni_modules/uni-fab-button/readme.md new file mode 100644 index 0000000..e87bdee --- /dev/null +++ b/uni_modules/uni-fab-button/readme.md @@ -0,0 +1,14 @@ +悬浮加号按钮 + +本组件常用于页面中右下角悬浮的、里面有一个加号的圆形按钮。 + +组件的根view默认是一个圆形,蓝色背景(#007AFF),并带有阴影。使用组件时注意组件的父容器要留出阴影的空间,避免被裁剪。 + +组件的二级view是2个view交叉组成的加号。其中垂直的view是通过transform变换方向实现的。 + +组件的属性为 plus-class,它是一个externalClass,用于外部修改组件内部的、组成加号的2个view的样式。 + +组件中加号的默认样式为背景白色、粗细为2px、尺寸为40%。 + +### 注意 +- 鸿蒙平台按下fab-button不缩写的问题,需升级到HBuilderX 5.09+解决 diff --git a/uni_modules/uni-icons-x/changelog.md b/uni_modules/uni-icons-x/changelog.md new file mode 100644 index 0000000..5fe59ad --- /dev/null +++ b/uni_modules/uni-icons-x/changelog.md @@ -0,0 +1,4 @@ +## 1.0.0 (2026-10-16) +- 新增 uni-app x 和蒸汽模式支持 +- 新增组合式 API 实现 +- 调整 支持直接使用 `class` 或 `style` 自定义颜色、字号和字体 diff --git a/uni_modules/uni-icons-x/components/uni-icons/icons.uts b/uni_modules/uni-icons-x/components/uni-icons/icons.uts new file mode 100644 index 0000000..a864322 --- /dev/null +++ b/uni_modules/uni-icons-x/components/uni-icons/icons.uts @@ -0,0 +1,169 @@ +/** + * uni-icons 图标名称与 Unicode 字符映射表 + * @description 独立映射表,可在其它组件中直接 import 复用 + */ +export const uniIconsMap = { + 'arrow-down': '\ue6be', + 'arrow-left': '\ue6bc', + 'arrow-right': '\ue6bb', + 'arrow-up': '\ue6bd', + 'auth': '\ue6ab', + 'auth-filled': '\ue6cc', + 'back': '\ue6b9', + 'bars': '\ue627', + 'calendar': '\ue6a0', + 'calendar-filled': '\ue6c0', + 'camera': '\ue65a', + 'camera-filled': '\ue658', + 'cart': '\ue631', + 'cart-filled': '\ue6d0', + 'chat': '\ue65d', + 'chat-filled': '\ue659', + 'chatboxes': '\ue696', + 'chatboxes-filled': '\ue692', + 'chatbubble': '\ue697', + 'chatbubble-filled': '\ue694', + 'checkbox': '\ue62b', + 'checkbox-filled': '\ue62c', + 'checkmarkempty': '\ue65c', + 'circle': '\ue65b', + 'circle-filled': '\ue65e', + 'clear': '\ue66d', + 'close': '\ue673', + 'closeempty': '\ue66c', + 'cloud-download': '\ue647', + 'cloud-download-filled': '\ue646', + 'cloud-upload': '\ue645', + 'cloud-upload-filled': '\ue648', + 'color': '\ue6cf', + 'color-filled': '\ue6c9', + 'compose': '\ue67f', + 'contact': '\ue693', + 'contact-filled': '\ue695', + 'down': '\ue6b8', + 'bottom': '\ue6b8', + 'download': '\ue68d', + 'download-filled': '\ue681', + 'email': '\ue69e', + 'email-filled': '\ue69a', + 'eye': '\ue651', + 'eye-filled': '\ue66a', + 'eye-slash': '\ue6b3', + 'eye-slash-filled': '\ue6b4', + 'fire': '\ue6a1', + 'fire-filled': '\ue6c5', + 'flag': '\ue65f', + 'flag-filled': '\ue660', + 'folder-add': '\ue6a9', + 'folder-add-filled': '\ue6c8', + 'font': '\ue6a3', + 'forward': '\ue6ba', + 'gear': '\ue664', + 'gear-filled': '\ue661', + 'gift': '\ue6a4', + 'gift-filled': '\ue6c4', + 'hand-down': '\ue63d', + 'hand-down-filled': '\ue63c', + 'hand-up': '\ue63f', + 'hand-up-filled': '\ue63e', + 'headphones': '\ue630', + 'heart': '\ue639', + 'heart-filled': '\ue641', + 'help': '\ue679', + 'help-filled': '\ue674', + 'home': '\ue662', + 'home-filled': '\ue663', + 'image': '\ue670', + 'image-filled': '\ue678', + 'images': '\ue650', + 'images-filled': '\ue64b', + 'info': '\ue669', + 'info-filled': '\ue649', + 'left': '\ue6b7', + 'link': '\ue6a5', + 'list': '\ue644', + 'location': '\ue6ae', + 'location-filled': '\ue6af', + 'locked': '\ue66b', + 'locked-filled': '\ue668', + 'loop': '\ue633', + 'mail-open': '\ue643', + 'mail-open-filled': '\ue63a', + 'map': '\ue667', + 'map-filled': '\ue666', + 'map-pin': '\ue6ad', + 'map-pin-ellipse': '\ue6ac', + 'medal': '\ue6a2', + 'medal-filled': '\ue6c3', + 'mic': '\ue671', + 'mic-filled': '\ue677', + 'micoff': '\ue67e', + 'micoff-filled': '\ue6b0', + 'minus': '\ue66f', + 'minus-filled': '\ue67d', + 'more': '\ue64d', + 'more-filled': '\ue64e', + 'navigate': '\ue66e', + 'navigate-filled': '\ue67a', + 'notification': '\ue6a6', + 'notification-filled': '\ue6c1', + 'paperclip': '\ue652', + 'paperplane': '\ue672', + 'paperplane-filled': '\ue675', + 'person': '\ue699', + 'person-filled': '\ue69d', + 'personadd': '\ue69f', + 'personadd-filled': '\ue698', + 'personadd-filled-copy': '\ue6d1', + 'phone': '\ue69c', + 'phone-filled': '\ue69b', + 'plus': '\ue676', + 'plus-filled': '\ue6c7', + 'plusempty': '\ue67b', + 'pulldown': '\ue632', + 'pyq': '\ue682', + 'qq': '\ue680', + 'redo': '\ue64a', + 'redo-filled': '\ue655', + 'refresh': '\ue657', + 'refresh-filled': '\ue656', + 'refreshempty': '\ue6bf', + 'reload': '\ue6b2', + 'right': '\ue6b5', + 'scan': '\ue62a', + 'search': '\ue654', + 'settings': '\ue653', + 'settings-filled': '\ue6ce', + 'shop': '\ue62f', + 'shop-filled': '\ue6cd', + 'smallcircle': '\ue67c', + 'smallcircle-filled': '\ue665', + 'sound': '\ue684', + 'sound-filled': '\ue686', + 'spinner-cycle': '\ue68a', + 'staff': '\ue6a7', + 'staff-filled': '\ue6cb', + 'star': '\ue688', + 'star-filled': '\ue68f', + 'starhalf': '\ue683', + 'trash': '\ue687', + 'trash-filled': '\ue685', + 'tune': '\ue6aa', + 'tune-filled': '\ue6ca', + 'undo': '\ue64f', + 'undo-filled': '\ue64c', + 'up': '\ue6b6', + 'top': '\ue6b6', + 'upload': '\ue690', + 'upload-filled': '\ue68e', + 'videocam': '\ue68c', + 'videocam-filled': '\ue689', + 'vip': '\ue6a8', + 'vip-filled': '\ue6c6', + 'wallet': '\ue6b1', + 'wallet-filled': '\ue6c2', + 'weibo': '\ue68b', + 'weixin': '\ue691', +} + +export type IconsType = keyof typeof uniIconsMap diff --git a/uni_modules/uni-icons-x/components/uni-icons/uni-icons.uvue b/uni_modules/uni-icons-x/components/uni-icons/uni-icons.uvue new file mode 100644 index 0000000..aa09bf8 --- /dev/null +++ b/uni_modules/uni-icons-x/components/uni-icons/uni-icons.uvue @@ -0,0 +1,66 @@ + + + + + diff --git a/uni_modules/uni-icons-x/components/uni-icons/uniicons.ttf b/uni_modules/uni-icons-x/components/uni-icons/uniicons.ttf new file mode 100644 index 0000000000000000000000000000000000000000..14696d038d828073edac09ea4e5ba1dec2f58115 GIT binary patch literal 35824 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 F{2S@rCxHL} literal 0 HcmV?d00001 diff --git a/uni_modules/uni-icons-x/package.json b/uni_modules/uni-icons-x/package.json new file mode 100644 index 0000000..214461a --- /dev/null +++ b/uni_modules/uni-icons-x/package.json @@ -0,0 +1,100 @@ +{ + "id": "uni-icons-x", + "displayName": "uni-icons 图标", + "version": "1.0.0", + "description": "适用于 uni-app x 的字体图标组件,可自定义颜色、大小和字体。", + "keywords": [ + "uni-ui-x", + "uni-app-x", + "icon", + "图标" + ], + "repository": "https://github.com/dcloudio/uni-ui-x", + "engines": { + "HBuilderX": "^5.0", + "uni-app-x": "^5.0", + "uni-app": "" + }, + "dcloudext": { + "type": "component-vue", + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui-x", + "darkmode": "x", + "i18n": "x", + "widescreen": "x" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "x", + "aliyun": "x", + "alipay": "x" + }, + "client": { + "uni-app": { + "vue": { + "vue2": "x", + "vue3": "x" + }, + "web": { + "safari": "-", + "chrome": "-" + }, + "app": { + "vue": "x", + "nvue": "x", + "android": "-", + "ios": "-", + "harmony": "-" + }, + "mp": { + "weixin": "-", + "alipay": "-", + "toutiao": "-", + "baidu": "-", + "kuaishou": "-", + "jd": "-", + "harmony": "-", + "qq": "-", + "lark": "-" + }, + "quickapp": { + "huawei": "-", + "union": "-" + } + }, + "uni-app-x": { + "web": { + "safari": "√", + "chrome": "√" + }, + "app": { + "android": "√", + "ios": "√", + "harmony": "√" + }, + "mp": { + "weixin": "√" + } + } + } + } + } +} diff --git a/uni_modules/uni-icons-x/readme.md b/uni_modules/uni-icons-x/readme.md new file mode 100644 index 0000000..31a1b0f --- /dev/null +++ b/uni_modules/uni-icons-x/readme.md @@ -0,0 +1 @@ +### [查看文档](https://doc.dcloud.net.cn/uni-app-x/component/uni-ui-x/uni-icons.html) diff --git a/uni_modules/uni-index-bar/changelog.md b/uni_modules/uni-index-bar/changelog.md new file mode 100644 index 0000000..a6ae3d2 --- /dev/null +++ b/uni_modules/uni-index-bar/changelog.md @@ -0,0 +1,4 @@ +## 1.0.1(2026-04-30) +- 更新文档 +## 1.0.0(2026-04-30) +- 支持 uni-index-bar 组件 diff --git a/uni_modules/uni-index-bar/components/uni-index-bar/uni-index-bar.uvue b/uni_modules/uni-index-bar/components/uni-index-bar/uni-index-bar.uvue new file mode 100644 index 0000000..82328cf --- /dev/null +++ b/uni_modules/uni-index-bar/components/uni-index-bar/uni-index-bar.uvue @@ -0,0 +1,384 @@ + + + + + diff --git a/uni_modules/uni-index-bar/package.json b/uni_modules/uni-index-bar/package.json new file mode 100644 index 0000000..dff0f87 --- /dev/null +++ b/uni_modules/uni-index-bar/package.json @@ -0,0 +1,75 @@ +{ + "id": "uni-index-bar", + "displayName": "uni-index-bar 索引栏", + "version": "1.0.1", + "description": "uni-index-bar 索引栏组件,用于显示索引栏,支持自定义索引栏的样式和内容。", + "isExt": true, + "extUrl": "https://ext.dcloud.net.cn/plugin?id=27847", + "keywords": [ + "uni-index-bar", + "索引栏", + "index-bar", + "uni-app-x", + "组件" + ], + "repository": "https://gitcode.com/dcloud/uni-ui-x", + "engines": { + "HBuilderX": "^5.08", + "uni-app-x": "^5.08", + "uni-app": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "type": "component-vue", + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "darkmode": "x", + "i18n": "x", + "widescreen": "x", + "npmurl": "" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "x", + "aliyun": "x", + "alipay": "x" + }, + "client": { + "uni-app-x": { + "web": { + "safari": "√", + "chrome": "√" + }, + "app": { + "android": "√", + "ios": "√", + "harmony": "√" + }, + "mp": { + "weixin": "√", + "alipay": "√" + } + } + } + } + } +} diff --git a/uni_modules/uni-index-bar/readme.md b/uni_modules/uni-index-bar/readme.md new file mode 100644 index 0000000..eed8fe8 --- /dev/null +++ b/uni_modules/uni-index-bar/readme.md @@ -0,0 +1,98 @@ +### 介绍 + +本组件以竖排方式显示字母索引,常悬浮或固定于列表右侧,用户通过点击或滑动索引条上的字母索引,触发 `select` 事件。在该事件中通过代码驱动指定的列表滚动到该字母索引所在的位置。 + +本组件为绝对定位,默认垂直居中于父容器右侧。 + +本组件不包括 list-view 组件,需要配合 list-view 使用。list-view 中需要有对应索引的 list-item,且这些 list-item 需要有 id 属性,可以通过 `scroll-into-view` 属性或 `scrollTop` 属性跳转。 + +本组件包括一个指示器(indicator)子组件。当用户手指触摸在索引条上的某个字母时,在触摸的字母左边悬浮显示一个水滴形气泡,箭头指向当前触摸的字母。 + +### 基本用法 + +```html + + + + + + +``` + +```ts +const onSelect = (index: string) => { + // 处理索引选择,滚动列表到对应位置 +} +``` + +### 事件 + +| 事件名 | 说明 | 回调参数 | +|--------|------|----------| +| select | 选择索引时触发 | index: string - 当前选择的索引字符 | + +### 外部样式类 + +| 类名 | 说明 | +|------|------| +| indicator-view-class | 指示器容器样式,可自定义气泡背景色等 | +| indicator-text-class | 指示器文字样式,可自定义文字颜色等 | + +### 示例 + +#### 自定义索引 + +```html + +``` + +#### 自定义样式 + +```html + + +``` + +```css +.custom-indicator { + background-color: #007aff; +} +.custom-indicator-text { + color: #ffff00; +} +``` + +#### 配合 list-view 使用 + +```html + + + + + + +``` + +```ts +const indexList = cityGroups.map((g): string => g.index).join('\n') +const indexViewID = ref("") + +const onSelect = (index: string) => { + indexViewID.value = 'idx-' + index +} +``` diff --git a/uni_modules/uni-link-x/changelog.md b/uni_modules/uni-link-x/changelog.md new file mode 100644 index 0000000..8de647a --- /dev/null +++ b/uni_modules/uni-link-x/changelog.md @@ -0,0 +1,2 @@ +## 1.0.0(2026-05-07) +- 支持 uni-link 组件 diff --git a/uni_modules/uni-link-x/components/uni-link/uni-link.uvue b/uni_modules/uni-link-x/components/uni-link/uni-link.uvue new file mode 100644 index 0000000..a2d2e02 --- /dev/null +++ b/uni_modules/uni-link-x/components/uni-link/uni-link.uvue @@ -0,0 +1,362 @@ + + + + diff --git a/uni_modules/uni-link-x/package.json b/uni_modules/uni-link-x/package.json new file mode 100644 index 0000000..2fabe25 --- /dev/null +++ b/uni_modules/uni-link-x/package.json @@ -0,0 +1,76 @@ +{ + "id": "uni-link-x", + "displayName": "uni-link 链接", + "version": "1.0.0", + "description": "Link 链接组件,可自定义链接的打开方式。", + "isExt": true, + "extUrl": "https://ext.dcloud.net.cn/plugin?id=27898", + "keywords": [ + "uni-ui", + "uniui", + "link", + "链接", + "uni-app-x" +], + "engines": { + "HBuilderX": "", + "uni-app": "", + "uni-app-x": "^5.07" + }, + "repository": "https://gitcode.com/dcloud/uni-ui-x", + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "type": "component-vue", + "darkmode": "x", + "i18n": "x", + "widescreen": "x", + "npmurl": "" + }, + "uni_modules": { + "dependencies": [ + "uts-openSchema" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "√", + "aliyun": "√", + "alipay": "x" + }, + "client": { + "uni-app-x": { + "web": { + "safari": "√", + "chrome": "√" + }, + "app": { + "android": "√", + "ios": "√", + "harmony": "√" + }, + "mp": { + "weixin": "√" + } + } + } + } + } +} diff --git a/uni_modules/uni-link-x/pages/link-webview.uvue b/uni_modules/uni-link-x/pages/link-webview.uvue new file mode 100644 index 0000000..82c418c --- /dev/null +++ b/uni_modules/uni-link-x/pages/link-webview.uvue @@ -0,0 +1,28 @@ + + + diff --git a/uni_modules/uni-link-x/readme.md b/uni_modules/uni-link-x/readme.md new file mode 100644 index 0000000..e2f3d6e --- /dev/null +++ b/uni_modules/uni-link-x/readme.md @@ -0,0 +1,45 @@ +打开web链接组件。点击后通过内置或外部浏览器打开链接,加载 `href` 属性中配置的 URL。 + +- `target="_self"` 时,使用 `uni_modules/uni-link-x/pages/link-webview` 页面内的 `web-view` 打开链接。 + * 注意小程序需要将域名加入应用的域名白名单。 +- `target="_blank"` 时, + * APP平台调用手机浏览器打开链接 + * Web调用 `window.open(url, '_blank')` + * 小程序平台无法打开手机浏览器,此时会走到失败回退逻辑中 + + `target="_blank"`失败时,可以通过组件属性`blank-fallback`来控制失败后的回退处理策略,`blank-fallback`属性有3个值: + + 1. turn-self :使用_self方式打开。此值为默认值。也就是 `target="_blank"` 时,小程序下默认转到内置浏览器打开。 + 2. copy-link-modal :复制链接并弹框提醒。会复制URL到剪贴板中,并弹框提示用户在外部浏览器中打开。 + 3. none :不回退。会触发组件的error事件。 + +- 开发者也可以通过条件编译组合使用,比如APP平台使用`_black`,小程序平台使用`_self` + +常见失败原因: +- 安装本组件时未在项目的pages.json中注册组件的内置页面。(HBuilderX导入本组件到项目下时会弹框提醒注册内置页面) +- 小程序平台未将域名加入应用的域名白名单。 +- iOS运行基座未包括openSchema插件,仅在无xcode时运行会触发。打包其实不会发生此错误。 +- app平台,有些手机系统会弹框询问用户是否允许app打开浏览器,如果用户点了拒绝,这种情况没有失败回调。但有种判断方式是app的onHide生命周期是否触发,如果没有触发,那么意味着并没有启动系统浏览器。 + +### 基本用法 + +```html +uni-app x 文档 + +``` + +### Events + +| 事件名 | 说明 | 回调参数 | +| --- | --- | --- | +| error | 打开失败时触发 | `message: string` | + +### 依赖 + +`APP` 平台依赖 `uts-openSchema`。`uni-link` 已在 `uni_modules/uni-link-x/package.json` 中声明该依赖。 + +组件目录下有 `pages_init.json`,会向应用的 pages.json 中注册组件中自带的内置浏览器页面。如在HBuilderX中弹框询问是否将组件中的页面注册到项目pages.json中时,请选择同意。 + +### 示例页面 + +`/pages/uni-ui/link/link` diff --git a/uni_modules/uni-nav-bar-x/changelog.md b/uni_modules/uni-nav-bar-x/changelog.md new file mode 100644 index 0000000..8c6e0d5 --- /dev/null +++ b/uni_modules/uni-nav-bar-x/changelog.md @@ -0,0 +1,4 @@ +## 1.0.1(2026-05-20) +- 纠正类名拼写错误 +## 1.0.0(2026-05-07) +- 支持 uni-nav-bar 组件 diff --git a/uni_modules/uni-nav-bar-x/components/uni-nav-bar/uni-nav-bar.uvue b/uni_modules/uni-nav-bar-x/components/uni-nav-bar/uni-nav-bar.uvue new file mode 100644 index 0000000..6ed8459 --- /dev/null +++ b/uni_modules/uni-nav-bar-x/components/uni-nav-bar/uni-nav-bar.uvue @@ -0,0 +1,361 @@ + + + + + diff --git a/uni_modules/uni-nav-bar-x/package.json b/uni_modules/uni-nav-bar-x/package.json new file mode 100644 index 0000000..98946ee --- /dev/null +++ b/uni_modules/uni-nav-bar-x/package.json @@ -0,0 +1,73 @@ +{ + "id": "uni-nav-bar-x", + "displayName": "uni-nav-bar", + "version": "1.0.1", + "description": "uni-nav-bar", + "isExt": true, + "extUrl": "https://ext.dcloud.net.cn/plugin?id=27897", + "keywords": [ + "uni-nav-bar", + "uni-app-x", + "导航栏", + "组件" + ], + "repository": "https://gitcode.com/dcloud/uni-ui-x", + "engines": { + "HBuilderX": "^5.0", + "uni-app-x": "^5.07", + "uni-app": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "type": "component-vue", + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "darkmode": "x", + "i18n": "x", + "widescreen": "x", + "npmurl": "" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "x", + "aliyun": "x", + "alipay": "x" + }, + "client": { + "uni-app-x": { + "web": { + "safari": "√", + "chrome": "√" + }, + "app": { + "android": "√", + "ios": "√", + "harmony": "√" + }, + "mp": { + "weixin": "√" + } + } + } + } + } +} diff --git a/uni_modules/uni-nav-bar-x/readme.md b/uni_modules/uni-nav-bar-x/readme.md new file mode 100644 index 0000000..fdfd813 --- /dev/null +++ b/uni_modules/uni-nav-bar-x/readme.md @@ -0,0 +1,63 @@ +自定义导航栏组件 + +在页面pages.json中关闭原生导航栏后,即使用`"style": {"navigationStyle": "custom"}`,可以使用本组件实现自定义导航栏。 + +同时注意在pages.json中配置页面style增加属性`disableScroll:true`,即禁止页面滚动。此时只有uni-nav-bar组件的下方才可以滚动。否则页面的回弹bounce效果会把自定义导航栏也拉下来。 + +本组件自动适配顶部安全区。用padding-top让出顶部状态栏的高度。除去状态栏高度后,本组件的高度为44px。 + +本组件左右两边默认各让出了6px的边距。也可以在left-class和right-class中自定义边距。 + +本组件分为left、mid、right 3个区域。 + +- left区域默认显示一个返回箭头,大小为44*44px。可以通过属性hideDefaultBack来隐藏,也可以传入一个slot name='left'来替代。可以通过left-class来修饰样式。 +- mid区域默认显示title属性设置的标题。也可以传入一个slot name='mid'来替代。其默认宽度为屏幕宽度-两边默认边距-left区域默认宽度-right区域默认宽度。可以通过mid-class来修饰样式。 +- right区域默认不显示内容,可以传入一个slot name='right'来显示自定义内容。right区域默认width也是44px,可以通过right-class来修饰样式。 + +支持属性: +- hideDefaultBack: 隐藏返回箭头 +- title: 通过属性方便设置标题。如果传入mid slot,则不生效 +- navigationBarTextStyle: 返回箭头和属性设置的标题,它们的颜色均由该属性控制,可选 white|black + +本组件默认没有背景色,即透明,会透显页面的背景色,开发者可通过组件的class自行设置背景色。 + +如果遇到顶部安全区避让失效的问题,可升级到 HBuilderX 5.25 版本。 + +在小程序端,如果需要规避右上角胶囊按钮,可以参考下方代码设置 margin-right 让出胶囊按钮的宽度和右侧间距。 + +```html + + + + + +``` diff --git a/uni_modules/uni-number-box-x/changelog.md b/uni_modules/uni-number-box-x/changelog.md new file mode 100644 index 0000000..cd4f8b5 --- /dev/null +++ b/uni_modules/uni-number-box-x/changelog.md @@ -0,0 +1,4 @@ +## 1.0.1(2026-05-07) +- 移除无用依赖 +## 1.0.0(2026-05-07) +- 支持 uni-number-box 组件 diff --git a/uni_modules/uni-number-box-x/components/uni-number-box/uni-number-box.uvue b/uni_modules/uni-number-box-x/components/uni-number-box/uni-number-box.uvue new file mode 100644 index 0000000..c014637 --- /dev/null +++ b/uni_modules/uni-number-box-x/components/uni-number-box/uni-number-box.uvue @@ -0,0 +1,802 @@ + + + + + diff --git a/uni_modules/uni-number-box-x/package.json b/uni_modules/uni-number-box-x/package.json new file mode 100644 index 0000000..4246d39 --- /dev/null +++ b/uni_modules/uni-number-box-x/package.json @@ -0,0 +1,74 @@ +{ + "id": "uni-number-box-x", + "displayName": "uni-number-box 数字输入框", + "version": "1.0.1", + "description": "NumberBox 带加减按钮的数字输入框组件,用户可以控制每次点击增加的数值,支持小数。", + "isExt": true, + "extUrl": "https://ext.dcloud.net.cn/plugin?id=27896", + "keywords": [ + "uni-ui", + "uniui", + "数字输入框", + "uni-app-x" +], + "repository": "https://gitcode.com/dcloud/uni-ui-x", + "engines": { + "HBuilderX": "", + "uni-app": "", + "uni-app-x": "^5.07" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "type": "component-vue", + "darkmode": "x", + "i18n": "x", + "widescreen": "x", + "npmurl": "" + }, + "uni_modules": { + "dependencies": [ + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "x", + "aliyun": "x", + "alipay": "x" + }, + "client": { + "uni-app-x": { + "web": { + "safari": "√", + "chrome": "√" + }, + "app": { + "android": "√", + "ios": "√", + "harmony": "√" + }, + "mp": { + "weixin": "√" + } + } + } + } + } +} diff --git a/uni_modules/uni-number-box-x/readme.md b/uni_modules/uni-number-box-x/readme.md new file mode 100644 index 0000000..6665c76 --- /dev/null +++ b/uni_modules/uni-number-box-x/readme.md @@ -0,0 +1,58 @@ +带加减按钮的数字输入框,常见于购物车商品数量选择。 + +### 基本用法 + +```html + +``` + +### 设置范围 + +```html + +``` + +### 设置步长 + +```html + + + + + +``` + +### 禁用状态 + +```html + +``` + +### 自定义样式 + +通过 `btn-class`、`input-class`、`icon-class` 自定义组件样式: + +```html + +``` + +```css +.custom-btn { + background-color: #007aff; +} +.custom-input { + background-color: #e8f4ff; + color: #007aff; +} +.custom-icon { + background-color: #fff; +} +``` + +### Events + +| 事件名 | 说明 | 回调参数 | +| --- | --- | --- | +| change | 值改变时触发 | value: number | +| focus | 输入框聚焦时触发 | event: UniInputFocusEvent | +| blur | 输入框失焦时触发 | event: UniInputBlurEvent | diff --git a/uni_modules/uni-rate-x/changelog.md b/uni_modules/uni-rate-x/changelog.md new file mode 100644 index 0000000..ad08e68 --- /dev/null +++ b/uni_modules/uni-rate-x/changelog.md @@ -0,0 +1,4 @@ +## 1.0.1(2026-05-11) +- 修复 starClass 和 starActiveClass 设置不生效的问题 +## 1.0.0(2026-05-07) +- 支持 uni-rate 组件 diff --git a/uni_modules/uni-rate-x/components/uni-rate/uni-rate.uvue b/uni_modules/uni-rate-x/components/uni-rate/uni-rate.uvue new file mode 100644 index 0000000..a28b1fd --- /dev/null +++ b/uni_modules/uni-rate-x/components/uni-rate/uni-rate.uvue @@ -0,0 +1,624 @@ + + + + + diff --git a/uni_modules/uni-rate-x/package.json b/uni_modules/uni-rate-x/package.json new file mode 100644 index 0000000..7401650 --- /dev/null +++ b/uni_modules/uni-rate-x/package.json @@ -0,0 +1,73 @@ +{ + "id": "uni-rate-x", + "displayName": "uni-rate 评分", + "version": "1.0.1", + "description": "Rate 评分组件,可自定义评分星星图标的大小、间隔、评分数。", + "isExt": true, + "extUrl": "https://ext.dcloud.net.cn/plugin?id=27900", + "keywords": [ + "uni-ui", + "uniui", + "评分", + "uni-app-x" +], + "repository": "https://gitcode.com/dcloud/uni-ui-x", + "engines": { + "HBuilderX": "", + "uni-app": "", + "uni-app-x": "^5.07" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "type": "component-vue", + "darkmode": "x", + "i18n": "x", + "widescreen": "x", + "npmurl": "" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "x", + "aliyun": "x", + "alipay": "x" + }, + "client": { + "uni-app-x": { + "web": { + "safari": "√", + "chrome": "√" + }, + "app": { + "android": "√", + "ios": "√", + "harmony": "√" + }, + "mp": { + "weixin": "√" + } + } + } + } + } +} diff --git a/uni_modules/uni-rate-x/readme.md b/uni_modules/uni-rate-x/readme.md new file mode 100644 index 0000000..fa71ff0 --- /dev/null +++ b/uni_modules/uni-rate-x/readme.md @@ -0,0 +1,32 @@ +评分组件,多用于购买商品后,对商品进行评价等场景 + +### 文件路径 +项目下有一个 /pages/uni-ui/rate/rate.uvue 页面,里面演示了rate组件的各种用法。 + +### 设计 + +rate组件是投票评分使用的组件。它使用unicode字符实现,通过修改字体的样式,实现各种效果。 + +它分2个版本,一个版本是高性能版本,只有一层,不显示灰星,仅显示亮星,亮星不支持小数点,不能点击。适于长列表中显示。 +另一个版本是普通版本,由背景灰星和前景亮星叠加而成,支持小数点,支持点击。 +这2个版本通过属性full来切换。 + +本组件支持v-model绑定响应式数据。 + +rate组件有如下属性: +- full + 是否显示全部5颗星。当黄色不够5星时,是否填充灰星以达到5星的固定长度。不填充灰星性能更高 +- readonly + 是否响应点击变化。full属性为false时不生效,即无灰星时一定只读 +- plain + 灰星是否镂空 +- star-class + 星星的样式 +- star-style + 星星的样式 +- star-active-class + 选中的星星的样式 +- star-active-style + 选中的星星的样式 + +rate组件有onChange事件,事件回调中会给出点击触发的新评分值。 diff --git a/uni_modules/uni-refresh-box/changelog.md b/uni_modules/uni-refresh-box/changelog.md new file mode 100644 index 0000000..47c2dd2 --- /dev/null +++ b/uni_modules/uni-refresh-box/changelog.md @@ -0,0 +1,2 @@ +## 1.0.0(2026-04-30) +- 支持 uni-refresh-box 组件 diff --git a/uni_modules/uni-refresh-box/components/uni-refresh-box/uni-refresh-box.uvue b/uni_modules/uni-refresh-box/components/uni-refresh-box/uni-refresh-box.uvue new file mode 100644 index 0000000..cb3edd0 --- /dev/null +++ b/uni_modules/uni-refresh-box/components/uni-refresh-box/uni-refresh-box.uvue @@ -0,0 +1,526 @@ + + + + + diff --git a/uni_modules/uni-refresh-box/package.json b/uni_modules/uni-refresh-box/package.json new file mode 100644 index 0000000..bf547bf --- /dev/null +++ b/uni_modules/uni-refresh-box/package.json @@ -0,0 +1,73 @@ +{ + "id": "uni-refresh-box", + "displayName": "uni-refresh-box", + "version": "1.0.0", + "description": "uni-refresh-box", + "isExt": true, + "extUrl": "https://ext.dcloud.net.cn/plugin?id=27848", + "keywords": [ + "uni-refresh-box", + "uni-app-x", + "uni-app x", + "组件" + ], + "repository": "https://gitcode.com/dcloud/uni-ui-x", + "engines": { + "HBuilderX": "^5.07", + "uni-app-x": "^5.07", + "uni-app": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "type": "component-vue", + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "darkmode": "x", + "i18n": "x", + "widescreen": "x", + "npmurl": "" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "x", + "aliyun": "x", + "alipay": "x" + }, + "client": { + "uni-app-x": { + "web": { + "safari": "√", + "chrome": "√" + }, + "app": { + "android": "√", + "ios": "√", + "harmony": "√" + }, + "mp": { + "weixin": "√" + } + } + } + } + } +} diff --git a/uni_modules/uni-refresh-box/readme.md b/uni_modules/uni-refresh-box/readme.md new file mode 100644 index 0000000..a913370 --- /dev/null +++ b/uni_modules/uni-refresh-box/readme.md @@ -0,0 +1,80 @@ +自定义下拉刷新组件。 + +本组件封装了 scroll-view/list-view等滚动容器的下拉刷新功能,使用者只需在滚动容器中加入本组件,设置组件的属性和样式,即可实现自定义下拉刷新功能。 + +本组件自带一个全平台通用的下拉刷新样式,即左边一个 loading 圈,右边为下拉刷新相关的文字。也提供了各种自定义方法,包括文字、样式,甚至通过插槽传入完全不同的自定义下拉效果。 + +### 基本用法 + +```vue + + + +``` + +#### state 状态说明 + +| 值 | 说明 | +| :-- | :-- | +| 0 | 下拉中(未达到阈值) | +| 1 | 松手可刷新(已达到阈值) | +| 2 | 刷新中 | +| 3 | 刷新完成 | +| 4 | 归位中(不显示文字) | + +### Slots + +| 名称 | 说明 | 插槽参数 | +| :-- | :-- | :-- | +| loading | 自定义图标 | - | diff --git a/uni_modules/uni-tab-bar/changelog.md b/uni_modules/uni-tab-bar/changelog.md new file mode 100644 index 0000000..b2f5db9 --- /dev/null +++ b/uni_modules/uni-tab-bar/changelog.md @@ -0,0 +1,6 @@ +## 1.0.2(2026-07-16) +- 修复微信小程序滚动内容穿透底部 tab-bar +## 1.0.1(2026-07-07) +- 优化组件的宽度计算方式 +## 1.0.0(2026-04-30) +- 支持 uni-tab-bar 组件 diff --git a/uni_modules/uni-tab-bar/components/uni-tab-bar/uni-tab-bar.uvue b/uni_modules/uni-tab-bar/components/uni-tab-bar/uni-tab-bar.uvue new file mode 100644 index 0000000..4a3938b --- /dev/null +++ b/uni_modules/uni-tab-bar/components/uni-tab-bar/uni-tab-bar.uvue @@ -0,0 +1,139 @@ + + + + + diff --git a/uni_modules/uni-tab-bar/components/uni-tab-content/uni-tab-content.uvue b/uni_modules/uni-tab-bar/components/uni-tab-content/uni-tab-content.uvue new file mode 100644 index 0000000..cde6af7 --- /dev/null +++ b/uni_modules/uni-tab-bar/components/uni-tab-content/uni-tab-content.uvue @@ -0,0 +1,104 @@ + + + + + diff --git a/uni_modules/uni-tab-bar/components/uni-tab-item/uni-tab-item.uvue b/uni_modules/uni-tab-bar/components/uni-tab-item/uni-tab-item.uvue new file mode 100644 index 0000000..c6386e5 --- /dev/null +++ b/uni_modules/uni-tab-bar/components/uni-tab-item/uni-tab-item.uvue @@ -0,0 +1,187 @@ + + + + + diff --git a/uni_modules/uni-tab-bar/components/uni-tab-midbutton/uni-tab-midbutton.uvue b/uni_modules/uni-tab-bar/components/uni-tab-midbutton/uni-tab-midbutton.uvue new file mode 100644 index 0000000..5bbd3d8 --- /dev/null +++ b/uni_modules/uni-tab-bar/components/uni-tab-midbutton/uni-tab-midbutton.uvue @@ -0,0 +1,159 @@ + + + + + diff --git a/uni_modules/uni-tab-bar/components/uni-tab/uni-tab.uvue b/uni_modules/uni-tab-bar/components/uni-tab/uni-tab.uvue new file mode 100644 index 0000000..6d85fcf --- /dev/null +++ b/uni_modules/uni-tab-bar/components/uni-tab/uni-tab.uvue @@ -0,0 +1,222 @@ + + + + + diff --git a/uni_modules/uni-tab-bar/package.json b/uni_modules/uni-tab-bar/package.json new file mode 100644 index 0000000..5033ee7 --- /dev/null +++ b/uni_modules/uni-tab-bar/package.json @@ -0,0 +1,103 @@ +{ + "id": "uni-tab-bar", + "displayName": "uni-tab-bar 选项卡", + "version": "1.0.2", + "description": "底部选项卡组件集", + "isExt": true, + "extUrl": "https://ext.dcloud.net.cn/plugin?id=27849", + "examples": [ + { + "name": "tab-bar-dark", + "description": "暗色风格", + "url": "https://gitcode.com/dcloud/hello-uni-app-x/blob/prod_alpha/pages/uni-ui/tab-bar/tab-bar-dark.uvue" + }, + { + "name": "tab-bar-custom", + "description": "自定义样式", + "url": "https://gitcode.com/dcloud/hello-uni-app-x/blob/prod_alpha/pages/uni-ui/tab-bar/tab-bar-custom.uvue" + }, + { + "name": "tab-bar-blur", + "description": "半透明毛玻璃", + "url": "https://gitcode.com/dcloud/hello-uni-app-x/blob/prod_alpha/pages/uni-ui/tab-bar/tab-bar-blur.uvue" + }, + { + "id": "uni-tab-midbutton", + "name": "tab-bar-midbutton", + "description": "中间按钮", + "url": "https://gitcode.com/dcloud/hello-uni-app-x/blob/prod_alpha/pages/uni-ui/tab-bar/tab-bar-midbutton.uvue" + }, + { + "id": "uni-tab-midbutton", + "name": "tab-bar-midbutton-notch", + "description": "中间按钮镂空", + "url": "https://gitcode.com/dcloud/hello-uni-app-x/blob/prod_alpha/pages/uni-ui/tab-bar/tab-bar-midbutton-notch.uvue" + } + ], + "keywords": [ + "uni-ui", + "uniui", + "自定义选项卡", + "uni-app-x", + "tab-bar" +], + "repository": "https://gitcode.com/dcloud/uni-ui-x", + "engines": { + "HBuilderX": "^5.07", + "uni-app": "", + "uni-app-x": "^5.07" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "type": "component-vue", + "darkmode": "x", + "i18n": "x", + "widescreen": "x", + "npmurl": "" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "√", + "aliyun": "x", + "alipay": "x" + }, + "client": { + "uni-app-x": { + "web": { + "safari": "√", + "chrome": "√" + }, + "app": { + "android": "√", + "ios": "√", + "harmony": "√" + }, + "mp": { + "weixin": "√" + } + } + } + } + } +} diff --git a/uni_modules/uni-tab-bar/readme.md b/uni_modules/uni-tab-bar/readme.md new file mode 100644 index 0000000..72ff30e --- /dev/null +++ b/uni_modules/uni-tab-bar/readme.md @@ -0,0 +1,93 @@ +自定义tab-bar组件, 包括一组前端组件: + +- `uni-tab`:最外层容器,负责激活索引、内容区高度和切换事件 +- `uni-tab-bar`:底部 tab 容器,负责底栏布局、安全区和中间按钮占位 +- `uni-tab-item`:普通 tab 项,负责插槽展示和 badge +- `uni-tab-midbutton`:中间悬浮按钮占位容器 +- `uni-tab-content`:上方内容区域容器,负责内容注册、懒渲染和显示切换 + +每个选项卡内容,不再是页面,而变成了uni-tab-content组件的子组件。 + +自定义tab-bar组件的使用方式是纯前端的、也是跨平台的。这与pages.json中的tabBar完全不同: +1. pages.json中的tabBar相关的api无法应用于自定义tab-bar,比如uni.switchTab、uni.showTabBar、uni.hideTabBar、uni.showTabBarRedDot、uni.hideTabBarRedDot、uni.setTabBarBadge、uni.removeTabBarBadge、uni.setTabBarStyle、uni.setTabBarItem。 +2. pages.json中的tabBar页面,生命周期仍然是页面生命周期。但自定义tab-bar的uni-tab-content内容不触发页面生命周期,只触发组件生命周期。 + +当然,自定义tab-bar组件拥有更强的功能、更灵活的控制方式。比如下凹式midbutton。在uni-app x的非小程序平台中,性能并不会比pages.json中的tabBar差。 + + +### 基础用法 + +```vue + +``` + +底栏主体高度默认为 `50px`。如果需要自定义,请在 `uni-tab` 上设置 `--uni-tab-bar-height` CSS 变量: + +```vue + + ... + +``` + +### 组件 API + +#### uni-tab + +##### 事件 + +| 事件名 | 返回参数 | 说明 | +| --- | --- | --- | +| `change` | `index: number` | 点击普通 tab 项切换后触发,返回新的激活索引 | + +#### uni-tab-bar + +无专用属性和API,可直接通过 `class` / `style` 自定义底栏外观。 + +#### uni-tab-content + +无专用属性和API,可直接通过 `class` / `style` 自定义外观。 + +多个选项卡内容揉在一个uni-tab中比较乱,一般推荐uni-tab-content下放一个子组件,子组件作为独立uvue文件。 + +各个uni-tab-content的子组件,在显示和隐藏的没有生命周期。 +如需监听显示/隐藏需要通过provide/inject方式从外层传入响应式变量activeIndex,然后在子组件中watch这个activeIndex。 + +### 使用注意事项 + +1. `uni-tab` 所在的页面不应该滚动,应该在pages.json中配置页面style为`disableScroll:true`。 +2. 屏幕变化适配,Android需HBuilderX 5.09+支持。之前版本组件里无法监听页面的onResize。 +3. `uni-tab-content` 与 `uni-tab-item` 的数量必须一致,顺序也必须一一对应,否则切换后的内容会错位。 +4. `uni-tab` 默认占满页面的宽度和高度,即 `style="flex: 1"` +5. 自定义底栏高度时,请在 `uni-tab` 上设置带长度单位的 `--uni-tab-bar-height` CSS 变量,例如 `--uni-tab-bar-height: 40px`。底栏和需要显式计算高度的内容区会共用该变量。 +6. 如果底栏存在镂空、下凹或透明边缘,需要让底部内容透出来,请在 `uni-tab` 上开启 `tab-content-height-full`,此时内容区会延伸到 `uni-tab-bar` 下方。 +7. `uni-tab-content` 采用“首次激活再渲染”的策略;第一次切入前不会创建实例,切走后只隐藏不销毁,适合保留 tab 内部状态。 +8. `uni-tab-item` 只负责普通 tab 项注册与展示,不提供单独点击事件;请统一监听 `uni-tab` 的 `change`。 +9. 使用 `uni-tab-midbutton` 时,中间按钮左右两侧的普通 `uni-tab-item` 数量需要相等,也就是普通项总数必须为偶数,才能保持居中均分布局。 +10. `uni-tab-midbutton` 只负责预留中间按钮位置,不参与激活索引计算;中间按钮点击行为需要在插槽内部自行处理。 +11. `badge-text` 传空字符串时显示红点,传 `'0'` 时不显示;如果需要自定义 badge 外观,请使用 `badge-class` 覆盖样式。 +12. `uni-tab-bar`、`uni-tab-item`、`uni-tab-content` 的根节点都支持直接挂 `class` / `style`,推荐把视觉差异放在页面侧处理,不要改组件内部逻辑。 +13. 微信小程序提供了一种webview方式渲染的底部tabBar方式,来进行tabBar自定义。这种方式仅微信小程序支持,与本组件无关。 diff --git a/uni_modules/uni-tab-bar/todo.md b/uni_modules/uni-tab-bar/todo.md new file mode 100644 index 0000000..dedeb77 --- /dev/null +++ b/uni_modules/uni-tab-bar/todo.md @@ -0,0 +1,10 @@ +## 中间凸起 +非下沉的,中间凸起的midbutton页面,缺少一个平滑的过渡曲线 + +## 左右滑 +Android常见,微信 +避免和页面内的内容冲突 + +## tab-bar高斯模糊 +等view支持高斯模糊的css后,补一个示例页面即可 + diff --git a/uni_modules/uni-time-format/changelog.md b/uni_modules/uni-time-format/changelog.md new file mode 100644 index 0000000..9b36732 --- /dev/null +++ b/uni_modules/uni-time-format/changelog.md @@ -0,0 +1,2 @@ +## 1.0.0(2026-04-30) +- 支持 uni-time-format 组件 diff --git a/uni_modules/uni-time-format/components/uni-time-format/uni-time-format.uvue b/uni_modules/uni-time-format/components/uni-time-format/uni-time-format.uvue new file mode 100644 index 0000000..028dba3 --- /dev/null +++ b/uni_modules/uni-time-format/components/uni-time-format/uni-time-format.uvue @@ -0,0 +1,290 @@ + + + + + \ No newline at end of file diff --git a/uni_modules/uni-time-format/package.json b/uni_modules/uni-time-format/package.json new file mode 100644 index 0000000..cba8d9f --- /dev/null +++ b/uni_modules/uni-time-format/package.json @@ -0,0 +1,73 @@ +{ + "id": "uni-time-format", + "displayName": "uni-time-format 时间格式化", + "version": "1.0.0", + "description": "TimeFormat 时间格式化组件,可自定义时间格式化。", + "isExt": true, + "extUrl": "https://ext.dcloud.net.cn/plugin?id=27851", + "keywords": [ + "uni-ui", + "uniui", + "time-format", + "uni-app-x" + ], + "engines": { + "HBuilderX": "^5.07", + "uni-app-x": "^5.07", + "uni-app": "" + }, + "repository": "https://gitcode.com/dcloud/uni-ui-x", + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "type": "component-vue", + "darkmode": "x", + "i18n": "x", + "widescreen": "x", + "npmurl": "" + }, + "uni_modules": { + "encrypt": [], + "dependencies": [], + "platforms": { + "cloud": { + "tcb": "x", + "aliyun": "x", + "alipay": "x" + }, + "client": { + "uni-app-x": { + "web": { + "safari": "√", + "chrome": "√" + }, + "app": { + "android": "√", + "ios": "√", + "harmony": "√" + }, + "mp": { + "weixin": "√" + } + } + } + } + } +} diff --git a/uni_modules/uni-time-format/readme.md b/uni_modules/uni-time-format/readme.md new file mode 100644 index 0000000..639d7a1 --- /dev/null +++ b/uni_modules/uni-time-format/readme.md @@ -0,0 +1,40 @@ +时间格式化展示组件,支持绝对时间与相对时间。 + +### 基本用法 + +```html + +``` + +### relative 规则 + +`format=relative` 按自然日输出: +- 今天内: +`< 1 分钟` 输出“刚刚” +`< 60 分钟` 输出“n分钟前” +`>= 60 分钟` 输出“n小时前” +- 昨天内:输出“昨天 HH:mm” +- 超过昨天:输出“YYYY-MM-DD HH:mm” +- 未来时间:直接输出“YYYY-MM-DD HH:mm” + +### 相对时间国际化 + +根据 `uni.getAppBaseInfo().appLanguage` 自动匹配: +- 简体中文(`zh`) +- 繁体中文(`zh-hant` / `zh-tw` / `zh-hk` / `zh-mo`) +- 英文(默认) +- 法文(`fr`) +- 拉丁文(`la`) + +法文、拉丁文使用短文案,避免长文本占位。 + +### 非法时间 + +当 `timestamp` 非法时,组件返回空字符串。 + +### 示例页面 + +`/pages/uni-ui/time-format/time-format` +示例页面包含两组相对时间演示: +- 输入框动态演示:输入 `yyyy-mm-dd hh:mm:ss`,实时展示对应相对时间 +- 固定分支演示:展示“刚刚 / 分钟前 / 小时前 / 昨天 HH:mm / 超过昨天”五种分支,且不随输入变化 diff --git a/uni_modules/uni-ui-x/changelog.md b/uni_modules/uni-ui-x/changelog.md new file mode 100644 index 0000000..0d827d4 --- /dev/null +++ b/uni_modules/uni-ui-x/changelog.md @@ -0,0 +1,19 @@ +## 1.0.6 (2026-10-08) +- uni-combox 修复安卓蒸汽模式会出现内容不显示的问题 +## 1.0.5 (2026-09-09) +- uni-tab-bar 优化组件的宽度计算方式 +- uni-tab-bar 修复微信小程序滚动内容穿透底部 tab-bar +## 1.0.4(2026-05-26) +- uni-badge-view 修复类名拼写错误 +- uni-nav-bar 纠正类名拼写错误 +## 1.0.3(2026-05-15) +- uni-collapse 支持 content-wrap-class 定义内层容器样式 +## 1.0.2(2026-05-12) +- uni-badge-view 更新文档 +- uni-index-bar 更新文档 +- uni-number-box 移除无用依赖 +- uni-rate 修复 starClass 和 starActiveClass 设置不生效的问题 +## 1.0.1(2026-05-07) +- 新增 uni-collapse、uni-link、uni-nav-bar、uni-number-box、uni-rate 组件 +## 1.0.0(2026-04-30) +- 支持 uni-ui-x 组件库 diff --git a/uni_modules/uni-ui-x/components/uni-ui-x/uni-ui-x.uvue b/uni_modules/uni-ui-x/components/uni-ui-x/uni-ui-x.uvue new file mode 100644 index 0000000..e69de29 diff --git a/uni_modules/uni-ui-x/package.json b/uni_modules/uni-ui-x/package.json new file mode 100644 index 0000000..1c0b4b7 --- /dev/null +++ b/uni_modules/uni-ui-x/package.json @@ -0,0 +1,106 @@ +{ + "id": "uni-ui-x", + "displayName": "uni-ui-x", + "version": "1.0.6", + "description": "uni-ui-x是DCloud为uni-app x提供的组件库", + "isExt": true, + "keywords": [ + "uni-ui-x", + "UI组件库", + "ui框架", + "ui库", + "uni-app-x" +], + "engines": { + "HBuilderX": "", + "uni-app-x": "^5.08", + "uni-app": "" + }, + "repository": "https://gitcode.com/dcloud/uni-ui-x", + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "type": "component-vue", + "darkmode": "√", + "i18n": "x", + "widescreen": "√", + "npmurl": "", + "vapor": "√" + }, + "uni_modules": { + "dependencies": [ + "uni-badge-view", + "uni-collapse-x", + "uni-drag-cell", + "uni-fab-button", + "uni-index-bar", + "uni-link-x", + "uni-nav-bar-x", + "uni-number-box-x", + "uni-rate-x", + "uni-refresh-box", + "uni-tab-bar", + "uni-time-format", + "uni-combox-x" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "x", + "aliyun": "x", + "alipay": "x" + }, + "client": { + "uni-app-x": { + "web": { + "safari": { + "extVersion": "1.0.3", + "minVersion": "" + }, + "chrome": { + "extVersion": "1.0.3", + "minVersion": "" + } + }, + "app": { + "android": { + "extVersion": "1.0.3", + "minVersion": "" + }, + "ios": { + "extVersion": "1.0.3", + "minVersion": "" + }, + "harmony": { + "extVersion": "1.0.3", + "minVersion": "" + } + }, + "mp": { + "weixin": { + "extVersion": "1.0.3", + "minVersion": "" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-ui-x/readme.md b/uni_modules/uni-ui-x/readme.md new file mode 100644 index 0000000..62c5fdc --- /dev/null +++ b/uni_modules/uni-ui-x/readme.md @@ -0,0 +1,127 @@ +## 项目介绍 + +uni-ui x,是DCloud为 uni-app x 提供的扩展组件库。[开源库地址](https://gitcode.com/dcloud/uni-ui-x/tree/alpha) + +uni-ui x 是全新设计的,与为uni-app做的uni-ui不同。 + +uni-ui x 是伴随着HBuilderX 5.08发版的。5.07版本上有部分组件在部分平台有兼容性问题,详见组件详情的兼容性表格。再往前的HBuilderX版本未测试。 + +## 特点 + +- **性能优先** + +uni-ui x 非常注重性能,在DOM层级控制、包体积、代码执行时间方面精益求精。 + +它了解 uni-app x 蒸汽模式的最佳实践,尽可能利用模板和样式的预编译特性来提升性能。当然它也支持非蒸汽模式。 + +- **合理的抽象** + +一个组件的抽象能力非常重要,如何在自定义性、易用性、性能之间尽可能三全其美。 + +很多组件为了满足使用者的需求,封装的属性越来越多,但组件使用者仍然在抱怨自定义性不足。 + +尤其是组件把样式封装在组件属性上,造成用户需要自定义样式时无限封装属性,并且这些属性的控制无法使用css变量。 + +uni-ui x 提供了完全的样式自定义能力,所有的样式定义都可以通过class设置,组件属性只控制功能逻辑。 + +* 组件的根节点样式,可以在使用组件时的组件class或style上设置。 +* 组件的子节点样式,可以通过[externalClass](https://doc.dcloud.net.cn/uni-app-x/css/common/style-isolation.html#external-class)设置样式。子组件开放自己的样式出来,外部可通过externalClass进行子组件的样式自定义。 + +组件使用者完全可以通过css自己控制所有样式。无需修改组件源码。加上css变量加持,灵活度远高于其他组件库。 + +使用者可以通过css变量完全自定义自己的主题,而不会被组件属性上不能写css变量困扰。 + +- **为AI设计** + +uni-ui x 优先代码而不是二进制文件。目前没有引入字体文件。这出于性能考虑,也出于AI友好度考虑。 + +以箭头为例,uni-ui x 中使用一个view配置左下边框并transform旋转来实现,这种设计性能比字体更好,且AI可以方便的理解这段代码的UI表现,可以在代码层面调整样式。 + +如果是字体图片,AI很难理解、生成、修改。 + +再举一个例子,uni-tab下沉midbutton,涉及一个贝塞尔曲线,此时没有使用图片,而是使用svg,同样是为了方便AI理解和修改。 + +- **中性** + +uni-ui x的风格是中性的,和uni内置组件、小程序内置组件一样,重视是开发者可以自定义成各种风格,而不是为开发者提供某套风格。 + +开发者可以通过自定义css,形成自己的风格,或者交给AI完成类似工作。 + +uni-ui x 没有写死的颜色和文字: + +* 没有写死的文字,不影响国际化。 +* 没有写死的颜色,不影响自定义主题。 + + +### uni-ui x 已支持的组件列表 + +| 组件名 | 组件说明 | +| --- | --- | +| uni-badge-view | [数字角标](https://ext.dcloud.net.cn/plugin?name=uni-badge-view) | +| uni-collapse | [折叠面板](https://ext.dcloud.net.cn/plugin?name=uni-collapse-x) | +| uni-drag-cell | [可拖拽排序组件](https://ext.dcloud.net.cn/plugin?name=uni-drag-cell) | +| uni-fab-button | [uni-fab-button](https://ext.dcloud.net.cn/plugin?name=uni-fab-button) | +| uni-index-bar | [uni-index-bar](https://ext.dcloud.net.cn/plugin?name=uni-index-bar) | +| uni-link | [链接](https://ext.dcloud.net.cn/plugin?name=uni-link-x) | +| uni-nav-bar | [uni-nav-bar](https://ext.dcloud.net.cn/plugin?name=uni-nav-bar-x) | +| uni-number-box | [数字输入框](https://ext.dcloud.net.cn/plugin?name=uni-number-box-x) | +| uni-rate | [评分](https://ext.dcloud.net.cn/plugin?name=uni-rate-x) | +| uni-refresh-box | [uni-refresh-box](https://ext.dcloud.net.cn/plugin?name=uni-refresh-box) | +| uni-tab | [选项卡](https://ext.dcloud.net.cn/plugin?name=uni-tab-bar) | +| uni-time-format | [时间格式化](https://ext.dcloud.net.cn/plugin?name=uni-time-format) | + + +## uni-ui的升级建议@uniuiupgrade + +为uni-app提供的uni-ui,升级到uni-app x时无法直接对照升级,因为组件名称、用法都有变化。 + +但升级时,老版uni-ui组件在uni-app x下同等功能的新组件是什么,这里说明下功能映射关系: + +|uni-ui组件 |中文名称 |在uni-app x下的方案 | +|-- |-- |-- | +|uni-badge |数字角标 |改用uni-badge-view | +|uni-calendar |日历 |见hello uni-app x里的模板中的日历页面 | +|uni-card |卡片 |无 | +|uni-collapse |折叠面板 |改用uni-collapse | +|uni-combox |组合框 |无 | +|uni-countdown |倒计时 |无 | +|uni-data-checkbox |数据选择器 |无 | +|uni-data-picker |数据驱动的picker选择器 |组件本身支持uni-app x | +|uni-dateformat |日期格式化 |改用uni-time-format | +|uni-datetime-picker |日期选择器 |内置组件picker支持mode=date | +|uni-drawer |抽屉 |改用内置组件page-container | +|uni-easyinput |增强输入框 |无 | +|uni-fab |悬浮按钮 |改用uni-fab-button | +|uni-fav |收藏按钮 |无 | +|uni-file-picker |文件选择上传 |无 | +|uni-forms |表单 |改用内置组件form | +|uni-goods-nav |商品导航 |无 | +|uni-grid |宫格 |无 | +|uni-group |分组 |无 | +|uni-icons |图标 |无 | +|uni-indexed-list |索引列表 |改用uni-index-bar | +|uni-link |超链接 |改用uni-link | +|uni-list |列表 |使用内置组件list | +|uni-load-more |加载更多 |使用内置组件loading | +|uni-nav-bar |自定义导航栏 |改用uni-nav-bar | +|uni-notice-bar |通告栏 |无 | +|uni-number-box |数字输入框 |改用uni-number-box | +|uni-pagination |分页器 |无 | +|uni-popup |弹出层 |改用内置组件page-container | +|uni-rate |评分 |改用uni-rate | +|uni-row |布局-行 |无 | +|uni-search-bar |搜索栏 |无 | +|uni-segmented-control|分段器 |无 | +|uni-steps |步骤条 |无 | +|uni-swipe-action |滑动操作 |参考hello uni-app x模板中可左滑删除长列表示例 | +|uni-swiper-dot |轮播图指示点 |改用内置组件swiper,通过externalClass控制指示点样式 | +|uni-table |表格 |推荐使用rich-text或web-view中的table。 | +|uni-tag |标签 |无 | +|uni-title |章节标题 |无 | +|uni-transition |过渡动画 |无 | + +标记为`无`的,大多比较简单,对于AI而言直接写相关UI比下载学习一个组件更合适。 + +## 交流群 + +有关 uni-ui-x 组件的任何意见或建议,欢迎通过 [uni-ui-x im 交流群](https://ext.dcloud.net.cn/publisher/start-session?pluginId=27852) 沟通交流。 diff --git a/uni_modules/uts-openSchema/changelog.md b/uni_modules/uts-openSchema/changelog.md new file mode 100644 index 0000000..5a1a2cc --- /dev/null +++ b/uni_modules/uts-openSchema/changelog.md @@ -0,0 +1,2 @@ +## 1.0.0(2026-04-30) +- 支持 uts-openSchema API diff --git a/uni_modules/uts-openSchema/package.json b/uni_modules/uts-openSchema/package.json new file mode 100644 index 0000000..65008f3 --- /dev/null +++ b/uni_modules/uts-openSchema/package.json @@ -0,0 +1,75 @@ +{ + "id": "uts-openSchema", + "displayName": "uts-openSchema", + "version": "1.0.0", + "description": "在 Android、iOS、HarmonyOS 上打开链接的 UTS API", + "keywords": [ + "uts-openSchema" + ], + "repository": "https://gitcode.com/dcloud/uni-ui-x", + "engines": { + "HBuilderX": "^4.0", + "uni-app-x": "^4.75" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "type": "uts", + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "darkmode": "x", + "i18n": "x", + "widescreen": "x" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "√", + "aliyun": "√", + "alipay": "√" + }, + "client": { + "uni-app-x": { + "web": { + "safari": "x", + "chrome": "x" + }, + "app": { + "android": { + "extVersion": "1.0.0", + "minVersion": "21" + }, + "ios": { + "extVersion": "1.0.0", + "minVersion": "12" + }, + "harmony": { + "extVersion": "1.1.2", + "minVersion": "5.0.0" + } + }, + "mp": { + "weixin": "x" + } + } + } + } + } +} diff --git a/uni_modules/uts-openSchema/readme.md b/uni_modules/uts-openSchema/readme.md new file mode 100644 index 0000000..b31db45 --- /dev/null +++ b/uni_modules/uts-openSchema/readme.md @@ -0,0 +1,57 @@ +打开链接,支持: + +1. 打开外部 App +2. 使用浏览器打开链接 +3. 打开地图到指定地点 +4. ... + +## 使用 + +1. 安装此插件 +2. 在要使用的地方 `import` 导入 + ```ts + import { openSchema, canOpenURL } from '@/uni_modules/uts-openSchema' + ``` +3. 直接调用 `openSchema` 方法: + ```ts + // #ifdef UNI-APP-X + // 使用外部浏览器打开指定URL + openSchema('https://uniapp.dcloud.io/uni-app-x') + + // #ifdef APP-ANDROID + // Android 使用应用商店打开指定App + openSchema('market://details?id=com.tencent.mm') + + // Android 打开地图坐标 + // 可以先用canOpenURL判断是否安装了地图软件 + if (canOpenURL('androidamap://')) { + openSchema('androidamap://viewMap?sourceApplication=Hello%20uni-app&poiname=DCloud&lat=39.9631018208&lon=116.3406135236&dev=0') + } else { + console.log('未安装高德地图') + } + // #endif --> + + // #ifdef APP-IOS + // 打开 AppStore 到搜索页 + openSchema('itms-apps://search.itunes.apple.com//WebObjects//MZSearch.woa/wa/search?media=software<erm=') + + // 打开 iOS 地图坐标 + openSchema('http://maps.apple.com/?q=Mexican+Restaurant&sll=50.894967,4.341626&z=10&t=s') + // #endif --> + + // #endif --> + ``` + +### 参数 + +- openSchema(url: string) // `url`:要打开的链接 `必填` `不为空字符串` + +## 相关开发文档 + +[UTS 语法](https://uniapp.dcloud.net.cn/tutorial/syntax-uts.html) + +[UTS API插件](https://uniapp.dcloud.net.cn/plugin/uts-plugin.html) + +[UTS 组件插件](https://uniapp.dcloud.net.cn/plugin/uts-component.html) + +[Hello UTS](https://gitcode.net/dcloud/hello-uts) diff --git a/uni_modules/uts-openSchema/utssdk/app-android/config.json b/uni_modules/uts-openSchema/utssdk/app-android/config.json new file mode 100644 index 0000000..5defc8f --- /dev/null +++ b/uni_modules/uts-openSchema/utssdk/app-android/config.json @@ -0,0 +1,3 @@ +{ + "minSdkVersion": "21" +} diff --git a/uni_modules/uts-openSchema/utssdk/app-android/index.uts b/uni_modules/uts-openSchema/utssdk/app-android/index.uts new file mode 100644 index 0000000..ae2f7de --- /dev/null +++ b/uni_modules/uts-openSchema/utssdk/app-android/index.uts @@ -0,0 +1,27 @@ +import Intent from 'android.content.Intent' +import Uri from 'android.net.Uri' +import { OpenSchema, CanOpenURL } from '../interface.uts' + +export const openSchema : OpenSchema = function (url : string) { + if (canOpenURL(url)) { + const context = UTSAndroid.getUniActivity()! + const uri = Uri.parse(url) + const intent = new Intent(Intent.ACTION_VIEW, uri) + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + intent.setData(uri) + context.startActivity(intent) + } else { + console.error('[uts-openSchema] url param Error:', JSON.stringify(url)) + } +} + +export const canOpenURL : CanOpenURL = function (url : string) : boolean { + if (typeof url === 'string' && url.length > 0) { + const context = UTSAndroid.getUniActivity()! + const uri = Uri.parse(url) + const intent = new Intent(Intent.ACTION_VIEW, uri) + return intent.resolveActivity(context.packageManager) != null ? true : false + } else { + return false + } +} diff --git a/uni_modules/uts-openSchema/utssdk/app-harmony/index.uts b/uni_modules/uts-openSchema/utssdk/app-harmony/index.uts new file mode 100644 index 0000000..06eedc5 --- /dev/null +++ b/uni_modules/uts-openSchema/utssdk/app-harmony/index.uts @@ -0,0 +1,19 @@ +import { bundleManager, common } from '@kit.AbilityKit' +import OpenLinkOptions from '@ohos.app.ability.OpenLinkOptions' +import { getAbilityContext } from '@dcloudio/uni-runtime' +import { OpenSchema, CanOpenURL } from '../interface.uts' + +export const openSchema : OpenSchema = function (url : string) : void { + ;(getAbilityContext() as common.UIAbilityContext)?.openLink(url, { + appLinkingOnly: false + } as OpenLinkOptions) +} + +export const canOpenURL : CanOpenURL = function (url : string) : boolean { + try { + return bundleManager.canOpenLink(url) + } catch (error) { + console.error('[uts-openSchema] url param Error:', JSON.stringify(url)) + return false + } +} diff --git a/uni_modules/uts-openSchema/utssdk/app-ios/config.json b/uni_modules/uts-openSchema/utssdk/app-ios/config.json new file mode 100644 index 0000000..86b8f0b --- /dev/null +++ b/uni_modules/uts-openSchema/utssdk/app-ios/config.json @@ -0,0 +1,3 @@ +{ + "deploymentTarget": "12.0" +} diff --git a/uni_modules/uts-openSchema/utssdk/app-ios/index.uts b/uni_modules/uts-openSchema/utssdk/app-ios/index.uts new file mode 100644 index 0000000..9adaf48 --- /dev/null +++ b/uni_modules/uts-openSchema/utssdk/app-ios/index.uts @@ -0,0 +1,22 @@ +import { UIApplication } from 'UIKit' +import { URL } from 'Foundation' +import { OpenSchema, CanOpenURL } from '../interface.uts' + +export const openSchema : OpenSchema = function (url : string) : void { + if (canOpenURL(url)) { + let uri = new URL(string = url) + UIApplication.shared.open(uri!) + } else { + console.error('[uts-openSchema] url param Error: ', url) + } +} + +export const canOpenURL : CanOpenURL = function (url : string) : boolean { + if (typeof url == 'string' && url.length > 0) { + let uri = new URL(string = url) + if (uri != null && UIApplication.shared.canOpenURL(uri!)) { + return true + } + } + return false +} diff --git a/uni_modules/uts-openSchema/utssdk/interface.uts b/uni_modules/uts-openSchema/utssdk/interface.uts new file mode 100644 index 0000000..2b599ac --- /dev/null +++ b/uni_modules/uts-openSchema/utssdk/interface.uts @@ -0,0 +1,2 @@ +export type OpenSchema = (url : string) => void +export type CanOpenURL = (url : string) => boolean diff --git a/uni_modules/uts-openSchema/utssdk/web/index.uts b/uni_modules/uts-openSchema/utssdk/web/index.uts new file mode 100644 index 0000000..7410334 --- /dev/null +++ b/uni_modules/uts-openSchema/utssdk/web/index.uts @@ -0,0 +1,12 @@ +import { OpenSchema, CanOpenURL } from '../interface.uts' + +export const openSchema : OpenSchema = function (url : string) : void { + location.href = url +} + +export const canOpenURL : CanOpenURL = function (url : string) : boolean { + if (url != '') { + return true + } + return false +} diff --git a/utils/request.ts b/utils/request.ts new file mode 100644 index 0000000..82acfb4 --- /dev/null +++ b/utils/request.ts @@ -0,0 +1,471 @@ +/** + * 网络请求封装 + * 支持请求拦截、响应拦截、Loading显示、错误处理 + */ + +// 响应数据类型 +export type ApiResponse = { + code: number + message: string + data: T | null +} + +// 请求配置类型 +export type RequestConfig = { + url: string + method?: 'GET' | 'POST' | 'PUT' | 'DELETE' + data?: any + header?: any + showLoading?: boolean + loadingText?: string + showError?: boolean +} + +// 基础URL配置 - 虚拟接口 +const BASE_URL = 'https://mock-api.example.com' + +// 请求超时时间 +const TIMEOUT = 10000 + +// 联调前统一使用测试响应,保证所有页面流程可完整验证。 +const USE_MOCK_API = true + +// Mock 接口模拟正常网络环境,响应延迟范围为 0 到 2 秒。 +const MOCK_RESPONSE_MAX_DELAY = 2000 + +// Loading引用计数 +let loadingCount = 0 + +/** + * 显示Loading + */ +function showLoading(text: string = '加载中...') { + loadingCount++ + if (loadingCount === 1) { + uni.showLoading({ + title: text, + mask: true + }) + } +} + +/** + * 隐藏Loading + */ +function hideLoading() { + loadingCount-- + if (loadingCount <= 0) { + loadingCount = 0 + uni.hideLoading() + } +} + +/** + * 获取Token + */ +function getToken(): string { + return uni.getStorageSync('token') as string ?? '' +} + +/** + * 请求拦截器 - 添加token等 + */ +function requestInterceptor(config: RequestConfig): RequestConfig { + // 添加完整URL + if (!config.url.startsWith('http')) { + config.url = BASE_URL + config.url + } + + // 添加请求头 + const token = getToken() + config.header = config.header ?? {} + config.header['Content-Type'] = 'application/json' + + if (token) { + config.header['Authorization'] = `Bearer ${token}` + } + + // 显示Loading + if (config.showLoading !== false) { + showLoading(config.loadingText) + } + + return config +} + +/** + * 响应拦截器 - 统一处理响应 + */ +function responseInterceptor(response: UniNamespace.RequestSuccessCallbackResult, config: RequestConfig): ApiResponse | null { + // 隐藏Loading + if (config.showLoading !== false) { + hideLoading() + } + + // HTTP状态码检查 + if (response.statusCode !== 200) { + handleError(response.statusCode, '网络请求失败', config.showError) + return null + } + + // 解析响应数据 + const result = response.data as ApiResponse + + // 业务状态码检查 + if (result.code !== 0) { + handleError(result.code, result.message, config.showError) + return null + } + + return result +} + +/** + * 错误处理 + */ +function handleError(code: number, message: string, showError: boolean = true) { + console.error(`[Request Error] Code: ${code}, Message: ${message}`) + + if (!showError) { + return + } + + // 根据错误码显示不同提示 + let errorMsg = message + + if (code === 401) { + errorMsg = '登录已过期,请重新登录' + // 清除token并跳转登录页 + uni.removeStorageSync('token') + uni.removeStorageSync('userInfo') + uni.reLaunch({ + url: '/pages/login/index' + }) + } else if (code === 403) { + errorMsg = '没有权限访问' + } else if (code === 404) { + errorMsg = '请求的资源不存在' + } else if (code === 500) { + errorMsg = '服务器错误,请稍后重试' + } + + uni.showToast({ + title: errorMsg, + icon: 'none', + duration: 2000 + }) +} + +/** + * 根据接口生成页面流程所需的测试数据。 + */ +function createMockResponse(config: RequestConfig): ApiResponse { + const requestData = (config.data ?? {}) as Record + let data: any = null + + if ( + config.url.includes('/api/auth/login') || + config.url.includes('/api/auth/login-by-code') || + config.url.includes('/api/auth/register') + ) { + data = { + userId: 'test-parent-001', + phone: requestData.phone ?? '13800000000', + nickname: requestData.parentName ?? '测试家长', + avatar: '', + token: 'test-token', + refreshToken: 'test-refresh-token' + } + } else if (config.url.includes('/api/auth/send-code')) { + data = { expireTime: 300 } + } else if (config.url.includes('/api/auth/forgot-password/verify')) { + data = { token: 'test-reset-token' } + } else if (config.url.includes('/api/auth/refresh-token')) { + data = { token: 'test-token', refreshToken: 'test-refresh-token' } + } else if ( + config.url.includes('/api/student/verify-qrcode') || + config.url.includes('/api/student/verify-account') + ) { + const studentAccount = requestData.account ?? 'test-student' + data = { + id: `test-student-${studentAccount}`, + name: requestData.name ?? '测试学生', + account: studentAccount, + avatar: '', + grade: '三年级', + class: '一班' + } + } else if (config.url.includes('/api/parent/check-phone')) { + data = { exists: false } + } else if (config.url.includes('/api/parent/students')) { + data = { + currentStudentId: 'test-student-001', + students: [ + { id: 'test-student-001', name: '张一诺', account: '20241001', grade: '初二', school: '实验中学', avatar: '' }, + { id: 'test-student-002', name: '张一鸣', account: '20241002', grade: '五年级', school: '实验小学', avatar: '' }, + { id: 'test-student-003', name: '张一宁', account: '20241003', grade: '高二', school: '实验中学', avatar: '' } + ] + } + } else if (config.url.includes('/api/parent/home-metrics')) { + const isSecondStudent = config.url.includes('studentId=test-student-002') + data = isSecondStudent ? { + studentId: 'test-student-002', onlineToday: true, checkedInToday: false, learningMinutesToday: 78, + accuracyToday: 88, learningDaysThisWeek: 4, pendingHomeworkCount: 2, updatedAt: '2026-10-28T08:30:00+08:00' + } : { + studentId: 'test-student-001', onlineToday: true, checkedInToday: true, learningMinutesToday: 155, + accuracyToday: 92, learningDaysThisWeek: 5, pendingHomeworkCount: 1, updatedAt: '2026-10-28T08:30:00+08:00' + } + } else if (config.url.includes('/api/parent/today-learning')) { + const isSecondStudent = config.url.includes('studentId=test-student-002') + data = isSecondStudent ? { + studentId: 'test-student-002', studentName: '张一鸣', dateText: '10月13日 周一', checkedIn: false, + learningMinutes: 78, answerMinutes: 56, answerCount: 42, completedPackages: 3, totalPackages: 6, masteredPoints: 8, + score: { accuracy: 84, yesterdayAccuracy: 81, change: 3 }, pointsTotal: 28, + pointDetails: [ + { id: 'point-201', label: '每日登录', points: 10 }, + { id: 'point-202', label: '每日提交作业', points: 8 }, + { id: 'point-203', label: '连续打卡', points: 6 }, + { id: 'point-204', label: '攻克知识点', points: 4 } + ], + courses: [ + { id: 'course-201', time: '08:30', subject: '语文', content: '古诗词诵读与默写', checkedIn: true }, + { id: 'course-202', time: '10:00', subject: '数学', content: '分数应用题练习', checkedIn: true }, + { id: 'course-203', time: '15:30', subject: '英语', content: 'Unit 4 单词听写', checkedIn: false } + ] + } : { + studentId: 'test-student-001', studentName: '张一诺', dateText: '10月13日 周一', checkedIn: true, + learningMinutes: 155, answerMinutes: 118, answerCount: 68, completedPackages: 5, totalPackages: 6, masteredPoints: 12, + score: { accuracy: 89, yesterdayAccuracy: 85, change: 4 }, pointsTotal: 42, + pointDetails: [ + { id: 'point-101', label: '每日登录', points: 10 }, + { id: 'point-102', label: '每日提交作业', points: 12 }, + { id: 'point-103', label: '连续打卡', points: 8 }, + { id: 'point-104', label: '攻克知识点', points: 12 } + ], + courses: [ + { id: 'course-101', time: '08:00', subject: '语文', content: '《背影》精读与批注', checkedIn: true }, + { id: 'course-102', time: '10:20', subject: '数学', content: '一元二次方程专项训练', checkedIn: true }, + { id: 'course-103', time: '14:00', subject: '英语', content: 'Unit 5 单词听写与跟读', checkedIn: true }, + { id: 'course-104', time: '19:30', subject: '物理', content: '声现象章节测试', checkedIn: false } + ] + } + } else if (config.url.includes('/api/parent/vocabulary')) { + const isSecondStudent = config.url.includes('studentId=test-student-002') + data = isSecondStudent ? { + studentId: 'test-student-002', studentName: '张一鸣', studentGrade: '五年级', studentClass: '二班', bookCount: 4, + books: [ + { id: 'vocab-201', name: '人教版八年级上册', subtitle: '总词量 620 词', totalWords: 620, learnedWords: 420, status: 'learning', statusText: '在学', levelText: '初中同步', color: 'green' }, + { id: 'vocab-202', name: '中考核心词汇 1600', subtitle: '总词量 1600 词', totalWords: 1600, learnedWords: 280, status: 'unfinished', statusText: '未学完', levelText: '中考冲刺', color: 'yellow' }, + { id: 'vocab-203', name: '人教版三年级起点·五年级上', subtitle: '总词量 380 词', totalWords: 380, learnedWords: 380, status: 'completed', statusText: '已学完', levelText: '小学基础', color: 'blue' }, + { id: 'vocab-204', name: '高中必修 5 核心词汇', subtitle: '总词量 1080 词', totalWords: 1080, learnedWords: 0, status: 'not_started', statusText: '未开始', levelText: '高中同步', color: 'gray' } + ] + } : { + studentId: 'test-student-001', studentName: '张一诺', studentGrade: '初二', studentClass: '3班', bookCount: 4, + books: [ + { id: 'vocab-101', name: '人教版八年级上册', subtitle: '总词量 620 词', totalWords: 620, learnedWords: 520, status: 'learning', statusText: '在学', levelText: '初中同步', color: 'green' }, + { id: 'vocab-102', name: '中考核心词汇 1600', subtitle: '总词量 1600 词', totalWords: 1600, learnedWords: 860, status: 'unfinished', statusText: '未学完', levelText: '中考冲刺', color: 'yellow' }, + { id: 'vocab-103', name: '人教版三年级起点·五年级上', subtitle: '总词量 380 词', totalWords: 380, learnedWords: 380, status: 'completed', statusText: '已学完', levelText: '小学基础', color: 'blue' }, + { id: 'vocab-104', name: '高中必修 5 核心词汇', subtitle: '总词量 1080 词', totalWords: 1080, learnedWords: 0, status: 'not_started', statusText: '未开始', levelText: '高中同步', color: 'gray' } + ] + } + } else if (config.url.includes('/api/parent/learning-tasks')) { + const isSecondStudent = config.url.includes('studentId=test-student-002') + data = isSecondStudent ? { + studentId: 'test-student-002', studentName: '张一鸣', studentGrade: '五年级', studentClass: '二班', pendingCount: 2, completedCount: 3, failedCount: 0, + groups: [ + { id: 'task-group-201', subject: '生物', unit: '预习', lesson: '第 3 课节', startText: '今天 14:00', endText: '14:40 开课', knowledgeCount: 0, note: '学生尚未进入课程,暂无学习句与知识点,进入课程后将自动生成', tasks: [] }, + { id: 'task-group-202', subject: '物理', unit: '复习', lesson: '第 4 课节', startText: '今天 16:00', endText: '16:40 开课', knowledgeCount: 3, note: '', tasks: [{ id: 'task-201', title: '声音的特性', status: 'success', statusText: '攻克成功', accuracyText: '正确率 92%', answerText: '答题时长 6 分钟', deadlineText: '今天 16:35 完成' }, { id: 'task-202', title: '回声与测距', status: 'in_progress', statusText: '攻克中', accuracyText: '正确率 78%', answerText: '答题时长 12 分钟', deadlineText: '今晚 21:00 截止' }, { id: 'task-203', title: '噪声的防治', status: 'failed', statusText: '攻克失败', accuracyText: '', answerText: '', deadlineText: '' }] } + ] + } : { + studentId: 'test-student-001', studentName: '张一诺', studentGrade: '初二', studentClass: '3班', pendingCount: 3, completedCount: 5, failedCount: 1, + groups: [{ id: 'task-group-101', subject: '生物', unit: '预习', lesson: '第 3 课节', startText: '今天 14:00', endText: '14:40 开课', knowledgeCount: 0, note: '学生尚未进入课程,暂无学习句与知识点,进入课程后将自动生成', tasks: [] }, { id: 'task-group-102', subject: '物理', unit: '复习', lesson: '第 4 课节', startText: '今天 16:00', endText: '16:40 开课', knowledgeCount: 3, note: '', tasks: [{ id: 'task-101', title: '声音的特性', status: 'success', statusText: '攻克成功', accuracyText: '正确率 92%', answerText: '答题时长 6 分钟', deadlineText: '今天 16:35 完成' }, { id: 'task-102', title: '回声与测距', status: 'in_progress', statusText: '攻克中', accuracyText: '正确率 78%', answerText: '答题时长 12 分钟', deadlineText: '今晚 21:00 截止' }, { id: 'task-103', title: '噪声的防治', status: 'failed', statusText: '攻克失败', accuracyText: '', answerText: '', deadlineText: '' }] }] + } + } else if (config.url.includes('/api/parent/growth')) { + const isSecondStudent = config.url.includes('studentId=test-student-002') + data = isSecondStudent ? { + studentId: 'test-student-002', studentName: '张一鸣', studentGrade: '五年级', studentClass: '二班', pointsBalance: 862, level: 4, rank: 128, streakDays: 3, totalPoints: 24, + records: [ + { id: 'growth-201', title: '每日登录', timeText: '今天 07:35', points: 10, icon: 'calendar' }, + { id: 'growth-202', title: '每日提交作业', timeText: '今天 19:22', points: 8, icon: 'assignment' }, + { id: 'growth-203', title: '连续打卡', timeText: '今天 07:40', points: 6, icon: 'streak' }, + { id: 'growth-204', title: '正确率≥80%', timeText: '昨天 20:05', points: 10, icon: 'accuracy' }, + { id: 'growth-205', title: '攻克知识点', timeText: '昨天 19:40', points: 8, icon: 'mastery' }, + { id: 'growth-206', title: '上传笔记', timeText: '10月12日 18:20', points: 5, icon: 'note' } + ] + } : { + studentId: 'test-student-001', studentName: '张一诺', studentGrade: '初二', studentClass: '3班', pointsBalance: 1268, level: 7, rank: 86, streakDays: 3, totalPoints: 42, + records: [ + { id: 'growth-101', title: '每日登录', timeText: '今天 07:35', points: 10, icon: 'calendar' }, + { id: 'growth-102', title: '每日提交作业', timeText: '今天 19:22', points: 12, icon: 'assignment' }, + { id: 'growth-103', title: '连续打卡', timeText: '今天 07:40', points: 8, icon: 'streak' }, + { id: 'growth-104', title: '正确率≥80%', timeText: '昨天 20:05', points: 10, icon: 'accuracy' }, + { id: 'growth-105', title: '攻克知识点', timeText: '昨天 19:40', points: 12, icon: 'mastery' }, + { id: 'growth-106', title: '上传笔记', timeText: '10月12日 18:20', points: 5, icon: 'note' } + ] + } + } else if (config.url.includes('/api/parent/study-report')) { + const isSecondStudent = config.url.includes('studentId=test-student-002') + const subjects = [ + { id: 'all', name: '全部科目' }, { id: 'math', name: '数学' }, { id: 'chinese', name: '语文' }, + { id: 'english', name: '英语' }, { id: 'physics', name: '物理' } + ] + const studentOneWeekActivity = { summary: '周六学习最投入,周四效率最高。', activeDays: 6, totalDurationText: '14小时20分', points: [{ label: '周一', studyMinutes: 0, plannedMinutes: 120, newMasteredCount: 0, studyHeight: 0, plannedHeight: 49, newMasteredHeight: 0 }, { label: '周二', studyMinutes: 0, plannedMinutes: 95, newMasteredCount: 0, studyHeight: 0, plannedHeight: 39, newMasteredHeight: 0 }, { label: '周三', studyMinutes: 142, plannedMinutes: 120, newMasteredCount: 2, studyHeight: 58, plannedHeight: 49, newMasteredHeight: 34 }, { label: '周四', studyMinutes: 88, plannedMinutes: 88, newMasteredCount: 2, studyHeight: 36, plannedHeight: 36, newMasteredHeight: 34 }, { label: '周五', studyMinutes: 0, plannedMinutes: 148, newMasteredCount: 0, studyHeight: 0, plannedHeight: 61, newMasteredHeight: 0 }, { label: '周六', studyMinutes: 178, plannedMinutes: 166, newMasteredCount: 1, studyHeight: 74, plannedHeight: 68, newMasteredHeight: 17 }, { label: '周日', studyMinutes: 0, plannedMinutes: 152, newMasteredCount: 0, studyHeight: 0, plannedHeight: 62, newMasteredHeight: 0 }] } + const studentOneMonthActivity = { summary: '本月学习节奏稳定,周末自主学习投入更高。', activeDays: 24, totalDurationText: '52小时40分', points: [{ label: '第1周', studyMinutes: 610, plannedMinutes: 650, newMasteredCount: 3, studyHeight: 52, plannedHeight: 56, newMasteredHeight: 24 }, { label: '第2周', studyMinutes: 690, plannedMinutes: 660, newMasteredCount: 4, studyHeight: 60, plannedHeight: 57, newMasteredHeight: 33 }, { label: '第3周', studyMinutes: 720, plannedMinutes: 680, newMasteredCount: 4, studyHeight: 64, plannedHeight: 59, newMasteredHeight: 33 }, { label: '第4周', studyMinutes: 780, plannedMinutes: 700, newMasteredCount: 3, studyHeight: 72, plannedHeight: 61, newMasteredHeight: 24 }] } + const studentOneWeekMasteries = [{ subjectName: '数学', masteredCount: 38, totalCount: 40, masteredPercent: 95 }, { subjectName: '语文', masteredCount: 26, totalCount: 28, masteredPercent: 93 }, { subjectName: '英语', masteredCount: 18, totalCount: 33, masteredPercent: 55 }, { subjectName: '物理', masteredCount: 9, totalCount: 18, masteredPercent: 50 }] + const studentOneMonthMasteries = [{ subjectName: '数学', masteredCount: 112, totalCount: 120, masteredPercent: 93 }, { subjectName: '语文', masteredCount: 84, totalCount: 92, masteredPercent: 91 }, { subjectName: '英语', masteredCount: 68, totalCount: 96, masteredPercent: 71 }, { subjectName: '物理', masteredCount: 42, totalCount: 68, masteredPercent: 62 }] + const studentTwoWeekActivity = { summary: '本周学习节奏平稳,周末阅读投入较多。', activeDays: 5, totalDurationText: '11小时10分', points: [{ label: '周一', studyMinutes: 82, plannedMinutes: 100, newMasteredCount: 1, studyHeight: 34, plannedHeight: 42, newMasteredHeight: 17 }, { label: '周二', studyMinutes: 0, plannedMinutes: 92, newMasteredCount: 0, studyHeight: 0, plannedHeight: 38, newMasteredHeight: 0 }, { label: '周三', studyMinutes: 110, plannedMinutes: 105, newMasteredCount: 1, studyHeight: 46, plannedHeight: 43, newMasteredHeight: 17 }, { label: '周四', studyMinutes: 96, plannedMinutes: 100, newMasteredCount: 1, studyHeight: 40, plannedHeight: 42, newMasteredHeight: 17 }, { label: '周五', studyMinutes: 0, plannedMinutes: 110, newMasteredCount: 0, studyHeight: 0, plannedHeight: 46, newMasteredHeight: 0 }, { label: '周六', studyMinutes: 152, plannedMinutes: 128, newMasteredCount: 2, studyHeight: 63, plannedHeight: 53, newMasteredHeight: 34 }, { label: '周日', studyMinutes: 130, plannedMinutes: 126, newMasteredCount: 1, studyHeight: 54, plannedHeight: 52, newMasteredHeight: 17 }] } + const studentTwoMonthActivity = { summary: '本月完成大部分学习计划,建议保持阅读习惯。', activeDays: 21, totalDurationText: '45小时30分', points: [{ label: '第1周', studyMinutes: 520, plannedMinutes: 600, newMasteredCount: 2, studyHeight: 44, plannedHeight: 51, newMasteredHeight: 17 }, { label: '第2周', studyMinutes: 590, plannedMinutes: 610, newMasteredCount: 3, studyHeight: 50, plannedHeight: 52, newMasteredHeight: 24 }, { label: '第3周', studyMinutes: 640, plannedMinutes: 620, newMasteredCount: 2, studyHeight: 55, plannedHeight: 53, newMasteredHeight: 17 }, { label: '第4周', studyMinutes: 670, plannedMinutes: 640, newMasteredCount: 3, studyHeight: 58, plannedHeight: 55, newMasteredHeight: 24 }] } + const studentTwoWeekMasteries = [{ subjectName: '数学', masteredCount: 32, totalCount: 40, masteredPercent: 80 }, { subjectName: '语文', masteredCount: 24, totalCount: 28, masteredPercent: 86 }, { subjectName: '英语', masteredCount: 16, totalCount: 30, masteredPercent: 53 }, { subjectName: '科学', masteredCount: 12, totalCount: 22, masteredPercent: 55 }] + const studentTwoMonthMasteries = [{ subjectName: '数学', masteredCount: 92, totalCount: 120, masteredPercent: 77 }, { subjectName: '语文', masteredCount: 76, totalCount: 92, masteredPercent: 83 }, { subjectName: '英语', masteredCount: 58, totalCount: 90, masteredPercent: 64 }, { subjectName: '科学', masteredCount: 38, totalCount: 68, masteredPercent: 56 }] + data = isSecondStudent ? { + studentId: 'test-student-002', studentName: '张一鸣', studentGrade: '五年级', studentClass: '二班', updatedAtText: '10月13日 08:30', subjects, + week: { + rangeText: '10月7日 - 10月13日', statusTitle: '本周学习状态良好', statusDescription: '本周学习完成度稳定,建议保持每天的阅读与口算练习。', + newMasteredPoints: 2, consecutiveDays: 8, averageAccuracy: 86, scaleLabels: ['120个', '116个', '112个', '108个'], + trends: [ + { subjectId: 'all', points: [{ label: '周一', value: 109, average: 110, valueHeight: 24, averageHeight: 30 }, { label: '周二', value: 110, average: 111, valueHeight: 30, averageHeight: 35 }, { label: '周三', value: 111, average: 111, valueHeight: 36, averageHeight: 35 }, { label: '周四', value: 112, average: 112, valueHeight: 42, averageHeight: 42 }, { label: '周五', value: 112, average: 113, valueHeight: 42, averageHeight: 48 }, { label: '周六', value: 114, average: 113, valueHeight: 54, averageHeight: 48 }, { label: '周日', value: 115, average: 114, valueHeight: 60, averageHeight: 54 }] }, + { subjectId: 'math', points: [{ label: '周一', value: 108, average: 109, valueHeight: 18, averageHeight: 24 }, { label: '周二', value: 109, average: 110, valueHeight: 24, averageHeight: 30 }, { label: '周三', value: 110, average: 110, valueHeight: 30, averageHeight: 30 }, { label: '周四', value: 111, average: 111, valueHeight: 36, averageHeight: 36 }, { label: '周五', value: 112, average: 112, valueHeight: 42, averageHeight: 42 }, { label: '周六', value: 113, average: 112, valueHeight: 48, averageHeight: 42 }, { label: '周日', value: 114, average: 113, valueHeight: 54, averageHeight: 48 }] } + ], activity: studentTwoWeekActivity, subjectMasteries: studentTwoWeekMasteries + }, + month: { + rangeText: '9月14日 - 10月13日', statusTitle: '本月学习状态稳步提升', statusDescription: '本月坚持学习,多个学科的知识点掌握情况有所提升。', + newMasteredPoints: 9, consecutiveDays: 8, averageAccuracy: 87, scaleLabels: ['120个', '116个', '112个', '108个'], + trends: [{ subjectId: 'all', points: [{ label: '第1周', value: 107, average: 108, valueHeight: 14, averageHeight: 18 }, { label: '第2周', value: 110, average: 109, valueHeight: 30, averageHeight: 24 }, { label: '第3周', value: 113, average: 111, valueHeight: 46, averageHeight: 36 }, { label: '第4周', value: 116, average: 113, valueHeight: 62, averageHeight: 48 }] }], activity: studentTwoMonthActivity, subjectMasteries: studentTwoMonthMasteries + } + } : { + studentId: 'test-student-001', studentName: '张一诺', studentGrade: '初二', studentClass: '3班', updatedAtText: '10月13日 08:30', subjects, + week: { + rangeText: '10月7日 - 10月13日', statusTitle: '本周学习状态良好', statusDescription: '本周状态良好,数学与英语逐渐稳定,物理薄弱题型进步明显。', + newMasteredPoints: 3, consecutiveDays: 12, averageAccuracy: 90, scaleLabels: ['120个', '116个', '112个', '108个'], + trends: [ + { subjectId: 'all', points: [{ label: '周一', value: 112, average: 111, valueHeight: 32, averageHeight: 26 }, { label: '周二', value: 112, average: 113, valueHeight: 32, averageHeight: 38 }, { label: '周三', value: 113, average: 113, valueHeight: 40, averageHeight: 38 }, { label: '周四', value: 114, average: 114, valueHeight: 48, averageHeight: 48 }, { label: '周五', value: 114, average: 114, valueHeight: 48, averageHeight: 48 }, { label: '周六', value: 116, average: 114, valueHeight: 64, averageHeight: 48 }, { label: '周日', value: 116, average: 115, valueHeight: 64, averageHeight: 56 }] }, + { subjectId: 'math', points: [{ label: '周一', value: 110, average: 110, valueHeight: 20, averageHeight: 20 }, { label: '周二', value: 111, average: 111, valueHeight: 28, averageHeight: 28 }, { label: '周三', value: 113, average: 112, valueHeight: 42, averageHeight: 35 }, { label: '周四', value: 114, average: 112, valueHeight: 49, averageHeight: 35 }, { label: '周五', value: 115, average: 113, valueHeight: 56, averageHeight: 42 }, { label: '周六', value: 116, average: 113, valueHeight: 63, averageHeight: 42 }, { label: '周日', value: 117, average: 114, valueHeight: 70, averageHeight: 49 }] } + ], activity: studentOneWeekActivity, subjectMasteries: studentOneWeekMasteries + }, + month: { + rangeText: '9月14日 - 10月13日', statusTitle: '本月学习状态持续向好', statusDescription: '本月学习计划完成度高,学科掌握情况保持上升趋势。', + newMasteredPoints: 14, consecutiveDays: 12, averageAccuracy: 89, scaleLabels: ['120个', '116个', '112个', '108个'], + trends: [{ subjectId: 'all', points: [{ label: '第1周', value: 108, average: 109, valueHeight: 16, averageHeight: 23 }, { label: '第2周', value: 111, average: 111, valueHeight: 38, averageHeight: 38 }, { label: '第3周', value: 114, average: 112, valueHeight: 53, averageHeight: 45 }, { label: '第4周', value: 116, average: 114, valueHeight: 68, averageHeight: 53 }] }], activity: studentOneMonthActivity, subjectMasteries: studentOneMonthMasteries + } + } + } else if (config.url.includes('/api/parent/alerts')) { + const isSecondStudent = config.url.includes('studentId=test-student-002') + const alerts = isSecondStudent ? [ + { id: 'alert-201', level: 'medium', title: '作业未完成', content: '英语《单元词汇练习》还有 2 题未完成', occurredAt: '2026-10-28T09:20:00+08:00', isRead: false }, + { id: 'alert-202', level: 'low', title: '学习时长提醒', content: '今日学习时长低于本周日均时长', occurredAt: '2026-10-28T08:40:00+08:00', isRead: true } + ] : [ + { id: 'alert-101', level: 'medium', title: '作业未完成', content: '物理《声现象章节测试》今晚 20:30 截止,尚未提交', occurredAt: '2026-10-28T09:30:00+08:00', isRead: false }, + { id: 'alert-102', level: 'high', title: '正确率下降', content: '今日数学正确率 78%,低于近 7 日均值 12%', occurredAt: '2026-10-28T09:10:00+08:00', isRead: false }, + { id: 'alert-103', level: 'high', title: '攻读失败', content: '数学《几何证明》已用满 2 次攻读机会,仍未完成', occurredAt: '2026-10-28T08:50:00+08:00', isRead: true }, + { id: 'alert-104', level: 'low', title: '上线提醒', content: '今日首次上线时间较平日延后 30 分钟', occurredAt: '2026-10-28T08:10:00+08:00', isRead: true } + ] + const list = config.url.includes('limit=3') ? alerts.slice(0, 3) : alerts + data = { list, pagination: { page: 1, pageSize: list.length, total: alerts.length, hasMore: false } } + } else if (config.url.includes('/api/parent/home-profile')) { + const isSecondStudent = config.url.includes('studentId=test-student-002') + data = isSecondStudent ? { + id: 'test-student-002', name: '张一鸣', account: '20241002', grade: '五年级', school: '实验小学', avatar: '', comprehensiveLevel: 'good' + } : { + id: 'test-student-001', name: '张一诺', account: '20241001', grade: '初二', school: '实验中学', avatar: '', comprehensiveLevel: 'excellent' + } + } + + return { + code: 0, + message: 'success', + data: data as T + } +} + +/** + * 通用请求方法 + */ +export function request(config: RequestConfig): Promise | null> { + return new Promise((resolve, reject) => { + // 请求拦截 + const finalConfig = requestInterceptor(config) + + if (USE_MOCK_API) { + const delay = Math.floor(Math.random() * (MOCK_RESPONSE_MAX_DELAY + 1)) + setTimeout(() => { + if (finalConfig.showLoading !== false) { + hideLoading() + } + resolve(createMockResponse(finalConfig)) + }, delay) + return + } + + // 发起请求 + uni.request({ + url: finalConfig.url, + method: finalConfig.method ?? 'GET', + data: finalConfig.data, + header: finalConfig.header, + timeout: TIMEOUT, + success: (res) => { + // 响应拦截 + const result = responseInterceptor(res, finalConfig) + resolve(result) + }, + fail: (err) => { + // 隐藏Loading + if (finalConfig.showLoading !== false) { + hideLoading() + } + + // 网络错误处理 + handleError(-1, err.errMsg ?? '网络连接失败', finalConfig.showError) + reject(err) + } + }) + }) +} + +/** + * GET请求 + */ +export function get(url: string, data?: any, config?: Partial): Promise | null> { + return request({ + url, + method: 'GET', + data, + ...config + }) +} + +/** + * POST请求 + */ +export function post(url: string, data?: any, config?: Partial): Promise | null> { + return request({ + url, + method: 'POST', + data, + ...config + }) +} + +/** + * PUT请求 + */ +export function put(url: string, data?: any, config?: Partial): Promise | null> { + return request({ + url, + method: 'PUT', + data, + ...config + }) +} + +/** + * DELETE请求 + */ +export function del(url: string, data?: any, config?: Partial): Promise | null> { + return request({ + url, + method: 'DELETE', + data, + ...config + }) +}