Compare commits

..

2 Commits

Author SHA1 Message Date
potato cbd417fe8d 1233 2024-10-30 16:31:19 +08:00
potato ba0ae9b9ef 123123 2024-10-30 16:30:30 +08:00
37 changed files with 390 additions and 331 deletions

View File

@ -1,3 +1,2 @@
{
"typescript.tsdk": "node_modules\\typescript\\lib"
}

View File

@ -62,6 +62,7 @@
"antd-mobile-icons": "^0.3.0",
"classnames": "^2.3.1",
"crypto-browserify": "3.12.0",
"readable-stream": "3.6.2",
"crypto-js": "^4.1.1",
"dayjs": "^1.11.5",
"echarts": "^5.3.0",
@ -94,7 +95,6 @@
"react-responsive": "^9.0.0-beta.10",
"react-router-dom": "^6.4.0",
"react-slick": "^0.29.0",
"readable-stream": "3.6.2",
"rmc-pull-to-refresh": "^1.0.13",
"uuid": "^8.3.2"
},
@ -205,8 +205,7 @@
"webpack": "^5.86.0",
"webpack-dev-server": "^4.15.1",
"webpack-manifest-plugin": "^4.0.2",
"workbox-webpack-plugin": "^6.4.1",
"typescript-plugin-css-modules": "^5.1.0"
"workbox-webpack-plugin": "^6.4.1"
},
"browserslist": {
"production": [

View File

@ -14,6 +14,24 @@ const checkers: Checker<EntityDict, 'essay', RuntimeCxt>[] = [
$or: [{ iState: 'unpublished' }, { iState: 'withdrawn' }],
},
},
// settop的时候只能是isTop为false的
{
entity: 'essay',
action: 'setTop',
type: 'row',
filter: {
isTop: false,
},
},
// cancelTop的时候只能是isTop为true的
{
entity: 'essay',
action: 'cancelTop',
type: 'row',
filter: {
isTop: true,
},
},
// 在删除essay的时候同步删除extraFile和essayLabels还有log
// 注意在checker中为非异步环境这点与trigger不同
{
@ -70,6 +88,21 @@ const checkers: Checker<EntityDict, 'essay', RuntimeCxt>[] = [
},
option
);
},
() => {
//删除相关的comment
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 @@
{
}

View File

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

View File

@ -0,0 +1,62 @@
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 { Content } from 'antd/es/layout/layout';
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 features = useFeatures();
const { t,execute,addItem} = props.methods;
const [newCommentText, setNewCommentText] = React.useState('');
const handleSumit = () => {
addItem({
content: newCommentText,
creatorId: features.token.getUserId(),
essayId:props.data.essayId,
});
execute();
//提交后清空输入框
setNewCommentText('');
}
return (
<>
This is
{list && (
<>
{list.map((item) => {
return <div key={item.id} className={Styles.item}>{item.id}</div>;
})}
</>
)}
{/* textarea */}
<textarea
value={newCommentText}
onChange={(e) => setNewCommentText(e.target.value)}
></textarea>
{/*button */}
<Button
onClick={handleSumit}
> {t('common::submit')}</Button>
</>
);
};
export default List;

View File

@ -1,6 +1,7 @@
import React from 'react';
import Styles from './styles.module.less';
import CategoryList from './categories';
import { useHomeContext } from './context/homeContext';
const FrontendHeader = () => {
return (

View File

@ -5,28 +5,14 @@
padding: 10px;
gap: 10px;
align-items: center;
height: 40px;
.title {
height: 100%;
cursor: pointer;
transition: all 0.3s;
// 内部文字居中
display: flex;
align-items: center;
padding: 0 10px;
&:hover {
color: #1890ff;
}
}
}
.noCategory {
display: flex;
justify-content: center;
align-items: center;
height: 40px;
color: #666;
font-weight: bold;
}

View File

@ -49,7 +49,7 @@ const CategoryList = (
})}
</div>
) : (
<div className={Styles.noCategory}>{t('noCategaries')}</div>
<div>{t('noCategaries')}</div>
)}
</>
);

View File

@ -9,9 +9,9 @@ export default OakComponent({
// 先按置顶排序
sorter: {
$attr: {
topState: 1,
isTop: 1,
},
$direction: 'asc',
$direction: 'desc',
},
},
{
@ -98,7 +98,6 @@ export default OakComponent({
cover: '',
};
}),
searchParam: this.props.searchParam
};
},
});

