This commit is contained in:
ch197511161
2025-12-11 01:00:25 +08:00
commit acb0b763db
12 changed files with 12993 additions and 0 deletions

22
.env Normal file
View File

@@ -0,0 +1,22 @@
# 全局环境变量配置
# Coze API 配置
COZE_API_TOKEN=pat_7VvJA4an8IbsjmID9SasNBZyVDCJ3NamxwBOBdDxfUQEnGOYGaQf3odBUoibO0aF
COZE_DEFAULT_WORKFLOW_ID=7554970254934605859
# Feishu API 配置
FEISHU_APP_ID=cli_a62aaf3d9d385013
FEISHU_APP_SECRET=VH46dJI4T4bbUD7rI9J6Se2EI5rWVpBf
# Coze OAuth 配置
COZE_CLIENT_ID=27608009841040583568491680329534.app.coze
COZE_CLIENT_SECRET=Bx7ctHOQlzWVdoaQnSYJgRPwlK64IUuhi4oPnRO3OyJ5K4yc
# 数据库配置
DATABASE_URL=file:./dev.db
# 其他配置
TOKEN_REFRESH_SCHEDULER_ENABLED=true
NUXT_PUBLIC_API_BASE=
# Server 配置
NUXT_DEV_PORT=8080

24
.env.example Normal file
View File

@@ -0,0 +1,24 @@
# 环境变量配置示例
# 复制此文件为 .env 并修改为你的实际配置
# Coze API 配置
COZE_API_TOKEN=your_coze_api_token_here
COZE_DEFAULT_WORKFLOW_ID=your_default_workflow_id_here
# Feishu API 配置
FEISHU_APP_ID=your_feishu_app_id_here
FEISHU_APP_SECRET=your_feishu_app_secret_here
# Coze OAuth 配置
COZE_CLIENT_ID=your_coze_client_id_here
COZE_CLIENT_SECRET=your_coze_client_secret_here
# 数据库配置
DATABASE_URL=file:./dev.db
# 其他配置
TOKEN_REFRESH_SCHEDULER_ENABLED=true
NUXT_PUBLIC_API_BASE=
# Server 配置
NUXT_DEV_PORT=8080

3
.npmrc Normal file
View File

@@ -0,0 +1,3 @@
shamefully-hoist=true
public-hoist-pattern[]=*@element-plus*
public-hoist-pattern[]=*dayjs*

11
.prettierignore Normal file
View File

@@ -0,0 +1,11 @@
# 忽略不需要格式化的文件
node_modules/
dist/
.nuxt/
.output/
*.log
*.min.js
*.min.css
.env*
prisma/migrations/
.DS_Store

12
.prettierrc Normal file
View File

@@ -0,0 +1,12 @@
{
"semi": false,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 100,
"arrowParens": "avoid",
"endOfLine": "lf",
"bracketSpacing": true,
"jsxBracketSameLine": false,
"proseWrap": "preserve"
}

94
Nuxt4Curd.code-workspace Normal file
View File

