Merge branch 'develop' of https://e.coding.net/ctcdevteam/hberp/hberp into develop

This commit is contained in:
shilixia 2021-09-22 10:05:08 +08:00
commit 1559fb95a4
17 changed files with 865 additions and 230 deletions

View File

@ -20,6 +20,8 @@
"axios": "^0.21.1", "axios": "^0.21.1",
"cache-loader": "^4.1.0", "cache-loader": "^4.1.0",
"compression-webpack-plugin": "^5.0.1", "compression-webpack-plugin": "^5.0.1",
"d3": "^5.16.0",
"dagre-d3": "^0.6.4",
"element-ui": "^2.15.5", "element-ui": "^2.15.5",
"file-saver": "^2.0.2", "file-saver": "^2.0.2",
"fuse.js": "^6.4.6", "fuse.js": "^6.4.6",

View File

@ -121,3 +121,11 @@ export function deleteWfTransition(id, data) {
data data
}) })
} }
//工单详情
export function getTickets(query) {
return request({
url: `/wf/ticket/`,
method: 'get',
params:query
})
}

View File

@ -7,7 +7,7 @@
<div class="right-menu"> <div class="right-menu">
<template> <template>
<search id="header-search" class="right-menu-item" /> <search id="header-search" class="right-menu-item" />
<el-tooltip content="Global Size" effect="dark" placement="bottom"> <el-tooltip content="全局组件大小" effect="dark" placement="bottom">
<size-select id="size-select" class="right-menu-item hover-effect" /> <size-select id="size-select" class="right-menu-item hover-effect" />
</el-tooltip> </el-tooltip>
</template> </template>

View File

@ -13,8 +13,8 @@
<item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" /> <item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" />
</template> </template>
<sidebar-item <sidebar-item
v-for="child in item.children" v-for="(child,$index) in item.children"
:key="child.path" :key="child.path+$index"
:is-nest="true" :is-nest="true"
:item="child" :item="child"
:base-path="resolvePath(child.path)" :base-path="resolvePath(child.path)"

View File

