开发赞助管理页面

This commit is contained in:
qyzh 2020-09-10 10:12:49 +08:00
parent 016c0bee00
commit 381ad7135b
8 changed files with 725 additions and 50 deletions

View File

@ -30,6 +30,7 @@ import { SnackbarHOC } from 'educoder'
import { initAxiosInterceptors } from './AppConfig'
import { Provider } from 'react-redux';
import configureStore from './redux/stores/configureStore';
import { load } from 'react-cookies';
const store = configureStore();
window.marked = marked;
const theme = createMuiTheme({
@ -81,6 +82,11 @@ const Sponsor = Loadable({
loader: () => import('./forge/sponsor/Sponsor'),
loading: Loading,
})
// Sponsor Confirmation
const SponsorConfirmation = Loadable({
loader: () => import('./forge/sponsor/SponsorConfirmation'),
loading: Loading,
})
class App extends Component {
constructor(props) {
@ -266,6 +272,11 @@ class App extends Component {
return (<InfosIndex {...this.props} {...this.state} />)
}
}></Route>
<Route exact path="/sponsor_confirmation/:username/:tier_id"
render={
(props) => (<SponsorConfirmation {...this.props}{...this.state}></SponsorConfirmation>)
}>
</Route>
<Route exact path="/sponsor/:username"
render={
(props) => (<Sponsor {...this.props}{...this.state}></Sponsor>)

View File

@ -1,6 +1,6 @@
import React, { Component } from "react";
import { Link } from "react-router-dom";
import { Avatar, Tag, Button, Spin } from "antd";
import { Avatar, Tag, Button, Spin, Input, Form} from "antd";
import { Route, Switch } from "react-router-dom";
import { withRouter } from "react-router";
@ -58,6 +58,36 @@ const tempSponsors = [
{image_url: "avatars/User/b", username: "万山川", user_id: 1369, login:"alpc104"},
];
const FormItem = Form.Item;
const TextArea = Input.TextArea;
const DescriptionForm = Form.create()(
class extends Component{
getItemsValue = ()=>{
const val= this.props.form.getFieldsValue(); // 获取from表单的值
return val;
}
render(){
const { form, initValue} = this.props;
const { getFieldDecorator } = form; // 校验控件
return(
<Form >
<FormItem>
{getFieldDecorator(`description`,{
rules: [],
initialValue: initValue ? initValue : undefined
})(
// <Input placeholder="请输入"/>
<TextArea rows={4} placeholder="请输入"></TextArea>
)}
</FormItem>
</Form>
)
}
}
);
class Sponsor extends Component {
constructor(props) {
@ -67,6 +97,7 @@ class Sponsor extends Component {
user: undefined,
description: undefined,
sponsors: undefined,
edit: false,
};
}
@ -107,6 +138,71 @@ class Sponsor extends Component {
});
}
editDescription = () => {
this.setState({
edit: true,
});
}
cancelEdit = () => {
this.setState({
edit: false,
})
}
confirmEdit = () => {
//TODO: 增加和后端交互
let value = this.formRef.getItemsValue().description;
// console.log(value);
let user = this.state.user;
if (user){
user.description = value;
}
this.setState({
user: user,
edit: false,
});
}
visitorTemplate = () => {
const user = this.state.user;
return (
<div>
<span>自我介绍:</span>
{user && user.description?
(<p>{user.description}</p>):(<p></p>)}
</div>
);
}
manageTemplate = () => {
const user = this.state.user;
return (
<div>
<span>
自我介绍:
<i className="iconfont icon-bianji3 font-15 mr5" onClick={() => this.editDescription()}></i>
</span>
{user && user.description? (<p>{user.description}</p>):(<p></p>)}
</div>
);
}
editTemplate = () => {
const user = this.state.user;
return (
<div>
<span>自我介绍:</span>
<DescriptionForm
initValue={user && user.description}
wrappedComponentRef={(form) => this.formRef = form}
/>
<Button onClick={() => this.confirmEdit()}>确认</Button>
<Button onClick={() => this.cancelEdit()}>取消</Button>
</div>
);
}
sponsorList(){
const {sponsors} = this.state;
const sponsorList = (sponsors && sponsors.length>0 ? sponsors.map((item, key)=>{
@ -121,12 +217,22 @@ class Sponsor extends Component {
render(){
const { current_user} = this.props;
const isSpin = false;
const { user } = this.state;
const { user, isSpin } = this.state;
const sponsorList = this.sponsorList();
// console.log("current", current_user)
// console.log("user", user)
var content = "";
if (current_user && user && user.login !== current_user.login){
content = this.visitorTemplate();
}
else if(current_user && user && user.login === current_user.login && this.state.edit){
content = this.editTemplate();
}
else if(current_user && user && user.login === current_user.login && !this.state.edit){
content = this.manageTemplate();
}
return (
<div className="newMain clearfix">
<Spin spinning={isSpin}>
@ -149,12 +255,7 @@ class Sponsor extends Component {
<div className="bgcF">
<div className="list-l-Menu pd20 ">
<span>自我介绍:
{user && current_user && user.login===current_user.login?
(<i className="iconfont icon-bianji3 font-15 mr5"></i>):""}
</span>
{user && user.description?
(<p>{user.description}</p>):(<p></p>)}
{content}
</div>
<div className="list-l-Menu pd20 ">
赞助者:

View File

@ -0,0 +1,221 @@
import React, { Component } from "react";
import { Link } from "react-router-dom";
import { Avatar, Tag, Button, Spin, Input, Divider, Form, Radio, Alert, message} from "antd";
import { Route, Switch } from "react-router-dom";
import { withRouter } from "react-router";
import { SnackbarHOC } from "educoder";
import { CNotificationHOC } from "../../modules/courses/common/CNotificationHOC";
import { TPMIndexHOC } from "../../modules/tpm/TPMIndexHOC";
import axios from "axios";
import { getImageUrl } from "educoder";
import Loadable from "react-loadable";
import Loading from "../../Loading";
import { ImageLayerOfCommentHOC } from "../../modules/page/layers/ImageLayerOfCommentHOC";
import "../users/new_user.css";
import "../css/index.scss";
import '../users/Index.scss';
import "./sponsor.css";
import { submitUserCode } from "../../redux/actions/ojForUser";
const tempData = {amount:100, description: "如果您更加慷慨的话就选择这个"};
const tempBalance = 999;
const FormItem = Form.Item;
const SponsorConfirmation = Form.create()(
class extends Component{
constructor(props) {
super(props);
this.state = {
isSpin: false,
user: undefined,
tier: undefined,
single: true,
balance: undefined,
};
}
componentDidMount = () => {
this.fetchUser();
this.fetchTierInformation();
this.fetchBalance();
};
fetchUser = () => {
this.setState({
isSpin: true,
});
const { current_user } = this.props;
const { username } = this.props.match.params;
let url = `/users/${username || (current_user && current_user.login)}.json`;
axios
.get(url)
.then((result) => {
this.setState({
user: result.data,
isSpin: false,
});
})
.catch((error) => {
console.log(error);
this.setState({
isSpin: false,
});
});
};
fetchTierInformation = () => {
//TODO: 和后端交互
this.setState({
tier: tempData
})
}
fetchBalance = () => {
this.setState({
balance: tempBalance
})
}
reSelect = () => {
const {user} = this.state;
this.setState({
route_type: undefined
})
this.props.history.push(`/sponsor/${user && user.login}`)
}
changeSingle = (e) => {
this.setState({
single: e.target.value
})
}
submit = () => {
const value = this.props.form.getFieldsValue();
console.log(value);
message.success("赞助成功!");
}
render(){
const {tier_id, username} = this.props.match.params;
const {current_user, form} = this.props;
const {user, isSpin, tier, balance} = this.state;
const {getFieldDecorator} = form;
const current_username = current_user && current_user.username;
const alert_text = this.state.single? `您选择了单次付费,本次扣除${tier && tier.amount}硬币`
:`您选择了按月付费每月20日将扣除${tier && tier.amount}硬币`;
// console.log("user", user)
// console.log("current user", current_user);
const radioStyle = {
display: 'block',
height: '30px',
lineHeight: '30px',
};
return (
<div className="newMain clearfix">
<Spin spinning={isSpin}>
<div className="new-content-flex">
<div className="sponsor-confirmation-left">
<div className="list-l-Menu pd20 ">
<p>
<Avatar
size={50}
src={getImageUrl(`images/${user && user.image_url}`)}
/>
<a href={`/sponsor/${user && user.login}`}>赞助{user && user.username}</a>/
</p>
<p>
赞助者为
<Avatar
size={50}
src={getImageUrl(`images/${current_user && current_user.image_url}`)}
/>
{current_user && current_user.username}
</p>
<Divider></Divider>
<p>所选赞助等级</p>
<p>{tier && tier.amount}硬币</p>
<p>{tier && tier.description}</p>
<Button onClick={() => this.reSelect()}>重新选择</Button>
</div>
</div>
<div className="sponsor-confirmation-right">
<div className="list-l-Menu pd20 ">
<Form>
<FormItem >
{getFieldDecorator(`single`,{
rules: [{
required: true
}],
initialValue: true
})(
<Radio.Group onChange={(e) => this.changeSingle(e)}>
<Radio style={radioStyle} value={true}>单次付费</Radio>
<Radio style={radioStyle} value={false}>按月支付</Radio>
</Radio.Group>
)}
</FormItem>
<p>本次支付金额{tier && tier.amount}硬币</p>
<p>{this.state.single? "单次付费": "即日起-次月20日"}</p>
<Divider></Divider>
<div>支付信息</div>
<div>
<div>
<Avatar
size={50}
src={getImageUrl(`images/${current_user && current_user.image_url}`)}
/>
</div>
<div>
<div>{current_user && current_user.username}</div>
<div>硬币余额{balance}</div>
</div>
</div>
<Alert message={alert_text} type="warning"/>
<Divider></Divider>
<span>赞助关系可见性</span>
<FormItem >
{getFieldDecorator(`private`,{
rules: [{
required: true
}],
initialValue: false
})(
<Radio.Group >
<Radio style={radioStyle} value={false}>所有人可见</Radio>
<Radio style={radioStyle} value={true}>私密</Radio>
</Radio.Group>
)}
</FormItem>
<Button onClick={() => this.submit()}>赞助{user && user.username}</Button>
</Form>
</div>
</div>
</div>
</Spin>
</div>
);
}
});
export default withRouter(
ImageLayerOfCommentHOC({
imgSelector: ".imageLayerParent img, .imageLayerParent .imageTarget",
parentSelector: ".newMain",
})(CNotificationHOC()(SnackbarHOC()(TPMIndexHOC(SponsorConfirmation))))
);

View File

@ -17,4 +17,22 @@
height: 50px;
border-radius: 50%;
margin-right: 11px;
}
.sponsor-confirmation-left{
width: 35%;
padding-right: 20px;
box-sizing: border-box;
margin-bottom: 20px;
}
.sponsor-confirmation-right{
width: 65%;
padding-right: 20px;
box-sizing: border-box;
margin-bottom: 20px;
}
.sponsor-confirmation-inline{
display: inline-block;
}

View File

@ -1,6 +1,6 @@
import React, { Component } from "react";
import { Link } from "react-router-dom";
import { Avatar, Tag, Button, Spin, Icon, Modal, Input, Form} from "antd";
import { Avatar, Tag, Button, Spin, Icon, Modal, Input, Form, message, Popconfirm, Divider} from "antd";
import { Route, Switch } from "react-router-dom";
import { withRouter } from "react-router";
// import { EditOutlined } from "@ant-design/icons";
@ -40,6 +40,7 @@ class extends Component{
{getFieldDecorator(`amount`,{
rules: [{
message: '必填字段!',
pattern: /^[0-9]+$/,
required: true
}],
initialValue: initValue1 ? initValue1 : undefined
@ -48,7 +49,7 @@ class extends Component{
)}
</FormItem>
<FormItem label={`描述`} >
{getFieldDecorator(`name`,{
{getFieldDecorator(`description`,{
rules: [{
message: '必填字段!',
required: true
@ -73,16 +74,41 @@ class SponsorTierItem extends Component{
this.state = {
tiers: props.tiers,
new_tier: false,
visible: false,
new_tier: undefined,
edit: edit,
isSpin: false,
}
}
componentDidMount = () => {
// console.log(this.props.tiers)
this.fetchUser();
}
fetchUser = () => {
this.setState({
isSpin: true,
});
const { current_user } = this.props;
const { username } = this.props.match.params;
let url = `/users/${username || (current_user && current_user.login)}.json`;
axios
.get(url)
.then((result) => {
this.setState({
user: result.data,
isSpin: false,
});
})
.catch((error) => {
console.log(error);
this.setState({
isSpin: false,
});
});
};
editTier = (item, key, e) => {
var edit = this.state.edit;
edit[key] = true;
@ -92,56 +118,70 @@ class SponsorTierItem extends Component{
}
confirmEdit = (item, key, e) => {
if (this.state.new_tier == key){
console.log("new tier");
}
console.log("key", key, "item", item)
let values = this.formRef.getItemsValue();
console.log("values:", values)
// TODO: 在此处加入对后端的修改请求
var edit = this.state.edit;
var {tiers, edit} = this.state;
tiers[key] = values;
edit[key] = false;
this.setState({
edit:edit
tiers: tiers,
edit: edit,
new_tier: undefined
})
}
canelEdit = (item, key, e) => {
var edit = this.state.edit;
edit[key] = false;
this.setState({
edit:edit
})
}
showModal = () => {
cancelEdit = (item, key, e) => {
let {tiers, edit} = this.state;
// this.setState({
// visible: true
// })
}
hideModal = () => {
tiers.pop();
edit.pop();
this.setState({
visible: false
tiers: tiers,
edit: edit,
new_tier: undefined
})
}
createTier = () => {
newTier = () => {
if (this.state.new_tier !== undefined){
message.info("请先完成当前编辑")
return;
}
let {tiers, edit} = this.state;
tiers.push({amount: 1, description: ""})
edit.push(true);
this.setState({
visible: false
tiers: tiers,
edit: edit,
new_tier: tiers.length - 1,
})
}
newTierModal = () => {
return (
<Modal
title="新增赞助等级"
visible={this.state.visible}
onCancel={this.hideModal}
onOk={this.createTier}>
<p>12312312</p>
</Modal>
);
deleteTier = (item, key, e) => {
// TODO: 此处向后端发送请求删除
let {tiers, edit} = this.state;
tiers.splice(key, 1)
edit.splice(key, 1)
this.setState({
tiers: tiers,
edit: edit,
})
}
toConformation = (item, key, e) => {
const {user} = this.state
this.setState({
route_type: undefined
})
this.props.history.push(`/sponsor_confirmation/${user && user.login}/${item.id}`)
}
visitorTemplate = (item, key) => {
@ -150,7 +190,7 @@ class SponsorTierItem extends Component{
<div className="font-15 mr5">
金额{item.amount}
<Button >选择</Button>
<Button onClick={(e) => this.toConformation(item, key, e)}>选择</Button>
</div>
<div className="font-15 mr5">
描述{item.description}
@ -168,7 +208,15 @@ class SponsorTierItem extends Component{
</div>
<div className="font-15 mr5">
描述{item.description}
<i className="iconfont icon-shanchu font-15"></i>
<Popconfirm
title="确认删除?"
onConfirm={e => this.deleteTier(item, key, e)}
// onCancel={cancel}
okText="是"
cancelText="否"
>
<i className="iconfont icon-shanchu font-15"></i>
</Popconfirm>,
</div>
</ul>
);
@ -203,7 +251,8 @@ class SponsorTierItem extends Component{
render() {
const {tiers} = this.state;
const { current_user, user} = this.props;
const { current_user} = this.props;
const {user} = this.state;
const renderList = (
tiers && tiers.length > 0 ? tiers.map((item, key) => {
@ -247,10 +296,9 @@ class SponsorTierItem extends Component{
<div>
<div>{renderList}</div>
{current_user && user && user.login === current_user.login ?
( <Button type="primary" onClick={() => this.showModal()}>新增赞助等级</Button>):
( <Button type="primary" onClick={() => this.newTier()}>新增赞助等级</Button>):
""
}
{this.newTierModal()}
</div>
)
}

View File

@ -40,6 +40,10 @@ const Wallet = Loadable({
loader: () => import("./sponsor/wallet"),
loading: Loading,
});
const SponsorManagement = Loadable({
loader: () => import("./sponsor/SponsorManagement"),
loading: Loading,
})
class Infos extends Component {
constructor(props) {
super(props);
@ -125,6 +129,14 @@ class Infos extends Component {
this.props.history.push(`/sponsor/${user && user.login}`)
}
sponsor_management_link = () => {
const {user} = this.state
this.setState({
route_type: undefined
})
this.props.history.push(`/users/${user && user.login}/sponsor_management`)
}
wallet_link = () => {
const {user} = this.state
this.setState({
@ -185,7 +197,13 @@ class Infos extends Component {
<i className="iconfont icon-shezhi4 font-15 mr5"></i>
修改资料
</Button>
<Button
block
className="text-button-grey"
href={`/sponsor/${user && user.login}`}
>
赞助信息修改
</Button>
</div>
@ -307,7 +325,7 @@ class Infos extends Component {
</span>
</p>
</li>
<li className="" onClick={() => this.sponsor_link()}>
<li className="" onClick={() => this.sponsor_management_link()}>
<p>
<span className="font-16">赞助管理</span>
<span className="color-blue">
@ -341,6 +359,12 @@ class Infos extends Component {
return <Wallet {...this.props} {...this.state} />;
}}
></Route>
<Route
path="/users/:username/sponsor_management"
render={(props) => {
return <SponsorManagement {...this.props} {...this.state} />;
}}
></Route>
<Route
path="/users/:username/fan_users"
render={() => {

View File

@ -0,0 +1,190 @@
import React, { Component } from "react";
import { Link } from "react-router-dom";
import { Menu, Input, Spin, Pagination, Popover, Button, Divider } from "antd";
import axios from "axios";
import CoinChangeItem from "./CoinChangeItem";
import Nodata from "../../Nodata";
import img_array from "../../Images/array.png";
import SponsorList from "./sponsor_list";
const Search = Input.Search;
const tempData = [
{image_url: "avatars/User/b", username: "万山川", user_id: 1369, login:"alpc104", format_time: "2020-08-01"},
{image_url: "avatars/User/b", username: "万山川", user_id: 1369, login:"alpc104", format_time: "2020-08-01"},
{image_url: "avatars/User/b", username: "万山川", user_id: 1369, login:"alpc104", format_time: "2020-08-01"},
{image_url: "avatars/User/b", username: "万山川", user_id: 1369, login:"alpc104", format_time: "2020-08-01"},
{image_url: "avatars/User/b", username: "万山川", user_id: 1369, login:"alpc104", format_time: "2020-08-01"},
{image_url: "avatars/User/b", username: "万山川", user_id: 1369, login:"alpc104", format_time: "2020-08-01"},
];
class SponsorManagement extends Component{
constructor(props) {
super(props);
this.state = {
page: 1,
limit: 15,
sort_by: undefined,
totalCount: undefined,
isSpin: false,
coinChangeList: undefined,
total: undefined,
category: undefined,
is_public: undefined,
sponsors: undefined,
};
}
changeCategory = (cate) => {
// let resultList = this.state.coinChangeList;
// switch (cate.target.value){
// case "income":
// resultList = resultList.filter(function(item,index,array){
// return item.amount > 0;
// });
// break;
// case "outcome":
// resultList = resultList.filter(function(item,index,array){
// return item.amount < 0;
// });
// break;
// }
// this.setState({
// displayList: resultList,
// });
};
componentDidMount = () => {
this.get_sponsors();
}
get_sponsors = ()=>{
this.setState({
sponsors: tempData,
})
}
//切换页数
changePage = (page) => {
this.state.page = page;
// this.get_coin_changes();
};
// 排序
ChangeSoryBy = (e) => {
// this.state.sort_by = e.key;
// this.get_coin_changes();
console.log(e.key)
};
changeSearchValue = (e) => {
this.setState({
search: e.target.value,
});
};
menu =()=> (
<Menu onClick={this.ChangeSoryBy}>
<Menu.Item key="time_order">时间从近到远排序</Menu.Item>
<Menu.Item key="reverse_time_order">时间从远到近排序</Menu.Item>
</Menu>
);
category_button=(category)=>{
const { current_user, user } = this.props;
const button_lists =
user && current_user && user.login === current_user.login
? [
{ type: "sponsoring", name: "赞助中" },
{ type: "stopped", name: "已终止" },
]
:
[];
let list = button_lists.map((item, key) => {
return (
<span key={key} className="pr15">
<Button
type={
(category && category === item.type) || (!category && !item.type)
? "primary"
: "default"
}
ghost={
(category && category === item.type) || (!category && !item.type)
}
value={item.type}
onClick={this.changeCategory}
>
{item.name}
</Button>
</span>
);
})
return list;
}
render() {
const { current_user, user } = this.props;
const { category , is_public, sponsors } = this.state;
const { coinChangeList, isSpin, total, search, limit, page } = this.state;
return (
<Spin spinning={isSpin}>
<div className="list-r-operation">
<Search
placeholder="输入关键字进行搜索"
enterButton="搜索"
size="large"
onSearch={this.get_coin_changes}
className="list-r-Search"
value={search}
onChange={this.changeSearchValue}
/>
<div>
<Popover content={this.menu()} trigger={["click"]} placement="bottom">
<a className="ant-dropdown-link">
<span className="color-blue font-16">
排序 <img src={img_array} alt="" width="10px" />
</span>
</a>
</Popover>
</div>
</div>
<div className="infosType">
<div>{this.category_button(category)}</div>
</div>
{sponsors && sponsors.length > 0 ?
<SponsorList
userClass={"w-33"}
current_user={current_user}
users={sponsors}
></SponsorList>
:
<Nodata _html={`暂时没有项目`} />
}
{
total && total > limit ?
<div className="edu-txt-center pt30 mb30 border-top-grey">
<Pagination
simple
defaultCurrent={page}
total={total}
pageSize={limit}
onChange={this.changePage}
></Pagination>
</div>
:
""
}
</Spin>
);
}
};
export default SponsorManagement;

View File

@ -0,0 +1,62 @@
import React, { Component } from "react";
import { getImageUrl } from "educoder";
import FocusButton from "../../UsersList/focus_button";
import { Button } from "antd";
import "../../UsersList/list.css";
class SponsorList extends Component {
renderList = (users, userClass, current_user) => {
if (users && users.length > 0) {
return users.map((item, key) => {
return (
<div className={`pull-left ${userClass}`} key={key}>
<div className="pbt25 grid-item mlr10 border-b-line">
<div>
<a
href={`/users/${item.login}`}
className="show-user-link"
>
<img
className="avatar-60"
src={getImageUrl(`images/${item.image_url}`)}
alt=""
/>
</a>
</div>
<div className="ml12">
<div>
<a
href={`/users/${item.login}`}
className="font-16 text-primary hide-1 task-hide max-w-200"
>
{item.username}
</a>
</div>
<div className="font-12 text-gray grid-item pb5">
<i className="iconfont icon-shijian user-join-time"></i>
<span className="ml4">加入时间{item.format_time}</span>
</div>
{
current_user && current_user.login === item.login ?
<Button type="default">当前用户</Button>
:
<FocusButton is_watch={item.is_watch} id={item.login} />
}
</div>
</div>
</div>
);
});
}
}
render() {
const { users, userClass, current_user } = this.props;
console.log(users);
return (
this.renderList(users, userClass, current_user)
);
}
}
export default SponsorList;