View File

@ -1,6 +1,6 @@
import React from 'react';
import { EntityDict } from '@project/oak-app-domain';
import { RowWithActions } from 'oak-frontend-base';
import { RowWithActions, WebComponentProps } from 'oak-frontend-base';
import Styles from './styles.module.less';
import dayjs from 'dayjs';
import useFeatures from '@project/hooks/useFeatures';
@ -19,6 +19,8 @@ const EssayItem = (props: {
};
}) => {
const { item, navigateTo, t, searchParam } = props;
const features = useFeatures();
// features.extraFile.
return (
<>
@ -44,7 +46,7 @@ const EssayItem = (props: {
</div>
<div className={Styles.body}>
<div className={Styles.title}>
{item.topState === 'top' && (
{item.isTop && (
<div className={Styles.top}>
{t('common::top')}
</div>

View File

@ -27,19 +27,6 @@
}
}
.noCover {
width: 200px;
height: 120px;
display: flex;
justify-content: center;
align-items: center;
background: #f0f0f0;
color: #666;
font-size: 12px;
font-weight: bold;
border-radius: 5px;
}
.body {
flex: 1;
@ -110,10 +97,5 @@
}
}
}
.creator {
display: flex;
align-items: center;
}
}
}

View File

@ -84,8 +84,3 @@
}
}
}
.categories {
// 放一个属性但是不影响任何东西
display: inherit;
}

View File

@ -14,6 +14,14 @@ const i18ns: I18n[] = [
"createCategory": "创建分类"
}
},
{
id: "63f061bd7f1010b396cadb4bb784734f",
namespace: "new-demo-p-console-comment-list",
language: "zh-CN",
module: "new-demo",
position: "src/pages/console/comment/list",
data: {}
},
{
id: "4813a0a3acdfac90d3b3b3cfa37de82c",
namespace: "new-demo-p-console-essay-list",
@ -116,18 +124,6 @@ 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",
@ -223,6 +219,14 @@ const i18ns: I18n[] = [
position: "src/components/console/switch",
data: {}
},
{
id: "cd2859b5ea39b8821f00ceccd2f7cc49",
namespace: "new-demo-c-frontend-comment-list",
language: "zh-CN",
module: "new-demo",
position: "src/components/frontend/comment/list",
data: {}
},
{
id: "e39f6f508caae16141519422f5b9377c",
namespace: "new-demo-c-frontend-home-categories",
@ -340,7 +344,8 @@ const i18ns: I18n[] = [
"userManage": "用户管理",
"relationManage": "关系管理",
"consoleHome": "控制台首页",
"labelManage": "标签管理"
"labelManage": "标签管理",
"conmmentManage": "评论管理"
}
},
{
@ -561,6 +566,21 @@ const i18ns: I18n[] = [
}
}
},
{
id: "f70261a2fc23af2a9f459353e5ac1ea3",
namespace: "comment",
language: "zh-CN",
module: "",
position: "oak-app-domain/Comment",
data: {
"name": "评论",
"attr": {
"creator": "创建者",
"content": "内容",
"essay": "文章"
}
}
},
{
id: "b06ea1d96db9f0a2d2f3ff3ad966c9c5",
namespace: "domain",
@ -628,25 +648,19 @@ const i18ns: I18n[] = [
"logs": "修改记录",
"category": "分类",
"iState": "状态",
"topState": "置顶状态"
"isTop": "是否置顶"
},
"action": {
"publish": "发布",
"withdraw": "撤回",
"setTop": "置顶",
"cancelTop": "取消置顶",
"like": "点赞",
"unlike": "取消点赞"
"cancelTop": "取消置顶"
},
"v": {
"iState": {
"unpublished": "未发布",
"published": "已发布",
"withdrawn": "已撤回"
},
"topState": {
"top": "置顶",
"normal": "普通"
}
}
}
@ -765,20 +779,6 @@ 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

