Browse Source

[update] 部分文件大小写修改

zhanghe 6 years ago
parent
commit
564d24cde1

+ 166 - 0
src/routes/Resource/Gallery/index.js

@@ -0,0 +1,166 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import queryString from 'query-string';
+import { connect } from 'dva';
+import { routerRedux } from 'dva/router';
+import { Card } from 'antd';
+import TableList from './table';
+import Search from './search';
+import ModalForm from './modal';
+import PageHeaderLayout from '../../../layouts/PageHeaderLayout';
+
+@connect(state => ({resource: state.resource}))
+export default class Gallery extends Component {
+
+  render() {
+    const { location, dispatch, resource } = this.props;
+    location.query = queryString.parse(location.search);
+    const { query, pathname } = location;
+    const { field, keyword, ...filters } = query;
+    const { list, listLoading, pagination, currentItem, itemLoading, modalVisible, modalType, signature } = resource;
+
+    // 把携带的参数中空值项删除
+    Object.keys(filters).map(key => { filters[key] ? null : delete filters[key] });
+    // 如果搜索内容不为空则添加进filters中
+    if (field && keyword) {
+      filters.field = field;
+      filters.keyword = keyword;
+    }
+
+    // 表头的搜索添加组件属性
+    const searchProps = {
+      field,
+      keyword,
+      onSearch: (payload) => {
+        if (!payload.keyword.length) {
+          delete payload.field;
+          delete payload.keyword;
+        }
+        dispatch(routerRedux.push({
+          pathname,
+          search: queryString.stringify({
+            ...payload
+          })
+        }));
+      },
+      onAdd: () => {
+        dispatch({
+          type: 'resource/showModal',
+          payload: {
+            modalType: 'create',
+          },
+        });
+      }
+    };
+
+    // 列表组件的属性
+    const listProps = {
+      pagination,
+      location,
+      dataSource: list,
+      loading: listLoading,
+      onChange: (pagination, filterArgs) => {
+        const getValue = obj => Object.keys(obj).map(key => obj[key]).join(',');
+        const tableFilters = Object.keys(filterArgs).reduce((obj, key) => {
+          const newObj = { ...obj };
+          newObj[key] = getValue(filterArgs[key]);
+          return newObj;
+        }, {});
+        const data = { ...filters, ...tableFilters };
+        Object.keys(data).map(key => data[key] ? null : delete data[key]);
+
+        dispatch(routerRedux.push({
+          pathname,
+          search: queryString.stringify({
+            ...data,
+            pageNo: pagination.current,
+            pageSize: pagination.pageSize
+          }),
+        }));
+      },
+      onEditItem: (item) => {
+        dispatch({
+          type: 'resource/showModal',
+          payload: {
+            modalType: 'update',
+            currentItem: item,
+          },
+          callback: () => {
+            dispatch(
+              routerRedux.push({
+                pathname,
+                search: queryString.stringify(filters),
+              })
+            );
+          }
+        })
+      },
+      // 删除一条数据后,刷新页面保持筛选数据不变
+      onDeleteItem: (data) => {
+        dispatch({
+          type: 'resource/delete',
+          payload: data,
+          callback: () => {
+            dispatch(
+              routerRedux.push({
+                pathname,
+                search: queryString.stringify(filters),
+              })
+            );
+          }
+        });
+      },
+    };
+
+    const modalProps = {
+      item: currentItem,
+      visible: modalVisible,
+      maskClosable: false,
+      signature,
+      modalType,
+      title: `${modalType == 'create' ? '添加图片' : '编辑图片'}`,
+      wrapClassName: 'vertical-center-modal',
+      onOk (data) {
+        dispatch({
+          type: `resource/${modalType}`,
+          payload: data,
+          callback: () => {
+            dispatch(
+              routerRedux.push({
+                pathname,
+                search: queryString.stringify(filters),
+              })
+            );
+          },
+        });
+      },
+      onCancel () {
+        dispatch({
+          type: 'resource/hideModal',
+        });
+      },
+      onUpload (data) {
+        dispatch({
+          type: 'resource/updateItem',
+          payload: data,
+        });
+      },
+      onRemove (data) {
+        dispatch({
+          type: 'resource/updateItem',
+          payload: data,
+        });
+      }
+    };
+
+    return (
+      <PageHeaderLayout>
+        <Card>
+          <Search { ...searchProps } />
+          <TableList { ...listProps } />
+          <ModalForm { ...modalProps } />
+        </Card>
+      </PageHeaderLayout>
+    );
+  }
+}

