@jiangood/open-admin 3.0.4 → 3.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -48,5 +48,5 @@
48
48
  "test:e2e": "playwright test",
49
49
  "test:e2e:ui": "playwright test --ui"
50
50
  },
51
- "version": "3.0.4"
51
+ "version": "3.0.5"
52
52
  }
@@ -6,6 +6,8 @@ import type {FieldProps} from '../types';
6
6
  export interface FieldEditorProps extends FieldProps<string> {
7
7
  /** 编辑器高度,默认 300 */
8
8
  height?: number;
9
+ /** 图片上传可见性,默认 public(富文本图片需公开访问) */
10
+ visibility?: 'public' | 'private';
9
11
  }
10
12
 
11
13
  /**
@@ -15,7 +17,8 @@ export interface FieldEditorProps extends FieldProps<string> {
15
17
  */
16
18
  export class FieldEditor extends React.Component<FieldEditorProps> {
17
19
  render() {
18
- const uploadUrl = UrlUtils.contextPath('/admin/sysFile/upload');
20
+ const visibility = this.props.visibility || 'public';
21
+ const uploadUrl = UrlUtils.contextPath('/admin/sysFile/upload') + '?visibility=' + visibility;
19
22
  const jsUrl = UrlUtils.contextPath('/admin/tinymce/tinymce.min.js');
20
23
  const {value, onChange, height} = this.props;
21
24
 
@@ -8,8 +8,8 @@ import {ViewFile} from "../../views";
8
8
  import {ObjectUtils, UrlUtils} from "../../utils";
9
9
  import type {FieldProps} from '../types';
10
10
 
11
- /** 框架内使用的上传文件对象,额外携带 sysFile 的 id */
12
- export type SysUploadFile = UploadFile & { id?: string };
11
+ /** 框架内使用的上传文件对象,额外携带 sysFile 的 objectName */
12
+ export type SysUploadFile = UploadFile & { objectName?: string };
13
13
 
14
14
  export interface FieldUploadFileProps extends FieldProps<string> {
15
15
  /** 最大上传数量,默认 1 */
@@ -22,6 +22,8 @@ export interface FieldUploadFileProps extends FieldProps<string> {
22
22
  listType?: UploadProps['listType'];
23
23
  /** 接受的文件类型,如 image/* */
24
24
  accept?: string;
25
+ /** 文件可见性,默认 public */
26
+ visibility?: 'public' | 'private';
25
27
  /** 文件列表变化回调(新增文件上传成功后才触发) */
26
28
  onFileChange?: (fileList: SysUploadFile[]) => void;
27
29
  }
@@ -30,9 +32,10 @@ interface FieldUploadFileState {
30
32
  maxCount: number;
31
33
  cropImage: boolean;
32
34
  fileList: SysUploadFile[];
33
- /** 逗号分隔的文件 id */
35
+ /** 逗号分隔的文件 objectName */
34
36
  value: string | null;
35
37
  accept?: string;
38
+ visibility?: 'public' | 'private';
36
39
  }
37
40
 
38
41
  /**
@@ -40,16 +43,17 @@ interface FieldUploadFileState {
40
43
  *
41
44
  * 可参考 react-easy-crop
42
45
  */
43
- export class FieldUploadFile extends React.Component<FieldUploadFileProps, FieldUploadFileState & { errorTitle?: string; errorContent?: string; previewFileId?: string }> {
46
+ export class FieldUploadFile extends React.Component<FieldUploadFileProps, FieldUploadFileState & { errorTitle?: string; errorContent?: string; previewObjectName?: string }> {
44
47
 
45
- state: FieldUploadFileState & { errorTitle?: string; errorContent?: string; previewFileId?: string } = {
48
+ state: FieldUploadFileState & { errorTitle?: string; errorContent?: string; previewObjectName?: string } = {
46
49
  // 传入的参数
47
50
  maxCount: 1,
48
51
  cropImage: false,
52
+ visibility: 'public',
49
53
 
50
54
  // 内部参数
51
55
  fileList: [],
52
- value: null, // 都好分隔的文件id
56
+ value: null, // 逗号分隔的文件objectName
53
57
  };
54
58
 
55
59
  constructor(props: FieldUploadFileProps) {
@@ -62,6 +66,7 @@ export class FieldUploadFile extends React.Component<FieldUploadFileProps, Field
62
66
  const next: Partial<FieldUploadFileState> = {};
63
67
  if (this.props.maxCount !== prevProps.maxCount) next.maxCount = this.props.maxCount;
64
68
  if (this.props.cropImage !== prevProps.cropImage) next.cropImage = this.props.cropImage;
69
+ if (this.props.visibility !== prevProps.visibility) next.visibility = this.props.visibility;
65
70
 
66
71
  const prevValue = prevProps.value ?? null;
67
72
  const curValue = this.props.value ?? null;
@@ -77,9 +82,9 @@ export class FieldUploadFile extends React.Component<FieldUploadFileProps, Field
77
82
  const list: SysUploadFile[] = [];
78
83
  if (value && value.length > 0) {
79
84
  const arr = value.split(",");
80
- for (const id of arr) {
81
- const url = UrlUtils.contextPath('/admin/sysFile/preview/' + id);
82
- const file = {id, url, uid: id, name: id, status: 'done', fileName: id} as SysUploadFile;
85
+ for (const objectName of arr) {
86
+ const url = UrlUtils.contextPath('/file/' + objectName);
87
+ const file = {objectName, url, uid: objectName, name: objectName, status: 'done', fileName: objectName} as SysUploadFile;
83
88
  list.push(file);
84
89
  }
85
90
  }
@@ -88,24 +93,24 @@ export class FieldUploadFile extends React.Component<FieldUploadFileProps, Field
88
93
  }
89
94
 
90
95
  convertComponentValueToOutput(fileList: SysUploadFile[]): string[] {
91
- const fileIds: string[] = [];
96
+ const objectNames: string[] = [];
92
97
  for (const f of fileList) {
93
98
  if (f.status === 'done') {
94
99
  if (f.response) { // 新上传的
95
100
  const ajaxResult = f.response;
96
101
  if (ajaxResult.success) {
97
- const {id, name} = ajaxResult.data;
98
- f.id = id;
99
- fileIds.push(id);
102
+ const {objectName, name} = ajaxResult.data;
103
+ f.objectName = objectName;
104
+ objectNames.push(objectName);
100
105
  } else {
101
106
  this.setState({errorTitle: '上传文件失败', errorContent: ajaxResult.message});
102
107
  }
103
108
  } else { // 老的
104
- fileIds.push(f.id as string);
109
+ objectNames.push(f.objectName as string);
105
110
  }
106
111
  }
107
112
  }
108
- return fileIds;
113
+ return objectNames;
109
114
  }
110
115
 
111
116
  handleChange = ({fileList, event, file}: UploadChangeParam<SysUploadFile>) => {
@@ -133,7 +138,7 @@ export class FieldUploadFile extends React.Component<FieldUploadFileProps, Field
133
138
  };
134
139
 
135
140
  handlePreview = (file) => {
136
- this.setState({previewFileId: file.id});
141
+ this.setState({previewObjectName: file.objectName});
137
142
  };
138
143
 
139
144
  render() {
@@ -150,9 +155,9 @@ export class FieldUploadFile extends React.Component<FieldUploadFileProps, Field
150
155
  onOk={() => this.setState({errorTitle: undefined})}>
151
156
  {this.state.errorContent}
152
157
  </Modal>
153
- <Modal open={!!this.state.previewFileId} title="文件预览" width="80vw" footer={null}
154
- onCancel={() => this.setState({previewFileId: undefined})}>
155
- {this.state.previewFileId && <ViewFile value={this.state.previewFileId} height='70vh'/>}
158
+ <Modal open={!!this.state.previewObjectName} title="文件预览" width="80vw" footer={null}
159
+ onCancel={() => this.setState({previewObjectName: undefined})}>
160
+ {this.state.previewObjectName && <ViewFile value={this.state.previewObjectName} height='70vh'/>}
156
161
  </Modal>
157
162
  </>
158
163
  }
@@ -162,6 +167,7 @@ export class FieldUploadFile extends React.Component<FieldUploadFileProps, Field
162
167
 
163
168
  return <Upload
164
169
  action={UrlUtils.contextPath('/admin/sysFile/upload')}
170
+ data={{visibility: this.state.visibility}}
165
171
  listType={this.props.listType || 'picture-card'}
166
172
  fileList={fileList}
167
173
  onChange={this.handleChange}
@@ -7,15 +7,15 @@ export class ViewFile extends React.Component {
7
7
 
8
8
 
9
9
  render() {
10
- const fileId = this.props.value
10
+ const value = this.props.value
11
11
 
12
- if (!fileId) {
12
+ if (!value) {
13
13
  return <Empty/>;
14
14
  }
15
15
 
16
- const arr = fileId.split(',');
16
+ const arr = value.split(',');
17
17
 
18
- const urlList = arr.map(id => UrlUtils.contextPath('/admin/sysFile/preview/' + id));
18
+ const urlList = arr.map(objectName => UrlUtils.contextPath('/file/' + objectName));
19
19
  const height = this.props.height;
20
20
 
21
21
  if(urlList.length === 1){
@@ -23,16 +23,16 @@ export class ViewImage extends React.Component {
23
23
 
24
24
  const urlList = [];
25
25
  for (let v of vs) {
26
- const isId = v.indexOf('/') === -1;
27
26
  const isAbsUrl = v.startsWith('http');
28
27
  const isDataUrl = v.startsWith('data:');
28
+ const isObjectName = v.startsWith('public/') || v.startsWith('private/');
29
29
  if (isAbsUrl || isDataUrl) {
30
30
  urlList.push(v);
31
31
  continue;
32
32
  }
33
33
 
34
- if (isId) {
35
- urlList.push(UrlUtils.contextPath('/admin/sysFile/preview/' + v));
34
+ if (isObjectName) {
35
+ urlList.push(UrlUtils.contextPath('/file/' + v));
36
36
  continue;
37
37
  }
38
38
 
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import {HttpUtils, Page} from "../../framework";
2
+ import {HttpUtils, Page, UrlUtils} from "../../framework";
3
3
 
4
4
  export default class extends React.Component {
5
5
 
@@ -37,6 +37,11 @@ export default class extends React.Component {
37
37
  }
38
38
 
39
39
  return <Page title={article.title}>
40
+ {article.mainImage && (
41
+ <div style={{marginBottom: 16}}>
42
+ <img src={UrlUtils.contextPath('/file/' + article.mainImage)} style={{maxWidth: '100%'}} alt='主图'/>
43
+ </div>
44
+ )}
40
45
  <div dangerouslySetInnerHTML={{__html: article.content}}/>
41
46
  </Page>
42
47
  }
@@ -1,16 +1,18 @@
1
1
  import {PlusOutlined} from '@ant-design/icons'
2
- import {Button, Form, Input, InputNumber, Popconfirm} from 'antd'
2
+ import {Button, Form, Input, InputNumber, Modal, Popconfirm} from 'antd'
3
3
  import React from 'react'
4
4
  import {
5
5
  DictUtils,
6
6
  FieldBoolean,
7
7
  FieldDictSelect,
8
8
  FieldEditor,
9
+ FieldUploadFile,
9
10
  FormModal,
10
11
  HttpUtils,
11
12
  Page,
12
13
  PermActions,
13
- ProTable
14
+ ProTable,
15
+ UrlUtils
14
16
  } from "../../../framework";
15
17
 
16
18
  const {TextArea} = Input;
@@ -19,6 +21,7 @@ export default class extends React.Component {
19
21
 
20
22
  state = {
21
23
  editing: false,
24
+ previewArticle: null,
22
25
  }
23
26
 
24
27
  modalRef = React.createRef()
@@ -34,6 +37,10 @@ export default class extends React.Component {
34
37
  this.modalRef.current.open(record)
35
38
  }
36
39
 
40
+ handlePreview = record => {
41
+ this.setState({previewArticle: record})
42
+ }
43
+
37
44
  onFinish = async values => {
38
45
  const isNew = !values.id;
39
46
  const url = isNew ? 'admin/article/create' : 'admin/article/update';
@@ -80,6 +87,7 @@ export default class extends React.Component {
80
87
  render: (_, record) => {
81
88
  return (
82
89
  <PermActions>
90
+ <Button size='small' onClick={() => this.handlePreview(record)}>预览</Button>
83
91
  <Button size='small' perm='article:update' onClick={() => this.handleEdit(record)}>编辑</Button>
84
92
  <Popconfirm perm='article:delete' title='确定删除?' onConfirm={() => this.handleDelete(record)}>
85
93
  <Button size='small'>删除</Button>
@@ -91,6 +99,7 @@ export default class extends React.Component {
91
99
  ]
92
100
 
93
101
  render() {
102
+ const {previewArticle} = this.state
94
103
  return <Page
95
104
  title="文章管理"
96
105
  description="管理系统文章,如关于、帮助等页面"
@@ -126,6 +135,10 @@ export default class extends React.Component {
126
135
  <Input/>
127
136
  </Form.Item>
128
137
 
138
+ <Form.Item label='主图' name='mainImage'>
139
+ <FieldUploadFile maxCount={1} cropImage accept='image/*'/>
140
+ </Form.Item>
141
+
129
142
  <Form.Item label='内容' name='content'>
130
143
  <FieldEditor />
131
144
  </Form.Item>
@@ -142,6 +155,21 @@ export default class extends React.Component {
142
155
  <FieldBoolean/>
143
156
  </Form.Item>
144
157
  </FormModal>
158
+
159
+ <Modal
160
+ open={!!previewArticle}
161
+ title={previewArticle?.title}
162
+ width={800}
163
+ footer={null}
164
+ onCancel={() => this.setState({previewArticle: null})}
165
+ >
166
+ {previewArticle?.mainImage && (
167
+ <div style={{marginBottom: 16}}>
168
+ <img src={UrlUtils.contextPath('/file/' + previewArticle.mainImage)} style={{maxWidth: '100%'}} alt='主图'/>
169
+ </div>
170
+ )}
171
+ <div dangerouslySetInnerHTML={{__html: previewArticle?.content || ''}}/>
172
+ </Modal>
145
173
  </Page>
146
174
  }
147
175
  }
@@ -15,7 +15,7 @@ export default class extends React.Component {
15
15
  formRef = React.createRef()
16
16
 
17
17
  handleDelete = row => {
18
- HttpUtils.post('admin/sysFile/delete', row).then(rs => {
18
+ HttpUtils.post('admin/sysFile/delete', {value: row.objectName}).then(rs => {
19
19
  this.tableRef.current.reload()
20
20
  })
21
21
  }
@@ -57,21 +57,9 @@ export default class extends React.Component {
57
57
  },
58
58
  {
59
59
  title: '预览',
60
- dataIndex: 'id',
61
- render(id, record) {
62
- const nodes = [<a href={UrlUtils.contextPath('/admin/sysFile/preview/' + record.id)} target='_blank'>预览</a>]
63
-
64
-
65
- if (record.imageUrls?.length > 0) {
66
- for (let item of record.imageUrls) {
67
- const url = item.url;
68
- const label = item.label;
69
- nodes.push(<a href={(url)} target='_blank' style={{marginLeft: 8}}>{label}</a>)
70
- }
71
- }
72
-
73
- return nodes;
74
-
60
+ dataIndex: 'objectName',
61
+ render(objectName) {
62
+ return <a href={UrlUtils.contextPath('/file/' + objectName)} target='_blank'>预览</a>
75
63
  }
76
64
  },
77
65
  {
@@ -294,7 +294,7 @@ export default class extends React.Component {
294
294
  title: '操作',
295
295
  dataIndex: 'option',
296
296
  render: (_, record) => {
297
- const url = UrlUtils.contextPath('/admin/sys/log/' + record.id);
297
+ const url = UrlUtils.contextPath('/admin/sys/log/job/' + record.id);
298
298
  return <a href={url} target='_blank'>日志</a>;
299
299
  },
300
300
  }