feat: R1 重构对称迁移 + 升级依赖 + 配置文件分离

- 命名重构:tenant → customer(对齐后端 R1 重构)
  - Tenant → Customer / tenant_id → customerId
  - tenantStore → customerStore / getTenant/setTenant → getCustomer/setCustomer
  - cookie key tenant → customer, HTTP header tenantId → customerId
- 后端契约更新:AuthResponse 加 sub 字段
- 接口 URL 修复:/auth/oidc/callback → /api/v1/auth/oidc/callback
- 依赖升级:vue 3.5 / element-plus 2.8 / vite 5 / typescript 5.6 / sass 1.79
- 删 node-sass / scss / sass-loader / moment / mock/index.ts
- 配置文件拆分:.env.development / .env.production / .env.example
- 删除冗余 view/auth/auth/callback.vue 副本
- .gitignore 增加 dist / .opencode / tmp
- 新增 AGENT.md 与后端风格对齐
This commit is contained in:
simple321vip
2026-06-22 08:02:21 +08:00
parent bef1906747
commit 4da6832b44
112 changed files with 6616 additions and 4802 deletions
+43
View File
@@ -0,0 +1,43 @@
# ============== 环境变量模板 ==============
# 复制为 .env.development / .env.production / .env.test 后填入真实值。
# 真实值不要 commit 到 git。
# 当前模式名(dev / test / prod
VITE_MODE_NAME=development
# 资源 CDN 前缀
VITE_RES_URL=http://localhost:3000/
# 后端 API 基础地址
# dev: http://localhost:8080/auth
# prod: https://www.violin-home.cn
VITE_APP_URL=http://localhost:8080/auth
# 应用元信息
VITE_APP_TITLE=violin-home
VITE_APP_ID=123456
VITE_AGENT_ID=123456
# 是否启用 mockdev 启用,prod 关闭)
VITE_LOGIN_TEST=true
# ============== OIDC / Authentik 配置 ==============
# 三个环境的 issuer + client-id + redirect-uri 都不一样。
# dev 与 prod 必须用 Authentik 后台两个独立的 Application
# 因为 redirect-uri 不同(dev 走 localhostprod 走域名)。
# Authentik 域名(dev / prod 共用,dev 仅用于登录页跳转)
VITE_OIDC_ISSUER=https://auth.violin-work.online
# 客户端 ID(在 Authentik 后台创建)
# dev: violin-home-dev
# prod: violin-home
VITE_OIDC_CLIENT_ID=
# 回调 URI(必须与 Authentik 后台配置的完全一致)
# dev: http://localhost:3000/auth/callback
# prod: https://www.violin-home.cn/auth/callback
VITE_OIDC_REDIRECT_URI=
# 编辑器标识
VITE_EDITOR=vscode
+7
View File
@@ -9,10 +9,17 @@ lerna-debug.log*
node_modules node_modules
dist-ssr dist-ssr
dist
*.local *.local
# 临时文件
tmp/
.opencode/
.opencode/tmp/
# 配置文件信息不能暴露 # 配置文件信息不能暴露
.env.* .env.*
!.env.example
# Editor directories and files # Editor directories and files
.vscode/* .vscode/*
+210 -1
View File
@@ -1 +1,210 @@
# AGENT.md # AGENT.md — violin-home
> 面向 AI agent 的工程导览:模块关系、路由约定、与后端的契约、扩展点。
> 代码即事实,本文件是索引;如代码与本文档冲突,**以代码为准**。
---
## 项目定位
violin-home 是 **violin 微服务套件的统一前端门户**,部署在 `https://www.violin-home.cn/`
- 认证:**Authentik OIDC**,回调路径 `/auth/callback`,前端把 code POST 到 `violin-auth` 服务换 token
- 后端:依赖 violin-auth(认证)/ violin-bookmark(书签)/ violin-calendar(日历)/ violin-onenote / violin-cloud / 等子服务
- 域名:生产 `https://www.violin-home.cn/`;本地 dev 走 `.env.development``VITE_APP_URL`
---
## 技术栈
| 维度 | 选型 |
|------|------|
| 框架 | Vue 3.5 + Composition API (`<script setup>`) |
| 构建 | Vite 5 |
| 语言 | TypeScript 5.6 |
| UI | Element Plus 2.8 + Icons |
| 状态 | Pinia 2.2 |
| 路由 | vue-router 4.4**memory history**,不是 hash / web |
| HTTP | axios 1.7 |
| 图表 | ECharts 5 |
| Markdown | md-editor-v3 4 |
| 日期 | dayjs**已废弃 moment** |
| Mock | vite-plugin-mock 3**仅 dev 启用** |
| 拖拽 | vuedraggable 4 |
| 样式 | SCSSDart Sass 1.79**已删除 node-sass** |
---
## 目录结构
```
violin-home/
├── public/ 静态资源(不打包)
│ ├── blog/*.ts 博客元数据(按业务模块拆分)
│ └── reset/ Authentik reset 入口(reset.html 等)
├── src/
│ ├── api/ axios 接口封装(每个业务一个文件)
│ ├── components/ 公共组件
│ │ ├── common/ 通用组件
│ │ ├── layout/ 顶层布局
│ │ ├── login/ 登录 / 注册 / sorryPage
│ │ ├── cloud/ 云盘 + aplayer 音乐
│ │ ├── illustration/ 插画
│ │ ├── customPic/ 自定义图片
│ │ └── operate/ 操作组件
│ ├── const/ 全局常量
│ ├── entity/ 实体类型(TypeScript interface
│ ├── router/ 路由表(**统一指向 @/view/**
│ ├── service/ 服务层(独立于 axios 的工具服务)
│ ├── store/ Pinia stores
│ ├── style/ 全局样式(mixin.scss 在 vite.config.ts 里自动注入)
│ ├── types/ 类型声明(*.d.ts
│ ├── utils/ 工具函数(auth 令牌管理、request axios 封装、date、number
│ ├── view/ **页面视图(业务页)** — 单数 view
│ ├── App.vue
│ ├── env.d.ts
│ ├── main.ts
│ └── permission.ts 路由守卫
├── mock/ vite-plugin-mock 的 mock 数据
├── authentik-deploy/ Authentik 部署相关(helm values、reset 脚本)
├── .env.development 本地开发
├── .env.test 测试环境
├── .env.production 生产环境
├── Dockerfile
├── Jenkinsfile
├── nginx.conf
├── vite.config.ts
└── tsconfig.json
```
---
## 路由约定
**全部路由指向 `@/view/`**(单数),包括 `/auth/callback`
- `router/index.ts` 是**唯一**的路由表
- 路由 meta.name 是侧边栏展示的中文名
- `/auth/callback``view/auth/callback.vue` 处理 OIDC 回调(拿 code → POST `/auth/oidc/callback` 到 violin-auth → 存 token
**禁止**新建 `views/`(复数)目录。
---
## 认证流程(OIDC
```
浏览器 → Authentik 登录页(不在本工程)
↓ 回调到 https://www.violin-home.cn/auth/callback?code=...&state=...
view/auth/callback.vue
↓ POST { code, redirect_uri } → VITE_APP_URL + '/auth/oidc/callback'
violin-auth 服务(cn.violin.auth.service.OAuthService.oidcAuthorize
↓ 返回 { token, user_id, name }
utils/auth.setToken / setTenant 存到 cookie + localStorage
↓ 跳到 /
```
**关键约定**
- `redirect_uri` 必须和 Authentik 应用的配置完全一致(`VITE_OIDC_REDIRECT_URI`
- `state` 必须校验(防 CSRF),前后端都用 `sessionStorage.oidc_state` 缓存
- token 由 `setToken()` 存到 cookie`js-cookie`),后续 axios 请求拦截器自动加 `Authorization: Bearer <token>`
---
## HTTP 封装(utils/request.ts
- `baseURL` = `import.meta.env.VITE_APP_URL`
- 请求拦截器:从 `js-cookie` 读 token,加到 header
- 响应拦截器:401 → 跳 `/login`5xx → ElMessage 报错
每个业务模块在 `src/api/` 下有自己的文件(如 `api/bookmark.ts`),导出按业务分组的函数:
```ts
import { service } from '@/utils/request'
export const getBookmarks = (params: PageQuery) =>
service({ url: '/api/v1/bookmark', method: 'get', params })
```
---
## 状态管理(Pinia
`src/store/` 下每个业务一个 storesettings、strategy、table、tenant)。**禁止**把所有状态塞进一个 store。
- `tenant` store 管当前 customer(对应后端的 customerId
- `settings` store 管用户偏好
- `strategy` store 管 CTA 策略模板
- `table` store 管通用表格筛选/分页状态
---
## 与后端子服务的对应
| 路径前缀 | 后端服务 | 端口(K8s 内) |
|---------|---------|--------------|
| `/auth/**` | violin-auth | 8080 |
| `/api/v1/bookmark/**` | violin-bookmark | 8081 |
| `/api/v1/calendar/**` | violin-calendar | 8082 |
| `/api/v1/onenote/**` | violin-onenote | 8083 |
| `/api/v1/cloud/**` | violin-cloud | 8084 |
| `/api/v1/trader/**` | violin-trader | 8085 |
| `/api/v1/user/**` | violin-authuser 模块) | 8080 |
路由由 K8s ingress 统一反向代理;前端只关心 `VITE_APP_URL` 单一域名。
---
## 构建与运行
```bash
# 安装依赖(推荐 pnpm,yarn 也行)
npm install
# 本地 dev(启动 mock
npm run dev
# 生产构建(先类型检查,再打包)
npm run build
# 测试环境构建
npm run build:test
# 预览构建产物
npm run preview
```
Nodev18+**不要**用 v14/16,老的 sass-loader/node-sass 不兼容)。
---
## 已知坑(不要"清理"它们)
### 1. `view/` 单数 vs `views/` 复数
**永远只创建 `view/` 单数目录**`views/` 是历史遗物,已删除过又留下空目录。
### 2. node-sass 已废弃
`package.json` 里**没有** `node-sass`。如果你看到 `npm install``node-sass` 相关错误,是因为旧依赖被缓存,**不要**手动加回 `node-sass`,用 Dart Sass`sass: ^1.79`)即可。
### 3. axios 0.x → 1.x
升级到 axios 1.7 后,`axios.create()` 配置有少量变化(如 `paramsSerializer`)。如果你看到 `interceptor is not a function` 之类的报错,看下拦截器是否还兼容旧签名。
### 4. Vue Router 4 用 memory history
`createMemoryHistory()` 而非 `createWebHistory()` / `createWebHashHistory()`。刷新页面会丢路由——如果要做刷新保留路由,需要切到 `createWebHistory()` 并配合 K8s ingress 重写。
### 5. vite-plugin-mock 仅 dev 启用
生产构建不会带 mock`localEnabled: command === 'serve'`)。如果发现生产缺数据,看下是不是依赖了 mock。
---
## 相关项目
- `violin-auth` — 认证服务(OIDC + JWT 签发)
- `violin-bookmark` / `violin-calendar` / `violin-onenote` / `violin-cloud` / `violin-trader` — 业务子服务
- `violin-common` / `violin-core` — 后端共享框架
- `violin-parent` — 后端父 POM
+14
View File
@@ -0,0 +1,14 @@
# ========== Secret(密码管理)============
# ⚠️ 生产环境请使用真正随机的密码
# 生成随机密码: python3 -c "import secrets; print(secrets.token_urlsafe(32))"
apiVersion: v1
kind: Secret
metadata:
name: authentik-secrets
namespace: global
type: Opaque
stringData:
# PostgreSQL 数据库密码
postgres-password: "你的postgres密码"
# Secret Key(用于加密 session、token 等)
authentik-secret-key: "你的随机密钥(至少32字符)"
+48
View File
@@ -0,0 +1,48 @@
# ========== 5. Ingress 配置 ==========
# 使用 cert-manager 自动管理 Let's Encrypt 证书
# 如未安装 cert-manager: kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.0/cert-manager.yaml
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: authentik
namespace: auth
annotations:
kubernetes.io/ingress.class: "nginx"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
spec:
tls:
- hosts:
- auth.violin-work.online
secretName: authentik-tls
rules:
- host: auth.violin-work.online
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: authentik-server
port:
number: 80
---
# 如果没有 cert-manager,用这个 ClusterIssuer(先安装 cert-manager
# apiVersion: cert-manager.io/v1
# kind: ClusterIssuer
# metadata:
# name: letsencrypt-prod
# spec:
# acme:
# server: https://acme-v02.api.letsencrypt.org/directory
# email: YOUR_EMAIL@domain.com # ⚠️ 改成你的邮箱
# privateKeySecretRef:
# name: letsencrypt-prod
# solvers:
# - http01:
# ingress:
# class: nginx
+27
View File
@@ -0,0 +1,27 @@
# Ingress - 只暴露 server 服务,worker 不需要对外访问
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: authentik
namespace: global
annotations:
kubernetes.io/ingress.class: "nginx"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
spec:
tls:
- hosts:
- auth.violin-work.online
secretName: authentik-tls
rules:
- host: auth.violin-work.online
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: authentik-server
port:
number: 80
+116
View File
@@ -0,0 +1,116 @@
# Authentik 部署指南
## 目录结构
```
authentik-deploy/
├── 00-ns.yaml # Namespace
├── 01-postgres.yaml # PostgreSQL 数据库
├── 02-secret.yaml # 密钥(密码)
├── auth-helm-values.yaml # Authentik Helm 配置
├── 03-ingress.yaml # Ingress + TLS 证书
├── install.sh # 一键安装脚本
└── README.md # 本文件
```
## 部署步骤
### 1. 准备
确保集群已安装:
- **Helm 3**
- **cert-manager**(自动管理 HTTPS 证书)
```bash
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.0/cert-manager.yaml
```
### 2. 修改配置
编辑 `02-secret.yaml`,替换为真实随机密码:
```bash
# 生成随机密码
python3 -c "import secrets; print(secrets.token_urlsafe(32))"
```
将输出填入 `authentik-secret-key` 和 `postgres-password`。
编辑 `03-ingress.yaml`,将 `YOUR_EMAIL@domain.com` 替换为你的邮箱。
### 3. 执行部署
```bash
cd authentik-deploy
chmod +x install.sh
./install.sh
```
或手动按顺序执行:
```bash
kubectl create ns auth
kubectl apply -f 00-ns.yaml
kubectl apply -f 01-postgres.yaml
kubectl apply -f 02-secret.yaml
helm repo add authentik https://charts.goauthentik.io && helm repo update
helm install authentik authentik/authentik -n auth -f auth-helm-values.yaml
kubectl apply -f 03-ingress.yaml
```
### 4. 初始化
部署完成后访问:
- **首次设置**: https://auth.violin-work.online/if/flow/initial/
- 设置管理员账号和密码
### 5. 验证
```bash
kubectl get pods -n auth
kubectl logs -n auth -l app.kubernetes.io/component=server --tail=20
```
---
## 后续:配置其他服务接入 AuthentikOIDC
### 在 Authentik 中创建 Application
1. 登录 Authentik Admin → **Applications**
2. 点击 **Create**
- **Name**: OpenViking(或任意名称)
- **Slug**: openviking
- **Provider**: 创建 OIDC Provider(见下方)
3. 创建 Provider
- **Name**: OpenViking Provider
- **Client ID**: openviking
- **Client Secret**: 生成一个随机值
- **Redirect URIs**: `https://viking.violin-work.online/-/oauth-callback/openviking/`(根据实际调整)
### OpenViking 配置 OIDC
在 OpenViking 的配置文件中加入:
```json
{
"oidc": {
"issuer": "https://auth.violin-work.online",
"client_id": "openviking",
"client_secret": "你的client_secret"
}
}
```
### KubeSphere 配置 OIDC
在 KubeSphere Web 控制台:
- **平台管理 → 访问控制 → 企业设置 → 第三方登录**
- 填入 Authentik 的 OIDC 信息
---
## 维护
```bash
# 更新 Authentik
helm repo update && helm upgrade authentik authentik/authentik -n auth -f auth-helm-values.yaml
# 卸载
helm uninstall authentik -n auth && kubectl delete ns auth
```
+33
View File
@@ -0,0 +1,33 @@
# Authentik Helm - 2026.5.3 腾讯云镜像配置
global:
image:
repository: ccr.ccs.tencentyun.com/tei_agent/authentik-server
tag: "2026.5.3"
authentik:
secret_key: "Mb83201048"
postgresql:
host: "192.168.3.49"
port: 5432
name: "authentik"
user: "authentik"
password: "authentik"
persistence:
enabled: true
size: 5Gi
postgresql:
enabled: false
server:
replicas: 1
ingress:
enabled: false
worker:
replicas: 1
geoip:
enabled: false
+39
View File
@@ -0,0 +1,39 @@
# ========== 一键安装脚本(Linux/macOS============
# 保存为 install.sh,执行: chmod +x install.sh && ./install.sh
#!/bin/bash
set -e
EMAIL="admin@violin-work.online" # ⚠️ 改成你的邮箱(Let's Encrypt 通知用)
DOMAIN="auth.violin-work.online" # ⚠️ 改成你的域名
echo "=== 1. 创建 namespace ==="
kubectl create ns auth --dry-run=client -o yaml | kubectl apply -f -
echo "=== 2. 创建 Secret ==="
kubectl apply -f 02-secret.yaml
echo "=== 3. 部署 PostgreSQL ==="
kubectl apply -f 01-postgres.yaml
echo "=== 4. 等待 PostgreSQL 就绪 ==="
kubectl wait --for=condition=ready pod -l app=authentik-postgres -n auth --timeout=120s
echo "=== 5. 添加 Helm 源并安装 Authentik ==="
helm repo add authentik https://charts.goauthentik.io --force-update
helm repo update
helm install authentik authentik/authentik -n auth -f auth-helm-values.yaml
echo "=== 6. 部署 Ingress + 证书 ==="
kubectl apply -f 03-ingress.yaml
echo "=== 7. 等待 Authentik Server 就绪 ==="
kubectl wait --for=condition=ready pod -l app.kubernetes.io/component=server -n auth --timeout=180s
echo ""
echo "=== 部署完成! ==="
echo "访问: https://$DOMAIN"
echo "初始设置: https://$DOMAIN/if/flow/initial/"
echo ""
echo "查看状态:"
echo " kubectl get pods -n auth"
echo " kubectl logs -n auth -l app.kubernetes.io/component=server --tail=50"
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 KiB

-18
View File
@@ -1,18 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>violin-home</title>
<script type="module" crossorigin src="/resets/index.e20aada4.js"></script>
<link rel="stylesheet" href="/resets/index.35a0e576.css">
</head>
<body>
<div id="app"></div>
</body>
</html>
-10
View File
File diff suppressed because one or more lines are too long
BIN
View File
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
import{H as e}from"./index.e20aada4.js";import{l as i}from"./index.7b5a1be8.js";const t={"Content-Type":"application/json;charsetset=UTF-8"},l=r=>e({url:"/wiki/api/v1/reader/blogs",method:"GET",params:r,paramsSerializer:a=>i.stringify(a,{arrayFormat:"repeat"})}),p=()=>e({url:"/wiki/api/v1/reader/blog_type",method:"GET",headers:t}),n=r=>e({url:"/wiki/api/v1/reader/blog/"+r,method:"GET",headers:t}),u=()=>e({url:"/wiki/api/v1/reader/blogs/publish/all",method:"GET"}),g=r=>e({url:"/wiki/api/v1/reader/blogs/publish/"+r,method:"GET"});export{p as a,g as b,n as c,l as g,u as p};
-1
View File
@@ -1 +0,0 @@
import{H as r}from"./index.e20aada4.js";import{l as t}from"./index.7b5a1be8.js";const m=o=>r({url:"/bookmark/api/v1/bookmark",method:"GET",params:o,paramsSerializer:a=>t.stringify(a,{arrayFormat:"repeat"})}),s=o=>r({url:"/bookmark/api/v1/bookmark/insert",method:"PUT",data:o}),b=o=>r({url:"/bookmark/api/v1/bookmark/update",method:"POST",data:o}),u=o=>r({url:"/bookmark/api/v1/bookmark/delete/"+o,method:"DELETE"}),n=()=>r({url:"/bookmark/api/v1/bookmark/count",method:"GET"});export{n as b,u as d,s as p,m as s,b as u};
-1
View File
@@ -1 +0,0 @@
.b_section[data-v-f8c4f944],.bt_section[data-v-f8c4f944],.c_section[data-v-f8c4f944]{display:flex;flex-direction:column;align-items:stretch;border-right:1px solid royalblue}.title_style[data-v-f8c4f944]{display:flex}.title_style[data-v-f8c4f944] .el-input__inner{flex-direction:row;height:50px;font-size:20px;border-right:0px;border-left:0px;border-radius:0;border-top:1px solid royalblue;border-bottom:1px solid royalblue}.blog_editer[data-v-f8c4f944]{display:flex}.item_header[data-v-f8c4f944]{display:flex;align-items:center;border-top:1px solid royalblue;border-bottom:1px solid royalblue}.item_header_button[data-v-f8c4f944]{border:0px;height:50px}.side_bar[data-v-f8c4f944]{display:flex;align-items:center;justify-content:flex-start;width:20px}.item[data-v-f8c4f944]{padding:0;margin-top:3px;margin-bottom:3px;display:flex;height:50px;flex-direction:row;align-items:center;justify-items:center;user-select:none;border:0px;border-bottom:1px}.item>label[data-v-f8c4f944]{color:#333;height:50px;width:15px}.item>label[data-v-f8c4f944]:hover{cursor:move}.item>div[data-v-f8c4f944]{display:flex;flex-direction:column;align-items:flex-end;border:0px}.operate_button[data-v-f8c4f944]{border-color:#fff;width:20px;border:0px}.bt-input[data-v-f8c4f944]{margin:0;border:0px;padding-left:0;padding-right:5px;font-size:15px;overflow:hidden;line-height:50px;height:50px;width:100px;flex-grow:1;cursor:pointer;white-space:nowrap;text-overflow:ellipsis}.bt-input[data-v-f8c4f944]:hover{background-color:#ebebeb}.item>p[data-v-f8c4f944]{padding:6px 10px;color:#8e3333}.chosenClass[data-v-f8c4f944]{opacity:1;border:solid 1px red}.ghost[data-v-f8c4f944]{border:solid 1px rgb(19,41,239)!important}
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
import{H as r}from"./index.e20aada4.js";const a=()=>r({url:"/trader/api/v1/strategy_file",method:"GET"}),s=()=>r({url:"/trader/api/v1/strategy_file/load",method:"GET"}),o=t=>r({url:"/trader/api/v1/strategy_file/"+t,method:"PUT"}),i=t=>r({url:"/trader/api/v1/strategy_file/"+t,method:"PATCH"}),d=t=>r({url:"/trader/api/v1/strategy_file/"+t,method:"DELETE"}),g=()=>r({url:"/trader/api/v1/strategies",method:"GET"}),n=t=>r({url:"/trader/api/v1/strategy/"+t.strategy_name,method:"POST",data:t}),l=t=>r({url:"/trader/api/v1/strategy/init/"+t,method:"PUT"}),u=t=>r({url:"/trader/api/v1/strategy/"+t,method:"PUT"}),_=t=>r({url:"/trader/api/v1/strategy/"+t,method:"PATCH"}),y=t=>r({url:"/trader/api/v1/strategy/"+t,method:"DELETE"});export{s as a,g as b,n as c,_ as d,y as e,a as g,l as i,o as l,d as r,u as s,i as u};
File diff suppressed because one or more lines are too long
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
.el-button--text[data-v-19ae7838]{margin-right:15px}.el-select[data-v-19ae7838],.el-input[data-v-19ae7838]{width:300px}.dialog-footer button[data-v-19ae7838]:first-child{margin-right:10px}
-1
View File
@@ -1 +0,0 @@
import{g as I,l as N,u as T,r as V}from"./cta_strategy.0971c7e2.js";import{d as R,c as S,b as $,f as _,h as e,w as s,u as t,a2 as P,X as U,a3 as W,e as i,o as n,a4 as g,a5 as u,B as m,a6 as r,E as c,i as h,g as M,a1 as X,U as k}from"./index.e20aada4.js";const j={class:"bookmark"},q=h("\u6DFB\u52A0\u7B56\u7565\u6A21\u677F"),G=h(" -> "),H=h(" \u4E0A\u4F20\u7B56\u7565\u6A21\u677F "),J=M("div",{class:"el-upload__tip text-red"}," \u4E00\u6B21\u53EA\u80FD\u4E0A\u4F20\u4E00\u4E2A\u6587\u4EF6 ",-1),K={key:0,style:{cursor:"pointer"}},L={key:0,style:{cursor:"pointer"}},O={key:0,style:{cursor:"pointer"}},ee=R({setup(Q){const p=S([]),d=$(),f=()=>{I().then(l=>{p.length=0,l.data.forEach((a,y)=>{p.push(a)})})},E=l=>{d.value.clearFiles();const a=l[0];a.uid=W(),d.value.handleStart(a)},v=()=>{d.value.submit(),f()},w=l=>{l.status=2,N(l.file_name).then(a=>{a.status==200&&(l.status=1),a.status==500&&(l.status=0)})},C=l=>{T(l.class_name).then(a=>{a.status==200&&(l.status=0),a.status==500&&(l.status=1)})},b=l=>{V(l.file_name).then(a=>{f()}).catch(a=>{X({message:k("p",null,[k("i",{style:"color: teal"},a)])})})};return f(),(l,a)=>{const y=i("InfoFilled"),B=i("SuccessFilled"),x=i("WarningFilled"),A=i("CirclePlusFilled"),D=i("RemoveFilled"),z=i("Delete");return n(),_("div",j,[e(t(P),{ref_key:"upload",ref:d,class:"upload-demo",action:"http://localhost:5000/trader/api/v1/strategy_file",limit:1,"on-exceed":E,"auto-upload":!1},{trigger:s(()=>[e(t(g),{type:"primary"},{default:s(()=>[q]),_:1})]),tip:s(()=>[J]),default:s(()=>[G,e(t(g),{class:"ml-3",type:"success",onClick:v},{default:s(()=>[H]),_:1})]),_:1},512),e(t(U),{ref:"multipleTableRef",data:t(p),style:{width:"100%"}},{default:s(()=>[e(t(u),{type:"selection",width:"55"}),e(t(u),{type:"index",label:"index",width:"80"}),e(t(u),{prop:"file_name",label:"\u7B56\u7565\u6587\u4EF6\u540D",width:"200"}),e(t(u),{prop:"class_name",label:"\u7B56\u7565\u7C7B\u540D",width:"200"}),e(t(u),{prop:"status",label:"\u72B6\u6001",width:"60"},{default:s(o=>[o.row.status==0?(n(),m(t(r),{key:0,size:20},{default:s(()=>[e(y)]),_:1})):c("",!0),o.row.status==1?(n(),m(t(r),{key:1,size:20,color:"green"},{default:s(()=>[e(B)]),_:1})):c("",!0),o.row.status==2?(n(),m(t(r),{key:2,size:20,color:"gold"},{default:s(()=>[e(x)]),_:1})):c("",!0)]),_:1}),e(t(u),{prop:"url",label:"\u52A0\u8F7D",width:"60"},{default:s(o=>[o.row.status==0?(n(),_("span",K,[e(t(r),{size:20,onClick:F=>w(o.row),color:"blue"},{default:s(()=>[e(A)]),_:2},1032,["onClick"])])):c("",!0)]),_:1}),e(t(u),{prop:"url",label:"\u5378\u8F7D",width:"60"},{default:s(o=>[o.row.status==1?(n(),_("span",L,[e(t(r),{size:20,onClick:F=>C(o.row),color:"red"},{default:s(()=>[e(D)]),_:2},1032,["onClick"])])):c("",!0)]),_:1}),e(t(u),{prop:"url",label:"\u5220\u9664",width:"60"},{default:s(o=>[o.row.status==0?(n(),_("span",O,[e(t(r),{size:20,onClick:F=>b(o.row),color:"red"},{default:s(()=>[e(z)]),_:2},1032,["onClick"])])):c("",!0)]),_:1})]),_:1},8,["data"])])}}});export{ee as default};
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

-1
View File
@@ -1 +0,0 @@
import{a as o}from"./number.46c8fb59.js";const r=(e,t)=>(e.getMonth()+1,e.getDate(),e.getHours(),e.getMinutes(),e.getSeconds(),Math.floor((e.getMonth()+3)/3),e.getMilliseconds(),/(y+)/.test(t)&&(t=t.replace(RegExp.$1,(e.getFullYear()+"").substr(4-RegExp.$1.length))),t),n=(e,t)=>(t||(t="yyyy-MM-dd hh:mm:ss"),r(new Date(e),t).toLocaleString()),l=e=>e.getFullYear()+"-"+o(e.getMonth()+1)+"-"+o(e.getDate());export{n as f,l as t};
-1
View File
@@ -1 +0,0 @@
import{E as s}from"./style.1aad9498.js";import{d as c,b as u,B as d,u as l,R as i,a8 as p,o as m}from"./index.e20aada4.js";import{u as f,a as _}from"./profile.50110a29.js";const U=c({setup(v){let e=u(""),a=u("");p().query;const r=async()=>{await f({content:e.value}).then(t=>{a.value=t.data.updateDatetime}).catch(()=>{})};return(async()=>{await _().then(t=>{e.value=t.data.content,a.value=t.data.updateDatetime}).catch(()=>{})})(),(t,o)=>(m(),d(l(s),{modelValue:l(e),"onUpdate:modelValue":o[0]||(o[0]=n=>i(e)?e.value=n:e=n),onSave:r},null,8,["modelValue"]))}});export{U as default};
Binary file not shown.

Before

Width:  |  Height:  |  Size: 410 KiB

-1
View File
@@ -1 +0,0 @@
.dashboard-line-box .dashboard-line[data-v-19c5604b]{background-color:#fff;height:360px;width:100%}.dashboard-line-box .dashboard-line-title[data-v-19c5604b]{font-weight:600;margin-bottom:12px}.commit-table[data-v-554f7f6a]{background-color:#fff;height:400px}.commit-table-title[data-v-554f7f6a]{font-weight:600;margin-bottom:12px}.commit-table .log-item[data-v-554f7f6a]{display:flex;justify-content:space-between;margin-top:14px}.commit-table .log-item .key-box[data-v-554f7f6a]{justify-content:center}.commit-table .log-item .key[data-v-554f7f6a]{display:inline-flex;justify-content:center;align-items:center;width:20px;height:20px;border-radius:50%;background:#F0F2F5;text-align:center;color:#000000a6}.commit-table .log-item .key.top[data-v-554f7f6a]{background:#314659;color:#fff}.commit-table .log-item .message[data-v-554f7f6a]{color:#000000a6}.commit-table .log-item .form[data-v-554f7f6a]{color:#000000a6;margin-left:12px}.commit-table .log-item .flex[data-v-554f7f6a]{line-height:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.commit-table .log-item .flex-1[data-v-554f7f6a]{flex:1}.commit-table .log-item .flex-2[data-v-554f7f6a]{flex:2}.commit-table .log-item .flex-3[data-v-554f7f6a]{flex:3}.commit-table .log-item .flex-4[data-v-554f7f6a]{flex:4}.commit-table .log-item .flex-5[data-v-554f7f6a]{flex:5}.page[data-v-14e09d9b]{background:#f0f2f5;padding:0}.page .gva-card-box[data-v-14e09d9b]{padding:12px 16px}.page .gva-card-box+.gva-card-box[data-v-14e09d9b]{padding-top:0}.page .gva-card[data-v-14e09d9b]{box-sizing:border-box;background-color:#fff;border-radius:2px;height:auto;padding:26px 30px;overflow:hidden;box-shadow:0 0 7px 1px #00000008}.page .gva-top-card[data-v-14e09d9b]{height:260px;display:flex;align-items:center;justify-content:space-between;color:#777}.page .gva-top-card-left[data-v-14e09d9b]{height:100%;display:flex;flex-direction:column}.page .gva-top-card-left-title[data-v-14e09d9b]{font-size:22px;color:#343844}.page .gva-top-card-left-dot[data-v-14e09d9b]{font-size:16px;color:#6b7687;margin-top:24px}.page .gva-top-card-left-rows[data-v-14e09d9b]{margin-top:18px;color:#6b7687;width:600px;align-items:center}.page .gva-top-card-left-item[data-v-14e09d9b]{margin-top:14px}.page .gva-top-card-left-item+.gva-top-card-left-item[data-v-14e09d9b]{margin-top:24px}.page .gva-top-card-right[data-v-14e09d9b]{height:600px;width:600px;margin-top:28px}.page[data-v-14e09d9b] .el-card__header{padding:0;border-bottom:none}.page .card-header[data-v-14e09d9b]{padding-bottom:20px;border-bottom:1px solid #e8e8e8}.page .quick-entrance-title[data-v-14e09d9b]{height:30px;font-size:22px;color:#333;width:100%;border-bottom:1px solid #eee}.page .quick-entrance-items[data-v-14e09d9b]{display:flex;align-items:center;justify-content:center;text-align:center;color:#333}.page .quick-entrance-items .quick-entrance-item[data-v-14e09d9b]{padding:16px 28px;margin-top:-16px;margin-bottom:-16px;border-radius:4px;transition:all .2s;cursor:pointer;height:auto;text-align:center}.page .quick-entrance-items .quick-entrance-item[data-v-14e09d9b]:hover{box-shadow:0 0 7px #d9d9d98c}.page .quick-entrance-items .quick-entrance-item-icon[data-v-14e09d9b]{width:50px;height:50px!important;border-radius:8px;display:flex;align-items:center;justify-content:center;margin:0 auto}.page .quick-entrance-items .quick-entrance-item-icon i[data-v-14e09d9b]{font-size:24px}.page .quick-entrance-items .quick-entrance-item p[data-v-14e09d9b]{margin-top:10px}.page .echart-box[data-v-14e09d9b]{padding:14px}.dashboard-icon[data-v-14e09d9b]{font-size:20px;color:#55a0f8;width:30px;height:30px;margin-right:10px;display:flex;align-items:center}.flex-center[data-v-14e09d9b]{display:flex;align-items:center}@media (max-width: 750px){.gva-card[data-v-14e09d9b]{padding:20px 10px!important}.gva-card .gva-top-card[data-v-14e09d9b]{height:auto}.gva-card .gva-top-card-left-title[data-v-14e09d9b]{font-size:20px!important}.gva-card .gva-top-card-left-rows[data-v-14e09d9b]{margin-top:15px;align-items:center}.gva-card .gva-top-card-right[data-v-14e09d9b]{display:none}.gva-card .gva-middle-card-item[data-v-14e09d9b]{line-height:20px}.gva-card .dashboard-icon[data-v-14e09d9b]{font-size:18px}}
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
-1
View File
File diff suppressed because one or more lines are too long
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
.category_card-box[data-v-2dc81d35]{display:flex;flex-wrap:wrap}.category_card__header[data-v-2dc81d35]{text-align:center;width:100px;line-height:100px;height:100px}.category_card__body[data-v-2dc81d35]{text-align:center}.onenote_card__header[data-v-2dc81d35]{width:120px}.onenote_card-box[data-v-2dc81d35]{display:flex;flex-direction:column}.onenote_item_card[data-v-2dc81d35]{display:flex;margin:10px}.onenote_category_card[data-v-2dc81d35]{display:flex;flex-direction:column;margin:20px;padding:20px}.category_icon[data-v-2dc81d35]{margin-bottom:50px}.category_theme[data-v-2dc81d35]{margin-right:30px}
-1
View File
@@ -1 +0,0 @@
import{v as q,H as U,_ as J,d as K,c as g,b as p,l as Q,f as u,M as X,B as Y,w as a,E as S,h as l,u as o,F as T,e as n,L as Z,o as i,g as c,z as r,x as ee,i as E,p as te,j as le,m as oe}from"./index.e20aada4.js";import{t as f}from"./date.86e91942.js";import{s as ae}from"./settings.e80a4132.js";import"./number.46c8fb59.js";const ne=q.create(),se=()=>ne({url:"https://holidays-jp.github.io/api/v1/2023/date.json",method:"GET"}),de={"Content-Type":"application/json;charsetset=UTF-8"},ie=_=>U({url:"/calendar/api/v1/event",method:"PUT",data:_}),ue=()=>U({url:"/calendar/api/v1/event",method:"GET",headers:de});const I=_=>(te("data-v-0e3dfbc2"),_=_(),le(),_),re={key:0,class:"date-cell-holiday"},ce=["onClick"],_e=I(()=>c("br",null,null,-1)),me={key:1,class:"date-cell"},pe=["onClick"],fe=["onClick"],ve={key:2,class:"date-cell-event"},he=I(()=>c("span",null,"\u5F85\u529E\u4E8B\u9879",-1)),be={class:"dialog-footer"},ge=E("Cancel"),ke=E(" Confirm "),ye=K({setup(_){const v=ae(),k=g({}),y=g([]),x=g({}),d=g({reminder_date:"",title:"",reminder_info:""}),D=p(new Date),m=p(!1),h="140px",w=p(!1),B=p(!0),F=p([]),$=["email","messageBox","\u624B\u673A\u77ED\u4FE1","\u5FAE\u4FE1\u901A\u77E5"],C=s=>{d.reminder_date=f(s),m.value=!0},N=async()=>{await ie(d).then(()=>{}),m.value=!1,O("refresh")},O=s=>{s==="refresh"&&(v.settings.isLoading=!0,v.settings.isRouterAlive=!1,oe(()=>{v.settings.isRouterAlive=!0,v.settings.isLoading=!1}))};return Q(async()=>{await se().then(s=>{Object.keys(s.data).forEach(t=>{k[t]=s.data[t]})}),await ue().then(s=>{y.length=0,s.data.forEach(t=>{y.push(t),x[t.reminder_date]=t.title})}),w.value=!0,B.value=!1}),(s,t)=>{const G=n("el-calendar"),V=n("el-table-column"),M=n("el-table"),b=n("el-form-item"),A=n("el-input"),R=n("el-checkbox"),z=n("el-checkbox-group"),H=n("el-form"),j=n("el-button"),P=n("el-dialog"),W=Z("loading");return i(),u(T,null,[w.value?X((i(),Y(G,{key:0,modelValue:D.value,"onUpdate:modelValue":t[0]||(t[0]=e=>D.value=e),"element-loading-text":"\u4E3B\u4EBA\uFF0C\u522B\u7740\u6025\u6211\u5728\u52AA\u529B\u52A0\u8F7D\u4E2D^_^"},{dateCell:a(({data:e})=>[Object.keys(o(k)).includes(o(f)(e.date))?(i(),u("div",re,[c("span",{style:{color:"red"},onClick:L=>C(e.date)},r(e.date.getDate()),9,ce),_e,c("span",null,r(o(k)[o(f)(e.date)]),1)])):(i(),u("div",me,[e.date.getDay()==0||e.date.getDay()==6?(i(),u("span",{key:0,style:{color:"red"},onClick:L=>C(e.date)},r(e.date.getDate()),9,pe)):(i(),u("span",{key:1,onClick:L=>C(e.date)},r(e.date.getDate()),9,fe))])),Object.keys(o(x)).includes(o(f)(e.date))?(i(),u("div",ve,[c("span",null,r(o(x)[o(f)(e.date)]),1)])):S("",!0)]),_:1},8,["modelValue"])),[[W,B.value]]):S("",!0),he,l(M,{data:o(y)},{default:a(()=>[l(V,{property:"reminder_date",label:"Date",width:"150"}),l(V,{property:"title",label:"title",width:"200"}),l(V,{property:"reminder_info",label:"eventInfo"})]),_:1},8,["data"]),l(P,{modelValue:m.value,"onUpdate:modelValue":t[5]||(t[5]=e=>m.value=e),title:"\u5F85\u529E\u4E8B\u9879"},{footer:a(()=>[c("span",be,[l(j,{onClick:t[4]||(t[4]=e=>m.value=!1)},{default:a(()=>[ge]),_:1}),l(j,{type:"primary",onClick:N},{default:a(()=>[ke]),_:1})])]),default:a(()=>[l(H,{model:o(d)},{default:a(()=>[l(b,{label:"\u65E5\u671F","label-width":h},{default:a(()=>[c("span",null,r(o(d).reminder_date),1)]),_:1}),l(b,{label:"title","label-width":h},{default:a(()=>[l(A,{modelValue:o(d).title,"onUpdate:modelValue":t[1]||(t[1]=e=>o(d).title=e),autocomplete:"off"},null,8,["modelValue"])]),_:1}),l(b,{label:"eventInfo","label-width":h},{default:a(()=>[l(A,{modelValue:o(d).reminder_info,"onUpdate:modelValue":t[2]||(t[2]=e=>o(d).reminder_info=e),autocomplete:"off",type:"textarea"},null,8,["modelValue"])]),_:1}),l(b,{label:"alert","label-width":h},{default:a(()=>[l(z,{modelValue:F.value,"onUpdate:modelValue":t[3]||(t[3]=e=>F.value=e),min:0,max:2},{default:a(()=>[(i(),u(T,null,ee($,e=>l(R,{key:e,label:e},{default:a(()=>[E(r(e),1)]),_:2},1032,["label"])),64))]),_:1},8,["modelValue"])]),_:1})]),_:1},8,["model"])]),_:1},8,["modelValue"])],64)}}});var De=J(ye,[["__scopeId","data-v-0e3dfbc2"]]);export{De as default};
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
import{d as s,B as a,w as n,a7 as c,a1 as l,e as i,o as r,i as p}from"./index.e20aada4.js";const _=p("Click to open the Message Box"),f=s({setup(m){const t=()=>{c.alert("This is a message","Title",{confirmButtonText:"OK",callback:e=>{l({type:"info",message:`action: ${e}`})}})};return(e,u)=>{const o=i("el-button");return r(),a(o,{text:"",onClick:t},{default:n(()=>[_]),_:1})}}});export{f as default};
-1
View File
@@ -1 +0,0 @@
import{_ as e,f as o,o as _}from"./index.e20aada4.js";const a={},n={class:"dialog-footer"};function r(c,s){return _(),o("span",n," \u5F53\u4F60\u770B\u5230\u4E86\uFF0C\u8FD9\u4E2A\u9875\u9762\uFF0C\u8FD9\u8868\u793A\u4E86\uFF0C\u4F60\u901A\u8FC7\u767E\u5EA6\u626B\u63CF\u767B\u9646\u4E86\u6211\u7684\u79D8\u5BC6\u57FA\u5730\uFF0C\u4F46\u662F\u5F88\u9057\u61BE\u7684\u544A\u8BC9\u4F60\uFF0C\u6211\u4EEC\u5C06\u4EE5\u975E\u6CD5\u767B\u5F55\u7684\u65B9\u5F0F\u5904\u7406\u4F60\u7684\u8BF7\u6C42\uFF0C88 ")}var d=e(a,[["render",r]]);export{d as default};
-1
View File
File diff suppressed because one or more lines are too long
Binary file not shown.
-4
View File
File diff suppressed because one or more lines are too long
Binary file not shown.
-60
View File
File diff suppressed because one or more lines are too long
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
.blog_list{display:flex;flex-direction:column;border-bottom:1px solid rgba(151,151,151,.3);padding-top:10px;padding-bottom:10px}.page_style{margin-top:25px;display:flex;flex-direction:column;align-items:center}
-1
View File
@@ -1 +0,0 @@
.el-table--enable-row-hover .el-table__body tr:hover>td{background-color:transparent!important}
-1
View File
@@ -1 +0,0 @@
.dashboard{background-color:beige}.iconStyle{margin-right:"6px"}.operate,.descriptionsclass{display:flex;align-items:center}
-1
View File
@@ -1 +0,0 @@
import{d as q,c as z,b as m,l as P,q as X,a1 as p,U as o,f as C,g as D,h as e,w as n,u as d,F as B,e as s,o as b,x as A,B as E,$ as G,a0 as H,Y as J}from"./index.e20aada4.js";import{s as K,g as O,a as Q,c as T}from"./strategy.41e66528.js";import{t as W}from"./number.46c8fb59.js";const ee={class:"demo-collapse"},le={style:{cursor:"pointer"}},te=D("label",null," concel subscribe",-1),ue=q({setup(oe){const g=J(),_=K(),w=z([]),v=m(),y=m(),x=m(0),k=m(["1"]),f=m(""),I=()=>{!v.value||Q(v.value).then(l=>{p({message:o("p",null,[o("i",{style:"color: green"},"\u884C\u60C5\u8BA2\u9605\u6210\u529F")])}),_.select_subscribe_vt_symbols(),h()}).catch(l=>{p({message:o("p",null,[o("i",{style:"color: red"},"\u884C\u60C5\u8BA2\u9605\u5931\u8D25")])})})},M=()=>{!f.value||T(f.value).then(l=>{p({message:o("p",null,[o("i",{style:"color: green"},"\u53D6\u6D88\u8BA2\u9605\u6210\u529F")])}),h()}).catch(l=>{p({message:o("p",null,[o("i",{style:"color: red"},"\u53D6\u6D88\u8BA2\u9605\u5931\u8D25")])})})},U=(l,t)=>{let a=(l.last_price-l.open_price)/l.open_price;return W(a)},N=(l,t,a,i)=>{f.value=l.symbol+"."+l.exchange,console.log(f.value)},S=(l,t,a,i)=>{},L=(l,t,a)=>{var r;let i=(r=g==null?void 0:g.proxy)==null?void 0:r.$refs.contextmenu;a.preventDefault(),i.show({top:a.clientY,left:a.clientX}),window.onclick=()=>{i.hide()}},h=()=>(O().then(l=>{w.length=0,l.data.ticks.forEach(t=>{w.push(t)})}),h);return P(()=>{clearInterval(x.value),x.value=window.setInterval(h(),3e3)}),X(()=>{window.clearInterval(x.value)}),_.select_subscribe_vt_symbols().catch(l=>{p({message:o("p",null,[o("i",{style:"color: teal"},"\u8BFB\u53D6vt_symbols\u5931\u8D25")])})}),_.select_vt_symbols().catch(l=>{p({message:o("p",null,[o("i",{style:"color: teal"},"\u8BFB\u53D6vt_symbols\u5931\u8D25")])})}),_.select_exchanges().catch(l=>{p({message:o("p",null,[o("i",{style:"color: teal"},"\u8BFB\u53D6exchanges\u5931\u8D25")])})}),(l,t)=>{const a=s("el-option"),i=s("el-select"),r=s("el-col"),R=s("ZoomIn"),V=s("el-icon"),$=s("el-row"),F=s("el-collapse-item"),u=s("el-table-column"),Y=s("el-table"),Z=s("el-collapse"),j=s("CircleClose");return b(),C(B,null,[D("div",ee,[e(Z,{modelValue:k.value,"onUpdate:modelValue":t[3]||(t[3]=c=>k.value=c)},{default:n(()=>[e(F,{title:"\u8BA2\u9605\u884C\u60C5",name:"2"},{default:n(()=>[e($,null,{default:n(()=>[e(r,{span:4},{default:n(()=>[e(i,{modelValue:y.value,"onUpdate:modelValue":t[0]||(t[0]=c=>y.value=c),placeholder:"select a exchange"},{default:n(()=>[(b(!0),C(B,null,A(d(_).strategy.exchanges,c=>(b(),E(a,{value:c},null,8,["value"]))),256))]),_:1},8,["modelValue"])]),_:1}),e(r,{span:1}),e(r,{span:4},{default:n(()=>[e(i,{modelValue:v.value,"onUpdate:modelValue":t[1]||(t[1]=c=>v.value=c),placeholder:"select a vt_symbol"},{default:n(()=>[(b(!0),C(B,null,A(d(_).strategy.vt_symbols.get(y.value),c=>(b(),E(a,{value:c},null,8,["value"]))),256))]),_:1},8,["modelValue"])]),_:1}),e(r,{span:1}),e(r,{span:4},{default:n(()=>[D("span",le,[e(V,{size:25,onClick:t[2]||(t[2]=c=>I())},{default:n(()=>[e(R)]),_:1})])]),_:1})]),_:1})]),_:1}),e(F,{title:"\u8BA2\u9605\u4E00\u89C8",name:"1"},{default:n(()=>[e(Y,{onRowContextmenu:L,onCellMouseEnter:N,onCellMouseLeave:S,data:d(w),style:{width:"100%"}},{default:n(()=>[e(u,{fixed:"",prop:"symbol",label:"symbol",width:"80"}),e(u,{prop:"exchange",label:"exchange",width:"100"}),e(u,{prop:"name",label:"Name",width:"120"}),e(u,{prop:"last_price",label:"last_price",width:"120"}),e(u,{prop:"volume",label:"volume",width:"100"}),e(u,{prop:"open_price",label:"open_price",width:"120"}),e(u,{prop:"high_price",label:"high_price",width:"120"}),e(u,{prop:"low_price",label:"low_price",width:"100"}),e(u,{prop:"differ",label:"differ",width:"70"}),e(u,{prop:"differ_percent",label:"differ_per",formatter:U,width:"120"})]),_:1},8,["data"])]),_:1})]),_:1},8,["modelValue"])]),e(d(H),{"auto-ajust-placement":"",ref:"contextmenu"},{default:n(()=>[e(d(G),{onClick:M},{default:n(()=>[e(V,{class:"contextmenu-icon"},{default:n(()=>[e(j)]),_:1}),te]),_:1})]),_:1},512)],64)}}});export{ue as default};
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
import{H as M,d as z,c as b,b as f,f as L,h as l,w as a,u as r,X as N,F as V,Y as $,Z as g,e as y,o as i,B as p,$ as m,E as h,a0 as j,g as k}from"./index.e20aada4.js";const O={"Content-Type":"application/json;charsetset=UTF-8"},U=w=>M({url:"/cloud/api/v1/file/list",method:"GET",params:{url:w},headers:O});const X=k("label",null," _new folder",-1),Y=k("label",null," _copy",-1),A=k("label",null," _rename",-1),G=k("label",null," _delete",-1),H=k("label",null," _delete",-1),q=z({setup(w){const u=$(),R=b([]),n=b(new Set),C=b({isCtrl:!1,isShift:!1}),E=f(),x=f(),d=f(!0);f();const v=f(!1),F=(e,o,t,s)=>{n.has(e)||(x.value=e)},B=(e,o,t,s)=>{n.has(e)||(x.value=void 0)},S=(e,o,t)=>{C.isCtrl?n.has(e)?n.delete(e):n.add(e):(n.clear(),n.add(e))},T=(e,o,t)=>{var c;v.value=!0;let s=(c=u==null?void 0:u.proxy)==null?void 0:c.$refs.contextmenu;n.has(e)||(n.clear(),n.add(e)),t.preventDefault(),s.show({top:t.clientY,left:t.clientX}),window.onclick=()=>{s.hide(),v.value=!1}},_=()=>{},D=({row:e,rowIndex:o})=>{var t;if(n.has(e))return{background:"#B4C7E7"};if(((t=g(x.value))==null?void 0:t.name)==g(e).name)return{background:"#DAE3F3"}};return document.onkeydown=e=>{switch(e.key){case"Control":C.isCtrl=!0;break}},document.onkeyup=e=>{var o;switch(e.key){case"Control":C.isCtrl=!1;break;case"Escape":v.value?(((o=u==null?void 0:u.proxy)==null?void 0:o.$refs.contextmenu).hide(),v.value=!1):n.clear(),C.isCtrl=!0;break}},U("").then(e=>{e.data.forEach(o=>{const t={size:o.size,isDir:o.isDir,name:o.server_filename,path:o.path};R.push(t)})}),(e,o)=>{const t=y("el-table-column"),s=y("FolderOpened"),c=y("el-icon");return i(),L(V,null,[l(r(N),{data:r(R),onCellMouseEnter:F,onCellMouseLeave:B,"row-style":D,onRowContextmenu:T,onRowClick:S,ref_key:"multipleTableRef",ref:E},{default:a(()=>[l(t,{prop:"name",width:"200px"}),l(t,{prop:"isDir",width:"130px"}),l(t,{prop:"size",width:"130px"}),l(t,{prop:"path",width:"130px"})]),_:1},8,["data"]),l(r(j),{"auto-ajust-placement":"",ref:"contextmenu"},{default:a(()=>[d.value?(i(),p(r(m),{key:0,onClick:_},{default:a(()=>[l(c,{class:"contextmenu-icon"},{default:a(()=>[l(s)]),_:1}),X]),_:1})):h("",!0),d.value?(i(),p(r(m),{key:1,onClick:_},{default:a(()=>[l(c,{class:"contextmenu-icon"},{default:a(()=>[l(s)]),_:1}),Y]),_:1})):h("",!0),d.value?(i(),p(r(m),{key:2,onClick:_},{default:a(()=>[l(c,{class:"contextmenu-icon"},{default:a(()=>[l(s)]),_:1}),A]),_:1})):h("",!0),d.value?(i(),p(r(m),{key:3,onClick:_},{default:a(()=>[l(c,{class:"contextmenu-icon"},{default:a(()=>[l(s)]),_:1}),G]),_:1})):h("",!0),d.value?(i(),p(r(m),{key:4,onClick:_},{default:a(()=>[l(c,{class:"contextmenu-icon"},{default:a(()=>[l(s)]),_:1}),H]),_:1})):h("",!0)]),_:1},512)],64)}}});export{q as default};
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
.date-cell-holiday[data-v-0e3dfbc2]{background:#dfd}.date-cell-event[data-v-0e3dfbc2]{background:yellow}
-1
View File
File diff suppressed because one or more lines are too long
Binary file not shown.
-61
View File
File diff suppressed because one or more lines are too long
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
import{g as x,a as j,p as q,b as A}from"./blogView.3f9577de.js";import{p as P}from"./profile.50110a29.js";import{d as T,c as h,f as m,h as e,w as l,u as a,F as y,x as k,g as i,r as C,e as u,o as r,B as W,z as B,i as c}from"./index.e20aada4.js";import"./index.7b5a1be8.js";const G=i("span",null,"\u5206\u7C7B\u641C\u7D22",-1),H=i("span",{class:"text-gray-500"},"-",-1),J=c("\u641C\u7D22"),K=c("\u521B\u4F5C"),M=c("\u91CD\u65B0\u53D1\u5E03Wiki"),O=c("\u91CD\u65B0\u53D1\u5E03profile"),Q={class:"blog_list"},R=["onClick"],X={style:{display:"flex","justify-content":"space-between","align-items":"center"}},Y={style:{"font-size":"12px",height:"16px","line-height":"16px",overflow:"hidden","text-overflow":"ellipsis"}},Z=c("\u91CD\u65B0\u53D1\u5E03 "),ee={class:"page_style"},ae=T({setup(te){const s=h({key_word:"",btId:"",start_day:"",end_day:"",page_number:1,page_size:5,total:1}),p=h([]),f=h([]),w=()=>{x(s).then(n=>{p.splice(0,p.length),n.data.forEach(t=>{p.push(t)})})},v=n=>{const{href:t}=C.resolve({path:"/view",query:{bid:n}});window.open(t,"_blank")},V=()=>{const{href:n}=C.resolve({path:"/write/"});window.open(n,"_blank")},E=()=>{q()},F=()=>{P()},D=n=>{A(n)};return x(s).then(n=>{n.data.forEach(t=>{p.push(t)})}),j().then(n=>{f.push({btId:"",btName:"\u9009\u62E9\u5206\u7C7B"}),n.data.forEach(t=>{const b={btId:t.btId,btName:t.btName};f.push(b)})}),(n,t)=>{const b=u("el-input"),d=u("el-col"),N=u("el-option"),I=u("el-select"),g=u("el-form-item"),z=u("el-date-picker"),U=u("el-time-picker"),_=u("el-button"),L=u("el-form"),S=u("el-pagination");return r(),m(y,null,[e(L,{model:a(s),"label-width":"120px"},{default:l(()=>[e(g,{label:"\u5173\u952E\u5B57\u641C\u7D22"},{default:l(()=>[e(d,{span:7},{default:l(()=>[e(b,{modelValue:a(s).key_word,"onUpdate:modelValue":t[0]||(t[0]=o=>a(s).key_word=o)},null,8,["modelValue"])]),_:1}),e(d,{span:1}),e(d,{span:10},{default:l(()=>[G,e(I,{modelValue:a(s).btId,"onUpdate:modelValue":t[1]||(t[1]=o=>a(s).btId=o),placeholder:"\u9009\u62E9\u5206\u7C7B"},{default:l(()=>[(r(!0),m(y,null,k(a(f),o=>(r(),W(N,{key:o.btId,label:o.btName,value:o.btId},null,8,["label","value"]))),128))]),_:1},8,["modelValue"])]),_:1})]),_:1}),e(g,{label:"\u521B\u4F5C\u65F6\u95F4"},{default:l(()=>[e(d,{span:7},{default:l(()=>[e(z,{modelValue:a(s).start_day,"onUpdate:modelValue":t[2]||(t[2]=o=>a(s).start_day=o),type:"date",placeholder:"\u5F00\u59CB\u65F6\u95F4",style:{width:"100%"}},null,8,["modelValue"])]),_:1}),e(d,{span:2,class:"text-center"},{default:l(()=>[H]),_:1}),e(d,{span:7},{default:l(()=>[e(U,{modelValue:a(s).end_day,"onUpdate:modelValue":t[3]||(t[3]=o=>a(s).end_day=o),placeholder:"\u7ED3\u675F\u65F6\u95F4",style:{width:"100%"}},null,8,["modelValue"])]),_:1}),e(d,{span:6,style:{"text-align":"center"}},{default:l(()=>[e(_,{type:"primary",onClick:w},{default:l(()=>[J]),_:1})]),_:1})]),_:1})]),_:1},8,["model"]),e(_,{onClick:V},{default:l(()=>[K]),_:1}),e(_,{onClick:E},{default:l(()=>[M]),_:1}),e(_,{onClick:F},{default:l(()=>[O]),_:1}),(r(!0),m(y,null,k(a(p),o=>(r(),m("div",Q,[i("h3",{style:{cursor:"pointer",width:"min-content","white-space":"nowrap"},onClick:$=>v(o.bid)},B(o.title),9,R),i("div",X,[i("p",Y,B(o.content),1),e(_,{size:"small",type:"success",onClick:$=>D(o.bid)},{default:l(()=>[Z]),_:2},1032,["onClick"])])]))),256)),i("div",ee,[e(S,{small:"",background:"",layout:"prev, pager, next",total:50,class:"mt-4"})])],64)}}});export{ae as default};
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
.el-button--text[data-v-84f682ae]{margin-right:15px}.el-select[data-v-84f682ae],.el-input[data-v-84f682ae]{width:300px}.dialog-footer button[data-v-84f682ae]:first-child{margin-right:10px}.el-button--text[data-v-25eee70b]{margin-right:15px}.el-select[data-v-25eee70b],.el-input[data-v-25eee70b]{width:300px}.dialog-footer button[data-v-25eee70b]:first-child{margin-right:10px}.dashboard{background-color:beige}.tag_list[data-v-85509c2e]{display:flex;border-top:1px solid rgba(151,151,151,.3);border-bottom:1px solid rgba(151,151,151,.3)}.click-icon[data-v-85509c2e]{width:40px;margin:10px;cursor:pointer}.create_dialog[data-v-85509c2e]{margin:10px}
-1
View File
@@ -1 +0,0 @@
.dashboard{background-color:beige}
File diff suppressed because one or more lines are too long
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
.section-main{height:100%;padding:14px}.headerAvatar[data-v-db52df48]{display:flex;justify-content:center;align-items:center;margin-right:8px}.file[data-v-db52df48]{width:80px;height:80px;position:relative}.right-box[data-v-47d8c705]{padding-top:16px;display:flex;justify-content:flex-end;align-items:center}.right-box img[data-v-47d8c705]{vertical-align:middle;border:1px solid #ccc;border-radius:6px}.item[data-v-47d8c705]{margin-top:5px;margin-right:30px}.header-avatar[data-v-47d8c705]{display:flex;justify-content:center;align-items:center}.side_Style[data-v-52f0823c]{width:100px}.side-main[data-v-52f0823c]{width:200px}.gva-menu-item[data-v-52f0823c]{color:#fff}.el-menu-item.is-active[data-v-52f0823c]{color:var(--ddab178a);background:var(--ddab178a)}.el-header[data-v-4dcee88b]{border-bottom-style:solid;border-bottom-width:.5px;border-bottom-color:#9797974d}.el-main[data-v-4dcee88b]{padding:16px;overflow:hidden;height:100%}.el-aside[data-v-4dcee88b]{margin-left:-10px;background:#191a23;position:fixed}.el-footer[data-v-4dcee88b]{margin-top:20px;display:flex;justify-content:space-around}.homeWrap[data-v-4dcee88b]{position:absolute;top:0;height:100%;width:100%}
Binary file not shown.
-1
View File
File diff suppressed because one or more lines are too long
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
.text[data-v-623cde64]{font-size:14px}.item[data-v-623cde64]{padding:18px 0}.ttt[data-v-623cde64]{display:flex;flex-direction:column}.yuliu[data-v-623cde64]{height:300px;float:left;background:url(/assets/have_a_nice_day.4bde267a.jpeg) center top no-repeat;background-size:cover}
-1
View File
@@ -1 +0,0 @@
import{E as u}from"./style.1aad9498.js";import{d as i,b as s,f as m,g as d,z as p,u as a,h as c,R as f,F as g,a8 as V,o as B}from"./index.e20aada4.js";import{c as _}from"./blogView.3f9577de.js";import"./index.7b5a1be8.js";const k=i({setup(v){let e=s(""),o=s("");const n=V().query;return(()=>{_(n.bid).then(t=>{e.value=t.data.content,o.value=t.data.title})})(),(t,l)=>(B(),m(g,null,[d("h2",null,p(a(o)),1),c(a(u),{modelValue:a(e),"onUpdate:modelValue":l[0]||(l[0]=r=>f(e)?e.value=r:e=r),previewOnly:""},null,8,["modelValue"])],64))}});export{k as default};
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

-1
View File
@@ -1 +0,0 @@
const r=t=>(t*100).toFixed(2)+"%",e=t=>t>10?t:"0"+t;export{e as a,r as t};
-1
View File
@@ -1 +0,0 @@
import{H as r}from"./index.e20aada4.js";const o={"Content-Type":"application/json;charsetset=UTF-8"},t=e=>r({url:"/wiki/api/v1/author/profile",method:"POST",data:e}),a=e=>r({url:"/wiki/api/v1/author/profile",method:"PUT",data:e}),p=()=>r({url:"/wiki/api/v1/author/profile/publish",method:"PUT"}),u=()=>r({url:"/wiki/api/v1/author/profile",method:"GET",headers:o}),l=()=>r({url:"/wiki/api/v1/author/profile/name",method:"GET",headers:o}),s=e=>r({url:"/wiki/api/v1/author/profile/judge",method:"POST",data:e});export{u as a,t as c,l as g,s as j,p,a as u};
-1
View File
@@ -1 +0,0 @@
import{d as f,c as b,f as V,h as e,w as l,u as o,F as C,i as m,g as B,a8 as E,e as r,o as F,bY as k,s as h,a as A,r as g}from"./index.e20aada4.js";const w=m(" \u8A72\u5F53\u30B7\u30B9\u30C6\u30E0\u306E\u3054\u5229\u7528\u306F\u30B7\u30B9\u30C6\u30E0\u7BA1\u7406\u8005\u306B\u8A31\u53EF\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002"),x=B("br",null,null,-1),U=m(" \u8A31\u53EF\u304C\u306A\u3051\u308C\u3070\u3001\u30E6\u30FC\u30B6\u30FC\u60C5\u5831\u3092\u767B\u9332\u3057\u3066\u3082\u3001\u30B7\u30B9\u30C6\u30E0\u306E\u3054\u5229\u7528\u306F\u3067\u304D\u307E\u305B\u3093\u306E\u3067\u3001\u3054\u6CE8\u610F\u304F\u3060\u3055\u3044\u3002 "),v=m("Register"),y=m("Cancel"),R=f({setup(I){const d=E().query,u=b({phone_number:"",code:"",tenant_id:d.tenantId,account:d.account,token:d.token}),i=()=>{k(u).then(_=>{if(_.status==200){const t={id:d.tenantId,account:d.account};h(d.token),A(t);const{href:a}=g.resolve({path:"/"});window.open(a,"_self")}})};return(_,t)=>{const a=r("el-input"),s=r("el-form-item"),c=r("el-button"),p=r("el-form");return F(),V(C,null,[w,x,U,e(p,{model:o(u),"label-width":"120px"},{default:l(()=>[e(s,{label:"phone_number"},{default:l(()=>[e(a,{modelValue:o(u).phone_number,"onUpdate:modelValue":t[0]||(t[0]=n=>o(u).phone_number=n)},null,8,["modelValue"])]),_:1}),e(s,{label:"auth_code"},{default:l(()=>[e(a,{modelValue:o(u).code,"onUpdate:modelValue":t[1]||(t[1]=n=>o(u).code=n)},null,8,["modelValue"])]),_:1}),e(s,{label:"tenantId"},{default:l(()=>[e(a,{modelValue:o(u).tenant_id,"onUpdate:modelValue":t[2]||(t[2]=n=>o(u).tenant_id=n),disabled:!0},null,8,["modelValue"])]),_:1}),e(s,{label:"account"},{default:l(()=>[e(a,{modelValue:o(u).account,"onUpdate:modelValue":t[3]||(t[3]=n=>o(u).account=n),disabled:!0},null,8,["modelValue"])]),_:1}),e(s,{label:"token"},{default:l(()=>[e(a,{modelValue:o(u).token,"onUpdate:modelValue":t[4]||(t[4]=n=>o(u).token=n),disabled:!0},null,8,["modelValue"])]),_:1}),e(s,null,{default:l(()=>[e(c,{type:"primary",onClick:i},{default:l(()=>[v]),_:1}),e(c,null,{default:l(()=>[y]),_:1})]),_:1})]),_:1},8,["model"])],64)}}});export{R as default};
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
import{I as n,b as r,J as s}from"./index.e20aada4.js";const d=n("settings",()=>{const e=r({sideMode:"#191a23",isMobile:!1,isRouterAlive:!0,isLoading:!1,profileName:null}),i=s(()=>e.value.sideMode),o=s(()=>e.value.isMobile),t=s(()=>e.value.isRouterAlive),a=s(()=>e.value.isLoading),l=s(()=>e.value.profileName);return{settings:e,sideMode:i,isMobile:o,isRouterAlive:t,isLoading:a,profileName:l}});export{d as s};
-1
View File
@@ -1 +0,0 @@
import{_ as e,f as r,o as c}from"./index.e20aada4.js";const o={},s={class:"yuliu"};function t(_,a){return c(),r("div",s," \u8FD9\u4E2A\u4EBA\u5F88\u61D2\uFF0C\u4EC0\u4E48\u4E5F\u6CA1\u5199 ")}var f=e(o,[["render",t]]);export{f as default};
-1
View File
@@ -1 +0,0 @@
import{H as e,I as c,c as a}from"./index.e20aada4.js";const v=()=>e({url:"/trader/api/v1/accounts",method:"GET"}),d=s=>e({url:"/trader/api/v1/tick/"+s,method:"GET"}),y=()=>e({url:"/trader/api/v1/ticks",method:"GET"}),h=s=>e({url:"/trader/api/v1/order",method:"POST",data:s}),g=s=>e({url:"/trader/api/v1/subscribe/"+s,method:"GET"}),o=()=>e({url:"/trader/api/v1/subscribe/vt_symbols",method:"GET"}),p=s=>e({url:"/trader/api/v1/subscribe/"+s,method:"DELETE"}),n=()=>e({url:"/trader/api/v1/vt_symbols",method:"GET"}),b=()=>e({url:"/trader/api/v1/exchanges",method:"GET"}),E=c("strategy",()=>{const s=a({class_names:[],vt_symbols:new Map,subscribe_vt_symbols:[],exchanges:[]});return{strategy:s,select_exchanges:async()=>{s.exchanges.length==0&&b().then(t=>{s.exchanges=t.data.exchanges})},select_vt_symbols:async()=>{s.vt_symbols.size==0&&n().then(t=>{Object.keys(t.data.vt_symbols).forEach(r=>{s.vt_symbols.set(r,t.data.vt_symbols[r])})})},select_subscribe_vt_symbols:async()=>{s.subscribe_vt_symbols.length==0&&o().then(t=>{s.subscribe_vt_symbols=t.data.subscribe_vt_symbols})}}});export{g as a,v as b,p as c,d,h as e,y as g,E as s};
Binary file not shown.
-84
View File
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
-6
View File
@@ -1,6 +0,0 @@
import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer';
import tenant from './tenant';
export function setupMock() {
createProdMockServer([...tenant]);
}
+4065 -3121
View File
File diff suppressed because it is too large Load Diff
+27 -37
View File
@@ -1,54 +1,44 @@
{ {
"name": "violin-home", "name": "violin-home",
"private": true, "private": true,
"version": "1.0.0", "version": "2.0.0",
"scripts": { "scripts": {
"dev": "vite --host", "dev": "vite --host",
"build": "vue-tsc --noEmit && vite build", "build": "vue-tsc --noEmit && vite build",
"development": "vite build --mode development", "build:test": "vue-tsc --noEmit && vite build --mode test",
"test": "vite --mode test", "build:dev": "vue-tsc --noEmit && vite build --mode development",
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@element-plus/icons-vue": "^1.1.4", "@element-plus/icons-vue": "^2.3.1",
"@types/node": "^17.0.23", "axios": "^1.7.7",
"aplayer": "^1.10.1", "clipboard": "^2.0.11",
"axios": "^0.26.1", "copy-to-clipboard": "^3.3.3",
"clipboard": "^2.0.10", "dayjs": "^1.11.13",
"copy-to-clipboard": "^3.3.1", "echarts": "^5.5.1",
"echarts": "^5.3.2", "element-plus": "^2.8.4",
"element-plus": "^2.1.7", "js-cookie": "^3.0.5",
"js-cookie": "^3.0.1",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"lodash-unified": "^1.0.2", "md-editor-v3": "^4.20.1",
"md-editor-v3": "^1.11.6", "pinia": "^2.2.4",
"moment": "^2.29.4", "qs": "^6.13.0",
"node-sass": "^8.0.0", "v-contextmenu": "^3.2.0",
"pinia": "^2.0.13", "vue": "^3.5.12",
"qs": "^6.10.3", "vue-router": "^4.4.5",
"sass": "^1.57.1",
"sass-loader": "^13.2.0",
"save": "^2.4.0",
"scss": "^0.2.4",
"style-loader": "^3.3.1",
"type-fest": "^2.12.2",
"vue": "^3.2.25",
"vue-router": "^4.0.14",
"vue-runtime-helpers": "^1.1.2",
"vuedraggable": "^4.1.0" "vuedraggable": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@types/js-cookie": "^3.0.1", "@types/js-cookie": "^3.0.6",
"@types/lodash-es": "^4.17.6", "@types/lodash-es": "^4.17.12",
"@types/qs": "^6.9.7", "@types/node": "^22.7.5",
"@vitejs/plugin-vue": "^2.3.0", "@types/qs": "^6.9.15",
"@vitejs/plugin-vue": "^5.1.4",
"mockjs": "^1.1.0", "mockjs": "^1.1.0",
"typescript": "^4.5.4", "sass": "^1.79.4",
"v-contextmenu": "^3.0.0", "typescript": "~5.6.2",
"vite": "^2.9.13", "vite": "^5.4.8",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-mock": "^2.9.6", "vite-plugin-mock": "^3.0.2",
"vue-tsc": "^0.29.8" "vue-tsc": "^2.1.6"
} }
} }
+7 -8
View File
@@ -1,11 +1,11 @@
<template> <template>
<span class="headerAvatar"> <span class="headerAvatar">
<template v-if="picType === 'avatar'"> <template v-if="picType === 'avatar'">
<el-avatar v-if="useTenantStore.tenant.headerImg" :size="30" :src="avatar" /> <el-avatar v-if="useCustomerStore.customer.headerImg" :size="30" :src="avatar" />
<el-avatar v-else :size="30" :src="noAvatar" /> <el-avatar v-else :size="30" :src="noAvatar" />
</template> </template>
<template v-if="picType === 'img'"> <template v-if="picType === 'img'">
<img v-if="useTenantStore.tenant.headerImg" :src="avatar" class="avatar"> <img v-if="useCustomerStore.customer.headerImg" :src="avatar" class="avatar">
<img v-else :src="noAvatar" class="avatar"> <img v-else :src="noAvatar" class="avatar">
</template> </template>
<template v-if="picType === 'file'"> <template v-if="picType === 'file'">
@@ -16,7 +16,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import { tenantStore } from '@/store/modules/tenant' import { customerStore } from '@/store/modules/customer'
import noAvatarPng from '@/assets/nobody.jpg' import noAvatarPng from '@/assets/nobody.jpg'
const props = defineProps({ const props = defineProps({
@@ -33,13 +33,13 @@ const props = defineProps({
}) })
const path = ref(import.meta.env.VITE_BASE_API + '/') const path = ref(import.meta.env.VITE_BASE_API + '/')
const noAvatar = ref(noAvatarPng) const noAvatar = ref(noAvatarPng)
const useTenantStore = tenantStore() const useCustomerStore = customerStore()
const avatar = computed(() => { const avatar = computed(() => {
if (props.picSrc === '') { if (props.picSrc === '') {
if (useTenantStore.tenant.headerImg !== '' && useTenantStore.tenant.headerImg.slice(0, 4) === 'http') { if (useCustomerStore.customer.headerImg !== '' && useCustomerStore.customer.headerImg.slice(0, 4) === 'http') {
return useTenantStore.tenant.headerImg return useCustomerStore.customer.headerImg
} }
return path.value + useTenantStore.tenant.headerImg return path.value + useCustomerStore.customer.headerImg
} else { } else {
if (props.picSrc !== '' && props.picSrc.slice(0, 4) === 'http') { if (props.picSrc !== '' && props.picSrc.slice(0, 4) === 'http') {
return props.picSrc return props.picSrc
@@ -61,7 +61,6 @@ const file = computed(() => {
align-items: center; align-items: center;
margin-right: 8px; margin-right: 8px;
} }
.file { .file {
width: 80px; width: 80px;
height: 80px; height: 80px;
+7 -7
View File
@@ -56,7 +56,7 @@
<span class="header-avatar" style="cursor: pointer"> <span class="header-avatar" style="cursor: pointer">
<CustomPic /> <CustomPic />
<span v-show="!useSettingsStore.isMobile" style="margin-left: 5px">{{ <span v-show="!useSettingsStore.isMobile" style="margin-left: 5px">{{
useTenantStore.tenant.account useCustomerStore.customer.account
}}</span> }}</span>
<el-icon> <el-icon>
<arrow-down /> <arrow-down />
@@ -67,10 +67,10 @@
<el-dropdown-menu class="dropdown-group"> <el-dropdown-menu class="dropdown-group">
<el-dropdown-item> <el-dropdown-item>
<span style="font-weight: 600;"> <span style="font-weight: 600;">
当前角色{{ useTenantStore.tenant.account }} 当前角色{{ useCustomerStore.customer.account }}
</span> </span>
</el-dropdown-item> </el-dropdown-item>
<template v-if="useTenantStore.tenant.account"> <template v-if="useCustomerStore.customer.account">
<!-- <el-dropdown-item v-for="item in userStore.userInfo.authorities.filter(i=>i.authorityId!==userStore.userInfo.authorityId)" :key="item.authorityId" @click="changeUserAuth(item.authorityId)"> <!-- <el-dropdown-item v-for="item in userStore.userInfo.authorities.filter(i=>i.authorityId!==userStore.userInfo.authorityId)" :key="item.authorityId" @click="changeUserAuth(item.authorityId)">
<span> <span>
切换为{{ item.authorityName }} 切换为{{ item.authorityName }}
@@ -78,7 +78,7 @@
</el-dropdown-item> --> </el-dropdown-item> -->
</template> </template>
<el-dropdown-item icon="avatar" @click="toPerson">个人信息</el-dropdown-item> <el-dropdown-item icon="avatar" @click="toPerson">个人信息</el-dropdown-item>
<el-dropdown-item icon="reading-lamp" @click="useTenantStore.logout"> </el-dropdown-item> <el-dropdown-item icon="reading-lamp" @click="useCustomerStore.logout"> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>
@@ -89,13 +89,13 @@
<script setup lang='ts'> <script setup lang='ts'>
import CustomPic from "@/components/customPic/index.vue"; import CustomPic from "@/components/customPic/index.vue";
import { tenantStore } from '@/store/modules/tenant' import { customerStore } from '@/store/modules/customer'
import { settingsStore } from '@/store/modules/settings' import { settingsStore } from '@/store/modules/settings'
import { Message } from "@element-plus/icons-vue" import { Message } from "@element-plus/icons-vue"
import { reactive, ref } from "vue"; import { reactive, ref } from "vue";
// -- IMPORT -- // -- IMPORT --
const useTenantStore = tenantStore() const useCustomerStore = customerStore()
const useSettingsStore = settingsStore() const useSettingsStore = settingsStore()
// -- REACTIVE OBJECT -- // -- REACTIVE OBJECT --
@@ -131,7 +131,7 @@ const toPerson = () => {
} }
useTenantStore.reflush() useCustomerStore.reflush()
</script> </script>
+17 -57
View File
@@ -4,17 +4,13 @@
</div> </div>
<el-card class="box-card"> <el-card class="box-card">
<el-form ref="ruleFormRef" :model="loginForm" :rules="loginRules" status-icon label-width="120px" <el-form>
class="demo-ruleForm">
<div class="title-container"> <div class="title-container">
<h3>秘密基地</h3> <h3>秘密基地</h3>
</div> </div>
<div> <div>
<el-avatar :size="28" :src="baiduCloudImage" @click="scan(qrcode)" /> <el-button type="primary" size="large" @click="handleOidcLogin">统一登录</el-button>
</div> </div>
<el-form-item>
其他登录方式暂时还没有哦
</el-form-item>
</el-form> </el-form>
------------------------------------------------------------ ------------------------------------------------------------
<div> <div>
@@ -31,61 +27,25 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { reactive, ref } from 'vue' import { reactive } from 'vue'
import type { FormInstance } from 'element-plus' import { ElMessage } from 'element-plus'
import { tenantStore } from '../../store/modules/tenant'
import router from '../../router'
import baiduCloudImage from "../../assets/baiducloud.png"
import { config, scan } from '../../const/config'
const handleOidcLogin = () => {
const issuer = import.meta.env.VITE_OIDC_ISSUER
const clientId = import.meta.env.VITE_OIDC_CLIENT_ID
const redirectUri = encodeURIComponent(import.meta.env.VITE_OIDC_REDIRECT_URI)
const scope = encodeURIComponent('openid profile email')
const state = Math.random().toString(36).substring(2)
let qrcode = config.BAIDU_CLOUD_URL + config.RESPONSE_TYPE + config.CLIENT_ID + config.REDIRECT_URI + config.SCOPE + config.DEVICE_ID + config.QR_CODE + config.DISPLAY if (!issuer || !clientId) {
ElMessage.error('OIDC 未配置,请联系管理员')
const useTenantStore = tenantStore()
const ruleFormRef = ref<FormInstance>()
const loginForm = reactive({
user_id: '',
password: ''
})
const validateUserName = (rule: any, value: string, callback: Function) => {
if (!value) {
callback(new Error("请输入的用户名"))
} else {
callback()
}
}
const validatePassword = (rule: any, value: string, callback: Function) => {
if (!value) {
callback(new Error("请输入密码"))
} else {
callback()
}
}
const loginRules = reactive({
user_id: [{ validator: validateUserName, trigger: 'blur' }],
password: [{ validator: validatePassword, trigger: 'blur' }]
})
const dologin = (form: any) => {
if (!form)
return return
// form.validate((valid: any) => { }
// if (valid) {
// useTenantStore.login(loginForm.user_id, loginForm.password).then(res => {
// console.log(1)
// router.push({
// path: '/home'
// })
// })
// } else { sessionStorage.setItem('oidc_state', state)
// return false window.location.href =
// } `${issuer}/application/o/authorize/?` +
// }) `client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code&scope=${scope}&state=${state}`
} }
const openSiteQuery = () => { const openSiteQuery = () => {
+8 -8
View File
@@ -11,8 +11,8 @@
<el-input v-model="register_form.code" /> <el-input v-model="register_form.code" />
</el-form-item> </el-form-item>
<el-form-item label="tenantId"> <el-form-item label="customerId">
<el-input v-model="register_form.tenant_id" :disabled="true" /> <el-input v-model="register_form.customerId" :disabled="true" />
</el-form-item> </el-form-item>
<el-form-item label="account"> <el-form-item label="account">
@@ -35,17 +35,17 @@ import { reactive } from 'vue'
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { register_user } from "./../../api/user"; import { register_user } from "./../../api/user";
import router from '../../router/index' import router from '../../router/index'
import { setTenant, setToken } from '../../utils/auth'; import { setCustomer, setToken } from '../../utils/auth';
const route = useRoute() const route = useRoute()
const params = route.query const params = route.query
// use phone number to register user(tenant) // use phone number to register user(customer)
const register_form = reactive({ const register_form = reactive({
phone_number: '', phone_number: '',
code: '', code: '',
tenant_id: params.tenantId as string, customerId: params.customerId as string,
account: params.account as string, account: params.account as string,
token: params.token as string token: params.token as string
}) })
@@ -54,13 +54,13 @@ const onSubmit = () => {
register_user(register_form).then((response) => { register_user(register_form).then((response) => {
if (response.status == 200) { if (response.status == 200) {
const tenant = { const customer = {
id: params.tenantId, id: params.customerId,
account: params.account account: params.account
} }
setToken(params.token as string) setToken(params.token as string)
setTenant(tenant) setCustomer(customer)
const { href } = router.resolve({ const { href } = router.resolve({
path: '/' path: '/'
+9 -9
View File
@@ -1,7 +1,7 @@
import { Tenant } from '@/entity'; import { Customer } from '@/entity';
import { tenantStore } from '@/store/modules/tenant'; import { customerStore } from '@/store/modules/customer';
import router from '../router' import router from '../router'
import { setToken, setTenant } from '../utils/auth' import { setToken, setCustomer } from '../utils/auth'
// config/index.js // config/index.js
const env = process.env.NODE_ENV!; const env = process.env.NODE_ENV!;
@@ -45,14 +45,14 @@ const scan_method: any = {
}, },
development: async () => { development: async () => {
const useTenantStore = tenantStore() const useCustomerStore = customerStore()
const tenant: Tenant = { const customer: Customer = {
tenant_id: '7788', customerId: '7788',
account: '小小的测试账户' account: '小小的测试账户'
} }
let token = 'ABCDEFG' let token = 'ABCDEFG'
await useTenantStore.login(tenant, token).then(() => { await useCustomerStore.login(customer, token).then(() => {
const { href } = router.resolve({ const { href } = router.resolve({
path: '/' path: '/'
}) })
@@ -65,9 +65,9 @@ const scan_method: any = {
}) })
setToken("token") setToken("token")
setTenant( setCustomer(
{ {
tenantId: "111" customerId: "111"
} }
) )
router.push({ router.push({
+4 -3
View File
@@ -1,5 +1,5 @@
type Tenant = { type Customer = {
tenant_id: string, customerId: string,
account: string account: string
} }
@@ -64,4 +64,5 @@ const wikiType = (data: any) => {
} as WikiType } as WikiType
} }
export { Tenant, Theme, DataTimeline, Event, Wiki, wiki, WikiType, wikiType } export type { Customer, Theme, DataTimeline, Event, Wiki, WikiType }
export { wiki, wikiType }
+11
View File
@@ -6,3 +6,14 @@ declare module '*.vue' {
const component: DefineComponent<{}, {}, any> const component: DefineComponent<{}, {}, any>
export default component export default component
} }
// 兼容 element-plus 2.8.x 与 vue-tsc 2.x 的 GlobalComponents 索引类型
// element-plus 在 .d.ts 里把 GlobalComponents 用作 Record<string, Component<...>>
// 但 vue 默认声明没提供 index signature,触发 TS2344
declare module 'vue' {
interface GlobalComponents {
[elemName: string]: any
}
}
export {}
+2 -4
View File
@@ -11,10 +11,8 @@ import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import contentmenu from 'v-contextmenu' import contentmenu from 'v-contextmenu'
import 'v-contextmenu/dist/themes/default.css' import 'v-contextmenu/dist/themes/default.css'
import { setupMock } from '../mock' // mock 数据:vite-plugin-mock 3.x 在 dev 模式下由 vite 插件自动加载,
if (process.env.NODE_ENV === 'development') { // 生产构建不带 mock(参考 AGENT.md「已知坑 #5」)
setupMock()
}
let app = createApp(App) let app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) { for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
+10 -10
View File
@@ -1,10 +1,10 @@
import router from './router' import router from './router'
import Cookies from 'js-cookie' import Cookies from 'js-cookie'
import { getToken, getTenant } from './utils/auth' import { getToken, getCustomer } from './utils/auth'
import { Tenant } from './entity/index' import { Customer } from './entity/index'
import { tenantStore } from './store/modules/tenant' import { customerStore } from './store/modules/customer'
const whiteList = ['/login', '/register', '/sorryPage', '/BlogViewer'] const whiteList = ['/login', '/register', '/sorryPage', '/BlogViewer', '/auth/callback']
let url = window.location.href let url = window.location.href
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
@@ -18,7 +18,7 @@ router.beforeEach((to, from, next) => {
next('/') next('/')
break; break;
case '/': case '/':
const tenant = <Tenant>(JSON.parse(getTenant() as string)) const customer = <Customer>(JSON.parse(getCustomer() as string))
if (url.search("write") != -1) { if (url.search("write") != -1) {
next('/BlogEditer') next('/BlogEditer')
} }
@@ -36,7 +36,7 @@ router.beforeEach((to, from, next) => {
else { else {
router.push({ router.push({
path: '/home', path: '/home',
query: tenant query: customer
}) })
} }
break; break;
@@ -71,12 +71,12 @@ router.beforeEach((to, from, next) => {
let parameter_list = parameter.split("=") let parameter_list = parameter.split("=")
query_data[parameter_list[0]] = parameter_list[1] query_data[parameter_list[0]] = parameter_list[1]
}) })
const tenant = { const customer = {
tenant_id: query_data.tenantId, customerId: query_data.customerId,
account: query_data.account account: query_data.account
} }
const tenant_store = tenantStore() const customer_store = customerStore()
tenant_store.login(tenant, query_data.token).then(() => { customer_store.login(customer, query_data.token).then(() => {
const { href } = router.resolve({ const { href } = router.resolve({
path: '/' path: '/'
}); });
+5 -1
View File
@@ -6,7 +6,7 @@ const home = () => import('@/view/dashboard/index.vue')
const routes: Array<RouteRecordRaw> = [ const routes: Array<RouteRecordRaw> = [
{ {
path: "/", path: "/",
component: () => import('@/components/layout/layout.vue'), component: () => import('@/components/layout/Layout.vue'),
children: [ children: [
{ {
path: '/home', path: '/home',
@@ -114,6 +114,10 @@ const routes: Array<RouteRecordRaw> = [
path: "/login", path: "/login",
component: login component: login
}, },
{
path: "/auth/callback",
component: () => import('@/view/auth/callback.vue')
},
{ {
path: "/illustration", path: "/illustration",
component: () => import('@/components/illustration/index.vue') component: () => import('@/components/illustration/index.vue')
+3 -3
View File
@@ -187,8 +187,8 @@ const getSign = (
openVal: number, openVal: number,
closeVal: number, closeVal: number,
closeDimIdx: number closeDimIdx: number
) => { ): number => {
var sign var sign: number
if (openVal > closeVal) { if (openVal > closeVal) {
sign = -1 sign = -1
} else if (openVal < closeVal) { } else if (openVal < closeVal) {
@@ -197,7 +197,7 @@ const getSign = (
sign = sign =
dataIndex > 0 dataIndex > 0
? // If close === open, compare with close of last record ? // If close === open, compare with close of last record
data[dataIndex - 1][closeDimIdx] <= closeVal Number(data[dataIndex - 1][closeDimIdx]) <= closeVal
? 1 ? 1
: -1 : -1
: // No record of previous, set to be positive : // No record of previous, set to be positive
+56
View File
@@ -0,0 +1,56 @@
import { defineStore, acceptHMRUpdate } from 'pinia'
import { authorize } from '../../api/user'
import { getToken, setToken, resetToken, setCustomer, getCustomer } from '../../utils/auth'
import { Customer } from '../../entity/index'
import { ref } from 'vue'
export const customerStore = defineStore('customer', () => {
const customer = ref({
account: '',
token: '',
id: '',
headerImg: 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png',
})
const login = async (customer: Customer, token: string) => {
try {
await authorize({
customerId: customer.customerId, token: token
})
setToken(token)
setCustomer(customer)
} catch {
Promise.resolve("authorize error")
}
}
const logout = async () => {
customer.value.account = ''
customer.value.token = ''
customer.value.id = ''
resetToken()
window.location.reload()
}
const reflush = async () => {
if (getToken()) {
const customerStorage = <Customer>(JSON.parse(getCustomer() as string))
const token = <string>getToken()
customer.value.account = customerStorage.account
customer.value.token = token
customer.value.id = customerStorage.customerId
}
}
return {
customer,
login,
logout,
reflush
}
})
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(customerStore, import.meta.hot))
}
-56
View File
@@ -1,56 +0,0 @@
import { defineStore, acceptHMRUpdate } from 'pinia'
import { authorize } from '../../api/user'
import { getToken, setToken, resetToken, setTenant, getTenant } from '../../utils/auth'
import { Tenant } from '../../entity/index'
import { ref } from 'vue'
export const tenantStore = defineStore('tenant', () => {
const tenant = ref({
account: '',
token: '',
id: '',
headerImg: 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png',
})
const login = async (tenant: Tenant, token: string) => {
try {
await authorize({
tenant_id: tenant.tenant_id, token: token
})
setToken(token)
setTenant(tenant)
} catch {
Promise.resolve("authorize error")
}
}
const logout = async () => {
tenant.value.account = ''
tenant.value.token = ''
tenant.value.id = ''
resetToken()
window.location.reload()
}
const reflush = async () => {
if (getToken()) {
const tenantStorage = <Tenant>(JSON.parse(getTenant() as string))
const token = <string>getToken()
tenant.value.account = tenantStorage.account
tenant.value.token = token
tenant.value.id = tenantStorage.tenant_id
}
}
return {
tenant,
login,
logout,
reflush
}
})
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(tenantStore, import.meta.hot))
}
+8 -8
View File
@@ -1,24 +1,24 @@
import Cookies from 'js-cookie' import Cookies from 'js-cookie'
const getToken = () => { const getToken = (): string | undefined => {
return Cookies.get('token') return Cookies.get('token')
} }
const setToken = (token: String) => { const setToken = (token: string) => {
Cookies.set('token', token) Cookies.set('token', token)
} }
const setTenant = (tenant: object) => { const setCustomer = (customer: object) => {
Cookies.set('tenant', JSON.stringify(tenant)) Cookies.set('customer', JSON.stringify(customer))
} }
const getTenant = () => { const getCustomer = (): string | undefined => {
return Cookies.get('tenant') return Cookies.get('customer')
} }
const resetToken = () => { const resetToken = () => {
Cookies.remove('token') Cookies.remove('token')
Cookies.remove('tenant') Cookies.remove('customer')
} }
export { getToken, setToken, resetToken, setTenant, getTenant } export { getToken, setToken, resetToken, setCustomer, getCustomer }

Some files were not shown because too many files have changed in this diff Show More