Commit 41cdb462 authored by 武广's avatar 武广

feat: 添加列表排序

parent 44956568
This diff is collapsed.
...@@ -56,6 +56,7 @@ ...@@ -56,6 +56,7 @@
"@antv/data-set": "^0.10.2", "@antv/data-set": "^0.10.2",
"antd": "^4.19.3", "antd": "^4.19.3",
"antd-virtual-select": "^1.1.2", "antd-virtual-select": "^1.1.2",
"array-move": "^4.0.0",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.1.0",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"date-fns": "^2.16.1", "date-fns": "^2.16.1",
...@@ -74,6 +75,7 @@ ...@@ -74,6 +75,7 @@
"react-dom": "^16.8.6", "react-dom": "^16.8.6",
"react-helmet": "^5.2.1", "react-helmet": "^5.2.1",
"react-router-dom": "^5.1.2", "react-router-dom": "^5.1.2",
"react-sortable-hoc": "^2.0.0",
"react-sortablejs": "^6.0.0", "react-sortablejs": "^6.0.0",
"slash2": "^2.0.0", "slash2": "^2.0.0",
"sortablejs": "^1.13.0", "sortablejs": "^1.13.0",
...@@ -129,4 +131,4 @@ ...@@ -129,4 +131,4 @@
"scripts/**/*.js" "scripts/**/*.js"
], ],
"author": "congmin.hao" "author": "congmin.hao"
} }
\ No newline at end of file
...@@ -7,7 +7,7 @@ import styles from '../../style.less'; ...@@ -7,7 +7,7 @@ import styles from '../../style.less';
const ItemTypes = { const ItemTypes = {
CARD: 'card', CARD: 'card',
}; };
const DragTag = ({ text, id, index, changePosition, selected, groupEdit, changeGroup }) => { const DragTag = ({ text, id, index, changePosition }) => {
const [isEdit, setIsEdit] = useState(false); const [isEdit, setIsEdit] = useState(false);
const [inputValue, setInputValue] = useState(''); const [inputValue, setInputValue] = useState('');
const refInput = useRef(); const refInput = useRef();
...@@ -82,7 +82,7 @@ const DragTag = ({ text, id, index, changePosition, selected, groupEdit, changeG ...@@ -82,7 +82,7 @@ const DragTag = ({ text, id, index, changePosition, selected, groupEdit, changeG
}} }}
> >
<HolderOutlined className={styles['groupBox-body--tag__move']} /> <HolderOutlined className={styles['groupBox-body--tag__move']} />
<span>{text}</span> <span className={styles['groupBox-body--tag__text']}>{text}</span>
<span> <span>
<FormOutlined className={styles['groupBox-body--tag__edit']} onClick={handleEdit} /> <FormOutlined className={styles['groupBox-body--tag__edit']} onClick={handleEdit} />
</span> </span>
......
import React, { useState } from 'react';
import { Button, Modal, message, notification, Tag } from 'antd';
import { DndProvider } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
import styles from '../../style.less';
import DragTag from './DragTag';
import InsertTag from './InsertTag';
const GoodsGroup = options => {
const [groupEdit, setGroupEdit] = useState(false);
const [selected, setSelected] = useState(0);
const [tags, setTags] = useState([
{
id: 0,
text: '000',
},
{
id: 1,
text: '111',
},
{
id: 2,
text: '000',
},
{
id: 3,
text: '111',
},
{
id: 4,
text: '000',
},
{
id: 5,
text: '111',
},
]);
const changePosition = (dragIndex, hoverIndex) => {
const data = tags.slice();
const temp = data[dragIndex];
// 交换位置
data[dragIndex] = data[hoverIndex];
data[hoverIndex] = temp;
setTags(data);
};
return (
<div className={styles.groupBox}>
<div className={styles['groupBox-title']}>
<div>商品分组</div>
<Button className={styles['groupBox-btn']} onClick={() => setGroupEdit(!groupEdit)}>
{groupEdit ? '完成' : '编辑分组'}
</Button>
</div>
<div className={styles['groupBox-body']}>
{groupEdit ? (
<DndProvider backend={HTML5Backend}>
<div className={styles['groupBox-body--dragbox']}>
{tags.map((item, index) => (
<DragTag
changePosition={changePosition}
index={index}
{...item}
selected={selected}
changeGroup={e => setSelected(e)}
key={item.id}
/>
))}
<InsertTag />
</div>
</DndProvider>
) : (
<div className={styles['groupBox-body--dragbox']}>
{tags.map((item, index) => (
<Tag
onClick={() => setSelected(index)}
className={[
styles['groupBox-body--tag-normal'],
selected === index ? styles['groupBox-body--tag__cur'] : '',
]}
>
<span className={styles['groupBox-body--tag__text']}>{item.text}</span>
</Tag>
))}
<InsertTag />
</div>
)}
</div>
</div>
);
};
export default GoodsGroup;
...@@ -41,6 +41,7 @@ const InsertTag = options => { ...@@ -41,6 +41,7 @@ const InsertTag = options => {
{!inputVisible && ( {!inputVisible && (
<Tag <Tag
className={[styles['groupBox-body--tag'], styles['groupBox-body--new']]} className={[styles['groupBox-body--tag'], styles['groupBox-body--new']]}
color="blue"
onClick={showInput} onClick={showInput}
> >
<PlusOutlined /> 添加 <PlusOutlined /> 添加
......
import React, { useState, useRef, useEffect, useCallback, useMemo } from 'react'; import React, { useState, useRef, useEffect, useCallback, useMemo } from 'react';
import { Spin, Button, Modal, message, notification, Tag } from 'antd'; import { Spin, Table, Modal, message, notification } from 'antd';
import { PlusOutlined, HolderOutlined, FormOutlined, CloseCircleOutlined } from '@ant-design/icons'; import { MenuOutlined, HolderOutlined, FormOutlined, CloseCircleOutlined } from '@ant-design/icons';
import { DndProvider } from 'react-dnd'; import { SortableContainer, SortableElement, SortableHandle } from 'react-sortable-hoc';
import HTML5Backend from 'react-dnd-html5-backend'; import { arrayMoveImmutable } from 'array-move';
import GoodsGroup from './components/GoodsGroup';
import { searchList } from '../service';
import styles from '../style.less'; import styles from '../style.less';
import DragTag from './components/DragTag'; import { takeawayColumn } from '../staticdata';
import InsertTag from './components/InsertTag';
const Takeaway = options => { const Takeaway = options => {
const [groupEdit, setGroupEdit] = useState(false); const actionRef = useRef(null);
const [selected, setSelected] = useState(0); const [tableData, setTableData] = useState([]);
const [loading, setLoading] = useState(false);
const rowSelection = {};
const DragHandle = SortableHandle(() => (
<MenuOutlined style={{ cursor: 'grab', color: '#999' }} />
));
const [tags, setTags] = useState([ const onSortEnd = ({ oldIndex, newIndex }) => {
{ if (oldIndex !== newIndex) {
id: 0, const newData = arrayMoveImmutable(tableData.slice(), oldIndex, newIndex).filter(el => !!el);
text: '000', setTableData(newData);
}, }
{ };
id: 1,
text: '111', const SortableItem = SortableElement(props => <tr {...props} />);
}, const SortableBody = SortableContainer(props => <tbody {...props} />);
]);
const DraggableContainer = props => (
<SortableBody
useDragHandle
disableAutoscroll
helperClass={styles['row-dragging']}
onSortEnd={onSortEnd}
{...props}
/>
);
const changePosition = (dragIndex, hoverIndex) => { const getDataList = async () => {
const data = tags.slice(); setLoading(true);
const temp = data[dragIndex]; const res = await searchList({ productType: 1 });
// 交换位置 setLoading(false);
data[dragIndex] = data[hoverIndex]; if (res && res.data) {
data[hoverIndex] = temp; setTableData(res.data.records);
setTags(data); }
}; };
const DraggableBodyRow = ({ className, style, ...restProps }) => {
// function findIndex base on Table rowKey props and should always be a right array index
const index = tableData.findIndex(x => x.skuId === restProps['data-row-key']);
return <SortableItem index={index} {...restProps} />;
};
useEffect(() => {
getDataList();
}, []);
return ( return (
<div className={styles.takeawayBox}> <div className={styles.takeawayBox}>
<div className={styles.groupBox}> <Spin spinning={loading}>
<div className={styles['groupBox-title']}> <GoodsGroup />
<div>商品分组</div> <Table
<Button className={styles['groupBox-btn']} onClick={() => setGroupEdit(!groupEdit)}> dataSource={tableData}
{groupEdit ? '完成' : '编辑分组'} bordered
</Button> columns={takeawayColumn.call(this)}
</div> rowKey={record => record.skuId}
<div className={styles['groupBox-body']}> pagination={false}
{groupEdit ? ( // scroll={{ x: '100%', y: 500 }}
<DndProvider backend={HTML5Backend}> // rowSelection={rowSelection}
<div className={styles['groupBox-body--dragbox']}> components={{
{tags.map((item, index) => ( body: {
<DragTag wrapper: DraggableContainer,
changePosition={changePosition} row: DraggableBodyRow,
index={index} },
{...item} }}
selected={selected} />
changeGroup={e => setSelected(e)} </Spin>
key={item.id}
/>
))}
<InsertTag />
</div>
</DndProvider>
) : (
<div className={styles['groupBox-body--dragbox']}>
{tags.map((item, index) => (
<Tag
onClick={() => setSelected(index)}
className={[
styles['groupBox-body--tag-normal'],
selected === index ? styles['groupBox-body--tag__cur'] : '',
]}
>
<span>{item.text}</span>
</Tag>
))}
<InsertTag />
</div>
)}
</div>
</div>
</div> </div>
); );
}; };
......
import React from 'react'; import React from 'react';
import { Button, Badge, Switch, Modal } from 'antd'; import { Button, Badge, Switch, Modal } from 'antd';
import { ExclamationCircleOutlined } from '@ant-design/icons'; import { ExclamationCircleOutlined, MenuOutlined } from '@ant-design/icons';
import styles from './style.less'; import styles from './style.less';
import { resetTime } from '../../utils/utils'; import { resetTime } from '../../utils/utils';
import { apiChangeStateGoods, apiQueryLastAuditRecord } from './service'; import { apiChangeStateGoods, apiQueryLastAuditRecord } from './service';
import { SortableContainer, SortableElement, SortableHandle } from 'react-sortable-hoc';
const { confirm } = Modal; const { confirm } = Modal;
...@@ -244,6 +245,188 @@ export function column() { ...@@ -244,6 +245,188 @@ export function column() {
}, },
]; ];
} }
export function takeawayColumn() {
const onChangeState = async ({ skuId, state }) => {
confirm({
icon: <ExclamationCircleOutlined />,
content: `确认${+state === 6 ? '下架' : '上架'}商品?`,
onOk: async () => {
const res = await apiChangeStateGoods({
ids: skuId,
productState: +state === 6 ? 7 : 6, // 6:上架,7:下架
});
if (res.businessCode === '0000' && res.code === '0000') {
this.handleSearch();
}
},
});
};
const onShowAudit = row => {
this.setState({
auditRow: row,
visibleAuditModal: true,
});
};
const DragHandle = SortableHandle(() => (
<MenuOutlined style={{ cursor: 'grab', color: '#999' }} />
));
return [
{
title: '排序',
dataIndex: 'sort',
width: 60,
align: 'center',
className: [styles['drag-visible'], styles['sort-td']],
render: () => <DragHandle />,
},
{
title: 'SKU编码',
dataIndex: 'skuId',
width: 125,
align: 'center',
render: (_, row) => {
if (row.type === 2) {
return (
<Badge
count={
<div
style={{
color: '#fff',
borderRadius: '3px',
background: '#f5222d',
padding: '2px',
fontSize: '10px',
opacity: 0.7,
}}
>
虚拟
</div>
}
>
<div
style={{
background: '#fbfbfb',
borderRadius: '3px',
padding: '2px',
}}
>
{row.skuId}
</div>
</Badge>
);
}
return (
<div
style={{
background: '#fbfbfb',
borderRadius: '3px',
padding: '2px',
}}
>
{row.skuId}
</div>
);
},
},
{
title: 'SKU商品名称',
width: 135,
align: 'center',
dataIndex: 'skuName',
},
{
title: '供应商价格',
dataIndex: 'marketPrice',
width: 150,
align: 'center',
sorter: (a, b) => a.supplyPrice - b.supplyPrice,
render: (_, row) => (
<div className={styles.price}>
<p>供货价:{(row.supplyPrice || 0).toFixed(2)}</p>
<p>市场价:{(row.marketPrice || 0).toFixed(2)}</p>
</div>
),
},
{
title: '库存',
width: 120,
dataIndex: 'stock',
align: 'center',
sorter: (a, b) => a.stock - b.stock,
render: (_, row) => {
// const stockView = row.productStock;
const stockView = (
<Button type="link" onClick={() => this.onShowStockModal(row)} style={{ padding: 0 }}>
{row.productStock}
</Button>
);
return (
<>
<p>当前库存:{stockView}</p>
<p>可售库存:{_}</p>
{row.type === 1 && row.productStockWarning > 0 && (
<p>预警值:{row.productStockWarning}</p>
)}
</>
);
},
},
{
title: '不支持配送区域',
dataIndex: 'areaTemplateName',
key: 'areaTemplateName',
width: 200,
align: 'center',
},
{
title: '上下架状态',
dataIndex: 'stateDesc', // 5:未上架 ,6 :上架,7:下架
width: 200,
align: 'center',
render: (_, row) => (
<div>
{row.type === 4 && row.state >= 5 ? (
<>
<Switch
checkedChildren="已上架"
checked={row.state === 6}
unCheckedChildren="已下架"
onClick={() => onChangeState(row)}
defaultChecked
/>
</>
) : (
'-'
)}
</div>
),
},
{
title: '审核状态',
dataIndex: 'stateDesc',
width: 200,
align: 'center',
render: (_, row) => (
<div>
<p>{row.state >= 5 ? '审核通过' : _}</p>
<div>
{row.updateState ? (
<Button onClick={() => onShowAudit(row)} type="link">
{row.updateStateDesc}
</Button>
) : (
'--'
)}
</div>
</div>
),
},
];
}
export const disSelectStatus = [2, 5]; export const disSelectStatus = [2, 5];
export const stateList = [ export const stateList = [
{ value: 3, label: '待审核' }, { value: 3, label: '待审核' },
......
...@@ -136,7 +136,7 @@ ...@@ -136,7 +136,7 @@
background-color: #fff; background-color: #fff;
} }
.groupBox { .groupBox {
padding: 0 24px; padding: 0 24px 15px 24px;
&-title { &-title {
display: flex; display: flex;
align-items: center; align-items: center;
...@@ -147,15 +147,15 @@ ...@@ -147,15 +147,15 @@
margin-left: 15px; margin-left: 15px;
} }
&-body { &-body {
padding: 5px 15px; padding: 5px 0;
&--tag { &--tag {
position: relative; position: relative;
box-sizing: border-box; box-sizing: border-box;
height: 28px; height: 34px;
margin-right: 20px; margin-right: 20px;
padding: 0 15px 0 10px; padding: 0 15px 0 10px;
font-size: 14px; font-size: 14px;
line-height: 26px; line-height: 32px;
&__move { &__move {
cursor: move; cursor: move;
} }
...@@ -173,13 +173,17 @@ ...@@ -173,13 +173,17 @@
transform: translate(50%, -50%); transform: translate(50%, -50%);
cursor: pointer; cursor: pointer;
} }
&__text {
user-select: none;
}
} }
&--tag-normal { &--tag-normal {
position: relative; position: relative;
height: 28px; height: 34px;
padding: 0 10px; margin-right: 0;
padding: 0 20px;
font-size: 14px; font-size: 14px;
line-height: 26px; line-height: 32px;
cursor: pointer; cursor: pointer;
} }
&--tag-input { &--tag-input {
...@@ -193,6 +197,11 @@ ...@@ -193,6 +197,11 @@
border: 1px solid #1890ff; border: 1px solid #1890ff;
} }
&--new { &--new {
height: 34px;
margin-right: 0 !important;
margin-left: 10px;
padding: 0 15px;
line-height: 32px;
cursor: pointer; cursor: pointer;
} }
&--dragbox { &--dragbox {
...@@ -200,3 +209,16 @@ ...@@ -200,3 +209,16 @@
} }
} }
} }
.row-dragging {
background: #fafafa;
border: 1px solid #ccc;
& td {
padding: 16px;
}
}
.drag-visible {
visibility: visible;
}
.sort-td {
text-align: center;
}
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