Browse Source

金句

master
nili 3 years ago
parent
commit
b45ae53555
  1. 1
      config/routes.ts
  2. 1
      dist/471.bede4387.async.js
  3. 78
      dist/502.219197d7.async.js
  4. 78
      dist/765.0e611d9e.async.js
  5. 4
      dist/765.9563962e.chunk.css
  6. 14
      dist/asset-manifest.json
  7. 2
      dist/author/index.html
  8. 2
      dist/index.html
  9. 1
      dist/p__AuthorPage.13dc7e47.async.js
  10. 1
      dist/p__AuthorPage.aa0c0ad9.async.js
  11. 1
      dist/p__PoemPage.661ee886.chunk.css
  12. 1
      dist/p__PoemPage.87b66799.async.js
  13. 1
      dist/p__PoemPage.e0b147d0.async.js
  14. 1
      dist/p__VersePage.1bfa01c7.async.js
  15. 1
      dist/p__VersePage.661ee886.chunk.css
  16. 2
      dist/poem/index.html
  17. 40
      dist/umi.bef416f0.js
  18. 254
      dist/verse/index.html
  19. 6
      src/components/AuthorSelect/index.tsx
  20. 53
      src/components/PoemSelect.tsx
  21. 8
      src/pages/AuthorPage/components/AuthorForm.tsx
  22. 28
      src/pages/AuthorPage/index.tsx
  23. 34
      src/pages/PoemPage/components/PoemCard.tsx
  24. 4
      src/pages/PoemPage/components/PoemForm.tsx
  25. 16
      src/pages/PoemPage/index.tsx
  26. 73
      src/pages/VersePage/components/VerseForm.tsx
  27. 174
      src/pages/VersePage/index.tsx
  28. 6
      src/services/luigi/author.ts
  29. 2
      src/services/luigi/index.ts
  30. 15
      src/services/luigi/poem.ts
  31. 43
      src/services/luigi/typings.d.ts
  32. 45
      src/services/luigi/verse.ts

1
config/routes.ts

@ -23,5 +23,6 @@ export default [
{ path: '/', redirect: '/poem' }, { path: '/', redirect: '/poem' },
{ name: '作者', icon: 'smile', path: '/author', component: './AuthorPage' }, { name: '作者', icon: 'smile', path: '/author', component: './AuthorPage' },
{ name: '诗词', icon: 'smile', path: '/poem', component: './PoemPage' }, { name: '诗词', icon: 'smile', path: '/poem', component: './PoemPage' },
{ name: '金句', icon: 'smile', path: '/verse', component: './VersePage' },
{ component: './404' }, { component: './404' },
]; ];

1
dist/471.bede4387.async.js

File diff suppressed because one or more lines are too long

78
dist/502.219197d7.async.js

File diff suppressed because one or more lines are too long

78
dist/765.0e611d9e.async.js

File diff suppressed because one or more lines are too long

4
dist/502.7bff7c20.chunk.css → dist/765.9563962e.chunk.css

File diff suppressed because one or more lines are too long

14
dist/asset-manifest.json

