nili
3 years ago
28 changed files with 722 additions and 476 deletions
@ -1,58 +1,26 @@ |
|||||
export default [ |
export default [ |
||||
{ |
{ |
||||
path: '/user', |
path: '/user', |
||||
layout: false, |
layout: false, |
||||
routes: [ |
routes: [ |
||||
{ |
{ path: '/user', routes: [{ name: '登录', path: '/user/login', component: './user/Login' }] }, |
||||
path: '/user', |
{ component: './404' }, |
||||
routes: [ |
|
||||
{ |
|
||||
name: 'login', |
|
||||
path: '/user/login', |
|
||||
component: './user/Login', |
|
||||
}, |
|
||||
], |
], |
||||
}, |
}, |
||||
{ |
{ path: '/welcome', name: '欢迎', icon: 'smile', component: './Welcome' }, |
||||
component: './404', |
|
||||
}, |
|
||||
], |
|
||||
}, |
|
||||
{ |
|
||||
path: '/welcome', |
|
||||
name: 'welcome', |
|
||||
icon: 'smile', |
|
||||
component: './Welcome', |
|
||||
}, |
|
||||
{ |
{ |
||||
path: '/admin', |
path: '/admin', |
||||
name: 'admin', |
name: '管理页', |
||||
icon: 'crown', |
icon: 'crown', |
||||
access: 'canAdmin', |
access: 'canAdmin', |
||||
component: './Admin', |
component: './Admin', |
||||
routes: [ |
routes: [ |
||||
{ |
{ path: '/admin/sub-page', name: '二级管理页', icon: 'smile', component: './Welcome' }, |
||||
path: '/admin/sub-page', |
{ component: './404' }, |
||||
name: 'sub-page', |
|
||||
icon: 'smile', |
|
||||
component: './Welcome', |
|
||||
}, |
|
||||
{ |
|
||||
component: './404', |
|
||||
}, |
|
||||
], |
], |
||||
}, |
}, |
||||
{ |
{ name: '查询表格', icon: 'table', path: '/list', component: './TableList' }, |
||||
name: 'list.table-list', |
{ path: '/', redirect: '/welcome' }, |
||||
icon: 'table', |
{ icon: 'smile', path: '/poem', component: './PoemPage' }, |
||||
path: '/list', |
{ component: './404' }, |
||||
component: './TableList', |
|
||||
}, |
|
||||
{ |
|
||||
path: '/', |
|
||||
redirect: '/welcome', |
|
||||
}, |
|
||||
{ |
|
||||
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