feat: add docker CI workflow, nginx env config, and improve card/device view logic

This commit is contained in:
Vickey
2025-03-06 17:36:57 +08:00
parent 5282ef1abe
commit a141e10df5
27 changed files with 265 additions and 162 deletions
+34
View File
@@ -0,0 +1,34 @@
name: Docker Image
on:
push:
tags:
- 'dc3.release.*'
jobs:
build-push:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Login Docker
uses: docker/login-action@v3
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set QEMU
uses: docker/setup-qemu-action@v3
- name: Set Docker Buildx
uses: docker/setup-buildx-action@v3
with:
platforms: linux/arm64,linux/amd64
- name: Build and Push
uses: docker/build-push-action@v6
with:
push: true
file: Dockerfile
context: '{{defaultContext}}:/'
platforms: linux/arm64,linux/amd64
tags: pnoker/dc3-web:2025.2.dev
+2 -2
View File
@@ -15,7 +15,7 @@
#
# builder
FROM registry.cn-beijing.aliyuncs.com/dc3/dc3-node:20.12 AS builder
FROM pnoker/dc3-node:2025.2 AS builder
LABEL dc3.author pnokers
LABEL dc3.author.email pnokers.icloud.com
@@ -29,7 +29,7 @@ RUN yarn
RUN yarn build
# runtime
FROM registry.cn-beijing.aliyuncs.com/dc3/dc3-nginx:1.26 AS runtime
FROM pnoker/dc3-nginx:2025.2 AS runtime
LABEL dc3.author pnokers
LABEL dc3.author.email pnokers.icloud.com
+1 -1
View File
@@ -19,7 +19,7 @@ services:
build:
context: ../
dockerfile: ./Dockerfile
image: registry.cn-beijing.aliyuncs.com/dc3/dc3-web:2024.3.0.dev
image: pnoker/dc3-web:2024.3.0.dev
restart: always
ports:
- '8080:80'
+5
View File
@@ -19,6 +19,11 @@ location / {
index index.html index.htm;
}
location /stub_status {
stub_status on;
access_log off;
}
location ^~/api/ {
proxy_pass http://dc3-gateway:8000/api/;
}
+5
View File
@@ -19,6 +19,11 @@ location / {
index index.html index.htm;
}
location /stub_status {
stub_status on;
access_log off;
}
location ^~/api/ {
proxy_pass http://${APP_API_HOST}:${APP_API_PORT}/api/;
}
+23 -23
View File
@@ -22,57 +22,57 @@
},
"dependencies": {
"@amap/amap-jsapi-loader": "^1.0.1",
"@antv/g2": "^5.2.10",
"@antv/g2": "^5.2.11",
"@element-plus/icons-vue": "^2.3.1",
"@tauri-apps/api": "^2.2.0",
"@tauri-apps/api": "^2.3.0",
"@tauri-apps/plugin-shell": "^2.2.0",
"axios": "^1.7.9",
"axios": "^1.8.1",
"clipboard": "^2.0.11",
"core-js": "^3.40.0",
"core-js": "^3.41.0",
"echarts": "^5.6.0",
"element-plus": "^2.9.4",
"element-plus": "^2.9.5",
"highlight.js": "^11.11.1",
"js-base64": "^3.7.7",
"js-cookie": "^3.0.5",
"js-md5": "^0.8.3",
"json-bigint": "^1.0.0",
"marked": "^15.0.6",
"marked": "^15.0.7",
"mitt": "^3.0.1",
"moment": "^2.30.1",
"nprogress": "^0.2.0",
"pinia": "^2.3.1",
"pinia": "^3.0.1",
"screenfull": "^6.0.2",
"vue": "^3.5.13",
"vue-router": "^4.5.0",
"vuex": "^4.1.0"
},
"devDependencies": {
"@tauri-apps/cli": "^2.2.7",
"@tauri-apps/cli": "^2.3.1",
"@types/js-cookie": "^3.0.6",
"@types/json-bigint": "^1.0.4",
"@types/node": "^22.13.1",
"@types/node": "^22.13.9",
"@types/nprogress": "^0.2.3",
"@typescript-eslint/eslint-plugin": "^8.23.0",
"@typescript-eslint/parser": "^8.23.0",
"@vitejs/plugin-legacy": "^6.0.1",
"@typescript-eslint/eslint-plugin": "^8.26.0",
"@typescript-eslint/parser": "^8.26.0",
"@vitejs/plugin-legacy": "^6.0.2",
"@vitejs/plugin-vue": "^5.2.1",
"dotenv": "^16.4.7",
"eslint": "^9.20.0",
"eslint-config-prettier": "^10.0.1",
"eslint": "^9.21.0",
"eslint-config-prettier": "^10.0.2",
"eslint-plugin-prettier": "^5.2.3",
"eslint-plugin-vue": "^9.32.0",
"eslint-plugin-vue": "^10.0.0",
"minimist": "^1.2.8",
"path": "^0.12.7",
"prettier": "^3.4.2",
"prettier": "^3.5.3",
"prettier-eslint": "^16.3.0",
"sass": "^1.84.0",
"terser": "^5.38.1",
"typescript": "~5.7.3",
"unplugin-auto-import": "^19.0.0",
"unplugin-vue-components": "^28.0.0",
"vite": "^6.1.0",
"sass": "^1.85.1",
"terser": "^5.39.0",
"typescript": "~5.8.2",
"unplugin-auto-import": "^19.1.1",
"unplugin-vue-components": "^28.4.1",
"vite": "^6.2.0",
"vue-class-component": "^8.0.0-0",
"vue-template-compiler": "^2.7.16",
"vue-tsc": "^2.2.0"
"vue-tsc": "^2.2.8"
}
}
+1 -1
View File
@@ -50,7 +50,7 @@
}
.el-card__body {
padding: 10px 5px 0px 5px;
padding: 10px 5px 0 5px;
}
.el-tabs__nav {
+2 -2
View File
@@ -24,7 +24,7 @@
</div>
</template>
<script lang="ts" setup />
<script lang="ts" setup></script>
<style lang="scss">
.detail-card {
@@ -46,7 +46,7 @@
}
.el-card__body {
padding: 10px 5px 0px 5px;
padding: 10px 5px 0 5px;
}
.el-tabs__nav {
+39 -32
View File
@@ -15,40 +15,47 @@
-->
<template>
<el-skeleton :loading="props.loading" animated>
<el-skeleton :loading="props.loading" :throttle="{ leading: 500 }" animated>
<template #template>
<div class="skeleton-card">
<el-card shadow="hover">
<div class="skeleton-card__container">
<div class="skeleton-card__header">
<el-skeleton-item ariant="image" class="skeleton-card-icon" />
<el-skeleton-item class="skeleton-card-name" variant="text" />
</div>
<div class="skeleton-card__body">
<div class="skeleton-card-content">
<ul>
<el-skeleton-item class="skeleton-card-text" variant="text" />
<el-skeleton-item class="skeleton-card-text" variant="text" />
<el-skeleton-item class="skeleton-card-text" variant="text" />
<el-skeleton-item class="skeleton-card-text" variant="text" />
</ul>
<el-row>
<el-col v-for="data in 12" :key="data" :lg="6" :md="8" :sm="12" :xl="4" :xs="24">
<div class="skeleton-card">
<el-card shadow="hover">
<div class="skeleton-card__container">
<div class="skeleton-card__header">
<el-skeleton-item ariant="image" class="skeleton-card-icon" />
<el-skeleton-item class="skeleton-card-name" variant="text" />
</div>
<div class="skeleton-card__body">
<div class="skeleton-card-content">
<ul>
<el-skeleton-item class="skeleton-card-text" variant="text" />
<el-skeleton-item class="skeleton-card-text" variant="text" />
<el-skeleton-item class="skeleton-card-text" variant="text" />
<el-skeleton-item class="skeleton-card-text" variant="text" />
</ul>
</div>
<div class="skeleton-card-content">
<el-skeleton-item class="skeleton-card-description" variant="text" />
</div>
</div>
<div v-if="props.footer" class="skeleton-card__footer">
<div class="skeleton-card-operation">
<el-skeleton-item class="skeleton-card-button" variant="button" />
<el-skeleton-item class="skeleton-card-button" variant="button" />
<el-skeleton-item class="skeleton-card-button" variant="button" />
<el-skeleton-item class="skeleton-card-button" variant="button" />
<el-skeleton-item class="skeleton-card-button" variant="button" />
</div>
</div>
</div>
<div class="skeleton-card-content">
<el-skeleton-item class="skeleton-card-description" variant="text" />
</div>
</div>
<div v-if="!props.footer" class="skeleton-card__footer">
<div class="skeleton-card-operation">
<el-skeleton-item class="skeleton-card-button" variant="button" />
<el-skeleton-item class="skeleton-card-button" variant="button" />
<el-skeleton-item class="skeleton-card-button" variant="button" />
<el-skeleton-item class="skeleton-card-button" variant="button" />
<el-skeleton-item class="skeleton-card-button" variant="button" />
</div>
</div>
</el-card>
</div>
</el-card>
</div>
</el-col>
</el-row>
</template>
<template #default>
<slot />
</template>
</el-skeleton>
</template>
@@ -64,7 +71,7 @@ const props = defineProps({
footer: {
type: Boolean,
default: () => {
return false
return true
}
}
})
+43 -43
View File
@@ -18,50 +18,50 @@
margin-top: 1px;
margin-bottom: 4px;
.tool-card__body {
display: flex;
flex-direction: column;
.tool-card-body-form {
display: flex;
justify-content: center;
}
.tool-card-body-button {
.el-form-item__content {
display: flex;
justify-content: center;
}
}
}
.tool-card__footer {
display: flex;
justify-content: space-between;
.tool-card-footer-button {
display: flex;
}
.tool-card-footer-page {
display: flex;
}
}
.tool-select {
.tool-select-input {
margin-left: 10px;
margin-right: 10px;
margin-bottom: 5px;
}
.tool-select-pagination {
justify-content: center;
margin-bottom: 5px;
}
}
.el-card {
border: 0;
}
}
.tool-card__body {
display: flex;
flex-direction: column;
.tool-card-body-form {
display: flex;
justify-content: center;
}
.tool-card-body-button {
.el-form-item__content {
display: flex;
justify-content: center;
}
}
}
.tool-card__footer {
display: flex;
justify-content: space-between;
.tool-card-footer-button {
display: flex;
}
.tool-card-footer-page {
display: flex;
}
}
.tool-select {
.tool-select-input {
margin-left: 10px;
margin-right: 10px;
margin-bottom: 5px;
}
.tool-select-pagination {
justify-content: center;
margin-bottom: 5px;
}
}
+22 -20
View File
@@ -41,31 +41,33 @@ const props = defineProps({
border-radius: 4px;
box-sizing: border-box;
.title-card__header {
font-size: 14px;
font-weight: bold;
}
.title-card__container {
.title-card__header {
font-size: 14px;
font-weight: bold;
}
&:first-child {
padding-top: 0;
padding-left: 0;
}
&:first-child {
padding-top: 0;
padding-left: 0;
}
.el-card {
width: 100%;
box-sizing: border-box;
}
.el-card {
width: 100%;
box-sizing: border-box;
}
.el-card__header {
padding: 10px;
}
.el-card__header {
padding: 10px;
}
.el-card__body {
padding: 10px 5px 0px 5px;
}
.el-card__body {
padding: 10px 5px 0px 5px;
}
.el-tabs__nav {
margin: 0 5px 0 5px;
.el-tabs__nav {
margin: 0 5px 0 5px;
}
}
}
</style>
+35 -17
View File
@@ -31,24 +31,42 @@
<blank-card>
<el-row>
<el-col v-for="data in 12" :key="data" :lg="6" :md="8" :sm="12" :xl="4" :xs="24">
<skeleton-card :loading="reactiveData.loading" />
</el-col>
<el-col v-if="hasData">
<el-empty description="暂无设备数据!" />
</el-col>
<el-col v-for="data in reactiveData.listData" :key="data.id" :lg="6" :md="8" :sm="12" :xl="4" :xs="24">
<device-card
:data="data"
:driver="reactiveData.driverTable[data.driverId]"
:embedded="embedded != ''"
:status="reactiveData.statusTable[data.id]"
@disable-thing="disableThing"
@enable-thing="enableThing"
@delete-thing="deleteThing"
/>
</el-col>
<skeleton-card :footer="true" :loading="reactiveData.loading">
<el-col v-if="hasData">
<el-empty description="暂无设备数据!" />
</el-col>
<el-col v-for="data in reactiveData.listData" :key="data.id" :lg="6" :md="8" :sm="12" :xl="4" :xs="24">
<device-card
:data="data"
:driver="reactiveData.driverTable[data.driverId]"
:embedded="embedded != ''"
:status="reactiveData.statusTable[data.id]"
@disable-thing="disableThing"
@enable-thing="enableThing"
@delete-thing="deleteThing"
/>
</el-col>
</skeleton-card>
</el-row>
<!-- <el-row>
<el-col v-for="data in 12" :key="data" :lg="6" :md="8" :sm="12" :xl="4" :xs="24">
<skeleton-card :footer="true" :loading="reactiveData.loading" />
</el-col>
<el-col v-if="hasData">
<el-empty description="暂无设备数据!" />
</el-col>
<el-col v-for="data in reactiveData.listData" :key="data.id" :lg="6" :md="8" :sm="12" :xl="4" :xs="24">
<device-card
:data="data"
:driver="reactiveData.driverTable[data.driverId]"
:embedded="embedded != ''"
:status="reactiveData.statusTable[data.id]"
@disable-thing="disableThing"
@enable-thing="enableThing"
@delete-thing="deleteThing"
/>
</el-col>
</el-row>-->
</blank-card>
<device-add-form ref="deviceAddFormRef" @add-thing="addThing" />
-2
View File
@@ -44,7 +44,6 @@
background
class="tool-select-pagination"
layout="prev, pager, next"
small
@current-change="driverCurrentChange"
></el-pagination>
</div>
@@ -73,7 +72,6 @@
background
class="tool-select-pagination"
layout="prev, pager, next"
small
@current-change="profileCurrentChange"
></el-pagination>
</div>
+1 -3
View File
@@ -59,7 +59,6 @@
background
class="tool-select-pagination"
layout="prev, pager, next"
small
@current-change="driverCurrentChange"
></el-pagination>
</div>
@@ -103,7 +102,6 @@
background
class="tool-select-pagination"
layout="prev, pager, next"
small
@current-change="profileCurrentChange"
></el-pagination>
</div>
@@ -197,7 +195,7 @@
</el-form-item>
<el-row>
<el-col v-for="data in 12" :key="data" :lg="6" :md="8" :sm="12" :xl="4" :xs="24">
<skeleton-card :footer="true" :loading="reactiveData.loading"></skeleton-card>
<skeleton-card :footer="false" :loading="reactiveData.loading"></skeleton-card>
</el-col>
<el-col v-for="data in reactiveData.pointInfoData" :key="data.id" :lg="6" :md="8" :sm="12" :xl="4" :xs="24">
<point-info-card :attributes="reactiveData.pointAttributes" :data="data" @select="selectPoint"></point-info-card>
+8 -1
View File
@@ -24,7 +24,14 @@ import router from '@/config/router'
import { getDriverDictionary, getProfileDictionary } from '@/api/dictionary'
import { getDeviceById, updateDevice } from '@/api/device'
import { getDriverAttributeByDriverId, getPointAttributeByDriverId } from '@/api/attribute'
import { addDriverInfo, addPointInfo, getDriverInfoByDeviceId, getPointInfoByDeviceId, updateDriverInfo, updatePointInfo } from '@/api/info'
import {
addDriverInfo,
addPointInfo,
getDriverInfoByDeviceId,
getPointInfoByDeviceId,
updateDriverInfo,
updatePointInfo
} from '@/api/info'
import { Dictionary, Order } from '@/config/entity'
@@ -31,7 +31,6 @@
<p>提示建议每次导入前下载新模板, 避免模板配置数据不一致</p>
<p>方法先选择驱动和模板, 然后点击生成导入模板, 再将待导入的设备数据填写到导入模板, 最后上传导入数据</p>
</el-alert>
<dev></dev>
<el-form-item class="things-dialog-form-item" label="所属驱动" prop="driverId">
<el-select v-model="reactiveData.formData.driverId" class="edit-form-special" clearable placeholder="请选择所属驱动" @visible-change="driverDictionaryVisible">
<div class="tool-select">
@@ -47,7 +46,6 @@
background
class="tool-select-pagination"
layout="prev, pager, next"
small
@current-change="driverCurrentChange"
></el-pagination>
</div>
@@ -76,7 +74,6 @@
background
class="tool-select-pagination"
layout="prev, pager, next"
small
@current-change="profileCurrentChange"
></el-pagination>
</div>
+9 -1
View File
@@ -16,7 +16,15 @@
import { computed, defineComponent, reactive, ref } from 'vue'
import { addDevice, deleteDevice, getDeviceList, getDeviceStatus, importDevice, importDeviceTemplate, updateDevice } from '@/api/device'
import {
addDevice,
deleteDevice,
getDeviceList,
getDeviceStatus,
importDevice,
importDeviceTemplate,
updateDevice
} from '@/api/device'
import { getDriverByIds } from '@/api/driver'
import { Order } from '@/config/entity'
-1
View File
@@ -44,7 +44,6 @@
background
class="tool-select-pagination"
layout="prev, pager, next"
small
@current-change="driverCurrentChange"
></el-pagination>
</div>
+1 -1
View File
@@ -22,7 +22,7 @@
<blank-card>
<el-row>
<el-col v-for="data in 12" :key="data.id" :lg="6" :md="8" :sm="12" :xl="4" :xs="24">
<skeleton-card :loading="reactiveData.loading"></skeleton-card>
<skeleton-card :footer="true" :loading="reactiveData.loading"></skeleton-card>
</el-col>
<el-col v-if="hasData">
<el-empty description="暂无驱动数据!"></el-empty>
+10 -1
View File
@@ -15,7 +15,16 @@
*/
import { computed, defineComponent } from 'vue'
import { CircleCheck, CircleClose, Connection, Edit, Monitor, Promotion, Sunset, SwitchButton } from '@element-plus/icons-vue'
import {
CircleCheck,
CircleClose,
Connection,
Edit,
Monitor,
Promotion,
Sunset,
SwitchButton
} from '@element-plus/icons-vue'
import router from '@/config/router'
import { copy, timestamp } from '@/utils/CommonUtil'
+1 -1
View File
@@ -35,7 +35,7 @@
<blank-card>
<el-row>
<el-col v-for="data in 12" :key="data" :lg="8" :md="12" :sm="12" :xl="6" :xs="24">
<skeleton-card :loading="reactiveData.loading"></skeleton-card>
<skeleton-card :footer="true" :loading="reactiveData.loading"></skeleton-card>
</el-col>
<el-col v-if="hasData">
<el-empty description="暂无位号数据!"></el-empty>
+11 -1
View File
@@ -15,7 +15,17 @@
*/
import { computed, defineComponent } from 'vue'
import { Bottom, CircleCheck, CircleClose, Edit, List, Location, Sunset, SwitchButton, Top } from '@element-plus/icons-vue'
import {
Bottom,
CircleCheck,
CircleClose,
Edit,
List,
Location,
Sunset,
SwitchButton,
Top
} from '@element-plus/icons-vue'
import router from '@/config/router'
-1
View File
@@ -43,7 +43,6 @@
background
class="tool-select-pagination"
layout="prev, pager, next"
small
@current-change="profileCurrentChange"
></el-pagination>
</div>
+1 -1
View File
@@ -29,7 +29,7 @@
<blank-card>
<el-row>
<el-col v-for="data in 12" :key="data" :lg="6" :md="8" :sm="12" :xl="4" :xs="24">
<skeleton-card :loading="reactiveData.loading"></skeleton-card>
<skeleton-card :footer="true" :loading="reactiveData.loading"></skeleton-card>
</el-col>
<el-col v-if="hasData">
<el-empty :description="embedded == 'device' ? '暂无设备数据' : '暂无数据, 请选择设备和位号!'"></el-empty>
@@ -41,7 +41,6 @@
background
class="tool-select-pagination"
layout="prev, pager, next"
small
@current-change="deviceCurrentChange"
></el-pagination>
</div>
@@ -70,7 +69,6 @@
background
class="tool-select-pagination"
layout="prev, pager, next"
small
@current-change="pointCurrentChange"
></el-pagination>
</div>
+1 -1
View File
@@ -31,7 +31,7 @@
<blank-card>
<el-row>
<el-col v-for="data in 12" :key="data" :lg="6" :md="8" :sm="12" :xl="4" :xs="24">
<skeleton-card :loading="reactiveData.loading"></skeleton-card>
<skeleton-card :footer="true" :loading="reactiveData.loading"></skeleton-card>
</el-col>
<el-col v-if="hasData">
<el-empty description="暂无模板数据!"></el-empty>
+10 -1
View File
@@ -15,7 +15,16 @@
*/
import { defineComponent } from 'vue'
import { CircleCheck, CircleClose, CollectionTag, Edit, IceCreamSquare, Right, Sunset, SwitchButton } from '@element-plus/icons-vue'
import {
CircleCheck,
CircleClose,
CollectionTag,
Edit,
IceCreamSquare,
Right,
Sunset,
SwitchButton
} from '@element-plus/icons-vue'
import router from '@/config/router'