Compare commits

...

49 Commits

Author SHA1 Message Date
xiaojinfei 777b92535d Merge pull request 'dev-xjf' (#57) from dev-xjf into master 2 months ago
肖晋飞 072c699d54 fix:表格加loading增加用户体验 2 months ago
xjf 9fca3ac2f0 fix:修改监听卡片数据变化的bug 2 months ago
xiaojinfei 21d13a35ad Merge pull request 'fix:删除首页-分析页' (#56) from dev-xjf into master 2 months ago
肖晋飞 74592a7ec0 fix:删除首页-分析页 2 months ago
xiaojinfei 78b9b741b5 Merge pull request 'fix:解决父子组件通信问题' (#55) from dev-xjf into master 2 months ago
肖晋飞 1095dcb068 fix:解决父子组件通信问题 2 months ago
xiaojinfei 63a1fe18f2 Merge pull request 'dev-xjf' (#53) from dev-xjf into master 2 months ago
肖晋飞 5361462e35 fix:动图测试 2 months ago
肖晋飞 cda9974a3e fix:设备首页卡片样式修改 2 months ago
xiaojinfei 9c1790ac5a Merge pull request 'dev-xjf' (#52) from dev-xjf into master 2 months ago
肖晋飞 fa9d21e551 fix:换一种方法实现变色 2 months ago
肖晋飞 9ddf6f65d0 fix:解决系统不支持url传参 2 months ago
肖晋飞 c184e89de0 fix:解决系统不支持url传参 2 months ago
肖晋飞 200b108f68 fix:本地配置文件改为本地地址,不影响部署 2 months ago
肖晋飞 5dbfc3b835 fix:增加根据id读取配置文件显示页面功能 2 months ago
肖晋飞 c31fe17c03 fix:增加故障变色功能 2 months ago
xiaojinfei 8681b6401e Merge pull request 'fix:修复bug' (#51) from dev-xjf into master 2 months ago
xjf 477d3fa69b fix:修复bug 2 months ago
xiaojinfei c18552afb4 Merge pull request 'fix:更换数据' (#50) from dev-xjf into master 2 months ago
肖晋飞 da7b9e3902 fix:更换数据 2 months ago
xiaojinfei 26a43c058c Merge pull request 'dev-xjf' (#49) from dev-xjf into master 2 months ago
肖晋飞 3ea477968b fix:更换数据 2 months ago
肖晋飞 96e2be221a fix:更改设备展示布局 2 months ago
xiaojinfei 0d09bd2451 Merge pull request 'fix:图片路径问题' (#48) from dev-xjf into master 2 months ago
肖晋飞 6767579d02 fix:图片路径问题 2 months ago
xiaojinfei f65202518c Merge pull request 'fix:修复图片路径不显示' (#47) from dev-xjf into master 2 months ago
肖晋飞 28265bebdd fix:修复图片路径不显示 2 months ago
xiaojinfei 90f9ad64ae Merge pull request 'fix:修复图片动态引入' (#46) from dev-xjf into master 2 months ago
肖晋飞 38f744b699 fix:修复图片动态引入 2 months ago
xiaojinfei b2d2e298a7 Merge pull request 'dev-xjf' (#45) from dev-xjf into master 2 months ago
肖晋飞 9f7f685c59 feat:修改浮动按钮功能 2 months ago
肖晋飞 434e1da621 feat:设备样例展示demo 2 months ago
肖晋飞 ae8bdeb120 fix:更改大loading为正式logo 2 months ago
肖晋飞 69216ea7bb fix:更改loading大logo 2 months ago
肖晋飞 eb51a69a6d fix:运行中心-预警实例配置 2 months ago
肖晋飞 74053a529d Merge branch 'master' into dev-xjf 2 months ago
肖晋飞 71492d157b feat:预警回算 2 months ago
chenjiale 133a48dbce Merge pull request 'fix:修改按钮位置及样式' (#31) from dev-xjf into master 3 months ago
dreamglade 9e1170bbc8 Merge pull request 'Zyq' (#44) from Zyq into master 3 months ago
Dreamglade 4349a02db8 系统配置 3 months ago
肖晋飞 9a41177294 Merge remote-tracking branch 'origin/master' into dev-xjf 4 months ago
Dreamglade f6c4ee9011 feat: 同步 master 的 Dockerfile 和 pnpm-lock 配置 4 months ago
Dreamglade 93393410dd 小改一下 4 months ago
Dreamglade c73d294c6e 改了一下版本 4 months ago
Dreamglade 6126dc5f60 pnpm 10.14.0 4 months ago
chenjiale 7fe3d8fa38 Merge pull request 'cjl-dev' (#41) from cjl-dev into master 4 months ago
肖晋飞 a2a5fc9efa fix:将excel改为csv 5 months ago
肖晋飞 7b6f54edf3 fix:增加机组、系统、子系统筛选条件 5 months ago
  1. BIN
      public/111.gif
  2. BIN
      public/resource/img/logo.png
  3. BIN
      public/resource/img/logo_yudao.png
  4. 8
      src/api/device/index.ts
  5. 42
      src/api/system/config/SystemConfig.ts
  6. BIN
      src/assets/111.gif
  7. 3
      src/components/Application/src/AppLogo.vue
  8. 14
      src/components/Page/src/PageWrapper.vue
  9. 108
      src/design/theme.less
  10. 1
      src/enums/appEnum.ts
  11. 14
      src/layouts/default/feature/index.vue
  12. 4
      src/locales/lang/en/action.json
  13. 3
      src/locales/lang/zh-CN/action.json
  14. 22
      src/router/routes/modules/dashboard.ts
  15. 1
      src/utils/file/download.ts
  16. 59
      src/views/dashboard/demo/components/DynamicInfo.vue
  17. 76
      src/views/dashboard/demo/components/Operation.vue
  18. 103
      src/views/dashboard/demo/components/ParameterInfo.vue
  19. 103
      src/views/dashboard/demo/components/ProjectCard.vue
  20. 16
      src/views/dashboard/demo/components/QuickNav.vue
  21. 91
      src/views/dashboard/demo/components/SaleRadar.vue
  22. 43
      src/views/dashboard/demo/components/WorkbenchHeader.vue
  23. 156
      src/views/dashboard/demo/components/data.ts
  24. 89
      src/views/dashboard/demo/components/demo.json
  25. 138
      src/views/dashboard/demo/components/demo_old.json
  26. 36
      src/views/dashboard/demo/components/staticImg.vue
  27. 68
      src/views/dashboard/demo/index copy.vue
  28. 95
      src/views/dashboard/demo/index.vue
  29. 13
      src/views/exa/config/CreateBatchModal.vue
  30. 188
      src/views/run/calc/calc.data.ts
  31. 185
      src/views/run/calc/index.vue
  32. 6
      src/views/run/instant/index.vue
  33. 80
      src/views/run/instant/instant.data.ts
  34. 0
      src/views/run/instant/warnConfig/UpdateModal.vue
  35. 0
      src/views/run/instant/warnConfig/index.vue
  36. 0
      src/views/run/instant/warnConfig/warn.data.ts
  37. 47
      src/views/system/config/SystemConfig/SystemConfig.ts
  38. 66
      src/views/system/config/SystemConfig/SystemConfigModal.vue
  39. 85
      src/views/system/config/SystemConfig/index.vue
  40. 24
      src/views/system/user/user.data.ts

BIN
public/111.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

BIN
public/resource/img/logo.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 136 KiB

BIN
public/resource/img/logo_yudao.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

8
src/api/device/index.ts

@ -0,0 +1,8 @@
import { defHttp } from '@/utils/http/axios'
// 获得地区树
export function getDeviceInfo(id: string) {
return defHttp.get({ url: `/device/info?id=${id}` })
}
// 获得 IP 对应的地区名

42
src/api/system/config/SystemConfig.ts

@ -0,0 +1,42 @@
import { defHttp } from '@/utils/http/axios'
/**
* @description: VO
*/
export interface SystemConfigVO {
id?: number
name: string
abbreviation?: string
}
/**
* @description:
*/
export interface SystemConfigPageReqVO {
name?: string
}
// 查询系统配置分页列表
export function getSystemConfigPage(params: SystemConfigPageReqV) {
return defHttp.get({ url: '/system/system-config/page', params })
}
// 获取系统配置详情
export function getSystemConfig(id: number) {
return defHttp.get({ url: `/system/system-config/get?id=${id}` })
}
// 新增系统配置
export function createSystemConfig(data: SystemConfigVO) {
return defHttp.post({ url: '/system/system-config/create', data })
}
// 修改系统配置
export function updateSystemConfig(data: SystemConfigVO) {
return defHttp.put({ url: '/system/system-config/update', data })
}
// 删除系统配置
export function deleteSystemConfig(id: number) {
return defHttp.delete({ url: `/system/system-config/delete?id=${id}` })
}

BIN
src/assets/111.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

3
src/components/Application/src/AppLogo.vue

@ -49,7 +49,8 @@ function goHome() {
.@{prefix-cls} {
display: flex;
align-items: center;
padding-left: 7px;
justify-content: center;
padding-left: 7px;
cursor: pointer;
transition: all 0.2s ease;

14
src/components/Page/src/PageWrapper.vue

@ -127,8 +127,10 @@ watch(
<template>
<div ref="wrapperRef" :class="getClass">
<PageHeader
v-if="getShowHeader" v-bind="omit($attrs, 'class')" ref="headerRef" :style="getHeaderStyle"
style="margin: 1rem; border-radius: 1rem;" :ghost="ghost" :title="title"
v-if="getShowHeader"
v-bind="omit($attrs, 'class')"
ref="headerRef" :style="getHeaderStyle"
style="padding:6px 8px;" :ghost="ghost" :title="title"
>
<template #default>
<template v-if="content">
@ -163,14 +165,14 @@ watch(
position: relative;
.@{prefix-cls}-content {
margin: 1rem;
border-radius: 1rem;
margin: 0.1rem;
border-radius: 0.5rem;
}
.ant-page-header {
&:empty {
margin: 1rem;
border-radius: 1rem;
margin: 0.1rem;
border-radius: 0.5rem;
}
}

108
src/design/theme.less

@ -60,3 +60,111 @@ html[data-theme="dark"] {
color: rgb(0 0 0 / 90%);
}
}
.workbench[data-theme="dark"] {
--text-color: rgb(255 255 255 0.85);
--component-background: rgb(36 37 37 75%);
--border-color: #b6b7b9;
color: rgb(255 255 255 0.85);
background: rgb(36 37 37 75%);
border-color: #b6b7b9;
.xingyuv-basic-table .ant-table-wrapper {
color: rgb(255 255 255 0.85);
background: rgb(36 37 37 75%);
}
.ant-table-wrapper table {
color: rgb(255 255 255 0.85);
background: rgb(36 37 37 75%);
}
.ant-table-wrapper .ant-table-thead > tr > td {
color: rgb(255 255 255 0.85);
background: rgb(36 37 37 75%);
}
.ant-table-container {
color: rgb(255 255 255 0.85);
background: rgb(36 37 37 75%);
}
.ant-table-wrapper .ant-table .ant-table-header {
color: rgb(255 255 255 0.85);
background: rgb(36 37 37 75%);
}
.ant-table-wrapper .ant-table.ant-table-small {
color: rgb(255 255 255 0.85);
background: rgb(36 37 37 75%);
}
.ant-card-head-title {
color: rgb(255 255 255 0.85);
}
.ant-card-body {
color: rgb(255 255 255 0.85);
}
.ant-list-split .ant-list-item {
border-block-end: 1px solid #8b949e;
}
.ant-list .ant-list-item .ant-list-item-meta .ant-list-item-meta-title {
color: rgb(255 255 255 0.85);
}
.ant-list .ant-list-item .ant-list-item-meta .ant-list-item-meta-description {
color: rgb(255 255 255 0.85);
}
.ant-comment .ant-comment-content-author-name {
color: #8b949e;
}
.ant-comment .ant-comment-content-author-name > * {
color: #8b949e;
}
.ant-comment .ant-comment-content-author-name > *:hover {
color: #8b949e;
}
.ant-comment .ant-comment-content-author-time {
color: #8b949e;
}
.ant-list .ant-list-item {
color: rgb(255 255 255 0.85);
}
.text-secondary {
color: #8b949e;
}
.ant-card-grid-hoverable:hover {
box-shadow:
0 3px 6px -4px rgb(0 0 0 / 48%),
0 6px 16px 0 rgb(0 0 0 / 32%),
0 9px 28px 8px rgb(0 0 0 / 20%);
}
.ant-card-grid {
box-shadow:
1px 0 0 0 #434343,
0 1px 0 0 #434343,
1px 1px 0 0 #434343,
1px 0 0 0 #434343 inset,
0 1px 0 0 #434343 inset;
}
.ant-calendar-selected-day .ant-calendar-date {
color: rgb(0 0 0 / 80%);
}
.ant-select-tree li .ant-select-tree-node-content-wrapper.ant-select-tree-node-selected {
color: rgb(0 0 0 / 90%);
}
}

1
src/enums/appEnum.ts

@ -73,5 +73,6 @@ export enum IconEnum {
ADDS = 'ant-design:plus-circle-outlined',
CHANGE = 'ant-design:one-to-one-outlined',
WARN = 'ant-design:warning-outlined',
BACK_CALC = 'ant-design:backward-outlined',
}

14
src/layouts/default/feature/index.vue

@ -1,5 +1,5 @@
<script lang="ts" setup>
import { FloatButton } from 'ant-design-vue'
import { FloatButton, notification } from 'ant-design-vue'
import { QuestionCircleOutlined } from '@ant-design/icons-vue'
import { computed, unref } from 'vue'
@ -36,6 +36,16 @@ const getIsFixedSettingDrawer = computed(() => {
return settingButtonPosition === SettingButtonPositionEnum.FIXED
})
function openNotification() {
notification.open({
message: '信息提示',
description: '该功能开发中,敬请期待!',
onClick: () => {
console.log('Notification Clicked!')
},
})
}
</script>
<template>
@ -46,7 +56,7 @@ const getIsFixedSettingDrawer = computed(() => {
type="primary"
:badge="{ dot: true }"
:style="{ right: '64px' }"
@click="openWindow(SITE_URL)"
@click="openNotification()"
>
<template #icon>
<QuestionCircleOutlined />

4
src/locales/lang/en/action.json

@ -14,6 +14,6 @@
"createBatch": "CreateBatch",
"createInstant": "CreateInstant",
"pointConfig": "PointConfig",
"warnConfig": "WarnConfig"
"warnConfig": "WarnConfig",
"backCalc": "backCalc"
}

3
src/locales/lang/zh-CN/action.json

@ -15,5 +15,6 @@
"createBatch": "批量导入",
"createInstant": "新增实例",
"pointConfig": "测点配置",
"warnConfig": "预警配置"
"warnConfig": "预警配置",
"backCalc": "回算"
}

22
src/router/routes/modules/dashboard.ts

@ -8,23 +8,23 @@ const dashboard: AppRouteModule = {
name: 'Dashboard',
component: LAYOUT,
parentId: 0,
redirect: '/dashboard/analysis',
redirect: '/dashboard/workbench',
meta: {
orderNo: 10,
icon: 'ant-design:home-outlined',
title: t('routes.dashboard.dashboard'),
},
children: [
{
path: 'analysis',
name: 'Analysis',
component: () => import('@/views/dashboard/analysis/index.vue'),
meta: {
// affix: true,
title: t('routes.dashboard.analysis'),
icon: 'ant-design:bar-chart-outlined',
},
},
// {
// path: 'analysis',
// name: 'Analysis',
// component: () => import('@/views/dashboard/analysis/index.vue'),
// meta: {
// // affix: true,
// title: t('routes.dashboard.analysis'),
// icon: 'ant-design:bar-chart-outlined',
// },
// },
{
path: 'workbench',
name: 'Workbench',

1
src/utils/file/download.ts

@ -36,6 +36,7 @@ export function downloadByBase64(buf: string, filename: string, mime?: string, b
export function downloadByData(data: BlobPart, filename: string, mime?: string, bom?: BlobPart) {
const blobData = typeof bom !== 'undefined' ? [bom, data] : [data]
const blob = new Blob(blobData, { type: mime || 'application/octet-stream' })
// const blob = new Blob(blobData, { type: 'text/csv;charset=utf-8' })
const blobURL = window.URL.createObjectURL(blob)
const tempLink = document.createElement('a')

59
src/views/dashboard/demo/components/DynamicInfo.vue

@ -0,0 +1,59 @@
<script lang="ts" setup>
import { toRefs, watch } from 'vue'
import { Card, List } from 'ant-design-vue'
const props = defineProps({
data: {
type: Object,
default: () => {},
},
})
watch(
() => props.data,
(newValue, oldValue) => {
//
console.log('a has changed', newValue, oldValue)
},
)
const ListItem = List.Item
const ListItemMeta = List.Item.Meta
</script>
<template>
<Card id="www" :title="props.data.Header" v-bind="$attrs" class-name="spcard">
<template #extra>
<a-button type="link">
更多
</a-button>
</template>
<List item-layout="horizontal" :data-source="props.data.Content">
<template #renderItem="{ item }">
<ListItem>
<ListItemMeta>
<template #description>
{{ item.StartTime }} {{ item.Duration }}
</template>
<!-- eslint-disable-next-line -->
<!-- <template #title> {{ item.Text }} <span v-html="item.desc"> </span> </template> -->
<template #title>
{{ item.Text }}
</template>
<!-- <template #avatar>
<Icon :icon="item.avatar" :size="30" />
</template> -->
</ListItemMeta>
</ListItem>
</template>
</List>
</Card>
</template>
<style scoped>
::v-deep(.ant-card-body) {
height:50%;
padding:10px;
overflow-y: auto;
}
</style>

76
src/views/dashboard/demo/components/Operation.vue

@ -0,0 +1,76 @@
<script lang="ts" setup>
import { Card, Comment, List, ListItem } from 'ant-design-vue'
import dayjs from 'dayjs'
import { watch } from 'vue'
import relativeTime from 'dayjs/plugin/relativeTime'
const props = defineProps({
data: {
type: Object,
default: () => {},
},
})
watch(
() => props.data,
(newValue, oldValue) => {
//
console.log('a has changed', newValue, oldValue)
},
)
dayjs.extend(relativeTime)
</script>
<template>
<Card :title="props.data.Header" v-bind="$attrs">
<template #extra>
<a-button type="link">
{{ props.data.Content.length || 0 }} 条指导建议
</a-button>
</template>
<List
class="comment-list"
item-layout="horizontal"
:data-source="props.data.Content"
>
<template #renderItem="{ item }">
<ListItem>
<Comment>
<template #author>
<a>{{ item.FaultDesc }}</a>
</template>
<template #content>
<p>
{{ item.Guide }}
</p>
</template>
<template #datetime>
<a-tooltip :title="dayjs().format('YYYY-MM-DD HH:mm:ss')">
<span>{{ dayjs().fromNow() }}</span>
</a-tooltip>
</template>
</Comment>
</ListItem>
</template>
</List>
</Card>
</template>
<style scoped>
::v-deep(.ant-card-body) {
height:70%;
padding:5px;
overflow-y: auto;
}
::v-deep(.ant-list-item) {
padding:5px;
}
::v-deep(.ant-list-header) {
padding:5px
}
::v-deep(.ant-comment-inner) {
padding: 5px 0
}
</style>

103
src/views/dashboard/demo/components/ParameterInfo.vue

@ -0,0 +1,103 @@
<script lang="ts" setup>
import { Card } from 'ant-design-vue'
import { onBeforeUpdate, onMounted, onUpdated, ref, toRefs, watch } from 'vue'
import { BasicTable, useTable } from '@/components/Table'
import { getExaNow } from '@/api/alert/exa'
const props = defineProps({
data: {
type: Object,
default: () => {},
},
})
const loading = ref(false)
watch(
() => props.data,
(newValue, oldValue) => {
//
console.log('a has changed', newValue, oldValue)
},
)
function getInterval(record: any) {
console.log(record)
if (record.predictValue === '-')
return '-'
record.predictValue = Number(record.predictValue).toFixed(2)
const threshold = record.ResidualThreshold
console.log(threshold)
const predictValue = record.predictValue || 0
return `[${Number(predictValue) - threshold},${Number(predictValue) + threshold}]`
}
const [registerTable, { setTableData }] = useTable({
dataSource: [],
rowKey: 'ID',
size: 'small',
immediate: false,
columns: props.data.Columns,
useSearchForm: false,
showTableSetting: false,
showIndexColumn: false,
})
//
onBeforeUpdate(async () => {
loading.value = true
try {
const rr = [] as any[]
const rows = props.data.Content || []
for (const item of rows) {
try {
// ObservedPoint PredictedPoint
const param = item.PredictedPoint ? `${item.ObservedPoint},${item.PredictedPoint}` : `${item.ObservedPoint}`
const res = await getExaNow(param)
let origin: any = '-'
let predict: any = '-'
if (res != null) {
if (Array.isArray(res)) {
origin = res[0] ?? '-'
predict = res[1] ?? '-'
}
else if (typeof res === 'object') {
origin = res.originValue ?? res[0] ?? '-'
predict = res.predictValue ?? res[1] ?? '-'
}
else {
origin = String(res)
}
}
rr.push(Object.assign({}, item, { originValue: origin, predictValue: predict }))
}
catch (e) {
}
}
setTableData(rr)
}
finally {
loading.value = false
}
})
</script>
<template>
<Card :title="props.data.Header" v-bind="$attrs">
<!-- <template #extra>
<a-button type="link">
更多
</a-button>
</template> -->
<BasicTable style="min-height:30vh" :loading="loading" @register="registerTable">
<template #threshold="{ record }">
<span>{{ getInterval(record) }}</span>
</template>
</BasicTable>
</Card>
</template>
<style scoped>
::v-deep(.ant-card-body) {
padding:5px;
}
</style>

103
src/views/dashboard/demo/components/ProjectCard.vue

@ -0,0 +1,103 @@
<script lang="ts" setup>
import { Card, CardGrid } from 'ant-design-vue'
import { onMounted, ref, watch } from 'vue'
import { getExaNow } from '@/api/alert/exa'
const props = defineProps({
data: {
type: Object,
default: () => {},
},
})
const gridData = ref<any>([])
watch(
() => props.data,
(newValue, oldValue) => {
//
console.log('a has changed', newValue, oldValue)
getNewData()
},
)
async function getNewData() {
const rows = props.data.Content || []
for (const item of rows) {
try {
const param = item.Point
const res = await getExaNow(param)
let ifColor: boolean = false
if (res != null)
ifColor = res === '1'
gridData.value.push(Object.assign({}, item, { ifColor }))
console.log(gridData.value)
}
catch (e) {
}
}
}
//
// onMounted(async () => {
// const rows = props.data.Content || []
// for (const item of rows) {
// try {
// const param = item.Point
// const res = await getExaNow(param)
// let ifColor: boolean = false
// if (res != null)
// ifColor = res === '1'
// gridData.value.push(Object.assign({}, item, { ifColor }))
// console.log(gridData.value)
// }
// catch (e) {
// }
// }
// })
</script>
<template>
<Card :title="props.data.Header" v-bind="$attrs">
<template #extra>
<a-button type="link">
更多
</a-button>
</template>
<CardGrid v-for="item in gridData" :key="item.Point" :class="{ 'bg-[#990000] bg-opacity-50': item.ifColor }" class="flex flex-col items-center justify-center !h-1/3 !w-full !md:w-1/4">
<!-- <Icon :icon="item.icon" :color="item.color" size="30" /> -->
<div class="flex flex-col items-center justify-center">
<div class="text-center text-base">
{{ item.FaultDesc }}
</div>
<!-- <div class="text-secondary mt-1 h-10 flex">
{{ item.Point }}
</div> -->
</div>
<!-- <div class="text-secondary flex justify-between">
<span>{{ item.group }}</span>
<span>{{ item.date }}</span>
</div> -->
</CardGrid>
</Card>
</template>
<style scoped>
::v-deep(.ant-card-grid) {
padding:15px;
}
::v-deep(.ant-card-body) {
height:80%;
overflow-y: auto;
}
</style>

16
src/views/dashboard/demo/components/QuickNav.vue

@ -0,0 +1,16 @@
<script lang="ts" setup>
import { Card, CardGrid } from 'ant-design-vue'
import { navItems } from './data'
import { Icon } from '@/components/Icon'
</script>
<template>
<Card title="快捷导航">
<CardGrid v-for="item in navItems" :key="item.title">
<span class="flex flex-col items-center">
<Icon :icon="item.icon" :color="item.color" size="20" />
<span class="text-md mt-2 truncate">{{ item.title }}</span>
</span>
</CardGrid>
</Card>
</template>

91
src/views/dashboard/demo/components/SaleRadar.vue

@ -0,0 +1,91 @@
<script lang="ts" setup>
import type { Ref } from 'vue'
import { ref, watch } from 'vue'
import { Card } from 'ant-design-vue'
import { useECharts } from '@/hooks/web/useECharts'
import { propTypes } from '@/utils/propTypes'
const props = defineProps({
loading: Boolean,
width: propTypes.string.def('100%'),
height: propTypes.string.def('400px'),
})
const chartRef = ref<HTMLDivElement | null>(null)
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>)
watch(
() => props.loading,
() => {
if (props.loading)
return
setOptions({
legend: {
bottom: 0,
data: ['Visits', 'Sales'],
},
tooltip: {},
radar: {
radius: '60%',
splitNumber: 8,
indicator: [
{
name: '2017',
},
{
name: '2017',
},
{
name: '2018',
},
{
name: '2019',
},
{
name: '2020',
},
{
name: '2021',
},
],
},
series: [
{
type: 'radar',
symbolSize: 0,
areaStyle: {
shadowBlur: 0,
shadowColor: 'rgba(0,0,0,.2)',
shadowOffsetX: 0,
shadowOffsetY: 10,
opacity: 1,
},
data: [
{
value: [90, 50, 86, 40, 50, 20],
name: 'Visits',
itemStyle: {
color: '#b6a2de',
},
},
{
value: [70, 75, 70, 76, 20, 85],
name: 'Sales',
itemStyle: {
color: '#67e0e3',
},
},
],
},
],
})
},
{ immediate: true },
)
</script>
<template>
<Card title="销售统计" :loading="loading">
<div ref="chartRef" :style="{ width, height }" />
</Card>
</template>

43
src/views/dashboard/demo/components/WorkbenchHeader.vue

@ -0,0 +1,43 @@
<script lang="ts" setup>
import { Card } from 'ant-design-vue'
const props = defineProps({
data: {
type: Object,
default: () => {},
},
})
</script>
<template>
<!-- <Avatar :src="userinfo.user.avatar || headerImg" :size="72" class="!mx-auto !block" /> -->
<div class="mt-2 md:mt-0">
<h1 class="text-center text-lg md:text-2xl">
<!-- 早安, {{ userinfo.user.nickname }}, 开始您一天的工作吧 -->
{{ props.data.title }}
</h1>
<!-- <span class="text-secondary"> 今日晴20 - 32 </span> -->
<!-- </div> -->
<!-- <div class="mt-4 flex flex-1 justify-end md:mt-0">
<div class="flex flex-col justify-center text-right">
<span class="text-secondary"> 待办 </span>
<span class="text-2xl">2/10</span>
</div>
<div class="mx-12 flex flex-col justify-center text-right md:mx-16">
<span class="text-secondary"> 项目 </span>
<span class="text-2xl">8</span>
</div>
<div class="mr-4 flex flex-col justify-center text-right md:mr-10">
<span class="text-secondary"> 团队 </span>
<span class="text-2xl">300</span>
</div>
</div> -->
</div>
</template>
<style scoped>
::v-deep(.ant-card-grid) {
padding:15px;
}
</style>

156
src/views/dashboard/demo/components/data.ts

@ -0,0 +1,156 @@
interface GroupItem {
title: string
icon: string
color: string
desc: string
date: string
group: string
}
interface NavItem {
title: string
icon: string
color: string
}
interface DynamicInfoItem {
avatar: string
name: string
date: string
desc: string
}
export const navItems: NavItem[] = [
{
title: '首页',
icon: 'ion:home-outline',
color: '#1fdaca',
},
{
title: '仪表盘',
icon: 'ion:grid-outline',
color: '#bf0c2c',
},
{
title: '组件',
icon: 'ion:layers-outline',
color: '#e18525',
},
{
title: '系统管理',
icon: 'ion:settings-outline',
color: '#3fb27f',
},
{
title: '权限管理',
icon: 'ion:key-outline',
color: '#4daf1bc9',
},
{
title: '图表',
icon: 'ion:bar-chart-outline',
color: '#00d8ff',
},
]
export const dynamicInfoItems: DynamicInfoItem[] = [
{
avatar: 'dynamic-avatar-1|svg',
name: '威廉',
date: '刚刚',
desc: '在 <a>开源组</a> 创建了项目 <a>Vue</a>',
},
{
avatar: 'dynamic-avatar-2|svg',
name: '艾文',
date: '1个小时前',
desc: '关注了 <a>威廉</a> ',
},
{
avatar: 'dynamic-avatar-3|svg',
name: '克里斯',
date: '1天前',
desc: '发布了 <a>个人动态</a> ',
},
{
avatar: 'dynamic-avatar-4|svg',
name: 'XingyuV',
date: '2天前',
desc: '发表文章 <a>如何编写一个Vite插件</a> ',
},
{
avatar: 'dynamic-avatar-5|svg',
name: '皮特',
date: '3天前',
desc: '回复了 <a>杰克</a> 的问题 <a>如何进行项目优化?</a>',
},
{
avatar: 'dynamic-avatar-6|svg',
name: '杰克',
date: '1周前',
desc: '关闭了问题 <a>如何运行项目</a> ',
},
{
avatar: 'dynamic-avatar-1|svg',
name: '威廉',
date: '1周前',
desc: '发布了 <a>个人动态</a> ',
},
{
avatar: 'dynamic-avatar-1|svg',
name: '威廉',
date: '2021-04-01 20:00',
desc: '推送了代码到 <a>Github</a>',
},
]
export const groupItems: GroupItem[] = [
{
title: 'Github',
icon: 'carbon:logo-github',
color: '',
desc: '不要等待机会,而要创造机会。',
group: '开源组',
date: '2021-04-01',
},
{
title: 'Vue',
icon: 'ion:logo-vue',
color: '#3fb27f',
desc: '现在的你决定将来的你。',
group: '算法组',
date: '2021-04-01',
},
{
title: 'Html5',
icon: 'ion:logo-html5',
color: '#e18525',
desc: '没有什么才能比努力更重要。',
group: '上班摸鱼',
date: '2021-04-01',
},
{
title: 'Angular',
icon: 'ion:logo-angular',
color: '#bf0c2c',
desc: '热情和欲望可以突破一切难关。',
group: 'UI',
date: '2021-04-01',
},
{
title: 'React',
icon: 'bx:bxl-react',
color: '#00d8ff',
desc: '健康的身体是实现目标的基石。',
group: '技术牛',
date: '2021-04-01',
},
{
title: 'Js',
icon: 'ion:logo-javascript',
color: '#EBD94E',
desc: '路是走出来的,而不是空想出来的。',
group: '架构组',
date: '2021-04-01',
},
]

89
src/views/dashboard/demo/components/demo.json

@ -0,0 +1,89 @@
{
"title": "示例设备展示",
"LeftUp": {
"Header": "1设备静态图",
"path": "",
"Content": ""
},
"LeftMidUp": {
"Header": "2故障诊断",
"Content": [{ "FaultDesc": "失速", "Point": "YFJ_A_GZ001" }, { "FaultDesc": "喘振", "Point": "YFJ_A_GZ002" }, { "FaultDesc": "动调故障", "Point": "YFJ_A_GZ003" }, { "FaultDesc": "风机动调机构漏油", "Point": "YFJ_A_GZ004" }, { "FaultDesc": "风机动调伺服连杆轴脱开", "Point": "YFJ_A_GZ005" }, { "FaultDesc": "电机前轴(驱动端)承润滑异常", "Point": "YFJ_A_GZ006" }, { "FaultDesc": "电机后轴(非驱动端)承润滑异常", "Point": "YFJ_A_GZ007" }, { "FaultDesc": "风机润滑油冷却系统异常", "Point": "YFJ_A_GZ008" }, { "FaultDesc": "电机前轴(驱动端)承瓦面磨损", "Point": "YFJ_A_GZ009" }, { "FaultDesc": "电机后轴(非驱动端)承瓦面磨损", "Point": "YFJ_A_GZ010" }, { "FaultDesc": "轴承箱轴承(润滑)冷却异常", "Point": "YFJ_A_GZ011" }, { "FaultDesc": "轴承箱前轴(驱动端)承损坏", "Point": "YFJ_A_GZ012" }, { "FaultDesc": "轴承箱中轴(推力轴承)承损坏", "Point": "YFJ_A_GZ013" }, { "FaultDesc": "轴承箱后轴(非驱动端)承损坏", "Point": "YFJ_A_GZ014" }, { "FaultDesc": "油站油箱内油温异常", "Point": "YFJ_A_GZ015" }, { "FaultDesc": "油站油泵1泵体损坏", "Point": "YFJ_A_GZ016" }, { "FaultDesc": "油站油泵2泵体损坏", "Point": "YFJ_A_GZ017" }, { "FaultDesc": "油站控制油外漏", "Point": "YFJ_A_GZ018" }, { "FaultDesc": "油站润滑油外漏", "Point": "YFJ_A_GZ019" }, { "FaultDesc": "电动机电源一相断线", "Point": "YFJ_A_GZ020" }, { "FaultDesc": "电动机定子绕组匝间短路", "Point": "YFJ_A_GZ021" }, { "FaultDesc": "油站油泵1电动机电源一相断线", "Point": "YFJ_A_GZ022" }, { "FaultDesc": "油站油泵1电动机定子绕组匝间短路", "Point": "YFJ_A_GZ023" }, { "FaultDesc": "油站油泵2电动机电源一相断线", "Point": "YFJ_A_GZ024" }, { "FaultDesc": "油站油泵2电动机定子绕组匝间短路", "Point": "YFJ_A_GZ025" }, { "FaultDesc": "风机电动机冷却装置故障", "Point": "YFJ_A_GZ026" }, { "FaultDesc": "风机电动机电流异常变大", "Point": "YFJ_A_GZ027" }, { "FaultDesc": "油站油泵1电动机风扇坏", "Point": "YFJ_A_GZ028" }, { "FaultDesc": "油站油泵2电动机风扇坏", "Point": "YFJ_A_GZ029" }, { "FaultDesc": "油站油泵1电动机机械卡死", "Point": "YFJ_A_GZ030" }, { "FaultDesc": "油站油泵2电动机机械卡死", "Point": "YFJ_A_GZ031" }, { "FaultDesc": "油站油泵1电动机过载", "Point": "YFJ_A_GZ032" }, { "FaultDesc": "油站油泵2电动机过载", "Point": "YFJ_A_GZ033" }, { "FaultDesc": "油泵1和其电机靠背轮脱落", "Point": "YFJ_A_GZ034" }, { "FaultDesc": "油泵2和其电机靠背轮脱落", "Point": "YFJ_A_GZ035" }, { "FaultDesc": "冷却/密封风机1叶轮磨损", "Point": "YFJ_A_GZ036" }, { "FaultDesc": "冷却/密封风机2叶轮磨损", "Point": "YFJ_A_GZ037" }, { "FaultDesc": "油站控制油过滤器堵", "Point": "YFJ_A_GZ038" }, { "FaultDesc": "油站润滑油过滤器堵", "Point": "YFJ_A_GZ039" }
]
},
"RightMidUp": {
"Header": "报警记录",
"Content": [{
"Text": "故障1",
"StartTime": "Diagnosis1",
"Duration": 12.3
}, {
"Text": "故障1",
"StartTime": "Diagnosis1",
"Duration": 12.3
}]
},
"RightUp": {
"Header": "大模型-操作指导",
"Content": [{
"FaultDesc": "引风机抢风",
"Guide": "A、B引风机动调在自动位,将电流小的风机动调切手动,开大动调,引风机电流增加?A,另一台引风机电流自动减少至?A,直至A、B引风机电流稳定。同时检查并确认引出口至烟囱烟道上档板处“全开”位,引风机出口余热换热器无堵塞后,逐步增加负荷,将A、B引风机电流、动调调至正常范围,投入引风机自动。若预警仍存在或工况改变易发生抢风,将A、B引风机动调设置一定偏置运行,维持A、B引风机电流稳定运行,根据实际能力带负荷。利用机组停机机会,对引风机出口烟道进行全面检查,尽可能降低烟道阻力,改善流动特性。"
}, {
"FaultDesc": "故障2",
"Guide": "如何处理故障2"
}]
},
"LeftDown": {
"Header": "生产流程",
"path": "svg/illustration.svg"
},
"RightDown": {
"Header": "参数信息",
"Columns": [{
"title": "序号",
"dataIndex": "ID",
"width": 40
}, {
"title": "描述",
"dataIndex": "Desc",
"width": 200
}, {
"title": "点号",
"dataIndex": "ObservedPoint",
"width": 150
},
{
"title": "单位",
"dataIndex": "Unit",
"width": 40
}, {
"title": "测量值",
"dataIndex": "originValue",
"width": 65
}, {
"title": "预测值",
"dataIndex": "predictValue",
"width": 65
}, {
"title": "安全区域",
"dataIndex": "threshold",
"width": 100,
"slots": { "customRender": "threshold" }
}],
"Content": [{
"ID": 1,
"Desc": "A引风机电机前轴承温度1",
"ObservedPoint": "SYG1_10HNC10CT110",
"PredictedPoint": "SYG1_10HNC10CT110_CG",
"ResidualThreshold": 1.0,
"Unit": "℃"
}, {
"ID": 2,
"Desc": "A引风机电机前轴承温度2",
"ObservedPoint": "SYG1_10HNC10CT111",
"PredictedPoint": "SYG1_10HNC10CT111_CG",
"ResidualThreshold": 2.0,
"Unit": "℃"
}]
}
}

138
src/views/dashboard/demo/components/demo_old.json

@ -0,0 +1,138 @@
{
"title": "示例设备展示",
"LeftUp": {
"Header": "设备静态图",
"path": "svg/illustration.svg",
"Content": ""
},
"LeftMidUp": {
"Header": "故障诊断",
"Content": [{
"FaultDesc": "故障1故障1故障1故障1故障1",
"Point": "Diagnosis1"
}, {
"FaultDesc": "故障2",
"Point": "Diagnosis2"
},
{
"FaultDesc": "故障2",
"Point": "Diagnosis2"
}, {
"FaultDesc": "故障2",
"Point": "Diagnosis2"
}, {
"FaultDesc": "故障2",
"Point": "Diagnosis2"
}, {
"FaultDesc": "故障2",
"Point": "Diagnosis2"
}, {
"FaultDesc": "故障2",
"Point": "Diagnosis2"
}, {
"FaultDesc": "故障2",
"Point": "Diagnosis2"
}, {
"FaultDesc": "故障2",
"Point": "Diagnosis2"
}]
},
"RightMidUp": {
"Header": "报警记录",
"Content": [{
"Text": "故障1",
"StartTime": "Diagnosis1",
"Duration": 12.3
}, {
"Text": "故障1",
"StartTime": "Diagnosis1",
"Duration": 12.3
}]
},
"RightUp": {
"Header": "大模型-操作指导",
"Content": [{
"FaultDesc": "故障1",
"Guide": "如何处理故障1"
}, {
"FaultDesc": "故障2",
"Guide": "如何处理故障2"
}]
},
"LeftDown": {
"Header": "生产流程",
"path": "svg/illustration.svg",
"Content": "/api/v1/assets/production_flow.svg"
},
"RightDown": {
"Header": "参数信息",
"Columns": [{
"title": "序号",
"dataIndex": "序号",
"width": 40
}, {
"title": "描述",
"dataIndex": "描述",
"width": 200
}, {
"title": "点号",
"dataIndex": "点号",
"width": 100
}, {
"title": "测量值",
"dataIndex": "测量值",
"width": 80
}, {
"title": "预测值",
"dataIndex": "预测值",
"width": 100
}, {
"title": "安全区域",
"dataIndex": "安全区域",
"width": 140
}],
"Content": [{
"序号": 1,
"描述": "",
"点号": "",
"测量值": 0.0,
"预测值": 0.0,
"安全区域": ""
}, {
"序号": 2,
"描述": "",
"点号": "",
"测量值": 0.0,
"预测值": 0.0,
"安全区域": ""
}, {
"序号": 3,
"描述": "",
"点号": "",
"测量值": 0.0,
"预测值": 0.0,
"安全区域": ""
}, {
"序号": 4,
"描述": "",
"点号": "",
"测量值": 0.0,
"预测值": 0.0,
"安全区域": ""
}, {
"序号": 5,
"描述": "",
"点号": "",
"测量值": 0.0,
"预测值": 0.0,
"安全区域": ""
}, {
"序号": 6,
"描述": "",
"点号": "",
"测量值": 0.0,
"预测值": 0.0,
"安全区域": ""
}]
}
}

36
src/views/dashboard/demo/components/staticImg.vue

@ -0,0 +1,36 @@
<script lang="ts" setup>
import { Card, CardGrid } from 'ant-design-vue'
import { ref, toRefs, watch } from 'vue'
const props = defineProps({
data: {
type: Object,
default: () => {},
},
})
// const { data } = toRefs(props)
const imgUrl = ref('')
watch(
() => props.data,
(newValue, oldValue) => {
//
console.log('a has changed', newValue, oldValue)
// ../assets/imgs
// imgUrl.value = getAssetsFile()
// imgUrl.value = '/111.gif'
},
)
function getAssetsFile() {
const url = props.data.path
console.log(url)
return new URL(`../../../../assets/${url}`, import.meta.url).href
}
</script>
<template>
<Card class="enter-y !my-1" :title="props.data.Header">
<img class="mx-auto h-70 w-full" :src="props.data.path">
</Card>
</template>

68
src/views/dashboard/demo/index copy.vue

@ -0,0 +1,68 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue'
import WorkbenchHeader from './components/WorkbenchHeader.vue'
import ProjectCard from './components/ProjectCard.vue'
import DynamicInfo from './components/DynamicInfo.vue'
import StaticImg from './components/staticImg.vue'
import ParameterInfo from './components/ParameterInfo.vue'
import Operation from './components/Operation.vue'
import data from './components/demo.json'
import { PageWrapper } from '@/components/Page'
const loading = ref(true)
onMounted(() => {
const htmlRoot = document.getElementsByClassName('workbench')
// let theme = window.localStorage.getItem('__APP__DARK__MODE__')
const theme = 'dark'
if (htmlRoot && theme) {
for (let i = 0; i < htmlRoot.length; i++)
htmlRoot[i].setAttribute('data-theme', theme)
}
// theme = htmlRoot = null
})
setTimeout(() => {
loading.value = false
}, 500)
</script>
<template>
<div class="workbench m-1" data-theme="light">
<WorkbenchHeader class="workbench m-1" data-theme="light" :data="data" />
<div class="enter-y workbench h-92 md:flex" data-theme="light">
<StaticImg data-theme="light" class="workbench m-1 w-full md:w-1/4" :loading="loading" :data="data.LeftUp" />
<ProjectCard data-theme="light" class="enter-y workbench m-1 w-full md:w-1/2" :loading="loading" :data="data.LeftMidUp" />
<div class="enter-y w-full !mr-4 md:w-1/4">
<DynamicInfo :loading="loading" data-theme="light" class="workbench enter-y m-1 h-35 w-full" :data="data.RightMidUp" />
<Operation data-theme="light" class="workbench enter-y m-1 h-54 w-full" :loading="loading" :data="data.RightUp" />
</div>
</div>
<div class="enter-y workbench h-95 md:flex" data-theme="light">
<StaticImg data-theme="light" class="workbench m-1 w-full md:w-1/2" :loading="loading" :data="data.LeftDown" />
<ParameterInfo data-theme="light" class="workbench m-1 w-full md:w-1/2" :loading="loading" :data="data.RightDown" />
</div>
<!-- <div class="enter-y w-full !mr-4 lg:w-7/10">
<StaticImg :loading="loading" :data="data.LeftUp" class="enter-y" />
</div> -->
<!-- <div class="enter-y w-full !mr-4 lg:w-7/10">
<ProjectCard :loading="loading" class="enter-y" />
<DynamicInfo :loading="loading" class="enter-y !my-4" />
</div>
<div class="enter-y w-full lg:w-3/10">
<QuickNav :loading="loading" class="enter-y" />
<Card class="enter-y !my-4" :loading="loading">
<img class="mx-auto h-30 xl:h-50" src="@/assets/svg/illustration.svg">
</Card>
<SaleRadar :loading="loading" class="enter-y" />
</div> -->
</div>
</template>
<style lang="less">
</style>

95
src/views/dashboard/demo/index.vue

@ -0,0 +1,95 @@
<script lang="ts" setup>
import { onBeforeMount, onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import { message } from 'ant-design-vue'
import WorkbenchHeader from './components/WorkbenchHeader.vue'
import ProjectCard from './components/ProjectCard.vue'
import DynamicInfo from './components/DynamicInfo.vue'
import StaticImg from './components/staticImg.vue'
import ParameterInfo from './components/ParameterInfo.vue'
import Operation from './components/Operation.vue'
import data1 from './components/demo.json'
import { PageWrapper } from '@/components/Page'
import { getDeviceInfo } from '@/api/device'
import { getExaNow } from '@/api/alert/exa'
const loading = ref(true)
const route = useRoute()
setTimeout(() => {
loading.value = false
}, 500)
// idcommitbug
const id = route.path.split('/').pop() || ''
const data = ref<any>(data1)
onMounted(async () => {
console.log(route.path)
const res = await getDeviceInfo(id)
console.log(res)
if (res)
data.value = JSON.parse(res.Page_Content)
// getColor(data.value)
})
// async function getColor(data: any) {
// const rows = data.LeftMidUp.Content || []
// const gridData = [] as any[]
// for (const item of rows) {
// try {
// const param = item.Point
// const res = await getExaNow(param)
// let ifColor: boolean = false
// if (res != null)
// ifColor = res === '1'
// gridData.push(Object.assign({}, item, { ifColor }))
// console.log(gridData)
// }
// catch (e) {
// }
// }
// data.LeftMidUp.Content = gridData
// }
</script>
<template>
<PageWrapper v-if="id != null && id !== undefined" class="m-1">
<!-- <template #headerContent>
<WorkbenchHeader class="m-1" :data="data" />
</template> -->
<div class="h-[calc(91vh)] bg-[#f5f5f5]">
<div class="enter-y h-[calc(91vh/2)] md:flex">
<StaticImg class="m-1 w-full md:w-1/4" :loading="loading" :data="data.LeftUp || {}" />
<ProjectCard class="enter-y m-1 w-full md:w-1/2" :loading="loading" :data="data.LeftMidUp || {}" />
<div class="enter-y h-[calc(91vh/2)] w-full md:w-1/4">
<DynamicInfo :loading="loading" class="enter-y m-1 h-[calc(17vh)] w-full" :data="data.RightMidUp || {}" />
<Operation class="enter-y m-1 h-[calc(27vh)] w-full" :loading="loading" :data="data.RightUp || {}" />
</div>
</div>
<div class="enter-y h-[calc(91vh/2)] md:flex">
<StaticImg class="m-1 w-full md:w-1/2" :loading="loading" :data="data.LeftDown || {}" />
<ParameterInfo class="m-1 w-full md:w-1/2" :loading="loading" :data="data.RightDown || {}" />
</div>
<!-- <div class="enter-y w-full !mr-4 lg:w-7/10">
<StaticImg :loading="loading" :data="data.LeftUp" class="enter-y" />
</div> -->
<!-- <div class="enter-y w-full !mr-4 lg:w-7/10">
<ProjectCard :loading="loading" class="enter-y" />
<DynamicInfo :loading="loading" class="enter-y !my-4" />
</div>
<div class="enter-y w-full lg:w-3/10">
<QuickNav :loading="loading" class="enter-y" />
<Card class="enter-y !my-4" :loading="loading">
<img class="mx-auto h-30 xl:h-50" src="@/assets/svg/illustration.svg">
</Card>
<SaleRadar :loading="loading" class="enter-y" />
</div> -->
</div>
</pagewrapper>
</template>

13
src/views/exa/config/CreateBatchModal.vue

@ -40,11 +40,12 @@ function beforeUpload(file) {
//
const extension = file.name.split('.')[1] === 'xls'
const extension = file.name.split('.')[1] === 'csv'
const extension2 = file.name.split('.')[1] === 'xlsx'
// const extension2 = file.name.split('.')[1] === 'csv'
if (!extension && !extension2) {
// if (!extension && !extension2) {
if (!extension) {
createMessage.warning('导入文件只能是 xls、xlsx格式!')
fileList.value = []
console.log(fileList)
@ -74,7 +75,7 @@ function handleDrop(e: DragEvent) {
async function importPointTemplate() {
const data = await importTemplate()
downloadByData(data, 'EXA测点导入模板.xlsx')
downloadByData(data, 'EXA测点导入模板.csv')
}
async function handleSubmit() {
@ -138,7 +139,7 @@ async function handleSubmit() {
@register="registerCreateBatchModal" @ok="handleSubmit"
>
<UploadDragger
v-model:fileList="fileList" :max-count="1" accept=".xlsx,.xls" :multiple="false" :before-upload="beforeUpload"
v-model:fileList="fileList" :max-count="1" accept=".csv" :multiple="false" :before-upload="beforeUpload"
@change="handleChange" @drop="handleDrop"
>
<p class="ant-upload-drag-icon">
@ -148,7 +149,7 @@ async function handleSubmit() {
点击或拖拽到此区域实现上传
</p>
<p class="ant-upload-hint">
只支持上传单个excel文件,请尽量根据模板上传
只支持上传单个csv文件,请尽量根据模板上传
</p>
</UploadDragger>
<Divider><span style="color: #0B55A4;cursor: pointer" @click="importPointTemplate">下载模板</span></Divider>

188
src/views/run/calc/calc.data.ts

@ -0,0 +1,188 @@
import moment from 'moment'
import { ref } from 'vue'
import type { BasicColumn, FormSchema } from '@/components/Table'
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
import { optionListApi, subSystemListApi } from '@/api/alert/model/select'
import type { systemSelectParams } from '@/api/alert/model/model/optionsModel'
export const columns: BasicColumn[] = [
{
title: '编号',
dataIndex: 'id',
width: 80,
fixed: 'left',
},
{
title: '模型名称',
dataIndex: 'modelName',
width: 250,
className: 'instant',
slots: { customRender: 'detail' },
fixed: 'left',
},
{
title: '算法',
dataIndex: 'Algorithm',
width: 200,
},
{
title: '模式覆盖率',
dataIndex: 'CoveredPercent',
width: 200,
},
{
title: '报警次数',
dataIndex: 'AlarmNumber',
width: 200,
},
{
title: '总报警时间(m)',
dataIndex: 'AlarmToatlMinutes',
width: 200,
},
{
title: '系统维度',
dataIndex: 'Dimension',
width: 200,
},
{
title: '计算耗时(s)',
dataIndex: 'CalcSeconds',
width: 200,
},
{
title: '状态',
dataIndex: 'status',
width: 100,
slots: { customRender: 'status' },
fixed: 'right',
},
]
const optionList = await optionListApi()
const systemOptions = ref<any>([])
systemOptions.value = optionList.systems
export const searchFormSchema: FormSchema[] = [
{
label: '机组',
field: 'unit',
component: 'Select',
defaultValue: optionList.units[0].id || null,
colProps: { span: 4 },
componentProps: ({ formModel }) => {
return {
// xxxx props
allowClear: false,
placeholder: '请选择机组',
options: optionList.units.map(unit => ({ value: unit.id, label: unit.name })),
onChange: async (e: any) => {
// const { reload } = tableAction
// reload()
// or
console.log(e)
const param: systemSelectParams = {
unitId: e,
typeId: formModel.type,
}
systemOptions.value = await subSystemListApi(param)
formModel.system = systemOptions.value[0].id
},
}
},
},
{
label: '系统',
field: 'type',
component: 'Select',
defaultValue: optionList.types[0].id || null,
colProps: { span: 4 },
componentProps: ({ formModel }) => {
return {
allowClear: false,
placeholder: '请选择系统',
options: optionList.types.map(type => ({ value: type.id, label: type.name })),
onChange: async (e: any) => {
// const { reload } = tableAction
// reload()
// or
console.log(e)
const param: systemSelectParams = {
unitId: formModel.unit,
typeId: e,
}
systemOptions.value = await subSystemListApi(param)
},
}
},
},
{
label: '子系统',
field: 'system',
component: 'Select',
defaultValue: systemOptions.value[0].id || null,
colProps: { span: 4 },
componentProps: () => {
return {
allowClear: false,
placeholder: '请选择子系统',
options: systemOptions.value.map(system => ({ value: system.id, label: system.name })),
}
},
},
{
label: '模型名称',
field: 'modelName',
component: 'Input',
defaultValue: '',
componentProps: {
placeholder: '请输入模型名称',
},
colProps: { span: 4 },
},
]
export const calcFormSchemas: FormSchema[] = [
{
label: '回算时间',
field: 'time',
component: 'RangePicker',
componentProps: {
placeholder: ['开始时间', '结束时间'],
defaultValue: [moment().subtract(1, 'day').format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD HH:mm:ss')],
valueFormat: 'YYYY-MM-DD HH:mm:ss',
showTime: {
defaultValue: [moment().subtract(1, 'day').format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD HH:mm:ss')],
},
onChange: (e: any) => {
console.log(e)
},
},
required: true,
colProps: {
span: 8,
},
},
{
label: '回算采样周期',
field: 'interval',
component: 'Select',
defaultValue: 60,
componentProps: {
options: [{ value: 10, label: '10秒' }, { value: 60, label: '60秒' }, { value: 300, label: '300秒' }],
},
required: true,
colProps: {
span: 4,
},
},
]

185
src/views/run/calc/index.vue

@ -0,0 +1,185 @@
<script lang="ts" setup>
import { Badge, Button, Divider, Switch } from 'ant-design-vue'
import { onMounted, ref } from 'vue'
import moment from 'moment'
import HistoryModal from '../../exa/config/HistoryModal.vue'
import { calcFormSchemas, columns, searchFormSchema } from './calc.data'
import { BasicTable, TableAction, useTable } from '@/components/Table'
import { BasicForm, useForm } from '@/components/Form'
import { getInstantCount, getInstantPage, updateInstant } from '@/api/alert/run/instant'
import { getExaNow } from '@/api/alert/exa'
import { useI18n } from '@/hooks/web/useI18n'
import { router } from '@/router'
import { useMessage } from '@/hooks/web/useMessage'
import { IconEnum } from '@/enums/appEnum'
import { useModal } from '@/components/Modal'
defineOptions({ name: 'InstantCalc' })
const { createMessage } = useMessage()
const { t } = useI18n()
const [registerHistoryModal, { openModal: openHistoryModal }] = useModal()
const [registerCreateModal, { openModal: openCreateModal }] = useModal()
const [registerUpdateModal, { openModal: openUpdateModal }] = useModal()
const [registerTable, { getForm, reload, getDataSource, updateTableDataRecord }] = useTable({
title: '实例列表',
api: getInstantPage,
rowKey: 'id',
immediate: true,
columns,
formConfig: {
labelWidth: 70,
schemas: searchFormSchema,
showResetButton: false,
actionColOptions: {
span: 2,
},
},
useSearchForm: true,
showTableSetting: true,
showIndexColumn: false,
actionColumn: {
width: 100,
title: t('common.action'),
dataIndex: 'action',
fixed: 'right',
},
})
const isShow = ref<boolean>(false)
function handleCreate() {
openCreateModal(true, { isUpdate: false })
isShow.value = true
}
function handleEdit(record: Recordable) {
openUpdateModal(true, { record, isUpdate: true })
}
async function handleDelete(record: Recordable) {
await deleteRole(record.id)
createMessage.success(t('common.delSuccessText'))
reload()
}
const itemName = ref<string>()
const legendName = ref<string[]>()
function handleHistory(record: Recordable) {
itemName.value = (JSON.parse(record.instantInfo)).model_state
legendName.value = []
legendName.value.push(`${record.mpName}-${itemName.value}`)
openHistoryModal(true, { record })
}
function handleWarnConfig(record: Recordable) {
router.push(`/run/warnConfig?id=${record.id}`)
}
/**
* BasicForm绑定注册;
*/
const [registerForm] = useForm({
//
schemas: calcFormSchemas,
// false
// showAdvancedButton: true,
// // // 3
// autoAdvancedLine: 1,
// // // 1
// alwaysShowLines: 1,
layout: 'horizontal',
model: { time: [moment().subtract(1, 'day').format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD HH:mm:ss')] },
fieldMapToTime: [
// datastartTimeendTime
['time', ['startTime', 'endTime'], 'YYYY-MM-DD HH:mm:ss'],
],
actionColOptions: { span: 2 },
showSubmitButton: false,
showResetButton: false,
})
/**
* 点击提交按钮的value值----改为导出按钮
* @param values
*/
function handleSubmit(values: any) {
console.log('提交按钮数据::::', values)
}
//
function handleCalc(values: any) {
console.log('提交按钮数据::::', values)
}
</script>
<template>
<div>
<BasicTable @register="registerTable">
<template #form-formFooter>
<!-- <a-button v-auth="['run:instant:create']" type="primary" :pre-icon="IconEnum.ADD" @click="handleCreate">
{{ t('action.create') }}
</a-button> -->
<Divider orientation="left">
回算
</Divider>
<!-- 自定义表单 -->
<BasicForm @register="registerForm">
<template #advanceBefore>
<a-button v-auth="['run:instant:create']" type="primary" :pre-icon="IconEnum.ADD" @click="handleCreate">
{{ t('action.create') }}
</a-button>
</template>
</BasicForm>
</template>
<!-- 统计量点击跳转历史曲线 -->
<template #history="{ record }">
<a @click="handleHistory(record)">
{{ record.pointSte }}
</a>
<!-- <SlidersOutlined class="click-status" /> -->
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{ icon: IconEnum.BACK_CALC, label: t('action.backCalc'), onClick: handleWarnConfig.bind(null, record) },
]"
/>
</template>
</template>
</BasicTable>
<HistoryModal :item-name="itemName" :legend-name="legendName" @register="registerHistoryModal" />
<CreateModal :item-name="itemName" :legend-name="legendName" @register="registerCreateModal" @success="reload" />
<UpdateModal @register="registerUpdateModal" @success="reload" />
</div>
</template>
<style lang="less" scoped>
:deep(.instant) {
font-weight: bold;
color: #0B55A4
}
:deep(.alarm .ant-badge-status-dot) {
animation: flash 1s linear infinite;
}
@keyframes flash {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.runningStatus {
cursor: pointer;
}
</style>

6
src/views/run/instant/index.vue

@ -31,7 +31,7 @@ const [registerTable, { getForm, reload, getDataSource, updateTableDataRecord }]
immediate: true,
columns,
formConfig: {
labelWidth: 120,
labelWidth: 80,
schemas: searchFormSchema,
showResetButton: false,
actionColOptions: {
@ -96,6 +96,8 @@ const countData = ref<countObj>()
onMounted(async () => {
countData.value = await getInstantCount()
console.log(countData.value)
const { setFieldsValue } = getForm()
await setFieldsValue({ system: null })
getNow()
})
async function getTableData(type) {
@ -134,7 +136,7 @@ async function getNow() {
}
function handleWarnConfig(record: Recordable) {
router.push(`/run/warnConfig?id=${record.id}`)
router.push(`/run/instant/warn/config?id=${record.id}`)
}
</script>

80
src/views/run/instant/instant.data.ts

@ -1,8 +1,11 @@
import moment from 'moment'
import { left } from 'inquirer/lib/utils/readline'
import { ref } from 'vue'
import type { BasicColumn, FormSchema } from '@/components/Table'
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
import { getModelVersionList } from '@/api/alert/run/model/index'
import { optionListApi, subSystemListApi } from '@/api/alert/model/select'
import type { systemSelectParams } from '@/api/alert/model/model/optionsModel'
export const columns: BasicColumn[] = [
{
@ -90,14 +93,87 @@ export const columns: BasicColumn[] = [
]
const optionList = await optionListApi()
const systemOptions = ref<any>([])
systemOptions.value = optionList.systems
export const searchFormSchema: FormSchema[] = [
{
label: '机组',
field: 'unit',
component: 'Select',
defaultValue: optionList.units[0].id || null,
colProps: { span: 4 },
componentProps: ({ schema, tableAction, formActionType, formModel }) => {
return {
// xxxx props
allowClear: false,
placeholder: '请选择机组',
options: optionList.units.map(unit => ({ value: unit.id, label: unit.name })),
onChange: async (e: any) => {
// const { reload } = tableAction
// reload()
// or
console.log(e)
const param: systemSelectParams = {
unitId: e,
typeId: formModel.type,
}
systemOptions.value = await subSystemListApi(param)
formModel.system = systemOptions.value[0].id
},
}
},
},
{
label: '系统',
field: 'type',
component: 'Select',
defaultValue: optionList.types[0].id || null,
colProps: { span: 4 },
componentProps: ({ formModel }) => {
return {
allowClear: false,
placeholder: '请选择系统',
options: optionList.types.map(type => ({ value: type.id, label: type.name })),
onChange: async (e: any) => {
// const { reload } = tableAction
// reload()
// or
console.log(e)
const param: systemSelectParams = {
unitId: formModel.unit,
typeId: e,
}
systemOptions.value = await subSystemListApi(param)
},
}
},
},
{
label: '子系统',
field: 'system',
component: 'Select',
defaultValue: systemOptions.value[0].id || null,
colProps: { span: 4 },
componentProps: () => {
return {
allowClear: false,
placeholder: '请选择子系统',
options: systemOptions.value.map(system => ({ value: system.id, label: system.name })),
}
},
},
{
label: '模型实例名称',
field: 'mpName',
component: 'Input',
labelWidth: 120,
defaultValue: '',
required: true,
colProps: { span: 8 },
colProps: { span: 5 },
},
{
label: '状态1',

0
src/views/warn/UpdateModal.vue → src/views/run/instant/warnConfig/UpdateModal.vue

0
src/views/warn/index.vue → src/views/run/instant/warnConfig/index.vue

0
src/views/warn/warn.data.ts → src/views/run/instant/warnConfig/warn.data.ts

47
src/views/system/config/SystemConfig/SystemConfig.ts

@ -0,0 +1,47 @@
import { h } from 'vue'
import { Tag } from 'ant-design-vue'
import type { BasicColumn, FormSchema } from '@/components/Table'
// 表格列定义
export const columns: BasicColumn[] = [
{
title: '系统名称',
dataIndex: 'name',
width: 180,
},
{
title: '系统简称',
dataIndex: 'abbreviation',
width: 180,
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
// 使用 Vben 框架的方式格式化时间
customRender: ({ text }) => {
return h(Tag, { color: 'blue' }, () => text)
},
},
]
// 弹窗表单定义
export const formSchema: FormSchema[] = [
{
label: '编号',
field: 'id',
show: false, // 隐藏,仅用于表单绑定
component: 'Input',
},
{
label: '系统名称',
field: 'name',
required: true,
component: 'Input',
},
{
label: '系统简称',
field: 'abbreviation',
component: 'Input',
},
]

66
src/views/system/config/SystemConfig/SystemConfigModal.vue

@ -0,0 +1,66 @@
<script lang="ts" setup>
import { ref, unref } from 'vue'
import { formSchema } from './SystemConfig'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { BasicForm, useForm } from '@/components/Form'
import { BasicModal, useModalInner } from '@/components/Modal'
import { createSystemConfig, getSystemConfig, updateSystemConfig } from '@/api/system/config/SystemConfig'
import type { SystemConfigVO } from '@/api/system/config/SystemConfig'
defineOptions({ name: 'SystemConfigModal' })
const emit = defineEmits(['success', 'register'])
const { t } = useI18n()
const { createMessage } = useMessage()
const isUpdate = ref(true)
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
labelWidth: 120,
baseColProps: { span: 24 },
schemas: formSchema,
showActionButtonGroup: false,
actionColOptions: { span: 23 },
})
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
resetFields()
setModalProps({ confirmLoading: false })
isUpdate.value = !!data?.isUpdate
if (unref(isUpdate)) {
//
const res = await getSystemConfig(data.record.id)
setFieldsValue({ ...res })
}
})
async function handleSubmit() {
try {
const values = await validate()
setModalProps({ confirmLoading: true })
if (unref(isUpdate))
await updateSystemConfig(values as SystemConfigVO)
else
await createSystemConfig(values as SystemConfigVO)
closeModal()
emit('success')
createMessage.success(t('common.saveSuccessText'))
}
finally {
setModalProps({ confirmLoading: false })
}
}
</script>
<template>
<BasicModal
v-bind="$attrs"
:title="isUpdate ? t('action.edit') : t('action.create')"
@register="registerModal"
@ok="handleSubmit"
>
<BasicForm @register="registerForm" />
</BasicModal>
</template>

85
src/views/system/config/SystemConfig/index.vue

@ -0,0 +1,85 @@
<script lang="ts" setup>
import SystemConfigModal from './SystemConfigModal.vue'
import { columns } from './SystemConfig'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { useModal } from '@/components/Modal'
import { IconEnum } from '@/enums/appEnum'
import { BasicTable, TableAction, useTable } from '@/components/Table'
import { deleteSystemConfig, getSystemConfigPage } from '@/api/system/config/SystemConfig'
defineOptions({ name: 'SystemConfig' })
const { t } = useI18n()
const { createMessage } = useMessage()
const [registerModal, { openModal }] = useModal()
const [registerTable, { reload }] = useTable({
title: '系统列表',
api: getSystemConfigPage, // API
columns,
rowKey: 'id',
pagination: true,
useSearchForm: false, // 使 true
bordered: true,
showIndexColumn: true,
actionColumn: {
width: 140,
title: t('common.action'),
dataIndex: 'action',
fixed: 'right',
},
})
function handleCreate() {
openModal(true, { isUpdate: false })
}
function handleEdit(record: Recordable) {
openModal(true, { record, isUpdate: true })
}
async function handleDelete(record: Recordable) {
await deleteSystemConfig(record.id)
createMessage.success(t('common.delSuccessText'))
reload() //
}
</script>
<template>
<div>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button v-auth="'system:system-config:create'" type="primary" :pre-icon="IconEnum.ADD" @click="handleCreate">
{{ t('action.create') }}
</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{
icon: IconEnum.EDIT,
label: t('action.edit'),
auth: 'system:system-config:update', //
onClick: handleEdit.bind(null, record),
},
{
icon: IconEnum.DELETE,
danger: true,
label: t('action.delete'),
auth: 'system:system-config:delete', //
popConfirm: {
title: t('common.delMessage'),
placement: 'left',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</template>
</BasicTable>
<SystemConfigModal @register="registerModal" @success="reload()" />
</div>
</template>

24
src/views/system/user/user.data.ts

@ -182,18 +182,18 @@ export const formSchema: FormSchema[] = [
options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX),
},
},
{
label: '岗位',
field: 'postIds',
component: 'ApiSelect',
defaultValue: [],
componentProps: {
api: () => listSimplePosts(),
labelField: 'name',
valueField: 'id',
mode: 'tags',
},
},
// {
// label: '岗位',
// field: 'postIds',
// component: 'ApiSelect',
// defaultValue: [],
// componentProps: {
// api: () => listSimplePosts(),
// labelField: 'name',
// valueField: 'id',
// mode: 'tags',
// },
// },
{
label: '状态',
field: 'status',

Loading…
Cancel
Save