Merge remote-tracking branch 'origin/master'

This commit is contained in:
YunaiV 2019-09-02 19:13:13 +08:00
commit 9b913af9c6
34 changed files with 1283 additions and 224 deletions

View File

@ -125,6 +125,11 @@ export default [
name: 'product-brand-list', name: 'product-brand-list',
component: './Product/ProductBrandList', component: './Product/ProductBrandList',
}, },
{
path: '/product/product-attr-list',
name: 'product-attr-list',
component: './Product/ProductAttrList',
},
], ],
}, },
// promotion // promotion

View File

@ -52,6 +52,8 @@ export default {
'menu.product.product-spu-update': '商品编辑', 'menu.product.product-spu-update': '商品编辑',
'menu.product.product-category-list': '商品分类', 'menu.product.product-category-list': '商品分类',
'menu.product.product-brand-list': '商品品牌', 'menu.product.product-brand-list': '商品品牌',
'menu.product.product-attr-list': '规格管理',
// 订单 // 订单
'menu.order': '订单管理', 'menu.order': '订单管理',
'menu.order.order-list': '订单管理', 'menu.order.order-list': '订单管理',

View File

@ -8,6 +8,7 @@ import {
queryAdminRoleList, queryAdminRoleList,
updateAdmin, updateAdmin,
updateAdminStatus, updateAdminStatus,
deptTreeAll,
} from '../../services/admin'; } from '../../services/admin';
import { arrayToStringParams } from '../../utils/request.qs'; import { arrayToStringParams } from '../../utils/request.qs';
import PaginationHelper from '../../../helpers/PaginationHelper'; import PaginationHelper from '../../../helpers/PaginationHelper';
@ -16,6 +17,21 @@ const SEARCH_PARAMS_DEFAULT = {
nickname: '', nickname: '',
}; };
const buildSelectTree = list => {
return list.map(item => {
let children = [];
if (item.children) {
children = buildSelectTree(item.children);
}
return {
title: item.name,
value: `${item.name}-${item.id}`,
key: item.id,
children,
};
});
};
export default { export default {
namespace: 'adminList', namespace: 'adminList',
@ -37,9 +53,20 @@ export default {
roleModalVisible: false, roleModalVisible: false,
roleCheckedKeys: [], // 此处的 Key 就是角色编号 roleCheckedKeys: [], // 此处的 Key 就是角色编号
roleAssignLoading: false, roleAssignLoading: false,
//部门相关
deptSelectTree: [],
}, },
effects: { effects: {
*getDeptmentTree({ payload }, { call, put }) {
const result = yield call(deptTreeAll, payload);
yield put({
type: 'treeSuccess',
payload: result.data,
});
},
// 查询列表 // 查询列表
*query({ payload }, { call, put }) { *query({ payload }, { call, put }) {
// 显示加载中 // 显示加载中
@ -57,8 +84,8 @@ export default {
list: response.data.list, list: response.data.list,
pagination: PaginationHelper.formatPagination(response.data, payload), pagination: PaginationHelper.formatPagination(response.data, payload),
searchParams: { searchParams: {
nickname: payload.nickname || '' nickname: payload.nickname || '',
} },
}, },
}); });
@ -87,7 +114,7 @@ export default {
yield put({ yield put({
type: 'query', type: 'query',
payload: { payload: {
...PaginationHelper.defaultPayload ...PaginationHelper.defaultPayload,
}, },
}); });
} }
@ -117,7 +144,7 @@ export default {
yield put({ yield put({
type: 'query', type: 'query',
payload: { payload: {
...PaginationHelper.defaultPayload ...PaginationHelper.defaultPayload,
}, },
}); });
} }
@ -139,7 +166,7 @@ export default {
yield put({ yield put({
type: 'query', type: 'query',
payload: { payload: {
...PaginationHelper.defaultPayload ...PaginationHelper.defaultPayload,
}, },
}); });
} }
@ -155,7 +182,7 @@ export default {
yield put({ yield put({
type: 'query', type: 'query',
payload: { payload: {
...PaginationHelper.defaultPayload ...PaginationHelper.defaultPayload,
}, },
}); });
} }
@ -220,6 +247,27 @@ export default {
}, },
reducers: { reducers: {
treeSuccess(state, { payload }) {
const resultData = payload;
const treeData = buildSelectTree(resultData);
// // value 要保护 displayName 不然搜索会失效
// const rootNode = [
// {
// title: '根节点',
// value: `根节点-0`,
// key: 0,
// children: [],
// },
// ];
// const deptSelectTree = rootNode.concat(treeData);
return {
...state,
// list: resultData,
deptSelectTree: treeData,
};
},
changeRoleCheckedKeys(state, { payload }) { changeRoleCheckedKeys(state, { payload }) {
return { return {
...state, ...state,
@ -251,6 +299,6 @@ export default {
...state, ...state,
...payload, ...payload,
}; };
} },
}, },
}; };

View File