+ 158 - 0
src/routes/Resource/Gallery/modal.js

@@ -0,0 +1,158 @@
+import React, { PureComponent } from 'react';
+import PropTypes from 'prop-types';
+import { message, Form, Switch, Input, Modal } from 'antd';
+import Uploader from '../../../components/Uploader';
+import { Codes, statuses } from '../../../utils/config';
+
+@Form.create()
+export default class ModalForm extends PureComponent {
+  constructor(props) {
+    super(props);
+    this.state = {
+      filePath: '',
+      fileCode: props.item.code,
+    };
+  }
+  componentWillReceiveProps(nextProps) {
+    if (nextProps.item.code) {
+      this.setState({ fileCode: nextProps.item.code });
+    }
+  }
+  // 图片上传成功
+  handleSingleUpload = (file, ossFileName, signature) => {
+    const { onUpload, onRemove } = this.props;
+    if (file) {
+      const separatorIndex = file.name.lastIndexOf('.');
+      const name = file.name.substring(0, separatorIndex);
+      const suffix = file.name.substring(separatorIndex + 1, file.name.length);
+      const data = {
+        name,
+        format: suffix,
+        size: file.size,
+        path: `${signature.dir}${ossFileName}`,
+        url: `${signature.host}/${signature.dir}${ossFileName}`,
+      };
+      message.success('图片上传成功!');
+      onUpload(data);
+    } else {
+      // 图片被移除
+      const data = { format: null, size: null, url: null, path: null };
+      onRemove(data);
+    }
+  }
+
+  handleCodeInputChange = (e) => {
+    this.setState({
+      fileCode: e.target.value,
+    });
+  }
+
+  handleOk = () => {
+    const {
+      form: {
+        validateFields,
+        getFieldsValue,
+        resetFields,
+      },
+      item,
+      onOk,
+      modalType,
+    } = this.props;
+    validateFields((errors) => {
+      if (errors) return;
+      const data = { ...getFieldsValue() };
+      data.code = this.state.fileCode;
+      if (modalType == 'update') {
+        const { id, status, type } = item;
+        data.id = id;
+        data.type = type;
+        data.status = status;
+      } else if (modalType == 'create') {
+        data.status = Codes.CODE_NORMAL; // 创建图片,默认状态status="NORAML"
+        data.type = Codes.CODE_IMAGE;  // 创建图片,默认类型type=3
+      }
+      onOk(data);
+      resetFields();
+    });
+  }
+
+  handleCancel = () => {
+    const { onCancel, form } = this.props;
+    const { resetFields } = form;
+    onCancel();
+    resetFields();
+    this.setState({ fileCode: '' });
+  }
+
+  render() {
+    const { item, form, modalType, ...modalProps } = this.props;
+    const { fileCode } = this.state;
+    const { getFieldDecorator, getFieldsValue } = form;
+    const { name, format, size, url, path } = item;
+
+    const formItemLayout = {
+      labelCol: {
+        span: 7,
+      },
+      wrapperCol: {
+        span: 14,
+      },
+    };
+
+    const modalOpts = {
+      ...modalProps,
+      onOk: this.handleOk,
+      onCancel: this.handleCancel,
+    };
+
+    return (
+      <Modal {...modalOpts}>
+        <Form layout="horizontal">
+          <Form.Item label="图片编号:" {...formItemLayout}>
+            <Input
+              value={fileCode}
+              onChange={this.handleCodeInputChange}
+              placeholder="请输入"
+            />
+          </Form.Item>
+          <Form.Item label="图片名称:" {...formItemLayout}>
+            {getFieldDecorator('name', {
+              initialValue: name,
+            })(
+              <Input placeholder="请输入" />
+            )}
+          </Form.Item>
+          <Form.Item label="图片路径:" {...formItemLayout}>
+            {getFieldDecorator('path', {
+              initialValue: path,
+            })(
+              <Input disabled={true} />
+            )}
+          </Form.Item>
+          <Form.Item label="图片格式:" {...formItemLayout}>
+            {getFieldDecorator('format', {
+              initialValue: format,
+            })(
+              <Input disabled={true}/>
+            )}
+          </Form.Item>
+          <Form.Item label="图片大小:" {...formItemLayout}>
+            {getFieldDecorator('size', {
+              initialValue: size,
+            })(
+              <Input disabled={true} suffix={"字节"} />
+            )}
+          </Form.Item>
+          <Form.Item label="单图上传" {...formItemLayout}>
+            <Uploader
+              accept="image"
+              fileUrl={url}
+              fileCode={fileCode}
+              onUpload={this.handleSingleUpload}
+            />
+          </Form.Item>
+        </Form>
+      </Modal>
+    );
+  }
+}