@ -1,17 +1,21 @@
{ {
"/umi.css": "/umi.3237de05.css", "/umi.css": "/umi.3237de05.css",
"/umi.js": "/umi.c65a8521.js", "/umi.js": "/umi.bef416f0.js",
"/t__plugin-layout__Layout.css": "/t__plugin-layout__Layout.44b8ae54.chunk.css", "/t__plugin-layout__Layout.css": "/t__plugin-layout__Layout.44b8ae54.chunk.css",
"/t__plugin-layout__Layout.js": "/t__plugin-layout__Layout.85d3f9bc.async.js", "/t__plugin-layout__Layout.js": "/t__plugin-layout__Layout.85d3f9bc.async.js",
"/p__AuthorPage.js": "/p__AuthorPage.13dc7e47.async.js", "/p__AuthorPage.js": "/p__AuthorPage.aa0c0ad9.async.js",
"/p__PoemPage.js": "/p__PoemPage.87b66799.async.js", "/p__PoemPage.css": "/p__PoemPage.661ee886.chunk.css",
"/p__PoemPage.js": "/p__PoemPage.e0b147d0.async.js",
"/p__VersePage.css": "/p__VersePage.661ee886.chunk.css",
"/p__VersePage.js": "/p__VersePage.1bfa01c7.async.js",
"/p__404.css": "/p__404.572eeed8.chunk.css", "/p__404.css": "/p__404.572eeed8.chunk.css",
"/p__404.js": "/p__404.341bc6c9.async.js", "/p__404.js": "/p__404.341bc6c9.async.js",
"/799.f321cbac.async.js": "/799.f321cbac.async.js", "/799.f321cbac.async.js": "/799.f321cbac.async.js",
"/105.2ed7a7f1.chunk.css": "/105.2ed7a7f1.chunk.css", "/105.2ed7a7f1.chunk.css": "/105.2ed7a7f1.chunk.css",
"/105.a25c9d08.async.js": "/105.a25c9d08.async.js", "/105.a25c9d08.async.js": "/105.a25c9d08.async.js",
"/502.7bff7c20.chunk.css": "/502.7bff7c20.chunk.css", "/765.9563962e.chunk.css": "/765.9563962e.chunk.css",
"/502.219197d7.async.js": "/502.219197d7.async.js", "/765.0e611d9e.async.js": "/765.0e611d9e.async.js",
"/471.bede4387.async.js": "/471.bede4387.async.js",
"/icons/icon-512x512.png": "/icons/icon-512x512.png", "/icons/icon-512x512.png": "/icons/icon-512x512.png",
"/favicon.ico": "/favicon.ico", "/favicon.ico": "/favicon.ico",
"/logo.svg": "/logo.svg", "/logo.svg": "/logo.svg",

2
dist/author/index.html

@ -249,6 +249,6 @@
</div> </div>
</div> </div>
<script src="/umi.c65a8521.js"></script> <script src="/umi.bef416f0.js"></script>
</body> </body>
</html> </html>

2
dist/index.html

@ -249,6 +249,6 @@
</div> </div>
</div> </div>
<script src="/umi.c65a8521.js"></script> <script src="/umi.bef416f0.js"></script>
</body> </body>
</html> </html>

1
dist/p__AuthorPage.13dc7e47.async.js

File diff suppressed because one or more lines are too long

1
dist/p__AuthorPage.aa0c0ad9.async.js

File diff suppressed because one or more lines are too long

1
dist/p__PoemPage.661ee886.chunk.css

File diff suppressed because one or more lines are too long

1
dist/p__PoemPage.87b66799.async.js

File diff suppressed because one or more lines are too long

1
dist/p__PoemPage.e0b147d0.async.js

File diff suppressed because one or more lines are too long

1
dist/p__VersePage.1bfa01c7.async.js

@ -0,0 +1 @@
(self.webpackChunkant_design_pro=self.webpackChunkant_design_pro||[]).push([[507],{61173:function(C,f,t){"use strict";t.r(f);var B=t(57663),D=t(71577),U=t(62350),P=t(75443),h=t(3182),o=t(11849),v=t(93224),A=t(402),I=t(61859),M=t(94043),u=t.n(M),T=t(79667),y=t(97830),c=t(26697),R=t(39750),g=t(67294),O=t(579),n=t(85893),j=[{dataIndex:"id",title:"id",width:"10%"},{title:"\u91D1\u53E5",dataIndex:"content",hideInSearch:!0,width:"30%"},{title:"\u8BD7\u8BCD\u6807\u9898",dataIndex:"title",ellipsis:!0,hideInSearch:!0,render:function(_,e){return(0,n.jsx)(I.Z.Link,{target:"_blank",href:"/poem?id="+e.originId,children:e.title})}},{title:"\u4F5C\u8005",dataIndex:"authorName",ellipsis:!0,hideInSearch:!0,render:function(_,e){return(0,n.jsx)(I.Z.Link,{target:"_blank",href:"/author?id="+e.authorId,children:e.authorName})}},{title:"\u4F5C\u8005",dataIndex:"authorId",hideInTable:!0,renderFormItem:function(_,e){var d=e.type,r=e.defaultRender,s=(0,v.Z)(e,["type","defaultRender"]);return(0,n.jsx)(T.Z,(0,o.Z)({},s))}},{title:"\u8BD7\u8BCD",dataIndex:"originId",hideInTable:!0,renderFormItem:function(_,e){var d=e.type,r=e.defaultRender,s=(0,v.Z)(e,["type","defaultRender"]);return(0,n.jsx)(y.Z,(0,o.Z)({},s))}},{title:"\u72B6\u6001",dataIndex:"status",valueType:"select",valueEnum:{0:{text:"\u672A\u6821\u51C6",status:"Error"},1:{text:"\u5DF2\u6821\u51C6",status:"Success"}}},{title:"\u64CD\u4F5C",valueType:"option",key:"option",render:function(_,e,d,r){return[(0,n.jsx)(O.Z,{verseId:e.id,formTitle:e.title||"\u7F16\u8F91",trigger:(0,n.jsx)("a",{href:"#",children:"\u7F16\u8F91"}),refresh:function(){return r==null?void 0:r.reload()}},e.id),(0,n.jsx)(P.Z,{title:"\u786E\u5B9A\u5220\u9664\u5417?",onConfirm:(0,h.Z)(u().mark(function s(){return u().wrap(function(a){for(;;)switch(a.prev=a.next){case 0:return a.next=2,(0,c.px)({id:e.id,status:-1});case 2:r==null||r.reload();case 3:case"end":return a.stop()}},s)})),okText:"Yes",cancelText:"No",children:(0,n.jsx)("a",{href:"#",children:"\u5220\u9664"})},"delete"+e.id),(0,n.jsx)(P.Z,{title:"\u786E\u5B9A\u6807\u8BB0\u5417?",onConfirm:(0,h.Z)(u().mark(function s(){return u().wrap(function(a){for(;;)switch(a.prev=a.next){case 0:return a.next=2,(0,c.px)({id:e.id,status:e.status==0?1:0});case 2:r==null||r.reload();case 3:case"end":return a.stop()}},s)})),okText:"Yes",cancelText:"No",children:(0,n.jsx)("a",{href:"#",children:e.status==0?"\u6821\u51C6":"\u53D6\u6D88\u6821\u51C6"})},"mark_"+e.id)]}}];f.default=function(){var i=(0,g.useRef)();return(0,n.jsx)(R.ZP,{columns:j,actionRef:i,cardBordered:!0,request:(0,h.Z)(u().mark(function _(){var e,d,r,s,m,a,p=arguments;return u().wrap(function(l){for(;;)switch(l.prev=l.next){case 0:return r=p.length>0&&p[0]!==void 0?p[0]:{},s=[],m=["id","authorId","status","originId"],Object.keys(r).forEach(function(E){m.includes(E)&&r[E]&&s.push({key:E,val:r[E]})}),l.next=6,(0,c.c)({current:r.current,pageSize:r.pageSize,query:s});case 6:return a=l.sent,l.abrupt("return",{data:(e=a.data)===null||e===void 0?void 0:e.list,total:(d=a.data)===null||d===void 0?void 0:d.total});case 8:case"end":return l.stop()}},_)})),columnsState:{persistenceKey:"pro-table-singe-demos",persistenceType:"localStorage"},rowKey:"id",search:{labelWidth:"auto"},pagination:{pageSize:20},form:{syncToUrl:function(e,d){return d==="get"?(0,o.Z)((0,o.Z)({},e),{},{created_at:[e.startTime,e.endTime]}):e}},headerTitle:"\u5217\u8868",dateFormatter:"string",toolBarRender:function(){return[(0,n.jsx)(O.Z,{trigger:(0,n.jsx)(D.Z,{type:"primary",children:"\u7F16\u8F91"}),formTitle:"\u521B\u5EFA"},"new")]}})}}}]);

1
dist/p__VersePage.661ee886.chunk.css

File diff suppressed because one or more lines are too long

2
dist/poem/index.html

@ -249,6 +249,6 @@
</div> </div>
</div> </div>
<script src="/umi.c65a8521.js"></script> <script src="/umi.bef416f0.js"></script>
</body> </body>
</html> </html>

40
dist/umi.c65a8521.js → dist/umi.bef416f0.js

File diff suppressed because one or more lines are too long

254
dist/verse/index.html

@ -0,0 +1,254 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="theme-color" content="#1890ff" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta
name="keywords"
content="antd,umi,umijs,ant design,Scaffolding, layout, Ant Design, project, Pro, admin, console, homepage, out-of-the-box, middle and back office, solution, component library"
/>
<meta
name="description"
content="
An out-of-box UI solution for enterprise applications as a React boilerplate."
/>
<meta
name="description"
content="
Out-of-the-box mid-stage front-end/design solution."
/>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
/>
<title>Ant Design Pro</title>
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
<link rel="stylesheet" href="/umi.3237de05.css" />
<script>
window.routerBase = "/";
</script>
<script>
//! umi version: 3.5.21
</script>
</head>
<body>
<noscript>
<div class="noscript-container">
Hi there! Please
<div class="noscript-enableJS">
<a
href="https://www.enablejavascript.io/en"
target="_blank"
rel="noopener noreferrer"
>
<b>enable Javascript</b>
</a>
</div>
in your browser to use Ant Design, Out-of-the-box mid-stage front/design
solution!
</div>
</noscript>
<div id="root">
<style>
html,
body,
#root {
height: 100%;
margin: 0;
padding: 0;
}
#root {
background-repeat: no-repeat;
background-size: 100% auto;
}
.noscript-container {
display: flex;
align-content: center;
justify-content: center;
margin-top: 90px;
font-size: 20px;
font-family: "Lucida Sans", "Lucida Sans Regular", "Lucida Grande",
"Lucida Sans Unicode", Geneva, Verdana, sans-serif;
}
.noscript-enableJS {
padding-right: 3px;
padding-left: 3px;
}
.page-loading-warp {
display: flex;
align-items: center;
justify-content: center;
padding: 98px;
}
.ant-spin {
position: absolute;
display: none;
-webkit-box-sizing: border-box;
box-sizing: border-box;
margin: 0;
padding: 0;
color: rgba(0, 0, 0, 0.65);
color: #1890ff;
font-size: 14px;
font-variant: tabular-nums;
line-height: 1.5;
text-align: center;
list-style: none;
opacity: 0;
-webkit-transition: -webkit-transform 0.3s
cubic-bezier(0.78, 0.14, 0.15, 0.86);
transition: -webkit-transform 0.3s
cubic-bezier(0.78, 0.14, 0.15, 0.86);
transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86),
-webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
-webkit-font-feature-settings: "tnum";
font-feature-settings: "tnum";
}
.ant-spin-spinning {
position: static;
display: inline-block;
opacity: 1;
}
.ant-spin-dot {
position: relative;
display: inline-block;
width: 20px;
height: 20px;
font-size: 20px;
}
.ant-spin-dot-item {
position: absolute;
display: block;
width: 9px;
height: 9px;
background-color: #1890ff;
border-radius: 100%;
-webkit-transform: scale(0.75);
-ms-transform: scale(0.75);
transform: scale(0.75);
-webkit-transform-origin: 50% 50%;
-ms-transform-origin: 50% 50%;
transform-origin: 50% 50%;
opacity: 0.3;
-webkit-animation: antspinmove 1s infinite linear alternate;
animation: antSpinMove 1s infinite linear alternate;
}
.ant-spin-dot-item:nth-child(1) {
top: 0;
left: 0;
}
.ant-spin-dot-item:nth-child(2) {
top: 0;
right: 0;
-webkit-animation-delay: 0.4s;
animation-delay: 0.4s;
}
.ant-spin-dot-item:nth-child(3) {
right: 0;
bottom: 0;
-webkit-animation-delay: 0.8s;
animation-delay: 0.8s;
}
.ant-spin-dot-item:nth-child(4) {
bottom: 0;
left: 0;
-webkit-animation-delay: 1.2s;
animation-delay: 1.2s;
}
.ant-spin-dot-spin {
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
-webkit-animation: antrotate 1.2s infinite linear;
animation: antRotate 1.2s infinite linear;
}
.ant-spin-lg .ant-spin-dot {
width: 32px;
height: 32px;
font-size: 32px;
}
.ant-spin-lg .ant-spin-dot i {
width: 14px;
height: 14px;
}
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
.ant-spin-blur {
background: #fff;
opacity: 0.5;
}
}
@-webkit-keyframes antSpinMove {
to {
opacity: 1;
}
}
@keyframes antSpinMove {
to {
opacity: 1;
}
}
@-webkit-keyframes antRotate {
to {
-webkit-transform: rotate(405deg);
transform: rotate(405deg);
}
}
@keyframes antRotate {
to {
-webkit-transform: rotate(405deg);
transform: rotate(405deg);
}
}
</style>
<div
style="
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
min-height: 420px;
"
>
<img src="/pro_icon.svg" alt="logo" width="256" />
<div class="page-loading-warp">
<div class="ant-spin ant-spin-lg ant-spin-spinning">
<span class="ant-spin-dot ant-spin-dot-spin"
><i class="ant-spin-dot-item"></i><i class="ant-spin-dot-item"></i
><i class="ant-spin-dot-item"></i><i class="ant-spin-dot-item"></i
></span>
</div>
</div>
<div
style="display: flex; align-items: center; justify-content: center"
>
<img
src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg"
width="32"
style="margin-right: 8px"
/>
Ant Design
</div>
</div>
</div>
<script src="/umi.bef416f0.js"></script>
</body>
</html>