@ -1,21 +1,21 @@
import { EntityDesc, EntityShape } from 'oak-domain/lib/types';
import { Schema as Essay } from './Essay';
import { EntityDesc, EntityShape } from 'oak-domain/lib/types';
import { String } from 'oak-domain/lib/types/DataType';
import { Schema as User } from 'oak-general-business/lib/entities/User';
// Like.ts
// 关联文章和用户,表示点赞记录
//Comment.ToDoIdSubQuery
export interface Schema extends EntityShape {
creator: User;
content: String<1024>;
essay: Essay;
user: User;
}
export const entityDesc: EntityDesc<Schema> = {
locales: {
zh_CN: {
name: '点赞记录',
name: '评论',
attr: {
creator: '创建者',
content: '内容',
essay: '文章',
user: '用户',
},
},
},

View File

@ -17,6 +17,7 @@ export interface Schema extends EntityShape {
images: ExtraFile[];
logs: Log[];
category?: Category;
isTop: Boolean;
}
export type IState = 'unpublished' | 'published' | 'withdrawn';
@ -32,22 +33,9 @@ export const IActionDef: ActionDef<IAction, IState> = {
};
// 用户行为操作
export type TopAction = 'setTop' | 'cancelTop';
export type CommonAction = 'setTop' | 'cancelTop';
export type TopState = 'top' | 'normal';
export const TopActionDef: ActionDef<TopAction, TopState> = {
stm: {
setTop: ['normal', 'top'],
cancelTop: ['top', 'normal'],
},
is: 'normal',
};
// 点赞操作
export type LikeAction = 'like' | 'unlike';
type Action = IAction | TopAction | LikeAction;
type Action = IAction | CommonAction;
export const entityDesc: EntityDesc<
Schema,
@ -55,7 +43,6 @@ export const entityDesc: EntityDesc<
'',
{
iState: IState;
topState: TopState;
}
> = {
locales: {
@ -71,15 +58,13 @@ export const entityDesc: EntityDesc<
logs: '修改记录',
category: '分类',
iState: '状态',
topState: '置顶状态',
isTop: '是否置顶',
},
action: {
publish: '发布',
withdraw: '撤回',
setTop: '置顶',
cancelTop: '取消置顶',
like: '点赞',
unlike: '取消点赞',
},
v: {
iState: {
@ -87,10 +72,6 @@ export const entityDesc: EntityDesc<
published: '已发布',
withdrawn: '已撤回',
},
topState: {
top: '置顶',
normal: '普通',
},
},
},
},
@ -102,10 +83,6 @@ export const entityDesc: EntityDesc<
published: '#87d068',
withdrawn: '#2db7f5',
},
topState: {
top: '#f50',
normal: '#87d068',
},
},
},
indexes: [

View File

@ -1,3 +1,4 @@
import { message } from 'antd';
import BaseConsole, { Menu } from 'oak-frontend-base/es/features/console';
import { EntityDict } from '@project/oak-app-domain';
import { groupBy } from 'oak-domain/lib/utils/lodash';
@ -38,25 +39,22 @@ const menus: IMenu[] = [
name: 'consoleHome',
icon: 'workbench',
url: '/home',
destEntity: 'userRelation',
destEntity: '',
order: 0,
actions: ['select'],
},
{
name: 'userManage',
icon: 'addpeople',
url: '/userRelation/manage',
parent: 'System',
destEntity: 'userRelation',
actions: ['select'],
destEntity: '',
},
{
name: 'relationManage',
icon: 'share',
url: '/relation/entityList',
parent: 'System',
destEntity: 'userRelation',
actions: ['select'],
destEntity: '',
},
// category
{
@ -64,16 +62,14 @@ const menus: IMenu[] = [
icon: 'stealth',
url: '/category/list',
order: 2,
destEntity: 'userRelation',
actions: ['select'],
destEntity: '',
},
{
name: 'essayManage',
icon: 'barrage',
url: '/essay/list',
order: 3,
destEntity: 'userRelation',
actions: ['select'],
destEntity: '',
},
// 标签关联
{
@ -81,9 +77,16 @@ const menus: IMenu[] = [
icon: 'accessory',
url: '/label/list',
order: 4,
destEntity: 'userRelation',
actions: ['select'],
destEntity: '',
},
//评论管理
{
name: 'commentManage',
icon: 'message',
url:'/comment/list',
destEntity: '',
order:5,
}
];
export default class Console extends BaseConsole<EntityDict, IMenu> {

View File

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

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,3 @@
{
}

View File

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

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 PageHeader from 'oak-frontend-base/es/components/pageHeader2';
import { ListPro } from '@project/components/AbstractComponents';
import path from 'path';
import { render } from 'nprogress';
const List = (
props: WebComponentProps<
EntityDict,
'comment',
true,
{
list: RowWithActions<EntityDict, 'comment'>[];
}
>
) => {
const { oakFullpath } = props.data;
const { list } = props.data;
const { t, navigateTo, updateItem, execute, addItem, clean, removeItem } =
props.methods;
const [upsertId, setUpsertId] = React.useState<string | null>(null);
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':
setUpsertId(row.id);
break;
case 'remove':
removeItem(row.id);
execute();
break;
default:
break;
}
}
return (
<PageHeader title={t('pageTitle')}>
<ListPro
entity='category'
attributes={attrs}
data={list}
oakPath={`${oakFullpath}`}
onAction={onAction}
></ListPro>
{list && (
<>
{list.map((item) => {
return (
<div key={item.id} className={Styles.item}>
{item.id}
</div>
);
})}
</>
)}
</PageHeader>
);
};
export default List;

View File

@ -9,9 +9,9 @@ export default OakComponent({
// 先按置顶排序
sorter: {
$attr: {
topState: 1,
isTop: 1,
},
$direction: 'asc',
$direction: 'desc',
},
},
{
@ -52,5 +52,5 @@ export default OakComponent({
}),
};
},
actions: ['update', 'withdraw', 'publish', 'remove', 'setTop', 'cancelTop'],
actions: ['update', 'withdraw', 'publish', 'setTop', 'cancelTop', 'remove'],
});

