[Feature][UI Next] Add task state statistics table. (#7760)
parent
c58dbefaa5
commit
4514eaeb8b
|
|
@ -55,13 +55,6 @@ const PieChart = defineComponent({
|
|||
show: false,
|
||||
position: 'center',
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: '40',
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
},
|
||||
labelLine: {
|
||||
show: false,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import { useI18n } from 'vue-i18n'
|
|||
import { useLanguageStore } from '@/store/language/language'
|
||||
|
||||
interface Props {
|
||||
sideMenuOptions: Array<any>,
|
||||
sideMenuOptions: Array<any>
|
||||
isShowSide: boolean
|
||||
}
|
||||
|
||||
|
|
@ -45,7 +45,8 @@ const Sidebar = (props: Props) => {
|
|||
const refreshOptionsRef = ref()
|
||||
|
||||
return (
|
||||
<NLayoutSider style={{display: props.isShowSide ? 'block' : 'none'}}
|
||||
<NLayoutSider
|
||||
style={{ display: props.isShowSide ? 'block' : 'none' }}
|
||||
bordered
|
||||
nativeScrollbar={false}
|
||||
show-trigger='bar'
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import { useDataList } from './use-dataList'
|
|||
import { useLanguageStore } from '@/store/language/language'
|
||||
|
||||
const Content = () => {
|
||||
|
||||
const { state, getHeaderMenuOptions } = useDataList()
|
||||
|
||||
const headerMenuOptions = getHeaderMenuOptions(state.menuOptions)
|
||||
|
|
|
|||
|
|
@ -30,9 +30,19 @@ interface UserList {
|
|||
count: number
|
||||
}
|
||||
|
||||
interface TaskCountDto {
|
||||
count: number
|
||||
taskStateType: string
|
||||
}
|
||||
|
||||
interface ProcessDefinitionRes {
|
||||
count: number
|
||||
userList: UserList[]
|
||||
}
|
||||
|
||||
export { CodeReq, StateReq, ProcessDefinitionRes }
|
||||
interface TaskStateRes {
|
||||
totalCount: number
|
||||
taskCountDtos: TaskCountDto[]
|
||||
}
|
||||
|
||||
export { CodeReq, StateReq, ProcessDefinitionRes, TaskStateRes }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
import { useProcessDefinition } from './use-process-definition'
|
||||
import BarChart from '@/components/chart/modules/Bar'
|
||||
import Card from '@/components/card'
|
||||
|
||||
const props = {
|
||||
title: {
|
||||
type: String as PropType<string>,
|
||||
},
|
||||
}
|
||||
|
||||
const DefinitionCard = defineComponent({
|
||||
name: 'DefinitionCard',
|
||||
props,
|
||||
setup() {
|
||||
const { getProcessDefinition } = useProcessDefinition()
|
||||
const processDefinition = getProcessDefinition()
|
||||
|
||||
return { processDefinition }
|
||||
},
|
||||
render() {
|
||||
const { title, processDefinition } = this
|
||||
|
||||
return (
|
||||
<Card title={title}>
|
||||
{{
|
||||
default: () =>
|
||||
processDefinition.xAxisData.length > 0 &&
|
||||
processDefinition.seriesData.length > 0 && (
|
||||
<BarChart
|
||||
xAxisData={processDefinition.xAxisData}
|
||||
seriesData={processDefinition.seriesData}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
</Card>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
export default DefinitionCard
|
||||
|
|
@ -15,75 +15,57 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { defineComponent } from 'vue'
|
||||
import { NGrid, NGi, NDataTable } from 'naive-ui'
|
||||
import { defineComponent, Ref, onMounted, ref, watch } from 'vue'
|
||||
import { NGrid, NGi } from 'naive-ui'
|
||||
import { startOfToday, getTime } from 'date-fns'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useTable } from './use-table'
|
||||
import { useProcessDefinition } from './use-process-definition'
|
||||
import Card from '@/components/card'
|
||||
import PieChart from '@/components/chart/modules/Pie'
|
||||
import BarChart from '@/components/chart/modules/Bar'
|
||||
import { useTaskState } from './use-task-state'
|
||||
import StateCard from './state-card'
|
||||
import DefinitionCard from './definition-card'
|
||||
import styles from './index.module.scss'
|
||||
import type { ProcessDefinitionRes } from '@/service/modules/projects-analysis/types'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'home',
|
||||
setup() {
|
||||
const { t } = useI18n()
|
||||
const { getProcessDefinition, formatProcessDefinition } =
|
||||
useProcessDefinition()
|
||||
const processDefinition = getProcessDefinition()
|
||||
const dateRef = ref([getTime(startOfToday()), Date.now()])
|
||||
const { getTaskState } = useTaskState()
|
||||
let taskStateRef: Ref<any> = ref([])
|
||||
|
||||
return { t, processDefinition, formatProcessDefinition }
|
||||
onMounted(() => {
|
||||
taskStateRef.value = getTaskState(dateRef.value)
|
||||
})
|
||||
|
||||
const handleTaskDate = (val: any) => {
|
||||
taskStateRef.value = getTaskState(val)
|
||||
}
|
||||
|
||||
return { t, dateRef, handleTaskDate, taskStateRef }
|
||||
},
|
||||
render() {
|
||||
const { columnsRef } = useTable()
|
||||
const { t, processDefinition, formatProcessDefinition } = this
|
||||
const chartData =
|
||||
Object.keys(processDefinition).length > 0 &&
|
||||
formatProcessDefinition(processDefinition as ProcessDefinitionRes)
|
||||
const { t, dateRef, handleTaskDate } = this
|
||||
|
||||
return (
|
||||
<div>
|
||||
<NGrid x-gap={12} cols={2}>
|
||||
<NGi>
|
||||
<Card title={t('home.task_state_statistics')}>
|
||||
{{
|
||||
default: () => (
|
||||
<div class={styles['card-table']}>
|
||||
<PieChart />
|
||||
<NDataTable columns={columnsRef} />
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
</Card>
|
||||
<StateCard
|
||||
title={t('home.task_state_statistics')}
|
||||
date={dateRef}
|
||||
tableData={this.taskStateRef.value}
|
||||
onUpdateDatePickerValue={handleTaskDate}
|
||||
/>
|
||||
</NGi>
|
||||
<NGi class={styles['card-table']}>
|
||||
<Card title={t('home.process_state_statistics')}>
|
||||
{{
|
||||
default: () => (
|
||||
<div class={styles['card-table']}>
|
||||
<PieChart />
|
||||
<NDataTable columns={columnsRef} />
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
</Card>
|
||||
<StateCard
|
||||
title={t('home.process_state_statistics')}
|
||||
date={dateRef}
|
||||
/>
|
||||
</NGi>
|
||||
</NGrid>
|
||||
<NGrid cols={1} style='margin-top: 12px;'>
|
||||
<NGi>
|
||||
<Card title={t('home.process_definition_statistics')}>
|
||||
{{
|
||||
default: () =>
|
||||
chartData && (
|
||||
<BarChart
|
||||
xAxisData={chartData.xAxisData}
|
||||
seriesData={chartData.seriesData}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
</Card>
|
||||
<DefinitionCard title={t('home.process_definition_statistics')} />
|
||||
</NGi>
|
||||
</NGrid>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
import { useTable } from './use-table'
|
||||
import styles from '@/views/home/index.module.scss'
|
||||
import PieChart from '@/components/chart/modules/Pie'
|
||||
import { NDataTable, NDatePicker } from 'naive-ui'
|
||||
import Card from '@/components/card'
|
||||
|
||||
const props = {
|
||||
title: {
|
||||
type: String as PropType<string>,
|
||||
},
|
||||
date: {
|
||||
type: Array as PropType<Array<any>>,
|
||||
},
|
||||
tableData: {
|
||||
type: [Array, Boolean] as PropType<Array<any> | false>,
|
||||
},
|
||||
}
|
||||
|
||||
const StateCard = defineComponent({
|
||||
name: 'StateCard',
|
||||
props,
|
||||
emits: ['updateDatePickerValue'],
|
||||
setup(props, ctx) {
|
||||
const onUpdateDatePickerValue = (val: any) => {
|
||||
ctx.emit('updateDatePickerValue', val)
|
||||
}
|
||||
|
||||
return { onUpdateDatePickerValue }
|
||||
},
|
||||
render() {
|
||||
const { title, date, tableData, onUpdateDatePickerValue } = this
|
||||
const { columnsRef } = useTable()
|
||||
|
||||
return (
|
||||
<Card title={title}>
|
||||
{{
|
||||
default: () => (
|
||||
<div class={styles['card-table']}>
|
||||
<PieChart />
|
||||
{tableData && (
|
||||
<NDataTable
|
||||
columns={columnsRef}
|
||||
data={tableData}
|
||||
striped
|
||||
size={'small'}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
'header-extra': () => (
|
||||
<NDatePicker
|
||||
default-value={date}
|
||||
onUpdateValue={onUpdateDatePickerValue}
|
||||
size='small'
|
||||
type='datetimerange'
|
||||
clearable
|
||||
/>
|
||||
),
|
||||
}}
|
||||
</Card>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
export default StateCard
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
interface ChartData {
|
||||
xAxisData: Array<string>
|
||||
seriesData: Array<number>
|
||||
}
|
||||
|
||||
interface TaskStateTableData {
|
||||
id: number
|
||||
number: number
|
||||
state: string
|
||||
}
|
||||
|
||||
export { ChartData, TaskStateTableData }
|
||||
|
|
@ -18,22 +18,23 @@
|
|||
import { useAsyncState } from '@vueuse/core'
|
||||
import { countDefinitionByUser } from '@/service/modules/projects-analysis'
|
||||
import type { ProcessDefinitionRes } from '@/service/modules/projects-analysis/types'
|
||||
import type { ChartData } from './types'
|
||||
|
||||
export function useProcessDefinition() {
|
||||
const getProcessDefinition = () => {
|
||||
const { state } = useAsyncState(
|
||||
countDefinitionByUser({ projectCode: 0 }),
|
||||
{}
|
||||
countDefinitionByUser({ projectCode: 0 }).then(
|
||||
(res: ProcessDefinitionRes): ChartData => {
|
||||
const xAxisData = res.userList.map((item) => item.userName)
|
||||
const seriesData = res.userList.map((item) => item.count)
|
||||
|
||||
return { xAxisData, seriesData }
|
||||
}
|
||||
),
|
||||
{ xAxisData: [], seriesData: [] }
|
||||
)
|
||||
return state
|
||||
}
|
||||
|
||||
const formatProcessDefinition = (data: ProcessDefinitionRes) => {
|
||||
const xAxisData: Array<string> = data.userList.map((item) => item.userName)
|
||||
const seriesData: Array<number> = data.userList.map((item) => item.count)
|
||||
|
||||
return { xAxisData, seriesData }
|
||||
}
|
||||
|
||||
return { getProcessDefinition, formatProcessDefinition }
|
||||
return { getProcessDefinition }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,3 +14,5 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useAsyncState } from '@vueuse/core'
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export function useTable() {
|
|||
const { t } = useI18n()
|
||||
|
||||
const columnsRef: TableColumns<any> = [
|
||||
{ title: '#', key: '#' },
|
||||
{ title: '#', key: 'id' },
|
||||
{ title: t('home.number'), key: 'number' },
|
||||
{ title: t('home.state'), key: 'state' },
|
||||
]
|
||||
|
|
|
|||
|
|
@ -14,3 +14,34 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useAsyncState } from '@vueuse/core'
|
||||
import { format } from 'date-fns'
|
||||
import { countTaskState } from '@/service/modules/projects-analysis'
|
||||
import type { TaskStateRes } from '@/service/modules/projects-analysis/types'
|
||||
import type { TaskStateTableData } from './types'
|
||||
|
||||
export function useTaskState() {
|
||||
const getTaskState = (date: Array<number>) => {
|
||||
const { state } = useAsyncState(
|
||||
countTaskState({
|
||||
startDate: format(date[0], 'yyyy-MM-dd HH:mm:ss'),
|
||||
endDate: format(date[1], 'yyyy-MM-dd HH:mm:ss'),
|
||||
projectCode: 0,
|
||||
}).then((res: TaskStateRes): Array<TaskStateTableData> => {
|
||||
return res.taskCountDtos.map((item, index) => {
|
||||
return {
|
||||
id: index + 1,
|
||||
state: item.taskStateType,
|
||||
number: item.count,
|
||||
}
|
||||
})
|
||||
}),
|
||||
[]
|
||||
)
|
||||
|
||||
return state
|
||||
}
|
||||
|
||||
return { getTaskState }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,80 +15,86 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { withKeys } from 'vue'
|
||||
import { defineComponent, toRefs, withKeys } from 'vue'
|
||||
import styles from './index.module.scss'
|
||||
import { NInput, NButton, NSwitch, NForm, NFormItem } from 'naive-ui'
|
||||
import { useValidate } from './use-validate'
|
||||
import { useTranslate } from './use-translate'
|
||||
import { useLogin } from './use-login'
|
||||
|
||||
const login = () => {
|
||||
const { state, t, locale } = useValidate()
|
||||
const login = defineComponent({
|
||||
name: 'login',
|
||||
setup() {
|
||||
const { state, t, locale } = useValidate()
|
||||
|
||||
const { handleChange } = useTranslate(locale)
|
||||
const { handleChange } = useTranslate(locale)
|
||||
|
||||
const { handleLogin } = useLogin(state)
|
||||
const { handleLogin } = useLogin(state)
|
||||
|
||||
return (
|
||||
<div class={styles.container}>
|
||||
<div class={styles['language-switch']}>
|
||||
<NSwitch
|
||||
onUpdateValue={handleChange}
|
||||
checked-value='en_US'
|
||||
unchecked-value='zh_CN'
|
||||
>
|
||||
{{
|
||||
checked: () => 'en_US',
|
||||
unchecked: () => 'zh_CN',
|
||||
}}
|
||||
</NSwitch>
|
||||
</div>
|
||||
<div class={styles['login-model']}>
|
||||
<div class={styles.logo}>
|
||||
<div class={styles['logo-img']} />
|
||||
</div>
|
||||
<div class={styles['form-model']}>
|
||||
<NForm rules={state.rules} ref='loginFormRef'>
|
||||
<NFormItem
|
||||
label={t('login.userName')}
|
||||
label-style={{ color: 'black' }}
|
||||
path='userName'
|
||||
>
|
||||
<NInput
|
||||
type='text'
|
||||
size='large'
|
||||
v-model={[state.loginForm.userName, 'value']}
|
||||
placeholder={t('login.userName_tips')}
|
||||
autofocus
|
||||
onKeydown={withKeys(handleLogin, ['enter'])}
|
||||
/>
|
||||
</NFormItem>
|
||||
<NFormItem
|
||||
label={t('login.userPassword')}
|
||||
label-style={{ color: 'black' }}
|
||||
path='userPassword'
|
||||
>
|
||||
<NInput
|
||||
type='password'
|
||||
size='large'
|
||||
v-model={[state.loginForm.userPassword, 'value']}
|
||||
placeholder={t('login.userPassword_tips')}
|
||||
onKeydown={withKeys(handleLogin, ['enter'])}
|
||||
/>
|
||||
</NFormItem>
|
||||
</NForm>
|
||||
<NButton
|
||||
round
|
||||
type='info'
|
||||
style={{ width: '100%' }}
|
||||
onClick={handleLogin}
|
||||
return { t, handleChange, handleLogin, ...toRefs(state) }
|
||||
},
|
||||
render() {
|
||||
return (
|
||||
<div class={styles.container}>
|
||||
<div class={styles['language-switch']}>
|
||||
<NSwitch
|
||||
onUpdateValue={this.handleChange}
|
||||
checked-value='en_US'
|
||||
unchecked-value='zh_CN'
|
||||
>
|
||||
{t('login.login')}
|
||||
</NButton>
|
||||
{{
|
||||
checked: () => 'en_US',
|
||||
unchecked: () => 'zh_CN',
|
||||
}}
|
||||
</NSwitch>
|
||||
</div>
|
||||
<div class={styles['login-model']}>
|
||||
<div class={styles.logo}>
|
||||
<div class={styles['logo-img']} />
|
||||
</div>
|
||||
<div class={styles['form-model']}>
|
||||
<NForm rules={this.rules} ref='loginFormRef'>
|
||||
<NFormItem
|
||||
label={this.t('login.userName')}
|
||||
label-style={{ color: 'black' }}
|
||||
path='userName'
|
||||
>
|
||||
<NInput
|
||||
type='text'
|
||||
size='large'
|
||||
v-model={[this.loginForm.userName, 'value']}
|
||||
placeholder={this.t('login.userName_tips')}
|
||||
autofocus
|
||||
onKeydown={withKeys(this.handleLogin, ['enter'])}
|
||||
/>
|
||||
</NFormItem>
|
||||
<NFormItem
|
||||
label={this.t('login.userPassword')}
|
||||
label-style={{ color: 'black' }}
|
||||
path='userPassword'
|
||||
>
|
||||
<NInput
|
||||
type='password'
|
||||
size='large'
|
||||
v-model={[this.loginForm.userPassword, 'value']}
|
||||
placeholder={this.t('login.userPassword_tips')}
|
||||
onKeydown={withKeys(this.handleLogin, ['enter'])}
|
||||
/>
|
||||
</NFormItem>
|
||||
</NForm>
|
||||
<NButton
|
||||
round
|
||||
type='info'
|
||||
style={{ width: '100%' }}
|
||||
onClick={this.handleLogin}
|
||||
>
|
||||
{this.t('login.login')}
|
||||
</NButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
export default login
|
||||
|
|
|
|||
Loading…
Reference in New Issue