Skip to content

Commit a48e824

Browse files
committed
add code color form
1 parent 06b2233 commit a48e824

File tree

7 files changed

+321
-2
lines changed

7 files changed

+321
-2
lines changed
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import React, {Ref, useEffect, useImperativeHandle} from 'react';
2+
import * as monaco from 'monaco-editor';
3+
4+
export interface CodeEditorAction {
5+
// 重置编辑器的值
6+
resetValue: (value: string) => void;
7+
// 获取选中的值
8+
getSelectedValue: () => string;
9+
// 获取编辑器的值
10+
getValue: () => string;
11+
// 获取编辑器实例
12+
getEditor:() => monaco.editor.IStandaloneCodeEditor | null;
13+
}
14+
15+
16+
interface CodeEditorProps {
17+
language?: string,
18+
value?: string,
19+
onChange?: (value: string) => void,
20+
onSelectedRun?: (value: string) => void;
21+
style?: React.CSSProperties;
22+
readonly?: boolean;
23+
theme?: string;
24+
fontSize?: number;
25+
actionRef?: Ref<CodeEditorAction>;
26+
}
27+
28+
29+
const CodeEditor: React.FC<CodeEditorProps> = (props) => {
30+
const language = props.language || 'javascript';
31+
const theme = props.theme || "vs-dark";
32+
const fontSize = props.fontSize || 14;
33+
const [editorId, setEditorId] = React.useState<string>("");
34+
35+
const container = React.useRef(null);
36+
37+
const style = props.style || {
38+
height: "80px",
39+
}
40+
41+
const codeEditorAction = {
42+
getEditor: ()=>{
43+
const editors = monaco.editor.getEditors();
44+
if(editors.length > 0) {
45+
return editors.find(editor => editor.getId() === editorId);
46+
}
47+
return null;
48+
},
49+
50+
resetValue: (value: string) => {
51+
const editor = codeEditorAction.getEditor();
52+
if(editor) {
53+
const position = editor.getPosition();
54+
editor.setValue(value);
55+
if (position) {
56+
editor.setPosition({
57+
lineNumber: position.lineNumber,
58+
column: position.column
59+
});
60+
}
61+
}
62+
},
63+
getSelectedValue: () => {
64+
const editor = codeEditorAction.getEditor();
65+
if(editor) {
66+
const selection = editor.getSelection();
67+
//@ts-ignore
68+
return editor.getModel().getValueInRange(selection);
69+
}
70+
return "";
71+
},
72+
getValue: () => {
73+
const editor = codeEditorAction.getEditor();
74+
if(editor) {
75+
return editor.getValue();
76+
}
77+
return "";
78+
}
79+
} as CodeEditorAction;
80+
81+
useImperativeHandle(props.actionRef, () => (codeEditorAction), [props.actionRef]);
82+
83+
useEffect(() => {
84+
if(props.value) {
85+
codeEditorAction.resetValue(props.value);
86+
}
87+
}, [props.value]);
88+
89+
useEffect(() => {
90+
const model = monaco.editor.createModel(props.value || "", language);
91+
const editor = monaco.editor.create(
92+
//@ts-ignore
93+
container?.current,
94+
{
95+
automaticLayout: true,
96+
model: model,
97+
fontSize: fontSize,
98+
theme: theme,
99+
readOnly: props.readonly,
100+
},
101+
);
102+
103+
setEditorId(editor.getId());
104+
105+
const subscription = editor.onDidChangeModelContent((event) => {
106+
props.onChange && props.onChange(editor.getValue());
107+
});
108+
109+
let runSelectedCodeActionDispose: monaco.IDisposable | null = null;
110+
if (props.onSelectedRun) {
111+
const runSelectedCodeAction = {
112+
id: "run-code",
113+
label: "Run Selected Code",
114+
contextMenuGroupId: "navigation",
115+
keybindings: [
116+
monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyR,
117+
monaco.KeyMod.WinCtrl | monaco.KeyCode.KeyR,
118+
],
119+
run: () => {
120+
const selection = editor.getSelection();
121+
//@ts-ignore
122+
const selectedText = editor.getModel().getValueInRange(selection);
123+
props.onSelectedRun && props.onSelectedRun(selectedText);
124+
},
125+
}
126+
runSelectedCodeActionDispose = monaco.editor.addEditorAction(runSelectedCodeAction);
127+
}
128+
129+
return () => {
130+
editor.dispose();
131+
subscription.dispose();
132+
model.dispose();
133+
134+
if (runSelectedCodeActionDispose !== null) {
135+
runSelectedCodeActionDispose.dispose();
136+
}
137+
};
138+
}, [props.readonly]);
139+
140+
return (
141+
<div ref={container} style={style}></div>
142+
);
143+
};
144+
145+
export default CodeEditor;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import React from "react";
2+
import {FormItemProps} from "@/components/form/types";
3+
import {Form} from "antd";
4+
import formFieldInit from "@/components/form/common";
5+
import "./form.scss";
6+
import CodeEditor from "@/components/CodeEditor";
7+
8+
9+
const FormCode: React.FC<FormItemProps> = (props) => {
10+
11+
const {formAction} = formFieldInit(props);
12+
13+
return (
14+
<Form.Item
15+
name={props.name}
16+
label={props.label}
17+
hidden={props.hidden}
18+
help={props.help}
19+
required={props.required}
20+
>
21+
<CodeEditor
22+
readonly={props.disabled}
23+
value={props.value}
24+
onChange={(value) => {
25+
formAction?.setFieldValue(props.name, value);
26+
props.onChange && props.onChange(value, formAction);
27+
}}
28+
theme={props.codeTheme}
29+
language={props.codeLanguage}
30+
fontSize={props.codeFontSize}
31+
style={props.codeStyle}
32+
actionRef={props.codeActionRef}
33+
onSelectedRun={props.onCodeSelectedRun}
34+
/>
35+
</Form.Item>
36+
)
37+
}
38+
39+
export default FormCode;
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import React from "react";
2+
import {FormItemProps} from "@/components/form/types";
3+
import {ColorPicker, Form} from "antd";
4+
import formFieldInit from "@/components/form/common";
5+
import "./form.scss";
6+
import type {AggregationColor} from "antd/es/color-picker/color";
7+
8+
const formToValue = (value: AggregationColor) => {
9+
if (value) {
10+
return value.toHexString();
11+
}
12+
return value;
13+
}
14+
15+
const FormColor: React.FC<FormItemProps> = (props) => {
16+
17+
const {formAction} = formFieldInit(props);
18+
19+
return (
20+
<Form.Item
21+
name={props.name}
22+
label={props.label}
23+
hidden={props.hidden}
24+
help={props.help}
25+
required={props.required}
26+
>
27+
<ColorPicker
28+
disabled={props.disabled}
29+
value={props.value}
30+
onChange={(value) => {
31+
const currentValue = formToValue(value);
32+
formAction?.setFieldValue(props.name, currentValue);
33+
props.onChange && props.onChange(currentValue, formAction);
34+
}}
35+
/>
36+
</Form.Item>
37+
)
38+
}
39+
40+
export default FormColor;

