m0_70156489 il y a 6 heures
Parent
commit
385576cc02

+ 16 - 9
application/admin/controller/Procuremenarchive.php

@@ -14,7 +14,7 @@ use think\Db;
  */
  */
 class Procuremenarchive extends Backend
 class Procuremenarchive extends Backend
 {
 {
-    protected $searchFields = 'CCYDH,CYJMC,CGYMC';
+    protected $searchFields = 'CCYDH,CYJMC,CGYMC,pick_company_name';
 
 
     public function _initialize()
     public function _initialize()
     {
     {
@@ -113,7 +113,7 @@ class Procuremenarchive extends Backend
             $listQuery = Db::table('purchase_order');
             $listQuery = Db::table('purchase_order');
             $applyFilters($listQuery);
             $applyFilters($listQuery);
             $rows = $listQuery
             $rows = $listQuery
-                ->field('id,scydgy_id,CCYDH,CYJMC,CGYMC,createtime,dStamp,pick_time')
+                ->field('id,scydgy_id,CCYDH,CYJMC,CGYMC,pick_company_name,createtime,dStamp,pick_time')
                 ->order($sortField, $orderDir)
                 ->order($sortField, $orderDir)
                 ->select();
                 ->select();
             if (!is_array($rows)) {
             if (!is_array($rows)) {
@@ -143,13 +143,14 @@ class Procuremenarchive extends Backend
                 $sid = (int)($r['scydgy_id'] ?? 0);
                 $sid = (int)($r['scydgy_id'] ?? 0);
                 $done = ProcuremenTime::resolveCompletedDone($r, $completeTsMap);
                 $done = ProcuremenTime::resolveCompletedDone($r, $completeTsMap);
                 $out[] = [
                 $out[] = [
-                    'id'              => (int)($r['id'] ?? 0),
-                    'scydgy_id'       => $sid,
-                    'CCYDH'           => trim((string)($r['CCYDH'] ?? '')),
-                    'CYJMC'           => trim((string)($r['CYJMC'] ?? '')),
-                    'CGYMC'           => trim((string)($r['CGYMC'] ?? '')),
-                    'createtime'      => $done['ts'],
-                    'createtime_text' => $done['text'],
+                    'id'                => (int)($r['id'] ?? 0),
+                    'scydgy_id'         => $sid,
+                    'CCYDH'             => trim((string)($r['CCYDH'] ?? '')),
+                    'CYJMC'             => trim((string)($r['CYJMC'] ?? '')),
+                    'CGYMC'             => trim((string)($r['CGYMC'] ?? '')),
+                    'pick_company_name' => trim((string)($r['pick_company_name'] ?? '')),
+                    'createtime'        => $done['ts'],
+                    'createtime_text'   => $done['text'],
                 ];
                 ];
             }
             }
 
 
@@ -217,16 +218,22 @@ class Procuremenarchive extends Backend
             });
             });
             $head = $groupRows[0];
             $head = $groupRows[0];
             $gymcList = [];
             $gymcList = [];
+            $supplierList = [];
             foreach ($groupRows as $r) {
             foreach ($groupRows as $r) {
                 $g = trim((string)($r['CGYMC'] ?? ''));
                 $g = trim((string)($r['CGYMC'] ?? ''));
                 if ($g !== '' && !in_array($g, $gymcList, true)) {
                 if ($g !== '' && !in_array($g, $gymcList, true)) {
                     $gymcList[] = $g;
                     $gymcList[] = $g;
                 }
                 }
+                $sn = trim((string)($r['pick_company_name'] ?? ''));
+                if ($sn !== '' && !in_array($sn, $supplierList, true)) {
+                    $supplierList[] = $sn;
+                }
             }
             }
             $merged = $head;
             $merged = $head;
             if (count($groupRows) > 1) {
             if (count($groupRows) > 1) {
                 $merged['CGYMC'] = implode('、', $gymcList);
                 $merged['CGYMC'] = implode('、', $gymcList);
             }
             }
+            $merged['pick_company_name'] = implode('、', $supplierList);
             $merged['process_count'] = count($groupRows);
             $merged['process_count'] = count($groupRows);
             $latestTs = 0;
             $latestTs = 0;
             $latestText = '';
             $latestText = '';

+ 66 - 14
application/admin/controller/Purchasecontent.php

@@ -169,6 +169,8 @@ class Purchasecontent extends Backend
         $isSuper = $this->auth->isSuperAdmin();
         $isSuper = $this->auth->isSuperAdmin();
 
 
         list($where, $sort, $order, $offset, $limit) = $this->buildparams();
         list($where, $sort, $order, $offset, $limit) = $this->buildparams();
+        $orderDir = strtoupper((string)$order) === 'ASC' ? 'ASC' : 'DESC';
+        $sortField = preg_match('/^[a-zA-Z0-9_]+$/', (string)$sort) ? (string)$sort : 'id';
 
 
         $query = $this->model->where($where);
         $query = $this->model->where($where);
         if (!$isSuper) {
         if (!$isSuper) {
@@ -184,13 +186,58 @@ class Purchasecontent extends Backend
             });
             });
         }
         }
 
 
-        $list = $query->order($sort, $order)->paginate($limit);
+        $this->applyPurchaseContentSort($query, $sortField, $orderDir, $adminId);
+        $list = $query->paginate($limit);
         $rows = $list->items();
         $rows = $list->items();
         $this->enrichContentRows($rows, $adminId);
         $this->enrichContentRows($rows, $adminId);
 
 
         return json(['total' => $list->total(), 'rows' => $rows]);
         return json(['total' => $list->total(), 'rows' => $rows]);
     }
     }
 
 
