Merge branch 'refs/heads/1.2.0-beta1' into preview

This commit is contained in:
xlsea 2024-07-19 10:21:20 +08:00
commit 1dc974baf1
11 changed files with 143 additions and 35 deletions

View File

@ -9,7 +9,7 @@ export function setupElegantRouter() {
blank: 'src/layouts/blank-layout/index.vue' blank: 'src/layouts/blank-layout/index.vue'
}, },
customRoutes: { customRoutes: {
names: [] names: ['document']
}, },
routePathTransformer(routeName, routePath) { routePathTransformer(routeName, routePath) {
const key = routeName as RouteKey; const key = routeName as RouteKey;

View File

@ -16,6 +16,9 @@
}, },
"format-horizontal-align-left": { "format-horizontal-align-left": {
"body": "<path fill=\"currentColor\" d=\"M11 16v-3h10v-2H11V8l-4 4zm-8 4h2V4H3z\"/>" "body": "<path fill=\"currentColor\" d=\"M11 16v-3h10v-2H11V8l-4 4zm-8 4h2V4H3z\"/>"
},
"file-document-multiple-outline": {
"body": "<path fill=\"currentColor\" d=\"M16 0H8C6.9 0 6 .9 6 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V6zm4 18H8V2h7v5h5zM4 4v18h16v2H4c-1.1 0-2-.9-2-2V4zm6 6v2h8v-2zm0 4v2h5v-2z\"/>"
} }
} }
} }

View File

