Skip to content

Commit 9176a99

Browse files
authored
feat: 添加发布状态管理功能 (#994)
* feat(notes/posts): 添加发布状态管理功能 - 在笔记和文章模型中添加 isPublished 字段 - 实现笔记和文章的发布和草稿状态切换 - 添加批量发布和批量设为草稿功能 - 优化列表展示,增加状态列显示 - 更新写入界面,增加发布状态开关 * style: 优化代码格式和组件结构 - 移除了 manage-notes/write.tsx 和 manage-posts/list.tsx 中的多余空行 - 统一了代码格式,提高了代码的可读性和一致性 - 优化了组件结构,有助于未来的维护和扩展 * refactor(manage-posts): 优化文章撰写界面 - 为标题输入框添加 label 属性 - 修改分类选择框的 path 和选项生成逻辑 - 优化标签选择框的样式和交互 - 调整相关文章选择框的实现方式 - 移除冗余代码和不必要的注释 * refactor(manage-posts): 优化文章撰写界面 - 为标题输入框添加 label 属性 - 修改分类选择框的 path 和选项生成逻辑 - 优化标签选择框的样式和交互 - 调整相关文章选择框的实现方式 - 移除冗余代码和不必要的注释
1 parent 7c1b853 commit 9176a99

9 files changed

Lines changed: 324 additions & 88 deletions

File tree

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@
88
"changelog": true
99
},
1010
"scripts": {
11-
"prepare": "pnpm exec simple-git-hooks",
12-
"prebuild": "rm -rf dist",
1311
"dev": "NODE_ENV=development vite --mode development --open --host",
1412
"build": "NODE_ENV=production vite build --mode production",
1513
"preview": "vite preview --port 2323",
1614
"lint": "biome lint --apply src/**/*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json} --no-errors-on-unmatched",
17-
"format": "prettier --write --loglevel warn \"src/**/*.{js,json,tsx,ts,css,scss,vue,html,md}\""
15+
"lint:fix": "biome lint --write .",
16+
"prepare": "husky install",
17+
"release": "sh ./release.sh",
18+
"test": "echo \"Error: no test specified\" && exit 1"
1819
},
1920
"simple-git-hooks": {
2021
"pre-commit": "pnpm exec lint-staged"

src/components/icons/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import Cogs from '@vicons/fa/es/Cogs'
99
import Comment from '@vicons/fa/es/Comment'
1010
import Comments from '@vicons/fa/es/Comments'
1111
import EllipsisH from '@vicons/fa/es/EllipsisH'
12+
import EyeOffIcon from '@vicons/fa/es/EyeDropper'
1213
import Eye from '@vicons/fa/es/Eye'
1314
import File from '@vicons/fa/es/File'
1415
import Flask from '@vicons/fa/es/Flask'
@@ -120,6 +121,7 @@ export { Heart as HeartIcon }
120121
export { EyeHide20Filled as EyeHideIcon }
121122
export { RefreshCircle }
122123
export { AddIcon as PlusIcon }
124+
export { EyeOffIcon as EyeOffIcon }
123125

124126
export const AddIcon = Add12Filled
125127

src/components/xlog-connect/class.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,20 @@ export class CrossBellConnector {
3535
}
3636

3737
static createOrUpdate(data: NoteModel | PostModel) {
38-
// 跳过隐藏的笔记
39-
const passedFields = ['hide', 'password', 'secret']
38+
// 跳过未发布的笔记
39+
const passedFields = ['password', 'secret']
4040
for (const field of passedFields) {
4141
if (field in data && data[field]) {
4242
message.info(`跳过隐藏笔记,命中字段:${field}`)
4343
return Promise.resolve()
4444
}
4545
}
46+
47+
// 跳过未发布的内容
48+
if ('isPublished' in data && data.isPublished === false) {
49+
message.info('跳过未发布内容')
50+
return Promise.resolve()
51+
}
4652

4753
return new Promise((resolve) => {
4854
if (!('ethereum' in window)) {

src/models/note.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
export interface NoteModel {
22
id: string
3-
hide: boolean
43
allowComment: boolean
54
count: {
65
read: number
@@ -22,6 +21,7 @@ export interface NoteModel {
2221
coordinates?: Coordinate
2322

2423
meta?: any
24+
isPublished?: boolean
2525
}
2626

2727
export interface Coordinate {

src/models/post.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export interface PostModel {
2424
pinOrder?: number
2525
related?: Pick<PostModel, 'id' | 'title'>[]
2626
meta?: any
27+
isPublished?: boolean
2728
}
2829

2930
export interface Category {

src/views/manage-notes/list.tsx

Lines changed: 94 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import {
22
BookIcon,
33
BookmarkIcon,
44
EyeHideIcon,
5+
EyeIcon,
6+
EyeOffIcon,
57
HeartIcon,
68
PlusIcon,
79
} from '~/components/icons'
@@ -41,7 +43,7 @@ export const ManageNoteListView = defineComponent({
4143
page,
4244
size,
4345
select:
44-
'title _id nid id created modified mood weather hide publicAt bookmark coordinates location count meta',
46+
'title _id nid id created modified mood weather publicAt bookmark coordinates location count meta isPublished',
4547
...(sortProps.sortBy
4648
? { sortBy: sortProps.sortBy, sortOrder: sortProps.sortOrder }
4749
: {}),
@@ -90,26 +92,27 @@ export const ManageNoteListView = defineComponent({
9092
filter: true,
9193
filterOptions: [
9294
{ label: '回忆项', value: 'bookmark' },
93-
{ label: '隐藏项', value: 'hide' },
95+
{ label: '草稿项', value: 'unpublished' },
9496
],
9597

9698
render(row) {
9799
const isSecret =
98100
row.publicAt && +new Date(row.publicAt) - Date.now() > 0
101+
const isUnpublished = !row.isPublished
99102
return (
100103
<TableTitleLink
101104
inPageTo={`/notes/edit?id=${row.id}`}
102105
title={row.title}
103106
externalLinkTo={`/notes/${row.nid}`}
104107
id={row.id}
105-
withToken={row.hide || isSecret}
108+
withToken={isUnpublished || isSecret}
106109
xLog={row.meta?.xLog}
107110
>
108111
{{
109112
default() {
110113
return (
111114
<>
112-
{row.hide || isSecret ? (
115+
{isUnpublished || isSecret ? (
113116
<Icon color="#34495e">
114117
<EyeHideIcon />
115118
</Icon>
@@ -255,6 +258,46 @@ export const ManageNoteListView = defineComponent({
255258
return <RelativeTime time={row.modified} />
256259
},
257260
},
261+
{
262+
title: '状态',
263+
key: 'isPublished',
264+
width: 120,
265+
render(row) {
266+
return (
267+
<NSpace size={4} align="center">
268+
<div class={`inline-flex items-center gap-1 px-2 py-1 rounded text-xs ${
269+
row.isPublished
270+
? 'bg-green-100 text-green-800'
271+
: 'bg-yellow-100 text-yellow-800'
272+
}`}>
273+
<Icon size={12}>
274+
{row.isPublished ? <EyeIcon /> : <EyeOffIcon />}
275+
</Icon>
276+
{row.isPublished ? '已发布' : '草稿'}
277+
</div>
278+
<NButton
279+
size="tiny"
280+
quaternary
281+
type={row.isPublished ? "warning" : "primary"}
282+
onClick={async () => {
283+
const newStatus = !row.isPublished
284+
try {
285+
await RESTManager.api.notes(row.id)('publish').patch({
286+
data: { isPublished: newStatus }
287+
})
288+
row.isPublished = newStatus
289+
message.success(newStatus ? '已发布' : '已设为草稿')
290+
} catch (_error) {
291+
message.error('操作失败')
292+
}
293+
}}
294+
>
295+
{row.isPublished ? '取消发布' : '发布'}
296+
</NButton>
297+
</NSpace>
298+
)
299+
}
300+
},
258301
{
259302
title: '操作',
260303
key: 'id',
@@ -318,7 +361,7 @@ export const ManageNoteListView = defineComponent({
318361
sortProps.sortBy = props.sortBy
319362
sortProps.sortOrder = props.sortOrder
320363
}}
321-
></Table>
364+
/>
322365
)
323366
},
324367
})
@@ -348,6 +391,52 @@ export const ManageNoteListView = defineComponent({
348391
fetchData()
349392
}}
350393
/>
394+
395+
<HeaderActionButton
396+
name="批量发布"
397+
disabled={checkedRowKeys.value.length === 0}
398+
icon={<EyeIcon />}
399+
variant="success"
400+
onClick={async () => {
401+
try {
402+
await Promise.all(
403+
checkedRowKeys.value.map(id =>
404+
RESTManager.api.notes(id as string)('publish').patch({
405+
data: { isPublished: true }
406+
})
407+
)
408+
)
409+
message.success('批量发布成功')
410+
fetchData() // 重新获取数据
411+
checkedRowKeys.value = []
412+
} catch (_error) {
413+
message.error('批量发布失败')
414+
}
415+
}}
416+
/>
417+
418+
<HeaderActionButton
419+
name="批量设为草稿"
420+
disabled={checkedRowKeys.value.length === 0}
421+
icon={<EyeOffIcon />}
422+
variant="warning"
423+
onClick={async () => {
424+
try {
425+
await Promise.all(
426+
checkedRowKeys.value.map(id =>
427+
RESTManager.api.notes(id as string)('publish').patch({
428+
data: { isPublished: false }
429+
})
430+
)
431+
)
432+
message.success('批量设置草稿成功')
433+
fetchData() // 重新获取数据
434+
checkedRowKeys.value = []
435+
} catch (_error) {
436+
message.error('批量设置草稿失败')
437+
}
438+
}}
439+
/>
351440
<HeaderActionButton to={'/notes/edit'} icon={<PlusIcon />} />
352441
</>
353442
),

src/views/manage-notes/write.tsx

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import { AiHelperButton } from '~/components/ai/ai-helper'
3535
import { HeaderActionButton } from '~/components/button/rounded-button'
3636
import { TextBaseDrawer } from '~/components/drawer/text-base-drawer'
3737
import { Editor } from '~/components/editor/universal'
38-
import { HeartIcon, SlidersHIcon, TelegramPlaneIcon } from '~/components/icons'
38+
import { HeartIcon, SlidersHIcon, TelegramPlaneIcon, EyeIcon, EyeOffIcon } from '~/components/icons'
3939
import { MaterialInput } from '~/components/input/material-input'
4040
import { GetLocationButton } from '~/components/location/get-location-button'
4141
import { SearchLocationButton } from '~/components/location/search-button'
@@ -63,12 +63,12 @@ const CrossBellConnectorIndirector = defineAsyncComponent({
6363
})
6464

6565
type NoteReactiveType = {
66-
hide: boolean
6766
mood: string
6867
weather: string
6968
password: string | null
7069
publicAt: Date | null
7170
bookmark: boolean
71+
isPublished: boolean
7272

7373
location: null | string
7474
coordinates: null | Coordinate
@@ -116,7 +116,6 @@ const NoteWriteView = defineComponent(() => {
116116
const resetReactive: () => NoteReactiveType = () => ({
117117
text: '',
118118
title: '',
119-
hide: false,
120119
bookmark: false,
121120
mood: '',
122121

@@ -126,6 +125,7 @@ const NoteWriteView = defineComponent(() => {
126125
location: '',
127126
coordinates: null,
128127
allowComment: true,
128+
isPublished: true,
129129

130130
id: undefined,
131131
nid: undefined,
@@ -272,6 +272,28 @@ const NoteWriteView = defineComponent(() => {
272272
/>
273273

274274
<HeaderPreviewButton data={data} iframe />
275+
276+
<HeaderActionButton
277+
icon={data.isPublished ? <EyeOffIcon /> : <EyeIcon />}
278+
variant={data.isPublished ? "warning" : "success"}
279+
onClick={async () => {
280+
if (!data.id) {
281+
message.warning('请先保存笔记')
282+
return
283+
}
284+
const newStatus = !data.isPublished
285+
try {
286+
await RESTManager.api.notes(data.id)('publish').patch({
287+
data: { isPublished: newStatus }
288+
})
289+
data.isPublished = newStatus
290+
message.success(newStatus ? '笔记已发布' : '笔记已设为草稿')
291+
} catch (error) {
292+
message.error('状态切换失败')
293+
}
294+
}}
295+
name={data.isPublished ? "设为草稿" : "立即发布"}
296+
/>
275297
<HeaderActionButton
276298
icon={<TelegramPlaneIcon />}
277299
onClick={handleSubmit}
@@ -284,6 +306,7 @@ const NoteWriteView = defineComponent(() => {
284306
onClick={() => {
285307
drawerShow.value = true
286308
}}
309+
title="打开设置"
287310
>
288311
<Icon>
289312
<SlidersHIcon />
@@ -508,13 +531,6 @@ const NoteWriteView = defineComponent(() => {
508531
</NDatePicker>
509532
</NFormItem>
510533

511-
<NFormItem label="隐藏" labelAlign="right" labelPlacement="left">
512-
<NSwitch
513-
value={data.hide}
514-
onUpdateValue={(e) => void (data.hide = e)}
515-
></NSwitch>
516-
</NFormItem>
517-
518534
<NFormItem
519535
label="标记为回忆项"
520536
labelAlign="right"
@@ -525,6 +541,17 @@ const NoteWriteView = defineComponent(() => {
525541
onUpdateValue={(e) => void (data.bookmark = e)}
526542
></NSwitch>
527543
</NFormItem>
544+
<NFormItem label="发布状态" labelAlign="right" labelPlacement="left">
545+
<NSwitch
546+
value={data.isPublished}
547+
onUpdateValue={(e) => void (data.isPublished = e)}
548+
>
549+
{{
550+
checked: () => '已发布',
551+
unchecked: () => '草稿'
552+
}}
553+
</NSwitch>
554+
</NFormItem>
528555
</TextBaseDrawer>
529556

530557
{/* Drawer END */}

0 commit comments

Comments
 (0)