U: Web终端的文件管理器,支持按文件修改时间排序

pull/592/head
yombo 2023-03-01 14:54:18 +08:00
parent 4cb86923d6
commit e7921e4171
1 changed files with 43 additions and 41 deletions

View File

@ -18,6 +18,7 @@ import { AuthButton, Action } from 'components';
import { http, uniqueId, X_TOKEN } from 'libs'; import { http, uniqueId, X_TOKEN } from 'libs';
import lds from 'lodash'; import lds from 'lodash';
import styles from './index.module.less' import styles from './index.module.less'
import moment from 'moment';
class FileManager extends React.Component { class FileManager extends React.Component {
@ -44,7 +45,7 @@ class FileManager extends React.Component {
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
if (this.props.id !== prevProps.id) { if (this.props.id !== prevProps.id) {
this.fetchFiles() this.fetchFiles()
this.setState({objects: []}) this.setState({ objects: [] })
} }
} }
@ -52,14 +53,14 @@ class FileManager extends React.Component {
title: '名称', title: '名称',
key: 'name', key: 'name',
render: info => info.kind === 'd' ? ( render: info => info.kind === 'd' ? (
<div onClick={() => this.handleChdir(info.name, '1')} style={{cursor: 'pointer'}}> <div onClick={() => this.handleChdir(info.name, '1')} style={{ cursor: 'pointer' }}>
<FolderOutlined style={{color: info.is_link ? '#008b8b' : '#2563fc'}}/> <FolderOutlined style={{ color: info.is_link ? '#008b8b' : '#2563fc' }} />
<span style={{color: info.is_link ? '#008b8b' : '#2563fc', paddingLeft: 5}}>{info.name}</span> <span style={{ color: info.is_link ? '#008b8b' : '#2563fc', paddingLeft: 5 }}>{info.name}</span>
</div> </div>
) : ( ) : (
<React.Fragment> <React.Fragment>
<FileOutlined/> <FileOutlined />
<span style={{paddingLeft: 5}}>{info.name}</span> <span style={{ paddingLeft: 5 }}>{info.name}</span>
</React.Fragment> </React.Fragment>
), ),
ellipsis: true ellipsis: true
@ -72,6 +73,7 @@ class FileManager extends React.Component {
}, { }, {
title: '修改时间', title: '修改时间',
dataIndex: 'date', dataIndex: 'date',
sorter: (a, b) => moment(a.date).unix() - moment(b.date).unix(),
width: 190 width: 190
}, { }, {
title: '属性', title: '属性',
@ -84,10 +86,10 @@ class FileManager extends React.Component {
key: 'action', key: 'action',
render: info => info.kind === '-' ? ( render: info => info.kind === '-' ? (
<Action> <Action>
<Action.Button className={styles.drawerBtn} icon={<DownloadOutlined/>} <Action.Button className={styles.drawerBtn} icon={<DownloadOutlined />}
onClick={() => this.handleDownload(info.name)}/> onClick={() => this.handleDownload(info.name)} />
<Action.Button danger auth="host.console.del" className={styles.drawerBtn} icon={<DeleteOutlined/>} <Action.Button danger auth="host.console.del" className={styles.drawerBtn} icon={<DeleteOutlined />}
onClick={() => this.handleDelete(info.name)}/> onClick={() => this.handleDelete(info.name)} />
</Action> </Action>
) : null ) : null
}]; }];
@ -97,23 +99,23 @@ class FileManager extends React.Component {
}; };
fetchFiles = (pwd) => { fetchFiles = (pwd) => {
this.setState({fetching: true}); this.setState({ fetching: true });
pwd = pwd || this.state.pwd; pwd = pwd || this.state.pwd;
const path = '/' + pwd.join('/'); const path = '/' + pwd.join('/');
return http.get('/api/file/', {params: {id: this.props.id, path}}) return http.get('/api/file/', { params: { id: this.props.id, path } })
.then(res => { .then(res => {
const objects = lds.orderBy(res, [this._kindSort, 'name'], ['desc', 'asc']); const objects = lds.orderBy(res, [this._kindSort, 'name'], ['desc', 'asc']);
this.setState({objects, pwd}) this.setState({ objects, pwd })
this.state.inputPath !== null && this.setState({inputPath: path}) this.state.inputPath !== null && this.setState({ inputPath: path })
}) })
.finally(() => this.setState({fetching: false})) .finally(() => this.setState({ fetching: false }))
}; };
handleChdir = (name, action) => { handleChdir = (name, action) => {
let pwd = this.state.pwd.map(x => x); let pwd = this.state.pwd.map(x => x);
if (action === '1') { if (action === '1') {
pwd.push(name) pwd.push(name)
this.setState({inputPath: null}) this.setState({ inputPath: null })
} else if (action === '2') { } else if (action === '2') {
const index = pwd.indexOf(name); const index = pwd.indexOf(name);
pwd = pwd.splice(0, index + 1) pwd = pwd.splice(0, index + 1)
@ -126,23 +128,23 @@ class FileManager extends React.Component {
handleInputEnter = () => { handleInputEnter = () => {
if (this.state.inputPath === null) { if (this.state.inputPath === null) {
if (this.state.pwd.length > 0) { if (this.state.pwd.length > 0) {
this.setState({inputPath: `/${this.state.pwd.join('/')}/`}) this.setState({ inputPath: `/${this.state.pwd.join('/')}/` })
} else { } else {
this.setState({inputPath: '/'}) this.setState({ inputPath: '/' })
} }
setTimeout(() => this.input2.focus(), 100) setTimeout(() => this.input2.focus(), 100)
} else { } else {
let pwdStr = this.state.inputPath.replace(/^\/+/, '') let pwdStr = this.state.inputPath.replace(/^\/+/, '')
pwdStr = pwdStr.replace(/\/+$/, '') pwdStr = pwdStr.replace(/\/+$/, '')
this.fetchFiles(pwdStr.split('/')) this.fetchFiles(pwdStr.split('/'))
.then(() => this.setState({inputPath: null})) .then(() => this.setState({ inputPath: null }))
} }
} }
handleUpload = () => { handleUpload = () => {
this.input.click(); this.input.click();
this.input.onchange = e => { this.input.onchange = e => {
this.setState({uploading: true, uploadStatus: 'active', percent: 0}); this.setState({ uploading: true, uploadStatus: 'active', percent: 0 });
const file = e.target['files'][0]; const file = e.target['files'][0];
const formData = new FormData(); const formData = new FormData();
const token = uniqueId(); const token = uniqueId();
@ -152,18 +154,18 @@ class FileManager extends React.Component {
formData.append('token', token); formData.append('token', token);
formData.append('path', '/' + this.state.pwd.join('/')); formData.append('path', '/' + this.state.pwd.join('/'));
this.input.value = ''; this.input.value = '';
http.post('/api/file/object/', formData, {timeout: 600000, onUploadProgress: this._updateLocal}) http.post('/api/file/object/', formData, { timeout: 600000, onUploadProgress: this._updateLocal })
.then(() => { .then(() => {
this.setState({uploadStatus: 'success'}); this.setState({ uploadStatus: 'success' });
this.fetchFiles() this.fetchFiles()
}, () => this.setState({uploadStatus: 'exception'})) }, () => this.setState({ uploadStatus: 'exception' }))
.finally(() => setTimeout(() => this.setState({uploading: false}), 2000)) .finally(() => setTimeout(() => this.setState({ uploading: false }), 2000))
} }
}; };
_updateLocal = (e) => { _updateLocal = (e) => {
const percent = e.loaded / e.total * 100 / 2 const percent = e.loaded / e.total * 100 / 2
this.setState({percent: Number(percent.toFixed(1))}) this.setState({ percent: Number(percent.toFixed(1)) })
} }
_updatePercent = token => { _updatePercent = token => {
@ -175,7 +177,7 @@ class FileManager extends React.Component {
this.socket.send('ping') this.socket.send('ping')
} else { } else {
const percent = this.state.percent + Number(e.data) / 2; const percent = this.state.percent + Number(e.data) / 2;
if (percent > this.state.percent) this.setState({percent: Number(percent.toFixed(1))}); if (percent > this.state.percent) this.setState({ percent: Number(percent.toFixed(1)) });
if (percent === 100) { if (percent === 100) {
this.socket.close() this.socket.close()
} }
@ -200,7 +202,7 @@ class FileManager extends React.Component {
title: '删除文件确认', title: '删除文件确认',
content: `确认删除文件:${file} ?`, content: `确认删除文件:${file} ?`,
onOk: () => { onOk: () => {
return http.delete('/api/file/object/', {params: {id: this.props.id, file}}) return http.delete('/api/file/object/', { params: { id: this.props.id, file } })
.then(() => { .then(() => {
message.success('删除成功'); message.success('删除成功');
this.fetchFiles() this.fetchFiles()
@ -217,18 +219,18 @@ class FileManager extends React.Component {
const scrollY = document.body.clientHeight - 168; const scrollY = document.body.clientHeight - 168;
return ( return (
<React.Fragment> <React.Fragment>
<input style={{display: 'none'}} type="file" ref={ref => this.input = ref}/> <input style={{ display: 'none' }} type="file" ref={ref => this.input = ref} />
<div className={styles.drawerHeader}> <div className={styles.drawerHeader}>
{this.state.inputPath !== null ? ( {this.state.inputPath !== null ? (
<Input ref={ref => this.input2 = ref} size="small" className={styles.input} <Input ref={ref => this.input2 = ref} size="small" className={styles.input}
suffix={<div style={{color: '#999', fontSize: 12}}>回车确认</div>} suffix={<div style={{ color: '#999', fontSize: 12 }}>回车确认</div>}
value={this.state.inputPath} onChange={e => this.setState({inputPath: e.target.value})} value={this.state.inputPath} onChange={e => this.setState({ inputPath: e.target.value })}
onBlur={this.handleInputEnter} onBlur={this.handleInputEnter}
onPressEnter={this.handleInputEnter}/> onPressEnter={this.handleInputEnter} />
) : ( ) : (
<Breadcrumb className={styles.bread}> <Breadcrumb className={styles.bread}>
<Breadcrumb.Item href="#" onClick={() => this.handleChdir('', '0')}> <Breadcrumb.Item href="#" onClick={() => this.handleChdir('', '0')}>
<HomeOutlined style={{fontSize: 16}}/> <HomeOutlined style={{ fontSize: 16 }} />
</Breadcrumb.Item> </Breadcrumb.Item>
{this.state.pwd.map(item => ( {this.state.pwd.map(item => (
<Breadcrumb.Item key={item} href="#" onClick={() => this.handleChdir(item, '2')}> <Breadcrumb.Item key={item} href="#" onClick={() => this.handleChdir(item, '2')}>
@ -236,7 +238,7 @@ class FileManager extends React.Component {
</Breadcrumb.Item> </Breadcrumb.Item>
))} ))}
<Breadcrumb.Item onClick={this.handleInputEnter}> <Breadcrumb.Item onClick={this.handleInputEnter}>
<EditOutlined className={styles.edit}/> <EditOutlined className={styles.edit} />
</Breadcrumb.Item> </Breadcrumb.Item>
</Breadcrumb> </Breadcrumb>
)} )}
@ -247,17 +249,17 @@ class FileManager extends React.Component {
checked={this.state.showDot} checked={this.state.showDot}
checkedChildren="开启" checkedChildren="开启"
unCheckedChildren="关闭" unCheckedChildren="关闭"
onChange={v => this.setState({showDot: v})}/> onChange={v => this.setState({ showDot: v })} />
{this.state.uploading ? ( {this.state.uploading ? (
<Progress className={styles.progress} strokeWidth={14} status={this.state.uploadStatus} <Progress className={styles.progress} strokeWidth={14} status={this.state.uploadStatus}
percent={this.state.percent}/> percent={this.state.percent} />
) : ( ) : (
<AuthButton <AuthButton
auth="host.console.upload" auth="host.console.upload"
style={{marginLeft: 12}} style={{ marginLeft: 12 }}
size="small" size="small"
type="primary" type="primary"
icon={<UploadOutlined/>} icon={<UploadOutlined />}
onClick={this.handleUpload}>上传文件</AuthButton> onClick={this.handleUpload}>上传文件</AuthButton>
)} )}
</div> </div>
@ -268,9 +270,9 @@ class FileManager extends React.Component {
loading={this.state.fetching} loading={this.state.fetching}
pagination={false} pagination={false}
columns={this.columns} columns={this.columns}
scroll={{y: scrollY}} scroll={{ y: scrollY }}
style={{fontFamily: 'Source Code Pro, Courier New, Courier, Monaco, monospace, PingFang SC, Microsoft YaHei'}} style={{ fontFamily: 'Source Code Pro, Courier New, Courier, Monaco, monospace, PingFang SC, Microsoft YaHei' }}
dataSource={objects}/> dataSource={objects} />
</React.Fragment> </React.Fragment>
) )
} }