.husky/commit-msg
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
yarn commitlint --edit $1
.vscode/settings.json
"ahooks",
"aliyuncs",
"antd",
"commitlint", // 新增
"deadcode",
"denormalize",
"echarts",
"EDITMSG", // 新增
"favicons",
"iconfont",
"IIFE",
commitlint.config.ts
import type { UserConfig } from '@commitlint/types';
import { RuleConfigSeverity } from '@commitlint/types';
import dotenv from 'dotenv';
import fs from 'fs';
import path from 'path';
dotenv.config({ path: path.resolve(__dirname, '.env.local') });
const Configuration: UserConfig = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-case': [RuleConfigSeverity.Error, 'always', ['lower-case']],
'type-enum': [RuleConfigSeverity.Error, 'always', ['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'build', 'ci', 'chore', 'revert']],
'subject-rule': [RuleConfigSeverity.Error, 'always']
},
plugins: [
{
rules: {
'type-enum': ({ type }) => {
const typeList = ['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'build', 'ci', 'chore', 'revert'];
return [typeList.includes(type || ''), `类型错误,只允许提交类型为【${typeList}】的 commit, 例如:feat: 【1112223334445556667】xxx`];
},
'subject-rule': ({ type, subject }) => {
let _subject = subject || '';
const regRex = /^【[0-9]{19}】(?!\s*$).+/;
if (process.env.TASK_NO && !regRex.test(_subject)) {
const commitEditMsgPath = path.resolve(process.cwd(), '.git', 'COMMIT_EDITMSG');
const commitMsgContent = fs.readFileSync(commitEditMsgPath, 'utf-8');
const lines = commitMsgContent.split('\n');
_subject = `【${process.env.TASK_NO}】${subject}`;
if (lines.length > 0) {
lines[0] = `${type}: ${_subject}`;
}
fs.writeFileSync(commitEditMsgPath, lines.join('\n'));
}
return [regRex.test(_subject), '提交格式不符合规范,需要包含19位任务ID,且包含基础的 commit 信息, 例如:feat: 【1112223334445556667】xxx'];
}
}
}
]
};
export default Configuration;
package.json
"devDependencies": {
"@commitlint/cli": "^17.8.1", // 新增
"@commitlint/config-conventional": "^17.8.1", // 新增
"@types/lodash": "^4.14.202",
"@types/node": "^20.11.6",
"@types/react": "^18.0.33",
"@types/react-dom": "^18.0.11",
"chalk": "^4.1.2",
"cross-env": "^7.0.3",
"dotenv": "^16.4.5", // 新增
"eslint": "^8.54.0",
"eslint-config-prettier": "^9.0.0",
"eslint-import-resolver-alias": "^1.1.2",
...
}
.env.local
# .env 的本地版本,这里配置的环境变量会覆盖 .env 中的变量。
APP_ENV=sz-test1
# APP_ENV=sz-dev
# TASK_NO=1788498654317685432
TASK="[{"release/2024-07-18" : "1807622818796223578"}]"
.env
# 对应测试环境,值从 config/server.ts 中的 key 中选
# 开发时请在本地根目录新建一个 `.env.local` 文件,自行修改为需要的开发环境
# APP_ENV=sz-test
# 多语言相关接口的域名。
# 各环境统一走 生产jk.cc.cn 域名(环境由接口的env参数做区分)
# 这个值基本不会动,除非服务端需要调试特定环境的多语言接口。
# 在使用时:
# 1. 当 APP_ENV=production 时,不使用当前变量,固定走生产的域名 jk.cc.cn。
# 2. 测试环境用当前变量指定的域名,而不是随环境对应的域名。
# 3. 本地开发会忽略该变量,走本地开发域名以mock本地语料
# 自动添加任务编号到 Commit, TASK_NO 根据自己要求填写
# TASK_NO=1803607484162225644
README.md
# 运营平台前端项目
## yarn
- 统一使用 yarn 作为包管理工具
## 配置本地开发环境
- 切换环境请按 `.env` 中关于 `APP_ENV` 的注释,在`.env.local` 中配置 `APP_ENV` 变量区分连接的环境。
- `yarn install` 时会自动创建一个空白的 `.env.local` 文件
## 代码校验
macos用户,需要在 `yarn install`之后,于项目目录下执行下面两行命令。使 husky 生效。
```sh
chmod ug+x .husky/*
chmod ug+x .git/hooks/*
UI库
- 依赖antd 4.24.x版本,但是不要直接从antd引入, UI相关的组件从核心库引入
// 错误引入方式:
import { Button } from 'antd';
// 正确引入方式
import { Button } from '@/component';
权限
- 抛弃umi的authority配置。菜单直接在路由里面配置权限code,具体权限code是什么,需要找后端
开发须知
- 请运行:
git config --global core.autocrlf false
去除 git 进入暂存区时自动转换为crlf
- 请运行:
git config --global core.ignorecase false
去除 git 忽略大小写 - 编辑器必须开启 eslint 检测和保存自动修复功能(具体配置详细看:编辑器配置 Eslint)
- master 是主分支,feature+版本号是开发分支,release+版本号是预发布分支
- 统一使用 yarn 作为包管理工具
野火本地开发
- 修改 hosts
172.22.3.196 im.jk.cn
不同环境 ip 不同
business 和 test 服务修改 hosts 即可成功连接本地联调只有 workdev 环境可连接成功 ( 请求头 Origin 为 localhost 造成连接失败, 连接非 dev 的服务连接会被拒绝 )
其他
参考umi开发文档 Umi
styled-components 使用方法
基础
// Create a Title component that'll render an <h1> tag with some styles
const Title = styled.h1`
font-size: 1.5em;
text-align: center;
color: #BF4F74;
`;
// Create a Wrapper component that'll render a <section> tag with some styles
const Wrapper = styled.section`
padding: 4em;
background: papayawhip;
`;
// Use Title and Wrapper like any other React component – except they're styled!
render(
<Wrapper>
<Title>
Hello World!
</Title>
</Wrapper>
);
根据属性进行调整
const Button = styled.button<{ $primary?: boolean; }>`
/* Adapt the colors based on primary prop */
background: ${props => props.$primary ? "#BF4F74" : "white"};
color: ${props => props.$primary ? "white" : "#BF4F74"};
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid #BF4F74;
border-radius: 3px;
`;
render(
<div>
<Button>Normal</Button>
<Button $primary>Primary</Button>
</div>
);
扩展样式
// The Button from the last section without the interpolations
const Button = styled.button`
color: #BF4F74;
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid #BF4F74;
border-radius: 3px;
`;
// A new component based on Button, but with some override styles
const TomatoButton = styled(Button)`
color: tomato;
border-color: tomato;
`;
render(
<div>
<Button>Normal Button</Button>
<TomatoButton>Tomato Button</TomatoButton>
</div>
);
伪元素、伪选择器和嵌套
const Thing = styled.div.attrs((/* props */) => ({ tabIndex: 0 }))`
color: blue;
&:hover {
color: red; // <Thing> when hovered
}
& ~ & {
background: tomato; // <Thing> as a sibling of <Thing>, but maybe not directly next to it
}
& + & {
background: lime; // <Thing> next to <Thing>
}
&.something {
background: orange; // <Thing> tagged with an additional CSS class ".something"
}
.something-else & {
border: 1px solid; // <Thing> inside another element labeled ".something-else"
}
`
render(
<React.Fragment>
<Thing>Hello world!</Thing>
<Thing>How ya doing?</Thing>
<Thing className="something">The sun is shining...</Thing>
<div>Pretty nice day today.</div>
<Thing>Don't you think?</Thing>
<div className="something-else">
<Thing>Splendid.</Thing>
</div>
</React.Fragment>
);
强制样式提升
const Thing = styled.div`
&& {
color: blue;
}
`
const GlobalStyle = createGlobalStyle`
div${Thing} {
color: red;
}
`
render(
<React.Fragment>
<GlobalStyle />
<Thing>
I'm blue, da ba dee da ba daa
</Thing>
</React.Fragment>
)