添加标签功能
This commit is contained in:
@@ -12,7 +12,8 @@ import {
|
||||
Text,
|
||||
makeStyles,
|
||||
shorthands,
|
||||
tokens
|
||||
tokens,
|
||||
Input
|
||||
} from '@fluentui/react-components';
|
||||
import {
|
||||
NumberSymbol24Regular,
|
||||
@@ -60,15 +61,90 @@ const useStyles = makeStyles({
|
||||
autoSaveText: {
|
||||
color: tokens.colorNeutralForeground4,
|
||||
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 styles = useStyles();
|
||||
const { isDarkMode } = useLayout();
|
||||
const [value, setValue] = useState<string | undefined>("");
|
||||
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(() => {
|
||||
// 模拟自动保存时间显示
|
||||
const now = new Date();
|
||||
@@ -88,6 +164,17 @@ const CreatePost: React.FC = () => {
|
||||
}}
|
||||
commands={getCommands()}
|
||||
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>
|
||||
|
||||
@@ -99,12 +186,28 @@ const CreatePost: React.FC = () => {
|
||||
>
|
||||
提交
|
||||
</Button>
|
||||
<Button
|
||||
appearance="subtle"
|
||||
icon={<NumberSymbol24Regular />}
|
||||
>
|
||||
添加标签
|
||||
</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
|
||||
appearance="subtle"
|
||||
icon={<NumberSymbol24Regular />}
|
||||
onClick={() => setShowTagInput(!showTagInput)}
|
||||
>
|
||||
添加标签
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.footerRight}>
|
||||
|
||||
Reference in New Issue
Block a user