浏览代码

学校资料/班级组织

zhuth 6 年之前
父节点
当前提交
35162ab983
共有 39 个文件被更改,包括 1758 次插入171 次删除
  1. 2 1
      frontend/pc-web/app.json
  2. 25 0
      frontend/pc-web/app/model/Class.js
  3. 30 0
      frontend/pc-web/app/model/Grade.js
  4. 61 0
      frontend/pc-web/app/view/basic/ClassDetail.js
  5. 86 0
      frontend/pc-web/app/view/basic/ClassInfo.js
  6. 170 0
      frontend/pc-web/app/view/basic/ClassInfoController.js
  7. 26 0
      frontend/pc-web/app/view/basic/ClassInfoModel.js
  8. 85 0
      frontend/pc-web/app/view/basic/ListCard.js
  9. 33 0
      frontend/pc-web/app/view/basic/ListCard.scss
  10. 31 0
      frontend/pc-web/app/view/basic/ListCardController.js
  11. 74 0
      frontend/pc-web/app/view/basic/SchoolInfo.js
  12. 64 0
      frontend/pc-web/app/view/core/chart/ChartBase.js
  13. 44 0
      frontend/pc-web/app/view/core/chart/ChartBase.scss
  14. 215 0
      frontend/pc-web/app/view/core/chart/EChartsBase.js
  15. 26 0
      frontend/pc-web/app/view/core/chart/EChartsBase.scss
  16. 3 5
      frontend/pc-web/app/view/core/tab/Controller.js
  17. 12 13
      frontend/pc-web/app/view/home/Home.js
  18. 29 0
      frontend/pc-web/app/view/home/HomeModel.js
  19. 39 4
      frontend/pc-web/app/view/home/InfoCard.js
  20. 121 0
      frontend/pc-web/app/view/home/charts/ChineseAvg.js
  21. 114 0
      frontend/pc-web/app/view/home/charts/StudentGender.js
  22. 4 4
      frontend/pc-web/app/view/main/Main.js
  23. 49 96
      frontend/pc-web/app/view/main/Navigation.js
  24. 0 1
      frontend/pc-web/app/view/main/Navigation.scss
  25. 1 1
      frontend/pc-web/app/view/viewport/ViewportController.js
  26. 230 3
      frontend/pc-web/app/view/viewport/ViewportModel.js
  27. 1 0
      frontend/pc-web/packages/font-school/Readme.md
  28. 37 0
      frontend/pc-web/packages/font-school/build.xml
  29. 2 0
      frontend/pc-web/packages/font-school/index.js
  30. 32 0
      frontend/pc-web/packages/font-school/package.json
  31. 二进制
      frontend/pc-web/packages/font-school/resources/fonts/iconfont.eot
  32. 0 0
      frontend/pc-web/packages/font-school/resources/fonts/iconfont.js
  33. 32 0
      frontend/pc-web/packages/font-school/resources/fonts/iconfont.svg
  34. 二进制
      frontend/pc-web/packages/font-school/resources/fonts/iconfont.ttf
  35. 二进制
      frontend/pc-web/packages/font-school/resources/fonts/iconfont.woff
  36. 二进制
      frontend/pc-web/packages/font-school/resources/fonts/iconfont.woff2
  37. 10 0
      frontend/pc-web/packages/font-school/sass/etc/icons.scss
  38. 17 0
      frontend/pc-web/packages/font-school/sass/src/all.scss
  39. 53 43
      frontend/pc-web/resources/json/navigation.json

+ 2 - 1
frontend/pc-web/app.json

@@ -20,7 +20,8 @@
         "font-awesome",
         "ux",
         "modern-locale",
-        "font-saas"
+        "font-saas",
+        "font-school"
     ],
     "locale":"zh_CN",
 

+ 25 - 0
frontend/pc-web/app/model/Class.js

@@ -0,0 +1,25 @@
+/**
+ * 班级
+ */
+Ext.define('school.model.Class', {
+    extend: 'school.model.Base',
+    fields: [{
+        name: 'clazz_id', // id
+        type: 'int'
+    }, {
+        name: 'clazz_name', // 名称
+        type: 'string'
+    }, {
+        name: 'clazz_status', // 状态
+        type: 'int'
+    }, {
+        name: 'clazz_remarks', // 备注
+        type: 'string'
+    }, {
+        name: 'clazz_adress', // 地址
+        type: 'string'
+    }, {
+        name: 'grade_id', // 所属年级
+        type: 'int'
+    }],
+});

+ 30 - 0
frontend/pc-web/app/model/Grade.js

@@ -0,0 +1,30 @@
+/**
+ * 年级
+ */
+Ext.define('school.model.Grade', {
+    extend: 'school.model.Base',
+    fields: [{
+        name: 'grade_id', // id
+        type: 'int'
+    }, {
+        name: 'grade_name', // 名称
+        type: 'string'
+    }, {
+        name: 'grade_status', // 状态
+        type: 'int'
+    }, {
+        name: 'grade_remarks', // 备注
+        type: 'string'
+    }, {
+        name: 'grade_adress', // 地址
+        type: 'string'
+    }, {
+        name: 'school_id', // 所属学校
+        type: 'int'
+    }],
+
+    hasMany: {
+        associatedName: 'classList',
+        model: 'school.model.Class'
+    }
+});

+ 61 - 0
frontend/pc-web/app/view/basic/ClassDetail.js

@@ -0,0 +1,61 @@
+Ext.define('school.view.basic.ClassDetail', {
+    extend: 'school.view.core.form.FormPanel',
+    xtype: 'classdetail',
+
+    //字段属性
+    _title: '班级信息',
+    _idField: 'id',
+    _codeField: 'pu_code',
+    _statusField: 'pu_status',
+    _statusCodeField: 'pu_statuscode',
+    _auditmanField: 'pu_auditman',
+    _auditdateField: 'pu_auditdate',
+    _relationColumn: 'pd_puid',
+    _readUrl: '/api/purchase/purchase/read',
+    _saveUrl: '/api/purchase/purchase/save',
+    _auditUrl: '/api/purchase/purchase/audit',
+    _unAuditUrl: '/api/purchase/purchase/unAudit',
+    _deleteUrl: '/api/purchase/purchase/delete',
+    _turnInUrl: '/api/purchase/purchase/turnProdin',
+    initId: 0,
+    initComponent: function () {
+        Ext.apply(this, {
+            defaultItems: [{
+                xtype: 'hidden',
+                name: 'currentDetail.id',
+                fieldLabel: 'id'
+            }, {
+                xtype: "textfield",
+                name: "currentDetail.name",
+                fieldLabel: "班级名称",
+                columnWidth: 0.5
+            }, {
+            //     xtype: 'textfield',
+            //     name: 'code',
+            //     fieldLabel: '班级代码',
+            // }, {
+                xtype: 'textfield',
+                name: 'currentDetail.grade',
+                fieldLabel: '所属年级',
+                columnWidth: 0.5
+            }, {
+                xtype: "textfield",
+                name: "address",
+                fieldLabel: "班级地址",
+                columnWidth: 1
+            }, {
+                xtype: "textfield",
+                name: 'status',
+                fieldLabel: "状态",
+                columnWidth: 0.5
+            }, {
+                xtype: "textfield",
+                name: 'remark',
+                fieldLabel: '备注',
+                columnWidth: 0.5
+            }]
+        });
+        this.callParent();
+    },
+
+});

+ 86 - 0
frontend/pc-web/app/view/basic/ClassInfo.js

@@ -0,0 +1,86 @@
+/**
+ * 班级信息
+ */
+Ext.define('school.view.basic.ClassInfo', {
+    extend: 'Ext.panel.Panel',
+    xtype: 'basic-classinfo',
+
+    controller: 'basic-classinfo',
+    viewModel: 'basic-classinfo',
+
+    layout: 'border',
+
+    // bodyPadding: '8 12 8 12',
+    items: [{
+        region: 'west',
+        split: true,
+        border: false,
+        hideHeaders: true,
+        singleExpand: true,
+        width: 250,
+        xtype: 'treepanel',
+        reference: 'treelist',
+        bind: {
+            store: '{grade}',
+            width: '{treeWidth}'
+        },
+        columns: [{
+            xtype: 'treecolumn',
+            text: 'Name',
+            dataIndex: 'text',
+            flex: 1,
+            sortable: true,
+            renderer: function(v, metaData, record) {
+                metaData.glyph = record.glyph;
+                return v;
+            }
+        }, {
+            xtype: 'actioncolumn',
+            width: 30,
+            items: [{
+                iconCls: 'action-icon x-hidden x-ss ss-add',
+                tooltip: '新增',
+                handler: 'onTreeAddClick'
+            }]
+        }, {
+            xtype: 'actioncolumn',
+            width: 30,
+            items: [{
+                iconCls: 'action-icon x-hidden x-ss ss-deleta',
+                tooltip: '删除',
+                handler: 'onTreeDeleteClick'
+            }]
+        }],
+        listeners: {
+            itemclick: 'onItemclick',
+            itemmouseenter: 'onItemMouseEnter',
+            itemmouseleave: 'onItemMouseLeave'
+        }
+    }, {
+        region: 'center',
+        bodyPadding: 10,
+        items: [{
+            xtype: 'panel',
+            tbar: [{
+                padding: '7 0 7 0',
+                ui: 'header',
+                bind: {
+                    iconCls:'x-sa {toggleIconCls}'
+                },
+                handler: 'onToggleTree'
+            }, {
+                xtype: 'button',
+                text: '上一级',
+                handler: 'onTreeUp'
+            }],
+            items: [{
+                xtype: 'listcard',
+                reference: 'listcard'
+            }, {
+                xtype: 'classdetail',
+                reference: 'classdetail',
+                hidden: true
+            }]
+        }]
+    }]
+});