6
src/components/AuthorSelect/index.tsx

@ -1,9 +1,9 @@
import { getAuthorByIdUsingGET, seekUsingGET } from '@/services/luigi/author'; import { getAuthorByIdUsingGET, seekAuthorUsingGET } from '@/services/luigi/author';
import React from 'react'; import React from 'react';
import type { ValueType } from '../DebunceSelect';
import DebounceSelect from '../DebunceSelect'; import DebounceSelect from '../DebunceSelect';
import type { ValueType } from '../DebunceSelect';
const AuthorSelect: React.FC<{ const AuthorSelect: React.FC<{
value?: number; value?: number;
onChange?: (value: ValueType) => void; onChange?: (value: ValueType) => void;
@ -31,7 +31,7 @@ const AuthorSelect: React.FC<{
<DebounceSelect <DebounceSelect
initOption={initOption} initOption={initOption}
fetchOptions={async (query) => { fetchOptions={async (query) => {
const response = await seekUsingGET({ const response = await seekAuthorUsingGET({
name: query, name: query,
}); });
if (response.data?.list) { if (response.data?.list) {

53
src/components/PoemSelect.tsx

@ -0,0 +1,53 @@
import { poemDetailUsingGET, seekPoemUsingGET } from '@/services/luigi/poem';
import React from 'react';
import DebounceSelect from './DebunceSelect';
import type { ValueType } from './DebunceSelect';
const PoemSelect: React.FC<{
value?: number;
onChange?: (value: ValueType) => void;
}> = (props) => {
const [initOption, setInitOption] = React.useState();
React.useEffect(() => {
if (!props.value) {
return;
}
const fetch = async () => {
const response = await poemDetailUsingGET({ id: props.value });
const option: ValueType = {
key: response.data?.id,
label: response.data?.title,
value: response.data?.id,
};
setInitOption(option);
};
fetch();
}, [props.value]);
if (props.value && !initOption) {
return null;
}
return (
<DebounceSelect
initOption={initOption}
fetchOptions={async (query) => {
const response = await seekPoemUsingGET({
title: query,
});
if (response.data?.list) {
return response.data.list?.map((poem) => {
const data: ValueType = { label: poem.title, value: poem.id, key: poem.id };
return data;
});
}
return undefined;
}}
onChange={props.onChange}
style={{
width: '100%',
}}
/>
);
};
export default PoemSelect;

8
src/pages/AuthorPage/components/AuthorForm.tsx

@ -6,18 +6,18 @@ import { getAuthorByIdUsingGET, saveAuthorUsingPOST } from '../../../services/lu
import type { ProFormInstance } from '@ant-design/pro-form'; import type { ProFormInstance } from '@ant-design/pro-form';
const AuthorForm: React.FC<{ const AuthorForm: React.FC<{
triggerText: string; trigger: JSX.Element;
formTitle: string; formTitle: string;
authorId?: number; authorId?: number;
refresh?: () => void; refresh?: () => void;
}> = (props) => { }> = (props) => {
const formRef = useRef<ProFormInstance<API.PoemBo>>(); const formRef = useRef<ProFormInstance<API.Author>>();
return ( return (
<DrawerForm<API.PoemBo> <DrawerForm<API.Author>
title={props.formTitle} title={props.formTitle}
formRef={formRef} formRef={formRef}
layout="horizontal" layout="horizontal"
trigger={<Button type="primary">{props.triggerText}</Button>} trigger={props.trigger}
autoFocusFirstInput autoFocusFirstInput
drawerProps={{ drawerProps={{
destroyOnClose: true, destroyOnClose: true,

28
src/pages/AuthorPage/index.tsx

@ -1,32 +1,26 @@
import AuthorSelect from '@/components/AuthorSelect'; import AuthorSelect from '@/components/AuthorSelect';
import { poemListUsingPOST } from '@/services/luigi/poem'; import { saveAuthorUsingPOST } from '@/services/luigi/author';
import ProTable from '@ant-design/pro-table'; import ProTable from '@ant-design/pro-table';
import { Popconfirm } from 'antd'; import { Button, Popconfirm, Typography, Input } from 'antd';
import React, { useRef } from 'react'; import React, { useRef } from 'react';
import { saveAuthorUsingPOST } from '@/services/luigi/author';
import AuthorForm from './components/AuthorForm';
import { authorListUsingPOST } from '../../services/luigi/author'; import { authorListUsingPOST } from '../../services/luigi/author';
import AuthorForm from './components/AuthorForm';
import type { ProColumns, ActionType } from '@ant-design/pro-table'; import type { ProColumns, ActionType } from '@ant-design/pro-table';
const columns: ProColumns<API.PoemBo>[] = [ const columns: ProColumns<API.PoemBo>[] = [
{ {
dataIndex: 'id', dataIndex: 'id',
title: 'id', title: 'id',
renderFormItem: (item, { type, defaultRender, ...rest }) => {
return <Input key={'id_' + item} {...rest} />;
},
}, },
{ {
title: '名字', title: '名字',
dataIndex: 'name', dataIndex: 'name',
ellipsis: true, ellipsis: true,
hideInSearch: true, hideInSearch: true,
formItemProps: {
rules: [
{
required: true,
message: '此项为必填项',
},
],
},
}, },
{ {
title: '朝代', title: '朝代',
@ -63,9 +57,9 @@ const columns: ProColumns<API.PoemBo>[] = [
hideInSearch: true, hideInSearch: true,
render: (item, record) => { render: (item, record) => {
return ( return (
<a target="blank" href={'/poem?authorId=' + record.id}> <Typography.Link key={record.id} target="_blank" href={'/poem?authorId=' + record.id}>
{item} {item}
</a> </Typography.Link>
); );
}, },
}, },
@ -78,7 +72,7 @@ const columns: ProColumns<API.PoemBo>[] = [
key={record.id} key={record.id}
authorId={record.id} authorId={record.id}
formTitle={record.title || '编辑'} formTitle={record.title || '编辑'}
triggerText="编辑" trigger={<a href="#"></a>}
refresh={() => action?.reload()} refresh={() => action?.reload()}
/>, />,
<Popconfirm <Popconfirm
@ -158,7 +152,9 @@ export default () => {
}} }}
headerTitle="列表" headerTitle="列表"
dateFormatter="string" dateFormatter="string"
toolBarRender={() => [<AuthorForm key="new" triggerText="新建" formTitle="创建" />]} toolBarRender={() => [
<AuthorForm key="new" trigger={<Button type="primary"></Button>} formTitle="创建" />,
]}
/> />
); );
}; };

34
src/pages/PoemPage/components/PoemCard.tsx

@ -0,0 +1,34 @@
import { poemDetailUsingGET } from '@/services/luigi/poem';
import { Card } from 'antd';
import React from 'react';
const PoemCard: React.FC<{
poemId?: number;
}> = (props) => {
const [poem, setPoem] = React.useState<API.PoemBo>();
React.useEffect(() => {
if (!props.poemId) {
return;
}
const fetch = async () => {
const response = await poemDetailUsingGET({ id: props.poemId });
setPoem(response.data);
};
fetch();
}, [props.poemId]);
if (!props.poemId || !poem) {
return null;
}
return (
<Card
size="small"
title={poem?.title}
style={{ margin: '0 auto 20px', width: '50%', textAlign: 'center' }}
extra={poem.authorName}
>
<p style={{ whiteSpace: 'break-spaces' }}>{poem?.content}</p>
</Card>
);
};
export default PoemCard;

4
src/pages/PoemPage/components/PoemForm.tsx

@ -7,7 +7,7 @@ import React, { useRef } from 'react';
import type { ProFormInstance } from '@ant-design/pro-form'; import type { ProFormInstance } from '@ant-design/pro-form';
const PoemForm: React.FC<{ const PoemForm: React.FC<{
triggerText: string; trigger: JSX.Element;
formTitle: string; formTitle: string;
poemId?: number; poemId?: number;
refresh?: () => void; refresh?: () => void;
@ -18,7 +18,7 @@ const PoemForm: React.FC<{
title={props.formTitle} title={props.formTitle}
formRef={formRef} formRef={formRef}
layout="horizontal" layout="horizontal"
trigger={<Button type="primary">{props.triggerText}</Button>} trigger={props.trigger}
autoFocusFirstInput autoFocusFirstInput
drawerProps={{ drawerProps={{
destroyOnClose: true, destroyOnClose: true,

16
src/pages/PoemPage/index.tsx

@ -6,8 +6,9 @@ import React, { useRef } from 'react';
import PoemForm from './components/PoemForm'; import PoemForm from './components/PoemForm';
import type { ProColumns, ActionType } from '@ant-design/pro-table'; import type { ProColumns, ActionType } from '@ant-design/pro-table';
import { Popconfirm } from 'antd'; import { Button, Popconfirm } from 'antd';
import { savePoemUsingPOST } from '../../services/luigi/poem'; import { savePoemUsingPOST } from '../../services/luigi/poem';
import VerseForm from '../VersePage/components/VerseForm';
const columns: ProColumns<API.PoemBo>[] = [ const columns: ProColumns<API.PoemBo>[] = [
{ {
dataIndex: 'id', dataIndex: 'id',
@ -67,14 +68,21 @@ const columns: ProColumns<API.PoemBo>[] = [
title: '操作', title: '操作',
valueType: 'option', valueType: 'option',
key: 'option', key: 'option',
width: '25%',
render: (text, record, _, action) => [ render: (text, record, _, action) => [
<PoemForm <PoemForm
key={record.id} key={record.id}
poemId={record.id} poemId={record.id}
formTitle={record.title || '编辑'} formTitle={record.title || '编辑'}
triggerText="编辑" trigger={<a href="#"></a>}
refresh={() => action?.reload()} refresh={() => action?.reload()}
/>, />,
<VerseForm
originId={record.id}
key={'add_verse_' + record.id}
formTitle={record.title || '编辑'}
trigger={<a href="#"></a>}
/>,
<Popconfirm <Popconfirm
title="确定删除吗?" title="确定删除吗?"
key={'delete' + record.id} key={'delete' + record.id}
@ -152,7 +160,9 @@ export default () => {
}} }}
headerTitle="列表" headerTitle="列表"
dateFormatter="string" dateFormatter="string"
toolBarRender={() => [<PoemForm key="new" triggerText="新建" formTitle="创建" />]} toolBarRender={() => [
<PoemForm trigger={<Button type="primary"></Button>} key="new" formTitle="创建" />,
]}
/> />
); );
}; };

73
src/pages/VersePage/components/VerseForm.tsx

@ -0,0 +1,73 @@
import PoemSelect from '@/components/PoemSelect';
import PoemCard from '@/pages/PoemPage/components/PoemCard';
import { getVerseByIdUsingGET, saveVerseUsingPOST } from '@/services/luigi/verse';
import { DrawerForm, ProFormDependency, ProFormTextArea } from '@ant-design/pro-form';
import { Button, Form, message } from 'antd';
import React, { ReactNode, useRef } from 'react';
import type { ProFormInstance } from '@ant-design/pro-form';
const VerseForm: React.FC<{
trigger: JSX.Element;
formTitle: string;
verseId?: number;
originId?: number;
refresh?: () => void;
}> = (props) => {
const formRef = useRef<ProFormInstance<API.Verse>>();
return (
<div>
<DrawerForm<API.Verse>
title={props.formTitle}
formRef={formRef}
layout="horizontal"
trigger={props.trigger}
autoFocusFirstInput
drawerProps={{
destroyOnClose: true,
}}
onVisibleChange={async (visible: boolean) => {
if (!visible) {
return;
}
if (!props.verseId) {
return;
}
const response = await getVerseByIdUsingGET({ id: props.verseId });
if (response.data) {
formRef.current?.setFieldsValue(response.data);
}
}}
onFinish={async (values) => {
const rsp = await saveVerseUsingPOST({ ...values, id: props.verseId });
if (rsp.code) {
message.success('提交成功');
if (props.refresh) {
props.refresh();
}
return true;
}
return false;
}}
>
<ProFormDependency name={['originId']}>
{({ originId }) => {
return <PoemCard poemId={props.originId || originId} />;
}}
</ProFormDependency>
<Form.Item
initialValue={props.originId}
style={{ width: '40%' }}
label="诗词"
name="originId"
shouldUpdate
>
<PoemSelect />
</Form.Item>
<ProFormTextArea width="md" name="content" label="金句" placeholder="请输入" />
</DrawerForm>
</div>
);
};
export default VerseForm;

174
src/pages/VersePage/index.tsx

@ -0,0 +1,174 @@
import AuthorSelect from '@/components/AuthorSelect';
import PoemSelect from '@/components/PoemSelect';
import { saveVerseUsingPOST, verseListUsingPOST } from '@/services/luigi/verse';
import ProTable from '@ant-design/pro-table';
import { Button, Popconfirm, Typography } from 'antd';
import React, { useRef } from 'react';
import VerseForm from './components/VerseForm';
import type { ProColumns, ActionType } from '@ant-design/pro-table';
const columns: ProColumns<API.PoemBo>[] = [
{
dataIndex: 'id',
title: 'id',
width: '10%',
},
{
title: '金句',
dataIndex: 'content',
hideInSearch: true,
width: '30%',
},
{
title: '诗词标题',
dataIndex: 'title',
ellipsis: true,
hideInSearch: true,
render: (item, record) => {
return (
<Typography.Link target="_blank" href={'/poem?id=' + record.originId}>
{record.title}
</Typography.Link>
);
},
},
{
title: '作者',
dataIndex: 'authorName',
ellipsis: true,
hideInSearch: true,
render: (item, record) => {
return (
<Typography.Link target="_blank" href={'/author?id=' + record.authorId}>
{record.authorName}
</Typography.Link>
);
},
},
{
title: '作者',
dataIndex: 'authorId',
hideInTable: true,
renderFormItem: (item, { type, defaultRender, ...rest }) => {
return <AuthorSelect {...rest} />;
},
},
{
title: '诗词',
dataIndex: 'originId',
hideInTable: true,
renderFormItem: (item, { type, defaultRender, ...rest }) => {
return <PoemSelect {...rest} />;
},
},
{
title: '状态',
dataIndex: 'status',
valueType: 'select',
valueEnum: {
0: {
text: '未校准',
status: 'Error',
},
1: {
text: '已校准',
status: 'Success',
},
},
},
{
title: '操作',
valueType: 'option',
key: 'option',
render: (text, record, _, action) => [
<VerseForm
key={record.id}
verseId={record.id}
formTitle={record.title || '编辑'}
trigger={<a href="#"></a>}
refresh={() => action?.reload()}
/>,
<Popconfirm
title="确定删除吗?"
key={'delete' + record.id}
onConfirm={async () => {
await saveVerseUsingPOST({ id: record.id, status: -1 });
action?.reload();
}}
okText="Yes"
cancelText="No"
>
<a href="#"></a>
</Popconfirm>,
<Popconfirm
title="确定标记吗?"
key={'mark_' + record.id}
onConfirm={async () => {
await saveVerseUsingPOST({ id: record.id, status: record.status == 0 ? 1 : 0 });
action?.reload();
}}
okText="Yes"
cancelText="No"
>
<a href="#">{record.status == 0 ? '校准' : '取消校准'}</a>
</Popconfirm>,
],
},
];
export default () => {
const actionRef = useRef<ActionType>();
return (
<ProTable<API.PoemBo>
columns={columns}
actionRef={actionRef}
cardBordered
request={async (params = {}) => {
const query: API.QueryParam[] = [];
const allowedKey = ['id', 'authorId', 'status', 'originId'];
Object.keys(params).forEach((x) => {
if (allowedKey.includes(x) && params[x]) {
query.push({ key: x, val: params[x] });
}
});
const response = await verseListUsingPOST({
current: params.current,
pageSize: params.pageSize,
query: query,
});
return { data: response.data?.list, total: response.data?.total };
}}
columnsState={{
persistenceKey: 'pro-table-singe-demos',
persistenceType: 'localStorage',
}}
rowKey="id"
search={{
labelWidth: 'auto',
}}
pagination={{
pageSize: 20,
}}
form={{
// 由于配置了 transform,提交的参与与定义的不同这里需要转化一下
syncToUrl: (values, type) => {
if (type === 'get') {
return {
...values,
created_at: [values.startTime, values.endTime],
};
}
return values;
},
}}
headerTitle="列表"
dateFormatter="string"
toolBarRender={() => [
<VerseForm key="new" trigger={<Button type="primary"></Button>} formTitle="创建" />,
]}
/>
);
};

6
src/services/luigi/author.ts

@ -44,10 +44,10 @@ export async function saveAuthorUsingPOST(body: API.Author, options?: { [key: st
}); });
} }
/** seek GET /api/luigi/author/seek */ /** seekAuthor GET /api/luigi/author/seek */
export async function seekUsingGET( export async function seekAuthorUsingGET(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象) // 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: API.seekUsingGETParams, params: API.seekAuthorUsingGETParams,
options?: { [key: string]: any }, options?: { [key: string]: any },
) { ) {
return request<API.ResponseCommonListAuthor>('/api/luigi/author/seek', { return request<API.ResponseCommonListAuthor>('/api/luigi/author/seek', {

2
src/services/luigi/index.ts

@ -5,8 +5,10 @@
import * as author from './author'; import * as author from './author';
import * as poem from './poem'; import * as poem from './poem';
import * as user from './user'; import * as user from './user';
import * as verse from './verse';
export default { export default {
author, author,
poem, poem,
user, user,
verse,
}; };

15
src/services/luigi/poem.ts

@ -43,3 +43,18 @@ export async function savePoemUsingPOST(body: API.PoemBo, options?: { [key: stri
...(options || {}), ...(options || {}),
}); });
} }
/** seekPoem GET /api/luigi/poem/seek */
export async function seekPoemUsingGET(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: API.seekPoemUsingGETParams,
options?: { [key: string]: any },
) {
return request<API.ResponseCommonListPoemBo>('/api/luigi/poem/seek', {
method: 'GET',
params: {
...params,
},
...(options || {}),
});
}

43
src/services/luigi/typings.d.ts

@ -22,6 +22,13 @@ declare namespace API {
total?: number; total?: number;
}; };
type CommonListVerse = {
current?: number;
list?: Verse[];
pageSize?: number;
total?: number;
};
type Note = { type Note = {
annotation?: string; annotation?: string;
citation?: string; citation?: string;
@ -69,6 +76,12 @@ declare namespace API {
message?: string; message?: string;
}; };
type ResponseCommonListVerse = {
code?: number;
data?: CommonListVerse;
message?: string;
};
type ResponsePoemBo = { type ResponsePoemBo = {
code?: number; code?: number;
data?: PoemBo; data?: PoemBo;
@ -81,6 +94,12 @@ declare namespace API {
message?: string; message?: string;
}; };
type ResponseVerse = {
code?: number;
data?: Verse;
message?: string;
};
type Responseint = { type Responseint = {
code?: number; code?: number;
data?: number; data?: number;
@ -94,12 +113,24 @@ declare namespace API {
role?: string; role?: string;
}; };
type Verse = {
authorId?: number;
authorName?: string;
chapterId?: number;
content?: string;
id?: number;
originId?: number;
status?: number;
title?: string;
type?: string;
};
type getAuthorByIdUsingGETParams = { type getAuthorByIdUsingGETParams = {
/** id */ /** id */
id: number; id: number;
}; };
type seekUsingGETParams = { type seekAuthorUsingGETParams = {
/** name */ /** name */
name: string; name: string;
}; };
@ -108,4 +139,14 @@ declare namespace API {
/** id */ /** id */
id: number; id: number;
}; };
type seekPoemUsingGETParams = {
/** title */
title: string;
};
type getVerseByIdUsingGETParams = {
/** id */
id: number;
};
} }

45
src/services/luigi/verse.ts

@ -0,0 +1,45 @@
// @ts-ignore
/* eslint-disable */
import { request } from 'umi';
/** getVerseById GET /api/luigi/verse/get */
export async function getVerseByIdUsingGET(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: API.getVerseByIdUsingGETParams,
options?: { [key: string]: any },
) {
return request<API.ResponseVerse>('/api/luigi/verse/get', {
method: 'GET',
params: {
...params,
},
...(options || {}),
});
}
/** verseList POST /api/luigi/verse/list */
export async function verseListUsingPOST(
body: API.PaginationQuery,
options?: { [key: string]: any },
) {
return request<API.ResponseCommonListVerse>('/api/luigi/verse/list', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** saveVerse POST /api/luigi/verse/save */
export async function saveVerseUsingPOST(body: API.Verse, options?: { [key: string]: any }) {
return request<API.Responseint>('/api/luigi/verse/save', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
Loading…
Cancel
Save