+    /**
+     * 列表排序(支持投递人/投递给/状态等衍生字段)
+     *
+     * @param mixed $query
+     */
+    protected function applyPurchaseContentSort($query, string $sortField, string $orderDir, int $adminId): void
+    {
+        $table = $this->model->getTable();
+        if ($sortField === 'sender_name') {
+            $query->orderRaw(
+                '(SELECT COALESCE(NULLIF(TRIM(sa.nickname), \'\'), sa.username, \'\') '
+                . 'FROM admin sa WHERE sa.id = `' . $table . '`.sender_id LIMIT 1) ' . $orderDir
+            )->order($table . '.id', 'DESC');
+
+            return;
+        }
+        if ($sortField === 'recipient_names') {
+            $query->orderRaw(
+                '(SELECT COUNT(*) FROM purchase_content_recipient rcnt WHERE rcnt.content_id = `' . $table . '`.id) ' . $orderDir
+            )->order($table . '.id', 'DESC');
+
+            return;
+        }
+        if ($sortField === 'is_read') {
+            $aid = max(0, $adminId);
+            $query->orderRaw(
+                '(SELECT CASE '
+                . 'WHEN COUNT(*) = 0 THEN 2 '
+                . 'WHEN SUM(CASE WHEN rr.read_time IS NULL OR rr.read_time = \'\' '
+                . 'OR CAST(rr.read_time AS CHAR) LIKE \'0000-%\' THEN 1 ELSE 0 END) > 0 THEN 0 '
+                . 'ELSE 1 END '
+                . 'FROM purchase_content_recipient rr '
+                . 'WHERE rr.content_id = `' . $table . '`.id AND rr.admin_id = ' . $aid . ') ' . $orderDir
+            )->order($table . '.id', 'DESC');
+
+            return;
+        }
+        $allow = ['id', 'createtime', 'subject', 'content', 'sender_id', 'creator_id', 'updatetime'];
+        if (!in_array($sortField, $allow, true)) {
+            $sortField = 'id';
+        }
+        $query->order($table . '.' . $sortField, $orderDir);
+    }
+
     public function add()
     public function add()
     {
     {
         if (!$this->request->isPost()) {
         if (!$this->request->isPost()) {
@@ -270,19 +317,23 @@ class Purchasecontent extends Backend
         }
         }
 
 
         $adminId = (int)$this->auth->id;
         $adminId = (int)$this->auth->id;
-        if (!$this->auth->isSuperAdmin() && (int)$row['creator_id'] !== $adminId) {
-            $isRecv = Db::table('purchase_content_recipient')
-                ->where('content_id', (int)$row['id'])
-                ->where('admin_id', $adminId)
-                ->count();
-            if (!$isRecv) {
-                $this->error('无权查看该通知');
-            }
-            $recvRow = Db::table('purchase_content_recipient')
-                ->where('content_id', (int)$row['id'])
-                ->where('admin_id', $adminId)
-                ->find();
-            if ($recvRow && empty($recvRow['read_time'])) {
+        $contentId = (int)$row['id'];
+        $isCreator = (int)($row['creator_id'] ?? 0) === $adminId;
+        $recvRow = Db::table('purchase_content_recipient')
+            ->where('content_id', $contentId)
+            ->where('admin_id', $adminId)
+            ->find();
+        $isReceiver = (bool)$recvRow;
+
+        if (!$this->auth->isSuperAdmin() && !$isCreator && !$isReceiver) {
+            $this->error('无权查看该通知');
+        }
+
+        // 当前用户是接收人时,首次查看标记为已读
+        if ($recvRow) {
+            $rt = $recvRow['read_time'] ?? null;
+            $unread = ($rt === null || $rt === '' || stripos((string)$rt, '0000-00-00') === 0);
+            if ($unread) {
                 Db::table('purchase_content_recipient')
                 Db::table('purchase_content_recipient')
                     ->where('id', (int)$recvRow['id'])
                     ->where('id', (int)$recvRow['id'])
                     ->update(['read_time' => date('Y-m-d H:i:s')]);
                     ->update(['read_time' => date('Y-m-d H:i:s')]);
@@ -451,6 +502,7 @@ class Purchasecontent extends Backend
                 'recipient_count' => $recv['count'],
                 'recipient_count' => $recv['count'],
                 'is_receiver'     => $recv['is_receiver'] ? 1 : 0,
                 'is_receiver'     => $recv['is_receiver'] ? 1 : 0,
                 'is_sender'       => $isSender ? 1 : 0,
                 'is_sender'       => $isSender ? 1 : 0,
+                'is_read'         => (!empty($recv['read_time'])) ? 1 : 0,
                 'read_time'       => $recv['read_time'],
                 'read_time'       => $recv['read_time'],
                 'send_time_text'  => $this->formatPurchaseContentTime($data['createtime'] ?? ''),
                 'send_time_text'  => $this->formatPurchaseContentTime($data['createtime'] ?? ''),
                 'can_delete'      => $isSender ? 1 : 0,
                 'can_delete'      => $isSender ? 1 : 0,

+ 3 - 0
application/admin/view/common/meta.html

@@ -12,7 +12,10 @@
 
 
 {if $Think.config.fastadmin.adminskin}
 {if $Think.config.fastadmin.adminskin}
 <link href="__CDN__/assets/css/skins/{$Think.config.fastadmin.adminskin}.css?v={$Think.config.site.version}" rel="stylesheet">
 <link href="__CDN__/assets/css/skins/{$Think.config.fastadmin.adminskin}.css?v={$Think.config.site.version}" rel="stylesheet">
+{else /}
+<link href="__CDN__/assets/css/skins/skin-blue-light.css?v={$Think.config.site.version}" rel="stylesheet">
 {/if}
 {/if}
+<link href="__CDN__/assets/css/xinhua-theme.css?v={$Think.config.site.version}" rel="stylesheet">
 
 
 <!-- HTML5 shim, for IE6-8 support of HTML5 elements. All other JS at the end of file. -->
 <!-- HTML5 shim, for IE6-8 support of HTML5 elements. All other JS at the end of file. -->
 <!--[if lt IE 9]>
 <!--[if lt IE 9]>

+ 2 - 2
application/admin/view/dashboard/index.html

@@ -160,12 +160,12 @@
     }
     }
 
 
     html, body {
     html, body {
-        background: #fff;
+        background: #f5f6f8;
     }
     }
 
 
     .panel-intro {
     .panel-intro {
         margin-bottom: 0;
         margin-bottom: 0;
-        background: #fff;
+        background: transparent;
     }
     }
 
 
     .proc-dash {
     .proc-dash {

+ 1 - 1
application/admin/view/index/index.html

@@ -4,7 +4,7 @@
         <!-- 加载样式及META信息 -->
         <!-- 加载样式及META信息 -->
         {include file="common/meta" /}
         {include file="common/meta" /}
     </head>
     </head>
-    <body class="hold-transition {$Think.config.fastadmin.adminskin|default='skin-black-blue'} sidebar-mini {:$Think.cookie.sidebar_collapse?'sidebar-collapse':''} fixed {:$Think.config.fastadmin.multipletab?'multipletab':''} {:$Think.config.fastadmin.multiplenav?'multiplenav':''}" id="tabs">
+    <body class="hold-transition {$Think.config.fastadmin.adminskin|default='skin-blue-light'} sidebar-mini {:$Think.cookie.sidebar_collapse?'sidebar-collapse':''} fixed {:$Think.config.fastadmin.multipletab?'multipletab':''} {:$Think.config.fastadmin.multiplenav?'multiplenav':''}" id="tabs">
 
 
         <div class="wrapper">
         <div class="wrapper">
 
 

+ 620 - 241
application/admin/view/index/login.html

@@ -20,252 +20,508 @@
             min-height: 100vh;
             min-height: 100vh;
             max-height: 100vh;
             max-height: 100vh;
             overflow: hidden;
             overflow: hidden;
-            background-color: #020b1e;
-            background-image: url('https://a-7in6-com.oss-cn-hangzhou.aliyuncs.com/xinhua/img/login1.png?v={$site.version|default='1.0.1'|htmlentities}');
+            background: #eef2f8;
+            color: #1f2937;
+            font-family: "PingFang SC", "Microsoft YaHei", "Helvetica Neue", sans-serif;
+            display: flex;
+        }
+
+        /* ========== 左侧(背景图 / 插画图后续替换下方 url) ========== */
+        .login-brand {
+            position: relative;
+            z-index: 1;
+            flex: 1 1 68%;
+            min-width: 0;
+            display: flex;
+            flex-direction: column;
+            align-items: center;
+            justify-content: space-between;
+            color: #fff;
+            /* 背景图 bj.png */
+            background-color: #1a3a6e;
+            background-image: url('https://a-7in6-com.oss-cn-hangzhou.aliyuncs.com/xinhua/img/bj.png?v={$site.version|default='1.0.1'|htmlentities}');
             background-repeat: no-repeat;
             background-repeat: no-repeat;
             background-position: center center;
             background-position: center center;
-            background-size: 100% 100%;
-            color: #fff;
-            font-family: "Microsoft YaHei", "PingFang SC", sans-serif;
+            background-size: cover;
+            padding: clamp(28px, 4vh, 48px) clamp(28px, 4vw, 56px) clamp(20px, 3vh, 36px);
+        }
+
+        .login-brand::before {
+            content: "";
+            position: absolute;
+            inset: 0;
+            background: linear-gradient(180deg, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0.03) 48%, rgba(15, 35, 70, 0.08) 100%);
+            pointer-events: none;
+        }
+
+        /* 中间内容:图标标题在上,插画在下(参考图二) */
+        .login-brand-stage {
             position: relative;
             position: relative;
+            z-index: 2;
+            flex: 1 1 auto;
+            min-height: 0;
+            width: 100%;
+            max-width: 720px;
+            display: flex;
+            flex-direction: column;
+            align-items: center;
+            justify-content: center;
+            gap: 14px;
+            margin: 8px 0;
         }
         }
 
 
-        .login-top {
+        .login-brand-header {
             position: relative;
             position: relative;
             z-index: 2;
             z-index: 2;
-            padding: 12px 20px 0;
-            text-align: center;
+            display: flex;
+            flex-direction: row;
+            align-items: center;
+            justify-content: center;
+            text-align: left;
+            gap: 16px;
         }
         }
 
 
-        .login-top-brand {
-            display: inline-block;
-            margin: 0 auto;
-            width: min(460px, 58vw);
+        .login-brand-logo {
+            flex: 0 0 auto;
+            width: 64px;
             height: 64px;
             height: 64px;
-            background: url('https://a-7in6-com.oss-cn-hangzhou.aliyuncs.com/xinhua/img/login4.png?v={$site.version|default='1.0.1'|htmlentities}') no-repeat center center;
-            background-size: contain;
+            border-radius: 14px;
+            background: transparent;
+            box-shadow: none;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            overflow: hidden;
+        }
+
+        .login-brand-logo img {
+            width: 100%;
+            height: 100%;
+            object-fit: contain;
+        }
+
+        .login-brand-text {
+            min-width: 0;
+            text-align: left;
+        }
+
+        .login-brand-title {
+            margin: 0 0 6px;
+            font-size: clamp(24px, 2.6vw, 34px);
+            font-weight: 700;
+            letter-spacing: 3px;
+            line-height: 1.25;
+        }
+
+        .login-brand-sub {
+            margin: 0;
+            font-size: 12px;
+            letter-spacing: 3px;
+            color: rgba(210, 228, 255, 0.72);
+            text-transform: uppercase;
+        }
+
+        /* 插画区域 ch.png:缩小并放在标题下方 */
+        .login-brand-illust {
+            position: relative;
+            z-index: 1;
+            flex: 0 1 auto;
+            width: 100%;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+        }
+
+        .login-brand-illust img {
+            display: block;
+            max-width: min(360px, 46%);
+            max-height: min(26vh, 240px);
+            width: auto;
+            height: auto;
+            object-fit: contain;
             pointer-events: none;
             pointer-events: none;
             user-select: none;
             user-select: none;
+            opacity: 0.96;
         }
         }
 
 
-        .login-main {
+        .login-brand-features {
             position: relative;
             position: relative;
             z-index: 2;
             z-index: 2;
             display: flex;
             display: flex;
             align-items: center;
             align-items: center;
-            justify-content: space-between;
-            gap: clamp(16px, 2.5vw, 40px);
-            width: 100%;
-            max-width: 1760px;
-            margin: 0 auto;
-            min-height: calc(100vh - 76px);
-            max-height: calc(100vh - 76px);
-            padding: 0 clamp(32px, 4vw, 72px) 48px;
-            overflow: hidden;
+            justify-content: center;
+            gap: 0;
+            margin: 6px 0 0;
+            padding: 0;
+            list-style: none;
+            flex-shrink: 0;
         }
         }
 
 
-        .login-visual {
-            flex: 1 1 auto;
-            min-width: 0;
+        .login-brand-features li {
             display: flex;
             display: flex;
+            flex-direction: row;
             align-items: center;
             align-items: center;
-            justify-content: flex-start;
-            overflow: hidden;
+            gap: 10px;
+            min-width: 0;
+            padding: 0 22px;
+            font-size: 13px;
+            color: rgba(235, 244, 255, 0.92);
+            position: relative;
+            white-space: nowrap;
         }
         }
 
 
-        /* 左图:login2 画布留白多,用 cover 放大裁切;login3 可改 login-visual-art--fit-contain */
-        .login-visual-art {
-            display: block;
+        .login-brand-features li + li::before {
+            content: "";
+            position: absolute;
+            left: 0;
+            top: 50%;
+            transform: translateY(-50%);
+            width: 1px;
+            height: 22px;
+            background: rgba(180, 210, 255, 0.28);
+        }
+
+        .login-brand-features .feat-icon {
+            width: 30px;
+            height: 30px;
+            border-radius: 50%;
+            background: rgba(255, 255, 255, 0.1);
+            border: 1px solid rgba(210, 228, 255, 0.35);
+            display: inline-flex;
+            align-items: center;
+            justify-content: center;
+            color: #eef4ff;
+            font-size: 13px;
             flex-shrink: 0;
             flex-shrink: 0;
-            height: calc(100vh - 105px);
-            max-height: calc(100vh - 105px);
-            width: min(calc((100vh - 105px) * 16 / 9), calc(100vw - 500px));
-            min-width: 360px;
-            min-height: 260px;
-            object-fit: cover;
-            object-position: center center;
-            pointer-events: none;
-            user-select: none;
         }
         }
 
 
-        .login-visual-art.login-visual-art--fit-contain {
-            object-fit: contain;
+        .login-brand-footer {
+            position: relative;
+            z-index: 2;
+            text-align: center;
+            font-size: 12px;
+            color: rgba(235, 244, 255, 0.72);
         }
         }
 
 
-        .login-panel {
-            flex: 0 0 auto;
-            width: 100%;
-            max-width: 400px;
-            min-width: 300px;
-            margin-left: 0;
-            margin-right: clamp(24px, 4vw, 64px);
-            padding: 0;
-            align-self: center;
-            transform: translateY(0);
+        .login-brand-footer a {
+            color: rgba(220, 235, 255, 0.58);
         }
         }
 
 
-        .login-panel-head {
+        .login-brand-footer a:hover {
+            color: #fff;
+        }
+
+        /* ========== 右侧登录(按设计稿红框区域) ========== */
+        body.login-page .login-side {
+            position: relative;
+            z-index: 2;
+            flex: 0 0 40% !important;
+            width: 40% !important;
+            min-width: 380px !important;
+            max-width: 500px !important;
+            height: 100vh !important;
+            background: linear-gradient(180deg, #f8fbff 0%, #f1f5fb 100%) !important;
+            display: flex !important;
+            flex-direction: column !important;
+            justify-content: stretch !important;
+            align-items: stretch !important;
+            padding: 0 48px !important;
+            margin: 0 !important;
+            box-shadow: -4px 0 24px rgba(15, 23, 42, 0.06);
+            border-left: 1px solid #e2e8f0;
+        }
+
+        body.login-page .login-side-main {
             width: 100%;
             width: 100%;
+            max-width: 340px;
+            margin: 0 auto;
+            flex: 1 1 auto;
+            display: flex;
+            flex-direction: column;
+            justify-content: center;
+            padding: 64px 0 28px;
+        }
+
+        body.login-page .login-side-head {
+            margin-bottom: 36px;
+            margin-top: 12px;
             text-align: center;
             text-align: center;
         }
         }
 
 
-        .login-sys-title {
-            display: block;
-            width: 86%;
-            height: 32px;
-            margin: 0 auto;
-            background: url('https://a-7in6-com.oss-cn-hangzhou.aliyuncs.com/xinhua/img/login5.png?v={$site.version|default='1.0.1'|htmlentities}') no-repeat center center;
-            background-size: contain;
-            pointer-events: none;
-            user-select: none;
+        body.login-page .login-side-head h1 {
+            margin: 0 0 10px;
+            font-size: 32px !important;
+            font-weight: 700 !important;
+            color: #111827 !important;
+            letter-spacing: 0.5px;
+            line-height: 1.25;
+            text-align: center;
         }
         }
 
 
-        .login-panel-body {
-            margin-top: 28px;
+        body.login-page .login-side-head p {
+            margin: 0;
+            font-size: 13px !important;
+            color: #9ca3af !important;
+            line-height: 1.6;
+            text-align: center;
         }
         }
 
 
-        .login-divider-wrap {
+        body.login-page #login-form {
             width: 100%;
             width: 100%;
-            height: 15px;
             margin: 0;
             margin: 0;
-            background: url('https://a-7in6-com.oss-cn-hangzhou.aliyuncs.com/xinhua/img/login6.png?v={$site.version|default='1.0.1'|htmlentities}') no-repeat center 54%;
-            background-size: 100% auto;
+            padding: 0;
+            background: transparent !important;
+            box-shadow: none !important;
+            border: none !important;
+        }
+
+        body.login-page #login-form .login-field {
+            margin-bottom: 18px;
+        }
+
+        body.login-page #login-form .login-field-label {
+            display: block;
+            margin: 0 0 8px;
+            font-size: 14px;
+            font-weight: 500;
+            color: #111827;
+            line-height: 1.2;
+            text-align: left;
+        }
+
+        body.login-page #login-form .login-input {
+            position: relative;
+        }
+
+        body.login-page #login-form .login-input-icon {
+            position: absolute;
+            left: 12px;
+            top: 50%;
+            transform: translateY(-50%);
+            width: 16px;
+            height: 16px;
             pointer-events: none;
             pointer-events: none;
-            user-select: none;
+            z-index: 2;
+            display: flex;
+            align-items: center;
+            justify-content: center;
         }
         }
 
 