+ 170 - 0
frontend/pc-web/app/view/basic/ClassInfoController.js

@@ -0,0 +1,170 @@
+Ext.define('school.view.basic.ClassInfoController', {
+    extend: 'Ext.app.ViewController',
+    alias: 'controller.basic-classinfo',
+
+    onItemMouseEnter: function(tree, record, item, index, e, eOpts)  {
+        var icons = item.getElementsByClassName('action-icon');
+        if(record.data.leaf) {
+            icons[1].classList.remove('x-hidden');
+            return;
+        }
+
+        if(record.data.id == 'root') {
+            icons[0].classList.remove('x-hidden');
+            return;
+        }
+
+        icons[0].classList.remove('x-hidden');
+        icons[1].classList.remove('x-hidden');
+    },
+
+    onItemMouseLeave: function(tree, record, item, index, e, eOpts) {
+        var icons = item.getElementsByClassName('action-icon');
+
+        for(var x = 0; x < icons.length; x++) {
+            icons[x].classList.add('x-hidden');
+        }
+    },
+
+    onItemclick: function(tree, record, item, index, e, eOpts) {
+        var me = this,
+        refs = me.getReferences(),
+        listCard = refs.listcard;
+
+        listCard.showNode(record);
+
+    },
+
+    onToggleTree: function() {
+        var me = this,
+        viewModel = me.getViewModel(),
+        refs = me.getReferences(),
+        treelist = refs.treelist,
+        nwidth,
+        nToggleIconCls;
+
+        if(treelist.nowidth) {
+            treelist.nowidth = false;
+            nwidth = treelist.owidth;
+            nToggleIconCls = 'sa-arrows-left'
+        }else {
+            treelist.nowidth = true;
+            treelist.owidth = treelist.getBox().width;
+            nwidth = 0;
+            nToggleIconCls = 'sa-arrows-right'
+        }
+
+        viewModel.set('toggleIconCls', nToggleIconCls);
+        viewModel.set('treeWidth', nwidth);
+    },
+
+    onTreeUp: function() {
+        var me = this,
+        viewModel = me.getViewModel(),
+        refs = me.getReferences(),
+        treeList = refs.treelist,
+        listCard = refs.listcard,
+        currentNode = viewModel.get('currentNode');
+
+        if(!!currentNode.parentNode) {
+            treeList.setSelection(currentNode.parentNode);
+            listCard.showNode(currentNode.parentNode);
+        }
+    },
+
+    onTreeAddClick: function(tree, row, col, item, e, eOpts, tr) {
+        var me = this,
+        store = tree.store,
+        record = store.getAt(row);
+
+        me.showTreeAddWin(record.data);
+
+    },
+
+    onTreeDeleteClick: function(tree, row, col, item, e, eOpts, tr) {
+        var me = this,
+        refs = me.getReferences(),
+        listCard = refs.listcard,
+        treelist = refs.treelist,
+        store = treelist.store,
+        record = store.getAt(row),
+        rootNode = treelist.getRootNode(),
+        id = record.data.id,
+        currentNode = rootNode.findChild('id', id, true),
+        parentNode = currentNode.parentNode;
+
+        treelist.setSelection(parentNode);
+        currentNode.remove();
+        
+        listCard.showNode(parentNode);
+    },
+
+    showTreeAddWin: function(data) {
+        var me = this,
+        view = me.getView(),
+        refs = me.getReferences(),
+        treelist = refs.treelist,
+        listCard = refs.listcard,
+        win = refs.treeaddwin,
+        id = data.id,
+        type = data.type;
+
+        var title = type == 'school' ? '新增年级' : '新增班级';
+        var sumType = type == 'school' ? 'grade' : 'class';
+        if(!win) {
+            win = Ext.create('Ext.window.Window', {
+                width: 300,
+                height: 180,
+                renderTo: Ext.getBody(),
+                references: 'treeaddwin',
+                modal: true,
+                bodyPadding: 10,
+                layout: 'fit',
+                items: [{
+                    xtype: 'form',
+                    layout: 'column',
+                    defaults: {
+                        columnWidth: 1
+                    },
+                    items: [{
+                        xtype: 'textfield',
+                        name: 'text',
+                        emptyText: '名称',
+                        allowBlank: false
+                    }],
+                    buttonAlign: 'center',
+                    buttons: [{
+                        text: '确定',
+                        formBind: true,
+                        handler: function() {
+                            var form = this.up('form');
+                            var text = form.getValues().text;
+                            var rootNode = treelist.getRootNode();
+                            var currentNode;
+                            var childNode = {
+                                text: text,
+                                type: sumType
+                            };
+                            if(id == 'root') {
+                                currentNode = rootNode;
+                                childNode.leaf = false;
+                                childNode.children = [];
+                            }else {
+                                currentNode = rootNode.findChild('id', id, true);
+                                childNode.leaf = true;
+                            }
+                            
+                            currentNode.appendChild(childNode);
+                            listCard.showNode(currentNode);
+                            win.close();
+                        }
+                    }]
+                }]
+            });
+            view.add(win);
+        }
+        win.setTitle(title);
+        win.show();
+    },
+
+});

+ 26 - 0
frontend/pc-web/app/view/basic/ClassInfoModel.js

@@ -0,0 +1,26 @@
+Ext.define('school.view.basic.ClassInfoModel', {
+    extend: 'Ext.app.ViewModel',
+    alias: 'viewmodel.basic-classinfo',
+
+    data: {
+        toggleIconCls: 'sa-arrows-left',
+        treeWidth: 250,
+        currentNode: {},
+        currentDetail: {}
+    },
+
+    stores: {
+        currentlist: {
+            fields: [{
+                name: 'text'
+            }],
+            data: [{
+                id: 'root',
+                type: 'school',
+                itemCls: 'item-add',
+                textCls: 'text-add x-ss ss-add'
+            }]
+        }
+    }
+
+});

+ 85 - 0
frontend/pc-web/app/view/basic/ListCard.js

@@ -0,0 +1,85 @@
+/**
+ * 年级班级卡片列表
+ */
+Ext.define('school.view.basic.ListCard', {
+    extend: 'Ext.view.View',
+    xtype: 'listcard',
+
+    controller: 'listcard',
+
+    cls: 'listcard',
+    tpl: ['<div class="list">',
+        '<tpl for=".">',
+            '<div class="item',
+                '<tpl if="itemCls">',
+                    ' {itemCls}',
+                '</tpl>',
+            '">',
+                '<div class="text',
+                    '<tpl if="textCls">',
+                        ' {textCls}',
+                    '</tpl>',
+                '">{text}</div>',
+            '</div>',
+        '</tpl>',
+    '</div>'],
+
+    bind: {
+        store: '{currentlist}'
+    },
+
+    trackOver: true,
+    overItemCls: 'item-over',
+    selectedItemCls: 'item-selected',
+    singleSelect: true,
+    itemSelector: '.item',
+    listeners: {
+        itemclick: 'cardItemClick',
+        beforeRender: 'onBeforeRender'
+    },
+
+    showNode: function(node) {
+        var me = this,
+        classInfo = me.up('basic-classinfo'),
+        viewModel = classInfo.getViewModel(),
+        currentlist = viewModel.get('currentlist'),
+        currentDetail = viewModel.get('currentDetail'),
+        refs = classInfo.getReferences(),
+        treeList = refs.treelist,
+        listCard = refs.listcard,
+        classDetail = refs.classdetail,
+        list, cardList;
+
+        if(node.data.type == 'class') {
+            currentlist.removeAll();
+            listCard.setVisible(false);
+            classDetail.setVisible(true);
+            viewModel.set('currentDetail', {
+                id: node.data.id,
+                name: node.data.text,
+                grade: node.parentNode.data.text,
+            })
+        }else {
+            node.expand();
+            listCard.setVisible(true);
+            classDetail.setVisible(false);
+
+            list = node.childNodes.map(function(c) {
+                return c.data
+            });
+
+            cardList = Ext.Array.merge(list, [{
+                addBtn: true,
+                id: node.data.id,
+                type: node.data.type,
+                itemCls: 'item-add',
+                textCls: 'text-add x-ss ss-add'
+            }]);
+
+            currentlist.loadData(cardList);
+        }
+
+        viewModel.set('currentNode', node);
+        treeList.setSelection(node);
+    }
+});

