联调过程监督 电能质量管理

This commit is contained in:
GGJ
2024-04-09 16:52:11 +08:00
parent ec6dec3eef
commit 926112d2a7
26 changed files with 5460 additions and 861 deletions

View File

@@ -15,11 +15,16 @@
"@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12",
"axios": "^1.6.2",
"bpmn-js": "^7.3.1",
"bpmn-js-properties-panel": "^0.37.2",
"bpmn-moddle": "^6.0.0",
"camunda-bpmn-moddle": "^4.5.0",
"crypto-js": "^4.2.0",
"diagram-js-minimap": "^2.0.4",
"echarts": "^5.4.3",
"echarts-gl": "^2.0.9",
"echarts4": "npm:echarts@^4.9.0",
"element-plus": "^2.5.3",
"element-plus": "2.5.3",
"exceljs": "^4.4.0",
"file-saver": "^2.0.5",
"html2canvas": "^1.4.1",

1685
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,7 @@ export function exportModel(data: any) {
return createAxios({
url: '/harmonic-boot/exportmodel/exportModel',
method: 'post',
data: data
data: data,
responseType: 'blob'
})
}

View File

@@ -0,0 +1,15 @@
import zh_cn from "./zh";
export default function customTranslate(template: string, replacements: { [key: string]: string }) {
replacements = replacements || {};
// Translate
for (const [key1, value] of Object.entries(zh_cn)) {
if (key1 === template) {
template = value || template;
return template.replace(/{([^}]+)}/g, function (_: string, key: string) {
return replacements[key] || "{" + key + "}";
});
}
}
return template;
}

141
src/assets/bpmn/zh.ts Normal file
View File

@@ -0,0 +1,141 @@
// 百度翻译的,可自行优化
const zh_cn = {
"Activate hand tool": "激活手动工具",
"Activate lasso tool": "激活套索工具",
"Activate create/remove space tool": "激活创建/删除空间工具",
"Activate global connect tool": "激活全局连接工具",
"Create start event": "创建开始事件",
"Create intermediate/boundary event": "创建中间/边界事件",
"Create end event": "创建结束事件",
"Create gateway": "创建网关",
"Create task": "创建任务",
"Create data object reference": "创建数据对象引用",
"Create data store reference": "创建数据存储引用",
"Create expanded sub-process": "创建扩展子进程",
"Create pool/participant": "创建池/参与者",
"Create group": "创建组",
General: "一般",
Documentation: "描述",
Compensation: "补偿",
Error: "错误",
Link: "链接",
Message: "留言",
"Multi-instance": "多实例",
Signal: "信号",
Escalation: "升级",
Timer: "定时器",
"History cleanup": "历史清理",
Tasklist: "任务列表",
"Candidate starter": "候选人启动器",
Implementation: "实施",
"External task": "外部任务",
"Process variables": "过程变量",
Errors: "错误",
"User assignment": "用户分配",
Forms: "表格",
"Form fields": "表格字段",
"Task listeners": "任务侦听器",
"Start initiator": "启动启动器",
Script: "脚本",
Condition: "条件",
"Called element": "调用元素",
"Asynchronous continuations": "异步延续",
"Job execution": "工作执行",
"In mapping propagation": "在映射传播中",
"In mappings": "在映射中",
Inputs: "输入",
"Connector inputs": "连接器输入",
"Out mapping propagation": "出映射传播",
"Out mappings": "输出映射",
Outputs: "输出",
"Connector outputs": "连接器输出",
"Execution listeners": "执行侦听器",
"Extension properties": "扩展属性",
"Field injections": "现场注射",
"Business key": "业务密钥",
Name: "名称",
ID: "ID",
"Version tag": "版本标签",
Executable: "可执行文件",
"Element documentation": "元素描述",
"Time to live": "生活时间",
Startable: "启动",
"Candidate starter groups": "候选起始组",
"Specify more than one group as a comma separated list.": "将多个组指定为逗号分隔的列表。",
"Candidate starter users": "候选入门用户",
"Specify more than one user as a comma separated list.": "将多个用户指定为逗号分隔的列表。",
Priority: "优先级",
"ID must be unique.": "ID必须是唯一的。",
Value: "价值",
"Event type": "事件类型",
start: "开始",
end: "结束",
"Listener type": "侦听器类型",
"Java class": "Java类",
Expression: "表达式",
Type: "类型",
"Condition Expression": "类型",
"Delegate expression": "委托表达式",
"Field injection": "现场注射",
"Append end event": "追加结束事件",
"Append gateway": "追加网关",
"Append task": "追加任务",
"Append intermediate/boundary event": "附加中间/边界事件",
"User task": "用户任务",
"Service task": "服务任务",
"Send task": "发送任务",
"Receive task": "接收任务",
"Manual task": "手动任务",
"Business rule task": "业务规则任务",
"Script task": "脚本任务",
"Call activity": "通话活动",
"Sub-process (collapsed)": "子进程(折叠)",
"Sub-process (expanded)": "子进程(扩展)",
"Parallel multi-instance": "并行多实例",
"Sequential multi-instance": "顺序多实例",
Loop: "循环",
"Change element": "更改元素",
"Add text annotation": "添加文本注释",
"Connect to other element": "连接到其他元素",
Delete: "删除",
Before: "之前",
After: "之后",
"Process variable name": "过程变量名",
"Assignment type": "分配类型",
List: "列表",
Map: "地图",
"String or expression": "字符串或表达式",
'Start typing "${}" to create an expression.': '开始键入 "${}" 以创建表达式。',
"Local variable name": "局部变量名",
"Created in": "创建于",
Transaction: "交易",
"Event sub-process": "事件子进程",
"Ad-hoc": "临时",
"Data object reference": "数据对象参考",
"Connect using data input association": "使用数据输入关联连接",
"Data store reference": "数据存储参考",
Collection: "收藏",
"Exclusive gateway": "排他网关",
"Parallel gateway": "并行网关",
"Inclusive gateway": "包容网关",
"Complex gateway": "复杂网关",
"Event-based gateway": "基于事件的网关",
Assignee: "代理人",
"Candidate groups": "候选群体",
"Candidate users": "候选用户",
"Due date": "截止日期",
"Follow up date": "跟进日期",
"The due date as an EL expression (e.g. ${someDate}) or an ISO date (e.g. 2015-06-26T09:54:00).":
"截止日期作为EL表达式例如${Date}或国际标准化组织日期例如2015-06-26T09:54:00。",
"The follow up date as an EL expression (e.g. ${someDate}) or an ISO date (e.g. 2015-06-26T09:54:00).":
"跟进日期作为EL表达式例如${Date}或国际标准化组织日期例如2015-06-26T09:54:00。",
"Loop cardinality": "循环基数",
"Completion condition": "完井条件",
"Element variable": "元素变量",
"Asynchronous before": "异步前",
"Asynchronous after": "后异步",
Exclusive: "独家",
"Retry time cycle": "重试时间周期",
};
export default zh_cn;

View File

