diff --git a/mock/_utils/result.ts b/mock/_utils/result.ts new file mode 100644 index 00000000..220aa462 --- /dev/null +++ b/mock/_utils/result.ts @@ -0,0 +1,18 @@ +/** + * 模拟返回结构 + */ +export default function result(success: boolean, message: string, data?: any) { + return { + success: success, + message: message, + data: data, + }; +} + +export function success(data: any = null) { + return result(true, 'ok', data); +} + +export function error(message = 'error', data: any = null) { + return result(false, message, data); +} diff --git a/mock/api.ts b/mock/api.ts new file mode 100644 index 00000000..a0b539ee --- /dev/null +++ b/mock/api.ts @@ -0,0 +1,8 @@ +import { success } from './_utils/result'; + +// http://mockjs.com/examples.html +export default { + 'GET /api/worked': (req: any, res: any) => { + return res.json(success()); + }, +}; diff --git a/mock/comment.ts b/mock/comment.ts new file mode 100644 index 00000000..f9ba9c88 --- /dev/null +++ b/mock/comment.ts @@ -0,0 +1,65 @@ +import { success } from './_utils/result'; +import Mock from 'mockjs'; +import { CommentType } from '@/Comment/components/type'; +import { UserType } from '@/Utils/interface'; + +// http://mockjs.com/examples.html +export default { + 'GET /api/ums/account': (req: any, res: any) => + res.json( + success({ + id: 1, + nickname: 'hocgin', + username: 'hocgin', + avatar: Mock.Random.image('100x100'), + }), + ), + 'POST /api/com/comment/:refType/:refId/like': (req: any, res: any) => + res.json(success(mockData())), + 'POST /api/com/comment/:refType/:refId/dislike': (req: any, res: any) => + res.json(success(mockData())), + 'POST /api/com/comment/:refType/:refId/report': (req: any, res: any) => + res.json(success(mockData())), + 'POST /api/com/comment/:refType/:refId/reply': (req: any, res: any) => + res.json(success(mockData())), + 'POST /api/com/comment/:refType/:refId/_scroll': (req: any, res: any) => + res.json( + success({ + nextId: 1, + hasMore: true, + records: [mockData()], + }), + ), + 'POST /api/com/comment/:refType/:refId/_paging': (req: any, res: any) => + res.json( + success({ + current: 1, + total: 1, + size: 1, + pages: 1, + records: [mockData()], + }), + ), +}; + +let mockData = () => { + return Mock.mock({ + id: '@integer()', + replyId: 1, + likes: '@integer(0, 10)', + disliked: '@integer(0, 10)', + action: 'none', + content: '@string()', + hasReply: '@bool()', + author: mockUser(), + replier: mockUser(), + }); +}; + +let mockUser = () => { + return { + id: 1, + title: 'hocgin', + avatarUrl: Mock.Random.image('100x100'), + } as UserType; +}; diff --git a/mock/file.ts b/mock/file.ts new file mode 100644 index 00000000..c05a7316 --- /dev/null +++ b/mock/file.ts @@ -0,0 +1,12 @@ +import { success } from './_utils/result'; +import Mock from 'mockjs'; + +// http://mockjs.com/examples.html +export default { + 'POST /api/com/file/upload': (req: any, res: any) => { + return res.json(success(Mock.Random.image('100x100'))); + }, + 'GET /api/com/file/upload': (req: any, res: any) => { + return res.json(success(Mock.Random.image('100x100'))); + }, +}; diff --git a/package.json b/package.json index 6afda206..618b588f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@hocgin/ui", - "version": "4.0.41", + "version": "4.0.42", "sideEffects": [ ".less", ".css" @@ -54,6 +54,7 @@ "draft-js-prism": "^1.0.6", "emoji-mart": "^3.0.1", "memoize-one": "^5.2.1", + "mockjs": "^1.1.0", "randexp": "^0.5.3", "react-copy-to-clipboard": "^5.0.4", "react-countup": "^5.2.0", diff --git a/src/Comment/components/Comment/index.tsx b/src/Comment/components/Comment/index.tsx index 2164b906..f55d27d0 100644 --- a/src/Comment/components/Comment/index.tsx +++ b/src/Comment/components/Comment/index.tsx @@ -52,13 +52,11 @@ const UserOptions: React.FC<{ comment: CommentType; useAction: UseAction; userAction?: string; - likesCount?: number; - dislikedCount?: number; }> = (props, ref) => { let { useAction, comment } = props; let [userAction, setUserAction] = useState(props?.userAction); - let [likesCount, setLikesCount] = useState(props?.likesCount || 0); - let [dislikedCount, setDislikedCount] = useState(props?.dislikedCount || 0); + let [likesCount, setLikesCount] = useState(comment?.likes || 0); + let [dislikedCount, setDislikedCount] = useState(comment?.disliked || 0); let commentId = comment.id; let options = { @@ -207,7 +205,7 @@ const Comment: React.FC<{ )} } - content={

