diff --git a/src/assets/iconfont/md-editor/iconfont.css b/src/assets/iconfont/md-editor/iconfont.css new file mode 100644 index 0000000..4f1455d --- /dev/null +++ b/src/assets/iconfont/md-editor/iconfont.css @@ -0,0 +1,96 @@ +@font-face { + font-family: 'iconfont'; /* Project id 4145942 */ + /* Color fonts */ + src: + url('iconfont.woff2?t=1689582142318') format('woff2'), + url('iconfont.woff?t=1689582142318') format('woff'), + url('iconfont.ttf?t=1689582142318') format('truetype'); +} + +.iconfont { + font-family: 'iconfont' !important; + font-size: 16px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-dian:before { + content: '\e640'; +} + +.icon-xuanzeyixuan:before { + content: '\e63c'; +} + +.icon-danger:before { + content: '\e614'; +} + +.icon-Success:before { + content: '\e605'; +} + +.icon-warning:before { + content: '\e600'; +} + +.icon-info:before { + content: '\e601'; +} + +.icon-dingwei:before { + content: '\e650'; +} + +.icon-wenjian:before { + content: '\e652'; +} + +.icon-shuju:before { + content: '\e653'; +} + +.icon-biaoqian:before { + content: '\e64a'; +} + +.icon-biaoqing:before { + content: '\e64b'; +} + +.icon-jinggao:before { + content: '\e64c'; +} + +.icon-dianzan:before { + content: '\e64d'; +} + +.icon-xiangqing:before { + content: '\e64e'; +} + +.icon-tishi:before { + content: '\e64f'; +} + +.icon-bangzhu:before { + content: '\e651'; +} + +.icon-wenjianjia:before { + content: '\e652'; +} + +.icon-touxiang:before { + content: '\e626'; +} + +.icon-jiedian:before { + content: '\e649'; +} + +.icon-cengji:before { + content: '\e67f'; +} diff --git a/src/assets/iconfont/md-editor/iconfont.ttf b/src/assets/iconfont/md-editor/iconfont.ttf new file mode 100644 index 0000000..ac2580d Binary files /dev/null and b/src/assets/iconfont/md-editor/iconfont.ttf differ diff --git a/src/assets/iconfont/md-editor/iconfont.woff b/src/assets/iconfont/md-editor/iconfont.woff new file mode 100644 index 0000000..fc6f659 Binary files /dev/null and b/src/assets/iconfont/md-editor/iconfont.woff differ diff --git a/src/assets/iconfont/md-editor/iconfont.woff2 b/src/assets/iconfont/md-editor/iconfont.woff2 new file mode 100644 index 0000000..78f2554 Binary files /dev/null and b/src/assets/iconfont/md-editor/iconfont.woff2 differ diff --git a/src/components/Dialog/Dialog.module.less b/src/components/Dialog/Dialog.module.less index 967fd0e..cf4ef66 100644 --- a/src/components/Dialog/Dialog.module.less +++ b/src/components/Dialog/Dialog.module.less @@ -31,7 +31,7 @@ left: 0; display: flex; width: 100%; - height: 30px; + height: 40px; } .header .bar { diff --git a/src/components/Dialog/Dialog.tsx b/src/components/Dialog/Dialog.tsx index 45342ee..483ce88 100644 --- a/src/components/Dialog/Dialog.tsx +++ b/src/components/Dialog/Dialog.tsx @@ -198,7 +198,7 @@ const Dialog = forwardRef>(function Dialog return ( <> -
+
{item.type === FileType.DIRECTORY ? ( - + <> + + + ) : null} {item.name} diff --git a/src/filehandle/components/styles/FileSideMenu.module.less b/src/filehandle/components/styles/FileSideMenu.module.less index b31384d..8dc6762 100644 --- a/src/filehandle/components/styles/FileSideMenu.module.less +++ b/src/filehandle/components/styles/FileSideMenu.module.less @@ -32,11 +32,11 @@ user-select: none; span { - margin-left: 8px; + margin-left: 18px; } .directoryIcon + span { - margin-left: 0; + margin-left: 3px; } > .list { @@ -63,6 +63,7 @@ } .list .item .row { + padding-block: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; diff --git a/src/filehandle/md_editor/component/MDContent.tsx b/src/filehandle/md_editor/component/MDContent.tsx index 8e33dcd..931f7b9 100644 --- a/src/filehandle/md_editor/component/MDContent.tsx +++ b/src/filehandle/md_editor/component/MDContent.tsx @@ -1,3 +1,4 @@ +import type { HTMLAttributes } from 'react'; import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react'; import { addGlobalListener, confirm, error } from '@/utils'; @@ -11,7 +12,7 @@ import type { IMDEditorExpose } from './MDEditor'; import MDEditor from './MDEditor'; import styles from './styles/MDContent.module.less'; -interface IMDContentProps { +interface IMDContentProps extends HTMLAttributes { handle: DH | FH; update(): void; } @@ -22,7 +23,7 @@ export interface IMDContentExpose { export const MDContent = forwardRef( function MDContent(props, ref) { - const { handle, update } = props; + const { handle, update, ...rest } = props; const [changed, setChanged] = useState(false); const [currentHandle, setCurrentHandle] = useState(null); @@ -127,7 +128,7 @@ export const MDContent = forwardRef( }; return ( -
+
{isDirectoryHandle(handle) ? ( ( ) : null}
- +
+ {currentHandle?.name} + {changed ? - 已编辑 : null} +
+ +
+ +
); diff --git a/src/filehandle/md_editor/component/MDHandle.tsx b/src/filehandle/md_editor/component/MDHandle.tsx index b478847..41aad27 100644 --- a/src/filehandle/md_editor/component/MDHandle.tsx +++ b/src/filehandle/md_editor/component/MDHandle.tsx @@ -177,7 +177,12 @@ const MDHandle: React.FC = (props) => { onMinimize={handleMinimize} onClose={onClose} > - + ); }; diff --git a/src/filehandle/md_editor/component/styles/MDContent.module.less b/src/filehandle/md_editor/component/styles/MDContent.module.less index de4ac16..d07a0c9 100644 --- a/src/filehandle/md_editor/component/styles/MDContent.module.less +++ b/src/filehandle/md_editor/component/styles/MDContent.module.less @@ -7,8 +7,35 @@ } .mdEditor { + position: relative; + padding: 0 50px 30px; flex: 1; height: 100%; - padding: 30px; overflow-y: auto; + background-color: rgb(199, 229, 252); +} + +.editStatus { + position: sticky; + top: 0; + height: 45px; + background-color: rgb(199, 229, 252); + text-align: center; + line-height: 45px; + font-weight: 700; + z-index: var(--z-normal); +} + +.changed { + color: var(--color-info); +} + +.editorWrapper { + margin: 4px auto; + max-width: 1140px; + padding: 30px; + background-color: #fff; + min-height: 100%; + border-radius: var(--border-radius-base); + box-shadow: 0 0 1px 4px #495060; } diff --git a/src/filehandle/md_editor/component/styles/MDEditor.module.less b/src/filehandle/md_editor/component/styles/MDEditor.module.less index f30dfd2..6f72899 100644 --- a/src/filehandle/md_editor/component/styles/MDEditor.module.less +++ b/src/filehandle/md_editor/component/styles/MDEditor.module.less @@ -1,3 +1,5 @@ +@import url('@/assets/iconfont/md-editor/iconfont.css'); + .editorContainer { :global(.milkdown) { height: 100%; @@ -47,17 +49,21 @@ h5, h6 { margin-bottom: 1em; + width: max-content; font-weight: 600; } h1 { + width: 100%; font-size: 38px; line-height: 1.21; + text-align: center; } h2 { font-size: 30px; line-height: 1.266; + border-bottom: 3px solid #fe5f58; } h3 { @@ -102,6 +108,25 @@ } } + h1::before { + margin-right: 3px; + font-family: 'iconfont'; + content: '\e626'; + vertical-align: middle; + } + + h2::before { + margin-right: 3px; + content: '#'; + color: #fe5f58; + } + + h3::before { + margin-right: 8px; + font-family: 'iconfont'; + content: '\e67f'; + } + strong { font-weight: 600; } @@ -188,7 +213,7 @@ } ul { - list-style: initial; + list-style: none; } // li[data-checked='false']::before { @@ -199,6 +224,26 @@ // content: 'done'; // } + li { + position: relative; + } + + li::before { + position: absolute; + top: 8px; + left: -20px; + display: inline-block; + content: ''; + width: 8px; + height: 8px; + text-align: center; + vertical-align: middle; + color: #000; + background-color: #fff; + border: 2px solid #fe5f58; + border-radius: 50%; + } + pre { position: relative; z-index: var(--z-ignore); @@ -238,9 +283,12 @@ } img { + margin: 0 auto; display: block; - max-width: 598px; - text-align: center; + max-width: 720px; + height: auto; + box-shadow: 0 0 5px 0px rgba(0, 0, 0, 0.18); + object-fit: cover; } a { diff --git a/src/tools/json2typescript/Index.tsx b/src/tools/json2typescript/Index.tsx index d60b81b..8c1540b 100644 --- a/src/tools/json2typescript/Index.tsx +++ b/src/tools/json2typescript/Index.tsx @@ -1,44 +1,35 @@ -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import style from './Index.module.less'; -import { createType, generate } from './JSON2TypeScript'; +import { json2ts } from './JSON2TypeScript'; import Editor from '@monaco-editor/react'; -// const transform = (obj: any) => { -// const type = typeof obj; - -// switch (type) { -// case 'string': -// case 'number': -// case 'bigint': -// case 'boolean': -// case 'object': -// } -// } - -const json2ts = (json: string) => { - try { - const obj = JSON.parse(json); - - console.log(createType(obj)); - - return generate(createType(obj)); - } catch (err) { - console.log(err); - - return void 0; - } -}; - const JSON2TypeScript: React.FC = () => { const [typescript, setTypeScript] = useState(''); + useEffect(() => { + handleChange(`{ + "userId": 1, + "id": 1, + "title": "delectus aut autem", + "completed": false +}`); + }, []); + const handleChange = (e: string | undefined) => { - const ts = json2ts(e || ''); + try { + const obj = JSON.parse(e || ''); + + const ts = json2ts(obj); + + // console.log(ts); - if (typeof ts === 'string') { - setTypeScript(ts); + if (typeof ts === 'string') { + setTypeScript(ts); + } + } catch (err) { + // console.log(err); } }; @@ -49,6 +40,12 @@ const JSON2TypeScript: React.FC = () => { diff --git a/src/tools/json2typescript/JSON2TypeScript.ts b/src/tools/json2typescript/JSON2TypeScript.ts index 91ec9a2..16e227d 100644 --- a/src/tools/json2typescript/JSON2TypeScript.ts +++ b/src/tools/json2typescript/JSON2TypeScript.ts @@ -1,202 +1,155 @@ -import { isArray, isBoolean, isNull, isNumber, isObject, isString, upperFirst } from 'lodash-es'; +import { isArray, isNull, isObject } from 'lodash-es'; -type type = 'string' | 'number' | 'boolean' | 'null' | Record | Type[]; +type BaseType = 'string' | 'number' | 'boolean' | 'null' | 'any' | JointType; + +type JointType = Record; type Type = { - type: type; - require: boolean; + type: BaseType; + required: boolean; + count?: number; }; const createStringType = (): Type => { - return { type: 'string', require: true }; + return { type: 'string', required: true }; }; const createNumberType = (): Type => { - return { type: 'number', require: true }; + return { type: 'number', required: true }; }; const createBooleanType = (): Type => { - return { type: 'boolean', require: true }; + return { type: 'boolean', required: true }; }; const createNullType = (): Type => { - return { type: 'null', require: true }; + return { type: 'null', required: true }; }; -const createObjectType = (data: Record): Type => { - const keys = Object.keys(data); - - const companion: Record = {}; - - for (let i = 0; i < keys.length; i++) { - const key = keys[i]; - - companion[key] = createType(data[key]); +/** + * + * @param obj 需要检索类型的对象 + * @param sibling 可选的同级类型 + * @returns 返回对象类型 + */ +function createObjectType(obj: Record, sibling?: BaseType): Type { + // 如果有同级类型,且同级类型不是对象,则返回 any + if (sibling && (typeof sibling === 'string' || isArray(sibling))) { + return { type: 'any', required: true }; } - return { type: companion, require: true }; -}; - -const createArrayType = (data: any[]): Type => { - const companion: Type[] = []; - - for (let i = 0; i < data.length; i++) { - const curr = data[i]; + const names = Object.getOwnPropertyNames(obj); + const oldNames = sibling ? Object.getOwnPropertyNames(sibling) : []; - companion.push(createType(curr)); - } + const typeObj: JointType = sibling || {}; - return { type: companion, require: true }; -}; + const merge = (t: Type, name: string) => { + // 没有同级类型,不做特殊处理 + if (!sibling) { + return (typeObj[name] = t); + } -export function createType(data: any) { - let type!: Type; + // 存在同级类型,但没有相同属性,标记为可选 + const oldType = typeObj[name]; + if (!oldType) { + t.required = false; + return (typeObj[name] = t); + } - switch (true) { - case isString(data): - type = createStringType(); - break; - case isNumber(data): - type = createNumberType(); - break; - case isNull(data): - type = createNullType(); - break; - case isBoolean(data): - type = createBooleanType(); - break; - case isArray(data): - type = createArrayType(data); - break; - case isObject(data): - type = createObjectType(data); - break; + // 类型不一致,标记为 any + if (oldType.type !== t.type) { + oldType.type = 'any'; + } + }; + + for (let i = 0; i < names.length; i++) { + const name = names[i]; + + const value = obj[name]; + const type = typeof value; + + switch (type) { + case 'string': + merge(createStringType(), name); + break; + case 'number': + merge(createNumberType(), name); + break; + case 'boolean': + merge(createBooleanType(), name); + break; + default: + if (isNull(value)) { + merge(createNullType(), name); + } else if (isArray(value)) { + merge(createArrayType(value), name); + } else if (isObject(value)) { + // 如果存在同级元素,将它向下传递 + merge(createObjectType(value, typeObj[name]?.type), name); + } + } } - return type; -} - -export function generate(tree: Type) { - const typeStr: string[] = []; - - const names = new Map(); - - const generateString = () => 'string;\n'; - const generateNumber = () => 'number;\n'; - const generateBoolean = () => 'boolean;\n'; - const generateNull = () => 'null;\n'; - - function getName(key: string) { - let typeName = key; + // obj 中如果没有 sibling 的属性,需要标记为可选 + oldNames + .filter((o) => !names.includes(o)) + .forEach((o) => { + typeObj[o].required = false; + }); - if (!Number.isNaN(window.parseInt(typeName))) { - const name = 'N' + typeName; - - let count = names.get(name); - - if (typeof count === 'number') { - typeName = name.padStart(name.length + ++count, 'N'); - - names.set(name, count); - } else { - typeName = name; - - names.set(name, 0); - } - } else { - let count = names.get(typeName); - - if (typeof count === 'number') { - count++; + return { type: typeObj, required: true }; +} - typeName += count; +function createArrayType(array: any[]): Type { + let type!: Type; - names.set(typeName, count); - } else { - names.set(typeName, 0); - } + for (let i = 0; i < array.length; i++) { + const ele = array[i]; + + const eleType = typeof ele; + + switch (eleType) { + case 'string': + createStringType(); + break; + case 'number': + createNumberType(); + break; + case 'boolean': + createBooleanType(); + break; + default: + if (isNull(ele)) { + createNullType(); + } else if (isArray(ele)) { + createArrayType(ele); + } else if (isObject(ele)) { + createObjectType(ele); + } } - - typeName = upperFirst(typeName); - - return typeName; } - function generateObject(data: Record, name?: string) { - let type = '{\n'; - - const keys = Object.keys(data); - - for (let i = 0; i < keys.length; i++) { - const key = keys[i] as keyof typeof data; - const prop = data[key]; - - const typeName = getName(key); - - switch (prop.type) { - case 'string': - type += ` ${key}: ${generateString()}`; - break; - case 'number': - type += ` ${key}: ${generateNumber()}`; - break; - case 'null': - type += ` ${key}: ${generateNull()}`; - break; - case 'boolean': - type += ` ${key}: ${generateBoolean()}`; - break; - default: - if (isArray(prop.type)) { - generateArray(prop.type, typeName); - - type += ` ${key}: ${typeName};\n`; - } else if (isObject(prop.type)) { - generateObject(prop.type, typeName); - - type += ` ${key}: ${typeName};\n`; - } - } - } - - if (!name) { - return typeStr.push(`export interface Root ${type}}`); - } + return type; +} - typeStr.push(`export interface ${name} ${type}}`); +export function transform(json: Record | any[]) { + if (isArray(json)) { + return createArrayType(json); + } else if (isObject(json)) { + return createObjectType(json); } - function generateArray(data: Type[], name?: string) { - let type = ''; - - if (data.length === 0) { - type += 'any[];\n'; - } else if (data.every((e) => e.type === 'string')) { - type += 'string[];\n'; - } else if (data.every((e) => e.type === 'number')) { - type += 'string[];\n'; - } else if (data.every((e) => e.type === 'boolean')) { - type += 'boolean[];\n'; - } else if (data.every((e) => e.type === 'null')) { - type += 'null[];\n'; - } else { - // for (let i = 0; i < data.length; i++) { - // const element = data[i]; - // } - } + throw new Error('Invail type of the json.'); +} - if (!name) { - return `export type Root = ` + type; - } +export const getTag = (count: number) => { + return '[]'.repeat(count); +}; - typeStr.push(`type ${name} = ${type}[];\n`); - } +export function json2ts(json: Record | any[]) { + const tree = transform(json); - if (isArray(tree.type)) { - generateArray(tree.type); - } else if (isObject(tree.type)) { - generateObject(tree.type); - } + console.log(tree); - return typeStr.reverse().join('\n\n'); + return ''; } diff --git a/src/variables.less b/src/variables.less index ccb5c70..b7a9777 100644 --- a/src/variables.less +++ b/src/variables.less @@ -20,6 +20,7 @@ 'Microsoft YaHei', simsun, sans-serif; --font-family-code: consolas, monaco, 'Andale Mono', 'Ubuntu Mono', monospace; --border-radius-base: 5px; + --border-color-primary: #d9d9d9ff; } :root[mode='dark'] { @@ -27,6 +28,7 @@ --color-primary-light: #dccec6; --color-info: #a9b0b7; --color-info-light: #cdcdcd; + --border-color-primary: #424242ff; --text-color-primary: #d9d9d9; --background-color: #202020; --mask-color: rgb(0 0 0 / 40%);