@ -25,7 +25,7 @@ const columns = defineModel<NaiveUI.TableColumnCheck[]>('columns', {
<div v-for="item in columns" :key="item.key" class="h-36px flex-y-center rd-4px hover:(bg-primary bg-opacity-20)"> <div v-for="item in columns" :key="item.key" class="h-36px flex-y-center rd-4px hover:(bg-primary bg-opacity-20)">
<icon-mdi-drag class="mr-8px h-full cursor-move text-icon" /> <icon-mdi-drag class="mr-8px h-full cursor-move text-icon" />
<NCheckbox v-model:checked="item.checked" class="none_draggable flex-1"> <NCheckbox v-model:checked="item.checked" class="none_draggable flex-1">
{{ item.title }} {{ typeof item.title === 'function' ? item.key.toUpperCase() : item.title }}
</NCheckbox> </NCheckbox>
</div> </div>
</VueDraggable> </VueDraggable>

View File

@ -282,14 +282,32 @@ const SnailLogComponent = defineComponent({
return <></>; return <></>;
} }
const restOfText = throwable.replace(/^.+(\n|$)/m, ''); const restOfText = throwable.replace(/^.+(\n|$)/m, '');
return <NCollapseItem title={firstLine[0]} name={message.index}>{`${restOfText}`}</NCollapseItem>; return <NCollapseItem title={firstLine[0]} name={`throwable-${message.index}`}>{`${restOfText}`}</NCollapseItem>;
};
const messageComponent = (message: Api.JobLog.JobMessage) => {
const msg = message.message;
if (!msg) {
return <></>;
}
const firstLine = msg.match(/^.+/m);
if (!firstLine) {
return <></>;
}
const restOfText = msg.replace(/^.+(\n|$)/m, '').replaceAll('\n', '\n - ');
if (restOfText) {
return (
<NCollapseItem title={firstLine[0]} name={`message-${message.index}`}>{` - ${restOfText}`}</NCollapseItem>
);
}
return <div class="pl-6px">- {`${msg}`}</div>;
}; };
const handleUpdateExpanded = (val: string[]) => { const handleUpdateExpanded = (val: string[]) => {
expandedNames.value = val; expandedNames.value = val;
}; };
const handleResize = () => { const handleResize = (_: ResizeObserverEntry) => {
expandedNames.value = []; expandedNames.value = [];
}; };
@ -305,6 +323,7 @@ const SnailLogComponent = defineComponent({
class="virtual-list" class="virtual-list"
itemSize={85} itemSize={85}
item-resizable item-resizable
ignore-item-resize
padding-bottom={16} padding-bottom={16}
items={logList.value} items={logList.value}
scrollbar-props={{ xScrollable: true }} scrollbar-props={{ xScrollable: true }}
@ -322,7 +341,7 @@ const SnailLogComponent = defineComponent({
<span class="log-hljs-thread mr-12px inline-block">{`[${message.thread}]`}</span> <span class="log-hljs-thread mr-12px inline-block">{`[${message.thread}]`}</span>
</div> </div>
<div class="log-hljs-location">{`${message.location}: `}</div> <div class="log-hljs-location">{`${message.location}: `}</div>
<div class="pl-6px">- {`${message.message}`}</div> <div>{messageComponent(message)}</div>
<div>{throwableComponent(message)}</div> <div>{throwableComponent(message)}</div>
<NDivider /> <NDivider />
</pre> </pre>
@ -479,6 +498,14 @@ const SnailLogComponent = defineComponent({
max-height: calc(100vh - 101px); max-height: calc(100vh - 101px);
} }
.v-vl {
min-height: calc(100vh - 101px);
}
.v-vl-items {
min-height: calc(100vh - 101px - 16px) !important;
}
.n-divider:not(.n-divider--vertical) { .n-divider:not(.n-divider--vertical) {
margin-top: 6px; margin-top: 6px;
margin-bottom: 6px; margin-bottom: 6px;

View File

@ -28,6 +28,10 @@ const appStore = useAppStore();
const themeStore = useThemeStore(); const themeStore = useThemeStore();
const routeStore = useRouteStore(); const routeStore = useRouteStore();
const menus = computed(() => {
return routeStore.menus.filter(item => item.show !== false);
});
interface MixMenuItemProps { interface MixMenuItemProps {
/** Menu item label */ /** Menu item label */
label: App.Global.Menu['label']; label: App.Global.Menu['label'];
@ -80,7 +84,7 @@ function handleClickMixMenu(menu: App.Global.Menu) {
<slot></slot> <slot></slot>
<SimpleScrollbar> <SimpleScrollbar>
<MixMenuItem <MixMenuItem
v-for="menu in routeStore.menus" v-for="menu in menus"
:key="menu.key" :key="menu.key"
:label="menu.label" :label="menu.label"
:icon="menu.icon" :icon="menu.icon"

View File

@ -317,6 +317,7 @@ const local: App.I18n.Schema = {
log: 'Log', log: 'Log',
home: 'Home', home: 'Home',
about: 'About', about: 'About',
document: 'Document',
pods: 'Online Machine', pods: 'Online Machine',
namespace: 'Namespace', namespace: 'Namespace',
notify: 'Notify', notify: 'Notify',

View File

@ -317,6 +317,7 @@ const local: App.I18n.Schema = {
log: '日志', log: '日志',
home: '首页', home: '首页',
about: '关于', about: '关于',
document: '文档',
pods: '在线机器', pods: '在线机器',
namespace: '命名空间', namespace: '命名空间',
group: '组管理', group: '组管理',

View File

@ -1,4 +1,4 @@
import { addAPIProvider } from '@iconify/vue'; import { addAPIProvider, enableCache } from '@iconify/vue';
/** Setup the iconify offline */ /** Setup the iconify offline */
export function setupIconifyOffline() { export function setupIconifyOffline() {
@ -6,5 +6,7 @@ export function setupIconifyOffline() {
if (VITE_ICONIFY_URL) { if (VITE_ICONIFY_URL) {
addAPIProvider('', { resources: [VITE_ICONIFY_URL] }); addAPIProvider('', { resources: [VITE_ICONIFY_URL] });
enableCache('all');
} }
} }

View File

@ -46,7 +46,7 @@ export const generatedRoutes: GeneratedRoute[] = [
meta: { meta: {
title: 'about', title: 'about',
i18nKey: 'route.about', i18nKey: 'route.about',
order: 1000, order: 999,
icon: 'material-symbols:help-outline-rounded' icon: 'material-symbols:help-outline-rounded'
} }
}, },
@ -58,7 +58,8 @@ export const generatedRoutes: GeneratedRoute[] = [
title: 'group', title: 'group',
i18nKey: 'route.group', i18nKey: 'route.group',
order: 30, order: 30,
icon: 'material-symbols:group-work-outline' icon: 'material-symbols:group-work-outline',
keepAlive: true
} }
}, },
{ {
@ -69,7 +70,8 @@ export const generatedRoutes: GeneratedRoute[] = [
title: 'home', title: 'home',
i18nKey: 'route.home', i18nKey: 'route.home',
icon: 'material-symbols:dashboard-outline-rounded', icon: 'material-symbols:dashboard-outline-rounded',
order: 1 order: 1,
keepAlive: true
} }
}, },
{ {
@ -93,7 +95,8 @@ export const generatedRoutes: GeneratedRoute[] = [
title: 'job', title: 'job',
i18nKey: 'route.job', i18nKey: 'route.job',
order: 50, order: 50,
icon: 'eos-icons:cronjob' icon: 'eos-icons:cronjob',
keepAlive: true
}, },
children: [ children: [
{ {
@ -104,7 +107,8 @@ export const generatedRoutes: GeneratedRoute[] = [
title: 'job_batch', title: 'job_batch',
i18nKey: 'route.job_batch', i18nKey: 'route.job_batch',
icon: 'carbon:batch-job', icon: 'carbon:batch-job',
order: 20 order: 20,
keepAlive: true
} }
}, },
{ {
@ -115,7 +119,8 @@ export const generatedRoutes: GeneratedRoute[] = [
title: 'job_task', title: 'job_task',
i18nKey: 'route.job_task', i18nKey: 'route.job_task',
icon: 'octicon:tasklist', icon: 'octicon:tasklist',
order: 10 order: 10,
keepAlive: true
} }
} }
] ]
@ -152,7 +157,8 @@ export const generatedRoutes: GeneratedRoute[] = [
i18nKey: 'route.namespace', i18nKey: 'route.namespace',
icon: 'eos-icons:namespace', icon: 'eos-icons:namespace',
order: 20, order: 20,
roles: ['R_ADMIN'] roles: ['R_ADMIN'],
keepAlive: true
} }
}, },
{ {
@ -163,7 +169,8 @@ export const generatedRoutes: GeneratedRoute[] = [
title: 'notify', title: 'notify',
i18nKey: 'route.notify', i18nKey: 'route.notify',
order: 100, order: 100,
icon: 'material-symbols:notifications-active-outline-rounded' icon: 'material-symbols:notifications-active-outline-rounded',
keepAlive: true
}, },
children: [ children: [
{ {
@ -173,7 +180,8 @@ export const generatedRoutes: GeneratedRoute[] = [
meta: { meta: {
title: 'notify_config', title: 'notify_config',
i18nKey: 'route.notify_config', i18nKey: 'route.notify_config',
icon: 'cbi:scene-dynamic' icon: 'cbi:scene-dynamic',
keepAlive: true
} }
}, },
{ {
@ -183,7 +191,8 @@ export const generatedRoutes: GeneratedRoute[] = [
meta: { meta: {
title: 'notify_recipient', title: 'notify_recipient',
i18nKey: 'route.notify_recipient', i18nKey: 'route.notify_recipient',
icon: 'fluent:people-call-20-filled' icon: 'fluent:people-call-20-filled',
keepAlive: true
} }
} }
] ]
@ -196,7 +205,8 @@ export const generatedRoutes: GeneratedRoute[] = [
title: 'pods', title: 'pods',
i18nKey: 'route.pods', i18nKey: 'route.pods',
icon: 'ant-design:database-outlined', icon: 'ant-design:database-outlined',
order: 10 order: 10,
keepAlive: true
} }
}, },
{ {
@ -207,7 +217,8 @@ export const generatedRoutes: GeneratedRoute[] = [
title: 'retry', title: 'retry',
i18nKey: 'route.retry', i18nKey: 'route.retry',
order: 70, order: 70,
icon: 'carbon:retry-failed' icon: 'carbon:retry-failed',
keepAlive: true
}, },
children: [ children: [
{ {
@ -218,7 +229,8 @@ export const generatedRoutes: GeneratedRoute[] = [
title: 'retry_dead-letter', title: 'retry_dead-letter',
i18nKey: 'route.retry_dead-letter', i18nKey: 'route.retry_dead-letter',
icon: 'streamline:interface-arrows-synchronize-warning-arrow-fail-notification-sync-warning-failure-synchronize-error', icon: 'streamline:interface-arrows-synchronize-warning-arrow-fail-notification-sync-warning-failure-synchronize-error',
order: 30 order: 30,
keepAlive: true
} }
}, },
{ {
@ -229,7 +241,8 @@ export const generatedRoutes: GeneratedRoute[] = [
title: 'retry_log', title: 'retry_log',
i18nKey: 'route.retry_log', i18nKey: 'route.retry_log',
icon: 'tabler:logs', icon: 'tabler:logs',
order: 20 order: 20,
keepAlive: true
} }
}, },
{ {
@ -240,7 +253,8 @@ export const generatedRoutes: GeneratedRoute[] = [
title: 'retry_scene', title: 'retry_scene',
i18nKey: 'route.retry_scene', i18nKey: 'route.retry_scene',
icon: 'cbi:scene-dynamic', icon: 'cbi:scene-dynamic',
order: 1 order: 1,
keepAlive: true
} }
}, },
{ {
@ -251,7 +265,8 @@ export const generatedRoutes: GeneratedRoute[] = [
title: 'retry_task', title: 'retry_task',
i18nKey: 'route.retry_task', i18nKey: 'route.retry_task',
icon: 'octicon:tasklist', icon: 'octicon:tasklist',
order: 10 order: 10,
keepAlive: true
} }
} }
] ]
@ -265,7 +280,8 @@ export const generatedRoutes: GeneratedRoute[] = [
i18nKey: 'route.user', i18nKey: 'route.user',
order: 900, order: 900,
icon: 'material-symbols:manage-accounts', icon: 'material-symbols:manage-accounts',
roles: ['R_ADMIN'] roles: ['R_ADMIN'],
keepAlive: true
}, },
children: [ children: [
{ {
@ -277,7 +293,8 @@ export const generatedRoutes: GeneratedRoute[] = [
i18nKey: 'route.user_manager', i18nKey: 'route.user_manager',
icon: 'streamline:interface-user-multiple-close-geometric-human-multiple-person-up-user', icon: 'streamline:interface-user-multiple-close-geometric-human-multiple-person-up-user',
order: 900, order: 900,
roles: ['R_ADMIN'] roles: ['R_ADMIN'],
keepAlive: true
} }
} }
] ]
@ -290,7 +307,8 @@ export const generatedRoutes: GeneratedRoute[] = [
title: 'workflow', title: 'workflow',
i18nKey: 'route.workflow', i18nKey: 'route.workflow',
order: 60, order: 60,
icon: 'lucide:workflow' icon: 'lucide:workflow',
keepAlive: true
}, },
children: [ children: [
{ {
@ -301,7 +319,8 @@ export const generatedRoutes: GeneratedRoute[] = [
title: 'workflow_batch', title: 'workflow_batch',
i18nKey: 'route.workflow_batch', i18nKey: 'route.workflow_batch',
icon: 'carbon:batch-job', icon: 'carbon:batch-job',
order: 10 order: 10,
keepAlive: true
} }
}, },
{ {
@ -373,7 +392,8 @@ export const generatedRoutes: GeneratedRoute[] = [
title: 'workflow_task', title: 'workflow_task',
i18nKey: 'route.workflow_task', i18nKey: 'route.workflow_task',
icon: 'octicon:tasklist', icon: 'octicon:tasklist',
order: 1 order: 1,
keepAlive: true
} }
} }
] ]