+ 38 - 0
src/routes/Resource/Gallery/search.js

@@ -0,0 +1,38 @@
+import react, { PureComponent } from 'react';
+import { Button, Row, Col, Icon } from 'antd';
+import DataSearch from '../../../components/DataSearch';
+
+export default class Search extends PureComponent {
+  render() {
+    const { field, keyword, onSearch, onAdd } = this.props;
+
+    const searchGroupProps = {
+      field,
+      keyword,
+      size: 'default',
+      select: true,
+      selectOptions: [{
+        value: 'name', name: '图片名称'
+      },{
+        value: 'code', name: '图片编号'
+      }],
+      selectProps: {
+        defaultValue: field || 'code',
+      },
+      onSearch: (value) => {
+        onSearch(value);
+      },
+    };
+
+    return (
+      <Row gutter={24}>
+        <Col lg={10} md={12} sm={16} xs={24} style={{ marginBottom: 16 }}>
+          <DataSearch { ...searchGroupProps } />
+        </Col>
+        <Col lg={{ offset: 7, span: 7 }} md={12} sm={8} xs={24} style={{ marginBottom: 16, textAlign: 'right' }}>
+          <Button type="primary" onClick={onAdd}><Icon type="plus-circle" />添加图片</Button>
+        </Col>
+      </Row>
+    );
+  }
+}

+ 94 - 0
src/routes/Resource/Gallery/table.js

@@ -0,0 +1,94 @@
+import React, { PureComponent } from 'react';
+import moment from 'moment';
+import { Divider, Popover, Modal, Table, Menu, Icon, Badge } from 'antd';
+import { statuses, Codes, ossHost } from '../../../utils/config'
+
+export default class TableList extends PureComponent {
+
+  handleDeleteItem = (record) => {
+    const { onDeleteItem } = this.props;
+    Modal.confirm({
+      title: `您确定要删除该图片?`,
+      okText: '确定',
+      cancelText: '取消',
+      onOk: () => onDeleteItem({id: record.id}),
+    });
+  }
+
+  render() {
+    const { onDeleteItem, onEditItem, pagination, ...tableProps } = this.props;
+
+    const columns = [{
+      title: '缩略图',
+      dataIndex: 'url',
+      key: 'url',
+      render: (text, record) => (
+        <Popover
+          content={<img alt="" src={`${ossHost}/${record.path}`} width={250} />}
+          title={record.name}
+          placement="top"
+        >
+          <img alt="" src={`${ossHost}/${record.path}`} width={70} />
+        </Popover>
+      ),
+      width: '14%',
+    },{
+      title: '图片编号',
+      dataIndex: 'code',
+      key: 'code',
+      width: '22%',
+    },{
+      title: '图片名称',
+      dataIndex: 'name',
+      key: 'name',
+      width: '22%',
+    },{
+      title: '图片大小(B)',
+      dataIndex: 'size',
+      key: 'size',
+      width: '12%',
+    },{
+      title: '状态',
+      dataIndex: 'status',
+      key: 'status',
+      render: (text, record) => {
+        const statusMap = {[Codes.CODE_NORMAL]: 'success', [Codes.CODE_DELETE]: 'error'};
+        return (<Badge status={statusMap[record.status]} text={statuses[record.status]} />);
+      },
+      width: '10%',
+    },{
+      title: '修改时间',
+      dataIndex: 'gmtModified',
+      key: 'gmtModified',
+      render: (text, record) => (
+        <div>{moment(text).format('YYYY-MM-DD')}</div>
+      ),
+      width: '12%',
+    },{
+      title: '操作',
+      dataIndex: 'operation',
+      key: 'operation',
+      render: (text, record) => (
+        <div>
+          <a onClick={() => onEditItem(record)}>编辑</a>
+          <Divider type="vertical" />
+          <a onClick={() => this.handleDeleteItem(record)}>删除</a>
+        </div>
+      ),
+      width: '8%',
+    }];
+
+    // 配置分页
+    tableProps.pagination = !!pagination && { ...pagination, showSizeChanger: true, showQuickJumper: true, showTotal: total => `共 ${total} 条`};
+
+    return (
+      <Table
+        simple
+        bordered
+        { ...tableProps }
+        columns={columns}
+        rowKey={record => record.id}
+      />
+    );
+  }
+}

+ 141 - 0
src/routes/Resource/Video/index.js