admin-pro-ui/src/components/form/factory.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import FormCascader from "@/components/form/cascader";
1515
import FormSelect from "@/components/form/select";
1616
import FormSelector from "@/components/form/selector";
1717
import FormUploader from "@/components/form/uploder";
18+
import FormColor from "@/components/form/color";
19+
import FormCode from "@/components/form/code";
1820

1921

2022
class FormFactory {
@@ -156,6 +158,24 @@ class FormFactory {
156158
/>
157159
)
158160
}
161+
162+
if (type === 'color') {
163+
return (
164+
<FormColor
165+
{...props}
166+
key={props.name}
167+
/>
168+
)
169+
}
170+
171+
if (type === 'code') {
172+
return (
173+
<FormCode
174+
{...props}
175+
key={props.name}
176+
/>
177+
)
178+
}
159179
}
160180

161181
}

admin-pro-ui/src/components/form/types.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {FormValidateContent} from "@/components/form/validate";
22
import {FormAction} from "@/components/form";
33
import React from "react";
44
import {NamePath} from "rc-field-form/es/interface";
5+
import {CodeEditorAction} from "@/components/CodeEditor";
56

67
// Form表单选项类型
78
export interface FormOption {
@@ -15,7 +16,8 @@ export interface FormOption {
1516
type FormFieldType =
1617
"input" | "cascader" | "select" | "password" | "date" |
1718
"radio" | "textarea" | "checkbox" | "uploader" | "switch" |
18-
"stepper" | "slider" | "rate" | "selector" | "captcha";
19+
"stepper" | "slider" | "rate" | "selector" | "captcha" |
20+
"code" | "color";
1921

2022
// FormField
2123
export interface FormField {
@@ -132,6 +134,19 @@ export interface FormItemProps {
132134
selectorMultiple?: boolean,
133135
// selector组件每行展示数量
134136
selectorColumn?: number,
137+
// code组件的语言,默认为javascript
138+
codeLanguage?: string,
139+
// code组件的主题,默认为vs-dark
140+
codeTheme?: string,
141+
// code组件的字体大小,默认为14
142+
codeFontSize?: number,
143+
// code组件的操作Action
144+
codeActionRef?: React.RefObject<CodeEditorAction>,
145+
// code组件的样式
146+
codeStyle?: React.CSSProperties,
147+
// code组件的选中运行事件
148+
onCodeSelectedRun?: (value: string) => void,
149+
135150
// Captcha组件切换验证码事件
136151
onCaptchaChange?: (value: string) => void;
137152
// Captcha组件刷新验证码事件

admin-pro-ui/src/pages/welcome/index.tsx

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import FormDate from "@/components/form/date";
1818
import FormCascader from "@/components/form/cascader";
1919
import FormSelect from "@/components/form/select";
2020
import FormUploader from "@/components/form/uploder";
21+
import FormColor from "@/components/form/color";
22+
import FormCode from "@/components/form/code";
2123

2224

2325
const FooterButtons: React.FC<{ formAction: React.RefObject<FormAction> }> = ({formAction}) => {
@@ -36,7 +38,9 @@ const FooterButtons: React.FC<{ formAction: React.RefObject<FormAction> }> = ({f
3638
date: '2021-08-01',
3739
cascader: '1,1-1,1-1-1',
3840
select: '1,2',
39-
avatar: 'c84fb304c180f61bb7db40efef7f85b7'
41+
avatar: 'c84fb304c180f61bb7db40efef7f85b7',
42+
color: '#000000',
43+
ideCode:'console.log("hello world")'
4044
}
4145
}
4246

@@ -350,6 +354,22 @@ const WelcomePage = () => {
350354
label: '头像',
351355
}
352356
},
357+
{
358+
type: 'color',
359+
props: {
360+
required: true,
361+
name: ['user', 'color'],
362+
label: '颜色',
363+
}
364+
},
365+
{
366+
type: 'code',
367+
props: {
368+
required: true,
369+
name: ['user', 'ideCode'],
370+
label: '代码',
371+
}
372+
},
353373
] as FormField[];
354374

355375

@@ -531,6 +551,18 @@ const WelcomePage = () => {
531551
name={["user", "avatar"]}
532552
label={"头像"}
533553
/>
554+
555+
<FormColor
556+
required={true}
557+
name={["user", "color"]}
558+
label={"颜色"}
559+
/>
560+
561+
<FormCode
562+
required={true}
563+
name={["user", "ideCode"]}
564+
label={"代码"}
565+
/>
534566
</Form>
535567
</Col>
536568

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import {FormValidateContent} from "@/components/form/validate";
2+
3+
class ValidateUtils {
4+
5+
/**
6+
* 非空校验
7+
* @param content 校验对象
8+
* @param message 错误信息
9+
*/
10+
static validateNotEmpty = async (content: FormValidateContent, message?: string) => {
11+
const field = content.getFieldProps();
12+
const errorMessage = message ? message : field?.props.label + '不能为空' || '不能为空';
13+
const value = content.value;
14+
if (value) {
15+
if (Array.isArray(value)) {
16+
if (value.length === 0) {
17+
return [errorMessage];
18+
}
19+
} else {
20+
return [];
21+
}
22+
} else {
23+
return [errorMessage];
24+
}
25+
}
26+
}
27+
28+
export default ValidateUtils;

0 commit comments

Comments
 (0)