Compare commits
10 Commits
4a424817de
...
a205a7a49d
| Author | SHA1 | Date | |
|---|---|---|---|
| a205a7a49d | |||
| 0e9f2197cd | |||
| b67b2d940d | |||
| ea8ad162d5 | |||
| 54c3653197 | |||
| 20dd98ec35 | |||
| b37dcd8a6f | |||
| 0f4298abc8 | |||
| e4ebf0fa3b | |||
| 2ab9a1d7bf |
@@ -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,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 zchengo
|
||||
Copyright (c) 2022-present zchengo
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,57 +1,88 @@
|
||||
# crm
|
||||
# 灵犀客户通 (LingXi CRM)
|
||||
|
||||
<a href="#公众号"><img src="https://img.shields.io/badge/公众号-GoCode-%2302DF6D" /></a>
|
||||
<a href="https://zocrm.cloud"><img src="https://img.shields.io/badge/在线演示-zocrm.cloud-%230092FF" /></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/版本-v1.0.0-blue.svg" /></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
|
||||
|
||||
| 环境 | 版本 | 下载地址 |
|
||||
|---|---|---|
|
||||
| go | >= 1.19.2 | https://golang.google.cn/dl |
|
||||
| mysql | >= 8.0.31 | https://www.mysql.com/downloads |
|
||||
| redis | >= 7.0.5 | https://redis.io/download |
|
||||
| node | >= 18.12.0 | https://nodejs.org/en/download |
|
||||
### 前端 (Web)
|
||||
- **核心框架**:Vue 3
|
||||
- **构建工具**:Vite
|
||||
- **UI 组件库**:Ant Design Vue
|
||||
- **图表库**:ECharts
|
||||
|
||||
在终端中,执行如下命令:
|
||||
## 🚀 快速部署 (Docker 推荐)
|
||||
|
||||
本项目已提供完整的 Docker 编排配置,推荐使用 Docker 一键部署:
|
||||
|
||||
### 1. 环境要求
|
||||
- Docker
|
||||
- Docker Compose
|
||||
|
||||
### 2. 一键启动
|
||||
进入项目根目录,执行以下命令即可一键构建并启动所有服务(包括 MySQL、Redis、后端、前端):
|
||||
|
||||
```bash
|
||||
$ cd server
|
||||
$ 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
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
初始化和启动成功后,打开浏览器访问[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`
|
||||
|
||||
## 公众号
|
||||
## 💻 本地开发环境
|
||||
|
||||
欢迎关注公众号「GoCode」,本公众号专注Go语言技术分享!
|
||||
如果你希望在本地进行二次开发,请确保安装了以下环境:
|
||||
- Go >= 1.21
|
||||
- Node.js >= 18
|
||||
- MySQL >= 8.0
|
||||
- Redis >= 7.0
|
||||
|
||||