-        .login-panel-box {
-            margin-top: -11px;
-            border-top: none;
-            background: rgba(36, 98, 168, 0.34);
-            border: 1px solid rgba(120, 195, 255, 0.42);
-            box-shadow: 0 8px 32px rgba(8, 40, 90, 0.22), inset 0 0 24px rgba(120, 190, 255, 0.08);
-            backdrop-filter: blur(10px);
-            -webkit-backdrop-filter: blur(10px);
-            padding: 29px 24px 40px;
+        body.login-page #login-form .login-input-icon img {
+            display: block;
+            width: 16px;
+            height: 16px;
+            object-fit: contain;
         }
         }
 
 
-        .login-form-title {
-            margin: 0 0 18px;
-            text-align: center;
-            font-size: 18px;
-            font-weight: 600;
-            letter-spacing: 3px;
-            color: #f0f7ff;
+        body.login-page #login-form .form-control {
+            height: 40px !important;
+            border: 1px solid #e5e7eb !important;
+            border-radius: 8px !important;
+            box-shadow: none !important;
+            font-size: 13px !important;
+            color: #111827 !important;
+            background: #ffffff !important;
+            padding: 8px 14px 8px 36px !important;
+            transition: border-color .2s ease, box-shadow .2s ease;
         }
         }
 
 
-        #login-form {
-            width: 100%;
+        body.login-page #login-form .login-password-wrap .form-control {
+            padding-right: 40px !important;
         }
         }
 
 
-        #login-form .input-group {
-            margin-bottom: 14px;
-            width: 100%;
-            background: rgba(72, 145, 215, 0.38);
-            border-radius: 0;
-            overflow: hidden;
-            border: 1px solid rgba(150, 210, 255, 0.32);
-            backdrop-filter: blur(6px);
-            -webkit-backdrop-filter: blur(6px);
+        body.login-page #login-form .form-control:hover {
+            border-color: #d1d5db !important;
         }
         }
 
 
-        #login-form .input-group-addon {
-            background: transparent;
-            border: none;
-            color: rgba(215, 235, 255, 0.88);
-            padding: 0 12px;
-            min-width: 42px;
-            text-align: center;
+        body.login-page #login-form .form-control:focus {
+            border-color: #818cf8 !important;
+            box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.12) !important;
+            background: #ffffff !important;
+            outline: none !important;
         }
         }
 
 
-        #login-form .input-group-addon .glyphicon {
-            font-size: 16px;
-            top: 0;
+        body.login-page #login-form .form-control::placeholder {
+            color: #c0c6d0 !important;
         }
         }
 
 
-        #login-form .form-control {
-            height: 42px;
+        body.login-page #login-form .btn-toggle-password {
+            position: absolute;
+            right: 10px;
+            top: 50%;
+            transform: translateY(-50%);
             border: none;
             border: none;
-            box-shadow: none;
-            font-size: 14px;
-            color: #f2f8ff;
             background: transparent;
             background: transparent;
+            padding: 2px;
+            cursor: pointer;
+            line-height: 1;
+            z-index: 2;
+            width: 24px;
+            height: 24px;
+            display: flex;
+            align-items: center;
+            justify-content: center;
         }
         }
 
 
-        #login-form .form-control:focus {
-            box-shadow: none;
-            background: rgba(255, 255, 255, 0.06);
+        body.login-page #login-form .btn-toggle-password img {
+            display: block;
+            width: 16px;
+            height: 16px;
+            object-fit: contain;
         }
         }
 
 
-        #login-form .form-control::placeholder {
-            color: rgba(200, 225, 250, 0.72);
+        body.login-page #login-form .btn-toggle-password:hover {
+            opacity: 0.8;
         }
         }
 
 
-        #login-form .input-group .input-group-addon:last-child {
-            padding: 0;
-            border: none;
+        body.login-page #login-form .login-captcha-row {
+            display: flex;
+            gap: 10px;
+            align-items: stretch;
         }
         }
 
 
-        #login-form .btn-login-submit {
-            margin-top: 6px;
-            height: 44px;
-            width: 100%;
-            border: none;
-            border-radius: 0;
-            background: linear-gradient(180deg, #45a8ff 0%, #1a72ea 100%);
-            color: #fff;
-            font-size: 16px;
-            font-weight: 600;
-            letter-spacing: 6px;
-            box-shadow: 0 4px 12px rgba(26, 111, 232, 0.4);
+        body.login-page #login-form .login-captcha-row .login-input {
+            flex: 1 1 auto;
+            min-width: 0;
         }
         }
 
 
-        #login-form .btn-login-submit:hover,
-        #login-form .btn-login-submit:focus {
-            background: linear-gradient(180deg, #52abff 0%, #2580f0 100%);
-            color: #fff;
+        body.login-page #login-form .login-captcha-img {
+            flex: 0 0 auto;
+            height: 40px;
+            border-radius: 8px;
+            border: 1px solid #e5e7eb;
+            overflow: hidden;
+            background: #fff;
+            cursor: pointer;
         }
         }
 
 
-        .login-form-links {
+        body.login-page #login-form .login-captcha-img img {
+            display: block;
+            height: 100%;
+            width: auto;
+            min-width: 110px;
+        }
+
+        body.login-page .login-form-extra {
             display: flex;
             display: flex;
             justify-content: space-between;
             justify-content: space-between;
             align-items: center;
             align-items: center;
-            margin-top: 12px;
-            padding: 0 4px;
-            font-size: 12px;
+            margin: 4px 0 22px;
+            font-size: 13px;
         }
         }
 
 
-        .login-form-links a {
-            color: rgba(195, 218, 245, 0.85);
-            text-decoration: none;
+        body.login-page .login-form-extra label {
+            margin: 0 !important;
+            font-weight: normal !important;
+            color: #6b7280 !important;
+            cursor: pointer;
+            display: inline-flex;
+            align-items: center;
+            gap: 8px;
+            user-select: none;
         }
         }
 
 
-        .login-form-links a:hover {
-            color: #fff;
-            text-decoration: underline;
+        body.login-page .login-form-extra input[type="checkbox"] {
+            margin: 0;
+            width: 15px;
+            height: 15px;
+            accent-color: #4f46e5;
+            vertical-align: middle;
         }
         }
 
 
-        .login-footer {
-            position: absolute;
-            left: 0;
-            right: 0;
-            bottom: 0;
-            z-index: 2;
-            padding: 14px 20px 18px;
+        body.login-page .login-form-extra .login-forgot {
+            color: #9ca3af !important;
+            text-decoration: none !important;
+        }
+
+        body.login-page .login-form-extra .login-forgot:hover {
+            color: #6b7280 !important;
+            text-decoration: underline !important;
+        }
+
+        body.login-page #login-form .form-group {
+            margin-bottom: 0 !important;
+        }
+
+        body.login-page #login-form .btn-login-submit {
+            height: 42px !important;
+            width: 100% !important;
+            border: none !important;
+            border-radius: 8px !important;
+            background: linear-gradient(90deg, #3b82f6 0%, #6366f1 45%, #8b5cf6 100%) !important;
+            color: #fff !important;
+            font-size: 16px !important;
+            font-weight: 600 !important;
+            letter-spacing: 3px;
+            box-shadow: 0 12px 28px rgba(99, 102, 241, 0.32) !important;
+            transition: transform .15s ease, box-shadow .15s ease, filter .15s ease;
+            display: inline-flex !important;
+            align-items: center;
+            justify-content: center;
+            gap: 10px;
+            text-shadow: none !important;
+            line-height: 1 !important;
+        }
+
+        body.login-page #login-form .btn-login-submit:hover,
+        body.login-page #login-form .btn-login-submit:focus {
+            color: #fff !important;
+            filter: brightness(1.05);
+            box-shadow: 0 14px 32px rgba(99, 102, 241, 0.4) !important;
+            transform: translateY(-1px);
+            background: linear-gradient(90deg, #3b82f6 0%, #6366f1 45%, #8b5cf6 100%) !important;
+        }
+
+        body.login-page #login-form .btn-login-submit:active {
+            transform: translateY(0);
+        }
+
+        body.login-page #errtips {
+            margin-bottom: 14px;
+        }
+
+        body.login-page .login-side-register {
+            margin-top: 22px;
             text-align: center;
             text-align: center;
             font-size: 13px;
             font-size: 13px;
-            color: rgba(220, 235, 255, 0.82);
         }
         }
 
 
-        .login-footer a {
-            color: rgba(220, 235, 255, 0.82);
+        body.login-page .login-side-register a {
+            color: #374151 !important;
+            text-decoration: none !important;
         }
         }
 
 
-        .login-footer a:hover {
-            color: #fff;
+        body.login-page .login-side-register a:hover {
+            color: #4f46e5 !important;
+            text-decoration: none !important;
+        }
+
+        body.login-page .login-side-company {
+            flex: 0 0 auto;
+            padding: 0 0 28px;
+            text-align: center;
+            font-size: 12px;
+            color: #9ca3af;
+            letter-spacing: 0.2px;
+        }
+
+        /* 覆盖 FastAdmin 默认灰色登录底 */
+        body.login-page {
+            background: #0a1630 !important;
+        }
+
+        body.login-page .wrapper,
+        body.login-page .content-wrapper,
+        body.login-page .login-box,
+        body.login-page .login-box-body {
+            background: transparent !important;
+            box-shadow: none !important;
+            margin: 0 !important;
+            padding: 0 !important;
+            width: auto !important;
         }
         }
 
 
-        @media (max-width: 1100px) {
+        @media (max-width: 960px) {
             html, body {
             html, body {
                 overflow-x: hidden;
                 overflow-x: hidden;
                 overflow-y: auto;
                 overflow-y: auto;
@@ -275,58 +531,63 @@
                 height: auto;
                 height: auto;
                 min-height: 100vh;
                 min-height: 100vh;
                 max-height: none;
                 max-height: none;
+                flex-direction: column;
                 overflow-x: hidden;
                 overflow-x: hidden;
                 overflow-y: auto;
                 overflow-y: auto;
             }
             }
 
 
-            .login-main {
-                flex-direction: column;
-                justify-content: flex-start;
-                align-items: center;
-                padding: 16px 24px 96px;
-                min-height: auto;
-                max-height: none;
-                overflow: visible;
+            .login-brand {
+                flex: 0 0 auto;
+                min-height: 40vh;
+                padding: 24px 20px 16px;
             }
             }
 
 
-            .login-visual {
-                flex: 0 0 auto;
-                width: 100%;
-                justify-content: center;
-                overflow: visible;
+            .login-brand-illust {
+                min-height: 110px;
             }
             }
 
 
-            .login-visual-art {
-                width: 100%;
-                max-width: 900px;
-                height: auto;
-                aspect-ratio: 16 / 9;
-                min-height: 280px;
-                max-height: none;
-                margin: 0 auto;
-                object-fit: cover;
-                object-position: center center;
+            .login-brand-illust img {
+                max-width: min(280px, 72%);
+                max-height: 180px;
+            }
+
+            .login-brand-features li {
+                min-width: 88px;
+                padding: 0 14px;
+                font-size: 12px;
+            }
+
+            body.login-page .login-side {
+                flex: 1 1 auto !important;
+                width: 100% !important;
+                max-width: none !important;
+                min-width: 0 !important;
+                height: auto !important;
+                min-height: 60vh;
+                padding: 0 24px !important;
+                border-radius: 20px 20px 0 0;
+            }
+
+            body.login-page .login-side-main {
+                padding: 36px 0 20px;
             }
             }
 
 
-            .login-panel {
-                max-width: 460px;
-                margin-left: 0;
-                margin-right: 0;
-                transform: none;
+            body.login-page .login-side-company {
+                padding-bottom: 24px;
             }
             }
         }
         }
 
 
         @media (max-width: 480px) {
         @media (max-width: 480px) {
-            .login-top {
-                padding-top: 16px;
+            .login-brand-features {
+                display: none;
             }
             }
 
 
-            .login-panel-box {
-                padding: 22px 18px 18px;
+            .login-side {
+                padding: 28px 18px 24px;
             }
             }
 
 
-            .login-form-title {
-                font-size: 18px;
+            .login-side-head h1 {
+                font-size: 24px;
             }
             }
         }
         }
     </style>
     </style>
