Compare commits

...

6 Commits

Author SHA1 Message Date
cjx ab734742ef 收藏功能 2024-11-02 12:32:32 +08:00
cjx d336c9eafd 重新完成点赞功能 2024-11-02 10:42:13 +08:00
cjx c2572d273b cjx-test2 2024-11-01 09:43:26 +08:00
cjx 779b62d739 评论区样式 2024-10-31 16:46:25 +08:00
cjx 47f02d7ec5 改了样式,修复了问题 2024-10-30 17:00:36 +08:00
cjx f5acbafaf4 10-30 2024-10-30 16:32:02 +08:00
28 changed files with 802 additions and 87 deletions

View File

@ -14,24 +14,24 @@ const checkers: Checker<EntityDict, 'essay', RuntimeCxt>[] = [
$or: [{ iState: 'unpublished' }, { iState: 'withdrawn' }], $or: [{ iState: 'unpublished' }, { iState: 'withdrawn' }],
}, },
}, },
// settop的时候只能是isTop为false的 // // settop的时候只能是isTop为false的
{ // {
entity: 'essay', // entity: 'essay',
action: 'setTop', // action: 'setTop',
type: 'row', // type: 'row',
filter: { // filter: {
isTop: false, // isTop: false,
}, // },
}, // },
// cancelTop的时候只能是isTop为true的 // // cancelTop的时候只能是isTop为true的
{ // {
entity: 'essay', // entity: 'essay',
action: 'cancelTop', // action: 'cancelTop',
type: 'row', // type: 'row',
filter: { // filter: {
isTop: true, // isTop: true,
}, // },
}, // },
// 在删除essay的时候同步删除extraFile和essayLabels还有log // 在删除essay的时候同步删除extraFile和essayLabels还有log
// 注意在checker中为非异步环境这点与trigger不同 // 注意在checker中为非异步环境这点与trigger不同
{ {
@ -88,9 +88,23 @@ const checkers: Checker<EntityDict, 'essay', RuntimeCxt>[] = [
}, },
option option
); );
},
() => {
return context.operate(
'comment',
{
action: 'remove',
id: generateNewId(),
data: {},
filter: {
essay: filter,
},
},
option
);
} }
); );
}, }
}, },
]; ];

View File

@ -0,0 +1,5 @@
{
"enablePullDownRefresh": false,
"usingComponents": {
}
}

View File

@ -0,0 +1,13 @@
export default OakComponent({
entity: 'comment',
isList: true,
projection: {
content: 1,
},
formData({ data }) {
return {
list: data,
essayId: this.props.essayId
};
},
});

View File

@ -0,0 +1,3 @@
{
"CommentSection":"评论区"
}

View File

@ -0,0 +1,39 @@
*{
font-size: 18px;
}
.heading{
font-size: 25px;
color:rgb( 22,119,255);
text-indent: 7em;
font-family: Microsoft YaHei;
}
.messageArea{
text-indent: 2em;
}
.editor {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
.left{
flex: 1;
margin-left: 15%;
}
.right{
margin-right: 15%;
}
}
.list {
margin-left: 16.5%;
margin-right: 15%;
margin-bottom: 5px;
.item {
border: 1px solid rgb(60, 134, 236);
padding: 20px;
border-radius: 20px;
}
}

View File

@ -0,0 +1,89 @@
import React from 'react';
import { EntityDict } from '@project/oak-app-domain';
import { RowWithActions, WebComponentProps } from 'oak-frontend-base';
import Styles from './styles.module.less';
import { Input } from 'antd';
import { Button } from 'antd';
import useFeatures from '@project/hooks/useFeatures';
const { TextArea } = Input;
const List = (
props: WebComponentProps<
EntityDict,
'comment',
true,
{
list: RowWithActions<EntityDict, 'comment'>[];
essayId: string;
}
>
) => {
const { list } = props.data;
const { t, addItem, execute } = props.methods;
const features = useFeatures();
const [newCommentText, setNewCommentText] = React.useState('');
const handleSubmit = () => {
addItem({
content: newCommentText,
creatorId: features.token.getUserId(),
essayId: props.data.essayId,
});
execute();
//提交后清空输入框
setNewCommentText('');
};
return (
<>
<h3 className={Styles.heading}>{t('CommentSection')}</h3>
<div className={Styles.editor}>
<div className={Styles.left}>
{/* textarea*/}
<TextArea
showCount
style={{
width: '95%',
height: '100px',
resize: 'none',
margin:'30px'
}}
placeholder='请输入评论'
value={newCommentText}
onChange={(e) => setNewCommentText(e.target.value)}
></TextArea>
</div>
<div className={Styles.right}>
{/* button */}
<Button
style={{
width: "80px",
height: "100px"
}}
type='primary'
size='large'
onClick={handleSubmit}
>
{t('common::submit')}
</Button>
</div>
</div>
{list && (
<div className={Styles.list}>
{list.map((item) => {
return (
<div key={item.id} className={`${Styles.item} ${Styles.messageArea}`}>
{item.id}:{item.content}
</div>
);
})}
</div>
)}
</>
);
};
export default List;