@ -1,5 +1,11 @@
import { message } from 'antd'; import { message } from 'antd';
import { deptTreePage, deptTreeAll, addDeptment, updateDeptment } from '../../services/admin'; import {
deptTreePage,
deptTreeAll,
addDeptment,
updateDeptment,
deleteDeptment,
} from '../../services/admin';
const buildSelectTree = list => { const buildSelectTree = list => {
return list.map(item => { return list.map(item => {
@ -35,6 +41,13 @@ export default {
onSuccess && onSuccess(); onSuccess && onSuccess();
} }
}, },
*delete({ payload }, { call, put }) {
const { onSuccess, body } = payload;
const response = yield call(deleteDeptment, body);
if (response && response.code === 0) {
onSuccess && onSuccess();
}
},
*update({ payload }, { call, put }) { *update({ payload }, { call, put }) {
const { onSuccess, body } = payload; const { onSuccess, body } = payload;
const response = yield call(updateDeptment, body); const response = yield call(updateDeptment, body);

View File

@ -1,36 +1,76 @@
import { message } from 'antd'; import { message } from 'antd';
import { productAttrTree, productAttrValueAdd } from '../../services/product'; import {
productAttrTree,
productAttrValueAdd,
productAttrPage,
productAttrAdd,
productAttrUpdate,
productAttrUpdateStatus,
productAttrValueUpdate,
productAttrValueUpdateStatus,
} from '../../services/product';
import PaginationHelper from '../../../helpers/PaginationHelper';
export default { export default {
namespace: 'productAttrList', namespace: 'productAttrList',
state: { state: {
list: [], list: [],
// tree: [],
attrData: [],
pagination: PaginationHelper.defaultPaginationConfig,
}, },
effects: { effects: {
// *add({ payload }, { call, put }) { *add({ payload }, { call, put }) {
// const { callback, body } = payload; const { onSuccess, onFail, body } = payload;
// const response = yield call(productCategoryAdd, body); const response = yield call(productAttrAdd, body);
// if (callback) { if (response && response.code === 0) {
// callback(response); onSuccess && onSuccess();
// } } else {
// yield put({ onFail && onFail(response);
// type: 'tree', }
// payload: {}, },
// });
// }, *update({ payload }, { call, put }) {
// *update({ payload }, { call, put }) { const { onSuccess, onFail, body } = payload;
// const { callback, body } = payload; const response = yield call(productAttrUpdate, body);
// const response = yield call(productCategoryUpdate, body); if (response && response.code === 0) {
// if (callback) { onSuccess && onSuccess();
// callback(response); } else {
// } onFail && onFail(response);
// yield put({ }
// type: 'tree', },
// payload: {},
// }); *value_update({ payload }, { call, put }) {
// }, const { onSuccess, onFail, body } = payload;
const response = yield call(productAttrValueUpdate, body);
if (response && response.code === 0) {
onSuccess && onSuccess();
} else {
onFail && onFail(response);
}
},
*update_status({ payload }, { call, put }) {
const { onSuccess, onFail, body } = payload;
const response = yield call(productAttrUpdateStatus, body);
if (response && response.code === 0) {
onSuccess && onSuccess();
} else {
onFail && onFail(response);
}
},
*value_update_status({ payload }, { call, put }) {
const { onSuccess, onFail, body } = payload;
const response = yield call(productAttrValueUpdateStatus, body);
if (response && response.code === 0) {
onSuccess && onSuccess();
} else {
onFail && onFail(response);
}
},
// *updateStatus({ payload }, { call, put }) { // *updateStatus({ payload }, { call, put }) {
// const { callback, body } = payload; // const { callback, body } = payload;
// const response = yield call(productCategoryUpdateStatus, body); // const response = yield call(productCategoryUpdateStatus, body);
@ -51,6 +91,21 @@ export default {
// }); // });
// }, // },
*page({ payload }, { call, put }) {
const result = yield call(productAttrPage, payload);
let attrData = {};
if (result.code === 0) {
attrData = result.data;
}
yield put({
type: 'save',
payload: {
attrData,
pagination: PaginationHelper.formatPagination(attrData, payload),
},
});
},
*tree({ payload }, { call, put }) { *tree({ payload }, { call, put }) {
const { queryParams } = payload; const { queryParams } = payload;
const response = yield call(productAttrTree, queryParams); const response = yield call(productAttrTree, queryParams);
@ -62,6 +117,17 @@ export default {
}, },
}); });
}, },
*value_add({ payload }, { call, put }) {
const { onSuccess, onFail, body } = payload;
const response = yield call(productAttrValueAdd, body);
if (response && response.code === 0) {
onSuccess && onSuccess();
} else {
onFail && onFail(response);
}
},
*addValue({ payload, callback }, { call, put }) { *addValue({ payload, callback }, { call, put }) {
// debugger; // debugger;
// const {queryParams} = payload; // const {queryParams} = payload;
@ -84,10 +150,16 @@ export default {
callback(response.data); callback(response.data);
} }
} }
} },
}, },
reducers: { reducers: {
save(state, action) {
return {
...state,
...action.payload,
};
},
treeSuccess(state, { payload }) { treeSuccess(state, { payload }) {
return { return {
...state, ...state,

View File

@ -2,22 +2,44 @@
import React, { PureComponent, Fragment } from 'react'; import React, { PureComponent, Fragment } from 'react';
import { connect } from 'dva'; import { connect } from 'dva';
import {Card, Form, Input, Button, Modal, message, Table, Divider, Tree, Spin, Row, Col, Select, Icon} from 'antd'; import {
import { checkTypeWithEnglishAndNumbers } from '../../../helpers/validator' Card,
Form,
Input,
Button,
Modal,
message,
Table,
Divider,
Tree,
Spin,
Row,
Col,
Select,
Icon,
TreeSelect,
} from 'antd';
import { checkTypeWithEnglishAndNumbers } from '../../../helpers/validator';
import PageHeaderWrapper from '@/components/PageHeaderWrapper'; import PageHeaderWrapper from '@/components/PageHeaderWrapper';
import styles from './AdminList.less'; import styles from './AdminList.less';
import moment from "moment"; import moment from 'moment';
import PaginationHelper from "../../../helpers/PaginationHelper"; import PaginationHelper from '../../../helpers/PaginationHelper';
const FormItem = Form.Item; const FormItem = Form.Item;
const { TreeNode } = Tree; const { TreeNode } = Tree;
const status = ['未知', '正常', '禁用']; const status = ['未知', '正常', '禁用'];
// 列表 // 列表
function List ({ dataSource, loading, pagination, searchParams, dispatch, function List({
handleModalVisible, handleRoleAssignModalVisible}) { dataSource,
loading,
pagination,
searchParams,
dispatch,
handleModalVisible,
handleRoleAssignModalVisible,
}) {
function handleRoleAssign(record) { function handleRoleAssign(record) {
// 显示 Modal // 显示 Modal
handleRoleAssignModalVisible(true, record); handleRoleAssignModalVisible(true, record);
@ -66,12 +88,16 @@ function List ({ dataSource, loading, pagination, searchParams, dispatch,
const columns = [ const columns = [
{ {
title: '账号', title: '账号',
dataIndex: 'username' dataIndex: 'username',
}, },
{ {
title: '员工姓名', title: '员工姓名',
dataIndex: 'nickname', dataIndex: 'nickname',
}, },
{
title: '部门',
dataIndex: 'deptment.name',
},
{ {
title: '角色', title: '角色',
dataIndex: 'roles', dataIndex: 'roles',
@ -85,8 +111,8 @@ function List ({ dataSource, loading, pagination, searchParams, dispatch,
text += roles[i].name; text += roles[i].name;
} }
} }
return (<span>{text}</span>); return <span>{text}</span>;
} },
}, },
{ {
title: '状态', title: '状态',
@ -114,30 +140,30 @@ function List ({ dataSource, loading, pagination, searchParams, dispatch,
<a className={styles.tableDelete} onClick={() => handleStatus(record)}> <a className={styles.tableDelete} onClick={() => handleStatus(record)}>
{statusText} {statusText}
</a> </a>
{ {record.status === 2 ? (
record.status === 2 ?
<span> <span>
<Divider type="vertical" /> <Divider type="vertical" />
<a className={styles.tableDelete} onClick={() => handleDelete(record)}> <a className={styles.tableDelete} onClick={() => handleDelete(record)}>
删除 删除
</a> </a>
</span> : null </span>
} ) : null}
</Fragment> </Fragment>
); );
}, },
}, },
]; ];
function onPageChange(page) { // 翻页 function onPageChange(page) {
// 翻页
dispatch({ dispatch({
type: 'adminList/query', type: 'adminList/query',
payload: { payload: {
pageNo: page.current, pageNo: page.current,
pageSize: page.pageSize, pageSize: page.pageSize,
...searchParams ...searchParams,
} },
}) });
} }
return ( return (
@ -149,7 +175,7 @@ function List ({ dataSource, loading, pagination, searchParams, dispatch,
pagination={pagination} pagination={pagination}
onChange={onPageChange} onChange={onPageChange}
/> />
) );
} }
// 搜索表单 // 搜索表单
@ -157,17 +183,23 @@ const SearchForm = Form.create()(props => {
const { const {
form, form,
form: { getFieldDecorator }, form: { getFieldDecorator },
dispatch dispatch,
deptSelectTree,
} = props; } = props;
function search() { function search() {
const fields = form.getFieldsValue();
if (fields.deptmentId) {
const deptmentId = fields.deptmentId.split('-')[1];
fields.deptmentId = deptmentId;
}
dispatch({ dispatch({
type: 'adminList/query', type: 'adminList/query',
payload: { payload: {
...PaginationHelper.defaultPayload, ...PaginationHelper.defaultPayload,
...form.getFieldsValue() ...fields,
} },
}) });
} }
// 提交搜索 // 提交搜索
@ -189,12 +221,28 @@ const SearchForm = Form.create()(props => {
return ( return (
<Form onSubmit={handleSubmit} layout="inline"> <Form onSubmit={handleSubmit} layout="inline">
<Row gutter={{ md: 8, lg: 24, xl: 48 }}> <Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={8} sm={24}> <Col md={6} sm={24}>
<FormItem label="员工姓名"> <FormItem label="员工姓名">
{getFieldDecorator('nickname')(<Input placeholder="请输入" />)} {getFieldDecorator('nickname')(<Input style={{ width: 250 }} placeholder="请输入" />)}
</FormItem> </FormItem>
</Col> </Col>
<Col md={8} sm={24}> <Col md={6} sm={24}>
<FormItem label="归属部门">
{getFieldDecorator('deptmentId', {
rules: [{ required: true, message: '请选择部门' }],
})(
<TreeSelect
showSearch
style={{ width: 250 }}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={deptSelectTree}
placeholder="选择部门"
/>
)}
</FormItem>
</Col>
<Col md={12} sm={24}>
<span className={styles.submitButtons}> <span className={styles.submitButtons}>
<Button type="primary" htmlType="submit"> <Button type="primary" htmlType="submit">
查询 查询
@ -211,12 +259,24 @@ const SearchForm = Form.create()(props => {
// 添加 or 修改 Form 表单 // 添加 or 修改 Form 表单
const AddOrUpdateForm = Form.create()(props => { const AddOrUpdateForm = Form.create()(props => {
const { dispatch, modalVisible, form, handleModalVisible, modalType, formVals } = props; const {
dispatch,
modalVisible,
form,
handleModalVisible,
modalType,
formVals,
deptSelectTree,
} = props;
const okHandle = () => { const okHandle = () => {
form.validateFields((err, fields) => { form.validateFields((err, fields) => {
if (err) return; if (err) return;
// 添加表单 // 添加表单
if (fields.deptmentId) {
const deptmentId = fields.deptmentId.split('-')[1];
fields.deptmentId = deptmentId;
}
if (modalType === 'add') { if (modalType === 'add') {
dispatch({ dispatch({
type: 'adminList/add', type: 'adminList/add',
@ -264,29 +324,52 @@ const AddOrUpdateForm = Form.create()(props => {
title={title} title={title}
visible={modalVisible} visible={modalVisible}
onOk={okHandle} onOk={okHandle}
okText='保存' okText="保存"
onCancel={() => handleModalVisible()} onCancel={() => handleModalVisible()}
> >
<FormItem labelCol={{ span: 5 }} wrapperCol={{ span: 15 }} label="账号"> <FormItem labelCol={{ span: 5 }} wrapperCol={{ span: 15 }} label="账号">
{form.getFieldDecorator('username', { {form.getFieldDecorator('username', {
rules: [{ required: true, message: '请输入账号'}, rules: [
{ required: true, message: '请输入账号' },
{ max: 16, min: 6, message: '长度为 6-16 ' }, { max: 16, min: 6, message: '长度为 6-16 ' },
{ validator: (rule, value, callback) => checkTypeWithEnglishAndNumbers(rule, value, callback, '数字以及字母')} {
validator: (rule, value, callback) =>
checkTypeWithEnglishAndNumbers(rule, value, callback, '数字以及字母'),
},
], ],
initialValue: formVals.username, initialValue: formVals.username,
})(<Input placeholder="请输入" />)} })(<Input placeholder="请输入" />)}
</FormItem> </FormItem>
<FormItem labelCol={{ span: 5 }} wrapperCol={{ span: 15 }} label="员工姓名"> <FormItem labelCol={{ span: 5 }} wrapperCol={{ span: 15 }} label="员工姓名">
{form.getFieldDecorator('nickname', { {form.getFieldDecorator('nickname', {
rules: [{ required: true, message: '请输入员工姓名'}, rules: [
{max: 10, message: '姓名最大长度为 10'}], { required: true, message: '请输入员工姓名' },
{ max: 10, message: '姓名最大长度为 10' },
],
initialValue: formVals.nickname, initialValue: formVals.nickname,
})(<Input placeholder="请输入" />)} })(<Input placeholder="请输入" />)}
</FormItem> </FormItem>
<FormItem labelCol={{ span: 5 }} wrapperCol={{ span: 15 }} label="归属部门">
{form.getFieldDecorator('deptmentId', {
rules: [{ required: true, message: '请选择部门' }],
initialValue:
formVals.deptmentId && formVals.deptmentId !== 0 ? formVals.deptmentId : null,
})(
<TreeSelect
showSearch
style={{ width: 280 }}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={deptSelectTree}
placeholder="选择部门"
/>
)}
</FormItem>
<FormItem labelCol={{ span: 5 }} wrapperCol={{ span: 15 }} label="密码"> <FormItem labelCol={{ span: 5 }} wrapperCol={{ span: 15 }} label="密码">
{form.getFieldDecorator('password', { {form.getFieldDecorator('password', {
rules: [{ required: modalType === 'add', message: '请填写密码'}, // 添加时必须输入密码 rules: [
{max: 16, min: 6, message: '长度为 6-18 '}], { required: modalType === 'add', message: '请填写密码' }, // 添加时必须输入密码
{ max: 16, min: 6, message: '长度为 6-18 ' },
],
initialValue: formVals.password, initialValue: formVals.password,
})(<Input placeholder="请输入" type="password" />)} })(<Input placeholder="请输入" type="password" />)}
</FormItem> </FormItem>
@ -321,7 +404,8 @@ const RoleAssignModal = Form.create()(props => {
const renderTreeNodes = data => { const renderTreeNodes = data => {
return data.map(item => { return data.map(item => {
if (item.children) { // 递归拼接节点 if (item.children) {
// 递归拼接节点
return ( return (
<TreeNode title={item.title} key={item.key} dataRef={item}> <TreeNode title={item.title} key={item.key} dataRef={item}>
{renderTreeNodes(item.children)} {renderTreeNodes(item.children)}
@ -387,32 +471,31 @@ const RoleAssignModal = Form.create()(props => {
onOk={okHandle} onOk={okHandle}
onCancel={() => handleModalVisible()} onCancel={() => handleModalVisible()}
> >
<Spin spinning={loading}> <Spin spinning={loading}>{renderModalContent(treeData)}</Spin>
{renderModalContent(treeData)}
</Spin>
</Modal> </Modal>
); );
}); });
@connect(({ adminList }) => ({ @connect(({ adminList }) => ({
// list: adminList.list, // list: adminList.list,
// pagination: adminList.pagination, // pagination: adminList.pagination,
...adminList, ...adminList,
})) }))
// 主界面 // 主界面
@Form.create() @Form.create()
class AdminList extends PureComponent { class AdminList extends PureComponent {
componentDidMount() { componentDidMount() {
const { dispatch } = this.props; const { dispatch } = this.props;
dispatch({ dispatch({
type: 'adminList/query', type: 'adminList/query',
payload: { payload: {
...PaginationHelper.defaultPayload ...PaginationHelper.defaultPayload,
}, },
}); });
dispatch({
type: 'adminList/getDeptmentTree',
payload: {},
});
} }
handleModalVisible = (modalVisible, modalType, record) => { handleModalVisible = (modalVisible, modalType, record) => {
@ -422,7 +505,7 @@ class AdminList extends PureComponent {
payload: { payload: {
modalVisible, modalVisible,
modalType, modalType,
formVals: record || {} formVals: record || {},
}, },
}); });
}; };
@ -433,18 +516,29 @@ class AdminList extends PureComponent {
type: 'adminList/setAll', type: 'adminList/setAll',
payload: { payload: {
roleModalVisible: roleModalVisible, roleModalVisible: roleModalVisible,
formVals: record || {} formVals: record || {},
}, },
}); });
}; };
render() { render() {
// let that = this; // let that = this;
const { dispatch, const {
list, listLoading, searchParams, pagination, dispatch,
modalVisible, modalType, formVals, list,
listLoading,
searchParams,
pagination,
modalVisible,
modalType,
formVals,
confirmLoading, confirmLoading,
roleList, roleModalVisible, roleAssignLoading, roleCheckedKeys } = this.props; roleList,
roleModalVisible,
roleAssignLoading,
roleCheckedKeys,
deptSelectTree,
} = this.props;
// 列表属性 // 列表属性
const listProps = { const listProps = {
@ -461,6 +555,7 @@ class AdminList extends PureComponent {
// 搜索表单属性 // 搜索表单属性
const searchFormProps = { const searchFormProps = {
dispatch, dispatch,
deptSelectTree,
}; };
// 添加 or 更新表单属性 // 添加 or 更新表单属性
@ -469,6 +564,7 @@ class AdminList extends PureComponent {
modalType, modalType,
formVals, formVals,
dispatch, dispatch,
deptSelectTree,
handleModalVisible: this.handleModalVisible, // Function handleModalVisible: this.handleModalVisible, // Function
}; };

View File

@ -107,13 +107,23 @@ export default class DepetmentList extends PureComponent {
componentDidMount() { componentDidMount() {
const { dispatch } = this.props; const { dispatch } = this.props;
dispatch({ dispatch({
type: 'deptmentList/getDeptmentList', type: 'deptmentList/getDeptmentAll',
payload: { payload: {
...PaginationHelper.defaultPayload, ...PaginationHelper.defaultPayload,
}, },
}); });
} }
initFetch = () => {
const { dispatch } = this.props;
dispatch({
type: 'deptmentList/getDeptmentAll',
payload: {
...PaginationHelper.defaultPayload,
},
});
};
handleModalVisible = (flag, modalType, initValues) => { handleModalVisible = (flag, modalType, initValues) => {
this.setState({ this.setState({
modalVisible: !!flag, modalVisible: !!flag,
@ -130,6 +140,33 @@ export default class DepetmentList extends PureComponent {
} }
}; };
handleDelete(row) {
const { dispatch } = this.props;
const _this = this;
Modal.confirm({
title: `确认删除?`,
content: `${row.name}`,
onOk() {
dispatch({
type: 'deptmentList/delete',
payload: {
body: {
id: row.id,
},
onSuccess: () => {
message.success('删除成功');
_this.initFetch();
},
onFail: response => {
message.warn('删除失败' + response.message);
},
},
});
},
onCancel() {},
});
}
handleAdd = ({ fields, modalType, initValues }) => { handleAdd = ({ fields, modalType, initValues }) => {
const { dispatch } = this.props; const { dispatch } = this.props;
if (modalType === 'add') { if (modalType === 'add') {
@ -142,6 +179,7 @@ export default class DepetmentList extends PureComponent {
onSuccess: () => { onSuccess: () => {
message.success('添加成功'); message.success('添加成功');
this.handleModalVisible(); this.handleModalVisible();
this.initFetch();
}, },
onFail: response => { onFail: response => {
message.warn('添加失败' + response.message); message.warn('添加失败' + response.message);
@ -159,6 +197,7 @@ export default class DepetmentList extends PureComponent {
onSuccess: () => { onSuccess: () => {
message.success('更新成功成功'); message.success('更新成功成功');
this.handleModalVisible(); this.handleModalVisible();
this.initFetch();
}, },
onFail: response => { onFail: response => {
message.warn('更新失败' + response.message); message.warn('更新失败' + response.message);
@ -169,7 +208,7 @@ export default class DepetmentList extends PureComponent {
}; };
render() { render() {
const { deptmentData, deptmentList } = this.props; const { deptmentData, deptmentList, loading } = this.props;
const { selectTree } = deptmentList; const { selectTree } = deptmentList;
const { modalVisible, modalType, initValues } = this.state; const { modalVisible, modalType, initValues } = this.state;
const parentMethods = { const parentMethods = {
@ -229,8 +268,9 @@ export default class DepetmentList extends PureComponent {
<Table <Table
defaultExpandAllRows={true} defaultExpandAllRows={true}
columns={columns} columns={columns}
dataSource={deptmentData.list ? deptmentData.list : []} dataSource={deptmentList.list ? deptmentList.list : []}
rowKey="id" rowKey="id"
loading={loading}
/> />
</Card> </Card>
<CreateForm {...parentMethods} selectTree={selectTree} modalVisible={modalVisible} /> <CreateForm {...parentMethods} selectTree={selectTree} modalVisible={modalVisible} />

View File

@ -0,0 +1,550 @@
import React, { PureComponent, Fragment, Component } from 'react';
import {
Row,
Col,
Form,
Card,
Table,
Button,
Divider,
Modal,
Input,
message,
Switch,
Select,
} from 'antd';
import moment from 'moment';
import { connect } from 'dva';
import PageHeaderWrapper from '@/components/PageHeaderWrapper';
import PaginationHelper from '../../../helpers/PaginationHelper';
import styles from './ProductAttrList.less';
const FormItem = Form.Item;
const Option = Select.Option;
const ValueCreateForm = Form.create()(props => {
const {
valueModalVisible,
form,
handleValueAdd,
handleValueModalVisible,
modalType,
initValues,
tree,
} = props;
const okHandle = () => {
form.validateFields((err, fieldsValue) => {
if (err) return;
let pid = fieldsValue.pid;
if (fieldsValue.pid) {
pid = pid.split('-')[1];
fieldsValue.pid = pid;
}
form.resetFields();
handleValueAdd({
fields: fieldsValue,
modalType,
initValues,
});
});
};
const selectStyle = {
width: 200,
};
function onTypeChange(event) {
initValues.type = parseInt(event.target.value);
}
const title = modalType === 'add' ? '添加规格值' : '编辑规格值';
const okText = modalType === 'add' ? '添加' : '编辑';
return (
<Modal
destroyOnClose
title={title}
visible={valueModalVisible}
onOk={okHandle}
okText={okText}
onCancel={() => handleValueModalVisible()}
>
{modalType === 'add' ? (
<FormItem labelCol={{ span: 5 }} wrapperCol={{ span: 15 }} label="规格">
{form.getFieldDecorator('attrId', {
// initialValue: template.durationHour ? template.durationHour : '3',
rules: [
{
required: true,
message: '请选择规格',
},
],
})(
<Select placeholder="请选择规格" style={{ width: 120 }}>
{tree.map(item => (
<Option value={item.id}>{item.name}</Option>
))}
{/* <Option value="1">1</Option> */}
</Select>
)}
</FormItem>
) : null}
<FormItem labelCol={{ span: 5 }} wrapperCol={{ span: 15 }} label="规格值">
{form.getFieldDecorator('name', {
initialValue: initValues ? initValues.name : null,
rules: [{ required: true, message: '请输入规格值', min: 2 }],
})(<Input placeholder="规格值" />)}
</FormItem>
</Modal>
);
});
const CreateForm = Form.create()(props => {
const { modalVisible, form, handleAdd, handleModalVisible, modalType, initValues } = props;
const okHandle = () => {
form.validateFields((err, fieldsValue) => {
if (err) return;
let pid = fieldsValue.pid;
if (fieldsValue.pid) {
pid = pid.split('-')[1];
fieldsValue.pid = pid;
}
form.resetFields();
handleAdd({
fields: fieldsValue,
modalType,
initValues,
});
});
};
const selectStyle = {
width: 200,
};
function onTypeChange(event) {
initValues.type = parseInt(event.target.value);
}
const title = modalType === 'add' ? '添加规格' : '编辑规格';
const okText = modalType === 'add' ? '添加' : '编辑';
return (
<Modal
destroyOnClose
title={title}
visible={modalVisible}
onOk={okHandle}
okText={okText}
onCancel={() => handleModalVisible()}
>
<FormItem labelCol={{ span: 5 }} wrapperCol={{ span: 15 }} label="规格名称">
{form.getFieldDecorator('name', {
initialValue: initValues ? initValues.name : null,
rules: [{ required: true, message: '请输入规格名称', min: 2 }],
})(<Input placeholder="规格名称" />)}
</FormItem>
</Modal>
);
});
@connect(({ productAttrList, loading }) => ({
productAttrList,
attrData: productAttrList.attrData,
tree: productAttrList.tree,
loading: loading.models.productAttrList,
}))
@Form.create()
export default class ProductAttrList extends PureComponent {
state = {
modalVisible: false,
valueModalVisible: false,
modalType: 'add', //add or update
initValues: {},
current: 1,
pageSize: 10,
name: null,
};
componentDidMount() {
this.initFetch();
}
initFetch = () => {
const { dispatch } = this.props;
const { current, pageSize, name } = this.state;
dispatch({
type: 'productAttrList/page',
payload: {
pageNo: current,
pageSize,
name,
},
});
// const { dispatch } = this.props;
dispatch({
type: 'productAttrList/tree',
payload: {
...PaginationHelper.defaultPayload,
},
});
};
expandedRowRender = record => {
const columns = [
{
title: '规格值',
dataIndex: 'name',
},
{
title: '状态',
// dataIndex: 'status',
render: (text, record) => (
<Switch
checked={record.status === 1}
onChange={checked => this.switchValueChange(checked, record)}
/>
),
},
{
title: '创建时间',
dataIndex: 'createTime',
sorter: true,
render: val => <span>{moment(val).format('YYYY-MM-DD')}</span>,
},
{
title: '操作',
render: (text, record) => (
<Fragment>
<a onClick={() => this.handleValueModalVisible(true, 'update', record)}>编辑</a>
<Divider type="vertical" />
{/* <a className={styles.tableDelete} onClick={() => this.handleDelete(record)}>
删除
</a> */}
</Fragment>
),
},
];
return <Table columns={columns} dataSource={record.values} pagination={false} />;
};
handleAdd = ({ fields, modalType, initValues }) => {
const { dispatch } = this.props;
if (modalType === 'add') {
dispatch({
type: 'productAttrList/add',
payload: {
body: {
...fields,
},
onSuccess: () => {
message.success('添加成功');
this.handleModalVisible();
this.initFetch();
},
onFail: response => {
message.warn('添加失败' + response.message);
},
},
});
} else {
dispatch({
type: 'productAttrList/update',
payload: {
body: {
...initValues,
...fields,
},
onSuccess: () => {
message.success('更新成功');
this.handleModalVisible();
this.initFetch();
},
onFail: response => {
message.warn('更新失败' + response.message);
},
},
});
}
};
handleValueAdd = ({ fields, modalType, initValues }) => {
const { dispatch } = this.props;
if (modalType === 'add') {
dispatch({
type: 'productAttrList/value_add',
payload: {
body: {
...fields,
},
onSuccess: () => {
message.success('添加成功');
this.handleValueModalVisible();
this.initFetch();
},
onFail: response => {
message.warn('添加失败' + response.message);
},
},
});
} else {
dispatch({
type: 'productAttrList/value_update',
payload: {
body: {
...initValues,
...fields,
},
onSuccess: () => {
message.success('更新成功');
this.handleValueModalVisible();
this.initFetch();
},
onFail: response => {
message.warn('更新失败' + response.message);
},
},
});
}
};
handleModalVisible = (flag, modalType, initValues) => {
this.setState({
modalVisible: !!flag,
initValues: initValues || {},
modalType: modalType || 'add',
});
};
handleValueModalVisible = (flag, modalType, initValues) => {
this.setState({
valueModalVisible: !!flag,
initValues: initValues || {},
modalType: modalType || 'add',
});
};
handleTableChange = pagination => {
const { pageSize, current, index } = pagination;
this.setState(
{
current,
pageSize,
},
function() {
this.initFetch();
}
);
};
switchValueChange = (checked, record) => {
const { dispatch } = this.props;
dispatch({
type: 'productAttrList/value_update_status',
payload: {
body: {
id: record.id,
status: checked ? 1 : 2,
},
onSuccess: () => {
message.success('修改状态成功');
this.initFetch();
},
},
});
};
switchChange = (checked, record) => {
const { dispatch } = this.props;
dispatch({
type: 'productAttrList/update_status',
payload: {
body: {
id: record.id,
status: checked ? 1 : 2,
},
onSuccess: () => {
message.success('修改状态成功');
this.initFetch();
},
},
});
};
handleFormReset = () => {
const { form, dispatch } = this.props;
form.resetFields();
this.setState(
{
name: null,
},
function() {
this.initFetch();
}
);
};
handleCondition = e => {
e.preventDefault();
const { dispatch, form } = this.props;
form.validateFields((err, fieldsValue) => {
if (err) return;
const values = {
...fieldsValue,
};
if (values.name) {
this.setState(
{
searched: true,
name: values.name,
},
function() {
this.initFetch();
}
);
} else {
this.initFetch();
}
// dispatch({
// type: 'fenfa/getCategoryList',
// payload: {
// key: values.name
// },
// });
});
};
renderSimpleForm() {
const { form } = this.props;
const { getFieldDecorator } = form;
return (
<Form onSubmit={this.handleCondition} layout="inline">
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={8} sm={24}>
<FormItem label="规格名称">
{getFieldDecorator('name')(<Input placeholder="请输入" />)}
</FormItem>
</Col>
<Col md={8} sm={24}>
<span className={styles.submitButtons}>
<Button type="primary" htmlType="submit">
查询
</Button>
<Button style={{ marginLeft: 8 }} onClick={this.handleFormReset}>
重置
</Button>
</span>
</Col>
</Row>
</Form>
);
}
render() {
const { attrData, productAttrList, loading, tree } = this.props;
const columns = [
{
title: '规格名称',
dataIndex: 'name',
},
{
title: '状态',
// dataIndex: 'status',
render: (text, record) => (
<Switch
checked={record.status === 1}
onChange={checked => this.switchChange(checked, record)}
/>
),
},
{
title: '创建时间',
dataIndex: 'createTime',
sorter: true,
render: val => <span>{moment(val).format('YYYY-MM-DD')}</span>,
},
{
title: '操作',
render: (text, record) => (
<Fragment>
<a onClick={() => this.handleModalVisible(true, 'update', record)}>编辑</a>
<Divider type="vertical" />
<a onClick={() => this.handleValueModalVisible(true, 'add', {})}>新建规格值</a>
{/* <a className={styles.tableDelete} onClick={() => this.handleDelete(record)}>
删除
</a> */}
</Fragment>
),
},
];
const { modalVisible, modalType, initValues, valueModalVisible } = this.state;
const parentMethods = {
handleAdd: this.handleAdd,
handleModalVisible: this.handleModalVisible,
modalType,
initValues,
};
const valueFormParentMethods = {
handleValueAdd: this.handleValueAdd,
handleValueModalVisible: this.handleValueModalVisible,
modalType,
initValues,
tree: tree,
};
const pagination = {
total: attrData.count,
index: this.state.current,
pageSize: this.state.pageSize,
};
return (
<PageHeaderWrapper>
<Card>
<div className={styles.tableList}>
<div className={styles.tableListOperator}>
<Row>
<Col span={8}>
<Button
icon="plus"
type="primary"
onClick={() => this.handleModalVisible(true, 'add', {})}
>
新建规格
</Button>
</Col>
<Col span={16}>
<div>{this.renderSimpleForm()}</div>
</Col>
</Row>
</div>
</div>
<Table
defaultExpandAllRows={true}
columns={columns}
dataSource={attrData.attrs ? attrData.attrs : []}
rowKey="id"
loading={loading}
pagination={pagination}
expandedRowRender={this.expandedRowRender}
defaultExpandAllRows={false}
onChange={pagination => this.handleTableChange(pagination)}
/>
</Card>
{modalVisible ? <CreateForm {...parentMethods} modalVisible={modalVisible} /> : null}
{valueModalVisible ? (
<ValueCreateForm {...valueFormParentMethods} valueModalVisible={valueModalVisible} />
) : null}
</PageHeaderWrapper>
);
}
}

View File

@ -0,0 +1,11 @@
@import '~antd/lib/style/themes/default.less';
@import '~@/utils/utils.less';
.tableList {
.tableListOperator {
margin-bottom: 16px;
button {
margin-right: 8px;
}
}
}

View File

@ -74,6 +74,12 @@ export async function updateDeptment(params) {
}); });
} }
export async function deleteDeptment(params) {
return request(`/admin-api/admins/dept/delete?${stringify(params)}`, {
method: 'POST',
});
}
export async function deptTreePage(params) { export async function deptTreePage(params) {
return request(`/admin-api/admins/dept/tree/page?${stringify(params)}`, { return request(`/admin-api/admins/dept/tree/page?${stringify(params)}`, {
method: 'GET', method: 'GET',

View File

@ -85,12 +85,53 @@ export async function productSpuInfo(params) {
// product attr + attr value // product attr + attr value
export async function productAttrPage(params) {
return request(`/product-api/admins/attr/page?${stringify(params)}`, {
method: 'GET',
});
}
export async function productAttrAdd(params) {
return request(`/product-api/admins/attr/add?${stringify(params)}`, {
method: 'POST',
body: {},
});
}
export async function productAttrUpdate(params) {
return request(`/product-api/admins/attr/update?${stringify(params)}`, {
method: 'POST',
body: {},
});
}
export async function productAttrUpdateStatus(params) {
return request(`/product-api/admins/attr/update_status?${stringify(params)}`, {
method: 'POST',
body: {},
});
}
export async function productAttrTree(params) { export async function productAttrTree(params) {
return request(`/product-api/admins/attr/tree?${stringify(params)}`, { return request(`/product-api/admins/attr/tree?${stringify(params)}`, {
method: 'GET', method: 'GET',
}); });
} }
export async function productAttrValueUpdate(params) {
return request(`/product-api/admins/attr_value/update?${stringify(params)}`, {
method: 'POST',
body: {},
});
}
export async function productAttrValueUpdateStatus(params) {
return request(`/product-api/admins/attr_value/update_status?${stringify(params)}`, {
method: 'POST',
body: {},
});
}
export async function productAttrValueAdd(params) { export async function productAttrValueAdd(params) {
return request(`/product-api/admins/attr_value/add?${stringify(params)}`, { return request(`/product-api/admins/attr_value/add?${stringify(params)}`, {
method: 'POST', method: 'POST',
@ -116,12 +157,12 @@ export async function productBrandUpdate(params) {
export async function productBrandGet(params) { export async function productBrandGet(params) {
return request(`/product-api/admins/brand/get?${stringify(params)}`, { return request(`/product-api/admins/brand/get?${stringify(params)}`, {
method: 'GET' method: 'GET',
}); });
} }
export async function productBrandPage(params) { export async function productBrandPage(params) {
return request(`/product-api/admins/brand/page?${stringify(params)}`, { return request(`/product-api/admins/brand/page?${stringify(params)}`, {
method: 'GET' method: 'GET',
}); });
} }

View File

@ -20,6 +20,7 @@
- [x] 展示类目 - [x] 展示类目
- [ ] 品牌管理【开发中 @黑子 - [ ] 品牌管理【开发中 @黑子
- [ ] 商品标签 - [ ] 商品标签
- [ ] 商品规格页面【开发中 @Tprotect曦
- [ ] 订单管理 - [ ] 订单管理
- [x] 销售单 - [x] 销售单
- [x] 售后单 - [x] 售后单
@ -49,7 +50,7 @@
- [x] 员工管理 - [x] 员工管理
- [x] 角色管理 <!--【前端页面需要细化下】--> - [x] 角色管理 <!--【前端页面需要细化下】-->
- [x] 权限管理 <!--【前端页面需要细化下】--> - [x] 权限管理 <!--【前端页面需要细化下】-->
- [ ] 部门管理【开发中 @Tprotect曦 - [x] 部门管理 <!--【员工页面部门搜索需要优化】-->
- [x] 数据字典 - [x] 数据字典
- [x] 短信管理 - [x] 短信管理
- [X] 短信模板 - [X] 短信模板

View File

@ -50,6 +50,7 @@ CREATE TABLE `admin` (
`nickname` varchar(10) NOT NULL COMMENT '昵称', `nickname` varchar(10) NOT NULL COMMENT '昵称',
`password` varchar(32) NOT NULL COMMENT '密码\n *\n * TODO 芋艿 暂时最简单的 MD5', `password` varchar(32) NOT NULL COMMENT '密码\n *\n * TODO 芋艿 暂时最简单的 MD5',
`status` tinyint(11) NOT NULL COMMENT '账号状态', `status` tinyint(11) NOT NULL COMMENT '账号状态',
`deptment_id` int(11) DEFAULT 0 NOT NULL COMMENT '部门id',
`create_time` datetime NOT NULL COMMENT '创建时间', `create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`deleted` bit(1) DEFAULT NULL, `deleted` bit(1) DEFAULT NULL,
@ -313,6 +314,8 @@ INSERT INTO `resource` VALUES (50, 2, 3, '删除字典', 19, '', NULL, 'system.d
INSERT INTO `resource` VALUES (51, 1, -1, '短信ss', 0, '', 'user', '', '2019-05-26 12:00:31', '2019-06-03 13:54:54', b'0'); INSERT INTO `resource` VALUES (51, 1, -1, '短信ss', 0, '', 'user', '', '2019-05-26 12:00:31', '2019-06-03 13:54:54', b'0');
INSERT INTO `resource` VALUES (52, 1, 1, '短信签名', 51, '/sms/sign-list', 'user', '', '2019-05-26 12:01:56', '2019-05-26 12:01:56', b'0'); INSERT INTO `resource` VALUES (52, 1, 1, '短信签名', 51, '/sms/sign-list', 'user', '', '2019-05-26 12:01:56', '2019-05-26 12:01:56', b'0');
INSERT INTO `resource` VALUES (53, 1, 2, '短信模板', 51, '/sms/template-list', 'user', '', '2019-05-26 12:02:19', '2019-05-26 12:02:18', b'0'); INSERT INTO `resource` VALUES (53, 1, 2, '短信模板', 51, '/sms/template-list', 'user', '', '2019-05-26 12:02:19', '2019-05-26 12:02:18', b'0');
INSERT INTO `resource` VALUES (54, 1, 3, '部门管理', 13, '/admin/dept-list', 'user', '', '2019-06-27 23:41:19', '2019-06-27 23:41:51', b'0');
INSERT INTO `resource` VALUES (55, 1, 4, '规格管理', 20, '/product/product-attr-list', null, null, '2019-08-14 23:59:38', '2019-08-14 23:59:38', b'0');
COMMIT; COMMIT;
-- ---------------------------- -- ----------------------------

View File

@ -139,6 +139,8 @@ CREATE TABLE `user_spu_collections` (
`spu_id` int(11) NOT NULL COMMENT '商品id', `spu_id` int(11) NOT NULL COMMENT '商品id',
`spu_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '商品名字', `spu_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '商品名字',
`spu_image` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '图片名字', `spu_image` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '图片名字',
`sell_point` varchar(50) NOT NULL DEFAULT '' COMMENT '卖点',
`price` int(11) DEFAULT NULL COMMENT '价格',
`create_time` datetime(0) NOT NULL COMMENT '创建时间', `create_time` datetime(0) NOT NULL COMMENT '创建时间',
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间', `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
`deleted` smallint(2) NOT NULL COMMENT '删除状态', `deleted` smallint(2) NOT NULL COMMENT '删除状态',

View File

@ -98,7 +98,7 @@
</div> </div>
<van-goods-action> <van-goods-action>
<van-goods-action-mini-btn icon="like-o" @click="onFavoriteClicked"> <van-goods-action-mini-btn icon="like-o" :class="{active:hasCollectionType === 1 }" @click="onFavoriteClicked">
收藏 收藏
</van-goods-action-mini-btn> </van-goods-action-mini-btn>
<van-goods-action-mini-btn icon="cart" :info="cartCount > 0 ? cartCount : undefined" @click="onClickCart"> <van-goods-action-mini-btn icon="cart" :info="cartCount > 0 ? cartCount : undefined" @click="onClickCart">
@ -208,6 +208,7 @@
calSkuPriceResult: { calSkuPriceResult: {
}, },
hasCollectionType:0
}; };
}, },
@ -291,6 +292,20 @@
} }
}); });
}, },
initHasUserSpuFavorite(spuId){
if (!checkLogin()) {
this.hasCollectionType = 0;
return;
}
//
hasUserSpuFavorite(spuId).then(data => {
let hasCollection = data;
// alert("==" + hasCollection);
if (hasCollection) {
this.hasCollectionType = 1;
}
});
},
onClickCart() { onClickCart() {
this.$router.push('/cart'); this.$router.push('/cart');
@ -327,11 +342,14 @@
// alert("hasCollectionType==" + hasCollectionType); // alert("hasCollectionType==" + hasCollectionType);
collectionSpu(id,hasCollectionType).then(data =>{ collectionSpu(id,hasCollectionType).then(data =>{
let v = data; let v = data;
if (hasCollectionType == 1 && v){ this.hasCollectionType = hasCollectionType;
alert("商品已收藏"); // if (hasCollectionType == 1 && v){
}else if (hasCollectionType == 2 && v){ // // alert("");
alert("商品已取消"); // this.hasCollectionType = hasCollectionType;
} // }else if (hasCollectionType == 2 && v){
// // alert("");
// this.hasCollectionType = hasCollectionType;
// }
}) })
}); });
@ -426,6 +444,7 @@
// attrValueMap // attrValueMap
this.attrValueMap.set(attr.attrValueId, attr.attrValueName); this.attrValueMap.set(attr.attrValueId, attr.attrValueName);
} }
} }
// debugger; // debugger;
this.vanSku = vanSku; this.vanSku = vanSku;
@ -435,6 +454,9 @@
this.initialSku.quantity = 1; this.initialSku.quantity = 1;
// sku // sku
this.doCalcSkuPrice(this.initialSku.id); this.doCalcSkuPrice(this.initialSku.id);
this.initHasUserSpuFavorite(id);
}); });
// //
if (checkLogin()) { if (checkLogin()) {
@ -448,6 +470,9 @@
<style lang="less"> <style lang="less">
.goods { .goods {
.active {
color: #f44;
}
padding-bottom: 50px; padding-bottom: 50px;
&-swipe { &-swipe {

View File

@ -8,9 +8,18 @@
> >
<div v-for="(item,index) in list" :key="index"> <div v-for="(item,index) in list" :key="index">
<van-swipe-cell :right-width="65" :on-close="onClose(item)"> <van-swipe-cell :right-width="65" :on-close="onClose(item)">
<product-card :product='item' /> <van-card
<span>{{item.spuName}}</span> :price="formatPrice(item.price)"
<span slot="right" >删除</span> :desc="item.sellPoint"
:title="item.spuName"
:thumb="item.spuImage"
@click="skipProductSpuInfo(item.spuId)"
>
</van-card>
<template slot="right">
<van-button square type="danger" text="删除"/>
</template>
</van-swipe-cell> </van-swipe-cell>
</div> </div>
</van-list> </van-list>
@ -19,7 +28,6 @@
<script> <script>
import {GetFavoritePage, DelFavorite} from "../../../api/user.js"; import {GetFavoritePage, DelFavorite} from "../../../api/user.js";
export default { export default {
data() { data() {
return { return {
@ -31,6 +39,13 @@ export default {
} }
}, },
methods: { methods: {
formatPrice(data) {
return (data / 100).toFixed(2);
},
skipProductSpuInfo(data) {
// return getProductSpuInfo(data);
this.$router.push('/product/'+data);
},
onClose(item) { onClose(item) {
return function (clickPosition, instance) { return function (clickPosition, instance) {
switch (clickPosition) { switch (clickPosition) {

View File

@ -37,6 +37,16 @@ public class ProductSpuCollectionMessage {
*/ */
private String spuImage; private String spuImage;
/**
* 卖点
*/
private String sellPoint;
/**
* 价格单位
*/
private Integer price;
/** /**
* 1 收藏 2 取消 * 1 收藏 2 取消
*/ */

View File

@ -72,7 +72,7 @@ public class ProductAttrServiceImpl implements ProductAttrService {
public ProductAttrPageBO getProductAttrPage(ProductAttrPageDTO productAttrPageDTO) { public ProductAttrPageBO getProductAttrPage(ProductAttrPageDTO productAttrPageDTO) {
ProductAttrPageBO productAttrPageBO = new ProductAttrPageBO(); ProductAttrPageBO productAttrPageBO = new ProductAttrPageBO();
// 查询分页数据 // 查询分页数据
int offset = productAttrPageDTO.getPageNo() * productAttrPageDTO.getPageSize(); int offset = (productAttrPageDTO.getPageNo()-1) * productAttrPageDTO.getPageSize();
productAttrPageBO.setAttrs(ProductAttrConvert.INSTANCE.convert(productAttrMapper.selectListByNameLike(productAttrPageDTO.getName(), productAttrPageBO.setAttrs(ProductAttrConvert.INSTANCE.convert(productAttrMapper.selectListByNameLike(productAttrPageDTO.getName(),
offset, productAttrPageDTO.getPageSize()))); offset, productAttrPageDTO.getPageSize())));
// 查询分页总数 // 查询分页总数

View File

@ -6,11 +6,14 @@ import cn.iocoder.mall.product.api.constant.ProductErrorCodeEnum;
import cn.iocoder.mall.product.api.message.ProductSpuCollectionMessage; import cn.iocoder.mall.product.api.message.ProductSpuCollectionMessage;
import cn.iocoder.mall.product.dao.ProductSpuMapper; import cn.iocoder.mall.product.dao.ProductSpuMapper;
import cn.iocoder.mall.product.dataobject.ProductSpuDO; import cn.iocoder.mall.product.dataobject.ProductSpuDO;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.List;
/** /**
* ProductSpuCollectionServiceImpl * ProductSpuCollectionServiceImpl
@ -47,9 +50,14 @@ public class ProductSpuCollectionServiceImpl implements ProductSpuCollectionServ
*/ */
private void sendProductSpuCollectionMessage(final ProductSpuDO productSpuDO, final Integer hasCollectionType, private void sendProductSpuCollectionMessage(final ProductSpuDO productSpuDO, final Integer hasCollectionType,
final Integer userId) { final Integer userId) {
List<String> result = Lists.newArrayList(Splitter.on(",").omitEmptyStrings().trimResults().split(productSpuDO.getPicUrls()));
ProductSpuCollectionMessage productSpuCollectionMessage = new ProductSpuCollectionMessage() ProductSpuCollectionMessage productSpuCollectionMessage = new ProductSpuCollectionMessage()
.setSpuId(productSpuDO.getId()).setSpuName(productSpuDO.getName()) .setSpuId(productSpuDO.getId())
.setSpuImage(productSpuDO.getPicUrls()).setHasCollectionType(hasCollectionType) .setSpuName(productSpuDO.getName())
.setSpuImage(result.size() > 0 ? result.get(0) : "")
.setSellPoint(productSpuDO.getSellPoint())
.setPrice(productSpuDO.getPrice())
.setHasCollectionType(hasCollectionType)
.setUserId(userId); .setUserId(userId);
rocketMQTemplate.convertAndSend(ProductSpuCollectionMessage.TOPIC, productSpuCollectionMessage); rocketMQTemplate.convertAndSend(ProductSpuCollectionMessage.TOPIC, productSpuCollectionMessage);
} }

View File

@ -5,8 +5,10 @@ import cn.iocoder.common.framework.util.CollectionUtil;
import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.common.framework.vo.PageResult; import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.admin.api.AdminService; import cn.iocoder.mall.admin.api.AdminService;
import cn.iocoder.mall.admin.api.DeptmentService;
import cn.iocoder.mall.admin.api.ResourceService; import cn.iocoder.mall.admin.api.ResourceService;
import cn.iocoder.mall.admin.api.RoleService; import cn.iocoder.mall.admin.api.RoleService;
import cn.iocoder.mall.admin.api.bo.deptment.DeptmentBO;
import cn.iocoder.mall.admin.api.bo.resource.ResourceBO; import cn.iocoder.mall.admin.api.bo.resource.ResourceBO;
import cn.iocoder.mall.admin.api.bo.role.RoleBO; import cn.iocoder.mall.admin.api.bo.role.RoleBO;
import cn.iocoder.mall.admin.api.bo.admin.AdminBO; import cn.iocoder.mall.admin.api.bo.admin.AdminBO;
@ -23,6 +25,7 @@ import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.*; import java.util.*;
@ -44,6 +47,9 @@ public class AdminController {
@Reference(validation = "true", version = "${dubbo.provider.RoleService.version}") @Reference(validation = "true", version = "${dubbo.provider.RoleService.version}")
private RoleService roleService; private RoleService roleService;
@Autowired
private DeptmentService deptmentService;
// =========== 当前管理员相关的资源 API =========== // =========== 当前管理员相关的资源 API ===========
// TODO 功能当前管理员 // TODO 功能当前管理员
@ -85,7 +91,7 @@ public class AdminController {
} }
// =========== 管理员管理 API =========== // =========== 管理员管理 API ===========
//TODO 目前需要增加搜索所有子部门的用户
@GetMapping("/page") @GetMapping("/page")
@RequiresPermissions("system.admin.page") @RequiresPermissions("system.admin.page")
@ApiOperation(value = "管理员分页") @ApiOperation(value = "管理员分页")
@ -97,7 +103,17 @@ public class AdminController {
// 查询角色数组 // 查询角色数组
Map<Integer, Collection<RoleBO>> roleMap = adminService.getAdminRolesMap(CollectionUtil.convertList(resultPage.getList(), AdminBO::getId)); Map<Integer, Collection<RoleBO>> roleMap = adminService.getAdminRolesMap(CollectionUtil.convertList(resultPage.getList(), AdminBO::getId));
resultPage.getList().forEach(admin -> admin.setRoles(AdminConvert.INSTANCE.convertAdminVORoleList(roleMap.get(admin.getId())))); resultPage.getList().forEach(admin -> admin.setRoles(AdminConvert.INSTANCE.convertAdminVORoleList(roleMap.get(admin.getId()))));
// 查询对应部门
List<DeptmentBO> deptmentBOS = deptmentService.getAllDeptments();
Map<Integer, String> deptNameMap = deptmentBOS.stream().collect(Collectors.toMap(d->d.getId(), d->d.getName()));
//管理员所在部门被删后变成未分配状态
deptNameMap.put(0, "未分配");
resultPage.getList().forEach(admin->{
admin.setDeptment(new AdminVO.Deptment(admin.getDeptmentId(), deptNameMap.get(admin.getDeptmentId())));
});
} }
return success(resultPage); return success(resultPage);
} }

View File

@ -49,7 +49,7 @@ public class DeptmentController {
public CommonResult<List<DeptmentVO>> treeAll(){ public CommonResult<List<DeptmentVO>> treeAll(){
List<DeptmentBO> list = deptmentService.getAllDeptments(); List<DeptmentBO> list = deptmentService.getAllDeptments();
List<DeptmentVO> voList = DeptmentConvert.INSTANCE.convert(list); List<DeptmentVO> voList = DeptmentConvert.INSTANCE.convert(list);
Map<Integer, DeptmentVO> nodeMap = calaNodeMap(voList); Map<Integer, DeptmentVO> nodeMap = calcNodeMap(voList);
// 获得到所有的根节点 // 获得到所有的根节点
List<DeptmentVO> rootNodes = nodeMap.values().stream() List<DeptmentVO> rootNodes = nodeMap.values().stream()
.filter(node -> node.getPid().equals(ResourceConstants.PID_ROOT)) .filter(node -> node.getPid().equals(ResourceConstants.PID_ROOT))
@ -64,7 +64,7 @@ public class DeptmentController {
PageResult<DeptmentVO> voPageResult = DeptmentConvert.INSTANCE.convert(pageResult); PageResult<DeptmentVO> voPageResult = DeptmentConvert.INSTANCE.convert(pageResult);
List<DeptmentBO> list = deptmentService.getAllDeptments(); List<DeptmentBO> list = deptmentService.getAllDeptments();
List<DeptmentVO> voList = DeptmentConvert.INSTANCE.convert(list); List<DeptmentVO> voList = DeptmentConvert.INSTANCE.convert(list);
Map<Integer, DeptmentVO> nodeMap = calaNodeMap(voList); Map<Integer, DeptmentVO> nodeMap = calcNodeMap(voList);
voPageResult.getList().forEach(d->{ voPageResult.getList().forEach(d->{
d.setChildren(nodeMap.get(d.getId()).getChildren()); d.setChildren(nodeMap.get(d.getId()).getChildren());
}); });
@ -97,7 +97,7 @@ public class DeptmentController {
)); ));
} }
private Map<Integer, DeptmentVO> calaNodeMap(List<DeptmentVO> voList){ private Map<Integer, DeptmentVO> calcNodeMap(List<DeptmentVO> voList){
Map<Integer, DeptmentVO> nodeMap = voList.stream().collect(Collectors.toMap(e->e.getId(), e->e)); Map<Integer, DeptmentVO> nodeMap = voList.stream().collect(Collectors.toMap(e->e.getId(), e->e));
nodeMap.values().stream() nodeMap.values().stream()

View File

@ -3,6 +3,7 @@ package cn.iocoder.mall.admin.application.vo.admin;
import cn.iocoder.mall.admin.api.bo.admin.AdminBO; import cn.iocoder.mall.admin.api.bo.admin.AdminBO;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
@ -15,6 +16,8 @@ public class AdminVO extends AdminBO {
private List<Role> roles; private List<Role> roles;
private Deptment deptment;
@ApiModel("管理员 VO - 角色") @ApiModel("管理员 VO - 角色")
@Data @Data
@Accessors(chain = true) @Accessors(chain = true)
@ -28,4 +31,19 @@ public class AdminVO extends AdminBO {
} }
@ApiModel("管理员 VO - 部门")
@Data
@Accessors(chain = true)
@AllArgsConstructor
public static class Deptment {
@ApiModelProperty(value = "部门编号", required = true, example = "1")
private Integer id;
@ApiModelProperty(value = "部门名称", required = true, example = "研发部")
private String name;
}
} }

View File

@ -28,4 +28,7 @@ public class AdminBO implements Serializable {
@ApiModelProperty(value = "创建时间", required = true, example = "时间戳格式") @ApiModelProperty(value = "创建时间", required = true, example = "时间戳格式")
private Date createTime; private Date createTime;
@ApiModelProperty(value = "部门ID", required = true, example = "1")
private Integer deptmentId;
} }

View File

@ -7,6 +7,7 @@ import lombok.experimental.Accessors;
import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern; import javax.validation.constraints.Pattern;
import java.io.Serializable; import java.io.Serializable;
@ -31,4 +32,8 @@ public class AdminAddDTO implements Serializable {
@Length(min = 4, max = 16, message = "密码长度为 4-16 位") @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
private String password; private String password;
@ApiModelProperty(value = "部门ID", required = true, example = "1")
@NotNull(message = "部门不能为空")
private Integer deptmentId;
} }

View File

@ -14,4 +14,8 @@ public class AdminPageDTO extends PageParam {
@ApiModelProperty(value = "昵称,模糊匹配", example = "小王") @ApiModelProperty(value = "昵称,模糊匹配", example = "小王")
private String nickname; private String nickname;
@ApiModelProperty(value = "所在部门ID")
private Integer deptmentId;
} }

View File

@ -35,4 +35,8 @@ public class AdminUpdateDTO implements Serializable {
@Length(min = 4, max = 16, message = "密码长度为 4-16 位") @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
private String password; private String password;
@ApiModelProperty(value = "部门ID", required = true, example = "1")
@NotNull(message = "部门不能为空")
private Integer deptmentId;
} }

View File

@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import org.omg.PortableInterceptor.INACTIVE;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
@Repository @Repository
@ -19,7 +20,14 @@ public interface AdminMapper extends BaseMapper<AdminDO> {
default IPage<AdminDO> selectPage(AdminPageDTO adminPageDTO) { default IPage<AdminDO> selectPage(AdminPageDTO adminPageDTO) {
return selectPage(new Page<>(adminPageDTO.getPageNo(), adminPageDTO.getPageSize()), return selectPage(new Page<>(adminPageDTO.getPageNo(), adminPageDTO.getPageSize()),
new QueryWrapperX<AdminDO>().likeIfPresent("nickname", adminPageDTO.getNickname())); new QueryWrapperX<AdminDO>().likeIfPresent("nickname", adminPageDTO.getNickname())
.eqIfPresent("deptment_id", adminPageDTO.getDeptmentId()));
}
default int updateDeptByDeptId(@Param("fromDeptId")Integer fromDeptId, @Param("toDeptId")Integer toDeptId){
QueryWrapper<AdminDO> query = new QueryWrapper<AdminDO>()
.eq("deptment_id", fromDeptId);
return update(new AdminDO().setDeptmentId(toDeptId), query);
} }
} }

View File

@ -36,6 +36,12 @@ public class AdminDO extends DeletableDO {
*/ */
private Integer status; private Integer status;
/**
* 管理员部门id
*/
private Integer deptmentId;
// TODO 芋艿最后登陆时间最后登陆 IP // TODO 芋艿最后登陆时间最后登陆 IP
// TODO 芋艿登陆日志 // TODO 芋艿登陆日志

View File

@ -11,6 +11,7 @@ import cn.iocoder.mall.admin.api.dto.depetment.DeptmentAddDTO;
import cn.iocoder.mall.admin.api.dto.depetment.DeptmentPageDTO; import cn.iocoder.mall.admin.api.dto.depetment.DeptmentPageDTO;
import cn.iocoder.mall.admin.api.dto.depetment.DeptmentUpdateDTO; import cn.iocoder.mall.admin.api.dto.depetment.DeptmentUpdateDTO;
import cn.iocoder.mall.admin.convert.DeptmentConvert; import cn.iocoder.mall.admin.convert.DeptmentConvert;
import cn.iocoder.mall.admin.dao.AdminMapper;
import cn.iocoder.mall.admin.dao.DeptmentMapper; import cn.iocoder.mall.admin.dao.DeptmentMapper;
import cn.iocoder.mall.admin.dao.DeptmentRoleMapper; import cn.iocoder.mall.admin.dao.DeptmentRoleMapper;
import cn.iocoder.mall.admin.dataobject.DeptmentDO; import cn.iocoder.mall.admin.dataobject.DeptmentDO;
@ -39,6 +40,9 @@ public class DeptmentServiceImpl implements DeptmentService {
@Autowired @Autowired
private DeptmentRoleMapper deptmentRoleMapper; private DeptmentRoleMapper deptmentRoleMapper;
@Autowired
private AdminMapper adminMapper;
@Override @Override
public DeptmentBO addDeptment(Integer adminId, DeptmentAddDTO deptmentAddDTO) { public DeptmentBO addDeptment(Integer adminId, DeptmentAddDTO deptmentAddDTO) {
if (deptmentAddDTO.getPid() != 0 && if (deptmentAddDTO.getPid() != 0 &&
@ -69,6 +73,8 @@ public class DeptmentServiceImpl implements DeptmentService {
deptmentRoleMapper.deleteByDeptmentId(deptmentId); deptmentRoleMapper.deleteByDeptmentId(deptmentId);
//将改部门下所有员工的DeptmentID设置为0
adminMapper.updateDeptByDeptId(deptmentId, 0);
return true; return true;
} }

View File

@ -45,6 +45,16 @@ public class UserProductSpuCollectionsBO implements Serializable {
*/ */
private String spuImage; private String spuImage;
/**
* 卖点
*/
private String sellPoint;
/**
* 价格单位
*/
private Integer price;
/** /**
* 创建时间 * 创建时间
*/ */

View File

@ -46,6 +46,16 @@ public class UserProductSpuCollectionsAddDTO implements Serializable {
*/ */
private String spuImage; private String spuImage;
/**
* 卖点
*/
private String sellPoint;
/**
* 价格单位
*/
private Integer price;
/** /**
* 创建时间 * 创建时间
*/ */

View File

@ -50,6 +50,16 @@ public class UserProductSpuCollectionsDO implements Serializable {
*/ */
private String spuImage; private String spuImage;
/**
* 卖点
*/
private String sellPoint;
/**
* 价格单位
*/
private Integer price;
/** /**
* 创建时间 * 创建时间
*/ */

View File

@ -11,6 +11,8 @@ import cn.iocoder.mall.user.api.dto.UserProductSpuCollectionsUpdateDTO;
import cn.iocoder.mall.user.biz.convert.UserProductSpuCollectionsConvert; import cn.iocoder.mall.user.biz.convert.UserProductSpuCollectionsConvert;
import cn.iocoder.mall.user.biz.dao.UserProductSpuCollectionsMapper; import cn.iocoder.mall.user.biz.dao.UserProductSpuCollectionsMapper;
import cn.iocoder.mall.user.biz.dataobject.UserProductSpuCollectionsDO; import cn.iocoder.mall.user.biz.dataobject.UserProductSpuCollectionsDO;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
@ -33,6 +35,10 @@ public class UserProductSpuCollectionsServiceImpl implements UserProductSpuColle
@Autowired @Autowired
private UserProductSpuCollectionsMapper userProductSpuCollectionsMapper; private UserProductSpuCollectionsMapper userProductSpuCollectionsMapper;
// TODO 暂时先使用冗余字段有需要在对接实时数据查询
// @Reference(validation = "true", version = "${dubbo.consumer.PromotionActivityService.version}")
// private ProductSpuService productSpuService;
@Override @Override
public int addUserSkuCollections(UserProductSpuCollectionsAddDTO userProductSpuCollectionsAddDTO) { public int addUserSkuCollections(UserProductSpuCollectionsAddDTO userProductSpuCollectionsAddDTO) {
@ -72,6 +78,11 @@ public class UserProductSpuCollectionsServiceImpl implements UserProductSpuColle
return CommonResult.success( return CommonResult.success(
new UserProductSpuCollectionsPageBO().setList(Collections.emptyList()).setTotal(totalCount)); new UserProductSpuCollectionsPageBO().setList(Collections.emptyList()).setTotal(totalCount));
} }
for (UserProductSpuCollectionsDO userProductSpuCollectionsDO : list
) {
List<String> result = Lists.newArrayList(Splitter.on(",").omitEmptyStrings().trimResults().split(userProductSpuCollectionsDO.getSpuImage()));
userProductSpuCollectionsDO.setSpuImage(result.size() > 0 ? result.get(0) : "");
}
UserProductSpuCollectionsPageBO userProductSpuCollectionsPageBO = new UserProductSpuCollectionsPageBO(); UserProductSpuCollectionsPageBO userProductSpuCollectionsPageBO = new UserProductSpuCollectionsPageBO();
userProductSpuCollectionsPageBO.setList(UserProductSpuCollectionsConvert.INSTANCE.convert(list)); userProductSpuCollectionsPageBO.setList(UserProductSpuCollectionsConvert.INSTANCE.convert(list));

View File

@ -4,7 +4,7 @@
<sql id="FIELDS"> <sql id="FIELDS">
id, user_id, nickname, spu_id, spu_name, id, user_id, nickname, spu_id, spu_name,
spu_image, create_time, update_time, spu_image,sell_point,price, create_time, update_time,
deleted deleted
</sql> </sql>