@@ -0,0 +1,141 @@
+import React, { PureComponent } from 'react';
+import PropTypes from 'prop-types';
+import queryString from 'query-string';
+import { connect } from 'dva';
+import { routerRedux } from 'dva/router';
+import { Card } from 'antd';
+import TableList from './table';
+import Search from './search';
+import ModalForm from './modal';
+import PageHeaderLayout from '../../../layouts/PageHeaderLayout';
+
+@connect(state => ({ resource: state.resource }))
+export default class Video extends PureComponent {
+  static propTypes = {
+    resource: PropTypes.object,
+    location: PropTypes.object,
+    dispatch: PropTypes.func,
+  };
+
+  render() {
+    const { location, dispatch, resource } = this.props;
+
+    location.query = queryString.parse(location.search);
+    const { query, pathname } = location;
+    const { field, keyword, ...filters } = query;
+    const { list, listLoading, pagination, currentItem, itemLoading, modalVisible, isPaused, modalType } = resource    // 把携带的参数中空值项删除
+
+    Object.keys(filters).map(key => { filters[key] ? null : delete filters[key] });
+    // 如果搜索内容不为空则添加进filters中
+    if (field && keyword) {
+      filters[field] = keyword;
+    }
+
+    const searchProps = {
+      field,
+      keyword,
+      onSearch: (payload) => {
+        if (!payload.keyword.length) {
+          delete payload.field;
+          delete payload.keyword;
+        }
+        dispatch(routerRedux.push({
+          pathname,
+          search: queryString.stringify({
+            ...payload
+          })
+        }));
+      }
+    };
+
+    const listProps = {
+      pagination,
+      location,
+      curStatus: filters.status,
+      dataSource: list,
+      loading: listLoading,
+      onChange: (pagination, filterArgs) => {
+        const getValue = obj => Object.keys(obj).map(key => obj[key]).join(',');
+        const tableFilters = Object.keys(filterArgs).reduce((obj, key) => {
+          const newObj = { ...obj };
+          newObj[key] = getValue(filterArgs[key]);
+          return newObj;
+        }, {});
+        const data = { ...filters, ...tableFilters };
+        Object.keys(data).map(key => data[key] ? null : delete data[key]);
+        dispatch(routerRedux.push({
+          pathname,
+          search: queryString.stringify({
+            ...data,
+            pageNo: pagination.current,
+            pageSize: pagination.pageSize,
+          }),
+        }));
+      },
+      onPlayVideo: (item) => {
+        dispatch({
+          type: 'resource/showModal',
+          payload: {
+            modalType: 'update',
+            currentItem: item,
+          },
+        })
+      },
+      // 删除一条数据后,刷新页面保持筛选数据不变
+      onDeleteItem: (id) => {
+        dispatch({
+          type: 'resource/delete',
+          payload: id,
+          callback: () => {
+            dispatch(
+              routerRedux.push({
+                pathname,
+                search: queryString.stringify(filters),
+              })
+            );
+          }
+        });
+      },
+      // 恢复一条数据后,刷新页面并保持筛选数据不变
+      onRecoverItem: (payload) => {
+        dispatch({
+          type: 'resource/recover',
+          payload,
+          callback: () => {
+            dispatch(
+              routerRedux.push({
+                pathname,
+                search: queryString.stringify(filters),
+              })
+            );
+          }
+        });
+      }
+    };
+
+    const modalProps = {
+      item: currentItem,
+      visible: modalVisible,
+      footer: null,
+      maskClosable: false,
+      isPaused,
+      title: currentItem.name || '观看视频',
+      wrapClassName: 'vertical-center-modal',
+      onCancel () {
+        dispatch({
+          type: 'resource/closeVideo',
+        })
+      },
+    };
+
+    return (
+      <PageHeaderLayout>
+        <Card>
+          <Search { ...searchProps } />
+          <TableList { ...listProps } />
+          <ModalForm { ...modalProps } />
+        </Card>
+      </PageHeaderLayout>
+    );
+  }
+}

+ 20 - 0
src/routes/Resource/Video/modal.js

@@ -0,0 +1,20 @@
+import React, { PureComponent } from 'react';
+import { Modal } from 'antd';
+import VideoPlayer from '../../../components/VideoPlayer';
+
+export default class ModalForm extends PureComponent {
+  render() {
+    const { item = {}, isPaused, ...modalProps } = this.props;
+    const newModalProps = {
+      ...modalProps,
+      key: item.id,
+    }
+    const playerProps = { m3u8: item.format === 'm3u8', url: item.url, isPaused, hlsConfig: {}, width:'100%', height: '100%', controls: true };
+
+    return (
+      <Modal {...newModalProps}>
+          <VideoPlayer { ...playerProps } />
+      </Modal>
+    );
+  }
+}