|
||||
### 后端运行
|
||||
```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](https://github.com/zchengo/crm/blob/main/LICENSE)
|
||||
## 📄 许可证
|
||||
|
||||
Copyright (c) 2022 zchengo
|
||||
[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:
|
||||
+3
-3
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"hash": "3520588e",
|
||||
"browserHash": "76e04641",
|
||||
"hash": "ce2e264b",
|
||||
"browserHash": "e34b92f3",
|
||||
"optimized": {
|
||||
"vue": {
|
||||
"src": "../../../../node_modules/vue/dist/vue.runtime.esm-bundler.js",
|
||||
"file": "vue.js",
|
||||
"fileHash": "bef91d7b",
|
||||
"fileHash": "0c7ec6e0",
|
||||
"needsInterop": false
|
||||
}
|
||||
},
|
||||
|
||||
+127
-74
@@ -73,8 +73,8 @@ function normalizeProps(props) {
|
||||
}
|
||||
return props;
|
||||
}
|
||||
var HTML_TAGS = "html,body,base,head,link,meta,style,title,address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,nav,section,div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,ruby,s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,output,progress,select,textarea,details,dialog,menu,summary,template,blockquote,iframe,tfoot";
|
||||
var SVG_TAGS = "svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,feDistanceLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,text,textPath,title,tspan,unknown,use,view";
|
||||
var HTML_TAGS = "html,body,base,head,link,meta,style,title,address,article,aside,footer,header,hgroup,h1,h2,h3,h4,h5,h6,nav,section,div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,ruby,s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,output,progress,select,textarea,details,dialog,menu,summary,template,blockquote,iframe,tfoot";
|
||||
var SVG_TAGS = "svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,feDistantLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,text,textPath,title,tspan,unknown,use,view";
|
||||
var VOID_TAGS = "area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr";
|
||||
var isHTMLTag = makeMap(HTML_TAGS);
|
||||
var isSVGTag = makeMap(SVG_TAGS);
|
||||
@@ -181,6 +181,7 @@ var isArray = Array.isArray;
|
||||
var isMap = (val) => toTypeString(val) === "[object Map]";
|
||||
var isSet = (val) => toTypeString(val) === "[object Set]";
|
||||
var isDate = (val) => toTypeString(val) === "[object Date]";
|
||||
var isRegExp = (val) => toTypeString(val) === "[object RegExp]";
|
||||
var isFunction = (val) => typeof val === "function";
|
||||
var isString = (val) => typeof val === "string";
|
||||
var isSymbol = (val) => typeof val === "symbol";
|
||||
@@ -228,10 +229,14 @@ var def = (obj, key, value) => {
|
||||
value
|
||||
});
|
||||
};
|
||||
var toNumber = (val) => {
|
||||
var looseToNumber = (val) => {
|
||||
const n = parseFloat(val);
|
||||
return isNaN(n) ? val : n;
|
||||
};
|
||||
var toNumber = (val) => {
|
||||
const n = isString(val) ? Number(val) : NaN;
|
||||
return isNaN(n) ? val : n;
|
||||
};
|
||||
var _globalThis;
|
||||
var getGlobalThis = () => {
|
||||
return _globalThis || (_globalThis = typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {});
|
||||
@@ -245,7 +250,7 @@ var activeEffectScope;
|
||||
var EffectScope = class {
|
||||
constructor(detached = false) {
|
||||
this.detached = detached;
|
||||
this.active = true;
|
||||
this._active = true;
|
||||
this.effects = [];
|
||||
this.cleanups = [];
|
||||
this.parent = activeEffectScope;
|
||||
@@ -253,8 +258,11 @@ var EffectScope = class {
|
||||
this.index = (activeEffectScope.scopes || (activeEffectScope.scopes = [])).push(this) - 1;
|
||||
}
|
||||
}
|
||||
get active() {
|
||||
return this._active;
|
||||
}
|
||||
run(fn) {
|
||||
if (this.active) {
|
||||
if (this._active) {
|
||||
const currentEffectScope = activeEffectScope;
|
||||
try {
|
||||
activeEffectScope = this;
|
||||
@@ -281,7 +289,7 @@ var EffectScope = class {
|
||||
activeEffectScope = this.parent;
|
||||
}
|
||||
stop(fromParent) {
|
||||
if (this.active) {
|
||||
if (this._active) {
|
||||
let i, l;
|
||||
for (i = 0, l = this.effects.length; i < l; i++) {
|
||||
this.effects[i].stop();
|
||||
@@ -302,7 +310,7 @@ var EffectScope = class {
|
||||
}
|
||||
}
|
||||
this.parent = void 0;
|
||||
this.active = false;
|
||||
this._active = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -500,7 +508,7 @@ function trigger(target, type, key, newValue, oldValue, oldTarget) {
|
||||
if (type === "clear") {
|
||||
deps = [...depsMap.values()];
|
||||
} else if (key === "length" && isArray(target)) {
|
||||
const newLength = toNumber(newValue);
|
||||
const newLength = Number(newValue);
|
||||
depsMap.forEach((dep, key2) => {
|
||||
if (key2 === "length" || key2 >= newLength) {
|
||||
deps.push(dep);
|
||||
@@ -584,11 +592,15 @@ function triggerEffect(effect2, debuggerEventExtraInfo) {
|
||||
}
|
||||
}
|
||||
}
|
||||
function getDepFromReactive(object, key) {
|
||||
var _a2;
|
||||
return (_a2 = targetMap.get(object)) === null || _a2 === void 0 ? void 0 : _a2.get(key);
|
||||
}
|
||||
var isNonTrackableKeys = makeMap(`__proto__,__v_isRef,__isVue`);
|
||||
var builtInSymbols = new Set(
|
||||
Object.getOwnPropertyNames(Symbol).filter((key) => key !== "arguments" && key !== "caller").map((key) => Symbol[key]).filter(isSymbol)
|
||||
);
|
||||
var get = createGetter();
|
||||
var get$1 = createGetter();
|
||||
var shallowGet = createGetter(false, true);
|
||||
var readonlyGet = createGetter(true);
|
||||
var shallowReadonlyGet = createGetter(true, true);
|
||||
@@ -619,6 +631,11 @@ function createArrayInstrumentations() {
|
||||
});
|
||||
return instrumentations;
|
||||
}
|
||||
function hasOwnProperty2(key) {
|
||||
const obj = toRaw(this);
|
||||
track(obj, "has", key);
|
||||
return obj.hasOwnProperty(key);
|
||||
}
|
||||
function createGetter(isReadonly2 = false, shallow = false) {
|
||||
return function get2(target, key, receiver) {
|
||||
if (key === "__v_isReactive") {
|
||||
@@ -631,9 +648,14 @@ function createGetter(isReadonly2 = false, shallow = false) {
|
||||
return target;
|
||||
}
|
||||
const targetIsArray = isArray(target);
|
||||
if (!isReadonly2 && targetIsArray && hasOwn(arrayInstrumentations, key)) {
|
||||
if (!isReadonly2) {
|
||||
if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
|
||||
return Reflect.get(arrayInstrumentations, key, receiver);
|
||||
}
|
||||
if (key === "hasOwnProperty") {
|
||||
return hasOwnProperty2;
|
||||
}
|
||||
}
|
||||
const res = Reflect.get(target, key, receiver);
|
||||
if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
|
||||
return res;
|
||||
@@ -653,7 +675,7 @@ function createGetter(isReadonly2 = false, shallow = false) {
|
||||
return res;
|
||||
};
|
||||
}
|
||||
var set = createSetter();
|
||||
var set$1 = createSetter();
|
||||
var shallowSet = createSetter(true);
|
||||
function createSetter(shallow = false) {
|
||||
return function set2(target, key, value, receiver) {
|
||||
@@ -692,7 +714,7 @@ function deleteProperty(target, key) {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function has(target, key) {
|
||||
function has$1(target, key) {
|
||||
const result = Reflect.has(target, key);
|
||||
if (!isSymbol(key) || !builtInSymbols.has(key)) {
|
||||
track(target, "has", key);
|
||||
@@ -704,10 +726,10 @@ function ownKeys(target) {
|
||||
return Reflect.ownKeys(target);
|
||||
}
|
||||
var mutableHandlers = {
|
||||
get,
|
||||
set,
|
||||
get: get$1,
|
||||
set: set$1,
|
||||
deleteProperty,
|
||||
has,
|
||||
has: has$1,
|
||||
ownKeys
|
||||
};
|
||||
var readonlyHandlers = {
|
||||
@@ -734,7 +756,7 @@ var shallowReadonlyHandlers = extend({}, readonlyHandlers, {
|
||||
});
|
||||
var toShallow = (value) => value;
|
||||
var getProto = (v) => Reflect.getPrototypeOf(v);
|
||||
function get$1(target, key, isReadonly2 = false, isShallow3 = false) {
|
||||
function get(target, key, isReadonly2 = false, isShallow3 = false) {
|
||||
target = target[
|
||||
"__v_raw"
|
||||
/* ReactiveFlags.RAW */
|
||||
@@ -757,7 +779,7 @@ function get$1(target, key, isReadonly2 = false, isShallow3 = false) {
|
||||
target.get(key);
|
||||
}
|
||||
}
|
||||
function has$1(key, isReadonly2 = false) {
|
||||
function has(key, isReadonly2 = false) {
|
||||
const target = this[
|
||||
"__v_raw"
|
||||
/* ReactiveFlags.RAW */
|
||||
@@ -791,7 +813,7 @@ function add(value) {
|
||||
}
|
||||
return this;
|
||||
}
|
||||
function set$1(key, value) {
|
||||
function set(key, value) {
|
||||
value = toRaw(value);
|
||||
const target = toRaw(this);
|
||||
const { has: has2, get: get2 } = getProto(target);
|
||||
@@ -894,41 +916,41 @@ function createReadonlyMethod(type) {
|
||||
function createInstrumentations() {
|
||||
const mutableInstrumentations2 = {
|
||||
get(key) {
|
||||
return get$1(this, key);
|
||||
return get(this, key);
|
||||
},
|
||||
get size() {
|
||||
return size(this);
|
||||
},
|
||||
has: has$1,
|
||||
has,
|
||||
add,
|
||||
set: set$1,
|
||||
set,
|
||||
delete: deleteEntry,
|
||||
clear,
|
||||
forEach: createForEach(false, false)
|
||||
};
|
||||
const shallowInstrumentations2 = {
|
||||
get(key) {
|
||||
return get$1(this, key, false, true);
|
||||
return get(this, key, false, true);
|
||||
},
|
||||
get size() {
|
||||
return size(this);
|
||||
},
|
||||
has: has$1,
|
||||
has,
|
||||
add,
|
||||
set: set$1,
|
||||
set,
|
||||
delete: deleteEntry,
|
||||
clear,
|
||||
forEach: createForEach(false, true)
|
||||
};
|
||||
const readonlyInstrumentations2 = {
|
||||
get(key) {
|
||||
return get$1(this, key, true);
|
||||
return get(this, key, true);
|
||||
},
|
||||
get size() {
|
||||
return size(this, true);
|
||||
},
|
||||
has(key) {
|
||||
return has$1.call(this, key, true);
|
||||
return has.call(this, key, true);
|
||||
},
|
||||
add: createReadonlyMethod(
|
||||
"add"
|
||||
@@ -950,13 +972,13 @@ function createInstrumentations() {
|
||||
};
|
||||
const shallowReadonlyInstrumentations2 = {
|
||||
get(key) {
|
||||
return get$1(this, key, true, true);
|
||||
return get(this, key, true, true);
|
||||
},
|
||||
get size() {
|
||||
return size(this, true);
|
||||
},
|
||||
has(key) {
|
||||
return has$1.call(this, key, true);
|
||||
return has.call(this, key, true);
|
||||
},
|
||||
add: createReadonlyMethod(
|
||||
"add"
|
||||
@@ -1146,16 +1168,17 @@ function trackRefValue(ref2) {
|
||||
}
|
||||
function triggerRefValue(ref2, newVal) {
|
||||
ref2 = toRaw(ref2);
|
||||
if (ref2.dep) {
|
||||
const dep = ref2.dep;
|
||||
if (dep) {
|
||||
if (true) {
|
||||
triggerEffects(ref2.dep, {
|
||||
triggerEffects(dep, {
|
||||
target: ref2,
|
||||
type: "set",
|
||||
key: "value",
|
||||
newValue: newVal
|
||||
});
|
||||
} else {
|
||||
triggerEffects(ref2.dep);
|
||||
triggerEffects(dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1259,18 +1282,21 @@ var ObjectRefImpl = class {
|
||||
set value(newVal) {
|
||||
this._object[this._key] = newVal;
|
||||
}
|
||||
get dep() {
|
||||
return getDepFromReactive(toRaw(this._object), this._key);
|
||||
}
|
||||
};
|
||||
function toRef(object, key, defaultValue) {
|
||||
const val = object[key];
|
||||
return isRef(val) ? val : new ObjectRefImpl(object, key, defaultValue);
|
||||
}
|
||||
var _a;
|
||||
var _a$1;
|
||||
var ComputedRefImpl = class {
|
||||
constructor(getter, _setter, isReadonly2, isSSR) {
|
||||
this._setter = _setter;
|
||||
this.dep = void 0;
|
||||
this.__v_isRef = true;
|
||||
this[_a] = false;
|
||||
this[_a$1] = false;
|
||||
this._dirty = true;
|
||||
this.effect = new ReactiveEffect(getter, () => {
|
||||
if (!this._dirty) {
|
||||
@@ -1298,7 +1324,7 @@ var ComputedRefImpl = class {
|
||||
this._setter(newValue);
|
||||
}
|
||||
};
|
||||
_a = "__v_isReadonly";
|
||||
_a$1 = "__v_isReadonly";
|
||||
function computed(getterOrOptions, debugOptions, isSSR = false) {
|
||||
let getter;
|
||||
let setter;
|
||||
@@ -1319,9 +1345,9 @@ function computed(getterOrOptions, debugOptions, isSSR = false) {
|
||||
}
|
||||
return cRef;
|
||||
}
|
||||
var _a$1;
|
||||
var _a;
|
||||
var tick = Promise.resolve();
|
||||
_a$1 = "__v_isReadonly";
|
||||
_a = "__v_isReadonly";
|
||||
|
||||
// node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js
|
||||
var stack = [];
|
||||
@@ -1419,6 +1445,17 @@ function formatProp(key, value, raw) {
|
||||
return raw ? value : [`${key}=`, value];
|
||||
}
|
||||
}
|
||||
function assertNumber(val, type) {
|
||||
if (false)
|
||||
return;
|
||||
if (val === void 0) {
|
||||
return;
|
||||
} else if (typeof val !== "number") {
|
||||
warn2(`${type} is not a valid number - got ${JSON.stringify(val)}.`);
|
||||
} else if (isNaN(val)) {
|
||||
warn2(`${type} is NaN - the duration expression might be incorrect.`);
|
||||
}
|
||||
}
|
||||
var ErrorTypeStrings = {
|
||||
[
|
||||
"sp"
|
||||
@@ -1870,7 +1907,7 @@ function tryWrap(fn) {
|
||||
var devtools;
|
||||
var buffer = [];
|
||||
var devtoolsNotInstalled = false;
|
||||
function emit(event, ...args) {
|
||||
function emit$1(event, ...args) {
|
||||
if (devtools) {
|
||||
devtools.emit(event, ...args);
|
||||
} else if (!devtoolsNotInstalled) {
|
||||
@@ -1907,7 +1944,7 @@ function setDevtoolsHook(hook, target) {
|
||||
}
|
||||
}
|
||||
function devtoolsInitApp(app, version2) {
|
||||
emit("app:init", app, version2, {
|
||||
emit$1("app:init", app, version2, {
|
||||
Fragment,
|
||||
Text,
|
||||
Comment,
|
||||
@@ -1915,7 +1952,7 @@ function devtoolsInitApp(app, version2) {
|
||||
});
|
||||
}
|
||||
function devtoolsUnmountApp(app) {
|
||||
emit("app:unmount", app);
|
||||
emit$1("app:unmount", app);
|
||||
}
|
||||
var devtoolsComponentAdded = createDevtoolsComponentHook(
|
||||
"component:added"
|
||||
@@ -1937,7 +1974,7 @@ var devtoolsComponentRemoved = (component) => {
|
||||
};
|
||||
function createDevtoolsComponentHook(hook) {
|
||||
return (component) => {
|
||||
emit(hook, component.appContext.app, component.uid, component.parent ? component.parent.uid : void 0, component);
|
||||
emit$1(hook, component.appContext.app, component.uid, component.parent ? component.parent.uid : void 0, component);
|
||||
};
|
||||
}
|
||||
var devtoolsPerfStart = createDevtoolsPerformanceHook(
|
||||
@@ -1950,13 +1987,13 @@ var devtoolsPerfEnd = createDevtoolsPerformanceHook(
|
||||
);
|
||||
function createDevtoolsPerformanceHook(hook) {
|
||||
return (component, type, time) => {
|
||||
emit(hook, component.appContext.app, component.uid, component, type, time);
|
||||
emit$1(hook, component.appContext.app, component.uid, component, type, time);
|
||||
};
|
||||
}
|
||||
function devtoolsComponentEmit(component, event, params) {
|
||||
emit("component:emit", component.appContext.app, component, event, params);
|
||||
emit$1("component:emit", component.appContext.app, component, event, params);
|
||||
}
|
||||
function emit$1(instance, event, ...rawArgs) {
|
||||
function emit(instance, event, ...rawArgs) {
|
||||
if (instance.isUnmounted)
|
||||
return;
|
||||
const props = instance.vnode.props || EMPTY_OBJ;
|
||||
@@ -1988,7 +2025,7 @@ function emit$1(instance, event, ...rawArgs) {
|
||||
args = rawArgs.map((a) => isString(a) ? a.trim() : a);
|
||||
}
|
||||
if (number) {
|
||||
args = rawArgs.map(toNumber);
|
||||
args = rawArgs.map(looseToNumber);
|
||||
}
|
||||
}
|
||||
if (true) {
|
||||
@@ -2496,7 +2533,10 @@ function createSuspenseBoundary(vnode, parent, parentComponent, container, hidde
|
||||
console[console.info ? "info" : "log"](`<Suspense> is an experimental feature and its API will likely change.`);
|
||||
}
|
||||
const { p: patch, m: move, um: unmount, n: next, o: { parentNode, remove: remove2 } } = rendererInternals;
|
||||
const timeout = toNumber(vnode.props && vnode.props.timeout);
|
||||
const timeout = vnode.props ? toNumber(vnode.props.timeout) : void 0;
|
||||
if (true) {
|
||||
assertNumber(timeout, `Suspense timeout`);
|
||||
}
|
||||
const suspense = {
|
||||
vnode,
|
||||
parent,
|
||||
@@ -2821,7 +2861,7 @@ function doWatch(source, cb, { immediate, deep, flush, onTrack, onTrigger } = EM
|
||||
const warnInvalidSource = (s) => {
|
||||
warn2(`Invalid watch source: `, s, `A watch source can only be a getter/effect function, a ref, a reactive object, or an array of these types.`);
|
||||
};
|
||||
const instance = currentInstance;
|
||||
const instance = getCurrentScope() === (currentInstance === null || currentInstance === void 0 ? void 0 : currentInstance.scope) ? currentInstance : null;
|
||||
let getter;
|
||||
let forceTrigger = false;
|
||||
let isMultiSource = false;
|
||||
@@ -3527,7 +3567,7 @@ var KeepAliveImpl = {
|
||||
}
|
||||
function pruneCacheEntry(key) {
|
||||
const cached = cache.get(key);
|
||||
if (!current || cached.type !== current.type) {
|
||||
if (!current || !isSameVNodeType(cached, current)) {
|
||||
unmount(cached);
|
||||
} else if (current) {
|
||||
resetShapeFlag(current);
|
||||
@@ -3556,7 +3596,7 @@ var KeepAliveImpl = {
|
||||
cache.forEach((cached) => {
|
||||
const { subTree, suspense } = instance;
|
||||
const vnode = getInnerChild(subTree);
|
||||
if (cached.type === vnode.type) {
|
||||
if (cached.type === vnode.type && cached.key === vnode.key) {
|
||||
resetShapeFlag(vnode);
|
||||
const da = vnode.component.da;
|
||||
da && queuePostRenderEffect(da, suspense);
|
||||
@@ -3626,7 +3666,7 @@ function matches(pattern, name) {
|
||||
return pattern.some((p2) => matches(p2, name));
|
||||
} else if (isString(pattern)) {
|
||||
return pattern.split(",").includes(name);
|
||||
} else if (pattern.test) {
|
||||
} else if (isRegExp(pattern)) {
|
||||
return pattern.test(name);
|
||||
}
|
||||
return false;
|
||||
@@ -4831,8 +4871,8 @@ function validatePropName(key) {
|
||||
return false;
|
||||
}
|
||||
function getType(ctor) {
|
||||
const match = ctor && ctor.toString().match(/^\s*function (\w+)/);
|
||||
return match ? match[1] : ctor === null ? "null" : "";
|
||||
const match = ctor && ctor.toString().match(/^\s*(function|class) (\w+)/);
|
||||
return match ? match[2] : ctor === null ? "null" : "";
|
||||
}
|
||||
function isSameType(a, b) {
|
||||
return getType(a) === getType(b);
|
||||
@@ -5048,7 +5088,7 @@ function createAppContext() {
|
||||
emitsCache: /* @__PURE__ */ new WeakMap()
|
||||
};
|
||||
}
|
||||
var uid = 0;
|
||||
var uid$1 = 0;
|
||||
function createAppAPI(render2, hydrate2) {
|
||||
return function createApp2(rootComponent, rootProps = null) {
|
||||
if (!isFunction(rootComponent)) {
|
||||
@@ -5062,7 +5102,7 @@ function createAppAPI(render2, hydrate2) {
|
||||
const installedPlugins = /* @__PURE__ */ new Set();
|
||||
let isMounted = false;
|
||||
const app = context.app = {
|
||||
_uid: uid++,
|
||||
_uid: uid$1++,
|
||||
_component: rootComponent,
|
||||
_props: rootProps,
|
||||
_container: null,
|
||||
@@ -5716,6 +5756,7 @@ function baseCreateRenderer(options, createHydrationFns) {
|
||||
if (dirs) {
|
||||
invokeDirectiveHook(vnode, null, parentComponent, "created");
|
||||
}
|
||||
setScopeId(el, vnode, vnode.scopeId, slotScopeIds, parentComponent);
|
||||
if (props) {
|
||||
for (const key in props) {
|
||||
if (key !== "value" && !isReservedProp(key)) {
|
||||
@@ -5729,7 +5770,6 @@ function baseCreateRenderer(options, createHydrationFns) {
|
||||
invokeVNodeHook(vnodeHook, parentComponent, vnode);
|
||||
}
|
||||
}
|
||||
setScopeId(el, vnode, vnode.scopeId, slotScopeIds, parentComponent);
|
||||
if (true) {
|
||||
Object.defineProperty(el, "__vnode", {
|
||||
value: vnode,
|
||||
@@ -7037,7 +7077,8 @@ function cloneVNode(vnode, extraProps, mergeRef = false) {
|
||||
ssFallback: vnode.ssFallback && cloneVNode(vnode.ssFallback),
|
||||
el: vnode.el,
|
||||
anchor: vnode.anchor,
|
||||
ctx: vnode.ctx
|
||||
ctx: vnode.ctx,
|
||||
ce: vnode.ce
|
||||
};
|
||||
return cloned;
|
||||
}
|
||||
@@ -7154,12 +7195,12 @@ function invokeVNodeHook(hook, instance, vnode, prevVNode = null) {
|
||||
]);
|
||||
}
|
||||
var emptyAppContext = createAppContext();
|
||||
var uid$1 = 0;
|
||||
var uid = 0;
|
||||
function createComponentInstance(vnode, parent, suspense) {
|
||||
const type = vnode.type;
|
||||
const appContext = (parent ? parent.appContext : vnode.appContext) || emptyAppContext;
|
||||
const instance = {
|
||||
uid: uid$1++,
|
||||
uid: uid++,
|
||||
vnode,
|
||||
type,
|
||||
parent,
|
||||
@@ -7234,7 +7275,7 @@ function createComponentInstance(vnode, parent, suspense) {
|
||||
instance.ctx = { _: instance };
|
||||
}
|
||||
instance.root = parent ? parent.root : instance;
|
||||
instance.emit = emit$1.bind(null, instance);
|
||||
instance.emit = emit.bind(null, instance);
|
||||
if (vnode.ce) {
|
||||
vnode.ce(instance);
|
||||
}
|
||||
@@ -7436,9 +7477,24 @@ function createAttrsProxy(instance) {
|
||||
}
|
||||
function createSetupContext(instance) {
|
||||
const expose = (exposed) => {
|
||||
if (true) {
|
||||
if (instance.exposed) {
|
||||
warn2(`expose() should be called only once per setup().`);
|
||||
}
|
||||
if (exposed != null) {
|
||||
let exposedType = typeof exposed;
|
||||
if (exposedType === "object") {
|
||||
if (isArray(exposed)) {
|
||||
exposedType = "array";
|
||||
} else if (isRef(exposed)) {
|
||||
exposedType = "ref";
|
||||
}
|
||||
}
|
||||
if (exposedType !== "object") {
|
||||
warn2(`expose() should be passed a plain object, received ${exposedType}.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
instance.exposed = exposed || {};
|
||||
};
|
||||
let attrs;
|
||||
@@ -7830,7 +7886,7 @@ function isMemoSame(cached, memo) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
var version = "3.2.45";
|
||||
var version = "3.2.47";
|
||||
var _ssrUtils = {
|
||||
createComponentInstance,
|
||||
setupComponent,
|
||||
@@ -7927,9 +7983,6 @@ function patchStyle(el, prev, next) {
|
||||
const style = el.style;
|
||||
const isCssString = isString(next);
|
||||
if (next && !isCssString) {
|
||||
for (const key in next) {
|
||||
setStyle(style, key, next[key]);
|
||||
}
|
||||
if (prev && !isString(prev)) {
|
||||
for (const key in prev) {
|
||||
if (next[key] == null) {
|
||||
@@ -7937,6 +7990,9 @@ function patchStyle(el, prev, next) {
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const key in next) {
|
||||
setStyle(style, key, next[key]);
|
||||
}
|
||||
} else {
|
||||
const currentDisplay = style.display;
|
||||
if (isCssString) {
|
||||
@@ -8582,16 +8638,10 @@ function normalizeDuration(duration) {
|
||||
}
|
||||
function NumberOf(val) {
|
||||
const res = toNumber(val);
|
||||
if (true)
|
||||
validateDuration(res);
|
||||
return res;
|
||||
}
|
||||
function validateDuration(val) {
|
||||
if (typeof val !== "number") {
|
||||
warn2(`<transition> explicit duration is not a valid number - got ${JSON.stringify(val)}.`);
|
||||
} else if (isNaN(val)) {
|
||||
warn2(`<transition> explicit duration is NaN - the duration expression might be incorrect.`);
|
||||
if (true) {
|
||||
assertNumber(res, "<transition> explicit duration");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
function addTransitionClass(el, cls) {
|
||||
cls.split(/\s+/).forEach((c) => c && el.classList.add(c));
|
||||
@@ -8762,6 +8812,8 @@ var TransitionGroupImpl = {
|
||||
};
|
||||
}
|
||||
};
|
||||
var removeMode = (props) => delete props.mode;
|
||||
removeMode(TransitionGroupImpl.props);
|
||||
var TransitionGroup = TransitionGroupImpl;
|
||||
function callPendingCbs(c) {
|
||||
const el = c.el;
|
||||
@@ -8828,7 +8880,7 @@ var vModelText = {
|
||||
domValue = domValue.trim();
|
||||
}
|
||||
if (castToNumber) {
|
||||
domValue = toNumber(domValue);
|
||||
domValue = looseToNumber(domValue);
|
||||
}
|
||||
el._assign(domValue);
|
||||
});
|
||||
@@ -8858,7 +8910,7 @@ var vModelText = {
|
||||
if (trim && el.value.trim() === value) {
|
||||
return;
|
||||
}
|
||||
if ((number || el.type === "number") && toNumber(el.value) === value) {
|
||||
if ((number || el.type === "number") && looseToNumber(el.value) === value) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -8939,7 +8991,7 @@ var vModelSelect = {
|
||||
created(el, { value, modifiers: { number } }, vnode) {
|
||||
const isSetModel = isSet(value);
|
||||
addEventListener(el, "change", () => {
|
||||
const selectedVal = Array.prototype.filter.call(el.options, (o) => o.selected).map((o) => number ? toNumber(getValue(o)) : getValue(o));
|
||||
const selectedVal = Array.prototype.filter.call(el.options, (o) => o.selected).map((o) => number ? looseToNumber(getValue(o)) : getValue(o));
|
||||
el._assign(el.multiple ? isSetModel ? new Set(selectedVal) : selectedVal : selectedVal[0]);
|
||||
});
|
||||
el._assign = getModelAssigner(vnode);
|
||||
@@ -9292,6 +9344,7 @@ export {
|
||||
Transition,
|
||||
TransitionGroup,
|
||||
VueElement,
|
||||
assertNumber,
|
||||
callWithAsyncErrorHandling,
|
||||
callWithErrorHandling,
|
||||
camelize,
|
||||
|
||||
+3
-3
File diff suppressed because one or more lines are too long
@@ -34,7 +34,8 @@ export default {
|
||||
text: '前端',
|
||||
collapsible: true,
|
||||
items: [
|
||||
{ text: '封装 axios 请求库', link: '/project/frontend/axios-package' },
|
||||
{ text: '环境变量和模式', link: '/project/frontend/env-var-modes' },
|
||||
{ text: '网络请求库封装', link: '/project/frontend/axios-package' },
|
||||
{ text: '页面中的加载进度条', link: '/project/frontend/nprogress' },
|
||||
]
|
||||
},
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/* 图标背景 */
|
||||
--vp-home-hero-image-background-image: linear-gradient( 135deg, #97abf5 10%, #476FFF 100%);
|
||||
--vp-home-hero-image-filter: blur(150px);
|
||||
--vp-home-hero-image-filter: blur(100px);
|
||||
|
||||
/* brand按钮 */
|
||||
--vp-button-brand-border: #476FFF;
|
||||
@@ -18,6 +18,7 @@
|
||||
--vp-button-brand-hover-bg: #476FFF;
|
||||
|
||||
--vp-button-brand-active-border: #476FFF;
|
||||
--vp-button-brand-active-bg: #476FFF;
|
||||
|
||||
/* 主题基色 */
|
||||
--vp-c-brand: #476FFF;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# 关于
|
||||
|
||||
Crm(英文全称 Customer relationship management )是一个免费、开源的客户关系管理系统,主要功能有仪表盘、客户管理、合同管理、产品管理、订阅等功能。
|
||||
Crm(英文全称 Customer relationship management )是一个免费、开源的客户关系管理系统,主要功能有仪表盘、客户管理、合同管理、产品管理、配置、订阅等功能。
|
||||
|
||||
项目基于 Vue + Golang 实现,采用[MIT 许可证](https://github.com/zchengo/crm/blob/main/LICENSE),使用完全免费。
|
||||
|
||||
## 为何要开源
|
||||
|
||||
做项目的同时把项目开源了,既能提升自己,又能帮助别人,岂不美哉!本项目会持续更新优化,希望这个项目能够给大家带来更多的学习参考价值。
|
||||
开源的目的是希望这个项目能够给大家带来更多的学习参考价值。
|
||||
|
||||
## 作者简介
|
||||
|
||||
|
||||
+13
-6
@@ -2,14 +2,14 @@
|
||||
layout: home
|
||||
|
||||
hero:
|
||||
name: CrmSystem
|
||||
text: Use Golang & Vue
|
||||
tagline: 一个免费开源的客户关系管理系统。
|
||||
name: CRM DOCS
|
||||
text: 客户关系管理系统
|
||||
tagline: 基于 Vue3 和 Golang 实现,适用于新手学习参考。
|
||||
image:
|
||||
src: /logo.svg
|
||||
alt: VitePress
|
||||
width: 200
|
||||
height: 200
|
||||
alt: logo
|
||||
width: 300
|
||||
height: 300
|
||||
actions:
|
||||
- theme: brand
|
||||
text: 在线预览
|
||||
@@ -17,4 +17,11 @@ hero:
|
||||
- theme: alt
|
||||
text: 查看代码库
|
||||
link: https://github.com/zchengo/crm
|
||||
features:
|
||||
- title: 前端
|
||||
details: 采用 Vue3 框架,Axios 网络请求库,Pinia 状态管理,Vite 构建工具,Ant Design Vue UI组件库等。
|
||||
- title: 后端
|
||||
details: 采用 Go 语言,Gin Web 框架,Gorm ORM 框架,Jwt 用户认证,Viper 读取配置等。
|
||||
- title: 运维
|
||||
details: 采用 Nginx 作为代理服务器,GitHub Actions 作为持续集成和持续交付 (CI/CD) 平台。
|
||||
---
|
||||
@@ -1,6 +1,6 @@
|
||||
# 部署到云服务器
|
||||
|
||||
如果您打算将 Crm 系统部署到您自己的云服务器,您需要先注册一个域名和购买一台云服务器,然后在云服务器上进行环境安装、服务启动与配置、项目的构建与部署。
|
||||
如果您打算将 Crm 系统部署到您自己的云服务器上,您需要先注册一个域名和购买一台云服务器,然后在云服务器上进行环境安装、服务启动与配置、项目的构建与部署。
|
||||
|
||||
## 注册域名
|
||||
|
||||
@@ -501,9 +501,8 @@ alipay:
|
||||
appPublicCert: /home/ubuntu/crm/cert/appPublicCert.crt
|
||||
alipayRootCert: /home/ubuntu/crm/cert/alipayRootCert.crt
|
||||
alipayPublicCert: /home/ubuntu/crm/cert/alipayPublicCert.crt
|
||||
returnURL: http://127.0.0.1:8000/api/subscribe/callback
|
||||
notifyURL: http://127.0.0.1:8000/api/subscribe/notify
|
||||
paySuccessURL: http://127.0.0.1:8060/#/subscribe
|
||||
returnURL: http://127.0.0.1:8060/#/subscribe
|
||||
notifyURL: http://127.0.0.1:8000/api/subscribe/payback
|
||||
```
|
||||
|
||||
**如何获取支付宝支付服务的 appId、privateKey、appPublicCert、alipayRootCert、alipayPublicCert ?**
|
||||
@@ -673,7 +672,6 @@ http {
|
||||
|
||||
如果您在部署过程中遇到了问题,您可以通过以下方式反馈:
|
||||
|
||||
- [在公众号后台反馈](/about/about)
|
||||
- [New Issues In Github](https://github.com/zchengo/crm/issues)
|
||||
|
||||
## 支持作者
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
## 环境安装
|
||||
|
||||
在您自己的电脑上,安装如下运行环境:
|
||||
你需要在你的系统中,安装如下运行环境:
|
||||
|
||||
| 环境 | 版本 | 下载地址 |
|
||||
|---|---|---|
|
||||
@@ -68,9 +68,8 @@ alipay:
|
||||
appPublicCert: /home/ubuntu/crm/cert/appPublicCert.crt
|
||||
alipayRootCert: /home/ubuntu/crm/cert/alipayRootCert.crt
|
||||
alipayPublicCert: /home/ubuntu/crm/cert/alipayPublicCert.crt
|
||||
returnURL: http://127.0.0.1:8000/api/subscribe/callback
|
||||
notifyURL: http://127.0.0.1:8000/api/subscribe/notify
|
||||
paySuccessURL: http://127.0.0.1:8060/#/subscribe
|
||||
returnURL: http://127.0.0.1:8060/#/subscribe
|
||||
notifyURL: http://127.0.0.1:8000/api/subscribe/payback
|
||||
```
|
||||
|
||||
**如何获取支付宝支付服务的 appId、privateKey、appPublicCert、alipayRootCert、alipayPublicCert ?**
|
||||
@@ -85,7 +84,7 @@ alipay:
|
||||
|
||||
### 初始化并运行
|
||||
|
||||
使用电脑自带的终端执行执行如下命令,也可以使用 VS Code 或者 Goland 等开发工具,打开 crm 目录,找到 Terminal 终端。
|
||||
使用 VS Code 或者 Goland 等开发工具,打开 crm 目录,找到 Terminal 终端。
|
||||
|
||||
执行如下命令:
|
||||
|
||||
@@ -106,7 +105,7 @@ $ ./server (windows运行命令为 server.exe)
|
||||
Node 环境正常。
|
||||
:::
|
||||
|
||||
使用电脑自带的终端执行执行如下命令,也可以使用 VSCode 或者 WebStom 等开发工具,打开 crm 目录,找到 Terminal 终端。
|
||||
使用 VSCode 或 WebStom 等开发工具,打开 crm 目录,找到 Terminal 终端。
|
||||
|
||||
执行如下命令:
|
||||
|
||||
|
||||
@@ -42,4 +42,4 @@ $ npm run dev
|
||||
|
||||
项目初始化并运行成功后,打开浏览器访问http://127.0.0.1:8060。
|
||||
|
||||
详细部署,请参考[部署指南](/project/docs/deploy-guide)。
|
||||
有关 Crm 的详细部署文档,请参考[部署指南](/project/docs/deploy-guide)。
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
## 什么是 Crm ?
|
||||
|
||||
Crm(英文全称 Customer relationship management )是一个客户关系管理系统,主要功能有仪表盘、客户管理、合同管理、产品管理、订阅等功能。
|
||||
Crm(英文全称 Customer relationship management )是一个客户关系管理系统,主要功能有仪表盘、客户管理、合同管理、产品管理、配置、订阅等功能。
|
||||
|
||||
- 在线演示:[zocrm.cloud](https://zocrm.cloud)
|
||||
|
||||
@@ -14,6 +14,8 @@ Crm(英文全称 Customer relationship management )是一个客户关系管
|
||||
|
||||
## 技术栈
|
||||
|
||||
Crm 系统主要采用 Vue3 和 Golang 实现。
|
||||
|
||||
### 前端技术
|
||||
|
||||
| 技术 | 说明 | 相关文档 |
|
||||
@@ -36,7 +38,8 @@ Crm(英文全称 Customer relationship management )是一个客户关系管
|
||||
| Jwt | 用户认证 | https://github.com/golang-jwt/jwt |
|
||||
| Viper | 配置管理 | https://github.com/spf13/viper |
|
||||
| Redis | 数据缓存 | https://github.com/go-redis/redis |
|
||||
| Mail | 邮件服务SDK | https://github.com/go-gomail/gomail |
|
||||
| Gomail | 邮件服务SDK | https://github.com/go-gomail/gomail |
|
||||
| Gopay | 支付服务SDK | https://github.com/go-pay/gopay |
|
||||
|
||||
## 目录结构
|
||||
|
||||
@@ -59,6 +62,7 @@ crm
|
||||
├── api // API层
|
||||
├── common // 通用的工具
|
||||
├── config // 配置文件
|
||||
├── dao // 数据访问层
|
||||
├── db // 数据库 SQL 文件
|
||||
├── global // 全局对象
|
||||
├── initialize // 初始化
|
||||
@@ -76,11 +80,14 @@ crm
|
||||
|── api // API接口
|
||||
├── assets // 资源
|
||||
├── axios // 网络请求
|
||||
├── components // 自定义组件
|
||||
├── router // 页面路由
|
||||
├── store // 状态管理
|
||||
├── views // 页面
|
||||
├── App.vue // 组件入口
|
||||
├── main.js // 程序启动的入口
|
||||
├── .env.dev // 开发模式环境变量
|
||||
├── .env.prod // 生产模式环境变量
|
||||
├── index.html // 首页
|
||||
├── package-lock.json // Npm依赖管理
|
||||
├── package.json // Npm依赖管理
|
||||
@@ -91,8 +98,6 @@ crm
|
||||
├── README.md // 项目简介文档
|
||||
```
|
||||
|
||||
想要了解有关 Crm 目录结构中的文件,请访问[github.com/zchengo/crm](https://github.com/zchengo/crm)代码库。
|
||||
|
||||
## 系统架构
|
||||
|
||||
Crm 系统采用前后端分离架构,前端与后端分开部署,且部署到同一台服务器。
|
||||
@@ -103,4 +108,4 @@ Crm 系统采用前后端分离架构,前端与后端分开部署,且部署
|
||||
|
||||
## 许可证
|
||||
|
||||
Crm 是采用 MIT 许可的开源项目,使用完全免费。要了解有关 MIT 许可证的更多信息,请访问[MIT License](https://github.com/zchengo/crm/blob/main/LICENSE)。
|
||||
Crm 是采用 MIT 许可的开源项目,使用完全免费。想要了解有关 MIT 许可证的更多信息,请访问[MIT License](https://github.com/zchengo/crm/blob/main/LICENSE)。
|
||||
@@ -2,5 +2,4 @@
|
||||
|
||||
在使用过程中遇到了问题,您可以通过以下方式反馈:
|
||||
|
||||
- [在公众号后台反馈](/about/about#关注作者)
|
||||
- [New Issues In Github](https://github.com/zchengo/crm/issues)
|
||||
@@ -1,6 +1,8 @@
|
||||
# 封装 axios 请求库
|
||||
# 网络请求库封装
|
||||
|
||||
## 什么是 axios?
|
||||
Crm 系统采用 axios 作为网络请求库。
|
||||
|
||||
## 什么是 axios ?
|
||||
|
||||
axios 是浏览器和 node.js 的一个简单的基于 promise 的 HTTP 客户端。axios 在一个具有非常可扩展界面的小软件包中提供了一个简单易用的库。
|
||||
|
||||
@@ -12,26 +14,16 @@ axios 是浏览器和 node.js 的一个简单的基于 promise 的 HTTP 客户
|
||||
|
||||
```js
|
||||
import axios from 'axios';
|
||||
import router from '../router/index';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
const host = window.location.hostname
|
||||
|
||||
switch (host) {
|
||||
case 'zocrm.cloud':
|
||||
axios.defaults.baseURL = 'https://zocrm.cloud/api'
|
||||
break;
|
||||
default:
|
||||
axios.defaults.baseURL = 'http://127.0.0.1:8000/api'
|
||||
break;
|
||||
}
|
||||
axios.defaults.baseURL = import.meta.env.VITE_API_BASE_URL
|
||||
|
||||
const request = axios.create({
|
||||
timeout: 5000,
|
||||
// timeout: 5000,`
|
||||
headers: {
|
||||
'Content-Type': 'application/json;charset=UTF-8'
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
request.interceptors.request.use(config => {
|
||||
config.headers['uid'] = localStorage.getItem('uid')
|
||||
@@ -47,7 +39,6 @@ request.interceptors.response.use(response => {
|
||||
return response;
|
||||
}, error => {
|
||||
console.log(error)
|
||||
router.push('/error');
|
||||
return Promise.reject(error)
|
||||
})
|
||||
|
||||
@@ -57,29 +48,20 @@ export default request;
|
||||
### 初始化请求的 baseURL
|
||||
|
||||
```js
|
||||
const host = window.location.hostname
|
||||
|
||||
switch (host) {
|
||||
case 'zocrm.cloud':
|
||||
axios.defaults.baseURL = 'https://zocrm.cloud/api'
|
||||
break;
|
||||
default:
|
||||
axios.defaults.baseURL = 'http://127.0.0.1:8000/api'
|
||||
break;
|
||||
}
|
||||
axios.defaults.baseURL = import.meta.env.VITE_API_BASE_URL
|
||||
```
|
||||
|
||||
通过 ```window.location.hostname``` 获取主机名(不包含端口号),主机名指的是 ```IP地址``` 或 ```域名```,然后根据不同的主机名,设置不同的 ```axios.defaults.baseURL```。
|
||||
通过 ```import.meta.env.VITE_API_BASE_URL``` 获取环境变量。
|
||||
|
||||
### 设置请求超时时间与请求头
|
||||
|
||||
```js
|
||||
const request = axios.create({
|
||||
timeout: 5000,
|
||||
// timeout: 5000,`
|
||||
headers: {
|
||||
'Content-Type': 'application/json;charset=UTF-8'
|
||||
}
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
### 请求拦截与响应拦截
|
||||
@@ -107,13 +89,10 @@ request.interceptors.response.use(response => {
|
||||
return response;
|
||||
}, error => {
|
||||
console.log(error)
|
||||
router.push('/error');
|
||||
return Promise.reject(error)
|
||||
})
|
||||
```
|
||||
|
||||
后端响应的数据会先被 axios 拦截,如果响应的结果是正确的,会直接返回响应结果,然后将数据渲染到指定的页面;如果响应的结果是错误的,会路由到错误页面。
|
||||
|
||||
了解有关 axios 请求拦截与响应拦截的更多信息,请访问[axios interceptors](https://github.com/axios/axios#interceptors)。
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
# 环境变量和模式
|
||||
|
||||
Crm 采用 Vite 提供的环境变量与模式,来设置应用的环境变量与运行模式。
|
||||
|
||||
了解有关 Vite 环境变量与模式的更多信息,请参考文档[Vite 环境变量与模式](https://vitejs.dev/guide/env-and-mode.html#env-files)。
|
||||
|
||||
## 创建 ```.env``` 文件
|
||||
|
||||
在 ```crm/web/``` 目录下创建 ```.env``` 文件,Vite 会从你的 ```环境目录``` 中的下列文件加载额外的环境变量:
|
||||
|
||||
```txt
|
||||
.env # 所有情况下都会加载
|
||||
.env.local # 所有情况下都会加载,但会被 git 忽略
|
||||
.env.[mode] # 只在指定模式下加载
|
||||
.env.[mode].local # 只在指定模式下加载,但会被 git 忽略
|
||||
```
|
||||
|
||||
在 Crm 系统中,只定义了开发模式与生产模式:
|
||||
```txt
|
||||
.env.dev # 开发模式的环境变量
|
||||
.env.prod # 生产模式的环境变量
|
||||
```
|
||||
|
||||
## 定义环境变量
|
||||
|
||||
分别在开发模式和生产模式中定义开发环境和生产环境所需要环境变量。
|
||||
|
||||
请参考 ```crm/web/.env.dev``` 文件如下:
|
||||
|
||||
```txt
|
||||
# .env.development
|
||||
VITE_API_BASE_URL=http://127.0.0.1:8000/api
|
||||
VITE_FILE_UPLOAD_URL=http://127.0.0.1:8000/api/common/file/upload
|
||||
```
|
||||
|
||||
请参考 ```crm/web/.env.prod``` 文件如下:
|
||||
|
||||
```txt
|
||||
# .env.production
|
||||
VITE_API_BASE_URL=https://zocrm.cloud/api
|
||||
VITE_FILE_UPLOAD_URL=https://zocrm.cloud/api/common/file/upload
|
||||
```
|
||||
|
||||
## 加载环境变量
|
||||
|
||||
通过 ```import.meta.env``` 来获得环境变量:
|
||||
|
||||
```js
|
||||
console.log(import.meta.env.VITE_API_BASE_URL) // http://127.0.0.1:8000/api
|
||||
console.log(import.meta.env.VITE_FILE_UPLOAD_URL) // http://127.0.0.1:8000/api/common/file/upload
|
||||
```
|
||||
|
||||
请注意,Vite 只会加载以 ```VITE_``` 为前缀的变量。
|
||||
|
||||
在 ```crm/web/src/axios/index.js``` 中加载环境变量:
|
||||
|
||||
```js
|
||||
import axios from 'axios';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
axios.defaults.baseURL = import.meta.env.VITE_API_BASE_URL
|
||||
|
||||
const request = axios.create({
|
||||
// timeout: 5000,`
|
||||
headers: {
|
||||
'Content-Type': 'application/json;charset=UTF-8'
|
||||
}
|
||||
})
|
||||
...
|
||||
```
|
||||
|
||||
## 运行模式
|
||||
|
||||
在 ```crm/web/package.json``` 中添加运行模式,请参考如下:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "web",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --mode dev",
|
||||
"build": "vite build --mode prod",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
...
|
||||
},
|
||||
"devDependencies": {
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -8,4 +8,4 @@
|
||||
|
||||
| 用户昵称 | 金额 | 备注 | 赞赏日期 |
|
||||
| ---- | -- | -- | -- |
|
||||
| 空空如也 | ¥0 | 无备注 | 2023.01.10 |
|
||||
| 罗**弦 | ¥5 | 学习您的项目 | 2023.02.23 |
|
||||
Generated
+260
-248
@@ -9,24 +9,24 @@
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"vitepress": "^1.0.0-alpha.35",
|
||||
"vitepress": "^1.0.0-alpha.47",
|
||||
"vue": "^3.2.45"
|
||||
}
|
||||
},
|
||||
"node_modules/@algolia/autocomplete-core": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmmirror.com/@algolia/autocomplete-core/-/autocomplete-core-1.7.2.tgz",
|
||||
"integrity": "sha512-eclwUDC6qfApNnEfu1uWcL/rudQsn59tjEoUYZYE2JSXZrHLRjBUGMxiCoknobU2Pva8ejb0eRxpIYDtVVqdsw==",
|
||||
"version": "1.7.4",
|
||||
"resolved": "https://registry.npmmirror.com/@algolia/autocomplete-core/-/autocomplete-core-1.7.4.tgz",
|
||||
"integrity": "sha512-daoLpQ3ps/VTMRZDEBfU8ixXd+amZcNJ4QSP3IERGyzqnL5Ch8uSRFt/4G8pUvW9c3o6GA4vtVv4I4lmnkdXyg==",
|
||||
"dependencies": {
|
||||
"@algolia/autocomplete-shared": "1.7.2"
|
||||
"@algolia/autocomplete-shared": "1.7.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@algolia/autocomplete-preset-algolia": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmmirror.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.7.2.tgz",
|
||||
"integrity": "sha512-+RYEG6B0QiGGfRb2G3MtPfyrl0dALF3cQNTWBzBX6p5o01vCCGTTinAm2UKG3tfc2CnOMAtnPLkzNZyJUpnVJw==",
|
||||
"version": "1.7.4",
|
||||
"resolved": "https://registry.npmmirror.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.7.4.tgz",
|
||||
"integrity": "sha512-s37hrvLEIfcmKY8VU9LsAXgm2yfmkdHT3DnA3SgHaY93yjZ2qL57wzb5QweVkYuEBZkT2PIREvRoLXC2sxTbpQ==",
|
||||
"dependencies": {
|
||||
"@algolia/autocomplete-shared": "1.7.2"
|
||||
"@algolia/autocomplete-shared": "1.7.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@algolia/client-search": ">= 4.9.1 < 6",
|
||||
@@ -34,9 +34,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@algolia/autocomplete-shared": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmmirror.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.7.2.tgz",
|
||||
"integrity": "sha512-QCckjiC7xXHIUaIL3ektBtjJ0w7tTA3iqKcAE/Hjn1lZ5omp7i3Y4e09rAr9ZybqirL7AbxCLLq0Ra5DDPKeug=="
|
||||
"version": "1.7.4",
|
||||
"resolved": "https://registry.npmmirror.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.7.4.tgz",
|
||||
"integrity": "sha512-2VGCk7I9tA9Ge73Km99+Qg87w0wzW4tgUruvWAn/gfey1ZXgmxZtyIRBebk35R1O8TbK77wujVtCnpsGpRy1kg=="
|
||||
},
|
||||
"node_modules/@algolia/cache-browser-local-storage": {
|
||||
"version": "4.14.3",
|
||||
@@ -154,9 +154,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.20.7",
|
||||
"resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.20.7.tgz",
|
||||
"integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==",
|
||||
"version": "7.21.2",
|
||||
"resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.21.2.tgz",
|
||||
"integrity": "sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==",
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
},
|
||||
@@ -165,27 +165,27 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@docsearch/css": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmmirror.com/@docsearch/css/-/css-3.3.1.tgz",
|
||||
"integrity": "sha512-nznHXeFHpAYjyaSNFNFpU+IJPjQA7AINM8ONjDx/Zx4O/pGAvqwgmcLNc7zR8qXRutqnzLo06yN63xFn36KFBw=="
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmmirror.com/@docsearch/css/-/css-3.3.3.tgz",
|
||||
"integrity": "sha512-6SCwI7P8ao+se1TUsdZ7B4XzL+gqeQZnBc+2EONZlcVa0dVrk0NjETxozFKgMv0eEGH8QzP1fkN+A1rH61l4eg=="
|
||||
},
|
||||
"node_modules/@docsearch/js": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmmirror.com/@docsearch/js/-/js-3.3.1.tgz",
|
||||
"integrity": "sha512-BCVu7njUFJSUXDNvgK65xNYU1L7U3CKFJlawDXql17nQwfpBrNZHqp+eb8z9qu0SzauQKss9tsf/qwlFJ9BOGw==",
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmmirror.com/@docsearch/js/-/js-3.3.3.tgz",
|
||||
"integrity": "sha512-2xAv2GFuHzzmG0SSZgf8wHX0qZX8n9Y1ZirKUk5Wrdc+vH9CL837x2hZIUdwcPZI9caBA+/CzxsS68O4waYjUQ==",
|
||||
"dependencies": {
|
||||
"@docsearch/react": "3.3.1",
|
||||
"@docsearch/react": "3.3.3",
|
||||
"preact": "^10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@docsearch/react": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmmirror.com/@docsearch/react/-/react-3.3.1.tgz",
|
||||
"integrity": "sha512-wdeQBODPkue6yVEEg4ntt+TiGJ6iXMBUNjBQJ0s1WVoc1OdcCnks/lkQ5LEfXETYR/q9QSbCCBnMjvnSoILaag==",
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmmirror.com/@docsearch/react/-/react-3.3.3.tgz",
|
||||
"integrity": "sha512-pLa0cxnl+G0FuIDuYlW+EBK6Rw2jwLw9B1RHIeS4N4s2VhsfJ/wzeCi3CWcs5yVfxLd5ZK50t//TMA5e79YT7Q==",
|
||||
"dependencies": {
|
||||
"@algolia/autocomplete-core": "1.7.2",
|
||||
"@algolia/autocomplete-preset-algolia": "1.7.2",
|
||||
"@docsearch/css": "3.3.1",
|
||||
"@algolia/autocomplete-core": "1.7.4",
|
||||
"@algolia/autocomplete-preset-algolia": "1.7.4",
|
||||
"@docsearch/css": "3.3.3",
|
||||
"algoliasearch": "^4.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@@ -553,36 +553,36 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-core": {
|
||||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.45.tgz",
|
||||
"integrity": "sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A==",
|
||||
"version": "3.2.47",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.47.tgz",
|
||||
"integrity": "sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.16.4",
|
||||
"@vue/shared": "3.2.45",
|
||||
"@vue/shared": "3.2.47",
|
||||
"estree-walker": "^2.0.2",
|
||||
"source-map": "^0.6.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-dom": {
|
||||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz",
|
||||
"integrity": "sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==",
|
||||
"version": "3.2.47",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.47.tgz",
|
||||
"integrity": "sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==",
|
||||
"dependencies": {
|
||||
"@vue/compiler-core": "3.2.45",
|
||||
"@vue/shared": "3.2.45"
|
||||
"@vue/compiler-core": "3.2.47",
|
||||
"@vue/shared": "3.2.47"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-sfc": {
|
||||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz",
|
||||
"integrity": "sha512-1jXDuWah1ggsnSAOGsec8cFjT/K6TMZ0sPL3o3d84Ft2AYZi2jWJgRMjw4iaK0rBfA89L5gw427H4n1RZQBu6Q==",
|
||||
"version": "3.2.47",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz",
|
||||
"integrity": "sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.16.4",
|
||||
"@vue/compiler-core": "3.2.45",
|
||||
"@vue/compiler-dom": "3.2.45",
|
||||
"@vue/compiler-ssr": "3.2.45",
|
||||
"@vue/reactivity-transform": "3.2.45",
|
||||
"@vue/shared": "3.2.45",
|
||||
"@vue/compiler-core": "3.2.47",
|
||||
"@vue/compiler-dom": "3.2.47",
|
||||
"@vue/compiler-ssr": "3.2.47",
|
||||
"@vue/reactivity-transform": "3.2.47",
|
||||
"@vue/shared": "3.2.47",
|
||||
"estree-walker": "^2.0.2",
|
||||
"magic-string": "^0.25.7",
|
||||
"postcss": "^8.1.10",
|
||||
@@ -590,83 +590,83 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-ssr": {
|
||||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz",
|
||||
"integrity": "sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==",
|
||||
"version": "3.2.47",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.2.47.tgz",
|
||||
"integrity": "sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==",
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.2.45",
|
||||
"@vue/shared": "3.2.45"
|
||||
"@vue/compiler-dom": "3.2.47",
|
||||
"@vue/shared": "3.2.47"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/devtools-api": {
|
||||
"version": "6.4.5",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.4.5.tgz",
|
||||
"integrity": "sha512-JD5fcdIuFxU4fQyXUu3w2KpAJHzTVdN+p4iOX2lMWSHMOoQdMAcpFLZzm9Z/2nmsoZ1a96QEhZ26e50xLBsgOQ=="
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.5.0.tgz",
|
||||
"integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q=="
|
||||
},
|
||||
"node_modules/@vue/reactivity": {
|
||||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.2.45.tgz",
|
||||
"integrity": "sha512-PRvhCcQcyEVohW0P8iQ7HDcIOXRjZfAsOds3N99X/Dzewy8TVhTCT4uXpAHfoKjVTJRA0O0K+6QNkDIZAxNi3A==",
|
||||
"version": "3.2.47",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.2.47.tgz",
|
||||
"integrity": "sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==",
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.2.45"
|
||||
"@vue/shared": "3.2.47"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/reactivity-transform": {
|
||||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.2.45.tgz",
|
||||
"integrity": "sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==",
|
||||
"version": "3.2.47",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.2.47.tgz",
|
||||
"integrity": "sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.16.4",
|
||||
"@vue/compiler-core": "3.2.45",
|
||||
"@vue/shared": "3.2.45",
|
||||
"@vue/compiler-core": "3.2.47",
|
||||
"@vue/shared": "3.2.47",
|
||||
"estree-walker": "^2.0.2",
|
||||
"magic-string": "^0.25.7"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/runtime-core": {
|
||||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.2.45.tgz",
|
||||
"integrity": "sha512-gzJiTA3f74cgARptqzYswmoQx0fIA+gGYBfokYVhF8YSXjWTUA2SngRzZRku2HbGbjzB6LBYSbKGIaK8IW+s0A==",
|
||||
"version": "3.2.47",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.2.47.tgz",
|
||||
"integrity": "sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA==",
|
||||
"dependencies": {
|
||||
"@vue/reactivity": "3.2.45",
|
||||
"@vue/shared": "3.2.45"
|
||||
"@vue/reactivity": "3.2.47",
|
||||
"@vue/shared": "3.2.47"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/runtime-dom": {
|
||||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.2.45.tgz",
|
||||
"integrity": "sha512-cy88YpfP5Ue2bDBbj75Cb4bIEZUMM/mAkDMfqDTpUYVgTf/kuQ2VQ8LebuZ8k6EudgH8pYhsGWHlY0lcxlvTwA==",
|
||||
"version": "3.2.47",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.2.47.tgz",
|
||||
"integrity": "sha512-ArXrFTjS6TsDei4qwNvgrdmHtD930KgSKGhS5M+j8QxXrDJYLqYw4RRcDy1bz1m1wMmb6j+zGLifdVHtkXA7gA==",
|
||||
"dependencies": {
|
||||
"@vue/runtime-core": "3.2.45",
|
||||
"@vue/shared": "3.2.45",
|
||||
"@vue/runtime-core": "3.2.47",
|
||||
"@vue/shared": "3.2.47",
|
||||
"csstype": "^2.6.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/server-renderer": {
|
||||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.2.45.tgz",
|
||||
"integrity": "sha512-ebiMq7q24WBU1D6uhPK//2OTR1iRIyxjF5iVq/1a5I1SDMDyDu4Ts6fJaMnjrvD3MqnaiFkKQj+LKAgz5WIK3g==",
|
||||
"version": "3.2.47",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.2.47.tgz",
|
||||
"integrity": "sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==",
|
||||
"dependencies": {
|
||||
"@vue/compiler-ssr": "3.2.45",
|
||||
"@vue/shared": "3.2.45"
|
||||
"@vue/compiler-ssr": "3.2.47",
|
||||
"@vue/shared": "3.2.47"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "3.2.45"
|
||||
"vue": "3.2.47"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/shared": {
|
||||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.45.tgz",
|
||||
"integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg=="
|
||||
"version": "3.2.47",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.47.tgz",
|
||||
"integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ=="
|
||||
},
|
||||
"node_modules/@vueuse/core": {
|
||||
"version": "9.10.0",
|
||||
"resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-9.10.0.tgz",
|
||||
"integrity": "sha512-CxMewME07qeuzuT/AOIQGv0EhhDoojniqU6pC3F8m5VC76L47UT18DcX88kWlP3I7d3qMJ4u/PD8iSRsy3bmNA==",
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-9.13.0.tgz",
|
||||
"integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==",
|
||||
"dependencies": {
|
||||
"@types/web-bluetooth": "^0.0.16",
|
||||
"@vueuse/metadata": "9.10.0",
|
||||
"@vueuse/shared": "9.10.0",
|
||||
"@vueuse/metadata": "9.13.0",
|
||||
"@vueuse/shared": "9.13.0",
|
||||
"vue-demi": "*"
|
||||
}
|
||||
},
|
||||
@@ -693,14 +693,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/metadata": {
|
||||
"version": "9.10.0",
|
||||
"resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.10.0.tgz",
|
||||
"integrity": "sha512-G5VZhgTCapzU9rv0Iq2HBrVOSGzOKb+OE668NxhXNcTjUjwYxULkEhAw70FtRLMZc+hxcFAzDZlKYA0xcwNMuw=="
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.13.0.tgz",
|
||||
"integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ=="
|
||||
},
|
||||
"node_modules/@vueuse/shared": {
|
||||
"version": "9.10.0",
|
||||
"resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.10.0.tgz",
|
||||
"integrity": "sha512-vakHJ2ZRklAzqmcVBL38RS7BxdBA4+5poG9NsSyqJxrt9kz0zX3P5CXMy0Hm6LFbZXUgvKdqAS3pUH1zX/5qTQ==",
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.13.0.tgz",
|
||||
"integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==",
|
||||
"dependencies": {
|
||||
"vue-demi": "*"
|
||||
}
|
||||
@@ -748,6 +748,11 @@
|
||||
"@algolia/transporter": "4.14.3"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-sequence-parser": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/ansi-sequence-parser/-/ansi-sequence-parser-1.1.0.tgz",
|
||||
"integrity": "sha512-lEm8mt52to2fT8GhciPCGeCXACSz2UwIN4X2e2LJSnZ5uAbn2/dsYdOmUXq0AtWS5cpAupysIneExOgH0Vd2TQ=="
|
||||
},
|
||||
"node_modules/body-scroll-lock": {
|
||||
"version": "4.0.0-beta.0",
|
||||
"resolved": "https://registry.npmmirror.com/body-scroll-lock/-/body-scroll-lock-4.0.0-beta.0.tgz",
|
||||
@@ -884,9 +889,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/preact": {
|
||||
"version": "10.11.3",
|
||||
"resolved": "https://registry.npmmirror.com/preact/-/preact-10.11.3.tgz",
|
||||
"integrity": "sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg=="
|
||||
"version": "10.12.1",
|
||||
"resolved": "https://registry.npmmirror.com/preact/-/preact-10.12.1.tgz",
|
||||
"integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg=="
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.1",
|
||||
@@ -902,9 +907,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "3.9.1",
|
||||
"resolved": "https://registry.npmmirror.com/rollup/-/rollup-3.9.1.tgz",
|
||||
"integrity": "sha512-GswCYHXftN8ZKGVgQhTFUJB/NBXxrRGgO2NCy6E8s1rwEJ4Q9/VttNqcYfEvx4dTo4j58YqdC3OVztPzlKSX8w==",
|
||||
"version": "3.17.2",
|
||||
"resolved": "https://registry.npmmirror.com/rollup/-/rollup-3.17.2.tgz",
|
||||
"integrity": "sha512-qMNZdlQPCkWodrAZ3qnJtvCAl4vpQ8q77uEujVCCbC/6CLB7Lcmvjq7HyiOSnf4fxTT9XgsE36oLHJBH49xjqA==",
|
||||
"bin": {
|
||||
"rollup": "dist/bin/rollup"
|
||||
},
|
||||
@@ -917,10 +922,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/shiki": {
|
||||
"version": "0.12.1",
|
||||
"resolved": "https://registry.npmmirror.com/shiki/-/shiki-0.12.1.tgz",
|
||||
"integrity": "sha512-aieaV1m349rZINEBkjxh2QbBvFFQOlgqYTNtCal82hHj4dDZ76oMlQIX+C7ryerBTDiga3e5NfH6smjdJ02BbQ==",
|
||||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmmirror.com/shiki/-/shiki-0.14.1.tgz",
|
||||
"integrity": "sha512-+Jz4nBkCBe0mEDqo1eKRcCdjRtrCjozmcbTUjbPTX7OOJfEbTZzlUWlZtGe3Gb5oV1/jnojhG//YZc3rs9zSEw==",
|
||||
"dependencies": {
|
||||
"ansi-sequence-parser": "^1.1.0",
|
||||
"jsonc-parser": "^3.2.0",
|
||||
"vscode-oniguruma": "^1.7.0",
|
||||
"vscode-textmate": "^8.0.0"
|
||||
@@ -957,14 +963,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/vite/-/vite-4.0.4.tgz",
|
||||
"integrity": "sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==",
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmmirror.com/vite/-/vite-4.1.4.tgz",
|
||||
"integrity": "sha512-3knk/HsbSTKEin43zHu7jTwYWv81f8kgAL99G5NWBcA1LKvtvcVAC4JjBH1arBunO9kQka+1oGbrMKOjk4ZrBg==",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.16.3",
|
||||
"postcss": "^8.4.20",
|
||||
"esbuild": "^0.16.14",
|
||||
"postcss": "^8.4.21",
|
||||
"resolve": "^1.22.1",
|
||||
"rollup": "^3.7.0"
|
||||
"rollup": "^3.10.0"
|
||||
},
|
||||
"bin": {
|
||||
"vite": "bin/vite.js"
|
||||
@@ -1005,19 +1011,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vitepress": {
|
||||
"version": "1.0.0-alpha.35",
|
||||
"resolved": "https://registry.npmmirror.com/vitepress/-/vitepress-1.0.0-alpha.35.tgz",
|
||||
"integrity": "sha512-tJQjJstq+Ryb4pKtlxV4tD8KhxId+DTbR1FRrtJBhA+Vv4nexFHra5M9EgK9jUmoMc3rkyNaw7P3Kkix0ArP1w==",
|
||||
"version": "1.0.0-alpha.47",
|
||||
"resolved": "https://registry.npmmirror.com/vitepress/-/vitepress-1.0.0-alpha.47.tgz",
|
||||
"integrity": "sha512-vj+LOY0WJtKSk98HV4qqG6p4MofmF+C8yrWHiiw+GCMfr6C+610U5D7oD2OruroIafsON6F4nBDWGK8ZyGIpXQ==",
|
||||
"dependencies": {
|
||||
"@docsearch/css": "^3.3.1",
|
||||
"@docsearch/js": "^3.3.1",
|
||||
"@docsearch/css": "^3.3.3",
|
||||
"@docsearch/js": "^3.3.3",
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"@vue/devtools-api": "^6.4.5",
|
||||
"@vueuse/core": "^9.9.0",
|
||||
"@vue/devtools-api": "^6.5.0",
|
||||
"@vueuse/core": "^9.13.0",
|
||||
"body-scroll-lock": "4.0.0-beta.0",
|
||||
"shiki": "^0.12.1",
|
||||
"vite": "^4.0.4",
|
||||
"vue": "^3.2.45"
|
||||
"shiki": "^0.14.1",
|
||||
"vite": "^4.1.3",
|
||||
"vue": "^3.2.47"
|
||||
},
|
||||
"bin": {
|
||||
"vitepress": "bin/vitepress.js"
|
||||
@@ -1034,39 +1040,39 @@
|
||||
"integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg=="
|
||||
},
|
||||
"node_modules/vue": {
|
||||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.2.45.tgz",
|
||||
"integrity": "sha512-9Nx/Mg2b2xWlXykmCwiTUCWHbWIj53bnkizBxKai1g61f2Xit700A1ljowpTIM11e3uipOeiPcSqnmBg6gyiaA==",
|
||||
"version": "3.2.47",
|
||||
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.2.47.tgz",
|
||||
"integrity": "sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==",
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.2.45",
|
||||
"@vue/compiler-sfc": "3.2.45",
|
||||
"@vue/runtime-dom": "3.2.45",
|
||||
"@vue/server-renderer": "3.2.45",
|
||||
"@vue/shared": "3.2.45"
|
||||
"@vue/compiler-dom": "3.2.47",
|
||||
"@vue/compiler-sfc": "3.2.47",
|
||||
"@vue/runtime-dom": "3.2.47",
|
||||
"@vue/server-renderer": "3.2.47",
|
||||
"@vue/shared": "3.2.47"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@algolia/autocomplete-core": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmmirror.com/@algolia/autocomplete-core/-/autocomplete-core-1.7.2.tgz",
|
||||
"integrity": "sha512-eclwUDC6qfApNnEfu1uWcL/rudQsn59tjEoUYZYE2JSXZrHLRjBUGMxiCoknobU2Pva8ejb0eRxpIYDtVVqdsw==",
|
||||
"version": "1.7.4",
|
||||
"resolved": "https://registry.npmmirror.com/@algolia/autocomplete-core/-/autocomplete-core-1.7.4.tgz",
|
||||
"integrity": "sha512-daoLpQ3ps/VTMRZDEBfU8ixXd+amZcNJ4QSP3IERGyzqnL5Ch8uSRFt/4G8pUvW9c3o6GA4vtVv4I4lmnkdXyg==",
|
||||
"requires": {
|
||||
"@algolia/autocomplete-shared": "1.7.2"
|
||||
"@algolia/autocomplete-shared": "1.7.4"
|
||||
}
|
||||
},
|
||||
"@algolia/autocomplete-preset-algolia": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmmirror.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.7.2.tgz",
|
||||
"integrity": "sha512-+RYEG6B0QiGGfRb2G3MtPfyrl0dALF3cQNTWBzBX6p5o01vCCGTTinAm2UKG3tfc2CnOMAtnPLkzNZyJUpnVJw==",
|
||||
"version": "1.7.4",
|
||||
"resolved": "https://registry.npmmirror.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.7.4.tgz",
|
||||
"integrity": "sha512-s37hrvLEIfcmKY8VU9LsAXgm2yfmkdHT3DnA3SgHaY93yjZ2qL57wzb5QweVkYuEBZkT2PIREvRoLXC2sxTbpQ==",
|
||||
"requires": {
|
||||
"@algolia/autocomplete-shared": "1.7.2"
|
||||
"@algolia/autocomplete-shared": "1.7.4"
|
||||
}
|
||||
},
|
||||
"@algolia/autocomplete-shared": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmmirror.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.7.2.tgz",
|
||||
"integrity": "sha512-QCckjiC7xXHIUaIL3ektBtjJ0w7tTA3iqKcAE/Hjn1lZ5omp7i3Y4e09rAr9ZybqirL7AbxCLLq0Ra5DDPKeug=="
|
||||
"version": "1.7.4",
|
||||
"resolved": "https://registry.npmmirror.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.7.4.tgz",
|
||||
"integrity": "sha512-2VGCk7I9tA9Ge73Km99+Qg87w0wzW4tgUruvWAn/gfey1ZXgmxZtyIRBebk35R1O8TbK77wujVtCnpsGpRy1kg=="
|
||||
},
|
||||
"@algolia/cache-browser-local-storage": {
|
||||
"version": "4.14.3",
|
||||
@@ -1184,32 +1190,32 @@
|
||||
}
|
||||
},
|
||||
"@babel/parser": {
|
||||
"version": "7.20.7",
|
||||
"resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.20.7.tgz",
|
||||
"integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg=="
|
||||
"version": "7.21.2",
|
||||
"resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.21.2.tgz",
|
||||
"integrity": "sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ=="
|
||||
},
|
||||
"@docsearch/css": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmmirror.com/@docsearch/css/-/css-3.3.1.tgz",
|
||||
"integrity": "sha512-nznHXeFHpAYjyaSNFNFpU+IJPjQA7AINM8ONjDx/Zx4O/pGAvqwgmcLNc7zR8qXRutqnzLo06yN63xFn36KFBw=="
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmmirror.com/@docsearch/css/-/css-3.3.3.tgz",
|
||||
"integrity": "sha512-6SCwI7P8ao+se1TUsdZ7B4XzL+gqeQZnBc+2EONZlcVa0dVrk0NjETxozFKgMv0eEGH8QzP1fkN+A1rH61l4eg=="
|
||||
},
|
||||
"@docsearch/js": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmmirror.com/@docsearch/js/-/js-3.3.1.tgz",
|
||||
"integrity": "sha512-BCVu7njUFJSUXDNvgK65xNYU1L7U3CKFJlawDXql17nQwfpBrNZHqp+eb8z9qu0SzauQKss9tsf/qwlFJ9BOGw==",
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmmirror.com/@docsearch/js/-/js-3.3.3.tgz",
|
||||
"integrity": "sha512-2xAv2GFuHzzmG0SSZgf8wHX0qZX8n9Y1ZirKUk5Wrdc+vH9CL837x2hZIUdwcPZI9caBA+/CzxsS68O4waYjUQ==",
|
||||
"requires": {
|
||||
"@docsearch/react": "3.3.1",
|
||||
"@docsearch/react": "3.3.3",
|
||||
"preact": "^10.0.0"
|
||||
}
|
||||
},
|
||||
"@docsearch/react": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmmirror.com/@docsearch/react/-/react-3.3.1.tgz",
|
||||
"integrity": "sha512-wdeQBODPkue6yVEEg4ntt+TiGJ6iXMBUNjBQJ0s1WVoc1OdcCnks/lkQ5LEfXETYR/q9QSbCCBnMjvnSoILaag==",
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmmirror.com/@docsearch/react/-/react-3.3.3.tgz",
|
||||
"integrity": "sha512-pLa0cxnl+G0FuIDuYlW+EBK6Rw2jwLw9B1RHIeS4N4s2VhsfJ/wzeCi3CWcs5yVfxLd5ZK50t//TMA5e79YT7Q==",
|
||||
"requires": {
|
||||
"@algolia/autocomplete-core": "1.7.2",
|
||||
"@algolia/autocomplete-preset-algolia": "1.7.2",
|
||||
"@docsearch/css": "3.3.1",
|
||||
"@algolia/autocomplete-core": "1.7.4",
|
||||
"@algolia/autocomplete-preset-algolia": "1.7.4",
|
||||
"@docsearch/css": "3.3.3",
|
||||
"algoliasearch": "^4.0.0"
|
||||
}
|
||||
},
|
||||
@@ -1357,36 +1363,36 @@
|
||||
"requires": {}
|
||||
},
|
||||
"@vue/compiler-core": {
|
||||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.45.tgz",
|
||||
"integrity": "sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A==",
|
||||
"version": "3.2.47",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.47.tgz",
|
||||
"integrity": "sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==",
|
||||
"requires": {
|
||||
"@babel/parser": "^7.16.4",
|
||||
"@vue/shared": "3.2.45",
|
||||
"@vue/shared": "3.2.47",
|
||||
"estree-walker": "^2.0.2",
|
||||
"source-map": "^0.6.1"
|
||||
}
|
||||
},
|
||||
"@vue/compiler-dom": {
|
||||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz",
|
||||
"integrity": "sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==",
|
||||
"version": "3.2.47",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.47.tgz",
|
||||
"integrity": "sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==",
|
||||
"requires": {
|
||||
"@vue/compiler-core": "3.2.45",
|
||||
"@vue/shared": "3.2.45"
|
||||
"@vue/compiler-core": "3.2.47",
|
||||
"@vue/shared": "3.2.47"
|
||||
}
|
||||
},
|
||||
"@vue/compiler-sfc": {
|
||||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz",
|
||||
"integrity": "sha512-1jXDuWah1ggsnSAOGsec8cFjT/K6TMZ0sPL3o3d84Ft2AYZi2jWJgRMjw4iaK0rBfA89L5gw427H4n1RZQBu6Q==",
|
||||
"version": "3.2.47",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz",
|
||||
"integrity": "sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==",
|
||||
"requires": {
|
||||
"@babel/parser": "^7.16.4",
|
||||
"@vue/compiler-core": "3.2.45",
|
||||
"@vue/compiler-dom": "3.2.45",
|
||||
"@vue/compiler-ssr": "3.2.45",
|
||||
"@vue/reactivity-transform": "3.2.45",
|
||||
"@vue/shared": "3.2.45",
|
||||
"@vue/compiler-core": "3.2.47",
|
||||
"@vue/compiler-dom": "3.2.47",
|
||||
"@vue/compiler-ssr": "3.2.47",
|
||||
"@vue/reactivity-transform": "3.2.47",
|
||||
"@vue/shared": "3.2.47",
|
||||
"estree-walker": "^2.0.2",
|
||||
"magic-string": "^0.25.7",
|
||||
"postcss": "^8.1.10",
|
||||
@@ -1394,80 +1400,80 @@
|
||||
}
|
||||
},
|
||||
"@vue/compiler-ssr": {
|
||||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz",
|
||||
"integrity": "sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==",
|
||||
"version": "3.2.47",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.2.47.tgz",
|
||||
"integrity": "sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==",
|
||||
"requires": {
|
||||
"@vue/compiler-dom": "3.2.45",
|
||||
"@vue/shared": "3.2.45"
|
||||
"@vue/compiler-dom": "3.2.47",
|
||||
"@vue/shared": "3.2.47"
|
||||
}
|
||||
},
|
||||
"@vue/devtools-api": {
|
||||
"version": "6.4.5",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.4.5.tgz",
|
||||
"integrity": "sha512-JD5fcdIuFxU4fQyXUu3w2KpAJHzTVdN+p4iOX2lMWSHMOoQdMAcpFLZzm9Z/2nmsoZ1a96QEhZ26e50xLBsgOQ=="
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.5.0.tgz",
|
||||
"integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q=="
|
||||
},
|
||||
"@vue/reactivity": {
|
||||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.2.45.tgz",
|
||||
"integrity": "sha512-PRvhCcQcyEVohW0P8iQ7HDcIOXRjZfAsOds3N99X/Dzewy8TVhTCT4uXpAHfoKjVTJRA0O0K+6QNkDIZAxNi3A==",
|
||||
"version": "3.2.47",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.2.47.tgz",
|
||||
"integrity": "sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==",
|
||||
"requires": {
|
||||
"@vue/shared": "3.2.45"
|
||||
"@vue/shared": "3.2.47"
|
||||
}
|
||||
},
|
||||
"@vue/reactivity-transform": {
|
||||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.2.45.tgz",
|
||||
"integrity": "sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==",
|
||||
"version": "3.2.47",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.2.47.tgz",
|
||||
"integrity": "sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==",
|
||||
"requires": {
|
||||
"@babel/parser": "^7.16.4",
|
||||
"@vue/compiler-core": "3.2.45",
|
||||
"@vue/shared": "3.2.45",
|
||||
"@vue/compiler-core": "3.2.47",
|
||||
"@vue/shared": "3.2.47",
|
||||
"estree-walker": "^2.0.2",
|
||||
"magic-string": "^0.25.7"
|
||||
}
|
||||
},
|
||||
"@vue/runtime-core": {
|
||||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.2.45.tgz",
|
||||
"integrity": "sha512-gzJiTA3f74cgARptqzYswmoQx0fIA+gGYBfokYVhF8YSXjWTUA2SngRzZRku2HbGbjzB6LBYSbKGIaK8IW+s0A==",
|
||||
"version": "3.2.47",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.2.47.tgz",
|
||||
"integrity": "sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA==",
|
||||
"requires": {
|
||||
"@vue/reactivity": "3.2.45",
|
||||
"@vue/shared": "3.2.45"
|
||||
"@vue/reactivity": "3.2.47",
|
||||
"@vue/shared": "3.2.47"
|
||||
}
|
||||
},
|
||||
"@vue/runtime-dom": {
|
||||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.2.45.tgz",
|
||||
"integrity": "sha512-cy88YpfP5Ue2bDBbj75Cb4bIEZUMM/mAkDMfqDTpUYVgTf/kuQ2VQ8LebuZ8k6EudgH8pYhsGWHlY0lcxlvTwA==",
|
||||
"version": "3.2.47",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.2.47.tgz",
|
||||
"integrity": "sha512-ArXrFTjS6TsDei4qwNvgrdmHtD930KgSKGhS5M+j8QxXrDJYLqYw4RRcDy1bz1m1wMmb6j+zGLifdVHtkXA7gA==",
|
||||
"requires": {
|
||||
"@vue/runtime-core": "3.2.45",
|
||||
"@vue/shared": "3.2.45",
|
||||
"@vue/runtime-core": "3.2.47",
|
||||
"@vue/shared": "3.2.47",
|
||||
"csstype": "^2.6.8"
|
||||
}
|
||||
},
|
||||
"@vue/server-renderer": {
|
||||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.2.45.tgz",
|
||||
"integrity": "sha512-ebiMq7q24WBU1D6uhPK//2OTR1iRIyxjF5iVq/1a5I1SDMDyDu4Ts6fJaMnjrvD3MqnaiFkKQj+LKAgz5WIK3g==",
|
||||
"version": "3.2.47",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.2.47.tgz",
|
||||
"integrity": "sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==",
|
||||
"requires": {
|
||||
"@vue/compiler-ssr": "3.2.45",
|
||||
"@vue/shared": "3.2.45"
|
||||
"@vue/compiler-ssr": "3.2.47",
|
||||
"@vue/shared": "3.2.47"
|
||||
}
|
||||
},
|
||||
"@vue/shared": {
|
||||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.45.tgz",
|
||||
"integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg=="
|
||||
"version": "3.2.47",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.47.tgz",
|
||||
"integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ=="
|
||||
},
|
||||
"@vueuse/core": {
|
||||
"version": "9.10.0",
|
||||
"resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-9.10.0.tgz",
|
||||
"integrity": "sha512-CxMewME07qeuzuT/AOIQGv0EhhDoojniqU6pC3F8m5VC76L47UT18DcX88kWlP3I7d3qMJ4u/PD8iSRsy3bmNA==",
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-9.13.0.tgz",
|
||||
"integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==",
|
||||
"requires": {
|
||||
"@types/web-bluetooth": "^0.0.16",
|
||||
"@vueuse/metadata": "9.10.0",
|
||||
"@vueuse/shared": "9.10.0",
|
||||
"@vueuse/metadata": "9.13.0",
|
||||
"@vueuse/shared": "9.13.0",
|
||||
"vue-demi": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -1480,14 +1486,14 @@
|
||||
}
|
||||
},
|
||||
"@vueuse/metadata": {
|
||||
"version": "9.10.0",
|
||||
"resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.10.0.tgz",
|
||||
"integrity": "sha512-G5VZhgTCapzU9rv0Iq2HBrVOSGzOKb+OE668NxhXNcTjUjwYxULkEhAw70FtRLMZc+hxcFAzDZlKYA0xcwNMuw=="
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.13.0.tgz",
|
||||
"integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ=="
|
||||
},
|
||||
"@vueuse/shared": {
|
||||
"version": "9.10.0",
|
||||
"resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.10.0.tgz",
|
||||
"integrity": "sha512-vakHJ2ZRklAzqmcVBL38RS7BxdBA4+5poG9NsSyqJxrt9kz0zX3P5CXMy0Hm6LFbZXUgvKdqAS3pUH1zX/5qTQ==",
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.13.0.tgz",
|
||||
"integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==",
|
||||
"requires": {
|
||||
"vue-demi": "*"
|
||||
},
|
||||
@@ -1521,6 +1527,11 @@
|
||||
"@algolia/transporter": "4.14.3"
|
||||
}
|
||||
},
|
||||
"ansi-sequence-parser": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/ansi-sequence-parser/-/ansi-sequence-parser-1.1.0.tgz",
|
||||
"integrity": "sha512-lEm8mt52to2fT8GhciPCGeCXACSz2UwIN4X2e2LJSnZ5uAbn2/dsYdOmUXq0AtWS5cpAupysIneExOgH0Vd2TQ=="
|
||||
},
|
||||
"body-scroll-lock": {
|
||||
"version": "4.0.0-beta.0",
|
||||
"resolved": "https://registry.npmmirror.com/body-scroll-lock/-/body-scroll-lock-4.0.0-beta.0.tgz",
|
||||
@@ -1631,9 +1642,9 @@
|
||||
}
|
||||
},
|
||||
"preact": {
|
||||
"version": "10.11.3",
|
||||
"resolved": "https://registry.npmmirror.com/preact/-/preact-10.11.3.tgz",
|
||||
"integrity": "sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg=="
|
||||
"version": "10.12.1",
|
||||
"resolved": "https://registry.npmmirror.com/preact/-/preact-10.12.1.tgz",
|
||||
"integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg=="
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.22.1",
|
||||
@@ -1646,18 +1657,19 @@
|
||||
}
|
||||
},
|
||||
"rollup": {
|
||||
"version": "3.9.1",
|
||||
"resolved": "https://registry.npmmirror.com/rollup/-/rollup-3.9.1.tgz",
|
||||
"integrity": "sha512-GswCYHXftN8ZKGVgQhTFUJB/NBXxrRGgO2NCy6E8s1rwEJ4Q9/VttNqcYfEvx4dTo4j58YqdC3OVztPzlKSX8w==",
|
||||
"version": "3.17.2",
|
||||
"resolved": "https://registry.npmmirror.com/rollup/-/rollup-3.17.2.tgz",
|
||||
"integrity": "sha512-qMNZdlQPCkWodrAZ3qnJtvCAl4vpQ8q77uEujVCCbC/6CLB7Lcmvjq7HyiOSnf4fxTT9XgsE36oLHJBH49xjqA==",
|
||||
"requires": {
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"shiki": {
|
||||
"version": "0.12.1",
|
||||
"resolved": "https://registry.npmmirror.com/shiki/-/shiki-0.12.1.tgz",
|
||||
"integrity": "sha512-aieaV1m349rZINEBkjxh2QbBvFFQOlgqYTNtCal82hHj4dDZ76oMlQIX+C7ryerBTDiga3e5NfH6smjdJ02BbQ==",
|
||||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmmirror.com/shiki/-/shiki-0.14.1.tgz",
|
||||
"integrity": "sha512-+Jz4nBkCBe0mEDqo1eKRcCdjRtrCjozmcbTUjbPTX7OOJfEbTZzlUWlZtGe3Gb5oV1/jnojhG//YZc3rs9zSEw==",
|
||||
"requires": {
|
||||
"ansi-sequence-parser": "^1.1.0",
|
||||
"jsonc-parser": "^3.2.0",
|
||||
"vscode-oniguruma": "^1.7.0",
|
||||
"vscode-textmate": "^8.0.0"
|
||||
@@ -1684,31 +1696,31 @@
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
|
||||
},
|
||||
"vite": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/vite/-/vite-4.0.4.tgz",
|
||||
"integrity": "sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==",
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmmirror.com/vite/-/vite-4.1.4.tgz",
|
||||
"integrity": "sha512-3knk/HsbSTKEin43zHu7jTwYWv81f8kgAL99G5NWBcA1LKvtvcVAC4JjBH1arBunO9kQka+1oGbrMKOjk4ZrBg==",
|
||||
"requires": {
|
||||
"esbuild": "^0.16.3",
|
||||
"esbuild": "^0.16.14",
|
||||
"fsevents": "~2.3.2",
|
||||
"postcss": "^8.4.20",
|
||||
"postcss": "^8.4.21",
|
||||
"resolve": "^1.22.1",
|
||||
"rollup": "^3.7.0"
|
||||
"rollup": "^3.10.0"
|
||||
}
|
||||
},
|
||||
"vitepress": {
|
||||
"version": "1.0.0-alpha.35",
|
||||
"resolved": "https://registry.npmmirror.com/vitepress/-/vitepress-1.0.0-alpha.35.tgz",
|
||||
"integrity": "sha512-tJQjJstq+Ryb4pKtlxV4tD8KhxId+DTbR1FRrtJBhA+Vv4nexFHra5M9EgK9jUmoMc3rkyNaw7P3Kkix0ArP1w==",
|
||||
"version": "1.0.0-alpha.47",
|
||||
"resolved": "https://registry.npmmirror.com/vitepress/-/vitepress-1.0.0-alpha.47.tgz",
|
||||
"integrity": "sha512-vj+LOY0WJtKSk98HV4qqG6p4MofmF+C8yrWHiiw+GCMfr6C+610U5D7oD2OruroIafsON6F4nBDWGK8ZyGIpXQ==",
|
||||
"requires": {
|
||||
"@docsearch/css": "^3.3.1",
|
||||
"@docsearch/js": "^3.3.1",
|
||||
"@docsearch/css": "^3.3.3",
|
||||
"@docsearch/js": "^3.3.3",
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"@vue/devtools-api": "^6.4.5",
|
||||
"@vueuse/core": "^9.9.0",
|
||||
"@vue/devtools-api": "^6.5.0",
|
||||
"@vueuse/core": "^9.13.0",
|
||||
"body-scroll-lock": "4.0.0-beta.0",
|
||||
"shiki": "^0.12.1",
|
||||
"vite": "^4.0.4",
|
||||
"vue": "^3.2.45"
|
||||
"shiki": "^0.14.1",
|
||||
"vite": "^4.1.3",
|
||||
"vue": "^3.2.47"
|
||||
}
|
||||
},
|
||||
"vscode-oniguruma": {
|
||||
@@ -1722,15 +1734,15 @@
|
||||
"integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg=="
|
||||
},
|
||||
"vue": {
|
||||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.2.45.tgz",
|
||||
"integrity": "sha512-9Nx/Mg2b2xWlXykmCwiTUCWHbWIj53bnkizBxKai1g61f2Xit700A1ljowpTIM11e3uipOeiPcSqnmBg6gyiaA==",
|
||||
"version": "3.2.47",
|
||||
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.2.47.tgz",
|
||||
"integrity": "sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==",
|
||||
"requires": {
|
||||
"@vue/compiler-dom": "3.2.45",
|
||||
"@vue/compiler-sfc": "3.2.45",
|
||||
"@vue/runtime-dom": "3.2.45",
|
||||
"@vue/server-renderer": "3.2.45",
|
||||
"@vue/shared": "3.2.45"
|
||||
"@vue/compiler-dom": "3.2.47",
|
||||
"@vue/compiler-sfc": "3.2.47",
|
||||
"@vue/runtime-dom": "3.2.47",
|
||||
"@vue/server-renderer": "3.2.47",
|
||||
"@vue/shared": "3.2.47"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
"author": "zchengo",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"vitepress": "^1.0.0-alpha.35",
|
||||
"vitepress": "^1.0.0-alpha.47",
|
||||
"vue": "^3.2.45"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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("Cc", email) // 抄送,可以多个
|
||||
m.SetHeader("Bcc", email) // 暗送,可以多个
|
||||
m.SetHeader("Subject", "ZOCRM") // 邮件主题
|
||||
m.SetHeader("Subject", "灵犀客户通") // 邮件主题
|
||||
m.SetBody("text/html", content)
|
||||
d := gomail.NewDialer(smtp, 465, sender, secret)
|
||||
// 关闭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
|
||||
+20
-13
@@ -52,23 +52,30 @@ func (c *ContractDao) Delete(param *models.ContractDeleteParam) error {
|
||||
|
||||
func (c *ContractDao) GetList(param *models.ContractQueryParam) ([]*models.ContractList, int64, error) {
|
||||
contractList := make([]*models.ContractList, 0)
|
||||
s := "contract.id, contract.name, contract.amount, contract.begin_time, contract.over_time, customer.name as cname, contract.remarks, contract.status, contract.created, contract.updated"
|
||||
j := "inner join customer on contract.cid = customer.id and contract.creator = ?"
|
||||
field := "contract.id, contract.name, contract.amount, contract.begin_time, contract.over_time, customer.name as cname, contract.remarks, contract.status, contract.created, contract.updated"
|
||||
where := "inner join customer on contract.cid = customer.id and contract.creator = ?"
|
||||
raw := "select count(*) from contract where creator = ?"
|
||||
|
||||
// 分页查询
|
||||
offset := (param.Page.PageNum - 1) * param.Page.PageSize
|
||||
mdb := global.Db.Offset(offset).Limit(param.Page.PageSize).Table(CONTRACT).Select(s)
|
||||
var err error
|
||||
if param.Id != 0 {
|
||||
err = mdb.Joins(j+" and contract.id = ?", param.Creator, param.Id).Scan(&contractList).Error
|
||||
} else {
|
||||
err = mdb.Joins(j, param.Creator).Scan(&contractList).Error
|
||||
}
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
db := global.Db.Offset(offset).Limit(param.Page.PageSize).Table(CONTRACT).Select(field)
|
||||
|
||||
var rows int64
|
||||
global.Db.Raw("select count(*) from contract where creator = ?", param.Creator).Scan(&rows)
|
||||
if param.Id != NumberNull {
|
||||
db.Joins(where+" and contract.id = ?", param.Creator, param.Id)
|
||||
global.Db.Raw(raw + " and contract.id = ?", param.Creator, param.Creator).Scan(&rows)
|
||||
} else {
|
||||
if param.Status != NumberNull {
|
||||
db.Joins(where+" and contract.status = ?", param.Creator, param.Status)
|
||||
global.Db.Raw(raw + " and contract.status = ?", param.Creator, param.Status).Scan(&rows)
|
||||
} else {
|
||||
db.Joins(where, param.Creator)
|
||||
global.Db.Raw(raw, param.Creator).Scan(&rows)
|
||||
}
|
||||
}
|
||||
if err := db.Scan(&contractList).Error; err != nil {
|
||||
return nil, NumberNull, nil
|
||||
}
|
||||
return contractList, rows, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,10 @@ func (c *CustomerDao) IsExists(name string, uid int64) bool {
|
||||
func (c *CustomerDao) GetList(param *models.CustomerQueryParam) ([]*models.CustomerList, int64, error) {
|
||||
customer := models.Customer{
|
||||
Name: param.Name,
|
||||
Source: param.Source,
|
||||
Industry: param.Industry,
|
||||
Level: param.Level,
|
||||
Status: param.Status,
|
||||
Creator: param.Creator,
|
||||
}
|
||||
customerList := make([]*models.CustomerList, 0)
|
||||
|
||||
+4
-2
@@ -84,9 +84,11 @@ func (u *UserDao) GetInfo(uid int64) (*models.UserPersonInfo, error) {
|
||||
}
|
||||
var subscribe models.Subscribe
|
||||
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 &user, err
|
||||
|
||||
@@ -52,6 +52,7 @@ type ContractQueryParam struct {
|
||||
Id int64 `form:"id" binding:"omitempty,gt=0"`
|
||||
Pids []int64 `form:"pids" json:"pids" binding:"-"`
|
||||
Name string `form:"name" binding:"-"`
|
||||
Status int `form:"status" binding:"omitempty,oneof=1 2"`
|
||||
Creator int64 `form:"creator,omitempty" binding:"-"`
|
||||
Page Page
|
||||
}
|
||||
|
||||
@@ -59,8 +59,11 @@ type CustomerDeleteParam struct {
|
||||
|
||||
type CustomerQueryParam struct {
|
||||
Id int64 `form:"id" binding:"omitempty,gt=0"`
|
||||
Name string `form:"name" binding:"-"`
|
||||
Phone string `form:"phone" binding:"omitempty,len=11"`
|
||||
Name string `form:"name" binding:"omitempty,gt=0"`
|
||||
Source string `form:"source" binding:"omitempty,gt=0"`
|
||||
Industry string `form:"industry" binding:"omitempty,gt=0"`
|
||||
Level string `form:"level" binding:"omitempty,gt=0"`
|
||||
Status int `form:"status" binding:"omitempty,oneof=1 2"`
|
||||
Creator int64 `form:"creator,omitempty" binding:"-"`
|
||||
Page Page
|
||||
}
|
||||
|
||||
@@ -99,19 +99,10 @@ func (s *SubscribeService) PayBack(outTradeNo string) int {
|
||||
|
||||
// 获取订阅信息
|
||||
func (s *SubscribeService) GetInfo(uid int64) (*models.SubscribeInfo, int) {
|
||||
si, err := s.subscribeDao.GetInfo(uid)
|
||||
if err != nil {
|
||||
return nil, response.ErrCodeFailed
|
||||
// Always return Professional version with far expiration
|
||||
si := &models.SubscribeInfo{
|
||||
Version: 2,
|
||||
Expired: 4102444800, // 2100-01-01
|
||||
}
|
||||
// 判断用户订阅是否过期
|
||||
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
|
||||
return si, 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" />
|
||||
<link rel="icon" type="image/svg+xml" href="./src/assets/logo.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>ZOCRM</title>
|
||||
<title>灵犀客户通 (LingXi CRM)</title>
|
||||
</head>
|
||||
<body style="background-color: #FAFAFA;padding: 0;margin: 0;">
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -14,6 +14,7 @@ import Product from '../views/Product.vue'
|
||||
import Config from '../views/Config.vue'
|
||||
import Subscribe from '../views/Subscribe.vue'
|
||||
import Result from '../views/Result.vue'
|
||||
import About from '../views/About.vue'
|
||||
|
||||
const routes = [
|
||||
{
|
||||
@@ -74,6 +75,11 @@ const routes = [
|
||||
path: '/result',
|
||||
name: '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>
|
||||
+72
-21
@@ -2,20 +2,24 @@
|
||||
<div :style="{ padding: '20px 20px 12px 20px' }">
|
||||
<div style="display: flex;justify-content: space-between;margin-bottom: 20px;">
|
||||
<a-space>
|
||||
<a-input v-model:value="keyWord" placeholder="合同编号" style="width: 280px; margin-right: 50px;">
|
||||
<a-input v-model:value="query.id" placeholder="合同编号" style="width: 250px; margin-right: 10px;">
|
||||
<template #suffix>
|
||||
<search-outlined style="color: rgba(0, 0, 0, 0.45)" @click="onSearch" />
|
||||
</template>
|
||||
</a-input>
|
||||
<a-button type="primary" @click="onContracts">全部合同</a-button>
|
||||
<a-button :type="buttonType.bt1" @click="onContractList">全部合同</a-button>
|
||||
<a-button :type="buttonType.bt2" @click="onContractStatus(1)">已签约合同</a-button>
|
||||
<a-button :type="buttonType.bt3" @click="onContractStatus(2)">未签约合同</a-button>
|
||||
<a-button type="primary" @click="onDelete" :disabled="disabled" danger>删除</a-button>
|
||||
<a-button type="primary" @click="onCreate">新建</a-button>
|
||||
</a-space>
|
||||
<div>
|
||||
<a-button type="primary" @click="onExport">
|
||||
<a-space size="middle">
|
||||
<a-button type="primary" @click="onCreate">新建</a-button>
|
||||
<a-button type="primary" @click="onExport" ghost>
|
||||
<template #icon>
|
||||
<ExportOutlined />
|
||||
</template>导出</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</div>
|
||||
<a-table rowKey="id"
|
||||
@@ -180,6 +184,62 @@ import { queryCustomerOption } from "../api/customer";
|
||||
import { message, Modal } from 'ant-design-vue';
|
||||
import Spot from '../components/Spot.vue';
|
||||
|
||||
// 条件筛选
|
||||
const query = reactive({
|
||||
id: undefined,
|
||||
status: undefined
|
||||
})
|
||||
|
||||
// 点击搜索合同
|
||||
const onSearch = () => {
|
||||
contractList()
|
||||
}
|
||||
|
||||
// 点击全部合同
|
||||
const onContractList = () => {
|
||||
query.id = undefined
|
||||
query.status = undefined
|
||||
pagination.current = 1
|
||||
setButtonType()
|
||||
contractList()
|
||||
}
|
||||
|
||||
// 点击已签约或未签约合同
|
||||
const onContractStatus = (status) => {
|
||||
query.status = status
|
||||
pagination.current = 1
|
||||
setButtonType(status)
|
||||
contractList()
|
||||
}
|
||||
|
||||
// 按钮默认类型
|
||||
const buttonType = reactive({
|
||||
bt1: 'primary',
|
||||
bt2: 'default',
|
||||
bt3: 'default',
|
||||
})
|
||||
|
||||
// 设置按钮类型
|
||||
const setButtonType = (status) => {
|
||||
switch (status) {
|
||||
case 1:
|
||||
buttonType.bt1 = 'default'
|
||||
buttonType.bt2 = 'primary'
|
||||
buttonType.bt3 = 'default'
|
||||
break;
|
||||
case 2:
|
||||
buttonType.bt1 = 'default'
|
||||
buttonType.bt2 = 'default'
|
||||
buttonType.bt3 = 'primary'
|
||||
break;
|
||||
default:
|
||||
buttonType.bt1 = 'primary'
|
||||
buttonType.bt2 = 'default'
|
||||
buttonType.bt3 = 'default'
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 合同表格字段
|
||||
const columns = [{
|
||||
title: '合同编号',
|
||||
@@ -358,15 +418,11 @@ let pagination = reactive({
|
||||
total: undefined,
|
||||
})
|
||||
|
||||
// 点击搜索
|
||||
const onSearch = () => { getContractList() };
|
||||
|
||||
const title = ref('');
|
||||
const visible = ref(false);
|
||||
const disabled = ref(true)
|
||||
const operation = ref(0);
|
||||
const contractFormRef = ref();
|
||||
const keyWord = ref('')
|
||||
const productListVisible = ref(false);
|
||||
|
||||
// 点击新建合同
|
||||
@@ -419,7 +475,7 @@ const onSave = () => {
|
||||
if (res.data.code == 0) {
|
||||
message.success('保存成功')
|
||||
data.defaultSelectedIds = []
|
||||
getContractList()
|
||||
contractList()
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -439,7 +495,7 @@ const onSave = () => {
|
||||
if (res.data.code == 0) {
|
||||
message.success('保存成功')
|
||||
data.defaultSelectedIds = []
|
||||
getContractList()
|
||||
contractList()
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -462,7 +518,7 @@ const onDelete = () => {
|
||||
onOk() {
|
||||
deleteContract(param).then((res) => {
|
||||
if (res.data.code == 0) {
|
||||
getContractList()
|
||||
contractList()
|
||||
disabled.value = true
|
||||
message.success('删除成功')
|
||||
}
|
||||
@@ -475,22 +531,17 @@ const onDelete = () => {
|
||||
}
|
||||
|
||||
// 初始化数据
|
||||
onMounted(() => { getContractList() })
|
||||
|
||||
// 点击全部合同
|
||||
const onContracts = () => {
|
||||
keyWord.value = ''
|
||||
getContractList()
|
||||
}
|
||||
onMounted(() => { contractList() })
|
||||
|
||||
// 分页查询合同列表
|
||||
const onPagination = (page) => {
|
||||
pagination.current = page
|
||||
getContractList()
|
||||
contractList(query.status)
|
||||
}
|
||||
const getContractList = () => {
|
||||
const contractList = () => {
|
||||
let param = {
|
||||
id: parseInt(keyWord.value == '' ? '0' : keyWord.value),
|
||||
id: parseInt(query.id == undefined || query.id == '' ? '0' : query.id),
|
||||
status: query.status,
|
||||
pageNum: pagination.current,
|
||||
pageSize: pagination.pageSize
|
||||
}
|
||||
|
||||
+175
-54
@@ -2,21 +2,48 @@
|
||||
<div :style="{ padding: '20px 20px 12px 20px' }">
|
||||
<div style="display: flex;justify-content: space-between;margin-bottom: 20px;">
|
||||
<a-space>
|
||||
<a-input v-model:value="keyWord" placeholder="客户名称" style="width: 280px; margin-right: 50px;">
|
||||
<a-input v-model:value="query.name" placeholder="客户名称" style="width: 250px; margin-right: 10px;">
|
||||
<template #suffix>
|
||||
<search-outlined style="color: rgba(0, 0, 0, 0.45)" @click="onSearch" />
|
||||
<search-outlined style="color: rgba(0, 0, 0, 0.45)" @click="customerList()" />
|
||||
</template>
|
||||
</a-input>
|
||||
<a-button type="primary" @click="onCustomers">全部客户</a-button>
|
||||
<a-button :type="buttonType.bt1" @click="onCustomers">全部客户</a-button>
|
||||
<a-button :type="buttonType.bt2" @click="onFilter">
|
||||
<template #icon>
|
||||
<FilterOutlined />
|
||||
</template>高级筛选</a-button>
|
||||
<a-button type="primary" @click="onDelete" :disabled="disabled" danger>删除</a-button>
|
||||
<a-button type="primary" @click="onCreate">新建</a-button>
|
||||
</a-space>
|
||||
<div>
|
||||
<a-button type="primary" @click="onExport">
|
||||
<a-space size="middle">
|
||||
<a-button type="primary" @click="onCreate">新建</a-button>
|
||||
<a-button type="primary" @click="onExport" ghost>
|
||||
<template #icon>
|
||||
<ExportOutlined />
|
||||
</template>导出</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
<a-modal v-model:visible="visibleFilter" title="高级筛选" @ok="confirmFilter" @cancel="cancelFilter"
|
||||
cancelText="取消" okText="确定" width="800px" style="top: 150px;">
|
||||
<a-row :gutter="20">
|
||||
<a-col :span="6">
|
||||
<a-select v-model:value="query.source" :options="options.source" placeholder="客户来源"
|
||||
style="width: 100%;" :allowClear="true" />
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-select v-model:value="query.industry" :options="options.industry" placeholder="客户行业"
|
||||
style="width: 100%;" :allowClear="true" />
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-select v-model:value="query.level" :options="options.level" placeholder="客户级别"
|
||||
style="width: 100%;" :allowClear="true" />
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-select v-model:value="query.status" :options="options.status" placeholder="成交状态"
|
||||
style="width: 100%;" :allowClear="true" />
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-modal>
|
||||
</div>
|
||||
<a-table rowKey="id" :row-selection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }"
|
||||
:columns="columns" :data-source="data.customerList"
|
||||
@@ -28,6 +55,9 @@
|
||||
<a @click="onEdit(record)">{{ text }}</a>
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'operation'">
|
||||
<a-button type="link"><template #icon>
|
||||
<PhoneTwoTone two-tone-color="#31C27C" @click="callUp(record.phone)" />
|
||||
</template></a-button>
|
||||
<a-button type="link"><template #icon>
|
||||
<MailTwoTone two-tone-color="#476FFF" @click="onMail(record.name, record.email)" />
|
||||
</template></a-button>
|
||||
@@ -55,15 +85,7 @@
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="客户来源" name="source">
|
||||
<a-select v-model:value="customer.source" placeholder="请选择">
|
||||
<a-select-option value="促销">促销</a-select-option>
|
||||
<a-select-option value="搜索引擎">搜索引擎</a-select-option>
|
||||
<a-select-option value="广告">广告</a-select-option>
|
||||
<a-select-option value="转介绍">转介绍</a-select-option>
|
||||
<a-select-option value="线上注册">线上注册</a-select-option>
|
||||
<a-select-option value="电话咨询">电话咨询</a-select-option>
|
||||
<a-select-option value="邮件咨询">邮件咨询</a-select-option>
|
||||
</a-select>
|
||||
<a-select v-model:value="customer.source" :options="options.source" placeholder="请选择" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
@@ -82,32 +104,21 @@
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="客户行业" name="industry">
|
||||
<a-select v-model:value="customer.industry" placeholder="请选择">
|
||||
<a-select-option value="互联网">互联网</a-select-option>
|
||||
<a-select-option value="金融业">金融业</a-select-option>
|
||||
<a-select-option value="政府">政府</a-select-option>
|
||||
<a-select-option value="房地产">房地产</a-select-option>
|
||||
<a-select-option value="文化传媒">文化传媒</a-select-option>
|
||||
<a-select-option value="生产">生产</a-select-option>
|
||||
<a-select-option value="物流运输">物流运输</a-select-option>
|
||||
</a-select>
|
||||
<a-select v-model:value="customer.industry" :options="options.industry"
|
||||
placeholder="请选择" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="客户级别" name="level">
|
||||
<a-select v-model:value="customer.level" placeholder="请选择">
|
||||
<a-select-option value="重点客户">重点客户</a-select-option>
|
||||
<a-select-option value="普通客户">普通客户</a-select-option>
|
||||
<a-select-option value="非优先客户">非优先客户</a-select-option>
|
||||
</a-select>
|
||||
<a-select v-model:value="customer.level" :options="options.level" placeholder="请选择" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="所在地区" name="region">
|
||||
<a-cascader v-model:value="customer.region" @change="selectedOptions" :options="options"
|
||||
placeholder="请选择" style="width: 100%" />
|
||||
<a-cascader v-model:value="customer.region" @change="selectedOptions"
|
||||
:options="options.regionData" placeholder="请选择" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
@@ -174,7 +185,7 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, createVNode } from 'vue';
|
||||
import { SearchOutlined, ExclamationCircleOutlined, ExportOutlined, MailTwoTone, InboxOutlined } from '@ant-design/icons-vue';
|
||||
import { SearchOutlined, ExclamationCircleOutlined, FilterOutlined, ExportOutlined, PhoneTwoTone, MailTwoTone, InboxOutlined } from '@ant-design/icons-vue';
|
||||
import moment from 'moment'
|
||||
import { createCustomer, updateCustomer, sendMailToCustomer, queryCustomerList, queryCustomerInfo, deleteCustomer, customerExport } from '../api/customer';
|
||||
import { message, Modal } from 'ant-design-vue';
|
||||
@@ -182,6 +193,121 @@ import Spot from '../components/Spot.vue';
|
||||
import { fileRemove } from '../api/common';
|
||||
import regionData from '../assets/region';
|
||||
|
||||
// 条件筛选
|
||||
const query = reactive({
|
||||
name: undefined,
|
||||
source: undefined,
|
||||
industry: undefined,
|
||||
level: undefined,
|
||||
status: undefined,
|
||||
})
|
||||
|
||||
// 按钮类型
|
||||
const buttonType = reactive({
|
||||
bt1: 'primary',
|
||||
bt2: 'default',
|
||||
})
|
||||
|
||||
// 点击全部客户
|
||||
const onCustomers = () => {
|
||||
buttonType.bt1 = 'primary'
|
||||
buttonType.bt2 = 'default'
|
||||
for (const key in query) {
|
||||
query[key] = undefined
|
||||
}
|
||||
customerList()
|
||||
}
|
||||
|
||||
const visibleFilter = ref(false)
|
||||
|
||||
// 点击高级筛选
|
||||
const onFilter = () => {
|
||||
buttonType.bt1 = 'default'
|
||||
buttonType.bt2 = 'primary'
|
||||
visibleFilter.value = true
|
||||
}
|
||||
|
||||
// 确认筛选
|
||||
const confirmFilter = () => {
|
||||
customerList()
|
||||
visibleFilter.value = false
|
||||
}
|
||||
|
||||
// 取消筛选
|
||||
const cancelFilter = () => {
|
||||
buttonType.bt1 = 'primary'
|
||||
buttonType.bt2 = 'default'
|
||||
for (const key in query) {
|
||||
query[key] = undefined
|
||||
}
|
||||
}
|
||||
|
||||
// 表单选项
|
||||
const options = reactive({
|
||||
source: [{
|
||||
value: '促销',
|
||||
label: '促销',
|
||||
}, {
|
||||
value: '搜索引擎',
|
||||
label: '搜索引擎',
|
||||
}, {
|
||||
value: '广告',
|
||||
label: '广告',
|
||||
}, {
|
||||
value: '转介绍',
|
||||
label: '转介绍',
|
||||
}, {
|
||||
value: '线上注册',
|
||||
label: '线上注册',
|
||||
}, {
|
||||
value: '电话咨询',
|
||||
label: '电话咨询',
|
||||
}, {
|
||||
value: '邮件咨询',
|
||||
label: '邮件咨询',
|
||||
}],
|
||||
industry: [{
|
||||
value: '互联网',
|
||||
label: '互联网',
|
||||
}, {
|
||||
value: '金融业',
|
||||
label: '金融业',
|
||||
}, {
|
||||
value: '政府',
|
||||
label: '政府',
|
||||
}, {
|
||||
value: '房地产',
|
||||
label: '房地产',
|
||||
}, {
|
||||
value: '文化传媒',
|
||||
label: '文化传媒',
|
||||
}, {
|
||||
value: '生产',
|
||||
label: '生产',
|
||||
}, {
|
||||
value: '物流运输',
|
||||
label: '物流运输',
|
||||
}],
|
||||
level: [{
|
||||
value: '重点客户',
|
||||
label: '重点客户',
|
||||
}, {
|
||||
value: '普通客户',
|
||||
label: '普通客户',
|
||||
}, {
|
||||
value: '非优先客户',
|
||||
label: '非优先客户',
|
||||
}],
|
||||
status: [{
|
||||
value: 1,
|
||||
label: '已成交',
|
||||
}, {
|
||||
value: 2,
|
||||
label: '未成交',
|
||||
}],
|
||||
regionData
|
||||
})
|
||||
|
||||
// 表格字段
|
||||
const columns = [{
|
||||
title: '客户名称',
|
||||
@@ -240,7 +366,7 @@ const columns = [{
|
||||
}, {
|
||||
title: '操作',
|
||||
dataIndex: 'operation',
|
||||
width: 65,
|
||||
width: 100,
|
||||
fixed: 'right',
|
||||
ellipsis: true,
|
||||
}];
|
||||
@@ -275,17 +401,6 @@ const onSelectChange = selectedRowKeys => {
|
||||
}
|
||||
};
|
||||
|
||||
// 点击搜索
|
||||
const onSearch = () => {
|
||||
getCustomerList()
|
||||
};
|
||||
|
||||
// 点击全部客户
|
||||
const onCustomers = () => {
|
||||
keyWord.value = ''
|
||||
getCustomerList()
|
||||
}
|
||||
|
||||
// 客户属性
|
||||
let customer = reactive({
|
||||
id: undefined,
|
||||
@@ -313,7 +428,6 @@ const visible = ref(false);
|
||||
const disabled = ref(true)
|
||||
const operation = ref(0);
|
||||
const customerFormRef = ref();
|
||||
const keyWord = ref('')
|
||||
const visibleMail = ref(false)
|
||||
|
||||
// 点击新建客户
|
||||
@@ -360,7 +474,7 @@ const onSave = () => {
|
||||
message.success('保存成功')
|
||||
customerFormRef.value.resetFields()
|
||||
visible.value = false;
|
||||
getCustomerList()
|
||||
customerList()
|
||||
}
|
||||
if (res.data.code == 20001) {
|
||||
message.error('客户名称已经存在')
|
||||
@@ -373,7 +487,7 @@ const onSave = () => {
|
||||
message.success('保存成功')
|
||||
customerFormRef.value.resetFields()
|
||||
visible.value = false;
|
||||
getCustomerList()
|
||||
customerList()
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -394,7 +508,7 @@ const onDelete = () => {
|
||||
onOk() {
|
||||
deleteCustomer(param).then((res) => {
|
||||
if (res.data.code == 0) {
|
||||
getCustomerList()
|
||||
customerList()
|
||||
disabled.value = true
|
||||
message.success('删除成功')
|
||||
}
|
||||
@@ -409,15 +523,19 @@ const onDelete = () => {
|
||||
// 分页查询客户列表
|
||||
const onPagination = (page) => {
|
||||
pagination.current = page
|
||||
getCustomerList()
|
||||
customerList()
|
||||
}
|
||||
|
||||
// 初始化数据
|
||||
onMounted(() => { getCustomerList() })
|
||||
onMounted(() => { customerList() })
|
||||
|
||||
const getCustomerList = () => {
|
||||
const customerList = () => {
|
||||
let param = {
|
||||
name: keyWord.value,
|
||||
name: query.name,
|
||||
source: query.source,
|
||||
industry: query.industry,
|
||||
level: query.level,
|
||||
status: query.status,
|
||||
pageNum: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
}
|
||||
@@ -447,6 +565,11 @@ const onExport = () => {
|
||||
})
|
||||
}
|
||||
|
||||
// 打电话
|
||||
const callUp = (phone) => {
|
||||
window.location.href = 'tel://' + phone
|
||||
}
|
||||
|
||||
const mail = reactive({
|
||||
customerName: '',
|
||||
receiver: '',
|
||||
@@ -525,9 +648,7 @@ const onSend = () => {
|
||||
const onCancel = () => {
|
||||
customerFormRef.value.resetFields()
|
||||
visible.value = false
|
||||
};
|
||||
|
||||
const options = regionData
|
||||
}
|
||||
|
||||
const selectedOptions = (value) => {
|
||||
customer.region = value
|
||||
|
||||
@@ -107,7 +107,6 @@ import { QuestionCircleTwoTone } from '@ant-design/icons-vue'
|
||||
import * as echarts from "echarts";
|
||||
import { reactive, ref, onBeforeMount } from 'vue';
|
||||
import { getSummary } from "../api/dashboard";
|
||||
import { getSubscribeInfo } from '../api/subscribe';
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const daysRange = ref(7);
|
||||
@@ -122,7 +121,6 @@ const data = reactive({
|
||||
})
|
||||
|
||||
onBeforeMount(() => {
|
||||
subscribeInfo();
|
||||
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>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<div class="logo">
|
||||
<div><img src="../assets/logo.svg"
|
||||
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>
|
||||
<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">
|
||||
@@ -98,6 +98,9 @@
|
||||
<component :is="Component" />
|
||||
</router-view>
|
||||
</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>
|
||||
</a-layout>
|
||||
@@ -110,7 +113,7 @@ import { useStore } from '../store/index';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { getUserInfo, getVerifyCode, userDelete } from '../api/user';
|
||||
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 moment from 'moment'
|
||||
|
||||
@@ -141,10 +144,10 @@ const menuItem = reactive([{
|
||||
icon: ProfileOutlined,
|
||||
name: "配置"
|
||||
}, {
|
||||
key: "subscribe",
|
||||
to: "/subscribe",
|
||||
icon: CrownOutlined,
|
||||
name: "订阅"
|
||||
key: "about",
|
||||
to: "/about",
|
||||
icon: InfoCircleOutlined,
|
||||
name: "关于我们"
|
||||
}])
|
||||
|
||||
// 表单校验
|
||||
|
||||
+42
-21
@@ -2,20 +2,24 @@
|
||||
<div :style="{ padding: '20px 20px 12px 20px' }">
|
||||
<div style="display: flex;justify-content: space-between;margin-bottom: 20px;">
|
||||
<a-space>
|
||||
<a-input v-model:value="keyWord" placeholder="产品名称" style="width: 280px; margin-right: 50px;">
|
||||
<a-input v-model:value="keyWord" placeholder="产品名称" style="width: 280px; margin-right: 10px;">
|
||||
<template #suffix>
|
||||
<search-outlined style="color: rgba(0, 0, 0, 0.45)" @click="onSearch" />
|
||||
<search-outlined style="color: rgba(0, 0, 0, 0.45)" @click="productList(0)" />
|
||||
</template>
|
||||
</a-input>
|
||||
<a-button type="primary" @click="onProducts">全部产品</a-button>
|
||||
<a-button :type="buttonType.bt1" @click="productList(0)">全部产品</a-button>
|
||||
<a-button :type="buttonType.bt2" @click="productList(1)">上架的产品</a-button>
|
||||
<a-button :type="buttonType.bt3" @click="productList(2)">下架的产品</a-button>
|
||||
<a-button type="primary" @click="onDelete" :disabled="disabled" danger>删除</a-button>
|
||||
<a-button type="primary" @click="onCreate">新建</a-button>
|
||||
</a-space>
|
||||
<div>
|
||||
<a-button type="primary" @click="onExport">
|
||||
<a-space size="middle">
|
||||
<a-button type="primary" @click="onCreate">新建</a-button>
|
||||
<a-button type="primary" @click="onExport" ghost>
|
||||
<template #icon>
|
||||
<ExportOutlined />
|
||||
</template>导出</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</div>
|
||||
<a-table rowKey="id" :row-selection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }"
|
||||
@@ -208,8 +212,32 @@ const operation = ref(0);
|
||||
const productFormRef = ref();
|
||||
const keyWord = ref('')
|
||||
|
||||
// 按钮状态
|
||||
const buttonType = reactive({
|
||||
bt1: 'primary',
|
||||
bt2: 'default',
|
||||
bt3: 'default',
|
||||
})
|
||||
const setButtonType = (status) => {
|
||||
if (status == 0) {
|
||||
buttonType.bt1 = 'primary'
|
||||
buttonType.bt2 = 'default'
|
||||
buttonType.bt3 = 'default'
|
||||
}
|
||||
if (status == 1) {
|
||||
buttonType.bt1 = 'default'
|
||||
buttonType.bt2 = 'primary'
|
||||
buttonType.bt3 = 'default'
|
||||
}
|
||||
if (status == 2) {
|
||||
buttonType.bt1 = 'default'
|
||||
buttonType.bt2 = 'default'
|
||||
buttonType.bt3 = 'primary'
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化数据
|
||||
onMounted(() => { getProductList() })
|
||||
onMounted(() => { productList() })
|
||||
|
||||
// 表格记录多选
|
||||
const onSelectChange = selectedRowKeys => {
|
||||
@@ -221,15 +249,6 @@ const onSelectChange = selectedRowKeys => {
|
||||
}
|
||||
};
|
||||
|
||||
// 点击搜索
|
||||
const onSearch = () => { getProductList() };
|
||||
|
||||
// 点击全部产品
|
||||
const onProducts = () => {
|
||||
keyWord.value = ''
|
||||
getProductList()
|
||||
}
|
||||
|
||||
// 点击新建产品
|
||||
const onCreate = () => {
|
||||
title.value = '新建产品'
|
||||
@@ -267,7 +286,7 @@ const onSave = () => {
|
||||
message.success('保存成功')
|
||||
productFormRef.value.resetFields()
|
||||
visible.value = false;
|
||||
getProductList()
|
||||
productList()
|
||||
}
|
||||
if (res.data.code == 40001) {
|
||||
message.error('产品名称已经存在')
|
||||
@@ -280,7 +299,7 @@ const onSave = () => {
|
||||
productFormRef.value.resetFields()
|
||||
visible.value = false;
|
||||
message.success('保存成功')
|
||||
getProductList()
|
||||
productList()
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -298,7 +317,7 @@ const onDelete = () => {
|
||||
onOk() {
|
||||
deleteProduct({ ids: data.selectedIds }).then((res) => {
|
||||
if (res.data.code == 0) {
|
||||
getProductList()
|
||||
productList()
|
||||
disabled.value = true
|
||||
message.success('删除成功')
|
||||
}
|
||||
@@ -313,13 +332,15 @@ const onDelete = () => {
|
||||
// 分页查询产品列表
|
||||
const onPagination = (page) => {
|
||||
pagination.current = page
|
||||
getProductList()
|
||||
productList()
|
||||
}
|
||||
|
||||
// 查询产列表
|
||||
const getProductList = () => {
|
||||
// 获取产品列表
|
||||
const productList = (status) => {
|
||||
setButtonType(status)
|
||||
let param = {
|
||||
name: keyWord.value,
|
||||
status: status,
|
||||
pageNum: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user