@@ -336,82 +597,200 @@
                 e.preventDefault();
                 e.preventDefault();
             }
             }
         }, {passive: false});
         }, {passive: false});
-
-        document.addEventListener('gesturestart', function (e) {
-            e.preventDefault();
-        });
-        document.addEventListener('gesturechange', function (e) {
-            e.preventDefault();
-        });
-        document.addEventListener('gestureend', function (e) {
-            e.preventDefault();
-        });
+        document.addEventListener('gesturestart', function (e) { e.preventDefault(); });
+        document.addEventListener('gesturechange', function (e) { e.preventDefault(); });
+        document.addEventListener('gestureend', function (e) { e.preventDefault(); });
     </script>
     </script>
 </head>
 </head>
 <body class="login-page">
 <body class="login-page">
-<header class="login-top">
-    <div class="login-top-brand" role="img" aria-label="浙江新华数码印务有限公司"></div>
-</header>
-
-<main class="login-main">
-    <div class="login-visual">
-        <img class="login-visual-art" src="https://a-7in6-com.oss-cn-hangzhou.aliyuncs.com/xinhua/img/login2.png?v={$site.version|default='1.0.1'|htmlentities}" alt=""/>
-    </div>
-
-    <div class="login-panel">
-        <div class="login-panel-head">
-            <div class="login-sys-title" role="img" aria-label="大数据协同系统"></div>
+    <section class="login-brand">
+        <div class="login-brand-stage">
+            <div class="login-brand-header">
+                <div class="login-brand-logo">
+                    <img src="https://a-7in6-com.oss-cn-hangzhou.aliyuncs.com/xinhua/img/tb.png?v={$site.version|default='1.0.1'|htmlentities}" alt="大数据协同系统"/>
+                </div>
+                <div class="login-brand-text">
+                    <h2 class="login-brand-title">大数据协同系统</h2>
+                    <p class="login-brand-sub">Big Data Collaborative System</p>
+                </div>
+            </div>
+
+            <div class="login-brand-illust">
+                <img src="https://a-7in6-com.oss-cn-hangzhou.aliyuncs.com/xinhua/img/ch.png?v={$site.version|default='1.0.1'|htmlentities}" alt=""/>
+            </div>
+
+            <ul class="login-brand-features">
+                <li>
+                    <span class="feat-icon"><i class="glyphicon glyphicon-dashboard"></i></span>
+                    <span>数据看板</span>
+                </li>
+                <li>
+                    <span class="feat-icon"><i class="glyphicon glyphicon-time"></i></span>
+                    <span>流程管控</span>
+                </li>
+                <li>
+                    <span class="feat-icon"><i class="glyphicon glyphicon-cog"></i></span>
+                    <span>生产协同</span>
+                </li>
+            </ul>
+        </div>
+
+        <div class="login-brand-footer">
+            Copyright @ 杭州可集达科技有限公司 2026-{:date('Y',time())} 版权所有
+            {if $site.beian}<a href="https://beian.miit.gov.cn" target="_blank"> {$site.beian|htmlentities}</a>{/if}
         </div>
         </div>
+    </section>
 
 
-        <div class="login-panel-body">
-            <div class="login-divider-wrap" aria-hidden="true"></div>
+    <aside class="login-side">
+        <div class="login-side-main">
+            <div class="login-side-head">
+                <h1>欢迎回来</h1>
+                <p>请输入账户登录大数据协同平台</p>
+            </div>
 
 
-            <div class="login-panel-box">
-            <h2 class="login-form-title">用户登录</h2>
             <form action="" method="post" id="login-form">
             <form action="" method="post" id="login-form">
                 <!--@AdminLoginFormBegin-->
                 <!--@AdminLoginFormBegin-->
                 <div id="errtips" class="hide"></div>
                 <div id="errtips" class="hide"></div>
                 {:token()}
                 {:token()}
-                <div class="input-group">
-                    <div class="input-group-addon">
-                        <span class="glyphicon glyphicon-user" aria-hidden="true"></span>
+
+                <div class="login-field">
+                    <div class="login-input">
+                        <span class="login-input-icon">
+                            <img src="https://a-7in6-com.oss-cn-hangzhou.aliyuncs.com/xinhua/img/5.png?v={$site.version|default='1.0.1'|htmlentities}" alt=""/>
+                        </span>
+                        <input type="text" class="form-control" id="pd-form-username" placeholder="请输入账号" name="username" autocomplete="username" value="" data-rule="{:__('Username')}:required;username"/>
                     </div>
                     </div>
-                    <input type="text" class="form-control" id="pd-form-username" placeholder="请输入用户名" name="username" autocomplete="off" value="" data-rule="{:__('Username')}:required;username"/>
                 </div>
                 </div>
 
 
-                <div class="input-group">
-                    <div class="input-group-addon">
-                        <span class="glyphicon glyphicon-lock" aria-hidden="true"></span>
+                <div class="login-field">
+                    <div class="login-input login-password-wrap">
+                        <span class="login-input-icon">
+                            <img src="https://a-7in6-com.oss-cn-hangzhou.aliyuncs.com/xinhua/img/6.png?v={$site.version|default='1.0.1'|htmlentities}" alt=""/>
+                        </span>
+                        <input type="password" class="form-control" id="pd-form-password" placeholder="请输入密码" name="password" autocomplete="current-password" value="" data-rule="{:__('Password')}:required;length(4~30)"/>
+                        <button type="button" class="btn-toggle-password" id="btn-toggle-password" title="显示/隐藏密码" aria-label="显示/隐藏密码"
+                                data-eye-open="https://a-7in6-com.oss-cn-hangzhou.aliyuncs.com/xinhua/img/11.png?v={$site.version|default='1.0.1'|htmlentities}"
+                                data-eye-close="https://a-7in6-com.oss-cn-hangzhou.aliyuncs.com/xinhua/img/10.png?v={$site.version|default='1.0.1'|htmlentities}">
+                            <img src="https://a-7in6-com.oss-cn-hangzhou.aliyuncs.com/xinhua/img/11.png?v={$site.version|default='1.0.1'|htmlentities}" alt="显示密码"/>
+                        </button>
                     </div>
                     </div>
-                    <input type="password" class="form-control" id="pd-form-password" placeholder="请输入登录密码" name="password" autocomplete="off" value="" data-rule="{:__('Password')}:required;length(4~30)"/>
                 </div>
                 </div>
 
 
                 {if $Think.config.fastadmin.login_captcha}
                 {if $Think.config.fastadmin.login_captcha}
-                <div class="input-group">
-                    <div class="input-group-addon">
-                        <span class="glyphicon glyphicon-option-horizontal" aria-hidden="true"></span>
+                <div class="login-field">
+                    <div class="login-captcha-row">
+                        <div class="login-input">
+                            <span class="login-input-icon"><span class="glyphicon glyphicon-option-horizontal" aria-hidden="true"></span></span>
+                            <input type="text" id="pd-form-captcha" name="captcha" class="form-control" placeholder="请输入验证码" data-rule="{:__('Captcha')}:required;length({$Think.config.captcha.length})" autocomplete="off"/>
+                        </div>
+                        <span class="login-captcha-img" title="点击刷新验证码">
+                            <img src="{:rtrim('__PUBLIC__', '/')}/index.php?s=/captcha" height="40" alt="captcha" onclick="this.src = '{:rtrim('__PUBLIC__', '/')}/index.php?s=/captcha&r=' + Math.random();"/>
+                        </span>
                     </div>
                     </div>
-                    <input type="text" name="captcha" class="form-control" placeholder="{:__('Captcha')}" data-rule="{:__('Captcha')}:required;length({$Think.config.captcha.length})" autocomplete="off"/>
-                    <span class="input-group-addon" style="cursor:pointer;">
-                        <img src="{:rtrim('__PUBLIC__', '/')}/index.php?s=/captcha" width="100" height="30" alt="captcha" onclick="this.src = '{:rtrim('__PUBLIC__', '/')}/index.php?s=/captcha&r=' + Math.random();"/>
-                    </span>
                 </div>
                 </div>
                 {/if}
                 {/if}
 
 
+                <div class="login-form-extra">
+                    <label>
+                        <input type="checkbox" id="remember-password" name="rememberpwd" value="1"/>
+                        记住密码
+                    </label>
+                    <a href="javascript:;" class="login-forgot" onclick="Layer && Layer.msg ? Layer.msg('请联系管理员重置密码') : alert('请联系管理员重置密码'); return false;">忘记密码?</a>
+                </div>
+
                 <div class="form-group" style="margin-bottom:0;">
                 <div class="form-group" style="margin-bottom:0;">
-                    <button type="submit" class="btn btn-login-submit btn-lg btn-block">登录</button>
+                    <button type="submit" class="btn btn-login-submit btn-lg btn-block">
+                        登录
+                        <span aria-hidden="true">→</span>
+                    </button>
                 </div>
                 </div>
                 <!--@AdminLoginFormEnd-->
                 <!--@AdminLoginFormEnd-->
             </form>
             </form>
         </div>
         </div>
-        </div>
-    </div>
-</main>
 
 
-<footer class="login-footer">
-    <p style="margin:0;">Copyright @ 杭州可集达科技有限公司 2026-{:date('Y',time())} 版权所有 <a href="https://beian.miit.gov.cn" target="_blank">{$site.beian|htmlentities}</a></p>
-</footer>
+        <div class="login-side-company">浙江新华数码印务有限公司</div>
+    </aside>
 
 
 {include file="common/script" /}
 {include file="common/script" /}
+<script>
+(function () {
+    var REMEMBER_KEY = 'xh_admin_remember_pwd';
+
+    function loadRemember() {
+        var $user = document.getElementById('pd-form-username');
+        var $pass = document.getElementById('pd-form-password');
+        var $cb = document.getElementById('remember-password');
+        if (!$user || !$pass || !$cb) {
+            return;
+        }
+        try {
+            var raw = localStorage.getItem(REMEMBER_KEY);
+            if (!raw) {
+                return;
+            }
+            var data = JSON.parse(raw);
+            if (data && data.username) {
+                $user.value = data.username;
+            }
+            if (data && data.password) {
+                $pass.value = data.password;
+            }
+            $cb.checked = true;
+        } catch (e) {
+        }
+    }
+
+    function saveRemember() {
+        var $user = document.getElementById('pd-form-username');
+        var $pass = document.getElementById('pd-form-password');
+        var $cb = document.getElementById('remember-password');
+        if (!$user || !$pass || !$cb) {
+            return;
+        }
+        if ($cb.checked) {
+            localStorage.setItem(REMEMBER_KEY, JSON.stringify({
+                username: $user.value || '',
+                password: $pass.value || ''
+            }));
+        } else {
+            localStorage.removeItem(REMEMBER_KEY);
+        }
+    }
+
+    loadRemember();
+
+    var form = document.getElementById('login-form');
+    if (form) {
+        form.addEventListener('submit', function () {
+            saveRemember();
+        });
+    }
+    var cb = document.getElementById('remember-password');
+    if (cb) {
+        cb.addEventListener('change', function () {
+            if (!cb.checked) {
+                localStorage.removeItem(REMEMBER_KEY);
+            }
+        });
+    }
+
+    var btn = document.getElementById('btn-toggle-password');
+    var input = document.getElementById('pd-form-password');
+    if (!btn || !input) {
+        return;
+    }
+    var eyeOpen = btn.getAttribute('data-eye-open') || '';
+    var eyeClose = btn.getAttribute('data-eye-close') || '';
+    btn.addEventListener('click', function () {
+        var show = input.getAttribute('type') === 'password';
+        input.setAttribute('type', show ? 'text' : 'password');
+        var img = btn.querySelector('img');
+        if (img) {
+            img.src = show ? eyeClose : eyeOpen;
+            img.alt = show ? '隐藏密码' : '显示密码';
+        }
+    });
+})();
+</script>
 </body>
 </body>
 </html>
 </html>