+ 33 - 0
frontend/pc-web/app/view/basic/ListCard.scss

@@ -0,0 +1,33 @@
+.listcard {
+    color: blue;
+    .list {
+        display: flex;
+        flex-wrap: wrap;
+        .item {
+            width: calc(25% - 10px);
+            background: #58DCEC;
+            margin: 0 10px 10px 0;
+            height: 42px;
+            cursor: pointer;
+
+            .text {
+                text-align: center;
+                color: white;
+                position: relative;
+                top: 50%;
+                transform: translate(0, -50%);
+            }
+
+            &-add {
+                background: white;
+                border: 1px solid #58dcec;
+                color: #58dcec;
+                
+                .text-add {
+                    color: #58dcec;
+                    font-size: 20px;
+                }
+            }
+        }
+    }
+}

+ 31 - 0
frontend/pc-web/app/view/basic/ListCardController.js

@@ -0,0 +1,31 @@
+Ext.define('school.view.basic.ListCardController', {
+    extend: 'Ext.app.ViewController',
+    alias: 'controller.listcard',
+
+    onBeforeRender: function() {
+        var me = this,
+        view = me.view,
+        viewModel = me.getViewModel(),
+        grade = viewModel.get('grade');
+
+        view.showNode(grade.getRootNode());
+    },
+
+    cardItemClick: function(view, record, navItem, index, e, eOpts) {
+        var me = this;
+        var view = me.getView();
+        var classInfo = view.up('basic-classinfo');
+        var classInfoController = classInfo.getController();
+        var refs = classInfo.getReferences();
+        var treeList = refs.treelist;
+        var rootNode = treeList.getRootNode();
+        var node;
+
+        if(!!record.get('addBtn')) {
+            classInfoController.showTreeAddWin(record.data);
+        }else {
+            node = rootNode.findChild('id', record.get('id'), true);
+            view.showNode(node);
+        }
+    },
+});

+ 74 - 0
frontend/pc-web/app/view/basic/SchoolInfo.js

@@ -0,0 +1,74 @@
+/**
+ * 学校信息
+ */
+Ext.define('school.view.basic.SchoolInfo', {
+    extend: 'school.view.core.form.FormPanel',
+    xtype: 'basic-schoolinfo',
+
+    // controller: 'purchase-purchase-formpanel',
+    // viewModel: 'purchase-purchase-formpanel',
+
+    viewName: 'basic-schoolinfo',
+
+    //字段属性
+    _title: '学校信息',
+    _idField: 'id',
+    _codeField: 'pu_code',
+    _statusField: 'pu_status',
+    _statusCodeField: 'pu_statuscode',
+    _auditmanField: 'pu_auditman',
+    _auditdateField: 'pu_auditdate',
+    _relationColumn: 'pd_puid',
+    _readUrl: '/api/purchase/purchase/read',
+    _saveUrl: '/api/purchase/purchase/save',
+    _auditUrl: '/api/purchase/purchase/audit',
+    _unAuditUrl: '/api/purchase/purchase/unAudit',
+    _deleteUrl: '/api/purchase/purchase/delete',
+    _turnInUrl: '/api/purchase/purchase/turnProdin',
+    initId: 0,
+    initComponent: function () {
+        Ext.apply(this, {
+            defaultItems: [{
+                xtype: 'hidden',
+                name: 'id',
+                fieldLabel: 'id'
+            }, {
+                xtype: "textfield",
+                name: "name",
+                fieldLabel: "学校名称",
+                columnWidth: 0.5
+            }, {
+                xtype: 'textfield',
+                name: 'code',
+                fieldLabel: '学校代码',
+            }, {
+                xtype: 'textfield',
+                name: 'type',
+                fieldLabel: '学校类型'
+            }, {
+                xtype: "textfield",
+                name: "address",
+                fieldLabel: "学校地址",
+                columnWidth: 1
+            }, {
+                xtype: "textfield",
+                name: 'man',
+                fieldLabel: "联系人"
+            }, {
+                xtype: "textfield",
+                name: 'phone',
+                fieldLabel: '联系电话',
+            }]
+        });
+        this.callParent();
+    },
+    toolBtns: [{
+        xtype: 'button',
+        text: '转采购验收单',
+        hidden: true,
+        bind: {
+            hidden: '{turnHidden}'
+        },
+        handler: 'turnIn'
+    }]
+});

+ 64 - 0
frontend/pc-web/app/view/core/chart/ChartBase.js

@@ -0,0 +1,64 @@
+Ext.define('school.view.core.chart.ChartBase', {
+    extend: 'Ext.panel.Panel',
+
+    height: 370,
+    bodyPadding: '16 16 16 0',
+
+    layout: 'fit',
+
+    defaults: {
+        width: '100%'
+    },
+
+    etc: {
+        tools: [{
+            name: 'fullscreen',
+            iconCls: 'x-sa sa-fullscreen',
+            handler: 'fullscreen'
+        }]
+    },
+
+    initComponent: function() {
+        var me = this;
+
+        Ext.apply(me, {
+            title: Ext.create('Ext.panel.Header', {
+                title: me.getTitleHtml(),
+                width: '100%',
+                // listeners: {
+                //     boxready: function() {
+                //         debugger
+                //     }
+                // }
+            }),
+            cls: 'quick-graph-panel ' + (me.cls || '')
+        });
+
+        me.callParent(arguments);
+    },
+
+    getTitleHtml: function() {
+        var me = this,
+        title = me.title || '',
+        tools = me.chartTools;
+
+        return '<div>' + title + '</div><div class="tools">' + me.getToolStr(tools) + '</div>';
+    },
+
+    getToolStr: function(names) {
+        names = Ext.isArray(names) ? names : [];
+
+        var me = this,
+        tools = me.etc.tools,
+        str = '';
+
+        tools.map(function(t) {
+            if(names.indexOf(t.name) != -1) {
+                str += '<a class="tool ' + t.iconCls + '" onClick=""></a>';
+            }
+        })
+
+        return str;
+    },
+
+});

+ 44 - 0
frontend/pc-web/app/view/core/chart/ChartBase.scss

@@ -0,0 +1,44 @@
+.quick-graph-panel {
+
+    .x-panel-header-default {
+        background-color: #fff;
+        border: none;
+        padding: 0;
+        margin-top: 20px;
+
+        .x-box-inner > .x-box-target > .x-container > .x-box-inner > .x-box-target {
+            &> .x-title {
+                .x-title-text {
+                    display: grid;
+                    grid-template-columns: 1fr 1fr;
+                    font-size: 14px;
+                    color: #485465;
+                    letter-spacing: -0.07px;
+                    padding-left: 15px;
+    
+                    .tools {
+                        text-align: right;
+                
+                        .tool {
+                            cursor: pointer;
+                        }
+                    }
+    
+                    &:before {
+                        content: ' ';
+                        position: absolute;
+                        width: 5px;
+                        height: 16px;
+                        border-radius: 4px;
+                        background: #33B4EE;
+                        left: 4px;
+                        top: 1px;
+                    }
+                }
+            }
+        }
+    }
+    .x-mask {
+        background-color: transparent;
+    }
+}

+ 215 - 0
frontend/pc-web/app/view/core/chart/EChartsBase.js

