Procházet zdrojové kódy

项目通知和系统通知

laiguoran před 5 roky
rodič
revize
2ec1798b16

+ 1 - 0
app/base/base_service.js

@@ -168,6 +168,7 @@ class BaseService extends Service {
         }
 
         const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
+        console.log(sql, sqlParam);
         const list = await this.db.query(sql, sqlParam);
 
         return list;

+ 9 - 0
app/const/account_permission.js

@@ -39,6 +39,15 @@ const permission = {
     //         { title: '关闭', value: 0 },
     //     ],
     // },
+    project_msg: {
+        class: '',
+        title: '发布项目通知',
+        type: 'radio',
+        children: [
+            { title: '启用', value: 1 },
+            { title: '关闭', value: 0 },
+        ],
+    },
 };
 
 module.exports = {

+ 132 - 1
app/controller/dashboard_controller.js

@@ -38,6 +38,12 @@ module.exports = app => {
             const officeName = officeList[salesmanData.office];
             // 获取版本信息
             const versionList = await ctx.service.version.getAllDataByCondition({ orders: [['id', 'desc']], limit: 5, offset: 0 });
+            // 获取项目通知
+            const msgList = await ctx.service.message.getMsgList(ctx.session.sessionProject.id);
+            const userPermission = pa !== undefined && pa.permission !== '' ? JSON.parse(pa.permission) : null;
+            const userMsgPermission = userPermission !== null && userPermission.project_msg !== undefined && parseInt(userPermission.project_msg) === 1;
+            // 获取系统通知
+            const sysMsgList = await ctx.service.message.getMsgList(ctx.session.sessionProject.id, 2, 0, 2);
             const renderData = {
                 auditTenders,
                 auditStages,
@@ -56,7 +62,11 @@ module.exports = app => {
                 projectData,
                 salesmanData,
                 officeName,
-                versionList: JSON.parse(JSON.stringify(versionList).replace(/\\r\\n/g, '<br>'))
+                versionList: JSON.parse(JSON.stringify(versionList).replace(/\\r\\n/g, '<br>').replace(/\\"/g, '&#34').replace(/'/g, '&#39')),
+                msgList: JSON.parse(JSON.stringify(msgList).replace(/\\r\\n/g, '<br>').replace(/\\"/g, '&#34').replace(/'/g, '&#39')),
+                sysMsgList: JSON.parse(JSON.stringify(sysMsgList).replace(/\\r\\n/g, '<br>').replace(/\\"/g, '&#34').replace(/'/g, '&#39')),
+                userMsgPermission,
+                uid: ctx.session.sessionUser.accountId,
             };
             await this.layout('dashboard/index.ejs', renderData, 'dashboard/modal.ejs');
             await ctx.service.projectAccount.defaultUpdate({
@@ -64,6 +74,127 @@ module.exports = app => {
                 last_notice: new Date(),
             });
         }
+
+        /**
+         * 控制面板-通知页面
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async msgList(ctx) {
+            const page = ctx.page;
+
+            const type = ctx.request.query.type ? parseInt(ctx.request.query.type) : 1;
+
+            const total = type === 1 ?
+                await ctx.service.message.count({ project_id: ctx.session.sessionProject.id, type }) :
+                await ctx.service.message.count({ status: 1, type });
+
+            const limit = 5;
+            const offset = limit * (this.ctx.page - 1);
+
+            const msgList = await ctx.service.message.getMsgList(ctx.session.sessionProject.id, limit, offset, type);
+
+            const pa = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
+            const userPermission = pa !== undefined && pa.permission !== '' ? JSON.parse(pa.permission) : null;
+            const userMsgPermission = userPermission !== null && userPermission.project_msg !== undefined && parseInt(userPermission.project_msg) === 1;
+
+            // 分页相关
+            const pageInfo = {
+                page,
+                total: Math.ceil(total / limit),
+                queryData: JSON.stringify(ctx.urlInfo.query),
+            };
+
+            const renderData = {
+                uid: ctx.session.sessionUser.accountId,
+                type,
+                pageInfo,
+                userMsgPermission,
+                msgList: JSON.parse(JSON.stringify(msgList).replace(/\\r\\n/g, '<br>').replace(/\\"/g, '&#34').replace(/'/g, '&#39')),
+            };
+            await this.layout('dashboard/msg.ejs', renderData);
+        }
+
+        /**
+         * 控制面板-通知添加和编辑页面
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async msgAdd(ctx) {
+            let id = ctx.params.id;
+            id = parseInt(id);
+            try {
+                if (isNaN(id) || id < 0) {
+                    throw '参数错误';
+                }
+                const rule = ctx.service.message.rule();
+                const jsValidator = await this.jsValidator.convert(rule).build();
+                const msgInfo = id === 0 ? {} : await ctx.service.message.getDataById(id);
+                const renderData = {
+                    jsValidator,
+                    msgInfo,
+                };
+                await this.layout('dashboard/msg_add.ejs', renderData, 'dashboard/msg_modal.ejs');
+            } catch (error) {
+                console.log(error);
+                // this.setMessage(error.toString(), this.messageType.ERROR);
+                ctx.redirect(ctx.request.header.referer);
+            }
+        }
+
+        /**
+         * 控制面板-通知保存
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async msgSet(ctx) {
+            try {
+                let id = ctx.params.id;
+                id = parseInt(id);
+                if (isNaN(id) || id < 0) {
+                    throw '参数错误';
+                }
+                const rule = ctx.service.message.rule();
+                ctx.helper.validate(rule);
+                const result = await ctx.service.message.save(id, ctx.request.body, ctx.session.sessionUser, ctx.session.sessionProject.id);
+                if (result) {
+                    ctx.redirect('/dashboard/msg/list');
+                }
+            } catch (error) {
+                console.log(error);
+                ctx.redirect(ctx.request.header.referer);
+            }
+        }
+
+        /**
+         * 控制面板-通知删除
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async msgDelete(ctx) {
+            try {
+                let id = ctx.params.id;
+                id = parseInt(id);
+                if (isNaN(id) || id <= 0) {
+                    throw '参数错误';
+                }
+                const msgInfo = await ctx.service.message.getDataById(id);
+                if (!msgInfo || msgInfo.create_uid !== ctx.session.sessionUser.accountId) {
+                    throw '通知不存在或无权限操作';
+                }
+                const result = await ctx.service.message.deleteById(msgInfo.id);
+                if (result) {
+                    ctx.redirect('/dashboard/msg/list');
+                }
+            } catch (error) {
+                console.log(error);
+                ctx.redirect(ctx.request.header.referer);
+            }
+        }
     }
 
     return DashboardController;

+ 1 - 0
app/controller/setting_controller.js

@@ -147,6 +147,7 @@ module.exports = app => {
                     accountData,
                     accountGroup,
                     permission,
+                    permissionStr: JSON.stringify(permission),
                     // rule: JSON.stringify(frontRule),
                 };
                 await this.layout('setting/user_permission.ejs', renderData, 'setting/user_permission_modal.ejs');

+ 1 - 1
app/lib/sql_builder.js

@@ -231,8 +231,8 @@ class SqlBuilder {
         this._andWhereBuild();
         this._orWhereBuild();
 
-        this._limitBuild();
         this._orderByBuild();
+        this._limitBuild();
 
         const sql = this.sql;
         const sqlParam = this.sqlParam;

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 556 - 527
app/public/css/main.css


+ 50 - 0
app/public/js/dashboard.js

@@ -0,0 +1,50 @@
+'use strict';
+
+/**
+ * 待办页js
+ *
+ * @author EllisRan.
+ * @date 2019/3/19
+ * @version
+ */
+
+$(document).ready(() => {
+    // $('a[href="#view-msg"]').on('click', function () {
+    //     const id = $(this).attr('msg-id');
+    //     const msgInfo = _.find(msgList,{ id: parseInt(id) });
+    //     $('#title').html(msgInfo.title);
+    //     $('#creator').html(msgInfo.creator);
+    //     $('#release_time').html(moment(msgInfo.release_time*1000).format('YYYY-MM-DD'));
+    //     $('#content').html(msgInfo.content);
+    //     $('#user_permission').html('');
+    //     if (userMsgPermission && parseInt(uid) === msgInfo.create_uid ) {
+    //         const permissionHtml = '<a href="/dashboard/msg/add/' + msgInfo.id +'" class="btn btn-sm btn-outline-primary">编辑</a>';
+    //         $('#user_permission').html(permissionHtml);
+    //     }
+    // });
+
+    $('.list-group-item a').on('click', function () {
+        const id = $(this).attr('msg-id');
+        const msgInfo = _.find(msgList, {id: parseInt(id)});
+        $('#title').html(msgInfo.title);
+        $('#creator').html(msgInfo.creator);
+        $('#release_time').html(moment(msgInfo.release_time * 1000).format('YYYY-MM-DD'));
+        $('#content').html(msgInfo.content);
+        $('#view-msg .modal-title').text('项目通知');
+        $('#user_permission').html('');
+        if (userMsgPermission && parseInt(uid) === msgInfo.create_uid) {
+            const permissionHtml = '<a href="/dashboard/msg/add/' + msgInfo.id + '" class="btn btn-sm btn-outline-primary">编辑</a>';
+            $('#user_permission').html(permissionHtml);
+        }
+    });
+
+    $('.system-msg').on('click', function () {
+        const id = $(this).attr('msg-id');
+        const msgInfo = _.find(sysMsgList, {id: parseInt(id)});
+        $('#title').html(msgInfo.title);
+        $('#creator').html('');
+        $('#release_time').html(moment(msgInfo.release_time * 1000).format('YYYY-MM-DD'));
+        $('#content').html(msgInfo.content);
+        $('#view-msg .modal-title').text('系统通知');
+    })
+});

+ 7 - 4
app/public/js/setting.js

@@ -121,14 +121,17 @@ $(document).ready(() => {
                 } else {
                     $('#edit-user2 input[name="cooperation"]').attr('disabled', true);
                 }
-                for (const index of permission[pm]) {
-
-                    $('#edit-user2 input:checkbox[id="' + pm + '_' + index + '"]').prop('checked', true);
+                if (allPermission[pm].type === 'checkbox') {
+                    for (const index of permission[pm]) {
+                        $('#edit-user2 input:checkbox[id="' + pm + '_' + index + '"]').prop('checked', true);
+                    }
+                } else if (allPermission[pm].type === 'radio') {
+                    $('#edit-user2 input:radio[id="' + pm + '_' + permission[pm] + '"]').prop('checked', true);
                 }
             }
         }
         // 协作赋值
-        $('#edit-user2 input:radio[value="' + account.cooperation + '"]').prop('checked', true);
+        $('#edit-user2 input:radio[name="cooperation"][value="' + account.cooperation + '"]').prop('checked', true);
     });
 
     // 选择创建标段功能后可选协作办公

+ 4 - 0
app/router.js

@@ -32,6 +32,10 @@ module.exports = app => {
 
     // 控制面板相关
     app.get('/dashboard', sessionAuth, 'dashboardController.index');
+    app.get('/dashboard/msg/list', sessionAuth, 'dashboardController.msgList');
+    app.get('/dashboard/msg/add/:id', sessionAuth, 'dashboardController.msgAdd');
+    app.post('/dashboard/msg/set/:id', sessionAuth, datetimeFill, 'dashboardController.msgSet');
+    app.get('/dashboard/msg/del/:id', sessionAuth, 'dashboardController.msgDelete');
 
     /**
      * 项目设置

+ 93 - 2
app/service/message.js

@@ -23,12 +23,48 @@ module.exports = app => {
         }
 
         /**
+         * 规则
+         *
+         * @return {Object} - 返回规则
+         */
+        rule() {
+            return {
+                title: { type: 'string', required: true, allowEmpty: false },
+                content: { type: 'string', required: false, allowEmpty: true },
+                istop: { type: 'string', required: false, allowEmpty: true },
+            };
+        }
+
+        /**
+         * 查询过虑
+         *
+         * @param {Object} data - 筛选表单中的get数据
+         * @return {void}
+         */
+        searchFilter(data, project_id) {
+            this.initSqlBuilder();
+
+            // 状态筛选
+            data.type = parseInt(data.type);
+            data.type = isNaN(data.type) || data.type <= 0 ? 1 : data.type;
+            // 获取用户数据
+            this.sqlBuilder.setAndWhere('type', {
+                value: data.type,
+                operate: '=',
+            });
+            this.sqlBuilder.setAndWhere('project_id', {
+                value: project_id,
+                operate: '=',
+            });
+        }
+
+        /**
          * 获取消息数据
          *
          * @param {Number} startTime - 时间标记位(获取这个时间点之后的数据)
          * @return {Array} - 返回消息数据
          */
-        async getMessage(startTime) {
+        async getMessage(startTime, project_id) {
             this.initSqlBuilder();
             this.sqlBuilder.setAndWhere('release_time', {
                 value: startTime,
@@ -36,8 +72,14 @@ module.exports = app => {
             });
 
             // 获取用户数据
+            this.sqlBuilder.setAndWhere('project_id', {
+                value: project_id,
+                operate: '=',
+            });
+
+            // 获取用户数据
             this.sqlBuilder.setAndWhere('type', {
-                value: 2,
+                value: 1,
                 operate: '=',
             });
 
@@ -46,6 +88,55 @@ module.exports = app => {
 
             return result;
         }
+
+        /**
+         * 修改通知信息
+         *
+         * @param {Object} data - post过来的数据
+         * @return {Boolean} - 返回修改结果
+         */
+        async save(id, data, user, projectId) {
+            if (data._csrf !== undefined) {
+                delete data._csrf;
+            }
+            if (id > 0) {
+                // 修改操作时
+                data.id = id;
+                const msgInfo = await this.getDataById(id);
+                data.istop = parseFloat(msgInfo.istop) === 0 && parseInt(data.istop) === 1 ? data.create_time : data.istop === undefined ? 0 : msgInfo.istop;
+                delete data.create_time;
+            } else {
+                data.release_time = data.create_time;
+                data.project_id = projectId;
+                data.create_uid = user.accountId;
+                data.creator = user.name;
+                data.istop = parseInt(data.istop) === 1 ? data.create_time : 0;
+            }
+            const operate = id === 0 ? await this.db.insert(this.tableName, data) :
+                await this.db.update(this.tableName, data);
+
+            const result = operate.affectedRows > 0;
+
+            return result;
+        }
+
+        /**
+         * 修改通知信息
+         *
+         * @param {Object} data - post过来的数据
+         * @return {Boolean} - 返回修改结果
+         */
+        async getMsgList(projectId, limit = 5, offset = 0, type = 1) {
+            if (type === 1) {
+                const sql = 'SELECT * FROM ?? WHERE `project_id` = ? AND `type` = ? ORDER BY CONCAT(`istop`,`release_time`) DESC LIMIT ?,?';
+                const sqlParam = [this.tableName, projectId, type, offset, limit];
+                return await this.db.query(sql, sqlParam);
+            }
+
+            const sql = 'SELECT * FROM ?? WHERE `type` = ? AND `status` = ? ORDER BY `release_time` DESC LIMIT ?,?';
+            const sqlParam = [this.tableName, type, 1, offset, limit];
+            return await this.db.query(sql, sqlParam);
+        }
     }
 
     return Message;

+ 1 - 1
app/service/notify.js

@@ -46,7 +46,7 @@ module.exports = app => {
                 const lastMessageTime = lastData.length <= 0 ? 0 : lastData[0].create_time;
 
                 // 获取后台设置的消息数据
-                const messageData = await this.ctx.service.message.getMessage(lastMessageTime);
+                const messageData = await this.ctx.service.message.getMessage(lastMessageTime, this.ctx.session.sessionProject.id);
                 // 如果已经是最新的消息则直接返回
                 if (messageData.length <= 0) {
                     return true;

+ 138 - 79
app/view/dashboard/index.ejs

@@ -15,7 +15,7 @@
                     </div>
                 </div>
                 <% } %>
-                <div class="col-5">
+                <div class="col-4">
                     <div class="card">
                         <div class="card-header">需要你处理</div>
                         <div class="card-body">
@@ -23,84 +23,86 @@
                                 <ul class="list-unstyled m-0">
                                     <% for (const t of auditTenders) { %>
                                         <% if (t.ledger_status === acLedger.status.checking) { %>
-                                            <li class="media">
-                                                <img class="mr-3" src="public/images/avatar.png">
+                                            <li class="media pb-3 mb-3 border-bottom-1">
                                                 <div class="media-body">
-                                                    <span class="pull-right text-muted"><%- t.begin_time.toLocaleString() %></span>
-                                                    <h5 class="mt-0"><%- ctx.session.sessionUser.name %><small class="ml-3 text-muted"><%- role %></small></h5>
-                                                    <p><a href="/tender/<%- t.id %>"><%- t.name %></a> 台帐 需要您 <a href="/tender/<%- t.id %>/ledger/audit" class="btn btn-sm btn-primary">审批</a>。</p>
+                                                    <div class="row">
+                                                        <div class="col-auto"><span class="badge badge-info">台帐审批</span></div>
+                                                        <div class="col-6"><a href="/tender/<%- t.id %>"><%- t.name %></a> 台帐</div>
+                                                        <div class="col-3 ml-auto text-right pl-0"><a href="/tender/<%- t.id %>/ledger/audit" class="btn btn-sm btn-outline-primary">审批</a></div>
+                                                    </div>
+                                                    <p class="mt-1 mb-0"><%- ctx.session.sessionUser.name %><small class="ml-1 text-muted"><%- (role ? '- ' + role : '') %></small>
+                                                        <span class="pull-right text-muted"><%- t.begin_time.toLocaleString() %></span>
+                                                    </p>
                                                 </div>
                                             </li>
                                         <% } else { %>
-                                            <li class="media">
-                                                <img class="mr-3" src="public/images/avatar.png">
+                                            <li class="media pb-3 mb-3 border-bottom-1">
                                                 <div class="media-body">
-                                                    <span class="pull-right text-muted"><%- t.end_time.toLocaleString() %></span>
-                                                    <h5 class="mt-0"><%- ctx.session.sessionUser.name %><small class="ml-3 text-muted"><%- role %></small></h5>
-                                                    <p><a href="/tender/<%- t.id %>"><%- t.name %></a> 台帐 需要您 <a href="/tender/<%- t.id %>/ledger" class="btn btn-sm btn-primary">重新上报</a>。</p>
+                                                    <div class="row">
+                                                        <div class="col-auto"><span class="badge badge-info">台帐审批</span></div>
+                                                        <div class="col-6"><a href="/tender/<%- t.id %>"><%- t.name %></a> 台帐</div>
+                                                        <div class="col-3 ml-auto text-right pl-0"><a href="/tender/<%- t.id %>/ledger" class="btn btn-sm btn-outline-primary">重新上报</a></div>
+                                                    </div>
+                                                    <p class="mt-1 mb-0"><%- ctx.session.sessionUser.name %><small class="ml-1 text-muted"><%- (role ? '- ' + role : '') %></small>
+                                                        <span class="pull-right text-muted"><%- t.end_time.toLocaleString() %></span>
+                                                    </p>
                                                 </div>
                                             </li>
                                         <% } %>
                                     <% } %>
                                     <% for (const revise of auditRevise) { %>
-                                        <li class="media">
-                                            <img class="mr-3" src="public/images/avatar.png">
+                                        <li class="media pb-3 mb-3 border-bottom-1">
                                             <div class="media-body">
-                                                <span class="pull-right text-muted"><%- (revise.end_time ? revise.end_time.toLocaleString() : '') %></span>
-                                                <h5 class="mt-0"><%- revise.audit_name %><small class="ml-3 text-muted"><%- revise.audit_role %></small></h5>
-                                                <p>
-                                                    <a href="/tender/<%- revise.t_id %>"><%- revise.t_name %></a>
-                                                    台账修订(第<%- revise.corder %>次)
-                                                    <% if (revise.status === acRevise.status.checkNo) { %>
-                                                        <span style="color: indianred">被退回</span>
-                                                    <% } %>
-                                                    需要您
-                                                    <a href="/tender/<%- revise.t_id %>/revise/info" class="btn btn-sm btn-primary"><% if (revise.status === acRevise.status.checking) { %>审批<% } else if (revise.status === acRevise.status.checkNo) { %>重新上报<% } %></a>。
+                                                <div class="row">
+                                                    <div class="col-auto"><span class="badge badge-info">台帐修订</span></div>
+                                                    <div class="col-6"><a href="/tender/<%- revise.t_id %>"><%- revise.t_name %></a> 台帐修订(第<%- revise.corder %>次)</div>
+                                                    <div class="col-3 ml-auto text-right pl-0"><a href="/tender/<%- revise.t_id %>/revise/info" class="btn btn-sm btn-outline-primary"><% if (revise.status === acRevise.status.checking) { %>审批<% } else if (revise.status === acRevise.status.checkNo) { %>重新上报<% } %></a></div>
+                                                </div>
+                                                <p class="mt-1 mb-0"><%- revise.audit_name %><small class="ml-1 text-muted"><%- (revise.audit_role ? '- ' + revise.audit_role: '') %></small>
+                                                    <span class="pull-right text-muted"><%- (revise.end_time ? revise.end_time.toLocaleString() : '') %></span>
                                                 </p>
                                             </div>
                                         </li>
                                     <% } %>
                                     <% for (const audit of auditStages) { %>
                                         <% if (audit.sstatus !== acStage.status.checkNo) { %>
-                                            <li class="media">
-                                                <img class="mr-3" src="public/images/avatar.png">
+                                            <li class="media pb-3 mb-3 border-bottom-1">
                                                 <div class="media-body">
-                                                    <span class="pull-right text-muted"><%- audit.begin_time.toLocaleString() %></span>
-                                                    <h5 class="mt-0"><%- ctx.session.sessionUser.name %><small class="ml-3 text-muted"><%- role %></small></h5>
-                                                    <p><a href="/tender/<%- audit.tid %>"><%- audit.name %></a>
-                                                        第<%- audit.sorder %>期 <% if (audit.sstatus === acStage.status.checkNoPre) { %><span style="color: indianred">被退回</span> <% } %> 需要您
-                                                        <a href="/tender/<%- audit.tid %>/measure/stage/<%- audit.sorder %>" class="btn btn-sm btn-primary"><% if (audit.sstatus === acStage.status.checkNoPre) { %>重新<% } %>审批</a>。
+                                                    <div class="row">
+                                                        <div class="col-auto"><span class="badge badge-success">计量审批</span></div>
+                                                        <div class="col-6"><a href="/tender/<%- audit.tid %>"><%- audit.name %></a> 第<%- audit.sorder %>期</div>
+                                                        <div class="col-3 ml-auto text-right pl-0"><a href="/tender/<%- audit.tid %>/measure/stage/<%- audit.sorder %>" class="btn btn-sm btn-outline-primary"><% if (audit.sstatus === acStage.status.checkNoPre) { %>重新<% } %>审批</a></div>
+                                                    </div>
+                                                    <p class="mt-1 mb-0"><%- ctx.session.sessionUser.name %><small class="ml-1 text-muted"><%- (role ? '- ' + role : '') %></small>
+                                                        <span class="pull-right text-muted"><%- audit.begin_time.toLocaleString() %></span>
                                                     </p>
                                                 </div>
                                             </li>
                                         <% } else { %>
-                                            <li class="media">
-                                                <img class="mr-3" src="public/images/avatar.png">
+                                            <li class="media pb-3 mb-3 border-bottom-1">
                                                 <div class="media-body">
-                                                    <span class="pull-right text-muted"><%- audit.end_time.toLocaleString() %></span>
-                                                    <h5 class="mt-0"><%- ctx.session.sessionUser.name %><small class="ml-3 text-muted"><%- role %></small></h5>
-                                                    <p><a href="/tender/<%- audit.tid %>"><%- audit.name %></a>
-                                                        第<%- audit.sorder %>期 <% if (audit.sstatus === acStage.status.checkNoPre) { %><span style="color: indianred">被退回</span> <% } %> 需要您
-                                                        <a href="/tender/<%- audit.tid %>/measure/stage/<%- audit.sorder %>" class="btn btn-sm btn-primary">重新上报</a>。
+                                                    <div class="row">
+                                                        <div class="col-auto"><span class="badge badge-success">计量审批</span></div>
+                                                        <div class="col-6"><a href="/tender/<%- audit.tid %>"><%- audit.name %></a> 第<%- audit.sorder %>期</div>
+                                                        <div class="col-3 ml-auto text-right pl-0"><a href="/tender/<%- audit.tid %>/measure/stage/<%- audit.sorder %>" class="btn btn-sm btn-outline-primary">重新上报</a></div>
+                                                    </div>
+                                                    <p class="mt-1 mb-0"><%- ctx.session.sessionUser.name %><small class="ml-1 text-muted"><%- (role ? '- ' + role : '') %></small>
+                                                        <span class="pull-right text-muted"><%- audit.end_time.toLocaleString() %></span>
                                                     </p>
                                                 </div>
                                             </li>
                                         <% } %>
                                     <% } %>
                                     <% for (const change of auditChanges) { %>
-                                        <li class="media">
-                                            <img class="mr-3" src="public/images/avatar.png">
+                                        <li class="media pb-3 mb-3 border-bottom-1">
                                             <div class="media-body">
-                                                <span class="pull-right text-muted"><%- (change.sin_time ? change.sin_time.toLocaleString() : '') %></span>
-                                                <h5 class="mt-0"><%- change.caname %><small class="ml-3 text-muted"><%- role %></small></h5>
-                                                <p>
-                                                    <a href="/tender/<%- change.tid %>"><%- change.name %></a>
-                                                    变更令 <%- change.ccode %>
-                                                    <% if (change.cstatus === acChange.status.backnew || change.cstatus === acChange.status.back) { %>
-                                                        <span style="color: indianred">被退回</span>
-                                                    <% } %>
-                                                    需要您
-                                                    <a href="/tender/<%- change.tid %>/change/<%- change.cid %>/info" class="btn btn-sm btn-primary"><% if (change.cstatus === acChange.status.checking) { %>审批<% } else if (change.cstatus === acChange.status.backnew) { %>重新审批<% } else { %>重新上报<% } %></a>。
+                                                <div class="row">
+                                                    <div class="col-auto"><span class="badge badge-danger">变更审批</span></div>
+                                                    <div class="col-6"><a href="/tender/<%- change.tid %>"><%- change.name %></a> 变更令 <%- change.ccode %></div>
+                                                    <div class="col-3 ml-auto text-right pl-0"><a href="/tender/<%- change.tid %>/change/<%- change.cid %>/info" class="btn btn-sm btn-outline-primary"><% if (change.cstatus === acChange.status.checking) { %>审批<% } else if (change.cstatus === acChange.status.backnew) { %>重新审批<% } else { %>重新上报<% } %></a></div>
+                                                </div>
+                                                <p class="mt-1 mb-0"><%- change.caname %><small class="ml-1 text-muted"><%- (role ? '- ' + role : '') %></small>
+                                                    <span class="pull-right text-muted"><%- (change.sin_time ? change.sin_time.toLocaleString() : '') %></span>
                                                 </p>
                                             </div>
                                         </li>
@@ -113,60 +115,74 @@
                         </div>
                     </div>
                 </div>
-                <div class="col-5">
+                <div class="col-4">
                     <div class="card">
                         <div class="card-header">需要你关注</div>
                         <div class="card-body">
                             <% if (noticeLedger.length !== 0 || noticeRevise.length !== 0 || noticeStage.length !== 0 || noticeChange.length !== 0) { %>
                                 <ul class="list-unstyled m-0">
                                     <% for (const nl of noticeLedger) { %>
-                                        <li class="media">
-                                            <img class="mr-3" src="public/images/avatar.png">
+                                        <li class="media pb-3 mb-3 border-bottom-1">
                                             <div class="media-body">
-                                                <span class="pull-right text-muted"><%- nl.end_time.toLocaleString() %></span>
-                                                <h5 class="mt-0"><%- nl.lu_name %><small class="ml-3 text-muted"><%- nl.lu_role %></small></h5>
-                                                <p><a href="/tender/<%- nl.id %>"><%- nl.name %></a> 台帐 <%- acLedger.statusString[nl.status]%>。</p>
+                                                <div class="row">
+                                                    <div class="col-auto"><span class="badge badge-info">台帐审批</span></div>
+                                                    <div class="col-6">
+                                                        <a href="/tender/<%- nl.id %>"><%- nl.name %></a> 台帐<%- acLedger.statusString[nl.status]%>
+                                                    </div>
+                                                </div>
+                                                <p class="mt-1 mb-0"><%- nl.lu_name %><small class="ml-1 text-muted"><%- (nl.lu_role ? '- ' + nl.role : '') %></small>
+                                                    <span class="pull-right text-muted"><%- nl.end_time.toLocaleString() %></span>
+                                                </p>
                                             </div>
                                         </li>
                                     <% } %>
                                     <% for (const nr of noticeRevise) { %>
-                                        <li class="media">
-                                            <img class="mr-3" src="public/images/avatar.png">
+                                        <li class="media pb-3 mb-3 border-bottom-1">
                                             <div class="media-body">
-                                                <span class="pull-right text-muted"><%- (nr.end_time ? nr.end_time.toLocaleString() : '') %></span>
-                                                <h5 class="mt-0"><%- nr.ru_name %><small class="ml-3 text-muted"><%- nr.ru_role %></small></h5>
-                                                <p>
-                                                    <a href="/tender/<%- nr.id %>"><%- nr.name %></a>
-                                                    <a href="/tender/<%- nr.t_id %>/revise/info">台账修订(第<%- nr.corder %>次)</a>
-                                                    <%- acRevise.statusString[nr.status]%>。
+                                                <div class="row">
+                                                    <div class="col-auto"><span class="badge badge-info">台账修订</span></div>
+                                                    <div class="col-6">
+                                                        <a href="/tender/<%- nr.id %>>"><%- nr.name %></a>
+                                                        <a href="/tender/<%- nr.t_id %>/revise/info">台账修订(第<%- nr.corder %>次)</a>
+                                                        <%- acRevise.statusString[nr.status]%>
+                                                    </div>
+                                                </div>
+                                                <p class="mt-1 mb-0"><%- nr.ru_name %><small class="ml-1 text-muted"><%- (nr.ru_role ? '- ' + nr.ru_role : '') %></small>
+                                                    <span class="pull-right text-muted"><%- (nr.end_time ? nr.end_time.toLocaleString() : '') %></span>
                                                 </p>
                                             </div>
                                         </li>
                                     <% } %>
                                     <% for (const ns of noticeStage) { %>
-                                        <li class="media">
-                                            <img class="mr-3" src="public/images/avatar.png">
+                                        <li class="media pb-3 mb-3 border-bottom-1">
                                             <div class="media-body">
-                                                <span class="pull-right text-muted"><%- ns.end_time.toLocaleString() %></span>
-                                                <h5 class="mt-0"><%- ns.su_name %><small class="ml-3 text-muted"><%- ns.su_role %></small></h5>
-                                                <p>
-                                                    <a href="/tender/<%- ns.tid %>"><%- ns.name %></a>
-                                                    <a href="/tender/<%- ns.tid %>/measure/stage/<%- ns.s_order %>">第<%- ns.s_order %>期 </a>
-                                                    <%- acStage.statusString[ns.status]%>。
+                                                <div class="row">
+                                                    <div class="col-auto"><span class="badge badge-success">计量审批</span></div>
+                                                    <div class="col-6">
+                                                        <a href="/tender/<%- ns.tid %>"><%- ns.name %></a>
+                                                        <a href="/tender/<%- ns.tid %>/measure/stage/<%- ns.s_order %>">第<%- ns.s_order %>期 </a>
+                                                        <%- acStage.statusString[ns.status]%>
+                                                    </div>
+                                                </div>
+                                                <p class="mt-1 mb-0"><%- ns.su_name %><small class="ml-1 text-muted"><%- (ns.su_role ? '- ' + ns.su_role : '') %></small>
+                                                    <span class="pull-right text-muted"><%- ns.end_time.toLocaleString() %></span>
                                                 </p>
                                             </div>
                                         </li>
                                     <% } %>
                                     <% for (const nc of noticeChange) { %>
-                                        <li class="media">
-                                            <img class="mr-3" src="public/images/avatar.png">
+                                        <li class="media pb-3 mb-3 border-bottom-1">
                                             <div class="media-body">
-                                                <span class="pull-right text-muted"><%- nc.cu_time.toLocaleString() %></span>
-                                                <h5 class="mt-0"><%- nc.cu_name %><small class="ml-3 text-muted"><%- nc.cu_role %></small></h5>
-                                                <p>
-                                                    <a href="/tender/<%- nc.id %>"><%- nc.name %></a>
-                                                    <a href="/tender/<%- nc.id %>/change/<%- nc.cid %>/info"><%- nc.c_code %> </a>
-                                                    <%- acChange.statusString[nc.cu_status]%>。
+                                                <div class="row">
+                                                    <div class="col-auto"><span class="badge badge-danger">变更审批</span></div>
+                                                    <div class="col-6">
+                                                        <a href="/tender/<%- nc.id %>"><%- nc.name %></a>
+                                                        <a href="/tender/<%- nc.id %>/change/<%- nc.cid %>/info"><%- nc.c_code %> </a>
+                                                        <%- acChange.statusString[nc.cu_status]%>
+                                                    </div>
+                                                </div>
+                                                <p class="mt-1 mb-0"><%- nc.cu_name %><small class="ml-1 text-muted"><%- (nc.cu_role ? '- ' + nc.cu_role : '') %></small>
+                                                    <span class="pull-right text-muted"><%- nc.cu_time.toLocaleString() %></span>
                                                 </p>
                                             </div>
                                         </li>
@@ -179,6 +195,28 @@
                         </div>
                     </div>
                 </div>
+                <!--项目消息通知-->
+                <div class="col-2">
+                    <div class="card">
+                        <div class="card-header d-flex">项目通知<span class="ml-auto"><a href="/dashboard/msg/list">查看所有</a></span></div>
+                        <ul class="list-group">
+                            <% if (msgList.length === 0) { %>
+                                <!--没有通知-->
+                                <li class="list-group-item text-muted"><p class="text-center mb-0">暂时没有通知</p></li>
+                            <% } else { %>
+                                <% for (const msg of msgList) { %>
+                                    <li class="list-group-item text-muted">
+                                        <a href="#view-msg" msg-id="<%= msg.id %>" data-toggle="modal" data-target="#view-msg"<% if (msg.istop !== '0') { %> class="text-danger">
+                                            <i class="fa fa-exclamation-triangle" ></i> <% } else { %>><% } %><%- msg.title %></a><br><%- moment(msg.release_time*1000).format('YYYY-MM-DD') %></li>
+                                <% } %>
+                            <% } %>
+                            <% if (userMsgPermission) { %>
+                                <!--发布通知-->
+                                <li class="list-group-item text-muted"><p class="text-center mb-0"><a href="/dashboard/msg/add/0">发布新通知</a></p></li>
+                            <% } %>
+                        </ul>
+                    </div>
+                </div>
                 <div class="col-2">
                     <div class="card">
                         <div class="card-body">
@@ -208,10 +246,31 @@
                     </div>
                     <% } %>
                 </div>
+                <!--系统消息通知-->
+                <% if (sysMsgList.length > 0) { %>
+                <div class="col-12 mt-3">
+                    <div>
+                        <div class="card-body row">
+                            <div class="col-auto">系统消息</div>
+                            <div class="col-auto">
+                                <% for (const sm of sysMsgList) { %>
+                                    <a href="#view-msg" msg-id="<%= sm.id %>" data-toggle="modal" data-target="#view-msg" class="mr-5 mb-2 system-msg"><%- sm.title %>(<%- moment(sm.release_time*1000).format('YYYY-MM-DD') %>)</a>
+                                <% } %>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <% } %>
             </div>
         </div>
     </div>
 </div>
+<script src="/public/js/moment/moment.min.js"></script>
+<script src="/public/js/dashboard.js"></script>
 <script>
     autoFlashHeight();
+    const msgList = JSON.parse('<%- JSON.stringify(msgList) %>');
+    const sysMsgList = JSON.parse('<%- JSON.stringify(sysMsgList) %>');
+    const uid = '<%- uid %>';
+    const userMsgPermission = <%- userMsgPermission %>;
 </script>

+ 31 - 0
app/view/dashboard/modal.ejs

@@ -25,3 +25,34 @@
     </div>
 </div>
 <% } %>
+<!--弹出所有项目通知-->
+<div class="modal fade" id="view-msg" data-backdrop="static">
+    <div class="modal-dialog modal-lg" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">项目通知</h5>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                    <span aria-hidden="true">&times;</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                <div class="row">
+                    <div class="col-12">
+                        <div class="modal-height-500 scroll-y">
+                            <h4 class="text-center" id="title">通知标题</h4>
+                            <p class="text-center text-muted">
+                                <span id="creator">张三</span> 发布于 <span id="release_time">2019-02-02</span>
+                                <span id="user_permission">
+                                </span>
+                            </p>
+                            <!--内容开始-->
+                            <div class="msg-content border-top-1 pt-3" id="content">
+
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>

+ 62 - 0
app/view/dashboard/msg.ejs

@@ -0,0 +1,62 @@
+<div class="panel-content">
+    <div class="panel-title fluid">
+        <div class="title-main"><h2>消息通知</h2></div>
+    </div>
+    <div class="content-wrap">
+        <div class="sjs-height-0">
+            <div class="container py-5">
+                <div class="row">
+                    <div class="col-3 bd-toc">
+                        <div class="card">
+                            <div class="card-header">
+                                <div class="btn-group btn-group-sm" role="group" aria-label="Basic example">
+                                    <a href="/dashboard/msg/list" <% if (type === 1) { %>class="btn btn-primary"<% } else { %>class="btn btn-outline-primary"<% } %>>项目通知</a>
+                                    <a href="/dashboard/msg/list?type=2" <% if (type === 2) { %>class="btn btn-primary"<% } else { %>class="btn btn-outline-primary"<% } %>>系统消息</a>
+                                </div>
+                            </div>
+                            <ul class="list-group list-group-flush msg-height-list">
+                                <% for (const msg of msgList) { %>
+                                <li class="list-group-item"><a href="javascript:void(0);" msg-id="<%= msg.id %>" <% if (msg.istop !== '0') { %>class="text-danger"><i class="fa fa-exclamation-triangle" ></i><% } else { %>><% } %> <%= msg.title %></a><br><%= moment(msg.release_time*1000).format('YYYY-MM-DD') %></li>
+                                <% } %>
+                                <li class="list-group-item pt-2">
+                                    <% include ../layout/page.ejs %>
+                                </li>
+                            </ul>
+                        </div>
+                    </div>
+                    <div class="card col-9">
+                        <div class="card-body">
+                            <div>
+                                <% if (msgList && msgList[0]) { %>
+                                <h4 class="text-center" id="title"><%= msgList[0].title %></h4>
+                                <p class="text-center text-muted">
+                                    <span id="creator"><%= msgList[0].creator %></span> 发布于 <span id="release_time"><%= moment(msgList[0].release_time*1000).format('YYYY-MM-DD') %></span>
+                                    <span id="user_permission">
+                                        <% if (uid === msgList[0].create_uid) { %>
+                                            <!--有编辑权用户-->
+                                            <a href="/dashboard/msg/add/<%- msgList[0].id %>" class="btn btn-sm btn-outline-primary">编辑</a>
+                                        <% } %>
+                                    </span>
+                                </p>
+                                <!--内容开始-->
+                                <div class="msg-content border-top-1 pt-3" id="content">
+                                    <%- msgList[0].content %>
+                                </div>
+                                <% } %>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+    autoFlashHeight();
+    const msgList = JSON.parse('<%- JSON.stringify(msgList) %>');
+    const uid = '<%- uid %>';
+    const userMsgPermission = <%- userMsgPermission %>;
+</script>
+<script src="/public/js/moment/moment.min.js"></script>
+<script src="/public/js/dashboard.js"></script>
+

+ 55 - 0
app/view/dashboard/msg_add.ejs

@@ -0,0 +1,55 @@
+<div class="panel-content">
+    <div class="panel-title fluid">
+        <div class="title-main"><h2>消息通知</h2></div>
+    </div>
+    <div class="content-wrap">
+        <div class="sjs-height-0">
+            <div class="container py-5">
+                <form id="save-form" class="row" action="/dashboard/msg/set/<%= msgInfo.id === undefined ? 0 : msgInfo.id %>" method="post">
+                    <div class="card col-9">
+                        <div class="card-body">
+                            <div class="msg-height-content">
+                                <input-text name="title" id="title" placeholder="请输入标题" required="true" label="标题"
+                                            value="<%= msgInfo.title %>"></input-text>
+                                <div class="form-group form-check">
+                                    <input type="checkbox" class="form-check-input" id="exampleCheck1" name="istop" value="1" <% if (msgInfo.istop !== undefined && msgInfo.istop !== '0') { %>checked<% } %>>
+                                    <label class="form-check-label" for="exampleCheck1">重要通知(标红置顶)</label>
+                                </div>
+                                <div class="form-group">
+                                    <label>内容</label>
+                                    <div style="height: calc(100vh - 320px);">
+                                        <textarea id="content" name="content" class="form-control form-control-sm"><%- msgInfo.content %></textarea>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="col-3 bd-toc">
+                        <div class="card">
+                            <div class="card-body">
+                                <input type="hidden" name="_csrf" value="<%= ctx.csrf %>">
+                                <% if (msgInfo.id === undefined) { %>
+                                    <button type="submit" class="btn btn-primary">确认添加</button>
+                                <% } else { %>
+                                    <button type="submit" class="btn btn-primary">确认修改</button>
+                                    <a href="#del-modal" data-toggle="modal" data-target="#del-modal" class="btn btn-danger">删除</a>
+                                <% } %>
+                            </div>
+                        </div>
+                    </div>
+                </form>
+            </div>
+        </div>
+    </div>
+</div>
+<%- jsValidator %>
+<script type="text/javascript">
+    new Vue({
+        el: '#save-form',
+    });
+    $(function () {
+        $('#btn_click').click(function () {
+            $('#btn_type').val(2);
+        })
+    })
+</script>

+ 16 - 0
app/view/dashboard/msg_modal.ejs

@@ -0,0 +1,16 @@
+<div class="modal fade" tabindex="-1" role="dialog" id="del-modal">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">删除确认</h5>
+            </div>
+            <div class="modal-body">
+                <h5>是否要删除本通知</h5>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
+                <a href="/dashboard/msg/del/<%= msgInfo.id %>" class="btn btn-danger confirm">确认删除</a>
+            </div>
+        </div>
+    </div>
+</div>

+ 15 - 5
app/view/setting/user_permission.ejs

@@ -38,11 +38,18 @@
                                     <td><%= account.company %></td>
                                     <td><%= account.role %></td>
                                     <td><% if (account.cooperation === 1) { %>启用<% } %></td>
-                                    <td><% if (account.permission !== '' && account.permission !== null && JSON.parse(account.permission).tender !== undefined) { %>
-                                            <% const tenderPermission = JSON.parse(account.permission).tender; %>
-                                            <% for (const tc in permission.tender.children) { %>
-                                                <% if (tenderPermission.indexOf(permission.tender.children[tc].value.toString()) !== -1) { %>
-                                                    <%= permission.tender.children[tc].title %>
+                                    <td>
+                                        <% if (account.permission !== '' && account.permission !== null) { %>
+                                            <% const accountPermission = JSON.parse(account.permission); %>
+                                            <% for(const ap in accountPermission) { %>
+                                                <% if (permission[ap].type === 'checkbox') { %>
+                                                    <% for (const tc in permission[ap].children) { %>
+                                                        <% if (accountPermission[ap].indexOf(permission[ap].children[tc].value.toString()) !== -1) { %>
+                                                            <%= permission[ap].children[tc].title %>
+                                                        <% } %>
+                                                    <% } %>
+                                                <% } else if (permission[ap].type === 'radio') { %>
+                                                    <%= permission[ap].title %>
                                                 <% } %>
                                             <% } %>
                                         <% } %>
@@ -57,5 +64,8 @@
         </div>
     </div>
 </div>
+<script>
+    const allPermission = JSON.parse('<%- permissionStr %>');
+</script>
 <script src="/public/js/setting.js"></script>
 <script>autoFlashHeight();</script>