first push
|
|
@ -0,0 +1,9 @@
|
|||
[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue}]
|
||||
charset = utf-8
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
end_of_line = lf
|
||||
max_line_length = 100
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# 本地环境
|
||||
VITE_APP_TITLE=瓷福隧道窑系统
|
||||
VITE_API_BASE_URL=http://10.0.11.74:60309/api
|
||||
VITE_APP_BASEURL=http://10.0.11.74:60309
|
||||
# 本地端口
|
||||
VITE_APP_PORT=2800
|
||||
# 是否开启代理
|
||||
VITE_APP_PROXY=true
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# 生产环境
|
||||
VITE_APP_TITLE=瓷福隧道窑系统
|
||||
VITE_API_BASE_URL=http://10.0.11.74:60309/api
|
||||
VITE_APP_BASEURL=http://10.0.11.74:60309
|
||||
# 本地端口
|
||||
VITE_APP_PORT=2800
|
||||
# 是否开启代理
|
||||
VITE_APP_PROXY=true
|
||||
|
|
@ -0,0 +1 @@
|
|||
* text=auto eol=lf
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
*.tsbuildinfo
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"CurrentProjectSetting": null
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"ExpandedNodes": [
|
||||
"",
|
||||
"\\src-tauri",
|
||||
"\\src-tauri\\capabilities"
|
||||
],
|
||||
"SelectedNode": "\\src-tauri\\capabilities\\default.json",
|
||||
"PreviewInSolutionExplorer": false
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"Version": 1,
|
||||
"WorkspaceRootPath": "D:\\testProjects\\yaodaoxiangmu\\vite_vue3_tsproject_change\\",
|
||||
"Documents": [],
|
||||
"DocumentGroupContainers": [
|
||||
{
|
||||
"Orientation": 0,
|
||||
"VerticalTabListWidth": 256,
|
||||
"DocumentGroups": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"recommendations": [
|
||||
"Vue.volar",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"EditorConfig.EditorConfig",
|
||||
"aminer.codegeex"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
# vue-project
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
|
||||
|
||||
## Type Support for `.vue` Imports in TS
|
||||
|
||||
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
|
||||
|
||||
## Customize configuration
|
||||
|
||||
See [Vite Configuration Reference](https://vite.dev/config/).
|
||||
|
||||
## Project Setup
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compile and Hot-Reload for Development
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Type-Check, Compile and Minify for Production
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Lint with [ESLint](https://eslint.org/)
|
||||
|
||||
```sh
|
||||
npm run lint
|
||||
```
|
||||
|
|
@ -0,0 +1,378 @@
|
|||
# Coal Feeding Layout Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Replace the right-side `<table>` in `src/views/coalFeeding.vue` with a block-based unit-column layout while preserving the current row/column visual structure and existing interactions.
|
||||
|
||||
**Architecture:** Keep all business state and handlers in `src/views/coalFeeding.vue`, but reorganize the right-side rendering around a fixed parameter label column plus horizontally scrollable unit columns. Use small in-file render configurations for upper rows, lower rows, and fixed-height visual blocks so the new block layout stays aligned with the former table structure.
|
||||
|
||||
**Tech Stack:** Vue 3 `<script setup lang="ts">`, existing CSS utility classes, `@opentiny/vue`, Vite build verification
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Prepare In-File Layout Metadata
|
||||
|
||||
**Files:**
|
||||
- Modify: `D:\testProjects\yaodaoxiangmu\vite_vue3_tsproject_change\src\views\coalFeeding.vue`
|
||||
|
||||
- [ ] **Step 1: Add typed row metadata for the upper and lower time-setting blocks**
|
||||
|
||||
Insert these constants near the existing type declarations in `src/views/coalFeeding.vue` so the new template can loop over block rows instead of repeating hard-coded table rows:
|
||||
|
||||
```ts
|
||||
type TimeFieldKey =
|
||||
| "tmyxsds"
|
||||
| "tmtzsds"
|
||||
| "tmljsds"
|
||||
| "blyxsds"
|
||||
| "tmyxsdx"
|
||||
| "tmtzsdx"
|
||||
| "tmljsdx"
|
||||
| "blyxsdx";
|
||||
|
||||
type DisplayFieldKey =
|
||||
| "tmljsjs"
|
||||
| "tmljsjx"
|
||||
| "tempera1"
|
||||
| "tempera2"
|
||||
| "carNumber";
|
||||
|
||||
type BlockRowConfig = {
|
||||
key: TimeFieldKey | DisplayFieldKey;
|
||||
label: string;
|
||||
kind: "time" | "display";
|
||||
idPrefix?: string;
|
||||
};
|
||||
|
||||
const upperBlockRows: BlockRowConfig[] = [
|
||||
{ key: "tmyxsds", label: "投煤运行时间设定", kind: "time", idPrefix: "tmyxsds" },
|
||||
{ key: "tmtzsds", label: "投煤停止时间设定", kind: "time", idPrefix: "tmtzsds" },
|
||||
{ key: "tmljsds", label: "投煤累积时间设定", kind: "time", idPrefix: "tmljsds" },
|
||||
{ key: "blyxsds", label: "布料运行时间设定", kind: "time", idPrefix: "blyxsds" },
|
||||
{ key: "tmljsjs", label: "投煤累积时间", kind: "display", idPrefix: "tmljsjs" },
|
||||
];
|
||||
|
||||
const lowerBlockRows: BlockRowConfig[] = [
|
||||
{ key: "tmyxsdx", label: "投煤运行时间设定", kind: "time", idPrefix: "tmyxsdx" },
|
||||
{ key: "tmtzsdx", label: "投煤停止时间设定", kind: "time", idPrefix: "tmtzsdx" },
|
||||
{ key: "tmljsdx", label: "投煤累积时间设定", kind: "time", idPrefix: "tmljsdx" },
|
||||
{ key: "blyxsdx", label: "布料运行时间设定", kind: "time", idPrefix: "blyxsdx" },
|
||||
{ key: "tmljsjx", label: "投煤累积时间", kind: "display", idPrefix: "tmljsjx" },
|
||||
];
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Add typed helpers for rendering ids and editable values**
|
||||
|
||||
Add these helpers below the row metadata so the template can render the new block layout without string duplication:
|
||||
|
||||
```ts
|
||||
const getCellId = (prefix: string | undefined, index: number) => {
|
||||
return prefix ? `${prefix}${index}` : undefined;
|
||||
};
|
||||
|
||||
const getTempDropId = (side: "tempera1" | "tempera2", item: any, index: number) => {
|
||||
return `${side}_unit${item.unit}_${index + 1}`;
|
||||
};
|
||||
|
||||
const isTimeRow = (row: BlockRowConfig) => row.kind === "time";
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Run a quick type sanity scan before touching the template**
|
||||
|
||||
Run: `rg -n "upperBlockRows|lowerBlockRows|getCellId|getTempDropId|isTimeRow" src\views\coalFeeding.vue`
|
||||
|
||||
Expected: five matches for the newly added metadata/helpers, all inside `src/views\coalFeeding.vue`
|
||||
|
||||
### Task 2: Replace the Table Template With a Column Layout
|
||||
|
||||
**Files:**
|
||||
- Modify: `D:\testProjects\yaodaoxiangmu\vite_vue3_tsproject_change\src\views\coalFeeding.vue`
|
||||
|
||||
- [ ] **Step 1: Replace the `<table>` wrapper with a block-based layout shell**
|
||||
|
||||
Replace the current `<table>` section with this outer structure inside the existing right-side panel container:
|
||||
|
||||
```vue
|
||||
<div class="control-panel">
|
||||
<div class="control-panel__labels">
|
||||
<div class="control-panel__corner"></div>
|
||||
<div class="control-panel__section">
|
||||
<div
|
||||
v-for="row in upperBlockRows"
|
||||
:key="`upper-label-${row.key}`"
|
||||
class="control-panel__label control-panel__label--row"
|
||||
>
|
||||
{{ row.label }}
|
||||
</div>
|
||||
<div class="control-panel__label control-panel__label--motor">中间侧</div>
|
||||
</div>
|
||||
<div class="control-panel__label control-panel__label--temp">2侧温度</div>
|
||||
<div class="control-panel__label control-panel__label--car">车位号</div>
|
||||
<div class="control-panel__label control-panel__label--temp">1侧温度</div>
|
||||
<div class="control-panel__section">
|
||||
<div
|
||||
v-for="row in lowerBlockRows"
|
||||
:key="`lower-label-${row.key}`"
|
||||
class="control-panel__label control-panel__label--row"
|
||||
>
|
||||
{{ row.label }}
|
||||
</div>
|
||||
<div class="control-panel__label control-panel__label--motor">回车侧</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-panel__columns">
|
||||
<!-- unit columns render here -->
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Render each unit as one vertical column**
|
||||
|
||||
Inside `control-panel__columns`, render one column per `dataList` item using this structure:
|
||||
|
||||
```vue
|
||||
<div
|
||||
v-for="(item, index) in dataList"
|
||||
:key="item.team"
|
||||
class="control-panel__column"
|
||||
>
|
||||
<div class="control-panel__column-header">
|
||||
<span class="inline_flex items_center justify_center h1_25 min_w_6 px0_375 rounded bg_primary text_primary_color fontw700 text_11">
|
||||
{{ item.team }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="control-panel__section">
|
||||
<!-- upper rows -->
|
||||
</div>
|
||||
|
||||
<div
|
||||
:id="getTempDropId('tempera2', item, index)"
|
||||
class="control-panel__cell control-panel__cell--temp"
|
||||
@dragover="onDjDragOver"
|
||||
@drop="onDjDrop($event, item, getTempDropId('tempera2', item, index))"
|
||||
>
|
||||
<span class="text_xs fontw700 min_w_2_25 text_right text_red">{{ item.tempera2 }}</span>
|
||||
<span> °C</span>
|
||||
</div>
|
||||
|
||||
<div class="control-panel__cell control-panel__cell--car">
|
||||
<span class="inline_flex items_center justify_center h1_25 min_w_1_75 px0_375 rounded_md bg_primary text_primary_color my0_3 fontw700 text_sm shadow_sm">
|
||||
{{ item.carNumber }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
:id="getTempDropId('tempera1', item, index)"
|
||||
class="control-panel__cell control-panel__cell--temp"
|
||||
@dragover="onDjDragOver"
|
||||
@drop="onDjDrop($event, item, getTempDropId('tempera1', item, index))"
|
||||
>
|
||||
<span class="text_xs fontw700 min_w_2_25 text_right text_red">{{ item.tempera1 }}</span>
|
||||
<span> °C</span>
|
||||
</div>
|
||||
|
||||
<div class="control-panel__section">
|
||||
<!-- lower rows -->
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Render the upper and lower time/display rows from metadata**
|
||||
|
||||
Use this pattern in both the upper and lower sections so editable rows and display rows remain consistent:
|
||||
|
||||
```vue
|
||||
<div
|
||||
v-for="row in upperBlockRows"
|
||||
:key="`upper-${item.team}-${row.key}`"
|
||||
:id="getCellId(row.idPrefix, index)"
|
||||
class="control-panel__cell control-panel__cell--row"
|
||||
>
|
||||
<span v-if="!isTimeRow(row) || item.statusAuto" class="px0_25 py0_125 text_center text_xs color_base">
|
||||
{{ item[row.key] }}
|
||||
</span>
|
||||
<input
|
||||
v-else
|
||||
v-model="item[row.key]"
|
||||
class="px0_25 py0_125 text_center text_xs color_base control-panel__input"
|
||||
/>
|
||||
</div>
|
||||
```
|
||||
|
||||
For the lower section, switch `upperBlockRows` to `lowerBlockRows` and keep the same markup.
|
||||
|
||||
- [ ] **Step 4: Move the middle-side and return-side motor rendering into the new sections**
|
||||
|
||||
Render the motor groups as fixed-height cells so the old `中间侧` and `回车侧` rows become block elements:
|
||||
|
||||
```vue
|
||||
<div class="control-panel__cell control-panel__cell--motor">
|
||||
<div
|
||||
v-for="dj in item.dianji"
|
||||
:id="dj.id"
|
||||
:key="dj.id"
|
||||
class="flexs flex_col items_center gap0_25 text_center dianji_status"
|
||||
@dragover="onDjDragOver"
|
||||
@drop="onDjDrop($event, dj, 'id')"
|
||||
>
|
||||
<!-- preserve existing motor inner markup and click handlers -->
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
For the lower section, keep the same structure but iterate `item.dianji2` and continue calling `toggleDjStop(dj, item.team, item.statusAuto, 'dianji2')`.
|
||||
|
||||
### Task 3: Rebuild Styling for Alignment and Visual Parity
|
||||
|
||||
**Files:**
|
||||
- Modify: `D:\testProjects\yaodaoxiangmu\vite_vue3_tsproject_change\src\views\coalFeeding.vue`
|
||||
|
||||
- [ ] **Step 1: Add block-layout container styles**
|
||||
|
||||
Append styles like these near the existing `<style>` block:
|
||||
|
||||
```css
|
||||
.control-panel {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
min-width: max-content;
|
||||
}
|
||||
|
||||
.control-panel__labels {
|
||||
flex: 0 0 132px;
|
||||
}
|
||||
|
||||
.control-panel__columns {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.control-panel__column {
|
||||
min-width: 96px;
|
||||
border-right: 1px solid rgba(148, 163, 184, 0.6);
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Add fixed row heights so labels and unit cells stay aligned**
|
||||
|
||||
Add shared row classes to replace the natural table sizing:
|
||||
|
||||
```css
|
||||
.control-panel__corner,
|
||||
.control-panel__column-header {
|
||||
height: 42px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-bottom: 1px solid rgba(148, 163, 184, 0.6);
|
||||
}
|
||||
|
||||
.control-panel__label--row,
|
||||
.control-panel__cell--row {
|
||||
height: 42px;
|
||||
}
|
||||
|
||||
.control-panel__label--temp,
|
||||
.control-panel__cell--temp,
|
||||
.control-panel__label--car,
|
||||
.control-panel__cell--car {
|
||||
height: 42px;
|
||||
}
|
||||
|
||||
.control-panel__label--motor,
|
||||
.control-panel__cell--motor {
|
||||
min-height: 120px;
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Restore row backgrounds and dense control-panel styling**
|
||||
|
||||
Add styling hooks so the new block elements preserve the current visual categories:
|
||||
|
||||
```css
|
||||
.control-panel__label,
|
||||
.control-panel__cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.25rem;
|
||||
border-bottom: 1px solid rgba(148, 163, 184, 0.6);
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.control-panel__label--temp,
|
||||
.control-panel__cell--temp {
|
||||
background: #fff1e8;
|
||||
}
|
||||
|
||||
.control-panel__label--car,
|
||||
.control-panel__cell--car {
|
||||
background: #dbeafe;
|
||||
}
|
||||
|
||||
.control-panel__label--motor,
|
||||
.control-panel__cell--motor {
|
||||
background: #eff6ff;
|
||||
}
|
||||
|
||||
.control-panel__input {
|
||||
width: 6rem;
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Run a targeted selector scan to make sure the old `<table>` layout is gone**
|
||||
|
||||
Run: `rg -n "<table|<thead|<tbody|<tr|<td|<th" src\views\coalFeeding.vue`
|
||||
|
||||
Expected: no matches
|
||||
|
||||
### Task 4: Verify Build and Behavior
|
||||
|
||||
**Files:**
|
||||
- Modify: `D:\testProjects\yaodaoxiangmu\vite_vue3_tsproject_change\src\views\coalFeeding.vue`
|
||||
|
||||
- [ ] **Step 1: Run the production build**
|
||||
|
||||
Run: `npm run build`
|
||||
|
||||
Expected: Vite build completes successfully and outputs the `dist` bundle without Vue template or TypeScript errors.
|
||||
|
||||
- [ ] **Step 2: Manually verify the right panel structure in the app**
|
||||
|
||||
Open the page and confirm:
|
||||
|
||||
```text
|
||||
1. The right panel no longer uses a table layout.
|
||||
2. The left parameter column remains visually aligned with each unit column.
|
||||
3. Each unit column shows:
|
||||
- upper block
|
||||
- 2侧温度
|
||||
- 车位号
|
||||
- 1侧温度
|
||||
- lower block
|
||||
4. 中间侧 and 回车侧 motor areas still render with the expected controls.
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Manually verify preserved interactions**
|
||||
|
||||
Confirm these behaviors still work:
|
||||
|
||||
```text
|
||||
1. Auto mode shows text; manual mode shows inputs.
|
||||
2. Clicking a motor still toggles stop/run state as before.
|
||||
3. Temperature targets still accept drag-and-drop point binding.
|
||||
4. Bottom manual/auto switch panel still renders and responds.
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Record completion status instead of committing**
|
||||
|
||||
Because this workspace currently has no `.git` directory, do not run commit commands here.
|
||||
Instead, record completion by listing the verified items in the final handoff:
|
||||
|
||||
```text
|
||||
- build result
|
||||
- layout conversion status
|
||||
- interaction checks completed
|
||||
```
|
||||
|
|
@ -0,0 +1,235 @@
|
|||
# Coal Feeding Motor Grid Alignment Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Update the motor sections in `src/views/coalFeeding.vue` so both `中间侧` and `回车侧` render motors two per row, with odd counts left-aligned, while keeping the left labels height-aligned with the right motor sections.
|
||||
|
||||
**Architecture:** Keep the existing block-based control-panel layout as the baseline. Add small computed helpers in `coalFeeding.vue` to calculate a shared minimum height for the middle and return motor sections, then apply that shared height to both the left labels and right motor containers. Replace the motor-item vertical stack with a two-column CSS grid wrapper while preserving all existing motor card interactions.
|
||||
|
||||
**Tech Stack:** Vue 3 `<script setup lang="ts">`, existing `coalFeeding.vue` local state, scoped CSS, Vite build verification
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Add Shared Motor Height Helpers
|
||||
|
||||
**Files:**
|
||||
- Modify: `D:\testProjects\yaodaoxiangmu\vite_vue3_tsproject_change\src\views\coalFeeding.vue`
|
||||
|
||||
- [ ] **Step 1: Add height constants near the existing control-panel helper functions**
|
||||
|
||||
Insert these constants near `getCellId`, `getTempDropId`, and `isTimeRow` so the motor section height calculation is explicit and easy to tune:
|
||||
|
||||
```ts
|
||||
const MOTOR_GRID_COLUMNS = 2;
|
||||
const MOTOR_SECTION_MIN_HEIGHT = 120;
|
||||
const MOTOR_CARD_HEIGHT = 92;
|
||||
const MOTOR_SECTION_VERTICAL_PADDING = 8;
|
||||
const MOTOR_GRID_ROW_GAP = 8;
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Add helpers to calculate motor row counts and shared heights**
|
||||
|
||||
Add these helpers below the constants so both left labels and right sections can consume the same height values:
|
||||
|
||||
```ts
|
||||
const getMotorRowCount = (count: number) => {
|
||||
return Math.max(1, Math.ceil(count / MOTOR_GRID_COLUMNS));
|
||||
};
|
||||
|
||||
const getMotorSectionHeight = (count: number) => {
|
||||
const rows = getMotorRowCount(count);
|
||||
const computedHeight =
|
||||
MOTOR_SECTION_VERTICAL_PADDING * 2 +
|
||||
rows * MOTOR_CARD_HEIGHT +
|
||||
(rows - 1) * MOTOR_GRID_ROW_GAP;
|
||||
|
||||
return `${Math.max(MOTOR_SECTION_MIN_HEIGHT, computedHeight)}px`;
|
||||
};
|
||||
|
||||
const middleMotorHeight = computed(() => {
|
||||
const maxCount = dataList.reduce((max, item) => Math.max(max, item.dianji.length), 0);
|
||||
return getMotorSectionHeight(maxCount);
|
||||
});
|
||||
|
||||
const returnMotorHeight = computed(() => {
|
||||
const maxCount = dataList.reduce((max, item) => Math.max(max, item.dianji2.length), 0);
|
||||
return getMotorSectionHeight(maxCount);
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Import the missing Vue utility for computed values**
|
||||
|
||||
Update the top Vue import so the new computed helpers are available:
|
||||
|
||||
```ts
|
||||
import { computed, onMounted, onUnmounted, reactive, ref } from "vue";
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Run a quick search to confirm the new helper symbols exist once**
|
||||
|
||||
Run: `rg -n "MOTOR_GRID_COLUMNS|getMotorRowCount|middleMotorHeight|returnMotorHeight|computed" src\views\coalFeeding.vue`
|
||||
|
||||
Expected: matches for the new constants/helpers and the updated Vue import in `src\views\coalFeeding.vue`
|
||||
|
||||
### Task 2: Apply Shared Heights to Labels and Motor Sections
|
||||
|
||||
**Files:**
|
||||
- Modify: `D:\testProjects\yaodaoxiangmu\vite_vue3_tsproject_change\src\views\coalFeeding.vue`
|
||||
|
||||
- [ ] **Step 1: Bind the left motor labels to the shared section heights**
|
||||
|
||||
Update the two left-side labels so they use the same computed heights as the corresponding right-side sections:
|
||||
|
||||
```vue
|
||||
<div class="control-panel__label control-panel__label--motor" :style="{ minHeight: middleMotorHeight }">
|
||||
中间侧
|
||||
</div>
|
||||
```
|
||||
|
||||
```vue
|
||||
<div class="control-panel__label control-panel__label--motor" :style="{ minHeight: returnMotorHeight }">
|
||||
回车侧
|
||||
</div>
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Bind the right-side upper motor section to the middle motor height**
|
||||
|
||||
Update the `中间侧` motor wrapper from a plain section cell into a shared-height container:
|
||||
|
||||
```vue
|
||||
<div class="control-panel__cell control-panel__cell--motor" :style="{ minHeight: middleMotorHeight }">
|
||||
<div class="control-panel__motor-grid">
|
||||
<!-- existing dianji cards stay here -->
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Bind the right-side lower motor section to the return motor height**
|
||||
|
||||
Update the `回车侧` motor wrapper the same way:
|
||||
|
||||
```vue
|
||||
<div class="control-panel__cell control-panel__cell--motor" :style="{ minHeight: returnMotorHeight }">
|
||||
<div class="control-panel__motor-grid">
|
||||
<!-- existing dianji2 cards stay here -->
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Task 3: Convert Motor Items to Two-Column Grid Layout
|
||||
|
||||
**Files:**
|
||||
- Modify: `D:\testProjects\yaodaoxiangmu\vite_vue3_tsproject_change\src\views\coalFeeding.vue`
|
||||
|
||||
- [ ] **Step 1: Wrap the upper motor cards in a dedicated grid container**
|
||||
|
||||
Move the existing `item.dianji` loop inside a grid wrapper without changing the motor card internals:
|
||||
|
||||
```vue
|
||||
<div class="control-panel__motor-grid">
|
||||
<div
|
||||
v-for="dj in item.dianji"
|
||||
:key="dj.id"
|
||||
:id="dj.id"
|
||||
class="control-panel__motor-card flexs flex_col items_center gap0_25 text_center dianji_status"
|
||||
@dragover="onDjDragOver"
|
||||
@drop="onDjDrop($event, dj, 'id')"
|
||||
>
|
||||
<!-- keep existing motor content and click handlers -->
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Wrap the lower motor cards in the same grid container pattern**
|
||||
|
||||
Apply the same grid wrapper to `item.dianji2`, keeping the existing click handler with the `dianji2` motor key:
|
||||
|
||||
```vue
|
||||
<div class="control-panel__motor-grid">
|
||||
<div
|
||||
v-for="dj in item.dianji2"
|
||||
:key="dj.id"
|
||||
class="control-panel__motor-card flexs flex_col items_center gap0_25 text_center dianji_status"
|
||||
@dragover="onDjDragOver"
|
||||
@drop="onDjDrop($event, dj, 'id')"
|
||||
>
|
||||
<!-- keep existing motor content and click handlers -->
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Add the motor grid styles so cards render two per row**
|
||||
|
||||
Append these scoped styles near the current control-panel CSS:
|
||||
|
||||
```css
|
||||
.control-panel__motor-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
align-items: start;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.control-panel__motor-card {
|
||||
width: 100%;
|
||||
min-height: 92px;
|
||||
}
|
||||
```
|
||||
|
||||
This keeps odd counts left-aligned automatically because the final odd card occupies the first cell of the last grid row.
|
||||
|
||||
- [ ] **Step 4: Update the motor container styles to support top-aligned grid content**
|
||||
|
||||
Refine the existing motor container style so the new grid aligns correctly:
|
||||
|
||||
```css
|
||||
.control-panel__cell--motor {
|
||||
min-height: 120px;
|
||||
background: #eff6ff;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
padding: 8px;
|
||||
}
|
||||
```
|
||||
|
||||
### Task 4: Verify Layout and Build
|
||||
|
||||
**Files:**
|
||||
- Modify: `D:\testProjects\yaodaoxiangmu\vite_vue3_tsproject_change\src\views\coalFeeding.vue`
|
||||
|
||||
- [ ] **Step 1: Run a selector scan to confirm the new motor-grid classes are present**
|
||||
|
||||
Run: `rg -n "control-panel__motor-grid|control-panel__motor-card|middleMotorHeight|returnMotorHeight" src\views\coalFeeding.vue`
|
||||
|
||||
Expected: matches for the new template bindings, helper logic, and styles inside `src\views\coalFeeding.vue`
|
||||
|
||||
- [ ] **Step 2: Run the production build**
|
||||
|
||||
Run: `npm run build`
|
||||
|
||||
Expected: Vite finishes successfully with no Vue template or TypeScript errors.
|
||||
|
||||
- [ ] **Step 3: Manually verify the refined motor layout**
|
||||
|
||||
Open the page and confirm:
|
||||
|
||||
```text
|
||||
1. 中间侧 motors render two per row.
|
||||
2. 回车侧 motors render two per row.
|
||||
3. If a section has an odd motor count, the last motor stays on the left side of the final row.
|
||||
4. The left labels for 中间侧 and 回车侧 stay height-aligned with the right motor blocks.
|
||||
5. Existing motor click and drag behavior still works.
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Record completion status instead of committing**
|
||||
|
||||
Because this workspace has no `.git` directory, do not run commit commands here.
|
||||
In the final handoff, report:
|
||||
|
||||
```text
|
||||
- build result
|
||||
- motor grid conversion status
|
||||
- left/right height alignment status
|
||||
- interaction checks completed
|
||||
```
|
||||
|
|
@ -0,0 +1,223 @@
|
|||
# coalFeeding.vue Right Panel Layout Redesign
|
||||
|
||||
## Overview
|
||||
|
||||
This design updates the right-side data presentation area in `src/views/coalFeeding.vue`.
|
||||
The current implementation uses a large HTML `<table>` to render the unit comparison area.
|
||||
The new implementation will remove `<table>` entirely and replace it with ordinary block elements while preserving the existing row-and-column visual structure.
|
||||
|
||||
The redesign will follow the confirmed "path 2" approach:
|
||||
|
||||
- Keep the current business fields and interaction logic unchanged.
|
||||
- Rebuild the right-side layout as a column-oriented block structure.
|
||||
- Split each unit column into reusable visual sections.
|
||||
- Preserve the current visual alignment, colors, and row semantics.
|
||||
|
||||
## Goals
|
||||
|
||||
- Remove the `<table>` layout from the right-side unit display area.
|
||||
- Preserve the current visual feeling of a multi-column industrial control panel.
|
||||
- Render the layout as:
|
||||
- one fixed parameter label column
|
||||
- multiple unit columns rendered with ordinary elements
|
||||
- Split each unit column into five ordered sections:
|
||||
1. upper block: four time-setting rows + middle-side motors + accumulated runtime
|
||||
2. `2-side temperature`
|
||||
3. `car number`
|
||||
4. `1-side temperature`
|
||||
5. lower block: four independent time-setting rows + return-side motors + accumulated runtime
|
||||
- Keep existing click, drag-and-drop, and auto/manual behaviors working.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Do not switch the page to the new backend `unit -> equipments -> role_points` structure in this change.
|
||||
- Do not refactor WebSocket payload handling in this change.
|
||||
- Do not alter the current business meanings of fields such as `tmyxsds`, `tempera2`, `dianji`, or `dianji2`.
|
||||
- Do not split logic into separate Vue files unless needed later; this change stays inside `coalFeeding.vue`.
|
||||
|
||||
## Current State
|
||||
|
||||
The current right panel is rendered as a long `<table>` with:
|
||||
|
||||
- a left sticky parameter-name column
|
||||
- repeated `v-for` cells for each unit
|
||||
- an upper group of rows before `2-side temperature`
|
||||
- a middle section for temperatures and car number
|
||||
- a lower group of rows after `1-side temperature`
|
||||
|
||||
This structure visually works, but it is difficult to reshape into two explicit per-column blocks because the DOM is organized by rows, not by columns.
|
||||
|
||||
## Chosen Approach
|
||||
|
||||
Use a block-based, column-oriented layout.
|
||||
|
||||
The new structure will be:
|
||||
|
||||
- outer right-panel container
|
||||
- left parameter column
|
||||
- horizontally scrollable unit-column area
|
||||
- each unit rendered as one column card-like container
|
||||
|
||||
Each unit column will render these sections in order:
|
||||
|
||||
1. Upper block
|
||||
- `投煤运行时间设定` -> `tmyxsds`
|
||||
- `投煤停止时间设定` -> `tmtzsds`
|
||||
- `投煤累积时间设定` -> `tmljsds`
|
||||
- `布料运行时间设定` -> `blyxsds`
|
||||
- `中间侧` -> `dianji`
|
||||
- `投煤累积时间` -> `tmljsjs`
|
||||
2. `2侧温度` -> `tempera2`
|
||||
3. `车位号` -> `carNumber`
|
||||
4. `1侧温度` -> `tempera1`
|
||||
5. Lower block
|
||||
- `投煤运行时间设定` -> `tmyxsdx`
|
||||
- `投煤停止时间设定` -> `tmtzsdx`
|
||||
- `投煤累积时间设定` -> `tmljsdx`
|
||||
- `布料运行时间设定` -> `blyxsdx`
|
||||
- `回车侧` -> `dianji2`
|
||||
- `投煤累积时间` -> `tmljsjx`
|
||||
|
||||
## Data Mapping
|
||||
|
||||
This change keeps the existing `dataList` shape unchanged.
|
||||
|
||||
Upper block mapping:
|
||||
|
||||
- `tmyxsds`
|
||||
- `tmtzsds`
|
||||
- `tmljsds`
|
||||
- `blyxsds`
|
||||
- `dianji`
|
||||
- `tmljsjs`
|
||||
|
||||
Middle display mapping:
|
||||
|
||||
- `tempera2`
|
||||
- `carNumber`
|
||||
- `tempera1`
|
||||
|
||||
Lower block mapping:
|
||||
|
||||
- `tmyxsdx`
|
||||
- `tmtzsdx`
|
||||
- `tmljsdx`
|
||||
- `blyxsdx`
|
||||
- `dianji2`
|
||||
- `tmljsjx`
|
||||
|
||||
## Interaction Preservation
|
||||
|
||||
The redesign must preserve the current interactions:
|
||||
|
||||
- Time-setting fields:
|
||||
- show plain text when `item.statusAuto === true`
|
||||
- show `<input>` when `item.statusAuto === false`
|
||||
- Middle-side motor controls continue to use `dianji`
|
||||
- Return-side motor controls continue to use `dianji2`
|
||||
- Motor click actions continue to call `toggleDjStop(...)`
|
||||
- Temperature drop targets keep current DOM id rules so point binding logic continues to work:
|
||||
- `tempera2_unit...`
|
||||
- `tempera1_unit...`
|
||||
- Existing drag/drop handlers stay in place for the fields already using them
|
||||
- Unit-level manual/auto toggle controls under the panel remain unchanged
|
||||
|
||||
## Rendering Structure
|
||||
|
||||
The template will be reorganized into these conceptual pieces:
|
||||
|
||||
- parameter label column
|
||||
- contains every row label in display order
|
||||
- unit columns container
|
||||
- renders one column per `dataList` item
|
||||
- unit column header
|
||||
- shows the unit name badge
|
||||
- upper section container
|
||||
- renders upper time rows, motor row, and accumulated runtime row
|
||||
- middle temperature section
|
||||
- renders `2-side temperature`, `car number`, and `1-side temperature`
|
||||
- lower section container
|
||||
- renders lower time rows, return-side motor row, and accumulated runtime row
|
||||
|
||||
Repeated visual blocks inside the unit column will be reduced into small render patterns:
|
||||
|
||||
- time item block
|
||||
- motor group block
|
||||
- temperature/car display block
|
||||
|
||||
These can remain inline in the same file as repeated template fragments or lightweight helper loops.
|
||||
|
||||
## Layout and Styling
|
||||
|
||||
The new layout will use ordinary block elements instead of table semantics:
|
||||
|
||||
- `display: flex` for the main horizontal layout
|
||||
- fixed-width parameter label column
|
||||
- horizontally scrollable unit column strip
|
||||
- each unit column with a consistent minimum width
|
||||
- each row item with controlled height and border styling
|
||||
|
||||
Visual continuity requirements:
|
||||
|
||||
- preserve row alignment between label column and unit columns
|
||||
- preserve the current gray/white parameter rows
|
||||
- preserve the blue motor-area styling
|
||||
- preserve the orange temperature styling
|
||||
- preserve the highlighted car-number styling
|
||||
- preserve the general dense control-panel appearance
|
||||
|
||||
To avoid layout drift after removing table semantics:
|
||||
|
||||
- shared row-height classes will be introduced for standard rows
|
||||
- motor rows will use a dedicated fixed-height style
|
||||
- upper and lower section wrappers will provide clear grouping without breaking alignment
|
||||
|
||||
## Risks and Mitigations
|
||||
|
||||
### Risk 1: alignment drift after removing `<table>`
|
||||
|
||||
Mitigation:
|
||||
|
||||
- define explicit heights for standard rows
|
||||
- define explicit height for motor rows
|
||||
- keep label column and unit columns rendered in the same vertical order
|
||||
|
||||
### Risk 2: drag/drop regressions on temperature fields
|
||||
|
||||
Mitigation:
|
||||
|
||||
- preserve the existing id naming convention for temperature targets
|
||||
- keep existing drop handlers on the same business elements
|
||||
|
||||
### Risk 3: motor interactions change unintentionally
|
||||
|
||||
Mitigation:
|
||||
|
||||
- reuse current click handler calls and existing state fields
|
||||
- only move markup; do not alter motor state logic during this change
|
||||
|
||||
### Risk 4: template becomes harder to read
|
||||
|
||||
Mitigation:
|
||||
|
||||
- group markup by column section instead of one giant table body
|
||||
- use repeated rendering patterns for time blocks and motor groups
|
||||
|
||||
## Testing Plan
|
||||
|
||||
After implementation, verify:
|
||||
|
||||
- the right panel renders without `<table>`
|
||||
- horizontal scrolling still works for the unit columns
|
||||
- label column and unit columns remain aligned
|
||||
- upper and lower blocks are visually distinct inside each unit column
|
||||
- auto/manual display behavior still works for time fields
|
||||
- motor click interactions still work
|
||||
- temperature drop targets still accept drag-and-drop
|
||||
- existing settings section below the panel still renders correctly
|
||||
|
||||
## Open Constraints
|
||||
|
||||
- The page still uses the current local `dataList` structure.
|
||||
- Future work may migrate the page to the new backend unit/equipment structure, but that is intentionally out of scope here.
|
||||
- The current project directory is not a Git repository, so this design document cannot be committed from the current workspace state.
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
# coalFeeding.vue Motor Grid Alignment Refinement
|
||||
|
||||
## Overview
|
||||
|
||||
This design refines the already approved block-based right-panel layout in `src/views/coalFeeding.vue`.
|
||||
It does not change the overall removal of `<table>`.
|
||||
Instead, it adjusts the motor areas so they better match the desired visual structure.
|
||||
|
||||
The refinement introduces two requirements:
|
||||
|
||||
- both `中间侧` and `回车侧` motor areas should render motors two per row instead of one per row
|
||||
- the left-side label blocks for those motor areas must stay height-aligned with the corresponding right-side motor blocks
|
||||
|
||||
## Goals
|
||||
|
||||
- Render motor items in a 2-column layout for both motor sections
|
||||
- Keep odd motor counts left-aligned on the final row
|
||||
- Keep the left label block height aligned with the right motor block height
|
||||
- Preserve all existing drag-and-drop, click, and status-display logic
|
||||
- Avoid reworking the surrounding block layout
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Do not change the current block layout order
|
||||
- Do not change time-field rendering
|
||||
- Do not change temperature or car-number sections
|
||||
- Do not switch to a new backend data structure
|
||||
- Do not introduce DOM measurement logic unless strictly necessary
|
||||
|
||||
## Chosen Approach
|
||||
|
||||
Use a CSS grid for motor layout plus shared computed heights for label and motor sections.
|
||||
|
||||
Why this approach:
|
||||
|
||||
- It preserves the current block-based layout structure
|
||||
- It keeps the change small and local to the motor sections
|
||||
- It avoids fragile DOM measurement code
|
||||
- It guarantees left/right alignment by making both sides use the same computed height source
|
||||
|
||||
## Motor Layout Rules
|
||||
|
||||
Both `中间侧` and `回车侧` motor groups will render with:
|
||||
|
||||
- two columns per row
|
||||
- consistent gaps between motor items
|
||||
- left-aligned final item when the count is odd
|
||||
|
||||
Implementation shape:
|
||||
|
||||
- wrap each motor list in a dedicated grid container
|
||||
- use `grid-template-columns: repeat(2, minmax(0, 1fr))`
|
||||
- allow each motor item to occupy one cell
|
||||
- do not center or span the final odd item
|
||||
|
||||
Expected examples:
|
||||
|
||||
- 2 motors -> 1 row of 2
|
||||
- 3 motors -> 2 rows, with the last item on the left of the second row
|
||||
- 4 motors -> 2 rows of 2
|
||||
|
||||
## Height Alignment Rules
|
||||
|
||||
The left labels for `中间侧` and `回车侧` must visually align with the height of their corresponding motor blocks across all unit columns.
|
||||
|
||||
To achieve this, compute two shared height values:
|
||||
|
||||
- `middleMotorHeight`
|
||||
- `returnMotorHeight`
|
||||
|
||||
These values will be derived from the largest motor-group row count in the current data.
|
||||
|
||||
Row count formula:
|
||||
|
||||
- `Math.ceil(motorCount / 2)`
|
||||
|
||||
Height formula:
|
||||
|
||||
- `sectionPaddingTop + sectionPaddingBottom + rows * itemHeight + (rows - 1) * rowGap`
|
||||
|
||||
Also enforce a minimum height so single-row sections do not become too short.
|
||||
|
||||
Both the left label block and the right motor block must use the same computed height source for each section.
|
||||
|
||||
## Rendering Changes
|
||||
|
||||
Keep the existing right-panel block structure.
|
||||
Only refine the motor section internals and the matching left labels.
|
||||
|
||||
Changes:
|
||||
|
||||
- `中间侧` label gets `:style="{ minHeight: middleMotorHeight }"`
|
||||
- `回车侧` label gets `:style="{ minHeight: returnMotorHeight }"`
|
||||
- upper motor section gets `:style="{ minHeight: middleMotorHeight }"`
|
||||
- lower motor section gets `:style="{ minHeight: returnMotorHeight }"`
|
||||
- motor items render inside a `control-panel__motor-grid` container
|
||||
|
||||
The individual motor cards keep their current:
|
||||
|
||||
- drag-and-drop handlers
|
||||
- run/stop toggles
|
||||
- local/remote badges
|
||||
- fault/normal text
|
||||
|
||||
## Styling Changes
|
||||
|
||||
Add a motor-grid wrapper class with:
|
||||
|
||||
- `display: grid`
|
||||
- two equal-width columns
|
||||
- fixed row gap
|
||||
- fixed column gap
|
||||
- left-aligned item flow
|
||||
|
||||
The motor section wrapper should:
|
||||
|
||||
- keep the current blue-tinted background
|
||||
- use the shared computed minimum height
|
||||
- align its content to the top instead of stretching awkwardly
|
||||
|
||||
The left label blocks should:
|
||||
|
||||
- continue using the current label styling
|
||||
- adopt the same computed minimum height as their corresponding right-side section
|
||||
|
||||
## Risks and Mitigations
|
||||
|
||||
### Risk 1: heights drift if units have different motor counts
|
||||
|
||||
Mitigation:
|
||||
|
||||
- compute height from the maximum row count across all units for each motor section
|
||||
|
||||
### Risk 2: odd counts look visually off
|
||||
|
||||
Mitigation:
|
||||
|
||||
- keep the final odd item left-aligned and do not stretch it to full width
|
||||
|
||||
### Risk 3: click and drag interactions break during markup changes
|
||||
|
||||
Mitigation:
|
||||
|
||||
- keep the motor card markup intact
|
||||
- move only the grouping wrapper and layout styles
|
||||
|
||||
## Testing Plan
|
||||
|
||||
After implementation, verify:
|
||||
|
||||
- `中间侧` motors render two per row
|
||||
- `回车侧` motors render two per row
|
||||
- odd motor counts leave the last motor on the left side
|
||||
- left label heights align with right motor-section heights
|
||||
- motor clicks still toggle state
|
||||
- drag-and-drop still works on motor items
|
||||
- the surrounding right-panel layout remains unchanged
|
||||
|
||||
## Open Constraints
|
||||
|
||||
- The workspace is still not a Git repository, so this design file cannot be committed here.
|
||||
- This refinement assumes the existing block-based right-panel layout remains the baseline for implementation.
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
/// <reference types="vite/client" />
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue'
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import pluginVue from 'eslint-plugin-vue'
|
||||
import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript'
|
||||
|
||||
// To allow more languages other than `ts` in `.vue` files, uncomment the following lines:
|
||||
// import { configureVueProject } from '@vue/eslint-config-typescript'
|
||||
// configureVueProject({ scriptLangs: ['ts', 'tsx'] })
|
||||
// More info at https://github.com/vuejs/eslint-config-typescript/#advanced-setup
|
||||
|
||||
export default defineConfigWithVueTs(
|
||||
{
|
||||
name: 'app/files-to-lint',
|
||||
files: ['**/*.{ts,mts,tsx,vue}'],
|
||||
},
|
||||
|
||||
{
|
||||
name: 'app/files-to-ignore',
|
||||
ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**'],
|
||||
},
|
||||
|
||||
pluginVue.configs['flat/essential'],
|
||||
vueTsConfigs.recommended,
|
||||
)
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<!doctype html>
|
||||
<html lang="">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>瓮福隧道窑系统</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"name": "vue-project",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "run-p type-check \"build-only {@}\" --",
|
||||
"preview": "vite preview",
|
||||
"build-only": "vite build",
|
||||
"type-check": "vue-tsc --build",
|
||||
"lint": "eslint . --fix",
|
||||
"tauri:dev": "tauri dev",
|
||||
"tauri:build": "tauri build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@opentiny/vue": "^3.29.0",
|
||||
"@tauri-apps/api": "^2.10.1",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tauri-apps/cli": "^2.10.1",
|
||||
"@tsconfig/node22": "^22.0.0",
|
||||
"@types/node": "^22.13.4",
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"@vue/eslint-config-typescript": "^14.4.0",
|
||||
"@vue/tsconfig": "^0.7.0",
|
||||
"eslint": "^9.20.1",
|
||||
"eslint-plugin-vue": "^9.32.0",
|
||||
"jiti": "^2.4.2",
|
||||
"npm-run-all2": "^7.0.2",
|
||||
"typescript": "~5.7.3",
|
||||
"vite": "^6.1.0",
|
||||
"vite-plugin-vue-devtools": "^7.7.2",
|
||||
"vue-tsc": "^2.2.2"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
|
|
@ -0,0 +1,4 @@
|
|||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
/gen/schemas
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
[package]
|
||||
name = "app"
|
||||
version = "0.1.0"
|
||||
description = "A Tauri App"
|
||||
authors = ["you"]
|
||||
license = ""
|
||||
repository = ""
|
||||
edition = "2021"
|
||||
rust-version = "1.77.2"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
name = "app_lib"
|
||||
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2.5.6", features = [] }
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
log = "0.4"
|
||||
tauri = { version = "2.10.3", features = [] }
|
||||
tauri-plugin-log = "2"
|
||||
|
||||
[profile.release]
|
||||
# Reduce final binary size for distribution builds.
|
||||
opt-level = "z"
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
strip = "symbols"
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
tauri_build::build()
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"$schema": "../gen/schemas/desktop-schema.json",
|
||||
"identifier": "default",
|
||||
"description": "enables the default permissions",
|
||||
"windows": [
|
||||
"main"
|
||||
],
|
||||
"permissions": [
|
||||
"core:default"
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
|
@ -0,0 +1,16 @@
|
|||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
tauri::Builder::default()
|
||||
.setup(|app| {
|
||||
if cfg!(debug_assertions) {
|
||||
app.handle().plugin(
|
||||
tauri_plugin_log::Builder::default()
|
||||
.level(log::LevelFilter::Info)
|
||||
.build(),
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
fn main() {
|
||||
app_lib::run();
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
|
||||
"productName": "vue-project",
|
||||
"version": "0.1.0",
|
||||
"identifier": "com.ctczc.vitevue3tsprojectchange",
|
||||
"build": {
|
||||
"frontendDist": "../dist",
|
||||
"devUrl": "http://localhost:5173",
|
||||
"beforeDevCommand": "npm run dev",
|
||||
"beforeBuildCommand": "npm run build"
|
||||
},
|
||||
"app": {
|
||||
"windows": [
|
||||
{
|
||||
"title": "vue-project",
|
||||
"width": 800,
|
||||
"height": 600,
|
||||
"resizable": true,
|
||||
"fullscreen": false
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": null
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": ["nsis"],
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/128x128@2x.png",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
<template>
|
||||
<div class="w_full h_full" style="display: flex; flex-direction: column">
|
||||
<header>
|
||||
<div class="headLeft" style="color: rgb(220, 38, 38)">瓮福隧道窑系统</div>
|
||||
<nav class="headCenter">
|
||||
<router-link
|
||||
class="headerNav"
|
||||
v-for="nav in navData"
|
||||
:key="nav.path"
|
||||
:to="nav.path"
|
||||
:class="{ active_link: $route.path === nav.path }"
|
||||
>
|
||||
{{ nav.name }}
|
||||
</router-link>
|
||||
</nav>
|
||||
<div class="headRight">
|
||||
<span>当前用户:{{ currentUser }}</span>
|
||||
<span style="margin: 0 1rem">PLC连接状态</span>
|
||||
<span>{{ currentTime }}</span>
|
||||
</div>
|
||||
</header>
|
||||
<RouterView />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onMounted, onUnmounted, ref } from "vue";
|
||||
import { RouterLink, RouterView } from "vue-router";
|
||||
const navData = [
|
||||
{ name: "PLC列表", path: "/plcList" },
|
||||
// { name: "报警消音", path: "/warning" },
|
||||
// { name: "运转系统", path: "/runsystem" },
|
||||
// { name: "烧砖系统1", path: "/firesystem" },
|
||||
// { name: "烧砖系统2", path: "/firesystem2" },
|
||||
{ name: "投煤器", path: "/coalFeeding" },
|
||||
{ name: "投煤器电流", path: "/coalFeedingA" },
|
||||
{ name: "投煤器2", path: "/coalFeeding2" },
|
||||
// { name: "开始画面", path: "/startPage" },
|
||||
];
|
||||
const currentUser = "管理员";
|
||||
const currentTime = ref(new Date().toLocaleString());
|
||||
let timeTimer: number | undefined;
|
||||
onMounted(() => {
|
||||
timeTimer = window.setInterval(() => {
|
||||
currentTime.value = new Date().toLocaleString();
|
||||
}, 1000);
|
||||
});
|
||||
onUnmounted(() => {
|
||||
if (timeTimer) window.clearInterval(timeTimer);
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.headLeft {
|
||||
height: 35px;
|
||||
width: 160px;
|
||||
line-height: 35px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.headCenter {
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
padding-top: 3px;
|
||||
}
|
||||
.headRight {
|
||||
height: 35px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.headerNav {
|
||||
padding: 0 1rem;
|
||||
color: #65758b;
|
||||
height: 29px;
|
||||
line-height: 29px;
|
||||
border-radius: 3px;
|
||||
display: inline-block;
|
||||
color: var(--color-text-mute);
|
||||
}
|
||||
.headerNav:hover {
|
||||
background-color: rgb(237, 239, 243);
|
||||
}
|
||||
.headerNav:active {
|
||||
color: #ffffff;
|
||||
background-color: rgb(11, 117, 203);
|
||||
}
|
||||
.active_link {
|
||||
color: #ffffff;
|
||||
background-color: rgb(11, 117, 203);
|
||||
}
|
||||
@media (min-width: 1024px) {
|
||||
}
|
||||
.tiny-button.tiny-button--small {
|
||||
padding: 0.375rem 0.75rem;
|
||||
min-width: 2.25rem;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
import { get, post, put, del, patch } from "../utils/request";
|
||||
|
||||
// Example API declarations. Replace paths with your backend endpoints.
|
||||
export const api = {
|
||||
getPlcList(params: Object) {
|
||||
return get("/source", params);
|
||||
},
|
||||
createPlc(data: Object) {
|
||||
console.log("创建", data);
|
||||
return post("/source", data);
|
||||
},
|
||||
updatePlc(id: string, data: Object) {
|
||||
return put(`/source/${id}`, data);
|
||||
},
|
||||
deletePlc(id: string) {
|
||||
return del(`/source/${id}`);
|
||||
},
|
||||
getNodeTree(id: string) {
|
||||
return get(`/source/${id}/node-tree`);
|
||||
},
|
||||
pointCreate( data: Object) {
|
||||
return post(`/point`, data);
|
||||
},
|
||||
pointUpdate( id: String, data: Object) {
|
||||
return put(`/point/${id}`, data);
|
||||
},
|
||||
pointDel( id: String) {
|
||||
return del(`/point/${id}`);
|
||||
},
|
||||
pointList(params: Object) {
|
||||
return get("/point", params);
|
||||
},
|
||||
getPage(data: Object) {
|
||||
return get("/page", data);
|
||||
},
|
||||
pageCreate(data: Object){
|
||||
return post("/page", data);
|
||||
},
|
||||
pageUpdate(id:string,data: Object){
|
||||
return put(`/page/${id}`, data);
|
||||
},
|
||||
pageDel( id: String) {
|
||||
return del(`/page/${id}`);
|
||||
},
|
||||
unitList(params: Object) {
|
||||
return get("/unit", params);
|
||||
},
|
||||
};
|
||||
|
||||
export default api;
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
/* color palette from <https://github.com/vuejs/theme> */
|
||||
:root {
|
||||
--vt-c-primary: rgb(249, 250, 251);
|
||||
--vt-c-white: #ffffff;
|
||||
--vt-c-white-soft: #f8f8f8;
|
||||
--vt-c-white-mute: #f2f2f2;
|
||||
|
||||
--vt-c-black: #181818;
|
||||
--vt-c-black-soft: #222222;
|
||||
--vt-c-black-mute: #282828;
|
||||
|
||||
--vt-c-indigo: #2c3e50;
|
||||
|
||||
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
|
||||
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
|
||||
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
|
||||
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
|
||||
|
||||
--vt-c-text-light-1: var(--vt-c-indigo);
|
||||
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
|
||||
--vt-c-text-dark-1: var(--vt-c-white);
|
||||
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
|
||||
}
|
||||
|
||||
/* semantic color variables for this project */
|
||||
:root {
|
||||
--color-background: var(--vt-c-primary);
|
||||
--color-background-soft: var(--vt-c-white-soft);
|
||||
--color-background-mute: var(--vt-c-white-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-light-2);
|
||||
--color-border-hover: var(--vt-c-divider-light-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-light-1);
|
||||
--color-text: var(--vt-c-text-light-1);
|
||||
|
||||
--section-gap: 160px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--color-background: var(--vt-c-black);
|
||||
--color-background-soft: var(--vt-c-black-soft);
|
||||
--color-background-mute: var(--vt-c-black-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-dark-2);
|
||||
--color-border-hover: var(--vt-c-divider-dark-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-dark-1);
|
||||
--color-text: var(--vt-c-text-dark-2);
|
||||
}
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
color: var(--color-text);
|
||||
background: var(--color-background);
|
||||
transition:
|
||||
color 0.5s,
|
||||
background-color 0.5s;
|
||||
line-height: 1.6;
|
||||
font-family:
|
||||
Inter,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
"Segoe UI",
|
||||
Roboto,
|
||||
Oxygen,
|
||||
Ubuntu,
|
||||
Cantarell,
|
||||
"Fira Sans",
|
||||
"Droid Sans",
|
||||
"Helvetica Neue",
|
||||
sans-serif;
|
||||
font-size: 15px;
|
||||
color: #65758b;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
|
@ -0,0 +1,836 @@
|
|||
@import "./base.css";
|
||||
* {
|
||||
border-color: hsl(var(--border));
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
line-height: inherit;
|
||||
background-color: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
}
|
||||
html,
|
||||
body {
|
||||
font-size: 16px;
|
||||
color: #65758b;
|
||||
}
|
||||
:root {
|
||||
--background: 210 20% 98%;
|
||||
--foreground: 220 20% 10%;
|
||||
--card: 0 0% 100%;
|
||||
--primary: 207 90% 42%;
|
||||
--primary-foreground: 0 0% 100%;
|
||||
--muted-foreground: 215 16% 47%;
|
||||
--border: 214 20% 88%;
|
||||
--radius: 0.5rem;
|
||||
--delete: 248 113 113;
|
||||
--primary_btn: 11 117 203;
|
||||
--sidebar-accent: 214 20% 94%;
|
||||
--sidebar-accent-foreground: 220 20% 10%;
|
||||
}
|
||||
html,
|
||||
:host {
|
||||
line-height: 1.5;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-moz-tab-size: 4;
|
||||
tab-size: 4;
|
||||
font-family: var(--font-inter), system-ui, sans-serif;
|
||||
font-feature-settings: normal;
|
||||
font-variation-settings: normal;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
hr {
|
||||
height: 0;
|
||||
color: inherit;
|
||||
border-top-width: 1px;
|
||||
}
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
table {
|
||||
text-indent: 0;
|
||||
border-color: inherit;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
button,
|
||||
input:where([type="button"]),
|
||||
input:where([type="reset"]),
|
||||
input:where([type="submit"]) {
|
||||
-webkit-appearance: button;
|
||||
background-color: transparent;
|
||||
background-image: none;
|
||||
}
|
||||
dl,
|
||||
dd,
|
||||
h2,
|
||||
hr,
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
legend {
|
||||
padding: 0;
|
||||
}
|
||||
ol,
|
||||
ul,
|
||||
menu {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
dialog {
|
||||
padding: 0;
|
||||
}
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
input {
|
||||
outline: none;
|
||||
border: 1px solid #e5e7eb;
|
||||
}
|
||||
input::placeholder,
|
||||
textarea::placeholder {
|
||||
opacity: 1;
|
||||
color: #9ca3af;
|
||||
}
|
||||
button,
|
||||
[role="button"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
:disabled {
|
||||
cursor: default;
|
||||
}
|
||||
img,
|
||||
svg,
|
||||
video,
|
||||
canvas {
|
||||
display: block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
img,
|
||||
video {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
*,
|
||||
::before,
|
||||
::after {
|
||||
--tw-border-spacing-x: 0;
|
||||
--tw-border-spacing-y: 0;
|
||||
--tw-translate-x: 0;
|
||||
--tw-translate-y: 0;
|
||||
--tw-rotate: 0;
|
||||
--tw-skew-x: 0;
|
||||
--tw-skew-y: 0;
|
||||
--tw-scale-x: 1;
|
||||
--tw-scale-y: 1;
|
||||
--tw-pan-x: ;
|
||||
--tw-pan-y: ;
|
||||
--tw-pinch-zoom: ;
|
||||
--tw-scroll-snap-strictness: proximity;
|
||||
--tw-gradient-from-position: ;
|
||||
--tw-gradient-via-position: ;
|
||||
--tw-gradient-to-position: ;
|
||||
--tw-ordinal: ;
|
||||
--tw-slashed-zero: ;
|
||||
--tw-numeric-figure: ;
|
||||
--tw-numeric-spacing: ;
|
||||
--tw-numeric-fraction: ;
|
||||
--tw-ring-inset: ;
|
||||
--tw-ring-offset-width: 0px;
|
||||
--tw-ring-offset-color: #fff;
|
||||
--tw-ring-color: rgb(59 130 246 / 0.5);
|
||||
--tw-ring-offset-shadow: 0 0 #0000;
|
||||
--tw-ring-shadow: 0 0 #0000;
|
||||
--tw-shadow: 0 0 #0000;
|
||||
--tw-shadow-colored: 0 0 #0000;
|
||||
--tw-blur: ;
|
||||
--tw-brightness: ;
|
||||
--tw-contrast: ;
|
||||
--tw-grayscale: ;
|
||||
--tw-hue-rotate: ;
|
||||
--tw-invert: ;
|
||||
--tw-saturate: ;
|
||||
--tw-sepia: ;
|
||||
--tw-drop-shadow: ;
|
||||
--tw-backdrop-blur: ;
|
||||
--tw-backdrop-brightness: ;
|
||||
--tw-backdrop-contrast: ;
|
||||
--tw-backdrop-grayscale: ;
|
||||
--tw-backdrop-hue-rotate: ;
|
||||
--tw-backdrop-invert: ;
|
||||
--tw-backdrop-opacity: ;
|
||||
--tw-backdrop-saturate: ;
|
||||
--tw-backdrop-sepia: ;
|
||||
--tw-contain-size: ;
|
||||
--tw-contain-layout: ;
|
||||
--tw-contain-paint: ;
|
||||
--tw-contain-style: ;
|
||||
--tw-content: "";
|
||||
box-sizing: border-box;
|
||||
border-width: 0;
|
||||
border-style: solid;
|
||||
border-color: #e5e7eb;
|
||||
}
|
||||
::backdrop {
|
||||
--tw-border-spacing-x: 0;
|
||||
--tw-border-spacing-y: 0;
|
||||
--tw-translate-x: 0;
|
||||
--tw-translate-y: 0;
|
||||
--tw-rotate: 0;
|
||||
--tw-skew-x: 0;
|
||||
--tw-skew-y: 0;
|
||||
--tw-scale-x: 1;
|
||||
--tw-scale-y: 1;
|
||||
--tw-pan-x: ;
|
||||
--tw-pan-y: ;
|
||||
--tw-pinch-zoom: ;
|
||||
--tw-scroll-snap-strictness: proximity;
|
||||
--tw-gradient-from-position: ;
|
||||
--tw-gradient-via-position: ;
|
||||
--tw-gradient-to-position: ;
|
||||
--tw-ordinal: ;
|
||||
--tw-slashed-zero: ;
|
||||
--tw-numeric-figure: ;
|
||||
--tw-numeric-spacing: ;
|
||||
--tw-numeric-fraction: ;
|
||||
--tw-ring-inset: ;
|
||||
--tw-ring-offset-width: 0px;
|
||||
--tw-ring-offset-color: #fff;
|
||||
--tw-ring-color: rgb(59 130 246 / 0.5);
|
||||
--tw-ring-offset-shadow: 0 0 #0000;
|
||||
--tw-ring-shadow: 0 0 #0000;
|
||||
--tw-shadow: 0 0 #0000;
|
||||
--tw-shadow-colored: 0 0 #0000;
|
||||
--tw-blur: ;
|
||||
--tw-brightness: ;
|
||||
--tw-contrast: ;
|
||||
--tw-grayscale: ;
|
||||
--tw-hue-rotate: ;
|
||||
--tw-invert: ;
|
||||
--tw-saturate: ;
|
||||
--tw-sepia: ;
|
||||
--tw-drop-shadow: ;
|
||||
--tw-backdrop-blur: ;
|
||||
--tw-backdrop-brightness: ;
|
||||
--tw-backdrop-contrast: ;
|
||||
--tw-backdrop-grayscale: ;
|
||||
--tw-backdrop-hue-rotate: ;
|
||||
--tw-backdrop-invert: ;
|
||||
--tw-backdrop-opacity: ;
|
||||
--tw-backdrop-saturate: ;
|
||||
--tw-backdrop-sepia: ;
|
||||
--tw-contain-size: ;
|
||||
--tw-contain-layout: ;
|
||||
--tw-contain-paint: ;
|
||||
--tw-contain-style: ;
|
||||
}
|
||||
.coal_main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.flexs {
|
||||
display: flex;
|
||||
}
|
||||
.flex_col {
|
||||
flex-direction: column;
|
||||
}
|
||||
.flex_wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.flex_1 {
|
||||
flex: 1 1 0%;
|
||||
}
|
||||
.shrink0 {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.grow {
|
||||
flex-grow: 1;
|
||||
}
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
.justify_center {
|
||||
justify-content: center;
|
||||
}
|
||||
.inline_flex {
|
||||
display: inline-flex;
|
||||
}
|
||||
.pageTitle {
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.75rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.1em;
|
||||
color: rgb(var(--primary_btn));
|
||||
}
|
||||
.shadows {
|
||||
box-shadow: 0 0 10px 3px rgba(213, 208, 208, 0.192);
|
||||
}
|
||||
.borderAll {
|
||||
border: 1px solid #dae0e7;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
.bgWhite {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.cursor_default {
|
||||
cursor: default;
|
||||
}
|
||||
.cursor_pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
.list_none {
|
||||
list-style-type: none;
|
||||
}
|
||||
.items_center {
|
||||
align-items: center;
|
||||
}
|
||||
.justify_center {
|
||||
justify-content: center;
|
||||
}
|
||||
.text_sm {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
.text_xs {
|
||||
font-size: 0.75rem;
|
||||
line-height: 1rem;
|
||||
}
|
||||
.fontw400 {
|
||||
font-weight: 400;
|
||||
}
|
||||
.fontw500 {
|
||||
font-weight: 500;
|
||||
}
|
||||
.fontw600 {
|
||||
font-weight: 600;
|
||||
}
|
||||
.fontw700 {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.border-collapse {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.w_full {
|
||||
width: 100%;
|
||||
}
|
||||
.w_half {
|
||||
width: 50%;
|
||||
display: inline-block;
|
||||
}
|
||||
.w_px {
|
||||
width: 1px;
|
||||
}
|
||||
.w2 {
|
||||
width: 2rem;
|
||||
}
|
||||
.w4 {
|
||||
width: 4rem;
|
||||
}
|
||||
.w10 {
|
||||
width: 10rem;
|
||||
}
|
||||
.w0_5 {
|
||||
width: 0.5rem;
|
||||
}
|
||||
.w0_625 {
|
||||
width: 0.625rem;
|
||||
}
|
||||
.w0_75 {
|
||||
width: 0.75rem;
|
||||
}
|
||||
.w1_2 {
|
||||
width: 1.2rem;
|
||||
}
|
||||
.min_w_0 {
|
||||
min-width: 0px;
|
||||
}
|
||||
.min_w_1_75 {
|
||||
min-width: 1.75rem;
|
||||
}
|
||||
.min_w_2 {
|
||||
min-width: 2rem;
|
||||
}
|
||||
.min_w_2_25 {
|
||||
min-width: 2.25rem;
|
||||
}
|
||||
.min_w_6 {
|
||||
min-width: 6rem;
|
||||
}
|
||||
.fixed {
|
||||
position: fixed;
|
||||
}
|
||||
.absolute {
|
||||
position: absolute;
|
||||
}
|
||||
.relative {
|
||||
position: relative;
|
||||
}
|
||||
.sticky {
|
||||
position: sticky;
|
||||
}
|
||||
.color_black {
|
||||
color: #14181f;
|
||||
}
|
||||
.color_green {
|
||||
color: #059669;
|
||||
}
|
||||
.color_primary {
|
||||
color: rgb(var(--primary_btn));
|
||||
}
|
||||
.color_red {
|
||||
color: #dc2626;
|
||||
}
|
||||
.color_base {
|
||||
color: #65758b;
|
||||
}
|
||||
.bg_card {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.bg_header {
|
||||
background-color: #edeff3;
|
||||
}
|
||||
.visible {
|
||||
visibility: visible;
|
||||
}
|
||||
.invisible {
|
||||
visibility: hidden;
|
||||
}
|
||||
.inset_0 {
|
||||
inset: 0px;
|
||||
}
|
||||
.left0 {
|
||||
left: 0px;
|
||||
}
|
||||
.z_10 {
|
||||
z-index: 10;
|
||||
}
|
||||
.ml0_25 {
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
.block {
|
||||
display: block;
|
||||
}
|
||||
.table {
|
||||
display: table;
|
||||
}
|
||||
.grid {
|
||||
display: grid;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
.h0_5 {
|
||||
height: 0.5rem;
|
||||
}
|
||||
.h0_625 {
|
||||
height: 0.625rem;
|
||||
}
|
||||
.h0_75 {
|
||||
height: 0.75rem;
|
||||
}
|
||||
.h0_9 {
|
||||
height: 0.9rem;
|
||||
line-height: 0.85rem;
|
||||
}
|
||||
.h1 {
|
||||
height: 1rem;
|
||||
}
|
||||
.h1_25 {
|
||||
height: 1.25rem;
|
||||
}
|
||||
.h_full {
|
||||
height: 100%;
|
||||
}
|
||||
.wh0_4{
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
}
|
||||
@keyframes ping {
|
||||
75%,
|
||||
100% {
|
||||
transform: scale(2);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
.animate-ping {
|
||||
animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
|
||||
}
|
||||
@keyframes pulse {
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
.gap_12 {
|
||||
gap: 12px;
|
||||
}
|
||||
.gap0_125 {
|
||||
gap: 0.125rem;
|
||||
}
|
||||
.gap0_25 {
|
||||
gap: 0.25rem;
|
||||
}
|
||||
.gap0_375 {
|
||||
gap: 0.375rem;
|
||||
}
|
||||
.gap0_5 {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.gap1_5 {
|
||||
gap: 1.5rem;
|
||||
}
|
||||
.overflow_hidden {
|
||||
overflow: hidden;
|
||||
}
|
||||
.overflowx_auto {
|
||||
overflow-x: auto;
|
||||
}
|
||||
.whitespace_nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.rounded {
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
.rounded_full {
|
||||
border-radius: 9999px;
|
||||
}
|
||||
.rounded_md {
|
||||
border-radius: calc(var(--radius) - 2px);
|
||||
}
|
||||
.border {
|
||||
border-width: 1px;
|
||||
}
|
||||
.border_2 {
|
||||
border-width: 2px;
|
||||
}
|
||||
.border_b {
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
.border_r {
|
||||
border-right-width: 1px;
|
||||
}
|
||||
.border_border0_5 {
|
||||
border-color: hsl(var(--border) / 0.5);
|
||||
}
|
||||
.border_border0_6 {
|
||||
border-color: hsl(var(--border) / 0.6);
|
||||
}
|
||||
.border_circle {
|
||||
border-color: rgb(218, 224, 231);
|
||||
}
|
||||
.border_green_M {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(16, 185, 129);
|
||||
}
|
||||
.border_green_yx {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(5, 150, 105);
|
||||
}
|
||||
.border_red_M {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(248, 113, 113);
|
||||
}
|
||||
.border_red {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(220, 38, 38);
|
||||
}
|
||||
.border_yellow {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(234, 179, 8);
|
||||
}
|
||||
.bg_blue {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239, 246, 255);
|
||||
}
|
||||
.bg_border {
|
||||
background-color: hsl(var(--border));
|
||||
}
|
||||
.bg_card {
|
||||
background-color: hsl(var(--card));
|
||||
}
|
||||
.bg_green_yc {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(52, 211, 153);
|
||||
}
|
||||
.bg_green_M {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(236 253 245 / var(--tw-bg-opacity, 1));
|
||||
}
|
||||
.bg_green_yx {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(16, 185, 129);
|
||||
}
|
||||
.bg_muted {
|
||||
background-color: rgb(234, 237, 240);
|
||||
}
|
||||
.bg_orange {
|
||||
background-color: rgb(255 247 237 / 0.5);
|
||||
}
|
||||
.bg_orange:hover {
|
||||
background-color: rgb(255 247 237 / 0.8);
|
||||
}
|
||||
.bg_primary {
|
||||
background-color: hsl(var(--primary));
|
||||
}
|
||||
.bg_red_M {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(254, 242, 242);
|
||||
}
|
||||
.bg_red {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239, 68, 68);
|
||||
}
|
||||
.bg_temperature {
|
||||
background-color: rgb(237, 239, 243);
|
||||
}
|
||||
.bg_yellow {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(250, 204, 21);
|
||||
}
|
||||
.p0_5 {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
.p0_75 {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
.px0_25 {
|
||||
padding-left: 0.25rem;
|
||||
padding-right: 0.25rem;
|
||||
}
|
||||
.px0_375 {
|
||||
padding-left: 0.375rem;
|
||||
padding-right: 0.375rem;
|
||||
}
|
||||
.px0_5 {
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
.px1 {
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
.py0_125 {
|
||||
padding-top: 0.125rem;
|
||||
padding-bottom: 0.125rem;
|
||||
}
|
||||
.py0_375 {
|
||||
padding-top: 0.375rem;
|
||||
padding-bottom: 0.375rem;
|
||||
}
|
||||
.py0_5 {
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
.my0_3 {
|
||||
margin-top: 0.3rem;
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
.text_left {
|
||||
text-align: left;
|
||||
}
|
||||
.text_center {
|
||||
text-align: center;
|
||||
}
|
||||
.text_right {
|
||||
text-align: right;
|
||||
}
|
||||
.text_10 {
|
||||
font-size: 10px;
|
||||
}
|
||||
.text_11 {
|
||||
font-size: 11px;
|
||||
}
|
||||
.text_7 {
|
||||
font-size: 7px;
|
||||
}
|
||||
.text_8 {
|
||||
font-size: 8px;
|
||||
}
|
||||
.leading_none {
|
||||
line-height: 1;
|
||||
}
|
||||
.letter_space0_05 {
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
.text_white {
|
||||
color: #ffffff;
|
||||
}
|
||||
.text_green {
|
||||
color: rgb(5, 150, 105);
|
||||
}
|
||||
.text_emerald {
|
||||
color: rgb(4, 120, 87);
|
||||
}
|
||||
.text_foreground {
|
||||
color: hsl(var(--foreground));
|
||||
}
|
||||
.text_orange_600 {
|
||||
color: rgb(234, 88, 12);
|
||||
}
|
||||
.text_primary {
|
||||
color: hsl(var(--primary));
|
||||
}
|
||||
.text_primary_color {
|
||||
color: hsl(var(--primary-foreground));
|
||||
}
|
||||
.text_red_M {
|
||||
color: rgb(239, 68, 68);
|
||||
}
|
||||
.text_red {
|
||||
color: rgb(220, 38, 38);
|
||||
}
|
||||
.shadow_sm {
|
||||
--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||
--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);
|
||||
box-shadow:
|
||||
var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
|
||||
var(--tw-shadow);
|
||||
}
|
||||
.transition_all {
|
||||
transition-property: all;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
}
|
||||
.duration_500 {
|
||||
transition-duration: 500ms;
|
||||
}
|
||||
.running {
|
||||
animation-play-state: running;
|
||||
}
|
||||
:-moz-focusring {
|
||||
outline: auto;
|
||||
}
|
||||
:-moz-ui-invalid {
|
||||
box-shadow: none;
|
||||
}
|
||||
::-webkit-inner-spin-button,
|
||||
::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
[type="search"] {
|
||||
-webkit-appearance: textfield;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button;
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
[hidden]:where(:not([hidden="until-found"])) {
|
||||
display: none;
|
||||
}
|
||||
/* Custom scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: hsl(214 20% 94%);
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: hsl(214 20% 80%);
|
||||
border-radius: 3px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: hsl(207 90% 42%);
|
||||
}
|
||||
.placeholder:text-muted-foreground::placeholder {
|
||||
color: hsl(var(--muted-foreground));
|
||||
}
|
||||
.active:bg-sidebar-accent:active {
|
||||
background-color: hsl(var(--sidebar-accent));
|
||||
}
|
||||
.active:text-sidebar-accent-foreground:active {
|
||||
color: hsl(var(--sidebar-accent-foreground));
|
||||
}
|
||||
@keyframes accordion-up {
|
||||
from {
|
||||
height: var(--radix-accordion-content-height);
|
||||
}
|
||||
to {
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
@keyframes accordion-down {
|
||||
from {
|
||||
height: 0;
|
||||
}
|
||||
to {
|
||||
height: var(--radix-accordion-content-height);
|
||||
}
|
||||
}
|
||||
@keyframes enter {
|
||||
from {
|
||||
opacity: var(--tw-enter-opacity, 1);
|
||||
transform: translate3d(
|
||||
var(--tw-enter-translate-x, 0),
|
||||
var(--tw-enter-translate-y, 0),
|
||||
0
|
||||
)
|
||||
scale3d(
|
||||
var(--tw-enter-scale, 1),
|
||||
var(--tw-enter-scale, 1),
|
||||
var(--tw-enter-scale, 1)
|
||||
)
|
||||
rotate(var(--tw-enter-rotate, 0));
|
||||
}
|
||||
}
|
||||
@keyframes exit {
|
||||
to {
|
||||
opacity: var(--tw-exit-opacity, 1);
|
||||
transform: translate3d(
|
||||
var(--tw-exit-translate-x, 0),
|
||||
var(--tw-exit-translate-y, 0),
|
||||
0
|
||||
)
|
||||
scale3d(
|
||||
var(--tw-exit-scale, 1),
|
||||
var(--tw-exit-scale, 1),
|
||||
var(--tw-exit-scale, 1)
|
||||
)
|
||||
rotate(var(--tw-exit-rotate, 0));
|
||||
}
|
||||
}
|
||||
.reBtn{
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
}
|
||||
.c_pointer{
|
||||
cursor: pointer;
|
||||
}
|
||||
.Mcircle {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
.dianji_status {
|
||||
min-width: 40px;
|
||||
display: inline-block;
|
||||
width: 50%;
|
||||
margin: 0.2rem 0;
|
||||
}
|
||||
.btns {
|
||||
width: 4rem;
|
||||
height: 1.5rem;
|
||||
text-align: center;
|
||||
border-radius: 1px;
|
||||
background: #e9eaea;
|
||||
line-height: 1.5rem;
|
||||
}
|
||||
.btnsActive {
|
||||
color: #ffffff;
|
||||
background: rgb(16, 185, 129);
|
||||
}
|
||||
#settings {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
.coalMain {
|
||||
padding: 12px;
|
||||
gap: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>
|
||||
|
After Width: | Height: | Size: 276 B |
|
|
@ -0,0 +1,30 @@
|
|||
@import "./base.css";
|
||||
@import "./coal.css";
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
|
||||
}
|
||||
#app {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
color: #65758b;
|
||||
}
|
||||
|
||||
a,
|
||||
.green {
|
||||
text-decoration: none;
|
||||
color: hsla(160, 100%, 37%, 1);
|
||||
transition: 0.4s;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
a:hover {
|
||||
background-color: rgb(237, 239, 243);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
msg: string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="greetings">
|
||||
<h1 class="green">{{ msg }}</h1>
|
||||
<h3>
|
||||
You’ve successfully created a project with
|
||||
<a href="https://vite.dev/" target="_blank" rel="noopener">Vite</a> +
|
||||
<a href="https://vuejs.org/" target="_blank" rel="noopener">Vue 3</a>. What's next?
|
||||
</h3>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
h1 {
|
||||
font-weight: 500;
|
||||
font-size: 2.6rem;
|
||||
position: relative;
|
||||
top: -10px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.greetings h1,
|
||||
.greetings h3 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.greetings h1,
|
||||
.greetings h3 {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
<template>
|
||||
<div class="item">
|
||||
<i>
|
||||
<slot name="icon"></slot>
|
||||
</i>
|
||||
<div class="details">
|
||||
<h3>
|
||||
<slot name="heading"></slot>
|
||||
</h3>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.item {
|
||||
margin-top: 2rem;
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.details {
|
||||
flex: 1;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
i {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
place-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.4rem;
|
||||
color: var(--color-heading);
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.item {
|
||||
margin-top: 0;
|
||||
padding: 0.4rem 0 1rem calc(var(--section-gap) / 2);
|
||||
}
|
||||
|
||||
i {
|
||||
top: calc(50% - 25px);
|
||||
left: -26px;
|
||||
position: absolute;
|
||||
border: 1px solid var(--color-border);
|
||||
background: var(--color-background);
|
||||
border-radius: 8px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.item:before {
|
||||
content: ' ';
|
||||
border-left: 1px solid var(--color-border);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: calc(50% + 25px);
|
||||
height: calc(50% - 25px);
|
||||
}
|
||||
|
||||
.item:after {
|
||||
content: ' ';
|
||||
border-left: 1px solid var(--color-border);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: calc(50% + 25px);
|
||||
height: calc(50% - 25px);
|
||||
}
|
||||
|
||||
.item:first-of-type:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.item:last-of-type:after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
|
||||
<path
|
||||
d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor">
|
||||
<path
|
||||
d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor">
|
||||
<path
|
||||
d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
|
||||
<path
|
||||
d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<!-- This icon is from <https://github.com/Templarian/MaterialDesign>, distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license-->
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
class="iconify iconify--mdi"
|
||||
width="24"
|
||||
height="24"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import "./assets/main.css";
|
||||
|
||||
import { createApp } from "vue";
|
||||
import App from "./App.vue";
|
||||
import router from "./router";
|
||||
import TinyVue from "@opentiny/vue"; // import '@opentiny/vue/dist/index.css'
|
||||
const app = createApp(App);
|
||||
app.use(router);
|
||||
app.use(TinyVue);
|
||||
app.mount("#app");
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
import { createRouter, createWebHistory } from "vue-router";
|
||||
import warning from "../views/warning.vue";
|
||||
import runsystem from "../views/runsystem.vue";
|
||||
import firesystem from "../views/firesystem.vue";
|
||||
import firesystem2 from "../views/firesystem2.vue";
|
||||
import coalFeeding from "../views/coalFeeding.vue";
|
||||
import coalFeeding2 from "../views/coalFeeding2.vue";
|
||||
import coalFeedingA from "../views/coalFeedingA.vue";
|
||||
import plcList from "../views/plcList.vue";
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: "/",
|
||||
name: "home",
|
||||
component: coalFeeding,
|
||||
},
|
||||
{
|
||||
path: "/warning",
|
||||
name: "warning",
|
||||
component: warning,
|
||||
},
|
||||
{
|
||||
path: "/plcList",
|
||||
name: "plcList",
|
||||
component: plcList,
|
||||
},
|
||||
{
|
||||
path: "/runsystem",
|
||||
name: "runsystem",
|
||||
component: runsystem,
|
||||
},
|
||||
{
|
||||
path: "/firesystem",
|
||||
name: "firesystem",
|
||||
component: firesystem,
|
||||
},
|
||||
{
|
||||
path: "/firesystem2",
|
||||
name: "firesystem2",
|
||||
component: firesystem2,
|
||||
},
|
||||
{
|
||||
path: "/coalFeeding",
|
||||
name: "coalFeeding",
|
||||
component: coalFeeding,
|
||||
},
|
||||
{
|
||||
path: "/coalFeedingA",
|
||||
name: "coalFeedingA",
|
||||
component: coalFeedingA,
|
||||
},
|
||||
{
|
||||
path: "/coalFeeding2",
|
||||
name: "coalFeeding2",
|
||||
component: coalFeeding2,
|
||||
},
|
||||
{
|
||||
path: "/startPage",
|
||||
name: "startPage",
|
||||
component: () => import("../views/startPage.vue"),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
const DEFAULT_TIMEOUT = 10000;
|
||||
|
||||
function toQueryString(params: Record<string, any> = {}): string {
|
||||
const searchParams = new URLSearchParams();
|
||||
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value === undefined || value === null) return;
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach((item) => searchParams.append(key, String(item)));
|
||||
return;
|
||||
}
|
||||
searchParams.append(key, String(value));
|
||||
});
|
||||
|
||||
const query = searchParams.toString();
|
||||
return query ? `?${query}` : "";
|
||||
}
|
||||
|
||||
function buildUrl(url: string, params?: Record<string, any>): string {
|
||||
if (!params || Object.keys(params).length === 0) return url;
|
||||
const query = toQueryString(params);
|
||||
return `${url}${url.includes("?") ? `&${query.slice(1)}` : query}`;
|
||||
}
|
||||
|
||||
async function parseResponse(response: Response): Promise<any> {
|
||||
const contentType = response.headers.get("content-type") || "";
|
||||
|
||||
if (contentType.includes("application/json")) {
|
||||
return response.json();
|
||||
}
|
||||
|
||||
return response.text();
|
||||
}
|
||||
|
||||
function normalizeError(status: number, message?: string, payload?: any): Error {
|
||||
const error = new Error(message || "Request failed");
|
||||
(error as any).status = status;
|
||||
(error as any).payload = payload;
|
||||
return error;
|
||||
}
|
||||
|
||||
export async function request(
|
||||
url: string,
|
||||
options: {
|
||||
method?: string;
|
||||
params?: Record<string, any>;
|
||||
data?: any;
|
||||
headers?: Record<string, string>;
|
||||
timeout?: number;
|
||||
baseURL?: string;
|
||||
cache?: RequestCache;
|
||||
} = {},
|
||||
): Promise<any> {
|
||||
const {
|
||||
method = "GET",
|
||||
params,
|
||||
data,
|
||||
headers = {},
|
||||
timeout = DEFAULT_TIMEOUT,
|
||||
baseURL = import.meta.env.VITE_API_BASE_URL || "",
|
||||
cache,
|
||||
...restOptions
|
||||
} = options;
|
||||
|
||||
const controller = new AbortController();
|
||||
const timer = setTimeout(() => controller.abort(), timeout);
|
||||
|
||||
const fullUrl = buildUrl(`${baseURL}${url}`, params);
|
||||
const finalHeaders = { ...headers };
|
||||
const fetchOptions: RequestInit = {
|
||||
method,
|
||||
headers: finalHeaders,
|
||||
signal: controller.signal,
|
||||
cache: cache ?? (method.toUpperCase() === "GET" ? "no-store" : undefined),
|
||||
...restOptions,
|
||||
};
|
||||
|
||||
if (data !== undefined) {
|
||||
if (data instanceof FormData) {
|
||||
fetchOptions.body = data;
|
||||
} else {
|
||||
finalHeaders["Content-Type"] = finalHeaders["Content-Type"] || "application/json";
|
||||
fetchOptions.body = JSON.stringify(data);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(fullUrl, fetchOptions);
|
||||
const payload = await parseResponse(response);
|
||||
|
||||
if (!response.ok) {
|
||||
throw normalizeError(response.status, payload?.message || response.statusText, payload);
|
||||
}
|
||||
|
||||
return payload;
|
||||
} catch (error) {
|
||||
if ((error as Error).name === "AbortError") {
|
||||
throw normalizeError(408, "Request timeout");
|
||||
}
|
||||
throw error;
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
|
||||
export const get = (
|
||||
url: string,
|
||||
params?: Record<string, any>,
|
||||
options: Record<string, any> = {},
|
||||
): Promise<any> => request(url, { ...options, method: "GET", params });
|
||||
|
||||
export const post = (url: string, data: any, options: Record<string, any> = {}): Promise<any> =>
|
||||
request(url, { ...options, method: "POST", data });
|
||||
export const put = (url: string, data: any, options: Record<string, any> = {}): Promise<any> =>
|
||||
request(url, { ...options, method: "PUT", data });
|
||||
export const patch = (url: string, data: any, options: Record<string, any> = {}): Promise<any> =>
|
||||
request(url, { ...options, method: "PATCH", data });
|
||||
export const del = (url: string,params?: Record<string, any>,options: Record<string, any> = {},): Promise<any> =>
|
||||
request(url, { ...options, method: "DELETE", params });
|
||||
|
|
@ -0,0 +1,922 @@
|
|||
<template>
|
||||
<main class="coal_main p0_75 gap_12" style="flex-grow: 1">
|
||||
<div class="text_center">
|
||||
<h2 class="pageTitle">1#隧道窑投煤器</h2>
|
||||
</div>
|
||||
<div class="flexs items_center justify_center gap1_5 py0_375 px1 bgWhite borderAll shadows">
|
||||
<div class="flexs items_center gap0_5">
|
||||
<span>站点总数</span>
|
||||
<span class="text_sm fontw700 color_black"
|
||||
>18
|
||||
<span class="fontw400 color_base ml0_25">台</span>
|
||||
</span>
|
||||
</div>
|
||||
<span class="h1 w_px bg_border"></span>
|
||||
<div class="flexs items_center gap0_5">
|
||||
<span>运行中</span>
|
||||
<span class="text_sm fontw700 color_green">
|
||||
16<span class="fontw400 color_base ml0_25">台</span>
|
||||
</span>
|
||||
</div>
|
||||
<span class="h1 w_px bg_border"></span>
|
||||
<div class="flexs items_center gap0_5">
|
||||
<span>自动模式</span>
|
||||
<span class="text_sm fontw700 color_primary">
|
||||
16<span class="fontw400 color_base ml0_25">台</span>
|
||||
</span>
|
||||
</div>
|
||||
<span class="h1 w_px bg_border"></span>
|
||||
<div class="flexs items_center gap0_5">
|
||||
<span>最高温度</span>
|
||||
<span class="text_sm fontw700 color_red">
|
||||
1000<span class="fontw400 color_base ml0_25">°C</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex_1 flexs gap0_5">
|
||||
<!-- 点位列表 -->
|
||||
<ul class="flexs flex_col gap0_5 w10 borderAll bgWhite p0_5">
|
||||
<li class="text_10">点位列表
|
||||
<tiny-button type="info" :icon="iconRefresh" plain size="small" @click="getPointList">刷新</tiny-button>
|
||||
</li>
|
||||
<li
|
||||
v-for="point in pointList"
|
||||
:key="point.id"
|
||||
class="point-item"
|
||||
draggable="true"
|
||||
@dragstart="onPointDragStart($event, point)"
|
||||
>
|
||||
<span class="text_10">{{ point.name }}</span>
|
||||
<span class="text_10" v-if="point.point_monitor">-[{{ point.point_monitor.value_text }}]</span>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- 数据表格 -->
|
||||
<div class="flex_1 shrink0 overflowx_auto borderAll bg_card">
|
||||
<div class="flex_1 overflowx_auto borderAll">
|
||||
<table class="w_full text_xs">
|
||||
<thead class="bg_header">
|
||||
<tr>
|
||||
<th
|
||||
class="sticky left0 z_10 p0_5 fontw600 text_left "
|
||||
style="background-color: #edeff3"
|
||||
>
|
||||
参数/站号
|
||||
</th>
|
||||
<th
|
||||
v-for="item in dataList"
|
||||
:key="item.team"
|
||||
class="px0_25 py0_5 text_center border_r border_border0_6 min_w_6"
|
||||
>
|
||||
<span
|
||||
class="inline_flex items_center justify_center h1_25 min_w_6 px0_375 rounded bg_primary text_primary_color fontw700 text_11"
|
||||
>{{ item.team }}</span
|
||||
>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- 投煤运行设定 -->
|
||||
<tr class="border_border0_5">
|
||||
<td class="sticky left0 z_10 bg_card px0_5 py0_375 text_11 color_base whitespace_nowrap border_r border_border fontw500">
|
||||
投煤运行设定
|
||||
</td>
|
||||
<td
|
||||
v-for="item in dataList"
|
||||
:key="item.team"
|
||||
class="px0_25 py0_375 text_center border_r border_border0_6 min_w_6"
|
||||
>
|
||||
<div v-if="item.statusAuto" class="px0_25 py0_125 text_center text_xs color_base">
|
||||
{{ item.tmyxsds }}
|
||||
</div>
|
||||
<input
|
||||
v-else
|
||||
class="px0_25 py0_125 text_center text_xs color_base"
|
||||
v-model="item.tmyxsds"
|
||||
style="width: 6rem"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- 投煤停止设定 -->
|
||||
<tr class="border_border0_5">
|
||||
<td class="sticky left0 z_10 bg_card px0_5 py0_375 text_11 color_base whitespace_nowrap border_r border_border fontw500">
|
||||
投煤停止设定
|
||||
</td>
|
||||
<td
|
||||
v-for="item in dataList"
|
||||
:key="item.team"
|
||||
class="px0_25 py0_375 text_center border_r border_border0_6 min_w_6"
|
||||
>
|
||||
<div v-if="item.statusAuto" class="px0_25 py0_125 text_center text_xs color_base">
|
||||
{{ item.tmtzsds }}
|
||||
</div>
|
||||
<input
|
||||
v-else
|
||||
class="px0_25 py0_125 text_center text_xs color_base"
|
||||
v-model="item.tmtzsds"
|
||||
style="width: 6rem"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- 投煤累积设定 -->
|
||||
<tr class="border_border0_5">
|
||||
<td class="sticky left0 z_10 bg_card px0_5 py0_375 text_11 color_base whitespace_nowrap border_r border_border fontw500">
|
||||
投煤累积设定
|
||||
</td>
|
||||
<td
|
||||
v-for="item in dataList"
|
||||
:key="item.team"
|
||||
class="px0_25 py0_375 text_center border_r border_border0_6 min_w_6"
|
||||
>
|
||||
<div v-if="item.statusAuto" class="px0_25 py0_125 text_center text_xs color_base">
|
||||
{{ item.tmljsds }}
|
||||
</div>
|
||||
<input
|
||||
v-else
|
||||
class="px0_25 py0_125 text_center text_xs color_base"
|
||||
v-model="item.tmljsds"
|
||||
style="width: 6rem"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- 布料运行设定 -->
|
||||
<tr class="border_border">
|
||||
<td class="sticky left0 z_10 bg_card px0_5 py0_375 text_11 color_base whitespace_nowrap border_r border_border fontw500">
|
||||
布料运行设定
|
||||
</td>
|
||||
<td
|
||||
v-for="item in dataList"
|
||||
:key="item.team"
|
||||
class="px0_25 py0_375 text_center border_r border_border0_6 min_w_6"
|
||||
>
|
||||
<div v-if="item.statusAuto" class="px0_25 py0_125 text_center text_xs color_base">
|
||||
{{ item.blyxsds }}
|
||||
</div>
|
||||
<input
|
||||
v-else
|
||||
class="px0_25 py0_125 text_center text_xs color_base"
|
||||
v-model="item.blyxsds"
|
||||
style="width: 6rem"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- 中间侧 -->
|
||||
<tr class="border_border bg_blue">
|
||||
<td class="sticky left0 z_10 bg_blue px0_5 text_10 fontw700 text_primary border_r border_border letter_space0_05">
|
||||
中间侧
|
||||
</td>
|
||||
<td
|
||||
v-for="(item,index1) in dataList"
|
||||
:key="item.team"
|
||||
class="py0_125 border_r border_border0_6 min_w_6"
|
||||
>
|
||||
<div
|
||||
v-for="(dj,index2) in item.dianji"
|
||||
class="flexs flex_col items_center gap0_25 text_center dianji_status"
|
||||
>
|
||||
<div class="flexs items_center gap0_25 justify_center" :id="'unit'+index1+'_dj'+index2">
|
||||
<div :class="['relative inline_flex items_center justify_center rounded_full border_2 Mcircle',{'border_red_M bg_red_M': dj.isStop,'border_green_M bg_green_M': !dj.isStop}]">
|
||||
<span :class="['fontw700 leading_none text_10',{'text_red_M': dj.isStop,'text_green': !dj.isStop}]">M</span>
|
||||
</div>
|
||||
<div class="flexs flex_col justify_center items_center gap0_25 my0_3">
|
||||
<span v-if="dj.isBendi" class="border w1_2 text_8 rounded h0_9 bg_yellow border_yellow text_white">本地</span>
|
||||
<span v-else class="border w1_2 text_8 rounded h0_9 bg_green_yc border_green_M text_white">远程</span>
|
||||
<span v-if="dj.isStop" class="border w1_2 text_8 rounded h0_9 bg_red border_red text_white c_pointer">停止</span>
|
||||
<span v-else class="border w1_2 text_8 rounded h0_9 bg_green_yx border_green_yx text_white c_pointer">运行</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="items_center">
|
||||
<span v-if="dj.eqm == '正常'" class="text_10 fontw600 text_emerald">{{dj.eqm}}</span>
|
||||
<span v-else class="text_10 fontw600 text_red">{{ dj.eqm }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- 投煤累积时间 -->
|
||||
<tr class="border_border0_5">
|
||||
<td class="sticky left0 z_10 bg_card px0_5 py0_375 text_11 color_base whitespace_nowrap border_r border_border fontw500">
|
||||
投煤累积时间
|
||||
</td>
|
||||
<td
|
||||
v-for="item in dataList"
|
||||
:key="item.team"
|
||||
class="px0_25 py0_375 text_center border_r border_border0_6 min_w_6"
|
||||
>
|
||||
<div class="px0_25 py0_125 text_center text_xs text_foreground fontw600">
|
||||
{{ item.tmljsjs }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- 2侧温度 -->
|
||||
<tr class="border_border0_5 bg_orange">
|
||||
<td class="sticky left0 z_10 px0_5 py0_5 text_11 text_orange_600 whitespace_nowrap border_r border_border fontw600">
|
||||
2侧温度
|
||||
</td>
|
||||
<td
|
||||
v-for="item in dataList"
|
||||
:key="item.team"
|
||||
class="px0_25 py0_375 border_r border_border0_6 min_w_6 text_center"
|
||||
>
|
||||
<span class="text_xs fontw700 min_w_2_25 text_right text_red">{{item.tempera2}}</span>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- 车位号 -->
|
||||
<tr class="border_border bg_temperature">
|
||||
<td class="sticky left0 z_10 bg_temperature px0_5 text_11 fontw700 text_foreground border_r border_border">
|
||||
车位号
|
||||
</td>
|
||||
<td
|
||||
v-for="item in dataList"
|
||||
:key="item.team"
|
||||
class="p0_25 text_center border_r border_border0_6 min_w_6"
|
||||
>
|
||||
<span class="inline_flex items_center justify_center h1_25 min_w_1_75 px0_375 rounded_md bg_primary text_primary_color my0_3 fontw700 text_sm shadow_sm">{{ item.carNumber }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- 1侧温度 -->
|
||||
<tr class="border_border0_5 bg_orange">
|
||||
<td class="sticky left0 z_10 px0_5 py0_5 text_11 text_orange_600 whitespace_nowrap border_r border_border fontw600">
|
||||
1侧温度
|
||||
</td>
|
||||
<td
|
||||
v-for="item in dataList"
|
||||
:key="item.team"
|
||||
class="px0_25 py0_375 border_r border_border0_6 min_w_6 text_center"
|
||||
>
|
||||
<span class="text_xs fontw700 min_w_2_25 text_right text_red">{{item.tempera1}}</span>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- 投煤累积时间 -->
|
||||
<tr class="border_border0_5">
|
||||
<td
|
||||
class="sticky left0 z_10 bg_card px0_5 py0_375 text_11 color_base whitespace_nowrap border_r border_border fontw500"
|
||||
>
|
||||
投煤累积时间
|
||||
</td>
|
||||
<td
|
||||
v-for="item in dataList"
|
||||
:key="item.team"
|
||||
class="px0_25 py0_375 text_center border_r border_border0_6 min_w_6"
|
||||
>
|
||||
<div class="px0_25 py0_125 text_center text_xs text_foreground fontw600">
|
||||
{{ item.tmljsjx }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- 回车侧 -->
|
||||
<tr class="border_border bg_blue">
|
||||
<td class="sticky left0 z_10 bg_blue px0_5 text_10 fontw700 text_primary border_r border_border letter_space0_05">
|
||||
回车侧
|
||||
</td>
|
||||
<td
|
||||
v-for="item in dataList"
|
||||
:key="item.team"
|
||||
class="py0_125 border_r border_border0_6 min_w_6"
|
||||
>
|
||||
<div
|
||||
v-for="dj in item.dianji"
|
||||
class="flexs flex_col items_center gap0_25 text_center dianji_status"
|
||||
>
|
||||
<div class="flexs items_center gap0_25 justify_center">
|
||||
<div :class="['relative inline_flex items_center justify_center rounded_full border_2 Mcircle',{'border_red_M bg_red_M': dj.isStop,'border_green_M bg_green_M': !dj.isStop}]">
|
||||
<span :class="['fontw700 leading_none text_10',{'text_red_M': dj.isStop,'text_green': !dj.isStop}]">M</span>
|
||||
</div>
|
||||
<div class="flexs flex_col justify_center items_center gap0_25 my0_3">
|
||||
<span v-if="dj.isBendi" class="border w1_2 text_8 rounded h0_9 bg_yellow border_yellow text_white">本地</span>
|
||||
<span v-else class="border w1_2 text_8 rounded h0_9 bg_green_yc border_green_M text_white">远程</span>
|
||||
<span v-if="dj.isStop" class="border w1_2 text_8 rounded h0_9 bg_red border_red text_white c_pointer">停止</span>
|
||||
<span v-else class="border w1_2 text_8 rounded h0_9 bg_green_yx border_green_yx text_white c_pointer">运行</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="items_center">
|
||||
<span v-if="dj.eqm == '正常'" class="text_10 fontw600 text_emerald">{{dj.eqm}}</span>
|
||||
<span v-else class="text_10 fontw600 text_red">{{ dj.eqm }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- 投煤运行设定 -->
|
||||
<tr class="border_border0_5">
|
||||
<td class="sticky left0 z_10 bg_card px0_5 py0_375 text_11 color_base whitespace_nowrap border_r border_border fontw500">
|
||||
投煤运行设定
|
||||
</td>
|
||||
<td
|
||||
v-for="item in dataList"
|
||||
:key="item.team"
|
||||
class="px0_25 py0_375 text_center border_r border_border0_6 min_w_6"
|
||||
>
|
||||
<div v-if="item.statusAuto" class="px0_25 py0_125 text_center text_xs color_base">
|
||||
{{ item.tmyxsdx }}
|
||||
</div>
|
||||
<input
|
||||
v-else
|
||||
class="px0_25 py0_125 text_center text_xs color_base"
|
||||
v-model="item.tmyxsdx"
|
||||
style="width: 6rem"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- 投煤停止设定 -->
|
||||
<tr class="border_border0_5">
|
||||
<td class="sticky left0 z_10 bg_card px0_5 py0_375 text_11 color_base whitespace_nowrap border_r border_border fontw500">
|
||||
投煤停止设定
|
||||
</td>
|
||||
<td
|
||||
v-for="item in dataList"
|
||||
:key="item.team"
|
||||
class="px0_25 py0_375 text_center border_r border_border0_6 min_w_6"
|
||||
>
|
||||
<div v-if="item.statusAuto" class="px0_25 py0_125 text_center text_xs color_base">
|
||||
{{ item.tmtzsdx }}
|
||||
</div>
|
||||
<input
|
||||
v-else
|
||||
class="px0_25 py0_125 text_center text_xs color_base"
|
||||
v-model="item.tmtzsdx"
|
||||
style="width: 6rem"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- 投煤累积设定 -->
|
||||
<tr class="border_border0_5">
|
||||
<td class="sticky left0 z_10 bg_card px0_5 py0_375 text_11 color_base whitespace_nowrap border_r border_border fontw500">
|
||||
投煤累积设定
|
||||
</td>
|
||||
<td
|
||||
v-for="item in dataList"
|
||||
:key="item.team"
|
||||
class="px0_25 py0_375 text_center border_r border_border0_6 min_w_6"
|
||||
>
|
||||
<div v-if="item.statusAuto" class="px0_25 py0_125 text_center text_xs color_base">
|
||||
{{ item.tmljsdx }}
|
||||
</div>
|
||||
<input
|
||||
v-else
|
||||
class="px0_25 py0_125 text_center text_xs color_base"
|
||||
v-model="item.tmljsdx"
|
||||
style="width: 6rem"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- 布料运行设定 -->
|
||||
<tr class="border_border">
|
||||
<td class="sticky left0 z_10 bg_card px0_5 py0_375 text_11 color_base whitespace_nowrap border_r border_border fontw500">
|
||||
布料运行设定
|
||||
</td>
|
||||
<td
|
||||
v-for="item in dataList"
|
||||
:key="item.team"
|
||||
class="px0_25 py0_375 text_center border_r border_border0_6 min_w_6"
|
||||
>
|
||||
<div v-if="item.statusAuto" class="px0_25 py0_125 text_center text_xs color_base">
|
||||
{{ item.blyxsdx }}
|
||||
</div>
|
||||
<input
|
||||
v-else
|
||||
class="px0_25 py0_125 text_center text_xs color_base"
|
||||
v-model="item.blyxsdx"
|
||||
style="width: 6rem"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="settings">
|
||||
<div class="flexs justify_center gap1_5">
|
||||
<div class="flexs gap0_25 flex_col items_center">
|
||||
<span class="text_11 fontw600">第一单元投煤器手动切换</span>
|
||||
<div class="flexs items_center gap0_125">
|
||||
<span
|
||||
:class="{ text_10: true, btns: true, btnsActive: !unit1 }"
|
||||
@click="statusChange('1')"
|
||||
>手动</span>
|
||||
<span
|
||||
:class="{ text_10: true, btns: true, btnsActive: unit1 }"
|
||||
@click="statusAuto('1')"
|
||||
>自动</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flexs gap0_25 flex_col items_center">
|
||||
<span class="text_11 fontw600">第二单元投煤器手动切换</span>
|
||||
<div class="flexs items_center gap0_125">
|
||||
<span
|
||||
:class="{ text_10: true, btns: true, btnsActive: !unit2 }"
|
||||
@click="statusChange('2')"
|
||||
>手动</span>
|
||||
<span
|
||||
:class="{ text_10: true, btns: true, btnsActive: unit2 }"
|
||||
@click="statusAuto('2')"
|
||||
>自动</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flexs gap0_25 flex_col items_center">
|
||||
<span class="text_11 fontw600">第三单元投煤器手动切换</span>
|
||||
<div class="flexs items_center gap0_125">
|
||||
<span
|
||||
:class="{ text_10: true, btns: true, btnsActive: !unit3 }"
|
||||
@click="statusChange('3')"
|
||||
>手动</span>
|
||||
<span
|
||||
:class="{ text_10: true, btns: true, btnsActive: unit3 }"
|
||||
@click="statusAuto('3')"
|
||||
>自动</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flexs gap0_25 flex_col items_center">
|
||||
<span class="text_11 fontw600">第四单元投煤器手动切换</span>
|
||||
<div class="flexs items_center gap0_125">
|
||||
<span
|
||||
:class="{ text_10: true, btns: true, btnsActive: !unit4 }"
|
||||
@click="statusChange('4')"
|
||||
>手动</span>
|
||||
<span
|
||||
:class="{ text_10: true, btns: true, btnsActive: unit4 }"
|
||||
@click="statusAuto('4')"
|
||||
>自动</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onMounted, onUnmounted, ref } from "vue";
|
||||
import { IconRefresh } from "@opentiny/vue-icon";
|
||||
import api from "../api";
|
||||
const iconRefresh = IconRefresh()
|
||||
const pointList = ref<PointItem[]>([]);
|
||||
const ws = ref<WebSocket | null>(null);
|
||||
const error = ref(null);
|
||||
type PointItem = {
|
||||
id?: string | number;
|
||||
name: string;
|
||||
point_monitor?: {
|
||||
value_text: string;
|
||||
};
|
||||
};
|
||||
const dataList = [
|
||||
{
|
||||
team: "第一单元",
|
||||
tmyxsds: 3,
|
||||
tmyxsdx: 3,
|
||||
tmtzsds: 55,
|
||||
tmtzsdx: 55,
|
||||
tmljsds: 55,
|
||||
tmljsdx: 58,
|
||||
blyxsds: 3,
|
||||
blyxsdx: 3,
|
||||
tmljsjs: 49,
|
||||
tmljsjx: 50,
|
||||
tempera1: 103,
|
||||
tempera2: 50,
|
||||
carNumber: 14,
|
||||
statusAuto: false,
|
||||
dianji: [
|
||||
{ isStop: true, isBendi: true, eqm: "正常" },
|
||||
{ isStop: true, isBendi: true, eqm: "正常" },
|
||||
{ isStop: true, isBendi: true, eqm: "正常" },
|
||||
{ isStop: true, isBendi: true, eqm: "正常" },
|
||||
],
|
||||
},
|
||||
{
|
||||
team: "第二单元第一组",
|
||||
tmyxsds: 2,
|
||||
tmyxsdx: 3,
|
||||
tmtzsds: 120,
|
||||
tmtzsdx: 120,
|
||||
tmljsds: 57,
|
||||
tmljsdx: 57,
|
||||
blyxsds: 40,
|
||||
blyxsdx: 40,
|
||||
tmljsjs: 39,
|
||||
tmljsjx: 23,
|
||||
tempera1: 70,
|
||||
tempera2: 44,
|
||||
carNumber: 15,
|
||||
statusAuto: true,
|
||||
dianji: [
|
||||
{ isStop: true, isBendi: true, eqm: "正常" },
|
||||
{ isStop: true, isBendi: true, eqm: "正常" },
|
||||
{ isStop: true, isBendi: true, eqm: "正常" },
|
||||
{ isStop: true, isBendi: true, eqm: "正常" },
|
||||
],
|
||||
},
|
||||
{
|
||||
team: "第二单元第二组",
|
||||
tmyxsds: 2,
|
||||
tmyxsdx: 2,
|
||||
tmtzsds: 480,
|
||||
tmtzsdx: 480,
|
||||
tmljsds: 58,
|
||||
tmljsdx: 58,
|
||||
blyxsds: 40,
|
||||
blyxsdx: 40,
|
||||
tmljsjs: 12,
|
||||
tmljsjx: 9,
|
||||
tempera1: 131,
|
||||
tempera2: 91,
|
||||
carNumber: 16,
|
||||
statusAuto: true,
|
||||
dianji: [
|
||||
{ isStop: true, isBendi: true, eqm: "正常" },
|
||||
{ isStop: true, isBendi: true, eqm: "正常" },
|
||||
{ isStop: true, isBendi: true, eqm: "正常" },
|
||||
{ isStop: true, isBendi: true, eqm: "正常" },
|
||||
],
|
||||
},
|
||||
{
|
||||
team: "第二单元第三组",
|
||||
tmyxsds: 2,
|
||||
tmyxsdx: 2,
|
||||
tmtzsds: 240,
|
||||
tmtzsdx: 240,
|
||||
tmljsds: 59,
|
||||
tmljsdx: 59,
|
||||
blyxsds: 38,
|
||||
blyxsdx: 38,
|
||||
tmljsjs: 12,
|
||||
tmljsjx: 27,
|
||||
tempera1: 274,
|
||||
tempera2: 359,
|
||||
carNumber: 17,
|
||||
statusAuto: true,
|
||||
dianji: [
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
],
|
||||
},
|
||||
{
|
||||
team: "第二单元第四组",
|
||||
tmyxsds: 3,
|
||||
tmyxsdx: 3,
|
||||
tmtzsds: 120,
|
||||
tmtzsdx: 120,
|
||||
tmljsds: 60,
|
||||
tmljsdx: 60,
|
||||
blyxsds: 38,
|
||||
blyxsdx: 38,
|
||||
tmljsjs: 41,
|
||||
tmljsjx: 36,
|
||||
tempera1: 292,
|
||||
tempera2: 60,
|
||||
carNumber: 18,
|
||||
statusAuto: true,
|
||||
dianji: [
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
],
|
||||
},
|
||||
{
|
||||
team: "第二单元第五组",
|
||||
tmyxsds: 2,
|
||||
tmyxsdx: 2,
|
||||
tmtzsds: 240,
|
||||
tmtzsdx: 240,
|
||||
tmljsds: 57,
|
||||
tmljsdx: 57,
|
||||
blyxsds: 36,
|
||||
blyxsdx: 36,
|
||||
tmljsjs: 32,
|
||||
tmljsjx: 22,
|
||||
tempera1: 395,
|
||||
tempera2: 368,
|
||||
carNumber: 19,
|
||||
statusAuto: true,
|
||||
dianji: [
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
],
|
||||
},
|
||||
{
|
||||
team: "第二单元第六组",
|
||||
tmyxsds: 2,
|
||||
tmyxsdx: 2,
|
||||
tmtzsds: 180,
|
||||
tmtzsdx: 180,
|
||||
tmljsds: 60,
|
||||
tmljsdx: 60,
|
||||
blyxsds: 36,
|
||||
blyxsdx: 36,
|
||||
tmljsjs: 21,
|
||||
tmljsjx: 25,
|
||||
tempera1: 558,
|
||||
tempera2: 477,
|
||||
carNumber: 20,
|
||||
statusAuto: true,
|
||||
dianji: [
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
],
|
||||
},
|
||||
{
|
||||
team: "第三单元第一组",
|
||||
tmyxsds: 2,
|
||||
tmyxsdx: 2,
|
||||
tmtzsds: 90,
|
||||
tmtzsdx: 90,
|
||||
tmljsds: 60,
|
||||
tmljsdx: 60,
|
||||
blyxsds: 36,
|
||||
blyxsdx: 36,
|
||||
tmljsjs: 30,
|
||||
tmljsjx: 48,
|
||||
tempera1: 683,
|
||||
tempera2: 652,
|
||||
carNumber: 21,
|
||||
statusAuto: true,
|
||||
dianji: [
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
],
|
||||
},
|
||||
{
|
||||
team: "第三单元第二组",
|
||||
tmyxsds: 3,
|
||||
tmyxsdx: 3,
|
||||
tmtzsds: 90,
|
||||
tmtzsdx: 90,
|
||||
tmljsds: 60,
|
||||
tmljsdx: 60,
|
||||
blyxsds: 36,
|
||||
blyxsdx: 36,
|
||||
tmljsjs: 58,
|
||||
tmljsjx: 31,
|
||||
tempera1: 916,
|
||||
tempera2: 905,
|
||||
carNumber: 22,
|
||||
statusAuto: true,
|
||||
dianji: [
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
],
|
||||
},
|
||||
{
|
||||
team: "第三单元第三组",
|
||||
tmyxsds: 2,
|
||||
tmyxsdx: 2,
|
||||
tmtzsds: 180,
|
||||
tmtzsdx: 180,
|
||||
tmljsds: 60,
|
||||
tmljsdx: 60,
|
||||
blyxsds: 36,
|
||||
blyxsdx: 36,
|
||||
tmljsjs: 6,
|
||||
tmljsjx: 6,
|
||||
tempera1: 939,
|
||||
tempera2: 898,
|
||||
carNumber: 23,
|
||||
statusAuto: true,
|
||||
dianji: [
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
],
|
||||
},
|
||||
{
|
||||
team: "第三单元第四组",
|
||||
tmyxsds: 2,
|
||||
tmyxsdx: 2,
|
||||
tmtzsds: 480,
|
||||
tmtzsdx: 480,
|
||||
tmljsds: 60,
|
||||
tmljsdx: 60,
|
||||
blyxsds: 36,
|
||||
blyxsdx: 36,
|
||||
tmljsjs: 46,
|
||||
tmljsjx: 37,
|
||||
tempera1: 1000,
|
||||
tempera2: 995,
|
||||
carNumber: 24,
|
||||
statusAuto: true,
|
||||
dianji: [
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
],
|
||||
},
|
||||
{
|
||||
team: "第三单元第五组",
|
||||
tmyxsds: 3,
|
||||
tmyxsdx: 3,
|
||||
tmtzsds: 90,
|
||||
tmtzsdx: 90,
|
||||
tmljsds: 59,
|
||||
tmljsdx: 59,
|
||||
blyxsds: 36,
|
||||
blyxsdx: 36,
|
||||
tmljsjs: 3,
|
||||
tmljsjx: 41,
|
||||
tempera1: 967,
|
||||
tempera2: 950,
|
||||
carNumber: 25,
|
||||
statusAuto: true,
|
||||
dianji: [
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "故障" },
|
||||
],
|
||||
},
|
||||
{
|
||||
team: "第三单元第六组",
|
||||
tmyxsds: 2,
|
||||
tmyxsdx: 2,
|
||||
tmtzsds: 60,
|
||||
tmtzsdx: 60,
|
||||
tmljsds: 63,
|
||||
tmljsdx: 63,
|
||||
blyxsds: 36,
|
||||
blyxsdx: 36,
|
||||
tmljsjs: 41,
|
||||
tmljsjx: 26,
|
||||
tempera1: 919,
|
||||
tempera2: 920,
|
||||
carNumber: 26,
|
||||
statusAuto: true,
|
||||
dianji: [
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
],
|
||||
},
|
||||
{
|
||||
team: "第四单元",
|
||||
tmyxsds: 2,
|
||||
tmyxsdx: 2,
|
||||
tmtzsds: 90,
|
||||
tmtzsdx: 90,
|
||||
tmljsds: 65,
|
||||
tmljsdx: 65,
|
||||
blyxsds: 36,
|
||||
blyxsdx: 36,
|
||||
tmljsjs: 34,
|
||||
tmljsjx: 41,
|
||||
tempera1: 716,
|
||||
tempera2: 755,
|
||||
carNumber: 27,
|
||||
statusAuto: true,
|
||||
dianji: [
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
{ isStop: false, isBendi: false, eqm: "正常" },
|
||||
],
|
||||
},
|
||||
];
|
||||
const unit1 = ref(false);
|
||||
const unit2 = ref(true);
|
||||
const unit3 = ref(true);
|
||||
const unit4 = ref(true);
|
||||
// 清除错误信息
|
||||
const clearError = () => {
|
||||
error.value = null;
|
||||
};
|
||||
const onPointDragStart = (event: DragEvent, point: PointItem) => {
|
||||
// 可以使用 event.dataTransfer.setData() 来设置拖拽数据
|
||||
if (event.dataTransfer) {
|
||||
event.dataTransfer.setData('text/plain', JSON.stringify(point));
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化WebSocket连接
|
||||
const initWebSocket = () => {
|
||||
ws.value = new WebSocket(`ws://10.0.11.74:60309/ws/public`);
|
||||
|
||||
ws.value.onmessage = (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
console.log('WebSocketdata:', data);
|
||||
if (data.type === "PointNewValue") {
|
||||
pointList.value.forEach(item => {
|
||||
if (item.id === data.data.point_id) {
|
||||
item.point_monitor = data.data;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
ws.value.onclose = () => {
|
||||
console.log('WebSocket连接关闭');
|
||||
// 5秒后尝试重连
|
||||
setTimeout(initWebSocket, 5000);
|
||||
};
|
||||
|
||||
ws.value.onerror = (err) => {
|
||||
console.error('WebSocket错误:', err);
|
||||
};
|
||||
};
|
||||
const getPointList = async () => {
|
||||
clearError();
|
||||
try {
|
||||
const res = await api.pointList({});
|
||||
pointList.value = res.data || [];
|
||||
} catch (err) {
|
||||
console.error("获取点位列表失败:", err);
|
||||
}
|
||||
};
|
||||
// 组件挂载时初始化WebSocket
|
||||
onMounted(() => {
|
||||
initWebSocket();
|
||||
getPointList();
|
||||
});
|
||||
// 组件卸载时关闭WebSocket
|
||||
onUnmounted(() => {
|
||||
if (ws.value) {
|
||||
ws.value.close();
|
||||
}
|
||||
});
|
||||
const statusChange = (unit: string) => {
|
||||
if (unit == "1") {
|
||||
unit1.value = false;
|
||||
dataList.forEach((item) => {
|
||||
if (item.team.indexOf("第一单元") > -1) {
|
||||
item.statusAuto = false;
|
||||
}
|
||||
});
|
||||
} else if (unit == "2") {
|
||||
unit2.value = false;
|
||||
dataList.forEach((item) => {
|
||||
if (item.team.indexOf("第二单元") > -1) {
|
||||
item.statusAuto = false;
|
||||
}
|
||||
});
|
||||
} else if (unit == "3") {
|
||||
unit3.value = false;
|
||||
dataList.forEach((item) => {
|
||||
if (item.team.indexOf("第三单元") > -1) {
|
||||
item.statusAuto = false;
|
||||
}
|
||||
});
|
||||
} else if (unit == "4") {
|
||||
unit4.value = false;
|
||||
dataList.forEach((item) => {
|
||||
if (item.team.indexOf("第四单元") > -1) {
|
||||
item.statusAuto = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
const statusAuto = (unit: string) => {
|
||||
if (unit == "1") {
|
||||
unit1.value = true;
|
||||
dataList.forEach((item) => {
|
||||
if (item.team.indexOf("第一单元") > -1) {
|
||||
item.statusAuto = true;
|
||||
}
|
||||
});
|
||||
} else if (unit == "2") {
|
||||
unit2.value = true;
|
||||
dataList.forEach((item) => {
|
||||
if (item.team.indexOf("第二单元") > -1) {
|
||||
item.statusAuto = true;
|
||||
}
|
||||
});
|
||||
} else if (unit == "3") {
|
||||
unit3.value = true;
|
||||
dataList.forEach((item) => {
|
||||
if (item.team.indexOf("第三单元") > -1) {
|
||||
item.statusAuto = true;
|
||||
}
|
||||
});
|
||||
} else if (unit == "4") {
|
||||
unit4.value = true;
|
||||
dataList.forEach((item) => {
|
||||
if (item.team.indexOf("第四单元") > -1) {
|
||||
item.statusAuto = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.point-list {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.hover:bg_gray_100:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
th.sticky.left0,
|
||||
td.sticky.left0 {
|
||||
width: 7rem;
|
||||
min-width: 7rem;
|
||||
max-width: 7rem;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<template>
|
||||
<div class="about">
|
||||
<h1>This is an electric current page</h1>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
@media (min-width: 1024px) {
|
||||
.about {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<template>
|
||||
<div class="about">
|
||||
<h1>This is an about page</h1>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
@media (min-width: 1024px) {
|
||||
.about {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<template>
|
||||
<div class="about">
|
||||
<h1>This is an about page</h1>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
@media (min-width: 1024px) {
|
||||
.about {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
<template>
|
||||
<div class="content">
|
||||
<tiny-layout class="h_full">
|
||||
<tiny-row :flex="true" :gutter="10" class="h_full">
|
||||
<tiny-col :span="12" :no="1" class="coal_main h_full">
|
||||
<tiny-button type="info" size="small" @click="rowAdd" style="width: 50px"
|
||||
>新增</tiny-button
|
||||
>
|
||||
<tiny-grid
|
||||
border
|
||||
:data="dataList"
|
||||
:edit-config="{ trigger: 'click', mode: 'cell', showStatus: true }"
|
||||
@row-click="handleRowClick"
|
||||
auto-resize
|
||||
style="flex-grow: 1"
|
||||
>
|
||||
<tiny-grid-column field="name" title="PLC名称"></tiny-grid-column>
|
||||
<tiny-grid-column field="number" title="PLC编号"></tiny-grid-column>
|
||||
<tiny-grid-column field="points" title="PLC点位"></tiny-grid-column>
|
||||
<tiny-grid-column title="操作" width="140">
|
||||
<template #default="data">
|
||||
<tiny-button type="info" size="small" @click="rowEdit(data.row)">编辑</tiny-button>
|
||||
<tiny-button type="danger" size="small" @click="rowDel(data.row)">删除</tiny-button>
|
||||
</template>
|
||||
</tiny-grid-column>
|
||||
</tiny-grid>
|
||||
</tiny-col>
|
||||
<tiny-col :span="12" :no="2" class="coal_main h_full">
|
||||
<!-- <tiny-button type="" size="small" style="width: 50px"></tiny-button> -->
|
||||
<tiny-grid
|
||||
:data="pointList"
|
||||
:edit-config="{ trigger: 'click', mode: 'cell', showStatus: true }"
|
||||
auto-resize
|
||||
style="flex-grow: 1"
|
||||
>
|
||||
<tiny-grid-column type="index" width="60"></tiny-grid-column>
|
||||
<tiny-grid-column type="selection" width="60"></tiny-grid-column>
|
||||
<tiny-grid-column field="name" title="点位名称"></tiny-grid-column>
|
||||
<tiny-grid-column field="number" title="点位编号"></tiny-grid-column>
|
||||
<tiny-grid-column field="type" title="点位类型"></tiny-grid-column>
|
||||
<tiny-grid-column title="操作" width="80">
|
||||
<template #default="data">
|
||||
<tiny-button type="danger" size="small" @click="pointDel(data.row)"
|
||||
>删除</tiny-button
|
||||
>
|
||||
</template>
|
||||
</tiny-grid-column>
|
||||
</tiny-grid>
|
||||
</tiny-col>
|
||||
</tiny-row>
|
||||
</tiny-layout>
|
||||
<plcList_form :visible="visible" :mode="mode" @update:visible="visible = $event" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from "vue";
|
||||
import api from "../api";
|
||||
import plcList_form from "./plcList_form.vue";
|
||||
const visible = ref(false);
|
||||
const mode = ref("add");
|
||||
const plc_id = ref(null);
|
||||
type PlcItem = {
|
||||
id?: string | number;
|
||||
name: string;
|
||||
number: string;
|
||||
points: number;
|
||||
};
|
||||
type PointItem = {
|
||||
id?: string | number;
|
||||
name: string;
|
||||
number: string;
|
||||
type: string;
|
||||
};
|
||||
const dataList = ref<PlcItem[]>([]);
|
||||
const pointList = ref<PointItem[]>([]);
|
||||
const rowDel = (item: PlcItem) => {
|
||||
console.log("删除", item);
|
||||
const res = api.deletePlc(String(item.id));
|
||||
};
|
||||
const pointDel = (point: PointItem) => {
|
||||
console.log("点位删除", point);
|
||||
};
|
||||
const loadDataList = async () => {
|
||||
try {
|
||||
const result = await api.getPlcList({});
|
||||
|
||||
if (Array.isArray(result)) {
|
||||
dataList.value = result;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(result?.data)) {
|
||||
dataList.value = result.data;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(result?.dataList)) {
|
||||
dataList.value = result.dataList;
|
||||
return;
|
||||
}
|
||||
|
||||
dataList.value = [];
|
||||
} catch (error) {
|
||||
console.error("获取PLC列表失败:", error);
|
||||
dataList.value = [];
|
||||
}
|
||||
};
|
||||
const loadPointList = async (id: string | number) => {
|
||||
try {
|
||||
const result = await api.pointList({ plcId: String(id) });
|
||||
|
||||
if (Array.isArray(result)) {
|
||||
pointList.value = result;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(result?.data)) {
|
||||
pointList.value = result.data;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(result?.points)) {
|
||||
pointList.value = result.points;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(result?.data?.points)) {
|
||||
pointList.value = result.data.points;
|
||||
return;
|
||||
}
|
||||
|
||||
pointList.value = [];
|
||||
} catch (error) {
|
||||
console.error("error:", error);
|
||||
pointList.value = [];
|
||||
}
|
||||
};
|
||||
const handleRowClick = async ({ row }: { row: PlcItem }) => {
|
||||
if (!row?.id) {
|
||||
pointList.value = [];
|
||||
return;
|
||||
}
|
||||
plc_id.value = row.id as any;
|
||||
await loadPointList(row.id);
|
||||
};
|
||||
const rowAdd = () => {
|
||||
mode.value = "add";
|
||||
visible.value = true;
|
||||
};
|
||||
const rowEdit = (row: { id: any,name: string; number: string; points: number }) => {
|
||||
plc_id.value = row.id;
|
||||
mode.value = "edit";
|
||||
visible.value = true;
|
||||
};
|
||||
onMounted(() => {
|
||||
loadDataList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.content {
|
||||
height: 100%;
|
||||
}
|
||||
.tiny-button.tiny-button--small {
|
||||
padding: 0 5px !important;
|
||||
min-width: 50px !important;
|
||||
}
|
||||
@media (min-width: 1024px) {
|
||||
.about {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
<template>
|
||||
<tiny-drawer
|
||||
title="标题"
|
||||
:visible="visible"
|
||||
@update:visible="visible = $event"
|
||||
@confirm="confirm"
|
||||
>
|
||||
<div class="about">
|
||||
<tiny-form label-width="60px">
|
||||
<tiny-form-item label="ID">
|
||||
<tiny-input v-model="createData.id" placeholder="请输入ID"></tiny-input>
|
||||
</tiny-form-item>
|
||||
<tiny-form-item label="名称">
|
||||
<tiny-input v-model="createData.name" placeholder="请输入名称"></tiny-input>
|
||||
</tiny-form-item>
|
||||
<tiny-form-item label="协议">
|
||||
<tiny-input v-model="createData.protocol" placeholder="请输入协议"></tiny-input>
|
||||
</tiny-form-item>
|
||||
<tiny-form-item label="端点">
|
||||
<tiny-input v-model="createData.endpoint" placeholder="请输入端点"></tiny-input>
|
||||
</tiny-form-item>
|
||||
<tiny-form-item label="点位" v-if="mode=='edit'">
|
||||
<tiny-tree
|
||||
ref="treeRef"
|
||||
show-checkbox
|
||||
:data="pointData"
|
||||
node-key="id"
|
||||
only-check-children="true"
|
||||
:props="{ children: 'children', label: 'browse_name' }"
|
||||
@check="check"
|
||||
@check-change="checkChange"
|
||||
default-expand-all
|
||||
></tiny-tree>
|
||||
</tiny-form-item>
|
||||
<tiny-form-item>
|
||||
<tiny-button type="primary" @click="submitClick"> 提交 </tiny-button>
|
||||
</tiny-form-item>
|
||||
</tiny-form>
|
||||
</div>
|
||||
</tiny-drawer>
|
||||
</template>
|
||||
<script setup lang="jsx">
|
||||
import { ref } from "vue";
|
||||
import api from "../api";
|
||||
import treedata from '@/utils/treedata.json';
|
||||
const visible = ref(false);
|
||||
const props = defineProps({
|
||||
mode: {
|
||||
type: String,
|
||||
default: "add",
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(["update:visible"]);
|
||||
const confirm = () => {
|
||||
emit("update:visible", false);
|
||||
};
|
||||
const treeRef = ref();
|
||||
const createData = ref({
|
||||
id: "",
|
||||
name: "",
|
||||
protocol: "",
|
||||
endpoint: "",
|
||||
});
|
||||
const pointData = ref(treedata);
|
||||
// function getChecks() {
|
||||
// const currentKey = treeRef.value.getCurrentKey();
|
||||
|
||||
// // 查询勾选的值
|
||||
// const checkedKeys = treeRef.value.getCheckedKeys();
|
||||
// // 查询勾选叶子的值
|
||||
// const checkedKeysOnlyLeaf = treeRef.value.getCheckedKeys(true);
|
||||
// // 查询勾选的节点数据
|
||||
// const checkedNodes = treeRef.value.getCheckedNodes();
|
||||
// // 查询勾选的叶子节点数据,且包含半选状态的节点
|
||||
// const checkedNodesOnlyLeafAndHalf = treeRef.value.getCheckedNodes(true, true);
|
||||
// // 查询半选的值
|
||||
// const checkedHalfKeys = treeRef.value.getHalfCheckedKeys();
|
||||
// // 查询半选的节点数据
|
||||
// const checkedHalfNodes = treeRef.value.getHalfCheckedNodes();
|
||||
|
||||
// console.log("当前组件的勾选状态为: ", treeRef.value, {
|
||||
// currentKey,
|
||||
// checkedKeys,
|
||||
// checkedKeysOnlyLeaf,
|
||||
// checkedNodes,
|
||||
// checkedNodesOnlyLeafAndHalf,
|
||||
// checkedHalfKeys,
|
||||
// checkedHalfNodes,
|
||||
// });
|
||||
// }
|
||||
// function clear() {
|
||||
// treeRef.value.setCheckedKeys([]);
|
||||
// }
|
||||
// function setChecked() {
|
||||
// // setChecked: (data, checked, deep) => void,deep 为 true 时,深度勾选
|
||||
// treeRef.value.setChecked({ id: "1-1" }, true, true);
|
||||
// treeRef.value.setChecked({ id: "3" }, true, false);
|
||||
// }
|
||||
// function setCheckedByNodeKey() {
|
||||
// // setCheckedByNodeKey: (key, checked) => void
|
||||
// treeRef.value.setCheckedByNodeKey("1-1", false);
|
||||
// }
|
||||
// function setCheckedKeys() {
|
||||
// // setCheckedKeys: (keys, leafOnly)=>void
|
||||
// treeRef.value.setCheckedKeys(["1-1", "2-1", "3-1"]);
|
||||
// }
|
||||
// function setCheckedNodes() {
|
||||
// // setCheckedNodes: (nodes, leafOnly)=>void 下一行设置 leafOnly, 所以 数据 1-1 没有被勾选。
|
||||
// treeRef.value.setCheckedNodes([{ id: "1-1" }, { id: "2-1" }, { id: "3-1" }], true);
|
||||
// }
|
||||
function check(data, currentChecked) {
|
||||
console.log("check 事件:", data, currentChecked);
|
||||
}
|
||||
function checkChange(data, checked, indeterminate) {
|
||||
console.log("checkChange 事件:", data, checked, indeterminate);
|
||||
}
|
||||
|
||||
const submitClick = async () => {
|
||||
// 表单验证
|
||||
if (!createData.value.id || !createData.value.name || !createData.value.protocol || !createData.value.endpoint) {
|
||||
alert('请填写完整信息');
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取选中的点位
|
||||
// const checkedKeys = treeRef.value.getCheckedKeys();
|
||||
// if (checkedKeys.length === 0) {
|
||||
// alert('请至少选择一个点位');
|
||||
// return;
|
||||
// }
|
||||
|
||||
try {
|
||||
// 准备提交数据
|
||||
const submitData = { ...createData.value};
|
||||
|
||||
// 这里可以添加API调用
|
||||
const res = await api.createPlc(submitData);
|
||||
alert('提交成功');
|
||||
// 触发父组件刷新
|
||||
emit('refresh');
|
||||
|
||||
// 关闭对话框
|
||||
visible.value = false;
|
||||
} catch (error) {
|
||||
console.error('提交失败:', error);
|
||||
alert('提交失败,请重试');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.btn {
|
||||
width: 4rem;
|
||||
height: 1.9rem;
|
||||
color: #ffffff;
|
||||
text-align: center;
|
||||
background: rgb(var(--primary_btn));
|
||||
line-height: 1.8rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.delete_btn {
|
||||
background: rgb(var(--delete));
|
||||
}
|
||||
@media (min-width: 1024px) {
|
||||
.about {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<template>
|
||||
<div class="about">
|
||||
<h1>This is an about page</h1>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
@media (min-width: 1024px) {
|
||||
.about {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<template>
|
||||
<div class="about">
|
||||
<h1>This is an about page</h1>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
@media (min-width: 1024px) {
|
||||
.about {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<template>
|
||||
<div class="about">
|
||||
<h1>This is an about page</h1>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
@media (min-width: 1024px) {
|
||||
.about {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
||||
"exclude": ["src/**/__tests__/*"],
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.node.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
}
|
||||
],
|
||||
"compilerOptions": {
|
||||
"types": ["vite/client"],
|
||||
"moduleResolution": "node"
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"extends": "@tsconfig/node22/tsconfig.json",
|
||||
"include": [
|
||||
"vite.config.*",
|
||||
"vitest.config.*",
|
||||
"cypress.config.*",
|
||||
"nightwatch.conf.*",
|
||||
"playwright.config.*",
|
||||
"eslint.config.*"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"noEmit": true,
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"types": ["node"]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
import { fileURLToPath, URL } from "node:url";
|
||||
|
||||
import { defineConfig } from "vite";
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
// import vueDevTools from "vite-plugin-vue-devtools";
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
// plugins: [vue(), vueDevTools()],
|
||||
plugins: [vue()],
|
||||
define: {
|
||||
"process.env": { TINY_MODE: "pc" },
|
||||
},
|
||||
build: {
|
||||
sourcemap: false,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks(id) {
|
||||
if (!id.includes("node_modules")) return;
|
||||
const parts = id.split("node_modules/")[1]?.split("/");
|
||||
const name = parts?.[0]?.startsWith("@") ? `${parts[0]}/${parts[1]}` : parts?.[0];
|
||||
if (!name) return "vendor";
|
||||
// Keep big UI libs separated for better caching and smaller initial chunk.
|
||||
if (name.startsWith("@opentiny/")) return "vendor-opentiny";
|
||||
if (name === "vue" || name === "vue-router") return "vendor-vue";
|
||||
return "vendor";
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': '/src'
|
||||
// "@": fileURLToPath(new URL("./src", import.meta.url)),
|
||||
},
|
||||
}
|
||||
});
|
||||