+ 151 - 20
application/admin/view/procuremen/index.html

@@ -60,35 +60,38 @@
         align-self: stretch;
         align-self: stretch;
         min-height: 0;
         min-height: 0;
         overflow-y: auto;
         overflow-y: auto;
-        border-right: 1px solid #e7e7e7;
+        border-right: 1px solid #f0f0f0;
         background: #fafafa;
         background: #fafafa;
     }
     }
     /* 左侧年月栏:桌面固定约 92px,略宽于纯数字月份便于显示「2026年」 */
     /* 左侧年月栏:桌面固定约 92px,略宽于纯数字月份便于显示「2026年」 */
     .procuremen-sidebar .year-title {
     .procuremen-sidebar .year-title {
         font-weight: 600;
         font-weight: 600;
-        padding: 6px 8px;
-        margin: 6px 0 2px;
-        background: #eee;
-        border-radius: 3px;
+        padding: 8px 6px 4px;
+        margin: 4px 0 2px;
+        background: transparent;
+        border-radius: 0;
         font-size: 12px;
         font-size: 12px;
+        color: #8c8c8c;
     }
     }
     .procuremen-sidebar .procuremen-ym-item {
     .procuremen-sidebar .procuremen-ym-item {
         display: block;
         display: block;
-        padding: 6px 8px;
-        margin: 1px 0;
-        color: #333;
-        border-radius: 3px;
+        padding: 7px 6px;
+        margin: 2px 4px;
+        color: #595959;
+        border-radius: 6px;
         text-decoration: none;
         text-decoration: none;
-        border: 1px solid transparent;
+        border: none;
+        font-size: 13px;
+        text-align: center;
     }
     }
     .procuremen-sidebar .procuremen-ym-item:hover {
     .procuremen-sidebar .procuremen-ym-item:hover {
-        background: #e8f4fc;
-        border-color: #bce8f1;
+        background: #f0f0f0;
+        color: #1f1f1f;
     }
     }
     .procuremen-sidebar .procuremen-ym-item.active {
     .procuremen-sidebar .procuremen-ym-item.active {
-        background: #3c8dbc;
-        color: #fff;
-        border-color: #367fa9;
+        background: #e6f4ff;
+        color: #1677ff;
+        font-weight: 500;
     }
     }
     #procuremen-layout .procuremen-main .widget-body {
     #procuremen-layout .procuremen-main .widget-body {
         overflow-x: hidden;
         overflow-x: hidden;
@@ -115,9 +118,9 @@
     }
     }
     /* JS 把 .fixed-table-toolbar 挪到此处:上一行按钮,下一行 tabs */
     /* JS 把 .fixed-table-toolbar 挪到此处:上一行按钮,下一行 tabs */
     .procuremen-toolbar-host {
     .procuremen-toolbar-host {
-        padding: 6px 12px 8px 0;
+        padding: 10px 12px 8px 0;
         margin-bottom: 0;
         margin-bottom: 0;
-        border-bottom: 1px solid #e7e7e7;
+        border-bottom: 1px solid #f0f0f0;
         width: 100%;
         width: 100%;
         box-sizing: border-box;
         box-sizing: border-box;
         overflow: visible;
         overflow: visible;
@@ -211,10 +214,24 @@
     .procuremen-wff-tabs {
     .procuremen-wff-tabs {
         margin-bottom: 0;
         margin-bottom: 0;
         margin-top: 0;
         margin-top: 0;
-        border-bottom: 1px solid #ddd;
+        border-bottom: 1px solid #f0f0f0;
+        background: #fff;
     }
     }
     .procuremen-wff-tabs > li > a {
     .procuremen-wff-tabs > li > a {
-        padding: 8px 14px;
+        padding: 10px 16px;
+        color: #8c8c8c;
+        border: none !important;
+        border-bottom: 2px solid transparent !important;
+        border-radius: 0 !important;
+        margin-right: 0;
+    }
+    .procuremen-wff-tabs > li.active > a,
+    .procuremen-wff-tabs > li.active > a:hover,
+    .procuremen-wff-tabs > li.active > a:focus {
+        color: #1677ff !important;
+        background: transparent !important;
+        border: none !important;
+        border-bottom: 2px solid #1677ff !important;
     }
     }
     #procuremen-layout .bootstrap-table .table > thead > tr > th,
     #procuremen-layout .bootstrap-table .table > thead > tr > th,
     #procuremen-layout .bootstrap-table .table > tbody > tr > td {
     #procuremen-layout .bootstrap-table .table > tbody > tr > td {
@@ -354,7 +371,121 @@
     #procuremen-layout .bootstrap-table thead th .th-inner.sortable.desc:after {
     #procuremen-layout .bootstrap-table thead th .th-inner.sortable.desc:after {
         color: #337ab7;
         color: #337ab7;
     }
     }
-    /* 完结确认弹层:与「已完结」卡片、完结按钮同为绿色 */
+    /* 采购确认弹层(完结/删除等) */
+    body .layui-layer-procuremen-finish {
+        display: flex !important;
+        flex-direction: column !important;
+        height: 400px !important;
+        border-radius: 8px;
+        overflow: hidden;
+        box-shadow: 0 8px 28px rgba(0, 0, 0, 0.12);
+    }
+    body .layui-layer-procuremen-finish .layui-layer-title {
+        background: #fff !important;
+        color: #1f1f1f !important;
+        border-bottom: 1px solid #f0f0f0;
+        height: 48px;
+        line-height: 48px;
+        font-size: 15px;
+        font-weight: 600;
+        flex-shrink: 0;
+        padding: 0 20px;
+    }
+    body .layui-layer-procuremen-finish .layui-layer-setwin {
+        top: 2px;
+        right: 12px;
+    }
+    body .layui-layer-procuremen-finish .layui-layer-content {
+        flex: 1 1 auto !important;
+        min-height: 0 !important;
+        padding: 16px 20px !important;
+        overflow: hidden !important;
+        height: auto !important;
+        display: flex !important;
+        flex-direction: column !important;
+    }
+    body .layui-layer-procuremen-finish .procuremen-confirm-body {
+        flex: 1 1 auto;
+        min-height: 0;
+        display: flex;
+        flex-direction: column;
+        text-align: left;
+        line-height: 1.6;
+        font-size: 13px;
+        color: #434343;
+    }
+    body .layui-layer-procuremen-finish .procuremen-confirm-intro {
+        margin: 0 0 12px;
+        flex-shrink: 0;
+    }
+    body .layui-layer-procuremen-finish .procuremen-confirm-scroll {
+        flex: 1 1 auto;
+        min-height: 0;
+        overflow-y: auto;
+        overflow-x: hidden;
+        border: 1px solid #f0f0f0;
+        border-radius: 6px;
+        background: #fafafa;
+    }
+    body .layui-layer-procuremen-finish .procuremen-confirm-table {
+        margin: 0;
+        font-size: 12px;
+        border: none;
+        background: #fff;
+        width: 100%;
+        table-layout: fixed;
+    }
+    body .layui-layer-procuremen-finish .procuremen-confirm-table th:nth-child(1),
+    body .layui-layer-procuremen-finish .procuremen-confirm-table td:nth-child(1) {
+        width: 150px;
+        white-space: nowrap;
+    }
+    body .layui-layer-procuremen-finish .procuremen-confirm-table th:nth-child(2),
+    body .layui-layer-procuremen-finish .procuremen-confirm-table td:nth-child(2) {
+        width: 140px;
+    }
+    body .layui-layer-procuremen-finish .procuremen-confirm-table th:nth-child(3),
+    body .layui-layer-procuremen-finish .procuremen-confirm-table td:nth-child(3) {
+        width: auto;
+    }
+    body .layui-layer-procuremen-finish .procuremen-confirm-table thead th {
+        position: sticky;
+        top: 0;
+        background: #f5f5f5;
+        z-index: 1;
+        border-bottom: 1px solid #f0f0f0;
+        color: #595959;
+        font-weight: 500;
+    }
+    body .layui-layer-procuremen-finish .procuremen-confirm-table tbody td {
+        border-color: #f5f5f5;
+        color: #434343;
+    }
+    body .layui-layer-procuremen-finish .layui-layer-btn {
+        flex-shrink: 0 !important;
+        text-align: right !important;
+        padding: 12px 20px !important;
+        background: #fff !important;
+        border-top: 1px solid #f0f0f0;
+        margin-top: auto;
+    }
+    body .layui-layer-procuremen-finish .layui-layer-btn a {
+        height: 32px;
+        line-height: 32px;
+        margin: 0 0 0 8px;
+        padding: 0 16px;
+        font-size: 13px;
+        border-radius: 4px;
+    }
+    body .layui-layer-procuremen-finish .layui-layer-btn1 {
+        background: #fff !important;
+        border: 1px solid #d9d9d9 !important;
+        color: #333 !important;
+    }
+    body .layui-layer-procuremen-finish .layui-layer-btn1:hover {
+        border-color: #1677ff !important;
+        color: #1677ff !important;
+    }
     body .layui-layer-procuremen-finish .layui-layer-btn0 {
     body .layui-layer-procuremen-finish .layui-layer-btn0 {
         background-color: #27C24C !important;
         background-color: #27C24C !important;
         border-color: #23ad44 !important;
         border-color: #23ad44 !important;

+ 1 - 1
application/admin/view/procuremen/rfqlist.html

@@ -4,7 +4,7 @@
     <div class="panel-body">
     <div class="panel-body">
         <div class="widget-body no-padding">
         <div class="widget-body no-padding">
             <div id="toolbar" class="toolbar">
             <div id="toolbar" class="toolbar">
-                {:build_toolbar('refresh')}
+                <a href="javascript:;" class="btn btn-primary btn-refresh" title="{:__('Refresh')}"><i class="fa fa-refresh"></i> 刷新</a>
             </div>
             </div>
             <table id="table" class="table table-striped table-bordered table-hover table-nowrap" width="100%"></table>
             <table id="table" class="table table-striped table-bordered table-hover table-nowrap" width="100%"></table>
         </div>
         </div>

+ 1 - 1
application/admin/view/procuremenarchive/index.html

@@ -4,7 +4,7 @@
     <div class="panel-body">
     <div class="panel-body">
         <div class="widget-body no-padding">
         <div class="widget-body no-padding">
             <div id="toolbar" class="toolbar">
             <div id="toolbar" class="toolbar">
-                {:build_toolbar('refresh')}
+                <a href="javascript:;" class="btn btn-primary btn-refresh" title="{:__('Refresh')}"><i class="fa fa-refresh"></i> 刷新</a>
                 {if $canArchiveAbandon}
                 {if $canArchiveAbandon}
                 <a href="javascript:;" class="btn btn-danger" id="btn-archive-abandon" title="勾选一条或多条已完结订单退回协助初选重新下发"><i class="fa fa-repeat"></i> 重新下发</a>
                 <a href="javascript:;" class="btn btn-danger" id="btn-archive-abandon" title="勾选一条或多条已完结订单退回协助初选重新下发"><i class="fa fa-repeat"></i> 重新下发</a>
                 {/if}
                 {/if}

+ 12 - 0
application/admin/view/purchasecontent/index.html

@@ -1,5 +1,17 @@
 <div class="panel panel-default panel-intro">
 <div class="panel panel-default panel-intro">
     {:build_heading()}
     {:build_heading()}
+    <style>
+        .purchase-content-page #table tbody tr.purchase-content-row-read > td {
+            background-color: #f5f5f5 !important;
+            color: #8c8c8c !important;
+        }
+        .purchase-content-page #table tbody tr.purchase-content-row-read:hover > td {
+            background-color: #efefef !important;
+        }
+        .purchase-content-page #table tbody tr.purchase-content-row-read .text-warning {
+            color: #8c8c8c !important;
+        }
+    </style>
 
 
     <div class="panel-body purchase-content-page">
     <div class="panel-body purchase-content-page">
         <div class="widget-body no-padding">
         <div class="widget-body no-padding">

