前言
隨著管理的文章數量增多,預設的幾個分類滿足不了現狀了...
趁著重構的過程把相關的功能考慮進去
本來想自己從頭寫過一個,看了下Antd
有內建該型別的控制元件了,就沒必要自己造了
一般自己寫,肯定優先考慮陣列物件格式[{tagName:'a',value:1}]
;
Antd
提供的是純陣列,[string,string]
,那如何不改變它提供的格式情況下拿到我們想要的!
擴充部分我們需要的東東,有興趣的瞧瞧,沒興趣的止步..
效果圖
需求分析及思路
需求梳理
- 從介面拿到
tags
陣列且構建列舉物件,tags
支援刪除新增 , - 高亮
tag
,追加刪除的情況要考慮進去; - 第一個為預設分類,不允許刪除
- 高亮顏色支援傳入
- 標籤文字過長,則截斷,用氣泡懸浮來展示完全的文字
- 不允許新增同樣的(阻止並給予反饋)
- 預設值初始化並且回饋,把值丟給父
實現
- 用
dva
的effect
維護介面資料的獲取 - 子元件除了暴露返回值,不做任何涉及
Dva
這類不純的東西,一切靠props
丟進去
程式碼實現
在引用處的父元件構建資料獲取,主要構建兩個,一個待渲染的陣列,一個是列舉(其實就是key-value
對映);
因為要考慮和以前的版本相容,所有一些固定的key-value
,還有預設值也要考慮進去(請求失敗的時候)
DocumentType.js
/*
* @Author: CRPER
* @LastEditors: CRPER
* @Github: https://github.com/crper
* @Motto: 折騰是一種樂趣,求知是一種追求。不懂就學,懂則分享。
* @Description: 文件型別維護
*/
import React, { PureComponent } from 'react';
import { Tag, Input, Tooltip, Icon, message } from 'antd';
// 物件深比較
import isEqual from 'lodash/isEqual';
export default class DocumentType extends PureComponent {
static getDerivedStateFromProps(nextProps, prevState) {
if (isEqual(nextProps.data, prevState.prevData)) {
return null;
}
if (nextProps.data) {
return {
defaultValue: nextProps.defaultValue ? nextProps.defaultValue : null,
tags: nextProps.data,
prevData: nextProps.data,
};
} else {
return null;
}
}
state = {
tags: [], // 標籤列表
hightlightIndeX: 0, // 若是外部沒有
inputVisible: false, // 輸入框預設隱藏
inputValue: '', // 輸入框預設值
};
//獲取預設值
initDefaultValue = () => {
const { defaultValue, hightlightIndeX, tags } = this.state;
// 若是有,則取遍歷取得;若是外部沒有傳入預設值則取陣列第一位
if (defaultValue) {
let index = tags.indexOf(defaultValue);
// 若是傳入的預設值不存在,則預設取下標為0的
index = index === -1 ? 0 : index;
this.setState({ hightlightIndeX: index }, () => {
this.props.onChange(this.getTagValueFromIndex(index));
});
} else {
this.props.onChange(this.getTagValueFromIndex(hightlightIndeX));
}
};
componentDidMount = () => {
this.initDefaultValue();
};
// 記錄控制元件的ref
input = React.createRef();
// 顯示input後,直接聚焦
showInput = () => {
this.setState({ inputVisible: true }, () => this.input.current.focus());
};
// 儲存input輸入的值
handleInputChange = e => {
this.setState({ inputValue: e.target.value });
};
// 新增判定
handleInputConfirm = () => {
const { inputValue, tags: prevTags, defaultValue } = this.state;
// 若是輸入的值已經存在或空值,則不新增
if (inputValue === defaultValue) {
message.error('已存在同樣的型別!!!');
this.setState({ inputValue: '' });
this.input.focus();
return false;
}
if (!inputValue) {
this.setState({ inputVisible: false, inputValue: '' });
return false;
}
let tags = prevTags;
if (inputValue && tags.indexOf(inputValue) === -1) {
tags = [...tags, inputValue];
}
this.setState({
tags,
inputVisible: false,
inputValue: '',
});
// 傳遞給父的新增標籤回撥
if (this.props.addTag) {
this.props.addTag(inputValue);
}
};
// 取得對應index下的tag的值
getTagValueFromIndex = index => {
const { tags } = this.state;
return tags[index];
};
// 高亮TAG
hightlightTag = index => {
this.setState({ hightlightIndeX: index });
if (this.props.onChange) {
this.props.onChange(this.getTagValueFromIndex(index));
}
};
// 刪除tag
handleClose = removeTag => {
const { hightlightIndeX, tags } = this.state;
if (this.props.removeTag) {
this.props.removeTag(removeTag);
}
// 若是刪除的位置和高亮的位置同一個,則高亮往前一位
if (tags.indexOf(removeTag) === tags.length - 1) {
this.hightlightTag(hightlightIndeX - 1);
}
};
render() {
const { tags, inputVisible, inputValue, hightlightIndeX } = this.state;
const { plusBtnText, activeColor } = this.props;
return (
<div>
{tags.map((tag, index) => {
const isLongTag = tag.length > 10;
const tagElem = (
<Tag
key={tag}
color={hightlightIndeX === index ? (activeColor ? activeColor : '#40a9ff') : ''}
closable={index !== 0}
onClick={() => this.hightlightTag(index)}
afterClose={() => this.handleClose(tag)}
>
{isLongTag ? `${tag.slice(0, 10)}...` : tag}
</Tag>
);
return isLongTag ? (
<Tooltip title={tag} key={tag}>
{tagElem}
</Tooltip>
) : (
tagElem
);
})}
{inputVisible && (
<Input
ref={this.input}
type="text"
size="small"
style={{ width: 78 }}
value={inputValue}
onChange={this.handleInputChange}
onBlur={this.handleInputConfirm}
onPressEnter={this.handleInputConfirm}
/>
)}
{!inputVisible && (
<Tag onClick={this.showInput} style={{ background: '#fff', borderStyle: 'dashed' }}>
<Icon type="plus" /> {plusBtnText ? plusBtnText : 'New Tag'}
</Tag>
)}
</div>
);
}
}
複製程式碼
用法
寫成受控元件,無資料不渲染
props |
解釋 | 格式型別 | 是否可選 |
---|---|---|---|
data |
待遍歷的陣列 | 陣列 | 必選 |
onChange |
選中的回撥 | 函式 | 必選 |
addTag |
新增標籤的回撥 | 函式 | 必選 |
remvoeTag |
移除標籤的回撥 | 函式 | 必選 |
defaultValue |
預設值 | 字串 | 可選 |
plusBtnText |
追加按鈕文字替換 | 字串 | 可選 |
activeColor |
高亮的顏色 | 字串 | 可選 |
{typeNames && typeNames.length > 0 ? (
<Row type="flex" justify="start" align="middle">
<span style={{ fontSize: 16, fontWeight: 700 }}>文章型別</span>
<Divider type="vertical" />
<DocumentType
data={typeNames}
onChange={this.getTagValue}
addTag={this.addTag}
removeTag={this.removeTag}
defaultValue="草稿"
activeColor="#108ee9"
plusBtnText="新的分類"
/>
</Row>
) : null}
複製程式碼
總結
不對之處請留言,會及時修正.謝謝閱讀.