正在显示
79 个修改的文件
包含
3040 行增加
和
1 行删除
.eslintrc.json
0 → 100644
| 1 | +{ | |
| 2 | + "env": { | |
| 3 | + "browser": true, | |
| 4 | + "es2021": true | |
| 5 | + }, | |
| 6 | + "extends": [ | |
| 7 | + "eslint:recommended", | |
| 8 | + "plugin:react/recommended", | |
| 9 | + "plugin:@typescript-eslint/recommended" | |
| 10 | + ], | |
| 11 | + "overrides": [], | |
| 12 | + "parser": "@typescript-eslint/parser", | |
| 13 | + "parserOptions": { | |
| 14 | + "ecmaVersion": "latest" | |
| 15 | + }, | |
| 16 | + "plugins": ["react", "@typescript-eslint"], | |
| 17 | + "rules": { | |
| 18 | + "react/react-in-jsx-scope": "off", // 无需在组件内引用React | |
| 19 | + "@typescript-eslint/explicit-module-boundary-types": "off", //关闭函数返回类型 | |
| 20 | + "arrow-spacing": ["error", { "before": true, "after": true }], | |
| 21 | + "semi": ["error", "always"], | |
| 22 | + "comma-spacing": ["error", { "before": false, "after": true }], | |
| 23 | + "complexity": 0, | |
| 24 | + "getter-return": ["error", { "allowImplicit": true }], | |
| 25 | + "no-unused-vars": ["error", { "vars": "local" }, { "args": "after-used" }], | |
| 26 | + "no-debugger": "error", | |
| 27 | + "no-empty": ["error", { "allowEmptyCatch": true }], | |
| 28 | + "array-callback-return": "error", | |
| 29 | + "eqeqeq": ["error", "always"], | |
| 30 | + "no-eval": ["error", { "allowIndirect": true }], | |
| 31 | + "indent": ["error", 2], | |
| 32 | + "linebreak-style": "off", | |
| 33 | + "@typescript-eslint/no-empty-function": "off", // 空函数非禁止使用 | |
| 34 | + "react/display-name": "off", //防止在React组件定义中丢失displayName | |
| 35 | + "camelcase": ["error", { "properties": "always" }], // 强制驼峰格式 | |
| 36 | + "require-await": "error", // 禁止使用不带 await 表达式的 async 函数 | |
| 37 | + "no-label-var": "error", // 不允许标签与变量同名 | |
| 38 | + "array-bracket-spacing": ["error", "never"], // 强制数组方括号中使用一致的空格 | |
| 39 | + "key-spacing": ["error", { "afterColon": true }], // 要求在对象字面量的冒号和值之间存在至少有一个空格 | |
| 40 | + "lines-around-comment": ["error", { "beforeBlockComment": true }], // 要求在块级注释之前有一空行 | |
| 41 | + "prefer-destructuring": ["error", { "object": true, "array": true }] //优先使用数组和对象解构 | |
| 42 | + } | |
| 43 | +} | ... | ... |
.gitignore
0 → 100644
.lintstagedrc.json
0 → 100644
.prettierrc
0 → 100644
.stylelintrc.js
0 → 100644
| 1 | +module.exports = { | |
| 2 | + extends: [ | |
| 3 | + 'stylelint-config-standard', | |
| 4 | + 'stylelint-prettier/recommended', | |
| 5 | + 'stylelint-config-rational-order', | |
| 6 | + ], | |
| 7 | + // 使用 stylelint 来 lint css in js? https://github.com/emotion-js/emotion/discussions/2694 | |
| 8 | + rules: { | |
| 9 | + 'function-name-case': ['lower'], | |
| 10 | + 'function-no-unknown': [ | |
| 11 | + true, | |
| 12 | + { | |
| 13 | + ignoreFunctions: [ | |
| 14 | + 'fade', | |
| 15 | + 'fadeout', | |
| 16 | + 'tint', | |
| 17 | + 'darken', | |
| 18 | + 'ceil', | |
| 19 | + 'fadein', | |
| 20 | + 'floor', | |
| 21 | + 'unit', | |
| 22 | + 'shade', | |
| 23 | + 'lighten', | |
| 24 | + 'percentage', | |
| 25 | + '-', | |
| 26 | + ], | |
| 27 | + }, | |
| 28 | + ], | |
| 29 | + 'import-notation': null, | |
| 30 | + 'no-descending-specificity': null, | |
| 31 | + 'no-invalid-position-at-import-rule': null, | |
| 32 | + 'declaration-empty-line-before': null, | |
| 33 | + 'keyframes-name-pattern': null, | |
| 34 | + 'custom-property-pattern': null, | |
| 35 | + 'number-max-precision': 8, | |
| 36 | + 'alpha-value-notation': 'number', | |
| 37 | + 'color-function-notation': 'legacy', | |
| 38 | + 'selector-class-pattern': null, | |
| 39 | + 'selector-id-pattern': null, | |
| 40 | + 'selector-not-notation': null, | |
| 41 | + }, | |
| 42 | +}; | ... | ... |
index.html
0 → 100644
| 1 | +<!DOCTYPE html> | |
| 2 | +<html lang="en" class="dark"> | |
| 3 | + <head> | |
| 4 | + <meta charset="UTF-8" /> | |
| 5 | + <link rel="icon" href="/favicon.ico" /> | |
| 6 | + <link rel="stylesheet" href="/image/partten/iconfont.css" /> | |
| 7 | + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| 8 | + <title></title> | |
| 9 | + </head> | |
| 10 | + <body> | |
| 11 | + <div id="app"></div> | |
| 12 | + <script type="module" src="/index.tsx"></script> | |
| 13 | + </body> | |
| 14 | +</html> | ... | ... |
index.tsx
0 → 100644
| ... | ... | @@ -5,7 +5,7 @@ |
| 5 | 5 | "main": "index.js", |
| 6 | 6 | "scripts": { |
| 7 | 7 | "dev": "vite", |
| 8 | - "build": "vite build", | |
| 8 | + "build": "vite build && tsc --declaration --emitDeclarationOnly --noEmitOnError false", | |
| 9 | 9 | "prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'", |
| 10 | 10 | "lint-staged": "lint-staged", |
| 11 | 11 | "test": "echo \"Error: no test specified\" && exit 1" | ... | ... |
packages/hr/App.tsx
0 → 100644
| 1 | +import { useMemo } from 'react'; | |
| 2 | +import AddTags from '@/hr/add_tags'; | |
| 3 | +import { FormProvider } from '@formily/react'; | |
| 4 | +import { createForm } from '@formily/core'; | |
| 5 | +import SearchWrap from '@/hr/search/search'; | |
| 6 | +import InputField from '@/hr/antd_input'; | |
| 7 | + | |
| 8 | +const App = () => { | |
| 9 | + const form = useMemo(() => createForm(), []); | |
| 10 | + return ( | |
| 11 | + <FormProvider form={form}> | |
| 12 | + <SearchWrap leftSectionComponents={[<InputField.text name="123" />]} /> | |
| 13 | + </FormProvider> | |
| 14 | + ); | |
| 15 | +}; | |
| 16 | + | |
| 17 | +export default App; | ... | ... |
packages/hr/add_tags/index.less
0 → 100644
| 1 | +.Ok { | |
| 2 | + font-size: 14px; | |
| 3 | + font-weight: 400; | |
| 4 | + color: #1677ff; | |
| 5 | + margin-left: 21px; | |
| 6 | + cursor: pointer; | |
| 7 | +} | |
| 8 | + | |
| 9 | +.cancle { | |
| 10 | + font-size: 14px; | |
| 11 | + font-weight: 400; | |
| 12 | + color: #606266; | |
| 13 | + cursor: pointer; | |
| 14 | + margin-left: 10px; | |
| 15 | +} | |
| 16 | + | |
| 17 | +.render-tag { | |
| 18 | + background-color: #f2f5fc; | |
| 19 | + border-radius: 4px; | |
| 20 | + padding: 5px 18px; | |
| 21 | + font-size: 14px; | |
| 22 | + font-weight: 300; | |
| 23 | + // color: #333333; | |
| 24 | + border-style: solid !important; | |
| 25 | + line-height: 22px; | |
| 26 | + border-color: #f2f5fc; | |
| 27 | + | |
| 28 | + &-checkable:not(&-checkable-checked):hover { | |
| 29 | + background-color: rgba(22, 119, 255, 0.5); | |
| 30 | + color: #fff; | |
| 31 | + } | |
| 32 | + | |
| 33 | + &-checkable-checked { | |
| 34 | + background-color: #1677ff; | |
| 35 | + color: #fff; | |
| 36 | + font-weight: 400; | |
| 37 | + | |
| 38 | + &:hover { | |
| 39 | + background-color: #4096ff !important; | |
| 40 | + } | |
| 41 | + } | |
| 42 | +} | |
| 43 | + | |
| 44 | +.render-tags { | |
| 45 | + background-color: #f2f5fc; | |
| 46 | + border-radius: 4px; | |
| 47 | + padding: 5px 18px; | |
| 48 | + font-size: 14px; | |
| 49 | + font-weight: 300; | |
| 50 | + border-style: solid !important; | |
| 51 | + line-height: 22px; | |
| 52 | + border-color: #f2f5fc; | |
| 53 | + | |
| 54 | + &-checkable:not(&-checkable-checked):hover { | |
| 55 | + background-color: rgba(22, 119, 255, 0.5); | |
| 56 | + color: #fff; | |
| 57 | + } | |
| 58 | + | |
| 59 | + &-checkable-checked { | |
| 60 | + background-color: #1677ff; | |
| 61 | + color: #fff; | |
| 62 | + font-weight: 400; | |
| 63 | + | |
| 64 | + &:hover { | |
| 65 | + background-color: #4096ff !important; | |
| 66 | + } | |
| 67 | + } | |
| 68 | +} | |
| 69 | + | |
| 70 | +.ovner-input { | |
| 71 | + width: 220px; | |
| 72 | + height: 34px; | |
| 73 | + line-height: 34px; | |
| 74 | + vertical-align: top; | |
| 75 | + margin-right: 5px; | |
| 76 | +} | ... | ... |
packages/hr/add_tags/index.tsx
0 → 100644
| 1 | +import React, { useEffect, useRef, useState } from 'react'; | |
| 2 | +import { PlusOutlined } from '@ant-design/icons'; | |
| 3 | +import { InputRef } from 'antd'; | |
| 4 | +import { Space, Input, Tag, Tooltip } from 'antd'; | |
| 5 | +import './index.less'; | |
| 6 | +import { Field } from '@formily/react'; | |
| 7 | +import { FieldProps } from '../typings'; | |
| 8 | +import { FormItem } from '@formily/antd-v5'; | |
| 9 | + | |
| 10 | +interface Props { | |
| 11 | + initValues?: string[]; | |
| 12 | + tag_text?: string; | |
| 13 | + showValues?: boolean; | |
| 14 | + onChange?: (value: string[]) => void; | |
| 15 | + value?: string[]; | |
| 16 | + maxLength?: number; | |
| 17 | +} | |
| 18 | + | |
| 19 | +export const AddTag = (props: Props) => { | |
| 20 | + const { onChange, tag_text, showValues = true, value = [], maxLength = 0 } = props; | |
| 21 | + | |
| 22 | + const [inputVisible, setInputVisible] = useState(false); | |
| 23 | + const [inputValue, setInputValue] = useState(''); | |
| 24 | + const inputRef = useRef<InputRef>(null); | |
| 25 | + | |
| 26 | + useEffect(() => { | |
| 27 | + if (inputVisible) { | |
| 28 | + inputRef.current?.focus(); | |
| 29 | + } | |
| 30 | + }, [inputVisible]); | |
| 31 | + | |
| 32 | + const handleClose = (removedTag: string) => { | |
| 33 | + const newTags = value?.filter((tag) => tag !== removedTag) || []; | |
| 34 | + | |
| 35 | + onChange && onChange([...newTags]); | |
| 36 | + }; | |
| 37 | + | |
| 38 | + const showInput = () => { | |
| 39 | + setInputVisible(true); | |
| 40 | + }; | |
| 41 | + | |
| 42 | + const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { | |
| 43 | + setInputValue(e.target.value); | |
| 44 | + }; | |
| 45 | + | |
| 46 | + const handleInputConfirm = () => { | |
| 47 | + if (inputValue && value?.indexOf(inputValue) === -1) { | |
| 48 | + onChange && onChange([...value, inputValue]); | |
| 49 | + } | |
| 50 | + setInputVisible(false); | |
| 51 | + setInputValue(''); | |
| 52 | + }; | |
| 53 | + | |
| 54 | + return ( | |
| 55 | + <Space size={10} wrap> | |
| 56 | + {showValues && ( | |
| 57 | + <Space size={10} wrap> | |
| 58 | + {value && | |
| 59 | + value.map((tag, index) => { | |
| 60 | + const isLongTag = tag.length > 20; | |
| 61 | + const tagElem = ( | |
| 62 | + <Tag | |
| 63 | + key={index} | |
| 64 | + closable | |
| 65 | + style={{ | |
| 66 | + userSelect: 'none', | |
| 67 | + }} | |
| 68 | + className="render-tag" | |
| 69 | + onClose={() => handleClose(tag)} | |
| 70 | + > | |
| 71 | + <span>{isLongTag ? `${tag.slice(0, 20)}...` : tag}</span> | |
| 72 | + </Tag> | |
| 73 | + ); | |
| 74 | + return isLongTag ? ( | |
| 75 | + <Tooltip title={tag} key={tag}> | |
| 76 | + {tagElem} | |
| 77 | + </Tooltip> | |
| 78 | + ) : ( | |
| 79 | + tagElem | |
| 80 | + ); | |
| 81 | + })} | |
| 82 | + </Space> | |
| 83 | + )} | |
| 84 | + {inputVisible ? ( | |
| 85 | + <> | |
| 86 | + <Input | |
| 87 | + ref={inputRef} | |
| 88 | + type="text" | |
| 89 | + size="small" | |
| 90 | + className="ovner-input" | |
| 91 | + value={inputValue} | |
| 92 | + onChange={handleInputChange} | |
| 93 | + onPressEnter={handleInputConfirm} | |
| 94 | + /> | |
| 95 | + <span onClick={handleInputConfirm} className="Ok"> | |
| 96 | + 确定 | |
| 97 | + </span> | |
| 98 | + <span | |
| 99 | + onClick={() => { | |
| 100 | + setInputVisible(false); | |
| 101 | + }} | |
| 102 | + className="cancle" | |
| 103 | + > | |
| 104 | + 取消 | |
| 105 | + </span> | |
| 106 | + </> | |
| 107 | + ) : ( | |
| 108 | + (!maxLength || !(maxLength <= value.length)) && ( | |
| 109 | + <Tag onClick={showInput} className="render-tags" color="#1677FF"> | |
| 110 | + <PlusOutlined /> {tag_text || '添加标签'} | |
| 111 | + </Tag> | |
| 112 | + ) | |
| 113 | + )} | |
| 114 | + </Space> | |
| 115 | + ); | |
| 116 | +}; | |
| 117 | + | |
| 118 | +interface AddTagsProps extends FieldProps { | |
| 119 | + name: string; | |
| 120 | + title?: string; | |
| 121 | + initValues?: string[]; | |
| 122 | +} | |
| 123 | +const AddTags: React.FC<AddTagsProps> = (props) => { | |
| 124 | + const { name, title, decoratorProps, componentProps, validator } = props; | |
| 125 | + | |
| 126 | + return ( | |
| 127 | + <Field | |
| 128 | + name={name} | |
| 129 | + title={title} | |
| 130 | + decorator={[FormItem, { ...decoratorProps }]} | |
| 131 | + component={[ | |
| 132 | + AddTag, | |
| 133 | + { | |
| 134 | + ...componentProps, | |
| 135 | + }, | |
| 136 | + ]} | |
| 137 | + validator={validator} | |
| 138 | + /> | |
| 139 | + ); | |
| 140 | +}; | |
| 141 | + | |
| 142 | +export default AddTags; | ... | ... |
| 1 | +#root .global_cascader { | |
| 2 | + .ant-formily-item { | |
| 3 | + &-label { | |
| 4 | + font-weight: 500; | |
| 5 | + color: #1d2129; | |
| 6 | + } | |
| 7 | + | |
| 8 | + &-control { | |
| 9 | + &-content { | |
| 10 | + &-component { | |
| 11 | + .ant-select { | |
| 12 | + &-selector { | |
| 13 | + height: 36px; | |
| 14 | + border-color: #ebeef5; | |
| 15 | + border-radius: 2px; | |
| 16 | + padding-left: 13px; | |
| 17 | + } | |
| 18 | + | |
| 19 | + &-arrow { | |
| 20 | + color: #adaeb1; | |
| 21 | + } | |
| 22 | + | |
| 23 | + &-selection-placeholder { | |
| 24 | + color: #ccc; | |
| 25 | + font-weight: 400; | |
| 26 | + line-height: 36px; | |
| 27 | + } | |
| 28 | + } | |
| 29 | + } | |
| 30 | + } | |
| 31 | + } | |
| 32 | + } | |
| 33 | +} | ... | ... |
| 1 | +import { Field } from '@formily/react'; | |
| 2 | +import { FormItem, Cascader } from '@formily/antd-v5'; | |
| 3 | +import cx from 'classnames'; | |
| 4 | +import './index.less'; | |
| 5 | +import { FieldProps } from '../../typings'; | |
| 6 | + | |
| 7 | +/** | |
| 8 | + * An interface that defines an option in the Cascader component. | |
| 9 | + */ | |
| 10 | +interface Option { | |
| 11 | + value: string | number; | |
| 12 | + label: string; | |
| 13 | + disabled?: boolean; | |
| 14 | + children?: Option[]; | |
| 15 | +} | |
| 16 | + | |
| 17 | +/** | |
| 18 | + * An interface that defines the properties of the CascaderInput component. | |
| 19 | + */ | |
| 20 | +export interface CascaderInputProps extends FieldProps { | |
| 21 | + options: Option[]; | |
| 22 | +} | |
| 23 | + | |
| 24 | +/** | |
| 25 | + * The CascaderInput component. | |
| 26 | + * | |
| 27 | + * This is a wrapper around the Cascader component that makes it easier to use in a Formily form. | |
| 28 | + */ | |
| 29 | +const CascaderInput: React.FC<CascaderInputProps> = (props) => { | |
| 30 | + const { name, title, validator = [], decoratorProps, componentProps, options } = props; | |
| 31 | + | |
| 32 | + return ( | |
| 33 | + <div className={cx('global_cascader')}> | |
| 34 | + <Field | |
| 35 | + {...props} | |
| 36 | + name={name} | |
| 37 | + title={title} | |
| 38 | + dataSource={options} | |
| 39 | + decorator={[FormItem, { ...decoratorProps }]} | |
| 40 | + component={[ | |
| 41 | + Cascader, | |
| 42 | + { | |
| 43 | + allowClear: true, | |
| 44 | + ...componentProps, | |
| 45 | + componenttypename: 'Cascader', | |
| 46 | + }, | |
| 47 | + ]} | |
| 48 | + validator={validator} | |
| 49 | + /> | |
| 50 | + </div> | |
| 51 | + ); | |
| 52 | +}; | |
| 53 | + | |
| 54 | +export default CascaderInput; | ... | ... |
| 1 | +#root .global_checkbox { | |
| 2 | + .ant-formily-item { | |
| 3 | + &-label { | |
| 4 | + font-weight: 500; | |
| 5 | + color: #1d2129; | |
| 6 | + } | |
| 7 | + | |
| 8 | + &-control { | |
| 9 | + &-content { | |
| 10 | + &-component { | |
| 11 | + .ant-checkbox-group { | |
| 12 | + .ant-checkbox-wrapper { | |
| 13 | + .ant-checkbox { | |
| 14 | + &-inner { | |
| 15 | + border-color: #ebeef5; | |
| 16 | + border-radius: 2px; | |
| 17 | + } | |
| 18 | + } | |
| 19 | + } | |
| 20 | + } | |
| 21 | + } | |
| 22 | + } | |
| 23 | + } | |
| 24 | + } | |
| 25 | +} | ... | ... |
| 1 | + | |
| 2 | +import { FormItem, Checkbox } from '@formily/antd-v5'; | |
| 3 | +import { Field } from '@formily/react'; | |
| 4 | +import cx from 'classnames'; | |
| 5 | +import './index.less'; | |
| 6 | +import { FieldProps } from '../../typings'; | |
| 7 | + | |
| 8 | +interface Option { | |
| 9 | + value: string | number; | |
| 10 | + label: string; | |
| 11 | + disabled?: boolean; | |
| 12 | +} | |
| 13 | + | |
| 14 | +export interface P extends FieldProps { | |
| 15 | + options: Option[]; | |
| 16 | +} | |
| 17 | + | |
| 18 | +const CheckboxGroup: React.FC<P> = (props) => { | |
| 19 | + const { name, title, options = [], validator = [], decoratorProps, componentProps } = props; | |
| 20 | + | |
| 21 | + return ( | |
| 22 | + <div className={cx('global_checkbox')}> | |
| 23 | + <Field | |
| 24 | + {...props} | |
| 25 | + name={name} | |
| 26 | + title={title} | |
| 27 | + dataSource={options} | |
| 28 | + decorator={[FormItem, { ...decoratorProps }]} | |
| 29 | + component={[ | |
| 30 | + Checkbox.Group, | |
| 31 | + { | |
| 32 | + ...componentProps, | |
| 33 | + componenttypename: 'Checkbox.Group', | |
| 34 | + }, | |
| 35 | + ]} | |
| 36 | + validator={validator} | |
| 37 | + /> | |
| 38 | + </div> | |
| 39 | + ); | |
| 40 | +}; | |
| 41 | + | |
| 42 | +export default CheckboxGroup; | ... | ... |
packages/hr/antd_input/date_input/index.less
0 → 100644
| 1 | +#root .global_date { | |
| 2 | + .ant-formily-item { | |
| 3 | + &-label { | |
| 4 | + font-weight: 500; | |
| 5 | + color: #1d2129; | |
| 6 | + } | |
| 7 | + | |
| 8 | + &-control { | |
| 9 | + &-content { | |
| 10 | + &-component { | |
| 11 | + .ant-picker { | |
| 12 | + border-color: #ebeef5; | |
| 13 | + border-radius: 2px; | |
| 14 | + padding: 6px 13px; | |
| 15 | + | |
| 16 | + &-input { | |
| 17 | + > input::placeholder { | |
| 18 | + color: #ccc; | |
| 19 | + font-weight: 400; | |
| 20 | + } | |
| 21 | + } | |
| 22 | + | |
| 23 | + &-suffix { | |
| 24 | + color: #606266; | |
| 25 | + } | |
| 26 | + } | |
| 27 | + } | |
| 28 | + } | |
| 29 | + } | |
| 30 | + } | |
| 31 | +} | ... | ... |
packages/hr/antd_input/date_input/index.tsx
0 → 100644
| 1 | + | |
| 2 | +import { FormItem, DatePicker } from '@formily/antd-v5'; | |
| 3 | +import { Field } from '@formily/react'; | |
| 4 | +import cx from 'classnames'; | |
| 5 | +import './index.less'; | |
| 6 | +import { FieldProps } from '../../typings'; | |
| 7 | + | |
| 8 | +const DateInput: React.FC<FieldProps> = (props) => { | |
| 9 | + const { name, title, validator = [], format, decoratorProps, componentProps } = props; | |
| 10 | + | |
| 11 | + return ( | |
| 12 | + <div className={cx('global_date')}> | |
| 13 | + <Field | |
| 14 | + {...props} | |
| 15 | + name={name} | |
| 16 | + title={title} | |
| 17 | + decorator={[FormItem, { ...decoratorProps }]} | |
| 18 | + component={[ | |
| 19 | + DatePicker, | |
| 20 | + { | |
| 21 | + allowClear: true, | |
| 22 | + format: format || 'YYYY/MM/DD', | |
| 23 | + ...componentProps, | |
| 24 | + componenttypename: 'DatePicker', | |
| 25 | + }, | |
| 26 | + ]} | |
| 27 | + validator={validator} | |
| 28 | + /> | |
| 29 | + </div> | |
| 30 | + ); | |
| 31 | +}; | |
| 32 | + | |
| 33 | +export default DateInput; | ... | ... |
packages/hr/antd_input/index.tsx
0 → 100644
| 1 | +import RadioGroup from './radio_group'; | |
| 2 | +import CheckboxGroup from './checkbox_input'; | |
| 3 | +import CascaderInput from './cascader_input'; | |
| 4 | +import TextAreaInput from './text_area_input'; | |
| 5 | +import SelectInput from './select'; | |
| 6 | +import NumberInput from './number_input'; | |
| 7 | +import TextInput from './text_input'; | |
| 8 | +import DateInput from './date_input'; | |
| 9 | +import RangeDate from './range_date'; | |
| 10 | +import SearchInput from './search_input'; | |
| 11 | +import SelectMap from './map_select'; | |
| 12 | +import RadioButton from './radio_button'; | |
| 13 | +import UploadWrap from './uploader/UploadWrap'; | |
| 14 | + | |
| 15 | +const InputItem = () => <div></div>; | |
| 16 | + | |
| 17 | +InputItem.search = SearchInput; | |
| 18 | +InputItem.rangeDate = RangeDate; | |
| 19 | +InputItem.date = DateInput; | |
| 20 | +InputItem.select = SelectInput; | |
| 21 | +InputItem.selectMap = SelectMap; | |
| 22 | +InputItem.number = NumberInput; | |
| 23 | +InputItem.text = TextInput; | |
| 24 | +InputItem.textArea = TextAreaInput; | |
| 25 | +InputItem.cascader = CascaderInput; | |
| 26 | +InputItem.checkbox = CheckboxGroup; | |
| 27 | +InputItem.radio = RadioGroup; | |
| 28 | +InputItem.radioBtn = RadioButton; | |
| 29 | +InputItem.uploader = UploadWrap; | |
| 30 | + | |
| 31 | +export default InputItem; | ... | ... |
packages/hr/antd_input/map_select/index.less
0 → 100644
| 1 | +#root .global_map_input { | |
| 2 | + .ant-formily-item { | |
| 3 | + &-label { | |
| 4 | + font-weight: 500; | |
| 5 | + color: #1d2129; | |
| 6 | + } | |
| 7 | + | |
| 8 | + &-control { | |
| 9 | + &-content { | |
| 10 | + &-component { | |
| 11 | + // line-height: 0 !important; | |
| 12 | + .ant-select-compact-item:not(.ant-select-compact-last-item) { | |
| 13 | + margin-right: 10px; | |
| 14 | + } | |
| 15 | + | |
| 16 | + .ant-select { | |
| 17 | + &-selector { | |
| 18 | + height: 36px; | |
| 19 | + border-color: #ebeef5; | |
| 20 | + border-radius: 2px; | |
| 21 | + padding-left: 13px; | |
| 22 | + } | |
| 23 | + | |
| 24 | + &-arrow { | |
| 25 | + color: #adaeb1; | |
| 26 | + } | |
| 27 | + | |
| 28 | + &-selection-placeholder { | |
| 29 | + color: #ccc; | |
| 30 | + font-weight: 400; | |
| 31 | + line-height: 36px; | |
| 32 | + } | |
| 33 | + } | |
| 34 | + } | |
| 35 | + } | |
| 36 | + } | |
| 37 | + } | |
| 38 | +} | ... | ... |
packages/hr/antd_input/map_select/index.tsx
0 → 100644
| 1 | +import { Field } from '@formily/react'; | |
| 2 | +import { FormItem } from '@formily/antd-v5'; | |
| 3 | +import cx from 'classnames'; | |
| 4 | +import './index.less'; | |
| 5 | +import { FieldProps } from '../../typings'; | |
| 6 | +import MapSelect from '@/hr/map_select'; | |
| 7 | + | |
| 8 | +const SelectMap: React.FC<FieldProps> = (props) => { | |
| 9 | + const { name, addressName, title, validator = [], decoratorProps, componentProps } = props; | |
| 10 | + | |
| 11 | + return ( | |
| 12 | + <div className={cx('global_map_input')}> | |
| 13 | + <Field | |
| 14 | + {...props} | |
| 15 | + name={name} | |
| 16 | + title={title} | |
| 17 | + decorator={[FormItem, { ...decoratorProps }]} | |
| 18 | + component={[ | |
| 19 | + MapSelect, | |
| 20 | + { | |
| 21 | + allowClear: true, | |
| 22 | + ...componentProps, | |
| 23 | + addressName: addressName, | |
| 24 | + componenttypename: 'MapSelect', | |
| 25 | + }, | |
| 26 | + ]} | |
| 27 | + validator={validator} | |
| 28 | + /> | |
| 29 | + </div> | |
| 30 | + ); | |
| 31 | +}; | |
| 32 | + | |
| 33 | +export default SelectMap; | ... | ... |
| 1 | +#root .global_number { | |
| 2 | + .ant-formily-item { | |
| 3 | + &-label { | |
| 4 | + font-weight: 500; | |
| 5 | + color: #1d2129; | |
| 6 | + } | |
| 7 | + | |
| 8 | + &-control { | |
| 9 | + &-content { | |
| 10 | + &-component { | |
| 11 | + line-height: 0 !important; | |
| 12 | + | |
| 13 | + .ant-input-number { | |
| 14 | + border-color: #ebeef5; | |
| 15 | + border-radius: 2px; | |
| 16 | + | |
| 17 | + &-input { | |
| 18 | + padding: 6px 13px; | |
| 19 | + height: 34px; | |
| 20 | + | |
| 21 | + &-wrap { | |
| 22 | + > input::placeholder { | |
| 23 | + color: #ccc; | |
| 24 | + font-weight: 400; | |
| 25 | + line-height: 34px; | |
| 26 | + } | |
| 27 | + } | |
| 28 | + } | |
| 29 | + } | |
| 30 | + } | |
| 31 | + } | |
| 32 | + } | |
| 33 | + } | |
| 34 | +} | ... | ... |
| 1 | + | |
| 2 | +import { Field } from '@formily/react'; | |
| 3 | +import { FormItem } from '@formily/antd-v5'; | |
| 4 | +import { InputNumber } from 'antd'; | |
| 5 | +import cx from 'classnames'; | |
| 6 | +import './index.less'; | |
| 7 | +import { FieldProps } from '../../typings'; | |
| 8 | + | |
| 9 | +const NumberInput: React.FC<FieldProps> = (props) => { | |
| 10 | + const { name, title, validator = [], decoratorProps, componentProps } = props; | |
| 11 | + | |
| 12 | + return ( | |
| 13 | + <div className={cx('global_number')}> | |
| 14 | + <Field | |
| 15 | + {...props} | |
| 16 | + name={name} | |
| 17 | + title={title} | |
| 18 | + decorator={[ | |
| 19 | + FormItem, | |
| 20 | + { | |
| 21 | + ...decoratorProps, | |
| 22 | + }, | |
| 23 | + ]} | |
| 24 | + component={[ | |
| 25 | + InputNumber, | |
| 26 | + { | |
| 27 | + allowClear: true, | |
| 28 | + ...componentProps, | |
| 29 | + componenttypename: 'InputNumber', | |
| 30 | + }, | |
| 31 | + ]} | |
| 32 | + validator={validator} | |
| 33 | + /> | |
| 34 | + </div> | |
| 35 | + ); | |
| 36 | +}; | |
| 37 | + | |
| 38 | +export default NumberInput; | ... | ... |
| 1 | +#root .global_radio_button { | |
| 2 | + .ant-formily-item { | |
| 3 | + &-label { | |
| 4 | + font-weight: 500; | |
| 5 | + color: #1d2129; | |
| 6 | + } | |
| 7 | + | |
| 8 | + .ant-radio-group { | |
| 9 | + .ant-radio-button-wrapper { | |
| 10 | + border-radius: 4px; | |
| 11 | + min-width: 140px; | |
| 12 | + text-align: center; | |
| 13 | + padding: 0; | |
| 14 | + font-size: 14px; | |
| 15 | + font-weight: 500; | |
| 16 | + line-height: 36px; | |
| 17 | + height: auto; | |
| 18 | + background: #f2f3f5; | |
| 19 | + border-color: #f2f3f5; | |
| 20 | + | |
| 21 | + &:not(:first-child) { | |
| 22 | + margin-left: 10px; | |
| 23 | + border-inline-start-width: 1px; | |
| 24 | + | |
| 25 | + &::before { | |
| 26 | + width: 0; | |
| 27 | + } | |
| 28 | + } | |
| 29 | + | |
| 30 | + &-checked:not(&-disabled) { | |
| 31 | + background-color: #4096ff; | |
| 32 | + color: #fff; | |
| 33 | + border-color: #4096ff; | |
| 34 | + } | |
| 35 | + | |
| 36 | + &-disabled.ant-radio-button-wrapper-checked { | |
| 37 | + background: rgba(22, 119, 255, 0.6); | |
| 38 | + color: #fff; | |
| 39 | + } | |
| 40 | + } | |
| 41 | + } | |
| 42 | + } | |
| 43 | +} | ... | ... |
| 1 | +import { FormItem, Radio } from '@formily/antd-v5'; | |
| 2 | +import { Field } from '@formily/react'; | |
| 3 | +import cx from 'classnames'; | |
| 4 | +import './index.less'; | |
| 5 | +import { FieldProps } from '../../typings'; | |
| 6 | + | |
| 7 | +/** | |
| 8 | + * An interface that defines an option in the RadioButton component. | |
| 9 | + */ | |
| 10 | +interface Option { | |
| 11 | + value: string | number; | |
| 12 | + label: string; | |
| 13 | + disabled?: boolean; | |
| 14 | +} | |
| 15 | + | |
| 16 | +/** | |
| 17 | + * An interface that defines the properties of the RadioButton component. | |
| 18 | + */ | |
| 19 | +export interface RadioButtonProps extends FieldProps { | |
| 20 | + options: Option[]; | |
| 21 | +} | |
| 22 | + | |
| 23 | +/** | |
| 24 | + * The RadioButton component. | |
| 25 | + * | |
| 26 | + * This is a wrapper around the Radio.Group component with optionType set to 'button' | |
| 27 | + * which makes it easier to use in a Formily form. | |
| 28 | + */ | |
| 29 | +const RadioButton: React.FC<RadioButtonProps> = (props) => { | |
| 30 | + const { name, title, options = [], validator = [], decoratorProps, componentProps } = props; | |
| 31 | + | |
| 32 | + return ( | |
| 33 | + <div className={cx('global_radio_button')}> | |
| 34 | + <Field | |
| 35 | + {...props} | |
| 36 | + name={name} | |
| 37 | + title={title} | |
| 38 | + dataSource={options} | |
| 39 | + decorator={[ | |
| 40 | + FormItem, | |
| 41 | + { | |
| 42 | + ...decoratorProps, | |
| 43 | + }, | |
| 44 | + ]} | |
| 45 | + component={[ | |
| 46 | + Radio.Group, | |
| 47 | + { | |
| 48 | + optionType: 'button', | |
| 49 | + ...componentProps, | |
| 50 | + componenttypename: 'Radio.Group', | |
| 51 | + }, | |
| 52 | + ]} | |
| 53 | + validator={validator} | |
| 54 | + /> | |
| 55 | + </div> | |
| 56 | + ); | |
| 57 | +}; | |
| 58 | + | |
| 59 | +export default RadioButton; | ... | ... |
| 1 | +#root .global_radio { | |
| 2 | + .ant-formily-item { | |
| 3 | + &-label { | |
| 4 | + font-weight: 500; | |
| 5 | + color: #1d2129; | |
| 6 | + } | |
| 7 | + | |
| 8 | + &-control { | |
| 9 | + &-content { | |
| 10 | + &-component { | |
| 11 | + .ant-radio-group { | |
| 12 | + .ant-radio-wrapper { | |
| 13 | + .ant-radio { | |
| 14 | + &-inner { | |
| 15 | + border-color: #ebeef5; | |
| 16 | + } | |
| 17 | + } | |
| 18 | + } | |
| 19 | + } | |
| 20 | + } | |
| 21 | + } | |
| 22 | + } | |
| 23 | + } | |
| 24 | +} | ... | ... |
packages/hr/antd_input/radio_group/index.tsx
0 → 100644
| 1 | +import { FormItem, Radio } from '@formily/antd-v5'; | |
| 2 | +import { Field } from '@formily/react'; | |
| 3 | +import cx from 'classnames'; | |
| 4 | +import './index.less'; | |
| 5 | +import { FieldProps } from '../../typings'; | |
| 6 | + | |
| 7 | +/** | |
| 8 | + * An interface that defines an option in the RadioGroup component. | |
| 9 | + */ | |
| 10 | +interface Option { | |
| 11 | + value: string | number; | |
| 12 | + label: string; | |
| 13 | + disabled?: boolean; | |
| 14 | +} | |
| 15 | + | |
| 16 | +/** | |
| 17 | + * An interface that defines the properties of the RadioGroup component. | |
| 18 | + */ | |
| 19 | +export interface RadioGroupProps extends FieldProps { | |
| 20 | + options: Option[]; | |
| 21 | +} | |
| 22 | + | |
| 23 | +/** | |
| 24 | + * The RadioGroup component. | |
| 25 | + * | |
| 26 | + * This is a wrapper around the Radio.Group component that makes it easier to use in a Formily form. | |
| 27 | + */ | |
| 28 | +const RadioGroup: React.FC<RadioGroupProps> = (props) => { | |
| 29 | + const { name, title, options = [], validator = [], decoratorProps, componentProps } = props; | |
| 30 | + | |
| 31 | + return ( | |
| 32 | + <div className={cx('global_radio')}> | |
| 33 | + <Field | |
| 34 | + {...props} | |
| 35 | + name={name} | |
| 36 | + title={title} | |
| 37 | + dataSource={options} | |
| 38 | + decorator={[ | |
| 39 | + FormItem, | |
| 40 | + { | |
| 41 | + ...decoratorProps, | |
| 42 | + }, | |
| 43 | + ]} | |
| 44 | + component={[ | |
| 45 | + Radio.Group, | |
| 46 | + { | |
| 47 | + ...componentProps, | |
| 48 | + componenttypename: 'Radio.Group', | |
| 49 | + }, | |
| 50 | + ]} | |
| 51 | + validator={validator} | |
| 52 | + /> | |
| 53 | + </div> | |
| 54 | + ); | |
| 55 | +}; | |
| 56 | + | |
| 57 | +export default RadioGroup; | ... | ... |
packages/hr/antd_input/range_date/index.less
0 → 100644
| 1 | +#root .global_rangedate { | |
| 2 | + .ant-formily-item { | |
| 3 | + &-label { | |
| 4 | + font-weight: 500; | |
| 5 | + color: #1d2129; | |
| 6 | + } | |
| 7 | + | |
| 8 | + &-control { | |
| 9 | + &-content { | |
| 10 | + &-component { | |
| 11 | + .ant-picker { | |
| 12 | + border-color: #ebeef5; | |
| 13 | + border-radius: 2px; | |
| 14 | + padding: 6px 13px; | |
| 15 | + | |
| 16 | + &-input { | |
| 17 | + > input::placeholder { | |
| 18 | + color: #ccc; | |
| 19 | + font-weight: 400; | |
| 20 | + } | |
| 21 | + } | |
| 22 | + | |
| 23 | + &-suffix { | |
| 24 | + color: #606266; | |
| 25 | + } | |
| 26 | + } | |
| 27 | + } | |
| 28 | + } | |
| 29 | + } | |
| 30 | + } | |
| 31 | +} | ... | ... |
packages/hr/antd_input/range_date/index.tsx
0 → 100644
| 1 | +import React from 'react'; | |
| 2 | +import { FormItem, DatePicker } from '@formily/antd-v5'; | |
| 3 | +import { Field } from '@formily/react'; | |
| 4 | +import cx from 'classnames'; | |
| 5 | +import './index.less'; | |
| 6 | +import { FieldProps } from '../../typings'; | |
| 7 | +import moment from 'moment'; | |
| 8 | + | |
| 9 | +const { RangePicker } = DatePicker; | |
| 10 | +const RangePickerWrap = (props: any) => { | |
| 11 | + const { value, onChange, unix } = props; | |
| 12 | + | |
| 13 | + const [stateValue, setStateValue] = React.useState<any>([]); | |
| 14 | + | |
| 15 | + React.useEffect(() => { | |
| 16 | + if (value && value.length && unix) { | |
| 17 | + setStateValue([moment(value[0] * 1000), moment(value[1] * 1000)]); | |
| 18 | + return; | |
| 19 | + } | |
| 20 | + | |
| 21 | + setStateValue(value); | |
| 22 | + }, [value]); | |
| 23 | + | |
| 24 | + const handleChange = (value: any) => { | |
| 25 | + const [start, end] = value || []; | |
| 26 | + | |
| 27 | + if (!value) { | |
| 28 | + onChange([]); | |
| 29 | + return; | |
| 30 | + } | |
| 31 | + if (unix) { | |
| 32 | + onChange([moment(start).unix(), moment(end).unix()]); | |
| 33 | + } else { | |
| 34 | + onChange(value); | |
| 35 | + } | |
| 36 | + }; | |
| 37 | + | |
| 38 | + return <RangePicker {...props} onChange={handleChange} value={stateValue} />; | |
| 39 | +}; | |
| 40 | + | |
| 41 | +const RangeDate: React.FC<FieldProps> = (props) => { | |
| 42 | + const { name, title, validator = [], format, decoratorProps, componentProps } = props; | |
| 43 | + | |
| 44 | + return ( | |
| 45 | + <div className={cx('global_rangedate')}> | |
| 46 | + <Field | |
| 47 | + {...props} | |
| 48 | + name={name} | |
| 49 | + title={title} | |
| 50 | + decorator={[ | |
| 51 | + FormItem, | |
| 52 | + { | |
| 53 | + ...decoratorProps, | |
| 54 | + }, | |
| 55 | + ]} | |
| 56 | + component={[ | |
| 57 | + RangePickerWrap, | |
| 58 | + { | |
| 59 | + allowClear: true, | |
| 60 | + componenttypename: 'DatePicker.RangePicker', | |
| 61 | + format: format || ['YYYY/MM/DD', 'YYYY/MM/DD'], | |
| 62 | + ...componentProps, | |
| 63 | + }, | |
| 64 | + ]} | |
| 65 | + validator={validator} | |
| 66 | + /> | |
| 67 | + </div> | |
| 68 | + ); | |
| 69 | +}; | |
| 70 | + | |
| 71 | +export default RangeDate; | ... | ... |
| 1 | +#root .global_search { | |
| 2 | + .ant-formily-item { | |
| 3 | + &-label { | |
| 4 | + font-weight: 500; | |
| 5 | + color: #1d2129; | |
| 6 | + } | |
| 7 | + | |
| 8 | + &-control { | |
| 9 | + &-content { | |
| 10 | + &-component { | |
| 11 | + .ant-input-affix-wrapper { | |
| 12 | + border-color: #f2f3f5; | |
| 13 | + border-radius: 2px; | |
| 14 | + padding: 6px 13px; | |
| 15 | + background: #f2f3f5; | |
| 16 | + | |
| 17 | + > input::placeholder { | |
| 18 | + color: #ccc; | |
| 19 | + font-weight: 400; | |
| 20 | + } | |
| 21 | + | |
| 22 | + .ant-input-prefix { | |
| 23 | + font-size: 16px; | |
| 24 | + color: rgba(0, 0, 0, 0.25); | |
| 25 | + margin-inline-end: 10px; | |
| 26 | + } | |
| 27 | + | |
| 28 | + .ant-input { | |
| 29 | + background-color: #f2f3f5; | |
| 30 | + } | |
| 31 | + } | |
| 32 | + } | |
| 33 | + } | |
| 34 | + } | |
| 35 | + } | |
| 36 | +} | ... | ... |
| 1 | +import { Field } from '@formily/react'; | |
| 2 | +import { FormItem, Input } from '@formily/antd-v5'; | |
| 3 | +import { SearchOutlined } from '@ant-design/icons'; | |
| 4 | +import cx from 'classnames'; | |
| 5 | +import './index.less'; | |
| 6 | +import { FieldProps } from '../../typings'; | |
| 7 | + | |
| 8 | +const SearchInput: React.FC<FieldProps> = (props) => { | |
| 9 | + const { name, validator = [], decoratorProps, componentProps } = props; | |
| 10 | + | |
| 11 | + return ( | |
| 12 | + <div className={cx('global_search')}> | |
| 13 | + <Field | |
| 14 | + {...props} | |
| 15 | + name={name} | |
| 16 | + decorator={[ | |
| 17 | + FormItem, | |
| 18 | + { | |
| 19 | + ...decoratorProps, | |
| 20 | + }, | |
| 21 | + ]} | |
| 22 | + component={[ | |
| 23 | + Input, | |
| 24 | + { | |
| 25 | + allowClear: true, | |
| 26 | + prefix: <SearchOutlined />, | |
| 27 | + ...componentProps, | |
| 28 | + componenttypename: 'Input', | |
| 29 | + }, | |
| 30 | + ]} | |
| 31 | + validator={validator} | |
| 32 | + /> | |
| 33 | + </div> | |
| 34 | + ); | |
| 35 | +}; | |
| 36 | + | |
| 37 | +export default SearchInput; | ... | ... |
packages/hr/antd_input/select/index.less
0 → 100644
| 1 | +#root .global_select { | |
| 2 | + .ant-formily-item { | |
| 3 | + &-label { | |
| 4 | + font-weight: 500; | |
| 5 | + color: #1d2129; | |
| 6 | + } | |
| 7 | + | |
| 8 | + &-control { | |
| 9 | + &-content { | |
| 10 | + &-component { | |
| 11 | + .ant-select { | |
| 12 | + &-selector { | |
| 13 | + height: 36px; | |
| 14 | + border-color: #ebeef5; | |
| 15 | + border-radius: 2px; | |
| 16 | + padding-left: 13px; | |
| 17 | + } | |
| 18 | + | |
| 19 | + &-arrow { | |
| 20 | + color: #adaeb1; | |
| 21 | + } | |
| 22 | + | |
| 23 | + &-selection-placeholder { | |
| 24 | + color: #ccc; | |
| 25 | + font-weight: 400; | |
| 26 | + line-height: 36px; | |
| 27 | + } | |
| 28 | + } | |
| 29 | + } | |
| 30 | + } | |
| 31 | + } | |
| 32 | + } | |
| 33 | +} | ... | ... |
packages/hr/antd_input/select/index.tsx
0 → 100644
| 1 | + | |
| 2 | +import { FormItem, Select } from '@formily/antd-v5'; | |
| 3 | +import { Field } from '@formily/react'; | |
| 4 | +import cx from 'classnames'; | |
| 5 | +import './index.less'; | |
| 6 | +import { FieldProps } from '../../typings'; | |
| 7 | + | |
| 8 | +const SelectInput: React.FC<FieldProps> = (props) => { | |
| 9 | + const { name, title, options = [], validator = [], decoratorProps, componentProps } = props; | |
| 10 | + | |
| 11 | + return ( | |
| 12 | + <div className={cx('global_select')}> | |
| 13 | + <Field | |
| 14 | + {...props} | |
| 15 | + name={name} | |
| 16 | + title={title} | |
| 17 | + dataSource={options} | |
| 18 | + decorator={[ | |
| 19 | + FormItem, | |
| 20 | + { | |
| 21 | + ...decoratorProps, | |
| 22 | + }, | |
| 23 | + ]} | |
| 24 | + component={[ | |
| 25 | + Select, | |
| 26 | + { | |
| 27 | + allowClear: true, | |
| 28 | + showSearch: true, | |
| 29 | + filterOption: (inputValue, option) => { | |
| 30 | + console.log('option.label3333', option.label); | |
| 31 | + let l = option.label + ''; | |
| 32 | + | |
| 33 | + return l && l.indexOf(inputValue) != -1; | |
| 34 | + }, | |
| 35 | + ...componentProps, | |
| 36 | + componenttypename: 'Select', | |
| 37 | + }, | |
| 38 | + ]} | |
| 39 | + validator={validator} | |
| 40 | + /> | |
| 41 | + </div> | |
| 42 | + ); | |
| 43 | +}; | |
| 44 | + | |
| 45 | +export default SelectInput; | ... | ... |
| 1 | +#root .global_textarea { | |
| 2 | + .ant-formily-item { | |
| 3 | + &-label { | |
| 4 | + font-weight: 500; | |
| 5 | + color: #1d2129; | |
| 6 | + } | |
| 7 | + | |
| 8 | + &-control { | |
| 9 | + &-content { | |
| 10 | + &-component { | |
| 11 | + .ant-input { | |
| 12 | + padding: 0; | |
| 13 | + | |
| 14 | + &-affix-wrapper { | |
| 15 | + border-color: #ebeef5; | |
| 16 | + border-radius: 2px; | |
| 17 | + padding: 6px 13px; | |
| 18 | + | |
| 19 | + &::placeholder { | |
| 20 | + color: #ccc; | |
| 21 | + font-weight: 400; | |
| 22 | + } | |
| 23 | + } | |
| 24 | + } | |
| 25 | + } | |
| 26 | + } | |
| 27 | + } | |
| 28 | + } | |
| 29 | +} | ... | ... |
| 1 | + | |
| 2 | +import { Field } from '@formily/react'; | |
| 3 | +import { FormItem, Input } from '@formily/antd-v5'; | |
| 4 | +import cx from 'classnames'; | |
| 5 | +import './index.less'; | |
| 6 | +import { FieldProps } from '../../typings'; | |
| 7 | + | |
| 8 | +const TextAreaInput: React.FC<FieldProps> = (props) => { | |
| 9 | + const { name, title, validator = [], decoratorProps, componentProps } = props; | |
| 10 | + | |
| 11 | + return ( | |
| 12 | + <div className={cx('global_textarea')}> | |
| 13 | + <Field | |
| 14 | + {...props} | |
| 15 | + name={name} | |
| 16 | + title={title} | |
| 17 | + decorator={[FormItem, { ...decoratorProps }]} | |
| 18 | + component={[ | |
| 19 | + Input.TextArea, | |
| 20 | + { | |
| 21 | + allowClear: true, | |
| 22 | + ...componentProps, | |
| 23 | + componenttypename: 'Input.TextArea', | |
| 24 | + }, | |
| 25 | + ]} | |
| 26 | + validator={validator} | |
| 27 | + /> | |
| 28 | + </div> | |
| 29 | + ); | |
| 30 | +}; | |
| 31 | + | |
| 32 | +export default TextAreaInput; | ... | ... |
packages/hr/antd_input/text_input/index.less
0 → 100644
| 1 | +#root .global_text { | |
| 2 | + .ant-formily-item { | |
| 3 | + &-label { | |
| 4 | + font-weight: 500; | |
| 5 | + color: #1d2129; | |
| 6 | + } | |
| 7 | + | |
| 8 | + &-control { | |
| 9 | + &-content { | |
| 10 | + &-component { | |
| 11 | + .ant-input-affix-wrapper { | |
| 12 | + border-color: #ebeef5; | |
| 13 | + border-radius: 2px; | |
| 14 | + padding: 6px 13px; | |
| 15 | + | |
| 16 | + > input::placeholder { | |
| 17 | + color: #ccc; | |
| 18 | + font-weight: 400; | |
| 19 | + } | |
| 20 | + } | |
| 21 | + } | |
| 22 | + } | |
| 23 | + } | |
| 24 | + } | |
| 25 | +} | ... | ... |
packages/hr/antd_input/text_input/index.tsx
0 → 100644
| 1 | + | |
| 2 | +import { Field } from '@formily/react'; | |
| 3 | +import { FormItem, Input } from '@formily/antd-v5'; | |
| 4 | +import cx from 'classnames'; | |
| 5 | +import './index.less'; | |
| 6 | +import { FieldProps } from '../../typings'; | |
| 7 | + | |
| 8 | +const TextInput: React.FC<FieldProps> = (props) => { | |
| 9 | + const { name, title, validator = [], decoratorProps, componentProps, ...args } = props; | |
| 10 | + | |
| 11 | + return ( | |
| 12 | + <div className={cx('global_text')}> | |
| 13 | + <Field | |
| 14 | + {...props} | |
| 15 | + name={name} | |
| 16 | + title={title} | |
| 17 | + {...args} | |
| 18 | + decorator={[FormItem, { ...decoratorProps }]} | |
| 19 | + component={[ | |
| 20 | + Input, | |
| 21 | + { | |
| 22 | + allowClear: true, | |
| 23 | + ...componentProps, | |
| 24 | + componenttypename: 'Input', | |
| 25 | + }, | |
| 26 | + ]} | |
| 27 | + validator={validator} | |
| 28 | + /> | |
| 29 | + </div> | |
| 30 | + ); | |
| 31 | +}; | |
| 32 | + | |
| 33 | +export default TextInput; | ... | ... |
| 1 | + | |
| 2 | +import { Field } from '@formily/react'; | |
| 3 | +import { FormItem } from '@formily/antd-v5'; | |
| 4 | +import cx from 'classnames'; | |
| 5 | +import './index.less'; | |
| 6 | +import { FieldProps } from '../../typings'; | |
| 7 | +import UploadFiles from '@/hr/upload_file'; | |
| 8 | + | |
| 9 | +const InputUpload: React.FC<FieldProps> = (props) => { | |
| 10 | + const { name, title, validator = [], decoratorProps, componentProps } = props; | |
| 11 | + | |
| 12 | + return ( | |
| 13 | + <div className={cx('global_textarea')}> | |
| 14 | + <Field | |
| 15 | + {...props} | |
| 16 | + name={name} | |
| 17 | + title={title} | |
| 18 | + decorator={[FormItem, { ...decoratorProps }]} | |
| 19 | + component={[ | |
| 20 | + UploadFiles, | |
| 21 | + { | |
| 22 | + ...componentProps, | |
| 23 | + }, | |
| 24 | + ]} | |
| 25 | + validator={validator} | |
| 26 | + /> | |
| 27 | + </div> | |
| 28 | + ); | |
| 29 | +}; | |
| 30 | + | |
| 31 | +export default InputUpload; | ... | ... |
| 1 | + | |
| 2 | +import { Field } from '@formily/react'; | |
| 3 | +import { FormItem } from '@formily/antd-v5'; | |
| 4 | +import cx from 'classnames'; | |
| 5 | +import './index.less'; | |
| 6 | +import { FieldProps } from '../../typings'; | |
| 7 | +import UploadFiles from '@/hr/upload_file'; | |
| 8 | + | |
| 9 | +const UploadWrap: React.FC<FieldProps> = (props) => { | |
| 10 | + const { name, title, validator = [], decoratorProps, componentProps } = props; | |
| 11 | + | |
| 12 | + return ( | |
| 13 | + <div className={cx('global_upload_wrap')}> | |
| 14 | + <Field | |
| 15 | + {...props} | |
| 16 | + name={name} | |
| 17 | + title={title} | |
| 18 | + decorator={[FormItem, { ...decoratorProps }]} | |
| 19 | + component={[ | |
| 20 | + UploadFiles, | |
| 21 | + { | |
| 22 | + ...componentProps, | |
| 23 | + componenttypename: 'UploadFiles', | |
| 24 | + }, | |
| 25 | + ]} | |
| 26 | + validator={validator} | |
| 27 | + /> | |
| 28 | + </div> | |
| 29 | + ); | |
| 30 | +}; | |
| 31 | + | |
| 32 | +export default UploadWrap; | ... | ... |
packages/hr/antd_input/uploader/index.less
0 → 100644
packages/hr/braft_editor/index.d.ts
0 → 100644
| 1 | +import React, { ReactElement } from 'react'; | |
| 2 | +import 'braft-editor/dist/index.css'; | |
| 3 | +import './index.less'; | |
| 4 | +import { IFieldProps } from '@formily/core'; | |
| 5 | +interface P extends IFieldProps { | |
| 6 | + name: string | [string, string]; | |
| 7 | + title?: string | ReactElement; | |
| 8 | + validator?: any; | |
| 9 | + [props: string]: any; | |
| 10 | + decoratorProps?: { | |
| 11 | + [props: string]: any; | |
| 12 | + }; | |
| 13 | + componentProps?: { | |
| 14 | + [props: string]: any; | |
| 15 | + }; | |
| 16 | +} | |
| 17 | +declare const BraftDeitor: React.FC<P>; | |
| 18 | +export default BraftDeitor; | ... | ... |
packages/hr/braft_editor/index.less
0 → 100644
| 1 | +.editor_warp { | |
| 2 | + border-radius: 6px; | |
| 3 | + margin-bottom: 30px; | |
| 4 | + | |
| 5 | + .ant-formily-item { | |
| 6 | + &-label { | |
| 7 | + font-weight: 500; | |
| 8 | + color: #1d2129; | |
| 9 | + } | |
| 10 | + | |
| 11 | + .editor { | |
| 12 | + border: 1px solid #ebeef5; | |
| 13 | + | |
| 14 | + .bf-container { | |
| 15 | + .public-DraftEditor-content { | |
| 16 | + padding: 6px 13px; | |
| 17 | + } | |
| 18 | + | |
| 19 | + .bf-content { | |
| 20 | + color: #1d2129; | |
| 21 | + font-size: 14px; | |
| 22 | + | |
| 23 | + .public-DraftEditorPlaceholder-root { | |
| 24 | + top: 6px; | |
| 25 | + left: 13px; | |
| 26 | + } | |
| 27 | + | |
| 28 | + .public-DraftEditorPlaceholder-inner { | |
| 29 | + color: #ccc; | |
| 30 | + font-weight: 400; | |
| 31 | + font-size: 14px; | |
| 32 | + | |
| 33 | + > div > p { | |
| 34 | + margin-block-start: 0; | |
| 35 | + margin-block-end: 3px; | |
| 36 | + } | |
| 37 | + } | |
| 38 | + } | |
| 39 | + } | |
| 40 | + } | |
| 41 | + } | |
| 42 | +} | ... | ... |
packages/hr/braft_editor/index.tsx
0 → 100644
| 1 | +// https://www.yuque.com/braft-editor/be/lzwpnr#ecff77a8 富文本编辑器 简介说明 | |
| 2 | + | |
| 3 | +import React, { ReactElement } from 'react'; | |
| 4 | +// 引入编辑器组件 | |
| 5 | +import BraftEditor, { BraftEditorProps, EditorState } from 'braft-editor'; | |
| 6 | +import 'braft-editor/dist/index.css'; | |
| 7 | +import cx from 'classnames'; | |
| 8 | +import './index.less'; | |
| 9 | +import { FormItem } from '@formily/antd-v5'; | |
| 10 | +import { Field } from '@formily/react'; | |
| 11 | +import { IFieldProps } from '@formily/core'; | |
| 12 | + | |
| 13 | +interface Props extends BraftEditorProps { | |
| 14 | + contentStyle?: { [props: string]: string | number }; | |
| 15 | + changeEditor?: (editorState: EditorState) => void; | |
| 16 | +} | |
| 17 | + | |
| 18 | +const Editor: React.FC<Props> = (props) => { | |
| 19 | + const { | |
| 20 | + value = null, | |
| 21 | + changeEditor, | |
| 22 | + controls = [], | |
| 23 | + excludeControls = [], | |
| 24 | + extendControls = [], | |
| 25 | + contentStyle = { width: 600, height: 120 }, | |
| 26 | + placeholder, | |
| 27 | + } = props; | |
| 28 | + | |
| 29 | + const { width, height } = contentStyle; | |
| 30 | + let myContentStyle = contentStyle | |
| 31 | + ? contentStyle | |
| 32 | + : { | |
| 33 | + height: '120px', | |
| 34 | + fontSize: '14px', | |
| 35 | + }; | |
| 36 | + | |
| 37 | + return ( | |
| 38 | + <div | |
| 39 | + className={cx('editor')} | |
| 40 | + style={{ | |
| 41 | + width, | |
| 42 | + height, | |
| 43 | + }} | |
| 44 | + > | |
| 45 | + <BraftEditor | |
| 46 | + value={BraftEditor.createEditorState(value)} | |
| 47 | + stripPastedStyles={true} | |
| 48 | + contentStyle={myContentStyle} | |
| 49 | + onBlur={(event: EditorState) => { | |
| 50 | + if (changeEditor) { | |
| 51 | + changeEditor(event.toHTML()); | |
| 52 | + } | |
| 53 | + }} | |
| 54 | + placeholder={placeholder} | |
| 55 | + controls={controls} //控件列表 | |
| 56 | + excludeControls={excludeControls} //不展示的 控件列表 | |
| 57 | + extendControls={extendControls} //自定义控件 | |
| 58 | + /> | |
| 59 | + </div> | |
| 60 | + ); | |
| 61 | +}; | |
| 62 | + | |
| 63 | +interface P extends IFieldProps { | |
| 64 | + name: string | [string, string]; | |
| 65 | + title?: string | ReactElement; | |
| 66 | + validator?: any; | |
| 67 | + [props: string]: any; | |
| 68 | + decoratorProps?: { | |
| 69 | + [props: string]: any; | |
| 70 | + }; | |
| 71 | + componentProps?: { | |
| 72 | + [props: string]: any; | |
| 73 | + }; | |
| 74 | +} | |
| 75 | + | |
| 76 | +const BraftDeitor: React.FC<P> = (props) => { | |
| 77 | + const { name, title, validator, decoratorProps, componentProps } = props; | |
| 78 | + | |
| 79 | + return ( | |
| 80 | + <div className={cx('editor_warp')}> | |
| 81 | + <Field | |
| 82 | + name={name} | |
| 83 | + title={title} | |
| 84 | + validator={validator} | |
| 85 | + decorator={[FormItem, decoratorProps]} | |
| 86 | + component={[Editor, componentProps]} | |
| 87 | + /> | |
| 88 | + </div> | |
| 89 | + ); | |
| 90 | +}; | |
| 91 | + | |
| 92 | +export default BraftDeitor; | ... | ... |
packages/hr/bread_crumb/bread.less
0 → 100644
packages/hr/bread_crumb/index.d.ts
0 → 100644
packages/hr/bread_crumb/index.tsx
0 → 100644
| 1 | +import React from 'react'; | |
| 2 | +import { Breadcrumb as MyBreadcrumb } from 'antd'; | |
| 3 | +import { ItemType } from 'antd/lib/breadcrumb/Breadcrumb'; | |
| 4 | +import './bread.less'; | |
| 5 | + | |
| 6 | +interface P { | |
| 7 | + separator?: string; | |
| 8 | + items: ItemType[]; | |
| 9 | +} | |
| 10 | + | |
| 11 | +const Breadcrumb: React.FC<P> = (props) => { | |
| 12 | + const { separator = '>', items = [] } = props; | |
| 13 | + | |
| 14 | + return ( | |
| 15 | + <div className={'bread_warp'}> | |
| 16 | + <MyBreadcrumb separator={separator} items={items} /> | |
| 17 | + </div> | |
| 18 | + ); | |
| 19 | +}; | |
| 20 | + | |
| 21 | +export default Breadcrumb; | ... | ... |
packages/hr/button_radio/index.d.ts
0 → 100644
| 1 | +import React from 'react'; | |
| 2 | +import { ButtonProps } from 'antd'; | |
| 3 | +import type { SpaceProps } from 'antd'; | |
| 4 | +export interface ButtonRadioProps extends SpaceProps { | |
| 5 | + className?: string; | |
| 6 | + prefixCls?: string; | |
| 7 | + buttonProps?: ButtonProps; | |
| 8 | + onChange?: (value: any) => void; | |
| 9 | + value?: any; | |
| 10 | + [props: string]: any; | |
| 11 | +} | |
| 12 | +declare const ButtonRadio: React.FC<ButtonRadioProps>; | |
| 13 | +export default ButtonRadio; | ... | ... |
packages/hr/button_radio/index.tsx
0 → 100644
| 1 | +import React from 'react'; | |
| 2 | +import { Button, Space, ButtonProps } from 'antd'; | |
| 3 | +import type { SpaceProps } from 'antd'; | |
| 4 | +export interface ButtonRadioProps extends SpaceProps { | |
| 5 | + className?: string; | |
| 6 | + prefixCls?: string; | |
| 7 | + buttonProps?: ButtonProps; | |
| 8 | + onChange?: (value: any) => void; | |
| 9 | + value?: any; | |
| 10 | + [props: string]: any; | |
| 11 | +} | |
| 12 | + | |
| 13 | +const ButtonRadio: React.FC<ButtonRadioProps> = (props) => { | |
| 14 | + const { options = [], buttonProps = {}, value, onChange } = props; | |
| 15 | + | |
| 16 | + return ( | |
| 17 | + <Space {...props}> | |
| 18 | + {options.map( | |
| 19 | + ( | |
| 20 | + option: { | |
| 21 | + value: any; | |
| 22 | + label: | |
| 23 | + | string | |
| 24 | + | number | |
| 25 | + | boolean | |
| 26 | + | React.ReactElement<any, string | React.JSXElementConstructor<any>> | |
| 27 | + | React.ReactFragment | |
| 28 | + | React.ReactPortal | |
| 29 | + | null | |
| 30 | + | undefined; | |
| 31 | + }, | |
| 32 | + i: React.Key | null | undefined, | |
| 33 | + ) => { | |
| 34 | + const type = option.value === value ? 'primary' : 'default'; | |
| 35 | + return ( | |
| 36 | + <Button | |
| 37 | + key={i} | |
| 38 | + {...buttonProps} | |
| 39 | + type={type} | |
| 40 | + onClick={() => { | |
| 41 | + onChange && onChange(option.value); | |
| 42 | + }} | |
| 43 | + > | |
| 44 | + {option.label} | |
| 45 | + </Button> | |
| 46 | + ); | |
| 47 | + }, | |
| 48 | + )} | |
| 49 | + </Space> | |
| 50 | + ); | |
| 51 | +}; | |
| 52 | + | |
| 53 | +export default ButtonRadio; | ... | ... |
packages/hr/form_modal/index.d.ts
0 → 100644
| 1 | +import React from 'react'; | |
| 2 | +import { ModalProps } from 'antd'; | |
| 3 | +interface Props extends ModalProps { | |
| 4 | + handleOk?: (values: { | |
| 5 | + [props: string]: string; | |
| 6 | + }, e: React.MouseEvent<HTMLButtonElement>) => void; | |
| 7 | + handleCancel?: (e: React.MouseEvent<HTMLButtonElement>) => void; | |
| 8 | + initialValues?: { | |
| 9 | + props?: string; | |
| 10 | + }; | |
| 11 | + open: boolean; | |
| 12 | +} | |
| 13 | +declare const FormModal: React.FC<Props>; | |
| 14 | +export default FormModal; | ... | ... |
packages/hr/form_modal/index.tsx
0 → 100644
| 1 | +import React, { useEffect } from 'react'; | |
| 2 | +import { Modal, ModalProps } from 'antd'; | |
| 3 | +import { FormProvider } from '@formily/react'; | |
| 4 | +import { createForm } from '@formily/core'; | |
| 5 | + | |
| 6 | +interface Props extends ModalProps { | |
| 7 | + handleOk?: (values: { [props: string]: string }, e: React.MouseEvent<HTMLButtonElement>) => void; | |
| 8 | + handleCancel?: (e: React.MouseEvent<HTMLButtonElement>) => void; | |
| 9 | + initialValues?: { props?: string }; | |
| 10 | + open: boolean; | |
| 11 | +} | |
| 12 | + | |
| 13 | +const FormModal: React.FC<Props> = (props) => { | |
| 14 | + const form = createForm(); | |
| 15 | + const { | |
| 16 | + initialValues, | |
| 17 | + title = '我是弹框标题', | |
| 18 | + handleOk, | |
| 19 | + handleCancel, | |
| 20 | + children = [], | |
| 21 | + open, | |
| 22 | + ...args | |
| 23 | + } = props; | |
| 24 | + const items = React.Children.toArray(children); | |
| 25 | + | |
| 26 | + useEffect(() => { | |
| 27 | + form.setInitialValues(initialValues); | |
| 28 | + }); | |
| 29 | + | |
| 30 | + const onOK = (e: React.MouseEvent<HTMLButtonElement>) => { | |
| 31 | + if (handleOk) { | |
| 32 | + form.submit().then((values: any) => { | |
| 33 | + handleOk(values, e); | |
| 34 | + }); | |
| 35 | + | |
| 36 | + return; | |
| 37 | + } | |
| 38 | + }; | |
| 39 | + | |
| 40 | + const onCancel = (e: React.MouseEvent<HTMLButtonElement>) => { | |
| 41 | + if (handleCancel) { | |
| 42 | + handleCancel(e); | |
| 43 | + | |
| 44 | + return; | |
| 45 | + } | |
| 46 | + }; | |
| 47 | + | |
| 48 | + return ( | |
| 49 | + <Modal | |
| 50 | + title={title} | |
| 51 | + open={open} | |
| 52 | + onOk={onOK} | |
| 53 | + onCancel={onCancel} | |
| 54 | + maskClosable={false} | |
| 55 | + centered | |
| 56 | + {...args} | |
| 57 | + getContainer={false} | |
| 58 | + > | |
| 59 | + <FormProvider form={form}>{React.Children.map(items, (item: any) => item)}</FormProvider> | |
| 60 | + </Modal> | |
| 61 | + ); | |
| 62 | +}; | |
| 63 | + | |
| 64 | +export default FormModal; | ... | ... |
packages/hr/image/index.d.ts
0 → 100644
packages/hr/image/index.tsx
0 → 100644
| 1 | +import { ImageProps, Image as MyImage } from 'antd'; | |
| 2 | + | |
| 3 | +interface Props extends ImageProps { | |
| 4 | + imgValues?: string[]; | |
| 5 | + show_image?: boolean; | |
| 6 | + setShowImage?: (value: any) => void; | |
| 7 | +} | |
| 8 | + | |
| 9 | +const Image: React.FC<Props> = (props) => { | |
| 10 | + const { src, style, preview, imgValues = [], show_image, setShowImage, ...args } = props; | |
| 11 | + let my_preview = preview | |
| 12 | + ? preview | |
| 13 | + : { | |
| 14 | + visible: show_image, | |
| 15 | + src: src, | |
| 16 | + }; | |
| 17 | + | |
| 18 | + return ( | |
| 19 | + <> | |
| 20 | + <MyImage | |
| 21 | + width={200} | |
| 22 | + style={style} | |
| 23 | + src={src} | |
| 24 | + preview={my_preview} | |
| 25 | + {...args} | |
| 26 | + onClick={() => setShowImage && setShowImage(!show_image)} | |
| 27 | + /> | |
| 28 | + <div style={{ display: 'none' }}> | |
| 29 | + <MyImage.PreviewGroup | |
| 30 | + preview={{ | |
| 31 | + visible: show_image, | |
| 32 | + onVisibleChange: (vis) => setShowImage && setShowImage(vis), | |
| 33 | + }} | |
| 34 | + > | |
| 35 | + {imgValues.map((data: string, index: number) => ( | |
| 36 | + <MyImage src={data} key={index} /> | |
| 37 | + ))} | |
| 38 | + </MyImage.PreviewGroup> | |
| 39 | + </div> | |
| 40 | + </> | |
| 41 | + ); | |
| 42 | +}; | |
| 43 | + | |
| 44 | +export default Image; | ... | ... |
packages/hr/map_select/index.d.ts
0 → 100644
packages/hr/map_select/index.less
0 → 100644
packages/hr/map_select/index.tsx
0 → 100644
| 1 | +import React, { useState, useEffect } from 'react'; | |
| 2 | +import { Input, Space, Select, Divider, Cascader } from 'antd'; | |
| 3 | +import AMapLoader from '@amap/amap-jsapi-loader'; | |
| 4 | +import './index.less'; | |
| 5 | +import areaData from '@/utils/areaData'; | |
| 6 | +import { getAreaData, getAreaNamesByCodes } from '@/utils/format'; | |
| 7 | +import $ from 'jquery'; | |
| 8 | +// 获取平铺的地区数据 | |
| 9 | +const areaDatas = getAreaData(areaData); | |
| 10 | + | |
| 11 | +declare global { | |
| 12 | + interface Window { | |
| 13 | + _AMapSecurityConfig: any; | |
| 14 | + AMapUI: any; | |
| 15 | + AMap: any; | |
| 16 | + } | |
| 17 | +} | |
| 18 | + | |
| 19 | +const { Search } = Input; | |
| 20 | + | |
| 21 | +window._AMapSecurityConfig = { | |
| 22 | + securityJsCode: 'f4e73616d1028a16dad9556a0d493571', | |
| 23 | +}; | |
| 24 | + | |
| 25 | +const MapRender: React.FC = (props: any) => { | |
| 26 | + const [map, setMap] = useState<any>(null); | |
| 27 | + const [marker, setMarker] = useState<any>(null); | |
| 28 | + const [infoWindow, setInfoWindow] = useState<any>(null); | |
| 29 | + const [mapVisble, setMapVisble] = useState<boolean>(true); | |
| 30 | + const { poiPicker, setPoiPicker, open, setSelectCode, addressName } = props; | |
| 31 | + const initMap = async () => { | |
| 32 | + const AMap = await AMapLoader.load({ | |
| 33 | + key: '6b7ba7695c5c2c76d10610a3be45a0f1', // 申请好的Web端开发者Key,首次调用 load 时必填 | |
| 34 | + version: '1.4.15', // 指定要加载的 JS API 的版本,缺省时默认为 1.4.15 | |
| 35 | + plugins: ['AMap.Scale'], // 需要使用的的插件列表,如比例尺'AMap.Scale'等 | |
| 36 | + AMapUI: { | |
| 37 | + version: '1.0', | |
| 38 | + }, | |
| 39 | + }); | |
| 40 | + | |
| 41 | + if (AMap) { | |
| 42 | + if (!map) { | |
| 43 | + const tempMap = new AMap.Map(addressName + 'map'); | |
| 44 | + | |
| 45 | + setMap(tempMap); | |
| 46 | + tempMap.addControl(new AMap.Scale()); | |
| 47 | + } | |
| 48 | + | |
| 49 | + if (!marker) { | |
| 50 | + setMarker(new AMap.Marker()); | |
| 51 | + } | |
| 52 | + if (!infoWindow) { | |
| 53 | + setInfoWindow( | |
| 54 | + new AMap.InfoWindow({ | |
| 55 | + offset: new AMap.Pixel(0, -20), | |
| 56 | + }), | |
| 57 | + ); | |
| 58 | + } | |
| 59 | + } | |
| 60 | + window.AMapUI.setDomLibrary($); | |
| 61 | + if (window.AMapUI) { | |
| 62 | + window.AMapUI.loadUI(['misc/PoiPicker', 'misc/PositionPicker'], function (PoiPicker: any) { | |
| 63 | + if (!poiPicker) { | |
| 64 | + const tempPoiPicker = new PoiPicker({ | |
| 65 | + //city:'北京', | |
| 66 | + input: addressName + 'search', | |
| 67 | + }); | |
| 68 | + setPoiPicker(tempPoiPicker); | |
| 69 | + } | |
| 70 | + }); | |
| 71 | + } | |
| 72 | + }; | |
| 73 | + | |
| 74 | + const initWindowMap = async () => { | |
| 75 | + if (!map) { | |
| 76 | + const tempMap = new window.AMap.Map(addressName + 'map', { | |
| 77 | + zoom: 10, | |
| 78 | + }); | |
| 79 | + setMap(tempMap); | |
| 80 | + } | |
| 81 | + if (!marker) { | |
| 82 | + // @ts-ignore | |
| 83 | + setMarker(new window.AMap.Marker()); | |
| 84 | + } | |
| 85 | + if (!infoWindow) { | |
| 86 | + setInfoWindow( | |
| 87 | + new window.AMap.InfoWindow({ | |
| 88 | + offset: new window.AMap.Pixel(0, -20), | |
| 89 | + }), | |
| 90 | + ); | |
| 91 | + } | |
| 92 | + if (window.AMapUI) { | |
| 93 | + window.AMapUI.loadUI(['misc/PoiPicker', 'misc/PositionPicker'], function (PoiPicker: any) { | |
| 94 | + if (!poiPicker) { | |
| 95 | + const tempPoiPicker = new PoiPicker({ | |
| 96 | + //city:'北京', | |
| 97 | + input: addressName + 'search', | |
| 98 | + }); | |
| 99 | + setPoiPicker(tempPoiPicker); | |
| 100 | + } | |
| 101 | + }); | |
| 102 | + } | |
| 103 | + }; | |
| 104 | + | |
| 105 | + useEffect(() => { | |
| 106 | + const search = document.getElementById(addressName + 'search'); | |
| 107 | + search?.focus(); | |
| 108 | + }, [document.getElementById(addressName + 'search')]); | |
| 109 | + | |
| 110 | + useEffect(() => { | |
| 111 | + if (document.getElementById(addressName + 'map')) { | |
| 112 | + if (window.AMap) { | |
| 113 | + initWindowMap(); | |
| 114 | + } else { | |
| 115 | + initMap(); | |
| 116 | + } | |
| 117 | + } else { | |
| 118 | + setMapVisble(!mapVisble); | |
| 119 | + } | |
| 120 | + if (poiPicker) { | |
| 121 | + //初始化poiPicker | |
| 122 | + poiPicker.on('poiPicked', function (poiResult: any) { | |
| 123 | + const { onChange, setOpen } = props; | |
| 124 | + const poi = poiResult.item; | |
| 125 | + const { adcode }: { adcode: string } = poi; | |
| 126 | + let provideCode = adcode.substring(0, 2) + '0000'; | |
| 127 | + let cityCode = adcode.substring(0, 4) + '00'; | |
| 128 | + let areaCode = adcode; | |
| 129 | + map.clearMap(); | |
| 130 | + marker.setMap(map); | |
| 131 | + infoWindow.setMap(map); | |
| 132 | + | |
| 133 | + marker.setPosition(poi.location); | |
| 134 | + infoWindow.setPosition(poi.location); | |
| 135 | + | |
| 136 | + // 根据code获取省市区名称 | |
| 137 | + const info = getAreaNamesByCodes([provideCode, cityCode, areaCode], areaDatas); | |
| 138 | + // infoWindow.setContent( | |
| 139 | + // 'POI信息: <pre>' + JSON.stringify(info, null, 2) + '</pre>', | |
| 140 | + // ); | |
| 141 | + // infoWindow.open(map, marker.getPosition()); | |
| 142 | + map.setCenter(marker.getPosition()); | |
| 143 | + | |
| 144 | + const { location = {}, address = '' } = poi; | |
| 145 | + const { lng, lat } = location; | |
| 146 | + const [provide, city, district] = info; | |
| 147 | + | |
| 148 | + if (onChange) { | |
| 149 | + // 给表单组件赋值 | |
| 150 | + onChange({ | |
| 151 | + address, | |
| 152 | + province_code: provideCode, | |
| 153 | + city_code: cityCode, | |
| 154 | + district_code: areaCode, | |
| 155 | + provide, | |
| 156 | + city, | |
| 157 | + district, | |
| 158 | + lng: lng + '', | |
| 159 | + ltg: lat + '', | |
| 160 | + }); | |
| 161 | + } | |
| 162 | + setSelectCode([provideCode, cityCode, areaCode]); | |
| 163 | + setOpen(false); | |
| 164 | + }); | |
| 165 | + } | |
| 166 | + }, [open, poiPicker, mapVisble]); | |
| 167 | + | |
| 168 | + return <div className="map" id={addressName + 'map'}></div>; | |
| 169 | +}; | |
| 170 | + | |
| 171 | +const MapSelect = (props: any) => { | |
| 172 | + const { value = {}, addressName = '', cascaderStyle = {}, selectStyle = {} } = props; | |
| 173 | + const [poiPicker, setPoiPicker] = useState<any>(null); | |
| 174 | + const [open, setOpen] = useState(false); | |
| 175 | + const [searchValue, setSearchValue] = useState(''); | |
| 176 | + const [selectCode, setSelectCode] = useState<any>([]); | |
| 177 | + const { province_code, city_code, district_code } = value; | |
| 178 | + | |
| 179 | + useEffect(() => { | |
| 180 | + if (province_code && city_code && district_code) { | |
| 181 | + setSelectCode([province_code, city_code, district_code]); | |
| 182 | + } | |
| 183 | + }, [district_code]); | |
| 184 | + | |
| 185 | + useEffect(() => { | |
| 186 | + setSearchValue(value?.address); | |
| 187 | + }, [value?.address]); | |
| 188 | + | |
| 189 | + return ( | |
| 190 | + <Space.Compact style={{ width: '100%' }}> | |
| 191 | + <Cascader | |
| 192 | + options={areaData} | |
| 193 | + style={cascaderStyle} | |
| 194 | + value={selectCode} | |
| 195 | + placeholder="省/市/区" | |
| 196 | + disabled | |
| 197 | + /> | |
| 198 | + <Select | |
| 199 | + open={open} | |
| 200 | + value={value?.address} | |
| 201 | + dropdownMatchSelectWidth={false} | |
| 202 | + style={selectStyle} | |
| 203 | + placeholder="请输入关键字选取地点" | |
| 204 | + onDropdownVisibleChange={(isopen) => { | |
| 205 | + if (isopen) { | |
| 206 | + setOpen(true); | |
| 207 | + return; | |
| 208 | + } | |
| 209 | + setOpen(false); | |
| 210 | + }} | |
| 211 | + dropdownRender={() => ( | |
| 212 | + <div style={{ overflow: 'auto' }}> | |
| 213 | + <Search | |
| 214 | + id={addressName + 'search'} | |
| 215 | + placeholder="请输入关键字选取地点" | |
| 216 | + value={searchValue} | |
| 217 | + onPressEnter={(e: any) => { | |
| 218 | + if (poiPicker && e.target.value) { | |
| 219 | + poiPicker.searchByKeyword(e.target.value); | |
| 220 | + } | |
| 221 | + setSearchValue(e.target.value); | |
| 222 | + }} | |
| 223 | + onChange={(e) => { | |
| 224 | + setSearchValue(e.target.value); | |
| 225 | + }} | |
| 226 | + // onSearch={(value) => {}} | |
| 227 | + /> | |
| 228 | + <Divider style={{ margin: '8px 0' }} /> | |
| 229 | + <MapRender | |
| 230 | + {...props} | |
| 231 | + open={open} | |
| 232 | + setOpen={setOpen} | |
| 233 | + poiPicker={poiPicker} | |
| 234 | + setPoiPicker={setPoiPicker} | |
| 235 | + setSelectCode={setSelectCode} | |
| 236 | + /> | |
| 237 | + </div> | |
| 238 | + )} | |
| 239 | + /> | |
| 240 | + </Space.Compact> | |
| 241 | + ); | |
| 242 | +}; | |
| 243 | + | |
| 244 | +export default MapSelect; | ... | ... |
packages/hr/modal_confirm/index.d.ts
0 → 100644
packages/hr/modal_confirm/index.tsx
0 → 100644
| 1 | +import { Modal, ModalFuncProps } from 'antd'; | |
| 2 | +import { ExclamationCircleFilled } from '@ant-design/icons'; | |
| 3 | +const { confirm } = Modal; | |
| 4 | + | |
| 5 | +const showPropsConfirm = (props: ModalFuncProps) => { | |
| 6 | + const { title, icon, content, okText, okType, okButtonProps, cancelText, onOk, onCancel } = props; | |
| 7 | + | |
| 8 | + confirm({ | |
| 9 | + title: title, | |
| 10 | + icon: icon || <ExclamationCircleFilled />, | |
| 11 | + content: content, | |
| 12 | + okText: okText, | |
| 13 | + okType: okType, | |
| 14 | + okButtonProps: okButtonProps, //是个对象,其中disabled属性就是在这里设置 | |
| 15 | + cancelText: cancelText, | |
| 16 | + onOk: onOk, | |
| 17 | + onCancel: onCancel, | |
| 18 | + }); | |
| 19 | +}; | |
| 20 | + | |
| 21 | +export default showPropsConfirm; | ... | ... |
packages/hr/pagination/index.less
0 → 100644
packages/hr/pagination/pagination.tsx
0 → 100644
| 1 | +import { Pagination } from 'antd'; | |
| 2 | + | |
| 3 | +interface paginationProps { | |
| 4 | + [props: string]: any; | |
| 5 | + onChange: Function; | |
| 6 | +} | |
| 7 | + | |
| 8 | +const PaginationWrap = (props: paginationProps) => { | |
| 9 | + const { pagination, changePagination, onChange, ...rest } = props; | |
| 10 | + | |
| 11 | + const { offset, limit = 10, total_count } = pagination; | |
| 12 | + | |
| 13 | + // 分页改变 | |
| 14 | + const onPaginationChange = (page: number, limit: number) => { | |
| 15 | + changePagination({ | |
| 16 | + ...pagination, | |
| 17 | + offset: (page - 1) * (limit || 10), | |
| 18 | + limit: limit, | |
| 19 | + }); | |
| 20 | + onChange({ | |
| 21 | + offset: (page - 1) * (limit || 10), | |
| 22 | + limit: limit, | |
| 23 | + }); | |
| 24 | + }; | |
| 25 | + | |
| 26 | + return ( | |
| 27 | + <Pagination | |
| 28 | + style={{ textAlign: 'right', marginTop: '16px' }} | |
| 29 | + current={(offset && Math.floor(offset / limit) + 1) || 1} | |
| 30 | + pageSize={limit ? limit : 10} | |
| 31 | + total={total_count} | |
| 32 | + showTotal={(total) => `共 ${total} 条`} | |
| 33 | + showSizeChanger={false} | |
| 34 | + onChange={(page: number, pageSize: number) => { | |
| 35 | + onPaginationChange(page, pageSize); | |
| 36 | + }} | |
| 37 | + {...rest} | |
| 38 | + // pageSizeOptions={['10', '25', '50', '100']} | |
| 39 | + /> | |
| 40 | + ); | |
| 41 | +}; | |
| 42 | + | |
| 43 | +export default PaginationWrap; | ... | ... |
packages/hr/search/index.less
0 → 100644
| 1 | +.search-component { | |
| 2 | + display: flex; | |
| 3 | + align-items: center; | |
| 4 | + // justify-content: space-between; | |
| 5 | + height: 72px; | |
| 6 | + background: #ffffff; | |
| 7 | + border-radius: 0px 0px 8px 8px; | |
| 8 | + padding: 30px; | |
| 9 | + margin-bottom: 20px; | |
| 10 | + justify-content: space-between; | |
| 11 | +} | |
| 12 | + | |
| 13 | +.left-section { | |
| 14 | + display: flex; | |
| 15 | + align-items: center; | |
| 16 | + overflow: hidden; | |
| 17 | + .ant-formily-item-feedback-layout-loose { | |
| 18 | + margin-bottom: 0 !important; | |
| 19 | + } | |
| 20 | +} | |
| 21 | + | |
| 22 | +.left-item { | |
| 23 | + margin-bottom: 5px; | |
| 24 | +} | |
| 25 | + | |
| 26 | +.right-section { | |
| 27 | + display: flex; | |
| 28 | + align-items: center; | |
| 29 | + position: relative; | |
| 30 | + margin-left: 10px; | |
| 31 | + .section-search { | |
| 32 | + .ant-formily-item-feedback-layout-loose { | |
| 33 | + margin-bottom: 0 !important; | |
| 34 | + } | |
| 35 | + } | |
| 36 | +} | |
| 37 | + | |
| 38 | +.more-categories-button { | |
| 39 | + margin-left: 10px; | |
| 40 | +} | |
| 41 | + | |
| 42 | +.btn { | |
| 43 | + display: flex; | |
| 44 | + align-items: center; | |
| 45 | + justify-content: flex-end; | |
| 46 | + margin-top: 20px; | |
| 47 | +} | |
| 48 | + | |
| 49 | +.popups { | |
| 50 | + padding: 20px; | |
| 51 | + width: 332px; | |
| 52 | + max-width: 500px; | |
| 53 | + | |
| 54 | + .ant-tooltip-inner { | |
| 55 | + padding: 20px; | |
| 56 | + right: 100px; | |
| 57 | + } | |
| 58 | +} | |
| 59 | + | |
| 60 | +.more { | |
| 61 | + margin-left: 20px; | |
| 62 | + font-size: 14px; | |
| 63 | + font-weight: 400; | |
| 64 | + color: #303133; | |
| 65 | +} | |
| 66 | + | |
| 67 | +.img { | |
| 68 | + margin-left: 7px; | |
| 69 | +} | ... | ... |
packages/hr/search/search.tsx
0 → 100644
| 1 | +import { ReactElement, useEffect, useMemo, useState } from 'react'; | |
| 2 | +import { Space, Button, Tooltip, Tag } from 'antd'; | |
| 3 | +import './index.less'; | |
| 4 | +import { FormProvider, VoidField } from '@formily/react'; | |
| 5 | +import { createForm, onFieldValueChange, Field } from '@formily/core'; | |
| 6 | +import { ossAddress } from '@/utils/ossAddress'; | |
| 7 | +import { useDebounce } from '@/utils/format'; | |
| 8 | +import moment from 'moment'; | |
| 9 | +export interface SearchWrapProps { | |
| 10 | + leftSectionComponents?: ReactElement[]; | |
| 11 | + searchInput?: ReactElement; | |
| 12 | + popupContent?: ReactElement[]; | |
| 13 | + searchChange?: Function; | |
| 14 | + getFormInstance?: Function; | |
| 15 | + moreCategories?: Boolean; | |
| 16 | + ProForm?: any; | |
| 17 | +} | |
| 18 | + | |
| 19 | +interface SearchChooseProps { | |
| 20 | + name: string; | |
| 21 | + title: string; | |
| 22 | + value: string; | |
| 23 | + path: any; | |
| 24 | + address: any; | |
| 25 | +} | |
| 26 | + | |
| 27 | +const _getValueLable = (field: Field) => { | |
| 28 | + const { title, value, dataSource, props, path, address, componentProps } = field; | |
| 29 | + const { name } = props; | |
| 30 | + | |
| 31 | + const { componenttypename, format } = componentProps; | |
| 32 | + const labelMap: any = { | |
| 33 | + name, | |
| 34 | + path, | |
| 35 | + address, | |
| 36 | + title: title ? title : '', | |
| 37 | + }; | |
| 38 | + const getValue = (key: any) => { | |
| 39 | + const dataSourceMap: Record<string, string> = {}; | |
| 40 | + dataSource.map((item) => { | |
| 41 | + if (item && item.value) { | |
| 42 | + dataSourceMap[item.value] = item.label; | |
| 43 | + } | |
| 44 | + }); | |
| 45 | + return dataSourceMap[key]; | |
| 46 | + }; | |
| 47 | + switch (componenttypename) { | |
| 48 | + case 'Radio.Group': | |
| 49 | + labelMap.value = getValue(value); | |
| 50 | + break; | |
| 51 | + case 'Select': | |
| 52 | + console.log(value, 'selectValue'); | |
| 53 | + | |
| 54 | + case 'Checkbox.Group': | |
| 55 | + const valArray: any[] = []; | |
| 56 | + if (value && value.length > 0 && typeof value != 'string') { | |
| 57 | + value.map((val: any) => { | |
| 58 | + valArray.push(getValue(val)); | |
| 59 | + }); | |
| 60 | + labelMap.value = valArray.join(','); | |
| 61 | + } else { | |
| 62 | + labelMap.value = getValue(value); | |
| 63 | + } | |
| 64 | + break; | |
| 65 | + case 'Cascader': | |
| 66 | + break; | |
| 67 | + case 'DatePicker': | |
| 68 | + case 'MapSelect': | |
| 69 | + case 'InputNumber': | |
| 70 | + case 'Input': | |
| 71 | + case 'Input.TextArea': | |
| 72 | + case 'UploadFiles': | |
| 73 | + labelMap.value = value; | |
| 74 | + break; | |
| 75 | + case 'DatePicker.RangePicker': | |
| 76 | + if (value && value.length > 0) { | |
| 77 | + const start = value[0] ? moment(value[0] * 1000).format(format[0]) : ''; | |
| 78 | + const end = value[1] ? moment(value[1] * 1000).format(format[1]) : ''; | |
| 79 | + labelMap.value = `${start}~${end}`; | |
| 80 | + } else { | |
| 81 | + labelMap.value = undefined; | |
| 82 | + } | |
| 83 | + break; | |
| 84 | + default: | |
| 85 | + labelMap.value = getValue(value); | |
| 86 | + } | |
| 87 | + return labelMap; | |
| 88 | +}; | |
| 89 | + | |
| 90 | +const _getPopupValuesLabel = (field: any, searchLabels: SearchChooseProps[]) => { | |
| 91 | + const { props } = field; | |
| 92 | + const { name } = props; | |
| 93 | + | |
| 94 | + const searchLableMap: any = {}; | |
| 95 | + const changeLabel = _getValueLable(field); | |
| 96 | + if (searchLabels.length > 0) { | |
| 97 | + searchLabels.map((label) => { | |
| 98 | + if (name == label.name) { | |
| 99 | + if (changeLabel && changeLabel.value) { | |
| 100 | + searchLableMap[label.name] = changeLabel; | |
| 101 | + } else { | |
| 102 | + delete searchLableMap[label.name]; | |
| 103 | + } | |
| 104 | + } else { | |
| 105 | + searchLableMap[label.name] = label; | |
| 106 | + } | |
| 107 | + }); | |
| 108 | + } | |
| 109 | + if (changeLabel && changeLabel.value) { | |
| 110 | + searchLableMap[name] = changeLabel; | |
| 111 | + } | |
| 112 | + return searchLableMap; | |
| 113 | +}; | |
| 114 | + | |
| 115 | +const SearchWrap: React.FC<SearchWrapProps> = (props) => { | |
| 116 | + const { | |
| 117 | + leftSectionComponents = [], | |
| 118 | + searchInput, | |
| 119 | + popupContent, | |
| 120 | + searchChange, | |
| 121 | + moreCategories = false, | |
| 122 | + ProForm, | |
| 123 | + getFormInstance, | |
| 124 | + } = props; | |
| 125 | + const [isPopupVisible, setPopupVisible] = useState(false); | |
| 126 | + const [open, setOpen] = useState(false); | |
| 127 | + const [searchLabels, setSearchLabels] = useState<SearchChooseProps[]>([]); | |
| 128 | + const [showPopupValue, setShowPopupValue] = useState<boolean>(false); | |
| 129 | + const [centerWidth, setCenterWidth] = useState<number>(0); | |
| 130 | + const valueChange = useDebounce((value: any) => { | |
| 131 | + searchChange && searchChange(value); | |
| 132 | + }, 800); | |
| 133 | + | |
| 134 | + const form = ProForm ? ProForm : useMemo(() => createForm(), []); | |
| 135 | + | |
| 136 | + useEffect(() => { | |
| 137 | + if (searchLabels && searchLabels.length === 0) { | |
| 138 | + setShowPopupValue(false); | |
| 139 | + } | |
| 140 | + }, [searchLabels]); | |
| 141 | + | |
| 142 | + useEffect(() => { | |
| 143 | + form.addEffects('popup_search', () => { | |
| 144 | + onFieldValueChange('*', (field) => { | |
| 145 | + const { | |
| 146 | + address: { entire = '' }, | |
| 147 | + } = field; | |
| 148 | + if ((entire as string).indexOf('popup') === -1) { | |
| 149 | + searchChange && valueChange(form.values); | |
| 150 | + } | |
| 151 | + if ((entire as string).indexOf('popup') > -1) { | |
| 152 | + setSearchLabels((searchLabels) => { | |
| 153 | + const labelValues = _getPopupValuesLabel(field, searchLabels); | |
| 154 | + | |
| 155 | + return Object.values(labelValues); | |
| 156 | + }); | |
| 157 | + } | |
| 158 | + }); | |
| 159 | + }); | |
| 160 | + }, [ProForm]); | |
| 161 | + | |
| 162 | + useEffect(() => { | |
| 163 | + getFormInstance && getFormInstance(form); | |
| 164 | + }, [getFormInstance]); | |
| 165 | + | |
| 166 | + useEffect(() => { | |
| 167 | + const leftWidth = document.querySelector('.left-section')?.clientWidth || 0; | |
| 168 | + const rightWidth = document.querySelector('.right-section')?.clientWidth || 0; | |
| 169 | + const clientWidth = document.querySelector('.ant-layout-content')?.clientWidth || 0; | |
| 170 | + const width = clientWidth - leftWidth - rightWidth - 85; | |
| 171 | + setCenterWidth(width); | |
| 172 | + }, []); | |
| 173 | + | |
| 174 | + const togglePopup = () => { | |
| 175 | + setPopupVisible(!isPopupVisible); | |
| 176 | + setOpen(!open); | |
| 177 | + }; | |
| 178 | + | |
| 179 | + const submit = () => { | |
| 180 | + const { values: allValue } = form; | |
| 181 | + searchChange && searchChange(allValue); | |
| 182 | + togglePopup(); | |
| 183 | + setShowPopupValue(true); | |
| 184 | + }; | |
| 185 | + | |
| 186 | + const closeLabel = (label: SearchChooseProps) => { | |
| 187 | + const { path } = label; | |
| 188 | + const field = form.query(path).take(); | |
| 189 | + if (field && field.setValue) { | |
| 190 | + field.setValue(undefined); | |
| 191 | + } | |
| 192 | + const { values: allValue } = form; | |
| 193 | + searchChange && searchChange(allValue); | |
| 194 | + }; | |
| 195 | + | |
| 196 | + const handleCancle = () => { | |
| 197 | + form.reset('popup.*'); | |
| 198 | + const { values: allValue } = form; | |
| 199 | + searchChange && searchChange(allValue); | |
| 200 | + togglePopup(); | |
| 201 | + }; | |
| 202 | + | |
| 203 | + const text = () => { | |
| 204 | + return ( | |
| 205 | + <div> | |
| 206 | + <VoidField name="popup"> | |
| 207 | + {popupContent && | |
| 208 | + popupContent.map((item, index) => { | |
| 209 | + return <div key={index}>{item}</div>; | |
| 210 | + })} | |
| 211 | + </VoidField> | |
| 212 | + <div className="btn"> | |
| 213 | + <Button onClick={handleCancle}>重置</Button> | |
| 214 | + <Button style={{ marginLeft: 10 }} type="primary" onClick={submit}> | |
| 215 | + 确定 | |
| 216 | + </Button> | |
| 217 | + </div> | |
| 218 | + </div> | |
| 219 | + ); | |
| 220 | + }; | |
| 221 | + | |
| 222 | + const labels = () => { | |
| 223 | + // 左边显示操作6个搜索条件 | |
| 224 | + const len = (leftSectionComponents && leftSectionComponents.length - 1) || 0; | |
| 225 | + return ( | |
| 226 | + <div style={{ width: centerWidth, overflow: 'hidden' }}> | |
| 227 | + {showPopupValue && | |
| 228 | + searchLabels && | |
| 229 | + searchLabels.slice(0, 6 - len).map((item, index) => { | |
| 230 | + return ( | |
| 231 | + <Tag key={item.name} closable onClose={() => closeLabel(item)}> | |
| 232 | + {item.value} | |
| 233 | + </Tag> | |
| 234 | + ); | |
| 235 | + })} | |
| 236 | + </div> | |
| 237 | + ); | |
| 238 | + }; | |
| 239 | + | |
| 240 | + return ( | |
| 241 | + <FormProvider form={form}> | |
| 242 | + <div className="search-component"> | |
| 243 | + <Space className="left-section"> | |
| 244 | + {leftSectionComponents.slice(0, 6).map((component, index) => ( | |
| 245 | + <div key={index} className="left-section-component"> | |
| 246 | + {component} | |
| 247 | + </div> | |
| 248 | + ))} | |
| 249 | + {labels()} | |
| 250 | + </Space> | |
| 251 | + <div className="right-section"> | |
| 252 | + <div className="section-search">{searchInput}</div> | |
| 253 | + {moreCategories && ( | |
| 254 | + <Tooltip | |
| 255 | + placement="bottomRight" | |
| 256 | + overlayStyle={{ padding: 0 }} | |
| 257 | + color="#fff" | |
| 258 | + title={text} | |
| 259 | + overlayClassName="popups" | |
| 260 | + trigger="click" | |
| 261 | + zIndex={10} | |
| 262 | + open={open} | |
| 263 | + getPopupContainer={(triggerNode) => triggerNode.parentNode as any} | |
| 264 | + > | |
| 265 | + <div | |
| 266 | + style={{ cursor: 'pointer' }} | |
| 267 | + onClick={() => { | |
| 268 | + setOpen(!open); | |
| 269 | + }} | |
| 270 | + > | |
| 271 | + <span className="more">更多筛选</span> | |
| 272 | + <img className="img" src={ossAddress['searchMore']}></img> | |
| 273 | + </div> | |
| 274 | + </Tooltip> | |
| 275 | + )} | |
| 276 | + </div> | |
| 277 | + </div> | |
| 278 | + </FormProvider> | |
| 279 | + ); | |
| 280 | +}; | |
| 281 | + | |
| 282 | +export default SearchWrap; | ... | ... |
packages/hr/table/index.less
0 → 100644
| 1 | +.table_components { | |
| 2 | + .table { | |
| 3 | + :global { | |
| 4 | + .ant-table-thead { | |
| 5 | + background: #f2f3f5; | |
| 6 | + font-size: 14px; | |
| 7 | + font-weight: 500; | |
| 8 | + color: #1d2129; | |
| 9 | + height: 54px; | |
| 10 | + | |
| 11 | + .ant-table-cell { | |
| 12 | + &::before { | |
| 13 | + width: 0 !important; | |
| 14 | + } | |
| 15 | + } | |
| 16 | + } | |
| 17 | + | |
| 18 | + .ant-table-tbody { | |
| 19 | + background-color: #fff; | |
| 20 | + | |
| 21 | + .ant-table-row { | |
| 22 | + font-size: 14px; | |
| 23 | + font-weight: 400; | |
| 24 | + color: #303133; | |
| 25 | + } | |
| 26 | + } | |
| 27 | + } | |
| 28 | + } | |
| 29 | +} | ... | ... |
packages/hr/table/table.tsx
0 → 100644
| 1 | +import { Table } from 'antd'; | |
| 2 | +import type { TableProps } from 'antd'; | |
| 3 | +import React, { useEffect, useState } from 'react'; | |
| 4 | +import moment from 'moment'; | |
| 5 | + | |
| 6 | +import s from './index.less'; | |
| 7 | + | |
| 8 | +export interface tableProps extends TableProps<Record<string, number>> { | |
| 9 | + tableHeader?: React.ReactNode; | |
| 10 | +} | |
| 11 | + | |
| 12 | +export const dealNumber = (val: number) => { | |
| 13 | + if (val && val != null) { | |
| 14 | + let money = String(val); | |
| 15 | + let left = money.split('.')[0], | |
| 16 | + right = money.split('.')[1]; | |
| 17 | + right = right ? (right.length >= 2 ? '.' + right.substr(0, 2) : '.' + right + '0') : '.00'; | |
| 18 | + let temp: any = left | |
| 19 | + .split('') | |
| 20 | + .reverse() | |
| 21 | + .join('') | |
| 22 | + .match(/(\d{1,3})/g); // 将字符串顺序反转 :字符串-->数组 --> 顺序反转-->字符串 | |
| 23 | + return (Number(money) < 0 ? '-' : '') + temp.join(',').split('').reverse().join('') + right; | |
| 24 | + } else if (val === 0) { | |
| 25 | + return '0.00'; | |
| 26 | + } else { | |
| 27 | + return ''; | |
| 28 | + } | |
| 29 | +}; | |
| 30 | + | |
| 31 | +const TableWrap: React.FC<tableProps> = (props) => { | |
| 32 | + const { tableHeader, columns, scroll } = props; | |
| 33 | + const [newScroll, setNewScroll] = useState<any>(0); | |
| 34 | + const [tableColumns, setTableColumns] = useState<any>([]); | |
| 35 | + | |
| 36 | + const sex = { | |
| 37 | + '1': '男', | |
| 38 | + '2': '女', | |
| 39 | + }; | |
| 40 | + | |
| 41 | + const handleAttr = (item: any) => { | |
| 42 | + const data: Record<string, any> = { | |
| 43 | + date: (text: any) => moment(text).format(item.formatDate || 'YYYY/MM/DD'), | |
| 44 | + unix: (text: any) => moment(text * 1000).format(item.formatDate || 'YYYY/MM/DD'), | |
| 45 | + decimal: (text: any) => text.toFixed(2), | |
| 46 | + money: (text: any) => dealNumber(text), | |
| 47 | + sex: (text: keyof typeof sex) => sex[text] || '', | |
| 48 | + }; | |
| 49 | + | |
| 50 | + if (data[item.attr]) item.render = data[item.attr]; | |
| 51 | + }; | |
| 52 | + | |
| 53 | + useEffect(() => { | |
| 54 | + let calculateScroll = 0; | |
| 55 | + columns?.map((item: any) => { | |
| 56 | + // 处理attr | |
| 57 | + if (item.attr) { | |
| 58 | + handleAttr(item); | |
| 59 | + } | |
| 60 | + // 设置ellipsis | |
| 61 | + item.ellipsis = true; | |
| 62 | + // 计算scroll | |
| 63 | + if (item.width) calculateScroll += item.width; | |
| 64 | + }); | |
| 65 | + | |
| 66 | + setTableColumns(columns); | |
| 67 | + setNewScroll(calculateScroll); | |
| 68 | + }, [columns]); | |
| 69 | + | |
| 70 | + return ( | |
| 71 | + <div className={s.table_components}> | |
| 72 | + {tableHeader && <div className={s.table_header}>{tableHeader}</div>} | |
| 73 | + <Table | |
| 74 | + className={s.table} | |
| 75 | + pagination={false} | |
| 76 | + rowKey={(row) => row.id as unknown as string} | |
| 77 | + scroll={scroll || { x: newScroll }} | |
| 78 | + dataSource={props.dataSource || []} | |
| 79 | + columns={tableColumns} | |
| 80 | + {...props} | |
| 81 | + /> | |
| 82 | + </div> | |
| 83 | + ); | |
| 84 | +}; | |
| 85 | + | |
| 86 | +export default TableWrap; | ... | ... |
packages/hr/tabs/index.tsx
0 → 100644
| 1 | + | |
| 2 | +import { Tabs as MyTabs } from 'antd'; | |
| 3 | +import { TabsProps } from 'antd/lib/Tabs'; | |
| 4 | +import './tabs.less'; | |
| 5 | + | |
| 6 | +const Tabs: React.FC<TabsProps> = (props) => { | |
| 7 | + const { defaultActiveKey, items = [], ...other } = props; | |
| 8 | + const defaultKey = defaultActiveKey || items[0].key; | |
| 9 | + | |
| 10 | + return ( | |
| 11 | + <div className={'tabs_warp'}> | |
| 12 | + <MyTabs defaultActiveKey={defaultKey} items={items} {...other} /> | |
| 13 | + </div> | |
| 14 | + ); | |
| 15 | +}; | |
| 16 | + | |
| 17 | +export default Tabs; | ... | ... |
packages/hr/tabs/tabs.less
0 → 100644
| 1 | +.tabs_warp { | |
| 2 | + .ant-tabs { | |
| 3 | + background-color: #fff; | |
| 4 | + line-height: 20px; | |
| 5 | + font-size: 16px; | |
| 6 | + font-weight: 400; | |
| 7 | + border-radius: 8px 8px 0 0; | |
| 8 | + | |
| 9 | + .ant-tabs-nav { | |
| 10 | + margin: 0; | |
| 11 | + | |
| 12 | + &::before { | |
| 13 | + border-color: #f2f3f5; | |
| 14 | + } | |
| 15 | + | |
| 16 | + .ant-tabs-nav-wrap { | |
| 17 | + padding: 0 32px; | |
| 18 | + | |
| 19 | + .ant-tabs-tab { | |
| 20 | + padding: 31px 0 21px 0; | |
| 21 | + | |
| 22 | + .ant-tabs-tab-btn { | |
| 23 | + font-size: 16px; | |
| 24 | + color: #606266; | |
| 25 | + | |
| 26 | + &::after { | |
| 27 | + content: ' '; | |
| 28 | + width: 20px; | |
| 29 | + position: absolute; | |
| 30 | + left: calc(~'50% - 10px'); | |
| 31 | + bottom: 1px; | |
| 32 | + border-bottom: 2px solid transparent; | |
| 33 | + box-shadow: 0px 4px 10px 0px rgba(78, 89, 105, 0.06); | |
| 34 | + border-radius: 2px; | |
| 35 | + transition: 0.4s; | |
| 36 | + transform: scaleX(0); | |
| 37 | + } | |
| 38 | + } | |
| 39 | + } | |
| 40 | + | |
| 41 | + .ant-tabs-tab-active { | |
| 42 | + .ant-tabs-tab-btn { | |
| 43 | + font-size: 16px; | |
| 44 | + color: #1d2129 !important; | |
| 45 | + | |
| 46 | + &::after { | |
| 47 | + border-color: #1677ff; | |
| 48 | + transform: scaleX(1); | |
| 49 | + } | |
| 50 | + } | |
| 51 | + } | |
| 52 | + | |
| 53 | + .ant-tabs-ink-bar { | |
| 54 | + display: none; | |
| 55 | + } | |
| 56 | + } | |
| 57 | + } | |
| 58 | + } | |
| 59 | +} | ... | ... |
packages/hr/typings.ts
0 → 100644
| 1 | +import { ReactElement } from 'react'; | |
| 2 | + | |
| 3 | +export interface FieldProps { | |
| 4 | + name: string | [string, string]; | |
| 5 | + title?: string | ReactElement; | |
| 6 | + validator?: any; | |
| 7 | + [props: string]: any; | |
| 8 | + decoratorProps?: { | |
| 9 | + [props: string]: any; | |
| 10 | + }; | |
| 11 | + componentProps?: { | |
| 12 | + [props: string]: any; | |
| 13 | + }; | |
| 14 | +} | ... | ... |
packages/hr/upload_file/index.less
0 → 100644
packages/hr/upload_file/index.tsx
0 → 100644
| 1 | +import React, { useState, useEffect } from 'react'; | |
| 2 | +import { message, Upload, notification, Button } from 'antd'; | |
| 3 | +import { uploadToken } from '@/services/upload'; | |
| 4 | +import { UploadFile, UploadListType, UploadProps } from 'antd/lib/upload/interface'; | |
| 5 | +import { UploadOutlined } from '@ant-design/icons'; | |
| 6 | +import cx from 'classnames'; | |
| 7 | + | |
| 8 | +interface UploadFileProps { | |
| 9 | + accept?: string; | |
| 10 | + listType?: UploadListType; | |
| 11 | + Callback?: (e: any) => void; | |
| 12 | + num?: number; | |
| 13 | + action?: string; | |
| 14 | + name?: string | undefined; | |
| 15 | + maxSize?: number; | |
| 16 | + onChange?: (val: any) => void; | |
| 17 | + value?: any; | |
| 18 | + [prop: string]: any; | |
| 19 | +} | |
| 20 | + | |
| 21 | +type Config = Record< | |
| 22 | + | 'action' | |
| 23 | + | 'OSSAccessKeyId' | |
| 24 | + | 'key' | |
| 25 | + | 'policy' | |
| 26 | + | 'signature' | |
| 27 | + | 'callback' | |
| 28 | + | 'x:access_token' | |
| 29 | + // | 'x-oss-security-token' | |
| 30 | + | 'success_action_status', | |
| 31 | + string | |
| 32 | +>; | |
| 33 | + | |
| 34 | +// picture-card | |
| 35 | +const UploadFiles: React.FC<UploadFileProps> = (props) => { | |
| 36 | + const { | |
| 37 | + value, | |
| 38 | + onChange, | |
| 39 | + Callback, | |
| 40 | + accept = '*.*', | |
| 41 | + maxSize = 52428800, | |
| 42 | + children, | |
| 43 | + multiple, | |
| 44 | + object_type, | |
| 45 | + } = props; | |
| 46 | + | |
| 47 | + const [fileLists, setFileList] = useState<Array<UploadFile>>([]); | |
| 48 | + const [config, setConfig] = useState<Config>(); | |
| 49 | + //解决Warning: Can‘t perform a React state update on an unmounted component.问题 | |
| 50 | + const [isFlag, setIsFlag] = useState(true); | |
| 51 | + | |
| 52 | + useEffect(() => { | |
| 53 | + if (!isFlag) { | |
| 54 | + return; | |
| 55 | + } | |
| 56 | + uploadToken({ | |
| 57 | + access_type: 'web_upload', | |
| 58 | + object_type: object_type || 'recruit', | |
| 59 | + }).then((res) => { | |
| 60 | + if (res.errors) { | |
| 61 | + notification.open({ | |
| 62 | + message: '错误', | |
| 63 | + description: res.message, | |
| 64 | + duration: 2, | |
| 65 | + }); | |
| 66 | + } else { | |
| 67 | + const data = { | |
| 68 | + action: `https://${res.bucket}.${res.domain}/`, | |
| 69 | + OSSAccessKeyId: res.access_key_id, | |
| 70 | + key: res.object_path + '/' + '${filename}', | |
| 71 | + policy: res.policy, | |
| 72 | + signature: res.signature, | |
| 73 | + callback: res.callback_body, | |
| 74 | + 'x:access_token': res.callback_token, | |
| 75 | + // 测试环境不能使用 | |
| 76 | + // 'x-oss-security-token': res.security_token, | |
| 77 | + success_action_status: '200', | |
| 78 | + }; | |
| 79 | + | |
| 80 | + setConfig(data); | |
| 81 | + } | |
| 82 | + }); | |
| 83 | + return () => setIsFlag(false); | |
| 84 | + }, []); | |
| 85 | + | |
| 86 | + const onchange = ({ fileList }: { fileList: any }) => { | |
| 87 | + if (fileList[fileList.length - 1].status === 'done') { | |
| 88 | + // setFileList([...fileLists, fileList[fileList.length - 1]]); | |
| 89 | + | |
| 90 | + if (onChange) { | |
| 91 | + onChange([...fileLists, fileList[fileList.length - 1]]); | |
| 92 | + } | |
| 93 | + if (Callback) Callback([...fileLists, fileList[fileList.length - 1]]); | |
| 94 | + } | |
| 95 | + }; | |
| 96 | + | |
| 97 | + const onRemove = (file: UploadFile) => { | |
| 98 | + const files = value.filter((v: { uid: string }) => v.uid !== file.uid); | |
| 99 | + setFileList(files); | |
| 100 | + if (onChange) { | |
| 101 | + onChange(files); | |
| 102 | + } | |
| 103 | + if (Callback) Callback([...files]); | |
| 104 | + }; | |
| 105 | + | |
| 106 | + const onPreview = async (file: any) => { | |
| 107 | + let src = file.url; | |
| 108 | + if (!src) { | |
| 109 | + src = await new Promise((resolve) => { | |
| 110 | + const reader = new FileReader(); | |
| 111 | + | |
| 112 | + reader.readAsDataURL(file.originFileObj); | |
| 113 | + reader.onload = () => resolve(reader.result); | |
| 114 | + }); | |
| 115 | + } | |
| 116 | + const image = new Image(); | |
| 117 | + image.src = src; | |
| 118 | + const imgWindow = window.open(src); | |
| 119 | + imgWindow?.document.write(image.outerHTML); | |
| 120 | + }; | |
| 121 | + | |
| 122 | + const beforeUpload = (file: UploadFile) => { | |
| 123 | + // 如果限制了上传类型 | |
| 124 | + if (accept !== '*.*') { | |
| 125 | + const { name } = file; | |
| 126 | + const accepts = accept.toLowerCase().split(','); | |
| 127 | + const names = name.toLowerCase().split('.'); | |
| 128 | + const extName = names[names.length - 1]; | |
| 129 | + if (!accepts.includes(`.${extName}`)) { | |
| 130 | + message.error('不支持的文件类型!'); | |
| 131 | + return false; | |
| 132 | + } | |
| 133 | + } | |
| 134 | + // 如果限制了上传大小 | |
| 135 | + if (maxSize) { | |
| 136 | + const { size = 0 } = file; | |
| 137 | + | |
| 138 | + if (size > maxSize) { | |
| 139 | + message.error('文件太大了!'); | |
| 140 | + return false; | |
| 141 | + } | |
| 142 | + } | |
| 143 | + return true; | |
| 144 | + }; | |
| 145 | + | |
| 146 | + const uploadProps: UploadProps = { | |
| 147 | + accept: props.accept || '*.*', | |
| 148 | + action: config?.action, | |
| 149 | + name: 'file', | |
| 150 | + data: config, | |
| 151 | + showUploadList: false, | |
| 152 | + multiple: multiple || false, | |
| 153 | + beforeUpload: beforeUpload, | |
| 154 | + onPreview: onPreview, | |
| 155 | + onChange: onchange, | |
| 156 | + onRemove: onRemove, | |
| 157 | + }; | |
| 158 | + | |
| 159 | + return ( | |
| 160 | + <div className={cx('global_upload')}> | |
| 161 | + <Upload {...uploadProps}> | |
| 162 | + {children || <Button icon={<UploadOutlined />}>上传文件</Button>} | |
| 163 | + </Upload> | |
| 164 | + </div> | |
| 165 | + ); | |
| 166 | +}; | |
| 167 | + | |
| 168 | +export default UploadFiles; | ... | ... |
packages/hr/wushuju_page/index.less
0 → 100644
| 1 | +.wushuju_page_warp { | |
| 2 | + background-color: #fff; | |
| 3 | + text-align: center; | |
| 4 | + padding-top: 127px; | |
| 5 | + | |
| 6 | + .wushuju_icon { | |
| 7 | + width: 156px; | |
| 8 | + height: 155px; | |
| 9 | + margin-bottom: 10px; | |
| 10 | + } | |
| 11 | + | |
| 12 | + .wushuju_word { | |
| 13 | + font-size: 16px; | |
| 14 | + font-weight: 400; | |
| 15 | + color: #1d2129; | |
| 16 | + line-height: 26px; | |
| 17 | + padding-bottom: 132px; | |
| 18 | + } | |
| 19 | +} | ... | ... |
packages/hr/wushuju_page/index.tsx
0 → 100644
| 1 | + | |
| 2 | +import cx from 'classnames'; | |
| 3 | +import './index.less'; | |
| 4 | +import { ossAddress } from '@/utils/ossAddress'; | |
| 5 | + | |
| 6 | +const WushujuPage: React.FC = () => { | |
| 7 | + return ( | |
| 8 | + <div className={cx('wushuju_page_warp')}> | |
| 9 | + <img className={cx('wushuju_icon')} src={ossAddress['nodata']} /> | |
| 10 | + <div className={cx('wushuju_word')}>暂无数据~</div> | |
| 11 | + </div> | |
| 12 | + ); | |
| 13 | +}; | |
| 14 | + | |
| 15 | +export default WushujuPage; | ... | ... |
packages/hro/typings.d.ts
0 → 100644
services/upload.ts
0 → 100644
tsconfig.json
0 → 100644
| 1 | +{ | |
| 2 | + "compilerOptions": { | |
| 3 | + "target": "ESNext", | |
| 4 | + "useDefineForClassFields": true, | |
| 5 | + "lib": ["DOM", "DOM.Iterable", "ESNext"], | |
| 6 | + "allowJs": false, | |
| 7 | + "skipLibCheck": true, | |
| 8 | + "esModuleInterop": false, | |
| 9 | + "strict": true, | |
| 10 | + "forceConsistentCasingInFileNames": true, | |
| 11 | + "module": "ESNext", | |
| 12 | + "moduleResolution": "Node", | |
| 13 | + "resolveJsonModule": true, | |
| 14 | + "isolatedModules": true, | |
| 15 | + "jsx": "react-jsx", | |
| 16 | + "allowSyntheticDefaultImports": true, | |
| 17 | + "paths": { | |
| 18 | + "@/hr/*": ["./packages/hr/*"], | |
| 19 | + "@/hro/*": ["./packages/hro/*"], | |
| 20 | + "@/utils/*": ["./utils/*"], | |
| 21 | + "@/services/*": ["./services/*"] | |
| 22 | + }, | |
| 23 | + "declaration": true, // 自动生成声明文件 | |
| 24 | + "declarationDir": "./", // 声明文件生成的目录 | |
| 25 | + "noEmitOnError": false | |
| 26 | + }, | |
| 27 | + "include": ["packages", "index.tsx", "typings.d.ts"], | |
| 28 | + "exclude": ["node_modules"], | |
| 29 | + "references": [{ "path": "./tsconfig.node.json" }] | |
| 30 | +} | ... | ... |
tsconfig.node.json
0 → 100644
typings.d.ts
0 → 100644
utils/areaData.ts
0 → 100644
此 diff 太大无法显示。
utils/format.ts
0 → 100644
| 1 | +import { useCallback, useEffect, useRef } from 'react'; | |
| 2 | + | |
| 3 | +interface IArea { | |
| 4 | + value: string; | |
| 5 | + label: string; | |
| 6 | + children?: IArea[]; | |
| 7 | +} | |
| 8 | + | |
| 9 | +// 省市区信息平铺 | |
| 10 | +export const getAreaData = (data: IArea[]) => { | |
| 11 | + let areaData: any = []; | |
| 12 | + const eachArea = (items: IArea[]) => { | |
| 13 | + items.map((item, i) => { | |
| 14 | + areaData.push({ | |
| 15 | + value: item.value, | |
| 16 | + label: item.label, | |
| 17 | + }); | |
| 18 | + if (item.children) eachArea(item.children); | |
| 19 | + }); | |
| 20 | + }; | |
| 21 | + eachArea(data); | |
| 22 | + return areaData; | |
| 23 | +}; | |
| 24 | + | |
| 25 | +// 根据code数组和地区信息返回数组地区名称 | |
| 26 | +export const getAreaNamesByCodes = (codes: string[], areaData: IArea[]) => { | |
| 27 | + let names: any = []; | |
| 28 | + const eachName = (items: IArea[]) => { | |
| 29 | + items.map((item, i) => { | |
| 30 | + if (codes.includes(item.value)) { | |
| 31 | + names.push(item.label); | |
| 32 | + } | |
| 33 | + }); | |
| 34 | + }; | |
| 35 | + eachName(areaData); | |
| 36 | + return names; | |
| 37 | +}; | |
| 38 | + | |
| 39 | +export function useDebounce(fn: any, delay: number) { | |
| 40 | + const { current } = useRef({ fn, timer: null as unknown as NodeJS.Timeout }); | |
| 41 | + useEffect( | |
| 42 | + function () { | |
| 43 | + current.fn = fn; | |
| 44 | + }, | |
| 45 | + [current.fn, fn], | |
| 46 | + ); | |
| 47 | + | |
| 48 | + return useCallback( | |
| 49 | + function (...args: any[]) { | |
| 50 | + if (current.timer) { | |
| 51 | + clearTimeout(current.timer as unknown as number); | |
| 52 | + } | |
| 53 | + current.timer = setTimeout(() => { | |
| 54 | + current.fn.call(current.fn, ...args); | |
| 55 | + }, delay); | |
| 56 | + }, | |
| 57 | + [current.fn, current.timer, delay], | |
| 58 | + ); | |
| 59 | +} | ... | ... |
utils/ossAddress.ts
0 → 100644
| 1 | +export const ossAddress = { | |
| 2 | + defaultPhone: 'https://hropublic.oss-cn-beijing.aliyuncs.com/hr-micro/resumephotosmall.png', | |
| 3 | + woman03: | |
| 4 | + 'https://hropublic.oss-cn-beijing.aliyuncs.com/hr-micro/%E5%A4%B4%E5%83%8F%E5%A5%B301%402x.png', | |
| 5 | + woman02: | |
| 6 | + 'https://hropublic.oss-cn-beijing.aliyuncs.com/hr-micro/%E5%A4%B4%E5%83%8F%E5%A5%B302%402x.png', | |
| 7 | + woman01: | |
| 8 | + 'https://hropublic.oss-cn-beijing.aliyuncs.com/hr-micro/%E5%A4%B4%E5%83%8F%E5%A5%B303%402x.png', | |
| 9 | + man05: | |
| 10 | + 'https://hropublic.oss-cn-beijing.aliyuncs.com/hr-micro/%E5%A4%B4%E5%83%8F%E7%94%B705%402x.png', | |
| 11 | + man04: | |
| 12 | + 'https://hropublic.oss-cn-beijing.aliyuncs.com/hr-micro/%E5%A4%B4%E5%83%8F%E7%94%B704%402x.png', | |
| 13 | + man03: | |
| 14 | + 'https://hropublic.oss-cn-beijing.aliyuncs.com/hr-micro/%E5%A4%B4%E5%83%8F%E7%94%B703%402x.png', | |
| 15 | + man02: | |
| 16 | + 'https://hropublic.oss-cn-beijing.aliyuncs.com/hr-micro/%E5%A4%B4%E5%83%8F%E7%94%B702%402x.png', | |
| 17 | + man01: | |
| 18 | + 'https://hropublic.oss-cn-beijing.aliyuncs.com/hr-micro/%E5%A4%B4%E5%83%8F%E7%94%B701%402x.png', | |
| 19 | + xianju: | |
| 20 | + 'https://hropublic.oss-cn-beijing.aliyuncs.com/hr-micro/%E7%8E%B0%E5%B1%85%E4%BD%8F%E5%9C%B0%402x.png', | |
| 21 | + huji: 'https://hropublic.oss-cn-beijing.aliyuncs.com/hr-micro/%E6%88%B7%E5%8F%A3%E5%9C%B0%402x.png', | |
| 22 | + searchMore: 'https://hropublic.oss-cn-beijing.aliyuncs.com/hr-micro/more.png', | |
| 23 | + add_cabdidate_button: 'https://hropublic.oss-cn-beijing.aliyuncs.com/hr-micro/add_candate.png', | |
| 24 | + add_cabdidate_icon: | |
| 25 | + 'https://hropublic.oss-cn-beijing.aliyuncs.com/hr-micro/%E5%9F%BA%E6%9C%AC%E4%BF%A1%E6%81%AF.png', | |
| 26 | + recruit_jobseekers_detail_photo: | |
| 27 | + 'https://hropublic.oss-cn-beijing.aliyuncs.com/hr-micro/resumephoto.png', | |
| 28 | + recruit_jobseekers_detail_phone: | |
| 29 | + 'https://hropublic.oss-cn-beijing.aliyuncs.com/hr-micro/phone.png', | |
| 30 | + recruit_jobseekers_detail_email: | |
| 31 | + 'https://hropublic.oss-cn-beijing.aliyuncs.com/hr-micro/email.png', | |
| 32 | + recruit_jobseekers_detail_idcard: | |
| 33 | + 'https://hropublic.oss-cn-beijing.aliyuncs.com/hr-micro/idcard.png', | |
| 34 | + recruit_jobseekers_detail_fileicon: | |
| 35 | + 'https://hropublic.oss-cn-beijing.aliyuncs.com/hr-micro/fileicon.png', | |
| 36 | + nodata: | |
| 37 | + 'https://hropublic.oss-cn-beijing.aliyuncs.com/hr-micro/%E6%9A%82%E6%97%A0%E6%95%B0%E6%8D%AE%402x.png', | |
| 38 | +}; | ... | ... |
vite.config.ts
0 → 100644
| 1 | +import { defineConfig } from 'vite'; | |
| 2 | +import react from '@vitejs/plugin-react'; | |
| 3 | +import { resolve } from 'path'; | |
| 4 | + | |
| 5 | +// https://vitejs.dev/config/ | |
| 6 | +export default defineConfig({ | |
| 7 | + plugins: [react()], | |
| 8 | + server: { | |
| 9 | + host: '127.0.0.1', | |
| 10 | + port: 10012, | |
| 11 | + strictPort: true, | |
| 12 | + open: true, | |
| 13 | + hmr: true, | |
| 14 | + }, | |
| 15 | + esbuild: { | |
| 16 | + jsxFactory: 'h', | |
| 17 | + jsxFragment: 'Fragment', | |
| 18 | + }, | |
| 19 | + resolve: { | |
| 20 | + alias: { | |
| 21 | + '@/hr': resolve(__dirname, 'packages/hr'), | |
| 22 | + '@/hro': resolve(__dirname, 'packages/hro'), | |
| 23 | + '@/utils': resolve(__dirname, 'utils'), | |
| 24 | + '@/services': resolve(__dirname, 'services'), | |
| 25 | + }, | |
| 26 | + }, | |
| 27 | +}); | ... | ... |
请
注册
或
登录
后发表评论