当前位置:网站首页>Build resume editor based on Nocode
Build resume editor based on Nocode
2022-07-26 16:02:00 【WindrunnerMax】
be based on NoCode Build resume editor
be based on NoCode Build resume editor , It's time to join the autumn recruitment , Because the details of various templates are not very satisfactory , So try to make a simple drag and drop resume editor .
describe
For no code NoCode And low code LowCode It's easy to confuse , In my understanding ,NoCode Emphasize your own programming for your own use , It gives users the feeling that it is a more powerful practical software , It is an upper application , in other words NoCode It needs to face very fixed fields to be easy to use ; And for LowCode for , In addition to considering that the process can be built in an interface way , Also consider when you need to expand , Expose the bottom layer , It has stronger customizable functions , That is to say, compared with NoCode You can not limit the use scenario to be so fixed .
For the resume editor , This is a very fixed field , And there is no need to write too much code in use , Use it out of the box , It is implemented as an upper application . For me, it's simply because I want to recruit in autumn , The details of various templates on the website are not very satisfactory , When I took a shower before going to bed at night, I suddenly had an idea to do this , Then one weekend, that is, two days, the liver came out with a simple based on NoCode Resume editor .
Get back to business , For the implementation of resume editor , These aspects need to be considered , Of course, because I made it in two days , It is only a relatively simple realization of some functions :
- You need to support drag page grid layout or free layout .
- Have the ability to edit each component independently .
- Generate
PDFWith the function of preview page . - Generate
JSONFormat configuration data . - Support loading of remote material resume templates .
- Pictures of basic components 、 Implementation of text, etc .
Realization
data storage
For data , Here is a maintenance JSON data , For the entire resume editor, there are relatively strict TS Definition , Therefore, it is necessary to declare component type definitions in advance , It is declared here LocalComponentConfig As the type definition of components , And for the whole generated JSON for , That's what I did LocalComponentConfig[] Nesting of .
The resume displayed in the project is completely adopted JSON In the form of configuration , The rendering of data and view is completely separated , So we can write multiple JSON Form of configuration , To achieve different resume theme templates . If you open the above mentioned Resume DEMO Words , You can see that a resume is preloaded , The content of this resume is completely JSON Configuration , Specifically, you can refer to src/components/debug/example.ts. If the data is in local storage The form of string is stored locally , The key value is cld-storage, If the local local storage Without this key , The initial resume of the example will be loaded , The data storage form is {origin: ${data}, expire: number | number}, adopt JSON.parse You can parse and extract data . With this JSON Data configuration .
// Data definition
// src/types/components-types.ts
export type LocalComponentConfig = {
id: string; // uuid
name: string;
props: Record<string, unknown>;
style: React.CSSProperties;
config: Record<string, unknown>;
children: LocalComponentConfig[];
[key: string]: unknown;
};
Here we actually have two sets of definitions of data structures , Because the purpose is to realize the separation of data and components , But components also need to have positions to define , In addition, because you want the entire editor to be detachable , Specifically, each basic component is registered independently , If its registration part is removed , It will not have any impact on the whole project , Only the view cannot be based on JSON Configuration of successfully rendered , The final effect is empty .
// Component definition
// src/types/components-types.ts
interface ComponentsBase {
name: string;
props?: Record<string, unknown>; // The default passed to the component `props`
style?: React.CSSProperties; // Style configuration information
config?: Record<string, unknown>; // Configuration information
}
export interface LocalComponent extends ComponentsBase {
module: Panel;
}
// Component definition
export const xxx: LocalComponent = {
// ...
}
// Component registration
// src/index.tsx
register(image, richText, blank);
data communication
Because it needs to be maintained JSON The data structure is still relatively complex , Here we use Context + useImmerReducer To achieve state management , Of course use reducer perhaps Mobx It's all possible , This is just a relatively simple solution that I think can be implemented .
// src/store/context.tsx
export const AppProvider: React.FC<{
mode?: ContextProps["mode"] }> = props => {
const {
mode = EDITOR_MODE.EDITOR, children } = props;
const [state, dispatch] = useImmerReducer(reducer, defaultContext.state);
return <AppContext.Provider value={
{
state, mode, dispatch }}>{
children}</AppContext.Provider>;
};
Page grid layout
The implementation of grid layout is relatively simple , And there is no need to realize the function of reference line to do alignment , Just display the grid directly when dragging . In addition, if it will expand a variety of widths in the future PDF If generated , It will not cause too much confusion in the previous canvas layout , Because it is the implementation of grid , It can be processed automatically according to the width , Of course, if it is suitable for mobile terminals, it still needs to make another set Layout Data .
The page layout of this grid is actually implemented as the canvas of the whole page layout ,React Our ecosystem has many libraries in this regard , I use the react-grid-layout This library is used to realize dragging , The specific use can be found in the reference section of this article Github link , The implementation of this library is also quite good , Basically, it can be used out of the box , But there are still many details to deal with . about layout Configuration item , Because we store a JSON Data structure of , So we need to generate through our own defined data structure layout, During the generation process, if cols perhaps rowHeight If the element exceeds the original range due to changes , It needs to be dealt with .
// src/views/main-panel/index.tsx
<ReferenceLine
display={
!isRender && dragging}
rows={
rowHeight}
cols={
cols}
>
<ResponsiveGridLayout
className="pedestal-responsive-grid-layout"
style={
{
minHeight }}
layout={
layouts}
autoSize
draggableHandle=".pedestal-drag-dot"
margin={
[0, 0]}
onLayoutChange={
layoutChange}
cols={
cols}
rowHeight={
rowHeight}
measureBeforeMount
onDragStart={
dragStart}
onDragStop={
dragStop}
onResizeStart={
resizeStart}
onResizeStop={
resizeStop}
allowOverlap={
allowOverlap}
compactType={
null} // Turn off vertical compaction
preventCollision // Turn off rearranging
useCSSTransforms={
false} // stay `ObserveResize` Animation will appear
>
</ResponsiveGridLayout>
</ReferenceLine>
about <ReferenceLine/> Components , Through here CSS Draw the grid points of the grid layout , So as to realize the function of reference line .
// src/views/main-panel/components/reference-line/index.tsx
<div
className={
classes(
"pedestal-main-reference-line",
props.className,
props.display && "enable"
)}
style={
{
backgroundSize: `${
cellWidth}px ${
props.rows}px`,
backgroundPositionX: cellWidth / 2,
backgroundPositionY: -props.rows / 2,
...props.style,
// background-image: radial-gradient(circle, #999 0.8px, transparent 0);
}}
ref={
referenceLineRef}
>
{
props.children}
</div>
Component independent editing
With the basic canvas components , We need to implement various basic components , Then the basic components need to implement independent editing functions , The independent editing function needs three parts : The first is the change of data , Because editing finally needs to be reflected in the data , That is, the one we want to maintain JSON data , Because we have a data communication scheme , So here we just need to define reducer Write it to the corresponding component configuration props Or other fields .
// src/store/reducer.ts
witch (action.type) {
// ...
case actions.UPDATE_ONE: {
const {
id: uuid, key, data, merge = true } = action.payload;
updateOneInNodeTree(state.cld.children, uuid, key, data, merge);
break;
}
// ...
}
// src/utils/node-tree-utils.ts
/** * @param tree LocalComponentConfig.children * @param uuid string * @param key string * @param data unknown * @returns boolean */
export const updateOneInNodeTree = (
tree: LocalComponentConfig["children"],
uuid: string,
key: string,
data: unknown,
merge: boolean
): boolean => {
const node = findOneInNodeTree(tree, uuid);
if (!node) return false;
let preKeyData: unknown = node;
const deepKey = key.split(".");
const lastKey = deepKey[deepKey.length - 1];
for (let i = 0, n = deepKey.length - 1; i < n; ++i) {
if (isObject(preKeyData)) preKeyData = preKeyData[deepKey[i]];
else return false;
}
if (isObject(preKeyData)) {
const target = preKeyData[lastKey];
if (isObject(target) && isObject(data)) {
if (merge) preKeyData[lastKey] = {
...target, ...data };
else preKeyData[lastKey] = {
...data };
} else {
preKeyData[lastKey] = data;
}
return true;
}
return false;
};
Next is the implementation of the toolbar , For toolbars , We need... For the selected element name Make a judgment , After loading the toolbar , For user operations , Only according to the currently selected id Apply to... Through data communication JSON In the data , Finally, its changes will be applied in the view .
// src/views/main-panel/components/tool-bar/index.tsx
const deleteBaseSection = () => {
// ...
};
const copySection = () => {
// ...
};
// ...
<Trigger
popupVisible={
selectedId === config.id}
popup={
() => Menu}
position="top"
trigger="contextMenu"
>
{
props.children}
</Trigger>
For the edit panel , Similar to toolbar , By loading the form , After the data of the form changes, you can pass reducer Applied to the JSON Data is enough , Because the editor implemented here is really simple , So I also loaded a CSS Editor , Through cooperation CSS You can achieve more style effects , Of course, by expanding the editing panel of various components, customization can be minimized CSS Of .
// src/views/editor-panel/index.tsx
const renderEditor = () => {
const [selectNodeName] = state.selectedNode.name.split(".");
if (!selectNodeName) return null;
const componentInstance = getComponentInstanceSync(selectNodeName);
if (!componentInstance || !componentInstance.main) return null;
const Component = componentInstance.editor;
return (
<>
<Component state={
state} dispatch={
dispatch}></Component>
<CustomCSS state={
state} dispatch={
dispatch}></CustomCSS>
</>
);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
const EditorPanel = useMemo(() => renderEditor(), [state.selectedNode.id]);
export PDF
export PDF The function is with the help of the browser , By printing Ctrl + P To implement the export PDF The effect of , Attention should be paid to when exporting :
- The resume is based on
A4The size of the paper is fixed in width and height , Expanding the editing area may cause more than one page of resume . - export
PDFYou need to set the paper size toA4、 Margin is none 、 Select the background graphics option Before you can export a full resume .
Basic components
Picture components
Picture components , Use the above pictures to show , Because there is no back end , So the picture can only be base64 Stored in JSON In the structure .
// src/components/image/index.ts
export const image: LocalComponent = {
name: "image" as const,
props: {
src: "./favicon.ico",
},
config: {
layout: {
x: 0,
y: 0,
w: 20,
h: 20,
isDraggable: true,
isResizable: true,
minW: 2,
minH: 2,
},
},
module: {
control: ImageControl,
main: ImageMain,
editor: ImageEditor,
},
};
Rich text component
Rich text component , For editing text , Here I happen to have a component implementation of rich text editor , You can refer to Github | Editor DEMO.
// src/components/text/index.ts
export const richText: LocalComponent = {
name: "rich-text" as const,
props: {
},
config: {
layout: {
x: 0,
y: 0,
w: 20,
h: 10,
isDraggable: true,
isResizable: true,
minW: 4,
minH: 2,
},
observeResize: true,
},
module: {
control: RichTextControl,
main: RichText,
editor: RichTextEditor,
},
};
Blank component
Blank component , It can be used as a placeholder , It can also be done through coordination CSS Achieve background effect .
// src/components/blank/index.ts
export const blank: LocalComponent = {
name: "blank" as const,
props: {
},
config: {
layout: {
x: 0,
y: 0,
w: 10,
h: 3,
isDraggable: true,
isResizable: true,
minW: 1,
minH: 1,
},
},
module: {
control: BlankControl,
main: BlankMain,
editor: BlankEditor,
},
};
A daily topic
https://github.com/WindrunnerMax/EveryDay
Reference resources
http://javakk.com/2127.html
http://blog.wuweiwang.cn/?p=27961
https://github.com/ctrlplusb/react-sizeme
https://juejin.cn/post/6961309077162950692
https://github.com/WindrunnerMax/DocEditor
https://github.com/react-grid-layout/react-grid-layout
边栏推荐
- Google Earth Engine——MERRA-2 M2T1NXSLV:1980-至今全球压力、温度、风等数据集
- js 对数组操作的 API 总结
- 辨析 Ruby 中的 Method 与 Proc
- 中金财富炒股安全吗 手续费最便宜的证券公司
- The solution to the display disorder of several events files in the tensorboard
- 线程和进程
- Tool skill learning (I): pre skills -makfile, make,.Mk
- Zynq PS + PL heterogeneous multicore Case Development Manual of Ti C6000 tms320c6678 DSP + zynq-7045 (1)
- 朋友圈如何测试(思维导图)
- Summary of QT plug-in development -- add plug-in menu in the main interface
猜你喜欢

认识JS基础与浏览器引擎

Delta controller rmc200

御神楽的学习记录之SoC FPGA的第一个工程-Hello World

德国emg电动执行器EB800-60II

We were tossed all night by a Kong performance bug

This article explains in detail the discovery and processing of bigkey and hotkey in redis

Development and implementation of campus epidemic prevention and control management system based on SSM

白话详解决策树模型之使用信息熵构建决策树

Zynq PS + PL heterogeneous multicore Case Development Manual of Ti C6000 tms320c6678 DSP + zynq-7045 (1)

TI C6000 TMS320C6678 DSP+ Zynq-7045的PS + PL异构多核案例开发手册(2)
随机推荐
邻接矩阵的COO格式
gcc/g++与动静库以及gdb
The solution to the display disorder of several events files in the tensorboard
《From SICP to Lisp》视频回播
A coal mine in Yangquan, Shanxi Province, suffered a safety accident that killed one person and was ordered to stop production for rectification
八叉树建立地图并实现路径规划导航
We were tossed all night by a Kong performance bug
辨析 Ruby 中的 Method 与 Proc
This article explains in detail the discovery and processing of bigkey and hotkey in redis
German EMG e-anji thruster ed301/6 HS
TI C6000 TMS320C6678 DSP+ Zynq-7045的PS + PL异构多核案例开发手册(2)
parker电磁阀D1VW020DNYPZ5
一文搞懂│XSS攻击、SQL注入、CSRF攻击、DDOS攻击、DNS劫持
Coo format of adjacency matrix
ROS problems and Solutions - relying on package installation and unable to correct errors
Yushenle's learning record: the first project of SOC FPGA -hello world
国元期货网上开户安全吗?开户办理流程是怎样的?
Google Earth engine - merra-2 m2t1nxlv: 1980 present global pressure, temperature, wind and other data sets
A comprehensive review of image enhancement technology in deep learning
Quanzhi a40i industrial core board, 100% domestic 4-core arm cortex-a7, supports "dual screen abnormal display" [display interface capability, preferred scheme for industrial HMI]