新增点赞相关功能

This commit is contained in:
Pan Qiancheng 2024-11-01 18:29:53 +08:00
parent 646953d544
commit 53edab4f35
9 changed files with 181 additions and 3 deletions

View File

@ -116,6 +116,18 @@ const i18ns: I18n[] = [
"tips": "请选择模式"
}
},
{
id: "b4b7c9f5d8cc5466e9424d7538ff0176",
namespace: "new-demo-p-frontend-essay-details",
language: "zh-CN",
module: "new-demo",
position: "src/pages/frontend/essay/details",
data: {
"like": "点赞",
"liked": "已点赞",
"unlike": "取消点赞"
}
},
{
id: "aa6631e469935d0fb5c556e96dcc4379",
namespace: "new-demo-p-frontend-home",
@ -622,7 +634,9 @@ const i18ns: I18n[] = [
"publish": "发布",
"withdraw": "撤回",
"setTop": "置顶",
"cancelTop": "取消置顶"
"cancelTop": "取消置顶",
"like": "点赞",
"unlike": "取消点赞"
},
"v": {
"iState": {
@ -747,6 +761,20 @@ const i18ns: I18n[] = [
}
}
},
{
id: "b9691c35f8511fb42aa59cbcc8645610",
namespace: "like",
language: "zh-CN",
module: "",
position: "oak-app-domain/Like",
data: {
"name": "点赞记录",
"attr": {
"essay": "文章",
"user": "用户"
}
}
},
{
id: "f3c966c0e1deb4ca4c10e10117143849",
namespace: "livestream",

View File

@ -35,7 +35,10 @@ export const IActionDef: ActionDef<IAction, IState> = {
// 用户行为操作
export type CommonAction = 'setTop' | 'cancelTop';
type Action = IAction | CommonAction;
// 点赞操作
export type LikeAction = 'like' | 'unlike';
type Action = IAction | CommonAction | LikeAction;
export const entityDesc: EntityDesc<
Schema,
@ -65,6 +68,8 @@ export const entityDesc: EntityDesc<
withdraw: '撤回',
setTop: '置顶',
cancelTop: '取消置顶',
like: '点赞',
unlike: '取消点赞',
},
v: {
iState: {

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

@ -0,0 +1,22 @@
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

@ -5,6 +5,10 @@ export default OakComponent({
isList: false,
projection: essayProjection,
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 fileIndex = data?.extraFile$entity?.findIndex(
(item) => item.tag1 === 'cover'
);
@ -16,11 +20,15 @@ export default OakComponent({
item: data,
// 获取封面的url地址
cover: url,
isLiked,
likeNums
};
}
return {
item: data,
cover: '',
isLiked,
likeNums
};
},
});

View File

@ -0,0 +1,5 @@
{
"like": "点赞",
"liked": "已点赞",
"unlike": "取消点赞"
}

View File

@ -3,6 +3,22 @@
display: flex;
flex-direction: column;
.back {
font-size: 14px;
}
.nums {
font-size: 14px;
color: #666;
}
.likes {
display: flex;
flex-direction: row;
align-items: center;
gap: 10px;
}
.top {
position: relative;
width: 100%;

View File

@ -6,6 +6,7 @@ import MdViewer from '@project/components/common/byteMD/MdViewer';
import FrontendFooter from '@project/components/frontend/home/FrontendFooter';
import { Button } from 'antd';
import { VerticalAlignTopOutlined } from '@ant-design/icons';
import OakIcon from 'oak-frontend-base/es/components/icon';
const EssayDetails = (
props: WebComponentProps<
@ -15,11 +16,14 @@ const EssayDetails = (
{
item: RowWithActions<EntityDict, 'essay'>;
cover: string;
isLiked: boolean;
likeNums: number;
}
>
) => {
const { item, cover } = props.data;
const { item, cover, isLiked, likeNums } = props.data;
const [showScrollTop, setShowScrollTop] = useState(false);
const { t, update, execute } = props.methods;
useEffect(() => {
const handleScroll = () => {
@ -74,6 +78,32 @@ const EssayDetails = (
>
</Button>
<div className={Styles.likes}>
{isLiked ? (
<Button
onClick={() => {
update({
}, 'unlike');
execute();
}}
shape='circle'
>
<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>
<MdViewer md={item.content!} />
</div>

View File

@ -2,6 +2,7 @@ import { EntityDict } from '@oak-app-domain';
import { Trigger } from 'oak-domain/lib/types';
import { BackendRuntimeContext } from '../context/BackendRuntimeContext';
import assert from 'assert';
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
const triggers: Trigger<EntityDict, 'essay', BackendRuntimeContext>[] = [
{
@ -52,6 +53,57 @@ const triggers: Trigger<EntityDict, 'essay', BackendRuntimeContext>[] = [
return 1;
},
},
// 调用like的action的时候创建点赞的记录
{
name: "调用like的action的时候创建点赞的记录",
entity: "essay",
action: "like",
when: "before",
fn: async ({ operation }, context, option) => {
const { filter } = operation;
assert(filter && filter.id, "filter is required");
const eId = filter.id;
const userId = context.getCurrentUserId();
// 创建点赞记录
const opres = await context.operate("like", {
id: await generateNewIdAsync(),
action: "create",
data: {
id: await generateNewIdAsync(),
essayId: eId as string,
userId,
}
}, option)
return opres.like?.create || 0;
}
},
// 调用unlike的action的时候移除点赞的记录
{
name: "调用unlike的action的时候移除点赞的记录",
entity: "essay",
action: "unlike",
when: "before",
fn: async ({ operation }, context, option) => {
const { filter, data } = operation;
assert(filter && filter.id, "filter is required");
const eId = filter.id;
const userId = context.getCurrentUserId();
// 创建点赞记录
const opres = await context.operate("like", {
id: await generateNewIdAsync(),
action: "remove",
data: {
},
filter: {
essayId: eId as string,
userId,
}
}, option)
return opres.like?.remove || 0;
}
}
];
export default triggers;

View File

@ -81,6 +81,18 @@ export const essayProjection: EntityDict['essay']['Selection']['data'] = {
},
count: 1,
},
// 点赞相关的记录
like$essay: {
$entity: "like",
data: {
id: 1,
user: {
id: 1,
name: 1,
nickname: 1,
}
}
}
};
export const categoryProjection: EntityDict['category']['Selection']['data'] = {