@ -271,6 +271,19 @@ export const asyncRoutes = [
meta: { title: '人员信息详情', icon: 'example', perms: ['configuration_manage'] }, meta: { title: '人员信息详情', icon: 'example', perms: ['configuration_manage'] },
hidden: true hidden: true
}, },
{
path: 'ticket',
name: 'ticket',
component: () => import('@/views/workflow/ticket'),
meta: { title: '工单列表', icon: 'example', perms: ['ticket_manage'] },
hidden: true
},{
path: 'test',
name: 'test',
component: () => import('@/views/workflow/test'),
meta: { title: '工单', icon: 'example', perms: ['test_manage'] },
hidden: true
},
] ]
}, },
{ {

View File

@ -49,7 +49,7 @@
<el-link <el-link
v-if="checkPermission(['customfield_delete'])" v-if="checkPermission(['customfield_delete'])"
type="danger" type="danger"
@click="handleDelete(scope)" @click="handleDeleteCustomfield(scope)"
>删除</el-link >删除</el-link
> >
</template> </template>
@ -59,34 +59,25 @@
</el-card> </el-card>
<el-dialog <el-dialog
:visible.sync="dialogVisible" :visible.sync="dialogVisible"
:title="dialogType === 'edit' ? '编辑自定义字段' : '新增自定义字段'" :title="dialogType === 'edit' ? '编辑自定义字段' : '新增自定义字段'">
>
<el-form <el-form
ref="Form" ref="Form"
:model="customfield" :model="customfield"
label-width="80px" label-width="80px"
label-position="right" label-position="right"
:rules="rule1" :rules="rule1">
>
<el-form-item label="字段标识" prop="field_key"> <el-form-item label="字段标识" prop="field_key">
<el-input v-model="customfield.field_key" placeholder="字段标识" /> <el-input v-model="customfield.field_key" placeholder="字段标识" />
</el-form-item> </el-form-item>
<el-form-item label="字段名称" prop="field_name"> <el-form-item label="字段名称" prop="field_name">
<el-input v-model="customfield.field_name" placeholder="字段名称" /> <el-input v-model="customfield.field_name" placeholder="字段名称" />
</el-form-item> </el-form-item>
<el-form-item label="字段描述" prop="description"> <el-form-item label="字段描述" prop="description">
<el-input v-model="customfield.description" placeholder="字段描述" /> <el-input v-model="customfield.description" placeholder="字段描述" />
</el-form-item> </el-form-item>
<el-form-item label="占位符" prop="placeholder"> <el-form-item label="占位符" prop="placeholder">
<el-input v-model="customfield.placeholder" placeholder="占位符" /> <el-input v-model="customfield.placeholder" placeholder="占位符" />
</el-form-item> </el-form-item>
<el-form-item label="字段类型" prop="field_type"> <el-form-item label="字段类型" prop="field_type">
<el-select style="width: 100%" v-model="customfield.field_type" placeholder="请选择"> <el-select style="width: 100%" v-model="customfield.field_type" placeholder="请选择">
<el-option <el-option
@ -96,72 +87,30 @@
:value="item.value"> :value="item.value">
</el-option> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="选项" v-show="customfield.field_type=='select'||customfield.field_type=='selects'">
<el-button @click.prevent="addDomain" style="border: none;">
<i class="el-icon-circle-plus-outline"></i>
<span style="font-size:14px;">添加</span>
</el-button>
<el-row v-for="(domain, $index) in choiceOption" :key=domain+$index style="margin-bottom: 10px">
<el-col :span="20">
<el-input v-model="choiceOption[$index]" auto-complete="off"></el-input>
</el-col>
<el-col :span="3" style="text-align: center" v-if="$index!==0">
<i class="el-icon-remove-outline" @click.prevent="removeDomain($index,'1')" style="color: red;font-size: 16px;"></i>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="顺序ID" prop="sort"> <el-form-item label="顺序ID" prop="sort">
<el-input v-model="customfield.sort" type="number" placeholder="顺序" /> <el-input v-model="customfield.sort" type="number" placeholder="顺序" />
</el-form-item> </el-form-item>
<el-form-item label="默认值" prop="default_value"> <el-form-item label="默认值" prop="default_value">
<el-input v-model="customfield.default_value" placeholder="默认值" /> <el-input v-model="customfield.default_value" placeholder="默认值" />
</el-form-item> </el-form-item>
<el-form-item
label="布尔显示定义"
prop="boolean_field_display"
label-width="120px"
>
<vue-json-editor
v-model="customfield.boolean_field_display"
:showBtns="false"
:mode="'code'"
lang="zh"
@json-change="onJsonChange"
@json-save="onJsonSave"
@has-error="onError"
/>
</el-form-item>
<el-form-item
label="选项"
prop="field_choice"
label-width="120px"
>
<vue-json-editor
v-model="customfield.field_choice"
:showBtns="false"
:mode="'code'"
lang="zh"
@json-change="onJsonChange1"
@json-save="onJsonSave1"
@has-error="onError1"
/>
</el-form-item>
<el-form-item
label="标签"
prop="label"
label-width="120px"
>
<vue-json-editor
v-model="customfield.label"
:showBtns="false"
:mode="'code'"
lang="zh"
@json-change="onJsonChange2"
@json-save="onJsonSave2"
@has-error="onError2"
/>
</el-form-item>
<el-form-item label="模板" prop="field_template"> <el-form-item label="模板" prop="field_template">
<el-input v-model="customfield.field_template" placeholder="你有一个待办工单:{title}" /> <el-input v-model="customfield.field_template" placeholder="你有一个待办工单:{title}" />
</el-form-item> </el-form-item>
</el-form> </el-form>
<div style="text-align: right"> <div style="text-align: right">
<el-button type="danger" @click="dialogVisible = false">取消</el-button> <el-button type="danger" @click="dialogVisible = false">取消</el-button>
@ -185,7 +134,16 @@ export default {
props: ["ID"], props: ["ID"],
data() { data() {
return { return {
customfield: defaultcustomfield, customfield: {
field_key:'',
field_name:'',
placeholder:'',
field_type:'',
sort:'',
default_value:'',
field_template:'',
field_choice:[]
},
view_permission_check:false, view_permission_check:false,
hasJsonFlag:true, // json是否验证通过 hasJsonFlag:true, // json是否验证通过
hasJsonFlag1:true, // json是否验证通过 hasJsonFlag1:true, // json是否验证通过
@ -195,31 +153,25 @@ export default {
}, },
options: [{ options: [{
value: 'string', value: 'string',
label: '字符串' label: '文本'
}, { }, {
value: 'int', value: 'int',
label: '' label: ''
}, { }, {
value: 'float', value: 'float',
label: '浮点型' label: '小数'
}, {
value: 'bool',
label: '布尔'
}, { }, {
value: 'date', value: 'date',
label: '日期' label: '日期'
}, { }, {
value: 'datetime', value: 'datetime',
label: '日期时间' label: '日期时间'
}, {
value: 'radio',
label: '单选框'
}, {
value: 'checkbox',
label: '多选框'
},{ },{
value: 'select', value: 'select',
label: '下拉列表' label: '单选'
},{
value: 'selects',
label: '多选'
}, { }, {
value: 'textarea', value: 'textarea',
label: '文本域' label: '文本域'
@ -236,6 +188,7 @@ export default {
boolean_field_display:[], boolean_field_display:[],
choiceOption:[''],
field_choice:[], field_choice:[],
dialogVisible: false, dialogVisible: false,
dialogType: "new", dialogType: "new",
@ -251,6 +204,14 @@ export default {
this.getList(); this.getList();
}, },
methods: { methods: {
//添加字段选项
addDomain() {
this.choiceOption.push('')
},
//删除字段选项
removeDomain(index){
this.choiceOption.splice(index, 1)
},
checkPermission, checkPermission,
getList() { getList() {
@ -286,6 +247,7 @@ export default {
handleEdit(scope) { handleEdit(scope) {
this.customfield = Object.assign({}, scope.row); // copy obj this.customfield = Object.assign({}, scope.row); // copy obj
this.choiceOption = scope.row.field_choice;
this.dialogType = "edit"; this.dialogType = "edit";
this.dialogVisible = true; this.dialogVisible = true;
this.$nextTick(() => { this.$nextTick(() => {
@ -321,6 +283,7 @@ export default {
this.checkJson(); this.checkJson();
this.checkJson1(); this.checkJson1();
this.checkJson2(); this.checkJson2();
this.customfield.field_choice = this.choiceOption;
updateWfCustomfield(this.customfield.id, this.customfield).then((res) => { updateWfCustomfield(this.customfield.id, this.customfield).then((res) => {
if (res.code >= 200) { if (res.code >= 200) {
this.getList(); this.getList();
@ -333,7 +296,7 @@ export default {
this.checkJson1(); this.checkJson1();
this.checkJson2(); this.checkJson2();
this.customfield.workflow=this.ID; this.customfield.workflow=this.ID;
this.customfield.field_choice = this.choiceOption;
createWfCustomfield(this.customfield).then((res) => { createWfCustomfield(this.customfield).then((res) => {
if (res.code >= 200) { if (res.code >= 200) {
this.getList(); this.getList();
@ -347,7 +310,7 @@ export default {
} }
}); });
}, },
handleDelete(scope) { handleDeleteCustomfield(scope) {
this.$confirm("确认删除?", "警告", { this.$confirm("确认删除?", "警告", {
confirmButtonText: "确认", confirmButtonText: "确认",
cancelButtonText: "取消", cancelButtonText: "取消",

View File

@ -25,9 +25,7 @@
> >
</div> </div>
<div style="margin-top: 10px"> <div style="margin-top: 10px">
<el-button type="primary" icon="el-icon-plus" @click="handleCreate" <el-button type="primary" icon="el-icon-plus" @click="handleCreate">新增</el-button>
>新增</el-button
>
</div> </div>
</el-card> </el-card>
<el-card style="margin-top: 10px"> <el-card style="margin-top: 10px">
@ -55,7 +53,6 @@
<el-table-column width="180" label="创建时间"> <el-table-column width="180" label="创建时间">
<template slot-scope="scope">{{ scope.row.create_time }}</template> <template slot-scope="scope">{{ scope.row.create_time }}</template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
align="center" align="center"
label="操作" label="操作"
@ -65,19 +62,20 @@
<el-link <el-link
v-if="checkPermission(['workflow_update'])" v-if="checkPermission(['workflow_update'])"
@click="handlecfgt(scope)" @click="handlecfgt(scope)"
>配置</el-link >配置</el-link>
>
<el-link <el-link
v-if="checkPermission(['workflow_update'])" v-if="checkPermission(['workflow_update'])"
@click="handleEdit(scope)" @click="handleEdit(scope)"
>编辑</el-link >编辑</el-link>
>
<el-link <el-link
v-if="checkPermission(['workflow_delete'])" v-if="checkPermission(['workflow_delete'])"
type="danger" type="danger"
@click="handleDelete(scope)" @click="handleDelete(scope)"
>删除</el-link >删除</el-link>
> <el-link
type="primary"
@click="handleTicket(scope)"
>查看工单</el-link>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -91,20 +89,19 @@
</el-card> </el-card>
<el-dialog <el-dialog
:visible.sync="dialogVisible" :visible.sync="dialogVisible"
:title="dialogType === 'edit' ? '编辑设备' : '新增设备'" :title="dialogType === 'edit' ? '编辑工作流' : '新增工作流'">
>
<el-form <el-form
ref="Form" ref="Form"
:model="workflow" :model="workflow"
label-width="80px" label-width="100px"
label-position="right" label-position="right"
:rules="rule1" :rules="rule1">
>
<el-form-item label="名称" prop="name"> <el-form-item label="名称" prop="name">
<el-input v-model="workflow.name" placeholder="名称" /> <el-input v-model="workflow.name" placeholder="名称" />
</el-form-item> </el-form-item>
<el-form-item label="流水号前缀">
<el-input v-model="workflow.sn_prefix " placeholder="流水号前缀" />
</el-form-item>
<el-form-item label="描述" prop="description"> <el-form-item label="描述" prop="description">
<el-input <el-input
type="textarea" type="textarea"
@ -120,40 +117,14 @@
> >
<el-switch v-model="workflow.view_permission_check"></el-switch> <el-switch v-model="workflow.view_permission_check"></el-switch>
</el-form-item> </el-form-item>
<el-form-item <el-form-item label="展现表单字段">
label="限制表达式" <el-transfer
prop="limit_expression"
label-width="120px"
>
<vue-json-editor
v-model="workflow.limit_expression"
:showBtns="false"
:mode="'code'"
lang="zh"
@json-change="onJsonChange"
@json-save="onJsonSave"
@has-error="onError"
/>
<br>
</el-form-item>
<el-form-item
label="展现表单字段"
prop="display_form_str"
label-width="120px"
>
<vue-json-editor
v-model="workflow.display_form_str" v-model="workflow.display_form_str"
:showBtns="false" :data="choiceOption"
:mode="'code'" :titles="['未展示字段', '展示字段']"
lang="zh" :props="{ key: 'id', label: 'field_name' }"
@json-change="onJsonChange1"
@json-save="onJsonSave1"
@has-error="onError1"
/> />
<br>
</el-form-item> </el-form-item>
<el-form-item label="标题模板" prop="title_template"> <el-form-item label="标题模板" prop="title_template">
<el-input v-model="workflow.title_template" placeholder="你有一个待办工单:{title}" /> <el-input v-model="workflow.title_template" placeholder="你有一个待办工单:{title}" />
</el-form-item> </el-form-item>
@ -169,30 +140,40 @@
</div> </div>
</template> </template>
<script> <script>
import { getWorkflowList, createWorkflow,updateWorkflow,deleteWorkflow } from "@/api/workflow"; import { getWorkflowList, createWorkflow,updateWorkflow,deleteWorkflow,getWfCustomfieldList,getWfStateList } from "@/api/workflow";
import checkPermission from "@/utils/permission"; import checkPermission from "@/utils/permission";
import vueJsonEditor from 'vue-json-editor' // import vueJsonEditor from 'vue-json-editor'
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
const defaultworkflow = { const defaultworkflow = {
name: "", name: "",
number: "", number: "",
}; };
export default { export default {
components: { Pagination,vueJsonEditor }, components: { Pagination },
data() { data() {
return { return {
workflow: defaultworkflow, // workflow: defaultworkflow,
workflow:{
name:'',
sn_prefix:'',
description:'',
view_permission_check:'',
display_form_str:'',
title_template:'',
content_template:'',
},
view_permission_check:false, view_permission_check:false,
hasJsonFlag:true, // json是否验证通过 hasJsonFlag:true, // json是否验证通过
hasJsonFlag1:true, // json是否验证通过 hasJsonFlag1:true, // json是否验证通过
workflowList: { workflowList: {
count: 0, count: 0,
}, },
listQuery: { listQuery: {
page: 1, page: 1,
page_size: 20, page_size: 20,
}, },
choiceOption:[],
display_form_str:[], display_form_str:[],
limit_expression:[], limit_expression:[],
listLoading: true, listLoading: true,
@ -211,7 +192,6 @@ export default {
}, },
methods: { methods: {
checkPermission, checkPermission,
getList() { getList() {
this.listLoading = true; this.listLoading = true;
getWorkflowList(this.listQuery).then((response) => { getWorkflowList(this.listQuery).then((response) => {
@ -221,8 +201,6 @@ export default {
this.listLoading = false; this.listLoading = false;
}); });
}, },
handleFilter() { handleFilter() {
this.listQuery.page = 1; this.listQuery.page = 1;
this.getList(); this.getList();
@ -242,7 +220,6 @@ export default {
this.$refs["Form"].clearValidate(); this.$refs["Form"].clearValidate();
}); });
}, },
handleEdit(scope) { handleEdit(scope) {
this.workflow = Object.assign({}, scope.row); // copy obj this.workflow = Object.assign({}, scope.row); // copy obj
this.dialogType = "edit"; this.dialogType = "edit";
@ -250,12 +227,17 @@ export default {
this.$nextTick(() => { this.$nextTick(() => {
this.$refs["Form"].clearValidate(); this.$refs["Form"].clearValidate();
}); });
getWfCustomfieldList(scope.row.id).then((response) => {
if (response.data) {
this.choiceOption = response.data;
}
});
}, },
handlecfgt(scope) handlecfgt(scope)
{ {
this.$router.push({name:"configuration",params:{workflow:scope.row.id}}) this.$router.push({name:"configuration",params:{workflow:scope.row.id}})
} },
,
handleDelete(scope) { handleDelete(scope) {
this.$confirm("确认删除?", "警告", { this.$confirm("确认删除?", "警告", {
confirmButtonText: "确认", confirmButtonText: "确认",
@ -271,8 +253,13 @@ export default {
console.error(err); console.error(err);
}); });
}, },
handleTicket(scope){
this.$router.push({name:"ticket",params:{workflow:scope.row.id}})
},
async confirm(form) { async confirm(form) {
debugger;
console.log(this.workflow.display_form_str)
this.$refs[form].validate((valid) => { this.$refs[form].validate((valid) => {
if (valid) { if (valid) {
const isEdit = this.dialogType === "edit"; const isEdit = this.dialogType === "edit";
@ -289,7 +276,6 @@ export default {
} else { } else {
this.checkJson(); this.checkJson();
this.checkJson2(); this.checkJson2();
createWorkflow(this.workflow).then((res) => { createWorkflow(this.workflow).then((res) => {
if (res.code >= 200) { if (res.code >= 200) {
this.getList(); this.getList();
@ -304,7 +290,6 @@ export default {
}); });
}, },
onJsonChange (value) { onJsonChange (value) {
// console.log('更改value:', value); // console.log('更改value:', value);
// 实时保存 // 实时保存
@ -361,8 +346,6 @@ export default {
return true return true
} }
}, },
}, },
}; };
</script> </script>

View File

@ -0,0 +1,133 @@
<template>
<div id="container" style="border: 1px solid #000000;">
<svg width="900" height="1500">
<g />
<rect />
</svg>
</div>
</template>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
import dagreD3 from "dagre-d3";
import * as d3 from "d3";
export default {
data() {
return {
nodes: [
{
id: "2",
label: "开始",
shape: "rect"
},
{
id: "3",
label: "部门领导审批",
shape: "diamond"
},
{
id: "4",
label: "综合办公室审批",
shape: "diamond"
},
{
id: "5",
label: "结束",
shape: "rect"
},
],
edges: [
{
source: "2",
target: "3",
label: ""
},
{
source: "3",
target: "4",
label: "同意"
},
{
source: "4",
target: "5",
label: "同意"
},
]
};
},
mounted() {
//获取D3
var g = new dagreD3.graphlib.Graph().setGraph({
align: 'DL',
nodesep: 100,
edgesep: 100,
ranksep: 50,
marginx: 50,
marginy: 100,
});
console.log(g);
// 添加节点
this.nodes.forEach((item) => {
g.setNode(item.id, {
// 节点标签
label: item.label,
// 节点形状
shape: item.shape,
toolText: item.label,
//节点样式
style: "fill:#fff;stroke:#000",
// 节点样式
labelStyle: "fill:#000;",
width: 83,
height: 40,
rx :5,
ry :5
});
});
// 链接关系
this.edges.forEach(item => {
g.setEdge(item.source, item.target, {
// 边标签
label: item.label,
// 边样式
style: "fill:#ffffff;stroke:#c0c1c3;stroke-width:1.5px" // 根据后台数据来改变连线的颜色
});
});
g.nodes().forEach(function (v) {
console.log("Node " + v + ": " + JSON.stringify(g.node(v)));
});
g.edges().forEach(function (e) {
console.log("Edge " + e.v + " -> " + e.w + ": " + JSON.stringify(g.edge(e)));
});
// 创建渲染器
let render = new dagreD3.render();
// 选择 svg 并添加一个g元素作为绘图容器.
let svg = d3.select('svg');
let svgGroup = svg.select('g');
// 在绘图容器上运行渲染器生成流程图.
render(d3.select("svg g"), g);
}
};
</script>
<style scoped>
svg {
}
.node rect {
stroke: #606266;
fill: #fff;
}
.edgePath path {
stroke: #606266;
fill: #333;
stroke-width: 1.5px;
}
#__SVG_SPRITE_NODE__{
position: absolute!important;
width: 689px!important;
height: 800px!important;
left: 300px!important;
top: 331px!important;
}
</style>

View File

@ -0,0 +1,238 @@
<template>
<div class="app-container">
<el-card style="margin-top: 10px">
<el-table v-loading="listLoading" :data="tickets" border fit stripe highlight-current-row max-height="600">
<el-table-column type="index" width="50" />
<el-table-column label="工单标题">
<template slot-scope="scope">{{ scope.row.title }}</template>
</el-table-column>
<el-table-column label="当前状态">
<template slot-scope="scope">{{ scope.row.act_state }}</template>
</el-table-column>
<el-table-column width="180" label="创建时间">
<template slot-scope="scope">{{ scope.row.create_time }}</template>
</el-table-column>
<el-table-column align="center" label="操作" width="220px">
<template slot-scope="scope">
<el-link v-if="stateSteps==scope.row.act_state" type="danger" @click="handlePicture(scope)">查看流程图</el-link>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
:page.sync="pageForm.page"
:limit.sync="pageForm.page_size"
@pagination="getList"
/>
</el-card>
<div class="svgMark" v-if="dialogVisible" @click="closeMark">
<div class="svgWrapper">
<div class="svgItem">工单流程图<i class="el-dialog__close el-icon el-icon-close" @click="closeMark"></i></div>
<svg height=600 id="svg">
<g id="svgG"/>
<rect/>
</svg>
</div>
</div>
</div>
</template>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
import {getWfStateList,getTickets } from "@/api/workflow";
import Pagination from "@/components/Pagination";
import dagreD3 from 'dagre-d3'
import * as d3 from 'd3'
export default {
name: "ticket",
components: { Pagination },
data(){
return{
total:0,
pageForm:{
page:1,
page_size:20,
workflow:0,
},
workflow:0,
stateSteps:0,
keyword:'',
tickets:[],
dialogVisible:false,
listLoading:false,
nodes: [],
tooltip:null,
edges: []
}
},
created(){
this.workflow = this.$route.params.workflow;
let workflow = localStorage.getItem('workflow');
if(this.workflow){
this.pageForm.workflow = parseInt(this.workflow);
if(workflow){
localStorage.removeItem('workflow');
localStorage.setItem('workflow',this.$route.params.workflow)
}else{
localStorage.setItem('workflow',this.$route.params.workflow)
}
}else{
this.workflow =workflow ;
this.pageForm.workflow =workflow ;
}
this.getList();
this.getStates();
},
methods:{
getList(){
getTickets( this.pageForm).then((res)=>{
if(res.data.results){
this.tickets = res.data.results;
}
})
},
getStates(){
getWfStateList(this.workflow).then((response) => {
if (response.data) {
let nodes = [];
let res = response.data;
this.stateSteps = res.length;
for(let i=0;i<res.length;i++){
let obj = new Object();
obj.id = res[i].id;
obj.label = res[i].name;
obj.shape = res[i].type===0? 'diamond':'rect';
nodes.push(obj)
}
this.nodes = nodes;
console.log(nodes)
this.getEdges(nodes);
}
});
},
getEdges(nodes){
let edge = [];
for(let i=1;i<nodes.length;i++){
let obj = new Object();
obj.source = nodes[i-1].id;
obj.target = nodes[i].id;
obj.label = i>1?'同意':'';
edge.push(obj);
}
this.edges = edge;
console.log(edge)
},
handleFilter(){},
handlePicture(){
// this.$router.push({name:"test"})
this.dialogVisible = true;
//获取D3
this.$nextTick(()=>{
var g = new dagreD3.graphlib.Graph().setGraph({
align: 'DL',
nodesep: 100,
edgesep: 100,
ranksep: 50,
marginx: 50,
marginy: 50,
});
console.log(g);
// 添加节点
this.nodes.forEach((item) => {
g.setNode(item.id, {
// 节点标签
label: item.label,
// 节点形状
shape: item.shape,
toolText: item.label,
//节点样式
style: "fill:#fff;stroke:#000",
// 节点样式
labelStyle: "fill:#000;",
width: 83,
height: 40,
rx :5,
ry :5
});
});
// 链接关系
this.edges.forEach(item => {
g.setEdge(item.source, item.target, {
// 边标签
label: item.label,
// 边样式
style: "fill:#ffffff;stroke:#c0c1c3;stroke-width:1.5px" // 根据后台数据来改变连线的颜色
});
});
g.nodes().forEach(function (v) {
console.log("Node " + v + ": " + JSON.stringify(g.node(v)));
});
g.edges().forEach(function (e) {
console.log("Edge " + e.v + " -> " + e.w + ": " + JSON.stringify(g.edge(e)));
});
// 创建渲染器
let render = new dagreD3.render();
// 选择 svg 并添加一个g元素作为绘图容器.
let svg = d3.select('svg');
let svgGroup = svg.select('g');
// 在绘图容器上运行渲染器生成流程图.
render(d3.select("svg g"), g);
})
},
closeMark(){
this.dialogVisible = false;
},
},
mounted() {
}
}
</script>
<style scoped>
.svgMark{
width: 100%;
height: 100%;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
overflow: auto;
margin: 0;
z-index: 2000;
background: rgba(0,0,0,.3);
}
.svgWrapper{
background: #fff;
width: 800px;
margin: 10vh auto 0;
text-align: center;
border-radius: 2px;
}
.svgItem{
padding: 20px 40px 0 ;
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
font-size: 18px;
display: flex;
justify-content: space-between;
}
svg {
font-size: 14px;
}
.node rect {
stroke: #606266;
fill: #fff;
}
.edgePath path {
stroke: #606266;
fill: #333;
stroke-width: 1.5px;
}
.el-icon-close{
cursor: pointer;
}
</style>

3
hb_daq/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
dist/
config/*
.build/

8
hb_daq/default.aproj Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<project ver="10" name="daq" libEmbed="true" icon="" ui="win" output="数据采集.exe" CompanyName="中存大数据" FileDescription="daq" LegalCopyright="Copyright (C) 作者 2021" ProductName="daq" InternalName="daq" FileVersion="0.0.0.08" ProductVersion="0.0.0.08" publishDir="/dist/" dstrip="false">
<file name="main.aardio" path="main.aardio" comment="main.aardio"/>
<folder name="资源文件" path="res" embed="true" local="false" ignored="false"/>
<folder name="窗体文件" path="dlg" comment="目录" embed="true" local="false" ignored="false">
<file name="add.aardio" path="dlg\add.aardio" comment="dlg\add.aardio"/>
</folder>
</project>

24
hb_daq/dlg/add.aardio Normal file
View File

@ -0,0 +1,24 @@
import win.ui;
/*DSG{{*/
var winform = win.form(text="新建采集内容";right=299;bottom=179;border="dialog frame";max=false;min=false)
winform.add(
button={cls="button";text="确定";left=90;top=119;right=196;bottom=154;z=5};
edit={cls="edit";left=84;top=26;right=281;bottom=50;dl=1;dr=1;dt=1;edge=1;z=2};
edit2={cls="edit";left=84;top=69;right=281;bottom=93;dl=1;dr=1;dt=1;edge=1;z=4};
static={cls="static";text="设备编号";left=8;top=29;right=78;bottom=54;dl=1;dt=1;notify=1;transparent=1;z=1};
static2={cls="static";text="文件夹地址";left=8;top=74;right=78;bottom=99;dl=1;dt=1;notify=1;transparent=1;z=3}
)
/*}}*/
winform.button.oncommand = function(id,event){
if(winform.edit.text!="" && winform.edit2.text!=""){
winform.parent.listview.addItem({winform.edit.text;winform.edit2.text})
winform.close()
}else{
winform.msgbox("请输入信息!")}
}
winform.show();
win.loopMessage();
return winform;

20
hb_daq/lib/config.aardio Normal file
View File

@ -0,0 +1,20 @@
//config 配置文件
import fsys.config;
config = fsys.config("/config/");
//config = fsys.config( io.appData("/软件作者/应用程序名/") );
//不需要序列化的配置名字前请添加下划线
namespace config {
__appName = "应用程序名";
__appVersion = "1.0.0.01";
__appDescription = "这是一个测试程序";
__website = "http://www.aardio.com/";
}
/**intellisense(config)
__appName = 应用程序名
__appVersion = 应用程序内部版本号
__appDescription = 程序说明
__website = 官方网站
? = 配置文件名,\n读写配置并序列化为一个表对象,\n表的成员值可以是支持序列化的普通变量,支持table对象\n配置文件在首次使用时自动加载,退出程序时自动保存\n!fsys_table.
end intellisense**/

208
hb_daq/main.aardio Normal file
View File

@ -0,0 +1,208 @@
import win.ui;
import win.inputBox;
import win.ui.menu;
import win.util.tray;
import win.ui.atom;
import win.timer;
import console;
import win.timer;
import fsys;
import web.multipartFormData;
import inet.http;
import fsys.config;
/*DSG{{*/
mainForm = win.form(text="数据采集";right=799;bottom=399;border="dialog frame";max=false)
mainForm.add(
add={cls="button";text="新建";left=27;top=130;right=98;bottom=154;font=LOGFONT(h=-14;name='微软雅黑');z=6};
backupPath={cls="edit";left=107;top=58;right=337;bottom=84;edge=1;font=LOGFONT(h=-14;name='微软雅黑');z=11};
backupPathLabel={cls="static";text="备份文件夹";left=31;top=60;right=118;bottom=81;font=LOGFONT(h=-14;name='微软雅黑');notify=1;transparent=1;z=10};
groupbox={cls="groupbox";text="采集内容";left=18;top=110;right=346;bottom=390;edge=1;z=5};
groupbox2={cls="groupbox";text="采集日志";left=353;top=7;right=792;bottom=390;ah=1;aw=1;db=1;dr=1;edge=1;z=1};
groupbox3={cls="groupbox";text="基础配置";left=18;top=7;right=346;bottom=102;edge=1;z=2};
listbox={cls="listbox";left=360;top=24;right=785;bottom=387;edge=1;hscroll=1;items={};vscroll=1;z=12};
listview={cls="listview";left=27;top=161;right=337;bottom=384;edge=1;fullRow=1;items={};msel=false;z=7};
serverUrl={cls="edit";left=107;top=25;right=337;bottom=51;edge=1;font=LOGFONT(h=-14;name='微软雅黑');z=4};
serverUrlLabel={cls="static";text="服务器地址";left=31;top=28;right=118;bottom=49;font=LOGFONT(h=-14;name='微软雅黑');notify=1;transparent=1;z=3};
start={cls="button";text="开始采集";left=266;top=130;right=337;bottom=154;font=LOGFONT(h=-14;name='微软雅黑');z=8};
statusLabel={cls="static";left=143;top=130;right=221;bottom=154;align="center";color=32768;font=LOGFONT(h=-16;name='微软雅黑';weight=700);transparent=1;z=9}
)
/*}}*/
/*
inputbox = win.inputBox(mainForm.hwnd)
inputbox.text = "输入框标题"
inputbox.info.text = "请在下面输入您的名字"
inputbox.input.text = "在这里输入您的名字"
name = inputbox.doModal();
if(name)
win.msgbox("您的名字是:"+name )
else
win.msgbox("你拒绝输入名字")
*/
mainForm.listview.adjust = function(cx,cy){
mainForm.listview.fillParent(/*列序号*/);
}
mainForm.listview.insertColumn("设备编号",100,,0x0/*_LVCFMT_LEFT*/)
mainForm.listview.insertColumn("文件夹地址",100,,0x0/*_LVCFMT_LEFT*/) //第二列开始可以使用_LVCFMT_CENTER居中
config = fsys.config("/config/")
mainForm.bindConfig( config.winform,{
serverUrl = "text";
backupPath = "text";
listview = "items";
} );
if(mainForm.serverUrl.text==""){
mainForm.serverUrl.text='http://127.0.0.1:8000'
}
if(mainForm.backupPath.text==""){
mainForm.backupPath.text="D:\Daq\Backup"
}
/*
if(table.count(mainForm.listview.items)==0){
mainForm.listview.items = {{"JN102";"D:\tmp"}};
}
*/
var isWorking = false;
var atom,hwnd/*冲突窗口的句柄,该函数会自动激活此窗口*/ = mainForm.atom("33D501DF-BFC2-4283-8BBE-AF17AADB1C27");
if(!atom){
/*为窗口设置原子值可以避免一个程序重复运行多个实例*/
win.quitMessage(); return;
}
mainForm.onMinimize = function(lParam){
var tray = win.util.tray(mainForm) //创建托盘图标
tray.tip = "数据采集";
mainForm.show(false); //隐藏窗口
return true;//阻击默认消息传递,取消最小化过程
}
mainForm.wndproc = {
[0xACCF/*_WM_TRAYMESSAGE*/ ] = function(hwnd,message,wParam,lParam){
if( lParam = 0x205/*_WM_RBUTTONUP*/ ){
var pt = ::POINT();
::User32.GetCursorPos(pt);
//弹出托盘菜单以前,一定要前置主窗口中,不然不点击菜单不会消失
win.setForeground(mainForm.hwnd)
mainForm.popmenu.popup(pt.x,pt.y,true )
}elseif(lParam = 0x203/*_WM_LBUTTONDBLCLK*/){
mainForm.show()
}
}
}
mainForm.popmenu = win.ui.popmenu(mainForm);//创建弹出菜单
mainForm.popmenu.add('&打开',function(id){
//在下面输入菜单响应代码
mainForm.show()
});
mainForm.popmenu.add();//分隔线
mainForm.popmenu.add('&退出',function(id){ mainForm.close() })
mainForm.start.oncommand = function(id,event){
toggleWorking();
}
var timer = win.timer( mainForm );
timer.setInterval(5000)
function toggleWorking(){
if(isWorking == false){
mainForm.serverUrl.disabled = true
mainForm.backupPath.disabled = true
mainForm.add.disabled = true
mainForm.statusLabel.text="采集中..."
mainForm.start.text="停止采集"
isWorking = true
timer.enable()
}else{
mainForm.serverUrl.disabled = false
mainForm.backupPath.disabled = false
mainForm.add.disabled = false
mainForm.statusLabel.text=""
mainForm.start.text="开始采集"
isWorking=false
timer.disable()
}
}
if(mainForm.serverUrl && table.count(mainForm.listview.items)>0){
toggleWorking();
}
timer.onTimer = function(hwnd,msg,id,tick){
for itemIndex in mainForm.listview.each(){
var number, dir = mainForm.listview.items[itemIndex][1], mainForm.listview.items[itemIndex][2]
//批量处理文件
fsys.enum( dir, //指定要遍历的目录
"*.*", //指定查询文件名,支持windows掩码
function(dir,filename,fullpath,findData){ //指定触发器
if(filename){
mainForm.listbox.add(tostring(time.now(),"%Y-%m-%d %H:%M:%S","chs") + ":新文件-"+filename, -1)
mainForm.listbox.add("正在上传并解析...")
var webData = web.multipartFormData();
webData.add("equip_num", number)
webData.add("file", "@"+fullpath)
webData.contentHeader()
var http = inet.http();
/*
http.beginRequest( mainForm.serverUrl.text + "/api/em/daq/", "POST" );
//小数据一次性上传
http.beginSendData(webData.size());
http.writeData(webData.readAll())
http.endSendData();
*/
var html,err,errCode = http.post( mainForm.serverUrl.text + "/api/em/daq/",webData.readAll(),webData.contentHeader());
if(html){
var res = web.json.parse(html)
if(res['code']==200){
import fsys
var theDir = fsys.createDir(mainForm.backupPath.text+"\"+number, false)
fsys.move(fullpath, theDir) //移动到备份文件库
mainForm.listbox.add("采集成功!")
}else{
mainForm.listbox.add("失败:"+res['msg'])
}
}else{
mainForm.listbox.add("失败:请求错误")
}
}
}, false
);
}
}
mainForm.listview.onnotify = function(id,code,ptr){
select(code) {
case 0xFFFFFFFB/*_NM_RCLICK*/ {
var x,y = win.getCursorPos();
var popmenu = win.ui.popmenu(mainForm);//创建弹出菜单
popmenu.add('删除',function(id){
//在下面输入菜单响应代码
mainForm.listview.delItem( mainForm.listview.selIndex )
});
popmenu.popup(x,y,true);//弹出菜单
}
}
}
mainForm.add.oncommand = function(id,event){
var frmChild = mainForm.loadForm("\dlg\add.aardio");
frmChild.doModal();
}
mainForm.show();
return win.loopMessage();

View File

@ -1,3 +1,4 @@
from rest_framework import serializers
from rest_framework.serializers import ModelSerializer from rest_framework.serializers import ModelSerializer
from .models import Equipment,Equipmentrecord from .models import Equipment,Equipmentrecord
@ -35,3 +36,7 @@ class EquipmentrecordSerializer(ModelSerializer):
""" Perform necessary eager loading of data. """ """ Perform necessary eager loading of data. """
queryset = queryset.select_related('equipment') queryset = queryset.select_related('equipment')
return queryset return queryset
class DaqCreateSerializer(serializers.Serializer):
number = serializers.CharField()
file = serializers.FileField()

View File

@ -1,6 +1,6 @@
from django.db.models import base from django.db.models import base
from rest_framework import urlpatterns from rest_framework import urlpatterns
from apps.em.views import EquipmentViewSet,EquipmentrecordViewSet from apps.em.views import DaqView, EquipmentViewSet,EquipmentrecordViewSet
from django.urls import path, include from django.urls import path, include
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
@ -8,6 +8,7 @@ router = DefaultRouter()
router.register('equipment', EquipmentViewSet, basename='equipment') router.register('equipment', EquipmentViewSet, basename='equipment')
router.register('equipmentrecord', EquipmentrecordViewSet, basename='equipmentrecord') router.register('equipmentrecord', EquipmentrecordViewSet, basename='equipmentrecord')
urlpatterns = [ urlpatterns = [
path('daq/', DaqView.as_view()),
path('', include(router.urls)), path('', include(router.urls)),
] ]

View File

@ -1,10 +1,12 @@
from django.shortcuts import render from django.shortcuts import render
from rest_framework.exceptions import APIException
from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from rest_framework import serializers, status from rest_framework import serializers, status
from rest_framework.response import Response from rest_framework.response import Response
from apps.em.models import Equipment,Equipmentrecord from apps.em.models import Equipment,Equipmentrecord
from apps.em.serializers import EquipmentSerializer,EquipmentrecordSerializer from apps.em.serializers import DaqCreateSerializer, EquipmentSerializer,EquipmentrecordSerializer
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
@ -58,3 +60,27 @@ class EquipmentrecordViewSet(CreateUpdateModelAMixin, OptimizationMixin, ModelVi
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
serializer.save() serializer.save()
return Response(status=status.HTTP_200_OK) return Response(status=status.HTTP_200_OK)
import uuid
import os
from django.conf import settings
from rest_framework.parsers import MultiPartParser
class DaqView(APIView):
"""
数据采集
"""
authentication_classes = []
permission_classes = []
parser_classes = [MultiPartParser]
def post(self, request, format=None):
data = request.data
file = data.get('file', None)
equip_num = data.get('equip_num', None)
filename = str(uuid.uuid4())
filepath = settings.BASE_DIR +'/temp/' + filename + os.path.splitext(file.name)[-1]
with open(filepath, 'wb') as f:
for chunk in file.chunks():
f.write(chunk)
return Response()
# raise APIException('解析失败')