From 853d9fc2f6173f31528e889dc33c173142665894 Mon Sep 17 00:00:00 2001 From: YunaiV <> Date: Fri, 15 Mar 2019 21:22:43 +0800 Subject: [PATCH] =?UTF-8?q?=E5=89=8D=E7=AB=AF=EF=BC=9A=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=95=86=E5=93=81=E5=88=86=E7=B1=BB=E7=9A=84=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin-web/config/router.config.js | 18 + admin-web/mock/geographic/admin-menu.json | 25 ++ .../src/models/product/productCategoryList.js | 66 ++++ .../src/pages/Product/ProductCategoryList.js | 269 ++++++++++++++ .../pages/Product/ProductCategoryList.less | 15 + admin-web/src/pages/Product/ProductSpuList.js | 350 ++++++++++++++++++ .../src/pages/Product/ProductSpuList.less | 15 + admin-web/src/services/product.js | 30 ++ 8 files changed, 788 insertions(+) create mode 100644 admin-web/src/models/product/productCategoryList.js create mode 100644 admin-web/src/pages/Product/ProductCategoryList.js create mode 100644 admin-web/src/pages/Product/ProductCategoryList.less create mode 100644 admin-web/src/pages/Product/ProductSpuList.js create mode 100644 admin-web/src/pages/Product/ProductSpuList.less create mode 100644 admin-web/src/services/product.js diff --git a/admin-web/config/router.config.js b/admin-web/config/router.config.js index f3b95b91f..99875d14e 100644 --- a/admin-web/config/router.config.js +++ b/admin-web/config/router.config.js @@ -58,6 +58,24 @@ export default [ }, ], }, + // product + { + path: '/product', + name: 'product', + icon: 'product', + routes: [ + { + path: '/product/product-spu-list', + name: 'product-spu-list', + component: './Product/ProductSpuList', + }, + { + path: '/product/product-category-list', + name: 'product-category-list', + component: './Product/ProductCategoryList', + }, + ] + }, { path: '/dashboard', name: 'dashboard', diff --git a/admin-web/mock/geographic/admin-menu.json b/admin-web/mock/geographic/admin-menu.json index 26d06edba..bd64da112 100644 --- a/admin-web/mock/geographic/admin-menu.json +++ b/admin-web/mock/geographic/admin-menu.json @@ -2,6 +2,31 @@ "code": 0, "message": "", "data": [ + { + "id": 20, + "handler": null, + "pid": 0, + "sort": 0, + "displayName": "商品管理", + "children": [ + { + "id": 21, + "handler": "/product/product-spu-list", + "pid": 20, + "sort": 1, + "displayName": "商品管理", + "children": null + }, + { + "id": 22, + "handler": "/product/product-category-list", + "pid": 20, + "sort": 2, + "displayName": "商品分类", + "children": null + } + ] + }, { "id": 13, "handler": null, diff --git a/admin-web/src/models/product/productCategoryList.js b/admin-web/src/models/product/productCategoryList.js new file mode 100644 index 000000000..a6850bf86 --- /dev/null +++ b/admin-web/src/models/product/productCategoryList.js @@ -0,0 +1,66 @@ +import { message } from 'antd'; +import { addResource, updateResource, deleteResource, resourceTree } from '../../services/admin'; +import { productCategoryTree } from '../../services/product'; + +export default { + namespace: 'productCategoryList', + + state: { + list: [], + }, + + effects: { + *add({ payload }, { call, put }) { + const { callback, body } = payload; + const response = yield call(addResource, body); + if (callback) { + callback(response); + } + yield put({ + type: 'tree', + payload: {}, + }); + }, + *update({ payload }, { call, put }) { + const { callback, body } = payload; + const response = yield call(updateResource, body); + if (callback) { + callback(response); + } + yield put({ + type: 'tree', + payload: {}, + }); + }, + *delete({ payload }, { call, put }) { + const response = yield call(deleteResource, payload); + message.info('删除成功!'); + yield put({ + type: 'treeSuccess', + payload: { + list: response.data, + }, + }); + }, + *tree({ payload }, { call, put }) { + const { queryParams } = payload; + const response = yield call(productCategoryTree, queryParams); + message.info('查询成功!'); + yield put({ + type: 'treeSuccess', + payload: { + list: response.data, + }, + }); + }, + }, + + reducers: { + treeSuccess(state, { payload }) { + return { + ...state, + ...payload, + }; + }, + }, +}; diff --git a/admin-web/src/pages/Product/ProductCategoryList.js b/admin-web/src/pages/Product/ProductCategoryList.js new file mode 100644 index 000000000..bcace4d86 --- /dev/null +++ b/admin-web/src/pages/Product/ProductCategoryList.js @@ -0,0 +1,269 @@ +/* eslint-disable */ + +import React, { PureComponent, Fragment } from 'react'; +import { connect } from 'dva'; +import moment from 'moment'; +import { Card, Form, Input, Select, Button, Modal, message, Table, Divider } from 'antd'; +import PageHeaderWrapper from '@/components/PageHeaderWrapper'; + +import styles from './ProductCategoryList.less'; + +const FormItem = Form.Item; +const { Option } = Select; +const status = ['未知', '开启', '禁用']; + +// 添加 form 表单 +const CreateForm = Form.create()(props => { + const { modalVisible, form, handleAdd, handleModalVisible, modalType, initValues } = props; + + const okHandle = () => { + form.validateFields((err, fieldsValue) => { + if (err) return; + form.resetFields(); + handleAdd({ + fields: fieldsValue, + modalType, + initValues, + }); + }); + }; + + const selectStyle = { + width: 200, + }; + + const title = modalType === 'add' ? '添加一个 Resource' : '更新一个 Resource'; + const okText = modalType === 'add' ? '添加' : '更新'; + return ( + handleModalVisible()} + > + + {form.getFieldDecorator('displayName', { + rules: [{ required: true, message: '请输入菜单展示名字!', min: 2 }], + initialValue: initValues.displayName, + })()} + + + {form.getFieldDecorator('handler', { + initialValue: initValues.handler, + })()} + + + {form.getFieldDecorator('name', { + rules: [{ required: true, message: '请输入资源名字!' }], + initialValue: initValues.name, + })()} + + + {form.getFieldDecorator('pid', { + rules: [{ required: true, message: '请输入父级编号!' }], + initialValue: initValues.pid, + })()} + 根节点为 0 + + + {form.getFieldDecorator('sort', { + rules: [{ required: true, message: '请输入菜单排序!' }], + initialValue: initValues.sort, + })()} + + + {form.getFieldDecorator('type', { + rules: [{ required: true, message: '请选择资源类型!' }], + initialValue: 1, + })( + + )} + + + ); +}); + +@connect(({ productCategoryList, loading }) => ({ + productCategoryList, + list: productCategoryList.list, + loading: loading.models.productCategoryList, +})) +@Form.create() +class ProductCategoryList extends PureComponent { + state = { + modalVisible: false, + modalType: 'add', //add update + initValues: {}, + }; + + componentDidMount() { + const { dispatch } = this.props; + dispatch({ + type: 'productCategoryList/tree', + payload: {}, + }); + } + + handleModalVisible = (flag, modalType, initValues) => { + this.setState({ + modalVisible: !!flag, + initValues: initValues || {}, + modalType: modalType || 'add', + }); + }; + + handleAdd = ({ fields, modalType, initValues }) => { + const { dispatch } = this.props; + if (modalType === 'add') { + dispatch({ + type: 'productCategoryList/add', + payload: { + body: { + ...fields, + }, + callback: () => { + message.success('添加成功'); + this.handleModalVisible(); + }, + }, + }); + } else { + dispatch({ + type: 'productCategoryList/update', + payload: { + body: { + ...initValues, + ...fields, + }, + callback: () => { + message.success('更新成功'); + this.handleModalVisible(); + }, + }, + }); + } + }; + + handleDelete(row) { + const { dispatch } = this.props; + Modal.confirm({ + title: `确认删除?`, + content: `${row.displayName}`, + onOk() { + dispatch({ + type: 'productCategoryList/delete', + payload: { + id: row.id, + }, + }); + }, + onCancel() {}, + }); + } + + render() { + const { list } = this.props; + const { modalVisible, modalType, initValues } = this.state; + const parentMethods = { + handleAdd: this.handleAdd, + handleModalVisible: this.handleModalVisible, + modalType, + initValues, + }; + const that = this; + + const columns = [ + { + title: 'id', + dataIndex: 'id', + render: text => {text}, + }, + { + title: '分类名', + dataIndex: 'name', + }, + { + title: '分类图片', + dataIndex: 'picUrl', + render(val) { + return ; + }, + }, + { + title: '排序值', + dataIndex: 'sort', + render: sort => {sort}, + }, + { + title: '创建时间', + dataIndex: 'createTime', + render: val => {moment(val).format('YYYY-MM-DD')}, + }, + { + title: '状态', + dataIndex: 'status', + render(val) { + return {status[val]}; + }, + }, + { + title: '描述', + dataIndex: 'description', + }, + { + title: '操作', + render: (text, record) => { + const statusText = record.status === 1 ? '禁用' : '开启'; + return ( + + this.handleModalVisible(true, 'update', record)}>更新 + + + this.handleStatus(record)}> + {statusText} + + + { + record.status === 2 ? ( + + + this.handleDelete(record)}> + 删除 + + + ) : '' + } + + ); + } + }, + ]; + + return ( + + +
+
+ +
+
+ + + + + ); + } +} + +export default ProductCategoryList; diff --git a/admin-web/src/pages/Product/ProductCategoryList.less b/admin-web/src/pages/Product/ProductCategoryList.less new file mode 100644 index 000000000..ebb45c292 --- /dev/null +++ b/admin-web/src/pages/Product/ProductCategoryList.less @@ -0,0 +1,15 @@ +@import '~antd/lib/style/themes/default.less'; +@import '~@/utils/utils.less'; + +.tableList { + .tableListOperator { + margin-bottom: 16px; + button { + margin-right: 8px; + } + } +} + +.tableDelete { + color: red; +} diff --git a/admin-web/src/pages/Product/ProductSpuList.js b/admin-web/src/pages/Product/ProductSpuList.js new file mode 100644 index 000000000..6adf813c8 --- /dev/null +++ b/admin-web/src/pages/Product/ProductSpuList.js @@ -0,0 +1,350 @@ +/* eslint-disable */ + +import React, { PureComponent, Fragment } from 'react'; +import { connect } from 'dva'; +import moment from 'moment'; +import { Card, Form, Input, Spin, Button, Modal, message, Table, Divider, Tree } from 'antd'; +import PageHeaderWrapper from '@/components/PageHeaderWrapper'; + +import styles from './ProductSpuList.less'; + +const FormItem = Form.Item; +const { TreeNode } = Tree; + +// 添加 form 表单 +const CreateForm = Form.create()(props => { + const { modalVisible, form, handleAdd, handleModalVisible, modalType, initValues } = props; + + const okHandle = () => { + form.validateFields((err, fieldsValue) => { + if (err) return; + form.resetFields(); + handleAdd({ + fields: fieldsValue, + modalType, + initValues, + }); + }); + }; + + const title = modalType === 'add' ? '添加一个 Role' : '更新一个 Role'; + const okText = modalType === 'add' ? '添加' : '更新'; + return ( + handleModalVisible()} + > + + {form.getFieldDecorator('name', { + rules: [{ required: true, message: '请输入角色名!', min: 2 }], + initialValue: initValues.name, + })()} + + + ); +}); + +// 角色分配 +const AssignModal = Form.create()(props => { + const { + modalVisible, + form, + handleOk, + handleModalVisible, + treeData, + checkedKeys, + loading, + handleCheckBoxClick, + } = props; + + const renderTreeNodes = data => { + return data.map(item => { + if (item.children) { + return ( + + {renderTreeNodes(item.children)} + + ); + } + return ; + }); + }; + + const renderModalContent = treeData => { + const RenderTreeNodes = renderTreeNodes(treeData); + if (RenderTreeNodes) { + return ( + + {form.getFieldDecorator('name', {})( + + {renderTreeNodes(treeData)} + + )} + + ); + } else { + return null; + } + }; + + const okHandle = () => { + form.validateFields((err, fieldsValue) => { + if (err) return; + form.resetFields(); + handleOk({ + fields: fieldsValue, + }); + }); + }; + + return ( + handleModalVisible()} + > + {renderModalContent(treeData)} + + ); +}); + +// roleList +@connect(({ roleList, loading }) => ({ + roleList, + list: roleList.list, + data: roleList, + loading: loading.models.resourceList, +})) +@Form.create() +class RoleList extends PureComponent { + state = { + modalVisible: false, + modalType: 'add', //add update + initValues: {}, + roleAssignVisible: false, + roleAssignRecord: {}, + }; + + componentDidMount() { + const { dispatch } = this.props; + dispatch({ + type: 'roleList/query', + payload: { + name: '', + pageNo: 0, + pageSize: 10, + }, + }); + } + + handleModalVisible = (flag, modalType, initValues) => { + this.setState({ + modalVisible: !!flag, + initValues: initValues || {}, + modalType: modalType || 'add', + }); + }; + + handleAssignModalVisible = (flag, record) => { + const { dispatch } = this.props; + dispatch({ + type: 'roleList/queryRoleAssign', + payload: { + id: record.id, + }, + }); + this.setState({ + roleAssignVisible: !!flag, + roleAssignRecord: record, + }); + }; + + handleAssignModalVisibleClose(flag) { + this.setState({ + roleAssignVisible: !!flag, + }); + } + + handleAssignCheckBoxClick = checkedKeys => { + const { dispatch } = this.props; + const newCheckedKeys = checkedKeys.map(item => { + return parseInt(item); + }); + dispatch({ + type: 'roleList/changeCheckedKeys', + payload: newCheckedKeys, + }); + }; + + handleAssignOK = () => { + const { dispatch, data } = this.props; + const { roleAssignRecord } = this.state; + dispatch({ + type: 'roleList/roleAssignResource', + payload: { + id: roleAssignRecord.id, + resourceIds: data.checkedKeys, + roleTreeData: data.roleTreeData, + }, + }); + this.handleAssignModalVisibleClose(false); + }; + + handleAdd = ({ fields, modalType, initValues }) => { + const { dispatch, data } = this.props; + const queryParams = { + pageNo: data.pageNo, + pageSize: data.pageSize, + }; + if (modalType === 'add') { + dispatch({ + type: 'roleList/add', + payload: { + body: { + ...fields, + }, + queryParams, + callback: () => { + message.success('添加成功'); + this.handleModalVisible(); + }, + }, + }); + } else { + dispatch({ + type: 'roleList/update', + payload: { + body: { + ...initValues, + ...fields, + }, + queryParams, + callback: () => { + message.success('更新成功'); + this.handleModalVisible(); + }, + }, + }); + } + }; + + handleDelete(row) { + const { dispatch, data } = this.props; + const queryParams = { + pageNo: data.pageNo, + pageSize: data.pageSize, + }; + Modal.confirm({ + title: `确认删除?`, + content: `${row.name}`, + onOk() { + dispatch({ + type: 'roleList/delete', + payload: { + body: { + id: row.id, + }, + queryParams, + }, + }); + }, + onCancel() {}, + }); + } + + render() { + const { list, data } = this.props; + + const { pageNo, pageSize, count, roleTreeData, checkedKeys, assignModalLoading } = data; + const { modalVisible, modalType, initValues, roleAssignVisible } = this.state; + + const parentMethods = { + handleAdd: this.handleAdd, + handleModalVisible: this.handleModalVisible, + modalType, + initValues, + }; + + const columns = [ + { + title: 'id', + dataIndex: 'id', + render: text => {text}, + }, + { + title: '名称', + dataIndex: 'name', + }, + { + title: '创建时间', + dataIndex: 'createTime', + sorter: true, + render: val => {moment(val).format('YYYY-MM-DD')}, + }, + { + title: '操作', + width: 200, + render: (text, record) => ( + + this.handleModalVisible(true, 'update', record)}>更新 + + this.handleAssignModalVisible(true, record)}>分配权限 + + this.handleDelete(record)}> + 删除 + + + ), + }, + ]; + + const paginationProps = { + current: pageNo, + pageSize: pageSize, + total: count, + }; + + return ( + + +
+
+ +
+
+
+ + + this.handleAssignModalVisibleClose(false)} + /> + + ); + } +} + +export default RoleList; diff --git a/admin-web/src/pages/Product/ProductSpuList.less b/admin-web/src/pages/Product/ProductSpuList.less new file mode 100644 index 000000000..ebb45c292 --- /dev/null +++ b/admin-web/src/pages/Product/ProductSpuList.less @@ -0,0 +1,15 @@ +@import '~antd/lib/style/themes/default.less'; +@import '~@/utils/utils.less'; + +.tableList { + .tableListOperator { + margin-bottom: 16px; + button { + margin-right: 8px; + } + } +} + +.tableDelete { + color: red; +} diff --git a/admin-web/src/services/product.js b/admin-web/src/services/product.js new file mode 100644 index 000000000..50a91d4d7 --- /dev/null +++ b/admin-web/src/services/product.js @@ -0,0 +1,30 @@ +import { stringify } from '@/utils/request.qs'; +import request from '@/utils/request'; + +// dictionary + +export async function productCategoryTree(params) { + return request(`/product-api/admins/category/tree?${stringify(params)}`, { + method: 'GET', + }); +} + +export async function dictionaryAdd(params) { + return request(`/admin-api/admins/data_dict/add?${stringify(params)}`, { + method: 'POST', + body: {}, + }); +} + +export async function dictionaryUpdate(params) { + return request(`/admin-api/admins/data_dict/update?${stringify(params)}`, { + method: 'POST', + body: {}, + }); +} + +export async function dictionaryDelete(params) { + return request(`/admin-api/admins/data_dict/delete?${stringify(params)}`, { + method: 'POST', + }); +} \ No newline at end of file