View File

@ -9,7 +9,7 @@ export default OakComponent({
// 先按置顶排序 // 先按置顶排序
sorter: { sorter: {
$attr: { $attr: {
isTop: 1, topState: 1,
}, },
$direction: 'desc', $direction: 'desc',
}, },

View File

@ -46,7 +46,7 @@ const EssayItem = (props: {
</div> </div>
<div className={Styles.body}> <div className={Styles.body}>
<div className={Styles.title}> <div className={Styles.title}>
{item.isTop && ( {item.topState === "isTop" && (
<div className={Styles.top}> <div className={Styles.top}>
{t('common::top')} {t('common::top')}
</div> </div>

View File

@ -14,6 +14,19 @@ const i18ns: I18n[] = [
"createCategory": "创建分类" "createCategory": "创建分类"
} }
}, },
{
id: "63f061bd7f1010b396cadb4bb784734f",
namespace: "new-demo-p-console-comment-list",
language: "zh-CN",
module: "new-demo",
position: "src/pages/console/comment/list",
data: {
"pageTitle": "评论列表",
"content": "内容",
"creator": "创建者",
"essay": "文章"
}
},
{ {
id: "4813a0a3acdfac90d3b3b3cfa37de82c", id: "4813a0a3acdfac90d3b3b3cfa37de82c",
namespace: "new-demo-p-console-essay-list", namespace: "new-demo-p-console-essay-list",
@ -35,7 +48,8 @@ const i18ns: I18n[] = [
"isTop": "已置顶", "isTop": "已置顶",
"notTop": "未置顶" "notTop": "未置顶"
}, },
"cover": "封面" "cover": "封面",
"label:selectLabels": "选择标签"
} }
}, },
{ {
@ -222,6 +236,16 @@ const i18ns: I18n[] = [
"categories": "分类" "categories": "分类"
} }
}, },
{
id: "b0e6409d76cb424d29812006e349a91c",
namespace: "new-demo-c-frontend-home-comment-list",
language: "zh-CN",
module: "new-demo",
position: "src/components/frontend/home/comment/list",
data: {
"CommentSection": "评论区"
}
},
{ {
id: "6fd812246bb915f692124fc05bb9ffd4", id: "6fd812246bb915f692124fc05bb9ffd4",
namespace: "new-demo-c-frontend-home-labels-byCategory", namespace: "new-demo-c-frontend-home-labels-byCategory",
@ -328,7 +352,8 @@ const i18ns: I18n[] = [
"userManage": "用户管理", "userManage": "用户管理",
"relationManage": "关系管理", "relationManage": "关系管理",
"consoleHome": "控制台首页", "consoleHome": "控制台首页",
"labelManage": "标签管理" "labelManage": "标签管理",
"commentManage": "评论管理"
} }
}, },
{ {
@ -549,6 +574,35 @@ const i18ns: I18n[] = [
} }
} }
}, },
{
id: "deabf32f550dba8ce03111112ef588ce",
namespace: "collect",
language: "zh-CN",
module: "",
position: "oak-app-domain/Collect",
data: {
"name": "收藏记录",
"attr": {
"essay": "文章",
"user": "用户"
}
}
},
{
id: "f70261a2fc23af2a9f459353e5ac1ea3",
namespace: "comment",
language: "zh-CN",
module: "",
position: "oak-app-domain/Comment",
data: {
"name": "评论",
"attr": {
"creator": "创建者",
"content": "内容",
"essay": "文章"
}
}
},
{ {
id: "b06ea1d96db9f0a2d2f3ff3ad966c9c5", id: "b06ea1d96db9f0a2d2f3ff3ad966c9c5",
namespace: "domain", namespace: "domain",
@ -616,19 +670,27 @@ const i18ns: I18n[] = [
"logs": "修改记录", "logs": "修改记录",
"category": "分类", "category": "分类",
"iState": "状态", "iState": "状态",
"isTop": "是否置顶" "topState": "置顶"
}, },
"action": { "action": {
"publish": "发布", "publish": "发布",
"withdraw": "撤回", "withdraw": "撤回",
"setTop": "置顶", "setTop": "置顶",
"cancelTop": "取消置顶" "cancelTop": "取消置顶",
"like": "点赞",
"unlike": "取消点赞",
"collect": "收藏",
"uncollect": "取消收藏"
}, },
"v": { "v": {
"iState": { "iState": {
"unpublished": "未发布", "unpublished": "未发布",
"published": "已发布", "published": "已发布",
"withdrawn": "已撤回" "withdrawn": "已撤回"
},
"topState": {
"isTop": "已置顶",
"notTop": "未置顶"
} }
} }
} }
@ -747,6 +809,20 @@ const i18ns: I18n[] = [
} }
} }
}, },
{
id: "b9691c35f8511fb42aa59cbcc8645610",
namespace: "like",
language: "zh-CN",
module: "",
position: "oak-app-domain/Like",
data: {
"name": "点赞记录",
"attr": {
"essay": "文章",
"user": "用户"
}
}
},
{ {
id: "f3c966c0e1deb4ca4c10e10117143849", id: "f3c966c0e1deb4ca4c10e10117143849",
namespace: "livestream", namespace: "livestream",

21
src/entities/Collect.ts Normal file
View File

@ -0,0 +1,21 @@
import { EntityDesc, EntityShape } from 'oak-domain/lib/types';
import { Schema as Essay } from './Essay';
import { Schema as User } from 'oak-general-business/lib/entities/User';
//Collect.ts
//关联文章和用户,表示收藏记录
export interface Schema extends EntityShape {
essay: Essay;
user: User;
}
export const entityDesc: EntityDesc<Schema> = {
locales: {
zh_CN: {
name: '收藏记录',
attr: {
essay: '文章',
user: '用户',
},
},
},
};

25
src/entities/Comment.ts Normal file
View File

@ -0,0 +1,25 @@
import { String } from 'oak-domain/lib/types/DataType';
import { Schema as User } from 'oak-general-business/lib/entities/User';
import { Schema as Essay } from './Essay';
import { EntityDesc, EntityShape } from 'oak-domain/lib/types';
//Comment.ts
export interface Schema extends EntityShape {
creator: User;
content: String<1024>;
essay: Essay;
}
export const entityDesc: EntityDesc<Schema> = {
locales: {
zh_CN: {
name: '评论',
attr: {
creator: '创建者',
content: '内容',
essay: '文章'
}
}
},
};

View File

@ -5,6 +5,7 @@ import { Schema as ExtraFile } from 'oak-general-business/lib/entities/ExtraFile
import { Schema as Log } from 'oak-domain/lib/entities/Log'; import { Schema as Log } from 'oak-domain/lib/entities/Log';
import { Schema as Category } from './Category'; import { Schema as Category } from './Category';
export type MetaData = Record<string, any>; export type MetaData = Record<string, any>;
// Essay.ts // Essay.ts
@ -17,7 +18,7 @@ export interface Schema extends EntityShape {
images: ExtraFile[]; images: ExtraFile[];
logs: Log[]; logs: Log[];
category?: Category; category?: Category;
isTop: Boolean; // isTop: Boolean;
} }
export type IState = 'unpublished' | 'published' | 'withdrawn'; export type IState = 'unpublished' | 'published' | 'withdrawn';
@ -32,10 +33,30 @@ export const IActionDef: ActionDef<IAction, IState> = {
is: 'unpublished', is: 'unpublished',
}; };
// 用户行为操作 export type TopState = 'isTop' | 'notTop';
export type CommonAction = 'setTop' | 'cancelTop'; export type TopAction = 'setTop' | 'cancelTop';
//点赞操作
export type LikeAction = 'like' | 'unlike';
//收藏操作
export type CollectAction = 'collect' | 'uncollect';
export const TopActionDef: ActionDef<TopAction, TopState> = {
stm: {
setTop: ['notTop', 'isTop'],
cancelTop: ['isTop', 'notTop'],
},
is: 'notTop',
};
// 用户行为操作
// export type CommonAction = 'setTop' | 'cancelTop';
// export type Action = IAction | CommonAction|TopAction;
export type State = IState | TopState;
export type Action = IAction | TopAction|LikeAction|CollectAction;
type Action = IAction | CommonAction;
export const entityDesc: EntityDesc< export const entityDesc: EntityDesc<
Schema, Schema,
@ -43,6 +64,7 @@ export const entityDesc: EntityDesc<
'', '',
{ {
iState: IState; iState: IState;
topState: TopState;
} }
> = { > = {
locales: { locales: {
@ -58,13 +80,18 @@ export const entityDesc: EntityDesc<
logs: '修改记录', logs: '修改记录',
category: '分类', category: '分类',
iState: '状态', iState: '状态',
isTop: '是否置顶', // isTop: '是否置顶',
topState: '置顶',
}, },
action: { action: {
publish: '发布', publish: '发布',
withdraw: '撤回', withdraw: '撤回',
setTop: '置顶', setTop: '置顶',
cancelTop: '取消置顶', cancelTop: '取消置顶',
like: '点赞',
unlike: '取消点赞',
collect: '收藏',
uncollect:'取消收藏'
}, },
v: { v: {
iState: { iState: {
@ -72,6 +99,10 @@ export const entityDesc: EntityDesc<
published: '已发布', published: '已发布',
withdrawn: '已撤回', withdrawn: '已撤回',
}, },
topState: {
isTop: '已置顶',
notTop: '未置顶',
},
}, },
}, },
}, },
@ -83,6 +114,10 @@ export const entityDesc: EntityDesc<
published: '#87d068', published: '#87d068',
withdrawn: '#2db7f5', withdrawn: '#2db7f5',
}, },
topState: {
isTop: '#C63300',
notTop: '#33FFDD',
},
}, },
}, },
indexes: [ indexes: [

21
src/entities/Like.ts Normal file
View File

@ -0,0 +1,21 @@
import { EntityDesc, EntityShape } from 'oak-domain/lib/types';
import { Schema as Essay } from './Essay';
import { Schema as User } from 'oak-general-business/lib/entities/User';
//like.ts
//关联文章和用户,表示点赞记录
export interface Schema extends EntityShape {
essay: Essay;
user: User;
}
export const entityDesc: EntityDesc<Schema> = {
locales: {
zh_CN: {
name: '点赞记录',
attr: {
essay: '文章',
user: '用户',
},
},
},
};

View File

@ -38,25 +38,22 @@ const menus: IMenu[] = [
name: 'consoleHome', name: 'consoleHome',
icon: 'workbench', icon: 'workbench',
url: '/home', url: '/home',
destEntity: 'userRelation', destEntity: '',
order: 0, order: 0,
actions: ['select'],
}, },
{ {
name: 'userManage', name: 'userManage',
icon: 'addpeople', icon: 'addpeople',
url: '/userRelation/manage', url: '/userRelation/manage',
parent: 'System', parent: 'System',
destEntity: 'userRelation', destEntity: '',
actions: ['select'],
}, },
{ {
name: 'relationManage', name: 'relationManage',
icon: 'share', icon: 'share',
url: '/relation/entityList', url: '/relation/entityList',
parent: 'System', parent: 'System',
destEntity: 'userRelation', destEntity: '',
actions: ['select'],
}, },
// category // category
{ {
@ -64,16 +61,14 @@ const menus: IMenu[] = [
icon: 'stealth', icon: 'stealth',
url: '/category/list', url: '/category/list',
order: 2, order: 2,
destEntity: 'userRelation', destEntity: '',
actions: ['select'],
}, },
{ {
name: 'essayManage', name: 'essayManage',
icon: 'barrage', icon: 'barrage',
url: '/essay/list', url: '/essay/list',
order: 3, order: 3,
destEntity: 'userRelation', destEntity: '',
actions: ['select'],
}, },
// 标签关联 // 标签关联
{ {
@ -81,9 +76,16 @@ const menus: IMenu[] = [
icon: 'accessory', icon: 'accessory',
url: '/label/list', url: '/label/list',
order: 4, order: 4,
destEntity: 'userRelation', destEntity: '',
actions: ['select'],
}, },
// 评论管理
{
name: 'commentManage',
icon: 'message',
url: '/comment/list',
destEntity: '',
order: 5,
}
]; ];
export default class Console extends BaseConsole<EntityDict, IMenu> { export default class Console extends BaseConsole<EntityDict, IMenu> {

View File

@ -5,5 +5,6 @@
"userManage": "用户管理", "userManage": "用户管理",
"relationManage": "关系管理", "relationManage": "关系管理",
"consoleHome" : "控制台首页", "consoleHome" : "控制台首页",
"labelManage": "标签管理" "labelManage": "标签管理",
"commentManage": "评论管理"
} }

View File

@ -0,0 +1,5 @@
{
"enablePullDownRefresh": false,
"usingComponents": {
}
}

View File

@ -0,0 +1,20 @@
export default OakComponent({
entity: 'comment',
isList: true,
projection: {
content: 1,
essay: {
title: 1,
},
creator: {
name: 1,
nickname: 1,
}
},
formData({ data }) {
return {
list: data,
};
},
actions: ['update', 'remove'],
});

View File

@ -0,0 +1,6 @@
{
"pageTitle": "评论列表",
"content": "内容",
"creator": "创建者",
"essay": "文章"
}

View File

@ -0,0 +1,7 @@
.id {
font-size: 18px;
}
.item {
font-size: 18px;
}

View File

@ -0,0 +1,76 @@
import React from 'react';
import { EntityDict } from '@project/oak-app-domain';
import { CascadeActionProps, RowWithActions, WebComponentProps } from 'oak-frontend-base';
import Styles from './styles.module.less';
import PageHeader from 'oak-frontend-base/es/components/pageHeader2';
import { ListPro } from '@project/components/AbstractComponents';
import labels from '@project/components/frontend/home/labels';
import { render } from 'nprogress';
const List = (
props: WebComponentProps<
EntityDict,
'comment',
true,
{
list: RowWithActions<EntityDict, 'comment'>[];
}
>
) => {
const { list } = props.data;
const { t } = props.methods;
const { removeItem, execute } = props.methods;
const attrs = [
{
path: 'content',
label: t('content'),
render: (row: any) => {
return row.content;
},
},
{
path: 'creator',
label: t('creator'),
render: (row: any) => {
return row.creator?.name || row.creator?.nickname || '';
}
},
{
path: 'essay',
label: t('essay'),
render: (row: any) => {
return row.essay?.title || '';
}
}
]
const onAction = (row: any, action: string) => {
switch (action) {
case 'update':
break;
case 'remove':
removeItem(row.id);
execute();
break;
default:
break;
}
};
return (
<PageHeader title={t('pageTitle')}>
<ListPro
entity="comment"
data={list}
attributes={attrs}
oakPath={'${oakFullPath}'}
onAction={onAction}
></ListPro>
</PageHeader>
);
};
export default List;

View File

@ -9,7 +9,7 @@ export default OakComponent({
// 先按置顶排序 // 先按置顶排序
sorter: { sorter: {
$attr: { $attr: {
isTop: 1, topState: 1,
}, },
$direction: 'desc', $direction: 'desc',
}, },

View File

@ -1,17 +1,18 @@
{ {
"essayList": "文章列表", "essayList": "文章列表",
"title": "文章标题", "title": "文章标题",
"summary": "文章摘要", "summary": "文章摘要",
"author": "作者", "author": "作者",
"action":{ "action": {
"setting": "设置" "setting": "设置"
}, },
"label": { "label": {
"selectLabels": "选择标签" "selectLabels": "选择标签"
}, },
"essay":{ "essay": {
"isTop": "已置顶", "isTop": "已置顶",
"notTop": "未置顶" "notTop": "未置顶"
}, },
"cover": "封面" "cover": "封面",
"label:selectLabels": "选择标签"
} }

View File

@ -11,6 +11,7 @@ import Styles from './web.pc.module.less';
import { Button, Image, Tag } from 'antd'; import { Button, Image, Tag } from 'antd';
import Modal from 'antd/es/modal/Modal'; import Modal from 'antd/es/modal/Modal';
import EssayUpsert from '@project/components/console/essay/upsert'; import EssayUpsert from '@project/components/console/essay/upsert';
import { Action } from '@project/oak-app-domain/Essay/Action';
const EssayList = ( const EssayList = (
props: WebComponentProps< props: WebComponentProps<
@ -67,7 +68,7 @@ const EssayList = (
render: (row) => { render: (row) => {
return ( return (
<> <>
{row.isTop ? ( {row.topState === "isTop" ? (
<Tag color='#f50'>{t('essay.isTop')}</Tag> <Tag color='#f50'>{t('essay.isTop')}</Tag>
) : ( ) : (
<Tag color='#108ee9'>{t('essay.notTop')}</Tag> <Tag color='#108ee9'>{t('essay.notTop')}</Tag>
@ -209,12 +210,15 @@ const EssayList = (
execute(); execute();
break; break;
// 置顶和取消置顶 // 置顶和取消置顶
case 'setTop': // case 'setTop':
case 'cancelTop': // case 'cancelTop':
updateItem({}, row.id, action); // updateItem({}, row.id, action);
execute(); // execute();
break; // break;
default: default:
updateItem({}, row.id, action as Action);
execute();
} }
}; };
@ -245,7 +249,7 @@ const EssayList = (
/> />
</PageHeader> </PageHeader>
<Modal <Modal
title={t('label:selectLabels')} title={t('label.selectLabels')}
open={showSelectLabels} open={showSelectLabels}
onCancel={() => setShowSelectLabels(false)} onCancel={() => setShowSelectLabels(false)}
onOk={() => { onOk={() => {

View File

@ -5,6 +5,16 @@ export default OakComponent({
isList: false, isList: false,
projection: essayProjection, projection: essayProjection,
formData({ data }) { formData({ data }) {
//点赞的数据
const isLiked = data?.like$essay?.some(item => {
return item.user.id === this.features.token.getUserId();
})
const likeNums = data?.like$essay?.length || 0;
//收藏的数据
const isCollected = data?.collect$essay?.some(item => {
return item.user.id === this.features.token.getUserId();
})
const collectNums = data?.collect$essay?.length || 0;
const fileIndex = data?.extraFile$entity?.findIndex( const fileIndex = data?.extraFile$entity?.findIndex(
(item) => item.tag1 === 'cover' (item) => item.tag1 === 'cover'
); );
@ -16,11 +26,19 @@ export default OakComponent({
item: data, item: data,
// 获取封面的url地址 // 获取封面的url地址
cover: url, cover: url,
isLiked,
likeNums,
isCollected,
collectNums
}; };
} }
return { return {
item: data, item: data,
cover: '', cover: '',
isLiked,
likeNums,
isCollected,
collectNums
}; };
}, },
}); });

View File

@ -115,4 +115,25 @@
&:hover { &:hover {
opacity: 1; opacity: 1;
} }
} }
.threeComponts{
float: right;
.likearea{
.likes{
float: left;
}
.nums{
float: right;
margin: 2.5px 10px;
}
}
.collectarea{
.collects{
float: left;
}
.nums{
float: right;
margin: 2.5px 10px;
}
}
}

View File

@ -6,6 +6,8 @@ import MdViewer from '@project/components/common/byteMD/MdViewer';
import FrontendFooter from '@project/components/frontend/home/FrontendFooter'; import FrontendFooter from '@project/components/frontend/home/FrontendFooter';
import { Button } from 'antd'; import { Button } from 'antd';
import { VerticalAlignTopOutlined } from '@ant-design/icons'; import { VerticalAlignTopOutlined } from '@ant-design/icons';
import List from '@project/components/frontend/home/comment/list';
import OakIcon from 'oak-frontend-base/es/components/icon';
const EssayDetails = ( const EssayDetails = (
props: WebComponentProps< props: WebComponentProps<
@ -15,11 +17,18 @@ const EssayDetails = (
{ {
item: RowWithActions<EntityDict, 'essay'>; item: RowWithActions<EntityDict, 'essay'>;
cover: string; cover: string;
isLiked: boolean;
likeNums: number;
isCollected: boolean;
collectNums: number;
} }
> >
) => { ) => {
const { item, cover } = props.data; const { oakFullpath } = props.data;
const { item, cover, isLiked, likeNums ,isCollected,collectNums} = props.data;
const [showScrollTop, setShowScrollTop] = useState(false); const [showScrollTop, setShowScrollTop] = useState(false);
const { t, update, execute } = props.methods;
useEffect(() => { useEffect(() => {
const handleScroll = () => { const handleScroll = () => {
@ -74,9 +83,78 @@ const EssayDetails = (
> >
</Button> </Button>
<div className={Styles.threeComponts}>
<div className={Styles.likearea}>
<div className={Styles.likes}>
{isLiked ? (
<Button
shape='circle'
onClick={() => {
update({}, 'unlike');
execute();
}}
>
<OakIcon
name='praise_fill'
size={24}
/>
</Button>
) : (
<Button
shape='circle'
onClick={() => {
update({}, 'like');
execute();
}}
>
<OakIcon name='praise' size={24} />
</Button>
)}
<div className={Styles.nums}>
{likeNums}
</div>
</div>
</div>
<div className={Styles.collectarea}>
<div className={Styles.collects}>
{isCollected ? (
<Button
shape='circle'
onClick={() => {
update({}, 'uncollect');
execute();
}}
>
<OakIcon
name='collection_fill'
size={24}
/>
</Button>
) : (
<Button
shape='circle'
onClick={() => {
update({}, 'collect');
execute();
}}
>
<OakIcon
name='collection'
size={24}
/>
</Button>
)}
</div>
<div className={Styles.nums}>{collectNums}</div>
</div>
</div>
</div> </div>
<MdViewer md={item.content!} /> <MdViewer md={item.content!} />
</div> </div>
<List
oakPath={`${oakFullpath}.comment$essay`}
essayId={item.id}
></List>
<div className={Styles.blank}></div> <div className={Styles.blank}></div>
</> </>
)} )}

View File

@ -1,7 +1,11 @@
import { EntityDict } from '@oak-app-domain'; import { EntityDict } from '@oak-app-domain';
import { Trigger } from 'oak-domain/lib/types'; import { RemoveTrigger, Trigger } from 'oak-domain/lib/types';
import { BackendRuntimeContext } from '../context/BackendRuntimeContext'; import { BackendRuntimeContext } from '../context/BackendRuntimeContext';
import assert from 'assert'; import assert from 'assert';
import Operation from 'antd/es/transfer/operation';
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
import { Filter } from './../oak-app-domain/Essay/Schema';
import { IdAction } from './../oak-app-domain/User/Action';
const triggers: Trigger<EntityDict, 'essay', BackendRuntimeContext>[] = [ const triggers: Trigger<EntityDict, 'essay', BackendRuntimeContext>[] = [
{ {
@ -14,7 +18,7 @@ const triggers: Trigger<EntityDict, 'essay', BackendRuntimeContext>[] = [
assert(data && !(data instanceof Array)); assert(data && !(data instanceof Array));
// 若不存在meta则初始化一个空对象 // 若不存在meta则初始化一个空对象
data.meta = data.meta || {}; data.meta = data.meta || {};
data.isTop = data.isTop || false; // data.topState = data.isTop || false;
assert(data.content, 'content is required'); assert(data.content, 'content is required');
if (!data.summary) { if (!data.summary) {
// 自动截取前面100个字符加上省略号 // 自动截取前面100个字符加上省略号
@ -26,30 +30,139 @@ const triggers: Trigger<EntityDict, 'essay', BackendRuntimeContext>[] = [
return 1; return 1;
}, },
}, },
// action setTop的时候设置isTop为true // // action setTop的时候设置isTop为true
{ // {
name: '置顶时设置isTop为true', // name: '置顶时设置isTop为true',
// entity: 'essay',
// action: 'setTop',
// when: 'before',
// fn: async ({ operation }, context, option) => {
// const { id, data } = operation;
// assert(data && !(data instanceof Array));
// data.isTop = true;
// return 1;
// },
// },
// // action cancelTop的时候设置isTop为false
// {
// name: '取消置顶时设置isTop为false',
// entity: 'essay',
// action: 'cancelTop',
// when: 'before',
// fn: async ({ operation }, context, option) => {
// const { id, data } = operation;
// assert(data && !(data instanceof Array));
// data.isTop = false;
// return 1;
// },
// },
// 调用like的action的时候创建点赞的记录
{
name: '调用like的action的时候创建点赞的记录',
entity: 'essay', entity: 'essay',
action: 'setTop', action: 'like',
when: 'before', when: 'before',
fn: async ({ operation }, context, option) => { fn: async ({ operation }, context, option) => {
const { id, data } = operation; const { filter } = operation;
assert(data && !(data instanceof Array)); assert(filter && filter.id, "filter is required");
data.isTop = true; const essayId = filter.id;
return 1; const userId = context.getCurrentUserId();
//创建点赞记录
const opers=await context.operate("like", {
id: await generateNewIdAsync(),
action: "create",
data: {
id: await generateNewIdAsync(),
essayId : essayId as string,
userId,
}
},option)
return opers.like?.create||0;
}, },
}, },
// action cancelTop的时候设置isTop为false // 调用unlike的action的时候移除点赞的记录
{ {
name: '取消置顶时设置isTop为false', name: '调用like的action的时候删除点赞的记录',
entity: 'essay', entity: 'essay',
action: 'cancelTop', action: 'unlike',
when: 'before', when: 'before',
fn: async ({ operation }, context, option) => { fn: async ({ operation }, context, option) => {
const { id, data } = operation; const { filter, data} = operation;
assert(data && !(data instanceof Array)); assert(filter && filter.id, "filter is required");
data.isTop = false; const essayId = filter.id;
return 1; const userId = context.getCurrentUserId();
//创建点赞记录
const opers=await context.operate("like", {
id: await generateNewIdAsync(),
action: "remove",
data: {
},
filter: {
essayId,
userId,
}
},option)
return opers.like?.remove||0;
},
},
// 调用collect的action的时候创建收藏的记录
{
name: '调用collect的action的时候创建收藏的记录',
entity: 'essay',
action: 'collect',
when: 'before',
fn: async ({ operation }, context, option) => {
const { filter } = operation;
assert(filter && filter.id, "filter is required");
const essayId = filter.id;
const userId = context.getCurrentUserId();
//创建收藏记录
const opers=await context.operate("collect", {
id: await generateNewIdAsync(),
action: "create",
data: {
id: await generateNewIdAsync(),
essayId : essayId as string,
userId,
}
},option)
return opers.collect?.create||0;
},
},
// 调用uncollect的action的时候移除收藏的记录
{
name: '调用uncollect的action的时候移除收藏的记录',
entity: 'essay',
action: 'uncollect',
when: 'before',
fn: async ({ operation }, context, option) => {
const { filter, data} = operation;
assert(filter && filter.id, "filter is required");
const essayId = filter.id;
const userId = context.getCurrentUserId();
//创建点赞记录
const opers=await context.operate("collect", {
id: await generateNewIdAsync(),
action: "remove",
data: {
},
filter: {
essayId,
userId,
}
},option)
return opers.collect?.remove||0;
}, },
}, },
]; ];

View File

@ -50,7 +50,7 @@ export const essayProjection: EntityDict['essay']['Selection']['data'] = {
name: 1, name: 1,
}, },
iState: 1, iState: 1,
isTop: 1, topState: 1,
// 相关labels // 相关labels
essayLabels$essay: { essayLabels$essay: {
$entity: 'essayLabels', $entity: 'essayLabels',
@ -81,6 +81,28 @@ export const essayProjection: EntityDict['essay']['Selection']['data'] = {
}, },
count: 1, count: 1,
}, },
//点赞相关的记录
like$essay: {
$entity: 'like',
data: {
user: {
id: 1,
name: 1,
nickname: 1,
},
},
},
//收藏相关的记录
collect$essay: {
$entity: 'collect',
data: {
user: {
id: 1,
name: 1,
nickname: 1,
},
},
},
}; };
export const categoryProjection: EntityDict['category']['Selection']['data'] = { export const categoryProjection: EntityDict['category']['Selection']['data'] = {