+ 2 - 2
application/config.php

@@ -291,8 +291,8 @@ return [
         'multipletab'           => true,
         'multipletab'           => true,
         //是否默认展示子菜单
         //是否默认展示子菜单
         'show_submenu'          => false,
         'show_submenu'          => false,
-        //后台皮肤,为空时表示使用skin-black-blue
-        'adminskin'             => '',
+        //后台皮肤,为空时表示使用skin-blue-light
+        'adminskin'             => 'skin-blue-light',
         //后台是否启用面包屑
         //后台是否启用面包屑
         'breadcrumb'            => false,
         'breadcrumb'            => false,
         //是否允许未知来源的插件压缩包
         //是否允许未知来源的插件压缩包

+ 2 - 2
application/extra/site.php

@@ -1,11 +1,11 @@
 <?php
 <?php
 
 
 return array (
 return array (
-  'name' => '浙江印刷集团有限公司',
+  'name' => '',
   'brand_logo' => 'https://a-7in6-com.oss-cn-hangzhou.aliyuncs.com/xinhua/img/logo1.png',
   'brand_logo' => 'https://a-7in6-com.oss-cn-hangzhou.aliyuncs.com/xinhua/img/logo1.png',
   'beian' => '',
   'beian' => '',
   'cdnurl' => '',
   'cdnurl' => '',
-  'version' => '1.0.4',
+  'version' => '1.4.1',
   'timezone' => 'Asia/Shanghai',
   'timezone' => 'Asia/Shanghai',
   'forbiddenip' => '',
   'forbiddenip' => '',
   'languages' => 
   'languages' => 

+ 328 - 0
public/assets/css/xinhua-theme.css

@@ -0,0 +1,328 @@
+/*
+ * 新华 ERP — 简洁后台主题
+ */
+.skin-blue-light .main-header .logo,
+.skin-blue-light .main-header .navbar {
+    background: #ffffff !important;
+    color: #1f1f1f !important;
+    border-bottom: 1px solid #f0f0f0;
+    box-shadow: none !important;
+}
+
+.skin-blue-light .main-header .logo {
+    border-right: 1px solid #f0f0f0 !important;
+    font-weight: 600;
+    font-size: 15px !important;
+}
+
+.skin-blue-light .main-header .logo:hover {
+    background: #fafafa !important;
+}
+
+.skin-blue-light .main-header .navbar .nav > li > a,
+.skin-blue-light .main-header .navbar .sidebar-toggle {
+    color: #595959 !important;
+}
+
+.skin-blue-light .main-header .navbar .nav > li > a:hover,
+.skin-blue-light .main-header .navbar .nav > li > a:focus,
+.skin-blue-light .main-header .navbar .sidebar-toggle:hover {
+    background: #f5f5f5 !important;
+    color: #1f1f1f !important;
+}
+
+.skin-blue-light .wrapper {
+    background: #f5f6f8 !important;
+}
+
+.skin-blue-light .main-sidebar,
+.skin-blue-light .left-side {
+    background: #ffffff !important;
+}
+
+.skin-blue-light .main-sidebar {
+    border-right: 1px solid #f0f0f0;
+    box-shadow: none !important;
+}
+
+.skin-blue-light .content-wrapper,
+.skin-blue-light .main-footer {
+    background: #f5f6f8 !important;
+    border-left: none !important;
+}
+
+.skin-blue-light .sidebar .user-panel {
+    border-bottom: 1px solid #f5f5f5;
+    padding-bottom: 12px;
+    margin-bottom: 6px;
+}
+
+.skin-blue-light .sidebar-menu > li.header {
+    background: transparent !important;
+    color: #bfbfbf !important;
+    font-size: 12px;
+}
+
+.skin-blue-light .sidebar-menu > li > a {
+    border-radius: 6px;
+    margin: 1px 8px;
+    padding: 10px 12px !important;
+    color: #434343 !important;
+    border-left: none !important;
+}
+
+.skin-blue-light .sidebar-menu > li:hover > a {
+    background: #f5f5f5 !important;
+    color: #1f1f1f !important;
+}
+
+.skin-blue-light .sidebar-menu > li.active > a {
+    background: #e6f4ff !important;
+    color: #1677ff !important;
+    font-weight: 500;
+    box-shadow: none !important;
+}
+
+.skin-blue-light .treeview-menu > li > a {
+    color: #8c8c8c !important;
+}
+
+.skin-blue-light .treeview-menu > li.active > a,
+.skin-blue-light .treeview-menu > li > a:hover {
+    color: #1677ff !important;
+}
+
+.skin-blue-light .sidebar-menu > li > .treeview-menu {
+    background: transparent !important;
+}
+
+.skin-blue-light .sidebar-form {
+    border: 1px solid #f0f0f0 !important;
+    border-radius: 6px !important;
+    background: #fafafa !important;
+}
+
+.skin-blue-light .user-panel > .info,
+.skin-blue-light .user-panel > .info > a {
+    color: #434343 !important;
+}
+
+.user-panel > .image,
+.user-panel > .image.is-brand-logo-avatar,
+.main-header .user-image.is-brand-logo-avatar-img {
+    background-color: #f5f5f5 !important;
+}
+
+/* 顶栏多标签:紧凑、可横向滚动,去掉灰底块 */
+#firstnav {
+    box-shadow: none !important;
+    border-bottom: 1px solid #f0f0f0 !important;
+}
+
+#firstnav .nav-addtabs {
+    overflow-x: auto;
+    overflow-y: hidden;
+    white-space: nowrap;
+    scrollbar-width: thin;
+}
+
+#firstnav .nav-addtabs::-webkit-scrollbar {
+    height: 3px;
+}
+
+#firstnav .nav-addtabs::-webkit-scrollbar-thumb {
+    background: #e8e8e8;
+    border-radius: 3px;
+}
+
+.skin-blue-light .nav-addtabs > li > a {
+    color: #8c8c8c !important;
+    border-radius: 0;
+    padding: 0 14px !important;
+    height: 48px !important;
+    line-height: 48px !important;
+    border-right: 1px solid #f5f5f5 !important;
+    font-size: 13px;
+}
+
+.skin-blue-light .nav-addtabs > li > a:hover,
+.skin-blue-light .nav-addtabs > li > a:focus {
+    background: #fafafa !important;
+    color: #434343 !important;
+    border-right-color: #f5f5f5 !important;
+}
+
+.skin-blue-light .nav-addtabs > li.active > a,
+.skin-blue-light .nav-addtabs > li.active > a:hover,
+.skin-blue-light .nav-addtabs > li.active > a:focus {
+    color: #1677ff !important;
+    background: #ffffff !important;
+    border-bottom: 2px solid #1677ff !important;
+    border-right: 1px solid #f5f5f5 !important;
+    font-weight: 500;
+}
+
+body.inside-header.inside-aside,
+body.inside-header.inside-aside #main,
+body.inside-header.inside-aside .tab-content,
+body.inside-header.inside-aside #content {
+    background: #f5f6f8 !important;
+}
+
+/* 去掉页面内灰条标题区 */
+.panel-intro {
+    background: transparent !important;
+    border: none !important;
+    box-shadow: none !important;
+}
+
+.panel-intro > .panel-heading,
+.panel-nav .panel-heading {
+    background: #ffffff !important;
+    border-color: #f0f0f0 !important;
+    border-bottom: 1px solid #f0f0f0 !important;
+    padding: 12px 16px 0 !important;
+}
+
+.panel-intro > .panel-body,
+.panel-nav .panel-body {
+    background: #ffffff !important;
+    border: 1px solid #f0f0f0 !important;
+    border-top: none !important;
+    border-radius: 0 0 8px 8px !important;
+}
+
+.panel-intro > .panel-heading .nav-tabs,
+.panel-nav .nav-tabs {
+    border-bottom: 1px solid #f0f0f0 !important;
+    margin-bottom: 0 !important;
+}
+
+.panel-intro > .panel-heading .nav-tabs > li > a,
+.panel-nav .nav-tabs > li > a {
+    margin-right: 0 !important;
+    padding: 10px 16px !important;
+    color: #8c8c8c !important;
+    background: transparent !important;
+    border: none !important;
+    border-bottom: 2px solid transparent !important;
+    border-radius: 0 !important;
+}
+
+.panel-intro > .panel-heading .nav-tabs > li > a:hover,
+.panel-intro > .panel-heading .nav-tabs > li > a:focus,
+.panel-nav .nav-tabs > li > a:hover,
+.panel-nav .nav-tabs > li > a:focus {
+    color: #434343 !important;
+    background: transparent !important;
+    border: none !important;
+    border-bottom: 2px solid #d9d9d9 !important;
+}
+
+.panel-intro > .panel-heading .nav-tabs > li.active > a,
+.panel-intro > .panel-heading .nav-tabs > li.active > a:hover,
+.panel-intro > .panel-heading .nav-tabs > li.active > a:focus,
+.panel-nav .nav-tabs > li.active > a,
+.panel-nav .nav-tabs > li.active > a:hover,
+.panel-nav .nav-tabs > li.active > a:focus {
+    color: #1677ff !important;
+    background: transparent !important;
+    border: none !important;
+    border-bottom: 2px solid #1677ff !important;
+}
+
+.panel-default > .panel-heading {
+    background: #ffffff !important;
+    border-color: #f0f0f0 !important;
+}
+
+.panel,
+.box {
+    border-radius: 8px !important;
+    border: 1px solid #f0f0f0 !important;
+    background: #ffffff !important;
+    box-shadow: none !important;
+}
+
+.content-wrapper .content {
+    padding: 12px 16px;
+}
+
+/* 表格区域 */
+.bootstrap-table .fixed-table-toolbar {
+    padding: 8px 0 !important;
+}
+
+.bootstrap-table .fixed-table-toolbar .btn {
+    border-radius: 6px !important;
+}
+
+.bootstrap-table .table,
+.table {
+    border-color: #f0f0f0 !important;
+}
+
+.bootstrap-table .table > thead > tr > th,
+.table > thead > tr > th {
+    background: #fafafa !important;
+    color: #434343 !important;
+    border-bottom: 1px solid #f0f0f0 !important;
+    font-weight: 500;
+    font-size: 13px;
+}
+
+.bootstrap-table .table > tbody > tr > td {
+    border-color: #f5f5f5 !important;
+    color: #434343;
+    font-size: 13px;
+}
+
+.bootstrap-table .table > tbody > tr:hover > td {
+    background: #fafafa !important;
+}
+
+.fixed-table-pagination {
+    border-top: 1px solid #f0f0f0 !important;
+    background: #ffffff !important;
+}
+
+.btn-primary {
+    background: #1677ff !important;
+    border-color: #1677ff !important;
+}
+
+.btn-primary:hover,
+.btn-primary:focus {
+    background: #4096ff !important;
+    border-color: #4096ff !important;
+}
+
+.btn-danger {
+    background: #fff !important;
+    border-color: #ffccc7 !important;
+    color: #ff4d4f !important;
+}
+
+.btn-danger:hover,
+.btn-danger:focus {
+    background: #fff1f0 !important;
+    border-color: #ffa39e !important;
+    color: #ff4d4f !important;
+}
+
+.form-control {
+    border-color: #d9d9d9 !important;
+    border-radius: 6px !important;
+    box-shadow: none !important;
+}
+
+.form-control:focus {
+    border-color: #1677ff !important;
+    box-shadow: 0 0 0 2px rgba(22, 119, 255, 0.1) !important;
+}
+
+@media (max-width: 767px) {
+    .skin-blue-light .main-header .navbar {
+        background: #ffffff !important;
+    }
+}