View File

@ -12,7 +12,23 @@ const customRoutes: CustomRoute[] = [];
/** create routes when the auth route mode is static */ /** create routes when the auth route mode is static */
export function createStaticRoutes() { export function createStaticRoutes() {
const constantRoutes: ElegantRoute[] = []; const constantRoutes: ElegantRoute[] = [
{
name: 'document',
path: '/document',
component: 'layout.base$view.iframe-page',
props: {
url: 'https://snailjob.opensnail.com/'
},
meta: {
title: 'document',
i18nKey: 'route.document',
order: 1000,
icon: 'mdi:file-document-multiple-outline',
href: 'https://snailjob.opensnail.com/'
}
}
];
const authRoutes: ElegantRoute[] = []; const authRoutes: ElegantRoute[] = [];

View File

@ -1,10 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, reactive, watch } from 'vue'; import { computed, reactive, ref, watch } from 'vue';
import { useFormRules, useNaiveForm } from '@/hooks/common/form'; import { useFormRules, useNaiveForm } from '@/hooks/common/form';
import OperateDrawer from '@/components/common/operate-drawer.vue'; import OperateDrawer from '@/components/common/operate-drawer.vue';
import { $t } from '@/locales'; import { $t } from '@/locales';
import { fetchAddRetryTask, fetchIdempotentIdGenerate } from '@/service/api'; import { fetchAddRetryTask, fetchIdempotentIdGenerate } from '@/service/api';
import { translateOptions } from '@/utils/common'; import { isNotNull, translateOptions } from '@/utils/common';
import { retryTaskStatusTypeOptions } from '@/constants/business'; import { retryTaskStatusTypeOptions } from '@/constants/business';
import CodeMirror from '@/components/common/code-mirror.vue'; import CodeMirror from '@/components/common/code-mirror.vue';
import SelectGroup from '@/components/common/select-group.vue'; import SelectGroup from '@/components/common/select-group.vue';
@ -33,6 +33,7 @@ const visible = defineModel<boolean>('visible', {
default: false default: false
}); });
const argsList = ref<string[]>([]);
const { formRef, validate, restoreValidation } = useNaiveForm(); const { formRef, validate, restoreValidation } = useNaiveForm();
const { defaultRequiredRule } = useFormRules(); const { defaultRequiredRule } = useFormRules();
@ -74,11 +75,31 @@ const rules: Record<RuleKey, App.Global.FormRule> = {
idempotentId: defaultRequiredRule, idempotentId: defaultRequiredRule,
bizNo: defaultRequiredRule, bizNo: defaultRequiredRule,
executorName: defaultRequiredRule, executorName: defaultRequiredRule,
argsStr: defaultRequiredRule, argsStr: { ...defaultRequiredRule, validator: validatorArgsStr },
retryStatus: defaultRequiredRule retryStatus: defaultRequiredRule
}; };
function validatorArgsStr() {
if (argsList.value.length === 0) {
return false;
}
try {
argsList.value.forEach(arg => {
if (!isNotNull(arg)) {
throw new Error($t('form.required'));
}
});
} catch {
return false;
}
return true;
}
function handleUpdateModelWhenEdit() { function handleUpdateModelWhenEdit() {
argsList.value = [];
if (props.operateType === 'add') { if (props.operateType === 'add') {
Object.assign(model, createDefaultModel()); Object.assign(model, createDefaultModel());
return; return;
@ -86,6 +107,7 @@ function handleUpdateModelWhenEdit() {
if (props.operateType === 'edit' && props.rowData) { if (props.operateType === 'edit' && props.rowData) {
Object.assign(model, props.rowData); Object.assign(model, props.rowData);
argsList.value = JSON.parse(props.rowData.argsStr || '[]');
} }
} }
@ -97,14 +119,14 @@ async function handleSubmit() {
await validate(); await validate();
if (props.operateType === 'add') { if (props.operateType === 'add') {
const { groupName, sceneName, idempotentId, bizNo, executorName, argsStr, retryStatus } = model; const { groupName, sceneName, idempotentId, bizNo, executorName, retryStatus } = model;
const { error } = await fetchAddRetryTask({ const { error } = await fetchAddRetryTask({
groupName, groupName,
sceneName, sceneName,
idempotentId, idempotentId,
bizNo, bizNo,
executorName, executorName,
argsStr, argsStr: JSON.stringify(argsList.value),
retryStatus retryStatus
}); });
if (error) return; if (error) return;
@ -184,7 +206,19 @@ async function setIdempotentId() {
/> />
</NFormItem> </NFormItem>
<NFormItem :label="$t('page.retryTask.argsStr')" path="argsStr"> <NFormItem :label="$t('page.retryTask.argsStr')" path="argsStr">
<CodeMirror v-model="model.argsStr" lang="json" :placeholder="$t('page.jobTask.form.argsStr')" /> <NDynamicInput v-model:value="argsList" :on-create="() => ''">
<template #default="{ index }">
<NFormItem
class="w-full"
ignore-path-change
:show-label="false"
:show-feedback="false"
:path="`argsStr[${index}]`"
>
<CodeMirror v-model="argsList[index]" lang="json" :placeholder="$t('page.jobTask.form.argsStr')" />
</NFormItem>
</template>
</NDynamicInput>
</NFormItem> </NFormItem>
<NFormItem :label="$t('page.retryTask.retryStatus')" path="retryStatus"> <NFormItem :label="$t('page.retryTask.retryStatus')" path="retryStatus">
<NSelect <NSelect