feat: 机器脚本新增分配、组件属性类型不匹配警告调整

This commit is contained in:
meilin.huang
2025-06-16 20:13:03 +08:00
parent cc66fcddf5
commit 7eb4d064ea
35 changed files with 162 additions and 64 deletions

View File

@@ -11,8 +11,8 @@
}, },
"dependencies": { "dependencies": {
"@element-plus/icons-vue": "^2.3.1", "@element-plus/icons-vue": "^2.3.1",
"@logicflow/core": "^2.0.15", "@logicflow/core": "^2.0.16",
"@logicflow/extension": "^2.0.20", "@logicflow/extension": "^2.0.21",
"@vueuse/core": "^13.3.0", "@vueuse/core": "^13.3.0",
"@xterm/addon-fit": "^0.10.0", "@xterm/addon-fit": "^0.10.0",
"@xterm/addon-search": "^0.15.0", "@xterm/addon-search": "^0.15.0",
@@ -24,7 +24,7 @@
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"echarts": "^5.6.0", "echarts": "^5.6.0",
"element-plus": "^2.10.1", "element-plus": "^2.10.2",
"js-base64": "^3.7.7", "js-base64": "^3.7.7",
"jsencrypt": "^3.3.2", "jsencrypt": "^3.3.2",
"mitt": "^3.0.1", "mitt": "^3.0.1",
@@ -45,7 +45,7 @@
"vuedraggable": "^4.1.0" "vuedraggable": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/vite": "^4.1.6", "@tailwindcss/vite": "^4.1.9",
"@types/crypto-js": "^4.2.2", "@types/crypto-js": "^4.2.2",
"@types/node": "^18.14.0", "@types/node": "^18.14.0",
"@types/nprogress": "^0.2.0", "@types/nprogress": "^0.2.0",
@@ -58,13 +58,13 @@
"code-inspector-plugin": "^0.20.9", "code-inspector-plugin": "^0.20.9",
"dotenv": "^16.3.1", "dotenv": "^16.3.1",
"eslint": "^9.27.0", "eslint": "^9.27.0",
"eslint-plugin-vue": "^10.1.0", "eslint-plugin-vue": "^10.2.0",
"postcss": "^8.5.4", "postcss": "^8.5.4",
"prettier": "^3.5.3", "prettier": "^3.5.3",
"sass": "^1.89.1", "sass": "^1.89.2",
"tailwindcss": "^4.1.8", "tailwindcss": "^4.1.9",
"typescript": "^5.8.2", "typescript": "^5.8.2",
"vite": "^6.3.5", "vite": "npm:rolldown-vite@latest",
"vite-plugin-progress": "0.0.7", "vite-plugin-progress": "0.0.7",
"vue-eslint-parser": "^10.1.3" "vue-eslint-parser": "^10.1.3"
}, },

View File

@@ -15,7 +15,7 @@ const config = {
baseWsUrl: `${(window as any).globalConfig.BaseWsUrl || `${location.protocol == 'https:' ? 'wss:' : 'ws:'}//${getBaseApiUrl()}`}/api`, baseWsUrl: `${(window as any).globalConfig.BaseWsUrl || `${location.protocol == 'https:' ? 'wss:' : 'ws:'}//${getBaseApiUrl()}`}/api`,
// 系统版本 // 系统版本
version: 'v1.10.0', version: 'v1.10.1',
}; };
export default config; export default config;

View File

@@ -42,4 +42,5 @@ export function exportFile(filename: string, content: string) {
link.setAttribute('download', `${filename}`); link.setAttribute('download', `${filename}`);
document.body.appendChild(link); document.body.appendChild(link);
link.click(); link.click();
document.body.removeChild(link); // 下载完成后移除元素
} }

View File

