Compare commits

..

11 Commits

Author SHA1 Message Date
simple321vip 75169c70a5 refacting: delete cloud comp 2026-06-30 07:56:09 +08:00
simple321vip 024f306636 v2 refacting 2026-06-28 23:02:32 +08:00
simple321vip 4da6832b44 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 与后端风格对齐
2026-06-22 08:02:21 +08:00
simple321vip bef1906747 test 2026-06-04 21:34:58 +08:00
simple321vip f501c6041b 22 2023-07-12 21:20:21 +08:00
simple321vip 4058cf584c 1 2023-07-12 21:06:37 +08:00
simple321vip c086b91c1f Merge branch 'master' of github.com:simple321vip/violin-home 2023-04-09 23:55:40 +08:00
simple321vip b33663b2f0 update nginx.conf 2023-04-09 23:55:26 +08:00
Guan efdf92990b Merge pull request #9 from simple321vip/release-1.5
Release 1.5
2023-04-09 23:34:57 +08:00
simple321vip 873fdeffc9 wiki1.4 向けprofile機能実装 2023-04-09 23:33:25 +08:00
simple321vip 67c4658f6b ADD wiki profile function 2023-04-08 22:47:23 +08:00
126 changed files with 8178 additions and 5313 deletions
+46
View File
@@ -0,0 +1,46 @@
# ============== 环境变量模板 ==============
# 复制为 .env.development / .env.production / .env.test 后填入真实值。
# 真实值不要 commit 到 git。
# 当前模式名(dev / test / prod
VITE_MODE_NAME=development
# 资源 CDN 前缀
VITE_RES_URL=http://localhost:5173/
# 后端 API 基础地址
# ⚠️ 本地必须写前端自己(http://localhost:5173),由 Vite proxy 转发
# 写 http://localhost:8080/auth 会让浏览器绕过 proxy 直连 8080
# dev: http://localhost:5173
# dev (K8s): https://dev.violin-home.cn
# prod: https://www.violin-home.cn
VITE_APP_URL=http://localhost:5173
# 应用元信息
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:5173/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
dist-ssr
dist
*.local
# 临时文件
tmp/
.opencode/
.opencode/tmp/
# 配置文件信息不能暴露
.env.*
!.env.example
# Editor directories and files
.vscode/*
+210
View File
@@ -0,0 +1,210 @@
# 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
+1 -1
View File
@@ -1,4 +1,4 @@
FROM nginx:1.23.1-alpine
FROM ccr.ccs.tencentyun.com/violin/nginx:latest
COPY dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
+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"
-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{D as e}from"./index.9e912d9b.js";import{l as i}from"./index.26aa7e8a.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{D as r}from"./index.9e912d9b.js";import{l as t}from"./index.26aa7e8a.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.
File diff suppressed because one or more lines are too long
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
import{D as r}from"./index.9e912d9b.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};
-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 V,u as N,r as S}from"./cta_strategy.cff46c25.js";import{d as T,c as $,b as R,f as _,h as e,w as s,u as t,a0 as P,V as W,a1 as M,e as i,o as n,a2 as g,a3 as u,B as m,a4 as r,I as c,i as h,g as U,$ as j,S as k}from"./index.9e912d9b.js";const q={class:"bookmark"},G=h("\u6DFB\u52A0\u7B56\u7565\u6A21\u677F"),H=h(" -> "),J=h(" \u4E0A\u4F20\u7B56\u7565\u6A21\u677F "),K=U("div",{class:"el-upload__tip text-red"}," \u4E00\u6B21\u53EA\u80FD\u4E0A\u4F20\u4E00\u4E2A\u6587\u4EF6 ",-1),L={key:0,style:{cursor:"pointer"}},O={key:0,style:{cursor:"pointer"}},Q={key:0,style:{cursor:"pointer"}},ee=T({setup(X){const p=$([]),d=R(),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=M(),d.value.handleStart(a)},v=()=>{d.value.submit(),f()},w=l=>{l.status=2,V(l.file_name).then(a=>{a.status==200&&(l.status=1),a.status==500&&(l.status=0)})},C=l=>{N(l.class_name).then(a=>{a.status==200&&(l.status=0),a.status==500&&(l.status=1)})},b=l=>{S(l.file_name).then(a=>{f()}).catch(a=>{j({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",q,[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(()=>[G]),_:1})]),tip:s(()=>[K]),default:s(()=>[H,e(t(g),{class:"ml-3",type:"success",onClick:v},{default:s(()=>[J]),_:1})]),_:1},512),e(t(W),{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",L,[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",O,[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",Q,[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};
Binary file not shown.

Before

Width:  |  Height:  |  Size: 410 KiB

-1
View File
@@ -1 +0,0 @@
import{g as x,a as $,p as j,b as q}from"./blogView.38a54248.js";import{d as A,c as h,f as r,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 _,B as T,z as w,i as m}from"./index.9e912d9b.js";import"./index.26aa7e8a.js";const G=i("span",null,"\u5206\u7C7B\u641C\u7D22",-1),H=i("span",{class:"text-gray-500"},"-",-1),J=m("\u641C\u7D22"),K=m("\u521B\u4F5C"),M=m("\u5168\u90E8\u91CD\u65B0\u53D1\u5E03"),O={class:"blog_list"},P=["onClick"],Q={style:{display:"flex","justify-content":"space-between","align-items":"center"}},R={style:{"font-size":"12px",height:"16px","line-height":"16px",overflow:"hidden","text-overflow":"ellipsis"}},W=m("\u91CD\u65B0\u53D1\u5E03 "),X={class:"page_style"},oe=A({setup(Y){const s=h({key_word:"",btId:"",start_day:"",end_day:"",page_number:1,page_size:20,total:1}),p=h([]),b=h([]),B=()=>{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=()=>{j()},F=n=>{q(n)};return x({}).then(n=>{n.data.forEach(t=>{p.push(t)})}),$().then(n=>{b.push({btId:"",btName:"\u9009\u62E9\u5206\u7C7B"}),n.data.forEach(t=>{const f={btId:t.btId,btName:t.btName};b.push(f)})}),(n,t)=>{const f=u("el-input"),d=u("el-col"),D=u("el-option"),N=u("el-select"),g=u("el-form-item"),I=u("el-date-picker"),z=u("el-time-picker"),c=u("el-button"),U=u("el-form"),L=u("el-pagination");return _(),r(y,null,[e(U,{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(f,{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(N,{modelValue:a(s).btId,"onUpdate:modelValue":t[1]||(t[1]=o=>a(s).btId=o),placeholder:"\u9009\u62E9\u5206\u7C7B"},{default:l(()=>[(_(!0),r(y,null,k(a(b),o=>(_(),T(D,{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(I,{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(z,{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(c,{type:"primary",onClick:B},{default:l(()=>[J]),_:1})]),_:1})]),_:1})]),_:1},8,["model"]),e(c,{onClick:V},{default:l(()=>[K]),_:1}),e(c,{onClick:E},{default:l(()=>[M]),_:1}),(_(!0),r(y,null,k(a(p),o=>(_(),r("div",O,[i("h3",{style:{cursor:"pointer",width:"min-content","white-space":"nowrap"},onClick:S=>v(o.bid)},w(o.title),9,P),i("div",Q,[i("p",R,w(o.content),1),e(c,{size:"small",type:"success",onClick:S=>F(o.bid)},{default:l(()=>[W]),_:2},1032,["onClick"])])]))),256)),i("div",X,[e(L,{small:"",background:"",layout:"prev, pager, next",total:50,class:"mt-4"})])],64)}}});export{oe as default};
Binary file not shown.
-4
View File
File diff suppressed because one or more lines are too long
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
import{d as q,c as z,b as m,l as P,q as W,$ as p,S 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,Y as X,Z as G,W as H}from"./index.9e912d9b.js";import{s as J,g as K,a as O,c as Q}from"./strategy.e0f2bc38.js";import{t as T}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=H(),_=J(),w=z([]),v=m(),y=m(),x=m(0),k=m(["1"]),f=m(""),I=()=>{!v.value||O(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||Q(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")])})})},S=(l,t)=>{let a=(l.last_price-l.open_price)/l.open_price;return T(a)},N=(l,t,a,i)=>{f.value=l.symbol+"."+l.exchange,console.log(f.value)},U=(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=()=>(K().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)}),W(()=>{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"),Z=s("el-row"),F=s("el-collapse-item"),u=s("el-table-column"),$=s("el-table"),Y=s("el-collapse"),j=s("CircleClose");return b(),C(B,null,[D("div",ee,[e(Y,{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(Z,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($,{onRowContextmenu:L,onCellMouseEnter:N,onCellMouseLeave:U,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:S,width:"120"})]),_:1},8,["data"])]),_:1})]),_:1},8,["modelValue"])]),e(d(G),{"auto-ajust-placement":"",ref:"contextmenu"},{default:n(()=>[e(d(X),{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.
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
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}
-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{v as q,D as T,_ as J,d as K,c as g,b as p,l as Q,f as u,H as X,B as Y,w as a,I as L,h as l,u as o,F as S,e as n,G 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.9e912d9b.js";import{t as f}from"./date.86e91942.js";import{s as ae}from"./settings.89e47c65.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=_=>T({url:"/calendar/api/v1/event",method:"PUT",data:_}),ue=()=>T({url:"/calendar/api/v1/event",method:"GET",headers:de});const U=_=>(te("data-v-0e3dfbc2"),_=_(),le(),_),re={key:0,class:"date-cell-holiday"},ce=["onClick"],_e=U(()=>c("br",null,null,-1)),me={key:1,class:"date-cell"},pe=["onClick"],fe=["onClick"],ve={key:2,class:"date-cell-event"},he=U(()=>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"),R=n("el-table"),b=n("el-form-item"),A=n("el-input"),z=n("el-checkbox"),H=n("el-checkbox-group"),M=n("el-form"),j=n("el-button"),P=n("el-dialog"),W=Z("loading");return i(),u(S,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:I=>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:I=>C(e.date)},r(e.date.getDate()),9,pe)):(i(),u("span",{key:1,onClick:I=>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)])):L("",!0)]),_:1},8,["modelValue"])),[[W,B.value]]):L("",!0),he,l(R,{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(M,{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(H,{modelValue:F.value,"onUpdate:modelValue":t[3]||(t[3]=e=>F.value=e),min:0,max:2},{default:a(()=>[(i(),u(S,null,ee($,e=>l(z,{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{_ as e,f as o,o as _}from"./index.9e912d9b.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
@@ -1 +0,0 @@
import{d as s,B as a,w as n,a5 as c,$ as l,e as i,o as r,i as p}from"./index.9e912d9b.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 @@
.dashboard{background-color:beige}.iconStyle{margin-right:"6px"}.operate,.descriptionsclass{display:flex;align-items:center}
-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-cb159114]{background:#f0f2f5;padding:0}.page .gva-card-box[data-v-cb159114]{padding:12px 16px}.page .gva-card-box+.gva-card-box[data-v-cb159114]{padding-top:0}.page .gva-card[data-v-cb159114]{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-cb159114]{height:260px;display:flex;align-items:center;justify-content:space-between;color:#777}.page .gva-top-card-left[data-v-cb159114]{height:100%;display:flex;flex-direction:column}.page .gva-top-card-left-title[data-v-cb159114]{font-size:22px;color:#343844}.page .gva-top-card-left-dot[data-v-cb159114]{font-size:16px;color:#6b7687;margin-top:24px}.page .gva-top-card-left-rows[data-v-cb159114]{margin-top:18px;color:#6b7687;width:600px;align-items:center}.page .gva-top-card-left-item[data-v-cb159114]{margin-top:14px}.page .gva-top-card-left-item+.gva-top-card-left-item[data-v-cb159114]{margin-top:24px}.page .gva-top-card-right[data-v-cb159114]{height:600px;width:600px;margin-top:28px}.page[data-v-cb159114] .el-card__header{padding:0;border-bottom:none}.page .card-header[data-v-cb159114]{padding-bottom:20px;border-bottom:1px solid #e8e8e8}.page .quick-entrance-title[data-v-cb159114]{height:30px;font-size:22px;color:#333;width:100%;border-bottom:1px solid #eee}.page .quick-entrance-items[data-v-cb159114]{display:flex;align-items:center;justify-content:center;text-align:center;color:#333}.page .quick-entrance-items .quick-entrance-item[data-v-cb159114]{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-cb159114]:hover{box-shadow:0 0 7px #d9d9d98c}.page .quick-entrance-items .quick-entrance-item-icon[data-v-cb159114]{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-cb159114]{font-size:24px}.page .quick-entrance-items .quick-entrance-item p[data-v-cb159114]{margin-top:10px}.page .echart-box[data-v-cb159114]{padding:14px}.dashboard-icon[data-v-cb159114]{font-size:20px;color:#55a0f8;width:30px;height:30px;margin-right:10px;display:flex;align-items:center}.flex-center[data-v-cb159114]{display:flex;align-items:center}@media (max-width: 750px){.gva-card[data-v-cb159114]{padding:20px 10px!important}.gva-card .gva-top-card[data-v-cb159114]{height:auto}.gva-card .gva-top-card-left-title[data-v-cb159114]{font-size:20px!important}.gva-card .gva-top-card-left-rows[data-v-cb159114]{margin-top:15px;align-items:center}.gva-card .gva-top-card-right[data-v-cb159114]{display:none}.gva-card .gva-middle-card-item[data-v-cb159114]{line-height:20px}.gva-card .dashboard-icon[data-v-cb159114]{font-size:18px}}
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
import{D as M,d as V,c as b,b as f,f as z,h as l,w as a,u as r,V as L,F as N,W as j,X as g,e as y,o as i,B as p,Y as m,I as h,Z as O,g as k}from"./index.9e912d9b.js";const U={"Content-Type":"application/json;charsetset=UTF-8"},X=w=>M({url:"/cloud/api/v1/file/list",method:"GET",params:{url:w},headers:U});const Y=k("label",null," _new folder",-1),$=k("label",null," _copy",-1),A=k("label",null," _rename",-1),G=k("label",null," _delete",-1),I=k("label",null," _delete",-1),Z=V({setup(w){const u=j(),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)},D=(e,o,t)=>{C.isCtrl?n.has(e)?n.delete(e):n.add(e):(n.clear(),n.add(e))},S=(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}},_=()=>{},T=({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}},X("").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(),z(N,null,[l(r(L),{data:r(R),onCellMouseEnter:F,onCellMouseLeave:B,"row-style":T,onRowContextmenu:S,onRowClick:D,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(O),{"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}),Y]),_:1})):h("",!0),d.value?(i(),p(r(m),{key:1,onClick:_},{default:a(()=>[l(c,{class:"contextmenu-icon"},{default:a(()=>[l(s)]),_:1}),$]),_: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}),I]),_:1})):h("",!0)]),_:1},512)],64)}}});export{Z as default};
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 @@
.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.
-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}
-1
View File
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.
File diff suppressed because one or more lines are too long
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
.text[data-v-6c04fabb]{font-size:14px}.item[data-v-6c04fabb]{padding:18px 0}.yuliu[data-v-6c04fabb]{height:600px;float:left;background:url(/assets/have_a_nice_day.4bde267a.jpeg) center top no-repeat;background-size:cover}
-1
View File
File diff suppressed because one or more lines are too long
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
import{E as u}from"./style.2622ccfc.js";import{d as i,b as s,f as m,g as d,z as p,u as a,h as c,P as f,F as g,a6 as V,o as B}from"./index.9e912d9b.js";import{c as _}from"./blogView.38a54248.js";import"./index.26aa7e8a.js";const F=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{F 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{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,a6 as E,e as r,o as F,bW as k,s as h,a as A,r as g}from"./index.9e912d9b.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{O as n,b as l,J as s}from"./index.9e912d9b.js";const d=n("settings",()=>{const e=l({sideMode:"#191a23",isMobile:!1,isRouterAlive:!0,isLoading:!1}),i=s(()=>e.value.sideMode),o=s(()=>e.value.isMobile),t=s(()=>e.value.isRouterAlive),a=s(()=>e.value.isLoading);return{settings:e,sideMode:i,isMobile:o,isRouterAlive:t,isLoading:a}});export{d as s};
-1
View File
@@ -1 +0,0 @@
import{_ as e,f as r,o as c}from"./index.9e912d9b.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{D as e,O as c,c as a}from"./index.9e912d9b.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.
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="/assets/index.9e912d9b.js"></script>
<link rel="stylesheet" href="/assets/index.35a0e576.css">
</head>
<body>
<div id="app"></div>
</body>
</html>
+1 -1
View File
@@ -5,7 +5,7 @@
<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>
<title>个人知识记录</title>
</head>
<body>
-31
View File
@@ -1,31 +0,0 @@
import { MockMethod } from 'vite-plugin-mock'
export default [
{
url: '/cloud/api/v1/file/list',
method: 'get',
response: ({ body, query }) => {
return [
{
server_filename: "java实战.pdf",
path: "/",
isDir: 0,
size: 1000
},
{
server_filename: "天道.pdf",
path: "/",
isDir: 0,
size: 1000
},
{
server_filename: "我得音乐",
path: "/",
isDir: 1,
size: 10001
},
]
}
},
] as MockMethod[]
-6
View File
@@ -1,6 +0,0 @@
import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer';
import tenant from './tenant';
export function setupMock() {
createProdMockServer([...tenant]);
}
+57
View File
@@ -0,0 +1,57 @@
import { MockMethod } from 'vite-plugin-mock'
export default [
{
url: '/traderCalender/api/v1/trader/calender/year/2024',
method: 'get',
response: ({ body, query }) => {
return [
{
month: 4,
month_beginning_balance: 132947.59,
offset_gain_and_loss: 0,
mark_to_market: 5300,
commission: 0,
deposit_and_withdrawal: 0,
month_end_balance: 138247.59,
},
{
month: 5,
month_beginning_balance: 138247.59,
offset_gain_and_loss: 5080,
mark_to_market: 1960,
commission: 108,
deposit_and_withdrawal: 10000,
month_end_balance: 135179.59,
},
{
month: 6,
month_beginning_balance: 135179.59,
offset_gain_and_loss: -2830,
mark_to_market: -2920,
commission: 32,
deposit_and_withdrawal: 10000,
month_end_balance: 119397.59,
},
{
month: 7,
month_beginning_balance: 119397.59,
offset_gain_and_loss: 0,
mark_to_market: -5640,
commission: 16,
deposit_and_withdrawal: 0,
month_end_balance: 113741.59,
},
{
month: 8,
month_beginning_balance: 113741.59,
offset_gain_and_loss: -2480,
mark_to_market: -4150,
commission: 24,
deposit_and_withdrawal: 1000,
month_end_balance: 106087.59,
},
]
}
},
] as MockMethod[]
+5
View File
@@ -14,6 +14,11 @@ server {
index index.html index.htm;
}
location /profile {
alias /usr/share/nginx/html;
index index.html index.htm;
}
location /home {
alias /usr/share/nginx/html;
index index.html index.htm;
+4069 -3125
View File
File diff suppressed because it is too large Load Diff
+27 -37
View File
@@ -1,54 +1,44 @@
{
"name": "violin-home",
"private": true,
"version": "1.0.0",
"version": "2.0.0",
"scripts": {
"dev": "vite --host",
"build": "vue-tsc --noEmit && vite build",
"development": "vite build --mode development",
"test": "vite --mode test",
"build:test": "vue-tsc --noEmit && vite build --mode test",
"build:dev": "vue-tsc --noEmit && vite build --mode development",
"preview": "vite preview"
},
"dependencies": {
"@element-plus/icons-vue": "^1.1.4",
"@types/node": "^17.0.23",
"aplayer": "^1.10.1",
"axios": "^0.26.1",
"clipboard": "^2.0.10",
"copy-to-clipboard": "^3.3.1",
"echarts": "^5.3.2",
"element-plus": "^2.1.7",
"js-cookie": "^3.0.1",
"lodash": "^4.17.21",
"@element-plus/icons-vue": "^2.3.1",
"axios": "^1.7.7",
"clipboard": "^2.0.11",
"copy-to-clipboard": "^3.3.3",
"dayjs": "^1.11.13",
"echarts": "^5.5.1",
"element-plus": "^2.14.1",
"js-cookie": "^3.0.5",
"lodash-es": "^4.17.21",
"lodash-unified": "^1.0.2",
"md-editor-v3": "^1.11.6",
"moment": "^2.29.4",
"node-sass": "^8.0.0",
"pinia": "^2.0.13",
"qs": "^6.10.3",
"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",
"md-editor-v3": "^4.20.1",
"pinia": "^2.2.4",
"qs": "^6.13.0",
"v-contextmenu": "^3.2.0",
"vue": "^3.5.12",
"vue-router": "^4.4.5",
"vuedraggable": "^4.1.0"
},
"devDependencies": {
"@types/js-cookie": "^3.0.1",
"@types/lodash-es": "^4.17.6",
"@types/qs": "^6.9.7",
"@vitejs/plugin-vue": "^2.3.0",
"@types/js-cookie": "^3.0.6",
"@types/lodash-es": "^4.17.12",
"@types/node": "^22.7.5",
"@types/qs": "^6.9.15",
"@vitejs/plugin-vue": "^5.1.4",
"mockjs": "^1.1.0",
"typescript": "^4.5.4",
"v-contextmenu": "^3.0.0",
"vite": "^2.9.13",
"sass": "^1.79.4",
"typescript": "~5.6.2",
"vite": "^5.4.8",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-mock": "^2.9.6",
"vue-tsc": "^0.29.8"
"vite-plugin-mock": "^3.0.2",
"vue-tsc": "^2.1.6"
}
}
+47 -12
View File
@@ -1,26 +1,61 @@
import { service } from '../utils/request'
const headers = {
'Content-Type': 'application/json;charsetset=UTF-8'
}
const put_event = (data: Object) => {
/**
* 日历事件 API
* 对应后端 CalendarController: /api/v1/events
*/
const post_event = (data: {
eventDate: string
title: string
eventInfo?: string
}) => {
return service({
url: '/calendar/api/v1/event',
method: 'PUT',
data: data
url: '/calendar/api/v1/events',
method: 'POST',
data
})
}
const get_event = () => {
/**
* 查询事件(按月份 + 分页)
* @param params.month yyyy-MM(必填,null 时后端默认当前月)
* @param params.page 页码(从 1 开始,默认 1)
* @param params.pageSize 每页大小(默认 10,上限 100)
*/
const get_event = (params?: {
month?: string
page?: number
pageSize?: number
}) => {
return service({
url: '/calendar/api/v1/event',
url: '/calendar/api/v1/events',
method: 'GET',
headers: headers
params
})
}
const put_event = (id: number, data: {
eventDate: string
title: string
eventInfo?: string
}) => {
return service({
url: `/calendar/api/v1/events/${id}`,
method: 'PUT',
data
})
}
const delete_event = (id: number) => {
return service({
url: `/calendar/api/v1/events/${id}`,
method: 'DELETE'
})
}
export {
get_event,
post_event,
put_event,
}
delete_event,
}
-16
View File
@@ -1,16 +0,0 @@
import { service } from '../utils/request'
const headers = {
'Content-Type': 'application/json;charsetset=UTF-8'
}
const getFileList = (url: string) => {
return service({
url: '/cloud/api/v1/file/list',
method: 'GET',
params: { url: url },
headers: headers
})
}
export { getFileList }
+9 -1
View File
@@ -20,6 +20,13 @@ const update_profile = (data: Object) => {
})
}
const publish_profile = () => {
return service({
url: '/wiki/api/v1/author/profile/publish',
method: 'PUT'
})
}
const get_profile = () => {
return service({
url: '/wiki/api/v1/author/profile',
@@ -51,5 +58,6 @@ export {
get_profile,
update_profile,
get_profileName,
judge_profile
judge_profile,
publish_profile
}
+25
View File
@@ -0,0 +1,25 @@
import { service } from '../utils/request'
const headers = {
'Content-Type': 'application/json;charsetset=UTF-8'
}
// To obtain the monthly data by year
const get_monthly_data = (year: string) => {
return service({
url: '/traderCalender/api/v1/trader/calender/year/' + year,
method: 'GET',
headers: headers,
})
}
// To update the monthly data by year
// const update_monthly_data = (data: object) => {
// return service({
// url: '/traderCalender/api/v1/trader/calender/year/' + year,
// method: 'POST',
// data: data
// })
// }
export { get_monthly_data }
+7 -8
View File
@@ -1,11 +1,11 @@
<template>
<span class="headerAvatar">
<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" />
</template>
<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">
</template>
<template v-if="picType === 'file'">
@@ -16,7 +16,7 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import { tenantStore } from '@/store/modules/tenant'
import { customerStore } from '@/store/modules/customer'
import noAvatarPng from '@/assets/nobody.jpg'
const props = defineProps({
@@ -33,13 +33,13 @@ const props = defineProps({
})
const path = ref(import.meta.env.VITE_BASE_API + '/')
const noAvatar = ref(noAvatarPng)
const useTenantStore = tenantStore()
const useCustomerStore = customerStore()
const avatar = computed(() => {
if (props.picSrc === '') {
if (useTenantStore.tenant.headerImg !== '' && useTenantStore.tenant.headerImg.slice(0, 4) === 'http') {
return useTenantStore.tenant.headerImg
if (useCustomerStore.customer.headerImg !== '' && useCustomerStore.customer.headerImg.slice(0, 4) === 'http') {
return useCustomerStore.customer.headerImg
}
return path.value + useTenantStore.tenant.headerImg
return path.value + useCustomerStore.customer.headerImg
} else {
if (props.picSrc !== '' && props.picSrc.slice(0, 4) === 'http') {
return props.picSrc
@@ -61,7 +61,6 @@ const file = computed(() => {
align-items: center;
margin-right: 8px;
}
.file {
width: 80px;
height: 80px;
+54 -12
View File
@@ -12,12 +12,14 @@
<MainApp></MainApp>
</el-main>
<el-footer>
<span>
管祥玮的个人网站 ©Copyright 2022-2022
</span>
<span @click="openSiteQuery">辽ICP备2022003637号-2</span>
<div>
<el-avatar @click="openGithub" src="https://github.githubassets.com/favicons/favicon.svg" />
<div class="footer-left">
<span>{{ SITE_NAME }} {{ COPYRIGHT_TEXT }}</span>
<span class="separator">|</span>
<span class="icp-link" @click="openSiteQuery">{{ ICP_NUMBER }}</span>
</div>
<div class="footer-right">
<el-avatar :size="24" class="github-icon" @click="openGithub"
src="https://github.githubassets.com/favicons/favicon.svg" />
</div>
</el-footer>
</el-container>
@@ -30,6 +32,7 @@ import MainApp from '../layout/MainApp.vue'
import NavBar from "../layout/NavBar.vue"
import SideBar from "../layout/SideBar.vue"
import { settingsStore } from "@/store/modules/settings"
import { SITE_NAME, COPYRIGHT_TEXT, ICP_NUMBER, ICP_QUERY_URL } from "@/const/site"
// -- IMPORT --
const useSettingsStore = settingsStore()
@@ -62,8 +65,7 @@ const openGithub = () => {
}
const openSiteQuery = () => {
let href = 'https://beian.miit.gov.cn/#/Integrated/index'
window.open(href, '_blank')
window.open(ICP_QUERY_URL, '_blank')
}
/**
@@ -81,9 +83,12 @@ iniPage()
}
.el-main {
/* 让 main 区占满 header 与 footer 之间的剩余空间,
内容多了 main 内部滚动,footer 永远钉在 layout 底部 */
padding: 16px;
overflow: hidden;
height: 100%;
flex: 1;
min-height: 0; /* flex item 默认 min-height: auto 会阻止收缩 */
overflow-y: auto;
}
.el-aside {
@@ -93,13 +98,50 @@ iniPage()
}
.el-footer {
/* flex: 0 0 auto 让 footer 保持自身高度(不撑开、不收缩) */
flex: 0 0 auto;
margin-top: 20px;
display: flex;
justify-content: space-around;
justify-content: space-between;
align-items: center;
padding: 12px 24px;
border-top: 1px solid rgba(151, 151, 151, 0.2);
font-size: 16px;
color: #666;
}
.footer-left {
display: flex;
align-items: center;
gap: 8px;
}
.footer-right {
display: flex;
align-items: center;
}
.separator {
color: #ccc;
}
.icp-link {
cursor: pointer;
user-select: none;
}
.icp-link:hover {
color: #409eff;
}
.github-icon {
/* 跟 footer 文字 baseline 对齐 */
vertical-align: middle;
cursor: pointer;
}
.main_container {
/* height: 100%; */
height: 100%;
}
.homeWrap {
+7 -7
View File
@@ -56,7 +56,7 @@
<span class="header-avatar" style="cursor: pointer">
<CustomPic />
<span v-show="!useSettingsStore.isMobile" style="margin-left: 5px">{{
useTenantStore.tenant.account
useCustomerStore.customer.account
}}</span>
<el-icon>
<arrow-down />
@@ -67,10 +67,10 @@
<el-dropdown-menu class="dropdown-group">
<el-dropdown-item>
<span style="font-weight: 600;">
当前角色{{ useTenantStore.tenant.account }}
当前角色{{ useCustomerStore.customer.account }}
</span>
</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)">
<span>
切换为{{ item.authorityName }}
@@ -78,7 +78,7 @@
</el-dropdown-item> -->
</template>
<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>
</template>
</el-dropdown>
@@ -89,13 +89,13 @@
<script setup lang='ts'>
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 { Message } from "@element-plus/icons-vue"
import { reactive, ref } from "vue";
// -- IMPORT --
const useTenantStore = tenantStore()
const useCustomerStore = customerStore()
const useSettingsStore = settingsStore()
// -- REACTIVE OBJECT --
@@ -131,7 +131,7 @@ const toPerson = () => {
}
useTenantStore.reflush()
useCustomerStore.reflush()
</script>
-3
View File
@@ -21,9 +21,6 @@
<el-icon v-if="index == 4" :size="20">
<Calendar />
</el-icon>
<el-icon v-if="index == 5" :size="20">
<MostlyCloudy />
</el-icon>
<el-icon v-if="index == 10" :size="20">
<Setting />
</el-icon>
+554 -83
View File
@@ -1,105 +1,576 @@
<template>
<div class="yuliu" :style="style">
</div>
<el-card class="box-card">
<el-form ref="ruleFormRef" :model="loginForm" :rules="loginRules" status-icon label-width="120px"
class="demo-ruleForm">
<div class="title-container">
<h3>秘密基地</h3>
</div>
<div>
<el-avatar :size="28" :src="baiduCloudImage" @click="scan(qrcode)" />
</div>
<el-form-item>
其他登录方式暂时还没有哦
</el-form-item>
</el-form>
------------------------------------------------------------
<div>
微信/支付宝
<div class="ttt">
<!-- 动态光斑背景 -->
<div class="bg-orbs">
<span class="orb orb-1"></span>
<span class="orb orb-2"></span>
<span class="orb orb-3"></span>
</div>
</el-card>
<div class="content">
<!-- 玻璃卡片 -->
<div class="glass-card">
<div class="brand">
<span class="brand-mark"></span>
<h1 class="brand-name">{{ SITE_NAME }}</h1>
<p class="brand-sub">Secret Base · 秘密基地</p>
</div>
<div class="divider"></div>
<el-button
class="login-btn"
size="large"
@click="handleOidcLogin"
>
<span class="login-btn-content">
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 2L3 7v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V7l-9-5zm0 2.18l7 3.89v4.93c0 4.43-3.07 8.58-7 9.71-3.93-1.13-7-5.28-7-9.71V8.07l7-3.89z"/>
<path d="M12 11h-2v2h-2v2h2v2h2v-2h2v-2h-2z"/>
</svg>
Login
</span>
</el-button>
<!-- <p class="login-hint">
通过 <a href="https://goauthentik.io" target="_blank">Authentik</a> OIDC 安全登录
</p> -->
</div>
</div>
<!-- 右侧关联链接 -->
<div class="side-links">
<a
v-for="link in sideLinks"
:key="link.name"
:href="link.url"
target="_blank"
class="side-link"
:title="link.name"
>
<span class="side-link-icon" v-html="link.icon"></span>
<span class="side-link-name">{{ link.name }}</span>
</a>
</div>
<!-- 左侧外部网站链接 -->
<div class="external-links">
<a
v-for="link in externalLinks"
:key="link.name"
:href="link.url"
target="_blank"
class="external-link"
:title="link.name"
>
<span class="external-link-icon" v-html="link.icon"></span>
<span class="external-link-name">{{ link.name }}</span>
</a>
</div>
<div class="footer">
<div class="footer-left">
<span>{{ SITE_NAME }} {{ COPYRIGHT_TEXT }}</span>
<span class="separator">|</span>
<span class="icp-link" @click="openSiteQuery">{{ ICP_NUMBER }}</span>
</div>
<div class="footer-right">
<el-avatar :size="24" class="github-icon" @click="openGithub"
src="https://github.githubassets.com/favicons/favicon.svg" />
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import type { FormInstance } 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'
import { ElMessage } from 'element-plus'
import { SITE_NAME, COPYRIGHT_TEXT, ICP_NUMBER, ICP_QUERY_URL } from '@/const/site'
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
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)
if (!issuer || !clientId) {
ElMessage.error('OIDC 未配置,请联系管理员')
return
// form.validate((valid: any) => {
// if (valid) {
// useTenantStore.login(loginForm.user_id, loginForm.password).then(res => {
// console.log(1)
// router.push({
// path: '/home'
// })
// })
}
// } else {
// return false
// }
// })
sessionStorage.setItem('oidc_state', state)
window.location.href =
`${issuer}/application/o/authorize/?` +
`client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code&scope=${scope}&state=${state}`
}
const style = reactive({} as any)
style.width = window.innerWidth * 0.7 + 'px'
const openSiteQuery = () => {
window.open(ICP_QUERY_URL, '_blank')
}
const openGithub = () => {
window.open('https://github.com/simple321vip', '_blank')
}
/**
* 右侧固定关联网站链接。改这里即可增删。
* icon: 简单 inline SVG24x24),用 currentColor 跟父元素文字色联动
*/
const sideLinks = [
{
name: 'Gitea',
url: 'https://gitea.violin-work.online',
// Gitea 标志性元素:圆形 + 三个肉垫(猫爪印)
icon: '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="14" r="6"/><ellipse cx="6" cy="8" rx="2" ry="2.5"/><ellipse cx="12" cy="5" rx="2" ry="2.5"/><ellipse cx="18" cy="8" rx="2" ry="2.5"/></svg>'
},
{
name: 'Blog',
url: 'https://www.violin-home.cn/blog',
icon: '<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V5h14v14zM7 10h10v2H7zm0 4h10v2H7z"/></svg>'
},
{
name: 'Authentik',
url: 'https://auth.violin-work.online',
// Authentik 标志性元素:盾牌 + 钥匙孔
icon: '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><path d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm0 10.5a2 2 0 110 1 2 2 0 010-1zm-2.83 4.95L8 14l-1.17 2.45L4 18l2.83-1.55z"/></svg>'
},
{
name: 'KubeSphere',
url: 'https://k8s.violin-work.online',
// KubeSphere 标志性元素:球体 + 经线纬线(地球网格)
icon: '<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="12" cy="12" r="10"/><ellipse cx="12" cy="12" rx="10" ry="4"/><ellipse cx="12" cy="12" rx="4" ry="10"/><line x1="2" y1="12" x2="22" y2="12"/></svg>'
},
{
name: 'Viking (RAG)',
url: 'https://viking.violin-work.online/',
// Viking / 维京:维京头盔 + 翅膀,或简化为双翼头盔
icon: '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2L9 5H6v4l-3 2 3 1v3a6 6 0 0012 0v-3l3-1-3-2V5h-3l-3-3zm0 4a2 2 0 110 4 2 2 0 010-4z"/><path d="M3 11l-1 1 1 1zm18 0l1 1-1 1z"/></svg>'
},
{
name: 'Nextcloud',
url: 'https://cloud.violin-work.online/',
// Nextcloud 标志性元素:三个圆环组成的"云"
icon: '<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="7" cy="14" r="4"/><circle cx="14" cy="9" r="4"/><circle cx="18" cy="15" r="3"/></svg>'
},
{
name: 'MinIO',
url: 'https://minio.violin-work.online/',
// MinIO 标志性元素:三层堆叠的小方块
icon: '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><rect x="3" y="14" width="18" height="6" rx="1"/><rect x="5" y="8" width="14" height="5" rx="1" opacity="0.7"/><rect x="7" y="3" width="10" height="4" rx="1" opacity="0.45"/></svg>'
}
] as const
/**
* 左侧固定外部网站链接。
*/
const externalLinks = [
{
name: 'GitHub',
url: 'https://github.com/simple321vip',
icon: '<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.4 3-.405 1.02.005 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>'
},
{
name: 'MiniMax',
url: 'https://www.minimaxi.com/',
// MiniMax 标志性元素:六角星 + 中心点(极小化极大概括)
icon: '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2l2.5 5.5L20 9l-4 4 1 6-5-3-5 3 1-6-4-4 5.5-1.5L12 2z"/></svg>'
},
{
name: '百度',
url: 'https://www.baidu.com/',
// 百度:爪形脚印
icon: '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><ellipse cx="9" cy="11" rx="3.5" ry="5"/><ellipse cx="15" cy="11" rx="3.5" ry="5"/><circle cx="6" cy="5" r="1.5"/><circle cx="12" cy="3" r="1.5"/><circle cx="18" cy="5" r="1.5"/></svg>'
},
{
name: '腾讯云',
url: 'https://cloud.tencent.com/',
// 腾讯云:云朵 + T 字标识
icon: '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><path d="M6 18a4 4 0 010-8 5 5 0 019.58-1.5A4 4 0 0118 18H6z"/><text x="12" y="16" font-size="8" font-weight="bold" text-anchor="middle" fill="#0f0c29">T</text></svg>'
},
{
name: '菜鸟教程',
url: 'https://www.runoob.com/',
// 菜鸟教程:鸟的剪影(飞行)
icon: '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><path d="M22 4l-2 2-3-1-5 3-4-2-3 3 2 2 4-2 3 2 4-2 4 1 1-2-3-2 2-2zM2 14l4 4 6-1 4 2 2-1-3-3-4 1-3-3-4 1-2-1z"/></svg>'
}
] as const
</script>
<style scoped>
.text {
font-size: 14px;
.ttt {
position: relative;
min-height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
background: linear-gradient(135deg, #0f0c29 0%, #302b63 50%, #24243e 100%);
}
.item {
padding: 18px 0;
/* ============ 动态光斑背景 ============ */
.bg-orbs {
position: absolute;
inset: 0;
overflow: hidden;
pointer-events: none;
z-index: 0;
}
.yuliu {
/* width: 600px; */
.orb {
position: absolute;
border-radius: 50%;
filter: blur(80px);
opacity: 0.5;
animation: float 20s ease-in-out infinite;
}
.orb-1 {
width: 500px;
height: 500px;
background: radial-gradient(circle, #667eea 0%, transparent 70%);
top: -150px;
left: -100px;
animation-delay: 0s;
}
.orb-2 {
width: 600px;
height: 600px;
float: left;
/* background-color: antiquewhite; */
background: url('../../assets/have_a_nice_day.jpeg') center top no-repeat;
background-size: cover;
background: radial-gradient(circle, #f093fb 0%, transparent 70%);
bottom: -200px;
right: -150px;
animation-delay: -7s;
}
.orb-3 {
width: 400px;
height: 400px;
background: radial-gradient(circle, #4facfe 0%, transparent 70%);
top: 40%;
left: 50%;
animation-delay: -14s;
}
@keyframes float {
0%, 100% { transform: translate(0, 0) scale(1); }
33% { transform: translate(50px, -30px) scale(1.1); }
66% { transform: translate(-30px, 50px) scale(0.9); }
}
/* ============ 内容区 ============ */
.content {
position: relative;
z-index: 1;
flex: 1;
display: flex;
align-items: center;
justify-content: center;
padding: 40px 20px;
}
/* ============ 玻璃卡片 ============ */
.glass-card {
width: 100%;
max-width: 420px;
padding: 56px 40px 48px;
background: rgba(255, 255, 255, 0.06);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: 20px;
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
color: #fff;
}
.brand {
text-align: center;
margin-bottom: 32px;
}
.brand-mark {
display: inline-block;
font-size: 32px;
background: linear-gradient(135deg, #667eea 0%, #f093fb 100%);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
margin-bottom: 12px;
animation: pulse 3s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 0.7; transform: scale(1); }
50% { opacity: 1; transform: scale(1.1); }
}
.brand-name {
margin: 0 0 8px;
font-size: 32px;
font-weight: 600;
letter-spacing: 1px;
background: linear-gradient(135deg, #fff 0%, #b8c6db 100%);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.brand-sub {
margin: 0;
font-size: 13px;
color: rgba(255, 255, 255, 0.5);
letter-spacing: 2px;
}
/* ============ 分隔线 ============ */
.divider {
height: 1px;
background: linear-gradient(90deg,
transparent 0%,
rgba(255, 255, 255, 0.2) 50%,
transparent 100%);
margin: 24px 0 32px;
}
/* ============ 登录按钮 ============ */
.login-btn {
width: 100%;
height: 50px !important;
border: none !important;
border-radius: 12px !important;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4) !important;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
overflow: hidden;
}
.login-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.6) !important;
}
.login-btn:active {
transform: translateY(0);
}
.login-btn-content {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
font-size: 16px;
font-weight: 500;
letter-spacing: 1px;
color: #fff;
}
.login-hint {
margin: 20px 0 0;
text-align: center;
font-size: 12px;
color: rgba(255, 255, 255, 0.4);
}
.login-hint a {
color: rgba(255, 255, 255, 0.6);
text-decoration: none;
border-bottom: 1px dashed rgba(255, 255, 255, 0.3);
}
.login-hint a:hover {
color: #fff;
border-bottom-color: #fff;
}
/* ============ 右侧固定关联链接 ============ */
.side-links {
position: fixed;
right: 32px;
top: 50%;
transform: translateY(-50%);
display: flex;
flex-direction: column;
gap: 14px;
z-index: 10;
}
.side-link {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.08);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.15);
color: rgba(255, 255, 255, 0.7);
text-decoration: none;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.side-link:hover {
background: rgba(102, 126, 234, 0.25);
border-color: rgba(102, 126, 234, 0.6);
color: #fff;
transform: scale(1.1);
box-shadow: 0 4px 20px rgba(102, 126, 234, 0.4);
}
.side-link-icon {
display: flex;
align-items: center;
justify-content: center;
}
/* tooltip 显示名字 */
.side-link-name {
position: absolute;
right: 56px;
white-space: nowrap;
padding: 6px 12px;
border-radius: 6px;
background: rgba(0, 0, 0, 0.7);
backdrop-filter: blur(10px);
color: #fff;
font-size: 13px;
opacity: 0;
transform: translateX(10px);
pointer-events: none;
transition: all 0.25s ease;
}
.side-link:hover .side-link-name {
opacity: 1;
transform: translateX(0);
}
/* 移动端隐藏侧栏(屏幕太窄) */
@media (max-width: 768px) {
.side-links {
display: none;
}
}
/* ============ 左侧外部网站链接 ============ */
.external-links {
position: fixed;
left: 32px;
top: 50%;
transform: translateY(-50%);
display: flex;
flex-direction: column;
gap: 14px;
z-index: 10;
}
.external-link {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.08);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.15);
color: rgba(255, 255, 255, 0.7);
text-decoration: none;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.external-link:hover {
background: rgba(118, 75, 162, 0.25);
border-color: rgba(240, 147, 251, 0.6);
color: #fff;
transform: scale(1.1);
box-shadow: 0 4px 20px rgba(240, 147, 251, 0.4);
}
.external-link-icon {
display: flex;
align-items: center;
justify-content: center;
}
/* tooltip 在右侧(跟左侧链接方向相反) */
.external-link-name {
position: absolute;
left: 56px;
white-space: nowrap;
padding: 6px 12px;
border-radius: 6px;
background: rgba(0, 0, 0, 0.7);
backdrop-filter: blur(10px);
color: #fff;
font-size: 13px;
opacity: 0;
transform: translateX(-10px);
pointer-events: none;
transition: all 0.25s ease;
}
.external-link:hover .external-link-name {
opacity: 1;
transform: translateX(0);
}
@media (max-width: 768px) {
.external-links {
display: none;
}
}
/* ============ Footer ============ */
.footer {
position: relative;
z-index: 1;
flex: 0 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 32px;
color: rgba(255, 255, 255, 0.5);
font-size: 13px;
}
.footer-left {
display: flex;
align-items: center;
gap: 8px;
}
.footer-right {
display: flex;
align-items: center;
}
.separator {
color: rgba(255, 255, 255, 0.25);
}
.icp-link {
cursor: pointer;
user-select: none;
transition: color 0.2s;
}
.icp-link:hover {
color: rgba(255, 255, 255, 0.85);
}
.github-icon {
vertical-align: middle;
cursor: pointer;
opacity: 0.7;
transition: opacity 0.2s;
}
.github-icon:hover {
opacity: 1;
}
</style>
+9 -9
View File
@@ -11,8 +11,8 @@
<el-input v-model="register_form.code" />
</el-form-item>
<el-form-item label="tenantId">
<el-input v-model="register_form.tenant_id" :disabled="true" />
<el-form-item label="customerId">
<el-input v-model="register_form.customerId" :disabled="true" />
</el-form-item>
<el-form-item label="account">
@@ -35,17 +35,17 @@ import { reactive } from 'vue'
import { useRoute } from 'vue-router';
import { register_user } from "./../../api/user";
import router from '../../router/index'
import { setTenant, setToken } from '../../utils/auth';
import { setCustomer, setToken } from '../../utils/auth';
const route = useRoute()
const params = route.query
// use phone number to register user(tenant)
// use phone number to register user(customer)
const register_form = reactive({
phone_number: '',
code: '',
tenant_id: params.tenantId as string,
customerId: params.customerId as string,
account: params.account as string,
token: params.token as string
})
@@ -54,13 +54,13 @@ const onSubmit = () => {
register_user(register_form).then((response) => {
if (response.status == 200) {
const tenant = {
id: params.tenantId,
const customer = {
id: params.customerId,
account: params.account
}
setToken(params.token as string)
setTenant(tenant)
setCustomer(customer)
const { href } = router.resolve({
path: '/'
@@ -74,4 +74,4 @@ const onSubmit = () => {
</script>
<style scoped>
</style>
</style>
+10 -10
View File
@@ -1,7 +1,7 @@
import { Tenant } from '@/entity';
import { tenantStore } from '@/store/modules/tenant';
import { Customer } from '@/entity';
import { customerStore } from '@/store/modules/customer';
import router from '../router'
import { setToken, setTenant } from '../utils/auth'
import { setToken, setCustomer } from '../utils/auth'
// config/index.js
const env = process.env.NODE_ENV!;
@@ -45,14 +45,14 @@ const scan_method: any = {
},
development: async () => {
const useTenantStore = tenantStore()
const tenant: Tenant = {
tenant_id: '7788',
const useCustomerStore = customerStore()
const customer: Customer = {
customerId: '7788',
account: '小小的测试账户'
}
let token = 'ABCDEFG'
await useTenantStore.login(tenant, token).then(() => {
await useCustomerStore.login(customer, token).then(() => {
const { href } = router.resolve({
path: '/'
})
@@ -65,9 +65,9 @@ const scan_method: any = {
})
setToken("token")
setTenant(
setCustomer(
{
tenantId: "111"
customerId: "111"
}
)
router.push({
@@ -82,4 +82,4 @@ const scan_method: any = {
const scan = scan_method[env]
const config = configObj[env]
export { scan, config }
export { scan, config }

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