Compare commits

..

No commits in common. "6e0d9cca5dff772240ecf1f323952a5a8b2f92b1" and "d6d86ed657b763c630699b719171d8a358d8b09a" have entirely different histories.

22 changed files with 106 additions and 1736 deletions

View File

@ -1,5 +0,0 @@
export default {
define: {
API_URL: 'http://127.0.0.1:3008',
},
};

View File

@ -1,5 +0,0 @@
export default {
define: {
API_URL: 'https://adseed-api.soyootech.com',
},
};

View File

@ -9,22 +9,7 @@ export default defineConfig({
layout: false, layout: false,
dva: {}, dva: {},
valtio: {}, valtio: {},
// history: { type: 'hash' },
plugins: [require.resolve('@umijs/plugins/dist/unocss')],
unocss: {
watch: ['src/**/*.tsx'],
},
extraPostCSSPlugins: [
require('tailwindcss')({
config: './tailwind.config.ts',
}),
],
routes: [ routes: [
{
path: '/login',
component: 'Login',
layout: false,
},
{ {
path: '/', path: '/',
id: 0, id: 0,
@ -33,5 +18,12 @@ export default defineConfig({
routes: [], routes: [],
}, },
], ],
proxy: {
'/api': {
target: 'http://127.0.0.1:3008',
changeOrigin: true,
pathRewrite: { '^/api': '' },
},
},
npmClient: 'pnpm', npmClient: 'pnpm',
}); });

View File

@ -1 +0,0 @@
exports.ossSecret = 'ck84eTxx4aSTjornlYrCy8RkurCHfc';

View File

@ -1,35 +0,0 @@
/* eslint-disable */
var aliOss = require('ali-oss');
var fs = require('fs');
var path = require('path');
var accessKeySecret = require('./config').ossSecret;
const adseedOssManager = new aliOss({
region: 'oss-cn-shanghai',
accessKeyId: 'LTAI5tEday8PJNaMTz5mp8g4',
accessKeySecret,
bucket: 'adseed-admin-ux',
});
const distDir = path.resolve(__dirname, '../dist');
const distFiles = traverseFiles(distDir, []);
console.log('start deploying');
Promise.all(distFiles.map((fileName) => adseedOssManager.put(fileName.slice(distDir.length), fileName)))
.then(() => console.log('deployment succeed'))
.catch((e) => console.log('deployment failed:', e));
function traverseFiles(dir, distFiles) {
const dirents = fs.readdirSync(dir, { withFileTypes: true });
for (const dirent of dirents) {
const res = `${dir}/${dirent.name}`;
const file = fs.statSync(res);
if (file.isDirectory()) {
traverseFiles(res, distFiles);
} else {
distFiles.push(res);
}
}
return distFiles;
}

View File