@@ -0,0 +1,94 @@
{
"folders": [
{
"name": "Nuxt4Curd - App",
"path": "./app"
},
{
"name": "Nuxt4Curd - Root",
"path": "."
}
],
"settings": {
"tailwindCSS.experimental.configFile": "./tailwind.config.js",
"tailwindCSS.cssPath": "./assets/css/main.css",
"tailwindCSS.includeLanguages": {
"vue": "html",
"javascript": "javascript",
"typescript": "typescript",
"vue-html": "html",
"plaintext": "css",
"markdown": "html"
},
"tailwindCSS.experimental.classRegex": [
["class:\\s*?[\"'`]([^\"'`]*).*?[\"'`]", "[\"'`]([^\"'`]*)[\"'`]"],
["className:\\s*?[\"'`]([^\"'`]*).*?[\"'`]", "[\"'`]([^\"'`]*)[\"'`]"],
["\\bclass\\s*=\\s*[\"']([^\"']*)[\"']", "([^\"']*)"],
["\\bclassName\\s*=\\s*[\"']([^\"']*)[\"']", "([^\"']*)"],
["class=\"([^\"]*)\"", "([^\"]*)"],
["class='([^']*)'", "([^']*)"],
["class=`([^`]*)`", "([^`]*)"],
["@apply\\s+([^;]*)", 1],
["@layer utilities\\s*{[^}]*?\\.([^\\s{]+)", 1],
["tw`([^`]*)`", 1],
["tw=\"([^\"]*)\"", 1],
["cva\\(([^\\)]*)\\)", 1],
["cx\\(([^\\)]*)\\)", 1],
["cn\\(([^\\)]*)\\)", 1]
],
"tailwindCSS.validate": true,
"tailwindCSS.suggestions": true,
"tailwindCSS.hovers": true,
"tailwindCSS.codeActions": true,
"tailwindCSS.experimental.classSorting": true,
"tailwindCSS.colorDecorators": true,
"tailwindCSS.showPixelEquivalents": true,
"tailwindCSS.rootFontSize": 16,
"tailwindCSS.experimental.classAttributes": ["class", "className", "ngClass"],
"typescript.preferences.importModuleSpecifier": "relative",
"typescript.suggest.autoImports": true,
"typescript.suggest.completeFunctionCalls": true,
"typescript.inlayHints.parameterNames.enabled": "all",
"typescript.inlayHints.parameterTypes.enabled": true,
"typescript.inlayHints.variableTypes.enabled": true,
"files.associations": {
"*.css": "tailwindcss",
"main.css": "tailwindcss"
},
"css.customData": [".vscode/css_custom_data.json"],
"css.validate": false,
"tailwindCSS.includeLanguages": {
"css": "css",
"scss": "scss",
"less": "less",
"postcss": "css"
},
"emmet.includeLanguages": {
"vue-html": "html",
"vue": "html",
"javascript": "javascriptreact",
"typescript": "typescriptreact"
},
"css.validate": true,
"css.completion.completePropertyWithSemicolon": true,
"css.completion.triggerPropertyValueCompletion": true,
"cssPeek.enable": true,
"cssPeek.enableGotoDefinition": true,
"cssPeek.enableHover": true,
"cssPeek.enableShowReferences": true,
"cssPeek.enableShowReferencesInPeek": true,
"cssPeek.enableShowDefinitionInPeek": true,
"cssPeek.enableShowQuickInfo": true,
"cssPeek.enableColors": true,
"cssPeek.enableFiles": ["css", "scss", "less", "vue", "html"],
"nuxt.isNuxtApp": true
},
"extensions": {
"recommendations": [
"bradlc.vscode-tailwindcss",
"austenc.tailwind-docs",
"Vue.volar",
"Vue.vscode-typescript-vue-plugin"
]
}
}

View File

