Commit f35972ec authored by zhangwenshuai's avatar zhangwenshuai

update

parent f15c9a8c
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
"build": "umi-lib build", "build": "umi-lib build",
"lint:ts": "tslint \"src/**/*.ts\" \"src/**/*.tsx\"", "lint:ts": "tslint \"src/**/*.ts\" \"src/**/*.tsx\"",
"precommit": "lint-staged:ts", "precommit": "lint-staged:ts",
"start": "webpack" "start": "cross-env BUILD_ENV=development PROXY_ENV=development ENV=development webpack-dev-server"
}, },
"peerDependencies": { "peerDependencies": {
"react": "16.x" "react": "16.x"
...@@ -52,6 +52,7 @@ ...@@ -52,6 +52,7 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"antd": "^3.25.2", "antd": "^3.25.2",
"dom-helpers": "^5.1.3",
"father": "^2.29.1", "father": "^2.29.1",
"file-saver": "^2.0.2", "file-saver": "^2.0.2",
"react-dom": "^16.12.0", "react-dom": "^16.12.0",
......
@import '../../common';
.colContainer {
display: flex;
align-items: center;
justify-content: space-between;
height: 100%;
width: 100%;
.colBrief {
display: flex;
align-items: center;
flex: 1;
width: 100%;
.colIcon {
width: @textFontGen;
color: @textPrimaryColor;
}
.colTitle {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: left;
margin-left: 5px;
color: @textPrimaryColor;
}
}
.colOpera {
width: 20px;
text-align: center;
cursor: pointer;
.colOperaIcon {
width: 7px;
height: 5px;
}
}
}
import React, { PureComponent } from 'react';
import { config } from './config';
import s from './Row.less';
import { ColumnProps } from './interface';
import IconFont from './base/extra/iconFont';
export default class TableRow extends PureComponent<ColumnProps> {
render() {
let { type, title, width = 200, columnAttrObj = {} } = this.props;
if (!config[String(type)]) {
type = '1';
}
let icon = config[String(type)] && config[String(type)].icon;
if (String(type) === '13' && !columnAttrObj.isMultiple) {
icon = 'iconziduan-lianxiangdanxuan';
}
return (
<div className={s.colContainer} style={{ width }}>
<div className={s.colBrief}>
{icon && (typeof icon === 'string' ? <IconFont className={s.colIcon} type={icon} /> : icon)}
<span className={s.colTitle}>{title}</span>
</div>
</div>
);
}
}
This diff is collapsed.
...@@ -29,13 +29,13 @@ export const config: any = { ...@@ -29,13 +29,13 @@ export const config: any = {
}, },
detail: require('./detail/textarea').default, detail: require('./detail/textarea').default,
}, },
'4': { // '4': {
name: '附件上传', // name: '附件上传',
component: require('./edit/upload').default, // component: require('./edit/upload').default,
getFormatter: GetFormatter['UPLOAD'], // getFormatter: GetFormatter['UPLOAD'],
setFormatter: SetFormatter['UPLOAD'], // setFormatter: SetFormatter['UPLOAD'],
detail: require('./detail/upload').default, // detail: require('./detail/upload').default,
}, // },
'5': { '5': {
name: '复选', name: '复选',
component: require('./edit/checkbox').default, // 暂未添加 component: require('./edit/checkbox').default, // 暂未添加
......
import { DatePickerProps } from 'antd/es/date-picker/interface';
export interface CommonProps { export interface CommonProps {
value: any[] | undefined; value: any[] | undefined;
placeholder?: string; placeholder?: string;
...@@ -32,8 +34,7 @@ export interface SelectProps extends CommonProps { ...@@ -32,8 +34,7 @@ export interface SelectProps extends CommonProps {
maxCount?: number; maxCount?: number;
tableId?: number; tableId?: number;
options?: any[]; options?: any[];
allowClear?:boolean; allowClear?: boolean;
} }
export interface RateProps { export interface RateProps {
value: number; value: number;
...@@ -60,9 +61,4 @@ export interface NumberState { ...@@ -60,9 +61,4 @@ export interface NumberState {
value: number | undefined; value: number | undefined;
cellData?: any[]; cellData?: any[];
} }
export interface DateProps extends CommonProps { export interface DateProps extends DatePickerProps {}
allowClear?: boolean;
disabled?: boolean;
format?: string | string[];
showTime?: Object | boolean;
}
import React from 'react'; import React from 'react';
import { Dropdown, Menu, Empty, Spin, Icon, Input } from 'antd'; import { Menu, Empty, Spin, Icon, Input, Dropdown } from 'antd';
import { findDOMNode } from 'react-dom'; import { findDOMNode } from 'react-dom';
import lodash from 'lodash'; import lodash from 'lodash';
import styles from './styles.less'; import styles from './styles.less';
...@@ -7,9 +7,37 @@ import styles from './styles.less'; ...@@ -7,9 +7,37 @@ import styles from './styles.less';
/* /*
* 此空间用于数据选择及输入控制,若不选择只传入输入框数据 * 此空间用于数据选择及输入控制,若不选择只传入输入框数据
*/ */
interface Props {
class TextSelect extends React.Component { initDataType?: undefined | 'onfocus'; // 初始化请求方式
request?: Function;
selfCom?: any;
dataSource?: [];
onChange: Function;
value?: any;
autoFocus?: boolean;
mode?: 'multiple' | 'tags' | undefined;
fieldNames?: {
value: any;
label: string;
};
placeholder?:string;
width?:number;
disabled?:boolean;
}
interface State {
searchStr: string,
list: any[],
loading: boolean,
selected: any[],
tempSelected: any[],
tempVisible: boolean,
}
class TextSelect extends React.Component<Props,State> {
container: any;
input: any; input: any;
width: number;
inputWidth = 320;
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
...@@ -20,15 +48,15 @@ class TextSelect extends React.Component { ...@@ -20,15 +48,15 @@ class TextSelect extends React.Component {
tempSelected: undefined, tempSelected: undefined,
tempVisible: false, tempVisible: false,
}; };
this._fetch = lodash.debounce(this._fetch, 400); this.container = React.createRef();
this.input = React.createRef();
this.fetch = lodash.debounce(this.fetch, 400);
} }
inputWidth: number = 320;
static getDerivedStateFromProps(nextProps, prevState) { static getDerivedStateFromProps(nextProps, prevState) {
if (JSON.stringify(nextProps.value) !== JSON.stringify(prevState.tempSelected)) { if (JSON.stringify(nextProps.value) !== JSON.stringify(prevState.tempSelected)) {
let searchStr = const [first] = nextProps.value || [];
nextProps.value && nextProps.value[0] ? nextProps.value[0].label || nextProps.value[0].text : ''; const searchStr = first ? first.label || first.text : '';
return { return {
tempSelected: nextProps.value || [], tempSelected: nextProps.value || [],
selected: nextProps.value, selected: nextProps.value,
...@@ -43,81 +71,109 @@ class TextSelect extends React.Component { ...@@ -43,81 +71,109 @@ class TextSelect extends React.Component {
} }
getInputWidth = () => { getInputWidth = () => {
if (this.input && findDOMNode(this.input)) { if (this.input.current) {
this.width = findDOMNode(this.input).offsetWidth; const dom = findDOMNode(this.input.current);
if (dom) {
// @ts-ignore
this.width = dom.offsetWidth;
}
} }
}; };
_fetch = async (val) => {
const request = this.props.request; fetch = async (val) => {
const { request } = this.props;
if (!request) return; if (!request) return;
await this.setState({ loading: true, list: [] }); await this.setState({ loading: true, list: [] });
const result = await request(val); const result = await request(val);
if (result && result.success) { if (result && result.success) {
const data = result.data || {}; const data = result.data || {};
let list = Array.isArray(data.list) ? data.list : []; let list = Array.isArray(data.list) ? data.list : [];
list = this.formateData(list); list = this.formatData(list);
this.setState({ list }); this.setState({ list });
} }
await this.setState({ loading: false }); await this.setState({ loading: false });
}; };
formateData = (data) => {
formatData = (data) => {
const { fieldNames = { label: 'name', value: 'id' } } = this.props || {}; const { fieldNames = { label: 'name', value: 'id' } } = this.props || {};
return data.map((item) => ({ const { label, value } = fieldNames;
return data.map((item) => {
return {
...item, ...item,
_label: item[fieldNames.label], _label: item[label],
_value: item[fieldNames.value], _value: item[value],
})); };
});
}; };
onResetValue = (searchStr) => { onResetValue = (searchStr) => {
return { value: '', label: searchStr }; return [{ value: '', label: searchStr }];
}; };
onSearch = (e) => { onSearch = (e) => {
const searchStr = e.currentTarget.value; const searchStr = e.currentTarget.value;
const selected = this.onResetValue(searchStr); const selected = this.onResetValue(searchStr);
this.setState({ searchStr, selected }, () => { this.setState({ searchStr, selected }, () => {
this._fetch(searchStr); this.fetch(searchStr);
this.onChange(selected); this.onChange(selected);
}); });
}; };
onFocus = () => { onFocus = () => {
this._fetch(this.state.searchStr); const { searchStr } = this.state;
this.fetch(searchStr);
}; };
onClickMenu = ({ item, key }) => { onClickMenu = ({ item, key }) => {
const { props } = item || {}; const { props } = item || {};
const { title } = props || {}; const { title } = props || {};
const selected = { label: title, value: key }; const selected = [{ label: title, value: key }];
this.setState({ selected, searchStr: title }); this.setState({ selected, searchStr: title });
this.onVisibleChange(false); this.onVisibleChange(false);
this.onChange(selected); this.onChange(selected);
}; };
onChange = (obj) => { onChange = (obj) => {
this.props.onChange && this.props.onChange(obj); const { onChange } = this.props;
if (typeof onChange === 'function') {
onChange(obj);
}
}; };
onClear = () => { onClear = () => {
const { tempVisible } = this.state; const { tempVisible } = this.state;
const searchStr = ''; const searchStr = '';
const selected = this.onResetValue(searchStr); const selected = this.onResetValue(searchStr);
this.setState({ searchStr, selected }, () => { this.setState({ searchStr, selected }, () => {
tempVisible && this._fetch(searchStr); if (tempVisible) {
this.fetch(searchStr);
}
this.onChange(selected); this.onChange(selected);
}); });
}; };
onVisibleChange = (tempVisible) => { onVisibleChange = (tempVisible) => {
this.setState({ tempVisible }); this.setState({ tempVisible });
}; };
renderList = () => { renderList = () => {
const { list, loading, selected } = this.state; const { list, loading, selected } = this.state;
const { width } = this.props;
return ( return (
<Menu <Menu
style={{ width: this.props.width || this.width }} style={{ width: width || this.width }}
onClick={this.onClickMenu} onClick={this.onClickMenu}
selectedKeys={selected ? [selected.value] : []} selectedKeys={selected ? [selected[0].value] : []}
> >
{list.map((item) => ( {list.map((item) => {
return (
// eslint-disable-next-line no-underscore-dangle
<Menu.Item key={item._value} title={item._label}> <Menu.Item key={item._value} title={item._label}>
{/* eslint-disable-next-line no-underscore-dangle */}
{item._label} {item._label}
</Menu.Item> </Menu.Item>
))} );
})}
{loading ? ( {loading ? (
<Menu.Item> <Menu.Item>
<span className={styles.spin}> <span className={styles.spin}>
...@@ -133,54 +189,36 @@ class TextSelect extends React.Component { ...@@ -133,54 +189,36 @@ class TextSelect extends React.Component {
</Menu> </Menu>
); );
}; };
renderSuffix = () => {
if (this.props.suffix) return this.props.suffix; getContainer = () => {
const { searchStr } = this.state; return this.container.current;
const { allowClear = true } = this.props;
return (
<>
<span className={styles.searchIcon}>
{' '}
<Icon type="search" />
</span>
{searchStr.length > 0 && allowClear ? (
<span className={styles.clearIcon} onClick={this.onClear}>
{' '}
<Icon
type="close-circle"
theme="filled"
style={{
fontSize: '14px',
color: 'rgba(0,0,0,.25)',
}}
/>
</span>
) : null}
</>
);
}; };
render() { render() {
const { placeholder } = this.props; const { placeholder, disabled } = this.props;
const { searchStr } = this.state;
return ( return (
<span className={styles.container} ref={this.container}>
<Dropdown <Dropdown
trigger={['click']} trigger={['click']}
overlayClassName={styles.overlayClassName} overlayClassName={styles.overlayClassName}
overlayStyle={{ maxHeight: 400, overflow: 'auto', marginTop: '7px' }}
onVisibleChange={this.onVisibleChange} onVisibleChange={this.onVisibleChange}
placement={'bottomCenter'} placement="bottomCenter"
overlay={this.renderList()} overlay={this.renderList()}
getPopupContainer={this.getContainer}
disabled={disabled}
> >
<Input <Input
ref={(dom) => (this.input = dom)} ref={this.input}
className={styles.input} className={styles.input}
value={this.state.searchStr} value={searchStr}
placeholder={placeholder} placeholder={placeholder}
onChange={this.onSearch} onChange={this.onSearch}
onFocus={this.onFocus} onFocus={this.onFocus}
suffix={this.renderSuffix()} disabled={disabled}
/> />
</Dropdown> </Dropdown>
</span>
); );
} }
} }
......
.container {
position: relative;
}
.overlayClassName {
max-height: 300px;
overflow: auto;
margin-top: 20px;
}
.searchIcon{ .searchIcon{
position: absolute; position: absolute;
top: 50%; top: 50%;
......
export const CDN_PATH = 'https://static.mttop.cn/admin';
export const CDN_HOST = 'https://static.mttop.cn';
export const fileConfig = {
image: {},
ppt: {
thumbUrl: `${CDN_PATH}/pptIcon.png`,
},
pptx: {
thumbUrl: `${CDN_PATH}/pptIcon.png`,
},
zip: {
thumbUrl: `${CDN_PATH}/zipIcon.png`,
},
xlsx: {
thumbUrl: `${CDN_PATH}/xlsIcon.png`,
},
xls: {
thumbUrl: `${CDN_PATH}/xlsIcon.png`,
},
doc: {
thumbUrl: `${CDN_PATH}/docIcon.png`,
},
docx: {
thumbUrl: `${CDN_PATH}/docIcon.png`,
},
pdf: {
thumbUrl: `${CDN_PATH}/pdfIcon.png`,
},
other: {
thumbUrl: `${CDN_PATH}/otherIcon.png`,
},
};
@import "../../../../../common";
.fileList {
display: flex;
flex-flow: row;
flex-wrap: wrap;
}
.fileListCenter {
justify-content: center;
}
.wrap {
position: relative;
cursor: pointer;
}
.wrap:hover .fileBoxHove {
display: flex;
}
.fileBox {
padding: 10px;
width: 100px;
display: flex;
flex-flow: column;
justify-items: center;
align-items: center;
border-radius: 4px;
overflow: hidden;
}
.fileBoxHove {
position: absolute;
top: 0;
left: 0;
display: none;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
border-radius: 4px;
.icon {
color: #fff;
padding: 5px;
}
}
.fileName {
margin-top: 5px;
width: 80px;
font-size: 12px;
font-weight: 400;
color: rgba(90, 104, 118, 1);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: center;
}
.fileStyle {
height: 56px;
}
.dialog {
width: 900px !important;
min-height: 500px;
}
.textUploadList {
box-sizing: border-box;
margin: 0;
padding: 0;
color: rgba(0, 0, 0, 0.65);
font-size: 14px;
font-variant: tabular-nums;
line-height: 1.5;
list-style: none;
-webkit-font-feature-settings: 'tnum';
font-feature-settings: 'tnum';
zoom: 1;
.textUploadListItem {
position: relative;
height: 22px;
margin-top: 8px;
font-size: 14px;
.textUploadListItemInfo {
height: 100%;
padding: 0 12px 0 4px;
.textUploadListItemBox {
display: flex;
align-items: center;
.textUploadListItemInfoName {
display: inline-block;
margin-right: 10px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
color: #5a6876;
&:hover {
color: #5c99ff;
}
}
}
}
}
}
.downIcon {
display: inline-block;
position: absolute;
right: 0;
top: 50%;
transform: translate(0, -50%);
cursor: pointer;
}
.pictureTextUploadList {
box-sizing: border-box;
margin: 0;
padding: 0;
color: rgba(0, 0, 0, 0.65);
font-size: 14px;
font-variant: tabular-nums;
line-height: 1.5;
list-style: none;
-webkit-font-feature-settings: 'tnum';
font-feature-settings: 'tnum';
zoom: 1;
overflow: hidden;
width: 100%;
.textUploadListItem {
position: relative;
display: flex;
justify-items: center;
height: 41px;
margin-top: 16px;
font-size: 14px;
width: 100%;
.textUploadListItemInfo {
width: 100%;
height: 100%;
padding: 0 12px 0 4px;
.textUploadListItemBox {
display: flex;
align-items: center;
height: 100%;
.fileImgStyle {
width: 32px;
margin-right: 10px;
cursor: pointer;
}
.textUploadListItemInfoName {
display: inline-block;
margin-right: 10px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
color: @textGeneralColor;
&:hover {
color: @primaryColor;
}
}
&:hover{
.downIcon{
opacity: 1;
}
}
.downIcon {
display: inline-block;
position: absolute;
right: 0;
top: 50%;
transform: translate(0, -50%);
cursor: pointer;
color: @textSupplyColor;
opacity: 0;
transition: opacity 0.2s linear;
}
}
}
}
}
// .textUploadListItemInfo:hover {
// background: #e6f7ff;
// }
import React from 'react';
import { Icon, message, Tooltip } from 'antd';
import { saveAs } from 'file-saver';
import styles from './detail.less';
import { checkoutFileType, handleName } from './utils/utils';
import Preview from './preview';
import { renderTxt } from '@/utils/hoverPopover';
import { CDN_HOST } from './config';
/*
* listType = 'text' 以文本展示
* listType = 'picture' 以圖文展示
*/
/* eslint-disable */
export default class uploadDetail extends React.Component {
model: any;
onPreview = (item: any) => {
if (this.model && this.model.onPreview) {
const url = `${item.domain ? 'https://' + item.domain : CDN_HOST}/${item.value}`;
this.model.onPreview(url);
}
};
onDownLoad = (item: any) => {
// debugger
if (!item.value) {
message('下载异常');
return;
}
const url = `${item.domain ? 'https://' + item.domain : CDN_HOST}/${item.value}`;
// const typeArr = url.match(/\.[a-zA-Z]+$/);
// const type = typeArr && typeArr[0] ? typeArr[0].replace('.', '') : '';
saveAs(url, item.name);
// let a = document.createElement('a');
// a.href = url;
// a.target = '_blank';
// a.download = item.name;
// a.click();
// a = null;
};
handleCancel = () => {
this.setState({ showDialog: false });
};
checkoutDetailType = () => {
const listType = this.props.listType;
switch (listType) {
case 'picture': // 单纯图片
return this.renderUpload();
case 'text': // 淡村文本
return this.rendertextUpload();
case 'picture-text': // 图文混排
return this.renderPictureText();
default:
return this.renderUpload();
}
};
renderPictureText = (item = this.props.data || []) => {
if (!Array.isArray(item)) return item;
return (
<ul className={styles.pictureTextUploadList}>
{item.map((ls, num) => (
<li key={num} className={styles.textUploadListItem}>
<div className={styles.textUploadListItemInfo}>
<span className={styles.textUploadListItemBox}>
<img
ref={(img) => (this.img = img)}
className={styles.fileImgStyle}
onClick={this.onPreview.bind(this, ls)}
src={
checkoutFileType(`${ls.domain ? 'https://' + ls.domain : CDN_HOST}/${ls.value}`)
.thumbUrl
}
/>
<Tooltip title={ls.name} placement="bottom">
<a
className={styles.textUploadListItemInfoName}
onClick={this.onPreview.bind(this, ls)}
>
{ls.name}
</a>
</Tooltip>
<Icon
type="download"
className={styles.downIcon}
onClick={this.onDownLoad.bind(this, ls)}
/>
</span>
</div>
</li>
))}
</ul>
);
};
rendertextUpload = (item = this.props.data || []) => {
if (!Array.isArray(item)) return item;
return (
<ul className={styles.textUploadList}>
{item.map((ls, index) => (
<li key={index} className={styles.textUploadListItem}>
<div className={styles.textUploadListItemInfo}>
<span className={styles.textUploadListItemBox}>
<i aria-label="图标: paper-clip" className="anticon anticon-paper-clip">
<svg
viewBox="64 64 896 896"
focusable="false"
className=""
data-icon="paper-clip"
width="1em"
height="1em"
fill="currentColor"
aria-hidden="true"
>
<path d="M779.3 196.6c-94.2-94.2-247.6-94.2-341.7 0l-261 260.8c-1.7 1.7-2.6 4-2.6 6.4s.9 4.7 2.6 6.4l36.9 36.9a9 9 0 0 0 12.7 0l261-260.8c32.4-32.4 75.5-50.2 121.3-50.2s88.9 17.8 121.2 50.2c32.4 32.4 50.2 75.5 50.2 121.2 0 45.8-17.8 88.8-50.2 121.2l-266 265.9-43.1 43.1c-40.3 40.3-105.8 40.3-146.1 0-19.5-19.5-30.2-45.4-30.2-73s10.7-53.5 30.2-73l263.9-263.8c6.7-6.6 15.5-10.3 24.9-10.3h.1c9.4 0 18.1 3.7 24.7 10.3 6.7 6.7 10.3 15.5 10.3 24.9 0 9.3-3.7 18.1-10.3 24.7L372.4 653c-1.7 1.7-2.6 4-2.6 6.4s.9 4.7 2.6 6.4l36.9 36.9a9 9 0 0 0 12.7 0l215.6-215.6c19.9-19.9 30.8-46.3 30.8-74.4s-11-54.6-30.8-74.4c-41.1-41.1-107.9-41-149 0L463 364 224.8 602.1A172.22 172.22 0 0 0 174 724.8c0 46.3 18.1 89.8 50.8 122.5 33.9 33.8 78.3 50.7 122.7 50.7 44.4 0 88.8-16.9 122.6-50.7l309.2-309C824.8 492.7 850 432 850 367.5c.1-64.6-25.1-125.3-70.7-170.9z" />
</svg>
</i>
<a
className={styles.textUploadListItemInfoName}
onClick={this.onPreview.bind(this, ls)}
>
{ls.name}
</a>
<Icon
type="download"
className={styles.downIcon}
onClick={this.onDownLoad.bind(this, ls)}
/>
</span>
</div>
</li>
))}
</ul>
);
};
renderUpload = (item = this.props.data || []) => {
if (!Array.isArray(item)) return item;
return item.map((ls, index) => {
return (
<div className={styles.wrap} key={index}>
<div className={styles.fileBox}>
<img
ref={(img) => (this.img = img)}
className={styles.fileStyle}
src={
checkoutFileType(`${ls.domain ? 'https://' + ls.domain : CDN_HOST}/${ls.value}`)
.thumbUrl
}
/>
<span className={styles.fileName}>{ls.name}</span>
</div>
<div className={styles.fileBoxHove}>
<span className={styles.icon} onClick={this.onPreview.bind(this, ls)}>
<Icon type="eye" style={{ fontSize: '20px', color: '#fff' }} />
</span>
<a className={styles.icon} onClick={this.onDownLoad.bind(this, ls)}>
<Icon type="download" style={{ fontSize: '20px', color: '#fff' }} />
</a>
</div>
</div>
);
});
};
render() {
const { stylePosition } = this.props;
return (
<>
<Preview ref={(dom) => (this.model = dom)} />
<div className={`${styles.fileList} ${stylePosition === 'center' ? styles.fileListCenter : null}`}>
{this.checkoutDetailType()}
</div>
</>
);
}
}
import React from 'react';
import { Button, Icon, message, Upload } from 'antd';
import moment from 'moment';
import Detail from './detail';
import { checkoutFileType } from './utils/utils';
import { getToken } from '../../../../services/globalSearchApi';
import Preview from './preview';
import { getRandom } from '../../../../utils/utils';
import './styles.less';
import { CDN_HOST } from './config';
message.config({
maxCount: 1,
});
const formatFileList = ({ list = [], fileList, domain, setFormat }: any) => {
return list.map((item, key) => {
//增加自定义回显数据格式
let value = setFormat ? setFormat(item) : item;
//处理上传成功但没保存到后台的数据(onSaveFileList把数据同步到父组件导致子组件重新渲染处理数据不一致)
let file = fileList
.filter((f) => f.status === 'done')
.find((f) => {
const url = `${domain ? 'https://' + domain : CDN_HOST}/${f.response.key}`;
return url === value.value;
});
if (file) {
return file;
}
//处理保存到后台的数据
return {
...value,
uid: value.value,
url: value.value,
...checkoutFileType(value.value),
};
});
};
const UploadCom: any = class UploadCom extends React.Component {
model: any;
static getDerivedStateFromProps(nextProps, prevState) {
const { prevProps } = prevState;
let nextState = {
...prevState,
prevProps: nextProps,
};
if (JSON.stringify(prevProps.value) !== JSON.stringify(nextProps.value)) {
nextState.fileList = formatFileList({
list: nextProps.value,
fileList: prevState.fileList,
domain: prevState.domain,
setFormat: nextProps.setFormat,
});
}
return nextState;
}
constructor(props) {
super(props);
const { value, setFormat } = props;
const fileList = formatFileList({ list: value, fileList: [], domain: null, setFormat: setFormat });
this.state = {
prevProps: props,
fileList,
token: null,
domain: null,
};
}
//获取七牛上传token
getToken = async () => {
let response = await getToken();
if (response && response.success && response.data) {
this.setState({
token: response.data.token,
domain: response.data.domain,
});
}
};
//自定义七牛文件唯一key
getKey = (file: any) => {
if (!file) return;
let suffix = file.name.match(/\.\w+$/)[0];
let rand6: string = getRandom();
let time = moment().format('YYYYMMDDHHmmss');
return time + rand6 + suffix;
};
//七牛上传额外数据,token和key
getData = (file: any) => {
const { token } = this.state;
return {
token,
key: this.getKey(file),
};
};
static checkoutFileType = checkoutFileType;
onSaveFileList = (fileList = []) => {
if (this.props.onChange) {
const newList = fileList
.filter((item: any) => item.value)
.map((item: any) => ({
name: item.name,
value: item.value,
size: item.size,
}));
this.props.onChange(newList);
}
};
beforeUpload = async () => {
await this.getToken();
return true;
};
onChange = ({ file, fileList, event }: any) => {
const { domain } = this.state;
let newList = fileList;
// const filterFilter = fileList.filter((item) => item.status && item.status !== 'done');
// if (filterFilter.length > 5) {
// message.warn('文件不能超过5个');
// return;
// }
if (file.status === 'done' && file.response) {
const data = file.response || {};
const url = `${domain ? 'https://' + domain : CDN_HOST}/${data.key}`;
//上传成功后处理为服务端数据结构
const itemObj = {
uid: file.uid,
name: file.name,
value: url,
size: file.size,
url,
domain,
};
newList = fileList.map((item: any) => {
return item.uid === file.uid ? { ...item, ...itemObj } : item;
});
this.onSaveFileList(newList);
}
//ant upload组件实时更新上传状态必须实时setState最新文件列表
this.setState({ fileList: newList });
};
previewFile = (file: any) => {
return new Promise((res, rej) => {
const obj = checkoutFileType(file.name);
const typeArr = file.name.match(/\.[a-zA-Z]+$/);
const type = typeArr && typeArr[0] ? typeArr[0].replace('.', '') : '';
if (!['png', 'gif', 'bmp', 'jpg', 'jpeg'].includes(type)) {
res(obj.thumbUrl);
}
});
};
onPreview = (file: any) => {
if (this.model && this.model.onPreview) {
this.model.onPreview(file.url, file.value);
}
};
onRemove = (file: any) => {
const fileList = this.state.fileList || [];
const newFileList = fileList.filter((item) => item.uid !== file.uid);
this.onSaveFileList(newFileList);
this.setState({ fileList: newFileList });
};
render() {
const { btnText, renderButton, listType } = this.props;
const { fileList } = this.state;
const classObj = {
picture: 'upload-list-picture',
text: 'upload-list-text',
};
return (
<div style={{ padding: '10px', paddingBottom: '20px' }}>
<Upload
listType="picture-card"
{...this.props}
action="https://upload-z1.qiniup.com/" //七牛上传地址
data={this.getData}
multiple
className={`${classObj[listType] || 'upload-list-picture'}`}
fileList={fileList}
previewFile={this.previewFile}
beforeUpload={this.beforeUpload}
onPreview={this.onPreview}
onChange={this.onChange}
onRemove={this.onRemove}
>
{renderButton ? (
renderButton
) : (
<Button>
<Icon type="upload" /> {btnText || '上传'}
</Button>
)}
</Upload>
<Preview ref={(dom) => (this.model = dom)} />
</div>
);
}
};
UploadCom.Preview = Preview;
UploadCom.Detail = Detail;
export default UploadCom;
import React from 'react';
import styles from './detail.less';
import { checkoutFileType, previewFile } from './utils/utils';
import { message, Modal } from 'antd';
/* eslint-disable */
export default class uploadDetail extends React.Component {
state = {
showDialog: false,
type: 'image',
url: '',
};
onPreview = (url: string, name: string) => {
const typeArr = (name || url).match(/\.[a-zA-Z]+$/);
let type = typeArr && typeArr[0] ? typeArr[0].replace('.', '') : '';
type = type.toLowerCase();
if (['png', 'gif', 'bmp', 'jpg', 'jpeg'].includes(type)) {
this.setState({ showDialog: true, type: 'image', url });
} else if (['doc', 'docx', 'document', 'xls', 'xlsx', 'ppt', 'pptx'].includes(type)) {
this.setState({ showDialog: true, type: 'office', url });
} else if (type === 'pdf') {
this.setState({ showDialog: true, type: 'pdf', url });
} else {
message.warn('暂不支持预览');
}
};
handleCancel = () => {
this.setState({ showDialog: false });
};
render() {
const { showDialog, type, url } = this.state;
return (
<div>
<Modal className={styles.dialog} visible={showDialog} onCancel={this.handleCancel} footer={null}>
{type === 'image' ? <img alt="example" style={{ width: '100%' }} src={url} /> : null}
{type === 'office' ? (
<iframe
width={'100%'}
height={500}
src={`https://view.officeapps.live.com/op/view.aspx?src=${url}`}
/>
) : null}
{type === 'pdf' ? <iframe width={'100%'} height={500} src={`${url}`} /> : null}
</Modal>
</div>
);
}
}
:global .upload-list-picture {
position: relative;
.ant-upload-list{
margin-top: 50px;
}
.ant-upload-select-picture-card{
background-color: transparent !important;
border-style: none !important;
}
.ant-upload-list-picture-card .ant-upload-list-item {
border-style: none;
display: flex;
margin:0;
:first-child{
// height: 100%;
flex: 1;
}
}
.ant-upload-list-item-info::before {
position: absolute;
top: 0;
left: 0;
z-index: 1;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.5);
opacity: 0;
-webkit-transition: all .3s;
transition: all .3s;
content: ' ';
}
.ant-upload-list-item:hover .ant-upload-list-item-info::before{
opacity: 1;
}
.ant-upload-list-item-card-actions.picture{
position: absolute;
top: 50%;
left: 50%;
z-index: 10;
opacity: 1;
transform: translate(-30%, -50%);
white-space: nowrap;
}
.ant-upload-list-item-card-actions .anticon{
color: #fff;
}
.ant-upload-list-item-uploading.ant-upload-list-item{
background-color: transparent !important;
}
.ant-upload-animate-enter {
animation-name: uploadAnimateInlineIn;
}
.ant-upload-animate-leave {
animation-name: uploadAnimateInlineOut;
}
.ant-upload-list-item-name{
position: absolute;
top:68px;
margin: 0;
padding: 0;
font-size: 12px;
display: block;
}
.ant-upload-list-picture-card .ant-upload-list-item-thumbnail{
position: inherit;
}
.ant-upload-list-picture-card .ant-upload-list-item-thumbnail img{
margin: 0 auto;
height: 60px;
width: auto;
max-width: 80px
}
.ant-upload.ant-upload-select-picture-card{
display: block;
height: auto;
position: absolute;
top: 0;
}
}
:global .upload-list-text{
}
:global .ant-upload-list-item-info{
padding: 0 20px 0 4px;
}
import { fileConfig } from '../config';
export const checkoutFileType = (url = '') => {
const typeArr = url.match(/\.[a-zA-Z]+$/);
let type = typeArr && typeArr[0] ? typeArr[0].replace('.', '') : '';
type = type.toLowerCase();
if (type === 'png' || type === 'jpg' || type === 'gif' || type === 'bmp' || type === 'jpeg') {
return { thumbUrl: url + '?imageView2/1/w/120/h/120', fileType: type }
}
return fileConfig[type] ? fileConfig[type] : fileConfig['other']
}
export const previewFile = (url) => {
const typeArr = url.match(/\.[a-zA-Z]+$/);
let type = typeArr && typeArr[0] ? typeArr[0].replace('.', '') : '';
type = type.toLowerCase();
if (type === 'png' || type === 'jpg' || type === 'gif' || type === 'bmp' || type === 'jpeg' || type === 'pdf') {
window.open(url, '_blank');
} else {
let new_url = 'https://view.officeapps.live.com/op/view.aspx?src=' + url;
window.open(new_url, '_blank');
}
}
export const handleName = (url) => {
const typeArr = url.match(/\.[a-zA-Z]+$/);
if (!typeArr || typeArr.length === 0) return [url]
const newStr = url.replace(typeArr[0], '');
return [newStr, typeArr[0]]
}
\ No newline at end of file
.container { .container {
overflow: hidden; overflow: hidden;
height: calc(100vh - 92px); height: 100%;
width: 100%; width: 100%;
.tableContainer { .tableContainer {
height: calc(100% - 45px); width: 100%;
height: 100%;
overflow: auto; overflow: auto;
position: relative; position: relative;
......
...@@ -36,7 +36,8 @@ export interface TableProps extends CommonProps { ...@@ -36,7 +36,8 @@ export interface TableProps extends CommonProps {
export interface TableState extends CommonState { export interface TableState extends CommonState {
prevProps: TableProps; prevProps: TableProps;
height: number; tableHeight: number;
tableWidth: number;
} }
export interface ColumnProps { export interface ColumnProps {
type: string; type: string;
......
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
body { body {
width: 100%; width: 100%;
height: 100%; height: 100%;
margin: 0;
padding: 0;
} }
</style> </style>
<body> <body>
......
...@@ -138,6 +138,6 @@ module.exports = { ...@@ -138,6 +138,6 @@ module.exports = {
contentBase: path.join(__dirname, 'dist'), contentBase: path.join(__dirname, 'dist'),
compress: false, compress: false,
hot: true, hot: true,
port: 8094, port: 9000,
}, },
}; };
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment