提交 dfcefd42 authored 作者: 王鹏飞's avatar 王鹏飞

chore: update

上级 81f68bb5
VITE_API_URL_WORD = https://zijingebook.ezijing.com/file/
VITE_API_URL_WORD = https://zijingebook.ezijing.com/api
VITE_API_URL_WORD = https://book-admin-web.ezijing.com/api
# VITE_API_URL_WORD = http://ebook-pc.ezijing.com:7419
VITE_API_BASE_API_PREFFIX = /api
VITE_API_WEBSOCKET_URL = wss://zijingebook.ezijing.com
......
module.exports = {
extends: [require.resolve('@umijs/lint/dist/config/eslint')],
globals: {
page: true,
REACT_APP_ENV: true,
},
};
root: true,
env: { browser: true, es2020: true },
extends: ['eslint:recommended', 'plugin:react/recommended', 'plugin:react/jsx-runtime', 'plugin:react-hooks/recommended'],
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
settings: { react: { version: '18.2' } }
}
{
"extends": [
"development"
],
"hints": {
"meta-viewport": "off"
}
}
\ No newline at end of file
{
"*.{js,jsx,ts,tsx,json}": ["prettier --write", "eslint --fix"],
"*.css": ["stylelint --fix", "prettier --write"]
}
# .prettierignore
**/*.svg
package.json
/dist
.dockerignore
.DS_Store
.eslintignore
*.png
*.toml
docker
.editorconfig
Dockerfile*
.gitignore
.prettierignore
LICENSE
.eslintcache
*.lock
yarn-error.log
.history
CNAME
/build
/public
const fabric = require('@umijs/fabric');
module.exports = {
// 一行最多 120 字符
printWidth: 120,
// 使用 2 个空格缩进
tabWidth: 2,
// 不使用缩进符,而使用空格
useTabs: false,
// 行尾需要有分号
semi: true,
// 使用单引号
singleQuote: true,
// 对象的 key 仅在必要时用引号
quoteProps: "as-needed",
// jsx 不使用单引号,而使用双引号
jsxSingleQuote: true,
// 末尾需要有逗号
trailingComma: "all",
// 大括号内的首尾需要空格
bracketSpacing: true,
// jsx 标签的反尖括号需要换行
jsxBracketSameLine: false,
// 箭头函数,只有一个参数的时候,也需要括号
arrowParens: "always",
// 每个文件格式化的范围是文件的全部内容
rangeStart: 0,
rangeEnd: Infinity,
// 不需要写文件开头的 @prettier
requirePragma: false,
// 不需要自动在文件开头插入 @prettier
insertPragma: false,
// 使用默认的折行标准
proseWrap: "preserve",
// 根据显示样式决定 html 要不要折行
htmlWhitespaceSensitivity: "css",
// vue 文件中的 script 和 style 内不用缩进
vueIndentScriptAndStyle: false,
// 换行符使用 lf
endOfLine: "lf",
// 格式化嵌入的内容
embeddedLanguageFormatting: "auto",
// 组件或者标签的属性是否控制一行只显示一个属性
singleAttributePerLine: false,
...fabric.prettier,
};
const fabric = require('@umijs/fabric');
module.exports = {
"extends": "stylelint-config-standard",
"rules": {
"string-quotes": "single",
"value-keyword-case": null,
"declaration-block-trailing-semicolon": "always",
"no-invalid-position-at-import-rule": null,
"selector-class-pattern": "^(?:(?:o|c|u|t|s|is|has|_|js|qa)-)?[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*(?:__[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*)?(?:--[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*)?(?:\\[.+\\])?$",
"property-no-unknown": [
true,
{
"ignoreProperties": ["composes"]
}
],
"selector-pseudo-class-no-unknown": [
true,
{
"ignorePseudoClasses": ["global"]
}
]
},
...fabric.stylelint,
};
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -49,15 +49,14 @@
"devDependencies": {
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@umijs/fabric": "^4.0.1",
"@umijs/lint": "^4.0.88",
"@vitejs/plugin-react-swc": "^3.7.0",
"eslint": "^8.53.0",
"eslint": "^8.57.0",
"eslint-plugin-react": "^7.34.2",
"eslint-plugin-react-hooks": "^4.6.2",
"husky": "^8.0.3",
"less": "^4.2.0",
"lint-staged": "^15.1.0",
"prettier": "^3.1.0",
"stylelint-config-standard": "^34.0.0",
"prettier": "^3.3.1",
"vite": "^5.2.12",
"vite-plugin-mkcert": "^1.17.5"
},
......
import { useEffect, useState } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import { Button } from 'antd';
import './App.less';
import { ErrorBoundary } from 'react-error-boundary'
import { Button } from 'antd'
import './App.less'
import { GetroutesDyamic } from '@/routes/index';
import { GetroutesDyamic } from '@/routes/index'
function fallbackRender({ error, resetErrorBoundary }) {
return (
<div role='alert'>
<div role="alert">
<p>出错啦:</p>
<pre style={{ color: 'red' }}>{error.message}</pre>
<Button type='link' onClick={resetErrorBoundary}>
<Button type="link" onClick={resetErrorBoundary}>
Try again
</Button>
</div>
);
)
}
function App() {
const [token, setToken] = useState('');
useEffect(() => {
const userToken = localStorage.getItem('kiwi.token');
if (userToken) {
setToken(userToken);
} else {
setToken('');
}
}, []);
return (
<ErrorBoundary fallbackRender={fallbackRender}>
<GetroutesDyamic />
</ErrorBoundary>
);
)
}
export default App;
export default App
import axios from '@/utils/axios'
export function getSTSToken(data) {
return axios.post('/api/common/OssUpload/getUploadToken', data)
}
import React, { useState, useEffect } from 'react';
import { Spin } from 'antd';
import { Editor, Toolbar } from '@wangeditor/editor-for-react';
import '@wangeditor/editor/dist/css/style.css'; // 引入 css
import AliOSS from 'ali-oss';
import { getAliOSSSTSToken } from '@/pages/setting/help/addedit/requet';
import add from '@/assets/images/icon/add.png';
import dayjs from 'dayjs';
import './index.less';
const EditorReader = (props) => {
const { form, fieldName, content, loadLoading } = props;
import React, { useState, useEffect } from 'react'
import { Spin } from 'antd'
import { Editor, Toolbar } from '@wangeditor/editor-for-react'
import '@wangeditor/editor/dist/css/style.css' // 引入 css
import AliOSS from 'ali-oss'
import { getAliOSSSTSToken } from '@/pages/setting/help/addedit/requet'
import add from '@/assets/images/icon/add.png'
import dayjs from 'dayjs'
import './index.less'
const EditorReader = props => {
const { form, fieldName, content, loadLoading } = props
// oss
const [ossClient, setOssClient] = useState(null); // oss 操作
const [STSToken, setSTSToken] = useState(null); // oss 过期设置
const [file, setFile] = useState({}); // oss 过期设置
const [uploading, setUploading] = useState(false);
const [ossClient, setOssClient] = useState(null) // oss 操作
const [STSToken, setSTSToken] = useState(null) // oss 过期设置
const [file, setFile] = useState({}) // oss 过期设置
const [uploading, setUploading] = useState(false)
// editor 实例
const [editor, setEditor] = useState(null);
const [editor, setEditor] = useState(null)
// 编辑器内容
const [html, setHtml] = useState(content);
const [loading, setLoading] = useState(loadLoading || false);
const [html, setHtml] = useState(content)
const [loading, setLoading] = useState(loadLoading || false)
const getStsAuthToken = async () => {
const data = await getAliOSSSTSToken();
const data = await getAliOSSSTSToken()
if (data) {
window.sessionStorage.setItem('sts', JSON.stringify(data));
setSTSToken(data);
window.sessionStorage.setItem('sts', JSON.stringify(data))
setSTSToken(data)
const ossClientTemp = await new AliOSS({
accessKeyId: data.AccessKeyId,
accessKeySecret: data.AccessKeySecret,
......@@ -37,18 +37,18 @@ const EditorReader = (props) => {
timeout: 180000,
refreshSTSToken: async () => {
const info = await getAliOSSSTSToken();
const info = await getAliOSSSTSToken()
return {
AccessKeyId: info.AccessKeyId,
AccessKeySecret: info.AccessKeySecret,
SecurityToken: info.SecurityToken,
};
SecurityToken: info.SecurityToken
}
},
refreshSTSTokenInterval: 14 * 60 * 1000,
});
setOssClient(ossClientTemp);
refreshSTSTokenInterval: 14 * 60 * 1000
})
setOssClient(ossClientTemp)
}
}
};
// 工具栏配置
const toolbarConfig = {
......@@ -65,9 +65,9 @@ const EditorReader = (props) => {
'fullScreen',
'insertLink',
'headerSelect',
'todo',
], //删除工具栏
}; // JS 语法
'todo'
] //删除工具栏
} // JS 语法
// 编辑器配置
const editorConfig = {
......@@ -92,14 +92,14 @@ const EditorReader = (props) => {
{ name: '六号', value: '10px' },
{ name: '小六', value: '8px' },
{ name: '七号', value: '7px' },
{ name: '八号', value: '6px' },
],
{ name: '八号', value: '6px' }
]
},
fontFamily: {
fontFamilyList: ['思源黑体', '思源宋体', '黑体', '宋体', '楷体', '仿宋'],
fontFamilyList: ['思源黑体', '思源宋体', '黑体', '宋体', '楷体', '仿宋']
},
lineHeight: {
lineHeightList: ['1', '1.25', '1.5', '2', '2.5', '3'],
lineHeightList: ['1', '1.25', '1.5', '2', '2.5', '3']
},
// 配置上传图片
uploadImage: {
......@@ -108,29 +108,29 @@ const EditorReader = (props) => {
fieldName: 'image',
headers: {
'Content-Type': 'multipart/form-data',
Authorization: 'Bearer ' + localStorage.getItem('token') || '',
Authorization: 'Bearer ' + localStorage.getItem('token') || ''
},
maxFileSize: 10 * 1024 * 1024, // 10M
base64LimitSize: 5 * 1024, // 5kb 以下插入 base64 // 用户自定义上传图片
customUpload: async (file, insertFn) => {
setLoading(true);
setLoading(true)
if (!ossClient) {
console.error('ossClient还未初始化', ossClient);
return false;
console.error('ossClient还未初始化', ossClient)
return false
}
const fileExt = file.name.substring(file.name.lastIndexOf('.'));
const fileName = `banner-${dayjs().valueOf()}-${Math.random().toString(36).substring(2)}${fileExt}`;
const filePath = `${dayjs().format('YYYY/MM/DD')}/${fileName}`;
const result = await ossClient.put(filePath, file);
const fileExt = file.name.substring(file.name.lastIndexOf('.'))
const fileName = `banner-${dayjs().valueOf()}-${Math.random().toString(36).substring(2)}${fileExt}`
const filePath = `${dayjs().format('YYYY/MM/DD')}/${fileName}`
const result = await ossClient.put(filePath, file)
console.log('result', result);
insertFn(result.url, '题库图片');
console.log('result', result)
insertFn(result.url, '题库图片')
// const { url } = await uploadFiles({ file, file_type: 'question' });
// insertFn(url, '题库图片');
setLoading(false);
},
setLoading(false)
}
},
uploadVideo: {
......@@ -139,75 +139,70 @@ const EditorReader = (props) => {
fieldName: 'video',
headers: {
'Content-Type': 'multipart/form-data',
Authorization: 'Bearer ' + localStorage.getItem('token') || '',
Authorization: 'Bearer ' + localStorage.getItem('token') || ''
},
maxFileSize: 10 * 1024 * 1024, // 10M
base64LimitSize: 5 * 1024, // 5kb 以下插入 base64 // 用户自定义上传图片
customUpload: async (file, insertFn) => {
setLoading(true);
setLoading(true)
if (!ossClient) {
console.error('ossClient还未初始化', ossClient);
return false;
console.error('ossClient还未初始化', ossClient)
return false
}
const result = await ossClient.put(file.name, file);
const result = await ossClient.put(file.name, file)
console.log('result', result);
insertFn(result.url, '题库视频');
console.log('result', result)
insertFn(result.url, '题库视频')
// const { url } = await uploadFiles({ file, file_type: 'question' });
// insertFn(url, '题库视频');
setLoading(false);
},
},
},
};
setLoading(false)
}
}
}
}
useEffect(() => {
getStsAuthToken();
}, []);
getStsAuthToken()
}, [])
// 及时销毁 editor ,重要!
useEffect(() => {
return () => {
if (editor === null) return;
editor.destroy();
setEditor(null);
};
}, [editor]);
if (editor === null) return
editor.destroy()
setEditor(null)
}
}, [editor])
useEffect(() => {
if (editor && ossClient) {
editor.setHtml(content);
editor.setHtml(content)
}
}, [editor, content, ossClient]);
}, [editor, content, ossClient])
return (
<div className='editor_customer_simple'>
<div className='editor-box' style={{ border: '1px solid #ccc', zIndex: 100 }}>
<Toolbar
editor={editor}
defaultConfig={toolbarConfig}
mode='default'
style={{ borderBottom: '1px solid #ccc' }}
/>
<div className="editor_customer_simple">
<div className="editor-box" style={{ border: '1px solid #ccc', zIndex: 100 }}>
<Toolbar editor={editor} defaultConfig={toolbarConfig} mode="default" style={{ borderBottom: '1px solid #ccc' }} />
<Spin spinning={loading}>
{ossClient && (
<Editor
defaultConfig={editorConfig}
value={html}
onCreated={setEditor}
onChange={(editor) => {
form.setFieldValue(fieldName, editor.getHtml());
setHtml(editor.getHtml());
onChange={editor => {
form.setFieldValue(fieldName, editor.getHtml())
setHtml(editor.getHtml())
}}
mode='default'
className='w-e-text'
mode="default"
className="w-e-text"
style={{ height: '400px', overflowY: 'hidden' }}
/>
)}
</Spin>
</div>
</div>
);
};
)
}
export default EditorReader;
export default EditorReader
import '@wangeditor/editor/dist/css/style.css'; // 引入 css
import React, { useState, useEffect } from 'react';
import { Editor, Toolbar } from '@wangeditor/editor-for-react';
import { useState, useEffect } from 'react'
import { Editor, Toolbar } from '@wangeditor/editor-for-react'
import '@wangeditor/editor/dist/css/style.css' // 引入 css
function MyEditor() {
// editor 实例
const [editor, setEditor] = useState(null); // JS 语法
const [editor, setEditor] = useState(null) // JS 语法
// 编辑器内容
const [html, setHtml] = useState('<p>hello</p>');
const [html, setHtml] = useState('<p>hello</p>')
// 模拟 ajax 请求,异步设置 html
useEffect(() => {
setTimeout(() => {
setHtml('<p>hello world</p>');
}, 1500);
}, []);
setHtml('<p>hello world</p>')
}, 1500)
}, [])
// 工具栏配置
const toolbarConfig = {};
const toolbarConfig = {}
// 编辑器配置
const editorConfig = {
placeholder: '请输入内容...',
};
placeholder: '请输入内容...'
}
useEffect(() => {
return () => {
if (editor === null) return;
editor.destroy();
setEditor(null);
};
}, [editor]);
if (editor === null) return
editor.destroy()
setEditor(null)
}
}, [editor])
return (
<>
<div style={{ border: '1px solid #ccc', zIndex: 100 }}>
<Toolbar
editor={editor}
defaultConfig={toolbarConfig}
mode='default'
style={{ borderBottom: '1px solid #ccc' }}
/>
<Toolbar editor={editor} defaultConfig={toolbarConfig} mode="default" style={{ borderBottom: '1px solid #ccc' }} />
<Editor
defaultConfig={editorConfig}
value={html}
onCreated={setEditor}
onChange={(editor) => setHtml(editor.getHtml())}
mode='default'
onChange={editor => setHtml(editor.getHtml())}
mode="default"
style={{ height: '500px', overflowY: 'hidden' }}
/>
</div>
<div style={{ marginTop: '15px' }}>{html}</div>
</>
);
)
}
export default MyEditor;
export default MyEditor
import React, { useState, useEffect } from 'react';
import { Layout, Spin } from 'antd';
import { Outlet, useNavigate, Link } from 'react-router-dom';
import { useDispatch } from 'react-redux';
import { setMenuRouter, setOperationPermissionsList } from '@/store/modules/user';
import React, { useState, useEffect } from 'react'
import { Layout, Spin } from 'antd'
import { Outlet, useNavigate, Link } from 'react-router-dom'
import { useDispatch } from 'react-redux'
import { setMenuRouter, setOperationPermissionsList } from '@/store/modules/user'
import UserInfo from './components/userinfo';
import UserMenu from './components/menu';
import BreadCrumbMenu from './components/breadMenu';
import UserInfo from './components/userinfo'
import UserMenu from './components/menu'
import BreadCrumbMenu from './components/breadMenu'
import './index.less';
import logo from '@/assets/images/logo.png';
import { getMenuList, checkSSOLogin } from './request';
import { generateToAntdMenus } from '@/utils/breadCrumb';
import './index.less'
import logo from '@/assets/images/logo.png'
import { getMenuList, checkSSOLogin } from './request'
import { generateToAntdMenus } from '@/utils/breadCrumb'
const { Header, Content, Footer } = Layout;
const { Header, Content, Footer } = Layout
const LayoutComponent = () => {
const dispatch = useDispatch();
const navigate = useNavigate();
const [loading, setLoading] = useState(false);
const [menuList, setMenuList] = useState([]);
const [menuRole, setMenuRole] = useState([]);
const [isSSOToken, setIsSSOToken] = useState(false);
const dispatch = useDispatch()
const navigate = useNavigate()
const [loading, setLoading] = useState(false)
const [menuList, setMenuList] = useState([])
const [menuRole, setMenuRole] = useState([])
const [isSSOToken, setIsSSOToken] = useState(false)
const getSSOToken = async () => {
const data = await checkSSOLogin();
const data = await checkSSOLogin()
if (data && data.token) {
localStorage.setItem('kiwi.token', data.token);
setIsSSOToken(data.token);
localStorage.setItem('kiwi.token', data.token)
setIsSSOToken(data.token)
}
}
};
// 获取菜单
const getMenu = async () => {
setLoading(true);
setLoading(true)
// 获取菜单内容
const { data, operation_permissions_list } = await getMenuList();
const { data, operation_permissions_list } = await getMenuList()
if (data && data.length) {
setMenuRole(data);
const menus = generateToAntdMenus(data, 'name', 'front_url', 'childs', 1);
setMenuList(menus);
dispatch(setMenuRouter(menus));
setMenuRole(data)
const menus = generateToAntdMenus(data, 'name', 'front_url', 'childs', 1)
setMenuList(menus)
dispatch(setMenuRouter(menus))
}
// 获取操作权限内容
if (operation_permissions_list && operation_permissions_list.length) {
dispatch(setOperationPermissionsList(operation_permissions_list));
dispatch(setOperationPermissionsList(operation_permissions_list))
}
setLoading(false)
}
setLoading(false);
};
useEffect(() => {
(async () => {
await getSSOToken();
await getMenu();
})();
}, []);
;(async () => {
await getSSOToken()
await getMenu()
})()
}, [])
useEffect(() => {
if (menuRole.length > 0 && location.pathname === '/') {
setLoading(true);
setLoading(true)
setTimeout(() => {
if (menuRole.length) {
navigate(menuRole[0].front_url);
navigate(menuRole[0].front_url)
}
setLoading(false);
}, 1000);
setLoading(false)
}, 1000)
}
}, [location.pathname, menuRole]);
}, [location.pathname, menuRole])
return (
<Layout className='layout-container'>
<Layout className="layout-container">
<Spin spinning={loading}>
<Header className='layout-header'>
<div className='header-logo'>
<Link to='/'>
<Header className="layout-header">
<div className="header-logo">
<Link to="/">
<img src={logo} />
</Link>
</div>
<UserMenu menuList={menuList} />
{menuList && menuList.length && <UserInfo />}
</Header>
<Content className='layout-content'>
<div className='crumb'>
<Content className="layout-content">
<div className="crumb">
<BreadCrumbMenu />
</div>
<div className='content-sandbox'>
<div className='content-pro' style={{ padding: 20 }}>
<div className="content-sandbox">
<div className="content-pro" style={{ padding: 20 }}>
<Outlet aa={2} />
</div>
</div>
</Content>
</Spin>
</Layout>
);
};
)
}
export default LayoutComponent;
export default LayoutComponent
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
// import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { BrowserRouter } from 'react-router-dom'
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import store, { persistor } from '@/store';
import { Provider } from 'react-redux'
import { PersistGate } from 'redux-persist/integration/react'
import store, { persistor } from '@/store'
import { ConfigProvider, App } from 'antd';
import zhCN from 'antd/locale/zh_CN';
import { ConfigProvider, App } from 'antd'
import 'dayjs/locale/zh-cn'
import zhCN from 'antd/locale/zh_CN'
// import 'dayjs/locale/zh-cn';
// import dayjs from 'dayjs';
// // 汉化
// dayjs.locale('zh-cn');
import MyApp from './App.jsx'
import MyApp from './App.jsx';
import 'dayjs/locale/zh-cn';
ReactDOM.createRoot(document.getElementById('root')).render(
// <React.StrictMode>
createRoot(document.getElementById('root')).render(
// <StrictMode>
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<ConfigProvider locale={zhCN} theme={{ token: { colorPrimary: '#ab1941' } }}>
......@@ -30,6 +24,6 @@ ReactDOM.createRoot(document.getElementById('root')).render(
</App>
</ConfigProvider>
</PersistGate>
</Provider>,
// </React.StrictMode>,
);
</Provider>
// </StrictMode>
)
import { fetchEventSource } from '@fortaine/fetch-event-source';
import { fetchEventSource } from '@fortaine/fetch-event-source'
function getToken() {
return window.localStorage.getItem('kiwi.token') ? window.localStorage.getItem('kiwi.token') : '';
return window.localStorage.getItem('kiwi.token') ? window.localStorage.getItem('kiwi.token') : ''
}
export default async function fetchEventSourceFn(url, options) {
const token = getToken();
const token = getToken()
// 应用设置
const appId = 'TzEU5jPk2tu80266';
const appSecret = '0a006048a4480481b18fef1405120b83';
const timestamp = Math.floor(Date.now() / 1000);
const appId = 'TzEU5jPk2tu80266'
const appSecret = '0a006048a4480481b18fef1405120b83'
const timestamp = Math.floor(Date.now() / 1000)
const defaultHeaders = {
Authorization: token,
AppId: appId,
AppSecret: appSecret,
Timestamp: timestamp,
'Content-Type': 'application/json',
};
'Content-Type': 'application/json'
}
const defaultOptions = {
method: 'POST',
headers: { ...defaultHeaders },
async onopen(response) {
if (response.ok) {
return response;
return response
} else {
throw response;
throw response
}
},
onmessage(res) {
const message = JSON.parse(res.data);
const message = JSON.parse(res.data)
console.log(res.data);
},
};
await fetchEventSource(url, { ...defaultOptions, ...options });
console.log(res.data)
}
}
await fetchEventSource(url, { ...defaultOptions, ...options })
}
import OSS from 'ali-oss'
import { getSTSToken } from '@/api/base'
// 获取token
export async function getToken() {
const { data } = await getSTSToken()
return { ...data, accessKeyId: data.AccessKeyId, accessKeySecret: data.AccessKeySecret, stsToken: data.SecurityToken }
}
// 创建oss
export async function createOSS() {
const store = new OSS({
bucket: 'zxts-book-file',
...(await getToken()),
refreshSTSToken: async () => {
return await getToken()
},
refreshSTSTokenInterval: 14 * 60 * 1000
})
return store
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论