feat(web): continue Ant settings migration and tighten page chrome
Migrate remaining settings screens to Ant Design Vue, densify teams/roles UI, drop page-header subtitles project-wide, and polish notes cards plus contacts row actions. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -790,12 +790,7 @@ onBeforeUnmount(() => {
|
||||
|
||||
<template>
|
||||
<a-flex vertical :gap="16">
|
||||
<a-flex vertical :gap="4">
|
||||
<a-typography-title :level="3" :style="{ margin: 0 }">یادداشتها</a-typography-title>
|
||||
<a-typography-text type="secondary">
|
||||
فکر کنید، یادداشت کنید، هیچ چیز را فراموش نکنید.
|
||||
</a-typography-text>
|
||||
</a-flex>
|
||||
<a-typography-title :level="3" :style="{ margin: 0 }">یادداشتها</a-typography-title>
|
||||
|
||||
<a-alert v-if="error" type="error" show-icon :message="error" closable @close="error = ''" />
|
||||
|
||||
@@ -896,7 +891,7 @@ onBeforeUnmount(() => {
|
||||
</a-button>
|
||||
</a-empty>
|
||||
|
||||
<a-row v-else :gutter="[12, 12]" align="top">
|
||||
<a-row v-else :gutter="[12, 12]" align="stretch">
|
||||
<a-col
|
||||
v-for="note in notes"
|
||||
:key="note.id"
|
||||
@@ -919,17 +914,27 @@ onBeforeUnmount(() => {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: `${token.marginXS}px`,
|
||||
minHeight: viewMode === 'list' ? undefined : '88px',
|
||||
width: '100%',
|
||||
aspectRatio: viewMode === 'list' ? undefined : '1 / 1',
|
||||
overflow: 'hidden',
|
||||
boxSizing: 'border-box',
|
||||
}"
|
||||
@click="openEditor('default', note)"
|
||||
@keydown.enter="openEditor('default', note)"
|
||||
>
|
||||
<a-flex justify="space-between" align="flex-start" :gap="token.marginXXS">
|
||||
<a-flex
|
||||
justify="space-between"
|
||||
align="flex-start"
|
||||
:gap="token.marginXXS"
|
||||
:style="{ flexShrink: 0 }"
|
||||
>
|
||||
<a-typography-text
|
||||
v-if="note.title"
|
||||
strong
|
||||
:ellipsis="viewMode === 'grid'"
|
||||
:style="{
|
||||
flex: 1,
|
||||
minWidth: 0,
|
||||
fontSize: `${token.fontSizeLG}px`,
|
||||
lineHeight: token.lineHeight,
|
||||
wordBreak: 'break-word',
|
||||
@@ -971,72 +976,89 @@ onBeforeUnmount(() => {
|
||||
</a-space>
|
||||
</a-flex>
|
||||
|
||||
<a-flex
|
||||
v-if="note.checklist?.length"
|
||||
vertical
|
||||
:gap="token.marginXXS"
|
||||
@click.stop
|
||||
>
|
||||
<a-checkbox
|
||||
v-for="item in note.checklist.slice(0, 6)"
|
||||
:key="item.id"
|
||||
:checked="item.done"
|
||||
@change="toggleCardChecklist(note, item.id)"
|
||||
>
|
||||
<a-typography-text
|
||||
:delete="item.done"
|
||||
:type="item.done ? 'secondary' : undefined"
|
||||
>
|
||||
{{ item.text }}
|
||||
</a-typography-text>
|
||||
</a-checkbox>
|
||||
<a-typography-text
|
||||
v-if="note.checklist.length > 6"
|
||||
type="secondary"
|
||||
>
|
||||
+{{ toFaDigits(note.checklist.length - 6) }} مورد دیگر
|
||||
</a-typography-text>
|
||||
</a-flex>
|
||||
<a-typography-paragraph
|
||||
v-else-if="note.body"
|
||||
:ellipsis="{ rows: 6 }"
|
||||
:style="{
|
||||
marginBottom: 0,
|
||||
color: token.colorText,
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordBreak: 'break-word',
|
||||
}"
|
||||
>
|
||||
{{ note.body }}
|
||||
</a-typography-paragraph>
|
||||
|
||||
<img
|
||||
v-if="firstImage(note) && blobCache[blobKey(note.id, firstImage(note)!.id)]"
|
||||
:src="blobCache[blobKey(note.id, firstImage(note)!.id)]"
|
||||
:alt="firstImage(note)!.fileName"
|
||||
:style="{
|
||||
width: '100%',
|
||||
maxHeight: '140px',
|
||||
objectFit: 'cover',
|
||||
borderRadius: `${token.borderRadius}px`,
|
||||
}"
|
||||
/>
|
||||
|
||||
<a-tag v-if="firstPdf(note)" :bordered="false">
|
||||
<FileOutlined />
|
||||
{{ firstPdf(note)!.fileName }}
|
||||
</a-tag>
|
||||
|
||||
<div
|
||||
v-if="firstAudio(note) && blobCache[blobKey(note.id, firstAudio(note)!.id)]"
|
||||
@click.stop
|
||||
:style="{
|
||||
flex: 1,
|
||||
minHeight: 0,
|
||||
overflow: 'hidden',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: `${token.marginXXS}px`,
|
||||
}"
|
||||
>
|
||||
<audio
|
||||
controls
|
||||
preload="metadata"
|
||||
:src="blobCache[blobKey(note.id, firstAudio(note)!.id)]"
|
||||
:style="{ width: '100%', height: '32px' }"
|
||||
<a-flex
|
||||
v-if="note.checklist?.length"
|
||||
vertical
|
||||
:gap="token.marginXXS"
|
||||
:style="{ overflow: 'hidden' }"
|
||||
@click.stop
|
||||
>
|
||||
<a-checkbox
|
||||
v-for="item in note.checklist.slice(0, viewMode === 'list' ? 6 : 4)"
|
||||
:key="item.id"
|
||||
:checked="item.done"
|
||||
@change="toggleCardChecklist(note, item.id)"
|
||||
>
|
||||
<a-typography-text
|
||||
:delete="item.done"
|
||||
:type="item.done ? 'secondary' : undefined"
|
||||
:ellipsis="viewMode === 'grid'"
|
||||
>
|
||||
{{ item.text }}
|
||||
</a-typography-text>
|
||||
</a-checkbox>
|
||||
<a-typography-text
|
||||
v-if="note.checklist.length > (viewMode === 'list' ? 6 : 4)"
|
||||
type="secondary"
|
||||
>
|
||||
+{{
|
||||
toFaDigits(note.checklist.length - (viewMode === 'list' ? 6 : 4))
|
||||
}}
|
||||
مورد دیگر
|
||||
</a-typography-text>
|
||||
</a-flex>
|
||||
<a-typography-paragraph
|
||||
v-else-if="note.body"
|
||||
:ellipsis="{ rows: viewMode === 'list' ? 6 : 4 }"
|
||||
:style="{
|
||||
marginBottom: 0,
|
||||
color: token.colorText,
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordBreak: 'break-word',
|
||||
}"
|
||||
>
|
||||
{{ note.body }}
|
||||
</a-typography-paragraph>
|
||||
|
||||
<img
|
||||
v-if="firstImage(note) && blobCache[blobKey(note.id, firstImage(note)!.id)]"
|
||||
:src="blobCache[blobKey(note.id, firstImage(note)!.id)]"
|
||||
:alt="firstImage(note)!.fileName"
|
||||
:style="{
|
||||
width: '100%',
|
||||
maxHeight: viewMode === 'list' ? '140px' : '72px',
|
||||
objectFit: 'cover',
|
||||
borderRadius: `${token.borderRadius}px`,
|
||||
flexShrink: 0,
|
||||
}"
|
||||
/>
|
||||
|
||||
<a-tag v-if="firstPdf(note)" :bordered="false">
|
||||
<FileOutlined />
|
||||
{{ firstPdf(note)!.fileName }}
|
||||
</a-tag>
|
||||
|
||||
<div
|
||||
v-if="firstAudio(note) && blobCache[blobKey(note.id, firstAudio(note)!.id)]"
|
||||
@click.stop
|
||||
>
|
||||
<audio
|
||||
controls
|
||||
preload="metadata"
|
||||
:src="blobCache[blobKey(note.id, firstAudio(note)!.id)]"
|
||||
:style="{ width: '100%', height: '32px' }"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a-flex
|
||||
@@ -1048,6 +1070,7 @@ onBeforeUnmount(() => {
|
||||
"
|
||||
wrap="wrap"
|
||||
:gap="token.marginXXS"
|
||||
:style="{ flexShrink: 0, overflow: 'hidden', maxHeight: '48px' }"
|
||||
>
|
||||
<a-tag
|
||||
v-for="tag in note.tags ?? []"
|
||||
@@ -1084,7 +1107,8 @@ onBeforeUnmount(() => {
|
||||
|
||||
<a-typography-text
|
||||
type="secondary"
|
||||
:style="{ fontSize: `${token.fontSizeSM}px` }"
|
||||
:ellipsis="true"
|
||||
:style="{ fontSize: `${token.fontSizeSM}px`, flexShrink: 0 }"
|
||||
>
|
||||
{{ formatNoteTime(note.updatedAt) }}
|
||||
<template v-if="note.person"> · {{ note.person.fullName }}</template>
|
||||
@@ -1107,13 +1131,16 @@ onBeforeUnmount(() => {
|
||||
border: `1px dashed ${token.colorBorder}`,
|
||||
padding: `${token.padding}px`,
|
||||
cursor: 'pointer',
|
||||
minHeight: '88px',
|
||||
width: '100%',
|
||||
aspectRatio: viewMode === 'list' ? undefined : '1 / 1',
|
||||
minHeight: viewMode === 'list' ? '88px' : undefined,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: `${token.marginXS}px`,
|
||||
background: token.colorBgContainer,
|
||||
boxSizing: 'border-box',
|
||||
}"
|
||||
@click="openEditor()"
|
||||
@keydown.enter="openEditor()"
|
||||
|
||||
Reference in New Issue
Block a user