diff --git a/AGENTS.md b/AGENTS.md
index dec33c0..2b49c4d 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -1,143 +1,212 @@
# AGENTS.md - Swimming uni-app Project
-## Build & Development Commands
+## Scope
+- This file applies to the entire repository rooted at `D:\Project\游泳\swimming`.
+- There are currently no deeper `AGENTS.md` files in subdirectories.
+- No `.cursor/rules/`, `.cursorrules`, or `.github/copilot-instructions.md` files exist in this repo.
-### Development Servers
+## Project Overview
+- This is a `uni-app` + Vue 3 + TypeScript project built with Vite.
+- Main app source lives under `src/`.
+- The app appears to target H5, app, and multiple mini-program platforms.
+- Primary UI library is `uview-plus`.
+- Charting-related code exists under `src/uni_modules/qiun-data-charts` and `src/uni_modules/lime-echart`.
+- Service-layer code is centralized under `src/Service/`.
+
+## Tooling Snapshot
+- Package manager: `npm`.
+- Bundler/dev server: `vite` via `@dcloudio/vite-plugin-uni`.
+- Type checking: `vue-tsc --noEmit`.
+- Language level: TypeScript `^4.9.4`.
+- Framework: Vue `^3.4.21`.
+- No ESLint config is present.
+- No Prettier config is present.
+- No unit or e2e test runner config is present.
+
+## Install
```bash
-npm run dev:h5 # Run H5 development server
-npm run dev:mp-weixin # Run WeChat mini-program development
-npm run dev:app # Run app development
-npm run dev:app-android # Run Android app development
-npm run dev:app-ios # Run iOS app development
-npm run dev:mp-alipay # Run Alipay mini-program
-npm run dev:mp-baidu # Run Baidu mini-program
-npm run dev:mp-qq # Run QQ mini-program
-npm run dev:mp-toutiao # Run Toutiao mini-program
+npm install
```
-### Build Commands
+## Build And Dev Commands
+
+### Common Development
```bash
-npm run build:h5 # Build for H5
-npm run build:mp-weixin # Build for WeChat mini-program
-npm run build:app # Build for app
-npm run build:app-android # Build for Android
-npm run build:app-ios # Build for iOS
+npm run dev:h5
+npm run dev:h5:ssr
+npm run dev:app
+npm run dev:app-android
+npm run dev:app-ios
+npm run dev:mp-weixin
+npm run dev:mp-alipay
+npm run dev:mp-baidu
+npm run dev:mp-jd
+npm run dev:mp-kuaishou
+npm run dev:mp-lark
+npm run dev:mp-qq
+npm run dev:mp-toutiao
+npm run dev:mp-xhs
+npm run dev:quickapp-webview
+npm run dev:quickapp-webview-huawei
+npm run dev:quickapp-webview-union
```
-### Type Checking
+### Common Build
```bash
-npm run type-check # Run TypeScript type checking (vue-tsc --noEmit)
+npm run build:h5
+npm run build:h5:ssr
+npm run build:app
+npm run build:app-android
+npm run build:app-ios
+npm run build:mp-weixin
+npm run build:mp-alipay
+npm run build:mp-baidu
+npm run build:mp-jd
+npm run build:mp-kuaishou
+npm run build:mp-lark
+npm run build:mp-qq
+npm run build:mp-toutiao
+npm run build:mp-xhs
+npm run build:quickapp-webview
+npm run build:quickapp-webview-huawei
+npm run build:quickapp-webview-union
```
-### Testing
-- **Note**: No test framework configured in this project yet.
-
-## Code Style Guidelines
-
-### Imports & Path Aliases
-- Use `@/` alias for imports from `src/` directory (configured in `tsconfig.json`)
-- Example: `import { Service } from '@/Service/Service'`
-- Import Vue composition API functions from 'vue'
-- Import uni-app lifecycle hooks from `@dcloudio/uni-app`
-
-### File Structure
+### Validation
+```bash
+npm run type-check
```
+
+## Lint And Test Status
+- There is no configured lint command in `package.json`.
+- There is no ESLint or Prettier configuration in the repository root.
+- There is no configured test framework such as Vitest, Jest, Playwright, Cypress, Mocha, or Ava.
+- There are no repository test files matching `*.spec.*` or `*.test.*` in app code.
+- Because no test runner exists, there is currently no supported command for running a single test.
+- For validation, prefer `npm run type-check` and, when relevant, a targeted platform build such as `npm run build:h5`.
+
+## Single-Test Guidance
+- Single-test execution is not available in the current repo state.
+- If a future test runner is added, update this file with:
+ - the root test command,
+ - the single-test command pattern,
+ - any platform-specific test setup,
+ - and the location/naming convention for test files.
+
+## Important Paths
+- `src/main.ts` bootstraps the app and registers `uview-plus`.
+- `src/App.vue` contains global app shell styles and setup.
+- `src/pages.json` defines pages and tab bar configuration.
+- `src/uni.scss` contains shared uni-app styling variables and theme-level styles.
+- `src/Service/` contains application services and shared app behavior.
+- `src/common/` contains shared domain models and utilities.
+- `src/components/` contains reusable Vue components.
+- `src/pages/` contains page-level Vue SFCs.
+- `src/uni_modules/` contains vendored or external uni-app modules.
+
+## Source Layout Conventions
+```text
src/
-├── pages/ # Page components (with sub-packages)
-├── components/ # Reusable Vue components
-├── Service/ # API services and utilities
-├── common/ # Common utilities and helpers
-├── static/ # Static assets (images, etc.)
-├── types/ # TypeScript type definitions
-├── uni_modules/ # Uni-app modules
-└── colorui/ # ColorUI CSS framework
+├── Service/ Service classes and base config
+├── common/ Shared utilities and domain models
+├── components/ Reusable Vue SFC components
+├── pages/ Routed uni-app pages
+├── static/ Static assets
+├── types/ Type declarations
+├── uni_modules/ Third-party or packaged uni modules
+├── colorui/ ColorUI-related assets/components
+├── App.vue Root application component
+├── main.ts App bootstrap
+├── pages.json Route and tab-bar registration
+└── uni.scss Global uni-app SCSS
```
-### Naming Conventions
-- **Files**: PascalCase for components (e.g., `ImageCropper.vue`), camelCase for utilities
-- **Components**: PascalCase for component names
-- **Variables**: camelCase for local variables and functions
-- **Constants**: UPPER_SNAKE_CASE for constants
-- **Classes**: PascalCase for class names (e.g., `Service`)
-- **CSS Classes**: kebab-case (e.g., `home-container`, `timer-card`)
+## Imports And Module Usage
+- Use the `@/` alias for imports rooted in `src/`.
+- `tsconfig.json` maps `@/*` to `./src/*`.
+- Prefer alias imports for app code instead of long relative paths.
+- Import Vue composition helpers from `vue`.
+- Import uni-app page lifecycle APIs from `@dcloudio/uni-app` when needed.
+- Keep imports grouped with framework imports first, then app services/types, then local modules.
+- Existing code sometimes re-exports shared utilities from `src/common/Common.ts`; preserve that pattern where it already exists.
-### TypeScript
-- Use `lang="ts"` in Vue SFC `
+## Service Layer Patterns
+- Base configuration lives in `src/Service/BaseConfig.ts`.
+- Shared service helpers live in `src/Service/Service.ts`.
+- Feature services under `src/Service/swimming/` usually:
+ - define endpoint path constants as `private static` fields,
+ - expose `static` methods,
+ - delegate network calls to `Service.Request()`,
+ - and export both the service class and `Service` where existing files already do so.
+- `Service.Request()` wraps request handling and normalizes API responses into `ResultData`.
+- `ResultData` lives in `src/common/Domain/ResultData.ts`.
-
-```
+## Error Handling And User Feedback
+- Use `Service.Msg()` for toast-style user feedback.
+- Use `Service.Alert()` or `Service.Confirm()` for modal interactions.
+- Route authenticated API requests through `Service.Request()`.
+- Be aware that `Service.Request()` already handles several auth and permission codes such as `401`, `40101`, `1004`, and `40188`.
+- When adding new service methods, return the request promise rather than swallowing errors silently.
+- Follow existing user-facing Chinese messaging style unless the surrounding screen uses a different language.
-### Formatting
-- **Indentation**: Use tabs (not spaces) for indentation
-- **Quotes**: Single quotes for strings in TypeScript/JavaScript
-- **Semicolons**: Optional but consistent (project uses semicolons)
-- **Line endings**: CRLF (Windows)
-- **SCSS**: Use nested selectors, kebab-case class names
+## Naming Conventions
+- Vue component filenames: PascalCase, for example `ImageCropper.vue`.
+- Utility and service helper filenames: usually camelCase or existing established names; match nearby files.
+- Service classes: PascalCase for class names, for example `PlanService`.
+- Some existing service class identifiers start lowercase, such as `studentService`; preserve existing public names unless a broader refactor is requested.
+- Local variables and functions: camelCase.
+- Constants: UPPER_SNAKE_CASE when they are true constants.
+- CSS class names: kebab-case.
+- Route paths must match entries in `src/pages.json`.
-### Error Handling
-- Use `Service.Msg()` for toast messages
-- Use `Service.Alert()` for modal dialogs
-- API requests through `Service.Request()` handle 401 (token expired) automatically
-- Return `ResultData` objects from service methods
-- Clean up intervals in `onUnmounted()` lifecycle hook
+## Formatting Expectations
+- Repository convention is tabs for indentation in source files.
+- Existing project code mostly uses single quotes in application TypeScript, though some files contain double quotes; prefer the dominant local style in the file you touch.
+- Semicolons are used frequently enough that new changes should stay consistent within the edited file.
+- Keep line endings as CRLF on Windows-oriented files when possible.
+- Use nested SCSS selectors where that is already the local pattern.
+- Avoid introducing broad formatting-only diffs.
-### UI Components
-- Use **uview-plus** as primary UI component library
-- Use **ColorUI** for CSS framework and icons
-- Component prefix: `u-` for uview-plus components (e.g., ``, ``)
+## Styling And UI
+- Prefer `uview-plus` components where the project already uses them.
+- ColorUI assets/components are also present; preserve established usage where relevant.
+- Use `rpx` units for responsive uni-app layouts.
+- Keep mobile-first layout assumptions.
+- Match the visual language already present on the page you are editing rather than redesigning unrelated UI.
-### Service Layer Pattern
-- Extend `BaseConfig` for service classes
-- Use static methods for utility functions
-- `Service` class provides:
- - API requests: `Service.Request()`
- - Navigation: `Service.GoPage()`, `Service.GoPageTab()`, `Service.GoPageBack()`
- - Storage: `Service.SetStorageCache()`, `Service.GetStorageCache()`
- - Messages: `Service.Msg()`, `Service.Alert()`
- - Loading: `Service.LoadIng()`, `Service.LoadClose()`
+## Platform And Runtime Notes
+- Use `uni.*` APIs for platform-specific behavior.
+- Navigation typically goes through `Service.GoPage()`, `Service.GoPageTab()`, `Service.GoPageBack()`, or `Service.GoPageDelse()`.
+- App state and auth token access are commonly wrapped by `Service.GetStorageCache()` and related helpers.
+- Be careful when changing files inside `src/uni_modules/`; many are third-party modules and should only be edited when necessary.
-### uni-app Specifics
-- Use `uni.` API for platform-specific operations
-- Pages registered in `src/pages.json`
-- Tab bar configured in `src/pages.json`
-- Use `rpx` units for responsive design
-- Global styles in `src/App.vue` and `src/uni.scss`
+## Agent Guidance
+- Before changing code, inspect nearby files and follow their existing conventions.
+- Keep edits focused and minimal; do not refactor unrelated areas opportunistically.
+- Do not invent lint or test commands that are not actually configured.
+- When describing validation, use real commands from `package.json`.
+- If you add a test framework or lint setup later, update this file immediately.
+- If you touch vendored `uni_modules`, call that out explicitly in your final summary.
-### Git & Commit
-- `.gitignore` includes `node_modules/`, `dist/`, `unpackage/`
-- Commit messages should be descriptive (in Chinese or English)
-
-### Key Dependencies
-- Vue 3.4.21
-- TypeScript 4.9.4
-- Vite 5.2.8
-- uni-app 3.0.0
-- uview-plus 3.3.54
-- dayjs (date manipulation)
-- echarts (charts)
-- vue-i18n (internationalization)
-
-### No Existing Rules
-- No `.cursor/rules/` or `.cursorrules` found
-- No `.github/copilot-instructions.md` found
-- No ESLint or Prettier configuration found
-- Follow existing code patterns when making changes
+## Current Gaps To Remember
+- No lint pipeline is configured.
+- No automated test pipeline is configured.
+- No single-test command exists yet.
+- No Cursor or Copilot repository instruction files exist.
+- Agents should rely on local code patterns plus this file when making changes.
diff --git a/package-lock.json b/package-lock.json
index 49b9869..87431db 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -26,7 +26,7 @@
"clipboard": "^2.0.11",
"dayjs": "^1.11.13",
"echarts": "^5.5.1",
- "uview-plus": "^3.3.54",
+ "uview-plus": "^3.7.32",
"vue": "^3.4.21",
"vue-i18n": "^9.1.9"
},
@@ -12682,9 +12682,9 @@
}
},
"node_modules/uview-plus": {
- "version": "3.7.13",
- "resolved": "https://registry.npmmirror.com/uview-plus/-/uview-plus-3.7.13.tgz",
- "integrity": "sha512-vHByf0kxKReYxam6BuU6wn/80giCkMaMUHEblhkf4kAjP852b86V3ctkjfGtV17MEIORFo3Vkve+HFnHNXpwNg==",
+ "version": "3.7.32",
+ "resolved": "https://registry.npmmirror.com/uview-plus/-/uview-plus-3.7.32.tgz",
+ "integrity": "sha512-sSUHHP0xgGkMoipRFB9XpiqTR/eaBn4WwF+rFQRcPLbr3CCey0hwH700HoqX6Uo6IsKensFIhv6Tw6Rs8XWv5w==",
"engines": {
"HBuilderX": "^3.1.0",
"uni-app": "^4.66",
diff --git a/src/Service/swimming/PlanService.ts b/src/Service/swimming/PlanService.ts
index 3130e9a..5dfac15 100644
--- a/src/Service/swimming/PlanService.ts
+++ b/src/Service/swimming/PlanService.ts
@@ -40,7 +40,84 @@ class PlanService {
return result;
}
-
+
+ private static UpdateGroupStudentPath : string = '/api/Plan/UpdateGroupStudent';
+ /*****修改分组学生学生接口*****/
+ static UpdateGroupStudent(data:any) {
+ var result = Service.Request(this.UpdateGroupStudentPath, "POST", data);
+ return result;
+ }
+
+
+ private static AddStudentToGroupPath : string = '/api/Plan/AddStudentToGroup';
+ /*****添加分组学生接口*****/
+ static AddStudentToGroup(data:any) {
+ var result = Service.Request(this.AddStudentToGroupPath, "POST", data);
+ return result;
+ }
+
+
+ private static RemoveStudentFromGroupPath : string = '/api/Plan/RemoveStudentFromGroup';
+ /*****从分组中删除学生接口*****/
+ static RemoveStudentFromGroup(data:any) {
+ var result = Service.Request(this.RemoveStudentFromGroupPath, "POST", data);
+ return result;
+ }
+
+ private static AddPlanLogPath : string = '/api/Plan/AddPlanLog';
+ /*****添加记录接口*****/
+ static AddPlanLog(planId:string,planType:string,timingEvents:string,subsection:string,baogan:string) {
+ var result = Service.Request(this.AddPlanLogPath, "POST", {planId,planType,timingEvents,subsection,baogan });
+ return result;
+ }
+
+ private static GetPlanListNoPagePath : string = '/api/Plan/GetPlanListNoPage';
+ /*****不分页项目接口*****/
+ static GetPlanListNoPage(planType:string) {
+ var result = Service.Request(this.GetPlanListNoPagePath, "GET", {planType});
+ return result;
+ }
+
+
+ private static GetBaoganLogPath : string = '/api/Plan/GetBaoganLog';
+ /*****包干数据接口*****/
+ static GetBaoganLog(data:any) {
+ var result = Service.Request(this.GetBaoganLogPath, "GET", data);
+ return result;
+ }
+
+
+ private static GetJishiLogPath : string = '/api/Plan/GetJishiLog';
+ /*****计时数据接口*****/
+ static GetJishiLog(data:any) {
+ var result = Service.Request(this.GetJishiLogPath, "GET", data);
+ return result;
+ }
+
+
+ private static GetQuxianLogPath : string = '/api/Plan/GetQuxianLog';
+ /*****曲线数据接口*****/
+ static GetQuxianLog(data:any) {
+ var result = Service.Request(this.GetQuxianLogPath, "GET", data);
+ return result;
+ }
+
+
+ private static GetFenduanLogPath : string = '/api/Plan/GetFenduanLog';
+ /*****分段接口*****/
+ static GetFenduanLog(data:any) {
+ var result = Service.Request(this.GetFenduanLogPath, "GET", data);
+ return result;
+ }
+
+
+ private static GetRankDataPath : string = '/api/Plan/GetRankData';
+ /*****成绩排名接口*****/
+ static GetRankData(planId:string) {
+ var result = Service.Request(this.GetRankDataPath, "GET", {planId });
+ return result;
+ }
+
}
export {
diff --git a/src/pages/dataAnalyze/Curve.vue b/src/pages/dataAnalyze/Curve.vue
index 3704517..5f05ec3 100644
--- a/src/pages/dataAnalyze/Curve.vue
+++ b/src/pages/dataAnalyze/Curve.vue
@@ -8,33 +8,49 @@
-
-
-
-
-
-
- {{ selectedProjectName }}
+
+
+
+ 选择项目
+
+ {{ selectProcet || '请选择项目' }}
-
+
+
+
+
+
+
+
+
+
+ 开始日期
+ {{ begin || '请选择' }}
+
+
+
+
+
+ 结束日期
+ {{ end || '请选择' }}
+
+
-
-
+
+
-
+
{{ student.name.charAt(0) }}
@@ -42,7 +58,7 @@
{{ student.name }}
-
+
@@ -57,18 +73,14 @@
选中项目
- {{ selectedProjectName }}
+ {{ selectProcet }}
对比学员
{{ selectedStudentIds.length }}人
-
-
- 训练次数
- {{ maxTrainingCount }}次
-
+
@@ -85,18 +97,12 @@
-
-
- 图例说明
-
-
-
- {{ student.name }}
- ({{ getStudentBestTime(student.id) }}s 最佳)
-
-
-
+
+
+
+
@@ -104,6 +110,7 @@
import { onShow, onLoad } from "@dcloudio/uni-app"
import { Service } from '@/Service/Service'
import { ref, computed, watch } from 'vue'
+ import { PlanService } from '@/Service/swimming/PlanService'
// ==================== 常量定义 ====================
@@ -115,30 +122,17 @@
// ==================== 响应式数据 - 项目相关 ====================
- // 项目列表数据
- const projectList = ref([
- { id: '1', name: '100米自由泳' },
- { id: '2', name: '50米自由泳' },
- { id: '3', name: '200米由泳' },
- { id: '4', name: '100米蛙泳' }
- ])
+ // 项目选择
+ let showProject = ref(false)
+ let selectProcet = ref('')
+ let selectId = ref('')
- // 项目选择器选项(用于 picker 组件)
- const projectOptions = ref(projectList.value)
+ // 日期选择
+ const begin = ref('')
+ const end = ref('')
+ const showCalendar = ref(false)
- // 当前选中的项目索引(用于 picker 组件的显示)
- const selectedProjectIndex = ref(-1)
-
- // 当前选中的项目 ID
- const selectedProjectId = ref('')
-
- // 当前选中的项目名称(用于显示)
- const selectedProjectName = computed(() => {
- if (selectedProjectIndex.value === -1) {
- return '请选择项目'
- }
- return projectList.value[selectedProjectIndex.value]?.name || '请选择项目'
- })
+ const projectOptions = ref>([[]])
// ==================== TypeScript 接口定义 ====================
@@ -147,7 +141,7 @@
* 定义学生的基本信息
*/
interface Student {
- id : string
+ studentId : string
name : string
}
@@ -175,7 +169,7 @@
// 已选中的学生详细信息列表((计算属性)
const selectedStudents = computed(() => {
return projectStudents.value.filter(student =>
- selectedStudentIds.value.includes(student.id)
+ selectedStudentIds.value.includes(student.studentId)
)
})
@@ -227,129 +221,59 @@
series: [] as any[]
})
- // 最大训练次数(计算属性)
- const maxTrainingCount = computed(() => {
- const allRecords = mockTrainingRecords[selectedProjectId.value] || []
- const studentCounts = new Map()
- allRecords.forEach(record => {
- studentCounts.set(record.studentId, (studentCounts.get(record.studentId) || 0) + 1)
- })
- return Math.max(0, ...Array.from(studentCounts.values()))
- })
- // ==================== 模拟数据 ====================
-
- // 模拟的学生数据(按项目分组)
- const mockProjectStudents : Record = {
- '1': [
- { id: 's1', name: '张小明' },
- { id: 's2', name: '李小红' },
- { id: 's3', name: '王小明' },
- { id: 's4', name: '赵小芳' },
- { id: 's5', name: '陈小刚' }
- ],
- '2': [
- { id: 's6', name: '刘小华' },
- { id: 's7', name: '张小丽' },
- { id: 's8', name: '杨小龙' }
- ],
- '3': [
- { id: 's9', name: '黄小东' },
- { id: 's10', name: '吴小西' },
- { id: 's11', name: '周小南' },
- { id: 's12', name: '徐小北' }
- ],
- '4': [
- { id: 's13', name: '马小兵' },
- { id: 's14', name: '朱小红' }
- ]
- }
-
- // 模拟的训练记录数据(按项目分组)
- const mockTrainingRecords : Record = {
- '1': [
- { studentId: 's1', studentName: '张小明', time: 28.5, recordDate: '03-12', recordFullDate: '2026-03-12', round: 1 },
- { studentId: 's1', studentName: '张小明', time: 27.2, recordDate: '03-14', recordFullDate: '2026-03-14', round: 1 },
- { studentId: 's1', studentName: '张小明', time: 26.5, recordDate: '03-16', recordFullDate: '2026-03-16', round: 1 },
- { studentId: 's1', studentName: '张小明', time: 25.8, recordDate: '03-19', recordFullDate: '2026-03-19', round: 1 },
- { studentId: 's1', studentName: '张小明', time: 25.2, recordDate: '03-24', recordFullDate: '2026-03-24', round: 1 },
- { studentId: 's2', studentName: '李小红', time: 32.5, recordDate: '03-12', recordFullDate: '2026-03-12', round: 1 },
- { studentId: 's2', studentName: '李小红', time: 31.2, recordDate: '03-14', recordFullDate: '2026-03-14', round: 1 },
- { studentId: 's2', studentName: '李小红', time: 30.5, recordDate: '03-19', recordFullDate: '2026-03-19', round: 1 },
- { studentId: 's2', studentName: '李小红', time: 29.8, recordDate: '03-24', recordFullDate: '2026-03-24', round: 1 },
- { studentId: 's3', studentName: '王小明', time: 25.8, recordDate: '03-12', recordFullDate: '2026-03-12', round: 1 },
- { studentId: 's3', studentName: '王小明', time: 24.5, recordDate: '03-16', recordFullDate: '2026-03-16', round: 1 },
- { studentId: 's3', studentName: '王小明', time: 23.8, recordDate: '03-24', recordFullDate: '2026-03-24', round: 1 },
- { studentId: 's4', studentName: '赵小芳', time: 33.5, recordDate: '03-14', recordFullDate: '2026-03-14', round: 1 },
- { studentId: 's4', studentName: '赵小芳', time: 32.2, recordDate: '03-19', recordFullDate: '2026-03-19', round: 1 },
- { studentId: 's5', studentName: '陈小刚', time: 35.5, recordDate: '03-19', recordFullDate: '2026-03-19', round: 1 }
- ],
- '2': [
- { studentId: 's6', studentName: '刘小华', time: 24.5, recordDate: '03-12', recordFullDate: '2026-03-12', round: 1 },
- { studentId: 's6', studentName: '刘小华', time: 23.8, recordDate: '03-14', recordFullDate: '2026-03-14', round: 1 },
- { studentId: 's6', studentName: '刘小华', time: 23.2, recordDate: '03-19', recordFullDate: '2026-03-19', round: 1 },
- { studentId: 's7', studentName: '张小丽', time: 26.8, recordDate: '03-12', recordFullDate: '2026-03-12', round: 1 },
- { studentId: 's7', studentName: '张小丽', time: 26.2, recordDate: '03-16', recordFullDate: '2026-03-16', round: 1 },
- { studentId: 's8', studentName: '杨小龙', time: 25.5, recordDate: '03-14', recordFullDate: '2026-03-14', round: 1 },
- { studentId: 's8', studentName: '杨小龙', time: 24.8, recordDate: '03-19', recordFullDate: '2026-03-19', round: 1 }
- ],
- '3': [
- { studentId: 's9', studentName: '黄小东', time: 125.8, recordDate: '03-12', recordFullDate: '2026-03-12', round: 1 },
- { studentId: 's9', studentName: '黄小东', time: 124.5, recordDate: '03-14', recordFullDate: '2026-03-14', round: 1 },
- { studentId: 's10', studentName: '吴小西', time: 128.5, recordDate: '03-12', recordFullDate: '2026-03-12', round: 1 },
- { studentId: 's10', studentName: '吴小西', time: 127.2, recordDate: '03-16', recordFullDate: '2026-03-16', round: 1 },
- { studentId: 's10', studentName: '吴小西', time: 126.8, recordDate: '03-19', recordFullDate: '2026-03-19', round: 1 }
- ],
- '4': [
- { studentId: 's13', studentName: '马小兵', time: 82.5, recordDate: '03-12', recordFullDate: '2026-03-12', round: 1 },
- { studentId: 's13', studentName: '马小兵', time: 81.8, recordDate: '03-14', recordFullDate: '2026-03-14', round: 1 },
- { studentId: 's14', studentName: '朱小红', time: 88.5, recordDate: '03-12', recordFullDate: '2026-03-12', round: 1 }
- ]
- }
-
- // ==================== 生命周期钩子 ====================
onLoad(() => {
- loadProjectData()
+ getProjectData()
})
onShow(() => {
- // TODO: 如果需要在页面显示时刷新数据,可以在这里添加逻辑
+
})
- // ==================== 监听器 ====================
- // 监听选中学生 ID 列表的变化
- // 使用 getter 函数返回数组副本,这样可以确保监听器触发
- watch(() => [...selectedStudentIds.value], (newIds, oldIds) => {
- console.log('selectedStudentIds changed:', newIds)
- console.log('oldIds:', oldIds)
- updateChartData()
- })
- // ==================== 业务逻辑方法 ====================
- const loadProjectData = () => {
- // TODO: 调用后端 API 获取项目列表
+
+ const getProjectData = () => {
+ PlanService.GetPlanListNoPage('计时项目').then(res => {
+ if (res.code == 0) {
+ projectOptions.value[0] = res.data.list
+ } else {
+ Service.Msg(res.msg)
+ }
+ })
}
- const handleProjectChange = (e : any) => {
- const index = e.detail.value
- selectedProjectIndex.value = index
- const selectedProject = projectList.value[index]
- if (selectedProject) {
- selectedProjectId.value = selectedProject.id
- loadProjectStudents(selectedProject.id)
- }
+ const selectProcetFunc = (e : any) => {
+ selectProcet.value = e.value[0].name
+ selectId.value = e.value[0].planId
selectedStudentIds.value = []
+ loadProjectStudents(selectId.value)
+ }
+
+ const openCalendar = () => {
+ showCalendar.value = true
+ }
+
+ const calendarConfirm = (e : any) => {
+ begin.value = e[0]
+ end.value = e[e.length - 1]
+ showCalendar.value = false
+ }
+
+ const calendarClose = () => {
+ showCalendar.value = false
}
const loadProjectStudents = (projectId : string) => {
- if (mockProjectStudents[projectId]) {
- projectStudents.value = mockProjectStudents[projectId]
- } else {
- projectStudents.value = []
- }
+ PlanService.GetPlanInfo(projectId).then(res => {
+ if (res.code == 0) {
+ projectStudents.value = res.data.plan.users
+ } else {
+ Service.Msg(res.msg)
+ }
+ })
}
const toggleStudent = (studentId : string) => {
@@ -363,57 +287,44 @@
}
selectedStudentIds.value.push(studentId)
}
- }
-
- const getStudentBestTime = (studentId : string) : number => {
- const records = mockTrainingRecords[selectedProjectId.value] || []
- const studentRecords = records.filter(r => r.studentId === studentId)
- if (studentRecords.length === 0) return 0
- return Math.min(...studentRecords.map(r => r.time))
- }
-
- const updateChartData = () => {
- if (selectedStudentIds.value.length === 0 || !selectedProjectId.value) {
- lineChartData.value.series = []
- lineChartData.value.categories = []
- return
+ let studentIdList = selectedStudentIds.value.map((item) => {
+ return item
+ })
+ let data = {
+ planId: selectId.value,
+ studentId: JSON.stringify(studentIdList),
+ sTime: begin.value,
+ eTime: end.value
+ }
+ interface DataItem {
+ name : string
+ data : any[]
}
- const projectRecords = mockTrainingRecords[selectedProjectId.value] || []
- const allDates = new Set()
- projectRecords.forEach(record => {
- allDates.add(record.recordFullDate)
+
+ const xData = ref(
+ Array.from({ length: studentIdList.length }, () => ({
+ name: '',
+ data: []
+ }))
+ )
+ PlanService.GetQuxianLog(data).then(res => {
+ if (res.code == 0) {
+
+ res.data.list.map((item : any) => {
+ lineChartData.value.categories.push(item.dayTime)
+ item.data.map((content : any, index : any) => {
+ xData.value[index].name = content.studentName
+ xData.value[index].data.push(content.time)
+ })
+ })
+ console.log(xData.value);
+ lineChartData.value.series = xData.value
+ } else {
+ Service.Msg(res.msg)
+ }
})
- const sortedDates = Array.from(allDates).sort()
-
- lineChartData.value.categories = sortedDates.map(date => {
- const parts = date.split('-')
- return `${parts[1]}-${parts[2]}`
- })
-
- const seriesData : any[] = []
-
- selectedStudentIds.value.forEach((studentId, index) => {
- const studentRecords = projectRecords.filter(r => r.studentId === studentId)
- studentRecords.sort((a, b) => a.recordFullDate.localeCompare(b.recordFullDate))
-
- const timeMap = new Map()
- studentRecords.forEach(record => {
- timeMap.set(record.recordFullDate, record.time)
- })
-
- const timeData = sortedDates.map(date => timeMap.get(date) || null)
- const studentName = projectStudents.value.find(s => s.id === studentId)?.name || '未知学员'
-
- seriesData.push({
- name: studentName,
- data: timeData,
- color: chartColors[index]
- })
- })
- console.log();
- lineChartData.value.series = seriesData
}
@@ -466,12 +377,55 @@
}
}
- .project-select-section {
+ .date-filter {
+ display: flex;
+ gap: 24rpx;
+
+ .date-picker {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ gap: 16rpx;
+ background-color: #fff;
+ border-radius: 16rpx;
+ padding: 20rpx;
+
+ .date-text {
+ .label {
+ display: block;
+ font-size: 26rpx;
+ color: #999;
+ }
+
+ .value {
+ display: block;
+ font-size: 30rpx;
+ color: #333;
+ margin-top: 4rpx;
+ font-weight: 600;
+ }
+ }
+ }
+ }
+
+ .select-section {
background-color: #fff;
margin: 0 20rpx 20rpx;
- border-radius: 16rpx;
+ border-radius: 20rpx;
padding: 28rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
+ position: relative;
+ overflow: hidden;
+
+ .select-label {
+ margin-bottom: 16rpx;
+
+ .label-text {
+ font-size: 26rpx;
+ font-weight: 600;
+ color: #666;
+ }
+ }
.picker-wrapper {
display: flex;
@@ -485,7 +439,8 @@
&:active {
background-color: #f0f0f0;
- border-color: #52c41a;
+ border-color: #faad14;
+ transform: scale(0.98);
}
.picker-text {
@@ -573,126 +528,126 @@
- .chart-section {
- margin: 0 20rpx;
+ .chart-section {
+ margin: 0 20rpx;
- .stats-card {
- background-color: #fff;
- border-radius: 16rpx;
- padding: 24rpx;
- margin-bottom: 20rpx;
- display: flex;
- align-items: center;
- justify-content: space-around;
- box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
+ .stats-card {
+ background-color: #fff;
+ border-radius: 16rpx;
+ padding: 24rpx;
+ margin-bottom: 20rpx;
+ display: flex;
+ align-items: center;
+ justify-content: space-around;
+ box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
- .stat-item {
- text-align: center;
+ .stat-item {
+ text-align: center;
- .stat-label {
- font-size: 24rpx;
- color: #999;
- display: block;
- margin-bottom: 8rpx;
- }
-
- .stat-value {
- font-size: 32rpx;
- font-weight: 700;
- color: #52c41a;
- }
+ .stat-label {
+ font-size: 24rpx;
+ color: #999;
+ display: block;
+ margin-bottom: 8rpx;
}
- .stat-divider {
- width: 1rpx;
- height: 50rpx;
- background-color: #eee;
+ .stat-value {
+ font-size: 32rpx;
+ font-weight: 700;
+ color: #52c41a;
}
}
- .chart-card {
- background-color: #fff;
- border-radius: 16rpx;
- padding: 24rpx;
- margin-bottom: 20rpx;
- box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
-
- .chart-header {
- margin-bottom: 20rpx;
- padding-left: 12rpx;
- border-left: 6rpx solid #52c41a;
-
- .chart-title {
- font-size: 30rpx;
- font-weight: 600;
- color: #333;
- display: block;
- margin-bottom: 6rpx;
- }
-
- .chart-desc {
- font-size: 24rpx;
- color: #999;
- }
- }
-
- .chart-scroll {
- width: 100%;
- height: 500rpx;
- background-color: #fafafa;
- border-radius: 12rpx;
- overflow: hidden;
- white-space: nowrap;
-
- .chart-box {
- height: 100%;
- display: inline-block;
- }
- }
+ .stat-divider {
+ width: 1rpx;
+ height: 50rpx;
+ background-color: #eee;
}
+ }
- .legend-section {
- background-color: #fff;
- border-radius: 16rpx;
- padding: 24rpx;
- box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
+ .chart-card {
+ background-color: #fff;
+ border-radius: 16rpx;
+ padding: 24rpx;
+ margin-bottom: 20rpx;
+ box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
- .legend-title {
- font-size: 28rpx;
+ .chart-header {
+ margin-bottom: 20rpx;
+ padding-left: 12rpx;
+ border-left: 6rpx solid #52c41a;
+
+ .chart-title {
+ font-size: 30rpx;
font-weight: 600;
color: #333;
- margin-bottom: 20rpx;
+ display: block;
+ margin-bottom: 6rpx;
}
- .legend-list {
+ .chart-desc {
+ font-size: 24rpx;
+ color: #999;
+ }
+ }
+
+ .chart-scroll {
+ width: 100%;
+ height: 500rpx;
+ background-color: #fafafa;
+ border-radius: 12rpx;
+ overflow: hidden;
+ white-space: nowrap;
+
+ .chart-box {
+ height: 100%;
+ display: inline-block;
+ }
+ }
+ }
+
+ .legend-section {
+ background-color: #fff;
+ border-radius: 16rpx;
+ padding: 24rpx;
+ box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
+
+ .legend-title {
+ font-size: 28rpx;
+ font-weight: 600;
+ color: #333;
+ margin-bottom: 20rpx;
+ }
+
+ .legend-list {
+ display: flex;
+ flex-direction: column;
+ gap: 16rpx;
+
+ .legend-item {
display: flex;
- flex-direction: column;
- gap: 16rpx;
+ align-items: center;
+ gap: 12rpx;
- .legend-item {
- display: flex;
- align-items: center;
- gap: 12rpx;
+ .legend-color {
+ width: 24rpx;
+ height: 24rpx;
+ border-radius: 50%;
+ flex-shrink: 0;
+ }
- .legend-color {
- width: 24rpx;
- height: 24rpx;
- border-radius: 50%;
- flex-shrink: 0;
- }
+ .legend-name {
+ font-size: 26rpx;
+ color: #333;
+ font-weight: 600;
+ }
- .legend-name {
- font-size: 26rpx;
- color: #333;
- font-weight: 600;
- }
-
- .legend-desc {
- font-size: 24rpx;
- color: #999;
- }
+ .legend-desc {
+ font-size: 24rpx;
+ color: #999;
}
}
}
}
+ }
\ No newline at end of file
diff --git a/src/pages/dataAnalyze/baoganAnalyze.vue b/src/pages/dataAnalyze/baoganAnalyze.vue
index c3e7f63..c5df7b2 100644
--- a/src/pages/dataAnalyze/baoganAnalyze.vue
+++ b/src/pages/dataAnalyze/baoganAnalyze.vue
@@ -11,31 +11,41 @@
- 选择项目
-
-
-
- {{ selectedProjectName }}
+ 选择项目
+
+ {{ selectProcet || '请选择项目' }}
-
+
+
-
-
-
-
+
+
+
+
+
+ 开始日期
+ {{ begin || '请选择' }}
+
+
+
+
+
+ 结束日期
+ {{ end || '请选择' }}
+
+
-
+
选择学生
- {{ selectedStudentDisplay }}
+ {{ selectedStudentDisplay }}
@@ -51,7 +61,7 @@
-
@@ -61,40 +71,29 @@
-
+
{{ student.name.charAt(0) }}
{{ student.name }}
-
-
- {{ student.gender }}
-
- {{ student.age }}岁
-
+
-
-
-
-
-
-
-
- 该日期暂无训练数据
-
-
-
+
+
-
+
+
+
@@ -102,6 +101,7 @@
import { onShow, onLoad } from "@dcloudio/uni-app"
import { Service } from '@/Service/Service'
import { ref, computed } from 'vue'
+ import { PlanService } from '@/Service/swimming/PlanService'
interface Project {
id : string
@@ -134,65 +134,62 @@
const currentMonth = ref(new Date().getMonth() + 1)
const selectedDate = ref('')
+ // 项目选择
+ let showProject = ref(false)
+ let selectProcet = ref('')
+ let selectId = ref('')
+
+ // 日期选择
+ const begin = ref('')
+ const end = ref('')
+ const showCalendar = ref(false)
+ const calendarType = ref<'begin' | 'end'>('begin')
+
+ // 分页相关
+ let page = ref(1)
+ let status = ref('loadmore')
+
+ let pageTotal = ref(10)
+
const columns = ref([
{
- label: '日期',
- prop: 'date',
- width: '100px'
+ label: '训练日期',
+ name: 'addTime',
},
{
label: '姓名',
- prop: 'name',
- width: '100px'
+ name: 'studentName',
},
{
label: '项目名称',
- prop: 'projectName',
- width: '120px'
+ name: 'name',
},
{
- label: '包干计划',
- prop: 'plan',
- width: '100px'
+ label: '项目类型',
+ name: 'planType',
},
{
label: '完成比例',
- prop: 'completion',
- width: '100px'
+ name: 'completion',
},
{
label: '详细数据',
- prop: 'detail',
- width: '120px'
+ name: 'detail',
}
])
const tableData = ref([])
- const projectList = ref([
- { id: '1', name: '100米自由泳' },
- { id: '2', name: '200米自由泳' },
- { id: '3', name: '400米自由泳' },
- { id: '4', name: '100米蛙泳' }
- ])
- const projectOptions = ref(projectList.value)
+
+ const projectOptions = ref>([[]])
const selectedProjectIndex = ref(-1)
const selectedProjectId = ref('')
- const selectedProjectName = computed(() => {
- if (selectedProjectIndex.value === -1) {
- return '请选择项目'
- }
- return projectList.value[selectedProjectIndex.value]?.name || '请选择项目'
- })
- const selectedDates = ref([
- { date: '2026-03-28', info: '训练' },
- { date: '2026-03-29', info: '训练' },
- { date: '2026-03-30', info: '训练' }
- ])
+
+
const studentList = ref([])
@@ -215,69 +212,12 @@
return Math.round(total / tableData.value.length)
})
- const mockData : Record> = {
- '1': {
- '2026-03-28': [
- { name: '张小明', projectName: '100米自由泳', plan: '2000米', completion: 95, detail: '1900/2000' },
- { name: '李小红', projectName: '100米自由泳', plan: '2000米', completion: 92, detail: '1840/2000' },
- { name: '王小明', projectName: '100米自由泳', plan: '2000米', completion: 90, detail: '1800/2000' }
- ],
- '2026-03-29': [
- { name: '张小明', projectName: '100米自由泳', plan: '2000米', completion: 90, detail: '1800/2000' },
- { name: '李小红', projectName: '100米自由泳', plan: '2000米', completion: 95, detail: '1900/2000' },
- { name: '王小明', projectName: '100米自由泳', plan: '2000米', completion: 88, detail: '1760/2000' }
- ]
- },
- '2': {
- '2026-03-28': [
- { name: '张小明', projectName: '200米自由泳', plan: '1500米', completion: 88, detail: '1320/1500' },
- { name: '李小红', projectName: '200米自由泳', plan: '1500米', completion: 92, detail: '1380/1500' },
- { name: '王小明', projectName: '200米自由泳', plan: '1500米', completion: 85, detail: '1275/1500' }
- ],
- '2026-03-29': [
- { name: '张小明', projectName: '200米自由泳', plan: '1500米', completion: 90, detail: '1350/1500' },
- { name: '李小红', projectName: '200米自由泳', plan: '1500米', completion: 88, detail: '1320/1500' }
- ]
- },
- '3': {
- '2026-03-28': [
- { name: '张小明', projectName: '400米自由泳', plan: '1200米', completion: 85, detail: '1020/1200' },
- { name: '李小红', projectName: '400米自由泳', plan: '1200米', completion: 90, detail: '1080/1200' }
- ]
- },
- '4': {
- '2026-03-28': [
- { name: '赵小芳', projectName: '100米蛙泳', plan: '1800米', completion: 88, detail: '1584/1800' },
- { name: '陈小' + '刚', projectName: '100米蛙泳', plan: '1800米', completion: 95, detail: '1710/1800' }
- ]
- }
- }
- const mockStudents : Record = {
- '1': [
- { id: 's1', name: '张小明', gender: '男', age: 12 },
- { id: 's2', name: '李小红', gender: '女', age: 11 },
- { id: 's3', name: '王小明', gender: '男', age: 13 }
-
- ],
- '2': [
- { id: 's1', name: '张小明', gender: '男', age: 12 },
- { id: 's2', name: '李小红', gender: '女', age: 11 },
- { id: 's3', name: '王小明', gender: '男', age: 13 }
-
- ],
- '3': [
- { id: 's1', name: '张小明', gender: '男', age: 12 },
- { id: 's2', name: '李小红', gender: '女', age: 11 }
- ],
- '4': [
- { id: 's4', name: '赵小芳', gender: '女', age: 10 },
- { id: 's5', name: '陈小' + '刚', gender: '男', age: 14 }
- ]
- }
+
onLoad(() => {
loadData()
+ getProjectData()
})
onShow(() => {
@@ -288,50 +228,53 @@
}
- const handleCellClick = (event : any) => {
- console.log('单元格点击事件:', event)
+
+ const getProjectData = () => {
+ PlanService.GetPlanListNoPage('计时项目').then(res => {
+ if (res.code == 0) {
+ projectOptions.value[0] = res.data.list
+ } else {
+ Service.Msg(res.msg)
+ }
+ })
}
- const handleProjectChange = (e : any) => {
- const index = e.detail.value
- selectedProjectIndex.value = index
- const selectedProject = projectList.value[index]
-
- if (selectedProject) {
- selectedProjectId.value = selectedProject.id
- selectedDate.value = ''
- selectedStudentIndexes.value = []
- selectedStudentIds.value = []
- tableData.value = []
- loadProjectStudents(selectedProject.id)
- }
+ const selectProcetFunc = (e : any) => {
+ selectProcet.value = e.value[0].name
+ selectId.value = e.value[0].planId
+ getProjectDetail()
}
- const handleMonthSwitch = (e : any) => {
- currentYear.value = e.year
- currentMonth.value = e.month
- loadData()
- selectedDate.value = ''
- selectedStudentIndexes.value = []
- selectedStudentIds.value = []
- tableData.value = []
+
+ const getProjectDetail = () => {
+ PlanService.GetPlanInfo(selectId.value).then(res => {
+ if (res.code == 0) {
+ studentList.value = res.data.plan.users
+ } else {
+ Service.Msg(res.msg)
+ }
+ })
}
- const handleDateChange = (e : any) => {
- console.log(e);
- const date = e.fulldate
- selectedDate.value = date
- selectedStudentIndexes.value = []
- selectedStudentIds.value = []
- tableData.value = []
+
+
+
+ const openCalendar = () => {
+ showCalendar.value = true
}
- const loadProjectStudents = (projectId : string) => {
- const students = mockStudents[projectId] || []
- studentList.value = students
- console.log(`项目 ${projectId} 的学生列表加载完成,共 ${students.length} 位学生`)
+ const calendarConfirm = (e : any) => {
+ begin.value = e[0]
+ end.value = e[e.length - 1]
+ showCalendar.value = false
}
+ const calendarClose = () => {
+ showCalendar.value = false
+ }
+
+
+
const selectAllStudents = () => {
selectedStudentIndexes.value = studentList.value.map((_, index) => index)
}
@@ -350,36 +293,50 @@
}
const confirmStudentSelection = () => {
- selectedStudentIds.value = selectedStudentIndexes.value.map(index => studentList.value[index].id)
+ selectedStudentIds.value = selectedStudentIndexes.value.map(index => studentList.value[index].studentId)
showStudentPicker.value = false
- filterAndLoadData()
+
+ getRecord()
}
+ const getRecord = () => {
+ page.value = 1
+ getRecordList()
+ }
+
+ const getRecordList = () => {
+ let studentIdList = selectedStudentIndexes.value.map(index => {
+ return studentList.value[index].studentId
+ })
+
+ let data = {
+ planId: selectId.value,
+ studentId: JSON.stringify(studentIdList),
+ sTime: begin.value,
+ eTime: end.value,
+ page: page.value
+ }
+
+ PlanService.GetBaoganLog(data).then(res => {
+ if (res.code == 0) {
+ pageTotal.value = res.data.pageTotal
+ tableData.value = res.data.list
+ } else {
+ Service.Msg(res.msg)
+ }
+ })
+ }
+
+
+ const pageChange = (e) => {
+ page.value = e
+ getRecordList()
+ }
+
+
const closeStudentPicker = () => {
showStudentPicker.value = false
}
-
- const filterAndLoadData = () => {
- if (!selectedDate.value || selectedStudentIndexes.value.length === 0) {
- tableData.value = []
- return
- }
-
- const projectData = mockData[selectedProjectId.value]
- if (!projectData) {
- tableData.value = []
- return
- }
-
- const dateData = projectData[selectedDate.value]
- if (!dateData) {
- tableData.value = []
- return
- }
-
- const selectedStudentNames = selectedStudentIndexes.value.map(index => studentList.value[index].name)
- tableData.value = dateData.filter(item => selectedStudentNames.includes(item.name))
- }
\ No newline at end of file
diff --git a/src/pages/dataAnalyze/timingAnalze.vue b/src/pages/dataAnalyze/timingAnalze.vue
index 27da24b..b670af2 100644
--- a/src/pages/dataAnalyze/timingAnalze.vue
+++ b/src/pages/dataAnalyze/timingAnalze.vue
@@ -1,5 +1,5 @@
-
+
-
-
-
-
+
+
+
+
+
+
+
+ 开始日期
+ {{ begin || '请选择' }}
+
+
+
+
+
+ 结束日期
+ {{ end || '请选择' }}
+
+
-
-
-
-
- 选中日期
- {{ selectedDate }}
-
-
-
- 训练人数
- {{ tableData.length }}人
-
-
-
- 平均速度
- {{ averageSpeed }}m/s
-
-
-
-
-
-
-
-
-
-
-
- 该日期暂无训练数据
-
-
-
+
+
-
+
+
+
@@ -66,250 +47,143 @@
import { onShow, onLoad } from "@dcloudio/uni-app"
import { Service } from '@/Service/Service'
import { ref, computed } from 'vue'
+ import { PlanService } from '@/Service/swimming/PlanService'
- // ==================== 响应式数据定义 ====================
+ interface Project {
+ id : string
+ name : string
+ }
+
+ interface Student {
+ id : string
+ name : string
+ gender : string
+ age : number
+ }
+
+ interface TableDataItem {
+ date : string
+ name : string
+ projectName : string
+ plan : string
+ completion : number
+ detail : string
+ }
+
+ interface StudentRecord {
+ student : Student
+ date : string
+ segments : TableDataItem[]
+ }
- // 当前年份,用于月份切换时记录当前年份
const currentYear = ref(new Date().getFullYear())
-
- // 当前月份(1-12),用于月份切换时记录当前月份
const currentMonth = ref(new Date().getMonth() + 1)
-
- // 当前选中的日期,格式为 'YYYY-MM-DD'
- // 空字符串表示未选择日期
const selectedDate = ref('')
- // ==================== 表格配置 ====================
+ // 项目选择
+ let showProject = ref(false)
+ let selectProcet = ref('')
+ let selectId = ref('')
+
+ // 日期选择
+ const begin = ref('')
+ const end = ref('')
+ const showCalendar = ref(false)
+ const calendarType = ref<'begin' | 'end'>('begin')
+
+ // 分页相关
+ let page = ref(1)
+ let status = ref('loadmore')
+
+ let pageTotal = ref(10)
- // 表格列定义
- // label: 列标题
- // prop: 数据项中对应的字段名
- // width: 列宽度
const columns = ref([
{
label: '姓名',
- prop: 'name',
- width: '100px'
+ name: 'studentName',
},
{
label: '项目名称',
- prop: 'projectName',
- width: '150px'
+ name: 'planName',
},
{
- label: '最大速度',
- prop: 'maxSpeed',
- width: '120px'
+ label: '最快速度',
+ name: 'quicklyTime',
},
{
- label: '计时数据',
- prop: 'timingData',
- width: '150px'
+ label: '详细数据',
+ name: 'time',
+ width:'200'
}
])
- // ==================== TypeScript 接口定义 ====================
-
- // 表格数据项接口定义
- // 描述一条计时记录的数据结构
- interface TableDataItem {
- name: string // 学员姓名
- projectName: string // 项目名称(如:100米自由泳)
- maxSpeed: number // 最大速度,单位:m/s
- timingData: string // 计时数据(如:1.23, 1.25, 1.24)
- }
-
- // ==================== 响应式数据 - 表格数据 ====================
-
- // 表格数据列表
- // 存储当前选中日期的所有计时记录
- // 初始状态为空数组,等待用户选择日期后加载数据
const tableData = ref([])
- // ==================== 响应式数据 - 日历标记 ====================
-
- // 日历打点数据
- // 用于在日历上标记有训练记录的日期
- // date: 日期字符串,格式为 'YYYY-MM-DD'
- // info: 显示在日期上的标记信息
- const selectedDates = ref([
- { date: '2026-03-12', info: '训练' },
- { date: '2026-03-14', info: '训练' },
- { date: '2026-03-16', info: '训练' },
- { date: '2026-03-19', info: '训练' },
- { date: '2026-03-24', info: '训练' }
- ])
-
- // ==================== 计算属性 ====================
-
- // 平均速度计算属性
- // 计算当前选中日期所有学员的平均最大速度
- // 如果没有数据则返回 0
- const averageSpeed = computed(() => {
- // 如果没有数据,返回 0
- if (tableData.value.length === 0) return 0
-
- // 计算所有学员最大速度的总和
- const total = tableData.value.reduce((sum, item) => sum + item.maxSpeed, 0)
-
- // 计算平均值,保留两位小数
- return (total / tableData.value.length).toFixed(2)
- })
-
- // ======================= 模拟数据 ====================
-
- // 模拟的训练数据
- // 键为日期字符串,值为该日期的计时记录列表
- // 在实际项目中,这里应该从后端 API 获取数据
- const mockData: Record = {
- '2026-03-12': [
- { name: '张小明', projectName: '50米自由泳', maxSpeed: 2.15, timingData: '25.35s, 25.42s, 25.28s' },
- { name: '李小红', projectName: '50米自由泳', maxSpeed: 1.98, timingData: '27.15s, 27.32s, 27.08s' },
- { name: '王小明', projectName: '50米自由泳', maxSpeed: 2.22, timingData: '24.68s, 24.75s, 24.62s' }
- ],
- '2026-03-14': [
- { name: '张小明', projectName: '50米自由泳', maxSpeed: 2.18, timingData: '25.12s, 25.18s, 25.08s' },
- { name: '李小红', projectName: '50米自由泳', maxSpeed: 2.02, timingData: '26.85s, 26.92s, 26.78s' },
- { name: '赵小芳', projectName: '50米自由泳', maxSpeed: 1.92, timingData: '27.95s, 28.12s, 27.88s' },
- { name: '王小明', projectName: '50米自由泳', maxSpeed: 2.25, timingData: '24.52s, 24.58s, 24.48s' }
- ],
- '2026-03-16': [
- { name: '张小明', projectName: '50米自由泳', maxSpeed: 2.12, timingData: '25.55s, 25.62s, 25.48s' },
- { name: '李小红', projectName: '50米自由泳', maxSpeed: 2.05, timingData: '26.72s, 26.85s, 26.68s' }
- ],
- '2026-03-19': [
- { name: '张小明', projectName: '50米自由泳', maxSpeed: 2.20, timingData: '24.98s, 25.05s, 24.92s' },
- { name: '李小红', projectName: '50米自由泳', maxSpeed: 2.08, timingData: '26.45s, 26.52s, 26.38s' },
- { name: '赵小芳', projectName: '50米自由泳', maxSpeed: 1.95, timingData: '27.75s, 27.88s, 27.65s' },
- { name: '王小明', projectName: '50米自由泳', maxSpeed: 2.28, timingData: '24.35s, 24.42s, 24.28s' },
- { name: '陈小刚', projectName: '50米自由泳', maxSpeed: 1.88, timingData: '28.25s, 28.38s, 28.15s' }
- ],
- '2026-03-24': [
- { name: '张小明', projectName: '50米自由泳', maxSpeed: 2.15, timingData: '25.30s, 25.38s, 25.25s' },
- { name: '李小红', projectName: '50米自由泳', maxSpeed: 2.10, timingData: '26.25s, 26.32s, 26.18s' },
- { name: '赵小芳', projectName: '50米自由泳', maxSpeed: 1.98, timingData: '27.65s, 27.78s, 27.55s' }
- ]
- }
-
- // ==================== 生命周期钩子 ====================
-
- // 页面加载时触发
- // 在页面初始化时执行,只执行一次
onLoad(() => {
- // 加载当前月份的数据
- loadData()
+
})
- // 页面显示时触发
- // 每次页面从后台切换到前台时执行
- // 可用于刷新页面显示的数据
onShow(() => {
- // TODO: 如果需要在页面显示时刷新数据,可以在这里添加逻辑
- // 例如:调用接口获取最新数据
+
})
- // ==================== 业务逻辑方法 ====================
-
- /**
- * 加载数据
- * 调用后端 API 获取数据
- * 当前为模拟数据,实际开发中应替换为真实 API 调用
- */
- const loadData = () => {
- // TODO: 调用后端 API 获取数据
- // 示例代码:
- // Service.Request('/api/timing/data', 'GET', {
- // year: currentYear.value,
- // month: currentMonth.value
- // }).then(res => {
- // // 处理返回的数据
- // })
+ const openCalendar = () => {
+ showCalendar.value = true
}
- /**
- * 处理表格单元格点击事件
- * @param event 点击事件对象,包含行索引、列索引、单元格数据等信息
- */
- const handleCellClick = (event: any) => {
- // 在控制台输出点击事件信息,用于调试
- console.log('表格单元格点击事件:', event)
-
- // TODO: 根据业务需求处理单元格点击
- // 例如:点击某行可以查看详细信息,点击某列可以进行排序等
+ const calendarConfirm = (e : any) => {
+ begin.value = e[0]
+ end.value = e[e.length - 1]
+ showCalendar.value = false
+ getRecord()
}
- /**
- * 处理日历月份切换事件
- * 当用户切换日历的月份时触发
- * @param e 切换事件对象,包含 year 和 month 属性
- */
- const handleMonthSwitch = (e: any) => {
- // 更新当前年份
- currentYear.value = e.year
-
- // 更新当前月份
- currentMonth.value = e.month
-
- // 重新加载数据
- // 获取切换后月份的训练记录和日历标记
- loadData()
-
- // 清空选中的日期
- // 因为切换了月份,之前选择的日期可能不再显示
- selectedDate.value = ''
-
- // 清空表格数据
- tableData.value = []
+ const calendarClose = () => {
+ showCalendar.value = false
}
- /**
- * 处理日历日期选择事件
- * 当用户点击日历上的某个日期时触发
- * @param e 选择事件对象,fulldate 属性包含完整的日期字符串
- */
- const handleDateChange = (e: any) => {
- // 获取选择的完整日期,格式为 'YYYY-MM-DD'
- const date = e.fulldate
- // 更新选中的日期
- selectedDate.value = date
- // 根据选择的日期加载对应的训练数据
- // 检查模拟数据中是否存在该日期的数据
- if (mockData[date]) {
- // 如果存在,加载数据
- tableData.value = mockData[date]
- } else {
- // 如果不存在,清空数据(表格将显示空状态)
- tableData.value = []
+
+ const getRecord = () => {
+ page.value = 1
+ getRecordList()
+ }
+
+ const getRecordList = () => {
+ let data = {
+ sTime: begin.value,
+ eTime: end.value
}
+ PlanService.GetJishiLog(data).then(res => {
+ if (res.code == 0) {
+ pageTotal.value = res.data.pageTotal
+ tableData.value = res.data.list
+ } else {
+ Service.Msg(res.msg)
+ }
+ })
+ }
- // TODO: 实际项目中应该调用 API 获取该日期的计时数据
- // 示例代码:
- // Service.Request('/api/timing/detail', 'GET', {
- // date: date
- // }).then(res => {
- // // 处理返回的数据并更新表格
- // tableData.value = res.data
- // })
+
+ const pageChange = (e) => {
+ page.value = e
+ getRecordList()
}
+
+ @keyframes fadeIn {
+ from {
+ opacity: 0;
+ }
+
+ to {
+ opacity: 1;
+ }
+ }
+
+ @keyframes slideUp {
+ from {
+ transform: translateY(100%);
+ }
+
+ to {
+ transform: translateY(0);
+ }
+ }
+
\ No newline at end of file
diff --git a/src/pages/index/index.vue b/src/pages/index/index.vue
index 4175b5e..8ac07fb 100644
--- a/src/pages/index/index.vue
+++ b/src/pages/index/index.vue
@@ -97,7 +97,7 @@
项目列表
-
+
@@ -110,7 +110,7 @@
{{ project.users.length }}人
-
+
暂无项目
@@ -125,18 +125,18 @@
@@ -652,6 +657,7 @@
display: flex;
flex-direction: column;
gap: 16rpx;
+ max-height: 240rpx;
}
.project-item {
@@ -660,7 +666,7 @@
gap: 20rpx;
padding: 28rpx 24rpx;
background: linear-gradient(135deg, #fff 0%, #fafbfc 100%);
- border-radius: 20rpx;
+ // border-radius: 20rpx;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06), 0 0 0 1rpx rgba(0, 0, 0, 0.04) inset;
position: relative;
diff --git a/src/pages/userFunc/project.vue b/src/pages/userFunc/project.vue
index 841eecc..d9cd77a 100644
--- a/src/pages/userFunc/project.vue
+++ b/src/pages/userFunc/project.vue
@@ -9,7 +9,7 @@
-
+