Skip to content

Commit f715c6e

Browse files
committed
feat: 支持二级分类
1 parent 90e914d commit f715c6e

3 files changed

Lines changed: 105 additions & 53 deletions

File tree

modules/common.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,3 +162,25 @@ exports.Avatar.propTypes = {
162162
height: PropTypes.string,
163163
width: PropTypes.string
164164
}
165+
166+
exports.makeTree = (objs) => {
167+
const [children, parents] = _.partition(objs, o => o.get('parent'))
168+
parents.forEach(o => {
169+
o.children = []
170+
})
171+
children.forEach(c => {
172+
const p = _.find(parents, {id: c.get('parent').id})
173+
p.children.push(c)
174+
})
175+
return parents
176+
}
177+
178+
exports.depthFirstSearchMap = (array, fn) => {
179+
return _.flatten(array.map(a => {
180+
const result = fn(a)
181+
if (a.children) {
182+
return [result, ...exports.depthFirstSearchMap(a.children, fn)]
183+
}
184+
return result
185+
}))
186+
}

modules/settings/Categories.js

Lines changed: 13 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
import React from 'react'
22
import _ from 'lodash'
33
import {Link} from 'react-router'
4+
import {FormGroup} from 'react-bootstrap'
45
import AV from 'leancloud-storage/live-query'
56

6-
import common, {UserLabel} from '../common'
7+
import common, {UserLabel, makeTree, depthFirstSearchMap} from '../common'
78

89
export default class Cagegories extends React.Component {
910

1011
constructor(props) {
1112
super(props)
1213
this.state = {
1314
categories: [],
14-
newCategory: '',
1515
checkedCategories: [],
1616
customerServices: [],
1717
}
@@ -32,29 +32,10 @@ export default class Cagegories extends React.Component {
3232
checkedCategories: AV.User.current().get('categories') || [],
3333
customerServices,
3434
})
35+
return
3536
})
3637
}
3738