{content}

} + content={
{content}
} actions={actions} > {children} @@ -284,7 +282,7 @@ const Index: React.FC<{ datetime={datetime} author={author} replier={replier} - content={

{content}

} + content={
{content}
} actions={[ (undefined); let [content, setContent] = useState(''); + let [replied, setReplied] = useState(false); + useInterval(() => setReplied?.(false), 2000); + reply$.useSubscription((comment?: CommentType) => { setReply(comment); }); @@ -54,6 +62,10 @@ const Editor: React.FC<{ debounceWait: 300, onSuccess: (data: ReplyDataType) => { replied$.emit(data); + + // 清除原先内容 + setContent(''); + setReplied(true); }, }); @@ -86,7 +98,7 @@ const Editor: React.FC<{
- {userName}{' '} + {userName} {hasBeReply && ( <> @@ -115,8 +127,14 @@ const Editor: React.FC<{ />
-
diff --git a/src/Comment/components/index.tsx b/src/Comment/components/index.tsx index 4d077500..78505a46 100644 --- a/src/Comment/components/index.tsx +++ b/src/Comment/components/index.tsx @@ -1,6 +1,9 @@ import React, { useState } from 'react'; import { - CommentType, ScrollDataType, ScrollParamsType, UseAction, + CommentType, + ScrollDataType, + ScrollParamsType, + UseAction, } from './type'; import classNames from 'classnames'; import styles from './index.less'; @@ -39,51 +42,58 @@ const Index: React.FC = (props, ref) => { }); // 请求 - let scrollPull = useRequest(useAction!.scroll, { - manual: true, - onSuccess: (data: ScrollDataType) => { - let hasMore: boolean = data?.hasMore || false; - let records: CommentType[] = data?.records || []; - let nextId = data?.nextId; + let scrollPull = useRequest( + useAction!.scroll, + { + manual: true, + onSuccess: (data: ScrollDataType) => { + let hasMore: boolean = data?.hasMore || false; + let records: CommentType[] = data?.records || []; + let nextId = data?.nextId; - setDefaultParams({ nextId: nextId }); - setDataSource([...dataSource, ...records] as CommentType[]); - setHasMore(hasMore); + setDefaultParams({ nextId: nextId }); + setDataSource([...dataSource, ...records] as CommentType[]); + setHasMore(hasMore); + }, }, - }); + ); let onLoadMore = (page = 1) => { scrollPull.run({ ...defaultParams, page } as ScrollParamsType); }; - return (
- - ( - - - - )} - /> - - -
); + return ( +
+ + ( + + + + )} + /> + + +
+ ); }; export default Index; diff --git a/src/FileUpload/components/FileUpload/index.tsx b/src/FileUpload/components/FileUpload/index.tsx index 6bce1bbd..d084307a 100644 --- a/src/FileUpload/components/FileUpload/index.tsx +++ b/src/FileUpload/components/FileUpload/index.tsx @@ -1,52 +1,55 @@ import React from 'react'; import { Upload, Button } from 'antd'; -import { HttpRequestHeader } from '@/Utils/interface'; import { UploadOutlined } from '@ant-design/icons'; -import { UploadChangeParam } from 'antd/lib/upload/interface'; -interface FileUploadProps { - children?: string | Node; - headers?: HttpRequestHeader; +const Index: React.FC<{ + children?: any; + headers?: any; action?: string; maxCount?: number; - defaultFileList?: any; + defaultFileList?: any[]; onChange?: (info: any) => void; -} - -interface FileUploadState { -} - -class Index extends React.PureComponent { - static defaultProps = { - children: ( - - ), - headers: [], - action: `/api/file/upload`, - }; - - render() { - let { children, action, headers, defaultFileList, maxCount, onChange, ...rest } = this.props; - return ( - - {children} - - ); - } -} +}> = ({ + children = ( + + ), + headers = {}, + action = `/api/file/upload`, + maxCount, + defaultFileList, + onChange, + ...rest +}) => { + return ( + `${parseFloat(percent.toFixed(2))}%`, + } as any + } + defaultFileList={defaultFileList} + maxCount={maxCount} + action={action} + headers={headers} + onChange={onChange} + {...rest} + > + {children} + + ); +}; export default Index; diff --git a/src/FileUpload/demos/index.tsx b/src/FileUpload/demos/index.tsx index 862f84d1..e8f378fe 100644 --- a/src/FileUpload/demos/index.tsx +++ b/src/FileUpload/demos/index.tsx @@ -9,7 +9,7 @@ import styles from './index.less'; export default () => { return ( <> - + ); }; diff --git a/src/Promise/components/Comment/index.tsx b/src/Promise/components/Comment/index.tsx new file mode 100644 index 00000000..dbcdd2c4 --- /dev/null +++ b/src/Promise/components/Comment/index.tsx @@ -0,0 +1,13 @@ +import * as React from 'react'; +import { Comment } from '@hocgin/ui'; +import useAction from './use_action'; + +const Index: React.FC<{ + refType: any; + refId: any; + total?: number; +}> = ({ total, refType, refId }) => { + return ; +}; + +export default Index; diff --git a/src/Promise/components/Comment/service.ts b/src/Promise/components/Comment/service.ts new file mode 100644 index 00000000..876fc25b --- /dev/null +++ b/src/Promise/components/Comment/service.ts @@ -0,0 +1,65 @@ +import { useGet, usePost, Dom } from '@hocgin/ui'; +import { ID } from '@/Utils/interface'; + +export default class { + static reply(refType: any, refId: any, commentId?: ID, content?: string) { + return usePost(`/api/com/comment/${refType}/${refId}/reply`, { + data: { commentId, content }, + }) + .then(Dom.tryErrorIfExits) + .then(Dom.thenData) + .catch(Dom.showErrorMessage); + } + + static scroll(refType: any, refId: any, payload: any = {}) { + return usePost(`/api/com/comment/${refType}/${refId}/_scroll`, { + data: { ...payload }, + }) + .then(Dom.tryErrorIfExits) + .then(Dom.thenData) + .catch(Dom.showErrorMessage); + } + + static paging(refType: any, refId: any, parentId: ID, payload: any = {}) { + return usePost(`/api/com/comment/${refType}/${refId}/_paging`, { + data: { parentId, ...payload }, + }) + .then(Dom.tryErrorIfExits) + .then(Dom.thenData) + .catch(Dom.showErrorMessage); + } + + static like(refType: any, refId: any, commentId: ID) { + return usePost(`/api/com/comment/${refType}/${refId}/like`, { + data: { commentId }, + }) + .then(Dom.tryErrorIfExits) + .then(Dom.thenData) + .catch(Dom.showErrorMessage); + } + + static dislike(refType: any, refId: any, commentId: ID) { + return usePost(`/api/com/comment/${refType}/${refId}/like`, { + data: { commentId }, + }) + .then(Dom.tryErrorIfExits) + .then(Dom.thenData) + .catch(Dom.showErrorMessage); + } + + static report(refType: any, refId: any, commentId: ID, reason: string) { + return usePost(`/api/com/comment/${refType}/${refId}/report`, { + data: { commentId, reason }, + }) + .then(Dom.tryErrorIfExits) + .then(Dom.thenData) + .catch(Dom.showErrorMessage); + } + + static getCurrentUser() { + return useGet(`/api/ums/account`, {}) + .then(Dom.tryErrorIfExits) + .then(Dom.thenData) + .catch(Dom.showErrorMessage); + } +} diff --git a/src/Promise/components/Comment/use_action.ts b/src/Promise/components/Comment/use_action.ts new file mode 100644 index 00000000..c831cb50 --- /dev/null +++ b/src/Promise/components/Comment/use_action.ts @@ -0,0 +1,54 @@ +import { + DislikeParamsType, + LikeParamsType, + PagingDataType, + PagingParamsType, + ReplyDataType, + ReplyParamsType, + ScrollDataType, + ScrollParamsType, + UserDataType, + UserParamsType, +} from '@/Comment/components/type'; + +import service from './service'; + +export default (refType: any, refId: any) => ({ + reply: async (args: ReplyParamsType) => { + let replyParams = args as ReplyParamsType; + let replyId = replyParams?.commentId; + let replyContent = replyParams?.content; + return (await service.reply( + refType, + refId, + replyId, + replyContent, + )) as ReplyDataType; + }, + // 查询根评论 + scroll: async (args: ScrollParamsType) => + (await service.scroll(refType, refId, args)) as ScrollDataType, + // 查询子评论 + paging: async (args: PagingParamsType) => + (await service.paging( + refType, + refId, + args?.parentId, + args, + )) as PagingDataType, + like: async (args: LikeParamsType) => + (await service.like(refType, refId, args?.commentId)) as any, + dislike: async (args: DislikeParamsType) => + (await service.dislike(refType, refId, args?.commentId)) as any, + // 当前登陆用户 + user: async (args: UserParamsType) => { + let { id, title, nickname, avatarUrl, avatar, href } = + (await service.getCurrentUser()) as any; + return { + id, + title: title || nickname, + avatarUrl: avatarUrl || avatar, + href, + } as UserDataType; + }, +}); diff --git a/src/Promise/components/FileUpload/index.tsx b/src/Promise/components/FileUpload/index.tsx index b43c46c9..503a8ed7 100644 --- a/src/Promise/components/FileUpload/index.tsx +++ b/src/Promise/components/FileUpload/index.tsx @@ -1,49 +1,23 @@ import React from 'react'; import { FileUpload, Utils, Dom } from '@hocgin/ui'; +import { FileInfo } from '@/Utils/interface'; -interface FileUploadProps { - children?: string | Node; +const Index: React.FC<{ + children?: any; action?: string; maxCount?: number; - value?: any; + value?: FileInfo | FileInfo[]; onChange?: (info: any) => void; -} - -class Index extends React.PureComponent { - static defaultProps = { - action: `/com/file/upload`, - maxCount: 1, - }; - state = { - fileList: [], - }; - - render() { - let { children, action, value, maxCount, ...rest } = this.props; - return ( - - {children} - - ); - } - - handleFileList = (values: any) => { - if (Utils.Lang.isNull(values)) { - return []; - } - if (values instanceof Array) { - return (values || []).map(Dom.asFile); - } - return [Dom.asFile(values, 0)]; - }; - - handleChange = ({ file, fileList }: any) => { - let { onChange, maxCount } = this.props; +}> = ({ + children, + action = `/com/file/upload`, + value, + maxCount = 1, + onChange, + ...rest +}) => { + let [fileList, setFileList] = React.useState([]); + let handleChange = ({ file, fileList }: any) => { fileList = fileList.map((file: any) => { let result = file.response; if (result) { @@ -56,12 +30,34 @@ class Index extends React.PureComponent { } return file; }); - this.setState({ fileList }); + setFileList(fileList); let uploadFiles = fileList .filter(({ url }: any) => url) - .map(({ url, name }: any) => ({ url, name })); + .map(({ url, name }: any) => ({ url, filename: name })); onChange && onChange(maxCount === 1 ? uploadFiles[0] : uploadFiles); }; -} + + let handleFileList = (values: any) => { + if (Utils.Lang.isNull(values)) { + return []; + } + if (values instanceof Array) { + return (values || []).map(Dom.asFile); + } + return [Dom.asFile(values, 0)]; + }; + + return ( + + {children} + + ); +}; export default Index; diff --git a/src/Promise/components/index.tsx b/src/Promise/components/index.tsx index 0c7e2e5e..9ad54044 100644 --- a/src/Promise/components/index.tsx +++ b/src/Promise/components/index.tsx @@ -7,6 +7,7 @@ import Encoding from './Encoding'; import RadioButton from './RadioButton'; import Radio from './Radio'; import Checkbox from './Checkbox'; +import Comment from './Comment'; import ExhibitSchemaConfig from './Schema/ExhibitSchemaConfig'; import ArchiveSchemaConfig from './Schema/ArchiveSchemaConfig'; import TableSchemaConfig from './Schema/TableSchemaConfig'; @@ -27,6 +28,7 @@ export default class Promise { static RadioButton: typeof RadioButton = RadioButton; static Radio: typeof Radio = Radio; static Checkbox: typeof Checkbox = Checkbox; + static Comment: typeof Comment = Comment; static ExhibitSchemaConfig: typeof ExhibitSchemaConfig = ExhibitSchemaConfig; static ArchiveSchemaConfig: typeof ArchiveSchemaConfig = ArchiveSchemaConfig; diff --git a/src/Promise/demos/comment.tsx b/src/Promise/demos/comment.tsx new file mode 100644 index 00000000..e57ed0c1 --- /dev/null +++ b/src/Promise/demos/comment.tsx @@ -0,0 +1,14 @@ +/** + * title: 我是标题 + * desc: 我是简介,我可以用 `Markdown` 来编写 + */ +import React from 'react'; +import { Promise } from '@hocgin/ui'; + +export default () => { + return ( + <> + + + ); +}; diff --git a/src/Promise/demos/file-upload.tsx b/src/Promise/demos/file-upload.tsx index 44ac6353..4d38f9ee 100644 --- a/src/Promise/demos/file-upload.tsx +++ b/src/Promise/demos/file-upload.tsx @@ -9,7 +9,9 @@ import styles from './index.less'; export default () => { return ( <> - + ); }; diff --git a/src/Promise/demos/index.tsx b/src/Promise/demos/index.tsx index 12f1a859..ae43bee3 100644 --- a/src/Promise/demos/index.tsx +++ b/src/Promise/demos/index.tsx @@ -7,5 +7,5 @@ import { Tpl } from '@hocgin/ui'; import styles from './index.less'; export default () => { - return ; + return ; }; diff --git a/src/Promise/demos/scheme-archive-config.tsx b/src/Promise/demos/scheme-archive-config.tsx index 06ef9077..488ab030 100644 --- a/src/Promise/demos/scheme-archive-config.tsx +++ b/src/Promise/demos/scheme-archive-config.tsx @@ -12,6 +12,10 @@ export const config: any = { encoding: '这是自定义的', search: 'search_key', searchName: 'searchName', + gin_upload_name: { + url: 'http://dummyimage.com/100x100', + filename: '_reset.jpg', + }, }; }, submit: async (params: Record) => { @@ -181,8 +185,8 @@ export const config: any = { dataIndex: 'gin_upload_name', valueType: Dom.columnPrefix('upload'), params: { - action: 'http://api-dev.hocgin.top/api/upload', - maxCount: 1, + action: `${window.location.origin}/api/com/file/upload`, + maxCount: 2, }, }, { diff --git a/src/Promise/promise-basic.md b/src/Promise/promise-basic.md index d5a8b9ba..b299982f 100644 --- a/src/Promise/promise-basic.md +++ b/src/Promise/promise-basic.md @@ -16,6 +16,10 @@ group: ### 基础用法 +评论组件 (Comment): + + + 基础网络组件 (Basic): diff --git a/src/Utils/dom/index.tsx b/src/Utils/dom/index.tsx index 852bce81..c1050f44 100644 --- a/src/Utils/dom/index.tsx +++ b/src/Utils/dom/index.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Tree, TreeSelect } from 'antd'; +import { message, Tree, TreeSelect } from 'antd'; import { Struct } from '../result'; import { FileInfo, TreeNode } from '@/Utils/interface'; import { SmileOutlined, HeartOutlined, HomeOutlined } from '@ant-design/icons'; @@ -115,8 +115,18 @@ export default class Dom { static getPagingPagination = Struct.getPagingPagination; static fastPagingPagination = Struct.fastPagingPagination; static getTableData = Struct.getTableData; + + // 获取分页数据(表格形式) static fastGetTableData = Struct.fastGetTableData; + // 解析错误信息 + static showErrorMessage = (e: Error) => message.error(e.message); + + // 检测到错误,直接抛出 + static tryErrorIfExits = Struct.thenTryErrorIfExits; + // 直接获取数据 + static thenData = Struct.thenData; + /** * 获取域名 */ diff --git a/src/Utils/result.ts b/src/Utils/result.ts index 5421b207..6e6ede2e 100644 --- a/src/Utils/result.ts +++ b/src/Utils/result.ts @@ -22,14 +22,9 @@ export class Struct { /** * 获取数据 - * @deprecated * @param result */ - static getData(result?: HttpResult): any { - return result && result.data; - } - - static thenData = Struct.getData; + static thenData = (result?: HttpResult) => result?.data; /** * 转换为 Antd 的 {label, value} @@ -59,6 +54,7 @@ export class Struct { /** * 过渡处理错误信息 + * @deprecated * @param result */ static thenShowErrorIfExits(