@@ -130,8 +130,9 @@
}
}
.el-select {
min-width: 220px;
.el-select,
.el-input {
min-width: 200px;
}
.el-tabs__content {

View File

@@ -43,7 +43,8 @@
.vxe-table--render-default .is--checked.vxe-cell--checkbox,
.vxe-table--render-default .is--checked.vxe-cell--checkbox .vxe-checkbox--icon,
.vxe-table--render-default .is--indeterminate.vxe-cell--checkbox,
.vxe-table--render-default .is--indeterminate.vxe-cell--checkbox .vxe-checkbox--icon {
.vxe-table--render-default .is--indeterminate.vxe-cell--checkbox .vxe-checkbox--icon,
.vxe-table--render-default .is--checked.vxe-cell--radio .vxe-radio--icon {
color: var(--el-color-primary-light-3);
}
@@ -51,6 +52,11 @@
.vxe-custom--checkbox-option:not(.is--disabled):hover .vxe-checkbox--icon,
.vxe-export--panel-column-option:not(.is--disabled):hover .vxe-checkbox--icon,
.vxe-table--filter-option:not(.is--disabled):hover .vxe-checkbox--icon,
.vxe-table--render-default .vxe-cell--checkbox:not(.is--disabled):hover .vxe-checkbox--icon {
color: var(--el-color-primary-light-8);
.vxe-table--render-default .vxe-cell--checkbox:not(.is--disabled):hover .vxe-checkbox--icon,
.vxe-radio:not(.is--disabled):hover .vxe-radio--icon,
.vxe-custom--radio-option:not(.is--disabled):hover .vxe-radio--icon,
.vxe-export--panel-column-option:not(.is--disabled):hover .vxe-radio--icon,
.vxe-table--filter-option:not(.is--disabled):hover .vxe-radio--icon,
.vxe-table--render-default .vxe-cell--radio:not(.is--disabled):hover .vxe-radio--icon {
color: var(--el-color-primary-light-5);
}

View File

@@ -96,8 +96,10 @@ function createAxios<Data = any, T = ApiPromise<Data>>(
response.data.code === 'A0000' ||
response.data.type === 'application/json' ||
Array.isArray(response.data) ||
response.data.type === 'application/octet-stream' ||
response.data.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
response.data.size
// ||
// response.data.type === 'application/octet-stream' ||
// response.data.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
) {
return options.reductDataFormat ? response.data : response
} else if (response.data.code == 'A0202') {

View File

@@ -0,0 +1,129 @@
<!--业务用户管理界面-->
<template>
<div class="default-main">
<TableHeader datePicker area>
<template v-slot:select>
<el-form-item label="筛选数据">
<el-input v-model="tableStore.table.params.searchValue" clearable placeholder="筛选数据" />
</el-form-item>
</template>
<template v-slot:operation>
<el-button type="primary" @click="exportEvent" class="ml10" icon="el-icon-Download">导出</el-button>
</template>
</TableHeader>
<!--表格-->
<Table ref="tableRef"></Table>
</div>
</template>
<script setup lang="ts">
import { ElMessage } from 'element-plus'
import TableStore from '@/utils/tableStore'
import Table from '@/components/table/index.vue'
import TableHeader from '@/components/table/header/index.vue'
import { onMounted, provide, ref } from 'vue'
import { useDictData } from '@/stores/dictData'
import { pageTable } from '@/api/harmonic-boot/luckyexcel.ts'
defineOptions({
name: 'harmonic-boot/reate/word'
})
const dictData = useDictData()
//区域联级选择
const industry = dictData.getBasicData('Interference_Source')
//用户信息弹出框
const tableRef = ref()
const tableStore = new TableStore({
url: '/harmonic-boot/qualifiedReport/pageTable',
method: 'POST',
column: [
{
title: '序号',
width: 60,
formatter: (row: any) => {
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
}
},
{ title: '变电站', field: 'subName', width: 200 },
{ title: '监测点名称', field: 'lineName', width: 200 },
{
title: '行业类型',
field: 'businessType',
formatter: (row: any) => {
return industry.find((item: any) => item.id == row.cellValue)?.name || '/'
}
},
{
title: '分类等级',
field: 'calssificationGrade',
formatter: (row: any) => {
return row.cellValue || '/'
}
},
{ title: '电压等级', field: 'voltageScale' },
{
title: '上级变电站',
field: 'superiorsSubstation',
formatter: (row: any) => {
return row.cellValue || '/'
}
},
{
title: '挂接线路',
field: 'hangLine',
formatter: (row: any) => {
return row.cellValue || '/'
}
},
{
title: 'PT变比',
field: 'pt',
formatter: (row: any) => {
return row.row.pt1 + '/' + row.row.pt2
}
},
{
title: 'CT变比',
field: 'ct',
formatter: (row: any) => {
return row.row.ct1 + '/' + row.row.ct2
}
},
{ title: '短路容量(MVA)', field: 'shortCapacity' },
{ title: '设备容量(MVA)', field: 'deviceCapacity' },
{ title: '协议容量(MVA)', field: 'dealCapacity' },
{ title: '谐波情况', field: 'harmDes' },
{ title: '电能质量情况', field: 'powerDes' }
],
beforeSearchFun: () => {
tableStore.table.params.beginTime = tableStore.table.params.startTime
tableStore.table.params.deptId = tableStore.table.params.deptIndex
}
})
onMounted(() => {
// 加载数据
tableStore.index()
})
tableStore.table.params.searchValue = ''
provide('tableStore', tableStore)
// 导出
const exportEvent = () => {
let form = JSON.parse(JSON.stringify(tableStore.table.params))
form.pageNum = 1
form.pageSize = tableStore.table.total
pageTable(form).then(res => {
tableRef.value.getRef().exportData({
filename: '合格率报告', // 文件名字
sheetName: 'Sheet1',
type: 'xlsx', //导出文件类型 xlsx 和 csv
useStyle: true,
data: res.data.records, // 数据源 // 过滤那个字段导出
columnFilterMethod: function (column: any) {
return !(column.$columnIndex === 0)
}
})
})
}
</script>

View File

@@ -23,7 +23,7 @@
:show-file-list="false"
ref="uploadRef"
action=""
:limit="1"
accept=".png,.jpg"
:on-change="choose"
:auto-upload="false"
>
@@ -47,6 +47,7 @@
</splitpanes>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref, provide } from 'vue'
import 'splitpanes/dist/splitpanes.css'
@@ -59,7 +60,7 @@ import { mainHeight } from '@/utils/layout'
import { exportModel } from '@/api/process-boot/reportForms'
import { genFileId, ElMessage } from 'element-plus'
defineOptions({
name: 'harmonic-boot/xieboReport'
name: 'harmonic-boot/report/word'
})
const height = mainHeight(20)
const size = ref(0)
@@ -90,7 +91,8 @@ const handleNodeClick = (data: any, node: any) => {
}
// 上传
const choose = (files: any) => {
uploadList.value - files
uploadList.value = files
ElMessage.success('上传成功')
}
// 生成
const exportEvent = () => {
@@ -106,13 +108,14 @@ const exportEvent = () => {
form.append('type', '0')
form.append('startTime', tableStore.table.params.startTime)
form.append('endTime', tableStore.table.params.endTime)
dotList.value.forEach(item => {
form.append('file', item)
})
// uploadList.value.forEach(item => {
// form.append('file', uploadList.value?.raw)
// })
exportModel(form).then((res: any) => {
let blob = new Blob([res], {
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document;charset=UTF-8'
type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document;charset=UTF-8",
})
// createObjectURL(blob); //创建下载的链接
const url = window.URL.createObjectURL(blob)

View File

@@ -22,7 +22,7 @@
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<div>
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="nextStep">下一步</el-button>
</div>
@@ -48,11 +48,17 @@
<el-button type="primary" icon="el-icon-Search" @click="searchOnLine">查询</el-button>
</el-form-item>
</el-form>
<vxe-table v-bind="defaultAttribute" v-loading="isLoading1" height="350" ref="xTableRef" :data="onlineAddData">
<vxe-column type="checkbox" width="60"></vxe-column>
<vxe-table v-bind="defaultAttribute" v-loading="isLoading1" height="350" ref="xTable1Ref" :data="onlineAddData">
<vxe-column type="radio" width="60"></vxe-column>
<vxe-column field="sustationName" title="变电站"></vxe-column>
<vxe-column field="barName" title="母线"></vxe-column>
<vxe-column field="measurementPointName" title="监测点名称"></vxe-column>
<vxe-column field="alarmType" title="告警类型" :formatter="formatter"></vxe-column>
<vxe-column field="overLimitrate" title="越跟天级占以">
<vxe-column field="measurementPointId" title="监测点编号"></vxe-column>
<vxe-column field="loadType" title="监测对象类型"></vxe-column>
<vxe-column field="objName" title="监测对象"></vxe-column>
<vxe-column field="voltageLevel" title="电压等级"></vxe-column>
<vxe-column field="harmonicType" title="告警类型" :formatter="formatter"></vxe-column>
<vxe-column field="overLimitrate" title="越线天数占比">
<template #default="{ row }">
<el-dropdown>
<span class="el-dropdown-link">
@@ -116,16 +122,16 @@
v-bind="defaultAttribute"
v-loading="isLoading2"
height="350"
ref="xTableRef"
ref="xTable2Ref"
:data="ordinaryAddData"
>
<vxe-column type="checkbox" width="60"></vxe-column>
<vxe-column field="voltageLevel" title="变电站电压等级(kV)"></vxe-column>
<vxe-column field="subName" title="变电站名称" :formatter="formatter"></vxe-column>
<vxe-column type="radio" width="60"></vxe-column>
<vxe-column field="subName" title="变电站名称"></vxe-column>
<vxe-column field="voltageLevelName" title="变电站电压等级"></vxe-column>
</vxe-table>
<el-divider content-position="left">第三步 选择问题指标</el-divider>
<el-form :model="ordinaryA" :rules="rules" ref="GeneralSurvey" :inline="true" class="form">
<el-form :model="ordinaryA" :rules="rules" ref="ordinaryARef" :inline="true" class="form">
<el-form-item label="稳态指标" prop="steadyState">
<el-checkbox-group v-model="ordinaryA.steadyState">
<el-checkbox v-for="(item, ind) in steadyStateList" :key="ind" :label="item.code">
@@ -208,45 +214,14 @@
<!-- 新增第二步(用户投诉问题新增) -->
<el-dialog draggable title="用户投诉问题新增" v-model="userAdd" width="1200px" :before-close="handleClose">
<el-divider content-position="left">第二步 选择投诉用户</el-divider>
<el-form :model="userA" :inline="true" :rules="rules">
<el-form-item label="用户类型:">
<el-select v-model="userA.userType" placeholder="请选择用户类型">
<el-option
v-for="item in userTypeData"
:key="item.code"
:label="item.name"
:value="item.code"
></el-option>
</el-select>
<el-form :model="userAdddata" :inline="true" ref="userAddRef" :rules="rules">
<el-form-item label="用户名称:" prop="userName">
<el-input v-model="userAdddata.userName" clearable placeholder="请输入用户名称"></el-input>
</el-form-item>
<el-form-item label="用户编号:">
<el-input v-model="userA.id" clearable placeholder="请输入用户编号"></el-input>
<el-form-item label="用户编号:" prop="userNo">
<el-input v-model="userAdddata.userNo" clearable placeholder="请输入用户编号"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-Search" @click="searchFnComplain">查询</el-button>
</el-form-item>
<vxe-table
v-bind="userAddDataList"
v-loading="isLoading3"
height="350"
ref="xTableRef"
:data="ordinaryAddData"
>
<vxe-column type="checkbox" width="60"></vxe-column>
<vxe-column field="id" title="用户编号"></vxe-column>
<vxe-column field="name" title="用户名称"></vxe-column>
<vxe-column
v-if="showUserType"
field="electricityType"
title="用电类别/电源类别"
:formatter="formatter"
></vxe-column>
<vxe-column v-else field="powerCategory" title="用电类别/电源类别" :formatter="formatter"></vxe-column>
</vxe-table>
<el-divider content-position="left">第三步 填写投诉详情</el-divider>
<el-form-item label="投诉内容:" style="margin-top: 10px" prop="complaintContent">
@@ -264,7 +239,7 @@
v-model="userAdddata.complaintTime"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
value-format="YYYY-MM-DD"
></el-date-picker>
</el-form-item>
@@ -311,9 +286,10 @@ import {
getRMpPartHarmonicDetail,
querySurveyPlanName,
querySurveyPlanOnQuestion,
getGenerateElectricityUserList,
getPowerUtilizationUserList,
addAbnormalIssues
addAbnormalIssues,
addComplaintIssues,
addGeneralSurveyIssues,
addExcessiveIssues
} from '@/api/process-boot/electricitymanagement'
import { ElMessage } from 'element-plus'
const steadyStateList = dictData.getBasicData('Steady_Indicator')
@@ -323,15 +299,16 @@ const add = ref(true)
const onlineAdd = ref(false)
const isLoading1 = ref(false)
const isLoading2 = ref(false)
const isLoading3 = ref(false)
const showUserType = ref(false)
const ordinaryAdd = ref(false)
const userAdd = ref(false)
const operationAdd = ref(false)
const ruleForm = ref()
const DateRef = ref()
const userAddRef = ref()
const operationAddRef = ref()
const xTableRef = ref()
const ordinaryARef = ref()
const xTable1Ref = ref()
const xTable2Ref = ref()
const harmonicTypes = ref([]) //稳态指标id集合
const harmonicTypesList = dictData.getBasicData('Indicator_Type')
const rate = ref(60) //越线天数占比(一个月)
@@ -366,14 +343,17 @@ const userAdddata = ref({
complaintContent: '',
complaintTime: '',
steadyState: [],
userName: '',
userNo: '',
transientIndicators: []
})
const planNameList: any = ref([])
const onlineAddData = ref([])
const userAddDataList = ref([])
const ordinaryAddData = ref([])
const rules = {
problemName: [{ required: true, message: '请输入问题名称', trigger: 'blur' }],
userName: [{ required: true, message: '请输入用户名称', trigger: 'blur' }],
userNo: [{ required: true, message: '请输入用户编号', trigger: 'blur' }],
complaintContent: [{ required: true, message: '请输入投诉内容', trigger: 'blur' }],
complaintTime: [{ required: true, message: '请选择时间', trigger: 'change' }],
problemSources: [{ required: true, message: '情选择问题来源', trigger: 'change' }],
@@ -416,7 +396,6 @@ const nextStep = () => {
} else if (addData.value.problemSources == 'User_Complaints') {
add.value = false
userAdd.value = true
searchFnComplain()
} else if (addData.value.problemSources == 'Dev_Exception') {
add.value = false
operationAdd.value = true
@@ -435,7 +414,19 @@ const previouStep = () => {
// 在线监测超标问题新增
const OnlineMonitoring = () => {
// xTableRef.value.getCheckboxRecords()
if (xTable1Ref.value.getRadioRecord() == null) {
return ElMessage.warning('请选择变电站')
} else {
addExcessiveIssues({
warnTarget:xTable1Ref.value.getRadioRecord().harmonicType,
...xTable1Ref.value.getRadioRecord(),
...addData.value
}).then((res: any) => {
ElMessage.success('新增成功!')
emit('handleClose')
emit('onSubmit')
})
}
}
// 在线监测告警查询
const searchOnLine = () => {
@@ -468,16 +459,33 @@ const searchFnExcessive = () => {
})
}
// 普测超标生成问题
const GeneralSurvey = () => {}
const GeneralSurvey = () => {
if (xTable2Ref.value.getRadioRecord() == null) {
return ElMessage.warning('请选择变电站')
} else {
ordinaryARef.value.validate((valid: any) => {
if (valid) {
addGeneralSurveyIssues({
substationId: xTable2Ref.value.getRadioRecord().subId,
...xTable2Ref.value.getRadioRecord(),
...ordinaryA.value,
...addData.value
}).then((res: any) => {
ElMessage.success('新增成功!')
emit('handleClose')
emit('onSubmit')
})
}
})
}
}
const formatter = (row: any) => {
if (row.column.field == 'alarmType') {
return alarmList.filter((item: any) => item.id == row.cellValue)[0]?.name
if (row.column.field == 'harmonicType') {
return harmonicTypesList.filter((item: any) => item.id == row.cellValue)[0]?.name
} else if (row.column.field == 'voltageLevel') {
return Voltage.filter((item: any) => item.id == row.cellValue)[0]?.name
} else if (row.column.field == 'electricityType') {
return dictData.getBasicData('Ele_Class').filter((item: any) => item.id == row.cellValue)[0]?.name
} else if (row.column.field == 'powerCategory') {
return dictData.getBasicData('Power_Category').filter((item: any) => item.id == row.cellValue)[0]?.name
} else {
return row.cellValue
}
@@ -489,35 +497,30 @@ const generateAbnormal = () => {
addAbnormalIssues({
...operationAdddata.value,
...addData.value
})
}).then((res: any) => {
ElMessage.success('新增成功!')
emit('handleClose')
emit('onSubmit')
})
}
})
}
//用户投诉问题
const searchFnComplain = () => {
isLoading3.value = true
userAddDataList.value = []
if (userA.value.userType == 'Elec_User') {
// 发电用户
showUserType.value = false
getGenerateElectricityUserList({ id: userA.value.id }).then(res => {
userAddDataList.value = res.data
})
} else {
showUserType.value = true
// 用电用户
getPowerUtilizationUserList({ id: userA.value.id }).then(res => {
userAddDataList.value = res.data
})
}
isLoading3.value = false
}
// 用户投诉新增
const userAddProblem = () => {}
const userAddProblem = () => {
userAddRef.value.validate((valid: any) => {
if (valid) {
addComplaintIssues({
...userAdddata.value,
...addData.value
}).then((res: any) => {
ElMessage.success('新增成功!')
emit('handleClose')
emit('onSubmit')
})
}
})
}
onMounted(() => {})
</script>
<style lang="scss" scoped>

View File

@@ -2,23 +2,21 @@
<el-dialog draggable title="详情" v-model="dialogVisible" width="1400px" :before-close="handleClose">
<el-tabs v-model="activeName" type="card">
<el-tab-pane label="指标" name="1">
<el-divider content-position="left" style="font-size: 18px; font-weight: bolder">
问题基本信息
</el-divider>
<el-form :model="addData" :inline="true" disabled label-width="auto">
<el-divider content-position="left" style="font-size: 18px">问题基本信息</el-divider>
<el-form :model="addData" :inline="true" disabled label-width="100px">
<el-form-item label="所属单位:">
<el-input v-model="addData.orgName" clearable placeholder="请填写"></el-input>
<el-input v-model="addData.orgName" placeholder="请填写"></el-input>
</el-form-item>
<el-form-item label="问题名称:">
<el-input v-model="addData.problemName" clearable placeholder="请填写"></el-input>
<el-input v-model="addData.problemName" placeholder="请填写"></el-input>
</el-form-item>
<el-form-item label="问题编号:">
<el-input v-model="addData.powerQualityProblemNo" clearable placeholder="请填写"></el-input>
<el-input v-model="addData.powerQualityProblemNo" placeholder="请填写"></el-input>
</el-form-item>
<el-form-item label="问题来源:">
<el-select v-model="addData.problemSources" placeholder="请选择">
<el-select v-model="addData.problemSources">
<el-option
v-for="item in problemData"
:key="item.code"
@@ -27,15 +25,118 @@
></el-option>
</el-select>
</el-form-item>
<br />
</el-form>
<!-- 在线监测超标问题 -->
<el-form :inline="true" label-width="100px" disabled v-if="problemSources == '在线监测告警'">
<el-form-item label="变电站:">
<el-input v-model="addData.sustationName"></el-input>
</el-form-item>
<el-form-item label="母线:">
<el-input v-model="addData.barName"></el-input>
</el-form-item>
<el-form-item label="监测点名称:">
<el-input v-model="addData.measurementPointName"></el-input>
</el-form-item>
<el-form-item label="监测点编号:">
<el-input v-model="addData.measurementPointId"></el-input>
</el-form-item>
<el-form-item label="监测对象类型:">
<el-input v-model="addData.loadType"></el-input>
</el-form-item>
<el-form-item label="监测对象:">
<el-input v-model="addData.objName"></el-input>
</el-form-item>
<el-form-item label="电压等级:">
<el-input v-model="addData.voltageLevel"></el-input>
</el-form-item>
</el-form>
<!-- 普测超标 -->
<el-form :inline="true" label-width="100px" disabled v-if="problemSources == '普测超标'">
<el-form-item label="普测计划名称:">
<el-input v-model="addData.planName" placeholder="请填写"></el-input>
</el-form-item>
<el-form-item label="所属变电站:">
<el-input v-model="addData.substationName" placeholder="请填写"></el-input>
</el-form-item>
<el-form-item label="母线名称:">
<el-input v-model="addData.busBarName" placeholder="请填写"></el-input>
</el-form-item>
<el-divider content-position="left" style="font-size: 18px; font-weight: bolder">
问题指标
</el-divider>
<el-form-item label="稳态指标:">
<el-checkbox-group v-model="addData.steadyIndicator">
<el-checkbox v-for="(item, ind) in steadyIndicatorList" :key="ind" :label="item.code">
{{ item.name }}
</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="暂态指标:">
<el-checkbox-group v-model="addData.transientIndicators">
<el-checkbox v-for="(item, ind) in transientIndicatorsList" :key="ind" :label="item.code">
{{ item.name }}
</el-checkbox>
</el-checkbox-group>
</el-form-item>
</el-form>
<!-- 用户投诉 -->
<el-form :inline="true" label-width="100px" disabled v-if="problemSources == '用户投诉'">
<el-form-item label="用户投诉名称:">
<el-input v-model="addData.userName" placeholder="请填写"></el-input>
</el-form-item>
<el-form-item label="投诉用户编号:">
<el-input v-model="addData.userNo" placeholder="请填写"></el-input>
</el-form-item>
<el-form-item label="投诉时间:">
<el-input v-model="addData.complaintTime" placeholder="请填写"></el-input>
</el-form-item>
<el-form-item label="投诉内容:">
<el-input
type="textarea"
:autosize="{ minRows: 2, maxRows: 4 }"
style="width: 400px"
placeholder="请输入内容"
v-model="addData.complaintContent"
></el-input>
</el-form-item>
<el-divider content-position="left" style="font-size: 18px; font-weight: bolder">
问题指标
</el-divider>
<el-form-item label="稳态指标:">
<el-checkbox-group v-model="addData.steadyIndicator">
<el-checkbox v-for="(item, ind) in steadyIndicatorList" :key="ind" :label="item.code">
{{ item.name }}
</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="暂态指标:">
<el-checkbox-group v-model="addData.transientIndicators">
<el-checkbox v-for="(item, ind) in transientIndicatorsList" :key="ind" :label="item.code">
{{ item.name }}
</el-checkbox>
</el-checkbox-group>
</el-form-item>
</el-form>
<!-- 设备异常 -->
<el-form :inline="true" label-width="auto" disabled v-if="problemSources == '设备异常'">
<el-form-item label="异常设备名称:">
<el-input v-model="addData.abnormalDevName" clearable placeholder="请填写"></el-input>
<el-input v-model="addData.abnormalDevName" placeholder="请填写"></el-input>
</el-form-item>
<el-form-item label="发现异常时间:">
<el-input v-model="addData.abnormalDevTime" clearable placeholder="请填写"></el-input>
<el-input v-model="addData.abnormalDevTime" placeholder="请填写"></el-input>
</el-form-item>
<br />
<el-form-item label="设备异常描述:" style="margin-top: 10px">
<el-form-item label="设备异常描述:">
<el-input
type="textarea"
style="width: 400px"
@@ -47,16 +148,16 @@
问题指标
</el-divider>
<el-form-item label="稳态指标:" style="margin-top: 10px">
<el-checkbox-group v-model="addData.steadyIndicator" disabled>
<el-form-item label="稳态指标:">
<el-checkbox-group v-model="addData.steadyIndicator">
<el-checkbox v-for="(item, ind) in steadyIndicatorList" :key="ind" :label="item.code">
{{ item.name }}
</el-checkbox>
</el-checkbox-group>
</el-form-item>
<br />
<el-form-item label="暂态指标:" style="margin-top: 10px">
<el-checkbox-group v-model="addData.transientIndicators" disabled>
<el-form-item label="暂态指标:">
<el-checkbox-group v-model="addData.transientIndicators">
<el-checkbox v-for="(item, ind) in transientIndicatorsList" :key="ind" :label="item.code">
{{ item.name }}
</el-checkbox>
@@ -64,13 +165,20 @@
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="流程" name="2"><Filling v-if="dialogVisible" :isDisabled='true' ref="FillingRef" /></el-tab-pane>
<el-tab-pane label="流程" name="2">
<Filling v-if="dialogVisible" :isDisabled="true" ref="FillingRef" />
</el-tab-pane>
</el-tabs>
</el-dialog>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { getAbnormalDetail } from '@/api/process-boot/electricitymanagement'
import {
getAbnormalDetail,
getComplaintDetail,
getGeneralSurveyDetail,
getExcessiveDetail
} from '@/api/process-boot/electricitymanagement'
import { useDictData } from '@/stores/dictData'
import Filling from './filling.vue'
@@ -79,16 +187,28 @@ const addData: any = ref({})
const FillingRef = ref()
const activeName = ref(`1`)
const dialogVisible: any = ref(false)
const problemSources = ref('')
const problemData = dictData.getBasicData('Problem_Sources')
const steadyIndicatorList = dictData.getBasicData('Steady_Indicator')
const transientIndicatorsList = dictData.getBasicData('Transient_Indicators')
const open = (row: any) => {
const open = async (row: any) => {
dialogVisible.value = true
getAbnormalDetail(row.powerQualityProblemNo).then((res: any) => {
let res: any = {}
problemSources.value = row.problemSources
if (row.problemSources == '设备异常') {
res = await getAbnormalDetail(row.powerQualityProblemNo)
} else if (row.problemSources == '在线监测告警') {
res = await getExcessiveDetail(row.powerQualityProblemNo)
} else if (row.problemSources == '用户投诉') {
res = await getComplaintDetail(row.powerQualityProblemNo)
} else if (row.problemSources == '普测超标') {
res = await getGeneralSurveyDetail(row.powerQualityProblemNo)
}
setTimeout(() => {
addData.value = res.data
FillingRef.value.open(row)
})
}, 0)
}
// 取消

View File

@@ -84,7 +84,12 @@ import process1 from './process1.vue'
import process2 from './process2.vue'
import process3 from './process3.vue'
import process4 from './process4.vue'
import { getAbnormalDetail } from '@/api/process-boot/electricitymanagement'
import {
getAbnormalDetail,
getComplaintDetail,
getGeneralSurveyDetail,
getExcessiveDetail
} from '@/api/process-boot/electricitymanagement'
const emit = defineEmits(['beforeClose'])
const dictData = useDictData()
const addData: any = ref({})
@@ -104,11 +109,22 @@ const prop = defineProps({
}
})
const problemData = dictData.getBasicData('Problem_Sources')
const open = (row: any) => {
const open = async (row: any) => {
addData.value = row
let res: any = {}
if (row.problemSources == '设备异常') {
res = await getAbnormalDetail(row.powerQualityProblemNo)
} else if (row.problemSources == '在线监测告警') {
res = await getExcessiveDetail(row.powerQualityProblemNo)
} else if (row.problemSources == '用户投诉') {
res = await getComplaintDetail(row.powerQualityProblemNo)
} else if (row.problemSources == '普测超标') {
res = await getGeneralSurveyDetail(row.powerQualityProblemNo)
}
getAbnormalDetail(row.powerQualityProblemNo).then((res: any) => {
setTimeout(() => {
List.value = res.data
if (res.data.filePathYyfx == null) {
active.value = 0
} else if (res.data.filePathJhzg == null) {
@@ -128,7 +144,7 @@ const open = (row: any) => {
}
}
control.value = active.value == 4 ? 3 : active.value
})
}, 0)
}
const step = (e: number) => {
@@ -149,13 +165,13 @@ onMounted(() => {
// 提交
const Submit = () => {
if (control.value == 0) {
process0Ref.value.submit()
process0Ref.value.submit(0)
} else if (control.value == 1) {
process1Ref.value.submit()
process1Ref.value.submit(1)
} else if (control.value == 2) {
process2Ref.value.submit()
process2Ref.value.submit(2)
} else if (control.value == 3) {
process3Ref.value.submit()
process3Ref.value.submit(3)
}
}
// 取消

View File

@@ -49,7 +49,7 @@
<NewlyAdd v-if="showNewlyAdded" @handleClose="handleClose" @onSubmit="beforeClose" />
<!-- 填报 -->
<el-dialog draggable title="填报" v-model="dialogVisible" width="1400px" :before-close="beforeClose">
<Filling ref="FillingRef" @beforeClose="beforeClose" />
<Filling ref="FillingRef" v-if="dialogVisible"@beforeClose="beforeClose" />
</el-dialog>
<!-- 详情 -->

View File

@@ -1,7 +1,7 @@
<template>
<el-form :model="causeAnalysisData" :rules="rules" ref="form1Ref" label-width="auto">
<el-form-item label="电网侧原因:" prop="reportProcessContentYyfx" :disabled="prop.disabled">
<el-checkbox-group v-model="causeAnalysisData.reportProcessContentYyfx">
<el-form-item label="电网侧原因:" prop="reportProcessContentYyfx">
<el-checkbox-group :disabled="prop.disabled" v-model="causeAnalysisData.reportProcessContentYyfx">
<el-checkbox v-for="(item, ind) in CauseList" :key="ind" :label="item.code">
{{ item.name }}
</el-checkbox>
@@ -10,8 +10,8 @@
<el-divider></el-divider>
<el-form-item label="用户侧原因:" prop="userReportProcessContentYyfx" :disabled="prop.disabled">
<el-checkbox-group v-model="causeAnalysisData.userReportProcessContentYyfx">
<el-form-item label="用户侧原因:" prop="userReportProcessContentYyfx">
<el-checkbox-group :disabled="prop.disabled" v-model="causeAnalysisData.userReportProcessContentYyfx">
<el-checkbox v-for="(item, ind) in userCauseList" :key="ind" :label="item.code">
{{ item.name }}
</el-checkbox>
@@ -20,8 +20,8 @@
<el-row v-if="prop.addData.problemSources == '用户投诉' || prop.addData.problemSources == '设备异常'">
<el-divider></el-divider>
<el-form-item label="电网侧受影响设备:" prop="powerGridAffectDev" :disabled="prop.disabled">
<el-checkbox-group v-model="causeAnalysisData.powerGridAffectDev">
<el-form-item label="电网侧受影响设备:" prop="powerGridAffectDev">
<el-checkbox-group :disabled="prop.disabled" v-model="causeAnalysisData.powerGridAffectDev">
<el-checkbox v-for="(item, ind) in powerGridAffectDevList" :key="ind" :label="item.code">
{{ item.name }}
</el-checkbox>
@@ -29,8 +29,8 @@
</el-form-item>
<el-divider></el-divider>
<el-form-item label="用户侧受影响设备:" prop="userAffectDev" :disabled="prop.disabled">
<el-checkbox-group v-model="causeAnalysisData.userAffectDev">
<el-form-item label="用户侧受影响设备:" prop="userAffectDev">
<el-checkbox-group :disabled="prop.disabled" v-model="causeAnalysisData.userAffectDev">
<el-checkbox v-for="(item, ind) in userAffectDevList" :key="ind" :label="item.code">
{{ item.name }}
</el-checkbox>
@@ -40,12 +40,13 @@
<el-divider></el-divider>
<el-form-item label="事件描述:" prop="eventDescriptionYyfx" :disabled="prop.disabled">
<el-form-item label="事件描述:" prop="eventDescriptionYyfx">
<el-input
style="width: 400px"
:autosize="{ minRows: 2, maxRows: 4 }"
type="textarea"
placeholder="请输入内容"
:disabled="prop.disabled"
v-model="causeAnalysisData.eventDescriptionYyfx"
></el-input>
</el-form-item>
@@ -73,7 +74,13 @@
</template>
<script setup lang="ts">
import { useDictData } from '@/stores/dictData'
import { uploadFile, reasonAnalysis } from '@/api/process-boot/electricitymanagement'
import {
uploadFile,
reasonAnalysis,
takeAction,
effectAnalysis,
correctiveAction
} from '@/api/process-boot/electricitymanagement'
import { UploadInstance, UploadProps, UploadRawFile, ElMessage, ElMessageBox } from 'element-plus'
import { genFileId } from 'element-plus'
import { ref, reactive, onMounted } from 'vue'
@@ -146,21 +153,42 @@ onMounted(() => {
if (prop.List.filePathYyfx != null) {
causeAnalysisData.value = prop.List
}
console.log(prop.disabled)
})
const submit = () => {
const submit = (num: number) => {
console.log(prop.addData.problemSources)
form1Ref.value.validate(async (valid: any) => {
if (valid) {
let form = new FormData()
form.append('file', causeAnalysisData.value.fileList[0].raw)
causeAnalysisData.value.powerQualityProblemNo = prop.addData.powerQualityProblemNo
await uploadFile(form).then((res: any) => {
causeAnalysisData.value.filePathYyfx = res.filePath
causeAnalysisData.value.fileNameYyfx = res.fileName
causeAnalysisData.value.filePathYyfx = res.data.minFileUrl
causeAnalysisData.value.fileNameYyfx = res.data.minFileName
})
await reasonAnalysis(causeAnalysisData.value).then((res: any) => {
if (num == 0) {
reasonAnalysis(causeAnalysisData.value).then((res: any) => {
ElMessage.success('提交成功')
emit('handleClose')
})
} else if (num == 1) {
correctiveAction(causeAnalysisData.value).then((res: any) => {
ElMessage.success('提交成功')
emit('handleClose')
})
} else if (num == 2) {
takeAction(causeAnalysisData.value).then((res: any) => {
ElMessage.success('提交成功')
emit('handleClose')
})
} else if (num == 3) {
effectAnalysis(causeAnalysisData.value).then((res: any) => {
ElMessage.success('提交成功')
emit('handleClose')
})
}
}
})
}

View File

@@ -6,16 +6,16 @@
label-width="auto"
>
<el-form-item label="电网侧整改治理措施:" prop="reportProcessContentJhzg" :disabled="prop.disabled">
<el-checkbox-group v-model="rectificationMeasuresData.reportProcessContentJhzg">
<el-form-item label="电网侧整改治理措施:" prop="reportProcessContentJhzg" >
<el-checkbox-group v-model="rectificationMeasuresData.reportProcessContentJhzg" :disabled="prop.disabled">
<el-checkbox v-for="item in JhzgList" :label="item.code">{{ item.name }}</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-divider></el-divider>
<el-form-item label="用户侧整改治理措施:" prop="userReportProcessContentJhzg" :disabled="prop.disabled">
<el-checkbox-group v-model="rectificationMeasuresData.userReportProcessContentJhzg">
<el-form-item label="用户侧整改治理措施:" prop="userReportProcessContentJhzg" >
<el-checkbox-group v-model="rectificationMeasuresData.userReportProcessContentJhzg" :disabled="prop.disabled">
<el-checkbox v-for="item in JhzgList" :label="item.code">{{ item.name }}</el-checkbox>
</el-checkbox-group>
</el-form-item>

View File

@@ -1,15 +1,15 @@
<template>
<el-form :model="rectificationMeasuresData" :rules="rules" ref="form2Ref" label-width="auto">
<el-form-item label="电网侧实际采取措施:" prop="reportProcessContentSjcq" :disabled="prop.disabled">
<el-checkbox-group v-model="rectificationMeasuresData.reportProcessContentSjcq">
<el-form-item label="电网侧实际采取措施:" prop="reportProcessContentSjcq" >
<el-checkbox-group v-model="rectificationMeasuresData.reportProcessContentSjcq" :disabled="prop.disabled">
<el-checkbox v-for="(item, ind) in SjcqList" :key="ind" :label="item.code">{{ item.name }}</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-divider></el-divider>
<el-form-item label="用户侧实际采取措施:" prop="userReportProcessContentSjcq" :disabled="prop.disabled">
<el-checkbox-group v-model="rectificationMeasuresData.userReportProcessContentSjcq">
<el-form-item label="用户侧实际采取措施:" prop="userReportProcessContentSjcq" >
<el-checkbox-group v-model="rectificationMeasuresData.userReportProcessContentSjcq" :disabled="prop.disabled">
<el-checkbox v-for="(item, ind) in SjcqList" :key="ind" :label="item.code">{{ item.name }}</el-checkbox>
</el-checkbox-group>
</el-form-item>

View File

@@ -1,7 +1,8 @@
<template>
<el-form :model="effectivenessAnalysisData" :rules="rules" ref="form2Ref" label-width="auto">
<el-form-item label="成效分析概述:" style="margin-top: 10px" prop="descriptionZlxg" :disabled="prop.disabled">
<el-form-item label="成效分析概述:" style="margin-top: 10px" prop="descriptionZlxg">
<el-input
:disabled="prop.disabled"
type="textarea"
style="width: 400px"
:autosize="{ minRows: 2, maxRows: 4 }"

View File

@@ -0,0 +1,16 @@
<template>
<div>
<vxe-table v-bind="defaultAttribute" height="350" ref="xTable1Ref" :data="list">
<vxe-column field="sustationName" title="变电站"></vxe-column>
<vxe-column field="barName" title="母线"></vxe-column>
<vxe-column field="measurementPointName" title="监测点名称"></vxe-column>
</vxe-table>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { defaultAttribute } from '@/components/table/defaultAttribute'
const list = ref([])
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,73 @@
<template>
<div class="containerBox" style="position: relative">
<div id="container" style="width: calc(100vw - 750px); height: calc(100vh - 150px)"></div>
<div id="js-properties-panel" class="panel"></div>
</div>
</template>
<script setup lang="ts">
import { onMounted, markRaw } from 'vue'
import 'bpmn-js/dist/assets/diagram-js.css' // 左边工具栏以及编辑节点的样式
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css'
import BpmnModeler from 'bpmn-js/lib/Modeler'
// bpmn-js-properties-panel相关
import 'bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css'
import propertiesPanelModule from 'bpmn-js-properties-panel'
import propertiesProviderModule from 'bpmn-js-properties-panel/lib/provider/camunda'
import camundaModdleDescriptor from 'camunda-bpmn-moddle/resources/camunda'
import translate from '@/assets/bpmn/translate'
onMounted(() => {
const containerEl = document.getElementById('container')
const bpmnModeler = markRaw(
new BpmnModeler({
container: containerEl,
// 添加控制板
propertiesPanel: {
parent: '#js-properties-panel'
},
// 右侧属性面板
additionalModules: [propertiesPanelModule, propertiesProviderModule],
moddleExtensions: {
camunda: camundaModdleDescriptor
}
})
)
bpmnModeler.createDiagram(() => {
bpmnModeler.get('canvas').zoom('fit-viewport')
})
})
var customTranslateModule = {
translate: ['value', translate]
}
const bpmnModeler = new BpmnModeler({
additionalModules: [customTranslateModule]
})
</script>
<style lang="scss" scoped>
.containerBox {
height: calc(100vh - 160px);
margin-top: 30px;
}
.containerBox #container {
height: calc(100vh - 160px);
border: 1px solid rgb(121, 121, 121);
}
.bpp-properties-panel [type='text'] {
box-sizing: border-box;
}
.panel {
width: 400px;
position: absolute;
top: 1px;
right: 1px;
height: 100%;
overflow: auto;
}
/* 右下角logo */
.bjs-powered-by {
display: none;
}
</style>

View File

@@ -1,9 +1,12 @@
<template>
<div class="default-main" :style="layout">123</div>
<div class="default-main" :style="layout">
<work />
</div>
</template>
<script setup lang="ts">
import { onMounted, reactive, ref, provide } from 'vue'
import work from './work.vue'
import { mainHeight } from '@/utils/layout'
defineOptions({

View File

@@ -0,0 +1,416 @@
<template>
<div v-loading.fullscreen.lock="loading" class="bpmn-dialog">
<el-dialog v-model="dialogVisible" :fullscreen="true" draggable @close="close">
<template #header>
<el-space>
<el-button icon="FolderChecked" @click="saveModeler">保存</el-button>
<el-button icon="Download" @click="downloadProcess('xml')">导出xml</el-button>
<el-button icon="Download" @click="downloadProcess('bpmn')">导出bpmn</el-button>
<el-upload
ref="uploadRef"
:auto-upload="false"
:show-file-list="false"
:limit="1"
accept="text/xml"
@change="openProcess"
>
<template #trigger>
<el-button icon="Upload">导入</el-button>
</template>
</el-upload>
</el-space>
<ul class="icon-btn">
<li v-for="item in iconBtn1" :key="item.key" :title="item.name" @click="handlerAlign(item)">
<el-icon>
<component :is="item.icon"></component>
</el-icon>
</li>
</ul>
<ul class="icon-btn">
<li v-for="item in iconBtn2" :key="item.key" :title="item.name" @click="handlerReset(item)">
<el-icon>
<component :is="item.icon"></component>
</el-icon>
</li>
</ul>
</template>
<splitpanes class="default-theme bpmn-body">
<pane min-size="20">
<div ref="canvasRef" class="canvas"></div>
</pane>
<!--属性面板将通过该元素呈现到其中-->
<pane :size="25" min-size="15" style="overflow-y: auto">
<div id="js-properties-panel" class="panel"></div>
</pane>
</splitpanes>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="ActDefinitionPage">
import { computed, watch, ref, nextTick } from 'vue'
import { ElMessage, type UploadFile } from 'element-plus'
import BpmnModeler from 'bpmn-js/lib/Modeler' // 引入 bpmn-js
import type { ModdleElement } from 'bpmn-js/lib/BaseModeler'
// 导入一下有关于bpmn-js的字体库以及样式文件
import 'bpmn-js/dist/assets/diagram-js.css'
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css'
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css'
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css'
// // 右侧属性面包样式
// import "bpmn-js-properties-panel/dist/assets/properties-panel.css";
// import "bpmn-js-properties-panel/dist/assets/element-templates.css";
// 属性面板相关模块
import {
BpmnPropertiesPanelModule,
BpmnPropertiesProviderModule,
CamundaPlatformPropertiesProviderModule
} from 'bpmn-js-properties-panel'
import CamundaBpmnModdle from 'camunda-bpmn-moddle/resources/camunda.json'
// 汉化包
import customTranslate from '@/assets/bpmn/translate'
// 注意这个xml文件
import createBpmnXml, { activitiToCamundaXml, camundaToActivitiXml } from './xml' // 放在下面了
// 窗格拆分器插件
import { Splitpanes, Pane } from 'splitpanes'
import 'splitpanes/dist/splitpanes.css'
import saveAs from 'file-saver'
import type CommandStack from 'diagram-js/lib/command/CommandStack'
import { getModelerDetail, updateModeler, type ActModelerObj, type XmlMetaInfo } from '@/api/activiti/modeler'
//外部参数 ################################################
const props = withDefaults(
defineProps<{
modelValue: boolean
modelId: string
}>(),
{}
)
//ref对象 ################################################
const loading = ref(false)
const iconBtn1 = ref([
{ name: '左对齐', key: 'left', icon: 'myAlignStartVertical' },
{ name: '水平居中', key: 'center', icon: 'myAlignCenterVertical' },
{ name: '右对齐', key: 'right', icon: 'myAlignEndVertical' },
{ name: '上对齐', key: 'top', icon: 'myAlignStartHorizontal' },
{ name: '垂直居中', key: 'middle', icon: 'myAlignCenterHorizontal' },
{ name: '下对齐', key: 'bottom', icon: 'myAlignEndHorizontal' }
])
const iconBtn2 = ref([
{ name: '撤销', key: 'undo', icon: 'myUndo' },
{ name: '恢复', key: 'redo', icon: 'myRedo' },
{ name: '擦除重做', key: 'restart', icon: 'myEraser' }
])
//elment ################################################
const canvasRef = ref()
const uploadRef = ref()
//emit
const emit = defineEmits<{ (event: 'update:modelValue', value: boolean): void; (event: 'close'): void }>()
//内部变量 ################################################
let bpmnModeler: BpmnModeler | undefined = undefined
let modelData: ActModelerObj = { id: '' }
//computed ################################################
const dialogVisible = computed({
get() {
return props.modelValue
},
set(value) {
emit('update:modelValue', value)
}
})
// watch ################################################
watch(dialogVisible, value => {
nextTick(() => {
if (value && !bpmnModeler) {
initModeler()
}
if (value) {
refuseModelerData()
}
})
})
// Function ################################################
/**
* 初始化 BpmnModeler
*/
const initModeler = () => {
// 将汉化包装成一个模块
const customTranslateModule = {
translate: ['value', customTranslate]
}
// 生成实例
bpmnModeler = new BpmnModeler({
container: canvasRef.value, // 获取到属性ref为“canvasRef”的dom节点
propertiesPanel: {
parent: '#js-properties-panel'
},
additionalModules: [
BpmnPropertiesPanelModule,
BpmnPropertiesProviderModule,
CamundaPlatformPropertiesProviderModule,
customTranslateModule // 汉化模块
],
moddleExtensions: {
camunda: CamundaBpmnModdle
}
})
}
/**刷新视图数据 */
const refuseModelerData = () => {
loading.value = true
getModelerDetail(props.modelId)
.then(({ data }) => {
data && (modelData = data)
const xml = data?.bpmnXml
createNewDiagram(xml ? activitiToCamundaXml(xml) : undefined)
})
.finally(() => {
loading.value = false
})
}
/**
* 对齐操作
*/
const handlerAlign = ({ key }: { key: string }) => {
if (bpmnModeler) {
const modeling = bpmnModeler.get('modeling')
const selection = bpmnModeler.get<{ get: () => ModdleElement[] }>('selection')
const align = bpmnModeler.get<ModdleElement>('alignElements')
if (modeling && selection) {
const SelectedElements = selection.get()
if (!SelectedElements || SelectedElements.length <= 1) {
return ElMessage.warning('请按住 Shift 键选择多个元素对齐')
}
align.trigger(SelectedElements, key)
}
}
}
/**
* 重置操作
*/
const handlerReset = ({ key }: { key: string }) => {
if (bpmnModeler) {
const command = bpmnModeler.get<CommandStack>('commandStack')
switch (key) {
case 'undo':
command && command.canUndo() && command.undo()
break
case 'redo':
command && command.canRedo() && command.redo()
break
case 'restart':
command && command.clear()
createNewDiagram()
break
}
}
}
/**
* 打开文件
*/
const openProcess = (rawFile: UploadFile) => {
if (!rawFile.raw) {
return false
}
loading.value = true
const reader = new FileReader()
reader.readAsText(rawFile.raw, 'utf8')
reader.onload = () => {
if (typeof reader.result === 'string' && bpmnModeler) {
if (reader.result.indexOf('bpmn2:definitions') <= 0) {
ElMessage.error('请选择bpmn20标准的流程文件')
loading.value = false
} else {
bpmnModeler
.importXML(reader.result)
.then(({ warnings }) => {
warnings && warnings.length > 0 && console.warn(warnings)
warnings && warnings.length > 0 && ElMessage.warning(warnings.toString())
})
.finally(() => {
loading.value = false
})
}
} else {
loading.value = false
}
uploadRef.value.clearFiles()
}
}
/**
* 加载bpmn.xml配置文件
*/
const createNewDiagram = (xml?: string) => {
loading.value = true
let bpmnXml = xml
if (!bpmnXml) {
const metaInfo = modelData.metaInfo ? (JSON.parse(modelData.metaInfo) as XmlMetaInfo) : {}
const param = {
key: modelData.key,
name: modelData.name,
description: metaInfo?.description,
version: metaInfo?.version
}
bpmnXml = createBpmnXml(param)
}
bpmnModeler &&
bpmnModeler
.importXML(bpmnXml)
.then(({ warnings }) => {
warnings && warnings.length > 0 && ElMessage.warning(warnings.toString())
})
.finally(() => {
loading.value = false
})
}
/**
* 保存模型
*/
const saveModeler = () => {
loading.value = true
if (!bpmnModeler) {
return
}
// 读取xml、svg
Promise.all([bpmnModeler.saveXML({}), bpmnModeler.saveSVG()])
.then(([rsp1, rsp2]) => {
const { error, xml } = rsp1
const { svg } = rsp2
if (error) {
return { code: 500, msg: error.message }
}
if (!xml) {
return { code: 500, msg: 'xml文件不存在' }
}
if (!svg) {
return { code: 500, msg: 'svg文件不存在' }
}
return updateModeler({
id: modelData.id,
name: modelData.name,
description: modelData.description,
bpmnXml: camundaToActivitiXml(xml),
svgXml: svg
})
})
.then(rsp => {
if (rsp.code === 200) {
ElMessage.success('保存成功')
} else {
ElMessage.success(rsp.msg)
}
})
.finally(() => {
loading.value = false
})
}
// 下载流程图到本地
/**
* @param {string} type
* @param {string} name
*/
const downloadProcess = (type = 'xml', name = 'diagram') => {
loading.value = true
bpmnModeler &&
bpmnModeler
.saveXML({})
.then(({ error, xml }) => {
// 读取异常时抛出异常
if (error || !xml) {
console.error(`[Process Designer Warn ]:`, error)
} else {
const blob = new Blob([camundaToActivitiXml(xml)], {
type: type === 'xml' ? 'text/xml' : 'bpmn20-xml'
})
saveAs(blob, `${name}.${type}`)
}
})
.finally(() => {
loading.value = false
})
}
const close = () => {
emit('close')
}
</script>
<style lang="less" scoped>
.bpmn-dialog {
:deep(.el-dialog__body) {
padding: 0;
width: 100%;
height: calc(100vh - 65px);
overflow-y: auto;
}
.bpmn-body {
width: 100%;
height: 100%;
min-height: 600px;
.canvas {
width: 100%;
height: 100%;
background: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdGggZD0iTTAgMTBoNDBNMTAgMHY0ME0wIDIwaDQwTTIwIDB2NDBNMCAzMGg0ME0zMCAwdjQwIiBmaWxsPSJub25lIiBzdHJva2U9IiNlMGUwZTAiIG9wYWNpdHk9Ii4yIi8+PHBhdGggZD0iTTQwIDBIMHY0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZTBlMGUwIi8+PC9wYXR0ZXJuPjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2EpIi8+PC9zdmc+')
repeat !important;
}
.panel {
width: 100%;
}
}
}
.icon-btn {
margin: 0;
padding: 0;
display: inline-flex;
margin-left: 30px;
li {
border-left: 1px solid var(--el-border-color);
border-top: 1px solid var(--el-border-color);
border-bottom: 1px solid var(--el-border-color);
color: var(--el-text-color-regular);
list-style: none;
font-size: 24px;
text-align: center;
cursor: pointer;
padding: 3px 8px 0 8px;
height: 32px;
}
li:hover {
background-color: #ecf5ff;
}
li:first-child {
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
li:last-child {
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
border-right: 1px solid var(--el-border-color);
}
.icon-popper {
width: 60px;
}
}
</style>

View File

@@ -0,0 +1,72 @@
import type { XmlMetaInfo } from "@/api/activiti/modeler";
/**
* bpmn2.0标准
* activiti标准转化为camunda标准
* @param xml
*/
export const activitiToCamundaXml = (xml: string, metaInfo?: XmlMetaInfo) => {
//替换activiti标准
xml = xml.replace("xmlns:activiti", "xmlns:camunda");
xml = xml.replace("http://activiti.org/bpmn", "http://camunda.org/schema/1.0/bpmn");
xml = xml.replaceAll("activiti:", "camunda:");
//显示版本信息
if (metaInfo && metaInfo.version) {
xml.replace("<bpmn2:process", `<bpmn:process camunda:versionTag="${metaInfo.version}"`);
}
return xml;
};
/**
* bpmn2.0标准
* camunda标准转化为activiti标准
* @param xml
*/
export const camundaToActivitiXml = (xml: string) => {
//追加头部信息
xml = xml.replace(
"<bpmn2:definitions",
`<bpmn2:definitions typeLanguage="http://www.w3.org/2001/XMLSchema"
expressionLanguage="http://www.w3.org/1999/XPath"`
);
//删除版本号
// xml = xml.replace(/camunda:versionTag="*"/, "");
//替换camunda标准
xml = xml.replace("xmlns:camunda", "xmlns:activiti");
xml = xml.replace("http://camunda.org/schema/1.0/bpmn", "http://activiti.org/bpmn");
xml = xml.replaceAll("camunda:", "activiti:");
return xml;
};
/**
* bpmn2.0标准
* 初始化一个camunda标准的xml
* @param {XmlMetaInfo} metaInfo
* @returns {string}
*/
const createBpmnXml = ({ key, name, description, version }: XmlMetaInfo) => {
const keyStr = key ? key : "Process_1";
const nameStr = name ? name : "";
const descriptionStr = description ? `<bpmn2:documentation>${description}</bpmn2:documentation>` : "";
const versionStr = version ? version : 1;
const xmlStr = `<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:camunda="http://camunda.org/schema/1.0/bpmn"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.activiti.org/test">
<bpmn2:process id="${keyStr}" name="${nameStr}" isExecutable="true" camunda:versionTag="${versionStr}" >
${descriptionStr}
</bpmn2:process>
<bpmndi:BPMNDiagram id="BpmnDiagram_1">
<bpmndi:BPMNPlane id="BpmnPlane_1" bpmnElement="${keyStr}" />
</bpmndi:BPMNDiagram>
</bpmn2:definitions>`;
return xmlStr;
};
export default createBpmnXml;

View File

@@ -13,8 +13,8 @@ export default defineConfig({
host: '0.0.0.0',
proxy: {
'/api': {
// target: 'http://192.168.1.31:10215', //数据中心
target: 'http://192.168.1.31:10215', //数据中心
// target: 'http://192.168.1.81:10215', //数据中心
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '') //路径重写,把'/api'替换为''
},

3265
yarn.lock Normal file

File diff suppressed because it is too large Load Diff