perf SQL查询和流程图设计移植

This commit is contained in:
xxm
2023-03-13 23:36:23 +08:00
parent 9be043af90
commit 018328673f
23 changed files with 494 additions and 620 deletions

View File

@@ -34,8 +34,6 @@
"@ant-design/colors": "^6.0.0",
"@ant-design/icons-vue": "^6.1.0",
"@iconify/iconify": "^2.2.1",
"@logicflow/core": "^1.1.13",
"@logicflow/extension": "^1.1.13",
"@vue/runtime-core": "^3.2.33",
"@vue/shared": "^3.2.33",
"@vueuse/core": "^8.3.0",

View File

@@ -3,18 +3,18 @@
<a-layout style="height: 100%">
<a-layout style="align-items: stretch">
<div ref="canvas" class="canvas" :style="{ minHeight: height + 'px' }"></div>
<a-drawer :visible="drawerVisible" title="流程信息" :width="450" placement="right" :closable="true" @close="drawerVisible = false">
<a-timeline>
<a-timeline-item v-for="o in currentTaskList" :key="o.id">
<p>开始时间: {{ o.startTime }}</p>
<p>状态{{ stateNameConvert(o.state) }}</p>
<p>处理结果{{ dictConvert('BpmTaskResult', o.result) }}</p>
<p>处理人: {{ o.userName }}</p>
<p>结束时间: {{ o.endTime ? o.endTime : '' }}</p>
<p>审批意见{{ o.reason ? o.reason : '' }}</p>
</a-timeline-item>
</a-timeline>
</a-drawer>
<!-- <a-drawer :visible="drawerVisible" title="流程信息" :width="450" placement="right" :closable="true" @close="drawerVisible = false">-->
<!-- <a-timeline>-->
<!-- <a-timeline-item v-for="o in currentTaskList" :key="o.id">-->
<!-- <p>开始时间: {{ o.startTime }}</p>-->
<!-- <p>状态{{ stateNameConvert(o.state) }}</p>-->
<!-- <p>处理结果{{ dictConvert('BpmTaskResult', o.result) }}</p>-->
<!-- <p>处理人: {{ o.userName }}</p>-->
<!-- <p>结束时间: {{ o.endTime ? o.endTime : '' }}</p>-->
<!-- <p>审批意见{{ o.reason ? o.reason : '' }}</p>-->
<!-- </a-timeline-item>-->
<!-- </a-timeline>-->
<!-- </a-drawer>-->
<a-layout-sider
class="sider"
style="
@@ -37,7 +37,11 @@
import Modeler from 'bpmn-js/lib/Modeler'
// 引入flowable的节点文件
import flowableModdle from './flowable/flowable.json'
import { addArrow } from './processViewerUtils'
import { useDict } from '/@/hooks/bootx/useDict'
const { dictConvert } = useDict()
export default {
name: 'WorkflowBpmnModeler',
@@ -88,24 +92,26 @@
* 生成实例
*/
initModeler() {
console.log(this.$refs.canvas)
this.modeler = new Modeler({
container: this.$refs.canvas,
additionalModules: [
{
translate: ['value', customTranslate],
paletteProvider: ['value', ''], // 禁用/清空左侧工具栏
labelEditingProvider: ['value', ''], // 禁用节点编辑
contextPadProvider: ['value', ''], // 禁用图形菜单
bendpoints: ['value', {}], // 禁用连线拖动
// paletteProvider: ['value', ''], // 禁用/清空左侧工具栏
// labelEditingProvider: ['value', ''], // 禁用节点编辑
// contextPadProvider: ['value', ''], // 禁用图形菜单
// bendpoints: ['value', {}], // 禁用连线拖动
// zoomScroll: ['value', ''], // 禁用滚动
// moveCanvas: ['value', ''], // 禁用拖动整个流程图
move: ['value', ''], // 禁用单个图形拖动
},
],
moddleExtensions: {
flowable: flowableModdle,
},
// moddleExtensions: {
// flowable: flowableModdle,
// },
})
console.log(this.modeler)
},
/**
* 创建流程图
@@ -282,9 +288,6 @@
stateNameConvert(state) {
return dictConvert('BpmTaskState', state)
},
dictConvert(dictCode, code) {
return dictConvert(dictCode, code)
},
},
}
</script>
@@ -293,10 +296,10 @@
// Font class
@import './icon/iconfont.css';
/*左边工具栏以及编辑节点的样式*/
@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/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 './css/ProcessViewerHighlight.less';
.view-mode {

View File

@@ -7,12 +7,9 @@ export default function customTranslate(template, replacements) {
template = translations[template] || template
// Replace
return template.replace(/{([^}]+)}/g, function(_, key) {
return template.replace(/{([^}]+)}/g, function (_, key) {
var str = replacements[key]
if (
translations[replacements[key]] !== null &&
translations[replacements[key]] !== 'undefined'
) {
if (translations[replacements[key]] !== null && translations[replacements[key]] !== 'undefined') {
str = translations[replacements[key]]
}
return str || '{' + key + '}'

View File

@@ -4,7 +4,7 @@ import { query as domQuery } from 'min-dom'
/**
* 添加箭头
*/
export function addArrow () {
export function addArrow() {
const todo = svgCreate('marker')
// 追加箭头svg元素 通过
svgAttr(todo, {
@@ -14,12 +14,12 @@ export function addArrow () {
refY: '10',
markerWidth: '10',
markerHeight: '10',
orient: 'auto'
orient: 'auto',
})
const todoPath = svgCreate('path')
svgAttr(todoPath, {
d: 'M 1 5 L 11 10 L 1 15 Z',
class: 'highlight-todo-arrow'
class: 'highlight-todo-arrow',
})
svgAppend(todo, todoPath)
// 追加箭头svg元素 通过
@@ -31,12 +31,12 @@ export function addArrow () {
refY: '10',
markerWidth: '10',
markerHeight: '10',
orient: 'auto'
orient: 'auto',
})
const passPath = svgCreate('path')
svgAttr(passPath, {
d: 'M 1 5 L 11 10 L 1 15 Z',
class: 'highlight-pass-arrow'
class: 'highlight-pass-arrow',
})
svgAppend(pass, passPath)
@@ -49,12 +49,12 @@ export function addArrow () {
refY: '10',
markerWidth: '10',
markerHeight: '10',
orient: 'auto'
orient: 'auto',
})
const rejectPath = svgCreate('path')
svgAttr(rejectPath, {
d: 'M 1 5 L 11 10 L 1 15 Z',
class: 'highlight-reject-arrow'
class: 'highlight-reject-arrow',
})
svgAppend(reject, rejectPath)
@@ -67,12 +67,12 @@ export function addArrow () {
refY: '10',
markerWidth: '10',
markerHeight: '10',
orient: 'auto'
orient: 'auto',
})
const cancelPath = svgCreate('path')
svgAttr(cancelPath, {
d: 'M 1 5 L 11 10 L 1 15 Z',
class: 'highlight-cancel-arrow'
class: 'highlight-cancel-arrow',
})
svgAppend(cancel, cancelPath)

View File

@@ -1,4 +0,0 @@
import { withInstall } from '/@/utils'
import flowChart from './src/FlowChart.vue'
export const FlowChart = withInstall(flowChart)

View File

@@ -1,158 +0,0 @@
<template>
<div class="h-full" :class="prefixCls">
<FlowChartToolbar :prefixCls="prefixCls" v-if="toolbar" @view-data="handlePreview" />
<div ref="lfElRef" class="h-full"></div>
<BasicModal @register="register" title="流程数据" width="50%">
<JsonPreview :data="graphData" />
</BasicModal>
</div>
</template>
<script lang="ts">
import type { Ref } from 'vue'
import type { Definition } from '@logicflow/core'
import { defineComponent, ref, onMounted, unref, nextTick, computed, watch } from 'vue'
import FlowChartToolbar from './FlowChartToolbar.vue'
import LogicFlow from '@logicflow/core'
import { Snapshot, BpmnElement, Menu, DndPanel, SelectionSelect } from '@logicflow/extension'
import { useDesign } from '/@/hooks/web/useDesign'
import { useAppStore } from '/@/store/modules/app'
import { createFlowChartContext } from './useFlowContext'
import { toLogicFlowData } from './adpterForTurbo'
import { useModal, BasicModal } from '/@/components/Modal'
import { JsonPreview } from '/@/components/CodeEditor'
import { configDefaultDndPanel } from './config'
import '@logicflow/core/dist/style/index.css'
import '@logicflow/extension/lib/style/index.css'
export default defineComponent({
name: 'FlowChart',
components: { BasicModal, FlowChartToolbar, JsonPreview },
props: {
flowOptions: {
type: Object as PropType<Definition>,
default: () => ({}),
},
data: {
type: Object as PropType<any>,
default: () => ({}),
},
toolbar: {
type: Boolean,
default: true,
},
patternItems: {
type: Array,
},
},
setup(props) {
const lfElRef = ref(null)
const graphData = ref({})
const lfInstance = ref(null) as Ref<LogicFlow | null>
const { prefixCls } = useDesign('flow-chart')
const appStore = useAppStore()
const [register, { openModal }] = useModal()
createFlowChartContext({
logicFlow: lfInstance as unknown as LogicFlow,
})
const getFlowOptions = computed(() => {
const { flowOptions } = props
const defaultOptions: Partial<Definition> = {
grid: true,
background: {
color: appStore.getDarkMode === 'light' ? '#f7f9ff' : '#151515',
},
keyboard: {
enabled: true,
},
...flowOptions,
}
return defaultOptions as Definition
})
watch(
() => props.data,
() => {
onRender()
},
)
// TODO
// watch(
// () => appStore.getDarkMode,
// () => {
// init();
// }
// );
watch(
() => unref(getFlowOptions),
(options) => {
unref(lfInstance)?.updateEditConfig(options)
},
)
// init logicFlow
async function init() {
await nextTick()
const lfEl = unref(lfElRef)
if (!lfEl) {
return
}
LogicFlow.use(DndPanel)
// Canvas configuration
LogicFlow.use(Snapshot)
// Use the bpmn plug-in to introduce bpmn elements, which can be used after conversion in turbo
LogicFlow.use(BpmnElement)
// Start the right-click menu
LogicFlow.use(Menu)
LogicFlow.use(SelectionSelect)
lfInstance.value = new LogicFlow({
...unref(getFlowOptions),
container: lfEl,
})
const lf = unref(lfInstance)!
lf?.setDefaultEdgeType('line')
onRender()
lf?.setPatternItems(props.patternItems || configDefaultDndPanel(lf))
}
async function onRender() {
await nextTick()
const lf = unref(lfInstance)
if (!lf) {
return
}
const lFData = toLogicFlowData(props.data)
lf.render(lFData)
}
function handlePreview() {
const lf = unref(lfInstance)
if (!lf) {
return
}
graphData.value = unref(lf).getGraphData()
openModal()
}
onMounted(init)
return {
register,
prefixCls,
lfElRef,
handlePreview,
graphData,
}
},
})
</script>

View File

@@ -1,159 +0,0 @@
<template>
<div :class="`${prefixCls}-toolbar`" class="flex items-center px-2 py-1">
<template v-for="item in toolbarItemList" :key="item.type">
<Tooltip placement="bottom" v-bind="item.disabled ? { visible: false } : {}">
<template #title>{{ item.tooltip }}</template>
<span :class="`${prefixCls}-toolbar__icon`" v-if="item.icon" @click="onControl(item)">
<Icon :icon="item.icon" :class="item.disabled ? 'cursor-not-allowed disabeld' : 'cursor-pointer'" />
</span>
</Tooltip>
<Divider v-if="item.separate" type="vertical" />
</template>
</div>
</template>
<script lang="ts">
import type { ToolbarConfig } from './types'
import { defineComponent, ref, onUnmounted, unref, nextTick, watchEffect } from 'vue'
import { Divider, Tooltip } from 'ant-design-vue'
import { Icon } from '/@/components/Icon'
import { useFlowChartContext } from './useFlowContext'
import { ToolbarTypeEnum } from './enum'
export default defineComponent({
name: 'FlowChartToolbar',
components: { Icon, Divider, Tooltip },
props: {
prefixCls: String,
},
emits: ['view-data'],
setup(_, { emit }) {
const toolbarItemList = ref<ToolbarConfig[]>([
{
type: ToolbarTypeEnum.ZOOM_IN,
icon: 'codicon:zoom-out',
tooltip: '缩小',
},
{
type: ToolbarTypeEnum.ZOOM_OUT,
icon: 'codicon:zoom-in',
tooltip: '放大',
},
{
type: ToolbarTypeEnum.RESET_ZOOM,
icon: 'codicon:screen-normal',
tooltip: '重置比例',
},
{ separate: true },
{
type: ToolbarTypeEnum.UNDO,
icon: 'ion:arrow-undo-outline',
tooltip: '后退',
disabled: true,
},
{
type: ToolbarTypeEnum.REDO,
icon: 'ion:arrow-redo-outline',
tooltip: '前进',
disabled: true,
},
{ separate: true },
{
type: ToolbarTypeEnum.SNAPSHOT,
icon: 'ion:download-outline',
tooltip: '下载',
},
{
type: ToolbarTypeEnum.VIEW_DATA,
icon: 'carbon:document-view',
tooltip: '查看数据',
},
])
const { logicFlow } = useFlowChartContext()
function onHistoryChange({ data: { undoAble, redoAble } }) {
const itemsList = unref(toolbarItemList)
const undoIndex = itemsList.findIndex((item) => item.type === ToolbarTypeEnum.UNDO)
const redoIndex = itemsList.findIndex((item) => item.type === ToolbarTypeEnum.REDO)
if (undoIndex !== -1) {
unref(toolbarItemList)[undoIndex].disabled = !undoAble
}
if (redoIndex !== -1) {
unref(toolbarItemList)[redoIndex].disabled = !redoAble
}
}
const onControl = (item) => {
const lf = unref(logicFlow)
if (!lf) {
return
}
switch (item.type) {
case ToolbarTypeEnum.ZOOM_IN:
lf.zoom()
break
case ToolbarTypeEnum.ZOOM_OUT:
lf.zoom(true)
break
case ToolbarTypeEnum.RESET_ZOOM:
lf.resetZoom()
break
case ToolbarTypeEnum.UNDO:
lf.undo()
break
case ToolbarTypeEnum.REDO:
lf.redo()
break
case ToolbarTypeEnum.SNAPSHOT:
lf.getSnapshot()
break
case ToolbarTypeEnum.VIEW_DATA:
emit('view-data')
break
}
}
watchEffect(async () => {
if (unref(logicFlow)) {
await nextTick()
unref(logicFlow)?.on('history:change', onHistoryChange)
}
})
onUnmounted(() => {
unref(logicFlow)?.off('history:change', onHistoryChange)
})
return { toolbarItemList, onControl }
},
})
</script>
<style lang="less">
@prefix-cls: ~'@{namespace}-flow-chart-toolbar';
html[data-theme='dark'] {
.lf-dnd {
background: #080808;
}
}
.@{prefix-cls} {
height: 36px;
background-color: @app-content-background;
border-bottom: 1px solid @border-color-base;
.disabeld {
color: @disabled-color;
}
&__icon {
display: inline-block;
padding: 2px 4px;
margin-right: 10px;
&:hover {
color: @primary-color;
}
}
}
</style>

View File

@@ -1,75 +0,0 @@
const TurboType = {
SEQUENCE_FLOW: 1,
START_EVENT: 2,
END_EVENT: 3,
USER_TASK: 4,
SERVICE_TASK: 5,
EXCLUSIVE_GATEWAY: 6,
}
function convertFlowElementToEdge(element) {
const { incoming, outgoing, properties, key } = element
const { text, startPoint, endPoint, pointsList, logicFlowType } = properties
const edge = {
id: key,
type: logicFlowType,
sourceNodeId: incoming[0],
targetNodeId: outgoing[0],
text,
startPoint,
endPoint,
pointsList,
properties: {},
}
const excludeProperties = ['startPoint', 'endPoint', 'pointsList', 'text', 'logicFlowType']
Object.keys(element.properties).forEach((property) => {
if (excludeProperties.indexOf(property) === -1) {
edge.properties[property] = element.properties[property]
}
})
return edge
}
function convertFlowElementToNode(element) {
const { properties, key } = element
const { x, y, text, logicFlowType } = properties
const node = {
id: key,
type: logicFlowType,
x,
y,
text,
properties: {},
}
const excludeProperties = ['x', 'y', 'text', 'logicFlowType']
Object.keys(element.properties).forEach((property) => {
if (excludeProperties.indexOf(property) === -1) {
node.properties[property] = element.properties[property]
}
})
return node
}
export function toLogicFlowData(data) {
const lfData: {
// TODO type
nodes: any[]
edges: any[]
} = {
nodes: [],
edges: [],
}
const list = data.flowElementList
list &&
list.length > 0 &&
list.forEach((element) => {
if (element.type === TurboType.SEQUENCE_FLOW) {
const edge = convertFlowElementToEdge(element)
lfData.edges.push(edge)
} else {
const node = convertFlowElementToNode(element)
lfData.nodes.push(node)
}
})
return lfData
}

View File

@@ -1,96 +0,0 @@
export const nodeList = [
{
text: '开始',
type: 'start',
class: 'node-start',
},
{
text: '矩形',
type: 'rect',
class: 'node-rect',
},
{
type: 'user',
text: '用户',
class: 'node-user',
},
{
type: 'push',
text: '推送',
class: 'node-push',
},
{
type: 'download',
text: '位置',
class: 'node-download',
},
{
type: 'end',
text: '结束',
class: 'node-end',
},
]
export const BpmnNode = [
{
type: 'bpmn:startEvent',
text: '开始',
class: 'bpmn-start',
},
{
type: 'bpmn:endEvent',
text: '结束',
class: 'bpmn-end',
},
{
type: 'bpmn:exclusiveGateway',
text: '网关',
class: 'bpmn-exclusiveGateway',
},
{
type: 'bpmn:userTask',
text: '用户',
class: 'bpmn-user',
},
]
export function configDefaultDndPanel(lf) {
return [
{
text: '选区',
icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAAOVJREFUOBGtVMENwzAIjKP++2026ETdpv10iy7WFbqFyyW6GBywLCv5gI+Dw2Bluj1znuSjhb99Gkn6QILDY2imo60p8nsnc9bEo3+QJ+AKHfMdZHnl78wyTnyHZD53Zzx73MRSgYvnqgCUHj6gwdck7Zsp1VOrz0Uz8NbKunzAW+Gu4fYW28bUYutYlzSa7B84Fh7d1kjLwhcSdYAYrdkMQVpsBr5XgDGuXwQfQr0y9zwLda+DUYXLaGKdd2ZTtvbolaO87pdo24hP7ov16N0zArH1ur3iwJpXxm+v7oAJNR4JEP8DoAuSFEkYH7cAAAAASUVORK5CYII=',
callback: () => {
lf.updateEditConfig({
stopMoveGraph: true,
})
},
},
{
type: 'circle',
text: '开始',
icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAAnBJREFUOBGdVL1rU1EcPfdGBddmaZLiEhdx1MHZQXApraCzQ7GKLgoRBxMfcRELuihWKcXFRcEWF8HBf0DdDCKYRZpnl7p0svLe9Zzbd29eQhTbC8nv+9zf130AT63jvooOGS8Vf9Nt5zxba7sXQwODfkWpkbjTQfCGUd9gIp3uuPP8bZ946g56dYQvnBg+b1HB8VIQmMFrazKcKSvFW2dQTxJnJdQ77urmXWOMBCmXM2Rke4S7UAW+/8ywwFoewmBps2tu7mbTdp8VMOkIRAkKfrVawalJTtIliclFbaOBqa0M2xImHeVIfd/nKAfVq/LGnPss5Kh00VEdSzfwnBXPUpmykNss4lUI9C1ga+8PNrBD5YeqRY2Zz8PhjooIbfJXjowvQJBqkmEkVnktWhwu2SM7SMx7Cj0N9IC0oQXRo8xwAGzQms+xrB/nNSUWVveI48ayrFGyC2+E2C+aWrZHXvOuz+CiV6iycWe1Rd1Q6+QUG07nb5SbPrL4426d+9E1axKjY3AoRrlEeSQo2Eu0T6BWAAr6COhTcWjRaYfKG5csnvytvUr/WY4rrPMB53Uo7jZRjXaG6/CFfNMaXEu75nG47X+oepU7PKJvvzGDY1YLSKHJrK7vFUwXKkaxwhCW3u+sDFMVrIju54RYYbFKpALZAo7sB6wcKyyrd+aBMryMT2gPyD6GsQoRFkGHr14TthZni9ck0z+Pnmee460mHXbRAypKNy3nuMdrWgVKj8YVV8E7PSzp1BZ9SJnJAsXdryw/h5ctboUVi4AFiCd+lQaYMw5z3LGTBKjLQOeUF35k89f58Vv/tGh+l+PE/wG0rgfIUbZK5AAAAABJRU5ErkJggg==',
},
{
type: 'rect',
text: '用户任务',
icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAEFVwZaAAAABGdBTUEAALGPC/xhBQAAAqlJREFUOBF9VM9rE0EUfrMJNUKLihGbpLGtaCOIR8VjQMGDePCgCCIiCNqzCAp2MyYUCXhUtF5E0D+g1t48qAd7CCLqQUQKEWkStcEfVGlLdp/fm3aW2QQdyLzf33zz5m2IsAZ9XhDpyaaIZkTS4ASzK41TFao88GuJ3hsr2pAbipHxuSYyKRugagICGANkfFnNh3HeE2N0b3nN2cgnpcictw5veJIzxmDamSlxxQZicq/mflxhbaH8BLRbuRwNtZp0JAhoplVRUdzmCe/vO27wFuuA3S5qXruGdboy5/PRGFsbFGKo/haRtQHIrM83bVeTrOgNhZReWaYGnE4aUQgTJNvijJFF4jQ8BxJE5xfKatZWmZcTQ+BVgh7s8SgPlCkcec4mGTmieTP4xd7PcpIEg1TX6gdeLW8rTVMVLVvb7ctXoH0Cydl2QOPJBG21STE5OsnbweVYzAnD3A7PVILuY0yiiyDwSm2g441r6rMSgp6iK42yqroI2QoXeJVeA+YeZSa47gZdXaZWQKTrG93rukk/l2Al6Kzh5AZEl7dDQy+JjgFahQjRopSxPbrbvK7GRe9ePWBo1wcU7sYrFZtavXALwGw/7Dnc50urrHJuTPSoO2IMV3gUQGNg87IbSOIY9BpiT9HV7FCZ94nPXb3MSnwHn/FFFE1vG6DTby+r31KAkUktB3Qf6ikUPWxW1BkXSPQeMHHiW0+HAd2GelJsZz1OJegCxqzl+CLVHa/IibuHeJ1HAKzhuDR+ymNaRFM+4jU6UWKXorRmbyqkq/D76FffevwdCp+jN3UAN/C9JRVTDuOxC/oh+EdMnqIOrlYteKSfadVRGLJFJPSB/ti/6K8f0CNymg/iH2gO/f0DwE0yjAFO6l8JaR5j0VPwPwfaYHqOqrCI319WzwhwzNW/aQAAAABJRU5ErkJggg==',
cls: 'important-node',
},
{
type: 'rect',
text: '系统任务',
icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAEFVwZaAAAABGdBTUEAALGPC/xhBQAAAqlJREFUOBF9VM9rE0EUfrMJNUKLihGbpLGtaCOIR8VjQMGDePCgCCIiCNqzCAp2MyYUCXhUtF5E0D+g1t48qAd7CCLqQUQKEWkStcEfVGlLdp/fm3aW2QQdyLzf33zz5m2IsAZ9XhDpyaaIZkTS4ASzK41TFao88GuJ3hsr2pAbipHxuSYyKRugagICGANkfFnNh3HeE2N0b3nN2cgnpcictw5veJIzxmDamSlxxQZicq/mflxhbaH8BLRbuRwNtZp0JAhoplVRUdzmCe/vO27wFuuA3S5qXruGdboy5/PRGFsbFGKo/haRtQHIrM83bVeTrOgNhZReWaYGnE4aUQgTJNvijJFF4jQ8BxJE5xfKatZWmZcTQ+BVgh7s8SgPlCkcec4mGTmieTP4xd7PcpIEg1TX6gdeLW8rTVMVLVvb7ctXoH0Cydl2QOPJBG21STE5OsnbweVYzAnD3A7PVILuY0yiiyDwSm2g441r6rMSgp6iK42yqroI2QoXeJVeA+YeZSa47gZdXaZWQKTrG93rukk/l2Al6Kzh5AZEl7dDQy+JjgFahQjRopSxPbrbvK7GRe9ePWBo1wcU7sYrFZtavXALwGw/7Dnc50urrHJuTPSoO2IMV3gUQGNg87IbSOIY9BpiT9HV7FCZ94nPXb3MSnwHn/FFFE1vG6DTby+r31KAkUktB3Qf6ikUPWxW1BkXSPQeMHHiW0+HAd2GelJsZz1OJegCxqzl+CLVHa/IibuHeJ1HAKzhuDR+ymNaRFM+4jU6UWKXorRmbyqkq/D76FffevwdCp+jN3UAN/C9JRVTDuOxC/oh+EdMnqIOrlYteKSfadVRGLJFJPSB/ti/6K8f0CNymg/iH2gO/f0DwE0yjAFO6l8JaR5j0VPwPwfaYHqOqrCI319WzwhwzNW/aQAAAABJRU5ErkJggg==',
cls: 'import_icon',
},
{
type: 'diamond',
text: '条件判断',
icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAYAAAHeEJUAAAAABGdBTUEAALGPC/xhBQAAAvVJREFUOBGNVEFrE0EU/mY3bQoiFlOkaUJrQUQoWMGePLX24EH0IIoHKQiCV0G8iE1covgLiqA/QTzVm1JPogc9tIJYFaQtlhQxqYjSpunu+L7JvmUTU3AgmTfvffPNN++9WSA1DO182f6xwILzD5btfAoQmwL5KJEwiQyVbSVZ0IgRyV6PTpIJ81E5ZvqfHQR0HUOBHW4L5Et2kQ6Zf7iAOhTFAA8s0pEP7AXO1uAA52SbqGk6h/6J45LaLhO64ByfcUzM39V7ZiAdS2yCePPEIQYvTUHqM/n7dgQNfBKWPjpF4ISk8q3J4nB11qw6X8l+FsF3EhlkEMfrjIer3wJTLwS2aCNcj4DbGxXTw00JmAuO+Ni6bBxVUCvS5d9aa04+so4pHW5jLTywuXAL7jJ+D06sl82Sgl2JuVBQn498zkc2bGKxULHjCnSMadBKYDYYHAtsby1EQ5lNGrQd4Y3v4Zo0XdGEmDno46yCM9Tk+RiJmUYHS/aXHPNTcjxcbTFna000PFJHIVZ5lFRqRpJWk9/+QtlOUYJj9HG5pVFEU7zqIYDVsw2s+AJaD8wTd2umgSCCyUxgGsS1Y6TBwXQQTFuZaHcd8gAGioE90hlsY+wMcs30RduYtxanjMGal8H5dMW67dmT1JFtYUEe8LiQLRsPZ6IIc7A4J5tqco3T0pnv/4u0kyzrYUq7gASuEyI8VXKvB9Odytv6jS/PNaZBln0nioJG/AVQRZvApOdhjj3Jt8QC8Im09SafwdBdvIpztpxWxpeKCC+EsFdS8DCyuCn2munFpL7ctHKp+Xc5cMybeIyMAN33SPL3ZR9QV1XVwLyzHm6Iv0/yeUuUb7PPlZC4D4HZkeu6dpF4v9j9MreGtMbxMMRLIcjJic9yHi7WQ3yVKzZVWUr5UrViJvn1FfUlwe/KYVfYyWRLSGNu16hR01U9IacajXPei0wx/5BqgInvJN+MMNtNme7ReU9SBbgntovn0kKHpFg7UogZvaZiOue/q1SBo9ktHzQAAAAASUVORK5CYII=',
},
{
type: 'circle',
text: '结束',
icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAA1BJREFUOBFtVE1IVUEYPXOf+tq40Y3vPcmFIdSjIorWoRG0ERWUgnb5FwVhYQSl72oUoZAboxKNFtWiwKRN0M+jpfSzqJAQclHo001tKkjl3emc8V69igP3znzfnO/M9zcDcKT67azmjYWTwl9Vn7Vumeqzj1DVb6cleQY4oAVnIOPb+mKAGxQmKI5CWNJ2aLPatxWa3aB9K7/fB+/Z0jUF6TmMlFLQqrkECWQzOZxYGjTlOl8eeKaIY5yHnFn486xBustDjWT6dG7pmjHOJd+33t0iitTPkK6tEvjxq4h2MozQ6WFSX/LkDUGfFwfhEZj1Auz/U4pyAi5Sznd7uKzznXeVHlI/Aywmk6j7fsUsEuCGADrWARXXwjxWQsUbIupDHJI7kF5dRktg0eN81IbiZXiTESic50iwS+t1oJgL83jAiBupLDCQqwziaWSoAFSeIR3P5Xv5az00wyIn35QRYTwdSYbz8pH8fxUUAtxnFvYmEmgI0wYXUXcCCSpeEVpXlsRhBnCEATxWylL9+EKCAYhe1NGstUa6356kS9NVvt3DU2fd+Wtbm/+lSbylJqsqkSm9CRhvoJVlvKPvF1RKY/FcPn5j4UfIMLn8D4UYb54BNsilTDXKnF4CfTobA0FpoW/LSp306wkXM+XaOJhZaFkcNM82ASNAWMrhrUbRfmyeI1FvRBTpN06WKxa9BK0o2E4Pd3zfBBEwPsv9sQBnmLVbLEIZ/Xe9LYwJu/Er17W6HYVBc7vmuk0xUQ+pqxdom5Fnp55SiytXLPYoMXNM4u4SNSCFWnrVIzKG3EGyMXo6n/BQOe+bX3FClY4PwydVhthOZ9NnS+ntiLh0fxtlUJHAuGaFoVmttpVMeum0p3WEXbcll94l1wM/gZ0Ccczop77VvN2I7TlsZCsuXf1WHvWEhjO8DPtyOVg2/mvK9QqboEth+7pD6NUQC1HN/TwvydGBARi9MZSzLE4b8Ru3XhX2PBxf8E1er2A6516o0w4sIA+lwURhAON82Kwe2iDAC1Watq4XHaGQ7skLcFOtI5lDxuM2gZe6WFIotPAhbaeYlU4to5cuarF1QrcZ/lwrLaCJl66JBocYZnrNlvm2+MBCTmUymPrYZVbjdlr/BxlMjmNmNI3SAAAAAElFTkSuQmCC',
},
]
}

View File

@@ -1,11 +0,0 @@
export enum ToolbarTypeEnum {
ZOOM_IN = 'zoomIn',
ZOOM_OUT = 'zoomOut',
RESET_ZOOM = 'resetZoom',
UNDO = 'undo',
REDO = 'redo',
SNAPSHOT = 'snapshot',
VIEW_DATA = 'viewData',
}

View File

@@ -1,14 +0,0 @@
import { NodeConfig } from '@logicflow/core'
import { ToolbarTypeEnum } from './enum'
export interface NodeItem extends NodeConfig {
icon: string
}
export interface ToolbarConfig {
type?: string | ToolbarTypeEnum
tooltip?: string | boolean
icon?: string
disabled?: boolean
separate?: boolean
}

View File

@@ -1,17 +0,0 @@
import type LogicFlow from '@logicflow/core'
import { provide, inject } from 'vue'
const key = Symbol('flow-chart')
type Instance = {
logicFlow: LogicFlow
}
export function createFlowChartContext(instance: Instance) {
provide(key, instance)
}
export function useFlowChartContext(): Instance {
return inject(key) as Instance
}

View File

@@ -1,7 +1,7 @@
import { defHttp } from '/@/utils/http/axios'
import { BaseEntity } from '/#/web'
import { PageResult, Result } from '/#/axios'
import { BpmModelNode } from '/@/views/modules/bpm/model/ModelNode.api'
import { BpmModelNode } from '/@/views/modules/bpm/model/node/ModelNode.api'
/**
* 分页

View File

@@ -81,6 +81,7 @@
</div>
<bpm-model-edit ref="bpmModelEdit" @ok="queryPage" />
<bpm-model-node-list ref="bpmModelNodeList" />
<bpm-modeler ref="bpmModeler" />
</div>
</template>
@@ -93,11 +94,12 @@
import { copy, del, page, publish, verifyModel } from '/@/views/modules/bpm/model/Model.api'
import BQuery from '/@/components/Bootx/Query/BQuery.vue'
import { useDict } from '/@/hooks/bootx/useDict'
import { PUBLISHED, UNPUBLISHED } from './BpmModelNodeCode'
import { PUBLISHED, UNPUBLISHED } from './node/BpmModelNodeCode'
import { useMessage } from '/@/hooks/web/useMessage'
import { FormEditType } from '/@/enums/formTypeEnum'
import BpmModelEdit from './BpmModelEdit.vue'
import BpmModelNodeList from './BpmModelNodeList.vue'
import BpmModelNodeList from './node/BpmModelNodeList.vue'
import BpmModeler from './BpmModeler.vue'
const { handleTableChange, resetQueryParams, pageQueryResHandel, pagination, pages, model, loading } = useTablePage(queryPage)
const { dictConvert } = useDict()

File diff suppressed because one or more lines are too long

View File

@@ -117,7 +117,7 @@
USER_GROUP,
USER,
ROLE,
} from '/@/views/modules/bpm/model/BpmModelNodeCode'
} from '/@/views/modules/bpm/model/node/BpmModelNodeCode'
import BUserSelectModal from '/@/components/Bootx/UserSelectModal/BUserSelectModal.vue'
import BRoleSelectModal from '/@/components/Bootx/RoleSelectModal/BRoleSelectModal.vue'
import { UserInfo } from '/@/views/modules/system/user/User.api'

View File

@@ -51,8 +51,8 @@
import { useMessage } from '/@/hooks/web/useMessage'
import { useDict } from '/@/hooks/bootx/useDict'
import { $ref } from 'vue/macros'
import { BpmModel } from './Model.api'
import { PUBLISHED } from '/@/views/modules/bpm/model/BpmModelNodeCode'
import { BpmModel } from '../Model.api'
import { PUBLISHED } from '/@/views/modules/bpm/model/node/BpmModelNodeCode'
import { BpmModelNode, del, listByModelId, sync } from './ModelNode.api'
import BpmModelNodeEdit from './BpmModelNodeEdit.vue'
import { FormEditType } from '/@/enums/formTypeEnum'

View File

@@ -0,0 +1,82 @@
import { defHttp } from '/@/utils/http/axios'
import { PageResult, Result } from '/#/axios'
import { BaseEntity } from '/#/web'
/**
* 分页
*/
export function page(params) {
return defHttp.get<Result<PageResult<QuerySql>>>({
url: '/query/sql/page',
params,
})
}
/**
* 查询全部
*/
export function findAll() {
return defHttp.get<Result<QuerySql[]>>({
url: '/query/sql/findAll',
})
}
/**
* 获取单条
*/
export function get(id) {
return defHttp.get<Result<QuerySql>>({
url: '/query/sql/findById',
params: { id },
})
}
/**
* 添加
*/
export function add(obj: QuerySql) {
return defHttp.post({
url: '/query/sql/add',
data: obj,
})
}
/**
* 更新
*/
export function update(obj: QuerySql) {
return defHttp.post({
url: '/query/sql/update',
data: obj,
})
}
/**
* 删除
*/
export function del(id) {
return defHttp.delete({
url: '/query/sql/delete',
params: { id },
})
}
/**
* SQL查询语句
*/
export interface QuerySql extends BaseEntity {
// 数据源ID
databaseId?: string
// 名称
name?: string
// sql语句
sql?: string
// 是否集合
isList?: boolean
// SQL查询参数
params?: string
// SQL查询结果字段
fields?: string
// 备注
remark?: string
}

View File

@@ -0,0 +1,133 @@
<template>
<basic-modal
defaultFullscreen
v-bind="$attrs"
:loading="confirmLoading"
:title="title"
:width="modalWidth"
:visible="visible"
:mask-closable="showable"
@cancel="handleCancel"
>
<a-form
class="small-from-item"
ref="formRef"
:model="form"
:rules="rules"
:validate-trigger="['blur', 'change']"
:label-col="labelCol"
:wrapper-col="wrapperCol"
>
<a-form-item label="主键" :hidden="true">
<a-input v-model:value="form.id" :disabled="showable" />
</a-form-item>
<a-form-item label="数据源ID" name="databaseId">
<a-input v-model:value="form.databaseId" :disabled="showable" placeholder="请输入数据源ID" />
</a-form-item>
<a-form-item label="名称" name="name">
<a-input v-model:value="form.name" :disabled="showable" placeholder="请输入名称" />
</a-form-item>
<a-form-item label="sql语句" name="sql">
<a-input v-model:value="form.sql" :disabled="showable" placeholder="请输入sql语句" />
</a-form-item>
<a-form-item label="是否集合" name="isList">
<a-input v-model:value="form.isList" :disabled="showable" placeholder="请输入是否集合" />
</a-form-item>
<a-form-item label="SQL查询参数" name="params">
<a-input v-model:value="form.params" :disabled="showable" placeholder="请输入SQL查询参数" />
</a-form-item>
<a-form-item label="SQL查询结果字段" name="fields">
<a-input v-model:value="form.fields" :disabled="showable" placeholder="请输入SQL查询结果字段" />
</a-form-item>
</a-form>
<template #footer>
<a-space>
<a-button key="cancel" @click="handleCancel">取消</a-button>
<a-button v-if="!showable" key="forward" :loading="confirmLoading" type="primary" @click="handleOk">保存</a-button>
</a-space>
</template>
</basic-modal>
</template>
<script lang="ts" setup>
import { nextTick, reactive } from 'vue'
import { $ref } from 'vue/macros'
import useFormEdit from '/@/hooks/bootx/useFormEdit'
import { add, get, update, QuerySql } from './QuerySql.api'
import { FormInstance, Rule } from 'ant-design-vue/lib/form'
import { FormEditType } from '/@/enums/formTypeEnum'
import { BasicModal } from '/@/components/Modal'
const {
initFormEditType,
handleCancel,
search,
labelCol,
wrapperCol,
modalWidth,
title,
confirmLoading,
visible,
editable,
showable,
formEditType,
} = useFormEdit()
// 表单
const formRef = $ref<FormInstance>()
let form = $ref<QuerySql>({
id: null,
databaseId: undefined,
name: '',
sql: '',
isList: false,
params: undefined,
fields: undefined,
})
// 校验
const rules = reactive({} as Record<string, Rule[]>)
// 事件
const emits = defineEmits(['ok'])
// 入口
function init(id, editType: FormEditType) {
initFormEditType(editType)
resetForm()
getInfo(id, editType)
}
// 获取信息
function getInfo(id, editType: FormEditType) {
if ([FormEditType.Edit, FormEditType.Show].includes(editType)) {
confirmLoading.value = true
get(id).then(({ data }) => {
form = data
confirmLoading.value = false
})
} else {
confirmLoading.value = false
}
}
// 保存
function handleOk() {
formRef?.validate().then(async () => {
confirmLoading.value = true
if (formEditType.value === FormEditType.Add) {
await add(form)
} else if (formEditType.value === FormEditType.Edit) {
await update(form)
}
confirmLoading.value = false
handleCancel()
emits('ok')
})
}
// 重置表单
function resetForm() {
nextTick(() => {
formRef?.resetFields()
})
}
defineExpose({
init,
})
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,121 @@
<template>
<div>
<div class="m-3 p-3 pt-5 bg-white">
<b-query :query-params="model.queryParam" :fields="fields" @query="queryPage" @reset="resetQueryParams" />
</div>
<div class="m-3 p-3 bg-white">
<vxe-toolbar ref="xToolbar" custom :refresh="{ query: queryPage }">
<template #buttons>
<a-space>
<a-button type="primary" pre-icon="ant-design:plus-outlined" @click="add">新建</a-button>
</a-space>
</template>
</vxe-toolbar>
<vxe-table row-id="id" ref="xTable" :data="pagination.records" :loading="loading">
<vxe-column type="seq" width="60" />
<vxe-column field="databaseId" title="数据源ID" />
<vxe-column field="name" title="名称" />
<vxe-column field="sql" title="sql语句" />
<vxe-column field="isList" title="是否集合">
<template #default="{ row }">
<a-tag>{{ row.isList ? '是' : '否' }}</a-tag>
</template>
</vxe-column>
<vxe-column field="remark" title="备注" />
<vxe-column field="createTime" title="创建时间" />
<vxe-column fixed="right" width="220" :showOverflow="false" title="操作">
<template #default="{ row }">
<span>
<a-link @click="show(row)">查看</a-link>
</span>
<a-divider type="vertical" />
<span>
<a-link @click="edit(row)">编辑</a-link>
</span>
<span>
<a-link @click="edit(row)">测试</a-link>
</span>
<a-divider type="vertical" />
<a-popconfirm title="是否删除" @confirm="remove(row)" okText="是" cancelText="否">
<a-link danger>删除</a-link>
</a-popconfirm>
</template>
</vxe-column>
</vxe-table>
<vxe-pager
size="medium"
:loading="loading"
:current-page="pagination.current"
:page-size="pagination.size"
:total="pagination.total"
@page-change="handleTableChange"
/>
<query-sql-edit ref="querySqlEdit" @ok="queryPage" />
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted } from 'vue'
import { $ref } from 'vue/macros'
import { del, page } from './QuerySql.api'
import useTablePage from '/@/hooks/bootx/useTablePage'
import QuerySqlEdit from './QuerySqlEdit.vue'
import { VxeTableInstance, VxeToolbarInstance } from 'vxe-table'
import BQuery from '/@/components/Bootx/Query/BQuery.vue'
import { FormEditType } from '/@/enums/formTypeEnum'
import { useMessage } from '/@/hooks/web/useMessage'
import { QueryField } from '/@/components/Bootx/Query/Query'
// 使用hooks
const { handleTableChange, pageQueryResHandel, resetQueryParams, pagination, pages, model, loading } = useTablePage(queryPage)
const { notification, createMessage, createConfirm } = useMessage()
// 查询条件
const fields = [] as QueryField[]
const xTable = $ref<VxeTableInstance>()
const xToolbar = $ref<VxeToolbarInstance>()
const querySqlEdit = $ref<any>()
onMounted(() => {
vxeBind()
queryPage()
})
function vxeBind() {
xTable?.connect(xToolbar as VxeToolbarInstance)
}
// 分页查询
function queryPage() {
loading.value = true
page({
...model.queryParam,
...pages,
}).then(({ data }) => {
pageQueryResHandel(data)
})
}
// 新增
function add() {
querySqlEdit.init(null, FormEditType.Add)
}
// 编辑
function edit(record) {
querySqlEdit.init(record.id, FormEditType.Edit)
}
// 查看
function show(record) {
querySqlEdit.init(record.id, FormEditType.Show)
}
// 删除
function remove(record) {
del(record.id).then(() => {
createMessage.success('删除成功')
queryPage()
})
}
</script>
<style lang="less" scoped></style>

View File

@@ -1324,24 +1324,6 @@
"@jridgewell/resolve-uri" "^3.0.3"
"@jridgewell/sourcemap-codec" "^1.4.10"
"@logicflow/core@^1.1.13", "@logicflow/core@^1.1.21":
version "1.1.21"
resolved "https://registry.npmmirror.com/@logicflow/core/-/core-1.1.21.tgz#80f3651a9559e7bf90631814f5cd3fe61e543e7a"
integrity sha512-LfFnHV009HhozPSWyFlLwsIsEXniFtjJKHGxQVxzRDwKqsgBOUBwyCTbDgejTIc4fw0zaINYhG5YRFSPGsDzWQ==
dependencies:
"@types/mousetrap" "^1.6.4"
mousetrap "^1.6.5"
preact "^10.4.8"
"@logicflow/extension@^1.1.13":
version "1.1.21"
resolved "https://registry.npmmirror.com/@logicflow/extension/-/extension-1.1.21.tgz#efa91c2fe6712568270e222ff7b73859caf09d45"
integrity sha512-jUrChV89BC7QsyejGt3tIWX9/EGI/Rh+LFsht0OZDafgabk4D99D3J8Glg1UC/unh9dOUNDoD3Hs3xaO8gDBzg==
dependencies:
"@logicflow/core" "^1.1.21"
ids "^1.0.0"
preact "^10.4.8"
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
resolved "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
@@ -1750,11 +1732,6 @@
resolved "https://registry.npmmirror.com/@types/mockjs/-/mockjs-1.0.6.tgz#dafdd5de65156f7aa63eab4e29fa3d532cd95609"
integrity sha512-Yu5YlqbYZyqsd6LjO4e8ONJDN9pTSnciHDcRP4teNOh/au2b8helFhgRx+3w8xsTFEnwr9jtfTVJbAx+eYmlHA==
"@types/mousetrap@^1.6.4":
version "1.6.9"
resolved "https://registry.npmmirror.com/@types/mousetrap/-/mousetrap-1.6.9.tgz#f1ef9adbd1eac3466f21b6988b1c82c633a45340"
integrity sha512-HUAiN65VsRXyFCTicolwb5+I7FM6f72zjMWr+ajGk+YTvzBgXqa2A5U7d+rtsouAkunJ5U4Sb5lNJjo9w+nmXg==
"@types/node@*", "@types/node@>=12":
version "18.0.3"
resolved "https://registry.npmmirror.com/@types/node/-/node-18.0.3.tgz#463fc47f13ec0688a33aec75d078a0541a447199"
@@ -3107,11 +3084,6 @@ chardet@^0.7.0:
resolved "https://registry.npmmirror.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
china-area-data@^5.0.1:
version "5.0.1"
resolved "https://registry.npmmirror.com/china-area-data/-/china-area-data-5.0.1.tgz#7943b83a0619f033bb5893da80cb46e52e44be66"
integrity sha512-BQDPpiv5Nn+018ekcJK2oSD9PAD+E1bvXB0wgabc//dFVS/KvRqCgg0QOEUt3vBkx9XzB5a9BmkJCEZDBxVjVw==
"chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.2:
version "3.5.3"
resolved "https://registry.npmmirror.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
@@ -7126,11 +7098,6 @@ modify-values@^1.0.0:
resolved "https://registry.npmmirror.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022"
integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==
mousetrap@^1.6.5:
version "1.6.5"
resolved "https://registry.npmmirror.com/mousetrap/-/mousetrap-1.6.5.tgz#8a766d8c272b08393d5f56074e0b5ec183485bf9"
integrity sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA==
mozjpeg@^7.0.0:
version "7.1.1"
resolved "https://registry.npmmirror.com/mozjpeg/-/mozjpeg-7.1.1.tgz#dfb61953536e66fcabd4ae795e7a312d42a51f18"
@@ -7943,11 +7910,6 @@ posthtml@^0.9.2:
posthtml-parser "^0.2.0"
posthtml-render "^1.0.5"
preact@^10.4.8:
version "10.9.0"
resolved "https://registry.npmmirror.com/preact/-/preact-10.9.0.tgz#69b282b26926b66481c9ae3450cf68610fee29ff"
integrity sha512-jO6/OvCRL+OT8gst/+Q2ir7dMybZAX8ioP02Zmzh3BkQMHLyqZSujvxbUriXvHi8qmhcHKC2Gwbog6Kt+YTh+Q==
preact@^10.5.13:
version "10.11.3"
resolved "https://registry.npmmirror.com/preact/-/preact-10.11.3.tgz#8a7e4ba19d3992c488b0785afcc0f8aa13c78d19"