commit a6a37473271c0707604b84818f026786b6105a5d Author: lovife <474239041@qq.com> Date: Fri May 15 11:48:26 2026 +0800 feat: 建筑数字化管理系统静态原型 Vue 3 + Element Plus + Vite,12 页面(PC 7 页 + 移动端 5 页), 暖色亲和视觉风格,手机模拟框小程序预览,完整模拟数据 + CRUD 操作。 Co-Authored-By: Claude Opus 4.7 diff --git a/20260515/.gitignore b/20260515/.gitignore new file mode 100644 index 0000000..fd41286 --- /dev/null +++ b/20260515/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +dist/ +.superpowers/ +.DS_Store +*.log diff --git a/20260515/docs/superpowers/plans/2026-05-15-construction-prototype-plan.md b/20260515/docs/superpowers/plans/2026-05-15-construction-prototype-plan.md new file mode 100644 index 0000000..2f2dfff --- /dev/null +++ b/20260515/docs/superpowers/plans/2026-05-15-construction-prototype-plan.md @@ -0,0 +1,1726 @@ +# 建筑数字化管理系统 - 静态原型实现计划 + +> **执行方式:** 使用 superpowers:subagent-driven-development 或 superpowers:executing-plans 逐任务实现。步骤使用 `- [ ]` 复选框跟踪进度。 + +**目标:** 基于 Vue 3 + Element Plus 构建建筑数字化管理系统 12 页静态原型,含 PC 后台端和手机模拟端,暖色亲和视觉风格。 + +**架构:** Vite + Vue 3 + Vue Router 4 + Element Plus。PC 端 7 页使用经典侧边栏布局 + Vue Router 路由切换。手机端 5 页通过 PhoneSimulator 组件内部动态组件切换,在 PC 端抽屉面板中以 375x667 模拟框展示。双端共享 reactive store 中的模拟数据,CRUD 操作在内存中生效。 + +**技术栈:** Vue 3 (Composition API), Vite 5, Vue Router 4, Element Plus, CSS 变量主题定制 + +--- + +### Task 1: 项目脚手架搭建 + +**文件:** +- 创建: `package.json`, `vite.config.js`, `index.html` +- 创建: `src/main.js`, `src/App.vue` + +- [ ] **Step 1: 初始化 package.json** + +```json +{ + "name": "construction-management-prototype", + "version": "1.0.0", + "private": true, + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "vue": "^3.4.0", + "vue-router": "^4.3.0", + "element-plus": "^2.7.0", + "@element-plus/icons-vue": "^2.3.1" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.0.0", + "vite": "^5.4.0" + } +} +``` + +- [ ] **Step 2: 创建 vite.config.js** + +```js +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +export default defineConfig({ + plugins: [vue()], + server: { + port: 3000, + open: true + } +}) +``` + +- [ ] **Step 3: 创建 index.html** + +```html + + + + + + 建筑数字化管理系统 + + +
+ + + +``` + +- [ ] **Step 4: 创建 src/main.js** + +```js +import { createApp } from 'vue' +import ElementPlus from 'element-plus' +import 'element-plus/dist/index.css' +import * as ElementPlusIconsVue from '@element-plus/icons-vue' +import App from './App.vue' +import router from './router' +import './styles/theme.css' + +const app = createApp(App) +app.use(ElementPlus, { locale: { el: { /* use default zh-CN from element-plus */ } } }) +app.use(router) +for (const [key, component] of Object.entries(ElementPlusIconsVue)) { + app.component(key, component) +} +app.mount('#app') +``` + +- [ ] **Step 5: 创建 src/App.vue** + +```vue + +``` + +- [ ] **Step 6: 安装依赖并验证** + +```bash +cd D:/IDEA_Project/tmp/20260515 && npm install +``` + +```bash +npm run dev +``` +验证: 浏览器打开 http://localhost:3000,看到空白页面(路由尚未配置),控制台无报错。 + +- [ ] **Step 7: 提交** + +```bash +git init && git add -A && git commit -m "feat: scaffold Vite + Vue 3 + Element Plus project" +``` + +--- + +### Task 2: 暖色主题样式 + +**文件:** +- 创建: `src/styles/theme.css` + +- [ ] **Step 1: 创建 Element Plus 暖色主题覆盖** + +```css +/* src/styles/theme.css */ +:root { + --el-color-primary: #e67e22; + --el-color-primary-light-1: #e98e3f; + --el-color-primary-light-2: #ed9e5c; + --el-color-primary-light-3: #f0ae79; + --el-color-primary-light-4: #f4be96; + --el-color-primary-light-5: #f7ceb3; + --el-color-primary-light-6: #fadecf; + --el-color-primary-light-7: #fceeeb; + --el-color-primary-light-8: #fef8f5; + --el-color-primary-light-9: #fefcfa; + --el-color-primary-dark-1: #cf711f; + --el-color-success: #67c23a; + --el-color-warning: #e6a23c; + --el-color-danger: #f56c6c; + --el-bg-color-page: #fdf8f3; + --el-bg-color: #ffffff; + --el-border-radius-base: 8px; + --el-border-radius-small: 6px; +} + +body { + margin: 0; + font-family: 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif; + background: var(--el-bg-color-page); + color: #303133; +} + +/* 侧边栏 */ +.sidebar { + background: linear-gradient(180deg, #3a3226 0%, #4a4033 100%); +} + +/* 卡片柔和阴影 */ +.card-soft { + background: #fff; + border-radius: 12px; + padding: 20px; + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04); +} + +/* 手机模拟框 */ +.phone-simulator { + width: 375px; + height: 667px; + border: 3px solid #333; + border-radius: 28px; + overflow: hidden; + background: #fff; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12); +} + +.phone-status-bar { + height: 24px; + background: #fff; + display: flex; + align-items: center; + justify-content: center; + font-size: 11px; + color: #333; +} + +.phone-header { + height: 42px; + background: linear-gradient(135deg, #e67e22, #f0a04b); + color: #fff; + display: flex; + align-items: center; + justify-content: center; + font-size: 14px; + font-weight: 600; + letter-spacing: 1px; +} + +.phone-content { + height: calc(667px - 24px - 42px - 50px); + overflow-y: auto; + background: #fdf8f3; +} + +.phone-tab-bar { + height: 50px; + background: #fff; + border-top: 1px solid #f0e8df; + display: flex; + align-items: center; +} + +.phone-tab-item { + flex: 1; + text-align: center; + font-size: 10px; + color: #b0a090; + padding: 4px 0; + cursor: pointer; + transition: color 0.2s; +} + +.phone-tab-item.active { + color: #e67e22; +} + +.phone-tab-item .tab-icon { + font-size: 18px; + display: block; + margin-bottom: 2px; +} +``` + +- [ ] **Step 2: 提交** + +```bash +git add src/styles/theme.css && git commit -m "feat: add warm amber theme CSS variables" +``` + +--- + +### Task 3: 模拟数据 Store + +**文件:** +- 创建: `src/stores/index.js` + +- [ ] **Step 1: 创建 reactive store,包含完整的模拟数据和 CRUD 方法** + +```js +// src/stores/index.js +import { reactive } from 'vue' + +const now = new Date() +const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}` + +const initialProjects = [ + { id: 1, name: '长沙万科金域国际二期', location: '湖南省长沙市岳麓区', contractAmount: 1200000, prepaymentReceived: 360000, nextPaymentDate: '2026-06-15', nextPaymentAmount: 300000, outstandingBalance: 540000, status: '进行中' }, + { id: 2, name: '南昌绿地国际博览城', location: '江西省南昌市红谷滩区', contractAmount: 980000, prepaymentReceived: 300000, nextPaymentDate: '2026-06-30', nextPaymentAmount: 250000, outstandingBalance: 430000, status: '进行中' }, + { id: 3, name: '九江中梁首府', location: '江西省九江市濂溪区', contractAmount: 750000, prepaymentReceived: 750000, nextPaymentDate: '-', nextPaymentAmount: 0, outstandingBalance: 0, status: '已完工' }, + { id: 4, name: '西宁碧桂园御湖', location: '青海省西宁市城西区', contractAmount: 1500000, prepaymentReceived: 500000, nextPaymentDate: '2026-05-20', nextPaymentAmount: 400000, outstandingBalance: 600000, status: '进行中' }, + { id: 5, name: '衡阳美的梧桐庄园', location: '湖南省衡阳市蒸湘区', contractAmount: 860000, prepaymentReceived: 200000, nextPaymentDate: '2026-07-01', nextPaymentAmount: 300000, outstandingBalance: 360000, status: '筹备中' }, +] + +const initialWorkers = [ + { id: 1, name: '张建国', phone: '13812345601', assignedProjectId: 1, joinDate: '2025-03-15', status: '在岗' }, + { id: 2, name: '李大军', phone: '13812345602', assignedProjectId: 1, joinDate: '2025-04-20', status: '在岗' }, + { id: 3, name: '王永强', phone: '13812345603', assignedProjectId: 1, joinDate: '2025-05-10', status: '在岗' }, + { id: 4, name: '刘志明', phone: '13812345604', assignedProjectId: 2, joinDate: '2025-06-01', status: '在岗' }, + { id: 5, name: '陈海峰', phone: '13812345605', assignedProjectId: 2, joinDate: '2025-07-15', status: '在岗' }, + { id: 6, name: '赵大勇', phone: '13812345606', assignedProjectId: 4, joinDate: '2025-08-01', status: '在岗' }, + { id: 7, name: '周文斌', phone: '13812345607', assignedProjectId: 4, joinDate: '2025-09-10', status: '在岗' }, + { id: 8, name: '吴国栋', phone: '13812345608', assignedProjectId: 4, joinDate: '2025-10-20', status: '休假' }, + { id: 9, name: '郑小军', phone: '13812345609', assignedProjectId: 5, joinDate: '2026-01-05', status: '在岗' }, + { id: 10, name: '马洪涛', phone: '13812345610', assignedProjectId: 5, joinDate: '2026-02-15', status: '在岗' }, +] + +const initialAttendance = [ + { id: 1, workerId: 1, projectId: 1, date: '2026-05-14', morningClockIn: '07:45', eveningClockOut: '18:05', score: 10, hoursWorked: 10, dailyWage: 100, status: '正常' }, + { id: 2, workerId: 2, projectId: 1, date: '2026-05-14', morningClockIn: '07:50', eveningClockOut: '17:00', score: 9, hoursWorked: 9, dailyWage: 90, status: '正常' }, + { id: 3, workerId: 3, projectId: 1, date: '2026-05-14', morningClockIn: '08:10', eveningClockOut: '18:00', score: 8, hoursWorked: 8, dailyWage: 80, status: '迟到' }, + { id: 4, workerId: 1, projectId: 1, date: '2026-05-15', morningClockIn: '07:30', eveningClockOut: '18:10', score: 10, hoursWorked: 10, dailyWage: 100, status: '正常' }, + { id: 5, workerId: 4, projectId: 2, date: '2026-05-14', morningClockIn: '07:55', eveningClockOut: '18:00', score: 10, hoursWorked: 10, dailyWage: 100, status: '正常' }, + { id: 6, workerId: 5, projectId: 2, date: '2026-05-14', morningClockIn: '-', eveningClockOut: '-', score: 0, hoursWorked: 0, dailyWage: 0, status: '缺勤' }, + { id: 7, workerId: 6, projectId: 4, date: '2026-05-14', morningClockIn: '07:40', eveningClockOut: '18:05', score: 10, hoursWorked: 10, dailyWage: 100, status: '正常' }, +] + +const initialAdmins = [ + { id: 1, username: 'admin', role: '超级管理员', lastLogin: '2026-05-15 08:30' }, + { id: 2, username: 'zhangsan', role: '管理员', lastLogin: '2026-05-14 17:45' }, +] + +const initialLogs = [ + { id: 1, adminId: 1, action: '手动补卡', target: '李大军', detail: '2026-05-14 下班打卡补录 18:00', time: '2026-05-14 19:30' }, + { id: 2, adminId: 1, action: '删除人员', target: '临时工-孙小伟', detail: '项目完工,人员清退', time: '2026-05-13 16:00' }, + { id: 3, adminId: 2, action: '修改工时', target: '王永强', detail: '5月13日工时从8小时调整为10小时', time: '2026-05-14 09:15' }, + { id: 4, adminId: 1, action: '新增项目', target: '衡阳美的梧桐庄园', detail: '合同金额 860,000 元', time: '2026-05-10 14:00' }, +] + +let nextId = { projects: 6, workers: 11, attendance: 8, admins: 3, logs: 5 } + +export const store = reactive({ + projects: [...initialProjects], + workers: [...initialWorkers], + attendance: [...initialAttendance], + admins: [...initialAdmins], + logs: [...initialLogs], + + // 当前选中的项目(手机端项目切换用) + currentProjectId: 1, + // 当前工人(模拟已登录工人) + currentWorkerId: 1, + // GPS 模拟状态 + gpsInRange: true, + + // === 项目操作 === + addProject(project) { + this.projects.push({ ...project, id: nextId.projects++ }) + this.addLog('新增项目', project.name, `合同金额 ${project.contractAmount.toLocaleString()} 元`) + }, + updateProject(id, data) { + const idx = this.projects.findIndex(p => p.id === id) + if (idx !== -1) Object.assign(this.projects[idx], data) + }, + deleteProject(id) { + const p = this.projects.find(p => p.id === id) + this.projects = this.projects.filter(p => p.id !== id) + if (p) this.addLog('删除项目', p.name, '') + }, + + // === 工人操作 === + addWorker(worker) { + this.workers.push({ ...worker, id: nextId.workers++ }) + this.addLog('新增人员', worker.name, `分配到项目ID: ${worker.assignedProjectId}`) + }, + deleteWorker(id) { + const w = this.workers.find(w => w.id === id) + this.workers = this.workers.filter(w => w.id !== id) + if (w) this.addLog('删除人员', w.name, '人员清退') + }, + assignProject(workerId, projectId) { + const w = this.workers.find(w => w.id === workerId) + if (w) w.assignedProjectId = projectId + }, + + // === 考勤操作 === + addAttendance(record) { + this.attendance.push({ ...record, id: nextId.attendance++ }) + }, + updateAttendance(id, data) { + const idx = this.attendance.findIndex(a => a.id === id) + if (idx !== -1) { + Object.assign(this.attendance[idx], data, { status: '手动调整' }) + this.addLog('修改工时', `记录#${id}`, `调整为 ${data.hoursWorked} 小时`) + } + }, + manualClockIn(workerId, projectId, date, morning, evening) { + const hours = calculateHours(morning, evening) + const score = calculateScore(morning, evening) + this.attendance.push({ + id: nextId.attendance++, workerId, projectId, date, + morningClockIn: morning, eveningClockOut: evening, + score, hoursWorked: hours, dailyWage: score * 10, + status: '手动调整' + }) + const worker = this.workers.find(w => w.id === workerId) + this.addLog('手动补卡', worker ? worker.name : `工人#${workerId}`, `${date} ${morning}-${evening}`) + }, + + // === 日志 === + addLog(action, target, detail) { + this.logs.unshift({ + id: nextId.logs++, adminId: 1, action, target, detail, + time: new Date().toLocaleString('zh-CN', { hour12: false }) + }) + }, +}) + +function calculateHours(morning, evening) { + if (morning === '-' || evening === '-') return 0 + const mparts = morning.split(':').map(Number) + const eparts = evening.split(':').map(Number) + return eparts[0] - mparts[0] + (eparts[1] - mparts[1]) / 60 +} + +function calculateScore(morning, evening) { + const hours = calculateHours(morning, evening) + if (hours >= 10) return 10 + return Math.max(0, Math.round(hours)) +} + +/* 辅助:根据工人ID获取今日考勤 */ +export function getTodayAttendance(workerId) { + return store.attendance.find(a => a.workerId === workerId && a.date === today) +} + +/* 辅助:根据工人ID获取历史考勤 */ +export function getWorkerAttendance(workerId) { + return store.attendance.filter(a => a.workerId === workerId).sort((a, b) => b.date.localeCompare(a.date)) +} + +/* 辅助:获取项目工人数 */ +export function getProjectWorkerCount(projectId) { + return store.workers.filter(w => w.assignedProjectId === projectId).length +} + +/* 辅助:获取今日出勤统计 */ +export function getTodayStats() { + const todayRecords = store.attendance.filter(a => a.date === today) + return { + total: store.workers.filter(w => w.status === '在岗').length, + present: todayRecords.filter(r => r.status !== '缺勤').length, + rate: store.workers.filter(w => w.status === '在岗').length > 0 + ? Math.round(todayRecords.filter(r => r.status !== '缺勤').length / store.workers.filter(w => w.status === '在岗').length * 100) + : 0 + } +} +``` + +- [ ] **Step 2: 提交** + +```bash +git add src/stores/index.js && git commit -m "feat: add mock data store with CRUD helpers" +``` + +--- + +### Task 4: 共享组件 + +**文件:** +- 创建: `src/components/StatCard.vue` +- 创建: `src/components/ScoreRing.vue` +- 创建: `src/components/GpsBadge.vue` + +- [ ] **Step 1: StatCard 统计卡片** + +```vue + + + + +``` + +- [ ] **Step 2: ScoreRing 环形评分组件** + +```vue + + + + +``` + +- [ ] **Step 3: GpsBadge GPS 状态徽章** + +```vue + + + + + + +``` + +- [ ] **Step 4: 提交** + +```bash +git add src/components/ && git commit -m "feat: add StatCard, ScoreRing, GpsBadge shared components" +``` + +--- + +### Task 5: 布局组件 + 手机模拟器 + +**文件:** +- 创建: `src/layouts/PcLayout.vue` +- 创建: `src/layouts/MobileFrame.vue` +- 创建: `src/components/PhoneSimulator.vue` + +- [ ] **Step 1: PcLayout 侧边栏 + 顶部栏布局** + +```vue + + + + +``` + +- [ ] **Step 2: PhoneSimulator 手机模拟框(内置移动端页面切换)** + +```vue + + + + +``` + +- [ ] **Step 3: MobileFrame 独立移动端布局(用于直接访问 /mobile/* 路由)** + +```vue + + + + +``` + +- [ ] **Step 4: 提交** + +```bash +git add src/layouts/ src/components/PhoneSimulator.vue && git commit -m "feat: add PcLayout, MobileFrame, PhoneSimulator layouts" +``` + +--- + +### Task 6: 路由配置 + +**文件:** +- 创建: `src/router/index.js` +- 修改: `src/App.vue` + +- [ ] **Step 1: 创建路由配置** + +```js +// src/router/index.js +import { createRouter, createWebHistory } from 'vue-router' + +const routes = [ + { + path: '/', + redirect: '/pc/dashboard' + }, + { + path: '/pc/login', + name: 'PcLogin', + component: () => import('../views/pc/PcLogin.vue') + }, + { + path: '/pc', + component: () => import('../layouts/PcLayout.vue'), + children: [ + { path: 'dashboard', name: 'Dashboard', component: () => import('../views/pc/Dashboard.vue') }, + { path: 'projects', name: 'Projects', component: () => import('../views/pc/ProjectMgmt.vue') }, + { path: 'workers', name: 'Workers', component: () => import('../views/pc/WorkerMgmt.vue') }, + { path: 'attendance', name: 'Attendance', component: () => import('../views/pc/AttendanceMgmt.vue') }, + { path: 'finance', name: 'Finance', component: () => import('../views/pc/FinanceMgmt.vue') }, + { path: 'settings', name: 'Settings', component: () => import('../views/pc/SystemSettings.vue') }, + ] + }, + { + path: '/mobile/login', + name: 'MobileLogin', + component: () => import('../views/mobile/MobileLogin.vue'), + meta: { layout: 'mobile' } + }, + { + path: '/mobile/home', + name: 'MobileHome', + component: () => import('../layouts/MobileFrame.vue'), + props: { page: 'home' } + }, + { + path: '/:pathMatch(.*)*', + redirect: '/pc/dashboard' + } +] + +const router = createRouter({ + history: createWebHistory(), + routes +}) + +export default router +``` + +- [ ] **Step 2: 提交** + +```bash +git add src/router/ && git commit -m "feat: add Vue Router with PC and mobile route groups" +``` + +--- + +### Task 7: PC 登录页 + 仪表盘 + +**文件:** +- 创建: `src/views/pc/PcLogin.vue` +- 创建: `src/views/pc/Dashboard.vue` + +- [ ] **Step 1: PC 登录页** + +```vue + + + + +``` + +- [ ] **Step 2: 仪表盘首页** + +```vue + + + + +``` + +- [ ] **Step 3: 提交** + +```bash +git add src/views/pc/PcLogin.vue src/views/pc/Dashboard.vue && git commit -m "feat: add PC login page and dashboard" +``` + +--- + +### Task 8: PC 项目管理 + 人员管理 + +**文件:** +- 创建: `src/views/pc/ProjectMgmt.vue` +- 创建: `src/views/pc/WorkerMgmt.vue` + +- [ ] **Step 1: 项目管理页** + +```vue + + + + +``` + +- [ ] **Step 2: 人员管理页** + +```vue + + + + +``` + +- [ ] **Step 3: 提交** + +```bash +git add src/views/pc/ProjectMgmt.vue src/views/pc/WorkerMgmt.vue && git commit -m "feat: add PC project management and worker management pages" +``` + +--- + +### Task 9: PC 考勤管理 + 财务管理 + 系统设置 + +**文件:** +- 创建: `src/views/pc/AttendanceMgmt.vue` +- 创建: `src/views/pc/FinanceMgmt.vue` +- 创建: `src/views/pc/SystemSettings.vue` + +- [ ] **Step 1: 考勤管理页** + +```vue + + + + +``` + +- [ ] **Step 2: 财务管理页** + +```vue + + + + +``` + +- [ ] **Step 3: 系统设置页** + +```vue + + + + +``` + +- [ ] **Step 4: 提交** + +```bash +git add src/views/pc/AttendanceMgmt.vue src/views/pc/FinanceMgmt.vue src/views/pc/SystemSettings.vue && git commit -m "feat: add PC attendance, finance, system settings pages" +``` + +--- + +### Task 10: 手机端全部页面 + +**文件:** +- 创建: `src/views/mobile/MobileLogin.vue` +- 创建: `src/views/mobile/ProjectSwitch.vue` +- 创建: `src/views/mobile/MobileHome.vue` +- 创建: `src/views/mobile/ClockIn.vue` +- 创建: `src/views/mobile/PersonalRecords.vue` + +- [ ] **Step 1: 手机登录页** + +```vue + + + + + + +``` + +- [ ] **Step 2: 项目切换页** + +```vue + + + + +``` + +- [ ] **Step 3: 手机首页** + +```vue + + + + +``` + +- [ ] **Step 4: 打卡页** + +```vue + + + + +``` + +- [ ] **Step 5: 个人记录页** + +```vue + + + + +``` + +- [ ] **Step 6: 提交** + +```bash +git add src/views/mobile/ && git commit -m "feat: add all 5 mobile pages (login, project switch, home, clock-in, records)" +``` + +--- + +### Task 11: 构建验证与修复 + +- [ ] **Step 1: 运行开发服务器** + +```bash +npm run dev +``` + +检查以下页面是否正常显示: +- http://localhost:3000 自动跳转到仪表盘 +- 左侧菜单切换 6 个 PC 页面 +- 点击「手机预览」打开手机模拟框 +- 手机框内切换 4 个 Tab,各页面正常渲染 + +- [ ] **Step 2: 常见问题修复** + +修复可能的导入路径问题: +- Element Plus 图标需要从 `@element-plus/icons-vue` 导入,确保 `main.js` 中已注册 +- 所有相对路径导入确保文件存在 + +- [ ] **Step 3: 生产构建** + +```bash +npm run build +``` + +验证 `dist/` 目录包含 `index.html` 和静态资源文件。 + +```bash +npx vite preview +``` + +验证构建产物正常运行。 + +- [ ] **Step 4: 提交** + +```bash +git add -A && git commit -m "chore: final build verification and fixes" +``` + +--- + +## 验证清单 + +- [ ] `npm run dev` 无报错启动 +- [ ] PC 仪表盘统计卡片显示正确数据 +- [ ] 项目管理增删改查正常 +- [ ] 人员管理增删改查正常 +- [ ] 考勤管理:筛选、手动补卡、工时调整正常工作 +- [ ] 财务管理:汇总数据和明细表正确 +- [ ] 系统设置:三个 Tab 正常切换 +- [ ] 手机预览抽屉正常打开,375x667 模拟框展示 +- [ ] 手机底部 Tab 切换正常(首页/打卡/记录/我的) +- [ ] 手机打卡功能:点击打卡按钮生成记录,评分自动计算 +- [ ] 手机首页评分环正常显示 +- [ ] 手机个人记录统计汇总正确 +- [ ] 暖色主题生效(主色为橙色系,非默认蓝色) +- [ ] `npm run build` 构建成功 diff --git a/20260515/docs/superpowers/specs/2026-05-15-construction-management-prototype-design.md b/20260515/docs/superpowers/specs/2026-05-15-construction-management-prototype-design.md new file mode 100644 index 0000000..c864f72 --- /dev/null +++ b/20260515/docs/superpowers/specs/2026-05-15-construction-management-prototype-design.md @@ -0,0 +1,165 @@ +# 建筑数字化管理系统 - 静态页面原型设计 + +**日期**: 2026-05-15 +**来源**: 2026-05-14 AI 会议纪要 (document.txt) + +## 概述 + +面向中小建筑企业的轻量化项目数字化管理系统静态原型。覆盖考勤打卡、工时评分、项目生命周期管理、财务回款追踪四大核心模块。原型包含小程序端(手机模拟视图)和 PC 后台管理端,双端共用一套模拟数据。 + +## 技术栈 + +- **框架**: Vue 3 + Vite +- **UI 库**: Element Plus(PC 端) +- **路由**: Vue Router 4,PC 和移动端独立路由分组 +- **移动端展示**: CSS 手机模拟框,375x667 视口,移动端样式适配 +- **状态管理**: Reactive store,模拟数据内嵌,无需后端 +- **构建产物**: `vite build` 输出纯静态 HTML/CSS/JS,可部署至任意 HTTP 服务器 + +## 视觉风格 + +暖色亲和风格: +- **主色调**: 琥珀/橙色系 +- **背景**: 暖白 / 浅奶油色 +- **卡片**: 圆角、柔和阴影 +- **字体**: 无衬线体,亲和但不失专业感 +- **定位**: 以人为本、工人视角优先,区别于传统冷冰冰的企业软件 + +## 页面清单(12 页) + +### 手机端(5 页)—— 375x667 手机模拟框展示 + +| 序号 | 页面 | 核心内容 | +|------|------|----------| +| 1 | 登录页 | 手机号 + 验证码登录 | +| 2 | 项目切换 | 所属项目列表,点击切换当前项目 | +| 3 | 首页 | 项目名称/位置、今日考勤状态、评分环、快捷统计 | +| 4 | 打卡页 | GPS 定位指示、早晚打卡按钮、评分实时反馈 | +| 5 | 个人记录 | 考勤历史列表、每日工时、日薪结算 | + +移动端导航:底部 4 个 Tab(首页、打卡、记录、我的)。 + +### PC 后台端(7 页) + +| 序号 | 页面 | 核心内容 | +|------|------|----------| +| 1 | 登录页 | 账号密码登录表单 | +| 2 | 仪表盘首页 | 统计卡片(项目数、在岗工人、出勤率、回款汇总)、项目概览表格、近期动态 | +| 3 | 项目管理 | 项目列表(名称、位置、合同金额、回款进度)、新增/编辑弹窗、项目详情抽屉 | +| 4 | 人员管理 | 工人列表(姓名、电话、所属项目)、增删人员、项目分配 | +| 5 | 考勤管理 | 打卡记录表格(按日期/项目/人员筛选)、手动补卡弹窗、工时调整 | +| 6 | 财务管理 | 回款追踪表格(合同金额、预付款到账、下笔款项日期、待收余额)、回款状态标签 | +| 7 | 系统设置 | 管理员账号列表、角色配置、操作日志表、系统参数 | + +PC 导航:深色侧边栏菜单(可折叠)+ 顶部栏(用户信息、手机预览按钮)。 + +## 布局结构 + +### PC 端布局 +``` ++-------+------------------------------------+ +| | 顶部栏(用户信息、通知、手机预览) | +| 侧边 +------------------------------------+ +| 栏 | | +| 菜单 | 内容区(面包屑 + 页面内容) | +| | | ++-------+------------------------------------+ +``` + +### 手机端布局(模拟框) +``` ++------------------+ +| 状态栏 | +| 页面标题栏 | +| | +| 内容区 | +| | ++------------------+ +| 底部 Tab 栏 | ++------------------+ +``` + +### 双端切换方式 +PC 端顶部栏提供「手机预览」按钮,点击后在右侧滑出面板内展示 375x667 手机模拟框。手机预览与 PC 端共享同一套模拟数据源,操作联动。 + +## 模拟数据模型 + +所有数据以 JS 常量内嵌,CRUD 操作在内存中生效,页面刷新后重置。 + +### 项目 +``` +id, 项目名称, 项目位置, 合同金额, 预付款已收, +下笔款项日期, 下笔款项金额, 待收余额, 状态 +``` + +### 工人 +``` +id, 姓名, 手机号, 所属项目ID, 入场日期, 状态 +``` + +### 考勤记录 +``` +id, 工人ID, 项目ID, 日期, 上班打卡时间, 下班打卡时间, +评分(0-10), 工时(小时), 日薪, 状态(正常/手动调整) +``` + +### 管理员 +``` +id, 用户名, 角色(超级管理员/管理员), 最后登录时间 +``` + +## 关键设计决策 + +1. **无审批流程**: 管理员操作(手动补卡、工时调整、删除人员)直接生效。会议明确要求简化操作,不走多级审批。 + +2. **10 分制评分可视化**: 手机端首页和打卡页展示评分环。满勤 10 小时 = 10 分 = 100% 日薪。早退每小时扣 1 分,日薪按比例折算(如 100 元/天,9 分得 90 元)。 + +3. **GPS 定位为 UI 状态指示**: 打卡页展示位置状态徽章(绿色 = 在范围内,红色 = 超出范围)。实际定位逻辑用模拟开关替代,方便演示。 + +4. **项目全生命周期一表展示**: 财务管理页将合同金额、预付款、付款计划、待收余额整合在一张表中,无需多层下钻。 + +5. **模拟数据力求真实**: 使用真实感的中文建筑项目名称、工人姓名、符合会议提及规模的财务数据(百万级合同额、百人规模工人)。 + +## 组件结构 + +``` +src/ + router/ + index.js # Vue Router,PC 和移动端路由分组 + stores/ + index.js # Reactive store,模拟数据 + CRUD 方法 + views/ + pc/ + PcLogin.vue # PC 登录页 + Dashboard.vue # 仪表盘首页 + ProjectMgmt.vue # 项目管理 + WorkerMgmt.vue # 人员管理 + AttendanceMgmt.vue # 考勤管理 + FinanceMgmt.vue # 财务管理 + SystemSettings.vue # 系统设置 + mobile/ + MobileLogin.vue # 手机登录页 + ProjectSwitch.vue # 项目切换 + MobileHome.vue # 手机首页 + ClockIn.vue # 打卡页 + PersonalRecords.vue # 个人记录 + layouts/ + PcLayout.vue # PC 端侧边栏 + 顶部栏外壳 + MobileFrame.vue # 手机模拟框包装器 + components/ + ScoreRing.vue # 环形评分组件 + GpsBadge.vue # GPS 位置状态徽章 + StatCard.vue # 仪表盘统计卡片 + PhoneSimulator.vue # 375x667 手机框容器 + App.vue + main.js +``` + +## 不包含范围 + +- 真实登录认证 / Token 管理 +- 后端 API 对接 +- 真实 GPS 定位 +- 文件上传 / 文档管理 +- 数据持久化(刷新即重置) +- PC 页面的响应式适配(固定桌面宽度) diff --git a/20260515/index.html b/20260515/index.html new file mode 100644 index 0000000..b1591b1 --- /dev/null +++ b/20260515/index.html @@ -0,0 +1,12 @@ + + + + + + 建筑数字化管理系统 + + +
+ + + diff --git a/20260515/package-lock.json b/20260515/package-lock.json new file mode 100644 index 0000000..0289468 --- /dev/null +++ b/20260515/package-lock.json @@ -0,0 +1,1423 @@ +{ + "name": "construction-management-prototype", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "construction-management-prototype", + "version": "1.0.0", + "dependencies": { + "@element-plus/icons-vue": "^2.3.1", + "element-plus": "^2.7.0", + "vue": "^3.4.0", + "vue-router": "^4.3.0" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.0.0", + "vite": "^5.4.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.3", + "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@ctrl/tinycolor": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-4.2.0.tgz", + "integrity": "sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/@element-plus/icons-vue": { + "version": "2.3.2", + "resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.3.2.tgz", + "integrity": "sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==", + "license": "MIT", + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.5", + "resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.7.5.tgz", + "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.6", + "resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.7.6.tgz", + "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.5", + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.11", + "resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.11.tgz", + "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", + "license": "MIT" + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@popperjs/core": { + "name": "@sxzz/popperjs-es", + "version": "2.11.8", + "resolved": "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.8.tgz", + "integrity": "sha512-wOwESXvvED3S8xBmcPWHs2dUuzrE4XiZeFu7e1hROIJkm02a49N120pmOXxY33sBb6hArItm5W5tcg1cBtV+HQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.4.tgz", + "integrity": "sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.4.tgz", + "integrity": "sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.4.tgz", + "integrity": "sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.4.tgz", + "integrity": "sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.4.tgz", + "integrity": "sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.4.tgz", + "integrity": "sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.4.tgz", + "integrity": "sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.4.tgz", + "integrity": "sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.4.tgz", + "integrity": "sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.4.tgz", + "integrity": "sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.4.tgz", + "integrity": "sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.4.tgz", + "integrity": "sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.4.tgz", + "integrity": "sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.4.tgz", + "integrity": "sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.4.tgz", + "integrity": "sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.4.tgz", + "integrity": "sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.4.tgz", + "integrity": "sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.4.tgz", + "integrity": "sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.4.tgz", + "integrity": "sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.4.tgz", + "integrity": "sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.4.tgz", + "integrity": "sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.4.tgz", + "integrity": "sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.4.tgz", + "integrity": "sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.4.tgz", + "integrity": "sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.4.tgz", + "integrity": "sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/lodash": { + "version": "4.17.24", + "resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.24.tgz", + "integrity": "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==", + "license": "MIT" + }, + "node_modules/@types/lodash-es": { + "version": "4.17.12", + "resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz", + "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", + "license": "MIT", + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.21", + "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", + "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", + "license": "MIT" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.4", + "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.34", + "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.34.tgz", + "integrity": "sha512-s9cLyK5mLcvZ4Agva5QgRsQyLKvts9WbU9DB6NqiZkkGEdwmcEiylj5Jbwkp680drF/NNCV8OlAJSe+yMLxaJw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.3", + "@vue/shared": "3.5.34", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.34", + "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.34.tgz", + "integrity": "sha512-EbF/T++k0e2MMZlJsBhzK8Sgwt0HcIPOhzn1CTB/lv6sQcyk+OWf8YeiLxZp3ro7MbbLcAfAJ6sEvjFWuNgUCw==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.34", + "@vue/shared": "3.5.34" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.34", + "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.34.tgz", + "integrity": "sha512-D/ihr6uZeIt6r+pVZf46RWT1fAsLFMbUP7k8G1VkiiWexriED9GrX3echHd4Abbt17zjlfiFJ8z7a3BxZOPNjg==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.3", + "@vue/compiler-core": "3.5.34", + "@vue/compiler-dom": "3.5.34", + "@vue/compiler-ssr": "3.5.34", + "@vue/shared": "3.5.34", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.14", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.34", + "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.34.tgz", + "integrity": "sha512-cDtTHKibkThKGHH1SP+WdccquNRYQDFH6rRjQCqT9G2ltFAfoR5pUftpab/z+aM5mW9HLLVQW7hfKKQe/1GBeQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.34", + "@vue/shared": "3.5.34" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/@vue/reactivity": { + "version": "3.5.34", + "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.34.tgz", + "integrity": "sha512-y9XDjCEuBp+98k+UL5dbYkh57AHU4o6cxZedOPXw3bmrZZYLQsVHguGurq7hVrPCSrQtrnz1f9dssyFr+dMXfQ==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.34" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.34", + "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.34.tgz", + "integrity": "sha512-mKeBYvu8tcMSLhypAHBmriUFfWXKTCF/23Z4jiCoYK3UtWepkliViNLuR90V9XOyD62mUxs9p1jsrpK3CCGIzw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.34", + "@vue/shared": "3.5.34" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.34", + "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.34.tgz", + "integrity": "sha512-e8kZzERmCwUnBRVsgSQlAfrfU2rGoy0FFKPBXSlfEjc/O3KfA7QP0t1/2ZylrbchjmIKB4dPTd07A6WPr0eOrg==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.34", + "@vue/runtime-core": "3.5.34", + "@vue/shared": "3.5.34", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.34", + "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.34.tgz", + "integrity": "sha512-nHxmJoTrKsmrkbILRhkC9gY1G3moZbJTqCzDd7DOOzG5KH9oeJ0Unqrff5f9v0pW//jES05ZkJcNtfE8JjOIew==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.34", + "@vue/shared": "3.5.34" + }, + "peerDependencies": { + "vue": "3.5.34" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.34", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.34.tgz", + "integrity": "sha512-24uqU4OIiX29ryC3MeWid/Xf2fa2EFRUVLb77nRhk+UrTVrh/XiGtFAFmJBAtBRbjwNdsPRP+jj/OL27Eg1NDA==", + "license": "MIT" + }, + "node_modules/@vueuse/core": { + "version": "14.3.0", + "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-14.3.0.tgz", + "integrity": "sha512-aHfz47g0ZhMtTVHmIzMVpJy8ePhhOy68GY5bv110+5DVtZ+W7BsOx+m61UNQqfrWyPztIHIanWa3E2tib3NFIw==", + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "14.3.0", + "@vueuse/shared": "14.3.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/@vueuse/metadata": { + "version": "14.3.0", + "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-14.3.0.tgz", + "integrity": "sha512-BwxmbAzwAVF50+MW57GXOUEV61nFBGnlBvrTqj49PqWJu3uw7hdu72ztXeZ33RdZtDY6kO+bfCAE1PCn88Tktw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "14.3.0", + "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-14.3.0.tgz", + "integrity": "sha512-bZpge9eSXwa4ToSiqJ7j6KRwhAsneMFoSz3LMWKQDkqimm3D/tbFlrklrs/IOqC8tEcYmXQZJ6N0UrjhBirVCg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/async-validator": { + "version": "4.2.5", + "resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz", + "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==", + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/dayjs": { + "version": "1.11.20", + "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.20.tgz", + "integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==", + "license": "MIT" + }, + "node_modules/element-plus": { + "version": "2.14.0", + "resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.14.0.tgz", + "integrity": "sha512-POgH+TtoreaEKWqYYAVQyE6i8rQMEFqAEublyF29dBA5yASWPLKY6EzfeqBTr2Uv26mPss4vSrMrNPyaK7LX5w==", + "license": "MIT", + "dependencies": { + "@ctrl/tinycolor": "^4.2.0", + "@element-plus/icons-vue": "^2.3.2", + "@floating-ui/dom": "^1.0.1", + "@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.8", + "@types/lodash": "^4.17.24", + "@types/lodash-es": "^4.17.12", + "@vueuse/core": "14.3.0", + "async-validator": "^4.2.5", + "dayjs": "^1.11.20", + "lodash": "^4.18.1", + "lodash-es": "^4.18.1", + "lodash-unified": "^1.0.3", + "memoize-one": "^6.0.0", + "normalize-wheel-es": "^1.2.0", + "vue-component-type-helpers": "^3.2.8" + }, + "peerDependencies": { + "vue": "^3.3.7" + } + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.18.1", + "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", + "license": "MIT" + }, + "node_modules/lodash-unified": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/lodash-unified/-/lodash-unified-1.0.3.tgz", + "integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==", + "license": "MIT", + "peerDependencies": { + "@types/lodash-es": "*", + "lodash": "*", + "lodash-es": "*" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/normalize-wheel-es": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz", + "integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==", + "license": "BSD-3-Clause" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.14", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.60.4", + "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.60.4.tgz", + "integrity": "sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.4", + "@rollup/rollup-android-arm64": "4.60.4", + "@rollup/rollup-darwin-arm64": "4.60.4", + "@rollup/rollup-darwin-x64": "4.60.4", + "@rollup/rollup-freebsd-arm64": "4.60.4", + "@rollup/rollup-freebsd-x64": "4.60.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.4", + "@rollup/rollup-linux-arm-musleabihf": "4.60.4", + "@rollup/rollup-linux-arm64-gnu": "4.60.4", + "@rollup/rollup-linux-arm64-musl": "4.60.4", + "@rollup/rollup-linux-loong64-gnu": "4.60.4", + "@rollup/rollup-linux-loong64-musl": "4.60.4", + "@rollup/rollup-linux-ppc64-gnu": "4.60.4", + "@rollup/rollup-linux-ppc64-musl": "4.60.4", + "@rollup/rollup-linux-riscv64-gnu": "4.60.4", + "@rollup/rollup-linux-riscv64-musl": "4.60.4", + "@rollup/rollup-linux-s390x-gnu": "4.60.4", + "@rollup/rollup-linux-x64-gnu": "4.60.4", + "@rollup/rollup-linux-x64-musl": "4.60.4", + "@rollup/rollup-openbsd-x64": "4.60.4", + "@rollup/rollup-openharmony-arm64": "4.60.4", + "@rollup/rollup-win32-arm64-msvc": "4.60.4", + "@rollup/rollup-win32-ia32-msvc": "4.60.4", + "@rollup/rollup-win32-x64-gnu": "4.60.4", + "@rollup/rollup-win32-x64-msvc": "4.60.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmmirror.com/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.5.34", + "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.34.tgz", + "integrity": "sha512-WdLBG9gm02OgJIG9axd5Hpx0TFLdzVgfG2evFFu8Rur5O/IoGc5cMjnjh3tPL6GnRGsYvUhBSKVPYVcxRKpMCA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.34", + "@vue/compiler-sfc": "3.5.34", + "@vue/runtime-dom": "3.5.34", + "@vue/server-renderer": "3.5.34", + "@vue/shared": "3.5.34" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-component-type-helpers": { + "version": "3.2.9", + "resolved": "https://registry.npmmirror.com/vue-component-type-helpers/-/vue-component-type-helpers-3.2.9.tgz", + "integrity": "sha512-S3BiWYaLSzHxTpln665ELSrMR9UYmrIDUmhik7nVZxmJjTKL2/a+ew1hvGxksKelivm0ujjWfG1fYOiU/2e8rA==", + "license": "MIT" + }, + "node_modules/vue-router": { + "version": "4.6.4", + "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.6.4.tgz", + "integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + } + } +} diff --git a/20260515/package.json b/20260515/package.json new file mode 100644 index 0000000..63f49e0 --- /dev/null +++ b/20260515/package.json @@ -0,0 +1,20 @@ +{ + "name": "construction-management-prototype", + "version": "1.0.0", + "private": true, + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "vue": "^3.4.0", + "vue-router": "^4.3.0", + "element-plus": "^2.7.0", + "@element-plus/icons-vue": "^2.3.1" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.0.0", + "vite": "^5.4.0" + } +} diff --git a/20260515/src/App.vue b/20260515/src/App.vue new file mode 100644 index 0000000..98240ae --- /dev/null +++ b/20260515/src/App.vue @@ -0,0 +1,3 @@ + diff --git a/20260515/src/components/GpsBadge.vue b/20260515/src/components/GpsBadge.vue new file mode 100644 index 0000000..d2c32c6 --- /dev/null +++ b/20260515/src/components/GpsBadge.vue @@ -0,0 +1,23 @@ + + + + + diff --git a/20260515/src/components/PhoneSimulator.vue b/20260515/src/components/PhoneSimulator.vue new file mode 100644 index 0000000..cbb27e7 --- /dev/null +++ b/20260515/src/components/PhoneSimulator.vue @@ -0,0 +1,53 @@ + + + diff --git a/20260515/src/components/ScoreRing.vue b/20260515/src/components/ScoreRing.vue new file mode 100644 index 0000000..b938806 --- /dev/null +++ b/20260515/src/components/ScoreRing.vue @@ -0,0 +1,31 @@ + + + diff --git a/20260515/src/components/StatCard.vue b/20260515/src/components/StatCard.vue new file mode 100644 index 0000000..ff5cfdc --- /dev/null +++ b/20260515/src/components/StatCard.vue @@ -0,0 +1,21 @@ + + + diff --git a/20260515/src/layouts/MobileFrame.vue b/20260515/src/layouts/MobileFrame.vue new file mode 100644 index 0000000..fab5b83 --- /dev/null +++ b/20260515/src/layouts/MobileFrame.vue @@ -0,0 +1,10 @@ + + + diff --git a/20260515/src/layouts/PcLayout.vue b/20260515/src/layouts/PcLayout.vue new file mode 100644 index 0000000..36219cb --- /dev/null +++ b/20260515/src/layouts/PcLayout.vue @@ -0,0 +1,63 @@ + + + diff --git a/20260515/src/main.js b/20260515/src/main.js new file mode 100644 index 0000000..04c79d0 --- /dev/null +++ b/20260515/src/main.js @@ -0,0 +1,15 @@ +import { createApp } from 'vue' +import ElementPlus from 'element-plus' +import 'element-plus/dist/index.css' +import * as ElementPlusIconsVue from '@element-plus/icons-vue' +import App from './App.vue' +import router from './router' +import './styles/theme.css' + +const app = createApp(App) +app.use(ElementPlus) +app.use(router) +for (const [key, component] of Object.entries(ElementPlusIconsVue)) { + app.component(key, component) +} +app.mount('#app') diff --git a/20260515/src/router/index.js b/20260515/src/router/index.js new file mode 100644 index 0000000..a054905 --- /dev/null +++ b/20260515/src/router/index.js @@ -0,0 +1,47 @@ +import { createRouter, createWebHistory } from 'vue-router' + +const routes = [ + { + path: '/', + redirect: '/pc/dashboard' + }, + { + path: '/pc/login', + name: 'PcLogin', + component: () => import('../views/pc/PcLogin.vue') + }, + { + path: '/pc', + component: () => import('../layouts/PcLayout.vue'), + children: [ + { path: 'dashboard', name: 'Dashboard', component: () => import('../views/pc/Dashboard.vue') }, + { path: 'projects', name: 'Projects', component: () => import('../views/pc/ProjectMgmt.vue') }, + { path: 'workers', name: 'Workers', component: () => import('../views/pc/WorkerMgmt.vue') }, + { path: 'attendance', name: 'Attendance', component: () => import('../views/pc/AttendanceMgmt.vue') }, + { path: 'finance', name: 'Finance', component: () => import('../views/pc/FinanceMgmt.vue') }, + { path: 'settings', name: 'Settings', component: () => import('../views/pc/SystemSettings.vue') }, + ] + }, + { + path: '/mobile/login', + name: 'MobileLogin', + component: () => import('../views/mobile/MobileLogin.vue') + }, + { + path: '/mobile/home', + name: 'MobileHome', + component: () => import('../layouts/MobileFrame.vue'), + props: { page: 'home' } + }, + { + path: '/:pathMatch(.*)*', + redirect: '/pc/dashboard' + } +] + +const router = createRouter({ + history: createWebHistory(), + routes +}) + +export default router diff --git a/20260515/src/stores/index.js b/20260515/src/stores/index.js new file mode 100644 index 0000000..881b48b --- /dev/null +++ b/20260515/src/stores/index.js @@ -0,0 +1,155 @@ +import { reactive } from 'vue' + +const now = new Date() +const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}` + +const initialProjects = [ + { id: 1, name: '长沙万科金域国际二期', location: '湖南省长沙市岳麓区', contractAmount: 1200000, prepaymentReceived: 360000, nextPaymentDate: '2026-06-15', nextPaymentAmount: 300000, outstandingBalance: 540000, status: '进行中' }, + { id: 2, name: '南昌绿地国际博览城', location: '江西省南昌市红谷滩区', contractAmount: 980000, prepaymentReceived: 300000, nextPaymentDate: '2026-06-30', nextPaymentAmount: 250000, outstandingBalance: 430000, status: '进行中' }, + { id: 3, name: '九江中梁首府', location: '江西省九江市濂溪区', contractAmount: 750000, prepaymentReceived: 750000, nextPaymentDate: '-', nextPaymentAmount: 0, outstandingBalance: 0, status: '已完工' }, + { id: 4, name: '西宁碧桂园御湖', location: '青海省西宁市城西区', contractAmount: 1500000, prepaymentReceived: 500000, nextPaymentDate: '2026-05-20', nextPaymentAmount: 400000, outstandingBalance: 600000, status: '进行中' }, + { id: 5, name: '衡阳美的梧桐庄园', location: '湖南省衡阳市蒸湘区', contractAmount: 860000, prepaymentReceived: 200000, nextPaymentDate: '2026-07-01', nextPaymentAmount: 300000, outstandingBalance: 360000, status: '筹备中' }, +] + +const initialWorkers = [ + { id: 1, name: '张建国', phone: '13812345601', assignedProjectId: 1, joinDate: '2025-03-15', status: '在岗' }, + { id: 2, name: '李大军', phone: '13812345602', assignedProjectId: 1, joinDate: '2025-04-20', status: '在岗' }, + { id: 3, name: '王永强', phone: '13812345603', assignedProjectId: 1, joinDate: '2025-05-10', status: '在岗' }, + { id: 4, name: '刘志明', phone: '13812345604', assignedProjectId: 2, joinDate: '2025-06-01', status: '在岗' }, + { id: 5, name: '陈海峰', phone: '13812345605', assignedProjectId: 2, joinDate: '2025-07-15', status: '在岗' }, + { id: 6, name: '赵大勇', phone: '13812345606', assignedProjectId: 4, joinDate: '2025-08-01', status: '在岗' }, + { id: 7, name: '周文斌', phone: '13812345607', assignedProjectId: 4, joinDate: '2025-09-10', status: '在岗' }, + { id: 8, name: '吴国栋', phone: '13812345608', assignedProjectId: 4, joinDate: '2025-10-20', status: '休假' }, + { id: 9, name: '郑小军', phone: '13812345609', assignedProjectId: 5, joinDate: '2026-01-05', status: '在岗' }, + { id: 10, name: '马洪涛', phone: '13812345610', assignedProjectId: 5, joinDate: '2026-02-15', status: '在岗' }, +] + +const initialAttendance = [ + { id: 1, workerId: 1, projectId: 1, date: '2026-05-14', morningClockIn: '07:45', eveningClockOut: '18:05', score: 10, hoursWorked: 10, dailyWage: 100, status: '正常' }, + { id: 2, workerId: 2, projectId: 1, date: '2026-05-14', morningClockIn: '07:50', eveningClockOut: '17:00', score: 9, hoursWorked: 9, dailyWage: 90, status: '正常' }, + { id: 3, workerId: 3, projectId: 1, date: '2026-05-14', morningClockIn: '08:10', eveningClockOut: '18:00', score: 8, hoursWorked: 8, dailyWage: 80, status: '迟到' }, + { id: 4, workerId: 1, projectId: 1, date: '2026-05-15', morningClockIn: '07:30', eveningClockOut: '18:10', score: 10, hoursWorked: 10, dailyWage: 100, status: '正常' }, + { id: 5, workerId: 4, projectId: 2, date: '2026-05-14', morningClockIn: '07:55', eveningClockOut: '18:00', score: 10, hoursWorked: 10, dailyWage: 100, status: '正常' }, + { id: 6, workerId: 5, projectId: 2, date: '2026-05-14', morningClockIn: '-', eveningClockOut: '-', score: 0, hoursWorked: 0, dailyWage: 0, status: '缺勤' }, + { id: 7, workerId: 6, projectId: 4, date: '2026-05-14', morningClockIn: '07:40', eveningClockOut: '18:05', score: 10, hoursWorked: 10, dailyWage: 100, status: '正常' }, +] + +const initialAdmins = [ + { id: 1, username: 'admin', role: '超级管理员', lastLogin: '2026-05-15 08:30' }, + { id: 2, username: 'zhangsan', role: '管理员', lastLogin: '2026-05-14 17:45' }, +] + +const initialLogs = [ + { id: 1, adminId: 1, action: '手动补卡', target: '李大军', detail: '2026-05-14 下班打卡补录 18:00', time: '2026-05-14 19:30' }, + { id: 2, adminId: 1, action: '删除人员', target: '临时工-孙小伟', detail: '项目完工,人员清退', time: '2026-05-13 16:00' }, + { id: 3, adminId: 2, action: '修改工时', target: '王永强', detail: '5月13日工时从8小时调整为10小时', time: '2026-05-14 09:15' }, + { id: 4, adminId: 1, action: '新增项目', target: '衡阳美的梧桐庄园', detail: '合同金额 860,000 元', time: '2026-05-10 14:00' }, +] + +let nextId = { projects: 6, workers: 11, attendance: 8, admins: 3, logs: 5 } + +export const store = reactive({ + projects: [...initialProjects], + workers: [...initialWorkers], + attendance: [...initialAttendance], + admins: [...initialAdmins], + logs: [...initialLogs], + + currentProjectId: 1, + currentWorkerId: 1, + gpsInRange: true, + + addProject(project) { + this.projects.push({ ...project, id: nextId.projects++ }) + this.addLog('新增项目', project.name, `合同金额 ${project.contractAmount.toLocaleString()} 元`) + }, + updateProject(id, data) { + const idx = this.projects.findIndex(p => p.id === id) + if (idx !== -1) Object.assign(this.projects[idx], data) + }, + deleteProject(id) { + const p = this.projects.find(p => p.id === id) + this.projects = this.projects.filter(p => p.id !== id) + if (p) this.addLog('删除项目', p.name, '') + }, + + addWorker(worker) { + this.workers.push({ ...worker, id: nextId.workers++ }) + this.addLog('新增人员', worker.name, `分配到项目ID: ${worker.assignedProjectId}`) + }, + deleteWorker(id) { + const w = this.workers.find(w => w.id === id) + this.workers = this.workers.filter(w => w.id !== id) + if (w) this.addLog('删除人员', w.name, '人员清退') + }, + assignProject(workerId, projectId) { + const w = this.workers.find(w => w.id === workerId) + if (w) w.assignedProjectId = projectId + }, + + addAttendance(record) { + this.attendance.push({ ...record, id: nextId.attendance++ }) + }, + updateAttendance(id, data) { + const idx = this.attendance.findIndex(a => a.id === id) + if (idx !== -1) { + Object.assign(this.attendance[idx], data, { status: '手动调整' }) + this.addLog('修改工时', `记录#${id}`, `调整为 ${data.hoursWorked} 小时`) + } + }, + manualClockIn(workerId, projectId, date, morning, evening) { + const hours = calculateHours(morning, evening) + const score = calculateScore(morning, evening) + this.attendance.push({ + id: nextId.attendance++, workerId, projectId, date, + morningClockIn: morning, eveningClockOut: evening, + score, hoursWorked: hours, dailyWage: score * 10, + status: '手动调整' + }) + const worker = this.workers.find(w => w.id === workerId) + this.addLog('手动补卡', worker ? worker.name : `工人#${workerId}`, `${date} ${morning}-${evening}`) + }, + + addLog(action, target, detail) { + this.logs.unshift({ + id: nextId.logs++, adminId: 1, action, target, detail, + time: new Date().toLocaleString('zh-CN', { hour12: false }) + }) + }, +}) + +function calculateHours(morning, evening) { + if (morning === '-' || evening === '-') return 0 + const mparts = morning.split(':').map(Number) + const eparts = evening.split(':').map(Number) + return Math.round((eparts[0] - mparts[0] + (eparts[1] - mparts[1]) / 60) * 10) / 10 +} + +function calculateScore(morning, evening) { + const hours = calculateHours(morning, evening) + if (hours >= 10) return 10 + return Math.max(0, Math.round(hours)) +} + +export function getTodayAttendance(workerId) { + return store.attendance.find(a => a.workerId === workerId && a.date === today) +} + +export function getWorkerAttendance(workerId) { + return store.attendance.filter(a => a.workerId === workerId).sort((a, b) => b.date.localeCompare(a.date)) +} + +export function getProjectWorkerCount(projectId) { + return store.workers.filter(w => w.assignedProjectId === projectId).length +} + +export function getTodayStats() { + const totalInPost = store.workers.filter(w => w.status === '在岗').length + const todayRecords = store.attendance.filter(a => a.date === today) + const present = todayRecords.filter(r => r.status !== '缺勤').length + return { + total: totalInPost, + present, + rate: totalInPost > 0 ? Math.round(present / totalInPost * 100) : 0 + } +} diff --git a/20260515/src/styles/theme.css b/20260515/src/styles/theme.css new file mode 100644 index 0000000..8c0a6c7 --- /dev/null +++ b/20260515/src/styles/theme.css @@ -0,0 +1,108 @@ +:root { + --el-color-primary: #e67e22; + --el-color-primary-light-1: #e98e3f; + --el-color-primary-light-2: #ed9e5c; + --el-color-primary-light-3: #f0ae79; + --el-color-primary-light-4: #f4be96; + --el-color-primary-light-5: #f7ceb3; + --el-color-primary-light-6: #fadecf; + --el-color-primary-light-7: #fceeeb; + --el-color-primary-light-8: #fef8f5; + --el-color-primary-light-9: #fefcfa; + --el-color-primary-dark-1: #cf711f; + --el-color-success: #67c23a; + --el-color-warning: #e6a23c; + --el-color-danger: #f56c6c; + --el-bg-color-page: #fdf8f3; + --el-bg-color: #ffffff; + --el-border-radius-base: 8px; + --el-border-radius-small: 6px; +} + +body { + margin: 0; + font-family: 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif; + background: var(--el-bg-color-page); + color: #303133; +} + +.sidebar { + background: linear-gradient(180deg, #3a3226 0%, #4a4033 100%); +} + +.card-soft { + background: #fff; + border-radius: 12px; + padding: 20px; + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04); +} + +.phone-simulator { + width: 375px; + height: 667px; + border: 3px solid #333; + border-radius: 28px; + overflow: hidden; + background: #fff; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12); +} + +.phone-status-bar { + height: 24px; + background: #fff; + display: flex; + align-items: center; + justify-content: center; + font-size: 11px; + color: #333; +} + +.phone-header { + height: 42px; + background: linear-gradient(135deg, #e67e22, #f0a04b); + color: #fff; + display: flex; + align-items: center; + justify-content: center; + font-size: 14px; + font-weight: 600; + letter-spacing: 1px; +} + +.phone-content { + height: calc(667px - 24px - 42px - 50px); + overflow-y: auto; + background: #fdf8f3; +} + +.phone-tab-bar { + height: 50px; + background: #fff; + border-top: 1px solid #f0e8df; + display: flex; + align-items: center; +} + +.phone-tab-item { + flex: 1; + text-align: center; + font-size: 10px; + color: #b0a090; + padding: 4px 0; + cursor: pointer; + transition: color 0.2s; +} + +.phone-tab-item.active { + color: #e67e22; +} + +.phone-tab-item .tab-icon { + display: block; + margin-bottom: 2px; + line-height: 1; +} +.phone-tab-item .tab-icon svg { + width: 18px; + height: 18px; +} diff --git a/20260515/src/views/mobile/ClockIn.vue b/20260515/src/views/mobile/ClockIn.vue new file mode 100644 index 0000000..96d2929 --- /dev/null +++ b/20260515/src/views/mobile/ClockIn.vue @@ -0,0 +1,67 @@ + + + diff --git a/20260515/src/views/mobile/MobileHome.vue b/20260515/src/views/mobile/MobileHome.vue new file mode 100644 index 0000000..5508cfd --- /dev/null +++ b/20260515/src/views/mobile/MobileHome.vue @@ -0,0 +1,37 @@ + + + diff --git a/20260515/src/views/mobile/MobileLogin.vue b/20260515/src/views/mobile/MobileLogin.vue new file mode 100644 index 0000000..752086f --- /dev/null +++ b/20260515/src/views/mobile/MobileLogin.vue @@ -0,0 +1,32 @@ + + + + + diff --git a/20260515/src/views/mobile/PersonalRecords.vue b/20260515/src/views/mobile/PersonalRecords.vue new file mode 100644 index 0000000..e1e36b0 --- /dev/null +++ b/20260515/src/views/mobile/PersonalRecords.vue @@ -0,0 +1,51 @@ + + + diff --git a/20260515/src/views/mobile/ProjectSwitch.vue b/20260515/src/views/mobile/ProjectSwitch.vue new file mode 100644 index 0000000..5260fda --- /dev/null +++ b/20260515/src/views/mobile/ProjectSwitch.vue @@ -0,0 +1,19 @@ + + + diff --git a/20260515/src/views/pc/AttendanceMgmt.vue b/20260515/src/views/pc/AttendanceMgmt.vue new file mode 100644 index 0000000..7c5155d --- /dev/null +++ b/20260515/src/views/pc/AttendanceMgmt.vue @@ -0,0 +1,145 @@ + + + diff --git a/20260515/src/views/pc/Dashboard.vue b/20260515/src/views/pc/Dashboard.vue new file mode 100644 index 0000000..431e4ba --- /dev/null +++ b/20260515/src/views/pc/Dashboard.vue @@ -0,0 +1,54 @@ + + + diff --git a/20260515/src/views/pc/FinanceMgmt.vue b/20260515/src/views/pc/FinanceMgmt.vue new file mode 100644 index 0000000..cb5c648 --- /dev/null +++ b/20260515/src/views/pc/FinanceMgmt.vue @@ -0,0 +1,65 @@ + + + diff --git a/20260515/src/views/pc/PcLogin.vue b/20260515/src/views/pc/PcLogin.vue new file mode 100644 index 0000000..eac0dce --- /dev/null +++ b/20260515/src/views/pc/PcLogin.vue @@ -0,0 +1,38 @@ + + + diff --git a/20260515/src/views/pc/ProjectMgmt.vue b/20260515/src/views/pc/ProjectMgmt.vue new file mode 100644 index 0000000..c94e6aa --- /dev/null +++ b/20260515/src/views/pc/ProjectMgmt.vue @@ -0,0 +1,101 @@ + + + diff --git a/20260515/src/views/pc/SystemSettings.vue b/20260515/src/views/pc/SystemSettings.vue new file mode 100644 index 0000000..e40889d --- /dev/null +++ b/20260515/src/views/pc/SystemSettings.vue @@ -0,0 +1,56 @@ + + + diff --git a/20260515/src/views/pc/WorkerMgmt.vue b/20260515/src/views/pc/WorkerMgmt.vue new file mode 100644 index 0000000..9977b5a --- /dev/null +++ b/20260515/src/views/pc/WorkerMgmt.vue @@ -0,0 +1,93 @@ + + + diff --git a/20260515/vite.config.js b/20260515/vite.config.js new file mode 100644 index 0000000..bcefbcd --- /dev/null +++ b/20260515/vite.config.js @@ -0,0 +1,10 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +export default defineConfig({ + plugins: [vue()], + server: { + port: 3000, + open: true + } +})