添加标签功能
This commit is contained in:
@@ -12,7 +12,8 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
makeStyles,
|
makeStyles,
|
||||||
shorthands,
|
shorthands,
|
||||||
tokens
|
tokens,
|
||||||
|
Input
|
||||||
} from '@fluentui/react-components';
|
} from '@fluentui/react-components';
|
||||||
import {
|
import {
|
||||||
NumberSymbol24Regular,
|
NumberSymbol24Regular,
|
||||||
@@ -60,15 +61,90 @@ const useStyles = makeStyles({
|
|||||||
autoSaveText: {
|
autoSaveText: {
|
||||||
color: tokens.colorNeutralForeground4,
|
color: tokens.colorNeutralForeground4,
|
||||||
fontSize: tokens.fontSizeBase200,
|
fontSize: tokens.fontSizeBase200,
|
||||||
|
},
|
||||||
|
tagInputContainer: {
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: '100%',
|
||||||
|
left: '0',
|
||||||
|
marginBottom: '8px',
|
||||||
|
display: 'flex',
|
||||||
|
gap: '8px',
|
||||||
|
backgroundColor: tokens.colorNeutralBackground1,
|
||||||
|
padding: '8px',
|
||||||
|
borderRadius: tokens.borderRadiusMedium,
|
||||||
|
boxShadow: tokens.shadow16,
|
||||||
|
zIndex: 10,
|
||||||
|
},
|
||||||
|
tagButtonWrapper: {
|
||||||
|
position: 'relative',
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 自定义 remark 插件,用于高亮 #tag
|
||||||
|
const remarkTagPlugin = () => {
|
||||||
|
return (tree: any) => {
|
||||||
|
const transformNode = (node: any, inLink = false) => {
|
||||||
|
if (node.type === 'link') inLink = true;
|
||||||
|
|
||||||
|
if (node.children) {
|
||||||
|
node.children = node.children.flatMap((child: any) => {
|
||||||
|
if (child.type === 'text' && !inLink) {
|
||||||
|
// Split by tag pattern (# followed by non-whitespace)
|
||||||
|
const parts = child.value.split(/(#\S+)/g);
|
||||||
|
return parts.map((part: string) => {
|
||||||
|
if (part.match(/^#\S+$/)) {
|
||||||
|
return {
|
||||||
|
type: 'link',
|
||||||
|
url: 'tag:' + part,
|
||||||
|
children: [{ type: 'text', value: part }]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (part === "") return [];
|
||||||
|
return { type: 'text', value: part };
|
||||||
|
}).flat();
|
||||||
|
}
|
||||||
|
return transformNode(child, inLink);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
};
|
||||||
|
transformNode(tree);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const CreatePost: React.FC = () => {
|
const CreatePost: React.FC = () => {
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
const { isDarkMode } = useLayout();
|
const { isDarkMode } = useLayout();
|
||||||
const [value, setValue] = useState<string | undefined>("");
|
const [value, setValue] = useState<string | undefined>("");
|
||||||
const [lastSaved, setLastSaved] = useState<string>("");
|
const [lastSaved, setLastSaved] = useState<string>("");
|
||||||
|
|
||||||
|
// 标签输入相关状态
|
||||||
|
const [showTagInput, setShowTagInput] = useState(false);
|
||||||
|
const [tagInputValue, setTagInputValue] = useState("");
|
||||||
|
|
||||||
|
const handleTagSubmit = () => {
|
||||||
|
if (!tagInputValue.trim()) {
|
||||||
|
setShowTagInput(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分割并处理标签
|
||||||
|
const tags = tagInputValue.trim().split(/\s+/).map(t => {
|
||||||
|
// 去除开头的 #(如果有多个也只去除第一个,或者确保开头有且仅有一个 #)
|
||||||
|
const cleanText = t.replace(/^#+/, '');
|
||||||
|
return '#' + cleanText;
|
||||||
|
}).join(' ');
|
||||||
|
|
||||||
|
// 添加到编辑器内容
|
||||||
|
setValue(prev => {
|
||||||
|
const prefix = prev ? prev + ' ' : '';
|
||||||
|
return prefix + tags;
|
||||||
|
});
|
||||||
|
|
||||||
|
setTagInputValue("");
|
||||||
|
setShowTagInput(false);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 模拟自动保存时间显示
|
// 模拟自动保存时间显示
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
@@ -88,6 +164,17 @@ const CreatePost: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
commands={getCommands()}
|
commands={getCommands()}
|
||||||
extraCommands={getExtraCommands()}
|
extraCommands={getExtraCommands()}
|
||||||
|
previewOptions={{
|
||||||
|
remarkPlugins: [remarkTagPlugin],
|
||||||
|
components: {
|
||||||
|
a: ({ node, ...props }) => {
|
||||||
|
if (props.href && props.href.startsWith('tag:')) {
|
||||||
|
return <span style={{ color: tokens.colorBrandForeground1 }}>{props.children}</span>;
|
||||||
|
}
|
||||||
|
return <a {...props} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -99,13 +186,29 @@ const CreatePost: React.FC = () => {
|
|||||||
>
|
>
|
||||||
提交
|
提交
|
||||||
</Button>
|
</Button>
|
||||||
|
<div className={styles.tagButtonWrapper}>
|
||||||
|
{showTagInput && (
|
||||||
|
<div className={styles.tagInputContainer}>
|
||||||
|
<Input
|
||||||
|
value={tagInputValue}
|
||||||
|
onChange={(e, data) => setTagInputValue(data.value)}
|
||||||
|
placeholder="输入标签..."
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === 'Enter') handleTagSubmit();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button appearance="primary" size="small" onClick={handleTagSubmit}>OK</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<Button
|
<Button
|
||||||
appearance="subtle"
|
appearance="subtle"
|
||||||
icon={<NumberSymbol24Regular />}
|
icon={<NumberSymbol24Regular />}
|
||||||
|
onClick={() => setShowTagInput(!showTagInput)}
|
||||||
>
|
>
|
||||||
添加标签
|
添加标签
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className={styles.footerRight}>
|
<div className={styles.footerRight}>
|
||||||
<Text className={styles.autoSaveText}>
|
<Text className={styles.autoSaveText}>
|
||||||
|
|||||||
Reference in New Issue
Block a user