View File

@ -29,16 +29,3 @@
gap: 10px;
margin-top: 10px;
}
.noCover {
width: 100px;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
background: #f0f0f0;
color: #666;
font-size: 12px;
font-weight: bold;
border-radius: 5px;
}

View File

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

View File

@ -5,10 +5,6 @@ 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'
);
@ -20,15 +16,11 @@ export default OakComponent({
item: data,
// 获取封面的url地址
cover: url,
isLiked,
likeNums
};
}
return {
item: data,
cover: '',
isLiked,
likeNums
};
},
});

View File

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

View File

@ -3,22 +3,6 @@
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,8 +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';
import List from '@project/components/frontend/comment/list';
const EssayDetails = (
props: WebComponentProps<
EntityDict,
@ -16,14 +15,13 @@ const EssayDetails = (
{
item: RowWithActions<EntityDict, 'essay'>;
cover: string;
isLiked: boolean;
likeNums: number;
}
>
) => {
const { item, cover, isLiked, likeNums } = props.data;
const { oakFullpath } = props.data;
const { item, cover } = props.data;
const [showScrollTop, setShowScrollTop] = useState(false);
const { t, update, execute } = props.methods;
useEffect(() => {
const handleScroll = () => {
@ -78,35 +76,13 @@ 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>
<List
oakPath={`${oakFullpath}.comment$essay`}
essayId={item.id}
></List>
<div className={Styles.blank}></div>
</>
)}

View File

@ -2,7 +2,6 @@ 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>[] = [
{
@ -15,6 +14,7 @@ const triggers: Trigger<EntityDict, 'essay', BackendRuntimeContext>[] = [
assert(data && !(data instanceof Array));
// 若不存在meta则初始化一个空对象
data.meta = data.meta || {};
data.isTop = data.isTop || false;
assert(data.content, 'content is required');
if (!data.summary) {
// 自动截取前面100个字符加上省略号
@ -23,60 +23,35 @@ const triggers: Trigger<EntityDict, 'essay', BackendRuntimeContext>[] = [
? data.content.slice(0, 100) + '...'
: data.content;
}
return 0;
return 1;
},
},
// 调用like的action的时候创建点赞的记录
// action setTop的时候设置isTop为true
{
name: "调用like的action的时候创建点赞的记录",
entity: "essay",
action: "like",
when: "before",
name: '置顶时设置isTop为true',
entity: 'essay',
action: 'setTop',
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;
}
const { id, data } = operation;
assert(data && !(data instanceof Array));
data.isTop = true;
return 1;
},
// 调用unlike的action的时候移除点赞的记录
},
// action cancelTop的时候设置isTop为false
{
name: "调用unlike的action的时候移除点赞的记录",
entity: "essay",
action: "unlike",
when: "before",
name: '取消置顶时设置isTop为false',
entity: 'essay',
action: 'cancelTop',
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: {
const { id, data } = operation;
assert(data && !(data instanceof Array));
data.isTop = false;
return 1;
},
},
filter: {
essayId: eId as string,
userId,
}
}, option)
return opres.like?.remove || 0;
}
}
];
export default triggers;

View File