@@ -0,0 +1,223 @@
<!--
PowerStation/index.vue
电站设备管理系统主页面
使用网格布局展示3D模型视图数据图表和属性面板
-->
<template>
<div
class="w-full h-screen flex flex-col overflow-hidden bg-[#0B1120] font-sans powerstation-scrollbar"
>
<!-- ============================================================
头部导航
============================================================ -->
<HeaderNavigation />
<!-- ============================================================
主要内容区域
============================================================ -->
<div class="flex-1 flex justify-start relative min-h-0">
<!-- 左侧面板模型结构树 -->
<ModelTreePanel class="w-[280px] h-full z-50 self-stretch flex-shrink-0" />
<!-- 右侧面板主内容区 -->
<div class="flex-1 transition-all duration-300">
<div
class="h-full bg-[#020617] p-2 gap-2 overflow-hidden grid transition-all duration-300 min-h-0"
:class="[
maximizedComponent
? 'grid-cols-1 grid-rows-1'
: 'grid-cols-[1fr_30%] grid-rows-[70%_30%]',
]"
>
<!-- 3D视图 - 左上区域 -->
<Model3DView
class="bg-[#0b1120] border border-cyan-500/20 rounded shadow-[0_0_10px_rgba(6,182,212,0.05)] transition-all duration-300 min-h-0"
:class="[
maximizedComponent === '3d'
? 'col-span-full row-span-full'
: maximizedComponent
? 'hidden'
: 'col-start-1 row-start-1',
]"
:is-maximized="maximizedComponent === '3d'"
@toggle-maximize="toggleMaximize('3d')"
/>
<!-- 数据图表 - 左下区域 -->
<DataChartsSection
class="bg-[#0b1120] border border-cyan-500/20 rounded shadow-[0_0_10px_rgba(6,182,212,0.05)] overflow-hidden transition-all duration-300 min-h-0"
:class="[
maximizedComponent === 'charts'
? 'col-span-full row-span-full'
: maximizedComponent
? 'hidden'
: 'col-start-1 row-start-2',
]"
:is-maximized="maximizedComponent === 'charts'"
@toggle-maximize="toggleMaximize('charts')"
/>
<!-- 属性面板 - 右侧区域跨两行 -->
<ModelPropertiesPanel
class="bg-[#0b1120] border border-cyan-500/20 rounded shadow-[0_0_10px_rgba(6,182,212,0.05)] overflow-hidden relative transition-all duration-300 border-l border-cyan-500/20"
:class="[
maximizedComponent === 'props'
? 'col-span-full row-span-full'
: maximizedComponent
? 'hidden'
: 'col-start-2 row-span-2',
]"
:is-maximized="maximizedComponent === 'props'"
@toggle-maximize="toggleMaximize('props')"
/>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
/**
* PowerStation 主页面
* 电站设备管理系统的核心页面
* 集成3D视图、数据图表和属性面板
*/
import DataChartsSection from '~/components/PowerStation/DataChartsSection.vue'
import HeaderNavigation from '~/components/PowerStation/HeaderNavigation.vue'
import Model3DView from '~/components/PowerStation/Model3DView.vue'
import ModelPropertiesPanel from '~/components/PowerStation/ModelPropertiesPanel.vue'
import ModelTreePanel from '~/components/PowerStation/ModelTreePanel.vue'
// ============================================================
// 页面配置
// ============================================================
definePageMeta({
layout: false, // 使用全屏自定义布局
})
// ============================================================
// 状态和引用
// ============================================================
const route = useRoute()
const store = usePowerStationStore()
/** 当前最大化的组件 */
const maximizedComponent = ref<string | null>(null)
// ============================================================
// 方法
// ============================================================
/**
* 切换组件最大化状态
* @param component - 组件标识('3d' | 'charts' | 'props'
*/
const toggleMaximize = (component: string) => {
if (maximizedComponent.value === component) {
maximizedComponent.value = null // 还原
} else {
maximizedComponent.value = component // 最大化指定组件
}
}
// ============================================================
// 路由同步
// ============================================================
// 监听路由变化确保store与路由参数同步
watch(
() => route.query.currentNodeId,
newId => {
if (newId && typeof newId === 'string') {
// 只在完整模式下同步
if (store.loadMode && store.currentNodeId !== newId) {
console.log('PowerStation页面同步路由currentNodeId到store:', newId)
store.currentNodeId = newId
}
}
},
{ immediate: true }
)
// ============================================================
// 生命周期
// ============================================================
onMounted(() => {
// 保留路由参数检查,供后续扩展使用
const currentNodeId = route.query.currentNodeId as string
if (currentNodeId) {
// 节点选择逻辑由子组件处理
}
})
</script>
<style>
/* ============================================================
页面全局样式
============================================================ */
body {
margin: 0;
overflow: hidden;
}
/* ============================================================
全局滚动条样式
============================================================ */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: rgba(15, 23, 42, 0.3);
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: rgba(34, 211, 238, 0.3);
border-radius: 4px;
border: 1px solid rgba(34, 211, 238, 0.1);
}
::-webkit-scrollbar-thumb:hover {
background: rgba(34, 211, 238, 0.5);
box-shadow: 0 0 8px rgba(34, 211, 238, 0.3);
}
::-webkit-scrollbar-corner {
background: rgba(15, 23, 42, 0.3);
}
/* Firefox滚动条样式 */
* {
scrollbar-width: thin;
scrollbar-color: rgba(34, 211, 238, 0.3) rgba(15, 23, 42, 0.3);
}
/* ============================================================
PowerStation页面特殊滚动条样式
============================================================ */
.powerstation-scrollbar ::-webkit-scrollbar {
width: 6px;
height: 6px;
}
.powerstation-scrollbar ::-webkit-scrollbar-track {
background: rgba(11, 17, 32, 0.5);
}
.powerstation-scrollbar ::-webkit-scrollbar-thumb {
background: rgba(6, 182, 212, 0.4);
border-radius: 3px;
}
.powerstation-scrollbar ::-webkit-scrollbar-thumb:hover {
background: rgba(6, 182, 212, 0.6);
}
</style>

