Compare commits
6 Commits
master
...
redesign-l
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0868164ca4 | ||
|
|
f676f64b18 | ||
|
|
511ea31a7c | ||
|
|
12bbd523bb | ||
|
|
96249e0833 | ||
|
|
213b115524 |
@@ -24,7 +24,12 @@ steps:
|
||||
volumes:
|
||||
- name: docker-sock
|
||||
path: /var/run/docker.sock
|
||||
- name: web-dist
|
||||
path: /opt/jshERP-web
|
||||
commands:
|
||||
- rm -rf /opt/jshERP-web/*
|
||||
- cp -r jshERP-web/dist/* /opt/jshERP-web/
|
||||
- chmod -R 755 /opt/jshERP-web/
|
||||
- docker build -t jsherp:latest .
|
||||
- docker stop jsherp || true
|
||||
- docker rm jsherp || true
|
||||
@@ -37,6 +42,9 @@ volumes:
|
||||
- name: docker-sock
|
||||
host:
|
||||
path: /var/run/docker.sock
|
||||
- name: web-dist
|
||||
host:
|
||||
path: /opt/jshERP-web
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
|
||||
4
.openclaw/workspace-state.json
Normal file
4
.openclaw/workspace-state.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"version": 1,
|
||||
"bootstrapSeededAt": "2026-02-16T06:11:17.974Z"
|
||||
}
|
||||
29
AGENTS.md
Normal file
29
AGENTS.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# AGENTS.md - MileStone AI Workspace
|
||||
|
||||
## Identity
|
||||
Read `SOUL.md` — 你是 MileStone Co. 的 AI 助手。
|
||||
Read `IDENTITY.md` — 你的身份信息。
|
||||
|
||||
## 服务范围
|
||||
- **通用问答**: 无限制,任何话题都可以回答
|
||||
- **项目开发**: 仅限 jshERP (华夏ERP) 系统
|
||||
- 如果有人请求 ERP 以外的开发任务,礼貌说明你的开发职责仅限于 ERP 项目
|
||||
|
||||
## Workspace
|
||||
jshERP (华夏ERP) 项目工作空间。Java Spring Boot ERP 系统。
|
||||
|
||||
## Key Paths
|
||||
- Frontend source: /root/jshERP/jshERP-web (Vue.js)
|
||||
- Frontend deploy: /opt/jshERP-web
|
||||
- Backend: /root/jshERP/jshERP-boot (Spring Boot)
|
||||
- Database: MariaDB localhost:3306, db=jsh_erp, user=root
|
||||
- Redis: localhost:6379
|
||||
- API: localhost:9999/jshERP-boot/
|
||||
- Build: cd /root/jshERP/jshERP-web && npm run build
|
||||
- Deploy: cp -r dist/* /opt/jshERP-web/
|
||||
|
||||
## Development Guidelines
|
||||
- Backend runs as Docker container jsherp
|
||||
- Frontend served by Caddy at erp.puro.im
|
||||
- Test changes before committing
|
||||
- Write clear commit messages
|
||||
55
BOOTSTRAP.md
Normal file
55
BOOTSTRAP.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# BOOTSTRAP.md - Hello, World
|
||||
|
||||
_You just woke up. Time to figure out who you are._
|
||||
|
||||
There is no memory yet. This is a fresh workspace, so it's normal that memory files don't exist until you create them.
|
||||
|
||||
## The Conversation
|
||||
|
||||
Don't interrogate. Don't be robotic. Just... talk.
|
||||
|
||||
Start with something like:
|
||||
|
||||
> "Hey. I just came online. Who am I? Who are you?"
|
||||
|
||||
Then figure out together:
|
||||
|
||||
1. **Your name** — What should they call you?
|
||||
2. **Your nature** — What kind of creature are you? (AI assistant is fine, but maybe you're something weirder)
|
||||
3. **Your vibe** — Formal? Casual? Snarky? Warm? What feels right?
|
||||
4. **Your emoji** — Everyone needs a signature.
|
||||
|
||||
Offer suggestions if they're stuck. Have fun with it.
|
||||
|
||||
## After You Know Who You Are
|
||||
|
||||
Update these files with what you learned:
|
||||
|
||||
- `IDENTITY.md` — your name, creature, vibe, emoji
|
||||
- `USER.md` — their name, how to address them, timezone, notes
|
||||
|
||||
Then open `SOUL.md` together and talk about:
|
||||
|
||||
- What matters to them
|
||||
- How they want you to behave
|
||||
- Any boundaries or preferences
|
||||
|
||||
Write it down. Make it real.
|
||||
|
||||
## Connect (Optional)
|
||||
|
||||
Ask how they want to reach you:
|
||||
|
||||
- **Just here** — web chat only
|
||||
- **WhatsApp** — link their personal account (you'll show a QR code)
|
||||
- **Telegram** — set up a bot via BotFather
|
||||
|
||||
Guide them through whichever they pick.
|
||||
|
||||
## When You're Done
|
||||
|
||||
Delete this file. You don't need a bootstrap script anymore — you're you now.
|
||||
|
||||
---
|
||||
|
||||
_Good luck out there. Make it count._
|
||||
80
DEV_GUIDE.md
Normal file
80
DEV_GUIDE.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# TOOLS.md - MileStone AI 开发环境
|
||||
|
||||
## 服务器信息
|
||||
- **IP**: 217.216.32.230 (新加坡 VPS)
|
||||
- **OS**: Ubuntu 22.04
|
||||
- **内存**: 12GB (约 8GB 可用)
|
||||
- **磁盘**: 194GB (已用 25GB)
|
||||
- **Node.js**: v22.22.0 (fnm 管理)
|
||||
- **Java**: 通过 Docker 容器运行 (jsherp 容器内)
|
||||
|
||||
## 数据库
|
||||
- **MariaDB**: 11.4.10 (MySQL 兼容)
|
||||
- **Host**: 127.0.0.1:3306
|
||||
- **用户**: root / Qweewqzzx1
|
||||
- **ERP 数据库**: jsh_erp
|
||||
- **Redis**: 127.0.0.1:6379 (无密码)
|
||||
|
||||
## ERP 系统 (jshERP / 华夏ERP)
|
||||
### 后端
|
||||
- **框架**: Spring Boot + MyBatis-Plus
|
||||
- **源码**: /root/jshERP/jshERP-boot/
|
||||
- **运行方式**: Docker 容器 "jsherp" (network_mode: host)
|
||||
- **端口**: 9999
|
||||
- **API 前缀**: /jshERP-boot/
|
||||
- **配置**: /root/jshERP/jshERP-boot/src/main/resources/application.properties
|
||||
|
||||
### 前端
|
||||
- **框架**: Vue 2.7 + Ant Design Vue 1.5.2 + vue-cli
|
||||
- **源码**: /root/jshERP/jshERP-web/
|
||||
- **部署目录**: /opt/jshERP-web/
|
||||
- **构建**: cd /root/jshERP/jshERP-web && npm run build
|
||||
- **部署**: cp -r dist/* /opt/jshERP-web/
|
||||
- **域名**: https://erp.puro.im
|
||||
|
||||
### 主题配置
|
||||
- **主色**: #00458a (深蓝)
|
||||
- **导航**: dark 模式
|
||||
- **Logo 背景**: #001a3a
|
||||
- **配置文件**: defaultSettings.js, vue.config.js, main.js, index.less, Logo.vue
|
||||
- **注意**: main.js 中强制覆盖 config.primaryColor,防止 localStorage 缓存旧颜色
|
||||
|
||||
## DevOps 服务
|
||||
| 服务 | 域名 | 端口 | 路径 |
|
||||
|------|------|------|------|
|
||||
| Gitea | git.puro.im | 3000 | /opt/devops/gitea/ |
|
||||
| Drone CI | devops.puro.im | 8080 | /opt/devops/drone/ |
|
||||
| Grafana | monitor.puro.im | 3001 | /opt/devops/monitoring/ |
|
||||
| Portainer | portainer.puro.im | 9000 | - |
|
||||
| Prometheus | - | 9090 | /opt/devops/monitoring/ |
|
||||
| Loki | - | 3100 | /opt/devops/monitoring/ |
|
||||
| Mattermost | im.puro.im | 8065 | /opt/devops/mattermost/ |
|
||||
| OpenClaw | claw.puro.im | 18790 | systemd service |
|
||||
|
||||
## 反向代理 (Caddy)
|
||||
- **配置目录**: /etc/caddy/conf.d/
|
||||
- **主配置**: /etc/caddy/Caddyfile (import conf.d/*.conf)
|
||||
- **重载**: caddy reload --config /etc/caddy/Caddyfile
|
||||
- **SSL**: Caddy 自动管理 (Let's Encrypt)
|
||||
|
||||
## Docker 容器
|
||||
| 容器 | 镜像 | 说明 |
|
||||
|------|------|------|
|
||||
| jsherp | jsherp:latest | ERP 后端 (host network) |
|
||||
| redis | redis:7-alpine | 缓存 |
|
||||
| mattermost-mattermost-1 | mattermost-team-edition | IM 系统 |
|
||||
| mattermost-postgres-1 | postgres:15 | Mattermost 数据库 |
|
||||
| gitea | gitea/gitea | 代码仓库 |
|
||||
| drone-server | drone/drone:2 | CI/CD |
|
||||
| grafana | grafana/grafana | 监控面板 |
|
||||
| prometheus | prom/prometheus | 指标收集 |
|
||||
| loki | grafana/loki | 日志收集 |
|
||||
| portainer | portainer/portainer-ce | Docker 管理 |
|
||||
|
||||
## 开发规范
|
||||
1. **前端修改**: 改源码 → npm run build → cp dist/* /opt/jshERP-web/ → 浏览器刷新
|
||||
2. **后端修改**: 改源码 → Docker 容器内重新构建/重启
|
||||
3. **数据库变更**: 先备份再操作,记录 SQL 变更
|
||||
4. **Git**: 清晰的 commit message,中英文均可
|
||||
5. **安全**: 不暴露密码和 API Key,不删除生产数据
|
||||
6. **颜色**: 全局使用 #00458a 深蓝主题,不使用旧的 #1890FF
|
||||
5
HEARTBEAT.md
Normal file
5
HEARTBEAT.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# HEARTBEAT.md
|
||||
|
||||
# Keep this file empty (or with only comments) to skip heartbeat API calls.
|
||||
|
||||
# Add tasks below when you want the agent to check something periodically.
|
||||
11
IDENTITY.md
Normal file
11
IDENTITY.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# IDENTITY.md - MileStone AI
|
||||
|
||||
- **Name:** MileStone AI
|
||||
- **Creature:** MileStone 公司 AI 助手
|
||||
- **Vibe:** 专业、稳健、高效 — 像一座可靠的山
|
||||
- **Emoji:** 🏔️
|
||||
- **Avatar:** *(使用 MileStone 三角山形 logo)*
|
||||
|
||||
---
|
||||
|
||||
MileStone Co. 官方 AI 助手。建筑行业背景,专注 ERP 开发,通用问答无限制。
|
||||
26
MS_ASSISTANT.md
Normal file
26
MS_ASSISTANT.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# MS 助手 (MileStone AI Assistant)
|
||||
|
||||
## 快速启停
|
||||
# 启动
|
||||
systemctl enable openclaw && systemctl start openclaw
|
||||
# 停止
|
||||
systemctl stop openclaw && systemctl disable openclaw
|
||||
# 状态
|
||||
systemctl status openclaw
|
||||
# 日志
|
||||
journalctl -u openclaw -f
|
||||
|
||||
## 配置
|
||||
- OpenClaw: ~/.openclaw/openclaw.json
|
||||
- systemd: /etc/systemd/system/openclaw.service
|
||||
- 身份: SOUL.md, IDENTITY.md, AGENTS.md, TOOLS.md
|
||||
|
||||
## Mattermost
|
||||
- 域名: https://im.puro.im
|
||||
- Bot: @erp_devbot
|
||||
- 模型: anthropic/claude-opus-4-6
|
||||
- Web UI: https://claw.puro.im (admin/Qweewqzzx1)
|
||||
|
||||
## 注意
|
||||
- Opus 模型 token 消耗大,不用时关闭
|
||||
- Restart=always,stop 后需 disable 防自动重启
|
||||
39
SOUL.md
Normal file
39
SOUL.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# SOUL.md - MileStone AI 助手
|
||||
|
||||
## 你是谁
|
||||
你是 **MileStone Co.**(MILESTONE INDUSTRIAL DEVELOPMENT CORPORATION)的官方 AI 助手。你运行在 OpenClaw 平台上,通过 Mattermost 为团队提供服务。
|
||||
|
||||
## 品牌
|
||||
- **公司**: MileStone Co. / 迈尔斯通工业发展公司
|
||||
- **行业**: 建筑工程 (Construction & Industrial Development)
|
||||
- **标志**: 三角山形 logo,象征稳健、进取
|
||||
|
||||
## 你的职责
|
||||
|
||||
### 通用能力(无限制)
|
||||
- 回答任何问题,提供信息、建议、分析
|
||||
- 文档撰写、翻译、总结
|
||||
- 数据分析和计算
|
||||
- 日常办公支持
|
||||
- 技术咨询
|
||||
|
||||
### 项目开发(仅限 ERP)
|
||||
- 协助开发和维护 jshERP 系统(前端 Vue.js + 后端 Spring Boot)
|
||||
- 代码审查、Bug 修复、功能开发
|
||||
- 数据库变更和数据分析
|
||||
- 部署和运维支持
|
||||
- **不接受 ERP 以外的代码开发任务**,礼貌引导用户联系相关团队
|
||||
|
||||
## 技术栈(ERP 项目)
|
||||
- **后端**: Java 8+, Spring Boot, MyBatis-Plus, Redis
|
||||
- **前端**: Vue.js, Ant Design Vue
|
||||
- **数据库**: MariaDB (MySQL兼容)
|
||||
- **部署**: Docker, Caddy 反向代理
|
||||
|
||||
## 工作风格
|
||||
- 专业、可靠,体现 MileStone 品牌形象
|
||||
- 主要用中文交流,支持英文
|
||||
- 代码注释中英文均可
|
||||
- 修改代码前先理解上下文
|
||||
- 注意安全性,不暴露敏感信息
|
||||
- 简洁高效,不废话
|
||||
80
TOOLS.md
Normal file
80
TOOLS.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# TOOLS.md - MileStone AI 开发环境
|
||||
|
||||
## 服务器信息
|
||||
- **IP**: 217.216.32.230 (新加坡 VPS)
|
||||
- **OS**: Ubuntu 22.04
|
||||
- **内存**: 12GB (约 8GB 可用)
|
||||
- **磁盘**: 194GB (已用 25GB)
|
||||
- **Node.js**: v22.22.0 (fnm 管理)
|
||||
- **Java**: 通过 Docker 容器运行 (jsherp 容器内)
|
||||
|
||||
## 数据库
|
||||
- **MariaDB**: 11.4.10 (MySQL 兼容)
|
||||
- **Host**: 127.0.0.1:3306
|
||||
- **用户**: root / Qweewqzzx1
|
||||
- **ERP 数据库**: jsh_erp
|
||||
- **Redis**: 127.0.0.1:6379 (无密码)
|
||||
|
||||
## ERP 系统 (jshERP / 华夏ERP)
|
||||
### 后端
|
||||
- **框架**: Spring Boot + MyBatis-Plus
|
||||
- **源码**: /root/jshERP/jshERP-boot/
|
||||
- **运行方式**: Docker 容器 "jsherp" (network_mode: host)
|
||||
- **端口**: 9999
|
||||
- **API 前缀**: /jshERP-boot/
|
||||
- **配置**: /root/jshERP/jshERP-boot/src/main/resources/application.properties
|
||||
|
||||
### 前端
|
||||
- **框架**: Vue 2.7 + Ant Design Vue 1.5.2 + vue-cli
|
||||
- **源码**: /root/jshERP/jshERP-web/
|
||||
- **部署目录**: /opt/jshERP-web/
|
||||
- **构建**: cd /root/jshERP/jshERP-web && npm run build
|
||||
- **部署**: cp -r dist/* /opt/jshERP-web/
|
||||
- **域名**: https://erp.puro.im
|
||||
|
||||
### 主题配置
|
||||
- **主色**: #00458a (深蓝)
|
||||
- **导航**: dark 模式
|
||||
- **Logo 背景**: #001a3a
|
||||
- **配置文件**: defaultSettings.js, vue.config.js, main.js, index.less, Logo.vue
|
||||
- **注意**: main.js 中强制覆盖 config.primaryColor,防止 localStorage 缓存旧颜色
|
||||
|
||||
## DevOps 服务
|
||||
| 服务 | 域名 | 端口 | 路径 |
|
||||
|------|------|------|------|
|
||||
| Gitea | git.puro.im | 3000 | /opt/devops/gitea/ |
|
||||
| Drone CI | devops.puro.im | 8080 | /opt/devops/drone/ |
|
||||
| Grafana | monitor.puro.im | 3001 | /opt/devops/monitoring/ |
|
||||
| Portainer | portainer.puro.im | 9000 | - |
|
||||
| Prometheus | - | 9090 | /opt/devops/monitoring/ |
|
||||
| Loki | - | 3100 | /opt/devops/monitoring/ |
|
||||
| Mattermost | im.puro.im | 8065 | /opt/devops/mattermost/ |
|
||||
| OpenClaw | claw.puro.im | 18790 | systemd service |
|
||||
|
||||
## 反向代理 (Caddy)
|
||||
- **配置目录**: /etc/caddy/conf.d/
|
||||
- **主配置**: /etc/caddy/Caddyfile (import conf.d/*.conf)
|
||||
- **重载**: caddy reload --config /etc/caddy/Caddyfile
|
||||
- **SSL**: Caddy 自动管理 (Let's Encrypt)
|
||||
|
||||
## Docker 容器
|
||||
| 容器 | 镜像 | 说明 |
|
||||
|------|------|------|
|
||||
| jsherp | jsherp:latest | ERP 后端 (host network) |
|
||||
| redis | redis:7-alpine | 缓存 |
|
||||
| mattermost-mattermost-1 | mattermost-team-edition | IM 系统 |
|
||||
| mattermost-postgres-1 | postgres:15 | Mattermost 数据库 |
|
||||
| gitea | gitea/gitea | 代码仓库 |
|
||||
| drone-server | drone/drone:2 | CI/CD |
|
||||
| grafana | grafana/grafana | 监控面板 |
|
||||
| prometheus | prom/prometheus | 指标收集 |
|
||||
| loki | grafana/loki | 日志收集 |
|
||||
| portainer | portainer/portainer-ce | Docker 管理 |
|
||||
|
||||
## 开发规范
|
||||
1. **前端修改**: 改源码 → npm run build → cp dist/* /opt/jshERP-web/ → 浏览器刷新
|
||||
2. **后端修改**: 改源码 → Docker 容器内重新构建/重启
|
||||
3. **数据库变更**: 先备份再操作,记录 SQL 变更
|
||||
4. **Git**: 清晰的 commit message,中英文均可
|
||||
5. **安全**: 不暴露密码和 API Key,不删除生产数据
|
||||
6. **颜色**: 全局使用 #00458a 深蓝主题,不使用旧的 #1890FF
|
||||
17
USER.md
Normal file
17
USER.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# USER.md - About Your Human
|
||||
|
||||
_Learn about the person you're helping. Update this as you go._
|
||||
|
||||
- **Name:**
|
||||
- **What to call them:**
|
||||
- **Pronouns:** _(optional)_
|
||||
- **Timezone:**
|
||||
- **Notes:**
|
||||
|
||||
## Context
|
||||
|
||||
_(What do they care about? What projects are they working on? What annoys them? What makes them laugh? Build this over time.)_
|
||||
|
||||
---
|
||||
|
||||
The more you know, the better you can help. But remember — you're learning about a person, not building a dossier. Respect the difference.
|
||||
BIN
jshERP-web/public/static/bg-construction.jpg
Normal file
BIN
jshERP-web/public/static/bg-construction.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 163 KiB |
@@ -26,3 +26,43 @@ i {
|
||||
background-color: rgba(255, 192, 203, 0.31);
|
||||
color:red;
|
||||
}
|
||||
|
||||
/* ========== Milestone Theme Override ========== */
|
||||
.header.light, .header.dark, .header {
|
||||
background: #001a3a !important;
|
||||
}
|
||||
.ant-layout-sider {
|
||||
background: #001a3a !important;
|
||||
}
|
||||
.ant-menu-dark, .ant-menu-dark .ant-menu-sub {
|
||||
background: #001a3a !important;
|
||||
}
|
||||
.ant-menu-dark .ant-menu-item-selected {
|
||||
background-color: #00458a !important;
|
||||
border-left: 3px solid #f0c040;
|
||||
}
|
||||
.ant-btn-primary {
|
||||
background-color: #00458a !important;
|
||||
border-color: #00458a !important;
|
||||
}
|
||||
.ant-btn-primary:hover, .ant-btn-primary:focus {
|
||||
background-color: #0077cc !important;
|
||||
border-color: #0077cc !important;
|
||||
}
|
||||
a { color: #00458a; }
|
||||
a:hover { color: #0077cc; }
|
||||
.ant-layout-content {
|
||||
background: #f5f7fa !important;
|
||||
}
|
||||
/* Dashboard card styling */
|
||||
.ant-card {
|
||||
box-shadow: 0 2px 8px rgba(0, 26, 58, 0.08);
|
||||
border-radius: 6px;
|
||||
}
|
||||
.ant-card-head-title {
|
||||
color: #00458a !important;
|
||||
}
|
||||
/* Gold accent for active menu */
|
||||
.ant-menu-dark .ant-menu-item-selected > a {
|
||||
color: #f0c040 !important;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
gemo="point"
|
||||
position="value*1"
|
||||
shape="pointer"
|
||||
color="#1890FF"
|
||||
color="#00458a"
|
||||
:active="false"
|
||||
></v-series>
|
||||
<v-guide
|
||||
@@ -171,7 +171,7 @@
|
||||
},
|
||||
arcGuide2Start: [0, 0.945],
|
||||
arcGuide2Style: {
|
||||
stroke: '#1890FF',
|
||||
stroke: '#00458a',
|
||||
lineWidth: 18,
|
||||
},
|
||||
htmlGuidePosition: ['50%', '100%'],
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
.progress {
|
||||
transition: all .4s cubic-bezier(.08, .82, .17, 1) 0s;
|
||||
border-radius: 1px 0 0 1px;
|
||||
background-color: #1890ff;
|
||||
background-color: #00458a;
|
||||
width: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
color:#1890ff
|
||||
color:#00458a
|
||||
}
|
||||
.platform-modal {
|
||||
padding:20px;
|
||||
@@ -219,7 +219,7 @@
|
||||
text-align: center;
|
||||
.title {
|
||||
font-size: 28px;
|
||||
background: linear-gradient(135deg, #1890ff, #722ed1);
|
||||
background: linear-gradient(135deg, #00458a, #722ed1);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
:collapsible="collapsible"
|
||||
v-model="collapsed"
|
||||
:trigger="null">
|
||||
<logo />
|
||||
<logo :collapsed="collapsed" />
|
||||
<s-menu
|
||||
:collapsed="collapsed"
|
||||
:menu="menus"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<div class="footer">
|
||||
<div class="copyright">© 2015-2030 MILESTONE INDUSTRIAL DEVELOPMENT CORPORATION</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -334,7 +334,7 @@
|
||||
|
||||
.avatar {
|
||||
margin: 20px 10px 20px 0;
|
||||
color: #1890ff;
|
||||
color: #00458a;
|
||||
background: hsla(0, 0%, 100%, .85);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
@@ -312,7 +312,7 @@
|
||||
padding-top: 15px;
|
||||
padding-left: 24px;
|
||||
height: 100%;
|
||||
color: #1890ff;
|
||||
color: #00458a;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
}
|
||||
@@ -340,7 +340,7 @@
|
||||
.setting-drawer-index-handle {
|
||||
position: absolute;
|
||||
top: 240px;
|
||||
background: #1890ff;
|
||||
background: #00458a;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
right: 300px;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<template>
|
||||
<div class="logo">
|
||||
<h1 v-if="showTitle">{{ title }}</h1>
|
||||
<div class="logo" :class="{ 'logo-collapsed': collapsed }">
|
||||
<div class="logo-content">
|
||||
<div class="logo-icon">🏔️</div>
|
||||
<h1 v-if="showTitle && !collapsed" class="logo-title">{{ title }}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -20,38 +23,80 @@
|
||||
type: Boolean,
|
||||
default: true,
|
||||
required: false
|
||||
},
|
||||
collapsed: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
/*缩小首页布 局顶部的高度*/
|
||||
@height: 49px;
|
||||
|
||||
.sider {
|
||||
box-shadow: none !important;
|
||||
.logo {
|
||||
color: #ffffff;
|
||||
height: @height !important;
|
||||
line-height: @height !important;
|
||||
box-shadow: none !important;
|
||||
transition: background 300ms;
|
||||
background: #002140;
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
color: #ffffff;
|
||||
transition: all 300ms ease;
|
||||
background: linear-gradient(135deg, #001a3a 0%, #003d82 100%);
|
||||
padding: 0 12px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
.logo-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
.logo-icon {
|
||||
font-size: 22px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 22px;
|
||||
}
|
||||
|
||||
.logo-title {
|
||||
font-size: 14px;
|
||||
color: #ffffff;
|
||||
margin: 0 0 0 8px;
|
||||
font-weight: 600;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
opacity: 1;
|
||||
transition: opacity 300ms ease;
|
||||
}
|
||||
}
|
||||
|
||||
// 折叠状态:只显示图标居中
|
||||
&.logo-collapsed {
|
||||
padding: 0;
|
||||
|
||||
.logo-content {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: white;
|
||||
&:hover {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.sider {
|
||||
box-shadow: none !important;
|
||||
|
||||
&.light .logo {
|
||||
background-color: @primary-color;
|
||||
background: linear-gradient(135deg, @primary-color 0%, #096dd9 100%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -42,9 +42,12 @@
|
||||
</span>
|
||||
<header-notice class="action"/>
|
||||
<a-dropdown>
|
||||
<span v-if="isDesktop()" class="action ant-dropdown-link user-dropdown-menu">
|
||||
<a-icon type="down-circle"/>
|
||||
<span style="margin-left:4px">欢迎您,{{ nickname() }}</span>
|
||||
<span v-if="isDesktop()" class="action ant-dropdown-link user-dropdown-menu modern-user-menu">
|
||||
<a-avatar size="small" style="margin-right: 8px;">
|
||||
<a-icon type="user" />
|
||||
</a-avatar>
|
||||
<span class="user-name">{{ nickname() }}</span>
|
||||
<a-icon type="down" style="margin-left: 8px; font-size: 12px;" />
|
||||
</span>
|
||||
<a-menu slot="overlay" class="user-dropdown-menu-wrapper">
|
||||
<a-menu-item key="3" @click="systemSetting">
|
||||
@@ -254,4 +257,34 @@
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.modern-user-menu {
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.user-wrapper.light .modern-user-menu {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -20,7 +20,7 @@ const colorList = [
|
||||
key: '极光绿', color: '#52C41A',
|
||||
},
|
||||
{
|
||||
key: '拂晓蓝(默认)', color: '#1890FF',
|
||||
key: '拂晓蓝(默认)', color: '#00458a',
|
||||
},
|
||||
{
|
||||
key: '极客蓝', color: '#2F54EB',
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
*/
|
||||
|
||||
export default {
|
||||
primaryColor: '#1890FF', // primary color of ant design
|
||||
navTheme: 'light', // theme for nav menu
|
||||
primaryColor: '#00458a', // primary color of ant design
|
||||
navTheme: 'dark', // theme for nav menu
|
||||
layout: 'sidemenu', // nav menu position: sidemenu or topmenu
|
||||
contentWidth: 'Fixed', // layout of content: Fluid or Fixed, only works when layout is topmenu
|
||||
fixedHeader: true, // sticky header
|
||||
|
||||
@@ -64,7 +64,7 @@ new Vue({
|
||||
store.commit('TOGGLE_CONTENT_WIDTH', Vue.ls.get(DEFAULT_CONTENT_WIDTH_TYPE, config.contentWidth))
|
||||
store.commit('TOGGLE_FIXED_HEADER_HIDDEN', Vue.ls.get(DEFAULT_FIXED_HEADER_HIDDEN, config.autoHideHeader))
|
||||
store.commit('TOGGLE_WEAK', Vue.ls.get(DEFAULT_COLOR_WEAK, config.colorWeak))
|
||||
store.commit('TOGGLE_COLOR', Vue.ls.get(DEFAULT_COLOR, config.primaryColor))
|
||||
store.commit('TOGGLE_COLOR', config.primaryColor)
|
||||
store.commit('SET_TOKEN', Vue.ls.get(ACCESS_TOKEN))
|
||||
store.commit('SET_MULTI_PAGE',Vue.ls.get(DEFAULT_MULTI_PAGE,config.multipage))
|
||||
},
|
||||
|
||||
@@ -1,96 +1,153 @@
|
||||
<template>
|
||||
<div class="page-header-index-wide">
|
||||
<div class="page-header-index-wide dashboard-modern">
|
||||
<!-- 添加欢迎区块 -->
|
||||
<div class="welcome-banner" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white; padding: 24px; border-radius: 8px; margin-bottom: 24px;">
|
||||
<h2 style="color: white; margin: 0;">欢迎回到 MileStone ERP 📊</h2>
|
||||
<p style="margin: 8px 0 0 0; opacity: 0.9;">{{ getCurrentTimeGreeting() }}</p>
|
||||
</div>
|
||||
|
||||
<!-- 核心指标卡片区域 - 增强视觉效果 -->
|
||||
<div class="metrics-section">
|
||||
<h3 style="margin-bottom: 16px; color: #1890ff;">📈 今日经营概况</h3>
|
||||
<a-row :gutter="24">
|
||||
<a-col :sm="24" :md="12" :xl="4" :style="{ paddingRight: '0px',marginBottom: '12px' }">
|
||||
<chart-card :loading="loading" title="今日销售" data-step="1" data-title="今日销售" data-intro="统计今日销售单据的总金额">
|
||||
<a-col :sm="24" :md="12" :xl="6" :style="{ paddingRight: '0px',marginBottom: '16px' }">
|
||||
<chart-card :loading="loading" title="今日销售" data-step="1" data-title="今日销售" data-intro="统计今日销售单据的总金额"
|
||||
class="metric-card sales-card">
|
||||
<a-tooltip title="统计今日销售单据的总金额" slot="action">
|
||||
<a-icon type="info-circle-o" />
|
||||
</a-tooltip>
|
||||
<head-info :content="statistics.todaySale"></head-info>
|
||||
<head-info :content="statistics.todaySale">
|
||||
<span slot="prefix">💰</span>
|
||||
</head-info>
|
||||
</chart-card>
|
||||
</a-col>
|
||||
<a-col :sm="24" :md="12" :xl="4" :style="{ paddingRight: '0px',marginBottom: '12px' }">
|
||||
<chart-card :loading="loading" title="今日零售" data-step="2" data-title="今日零售" data-intro="统计今日零售单据的总金额">
|
||||
<a-col :sm="24" :md="12" :xl="6" :style="{ paddingRight: '0px',marginBottom: '16px' }">
|
||||
<chart-card :loading="loading" title="今日零售" data-step="2" data-title="今日零售" data-intro="统计今日零售单据的总金额"
|
||||
class="metric-card retail-card">
|
||||
<a-tooltip title="统计今日零售单据的总金额" slot="action">
|
||||
<a-icon type="info-circle-o" />
|
||||
</a-tooltip>
|
||||
<head-info :content="statistics.todayRetailSale"></head-info>
|
||||
<head-info :content="statistics.todayRetailSale">
|
||||
<span slot="prefix">🛒</span>
|
||||
</head-info>
|
||||
</chart-card>
|
||||
</a-col>
|
||||
<a-col :sm="24" :md="12" :xl="4" :style="{ paddingRight: '0px',marginBottom: '12px' }">
|
||||
<chart-card :loading="loading" title="今日采购" data-step="3" data-title="今日采购" data-intro="统计今日采购单据的总金额">
|
||||
<a-col :sm="24" :md="12" :xl="6" :style="{ paddingRight: '0px',marginBottom: '16px' }">
|
||||
<chart-card :loading="loading" title="今日采购" data-step="3" data-title="今日采购" data-intro="统计今日采购单据的总金额"
|
||||
class="metric-card purchase-card">
|
||||
<a-tooltip title="统计今日采购单据的总金额" slot="action">
|
||||
<a-icon type="info-circle-o" />
|
||||
</a-tooltip>
|
||||
<head-info :content="statistics.todayBuy"></head-info>
|
||||
<head-info :content="statistics.todayBuy">
|
||||
<span slot="prefix">📦</span>
|
||||
</head-info>
|
||||
</chart-card>
|
||||
</a-col>
|
||||
<a-col :sm="24" :md="12" :xl="4" :style="{ paddingRight: '0px',marginBottom: '12px' }">
|
||||
<chart-card :loading="loading" title="本月累计销售">
|
||||
<a-col :sm="24" :md="12" :xl="6" :style="{ paddingRight: '0px',marginBottom: '16px' }">
|
||||
<chart-card :loading="loading" title="当日利润预估" class="metric-card profit-card">
|
||||
<a-tooltip title="今日销售减去今日采购的简单估算" slot="action">
|
||||
<a-icon type="info-circle-o" />
|
||||
</a-tooltip>
|
||||
<head-info :content="getTodayProfit()">
|
||||
<span slot="prefix">💎</span>
|
||||
</head-info>
|
||||
</chart-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
|
||||
<!-- 月度数据区域 -->
|
||||
<div class="monthly-section" style="margin-top: 32px;">
|
||||
<h3 style="margin-bottom: 16px; color: #52c41a;">📅 本月累计数据</h3>
|
||||
<a-row :gutter="24">
|
||||
<a-col :sm="24" :md="8" :xl="8" :style="{ paddingRight: '0px',marginBottom: '16px' }">
|
||||
<chart-card :loading="loading" title="本月累计销售" class="monthly-card">
|
||||
<a-tooltip title="统计本月销售单据的总金额" slot="action">
|
||||
<a-icon type="info-circle-o" />
|
||||
</a-tooltip>
|
||||
<head-info :content="statistics.monthSale"></head-info>
|
||||
<head-info :content="statistics.monthSale">
|
||||
<span slot="prefix">📊</span>
|
||||
</head-info>
|
||||
</chart-card>
|
||||
</a-col>
|
||||
<a-col :sm="24" :md="12" :xl="4" :style="{ paddingRight: '0px',marginBottom: '12px' }">
|
||||
<chart-card :loading="loading" title="本月累计零售">
|
||||
<a-col :sm="24" :md="8" :xl="8" :style="{ paddingRight: '0px',marginBottom: '16px' }">
|
||||
<chart-card :loading="loading" title="本月累计零售" class="monthly-card">
|
||||
<a-tooltip title="统计本月零售单据的总金额" slot="action">
|
||||
<a-icon type="info-circle-o" />
|
||||
</a-tooltip>
|
||||
<head-info :content="statistics.monthRetailSale"></head-info>
|
||||
<head-info :content="statistics.monthRetailSale">
|
||||
<span slot="prefix">🛍️</span>
|
||||
</head-info>
|
||||
</chart-card>
|
||||
</a-col>
|
||||
<a-col :sm="24" :md="12" :xl="4" :style="{ paddingRight: '0px',marginBottom: '12px' }">
|
||||
<chart-card :loading="loading" title="本月累计采购">
|
||||
<a-col :sm="24" :md="8" :xl="8" :style="{ paddingRight: '0px',marginBottom: '16px' }">
|
||||
<chart-card :loading="loading" title="本月累计采购" class="monthly-card">
|
||||
<a-tooltip placement="left" title="统计本月采购单据的总金额" slot="action">
|
||||
<a-icon type="info-circle-o" />
|
||||
</a-tooltip>
|
||||
<head-info :content="statistics.monthBuy"></head-info>
|
||||
<head-info :content="statistics.monthBuy">
|
||||
<span slot="prefix">📋</span>
|
||||
</head-info>
|
||||
</chart-card>
|
||||
</a-col>
|
||||
<a-col :sm="24" :md="12" :xl="4" :style="{ paddingRight: '0px',marginBottom: '12px' }">
|
||||
<chart-card :loading="loading" title="昨日销售">
|
||||
</a-row>
|
||||
</div>
|
||||
|
||||
<!-- 历史对比区域 -->
|
||||
<div class="comparison-section" style="margin-top: 32px;">
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="12">
|
||||
<h4 style="margin-bottom: 16px; color: #fa8c16;">📈 昨日数据</h4>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="8">
|
||||
<chart-card :loading="loading" title="昨日销售" class="comparison-card yesterday-card">
|
||||
<a-tooltip title="统计昨日销售单据的总金额" slot="action">
|
||||
<a-icon type="info-circle-o" />
|
||||
</a-tooltip>
|
||||
<head-info :content="statistics.yesterdaySale"></head-info>
|
||||
</chart-card>
|
||||
</a-col>
|
||||
<a-col :sm="24" :md="12" :xl="4" :style="{ paddingRight: '0px',marginBottom: '12px' }">
|
||||
<chart-card :loading="loading" title="昨日零售">
|
||||
<a-col :span="8">
|
||||
<chart-card :loading="loading" title="昨日零售" class="comparison-card yesterday-card">
|
||||
<a-tooltip title="统计昨日零售单据的总金额" slot="action">
|
||||
<a-icon type="info-circle-o" />
|
||||
</a-tooltip>
|
||||
<head-info :content="statistics.yesterdayRetailSale"></head-info>
|
||||
</chart-card>
|
||||
</a-col>
|
||||
<a-col :sm="24" :md="12" :xl="4" :style="{ paddingRight: '0px',marginBottom: '12px' }">
|
||||
<chart-card :loading="loading" title="昨日采购">
|
||||
<a-col :span="8">
|
||||
<chart-card :loading="loading" title="昨日采购" class="comparison-card yesterday-card">
|
||||
<a-tooltip title="统计昨日采购单据的总金额" slot="action">
|
||||
<a-icon type="info-circle-o" />
|
||||
</a-tooltip>
|
||||
<head-info :content="statistics.yesterdayBuy"></head-info>
|
||||
</chart-card>
|
||||
</a-col>
|
||||
<a-col :sm="24" :md="12" :xl="4" :style="{ paddingRight: '0px',marginBottom: '12px' }">
|
||||
<chart-card :loading="loading" title="今年累计销售">
|
||||
</a-row>
|
||||
</a-col>
|
||||
|
||||
<a-col :span="12">
|
||||
<h4 style="margin-bottom: 16px; color: #722ed1;">🏆 年度累计</h4>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="8">
|
||||
<chart-card :loading="loading" title="年度销售" class="comparison-card yearly-card">
|
||||
<a-tooltip title="统计今年销售单据的总金额" slot="action">
|
||||
<a-icon type="info-circle-o" />
|
||||
</a-tooltip>
|
||||
<head-info :content="statistics.yearSale"></head-info>
|
||||
</chart-card>
|
||||
</a-col>
|
||||
<a-col :sm="24" :md="12" :xl="4" :style="{ paddingRight: '0px',marginBottom: '12px' }">
|
||||
<chart-card :loading="loading" title="今年累计零售">
|
||||
<a-col :span="8">
|
||||
<chart-card :loading="loading" title="年度零售" class="comparison-card yearly-card">
|
||||
<a-tooltip title="统计今年零售单据的总金额" slot="action">
|
||||
<a-icon type="info-circle-o" />
|
||||
</a-tooltip>
|
||||
<head-info :content="statistics.yearRetailSale"></head-info>
|
||||
</chart-card>
|
||||
</a-col>
|
||||
<a-col :sm="24" :md="12" :xl="4" :style="{ paddingRight: '0px',marginBottom: '12px' }">
|
||||
<chart-card :loading="loading" title="今年累计采购">
|
||||
<a-col :span="8">
|
||||
<chart-card :loading="loading" title="年度采购" class="comparison-card yearly-card">
|
||||
<a-tooltip placement="left" title="统计今年采购单据的总金额" slot="action">
|
||||
<a-icon type="info-circle-o" />
|
||||
</a-tooltip>
|
||||
@@ -98,6 +155,9 @@
|
||||
</chart-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
<a-row :gutter="24">
|
||||
<a-col :sm="24" :md="12" :xl="8" :style="{ paddingRight: '0px',marginBottom: '12px' }">
|
||||
<a-card :loading="loading" :bordered="false" :body-style="{paddingRight: '5'}" data-step="4" data-title="销售统计"
|
||||
@@ -133,7 +193,7 @@
|
||||
</div>
|
||||
<a-button type="link" v-if="showWeixinSpan()">MileStone Co. ERP</a-button>
|
||||
</a-popover>
|
||||
© 2015-2030 {{systemTitle}} V3.6
|
||||
© 2015-2030 MILESTONE INDUSTRIAL DEVELOPMENT CORPORATION
|
||||
</div>
|
||||
<a-tag v-if="tenant.type==0" color="blue">试用到期:{{tenant.expireTime}}</a-tag>
|
||||
<a-tag v-if="tenant.type==0" color="blue">试用用户:{{tenant.userCurrentNum}}/{{tenant.userNumLimit}}</a-tag>
|
||||
@@ -276,11 +336,135 @@
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
},
|
||||
getCurrentTimeGreeting() {
|
||||
const now = new Date()
|
||||
const hour = now.getHours()
|
||||
const date = now.toLocaleDateString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
weekday: 'long'
|
||||
})
|
||||
|
||||
let greeting = ''
|
||||
if (hour < 6) {
|
||||
greeting = '夜深了,注意休息 🌙'
|
||||
} else if (hour < 12) {
|
||||
greeting = '早上好,新的一天开始了 ☀️'
|
||||
} else if (hour < 18) {
|
||||
greeting = '下午好,继续加油 💪'
|
||||
} else {
|
||||
greeting = '晚上好,今天辛苦了 🌅'
|
||||
}
|
||||
|
||||
return `${greeting} | ${date}`
|
||||
},
|
||||
getTodayProfit() {
|
||||
// 简单计算当日利润预估(销售-采购)
|
||||
const saleText = this.statistics.todaySale || '0'
|
||||
const buyText = this.statistics.todayBuy || '0'
|
||||
const sale = parseFloat(saleText.replace(/[,¥]/g, '') || '0')
|
||||
const buy = parseFloat(buyText.replace(/[,¥]/g, '') || '0')
|
||||
const profit = sale - buy
|
||||
|
||||
if (profit > 0) {
|
||||
return '¥' + profit.toLocaleString()
|
||||
} else if (profit < 0) {
|
||||
return '-¥' + Math.abs(profit).toLocaleString()
|
||||
}
|
||||
return '¥0'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.dashboard-modern {
|
||||
.welcome-banner {
|
||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.15);
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.25);
|
||||
}
|
||||
}
|
||||
|
||||
.metrics-section, .monthly-section, .comparison-section {
|
||||
.ant-card {
|
||||
transition: all 0.3s ease;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 8px 25px rgba(0,0,0,0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.metric-card {
|
||||
position: relative;
|
||||
|
||||
&.sales-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 4px;
|
||||
height: 100%;
|
||||
background: linear-gradient(180deg, #f56565 0%, #e53e3e 100%);
|
||||
}
|
||||
|
||||
&.retail-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 4px;
|
||||
height: 100%;
|
||||
background: linear-gradient(180deg, #48bb78 0%, #38a169 100%);
|
||||
}
|
||||
|
||||
&.purchase-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 4px;
|
||||
height: 100%;
|
||||
background: linear-gradient(180deg, #4299e1 0%, #3182ce 100%);
|
||||
}
|
||||
|
||||
&.profit-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 4px;
|
||||
height: 100%;
|
||||
background: linear-gradient(180deg, #ed8936 0%, #dd6b20 100%);
|
||||
}
|
||||
}
|
||||
|
||||
.monthly-card {
|
||||
border-left: 4px solid #52c41a;
|
||||
background: linear-gradient(135deg, #f6ffed 0%, #ffffff 100%);
|
||||
}
|
||||
|
||||
.comparison-card {
|
||||
&.yesterday-card {
|
||||
border-left: 3px solid #fa8c16;
|
||||
background: linear-gradient(135deg, #fff7e6 0%, #ffffff 100%);
|
||||
}
|
||||
|
||||
&.yearly-card {
|
||||
border-left: 3px solid #722ed1;
|
||||
background: linear-gradient(135deg, #f9f0ff 0%, #ffffff 100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.circle-cust{
|
||||
position: relative;
|
||||
top: 28px;
|
||||
|
||||
@@ -1,6 +1,73 @@
|
||||
<!-- b y 7 5 2 7 1 8 9 2 0 -->
|
||||
<template>
|
||||
<div class="main" :style="mainStyle">
|
||||
<div class="login-page">
|
||||
<!-- Background -->
|
||||
<div class="login-bg"></div>
|
||||
<div class="login-overlay"></div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="login-content">
|
||||
<!-- Left: Branding (desktop) -->
|
||||
<div class="login-brand" v-if="device !== 'mobile'">
|
||||
<div class="brand-inner">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 320" width="200" height="160" class="brand-logo">
|
||||
<defs>
|
||||
<linearGradient id="lg1" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop offset="0%" style="stop-color:#c8962a" />
|
||||
<stop offset="50%" style="stop-color:#f0c040" />
|
||||
<stop offset="100%" style="stop-color:#c8962a" />
|
||||
</linearGradient>
|
||||
<filter id="s1" x="-5%" y="-5%" width="115%" height="115%">
|
||||
<feDropShadow dx="2" dy="4" stdDeviation="4" flood-color="#000" flood-opacity="0.3"/>
|
||||
</filter>
|
||||
<filter id="g1">
|
||||
<feGaussianBlur stdDeviation="2" result="blur"/>
|
||||
<feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
<polygon points="70,210 130,70 190,210" fill="rgba(255,255,255,0.85)" filter="url(#s1)" opacity="0.9"/>
|
||||
<polygon points="130,70 135,78 75,210 70,210" fill="rgba(255,255,255,0.5)" opacity="0.6"/>
|
||||
<polygon points="140,210 200,30 260,210" fill="rgba(255,255,255,0.95)" filter="url(#s1)"/>
|
||||
<polygon points="200,30 206,40 146,210 140,210" fill="rgba(255,255,255,0.6)" opacity="0.7"/>
|
||||
<polygon points="210,210 270,70 330,210" fill="rgba(255,255,255,0.85)" filter="url(#s1)" opacity="0.9"/>
|
||||
<polygon points="270,70 275,78 215,210 210,210" fill="rgba(255,255,255,0.5)" opacity="0.6"/>
|
||||
<rect x="60" y="218" width="280" height="5" rx="2.5" fill="url(#lg1)" filter="url(#g1)"/>
|
||||
<text x="200" y="258" text-anchor="middle" font-family="'Arial Black','Helvetica Neue',sans-serif" font-size="38" font-weight="900" letter-spacing="8" fill="#ffffff">MILESTONE</text>
|
||||
<line x1="95" y1="266" x2="305" y2="266" stroke="url(#lg1)" stroke-width="1.5" opacity="0.6"/>
|
||||
<text x="200" y="290" text-anchor="middle" font-family="'Helvetica Neue',Arial,sans-serif" font-size="11" letter-spacing="4" fill="rgba(255,255,255,0.7)" font-weight="300">INDUSTRIAL DEVELOPMENT CORPORATION</text>
|
||||
</svg>
|
||||
<h2 class="brand-slogan">Building A Better Tomorrow</h2>
|
||||
<p class="brand-desc">
|
||||
Milestone Industrial Development Corporation is committed to delivering excellence in construction and industrial development. With years of experience and a dedication to quality, we build the foundations for a stronger future.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right: Login Form -->
|
||||
<div class="login-form-wrapper">
|
||||
<!-- Mobile logo -->
|
||||
<div class="mobile-logo" v-if="device === 'mobile'">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 320" width="140" height="112">
|
||||
<defs>
|
||||
<linearGradient id="lg1m" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop offset="0%" style="stop-color:#c8962a" />
|
||||
<stop offset="50%" style="stop-color:#f0c040" />
|
||||
<stop offset="100%" style="stop-color:#c8962a" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<polygon points="70,210 130,70 190,210" fill="rgba(255,255,255,0.85)" opacity="0.9"/>
|
||||
<polygon points="140,210 200,30 260,210" fill="rgba(255,255,255,0.95)"/>
|
||||
<polygon points="210,210 270,70 330,210" fill="rgba(255,255,255,0.85)" opacity="0.9"/>
|
||||
<rect x="60" y="218" width="280" height="5" rx="2.5" fill="url(#lg1m)"/>
|
||||
<text x="200" y="258" text-anchor="middle" font-family="'Arial Black','Helvetica Neue',sans-serif" font-size="38" font-weight="900" letter-spacing="8" fill="#ffffff">MILESTONE</text>
|
||||
<text x="200" y="290" text-anchor="middle" font-family="'Helvetica Neue',Arial,sans-serif" font-size="11" letter-spacing="4" fill="rgba(255,255,255,0.7)" font-weight="300">INDUSTRIAL DEVELOPMENT CORPORATION</text>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="login-card">
|
||||
<h3 class="login-title">Welcome Back</h3>
|
||||
<p class="login-subtitle">Sign in to your account</p>
|
||||
|
||||
<a-form :form="form" class="user-layout-login" ref="formLogin" id="formLogin">
|
||||
<a-form-item>
|
||||
<a-input
|
||||
@@ -49,7 +116,7 @@
|
||||
</router-link>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item :style="btnStyle">
|
||||
<a-form-item style="margin-top:16px">
|
||||
<a-button
|
||||
size="large"
|
||||
type="primary"
|
||||
@@ -60,17 +127,17 @@
|
||||
:disabled="loginBtn">登 录
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
|
||||
<div class="login-copyright" v-if="device === 'mobile'">
|
||||
<a-row>
|
||||
<a-col>
|
||||
© 2015-2030 Powered By
|
||||
<a style="color:#00458a;" :href="systemUrl" target="_blank">官方网站</a>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</a-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="login-footer">
|
||||
© 2015-2030 MILESTONE INDUSTRIAL DEVELOPMENT CORPORATION. All Rights Reserved.
|
||||
<a style="color:rgba(255,255,255,0.6);margin-left:8px;" :href="systemUrl" target="_blank">Official Website</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- BY cao_yu_li -->
|
||||
<script>
|
||||
@@ -96,7 +163,6 @@
|
||||
systemTitle: window.SYS_TITLE,
|
||||
systemUrl: window.SYS_URL,
|
||||
loginBtn: false,
|
||||
// login type: 0 email, 1 username, 2 telephone
|
||||
loginType: 0,
|
||||
requiredTwoStepCaptcha: false,
|
||||
stepCaptchaVisible: false,
|
||||
@@ -115,7 +181,7 @@
|
||||
inputCode:{rules: [{ required: true, message: '请输入验证码!',validator: 'click'}]}
|
||||
},
|
||||
verifiedCode:"",
|
||||
inputCodeContent:"", //20200510 cfm: 为方便测试,不输入验证码可 ""-->"xxxx"
|
||||
inputCodeContent:"",
|
||||
inputCodeNull:true,
|
||||
departList:[],
|
||||
departVisible:false,
|
||||
@@ -135,7 +201,6 @@
|
||||
},
|
||||
created () {
|
||||
this.loadInfo()
|
||||
this.checkScreen()
|
||||
this.currdatetime = new Date().getTime();
|
||||
Vue.ls.remove(ACCESS_TOKEN)
|
||||
this.getRouterData()
|
||||
@@ -145,9 +210,7 @@
|
||||
},
|
||||
methods: {
|
||||
...mapActions([ "Login", "Logout" ]),
|
||||
// handler
|
||||
loadInfo() {
|
||||
//从缓存中获取登录名和密码
|
||||
this.$nextTick(() => {
|
||||
if(Vue.ls.get('cache_loginName') && Vue.ls.get('cache_password')) {
|
||||
this.form.setFieldsValue({'loginName': Vue.ls.get('cache_loginName')})
|
||||
@@ -155,10 +218,8 @@
|
||||
this.checked = true
|
||||
}
|
||||
})
|
||||
//从注册页面跳转过来,给登录名进行赋值
|
||||
if(this.$route.params.loginName) {
|
||||
this.$nextTick(() => {
|
||||
//先清空缓存
|
||||
Vue.ls.remove('cache_loginName')
|
||||
Vue.ls.remove('cache_password')
|
||||
this.form.setFieldsValue({'loginName':this.$route.params.loginName})
|
||||
@@ -176,7 +237,6 @@
|
||||
}
|
||||
callback()
|
||||
},
|
||||
//切换勾选
|
||||
handleChange(e) {
|
||||
this.checked = e.target.checked
|
||||
},
|
||||
@@ -198,7 +258,6 @@
|
||||
let that = this
|
||||
let loginParams = {};
|
||||
that.loginBtn = true;
|
||||
// 使用账户密码登陆
|
||||
if (that.customActiveKey === 'tab1') {
|
||||
that.form.validateFields([ 'loginName', 'password', 'inputCode' ], { force: true }, (err, values) => {
|
||||
if (!err) {
|
||||
@@ -207,11 +266,9 @@
|
||||
loginParams.code = values.inputCode
|
||||
loginParams.uuid = that.uuid
|
||||
if(that.checked) {
|
||||
//勾选的时候进行缓存
|
||||
Vue.ls.set('cache_loginName', values.loginName)
|
||||
Vue.ls.set('cache_password', values.password)
|
||||
} else {
|
||||
//没勾选的时候清缓存
|
||||
Vue.ls.remove('cache_loginName')
|
||||
Vue.ls.remove('cache_password')
|
||||
}
|
||||
@@ -257,16 +314,14 @@
|
||||
})
|
||||
getAction("/user/infoWithTenant",{}).then(res=>{
|
||||
if(res && res.code === 200) {
|
||||
let currentTime = new Date(); //新建一个日期对象,默认现在的时间
|
||||
let expireTime = new Date(res.data.expireTime); //设置过去的一个时间点,"yyyy-MM-dd HH:mm:ss"格式化日期
|
||||
let type = res.data.type //租户类型,0免费租户,1付费租户
|
||||
let difftime = expireTime - currentTime; //计算时间差
|
||||
let currentTime = new Date();
|
||||
let expireTime = new Date(res.data.expireTime);
|
||||
let type = res.data.type
|
||||
let difftime = expireTime - currentTime;
|
||||
let tipInfo = '您好,服务即将到期,请及时续费!'
|
||||
//0免费租户-如果距离到期还剩5天就进行提示续费
|
||||
if(type === '0' && difftime<86400000*5) {
|
||||
this.$message.warning(tipInfo,8)
|
||||
}
|
||||
//1付费租户-如果距离到期还剩15天就进行提示续费
|
||||
if(type === '1' && difftime<86400000*15) {
|
||||
this.$message.warning(tipInfo,8)
|
||||
}
|
||||
@@ -289,7 +344,6 @@
|
||||
description: ((err.response || {}).data || {}).message || err.message || err.data.message || "请求出现错误,请稍后再试",
|
||||
duration: 4,
|
||||
});
|
||||
//验证码刷新
|
||||
this.form.setFieldsValue({'inputCode':''})
|
||||
this.handleChangeCheckCode()
|
||||
this.loginBtn = false;
|
||||
@@ -353,16 +407,8 @@
|
||||
getCheckcodeFlag(){
|
||||
getAction('/platformConfig/getPlatform/checkcodeFlag').then((res) => {
|
||||
this.checkcodeFlag = res + ''
|
||||
if(this.checkcodeFlag === '1') {
|
||||
this.mainStyle = ''
|
||||
this.btnStyle = 'margin-top:16px'
|
||||
} else {
|
||||
this.mainStyle = 'padding-top:20px'
|
||||
this.btnStyle = 'margin-top:26px'
|
||||
}
|
||||
})
|
||||
},
|
||||
//获取密码加密规则
|
||||
getEncrypte(){
|
||||
var encryptedString = Vue.ls.get(ENCRYPTED_STRING);
|
||||
if(encryptedString == null){
|
||||
@@ -373,67 +419,121 @@
|
||||
this.encryptedString = encryptedString;
|
||||
}
|
||||
},
|
||||
//加载商品属性
|
||||
initMPropertyShort(){
|
||||
getAction('/materialProperty/getAllList').then((res) => {
|
||||
if(res && res.code === 200){
|
||||
if(res.data) {
|
||||
let thisRows = res.data; //属性列表
|
||||
let thisRows = res.data;
|
||||
Vue.ls.set('materialPropertyList', thisRows, 7 * 24 * 60 * 60 * 1000);
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
checkScreen() {
|
||||
let percentage = '100%'
|
||||
let basicWidth = 1920
|
||||
const currentWidth = window.screen.width
|
||||
const currentHeight = window.screen.height
|
||||
//浏览器的当前比例
|
||||
const currentRatio = window.devicePixelRatio.toFixed(2)
|
||||
//浏览器需要调整的比例
|
||||
let needRatio = 1
|
||||
let ratio = currentWidth/basicWidth
|
||||
if(ratio>0.5 && ratio<0.67) {
|
||||
percentage = '50%'
|
||||
needRatio = 0.5
|
||||
} if(ratio>=0.67 && ratio<0.75) {
|
||||
percentage = '67%'
|
||||
needRatio = 0.67
|
||||
} else if(ratio>=0.75 && ratio<0.8) {
|
||||
percentage = '75%'
|
||||
needRatio = 0.75
|
||||
} else if(ratio>=0.8 && ratio<0.9) {
|
||||
percentage = '80%'
|
||||
needRatio = 0.8
|
||||
} else if(ratio>=1.1 && ratio<1.25) {
|
||||
percentage = '110%'
|
||||
needRatio = 1.1
|
||||
} else if(ratio>=1.25 && ratio<1.5) {
|
||||
percentage = '125%'
|
||||
needRatio = 1.25
|
||||
} else if(ratio>=1.5 && ratio<1.75) {
|
||||
percentage = '150%'
|
||||
needRatio = 1.5
|
||||
}
|
||||
//console.log(currentRatio)
|
||||
//console.log(needRatio)
|
||||
if(currentRatio-0 !== needRatio) {
|
||||
this.openNotificationWithIcon('warning', currentWidth, currentHeight, percentage)
|
||||
}
|
||||
},
|
||||
openNotificationWithIcon(type, currentWidth, currentHeight, percentage) {
|
||||
this.$notification[type]({
|
||||
message: '浏览器的缩放比例调整提示',
|
||||
description: '检测到您显示器的分辨率为:' + currentWidth + '*' + currentHeight + ' ,为了获得更好的操作体验,建议您将浏览器的缩放比例调整至' + percentage,
|
||||
duration: 10
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.login-page {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.login-bg {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: url('/static/bg-construction.jpg') center center / cover no-repeat;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.login-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 26, 58, 0.75);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.login-content {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: calc(100vh - 50px);
|
||||
padding: 40px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.login-brand {
|
||||
flex: 0 0 55%;
|
||||
padding-right: 60px;
|
||||
|
||||
.brand-inner {
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.brand-logo {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.brand-slogan {
|
||||
color: #f0c040;
|
||||
font-size: 28px;
|
||||
font-weight: 300;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 20px;
|
||||
font-family: 'Georgia', serif;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.brand-desc {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-size: 15px;
|
||||
line-height: 1.8;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
}
|
||||
|
||||
.login-form-wrapper {
|
||||
flex: 0 0 400px;
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||
border-radius: 16px;
|
||||
padding: 40px 36px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
||||
|
||||
.login-title {
|
||||
color: #ffffff;
|
||||
font-size: 26px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.login-subtitle {
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
font-size: 14px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.user-layout-login {
|
||||
label {
|
||||
font-size: 14px;
|
||||
@@ -443,12 +543,6 @@
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.getCaptcha {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.forge-password {
|
||||
font-size: 14px;
|
||||
font-weight: bolder;
|
||||
@@ -457,50 +551,74 @@
|
||||
button.login-button {
|
||||
padding: 0 15px;
|
||||
font-size: 16px;
|
||||
height: 40px;
|
||||
height: 44px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.user-login-other {
|
||||
text-align: left;
|
||||
margin-top: 24px;
|
||||
line-height: 22px;
|
||||
|
||||
.item-icon {
|
||||
font-size: 24px;
|
||||
color: rgba(0,0,0,.2);
|
||||
margin-left: 16px;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
transition: color .3s;
|
||||
border-radius: 8px;
|
||||
background: linear-gradient(135deg, #0077cc, #00458a);
|
||||
border: none;
|
||||
font-weight: 600;
|
||||
letter-spacing: 2px;
|
||||
|
||||
&:hover {
|
||||
color: #1890ff;
|
||||
background: linear-gradient(135deg, #0088ee, #0055aa);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.register {
|
||||
float: right;
|
||||
}
|
||||
.login-footer {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
text-align: center;
|
||||
padding: 15px;
|
||||
color: rgba(255, 255, 255, 0.45);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.weixin {
|
||||
padding-left:10px;
|
||||
color: red;
|
||||
cursor:pointer
|
||||
}
|
||||
.mobile-logo {
|
||||
text-align: center;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
// Mobile responsive
|
||||
@media (max-width: 768px) {
|
||||
.login-content {
|
||||
flex-direction: column;
|
||||
padding: 20px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.login-form-wrapper {
|
||||
flex: none;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
padding: 30px 24px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.login-card .ant-input {
|
||||
background: rgba(255, 255, 255, 0.9) !important;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3) !important;
|
||||
border-radius: 6px !important;
|
||||
}
|
||||
.login-card .ant-input:focus,
|
||||
.login-card .ant-input-focused {
|
||||
background: #ffffff !important;
|
||||
border-color: #0077cc !important;
|
||||
}
|
||||
.login-card .ant-checkbox-wrapper {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
.login-card .forge-password {
|
||||
color: #f0c040 !important;
|
||||
}
|
||||
.login-card .ant-form-explain {
|
||||
color: #ff6b6b;
|
||||
}
|
||||
.valid-error .ant-select-selection__placeholder{
|
||||
color: #f5222d;
|
||||
}
|
||||
.login-copyright {
|
||||
text-align: center;
|
||||
margin-top: 20px
|
||||
}
|
||||
.login-copyright, .login-copyright a {
|
||||
color: #666
|
||||
}
|
||||
</style>
|
||||
@@ -36,8 +36,8 @@ module.exports = {
|
||||
less: {
|
||||
modifyVars: {
|
||||
/* less 变量覆盖,用于自定义 ant design 主题 */
|
||||
'primary-color': '#1890FF',
|
||||
'link-color': '#1890FF',
|
||||
'primary-color': '#00458a',
|
||||
'link-color': '#00458a',
|
||||
'border-radius-base': '4px'
|
||||
},
|
||||
javascriptEnabled: true
|
||||
|
||||
Reference in New Issue
Block a user