feat: 新增首页卡片统计
This commit is contained in:
parent
6b1cf17428
commit
8e405db674
6
.env
6
.env
@ -1,8 +1,10 @@
|
||||
VITE_BASE_URL=/
|
||||
|
||||
VITE_APP_TITLE=SoybeanAdmin
|
||||
VITE_APP_TITLE=Easy Retry
|
||||
|
||||
VITE_APP_DESC=SoybeanAdmin is a fresh and elegant admin template
|
||||
VITE_APP_DESC=A flexible, reliable, and fast platform for distributed task retry and distributed task scheduling.
|
||||
|
||||
VITE_APP_VERSION=v3.1.0
|
||||
|
||||
# the prefix of the icon name
|
||||
VITE_ICON_PREFIX=icon
|
||||
|
@ -1,5 +1,5 @@
|
||||
# backend service base url, prod environment
|
||||
VITE_SERVICE_BASE_URL=https://mock.apifox.com/m1/3109515-0-default
|
||||
VITE_SERVICE_BASE_URL=/proxy-default
|
||||
|
||||
# other backend service base url, prod environment
|
||||
VITE_OTHER_SERVICE_BASE_URL= `{
|
||||
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 9.6 KiB |
BIN
public/logo.png
Normal file
BIN
public/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 54 KiB |
60
src/assets/svg-icon/full-logo.svg
Normal file
60
src/assets/svg-icon/full-logo.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 11 KiB |
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 9.6 KiB |
@ -2,12 +2,14 @@
|
||||
defineOptions({
|
||||
name: 'GlobalFooter'
|
||||
});
|
||||
|
||||
const { VITE_APP_VERSION } = import.meta.env;
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DarkModeContainer class="h-full flex-center">
|
||||
<a href="https://github.com/soybeanjs/soybean-admin/blob/main/LICENSE" target="_blank" rel="noopener noreferrer">
|
||||
Copyright MIT © 2021 Soybean
|
||||
<a href="https://www.easyretry.com/" target="_blank" rel="noopener noreferrer">
|
||||
Copyright © 2024 Easy Retry {{ VITE_APP_VERSION }}
|
||||
</a>
|
||||
</DarkModeContainer>
|
||||
</template>
|
||||
|
@ -16,9 +16,7 @@ const options = ref(
|
||||
|
||||
const onChange = (value: string) => {
|
||||
localStg.set('namespaceId', value);
|
||||
setTimeout(() => {
|
||||
router.go(0);
|
||||
}, 500);
|
||||
router.go(0);
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
const local: App.I18n.Schema = {
|
||||
system: {
|
||||
title: 'SoybeanAdmin'
|
||||
title: 'Easy Retry',
|
||||
desc: 'A flexible, reliable, and fast platform for distributed task retry and distributed task scheduling.'
|
||||
},
|
||||
common: {
|
||||
action: 'Action',
|
||||
@ -35,6 +36,10 @@ const local: App.I18n.Schema = {
|
||||
update: 'Update',
|
||||
updateSuccess: 'Update Success',
|
||||
userCenter: 'User Center',
|
||||
success: 'Success',
|
||||
fail: 'Fail',
|
||||
stop: 'Stop',
|
||||
running: 'Running',
|
||||
yesOrNo: {
|
||||
yes: 'Yes',
|
||||
no: 'No'
|
||||
@ -164,6 +169,7 @@ const local: App.I18n.Schema = {
|
||||
confirmPasswordPlaceholder: 'Please enter password again',
|
||||
codeLogin: 'Verification code login',
|
||||
confirm: 'Confirm',
|
||||
login: 'Login',
|
||||
back: 'Back',
|
||||
validateSuccess: 'Verification passed',
|
||||
loginSuccess: 'Login successfully',
|
||||
@ -212,8 +218,30 @@ const local: App.I18n.Schema = {
|
||||
devDep: 'Development Dependency'
|
||||
},
|
||||
home: {
|
||||
greeting: 'Good morning, {userName}, today is another day full of vitality!',
|
||||
// 问候语
|
||||
Greeting: '{userName}, welcome back.',
|
||||
morningGreeting: 'Good morning, {userName}, today is another day full of vitality!',
|
||||
bthGreeting: "Good morning, {userName}, how's work going? Don't be sedentary. Get up and walk around more often!",
|
||||
noonGreeting: "Good noon, {userName}, it's lunchtime after a long morning at work!",
|
||||
athGreeting: "Good afternoon, {userName}, it's easy to get sleepy in the late afternoon yet, time for a nap!",
|
||||
duskGreeting:
|
||||
"{userName}, it's evening, the view of the sunset outside the window is very beautiful, the most beautiful thing is the red sunset.",
|
||||
eveningGreeting: 'Good evening, {userName}, how are you doing today? Please take care to rest early!',
|
||||
earlyMorningGreeting: "{userName}, It's so late already. Get some rest. Good night.",
|
||||
weatherDesc: 'Today is cloudy to clear, 20℃ - 25℃!',
|
||||
// 卡片统计
|
||||
retryTaskCount: 'Retry Task',
|
||||
jobTaskCount: 'Job Task',
|
||||
userCount: 'User',
|
||||
retryTask: 'Retry Task',
|
||||
retryTaskTip: 'Total task volume: retry/callback task volume',
|
||||
jobTask: 'Job Task',
|
||||
jobTaskTip: 'Success rate: total completion/total dispatch amount',
|
||||
onlineServiceCount: 'Online Machine',
|
||||
onlineServiceTip: 'Always online machines: the sum of clients and servers registered to the system',
|
||||
workflow: 'Workflow',
|
||||
workflowTip: 'Workflow Tip',
|
||||
// ...
|
||||
projectCount: 'Project Count',
|
||||
todo: 'Todo',
|
||||
message: 'Message',
|
||||
@ -271,6 +299,18 @@ const local: App.I18n.Schema = {
|
||||
disable: 'Disable'
|
||||
}
|
||||
},
|
||||
machine: {
|
||||
type: {
|
||||
client: 'Client',
|
||||
server: 'Server'
|
||||
}
|
||||
},
|
||||
retryTask: {
|
||||
status: {
|
||||
maxRetryTimes: 'Max times',
|
||||
pauseRetry: 'Pause'
|
||||
}
|
||||
},
|
||||
role: {
|
||||
title: 'Role List',
|
||||
roleName: 'Role Name',
|
||||
|
@ -1,6 +1,7 @@
|
||||
const local: App.I18n.Schema = {
|
||||
system: {
|
||||
title: 'Soybean 管理系统'
|
||||
title: 'Easy Retry',
|
||||
desc: '灵活,可靠和快速的分布式任务重试和分布式任务调度平台'
|
||||
},
|
||||
common: {
|
||||
action: '操作',
|
||||
@ -35,6 +36,10 @@ const local: App.I18n.Schema = {
|
||||
update: '更新',
|
||||
updateSuccess: '更新成功',
|
||||
userCenter: '个人中心',
|
||||
success: '成功',
|
||||
fail: '失败',
|
||||
stop: '停止',
|
||||
running: '运行中',
|
||||
yesOrNo: {
|
||||
yes: '是',
|
||||
no: '否'
|
||||
@ -163,6 +168,7 @@ const local: App.I18n.Schema = {
|
||||
passwordPlaceholder: '请输入密码',
|
||||
confirmPasswordPlaceholder: '请再次输入密码',
|
||||
codeLogin: '验证码登录',
|
||||
login: '登录',
|
||||
confirm: '确定',
|
||||
back: '返回',
|
||||
validateSuccess: '验证成功',
|
||||
@ -212,8 +218,26 @@ const local: App.I18n.Schema = {
|
||||
devDep: '开发依赖'
|
||||
},
|
||||
home: {
|
||||
greeting: '早安,{userName}, 今天又是充满活力的一天!',
|
||||
Greeting: '{userName},欢迎回来!',
|
||||
morningGreeting: '早安,{userName},今天又是充满活力的一天!',
|
||||
bthGreeting: '上午好,{userName},工作顺利吗,不要久坐,多起来走动走动哦!',
|
||||
noonGreeting: '中午好,{userName},工作了一个上午,现在是午餐时间!',
|
||||
athGreeting: '下午好,{userName},午后很容易犯困呢,是时候该打个盹了!',
|
||||
duskGreeting: '{userName},傍晚了,窗外夕阳的景色很美丽呢,最美不过夕阳红~',
|
||||
eveningGreeting: '晚上好,{userName},今天过得怎么样?请注意早点休息!',
|
||||
earlyMorningGreeting: '{userName},已经这么晚了呀,早点休息吧,晚安~',
|
||||
weatherDesc: '今日多云转晴,20℃ - 25℃!',
|
||||
retryTaskCount: '重试任务',
|
||||
jobTaskCount: '定时任务',
|
||||
userCount: '用户',
|
||||
retryTask: '重试任务',
|
||||
retryTaskTip: '总任务量: 重试/回调任务量',
|
||||
jobTask: '定时任务',
|
||||
jobTaskTip: '成功率:总完成/总调度量',
|
||||
onlineServiceCount: '总在线机器',
|
||||
onlineServiceTip: '总在线机器:注册到系统的客户端和服务端之和',
|
||||
workflow: '工作流',
|
||||
workflowTip: '工作流提示',
|
||||
projectCount: '项目数',
|
||||
todo: '待办',
|
||||
message: '消息',
|
||||
@ -271,6 +295,18 @@ const local: App.I18n.Schema = {
|
||||
disable: '禁用'
|
||||
}
|
||||
},
|
||||
machine: {
|
||||
type: {
|
||||
client: '客户端',
|
||||
server: '服务端'
|
||||
}
|
||||
},
|
||||
retryTask: {
|
||||
status: {
|
||||
maxRetryTimes: '最大重试次数',
|
||||
pauseRetry: '暂停重试'
|
||||
}
|
||||
},
|
||||
role: {
|
||||
title: '角色列表',
|
||||
roleName: '角色名称',
|
||||
|
@ -2,7 +2,7 @@
|
||||
import { getRgbOfColor } from '@sa/utils';
|
||||
import { $t } from '@/locales';
|
||||
import { localStg } from '@/utils/storage';
|
||||
import systemLogo from '@/assets/svg-icon/logo.svg?raw';
|
||||
import systemLogo from '@/assets/svg-icon/full-logo.svg?raw';
|
||||
|
||||
export function setupLoading() {
|
||||
const themeColor = localStg.get('themeColor') || '#646cff';
|
||||
@ -18,7 +18,7 @@ export function setupLoading() {
|
||||
'right-0 bottom-0 animate-delay-1500'
|
||||
];
|
||||
|
||||
const logoWithClass = systemLogo.replace('<svg', `<svg class="size-128px text-primary"`);
|
||||
const logoWithClass = systemLogo.replace('<svg', `<svg class="size-256px text-primary"`);
|
||||
|
||||
const dot = loadingClasses
|
||||
.map(item => {
|
||||
@ -34,7 +34,7 @@ export function setupLoading() {
|
||||
${dot}
|
||||
</div>
|
||||
</div>
|
||||
<h2 class="text-28px font-500 text-#646464">${$t('system.title')}</h2>
|
||||
<h2 class="text-26px font-500 pt-32px text-#646464">${$t('system.desc')}</h2>
|
||||
</div>`;
|
||||
|
||||
const app = document.getElementById('app');
|
||||
|
9
src/service/api/dashboard.ts
Normal file
9
src/service/api/dashboard.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { request } from '../request';
|
||||
|
||||
/** Version */
|
||||
export function fetchCardCount() {
|
||||
return request<Api.Dashboard.CardCount>({
|
||||
url: '/dashboard/task-retry-job',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
export * from './auth';
|
||||
export * from './route';
|
||||
export * from './system';
|
||||
export * from './dashboard';
|
||||
export * from './system-manage';
|
||||
|
9
src/service/api/system.ts
Normal file
9
src/service/api/system.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { request } from '../request';
|
||||
|
||||
/** Version */
|
||||
export function fetchVersion() {
|
||||
return request<string>({
|
||||
url: '/system/version',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
/** Default theme settings */
|
||||
export const themeSettings: App.Theme.ThemeSetting = {
|
||||
themeScheme: 'light',
|
||||
themeColor: '#646cff',
|
||||
themeColor: '#22aae3',
|
||||
otherColor: {
|
||||
info: '#2080f0',
|
||||
success: '#52c41a',
|
||||
|
42
src/typings/api.d.ts
vendored
42
src/typings/api.d.ts
vendored
@ -95,6 +95,48 @@ declare namespace Api {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* namespace Dashboard
|
||||
*
|
||||
* backend api module: "dashboard"
|
||||
*/
|
||||
namespace Dashboard {
|
||||
type CardCount = {
|
||||
jobTask: JobTask;
|
||||
retryTask: RetryTask;
|
||||
retryTaskBarList: RetryTaskBarList[];
|
||||
onLineService: OnlineService;
|
||||
};
|
||||
|
||||
type OnlineService = {
|
||||
total: number;
|
||||
clientTotal: number;
|
||||
serverTotal: number;
|
||||
};
|
||||
|
||||
type RetryTaskBarList = {
|
||||
x: string;
|
||||
taskTotal: number;
|
||||
};
|
||||
|
||||
type RetryTask = {
|
||||
totalNum: number;
|
||||
runningNum: number;
|
||||
finishNum: number;
|
||||
maxCountNum: number;
|
||||
suspendNum: number;
|
||||
};
|
||||
|
||||
type JobTask = {
|
||||
successNum: number;
|
||||
failNum: number;
|
||||
cancelNum: number;
|
||||
stopNum: number;
|
||||
totalNum: number;
|
||||
successRate: number;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* namespace SystemManage
|
||||
*
|
||||
|
38
src/typings/app.d.ts
vendored
38
src/typings/app.d.ts
vendored
@ -247,6 +247,7 @@ declare namespace App {
|
||||
type Schema = {
|
||||
system: {
|
||||
title: string;
|
||||
desc: string;
|
||||
};
|
||||
common: {
|
||||
action: string;
|
||||
@ -281,6 +282,10 @@ declare namespace App {
|
||||
update: string;
|
||||
updateSuccess: string;
|
||||
userCenter: string;
|
||||
success: string;
|
||||
fail: string;
|
||||
stop: string;
|
||||
running: string;
|
||||
yesOrNo: {
|
||||
yes: string;
|
||||
no: string;
|
||||
@ -346,6 +351,7 @@ declare namespace App {
|
||||
passwordPlaceholder: string;
|
||||
confirmPasswordPlaceholder: string;
|
||||
codeLogin: string;
|
||||
login: string;
|
||||
confirm: string;
|
||||
back: string;
|
||||
validateSuccess: string;
|
||||
@ -395,7 +401,25 @@ declare namespace App {
|
||||
devDep: string;
|
||||
};
|
||||
home: {
|
||||
greeting: string;
|
||||
Greeting: string;
|
||||
morningGreeting: string;
|
||||
bthGreeting: string;
|
||||
noonGreeting: string;
|
||||
athGreeting: string;
|
||||
duskGreeting: string;
|
||||
eveningGreeting: string;
|
||||
earlyMorningGreeting: string;
|
||||
retryTaskCount: string;
|
||||
jobTaskCount: string;
|
||||
userCount: string;
|
||||
retryTask: string;
|
||||
retryTaskTip: string;
|
||||
jobTask: string;
|
||||
jobTaskTip: string;
|
||||
onlineServiceCount: string;
|
||||
onlineServiceTip: string;
|
||||
workflow: string;
|
||||
workflowTip: string;
|
||||
weatherDesc: string;
|
||||
projectCount: string;
|
||||
todo: string;
|
||||
@ -454,6 +478,18 @@ declare namespace App {
|
||||
disable: string;
|
||||
};
|
||||
};
|
||||
machine: {
|
||||
type: {
|
||||
client: string;
|
||||
server: string;
|
||||
};
|
||||
};
|
||||
retryTask: {
|
||||
status: {
|
||||
maxRetryTimes: string;
|
||||
pauseRetry: string;
|
||||
};
|
||||
};
|
||||
role: {
|
||||
title: string;
|
||||
roleName: string;
|
||||
|
19
src/typings/components.d.ts
vendored
19
src/typings/components.d.ts
vendored
@ -16,14 +16,21 @@ declare module 'vue' {
|
||||
FullScreen: typeof import('./../components/common/full-screen.vue')['default']
|
||||
IconAntDesignEnterOutlined: typeof import('~icons/ant-design/enter-outlined')['default']
|
||||
IconAntDesignReloadOutlined: typeof import('~icons/ant-design/reload-outlined')['default']
|
||||
IconAntDesignSettingOutlined: typeof import('~icons/ant-design/setting-outlined')['default']
|
||||
IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default']
|
||||
IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default']
|
||||
IconIcRoundDelete: typeof import('~icons/ic/round-delete')['default']
|
||||
IconIcRoundPlus: typeof import('~icons/ic/round-plus')['default']
|
||||
IconIcRoundRefresh: typeof import('~icons/ic/round-refresh')['default']
|
||||
IconIcRoundSearch: typeof import('~icons/ic/round-search')['default']
|
||||
IconLocalBanner: typeof import('~icons/local/banner')['default']
|
||||
IconLocalLogo: typeof import('~icons/local/logo')['default']
|
||||
IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default']
|
||||
IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default']
|
||||
IconMdiDrag: typeof import('~icons/mdi/drag')['default']
|
||||
IconMdiKeyboardEsc: typeof import('~icons/mdi/keyboard-esc')['default']
|
||||
IconMdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default']
|
||||
IconMdiRefresh: typeof import('~icons/mdi/refresh')['default']
|
||||
IconUilSearch: typeof import('~icons/uil/search')['default']
|
||||
LangSwitch: typeof import('./../components/common/lang-switch.vue')['default']
|
||||
LookForward: typeof import('./../components/custom/look-forward.vue')['default']
|
||||
@ -34,6 +41,9 @@ declare module 'vue' {
|
||||
NCard: typeof import('naive-ui')['NCard']
|
||||
NCheckbox: typeof import('naive-ui')['NCheckbox']
|
||||
NColorPicker: typeof import('naive-ui')['NColorPicker']
|
||||
NDataTable: typeof import('naive-ui')['NDataTable']
|
||||
NDescriptions: typeof import('naive-ui')['NDescriptions']
|
||||
NDescriptionsItem: typeof import('naive-ui')['NDescriptionsItem']
|
||||
NDialogProvider: typeof import('naive-ui')['NDialogProvider']
|
||||
NDivider: typeof import('naive-ui')['NDivider']
|
||||
NDrawer: typeof import('naive-ui')['NDrawer']
|
||||
@ -42,6 +52,7 @@ declare module 'vue' {
|
||||
NEmpty: typeof import('naive-ui')['NEmpty']
|
||||
NForm: typeof import('naive-ui')['NForm']
|
||||
NFormItem: typeof import('naive-ui')['NFormItem']
|
||||
NFormItemGi: typeof import('naive-ui')['NFormItemGi']
|
||||
NGi: typeof import('naive-ui')['NGi']
|
||||
NGrid: typeof import('naive-ui')['NGrid']
|
||||
NInput: typeof import('naive-ui')['NInput']
|
||||
@ -54,13 +65,21 @@ declare module 'vue' {
|
||||
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
||||
NModal: typeof import('naive-ui')['NModal']
|
||||
NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
|
||||
NPopconfirm: typeof import('naive-ui')['NPopconfirm']
|
||||
NPopover: typeof import('naive-ui')['NPopover']
|
||||
NProgress: typeof import('naive-ui')['NProgress']
|
||||
NRadio: typeof import('naive-ui')['NRadio']
|
||||
NRadioGroup: typeof import('naive-ui')['NRadioGroup']
|
||||
NScrollbar: typeof import('naive-ui')['NScrollbar']
|
||||
NSelect: typeof import('naive-ui')['NSelect']
|
||||
NSkeleton: typeof import('naive-ui')['NSkeleton']
|
||||
NSpace: typeof import('naive-ui')['NSpace']
|
||||
NSpin: typeof import('naive-ui')['NSpin']
|
||||
NStatistic: typeof import('naive-ui')['NStatistic']
|
||||
NSwitch: typeof import('naive-ui')['NSwitch']
|
||||
NTab: typeof import('naive-ui')['NTab']
|
||||
NTabs: typeof import('naive-ui')['NTabs']
|
||||
NTag: typeof import('naive-ui')['NTag']
|
||||
NThing: typeof import('naive-ui')['NThing']
|
||||
NTooltip: typeof import('naive-ui')['NTooltip']
|
||||
PinToggler: typeof import('./../components/common/pin-toggler.vue')['default']
|
||||
|
2
src/typings/env.d.ts
vendored
2
src/typings/env.d.ts
vendored
@ -15,6 +15,8 @@ declare namespace Env {
|
||||
readonly VITE_APP_TITLE: string;
|
||||
/** The description of the application */
|
||||
readonly VITE_APP_DESC: string;
|
||||
/** The version of the application */
|
||||
readonly VITE_APP_VERSION: string;
|
||||
/** The router history mode */
|
||||
readonly VITE_ROUTER_HISTORY_MODE?: RouterHistoryMode;
|
||||
/** The prefix of the iconify icon */
|
||||
|
@ -3,14 +3,11 @@ import { computed } from 'vue';
|
||||
import type { Component } from 'vue';
|
||||
import { getColorPalette, mixColor } from '@sa/utils';
|
||||
import { $t } from '@/locales';
|
||||
import GlobalFooter from '@/layouts/modules/global-footer/index.vue';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import { loginModuleRecord } from '@/constants/app';
|
||||
import PwdLogin from './modules/pwd-login.vue';
|
||||
import CodeLogin from './modules/code-login.vue';
|
||||
import Register from './modules/register.vue';
|
||||
import ResetPwd from './modules/reset-pwd.vue';
|
||||
import BindWechat from './modules/bind-wechat.vue';
|
||||
|
||||
interface Props {
|
||||
/** The login module */
|
||||
@ -21,6 +18,8 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
module: 'pwd-login'
|
||||
});
|
||||
|
||||
const { VITE_APP_VERSION } = import.meta.env;
|
||||
|
||||
const appStore = useAppStore();
|
||||
const themeStore = useThemeStore();
|
||||
|
||||
@ -30,13 +29,7 @@ interface LoginModule {
|
||||
component: Component;
|
||||
}
|
||||
|
||||
const modules: LoginModule[] = [
|
||||
{ key: 'pwd-login', label: loginModuleRecord['pwd-login'], component: PwdLogin },
|
||||
{ key: 'code-login', label: loginModuleRecord['code-login'], component: CodeLogin },
|
||||
{ key: 'register', label: loginModuleRecord.register, component: Register },
|
||||
{ key: 'reset-pwd', label: loginModuleRecord['reset-pwd'], component: ResetPwd },
|
||||
{ key: 'bind-wechat', label: loginModuleRecord['bind-wechat'], component: BindWechat }
|
||||
];
|
||||
const modules: LoginModule[] = [{ key: 'pwd-login', label: loginModuleRecord['pwd-login'], component: PwdLogin }];
|
||||
|
||||
const activeModule = computed(() => {
|
||||
const findItem = modules.find(item => item.key === props.module);
|
||||
@ -63,7 +56,10 @@ const bgColor = computed(() => {
|
||||
<div class="w-400px <sm:w-300px">
|
||||
<header class="flex-y-center justify-between">
|
||||
<SystemLogo class="text-64px text-primary <sm:text-48px" />
|
||||
<h3 class="text-28px text-primary font-500 <sm:text-22px">{{ $t('system.title') }}</h3>
|
||||
<h3 class="flex text-28px text-primary font-500 <sm:text-22px">
|
||||
{{ $t('system.title') }}
|
||||
<span class="mt-3px pl-12px text-16px color-#00000072 font-600">{{ VITE_APP_VERSION }}</span>
|
||||
</h3>
|
||||
<div class="i-flex-vertical">
|
||||
<ThemeSchemaSwitch
|
||||
:theme-schema="themeStore.themeScheme"
|
||||
@ -80,12 +76,18 @@ const bgColor = computed(() => {
|
||||
</div>
|
||||
</header>
|
||||
<main class="pt-24px">
|
||||
<h3 class="text-18px text-primary font-medium">{{ $t(activeModule.label) }}</h3>
|
||||
<div class="pt-24px">
|
||||
<!-- <h3 class="text-18px text-primary font-medium">{{ $t(activeModule.label) }}</h3> -->
|
||||
<div class="pt-12px">
|
||||
<Transition :name="themeStore.page.animateMode" mode="out-in" appear>
|
||||
<component :is="activeModule.component" />
|
||||
</Transition>
|
||||
</div>
|
||||
<div class="pt-12px text-center">
|
||||
<ButtonIcon tooltip-content="Mail" class="color-#272636 dark:color-#f0f2f5" icon="simple-icons:maildotru" />
|
||||
<ButtonIcon class="color-#c71d23" tooltip-content="Gitee" icon="simple-icons:gitee" />
|
||||
<ButtonIcon tooltip-content="Github" class="color-#010409 dark:color-#e6edf3" icon="simple-icons:github" />
|
||||
</div>
|
||||
<GlobalFooter />
|
||||
</main>
|
||||
</div>
|
||||
</NCard>
|
||||
|
@ -2,8 +2,6 @@
|
||||
import { computed, reactive } from 'vue';
|
||||
import { Md5 } from 'ts-md5';
|
||||
import { $t } from '@/locales';
|
||||
import { loginModuleRecord } from '@/constants/app';
|
||||
import { useRouterPush } from '@/hooks/common/router';
|
||||
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
|
||||
import { useAuthStore } from '@/store/modules/auth';
|
||||
|
||||
@ -12,7 +10,6 @@ defineOptions({
|
||||
});
|
||||
|
||||
const authStore = useAuthStore();
|
||||
const { toggleLoginModule } = useRouterPush();
|
||||
const { formRef, validate } = useNaiveForm();
|
||||
|
||||
interface FormModel {
|
||||
@ -56,21 +53,9 @@ async function handleSubmit() {
|
||||
/>
|
||||
</NFormItem>
|
||||
<NSpace vertical :size="24">
|
||||
<div class="flex-y-center justify-between">
|
||||
<NCheckbox>{{ $t('page.login.pwdLogin.rememberMe') }}</NCheckbox>
|
||||
<NButton quaternary>{{ $t('page.login.pwdLogin.forgetPassword') }}</NButton>
|
||||
</div>
|
||||
<NButton type="primary" size="large" round block :loading="authStore.loginLoading" @click="handleSubmit">
|
||||
{{ $t('common.confirm') }}
|
||||
{{ $t('page.login.common.login') }}
|
||||
</NButton>
|
||||
<div class="flex-y-center justify-between gap-12px">
|
||||
<NButton class="flex-1" block @click="toggleLoginModule('code-login')">
|
||||
{{ $t(loginModuleRecord['code-login']) }}
|
||||
</NButton>
|
||||
<NButton class="flex-1" block @click="toggleLoginModule('register')">
|
||||
{{ $t(loginModuleRecord.register) }}
|
||||
</NButton>
|
||||
</div>
|
||||
</NSpace>
|
||||
</NForm>
|
||||
</template>
|
||||
|
@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import { fetchCardCount } from '@/service/api';
|
||||
import HeaderBanner from './modules/header-banner.vue';
|
||||
import CardData from './modules/card-data.vue';
|
||||
import LineChart from './modules/line-chart.vue';
|
||||
@ -11,12 +12,23 @@ import CreativityBanner from './modules/creativity-banner.vue';
|
||||
const appStore = useAppStore();
|
||||
|
||||
const gap = computed(() => (appStore.isMobile ? 0 : 16));
|
||||
|
||||
const cardCount = ref<Api.Dashboard.CardCount>();
|
||||
|
||||
const getCardData = async () => {
|
||||
const { data: cardData, error } = await fetchCardCount();
|
||||
if (!error) {
|
||||
cardCount.value = cardData;
|
||||
}
|
||||
};
|
||||
|
||||
getCardData();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NSpace vertical :size="16">
|
||||
<HeaderBanner />
|
||||
<CardData />
|
||||
<CardData class="h-165px" :model-value="cardCount!" />
|
||||
<NGrid :x-gap="gap" :y-gap="16" responsive="screen" item-responsive>
|
||||
<NGi span="24 s:24 m:14">
|
||||
<NCard :bordered="false" class="card-wrapper">
|
||||
|
@ -7,62 +7,138 @@ defineOptions({
|
||||
name: 'CardData'
|
||||
});
|
||||
|
||||
interface Props {
|
||||
modelValue: Api.Dashboard.CardCount;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
interface CardData {
|
||||
key: string;
|
||||
title: string;
|
||||
tip: string;
|
||||
value: number;
|
||||
unit: string;
|
||||
color: {
|
||||
start: string;
|
||||
end: string;
|
||||
};
|
||||
icon: string;
|
||||
bottom: { label: string; value: number }[];
|
||||
}
|
||||
|
||||
console.log(props.modelValue);
|
||||
|
||||
const cardData = computed<CardData[]>(() => [
|
||||
{
|
||||
key: 'visitCount',
|
||||
title: $t('page.home.visitCount'),
|
||||
value: 9725,
|
||||
key: 'retryTask',
|
||||
title: $t('page.home.retryTask'),
|
||||
tip: $t('page.home.retryTaskTip'),
|
||||
value: props.modelValue?.retryTask.totalNum ?? 0,
|
||||
unit: '',
|
||||
color: {
|
||||
start: '#ec4786',
|
||||
end: '#b955a4'
|
||||
start: '#40e9c5',
|
||||
end: '#BEE3DB'
|
||||
},
|
||||
icon: 'ant-design:bar-chart-outlined'
|
||||
icon: 'ant-design:schedule-outlined',
|
||||
bottom: [
|
||||
{
|
||||
label: $t('common.success'),
|
||||
value: props.modelValue?.retryTask.finishNum ?? 0
|
||||
},
|
||||
{
|
||||
label: $t('common.running'),
|
||||
value: props.modelValue?.retryTask.runningNum ?? 0
|
||||
},
|
||||
{
|
||||
label: $t('page.manage.retryTask.status.maxRetryTimes'),
|
||||
value: props.modelValue?.retryTask.maxCountNum ?? 0
|
||||
},
|
||||
{
|
||||
label: $t('page.manage.retryTask.status.pauseRetry'),
|
||||
value: props.modelValue?.retryTask.suspendNum ?? 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'turnover',
|
||||
title: $t('page.home.turnover'),
|
||||
value: 1026,
|
||||
unit: '$',
|
||||
key: 'jobTask',
|
||||
title: $t('page.home.jobTask'),
|
||||
tip: $t('page.home.jobTaskTip'),
|
||||
value: props.modelValue?.jobTask.totalNum ?? 0,
|
||||
color: {
|
||||
start: '#865ec0',
|
||||
end: '#5144b4'
|
||||
start: '#f5b386',
|
||||
end: '#FFD6BA'
|
||||
},
|
||||
icon: 'ant-design:money-collect-outlined'
|
||||
icon: 'ant-design:profile-outlined',
|
||||
bottom: [
|
||||
{
|
||||
label: $t('common.success'),
|
||||
value: props.modelValue?.jobTask.successNum ?? 0
|
||||
},
|
||||
{
|
||||
label: $t('common.fail'),
|
||||
value: props.modelValue?.jobTask.failNum ?? 0
|
||||
},
|
||||
{
|
||||
label: $t('common.stop'),
|
||||
value: props.modelValue?.jobTask.stopNum ?? 0
|
||||
},
|
||||
{
|
||||
label: $t('common.cancel'),
|
||||
value: props.modelValue?.jobTask.cancelNum ?? 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'downloadCount',
|
||||
title: $t('page.home.downloadCount'),
|
||||
value: 970925,
|
||||
key: 'onlineServiceCount',
|
||||
title: $t('page.home.onlineServiceCount'),
|
||||
tip: $t('page.home.onlineServiceTip'),
|
||||
value: props.modelValue?.onLineService.total ?? 0,
|
||||
unit: '',
|
||||
color: {
|
||||
start: '#56cdf3',
|
||||
end: '#719de3'
|
||||
start: '#b686d4',
|
||||
end: '#c5a5d8'
|
||||
},
|
||||
icon: 'carbon:document-download'
|
||||
icon: 'ant-design:database-outlined',
|
||||
bottom: [
|
||||
{
|
||||
label: $t('page.manage.machine.type.client'),
|
||||
value: props.modelValue?.onLineService.clientTotal ?? 0
|
||||
},
|
||||
{
|
||||
label: $t('page.manage.machine.type.server'),
|
||||
value: props.modelValue?.onLineService.serverTotal ?? 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'dealCount',
|
||||
title: $t('page.home.dealCount'),
|
||||
value: 9527,
|
||||
key: 'workflow',
|
||||
title: $t('page.home.workflow'),
|
||||
tip: $t('page.home.workflowTip'),
|
||||
value: 7,
|
||||
unit: '',
|
||||
color: {
|
||||
start: '#fcbc25',
|
||||
end: '#f68057'
|
||||
start: '#ec6f6f',
|
||||
end: '#f99797'
|
||||
},
|
||||
icon: 'ant-design:trademark-circle-outlined'
|
||||
icon: 'ant-design:database-outlined',
|
||||
bottom: [
|
||||
{
|
||||
label: $t('common.success'),
|
||||
value: 185
|
||||
},
|
||||
{
|
||||
label: $t('common.fail'),
|
||||
value: 37
|
||||
},
|
||||
{
|
||||
label: $t('common.stop'),
|
||||
value: 5
|
||||
},
|
||||
{
|
||||
label: $t('common.cancel'),
|
||||
value: 13
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
@ -89,21 +165,45 @@ function getGradientColor(color: CardData['color']) {
|
||||
|
||||
<NGrid cols="s:1 m:2 l:4" responsive="screen" :x-gap="16" :y-gap="16">
|
||||
<NGi v-for="item in cardData" :key="item.key">
|
||||
<GradientBg :gradient-color="getGradientColor(item.color)" class="flex-1">
|
||||
<h3 class="text-16px">{{ item.title }}</h3>
|
||||
<div class="flex justify-between pt-12px">
|
||||
<SvgIcon :icon="item.icon" class="text-32px" />
|
||||
<CountTo
|
||||
:prefix="item.unit"
|
||||
:start-value="1"
|
||||
:end-value="item.value"
|
||||
class="text-30px text-white dark:text-dark"
|
||||
/>
|
||||
</div>
|
||||
</GradientBg>
|
||||
<NSpin :show="false">
|
||||
<GradientBg :gradient-color="getGradientColor(item.color)" class="h-165px flex-1">
|
||||
<div class="flex justify-between">
|
||||
<div class="align-center flex">
|
||||
<SvgIcon :icon="item.icon" class="text-26px" />
|
||||
<h3 class="ml-2 text-18px">{{ item.title }}</h3>
|
||||
</div>
|
||||
<NPopover trigger="hover">
|
||||
<template #trigger>
|
||||
<NButton text>
|
||||
<SvgIcon icon="ant-design:info-circle-outlined" class="text-20px color-white" />
|
||||
</NButton>
|
||||
</template>
|
||||
{{ item.tip }}
|
||||
</NPopover>
|
||||
</div>
|
||||
<div class="flex pt-12px">
|
||||
<CountTo :start-value="0" :end-value="item.value" class="text-30px text-white" />
|
||||
</div>
|
||||
<NProgress type="line" color="#728bf9" rail-color="#ebebeb" :percentage="30" indicator-text-color="#fff" />
|
||||
<NDivider />
|
||||
<template v-for="(bottomItem, bottomIndex) in item.bottom" :key="bottomIndex">
|
||||
<NDivider v-if="bottomIndex !== 0" vertical />
|
||||
{{ bottomItem.label }}
|
||||
<CountTo :start-value="0" :end-value="bottomItem.value" class="text-white" />
|
||||
</template>
|
||||
</GradientBg>
|
||||
</NSpin>
|
||||
</NGi>
|
||||
</NGrid>
|
||||
</NCard>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
<style scoped>
|
||||
.n-divider {
|
||||
margin: 16px 0 6px;
|
||||
}
|
||||
|
||||
.n-divider--vertical {
|
||||
margin: 0 1px 0 5px;
|
||||
}
|
||||
</style>
|
||||
|
@ -22,41 +22,65 @@ interface StatisticData {
|
||||
const statisticData = computed<StatisticData[]>(() => [
|
||||
{
|
||||
id: 0,
|
||||
label: $t('page.home.projectCount'),
|
||||
value: '25'
|
||||
label: $t('page.home.userCount'),
|
||||
value: '2'
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
label: $t('page.home.todo'),
|
||||
value: '4/16'
|
||||
label: $t('page.home.jobTaskCount'),
|
||||
value: '8'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
label: $t('page.home.message'),
|
||||
value: '12'
|
||||
label: $t('page.home.retryTaskCount'),
|
||||
value: '3'
|
||||
}
|
||||
]);
|
||||
|
||||
const timeFix = () => {
|
||||
const time = new Date();
|
||||
const hour = time.getHours();
|
||||
let text = '';
|
||||
if (hour > 5 && hour <= 8) {
|
||||
text = 'morning';
|
||||
} else if (hour > 8 && hour <= 11) {
|
||||
text = 'bth';
|
||||
} else if (hour > 11 && hour <= 14) {
|
||||
text = 'noon';
|
||||
} else if (hour > 14 && hour <= 17) {
|
||||
text = 'ath';
|
||||
} else if (hour > 17 && hour <= 19) {
|
||||
text = 'dusk';
|
||||
} else if (hour > 19 && hour <= 21) {
|
||||
text = 'evening';
|
||||
} else if (hour > 21 && hour <= 5) {
|
||||
text = 'earlyMorning';
|
||||
}
|
||||
return text;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NCard :bordered="false" class="card-wrapper">
|
||||
<NGrid :x-gap="gap" :y-gap="16" responsive="screen" item-responsive>
|
||||
<NGi span="24 s:24 m:18">
|
||||
<NGi class="flex" span="24 s:24 m:18">
|
||||
<div class="flex-y-center">
|
||||
<div class="size-72px shrink-0 overflow-hidden rd-1/2">
|
||||
<img src="@/assets/imgs/soybean.jpg" class="size-full" />
|
||||
</div>
|
||||
<div class="pl-12px">
|
||||
<h3 class="text-18px font-semibold">
|
||||
{{ $t('page.home.greeting', { userName: authStore.userInfo.username }) }}
|
||||
<h3 class="text-22px font-semibold">
|
||||
{{ $t(`page.home.${timeFix()}Greeting`, { userName: authStore.userInfo.username }) }}
|
||||
</h3>
|
||||
<p class="text-#999 leading-30px">{{ $t('page.home.weatherDesc') }}</p>
|
||||
<!-- <p class="text-#999 leading-30px">{{ $t('system.title') + ' - ' + $t('system.desc') }}</p> -->
|
||||
</div>
|
||||
</div>
|
||||
</NGi>
|
||||
<NGi span="24 s:24 m:6">
|
||||
<NSpace :size="24" justify="end">
|
||||
<NStatistic v-for="item in statisticData" :key="item.id" class="whitespace-nowrap" v-bind="item" />
|
||||
<NStatistic
|
||||
v-for="item in statisticData"
|
||||
:key="item.id"
|
||||
class="whitespace-nowrap text-center"
|
||||
v-bind="item"
|
||||
/>
|
||||
</NSpace>
|
||||
</NGi>
|
||||
</NGrid>
|
||||
|
Loading…
Reference in New Issue
Block a user