diff --git a/packages/client/src/schema-component/antd/index.ts b/packages/client/src/schema-component/antd/index.ts
index 2baa7d275f..991032dd6a 100644
--- a/packages/client/src/schema-component/antd/index.ts
+++ b/packages/client/src/schema-component/antd/index.ts
@@ -8,5 +8,6 @@ export * from './form';
export * from './form-item';
export * from './grid';
export * from './input';
+export * from './password';
export * from './radio';
export * from './menu';
\ No newline at end of file
diff --git a/packages/client/src/schema-component/antd/password/Password.tsx b/packages/client/src/schema-component/antd/password/Password.tsx
new file mode 100644
index 0000000000..31257a7e6b
--- /dev/null
+++ b/packages/client/src/schema-component/antd/password/Password.tsx
@@ -0,0 +1,69 @@
+import { connect, mapReadPretty } from '@formily/react';
+import { isValid } from '@formily/shared';
+import { Input } from 'antd';
+import { PasswordProps } from 'antd/lib/input';
+import React from 'react';
+import { PasswordStrength } from './PasswordStrength';
+
+export interface IPasswordProps extends PasswordProps {
+ checkStrength: boolean;
+}
+
+export const Password = connect(
+ (props: IPasswordProps) => {
+ const { value, className, checkStrength, ...others } = props;
+ const blockStyle: React.CSSProperties = {
+ position: 'absolute',
+ zIndex: 1,
+ height: 8,
+ top: 0,
+ background: '#fff',
+ width: 1,
+ transform: 'translate(-50%, 0)',
+ };
+ return (
+
+
+ {checkStrength && (
+
+ {(score) => {
+ return (
+
+ );
+ }}
+
+ )}
+
+ );
+ },
+ mapReadPretty((props) => {
+ if (!props.value) {
+ return
;
+ }
+ return ********
;
+ }),
+);
+
+export default Password;
diff --git a/packages/client/src/schema-component/antd/password/PasswordStrength.tsx b/packages/client/src/schema-component/antd/password/PasswordStrength.tsx
new file mode 100644
index 0000000000..a910ab0883
--- /dev/null
+++ b/packages/client/src/schema-component/antd/password/PasswordStrength.tsx
@@ -0,0 +1,162 @@
+import { isFn } from '@formily/shared';
+import React, { Fragment } from 'react';
+
+type ReactRenderPropsChildren = React.ReactNode | ((props: T) => React.ReactElement);
+
+interface IPasswordStrengthProps {
+ value?: any;
+ children?: ReactRenderPropsChildren;
+}
+
+const isNum = function (c) {
+ return c >= 48 && c <= 57;
+};
+const isLower = function (c) {
+ return c >= 97 && c <= 122;
+};
+const isUpper = function (c) {
+ return c >= 65 && c <= 90;
+};
+const isSymbol = function (c) {
+ return !(isLower(c) || isUpper(c) || isNum(c));
+};
+const isLetter = function (c) {
+ return isLower(c) || isUpper(c);
+};
+
+const getStrength = (val) => {
+ if (!val) return 0;
+ let num = 0;
+ let lower = 0;
+ let upper = 0;
+ let symbol = 0;
+ let MNS = 0;
+ let rep = 0;
+ let repC = 0;
+ let consecutive = 0;
+ let sequential = 0;
+ const len = () => num + lower + upper + symbol;
+ const callMe = () => {
+ let re = num > 0 ? 1 : 0;
+ re += lower > 0 ? 1 : 0;
+ re += upper > 0 ? 1 : 0;
+ re += symbol > 0 ? 1 : 0;
+ if (re > 2 && len() >= 8) {
+ return re + 1;
+ } else {
+ return 0;
+ }
+ };
+ for (let i = 0; i < val.length; i++) {
+ const c = val.charCodeAt(i);
+ if (isNum(c)) {
+ num++;
+ if (i !== 0 && i !== val.length - 1) {
+ MNS++;
+ }
+ if (i > 0 && isNum(val.charCodeAt(i - 1))) {
+ consecutive++;
+ }
+ } else if (isLower(c)) {
+ lower++;
+ if (i > 0 && isLower(val.charCodeAt(i - 1))) {
+ consecutive++;
+ }
+ } else if (isUpper(c)) {
+ upper++;
+ if (i > 0 && isUpper(val.charCodeAt(i - 1))) {
+ consecutive++;
+ }
+ } else {
+ symbol++;
+ if (i !== 0 && i !== val.length - 1) {
+ MNS++;
+ }
+ }
+ let exists = false;
+ for (let j = 0; j < val.length; j++) {
+ if (val[i] === val[j] && i !== j) {
+ exists = true;
+ repC += Math.abs(val.length / (j - i));
+ }
+ }
+ if (exists) {
+ rep++;
+ const unique = val.length - rep;
+ repC = unique ? Math.ceil(repC / unique) : Math.ceil(repC);
+ }
+ if (i > 1) {
+ const last1 = val.charCodeAt(i - 1);
+ const last2 = val.charCodeAt(i - 2);
+ if (isLetter(c)) {
+ if (isLetter(last1) && isLetter(last2)) {
+ const v = val.toLowerCase();
+ const vi = v.charCodeAt(i);
+ const vi1 = v.charCodeAt(i - 1);
+ const vi2 = v.charCodeAt(i - 2);
+ if (vi - vi1 === vi1 - vi2 && Math.abs(vi - vi1) === 1) {
+ sequential++;
+ }
+ }
+ } else if (isNum(c)) {
+ if (isNum(last1) && isNum(last2)) {
+ if (c - last1 === last1 - last2 && Math.abs(c - last1) === 1) {
+ sequential++;
+ }
+ }
+ } else {
+ if (isSymbol(last1) && isSymbol(last2)) {
+ if (c - last1 === last1 - last2 && Math.abs(c - last1) === 1) {
+ sequential++;
+ }
+ }
+ }
+ }
+ }
+ let sum = 0;
+ const length = len();
+ sum += 4 * length;
+ if (lower > 0) {
+ sum += 2 * (length - lower);
+ }
+ if (upper > 0) {
+ sum += 2 * (length - upper);
+ }
+ if (num !== length) {
+ sum += 4 * num;
+ }
+ sum += 6 * symbol;
+ sum += 2 * MNS;
+ sum += 2 * callMe();
+ if (length === lower + upper) {
+ sum -= length;
+ }
+ if (length === num) {
+ sum -= num;
+ }
+ sum -= repC;
+ sum -= 2 * consecutive;
+ sum -= 3 * sequential;
+ sum = sum < 0 ? 0 : sum;
+ sum = sum > 100 ? 100 : sum;
+
+ if (sum >= 80) {
+ return 100;
+ } else if (sum >= 60) {
+ return 80;
+ } else if (sum >= 40) {
+ return 60;
+ } else if (sum >= 20) {
+ return 40;
+ } else {
+ return 20;
+ }
+};
+
+export const PasswordStrength: React.FC = (props) => {
+ if (isFn(props.children)) {
+ return props.children(getStrength(String(props.value || '')));
+ } else {
+ return {props.children};
+ }
+};
diff --git a/packages/client/src/schema-component/antd/password/demos/demo1.tsx b/packages/client/src/schema-component/antd/password/demos/demo1.tsx
new file mode 100644
index 0000000000..c7b6f0cb21
--- /dev/null
+++ b/packages/client/src/schema-component/antd/password/demos/demo1.tsx
@@ -0,0 +1,41 @@
+/**
+ * title: Password
+ */
+import { FormItem } from '@formily/antd';
+import { Password, SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
+import React from 'react';
+
+const schema = {
+ type: 'object',
+ properties: {
+ input: {
+ type: 'boolean',
+ title: `Editable`,
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Password',
+ 'x-reactions': {
+ target: 'read',
+ fulfill: {
+ state: {
+ value: '{{$self.value}}',
+ },
+ },
+ },
+ },
+ read: {
+ type: 'boolean',
+ title: `Read pretty`,
+ 'x-read-pretty': true,
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Password',
+ },
+ },
+};
+
+export default () => {
+ return (
+
+
+
+ );
+};
diff --git a/packages/client/src/schema-component/antd/password/demos/demo2.tsx b/packages/client/src/schema-component/antd/password/demos/demo2.tsx
new file mode 100644
index 0000000000..b6c3710c94
--- /dev/null
+++ b/packages/client/src/schema-component/antd/password/demos/demo2.tsx
@@ -0,0 +1,45 @@
+/**
+ * title: Check strength
+ */
+ import { FormItem } from '@formily/antd';
+ import { Password, SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
+ import React from 'react';
+
+ const schema = {
+ type: 'object',
+ properties: {
+ input: {
+ type: 'boolean',
+ title: `Editable`,
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Password',
+ 'x-component-props': {
+ checkStrength: true,
+ },
+ 'x-reactions': {
+ target: 'read',
+ fulfill: {
+ state: {
+ value: '{{$self.value}}',
+ },
+ },
+ },
+ },
+ read: {
+ type: 'boolean',
+ title: `Read pretty`,
+ 'x-read-pretty': true,
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Password',
+ },
+ },
+ };
+
+ export default () => {
+ return (
+
+
+
+ );
+ };
+
\ No newline at end of file
diff --git a/packages/client/src/schema-component/antd/password/index.md b/packages/client/src/schema-component/antd/password/index.md
index a25b4f20dd..f2b0a9e433 100644
--- a/packages/client/src/schema-component/antd/password/index.md
+++ b/packages/client/src/schema-component/antd/password/index.md
@@ -6,3 +6,19 @@ group:
---
# Password
+
+## Examples
+
+### Password
+
+
+
+### Check strength
+
+
+
+## API
+
+基于 antd 的 Password,新增的属性:
+
+- `checkStrength` 检测密码强度
diff --git a/packages/client/src/schema-component/antd/password/index.ts b/packages/client/src/schema-component/antd/password/index.ts
new file mode 100644
index 0000000000..74946a7c07
--- /dev/null
+++ b/packages/client/src/schema-component/antd/password/index.ts
@@ -0,0 +1 @@
+export * from './Password';