184 changed files with 8 additions and 12744 deletions
@ -1,26 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { computed, unref } from 'vue' |
||||
import { Tooltip } from 'ant-design-vue' |
||||
import { useFullscreen } from '@vueuse/core' |
||||
import { FullscreenExitOutlined, FullscreenOutlined } from '@ant-design/icons-vue' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
|
||||
defineOptions({ name: 'FullScreen' }) |
||||
const { t } = useI18n() |
||||
const { toggle, isFullscreen } = useFullscreen() |
||||
// 重新检查全屏状态 |
||||
isFullscreen.value = !!document.fullscreenElement |
||||
|
||||
const getTitle = computed(() => { |
||||
return unref(isFullscreen) ? t('layout.header.tooltipExitFull') : t('layout.header.tooltipEntryFull') |
||||
}) |
||||
</script> |
||||
|
||||
<template> |
||||
<Tooltip :title="getTitle" placement="bottom" :mouse-enter-delay="0.5"> |
||||
<span @click="toggle"> |
||||
<FullscreenOutlined v-if="!isFullscreen" /> |
||||
<FullscreenExitOutlined v-else /> |
||||
</span> |
||||
</Tooltip> |
||||
</template> |
@ -1,121 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { computed, ref, unref, watch } from 'vue' |
||||
import { Avatar, List, Tag, Typography } from 'ant-design-vue' |
||||
import type { ListItem } from './data' |
||||
import { isNumber } from '@/utils/is' |
||||
|
||||
const props = defineProps({ |
||||
list: { |
||||
type: Array as PropType<ListItem[]>, |
||||
default: () => [], |
||||
}, |
||||
pageSize: { |
||||
type: [Boolean, Number] as PropType<boolean | number>, |
||||
default: 5, |
||||
}, |
||||
currentPage: { |
||||
type: Number, |
||||
default: 1, |
||||
}, |
||||
titleRows: { |
||||
type: Number, |
||||
default: 1, |
||||
}, |
||||
descRows: { |
||||
type: Number, |
||||
default: 2, |
||||
}, |
||||
onTitleClick: { |
||||
type: Function as PropType<(Recordable) => void>, |
||||
}, |
||||
}) |
||||
const emit = defineEmits(['update:currentPage']) |
||||
const current = ref(props.currentPage || 1) |
||||
const getData = computed(() => { |
||||
const { pageSize, list } = props |
||||
if (pageSize === false) |
||||
return [] |
||||
const size = isNumber(pageSize) ? pageSize : 5 |
||||
return list.slice(size * (unref(current) - 1), size * unref(current)) |
||||
}) |
||||
watch( |
||||
() => props.currentPage, |
||||
(v) => { |
||||
current.value = v |
||||
}, |
||||
) |
||||
const isTitleClickable = computed(() => !!props.onTitleClick) |
||||
const getPagination = computed(() => { |
||||
const { list, pageSize } = props |
||||
// compatible line 104 |
||||
// if typeof pageSize is boolean, Number(true) && 5 = 5, Number(false) && 5 = 0 |
||||
const size = isNumber(pageSize) ? pageSize : Number(pageSize) && 5 |
||||
if (size > 0 && list && list.length > size) { |
||||
return { |
||||
total: list.length, |
||||
pageSize: size, |
||||
// size: 'small', |
||||
current: unref(current), |
||||
onChange(page) { |
||||
current.value = page |
||||
emit('update:currentPage', page) |
||||
}, |
||||
} |
||||
} |
||||
else { |
||||
return false |
||||
} |
||||
}) |
||||
|
||||
function handleTitleClick(item: ListItem) { |
||||
props.onTitleClick && props.onTitleClick(item) |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<List class="display-none" bordered :pagination="getPagination"> |
||||
<template v-for="item in getData" :key="item.id"> |
||||
<List.Item class="cursor-pointer overflow-hidden p-1.5 transition-all delay-300"> |
||||
<List.Item.Meta> |
||||
<template #title> |
||||
<div class="mb-2 font-normal"> |
||||
<Typography.Paragraph |
||||
style="width: 100%; margin-bottom: 0 !important" |
||||
:style="{ cursor: isTitleClickable ? 'pointer' : '' }" |
||||
:delete="!!item.titleDelete" |
||||
:ellipsis="$props.titleRows && $props.titleRows > 0 ? { rows: $props.titleRows, tooltip: !!item.title } : false" |
||||
:content="item.title" |
||||
@click="handleTitleClick(item)" |
||||
/> |
||||
<div v-if="item.extra" class="float-right mr-0 font-normal -mt-0.375"> |
||||
<Tag class="mr-0" :color="item.color"> |
||||
{{ item.extra }} |
||||
</Tag> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<template #avatar> |
||||
<Avatar v-if="item.avatar" class="mt-1" :src="item.avatar" /> |
||||
<span v-else> {{ item.avatar }}</span> |
||||
</template> |
||||
|
||||
<template #description> |
||||
<div> |
||||
<div v-if="item.description" class="text-xs/18"> |
||||
<Typography.Paragraph |
||||
style="width: 100%; margin-bottom: 0 !important" |
||||
:ellipsis="$props.descRows && $props.descRows > 0 ? { rows: $props.descRows, tooltip: !!item.description } : false" |
||||
:content="item.description" |
||||
/> |
||||
</div> |
||||
<div class="mt-1 text-xs/18"> |
||||
{{ item.datetime }} |
||||
</div> |
||||
</div> |
||||
</template> |
||||
</List.Item.Meta> |
||||
</List.Item> |
||||
</template> |
||||
</List> |
||||
</template> |
@ -1,192 +0,0 @@
|
||||
export interface ListItem { |
||||
id: string |
||||
avatar: string |
||||
// 通知的标题内容
|
||||
title: string |
||||
// 是否在标题上显示删除线
|
||||
titleDelete?: boolean |
||||
datetime: string |
||||
type: string |
||||
read?: boolean |
||||
description: string |
||||
clickClose?: boolean |
||||
extra?: string |
||||
color?: string |
||||
} |
||||
|
||||
export interface TabItem { |
||||
key: string |
||||
name: string |
||||
list: ListItem[] |
||||
unreadlist?: ListItem[] |
||||
} |
||||
|
||||
export const tabListData: TabItem[] = [ |
||||
{ |
||||
key: '1', |
||||
name: '通知', |
||||
list: [ |
||||
{ |
||||
id: '000000001', |
||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png', |
||||
title: '你收到了 14 份新周报', |
||||
description: '', |
||||
datetime: '2017-08-09', |
||||
type: '1', |
||||
}, |
||||
{ |
||||
id: '000000002', |
||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png', |
||||
title: '你推荐的 曲妮妮 已通过第三轮面试', |
||||
description: '', |
||||
datetime: '2017-08-08', |
||||
type: '1', |
||||
}, |
||||
{ |
||||
id: '000000003', |
||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png', |
||||
title: '这种模板可以区分多种通知类型', |
||||
description: '', |
||||
datetime: '2017-08-07', |
||||
// read: true,
|
||||
type: '1', |
||||
}, |
||||
{ |
||||
id: '000000004', |
||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png', |
||||
title: '左侧图标用于区分不同的类型', |
||||
description: '', |
||||
datetime: '2017-08-07', |
||||
type: '1', |
||||
}, |
||||
{ |
||||
id: '000000005', |
||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png', |
||||
title: '标题可以设置自动显示省略号,本例中标题行数已设为1行,如果内容超过1行将自动截断并支持tooltip显示完整标题。', |
||||
description: '', |
||||
datetime: '2017-08-07', |
||||
type: '1', |
||||
}, |
||||
{ |
||||
id: '000000006', |
||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png', |
||||
title: '左侧图标用于区分不同的类型', |
||||
description: '', |
||||
datetime: '2017-08-07', |
||||
type: '1', |
||||
}, |
||||
{ |
||||
id: '000000007', |
||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png', |
||||
title: '左侧图标用于区分不同的类型', |
||||
description: '', |
||||
datetime: '2017-08-07', |
||||
type: '1', |
||||
}, |
||||
{ |
||||
id: '000000008', |
||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png', |
||||
title: '左侧图标用于区分不同的类型', |
||||
description: '', |
||||
datetime: '2017-08-07', |
||||
type: '1', |
||||
}, |
||||
{ |
||||
id: '000000009', |
||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png', |
||||
title: '左侧图标用于区分不同的类型', |
||||
description: '', |
||||
datetime: '2017-08-07', |
||||
type: '1', |
||||
}, |
||||
{ |
||||
id: '000000010', |
||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png', |
||||
title: '左侧图标用于区分不同的类型', |
||||
description: '', |
||||
datetime: '2017-08-07', |
||||
type: '1', |
||||
}, |
||||
], |
||||
}, |
||||
{ |
||||
key: '2', |
||||
name: '消息', |
||||
list: [ |
||||
{ |
||||
id: '000000006', |
||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg', |
||||
title: '曲丽丽 评论了你', |
||||
description: '描述信息描述信息描述信息', |
||||
datetime: '2017-08-07', |
||||
type: '2', |
||||
clickClose: true, |
||||
}, |
||||
{ |
||||
id: '000000007', |
||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg', |
||||
title: '朱偏右 回复了你', |
||||
description: '这种模板用于提醒谁与你发生了互动', |
||||
datetime: '2017-08-07', |
||||
type: '2', |
||||
clickClose: true, |
||||
}, |
||||
{ |
||||
id: '000000008', |
||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg', |
||||
title: '标题', |
||||
description: |
||||
'请将鼠标移动到此处,以便测试超长的消息在此处将如何处理。本例中设置的描述最大行数为2,超过2行的描述内容将被省略并且可以通过tooltip查看完整内容', |
||||
datetime: '2017-08-07', |
||||
type: '2', |
||||
clickClose: true, |
||||
}, |
||||
], |
||||
}, |
||||
{ |
||||
key: '3', |
||||
name: '待办', |
||||
list: [ |
||||
{ |
||||
id: '000000009', |
||||
avatar: '', |
||||
title: '任务名称', |
||||
description: '任务需要在 2017-01-12 20:00 前启动', |
||||
datetime: '', |
||||
extra: '未开始', |
||||
color: '', |
||||
type: '3', |
||||
}, |
||||
{ |
||||
id: '000000010', |
||||
avatar: '', |
||||
title: '第三方紧急代码变更', |
||||
description: '冠霖 需在 2017-01-07 前完成代码变更任务', |
||||
datetime: '', |
||||
extra: '马上到期', |
||||
color: 'red', |
||||
type: '3', |
||||
}, |
||||
{ |
||||
id: '000000011', |
||||
avatar: '', |
||||
title: '信息安全考试', |
||||
description: '指派竹尔于 2017-01-09 前完成更新并发布', |
||||
datetime: '', |
||||
extra: '已耗时 8 天', |
||||
color: 'gold', |
||||
type: '3', |
||||
}, |
||||
{ |
||||
id: '000000012', |
||||
avatar: '', |
||||
title: 'ABCD 版本发布', |
||||
description: '指派竹尔于 2017-01-09 前完成更新并发布', |
||||
datetime: '', |
||||
extra: '进行中', |
||||
color: 'blue', |
||||
type: '3', |
||||
}, |
||||
], |
||||
}, |
||||
] |
@ -1,35 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { computed, onMounted } from 'vue' |
||||
import { Badge, Tooltip } from 'ant-design-vue' |
||||
import { BellOutlined } from '@ant-design/icons-vue' |
||||
import { storeToRefs } from 'pinia' |
||||
import { useGo } from '@/hooks/web/usePage' |
||||
import { PageEnum } from '@/enums/pageEnum' |
||||
import { useUserMessageStore } from '@/store/modules/userMessage' |
||||
|
||||
const go = useGo() |
||||
|
||||
const store = useUserMessageStore() |
||||
const { unreadCount } = storeToRefs(store) |
||||
const tips = computed<string>(() => { |
||||
if (unreadCount.value === 0) |
||||
return '查看站内信' |
||||
|
||||
return `查看站内信: 未读 ${unreadCount.value} 条` |
||||
}) |
||||
|
||||
onMounted(async () => { |
||||
// 通过store进行更新 |
||||
store.updateUnreadCount() |
||||
}) |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<Tooltip :title="tips"> |
||||
<Badge :count="unreadCount" :offset="[0, 15]" size="small" @click="go({ path: PageEnum.MESSAGE_PAGE })"> |
||||
<BellOutlined /> |
||||
</Badge> |
||||
</Tooltip> |
||||
</div> |
||||
</template> |
@ -1,35 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { computed, unref } from 'vue' |
||||
import { Icon } from '@/components/Icon' |
||||
|
||||
import { useDesign } from '@/hooks/web/useDesign' |
||||
import { useHeaderSetting } from '@/hooks/setting/useHeaderSetting' |
||||
import { useMenuSetting } from '@/hooks/setting/useMenuSetting' |
||||
import { triggerWindowResize } from '@/utils/event' |
||||
|
||||
defineOptions({ name: 'FoldButton' }) |
||||
|
||||
const { prefixCls } = useDesign('multiple-tabs-content') |
||||
const { getShowMenu, setMenuSetting } = useMenuSetting() |
||||
const { getShowHeader, setHeaderSetting } = useHeaderSetting() |
||||
|
||||
const getIsUnFold = computed(() => !unref(getShowMenu) && !unref(getShowHeader)) |
||||
|
||||
const getIcon = computed(() => (unref(getIsUnFold) ? 'codicon:screen-normal' : 'codicon:screen-full')) |
||||
|
||||
function handleFold() { |
||||
const isUnFold = unref(getIsUnFold) |
||||
setMenuSetting({ |
||||
show: isUnFold, |
||||
hidden: !isUnFold, |
||||
}) |
||||
setHeaderSetting({ show: isUnFold }) |
||||
triggerWindowResize() |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<span :class="`${prefixCls}__extra-fold`" @click="handleFold"> |
||||
<Icon :icon="getIcon" /> |
||||
</span> |
||||
</template> |
@ -1,28 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { ref } from 'vue' |
||||
import { RedoOutlined } from '@ant-design/icons-vue' |
||||
import { useDesign } from '@/hooks/web/useDesign' |
||||
import { useTabs } from '@/hooks/web/useTabs' |
||||
|
||||
defineOptions({ name: 'TabRedo' }) |
||||
|
||||
const loading = ref(false) |
||||
|
||||
const { prefixCls } = useDesign('multiple-tabs-content') |
||||
const { refreshPage } = useTabs() |
||||
|
||||
async function handleRedo() { |
||||
loading.value = true |
||||
await refreshPage() |
||||
setTimeout(() => { |
||||
loading.value = false |
||||
// Animation execution time |
||||
}, 1200) |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<span :class="`${prefixCls}__extra-redo`" @click="handleRedo"> |
||||
<RedoOutlined :spin="loading" /> |
||||
</span> |
||||
</template> |
@ -1,31 +0,0 @@
|
||||
import type { AppRouteModule } from '@/router/types' |
||||
|
||||
import { LAYOUT } from '@/router/constant' |
||||
import { t } from '@/hooks/web/useI18n' |
||||
|
||||
const about: AppRouteModule = { |
||||
path: '/about', |
||||
name: 'About', |
||||
component: LAYOUT, |
||||
redirect: '/about/index', |
||||
meta: { |
||||
hideChildrenInMenu: true, |
||||
icon: 'ant-design:pushpin-filled', |
||||
title: t('routes.dashboard.about'), |
||||
orderNo: 100000, |
||||
}, |
||||
children: [ |
||||
{ |
||||
path: 'index', |
||||
name: 'AboutPage', |
||||
component: () => import('@/views/base/about/index.vue'), |
||||
meta: { |
||||
title: t('routes.dashboard.about'), |
||||
icon: 'ant-design:pushpin-filled', |
||||
hideMenu: true, |
||||
}, |
||||
}, |
||||
], |
||||
} |
||||
|
||||
export default about |
@ -1,8 +0,0 @@
|
||||
// github repo url
|
||||
export const GITHUB_URL = 'https://gitee.com/xingyuv/vue-vben-admin' |
||||
|
||||
// vue-vben-admin-next-doc
|
||||
export const DOC_URL = 'http://vben-doc.x-surge.com/' |
||||
|
||||
// site url
|
||||
export const SITE_URL = 'http://static.yudao.iocoder.cn/mp/xinyu370.jpeg' |
@ -1,107 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { h } from 'vue' |
||||
import { Tag } from 'ant-design-vue' |
||||
import { PageWrapper } from '@/components/Page' |
||||
import type { DescItem } from '@/components/Description' |
||||
import { Description, useDescription } from '@/components/Description' |
||||
import { DOC_URL, GITHUB_URL, SITE_URL } from '@/settings/siteSetting' |
||||
|
||||
const { pkg, lastBuildTime } = __APP_INFO__ |
||||
|
||||
const { dependencies, devDependencies, name, version } = pkg |
||||
|
||||
const schema: DescItem[] = [] |
||||
const devSchema: DescItem[] = [] |
||||
|
||||
const commonTagRender = (color: string) => curVal => h(Tag, { color }, () => curVal) |
||||
const commonLinkRender = (text: string) => href => h('a', { href, target: '_blank' }, text) |
||||
|
||||
const infoSchema: DescItem[] = [ |
||||
{ |
||||
label: '版本', |
||||
field: 'version', |
||||
render: commonTagRender('blue'), |
||||
}, |
||||
{ |
||||
label: '最后编译时间', |
||||
field: 'lastBuildTime', |
||||
render: commonTagRender('blue'), |
||||
}, |
||||
{ |
||||
label: '文档地址', |
||||
field: 'doc', |
||||
render: commonLinkRender('文档地址'), |
||||
}, |
||||
{ |
||||
label: '预览地址', |
||||
field: 'preview', |
||||
render: commonLinkRender('预览地址'), |
||||
}, |
||||
{ |
||||
label: 'Github', |
||||
field: 'github', |
||||
render: commonLinkRender('Github'), |
||||
}, |
||||
{ |
||||
label: '外包服务', |
||||
field: 'outsourcing', |
||||
render: commonLinkRender('外包服务'), |
||||
}, |
||||
] |
||||
|
||||
const infoData = { |
||||
version, |
||||
lastBuildTime, |
||||
doc: DOC_URL, |
||||
preview: SITE_URL, |
||||
github: GITHUB_URL, |
||||
outsourcing: SITE_URL, |
||||
} |
||||
|
||||
Object.keys(dependencies).forEach((key) => { |
||||
schema.push({ field: key, label: key }) |
||||
}) |
||||
|
||||
Object.keys(devDependencies).forEach((key) => { |
||||
devSchema.push({ field: key, label: key }) |
||||
}) |
||||
|
||||
const [register] = useDescription({ |
||||
title: '生产环境依赖', |
||||
data: dependencies, |
||||
schema, |
||||
column: 3, |
||||
}) |
||||
|
||||
const [registerDev] = useDescription({ |
||||
title: '开发环境依赖', |
||||
data: devDependencies, |
||||
schema: devSchema, |
||||
column: 3, |
||||
}) |
||||
|
||||
const [infoRegister] = useDescription({ |
||||
title: '项目信息', |
||||
data: infoData, |
||||
schema: infoSchema, |
||||
column: 2, |
||||
}) |
||||
</script> |
||||
|
||||
<template> |
||||
<PageWrapper title="关于"> |
||||
<template #headerContent> |
||||
<div class="flex items-center justify-between"> |
||||
<span class="flex-1"> |
||||
<a :href="GITHUB_URL" target="_blank">{{ name }}</a> |
||||
基于Vue3.0、Vite、 Ant-Design-Vue 、TypeScript |
||||
的后台解决方案,目标是为中大型项目开发,提供现成的开箱解决方案及丰富的示例,原则上不会限制任何代码用于商用。<br> |
||||
同时,我们也提供<a :href="SITE_URL" target="_blank">外包服务</a>。 |
||||
</span> |
||||
</div> |
||||
</template> |
||||
<Description class="enter-y" @register="infoRegister" /> |
||||
<Description class="enter-y my-4" @register="register" /> |
||||
<Description class="enter-y" @register="registerDev" /> |
||||
</PageWrapper> |
||||
</template> |
@ -1,28 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { List, Switch } from 'ant-design-vue' |
||||
import { msgNotifyList } from './data' |
||||
import { CollapseContainer } from '@/components/Container/index' |
||||
|
||||
const ListItem = List.Item |
||||
const ListItemMeta = List.Item.Meta |
||||
</script> |
||||
|
||||
<template> |
||||
<CollapseContainer title="新消息通知" :can-expan="false"> |
||||
<List> |
||||
<template v-for="item in msgNotifyList" :key="item.key"> |
||||
<ListItem> |
||||
<ListItemMeta> |
||||
<template #title> |
||||
{{ item.title }} |
||||
<Switch class="float-right mr-7.5 mt-0" checked-children="开" un-checked-children="关" default-checked /> |
||||
</template> |
||||
<template #description> |
||||
<div>{{ item.description }}</div> |
||||
</template> |
||||
</ListItemMeta> |
||||
</ListItem> |
||||
</template> |
||||
</List> |
||||
</CollapseContainer> |
||||
</template> |
@ -1,3 +0,0 @@
|
||||
<template> |
||||
<div>开发中 definition</div> |
||||
</template> |
@ -1,43 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { ref } from 'vue' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { VFormCreate } from '@/components/FormDesign' |
||||
import { BasicModal, useModalInner } from '@/components/Modal' |
||||
import { getForm } from '@/api/bpm/form' |
||||
|
||||
defineOptions({ name: 'BpmFormModal' }) |
||||
|
||||
const emit = defineEmits(['success', 'register']) |
||||
const { t } = useI18n() |
||||
const { createMessage } = useMessage() |
||||
const formConfig = ref({ |
||||
schemas: [], |
||||
rule: [], |
||||
option: {}, |
||||
}) |
||||
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => { |
||||
setModalProps({ confirmLoading: false }) |
||||
const res = await getForm(data.record.id) |
||||
formConfig.value.schemas = res.fields |
||||
formConfig.value.option = res.conf |
||||
}) |
||||
|
||||
async function handleSubmit() { |
||||
try { |
||||
closeModal() |
||||
emit('success') |
||||
createMessage.success(t('common.saveSuccessText')) |
||||
} |
||||
finally { |
||||
setModalProps({ confirmLoading: false }) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<BasicModal v-bind="$attrs" title="表单详情" @register="registerModal" @ok="handleSubmit"> |
||||
<VFormCreate :form-config="formConfig" /> |
||||
</BasicModal> |
||||
</template> |
@ -1,9 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { VFormDesign } from '@/components/FormDesign' |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<VFormDesign /> |
||||
</div> |
||||
</template> |
@ -1,46 +0,0 @@
|
||||
import type { BasicColumn, FormSchema } from '@/components/Table' |
||||
import { useRender } from '@/components/Table' |
||||
import { DICT_TYPE } from '@/utils/dict' |
||||
|
||||
export const columns: BasicColumn[] = [ |
||||
{ |
||||
title: '编号', |
||||
dataIndex: 'id', |
||||
width: 100, |
||||
}, |
||||
{ |
||||
title: '表单名', |
||||
dataIndex: 'name', |
||||
width: 180, |
||||
}, |
||||
{ |
||||
title: '状态', |
||||
dataIndex: 'status', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDict(text, DICT_TYPE.COMMON_STATUS) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '备注', |
||||
dataIndex: 'remark', |
||||
width: 180, |
||||
}, |
||||
{ |
||||
title: '创建时间', |
||||
dataIndex: 'createTime', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDate(text) |
||||
}, |
||||
}, |
||||
] |
||||
|
||||
export const searchFormSchema: FormSchema[] = [ |
||||
{ |
||||
label: '表单名', |
||||
field: 'name', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
] |
@ -1,86 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import BpmFormModal from './FormModal.vue' |
||||
import { columns, searchFormSchema } from './form.data' |
||||
import { useGo } from '@/hooks/web/usePage' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { useModal } from '@/components/Modal' |
||||
import { IconEnum } from '@/enums/appEnum' |
||||
import { BasicTable, TableAction, useTable } from '@/components/Table' |
||||
import { deleteForm, getFormPage } from '@/api/bpm/form' |
||||
|
||||
defineOptions({ name: 'BpmForm' }) |
||||
|
||||
const go = useGo() |
||||
const { t } = useI18n() |
||||
const { createMessage } = useMessage() |
||||
const [registerModal, { openModal }] = useModal() |
||||
|
||||
const [registerTable, { reload }] = useTable({ |
||||
title: '流程表单列表', |
||||
api: getFormPage, |
||||
columns, |
||||
formConfig: { labelWidth: 120, schemas: searchFormSchema }, |
||||
useSearchForm: true, |
||||
showTableSetting: true, |
||||
actionColumn: { |
||||
width: 180, |
||||
title: t('common.action'), |
||||
dataIndex: 'action', |
||||
fixed: 'right', |
||||
}, |
||||
}) |
||||
|
||||
function handleCreate() { |
||||
// openModal(true, { isUpdate: false }) |
||||
} |
||||
|
||||
function openForm(record: Recordable) { |
||||
if (typeof record.id === 'number') |
||||
go({ name: 'BpmFormEditor', query: { id: record.id } }) |
||||
} |
||||
|
||||
function openDetail(record: Recordable) { |
||||
openModal(true, { record }) |
||||
} |
||||
|
||||
async function handleDelete(record: Recordable) { |
||||
await deleteForm(record.id) |
||||
createMessage.success(t('common.delSuccessText')) |
||||
reload() |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<BasicTable @register="registerTable"> |
||||
<template #toolbar> |
||||
<a-button v-auth="['bpm:form:create']" type="primary" :pre-icon="IconEnum.ADD" @click="handleCreate"> |
||||
{{ t('action.create') }} |
||||
</a-button> |
||||
</template> |
||||
<template #bodyCell="{ column, record }"> |
||||
<template v-if="column.key === 'action'"> |
||||
<TableAction |
||||
:actions="[ |
||||
{ icon: IconEnum.EDIT, label: t('action.edit'), auth: 'bpm:form:update', onClick: openForm.bind(null, record) }, |
||||
{ icon: IconEnum.VIEW, label: t('action.detail'), auth: 'bpm:form:query', onClick: openDetail.bind(null, record) }, |
||||
{ |
||||
icon: IconEnum.DELETE, |
||||
danger: true, |
||||
label: t('action.delete'), |
||||
auth: 'bpm:form:delete', |
||||
popConfirm: { |
||||
title: t('common.delMessage'), |
||||
placement: 'left', |
||||
confirm: handleDelete.bind(null, record), |
||||
}, |
||||
}, |
||||
]" |
||||
/> |
||||
</template> |
||||
</template> |
||||
</BasicTable> |
||||
<BpmFormModal @register="registerModal" @success="reload()" /> |
||||
</div> |
||||
</template> |
@ -1,58 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { ref, unref } from 'vue' |
||||
import { formSchema } from './group.data' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { BasicForm, useForm } from '@/components/Form' |
||||
import { BasicModal, useModalInner } from '@/components/Modal' |
||||
import { createUserGroup, getUserGroup, updateUserGroup } from '@/api/bpm/userGroup' |
||||
|
||||
defineOptions({ name: 'BpmGroupModal' }) |
||||
|
||||
const emit = defineEmits(['success', 'register']) |
||||
const { t } = useI18n() |
||||
const { createMessage } = useMessage() |
||||
const isUpdate = ref(true) |
||||
|
||||
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({ |
||||
labelWidth: 120, |
||||
baseColProps: { span: 24 }, |
||||
schemas: formSchema, |
||||
showActionButtonGroup: false, |
||||
actionColOptions: { span: 23 }, |
||||
}) |
||||
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => { |
||||
resetFields() |
||||
setModalProps({ confirmLoading: false }) |
||||
isUpdate.value = !!data?.isUpdate |
||||
if (unref(isUpdate)) { |
||||
const res = await getUserGroup(data.record.id) |
||||
setFieldsValue({ ...res }) |
||||
} |
||||
}) |
||||
|
||||
async function handleSubmit() { |
||||
try { |
||||
const values = await validate() as any |
||||
setModalProps({ confirmLoading: true }) |
||||
if (unref(isUpdate)) |
||||
await updateUserGroup(values) |
||||
else |
||||
await createUserGroup(values) |
||||
|
||||
closeModal() |
||||
emit('success') |
||||
createMessage.success(t('common.saveSuccessText')) |
||||
} |
||||
finally { |
||||
setModalProps({ confirmLoading: false }) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<BasicModal v-bind="$attrs" :title="isUpdate ? t('action.edit') : t('action.create')" @register="registerModal" @ok="handleSubmit"> |
||||
<BasicForm @register="registerForm" /> |
||||
</BasicModal> |
||||
</template> |
@ -1,134 +0,0 @@
|
||||
import type { BasicColumn, FormSchema } from '@/components/Table' |
||||
import { useRender } from '@/components/Table' |
||||
import { DICT_TYPE, getDictOptions } from '@/utils/dict' |
||||
import { getListSimpleUsers } from '@/api/system/user' |
||||
|
||||
let users: any[] = [] |
||||
|
||||
async function getUserList() { |
||||
const res = await getListSimpleUsers() |
||||
users = res |
||||
} |
||||
|
||||
await getUserList() |
||||
|
||||
export const columns: BasicColumn[] = [ |
||||
{ |
||||
title: '编号', |
||||
dataIndex: 'id', |
||||
width: 100, |
||||
}, |
||||
{ |
||||
title: '组名', |
||||
dataIndex: 'name', |
||||
width: 180, |
||||
}, |
||||
{ |
||||
title: '描述', |
||||
dataIndex: 'description', |
||||
width: 200, |
||||
}, |
||||
{ |
||||
title: '成员', |
||||
dataIndex: 'memberUserIds', |
||||
width: 180, |
||||
customRender: ({ record, text }) => { |
||||
const names: any[] = [] |
||||
if (text) { |
||||
for (const userId of record.memberUserIds) { |
||||
let isUser = false |
||||
users.forEach((user) => { |
||||
if (userId === user.id) { |
||||
names.push(user.nickname) |
||||
isUser = true |
||||
} |
||||
}) |
||||
if (!isUser) |
||||
names.push(`未知(${userId})`) |
||||
} |
||||
return useRender.renderTags(names) |
||||
} |
||||
}, |
||||
}, |
||||
{ |
||||
title: '状态', |
||||
dataIndex: 'status', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDict(text, DICT_TYPE.COMMON_STATUS) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '创建时间', |
||||
dataIndex: 'createTime', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDate(text) |
||||
}, |
||||
}, |
||||
] |
||||
|
||||
export const searchFormSchema: FormSchema[] = [ |
||||
{ |
||||
label: '组名', |
||||
field: 'name', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '状态', |
||||
field: 'status', |
||||
component: 'Select', |
||||
componentProps: { |
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS) as any, |
||||
}, |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '创建时间', |
||||
field: 'createTime', |
||||
component: 'RangePicker', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
] |
||||
|
||||
export const formSchema: FormSchema[] = [ |
||||
{ |
||||
label: '编号', |
||||
field: 'id', |
||||
show: false, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '组名', |
||||
field: 'name', |
||||
required: true, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '描述', |
||||
field: 'description', |
||||
required: true, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '成员', |
||||
field: 'memberUserIds', |
||||
required: true, |
||||
component: 'ApiTransfer', |
||||
componentProps: { |
||||
api: () => getListSimpleUsers(), |
||||
showSearch: true, |
||||
labelField: 'nickname', |
||||
valueField: 'id', |
||||
}, |
||||
}, |
||||
{ |
||||
label: '状态', |
||||
field: 'status', |
||||
component: 'RadioGroup', |
||||
componentProps: { |
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS), |
||||
}, |
||||
}, |
||||
] |
@ -1,78 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import GroupModal from './GroupModal.vue' |
||||
import { columns, searchFormSchema } from './group.data' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { useModal } from '@/components/Modal' |
||||
import { IconEnum } from '@/enums/appEnum' |
||||
import { BasicTable, TableAction, useTable } from '@/components/Table' |
||||
import { deleteUserGroup, getUserGroupPage } from '@/api/bpm/userGroup' |
||||
|
||||
defineOptions({ name: 'BpmGroup' }) |
||||
|
||||
const { t } = useI18n() |
||||
const { createMessage } = useMessage() |
||||
const [registerModal, { openModal }] = useModal() |
||||
|
||||
const [registerTable, { reload }] = useTable({ |
||||
title: '用户分组列表', |
||||
api: getUserGroupPage, |
||||
columns, |
||||
formConfig: { labelWidth: 120, schemas: searchFormSchema }, |
||||
useSearchForm: true, |
||||
showTableSetting: true, |
||||
actionColumn: { |
||||
width: 140, |
||||
title: t('common.action'), |
||||
dataIndex: 'action', |
||||
fixed: 'right', |
||||
}, |
||||
}) |
||||
|
||||
function handleCreate() { |
||||
openModal(true, { isUpdate: false }) |
||||
} |
||||
|
||||
function handleEdit(record: Recordable) { |
||||
openModal(true, { record, isUpdate: true }) |
||||
} |
||||
|
||||
async function handleDelete(record: Recordable) { |
||||
await deleteUserGroup(record.id) |
||||
createMessage.success(t('common.delSuccessText')) |
||||
reload() |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<BasicTable @register="registerTable"> |
||||
<template #toolbar> |
||||
<a-button v-auth="['bpm:user-group:create']" type="primary" :pre-icon="IconEnum.ADD" @click="handleCreate"> |
||||
{{ t('action.create') }} |
||||
</a-button> |
||||
</template> |
||||
<template #bodyCell="{ column, record }"> |
||||
<template v-if="column.key === 'action'"> |
||||
<TableAction |
||||
:actions="[ |
||||
{ icon: IconEnum.EDIT, label: t('action.edit'), auth: 'bpm:user-group:update', onClick: handleEdit.bind(null, record) }, |
||||
{ |
||||
icon: IconEnum.DELETE, |
||||
danger: true, |
||||
label: t('action.delete'), |
||||
auth: 'bpm:user-group:delete', |
||||
popConfirm: { |
||||
title: t('common.delMessage'), |
||||
placement: 'left', |
||||
confirm: handleDelete.bind(null, record), |
||||
}, |
||||
}, |
||||
]" |
||||
/> |
||||
</template> |
||||
</template> |
||||
</BasicTable> |
||||
<GroupModal @register="registerModal" @success="reload()" /> |
||||
</div> |
||||
</template> |
@ -1,78 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import type { FormSchema } from '@/components/Form' |
||||
import { BasicForm, useForm } from '@/components/Form' |
||||
import { BasicModal, useModalInner } from '@/components/Modal' |
||||
import { importModel } from '@/api/bpm/model' |
||||
|
||||
defineOptions({ name: 'ModelImportForm' }) |
||||
|
||||
const emit = defineEmits(['success', 'register']) |
||||
|
||||
const formSchema: FormSchema[] = [ |
||||
{ |
||||
label: '流程标识', |
||||
field: 'key', |
||||
component: 'Input', |
||||
required: true, |
||||
}, |
||||
{ |
||||
label: '流程名称', |
||||
field: 'name', |
||||
component: 'Input', |
||||
required: true, |
||||
}, |
||||
{ |
||||
label: '流程描述', |
||||
field: 'description', |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '流程文件', |
||||
field: 'bpmnFile', |
||||
component: 'FileUpload', |
||||
componentProps: { |
||||
maxCount: 1, |
||||
fileType: 'file', |
||||
}, |
||||
}, |
||||
] |
||||
|
||||
const { t } = useI18n() |
||||
const { createMessage } = useMessage() |
||||
|
||||
const [registerForm, { resetFields, validate }] = useForm({ |
||||
labelWidth: 120, |
||||
baseColProps: { span: 24 }, |
||||
schemas: formSchema, |
||||
showActionButtonGroup: false, |
||||
actionColOptions: { span: 23 }, |
||||
}) |
||||
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(() => { |
||||
resetFields() |
||||
setModalProps({ confirmLoading: false }) |
||||
}) |
||||
|
||||
async function handleSubmit() { |
||||
try { |
||||
const values = await validate() |
||||
setModalProps({ confirmLoading: true }) |
||||
console.info(values) |
||||
await importModel(values) |
||||
closeModal() |
||||
emit('success') |
||||
createMessage.success(t('common.saveSuccessText')) |
||||
} |
||||
finally { |
||||
setModalProps({ confirmLoading: false }) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<BasicModal v-bind="$attrs" title="导入流程" @register="registerModal" @ok="handleSubmit"> |
||||
<BasicForm @register="registerForm" /> |
||||
</BasicModal> |
||||
</template> |
@ -1,58 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { ref, unref } from 'vue' |
||||
import { formSchema } from './model.data' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { BasicForm, useForm } from '@/components/Form' |
||||
import { BasicModal, useModalInner } from '@/components/Modal' |
||||
import { createModel, getModel, updateModel } from '@/api/bpm/model' |
||||
|
||||
defineOptions({ name: 'ModelForm' }) |
||||
|
||||
const emit = defineEmits(['success', 'register']) |
||||
const { t } = useI18n() |
||||
const { createMessage } = useMessage() |
||||
const isUpdate = ref(true) |
||||
|
||||
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({ |
||||
labelWidth: 120, |
||||
baseColProps: { span: 24 }, |
||||
schemas: formSchema, |
||||
showActionButtonGroup: false, |
||||
actionColOptions: { span: 23 }, |
||||
}) |
||||
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => { |
||||
resetFields() |
||||
setModalProps({ confirmLoading: false }) |
||||
isUpdate.value = !!data?.isUpdate |
||||
if (unref(isUpdate)) { |
||||
const res = await getModel(data.record.id) |
||||
setFieldsValue({ ...res }) |
||||
} |
||||
}) |
||||
|
||||
async function handleSubmit() { |
||||
try { |
||||
const values = await validate() as any |
||||
setModalProps({ confirmLoading: true }) |
||||
if (unref(isUpdate)) |
||||
await updateModel(values) |
||||
else |
||||
await createModel(values) |
||||
|
||||
closeModal() |
||||
emit('success') |
||||
createMessage.success(t('common.saveSuccessText')) |
||||
} |
||||
finally { |
||||
setModalProps({ confirmLoading: false }) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<BasicModal v-bind="$attrs" :title="isUpdate ? t('action.edit') : t('action.create')" @register="registerModal" @ok="handleSubmit"> |
||||
<BasicForm @register="registerForm" /> |
||||
</BasicModal> |
||||
</template> |
@ -1,3 +0,0 @@
|
||||
<template> |
||||
<div>123</div> |
||||
</template> |
@ -1,143 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import ModelModal from './ModelModal.vue' |
||||
import ModelImportModal from './ModelImportModal.vue' |
||||
import { columns, searchFormSchema } from './model.data' |
||||
import { useGo } from '@/hooks/web/usePage' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { useModal } from '@/components/Modal' |
||||
import { IconEnum } from '@/enums/appEnum' |
||||
import { BasicTable, TableAction, useTable } from '@/components/Table' |
||||
import { deleteModel, deployModel, getModelPage } from '@/api/bpm/model' |
||||
|
||||
// import { getAccessToken, getTenantId } from '@/utils/auth' |
||||
|
||||
defineOptions({ name: 'BpmModel' }) |
||||
|
||||
const go = useGo() |
||||
const { t } = useI18n() |
||||
const { createMessage } = useMessage() |
||||
const [registerModal, { openModal }] = useModal() |
||||
const [registerImportModal, { openModal: openImportModal }] = useModal() |
||||
|
||||
// const uploadParams = ref({ |
||||
// 'Authorization': `Bearer ${getAccessToken()}`, |
||||
// 'tenant-id': getTenantId(), |
||||
// }) |
||||
|
||||
const [registerTable, { reload }] = useTable({ |
||||
title: '流程模型图列表', |
||||
api: getModelPage, |
||||
columns, |
||||
formConfig: { labelWidth: 120, schemas: searchFormSchema }, |
||||
useSearchForm: true, |
||||
showTableSetting: true, |
||||
actionColumn: { |
||||
width: 140, |
||||
title: t('common.action'), |
||||
dataIndex: 'action', |
||||
fixed: 'right', |
||||
}, |
||||
}) |
||||
|
||||
function handleCreate() { |
||||
openModal(true, { isUpdate: false }) |
||||
} |
||||
|
||||
function handleEdit(record: Recordable) { |
||||
openModal(true, { record, isUpdate: true }) |
||||
} |
||||
|
||||
/** 设计流程 */ |
||||
function handleDesign(record: Recordable) { |
||||
go({ name: 'BpmModelEditor', query: { modelId: record.id } }) |
||||
} |
||||
|
||||
/** 点击任务分配按钮 */ |
||||
function handleAssignRule(record: Recordable) { |
||||
go({ name: 'BpmTaskAssignRuleList', query: { modelId: record.id } }) |
||||
} |
||||
|
||||
/** 发布流程 */ |
||||
async function handleDeploy(record: Recordable) { |
||||
await deployModel(record.id) |
||||
createMessage.success(t('common.successText')) |
||||
reload() |
||||
} |
||||
|
||||
/** 跳转到指定流程定义列表 */ |
||||
function handleDefinitionList(record: Recordable) { |
||||
go({ name: 'BpmProcessDefinition', query: { modelId: record.key } }) |
||||
} |
||||
|
||||
async function handleDelete(record: Recordable) { |
||||
await deleteModel(record.id) |
||||
createMessage.success(t('common.delSuccessText')) |
||||
reload() |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<BasicTable @register="registerTable"> |
||||
<template #toolbar> |
||||
<a-button v-auth="['bpm:model:create']" type="primary" :pre-icon="IconEnum.ADD" @click="handleCreate"> |
||||
{{ t('action.create') }} |
||||
</a-button> |
||||
<a-button v-auth="['bpm:model:import']" type="primary" :pre-icon="IconEnum.UPLOAD" @click="openImportModal"> |
||||
{{ t('action.import') }} |
||||
</a-button> |
||||
<!-- <BasicUpload |
||||
:max-size="20" |
||||
:max-number="1" |
||||
:empty-hide-preview="true" |
||||
:upload-params="uploadParams" |
||||
:api="importModel" |
||||
class="my-5" |
||||
:accept="['.bpmn', '.xml']" |
||||
@change="reload" |
||||
/> --> |
||||
</template> |
||||
<template #bodyCell="{ column, record }"> |
||||
<template v-if="column.key === 'action'"> |
||||
<TableAction |
||||
:actions="[{ icon: IconEnum.EDIT, label: t('action.edit'), auth: 'bpm:model:update', onClick: handleEdit.bind(null, record) }]" |
||||
:drop-down-actions="[ |
||||
{ icon: IconEnum.EDIT, label: '设计流程', auth: 'bpm:model:update', onClick: handleDesign.bind(null, record) }, |
||||
{ icon: IconEnum.EDIT, label: '分配规则', auth: 'bpm:task-assign-rule:query', onClick: handleAssignRule.bind(null, record) }, |
||||
{ |
||||
icon: IconEnum.EDIT, |
||||
label: '发布流程', |
||||
auth: 'bpm:model:deploy', |
||||
popConfirm: { |
||||
title: t('common.delMessage'), |
||||
placement: 'left', |
||||
confirm: handleDeploy.bind(null, record), |
||||
}, |
||||
}, |
||||
{ |
||||
icon: IconEnum.EDIT, |
||||
label: '流程定义', |
||||
auth: 'bpm:process-definition:query', |
||||
onClick: handleDefinitionList.bind(null, record), |
||||
}, |
||||
{ |
||||
icon: IconEnum.DELETE, |
||||
danger: true, |
||||
label: t('action.delete'), |
||||
auth: 'bpm:model:delete', |
||||
popConfirm: { |
||||
title: t('common.delMessage'), |
||||
placement: 'left', |
||||
confirm: handleDelete.bind(null, record), |
||||
}, |
||||
}, |
||||
]" |
||||
/> |
||||
</template> |
||||
</template> |
||||
</BasicTable> |
||||
<ModelModal @register="registerModal" @success="reload()" /> |
||||
<ModelImportModal @register="registerImportModal" @success="reload()" /> |
||||
</div> |
||||
</template> |
@ -1,225 +0,0 @@
|
||||
import { Button, Switch } from 'ant-design-vue' |
||||
import { h } from 'vue' |
||||
import { getSimpleForms } from '@/api/bpm/form' |
||||
import { updateModelState } from '@/api/bpm/model' |
||||
import type { BasicColumn, FormSchema } from '@/components/Table' |
||||
import { useRender } from '@/components/Table' |
||||
import { useGo } from '@/hooks/web/usePage' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { DICT_TYPE, getDictOptions } from '@/utils/dict' |
||||
|
||||
export const columns: BasicColumn[] = [ |
||||
{ |
||||
title: '编号', |
||||
dataIndex: 'id', |
||||
defaultHidden: true, |
||||
width: 100, |
||||
}, |
||||
{ |
||||
title: '流程标识', |
||||
dataIndex: 'key', |
||||
width: 180, |
||||
}, |
||||
{ |
||||
title: '流程名称', |
||||
dataIndex: 'name', |
||||
width: 180, |
||||
customRender: ({ record }) => { |
||||
return h(Button, { type: 'link', onClick: handleBpmnDetail.bind(null, record) }, () => record.formName) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '流程分类', |
||||
dataIndex: 'category', |
||||
width: 120, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDict(text, DICT_TYPE.BPM_MODEL_CATEGORY) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '表单信息', |
||||
dataIndex: 'formType', |
||||
width: 180, |
||||
customRender: ({ record }) => { |
||||
if (record.formId) |
||||
return h(Button, { type: 'link', onClick: handleFormDetail.bind(null, record) }, () => record.formName) |
||||
else if (record.formCustomCreatePath) |
||||
return h(Button, { type: 'link', onClick: handleFormDetail.bind(null, record) }, () => record.formCustomCreatePath) |
||||
else |
||||
return useRender.renderTag('暂无表单') |
||||
}, |
||||
}, |
||||
{ |
||||
title: '创建时间', |
||||
dataIndex: 'createTime', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDate(text) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '最新部署的流程定义', |
||||
children: [ |
||||
{ |
||||
title: '流程版本', |
||||
dataIndex: 'processDefinition.version', |
||||
width: 160, |
||||
customRender: ({ record }) => { |
||||
if (record.processDefinition) |
||||
return useRender.renderTag(`v${record.processDefinition.version}`) |
||||
else |
||||
return useRender.renderTag('未部署') |
||||
}, |
||||
}, |
||||
{ |
||||
title: '激活状态', |
||||
dataIndex: 'processDefinition.suspensionState', |
||||
width: 100, |
||||
customRender: ({ record }) => { |
||||
if (record.processDefinition) { |
||||
if (!Reflect.has(record, 'suspensionState')) |
||||
record.pendingStatus = false |
||||
|
||||
return h(Switch, { |
||||
checked: record.processDefinition.suspensionState === 1, |
||||
checkedChildren: '激活', |
||||
unCheckedChildren: '挂起', |
||||
onChange(checked: boolean) { |
||||
const newStatus = checked ? 0 : 1 |
||||
const { createMessage } = useMessage() |
||||
updateModelState(record.id, newStatus) |
||||
.then(() => { |
||||
record.status = newStatus |
||||
createMessage.success('已成功修改流程状态') |
||||
}) |
||||
.catch(() => { |
||||
createMessage.error('修改流程状态失败') |
||||
}) |
||||
.finally(() => { |
||||
record.pendingStatus = false |
||||
}) |
||||
}, |
||||
}) |
||||
} |
||||
}, |
||||
}, |
||||
{ |
||||
title: '部署时间', |
||||
dataIndex: 'processDefinition.deploymentTim', |
||||
width: 180, |
||||
customRender: ({ record }) => { |
||||
if (record.processDefinition) |
||||
return useRender.renderDate(record.processDefinition.deploymentTime) |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
] |
||||
|
||||
export const searchFormSchema: FormSchema[] = [ |
||||
{ |
||||
label: '流程标识', |
||||
field: 'key', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '流程名称', |
||||
field: 'name', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '流程分类', |
||||
field: 'category', |
||||
component: 'Select', |
||||
componentProps: { |
||||
options: getDictOptions(DICT_TYPE.BPM_MODEL_CATEGORY) as any, |
||||
}, |
||||
colProps: { span: 8 }, |
||||
}, |
||||
] |
||||
|
||||
export const formSchema: FormSchema[] = [ |
||||
{ |
||||
label: '编号', |
||||
field: 'id', |
||||
show: false, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '流程标识', |
||||
field: 'key', |
||||
required: true, |
||||
component: 'Input', |
||||
dynamicDisabled: ({ values }) => !!values.id, |
||||
}, |
||||
{ |
||||
label: '流程名称', |
||||
field: 'name', |
||||
required: true, |
||||
component: 'Input', |
||||
dynamicDisabled: ({ values }) => !!values.id, |
||||
}, |
||||
{ |
||||
label: '流程分类', |
||||
field: 'category', |
||||
component: 'Select', |
||||
componentProps: { |
||||
options: getDictOptions(DICT_TYPE.BPM_MODEL_CATEGORY) as any, |
||||
}, |
||||
}, |
||||
{ |
||||
label: '流程描述', |
||||
field: 'description', |
||||
component: 'InputTextArea', |
||||
}, |
||||
{ |
||||
label: '表单类型', |
||||
field: 'formType', |
||||
component: 'Select', |
||||
ifShow: ({ values }) => !!values.id, |
||||
componentProps: { |
||||
options: getDictOptions(DICT_TYPE.BPM_MODEL_FORM_TYPE) as any, |
||||
}, |
||||
}, |
||||
{ |
||||
label: '流程表单', |
||||
field: 'formId', |
||||
component: 'ApiSelect', |
||||
ifShow: ({ values }) => !!values.id && values.formType === 10, |
||||
componentProps: { |
||||
api: () => getSimpleForms(), |
||||
labelField: 'name', |
||||
valueField: 'id', |
||||
}, |
||||
}, |
||||
{ |
||||
label: '表单提交路由', |
||||
field: 'formCustomCreatePath', |
||||
component: 'Input', |
||||
helpMessage: '自定义表单的提交路径,使用 Vue 的路由地址,例如说:bpm/oa/leave/create', |
||||
ifShow: ({ values }) => !!values.id && values.formType === 20, |
||||
}, |
||||
{ |
||||
label: '表单查看路由', |
||||
field: 'formCustomViewPath', |
||||
component: 'Input', |
||||
helpMessage: '自定义表单的查看路径,使用 Vue 的路由地址,例如说:bpm/oa/leave/view', |
||||
ifShow: ({ values }) => !!values.id && values.formType === 20, |
||||
}, |
||||
] |
||||
|
||||
function handleBpmnDetail(record: Recordable) { |
||||
console.info('handleBpmnDetail', record) |
||||
} |
||||
|
||||
function handleFormDetail(record: Recordable) { |
||||
if (record.formType === 10) { |
||||
console.info('handleFormDetail') |
||||
} |
||||
else { |
||||
const go = useGo() |
||||
go({ path: record.formCustomCreatePath }) |
||||
} |
||||
} |
@ -1,42 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { onMounted } from 'vue' |
||||
import { formSchema } from './leave.data' |
||||
import { BasicForm, useForm } from '@/components/Form' |
||||
import { PageWrapper } from '@/components/Page' |
||||
import { createLeave } from '@/api/bpm/leave' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
|
||||
defineOptions({ name: 'LeaveCreate' }) |
||||
const { t } = useI18n() |
||||
const { createMessage } = useMessage() |
||||
|
||||
const [registerForm, { resetFields, validate }] = useForm({ |
||||
labelWidth: 140, |
||||
baseColProps: { span: 24 }, |
||||
schemas: formSchema, |
||||
showResetButton: false, |
||||
submitButtonOptions: { text: t('common.saveText') }, |
||||
actionColOptions: { span: 23 }, |
||||
}) |
||||
|
||||
async function handleSubmit() { |
||||
try { |
||||
const values = await validate() as any |
||||
await createLeave(values) |
||||
} |
||||
finally { |
||||
createMessage.success(t('common.saveSuccessText')) |
||||
} |
||||
} |
||||
|
||||
onMounted(() => { |
||||
resetFields() |
||||
}) |
||||
</script> |
||||
|
||||
<template> |
||||
<PageWrapper> |
||||
<BasicForm class="mt-10 h-120 w-200" @register="registerForm" @submit="handleSubmit" /> |
||||
</PageWrapper> |
||||
</template> |
@ -1,40 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { onMounted, ref } from 'vue' |
||||
import { useRoute } from 'vue-router' |
||||
import { descSchema } from './leave.data' |
||||
import { Description, useDescription } from '@/components/Description' |
||||
import { PageWrapper } from '@/components/Page' |
||||
import { getLeave } from '@/api/bpm/leave' |
||||
import { propTypes } from '@/utils/propTypes' |
||||
|
||||
defineOptions({ name: 'InfraJobModal' }) |
||||
|
||||
const props = defineProps({ |
||||
id: propTypes.number.def(undefined), |
||||
}) |
||||
|
||||
const { query } = useRoute() |
||||
const datas = ref() |
||||
|
||||
const [registerDesc] = useDescription({ |
||||
schema: descSchema, |
||||
data: datas, |
||||
}) |
||||
|
||||
async function getInfo() { |
||||
const queryId = query.id as unknown as number // 从 URL 传递过来的 id 编号 |
||||
const res = await getLeave(props.id || queryId) |
||||
datas.value = res |
||||
} |
||||
|
||||
/** 初始化 */ |
||||
onMounted(async () => { |
||||
await getInfo() |
||||
}) |
||||
</script> |
||||
|
||||
<template> |
||||
<PageWrapper> |
||||
<Description :column="1" @register="registerDesc" /> |
||||
</PageWrapper> |
||||
</template> |
@ -1,104 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { columns, searchFormSchema } from './leave.data' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { IconEnum } from '@/enums/appEnum' |
||||
import { BasicTable, TableAction, useTable } from '@/components/Table' |
||||
import { getLeavePage } from '@/api/bpm/leave' |
||||
import { useGo } from '@/hooks/web/usePage' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { cancelProcessInstance } from '@/api/bpm/processInstance' |
||||
|
||||
defineOptions({ name: 'BpmLeave' }) |
||||
|
||||
const { t } = useI18n() |
||||
const go = useGo() |
||||
const { createMessage } = useMessage() |
||||
|
||||
const [registerTable, { reload }] = useTable({ |
||||
title: '请假列表', |
||||
api: getLeavePage, |
||||
columns, |
||||
formConfig: { labelWidth: 120, schemas: searchFormSchema }, |
||||
useSearchForm: true, |
||||
showTableSetting: true, |
||||
actionColumn: { |
||||
width: 160, |
||||
title: t('common.action'), |
||||
dataIndex: 'action', |
||||
fixed: 'right', |
||||
}, |
||||
}) |
||||
|
||||
/** 添加操作 */ |
||||
function handleCreate() { |
||||
go({ name: 'OALeaveCreate' }) |
||||
} |
||||
|
||||
/** 详情操作 */ |
||||
function handleDetail(row) { |
||||
go({ |
||||
name: 'OALeaveDetail', |
||||
query: { |
||||
id: row.id, |
||||
}, |
||||
}) |
||||
} |
||||
|
||||
/** 取消请假操作 */ |
||||
async function cancelLeave(row) { |
||||
// // 二次确认 |
||||
// const { value } = await ElMessageBox.prompt('请输入取消原因', '取消流程', { |
||||
// confirmButtonText: t('common.ok'), |
||||
// cancelButtonText: t('common.cancel'), |
||||
// inputPattern: /^[\s\S]*.*\S[\s\S]*$/, // 判断非空,且非空格 |
||||
// inputErrorMessage: '取消原因不能为空', |
||||
// }) |
||||
const value = '' |
||||
// 发起取消 |
||||
await cancelProcessInstance(row.id, value) |
||||
createMessage.success(t('common.delSuccessText')) |
||||
reload() |
||||
} |
||||
|
||||
/** 审批进度 */ |
||||
function handleProcessDetail(row) { |
||||
go({ |
||||
name: 'BpmProcessInstanceDetail', |
||||
query: { |
||||
id: row.processInstanceId, |
||||
}, |
||||
}) |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<BasicTable @register="registerTable"> |
||||
<template #toolbar> |
||||
<a-button type="primary" :pre-icon="IconEnum.ADD" @click="handleCreate"> |
||||
发起请假 |
||||
</a-button> |
||||
</template> |
||||
<template #bodyCell="{ column, record }"> |
||||
<template v-if="column.key === 'action'"> |
||||
<TableAction |
||||
:actions="[ |
||||
{ icon: IconEnum.SEARCH, label: t('action.detail'), auth: 'bpm:oa-leave:query', onClick: handleDetail.bind(null, record) }, |
||||
{ icon: IconEnum.LOG, label: '进度', auth: 'bpm:oa-leave:query', onClick: handleProcessDetail.bind(null, record) }, |
||||
{ |
||||
icon: IconEnum.DELETE, |
||||
danger: true, |
||||
label: t('action.cancel'), |
||||
auth: 'bpm:oa-leave:create', |
||||
ifShow: () => { |
||||
return record.result === 1 |
||||
}, |
||||
onClick: cancelLeave.bind(null, record), |
||||
}, |
||||
]" |
||||
/> |
||||
</template> |
||||
</template> |
||||
</BasicTable> |
||||
</div> |
||||
</template> |
@ -1,158 +0,0 @@
|
||||
import type { DescItem } from '@/components/Description' |
||||
import type { BasicColumn, FormSchema } from '@/components/Table' |
||||
import { useRender } from '@/components/Table' |
||||
import { DICT_TYPE, getDictOptions } from '@/utils/dict' |
||||
|
||||
export const columns: BasicColumn[] = [ |
||||
{ |
||||
title: '申请编号', |
||||
dataIndex: 'id', |
||||
width: 100, |
||||
}, |
||||
{ |
||||
title: '状态', |
||||
dataIndex: 'result', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDict(text, DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '开始时间', |
||||
dataIndex: 'startTime', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDate(text) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '结束时间', |
||||
dataIndex: 'endTime', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDate(text) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '请假类型', |
||||
dataIndex: 'type', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDict(text, DICT_TYPE.BPM_OA_LEAVE_TYPE) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '原因', |
||||
dataIndex: 'reason', |
||||
width: 180, |
||||
}, |
||||
{ |
||||
title: '申请时间', |
||||
dataIndex: 'createTime', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDate(text) |
||||
}, |
||||
}, |
||||
] |
||||
|
||||
export const searchFormSchema: FormSchema[] = [ |
||||
{ |
||||
label: '请假类型', |
||||
field: 'type', |
||||
component: 'Select', |
||||
componentProps: { |
||||
options: getDictOptions(DICT_TYPE.BPM_OA_LEAVE_TYPE) as any, |
||||
}, |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '申请时间', |
||||
field: 'createTime', |
||||
component: 'RangePicker', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '结果', |
||||
field: 'result', |
||||
component: 'Select', |
||||
componentProps: { |
||||
options: getDictOptions(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT), |
||||
}, |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '原因', |
||||
field: 'reason', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
] |
||||
|
||||
export const formSchema: FormSchema[] = [ |
||||
{ |
||||
label: '请假类型', |
||||
field: 'type', |
||||
required: true, |
||||
component: 'Select', |
||||
componentProps: { |
||||
options: getDictOptions(DICT_TYPE.BPM_OA_LEAVE_TYPE) as any, |
||||
}, |
||||
}, |
||||
{ |
||||
label: '开始时间', |
||||
field: 'startTime', |
||||
required: true, |
||||
component: 'DatePicker', |
||||
componentProps: { |
||||
showTime: true, |
||||
format: 'YYYY-MM-DD HH:mm:ss', |
||||
valueFormat: 'x', |
||||
}, |
||||
}, |
||||
{ |
||||
label: '结束时间', |
||||
field: 'endTime', |
||||
required: true, |
||||
component: 'DatePicker', |
||||
componentProps: { |
||||
showTime: true, |
||||
format: 'YYYY-MM-DD HH:mm:ss', |
||||
valueFormat: 'x', |
||||
}, |
||||
}, |
||||
{ |
||||
label: '原因', |
||||
field: 'reason', |
||||
required: true, |
||||
component: 'Input', |
||||
}, |
||||
] |
||||
|
||||
export const descSchema: DescItem[] = [ |
||||
{ |
||||
label: '请假类型', |
||||
field: 'merchantOrderId', |
||||
render: (curVal) => { |
||||
return useRender.renderTag(curVal) |
||||
}, |
||||
}, |
||||
{ |
||||
label: '开始时间', |
||||
field: 'startTime', |
||||
render: (curVal) => { |
||||
return useRender.renderDate(curVal, 'YYYY-MM-DD') |
||||
}, |
||||
}, |
||||
{ |
||||
label: '结束时间', |
||||
field: 'endTime', |
||||
render: (curVal) => { |
||||
return useRender.renderDate(curVal, 'YYYY-MM-DD') |
||||
}, |
||||
}, |
||||
{ |
||||
label: '原因', |
||||
field: 'reason', |
||||
}, |
||||
] |
@ -1,32 +0,0 @@
|
||||
import type { BasicColumn } from '@/components/Table' |
||||
import { DICT_TYPE } from '@/utils/dict' |
||||
import { useRender } from '@/components/Table' |
||||
|
||||
export const columns: BasicColumn[] = [ |
||||
{ |
||||
title: '流程名称', |
||||
dataIndex: 'name', |
||||
width: 120, |
||||
}, |
||||
{ |
||||
title: '流程分类', |
||||
dataIndex: 'category', |
||||
width: 120, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDict(text, DICT_TYPE.BPM_MODEL_CATEGORY) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '流程版本', |
||||
dataIndex: 'name', |
||||
width: 120, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderTag(text) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '流程描述', |
||||
dataIndex: 'description', |
||||
width: 200, |
||||
}, |
||||
] |
@ -1,115 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { Card, Steps } from 'ant-design-vue' |
||||
import { ref } from 'vue' |
||||
import { columns } from './create.data' |
||||
import { useGo } from '@/hooks/web/usePage' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { PageWrapper } from '@/components/Page' |
||||
import { IconEnum } from '@/enums/appEnum' |
||||
import Icon from '@/components/Icon' |
||||
import { BasicTable, TableAction, useTable } from '@/components/Table' |
||||
import { getProcessDefinitionBpmnXML, getProcessDefinitionList } from '@/api/bpm/definition' |
||||
import { createProcessInstance } from '@/api/bpm/processInstance' |
||||
|
||||
defineOptions({ name: 'BpmProcessInstanceCreate' }) |
||||
|
||||
const go = useGo() |
||||
const { t } = useI18n() |
||||
const { createMessage } = useMessage() |
||||
const current = ref(0) |
||||
|
||||
const bpmnXML = ref(null) // BPMN 数据 |
||||
const selectProcessInstance = ref() // 选择的流程实例 |
||||
|
||||
const [registerTable] = useTable({ |
||||
api: getProcessDefinitionList, |
||||
columns, |
||||
actionColumn: { |
||||
width: 140, |
||||
title: t('common.action'), |
||||
dataIndex: 'action', |
||||
fixed: 'right', |
||||
}, |
||||
}) |
||||
|
||||
/** 处理选择流程的按钮操作 */ |
||||
async function handleSelect(row) { |
||||
// 设置选择的流程 |
||||
selectProcessInstance.value = row |
||||
|
||||
// 情况一:流程表单 |
||||
if (row.formType === 10) { |
||||
// 设置表单 |
||||
// setConfAndFields2(detailForm, row.formConf, row.formFields) |
||||
// 加载流程图 |
||||
bpmnXML.value = await getProcessDefinitionBpmnXML(row.id) |
||||
// 情况二:业务表单 |
||||
} |
||||
else if (row.formCustomCreatePath) { |
||||
await go({ |
||||
path: row.formCustomCreatePath, |
||||
}) |
||||
// 这里暂时无需加载流程图,因为跳出到另外个 Tab; |
||||
} |
||||
} |
||||
|
||||
/** 提交按钮 */ |
||||
async function submitForm(formData) { |
||||
// if (!fApi.value || !selectProcessInstance.value) |
||||
// return |
||||
|
||||
// // 提交请求 |
||||
// fApi.value.btn.loading(true) |
||||
try { |
||||
await createProcessInstance({ |
||||
processDefinitionId: selectProcessInstance.value.id, |
||||
variables: formData, |
||||
}) |
||||
// 提示 |
||||
createMessage.success('发起流程成功') |
||||
go() |
||||
} |
||||
finally { |
||||
// fApi.value.btn.loading(false) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<PageWrapper> |
||||
<div class="mx-auto my-0 mt-2.5 w-200"> |
||||
<Steps :current="current"> |
||||
<Steps.Step title="选择流程" /> |
||||
<Steps.Step title="流程提交" /> |
||||
</Steps> |
||||
</div> |
||||
<div class="m-5"> |
||||
<BasicTable v-if="!selectProcessInstance" @register="registerTable"> |
||||
<template #bodyCell="{ column, record }"> |
||||
<template v-if="column.key === 'action'"> |
||||
<TableAction |
||||
:actions="[ |
||||
{ icon: IconEnum.SEND, label: '选择', onClick: handleSelect.bind(null, record) }, |
||||
]" |
||||
/> |
||||
</template> |
||||
</template> |
||||
</BasicTable> |
||||
<div v-if="selectProcessInstance"> |
||||
<Card :title="`申请信息——【${selectProcessInstance.name}】`"> |
||||
<template #extra> |
||||
<a-button type="primary" @click="selectProcessInstance = undefined"> |
||||
<Icon icon="ep:delete" /> 选择其它流程 |
||||
</a-button> |
||||
</template> |
||||
<p>表单</p> |
||||
<a-button type="primary" @click="submitForm"> |
||||
提交 |
||||
</a-button> |
||||
<p>流程图</p> |
||||
</Card> |
||||
</div> |
||||
</div> |
||||
</PageWrapper> |
||||
</template> |
@ -1,3 +0,0 @@
|
||||
<template> |
||||
<div>123</div> |
||||
</template> |
@ -1,84 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { columns, searchFormSchema } from './processInstance.data' |
||||
import { useGo } from '@/hooks/web/usePage' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { BasicTable, TableAction, useTable } from '@/components/Table' |
||||
import { IconEnum } from '@/enums/appEnum' |
||||
import { cancelProcessInstance, getMyProcessInstancePage } from '@/api/bpm/processInstance' |
||||
|
||||
defineOptions({ name: 'InfraApiErrorLog' }) |
||||
|
||||
const go = useGo() |
||||
const { t } = useI18n() |
||||
const { createMessage } = useMessage() |
||||
const [registerTable, { reload }] = useTable({ |
||||
title: '我的流程列表', |
||||
api: getMyProcessInstancePage, |
||||
columns, |
||||
formConfig: { labelWidth: 120, schemas: searchFormSchema }, |
||||
useSearchForm: true, |
||||
showTableSetting: true, |
||||
actionColumn: { |
||||
width: 140, |
||||
title: t('common.action'), |
||||
dataIndex: 'action', |
||||
fixed: 'right', |
||||
}, |
||||
}) |
||||
|
||||
/** 发起流程操作 */ |
||||
function handleCreate() { |
||||
go({ name: 'BpmProcessInstanceCreate' }) |
||||
} |
||||
|
||||
/** 查看详情 */ |
||||
function handleDetail(record: Recordable) { |
||||
go({ name: 'BpmProcessInstanceDetail', query: { id: record.id } }) |
||||
} |
||||
|
||||
/** 取消按钮操作 */ |
||||
async function handleCancel(record: Recordable) { |
||||
await cancelProcessInstance(record.id, 'TODO') |
||||
createMessage.success('取消成功') |
||||
reload() |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<BasicTable @register="registerTable"> |
||||
<template #toolbar> |
||||
<a-button v-auth="['bpm:process-instance:query']" :pre-icon="IconEnum.ADD" @click="handleCreate"> |
||||
发起流程 |
||||
</a-button> |
||||
</template> |
||||
<template #bodyCell="{ column, record }"> |
||||
<template v-if="column.key === 'action'"> |
||||
<TableAction |
||||
:actions="[ |
||||
{ |
||||
icon: IconEnum.VIEW, |
||||
label: t('action.detail'), |
||||
auth: 'bpm:process-instance:query', |
||||
onClick: handleDetail.bind(null, record), |
||||
}, |
||||
{ |
||||
icon: IconEnum.DELETE, |
||||
danger: true, |
||||
label: t('action.cancel'), |
||||
ifShow: record.result === 1, |
||||
auth: 'bpm:process-instance:cancel', |
||||
popConfirm: { |
||||
title: t('action.cancel'), |
||||
placement: 'left', |
||||
confirm: handleCancel.bind(null, record), |
||||
}, |
||||
}, |
||||
]" |
||||
/> |
||||
</template> |
||||
</template> |
||||
</BasicTable> |
||||
</div> |
||||
</template> |
@ -1,118 +0,0 @@
|
||||
import type { BasicColumn, FormSchema } from '@/components/Table' |
||||
import { useRender } from '@/components/Table' |
||||
import { DICT_TYPE, getDictOptions } from '@/utils/dict' |
||||
|
||||
export const columns: BasicColumn[] = [ |
||||
{ |
||||
title: '流程编号', |
||||
dataIndex: 'id', |
||||
width: 260, |
||||
}, |
||||
{ |
||||
title: '流程名称', |
||||
dataIndex: 'name', |
||||
width: 100, |
||||
}, |
||||
{ |
||||
title: '流程分类', |
||||
dataIndex: 'category', |
||||
width: 120, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDict(text, DICT_TYPE.BPM_MODEL_CATEGORY) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '当前审批任务', |
||||
dataIndex: 'tasks', |
||||
width: 120, |
||||
customRender: ({ record }) => { |
||||
if (record.tasks && record.tasks.length > 0) { |
||||
const texts: any[] = [] |
||||
record.tasks.forEach((val) => { |
||||
texts.push(val.name) |
||||
}) |
||||
return useRender.renderTags(texts) |
||||
} |
||||
}, |
||||
}, |
||||
{ |
||||
title: '状态', |
||||
dataIndex: 'status', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDict(text, DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '结果', |
||||
dataIndex: 'result', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDict(text, DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '提交时间', |
||||
dataIndex: 'createTime', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDate(text) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '结束时间', |
||||
dataIndex: 'endTime', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDate(text) |
||||
}, |
||||
}, |
||||
] |
||||
|
||||
export const searchFormSchema: FormSchema[] = [ |
||||
{ |
||||
label: '流程名称', |
||||
field: 'name', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '所属流程', |
||||
field: 'processDefinitionId', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '流程分类', |
||||
field: 'category', |
||||
component: 'Select', |
||||
componentProps: { |
||||
options: getDictOptions(DICT_TYPE.BPM_MODEL_CATEGORY) as any, |
||||
}, |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '状态', |
||||
field: 'status', |
||||
component: 'Select', |
||||
componentProps: { |
||||
options: getDictOptions(DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS), |
||||
}, |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '结果', |
||||
field: 'result', |
||||
component: 'Select', |
||||
componentProps: { |
||||
options: getDictOptions(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT), |
||||
}, |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '提交时间', |
||||
field: 'createTime', |
||||
component: 'RangePicker', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
] |
@ -1,79 +0,0 @@
|
||||
import type { BasicColumn, FormSchema } from '@/components/Table' |
||||
import { useRender } from '@/components/Table' |
||||
import { getDate } from '@/utils/dateUtil' |
||||
import { DICT_TYPE } from '@/utils/dict' |
||||
|
||||
export const columns: BasicColumn[] = [ |
||||
{ |
||||
title: '任务编号', |
||||
dataIndex: 'id', |
||||
width: 100, |
||||
}, |
||||
{ |
||||
title: '任务名称', |
||||
dataIndex: 'name', |
||||
width: 180, |
||||
}, |
||||
{ |
||||
title: '所属流程', |
||||
dataIndex: 'processInstance.name', |
||||
width: 180, |
||||
}, |
||||
{ |
||||
title: '流程发起人', |
||||
dataIndex: 'processInstance.startUserNickname', |
||||
width: 180, |
||||
}, |
||||
{ |
||||
title: '结果', |
||||
dataIndex: 'result', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDict(text, DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '审批意见', |
||||
dataIndex: 'reason', |
||||
width: 180, |
||||
}, |
||||
{ |
||||
title: '创建时间', |
||||
dataIndex: 'createTime', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDate(text) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '审批时间', |
||||
dataIndex: 'endTime', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDate(text) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '耗时', |
||||
dataIndex: 'durationInMillis', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return getDate(text) |
||||
}, |
||||
}, |
||||
] |
||||
|
||||
export const searchFormSchema: FormSchema[] = [ |
||||
{ |
||||
label: '流程名', |
||||
field: 'name', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '创建时间', |
||||
field: 'createTime', |
||||
component: 'RangePicker', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
] |
@ -1,53 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { columns, searchFormSchema } from './done.data' |
||||
import { useGo } from '@/hooks/web/usePage' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { BasicTable, TableAction, useTable } from '@/components/Table' |
||||
import { IconEnum } from '@/enums/appEnum' |
||||
import { getDoneTaskPage } from '@/api/bpm/task' |
||||
|
||||
defineOptions({ name: 'BpmDoneTask' }) |
||||
|
||||
const go = useGo() |
||||
const { t } = useI18n() |
||||
|
||||
const [registerTable] = useTable({ |
||||
title: '审批列表', |
||||
api: getDoneTaskPage, |
||||
columns, |
||||
formConfig: { labelWidth: 120, schemas: searchFormSchema }, |
||||
useSearchForm: true, |
||||
showTableSetting: true, |
||||
actionColumn: { |
||||
width: 140, |
||||
title: t('common.action'), |
||||
dataIndex: 'action', |
||||
fixed: 'right', |
||||
}, |
||||
}) |
||||
|
||||
function openDetail(record: Recordable) { |
||||
console.info(record) |
||||
} |
||||
|
||||
function handleAudit(record: Recordable) { |
||||
go({ name: 'BpmProcessInstanceDetail', query: { id: record.id } }) |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<BasicTable @register="registerTable"> |
||||
<template #bodyCell="{ column, record }"> |
||||
<template v-if="column.key === 'action'"> |
||||
<TableAction |
||||
:actions="[ |
||||
{ icon: IconEnum.VIEW, label: t('action.detail'), onClick: openDetail.bind(null, record) }, |
||||
{ icon: IconEnum.VIEW, label: '流程', onClick: handleAudit.bind(null, record) }, |
||||
]" |
||||
/> |
||||
</template> |
||||
</template> |
||||
</BasicTable> |
||||
</div> |
||||
</template> |
@ -1,44 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { columns, searchFormSchema } from './todo.data' |
||||
import { useGo } from '@/hooks/web/usePage' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { BasicTable, TableAction, useTable } from '@/components/Table' |
||||
import { IconEnum } from '@/enums/appEnum' |
||||
import { getTodoTaskPage } from '@/api/bpm/task' |
||||
|
||||
defineOptions({ name: 'BpmTodoTask' }) |
||||
|
||||
const go = useGo() |
||||
const { t } = useI18n() |
||||
|
||||
const [registerTable] = useTable({ |
||||
title: '审批列表', |
||||
api: getTodoTaskPage, |
||||
columns, |
||||
formConfig: { labelWidth: 120, schemas: searchFormSchema }, |
||||
useSearchForm: true, |
||||
showTableSetting: true, |
||||
actionColumn: { |
||||
width: 140, |
||||
title: t('common.action'), |
||||
dataIndex: 'action', |
||||
fixed: 'right', |
||||
}, |
||||
}) |
||||
|
||||
function handleAudit(record: Recordable) { |
||||
go({ name: 'BpmProcessInstanceDetail', query: { id: record.id } }) |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<BasicTable @register="registerTable"> |
||||
<template #bodyCell="{ column, record }"> |
||||
<template v-if="column.key === 'action'"> |
||||
<TableAction :actions="[{ icon: IconEnum.VIEW, label: '审批进度', onClick: handleAudit.bind(null, record) }]" /> |
||||
</template> |
||||
</template> |
||||
</BasicTable> |
||||
</div> |
||||
</template> |
@ -1,59 +0,0 @@
|
||||
import type { BasicColumn, FormSchema } from '@/components/Table' |
||||
import { useRender } from '@/components/Table' |
||||
|
||||
export const columns: BasicColumn[] = [ |
||||
{ |
||||
title: '任务编号', |
||||
dataIndex: 'id', |
||||
width: 100, |
||||
}, |
||||
{ |
||||
title: '任务名称', |
||||
dataIndex: 'name', |
||||
width: 180, |
||||
}, |
||||
{ |
||||
title: '所属流程', |
||||
dataIndex: 'processInstance.name', |
||||
width: 180, |
||||
}, |
||||
{ |
||||
title: '流程发起人', |
||||
dataIndex: 'processInstance.startUserNickname', |
||||
width: 180, |
||||
}, |
||||
{ |
||||
title: '创建时间', |
||||
dataIndex: 'createTime', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDate(text) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '状态', |
||||
dataIndex: 'suspensionState', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
if (text === 1) |
||||
return useRender.renderTag('激活', 'success') |
||||
else if (text === 2) |
||||
return useRender.renderTag('挂起', 'warning') |
||||
}, |
||||
}, |
||||
] |
||||
|
||||
export const searchFormSchema: FormSchema[] = [ |
||||
{ |
||||
label: '流程名', |
||||
field: 'name', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '创建时间', |
||||
field: 'createTime', |
||||
component: 'RangePicker', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
] |
@ -1,3 +0,0 @@
|
||||
<template> |
||||
<div>开发中 taskAssignRule</div> |
||||
</template> |
@ -1,25 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { ref } from 'vue' |
||||
import { infoSchema } from './apiAccessLog.data' |
||||
import { BasicModal, useModalInner } from '@/components/Modal' |
||||
import { Description, useDescription } from '@/components/Description/index' |
||||
|
||||
defineOptions({ name: 'AcessLogModal' }) |
||||
|
||||
const logData = ref() |
||||
const [registerModalInner, { closeModal }] = useModalInner((record: Recordable) => { |
||||
logData.value = record |
||||
}) |
||||
|
||||
const [registerDescription] = useDescription({ |
||||
column: 1, |
||||
schema: infoSchema, |
||||
data: logData, |
||||
}) |
||||
</script> |
||||
|
||||
<template> |
||||
<BasicModal v-bind="$attrs" title="访问日志详情" width="800px" @register="registerModalInner" @ok="closeModal"> |
||||
<Description @register="registerDescription" /> |
||||
</BasicModal> |
||||
</template> |
@ -1,222 +0,0 @@
|
||||
import { h } from 'vue' |
||||
import type { BasicColumn, FormSchema } from '@/components/Table' |
||||
import { useRender } from '@/components/Table' |
||||
import { DICT_TYPE, getDictOptions } from '@/utils/dict' |
||||
import type { DescItem } from '@/components/Description/index' |
||||
|
||||
export const columns: BasicColumn[] = [ |
||||
{ |
||||
title: '日志编号', |
||||
dataIndex: 'id', |
||||
width: 100, |
||||
}, |
||||
{ |
||||
title: '用户编号', |
||||
dataIndex: 'userId', |
||||
width: 100, |
||||
}, |
||||
{ |
||||
title: '用户类型', |
||||
dataIndex: 'userType', |
||||
width: 120, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDict(text, DICT_TYPE.USER_TYPE) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '应用名', |
||||
dataIndex: 'applicationName', |
||||
width: 120, |
||||
}, |
||||
{ |
||||
title: '请求方法名', |
||||
dataIndex: 'requestMethod', |
||||
width: 120, |
||||
}, |
||||
{ |
||||
title: '请求地址', |
||||
dataIndex: 'requestUrl', |
||||
width: 250, |
||||
}, |
||||
{ |
||||
title: '请求时间', |
||||
dataIndex: 'beginTime', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDate(text) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '执行时长', |
||||
dataIndex: 'duration', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderText(text.toString(), 'ms') |
||||
}, |
||||
}, |
||||
{ |
||||
title: '操作结果', |
||||
dataIndex: 'status', |
||||
width: 180, |
||||
ellipsis: true, |
||||
customRender: ({ record }) => { |
||||
const success = record.resultCode === 0 |
||||
return useRender.renderTag(success ? '成功' : '失败', success ? '#87d068' : '#f50') |
||||
}, |
||||
}, |
||||
] |
||||
|
||||
export const searchFormSchema: FormSchema[] = [ |
||||
{ |
||||
label: '用户编号', |
||||
field: 'userId', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '用户类型', |
||||
field: 'userType', |
||||
component: 'Select', |
||||
componentProps: { |
||||
options: getDictOptions(DICT_TYPE.USER_TYPE) as any, |
||||
}, |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '应用名', |
||||
field: 'applicationName', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '请求地址', |
||||
field: 'requestUrl', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '请求时间', |
||||
field: 'beginTime', |
||||
component: 'RangePicker', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '执行时长', |
||||
field: 'duration', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '结果码', |
||||
field: 'resultCode', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
] |
||||
|
||||
const httpMethods = [ |
||||
{ value: 'GET', color: '#108ee9' }, |
||||
{ value: 'POST', color: '#2db7f5' }, |
||||
{ value: 'PUT', color: 'warning' }, |
||||
{ value: 'DELETE', color: '#f50' }, |
||||
] |
||||
|
||||
export const infoSchema: DescItem[] = [ |
||||
{ |
||||
label: '日志id', |
||||
field: 'id', |
||||
}, |
||||
{ |
||||
label: '链路id', |
||||
field: 'traceId', |
||||
show: data => data && data.traceId && data.traceId !== '', |
||||
}, |
||||
{ |
||||
label: '应用名称', |
||||
field: 'applicationName', |
||||
labelMinWidth: 100, |
||||
}, |
||||
{ |
||||
field: 'userId', |
||||
label: '用户id', |
||||
render(value, data) { |
||||
const tag = useRender.renderDict(data.userType, DICT_TYPE.USER_TYPE) |
||||
const uidTag = useRender.renderTag(`uid: ${value}`) |
||||
return h('span', {}, [tag, uidTag]) |
||||
}, |
||||
}, |
||||
{ |
||||
field: 'resultCode', |
||||
label: '请求结果', |
||||
render(value) { |
||||
return useRender.renderTag(value === 0 ? '成功' : '失败', value === 0 ? '#87d068' : '#f50') |
||||
}, |
||||
}, |
||||
{ |
||||
field: 'resultMsg', |
||||
label: '响应信息', |
||||
show(data) { |
||||
return data && data.resultMsg && data.resultMsg !== '' |
||||
}, |
||||
render(value) { |
||||
return h('span', { style: { color: 'red', fontWeight: 'bold' } }, value) |
||||
}, |
||||
}, |
||||
{ |
||||
field: 'userIp', |
||||
label: '请求ip', |
||||
}, |
||||
{ |
||||
field: 'userAgent', |
||||
label: 'userAgent', |
||||
}, |
||||
{ |
||||
field: 'beginTime', |
||||
label: '请求时间', |
||||
render(value) { |
||||
return useRender.renderDate(value) |
||||
}, |
||||
}, |
||||
{ |
||||
field: 'requestUrl', |
||||
label: '请求路径', |
||||
render(_, data) { |
||||
if (!data) |
||||
return '' |
||||
|
||||
const { requestMethod, requestUrl } = data |
||||
const current = httpMethods.find(item => item.value === requestMethod.toUpperCase()) |
||||
const methodTag = current ? useRender.renderTag(requestMethod, current.color) : requestMethod |
||||
return h('span', {}, [methodTag, requestUrl]) |
||||
}, |
||||
}, |
||||
{ |
||||
field: 'requestParams', |
||||
label: '请求参数', |
||||
render(value) { |
||||
return useRender.renderJsonPreview(value) |
||||
}, |
||||
}, |
||||
{ |
||||
field: 'beginTime', |
||||
label: '请求开始时间', |
||||
render(value) { |
||||
return useRender.renderDate(value) |
||||
}, |
||||
}, |
||||
{ |
||||
field: 'endTime', |
||||
label: '请求结束时间', |
||||
render(value) { |
||||
return useRender.renderDate(value) |
||||
}, |
||||
}, |
||||
{ |
||||
field: 'duration', |
||||
label: '请求耗时', |
||||
render(value) { |
||||
// 为0的话需要转为string 否则不会显示
|
||||
return useRender.renderText(String(value), 'ms') |
||||
}, |
||||
}, |
||||
] |
@ -1,74 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { columns, searchFormSchema } from './apiAccessLog.data' |
||||
import AccessLogModal from './AccessLogModal.vue' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { BasicTable, TableAction, useTable } from '@/components/Table' |
||||
import { IconEnum } from '@/enums/appEnum' |
||||
import type { ApiAccessLogExportReqVO } from '@/api/infra/apiAccessLog' |
||||
import { exportApiAccessLog, getApiAccessLogPage } from '@/api/infra/apiAccessLog' |
||||
import { useModal } from '@/components/Modal' |
||||
|
||||
defineOptions({ name: 'InfraApiErrorLog' }) |
||||
|
||||
const { t } = useI18n() |
||||
const { createConfirm, createMessage } = useMessage() |
||||
const [registerTable, { getForm }] = useTable({ |
||||
title: '访问日志列表', |
||||
api: getApiAccessLogPage, |
||||
columns, |
||||
formConfig: { labelWidth: 120, schemas: searchFormSchema }, |
||||
useSearchForm: true, |
||||
showTableSetting: true, |
||||
showIndexColumn: false, |
||||
actionColumn: { |
||||
width: 120, |
||||
title: t('common.action'), |
||||
dataIndex: 'action', |
||||
fixed: 'right', |
||||
}, |
||||
}) |
||||
|
||||
const [registerModal, { openModal }] = useModal() |
||||
function handleShowInfo(record: Recordable) { |
||||
openModal(true, record) |
||||
} |
||||
|
||||
async function handleExport() { |
||||
createConfirm({ |
||||
title: t('common.exportTitle'), |
||||
iconType: 'warning', |
||||
content: t('common.exportMessage'), |
||||
async onOk() { |
||||
await exportApiAccessLog(getForm().getFieldsValue() as ApiAccessLogExportReqVO) |
||||
createMessage.success(t('common.exportSuccessText')) |
||||
}, |
||||
}) |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<BasicTable @register="registerTable"> |
||||
<template #toolbar> |
||||
<a-button v-auth="['infra:api-access-log:export']" :pre-icon="IconEnum.EXPORT" @click="handleExport"> |
||||
{{ t('action.export') }} |
||||
</a-button> |
||||
</template> |
||||
<template #bodyCell="{ column, record }"> |
||||
<template v-if="column.key === 'action'"> |
||||
<TableAction |
||||
:actions="[ |
||||
{ |
||||
icon: IconEnum.VIEW, |
||||
label: t('action.detail'), |
||||
onClick: handleShowInfo.bind(null, record), |
||||
}, |
||||
]" |
||||
/> |
||||
</template> |
||||
</template> |
||||
</BasicTable> |
||||
<AccessLogModal @register="registerModal" /> |
||||
</div> |
||||
</template> |
@ -1,25 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { ref } from 'vue' |
||||
import { infoSchema } from './apiErrorLog.data' |
||||
import { BasicModal, useModalInner } from '@/components/Modal' |
||||
import { Description, useDescription } from '@/components/Description/index' |
||||
|
||||
defineOptions({ name: 'ErrorLogModal' }) |
||||
|
||||
const logData = ref() |
||||
const [registerModalInner, { closeModal }] = useModalInner((record: Recordable) => { |
||||
logData.value = record |
||||
}) |
||||
|
||||
const [registerDescription] = useDescription({ |
||||
column: 1, |
||||
schema: infoSchema, |
||||
data: logData, |
||||
}) |
||||
</script> |
||||
|
||||
<template> |
||||
<BasicModal v-bind="$attrs" title="错误日志详情" width="800px" @register="registerModalInner" @ok="closeModal"> |
||||
<Description @register="registerDescription" /> |
||||
</BasicModal> |
||||
</template> |
@ -1,248 +0,0 @@
|
||||
import { Textarea } from 'ant-design-vue' |
||||
import { h } from 'vue' |
||||
import type { BasicColumn, FormSchema } from '@/components/Table' |
||||
import { useRender } from '@/components/Table' |
||||
import { DICT_TYPE, getDictOptions } from '@/utils/dict' |
||||
import type { DescItem } from '@/components/Description/index' |
||||
|
||||
export const columns: BasicColumn[] = [ |
||||
{ |
||||
title: '日志编号', |
||||
dataIndex: 'id', |
||||
width: 100, |
||||
}, |
||||
{ |
||||
title: '用户编号', |
||||
dataIndex: 'userId', |
||||
width: 100, |
||||
}, |
||||
{ |
||||
title: '用户类型', |
||||
dataIndex: 'userType', |
||||
width: 120, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDict(text, DICT_TYPE.USER_TYPE) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '应用名', |
||||
dataIndex: 'applicationName', |
||||
width: 120, |
||||
}, |
||||
{ |
||||
title: '请求方法名', |
||||
dataIndex: 'requestMethod', |
||||
width: 120, |
||||
}, |
||||
{ |
||||
title: '请求地址', |
||||
dataIndex: 'requestUrl', |
||||
width: 250, |
||||
}, |
||||
{ |
||||
title: '异常发生时间', |
||||
dataIndex: 'exceptionTime', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDate(text) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '异常名', |
||||
dataIndex: 'exceptionName', |
||||
width: 250, |
||||
}, |
||||
{ |
||||
title: '处理状态', |
||||
dataIndex: 'processStatus', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDict(text, DICT_TYPE.INFRA_API_ERROR_LOG_PROCESS_STATUS) |
||||
}, |
||||
}, |
||||
] |
||||
|
||||
export const searchFormSchema: FormSchema[] = [ |
||||
{ |
||||
label: '用户编号', |
||||
field: 'userId', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '用户名称', |
||||
field: 'username', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '用户类型', |
||||
field: 'userType', |
||||
component: 'Select', |
||||
componentProps: { |
||||
options: getDictOptions(DICT_TYPE.USER_TYPE) as any, |
||||
}, |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '应用名', |
||||
field: 'applicationName', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '请求地址', |
||||
field: 'requestUrl', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '异常时间', |
||||
field: 'exceptionTime', |
||||
component: 'RangePicker', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '处理状态', |
||||
field: 'processStatus', |
||||
component: 'Select', |
||||
componentProps: { |
||||
options: getDictOptions(DICT_TYPE.INFRA_API_ERROR_LOG_PROCESS_STATUS), |
||||
}, |
||||
colProps: { span: 8 }, |
||||
}, |
||||
] |
||||
|
||||
function renderText(value: string, color: string, bold = true) { |
||||
return h('span', { style: { color, fontWeight: bold ? 'bold' : 'normal' } }, value) |
||||
} |
||||
|
||||
const httpMethods = [ |
||||
{ value: 'GET', color: '#108ee9' }, |
||||
{ value: 'POST', color: '#2db7f5' }, |
||||
{ value: 'PUT', color: 'warning' }, |
||||
{ value: 'DELETE', color: '#f50' }, |
||||
] |
||||
|
||||
export const infoSchema: DescItem[] = [ |
||||
{ |
||||
field: 'id', |
||||
label: '异常id', |
||||
}, |
||||
{ |
||||
field: 'traceId', |
||||
label: '链路ID', |
||||
show(data) { |
||||
return data && data.traceId && data.traceId !== '' |
||||
}, |
||||
}, |
||||
{ |
||||
field: 'applicationName', |
||||
label: '应用名称', |
||||
labelMinWidth: 100, |
||||
}, |
||||
{ |
||||
field: 'processStatus', |
||||
label: '处理状态', |
||||
render(_, data) { |
||||
const { processStatus, processUserId } = data |
||||
const tag = useRender.renderDict(processStatus, DICT_TYPE.INFRA_API_ERROR_LOG_PROCESS_STATUS) |
||||
if (!processUserId) |
||||
return tag |
||||
|
||||
const uidTag = useRender.renderTag(`uid: ${processUserId}`) |
||||
return h('span', {}, [tag, uidTag]) |
||||
}, |
||||
}, |
||||
{ |
||||
field: 'processTime', |
||||
label: '处理时间', |
||||
show(data) { |
||||
return data && data.processTime && data.processTime !== '' |
||||
}, |
||||
render(value) { |
||||
return useRender.renderDate(value) |
||||
}, |
||||
}, |
||||
{ |
||||
field: 'userId', |
||||
label: '用户id', |
||||
render(value, data) { |
||||
const tag = useRender.renderDict(data.userType, DICT_TYPE.USER_TYPE) |
||||
const uidTag = useRender.renderTag(`uid: ${value}`) |
||||
return h('span', {}, [tag, uidTag]) |
||||
}, |
||||
}, |
||||
{ |
||||
field: 'userIp', |
||||
label: 'ip地址', |
||||
}, |
||||
{ |
||||
field: 'requestUrl', |
||||
label: '请求地址', |
||||
render(_, data) { |
||||
if (data) { |
||||
const { requestMethod } = data |
||||
const current = httpMethods.find(item => item.value === requestMethod) |
||||
const tag = current ? useRender.renderTag(requestMethod, current.color) : requestMethod |
||||
return h('span', {}, [tag, data.requestUrl]) |
||||
} |
||||
}, |
||||
}, |
||||
{ |
||||
field: 'requestParams', |
||||
label: '请求参数', |
||||
render(value) { |
||||
return useRender.renderJsonPreview(value) |
||||
}, |
||||
}, |
||||
{ |
||||
field: 'userAgent', |
||||
label: 'userAgent', |
||||
}, |
||||
{ |
||||
field: 'exceptionTime', |
||||
label: '异常时间', |
||||
render(value) { |
||||
return useRender.renderDate(value) |
||||
}, |
||||
}, |
||||
{ |
||||
field: 'exceptionClassName', |
||||
label: '异常类名/方法', |
||||
render(_, data) { |
||||
if (data) |
||||
return renderText(`${data.exceptionClassName} / ${data.exceptionMethodName}`, 'red') |
||||
}, |
||||
}, |
||||
{ |
||||
field: 'exceptionMessage', |
||||
label: '异常信息', |
||||
render(value) { |
||||
return renderText(value, 'red') |
||||
}, |
||||
}, |
||||
{ |
||||
field: 'exceptionFileName', |
||||
label: '异常文件名', |
||||
render(_, data) { |
||||
if (data) |
||||
return useRender.renderText(data.exceptionFileName, `Line: ${data.exceptionLineNumber}`) |
||||
}, |
||||
}, |
||||
{ |
||||
field: 'exceptionName', |
||||
label: '异常名称', |
||||
}, |
||||
{ |
||||
field: 'exceptionRootCauseMessage', |
||||
label: '异常信息', |
||||
}, |
||||
{ |
||||
field: 'exceptionStackTrace', |
||||
label: '异常堆栈', |
||||
render(value) { |
||||
return h(Textarea, { value, readonly: true, style: { minHeight: '300px' } }) |
||||
}, |
||||
}, |
||||
] |
@ -1,101 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { columns, searchFormSchema } from './apiErrorLog.data' |
||||
import ErrorLogModal from './ErrorLogModal.vue' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { IconEnum } from '@/enums/appEnum' |
||||
import { InfraApiErrorLogProcessStatusEnum } from '@/enums/systemEnum' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { BasicTable, TableAction, useTable } from '@/components/Table' |
||||
import type { ApiErrorLogExportReqVO } from '@/api/infra/apiErrorLog' |
||||
import { exportApiErrorLog, getApiErrorLogPage, updateApiErrorLogProcess } from '@/api/infra/apiErrorLog' |
||||
import { useModal } from '@/components/Modal' |
||||
|
||||
defineOptions({ name: 'InfraApiErrorLog' }) |
||||
|
||||
const { t } = useI18n() |
||||
const { createConfirm, createMessage } = useMessage() |
||||
const [registerTable, { getForm, reload }] = useTable({ |
||||
title: '异常日志列表', |
||||
api: getApiErrorLogPage, |
||||
columns, |
||||
formConfig: { labelWidth: 120, schemas: searchFormSchema }, |
||||
useSearchForm: true, |
||||
showTableSetting: true, |
||||
showIndexColumn: false, |
||||
actionColumn: { |
||||
width: 220, |
||||
title: t('common.action'), |
||||
dataIndex: 'action', |
||||
fixed: 'right', |
||||
}, |
||||
}) |
||||
|
||||
const [registerModal, { openModal }] = useModal() |
||||
function handleShowInfo(record: Recordable) { |
||||
openModal(true, record) |
||||
} |
||||
|
||||
function handleProcessClick(record, processStatus: number, type: string) { |
||||
createConfirm({ |
||||
iconType: 'warning', |
||||
content: `确认标记为${type}?`, |
||||
async onOk() { |
||||
await updateApiErrorLogProcess(record.id, processStatus) |
||||
createMessage.success(t('common.successText')) |
||||
reload() |
||||
}, |
||||
}) |
||||
} |
||||
|
||||
async function handleExport() { |
||||
createConfirm({ |
||||
title: t('common.exportTitle'), |
||||
iconType: 'warning', |
||||
content: t('common.exportMessage'), |
||||
async onOk() { |
||||
await exportApiErrorLog(getForm().getFieldsValue() as ApiErrorLogExportReqVO) |
||||
createMessage.success(t('common.exportSuccessText')) |
||||
}, |
||||
}) |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<BasicTable @register="registerTable"> |
||||
<template #toolbar> |
||||
<a-button v-auth="['infra:api-error-log:export']" :pre-icon="IconEnum.EXPORT" @click="handleExport"> |
||||
{{ t('action.export') }} |
||||
</a-button> |
||||
</template> |
||||
<template #bodyCell="{ column, record }"> |
||||
<template v-if="column.key === 'action'"> |
||||
<TableAction |
||||
:actions="[ |
||||
{ |
||||
icon: IconEnum.VIEW, |
||||
label: t('action.detail'), |
||||
onClick: handleShowInfo.bind(null, record), |
||||
}, |
||||
{ |
||||
icon: IconEnum.EDIT, |
||||
label: '已处理', |
||||
auth: 'infra:api-error-log:update-status', |
||||
ifShow: () => record.processStatus === InfraApiErrorLogProcessStatusEnum.INIT, |
||||
onClick: handleProcessClick.bind(null, record, InfraApiErrorLogProcessStatusEnum.DONE, '已处理'), |
||||
}, |
||||
{ |
||||
icon: IconEnum.EDIT, |
||||
label: '已忽略', |
||||
auth: 'infra:api-error-log:update-status', |
||||
ifShow: () => record.processStatus === InfraApiErrorLogProcessStatusEnum.INIT, |
||||
onClick: handleProcessClick.bind(null, record, InfraApiErrorLogProcessStatusEnum.IGNORE, '已忽略'), |
||||
}, |
||||
]" |
||||
/> |
||||
</template> |
||||
</template> |
||||
</BasicTable> |
||||
<ErrorLogModal @register="registerModal" /> |
||||
</div> |
||||
</template> |
@ -1,10 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { PageWrapper } from '@/components/Page' |
||||
import { VFormDesign } from '@/components/FormDesign' |
||||
</script> |
||||
|
||||
<template> |
||||
<PageWrapper dense fixed-height content-full-height> |
||||
<VFormDesign /> |
||||
</PageWrapper> |
||||
</template> |
@ -1,96 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { onMounted, reactive, ref } from 'vue' |
||||
import { Steps } from 'ant-design-vue' |
||||
import { useRoute } from 'vue-router' |
||||
import BasicInfoForm from './components/BasicInfoForm.vue' |
||||
import CloumInfoForm from './components/CloumInfoForm.vue' |
||||
import FinishForm from './components/FinishForm.vue' |
||||
import { PageWrapper } from '@/components/Page' |
||||
import { getCodegenTable, updateCodegenTable } from '@/api/infra/codegen' |
||||
|
||||
const Step = Steps.Step |
||||
|
||||
const { query } = useRoute() |
||||
|
||||
// 表详细信息 |
||||
const basicInfo = ref<any>() |
||||
// 表列信息 |
||||
const columnsInfo = ref<any[]>([]) |
||||
|
||||
const basicInfoValue = ref() |
||||
|
||||
const columnsInfoValue = ref() |
||||
|
||||
const current = ref(0) |
||||
const state = reactive({ |
||||
initSetp2: false, |
||||
initSetp3: false, |
||||
}) |
||||
|
||||
function handleStep1Next(step1Values: any) { |
||||
current.value++ |
||||
basicInfoValue.value = step1Values |
||||
state.initSetp2 = true |
||||
} |
||||
|
||||
function handleStepPrev() { |
||||
current.value-- |
||||
} |
||||
|
||||
async function handleStep2Next(step2Values: any) { |
||||
current.value++ |
||||
columnsInfoValue.value = step2Values |
||||
await handleSubmit() |
||||
state.initSetp3 = true |
||||
} |
||||
|
||||
async function handleSubmit() { |
||||
basicInfoValue.value.id = query.id as unknown as number |
||||
const genTable = { |
||||
table: basicInfoValue.value, |
||||
columns: columnsInfoValue.value, |
||||
} |
||||
await updateCodegenTable(genTable) |
||||
} |
||||
|
||||
function handleRedo() { |
||||
current.value = 0 |
||||
state.initSetp2 = false |
||||
state.initSetp3 = false |
||||
} |
||||
|
||||
async function getList() { |
||||
const tableId = query.id as unknown as number |
||||
const res = await getCodegenTable(tableId) |
||||
basicInfo.value = res.table |
||||
columnsInfo.value = res.columns |
||||
} |
||||
|
||||
onMounted(async () => { |
||||
await getList() |
||||
}) |
||||
</script> |
||||
|
||||
<template> |
||||
<PageWrapper> |
||||
<div class="mx-auto my-0 mt-2.5 w-200"> |
||||
<Steps :current="current"> |
||||
<Step title="生成信息" /> |
||||
<Step title="字段信息" /> |
||||
<Step title="完成" /> |
||||
</Steps> |
||||
</div> |
||||
|
||||
<div class="m-5"> |
||||
<BasicInfoForm v-show="current === 0" :basic-info="basicInfo" @next="handleStep1Next" /> |
||||
<CloumInfoForm |
||||
v-show="current === 1" |
||||
v-if="state.initSetp2" |
||||
:columns-info="columnsInfo" |
||||
@prev="handleStepPrev" |
||||
@next="handleStep2Next" |
||||
/> |
||||
<FinishForm v-show="current === 2" v-if="state.initSetp3" @redo="handleRedo" /> |
||||
</div> |
||||
</PageWrapper> |
||||
</template> |
@ -1,157 +0,0 @@
|
||||
import { getDataSourceConfigList } from '@/api/infra/dataSourceConfig' |
||||
import type { BasicColumn, FormSchema } from '@/components/Table' |
||||
import { useRender } from '@/components/Table' |
||||
import { DICT_TYPE, getDictOptions } from '@/utils/dict' |
||||
|
||||
const dataSourceConfigs = await getDataSourceConfigList() |
||||
|
||||
export const columns: BasicColumn[] = [ |
||||
{ |
||||
title: '数据源', |
||||
dataIndex: 'dataSourceConfigId', |
||||
width: 100, |
||||
customRender: ({ text }) => { |
||||
for (const config of dataSourceConfigs) { |
||||
if (text === config.id) |
||||
return config.name |
||||
} |
||||
return `未知【${text}】` |
||||
}, |
||||
}, |
||||
{ |
||||
title: '表名称', |
||||
dataIndex: 'tableName', |
||||
width: 200, |
||||
}, |
||||
{ |
||||
title: '表描述', |
||||
dataIndex: 'tableComment', |
||||
width: 120, |
||||
}, |
||||
{ |
||||
title: '实体', |
||||
dataIndex: 'className', |
||||
width: 200, |
||||
}, |
||||
{ |
||||
title: '创建时间', |
||||
dataIndex: 'createTime', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDate(text) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '更新时间', |
||||
dataIndex: 'updateTime', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDate(text) |
||||
}, |
||||
}, |
||||
] |
||||
|
||||
export const searchFormSchema: FormSchema[] = [ |
||||
{ |
||||
label: '表名称', |
||||
field: 'tableName', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '表描述', |
||||
field: 'tableComment', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '创建时间', |
||||
field: 'createTime', |
||||
component: 'RangePicker', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
] |
||||
|
||||
export const formSchema: FormSchema[] = [ |
||||
{ |
||||
label: '编号', |
||||
field: 'id', |
||||
show: false, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '岗位名称', |
||||
field: 'name', |
||||
required: true, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '岗位编码', |
||||
field: 'code', |
||||
required: true, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '岗位顺序', |
||||
field: 'sort', |
||||
required: true, |
||||
defaultValue: 0, |
||||
component: 'InputNumber', |
||||
}, |
||||
{ |
||||
label: '状态', |
||||
field: 'status', |
||||
component: 'Select', |
||||
componentProps: { |
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS) as any, |
||||
}, |
||||
}, |
||||
{ |
||||
label: '备注', |
||||
field: 'remark', |
||||
component: 'InputTextArea', |
||||
}, |
||||
] |
||||
|
||||
export const importTableColumns: BasicColumn[] = [ |
||||
{ |
||||
title: '表名称', |
||||
dataIndex: 'name', |
||||
width: 200, |
||||
}, |
||||
{ |
||||
title: '表描述', |
||||
dataIndex: 'comment', |
||||
width: 120, |
||||
}, |
||||
] |
||||
|
||||
export const importTableSearchFormSchema: FormSchema[] = [ |
||||
{ |
||||
label: '数据源', |
||||
field: 'dataSourceConfigId', |
||||
component: 'Select', |
||||
required: true, |
||||
defaultValue: dataSourceConfigs[0].id, |
||||
componentProps: { |
||||
options: dataSourceConfigs, |
||||
fieldNames: { |
||||
label: 'name', |
||||
value: 'id', |
||||
}, |
||||
}, |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '表名称', |
||||
field: 'name', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '表描述', |
||||
field: 'comment', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
] |
@ -1,70 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { Divider } from 'ant-design-vue' |
||||
import { watch } from 'vue' |
||||
import { basicInfoSchemas } from './data' |
||||
import { BasicForm, useForm } from '@/components/Form' |
||||
import type { CodegenTableVO } from '@/api/infra/codegen/types' |
||||
|
||||
const props = defineProps({ |
||||
basicInfo: { |
||||
type: Object as PropType<Nullable<CodegenTableVO>>, |
||||
default: () => null, |
||||
}, |
||||
}) |
||||
|
||||
const emit = defineEmits(['next']) |
||||
|
||||
const [register, { setFieldsValue, resetFields, validate }] = useForm({ |
||||
labelWidth: 120, |
||||
schemas: basicInfoSchemas, |
||||
actionColOptions: { |
||||
span: 14, |
||||
}, |
||||
showResetButton: false, |
||||
submitButtonOptions: { |
||||
text: '保存', |
||||
}, |
||||
submitFunc: customSubmitFunc, |
||||
}) |
||||
|
||||
async function customSubmitFunc() { |
||||
try { |
||||
const values = await validate() |
||||
emit('next', values) |
||||
} |
||||
catch (error) {} |
||||
} |
||||
|
||||
watch( |
||||
() => props.basicInfo, |
||||
(basicInfo) => { |
||||
if (!basicInfo) |
||||
return |
||||
resetFields() |
||||
setFieldsValue({ ...basicInfo }) |
||||
}, |
||||
{ |
||||
deep: true, |
||||
immediate: true, |
||||
}, |
||||
) |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<div class="mx-auto my-0 w-80%"> |
||||
<BasicForm @register="register" /> |
||||
</div> |
||||
<Divider /> |
||||
<h3 class="mb-3 text-base"> |
||||
说明 |
||||
</h3> |
||||
<h4 class="mb-1 text-sm"> |
||||
基本信息 |
||||
</h4> |
||||
<p> 配置生成的基本信息 </p> |
||||
|
||||
<h4>生成信息</h4> |
||||
<p> 配置生成生成的详细信息。 </p> |
||||
</div> |
||||
</template> |
@ -1,62 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { Divider } from 'ant-design-vue' |
||||
import { columns } from './data' |
||||
import type { EditRecordRow } from '@/components/Table' |
||||
import { BasicTable, useTable } from '@/components/Table' |
||||
import type { CodegenColumnVO } from '@/api/infra/codegen/types' |
||||
|
||||
defineProps({ |
||||
columnsInfo: { |
||||
type: Array as PropType<CodegenColumnVO[]>, |
||||
default: () => null, |
||||
}, |
||||
}) |
||||
|
||||
const emit = defineEmits(['next', 'prev']) |
||||
|
||||
const [registerTable, { getDataSource }] = useTable({ |
||||
columns, |
||||
maxHeight: 700, |
||||
pagination: false, |
||||
useSearchForm: false, |
||||
showTableSetting: false, |
||||
showIndexColumn: false, |
||||
}) |
||||
|
||||
async function customResetFunc() { |
||||
emit('prev') |
||||
} |
||||
|
||||
async function customSubmitFunc() { |
||||
const tableValue = getDataSource() |
||||
emit('next', tableValue) |
||||
} |
||||
|
||||
function handleEdit(record: EditRecordRow) { |
||||
record.onEdit?.(true) |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<div class="step2"> |
||||
<div class="mx-auto my-0 w-full"> |
||||
<BasicTable :data-source="columnsInfo" @register="registerTable" @row-click="handleEdit" /> |
||||
</div> |
||||
<Divider /> |
||||
<div class="flex justify-center"> |
||||
<a-button @click="customResetFunc"> |
||||
上一步 |
||||
</a-button> |
||||
<a-button type="primary" @click="customSubmitFunc"> |
||||
提交 |
||||
</a-button> |
||||
</div> |
||||
<h3 class="mb-3 text-base"> |
||||
说明 |
||||
</h3> |
||||
<h4 class="mb-1 text-sm"> |
||||
配置字段 |
||||
</h4> |
||||
<p> 配置表的字段类型,增删改查,字典等 </p> |
||||
</div> |
||||
</template> |
@ -1,55 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { Result } from 'ant-design-vue' |
||||
import { useRoute } from 'vue-router' |
||||
import PreviewModal from './PreviewModal.vue' |
||||
import { useModal } from '@/components/Modal' |
||||
import { useGo } from '@/hooks/web/usePage' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useTabs } from '@/hooks/web/useTabs' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { downloadCodegen, getCodegenTable } from '@/api/infra/codegen' |
||||
|
||||
const go = useGo() |
||||
const { closeCurrent } = useTabs() |
||||
const { query } = useRoute() |
||||
const { t } = useI18n() |
||||
const { createMessage } = useMessage() |
||||
const [registerPreviewModal, { openModal: openPreviewModal }] = useModal() |
||||
|
||||
function handlePreview() { |
||||
const tableId = query.id as unknown as number |
||||
const record = { id: tableId } |
||||
openPreviewModal(true, { record }) |
||||
} |
||||
|
||||
async function handleGenTable() { |
||||
const tableId = query.id as unknown as number |
||||
const res = await getCodegenTable(tableId) |
||||
await downloadCodegen(res.table) |
||||
createMessage.success(t('common.successText')) |
||||
} |
||||
|
||||
function handleGoList() { |
||||
closeCurrent() |
||||
go('/infra/codegen') |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<div class="m-5 bg-white px-8 py-12 dark:bg-dark"> |
||||
<Result status="success" title="代码生成成功" sub-title="可点击下方按钮预览、下载,或返回列表页。"> |
||||
<template #extra> |
||||
<a-button key="console" type="primary" @click="handleGoList"> |
||||
返回列表 |
||||
</a-button> |
||||
<a-button key="preview" @click="handlePreview"> |
||||
预览 |
||||
</a-button> |
||||
<a-button key="download" @click="handleGenTable"> |
||||
生成 |
||||
</a-button> |
||||
</template> |
||||
</Result> |
||||
<PreviewModal @register="registerPreviewModal" /> |
||||
</div> |
||||
</template> |
@ -1,43 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { importTableColumns, importTableSearchFormSchema } from '../codegen.data' |
||||
import { BasicModal, useModalInner } from '@/components/Modal' |
||||
import { BasicTable, useTable } from '@/components/Table' |
||||
import { createCodegenList, getSchemaTableList } from '@/api/infra/codegen' |
||||
|
||||
defineOptions({ name: 'InfraImportTableModal' }) |
||||
const emit = defineEmits(['success', 'register']) |
||||
|
||||
const [registerTable, { getSelectRowKeys, getForm }] = useTable({ |
||||
api: getSchemaTableList, |
||||
columns: importTableColumns, |
||||
formConfig: { |
||||
labelWidth: 80, |
||||
schemas: importTableSearchFormSchema, |
||||
}, |
||||
rowSelection: { type: 'checkbox' }, |
||||
rowKey: 'name', |
||||
useSearchForm: true, |
||||
pagination: false, |
||||
showTableSetting: false, |
||||
showIndexColumn: false, |
||||
immediate: false, |
||||
}) |
||||
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async () => { |
||||
setModalProps({ confirmLoading: false }) |
||||
}) |
||||
|
||||
async function handleSubmit() { |
||||
const datas = await getSelectRowKeys() |
||||
const form = await getForm() |
||||
await createCodegenList({ dataSourceConfigId: form.getFieldsValue().dataSourceConfigId, tableNames: datas }) |
||||
closeModal() |
||||
emit('success') |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<BasicModal v-bind="$attrs" :width="800" title="导入" @register="registerModal" @ok="handleSubmit"> |
||||
<BasicTable @register="registerTable" /> |
||||
</BasicModal> |
||||
</template> |
@ -1,144 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { ref, unref } from 'vue' |
||||
import { Card, Tabs } from 'ant-design-vue' |
||||
import { useClipboard } from '@vueuse/core' |
||||
import { BasicTree } from '@/components/Tree' |
||||
import { BasicModal, useModalInner } from '@/components/Modal' |
||||
import { CodeEditor, MODE } from '@/components/CodeEditor' |
||||
import { previewCodegen } from '@/api/infra/codegen' |
||||
import type { CodegenPreviewVO } from '@/api/infra/codegen/types' |
||||
import { handleTree2 } from '@/utils/tree' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
|
||||
defineOptions({ name: 'InfraPreviewModal' }) |
||||
|
||||
const { createMessage } = useMessage() |
||||
|
||||
const fileTree = ref([]) |
||||
const activeKey = ref('') |
||||
const modeValue = ref(MODE.JS) |
||||
const previewCodes = ref<CodegenPreviewVO[]>() |
||||
|
||||
const [registerModal, { setModalProps }] = useModalInner(async (data) => { |
||||
setModalProps({ confirmLoading: false }) |
||||
|
||||
const res = await previewCodegen(data.record.id) |
||||
const file = handleFiles(res) |
||||
previewCodes.value = res |
||||
activeKey.value = res[0].filePath |
||||
fileTree.value = handleTree2(file, 'id', 'parentId', 'children', '/') |
||||
}) |
||||
|
||||
function handleSelect(keys) { |
||||
activeKey.value = keys[0] |
||||
} |
||||
|
||||
/** 生成 files 目录 */ |
||||
interface filesType { |
||||
id: string |
||||
label: string |
||||
parentId: string |
||||
} |
||||
|
||||
function handleFiles(datas) { |
||||
const exists = {} // key:file 的 id;value:true |
||||
const files: filesType[] = [] |
||||
// 遍历每个元素 |
||||
for (const data of datas) { |
||||
let paths = data.filePath.split('/') |
||||
let fullPath = '' // 从头开始的路径,用于生成 id |
||||
// 特殊处理 java 文件 |
||||
if (paths[paths.length - 1].includes('.java')) { |
||||
const newPaths: string[] = [] |
||||
for (let i = 0; i < paths.length; i++) { |
||||
let path = paths[i] |
||||
if (path !== 'java') { |
||||
newPaths.push(path) |
||||
continue |
||||
} |
||||
newPaths.push(path) |
||||
// 特殊处理中间的 package,进行合并 |
||||
let tmp = '' |
||||
while (i < paths.length) { |
||||
path = paths[i + 1] |
||||
if ( |
||||
path === 'controller' |
||||
|| path === 'convert' |
||||
|| path === 'dal' |
||||
|| path === 'enums' |
||||
|| path === 'service' |
||||
|| path === 'vo' // 下面三个,主要是兜底。可能考虑到有人改了包结构 |
||||
|| path === 'mysql' |
||||
|| path === 'dataobject' |
||||
) |
||||
break |
||||
|
||||
tmp = tmp ? `${tmp}.${path}` : path |
||||
i++ |
||||
} |
||||
if (tmp) |
||||
newPaths.push(tmp) |
||||
} |
||||
paths = newPaths |
||||
} |
||||
// 遍历每个 path, 拼接成树 |
||||
for (let i = 0; i < paths.length; i++) { |
||||
// 已经添加到 files 中,则跳过 |
||||
const oldFullPath = fullPath |
||||
// 下面的 replaceAll 的原因,是因为上面包处理了,导致和 tabs 不匹配,所以 replaceAll 下 |
||||
fullPath = fullPath.length === 0 ? paths[i] : `${fullPath.replaceAll('.', '/')}/${paths[i]}` |
||||
if (exists[fullPath]) |
||||
continue |
||||
|
||||
// 添加到 files 中 |
||||
exists[fullPath] = true |
||||
files.push({ |
||||
id: fullPath, |
||||
label: paths[i], |
||||
parentId: oldFullPath || '/', // "/" 为根节点 |
||||
}) |
||||
} |
||||
} |
||||
return files |
||||
} |
||||
|
||||
/** 复制 */ |
||||
async function copy(text: string) { |
||||
const { copy, copied, isSupported } = useClipboard({ source: text }) |
||||
if (!isSupported) { |
||||
createMessage.error('复制失败') |
||||
} |
||||
else { |
||||
await copy() |
||||
if (unref(copied)) |
||||
createMessage.success('复制成功') |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<BasicModal v-bind="$attrs" :default-fullscreen="true" title="预览代码" @register="registerModal"> |
||||
<div class="flex"> |
||||
<Card class="min-w-130 w-1/4"> |
||||
<BasicTree |
||||
title="文件夹列表" toolbar :default-expand-all="true" tree-wrapper-class-name="h-[800px] overflow-auto" |
||||
:click-row-to-expand="false" :tree-data="fileTree" :field-names="{ key: 'id', title: 'label' }" |
||||
@select="handleSelect" |
||||
/> |
||||
</Card> |
||||
<Card class="w-3/4"> |
||||
<Tabs v-model:activeKey="activeKey"> |
||||
<Tabs.TabPane |
||||
v-for="item in previewCodes" :key="item.filePath" |
||||
:tab="item.filePath.substring(item.filePath.lastIndexOf('/') + 1)" |
||||
> |
||||
<a-button type="link" style="float: right; padding: 4px 60px;" @click="copy(item.code)"> |
||||
复制 |
||||
</a-button> |
||||
<CodeEditor class="max-h-200" :value="item.code" :mode="modeValue" :readonly="true" /> |
||||
</Tabs.TabPane> |
||||
</Tabs> |
||||
</Card> |
||||
</div> |
||||
</BasicModal> |
||||
</template> |
@ -1,328 +0,0 @@
|
||||
import { listSimpleDictType } from '@/api/system/dict/type' |
||||
import { listSimpleMenus } from '@/api/system/menu' |
||||
import type { FormSchema } from '@/components/Form' |
||||
import type { BasicColumn } from '@/components/Table' |
||||
import { DICT_TYPE, getDictOptions } from '@/utils/dict' |
||||
|
||||
async function getDictTypeOptions() { |
||||
const dictTypeOptions: any[] = [] |
||||
const res = await listSimpleDictType() |
||||
dictTypeOptions.push(...res) |
||||
return dictTypeOptions |
||||
} |
||||
|
||||
export const basicInfoSchemas: FormSchema[] = [ |
||||
{ |
||||
label: '基本信息', |
||||
field: 'basicInfo', |
||||
component: 'Divider', |
||||
colProps: { span: 24 }, |
||||
}, |
||||
{ |
||||
label: '表名称', |
||||
field: 'tableName', |
||||
required: true, |
||||
component: 'Input', |
||||
colProps: { span: 12 }, |
||||
}, |
||||
{ |
||||
label: '表描述', |
||||
field: 'tableComment', |
||||
required: true, |
||||
component: 'Input', |
||||
colProps: { span: 12 }, |
||||
}, |
||||
{ |
||||
label: '实体类名称', |
||||
field: 'className', |
||||
required: true, |
||||
helpMessage: '默认去除表名的前缀。如果存在重复,则需要手动添加前缀,避免 MyBatis 报 Alias 重复的问题。', |
||||
component: 'Input', |
||||
colProps: { span: 12 }, |
||||
}, |
||||
{ |
||||
label: '作者', |
||||
field: 'author', |
||||
required: true, |
||||
component: 'Input', |
||||
colProps: { span: 12 }, |
||||
}, |
||||
{ |
||||
label: '生成信息', |
||||
field: 'genInfo', |
||||
component: 'Divider', |
||||
colProps: { span: 24 }, |
||||
}, |
||||
{ |
||||
label: '生成模板', |
||||
field: 'templateType', |
||||
required: true, |
||||
component: 'Select', |
||||
defaultValue: '30', |
||||
componentProps: { |
||||
options: getDictOptions(DICT_TYPE.INFRA_CODEGEN_TEMPLATE_TYPE) as any, |
||||
}, |
||||
colProps: { span: 12 }, |
||||
}, |
||||
{ |
||||
label: '前端类型', |
||||
field: 'frontType', |
||||
required: true, |
||||
component: 'Select', |
||||
componentProps: { |
||||
options: getDictOptions(DICT_TYPE.INFRA_CODEGEN_FRONT_TYPE) as any, |
||||
}, |
||||
colProps: { span: 12 }, |
||||
}, |
||||
{ |
||||
label: '生成场景', |
||||
field: 'scene', |
||||
required: true, |
||||
component: 'Select', |
||||
componentProps: { |
||||
options: getDictOptions(DICT_TYPE.INFRA_CODEGEN_SCENE), |
||||
}, |
||||
colProps: { span: 12 }, |
||||
}, |
||||
{ |
||||
label: '模块名', |
||||
field: 'moduleName', |
||||
required: true, |
||||
helpMessage: '模块名,即一级目录,例如 system、infra、tool 等等', |
||||
component: 'Input', |
||||
colProps: { span: 12 }, |
||||
}, |
||||
{ |
||||
label: '业务名', |
||||
field: 'businessName', |
||||
required: true, |
||||
component: 'Input', |
||||
helpMessage: '业务名,即二级目录,例如 user、permission、dict 等等', |
||||
colProps: { span: 12 }, |
||||
}, |
||||
{ |
||||
label: '类名称', |
||||
field: 'className', |
||||
required: true, |
||||
component: 'Input', |
||||
helpMessage: '类名称(首字母大写),例如SysUser、SysMenu、SysDictData 等等', |
||||
colProps: { span: 12 }, |
||||
}, |
||||
{ |
||||
label: '类描述', |
||||
field: 'classComment', |
||||
required: true, |
||||
component: 'Input', |
||||
helpMessage: '用作类描述,例如 用户', |
||||
colProps: { span: 12 }, |
||||
}, |
||||
{ |
||||
label: '上级菜单', |
||||
field: 'parentMenuId', |
||||
required: true, |
||||
component: 'ApiTreeSelect', |
||||
componentProps: { |
||||
api: () => listSimpleMenus(), |
||||
handleTree: 'id', |
||||
}, |
||||
colProps: { span: 12 }, |
||||
}, |
||||
{ |
||||
label: '自定义路径', |
||||
field: 'genPath', |
||||
component: 'Input', |
||||
helpMessage: '填写磁盘绝对路径,若不填写,则生成到当前Web项目下', |
||||
defaultValue: '/', |
||||
ifShow: ({ values }) => values.genType === '1', |
||||
colProps: { span: 12 }, |
||||
}, |
||||
{ |
||||
label: '备注', |
||||
field: 'remark', |
||||
component: 'InputTextArea', |
||||
colProps: { span: 24 }, |
||||
}, |
||||
] |
||||
|
||||
export const columns: BasicColumn[] = [ |
||||
{ |
||||
title: '字段列名', |
||||
dataIndex: 'columnName', |
||||
width: 60, |
||||
}, |
||||
{ |
||||
title: '基础属性', |
||||
children: [ |
||||
{ |
||||
title: '物理类型', |
||||
dataIndex: 'dataType', |
||||
editComponent: 'Select', |
||||
width: 50, |
||||
}, |
||||
{ |
||||
title: '字段描述', |
||||
dataIndex: 'columnComment', |
||||
editRow: true, |
||||
editComponent: 'Input', |
||||
width: 50, |
||||
}, |
||||
{ |
||||
title: 'Java类型', |
||||
dataIndex: 'javaType', |
||||
editRow: true, |
||||
editComponent: 'Select', |
||||
editComponentProps: { |
||||
options: [ |
||||
{ |
||||
label: 'Long', |
||||
value: 'Long', |
||||
}, |
||||
{ |
||||
label: 'String', |
||||
value: 'String', |
||||
}, |
||||
{ |
||||
label: 'Integer', |
||||
value: 'Integer', |
||||
}, |
||||
{ |
||||
label: 'Double', |
||||
value: 'Double', |
||||
}, |
||||
{ |
||||
label: 'BigDecimal', |
||||
value: 'BigDecimal', |
||||
}, |
||||
{ |
||||
label: 'LocalDateTime', |
||||
value: 'LocalDateTime', |
||||
}, |
||||
{ |
||||
label: 'Boolean', |
||||
value: 'Boolean', |
||||
}, |
||||
], |
||||
}, |
||||
width: 50, |
||||
}, |
||||
{ |
||||
title: 'java属性', |
||||
dataIndex: 'javaField', |
||||
editRow: true, |
||||
editComponent: 'Input', |
||||
width: 50, |
||||
}, |
||||
], |
||||
}, |
||||
{ |
||||
title: '增删改查', |
||||
children: [ |
||||
{ |
||||
title: '插入', |
||||
dataIndex: 'createOperation', |
||||
editRow: true, |
||||
editComponent: 'Checkbox', |
||||
editValueMap: (value) => { |
||||
return value ? '是' : '否' |
||||
}, |
||||
width: 40, |
||||
}, |
||||
{ |
||||
title: '编辑', |
||||
dataIndex: 'updateOperation', |
||||
editRow: true, |
||||
editComponent: 'Checkbox', |
||||
editValueMap: (value) => { |
||||
return value ? '是' : '否' |
||||
}, |
||||
width: 40, |
||||
}, |
||||
{ |
||||
title: '列表', |
||||
dataIndex: 'listOperationResult', |
||||
editRow: true, |
||||
editComponent: 'Checkbox', |
||||
editValueMap: (value) => { |
||||
return value ? '是' : '否' |
||||
}, |
||||
width: 40, |
||||
}, |
||||
{ |
||||
title: '查询', |
||||
dataIndex: 'listOperation', |
||||
editRow: true, |
||||
editComponent: 'Checkbox', |
||||
editValueMap: (value) => { |
||||
return value ? '是' : '否' |
||||
}, |
||||
width: 40, |
||||
}, |
||||
{ |
||||
title: '查询方式', |
||||
dataIndex: 'listOperationCondition', |
||||
editRow: true, |
||||
editComponent: 'Select', |
||||
editComponentProps: { |
||||
options: [ |
||||
{ label: '=', value: '=' }, |
||||
{ label: '!=', value: '!=' }, |
||||
{ label: '>', value: '>' }, |
||||
{ label: '>=', value: '>=' }, |
||||
{ label: '<', value: '<' }, |
||||
{ label: '<=', value: '<=' }, |
||||
{ label: 'LIKE', value: 'LIKE' }, |
||||
{ label: 'BETWEEN', value: 'BETWEEN' }, |
||||
], |
||||
}, |
||||
width: 80, |
||||
}, |
||||
{ |
||||
title: '允许空', |
||||
dataIndex: 'nullable', |
||||
editRow: true, |
||||
editComponent: 'Checkbox', |
||||
editValueMap: (value) => { |
||||
return value ? '是' : '否' |
||||
}, |
||||
width: 40, |
||||
}, |
||||
{ |
||||
title: '显示类型', |
||||
dataIndex: 'htmlType', |
||||
editRow: true, |
||||
editComponent: 'Select', |
||||
editComponentProps: { |
||||
options: [ |
||||
{ label: '文本框', value: 'input' }, |
||||
{ label: '文本域', value: 'textarea' }, |
||||
{ label: '下拉框', value: 'select' }, |
||||
{ label: '单选框', value: 'radio' }, |
||||
{ label: '复选框', value: 'checkbox' }, |
||||
{ label: '日期控件', value: 'datetime' }, |
||||
{ label: '图片上传', value: 'imageUpload' }, |
||||
{ label: '文件上传', value: 'fileUpload' }, |
||||
{ label: '富文本控件', value: 'editor' }, |
||||
], |
||||
}, |
||||
width: 60, |
||||
}, |
||||
{ |
||||
title: '字典类型', |
||||
dataIndex: 'dictType', |
||||
editRow: true, |
||||
editComponent: 'Select', |
||||
editComponentProps: { |
||||
options: (await getDictTypeOptions()).map(item => ({ value: item.type, label: item.name })), |
||||
}, |
||||
width: 100, |
||||
}, |
||||
{ |
||||
title: '示例', |
||||
dataIndex: 'example', |
||||
editRow: true, |
||||
editComponent: 'Input', |
||||
width: 60, |
||||
}, |
||||
], |
||||
}, |
||||
] |
@ -1,109 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import PreviewModal from './components/PreviewModal.vue' |
||||
import ImportTableModal from './components/ImportTableModal.vue' |
||||
import { columns, searchFormSchema } from './codegen.data' |
||||
import { useGo } from '@/hooks/web/usePage' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { useModal } from '@/components/Modal' |
||||
import { IconEnum } from '@/enums/appEnum' |
||||
import { BasicTable, TableAction, useTable } from '@/components/Table' |
||||
import { deleteCodegenTable, downloadCodegen, getCodegenTablePage, syncCodegenFromDB } from '@/api/infra/codegen' |
||||
|
||||
defineOptions({ name: 'InfraCodegen' }) |
||||
|
||||
const go = useGo() |
||||
const { t } = useI18n() |
||||
const { createMessage } = useMessage() |
||||
const [registerPreviewModal, { openModal: openPreviewModal }] = useModal() |
||||
const [registerImportTableModal, { openModal: openImportTableModal }] = useModal() |
||||
|
||||
const [registerTable, { reload }] = useTable({ |
||||
title: '代码生成列表', |
||||
api: getCodegenTablePage, |
||||
columns, |
||||
formConfig: { labelWidth: 120, schemas: searchFormSchema }, |
||||
useSearchForm: true, |
||||
showTableSetting: true, |
||||
showIndexColumn: false, |
||||
actionColumn: { |
||||
width: 360, |
||||
title: t('common.action'), |
||||
dataIndex: 'action', |
||||
fixed: 'right', |
||||
}, |
||||
}) |
||||
|
||||
function handlePreview(record: Recordable) { |
||||
openPreviewModal(true, { |
||||
record, |
||||
}) |
||||
} |
||||
|
||||
function handleEditTable(record: Recordable) { |
||||
go(`/codegen/editTable?id=${record.id}`) |
||||
} |
||||
|
||||
async function handleGenTable(record: Recordable) { |
||||
await downloadCodegen(record) |
||||
createMessage.success(t('common.successText')) |
||||
} |
||||
|
||||
async function handleSynchDb(record: Recordable) { |
||||
await syncCodegenFromDB(record.id) |
||||
createMessage.success(t('common.successText')) |
||||
reload() |
||||
} |
||||
|
||||
async function handleDelete(record: Recordable) { |
||||
await deleteCodegenTable(record.id) |
||||
createMessage.success(t('common.delSuccessText')) |
||||
reload() |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<BasicTable @register="registerTable"> |
||||
<template #toolbar> |
||||
<a-button v-auth="['infra:codegen:create']" type="primary" :pre-icon="IconEnum.IMPORT" @click="openImportTableModal(true)"> |
||||
{{ t('action.import') }} |
||||
</a-button> |
||||
</template> |
||||
<template #bodyCell="{ column, record }"> |
||||
<template v-if="column.key === 'action'"> |
||||
<TableAction |
||||
:actions="[ |
||||
{ icon: IconEnum.PREVIEW, label: '预览', auth: 'infra:codegen:preview', onClick: handlePreview.bind(null, record) }, |
||||
{ icon: IconEnum.EDIT, label: t('action.edit'), auth: 'infra:codegen:update', onClick: handleEditTable.bind(null, record) }, |
||||
{ icon: IconEnum.DOWNLOAD, label: '生成', auth: 'infra:codegen:download', onClick: handleGenTable.bind(null, record) }, |
||||
{ |
||||
icon: IconEnum.RESET, |
||||
label: t('action.sync'), |
||||
auth: 'infra:codegen:update', |
||||
popConfirm: { |
||||
title: `确认要强制同步${record.tableName}表结构吗?`, |
||||
placement: 'left', |
||||
confirm: handleSynchDb.bind(null, record), |
||||
}, |
||||
}, |
||||
{ |
||||
icon: IconEnum.DELETE, |
||||
danger: true, |
||||
label: t('action.delete'), |
||||
auth: 'infra:codegen:delete', |
||||
popConfirm: { |
||||
title: t('common.delMessage'), |
||||
placement: 'left', |
||||
confirm: handleDelete.bind(null, record), |
||||
}, |
||||
}, |
||||
]" |
||||
/> |
||||
</template> |
||||
</template> |
||||
</BasicTable> |
||||
<PreviewModal @register="registerPreviewModal" /> |
||||
<ImportTableModal @register="registerImportTableModal" @success="reload()" /> |
||||
</div> |
||||
</template> |
@ -1,58 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { ref, unref } from 'vue' |
||||
import { formSchema } from './config.data' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { BasicForm, useForm } from '@/components/Form' |
||||
import { BasicModal, useModalInner } from '@/components/Modal' |
||||
import { createConfig, getConfig, updateConfig } from '@/api/infra/config' |
||||
|
||||
defineOptions({ name: 'InfraConfigModal' }) |
||||
|
||||
const emit = defineEmits(['success', 'register']) |
||||
const { t } = useI18n() |
||||
const { createMessage } = useMessage() |
||||
const isUpdate = ref(true) |
||||
|
||||
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({ |
||||
labelWidth: 120, |
||||
baseColProps: { span: 24 }, |
||||
schemas: formSchema, |
||||
showActionButtonGroup: false, |
||||
actionColOptions: { span: 23 }, |
||||
}) |
||||
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => { |
||||
resetFields() |
||||
setModalProps({ confirmLoading: false }) |
||||
isUpdate.value = !!data?.isUpdate |
||||
if (unref(isUpdate)) { |
||||
const res = await getConfig(data.record.id) |
||||
setFieldsValue({ ...res }) |
||||
} |
||||
}) |
||||
|
||||
async function handleSubmit() { |
||||
try { |
||||
const values = await validate() as any |
||||
setModalProps({ confirmLoading: true }) |
||||
if (unref(isUpdate)) |
||||
await updateConfig(values) |
||||
else |
||||
await createConfig(values) |
||||
|
||||
closeModal() |
||||
emit('success') |
||||
createMessage.success(t('common.saveSuccessText')) |
||||
} |
||||
finally { |
||||
setModalProps({ confirmLoading: false }) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<BasicModal v-bind="$attrs" :title="isUpdate ? t('action.edit') : t('action.create')" @register="registerModal" @ok="handleSubmit"> |
||||
<BasicForm @register="registerForm" /> |
||||
</BasicModal> |
||||
</template> |
@ -1,139 +0,0 @@
|
||||
import type { BasicColumn, FormSchema } from '@/components/Table' |
||||
import { useRender } from '@/components/Table' |
||||
import { DICT_TYPE, getDictOptions } from '@/utils/dict' |
||||
|
||||
export const columns: BasicColumn[] = [ |
||||
{ |
||||
title: '参数主键', |
||||
dataIndex: 'id', |
||||
width: 100, |
||||
}, |
||||
{ |
||||
title: '参数分类', |
||||
dataIndex: 'category', |
||||
width: 180, |
||||
}, |
||||
{ |
||||
title: '参数名称', |
||||
dataIndex: 'name', |
||||
width: 100, |
||||
}, |
||||
{ |
||||
title: '参数键名', |
||||
dataIndex: 'key', |
||||
width: 120, |
||||
}, |
||||
{ |
||||
title: '参数键值', |
||||
dataIndex: 'value', |
||||
width: 120, |
||||
}, |
||||
{ |
||||
title: '系统内置', |
||||
dataIndex: 'type', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDict(text, DICT_TYPE.INFRA_CONFIG_TYPE) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '是否可见', |
||||
dataIndex: 'visible', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderTag(text ? '是' : '否') |
||||
}, |
||||
}, |
||||
{ |
||||
title: '备注', |
||||
dataIndex: 'remark', |
||||
width: 180, |
||||
}, |
||||
{ |
||||
title: '创建时间', |
||||
dataIndex: 'createTime', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDate(text) |
||||
}, |
||||
}, |
||||
] |
||||
|
||||
export const searchFormSchema: FormSchema[] = [ |
||||
{ |
||||
label: '参数名称', |
||||
field: 'name', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '参数键名', |
||||
field: 'key', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '系统内置', |
||||
field: 'type', |
||||
component: 'Select', |
||||
componentProps: { |
||||
options: getDictOptions(DICT_TYPE.INFRA_CONFIG_TYPE) as any, |
||||
}, |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '创建时间', |
||||
field: 'createTime', |
||||
component: 'RangePicker', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
] |
||||
|
||||
export const formSchema: FormSchema[] = [ |
||||
{ |
||||
label: '编号', |
||||
field: 'id', |
||||
show: false, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '参数分类', |
||||
field: 'category', |
||||
required: true, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '参数名称', |
||||
field: 'name', |
||||
required: true, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '参数键名', |
||||
field: 'key', |
||||
required: true, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '参数键值', |
||||
field: 'value', |
||||
required: true, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '是否可见', |
||||
field: 'visible', |
||||
component: 'RadioGroup', |
||||
componentProps: { |
||||
options: [ |
||||
{ label: '是', value: true }, |
||||
{ label: '否', value: false }, |
||||
], |
||||
}, |
||||
}, |
||||
{ |
||||
label: '备注', |
||||
field: 'remark', |
||||
component: 'InputTextArea', |
||||
}, |
||||
] |
@ -1,95 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import ConfigModal from './ConfigModal.vue' |
||||
import { columns, searchFormSchema } from './config.data' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { useModal } from '@/components/Modal' |
||||
import { IconEnum } from '@/enums/appEnum' |
||||
import { BasicTable, TableAction, useTable } from '@/components/Table' |
||||
import type { ConfigExportReqVO } from '@/api/infra/config' |
||||
import { deleteConfig, exportConfig, getConfigPage } from '@/api/infra/config' |
||||
|
||||
defineOptions({ name: 'InfraConfig' }) |
||||
|
||||
const { t } = useI18n() |
||||
const { createConfirm, createMessage } = useMessage() |
||||
const [registerModal, { openModal }] = useModal() |
||||
|
||||
const [registerTable, { getForm, reload }] = useTable({ |
||||
title: '配置中心列表', |
||||
api: getConfigPage, |
||||
columns, |
||||
formConfig: { labelWidth: 120, schemas: searchFormSchema }, |
||||
useSearchForm: true, |
||||
showTableSetting: true, |
||||
showIndexColumn: false, |
||||
actionColumn: { |
||||
width: 140, |
||||
title: t('common.action'), |
||||
dataIndex: 'action', |
||||
fixed: 'right', |
||||
}, |
||||
}) |
||||
|
||||
function handleCreate() { |
||||
openModal(true, { isUpdate: false }) |
||||
} |
||||
|
||||
function handleEdit(record: Recordable) { |
||||
openModal(true, { record, isUpdate: true }) |
||||
} |
||||
|
||||
async function handleExport() { |
||||
createConfirm({ |
||||
title: t('common.exportTitle'), |
||||
iconType: 'warning', |
||||
content: t('common.exportMessage'), |
||||
async onOk() { |
||||
await exportConfig(getForm().getFieldsValue() as ConfigExportReqVO) |
||||
createMessage.success(t('common.exportSuccessText')) |
||||
}, |
||||
}) |
||||
} |
||||
|
||||
async function handleDelete(record: Recordable) { |
||||
await deleteConfig(record.id) |
||||
createMessage.success(t('common.delSuccessText')) |
||||
reload() |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<BasicTable @register="registerTable"> |
||||
<template #toolbar> |
||||
<a-button v-auth="['infra:config:create']" type="primary" :pre-icon="IconEnum.ADD" @click="handleCreate"> |
||||
{{ t('action.create') }} |
||||
</a-button> |
||||
<a-button v-auth="['infra:config:export']" :pre-icon="IconEnum.EXPORT" @click="handleExport"> |
||||
{{ t('action.export') }} |
||||
</a-button> |
||||
</template> |
||||
<template #bodyCell="{ column, record }"> |
||||
<template v-if="column.key === 'action'"> |
||||
<TableAction |
||||
:actions="[ |
||||
{ icon: IconEnum.EDIT, label: t('action.edit'), auth: 'infra:config:update', onClick: handleEdit.bind(null, record) }, |
||||
{ |
||||
icon: IconEnum.DELETE, |
||||
danger: true, |
||||
label: t('action.delete'), |
||||
auth: 'infra:config:delete', |
||||
popConfirm: { |
||||
title: t('common.delMessage'), |
||||
placement: 'left', |
||||
confirm: handleDelete.bind(null, record), |
||||
}, |
||||
}, |
||||
]" |
||||
/> |
||||
</template> |
||||
</template> |
||||
</BasicTable> |
||||
<ConfigModal @register="registerModal" @success="reload()" /> |
||||
</div> |
||||
</template> |
@ -1,56 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { ref, unref } from 'vue' |
||||
import { formSchema } from './dataSourceConfig.data' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { BasicForm, useForm } from '@/components/Form' |
||||
import { BasicModal, useModalInner } from '@/components/Modal' |
||||
import { createDataSourceConfig, getDataSourceConfig, updateDataSourceConfig } from '@/api/infra/dataSourceConfig' |
||||
|
||||
const emit = defineEmits(['success', 'register']) |
||||
const { t } = useI18n() |
||||
const { createMessage } = useMessage() |
||||
const isUpdate = ref(true) |
||||
|
||||
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({ |
||||
labelWidth: 120, |
||||
baseColProps: { span: 24 }, |
||||
schemas: formSchema, |
||||
showActionButtonGroup: false, |
||||
actionColOptions: { span: 23 }, |
||||
}) |
||||
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => { |
||||
resetFields() |
||||
setModalProps({ confirmLoading: false }) |
||||
isUpdate.value = !!data?.isUpdate |
||||
if (unref(isUpdate)) { |
||||
const res = await getDataSourceConfig(data.record.id) |
||||
setFieldsValue({ ...res }) |
||||
} |
||||
}) |
||||
|
||||
async function handleSubmit() { |
||||
try { |
||||
const values = await validate() as any |
||||
setModalProps({ confirmLoading: true }) |
||||
if (unref(isUpdate)) |
||||
await updateDataSourceConfig(values) |
||||
else |
||||
await createDataSourceConfig(values) |
||||
|
||||
closeModal() |
||||
emit('success') |
||||
createMessage.success(t('common.saveSuccessText')) |
||||
} |
||||
finally { |
||||
setModalProps({ confirmLoading: false }) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<BasicModal v-bind="$attrs" :title="isUpdate ? t('action.edit') : t('action.create')" @register="registerModal" @ok="handleSubmit"> |
||||
<BasicForm @register="registerForm" /> |
||||
</BasicModal> |
||||
</template> |
@ -1,66 +0,0 @@
|
||||
import type { BasicColumn, FormSchema } from '@/components/Table' |
||||
import { useRender } from '@/components/Table' |
||||
|
||||
export const columns: BasicColumn[] = [ |
||||
{ |
||||
title: '主键编号', |
||||
dataIndex: 'id', |
||||
width: 100, |
||||
}, |
||||
{ |
||||
title: '数据源名称', |
||||
dataIndex: 'name', |
||||
width: 180, |
||||
}, |
||||
{ |
||||
title: '数据源连接', |
||||
dataIndex: 'url', |
||||
width: 100, |
||||
}, |
||||
{ |
||||
title: '用户名', |
||||
dataIndex: 'username', |
||||
width: 120, |
||||
}, |
||||
{ |
||||
title: '创建时间', |
||||
dataIndex: 'createTime', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDate(text) |
||||
}, |
||||
}, |
||||
] |
||||
|
||||
export const formSchema: FormSchema[] = [ |
||||
{ |
||||
label: '编号', |
||||
field: 'id', |
||||
show: false, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '数据源名称', |
||||
field: 'name', |
||||
required: true, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '数据源连接', |
||||
field: 'url', |
||||
required: true, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '用户名', |
||||
field: 'username', |
||||
required: true, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '密码', |
||||
field: 'password', |
||||
required: true, |
||||
component: 'Input', |
||||
}, |
||||
] |
@ -1,86 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import DataSourceConfigModal from './DataSourceConfigModal.vue' |
||||
import { columns } from './dataSourceConfig.data' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { useModal } from '@/components/Modal' |
||||
import { IconEnum } from '@/enums/appEnum' |
||||
import { BasicTable, TableAction, useTable } from '@/components/Table' |
||||
import { deleteDataSourceConfig, getDataSourceConfigList } from '@/api/infra/dataSourceConfig' |
||||
|
||||
defineOptions({ name: 'InfraDataSourceConfig' }) |
||||
|
||||
const { t } = useI18n() |
||||
const { createMessage } = useMessage() |
||||
const [registerModal, { openModal }] = useModal() |
||||
|
||||
const [registerTable, { reload }] = useTable({ |
||||
title: '数据源列表', |
||||
api: getDataSourceConfigList, |
||||
columns, |
||||
pagination: false, |
||||
useSearchForm: false, |
||||
showTableSetting: true, |
||||
showIndexColumn: false, |
||||
actionColumn: { |
||||
width: 140, |
||||
title: t('common.action'), |
||||
dataIndex: 'action', |
||||
fixed: 'right', |
||||
}, |
||||
}) |
||||
|
||||
function handleCreate() { |
||||
openModal(true, { isUpdate: false }) |
||||
} |
||||
|
||||
function handleEdit(record: Recordable) { |
||||
openModal(true, { record, isUpdate: true }) |
||||
} |
||||
|
||||
async function handleDelete(record: Recordable) { |
||||
await deleteDataSourceConfig(record.id) |
||||
createMessage.success(t('common.delSuccessText')) |
||||
reload() |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<BasicTable @register="registerTable"> |
||||
<template #toolbar> |
||||
<a-button v-auth="['infra:data-source-config:create']" type="primary" :pre-icon="IconEnum.ADD" @click="handleCreate"> |
||||
{{ t('action.create') }} |
||||
</a-button> |
||||
</template> |
||||
<template #bodyCell="{ column, record }"> |
||||
<template v-if="column.key === 'action'"> |
||||
<TableAction |
||||
:actions="[ |
||||
{ |
||||
icon: IconEnum.EDIT, |
||||
label: t('action.edit'), |
||||
ifShow: record.id !== 0, |
||||
auth: 'infra:data-source-config:update', |
||||
onClick: handleEdit.bind(null, record), |
||||
}, |
||||
{ |
||||
icon: IconEnum.DELETE, |
||||
danger: true, |
||||
label: t('action.delete'), |
||||
ifShow: record.id !== 0, |
||||
auth: 'infra:data-source-config:delete', |
||||
popConfirm: { |
||||
title: t('common.delMessage'), |
||||
placement: 'left', |
||||
confirm: handleDelete.bind(null, record), |
||||
}, |
||||
}, |
||||
]" |
||||
/> |
||||
</template> |
||||
</template> |
||||
</BasicTable> |
||||
<DataSourceConfigModal @register="registerModal" @success="reload()" /> |
||||
</div> |
||||
</template> |
@ -1,53 +0,0 @@
|
||||
<script setup lang="ts" name="InfraDbDoc"> |
||||
import { onMounted, ref } from 'vue' |
||||
import { PageWrapper } from '@/components/Page' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { IFrame } from '@/components/IFrame' |
||||
import * as DbDocApi from '@/api/infra/dbDoc' |
||||
import { downloadByData } from '@/utils/file/download' |
||||
|
||||
const { t } = useI18n() |
||||
const src = ref('') |
||||
/** 页面加载 */ |
||||
async function init() { |
||||
const res = await DbDocApi.exportHtml() |
||||
const blob = new Blob([res], { type: 'text/html' }) |
||||
const blobUrl = window.URL.createObjectURL(blob) |
||||
src.value = blobUrl |
||||
} |
||||
/** 处理导出 */ |
||||
async function handleExport(type: string) { |
||||
if (type === 'HTML') { |
||||
const res = await DbDocApi.exportHtml() |
||||
downloadByData(res, '数据库文档.html') |
||||
} |
||||
if (type === 'Word') { |
||||
const res = await DbDocApi.exportWord() |
||||
downloadByData(res, '数据库文档.doc') |
||||
} |
||||
if (type === 'Markdown') { |
||||
const res = await DbDocApi.exportMarkdown() |
||||
downloadByData(res, '数据库文档.md') |
||||
} |
||||
} |
||||
onMounted(async () => { |
||||
await init() |
||||
}) |
||||
</script> |
||||
|
||||
<template> |
||||
<PageWrapper> |
||||
<div class="mb-3"> |
||||
<a-button type="primary" size="small" class="mr-1" @click="handleExport('HTML')"> |
||||
{{ `${t('action.export')}Html` }} |
||||
</a-button> |
||||
<a-button type="primary" size="small" class="mr-1" @click="handleExport('Word')"> |
||||
{{ `${t('action.export')}Word` }} |
||||
</a-button> |
||||
<a-button type="primary" size="small" @click="handleExport('Markdown')"> |
||||
{{ `${t('action.export')}Markdown` }} |
||||
</a-button> |
||||
</div> |
||||
<IFrame :src="src" /> |
||||
</PageWrapper> |
||||
</template> |
@ -1,12 +0,0 @@
|
||||
<script setup lang="ts" name="InfraDruid"> |
||||
import { ref } from 'vue' |
||||
import { IFrame } from '@/components/IFrame' |
||||
|
||||
const src = ref(`${import.meta.env.VITE_GLOB_BASE_URL}/druid/index.html`) |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<IFrame :src="src" /> |
||||
</div> |
||||
</template> |
@ -1,79 +0,0 @@
|
||||
import type { BasicColumn, FormSchema } from '@/components/Table' |
||||
import { useRender } from '@/components/Table' |
||||
|
||||
export const columns: BasicColumn[] = [ |
||||
{ |
||||
title: '编号', |
||||
dataIndex: 'id', |
||||
width: 100, |
||||
}, |
||||
{ |
||||
title: '文件名', |
||||
dataIndex: 'name', |
||||
width: 200, |
||||
}, |
||||
{ |
||||
title: '文件 URL', |
||||
dataIndex: 'url', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderImg(text) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '文件路径', |
||||
dataIndex: 'path', |
||||
width: 180, |
||||
}, |
||||
{ |
||||
title: '文件大小', |
||||
dataIndex: 'size', |
||||
width: 120, |
||||
customRender: ({ text }) => { |
||||
const unitArr = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] |
||||
const srcSize = Number.parseFloat(text) |
||||
const index = Math.floor(Math.log(srcSize) / Math.log(1024)) |
||||
const size = srcSize / 1024 ** index |
||||
return `${size.toFixed(2)} ${unitArr[index]}` |
||||
}, |
||||
}, |
||||
{ |
||||
title: '文件类型', |
||||
dataIndex: 'type', |
||||
width: 100, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderTag(text) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '文件内容', |
||||
dataIndex: 'content', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderImg(text) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '上传时间', |
||||
dataIndex: 'createTime', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDate(text) |
||||
}, |
||||
}, |
||||
] |
||||
|
||||
export const searchFormSchema: FormSchema[] = [ |
||||
{ |
||||
label: '文件路径', |
||||
field: 'path', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '创建时间', |
||||
field: 'createTime', |
||||
component: 'RangePicker', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
] |
@ -1,92 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { ref } from 'vue' |
||||
import { columns, searchFormSchema } from './file.data' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { IconEnum } from '@/enums/appEnum' |
||||
import { BasicUpload } from '@/components/Upload' |
||||
import { BasicTable, TableAction, useTable } from '@/components/Table' |
||||
import { deleteFile, getFilePage } from '@/api/infra/file' |
||||
import { getAccessToken, getTenantId } from '@/utils/auth' |
||||
import { copyText } from '@/utils/copyTextToClipboard' |
||||
import { uploadApi } from '@/api/base/upload' |
||||
|
||||
defineOptions({ name: 'InfraFile' }) |
||||
|
||||
const { t } = useI18n() |
||||
const { createMessage } = useMessage() |
||||
|
||||
const uploadParams = ref({ |
||||
'Authorization': `Bearer ${getAccessToken()}`, |
||||
'tenant-id': getTenantId(), |
||||
}) |
||||
|
||||
const [registerTable, { reload }] = useTable({ |
||||
title: '文件列表', |
||||
api: getFilePage, |
||||
columns, |
||||
formConfig: { labelWidth: 120, schemas: searchFormSchema }, |
||||
useSearchForm: true, |
||||
showTableSetting: true, |
||||
showIndexColumn: false, |
||||
actionColumn: { |
||||
width: 160, |
||||
title: t('common.action'), |
||||
dataIndex: 'action', |
||||
fixed: 'right', |
||||
}, |
||||
}) |
||||
|
||||
function handleChange() { |
||||
reload() |
||||
} |
||||
|
||||
function handleCopy(record: Recordable) { |
||||
copyText(record.url) |
||||
} |
||||
|
||||
async function handleDelete(record: Recordable) { |
||||
await deleteFile(record.id) |
||||
createMessage.success(t('common.delSuccessText')) |
||||
reload() |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<BasicTable @register="registerTable"> |
||||
<template #toolbar> |
||||
<BasicUpload |
||||
:max-size="20" |
||||
:max-number="10" |
||||
:empty-hide-preview="true" |
||||
:upload-params="uploadParams" |
||||
:api="uploadApi" |
||||
class="my-5" |
||||
:accept="['image/*']" |
||||
@change="handleChange" |
||||
/> |
||||
</template> |
||||
<template #bodyCell="{ column, record }"> |
||||
<template v-if="column.key === 'action'"> |
||||
<TableAction |
||||
:actions="[ |
||||
{ icon: IconEnum.VIEW, label: '复制链接', onClick: handleCopy.bind(null, record) }, |
||||
{ |
||||
icon: IconEnum.DELETE, |
||||
danger: true, |
||||
label: t('action.delete'), |
||||
auth: 'infra:file:delete', |
||||
popConfirm: { |
||||
title: t('common.delMessage'), |
||||
placement: 'left', |
||||
confirm: handleDelete.bind(null, record), |
||||
}, |
||||
}, |
||||
]" |
||||
/> |
||||
</template> |
||||
</template> |
||||
</BasicTable> |
||||
</div> |
||||
</template> |
@ -1,58 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { ref, unref } from 'vue' |
||||
import { formSchema } from './ficleConfig.data' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { BasicForm, useForm } from '@/components/Form' |
||||
import { BasicModal, useModalInner } from '@/components/Modal' |
||||
import { createFileConfig, getFileConfig, updateFileConfig } from '@/api/infra/fileConfig' |
||||
|
||||
defineOptions({ name: 'InfraFileConfigModal' }) |
||||
|
||||
const emit = defineEmits(['success', 'register']) |
||||
const { t } = useI18n() |
||||
const { createMessage } = useMessage() |
||||
const isUpdate = ref(true) |
||||
|
||||
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({ |
||||
labelWidth: 120, |
||||
baseColProps: { span: 24 }, |
||||
schemas: formSchema, |
||||
showActionButtonGroup: false, |
||||
actionColOptions: { span: 23 }, |
||||
}) |
||||
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => { |
||||
resetFields() |
||||
setModalProps({ confirmLoading: false }) |
||||
isUpdate.value = !!data?.isUpdate |
||||
if (unref(isUpdate)) { |
||||
const res = await getFileConfig(data.record.id) |
||||
setFieldsValue({ ...res }) |
||||
} |
||||
}) |
||||
|
||||
async function handleSubmit() { |
||||
try { |
||||
const values = await validate() |
||||
setModalProps({ confirmLoading: true }) |
||||
if (unref(isUpdate)) |
||||
await updateFileConfig(values) |
||||
else |
||||
await createFileConfig(values) |
||||
|
||||
closeModal() |
||||
emit('success') |
||||
createMessage.success(t('common.saveSuccessText')) |
||||
} |
||||
finally { |
||||
setModalProps({ confirmLoading: false }) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<BasicModal v-bind="$attrs" :title="isUpdate ? t('action.edit') : t('action.create')" @register="registerModal" @ok="handleSubmit"> |
||||
<BasicForm @register="registerForm" /> |
||||
</BasicModal> |
||||
</template> |
@ -1,179 +0,0 @@
|
||||
import type { BasicColumn, FormSchema } from '@/components/Table' |
||||
import { useRender } from '@/components/Table' |
||||
import { DICT_TYPE, getDictOptions } from '@/utils/dict' |
||||
|
||||
export const columns: BasicColumn[] = [ |
||||
{ |
||||
title: '编号', |
||||
dataIndex: 'id', |
||||
width: 100, |
||||
}, |
||||
{ |
||||
title: '配置名', |
||||
dataIndex: 'name', |
||||
width: 180, |
||||
}, |
||||
{ |
||||
title: '存储器', |
||||
dataIndex: 'storage', |
||||
width: 100, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDict(text, DICT_TYPE.INFRA_FILE_STORAGE) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '主配置', |
||||
dataIndex: 'master', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDict(text, DICT_TYPE.INFRA_BOOLEAN_STRING) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '备注', |
||||
dataIndex: 'remark', |
||||
width: 180, |
||||
}, |
||||
{ |
||||
title: '创建时间', |
||||
dataIndex: 'createTime', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDate(text) |
||||
}, |
||||
}, |
||||
] |
||||
|
||||
export const searchFormSchema: FormSchema[] = [ |
||||
{ |
||||
label: '配置名', |
||||
field: 'name', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '存储器', |
||||
field: 'storage', |
||||
component: 'Select', |
||||
componentProps: { |
||||
options: getDictOptions(DICT_TYPE.INFRA_FILE_STORAGE) as any, |
||||
}, |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '创建时间', |
||||
field: 'createTime', |
||||
component: 'RangePicker', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
] |
||||
|
||||
export const formSchema: FormSchema[] = [ |
||||
{ |
||||
label: '编号', |
||||
field: 'id', |
||||
show: false, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '配置名', |
||||
field: 'name', |
||||
required: true, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '存储器', |
||||
field: 'storage', |
||||
component: 'Select', |
||||
required: true, |
||||
dynamicDisabled: ({ values }) => !!values.id, |
||||
componentProps: { |
||||
options: getDictOptions(DICT_TYPE.INFRA_FILE_STORAGE) as any, |
||||
}, |
||||
}, |
||||
{ |
||||
label: '基础路径', |
||||
field: 'config.basePath', |
||||
required: true, |
||||
ifShow: ({ values }) => values.storage >= 10 && values.storage <= 12, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '主机地址', |
||||
field: 'config.host', |
||||
required: true, |
||||
ifShow: ({ values }) => values.storage >= 11 && values.storage <= 12, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '主机端口', |
||||
field: 'config.port', |
||||
required: true, |
||||
ifShow: ({ values }) => values.storage >= 11 && values.storage <= 12, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '用户名', |
||||
field: 'config.username', |
||||
required: true, |
||||
ifShow: ({ values }) => values.storage >= 11 && values.storage <= 12, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '密码', |
||||
field: 'config.password', |
||||
required: true, |
||||
ifShow: ({ values }) => values.storage >= 11 && values.storage <= 12, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '连接模式', |
||||
field: 'config.basePath', |
||||
required: true, |
||||
ifShow: ({ values }) => values.storage === 11, |
||||
component: 'Select', |
||||
componentProps: { |
||||
options: [ |
||||
{ lable: '主动模式', key: 'Active', value: 'Active' }, |
||||
{ lable: '被动模式', key: 'Passive', value: 'Passive' }, |
||||
], |
||||
}, |
||||
}, |
||||
{ |
||||
label: '节点地址', |
||||
field: 'config.endpoint', |
||||
required: true, |
||||
ifShow: ({ values }) => values.storage === 20, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '存储 bucket', |
||||
field: 'config.bucket', |
||||
required: true, |
||||
ifShow: ({ values }) => values.storage === 20, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: 'accessKey', |
||||
field: 'config.accessKey', |
||||
ifShow: ({ values }) => values.storage === 20, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: 'accessSecret', |
||||
field: 'config.accessSecret', |
||||
ifShow: ({ values }) => values.storage === 20, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '自定义域名', |
||||
field: 'config.domain', |
||||
required: true, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '备注', |
||||
field: 'remark', |
||||
component: 'InputTextArea', |
||||
}, |
||||
] |
@ -1,107 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import FileConfigModal from './FileConfigModal.vue' |
||||
import { columns, searchFormSchema } from './ficleConfig.data' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { useModal } from '@/components/Modal' |
||||
import { IconEnum } from '@/enums/appEnum' |
||||
import { BasicTable, TableAction, useTable } from '@/components/Table' |
||||
import { deleteFileConfig, getFileConfigPage, testFileConfig, updateFileConfigMaster } from '@/api/infra/fileConfig' |
||||
|
||||
defineOptions({ name: 'InfraFileConfig' }) |
||||
|
||||
const { t } = useI18n() |
||||
const { createConfirm, createMessage, createSuccessModal } = useMessage() |
||||
const [registerModal, { openModal }] = useModal() |
||||
|
||||
const [registerTable, { reload }] = useTable({ |
||||
title: '文件配置列表', |
||||
api: getFileConfigPage, |
||||
columns, |
||||
formConfig: { labelWidth: 120, schemas: searchFormSchema }, |
||||
useSearchForm: true, |
||||
showTableSetting: true, |
||||
showIndexColumn: false, |
||||
actionColumn: { |
||||
width: 280, |
||||
title: t('common.action'), |
||||
dataIndex: 'action', |
||||
fixed: 'right', |
||||
}, |
||||
}) |
||||
|
||||
function handleCreate() { |
||||
openModal(true, { isUpdate: false }) |
||||
} |
||||
|
||||
function handleEdit(record: Recordable) { |
||||
openModal(true, { record, isUpdate: true }) |
||||
} |
||||
|
||||
async function handleTest(record: Recordable) { |
||||
const res = await testFileConfig(record.id) |
||||
createSuccessModal({ content: res }) |
||||
} |
||||
|
||||
function handleMaster(record: Recordable) { |
||||
createConfirm({ |
||||
title: '主配置', |
||||
iconType: 'warning', |
||||
content: `是否确认修改配置编号为"${record.id}"的数据项为主配置?`, |
||||
async onOk() { |
||||
await updateFileConfigMaster(record.id) |
||||
createMessage.success('配置成功') |
||||
reload() |
||||
}, |
||||
}) |
||||
} |
||||
|
||||
async function handleDelete(record: Recordable) { |
||||
await deleteFileConfig(record.id) |
||||
createMessage.success(t('common.delSuccessText')) |
||||
reload() |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<BasicTable @register="registerTable"> |
||||
<template #toolbar> |
||||
<a-button v-auth="['infra:file-config:create']" type="primary" :pre-icon="IconEnum.ADD" @click="handleCreate"> |
||||
{{ t('action.create') }} |
||||
</a-button> |
||||
</template> |
||||
<template #bodyCell="{ column, record }"> |
||||
<template v-if="column.key === 'action'"> |
||||
<TableAction |
||||
:actions="[ |
||||
{ icon: IconEnum.EDIT, label: t('action.edit'), auth: 'infra:file-config:update', onClick: handleEdit.bind(null, record) }, |
||||
{ icon: IconEnum.TEST, label: t('action.test'), auth: 'infra:file-config:update', onClick: handleTest.bind(null, record) }, |
||||
{ |
||||
icon: IconEnum.AUTH, |
||||
label: '主配置', |
||||
auth: 'infra:file-config:update', |
||||
ifShow: () => { |
||||
return !record.master |
||||
}, |
||||
onClick: handleMaster.bind(null, record), |
||||
}, |
||||
{ |
||||
icon: IconEnum.DELETE, |
||||
danger: true, |
||||
label: t('action.delete'), |
||||
auth: 'infra:file-config:delete', |
||||
popConfirm: { |
||||
title: t('common.delMessage'), |
||||
placement: 'left', |
||||
confirm: handleDelete.bind(null, record), |
||||
}, |
||||
}, |
||||
]" |
||||
/> |
||||
</template> |
||||
</template> |
||||
</BasicTable> |
||||
<FileConfigModal @register="registerModal" @success="reload()" /> |
||||
</div> |
||||
</template> |
@ -1,92 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { ref, unref } from 'vue' |
||||
import { Steps } from 'ant-design-vue' |
||||
import { descSchema, formSchema } from './job.data' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { BasicForm, useForm } from '@/components/Form' |
||||
import { BasicModal, useModalInner } from '@/components/Modal' |
||||
import { Description, useDescription } from '@/components/Description' |
||||
import { createJob, getJob, getJobNextTimes, updateJob } from '@/api/infra/job' |
||||
import { formatToDateTime } from '@/utils/dateUtil' |
||||
|
||||
defineOptions({ name: 'InfraJobModal' }) |
||||
|
||||
const emit = defineEmits(['success', 'register']) |
||||
|
||||
const Step = Steps.Step |
||||
|
||||
const { t } = useI18n() |
||||
const { createMessage } = useMessage() |
||||
const isUpdate = ref(true) |
||||
const isEdit = ref(true) |
||||
|
||||
const datas = ref() |
||||
const nextTimes = ref() |
||||
|
||||
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({ |
||||
labelWidth: 120, |
||||
baseColProps: { span: 24 }, |
||||
schemas: formSchema, |
||||
showActionButtonGroup: false, |
||||
actionColOptions: { span: 23 }, |
||||
}) |
||||
|
||||
const [registerDesc] = useDescription({ |
||||
schema: descSchema, |
||||
data: datas, |
||||
}) |
||||
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => { |
||||
isEdit.value = !!data?.isEdit |
||||
setModalProps({ confirmLoading: false }) |
||||
if (data?.isEdit) { |
||||
resetFields() |
||||
|
||||
isUpdate.value = !!data?.isUpdate |
||||
if (unref(isUpdate)) { |
||||
const res = await getJob(data.record.id) |
||||
setFieldsValue({ ...res }) |
||||
} |
||||
} |
||||
else { |
||||
datas.value = await getJob(data.record.id) |
||||
nextTimes.value = await getJobNextTimes(data.record.id) |
||||
} |
||||
}) |
||||
|
||||
async function handleSubmit() { |
||||
try { |
||||
const values = await validate() as any |
||||
setModalProps({ confirmLoading: true }) |
||||
if (unref(isUpdate)) |
||||
await updateJob(values) |
||||
else |
||||
await createJob(values) |
||||
|
||||
closeModal() |
||||
emit('success') |
||||
createMessage.success(t('common.saveSuccessText')) |
||||
} |
||||
finally { |
||||
setModalProps({ confirmLoading: false }) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<BasicModal |
||||
v-bind="$attrs" |
||||
:title="isEdit ? (isUpdate ? t('action.edit') : t('action.create')) : t('action.detail')" |
||||
@register="registerModal" |
||||
@ok="handleSubmit" |
||||
> |
||||
<BasicForm v-if="isEdit" @register="registerForm" /> |
||||
<Description v-if="!isEdit" :column="2" @register="registerDesc" /> |
||||
<Steps v-if="!isEdit" progress-dot :current="nextTimes && nextTimes.length" direction="vertical"> |
||||
<template v-for="(nextTime, index) in nextTimes" :key="index"> |
||||
<Step :title="formatToDateTime(nextTime)" :description="'第' + `${index + 1}` + '次'" /> |
||||
</template> |
||||
</Steps> |
||||
</BasicModal> |
||||
</template> |
@ -1,141 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import JobModal from './JobModal.vue' |
||||
import { columns, searchFormSchema } from './job.data' |
||||
import { useGo } from '@/hooks/web/usePage' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { useModal } from '@/components/Modal' |
||||
import { IconEnum } from '@/enums/appEnum' |
||||
import { BasicTable, TableAction, useTable } from '@/components/Table' |
||||
import type { JobExportReqVO } from '@/api/infra/job' |
||||
import { deleteJob, exportJob, getJobPage, runJob, updateJobStatus } from '@/api/infra/job' |
||||
import { InfraJobStatusEnum } from '@/enums/systemEnum' |
||||
|
||||
defineOptions({ name: 'InfraJob' }) |
||||
|
||||
const go = useGo() |
||||
const { t } = useI18n() |
||||
const { createConfirm, createMessage } = useMessage() |
||||
const [registerModal, { openModal }] = useModal() |
||||
|
||||
const [registerTable, { getForm, reload }] = useTable({ |
||||
title: '定时任务列表', |
||||
api: getJobPage, |
||||
columns, |
||||
formConfig: { labelWidth: 120, schemas: searchFormSchema }, |
||||
useSearchForm: true, |
||||
showTableSetting: true, |
||||
showIndexColumn: false, |
||||
actionColumn: { |
||||
width: 140, |
||||
title: t('common.action'), |
||||
dataIndex: 'action', |
||||
fixed: 'right', |
||||
}, |
||||
}) |
||||
|
||||
function handleCreate() { |
||||
openModal(true, { isEdit: true, isUpdate: false }) |
||||
} |
||||
|
||||
function handleEdit(record: Recordable) { |
||||
openModal(true, { record, isEdit: true, isUpdate: true }) |
||||
} |
||||
|
||||
function handleChangeStatus(record: Recordable, open: boolean) { |
||||
const status = open ? InfraJobStatusEnum.NORMAL : InfraJobStatusEnum.STOP |
||||
const statusStr = open ? '开启' : '关闭' |
||||
createConfirm({ |
||||
title: '调整状态', |
||||
iconType: 'warning', |
||||
content: `是否确认${statusStr}定时任务编号为"${record.id}"的数据项?`, |
||||
async onOk() { |
||||
await updateJobStatus(record.id, status) |
||||
createMessage.success(t('common.successText')) |
||||
reload() |
||||
}, |
||||
}) |
||||
} |
||||
|
||||
function handleRun(record: Recordable) { |
||||
createConfirm({ |
||||
title: '执行', |
||||
iconType: 'warning', |
||||
content: `确认要立即执行一次"${record.name}"任务吗?`, |
||||
async onOk() { |
||||
await runJob(record.id) |
||||
createMessage.success(t('common.successText')) |
||||
}, |
||||
}) |
||||
} |
||||
|
||||
function handleView(record: Recordable) { |
||||
openModal(true, { record, isEdit: false }) |
||||
} |
||||
|
||||
function handleJobLog(record: Recordable) { |
||||
if (record.id > 0) |
||||
go(`/job/job-log?id=${record.id}`) |
||||
else |
||||
go('/job/job-log') |
||||
} |
||||
|
||||
async function handleExport() { |
||||
createConfirm({ |
||||
title: t('common.exportTitle'), |
||||
iconType: 'warning', |
||||
content: t('common.exportMessage'), |
||||
async onOk() { |
||||
await exportJob(getForm().getFieldsValue() as JobExportReqVO) |
||||
createMessage.success(t('common.exportSuccessText')) |
||||
}, |
||||
}) |
||||
} |
||||
|
||||
async function handleDelete(record: Recordable) { |
||||
await deleteJob(record.id) |
||||
createMessage.success(t('common.delSuccessText')) |
||||
reload() |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<BasicTable @register="registerTable"> |
||||
<template #toolbar> |
||||
<a-button v-auth="['infra:job:create']" type="primary" :pre-icon="IconEnum.ADD" @click="handleCreate"> |
||||
{{ t('action.create') }} |
||||
</a-button> |
||||
<a-button v-auth="['infra:job:export']" :pre-icon="IconEnum.EXPORT" @click="handleExport"> |
||||
{{ t('action.export') }} |
||||
</a-button> |
||||
</template> |
||||
<template #bodyCell="{ column, record }"> |
||||
<template v-if="column.key === 'action'"> |
||||
<TableAction |
||||
:actions="[{ icon: IconEnum.EDIT, label: t('action.edit'), onClick: handleEdit.bind(null, record) }]" |
||||
:drop-down-actions="[ |
||||
{ icon: IconEnum.AUTH, label: '开启', auth: 'infra:job:update', onClick: handleChangeStatus.bind(null, record, true) }, |
||||
{ icon: IconEnum.EDIT, label: '暂停', auth: 'infra:job:update', onClick: handleChangeStatus.bind(null, record, false) }, |
||||
{ icon: IconEnum.TEST, label: '执行一次', auth: 'infra:job:trigger', onClick: handleRun.bind(null, record) }, |
||||
{ icon: IconEnum.PREVIEW, label: '任务详细', auth: 'infra:job:query', onClick: handleView.bind(null, record) }, |
||||
{ icon: IconEnum.LOG, label: '调度日志', auth: 'infra:job:query', onClick: handleJobLog.bind(null, record) }, |
||||
{ |
||||
icon: IconEnum.DELETE, |
||||
danger: true, |
||||
label: t('action.delete'), |
||||
auth: 'infra:job:delete', |
||||
popConfirm: { |
||||
title: t('common.delMessage'), |
||||
placement: 'left', |
||||
confirm: handleDelete.bind(null, record), |
||||
}, |
||||
}, |
||||
]" |
||||
/> |
||||
</template> |
||||
</template> |
||||
</BasicTable> |
||||
<JobModal @register="registerModal" @success="reload()" /> |
||||
</div> |
||||
</template> |
@ -1,172 +0,0 @@
|
||||
import type { DescItem } from '@/components/Description' |
||||
import type { BasicColumn, FormSchema } from '@/components/Table' |
||||
import { useRender } from '@/components/Table' |
||||
import { DICT_TYPE, getDictOptions } from '@/utils/dict' |
||||
import { useComponentRegister } from '@/components/Form' |
||||
import { CronTab } from '@/components/CronTab' |
||||
|
||||
useComponentRegister('CronTab', CronTab) |
||||
|
||||
export const columns: BasicColumn[] = [ |
||||
{ |
||||
title: '任务编号', |
||||
dataIndex: 'id', |
||||
width: 100, |
||||
}, |
||||
{ |
||||
title: '任务名称', |
||||
dataIndex: 'name', |
||||
width: 180, |
||||
}, |
||||
{ |
||||
title: '处理器的名字', |
||||
dataIndex: 'handlerName', |
||||
width: 180, |
||||
}, |
||||
{ |
||||
title: '处理器的参数', |
||||
dataIndex: 'handlerParam', |
||||
width: 180, |
||||
}, |
||||
{ |
||||
title: 'CRON 表达式', |
||||
dataIndex: 'cronExpression', |
||||
width: 200, |
||||
}, |
||||
{ |
||||
title: '任务状态', |
||||
dataIndex: 'status', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderDict(text, DICT_TYPE.INFRA_JOB_STATUS) |
||||
}, |
||||
}, |
||||
] |
||||
|
||||
export const searchFormSchema: FormSchema[] = [ |
||||
{ |
||||
label: '任务名称', |
||||
field: 'name', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '任务状态', |
||||
field: 'status', |
||||
component: 'Select', |
||||
componentProps: { |
||||
options: getDictOptions(DICT_TYPE.INFRA_JOB_STATUS) as any, |
||||
}, |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '处理器的名字', |
||||
field: 'handlerName', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
] |
||||
|
||||
export const formSchema: FormSchema[] = [ |
||||
{ |
||||
label: '任务编号', |
||||
field: 'id', |
||||
show: false, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '任务名称', |
||||
field: 'name', |
||||
required: true, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '处理器的名字', |
||||
field: 'handlerName', |
||||
required: true, |
||||
dynamicDisabled: ({ values }) => !!values.id, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '处理器的参数', |
||||
field: 'handlerParam', |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: 'CRON 表达式', |
||||
field: 'cronExpression', |
||||
required: true, |
||||
component: 'CronTab', |
||||
}, |
||||
{ |
||||
label: '重试次数', |
||||
field: 'retryCount', |
||||
required: true, |
||||
helpMessage: '设置为 0 时,不进行重试', |
||||
defaultValue: 0, |
||||
component: 'InputNumber', |
||||
}, |
||||
{ |
||||
label: '重试间隔', |
||||
field: 'retryInterval', |
||||
required: true, |
||||
helpMessage: '单位:毫秒。设置为 0 时,无需间隔', |
||||
defaultValue: 0, |
||||
component: 'InputNumber', |
||||
suffix: '毫秒', |
||||
}, |
||||
{ |
||||
label: '监控超时时间', |
||||
field: 'monitorTimeout', |
||||
component: 'Input', |
||||
suffix: '毫秒', |
||||
}, |
||||
] |
||||
|
||||
export const descSchema: DescItem[] = [ |
||||
{ |
||||
label: '任务编号', |
||||
field: 'id', |
||||
}, |
||||
{ |
||||
label: '任务名称', |
||||
field: 'name', |
||||
}, |
||||
{ |
||||
label: '任务状态', |
||||
field: 'status', |
||||
render: (curVal) => { |
||||
return useRender.renderDict(curVal, DICT_TYPE.INFRA_JOB_STATUS) |
||||
}, |
||||
}, |
||||
{ |
||||
label: '处理器的名字', |
||||
field: 'handlerName', |
||||
}, |
||||
{ |
||||
label: '处理器的参数', |
||||
field: 'handlerParam', |
||||
}, |
||||
{ |
||||
label: 'Cron 表达式', |
||||
field: 'cronExpression', |
||||
}, |
||||
{ |
||||
label: '重试次数', |
||||
field: 'retryCount', |
||||
}, |
||||
{ |
||||
label: '重试间隔', |
||||
field: 'cronExpression', |
||||
render: (curVal) => { |
||||
return useRender.renderText(curVal, ' 毫秒') |
||||
}, |
||||
}, |
||||
{ |
||||
label: '监控超时时间', |
||||
field: 'monitorTimeout', |
||||
render: (curVal) => { |
||||
return curVal > 0 ? `${curVal} 毫秒` : '未开启' |
||||
}, |
||||
}, |
||||
] |
@ -1,28 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { ref } from 'vue' |
||||
import { descSchema } from './jobLog.data' |
||||
import { Description, useDescription } from '@/components/Description' |
||||
import { BasicModal, useModalInner } from '@/components/Modal' |
||||
import { getJobLog } from '@/api/infra/jobLog' |
||||
|
||||
defineOptions({ name: 'InfraJobLogModal' }) |
||||
|
||||
const datas = ref() |
||||
|
||||
const [registerDesc] = useDescription({ |
||||
schema: descSchema, |
||||
data: datas, |
||||
}) |
||||
|
||||
const [registerModal, { setModalProps }] = useModalInner(async (data) => { |
||||
setModalProps({ confirmLoading: false }) |
||||
const res = await getJobLog(data.record.id) |
||||
datas.value = res |
||||
}) |
||||
</script> |
||||
|
||||
<template> |
||||
<BasicModal v-bind="$attrs" title="查看详情" @register="registerModal"> |
||||
<Description :column="2" @register="registerDesc" /> |
||||
</BasicModal> |
||||
</template> |
@ -1,70 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { useRoute } from 'vue-router' |
||||
import JobLogModal from './JobLogModal.vue' |
||||
import { columns, searchFormSchema } from './jobLog.data' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { useModal } from '@/components/Modal' |
||||
import { IconEnum } from '@/enums/appEnum' |
||||
import { BasicTable, TableAction, useTable } from '@/components/Table' |
||||
import type { JobLogExportReqVO } from '@/api/infra/jobLog' |
||||
import { exportJobLog, getJobLogPage } from '@/api/infra/jobLog' |
||||
|
||||
defineOptions({ name: 'InfraJobLog' }) |
||||
|
||||
const { t } = useI18n() |
||||
const { query } = useRoute() |
||||
const { createConfirm, createMessage } = useMessage() |
||||
const [registerModal, { openModal }] = useModal() |
||||
|
||||
const [registerTable, { getForm, reload }] = useTable({ |
||||
title: '定时任务日志列表', |
||||
api: getJobLogPage, |
||||
searchInfo: { id: query.id as unknown as number }, |
||||
columns, |
||||
formConfig: { labelWidth: 120, schemas: searchFormSchema }, |
||||
useSearchForm: true, |
||||
showTableSetting: true, |
||||
showIndexColumn: false, |
||||
actionColumn: { |
||||
width: 140, |
||||
title: t('common.action'), |
||||
dataIndex: 'action', |
||||
fixed: 'right', |
||||
}, |
||||
}) |
||||
|
||||
function handleDetail(record: Recordable) { |
||||
openModal(true, { record }) |
||||
} |
||||
|
||||
async function handleExport() { |
||||
createConfirm({ |
||||
title: t('common.exportTitle'), |
||||
iconType: 'warning', |
||||
content: t('common.exportMessage'), |
||||
async onOk() { |
||||
await exportJobLog(getForm().getFieldsValue() as JobLogExportReqVO) |
||||
createMessage.success(t('common.exportSuccessText')) |
||||
}, |
||||
}) |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<BasicTable @register="registerTable"> |
||||
<template #toolbar> |
||||
<a-button v-auth="['infra:job:export']" :pre-icon="IconEnum.EXPORT" @click="handleExport"> |
||||
{{ t('action.export') }} |
||||
</a-button> |
||||
</template> |
||||
<template #bodyCell="{ column, record }"> |
||||
<template v-if="column.key === 'action'"> |
||||
<TableAction :actions="[{ icon: IconEnum.EDIT, label: t('action.detail'), onClick: handleDetail.bind(null, record) }]" /> |
||||
</template> |
||||
</template> |
||||
</BasicTable> |
||||
<JobLogModal @register="registerModal" @success="reload()" /> |
||||
</div> |
||||
</template> |
@ -1,150 +0,0 @@
|
||||
import { h } from 'vue' |
||||
import type { DescItem } from '@/components/Description' |
||||
import type { BasicColumn, FormSchema } from '@/components/Table' |
||||
import { useRender } from '@/components/Table' |
||||
import { DICT_TYPE, getDictOptions } from '@/utils/dict' |
||||
import { JsonPreview } from '@/components/CodeEditor' |
||||
|
||||
export const columns: BasicColumn[] = [ |
||||
{ |
||||
title: '日志编号', |
||||
dataIndex: 'id', |
||||
width: 100, |
||||
}, |
||||
{ |
||||
title: '任务编号', |
||||
dataIndex: 'jobId', |
||||
width: 100, |
||||
}, |
||||
{ |
||||
title: '处理器的名字', |
||||
dataIndex: 'handlerName', |
||||
width: 180, |
||||
}, |
||||
{ |
||||
title: '处理器的参数', |
||||
dataIndex: 'handlerParam', |
||||
width: 180, |
||||
}, |
||||
{ |
||||
title: '第几次执行', |
||||
dataIndex: 'executeIndex', |
||||
width: 100, |
||||
}, |
||||
{ |
||||
title: '执行时间', |
||||
dataIndex: 'beginTime', |
||||
width: 180, |
||||
customRender: ({ record }) => { |
||||
const startTime = useRender.renderDate(record.beginTime) |
||||
const endTime = useRender.renderDate(record.endTime) |
||||
return useRender.renderTags([startTime, endTime]) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '执行时长', |
||||
dataIndex: 'duration', |
||||
width: 180, |
||||
customRender: ({ text }) => { |
||||
return useRender.renderText(text, ' 毫秒') |
||||
}, |
||||
}, |
||||
{ |
||||
title: '任务状态', |
||||
dataIndex: 'status', |
||||
width: 180, |
||||
customRender: ({ record }) => { |
||||
return useRender.renderDict(record.status, DICT_TYPE.INFRA_JOB_LOG_STATUS) |
||||
}, |
||||
}, |
||||
] |
||||
|
||||
export const searchFormSchema: FormSchema[] = [ |
||||
{ |
||||
label: '处理器的名字', |
||||
field: 'handlerName', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '开始执行时间', |
||||
field: 'beginTime', |
||||
component: 'DatePicker', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '结束执行时间', |
||||
field: 'endTime', |
||||
component: 'DatePicker', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '任务状态', |
||||
field: 'status', |
||||
component: 'Select', |
||||
componentProps: { |
||||
options: getDictOptions(DICT_TYPE.INFRA_JOB_STATUS) as any, |
||||
}, |
||||
colProps: { span: 8 }, |
||||
}, |
||||
{ |
||||
label: '处理器的名字', |
||||
field: 'handlerName', |
||||
component: 'Input', |
||||
colProps: { span: 8 }, |
||||
}, |
||||
] |
||||
|
||||
export const descSchema: DescItem[] = [ |
||||
{ |
||||
label: '日志编号', |
||||
field: 'id', |
||||
}, |
||||
{ |
||||
label: '任务编号', |
||||
field: 'jobId', |
||||
}, |
||||
{ |
||||
label: '处理器的名字', |
||||
field: 'handlerName', |
||||
}, |
||||
{ |
||||
label: '处理器的参数', |
||||
field: 'handlerParam', |
||||
}, |
||||
{ |
||||
label: '第几次执行', |
||||
field: 'executeIndex', |
||||
}, |
||||
{ |
||||
label: '执行时间', |
||||
field: 'beginTime', |
||||
render: (_, data) => { |
||||
const startTime = `开始: ${useRender.renderDate(data.beginTime)}` |
||||
const endTime = `结束: ${useRender.renderDate(data.endTime)}` |
||||
return h('span', {}, [startTime, h('br'), endTime]) |
||||
}, |
||||
}, |
||||
{ |
||||
label: '执行时长', |
||||
field: 'duration', |
||||
render: (curVal) => { |
||||
return useRender.renderText(curVal, ' 毫秒') |
||||
}, |
||||
}, |
||||
{ |
||||
label: '任务状态', |
||||
field: 'status', |
||||
render: (curVal) => { |
||||
return useRender.renderDict(curVal, DICT_TYPE.INFRA_JOB_LOG_STATUS) |
||||
}, |
||||
}, |
||||
{ |
||||
label: '执行结果', |
||||
field: 'result', |
||||
render: (curVal) => { |
||||
const data = JSON.parse(curVal) |
||||
return h(JsonPreview, { data }) |
||||
}, |
||||
}, |
||||
] |
@ -1,53 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import type { Ref } from 'vue' |
||||
import { ref, watch } from 'vue' |
||||
import { Card } from 'ant-design-vue' |
||||
import { useECharts } from '@/hooks/web/useECharts' |
||||
import { propTypes } from '@/utils/propTypes' |
||||
|
||||
const props = defineProps({ |
||||
loading: propTypes.bool.def(true), |
||||
commandStats: propTypes.array.def([]), |
||||
width: propTypes.string.def('100%'), |
||||
height: propTypes.string.def('300px'), |
||||
}) |
||||
|
||||
const chartRef = ref<HTMLDivElement | null>(null) |
||||
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>) |
||||
|
||||
const optionsData = ref<any[]>(props.commandStats) |
||||
|
||||
watch( |
||||
() => props.loading, |
||||
() => { |
||||
if (props.loading) |
||||
return |
||||
|
||||
setOptions({ |
||||
tooltip: { |
||||
trigger: 'item', |
||||
formatter: '{a} <br/>{b} : {c} ({d}%)', |
||||
}, |
||||
series: [ |
||||
{ |
||||
name: '命令', |
||||
type: 'pie', |
||||
roseType: 'radius', |
||||
radius: [15, 95], |
||||
center: ['50%', '38%'], |
||||
data: optionsData.value, |
||||
animationEasing: 'cubicInOut', |
||||
animationDuration: 1000, |
||||
}, |
||||
], |
||||
}) |
||||
}, |
||||
{ immediate: true }, |
||||
) |
||||
</script> |
||||
|
||||
<template> |
||||
<Card title="命令统计" :loading="loading"> |
||||
<div ref="chartRef" :style="{ width, height }" /> |
||||
</Card> |
||||
</template> |
@ -1,55 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import type { Ref } from 'vue' |
||||
import { ref, watch } from 'vue' |
||||
import { Card } from 'ant-design-vue' |
||||
import { useECharts } from '@/hooks/web/useECharts' |
||||
import { propTypes } from '@/utils/propTypes' |
||||
|
||||
const props = defineProps({ |
||||
loading: propTypes.bool.def(true), |
||||
memoryHuman: propTypes.string.def('0'), |
||||
width: propTypes.string.def('100%'), |
||||
height: propTypes.string.def('300px'), |
||||
}) |
||||
|
||||
const chartRef = ref<HTMLDivElement | null>(null) |
||||
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>) |
||||
|
||||
watch( |
||||
() => props.loading, |
||||
() => { |
||||
if (props.loading) |
||||
return |
||||
|
||||
setOptions({ |
||||
tooltip: { |
||||
formatter: `{b} <br/>{a} : ${props.memoryHuman}`, |
||||
}, |
||||
series: [ |
||||
{ |
||||
name: '峰值', |
||||
type: 'gauge', |
||||
min: 0, |
||||
max: 100, |
||||
detail: { |
||||
formatter: props.memoryHuman, |
||||
}, |
||||
data: [ |
||||
{ |
||||
value: Number.parseFloat(props.memoryHuman), |
||||
name: '内存消耗', |
||||
}, |
||||
], |
||||
}, |
||||
], |
||||
}) |
||||
}, |
||||
{ immediate: true }, |
||||
) |
||||
</script> |
||||
|
||||
<template> |
||||
<Card title="内存信息" :loading="loading"> |
||||
<div ref="chartRef" :style="{ width, height }" /> |
||||
</Card> |
||||
</template> |
@ -1,46 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { onMounted, ref } from 'vue' |
||||
import { baseInfoSchema } from './redis.data' |
||||
import { Description } from '@/components/Description' |
||||
import { getCache } from '@/api/infra/redis' |
||||
import { createAsyncComponent } from '@/utils/factory/createAsyncComponent' |
||||
|
||||
defineOptions({ name: 'InfraRedis' }) |
||||
const CommandStats = createAsyncComponent(() => import('./components/CommandStats.vue')) |
||||
const Memory = createAsyncComponent(() => import('./components/Memory.vue')) |
||||
|
||||
const loading = ref(true) |
||||
const cacheInfo = ref<any>() |
||||
const commandStats = ref<any[]>([]) |
||||
const memoryHuman = ref<any>() |
||||
|
||||
async function getList() { |
||||
const res = await getCache() |
||||
cacheInfo.value = res.info |
||||
memoryHuman.value = res.info.used_memory_human |
||||
await res.commandStats.forEach((val) => { |
||||
commandStats.value.push({ name: val.command, value: val.calls }) |
||||
}) |
||||
loading.value = false |
||||
} |
||||
|
||||
onMounted(async () => { |
||||
await getList() |
||||
}) |
||||
</script> |
||||
|
||||
<template> |
||||
<div class="p-4"> |
||||
<Description |
||||
title="基础信息" |
||||
:collapse-options="{ canExpand: true, helpMessage: 'Redis 基本信息' }" |
||||
:column="6" |
||||
:data="cacheInfo" |
||||
:schema="baseInfoSchema" |
||||
/> |
||||
<div class="enter-y mt-4 md:flex"> |
||||
<CommandStats class="w-full md:w-1/2" :loading="loading" :command-stats="commandStats" /> |
||||
<Memory class="w-full !my-4 md:w-1/2 !md:mx-4 !md:my-0" :loading="loading" :memory-human="memoryHuman" /> |
||||
</div> |
||||
</div> |
||||
</template> |
@ -1,64 +0,0 @@
|
||||
import type { DescItem } from '@/components/Description' |
||||
|
||||
export const baseInfoSchema: DescItem[] = [ |
||||
{ |
||||
label: 'Redis版本', |
||||
field: 'redis_version', |
||||
}, |
||||
{ |
||||
label: '运行模式', |
||||
field: 'redis_mode', |
||||
render: (val) => { |
||||
return val === 'standalone' ? '单机' : '集群' |
||||
}, |
||||
}, |
||||
{ |
||||
label: '端口', |
||||
field: 'tcp_port', |
||||
}, |
||||
{ |
||||
label: '客户端数', |
||||
field: 'connected_clients', |
||||
}, |
||||
{ |
||||
label: '运行时间(天)', |
||||
field: 'uptime_in_days', |
||||
}, |
||||
{ |
||||
label: '使用内存', |
||||
field: 'used_memory_human', |
||||
}, |
||||
{ |
||||
label: '使用CPU', |
||||
field: 'tcp_port', |
||||
render: (val) => { |
||||
return Number.parseFloat(val).toFixed(2) |
||||
}, |
||||
}, |
||||
{ |
||||
label: '内存配置', |
||||
field: 'maxmemory_human', |
||||
}, |
||||
{ |
||||
label: 'AOF是否开启', |
||||
field: 'maxmemory_human', |
||||
render: (val) => { |
||||
return val === '0' ? '否' : '是' |
||||
}, |
||||
}, |
||||
{ |
||||
label: 'RDB是否成功', |
||||
field: 'rdb_last_bgsave_status', |
||||
}, |
||||
{ |
||||
label: 'Key数量', |
||||
field: 'expired_keys', |
||||
}, |
||||
{ |
||||
label: '网络入口/出口', |
||||
field: 'instantaneous_input_kbps', |
||||
render: (_val, data) => { |
||||
return `${data.instantaneous_input_kbps}kps / ${data.instantaneous_output_kbps}kps` |
||||
}, |
||||
}, |
||||
] |
@ -1,29 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { onMounted, ref } from 'vue' |
||||
import { IFrame } from '@/components/IFrame' |
||||
import { getConfigKey } from '@/api/infra/config' |
||||
|
||||
defineOptions({ name: 'InfraAdminServer' }) |
||||
|
||||
const src = ref(`${import.meta.env.VITE_GLOB_BASE_URL}/admin/applications`) |
||||
|
||||
const loading = ref(true) |
||||
|
||||
async function getInfo() { |
||||
const res = await getConfigKey('url.spring-boot-admin') |
||||
if (res && res.length !== 0) |
||||
src.value = res |
||||
|
||||
loading.value = false |
||||
} |
||||
|
||||
onMounted(() => { |
||||
getInfo() |
||||
}) |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<IFrame v-if="!loading" :src="src" /> |
||||
</div> |
||||
</template> |
@ -1,14 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { ref } from 'vue' |
||||
import { IFrame } from '@/components/IFrame' |
||||
|
||||
defineOptions({ name: 'InfraSkywalking' }) |
||||
|
||||
const src = ref('http://skywalking.shop.iocoder.cn') |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<IFrame :src="src" /> |
||||
</div> |
||||
</template> |
@ -1,16 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { ref } from 'vue' |
||||
import { IFrame } from '@/components/IFrame' |
||||
|
||||
defineOptions({ name: 'InfraSwagger' }) |
||||
|
||||
// knife4j |
||||
// const src = ref(import.meta.env.VITE_GLOB_BASE_URL + '/doc.html') |
||||
const src = ref(`${import.meta.env.VITE_GLOB_BASE_URL}/swagger-ui`) |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<IFrame :src="src" /> |
||||
</div> |
||||
</template> |
@ -1,3 +0,0 @@
|
||||
<template> |
||||
<div>开发中 testDemo</div> |
||||
</template> |
@ -1,112 +0,0 @@
|
||||
<script lang="ts" setup> |
||||
import { computed, reactive, ref, watchEffect } from 'vue' |
||||
import { Input, Tag } from 'ant-design-vue' |
||||
import { useWebSocket } from '@vueuse/core' |
||||
import { PageWrapper } from '@/components/Page' |
||||
import { formatToDateTime } from '@/utils/dateUtil' |
||||
import { useUserStore } from '@/store/modules/user' |
||||
|
||||
defineOptions({ name: 'InfraWebSocket' }) |
||||
|
||||
const InputTextArea = Input.TextArea |
||||
|
||||
const userStore = useUserStore() |
||||
|
||||
const state = reactive({ |
||||
sendValue: '', |
||||
recordList: [] as { id: number, time: number, res: string }[], |
||||
}) |
||||
const server = ref( |
||||
`${(`${import.meta.env.VITE_GLOB_BASE_URL}/websocket/message`).replace('http', 'ws')}?userId=${userStore.getUserInfo.user.id}`, |
||||
) |
||||
const { status, data, send, close, open } = useWebSocket(server.value, { |
||||
autoReconnect: false, |
||||
heartbeat: true, |
||||
}) |
||||
watchEffect(() => { |
||||
if (data.value) { |
||||
try { |
||||
const res = JSON.parse(data.value) |
||||
state.recordList.push(res) |
||||
} |
||||
catch (error) { |
||||
state.recordList.push({ |
||||
res: data.value, |
||||
id: Math.ceil(Math.random() * 1000), |
||||
time: new Date().getTime(), |
||||
}) |
||||
} |
||||
} |
||||
}) |
||||
const getIsOpen = computed(() => status.value === 'OPEN') |
||||
const getTagColor = computed(() => (getIsOpen.value ? 'success' : 'red')) |
||||
const getList = computed(() => { |
||||
return [...state.recordList].reverse() |
||||
}) |
||||
function handlerSend() { |
||||
send(state.sendValue) |
||||
state.sendValue = '' |
||||
} |
||||
function toggle() { |
||||
if (getIsOpen.value) |
||||
close() |
||||
else |
||||
open() |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<PageWrapper title="WebSocket 示例"> |
||||
<div class="flex"> |
||||
<div class="w-1/3 p-4"> |
||||
<div class="flex items-center"> |
||||
<span class="mr-4 text-lg font-medium"> 连接状态: </span> |
||||
<Tag :color="getTagColor"> |
||||
{{ status }} |
||||
</Tag> |
||||
</div> |
||||
<hr class="my-4"> |
||||
|
||||
<div class="flex"> |
||||
<Input v-model:value="server" disabled> |
||||
<template #addonBefore> |
||||
服务地址 |
||||
</template> |
||||
</Input> |
||||
<a-button :type="getIsOpen ? 'danger' : 'primary'" @click="toggle"> |
||||
{{ getIsOpen ? '关闭连接' : '开启连接' }} |
||||
</a-button> |
||||
</div> |
||||
<p class="mt-4 text-lg font-medium"> |
||||
设置 |
||||
</p> |
||||
<hr class="my-4"> |
||||
|
||||
<InputTextArea v-model:value="state.sendValue" placeholder="需要发送到服务器的内容" :disabled="!getIsOpen" allow-clear /> |
||||
|
||||
<a-button type="primary" block class="mt-4" :disabled="!getIsOpen" @click="handlerSend"> |
||||
发送 |
||||
</a-button> |
||||
</div> |
||||
|
||||
<div class="ml-4 w-2/3 p-4"> |
||||
<span class="mr-4 text-lg font-medium"> 消息记录: </span> |
||||
<hr class="my-4"> |
||||
|
||||
<div class="max-h-80 overflow-auto"> |
||||
<ul> |
||||
<li v-for="item in getList" :key="item.time" class="mt-2"> |
||||
<div class="flex items-center"> |
||||
<span class="mr-2 font-medium text-primary">收到消息:</span> |
||||
<span>{{ formatToDateTime(item.time) }}</span> |
||||
</div> |
||||
<div> |
||||
{{ item.res }} |
||||
</div> |
||||
</li> |
||||
</ul> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</PageWrapper> |
||||
</template> |
@ -1,3 +0,0 @@
|
||||
<template> |
||||
<div>开发中</div> |
||||
</template> |
@ -1,3 +0,0 @@
|
||||
<template> |
||||
<div>开发中</div> |
||||
</template> |
@ -1,3 +0,0 @@
|
||||
<template> |
||||
<div>开发中</div> |
||||
</template> |
@ -1,3 +0,0 @@
|
||||
<template> |
||||
<div>开发中</div> |
||||
</template> |
@ -1,3 +0,0 @@
|
||||
<template> |
||||
<div>开发中</div> |
||||
</template> |
@ -1,3 +0,0 @@
|
||||
<template> |
||||
<div>开发中</div> |
||||
</template> |
@ -1,3 +0,0 @@
|
||||
<template> |
||||
<div>开发中</div> |
||||
</template> |
Some files were not shown because too many files have changed in this diff Show More
Reference in new issue