@ -29,7 +29,7 @@ const triggers: Trigger<EntityDict, 'essayLabels', BackendRuntimeContext>[] = [
const sequence = count + 1;
data.sequence = sequence;
data.meta = {};
return 0;
return 1;
},
},
// 在create之前查询essayId和labelId的组合是否已经存在如果存在则不允许创建
@ -83,7 +83,7 @@ const triggers: Trigger<EntityDict, 'essayLabels', BackendRuntimeContext>[] = [
if (count3 > 0) {
throw new OakUserException('该标签已经关联到文章中');
}
return 0;
return 1;
},
},
{
@ -143,7 +143,7 @@ const triggers: Trigger<EntityDict, 'essayLabels', BackendRuntimeContext>[] = [
option
);
// 将其和当前的seq互换
const opres1 = await context.operate(
await context.operate(
'essayLabels',
{
id: await generateNewIdAsync(),
@ -157,7 +157,7 @@ const triggers: Trigger<EntityDict, 'essayLabels', BackendRuntimeContext>[] = [
},
option
);
const opres2 = await context.operate(
await context.operate(
'essayLabels',
{
id: await generateNewIdAsync(),
@ -172,10 +172,7 @@ const triggers: Trigger<EntityDict, 'essayLabels', BackendRuntimeContext>[] = [
option
);
return (
(opres1.essayLabels?.update || 0) +
(opres2.essayLabels?.update || 0)
);
return 1;
},
},
{
@ -245,7 +242,7 @@ const triggers: Trigger<EntityDict, 'essayLabels', BackendRuntimeContext>[] = [
option
);
// 将其和当前的seq互换
const opres1 = await context.operate(
await context.operate(
'essayLabels',
{
id: await generateNewIdAsync(),
@ -259,7 +256,7 @@ const triggers: Trigger<EntityDict, 'essayLabels', BackendRuntimeContext>[] = [
},
option
);
const opres2 = await context.operate(
await context.operate(
'essayLabels',
{
id: await generateNewIdAsync(),
@ -273,10 +270,7 @@ const triggers: Trigger<EntityDict, 'essayLabels', BackendRuntimeContext>[] = [
},
option
);
return (
(opres1.essayLabels?.update || 0) +
(opres2.essayLabels?.update || 0)
);
return 1;
},
},
{
@ -297,9 +291,6 @@ const triggers: Trigger<EntityDict, 'essayLabels', BackendRuntimeContext>[] = [
id && nextId,
'id and nextId are required when moving essayLabels'
);
let res = 0;
// 查询当前 essayLabel 和目标 essayLabel 的信息
const [currentLabel, nextLabel] = await Promise.all([
context.select(
@ -351,7 +342,7 @@ const triggers: Trigger<EntityDict, 'essayLabels', BackendRuntimeContext>[] = [
);
for (const label of affectedLabels) {
const opres = await context.operate(
await context.operate(
'essayLabels',
{
id: await generateNewIdAsync(),
@ -361,7 +352,6 @@ const triggers: Trigger<EntityDict, 'essayLabels', BackendRuntimeContext>[] = [
},
option
);
res += opres.essayLabels?.update || 0;
}
} else {
// 下移逻辑
@ -381,7 +371,7 @@ const triggers: Trigger<EntityDict, 'essayLabels', BackendRuntimeContext>[] = [
);
for (const label of affectedLabels) {
const opres = await context.operate(
await context.operate(
'essayLabels',
{
id: await generateNewIdAsync(),
@ -391,13 +381,11 @@ const triggers: Trigger<EntityDict, 'essayLabels', BackendRuntimeContext>[] = [
},
option
);
res += opres.essayLabels?.update || 0;
}
}
// 更新当前 essayLabel 的 sequence
const opres = await context.operate(
await context.operate(
'essayLabels',
{
id: await generateNewIdAsync(),
@ -407,14 +395,12 @@ const triggers: Trigger<EntityDict, 'essayLabels', BackendRuntimeContext>[] = [
},
option
);
res += opres.essayLabels?.update || 0;
} else {
// 位置相同,无需移动
return 0;
return 1;
}
return res;
return 1;
},
},
{
@ -428,8 +414,6 @@ const triggers: Trigger<EntityDict, 'essayLabels', BackendRuntimeContext>[] = [
const { id } = filter as { id: string };
assert(id, 'id is required when moving essayLabels to first');
let res = 0;
// 查询当前 essayLabel 的信息
const [currentLabel] = await context.select(
'essayLabels',
@ -466,7 +450,7 @@ const triggers: Trigger<EntityDict, 'essayLabels', BackendRuntimeContext>[] = [
// 更新受影响的 essayLabels
for (const label of affectedLabels) {
const opres = await context.operate(
await context.operate(
'essayLabels',
{
id: await generateNewIdAsync(),
@ -476,11 +460,10 @@ const triggers: Trigger<EntityDict, 'essayLabels', BackendRuntimeContext>[] = [
},
option
);
res += opres.essayLabels?.update || 0;
}
// 将当前 essayLabels 移到第一位
const opres = await context.operate(
await context.operate(
'essayLabels',
{
id: await generateNewIdAsync(),
@ -490,10 +473,9 @@ const triggers: Trigger<EntityDict, 'essayLabels', BackendRuntimeContext>[] = [
},
option
);
res += opres.essayLabels?.update || 0;
}
return res;
return 1;
},
},
{
@ -507,8 +489,6 @@ const triggers: Trigger<EntityDict, 'essayLabels', BackendRuntimeContext>[] = [
const { id } = filter as { id: string };
assert(id, 'id is required when moving essayLabels to last');
let res = 0;
// 查询当前 essayLabels 的信息
const [currentLabel] = await context.select(
'essayLabels',
@ -558,7 +538,7 @@ const triggers: Trigger<EntityDict, 'essayLabels', BackendRuntimeContext>[] = [
// 更新受影响的 essayLabels
for (const label of affectedLabels) {
const opres = await context.operate(
await context.operate(
'essayLabels',
{
id: await generateNewIdAsync(),
@ -568,12 +548,10 @@ const triggers: Trigger<EntityDict, 'essayLabels', BackendRuntimeContext>[] = [
},
option
);
res += opres.essayLabels?.update || 0;
}
// 将当前 essayLabels 移到最后一位
const opres = await context.operate(
await context.operate(
'essayLabels',
{
id: await generateNewIdAsync(),
@ -583,11 +561,9 @@ const triggers: Trigger<EntityDict, 'essayLabels', BackendRuntimeContext>[] = [
},
option
);
res += opres.essayLabels?.update || 0;
}
return res;
return 1;
},
},
{
@ -599,7 +575,6 @@ const triggers: Trigger<EntityDict, 'essayLabels', BackendRuntimeContext>[] = [
const { filter } = operation;
assert(filter);
let res = 0;
// 因为删除可能是一个批量操作filter可以查询到0-n条记录
const essayLabels = await context.select(
'essayLabels',
@ -634,7 +609,7 @@ const triggers: Trigger<EntityDict, 'essayLabels', BackendRuntimeContext>[] = [
// 更新受影响的 essayLabels
for (const label of affectedLabels) {
const opres = await context.operate(
await context.operate(
'essayLabels',
{
id: await generateNewIdAsync(),
@ -644,11 +619,9 @@ const triggers: Trigger<EntityDict, 'essayLabels', BackendRuntimeContext>[] = [
},
option
);
res += opres.essayLabels?.update || 0;
}
}
return res;
return 1;
},
},
];

View File

@ -15,7 +15,7 @@ const triggers: Trigger<EntityDict, 'label', BackendRuntimeContext>[] = [
const { data } = operation;
assert(data && !Array.isArray(data));
data.meta = {};
return 0;
return 1;
},
},
// 在执行addToEssay时将创建essayLabels将label关联到essay中
@ -57,7 +57,7 @@ const triggers: Trigger<EntityDict, 'label', BackendRuntimeContext>[] = [
}
// 创建关联
const opres = await context.operate(
await context.operate(
'essayLabels',
{
id: await generateNewIdAsync(),
@ -71,7 +71,7 @@ const triggers: Trigger<EntityDict, 'label', BackendRuntimeContext>[] = [
option
);
return opres.essayLabels?.create || 0;
return 1;
},
},
];

View File

@ -50,7 +50,7 @@ export const essayProjection: EntityDict['essay']['Selection']['data'] = {
name: 1,
},
iState: 1,
topState: 1,
isTop: 1,
// 相关labels
essayLabels$essay: {
$entity: 'essayLabels',
@ -81,18 +81,6 @@ 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'] = {

View File

@ -16,11 +16,6 @@
"dom.iterable",
"esnext"
],
"plugins": [
{
"name": "typescript-plugin-css-modules",
},
],
//"outDir": "lib", /* Redirect output structure to the directory. */
//"rootDir": "src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
"types": [