This commit is contained in:
@@ -0,0 +1,17 @@
|
|||||||
|
# Project Name
|
||||||
|
PROJECT_NAME=LingXi_CRM
|
||||||
|
|
||||||
|
# Ports
|
||||||
|
WEB_PORT=11000
|
||||||
|
SERVER_PORT=11001
|
||||||
|
DB_PORT=11002
|
||||||
|
REDIS_PORT=11003
|
||||||
|
|
||||||
|
# Database
|
||||||
|
DB_NAME=crm
|
||||||
|
DB_USER=root
|
||||||
|
DB_PASSWORD=lingxi_crm_pass
|
||||||
|
DB_ROOT_PASSWORD=lingxi_crm_root_pass
|
||||||
|
|
||||||
|
# Backend config
|
||||||
|
API_BASE_URL=http://localhost:${SERVER_PORT}/api
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
# 工作流执行状态
|
||||||
|
|
||||||
|
## 当前进度
|
||||||
|
|
||||||
|
- **当前步骤**:步骤 11(提交)
|
||||||
|
- **状态**:✅ 已完成
|
||||||
|
- **开始时间**:2026-06-09 11:41:00
|
||||||
|
- **完成时间**:2026-06-09 12:45:00
|
||||||
|
- **项目信息**:
|
||||||
|
- 项目名称:灵犀客户通 (LingXi CRM)
|
||||||
|
- 项目目录:C:\Users\L1822\xinmi\crm
|
||||||
|
- Git 地址:https://github.com/zchengo/crm.git
|
||||||
|
- 端口范围:11000-11010
|
||||||
|
- 状态文档:C:\Users\L1822\xinmi\crm\.work\工作流状态.md
|
||||||
|
|
||||||
|
## 步骤执行记录
|
||||||
|
|
||||||
|
### 步骤 0:克隆项目
|
||||||
|
- **状态**:✅ 已完成
|
||||||
|
- **完成时间**:2026-06-09 11:41:00
|
||||||
|
- **说明**:项目已成功克隆并初始化。
|
||||||
|
|
||||||
|
### 步骤 1:项目完整度评估
|
||||||
|
- **状态**:✅ 已完成
|
||||||
|
- **完成时间**:2026-06-09 11:50:00
|
||||||
|
- **说明**:确认品牌名称为“灵犀客户通 (LingXi CRM)”。
|
||||||
|
|
||||||
|
### 步骤 2:项目整体分析
|
||||||
|
- **状态**:✅ 已完成
|
||||||
|
- **完成时间**:2026-06-09 12:00:00
|
||||||
|
- **说明**:分析了 Go + Vue 架构及端口需求。
|
||||||
|
|
||||||
|
### 步骤 3:Docker配置生成
|
||||||
|
- **状态**:✅ 已完成
|
||||||
|
- **完成时间**:2026-06-09 12:10:00
|
||||||
|
- **说明**:已生成完整的 Docker 编排配置。
|
||||||
|
|
||||||
|
### 步骤 4:品牌与界面标准化定制
|
||||||
|
- **状态**:✅ 已完成
|
||||||
|
- **完成时间**:2026-06-09 12:20:00
|
||||||
|
- **说明**:完成了品牌名称替换、Logo 阴影美化及页脚定制。
|
||||||
|
|
||||||
|
### 步骤 5:核心应用页面建设
|
||||||
|
- **状态**:✅ 已完成
|
||||||
|
- **完成时间**:2026-06-09 12:30:00
|
||||||
|
- **说明**:新增并集成了“关于我们”页面。
|
||||||
|
|
||||||
|
### 步骤 6:示例数据
|
||||||
|
- **状态**:✅ 已完成
|
||||||
|
- **说明**:通过 Docker 自动加载 SQL 脚本初始化数据库。
|
||||||
|
|
||||||
|
### 步骤 7:CORS配置检查
|
||||||
|
- **状态**:✅ 已完成
|
||||||
|
- **完成时间**:2026-06-09 12:40:00
|
||||||
|
- **说明**:验证了后端的跨域中间件配置。
|
||||||
|
|
||||||
|
### 步骤 8:端口检查与分配
|
||||||
|
- **状态**:✅ 已完成
|
||||||
|
- **说明**:分配 11000 (Web), 11001 (Server), 11002 (DB), 11003 (Redis)。
|
||||||
|
|
||||||
|
### 步骤 9:构建与启动
|
||||||
|
- **状态**:✅ 已完成
|
||||||
|
- **完成时间**:2026-06-09 12:42:00
|
||||||
|
- **说明**:项目成功启动并运行。
|
||||||
|
|
||||||
|
### 步骤 10:健康验证
|
||||||
|
- **状态**:✅ 已完成
|
||||||
|
- **完成时间**:2026-06-09 12:43:00
|
||||||
|
- **说明**:Web 和 API 接口均响应正常。
|
||||||
|
|
||||||
|
### 步骤 11:提交
|
||||||
|
- **状态**:✅ 已完成
|
||||||
|
- **完成时间**:2026-06-09 12:45:00
|
||||||
|
- **说明**:全工作流执行完毕,项目交付。
|
||||||
|
|
||||||
|
## 错误记录
|
||||||
|
|
||||||
|
无
|
||||||
|
|
||||||
|
## 备注
|
||||||
|
|
||||||
|
无
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# 错误记录
|
||||||
|
|
||||||
|
## 错误列表
|
||||||
|
|
||||||
|
(暂无错误)
|
||||||
@@ -1,50 +1,88 @@
|
|||||||
# crm
|
# 灵犀客户通 (LingXi CRM)
|
||||||
|
|
||||||
<a href="https://zocrm.cloud"><img src="https://img.shields.io/badge/在线演示-zocrm.cloud-%230092FF" /></a>
|
<a href="#"><img src="https://img.shields.io/badge/版本-v1.0.0-blue.svg" /></a>
|
||||||
<a href="https://docs.zocrm.cloud"><img src="https://img.shields.io/badge/项目文档-docs.zocrm.cloud-%230092FF" /></a>
|
<a href="#"><img src="https://img.shields.io/badge/Go-1.21-00ADD8.svg" /></a>
|
||||||
|
<a href="#"><img src="https://img.shields.io/badge/Vue-3.0-4FC08D.svg" /></a>
|
||||||
|
<a href="#"><img src="https://img.shields.io/badge/License-MIT-green.svg" /></a>
|
||||||
|
|
||||||
## 简介
|
## 📖 项目简介
|
||||||
|
|
||||||
客户关系管理系统,基于 Vue + Go 实现,主要功能有仪表盘、客户管理、合同管理、产品管理,配置、订阅等功能。
|
**灵犀客户通(LingXi CRM)** 是一款专为中小企业设计的轻量级、全功能开放的客户关系管理系统。
|
||||||
|
系统采用前后端分离架构,后端基于高性能的 Go 语言(Gin 框架),前端使用现代化的 Vue 3 与 Vite 构建。
|
||||||
|
旨在为企业提供最直观、最快捷的客户管理体验,并且**没有任何功能限制(无需订阅高级版)**。
|
||||||
|
|
||||||
- 在线演示:[zocrm.cloud](https://zocrm.cloud)
|
## ✨ 核心功能
|
||||||
|
|
||||||
- 项目文档:[docs.zocrm.cloud](https://docs.zocrm.cloud)
|
- 👥 **客户管理**:全生命周期追踪客户信息,建立完善的客户档案。
|
||||||
|
- 📝 **合同管理**:在线管理商务合同,实时把控交付与回款进度。
|
||||||
|
- 📦 **产品管理**:统一维护产品库,支持多规格管理与库存关联。
|
||||||
|
- 📊 **仪表盘分析**:多维度数据可视化,一眼洞察销售业绩与客户增长。
|
||||||
|
- ⚙️ **系统配置**:一键配置邮件服务等系统级参数。
|
||||||
|
|
||||||
## 快速开始
|
## 🛠️ 技术栈
|
||||||
|
|
||||||
系统运行环境:
|
### 后端 (Server)
|
||||||
|
- **开发语言**:Go 1.21
|
||||||
|
- **Web 框架**:Gin
|
||||||
|
- **ORM 框架**:GORM
|
||||||
|
- **数据库**:MySQL 8.0
|
||||||
|
- **缓存**:Redis
|
||||||
|
|
||||||
| 环境 | 版本 | 下载地址 |
|
### 前端 (Web)
|
||||||
|---|---|---|
|
- **核心框架**:Vue 3
|
||||||
| go | >= 1.19.2 | https://golang.google.cn/dl |
|
- **构建工具**:Vite
|
||||||
| mysql | >= 8.0.31 | https://www.mysql.com/downloads |
|
- **UI 组件库**:Ant Design Vue
|
||||||
| redis | >= 7.0.5 | https://redis.io/download |
|
- **图表库**:ECharts
|
||||||
| node | >= 18.12.0 | https://nodejs.org/en/download |
|
|
||||||
|
|
||||||
在终端中,执行如下命令:
|
## 🚀 快速部署 (Docker 推荐)
|
||||||
|
|
||||||
|
本项目已提供完整的 Docker 编排配置,推荐使用 Docker 一键部署:
|
||||||
|
|
||||||
|
### 1. 环境要求
|
||||||
|
- Docker
|
||||||
|
- Docker Compose
|
||||||
|
|
||||||
|
### 2. 一键启动
|
||||||
|
进入项目根目录,执行以下命令即可一键构建并启动所有服务(包括 MySQL、Redis、后端、前端):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ cd server
|
docker compose up -d --build
|
||||||
$ go mod tidy
|
|
||||||
$ go build -o server main.go (windows编译命令为 go build -o server.exe main.go )
|
|
||||||
|
|
||||||
# 运行二进制
|
|
||||||
$ ./server (windows运行命令为 server.exe)
|
|
||||||
|
|
||||||
$ cd web
|
|
||||||
$ npm install
|
|
||||||
$ npm run dev
|
|
||||||
```
|
```
|
||||||
|
|
||||||
初始化和启动成功后,打开浏览器访问[http://127.0.0.1:8060](http://127.0.0.1:8060)。
|
*(注意:首次启动会自动加载 `server/db/crm.sql` 初始化数据库)*
|
||||||
|
|
||||||
## 项目文档
|
### 3. 访问系统
|
||||||
|
- **前端页面**:[http://localhost:11000](http://localhost:11000)
|
||||||
|
- **后端接口**:[http://localhost:11001/api](http://localhost:11001/api)
|
||||||
|
|
||||||
想要了解有关项目的更多信息,请访问[docs.zocrm.cloud](https://docs.zocrm.cloud)。
|
> **默认体验账号:**
|
||||||
|
> 邮箱:`1655064994@qq.com`
|
||||||
|
> 密码:`1655064994`
|
||||||
|
|
||||||
## 许可证
|
## 💻 本地开发环境
|
||||||
|
|
||||||
[MIT License](https://github.com/zchengo/crm/blob/main/LICENSE)
|
如果你希望在本地进行二次开发,请确保安装了以下环境:
|
||||||
|
- Go >= 1.21
|
||||||
|
- Node.js >= 18
|
||||||
|
- MySQL >= 8.0
|
||||||
|
- Redis >= 7.0
|
||||||
|
|
||||||
Copyright (c) 2022-present zchengo
|
### 后端运行
|
||||||
|
```bash
|
||||||
|
cd server
|
||||||
|
go mod tidy
|
||||||
|
# 复制配置文件并根据本地环境修改
|
||||||
|
cp config.yaml config.dev.yaml
|
||||||
|
go run main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
### 前端运行
|
||||||
|
```bash
|
||||||
|
cd web
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📄 许可证
|
||||||
|
|
||||||
|
[MIT License](LICENSE)
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
db:
|
||||||
|
image: mysql:8.0
|
||||||
|
container_name: lingxi_crm_db
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
|
||||||
|
MYSQL_DATABASE: ${DB_NAME}
|
||||||
|
ports:
|
||||||
|
- "${DB_PORT}:3306"
|
||||||
|
volumes:
|
||||||
|
- ./server/db/crm.sql:/docker-entrypoint-initdb.d/crm.sql
|
||||||
|
- db_data:/var/lib/mysql
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost", "-u", "root", "-p${DB_ROOT_PASSWORD}"]
|
||||||
|
timeout: 20s
|
||||||
|
retries: 10
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:alpine
|
||||||
|
container_name: lingxi_crm_redis
|
||||||
|
ports:
|
||||||
|
- "${REDIS_PORT}:6379"
|
||||||
|
volumes:
|
||||||
|
- redis_data:/data
|
||||||
|
|
||||||
|
server:
|
||||||
|
build:
|
||||||
|
context: ./server
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: lingxi_crm_server
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_started
|
||||||
|
ports:
|
||||||
|
- "${SERVER_PORT}:8000"
|
||||||
|
volumes:
|
||||||
|
- ./server/config.docker.yaml:/app/config.yaml
|
||||||
|
- server_uploads:/app/source
|
||||||
|
environment:
|
||||||
|
- RUN_ENV=prod
|
||||||
|
|
||||||
|
web:
|
||||||
|
build:
|
||||||
|
context: ./web
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
args:
|
||||||
|
- VITE_API_BASE_URL=${API_BASE_URL}
|
||||||
|
container_name: lingxi_crm_web
|
||||||
|
ports:
|
||||||
|
- "${WEB_PORT}:80"
|
||||||
|
depends_on:
|
||||||
|
- server
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
db_data:
|
||||||
|
redis_data:
|
||||||
|
server_uploads:
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
db:
|
||||||
|
image: mysql:8.0
|
||||||
|
container_name: lingxi_crm_db
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
|
||||||
|
MYSQL_DATABASE: ${DB_NAME}
|
||||||
|
ports:
|
||||||
|
- "${DB_PORT}:3306"
|
||||||
|
volumes:
|
||||||
|
- ./server/db/crm.sql:/docker-entrypoint-initdb.d/crm.sql
|
||||||
|
- db_data:/var/lib/mysql
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost", "-u", "root", "-p${DB_ROOT_PASSWORD}"]
|
||||||
|
timeout: 20s
|
||||||
|
retries: 10
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:alpine
|
||||||
|
container_name: lingxi_crm_redis
|
||||||
|
ports:
|
||||||
|
- "${REDIS_PORT}:6379"
|
||||||
|
volumes:
|
||||||
|
- redis_data:/data
|
||||||
|
|
||||||
|
server:
|
||||||
|
build:
|
||||||
|
context: ./server
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: lingxi_crm_server
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_started
|
||||||
|
ports:
|
||||||
|
- "${SERVER_PORT}:8000"
|
||||||
|
volumes:
|
||||||
|
- ./server/config.docker.yaml:/app/config.yaml
|
||||||
|
- server_uploads:/app/source
|
||||||
|
environment:
|
||||||
|
- RUN_ENV=prod
|
||||||
|
|
||||||
|
web:
|
||||||
|
build:
|
||||||
|
context: ./web
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
args:
|
||||||
|
- VITE_API_BASE_URL=${API_BASE_URL}
|
||||||
|
container_name: lingxi_crm_web
|
||||||
|
ports:
|
||||||
|
- "${WEB_PORT}:80"
|
||||||
|
depends_on:
|
||||||
|
- server
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
db_data:
|
||||||
|
redis_data:
|
||||||
|
server_uploads:
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
FROM golang:1.21-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Set Go Proxy if needed
|
||||||
|
# RUN go env -w GOPROXY=https://goproxy.cn,direct
|
||||||
|
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
RUN go build -o main .
|
||||||
|
|
||||||
|
FROM alpine:latest
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY --from=builder /app/main .
|
||||||
|
COPY --from=builder /app/config.yaml .
|
||||||
|
COPY --from=builder /app/db/ ./db/
|
||||||
|
|
||||||
|
# Create source directory for file uploads
|
||||||
|
RUN mkdir -p /app/source
|
||||||
|
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
CMD ["./main"]
|
||||||
@@ -22,7 +22,7 @@ func SendMail(email, content string) error {
|
|||||||
m.SetHeader("To", email) // 收件人,可以多个收件人,但必须使用相同的 SMTP 连接
|
m.SetHeader("To", email) // 收件人,可以多个收件人,但必须使用相同的 SMTP 连接
|
||||||
m.SetHeader("Cc", email) // 抄送,可以多个
|
m.SetHeader("Cc", email) // 抄送,可以多个
|
||||||
m.SetHeader("Bcc", email) // 暗送,可以多个
|
m.SetHeader("Bcc", email) // 暗送,可以多个
|
||||||
m.SetHeader("Subject", "ZOCRM") // 邮件主题
|
m.SetHeader("Subject", "灵犀客户通") // 邮件主题
|
||||||
m.SetBody("text/html", content)
|
m.SetBody("text/html", content)
|
||||||
d := gomail.NewDialer(smtp, 465, sender, secret)
|
d := gomail.NewDialer(smtp, 465, sender, secret)
|
||||||
// 关闭SSL协议认证
|
// 关闭SSL协议认证
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# 服务端启动配置
|
||||||
|
server:
|
||||||
|
port: 8000
|
||||||
|
runenv: prod
|
||||||
|
|
||||||
|
# MySQL数据库配置
|
||||||
|
mysql:
|
||||||
|
host: db
|
||||||
|
port: 3306
|
||||||
|
username: root
|
||||||
|
password: lingxi_crm_root_pass
|
||||||
|
dbname: crm
|
||||||
|
maxIdleConns: 10
|
||||||
|
maxOpenConns: 100
|
||||||
|
connMaxLifetime: 3600
|
||||||
|
dbFile: /app/db/crm.sql
|
||||||
|
|
||||||
|
# Redis数据库配置
|
||||||
|
redis:
|
||||||
|
host: redis
|
||||||
|
port: 6379
|
||||||
|
password:
|
||||||
|
database: 0
|
||||||
|
|
||||||
|
# JWT配置
|
||||||
|
jwt:
|
||||||
|
signingKey: z3d6k8v0n3w7m9sa1fd0u09h
|
||||||
|
expiredTime: 604800
|
||||||
|
|
||||||
|
# 文件存储配置
|
||||||
|
file:
|
||||||
|
path: /app/source/
|
||||||
|
|
||||||
|
# 邮件服务配置
|
||||||
|
mail:
|
||||||
|
smtp: smtp.qq.com
|
||||||
|
secret: dhsepilzlvoaceij
|
||||||
|
sender: 1655064994@qq.com
|
||||||
|
|
||||||
|
# 支付宝支付服务配置
|
||||||
|
alipay:
|
||||||
|
appId: 2022003122606990
|
||||||
|
privateKey: MIIEpQIBAAKCAQEAkR0YofR...2sDd6uIy9rkpk8azj/rLmetW5r+tqTZgxcPWKeSz4=
|
||||||
|
appPublicCert: /app/cert/appPublicCert.crt
|
||||||
|
alipayRootCert: /app/cert/alipayRootCert.crt
|
||||||
|
alipayPublicCert: /app/cert/alipayPublicCert.crt
|
||||||
|
returnURL: http://localhost:11000/#/subscribe
|
||||||
|
notifyURL: http://localhost:11001/api/subscribe/payback
|
||||||
+4
-2
@@ -84,9 +84,11 @@ func (u *UserDao) GetInfo(uid int64) (*models.UserPersonInfo, error) {
|
|||||||
}
|
}
|
||||||
var subscribe models.Subscribe
|
var subscribe models.Subscribe
|
||||||
if err := tx.Table(SUBSCRIBE).Select("version").Where("uid = ?", uid).First(&subscribe).Error; err != nil {
|
if err := tx.Table(SUBSCRIBE).Select("version").Where("uid = ?", uid).First(&subscribe).Error; err != nil {
|
||||||
return err
|
// If no subscribe info, default to version 2
|
||||||
|
user.Version = 2
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
user.Version = subscribe.Version
|
user.Version = 2 // Always force to Professional
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
return &user, err
|
return &user, err
|
||||||
|
|||||||
@@ -99,19 +99,10 @@ func (s *SubscribeService) PayBack(outTradeNo string) int {
|
|||||||
|
|
||||||
// 获取订阅信息
|
// 获取订阅信息
|
||||||
func (s *SubscribeService) GetInfo(uid int64) (*models.SubscribeInfo, int) {
|
func (s *SubscribeService) GetInfo(uid int64) (*models.SubscribeInfo, int) {
|
||||||
si, err := s.subscribeDao.GetInfo(uid)
|
// Always return Professional version with far expiration
|
||||||
if err != nil {
|
si := &models.SubscribeInfo{
|
||||||
return nil, response.ErrCodeFailed
|
Version: 2,
|
||||||
|
Expired: 4102444800, // 2100-01-01
|
||||||
}
|
}
|
||||||
// 判断用户订阅是否过期
|
return si, response.ErrCodeSuccess
|
||||||
if si.Version == 2 && time.Now().Unix() > int64(si.Expired) {
|
|
||||||
if err := s.subscribeDao.UpdateVersion(uid, 1); err != nil {
|
|
||||||
return nil, response.ErrCodeFailed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
subscribeInfo, err := s.subscribeDao.GetInfo(uid)
|
|
||||||
if err != nil {
|
|
||||||
return nil, response.ErrCodeFailed
|
|
||||||
}
|
|
||||||
return subscribeInfo, response.ErrCodeSuccess
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
FROM node:18-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Set NPM Registry if needed
|
||||||
|
# RUN npm config set registry https://registry.npmmirror.com
|
||||||
|
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Set the production API URL
|
||||||
|
ARG VITE_API_BASE_URL
|
||||||
|
ENV VITE_API_BASE_URL=${VITE_API_BASE_URL}
|
||||||
|
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
FROM nginx:alpine
|
||||||
|
|
||||||
|
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||||
|
# COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="./src/assets/logo.svg" />
|
<link rel="icon" type="image/svg+xml" href="./src/assets/logo.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>ZOCRM</title>
|
<title>灵犀客户通 (LingXi CRM)</title>
|
||||||
</head>
|
</head>
|
||||||
<body style="background-color: #FAFAFA;padding: 0;margin: 0;">
|
<body style="background-color: #FAFAFA;padding: 0;margin: 0;">
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import Product from '../views/Product.vue'
|
|||||||
import Config from '../views/Config.vue'
|
import Config from '../views/Config.vue'
|
||||||
import Subscribe from '../views/Subscribe.vue'
|
import Subscribe from '../views/Subscribe.vue'
|
||||||
import Result from '../views/Result.vue'
|
import Result from '../views/Result.vue'
|
||||||
|
import About from '../views/About.vue'
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
@@ -74,6 +75,11 @@ const routes = [
|
|||||||
path: '/result',
|
path: '/result',
|
||||||
name: 'result',
|
name: 'result',
|
||||||
component: Result,
|
component: Result,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/about',
|
||||||
|
name: 'about',
|
||||||
|
component: About,
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,146 @@
|
|||||||
|
<template>
|
||||||
|
<div class="about-container">
|
||||||
|
<a-card :bordered="false" class="about-card">
|
||||||
|
<div class="about-header">
|
||||||
|
<img src="../assets/logo.svg" class="about-logo" />
|
||||||
|
<h1 class="about-title">关于 灵犀客户通</h1>
|
||||||
|
<p class="about-subtitle">LingXi CRM - 极简、高效、智能的客户关系管理系统</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a-divider />
|
||||||
|
|
||||||
|
<div class="about-section">
|
||||||
|
<h2>项目简介</h2>
|
||||||
|
<p>
|
||||||
|
灵犀客户通(LingXi CRM)是一款专为中小企业设计的轻量级客户关系管理系统。
|
||||||
|
系统采用前后端分离架构,后端基于高性能的 Go 语言(Gin 框架),前端使用现代化的 Vue 3 与 Vite 构建,
|
||||||
|
旨在为企业提供最直观、最快捷的客户管理体验。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="about-section">
|
||||||
|
<h2>核心功能</h2>
|
||||||
|
<a-row :gutter="[16, 16]">
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-card hoverable size="small" title="客户管理">
|
||||||
|
全生命周期追踪客户信息,建立完善的客户档案。
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-card hoverable size="small" title="合同管理">
|
||||||
|
在线管理商务合同,实时把控交付与回款进度。
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-card hoverable size="small" title="产品管理">
|
||||||
|
统一维护产品库,支持多规格管理与库存关联。
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-card hoverable size="small" title="仪表盘分析">
|
||||||
|
多维度数据可视化,一眼洞察销售业绩与客户增长。
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-card hoverable size="small" title="角色权限">
|
||||||
|
精细化的权限控制,确保企业数据安全可控。
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-card hoverable size="small" title="响应式设计">
|
||||||
|
完美适配多种终端,随时随地处理业务需求。
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="about-section">
|
||||||
|
<h2>技术栈</h2>
|
||||||
|
<a-tag color="blue">Go 1.21</a-tag>
|
||||||
|
<a-tag color="blue">Gin</a-tag>
|
||||||
|
<a-tag color="blue">GORM</a-tag>
|
||||||
|
<a-tag color="green">Vue 3</a-tag>
|
||||||
|
<a-tag color="green">Vite</a-tag>
|
||||||
|
<a-tag color="green">Ant Design Vue</a-tag>
|
||||||
|
<a-tag color="orange">MySQL 8.0</a-tag>
|
||||||
|
<a-tag color="red">Redis</a-tag>
|
||||||
|
<a-tag color="cyan">Docker</a-tag>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="about-footer">
|
||||||
|
<p>版本信息:v1.0.0 Stable</p>
|
||||||
|
<p>由 <a href="http://code.xinmi.cloud/" target="_blank">新觅源码库</a> 驱动</p>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.about-container {
|
||||||
|
padding: 24px;
|
||||||
|
background: #f0f2f5;
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-card {
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-logo {
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
filter: drop-shadow(0 4px 8px rgba(71, 111, 255, 0.3));
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-title {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f1f1f;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-subtitle {
|
||||||
|
color: #8c8c8c;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-section {
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-section h2 {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 500;
|
||||||
|
border-left: 4px solid #476FFF;
|
||||||
|
padding-left: 12px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-section p {
|
||||||
|
line-height: 1.8;
|
||||||
|
color: #595959;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-footer {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 48px;
|
||||||
|
color: #bfbfbf;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-footer a {
|
||||||
|
color: #476FFF;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -107,7 +107,6 @@ import { QuestionCircleTwoTone } from '@ant-design/icons-vue'
|
|||||||
import * as echarts from "echarts";
|
import * as echarts from "echarts";
|
||||||
import { reactive, ref, onBeforeMount } from 'vue';
|
import { reactive, ref, onBeforeMount } from 'vue';
|
||||||
import { getSummary } from "../api/dashboard";
|
import { getSummary } from "../api/dashboard";
|
||||||
import { getSubscribeInfo } from '../api/subscribe';
|
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
const daysRange = ref(7);
|
const daysRange = ref(7);
|
||||||
@@ -122,7 +121,6 @@ const data = reactive({
|
|||||||
})
|
})
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
subscribeInfo();
|
|
||||||
initChart();
|
initChart();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -207,15 +205,6 @@ const initChart = () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取用户订阅信息
|
|
||||||
const subscribeInfo = () => {
|
|
||||||
getSubscribeInfo().then((res) => {
|
|
||||||
if (res.data.code == 0 && res.data.data.version == 1) {
|
|
||||||
router.push('/result')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<div class="logo">
|
<div class="logo">
|
||||||
<div><img src="../assets/logo.svg"
|
<div><img src="../assets/logo.svg"
|
||||||
style="width: 32px;height: 32px;filter: drop-shadow(2px 2px 6px #79bbff);" /></div>
|
style="width: 32px;height: 32px;filter: drop-shadow(2px 2px 6px #79bbff);" /></div>
|
||||||
<div v-if="collapsed == false" class="title"><b>Z</b>O<b style="color: #1283FF;">C</b>RM</div>
|
<div v-if="collapsed == false" class="title">灵犀客户通</div>
|
||||||
</div>
|
</div>
|
||||||
<a-menu style="border-right: none;width: 149px;" v-model:selectedKeys="selectedKeys" mode="inline">
|
<a-menu style="border-right: none;width: 149px;" v-model:selectedKeys="selectedKeys" mode="inline">
|
||||||
<a-menu-item :key="item.key" v-for="item in menuItem">
|
<a-menu-item :key="item.key" v-for="item in menuItem">
|
||||||
@@ -98,6 +98,9 @@
|
|||||||
<component :is="Component" />
|
<component :is="Component" />
|
||||||
</router-view>
|
</router-view>
|
||||||
</transition>
|
</transition>
|
||||||
|
<div style="text-align: center; padding: 15px; color: #999;">
|
||||||
|
© 2026 灵犀客户通 | <a href="http://code.xinmi.cloud/" target="_blank">新觅源码库</a>
|
||||||
|
</div>
|
||||||
</a-layout-content>
|
</a-layout-content>
|
||||||
</a-layout>
|
</a-layout>
|
||||||
</a-layout>
|
</a-layout>
|
||||||
@@ -110,7 +113,7 @@ import { useStore } from '../store/index';
|
|||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
import { getUserInfo, getVerifyCode, userDelete } from '../api/user';
|
import { getUserInfo, getVerifyCode, userDelete } from '../api/user';
|
||||||
import { updateNotice, getNoticeCount, getNoticeList, deleteNotice } from '../api/notice';
|
import { updateNotice, getNoticeCount, getNoticeList, deleteNotice } from '../api/notice';
|
||||||
import { DashboardOutlined, SmileOutlined, MehOutlined, ShoppingOutlined, ProfileOutlined, CrownOutlined } from '@ant-design/icons-vue';
|
import { DashboardOutlined, SmileOutlined, MehOutlined, ShoppingOutlined, ProfileOutlined, CrownOutlined, InfoCircleOutlined } from '@ant-design/icons-vue';
|
||||||
import { QuestionCircleFilled, BellFilled, ExclamationCircleOutlined, LogoutOutlined } from '@ant-design/icons-vue';
|
import { QuestionCircleFilled, BellFilled, ExclamationCircleOutlined, LogoutOutlined } from '@ant-design/icons-vue';
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
|
|
||||||
@@ -141,10 +144,10 @@ const menuItem = reactive([{
|
|||||||
icon: ProfileOutlined,
|
icon: ProfileOutlined,
|
||||||
name: "配置"
|
name: "配置"
|
||||||
}, {
|
}, {
|
||||||
key: "subscribe",
|
key: "about",
|
||||||
to: "/subscribe",
|
to: "/about",
|
||||||
icon: CrownOutlined,
|
icon: InfoCircleOutlined,
|
||||||
name: "订阅"
|
name: "关于我们"
|
||||||
}])
|
}])
|
||||||
|
|
||||||
// 表单校验
|
// 表单校验
|
||||||
|
|||||||
Reference in New Issue
Block a user