页面完成
This commit is contained in:
parent
c0df5c0869
commit
665b1efdae
@ -1,5 +1,5 @@
|
||||
# 页面标题
|
||||
VITE_APP_TITLE = 若依管理系统
|
||||
VITE_APP_TITLE = 测偏仪后台管理系统
|
||||
|
||||
# 开发环境配置
|
||||
VITE_APP_ENV = 'development'
|
||||
|
@ -1,11 +1,11 @@
|
||||
# 页面标题
|
||||
VITE_APP_TITLE = 若依管理系统
|
||||
VITE_APP_TITLE = 测偏仪后台管理系统
|
||||
|
||||
# 生产环境配置
|
||||
VITE_APP_ENV = 'production'
|
||||
|
||||
# 若依管理系统/生产环境
|
||||
VITE_APP_BASE_API = '/prod-api'
|
||||
VITE_APP_BASE_API = '/api'
|
||||
|
||||
# 是否在打包时开启压缩,支持 gzip 和 brotli
|
||||
VITE_BUILD_COMPRESS = gzip
|
49
.gitignore
vendored
49
.gitignore
vendored
@ -1,26 +1,33 @@
|
||||
# ---> Java
|
||||
# Compiled class file
|
||||
*.class
|
||||
.DS_Store
|
||||
.history
|
||||
node_modules/
|
||||
dist/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
**/*.log
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
tests/**/coverage/
|
||||
tests/e2e/reports
|
||||
selenium-debug.log
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.local
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
pnpm-lock.yaml
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
replay_pid*
|
||||
# 编译生成的文件
|
||||
auto-imports.d.ts
|
||||
components.d.ts
|
||||
|
||||
# 项目配置文件
|
||||
vite.config.ts
|
||||
dist.zip
|
||||
|
83
package-lock.json
generated
83
package-lock.json
generated
@ -24,8 +24,11 @@
|
||||
"nprogress": "0.2.0",
|
||||
"pinia": "2.1.7",
|
||||
"splitpanes": "3.1.5",
|
||||
"typescript": "^5.8.3",
|
||||
"vue": "3.4.31",
|
||||
"vue-clipboard3": "^2.0.0",
|
||||
"vue-cropper": "1.1.1",
|
||||
"vue-i18n": "^11.1.3",
|
||||
"vue-router": "4.4.0",
|
||||
"vuedraggable": "4.1.0"
|
||||
},
|
||||
@ -529,6 +532,47 @@
|
||||
"integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@intlify/core-base": {
|
||||
"version": "11.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/@intlify/core-base/-/core-base-11.1.3.tgz",
|
||||
"integrity": "sha512-cMuHunYO7LE80azTitcvEbs1KJmtd6g7I5pxlApV3Jo547zdO3h31/0uXpqHc+Y3RKt1wo2y68RGSx77Z1klyA==",
|
||||
"dependencies": {
|
||||
"@intlify/message-compiler": "11.1.3",
|
||||
"@intlify/shared": "11.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/kazupon"
|
||||
}
|
||||
},
|
||||
"node_modules/@intlify/message-compiler": {
|
||||
"version": "11.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-11.1.3.tgz",
|
||||
"integrity": "sha512-7rbqqpo2f5+tIcwZTAG/Ooy9C8NDVwfDkvSeDPWUPQW+Dyzfw2o9H103N5lKBxO7wxX9dgCDjQ8Umz73uYw3hw==",
|
||||
"dependencies": {
|
||||
"@intlify/shared": "11.1.3",
|
||||
"source-map-js": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/kazupon"
|
||||
}
|
||||
},
|
||||
"node_modules/@intlify/shared": {
|
||||
"version": "11.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/@intlify/shared/-/shared-11.1.3.tgz",
|
||||
"integrity": "sha512-pTFBgqa/99JRA2H1qfyqv97MKWJrYngXBA/I0elZcYxvJgcCw3mApAoPW3mJ7vx3j+Ti0FyKUFZ4hWxdjKaxvA==",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/kazupon"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/cliui": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||
@ -6566,6 +6610,18 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.8.3.tgz",
|
||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/ufo": {
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz",
|
||||
@ -7014,12 +7070,39 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vue-clipboard3": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/vue-clipboard3/-/vue-clipboard3-2.0.0.tgz",
|
||||
"integrity": "sha512-Q9S7dzWGax7LN5iiSPcu/K1GGm2gcBBlYwmMsUc5/16N6w90cbKow3FnPmPs95sungns4yvd9/+JhbAznECS2A==",
|
||||
"dependencies": {
|
||||
"clipboard": "^2.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-cropper": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-cropper/-/vue-cropper-1.1.1.tgz",
|
||||
"integrity": "sha512-WsqKMpaBf9Osi1LQlE/5AKdD0nHWOy1asLXocaG8NomOWO07jiZi968+/PbMmnD0QbPJOumDQaGuGa13qys85A==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/vue-i18n": {
|
||||
"version": "11.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-11.1.3.tgz",
|
||||
"integrity": "sha512-Pcylh9z9S5+CJAqgbRZ3EKxFIBIrtY5YUppU722GIT65+Nukm0TCqiQegZnNLCZkXGthxe0cpqj0AoM51H+6Gw==",
|
||||
"dependencies": {
|
||||
"@intlify/core-base": "11.1.3",
|
||||
"@intlify/shared": "11.1.3",
|
||||
"@vue/devtools-api": "^6.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/kazupon"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-router": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.4.0.tgz",
|
||||
|
@ -31,8 +31,11 @@
|
||||
"nprogress": "0.2.0",
|
||||
"pinia": "2.1.7",
|
||||
"splitpanes": "3.1.5",
|
||||
"typescript": "^5.8.3",
|
||||
"vue": "3.4.31",
|
||||
"vue-clipboard3": "^2.0.0",
|
||||
"vue-cropper": "1.1.1",
|
||||
"vue-i18n": "^11.1.3",
|
||||
"vue-router": "4.4.0",
|
||||
"vuedraggable": "4.1.0"
|
||||
},
|
||||
|
92
src/api/customer/index.js
Normal file
92
src/api/customer/index.js
Normal file
@ -0,0 +1,92 @@
|
||||
import request from '@/utils/request';
|
||||
// 获取客户列表
|
||||
export function getCustomerListApi(query) {
|
||||
return request({
|
||||
url: '/system/dept/customerList',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
// 获取客户列表
|
||||
export function getDepListApi() {
|
||||
return request({
|
||||
url: '/system/dept/list',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
//新增客户-部门管理
|
||||
export function addDepartmentApi(data) {
|
||||
return request({
|
||||
url: '/system/dept',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
//编辑客户-部门管理
|
||||
export function editDepartmentApi(data) {
|
||||
return request({
|
||||
url: '/system/dept',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
//查询客户-部门管理详情
|
||||
export function getDepartmentDetailApi(id) {
|
||||
return request({
|
||||
url: '/system/dept/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
//删除客户-部门管理
|
||||
export function deleteDepartmentApi(id) {
|
||||
return request({
|
||||
url: '/system/dept/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
//获取客户-部门管理下拉树结构
|
||||
export function getDepartmentTreeApi() {
|
||||
return request({
|
||||
url: '/system/user/deptTree',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
//获取客户-部门-岗位关联列表
|
||||
export function getDepartmentPostListApi(query) {
|
||||
return request({
|
||||
url: '/customer/dept/post/rel/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
//新增客户-部门-岗位关联
|
||||
export function addDepartmentPostApi(data) {
|
||||
return request({
|
||||
url: '/customer/dept/post/rel',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
//编辑客户-部门-岗位关联
|
||||
export function editDepartmentPostApi(data) {
|
||||
return request({
|
||||
url: '/customer/dept/post/rel',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
//删除客户-部门-岗位关联
|
||||
export function deleteDepartmentPostApi(id) {
|
||||
return request({
|
||||
url: '/customer/dept/post/rel/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
//获取客户-部门-岗位关联详情
|
||||
export function getDepartmentPostDetailApi(id) {
|
||||
return request({
|
||||
url: '/customer/dept/post/rel/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
22
src/api/dashboard.js
Normal file
22
src/api/dashboard.js
Normal file
@ -0,0 +1,22 @@
|
||||
import request from '@/utils/request'
|
||||
//获取数据总览
|
||||
export function getDashboardApi() {
|
||||
return request({
|
||||
url: '/index/overview',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
// 维保分析
|
||||
export function getMaintainAnalysisApi() {
|
||||
return request({
|
||||
url: '/index/maintenanceAnalysis',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
//维保汇总
|
||||
export function getMaintainSummaryApi() {
|
||||
return request({
|
||||
url: '/index/maintenanceOverview',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
99
src/api/equipment/index.js
Normal file
99
src/api/equipment/index.js
Normal file
@ -0,0 +1,99 @@
|
||||
import request from '@/utils/request';
|
||||
import {getDicts} from "@/api/system/dict/data.js";
|
||||
//获取设备型号列表
|
||||
export function getDeviceModelListApi(query) {
|
||||
return request({
|
||||
url: '/device/model/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
//新增设备型号
|
||||
export function addDeviceModelApi(data) {
|
||||
return request({
|
||||
url: '/device/model',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
//编辑设备型号
|
||||
export function editDeviceModelApi(data) {
|
||||
return request({
|
||||
url: '/device/model',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
//查询设备型号详情
|
||||
export function getDeviceModelDetailApi(id) {
|
||||
return request({
|
||||
url: '/device/model/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
//删除设备型号
|
||||
export function deleteDeviceModelApi(id) {
|
||||
return request({
|
||||
url: '/device/model/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
//获取设备信息列表
|
||||
export function getDeviceListApi(query) {
|
||||
return request({
|
||||
url: '/device/info/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
//新增设备信息
|
||||
export function addDeviceApi(data) {
|
||||
return request({
|
||||
url: '/device/info',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
//编辑设备信息
|
||||
export function editDeviceApi(data) {
|
||||
return request({
|
||||
url: '/device/info',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
//查询设备信息详情
|
||||
export function getDeviceDetailApi(id) {
|
||||
return request({
|
||||
url: '/device/info/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
//删除设备信息
|
||||
export function deleteDeviceApi(id) {
|
||||
return request({
|
||||
url: '/device/info/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
//获取设备下拉列表
|
||||
export function getDeviceSelectApi() {
|
||||
return request({
|
||||
url: '/device/info/dropList',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
//获取用户下拉列表
|
||||
export function getUserSelectApi() {
|
||||
return request({
|
||||
url: '/system/user/dropList',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
//查看鉴权信息
|
||||
export function getAuthInfoApi(id) {
|
||||
return request({
|
||||
url: '/emqx/auth/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
@ -57,4 +57,16 @@ export function getCodeImg() {
|
||||
method: 'get',
|
||||
timeout: 20000
|
||||
})
|
||||
}
|
||||
export function changeLanguage(lang){
|
||||
return request({
|
||||
url: '/changeLanguage',
|
||||
method: 'get',
|
||||
headers: {
|
||||
isToken: false,
|
||||
},
|
||||
params: {
|
||||
lang: lang
|
||||
}
|
||||
})
|
||||
}
|
47
src/api/maintenance/index.js
Normal file
47
src/api/maintenance/index.js
Normal file
@ -0,0 +1,47 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
// 获取设备维保记录
|
||||
export function getMaintainListApi(query) {
|
||||
return request({
|
||||
url: '/maintain/record/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
//新增维保记录
|
||||
export function addMaintainApi(data) {
|
||||
return request({
|
||||
url: '/maintain/record',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
//编辑维保记录
|
||||
export function editMaintainApi(data) {
|
||||
return request({
|
||||
url: '/maintain/record',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
//查询维保记录详情
|
||||
export function getMaintainDetailApi(id) {
|
||||
return request({
|
||||
url: '/maintain/record/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
//获取维保类型
|
||||
export function getMaintainTypeApi() {
|
||||
return request({
|
||||
url: '/system/dict/data/type/' + 'tg_maintain_type',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
//获取设备编码列表
|
||||
export function getDeviceCodeListApi() {
|
||||
return request({
|
||||
url: '/device/info/dropList',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
BIN
src/assets/dashboard/customerNum.png
Normal file
BIN
src/assets/dashboard/customerNum.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.0 KiB |
BIN
src/assets/dashboard/deviceNum.png
Normal file
BIN
src/assets/dashboard/deviceNum.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.1 KiB |
BIN
src/assets/dashboard/maintenanceNum.png
Normal file
BIN
src/assets/dashboard/maintenanceNum.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.7 KiB |
BIN
src/assets/dashboard/offlineDevice.png
Normal file
BIN
src/assets/dashboard/offlineDevice.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.9 KiB |
BIN
src/assets/dashboard/onlineDevice.png
Normal file
BIN
src/assets/dashboard/onlineDevice.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.3 KiB |
BIN
src/assets/images/loginBg.jpg
Normal file
BIN
src/assets/images/loginBg.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 MiB |
@ -52,6 +52,7 @@
|
||||
left: 0;
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
top: 40px;
|
||||
}
|
||||
|
||||
// refine element ui upload
|
||||
|
@ -9,10 +9,14 @@ $yellow: #FEC171;
|
||||
$panGreen: #30B08F;
|
||||
|
||||
// 默认主题变量
|
||||
$menuText: #bfcbd9;
|
||||
$menuActiveText: #409eff;
|
||||
$menuBg: #304156;
|
||||
$menuHover: #263445;
|
||||
//$menuText: #bfcbd9;
|
||||
$menuText: #000;
|
||||
$menuActiveText: #fff;
|
||||
//$menuActiveText: #409eff;
|
||||
//$menuBg: #304156;
|
||||
$menuBg: #ffffff;
|
||||
//$menuHover: #263445;
|
||||
$menuHover: #f0f1f5;
|
||||
|
||||
// 浅色主题theme-light
|
||||
$menuLightBg: #ffffff;
|
||||
@ -21,15 +25,20 @@ $menuLightText: #303133;
|
||||
$menuLightActiveText: #409EFF;
|
||||
|
||||
// 基础变量
|
||||
$base-sidebar-width: 200px;
|
||||
$sideBarWidth: 200px;
|
||||
$base-sidebar-width: 180px;
|
||||
$sideBarWidth: 180px;
|
||||
|
||||
// 菜单暗色变量
|
||||
//$base-menu-color: #bfcbd9;
|
||||
//$base-menu-color-active: #f4f4f5;
|
||||
//$base-menu-background: #304156;
|
||||
//$base-sub-menu-background: #1f2d3d;
|
||||
//$base-sub-menu-hover: #001528;
|
||||
$base-menu-color: #bfcbd9;
|
||||
$base-menu-color-active: #f4f4f5;
|
||||
$base-menu-background: #304156;
|
||||
$base-sub-menu-background: #1f2d3d;
|
||||
$base-sub-menu-hover: #001528;
|
||||
$base-menu-color-active: #409EFF;
|
||||
$base-menu-background: #409EFF;
|
||||
$base-sub-menu-background:#fff;
|
||||
$base-sub-menu-hover:#f4f4f5;
|
||||
|
||||
// 组件变量
|
||||
$--color-primary: #409EFF;
|
||||
|
97
src/components/DashChart.vue
Normal file
97
src/components/DashChart.vue
Normal file
@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<div ref="chartRef" style="width: 100%; height: 400px"></div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
const props = defineProps({
|
||||
chartData: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
seriesData: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
const chartRef = ref(null)
|
||||
let myChart = null
|
||||
|
||||
const initChart = () => {
|
||||
if (myChart) {
|
||||
myChart.dispose()
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
myChart = echarts.init(chartRef.value)
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: props.seriesData.map(item => item.name) // 动态生成图例
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data:Array.isArray(props.chartData) ? props.chartData.map(item => item.name || '') : [],
|
||||
axisLabel: {
|
||||
interval: 0,
|
||||
rotate: 30 // 斜着显示避免重叠
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
minInterval: 1, // 最小间隔设为1
|
||||
axisLabel: {
|
||||
formatter: '{value}' // 使用整数格式
|
||||
}
|
||||
},
|
||||
series: props.seriesData.map(item => ({
|
||||
name: item.name,
|
||||
type: 'bar',
|
||||
stack: 'total',
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
data: item.data || [],
|
||||
itemStyle: {
|
||||
borderRadius: [0, 0, 0, 0]
|
||||
}
|
||||
}))
|
||||
}
|
||||
myChart.setOption(option, true) // 强制更新
|
||||
})
|
||||
}
|
||||
watch(
|
||||
() => [props.chartData, props.seriesData],
|
||||
() => {
|
||||
console.log(props.chartData)
|
||||
initChart()
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
// 监听窗口大小变化
|
||||
const handleResize = () => {
|
||||
myChart && myChart.resize()
|
||||
}
|
||||
// 获取维保分析数据
|
||||
onMounted(() => {
|
||||
initChart()
|
||||
window.addEventListener('resize', handleResize)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', handleResize)
|
||||
myChart && myChart.dispose()
|
||||
})
|
||||
</script>
|
62
src/components/DashPie.vue
Normal file
62
src/components/DashPie.vue
Normal file
@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div ref="chartRef" style="width: 100%; height: 300px"></div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted, watch } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
const props = defineProps({
|
||||
chartData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
|
||||
const chartRef = ref(null)
|
||||
let myChart = null
|
||||
|
||||
const initChart = () => {
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
right: '4%',
|
||||
bottom: '3%'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: '50%',
|
||||
data: (props.chartData.data || []).filter(item => item.value > 0),
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
myChart = echarts.init(chartRef.value)
|
||||
myChart.setOption(option)
|
||||
}
|
||||
|
||||
// 修改 watch 部分
|
||||
watch(() => props.chartData.data, (newVal) => {
|
||||
if (newVal && newVal.length > 0) {
|
||||
initChart()
|
||||
}
|
||||
}, { deep: true, immediate: true })
|
||||
onMounted(() => {
|
||||
initChart()
|
||||
window.addEventListener('resize', () => myChart?.resize())
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', () => myChart?.resize())
|
||||
myChart?.dispose()
|
||||
})
|
||||
</script>
|
40
src/components/LangSelect/index.vue
Normal file
40
src/components/LangSelect/index.vue
Normal file
@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<el-dropdown trigger="click" @command="handleLanguageChange">
|
||||
<div class="lang-select--style">
|
||||
<svg-icon icon-class="language" />
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item :disabled="appStore.language === 'zh_CN'" command="zh_CN"> 中文 </el-dropdown-item>
|
||||
<el-dropdown-item :disabled="appStore.language === 'en_US'" command="en_US"> English </el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import SvgIcon from '@/components/SvgIcon/index.vue';
|
||||
import {ElMessage} from "element-plus";
|
||||
|
||||
const appStore = useAppStore();
|
||||
const { locale } = useI18n();
|
||||
|
||||
const message: any = {
|
||||
zh_CN: '切换语言成功!',
|
||||
en_US: 'Switch Language Successful!'
|
||||
};
|
||||
const handleLanguageChange = (lang: any) => {
|
||||
locale.value = lang;
|
||||
appStore.changeLanguage(lang);
|
||||
ElMessage.success(message[lang] || '切换语言成功!');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.lang-select--style {
|
||||
font-size: 28px;
|
||||
margin-top:10px;
|
||||
}
|
||||
</style>
|
@ -16,7 +16,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import useAppStore from "@/store/modules/app";
|
||||
import {useAppStore} from "@/store/modules/app";
|
||||
|
||||
const appStore = useAppStore();
|
||||
const size = computed(() => appStore.size);
|
||||
|
@ -35,7 +35,7 @@
|
||||
<script setup>
|
||||
import { constantRoutes } from "@/router"
|
||||
import { isHttp } from '@/utils/validate'
|
||||
import useAppStore from '@/store/modules/app'
|
||||
import {useAppStore} from '@/store/modules/app'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import usePermissionStore from '@/store/modules/permission'
|
||||
|
||||
|
5
src/enums/LanguageEnum.ts
Normal file
5
src/enums/LanguageEnum.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export enum LanguageEnum {
|
||||
zh_CN = 'zh_CN',
|
||||
|
||||
en_US = 'en_US'
|
||||
}
|
15
src/enums/MenuTypeEnum.ts
Normal file
15
src/enums/MenuTypeEnum.ts
Normal file
@ -0,0 +1,15 @@
|
||||
export enum MenuTypeEnum {
|
||||
/**
|
||||
* 目录
|
||||
*/
|
||||
M = 'M',
|
||||
/**
|
||||
* 菜单
|
||||
*/
|
||||
C = 'C',
|
||||
|
||||
/**
|
||||
* 按钮
|
||||
*/
|
||||
F = 'F'
|
||||
}
|
90
src/enums/RespEnum.ts
Normal file
90
src/enums/RespEnum.ts
Normal file
@ -0,0 +1,90 @@
|
||||
export enum HttpStatus {
|
||||
/**
|
||||
* 操作成功
|
||||
*/
|
||||
SUCCESS = 200,
|
||||
/**
|
||||
* 对象创建成功
|
||||
*/
|
||||
CREATED = 201,
|
||||
/**
|
||||
* 请求已经被接受
|
||||
*/
|
||||
ACCEPTED = 202,
|
||||
/**
|
||||
* 操作已经执行成功,但是没有返回数据
|
||||
*/
|
||||
NO_CONTENT = 204,
|
||||
/**
|
||||
* 资源已经被移除
|
||||
*/
|
||||
MOVED_PERM = 301,
|
||||
/**
|
||||
* 重定向
|
||||
*/
|
||||
SEE_OTHER = 303,
|
||||
/**
|
||||
* 资源没有被修改
|
||||
*/
|
||||
NOT_MODIFIED = 304,
|
||||
/**
|
||||
* 参数列表错误(缺少,格式不匹配)
|
||||
*/
|
||||
PARAM_ERROR = 400,
|
||||
/**
|
||||
* 未授权
|
||||
*/
|
||||
UNAUTHORIZED = 401,
|
||||
/**
|
||||
* 访问受限,授权过期
|
||||
*/
|
||||
FORBIDDEN = 403,
|
||||
/**
|
||||
* 资源,服务未找到
|
||||
*/
|
||||
NOT_FOUND = 404,
|
||||
/**
|
||||
* 不允许的http方法
|
||||
*/
|
||||
BAD_METHOD = 405,
|
||||
/**
|
||||
* 资源冲突,或者资源被锁
|
||||
*/
|
||||
CONFLICT = 409,
|
||||
/**
|
||||
* 不支持的数据,媒体类型
|
||||
*/
|
||||
UNSUPPORTED_TYPE = 415,
|
||||
/**
|
||||
* 系统内部错误
|
||||
*/
|
||||
SERVER_ERROR = 500,
|
||||
/**
|
||||
* 接口未实现
|
||||
*/
|
||||
NOT_IMPLEMENTED = 501,
|
||||
/**
|
||||
* 服务不可用,过载或者维护
|
||||
*/
|
||||
BAD_GATEWAY = 502,
|
||||
/**
|
||||
* 网关超时
|
||||
*/
|
||||
GATEWAY_TIMEOUT = 504,
|
||||
/**
|
||||
* 未知错误
|
||||
*/
|
||||
UNKNOWN_ERROR = 520,
|
||||
/**
|
||||
* 服务未知错误
|
||||
*/
|
||||
SERVICE_ERROR = 521,
|
||||
/**
|
||||
* 数据库未知错误
|
||||
*/
|
||||
DATABASE_ERROR = 522,
|
||||
/**
|
||||
* 系统警告消息
|
||||
*/
|
||||
WARN = 601
|
||||
}
|
4
src/enums/SideThemeEnum.ts
Normal file
4
src/enums/SideThemeEnum.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export enum SideThemeEnum {
|
||||
DARK = 'theme-dark',
|
||||
LIGHT = 'theme-light'
|
||||
}
|
17
src/enums/bpmn/IndexEnums.ts
Normal file
17
src/enums/bpmn/IndexEnums.ts
Normal file
@ -0,0 +1,17 @@
|
||||
export enum AllocationTypeEnum {
|
||||
USER = 'user',
|
||||
CANDIDATE = 'candidate',
|
||||
YOURSELF = 'yourself',
|
||||
SPECIFY = 'specify'
|
||||
}
|
||||
|
||||
export enum SpecifyDescEnum {
|
||||
SPECIFY_MULTIPLE = 'specifyMultiple',
|
||||
SPECIFY_SINGLE = 'specifySingle'
|
||||
}
|
||||
|
||||
export enum MultiInstanceTypeEnum {
|
||||
SERIAL = 'serial',
|
||||
PARALLEL = 'parallel',
|
||||
NONE = 'none'
|
||||
}
|
4
src/enums/layout/LayoutEnum.ts
Normal file
4
src/enums/layout/LayoutEnum.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export enum ThemeEnum {
|
||||
DARK = 'theme-dark',
|
||||
LIGHT = 'theme-light'
|
||||
}
|
240
src/lang/en_US.js
Normal file
240
src/lang/en_US.js
Normal file
@ -0,0 +1,240 @@
|
||||
export default {
|
||||
route: {
|
||||
dashboard: 'Dashboard',
|
||||
document: 'Document'
|
||||
},
|
||||
login: {
|
||||
selectPlaceholder: 'Please select/enter company name',
|
||||
username: 'Username',
|
||||
password: 'Password',
|
||||
loginTitle: 'Welcome',
|
||||
loginTip: 'Deflection Meter Management System',
|
||||
login: 'Login',
|
||||
logging: 'Logging in...',
|
||||
code: 'Verification Code',
|
||||
rememberPassword: 'Remember Password',
|
||||
switchRegisterPage: 'Register Now',
|
||||
rule: {
|
||||
tenantId: {
|
||||
required: 'Please enter your tenant ID'
|
||||
},
|
||||
username: {
|
||||
required: 'Please enter your username'
|
||||
},
|
||||
password: {
|
||||
required: 'Please enter your password'
|
||||
},
|
||||
code: {
|
||||
required: 'Please enter verification code'
|
||||
}
|
||||
},
|
||||
social: {
|
||||
wechat: 'WeChat Login',
|
||||
maxkey: 'MaxKey Login',
|
||||
topiam: 'TopIam Login',
|
||||
gitee: 'Gitee Login',
|
||||
github: 'Github Login'
|
||||
}
|
||||
},
|
||||
register: {
|
||||
selectPlaceholder: 'Please select/enter company name',
|
||||
username: 'Username',
|
||||
password: 'Password',
|
||||
confirmPassword: 'Confirm Password',
|
||||
register: 'Register',
|
||||
registering: 'Registering...',
|
||||
registerSuccess: 'Congratulations, your account {username} has been registered successfully!',
|
||||
code: 'Verification Code',
|
||||
switchLoginPage: 'Login with existing account',
|
||||
rule: {
|
||||
tenantId: {
|
||||
required: 'Please enter your tenant ID'
|
||||
},
|
||||
username: {
|
||||
required: 'Please enter your username',
|
||||
length: 'Username length must be between {min} and {max}'
|
||||
},
|
||||
password: {
|
||||
required: 'Please enter your password',
|
||||
length: 'Password length must be between {min} and {max}',
|
||||
pattern: 'Cannot contain illegal characters: {strings}'
|
||||
},
|
||||
code: {
|
||||
required: 'Please enter verification code'
|
||||
},
|
||||
confirmPassword: {
|
||||
required: 'Please enter your password again',
|
||||
equalToPassword: 'The two passwords do not match'
|
||||
}
|
||||
}
|
||||
},
|
||||
navbar: {
|
||||
full: 'Full Screen',
|
||||
language: 'Language',
|
||||
dashboard: 'Dashboard',
|
||||
document: 'Document',
|
||||
message: 'Message',
|
||||
layoutSize: 'Layout Size',
|
||||
selectTenant: 'Select Tenant',
|
||||
layoutSetting: 'Layout Settings',
|
||||
personalCenter: 'Personal Center',
|
||||
logout: 'Logout'
|
||||
},
|
||||
maintenance: {
|
||||
deviceName: 'Device Name',
|
||||
deviceCode: 'Device Code',
|
||||
deviceModel: 'Device Model',
|
||||
maintenanceType: 'Maintenance Type',
|
||||
maintenanceDate: 'Maintenance Date',
|
||||
maintenanceUser: 'Maintenance User',
|
||||
inputMaintenanceUser: 'Please enter maintenance user',
|
||||
inputDeviceName: 'Please enter device name',
|
||||
inputDeviceCode: 'Please enter device code',
|
||||
inputMaintenanceType: 'Please enter maintenance type',
|
||||
inputMaintenanceDate: 'Please enter maintenance date',
|
||||
},
|
||||
// 客户管理
|
||||
customer: {
|
||||
customerName: 'Customer Name',
|
||||
customerCode: 'Customer Code',
|
||||
customerType: 'Customer Type',
|
||||
customerAddress: 'Customer Address',
|
||||
customerPhone: 'Customer Phone',
|
||||
inputCustomerName: 'Please enter customer name',
|
||||
inputCustomerCode: 'Please enter customer code',
|
||||
inputCustomerType: 'Please enter customer type',
|
||||
inputCustomerAddress: 'Please enter customer address',
|
||||
inputCustomerPhone: 'Please enter customer phone',
|
||||
addCustomer: 'Add Customer',
|
||||
editCustomer: 'Edit Customer',
|
||||
contact:'Contact Method',
|
||||
inputContact:'Please enter contact method',
|
||||
leader:'Person in Charge',
|
||||
inputLeader:'Please enter Person in Charge',
|
||||
contactAddress:'Contact Address',
|
||||
inputContactAddress:'Please enter contact address',
|
||||
createTime:'Creation Time',
|
||||
createBy:'Created by',
|
||||
checkDevice: 'View Device',
|
||||
},
|
||||
// 部门管理
|
||||
department: {
|
||||
departmentName: 'Department Name',
|
||||
inputDepartmentName: 'Please enter department name',
|
||||
addDepartment: 'Add Department',
|
||||
editDepartment: 'Edit Department',
|
||||
allCustomer: 'All Customers',
|
||||
leader:'Department Leader',
|
||||
inputLeader:'Please enter department leader',
|
||||
},
|
||||
// 岗位管理
|
||||
position: {
|
||||
positionName:'Position Name',
|
||||
inputPositionName:'Please enter position name',
|
||||
addPosition:'Add Position',
|
||||
editPosition:'Edit Position',
|
||||
department:'Department Affiliation',
|
||||
},
|
||||
// 设备
|
||||
equipment: {
|
||||
deviceName:'Device Name',
|
||||
inputDeviceName:'Please enter device name',
|
||||
deviceCode:'Device Code',
|
||||
inputDeviceCode:'Please enter device code',
|
||||
deviceModel:'Device Model',
|
||||
inputDeviceModel:'Please enter device model',
|
||||
addDevice:'Add Device',
|
||||
editDevice:'Edit Device',
|
||||
deviceStatus:'Device Status',
|
||||
serviceStatus:'Service Status',
|
||||
lifespan:'Consumables lifespan(h)',
|
||||
usedTime:'Used Time',
|
||||
viewAuth:'View Authorization',
|
||||
nickName:'nickName',
|
||||
userName:'userName',
|
||||
userStatus:'userStatus',
|
||||
EMQConnect:'EMQ Connection Details',
|
||||
localEMQ: 'Local EMQ Username',
|
||||
localPassword: 'Local Password',
|
||||
localEMQPassword: 'Cloud EMQ Username',
|
||||
localEMQUser: 'Cloud Password',
|
||||
deviceImage:'Device Image',
|
||||
inputDeviceImage:'Please enter device image',
|
||||
uploadImage:'Upload Image',
|
||||
limitImage:'Only one JPG/PNG file can be uploaded, and the file size cannot exceed 50kb',
|
||||
Ray:'Ray',
|
||||
imagePage:'Image Page',
|
||||
reporting:'Generate Report',
|
||||
deviceShare:'Device Sharing',
|
||||
sketch:'Sketch',
|
||||
historyData:'History Data',
|
||||
deviceInfo:'Device Info',
|
||||
paramsSetting:'Parameter Setting',
|
||||
deviceTemperature:'Device Temperature',
|
||||
warningInfo:'Alarm Information',
|
||||
imageView:'Image Preview',
|
||||
trendChart:'Trend Chart',
|
||||
},
|
||||
// 首页
|
||||
dashboard: {
|
||||
deviceCount:'Device Count',
|
||||
customerCount:'Customer Count',
|
||||
maintenanceCount:'Maintenance Count',
|
||||
onlineDeviceCount:'Online Device Count',
|
||||
offlineDeviceCount: 'Offline Device Count',
|
||||
dataOverview:'Data Overview',
|
||||
maintenanceAnalysis:'Maintenance Analysis',
|
||||
maintenanceSummary:'Maintenance Summary',
|
||||
},
|
||||
button: {
|
||||
add: 'Add',
|
||||
edit: 'Edit',
|
||||
delete: 'Delete',
|
||||
save: 'Save',
|
||||
cancel: 'Cancel',
|
||||
search: 'Search',
|
||||
reset: 'Reset',
|
||||
import: 'Import',
|
||||
export: 'Export',
|
||||
check: 'View',
|
||||
details: 'Details',
|
||||
return:'Return',
|
||||
viewDetails:'View Details',
|
||||
submit:'Submit',
|
||||
},
|
||||
common: {
|
||||
sort: 'Index',
|
||||
customerAffiliation: 'Customer Affiliation',
|
||||
userAffiliation: 'User Affiliation',
|
||||
comments: 'Comments',
|
||||
inputComments:'Please enter comments',
|
||||
action: 'Action',
|
||||
select:'Please select',
|
||||
selectMul:'Please select(supports multiple selections)',
|
||||
selectDate:'Please select date',
|
||||
inputText:'Please enter text',
|
||||
formValidateError:'Form validation failed',
|
||||
warning:'Reminder',
|
||||
deleteItem:'Are you sure to delete?',
|
||||
normal:'Normal',
|
||||
deactivate:'Deactivate',
|
||||
online:'online',
|
||||
offline:'offline',
|
||||
open:'open',
|
||||
close:'close',
|
||||
personalCenter:'Personal Center',
|
||||
logout:'Logout',
|
||||
logoutSystem:'Exit the system?',
|
||||
personalInfo:'Personal Information',
|
||||
userName:'Username',
|
||||
phoneNumber:'Phone Number',
|
||||
userEmail:'User Email',
|
||||
role:'Role Affiliation',
|
||||
baseInfo:'Basic Information',
|
||||
editPassword:'Edit Password',
|
||||
editAvatar:'Edit Avatar',
|
||||
choose:'Choose',
|
||||
modifySuccess:'Modification Successful',
|
||||
sex:'Sex'
|
||||
}
|
||||
}
|
36
src/lang/index.js
Normal file
36
src/lang/index.js
Normal file
@ -0,0 +1,36 @@
|
||||
// 自定义国际化配置
|
||||
import { createI18n } from 'vue-i18n'
|
||||
import { useStorage } from "@vueuse/core"
|
||||
import zh_CN from '@/lang/zh_CN.js'
|
||||
import en_US from '@/lang/en_US.js'
|
||||
|
||||
// 语言枚举
|
||||
export const LanguageEnum = {
|
||||
zh_CN: 'zh_CN',
|
||||
en_US: 'en_US'
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前语言
|
||||
* @returns {string} zh_CN|en_US
|
||||
*/
|
||||
export const getLanguage = () => {
|
||||
const language = useStorage('language', LanguageEnum.zh_CN)
|
||||
if (language.value) {
|
||||
return language.value
|
||||
}
|
||||
return LanguageEnum.zh_CN
|
||||
}
|
||||
|
||||
const i18n = createI18n({
|
||||
globalInjection: true,
|
||||
allowComposition: true,
|
||||
legacy: false,
|
||||
locale: getLanguage(),
|
||||
messages: {
|
||||
zh_CN: zh_CN,
|
||||
en_US: en_US
|
||||
}
|
||||
})
|
||||
|
||||
export default i18n
|
247
src/lang/zh_CN.js
Normal file
247
src/lang/zh_CN.js
Normal file
@ -0,0 +1,247 @@
|
||||
export default {
|
||||
// 路由国际化
|
||||
route: {
|
||||
dashboard: '首页',
|
||||
document: '项目文档'
|
||||
},
|
||||
// 登录页面国际化
|
||||
login: {
|
||||
selectPlaceholder: '请选择/输入公司名称',
|
||||
username: '用户名',
|
||||
password: '密码',
|
||||
loginTitle:'欢迎登录',
|
||||
loginTip:'测偏仪后台管理系统',
|
||||
login: '登 录',
|
||||
logging: '登 录 中...',
|
||||
code: '验证码',
|
||||
rememberPassword: '记住密码',
|
||||
switchRegisterPage: '立即注册',
|
||||
rule: {
|
||||
tenantId: {
|
||||
required: '请输入您的租户编号'
|
||||
},
|
||||
username: {
|
||||
required: '请输入您的账号'
|
||||
},
|
||||
password: {
|
||||
required: '请输入您的密码'
|
||||
},
|
||||
code: {
|
||||
required: '请输入验证码'
|
||||
}
|
||||
},
|
||||
social: {
|
||||
wechat: '微信登录',
|
||||
maxkey: 'MaxKey登录',
|
||||
topiam: 'TopIam登录',
|
||||
gitee: 'Gitee登录',
|
||||
github: 'Github登录'
|
||||
}
|
||||
},
|
||||
// 注册页面国际化
|
||||
register: {
|
||||
selectPlaceholder: '请选择/输入公司名称',
|
||||
username: '用户名',
|
||||
password: '密码',
|
||||
confirmPassword: '确认密码',
|
||||
register: '注 册',
|
||||
registering: '注 册 中...',
|
||||
registerSuccess: '恭喜你,您的账号 {username} 注册成功!',
|
||||
code: '验证码',
|
||||
switchLoginPage: '使用已有账户登录',
|
||||
rule: {
|
||||
tenantId: {
|
||||
required: '请输入您的租户编号'
|
||||
},
|
||||
username: {
|
||||
required: '请输入您的账号',
|
||||
length: '用户账号长度必须介于 {min} 和 {max} 之间'
|
||||
},
|
||||
password: {
|
||||
required: '请输入您的密码',
|
||||
length: '用户密码长度必须介于 {min} 和 {max} 之间',
|
||||
pattern: '不能包含非法字符:{strings}'
|
||||
},
|
||||
code: {
|
||||
required: '请输入验证码'
|
||||
},
|
||||
confirmPassword: {
|
||||
required: '请再次输入您的密码',
|
||||
equalToPassword: '两次输入的密码不一致'
|
||||
}
|
||||
}
|
||||
},
|
||||
// 导航栏国际化
|
||||
navbar: {
|
||||
full: '全屏',
|
||||
language: '语言',
|
||||
dashboard: '首页',
|
||||
document: '项目文档',
|
||||
message: '消息',
|
||||
layoutSize: '布局大小',
|
||||
selectTenant: '选择租户',
|
||||
layoutSetting: '布局设置',
|
||||
personalCenter: '个人中心',
|
||||
logout: '退出登录'
|
||||
},
|
||||
//维保管理
|
||||
maintenance: {
|
||||
deviceName:'设备名称',
|
||||
deviceCode:'设备编码',
|
||||
deviceModel:'设备型号',
|
||||
maintenanceType:'维保类型',
|
||||
maintenanceDate:'维保日期',
|
||||
maintenanceUser:'维护人',
|
||||
inputMaintenanceUser:'请输入维护人',
|
||||
inputDeviceName:'请输入设备名称',
|
||||
inputDeviceCode:'请输入设备编码',
|
||||
inputMaintenanceType:'请输入维保类型',
|
||||
inputMaintenanceDate:'请输入维保日期',
|
||||
},
|
||||
// 客户管理
|
||||
customer: {
|
||||
customerName:'客户名称',
|
||||
customerCode:'客户编码',
|
||||
customerAddress:'客户地址',
|
||||
customerPhone:'客户电话',
|
||||
customerEmail:'客户邮箱',
|
||||
inputCustomerName:'请输入客户名称',
|
||||
inputCustomerCode:'请输入客户编码',
|
||||
inputCustomerAddress:'请输入客户地址',
|
||||
inputCustomerPhone:'请输入客户电话',
|
||||
inputCustomerEmail:'请输入客户邮箱',
|
||||
addCustomer:'新建客户',
|
||||
editCustomer:'编辑客户',
|
||||
contact:'联系方式',
|
||||
inputContact:'请输入联系方式',
|
||||
leader:'负责人',
|
||||
inputLeader:'请输入负责人',
|
||||
contactAddress:'联系地址',
|
||||
proxy:'请输入联系方式',
|
||||
createTime:'创建时间',
|
||||
createBy:'创建人',
|
||||
checkDevice:'查看设备',
|
||||
},
|
||||
// 部门管理
|
||||
department: {
|
||||
departmentName:'部门名称',
|
||||
inputDepartmentName:'请输入部门名称',
|
||||
addDepartment:'新建部门',
|
||||
editDepartment:'编辑部门',
|
||||
leader:'部门负责人',
|
||||
inputLeader:'请输入部门负责人',
|
||||
allCustomer:'全部客户'
|
||||
},
|
||||
// 岗位管理
|
||||
position: {
|
||||
positionName:'岗位名称',
|
||||
inputPositionName:'请输入岗位名称',
|
||||
addPosition:'新建岗位',
|
||||
editPosition:'编辑岗位',
|
||||
department:'所属部门',
|
||||
},
|
||||
// 设备
|
||||
equipment: {
|
||||
deviceName: '设备名称',
|
||||
inputDeviceName: '请输入设备名称',
|
||||
deviceCode: '设备编码',
|
||||
inputDeviceCode: '请输入设备编码',
|
||||
deviceModel: '设备型号',
|
||||
inputDeviceModel: '请输入设备型号',
|
||||
addDevice: '新增设备',
|
||||
editDevice: '编辑设备',
|
||||
deviceStatus:'设备状态',
|
||||
serviceStatus:'服务状态',
|
||||
lifespan:'耗材寿命(h)',
|
||||
usedTime:'已使用时长',
|
||||
viewAuth:'查看鉴权',
|
||||
nickName:'用户昵称',
|
||||
userName:'登陆账户',
|
||||
userStatus:'用户状态',
|
||||
EMQConnect:'EMQ连接详情',
|
||||
localEMQ:'本地EMQ用户名',
|
||||
localPassword:'本地密码',
|
||||
localEMQPassword:'云EMQ用户名',
|
||||
localEMQUser:'云密码',
|
||||
deviceImage:'设备图片',
|
||||
inputDeviceImage:'请输入设备图片',
|
||||
uploadImage:'上传图片',
|
||||
limitImage:'只能上传一张jpg/png格式文件,文件不能超过50kb',
|
||||
Ray:'射线',
|
||||
imagePage:'图像页',
|
||||
reporting:'生成报告',
|
||||
deviceShare:'设备分享',
|
||||
sketch:'示意图',
|
||||
historyData:'历史数据',
|
||||
deviceInfo:'设备信息',
|
||||
paramsSetting:'参数设定',
|
||||
deviceTemperature:'设备温度',
|
||||
warningInfo:'报警信息',
|
||||
imageView:'图像预览',
|
||||
trendChart:'趋势图',
|
||||
},
|
||||
// 首页
|
||||
dashboard: {
|
||||
deviceCount:'设备数量',
|
||||
customerCount:'客户数量',
|
||||
maintenanceCount:'用户数量',
|
||||
onlineDeviceCount:'在线设备数量',
|
||||
offlineDeviceCount:'离线设备数量',
|
||||
dataOverview:'数据总览',
|
||||
maintenanceAnalysis: '维保分析',
|
||||
maintenanceSummary: '维保汇总',
|
||||
},
|
||||
// 按钮
|
||||
button: {
|
||||
add:'新 增',
|
||||
edit:'编 辑',
|
||||
delete:'删除',
|
||||
save:'确 定',
|
||||
cancel:'取 消',
|
||||
search:'查 询',
|
||||
reset:'重 置',
|
||||
import:'导 入',
|
||||
export:'导 出',
|
||||
check:'查 看',
|
||||
details:'详 情',
|
||||
return:'返 回',
|
||||
viewDetails:'查看详情',
|
||||
submit:'提 交',
|
||||
},
|
||||
common: {
|
||||
sort:'序号',
|
||||
customerAffiliation:'所属客户',
|
||||
userAffiliation:'所属用户',
|
||||
comments:'备注',
|
||||
inputComments:'请输入备注',
|
||||
action:'操作',
|
||||
select:'请选择',
|
||||
selectMul:'请选择(支持多选)',
|
||||
selectDate:'请选择日期',
|
||||
inputText:'请输入文字',
|
||||
formValidateError:'表单验证失败',
|
||||
warning:'警告',
|
||||
deleteItem:'是否确认删除?',
|
||||
normal:'正常',
|
||||
deactivate:'停用',
|
||||
online:'在线',
|
||||
offline:'离线',
|
||||
open:'开启',
|
||||
close:'关闭',
|
||||
personalCenter: '个人中心',
|
||||
logout:'退出登录',
|
||||
logoutSystem:'是否确认退出系统?',
|
||||
personalInfo:'个人信息',
|
||||
userName:'用户名称',
|
||||
phoneNumber:'手机号',
|
||||
inputPhoneNumber:'请输入手机号',
|
||||
userEmail:'用户邮箱',
|
||||
role:'所属角色',
|
||||
baseInfo:'基本资料',
|
||||
editPassword:'修改密码',
|
||||
editAvatar:'修改头像',
|
||||
choose:'选择',
|
||||
modifySuccess:'修改成功',
|
||||
sex:'性别',
|
||||
}
|
||||
};
|
@ -22,7 +22,7 @@ onMounted(() => {
|
||||
addIframe()
|
||||
})
|
||||
|
||||
watch((route) => {
|
||||
watchEffect((route) => {
|
||||
addIframe()
|
||||
})
|
||||
|
||||
|
@ -6,27 +6,30 @@
|
||||
|
||||
<div class="right-menu">
|
||||
<template v-if="appStore.device !== 'mobile'">
|
||||
<header-search id="header-search" class="right-menu-item" />
|
||||
<!-- <header-search id="header-search" class="right-menu-item" />-->
|
||||
|
||||
<el-tooltip content="源码地址" effect="dark" placement="bottom">
|
||||
<ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
|
||||
</el-tooltip>
|
||||
<!-- <el-tooltip content="源码地址" effect="dark" placement="bottom">-->
|
||||
<!-- <ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />-->
|
||||
<!-- </el-tooltip>-->
|
||||
|
||||
<el-tooltip content="文档地址" effect="dark" placement="bottom">
|
||||
<ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" />
|
||||
</el-tooltip>
|
||||
<!-- <el-tooltip content="文档地址" effect="dark" placement="bottom">-->
|
||||
<!-- <ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" />-->
|
||||
<!-- </el-tooltip>-->
|
||||
|
||||
<screenfull id="screenfull" class="right-menu-item hover-effect" />
|
||||
<!-- <screenfull id="screenfull" class="right-menu-item hover-effect" />-->
|
||||
|
||||
<el-tooltip content="主题模式" effect="dark" placement="bottom">
|
||||
<div class="right-menu-item hover-effect theme-switch-wrapper" @click="toggleTheme">
|
||||
<svg-icon v-if="settingsStore.isDark" icon-class="sunny" />
|
||||
<svg-icon v-if="!settingsStore.isDark" icon-class="moon" />
|
||||
</div>
|
||||
</el-tooltip>
|
||||
<!-- <el-tooltip content="主题模式" effect="dark" placement="bottom">-->
|
||||
<!-- <div class="right-menu-item hover-effect theme-switch-wrapper" @click="toggleTheme">-->
|
||||
<!-- <svg-icon v-if="settingsStore.isDark" icon-class="sunny" />-->
|
||||
<!-- <svg-icon v-if="!settingsStore.isDark" icon-class="moon" />-->
|
||||
<!-- </div>-->
|
||||
<!-- </el-tooltip>-->
|
||||
|
||||
<el-tooltip content="布局大小" effect="dark" placement="bottom">
|
||||
<size-select id="size-select" class="right-menu-item hover-effect" />
|
||||
<!-- <el-tooltip content="布局大小" effect="dark" placement="bottom">-->
|
||||
<!-- <size-select id="size-select" class="right-menu-item hover-effect" />-->
|
||||
<!-- </el-tooltip>-->
|
||||
<el-tooltip :content="proxy.$t('navbar.language')" effect="dark" placement="bottom">
|
||||
<LangSelect id="lang-select" class="right-menu-item hover-effect" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<div class="avatar-container">
|
||||
@ -38,13 +41,13 @@
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<router-link to="/user/profile">
|
||||
<el-dropdown-item>个人中心</el-dropdown-item>
|
||||
<el-dropdown-item>{{proxy.$t('common.personalCenter')}}</el-dropdown-item>
|
||||
</router-link>
|
||||
<el-dropdown-item command="setLayout" v-if="settingsStore.showSettings">
|
||||
<span>布局设置</span>
|
||||
</el-dropdown-item>
|
||||
<!-- <el-dropdown-item command="setLayout" v-if="settingsStore.showSettings">-->
|
||||
<!-- <span>布局设置</span>-->
|
||||
<!-- </el-dropdown-item>-->
|
||||
<el-dropdown-item divided command="logout">
|
||||
<span>退出登录</span>
|
||||
<span>{{proxy.$t('common.logout')}}</span>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
@ -59,23 +62,25 @@ import { ElMessageBox } from 'element-plus'
|
||||
import Breadcrumb from '@/components/Breadcrumb'
|
||||
import TopNav from '@/components/TopNav'
|
||||
import Hamburger from '@/components/Hamburger'
|
||||
import LangSelect from '@/components/LangSelect/index.vue'
|
||||
import Screenfull from '@/components/Screenfull'
|
||||
import SizeSelect from '@/components/SizeSelect'
|
||||
import HeaderSearch from '@/components/HeaderSearch'
|
||||
import RuoYiGit from '@/components/RuoYi/Git'
|
||||
import RuoYiDoc from '@/components/RuoYi/Doc'
|
||||
import useAppStore from '@/store/modules/app'
|
||||
import {useAppStore} from '@/store/modules/app'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
|
||||
import { useI18n } from 'vue-i18n';
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { t } = useI18n();
|
||||
const appStore = useAppStore()
|
||||
const userStore = useUserStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
|
||||
function toggleSideBar() {
|
||||
appStore.toggleSideBar()
|
||||
appStore.toggleSideBar(false)
|
||||
}
|
||||
|
||||
function handleCommand(command) {
|
||||
switch (command) {
|
||||
case "setLayout":
|
||||
@ -90,11 +95,14 @@ function handleCommand(command) {
|
||||
}
|
||||
|
||||
function logout() {
|
||||
ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
ElMessageBox.confirm(proxy.$t('common.logoutSystem'),
|
||||
proxy.$t('common.warning'),
|
||||
{
|
||||
confirmButtonText: proxy.$t('button.save'),
|
||||
cancelButtonText: proxy.$t('button.cancel'),
|
||||
type: "warning"
|
||||
}
|
||||
).then(() => {
|
||||
userStore.logOut().then(() => {
|
||||
location.href = '/index';
|
||||
})
|
||||
|
@ -83,7 +83,7 @@ import variables from '@/assets/styles/variables.module.scss'
|
||||
import axios from 'axios'
|
||||
import { ElLoading, ElMessage } from 'element-plus'
|
||||
import { useDynamicTitle } from '@/utils/dynamicTitle'
|
||||
import useAppStore from '@/store/modules/app'
|
||||
import {useAppStore} from '@/store/modules/app'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import usePermissionStore from '@/store/modules/permission'
|
||||
import { handleThemeStyle } from '@/utils/theme'
|
||||
|
@ -2,12 +2,12 @@
|
||||
<div class="sidebar-logo-container" :class="{ 'collapse': collapse }">
|
||||
<transition name="sidebarLogoFade">
|
||||
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
|
||||
<img v-if="logo" :src="logo" class="sidebar-logo" />
|
||||
<h1 v-else class="sidebar-title">{{ title }}</h1>
|
||||
<!-- <img v-if="logo" :src="logo" class="sidebar-logo" />-->
|
||||
<h1 class="sidebar-title">TIANGONIX</h1>
|
||||
</router-link>
|
||||
<router-link v-else key="expand" class="sidebar-logo-link" to="/">
|
||||
<img v-if="logo" :src="logo" class="sidebar-logo" />
|
||||
<h1 class="sidebar-title">{{ title }}</h1>
|
||||
<!-- <img v-if="logo" :src="logo" class="sidebar-logo" />-->
|
||||
<h1 class="sidebar-title">TIANGONIX</h1>
|
||||
</router-link>
|
||||
</transition>
|
||||
</div>
|
||||
@ -81,10 +81,11 @@ const getLogoTextColor = computed(() => {
|
||||
& .sidebar-title {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
color: v-bind(getLogoTextColor);
|
||||
color:#F59537;
|
||||
//color: v-bind(getLogoTextColor);
|
||||
font-weight: 600;
|
||||
line-height: 50px;
|
||||
font-size: 14px;
|
||||
font-size: 18px;
|
||||
font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
@ -28,7 +28,7 @@
|
||||
import Logo from './Logo'
|
||||
import SidebarItem from './SidebarItem'
|
||||
import variables from '@/assets/styles/variables.module.scss'
|
||||
import useAppStore from '@/store/modules/app'
|
||||
import {useAppStore} from '@/store/modules/app'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import usePermissionStore from '@/store/modules/permission'
|
||||
|
||||
|
@ -19,7 +19,7 @@ import Sidebar from './components/Sidebar/index.vue'
|
||||
import { AppMain, Navbar, Settings, TagsView } from './components'
|
||||
import defaultSettings from '@/settings'
|
||||
|
||||
import useAppStore from '@/store/modules/app'
|
||||
import {useAppStore} from '@/store/modules/app'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
|
||||
const settingsStore = useSettingsStore()
|
||||
|
19
src/main.js
19
src/main.js
@ -13,7 +13,7 @@ import App from './App'
|
||||
import store from './store'
|
||||
import router from './router'
|
||||
import directive from './directive' // directive
|
||||
|
||||
import { createPinia } from 'pinia'
|
||||
// 注册指令
|
||||
import plugins from './plugins' // plugins
|
||||
import { download } from '@/utils/request'
|
||||
@ -27,7 +27,8 @@ import './permission' // permission control
|
||||
|
||||
import { useDict } from '@/utils/dict'
|
||||
import { parseTime, resetForm, addDateRange, handleTree, selectDictLabel, selectDictLabels } from '@/utils/ruoyi'
|
||||
|
||||
// 国际化
|
||||
import i18n from '@/lang/index.js';
|
||||
// 分页组件
|
||||
import Pagination from '@/components/Pagination'
|
||||
// 自定义表格工具组件
|
||||
@ -42,9 +43,13 @@ import ImageUpload from "@/components/ImageUpload"
|
||||
import ImagePreview from "@/components/ImagePreview"
|
||||
// 字典标签组件
|
||||
import DictTag from '@/components/DictTag'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
const pinia = createPinia()
|
||||
app.use(pinia)
|
||||
const appStore = useAppStore()
|
||||
// 全局方法挂载
|
||||
app.config.globalProperties.useDict = useDict
|
||||
app.config.globalProperties.download = download
|
||||
@ -68,15 +73,15 @@ app.use(router)
|
||||
app.use(store)
|
||||
app.use(plugins)
|
||||
app.use(elementIcons)
|
||||
app.use(i18n)
|
||||
app.component('svg-icon', SvgIcon)
|
||||
|
||||
directive(app)
|
||||
|
||||
// 使用element-plus 并且设置全局的大小
|
||||
app.use(ElementPlus, {
|
||||
locale: locale,
|
||||
// 支持 large、default、small
|
||||
size: Cookies.get('size') || 'default'
|
||||
locale: appStore.locale,
|
||||
size: appStore.size
|
||||
})
|
||||
|
||||
// app.use(ElementPlus)
|
||||
app.mount('#app')
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { LanguageEnum } from '@/enums/LanguageEnum';
|
||||
export default {
|
||||
/**
|
||||
* 网页标题
|
||||
@ -43,5 +44,6 @@ export default {
|
||||
* The default is only used in the production env
|
||||
* If you want to also use it in dev, you can pass ['production', 'development']
|
||||
*/
|
||||
errorLog: 'production'
|
||||
errorLog: 'production',
|
||||
language: LanguageEnum.zh_CN,
|
||||
}
|
||||
|
@ -1,6 +1,11 @@
|
||||
import zhCN from 'element-plus/es/locale/lang/zh-cn';
|
||||
import enUS from 'element-plus/es/locale/lang/en';
|
||||
import { defineStore } from 'pinia';
|
||||
import { useStorage } from '@vueuse/core';
|
||||
import { ref, reactive, computed } from 'vue';
|
||||
import Cookies from 'js-cookie'
|
||||
|
||||
const useAppStore = defineStore(
|
||||
export const useAppStore = defineStore(
|
||||
'app',
|
||||
{
|
||||
state: () => ({
|
||||
@ -10,8 +15,20 @@ const useAppStore = defineStore(
|
||||
hide: false
|
||||
},
|
||||
device: 'desktop',
|
||||
size: Cookies.get('size') || 'default'
|
||||
size: Cookies.get('size') || 'default',
|
||||
// 添加语言相关状态
|
||||
language: useStorage('language', 'zh_CN'),
|
||||
languageObj: {
|
||||
en_US: enUS,
|
||||
zh_CN: zhCN
|
||||
}
|
||||
}),
|
||||
getters: {
|
||||
locale: (state) => {
|
||||
console.log(state,'===')
|
||||
return state.languageObj[state.language]
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
toggleSideBar(withoutAnimation) {
|
||||
if (this.sidebar.hide) {
|
||||
@ -39,8 +56,10 @@ const useAppStore = defineStore(
|
||||
},
|
||||
toggleSideBarHide(status) {
|
||||
this.sidebar.hide = status
|
||||
},
|
||||
// 添加修改语言的方法
|
||||
changeLanguage(val) {
|
||||
this.language = val
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default useAppStore
|
||||
|
15
src/utils/i18n.js
Normal file
15
src/utils/i18n.js
Normal file
@ -0,0 +1,15 @@
|
||||
import i18n from '@/lang/index.js'
|
||||
|
||||
/**
|
||||
* 获取国际化路由,如果不存在则原生返回
|
||||
* @param {string} title 路由名称
|
||||
* @returns {string}
|
||||
*/
|
||||
export const translateRouteTitle = (title) => {
|
||||
const hasKey = i18n.global.te('route.' + title)
|
||||
if (hasKey) {
|
||||
const translatedTitle = i18n.global.t('route.' + title)
|
||||
return translatedTitle
|
||||
}
|
||||
return title
|
||||
}
|
@ -10,7 +10,12 @@ import useUserStore from '@/store/modules/user'
|
||||
let downloadLoadingInstance;
|
||||
// 是否显示重新登录
|
||||
export let isRelogin = { show: false };
|
||||
|
||||
export const globalHeaders = () => {
|
||||
return {
|
||||
Authorization: 'Bearer ' + getToken(),
|
||||
clientid: import.meta.env.VITE_APP_CLIENT_ID
|
||||
};
|
||||
};
|
||||
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
|
||||
// 创建axios实例
|
||||
const service = axios.create({
|
||||
|
145
src/views/customer/common/CustomerDialog.vue
Normal file
145
src/views/customer/common/CustomerDialog.vue
Normal file
@ -0,0 +1,145 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:title="title"
|
||||
v-model="visible"
|
||||
style="width: 460px"
|
||||
append-to-body
|
||||
:before-close="cancel"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-width="appStore.language === 'zh_CN' ? '100px' : '160px'"
|
||||
label-position="right"
|
||||
>
|
||||
|
||||
<el-form-item :label="proxy.$t('customer.customerName')" prop="deptName">
|
||||
<el-input v-model="form.deptName" :placeholder="proxy.$t('customer.inputCustomerName')" style="width:300px"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="proxy.$t('customer.leader')" prop="leader">
|
||||
<el-input v-model="form.leader" :placeholder="proxy.$t('customer.inputLeader')" style="width:300px"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="proxy.$t('customer.contact')" prop="phone">
|
||||
<el-input v-model="form.phone" :placeholder="proxy.$t('customer.inputContact')" style="width:300px"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="proxy.$t('customer.contactAddress')" prop="address">
|
||||
<el-input
|
||||
v-model="form.address"
|
||||
type="textarea"
|
||||
:placeholder="proxy.$t('customer.inputContactAddress')"
|
||||
style="width:300px"
|
||||
:rows="4"
|
||||
maxlength="200"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">{{proxy.$t('button.save')}}</el-button>
|
||||
<el-button @click="cancel">{{proxy.$t('button.cancel')}}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive,onMounted } from 'vue'
|
||||
import {addDepartmentApi, editDepartmentApi} from '@/api/customer'
|
||||
import {ElMessage} from "element-plus";
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
const appStore = useAppStore();
|
||||
const { proxy } = getCurrentInstance();
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
formData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue', 'success'])
|
||||
|
||||
const visible = ref(false)
|
||||
const formRef = ref()
|
||||
|
||||
|
||||
const form = reactive({
|
||||
deptName: '',
|
||||
leader: '',
|
||||
phone: '',
|
||||
address: '',
|
||||
orderNum: 0,
|
||||
status:'0',
|
||||
parentId:100,
|
||||
ancestors: '',
|
||||
deptId: undefined,
|
||||
})
|
||||
|
||||
const rules = {
|
||||
deptName: [{ required: true, message: proxy.$t('customer.inputCustomerName'), trigger: 'blur' }],
|
||||
leader: [{ required: true, message: proxy.$t('customer.inputLeader'), trigger: 'blur' }],
|
||||
phone: [{ required: true, message: proxy.$t('customer.inputContact'), trigger: 'blur' }],
|
||||
address: [{ required: true, message: proxy.$t('customer.contactAddress'), trigger: 'blur' }]
|
||||
}
|
||||
|
||||
const submitForm = async () => {
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
// 这里添加提交逻辑
|
||||
const res = props.title === proxy.$t('customer.addCustomer') ? await addDepartmentApi(form) : await editDepartmentApi(form)
|
||||
if(res.code === 200) {
|
||||
ElMessage.success(res.msg)
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
emit('success')
|
||||
cancel()
|
||||
} catch (error) {
|
||||
console.error(proxy.$t('common.formValidateError'), error)
|
||||
}
|
||||
}
|
||||
|
||||
const cancel = () => {
|
||||
resetForm()
|
||||
emit('update:modelValue', false)
|
||||
}
|
||||
|
||||
const resetForm = () => {
|
||||
formRef.value?.resetFields()
|
||||
if (props.title === proxy.$t('customer.addCustomer')) {
|
||||
// 新增模式下才清空表单
|
||||
Object.keys(form).forEach(key => {
|
||||
form[key] = ''
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
[() => props.title, () => props.formData],
|
||||
([newTitle, newFormData]) => {
|
||||
if (newTitle === proxy.$t('customer.editCustomer') && Object.keys(newFormData).length > 0) {
|
||||
Object.keys(form).forEach(key => {
|
||||
form[key] = newFormData[key]
|
||||
})
|
||||
}
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
|
||||
defineExpose({
|
||||
visible
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-footer {
|
||||
text-align: center;
|
||||
}
|
||||
:deep(.el-form-item__label) {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
182
src/views/customer/common/DepartmentDialog.vue
Normal file
182
src/views/customer/common/DepartmentDialog.vue
Normal file
@ -0,0 +1,182 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:title="title"
|
||||
v-model="visible"
|
||||
style="width: 460px"
|
||||
append-to-body
|
||||
:before-close="cancel"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-width="appStore.language === 'zh_CN' ? '100px' : '160px'"
|
||||
label-position="right"
|
||||
>
|
||||
|
||||
<el-form-item :label="proxy.$t('common.customerAffiliation')" prop="parentName">
|
||||
<el-select v-model="form.parentName" style="width:300px">
|
||||
<el-option
|
||||
v-for="item in deptList"
|
||||
:key="item.deptId"
|
||||
:label="item.deptName"
|
||||
:value="item.deptId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="proxy.$t('department.departmentName')" prop="deptName">
|
||||
<el-input v-model="form.deptName" :placeholder="proxy.$t('department.inputDepartmentName')" style="width:300px"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="proxy.$t('department.leader')" prop="leader">
|
||||
<el-input v-model="form.leader" :placeholder="proxy.$t('department.inputLeader')" style="width:300px"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="proxy.$t('customer.contact')" prop="phone">
|
||||
<el-input v-model="form.phone" :placeholder="proxy.$t('customer.inputContact')" style="width:300px"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">{{proxy.$t('button.save')}}</el-button>
|
||||
<el-button @click="cancel">{{proxy.$t('button.cancel')}}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import {addDepartmentApi, editDepartmentApi} from '@/api/customer'
|
||||
import {ElMessage} from "element-plus";
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
const appStore = useAppStore();
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '新建部门'
|
||||
},
|
||||
formData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
deptList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
selectedName: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
selectedId: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
orderNum: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue', 'success'])
|
||||
|
||||
const visible = ref(false)
|
||||
const formRef = ref()
|
||||
|
||||
|
||||
const form = reactive({
|
||||
deptName: '',
|
||||
leader: '',
|
||||
phone: '',
|
||||
address: '',
|
||||
orderNum: 0,
|
||||
status:'0',
|
||||
parentId:undefined,
|
||||
ancestors: '',
|
||||
deptId: undefined,
|
||||
parentName:undefined
|
||||
})
|
||||
|
||||
const rules = {
|
||||
parentName: [{ required: true, message: proxy.$t('common.select'), trigger: 'change' }],
|
||||
deptName: [{ required: true, message: proxy.$t('department.inputDepartmentName'), trigger: 'blur' }],
|
||||
leader: [{ required: true, message: proxy.$t('department.inputLeader'), trigger: 'blur' }],
|
||||
phone: [{ required: true, message: proxy.$t('customer.inputContact'), trigger: 'blur' }],
|
||||
}
|
||||
|
||||
const submitForm = async () => {
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
// 这里添加提交逻辑
|
||||
form.orderNum = 0
|
||||
const res = props.title === proxy.$t('department.addDepartment') ? await addDepartmentApi(form) : await editDepartmentApi(form)
|
||||
if(res.code === 200) {
|
||||
ElMessage.success(res.msg)
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
emit('success')
|
||||
cancel()
|
||||
} catch (error) {
|
||||
console.error( proxy.$t('common.formValidateError'), error)
|
||||
}
|
||||
}
|
||||
|
||||
const cancel = () => {
|
||||
resetForm()
|
||||
emit('update:modelValue', false)
|
||||
}
|
||||
|
||||
const resetForm = () => {
|
||||
formRef.value?.resetFields()
|
||||
if(props.title === proxy.$t('department.addDepartment')) {
|
||||
Object.keys(form).forEach(key => {
|
||||
if (key !== 'parentId' && key !== 'parentName'&& key !== 'orderNum') {
|
||||
form[key] = ''
|
||||
}
|
||||
})
|
||||
}else {
|
||||
Object.keys(form).forEach(key => {
|
||||
form[key] = ''
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
[() => props.title, () => props.formData],
|
||||
([newTitle, newFormData]) => {
|
||||
if (newTitle === proxy.$t('department.editDepartment') && Object.keys(newFormData).length > 0) {
|
||||
Object.keys(form).forEach(key => {
|
||||
form[key] = newFormData[key]
|
||||
})
|
||||
}
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
watchEffect(() => {
|
||||
if (props.selectedName) {
|
||||
form.parentName = props.selectedName
|
||||
}
|
||||
})
|
||||
watchEffect(() => {
|
||||
if (props.selectedId) {
|
||||
form.parentId = props.selectedId
|
||||
}
|
||||
})
|
||||
watchEffect(() => {
|
||||
if (props.orderNum) {
|
||||
form.orderNum = props.orderNum
|
||||
}
|
||||
})
|
||||
defineExpose({
|
||||
visible
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-footer {
|
||||
text-align: center;
|
||||
}
|
||||
:deep(.el-form-item__label) {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
208
src/views/customer/common/PostDialog.vue
Normal file
208
src/views/customer/common/PostDialog.vue
Normal file
@ -0,0 +1,208 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:title="title"
|
||||
v-model="visible"
|
||||
style="width: 460px"
|
||||
append-to-body
|
||||
:before-close="cancel"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-width="appStore.language === 'zh_CN' ? '100px' : '200px'"
|
||||
label-position="right"
|
||||
>
|
||||
<el-form-item :label="proxy.$t('position.positionName')" prop="postName">
|
||||
<el-input v-model="form.postName" :placeholder="proxy.$t('position.inputPositionName')" style="width:300px"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="proxy.$t('common.customerAffiliation')" prop="customerId">
|
||||
<el-select v-model="form.customerId" style="width:300px" @change="handleCustomerChange">
|
||||
<el-option
|
||||
v-for="item in deptList"
|
||||
:key="item.deptId"
|
||||
:label="item.deptName"
|
||||
:value="item.deptId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="proxy.$t('position.department')" prop="deptId">
|
||||
<el-select v-model="form.deptId" style="width:300px" :disabled="!form.customerName">
|
||||
<el-option
|
||||
v-for="item in postList"
|
||||
:key="item.deptId"
|
||||
:label="item.deptName"
|
||||
:value="item.deptId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">{{proxy.$t('button.save')}}</el-button>
|
||||
<el-button @click="cancel">{{proxy.$t('button.cancel')}}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive,onMounted } from 'vue'
|
||||
import {
|
||||
addDepartmentPostApi,
|
||||
editDepartmentPostApi,getCustomerListApi
|
||||
} from '@/api/customer'
|
||||
import {ElMessage} from "element-plus";
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
const appStore = useAppStore();
|
||||
const { proxy } = getCurrentInstance();
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '新建岗位'
|
||||
},
|
||||
formData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
selectId: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
selectName: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
parentId: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
const deptList = ref([])
|
||||
const postList = ref([])
|
||||
const emit = defineEmits(['update:modelValue', 'success'])
|
||||
|
||||
const visible = ref(false)
|
||||
const formRef = ref()
|
||||
|
||||
|
||||
const form = reactive({
|
||||
deptId:'',
|
||||
deptName: '',
|
||||
postId:'',
|
||||
postName: '',
|
||||
customerId:'',
|
||||
customerName: '',
|
||||
id:''
|
||||
})
|
||||
const queryParams = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 100000000,
|
||||
deptName: undefined,
|
||||
parentId: 100,
|
||||
})
|
||||
const rules = {
|
||||
deptId: [{ required: true, message: proxy.$t('common.select'), trigger: 'change' }],
|
||||
postName: [{ required: true, message: proxy.$t('position.inputPositionName'), trigger: 'blur' }],
|
||||
customerId: [{ required: true, message: proxy.$t('common.select'), trigger: 'change' }],
|
||||
}
|
||||
const getDepList = async() => {
|
||||
const res = await getCustomerListApi(queryParams);
|
||||
deptList.value = res.rows || []
|
||||
}
|
||||
const submitForm = async () => {
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
// 这里添加提交逻辑
|
||||
console.log(props.selectId,'id---')
|
||||
const res = props.title === proxy.$t('position.addPosition') ? await addDepartmentPostApi(form) : await editDepartmentPostApi(form)
|
||||
if(res.code === 200) {
|
||||
ElMessage.success(res.msg)
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
emit('success')
|
||||
cancel()
|
||||
} catch (error) {
|
||||
console.error(proxy.$t('common.formValidateError') , error)
|
||||
}
|
||||
}
|
||||
|
||||
const cancel = () => {
|
||||
resetForm()
|
||||
emit('update:modelValue', false)
|
||||
emit('update:formData', {})
|
||||
}
|
||||
const handleCustomerChange = async (value) => {
|
||||
form.deptName = ''
|
||||
if (value) {
|
||||
queryParams.parentId = value
|
||||
getPostList()
|
||||
} else {
|
||||
postList.value = []
|
||||
}
|
||||
}
|
||||
const getPostList = async () => {
|
||||
const res = await getCustomerListApi(queryParams);
|
||||
if (res.code === 200) {
|
||||
postList.value = res.rows || []
|
||||
}
|
||||
}
|
||||
const resetForm = () => {
|
||||
formRef.value?.resetFields()
|
||||
if(props.title === proxy.$t('position.addPosition')) {
|
||||
form.postId = ''
|
||||
form.postName = ''
|
||||
}else {
|
||||
Object.keys(form).forEach(key => {
|
||||
form[key] = ''
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
[() => props.title, () => props.formData],
|
||||
([newTitle, newFormData]) => {
|
||||
if (newTitle === proxy.$t('position.editPosition') && Object.keys(newFormData).length > 0) {
|
||||
Object.keys(form).forEach(key => {
|
||||
form[key] = newFormData[key]
|
||||
})
|
||||
}
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
watchEffect(() => {
|
||||
if (props.selectId && postList.value.length > 0) {
|
||||
nextTick(() => {
|
||||
form.deptId = props.selectId
|
||||
})
|
||||
}
|
||||
})
|
||||
watchEffect(() => {
|
||||
if (props.parentId) {
|
||||
form.customerId = props.parentId
|
||||
queryParams.parentId = props.parentId
|
||||
getPostList()
|
||||
// const selectedDept = deptList.value.find(item => item.deptId === props.parentId)
|
||||
// if (selectedDept) {
|
||||
// form.customerName = selectedDept.deptName
|
||||
// form.customerId = props.parentId
|
||||
// }
|
||||
}
|
||||
})
|
||||
onMounted(() => {
|
||||
getDepList()
|
||||
})
|
||||
defineExpose({
|
||||
visible
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-footer {
|
||||
text-align: center;
|
||||
}
|
||||
:deep(.el-form-item__label) {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
233
src/views/customer/customerManage.vue
Normal file
233
src/views/customer/customerManage.vue
Normal file
@ -0,0 +1,233 @@
|
||||
<template>
|
||||
<div class="contentBox">
|
||||
<el-card class="card" shadow="hover">
|
||||
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch">
|
||||
<el-form-item :label="proxy.$t('customer.customerName')" prop="customerName">
|
||||
<el-input
|
||||
v-model="queryParams.deptName"
|
||||
:placeholder="proxy.$t('customer.inputCustomerName')"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">{{proxy.$t('button.search')}}</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">{{proxy.$t('button.reset')}}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Plus"
|
||||
@click="handleAdd"
|
||||
>{{proxy.$t('customer.addCustomer')}}</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="customerList" boreder>
|
||||
<el-table-column :label="proxy.$t('common.sort')" type="index" width="60" align="center" />
|
||||
<el-table-column :label="proxy.$t('customer.customerName')" align="center" prop="deptName" />
|
||||
<el-table-column :label="proxy.$t('customer.leader')" align="center" prop="leader" />
|
||||
<el-table-column :label="proxy.$t('customer.contact')" align="center" prop="phone" />
|
||||
<el-table-column :label="proxy.$t('customer.contactAddress')" align="center" prop="address" show-overflow-tooltip />
|
||||
<el-table-column :label="proxy.$t('customer.createBy')" align="center" prop="createBy" />
|
||||
<el-table-column :label="proxy.$t('customer.createTime')" align="center" prop="createTime" width="160" />
|
||||
<el-table-column :label="proxy.$t('common.action')" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
>{{proxy.$t('button.edit')}}</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
>{{proxy.$t('button.delete')}}</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="View"
|
||||
@click="handleViewDevices(scope.row)"
|
||||
>{{ proxy.$t('customer.checkDevice') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination">
|
||||
<el-pagination
|
||||
v-if="total > 0"
|
||||
v-model:current-page="queryParams.pageNum"
|
||||
v-model:page-size="queryParams.pageSize"
|
||||
:total="total"
|
||||
:page-sizes="[10, 20, 30, 50]"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
<CustomerDialog
|
||||
ref="dialogRef"
|
||||
:title="dialog.title"
|
||||
:formData="dialog.form"
|
||||
v-model="dialog.visible"
|
||||
@success="handleSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { ElMessageBox, ElMessage } from 'element-plus'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import CustomerDialog from './common/CustomerDialog.vue';
|
||||
import {getCustomerListApi, getDepartmentDetailApi, deleteDepartmentApi} from "@/api/customer/index.js";
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { t } = useI18n();
|
||||
const router = useRouter()
|
||||
const loading = ref(false)
|
||||
const showSearch = ref(true)
|
||||
const total = ref(0)
|
||||
// 添加弹框相关的数据
|
||||
const dialog = reactive({
|
||||
title: proxy.$t('customer.addCustomer'),
|
||||
visible: false,
|
||||
form: {
|
||||
deptName: '',
|
||||
leader: '',
|
||||
phone: '',
|
||||
address: '',
|
||||
orderNum: 0,
|
||||
status:"0",
|
||||
parentId:100,
|
||||
ancestors: '',
|
||||
deptId: undefined,
|
||||
}
|
||||
})
|
||||
const dialogRef = ref()
|
||||
// 查询参数
|
||||
const queryParams = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
deptName: undefined,
|
||||
parentId: 100,
|
||||
})
|
||||
|
||||
// 客户列表数据
|
||||
const customerList = ref([])
|
||||
|
||||
// 获取客户列表
|
||||
const getList = async() => {
|
||||
loading.value = true
|
||||
// 这里调用后端API获取数据
|
||||
const res = await getCustomerListApi(queryParams);
|
||||
if(res.code === 200) {
|
||||
customerList.value = res.rows || []
|
||||
total.value = res.total
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 500)
|
||||
}
|
||||
|
||||
// 查询按钮操作
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNum = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
// 重置按钮操作
|
||||
const resetQuery = () => {
|
||||
queryParams.deptName = undefined
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
// 新增按钮操作
|
||||
const handleAdd = () => {
|
||||
dialog.title = proxy.$t('customer.addCustomer')
|
||||
dialog.visible = true
|
||||
}
|
||||
|
||||
// 修改按钮操作
|
||||
const handleUpdate = async(row) => {
|
||||
console.log(row)
|
||||
const res = await getDepartmentDetailApi(row.deptId)
|
||||
if(res.code === 200) {
|
||||
dialog.title = proxy.$t('customer.editCustomer')
|
||||
dialog.visible = true
|
||||
dialog.form.deptName = res.data.deptName
|
||||
dialog.form.leader = res.data.leader
|
||||
dialog.form.phone = res.data.phone
|
||||
dialog.form.address = res.data.address
|
||||
dialog.form.parentId = res.data.parentId
|
||||
dialog.form.orderNum = res.data.orderNum
|
||||
dialog.form.status = res.data.status
|
||||
dialog.form.deptId = res.data.deptId
|
||||
dialog.form.ancestors = res.data.ancestors
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
}
|
||||
|
||||
// 删除按钮操作
|
||||
const handleDelete = (row) => {
|
||||
ElMessageBox.confirm(
|
||||
proxy.$t('common.deleteItem'),
|
||||
proxy.$t('common.warning'),
|
||||
{
|
||||
confirmButtonText: proxy.$t('button.save'),
|
||||
cancelButtonText: proxy.$t('button.cancel'),
|
||||
type: "warning"
|
||||
}
|
||||
).then(async () => {
|
||||
// 这里调用删除API
|
||||
const res = await deleteDepartmentApi(row.deptId)
|
||||
if(res.code === 200) {
|
||||
ElMessage.success(res.msg)
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
getList()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
// 查看设备按钮操作
|
||||
const handleViewDevices = (row) => {
|
||||
router.push({ path: '/equipment/equipmentManage', query: { id: row.deptId } });
|
||||
}
|
||||
// 添加成功回调方法
|
||||
const handleSuccess = () => {
|
||||
getList()
|
||||
}
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.contentBox {
|
||||
height:calc(100vh - 130px);
|
||||
margin:20px;
|
||||
.card {
|
||||
width:100%;
|
||||
height: 100%;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
}
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.mb8 {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
</style>
|
330
src/views/customer/departManage.vue
Normal file
330
src/views/customer/departManage.vue
Normal file
@ -0,0 +1,330 @@
|
||||
<template>
|
||||
<div class="contentBox">
|
||||
<div class="contentLeft">
|
||||
<el-card>
|
||||
<el-input
|
||||
v-model="queryParams.deptName"
|
||||
:placeholder="proxy.$t('customer.inputCustomerName')"
|
||||
class="filter-input"
|
||||
clearable
|
||||
@input="handleFilter"
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon><Search /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-button style="margin:20px 0 10px" type="primary" @click="handleAll" link>{{proxy.$t('department.allCustomer')}}</el-button>
|
||||
<el-divider style="margin:0"/>
|
||||
<ul class="customer-container">
|
||||
<li :class="{ active: selectedId === item.deptId }" @click="handleNodeClick(item)" class="customer-item" v-for="(item,index) in customerList" :key="index">
|
||||
{{item.deptName}}
|
||||
</li>
|
||||
</ul>
|
||||
</el-card>
|
||||
</div>
|
||||
<div class="contentRight">
|
||||
<el-card>
|
||||
<el-form :model="queryParams" ref="queryForm" :inline="true">
|
||||
<el-form-item :label="proxy.$t('department.departmentName')" prop="deptName">
|
||||
<el-input
|
||||
v-model="queryParams.deptName"
|
||||
:placeholder="proxy.$t('department.inputDepartmentName')"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">{{proxy.$t('button.search')}}</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">{{proxy.$t('button.reset')}}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Plus"
|
||||
@click="handleAdd"
|
||||
>{{proxy.$t('department.addDepartment')}}</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="deptList" border>
|
||||
<el-table-column :label="proxy.$t('common.sort')" type="index" width="60" align="center" />
|
||||
<el-table-column :label="proxy.$t('department.departmentName')" align="center" prop="deptName" />
|
||||
<el-table-column :label="proxy.$t('department.leader')" align="center" prop="leader" />
|
||||
<el-table-column :label="proxy.$t('common.customerAffiliation')" align="center" prop="parentName" />
|
||||
<el-table-column :label="proxy.$t('customer.contact')" align="center" prop="phone" />
|
||||
<el-table-column :label="proxy.$t('customer.createTime')" align="center" prop="createTime" width="160" />
|
||||
<el-table-column :label="proxy.$t('common.action')" align="center" width="150">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
>{{proxy.$t('button.edit')}}</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
>{{proxy.$t('button.delete')}}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="pagination">
|
||||
<el-pagination
|
||||
v-if="total > 0"
|
||||
v-model:current-page="queryParamsNoPage.pageNum"
|
||||
v-model:page-size="queryParamsNoPage.pageSize"
|
||||
:total="total"
|
||||
:page-sizes="[10, 20, 30, 50]"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
<DepartmentDialog
|
||||
ref="dialogRef"
|
||||
:title="dialog.title"
|
||||
:dept-list="customerList"
|
||||
:selected-name="selectName"
|
||||
:selected-id="selectedId"
|
||||
:order-num="0"
|
||||
:formData="dialog.form"
|
||||
v-model="dialog.visible"
|
||||
@success="handleSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { ElMessageBox, ElMessage } from 'element-plus'
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import DepartmentDialog from "@/views/customer/common/DepartmentDialog.vue";
|
||||
import {getDepartmentDetailApi, getCustomerListApi, deleteDepartmentApi} from '@/api/customer'
|
||||
const loading = ref(false)
|
||||
const { proxy } = getCurrentInstance();
|
||||
const selectName = ref('')
|
||||
const customerList = ref([])
|
||||
const originalList = ref([])
|
||||
const total = ref(0)
|
||||
// 添加弹框相关的数据
|
||||
const dialog = reactive({
|
||||
title: proxy.$t('department.addDepartment'),
|
||||
visible: false,
|
||||
form: {
|
||||
deptName: '',
|
||||
leader: '',
|
||||
phone: '',
|
||||
address: '',
|
||||
orderNum: 0,
|
||||
status:"0",
|
||||
parentId:100,
|
||||
ancestors: '',
|
||||
deptId: undefined,
|
||||
parentName:undefined
|
||||
}
|
||||
})
|
||||
const dialogRef = ref()
|
||||
const selectedId = ref(null)
|
||||
|
||||
|
||||
// 查询参数
|
||||
const queryParams = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10000000000,
|
||||
parentId: 100,
|
||||
deptName: '',
|
||||
customerId: undefined
|
||||
})
|
||||
const queryParamsNoPage = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
parentId: -1,
|
||||
deptName: '',
|
||||
customerId: undefined
|
||||
})
|
||||
|
||||
// 部门列表数据
|
||||
const deptList = ref([])
|
||||
// 点击全部
|
||||
const handleAll = () => {
|
||||
selectedId.value = null
|
||||
queryParamsNoPage.parentId = -1
|
||||
selectName.value = ''
|
||||
// 查询全部部门
|
||||
getList()
|
||||
}
|
||||
// 树节点点击事件
|
||||
const handleNodeClick = (item) => {
|
||||
selectedId.value = item.deptId
|
||||
selectName.value = item.deptName
|
||||
queryParamsNoPage.parentId = selectedId.value
|
||||
getList()
|
||||
}
|
||||
const handleFilter = () => {
|
||||
if (!queryParams.deptName) {
|
||||
customerList.value = originalList.value
|
||||
return
|
||||
}
|
||||
customerList.value = originalList.value.filter(item =>
|
||||
item.deptName.toLowerCase().includes(queryParams.deptName.toLowerCase())
|
||||
)
|
||||
}
|
||||
// 查询所有客户
|
||||
const getCustomerList = async() => {
|
||||
loading.value = true
|
||||
// 这里调用后端API获取数据
|
||||
const res = await getCustomerListApi(queryParams);
|
||||
if(res.code === 200) {
|
||||
customerList.value = res.rows || []
|
||||
originalList.value = [...customerList.value]
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 500)
|
||||
}
|
||||
|
||||
// 获取部门列表
|
||||
const getList = async() => {
|
||||
loading.value = true
|
||||
const res = await getCustomerListApi(queryParamsNoPage);
|
||||
if(res.code === 200) {
|
||||
deptList.value = res.rows || []
|
||||
total.value = res.total
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 500)
|
||||
}
|
||||
const handleSizeChange = (val) => {
|
||||
queryParamsNoPage.pageSize = val
|
||||
getList()
|
||||
}
|
||||
|
||||
const handleCurrentChange = (val) => {
|
||||
queryParamsNoPage.pageNum = val
|
||||
getList()
|
||||
}
|
||||
// 添加成功回调方法
|
||||
const handleSuccess = () => {
|
||||
getList()
|
||||
}
|
||||
// 查询按钮操作
|
||||
const handleQuery = () => {
|
||||
getList()
|
||||
}
|
||||
|
||||
// 重置按钮操作
|
||||
const resetQuery = () => {
|
||||
queryParams.deptName = ''
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
// 新增按钮操作
|
||||
const handleAdd = () => {
|
||||
// 处理新增部门
|
||||
dialog.title = proxy.$t('department.addDepartment')
|
||||
dialog.visible = true
|
||||
}
|
||||
|
||||
// 修改按钮操作
|
||||
const handleUpdate = async(row) => {
|
||||
// 处理编辑部门
|
||||
const res = await getDepartmentDetailApi(row.deptId)
|
||||
if(res.code === 200) {
|
||||
dialog.title = proxy.$t('department.editDepartment')
|
||||
dialog.visible = true
|
||||
dialog.form.deptName = res.data.deptName
|
||||
dialog.form.leader = res.data.leader
|
||||
dialog.form.phone = res.data.phone
|
||||
dialog.form.address = res.data.address
|
||||
dialog.form.parentId = res.data.parentId
|
||||
dialog.form.orderNum = res.data.orderNum
|
||||
dialog.form.status = res.data.status
|
||||
dialog.form.deptId = res.data.deptId
|
||||
dialog.form.ancestors = res.data.ancestors
|
||||
dialog.form.parentName = res.data.parentName
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
}
|
||||
|
||||
// 删除按钮操作
|
||||
const handleDelete = (row) => {
|
||||
ElMessageBox.confirm(
|
||||
proxy.$t('common.deleteItem'),
|
||||
proxy.$t('common.warning'),
|
||||
{
|
||||
confirmButtonText: proxy.$t('button.save'),
|
||||
cancelButtonText: proxy.$t('button.cancel'),
|
||||
type: "warning"
|
||||
}
|
||||
).then(async () => {
|
||||
const res = await deleteDepartmentApi(row.deptId)
|
||||
if (res.code === 200) {
|
||||
ElMessage.success(res.msg)
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
getList()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getCustomerList()
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.contentBox {
|
||||
height:calc(100vh - 130px);
|
||||
margin:20px;
|
||||
display: flex;
|
||||
.contentLeft {
|
||||
width:24%;
|
||||
margin-right:14px;
|
||||
}
|
||||
.contentRight {
|
||||
flex:1;
|
||||
}
|
||||
.contentLeft,.contentRight {
|
||||
height:100%;
|
||||
overflow-y: scroll;
|
||||
padding-bottom:10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.card {
|
||||
width:100%;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
}
|
||||
.customer-item {
|
||||
color: #606266;
|
||||
padding:12px 0;
|
||||
cursor: pointer;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
.customer-item:hover {
|
||||
background-color: #F5F7FA;
|
||||
}
|
||||
|
||||
.customer-item.active {
|
||||
color: #409EFF;
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
282
src/views/customer/positionManage.vue
Normal file
282
src/views/customer/positionManage.vue
Normal file
@ -0,0 +1,282 @@
|
||||
<template>
|
||||
<div class="contentBox">
|
||||
<div class="contentLeft">
|
||||
<el-card>
|
||||
<el-input v-model="deptName" :placeholder="proxy.$t('department.inputDepartmentName')" clearable>
|
||||
<template #append>
|
||||
<el-button :icon="Search" />
|
||||
</template>
|
||||
</el-input>
|
||||
<div style="margin-top:10px">
|
||||
<el-tree :data="deptOption" :props="{ label: 'label', children: 'children' }" :expand-on-click-node="false" :filter-node-method="filterNode" ref="deptTreeRef" node-key="id" highlight-current default-expand-all @node-click="handleNodeClick" />
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
<div class="contentRight">
|
||||
<el-card>
|
||||
<el-form :model="queryParams" ref="queryForm" :inline="true">
|
||||
<el-form-item :label="proxy.$t('position.positionName')" prop="positionName">
|
||||
<el-input
|
||||
v-model="queryParams.positionName"
|
||||
:placeholder="proxy.$t('position.inputPositionName')"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">{{proxy.$t('button.search')}}</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">{{proxy.$t('button.reset')}}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Plus"
|
||||
@click="handleAdd"
|
||||
>{{proxy.$t('position.addPosition')}}</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="positionList" border>
|
||||
<el-table-column :label="proxy.$t('common.sort')" type="index" width="60" align="center" />
|
||||
<el-table-column :label="proxy.$t('position.positionName')" align="center" prop="postName" />
|
||||
<el-table-column :label="proxy.$t('common.customerAffiliation')" align="center" prop="customerName" />
|
||||
<el-table-column :label="proxy.$t('position.department')" align="center" prop="deptName" />
|
||||
<el-table-column :label="proxy.$t('customer.createBy')" align="center" prop="createBy" />
|
||||
<el-table-column :label="proxy.$t('customer.createTime')" align="center" prop="createTime" width="160" />
|
||||
<el-table-column :label="proxy.$t('common.action')" align="center" width="150">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
>{{proxy.$t('button.edit')}}</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
>{{proxy.$t('button.delete')}}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="pagination">
|
||||
<el-pagination
|
||||
v-if="total > 0"
|
||||
v-model:current-page="queryParams.pageNum"
|
||||
v-model:page-size="queryParams.pageSize"
|
||||
:total="total"
|
||||
:page-sizes="[10, 20, 30, 50]"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
<PostDialog
|
||||
ref="dialogRef"
|
||||
:title="dialog.title"
|
||||
:parent-id="parentId"
|
||||
:select-id="queryParams.deptId"
|
||||
:select-name="selectedName"
|
||||
:formData="dialog.form"
|
||||
v-model="dialog.visible"
|
||||
@success="handleSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import {Search} from "@element-plus/icons-vue";
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import {getDepartmentPostListApi, getDepartmentTreeApi, deleteDepartmentPostApi,getDepartmentPostDetailApi} from '@/api/customer'
|
||||
import PostDialog from './common/PostDialog.vue';
|
||||
const { proxy } = getCurrentInstance();
|
||||
const loading = ref(false)
|
||||
const total = ref(0)
|
||||
const dialog = reactive({
|
||||
title: proxy.$t('position.addPosition'),
|
||||
visible: false,
|
||||
form: {
|
||||
customerId:'',
|
||||
customerName:'',
|
||||
deptId: '',
|
||||
deptName:'',
|
||||
postName:'',
|
||||
postId:'',
|
||||
id:''
|
||||
}
|
||||
})
|
||||
// 查询参数
|
||||
const queryParams = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
postName: undefined,
|
||||
deptId: undefined,
|
||||
})
|
||||
|
||||
const deptName = ref("");
|
||||
const deptOption = ref(undefined);
|
||||
const filterNode = (value, data) => {
|
||||
if (!value) return true;
|
||||
return data.label.indexOf(value) !== -1;
|
||||
};
|
||||
const dialogRef = ref()
|
||||
const selectedName = ref('')
|
||||
const parentId = ref(null)
|
||||
const handleNodeClick = (data) => {
|
||||
console.log(data,'===data')
|
||||
queryParams.deptId = data.id;
|
||||
selectedName.value = data.label;
|
||||
parentId.value = data.parentId;
|
||||
getList();
|
||||
};
|
||||
watch(deptName, val => {
|
||||
proxy.$refs["deptTreeRef"].filter(val);
|
||||
});
|
||||
|
||||
// 岗位列表数据
|
||||
const positionList = ref([])
|
||||
|
||||
// 查询按钮操作
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNum = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
// 重置按钮操作
|
||||
const resetQuery = () => {
|
||||
queryParams.deptId = undefined;
|
||||
proxy.$refs.deptTreeRef.setCurrentKey(null);
|
||||
queryParams.postName = undefined
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
// 新增按钮操作
|
||||
const handleAdd = () => {
|
||||
// 处理新增部门
|
||||
dialog.title = proxy.$t('position.addPosition')
|
||||
dialog.visible = true
|
||||
}
|
||||
|
||||
// 修改按钮操作
|
||||
const handleUpdate = async(row) => {
|
||||
// 处理编辑部门
|
||||
console.log(row,'===')
|
||||
const res = await getDepartmentPostDetailApi(row.id)
|
||||
if(res.code === 200) {
|
||||
dialog.title = proxy.$t('position.editPosition')
|
||||
dialog.visible = true
|
||||
dialog.form.postName = res.data.postName
|
||||
dialog.form.customerId = res.data.customerId
|
||||
dialog.form.deptId = res.data.deptId
|
||||
dialog.form.customerName = res.data.customerName
|
||||
dialog.form.deptName = res.data.deptName
|
||||
dialog.form.postId = res.data.postId
|
||||
dialog.form.id = res.data.id
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
}
|
||||
|
||||
// 添加成功回调方法
|
||||
const handleSuccess = () => {
|
||||
getList()
|
||||
}
|
||||
// 删除按钮操作
|
||||
const handleDelete = (row) => {
|
||||
console.log(row)
|
||||
ElMessageBox.confirm(
|
||||
proxy.$t('common.deleteItem'),
|
||||
proxy.$t('common.warning'),
|
||||
{
|
||||
confirmButtonText: proxy.$t('button.save'),
|
||||
cancelButtonText: proxy.$t('button.cancel'),
|
||||
type: "warning"
|
||||
}
|
||||
).then(async () => {
|
||||
// 这里调用删除API
|
||||
const res = await deleteDepartmentPostApi(row.id)
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('删除成功')
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
getList()
|
||||
}).catch(() => {})
|
||||
}
|
||||
// 获取部门树形数据
|
||||
const getDeptTree = async() => {
|
||||
// 这里调用获取部门树形数据的API
|
||||
const res = await getDepartmentTreeApi();
|
||||
if(res.code === 200) {
|
||||
deptOption.value = res.data
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
}
|
||||
// 获取岗位列表
|
||||
const getList = async() => {
|
||||
loading.value = true
|
||||
// 这里调用后端API获取数据
|
||||
const res = await getDepartmentPostListApi(queryParams);
|
||||
console.log(res,'000')
|
||||
if(res.code === 200) {
|
||||
positionList.value = res.rows
|
||||
total.value = res.total
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 500)
|
||||
}
|
||||
|
||||
const handleSizeChange = (val) => {
|
||||
queryParams.pageSize = val
|
||||
getList()
|
||||
}
|
||||
|
||||
const handleCurrentChange = (val) => {
|
||||
queryParams.pageNum = val
|
||||
getList()
|
||||
}
|
||||
onMounted(() => {
|
||||
getList()
|
||||
getDeptTree()
|
||||
})
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.contentBox {
|
||||
height:calc(100vh - 130px);
|
||||
margin:20px;
|
||||
display: flex;
|
||||
.contentLeft {
|
||||
width:24%;
|
||||
margin-right:14px;
|
||||
}
|
||||
.contentRight {
|
||||
flex:1;
|
||||
}
|
||||
.contentLeft,.contentRight {
|
||||
height:100%;
|
||||
overflow-y: scroll;
|
||||
padding-bottom:10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.card {
|
||||
width:100%;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
}
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
156
src/views/equipment/common/CircleDetectionChart.vue
Normal file
156
src/views/equipment/common/CircleDetectionChart.vue
Normal file
@ -0,0 +1,156 @@
|
||||
<template>
|
||||
<div class="circle-chart">
|
||||
<div ref="chartRef" style="width: 400px; height: 400px;"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted, watch } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
|
||||
const props = defineProps({
|
||||
innerData: {
|
||||
type: Array,
|
||||
default: () => [0.68, 0.85, 1.07, 1.18, 1.13, 0.97, 0.67, 0.98]
|
||||
},
|
||||
middleData: {
|
||||
type: Array,
|
||||
default: () => [9.52, 9.95, 10.53, 11.04, 11.34, 11.15, 9.66, 10.40]
|
||||
},
|
||||
outerData: {
|
||||
type: Array,
|
||||
default: () => [1.15, 1.19, 1.21, 1.17, 1.05, 0.96, 1.06, 0.79]
|
||||
},
|
||||
errorRange: {
|
||||
type: Array,
|
||||
default: () => [180, 225] // 错误区域的角度范围
|
||||
}
|
||||
})
|
||||
|
||||
const chartRef = ref(null)
|
||||
let chart = null
|
||||
|
||||
const initChart = () => {
|
||||
if (chart) {
|
||||
chart.dispose()
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
chart = echarts.init(chartRef.value)
|
||||
const option = {
|
||||
color: ['#67F9D8', '#FFE434', '#56A3F1', '#FF917C'],
|
||||
legend: {
|
||||
show: false
|
||||
},
|
||||
radar: [
|
||||
{
|
||||
indicator: [
|
||||
{ text: `${props.outerData[0]}\n${props.middleData[0]}\n${props.innerData[0]}`, max: 100 },
|
||||
{ text: `${props.outerData[1]}\n${props.middleData[1]}\n${props.innerData[1]}`, max: 100 },
|
||||
{ text: `${props.outerData[2]}\n${props.middleData[2]}\n${props.innerData[2]}`, max: 100 },
|
||||
{ text: `${props.outerData[3]}\n${props.middleData[3]}\n${props.innerData[3]}`, max: 100 },
|
||||
{ text: `${props.outerData[4]}\n${props.middleData[4]}\n${props.innerData[4]}`, max: 100 },
|
||||
{ text: `${props.outerData[5]}\n${props.middleData[5]}\n${props.innerData[5]}`, max: 100 },
|
||||
{ text: `${props.outerData[6]}\n${props.middleData[6]}\n${props.innerData[6]}`, max: 100 },
|
||||
{ text: `${props.outerData[7]}\n${props.middleData[7]}\n${props.innerData[7]}`, max: 100 }
|
||||
],
|
||||
center: ['50%', '50%'], // 居中显示
|
||||
radius: '60%', // 调整大小
|
||||
startAngle: 90,
|
||||
splitNumber: 1, // 只显示一个圈
|
||||
shape: 'circle',
|
||||
axisName: {
|
||||
show: true,
|
||||
color: '#333',
|
||||
fontSize: 12,
|
||||
rich: {
|
||||
value: {
|
||||
lineHeight: 20,
|
||||
align: 'center'
|
||||
}
|
||||
}
|
||||
},
|
||||
splitArea: {
|
||||
show: false
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#999'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#999',
|
||||
type: 'dashed'
|
||||
},
|
||||
formatter: function(params) {
|
||||
// 只显示45°、135°、225°、315°的线
|
||||
const angles = [45, 135, 225, 315]
|
||||
return angles.includes(params.value) ? params.value : ''
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
type: 'radar',
|
||||
data: [
|
||||
{
|
||||
value: [80, 70, 90, 85, 75, 88, 82, 78],
|
||||
symbol: 'none', // 去掉数据点
|
||||
lineStyle: {
|
||||
color: '#1890ff',
|
||||
width: 2
|
||||
},
|
||||
areaStyle: {
|
||||
color: 'rgba(24, 144, 255, 0.2)'
|
||||
},
|
||||
smooth: true, // 启用平滑曲线
|
||||
smoothMonotone: 'cw', // 顺时针平滑
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
chart.setOption(option)
|
||||
})
|
||||
}
|
||||
// 监听数据变化
|
||||
// watch([() => props.innerData, () => props.middleData, () => props.outerData], () => {
|
||||
// initChart()
|
||||
// }, { deep: true })
|
||||
|
||||
// 添加刷新方法
|
||||
const refresh = () => {
|
||||
initChart()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initChart()
|
||||
window.addEventListener('resize', () => chart?.resize())
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', () => chart?.resize())
|
||||
chart?.dispose()
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
refresh
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.circle-chart {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
157
src/views/equipment/common/CurveChart.vue
Normal file
157
src/views/equipment/common/CurveChart.vue
Normal file
@ -0,0 +1,157 @@
|
||||
<template>
|
||||
<div class="curve-chart">
|
||||
<div class="chart-title">{{title}}</div>
|
||||
<div ref="chartRef" style="width: 100%; height: 400px"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '#ff4444'
|
||||
},
|
||||
})
|
||||
const chartRef = ref(null)
|
||||
let chart = null
|
||||
|
||||
const initChart = () => {
|
||||
if (chart) {
|
||||
chart.dispose()
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
chart = echarts.init(chartRef.value)
|
||||
const option = {
|
||||
grid: {
|
||||
left: '12%', // 增加左边距
|
||||
right: '5%',
|
||||
top: '8%', // 增加顶部边距
|
||||
bottom: '12%', // 增加底部边距
|
||||
containLabel: true // 确保标签显示完整
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
min: 0,
|
||||
max: 120,
|
||||
interval: 15,
|
||||
axisLine: {
|
||||
show: true
|
||||
},
|
||||
axisLabel: {
|
||||
margin: 12, // 增加标签与轴线距离
|
||||
formatter: '{value}'
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
type: 'dashed'
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
min: 0,
|
||||
max: 48000,
|
||||
interval: 6000,
|
||||
axisLine: {
|
||||
show: true
|
||||
},
|
||||
axisLabel: {
|
||||
margin: 16, // 增加标签与轴线距离
|
||||
formatter: '{value}'
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
type: 'dashed'
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [{
|
||||
type: 'line',
|
||||
data: generateData(),
|
||||
symbol: 'none',
|
||||
smooth: 0.2, // 添加平滑度
|
||||
smoothMonotone: 'x', // 保持单调性
|
||||
lineStyle: {
|
||||
color: props.color,
|
||||
width: 2
|
||||
},
|
||||
animation: false
|
||||
}]
|
||||
}
|
||||
|
||||
chart.setOption(option)
|
||||
})
|
||||
}
|
||||
|
||||
// 添加 resize 方法
|
||||
const resize = () => {
|
||||
nextTick(() => {
|
||||
chart?.resize()
|
||||
})
|
||||
}
|
||||
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
resize
|
||||
})
|
||||
|
||||
const generateData = () => {
|
||||
const data = []
|
||||
for (let i = 0; i <= 120; i += 0.5) { // 增加采样点密度
|
||||
let value
|
||||
if (i <= 45) {
|
||||
value = 40000
|
||||
} else if (i > 45 && i <= 60) {
|
||||
// 使用非线性插值
|
||||
const progress = (i - 45) / 15
|
||||
value = 40000 - (34000 * Math.pow(progress, 1.2))
|
||||
} else if (i > 60 && i <= 75) {
|
||||
value = 6000
|
||||
} else if (i > 75 && i <= 90) {
|
||||
// 使用非线性插值
|
||||
const progress = (i - 75) / 15
|
||||
value = 6000 + (34000 * Math.pow(progress, 0.8))
|
||||
} else {
|
||||
value = 40000
|
||||
}
|
||||
data.push([i, value])
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initChart()
|
||||
window.addEventListener('resize', () => chart?.resize())
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', () => chart?.resize())
|
||||
chart?.dispose()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.curve-chart {
|
||||
width:100%;
|
||||
height:100%;
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
|
||||
.chart-title {
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
32
src/views/equipment/common/DataTable.vue
Normal file
32
src/views/equipment/common/DataTable.vue
Normal file
@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<div class="data-table">
|
||||
<el-table :data="tableData" border>
|
||||
<el-table-column prop="name" label="数据项" width="180" />
|
||||
<el-table-column prop="arm1" label="扫描臂1">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.type === 'status'" :type="scope.row.arm1.type">
|
||||
{{ scope.row.arm1.value }}
|
||||
</el-tag>
|
||||
<span v-else>{{ scope.row.arm1 }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="arm2" label="扫描臂2">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.type === 'status'" :type="scope.row.arm2.type">
|
||||
{{ scope.row.arm2.value }}
|
||||
</el-tag>
|
||||
<span v-else>{{ scope.row.arm2 }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps({
|
||||
tableData: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
</script>
|
216
src/views/equipment/common/DeviceDialog.vue
Normal file
216
src/views/equipment/common/DeviceDialog.vue
Normal file
@ -0,0 +1,216 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:title="title"
|
||||
v-model="visible"
|
||||
width="600px"
|
||||
append-to-body
|
||||
:before-close="cancel"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-width="appStore.language === 'zh_CN' ? '140px' : '200px'"
|
||||
label-position="right"
|
||||
>
|
||||
<el-form-item :label="proxy.$t('equipment.deviceName')" prop="deviceName">
|
||||
<el-input v-model="form.deviceName" :placeholder="proxy.$t('equipment.inputDeviceName')" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="proxy.$t('equipment.deviceCode')" prop="deviceCode">
|
||||
<el-input v-model="form.deviceCode" :placeholder="proxy.$t('equipment.inputDeviceCode')" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="proxy.$t('equipment.deviceModel')" prop="deviceModel">
|
||||
<el-select v-model="form.deviceModel" :placeholder="proxy.$t('equipment.inputDeviceModel')">
|
||||
<el-option
|
||||
v-for="item in typeOptions"
|
||||
:key="item.id"
|
||||
:label="item.deviceModel"
|
||||
:value="item.deviceModel"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="proxy.$t('common.customerAffiliation')" prop="customerId">
|
||||
<el-select v-model="form.customerId" :placeholder="proxy.$t('common.select')">
|
||||
<el-option
|
||||
v-for="item in customerList"
|
||||
:key="item.deptId"
|
||||
:label="item.deptName"
|
||||
:value="item.deptId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="proxy.$t('common.userAffiliation')" prop="userIdList">
|
||||
<el-select v-model="form.userIdList" :placeholder="proxy.$t('common.selectMul')" multiple>
|
||||
<el-option
|
||||
v-for="item in userList"
|
||||
:key="item.userId"
|
||||
:label="item.nickName"
|
||||
:value="item.userId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="proxy.$t('equipment.localEMQ')" v-if="title === proxy.$t('button.add')" prop="emqxAuth.localUserName" >
|
||||
<el-input v-model="form.emqxAuth.localUserName" :placeholder="proxy.$t('common.select')" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="proxy.$t('equipment.localPassword')" v-if="title === proxy.$t('button.add')" prop="emqxAuth.localPass">
|
||||
<el-input v-model="form.emqxAuth.localPass" :placeholder="proxy.$t('common.select')" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="proxy.$t('equipment.localEMQPassword')" v-if="title === proxy.$t('button.add')" prop="emqxAuth.cloudUserName">
|
||||
<el-input v-model="form.emqxAuth.cloudUserName" :placeholder="proxy.$t('common.select')" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="proxy.$t('equipment.localEMQUser')" v-if="title === proxy.$t('button.add')" prop="emqxAuth.cloudPass">
|
||||
<el-input v-model="form.emqxAuth.cloudPass" :placeholder="proxy.$t('common.select')" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">{{ proxy.$t('button.save') }}</el-button>
|
||||
<el-button @click="cancel">{{proxy.$t('button.cancel')}}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive,watch } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import {getDeviceModelListApi, addDeviceApi, editDeviceApi} from "@/api/equipment/index.js";
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
const appStore = useAppStore();
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '新建设备'
|
||||
},
|
||||
formData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
customerList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
userList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'success'])
|
||||
|
||||
const visible = ref(false)
|
||||
const formRef = ref()
|
||||
|
||||
const form = reactive({
|
||||
deviceName: '',
|
||||
deviceCode: '',
|
||||
deviceModel: '',
|
||||
customerId: '',
|
||||
userIdList: [],
|
||||
emqxAuth: {
|
||||
localEmqUser: '',
|
||||
localPassword: '',
|
||||
cloudEmqUser: '',
|
||||
cloudPassword: ''
|
||||
},
|
||||
id:''
|
||||
})
|
||||
|
||||
const rules = {
|
||||
deviceName: [{ required: true, message: '请输入设备名称', trigger: 'blur' }],
|
||||
deviceCode: [{ required: true, message: '请输入设备编码', trigger: 'blur' }],
|
||||
deviceModel: [{ required: true, message: '请选择设备型号', trigger: 'change' }],
|
||||
customerId: [{ required: true, message: '请选择所属客户', trigger: 'change' }],
|
||||
emqxAuth: {
|
||||
localUserName: [{ required: true, message: '请输入本地EMQ用户名', trigger: 'blur' }],
|
||||
localPass: [{ required: true, message: '请输入本地密码', trigger: 'blur' }],
|
||||
cloudUserName: [{ required: true, message: '请输入云EMQ用户名', trigger: 'blur' }],
|
||||
cloudPass: [{ required: true, message: '请输入云密码', trigger: 'blur' }]
|
||||
}
|
||||
}
|
||||
const query = {
|
||||
pageNum: 1,
|
||||
pageSize: 100000000
|
||||
}
|
||||
// 设备型号选项
|
||||
const typeOptions = ref([])
|
||||
// 查询设备型号列表
|
||||
const getDeviceTypeList = async () => {
|
||||
// 这里应该调用后端API获取数据
|
||||
const res = await getDeviceModelListApi(query)
|
||||
if (res.code === 200) {
|
||||
typeOptions.value = res.rows || []
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
}
|
||||
const submitForm = async () => {
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
const res = props.title==='新增' ? await addDeviceApi(form): await editDeviceApi({deviceName: form.deviceName,
|
||||
deviceCode: form.deviceCode,
|
||||
deviceModel: form.deviceModel,
|
||||
customerId: form.customerId,
|
||||
userIdList: form.userIdList,
|
||||
id:form.id
|
||||
})
|
||||
if (res.code === 200) {
|
||||
emit('success', form)
|
||||
ElMessage.success(res.msg)
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
cancel()
|
||||
} catch (error) {
|
||||
console.error('表单验证失败', error)
|
||||
}
|
||||
}
|
||||
|
||||
const cancel = () => {
|
||||
formRef.value?.resetFields()
|
||||
Object.keys(form).forEach(key => {
|
||||
if (Array.isArray(form[key])) {
|
||||
form[key] = []
|
||||
} else {
|
||||
form[key] = ''
|
||||
}
|
||||
})
|
||||
emit('update:modelValue', false)
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.formData,
|
||||
(newVal) => {
|
||||
if (props.title === '编辑' && Object.keys(newVal).length > 0) {
|
||||
Object.keys(form).forEach(key => {
|
||||
if (key === 'userIdList') {
|
||||
form[key] = Array.isArray(newVal[key]) ? [...newVal[key]] : []
|
||||
} else if (key === 'emqxAuth') {
|
||||
form[key] = { ...newVal[key] }
|
||||
} else {
|
||||
form[key] = newVal[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
defineExpose({
|
||||
visible
|
||||
})
|
||||
onMounted(() => {
|
||||
getDeviceTypeList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-footer {
|
||||
text-align: center;
|
||||
padding-top: 20px;
|
||||
}
|
||||
:deep(.el-form-item__label) {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
88
src/views/equipment/common/DeviceSetting.vue
Normal file
88
src/views/equipment/common/DeviceSetting.vue
Normal file
@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<div class="device-settings">
|
||||
<el-form :model="formData" label-width="150px">
|
||||
<el-form-item v-for="item in settingItems" :key="item.key" :label="item.label">
|
||||
<div class="setting-item">
|
||||
<el-input
|
||||
v-model="formData[item.key]"
|
||||
:suffix="item.unit"
|
||||
style="width: 200px"
|
||||
>
|
||||
<template #append>
|
||||
<el-icon><Edit /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-button type="primary" @click="handleSet(item)">设定</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { Edit } from '@element-plus/icons-vue'
|
||||
|
||||
const formData = ref({
|
||||
oilTemp: '45.0',
|
||||
waterTemp: '40.0',
|
||||
airTemp: '40.0',
|
||||
boardTemp: '45.0',
|
||||
outTemp: '30.0',
|
||||
waterFlow: '0.0',
|
||||
scanSpeed: '40',
|
||||
interval: '10',
|
||||
voltage1: '55000',
|
||||
current1: '1800',
|
||||
filament1: '3000',
|
||||
preheat1: '1000',
|
||||
voltage2: '55000'
|
||||
})
|
||||
|
||||
const settingItems = [
|
||||
{ label: '油温上限', key: 'oilTemp', unit: '℃' },
|
||||
{ label: '水温报警线', key: 'waterTemp', unit: '℃' },
|
||||
{ label: '气温报警线', key: 'airTemp', unit: '℃' },
|
||||
{ label: '采集板温度报警线', key: 'boardTemp', unit: '℃' },
|
||||
{ label: '出风设定温度', key: 'outTemp', unit: '℃' },
|
||||
{ label: '水流下限', key: 'waterFlow', unit: 'L/min' },
|
||||
{ label: '扫描臂速度', key: 'scanSpeed', unit: 'mm/s' },
|
||||
{ label: '自定义间隔', key: 'interval', unit: 'min' },
|
||||
{ label: '射线电源设定电压1', key: 'voltage1', unit: 'V' },
|
||||
{ label: '射线电源设定电流1', key: 'current1', unit: 'μA' },
|
||||
{ label: '灯丝设定电流1', key: 'filament1', unit: 'mA' },
|
||||
{ label: '灯丝预热设定电流1', key: 'preheat1', unit: 'mA' },
|
||||
{ label: '射线电源设定电压2', key: 'voltage2', unit: 'mA' }
|
||||
]
|
||||
|
||||
const emit = defineEmits(['update'])
|
||||
|
||||
const handleSet = (item) => {
|
||||
console.log(item)
|
||||
emit('update', {
|
||||
key: item.key,
|
||||
value: formData.value[item.key]
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.device-settings {
|
||||
padding: 20px;
|
||||
|
||||
.setting-item {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:deep(.el-input-group__append) {
|
||||
padding: 0 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.el-form-item {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
80
src/views/equipment/common/DeviceStatus.vue
Normal file
80
src/views/equipment/common/DeviceStatus.vue
Normal file
@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<div class="device-status-table">
|
||||
<el-table :data="tableData" border style="width: 100%">
|
||||
<el-table-column prop="name" label="参数" align="center" />
|
||||
<el-table-column prop="arm1" label="扫描臂1" align="center">
|
||||
<template #default="scope">
|
||||
<template v-if="scope.row.type === 'tag'">
|
||||
<el-tag :type="scope.row.arm1.type" effect="plain">
|
||||
{{ scope.row.arm1.value }}
|
||||
</el-tag>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ scope.row.arm1 }}
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="arm2" label="扫描臂2" align="center">
|
||||
<template #default="scope">
|
||||
<template v-if="scope.row.type === 'tag'">
|
||||
<el-tag :type="scope.row.arm2.type" effect="plain">
|
||||
{{ scope.row.arm2.value }}
|
||||
</el-tag>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ scope.row.arm2 }}
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
statusData: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const tableData = computed(() => [
|
||||
{ name: '通信状态', arm1: props.statusData.arm1.communication, arm2: props.statusData.arm2.communication },
|
||||
{ name: '故障状态', arm1: props.statusData.arm1.fault, arm2: props.statusData.arm2.fault },
|
||||
{ name: '使能状态', arm1: props.statusData.arm1.enable, arm2: props.statusData.arm2.enable },
|
||||
{ name: '当前位置', arm1: props.statusData.arm1.position, arm2: props.statusData.arm2.position },
|
||||
{ name: '当前速度[r/min]', arm1: props.statusData.arm1.speed, arm2: props.statusData.arm2.speed },
|
||||
{
|
||||
name: '射线电源开关',
|
||||
type: 'tag',
|
||||
arm1: props.statusData.arm1.powerSwitch,
|
||||
arm2: props.statusData.arm2.powerSwitch
|
||||
},
|
||||
{ name: '射线电源安全锁', arm1: props.statusData.arm1.safetyLock, arm2: props.statusData.arm2.safetyLock },
|
||||
{ name: '射线电源设定电压[V]', arm1: props.statusData.arm1.voltageSet, arm2: props.statusData.arm2.voltageSet },
|
||||
{ name: '射线电源反馈电压[V]', arm1: props.statusData.arm1.voltageFeedback, arm2: props.statusData.arm2.voltageFeedback },
|
||||
{ name: '射线电源反馈电流[μA]', arm1: props.statusData.arm1.currentFeedback, arm2: props.statusData.arm2.currentFeedback },
|
||||
{ name: '射线电源设定电流[μA]', arm1: props.statusData.arm1.currentSet, arm2: props.statusData.arm2.currentSet }
|
||||
])
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.device-status-table {
|
||||
:deep(.el-table) {
|
||||
.el-tag {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
|
||||
&.el-tag--danger {
|
||||
background-color: #fef0f0;
|
||||
}
|
||||
}
|
||||
|
||||
.cell {
|
||||
padding: 8px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
207
src/views/equipment/common/DualChannelChart.vue
Normal file
207
src/views/equipment/common/DualChannelChart.vue
Normal file
@ -0,0 +1,207 @@
|
||||
<template>
|
||||
<div class="dual-channel-chart">
|
||||
<div class="chart-title">{{ title }}</div>
|
||||
<div ref="chartRef" style="height: 200px"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
|
||||
const props = defineProps({
|
||||
title: String,
|
||||
chartData: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const chartRef = ref(null)
|
||||
let chart = null
|
||||
|
||||
// const initChart = () => {
|
||||
// chart = echarts.init(chartRef.value)
|
||||
// const option = {
|
||||
// grid: {
|
||||
// top: 30,
|
||||
// right: 10,
|
||||
// bottom: 20,
|
||||
// left: 40
|
||||
// },
|
||||
// legend: {
|
||||
// data: ['a通道', 'b通道'],
|
||||
// right: 0,
|
||||
// top: 0
|
||||
// },
|
||||
// xAxis: {
|
||||
// type: 'value',
|
||||
// min: 0,
|
||||
// max: 100
|
||||
// },
|
||||
// yAxis: {
|
||||
// type: 'value',
|
||||
// min: 0,
|
||||
// max: 10000
|
||||
// },
|
||||
// series: [
|
||||
// {
|
||||
// name: 'a通道',
|
||||
// type: 'line',
|
||||
// data: props.chartData.a,
|
||||
// color: '#ff4444',
|
||||
// smooth: true
|
||||
// },
|
||||
// {
|
||||
// name: 'b通道',
|
||||
// type: 'line',
|
||||
// data: props.chartData.b,
|
||||
// color: '#67c23a',
|
||||
// smooth: true
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// chart = echarts.init(chartRef.value)
|
||||
// chart.setOption(option)
|
||||
// }
|
||||
const initChart = () => {
|
||||
const option = {
|
||||
grid: {
|
||||
left: '10%', // 增加左边距
|
||||
right: '5%',
|
||||
top: '10%', // 增加顶部边距
|
||||
bottom: '10%', // 增加底部边距
|
||||
containLabel: true
|
||||
},
|
||||
legend: {
|
||||
data: ['a通道', 'b通道'],
|
||||
right: 10,
|
||||
top: 0
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
min: 0,
|
||||
max: 90,
|
||||
interval: 15,
|
||||
axisLine: {
|
||||
show: true
|
||||
},
|
||||
splitLine: { // 添加网格线
|
||||
show: true,
|
||||
lineStyle: {
|
||||
type: 'dashed'
|
||||
}
|
||||
},
|
||||
axisLabel: { // 调整标签样式
|
||||
margin: 12, // 增加标签与轴线的距离
|
||||
formatter: '{value}'
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
min: 0,
|
||||
max: 9000,
|
||||
interval: 1500,
|
||||
axisLine: {
|
||||
show: true
|
||||
},
|
||||
splitLine: { // 添加网格线
|
||||
show: true,
|
||||
lineStyle: {
|
||||
type: 'dashed'
|
||||
}
|
||||
},
|
||||
axisLabel: { // 调整标签样式
|
||||
margin: 16, // 增加标签与轴线的距离
|
||||
formatter: '{value}'
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'a通道',
|
||||
type: 'line',
|
||||
data: generateData('a'),
|
||||
symbol: 'none',
|
||||
lineStyle: {
|
||||
color: '#ff4444',
|
||||
width: 2
|
||||
},
|
||||
smooth: true
|
||||
},
|
||||
{
|
||||
name: 'b通道',
|
||||
type: 'line',
|
||||
data: generateData('b'),
|
||||
symbol: 'none',
|
||||
lineStyle: {
|
||||
color: '#67c23a',
|
||||
width: 2
|
||||
},
|
||||
smooth: true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
chart = echarts.init(chartRef.value)
|
||||
chart.setOption(option)
|
||||
}
|
||||
const generateData = (type) => {
|
||||
const data = []
|
||||
for (let i = 0; i <= 90; i++) {
|
||||
let value
|
||||
if (i < 45) {
|
||||
value = type === 'a' ? 8500 : 7500
|
||||
} else if (i >= 45 && i < 60) {
|
||||
// 下降段
|
||||
const progress = (i - 45) / 15
|
||||
value = type === 'a' ?
|
||||
8500 - (8000 * progress) :
|
||||
7500 - (7000 * progress)
|
||||
} else if (i >= 60 && i < 75) {
|
||||
// 上升段
|
||||
const progress = (i - 60) / 15
|
||||
value = type === 'a' ?
|
||||
500 + (8000 * progress) :
|
||||
500 + (7000 * progress)
|
||||
} else {
|
||||
value = type === 'a' ? 8500 : 7500
|
||||
}
|
||||
data.push([i, value])
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initChart()
|
||||
window.addEventListener('resize', () => chart?.resize())
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', () => chart?.resize())
|
||||
chart?.dispose()
|
||||
})
|
||||
const resize = () => {
|
||||
nextTick(() => {
|
||||
chart?.resize()
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
resize
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.dual-channel-chart {
|
||||
width: 100%;
|
||||
//height: 100%;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
padding: 15px;
|
||||
|
||||
.chart-title {
|
||||
margin-bottom: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
</style>
|
60
src/views/equipment/common/EmqDialog.vue
Normal file
60
src/views/equipment/common/EmqDialog.vue
Normal file
@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:title="proxy.$t('equipment.EMQConnect')"
|
||||
v-model="visible"
|
||||
width="600px"
|
||||
append-to-body
|
||||
:before-close="handleCancel"
|
||||
>
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item :label="proxy.$t('equipment.deviceName')">{{ formData.deviceName }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="proxy.$t('equipment.deviceCode')">{{ formData.deviceCode }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="proxy.$t('equipment.localEMQ')">{{ formData.localUserName }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="proxy.$t('equipment.localPassword')">{{ formData.localPass }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="proxy.$t('equipment.localEMQPassword')">{{ formData.cloudUserName }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="proxy.$t('equipment.localEMQUser')">{{ formData.cloudPass }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<!-- <el-button type="primary" @click="handleConfirm">确定</el-button>-->
|
||||
<el-button @click="handleCancel">{{proxy.$t('button.cancel')}}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const props = defineProps({
|
||||
formData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
|
||||
const visible = ref(false)
|
||||
|
||||
const handleCancel = () => {
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
|
||||
defineExpose({
|
||||
visible
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-footer {
|
||||
text-align: center;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
:deep(.el-descriptions__label) {
|
||||
width: 120px;
|
||||
justify-content: flex-end;
|
||||
color: var(--el-text-color-regular);
|
||||
}
|
||||
</style>
|
108
src/views/equipment/common/HistoryDataDialog.vue
Normal file
108
src/views/equipment/common/HistoryDataDialog.vue
Normal file
@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
title="选择历史数据"
|
||||
width="600px"
|
||||
:before-close="handleClose"
|
||||
>
|
||||
<div class="history-data">
|
||||
<el-form :model="form" label-width="80px">
|
||||
<el-form-item label="选择日期" required>
|
||||
<el-date-picker
|
||||
v-model="form.date"
|
||||
type="datetime"
|
||||
placeholder="选择日期+小时"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table :data="tableData" style="width: 100%">
|
||||
<el-table-column prop="index" label="序号" width="80" />
|
||||
<el-table-column prop="name" label="字典名称" />
|
||||
</el-table>
|
||||
|
||||
<div class="pagination">
|
||||
<el-pagination
|
||||
v-model:current-page="currentPage"
|
||||
v-model:page-size="pageSize"
|
||||
:page-sizes="[20, 50, 100]"
|
||||
layout="sizes, prev, pager, next, jumper, total"
|
||||
:total="total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleClose">取消</el-button>
|
||||
<el-button type="primary" @click="handleConfirm">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const visible = ref(false)
|
||||
const form = ref({
|
||||
date: ''
|
||||
})
|
||||
const tableData = ref([])
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(20)
|
||||
const total = ref(101)
|
||||
|
||||
const emit = defineEmits(['confirm', 'cancel'])
|
||||
|
||||
const show = () => {
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
visible.value = false
|
||||
emit('cancel')
|
||||
}
|
||||
|
||||
const handleConfirm = () => {
|
||||
visible.value = false
|
||||
emit('confirm', form.value)
|
||||
}
|
||||
|
||||
const handleSizeChange = (val) => {
|
||||
pageSize.value = val
|
||||
}
|
||||
|
||||
const handleCurrentChange = (val) => {
|
||||
currentPage.value = val
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
show
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.history-data {
|
||||
.pagination {
|
||||
margin-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
:deep(.el-pagination) {
|
||||
justify-content: flex-end;
|
||||
flex-wrap: wrap;
|
||||
row-gap: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-dialog__body) {
|
||||
padding-top: 16px;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
</style>
|
217
src/views/equipment/common/RealtimeChart.vue
Normal file
217
src/views/equipment/common/RealtimeChart.vue
Normal file
@ -0,0 +1,217 @@
|
||||
<template>
|
||||
<div class="realtime-chart">
|
||||
<div class="chart-header">
|
||||
<div class="title">趋势图</div>
|
||||
<div class="subtitle">近 30min 数据</div>
|
||||
<div class="actions">
|
||||
<el-dropdown>
|
||||
<el-button type="primary">
|
||||
冷直径
|
||||
<el-icon class="el-icon--right"><CaretBottom /></el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item v-for="(item,index) in options" :key="index">{{item.label}}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<el-button @click="refreshData">
|
||||
<el-icon><Refresh /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div ref="chartRef" style="width: 100%; height: 300px"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { CaretBottom, Refresh } from '@element-plus/icons-vue'
|
||||
import * as echarts from 'echarts'
|
||||
// import { getRealtimeData } from '@/api/scanner'
|
||||
|
||||
const chartRef = ref(null)
|
||||
let myChart = null
|
||||
let timer = null
|
||||
|
||||
const selectedType = ref('coldDiameter')
|
||||
const options = [
|
||||
{ label: '冷直径', value: 'coldDiameter' },
|
||||
{ label: '热外径', value: 'hotDiameter' },
|
||||
{ label: '内屏蔽层偏心度', value: 'eccentricity' },
|
||||
{ label: '内屏蔽厚度', value: 'ovality' },
|
||||
{ label: '外屏蔽层偏心度', value: 'thickness' },
|
||||
{ label: '外屏蔽层厚度', value: 'innerDiameter' },
|
||||
{ label: '绝缘层偏心度', value: 'outerDiameter' },
|
||||
{ label: '绝缘层厚度', value: 'surfaceQuality' }
|
||||
]
|
||||
const mockData = {
|
||||
times: ['13:21', '13:22', '13:23', '13:24', '13:25', '13:26', '13:27', '13:28', '13:29', '13:30',
|
||||
'13:31', '13:32', '13:33', '13:34', '13:35', '13:36', '13:37', '13:38', '13:39', '13:40', '13:41'],
|
||||
values: [3.2, 3.1, 3.3, 3.2, 3.4, 3.3, 3.5, 3.6, 3.4, 3.3, 3.2, 3.4, 3.3, 3.2, 3.1, 3.0, 3.2, 2.8, 3.5, 3.6, 3.4]
|
||||
}
|
||||
const initChart = () => {
|
||||
console.log(myChart,'myChart')
|
||||
if (myChart) {
|
||||
myChart.dispose()
|
||||
}
|
||||
// 确保DOM已经渲染完成
|
||||
nextTick(() => {
|
||||
myChart = echarts.init(chartRef.value)
|
||||
const option = {
|
||||
grid: {
|
||||
left: '0%',
|
||||
right: '0%',
|
||||
bottom: '15%',
|
||||
top: '10%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: mockData.times,
|
||||
axisLabel: {
|
||||
interval: 2,
|
||||
margin: 12 // 增加标签与轴线的距离
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
min: 0,
|
||||
max: 5,
|
||||
interval: 1,
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: 'dashed'
|
||||
}
|
||||
},
|
||||
xisLabel: {
|
||||
margin: 16, // 增加标签与轴线的距离
|
||||
formatter: '{value}'
|
||||
},
|
||||
nameTextStyle: {
|
||||
padding: [0, 0, 0, 40] // 调整单位文字位置
|
||||
}
|
||||
},
|
||||
series: [{
|
||||
data: mockData.values,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
areaStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [{
|
||||
offset: 0,
|
||||
color: 'rgba(64, 158, 255, 0.3)'
|
||||
}, {
|
||||
offset: 1,
|
||||
color: 'rgba(64, 158, 255, 0.1)'
|
||||
}]
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
color: '#409EFF'
|
||||
},
|
||||
lineStyle: {
|
||||
width: 1,
|
||||
color: '#409EFF'
|
||||
},
|
||||
symbol: 'none'
|
||||
}]
|
||||
}
|
||||
|
||||
myChart.setOption(option)
|
||||
})
|
||||
}
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const res = await getRealtimeData({
|
||||
type: selectedType.value,
|
||||
minutes: 30
|
||||
})
|
||||
if (res.code === 200) {
|
||||
myChart.setOption({
|
||||
xAxis: {
|
||||
data: res.data.times
|
||||
},
|
||||
series: [{
|
||||
data: res.data.values
|
||||
}]
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取数据失败', error)
|
||||
}
|
||||
}
|
||||
|
||||
// const refreshData = () => {
|
||||
// // fetchData()
|
||||
// }
|
||||
|
||||
// watch(() => selectedType.value, () => {
|
||||
// // fetchData()
|
||||
// })
|
||||
const refreshData = () => {
|
||||
initChart()
|
||||
}
|
||||
onMounted(() => {
|
||||
initChart()
|
||||
// fetchData()
|
||||
// 每分钟刷新一次
|
||||
// timer = setInterval(fetchData, 60000)
|
||||
window.addEventListener('resize', () => myChart?.resize())
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
// clearInterval(timer)
|
||||
window.removeEventListener('resize', () => myChart?.resize())
|
||||
myChart?.dispose()
|
||||
})
|
||||
|
||||
// 添加重新渲染方法
|
||||
const resize = () => {
|
||||
nextTick(() => {
|
||||
myChart?.resize()
|
||||
})
|
||||
}
|
||||
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
resize
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.realtime-chart {
|
||||
padding-top: 14px;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
|
||||
.chart-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: #909399;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
129
src/views/equipment/common/TemperatureChart.vue
Normal file
129
src/views/equipment/common/TemperatureChart.vue
Normal file
@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<div class="temperature-container">
|
||||
<div ref="chartRef" style="width: 100%; height: 400px;"></div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
|
||||
const props = defineProps({
|
||||
chartData: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
arm1: [],
|
||||
arm2: [],
|
||||
times: []
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const chartRef = ref(null)
|
||||
let myChart = null
|
||||
|
||||
const initChart = () => {
|
||||
const option = {
|
||||
title: {
|
||||
left: 0,
|
||||
top: 0
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter: (params) => {
|
||||
const time = params[0].axisValue
|
||||
return `${time}<br/>
|
||||
扫描臂1: ${params[0].data}<br/>
|
||||
扫描臂2: ${params[1].data}`
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: ['扫描臂1', '扫描臂2'],
|
||||
right: 0,
|
||||
top: 0
|
||||
},
|
||||
grid: {
|
||||
left: 60, // 增加左边距
|
||||
right: 30, // 增加右边距
|
||||
top: 40,
|
||||
bottom: 40,
|
||||
containLabel: true // 确保刻度标签显示完整
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: props.chartData.times,
|
||||
axisLabel: {
|
||||
formatter: (value) => {
|
||||
return value.substring(11, 19)
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '设备温度(℃)',
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: 'dashed'
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
margin: 16, // 增加标签与轴线的距离
|
||||
formatter: '{value}'
|
||||
},
|
||||
min: 0, // 设置最小值
|
||||
max: 100, // 设置最大值
|
||||
interval: 20
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '扫描臂1',
|
||||
type: 'line',
|
||||
data: props.chartData.arm1,
|
||||
itemStyle: {
|
||||
color: '#f56c6c'
|
||||
},
|
||||
lineStyle: {
|
||||
width: 2
|
||||
},
|
||||
smooth: true
|
||||
},
|
||||
{
|
||||
name: '扫描臂2',
|
||||
type: 'line',
|
||||
data: props.chartData.arm2,
|
||||
itemStyle: {
|
||||
color: '#67c23a'
|
||||
},
|
||||
lineStyle: {
|
||||
width: 2
|
||||
},
|
||||
smooth: true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
myChart = echarts.init(chartRef.value)
|
||||
myChart.setOption(option)
|
||||
}
|
||||
|
||||
watch(() => props.chartData, () => {
|
||||
initChart()
|
||||
}, { deep: true })
|
||||
|
||||
onMounted(() => {
|
||||
initChart()
|
||||
window.addEventListener('resize', () => myChart?.resize())
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', () => myChart?.resize())
|
||||
myChart?.dispose()
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.temperature-container {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
82
src/views/equipment/common/shareDialog.vue
Normal file
82
src/views/equipment/common/shareDialog.vue
Normal file
@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
title="分享链接"
|
||||
v-model="visible"
|
||||
width="500px"
|
||||
append-to-body
|
||||
>
|
||||
<div class="share-content">
|
||||
<el-input
|
||||
v-model="shareUrl"
|
||||
readonly
|
||||
:autosize="{ minRows: 2, maxRows: 4 }"
|
||||
type="textarea"
|
||||
>
|
||||
<template #append>
|
||||
<el-button type="primary" @click="copyUrl">复制链接</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
<div class="tip-text">点击分享链接,生成一个web超链接设备详情页,弹出提示条"分享链接已复制!"</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="handleClose">关闭</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import useClipboard from 'vue-clipboard3'
|
||||
|
||||
const { toClipboard } = useClipboard()
|
||||
const visible = ref(false)
|
||||
const shareUrl = ref('')
|
||||
|
||||
const props = defineProps({
|
||||
deviceId: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => props.deviceId, (newVal) => {
|
||||
if (newVal) {
|
||||
// 根据实际情况生成分享链接
|
||||
shareUrl.value = `${window.location.origin}/device/detail/${newVal}`
|
||||
}
|
||||
})
|
||||
|
||||
const copyUrl = async () => {
|
||||
try {
|
||||
await toClipboard(shareUrl.value)
|
||||
ElMessage.success('分享链接已复制!')
|
||||
} catch (e) {
|
||||
ElMessage.error('复制失败,请手动复制')
|
||||
}
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
visible
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.share-content {
|
||||
padding: 20px 0;
|
||||
}
|
||||
.tip-text {
|
||||
margin-top: 10px;
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
}
|
||||
.dialog-footer {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
858
src/views/equipment/equipmentDetails.vue
Normal file
858
src/views/equipment/equipmentDetails.vue
Normal file
@ -0,0 +1,858 @@
|
||||
<template>
|
||||
<div class="equipment-container">
|
||||
<div class="data-overview">
|
||||
<div class="data-left">
|
||||
<div class="equipment-name">设备名称(设备编码)</div>
|
||||
<el-button type="info" size="small">离线</el-button>
|
||||
<div style="margin-left:16px;color:#606266">时间显示</div>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<div class="button-group">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click="handleShowImage()"
|
||||
>
|
||||
{{proxy.$t('equipment.imagePage')}}
|
||||
</el-button>
|
||||
<el-button
|
||||
:type="mode === 'fast' ? 'primary' : ''"
|
||||
@click="handleModeChange('fast')"
|
||||
>
|
||||
<el-icon><Timer /></el-icon>
|
||||
快速模式
|
||||
</el-button>
|
||||
|
||||
<el-button
|
||||
:type="xRayStatus ? 'danger' : 'success'"
|
||||
@click="handleXRayToggle"
|
||||
>
|
||||
X{{proxy.$t('equipment.Ray')}}:{{ xRayStatus ? proxy.$t('common.open') : proxy.$t('common.close') }}
|
||||
</el-button>
|
||||
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="handleGenerateReport"
|
||||
>
|
||||
{{proxy.$t('equipment.reporting')}}
|
||||
</el-button>
|
||||
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
@click="handleDeviceShare"
|
||||
>
|
||||
{{proxy.$t('equipment.deviceShare')}}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart-container">
|
||||
<div class="chart-box chart-box-left">
|
||||
<el-tabs
|
||||
v-model="activeName"
|
||||
type="card"
|
||||
class="demo-tabs"
|
||||
@tab-click="handleClick"
|
||||
>
|
||||
<el-tab-pane label="数据组1" name="first">
|
||||
<div class="card-container">
|
||||
<div class="card-list">
|
||||
<div class="eccentricity-card" v-for="(item, index) in cardList" :key="index">
|
||||
<div class="card-header">
|
||||
<span class="title">{{item.title}}</span>
|
||||
<el-icon class="close-icon"><Close /></el-icon>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<div class="card-text">
|
||||
<div>
|
||||
<div class="difference">
|
||||
<span class="plus">收缩率</span>
|
||||
<span class="label">9%</span>
|
||||
</div>
|
||||
<div class="difference">
|
||||
<span class="plus">+</span>
|
||||
<span class="label">公差</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="value">
|
||||
21.15<span class="unit">%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tolerance-section">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
class="switch-btn left"
|
||||
@click="handlePrevious"
|
||||
>
|
||||
<el-icon><ArrowLeft /></el-icon>
|
||||
</el-button>
|
||||
|
||||
<div class="tolerance-text">
|
||||
偏心度公差 {{ toleranceList[currentIndex] }}
|
||||
</div>
|
||||
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
class="switch-btn right"
|
||||
@click="handleNext"
|
||||
>
|
||||
<el-icon><ArrowRight /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="数据组2" name="second">数据组2</el-tab-pane>
|
||||
<el-tab-pane label="数据组3" name="third">数据组3</el-tab-pane>
|
||||
</el-tabs>
|
||||
<div class="btn">
|
||||
<el-button type="primary">{{proxy.$t('button.add')}}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart-box chart-box-right">
|
||||
<el-tabs
|
||||
v-model="activeTwoName"
|
||||
type="card"
|
||||
class="demo-tabs"
|
||||
@tab-click="handleClick"
|
||||
>
|
||||
<el-tab-pane :label="proxy.$t('equipment.sketch')" name="first">
|
||||
<div class="detection-section">
|
||||
<CircleDetectionChart
|
||||
:inner-data="innerLayerData"
|
||||
:middle-data="middleLayerData"
|
||||
:outer-data="outerLayerData"
|
||||
/>
|
||||
<div class="history-content">
|
||||
<div>左边指南针</div>
|
||||
<div class="history-right">
|
||||
<div>
|
||||
<el-button v-if="showHistory" type="primary" @click="showHistoryDialog">选择历史数据</el-button>
|
||||
<el-switch
|
||||
v-model="showHistory"
|
||||
size="large"
|
||||
style="margin-left: 6px"
|
||||
:inactive-text="proxy.$t('equipment.historyData')"
|
||||
/>
|
||||
</div>
|
||||
<div class="scanItem">扫描时间:2025/01/07 04:34:8</div>
|
||||
<div class="scanItem">2025-01-07_04_30_34_866_direction_1_device2.json</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="proxy.$t('equipment.deviceInfo')" name="second">
|
||||
<div class="device-status">
|
||||
<DeviceStatus v-if="!showBtn" :status-data="deviceStatus" />
|
||||
<DeviceSetting v-if="showBtn" @update="handleSettingUpdate" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<div class="btn" v-if="activeTwoName === 'second'">
|
||||
<el-button v-if="!showBtn" type="primary" @click="paramsHandle">{{proxy.$t('equipment.paramsSetting')}}</el-button>
|
||||
<el-button v-if="showBtn" type="primary" @click="paramsHandle">{{proxy.$t('button.return')}}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart-container" style="margin-top: 14px">
|
||||
<div class="chart-box chart-bottom-left">
|
||||
<el-tabs
|
||||
v-model="activeThreeName"
|
||||
type="card"
|
||||
class="demo-tabs"
|
||||
@tab-click="handleClick"
|
||||
>
|
||||
<el-tab-pane :label="proxy.$t('equipment.deviceTemperature')" name="first">
|
||||
<TemperatureChart :chart-data="temperatureData"></TemperatureChart>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="proxy.$t('equipment.warningInfo')" name="second">
|
||||
<div class="warning-container">
|
||||
<div class="warning-item" v-for="(item, index) in warningList" :key="index">
|
||||
{{item.title}}
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="proxy.$t('equipment.imageView')" name="third">
|
||||
<div class="controls">
|
||||
<el-button-group>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
class="switch-btn right"
|
||||
@click="handleImagePrev"
|
||||
>
|
||||
<el-icon><ArrowLeft /></el-icon>
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
class="switch-btn right switch"
|
||||
@click="handleImageNext"
|
||||
>
|
||||
<el-icon><ArrowRight /></el-icon>
|
||||
</el-button>
|
||||
</el-button-group>
|
||||
</div>
|
||||
<div style="display: flex">
|
||||
<div v-if="showRed">
|
||||
<CurveChart ref="bowlCurveRef" :title="chartTitle.left" :color="colorRed"/>
|
||||
<CurveChart ref="bowlCurveRef" :title="chartTitle.right" :color="colorRed"/>
|
||||
</div>
|
||||
<div v-if="showGreen">
|
||||
<CurveChart ref="bowlCurveRef" :title="chartTitle.left" :color="colorGreen"/>
|
||||
<CurveChart ref="bowlCurveRef" :title="chartTitle.right" :color="colorGreen"/>
|
||||
</div>
|
||||
<div v-if="showRedGreen">
|
||||
<DualChannelChart
|
||||
title="扫描臂1"
|
||||
ref="dualChart1Ref"
|
||||
:chart-data="arm1ChartData"
|
||||
/>
|
||||
<DualChannelChart
|
||||
title="扫描臂2"
|
||||
ref="dualChart2Ref"
|
||||
:chart-data="arm2ChartData"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="proxy.$t('equipment.trendChart')" name="four">
|
||||
<RealtimeChart ref="realtimeChartRef" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
<div class="chart-box chart-bottom-right">
|
||||
<div class="header">
|
||||
<div>
|
||||
<span>{{proxy.$t('equipment.imagePage')}}</span>
|
||||
<span class="time">{{ currentTime }}</span>
|
||||
</div>
|
||||
<el-divider/>
|
||||
</div>
|
||||
<div class="imagePage-container">
|
||||
<div class="imagePage-left">
|
||||
<DataTable :table-data="tableData" />
|
||||
</div>
|
||||
<div class="charts">
|
||||
<div class="controls">
|
||||
<el-button-group>
|
||||
<el-button type="primary" plain class="switch-btn" @click="handleImagePagePrev"><el-icon><ArrowLeft /></el-icon></el-button>
|
||||
<el-button type="primary" plain class="switch-btn switch" @click="handleImagePageNext"><el-icon><ArrowRight /></el-icon></el-button>
|
||||
<el-button type="primary" plain class="switch-btn switch" @click="handleRefresh"><el-icon><Refresh /></el-icon></el-button>
|
||||
</el-button-group>
|
||||
</div>
|
||||
<div v-if="showImagePageRed">
|
||||
<CurveChart ref="bowlCurvePageRef" :title="chartTitle.left" :color="colorRed"/>
|
||||
<CurveChart ref="bowlCurvePageRef" :title="chartTitle.right" :color="colorRed"/>
|
||||
</div>
|
||||
<div v-if="showImagePageGreen">
|
||||
<CurveChart ref="bowlCurvePageRef" :title="chartTitle.left" :color="colorGreen"/>
|
||||
<CurveChart ref="bowlCurvePageRef" :title="chartTitle.right" :color="colorGreen"/>
|
||||
</div>
|
||||
<div v-if="showImagePageRedGreen">
|
||||
<DualChannelChart
|
||||
title="扫描臂1"
|
||||
ref="dualChart1PageRef"
|
||||
:chart-data="arm1ChartData"
|
||||
/>
|
||||
<DualChannelChart
|
||||
title="扫描臂2"
|
||||
ref="dualChart2PageRef"
|
||||
:chart-data="arm2ChartData"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 历史数据弹框-->
|
||||
<HistoryDataDialog
|
||||
ref="historyDialogRef"
|
||||
@confirm="handleHistoryConfirm"
|
||||
@cancel="handleHistoryCancel"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { Close, ArrowLeft, ArrowRight, Timer, Refresh } from '@element-plus/icons-vue'
|
||||
import TemperatureChart from './common/TemperatureChart.vue'
|
||||
import RealtimeChart from './common/RealtimeChart.vue';
|
||||
import DeviceStatus from './common/DeviceStatus.vue';
|
||||
import DeviceSetting from './common/DeviceSetting.vue';
|
||||
import DataTable from './common/DataTable.vue';
|
||||
import DualChannelChart from './common/DualChannelChart.vue';
|
||||
import CurveChart from './common/CurveChart.vue';
|
||||
import CircleDetectionChart from "./common/CircleDetectionChart.vue";
|
||||
import HistoryDataDialog from './common/HistoryDataDialog.vue';
|
||||
import {ElMessageBox} from "element-plus";
|
||||
const { proxy } = getCurrentInstance();
|
||||
const toleranceList = ['15%', '20%', '25%']
|
||||
const showRed = ref(true)
|
||||
const showGreen = ref(false)
|
||||
const showRedGreen = ref(false)
|
||||
const showImagePageRed = ref(true)
|
||||
const showImagePageGreen = ref(false)
|
||||
const showImagePageRedGreen = ref(false)
|
||||
const colorRed = ref('#ff4444')
|
||||
const colorGreen = ref('#67c23a')
|
||||
const currentIndex = ref(0)
|
||||
const showHistory = ref(false)
|
||||
const mode = ref('normal')
|
||||
const realtimeChartRef = ref(null)
|
||||
const dualChart1Ref = ref(null)
|
||||
const dualChart2Ref = ref(null)
|
||||
const dualChart1PageRef = ref(null)
|
||||
const dualChart2PageRef = ref(null)
|
||||
const bowlCurvePageRef = ref(null)
|
||||
const xRayStatus = ref(false)
|
||||
const activeName = ref('first')
|
||||
const activeTwoName = ref('first')
|
||||
const activeThreeName = ref('first')
|
||||
const bowlCurveRef = ref(null)
|
||||
const showBtn = ref(false)
|
||||
const historyDialogRef = ref(null)
|
||||
const currentTime = ref('2021-02-11 13:23:39')
|
||||
const chartTitle = ref({
|
||||
left: '扫描臂1 曲线',
|
||||
right: '扫描臂2 曲线'
|
||||
})
|
||||
const cardList = [
|
||||
{ title: '外屏层偏心度' },
|
||||
{ title: '内屏层偏心度' },
|
||||
{ title: '绝缘层偏心度' },
|
||||
{ title: '外屏层厚度' },
|
||||
{ title: '内屏层厚度' },
|
||||
{ title: '绝缘层厚度' }
|
||||
]
|
||||
const warningList = ref([
|
||||
{ title: '设备1离线', status: 'offline' },
|
||||
{ title: '设备2离线', status: 'offline' },
|
||||
{ title: '设备3离线', status: 'offline' }
|
||||
])
|
||||
// 示意图数据
|
||||
const innerLayerData = ref([0.68, 0.85, 1.07, 1.18, 1.13, 0.97, 0.67, 0.98])
|
||||
const middleLayerData = ref([9.52, 9.95, 10.53, 11.04, 11.34, 11.15, 9.66, 10.40])
|
||||
const outerLayerData = ref([1.15, 1.19, 1.21, 1.17, 1.05, 0.96, 1.06, 0.79])
|
||||
// 温度数据
|
||||
const temperatureData = ref({
|
||||
arm1: [33, 46, 57, 24, 48, 51, 33],
|
||||
arm2: [11, 24, 39, 13, 48, 41, 33],
|
||||
times: [
|
||||
'2023-01-01 09:00:00',
|
||||
'2023-01-01 10:00:00',
|
||||
'2023-01-01 11:00:00',
|
||||
'2023-01-01 12:00:00',
|
||||
'2023-01-01 13:00:00',
|
||||
'2023-01-01 14:00:00'
|
||||
]
|
||||
})
|
||||
const deviceStatus = ref({
|
||||
arm1: {
|
||||
communication: '正常',
|
||||
fault: '正常',
|
||||
enable: '未使用',
|
||||
position: '下过渡段',
|
||||
speed: '0',
|
||||
powerSwitch: { value: '关闭', type: 'danger' },
|
||||
safetyLock: '打开',
|
||||
voltageSet: '55000',
|
||||
voltageFeedback: '15',
|
||||
currentFeedback: '2',
|
||||
currentSet: '1800'
|
||||
},
|
||||
arm2: {
|
||||
communication: '正常',
|
||||
fault: '正常',
|
||||
enable: '未使用',
|
||||
position: '下过渡段',
|
||||
speed: '0',
|
||||
powerSwitch: { value: '关闭', type: 'danger' },
|
||||
safetyLock: '打开',
|
||||
voltageSet: '55000',
|
||||
voltageFeedback: '0',
|
||||
currentFeedback: '2',
|
||||
currentSet: '1800'
|
||||
}
|
||||
})
|
||||
// 表格数据
|
||||
const tableData = ref([
|
||||
{ name: '射线电源反馈电压[V]', arm1: '15', arm2: '0' },
|
||||
{ name: '射线电源设定电压[V]', arm1: '55000', arm2: '55000' },
|
||||
// ... 其他数据
|
||||
])
|
||||
|
||||
// 图表数据
|
||||
const arm1ChartData = ref({
|
||||
a: Array.from({ length: 100 }, () => Math.random() * 9000),
|
||||
b: Array.from({ length: 100 }, () => Math.random() * 9000)
|
||||
})
|
||||
|
||||
const arm2ChartData = ref({
|
||||
a: Array.from({ length: 100 }, () => Math.random() * 9000),
|
||||
b: Array.from({ length: 100 }, () => Math.random() * 9000)
|
||||
})
|
||||
// 图像页展示
|
||||
const handleImagePageNext = () => {
|
||||
if (showImagePageRedGreen.value) {
|
||||
showImagePageRedGreen.value = false
|
||||
showImagePageRed.value = true
|
||||
showImagePageGreen.value = false
|
||||
} else if (showImagePageRed.value) {
|
||||
showImagePageRedGreen.value = false
|
||||
showImagePageRed.value = false
|
||||
showImagePageGreen.value = true
|
||||
} else if (showImagePageGreen.value) {
|
||||
showImagePageRedGreen.value = true
|
||||
showImagePageRed.value = false
|
||||
showImagePageGreen.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleImagePagePrev = () => {
|
||||
if (showImagePageRedGreen.value) {
|
||||
showImagePageRedGreen.value = false
|
||||
showImagePageRed.value = false
|
||||
showImagePageGreen.value = true
|
||||
} else if (showImagePageGreen.value) {
|
||||
showImagePageRedGreen.value = false
|
||||
showImagePageRed.value = true
|
||||
showImagePageGreen.value = false
|
||||
} else if (showImagePageRed.value) {
|
||||
showImagePageRedGreen.value = true
|
||||
showImagePageRed.value = false
|
||||
showImagePageGreen.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleRefresh = () => {
|
||||
// 刷新当前显示的图表
|
||||
if (showImagePageRedGreen.value) {
|
||||
dualChart1PageRef.value?.refresh()
|
||||
dualChart2PageRef.value?.refresh()
|
||||
} else {
|
||||
bowlCurvePageRef.value?.refresh()
|
||||
}
|
||||
}
|
||||
// 打开历史数据弹窗
|
||||
const showHistoryDialog = () => {
|
||||
historyDialogRef.value.show()
|
||||
}
|
||||
|
||||
const handleHistoryConfirm = (data) => {
|
||||
console.log('选择的历史数据:', data)
|
||||
}
|
||||
|
||||
const handleHistoryCancel = () => {
|
||||
console.log('取消选择')
|
||||
}
|
||||
// 图像预览
|
||||
const handleImageNext = () => {
|
||||
if (showRedGreen.value) {
|
||||
showRedGreen.value = false
|
||||
showRed.value = true
|
||||
showGreen.value = false
|
||||
} else if (showRed.value) {
|
||||
showRedGreen.value = false
|
||||
showRed.value = false
|
||||
showGreen.value = true
|
||||
} else if (showGreen.value) {
|
||||
showRedGreen.value = true
|
||||
showRed.value = false
|
||||
showGreen.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleImagePrev = () => {
|
||||
if (showRedGreen.value) {
|
||||
showRedGreen.value = false
|
||||
showRed.value = false
|
||||
showGreen.value = true
|
||||
} else if (showGreen.value) {
|
||||
showRedGreen.value = false
|
||||
showRed.value = true
|
||||
showGreen.value = false
|
||||
} else if (showRed.value) {
|
||||
showRedGreen.value = true
|
||||
showRed.value = false
|
||||
showGreen.value = false
|
||||
}
|
||||
}
|
||||
const generateData = (type) => {
|
||||
const data = []
|
||||
for (let i = 0; i <= 90; i++) {
|
||||
let value
|
||||
if (i < 45) {
|
||||
value = type === 'a' ? 8500 : 7500
|
||||
} else if (i >= 45 && i < 60) {
|
||||
value = 8500 - ((i - 45) * 500)
|
||||
} else if (i >= 60 && i < 75) {
|
||||
value = 1000 + ((i - 60) * 500)
|
||||
} else {
|
||||
value = type === 'a' ? 8500 : 7500
|
||||
}
|
||||
data.push([i, value])
|
||||
}
|
||||
arm1ChartData.value = data
|
||||
}
|
||||
// 标签页切换
|
||||
const handleClick = (tab, event) => {
|
||||
console.log(tab.props.label,'----')
|
||||
nextTick(() => {
|
||||
if(tab.props.label === '趋势图') {
|
||||
realtimeChartRef.value?.resize()
|
||||
}
|
||||
if(tab.props.label === '图像预览') {
|
||||
bowlCurveRef.value?.resize()
|
||||
}
|
||||
})
|
||||
}
|
||||
const handlePrevious = () => {
|
||||
if (currentIndex.value > 0) {
|
||||
currentIndex.value--
|
||||
}
|
||||
}
|
||||
|
||||
const handleNext = () => {
|
||||
if (currentIndex.value < toleranceList.length - 1) {
|
||||
currentIndex.value++
|
||||
}
|
||||
}
|
||||
// 设备信息
|
||||
const paramsHandle = () => {
|
||||
showBtn.value = !showBtn.value
|
||||
}
|
||||
// 参数设定按钮
|
||||
const handleSettingUpdate = ({ key, value }) => {
|
||||
console.log('更新设置:', key, value)
|
||||
// 调用接口更新设置
|
||||
}
|
||||
// 图像预览
|
||||
const handleDataUpdate = (data) => {
|
||||
console.log('数据更新:', data)
|
||||
}
|
||||
// 展示图像页
|
||||
const handleShowImage = () => {
|
||||
// router.push({ path: '/image-page' })
|
||||
// ElMessage.success('图像页已打开')
|
||||
}
|
||||
// 快速模式
|
||||
const handleModeChange = (newMode) => {
|
||||
mode.value = newMode
|
||||
}
|
||||
//x射线
|
||||
const handleXRayToggle = () => {
|
||||
ElMessageBox.confirm(
|
||||
'是否确认切换X射线状态?',
|
||||
'提示',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
).then(() => {
|
||||
// 这里可以调用接口来切换X射线状态
|
||||
xRayStatus.value = !xRayStatus.value
|
||||
}).catch(() => {
|
||||
// 用户取消操作
|
||||
})
|
||||
}
|
||||
// 生成报告
|
||||
const handleGenerateReport = () => {
|
||||
}
|
||||
// 设备分享
|
||||
const handleDeviceShare = () => {
|
||||
}
|
||||
onMounted(() => {
|
||||
generateData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.equipment-container {
|
||||
padding: 20px;
|
||||
|
||||
h3 {
|
||||
margin: 0 0 20px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.data-overview {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
.data-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
.data-card {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
align-items: center;
|
||||
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
|
||||
.img-icon {
|
||||
border:1px dashed #ccc;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.number {
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
color:#333333;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
|
||||
.chart-box {
|
||||
position: relative;
|
||||
padding: 14px;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
|
||||
.btn {
|
||||
position: absolute;
|
||||
top:14px;
|
||||
right: 14px;
|
||||
}
|
||||
}
|
||||
.chart-box-left {
|
||||
width:60%;
|
||||
}
|
||||
.chart-box-right {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.action-buttons {
|
||||
padding: 10px 0;
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
|
||||
.el-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
|
||||
&.el-button--danger {
|
||||
background-color: #f56c6c;
|
||||
border-color: #f56c6c;
|
||||
color: #fff;
|
||||
|
||||
&:hover {
|
||||
background-color: #f78989;
|
||||
border-color: #f78989;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.demo-tabs > .el-tabs__content {
|
||||
padding: 32px;
|
||||
color: #6b778c;
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.eccentricity-card {
|
||||
background-color: #F5F7FA;
|
||||
border-radius: 4px;
|
||||
padding: 14px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-bottom: 10px;
|
||||
border-bottom:1px solid #ccc;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.title {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
cursor: pointer;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.card-content {
|
||||
.card-text {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.difference {
|
||||
margin-bottom: 6px;
|
||||
font-size: 14px;
|
||||
|
||||
.plus {
|
||||
color: #f56c6c;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: #f56c6c;
|
||||
}
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
color: red;
|
||||
margin-bottom: 15px;
|
||||
|
||||
.unit {
|
||||
font-size: 16px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.tolerance-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: #E5ECF6 ;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
|
||||
|
||||
.tolerance-text {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.card-container {
|
||||
|
||||
.card-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 15px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
.chart-bottom-left {
|
||||
width:38%;
|
||||
}
|
||||
.chart-bottom-right {
|
||||
flex:1
|
||||
}
|
||||
.warning-container {
|
||||
padding-left:10px;
|
||||
.warning-item {
|
||||
color:red;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
.device-status {
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
height: 346px;
|
||||
overflow-y: scroll;
|
||||
:deep(.el-table) {
|
||||
.el-tag {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
|
||||
&.el-tag--danger {
|
||||
background-color: #fef0f0;
|
||||
}
|
||||
}
|
||||
|
||||
.cell {
|
||||
padding: 8px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.header {
|
||||
//display: flex;
|
||||
//justify-content: space-between;
|
||||
//align-items: center;
|
||||
//margin-bottom: 14px;
|
||||
.time {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
margin-left: 6px;
|
||||
}
|
||||
}
|
||||
.imagePage-container {
|
||||
display: flex;
|
||||
.imagePage-left {
|
||||
width: 52%;
|
||||
}
|
||||
}
|
||||
.charts {
|
||||
//display: flex;
|
||||
//gap: 20px;
|
||||
padding: 0 6px;
|
||||
flex:1;
|
||||
//
|
||||
//> div {
|
||||
// flex: 1;
|
||||
//}
|
||||
}
|
||||
.controls {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
.switch {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
.switch-btn {
|
||||
padding: 5px;
|
||||
|
||||
//&:hover {
|
||||
// background: #f0f9eb;
|
||||
//}
|
||||
}
|
||||
.detection-section {
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.history-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.history-right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
}
|
||||
.scanItem {
|
||||
color:#666;
|
||||
font-size: 12px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
}
|
||||
</style>
|
535
src/views/equipment/equipmentManage.vue
Normal file
535
src/views/equipment/equipmentManage.vue
Normal file
@ -0,0 +1,535 @@
|
||||
<template>
|
||||
<div class="contentBox">
|
||||
<el-card class="card" shadow="hover">
|
||||
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch">
|
||||
<el-form-item :label="proxy.$t('maintenance.deviceCode')" prop="deviceCode">
|
||||
<el-input v-model="queryParams.deviceCode" :placeholder="proxy.$t('maintenance.deviceCode')" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="proxy.$t('common.customerAffiliation')" prop="customerId" v-if="customerList.length>1">
|
||||
<el-select v-model="queryParams.customerId" :placeholder="proxy.$t('common.select')" clearable style="width:200px" >
|
||||
<el-option
|
||||
v-for="item in customerList"
|
||||
:key="item.deptId"
|
||||
:label="item.deptName"
|
||||
:value="item.deptId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="proxy.$t('common.userAffiliation')" prop="userId" v-if="belongUserList.length>1">
|
||||
<el-select v-model="queryParams.userId" :placeholder="proxy.$t('common.select')" clearable style="width:200px" >
|
||||
<el-option
|
||||
v-for="item in belongUserList"
|
||||
:key="item.userId"
|
||||
:label="item.nickName"
|
||||
:value="item.userId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="proxy.$t('equipment.deviceStatus')" prop="deviceStatus">
|
||||
<el-select v-model="queryParams.deviceStatus" :placeholder="proxy.$t('common.select')" clearable style="width:200px" >
|
||||
<el-option
|
||||
v-for="item in statusList"
|
||||
:key="item.dictValue"
|
||||
:label="item.dictLabel"
|
||||
:value="item.dictValue"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="proxy.$t('equipment.serviceStatus')" prop="serviceStatus">
|
||||
<el-select v-model="queryParams.serviceStatus" :placeholder="proxy.$t('common.select')" clearable style="width:200px" >
|
||||
<el-option
|
||||
v-for="item in statusList"
|
||||
:key="item.dictValue"
|
||||
:label="item.dictLabel"
|
||||
:value="item.dictValue"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">{{proxy.$t('button.search')}}</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">{{proxy.$t('button.reset')}}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Plus"
|
||||
@click="handleAdd"
|
||||
>{{proxy.$t('button.add')}}</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="exportDevice"
|
||||
>{{proxy.$t('button.export')}}</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" style="width: 100%" :data="deviceList" border>
|
||||
<el-table-column fixed :label="proxy.$t('common.sort')" type="index" width="60" align="center" />
|
||||
<el-table-column :label="proxy.$t('equipment.deviceName')" align="center" prop="deviceName" width="120"/>
|
||||
<el-table-column :label="proxy.$t('equipment.deviceModel')" align="center" prop="deviceModel" width="120" />
|
||||
<el-table-column :label="proxy.$t('equipment.deviceCode')" align="center" prop="deviceCode" width="120"/>
|
||||
<el-table-column :label="proxy.$t('common.customerAffiliation')" align="center" prop="customerName" width="140"/>
|
||||
<el-table-column :label="proxy.$t('common.userAffiliation')" align="center" prop="userRelList">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" link @click="viewCustomer(scope.row)">{{proxy.$t('button.check')}}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="proxy.$t('equipment.lifespan')" width="140" align="center" prop="lifespan" />
|
||||
<el-table-column :label="proxy.$t('equipment.usedTime')" width="140" align="center" prop="usedTime" />
|
||||
<el-table-column :label="proxy.$t('equipment.deviceStatus')" align="center" prop="deviceStatus" width="100">
|
||||
<template #default="scope">
|
||||
{{statusFormat(scope.row.deviceStatus)}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="proxy.$t('equipment.serviceStatus')" align="center" prop="serviceStatus" width="100">
|
||||
<template #default="scope">
|
||||
{{statusFormat(scope.row.serviceStatus)}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="proxy.$t('customer.createBy')" align="center" prop="createBy" width="160" />
|
||||
<el-table-column :label="proxy.$t('customer.createTime')" align="center" prop="createTime" width="200" />
|
||||
<el-table-column :label="proxy.$t('common.action')" align="center" width="380" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="handleUpdate(scope.row,proxy.$t('button.edit'))"
|
||||
>{{proxy.$t('button.edit')}}</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="handleDelete(scope.row)"
|
||||
>{{proxy.$t('button.delete')}}</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="handleGo(scope.row)"
|
||||
>{{ proxy.$t('button.viewDetails') }}</el-button>
|
||||
<!-- <el-dropdown @command="handleCommand">-->
|
||||
<!-- <el-button type="primary" link>-->
|
||||
<!-- 更多<el-icon class="el-icon--right"><CaretBottom /></el-icon>-->
|
||||
<!-- </el-button>-->
|
||||
<!-- <template #dropdown>-->
|
||||
<!-- <el-dropdown-menu>-->
|
||||
<!-- <el-dropdown-item :command="{ type: 'stop', row }"-->
|
||||
<!-- :disabled="row.status === 0">停止服务</el-dropdown-item>-->
|
||||
<!-- <el-dropdown-item :command="{ type: 'restart', row }"-->
|
||||
<!-- :disabled="row.status === 0">重启服务</el-dropdown-item>-->
|
||||
<!-- <el-dropdown-item :command="{ type: 'start', row }"-->
|
||||
<!-- :disabled="row.status === 1">启动服务</el-dropdown-item>-->
|
||||
<!-- </el-dropdown-menu>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-dropdown>-->
|
||||
<!-- <el-dropdown placement="bottom">-->
|
||||
<!-- <el-button-->
|
||||
<!-- link-->
|
||||
<!-- type="primary"-->
|
||||
<!-- >更多</el-button>-->
|
||||
<!-- <template #dropdown>-->
|
||||
<!-- <el-dropdown-menu>-->
|
||||
<!-- <el-dropdown-item>停止服务</el-dropdown-item>-->
|
||||
<!-- <el-dropdown-item>重启服务</el-dropdown-item>-->
|
||||
<!-- <el-dropdown-item>启动服务</el-dropdown-item>-->
|
||||
<!-- </el-dropdown-menu>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-dropdown>-->
|
||||
<!-- <el-button-->
|
||||
<!-- link-->
|
||||
<!-- type="primary"-->
|
||||
<!-- @click="handleShare(scope.row)"-->
|
||||
<!-- >分享</el-button>-->
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="handleEmqx(scope.row)"
|
||||
>{{proxy.$t('equipment.viewAuth')}}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination">
|
||||
<el-pagination
|
||||
v-if="total > 0"
|
||||
v-model:current-page="queryParams.pageNum"
|
||||
v-model:page-size="queryParams.pageSize"
|
||||
:total="total"
|
||||
:page-sizes="[10, 20, 30, 50]"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
<DeviceDialog
|
||||
v-model="dialog.visible"
|
||||
:title="dialog.title"
|
||||
:customer-list="customerList"
|
||||
:user-list="belongUserList"
|
||||
:form-data="dialog.form"
|
||||
@success="handleSuccess"
|
||||
/>
|
||||
<!--查看所属用户列表-->
|
||||
<el-dialog
|
||||
:title="proxy.$t('common.userAffiliation')"
|
||||
v-model="visible"
|
||||
width="800px"
|
||||
append-to-body
|
||||
>
|
||||
<el-table :data="userList" border>
|
||||
<el-table-column :label="proxy.$t('common.sort')" type="index" width="80" align="center" />
|
||||
<el-table-column :label="proxy.$t('equipment.nickName')" prop="nickName" align="center" />
|
||||
<el-table-column :label="proxy.$t('equipment.userName')" prop="userName" align="center" />
|
||||
<el-table-column :label="proxy.$t('position.department')" prop="deptName" align="center" />
|
||||
<el-table-column :label="proxy.$t('equipment.userStatus')" align="center" prop="status">
|
||||
<template #default="scope">
|
||||
{{Number(scope.row.status) === 0 ? proxy.$t('common.normal') :proxy.$t('common.deactivate')}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="visible = false">{{ proxy.$t('button.cancel') }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<!--查看鉴权-->
|
||||
<EmqDialog ref="emqDialogRef" :form-data="emqFormData" ></EmqDialog>
|
||||
<!--分享-->
|
||||
<ShareDialog ref="shareDialogRef" :form-data="shareDialogRef"></ShareDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { useRouter,useRoute } from 'vue-router'
|
||||
import {
|
||||
getDeviceListApi,
|
||||
deleteDeviceApi,
|
||||
getDeviceSelectApi,
|
||||
getUserSelectApi,
|
||||
getDeviceDetailApi,
|
||||
getAuthInfoApi
|
||||
} from '@/api/equipment'
|
||||
import DeviceDialog from './common/DeviceDialog.vue'
|
||||
import shareDialog from './common/shareDialog.vue'
|
||||
import { CaretBottom } from '@element-plus/icons-vue'
|
||||
import {ElMessage, ElMessageBox} from "element-plus";
|
||||
import {getDicts} from "@/api/system/dict/data.js";
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import {deleteDepartmentPostApi, getCustomerListApi} from "@/api/customer/index.js";
|
||||
import EmqDialog from './common/EmqDialog.vue'
|
||||
const emqDialogRef = ref()
|
||||
const emqFormData = ref({})
|
||||
const router = useRouter()
|
||||
const { proxy } = getCurrentInstance();
|
||||
const queryFormRef = ref()
|
||||
const loading = ref(false)
|
||||
const showSearch = ref(true)
|
||||
const total = ref(0)
|
||||
const deviceList = ref([])
|
||||
const { t } = useI18n();
|
||||
const visible = ref(false)
|
||||
const belongUserList = ref([])
|
||||
const userList = ref([])
|
||||
const statusList = ref([])
|
||||
const shareDialogRef = ref()
|
||||
const currentDeviceId = ref('')
|
||||
const queryParams = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
deviceCode: '',
|
||||
userId:'',
|
||||
customerId:'',
|
||||
deviceStatus:'',
|
||||
serviceStatus:''
|
||||
})
|
||||
const route = useRoute()
|
||||
// 添加弹框相关的数据
|
||||
const dialog = reactive({
|
||||
title: proxy.$t('button.add'),
|
||||
visible: false,
|
||||
form: {
|
||||
deviceName: '',
|
||||
deviceCode: '',
|
||||
deviceModel: '',
|
||||
customerId: '',
|
||||
userIdList: [],
|
||||
id: '',
|
||||
}
|
||||
})
|
||||
const emqVisible = ref(false)
|
||||
const queryParamsCustomer = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 100000000,
|
||||
deptName: undefined,
|
||||
parentId: 100,
|
||||
})
|
||||
const customerList = ref([])
|
||||
const statusFormat = (status) => {
|
||||
if(status === undefined || status === null) {
|
||||
return ''
|
||||
}
|
||||
return status === 1 ? proxy.$t('common.online') : proxy.$t('common.offline')
|
||||
}
|
||||
// 获取状态
|
||||
const getStatus = async() => {
|
||||
const res = await getDicts('tg_device_status')
|
||||
if(res.code === 200) {
|
||||
statusList.value = res.data || []
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
}
|
||||
// const handleCommand = async ({ type, row }) => {
|
||||
// const actionMap = {
|
||||
// stop: {
|
||||
// title: '停止服务',
|
||||
// // api: stopDeviceApi,
|
||||
// successMsg: '停止成功'
|
||||
// },
|
||||
// restart: {
|
||||
// title: '重启服务',
|
||||
// // api: restartDeviceApi,
|
||||
// successMsg: '重启成功'
|
||||
// },
|
||||
// start: {
|
||||
// title: '启动服务',
|
||||
// // api: startDeviceApi,
|
||||
// successMsg: '启动成功'
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// const action = actionMap[type]
|
||||
//
|
||||
// try {
|
||||
// await ElMessageBox.confirm(
|
||||
// `是否确认${action.title}?`,
|
||||
// '警告',
|
||||
// {
|
||||
// confirmButtonText: '确定',
|
||||
// cancelButtonText: '取消',
|
||||
// type: 'warning'
|
||||
// }
|
||||
// )
|
||||
//
|
||||
// const res = await action.api(row.id)
|
||||
// if (res.code === 200) {
|
||||
// ElMessage.success(action.successMsg)
|
||||
// getList() // 刷新列表
|
||||
// } else {
|
||||
// ElMessage.error(res.msg)
|
||||
// }
|
||||
// } catch {
|
||||
// // 取消操作
|
||||
// }
|
||||
// }
|
||||
// 导出
|
||||
const exportDevice = async() => {
|
||||
proxy?.download(
|
||||
'/device/info/export',
|
||||
{
|
||||
...queryParams
|
||||
},
|
||||
`equipment_${new Date().getTime()}.xlsx`
|
||||
);
|
||||
}
|
||||
// 获取客户列表
|
||||
const getCustomerList = async() => {
|
||||
const res = await getCustomerListApi(queryParamsCustomer);
|
||||
customerList.value = res.rows || []
|
||||
}
|
||||
const getList = async() => {
|
||||
loading.value = true
|
||||
// 这里应该调用后端API获取数据
|
||||
const res = await getDeviceListApi(queryParams);
|
||||
if(res.code === 200) {
|
||||
deviceList.value = res.rows || []
|
||||
total.value = res.total
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNum = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
const resetQuery = () => {
|
||||
queryParams.deviceCode = ''
|
||||
queryParams.customerId = ''
|
||||
queryParams.userId = ''
|
||||
queryParams.deviceStatus = ''
|
||||
queryParams.serviceStatus = ''
|
||||
queryFormRef.value?.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
// 查看所属用户列表
|
||||
const viewCustomer = async(row) => {
|
||||
visible.value = true
|
||||
if(row.userRelList?.length > 0) {
|
||||
userList.value = row.userRelList
|
||||
} else {
|
||||
userList.value = []
|
||||
}
|
||||
}
|
||||
// 打开新增弹框
|
||||
const handleAdd = () => {
|
||||
dialog.title = proxy.$t('button.add')
|
||||
dialog.visible = true
|
||||
}
|
||||
const handleShare = (row) => {
|
||||
currentDeviceId.value = row.id
|
||||
shareDialogRef.value.visible = true
|
||||
}
|
||||
// 删除设备
|
||||
const handleDelete = (row) => {
|
||||
ElMessageBox.confirm(
|
||||
proxy.$t('common.deleteItem'),
|
||||
proxy.$t('common.warning'),
|
||||
{
|
||||
confirmButtonText: proxy.$t('button.save'),
|
||||
cancelButtonText: proxy.$t('button.cancel'),
|
||||
type: "warning"
|
||||
}
|
||||
).then(async () => {
|
||||
// 这里调用删除API
|
||||
const res = await deleteDeviceApi(row.id)
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('删除成功')
|
||||
}
|
||||
getList()
|
||||
}).catch(() => {})
|
||||
}
|
||||
// 添加成功回调方法
|
||||
const handleSuccess = () => {
|
||||
getList()
|
||||
}
|
||||
// 页码改变
|
||||
const handleSizeChange = (val) => {
|
||||
queryParams.pageSize = val
|
||||
getList()
|
||||
}
|
||||
|
||||
const handleCurrentChange = (val) => {
|
||||
queryParams.pageNum = val
|
||||
getList()
|
||||
}
|
||||
// 查看详情
|
||||
const handleGo = (row) => {
|
||||
// 跳转到详情页
|
||||
router.push({ path: '/equipment/equipmentDetails', query: { id: row.id } });
|
||||
}
|
||||
// 查看鉴权
|
||||
const handleEmqx = async (row) => {
|
||||
const res = await getAuthInfoApi(row.deviceCode)
|
||||
emqFormData.value = res.data
|
||||
emqDialogRef.value.visible = true
|
||||
}
|
||||
// 编辑
|
||||
const handleUpdate = async(row) => {
|
||||
const res = await getDeviceDetailApi(row.id)
|
||||
console.log(res.data,'===')
|
||||
if(res.code === 200) {
|
||||
dialog.title = proxy.$t('button.edit')
|
||||
dialog.visible = true
|
||||
dialog.form.deviceName = res.data.deviceName
|
||||
dialog.form.deviceCode = res.data.deviceCode
|
||||
dialog.form.deviceModel = res.data.deviceModel
|
||||
dialog.form.customerId = res.data.customerId
|
||||
dialog.form.userIdList = res.data.userIdList
|
||||
dialog.form.id = res.data.id
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
}
|
||||
// 获取用户下拉列
|
||||
const getUserSelect = async() => {
|
||||
const res = await getUserSelectApi()
|
||||
if(res.code === 200) {
|
||||
belongUserList.value = res.data || []
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
}
|
||||
// 获取设备下拉列表
|
||||
// const getDeviceSelect = async() => {
|
||||
// const res = await getDeviceSelectApi()
|
||||
// if(res.code === 200) {
|
||||
// deviceDropList.value = res.data || []
|
||||
// } else {
|
||||
// ElMessage.error(res.msg)
|
||||
// }
|
||||
// }
|
||||
watch(
|
||||
() => route.query,
|
||||
(newQuery) => {
|
||||
if (newQuery.id) {
|
||||
queryParams.customerId = newQuery.id
|
||||
// 添加客户ID的处理
|
||||
if (newQuery.id) {
|
||||
queryParams.customerId = Number(newQuery.id) // 转换为数字类型
|
||||
}
|
||||
handleQuery()
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
// 如果有路由参数,优先使用路由参数
|
||||
if (route.query.id) {
|
||||
queryParams.customerId = route.query.id
|
||||
}
|
||||
if (route.query.id) {
|
||||
queryParams.customerId = Number(route.query.id)
|
||||
}
|
||||
getList()
|
||||
getCustomerList()
|
||||
getUserSelect()
|
||||
getStatus()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.contentBox {
|
||||
height:calc(100vh - 130px);
|
||||
margin:20px;
|
||||
.card {
|
||||
width:100%;
|
||||
height: 100%;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
}
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.mb8 {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.dialog-footer {
|
||||
text-align: center;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
:deep(.el-tag) {
|
||||
border-radius: 2px;
|
||||
}
|
||||
:deep(.reset-style) {
|
||||
padding: 0;
|
||||
height: auto;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
:deep(.el-dropdown-menu__item) {
|
||||
padding: 5px 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
355
src/views/equipment/equipmentModel.vue
Normal file
355
src/views/equipment/equipmentModel.vue
Normal file
@ -0,0 +1,355 @@
|
||||
<template>
|
||||
<div class="contentBox">
|
||||
<el-card class="card" shadow="hover">
|
||||
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch">
|
||||
<el-form-item :label="proxy.$t('equipment.deviceModel')" prop="deviceModel">
|
||||
<el-input v-model="queryParams.deviceModel" :placeholder="proxy.$t('equipment.inputDeviceModel')" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">{{proxy.$t('button.search')}}</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">{{proxy.$t('button.reset')}}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Plus"
|
||||
@click="handleAdd"
|
||||
>{{proxy.$t('button.add')}}</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="maintenanceList" border>
|
||||
<el-table-column :label="proxy.$t('common.sort')" type="index" width="60" align="center" />
|
||||
<el-table-column :label="proxy.$t('equipment.deviceModel')" align="center" prop="deviceModel" />
|
||||
<el-table-column :label="proxy.$t('equipment.deviceImage')" align="center" prop="devicePic">
|
||||
<template #default="scope">
|
||||
<image-preview :height="50" :src="scope.row.devicePic" :width="50" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="proxy.$t('customer.createBy')" align="center" prop="createBy" />
|
||||
<el-table-column :label="proxy.$t('customer.createTime')" align="center" prop="createTime" width="160" />
|
||||
<el-table-column :label="proxy.$t('common.action')" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
>{{proxy.$t('button.edit')}}</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
>{{proxy.$t('button.delete')}}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination">
|
||||
<el-pagination
|
||||
v-if="total > 0"
|
||||
v-model:current-page="queryParams.pageNum"
|
||||
v-model:page-size="queryParams.pageSize"
|
||||
:total="total"
|
||||
:page-sizes="[10, 20, 30, 50]"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-dialog
|
||||
:title="title"
|
||||
v-model="visible"
|
||||
width="500px"
|
||||
append-to-body
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-width="appStore.language === 'zh_CN' ? '80px' : '120px'"
|
||||
label-position="right"
|
||||
>
|
||||
<el-form-item :label="proxy.$t('equipment.deviceModel')" prop="deviceModel">
|
||||
<el-input v-model="form.deviceModel" :placeholder="proxy.$t('common.select')" style="width: 300px" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="proxy.$t('equipment.deviceImage')" prop="devicePic">
|
||||
<el-upload
|
||||
class="upload-demo"
|
||||
:action="upload.url"
|
||||
:headers="upload.headers"
|
||||
:before-upload="beforeUpload"
|
||||
:on-success="handleUploadSuccess"
|
||||
:on-error="handleUploadError"
|
||||
:limit="1"
|
||||
accept=".jpg,.png"
|
||||
:show-file-list="false"
|
||||
>
|
||||
<template #default>
|
||||
<div v-if="!form.devicePic" class="upload-box">
|
||||
<el-icon><Plus /></el-icon>
|
||||
<div>{{proxy.$t('equipment.uploadImage')}}</div>
|
||||
</div>
|
||||
<div v-else class="upload-file">
|
||||
<el-image
|
||||
:src="form.devicePic"
|
||||
class="upload-image"
|
||||
fit="cover"
|
||||
:preview-src-list="[form.devicePic]"
|
||||
:initial-index="0"
|
||||
preview-teleported
|
||||
/>
|
||||
<span class="delete-icon" @click.stop="handleRemove">
|
||||
<el-icon><Close /></el-icon>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">
|
||||
{{proxy.$t('equipment.limitImage')}}
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">{{proxy.$t('button.save')}}</el-button>
|
||||
<el-button @click="cancel">{{proxy.$t('button.cancel')}}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import {getDeviceModelListApi, deleteDeviceModelApi, getDeviceModelDetailApi, editDeviceModelApi, addDeviceModelApi} from '@/api/equipment'
|
||||
import {ElMessage, ElMessageBox} from "element-plus";
|
||||
import { Plus, Close } from '@element-plus/icons-vue'
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { globalHeaders } from '@/utils/request';
|
||||
import {deleteDepartmentApi} from "@/api/customer/index.js";
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
const appStore = useAppStore();
|
||||
const { proxy } = getCurrentInstance();
|
||||
const loading = ref(false)
|
||||
const showSearch = ref(true)
|
||||
const total = ref(0)
|
||||
const maintenanceList = ref([])
|
||||
const { t } = useI18n();
|
||||
|
||||
const queryParams = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
deviceModel:''
|
||||
})
|
||||
// 添加弹框相关的数据
|
||||
const visible = ref(false)
|
||||
const title = ref('')
|
||||
const formRef = ref()
|
||||
|
||||
const form = reactive({
|
||||
deviceModel: '',
|
||||
devicePic: '',
|
||||
})
|
||||
const upload = reactive({
|
||||
// 设置上传的请求头部
|
||||
headers: globalHeaders(),
|
||||
// 上传的地址
|
||||
url: import.meta.env.VITE_APP_BASE_API + '/file/upload'
|
||||
});
|
||||
const rules = {
|
||||
deviceModel: [
|
||||
{ required: true, message: proxy.$t('equipment.deviceModel'), trigger: 'blur' }
|
||||
],
|
||||
devicePic: [
|
||||
{ required: true, message: proxy.$t('equipment.inputDeviceImage'), trigger: 'change' }
|
||||
]
|
||||
}
|
||||
|
||||
// 获取列表数据
|
||||
const getList = async() => {
|
||||
loading.value = true
|
||||
// 这里应该调用后端API获取数据
|
||||
const res = await getDeviceModelListApi(queryParams);
|
||||
if(res.code === 200) {
|
||||
maintenanceList.value = res.rows || []
|
||||
total.value = res.total
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNum = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
const resetQuery = () => {
|
||||
queryParams.deviceModel = ''
|
||||
handleQuery()
|
||||
}
|
||||
const handleDelete = (row) => {
|
||||
ElMessageBox.confirm(
|
||||
proxy.$t('common.deleteItem'),
|
||||
proxy.$t('common.warning'),
|
||||
{
|
||||
confirmButtonText: proxy.$t('button.save'),
|
||||
cancelButtonText: proxy.$t('button.cancel'),
|
||||
type: "warning"
|
||||
}
|
||||
|
||||
).then(async () => {
|
||||
const res = await deleteDeviceModelApi(row.id)
|
||||
if (res.code === 200) {
|
||||
ElMessage.success(res.msg)
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
getList()
|
||||
}).catch(() => {})
|
||||
}
|
||||
// 打开新增弹框
|
||||
const handleAdd = () => {
|
||||
visible.value = true
|
||||
title.value = proxy.$t('button.add')
|
||||
form.devicePic = ''
|
||||
form.deviceModel = ''
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
const beforeUpload = (file) => {
|
||||
const isJPG = file.type === 'image/jpeg' || file.type === 'image/png'
|
||||
const isLt50K = file.size / 1024 < 50
|
||||
console.log(file,'0000')
|
||||
if (!isJPG) {
|
||||
ElMessage.error('只能上传 JPG/PNG 格式的图片!')
|
||||
return false
|
||||
}
|
||||
if (!isLt50K) {
|
||||
ElMessage.error('图片大小不能超过 50KB!')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
const handleUploadSuccess = (response, file) => {
|
||||
form.devicePic = response.data.url
|
||||
ElMessage.success('上传成功')
|
||||
}
|
||||
|
||||
const handleUploadError = () => {
|
||||
ElMessage.error('上传失败')
|
||||
}
|
||||
|
||||
const handleRemove = (file) => {
|
||||
form.devicePic = ''
|
||||
}
|
||||
|
||||
const submitForm = async () => {
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
// 这里添加提交逻辑
|
||||
const res = title.value === proxy.$t('button.add') ? await addDeviceModelApi(form): await editDeviceModelApi(form)
|
||||
if(res.code === 200) {
|
||||
ElMessage.success(res.msg)
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
getList()
|
||||
cancel()
|
||||
} catch (error) {
|
||||
console.error(proxy.$t('common.formValidateError'), error)
|
||||
}
|
||||
}
|
||||
|
||||
const cancel = () => {
|
||||
visible.value = false
|
||||
form.devicePic = ''
|
||||
form.deviceModel = ''
|
||||
}
|
||||
// 编辑
|
||||
const handleUpdate = async(row) => {
|
||||
console.log(row,'---')
|
||||
const res = await getDeviceModelDetailApi(row.id)
|
||||
if(res.code === 200) {
|
||||
form.deviceModel = res.data.deviceModel
|
||||
form.devicePic = res.data.devicePic
|
||||
form.id = res.data.id
|
||||
title.value = proxy.$t('button.edit')
|
||||
visible.value = true
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.contentBox {
|
||||
height:calc(100vh - 130px);
|
||||
margin:20px;
|
||||
.card {
|
||||
width:100%;
|
||||
height: 100%;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
}
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.mb8 {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.upload-box, .upload-file {
|
||||
width:200px;
|
||||
height: 200px;
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.upload-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.upload-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.delete-icon {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
padding: 4px;
|
||||
color: #fff;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.delete-icon:hover {
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
:deep(.el-form-item__label) {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
@ -1,414 +1,191 @@
|
||||
<template>
|
||||
<div class="app-container home">
|
||||
<el-row :gutter="20">
|
||||
<el-col :sm="24" :lg="12" style="padding-left: 20px">
|
||||
<h2>测偏移管理框架</h2>
|
||||
<p>
|
||||
<b>当前版本:</b> <span>v{{ version }}</span>
|
||||
</p>
|
||||
<p>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="Cloudy"
|
||||
plain
|
||||
@click="goTarget('https://gitee.com/y_project/RuoYi-Vue')"
|
||||
>访问码云</el-button
|
||||
>
|
||||
<el-button
|
||||
icon="HomeFilled"
|
||||
plain
|
||||
@click="goTarget('http://ruoyi.vip')"
|
||||
>访问主页</el-button
|
||||
>
|
||||
</p>
|
||||
</el-col>
|
||||
<div class="dashboard">
|
||||
<div class="data-overview">
|
||||
<h3>{{proxy.$t('dashboard.dataOverview')}}</h3>
|
||||
<div class="card-list">
|
||||
<div class="data-card">
|
||||
<div class="img-icon">
|
||||
<img src="../assets/dashboard/customerNum.png" alt=""/>
|
||||
</div>
|
||||
<div class="data-right">
|
||||
<div class="number">{{ statistics.customerCount || 0 }}</div>
|
||||
<div class="label">{{proxy.$t('dashboard.customerCount')}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-card">
|
||||
<div class="img-icon">
|
||||
<img src="../assets/dashboard/deviceNum.png" alt=""/>
|
||||
</div>
|
||||
<div class="data-right">
|
||||
<div class="number">{{ statistics.deviceCount|| 0 }}</div>
|
||||
<div class="label">{{proxy.$t('dashboard.customerCount')}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-card">
|
||||
<div class="img-icon">
|
||||
<img src="../assets/dashboard/maintenanceNum.png" alt=""/>
|
||||
</div>
|
||||
<div class="data-right">
|
||||
<div class="number" >{{ statistics.maintainCount || 0 }}</div>
|
||||
<div class="label">{{proxy.$t('dashboard.maintenanceCount')}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-card">
|
||||
<div class="img-icon">
|
||||
<img src="../assets/dashboard/onlineDevice.png" alt=""/>
|
||||
</div>
|
||||
<div class="data-right">
|
||||
<div class="number" >{{ statistics.onlineCount || 0 }}</div>
|
||||
<div class="label">{{proxy.$t('dashboard.onlineDeviceCount')}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-card">
|
||||
<div class="img-icon">
|
||||
<img src="../assets/dashboard/offlineDevice.png" alt=""/>
|
||||
</div>
|
||||
<div class="data-right">
|
||||
<div class="number" >{{ statistics.offlineCount || 0 }}</div>
|
||||
<div class="label">{{proxy.$t('dashboard.offlineDeviceCount')}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-col :sm="24" :lg="12" style="padding-left: 50px">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<h2>技术选型</h2>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="6">
|
||||
<h4>后端技术</h4>
|
||||
<ul>
|
||||
<li>SpringBoot</li>
|
||||
<li>Spring Security</li>
|
||||
<li>JWT</li>
|
||||
<li>MyBatis</li>
|
||||
<li>Druid</li>
|
||||
<li>Fastjson</li>
|
||||
<li>...</li>
|
||||
</ul>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<h4>前端技术</h4>
|
||||
<ul>
|
||||
<li>Vue</li>
|
||||
<li>Vuex</li>
|
||||
<li>Element-ui</li>
|
||||
<li>Axios</li>
|
||||
<li>Sass</li>
|
||||
<li>Quill</li>
|
||||
<li>...</li>
|
||||
</ul>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-divider />
|
||||
<el-row :gutter="20">
|
||||
<el-col :xs="24" :sm="24" :md="12" :lg="8">
|
||||
<el-card class="update-log">
|
||||
<template v-slot:header>
|
||||
<div class="clearfix">
|
||||
<span>联系信息</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="body">
|
||||
<p>
|
||||
<i class="el-icon-s-promotion"></i> 官网:<el-link
|
||||
href="http://www.ruoyi.vip"
|
||||
target="_blank"
|
||||
>http://www.ruoyi.vip</el-link
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="12" :lg="8">
|
||||
<el-card class="update-log">
|
||||
<template v-slot:header>
|
||||
<div class="clearfix">
|
||||
<span>更新日志</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-collapse accordion>
|
||||
<el-collapse-item title="v3.8.9 - 2024-12-30">
|
||||
<ol>
|
||||
<li>用户管理支持分栏拖动</li>
|
||||
<li>修改主题样式本地读取</li>
|
||||
<li>用户头像http(s)链接支持</li>
|
||||
<li>用户管理过滤掉已禁用部门</li>
|
||||
<li>支持自定义显示Excel属性列</li>
|
||||
<li>操作日志记录DELETE请求参数</li>
|
||||
<li>白名单支持对通配符路径匹配</li>
|
||||
<li>校检文件名是否包含特殊字符</li>
|
||||
<li>代码生成创建表屏蔽违规的字符</li>
|
||||
<li>菜单面包屑导航支持多层级显示</li>
|
||||
<li>Excel注解支持wrapText是否允许内容换行</li>
|
||||
<li>代码生成新增配置是否允许文件覆盖到本地</li>
|
||||
<li>修复角色禁用权限不失效问题</li>
|
||||
<li>修复代码生成上级菜单显示问题</li>
|
||||
<li>修复导出子列表对象只能在最后的问题</li>
|
||||
<li>修复TopNav无法正确获取active的问题</li>
|
||||
<li>修复默认关闭Tags-Views内链页面打不开</li>
|
||||
<li>升级oshi到最新版本6.6.5</li>
|
||||
<li>升级tomcat到最新版本9.0.96</li>
|
||||
<li>升级fastjson到最新版2.0.53</li>
|
||||
<li>升级logback到最新版本1.2.13</li>
|
||||
<li>升级spring-framework到最新版本5.3.39</li>
|
||||
<li>升级quill到最新版本2.0.2</li>
|
||||
<li>升级axios到最新版本0.28.1</li>
|
||||
<li>优化身份证脱敏正则</li>
|
||||
<li>优化权限更新后同步缓存</li>
|
||||
<li>优化查询时间范围日期格式</li>
|
||||
<li>优化参数键值更换为多行文本</li>
|
||||
<li>优化导入带标题文件关闭清理</li>
|
||||
<li>优化上传图片带域名不增加前缀</li>
|
||||
<li>优化特殊字符密码修改失败问题</li>
|
||||
<li>优化无用户编号不校验数据权限</li>
|
||||
<li>优化TopNav内链菜单点击没有高亮</li>
|
||||
<li>优化菜单管理切换Mini布局错乱问题</li>
|
||||
<li>其他细节优化</li>
|
||||
</ol>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="v3.8.8 - 2024-06-30">
|
||||
<ol>
|
||||
<li>菜单管理新增路由名称</li>
|
||||
<li>新增数据脱敏过滤注解</li>
|
||||
<li>用户密码新增非法字符验证</li>
|
||||
<li>限制用户操作数据权限范围</li>
|
||||
<li>代码生成新增创建表结构功能</li>
|
||||
<li>定时任务白名单配置范围缩小</li>
|
||||
<li>优化代码生成主子表关联查询方式</li>
|
||||
<li>Excel注解新增属性comboReadDict</li>
|
||||
<li>Excel注解ColumnType类型新增文本</li>
|
||||
<li>新增国际化资源文件配置</li>
|
||||
<li>升级oshi到最新版本6.6.1</li>
|
||||
<li>升级druid到最新版本1.2.23</li>
|
||||
<li>升级core-js到最新版本3.37.1</li>
|
||||
<li>更新HttpUtils中的User-Agent</li>
|
||||
<li>更新compressionPlugin到6.1.2以兼容node18+</li>
|
||||
<li>升级spring-security到安全版本,防止漏洞风险</li>
|
||||
<li>升级spring-framework到安全版本,防止漏洞风险</li>
|
||||
<li>优化自定义XSS注解匹配方式</li>
|
||||
<li>优化缓存监控键名列表排序显示</li>
|
||||
<li>优化定时任务日志默认按时间排序</li>
|
||||
<li>优化默认文件大小超过2G无效的问题</li>
|
||||
<li>优化查表特殊字符使用反斜杠进行转义</li>
|
||||
<li>优化定时任务cron表达式小时配置显示错误问题</li>
|
||||
<li>优化多个自定数据权限使用in查询,避免多次拼接</li>
|
||||
<li>优化导入Excel时设置dictType属性重复查缓存问题</li>
|
||||
<li>其他细节优化</li>
|
||||
</ol>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="v3.8.7 - 2023-12-08">
|
||||
<ol>
|
||||
<li>操作日志记录部门名称</li>
|
||||
<li>全局数据存储用户编号</li>
|
||||
<li>新增编程式判断资源访问权限</li>
|
||||
<li>操作日志列表新增IP地址查询</li>
|
||||
<li>定时任务新增页去除状态选项</li>
|
||||
<li>代码生成支持选择前端模板类型</li>
|
||||
<li>显隐列组件支持复选框弹出类型</li>
|
||||
<li>通用排序属性orderBy参数限制长度</li>
|
||||
<li>Excel自定义数据处理器增加单元格/工作簿对象</li>
|
||||
<li>升级oshi到最新版本6.4.8</li>
|
||||
<li>升级druid到最新版本1.2.20</li>
|
||||
<li>升级fastjson到最新版2.0.43</li>
|
||||
<li>升级pagehelper到最新版1.4.7</li>
|
||||
<li>升级commons.io到最新版本2.13.0</li>
|
||||
<li>升级element-ui到最新版本2.15.14</li>
|
||||
<li>修复五级路由缓存无效问题</li>
|
||||
<li>修复外链带端口出现的异常</li>
|
||||
<li>修复树模板父级编码变量错误</li>
|
||||
<li>修复字典表详情页面搜索问题</li>
|
||||
<li>修复内链iframe没有传递参数问题</li>
|
||||
<li>修复自定义字典样式不生效的问题</li>
|
||||
<li>修复字典缓存删除方法参数错误问题</li>
|
||||
<li>修复Excel导入数据临时文件无法删除问题</li>
|
||||
<li>修复未登录带参数访问成功后参数丢失问题</li>
|
||||
<li>修复HeaderSearch组件跳转query参数丢失问题</li>
|
||||
<li>修复代码生成导入后必填项与数据库不匹配问题</li>
|
||||
<li>修复Excels导入时无法获取到dictType字典值问题</li>
|
||||
<li>优化下载zip方法新增遮罩层</li>
|
||||
<li>优化头像上传参数新增文件名称</li>
|
||||
<li>优化字典标签支持自定义分隔符</li>
|
||||
<li>优化菜单管理类型为按钮状态可选</li>
|
||||
<li>优化前端防重复提交数据大小限制</li>
|
||||
<li>优化TopNav菜单没有图标svg不显示</li>
|
||||
<li>优化数字金额大写转换精度丢失问题</li>
|
||||
<li>优化富文本Editor组件检验图片格式</li>
|
||||
<li>优化页签在Firefox浏览器被遮挡的问题</li>
|
||||
<li>优化个人中心/基本资料修改时数据显示问题</li>
|
||||
<li>优化缓存监控图表支持跟随屏幕大小自适应调整</li>
|
||||
<li>其他细节优化</li>
|
||||
</ol>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="v3.8.6 - 2023-06-30">
|
||||
<ol>
|
||||
<li>支持登录IP黑名单限制</li>
|
||||
<li>新增监控页面图标显示</li>
|
||||
<li>操作日志新增消耗时间属性</li>
|
||||
<li>屏蔽定时任务bean违规的字符</li>
|
||||
<li>日志管理使用索引提升查询性能</li>
|
||||
<li>日志注解支持排除指定的请求参数</li>
|
||||
<li>支持自定义隐藏属性列过滤子对象</li>
|
||||
<li>升级oshi到最新版本6.4.3</li>
|
||||
<li>升级druid到最新版本1.2.16</li>
|
||||
<li>升级fastjson到最新版2.0.34</li>
|
||||
<li>升级spring-boot到最新版本2.5.15</li>
|
||||
<li>升级element-ui到最新版本2.15.13</li>
|
||||
<li>移除apache/commons-fileupload依赖</li>
|
||||
<li>修复页面切换时布局错乱的问题</li>
|
||||
<li>修复匿名注解Anonymous空指针问题</li>
|
||||
<li>修复路由跳转被阻止时内部产生报错信息问题</li>
|
||||
<li>修复isMatchedIp的参数判断产生空指针的问题</li>
|
||||
<li>修复用户多角色数据权限可能出现权限抬升的情况</li>
|
||||
<li>修复开启TopNav后一级菜单路由参数设置无效问题</li>
|
||||
<li>修复DictTag组件value没有匹配的值时则展示value</li>
|
||||
<li>优化文件下载出现的异常</li>
|
||||
<li>优化选择图标组件高亮回显</li>
|
||||
<li>优化弹窗后导航栏偏移的问题</li>
|
||||
<li>优化修改密码日志存储明文问题</li>
|
||||
<li>优化页签栏关闭其他出现的异常问题</li>
|
||||
<li>优化页签关闭左侧选项排除首页选项</li>
|
||||
<li>优化关闭当前tab页跳转最右侧tab页</li>
|
||||
<li>优化缓存列表清除操作提示不变的问题</li>
|
||||
<li>优化字符未使用下划线不进行驼峰式处理</li>
|
||||
<li>优化用户导入更新时需获取用户编号问题</li>
|
||||
<li>优化侧边栏的平台标题与VUE_APP_TITLE保持同步</li>
|
||||
<li>优化导出Excel时设置dictType属性重复查缓存问题</li>
|
||||
<li>连接池Druid支持新的配置connectTimeout和socketTimeout</li>
|
||||
<li>其他细节优化</li>
|
||||
</ol>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="v3.8.5 - 2023-01-01">
|
||||
<ol>
|
||||
<li>定时任务违规的字符</li>
|
||||
<li>重置时取消部门选中</li>
|
||||
<li>新增返回警告消息提示</li>
|
||||
<li>忽略不必要的属性数据返回</li>
|
||||
<li>修改参数键名时移除前缓存配置</li>
|
||||
<li>导入更新用户数据前校验数据权限</li>
|
||||
<li>兼容Excel下拉框内容过多无法显示的问题</li>
|
||||
<li>升级echarts到最新版本5.4.0</li>
|
||||
<li>升级core-js到最新版本3.25.3</li>
|
||||
<li>升级oshi到最新版本6.4.0</li>
|
||||
<li>升级kaptcha到最新版2.3.3</li>
|
||||
<li>升级druid到最新版本1.2.15</li>
|
||||
<li>升级fastjson到最新版2.0.20</li>
|
||||
<li>升级pagehelper到最新版1.4.6</li>
|
||||
<li>优化弹窗内容过多展示不全问题</li>
|
||||
<li>优化swagger-ui静态资源使用缓存</li>
|
||||
<li>开启TopNav没有子菜单隐藏侧边栏</li>
|
||||
<li>删除fuse无效选项maxPatternLength</li>
|
||||
<li>优化导出对象的子列表为空会出现[]问题</li>
|
||||
<li>优化编辑头像时透明部分会变成黑色问题</li>
|
||||
<li>优化小屏幕上修改头像界面布局错位的问题</li>
|
||||
<li>修复代码生成勾选属性无效问题</li>
|
||||
<li>修复文件上传组件格式验证问题</li>
|
||||
<li>修复回显数据字典数组异常问题</li>
|
||||
<li>修复sheet超出最大行数异常问题</li>
|
||||
<li>修复Log注解GET请求记录不到参数问题</li>
|
||||
<li>修复调度日志点击多次数据不变化的问题</li>
|
||||
<li>修复主题颜色在Drawer组件不会加载问题</li>
|
||||
<li>修复文件名包含特殊字符的文件无法下载问题</li>
|
||||
<li>修复table中更多按钮切换主题色未生效修复问题</li>
|
||||
<li>修复某些特性的环境生成代码变乱码TXT文件问题</li>
|
||||
<li>修复代码生成图片/文件/单选时选择必填无法校验问题</li>
|
||||
<li>修复某些特性的情况用户编辑对话框中角色和部门无法修改问题</li>
|
||||
<li>其他细节优化</li>
|
||||
</ol>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="v3.8.4 - 2022-09-26">
|
||||
<ol>
|
||||
<li>数据逻辑删除不进行唯一验证</li>
|
||||
<li>Excel注解支持导出对象的子列表方法</li>
|
||||
<li>Excel注解支持自定义隐藏属性列</li>
|
||||
<li>Excel注解支持backgroundColor属性设置背景色</li>
|
||||
<li>支持配置密码最大错误次数/锁定时间</li>
|
||||
<li>登录日志新增解锁账户功能</li>
|
||||
<li>通用下载方法新增config配置选项</li>
|
||||
<li>支持多权限字符匹配角色数据权限</li>
|
||||
<li>页面内嵌iframe切换tab不刷新数据</li>
|
||||
<li>操作日志记录支持排除敏感属性字段</li>
|
||||
<li>修复多文件上传报错出现的异常问题</li>
|
||||
<li>修复图片预览组件src属性为null值控制台报错问题</li>
|
||||
<li>升级oshi到最新版本6.2.2</li>
|
||||
<li>升级fastjson到最新版2.0.14</li>
|
||||
<li>升级pagehelper到最新版1.4.3</li>
|
||||
<li>升级core-js到最新版本3.25.2</li>
|
||||
<li>升级element-ui到最新版本2.15.10</li>
|
||||
<li>优化任务过期不执行调度</li>
|
||||
<li>优化字典数据使用store存取</li>
|
||||
<li>优化修改资料头像被覆盖的问题</li>
|
||||
<li>优化修改用户登录账号重复验证</li>
|
||||
<li>优化代码生成同步后值NULL问题</li>
|
||||
<li>优化定时任务支持执行父类方法</li>
|
||||
<li>优化用户个人信息接口防止修改部门</li>
|
||||
<li>优化布局设置使用el-drawer抽屉显示</li>
|
||||
<li>优化没有权限的用户编辑部门缺少数据</li>
|
||||
<li>优化日志注解记录限制请求地址的长度</li>
|
||||
<li>优化excel/scale属性导出单元格数值类型</li>
|
||||
<li>优化日志操作中重置按钮时重复查询的问题</li>
|
||||
<li>优化多个相同角色数据导致权限SQL重复问题</li>
|
||||
<li>优化表格上右侧工具条(搜索按钮显隐&右侧样式凸出)</li>
|
||||
<li>其他细节优化</li>
|
||||
</ol>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="v1.0.0 - 2019-10-08">
|
||||
<ol>
|
||||
<li>若依前后端分离系统正式发布</li>
|
||||
</ol>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="12" :lg="8">
|
||||
<el-card class="update-log">
|
||||
<template v-slot:header>
|
||||
<div class="clearfix">
|
||||
<span>捐赠支持</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="body">
|
||||
<img
|
||||
src="@/assets/images/pay.png"
|
||||
alt="donate"
|
||||
style="width:100%"
|
||||
/>
|
||||
<span style="display: inline-block; height: 30px; line-height: 30px"
|
||||
>你可以请作者喝杯咖啡表示鼓励</span
|
||||
>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="chart-container">
|
||||
<div class="chart-box">
|
||||
<h3>{{proxy.$t('dashboard.maintenanceAnalysis')}}</h3>
|
||||
<DashChart :chart-data="maintenanceData" :series-data="seriesData"/>
|
||||
</div>
|
||||
<div class="chart-box">
|
||||
<h3>{{proxy.$t('dashboard.maintenanceSummary')}}</h3>
|
||||
<DashPie :chart-data="maintenanceSummary" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Index">
|
||||
const version = ref('3.8.9')
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import DashChart from '../components/DashChart.vue'
|
||||
import DashPie from '../components/DashPie.vue'
|
||||
import { getDashboardApi, getMaintainSummaryApi, getMaintainAnalysisApi } from '@/api/dashboard'
|
||||
const { proxy } = getCurrentInstance();
|
||||
const statistics = ref({})
|
||||
const maintenanceData = ref({})
|
||||
const seriesData = ref([])
|
||||
const maintenanceSummary = ref({
|
||||
total: 38,
|
||||
data: []
|
||||
})
|
||||
|
||||
function goTarget(url) {
|
||||
window.open(url, '__blank')
|
||||
const getStatistics = async () => {
|
||||
try {
|
||||
const res = await getDashboardApi()
|
||||
if (res.code === 200) {
|
||||
statistics.value = res.data
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取统计数据失败', error)
|
||||
}
|
||||
}
|
||||
const getMaintainAnalysis = async() => {
|
||||
try {
|
||||
const res = await getMaintainAnalysisApi()
|
||||
if (res.code === 200) {
|
||||
maintenanceData.value =
|
||||
res.data.customerList.map(item => ({
|
||||
name: item
|
||||
}))
|
||||
console.log(res,'===')
|
||||
seriesData.value = res.data.maintenanceTypeList.map(item => ({
|
||||
name: item.maintenanceTypeName,
|
||||
data: item.dataList
|
||||
}))
|
||||
console.log(seriesData.value,'===')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取维保分析数据失败', error)
|
||||
}
|
||||
}
|
||||
// 获取维保汇总数据
|
||||
const getMaintainSummary = async () => {
|
||||
try {
|
||||
const res = await getMaintainSummaryApi()
|
||||
if (res.code === 200) {
|
||||
maintenanceSummary.value = {
|
||||
total: res.data.reduce((sum, item) => sum + item.maintenanceTypeCount, 0),
|
||||
data: res.data.map(item => ({
|
||||
name: item.maintenanceTypeName,
|
||||
value: item.maintenanceTypeCount
|
||||
}))
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取维保汇总数据失败', error)
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
getStatistics()
|
||||
getMaintainSummary()
|
||||
getMaintainAnalysis()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.home {
|
||||
blockquote {
|
||||
padding: 10px 20px;
|
||||
.dashboard {
|
||||
padding: 20px;
|
||||
|
||||
h3 {
|
||||
margin: 0 0 20px;
|
||||
font-size: 17.5px;
|
||||
border-left: 5px solid #eee;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
hr {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
border: 0;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
.col-item {
|
||||
|
||||
.data-overview {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
.card-list {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
color: #676a6c;
|
||||
overflow-x: hidden;
|
||||
.data-card {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
align-items: center;
|
||||
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
|
||||
.img-icon {
|
||||
border:1px dashed #ccc;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.number {
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
color:#333333;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 10px;
|
||||
font-size: 26px;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 10px;
|
||||
|
||||
b {
|
||||
font-weight: 700;
|
||||
.label {
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.update-log {
|
||||
ol {
|
||||
display: block;
|
||||
list-style-type: decimal;
|
||||
margin-block-start: 1em;
|
||||
margin-block-end: 1em;
|
||||
margin-inline-start: 0;
|
||||
margin-inline-end: 0;
|
||||
padding-inline-start: 40px;
|
||||
.chart-container {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
|
||||
.chart-box {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
</style>
|
@ -1,14 +1,15 @@
|
||||
<template>
|
||||
<div class="login">
|
||||
<el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
|
||||
<h3 class="title">{{ title }}</h3>
|
||||
<h3 class="title-color">{{ proxy.$t('login.loginTitle') }}</h3>
|
||||
<h3 class="title">{{ proxy.$t('login.loginTip') }}</h3>
|
||||
<el-form-item prop="username">
|
||||
<el-input
|
||||
v-model="loginForm.username"
|
||||
type="text"
|
||||
size="large"
|
||||
style="width:300px"
|
||||
auto-complete="off"
|
||||
placeholder="账号"
|
||||
:placeholder="proxy.$t('login.username')"
|
||||
>
|
||||
<template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template>
|
||||
</el-input>
|
||||
@ -17,30 +18,30 @@
|
||||
<el-input
|
||||
v-model="loginForm.password"
|
||||
type="password"
|
||||
size="large"
|
||||
style="width:300px"
|
||||
auto-complete="off"
|
||||
placeholder="密码"
|
||||
:placeholder="proxy.$t('login.password')"
|
||||
@keyup.enter="handleLogin"
|
||||
>
|
||||
<template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="code" v-if="captchaEnabled">
|
||||
<el-input
|
||||
v-model="loginForm.code"
|
||||
size="large"
|
||||
auto-complete="off"
|
||||
placeholder="验证码"
|
||||
style="width: 63%"
|
||||
@keyup.enter="handleLogin"
|
||||
>
|
||||
<template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
|
||||
</el-input>
|
||||
<div class="login-code">
|
||||
<img :src="codeUrl" @click="getCode" class="login-code-img"/>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox>
|
||||
<!-- <el-form-item prop="code" v-if="captchaEnabled">-->
|
||||
<!-- <el-input-->
|
||||
<!-- v-model="loginForm.code"-->
|
||||
<!-- size="large"-->
|
||||
<!-- auto-complete="off"-->
|
||||
<!-- placeholder="请输入密码"-->
|
||||
<!-- style="width: 63%"-->
|
||||
<!-- @keyup.enter="handleLogin"-->
|
||||
<!-- >-->
|
||||
<!-- <template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>-->
|
||||
<!-- </el-input>-->
|
||||
<!-- <div class="login-code">-->
|
||||
<!-- <img :src="codeUrl" @click="getCode" class="login-code-img"/>-->
|
||||
<!-- </div>-->
|
||||
<!-- </el-form-item>-->
|
||||
<el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">{{ proxy.$t('login.rememberPassword') }}</el-checkbox>
|
||||
<el-form-item style="width:100%;">
|
||||
<el-button
|
||||
:loading="loading"
|
||||
@ -49,18 +50,18 @@
|
||||
style="width:100%;"
|
||||
@click.prevent="handleLogin"
|
||||
>
|
||||
<span v-if="!loading">登 录</span>
|
||||
<span v-else>登 录 中...</span>
|
||||
<span v-if="!loading">{{ proxy.$t('login.login') }}</span>
|
||||
<span v-else>{{ proxy.$t('login.logging') }}</span>
|
||||
</el-button>
|
||||
<div style="float: right;" v-if="register">
|
||||
<router-link class="link-type" :to="'/register'">立即注册</router-link>
|
||||
<router-link class="link-type" :to="'/register'">{{ proxy.$t('login.switchRegisterPage') }}</router-link>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<!-- 底部 -->
|
||||
<div class="el-login-footer">
|
||||
<span>Copyright © 2018-2025 ruoyi.vip All Rights Reserved.</span>
|
||||
</div>
|
||||
<!-- <div class="el-login-footer">-->
|
||||
<!-- <span>Copyright © 2018-2025 ruoyi.vip All Rights Reserved.</span>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -69,13 +70,12 @@ import { getCodeImg } from "@/api/login";
|
||||
import Cookies from "js-cookie";
|
||||
import { encrypt, decrypt } from "@/utils/jsencrypt";
|
||||
import useUserStore from '@/store/modules/user'
|
||||
|
||||
const title = import.meta.env.VITE_APP_TITLE;
|
||||
import { useI18n } from 'vue-i18n';
|
||||
const userStore = useUserStore();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const { t } = useI18n();
|
||||
const loginForm = ref({
|
||||
username: "admin",
|
||||
password: "admin123",
|
||||
@ -168,21 +168,22 @@ getCookie();
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
background-image: url("../assets/images/login-background.jpg");
|
||||
background-image: url("../assets/images/loginBg.jpg");
|
||||
background-size: cover;
|
||||
}
|
||||
.title {
|
||||
margin: 0px auto 30px auto;
|
||||
text-align: center;
|
||||
color: #707070;
|
||||
}
|
||||
color: #000;
|
||||
|
||||
}
|
||||
.title-color {
|
||||
color:#F59537;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.login-form {
|
||||
border-radius: 6px;
|
||||
background: #ffffff;
|
||||
width: 400px;
|
||||
padding: 25px 25px 5px 25px;
|
||||
position: absolute;
|
||||
right:20%;
|
||||
.el-input {
|
||||
height: 40px;
|
||||
input {
|
||||
|
190
src/views/maintenance/common/MaintenanceDialog.vue
Normal file
190
src/views/maintenance/common/MaintenanceDialog.vue
Normal file
@ -0,0 +1,190 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:title="title"
|
||||
v-model="visible"
|
||||
style="width: 460px"
|
||||
append-to-body
|
||||
:before-close="cancel"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-width="appStore.language === 'zh_CN' ? '100px' : '160px'"
|
||||
label-position="right"
|
||||
>
|
||||
<el-form-item :label="proxy.$t('maintenance.deviceCode')" prop="deviceCode">
|
||||
<el-select v-model="form.deviceCode" :disabled="title === proxy.$t('button.details') " :placeholder="proxy.$t('common.select')" style="width:300px">
|
||||
<el-option
|
||||
v-for="item in deviceOptions"
|
||||
:key="item.deviceCode"
|
||||
:label="item.deviceCode"
|
||||
:value="item.deviceCode"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="proxy.$t('maintenance.maintenanceType')" prop="maintenanceType">
|
||||
<el-select v-model="form.maintenanceType" :disabled="title === proxy.$t('button.details') " :placeholder="proxy.$t('common.select')" style="width:300px">
|
||||
<el-option
|
||||
v-for="item in maintenanceTypeList"
|
||||
:key="item.dictValue"
|
||||
:label="item.dictLabel"
|
||||
:value="item.dictValue"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="proxy.$t('maintenance.maintenanceUser')" prop="maintenanceUser">
|
||||
<el-input :disabled="title === proxy.$t('button.details') " v-model="form.maintenanceUser" :placeholder="proxy.$t('maintenance.inputMaintenanceUser')" style="width:300px"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="proxy.$t('maintenance.maintenanceDate')" prop="maintenanceDate">
|
||||
<el-date-picker
|
||||
:disabled="title === proxy.$t('button.details') "
|
||||
style="width:300px"
|
||||
v-model="form.maintenanceDate"
|
||||
type="date"
|
||||
:placeholder="proxy.$t('common.selectDate')"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
:disabled-date="disabledDate"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="proxy.$t('common.comments')" prop="comments">
|
||||
<el-input
|
||||
v-model="form.comments"
|
||||
style="width:300px"
|
||||
:disabled="title === proxy.$t('button.details') "
|
||||
type="textarea"
|
||||
:placeholder="proxy.$t('common.inputText')"
|
||||
:rows="4"
|
||||
maxlength="200"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" v-if="title!== proxy.$t('button.details') " @click="submitForm">{{proxy.$t('button.save')}}</el-button>
|
||||
<el-button @click="cancel">{{ proxy.$t('button.cancel') }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive,onMounted } from 'vue'
|
||||
import {addMaintainApi, editMaintainApi,getDeviceCodeListApi} from '@/api/maintenance';
|
||||
import {ElMessage} from "element-plus";
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
const appStore = useAppStore();
|
||||
const { proxy } = getCurrentInstance();
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
maintenanceTypeList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
formData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue', 'success'])
|
||||
|
||||
const visible = ref(false)
|
||||
const formRef = ref()
|
||||
|
||||
const deviceOptions = ref([
|
||||
{ value: 'CPY0092', label: 'CPY0092' },
|
||||
{ value: 'CPY0057', label: 'CPY0057' }
|
||||
])
|
||||
|
||||
const form = reactive({
|
||||
deviceCode: '',
|
||||
maintenanceType: '',
|
||||
maintenanceUser: '',
|
||||
maintenanceDate: '',
|
||||
comments: ''
|
||||
})
|
||||
|
||||
const rules = {
|
||||
deviceCode: [{ required: true, message: proxy.$t('maintenance.inputDeviceCode'), trigger: 'change' }],
|
||||
maintenanceType: [{ required: true, message: proxy.$t('maintenance.inputMaintenanceType'), trigger: 'change' }],
|
||||
maintenanceUser: [{ required: true, message: proxy.$t('maintenance.inputMaintenanceUser'), trigger: 'blur' }],
|
||||
maintenanceDate: [{ required: true, message: proxy.$t('maintenance.inputMaintenanceDate'), trigger: 'change' }],
|
||||
comments: [{ required: true, message: proxy.$t('common.inputComments'), trigger: 'blur' }]
|
||||
}
|
||||
|
||||
const disabledDate = (time) => {
|
||||
return time.getTime() > Date.now()
|
||||
}
|
||||
|
||||
const submitForm = async () => {
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
// 这里添加提交逻辑
|
||||
const res = props.title === proxy.$t('button.add') ? await addMaintainApi(form) : await editMaintainApi(form)
|
||||
if(res.code === 200) {
|
||||
ElMessage.success(res.msg)
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
emit('success')
|
||||
cancel()
|
||||
} catch (error) {
|
||||
console.error(proxy.$t('common.formValidateError'), error)
|
||||
}
|
||||
}
|
||||
|
||||
const cancel = () => {
|
||||
resetForm()
|
||||
emit('update:modelValue', false)
|
||||
}
|
||||
|
||||
const resetForm = () => {
|
||||
formRef.value?.resetFields()
|
||||
if (props.title === proxy.$t('button.add')) {
|
||||
// 新增模式下才清空表单
|
||||
Object.keys(form).forEach(key => {
|
||||
form[key] = ''
|
||||
})
|
||||
}
|
||||
}
|
||||
// 获取设备编码
|
||||
const getDeviceCode = async () => {
|
||||
// 这里应该调用后端API获取数据
|
||||
const res = await getDeviceCodeListApi();
|
||||
if(res.code === 200) {
|
||||
deviceOptions.value = res.data || []
|
||||
}
|
||||
}
|
||||
watch(
|
||||
() => props.title,
|
||||
(newVal) => {
|
||||
if (newVal === proxy.$t('button.edit') || newVal===proxy.$t('button.details') && Object.keys(props.formData).length > 0) {
|
||||
// 编辑模式下,使用传入的表单数据
|
||||
Object.keys(form).forEach(key => {
|
||||
form[key] = props.formData[key]
|
||||
})
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
onMounted(() => {
|
||||
getDeviceCode()
|
||||
})
|
||||
defineExpose({
|
||||
visible
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
:deep(.el-form-item__label) {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.dialog-footer {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
233
src/views/maintenance/index.vue
Normal file
233
src/views/maintenance/index.vue
Normal file
@ -0,0 +1,233 @@
|
||||
<template>
|
||||
<div class="contentBox">
|
||||
<el-card class="card" shadow="hover">
|
||||
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch">
|
||||
<el-form-item :label="proxy.$t('maintenance.deviceName')" prop="deviceName">
|
||||
<el-input v-model="queryParams.deviceName" :placeholder="proxy.$t('maintenance.deviceName')" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="proxy.$t('maintenance.deviceCode')" prop="deviceCode">
|
||||
<el-input v-model="queryParams.deviceCode" :placeholder="proxy.$t('maintenance.deviceCode')" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="proxy.$t('maintenance.maintenanceType')" prop="maintenanceType">
|
||||
<el-select v-model="queryParams.maintenanceType" :placeholder="proxy.$t('maintenance.maintenanceType')" clearable style="width:200px" >
|
||||
<el-option
|
||||
v-for="item in maintenanceTypeList"
|
||||
:key="item.dictValue"
|
||||
:label="item.dictLabel"
|
||||
:value="item.dictValue"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="proxy.$t('maintenance.maintenanceDate')" prop="maintenanceDate">
|
||||
<el-date-picker
|
||||
v-model="queryParams.maintenanceDate"
|
||||
type="date"
|
||||
:disabled-date="disabledDate"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
:placeholder="proxy.$t('maintenance.maintenanceDate')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">{{proxy.$t('button.search')}}</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">{{proxy.$t('button.reset')}}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Plus"
|
||||
@click="handleAdd"
|
||||
>{{proxy.$t('button.add')}}</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="maintenanceList" border>
|
||||
<el-table-column :label="proxy.$t('common.sort')" type="index" width="60" align="center" />
|
||||
<el-table-column :label="proxy.$t('maintenance.deviceName')" align="center" prop="deviceName" />
|
||||
<el-table-column :label="proxy.$t('maintenance.deviceModel')" align="center" prop="deviceModel" />
|
||||
<el-table-column :label="proxy.$t('maintenance.deviceCode')" align="center" prop="deviceCode" />
|
||||
<el-table-column :label="proxy.$t('common.customerAffiliation')" align="center" prop="customerName" />
|
||||
<el-table-column :label="proxy.$t('maintenance.maintenanceType')" align="center" prop="maintenanceTypeName" />
|
||||
<el-table-column :label="proxy.$t('common.comments')" align="center" prop="comments" />
|
||||
<el-table-column :label="proxy.$t('maintenance.maintenanceUser')" align="center" prop="maintenanceUser" />
|
||||
<el-table-column :label="proxy.$t('maintenance.maintenanceDate')" align="center" prop="maintenanceDate" width="100" />
|
||||
<el-table-column :label="proxy.$t('maintenance.createTime')" align="center" prop="createTime" width="160" />
|
||||
<el-table-column :label="proxy.$t('common.action')" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Edit"
|
||||
@click="handleUpdate(scope.row,proxy.$t('button.edit'))"
|
||||
>{{ proxy.$t('button.edit') }}</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="View"
|
||||
@click="handleUpdate(scope.row,proxy.$t('button.details'))"
|
||||
>{{proxy.$t('button.check')}}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination">
|
||||
<el-pagination
|
||||
v-if="total > 0"
|
||||
v-model:current-page="queryParams.pageNum"
|
||||
v-model:page-size="queryParams.pageSize"
|
||||
:total="total"
|
||||
:page-sizes="[10, 20, 30, 50]"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
<MaintenanceDialog
|
||||
ref="dialogRef"
|
||||
:title="dialog.title"
|
||||
:formData="dialog.form"
|
||||
:maintenance-type-list="maintenanceTypeList"
|
||||
v-model="dialog.visible"
|
||||
@success="handleSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import {getMaintainListApi,getMaintainTypeApi, getMaintainDetailApi} from '@/api/maintenance/index.js'
|
||||
import {ElMessage} from "element-plus";
|
||||
import MaintenanceDialog from "./common/MaintenanceDialog.vue";
|
||||
import { useI18n } from 'vue-i18n';
|
||||
const router = useRouter()
|
||||
const { proxy } = getCurrentInstance();
|
||||
const queryFormRef = ref()
|
||||
const loading = ref(false)
|
||||
const showSearch = ref(true)
|
||||
const total = ref(0)
|
||||
const maintenanceTypeList = ref([])
|
||||
const maintenanceList = ref([])
|
||||
const { t } = useI18n();
|
||||
|
||||
const queryParams = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
deviceName: '',
|
||||
deviceCode: '',
|
||||
maintenanceType: '',
|
||||
maintenanceDate: ''
|
||||
})
|
||||
// 添加弹框相关的数据
|
||||
const dialog = reactive({
|
||||
title: proxy.$t('button.add'),
|
||||
visible: false,
|
||||
form: {
|
||||
deviceCode: '',
|
||||
maintenanceType: '',
|
||||
maintenanceUser: '',
|
||||
maintenanceDate: '',
|
||||
comments: ''
|
||||
}
|
||||
})
|
||||
const dialogRef = ref()
|
||||
|
||||
const getList = async() => {
|
||||
loading.value = true
|
||||
// 这里应该调用后端API获取数据
|
||||
const res = await getMaintainListApi(queryParams);
|
||||
if(res.code === 200) {
|
||||
maintenanceList.value = res.rows || []
|
||||
total.value = res.total
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNum = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
// 打开新增弹框
|
||||
const handleAdd = () => {
|
||||
dialog.title = proxy.$t('button.add')
|
||||
dialog.visible = true
|
||||
}
|
||||
|
||||
// 添加成功回调方法
|
||||
const handleSuccess = () => {
|
||||
getList()
|
||||
}
|
||||
// 编辑
|
||||
const handleUpdate = async(row,value) => {
|
||||
const res = await getMaintainDetailApi(row.id)
|
||||
if(res.code === 200) {
|
||||
dialog.title = value
|
||||
dialog.visible = true
|
||||
dialog.form.deviceCode = res.data.deviceCode
|
||||
dialog.form.maintenanceType = res.data.maintenanceType.toString()
|
||||
dialog.form.maintenanceUser = res.data.maintenanceUser
|
||||
dialog.form.maintenanceDate = res.data.maintenanceDate
|
||||
dialog.form.comments = res.data.comments
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
}
|
||||
|
||||
const disabledDate = (time) => {
|
||||
return time.getTime() > Date.now()
|
||||
}
|
||||
const handleSizeChange = (val) => {
|
||||
queryParamsNoPage.pageSize = val
|
||||
getList()
|
||||
}
|
||||
|
||||
const handleCurrentChange = (val) => {
|
||||
queryParamsNoPage.pageNum = val
|
||||
getList()
|
||||
}
|
||||
// 获取维保类型
|
||||
const getMaintainType = async() => {
|
||||
const res = await getMaintainTypeApi()
|
||||
if(res.code === 200) {
|
||||
maintenanceTypeList.value = res.data || []
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
getList()
|
||||
getMaintainType()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.contentBox {
|
||||
height:calc(100vh - 130px);
|
||||
margin:20px;
|
||||
.card {
|
||||
width:100%;
|
||||
height: 100%;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
}
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.mb8 {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
</style>
|
@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6" :xs="24">
|
||||
<el-col :span="8" :xs="24">
|
||||
<el-card class="box-card">
|
||||
<template v-slot:header>
|
||||
<div class="clearfix">
|
||||
<span>个人信息</span>
|
||||
<span>{{proxy.$t('common.personalInfo')}}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
@ -14,45 +14,45 @@
|
||||
</div>
|
||||
<ul class="list-group list-group-striped">
|
||||
<li class="list-group-item">
|
||||
<svg-icon icon-class="user" />用户名称
|
||||
<svg-icon icon-class="user" />{{proxy.$t('common.userName')}}
|
||||
<div class="pull-right">{{ state.user.userName }}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<svg-icon icon-class="phone" />手机号码
|
||||
<svg-icon icon-class="phone" />{{proxy.$t('common.phoneNumber')}}
|
||||
<div class="pull-right">{{ state.user.phonenumber }}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<svg-icon icon-class="email" />用户邮箱
|
||||
<svg-icon icon-class="email" />{{proxy.$t('common.userEmail')}}
|
||||
<div class="pull-right">{{ state.user.email }}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<svg-icon icon-class="tree" />所属部门
|
||||
<svg-icon icon-class="tree" />{{proxy.$t('position.department')}}
|
||||
<div class="pull-right" v-if="state.user.dept">{{ state.user.dept.deptName }} / {{ state.postGroup }}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<svg-icon icon-class="peoples" />所属角色
|
||||
<svg-icon icon-class="peoples" />{{proxy.$t('common.role')}}
|
||||
<div class="pull-right">{{ state.roleGroup }}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<svg-icon icon-class="date" />创建日期
|
||||
<svg-icon icon-class="date" />{{proxy.$t('customer.createTime')}}
|
||||
<div class="pull-right">{{ state.user.createTime }}</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="18" :xs="24">
|
||||
<el-col :span="16" :xs="24">
|
||||
<el-card>
|
||||
<template v-slot:header>
|
||||
<div class="clearfix">
|
||||
<span>基本资料</span>
|
||||
<span>{{proxy.$t('common.baseInfo')}}</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-tabs v-model="activeTab">
|
||||
<el-tab-pane label="基本资料" name="userinfo">
|
||||
<el-tab-pane :label="proxy.$t('common.baseInfo')" name="userinfo">
|
||||
<userInfo :user="state.user" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="修改密码" name="resetPwd">
|
||||
<el-tab-pane :label="proxy.$t('common.editPassword')" name="resetPwd">
|
||||
<resetPwd />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
@ -67,7 +67,7 @@ import userAvatar from "./userAvatar";
|
||||
import userInfo from "./userInfo";
|
||||
import resetPwd from "./resetPwd";
|
||||
import { getUserProfile } from "@/api/system/user";
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const activeTab = ref("userinfo");
|
||||
const state = reactive({
|
||||
user: {},
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="user-info-head" @click="editCropper()">
|
||||
<img :src="options.img" title="点击上传头像" class="img-circle img-lg" />
|
||||
<el-dialog :title="title" v-model="open" width="800px" append-to-body @opened="modalOpened" @close="closeDialog">
|
||||
<el-dialog :title="proxy.$t('common.editAvatar')" v-model="open" width="800px" append-to-body @opened="modalOpened" @close="closeDialog">
|
||||
<el-row>
|
||||
<el-col :xs="24" :md="12" :style="{ height: '350px' }">
|
||||
<vue-cropper
|
||||
@ -33,7 +33,7 @@
|
||||
:before-upload="beforeUpload"
|
||||
>
|
||||
<el-button>
|
||||
选择
|
||||
{{proxy.$t('common.choose')}}
|
||||
<el-icon class="el-icon--right"><Upload /></el-icon>
|
||||
</el-button>
|
||||
</el-upload>
|
||||
@ -51,7 +51,7 @@
|
||||
<el-button icon="RefreshRight" @click="rotateRight()"></el-button>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 2, offset: 6 }" :md="2">
|
||||
<el-button type="primary" @click="uploadImg()">提 交</el-button>
|
||||
<el-button type="primary" @click="uploadImg()">{{proxy.$t('button.submit')}}</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-dialog>
|
||||
@ -69,7 +69,6 @@ const { proxy } = getCurrentInstance();
|
||||
|
||||
const open = ref(false);
|
||||
const visible = ref(false);
|
||||
const title = ref("修改头像");
|
||||
|
||||
//图片裁剪数据
|
||||
const options = reactive({
|
||||
@ -135,7 +134,7 @@ function uploadImg() {
|
||||
open.value = false;
|
||||
options.img = import.meta.env.VITE_APP_BASE_API + response.imgUrl;
|
||||
userStore.avatar = options.img;
|
||||
proxy.$modal.msgSuccess("修改成功");
|
||||
proxy.$modal.msgSuccess(proxy.$t('common.modifySuccess'));
|
||||
visible.value = false;
|
||||
});
|
||||
});
|
||||
|
@ -1,23 +1,23 @@
|
||||
<template>
|
||||
<el-form ref="userRef" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="用户昵称" prop="nickName">
|
||||
<el-form-item :label="proxy.$t('equipment.nickName')" prop="nickName">
|
||||
<el-input v-model="form.nickName" maxlength="30" />
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="phonenumber">
|
||||
<el-form-item :label="proxy.$t('common.phoneNumber')" prop="phonenumber">
|
||||
<el-input v-model="form.phonenumber" maxlength="11" />
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-form-item :label="proxy.$t('common.userEmail')" prop="email">
|
||||
<el-input v-model="form.email" maxlength="50" />
|
||||
</el-form-item>
|
||||
<el-form-item label="性别">
|
||||
<el-form-item :label="proxy.$t('common.sex')">
|
||||
<el-radio-group v-model="form.sex">
|
||||
<el-radio value="0">男</el-radio>
|
||||
<el-radio value="1">女</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submit">保存</el-button>
|
||||
<el-button type="danger" @click="close">关闭</el-button>
|
||||
<el-button type="primary" @click="submit">{{proxy.$t('button.save')}}</el-button>
|
||||
<el-button type="danger" @click="close">{{proxy.$t('button.cancel')}}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
@ -25,15 +25,16 @@ export default defineConfig(({ mode, command }) => {
|
||||
},
|
||||
// vite 相关配置
|
||||
server: {
|
||||
port: 80,
|
||||
port: 90,
|
||||
host: true,
|
||||
open: true,
|
||||
proxy: {
|
||||
// https://cn.vitejs.dev/config/#server-proxy
|
||||
'/dev-api': {
|
||||
target: 'http://localhost:8080',
|
||||
[env.VITE_APP_BASE_API]: {
|
||||
target: 'http://124.236.46.74:8104',
|
||||
// target: 'http://192.168.0.22:8111/',
|
||||
changeOrigin: true,
|
||||
rewrite: (p) => p.replace(/^\/dev-api/, '')
|
||||
// rewrite: (p) => p.replace(/^\/dev-api/, '')
|
||||
rewrite: (path) => path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '/api')
|
||||
}
|
||||
}
|
||||
},
|
||||
|
48
yarn.lock
48
yarn.lock
@ -67,6 +67,27 @@
|
||||
resolved "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz"
|
||||
integrity sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==
|
||||
|
||||
"@intlify/core-base@11.1.3":
|
||||
version "11.1.3"
|
||||
resolved "https://registry.npmmirror.com/@intlify/core-base/-/core-base-11.1.3.tgz"
|
||||
integrity sha512-cMuHunYO7LE80azTitcvEbs1KJmtd6g7I5pxlApV3Jo547zdO3h31/0uXpqHc+Y3RKt1wo2y68RGSx77Z1klyA==
|
||||
dependencies:
|
||||
"@intlify/message-compiler" "11.1.3"
|
||||
"@intlify/shared" "11.1.3"
|
||||
|
||||
"@intlify/message-compiler@11.1.3":
|
||||
version "11.1.3"
|
||||
resolved "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-11.1.3.tgz"
|
||||
integrity sha512-7rbqqpo2f5+tIcwZTAG/Ooy9C8NDVwfDkvSeDPWUPQW+Dyzfw2o9H103N5lKBxO7wxX9dgCDjQ8Umz73uYw3hw==
|
||||
dependencies:
|
||||
"@intlify/shared" "11.1.3"
|
||||
source-map-js "^1.0.2"
|
||||
|
||||
"@intlify/shared@11.1.3":
|
||||
version "11.1.3"
|
||||
resolved "https://registry.npmmirror.com/@intlify/shared/-/shared-11.1.3.tgz"
|
||||
integrity sha512-pTFBgqa/99JRA2H1qfyqv97MKWJrYngXBA/I0elZcYxvJgcCw3mApAoPW3mJ7vx3j+Ti0FyKUFZ4hWxdjKaxvA==
|
||||
|
||||
"@isaacs/cliui@^8.0.2":
|
||||
version "8.0.2"
|
||||
resolved "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz"
|
||||
@ -654,7 +675,7 @@ class-utils@^0.3.5:
|
||||
isobject "^3.0.0"
|
||||
static-extend "^0.1.1"
|
||||
|
||||
clipboard@2.0.11:
|
||||
clipboard@^2.0.6, clipboard@2.0.11:
|
||||
version "2.0.11"
|
||||
resolved "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz"
|
||||
integrity sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==
|
||||
@ -2841,7 +2862,7 @@ sortablejs@1.14.0:
|
||||
resolved "https://registry.npmjs.org/sortablejs/-/sortablejs-1.14.0.tgz"
|
||||
integrity sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==
|
||||
|
||||
source-map-js@^1.2.0, source-map-js@^1.2.1, "source-map-js@>=0.6.2 <2.0.0":
|
||||
source-map-js@^1.0.2, source-map-js@^1.2.0, source-map-js@^1.2.1, "source-map-js@>=0.6.2 <2.0.0":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz"
|
||||
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
|
||||
@ -3169,6 +3190,11 @@ typedarray.prototype.slice@^1.0.5:
|
||||
typed-array-buffer "^1.0.3"
|
||||
typed-array-byte-offset "^1.0.4"
|
||||
|
||||
typescript@*, typescript@^5.8.3, typescript@>=4.4.4:
|
||||
version "5.8.3"
|
||||
resolved "https://registry.npmmirror.com/typescript/-/typescript-5.8.3.tgz"
|
||||
integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==
|
||||
|
||||
ufo@^1.5.4:
|
||||
version "1.5.4"
|
||||
resolved "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz"
|
||||
@ -3317,6 +3343,13 @@ vite@^5.0.0, vite@>=2.0.0, vite@5.3.2:
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.3"
|
||||
|
||||
vue-clipboard3@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmmirror.com/vue-clipboard3/-/vue-clipboard3-2.0.0.tgz"
|
||||
integrity sha512-Q9S7dzWGax7LN5iiSPcu/K1GGm2gcBBlYwmMsUc5/16N6w90cbKow3FnPmPs95sungns4yvd9/+JhbAznECS2A==
|
||||
dependencies:
|
||||
clipboard "^2.0.6"
|
||||
|
||||
vue-cropper@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmjs.org/vue-cropper/-/vue-cropper-1.1.1.tgz"
|
||||
@ -3337,6 +3370,15 @@ vue-demi@>=0.14.8:
|
||||
resolved "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz"
|
||||
integrity sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==
|
||||
|
||||
vue-i18n@^11.1.3:
|
||||
version "11.1.3"
|
||||
resolved "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-11.1.3.tgz"
|
||||
integrity sha512-Pcylh9z9S5+CJAqgbRZ3EKxFIBIrtY5YUppU722GIT65+Nukm0TCqiQegZnNLCZkXGthxe0cpqj0AoM51H+6Gw==
|
||||
dependencies:
|
||||
"@intlify/core-base" "11.1.3"
|
||||
"@intlify/shared" "11.1.3"
|
||||
"@vue/devtools-api" "^6.5.0"
|
||||
|
||||
vue-router@4.4.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.npmjs.org/vue-router/-/vue-router-4.4.0.tgz"
|
||||
@ -3344,7 +3386,7 @@ vue-router@4.4.0:
|
||||
dependencies:
|
||||
"@vue/devtools-api" "^6.5.1"
|
||||
|
||||
"vue@^2.6.14 || ^3.3.0", "vue@^3.0.0-0 || ^2.6.0", vue@^3.0.1, vue@^3.2.0, vue@^3.2.25, vue@^3.2.41, vue@3.4.31:
|
||||
"vue@^2.6.14 || ^3.3.0", vue@^3.0.0, "vue@^3.0.0-0 || ^2.6.0", vue@^3.0.1, vue@^3.2.0, vue@^3.2.25, vue@^3.2.41, vue@3.4.31:
|
||||
version "3.4.31"
|
||||
resolved "https://registry.npmjs.org/vue/-/vue-3.4.31.tgz"
|
||||
integrity sha512-njqRrOy7W3YLAlVqSKpBebtZpDVg21FPoaq1I7f/+qqBThK9ChAIjkRWgeP6Eat+8C+iia4P3OYqpATP21BCoQ==
|
||||
|
Loading…
Reference in New Issue
Block a user