nili
3 years ago
28 changed files with 722 additions and 476 deletions
@ -1,58 +1,26 @@ |
|||
export default [ |
|||
export default [ |
|||
{ |
|||
path: '/user', |
|||
layout: false, |
|||
routes: [ |
|||
{ |
|||
path: '/user', |
|||
routes: [ |
|||
{ |
|||
name: 'login', |
|||
path: '/user/login', |
|||
component: './user/Login', |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
component: './404', |
|||
}, |
|||
{ path: '/user', routes: [{ name: '登录', path: '/user/login', component: './user/Login' }] }, |
|||
{ component: './404' }, |
|||
], |
|||
}, |
|||
{ |
|||
path: '/welcome', |
|||
name: 'welcome', |
|||
icon: 'smile', |
|||
component: './Welcome', |
|||
}, |
|||
{ path: '/welcome', name: '欢迎', icon: 'smile', component: './Welcome' }, |
|||
{ |
|||
path: '/admin', |
|||
name: 'admin', |
|||
name: '管理页', |
|||
icon: 'crown', |
|||
access: 'canAdmin', |
|||
component: './Admin', |
|||
routes: [ |
|||
{ |
|||
path: '/admin/sub-page', |
|||
name: 'sub-page', |
|||
icon: 'smile', |
|||
component: './Welcome', |
|||
}, |
|||
{ |
|||
component: './404', |
|||
}, |
|||
{ path: '/admin/sub-page', name: '二级管理页', icon: 'smile', component: './Welcome' }, |
|||
{ component: './404' }, |
|||
], |
|||
}, |
|||
{ |
|||
name: 'list.table-list', |
|||
icon: 'table', |
|||
path: '/list', |
|||
component: './TableList', |
|||
}, |
|||
{ |
|||
path: '/', |
|||
redirect: '/welcome', |
|||
}, |
|||
{ |
|||
component: './404', |
|||
}, |
|||
{ name: '查询表格', icon: 'table', path: '/list', component: './TableList' }, |
|||
{ path: '/', redirect: '/welcome' }, |
|||
{ icon: 'smile', path: '/poem', component: './PoemPage' }, |
|||
{ component: './404' }, |
|||
]; |
|||
|
@ -0,0 +1,28 @@ |
|||
import { seekUsingGET } from '@/services/luigi/author'; |
|||
import React from 'react'; |
|||
|
|||
import DebounceSelect from '../DebunceSelect'; |
|||
|
|||
const AuthorSelect: React.FC<{ |
|||
value?: string; |
|||
onChange?: (value: string) => void; |
|||
}> = (props) => { |
|||
return ( |
|||
<DebounceSelect |
|||
initOption={props.value} |
|||
fetchOptions={seekUsingGET} |
|||
onChange={(newValue) => { |
|||
if (typeof newValue == 'string') { |
|||
props.onChange(JSON.parse(newValue)); |
|||
} else { |
|||
props.onChange(newValue); |
|||
} |
|||
}} |
|||
style={{ |
|||
width: '100%', |
|||
}} |
|||
/> |
|||
); |
|||
}; |
|||
|
|||
export default AuthorSelect; |
@ -0,0 +1,66 @@ |
|||
import { Select, Spin } from 'antd'; |
|||
import { SelectProps } from 'antd/es/select'; |
|||
import debounce from 'lodash/debounce'; |
|||
import React from 'react'; |
|||
import { useEffect } from '../../.umi/.cache/.mfsu/mf-dep_vendors-node_modules_react_index_js.598bdd42.async'; |
|||
|
|||
export interface DebounceSelectProps<ValueType = any> |
|||
extends Omit<SelectProps<ValueType>, 'options' | 'children'> { |
|||
fetchOptions: (search: string) => Promise<ValueType[]>; |
|||
debounceTimeout?: number; |
|||
initOption?: string; |
|||
} |
|||
|
|||
function DebounceSelect< |
|||
ValueType extends { key?: string; label: React.ReactNode; value: string | number } = any, |
|||
>({ fetchOptions, debounceTimeout = 800, initOption, ...props }: DebounceSelectProps) { |
|||
const [fetching, setFetching] = React.useState(false); |
|||
const initOptions = []; |
|||
let defaultValue = null; |
|||
if (initOption && typeof initOption == 'string') { |
|||
const option = JSON.parse(initOption); |
|||
initOptions.push(option); |
|||
defaultValue = option.value; |
|||
} |
|||
const [options, setOptions] = React.useState<ValueType[]>(initOptions); |
|||
|
|||
const fetchRef = React.useRef(0); |
|||
|
|||
const debounceFetcher = React.useMemo(() => { |
|||
const loadOptions = (value: string | number) => { |
|||
fetchRef.current += 1; |
|||
const fetchId = fetchRef.current; |
|||
setOptions([]); |
|||
setFetching(true); |
|||
|
|||
fetchOptions({ query: value }).then((newOptions) => { |
|||
if (fetchId !== fetchRef.current) { |
|||
// for fetch callback order
|
|||
return; |
|||
} |
|||
const list = newOptions.data.list.map((user: { id: number; name: string }) => ({ |
|||
label: user.name, |
|||
value: user.id, |
|||
})); |
|||
setOptions(list); |
|||
setFetching(false); |
|||
}); |
|||
}; |
|||
return debounce(loadOptions, debounceTimeout); |
|||
}, [fetchOptions, debounceTimeout]); |
|||
|
|||
return ( |
|||
<Select<ValueType> |
|||
showSearch |
|||
labelInValue |
|||
filterOption={false} |
|||
defaultValue={defaultValue} |
|||
onSearch={debounceFetcher} |
|||
notFoundContent={fetching ? <Spin size="small" /> : null} |
|||
{...props} |
|||
options={options} |
|||
/> |
|||
); |
|||
} |
|||
|
|||
export default DebounceSelect; |
@ -0,0 +1,56 @@ |
|||
import React from 'react'; |
|||
import { Form, message } from 'antd'; |
|||
import ProForm, { |
|||
ProFormDateRangePicker, |
|||
ProFormSelect, |
|||
ProFormDatePicker, |
|||
} from '@ant-design/pro-form'; |
|||
import AuthorSelect from '@/components/AuthorSelect'; |
|||
|
|||
export default () => { |
|||
return ( |
|||
<ProForm |
|||
onFinish={async () => { |
|||
message.success('提交成功'); |
|||
}} |
|||
syncToUrl={(values, type) => { |
|||
if (type === 'get') { |
|||
// 为了配合 transform
|
|||
// startTime 和 endTime 拼成 createTimeRanger
|
|||
return { |
|||
...values, |
|||
createTimeRanger: |
|||
values.startTime || values.endTime ? [values.startTime, values.endTime] : undefined, |
|||
}; |
|||
} |
|||
// expirationTime 不同步到 url
|
|||
return { |
|||
...values, |
|||
expirationTime: undefined, |
|||
}; |
|||
}} |
|||
initialValues={{ |
|||
name: '蚂蚁设计有限公司', |
|||
useMode: 'chapter', |
|||
}} |
|||
autoFocusFirstInput |
|||
> |
|||
<ProFormSelect |
|||
options={[ |
|||
{ |
|||
value: 'chapter', |
|||
label: '盖章后生效', |
|||
}, |
|||
]} |
|||
width="sm" |
|||
name="useMode" |
|||
label="合同约定生效方式" |
|||
/> |
|||
<Form.Item name="authorId"> |
|||
{(form) => { |
|||
return <AuthorSelect />; |
|||
}} |
|||
</Form.Item> |
|||
</ProForm> |
|||
); |
|||
}; |
@ -0,0 +1,6 @@ |
|||
@import '~antd/es/style/themes/default.less'; |
|||
|
|||
.main { |
|||
width: 100%; |
|||
background: @component-background; |
|||
} |
@ -0,0 +1,138 @@ |
|||
import AuthorSelect from '@/components/AuthorSelect'; |
|||
import { poemListUsingPOST } from '@/services/luigi/poem'; |
|||
import { PlusOutlined } from '@ant-design/icons'; |
|||
import ProTable from '@ant-design/pro-table'; |
|||
import { Button } from 'antd'; |
|||
import React, { useRef, useState } from 'react'; |
|||
|
|||
import type { ProColumns, ActionType } from '@ant-design/pro-table'; |
|||
const columns: ProColumns<API.PoemBo>[] = [ |
|||
{ |
|||
dataIndex: 'index', |
|||
valueType: 'indexBorder', |
|||
width: 48, |
|||
}, |
|||
{ |
|||
title: '标题', |
|||
dataIndex: 'title', |
|||
ellipsis: true, |
|||
hideInSearch: true, |
|||
tip: '标题过长会自动收缩', |
|||
formItemProps: { |
|||
rules: [ |
|||
{ |
|||
required: true, |
|||
message: '此项为必填项', |
|||
}, |
|||
], |
|||
}, |
|||
}, |
|||
{ |
|||
title: '状态', |
|||
dataIndex: 'status', |
|||
valueType: 'select', |
|||
valueEnum: { |
|||
0: { |
|||
text: '未校准', |
|||
status: 'Error', |
|||
}, |
|||
1: { |
|||
text: '已校准', |
|||
status: 'Success', |
|||
}, |
|||
}, |
|||
}, |
|||
{ |
|||
title: '作者', |
|||
dataIndex: 'authorName', |
|||
hideInSearch: true, |
|||
}, |
|||
{ |
|||
title: '作者', |
|||
dataIndex: 'author', |
|||
hideInTable: true, |
|||
formItemProps: { |
|||
rules: [ |
|||
{ |
|||
required: true, |
|||
message: '此项为必填项', |
|||
}, |
|||
], |
|||
}, |
|||
renderFormItem: (item, { type, defaultRender, ...rest }, form) => { |
|||
return <AuthorSelect {...rest} />; |
|||
}, |
|||
}, |
|||
{ |
|||
title: '首行', |
|||
dataIndex: 'line', |
|||
hideInSearch: true, |
|||
}, |
|||
]; |
|||
|
|||
export default () => { |
|||
const actionRef = useRef<ActionType>(); |
|||
|
|||
return ( |
|||
<ProTable<API.PoemBo> |
|||
columns={columns} |
|||
actionRef={actionRef} |
|||
cardBordered |
|||
request={async (params = {}, sort, filter) => { |
|||
const query: API.QueryParam[] = []; |
|||
if (params.author) { |
|||
let author = params.author; |
|||
if (typeof params.author == 'string') { |
|||
author = JSON.parse(params.author); |
|||
} |
|||
query.push({ key: 'authorId', val: author.value }); |
|||
} |
|||
if (params.status) { |
|||
query.push({ key: 'status', val: params.status }); |
|||
} |
|||
const response = await poemListUsingPOST({ |
|||
current: params.current, |
|||
pageSize: params.pageSize, |
|||
query: query, |
|||
}); |
|||
return { data: response.data?.list, total: response.data?.total }; |
|||
}} |
|||
editable={{ |
|||
type: 'multiple', |
|||
}} |
|||
columnsState={{ |
|||
persistenceKey: 'pro-table-singe-demos', |
|||
persistenceType: 'localStorage', |
|||
onChange(value) { |
|||
console.log('value: ', value); |
|||
}, |
|||
}} |
|||
rowKey="id" |
|||
search={{ |
|||
labelWidth: 'auto', |
|||
}} |
|||
form={{ |
|||
// 由于配置了 transform,提交的参与与定义的不同这里需要转化一下
|
|||
syncToUrl: (values, type) => { |
|||
if (type === 'get') { |
|||
return { |
|||
...values, |
|||
created_at: [values.startTime, values.endTime], |
|||
}; |
|||
} |
|||
return values; |
|||
}, |
|||
}} |
|||
pagination={{ |
|||
pageSize: 5, |
|||
}} |
|||
dateFormatter="string" |
|||
headerTitle="高级表格" |
|||
toolBarRender={() => [ |
|||
<Button key="button" icon={<PlusOutlined />} type="primary"> |
|||
新建 |
|||
</Button>, |
|||
]} |
|||
/> |
|||
); |
|||
}; |
@ -0,0 +1,42 @@ |
|||
// @ts-ignore
|
|||
/* eslint-disable */ |
|||
import { request } from 'umi'; |
|||
|
|||
/** 获取规则列表 GET /api/rule */ |
|||
export async function rule( |
|||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|||
params: API.ruleParams, |
|||
options?: { [key: string]: any }, |
|||
) { |
|||
return request<API.RuleList>('/api/rule', { |
|||
method: 'GET', |
|||
params: { |
|||
...params, |
|||
}, |
|||
...(options || {}), |
|||
}); |
|||
} |
|||
|
|||
/** 新建规则 PUT /api/rule */ |
|||
export async function updateRule(options?: { [key: string]: any }) { |
|||
return request<API.RuleListItem>('/api/rule', { |
|||
method: 'PUT', |
|||
...(options || {}), |
|||
}); |
|||
} |
|||
|
|||
/** 新建规则 POST /api/rule */ |
|||
export async function addRule(options?: { [key: string]: any }) { |
|||
return request<API.RuleListItem>('/api/rule', { |
|||
method: 'POST', |
|||
...(options || {}), |
|||
}); |
|||
} |
|||
|
|||
/** 删除规则 DELETE /api/rule */ |
|||
export async function removeRule(options?: { [key: string]: any }) { |
|||
return request<Record<string, any>>('/api/rule', { |
|||
method: 'DELETE', |
|||
...(options || {}), |
|||
}); |
|||
} |
@ -0,0 +1,18 @@ |
|||
// @ts-ignore
|
|||
/* eslint-disable */ |
|||
import { request } from 'umi'; |
|||
|
|||
/** seek GET /api/luigi/author/seek */ |
|||
export async function seekUsingGET( |
|||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|||
params: API.seekUsingGETParams, |
|||
options?: { [key: string]: any }, |
|||
) { |
|||
return request<API.ResponseCommonListAuthor>('/api/luigi/author/seek', { |
|||
method: 'GET', |
|||
params: { |
|||
...params, |
|||
}, |
|||
...(options || {}), |
|||
}); |
|||
} |
@ -0,0 +1,12 @@ |
|||
// @ts-ignore
|
|||
/* eslint-disable */ |
|||
// API 更新时间:
|
|||
// API 唯一标识:
|
|||
import * as author from './author'; |
|||
import * as poem from './poem'; |
|||
import * as user from './user'; |
|||
export default { |
|||
author, |
|||
poem, |
|||
user, |
|||
}; |
@ -0,0 +1,30 @@ |
|||
// @ts-ignore
|
|||
/* eslint-disable */ |
|||
import { request } from 'umi'; |
|||
|
|||
/** poemDetail GET /api/luigi/poem/detail */ |
|||
export async function poemDetailUsingGET( |
|||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|||
params: API.poemDetailUsingGETParams, |
|||
options?: { [key: string]: any }, |
|||
) { |
|||
return request<API.ResponsePoemBo>('/api/luigi/poem/detail', { |
|||
method: 'GET', |
|||
params: { |
|||
...params, |
|||
}, |
|||
...(options || {}), |
|||
}); |
|||
} |
|||
|
|||
/** poemList POST /api/luigi/poem/list */ |
|||
export async function poemListUsingPOST(body: API.PoemQuery, options?: { [key: string]: any }) { |
|||
return request<API.ResponseCommonListPoemBo>('/api/luigi/poem/list', { |
|||
method: 'POST', |
|||
headers: { |
|||
'Content-Type': 'application/json', |
|||
}, |
|||
data: body, |
|||
...(options || {}), |
|||
}); |
|||
} |
@ -0,0 +1,88 @@ |
|||
declare namespace API { |
|||
type Author = { |
|||
dynasty?: string; |
|||
id?: number; |
|||
introduction?: string; |
|||
name?: string; |
|||
poemCnt?: number; |
|||
status?: number; |
|||
}; |
|||
|
|||
type CommonListAuthor = { |
|||
current?: number; |
|||
list?: Author[]; |
|||
pageSize?: number; |
|||
total?: number; |
|||
}; |
|||
|
|||
type CommonListPoemBo = { |
|||
current?: number; |
|||
list?: PoemBo[]; |
|||
pageSize?: number; |
|||
total?: number; |
|||
}; |
|||
|
|||
type PoemBo = { |
|||
authorId?: number; |
|||
authorName?: string; |
|||
content?: string; |
|||
id?: number; |
|||
line?: string; |
|||
note?: string; |
|||
status?: number; |
|||
title?: string; |
|||
translation?: string; |
|||
}; |
|||
|
|||
type PoemQuery = { |
|||
current?: number; |
|||
pageSize?: number; |
|||
query?: QueryParam[]; |
|||
}; |
|||
|
|||
type QueryParam = { |
|||
key?: string; |
|||
val?: string; |
|||
}; |
|||
|
|||
type ResponseCommonListAuthor = { |
|||
code?: number; |
|||
data?: CommonListAuthor; |
|||
message?: string; |
|||
}; |
|||
|
|||
type ResponseCommonListPoemBo = { |
|||
code?: number; |
|||
data?: CommonListPoemBo; |
|||
message?: string; |
|||
}; |
|||
|
|||
type ResponsePoemBo = { |
|||
code?: number; |
|||
data?: PoemBo; |
|||
message?: string; |
|||
}; |
|||
|
|||
type ResponseUserBo = { |
|||
code?: number; |
|||
data?: UserBo; |
|||
message?: string; |
|||
}; |
|||
|
|||
type UserBo = { |
|||
avatar?: string; |
|||
id?: number; |
|||
name?: string; |
|||
role?: string; |
|||
}; |
|||
|
|||
type seekUsingGETParams = { |
|||
/** name */ |
|||
name: string; |
|||
}; |
|||
|
|||
type poemDetailUsingGETParams = { |
|||
/** id */ |
|||
id: number; |
|||
}; |
|||
} |
@ -0,0 +1,11 @@ |
|||
// @ts-ignore
|
|||
/* eslint-disable */ |
|||
import { request } from 'umi'; |
|||
|
|||
/** getCurrent GET /api/luigi/user/current */ |
|||
export async function getCurrentUsingGET(options?: { [key: string]: any }) { |
|||
return request<API.ResponseUserBo>('/api/luigi/user/current', { |
|||
method: 'GET', |
|||
...(options || {}), |
|||
}); |
|||
} |
Loading…
Reference in new issue