@ -2,8 +2,8 @@
"private": true, "private": true,
"author": "guofei <guofeichu@gmail.com>", "author": "guofei <guofeichu@gmail.com>",
"scripts": { "scripts": {
"build": "cross-env NODE_ENV=prod max build && node ./devops/deploy.js", "build": "max build",
"dev": "cross-env UMI_ENV=dev max dev", "dev": "max dev",
"format": "prettier --cache --write .", "format": "prettier --cache --write .",
"postinstall": "max setup", "postinstall": "max setup",
"prepare": "husky", "prepare": "husky",
@ -14,23 +14,15 @@
"@ant-design/icons": "^5.0.1", "@ant-design/icons": "^5.0.1",
"@ant-design/pro-components": "^2.7.9", "@ant-design/pro-components": "^2.7.9",
"@umijs/max": "^4.2.8", "@umijs/max": "^4.2.8",
"@umijs/plugins": "^4.2.9",
"@unocss/cli": "^0.60.4",
"antd": "^5.18.0", "antd": "^5.18.0",
"antd-style": "^3.6.2", "antd-style": "^3.6.2",
"axios": "^1.7.2", "axios": "^1.7.2",
"lodash-es": "^4.17.21", "qs": "^6.12.1"
"qs": "^6.12.1",
"tailwindcss": "^3.4.3",
"unocss": "^0.60.4"
}, },
"devDependencies": { "devDependencies": {
"@types/lodash": "^4.17.4",
"@types/qs": "^6.9.15", "@types/qs": "^6.9.15",
"@types/react": "^18.0.33", "@types/react": "^18.0.33",
"@types/react-dom": "^18.0.11", "@types/react-dom": "^18.0.11",
"ali-oss": "^6.20.0",
"cross-env": "^7.0.3",
"husky": "^9", "husky": "^9",
"lint-staged": "^13.2.0", "lint-staged": "^13.2.0",
"prettier": "^2.8.7", "prettier": "^2.8.7",

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,3 @@
import { getSupderInfoApi } from '@/services/system/supderLogin';
import { history } from '@umijs/max';
import { routes as appRouters, loopMenuItem, type MenuItem } from './utils/router'; import { routes as appRouters, loopMenuItem, type MenuItem } from './utils/router';
export function patchClientRoutes({ routes }: any) { export function patchClientRoutes({ routes }: any) {
@ -12,17 +10,19 @@ export function render(oldRender: any) {
oldRender(); oldRender();
} }
export async function getInitialState(): Promise<LoginAPI.LoginUserInfo | null> { // 全局初始化数据配置,用于 Layout 用户信息和权限初始化
const { data: userInfo }: { data: LoginAPI.LoginUserInfo } = await getSupderInfoApi(); // 更多信息见文档https://umijs.org/docs/api/runtime-config#getinitialstate
if (!userInfo) { export async function getInitialState(): Promise<{ name: string }> {
history.replace('/login');
return null;
}
if (userInfo && history.location.pathname === '/login') {
history.push('/');
}
return { return {
...userInfo, name: 'xx',
}; };
} }
// export const layout = () => {
// return {
// logo: 'https://img.alicdn.com/tfs/TB1YHEpwUT1gK0jSZFhXXaAtVXa-28-27.svg',
// menu: {
// locale: false,
// },
// };
// };

View File

@ -1,91 +0,0 @@
import { updatePasswordAPI, userLogoutAPI } from '@/services/system/supderLogin';
import { history, useModel } from '@umijs/max';
import { Alert, Form, Input, Modal, Spin, message } from 'antd';
import { useEffect, useState } from 'react';
export default () => {
const { initialState } = useModel('@@initialState');
const [loading, setLoading] = useState<boolean>(false);
const [formRef] = Form.useForm();
const [updatePwd, setUpdatePwd] = useState<boolean>(false);
useEffect(() => {
const isInitPwd = initialState?.isFirstLogin;
console.log(initialState?.isFirstLogin);
if (isInitPwd) {
setUpdatePwd(true);
}
}, []);
const handleUpdatePwd = async () => {
const formValues = await formRef.validateFields();
setLoading(true);
const params: LoginAPI.UpdatePassWordType = { oldPwd: '123456', newPwd: formValues.password };
const result = await updatePasswordAPI(params);
setLoading(false);
if (result.code === 200) {
message.success('密码修改成功,请重新登录');
setTimeout(() => {
userLogoutAPI();
history.replace('/login');
}, 2000);
}
};
return (
<Modal
title="提示"
open={updatePwd}
cancelButtonProps={{ style: { display: 'none' } }}
okText="确定修改"
closeIcon={null}
onOk={handleUpdatePwd}
>
<Spin spinning={loading}>
<Alert message="系统监测到您的密码为初始密码,请修改。" type="warning" style={{ marginBottom: '10px' }} />
<Form form={formRef}>
<Form.Item
name="password"
label="请输入新密码"
rules={[
{
required: true,
message: '请输入要修改的密码!',
},
{
pattern: /^[A-Za-z0-9]{6,15}$/,
message: '请输入6~15位数的密码其中包含大小写字母、数字',
},
]}
hasFeedback
>
<Input.Password />
</Form.Item>
<Form.Item
name="confirm"
label="请确认新密码"
dependencies={['password']}
hasFeedback
rules={[
{
required: true,
message: '请确定修改的密码!',
},
({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue('password') === value) {
return Promise.resolve();
}
return Promise.reject(new Error('俩次密码不一样!'));
},
}),
]}
>
<Input.Password />
</Form.Item>
</Form>
</Spin>
</Modal>
);
};

View File

@ -1,5 +1,4 @@
import { userLoginAPI } from '@/services/system/supderLogin'; import type { ConnectProps, Reducer } from '@umijs/max';
import type { ConnectProps, Effect, Reducer } from '@umijs/max';
type UserModelState = { type UserModelState = {
loading: boolean; loading: boolean;
@ -12,9 +11,9 @@ type UserModelType = {
namespace: 'user'; namespace: 'user';
state: UserModelState; state: UserModelState;
effects: { effects: {
login: Effect; // login: Effect;
setLogin: Effect; // setLogin: Effect;
setKey: Effect; // setKey: Effect;
}; };
reducers: { reducers: {
save: Reducer<UserModelState>; save: Reducer<UserModelState>;
@ -32,24 +31,14 @@ const UserModel: UserModelType = {
loginLoading: false, loginLoading: false,
}, },
effects: { effects: {
*login({ payload }, { call, put }) { // 登录
try { // *login({ payload }, { call, put }) {},
yield put({ type: 'save', payload: { loginLoading: true } }); // *setLogin({ payload }, { put }) {
const result: LoginAPI.LoginResponse = yield call(userLoginAPI, payload); // yield put({ type: 'save', payload: { loading: payload } });
localStorage.setItem('Authorization', result.data.token); // },
yield put({ type: 'save', payload: { loginLoading: true, isLogin: true } }); // *setKey({ payload }, { put }) {
} catch { // yield put({ type: 'save', payload: { ...payload } });
yield put({ type: 'save', payload: { loginLoading: false, isLogin: false } }); // },
} finally {
yield put({ type: 'save', payload: { loginLoading: false } });
}
},
*setLogin({ payload }, { put }) {
yield put({ type: 'save', payload: { loading: payload } });
},
*setKey({ payload }, { put }) {
yield put({ type: 'save', payload: { ...payload } });
},
}, },
reducers: { reducers: {
save(state, action) { save(state, action) {

View File

@ -1,5 +1,4 @@
import Guide from '@/components/Guide'; import Guide from '@/components/Guide';
import UpdatePwd from '@/layouts/UpdatePwd';
import { trim } from '@/utils/format'; import { trim } from '@/utils/format';
import { PageContainer } from '@ant-design/pro-components'; import { PageContainer } from '@ant-design/pro-components';
import { useModel } from '@umijs/max'; import { useModel } from '@umijs/max';
@ -9,7 +8,6 @@ const HomePage: React.FC = () => {
const { name } = useModel('global'); const { name } = useModel('global');
return ( return (
<PageContainer ghost> <PageContainer ghost>
<UpdatePwd />
<div className={styles.container}> <div className={styles.container}>
<Guide name={trim(name)} /> <Guide name={trim(name)} />
</div> </div>

View File

@ -1,49 +0,0 @@
.container {
display: flex;
flex-direction: column;
height: 100vh;
overflow: auto;
background: #f0f2f5;
}
.lang {
width: 100%;
height: 40px;
line-height: 44px;
text-align: right;
:global(.ant-dropdown-trigger) {
margin-right: 24px;
}
}
.content {
flex: 1;
padding: 32px 0;
}
@media (min-width: 768px) {
.container {
background-image: url('https://gw.alipayobjects.com/zos/rmsportal/TVYTbAXWheQpRcWDaDMu.svg');
background-repeat: no-repeat;
background-position: center 110px;
background-size: 100%;
}
.content {
padding: 32px 0 24px;
}
}
.icon {
margin-left: 8px;
color: rgba(0, 0, 0, 20%);
font-size: 24px;
vertical-align: middle;
cursor: pointer;
transition: color 0.3s;
&:hover {
color: #1890ff;
}
}

View File

@ -1,91 +0,0 @@
import type { UserConnectedProps } from '@/models/user';
import { LockOutlined, UserOutlined } from '@ant-design/icons';
import { LoginForm, ProFormInstance, ProFormText } from '@ant-design/pro-components';
import { connect } from '@umijs/max';
import { Button } from 'antd';
import { FC, Fragment, useEffect, useRef } from 'react';
import styles from './index.less';
const Login: FC<UserConnectedProps> = (props) => {
const { user, dispatch } = props;
const { loginLoading, isLogin } = user;
const formRef = useRef<ProFormInstance>();
const handleLoginSubmit = async (values: LoginAPI.LoginParams) => {
dispatch?.({
type: 'user/login',
payload: values,
});
};
useEffect(() => {
console.log(isLogin);
if (isLogin) {
window.location.href = '/home';
}
}, [isLogin]);
return (
<>
<div className={styles.container}>
<div className={styles.content}>
<LoginForm
formRef={formRef}
title="Adseed 管理系统"
subTitle=" "
initialValues={{
autoLogin: true,
}}
submitter={{
render: () => {
return (
<Button type="primary" htmlType="submit" className="w-full" loading={loginLoading}>
</Button>
);
},
}}
onFinish={async (values) => {
await handleLoginSubmit(values);
}}
>
<Fragment>
<ProFormText
name="userName"
fieldProps={{
size: 'large',
prefix: <UserOutlined />,
}}
placeholder="账号"
rules={[
{
required: true,
message: '请输入用户名!',
},
]}
/>
<ProFormText.Password
name="password"
fieldProps={{
size: 'large',
prefix: <LockOutlined />,
}}
placeholder="密码"
rules={[
{
required: true,
message: '请输入密码!',
},
]}
/>
</Fragment>
</LoginForm>
</div>
</div>
</>
);
};
const UserConnect = ({ user }: { user: UserConnectedProps['user'] }) => ({ user });
export default connect(UserConnect)(Login);

View File

@ -1,5 +1,4 @@
import { addSupderAdminAPI, editSupderAdminAPI } from '@/services/system/supderAdmin'; import { addSupderAdminAPI, editSupderAdminAPI } from '@/services/system/supderAdmin';
import { Reg } from '@/utils/reg';
import { ActionType, ProFormRadio, ProFormText } from '@ant-design/pro-components'; import { ActionType, ProFormRadio, ProFormText } from '@ant-design/pro-components';
import { App, Form, Modal } from 'antd'; import { App, Form, Modal } from 'antd';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
@ -66,45 +65,10 @@ const AddSupderAdmin = (props: PropTypes) => {
required required
label="管理员账户" label="管理员账户"
placeholder="请输入管理员账户" placeholder="请输入管理员账户"
rules={[ rules={[{ required: true, message: '请输入管理员账户' }]}
{ required: true, message: '请输入管理员账户' },
{ pattern: Reg.SuperAdminAccount, message: '验证失败4~15位的非中文账户' },
]}
/>
<ProFormText
initialValue={''}
width="md"
name="email"
label="邮箱"
placeholder="请输入邮箱"
rules={[
{
validator: (rule, value) => {
if (value && !Reg.Email.test(value)) {
return Promise.reject(new Error('邮箱格式不正确'));
}
return Promise.resolve();
},
},
]}
/>
<ProFormText
initialValue={''}
width="md"
name="phone"
label="手机号"
placeholder="请输入手机号"
rules={[
{
validator: (rule, value) => {
if (value && !Reg.Phone.test(value)) {
return Promise.reject(new Error('手机号格式不正确'));
}
return Promise.resolve();
},
},
]}
/> />
<ProFormText initialValue={''} width="md" name="email" label="邮箱" placeholder="请输入邮箱" />
<ProFormText initialValue={''} width="md" name="phone" label="手机号" placeholder="请输入手机号" />
<ProFormRadio.Group <ProFormRadio.Group
width="md" width="md"
required required

View File

@ -42,20 +42,6 @@ const Page = () => {
return record.role; return record.role;
}, },
}, },
{
title: '状态',
dataIndex: 'status',
align: 'center',
hideInSearch: true,
renderText: (_, record: SuperAdmin.SuperAdminItem) => {
if (record.status === 1) {
return '正常';
} else if (record.status === 2) {
return '禁用';
}
return record.status;
},
},
{ {
title: '创建时间', title: '创建时间',
dataIndex: 'createTime', dataIndex: 'createTime',
@ -155,7 +141,6 @@ const Page = () => {
<AddSupderAdmin <AddSupderAdmin
visible={createModalVisible} visible={createModalVisible}
onCancel={() => { onCancel={() => {
setEditRow(undefined);
setCreateModalVisible(false); setCreateModalVisible(false);
}} }}
tableRef={tableRef} tableRef={tableRef}

View File

@ -1,18 +0,0 @@
import request from '@/utils/request';
//登录
export const userLoginAPI = (data: LoginAPI.LoginParams): Promise<LoginAPI.LoginResponse> => {
return request.post('/system/account/login', data);
};
export const getSupderInfoApi = (): Promise<API.ResponstBody<LoginAPI.LoginUserInfo>> => {
return request.post('/system/account/userInfo');
};
export const updatePasswordAPI = (data: LoginAPI.UpdatePassWordType): Promise<API.ResponstBody> => {
return request.post('/system/account/updatePwd', data);
};
export const userLogoutAPI = (): Promise<API.ResponstBody> => {
return request.post('/system/account/logout');
};

View File

@ -1,29 +0,0 @@
declare namespace LoginAPI {
type LoginParams = {
userName?: string;
password?: string;
};
type LoginUserInfo = {
id?: string;
userName?: string;
email?: string;
phone?: string;
status?: number;
createTime?: string;
updateTime?: string;
isFirstLogin?: boolean;
role?: number;
};
type UpdatePassWordType = {
oldPwd: string;
newPwd: string;
};
type LoginRespone = {
token: string;
};
type LoginResponse = API.ResponstBody<LoginRespone>;
}

View File

@ -1,7 +0,0 @@
export const Reg = {
Email:
// eslint-disable-next-line no-useless-escape
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
Phone: /^1\d{10}$/,
SuperAdminAccount: /^[A-Za-z][-_!@#$%^&*.a-zA-Z0-9]{4,15}$/,
};

View File

@ -1,7 +1,7 @@
import { message as antMessage, notification } from 'antd'; import { notification } from 'antd';
import axios, { AxiosRequestHeaders } from 'axios'; import axios, { AxiosRequestHeaders } from 'axios';
const instance = axios.create({ baseURL: API_URL + '/backend/' }); const instance = axios.create({ baseURL: 'http://127.0.0.1:3008/backend/' });
instance.interceptors.request.use( instance.interceptors.request.use(
(config) => { (config) => {
@ -17,10 +17,9 @@ instance.interceptors.request.use(
instance.interceptors.response.use( instance.interceptors.response.use(
(response) => { (response) => {
const { data } = response; const { data } = response;
const { code, message } = data; const { code } = data;
if (code === 401) { if (code === 401 || code === 403) {
antMessage.error(message);
localStorage.removeItem('Authorization'); localStorage.removeItem('Authorization');
} }

View File

@ -1,20 +0,0 @@
module.exports = {
mode: 'jit',
purge: ['./public/**/*.html', './src/**/*.{js,jsx,ts,tsx}'],
darkMode: false, // or 'media' or 'class'
theme: {
backgroundColor: (theme) => ({
...theme('colors'),
dark70: 'rgba(0,0,0,.7)',
}),
extend: {
colors: {
primary: '#1677ff',
},
},
},
variants: {
extend: {},
},
plugins: [],
};

11
typings.d.ts vendored
View File

@ -2,15 +2,4 @@ import '@umijs/max/typings';
declare global { declare global {
declare type EditRow<T> = T | undefined; declare type EditRow<T> = T | undefined;
const API_URL: string;
} }
declare module '*.less';
declare module '*.svg';
declare module '*.png';
declare module '*.jpg';
declare module '*.jpeg';
declare module '*.gif';
declare module '*.bmp';
declare module '*.tiff';

View File

@ -1,10 +0,0 @@
import { defineConfig, presetAttributify, presetUno } from 'unocss';
export function createConfig({ strict = true, dev = true } = {}) {
return defineConfig({
envMode: dev ? 'dev' : 'build',
presets: [presetAttributify({ strict }), presetUno()],
});
}
export default createConfig();