+ 35 - 3
public/assets/js/backend/index.js

@@ -383,11 +383,35 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'adminlte', 'form'], functi
 
 
         },
         },
         login: function () {
         login: function () {
+            var REMEMBER_KEY = 'xh_admin_remember_pwd';
+            try {
+                var remembered = localStorage.getItem(REMEMBER_KEY);
+                if (remembered) {
+                    remembered = JSON.parse(remembered);
+                    if (remembered && remembered.username) {
+                        $("#pd-form-username").val(remembered.username);
+                    }
+                    if (remembered && remembered.password) {
+                        $("#pd-form-password").val(remembered.password);
+                    }
+                    $("#remember-password").prop("checked", true);
+                }
+            } catch (e) {
+            }
+
             var lastlogin = localStorage.getItem("lastlogin");
             var lastlogin = localStorage.getItem("lastlogin");
             if (lastlogin) {
             if (lastlogin) {
-                lastlogin = JSON.parse(lastlogin);
-                $("#profile-img").attr("src", Backend.api.cdnurl(lastlogin.avatar));
-                $("#profile-name").val(lastlogin.username);
+                try {
+                    lastlogin = JSON.parse(lastlogin);
+                    if (lastlogin && lastlogin.avatar) {
+                        $("#profile-img").attr("src", Backend.api.cdnurl(lastlogin.avatar));
+                    }
+                    if (lastlogin && lastlogin.username && !$("#pd-form-username").val()) {
+                        $("#profile-name").val(lastlogin.username);
+                        $("#pd-form-username").val(lastlogin.username);
+                    }
+                } catch (e2) {
+                }
             }
             }
 
 
             //让错误提示框居中
             //让错误提示框居中
@@ -410,6 +434,14 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'adminlte', 'form'], functi
                     username: data.username,
                     username: data.username,
                     avatar: data.avatar
                     avatar: data.avatar
                 }));
                 }));