+ 34 - 0
src/routes/Resource/Video/search.js

@@ -0,0 +1,34 @@
+import react, { PureComponent } from 'react';
+import { Button, Row, Col, Icon } from 'antd';
+import DataSearch from '../../../components/DataSearch';
+
+export default class Search extends PureComponent {
+  render() {
+    const { field, keyword, onSearch } = this.props;
+    const searchGroupProps = {
+      field,
+      keyword,
+      size: 'default',
+      select: true,
+      selectOptions: [{
+        value: 'name', name: '视频名称'
+      },{
+        value: 'code', name: '视频编号'
+      }],
+      selectProps: {
+        defaultValue: field || 'code',
+      },
+      onSearch: (value) => {
+        onSearch(value);
+      },
+    };
+
+    return (
+      <Row gutter={24}>
+        <Col lg={10} md={12} sm={16} xs={24} style={{ marginBottom: 16 }}>
+          <DataSearch { ...searchGroupProps } />
+        </Col>
+      </Row>
+    );
+  }
+}

+ 78 - 0
src/routes/Resource/Video/table.js

@@ -0,0 +1,78 @@
+import React, { PureComponent } from 'react';
+import moment from 'moment';
+import { Table, Badge } from 'antd';
+import { statuses, quality, Codes } from '../../../utils/config'
+
+export default class TableList extends PureComponent {
+
+  render() {
+    const { onPlayVideo, pagination, ...tableProps } = this.props;
+
+    const columns = [{
+      title: '视频编号',
+      dataIndex: 'code',
+      key: 'code',
+      width: '15%',
+    },{
+      title: '视频名称',
+      dataIndex: 'name',
+      key: 'name',
+      width: '32%',
+    },{
+      title: '视频格式',
+      dataIndex: 'format',
+      key: 'format',
+      width: '10%',
+    },{
+      title: '视频质量',
+      dataIndex: 'quality',
+      key: 'quality',
+      render: (text, record) => quality[text],
+      width: '10%',
+    },{
+      title: '状态',
+      dataIndex: 'status',
+      key: 'status',
+      render: (text, record) => {
+        const statusMap = {[Codes.CODE_NORMAL]: 'success', [Codes.CODE_DELETE]: 'error'};
+        return (<Badge status={statusMap[record.status]} text={statuses[record.status]} />);
+      },
+      width: '10%',
+    },{
+      title: '添加时间',
+      dataIndex: 'gmtCreated',
+      key: 'gmtCreated',
+      render: (text, record) => (
+        <div>{moment(text).format('YYYY-MM-DD')}</div>
+      ),
+      width: '15%',
+    },{
+      title: '操作',
+      dataIndex: 'operation',
+      key: 'operation',
+      render: (text, record) => (
+        <div>
+          <a onClick={() => onPlayVideo(record)}>播放</a>
+        </div>
+      ),
+      width: '8%',
+    }];
+
+    // 配置分页
+    tableProps.pagination = !!pagination && { ...pagination, showSizeChanger: true, showQuickJumper: true, showTotal: total => `共 ${total} 条`};
+    // 视频返回的数据量每页不固定,会出现分页bug,这里截取与pageSize相等
+    const pageSize = tableProps.pagination.pageSize;
+    const newSource = tableProps.dataSource.slice(0, pageSize);
+
+    return (
+      <Table
+        simple
+        bordered
+        { ...tableProps }
+        columns={columns}
+        dataSource={newSource}
+        rowKey={record => record.id}
+      />
+    );
+  }
+}

+ 28 - 0
src/services/cmsUser.js

@@ -0,0 +1,28 @@
+import { stringify } from 'qs';
+import request from '../utils/request';
+import { cmsUsers, cmsUser } from '../utils/api';
+
+export async function query(params) {
+  return request(`${cmsUsers}?${stringify(params)}`);
+}
+
+export async function create(params) {
+  const options = {
+    method: 'POST',
+    body: JSON.stringify(params),
+  };
+  return request(`${cmsUser}`, options);
+}
+
+export async function update(params) {
+  const options = {
+    method: 'PUT',
+    body: JSON.stringify(params),
+  };
+  return request(`${cmsUser}`, options);
+}
+
+export async function remove({ id }) {
+  const options = { method: 'DELETE' }
+  return request(`${cmsUser}/${id}`, options);
+}