@@ -0,0 +1,215 @@
+Ext.define('school.view.core.EChartsBase', {
+    extend: 'Ext.Container',
+    xtype: 'echartsbase',
+
+    mixins: [
+        'Ext.util.StoreHolder'
+    ],
+
+    border: false,
+    cls: 'x-echarts-container',
+    style: {
+        width: '100%',
+        height: '100%'
+    },
+    config: {
+        option: null
+    },
+
+    initComponent: function () {
+        var me = this;
+        me.on('resize', function(container, width, height, oldWidth, oldHeight, eOpts) {
+            if(me.timer) {
+                window.clearTimeout(me.timer);
+            }
+            me.timer = window.setTimeout(function() {
+                me.echarts.resize();
+            }, 100);
+        });
+        me.on("boxready", function () {
+            me.echarts = echarts.init(me.getEl().dom);
+        });
+        me.callParent();
+    },
+
+    setStore: function (newStore) {
+        var me = this;
+ 
+        if (me.store !== newStore) {
+            if (me.isConfiguring) {
+                me.store = newStore;
+            } else {
+                me.bindStore(newStore, false);
+            }
+        }
+    },
+
+    onBindStore: function(store, oldStore) {
+        var me = this;
+ 
+        if (me.store.isBufferedStore) {
+            me.store.preserveScrollOnReload = me.preserveScrollOnReload;
+        }
+        if (oldStore && oldStore.isBufferedStore) {
+            delete oldStore.preserveScrollOnReload;
+        }
+ 
+        if (!me.dataSource) {
+            me.dataSource = store;
+        }
+    },
+
+    onUnbindStore: function(store) {
+        if (this.dataSource === store) {
+            this.dataSource = null;
+        }
+    },
+
+    bindStore: function (store, initial) {
+        var me = this;
+        
+        me.mixins.storeholder.bindStore.apply(me, arguments);
+ 
+ 
+        if (store && me.componentLayoutCounter && !me.blockRefresh) {
+            me.doFirstRefresh(store, !initial);
+        }
+    },
+
+    doFirstRefresh: function(store, noDefer) {
+        var me = this;
+ 
+        if (me.deferInitialRefresh && !noDefer) {
+            Ext.defer(me.doFirstRefresh, 1, me, [store, true]);
+        }
+ 
+        else {
+            if (store && !me.deferRefreshForLoad(store)) {
+                me.refresh();
+            }
+        }
+    },
+
+    deferRefreshForLoad: function(store) {
+        return store.isLoading();
+    },
+
+    getStoreListeners: function() {
+        var me = this;
+        return {
+            scope: me,
+            refresh: me.onDataRefresh,
+            replace: me.onReplace,
+            add: me.onAdd,
+            remove: me.onRemove,
+            update: me.onUpdate,
+            clear: me.onDataRefresh,
+            beginupdate: me.onBeginUpdate,
+            endupdate: me.onEndUpdate
+        };
+    },
+
+    onDataRefresh: function(store) {
+        // console.log('onDataRefresh');
+        var me = this,
+            preserveScrollOnRefresh = me.preserveScrollOnRefresh;
+ 
+        if (store.loadCount > (me.lastRefreshLoadCount || 0)) {
+            me.preserveScrollOnRefresh = me.preserveScrollOnReload;
+        }
+        me.refreshView();
+        me.preserveScrollOnRefresh = preserveScrollOnRefresh;
+        me.lastRefreshLoadCount = store.loadCount;
+    },
+
+    onReplace: function() {
+        // console.log('onReplace');
+    },
+
+    onAdd: function() {
+        // console.log('onAdd');
+    },
+
+    onRemove: function() {
+        // console.log('onRemove');
+    },
+
+    onUpdate: function() {
+        // console.log('onUpdate');
+    },
+
+    onBeginUpdate: function() {
+        // console.log('onBeginUpdate');
+    },
+
+    onEndUpdate: function() {
+        // console.log('onEndUpdate');
+    },
+
+    refreshView: function(startIndex) {
+        var me = this,
+            // If we have an ancestor in a non-boxready state (collapsed or about to collapse, or hidden), then block the 
+            // refresh because the next layout will trigger the refresh 
+            blocked = me.blockRefresh || !me.rendered || me.up('[collapsed],[isCollapsingOrExpanding=1],[hidden]'),
+            bufferedRenderer = me.bufferedRenderer;
+ 
+        // If we are blocked in any way due to either a setting, or hidden or collapsed, or animating ancestor, then 
+        // the next refresh attempt at the upcoming layout must not defer. 
+        if (blocked) {
+            me.refreshNeeded = true;
+        } else {
+            if (bufferedRenderer) {
+                bufferedRenderer.refreshView(startIndex);
+            } else {
+                me.refresh();
+            }
+        }
+    },
+
+    refresh: function() {
+        var me = this,
+        store = me.store,
+        dataCount = store.getCount(),
+        option;
+
+        me.removeEmptyChart();
+
+        if(me.isEmpty(store)) {
+            me.appendEmptyChart();
+        }else {
+            option = me.createOption(store);
+            me.echarts.setOption(option);
+        }
+    },
+
+    appendEmptyChart: function() {
+        var me = this,
+        echarts = me.echarts,
+        dom = echarts._dom;
+
+        var child = document.createElement('div');
+        child.innerHTML = '<div class="img"></div><div class="text">暂无数据</div>';
+        child.classList.add('x-empty-chart');
+        dom.appendChild(child);
+    },
+
+    removeEmptyChart: function() {
+        var me = this,
+        echarts = me.echarts,
+        dom = echarts._dom;
+
+        var child = dom.getElementsByClassName('x-empty-chart')[0];
+        if(child) {
+            dom.removeChild(child);
+        }
+    },
+
+    isEmpty: function(store) {
+        var dataCount = store.getCount();
+        return dataCount <= 0;
+    },
+
+    createOption: function(store) {
+        return null;
+    }
+});

+ 26 - 0
frontend/pc-web/app/view/core/chart/EChartsBase.scss

@@ -0,0 +1,26 @@
+.x-echarts-container {
+
+    .x-empty-chart {
+        background: #fff;
+        position: absolute;
+        width: 100%;
+        height: 100%;
+        top: 0;
+
+        .img {
+            width: 100%;
+            height: calc(100% - 58px);
+            background: url(/resources/images/grid/empty.png) no-repeat;
+            background-position: center;
+        }
+        
+        .text {
+            font-size: 14px;
+            color: #bebebe;
+            letter-spacing: 0;
+            text-align: center;
+            height: 26px;
+            line-height: 26px;
+        }
+    }
+}

+ 3 - 5
frontend/pc-web/app/view/core/tab/Controller.js