@@ -12,8 +12,9 @@ const props = defineProps({
required: true, required: true,
}, },
value: { value: {
type: [Object, String, Number], type: [Object, String, Number, null],
required: true, required: true,
default: () => null,
}, },
}); });
@@ -40,7 +41,7 @@ onMounted(() => {
}); });
const convert = (value: any) => { const convert = (value: any) => {
const enumValue = EnumValue.getEnumByValue(props.enums, value) as any; const enumValue = EnumValue.getEnumByValue(props.enums, value);
if (!enumValue) { if (!enumValue) {
state.enumLabel = '-'; state.enumLabel = '-';
state.type = 'danger'; state.type = 'danger';
@@ -50,8 +51,8 @@ const convert = (value: any) => {
state.enumLabel = enumValue?.label || ''; state.enumLabel = enumValue?.label || '';
if (enumValue.tag) { if (enumValue.tag) {
state.color = enumValue.tag.color; state.color = enumValue.tag.color || '';
state.type = enumValue.tag.type; state.type = enumValue.tag.type || defaultType;
} else { } else {
state.type = defaultType; state.type = defaultType;
} }

View File

@@ -68,7 +68,7 @@
trigger="click" trigger="click"
> >
<div v-for="(item, index) in tableColumns" :key="index"> <div v-for="(item, index) in tableColumns" :key="index">
<el-checkbox v-model="item.show" :label="$t(item.label)" :true-value="true" :false-value="false" /> <el-checkbox v-model="item.show" :label="$t(item.label)" :true-value="1" :false-value="0" />
</div> </div>
<template #reference> <template #reference>
<el-button icon="Operation" circle :size="props.size"></el-button> <el-button icon="Operation" circle :size="props.size"></el-button>

View File

@@ -71,9 +71,9 @@ export class TableColumn {
formatFunc: Function; formatFunc: Function;
/** /**
* 是否显示该列 * 是否显示该列,1显示 0不显示
*/ */
show: boolean = true; show: number = 1;
/** /**
* 是否展示美化按钮主要用于美化json文本等 * 是否展示美化按钮主要用于美化json文本等

View File

@@ -87,6 +87,8 @@ export default {
scriptResultEnumRealTime: 'Real-time', scriptResultEnumRealTime: 'Real-time',
scriptTypeEnumPrivate: 'Private', scriptTypeEnumPrivate: 'Private',
scriptTypeEnumPublic: 'Public', scriptTypeEnumPublic: 'Public',
category: 'Category',
categoryTips: 'support input new category and selection',
// security // security
cmdConfig: 'Command Config', cmdConfig: 'Command Config',

View File

@@ -27,7 +27,6 @@ export default {
menuCodePlaceholder: `A menu that does not begin with '/' will automatically concatenate the parent menu path`, menuCodePlaceholder: `A menu that does not begin with '/' will automatically concatenate the parent menu path`,
routerNameTips: routerNameTips:
'For component caching to work, the key for route.ts in the frontend module should match the vue component name, such as ResourceList', 'For component caching to work, the key for route.ts in the frontend module should match the vue component name, such as ResourceList',
componentPathTips: 'Access path components, such as: ` system/resource/ResourceList `, default in ` views ` directory',
isCacheTips: `If yes is selected, it will be 'keepalive' cached (reentering the page without refreshing the page and requesting data again), and needs the route name to match the vue component name`, isCacheTips: `If yes is selected, it will be 'keepalive' cached (reentering the page without refreshing the page and requesting data again), and needs the route name to match the vue component name`,
isHideTips: isHideTips:
'Select Hide and the route will not appear in the menu bar, but it will still be accessible. Disabled will not be able to access and operate', 'Select Hide and the route will not appear in the menu bar, but it will still be accessible. Disabled will not be able to access and operate',

View File

@@ -88,6 +88,8 @@ export default {
scriptResultEnumRealTime: '实时交互', scriptResultEnumRealTime: '实时交互',
scriptTypeEnumPrivate: '私有', scriptTypeEnumPrivate: '私有',
scriptTypeEnumPublic: '公共', scriptTypeEnumPublic: '公共',
category: '分类',
categoryTips: '支持输入新分类并选择',
// security // security
cmdConfig: '命令配置', cmdConfig: '命令配置',

View File

@@ -26,7 +26,6 @@ export default {
menuCodeTips: `菜单类型则为访问路径(若菜单路径不以'/'开头则访问地址会自动拼接父菜单路径)、否则为资源唯一编码`, menuCodeTips: `菜单类型则为访问路径(若菜单路径不以'/'开头则访问地址会自动拼接父菜单路径)、否则为资源唯一编码`,
menuCodePlaceholder: `菜单不以'/'开头则自动拼接父菜单路径`, menuCodePlaceholder: `菜单不以'/'开头则自动拼接父菜单路径`,
routerNameTips: '前端模块下route.ts中对应的key与vue的组件名一致才可使组件缓存生效如ResourceList', routerNameTips: '前端模块下route.ts中对应的key与vue的组件名一致才可使组件缓存生效如ResourceList',
componentPathTips: '访问的组件路径,如:`system/resource/ResourceList`,默认在`views`目录下',
isCacheTips: '选择是则会被`keep-alive`缓存(重新进入页面不会刷新页面及重新请求数据)需要路由名与vue的组件名一致', isCacheTips: '选择是则会被`keep-alive`缓存(重新进入页面不会刷新页面及重新请求数据)需要路由名与vue的组件名一致',
isHideTips: '选择隐藏则路由将不会出现在菜单栏中,但仍然可以访问。禁用则不可访问与操作', isHideTips: '选择隐藏则路由将不会出现在菜单栏中,但仍然可以访问。禁用则不可访问与操作',
externalLinkTips: '内嵌: 以iframe展示、外链: 新标签打开', externalLinkTips: '内嵌: 以iframe展示、外链: 新标签打开',

View File

@@ -2,7 +2,7 @@
<div class="layout-navbars-breadcrumb" v-show="themeConfig.isBreadcrumb"> <div class="layout-navbars-breadcrumb" v-show="themeConfig.isBreadcrumb">
<SvgIcon class="layout-navbars-breadcrumb-icon" :name="themeConfig.isCollapse ? 'expand' : 'fold'" @click="onThemeConfigChange" /> <SvgIcon class="layout-navbars-breadcrumb-icon" :name="themeConfig.isCollapse ? 'expand' : 'fold'" @click="onThemeConfigChange" />
<el-breadcrumb class="layout-navbars-breadcrumb-hide"> <el-breadcrumb class="layout-navbars-breadcrumb-hide">
<transition-group name="breadcrumb" mode="out-in"> <transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(v, k) in state.breadcrumbList" :key="v.meta.title"> <el-breadcrumb-item v-for="(v, k) in state.breadcrumbList" :key="v.meta.title">
<span v-if="k === state.breadcrumbList.length - 1" class="layout-navbars-breadcrumb-span"> <span v-if="k === state.breadcrumbList.length - 1" class="layout-navbars-breadcrumb-span">
<SvgIcon :name="v.meta.icon" class="layout-navbars-breadcrumb-iconfont" v-if="themeConfig.isBreadcrumbIcon" /> <SvgIcon :name="v.meta.icon" class="layout-navbars-breadcrumb-iconfont" v-if="themeConfig.isBreadcrumbIcon" />

View File

@@ -83,9 +83,8 @@ type RouterConvCallbackFunc = (router: any) => void;
* @param name ==> title路由标题 相当于route.meta.title * @param name ==> title路由标题 相当于route.meta.title
* *
* @param meta ==> 路由菜单元信息 * @param meta ==> 路由菜单元信息
* @param meta.routeName ==> route.name -> 路由 name (对应页面组件 name, 可用作 KeepAlive 缓存标识 && 按钮权限筛选) * @param meta.routeName ==> route.name -> 路由 name (对应页面组件 name, 可用作 KeepAlive 缓存标识 && 按钮权限筛选) -> 对应模块下route.ts字段key
* @param meta.redirect ==> route.redirect -> 路由重定向地址 * @param meta.redirect ==> route.redirect -> 路由重定向地址
* @param meta.component ==> 文件路径
* @param meta.icon ==> 菜单和面包屑对应的图标 * @param meta.icon ==> 菜单和面包屑对应的图标
* @param meta.isHide ==> 是否在菜单中隐藏 (通常列表详情页需要隐藏) * @param meta.isHide ==> 是否在菜单中隐藏 (通常列表详情页需要隐藏)
* @param meta.isFull ==> 菜单是否全屏 (示例:数据大屏页面) * @param meta.isFull ==> 菜单是否全屏 (示例:数据大屏页面)

View File

@@ -21,7 +21,7 @@
<enum-tag :enums="FlowBizType" :value="procinst.bizType"></enum-tag> <enum-tag :enums="FlowBizType" :value="procinst.bizType"></enum-tag>
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item :span="1" :label="$t('flow.initiator')"> <el-descriptions-item :span="1" :label="$t('flow.initiator')">
<AccountInfo :username="procinst.creator" /> <AccountInfo :username="procinst.creator || ''" />
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item :span="1" :label="$t('flow.procinstStatus')"> <el-descriptions-item :span="1" :label="$t('flow.procinstStatus')">

View File

@@ -10,7 +10,7 @@
<el-table-column :label="$t('flow.approver')" min-width="100"> <el-table-column :label="$t('flow.approver')" min-width="100">
<template #default="scope"> <template #default="scope">
<AccountInfo :username="scope.row.handler" /> <AccountInfo :username="scope.row.handler || ''" />
</template> </template>
</el-table-column> </el-table-column>

View File

@@ -19,6 +19,7 @@ const props = defineProps({
authCerts: { authCerts: {
type: [Array<any>], type: [Array<any>],
required: true, required: true,
default: () => [],
}, },
}); });

View File

@@ -26,6 +26,7 @@ const props = defineProps({
tags: { tags: {
type: [Array<any>], type: [Array<any>],
required: true, required: true,
default: () => [],
}, },
}); });
</script> </script>

View File

@@ -19,10 +19,9 @@
:default-expanded-keys="props.defaultExpandedKeys" :default-expanded-keys="props.defaultExpandedKeys"
> >
<template #default="{ node, data }"> <template #default="{ node, data }">
<span <div
:id="node.key" :id="node.key"
@dblclick="treeNodeDblclick(data, node)" class="w-full node-container flex items-center cursor-pointer select-none"
class="node-container flex items-center cursor-pointer select-none"
:class="data.type.nodeDblclickFunc ? 'select-none' : ''" :class="data.type.nodeDblclickFunc ? 'select-none' : ''"
> >
<span v-if="data.type.value == TagTreeNode.TagPath"> <span v-if="data.type.value == TagTreeNode.TagPath">
@@ -44,7 +43,7 @@
<span class="absolute right-2.5 mt-0.5 text-[10px] text-gray-400"> <span class="absolute right-2.5 mt-0.5 text-[10px] text-gray-400">
<slot :node="node" :data="data" name="suffix"></slot> <slot :node="node" :data="data" name="suffix"></slot>
</span> </span>
</span> </div>
</template> </template>
</el-tree> </el-tree>
@@ -153,7 +152,16 @@ const loadNode = async (node: any, resolve: (data: any) => void, reject: () => v
return resolve(nodes); return resolve(nodes);
}; };
const treeNodeClick = async (data: any) => { let lastNodeClickTime = 0;
const treeNodeClick = async (data: any, node: any) => {
const currentClickNodeTime = Date.now();
if (currentClickNodeTime - lastNodeClickTime < 300) {
treeNodeDblclick(data, node);
return;
}
lastNodeClickTime = currentClickNodeTime;
if (!data.disabled && !data.type.nodeDblclickFunc && data.type.nodeClickFunc) { if (!data.disabled && !data.type.nodeDblclickFunc && data.type.nodeClickFunc) {
emit('nodeClick', data); emit('nodeClick', data);
await data.type.nodeClickFunc(data); await data.type.nodeClickFunc(data);
@@ -170,7 +178,6 @@ const treeNodeDblclick = (data: any, node: any) => {
node.expand(); node.expand();
} }
// emit('nodeDblick', data);
if (!data.disabled && data.type.nodeDblclickFunc) { if (!data.disabled && data.type.nodeDblclickFunc) {
data.type.nodeDblclickFunc(data); data.type.nodeDblclickFunc(data);
} }

View File

@@ -89,7 +89,7 @@ import { Rules } from '@/common/rule';
const props = defineProps({ const props = defineProps({
instance: { instance: {
type: [Boolean, Object], type: [Boolean, Object, null],
}, },
db: { db: {
type: [Boolean, Object], type: [Boolean, Object],

View File

@@ -365,7 +365,7 @@ const editDb = (data: any) => {
state.dbEditDialog.data = { ...data }; state.dbEditDialog.data = { ...data };
} else { } else {
state.dbEditDialog.data = { state.dbEditDialog.data = {
instanceId: props.instance.id, instanceId: props.instance?.id,
}; };
} }
state.dbEditDialog.title = data ? useI18nEditTitle('db.db') : useI18nCreateTitle('db.db'); state.dbEditDialog.title = data ? useI18nEditTitle('db.db') : useI18nCreateTitle('db.db');
@@ -373,7 +373,7 @@ const editDb = (data: any) => {
}; };
const confirmEditDb = async (db: any) => { const confirmEditDb = async (db: any) => {
db.instanceId = props.instance.id; db.instanceId = props.instance?.id;
await dbApi.saveDb.request(db); await dbApi.saveDb.request(db);
useI18nSaveSuccessMsg(); useI18nSaveSuccessMsg();
search(); search();

View File

@@ -155,7 +155,7 @@ const state = reactive({
}, },
dbEditDialog: { dbEditDialog: {
visible: false, visible: false,
instance: null as any, instance: {},
title: '', title: '',
}, },
}); });

View File

@@ -61,10 +61,8 @@
</el-form> </el-form>
<template #footer> <template #footer>
<div> <el-button @click="onCancel()">{{ $t('common.cancel') }}</el-button>
<el-button @click="onCancel()">{{ $t('common.cancel') }}</el-button> <el-button type="primary" :loading="saveBtnLoading" @click="onConfirm">{{ $t('common.confirm') }}</el-button>
<el-button type="primary" :loading="saveBtnLoading" @click="onConfirm">{{ $t('common.confirm') }}</el-button>
</div>
</template> </template>
</el-drawer> </el-drawer>
</div> </div>

View File

@@ -1,15 +1,20 @@
<template> <template>
<div> <div>
<el-dialog <el-drawer
:title="title" :title="title"
v-model="dialogVisible" v-model="dialogVisible"
:close-on-click-modal="false" :close-on-click-modal="false"
:before-close="cancel" :before-close="onCancel"
:show-close="true" :show-close="true"
:destroy-on-close="true" :destroy-on-close="true"
width="1000px" size="1000px"
header-class="!mb-1"
> >
<el-form :model="form" :rules="rules" ref="scriptForm" label-width="auto"> <template #header>
<DrawerHeader :header="title" :back="onCancel" />
</template>
<el-form :model="form" :rules="rules" ref="scriptForm" label-position="top">
<el-form-item prop="name" :label="$t('common.name')" required> <el-form-item prop="name" :label="$t('common.name')" required>
<el-input v-model="form.name"></el-input> <el-input v-model="form.name"></el-input>
</el-form-item> </el-form-item>
@@ -22,6 +27,12 @@
<EnumSelect :enums="ScriptResultEnum" v-model="form.type" default-first-option /> <EnumSelect :enums="ScriptResultEnum" v-model="form.type" default-first-option />
</el-form-item> </el-form-item>
<el-form-item prop="category" :label="$t('machine.category')">
<el-select v-model="form.category" filterable allow-create :placeholder="$t('machine.categoryTips')">
<el-option v-for="item in categorys" :key="item" :label="item" :value="item" />
</el-select>
</el-form-item>
<el-form-item class="!w-full"> <el-form-item class="!w-full">
<template #label> <template #label>
<el-tooltip placement="top"> <el-tooltip placement="top">
@@ -43,14 +54,12 @@
</el-form> </el-form>
<template #footer> <template #footer>
<div class="dialog-footer"> <el-button @click="onCancel()">{{ $t('common.cancel') }}</el-button>
<el-button @click="cancel()">{{ $t('common.cancel') }}</el-button> <el-button v-auth="'machine:script:save'" type="primary" :loading="btnLoading" @click="onConfirm">
<el-button v-auth="'machine:script:save'" type="primary" :loading="btnLoading" @click="btnOk"> {{ $t('common.save') }}
{{ $t('common.save') }} </el-button>
</el-button>
</div>
</template> </template>
</el-dialog> </el-drawer>
</div> </div>
</template> </template>
@@ -64,6 +73,7 @@ import SvgIcon from '@/components/svgIcon/index.vue';
import EnumSelect from '@/components/enumselect/EnumSelect.vue'; import EnumSelect from '@/components/enumselect/EnumSelect.vue';
import { useI18nFormValidate, useI18nSaveSuccessMsg } from '@/hooks/useI18n'; import { useI18nFormValidate, useI18nSaveSuccessMsg } from '@/hooks/useI18n';
import { Rules } from '@/common/rule'; import { Rules } from '@/common/rule';
import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
const props = defineProps({ const props = defineProps({
data: { data: {
@@ -93,6 +103,7 @@ const rules = {
const { isCommon, machineId } = toRefs(props); const { isCommon, machineId } = toRefs(props);
const scriptForm: any = ref(null); const scriptForm: any = ref(null);
const categorys = ref([]);
const state = reactive({ const state = reactive({
params: [] as any, params: [] as any,
@@ -104,6 +115,7 @@ const state = reactive({
script: '', script: '',
params: '', params: '',
type: null, type: null,
category: '',
}, },
btnLoading: false, btnLoading: false,
}); });
@@ -114,6 +126,9 @@ watch(props, (newValue: any) => {
if (!dialogVisible.value) { if (!dialogVisible.value) {
return; return;
} }
machineApi.scriptCategorys.request().then((res: any) => {
categorys.value = res;
});
if (newValue.data) { if (newValue.data) {
state.form = { ...newValue.data }; state.form = { ...newValue.data };
if (state.form.params) { if (state.form.params) {
@@ -125,7 +140,7 @@ watch(props, (newValue: any) => {
} }
}); });
const btnOk = async () => { const onConfirm = async () => {
state.form.machineId = isCommon.value ? 9999999 : (machineId?.value as any); state.form.machineId = isCommon.value ? 9999999 : (machineId?.value as any);
await useI18nFormValidate(scriptForm); await useI18nFormValidate(scriptForm);
if (state.params) { if (state.params) {
@@ -134,11 +149,11 @@ const btnOk = async () => {
machineApi.saveScript.request(state.form).then(() => { machineApi.saveScript.request(state.form).then(() => {
useI18nSaveSuccessMsg(); useI18nSaveSuccessMsg();
emit('submitSuccess'); emit('submitSuccess');
cancel(); onCancel();
}); });
}; };
const cancel = () => { const onCancel = () => {
dialogVisible.value = false; dialogVisible.value = false;
emit('cancel'); emit('cancel');
state.params = []; state.params = [];

View File

@@ -99,6 +99,7 @@ import { DynamicFormDialog } from '@/components/dynamic-form';
import { SearchItem } from '@/components/SearchForm'; import { SearchItem } from '@/components/SearchForm';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useI18nCreateTitle, useI18nDeleteConfirm, useI18nDeleteSuccessMsg, useI18nEditTitle } from '@/hooks/useI18n'; import { useI18nCreateTitle, useI18nDeleteConfirm, useI18nDeleteSuccessMsg, useI18nEditTitle } from '@/hooks/useI18n';
import { OptionsApi } from '@/components/SearchForm/index';
const { t } = useI18n(); const { t } = useI18n();
@@ -117,11 +118,24 @@ const pageTableRef: Ref<any> = ref(null);
const state = reactive({ const state = reactive({
selectionData: [], selectionData: [],
searchItems: [SearchItem.select('type', 'common.type').withEnum(ScriptTypeEnum)], searchItems: [
SearchItem.select('type', 'common.type').withEnum(ScriptTypeEnum),
SearchItem.select('category', 'machine.category').withOptionsApi(
OptionsApi.new(machineApi.scriptCategorys, {}).withConvertFn((res) => {
return res.map((x: any) => {
return {
label: x,
value: x,
};
});
})
),
],
columns: [ columns: [
TableColumn.new('name', 'common.name'), TableColumn.new('name', 'common.name'),
TableColumn.new('description', 'common.remark'), TableColumn.new('description', 'common.remark'),
TableColumn.new('type', 'common.type').typeTag(ScriptResultEnum), TableColumn.new('type', 'common.type').typeTag(ScriptResultEnum),
TableColumn.new('category', 'machine.category'),
TableColumn.new('action', 'common.operation').isSlot().setMinWidth(140).alignCenter(), TableColumn.new('action', 'common.operation').isSlot().setMinWidth(140).alignCenter(),
], ],
query: { query: {

View File

@@ -23,6 +23,7 @@ export const machineApi = {
// 删除机器 // 删除机器
del: Api.newDelete('/machines/{id}'), del: Api.newDelete('/machines/{id}'),
scripts: Api.newGet('/machines/{machineId}/scripts'), scripts: Api.newGet('/machines/{machineId}/scripts'),
scriptCategorys: Api.newGet('/machines/scripts/categorys'),
runScript: Api.newGet('/machines/scripts/{scriptId}/{ac}/run'), runScript: Api.newGet('/machines/scripts/{scriptId}/{ac}/run'),
saveScript: Api.newPost('/machines/{machineId}/scripts'), saveScript: Api.newPost('/machines/{machineId}/scripts'),
deleteScript: Api.newDelete('/machines/{machineId}/scripts/{scriptId}'), deleteScript: Api.newDelete('/machines/{machineId}/scripts/{scriptId}'),

View File

@@ -117,6 +117,7 @@
</el-tab-pane> </el-tab-pane>
<el-tab-pane <el-tab-pane
class="h-full"
:disabled="currentTag.type != TagResourceTypeEnum.Tag.value" :disabled="currentTag.type != TagResourceTypeEnum.Tag.value"
:label="`Redis (${resourceCount.redis || 0})`" :label="`Redis (${resourceCount.redis || 0})`"
:name="RedisTag" :name="RedisTag"

View File

@@ -2,7 +2,7 @@
<div class="h-full"> <div class="h-full">
<page-table :page-api="logApi.list" :search-items="searchItems" v-model:query-form="query" :columns="columns"> <page-table :page-api="logApi.list" :search-items="searchItems" v-model:query-form="query" :columns="columns">
<template #creator="{ data }"> <template #creator="{ data }">
<account-info :username="data.creator" /> <account-info :username="data.creator || ''" />
</template> </template>
</page-table> </page-table>
</div> </div>

View File

@@ -58,11 +58,21 @@ const viteConfig: UserConfig = {
entryFileNames: `assets/[name]-[hash].js`, entryFileNames: `assets/[name]-[hash].js`,
chunkFileNames: `assets/[name]-[hash].js`, chunkFileNames: `assets/[name]-[hash].js`,
assetFileNames: `assets/[name]-[hash].[ext]`, assetFileNames: `assets/[name]-[hash].[ext]`,
compact: true, advancedChunks: {
manualChunks: { groups: [
vue: ['vue', 'vue-router', 'pinia'], {
echarts: ['echarts'], name: 'vue',
monaco: ['monaco-editor'], test: /(vue|vue-router|pinia)/i,
},
{
name: 'echarts',
test: /(echarts)/i,
},
{
name: 'monaco',
test: /(monaco-editor)/i,
},
],
}, },
}, },
}, },

View File

@@ -68,7 +68,7 @@ func (d *dbAppImpl) GetPageList(condition *entity.DbQuery, orderBy ...string) (*
func (d *dbAppImpl) SaveDb(ctx context.Context, dbEntity *entity.Db) error { func (d *dbAppImpl) SaveDb(ctx context.Context, dbEntity *entity.Db) error {
// 查找是否存在 // 查找是否存在
oldDb := &entity.Db{Name: dbEntity.Name, InstanceId: dbEntity.InstanceId} oldDb := &entity.Db{Name: dbEntity.Name, InstanceId: dbEntity.InstanceId, AuthCertName: dbEntity.AuthCertName}
authCert, err := d.resourceAuthCertApp.GetAuthCert(dbEntity.AuthCertName) authCert, err := d.resourceAuthCertApp.GetAuthCert(dbEntity.AuthCertName)
if err != nil { if err != nil {

View File

@@ -32,6 +32,7 @@ type MachineScriptForm struct {
Name string `json:"name" binding:"required"` Name string `json:"name" binding:"required"`
MachineId uint64 `json:"machineId" binding:"required"` MachineId uint64 `json:"machineId" binding:"required"`
Type int `json:"type" binding:"required"` Type int `json:"type" binding:"required"`
Category string `json:"category"`
Description string `json:"description" binding:"required"` Description string `json:"description" binding:"required"`
Params string `json:"params"` Params string `json:"params"`
Script string `json:"script" binding:"required"` Script string `json:"script" binding:"required"`

View File

@@ -28,6 +28,8 @@ func (ms *MachineScript) ReqConfs() *req.Confs {
// 获取指定机器脚本列表 // 获取指定机器脚本列表
req.NewGet(":machineId/scripts", ms.MachineScripts), req.NewGet(":machineId/scripts", ms.MachineScripts),
req.NewGet("/scripts/categorys", ms.MachineScriptCategorys),
req.NewPost(":machineId/scripts", ms.SaveMachineScript).Log(req.NewLogSave("机器-保存脚本")).RequiredPermissionCode("machine:script:save"), req.NewPost(":machineId/scripts", ms.SaveMachineScript).Log(req.NewLogSave("机器-保存脚本")).RequiredPermissionCode("machine:script:save"),
req.NewDelete(":machineId/scripts/:scriptId", ms.DeleteMachineScript).Log(req.NewLogSave("机器-删除脚本")).RequiredPermissionCode("machine:script:del"), req.NewDelete(":machineId/scripts/:scriptId", ms.DeleteMachineScript).Log(req.NewLogSave("机器-删除脚本")).RequiredPermissionCode("machine:script:del"),
@@ -39,12 +41,18 @@ func (ms *MachineScript) ReqConfs() *req.Confs {
} }
func (m *MachineScript) MachineScripts(rc *req.Ctx) { func (m *MachineScript) MachineScripts(rc *req.Ctx) {
condition := &entity.MachineScript{MachineId: GetMachineId(rc)} condition := &entity.MachineScript{MachineId: GetMachineId(rc), Category: rc.Query("category")}
res, err := m.machineScriptApp.GetPageList(condition, rc.GetPageParam()) res, err := m.machineScriptApp.GetPageList(condition, rc.GetPageParam())
biz.ErrIsNil(err) biz.ErrIsNil(err)
rc.ResData = model.PageResultConv[*entity.MachineScript, *vo.MachineScriptVO](res) rc.ResData = model.PageResultConv[*entity.MachineScript, *vo.MachineScriptVO](res)
} }
func (m *MachineScript) MachineScriptCategorys(rc *req.Ctx) {
res, err := m.machineScriptApp.GetScriptCategorys(rc.MetaCtx)
biz.ErrIsNil(err)
rc.ResData = res
}
func (m *MachineScript) SaveMachineScript(rc *req.Ctx) { func (m *MachineScript) SaveMachineScript(rc *req.Ctx) {
form, machineScript := req.BindJsonAndCopyTo[*form.MachineScriptForm, *entity.MachineScript](rc) form, machineScript := req.BindJsonAndCopyTo[*form.MachineScriptForm, *entity.MachineScript](rc)

View File

@@ -49,6 +49,7 @@ type MachineScriptVO struct {
Name *string `json:"name"` Name *string `json:"name"`
Script *string `json:"script"` Script *string `json:"script"`
Type *int `json:"type"` Type *int `json:"type"`
Category string `json:"category"`
Description *string `json:"description"` Description *string `json:"description"`
Params *string `json:"params"` Params *string `json:"params"`
MachineId *uint64 `json:"machineId"` MachineId *uint64 `json:"machineId"`

View File

@@ -7,6 +7,7 @@ import (
"mayfly-go/pkg/base" "mayfly-go/pkg/base"
"mayfly-go/pkg/errorx" "mayfly-go/pkg/errorx"
"mayfly-go/pkg/model" "mayfly-go/pkg/model"
"mayfly-go/pkg/utils/collx"
) )
type MachineScript interface { type MachineScript interface {
@@ -15,11 +16,16 @@ type MachineScript interface {
// 分页获取机器脚本信息列表 // 分页获取机器脚本信息列表
GetPageList(condition *entity.MachineScript, pageParam model.PageParam, orderBy ...string) (*model.PageResult[*entity.MachineScript], error) GetPageList(condition *entity.MachineScript, pageParam model.PageParam, orderBy ...string) (*model.PageResult[*entity.MachineScript], error)
// GetScriptCategorys 获取脚本分类
GetScriptCategorys(ctx context.Context) ([]string, error)
Save(ctx context.Context, entity *entity.MachineScript) error Save(ctx context.Context, entity *entity.MachineScript) error
Delete(ctx context.Context, id uint64) Delete(ctx context.Context, id uint64)
} }
var _ (MachineScript) = (*machineScriptAppImpl)(nil)
type machineScriptAppImpl struct { type machineScriptAppImpl struct {
base.AppImpl[*entity.MachineScript, repository.MachineScript] base.AppImpl[*entity.MachineScript, repository.MachineScript]
@@ -33,6 +39,15 @@ func (m *machineScriptAppImpl) GetPageList(condition *entity.MachineScript, page
return m.GetRepo().GetPageList(condition, pageParam, orderBy...) return m.GetRepo().GetPageList(condition, pageParam, orderBy...)
} }
func (m *machineScriptAppImpl) GetScriptCategorys(ctx context.Context) ([]string, error) {
scripts, err := m.ListByCond(new(entity.MachineScript), "category")
if err != nil {
return nil, err
}
return collx.ArrayRemoveBlank(collx.ArrayDeduplicate(collx.ArrayMap(scripts, func(script *entity.MachineScript) string { return script.Category }))), nil
}
// 保存机器脚本 // 保存机器脚本
func (m *machineScriptAppImpl) Save(ctx context.Context, ms *entity.MachineScript) error { func (m *machineScriptAppImpl) Save(ctx context.Context, ms *entity.MachineScript) error {
// 如果机器id不为公共脚本id则校验机器是否存在 // 如果机器id不为公共脚本id则校验机器是否存在

View File

@@ -8,7 +8,8 @@ type MachineScript struct {
Name string `json:"name" gorm:"not null;size:255;comment:脚本名"` // 脚本名 Name string `json:"name" gorm:"not null;size:255;comment:脚本名"` // 脚本名
MachineId uint64 `json:"machineId" gorm:"not null;comment:机器id[0:公共]"` // 机器id MachineId uint64 `json:"machineId" gorm:"not null;comment:机器id[0:公共]"` // 机器id
Type int `json:"type" gorm:"comment:脚本类型[1: 有结果2无结果3实时交互]"` // 脚本类型[1: 有结果2无结果3实时交互] Type int `json:"type" gorm:"comment:脚本类型[1: 有结果2无结果3实时交互]"` // 脚本类型[1: 有结果2无结果3实时交互]
Description string `json:"description" gorm:"size:255;comment:脚本描述"` // 脚本描述 Category string `json:"category" gorm:"size:20;comment:分类"`
Params string `json:"params" gorm:"size:500;comment:脚本入参"` // 参数列表json Description string `json:"description" gorm:"size:255;comment:脚本描述"` // 脚本描述
Script string `json:"script" gorm:"type:text;comment:脚本内容"` // 脚本内容 Params string `json:"params" gorm:"size:500;comment:脚本入参"` // 参数列表json
Script string `json:"script" gorm:"type:text;comment:脚本内容"` // 脚本内容
} }

View File

@@ -4,7 +4,7 @@ import "fmt"
const ( const (
AppName = "mayfly-go" AppName = "mayfly-go"
Version = "v1.10.0" Version = "v1.10.1"
) )
func GetAppInfo() string { func GetAppInfo() string {

View File

@@ -3,6 +3,7 @@ package migrations
import ( import (
esentity "mayfly-go/internal/es/domain/entity" esentity "mayfly-go/internal/es/domain/entity"
flowentity "mayfly-go/internal/flow/domain/entity" flowentity "mayfly-go/internal/flow/domain/entity"
machineentity "mayfly-go/internal/machine/domain/entity"
sysentity "mayfly-go/internal/sys/domain/entity" sysentity "mayfly-go/internal/sys/domain/entity"
"mayfly-go/pkg/model" "mayfly-go/pkg/model"
"time" "time"
@@ -14,6 +15,7 @@ import (
func V1_10() []*gormigrate.Migration { func V1_10() []*gormigrate.Migration {
var migrations []*gormigrate.Migration var migrations []*gormigrate.Migration
migrations = append(migrations, V1_10_0()...) migrations = append(migrations, V1_10_0()...)
migrations = append(migrations, V1_10_1()...)
return migrations return migrations
} }
@@ -132,3 +134,22 @@ func V1_10_0() []*gormigrate.Migration {
}, },
} }
} }
func V1_10_1() []*gormigrate.Migration {
return []*gormigrate.Migration{
{
ID: "20250610-v1.10.1",
Migrate: func(tx *gorm.DB) error {
if !tx.Migrator().HasColumn(&machineentity.MachineScript{}, "category") {
if err := tx.Migrator().AddColumn(&machineentity.MachineScript{}, "category"); err != nil {
return err
}
}
return nil
},
Rollback: func(tx *gorm.DB) error {
return nil
},
},
}
}