This commit is contained in:
lcw
2025-08-16 16:54:03 +08:00
parent 71487ac647
commit 42f5e37f65
69 changed files with 5913 additions and 978 deletions

View File

@ -1,7 +1,7 @@
<template>
<el-dialog v-model="showDialog" :destroy-on-close="true" title="新增车辆" @close="close" :close-on-click-modal="false">
<FormMessage v-model="listQuery" :formList="formData" labelWidth="120px" ref="elform" :rules="rules">
<template #bqList>
<!-- <template #bqList>
<div class="marks pointer" @click="chooseMarksVisible = true">
<span style="color: rgb(175 178 184);padding-left: 10px;"
v-if="!listQuery.bqList || listQuery.bqList.length == 0">请选择标签</span>
@ -10,7 +10,7 @@
:key="idx">{{ it.bqMc }}</el-tag>
</span>
</div>
</template>
</template> -->
</FormMessage>
<template #footer>
<div class="flex just-center">
@ -27,7 +27,7 @@ import ChooseMarks from "@/components/ChooseList/ChooseMarks/index.vue";
import FormMessage from "@/components/aboutTable/FormMessage.vue";
import { reactive, ref, getCurrentInstance } from 'vue';
const { proxy } = getCurrentInstance();
const { D_BZ_XB } = proxy.$dict("D_BZ_XB"); // 获取字典数据
const { D_GS_ZDR_YJDJ ,BD_BK_CLYJBQ} = proxy.$dict("D_GS_ZDR_YJDJ",'BD_BK_CLYJBQ'); // 获取字典数据
const elform = ref()
const roleIds = ref([])
const showDialog = ref(false)
@ -35,46 +35,42 @@ const chooseMarksVisible = ref(false)
const emit = defineEmits(['change'])
const listQuery = ref({})
const formData = ref([
{ label: "车牌号", prop: "hphm", type: "input" },
{ label: "车架号", prop: "clCjh", type: "input" },
{ label: "车牌号", prop: "clCph", type: "input" },
{
label: "车辆颜色",
prop: "clYs",
type: "input",
},
{ label: "车辆所有人", prop: "clSyr", type: "input" },
{ label: "人身份证", prop: "clSyrsfzh", type: "input" },
{ label: "责任单位", prop: "zrSsbmdm", depMc: 'zrSsbmmc', type: "department" },
{ label: "管辖单位", prop: "gxSsbmdm", depMc: 'gxSsbmmc', type: "department" },
{ label: "管控民警姓名", prop: "gkMjXm", type: "input" },
{ label: "管控民警警号", prop: "gkMjJh", type: "input" },
{ label: "管控原因", prop: "clLkyy", type: "textarea", width: "100%" },
{ label: "所有人身份证", prop: "clSyrsfzh", type: "input" },
{ label: "所属部门", prop: "ssbmdm", type: "department" },
{ label: "预警等级", prop: "yjdj", type: "select", options: D_GS_ZDR_YJDJ },
{ label: "预警标签", prop: "yjbq", type: "select", options: BD_BK_CLYJBQ },
{ label: "车辆照片", prop: "fjdz", type: "upload", width: "100%" },
])
const rules = reactive({
hphm: [{ required: true, message: "请输入车牌号", trigger: "blur" }],
clCjh: [{ required: true, message: "请输入车架号", trigger: "blur" }],
clYs: [{ required: true, message: "请输入车辆颜色", trigger: "blur" }],
clSyr: [{ required: true, message: "请输入车辆所有人", trigger: "blur" }],
clSyrsfzh: [{ required: true, message: "请输入人员身份证", trigger: "blur" }],
clCph: [{ required: true, message: "请输入车牌号", trigger: "blur" }],
yjdj: [{ required: true, message: "请选择预警等级", trigger: "change" }],
yjbq: [{ required: true, message: "请选择预警标签", trigger: "change" }],
ssbmdm:[{ required: true, message: "请选择所属部门", trigger: "change" }]
})
const init = () => {
showDialog.value = true;
}
// 选择标签
const choosed = (val) => {
listQuery.value.bqList = val.map(v => {
return { bqZl: v.bqLb, bqId: v.id, bqLx: v.bqLx, bqLb: v.bqLb, bqMc: v.bqMc, bqDm: v.bqDm }
});
roleIds.value = val.map(v => v.id)
}
// const choosed = (val) => {
// listQuery.value.bqList = val.map(v => {
// return { bqZl: v.bqLb, bqId: v.id, bqLx: v.bqLx, bqLb: v.bqLb, bqMc: v.bqMc, bqDm: v.bqDm }
// });
// roleIds.value = val.map(v => v.id)
// }
// 删除数据
const closeTag = (idx) => {
listQuery.value.bqList.splice(idx, 1)
roleIds.value.splice(idx, 1)
}
// const closeTag = (idx) => {
// listQuery.value.bqList.splice(idx, 1)
// roleIds.value.splice(idx, 1)
// }
const submitForm = () => {
elform.value.submit((val) => {
@ -86,6 +82,7 @@ const submitForm = () => {
const close = () => {
elform.value.reset();
listQuery.value = {}
listQuery.value.bqList = []
roleIds.value = []
showDialog.value = false;

View File

@ -1,46 +1,50 @@
<template>
<el-dialog width="1400px" :model-value="modelValue" append-to-body @close="closed">
<template #title>
<span class="mr10 f16">选择布控车辆</span>
<el-button type="primary" size="small" @click="zdyaddPerson">添加其他车辆</el-button>
<span class="mr10 f16">选择布控车辆</span>
<el-button type="primary" size="small" @click="zdyaddPerson">添加其他车辆</el-button>
</template>
<el-form :model="listQuery" :inline="true">
<el-form-item label="车牌号">
<el-input placeholder="请输入车牌号" v-model="listQuery.hphm" clearable ></el-input>
</el-form-item>
<el-form-item label="车架号">
<el-input placeholder="请输入车架号" v-model="listQuery.clCjh" clearable ></el-input>
</el-form-item>
<el-form-item label="车辆所有人">
<el-input placeholder="请输入车辆所有人" v-model="listQuery.clSyr" clearable ></el-input>
</el-form-item>
<el-form-item>
<el-button type="success" @click="handleFilter">查询</el-button>
<el-button type="info" @click="reset()"> 重置 </el-button>
</el-form-item>
<el-form-item label="车牌号">
<el-input placeholder="请输入车牌号" v-model="listQuery.hphm" clearable></el-input>
</el-form-item>
<el-form-item label="车架号">
<el-input placeholder="请输入车架号" v-model="listQuery.clCjh" clearable></el-input>
</el-form-item>
<el-form-item label="车辆所有人">
<el-input placeholder="请输入车辆所有人" v-model="listQuery.clSyr" clearable></el-input>
</el-form-item>
<el-form-item>
<el-button type="success" @click="handleFilter">查询</el-button>
<el-button type="info" @click="reset()"> 重置 </el-button>
</el-form-item>
</el-form>
<div class="tabBox" :class="props.Single ? 'tabBoxRadio' : ''" style="margin-top: 0px">
<el-table ref="multipleUserRef" :key="keyTabel" v-loading="loading" @selection-change="handleSelectionChange" :data="tableData" border :row-key="keyid" style="width: 100%" height="450">
<el-table-column type="selection" width="55" :reserve-selection="true"/>
<el-table-column prop="hphm" align="center" label="车牌号"/>
<el-table-column prop="clCjh" align="center" label="车号"/>
<el-table-column prop="clYs" align="center" label="车辆颜色"/>
<el-table-column prop="clSyr" align="center" label="车辆所有人"/>
<el-table-column prop="gxSsbmmc" align="center" label="管辖单位"/>
<el-table-column prop="gkMjXm" align="center" label="管控民警"/>
</el-table>
<el-table ref="multipleUserRef" :key="keyTabel" v-loading="loading" @selection-change="handleSelectionChange"
:data="tableData" border :row-key="keyid" style="width: 100%" height="450">
<el-table-column type="selection" width="55" :reserve-selection="true" />
<el-table-column prop="hphm" align="center" label="车号" />
<el-table-column prop="clCjh" align="center" label="车架号" />
<el-table-column prop="clYs" align="center" label="车辆颜色" />
<el-table-column prop="clSyr" align="center" label="车辆所有人" />
<el-table-column prop="gxSsbmmc" align="center" label="管辖单位" />
<el-table-column prop="gkMjXm" align="center" label="管控民警" />
<el-table-column prop="gxSsbmmc" align="center" label="预警等级">
<template #default="scope">
<DictTag :tag="false" :value="scope.row.yjdj" :options="D_GS_ZDR_YJDJ" />
</template>
</el-table-column>
<el-table-column prop="yjbq" align="center" label="预警标签">
<template #default="scope">
<DictTag :tag="false" :value="scope.row.yjbq" :options="BD_BK_CLYJBQ" />
</template>
</el-table-column>
</el-table>
</div>
<div class="fenye flex just-end " :style="{ top: tableHeight + 'px' }">
<el-pagination
class="pagination"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="listQuery.pageCurrent"
:page-sizes="[10, 20, 50, 100]"
:page-size="listQuery.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
></el-pagination>
<el-pagination class="pagination" @size-change="handleSizeChange" @current-change="handleCurrentChange"
:current-page="listQuery.pageCurrent" :page-sizes="[10, 20, 50, 100]" :page-size="listQuery.pageSize"
layout="total, sizes, prev, pager, next, jumper" :total="total"></el-pagination>
</div>
<template #footer>
<div class="flex just-center">
@ -56,14 +60,15 @@
<script setup>
import AddPeo from './addPeo.vue'
import { qcckGet} from "@/api/qcckApi.js";
import { defineProps, ref ,getCurrentInstance, watch} from "vue";
import { qcckGet } from "@/api/qcckApi.js";
import { Dict } from 'core-js';
import { defineProps, ref, getCurrentInstance, watch } from "vue";
const { proxy } = getCurrentInstance();
const { D_BZ_XB,D_BZ_MZ } = proxy.$dict("D_BZ_XB","D_BZ_MZ"); //获取字典数据
const { BD_BK_CLYJBQ, D_GS_ZDR_YJDJ } = proxy.$dict("BD_BK_CLYJBQ", "D_GS_ZDR_YJDJ"); //获取字典数据
const props = defineProps({
modelValue: {
type: Boolean,
default:false
default: false
},
LeaderType: {
type: String,
@ -89,7 +94,7 @@ const addPerson = ref()
const multipleUserRef = ref(null);
const multipleSelectionUser = ref([]);
const tableData = ref([]);
const emits = defineEmits(["update:modelValue", "choosed","choosedAdd"]);
const emits = defineEmits(["update:modelValue", "choosed", "choosedAdd"]);
const keyTabel = ref(0)
const keyid = (row) => {
return row.id;
@ -99,7 +104,7 @@ const closed = () => {
emits("update:modelValue", false);
};
const reset = () => {
listQuery.value = { pageCurrent: 1, pageSize: 20, };
listQuery.value = { pageCurrent: 1, pageSize: 20, };
getListData();
};
@ -140,14 +145,14 @@ const handleCurrentChange = (currentPage) => {
};
const getListData = () => {
keyTabel.value++
const params = {...listQuery.value}
const params = { ...listQuery.value }
loading.value = true;
qcckGet(params,'/mosty-gsxt/tbGsxtZdcl/selectPage').then(res=>{
qcckGet(params, '/mosty-gsxt/tbGsxtZdcl/selectPage').then(res => {
loading.value = false;
tableData.value = res.records || [];
total.value = res.total;
multipleUser();
}).catch(()=>{
}).catch(() => {
loading.value = false;
})
};
@ -155,7 +160,7 @@ const getListData = () => {
//列表回显
function multipleUser() {
tableData.value.forEach((item) => {
multipleUserRef.value.toggleRowSelection(item, false);
multipleUserRef.value.toggleRowSelection(item, false);
if (props.roleIds.some((id) => id == item.id)) {
multipleUserRef.value.toggleRowSelection(item, true);
}
@ -183,9 +188,9 @@ const zdyaddPerson = () => {
addPerson.value.init();
};
watch(()=>props.modelValue,val=>{
if(val) handleFilter();
},{immediate:true})
watch(() => props.modelValue, val => {
if (val) handleFilter();
}, { immediate: true })
</script>
@ -197,8 +202,8 @@ watch(()=>props.modelValue,val=>{
.tabBoxRadio .el-checkbox__inner {
border-radius: 50% !important;
}
.tabBoxRadio .el-table__header-wrapper .el-checkbox {
display: none;
}
</style>

View File

@ -25,7 +25,7 @@ import ChooseMarks from "@/components/ChooseList/ChooseMarks/index.vue";
import FormMessage from "@/components/aboutTable/FormMessage.vue";
import { reactive, ref,getCurrentInstance } from 'vue';
const { proxy } = getCurrentInstance();
const { D_BZ_XB } = proxy.$dict("D_BZ_XB"); // 获取字典数据
const { D_BZ_XB,D_GS_ZDR_YJDJ } = proxy.$dict("D_BZ_XB","D_GS_ZDR_YJDJ"); // 获取字典数据
const elform = ref()
const roleIds = ref([])
const showDialog = ref(false)
@ -37,21 +37,26 @@ const formData = ref([
{ label: "性别", prop: "ryXb", type: "select" ,width:'45%',options:D_BZ_XB},
{ label: "身份证号", prop: "rySfzh", type: "input" ,width:'45%'},
{ label: "手机号码", prop: "ryLxdh", type: "input",width:'45%' },
{ label: "户籍地址", prop: "hjdXz", type: "input",width:'100%'},
{ label: "户籍地派出所", prop: "hjdpcsdm",depMc:'hjdpcs', type: "department" ,width:'48%'},
{ label: "户籍地址", prop: "hjdXz", type: "input", width: '100%' },
{ label: "户籍地派出所", prop: "hjdpcsdm", depMc: 'hjdpcs', type: "department", width: '48%' },
{ label: "所属部门", prop: "ssbmdm", type: "department" },
{ label: "特征描述", prop: "qtTzms", type: "input" ,width:'100%'},
{ label: "人员标签", prop: "bqList", type: "slot" ,width:'100%'},
{ label: "车牌号", prop: "clCph", type: "input" ,width:'45%'},
{ label: "车架号", prop: "clCjh", type: "input" ,width:'45%'},
{ label: "车牌号", prop: "clCph", type: "input", width: '45%' },
{ label: "预警等级", prop: "yjdj", type: "select", options: D_GS_ZDR_YJDJ },
{ label: "车辆识别代码", prop: "clCjh", type: "input" ,width:'45%'},
{ label: "人员照片", prop: "fjZp", type: "upload" ,width:'100%'},
])
const rules = reactive({
// fjZp: [{ required: true, message: "请上传人员照片", trigger: "blur" }],
ryXm: [{ required: true, message: "请输入姓名", trigger: "blur" }],
rySfzh: [{ required: true, message: "请输入身份证号", trigger: "blur" }],
ryXb: [{ required: true, message: "请选择性别", trigger: "change" }],
ryLxdh: [{ required: true, message: "请输入手机号码", trigger: "blur" }],
hjdXz: [{ required: true, message: "请输入户籍地", trigger: "blur" }],
xzdXz: [{ required: true, message: "请输入现居住地址", trigger: "blur" }],
ryXb: [{ required: true, message: "请选择性别", trigger: "change" }],
yjdj: [{ required: true, message: "请选择预警等级", trigger: "change" }],
ssbmdm:[{ required: true, message: "请选择所属部门", trigger: "change" }]
// ryLxdh: [{ required: true, message: "请输入手机号码", trigger: "blur" }],
// hjdXz: [{ required: true, message: "请输入户籍地", trigger: "blur" }],
// xzdXz: [{ required: true, message: "请输入现居住地址", trigger: "blur" }],
})
const init = () =>{
showDialog.value = true;
@ -80,7 +85,9 @@ const submitForm = () =>{
}
const close = () =>{
elform.value.reset();
elform.value.reset();
listQuery.value = {}
listQuery.value.bqList = []
roleIds.value = []
showDialog.value = false;
@ -97,4 +104,4 @@ defineExpose({init})
border: 1px solid #e9e9e9;
border-radius: 4px;
}
</style>
</style>

View File

@ -39,8 +39,13 @@
<el-table-column prop="ryLxdh" align="center" label="手机号"/>
<el-table-column prop="qtXnsf" align="center" label="虚拟身份"/>
<el-table-column prop="clCph" align="center" label="车牌号"/>
<el-table-column prop="clCjh" align="center" label="车架号"/>
<el-table-column prop="clCjh" align="center" label="车辆识别代码"/>
<el-table-column prop="qtTzms" align="center" label="特征描述"/>
<el-table-column prop="zdrYjdj" align="center" label="预警等级">
<template #default="{ row }">
<DictTag :tag="false" :value="row.zdrYjdj" :options="D_GS_ZDR_YJDJ" />
</template>
</el-table-column>
<el-table-column prop="bqList" align="center" label="人员标签">
<template #default="{ row }">
<span v-if="row.bqList">
@ -79,7 +84,7 @@ import AddPeo from './addPeo.vue'
import { qcckGet} from "@/api/qcckApi.js";
import { defineProps, ref ,getCurrentInstance, watch} from "vue";
const { proxy } = getCurrentInstance();
const { D_BZ_XB,D_BZ_MZ } = proxy.$dict("D_BZ_XB","D_BZ_MZ"); //获取字典数据
const { D_BZ_XB,D_BZ_MZ,D_GS_ZDR_YJDJ } = proxy.$dict("D_BZ_XB","D_BZ_MZ",'D_GS_ZDR_YJDJ'); //获取字典数据
const props = defineProps({
modelValue: {
type: Boolean,

View File

@ -106,6 +106,15 @@ const handleChange = (e) => {
if (props.multiple === true) {
const data = e.map((item) => {return item[item.length - 1];});
emits("update:modelValue", data);
const obj= data.map(items=>{
return {
... depList.value.find(item=>{ return item.orgCode == items})
}
})
console.log(obj);
emits("getDepValue", obj);
} else {
const data = e ? e[e.length - 1] : "";
emits("update:modelValue", data);

View File

@ -5,7 +5,9 @@
:on-exceed="handleExceed" :on-success="handlerSuccess" :before-upload="beforeImgUpload">
<template #default>
<el-button v-if="props.showBtn" size="small" type="primary">上传文件</el-button>
<el-icon v-else><Plus /></el-icon>
<el-icon v-else>
<Plus />
</el-icon>
</template>
<template #file="{ file }" v-if="!props.showBtn">
<div v-if="props.isImg">
@ -15,7 +17,9 @@
<el-icon> <zoom-in /></el-icon>
</span>
<span v-if="!disabled" class="el-upload-list__item-delete" @click="handleRemove(file, fileList)">
<el-icon><Delete /></el-icon>
<el-icon>
<Delete />
</el-icon>
</span>
</span>
</div>
@ -26,10 +30,14 @@
</div>
<span class="el-upload-list__item-actions">
<span v-if="!disabled" class="el-upload-list__item-delete" @click="handleDownload(file)">
<el-icon><Download /></el-icon>
<el-icon>
<Download />
</el-icon>
</span>
<span v-if="!disabled" class="el-upload-list__item-delete" @click="handleRemove(file, fileList)">
<el-icon><Delete /></el-icon>
<el-icon>
<Delete />
</el-icon>
</span>
</span>
</div>
@ -158,7 +166,9 @@ watch(
let arr = val ? val : [];
if (arr && arr.length > 0) {
if (!props.sfUrl) {
fileList.value = arr.map((el) => {
console.log(arr, "图片");
if (Array.isArray(arr)) {
fileList.value = arr.map((el) => {
if (Object.prototype.toString.call(el) === "[object Object]") {
return {
url: `/mosty-api/mosty-base/minio/image/download/` + el,
@ -168,6 +178,20 @@ watch(
return { url: `/mosty-api/mosty-base/minio/image/download/` + el };
}
});
} else {
const fjListData=arr.split(',')
fileList.value = fjListData.map((el) => {
if (Object.prototype.toString.call(el) === "[object Object]") {
return {
url: `/mosty-api/mosty-base/minio/image/download/` + el,
name: el.name
};
} else {
return { url: `/mosty-api/mosty-base/minio/image/download/` + el };
}
});
}
} else {
fileList.value = arr.map((el) => {
if (Object.prototype.toString.call(el) === "[object Object]") {
@ -285,4 +309,4 @@ const propsModelValue = ref();
-webkit-line-clamp: 2;
}
}
</style>
</style>

View File

@ -1,6 +1,7 @@
<template>
<el-form ref="elform" :model="listQuery" :label-width="props.labelWidth" :rules="props.rules" :inline="props.inline"
label-position="right" :disabled="props.disabled">
<el-form-item v-for="(item, idx) in props.formList" :style="item.width && { width: item.width }" :prop="item.prop"
:label="item.label" :label-width="item.labelWidth" :key="idx">
<!-- input表单 input-->
@ -21,7 +22,7 @@
<!-- 部门department -->
<template v-else-if="item.type === 'department'">
<MOSTY.Department style="width: 100%;" clearable :isAll="item.isAll" @getDepValue="getdep($event, item.depMc)"
v-model="listQuery[item.prop]" :placeholder="listQuery[item.depMc] ? listQuery[item.depMc] : '请选择'" />
:multiple="item.multiple" v-model="listQuery[item.prop]" :placeholder="listQuery[item.depMc] ? listQuery[item.depMc] : '请选择'" />
</template>
<!-- 上传 upload -->

View File

@ -18,6 +18,7 @@
</template>
</el-dialog>
<ChooseMarks v-model="chooseMarksVisible" @choosed="choosed" :roleIds="roleIds" />
</template>
<script setup>
@ -105,4 +106,4 @@ defineExpose({init})
border: 1px solid #e9e9e9;
border-radius: 4px;
}
</style>
</style>

View File

@ -0,0 +1,303 @@
<template>
<div class="smallTitle">审批信息</div>
<div class="ww100">
<el-steps :active="0" space="500" finish-status="success" direction="vertical" status=''>
<el-step :title="item.eventType == '0' ? '发起申请' : item.eventType == '1' ? '审批结束' : item.nodeName"
v-for="(item, index) in workflow" :key="index">
<template #description>
<div class="ww100 mt10 mb20 nodeBox" v-if="item.eventType == '0'||item.eventType == '1'">
<div class="nodeorgNameTg">{{ item.xtLrrbm }}</div>
<div class="flex just-between nameTag">
<div>{{ item.log.userName }}</div>
<div class="fontColor">{{ item.eventType == '0' ? '发起' : '结束' }}</div>
</div>
<div>{{ item.log.xtLrsj }}</div>
</div>
<div v-else class="ww100 mt10 mb20 nodeBox">
<div :class="item.taskStatus=='2'?'nodeorgNameTg':'nodeorgNameDd'">{{ item.orgNameData.orgname }}</div>
<div v-for="(items, indexs) in item.log" :key="indexs">
<div class="flex just-between nameTag" >
<div>{{ items.userName }}</div>
<div :class="item.taskStatus=='2'?'fontColor':'fontColorDd'">审批中</div>
</div>
<div>{{ items.xtLrsj }}</div>
</div>
</div>
</template>
</el-step>
</el-steps>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { queryProcessNode, queryProcessNodeLog, queryProcess } from '@/api/spl'
const workflow = ref()
const getWorkflow = async (id) => {
const promes = {
processId: id
}
const process = await queryProcess(promes)
const proNode = await queryProcessNode(promes)
const proNodeLog = await queryProcessNodeLog(promes)
const sortedNodes = sortNodesWithNoPreviousFirst(proNode.rows)
workflow.value = sortedNodes.map(item => {
const log = proNodeLog.rows.filter(items => item.nodeId == items.nodeId)
if (item.eventType == '0') {
console.log(item);
return {
...item,
log: {
// userData: JSON.parse(item.userData),
// xtLrrbm:process.rows[0].xtLrrbm,
userName: process.rows[0].userName,
xtLrsj: process.rows[0].xtLrsj,
processStatus: process.rows[0].processStatus,
}
}
} else {
return {
...item,
orgNameData: JSON.parse(log[0].userData),
log: log
}
}
})
};
function sortProcessNodes(nodes) {
// 1. 创建节点映射表和子节点关系图
const nodeMap = {};
const childrenMap = {}; // 存储每个节点的子节点
// 初始化
nodes.forEach(node => {
nodeMap[node.nodeId] = node;
childrenMap[node.nodeId] = [];
});
// 2. 构建子节点关系
nodes.forEach(node => {
if (node.prevPNodeId && nodeMap[node.prevPNodeId]) {
childrenMap[node.prevPNodeId].push(node.nodeId);
}
});
// 3. 找到所有开始节点没有prevPNodeId或prevPNodeId不在节点列表中的节点
const startNodes = nodes.filter(node =>
!node.prevPNodeId || !nodeMap[node.prevPNodeId]
);
// 4. 深度优先遍历生成排序列表
const sortedNodes = [];
const visited = new Set();
function traverse(nodeId) {
if (visited.has(nodeId)) return;
visited.add(nodeId);
// 添加当前节点
sortedNodes.push(nodeMap[nodeId]);
// 获取所有子节点相同prevPNodeId的节点
const children = childrenMap[nodeId];
// 对子节点按某种规则排序(如按创建时间)
const sortedChildren = [...children].sort((a, b) => {
return new Date(nodeMap[a].xtLrsj) - new Date(nodeMap[b].xtLrsj);
});
// 递归处理每个子节点
sortedChildren.forEach(childId => {
traverse(childId);
});
}
// 从每个开始节点开始遍历
startNodes.forEach(node => {
traverse(node.nodeId);
});
// 5. 处理可能的孤立节点(不应该存在,但作为保护)
nodes.forEach(node => {
if (!visited.has(node.nodeId)) {
sortedNodes.push(node);
}
});
return sortedNodes;
}
function sortNodesWithNoPreviousFirst(nodes) {
if (!Array.isArray(nodes)) {
console.error('参数必须是一个数组');
return [];
}
if (nodes.length === 0) {
return [];
}
// 1. 创建节点ID到节点的映射表用于快速查找
const nodeMap = nodes.reduce((map, node) => {
if (node && node.nodeId) {
map[node.nodeId] = node;
}
console.log(node,"xxxxxx");
return map;
}, {});
console.log(nodeMap);
// 2. 分离节点:没有前驱的节点和有前驱的节点
const result = nodes.reduce((acc, node) => {
// 检查是否为有效节点
if (!node || !node.nodeId) {
console.warn('发现无效节点,将被忽略:', node);
return acc;
}
// 判断是否没有前驱节点
const hasNoPrevious = !node.prevPNodeId && !nodeMap[node.prevPNodeId];
if (hasNoPrevious) {
acc.noPrevious.push(node);
} else {
acc.hasPrevious.push(node);
}
return acc;
}, { noPrevious: [], hasPrevious: [] });
// 3. 合并结果:没有前驱的节点在前,有前驱的节点在后
console.log(result.noPrevious);
console.log(result.hasPrevious);
return [...result.noPrevious, ...result.hasPrevious];
}
defineExpose({
getWorkflow
})
</script>
<style lang="scss" scoped>
@import "~@/assets/css/layout.scss";
@import "~@/assets/css/element-plus.scss";
.smallTitle {
width: 100%;
font-size: 15px;
line-height: 50px;
font-weight: 550;
color: #606266;
}
.mapSearch {
left: 10px;
top: 10px;
z-index: 100;
}
.dialog {
::v-deep .el-form-item--default {
margin: 0 1% 0 0 !important;
padding-bottom: 0 !important;
}
}
::v-deep .avatar-uploader {
display: flex;
}
::v-deep .el-upload--picture-card i {
width: 156px;
}
.depBox {
border: 1px solid #e9e9e9;
width: 305px;
padding: 0 0 0 4px;
border-radius: 4px;
::v-deep .el-input__inner {
border: none;
}
::v-deep .el-cascader .el-input.is-focus .el-input__inner {
border-color: transparent !important;
}
::v-deep .el-input__inner:focus {
box-shadow: none;
}
::v-deep .el-input.is-disabled .el-input__inner {
border-color: transparent !important;
}
}
/* 使用深度选择器覆盖子组件样式 */
::v-deep .el-step.is-vertical .el-step__title {
color: #000000 !important;
border-color: #000000 !important;
}
::v-deep .el-step__description{
padding: 0 !important;
}
::v-deep .el-step__description.is-wait {
color: #000000 !important;
border-color: #000000 !important;
}
.el-step__title.is-wait {
color: #000000 !important;
border-color: #000000 !important;
}
::v-deep .el-step__head.is-wait {
color: #000000 !important;
border-color: #000000 !important;
}
::v-deep .el-step__line {
background-color: #000000 !important;
}
.nodeBox {
width: 80%;
text-align: center;
line-height: 30px;
border-radius: 10px;
overflow: hidden;
background-color: aliceblue;
.nodeorgNameTg {
background-color: #1abe20;
}
.nameTag {
line-height: 30px;
padding: 0 10px;
}
.nodeorgNameDd {
background-color: #18a2dd;
}
.fontColor {
color: #1abe20;
} .fontColorDd {
color: #18a2dd;
}
}
</style>

View File

@ -0,0 +1,316 @@
<template>
<el-dialog class="dialog-container" :model-value="modelValue" :title="titles" :before-close="handleClose">
<div class="dialog-header">
<div class="flex align-center">下一节点
<el-checkbox v-model="showNode" :label="modelData.nodeName" size="large" />
</div>
<div> <el-button type="success" :icon="Plus" @click="newAdditions" /></div>
</div>
<div class="container-box">
<div v-if="showNode">
<el-divider content-position="left">节点</el-divider>
<div>
<div class="row" v-for="(item, index) in nodeData" :key="index">
<MOSTY.Department filterable v-model="item.deptId" width="100%"
@getDepValue="(obj) => changePostList(index, obj)" clearable placeholder="请选择所属部门" />
<el-select class="select-user" v-model="item.userId" filterable placeholder="选择审批人"
@change="changeUser(index)">
<el-option v-for="item in item.listData" :key="item.id" :label="item.userName" :value="item.id" />
</el-select>
<div>
<el-button type="danger" @click="newDelitions(index)">删除</el-button>
</div>
</div>
</div>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="close">取消</el-button>
<el-button type="primary" @click="qcckPostList">
确认
</el-button>
</div>
</template>
</el-dialog>
</template>
<script>
</script>
<script setup>
import { ref, getCurrentInstance, reactive, watch } from 'vue'
import { qcckGet, qcckPost } from '@/api/qcckApi'
import { splGet, splPost, submitProcess } from '@/api/spl'
import { getItem } from '@/utils/storage'
import { Plus } from '@element-plus/icons-vue'
import * as MOSTY from "@/components/MyComponents/index";
const { proxy } = getCurrentInstance();
const props = defineProps({
modelValue: {
type: Boolean,
required: true
}, title: {
type: String,
default: '发起流程'
},
createProcess: {
type: Object,
default: () => { }
}, radioData: {
type: String,
default: ''
}, path: {
type: Object,
default: {
byMeansOf: '',
nobyMeansOf: '',
clueVerification: "",
recycle: ""
}
}, userData: {
type: Object,
default: () => {
return {
ajmc: "",
flowType: ""
}
}
}
})
const emit = defineEmits(['update:modelValue', 'close', 'getList'])
const titles = ref(props.title)
const showNode = ref(true)
const InterfaceAddress = 'http://155.2422.30:2109/mosty-api/mosty-gsxt/'
// 新增
const nodeData = ref([
{
deptId: '',
userId: '',
userData: {},
orgData: {},
}
])
//新增表单
const newAdditions = () => {
nodeData.value.push({
deptId: '',
userId: '',
userData: {},
orgData: {},
listData: []
})
}
// 删除表单
const newDelitions = (item) => {
nodeData.value.splice(item, 1)
}
const chageIndex = ref(0)
// 选取角色
const changeUser = (index) => {
chageIndex.value = index
let obj = nodeData.value[index].listData.find(item => { return item.id == nodeData.value[index].userId })
nodeData.value[index].userData = {
userId: obj.userId,
userName: obj.userName,
sfzh: obj.idEntityCard,
userData: {
// id: '41fbffaf92a34b31bde6302004277486',
// orgid: '41fbffaf92a34b31bde6302004277486',
id: orgId.value,
orgid: orgId.value,
orgcode: obj.deptCode,
orgname: obj.deptName
}
}
}
//选取部门
const orgId=ref()
const changePostList = (index, e) => {
orgId.value=e.fzOrgId
chageIndex.value = index
nodeData.value[chageIndex.value].id = orgId.value
nodeData.value[chageIndex.value].orgData = {
id:orgId.value,
orgid:orgId.value,
// id: '41fbffaf92a34b31bde6302004277486',
// orgid: '41fbffaf92a34b31bde6302004277486',
orgcode: e.orgCode,
orgname: e.orgName
}
getUser()
}
// 提交
const qcckPostList = async () => {
const deptId = getItem('deptId')
let users = nodeData.value.map(item => {
return {
userId: item.userData.userId,
// userId: '540422200010197495',
userName: item.userData.userName,
userData: JSON.stringify(item.userData.userData)
// ...item.userData
}
})
let userData = {
id:deptId[0].fzOrgId,
orgid:deptId[0].fzOrgId,
// id: '41fbffaf92a34b31bde6302004277486',
// orgid: '41fbffaf92a34b31bde6302004277486',
orgcode: deptId[0].deptCode,
orgname: deptId[0].deptName,
}
const promes = {
...props.createProcess,
// processData: { ...props.createProcess.processData.sqrw, orgNameData: userData },
flowType: "ZyCompany",
iframe: `${props.path.clueVerification}?id=${props.createProcess.processData.rwbh}`,
callback: {
"START": "",//流程开始
"APPROVE": "",//节点审批通过
"BACK": "",//退回上一节点
"REVOKE": "",//撤回审批
"DONE": `${InterfaceAddress}${props.path.byMeansOf}?id=${props.createProcess.rwbh}&bkZt=05`,//流程审批通过
"ABORT": `${InterfaceAddress}${props.path.nobyMeansOf}?id=${props.createProcess.rwbh}&bkZt=06`,//审批不通过
"RECOVER": "",//流程回收
"AGAIN": `${InterfaceAddress}${props.path.recycle}?id=${props.createProcess.rwbh}&bkZt=07`//流程退回初始
},
decision: JSON.stringify({
[modelData.value.nodeId]: {
users: users,
services: [],
events: [],
userData: JSON.stringify(userData)
}
})
}
promes.processData = JSON.stringify(promes.processData)
promes.callback = JSON.stringify(promes.callback)
console.log(promes);
await submitProcess({ ...promes }).then(res => {
proxy.$message({ type: "success", message: "提交审批成功" });
sendMessage(res.rows[0])
})
await emit('getList')
emit('close')
close()
}
//获取角色
const getUser = () => {
const promes = {
size: 200, current: 1,
deptId: nodeData.value[chageIndex.value].id
}
qcckGet(promes, '/mosty-base/sysUser/selectPage').then(res => {
nodeData.value[chageIndex.value].listData = res.records
})
}
//查询模板
const modelData = ref({})
const modelMsg = ref()
const queryModel = async () => {
const prrmes = {
modelId: props.radioData,
modelStatus: 1
}
const res = await splPost(prrmes, '/model/queryModel')
const versionRes = await splPost({ modelVersionId: res.rows[0].currentVersionId }, '/modelVersion/queryModelVersion')
modelMsg.value = versionRes.rows[0]
const nodeRes = await splPost({ modelVersionId: res.rows[0].currentVersionId }, '/modelVersion/queryModelNode')
if (versionRes.rows.length > 0) {
modelData.value = nodeRes.rows.filter(item => item.nodeType == '1')[0]
}
}
//发消息
const sendMessage = (gzlid) => {
const bkshrSfzh = nodeData.value.map(item => {
return {
bkshrXm: item.userData.userName,
bkshrSfzh: item.userData.sfzh,
bkshrSsbmmc: item.userData.userData.orgname,
bkshrSsbmdm: item.userData.userData.orgcode,
}
})
const promes = {
xxly: '005',
gzlid: gzlid,
list: bkshrSfzh,
versionId: modelMsg.value.modelVersionId,
id: props.createProcess.processData.rwbh
}
switch (props.userData.flowType) {
case 'BKSP':
qcckPost(promes, '/mosty-gsxt/tbGsxtBk/updateBkgzl').then(res => {
console.log(res);
})
break;
case 'ZDRYSDFJDP':
qcckPost(promes, '/mosty-gsxt/tbGsxtRqfjRy/updateBkgzl').then(res => {
console.log(res);
})
break;
case 'XSSJCJSP':
qcckPost(promes, '/mosty-gsxt/qbcj/updateBkgzl').then(res => {
console.log(res);
})
break;
}
}
watch(() => props.modelValue, (val) => {
if (val) {
queryModel()
}
})
const close = () => {
nodeData.value = [
{
deptId: '',
userId: '',
userData: {},
orgData: {},
}
]
emit('update:modelValue', false)
}
</script>
<style lang="scss" scoped>
@import "@/assets/css/homeScreen.scss";
::v-deep .el-dialog__body {
padding-top: 0 !important;
padding-bottom: 0 !important;
}
.dialog-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.container-box {
height: 300px;
overflow: auto;
.row {
margin-top: 10px;
align-items: center;
display: flex;
margin-right: 10px;
.select-user {
margin: 0 10px;
}
}
}
</style>

View File

@ -0,0 +1,143 @@
<template>
<el-dialog class="dialog-container" :model-value="modelValue" :title="title" :before-close="close">
<div style="height: 300px;overflow: auto" v-infinite-scroll="load">
<el-radio-group v-model="radio" @change="changeRadio" v-if="lyquery.rows.length > 0">
<el-radio :label="item.modelId" v-for="(item) in lyquery.rows" :key="item.id">
{{ item.modelName }}
</el-radio>
</el-radio-group>
<div class="ww100 flex just-center">
<MOSTY.Empty :show="lyquery.rows.length == 0"></MOSTY.Empty>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="close">取消</el-button>
<el-button type="primary" @click="onComfirm">
确认
</el-button>
</div>
</template>
</el-dialog>
<SelectingPeople v-model="showDialog"
:createProcess="createProcess" @close="close" :radioData="radioData" :path="path" @getList="emit('getList')" :userData="userData"/>
</template>
<script setup>
import { ref, getCurrentInstance, reactive, watch } from 'vue'
import SelectingPeople from './SelectingPeople.vue';
import * as MOSTY from "@/components/MyComponents/index";
import { splGet, splPost } from '@/api/spl'
const props = defineProps({
modelValue: {
type: Boolean,
required: true
}, title: {
type: String,
default: '发起流程'
}, data: {
type: Object,
default: () => { }
}, userData: {
type: Object,
default: () => {
return {
ajmc: "",
flowType: ""
}
}
}, path: {
type: Object,
default: {}
}
})
const showDialog = ref(false)
const emit = defineEmits(['update:modelValue','getList'])
const titles = ref(props.title)
const lyquery = reactive({
rows: [],
total: 0,
promes: {
page: 1,
rows: 100,
modelName:props.userData.modelName
}
})
const radio = ref(3)
const qcckGetList = () => {
const prrmes = {
...lyquery.promes
}
splGet(prrmes, '/model/queryModel').then(res => {
lyquery.rows = lyquery.promes.page == 1 ? res.rows : lyquery.rows.concat(res.rows)
lyquery.total = res.total
})
}
watch(() => props.modelValue, (newVal) => {
if (newVal) {
qcckGetList()
}
})
const createProcess = ref({})
const radioData = ref('')
const changeRadio = (e) => {
radioData.value = e
const item = lyquery.rows.find(item => item.modelId == e)
if (item) {
createProcess.value = {
modelId: e,
processName: item.modelName ? item.modelName : '',
processType:1,
processData:
{
iframe: `${props.path.clueVerification}?id=${props.data.id}`,
hostPrefix: "sgxtPath",
rwbh: props.data.id,
flowType: 'SGSP',
sqrw: props.data,
}
}
}
}
const onComfirm = () => {
showDialog.value = true
}
const load = () => {
if (lyquery.total == lyquery.rows.length) {
return
} else {
lyquery.promes.page++
qcckGetList()
}
}
const close = () => {
radio.value=''
emit('update:modelValue', false)
}
</script>
<style lang="scss" scoped>
@import "@/assets/css/homeScreen.scss";
::v-deep .el-dialog__body {
padding-top: 0 !important;
padding-bottom: 0 !important;
}
.dialog-container {
height: 500px;
}
::v-deep .el-radio__label {
color: #000;
}
</style>

View File

@ -0,0 +1,153 @@
<template>
<FormMessage v-model="listQuery" :formList="formData" labelWidth="100px" ref="elform" :rules="rules">
<template #zrSsbmdm>
<MOSTY.Department filterable v-model="listQuery.zrSsbmdm" width="100%" @getDepValue="getDepValue" clearable
placeholder="请选择所属部门" :multiple="true" />
</template>
<template #ry>
<el-input readonly v-model="listQuery.ry" @click="chooseUserVisible = true" placeholder="请选择民警"></el-input>
</template>
</FormMessage>
<ChooseUser v-model="chooseUserVisible" @choosedUsers="handleUserSelected" :roleIds="roleIds" :Single="false" />
</template>
<script setup>
import { reactive, ref, onMounted, watch } from "vue";
import { sendFqzl, ZdrfjSendFqzl ,qbcjSendFqzl} from '@/api/commit'
import { getItem } from '@/utils/storage'
import * as MOSTY from "@/components/MyComponents/index";
import ChooseUser from "@/components/ChooseList/ChooseUser/index.vue"
import FormMessage from "@/components/aboutTable/FormMessage.vue";
import { ElMessage } from "element-plus";
const props = defineProps({
itemData: {
type: Object,
default: () => { }
}, identification: {
type: String,
default: ""
}, tacitly: {
type: Object,
default: () => { }
}
})
const emit = defineEmits(['handleClose'])
// 表单数据
const listQuery = ref({}); //表单
// 选择人员
const ryStr = ref('')
const elform = ref()
const personnelEntity = ref()
const formData = ref([
{ label: "标题", prop: "title", type: "input", width: "40%" },
{ label: "接收单位", prop: "zrSsbmdm", type: "slot", width: "40%" },
{ label: "指令状态", prop: "status", type: "input", width: "40%" },
{ label: "人员选择", prop: "ry", type: "slot", width: "40%" },
{ label: "附件", prop: "attachmentPath", type: "upload" },
{ label: "指令内容", prop: "instructionContent", type: "textarea", width: "100%" },
])
const rules = reactive({
title: [{ required: true, message: "请输入指令标题", trigger: "blur" }],
zrSsbmdm: [{ required: true, message: "请选择接收单位", trigger: "blur" }],
instructionContent: [{ required: true, message: "请输入指令内容", trigger: "blur" }],
ry: [{ required: true, message: "请选择人员", trigger: "blur" }]
});
const deptId = getItem('deptId')
const getsendFqzl = () => {
elform.value.submit(async (val) => {
if (val) {
const data = { ...listQuery.value }
delete data.ry
const promes = {
instructionsEntity: {
unitCode: deptId[0].deptCode,
unitName: deptId[0].deptName,
...data,
receivingUnitCode: listQuery.value.zrSsbmdm.toString(),
receivingUnit: listQuery.value.receivingUnit.toString(),
attachmentPath: JSON.stringify(listQuery.value.attachmentPath)
}, id: props.itemData.id,
personnelEntity: personnelEntity.value
}
try {
let res
switch (props.identification) {
case 'yj':
res = await sendFqzl(promes)
break;
case 'zdrfj':
res = await ZdrfjSendFqzl(promes)
break;
case 'qbcj':
res = await qbcjSendFqzl(promes)
break;
}
const str = JSON.parse(res)
if (str.code == 200) {
ElMessage.success('发送成功')
listQuery.value = {}
listQuery.value.attachmentPath = ''
emit('handleClose')
} else {
ElMessage.error(str.msg)
}
} catch (error) {
console.log(error);
}
}
})
}
console.log(props.itemData);
watch(() => props.itemData, (val) => {
listQuery.value.title = val[props.tacitly['title']]
if (props.tacitly['instructionContent']) {
listQuery.value.instructionContent = val[props.tacitly['instructionContent']]
}
}, { deep: true, immediate: true })
const chooseUserVisible = ref(false)
const roleIds = ref([])
// 选取角色
const handleUserSelected = (val) => {
personnelEntity.value = val.map((item, index) => {
return {
name: item.userName,
idNumber: item.idEntityCard,
phoneNumber: item.mobile,
personTypeId: "",
personTypeName: "",
domicilePlace: "",
orderId: index + 1
}
})
// ryStr.value
listQuery.value.ry = personnelEntity.value.map(item => item.name)
}
// 选取部门
const getDepValue = (e) => {
listQuery.value.receivingUnit = e.map(item => item.orgName)
}
const close = () => {
listQuery.value = {}
listQuery.value.attachmentPath = ''
}
defineExpose({
getsendFqzl,
close
})
</script>
<style scoped lang="scss"></style>