update
This commit is contained in:
parent
521255c1af
commit
9add82dc7e
@ -20,12 +20,14 @@
|
||||
"@ant-design/icons": "^6.0.0",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.0",
|
||||
"@kevisual/cache": "^0.0.1",
|
||||
"@kevisual/codemirror": "workspace:*",
|
||||
"@kevisual/components": "workspace:*",
|
||||
"@kevisual/container": "1.0.0",
|
||||
"@kevisual/query": "^0.0.15",
|
||||
"@kevisual/query-config": "workspace:*",
|
||||
"@kevisual/query-login": "workspace:*",
|
||||
"@kevisual/query-upload": "workspace:*",
|
||||
"@kevisual/resources": "workspace:*",
|
||||
"@monaco-editor/react": "^4.7.0",
|
||||
"@mui/material": "^7.0.1",
|
||||
@ -47,6 +49,7 @@
|
||||
"qrcode": "^1.5.4",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react-dropzone": "^14.3.8",
|
||||
"react-hook-form": "^7.55.0",
|
||||
"react-i18next": "^15.4.1",
|
||||
"react-resizable-panels": "^2.1.7",
|
||||
|
@ -20,6 +20,7 @@ type BaseEditorOpts = {
|
||||
open?: boolean;
|
||||
overrideList?: Completion[];
|
||||
};
|
||||
onChange?: (value: string) => void;
|
||||
};
|
||||
export class BaseEditor {
|
||||
editor?: EditorView;
|
||||
@ -32,7 +33,7 @@ export class BaseEditor {
|
||||
open?: boolean;
|
||||
overrideList?: Completion[];
|
||||
};
|
||||
|
||||
onChange?: (value: string) => void;
|
||||
constructor(opts?: BaseEditorOpts) {
|
||||
this.filename = opts?.filename || '';
|
||||
this.language = opts?.language || 'typescript';
|
||||
@ -43,6 +44,7 @@ export class BaseEditor {
|
||||
}
|
||||
this.autoComplete = opts?.autoComplete || { open: true, overrideList: [] };
|
||||
this.onChangeCompartment = new Compartment(); // 创建一个用于 onChange 的独立扩展
|
||||
this.onChange = opts?.onChange;
|
||||
}
|
||||
onEditorChange(view: EditorView) {
|
||||
console.log('onChange', view);
|
||||
@ -54,6 +56,7 @@ export class BaseEditor {
|
||||
this.el = el;
|
||||
const onChangeCompartment = this.onChangeCompartment!;
|
||||
const language = this.language;
|
||||
const onChange = this.onChange;
|
||||
const extensions: Extension[] = [
|
||||
vscodeLight,
|
||||
formatKeymap,
|
||||
@ -91,7 +94,15 @@ export class BaseEditor {
|
||||
parent: el,
|
||||
extensions: [
|
||||
...extensions, //
|
||||
onChangeCompartment.of([]),
|
||||
onChangeCompartment.of([
|
||||
[
|
||||
EditorView.updateListener.of((update) => {
|
||||
if (update.changes.length > 0) {
|
||||
onChange?.(update.state.doc.toString());
|
||||
}
|
||||
}),
|
||||
],
|
||||
]),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ export { PermissionManager } from './pages/file/modules/PermissionManager.tsx';
|
||||
export { PermissionModal, usePermissionModal } from './pages/file/modules/PermissionModal.tsx';
|
||||
export { iText } from './i-text/index.ts';
|
||||
|
||||
export { uploadFiles, uploadFileChunked, getDirectoryAndName, toFile, createDirectory } from './pages/upload/app';
|
||||
export { uploadFiles, uploadFileChunked, getDirectoryAndName, createDirectory } from './pages/upload/app';
|
||||
export { DialogDirectory, DialogDeleteDirectory } from './pages/upload/DialogDirectory';
|
||||
|
||||
export { uploadChunkV2 } from './pages/upload/v2/upload-chunk';
|
||||
export { uploadChunkV2, toFile } from './pages/upload/v2/upload-chunk';
|
||||
|
@ -4,7 +4,7 @@ import { Id, toast } from 'react-toastify';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { toastLogin } from '@kevisual/resources/pages/message/ToastLogin';
|
||||
import { uploadFileChunked, UploadProgress } from '@kevisual/query-upload/query-upload';
|
||||
|
||||
import { toFile } from '@kevisual/query-upload/query-upload';
|
||||
export type ConvertOpts = {
|
||||
appKey?: string;
|
||||
version?: string;
|
||||
@ -12,6 +12,10 @@ export type ConvertOpts = {
|
||||
directory?: string;
|
||||
isPublic?: boolean;
|
||||
filename?: string;
|
||||
/**
|
||||
* 是否不检查应用文件, 默认 true,默认不检测
|
||||
*/
|
||||
noCheckAppFiles?: boolean;
|
||||
};
|
||||
|
||||
export const uploadChunkV2 = async (file: File, opts: ConvertOpts) => {
|
||||
@ -53,3 +57,4 @@ export const uploadChunkV2 = async (file: File, opts: ConvertOpts) => {
|
||||
});
|
||||
return result;
|
||||
};
|
||||
export { toFile };
|
||||
|
197
pnpm-lock.yaml
generated
197
pnpm-lock.yaml
generated
@ -17,6 +17,9 @@ importers:
|
||||
'@emotion/styled':
|
||||
specifier: ^11.14.0
|
||||
version: 11.14.0(@emotion/react@11.14.0(@types/react@19.1.0)(react@19.1.0))(@types/react@19.1.0)(react@19.1.0)
|
||||
'@kevisual/cache':
|
||||
specifier: ^0.0.1
|
||||
version: 0.0.1(rollup@4.37.0)(tslib@2.8.1)(typescript@5.8.2)
|
||||
'@kevisual/codemirror':
|
||||
specifier: workspace:*
|
||||
version: link:packages/codemirror
|
||||
@ -35,6 +38,9 @@ importers:
|
||||
'@kevisual/query-login':
|
||||
specifier: workspace:*
|
||||
version: link:submodules/query-login
|
||||
'@kevisual/query-upload':
|
||||
specifier: workspace:*
|
||||
version: link:submodules/query-upload
|
||||
'@kevisual/resources':
|
||||
specifier: workspace:*
|
||||
version: link:packages/resources
|
||||
@ -98,6 +104,9 @@ importers:
|
||||
react-dom:
|
||||
specifier: 19.1.0
|
||||
version: 19.1.0(react@19.1.0)
|
||||
react-dropzone:
|
||||
specifier: ^14.3.8
|
||||
version: 14.3.8(react@19.1.0)
|
||||
react-hook-form:
|
||||
specifier: ^7.55.0
|
||||
version: 7.55.0(react@19.1.0)
|
||||
@ -339,29 +348,29 @@ importers:
|
||||
dependencies:
|
||||
'@emotion/react':
|
||||
specifier: ^11.14.0
|
||||
version: 11.14.0(@types/react@19.1.0)(react@19.0.0)
|
||||
version: 11.14.0(@types/react@19.1.0)(react@19.1.0)
|
||||
'@emotion/styled':
|
||||
specifier: ^11.14.0
|
||||
version: 11.14.0(@emotion/react@11.14.0(@types/react@19.1.0)(react@19.0.0))(@types/react@19.1.0)(react@19.0.0)
|
||||
version: 11.14.0(@emotion/react@11.14.0(@types/react@19.1.0)(react@19.1.0))(@types/react@19.1.0)(react@19.1.0)
|
||||
'@mui/material':
|
||||
specifier: ^6.4.7
|
||||
version: 6.4.7(@emotion/react@11.14.0(@types/react@19.1.0)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.0)(react@19.0.0))(@types/react@19.1.0)(react@19.0.0))(@types/react@19.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
specifier: ^7.0.1
|
||||
version: 7.0.1(@emotion/react@11.14.0(@types/react@19.1.0)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.0)(react@19.1.0))(@types/react@19.1.0)(react@19.1.0))(@types/react@19.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
react:
|
||||
specifier: 19.0.0
|
||||
version: 19.0.0
|
||||
specifier: 19.1.0
|
||||
version: 19.1.0
|
||||
react-dom:
|
||||
specifier: 19.0.0
|
||||
version: 19.0.0(react@19.0.0)
|
||||
specifier: 19.1.0
|
||||
version: 19.1.0(react@19.1.0)
|
||||
react-hook-form:
|
||||
specifier: ^7.54.2
|
||||
version: 7.54.2(react@19.0.0)
|
||||
specifier: ^7.55.0
|
||||
version: 7.55.0(react@19.1.0)
|
||||
devDependencies:
|
||||
clsx:
|
||||
specifier: ^2.1.1
|
||||
version: 2.1.1
|
||||
tailwind-merge:
|
||||
specifier: ^3.0.2
|
||||
version: 3.0.2
|
||||
specifier: ^3.1.0
|
||||
version: 3.1.0
|
||||
|
||||
packages/kevisual-official:
|
||||
dependencies:
|
||||
@ -1548,35 +1557,12 @@ packages:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
'@mui/core-downloads-tracker@6.4.7':
|
||||
resolution: {integrity: sha512-XjJrKFNt9zAKvcnoIIBquXyFyhfrHYuttqMsoDS7lM7VwufYG4fAPw4kINjBFg++fqXM2BNAuWR9J7XVIuKIKg==}
|
||||
|
||||
'@mui/core-downloads-tracker@6.4.8':
|
||||
resolution: {integrity: sha512-vjP4+A1ybyCRhDZC7r5EPWu/gLseFZxaGyPdDl94vzVvk6Yj6gahdaqcjbhkaCrJjdZj90m3VioltWPAnWF/zw==}
|
||||
|
||||
'@mui/core-downloads-tracker@7.0.1':
|
||||
resolution: {integrity: sha512-T5DNVnSD9pMbj4Jk/Uphz+yvj9dfpl2+EqsOuJtG12HxEihNG5pd3qzX5yM1Id4dDwKRvM3dPVcxyzavTFhJeA==}
|
||||
|
||||
'@mui/material@6.4.7':
|
||||
resolution: {integrity: sha512-K65StXUeGAtFJ4ikvHKtmDCO5Ab7g0FZUu2J5VpoKD+O6Y3CjLYzRi+TMlI3kaL4CL158+FccMoOd/eaddmeRQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
'@emotion/react': ^11.5.0
|
||||
'@emotion/styled': ^11.3.0
|
||||
'@mui/material-pigment-css': ^6.4.7
|
||||
'@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react: ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
peerDependenciesMeta:
|
||||
'@emotion/react':
|
||||
optional: true
|
||||
'@emotion/styled':
|
||||
optional: true
|
||||
'@mui/material-pigment-css':
|
||||
optional: true
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@mui/material@6.4.8':
|
||||
resolution: {integrity: sha512-5S9UTjKZZBd9GfbcYh/nYfD9cv6OXmj5Y7NgKYfk7JcSoshp8/pW5zP4wecRiroBSZX8wcrywSgogpVNO+5W0Q==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
@ -1617,16 +1603,6 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@mui/private-theming@6.4.6':
|
||||
resolution: {integrity: sha512-T5FxdPzCELuOrhpA2g4Pi6241HAxRwZudzAuL9vBvniuB5YU82HCmrARw32AuCiyTfWzbrYGGpZ4zyeqqp9RvQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
'@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react: ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@mui/private-theming@6.4.8':
|
||||
resolution: {integrity: sha512-sWwQoNSn6elsPTAtSqCf+w5aaGoh7AASURNmpy+QTTD/zwJ0Jgwt0ZaaP6mXq2IcgHxYnYloM/+vJgHPMkRKTQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
@ -1647,19 +1623,6 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@mui/styled-engine@6.4.6':
|
||||
resolution: {integrity: sha512-vSWYc9ZLX46be5gP+FCzWVn5rvDr4cXC5JBZwSIkYk9xbC7GeV+0kCvB8Q6XLFQJy+a62bbqtmdwS4Ghi9NBlQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
'@emotion/react': ^11.4.1
|
||||
'@emotion/styled': ^11.3.0
|
||||
react: ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
peerDependenciesMeta:
|
||||
'@emotion/react':
|
||||
optional: true
|
||||
'@emotion/styled':
|
||||
optional: true
|
||||
|
||||
'@mui/styled-engine@6.4.8':
|
||||
resolution: {integrity: sha512-oyjx1b1FvUCI85ZMO4trrjNxGm90eLN3Ohy0AP/SqK5gWvRQg1677UjNf7t6iETOKAleHctJjuq0B3aXO2gtmw==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
@ -1686,22 +1649,6 @@ packages:
|
||||
'@emotion/styled':
|
||||
optional: true
|
||||
|
||||
'@mui/system@6.4.7':
|
||||
resolution: {integrity: sha512-7wwc4++Ak6tGIooEVA9AY7FhH2p9fvBMORT4vNLMAysH3Yus/9B9RYMbrn3ANgsOyvT3Z7nE+SP8/+3FimQmcg==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
'@emotion/react': ^11.5.0
|
||||
'@emotion/styled': ^11.3.0
|
||||
'@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react: ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
peerDependenciesMeta:
|
||||
'@emotion/react':
|
||||
optional: true
|
||||
'@emotion/styled':
|
||||
optional: true
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@mui/system@6.4.8':
|
||||
resolution: {integrity: sha512-gV7iBHoqlsIenU2BP0wq14BefRoZcASZ/4LeyuQglayBl+DfLX5rEd3EYR3J409V2EZpR0NOM1LATAGlNk2cyA==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
@ -1734,14 +1681,6 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@mui/types@7.2.21':
|
||||
resolution: {integrity: sha512-6HstngiUxNqLU+/DPqlUJDIPbzUBxIVHb1MmXP0eTWDIROiCR2viugXpEif0PPe2mLqqakPzzRClWAnK+8UJww==}
|
||||
peerDependencies:
|
||||
'@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@mui/types@7.2.24':
|
||||
resolution: {integrity: sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==}
|
||||
peerDependencies:
|
||||
@ -1758,16 +1697,6 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@mui/utils@6.4.6':
|
||||
resolution: {integrity: sha512-43nZeE1pJF2anGafNydUcYFPtHwAqiBiauRtaMvurdrZI3YrUjHkAu43RBsxef7OFtJMXGiHFvq43kb7lig0sA==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
'@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react: ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@mui/utils@6.4.8':
|
||||
resolution: {integrity: sha512-C86gfiZ5BfZ51KqzqoHi1WuuM2QdSKoFhbkZeAfQRB+jCc4YNhhj11UXFVMMsqBgZ+Zy8IHNJW3M9Wj/LOwRXQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
@ -6280,33 +6209,10 @@ snapshots:
|
||||
react: 19.1.0
|
||||
react-dom: 19.1.0(react@19.1.0)
|
||||
|
||||
'@mui/core-downloads-tracker@6.4.7': {}
|
||||
|
||||
'@mui/core-downloads-tracker@6.4.8': {}
|
||||
|
||||
'@mui/core-downloads-tracker@7.0.1': {}
|
||||
|
||||
'@mui/material@6.4.7(@emotion/react@11.14.0(@types/react@19.1.0)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.0)(react@19.0.0))(@types/react@19.1.0)(react@19.0.0))(@types/react@19.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.0
|
||||
'@mui/core-downloads-tracker': 6.4.7
|
||||
'@mui/system': 6.4.7(@emotion/react@11.14.0(@types/react@19.1.0)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.0)(react@19.0.0))(@types/react@19.1.0)(react@19.0.0))(@types/react@19.1.0)(react@19.0.0)
|
||||
'@mui/types': 7.2.21(@types/react@19.1.0)
|
||||
'@mui/utils': 6.4.6(@types/react@19.1.0)(react@19.0.0)
|
||||
'@popperjs/core': 2.11.8
|
||||
'@types/react-transition-group': 4.4.12(@types/react@19.1.0)
|
||||
clsx: 2.1.1
|
||||
csstype: 3.1.3
|
||||
prop-types: 15.8.1
|
||||
react: 19.0.0
|
||||
react-dom: 19.0.0(react@19.0.0)
|
||||
react-is: 19.1.0
|
||||
react-transition-group: 4.4.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
optionalDependencies:
|
||||
'@emotion/react': 11.14.0(@types/react@19.1.0)(react@19.0.0)
|
||||
'@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.1.0)(react@19.0.0))(@types/react@19.1.0)(react@19.0.0)
|
||||
'@types/react': 19.1.0
|
||||
|
||||
'@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.10
|
||||
@ -6370,15 +6276,6 @@ snapshots:
|
||||
'@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.1.0)(react@19.1.0))(@types/react@19.1.0)(react@19.1.0)
|
||||
'@types/react': 19.1.0
|
||||
|
||||
'@mui/private-theming@6.4.6(@types/react@19.1.0)(react@19.0.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.10
|
||||
'@mui/utils': 6.4.8(@types/react@19.1.0)(react@19.0.0)
|
||||
prop-types: 15.8.1
|
||||
react: 19.0.0
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.0
|
||||
|
||||
'@mui/private-theming@6.4.8(@types/react@19.0.12)(react@19.0.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.10
|
||||
@ -6406,19 +6303,6 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.0
|
||||
|
||||
'@mui/styled-engine@6.4.6(@emotion/react@11.14.0(@types/react@19.1.0)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.0)(react@19.0.0))(@types/react@19.1.0)(react@19.0.0))(react@19.0.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.10
|
||||
'@emotion/cache': 11.14.0
|
||||
'@emotion/serialize': 1.3.3
|
||||
'@emotion/sheet': 1.4.0
|
||||
csstype: 3.1.3
|
||||
prop-types: 15.8.1
|
||||
react: 19.0.0
|
||||
optionalDependencies:
|
||||
'@emotion/react': 11.14.0(@types/react@19.1.0)(react@19.0.0)
|
||||
'@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.1.0)(react@19.0.0))(@types/react@19.1.0)(react@19.0.0)
|
||||
|
||||
'@mui/styled-engine@6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(react@19.0.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.10
|
||||
@ -6458,22 +6342,6 @@ snapshots:
|
||||
'@emotion/react': 11.14.0(@types/react@19.1.0)(react@19.1.0)
|
||||
'@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.1.0)(react@19.1.0))(@types/react@19.1.0)(react@19.1.0)
|
||||
|
||||
'@mui/system@6.4.7(@emotion/react@11.14.0(@types/react@19.1.0)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.0)(react@19.0.0))(@types/react@19.1.0)(react@19.0.0))(@types/react@19.1.0)(react@19.0.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.10
|
||||
'@mui/private-theming': 6.4.6(@types/react@19.1.0)(react@19.0.0)
|
||||
'@mui/styled-engine': 6.4.6(@emotion/react@11.14.0(@types/react@19.1.0)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.0)(react@19.0.0))(@types/react@19.1.0)(react@19.0.0))(react@19.0.0)
|
||||
'@mui/types': 7.2.24(@types/react@19.1.0)
|
||||
'@mui/utils': 6.4.8(@types/react@19.1.0)(react@19.0.0)
|
||||
clsx: 2.1.1
|
||||
csstype: 3.1.3
|
||||
prop-types: 15.8.1
|
||||
react: 19.0.0
|
||||
optionalDependencies:
|
||||
'@emotion/react': 11.14.0(@types/react@19.1.0)(react@19.0.0)
|
||||
'@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.1.0)(react@19.0.0))(@types/react@19.1.0)(react@19.0.0)
|
||||
'@types/react': 19.1.0
|
||||
|
||||
'@mui/system@6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.10
|
||||
@ -6522,10 +6390,6 @@ snapshots:
|
||||
'@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.1.0)(react@19.1.0))(@types/react@19.1.0)(react@19.1.0)
|
||||
'@types/react': 19.1.0
|
||||
|
||||
'@mui/types@7.2.21(@types/react@19.1.0)':
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.0
|
||||
|
||||
'@mui/types@7.2.24(@types/react@19.0.12)':
|
||||
optionalDependencies:
|
||||
'@types/react': 19.0.12
|
||||
@ -6540,18 +6404,6 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.0
|
||||
|
||||
'@mui/utils@6.4.6(@types/react@19.1.0)(react@19.0.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.10
|
||||
'@mui/types': 7.2.24(@types/react@19.1.0)
|
||||
'@types/prop-types': 15.7.14
|
||||
clsx: 2.1.1
|
||||
prop-types: 15.8.1
|
||||
react: 19.0.0
|
||||
react-is: 19.1.0
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.0
|
||||
|
||||
'@mui/utils@6.4.8(@types/react@19.0.12)(react@19.0.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.10
|
||||
@ -9603,6 +9455,13 @@ snapshots:
|
||||
prop-types: 15.8.1
|
||||
react: 19.0.0
|
||||
|
||||
react-dropzone@14.3.8(react@19.1.0):
|
||||
dependencies:
|
||||
attr-accept: 2.2.5
|
||||
file-selector: 2.1.2
|
||||
prop-types: 15.8.1
|
||||
react: 19.1.0
|
||||
|
||||
react-hook-form@7.54.2(react@19.0.0):
|
||||
dependencies:
|
||||
react: 19.0.0
|
||||
|
@ -87,5 +87,9 @@
|
||||
"delete_directory_success": "Delete directory success",
|
||||
"create_directory": "Create Directory",
|
||||
"create_directory_success": "Create directory success",
|
||||
"Apply Change Name": "Apply Change Name"
|
||||
"Apply Change Name": "Apply Change Name",
|
||||
"Tips": "Tips",
|
||||
"Check username introduction": "Check your username, if the username is not available, you can contact the customer service to modify.",
|
||||
"Delete and remove file": "Delete and remove file",
|
||||
"Delete App Introduce": "Delete App, when you click the delete button, the file will be deleted from the file management system."
|
||||
}
|
@ -87,5 +87,9 @@
|
||||
"delete_directory_success": "删除目录成功",
|
||||
"create_directory": "创建目录",
|
||||
"create_directory_success": "创建目录成功",
|
||||
"Apply Change Name": "申请修改名称"
|
||||
"Apply Change Name": "申请修改名称",
|
||||
"Tips": "提示",
|
||||
"Check username introduction": "查看是否可以取名,如果当前用户名已存在,请更换其他用户名。其中用户名以@开头",
|
||||
"Delete and remove file": "删除并删除文件",
|
||||
"Delete App Introduce": "删除应用, 当点击同时删除文件,会把文件管理中,应用相关的存储的资源同步删除。"
|
||||
}
|
@ -8,6 +8,7 @@ import { App as OrgApp } from './pages/org';
|
||||
import { App as ConfigApp } from './pages/config';
|
||||
import { App as PayApp } from './pages/pay';
|
||||
import { App as DomainApp } from './pages/domain';
|
||||
import { App as HomeApp } from './pages/home';
|
||||
import { basename } from './modules/basename';
|
||||
import { Redirect } from './modules/Redirect';
|
||||
import { CustomThemeProvider } from '@kevisual/components/theme/index.tsx';
|
||||
@ -82,6 +83,7 @@ export const App = () => {
|
||||
<Route path='/file/*' element={<FileApp />} />
|
||||
<Route path='/pay/*' element={<PayApp />} />
|
||||
<Route path='/domain/*' element={<DomainApp />} />
|
||||
<Route path='/home/*' element={<HomeApp />} />
|
||||
<Route path='/404' element={<div>404</div>} />
|
||||
<Route path='*' element={<div>404</div>} />
|
||||
</Routes>
|
||||
|
@ -12,6 +12,7 @@ import { useNewNavigate } from '../navicate';
|
||||
import { LogOut, Map, SquareUser, Users, X, ArrowDownLeftFromSquareIcon } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import React from 'react';
|
||||
import { useQuickMenu } from './Menu';
|
||||
|
||||
export const LayoutUser = () => {
|
||||
const { open, setOpen, isAdmin, ...store } = useLayoutStore(
|
||||
@ -77,7 +78,6 @@ export const LayoutUser = () => {
|
||||
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
@ -12,14 +12,29 @@ import SmileOutlined from '@ant-design/icons/SmileOutlined';
|
||||
import { X, Settings } from 'lucide-react';
|
||||
import { useNewNavigate } from '../navicate';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Map } from 'lucide-react';
|
||||
export const useQuickMenu = () => {
|
||||
const { t } = useTranslation();
|
||||
return [
|
||||
{
|
||||
title: t('User App'),
|
||||
icon: <AppstoreOutlined />,
|
||||
link: '/app/edit/list',
|
||||
},
|
||||
{
|
||||
title: t('File App'),
|
||||
icon: <FolderOutlined />,
|
||||
link: '/file/edit/list',
|
||||
},
|
||||
];
|
||||
};
|
||||
export const LayoutMenu = () => {
|
||||
const { t } = useTranslation();
|
||||
const meun = [
|
||||
{
|
||||
title: t('Home'),
|
||||
icon: <HomeOutlined />,
|
||||
link: '/map',
|
||||
link: '/home',
|
||||
},
|
||||
{
|
||||
title: t('User App'),
|
||||
@ -37,6 +52,7 @@ export const LayoutMenu = () => {
|
||||
link: '/container/edit/list',
|
||||
},
|
||||
{ title: t('Config'), icon: <Settings size={16} />, link: '/config/edit/list' },
|
||||
{ title: t('Map'), icon: <Map size={16} />, link: '/map' },
|
||||
{
|
||||
title: t('About'),
|
||||
icon: <SmileOutlined />,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { MenuOutlined, SwapOutlined } from '@ant-design/icons';
|
||||
import { Tooltip } from '@mui/material';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
import { LayoutMenu } from './Menu';
|
||||
import { Outlet, useNavigate } from 'react-router-dom';
|
||||
import { LayoutMenu, useQuickMenu } from './Menu';
|
||||
import { useLayoutStore, usePlatformStore } from './store';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { useEffect, useLayoutEffect, useState } from 'react';
|
||||
@ -46,6 +46,8 @@ export const LayoutMain = (props: LayoutMainProps) => {
|
||||
}),
|
||||
);
|
||||
const { isMac, mount, isElectron } = platformStore;
|
||||
const navigate = useNavigate();
|
||||
const quickMenu = useQuickMenu();
|
||||
|
||||
useLayoutEffect(() => {
|
||||
platformStore.init();
|
||||
@ -88,7 +90,26 @@ export const LayoutMain = (props: LayoutMainProps) => {
|
||||
<MenuOutlined />
|
||||
</IconButton>
|
||||
<div className='flex grow justify-between pl-4 items-center'>
|
||||
{props.title}
|
||||
<div className='flex items-center gap-2'>
|
||||
<div className='text-xl font-bold'>{props.title}</div>
|
||||
<div className='ml-4 flex items-center gap-2 text-sm '>
|
||||
{quickMenu.map((item, index) => {
|
||||
const isActive = location.pathname.includes(item.link);
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={clsx('flex items-center gap-2 px-1', isActive && 'border border-white')}
|
||||
onClick={() => {
|
||||
navigate(item.link);
|
||||
}}>
|
||||
<Tooltip title={item.title}>
|
||||
<div className='cursor-pointer'>{item.icon}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className='mr-4 flex gap-4 items-center no-drag'>
|
||||
<div className='group relative'>
|
||||
<IconButton>
|
||||
|
@ -64,8 +64,9 @@ export type LayoutStore = {
|
||||
switchOrg: (username?: string, type?: 'user' | 'org') => Promise<void>;
|
||||
isAdmin: boolean;
|
||||
setIsAdmin: (isAdmin: boolean) => void;
|
||||
checkHasOrg: () => boolean;
|
||||
};
|
||||
export const useLayoutStore = create<LayoutStore>((set) => ({
|
||||
export const useLayoutStore = create<LayoutStore>((set, get) => ({
|
||||
open: false,
|
||||
setOpen: (open) => set({ open }),
|
||||
me: {},
|
||||
@ -92,4 +93,11 @@ export const useLayoutStore = create<LayoutStore>((set) => ({
|
||||
},
|
||||
isAdmin: false,
|
||||
setIsAdmin: (isAdmin) => set({ isAdmin }),
|
||||
checkHasOrg: () => {
|
||||
const user = get().me || {};
|
||||
if (!user.orgs) {
|
||||
return false;
|
||||
}
|
||||
return user?.orgs?.length > 0;
|
||||
},
|
||||
}));
|
||||
|
@ -8,7 +8,6 @@ import FileOutlined from '@ant-design/icons/FileOutlined';
|
||||
import LeftOutlined from '@ant-design/icons/LeftOutlined';
|
||||
import LinkOutlined from '@ant-design/icons/LinkOutlined';
|
||||
import PlusOutlined from '@ant-design/icons/PlusOutlined';
|
||||
import { useModal } from '@kevisual/components/modal/Confirm.tsx';
|
||||
import { Tooltip } from '@mui/material';
|
||||
import { isObjectNull } from '@/utils/is-null';
|
||||
import { FileUpload } from '../modules/FileUpload';
|
||||
@ -22,6 +21,7 @@ import { IconButton } from '@kevisual/components/button/index.tsx';
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import { TextField } from '@mui/material';
|
||||
import { pick } from 'lodash-es';
|
||||
import { useAppDeleteModalStore, AppDeleteModal } from '../modules/AppDeleteModal';
|
||||
|
||||
const FormModal = () => {
|
||||
const { t } = useTranslation();
|
||||
@ -108,8 +108,14 @@ export const AppVersionList = () => {
|
||||
};
|
||||
}),
|
||||
);
|
||||
const appDeleteModalStore = useAppDeleteModalStore(
|
||||
useShallow((state) => {
|
||||
return {
|
||||
onClickDelete: state.onClickDelete,
|
||||
};
|
||||
}),
|
||||
);
|
||||
const navigate = useNewNavigate();
|
||||
const [modal, contextHolder] = useModal();
|
||||
const [isUpload, setIsUpload] = useState(false);
|
||||
useEffect(() => {
|
||||
// fetch app version list
|
||||
@ -178,13 +184,7 @@ export const AppVersionList = () => {
|
||||
<Tooltip title='Delete'>
|
||||
<Button
|
||||
onClick={(e) => {
|
||||
modal.confirm({
|
||||
title: 'Delete',
|
||||
content: 'Are you sure delete this data?',
|
||||
onOk: () => {
|
||||
versionStore.deleteData(item.id);
|
||||
},
|
||||
});
|
||||
appDeleteModalStore.onClickDelete('app-version', item);
|
||||
e.stopPropagation();
|
||||
}}>
|
||||
<DeleteOutlined />
|
||||
@ -229,7 +229,6 @@ export const AppVersionList = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{contextHolder}
|
||||
<div className='shark h-full'>
|
||||
{isUpload && (
|
||||
<div className='bg-white p-2 w-[600px] h-full flex flex-col'>
|
||||
@ -249,6 +248,7 @@ export const AppVersionList = () => {
|
||||
)}
|
||||
</div>
|
||||
<FormModal />
|
||||
<AppDeleteModal />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -268,8 +268,8 @@ export const AppVersionFile = () => {
|
||||
return (
|
||||
<>
|
||||
<div>version: {versionStore.formData.version}</div>
|
||||
<div className='border rounded-md my-2 grow overflow-hidden'>
|
||||
<div className='flex gap-2 items-center border-b py-2 px-2'>
|
||||
<div className='border border-gray-200 rounded-md my-2 grow overflow-hidden'>
|
||||
<div className='flex gap-2 items-center border-b border-b-gray-200 py-2 px-2'>
|
||||
Files
|
||||
<FileUpload />
|
||||
</div>
|
||||
@ -284,7 +284,7 @@ export const AppVersionFile = () => {
|
||||
const _path = file.path || '';
|
||||
const path = _path.replace(prefix, '');
|
||||
return (
|
||||
<div className='flex gap-2 px-4 py-2 border-b' key={index}>
|
||||
<div className='flex gap-2 px-4 py-2 border-b border-b-gray-200' key={index}>
|
||||
{/* <div className='w-[100px] truncate'>{file.name}</div> */}
|
||||
<div>
|
||||
<FileOutlined />
|
||||
|
@ -1,15 +1,11 @@
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { useUserAppStore } from '../store';
|
||||
import { useAppVersionStore, useUserAppStore } from '../store';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useModal } from '@kevisual/components/modal/Confirm.tsx';
|
||||
|
||||
import DeleteOutlined from '@ant-design/icons/DeleteOutlined';
|
||||
import EditOutlined from '@ant-design/icons/EditOutlined';
|
||||
import LinkOutlined from '@ant-design/icons/LinkOutlined';
|
||||
import PlusOutlined from '@ant-design/icons/PlusOutlined';
|
||||
import UnorderedListOutlined from '@ant-design/icons/UnorderedListOutlined';
|
||||
import CodeOutlined from '@ant-design/icons/CodeOutlined';
|
||||
import ShareAltOutlined from '@ant-design/icons/ShareAltOutlined';
|
||||
import { FormControlLabel, Switch, useTheme } from '@mui/material';
|
||||
import { isObjectNull } from '@/utils/is-null';
|
||||
import { queryLogin, useNewNavigate } from '@/modules';
|
||||
@ -29,6 +25,8 @@ import { useForm, Controller } from 'react-hook-form';
|
||||
import { pick } from 'lodash-es';
|
||||
import copy from 'copy-to-clipboard';
|
||||
import { useLayoutStore } from '@/modules/layout/store';
|
||||
import { useAppDeleteModalStore, AppDeleteModal } from '../modules/AppDeleteModal';
|
||||
import { AppWindow, Edit, Link, RefreshCcw, Share2, Trash } from 'lucide-react';
|
||||
|
||||
const FormModal = () => {
|
||||
const defaultValues = {
|
||||
@ -57,7 +55,6 @@ const FormModal = () => {
|
||||
const open = containerStore.showEdit;
|
||||
if (open) {
|
||||
const isNull = isObjectNull(containerStore.userApp);
|
||||
console.log('isNull', containerStore.userApp);
|
||||
if (isNull) {
|
||||
reset(defaultValues);
|
||||
} else {
|
||||
@ -152,7 +149,7 @@ const ShareModal = () => {
|
||||
const permission = containerStore.userApp?.data?.permission || {};
|
||||
const runtime = containerStore.userApp?.data?.runtime || [];
|
||||
if (isObjectNull(permission)) {
|
||||
setPermission(null);
|
||||
setPermission({ share: 'private' });
|
||||
} else {
|
||||
setPermission(permission);
|
||||
}
|
||||
@ -173,9 +170,9 @@ const ShareModal = () => {
|
||||
containerStore.setShowEdit(false);
|
||||
};
|
||||
const { t } = useTranslation();
|
||||
console.log('runtime', runtime);
|
||||
const theme = useTheme();
|
||||
const defaultProps = theme.components?.MuiTextField?.defaultProps as any;
|
||||
const isAdmin = useLayoutStore(useShallow((state) => state.isAdmin));
|
||||
return (
|
||||
<Dialog
|
||||
open={containerStore.showEdit}
|
||||
@ -191,35 +188,37 @@ const ShareModal = () => {
|
||||
setPermission(value);
|
||||
}}
|
||||
/>
|
||||
<FormControlLabel
|
||||
label={t('app.runtime')}
|
||||
labelPlacement='top'
|
||||
sx={{
|
||||
'& .MuiFormControlLabel-label': {
|
||||
width: '100%',
|
||||
},
|
||||
}}
|
||||
control={
|
||||
<Select
|
||||
multiple
|
||||
size='small'
|
||||
value={runtime}
|
||||
onChange={(e) => {
|
||||
setRuntime(e.target.value as string[]);
|
||||
}}
|
||||
options={[
|
||||
{
|
||||
label: 'Node',
|
||||
value: 'node',
|
||||
},
|
||||
{
|
||||
label: 'Browser',
|
||||
value: 'browser',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{isAdmin && (
|
||||
<FormControlLabel
|
||||
label={t('app.runtime')}
|
||||
labelPlacement='top'
|
||||
sx={{
|
||||
'& .MuiFormControlLabel-label': {
|
||||
width: '100%',
|
||||
},
|
||||
}}
|
||||
control={
|
||||
<Select
|
||||
multiple
|
||||
size='small'
|
||||
value={runtime}
|
||||
onChange={(e) => {
|
||||
setRuntime(e.target.value as string[]);
|
||||
}}
|
||||
options={[
|
||||
{
|
||||
label: 'Node',
|
||||
value: 'node',
|
||||
},
|
||||
{
|
||||
label: 'Browser',
|
||||
value: 'browser',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
@ -250,6 +249,20 @@ export const List = () => {
|
||||
};
|
||||
}),
|
||||
);
|
||||
const appVersionStore = useAppVersionStore(
|
||||
useShallow((state) => {
|
||||
return {
|
||||
publishVersion: state.publishVersion,
|
||||
};
|
||||
}),
|
||||
);
|
||||
const appDeleteModalStore = useAppDeleteModalStore(
|
||||
useShallow((state) => {
|
||||
return {
|
||||
onClickDelete: state.onClickDelete,
|
||||
};
|
||||
}),
|
||||
);
|
||||
const navicate = useNewNavigate();
|
||||
useEffect(() => {
|
||||
userAppStore.getList();
|
||||
@ -299,11 +312,22 @@ export const List = () => {
|
||||
{userAppStore.list.map((item) => {
|
||||
const isRunning = item.status === 'running';
|
||||
const hasDescription = !!item.description;
|
||||
const content = marked.parse(item.description);
|
||||
// const content = marked.parse(item.description);
|
||||
const content = item.description;
|
||||
return (
|
||||
<div className='card w-[300px] relative pb-10 ' key={item.id}>
|
||||
<div className='card-title flex font-bold justify-between' onClick={() => {}}>
|
||||
{item.title}
|
||||
<Tooltip
|
||||
title={
|
||||
<pre className=''>
|
||||
<span className='text-sm'>{item.title}</span>
|
||||
<i className='text-xs text-white ml-4'>{item.key}</i>
|
||||
</pre>
|
||||
}>
|
||||
<div>
|
||||
{item.title} <i className='text-xs text-gray-400'>{item.key}</i>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<div>
|
||||
<Tooltip title={isRunning ? '网页可正常访问' : '网页被关闭'}>
|
||||
<div className={`${isRunning ? 'bg-green-500' : 'bg-red-500'} w-4 h-4 rounded-full`}></div>
|
||||
@ -329,8 +353,10 @@ export const List = () => {
|
||||
<div className='text-xs'>
|
||||
{t('app.version')}: {item.version}
|
||||
</div>
|
||||
|
||||
<div className={clsx('text-sm border border-gray-200 p-2 max-h-[140px] scrollbar my-1', !hasDescription && 'hidden')}>
|
||||
<div dangerouslySetInnerHTML={{ __html: content }}></div>
|
||||
{/* <div dangerouslySetInnerHTML={{ __html: content }}></div> */}
|
||||
<div className='text-sm whitespace-pre-wrap'>{content}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='py-4'></div>
|
||||
@ -346,7 +372,7 @@ export const List = () => {
|
||||
userAppStore.setFormData(item);
|
||||
userAppStore.setShowEdit(true);
|
||||
}}>
|
||||
<EditOutlined />
|
||||
<Edit size={16} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title={'App Version List'}>
|
||||
@ -354,7 +380,7 @@ export const List = () => {
|
||||
onClick={() => {
|
||||
navicate(`/app/${item.key}/version/list`);
|
||||
}}>
|
||||
<UnorderedListOutlined />
|
||||
<AppWindow size={16} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title={iText.share.tips}>
|
||||
@ -364,7 +390,15 @@ export const List = () => {
|
||||
userAppStore.setFormData(item);
|
||||
userAppStore.setShowShareEdit(true);
|
||||
}}>
|
||||
<ShareAltOutlined />
|
||||
<Share2 size={16} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title={'reload'}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
appVersionStore.publishVersion({ appKey: item.key, version: item.version }, { showToast: false });
|
||||
}}>
|
||||
<RefreshCcw size={16} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title={'To App'}>
|
||||
@ -396,23 +430,16 @@ export const List = () => {
|
||||
message.error('The app is not running');
|
||||
}
|
||||
}}>
|
||||
<LinkOutlined />
|
||||
<Link size={16} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title={'Delete'}>
|
||||
<Button
|
||||
onClick={(e) => {
|
||||
console.log('delete', item);
|
||||
modal.confirm({
|
||||
title: 'Delete',
|
||||
content: 'Are you sure delete this data?',
|
||||
onOk: () => {
|
||||
userAppStore.deleteData(item.id);
|
||||
},
|
||||
});
|
||||
appDeleteModalStore.onClickDelete('user-app', item);
|
||||
e.stopPropagation();
|
||||
}}>
|
||||
<DeleteOutlined />
|
||||
<Trash size={16} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</ButtonGroup>
|
||||
@ -427,6 +454,7 @@ export const List = () => {
|
||||
{contextHolder}
|
||||
<FormModal />
|
||||
<ShareModal />
|
||||
<AppDeleteModal />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
79
src/pages/app/modules/AppDeleteModal.tsx
Normal file
79
src/pages/app/modules/AppDeleteModal.tsx
Normal file
@ -0,0 +1,79 @@
|
||||
import { Button, Dialog, DialogActions, DialogContent, DialogTitle } from '@mui/material';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { create } from 'zustand';
|
||||
import { useAppVersionStore, useUserAppStore } from '../store';
|
||||
import { useShallow } from 'zustand/shallow';
|
||||
|
||||
type AppDeleteModalStore = {
|
||||
open: boolean;
|
||||
setOpen: (open: boolean) => void;
|
||||
app: any;
|
||||
setApp: (app: any) => void;
|
||||
type: 'user-app' | 'app-version';
|
||||
setType: (type: 'user-app' | 'app-version') => void;
|
||||
onClickDelete: (type: 'user-app' | 'app-version', data: any) => void;
|
||||
};
|
||||
export const useAppDeleteModalStore = create<AppDeleteModalStore>((set) => ({
|
||||
open: false,
|
||||
setOpen: (open) => set({ open }),
|
||||
app: null,
|
||||
setApp: (app) => set({ app }),
|
||||
type: 'user-app',
|
||||
setType: (type) => set({ type }),
|
||||
onClickDelete: (type, data) => {
|
||||
set({ open: true, type, app: data });
|
||||
},
|
||||
}));
|
||||
|
||||
export const AppDeleteModal = () => {
|
||||
const { t } = useTranslation();
|
||||
const { open, setOpen, app, type, setType } = useAppDeleteModalStore();
|
||||
const userAppStore = useUserAppStore(
|
||||
useShallow((state) => {
|
||||
return {
|
||||
deleteData: state.deleteData,
|
||||
};
|
||||
}),
|
||||
);
|
||||
const appVersionStore = useAppVersionStore(
|
||||
useShallow((state) => {
|
||||
return {
|
||||
deleteData: state.deleteData,
|
||||
};
|
||||
}),
|
||||
);
|
||||
const onClose = () => {
|
||||
setOpen(false);
|
||||
};
|
||||
const onDelete = (deleteFile = false) => {
|
||||
if (type === 'user-app') {
|
||||
userAppStore.deleteData(app.id, deleteFile);
|
||||
} else {
|
||||
appVersionStore.deleteData(app.id, deleteFile);
|
||||
}
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onClose={onClose}>
|
||||
<DialogTitle>{t('Tips')}</DialogTitle>
|
||||
<DialogContent>
|
||||
<div className='w-[400px]'>
|
||||
<p className='text-sm text-gray-500'>{t('Delete App Introduce')}</p>
|
||||
</div>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button variant='contained' onClick={() => onDelete()}>{t('Delete')}</Button>
|
||||
<Button onClick={onClose}>{t('Cancel')}</Button>
|
||||
<Button
|
||||
color='error'
|
||||
onClick={() => {
|
||||
onDelete(true);
|
||||
}}>
|
||||
{t('Delete and remove file')}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
@ -17,8 +17,14 @@ type AppVersionStore = {
|
||||
app: any;
|
||||
getApp: (key: string, force?: boolean) => Promise<void>;
|
||||
updateData: (data: any) => Promise<void>;
|
||||
deleteData: (id: string) => Promise<void>;
|
||||
publishVersion: (data: any) => Promise<void>;
|
||||
/**
|
||||
* 删除应用版本
|
||||
* @param id 应用版本id
|
||||
* @param deleteFile 是否删除文件
|
||||
* @returns
|
||||
*/
|
||||
deleteData: (id: string, deleteFile?: boolean) => Promise<void>;
|
||||
publishVersion: (data: { id?: string; appKey?: string; version?: string }, opts?: { showToast?: boolean }) => Promise<any>;
|
||||
};
|
||||
export const useAppVersionStore = create<AppVersionStore>((set, get) => {
|
||||
return {
|
||||
@ -94,12 +100,13 @@ export const useAppVersionStore = create<AppVersionStore>((set, get) => {
|
||||
message.error(res.message || 'Request failed');
|
||||
}
|
||||
},
|
||||
deleteData: async (id) => {
|
||||
deleteData: async (id, deleteFile = false) => {
|
||||
const { getList } = get();
|
||||
const res = await query.post({
|
||||
path: 'app',
|
||||
key: 'delete',
|
||||
id,
|
||||
deleteFile,
|
||||
});
|
||||
if (res.code === 200) {
|
||||
getList();
|
||||
@ -108,18 +115,24 @@ export const useAppVersionStore = create<AppVersionStore>((set, get) => {
|
||||
message.error(res.message || 'Request failed');
|
||||
}
|
||||
},
|
||||
publishVersion: async (data) => {
|
||||
publishVersion: async (data, opts) => {
|
||||
const showToast = opts?.showToast ?? true;
|
||||
const res = await query.post({
|
||||
path: 'app',
|
||||
key: 'publish',
|
||||
data,
|
||||
});
|
||||
if (res.code === 200) {
|
||||
message.success('Success');
|
||||
get().getApp(get().key, true);
|
||||
if (showToast) {
|
||||
message.success('Success');
|
||||
get().getApp(get().key, true);
|
||||
}
|
||||
} else {
|
||||
message.error(res.message || 'Request failed');
|
||||
if (showToast) {
|
||||
message.error(res.message || 'Request failed');
|
||||
}
|
||||
}
|
||||
return res;
|
||||
},
|
||||
};
|
||||
});
|
||||
|
@ -11,7 +11,13 @@ type UserAppStore = {
|
||||
list: any[];
|
||||
getList: () => Promise<void>;
|
||||
updateData: (data: any) => Promise<void>;
|
||||
deleteData: (id: string) => Promise<void>;
|
||||
/**
|
||||
* 删除用户应用
|
||||
* @param id 用户应用id
|
||||
* @param deleteFile 是否删除文件
|
||||
* @returns
|
||||
*/
|
||||
deleteData: (id: string, deleteFile?: boolean) => Promise<void>;
|
||||
showShareEdit: boolean;
|
||||
setShowShareEdit: (showShareEdit: boolean) => void;
|
||||
userApp: any;
|
||||
@ -56,12 +62,13 @@ export const useUserAppStore = create<UserAppStore>((set, get) => {
|
||||
message.error(res.message || 'Request failed');
|
||||
}
|
||||
},
|
||||
deleteData: async (id) => {
|
||||
deleteData: async (id, deleteFile = false) => {
|
||||
const { getList } = get();
|
||||
const res = await query.post({
|
||||
path: 'user-app',
|
||||
key: 'delete',
|
||||
id,
|
||||
deleteFile,
|
||||
});
|
||||
if (res.code === 200) {
|
||||
getList();
|
||||
|
107
src/pages/home/Home.tsx
Normal file
107
src/pages/home/Home.tsx
Normal file
@ -0,0 +1,107 @@
|
||||
import CloudUploadOutlined from '@ant-design/icons/CloudUploadOutlined';
|
||||
|
||||
import { BaseEditor } from '@kevisual/codemirror/editor/editor.ts';
|
||||
import { IconButton } from '@kevisual/components/button/index.tsx';
|
||||
import { Button, Tooltip } from 'antd';
|
||||
import { UploadIcon } from 'lucide-react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useDropzone } from 'react-dropzone';
|
||||
import { CacheWorkspace } from '@kevisual/cache';
|
||||
import { useHomeStore } from './store/index.ts';
|
||||
import { UploadModal } from './module/UploadModal.tsx';
|
||||
import { useShallow } from 'zustand/shallow';
|
||||
import { toast } from 'react-toastify';
|
||||
import { SuccessModal } from './module/SuccessModal.tsx';
|
||||
|
||||
export const Home = () => {
|
||||
const editorElRef = useRef<HTMLDivElement>(null);
|
||||
const editorRef = useRef<BaseEditor>(null);
|
||||
const { initApp, setOpenUploadModal, setText, filename } = useHomeStore(
|
||||
useShallow((state) => ({ initApp: state.initApp, setOpenUploadModal: state.setOpenUploadModal, setText: state.setText, filename: state.filename })),
|
||||
);
|
||||
const onDrop = (acceptedFiles) => {
|
||||
console.log(acceptedFiles);
|
||||
const file = acceptedFiles[0];
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const content = e.target?.result as string;
|
||||
editorRef.current!.setContent(content);
|
||||
};
|
||||
reader.readAsText(file);
|
||||
};
|
||||
const { getRootProps, getInputProps } = useDropzone({ onDrop, accept: { 'text/html': ['.html'], 'text/javascript': ['.js'], 'text/css': ['.css'] } });
|
||||
|
||||
useEffect(() => {
|
||||
initApp();
|
||||
initEditor();
|
||||
return () => {
|
||||
if (editorRef.current) {
|
||||
editorRef.current.destroyEditor();
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const initEditor = async () => {
|
||||
if (!editorElRef.current) return;
|
||||
const cache = new CacheWorkspace();
|
||||
let cacheData = '';
|
||||
try {
|
||||
cacheData = (await cache.storage.get('html-editor')) || '';
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
editorRef.current = new BaseEditor({
|
||||
filename: filename || 'index.html',
|
||||
onChange: (value) => {
|
||||
cache.storage.set('html-editor', value);
|
||||
},
|
||||
});
|
||||
editorRef.current.createEditor(editorElRef.current);
|
||||
setTimeout(() => {
|
||||
editorRef.current!.setContent(cacheData);
|
||||
}, 300);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='flex flex-row w-full h-full'>
|
||||
<div className='flex flex-col text-primary border-r border-gray-200 px-2 pt-2'>
|
||||
<div>
|
||||
<Tooltip title='部署应用' placement='right'>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
const editorContent = editorRef.current?.getContent();
|
||||
if (editorContent) {
|
||||
setOpenUploadModal(true);
|
||||
setText(editorContent);
|
||||
} else {
|
||||
toast.error('请先输入代码');
|
||||
}
|
||||
}}>
|
||||
<CloudUploadOutlined />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className=' mt-2' {...getRootProps()}>
|
||||
<Tooltip title='点击上传html或者js文件 ' placement='right'>
|
||||
<IconButton>
|
||||
<UploadIcon size={16} />
|
||||
</IconButton>
|
||||
<input type='file' style={{ display: 'none' }} {...getInputProps()} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div className='h-full grow px-2'>
|
||||
<div className=' py-1'>
|
||||
<i className=' text-gray-500' style={{ fontSize: 10 }}>
|
||||
{'>'} 快速部署html小应用, 粘贴前端html代码。点击部署。(这个页面内容自动缓存到本地)
|
||||
</i>
|
||||
</div>
|
||||
<div className=' border rounded-md border-gray-200 scrollbar' style={{ height: 'calc(100% - 50px)' }}>
|
||||
<div className='w-full h-full ' ref={editorElRef}></div>
|
||||
</div>
|
||||
</div>
|
||||
<UploadModal />
|
||||
<SuccessModal />
|
||||
</div>
|
||||
);
|
||||
};
|
12
src/pages/home/index.tsx
Normal file
12
src/pages/home/index.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { Route, Routes } from 'react-router-dom';
|
||||
import { Main } from './layouts';
|
||||
import { Home } from './Home';
|
||||
export const App = () => {
|
||||
return (
|
||||
<Routes>
|
||||
<Route element={<Main />}>
|
||||
<Route path='/' element={<Home />}></Route>
|
||||
</Route>
|
||||
</Routes>
|
||||
);
|
||||
};
|
5
src/pages/home/layouts/index.tsx
Normal file
5
src/pages/home/layouts/index.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
import { LayoutMain } from '@/modules/layout';
|
||||
|
||||
export const Main = () => {
|
||||
return <LayoutMain title='Home' />;
|
||||
};
|
55
src/pages/home/module/SuccessModal.tsx
Normal file
55
src/pages/home/module/SuccessModal.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
import { Dialog, DialogContent, DialogTitle } from '@mui/material';
|
||||
import { useShallow } from 'zustand/shallow';
|
||||
import { useHomeStore } from '../store';
|
||||
import { useMemo } from 'react';
|
||||
import { useLayoutStore } from '@/modules/layout/store';
|
||||
export const Label = ({ label, children }: { label: string; children: React.ReactNode }) => {
|
||||
return (
|
||||
<div className='text-sm text-gray-500 w-full flex gap-2'>
|
||||
<div className='min-w-[60px]'>{label}</div>
|
||||
<div className=''>{children}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export const SuccessModal = () => {
|
||||
const { openSuccessModal, setOpenSuccessModal, appKey, version, filename } = useHomeStore(
|
||||
useShallow((state) => ({
|
||||
openSuccessModal: state.openSuccessModal,
|
||||
setOpenSuccessModal: state.setOpenSuccessModal,
|
||||
appKey: state.appKey, //
|
||||
version: state.version, //
|
||||
filename: state.filename, //
|
||||
})),
|
||||
);
|
||||
const { me } = useLayoutStore(useShallow((state) => ({ me: state.me })));
|
||||
const link = useMemo(() => {
|
||||
const _currentHref = new URL(window.location.href);
|
||||
const username = me?.username;
|
||||
const newHref = new URL(`/${username}/${appKey}/`, _currentHref.origin);
|
||||
return newHref.toString();
|
||||
}, [me, appKey]);
|
||||
return (
|
||||
<Dialog open={openSuccessModal} onClose={() => setOpenSuccessModal(false)}>
|
||||
<DialogTitle className='text-black'>部署成功</DialogTitle>
|
||||
<DialogContent>
|
||||
<div className='flex flex-col gap-2 w-[400px] min-h-[100px] text-black'>
|
||||
<Label label='应用 Key: '>{appKey}</Label>
|
||||
<Label label='版本:'>{version}</Label>
|
||||
<Label label='访问地址:'>
|
||||
<a href={link} className='text-blue-500' target='_blank' rel='noreferrer'>
|
||||
{link}
|
||||
</a>
|
||||
</Label>
|
||||
<Label label='配置地址:'>
|
||||
<a href={`/app/edit/list`} className='text-blue-500' target='_self' rel='noreferrer'>
|
||||
{`/app/edit/list`}
|
||||
</a>
|
||||
</Label>
|
||||
<div className='mt-1 text-gray-500 italic' style={{ fontSize: 10 }}>
|
||||
注: 如果需要其他人访问,需要设置共享。
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
98
src/pages/home/module/UploadModal.tsx
Normal file
98
src/pages/home/module/UploadModal.tsx
Normal file
@ -0,0 +1,98 @@
|
||||
import { Button, Dialog, DialogActions, DialogContent, DialogTitle } from '@mui/material';
|
||||
import { useHomeStore } from '../store';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { TextField } from '@mui/material';
|
||||
import { useEffect } from 'react';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { toast } from 'react-toastify';
|
||||
import { uploadFile } from './upload-file';
|
||||
import { useAppVersionStore } from '@/pages/app/store';
|
||||
import { useShallow } from 'zustand/shallow';
|
||||
|
||||
export const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 10);
|
||||
export const UploadModal = () => {
|
||||
const { appKey, version, filename, openUploadModal, text, setOpenUploadModal, setAppKey, setVersion, setFilename, setOpenSuccessModal } = useHomeStore();
|
||||
const { control, handleSubmit, reset } = useForm();
|
||||
const { publishVersion } = useAppVersionStore(useShallow((state) => ({ publishVersion: state.publishVersion })));
|
||||
useEffect(() => {
|
||||
if (openUploadModal) {
|
||||
const randomAppKey = nanoid(4) + nanoid(4);
|
||||
reset({ appKey: appKey || randomAppKey, version: version || '1.0.0', filename: filename || 'index.html' });
|
||||
}
|
||||
}, [openUploadModal]);
|
||||
const onSubmit = async (data: any) => {
|
||||
console.log(data);
|
||||
if (!text) {
|
||||
toast.error('代码不能为空');
|
||||
return;
|
||||
}
|
||||
if (!data.appKey) {
|
||||
toast.error('应用key不能为空');
|
||||
return;
|
||||
}
|
||||
if (!data.version) {
|
||||
toast.error('版本不能为空');
|
||||
return;
|
||||
}
|
||||
if (!data.filename) {
|
||||
toast.error('文件名不能为空');
|
||||
return;
|
||||
}
|
||||
setAppKey(data.appKey);
|
||||
setVersion(data.version);
|
||||
setFilename(data.filename);
|
||||
const res = await uploadFile({
|
||||
appKey: data.appKey,
|
||||
version: data.version,
|
||||
filename: data.filename,
|
||||
text,
|
||||
});
|
||||
if (res?.code === 200) {
|
||||
toast.success('部署成功');
|
||||
const toastId = toast.loading('发布中...');
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
const res = await publishVersion({ appKey: data.appKey, version: data.version }, { showToast: false });
|
||||
toast.dismiss(toastId);
|
||||
if (res?.code === 200) {
|
||||
toast.success('发布成功');
|
||||
setOpenSuccessModal(true);
|
||||
} else {
|
||||
toast.error(res?.message || '发布失败');
|
||||
}
|
||||
} else {
|
||||
toast.error(res?.message || '部署失败');
|
||||
}
|
||||
};
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Dialog open={openUploadModal} onClose={() => setOpenUploadModal(false)}>
|
||||
<DialogTitle>部署页面</DialogTitle>
|
||||
<DialogContent>
|
||||
<form className='flex flex-col gap-3 pt-1 w-[500px]' onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className='flex flex-col gap-1'>
|
||||
<label className='text-sm'>应用key</label>
|
||||
<Controller control={control} name='appKey' render={({ field }) => <TextField {...field} />} />
|
||||
</div>
|
||||
<div className='flex flex-col gap-1'>
|
||||
<label className='text-sm'>版本</label>
|
||||
<Controller control={control} name='version' render={({ field }) => <TextField {...field} />} />
|
||||
</div>
|
||||
<div className='flex flex-col gap-1'>
|
||||
<label className='text-sm'>文件名</label>
|
||||
<Controller control={control} name='filename' render={({ field }) => <TextField {...field} />} />
|
||||
</div>
|
||||
<DialogActions>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setOpenUploadModal(false);
|
||||
}}>
|
||||
{t('Cancel')}
|
||||
</Button>
|
||||
<Button type='submit'>{t('Submit')}</Button>
|
||||
</DialogActions>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
29
src/pages/home/module/upload-file.ts
Normal file
29
src/pages/home/module/upload-file.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { uploadChunkV2, toFile } from '@kevisual/resources/index.ts';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
type UploadFileOpts = {
|
||||
appKey: string;
|
||||
version: string;
|
||||
filename: string;
|
||||
text: string;
|
||||
};
|
||||
const getFilenameExtension = (filename: string) => {
|
||||
return filename.split('.').pop() || '';
|
||||
};
|
||||
const allowFilesName = ['js', 'css', 'json', 'html'];
|
||||
export const uploadFile = async (uploadFileOpts: UploadFileOpts) => {
|
||||
const { appKey, version, filename, text } = uploadFileOpts;
|
||||
const extension = getFilenameExtension(filename);
|
||||
if (!allowFilesName.includes(extension)) {
|
||||
toast.error('文件类型不支持');
|
||||
return;
|
||||
}
|
||||
const file = toFile(text, filename);
|
||||
const res = await uploadChunkV2(file, {
|
||||
appKey,
|
||||
version,
|
||||
filename,
|
||||
noCheckAppFiles: false,
|
||||
});
|
||||
return res as any;
|
||||
};
|
51
src/pages/home/store/index.ts
Normal file
51
src/pages/home/store/index.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { create } from 'zustand';
|
||||
export type HomeStore = {
|
||||
appKey: string;
|
||||
version: string;
|
||||
setAppKey: (appKey: string) => void;
|
||||
setVersion: (version: string) => void;
|
||||
filename: string;
|
||||
setFilename: (filename: string) => void;
|
||||
initApp: () => void;
|
||||
openUploadModal: boolean;
|
||||
setOpenUploadModal: (open: boolean) => void;
|
||||
text: string;
|
||||
setText: (text: string) => void;
|
||||
openSuccessModal: boolean;
|
||||
setOpenSuccessModal: (open: boolean) => void;
|
||||
};
|
||||
export const useHomeStore = create<HomeStore>((set) => ({
|
||||
appKey: '',
|
||||
version: '',
|
||||
setAppKey: (appKey: string) => {
|
||||
set({ appKey });
|
||||
localStorage.setItem('home-app-key', appKey);
|
||||
},
|
||||
setVersion: (version: string) => {
|
||||
set({ version });
|
||||
localStorage.setItem('home-app-version', version);
|
||||
},
|
||||
filename: '',
|
||||
setFilename: (filename: string) => {
|
||||
set({ filename });
|
||||
localStorage.setItem('home-file-name', filename);
|
||||
},
|
||||
initApp: () => {
|
||||
const appKey = localStorage.getItem('home-app-key') || '';
|
||||
const version = localStorage.getItem('home-app-version') || '';
|
||||
const filename = localStorage.getItem('home-file-name') || '';
|
||||
set({ appKey, version, filename });
|
||||
},
|
||||
openUploadModal: false,
|
||||
setOpenUploadModal: (open: boolean) => {
|
||||
set({ openUploadModal: open });
|
||||
},
|
||||
text: '',
|
||||
setText: (text: string) => {
|
||||
set({ text });
|
||||
},
|
||||
openSuccessModal: false,
|
||||
setOpenSuccessModal: (open: boolean) => {
|
||||
set({ openSuccessModal: open });
|
||||
},
|
||||
}));
|
@ -1,10 +1,21 @@
|
||||
import clsx from 'clsx';
|
||||
import { useNewNavigate } from '@/modules';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useLayoutStore } from '@/modules/layout/store';
|
||||
import { useShallow } from 'zustand/shallow';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
const ServerPath = () => {
|
||||
const navigate = useNewNavigate();
|
||||
const { t } = useTranslation();
|
||||
const layoutStore = useLayoutStore(
|
||||
useShallow((state) => {
|
||||
return {
|
||||
isAdmin: state.isAdmin,
|
||||
checkHasOrg: state.checkHasOrg,
|
||||
};
|
||||
}),
|
||||
);
|
||||
const serverPath = [
|
||||
{
|
||||
path: 'container',
|
||||
@ -28,9 +39,9 @@ const ServerPath = () => {
|
||||
},
|
||||
];
|
||||
return (
|
||||
<div className='p-2 w-full h-full bg-gray-200'>
|
||||
<h1 className='p-4 w-1/2 m-auto h1'>{t('Site Map')}</h1>
|
||||
<div className='w-1/2 m-auto bg-white p-4 border rounded-md shadow-md min-w-[700px] max-h-[80vh] overflow-auto scrollbar'>
|
||||
<div className='p-2 w-full h-full bg-gray-200 '>
|
||||
<h1 className='py-4 w-1/2 m-auto h1 text-primary'>{t('Site Map')}</h1>
|
||||
<div className='w-1/2 m-auto bg-white p-4 border border-gray-200 rounded-md shadow-md min-w-[700px] max-h-[80vh] overflow-auto scrollbar'>
|
||||
<div className='flex flex-col w-full'>
|
||||
{serverPath.map((item) => {
|
||||
const links = item.links.map((link) => {
|
||||
@ -54,7 +65,7 @@ const ServerPath = () => {
|
||||
navigate(`/${item.path}`);
|
||||
}
|
||||
}}>
|
||||
<div className={clsx('border rounded-md p-2 m-2', hasId && 'bg-gray-200')}>{_path}</div>
|
||||
<div className={clsx('border border-gray-200 rounded-md p-2 m-2', hasId && 'bg-gray-200')}>{_path}</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { LayoutMain } from '@/modules/layout';
|
||||
export const Main = () => {
|
||||
return <LayoutMain title='User' />;
|
||||
return <LayoutMain title='User Org' />;
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { InputAdornment, TextField, Tooltip } from '@mui/material';
|
||||
import { Dialog, DialogContent, DialogTitle, InputAdornment, TextField, Tooltip } from '@mui/material';
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import { Button } from '@mui/material';
|
||||
import { useUserStore } from '../store';
|
||||
@ -12,6 +12,56 @@ import { FileUpload } from '../module/FileUpload';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Edit } from 'lucide-react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useAdminStore } from '../admin/store/admin-store';
|
||||
export const CheckUserExistModal = () => {
|
||||
const { t } = useTranslation();
|
||||
const userStore = useUserStore(
|
||||
useShallow((state) => {
|
||||
return {
|
||||
showCheckUserExist: state.showCheckUserExist,
|
||||
setShowCheckUserExist: state.setShowCheckUserExist,
|
||||
};
|
||||
}),
|
||||
);
|
||||
const adminStore = useAdminStore(
|
||||
useShallow((state) => {
|
||||
return {
|
||||
checkUserExist: state.checkUserExist,
|
||||
};
|
||||
}),
|
||||
);
|
||||
const onClose = () => {
|
||||
userStore.setShowCheckUserExist(false);
|
||||
};
|
||||
const [username, setUsername] = useState('@');
|
||||
|
||||
const onCheck = async () => {
|
||||
const res = await adminStore.checkUserExist(username);
|
||||
console.log(res);
|
||||
if (res === false) {
|
||||
toast.info('当前用户名可以修改,点击右上角关注公众号联系客服修改。', {
|
||||
autoClose: 20000,
|
||||
});
|
||||
// toast.success(t('Check success'));
|
||||
} else {
|
||||
toast.error('当前用户名已存在,请更换其他用户名。');
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Dialog open={userStore.showCheckUserExist} onClose={onClose}>
|
||||
<DialogTitle>{t('Tips')}</DialogTitle>
|
||||
<DialogContent>
|
||||
<div className='flex flex-col pt-4 gap-6 w-[400px]'>
|
||||
<div className='text-sm text-secondary'>{t('Check username introduction')}</div>
|
||||
<TextField label='username' value={username} onChange={(e) => setUsername(e.target.value)} />
|
||||
<Button variant='contained' color='primary' onClick={onCheck}>
|
||||
{t('Submit')}
|
||||
</Button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export const Profile = () => {
|
||||
const { t } = useTranslation();
|
||||
@ -32,6 +82,7 @@ export const Profile = () => {
|
||||
updateData: state.updateData,
|
||||
setFormData: state.setFormData,
|
||||
updateSelf: state.updateSelf,
|
||||
setShowCheckUserExist: state.setShowCheckUserExist,
|
||||
};
|
||||
}),
|
||||
);
|
||||
@ -104,9 +155,7 @@ export const Profile = () => {
|
||||
className='w-4 h-4 cursor-pointer text-primary'
|
||||
onClick={() => {
|
||||
console.log('onClick edit');
|
||||
toast.info('联系客服修改,因为名称和上传文件绑定了。', {
|
||||
autoClose: 20000,
|
||||
});
|
||||
userStore.setShowCheckUserExist(true);
|
||||
}}
|
||||
/>
|
||||
</InputAdornment>
|
||||
@ -153,6 +202,7 @@ export const Profile = () => {
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<CheckUserExistModal />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -6,6 +6,8 @@ type UserStore = {
|
||||
setShowEdit: (showEdit: boolean) => void;
|
||||
showNameEdit: boolean;
|
||||
setShowNameEdit: (showNameEdit: boolean) => void;
|
||||
showCheckUserExist: boolean;
|
||||
setShowCheckUserExist: (showCheckUserExist: boolean) => void;
|
||||
formData: any;
|
||||
setFormData: (formData: any) => void;
|
||||
loading: boolean;
|
||||
@ -22,6 +24,8 @@ export const useUserStore = create<UserStore>((set, get) => {
|
||||
setShowEdit: (showEdit) => set({ showEdit }),
|
||||
showNameEdit: false,
|
||||
setShowNameEdit: (showNameEdit) => set({ showNameEdit }),
|
||||
showCheckUserExist: false,
|
||||
setShowCheckUserExist: (showCheckUserExist) => set({ showCheckUserExist }),
|
||||
formData: {},
|
||||
setFormData: (formData) => set({ formData }),
|
||||
loading: false,
|
||||
|
Loading…
x
Reference in New Issue
Block a user