67
package.json Normal file
View File

@@ -0,0 +1,67 @@
{
"name": "@nuxt4crud/monorepo",
"version": "1.0.0",
"description": "Nuxt 4 CRUD 应用 - 前后端分离架构",
"private": true,
"type": "module",
"workspaces": [
"shared",
"app",
"server"
],
"scripts": {
"dev": "concurrently \"pnpm run dev:shared\" \"pnpm run dev:app\"",
"dev:shared": "pnpm -F @nuxt4crud/shared dev",
"dev:app": "pnpm -F @nuxt4crud/app dev",
"build": "pnpm run build:shared && pnpm run build:app",
"build:shared": "pnpm -F @nuxt4crud/shared build",
"build:app": "pnpm -F @nuxt4crud/app build",
"build:prod": "chmod +x build-and-deploy.sh && ./build-and-deploy.sh",
"build:prod:win": "build-and-deploy.bat",
"start": "pnpm -F @nuxt4crud/app start",
"start:prod": "cd app/.output && node server/index.mjs",
"start:prod:win": "cd app/.output && node server/index.mjs",
"clean": "pnpm -F @nuxt4crud/shared clean && pnpm -F @nuxt4crud/app clean",
"clean:all": "rimraf app/.nuxt app/.output app/dist shared/dist node_modules",
"format": "prettier --write .",
"format:check": "prettier --check .",
"lint": "pnpm -F @nuxt4crud/app lint",
"lint:fix": "pnpm -F @nuxt4crud/app lint:fix",
"type-check": "pnpm -F @nuxt4crud/shared type-check && pnpm -F @nuxt4crud/app type-check",
"test": "echo \"No tests specified\"",
"postinstall": "pnpm run build:shared && pnpm -F @nuxt4crud/server prisma:generate",
"setup": "pnpm install && pnpm run build:shared && pnpm run prisma:migrate",
"prisma:migrate": "pnpm -F @nuxt4crud/server prisma:migrate",
"prisma:studio": "pnpm -F @nuxt4crud/server prisma:studio",
"seed": "node scripts/simple-seed.js",
"check-db": "node scripts/check-users.js",
"test:env": "node -e \"require('dotenv').config({path: '.env'}); console.log('环境变量测试:'); console.log('FEISHU_APP_ID:', process.env.FEISHU_APP_ID); console.log('COZE_API_TOKEN:', process.env.COZE_API_TOKEN ? '已设置' : '未设置'); console.log('DATABASE_URL:', process.env.DATABASE_URL);\""
},
"devDependencies": {
"@nuxt4crud/prettier-config": "file:./prettier-config",
"@types/node": "^20.17.6",
"axios": "^1.13.2",
"concurrently": "^8.2.2",
"dotenv": "^17.2.3",
"prettier": "^3.3.3",
"rimraf": "^5.0.10",
"typescript": "^5.6.3"
},
"engines": {
"node": ">=18.0.0",
"pnpm": ">=8.0.0"
},
"keywords": [
"nuxt4",
"crud",
"monorepo",
"typescript",
"prisma"
],
"author": "Your Name",
"license": "MIT",
"dependencies": {
"@pinia/nuxt": "^0.11.3",
"pinia": "^3.0.4"
}
}

12495
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

5
pnpm-workspace.yaml Normal file
View File

@@ -0,0 +1,5 @@
packages:
- 'shared'
- 'app'
- 'server'
- 'prettier-config'

13
prisma.config.ts Normal file
View File

@@ -0,0 +1,13 @@
import 'dotenv/config'
import { defineConfig, env } from 'prisma/config'
export default defineConfig({
schema: 'prisma/schema.prisma',
migrations: {
path: 'prisma/migrations',
},
engine: 'classic',
datasource: {
url: env('DATABASE_URL'),
},
})

24
tsconfig.base.json Normal file
View File

@@ -0,0 +1,24 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"allowImportingTsExtensions": false,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"declaration": true,
"declarationMap": true,
"removeComments": false,
"sourceMap": true,
"resolveJsonModule": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true
}
}