From 89d8f3057029b02f04c62f4c968200bd29012cab Mon Sep 17 00:00:00 2001 From: vapao Date: Wed, 27 Dec 2023 23:47:26 +0800 Subject: [PATCH] update --- spug_web2/package.json | 1 - spug_web2/src/App.jsx | 17 ++--- spug_web2/src/components/STable/Setting.jsx | 62 ++++++++++++++++++ spug_web2/src/components/STable/index.jsx | 63 +++++++++++++++++++ .../src/components/STable/index.module.scss | 15 +++++ spug_web2/src/i18n.js | 6 +- spug_web2/src/layout/Header.jsx | 5 +- spug_web2/src/libs/app.js | 45 +++++++++++++ spug_web2/src/libs/http.js | 4 +- spug_web2/src/libs/index.js | 4 +- spug_web2/src/libs/session.js | 43 ------------- spug_web2/src/libs/utils.js | 12 ++++ spug_web2/src/routes.jsx | 8 +-- 13 files changed, 221 insertions(+), 64 deletions(-) create mode 100644 spug_web2/src/components/STable/Setting.jsx create mode 100644 spug_web2/src/components/STable/index.jsx create mode 100644 spug_web2/src/components/STable/index.module.scss create mode 100644 spug_web2/src/libs/app.js delete mode 100644 spug_web2/src/libs/session.js diff --git a/spug_web2/package.json b/spug_web2/package.json index 1a2c026..e773461 100644 --- a/spug_web2/package.json +++ b/spug_web2/package.json @@ -10,7 +10,6 @@ "preview": "vite preview" }, "dependencies": { - "@ant-design/icons": "^5.2.6", "antd": "^5.12.4", "dayjs": "^1.11.10", "i18next": "^23.7.7", diff --git a/spug_web2/src/App.jsx b/spug_web2/src/App.jsx index 6c210a7..4321343 100644 --- a/spug_web2/src/App.jsx +++ b/spug_web2/src/App.jsx @@ -1,24 +1,25 @@ import {createBrowserRouter, RouterProvider} from 'react-router-dom' import {ConfigProvider, App as AntdApp, theme} from 'antd' +import {IconContext} from 'react-icons' import zhCN from 'antd/locale/zh_CN' import enUS from 'antd/locale/en_US' import dayjs from 'dayjs' import routes from './routes.jsx' -import {session, SContext} from '@/libs' +import {app, SContext} from '@/libs' import {useImmer} from 'use-immer' import './i18n.js' -dayjs.locale(session.lang) +dayjs.locale(app.lang) const router = createBrowserRouter(routes) function App() { - const [S, updateS] = useImmer({theme: session.theme}) + const [S, updateS] = useImmer({theme: app.theme}) return ( - - - + + + + + ) diff --git a/spug_web2/src/components/STable/Setting.jsx b/spug_web2/src/components/STable/Setting.jsx new file mode 100644 index 0000000..9b53d99 --- /dev/null +++ b/spug_web2/src/components/STable/Setting.jsx @@ -0,0 +1,62 @@ +import {useState, useEffect} from 'react' +import {Button, Checkbox, Flex, Popover} from 'antd' +import {IoSettingsOutline} from 'react-icons/io5' +import {app, clsNames} from '@/libs' + + +function Setting(props) { + const {skey, columns, setCols} = props + const [state, setState] = useState(app.getStable(skey)) + + useEffect(() => { + const newColumns = [] + for (const item of columns) { + if (state[item.key] ?? !item.hidden) { + newColumns.push(item) + } + } + setCols(newColumns) + }, [state]); + + function handleChange(e) { + const {value, checked} = e.target + const newState = {...state, [value]: checked} + setState(newState) + app.updateStable(skey, newState) + } + + function handleReset() { + setState({}) + app.updateStable(skey, {}) + } + + return ( + +
{t('展示字段')}
+ + )} + trigger="click" + placement="bottomRight" + content={( + + {columns.map((item, index) => ( + + {item.title} + + ))} + + )}> +
+ +
+
+ ) +} + +export default Setting diff --git a/spug_web2/src/components/STable/index.jsx b/spug_web2/src/components/STable/index.jsx new file mode 100644 index 0000000..a5fa2f1 --- /dev/null +++ b/spug_web2/src/components/STable/index.jsx @@ -0,0 +1,63 @@ +import {useRef, useState} from 'react' +import {Card, Table, Flex, Divider} from 'antd' +import {IoExpand, IoContract, IoReloadOutline} from 'react-icons/io5' +import {clsNames} from '@/libs' +import Setting from './Setting.jsx' +import css from './index.module.scss' + +function Stable(props) { + const {skey, loading, columns, dataSource, actions, pagination} = props + const ref = useRef(); + const [cols, setCols] = useState([]) + const [isFull, setIsFull] = useState(false) + + if (!skey) throw new Error('skey is required') + + function handleFullscreen() { + if (ref.current && document.fullscreenEnabled) { + if (document.fullscreenElement) { + document.exitFullscreen() + setIsFull(false) + } else { + ref.current.requestFullscreen() + setIsFull(true) + } + } + } + + return ( + + + + {actions} + {actions.length ? : null} + + + {isFull ? ( + + ) : ( + + )} + + + + + ) +} + +Stable.defaultProps = { + sKey: null, + loading: false, + actions: [], + defaultFields: [], + pagination: { + showSizeChanger: true, + showLessItems: true, + showTotal: total => t('page', {total}), + pageSizeOptions: ['10', '20', '50', '100'] + }, + onReload: () => { + }, +} + +export default Stable \ No newline at end of file diff --git a/spug_web2/src/components/STable/index.module.scss b/spug_web2/src/components/STable/index.module.scss new file mode 100644 index 0000000..a7b411c --- /dev/null +++ b/spug_web2/src/components/STable/index.module.scss @@ -0,0 +1,15 @@ +.stable { + :global(.ant-pagination) { + margin: 16px 0 0 !important; + } +} + +.toolbar { + margin-bottom: 12px; + + .icon { + font-size: 18px; + cursor: pointer; + } +} + diff --git a/spug_web2/src/i18n.js b/spug_web2/src/i18n.js index 61ebb85..a89b29d 100644 --- a/spug_web2/src/i18n.js +++ b/spug_web2/src/i18n.js @@ -1,9 +1,9 @@ import i18n from 'i18next' import {initReactI18next} from 'react-i18next' -import {session} from '@/libs' +import {app} from '@/libs' i18n.use(initReactI18next).init({ - lng: session.lang, + lng: app.lang, resources: { en: { translation: { @@ -16,6 +16,8 @@ i18n.use(initReactI18next).init({ '重置': 'Reset', '展示字段': 'Columns Display', '年龄': 'Age', + // buttons + '新建': 'Add', 'page': 'Total {{total}} items', } }, diff --git a/spug_web2/src/layout/Header.jsx b/spug_web2/src/layout/Header.jsx index 586431c..528a8a1 100644 --- a/spug_web2/src/layout/Header.jsx +++ b/spug_web2/src/layout/Header.jsx @@ -1,7 +1,6 @@ import {useContext, useEffect} from 'react' import {Dropdown, Flex, Layout, theme as antdTheme} from 'antd' -import {AiOutlineTranslation} from 'react-icons/ai' -import {IoMoon, IoSunny} from 'react-icons/io5' +import {IoMoon, IoSunny, IoLanguage} from 'react-icons/io5' import {SContext} from '@/libs' import css from './index.module.scss' import i18n from '@/i18n.js' @@ -44,7 +43,7 @@ function Header() {
admin
- +
diff --git a/spug_web2/src/libs/app.js b/spug_web2/src/libs/app.js new file mode 100644 index 0000000..ce191a6 --- /dev/null +++ b/spug_web2/src/libs/app.js @@ -0,0 +1,45 @@ +import {isSubArray, loadJSONStorage} from "@/libs/utils.js"; + +class App { + constructor() { + this.lang = localStorage.getItem('lang') || 'zh'; + this.theme = localStorage.getItem('theme') || 'light'; + this.stable = loadJSONStorage('stable', {}); + this.session = loadJSONStorage('session', {}); + } + + get access_token() { + return this.session['access_token'] || ''; + } + + get nickname() { + return this.session['nickname']; + } + + hasPermission(code) { + const {isSuper, permissions} = this.session; + if (!code || isSuper) return true; + for (let item of code.split('|')) { + if (isSubArray(permissions, item.split('&'))) { + return true + } + } + return false + } + + updateSession(data) { + Object.assign(this.session, data); + localStorage.setItem('session', JSON.stringify(this.session)); + } + + getStable(key) { + return this.stable[key] ?? {}; + } + + updateStable(key, data) { + this.stable[key] = data; + localStorage.setItem('stable', JSON.stringify(this.stable)); + } +} + +export default new App(); \ No newline at end of file diff --git a/spug_web2/src/libs/http.js b/spug_web2/src/libs/http.js index 27cb706..efc9665 100644 --- a/spug_web2/src/libs/http.js +++ b/spug_web2/src/libs/http.js @@ -1,6 +1,6 @@ import useSWR from 'swr' import {message} from 'antd' -import session from '@/libs/session' +import app from '@/libs/app.js' import {redirect} from 'react-router-dom' function fetcher(resource, init) { @@ -33,7 +33,7 @@ function SWRGet(url, params) { } function request(method, url, params) { - const init = {method, headers: {'X-Token': session.access_token}} + const init = {method, headers: {'X-Token': app.accessToken}} if (['GET', 'DELETE'].includes(method)) { if (params) url = `${url}?${new URLSearchParams(params).toString()}` return fetcher(url, init) diff --git a/spug_web2/src/libs/index.js b/spug_web2/src/libs/index.js index e0e157e..0867c20 100644 --- a/spug_web2/src/libs/index.js +++ b/spug_web2/src/libs/index.js @@ -1,11 +1,11 @@ import React from 'react' import http from './http' -import session from './session' +import app from './app.js' const SContext = React.createContext({}) export * from './utils.js' export { + app, http, - session, SContext, } diff --git a/spug_web2/src/libs/session.js b/spug_web2/src/libs/session.js deleted file mode 100644 index 03fa021..0000000 --- a/spug_web2/src/libs/session.js +++ /dev/null @@ -1,43 +0,0 @@ -import {isSubArray} from "@/libs/utils.js"; - -class Session { - constructor() { - this._session = {}; - this.lang = localStorage.getItem('lang') || 'zh'; - this.theme = localStorage.getItem('theme') || 'light'; - const tmp = localStorage.getItem('session'); - if (tmp) { - try { - this._session = JSON.parse(tmp); - } catch (e) { - localStorage.removeItem('session'); - } - } - } - - get access_token() { - return this._session['access_token'] || ''; - } - - get nickname() { - return this._session['nickname']; - } - - hasPermission(code) { - const {isSuper, permissions} = this._session; - if (!code || isSuper) return true; - for (let item of code.split('|')) { - if (isSubArray(permissions, item.split('&'))) { - return true - } - } - return false - } - - update(data) { - Object.assign(this._session, data); - localStorage.setItem('session', JSON.stringify(this._session)); - } -} - -export default new Session(); \ No newline at end of file diff --git a/spug_web2/src/libs/utils.js b/spug_web2/src/libs/utils.js index 5539857..384e9d6 100644 --- a/spug_web2/src/libs/utils.js +++ b/spug_web2/src/libs/utils.js @@ -42,4 +42,16 @@ export function includes(s, keys) { } else { return isInclude(s, keys) } +} + +export function loadJSONStorage(key, defaultValue = null) { + const tmp = localStorage.getItem(key) + if (tmp) { + try { + return JSON.parse(tmp) + } catch (e) { + localStorage.removeItem(key) + } + } + return defaultValue } \ No newline at end of file diff --git a/spug_web2/src/routes.jsx b/spug_web2/src/routes.jsx index f97c77f..3994486 100644 --- a/spug_web2/src/routes.jsx +++ b/spug_web2/src/routes.jsx @@ -1,4 +1,4 @@ -import {AiOutlineDesktop, AiOutlineCloudServer, AiOutlineCluster} from 'react-icons/ai' +import {FaDesktop, FaServer, FaSitemap} from 'react-icons/fa6' import Layout from './layout/index.jsx' import ErrorPage from './error-page.jsx' import LoginIndex from './pages/login/index.jsx' @@ -16,18 +16,18 @@ let routes = [ path: 'home', element: , title: t('工作台'), - icon: , + icon: , }, { path: 'host', element: , title: t('主机管理'), - icon: + icon: }, { path: 'exec', title: t('批量执行'), - icon: , + icon: , children: [ { path: 'task',