+                if ($("#remember-password").is(":checked")) {
+                    localStorage.setItem(REMEMBER_KEY, JSON.stringify({
+                        username: $("#pd-form-username").val() || data.username || '',
+                        password: $("#pd-form-password").val() || ''
+                    }));
+                } else {
+                    localStorage.removeItem(REMEMBER_KEY);
+                }
                 location.href = Backend.api.fixurl(data.url);
                 location.href = Backend.api.fixurl(data.url);
             }, function (data) {
             }, function (data) {
                 $("input[name=captcha]").next(".input-group-addon").find("img").trigger("click");
                 $("input[name=captcha]").next(".input-group-addon").find("img").trigger("click");

+ 58 - 49
public/assets/js/backend/procuremen.js

@@ -917,6 +917,44 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                     .replace(/>/g, '&gt;')
                     .replace(/>/g, '&gt;')
                     .replace(/"/g, '&quot;');
                     .replace(/"/g, '&quot;');
             }
             }
+            function procuremenBuildConfirmRowsTable(rows) {
+                var html = '<table class="table table-bordered table-condensed procuremen-confirm-table">'
+                    + '<thead><tr><th>订单号</th><th>工序名称</th><th>印件名称</th></tr></thead><tbody>';
+                $.each(rows, function (i, rd) {
+                    html += '<tr><td>' + procuremenEscHtml(String(rd.CCYDH || '').trim() || '—') + '</td>'
+                        + '<td>' + procuremenEscHtml(String(rd.CGYMC || '').trim() || '—') + '</td>'
+                        + '<td>' + procuremenEscHtml(String(rd.CYJMC || '').trim() || '—') + '</td></tr>';
+                });
+                html += '</tbody></table>';
+                return html;
+            }
+            function procuremenBuildConfirmMsg(introHtml, rowsTableHtml) {
+                return '<div class="procuremen-confirm-body">'
+                    + '<p class="procuremen-confirm-intro">' + introHtml + '</p>'
+                    + '<div class="procuremen-confirm-scroll">' + rowsTableHtml + '</div>'
+                    + '</div>';
+            }
+            var procuremenConfirmLayerBase = {
+                area: ['680px', '400px'],
+                icon: -1,
+                skin: 'layui-layer-procuremen-finish',
+                success: function (layero) {
+                    var $layer = $(layero);
+                    $layer.css({
+                        display: 'flex',
+                        flexDirection: 'column',
+                        height: '400px',
+                        overflow: 'hidden'
+                    });
+                    $layer.find('.layui-layer-content').css({
+                        flex: '1 1 auto',
+                        minHeight: 0,
+                        overflow: 'hidden',
+                        height: 'auto'
+                    });
+                    $layer.find('.layui-layer-btn').css({flexShrink: 0});
+                }
+            };
             function procuremenExpandOrderRows(row) {
             function procuremenExpandOrderRows(row) {
                 if (!row) {
                 if (!row) {
                     return [];
                     return [];
@@ -994,7 +1032,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                     sessionStorage.setItem('procuremen_merge_rows', JSON.stringify(selRows));
                     sessionStorage.setItem('procuremen_merge_rows', JSON.stringify(selRows));
                 } catch (ignoreStore) {
                 } catch (ignoreStore) {
                 }
                 }
-                var titlePick = '协助下发';
+                var titlePick = ' ';
                 var revUrl = Fast.api.fixurl('procuremen/pickreview?ids=' + encodeURIComponent(ids[0]) + '&merge=1');
                 var revUrl = Fast.api.fixurl('procuremen/pickreview?ids=' + encodeURIComponent(ids[0]) + '&merge=1');
                 var winPick = window;
                 var winPick = window;
                 if (!winPick.Backend || !winPick.Backend.api) {
                 if (!winPick.Backend || !winPick.Backend.api) {
@@ -1024,33 +1062,25 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                     return;
                     return;
                 }
                 }
                 var idSet = {};
                 var idSet = {};
-                var rowsHtml = '<table class="table table-bordered table-condensed" style="margin:8px 0 0;font-size:12px;">'
-                    + '<thead><tr><th>订单号</th><th>工序名称</th><th>印件名称</th></tr></thead><tbody>';
                 $.each(selAb, function (ai, ra) {
                 $.each(selAb, function (ai, ra) {
                     var sid = ra && (ra.ID != null ? ra.ID : ra.id);
                     var sid = ra && (ra.ID != null ? ra.ID : ra.id);
                     if (sid != null && sid !== '' && String(sid) !== '0') {
                     if (sid != null && sid !== '' && String(sid) !== '0') {
                         idSet[String(sid)] = true;
                         idSet[String(sid)] = true;
                     }
                     }
-                    rowsHtml += '<tr><td>' + procuremenEscHtml(String(ra.CCYDH || '').trim() || '—') + '</td>'
-                        + '<td>' + procuremenEscHtml(String(ra.CGYMC || '').trim() || '—') + '</td>'
-                        + '<td>' + procuremenEscHtml(String(ra.CYJMC || '').trim() || '—') + '</td></tr>';
                 });
                 });
-                rowsHtml += '</tbody></table>';
                 var idList = Object.keys(idSet);
                 var idList = Object.keys(idSet);
                 if (!idList.length) {
                 if (!idList.length) {
                     Toastr.warning('所选行无效,请重新勾选');
                     Toastr.warning('所选行无效,请重新勾选');
                     return;
                     return;
                 }
                 }
-                var abandonMsg = '<div style="text-align:left;line-height:1.65;font-size:13px;">'
-                    + '<p style="margin:0 0 8px 0;">即将退回协助初选并重新下发以下 <strong>' + idList.length + '</strong> 个订单(历史下发与操作记录会保留)</p>'
-                    + rowsHtml
-                    + '<p style="margin:10px 0 0;color:#a94442;"><strong>提示:</strong>退回后需重新选择供应商下发。</p></div>';
-                Layer.confirm(abandonMsg, {
+                var abandonMsg = procuremenBuildConfirmMsg(
+                    '退回初选并重新下发以下 <strong>' + idList.length + '</strong> 个订单:',
+                    procuremenBuildConfirmRowsTable(selAb)
+                );
+                Layer.confirm(abandonMsg, $.extend({}, procuremenConfirmLayerBase, {
                     title: '重新下发确认',
                     title: '重新下发确认',
-                    area: ['560px', 'auto'],
-                    icon: 3,
                     btn: ['确认重新下发', '取消']
                     btn: ['确认重新下发', '取消']
-                }, function (idxAb) {
+                }), function (idxAb) {
                     Layer.close(idxAb);
                     Layer.close(idxAb);
                     Fast.api.ajax({
                     Fast.api.ajax({
                         url: 'procuremen/auditabandon',
                         url: 'procuremen/auditabandon',
@@ -1076,24 +1106,14 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                     Toastr.warning('请先勾选至少 1 条工序');
                     Toastr.warning('请先勾选至少 1 条工序');
                     return;
                     return;
                 }
                 }
-                var rowsHtml = '<table class="table table-bordered table-condensed" style="margin:8px 0 0;font-size:12px;">'
-                    + '<thead><tr><th>订单号</th><th>工序名称</th><th>印件名称</th></tr></thead><tbody>';
-                $.each(selDel, function (di, rd) {
-                    rowsHtml += '<tr><td>' + procuremenEscHtml(String(rd.CCYDH || '').trim() || '—') + '</td>'
-                        + '<td>' + procuremenEscHtml(String(rd.CGYMC || '').trim() || '—') + '</td>'
-                        + '<td>' + procuremenEscHtml(String(rd.CYJMC || '').trim() || '—') + '</td></tr>';
-                });
-                rowsHtml += '</tbody></table>';
-                var deleteMsg = '<div style="text-align:left;line-height:1.65;font-size:13px;">'
-                    + '<p style="margin:0 0 8px 0;">即将从初选列表移除以下 <strong>' + selDel.length + '</strong> 条工序:</p>'
-                    + rowsHtml
-                    + '<p style="margin:10px 0 0;color:#a94442;"><strong>提示:</strong>删除后不再显示;已下发或已进入后续流程的工序无法删除。</p></div>';
-                Layer.confirm(deleteMsg, {
+                var deleteMsg = procuremenBuildConfirmMsg(
+                    '从初选列表移除以下 <strong>' + selDel.length + '</strong> 条工序:',
+                    procuremenBuildConfirmRowsTable(selDel)
+                );
+                Layer.confirm(deleteMsg, $.extend({}, procuremenConfirmLayerBase, {
                     title: '删除确认',
                     title: '删除确认',
-                    area: ['560px', 'auto'],
-                    icon: 3,
                     btn: ['确定删除', '取消']
                     btn: ['确定删除', '取消']
-                }, function (idxDel) {
+                }), function (idxDel) {
                     Layer.close(idxDel);
                     Layer.close(idxDel);
                     Fast.api.ajax({
                     Fast.api.ajax({
                         url: 'procuremen/pickdelete',
                         url: 'procuremen/pickdelete',
@@ -1119,25 +1139,14 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                     Toastr.warning('请先勾选至少 1 条工序');
                     Toastr.warning('请先勾选至少 1 条工序');
                     return;
                     return;
                 }
                 }
-                var rowsHtml = '<table class="table table-bordered table-condensed" style="margin:8px 0 0;font-size:12px;">'
-                    + '<thead><tr><th>订单号</th><th>工序名称</th><th>印件名称</th></tr></thead><tbody>';
-                $.each(selFin, function (fi, rf) {
-                    rowsHtml += '<tr><td>' + procuremenEscHtml(String(rf.CCYDH || '').trim() || '—') + '</td>'
-                        + '<td>' + procuremenEscHtml(String(rf.CGYMC || '').trim() || '—') + '</td>'
-                        + '<td>' + procuremenEscHtml(String(rf.CYJMC || '').trim() || '—') + '</td></tr>';
-                });
-                rowsHtml += '</tbody></table>';
-                var finishBatchMsg = '<div style="text-align:left;line-height:1.65;font-size:13px;">'
-                    + '<p style="margin:0 0 8px 0;">即将对以下 <strong>' + selFin.length + '</strong> 条工序标记为<strong>已完结</strong>:</p>'
-                    + rowsHtml
-                    + '<p style="margin:10px 0 0;color:#a94442;"><strong>提示:</strong>点击「确定」后不可撤回或更改。</p></div>';
-                Layer.confirm(finishBatchMsg, {
+                var finishBatchMsg = procuremenBuildConfirmMsg(
+                    '即将对以下 <strong>' + selFin.length + '</strong> 条工序标记为<strong>已完结</strong>:',
+                    procuremenBuildConfirmRowsTable(selFin)
+                );
+                Layer.confirm(finishBatchMsg, $.extend({}, procuremenConfirmLayerBase, {
                     title: '完结确认',
                     title: '完结确认',
-                    area: ['560px', 'auto'],
-                    icon: 3,
-                    btn: ['确定', '取消'],
-                    skin: 'layui-layer-procuremen-finish'
-                }, function (idxFin) {
+                    btn: ['确定', '取消']
+                }), function (idxFin) {
                     Layer.close(idxFin);
                     Layer.close(idxFin);
                     var tokenFin = $('input[name=\'__token__\']').val() || '';
                     var tokenFin = $('input[name=\'__token__\']').val() || '';
                     var pending = selFin.length;
                     var pending = selFin.length;

+ 34 - 3
public/assets/js/backend/procuremenarchive.js

@@ -73,9 +73,40 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                 columns: [
                 columns: [
                     [
                     [
                         {checkbox: true},
                         {checkbox: true},
-                        {field: 'CCYDH', title: '订单号', operate: 'LIKE'},
-                        {field: 'CYJMC', title: '印件名称', operate: 'LIKE', class: 'autocontent', formatter: Table.api.formatter.content},
-                        {field: 'CGYMC', title: '工序名称', operate: 'LIKE', class: 'autocontent', formatter: Table.api.formatter.content},
+                        {field: 'CCYDH', title: '订单号', operate: 'LIKE', width: 120},
+                        {
+                            field: 'CYJMC',
+                            title: '印件名称',
+                            operate: 'LIKE',
+                            formatter: function (value) {
+                                return archiveEscHtml(value == null ? '' : value);
+                            },
+                            cellStyle: function () {
+                                return {css: {'white-space': 'normal', 'word-break': 'break-all', 'min-width': '220px'}};
+                            }
+                        },
+                        {
+                            field: 'CGYMC',
+                            title: '工序名称',
+                            operate: 'LIKE',
+                            formatter: function (value) {
+                                return archiveEscHtml(value == null ? '' : value);
+                            },
+                            cellStyle: function () {
+                                return {css: {'white-space': 'normal', 'word-break': 'break-all', 'min-width': '140px'}};
+                            }
+                        },
+                        {
+                            field: 'pick_company_name',
+                            title: '已选供应商',
+                            operate: 'LIKE',
+                            formatter: function (value) {
+                                return archiveEscHtml(value == null ? '' : value);
+                            },
+                            cellStyle: function () {
+                                return {css: {'white-space': 'normal', 'word-break': 'break-all', 'min-width': '180px'}};
+                            }
+                        },
                         {
                         {
                             field: 'createtime',
                             field: 'createtime',
                             title: '完结时间',
                             title: '完结时间',

+ 40 - 14
public/assets/js/backend/purchasecontent.js

@@ -39,17 +39,24 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                 sortName: 'id',
                 sortName: 'id',
                 sortOrder: 'desc',
                 sortOrder: 'desc',
                 commonSearch: true,
                 commonSearch: true,
+                rowStyle: function (row) {
+                    if (parseInt(row.is_receiver, 10) === 1 && parseInt(row.is_read, 10) === 1) {
+                        return {classes: 'purchase-content-row-read'};
+                    }
+                    return {};
+                },
                 columns: [
                 columns: [
                     [
                     [
                         {checkbox: true},
                         {checkbox: true},
-                        {field: 'id', title: 'ID', width: 60, operate: false},
-                        {field: 'sender_name', title: '投递人', operate: false, width: 100, align: 'center'},
+                        {field: 'id', title: 'ID', width: 60, operate: false, sortable: true},
+                        {field: 'sender_name', title: '投递人', operate: false, width: 100, align: 'center', sortable: true},
                         {
                         {
                             field: 'recipient_names',
                             field: 'recipient_names',
                             title: '投递给',
                             title: '投递给',
                             operate: false,
                             operate: false,
                             align: 'left',
                             align: 'left',
                             class: 'autocontent',
                             class: 'autocontent',
+                            sortable: true,
                             formatter: function (value, row) {
                             formatter: function (value, row) {
                                 var cnt = parseInt(row.recipient_count, 10) || 0;
                                 var cnt = parseInt(row.recipient_count, 10) || 0;
                                 var text = value != null && value !== '' ? String(value) : '';
                                 var text = value != null && value !== '' ? String(value) : '';
@@ -59,13 +66,14 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                                 return text;
                                 return text;
                             }
                             }
                         },
                         },
-                        {field: 'subject', title: '主题', operate: 'LIKE', align: 'left'},
+                        {field: 'subject', title: '主题', operate: 'LIKE', align: 'left', sortable: true},
                         {
                         {
                             field: 'content',
                             field: 'content',
                             title: '内容',
                             title: '内容',
                             operate: 'LIKE',
                             operate: 'LIKE',
                             align: 'left',
                             align: 'left',
                             class: 'autocontent',
                             class: 'autocontent',
+                            sortable: true,
                             formatter: Table.api.formatter.content
                             formatter: Table.api.formatter.content
                         },
                         },
                         {
                         {
@@ -75,6 +83,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                             addclass: 'datetimerange',
                             addclass: 'datetimerange',
                             width: 165,
                             width: 165,
                             align: 'center',
                             align: 'center',
+                            sortable: true,
                             formatter: function (value, row) {
                             formatter: function (value, row) {
                                 if (row.send_time_text) {
                                 if (row.send_time_text) {
                                     return row.send_time_text;
                                     return row.send_time_text;
@@ -83,15 +92,20 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                             }
                             }
                         },
                         },
                         {
                         {
-                            field: 'is_sender',
+                            field: 'is_read',
                             title: '状态',
                             title: '状态',
                             operate: false,
                             operate: false,
                             width: 72,
                             width: 72,
                             align: 'center',
                             align: 'center',
-                            formatter: function (value) {
+                            sortable: true,
+                            formatter: function (value, row) {
+                                // 仅接收人区分已读/未读;纯发件人无阅读状态
+                                if (parseInt(row.is_receiver, 10) !== 1) {
+                                    return '<span class="text-muted">—</span>';
+                                }
                                 return parseInt(value, 10) === 1
                                 return parseInt(value, 10) === 1
-                                    ? '<span class="text-primary">发件</span>'
-                                    : '<span class="text-success">收件</span>';
+                                    ? '<span class="text-muted">已读</span>'
+                                    : '<span class="text-warning">未读</span>';
                             }
                             }
                         },
                         },
                         {
                         {
@@ -99,18 +113,33 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                             title: __('Operate'),
                             title: __('Operate'),
                             width: 120,
                             width: 120,
                             align: 'center',
                             align: 'center',
+                            operate: false,
                             table: table,
                             table: table,
-                            events: Table.api.events.operate,
+                            events: $.extend({}, Table.api.events.operate, {
+                                'click .btn-purchasecontent-view': function (e, value, row) {
+                                    e.stopPropagation();
+                                    e.preventDefault();
+                                    var id = row && row.id;
+                                    if (!id) {
+                                        return;
+                                    }
+                                    Fast.api.open('purchasecontent/detail/ids/' + id, '查看通知', {
+                                        area: DIALOG_AREA,
+                                        end: function () {
+                                            table.bootstrapTable('refresh');
+                                        }
+                                    });
+                                }
+                            }),
                             formatter: Table.api.formatter.operate,
                             formatter: Table.api.formatter.operate,
                             buttons: [
                             buttons: [
                                 {
                                 {
                                     name: 'detail',
                                     name: 'detail',
                                     text: '查看',
                                     text: '查看',
                                     title: '查看通知',
                                     title: '查看通知',
-                                    classname: 'btn btn-xs btn-info btn-dialog',
+                                    classname: 'btn btn-xs btn-info btn-purchasecontent-view',
                                     icon: 'fa fa-eye',
                                     icon: 'fa fa-eye',
-                                    url: 'purchasecontent/detail',
-                                    extend: 'data-area=\'["920px","800px"]\''
+                                    url: 'purchasecontent/detail'
                                 },
                                 },
                                 {
                                 {
                                     name: 'del',
                                     name: 'del',
@@ -143,9 +172,6 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
             $('#toolbar .btn-add').data('area', DIALOG_AREA);
             $('#toolbar .btn-add').data('area', DIALOG_AREA);
             table.on('post-body.bs.table', function () {
             table.on('post-body.bs.table', function () {
                 syncRecipientDeleteCheckbox();
                 syncRecipientDeleteCheckbox();
-                table.closest('.bootstrap-table').find('.btn-dialog').each(function () {
-                    $(this).data('area', DIALOG_AREA);
-                });
             });
             });
         },
         },
         add: function () {
         add: function () {

+ 26 - 2
public/assets/js/require-login.js

@@ -33,14 +33,30 @@ require(['jquery', 'bootstrap'], function ($) {
         $(function () {
         $(function () {
             Fast.config.toastr.positionClass = 'toast-top-center';
             Fast.config.toastr.positionClass = 'toast-top-center';
 
 
+            var REMEMBER_KEY = 'xh_admin_remember_pwd';
+            try {
+                var remembered = localStorage.getItem(REMEMBER_KEY);
+                if (remembered) {
+                    remembered = JSON.parse(remembered);
+                    if (remembered && remembered.username) {
+                        $('#pd-form-username').val(remembered.username);
+                    }
+                    if (remembered && remembered.password) {
+                        $('#pd-form-password').val(remembered.password);
+                    }
+                    $('#remember-password').prop('checked', true);
+                }
+            } catch (e) {
+            }
+
             var lastlogin = localStorage.getItem('lastlogin');
             var lastlogin = localStorage.getItem('lastlogin');
             if (lastlogin) {
             if (lastlogin) {
                 try {
                 try {
                     lastlogin = JSON.parse(lastlogin);
                     lastlogin = JSON.parse(lastlogin);
-                    if (lastlogin && lastlogin.username) {
+                    if (lastlogin && lastlogin.username && !$('#pd-form-username').val()) {
                         $('#pd-form-username').val(lastlogin.username);
                         $('#pd-form-username').val(lastlogin.username);
                     }
                     }
-                } catch (e) {
+                } catch (e2) {
                 }
                 }
             }
             }
 
 
@@ -59,6 +75,14 @@ require(['jquery', 'bootstrap'], function ($) {
                     username: data.username,
                     username: data.username,
                     avatar: data.avatar
                     avatar: data.avatar
                 }));
                 }));
+                if ($('#remember-password').is(':checked')) {
+                    localStorage.setItem(REMEMBER_KEY, JSON.stringify({
+                        username: $('#pd-form-username').val() || data.username || '',
+                        password: $('#pd-form-password').val() || ''
+                    }));
+                } else {
+                    localStorage.removeItem(REMEMBER_KEY);
+                }
                 location.href = Fast.api.fixurl(data.url);
                 location.href = Fast.api.fixurl(data.url);
             }, function () {
             }, function () {
                 $('input[name=captcha]').next('.input-group-addon').find('img').trigger('click');
                 $('input[name=captcha]').next('.input-group-addon').find('img').trigger('click');