@@ -33,14 +33,13 @@ Ext.define('school.view.core.tab.Controller', {
         if(!component.resetCloseClick) {
             component.tab.onCloseClick = function() {
                 var me = this,
-                tabView = component.down('panel');
+                tabView = component.down('container');
          
                 if (me.fireEvent('beforeclose', me) !== false) {
                     if (me.tabBar) {
-                        if(tabView.fireEvent('beforeclose', tabView) !== false && tabView.promiseCloseTab && typeof tabView.promiseCloseTab == 'function') {
+                        if(tabView && tabView.fireEvent('beforeclose', tabView) !== false && tabView.promiseCloseTab && typeof tabView.promiseCloseTab == 'function') {
                             tabView.promiseCloseTab()
                             .then(function(close) {
-                                console.log();
                                 if(close) {
                                     if (me.tabBar.closeTab(me) === false) {
                                         return;
@@ -53,10 +52,9 @@ Ext.define('school.view.core.tab.Controller', {
                             }
                         }
                     } else {
-                        if(tabView.fireEvent('beforeclose', tabView) !== false && tabView.promiseCloseTab && typeof tabView.promiseCloseTab == 'function') {
+                        if(tabView && tabView.fireEvent('beforeclose', tabView) !== false && tabView.promiseCloseTab && typeof tabView.promiseCloseTab == 'function') {
                             tabView.promiseCloseTab()
                             .then(function(close) {
-                                console.log();
                                 if(close) {
                                     if (me.tabBar.closeTab(me) === false) {
                                         return;

+ 12 - 13
frontend/pc-web/app/view/home/Home.js

@@ -39,21 +39,17 @@ Ext.define('school.view.home.Home', {
 
         latyout: 'responsivecolumn',
         defaults: {
-            margin: '0 16 0 0',
-            userCls: 'x-home-chart big-33 small-50',
+            userCls: 'x-home-chart big-50 small-50',
         },
-        items: []
-    }, {
-        xtype: 'panel',
-        latyout: 'responsivecolumn',
-        style: {
-            background: '#f4f4f4',
-        },
-        defaults: {
+        items: [{
+            xtype: 'studentgender',
+            padding: '0 0 0 14',
             margin: '0 16 0 0',
-            userCls: 'x-home-chart big-33 small-50',
-        },
-        items: []
+        }, {
+            xtype: 'chinessavg',
+            padding: '0 0 0 14',
+            margin: 0,
+        }]
     }],
 
     initComponent: function() {
@@ -63,15 +59,18 @@ Ext.define('school.view.home.Home', {
 
     listeners: {
         onTabActivate: function(p) {
+            return;
             p.refreshId = window.setInterval((p.refreshStores.bind(p)()).bind(p), p.REFRESH_INTERVALS);
         },
         onTabDeactivate: function(p) {
+            return;
             p.lastTime = 0;
             window.clearInterval(p.refreshId);
         }
     },
 
     refreshStores: function() {
+        return;
         var me = this,
         lastTime = me.lastTime || 0,
         now = Ext.Date.now(),

+ 29 - 0
frontend/pc-web/app/view/home/HomeModel.js

@@ -6,5 +6,34 @@ Ext.define('school.view.home.HomeModel', {
     },
 
     stores: {
+        studentgender: {
+            fields: ['x', 'y'],
+            data: [{
+                x: "男",
+                y: 573,
+            }, {
+                x: '女',
+                y: 407
+            }]
+        },
+        chinessavg: {
+            fields: ['x', 'y'],
+            data: [{
+                x: '一班',
+                y: '72.3'
+            }, {
+                x: '二班',
+                y: '67.3'
+            }, {
+                x: '三班',
+                y: '71.1'
+            }, {
+                x: '四班',
+                y: '68.9'
+            }, {
+                x: '五班',
+                y: '73.8'
+            }]
+        }
     }
 });

+ 39 - 4
frontend/pc-web/app/view/home/InfoCard.js

@@ -34,7 +34,36 @@ Ext.define('school.view.home.InfoCard', {
 
         Ext.apply(me, {
             cards: {
-                
+                staff: {
+                    title: '职工人数',
+                    color: 'yellow',
+                    viewType: 'home-infocardlist-saleout',
+                },
+                teacher: {
+                    title: '教师人数',
+                    color: 'purple',
+                    viewType: 'home-infocardlist-purchasein',
+                },
+                grade: {
+                    title: '年级',
+                    color: 'red',
+                    viewType: 'home-infocardlist-payment',
+                },
+                class: {
+                    title: '班级',
+                    color: 'pink',
+                    viewType: 'home-infocardlist-recment',
+                },
+                student: {
+                    title: '学生人数',
+                    color: 'blue',
+                    viewType: 'home-infocardlist-unauditcheckin',
+                },
+                parent: {
+                    title: '家长人数',
+                    color: 'default',
+                    viewType: 'home-infocardlist-unauditsaleout',
+                }
             },
             userCls: 'x-info-card ' + me.userCls,
             lbar: [{
@@ -68,7 +97,14 @@ Ext.define('school.view.home.InfoCard', {
     },
 
     initCardItems: function() {
-        this.addCardItems({});
+        this.addCardItems({
+            staff: 39,
+            teacher: 89,
+            grade: 6,
+            class: 36,
+            student: 980,
+            parent: 1783
+        });
     },
 
     addCardItems: function(infoData) {
@@ -78,7 +114,6 @@ Ext.define('school.view.home.InfoCard', {
         p = me.up('home'),
         cards = me.cards,
         datas = [],
-        items = [];
         size = Math.ceil(me.body.el.getBox().width / 230);
 
         me.removeAll();
@@ -110,7 +145,7 @@ Ext.define('school.view.home.InfoCard', {
                 itemSelector: 'div.x-box',
                 listeners: {
                     itemclick: function(th, record, item, index, e, eOpts) {
-                        school.util.BaseUtil.openTab(record.get('viewType'), record.get('title'), record.get('id'));
+                        // school.util.BaseUtil.openTab(record.get('viewType'), record.get('title'), record.get('id'));
                     }
                 }
             });

+ 121 - 0
frontend/pc-web/app/view/home/charts/ChineseAvg.js

@@ -0,0 +1,121 @@
+Ext.define('school.view.home.charts.ChinessAvg', {
+    extend: 'school.view.core.chart.ChartBase',
+    xtype: 'chinessavg',
+
+    id: 'chinessavg',
+
+    title: '一年级语文平均分',
+
+    initComponent: function () {
+        var me = this;
+
+        Ext.apply(me, {
+            items: [{
+                xtype: 'echartsbase',
+                bind: {
+                    store: '{chinessavg}',
+                },
+                createOption: me.createOption
+            }]
+        });
+
+        me.callParent(arguments);
+    },
+
+    createOption: function (store) {
+        var fields = [],
+            data = [];
+
+        store.each(function (d) {
+            var d = d.data;
+            fields.push(d.x);
+            data.push(d.y);
+        });
+        return {
+            color: [
+                '#34BAF6'
+            ],
+            grid: {
+                left: 0,
+                right: 0,
+                top: 10,
+                bottom: 5,
+                borderColor: '#E5EAEF',
+                containLabel: true
+            },
+            tooltip: {
+                trigger: 'axis',
+                formatter: function (params, ticket, callback) {
+                    var name = '',
+                    series = [];
+
+                    for(var x = 0; x < params.length; x++) {
+                        var p = params[x],
+                        marker = p.marker,
+                        seriesName = p.seriesName,
+                        name = p.name,
+                        value = p.value;
+
+                        value = school.util.BaseUtil.numberFormat(value, 4, true);
+
+                        series.push(marker + value);
+                    }
+
+                    if(name.length > 9) {
+                        name = Ext.String.insert(name, '<br/>', 7)
+                    }
+        
+
+                    return name + ': ' + '<br/>' + series.join('<br/>');
+                }
+            },
+            xAxis: {
+                type: 'category',
+                axisLine: {
+                    lineStyle: {
+                        color: '#E5EAEF',
+                    }
+                },
+                axisLabel: {
+                    color: '#485465',
+                    interval: 0,
+                    formatter: function (value, index) {
+                        var cWidth = Ext.getCmp('chinessavg').items.items[0].echarts.getWidth() * 0.9,
+                            dataCount = fields.length,
+                            maxLength = Math.ceil((cWidth / dataCount) / 20);
+                        return value.length > maxLength ? value.substring(0, maxLength) + '...' : value;
+                    },
+                },
+                data: fields,
+            },
+            yAxis: {
+                type: 'value',
+                axisLine: {
+                    lineStyle: {
+                        color: '#E5EAEF',
+                    }
+                },
+                splitLine: {
+                    lineStyle: {
+                        color: ['#E5EAEF']
+                    }
+                },
+                axisLabel: {
+                    color: '#485465'
+                }
+            },
+            series: [{
+                type: 'bar',
+                barWidth: 25,
+                data: data,
+                label: {
+                    normal: {
+                        show: true,
+                        position: 'outside'
+                    }
+                },
+            }]
+        };
+    }
+
+});

+ 114 - 0
frontend/pc-web/app/view/home/charts/StudentGender.js

@@ -0,0 +1,114 @@
+Ext.define('school.view.home.charts.StudentGender', {
+    extend: 'school.view.core.chart.ChartBase',
+    xtype: 'studentgender',
+
+    id: 'studentgender',
+
+    title: '在校男女学生比例', // 设置默认标题占好默认高度,使容器内的组件可以正确获得展示高度
+
+    initComponent: function () {
+        var me = this;
+
+        Ext.apply(me, {
+            items: [{
+                xtype: 'echartsbase',
+                bind: {
+                    store: '{studentgender}',
+                },
+                isEmpty: me.isEmpty,
+                createOption: me.createOption
+            }]
+        });
+
+        me.callParent(arguments);
+    },
+
+    isEmpty: function (store) {
+        var index = store.findBy(function (s) {
+            return s.get('y') > 0;
+        });
+
+        var flag = index == -1;
+
+        return flag;
+    },
+
+    createOption: function (store) {
+        var fields = [],
+            data = [];
+
+        store.each(function (d) {
+            var d = d.data;
+            if (d.y > 0) {
+                fields.push(d.x);
+                data.push({
+                    value: d.y,
+                    name: d.x
+                });
+            }
+        });
+
+        return {
+            color: [
+                '#77EAF7',
+                '#FD9C8D',
+            ],
+            tooltip: {
+                trigger: 'item',
+                formatter: function (params, ticket, callback) {
+                    var name = params.name,
+                        value = params.value,
+                        marker = params.marker,
+                        percent = params.percent;
+
+                    if (name.length > 7) {
+                        name = Ext.String.insert(name, '<br/>', 7)
+                    }
+                    value = school.util.BaseUtil.numberFormat(value, 4, true);
+
+                    return name + '<br/>' + marker + value + '  (' + percent + '%)';
+                }
+            },
+            legend: {
+                orient: 'vertical',
+                // left: '70%',
+                // width: '30%',
+                // icon: 'circle',
+                right: '10%',
+                data: fields,
+                // itemWidth: 9,
+                // itemHeight: 9,
+                formatter: function (name) {
+                    var cWidth = Ext.getCmp('studentgender').items.items[0].echarts.getWidth() * 0.3,
+                        dataCount = fields.length,
+                        maxLength = Math.ceil((cWidth / dataCount) / 20);
+                    return name.length > maxLength ? name.substring(0, maxLength) + '...' : name;
+                },
+            },
+            series: [{
+                type: 'pie',
+                // radius: ['48%', '88%'],
+                // center: ['35%', '50%'],
+                label: {
+                    normal: {
+                        show: true,
+                        formatter: '{b}: {c}'
+                    },
+                    emphasis: {
+                        show: false,
+                        textStyle: {
+                            fontSize: '30',
+                            fontWeight: 'bold'
+                        }
+                    }
+                },
+                labelLine: {
+                    normal: {
+                        show: false
+                    }
+                },
+                data: data
+            }]
+        }
+    }
+});

+ 4 - 4
frontend/pc-web/app/view/main/Main.js

@@ -29,7 +29,6 @@ Ext.define('school.view.main.Main', {
                 {
                     xtype: 'component',
                     reference: 'mainLogo',
-                    width: 180,
                     cls: 'main-logo-wrap',
                     html: '<div class="main-logo"><img src="resources/images/default/logo-default.png"/><div class="logo-text">智慧校园</div></div>',
                     bind: {
@@ -48,9 +47,10 @@ Ext.define('school.view.main.Main', {
                     reference: 'mainprofile',
                     cls:'x-main-master',
                     ui: 'header',
-                    bind: {
-                        text: '{company.name}'
-                    }
+                    text: '金色阳光希望小学'
+                    // bind: {
+                    //     text: '{company.name}'
+                    // }
                 },
                 '->',
                 {

+ 49 - 96
frontend/pc-web/app/view/main/Navigation.js

@@ -81,117 +81,36 @@ Ext.define('school.view.main.Navigation', {
                 }),
                 tpl: new Ext.XTemplate('<div class="x-navitem-menu">',
                     '<div class="nav-menu-body">',
-                    '<tpl for=".">',
-                    '<div class="menu">',
-                        '<div class="menu-title">',
-                            '<span class="menu-title-text">{text}</span>',
-                        '</div>',
-                        '<div class="menu-content">',
-                            '<tpl for="items">',
-                            '<div class="menuitem">',
-                                '<div class="item-text" data-config="{config}" data-id="{id}" data-text="{text}" data-type="query" data-viewType="{viewType}" data-ctype="{ctype}">',
-                                    '{text}',
+                            '<div class="menu">',
+                                '<div class="menu-content">',
+                                    '<tpl for=".">',
+                                    '<div class="menuitem">',
+                                        '<div class="item-text" data-text="{text}" data-viewType="{viewType}">',
+                                            '{text}',
+                                        '</div>',
+                                    '</div>',
+                                    '</tpl>',
                                 '</div>',
-                                '<tpl if="{addType}">',
-                                '<div class="item-icon" data-id="{id}" data-text="{text}" data-type="form" data-viewType="{addType}">新增</div>',
-                                '</tpl>',
                             '</div>',
-                            '</tpl>',
-                        '</div>',
                     '</div>',
-                    '</tpl>',
-                    '</div>',
-                    '</div>'),
+                '</div>'),
                 trackOver: true,
                 overItemCls: 'menuitem-over',
                 selectedItemCls: 'menuitem-selected',
                 singleSelect: true,
-                itemSelector: 'menu',
+                itemSelector: '.menuitem',
                 listeners: {
-                    boxready: function (view, width, height, eOpts) {
-                        var menu = view.up('menu'),
-                            menuView = view.el.dom.getElementsByClassName('x-navitem-menu')[0],
-                            menuBox = menuView.getBoundingClientRect(),
-                            menuViewWidth = menuBox.width + me.menuPadding * 2,
-                            menuViewHeight = menuBox.height + me.menuPadding * 2,
-                            menuItem = menuView.getElementsByClassName('menuitem');
-
-                        menu.setWidth(menuViewWidth);
-                        menu.setHeight(menuViewHeight);
-                        menu.updateLayout();
-
-                        view.el.dom.addEventListener('mouseenter', function (e) {
-                            menu.show();
-                            menu.navItem.classList.add(menu.navView.overItemCls);
-                        });
-
-                        view.el.dom.addEventListener('mouseleave', function (e) {
-                            menu.navItem.classList.remove(menu.navView.overItemCls);
-                            menu.hide();
-                            // var ex = e.clientX,
-                            //     ey = e.clientY,
-                            //     box = menuView.getBoundingClientRect(),
-                            //     navItem = menu.navItem,
-                            //     navBox = navItem.getBoundingClientRect();
-
-                            // if ((ex <= box.left && (ey <= (navBox.top - 5) || ey >= (navBox.top + navBox.height))) || ey <= (box.top - 5) || ex >= (box.left + box.width + 5) || ey >= (box.top + box.height + 5)) {
-                            //     menu.navItem.classList.remove(menu.navView.overItemCls);
-                            //     menu.hide();
-                            // }
-                        });
-
-                        Ext.Array.each(menuItem, function (mi) {
-                            var menuItemText = mi.getElementsByClassName('item-text');
-                            var menuItemIcon = mi.getElementsByClassName('item-icon');
-
-                            Ext.Array.each(menuItemText, function (item) {
-                                item.addEventListener('click', function (e) {
-                                    var target = e.target,
-                                    dataset = target.dataset,
-                                    viewType = dataset.viewtype,
-                                    type = dataset.type,
-                                    text = dataset.text,
-                                    config = dataset.config,
-                                    id = dataset.id,
-                                    componentType = dataset.ctype || 'Tab';
-
-                                    var tabTitle = text,
-                                    tabId = 'maintab-' + type + '-' + id;
-                                    
-                                    menu.navItem.classList.remove(menu.navView.overItemCls);
-                                    // smartschool.util.BaseUtil.openTab(viewType, tabTitle, tabId,config);
-                                    school.util.BaseUtil['open' + componentType](viewType, tabTitle, tabId,config);
-                                    menu.hide();
-                                });
-                            });
-                            Ext.Array.each(menuItemIcon, function (item) {
-                                item.addEventListener('click', function (e) {
-                                    var target = e.target,
-                                    dataset = target.dataset,
-                                    viewType = dataset.viewtype,
-                                    type = dataset.type,
-                                    text = dataset.text,
-                                    id = dataset.id,
-                                    componentType = dataset.ctype || 'Tab';
-
-                                    var tabTitle ='新增' + text,
-                                    tabId = viewType + '-add';
-
-                                    menu.navItem.classList.remove(menu.navView.overItemCls);
-                                    // smartschool.util.BaseUtil.openTab(viewType, tabTitle, tabId);
-                                    school.util.BaseUtil['open' + componentType](viewType, tabTitle, tabId);
-                                    menu.hide();
-                                });
-                            });
-                        });
+                    boxready: function(view, width, height, eOpts) {
+                        me.menuBoxReady(view, width, height, eOpts);
                     },
+                    itemclick: me.menuItemClick
                 }
             });
             var menu = Ext.create('Ext.menu.Menu', {
                 navView: navView,
                 navItem: navItem,
                 id: menuId,
-                width: window.innerWidth,
+                width: 200,
                 cls: 'x-nav-menu',
                 layout: 'fit',
                 shadow: 'drop',
@@ -216,5 +135,39 @@ Ext.define('school.view.main.Navigation', {
                 menu.hide();
             }
         }
+    },
+
+    menuBoxReady: function (view, width, height, eOpts) {
+        var me = this;
+        var menu = view.up('menu'),
+            menuView = view.el.dom.getElementsByClassName('x-navitem-menu')[0],
+            menuBox = menuView.getBoundingClientRect(),
+            menuViewWidth = menuBox.width + me.menuPadding * 2,
+            menuViewHeight = menuBox.height + me.menuPadding * 2,
+            menuItem = menuView.getElementsByClassName('menuitem');
+
+        menu.setWidth(menuViewWidth);
+        menu.setHeight(menuViewHeight);
+        menu.updateLayout();
+
+        view.el.dom.addEventListener('mouseenter', function (e) {
+            menu.show();
+            menu.navItem.classList.add(menu.navView.overItemCls);
+        });
+
+        view.el.dom.addEventListener('mouseleave', function (e) {
+            menu.navItem.classList.remove(menu.navView.overItemCls);
+            menu.hide();
+        });
+    },
+
+    menuItemClick: function(view, record, item, index, e, eOpts) {
+        var viewType = record.get('view');
+        var tabTitle = record.get('text');
+        var tabId = record.get('id');
+        var config = record.get('config') || [];
+        var menu = view.up('menu');
+        school.util.BaseUtil.openTab(viewType, tabTitle, tabId, config);
+        menu.hide();
     }
 });

+ 0 - 1
frontend/pc-web/app/view/main/Navigation.scss

@@ -172,7 +172,6 @@ $menu-body-background-color: #65678C;
 
             .menu-content {
                 height: 100%;
-                padding-bottom: 20px;
                 background: $menu-body-background-color;
                 
                 .menuitem {

+ 1 - 1
frontend/pc-web/app/view/viewport/ViewportController.js

@@ -38,7 +38,7 @@ Ext.define('school.view.viewport.ViewportController', {
     },
 
     showAuth: function() {
-        this.showView('login');
+        this.showView('main');
     },
 
     showMain: function() {

+ 230 - 3
frontend/pc-web/app/view/viewport/ViewportModel.js

@@ -9,7 +9,7 @@ Ext.define('school.view.viewport.ViewportModel', {
     formulas: {
         company: function (get) {
             var account = get('account');
-            return account && account.companies.find(function(c){
+            return account && account.companies.find(function (c) {
                 return c.id == account.companyId;
             });
         },
@@ -17,5 +17,232 @@ Ext.define('school.view.viewport.ViewportModel', {
             var account = get('account');
             return (account && account.avatarUrl) || 'resources/images/default/user-icon.png'
         }
-    }    
-});
+    },
+
+    stores: {
+        grade: {
+            type: 'tree',
+            // model: 'school.model.Grade',
+            fields: [{
+                name: 'text'
+            }],
+            root: {
+                text: '全年级',
+                type: 'school',
+                expanded: true
+            },
+            data: [{
+                id: 'g1',
+                text: '一年级',
+                // grade_id: '1',
+                // grade_name: '一年级',
+                // grade_status: '1',
+                // grade_remarks: 'beizhu',
+                // grade_adress: '值dadada dad',
+                // school_id: '1',
+                type: 'grade',
+                children: [{
+                    id: 'c11',
+                    text: '一年级1班',
+                    type: 'class',
+                    leaf: true
+                }, {
+                    id: 'c12',
+                    text: '一年级2班',
+                    type: 'class',
+                    leaf: true
+                }, {
+                    id: 'c13',
+                    text: '一年级3班',
+                    type: 'class',
+                    leaf: true
+                }, {
+                    id: 'c14',
+                    text: '一年级4班',
+                    type: 'class',
+                    leaf: true
+                }, {
+                    id: 'c15',
+                    text: '一年级5班',
+                    type: 'class',
+                    leaf: true
+                }]
+            }, {
+                id: 'g2',
+                text: '二年级',
+                type: 'grade',
+                children: [{
+                    id: 'c21',
+                    text: '二年级1班',
+                    type: 'class',
+                    leaf: true
+                }, {
+                    id: 'c22',
+                    text: '二年级2班',
+                    type: 'class',
+                    leaf: true
+                }, {
+                    id: 'c23',
+                    text: '二年级3班',
+                    type: 'class',
+                    leaf: true
+                }, {
+                    id: 'c24',
+                    text: '二年级4班',
+                    type: 'class',
+                    leaf: true
+                }]
+            }, {
+                id: 'g3',
+                text: '三年级',
+                type: 'grade',
+                children: [{
+                    id: 'c31',
+                    text: '三年级1班',
+                    type: 'class',
+                    leaf: true
+                }, {
+                    id: 'c32',
+                    text: '三年级2班',
+                    type: 'class',
+                    leaf: true
+                }, {
+                    id: 'c33',
+                    text: '三年级3班',
+                    type: 'class',
+                    leaf: true
+                }, {
+                    id: 'c34',
+                    text: '三年级4班',
+                    type: 'class',
+                    leaf: true
+                }, {
+                    id: 'c35',
+                    text: '三年级5班',
+                    type: 'class',
+                    leaf: true
+                }, {
+                    id: 'c36',
+                    text: '三年级6班',
+                    type: 'class',
+                    leaf: true
+                }]
+            }, {
+                id: 'g4',
+                text: '四年级',
+                type: 'grade',
+                children: [{
+                    id: 'c41',
+                    text: '四年级1班',
+                    type: 'class',
+                    leaf: true
+                }, {
+                    id: 'c42',
+                    text: '四年级2班',
+                    type: 'class',
+                    leaf: true
+                }, {
+                    id: 'c43',
+                    text: '四年级3班',
+                    type: 'class',
+                    leaf: true
+                }, {
+                    id: 'c44',
+                    text: '四年级4班',
+                    type: 'class',
+                    leaf: true
+                }, {
+                    id: 'c45',
+                    text: '四年级5班',
+                    type: 'class',
+                    leaf: true
+                }, {
+                    id: 'c46',
+                    text: '四年级6班',
+                    type: 'class',
+                    leaf: true
+                }]
+            }, {
+                id: 'g5',
+                text: '五年级',
+                type: 'grade',
+                children: [{
+                    id: 'c51',
+                    text: '五年级1班',
+                    type: 'class',
+                    leaf: true
+                }, {
+                    id: 'c52',
+                    text: '五年级2班',
+                    type: 'class',
+                    leaf: true
+                }, {
+                    id: 'c53',
+                    text: '五年级3班',
+                    type: 'class',
+                    leaf: true
+                }, {
+                    id: 'c54',
+                    text: '五年级4班',
+                    type: 'class',
+                    leaf: true
+                }, {
+                    id: 'c55',
+                    text: '五年级5班',
+                    type: 'class',
+                    leaf: true
+                }, {
+                    id: 'c56',
+                    text: '五年级6班',
+                    type: 'class',
+                    leaf: true
+                }]
+            }, {
+                id: 'g6',
+                text: '六年级',
+                type: 'grade',
+                children: [{
+                    id: 'c61',
+                    text: '六年级1班',
+                    type: 'class',
+                    leaf: true
+                }, {
+                    id: 'c62',
+                    text: '六年级2班',
+                    type: 'class',
+                    leaf: true
+                }, {
+                    id: 'c63',
+                    text: '六年级3班',
+                    type: 'class',
+                    leaf: true
+                }, {
+                    id: 'c64',
+                    text: '六年级4班',
+                    type: 'class',
+                    leaf: true
+                }, {
+                    id: 'c65',
+                    text: '六年级5班',
+                    type: 'class',
+                    leaf: true
+                }, {
+                    id: 'c66',
+                    text: '六年级6班',
+                    type: 'class',
+                    leaf: true
+                }, {
+                    id: 'c6y1',
+                    text: '六年级扬帆班',
+                    type: 'class',
+                    leaf: true
+                }]
+            }, {
+                id: 'gn',
+                text: '新的年级',
+                type: 'grade',
+                children: []
+            }]
+        }
+    }
+});

+ 1 - 0
frontend/pc-web/packages/font-school/Readme.md

@@ -0,0 +1 @@
+# font-school

+ 37 - 0
frontend/pc-web/packages/font-school/build.xml

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<project name="font-ios" default=".help">
+    <!--
+    The build-impl.xml file imported here contains the guts of the build process. It is
+    a great idea to read that file to understand how the process works, but it is best to
+    limit your changes to this file.
+    -->
+    <import file="${basedir}/.sencha/package/build-impl.xml"/>
+
+    <!--
+    The following targets can be provided to inject logic before and/or after key steps
+    of the build process:
+
+        The "init-local" target is used to initialize properties that may be personalized
+        for the local machine.
+
+            <target name="-before-init-local"/>
+            <target name="-after-init-local"/>
+
+        The "clean" target is used to clean build output from the build.dir.
+
+            <target name="-before-clean"/>
+            <target name="-after-clean"/>
+
+        The general "init" target is used to initialize all other properties, including
+        those provided by Sencha Cmd.
+
+            <target name="-before-init"/>
+            <target name="-after-init"/>
+        
+        The "build" target performs the call to Sencha Cmd to build the application.
+
+            <target name="-before-build"/>
+            <target name="-after-build"/>
+    -->
+
+</project>

+ 2 - 0
frontend/pc-web/packages/font-school/index.js

@@ -0,0 +1,2 @@
+// This file was intentionally left blank.
+// This file is used by require.resolve to property locate this module.

+ 32 - 0
frontend/pc-web/packages/font-school/package.json

@@ -0,0 +1,32 @@
+{
+    "name": "@extjs/ext-font-school",
+    "ext-react-name": "@extjs/ext-react-font-school",
+    "ext-name": "@extjs/ext-font-school",
+    "SenchaExtName": "@sencha/ext-font-school",
+    "SenchaExtReactName": "@sencha/ext-react-font-school",
+    "version": "6.6.0.258",
+    "sencha": {
+        "name": "font-school",
+        "namespace": "Ext",
+        "type": "code",
+        "creator": "Sencha",
+        "summary": "school iconfont",
+        "detailedDescription": "school iconfont from http://www.iconfont.cn/",
+        "version": "6.6.0.258",
+        "compatVersion": "6.2.0",
+        "format": "1",
+        "output": "${framework.dir}/build/packages/${package.name}",
+        "local": true,
+        "sass" : {
+            "namespace": "Ext",
+            "etc": "${package.dir}/sass/etc/all.scss",
+            "var": "${package.dir}/sass/var",
+            "src": [
+                "${package.dir}/sass/src",
+                "${package.dir}/sass/src/all.scss"
+            ]
+        },
+        "classpath": "${package.dir}/src",
+        "overrides": "${package.dir}/overrides"
+    }
+}

二进制
frontend/pc-web/packages/font-school/resources/fonts/iconfont.eot


文件差异内容过多而无法显示
+ 0 - 0
frontend/pc-web/packages/font-school/resources/fonts/iconfont.js


+ 32 - 0
frontend/pc-web/packages/font-school/resources/fonts/iconfont.svg

@@ -0,0 +1,32 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<!--
+2013-9-30: Created.
+-->
+<svg>
+<metadata>
+Created by iconfont
+</metadata>
+<defs>
+
+<font id="font-school" horiz-adv-x="1024" >
+  <font-face
+    font-family="font-school"
+    font-weight="500"
+    font-stretch="normal"
+    units-per-em="1024"
+    ascent="896"
+    descent="-128"
+  />
+    <missing-glyph />
+    
+    <glyph glyph-name="deleta" unicode="&#59650;" d="M512 896C227.84 896 0 668.16 0 384s227.84-512 512-512 512 227.84 512 512S796.16 896 512 896zM726.016 169.98400000000004c-13.824-13.824-37.376-13.824-51.2 0L512 332.79999999999995l-162.816-162.816c-13.824-13.824-37.376-13.824-51.2 0s-13.824 37.376 0 51.2L460.8 384 297.984 546.816c-13.824 13.824-13.824 37.376 0 51.2s37.376 13.824 51.2 0L512 435.2l162.816 162.816c13.824 13.824 37.376 13.824 51.2 0s13.824-37.376 0-51.2L563.2 384l162.816-162.816c18.944-13.824 18.944-39.424 0-51.2z"  horiz-adv-x="1024" />
+
+    
+    <glyph glyph-name="add" unicode="&#59649;" d="M512 384m-512 0a512 512 0 1 1 1024 0 512 512 0 1 1-1024 0ZM585.142857 457.142857h219.428572v-146.285714H585.142857v-219.428572H438.857143V310.85714299999995H219.428571V457.142857h219.428572V676.571429h146.285714v-219.428572z"  horiz-adv-x="1024" />
+
+    
+
+
+  </font>
+</defs></svg>

二进制
frontend/pc-web/packages/font-school/resources/fonts/iconfont.ttf


二进制
frontend/pc-web/packages/font-school/resources/fonts/iconfont.woff


二进制
frontend/pc-web/packages/font-school/resources/fonts/iconfont.woff2


+ 10 - 0
frontend/pc-web/packages/font-school/sass/etc/icons.scss

@@ -0,0 +1,10 @@
+
+.ss-deleta:before {
+    content: "\e902";
+  }
+  
+  .ss-add:before {
+    content: "\e901";
+  }
+  
+  

+ 17 - 0
frontend/pc-web/packages/font-school/sass/src/all.scss

@@ -0,0 +1,17 @@
+$ext-font-path: get-resource-path('fonts');
+@import "../etc/icons.scss";
+
+@font-face {font-family: "font-school";
+  src: url('iconfont.eot?t=1547886828483'); /* IE9 */
+  src: url('iconfont.eot?t=1547886828483#iefix') format('embedded-opentype'), /* IE6-IE8 */
+  url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAL0AAsAAAAAByQAAAKnAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCCfgqBcIFcATYCJAMMCwgABCAFhREHNhs+BsiemjxtEepyH+KMIPsHgJ8QQTSWzt5tCEBRkWVUVJGEYwukZesaIclW/+StfYY/8Aey863ns30ceBpp5CUU7/r91iIaOsyGgkaS6ElCfb8+YZaFGUq785BJ/eINOy1+l3JOtLQ0CrNtpufiG/9e0/+aPCwV+HlTp70GcIWaxscYVipJ7zaeqN8wdhOBQ7hOoN00Ct7ovo4gLYF5gXjru3tI52xSWsFWoblma4rfmrSWL+UPwC/+8XEM1WilaKrA0r3nnRPUD+FhtH5ufHaXYuwEdXcTKjYBSezX+vbEhEXA2knjFgEpLYel0XVc/McTEkUQzfTETrCq+QuHgWkpQ2XccV4FzV4h7hrwAhDl2KNr51R0dr77f0bLO97+6fqtaraTEbl3TG46+UlTMePpbxmiHo2/Y6BdhkzmRyPt2gNiHcSz9fNBfHaWHEhwVmOlEfjPwz86IDNtO3cL0KqU+jzz+iej6GlQv90fN3IC/OK/8aRr/CiwC//TKMG/V/CEz218Tqif0aqMoP9Qes+2DJlAXtOnYVpsqNZuUFNQk9rRAEz93vGioRsPhA5DKSpazKKqwwpIuAWa4EXQDO+gdhv87g4DTjGRbVh3KSgMe1DR7S+qhn0g4Y814aMDNMM51G4/Th/ZYTl+tLeY9kInHO94HK1xkFSy5JeFevSsVQNyr2rPpfLk1QnxxbcGFZc1wR4Z4vlW1JtbbeLgylaj63AbKWUxsQ3oKMVd5FwTYq0TFyf1CZB1HSN7gpyg0R06HFmGQ00iK08rqI48lqUMENdV6WWP2+uoyZ0g7MJvGQxSONkP9Himb9U9PlLduKoa4UArZmnIzTKJKIqFUv2kADkSRfsIz1mNgN3Wvrzi6QX6i3OodYDzapSokZqfkiLZxx6dTgAA') format('woff2'),
+  url('iconfont.woff?t=1547886828483') format('woff'),
+  url('iconfont.ttf?t=1547886828483') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
+  url('iconfont.svg?t=1547886828483#font-school') format('svg'); /* iOS 4.1- */
+}
+
+.x-ss:before {
+  font-family:"font-school" !important;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}

+ 53 - 43
frontend/pc-web/resources/json/navigation.json

@@ -1,55 +1,65 @@
 [{
-    "text": "统计分析",
+    "text": "基础资料",
     "iconCls": "x-sa sa-accountCenter",
     "items": [{
-        "text": "注册统计",
-        "items": [{
-            "id": "1",
-            "text": "企业注册数据",
-            "viewType": "statistical-companyreginfo"
-        }, {
-            "id": "2",
-            "text": "个人注册数据",
-            "viewType": "statistical-personreginfo"
-        }]
-    }, {
-        "text": "行为统计",
-        "items": [{
-            "id": "4",
-            "text": "用户访问日志",
-            "viewType": "statistical-loginlog"
-        }, {
-            "id": "5",
-            "text": "企业分析",
-            "viewType": "statistical-companyanalysis"
-        }]
+        "id": "basic-schoolinfo",
+        "text": "学校信息",
+        "view": "basic-schoolinfo"
+    }, {
+        "id": "basic-classinfo",
+        "text": "班级组织",
+        "view": "basic-classinfo"
+    }, {
+        "id": "staff -info",
+        "text": "教职工信息",
+        "view": "staff -info"
+    }, {
+        "id": "student-info",
+        "text": "学生信息",
+        "view": "student-info"
     }]
 }, {
-    "text": "客户服务",
-    "iconCls": "x-sa sa-info",
+    "text": "系统设置",
+    "iconCls": "x-sa sa-setting",
     "items": [{
-        "text": "客户反馈",
-        "items": [{
-            "id": "6",
-            "text": "在线反馈",
-            "viewType": "cuservice-feedback"
-        }]
+        "id": "role-access",
+        "text": "角色授权",
+        "view": "role-access"
+    }, {
+        "id": "device-param",
+        "text": "设备参数",
+        "view": "device-param"
+    }, {
+        "id": "operation-log",
+        "text": "操作日志",
+        "view": "operation-log"
     }]
 }, {
-    "text": "研发助手",
+    "text": "家校互动",
     "iconCls": "x-sa sa-setting",
     "items": [{
-        "text": "工具",
-        "items": [{
-            "id": "9",
-            "text":"清除系统缓存",
-            "ctype": "Win",
-            "viewType": "tools-clearcache"
-        }, {
-            "id": "10",
-            "text": "冻结账号恢复",
-            "ctype": "Win",
-            "viewType": "tools-resetaccount"
-        }]
+        "id": "notice",
+        "text": "学校通知",
+        "view": "notice"
+    }, {
+        "id": "homework",
+        "text": "作业发布",
+        "view": "homework"
+    }, {
+        "id": "timetable",
+        "text": "课程表",
+        "view": "timetable"
+    }, {
+        "id": "grade",
+        "text": "成绩发布",
+        "view": "grade"
+    }, {
+        "id": "mailbox",
+        "text": "校长信箱",
+        "view": "mailbox"
+    }, {
+        "id": "crossing-record",
+        "text": "出入校记录",
+        "view": "crossing-record"
     }]
 }]

部分文件因为文件数量过多而无法显示