38-
handleNewCategoryChange(e) {
39-
this.setState({newCategory: e.target.value})
40-
}
41-
42-
handleCategorySubmit(e) {
43-
e.preventDefault()
44-
if (this.state.newCategory) {
45-
new AV.Object('Category')
46-
.save({name: this.state.newCategory})
47-
.then((category) => {
48-
const categories = this.state.categories
49-
categories.unshift(category)
50-
this.setState({
51-
categories,
52-
newCategory: ''
53-
})
54-
})
55-
}
56-
}
57-
5839
handleCategoryChange(e, categoryId) {
5940
let categories = this.state.checkedCategories
6041
if (e.target.checked) {
@@ -63,24 +44,26 @@ export default class Cagegories extends React.Component {
6344
} else {
6445
categories = _.reject(categories, {objectId: categoryId})
6546
}
66-
AV.User.current()
47+
return AV.User.current()
6748
.set('categories', categories)
6849
.save()
6950
.then(() => {
7051
this.setState({checkedCategories: categories})
52+
return
7153
})
7254
}
7355

7456
render() {
75-
const categories = this.state.categories.map((category) => {
57+
const categoriesTree = makeTree(this.state.categories)
58+
const categories = depthFirstSearchMap(categoriesTree, (category) => {
7659
const selectCustomerServices = _.filter(this.state.customerServices, (user) => {
7760
return _.find(user.get('categories'), {objectId: category.id})
7861
}).map((user) => {
7962
return <span key={user.id}><UserLabel user={user} /> </span>
8063
})
8164
return (
8265
<tr key={category.id}>
83-
<td><Link to={'/settings/categories/' + category.id}>{category.get('name')}</Link></td>
66+
<td>{category.get('parent') && ' └ '}<Link to={'/settings/categories/' + category.id}>{category.get('name')}</Link></td>
8467
<td><input type='checkbox'
8568
checked={!!_.find(this.state.checkedCategories, {objectId: category.id})}
8669
onChange={(e) => this.handleCategoryChange(e, category.id)}
@@ -91,13 +74,11 @@ export default class Cagegories extends React.Component {
9174
})
9275
return (
9376
<div>
94-
<div className="form-inline form-group">
95-
<div className='form-group'>
96-
<input type="text" className="form-control" placeholder="分类名称" value={this.state.newCategory} onChange={this.handleNewCategoryChange.bind(this)} />
97-
</div>
98-
{' '}
99-
<button type="button" className="btn btn-default" onClick={this.handleCategorySubmit.bind(this)}>新增分类</button>
100-
</div>
77+
<form>
78+
<FormGroup>
79+
<Link to={'/settings/categories/_new'}>新增分类</Link>
80+
</FormGroup>
81+
</form>
10182
<table className='table table-bordered'>
10283
<thead>
10384
<tr>

modules/settings/Category.js

Lines changed: 70 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import _ from 'lodash'
12
import React from 'react'
23
import PropTypes from 'prop-types'
34
import {FormGroup, ControlLabel, FormControl, Button} from 'react-bootstrap'
@@ -7,41 +8,74 @@ export default class Category extends React.Component {
78

89
componentDidMount() {
910
return new AV.Query('Category')
10-
.get(this.props.params.id)
11-
.then((category) => {
12-
this.setState({
13-
category
11+
.doesNotExist('parent')
12+
.find()
13+
.then(categories => {
14+
const categoryId = this.props.params.id
15+
return Promise.resolve()
16+
.then(() => {
17+
if (categoryId == '_new') {
18+
return new AV.Object('Category', {
19+
name: '',
20+
qTemplate: '',
21+
})
22+
}
23+
24+
const category = _.find(categories, {id: categoryId})
25+
if (category) {
26+
return category
27+
}
28+
29+
return new AV.Query('Category').get(categoryId)
30+
})
31+
.then(category => {
32+
this.setState({
33+
name: category.get('name'),
34+
qTemplate: category.get('qTemplate'),
35+
category,
36+
parentCategory: null,
37+
categories,
38+
})
39+
return
1440
})
1541
})
1642
}
1743

1844
handleNameChange(e) {
19-
const category = this.state.category
20-
category.set('name', e.target.value)
21-
this.setState({category})
45+
this.setState({name: e.target.value})
46+
}
47+
48+
handleParentChange(e) {
49+
const category = this.state.categories.find(c => c.id === e.target.value)
50+
this.setState({parentCategory: category})
2251
}
2352

2453
handleQTemplateChange(e) {
25-
const category = this.state.category
26-
category.set('qTemplate', e.target.value)
27-
this.setState({category})
54+
this.setState({qTemplate: e.target.value})
2855
}
2956

3057
handleSubmit(e) {
3158
e.preventDefault()
3259
const category = this.state.category
33-
category.save()
60+
return category.save({
61+
name: this.state.name,
62+
parent: this.state.parentCategory,
63+
qTemplate: this.state.qTemplate,
64+
})
3465
.then(() => {
35-
this.setState({category})
66+
this.context.router.push('/settings/categories')
67+
return
3668
})
69+
.then(this.context.addNotification)
3770
}
3871

3972
handleDelete() {
40-
const result = confirm('确认要删除分类:' + this.state.category.get('name'))
73+
const result = confirm('确认要停用分类:' + this.state.category.get('name'))
4174
if (result) {
42-
this.state.category.destroy()
75+
return this.state.category.destroy()
4376
.then(() => {
4477
this.context.router.push('/settings/categories')
78+
return
4579
})
4680
.catch(this.context.addNotification)
4781
}
@@ -52,26 +86,41 @@ export default class Category extends React.Component {
5286
return <div>数据读取中……</div>
5387
}
5488

89+
const categorieOptions = this.state.categories.map(c => {
90+
return <option key={c.id} value={c.id}>{c.get('name')}</option>
91+
})
92+
5593
return (
5694
<div>
57-
<h1>分类修改</h1>
5895
<form onSubmit={this.handleSubmit.bind(this)}>
59-
<FormGroup controlId="qTemplateTextarea">
96+
<FormGroup controlId="nameText">
6097
<ControlLabel>分类名称</ControlLabel>
61-
<FormControl type="text" value={this.state.category.get('name')} onChange={this.handleNameChange.bind(this)} />
98+
<FormControl type="text" value={this.state.name} onChange={this.handleNameChange.bind(this)} />
99+
</FormGroup>
100+
<FormGroup controlId="parentSelect">
101+
<ControlLabel>父分类(可选)</ControlLabel>
102+
<FormControl componentClass='select'
103+
value={this.state.category.get('parent') && this.state.category.get('parent').id}
104+
onChange={this.handleParentChange.bind(this)}>
105+
<option value=''></option>
106+
{categorieOptions}
107+
</FormControl>
62108
</FormGroup>
63-
<FormGroup>
109+
<FormGroup controlId="qTemplateTextarea">
64110
<ControlLabel>问题描述模板</ControlLabel>
65111
<FormControl
66112
componentClass="textarea"
67113
placeholder="用户新建该分类工单时,问题描述默认显示这里的内容。"
68114
rows='8'
69-
value={this.state.category.get('qTemplate')}
115+
value={this.state.qTemplate}
70116
onChange={this.handleQTemplateChange.bind(this)}/>
71117
</FormGroup>
72-
<Button type='submit'>保存</Button>
118+
<Button type='submit' bsStyle='success'>保存</Button>
73119
{' '}
74-
<Button type='button' bsStyle="danger" onClick={this.handleDelete.bind(this)}>删除</Button>
120+
{this.state.category.id
121+
&& <Button type='button' bsStyle="danger" onClick={this.handleDelete.bind(this)}>删除</Button>
122+
|| <Button type='button' onClick={() => this.context.router.push('/settings/categories')}>取消</Button>
123+
}
75124
</form>
76125
</div>
77126
)

0 commit comments

Comments
 (0)