feat: 添加员工门户项目及相关后端改造
- 新增 hzhub-portal-employee 员工门户前端项目(基于 Vue3 + Element Plus) - 后端登录接口增加返回 nickName 字段 - 移除 KnowledgeInfoController 的 @SaCheckPermission 注解 - 删除 hzhub-portal-company 旧门户项目 - 更新项目文档和架构说明 - 添加后台运行管理脚本(start-all.sh / status-all.sh / stop-all.sh) - 更新 docker-compose 配置 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
39
hzhub-portal-employee/.build/plugins/index.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import type { ConfigEnv, PluginOption } from 'vite';
|
||||
import path from 'node:path';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import UnoCSS from 'unocss/vite';
|
||||
import AutoImport from 'unplugin-auto-import/vite';
|
||||
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
|
||||
import Components from 'unplugin-vue-components/vite';
|
||||
import envTyped from 'vite-plugin-env-typed';
|
||||
import createSvgIcon from './svg-icon';
|
||||
|
||||
const root = path.resolve(__dirname, '../../');
|
||||
|
||||
function plugins({ mode, command }: ConfigEnv): PluginOption[] {
|
||||
return [
|
||||
UnoCSS(),
|
||||
envTyped({
|
||||
mode,
|
||||
envDir: root,
|
||||
envPrefix: 'VITE_',
|
||||
filePath: path.join(root, 'types', 'import_meta.d.ts'),
|
||||
}),
|
||||
vue(),
|
||||
AutoImport({
|
||||
imports: ['vue'],
|
||||
eslintrc: {
|
||||
enabled: true,
|
||||
},
|
||||
resolvers: [ElementPlusResolver()],
|
||||
dts: path.join(root, 'types', 'auto-imports.d.ts'),
|
||||
}),
|
||||
Components({
|
||||
resolvers: [ElementPlusResolver()],
|
||||
dts: path.join(root, 'types', 'components.d.ts'),
|
||||
}),
|
||||
createSvgIcon(command === 'build'),
|
||||
];
|
||||
}
|
||||
|
||||
export default plugins;
|
||||
21
hzhub-portal-employee/.build/plugins/svg-icon.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import path from 'node:path';
|
||||
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
|
||||
|
||||
const root = path.resolve(__dirname, '../../');
|
||||
|
||||
export default function createSvgIcon(isBuild: boolean) {
|
||||
return createSvgIconsPlugin({
|
||||
iconDirs: [
|
||||
path.join(root, 'src/assets/icons/svg'),
|
||||
path.join(root, 'src/assets/icons/Buildings'),
|
||||
path.join(root, 'src/assets/icons/Business'),
|
||||
path.join(root, 'src/assets/icons/Device'),
|
||||
path.join(root, 'src/assets/icons/Document'),
|
||||
path.join(root, 'src/assets/icons/Others'),
|
||||
path.join(root, 'src/assets/icons/System'),
|
||||
path.join(root, 'src/assets/icons/User'),
|
||||
],
|
||||
symbolId: 'icon-[dir]-[name]',
|
||||
svgoOptions: isBuild,
|
||||
});
|
||||
}
|
||||
4
hzhub-portal-employee/.env.development
Normal file
@@ -0,0 +1,4 @@
|
||||
# 开发环境配置
|
||||
VITE_API_URL=
|
||||
VITE_CLIENT_ID=e5cd7e4891bf95d1d19206ce24a7b32e
|
||||
VITE_WEB_TITLE=Employee Portal 企业员工门户
|
||||
78
hzhub-portal-employee/.eslintrc-auto-import.json
Normal file
@@ -0,0 +1,78 @@
|
||||
{
|
||||
"globals": {
|
||||
"Component": true,
|
||||
"ComponentPublicInstance": true,
|
||||
"ComputedRef": true,
|
||||
"DirectiveBinding": true,
|
||||
"EffectScope": true,
|
||||
"ElMessage": true,
|
||||
"ElMessageBox": true,
|
||||
"ExtractDefaultPropTypes": true,
|
||||
"ExtractPropTypes": true,
|
||||
"ExtractPublicPropTypes": true,
|
||||
"InjectionKey": true,
|
||||
"MaybeRef": true,
|
||||
"MaybeRefOrGetter": true,
|
||||
"PropType": true,
|
||||
"Ref": true,
|
||||
"Slot": true,
|
||||
"Slots": true,
|
||||
"VNode": true,
|
||||
"WritableComputedRef": true,
|
||||
"computed": true,
|
||||
"createApp": true,
|
||||
"customRef": true,
|
||||
"defineAsyncComponent": true,
|
||||
"defineComponent": true,
|
||||
"effectScope": true,
|
||||
"getCurrentInstance": true,
|
||||
"getCurrentScope": true,
|
||||
"h": true,
|
||||
"inject": true,
|
||||
"isProxy": true,
|
||||
"isReactive": true,
|
||||
"isReadonly": true,
|
||||
"isRef": true,
|
||||
"markRaw": true,
|
||||
"nextTick": true,
|
||||
"onActivated": true,
|
||||
"onBeforeMount": true,
|
||||
"onBeforeUnmount": true,
|
||||
"onBeforeUpdate": true,
|
||||
"onDeactivated": true,
|
||||
"onErrorCaptured": true,
|
||||
"onMounted": true,
|
||||
"onRenderTracked": true,
|
||||
"onRenderTriggered": true,
|
||||
"onScopeDispose": true,
|
||||
"onServerPrefetch": true,
|
||||
"onUnmounted": true,
|
||||
"onUpdated": true,
|
||||
"onWatcherCleanup": true,
|
||||
"provide": true,
|
||||
"reactive": true,
|
||||
"readonly": true,
|
||||
"ref": true,
|
||||
"resolveComponent": true,
|
||||
"shallowReactive": true,
|
||||
"shallowReadonly": true,
|
||||
"shallowRef": true,
|
||||
"toRaw": true,
|
||||
"toRef": true,
|
||||
"toRefs": true,
|
||||
"toValue": true,
|
||||
"triggerRef": true,
|
||||
"unref": true,
|
||||
"useAttrs": true,
|
||||
"useCssModule": true,
|
||||
"useCssVars": true,
|
||||
"useId": true,
|
||||
"useModel": true,
|
||||
"useSlots": true,
|
||||
"useTemplateRef": true,
|
||||
"watch": true,
|
||||
"watchEffect": true,
|
||||
"watchPostEffect": true,
|
||||
"watchSyncEffect": true
|
||||
}
|
||||
}
|
||||
1
hzhub-portal-employee/.pid
Normal file
@@ -0,0 +1 @@
|
||||
3422048
|
||||
92
hzhub-portal-employee/CLAUDE.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
This is **hzhub-portal-employee**, a Vue 3 Employee Portal application built with TypeScript, Vite, and Element Plus. It provides enterprise employee portal features including workflow approvals, CRM management, BI reports, and AI application integration.
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Development
|
||||
pnpm dev # Start dev server
|
||||
|
||||
# Build
|
||||
pnpm build # Type-check and build for production
|
||||
|
||||
# Linting
|
||||
pnpm lint # Run ESLint
|
||||
pnpm lint:stylelint # Run Stylelint
|
||||
pnpm fix # Auto-fix ESLint issues
|
||||
|
||||
# Preview production build
|
||||
pnpm preview
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
### Tech Stack
|
||||
- **Vue 3** with Composition API (`<script setup>`)
|
||||
- **TypeScript** for type safety
|
||||
- **Pinia** for state management with persistence (`pinia-plugin-persistedstate`)
|
||||
- **Vue Router** for routing with navigation guards
|
||||
- **Element Plus** for UI components
|
||||
- **UnoCSS** for atomic CSS
|
||||
- **hook-fetch** for HTTP requests with SSE support
|
||||
|
||||
### Directory Structure
|
||||
|
||||
```
|
||||
src/
|
||||
├── api/ # API modules (auth, chat, model, session) with types
|
||||
├── assets/ # Static assets (SVG icons organized by category)
|
||||
├── components/ # Reusable components (LoginDialog, ModelSelect, etc.)
|
||||
├── config/ # App configuration constants
|
||||
├── constants/ # Enum definitions
|
||||
├── hooks/ # Custom Vue composables
|
||||
├── layouts/ # Layout components (LayoutVertical, LayoutMobile)
|
||||
├── pages/ # Page components (chat, error pages)
|
||||
├── routers/ # Route definitions and guards
|
||||
├── stores/ # Pinia stores (user, chat, session, model, design)
|
||||
├── styles/ # Global SCSS styles and variables
|
||||
├── utils/ # Utilities (request wrapper, markdown renderers)
|
||||
└── main.ts # App entry point
|
||||
```
|
||||
|
||||
### Key Patterns
|
||||
|
||||
**API Layer** (`src/api/`):
|
||||
- Each module has `index.ts` for API calls and `types.ts` for TypeScript interfaces
|
||||
- Uses `hook-fetch` with JWT plugin for authentication
|
||||
- Base URL configured via `VITE_API_URL` environment variable
|
||||
|
||||
**State Management** (`src/stores/`):
|
||||
- Stores use Composition API style with `defineStore`
|
||||
- User store persists token and userInfo
|
||||
- Session store handles chat sessions with pagination
|
||||
- Chat store manages messages and deep thinking state
|
||||
|
||||
**Routing** (`src/routers/`):
|
||||
- Static routes defined in `modules/staticRouter.ts`
|
||||
- Route guard checks token and handles auth redirect
|
||||
- White list routes bypass auth check
|
||||
|
||||
**HTTP Requests** (`src/utils/request.ts`):
|
||||
- Auto-injects `Bearer` token and `ClientID` headers
|
||||
- Handles 401 (logout) and 403 (redirect) responses
|
||||
- Supports SSE streaming via `sseTextDecoderPlugin`
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Configure in `.env.development`:
|
||||
- `VITE_API_URL` - Backend API base URL
|
||||
- `VITE_CLIENT_ID` - Client identifier for auth
|
||||
- `VITE_WEB_TITLE` - Page title
|
||||
|
||||
### Code Style
|
||||
|
||||
- ESLint with `@antfu/eslint-config`
|
||||
- Vue blocks order: script → template → style
|
||||
- Single quotes, semicolons, 2-space indent
|
||||
- Commit messages follow conventional commits with commitlint
|
||||
47
hzhub-portal-employee/Dockerfile
Normal file
@@ -0,0 +1,47 @@
|
||||
# 构建阶段
|
||||
FROM node:22-alpine AS builder
|
||||
|
||||
# 接收构建参数
|
||||
ARG VITE_API_URL
|
||||
ARG VITE_APP_ENV=production
|
||||
|
||||
# 设置环境变量
|
||||
ENV VITE_API_URL=${VITE_API_URL}
|
||||
ENV VITE_APP_ENV=${VITE_APP_ENV}
|
||||
|
||||
# 设置工作目录
|
||||
WORKDIR /app
|
||||
|
||||
# 复制 package 文件
|
||||
COPY package.json pnpm-lock.yaml ./
|
||||
|
||||
# 安装 pnpm 并安装依赖
|
||||
RUN npm install -g pnpm && \
|
||||
pnpm install --frozen-lockfile
|
||||
|
||||
# 复制源代码
|
||||
COPY . .
|
||||
|
||||
# 构建生产版本(使用 production 模式)
|
||||
RUN pnpm build
|
||||
|
||||
# 生产阶段
|
||||
FROM nginx:alpine
|
||||
|
||||
# 安装 envsubst
|
||||
RUN apk add --no-cache gettext
|
||||
|
||||
# 复制构建产物
|
||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||
|
||||
# 设置后端地址环境变量(默认指向 hzhub-ai 服务)
|
||||
ENV UPSTREAM_URL=http://hzhub-ai:6039
|
||||
|
||||
# 复制 Nginx 配置模板
|
||||
COPY nginx.conf /etc/nginx/templates/default.conf.template
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 5137
|
||||
|
||||
# 启动 Nginx(使用 envsubst 替换环境变量)
|
||||
CMD /bin/sh -c "envsubst '\$UPSTREAM_URL' < /etc/nginx/templates/default.conf.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"
|
||||
44
hzhub-portal-employee/Dockerfile.frontend
Normal file
@@ -0,0 +1,44 @@
|
||||
# 构建阶段
|
||||
FROM node:22-alpine AS builder
|
||||
|
||||
# 接收构建参数
|
||||
ARG VITE_API_URL
|
||||
ARG VITE_APP_ENV=production
|
||||
|
||||
# 设置环境变量
|
||||
ENV VITE_API_URL=${VITE_API_URL}
|
||||
ENV VITE_APP_ENV=${VITE_APP_ENV}
|
||||
|
||||
# 设置工作目录
|
||||
WORKDIR /app
|
||||
|
||||
# 复制 package 文件
|
||||
COPY package.json pnpm-lock.yaml ./
|
||||
|
||||
# 安装 pnpm 并安装依赖
|
||||
RUN npm install -g pnpm && \
|
||||
pnpm install --frozen-lockfile
|
||||
|
||||
# 复制源代码
|
||||
COPY . .
|
||||
|
||||
# 构建生产版本(使用 production 模式)
|
||||
RUN pnpm build
|
||||
|
||||
# 生产阶段
|
||||
FROM nginx:alpine
|
||||
|
||||
# 复制构建产物
|
||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||
|
||||
# 设置后端地址环境变量(默认指向本机 6039 端口)
|
||||
ENV UPSTREAM_URL=http://127.0.0.1:6039
|
||||
|
||||
# 复制 Nginx 配置并使用 envsubst 替换环境变量
|
||||
COPY nginx.conf /etc/nginx/templates/default.conf.template
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 5137
|
||||
|
||||
# 启动 Nginx(docker-compose 中可通过环境变量 UPSTREAM_URL 覆盖后端地址)
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
149
hzhub-portal-employee/README.md
Normal file
@@ -0,0 +1,149 @@
|
||||
# HZHub 员工门户
|
||||
|
||||
<div align="center">
|
||||
|
||||
### 企业员工门户系统
|
||||
|
||||
*HZHub 企业员工门户,提供审批流程、销售CRM、BI报表、AI应用集成等功能*
|
||||
|
||||
**[后端服务](https://github.com/hzhub/hzhub-ai)** | **[管理后台](https://github.com/hzhub/hzhub-admin)** | **[经销商门户](https://github.com/hzhub/hzhub-portal-dealer)**
|
||||
|
||||
</div>
|
||||
|
||||
## 功能特性
|
||||
|
||||
- 🔄 **审批流程**: 支持多级审批、流程管理
|
||||
- 📊 **BI报表**: 实时数据分析、可视化报表
|
||||
- 🤝 **CRM管理**: 客户管理、销售跟踪
|
||||
- 🤖 **AI应用**: AI助手、智能问答
|
||||
- 📱 **企业微信**: 支持企业微信H5集成
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **框架**: Vue 3 + TypeScript
|
||||
- **UI组件**: Element Plus
|
||||
- **状态管理**: Pinia
|
||||
- **构建工具**: Vite
|
||||
- **HTTP请求**: hook-fetch (支持SSE流式传输)
|
||||
|
||||
## Docker 部署
|
||||
|
||||
### 一键启动(推荐)
|
||||
|
||||
```bash
|
||||
cd hzhub-deploy
|
||||
docker-compose up -d
|
||||
|
||||
# 访问员工门户
|
||||
# 地址: http://localhost:5137
|
||||
```
|
||||
|
||||
### 单独部署
|
||||
|
||||
```bash
|
||||
# 进入项目目录
|
||||
cd hzhub-portal-employee
|
||||
|
||||
# 构建并启动
|
||||
docker-compose up -d --build
|
||||
```
|
||||
|
||||
### 服务端口
|
||||
|
||||
| 服务 | 端口 | 说明 |
|
||||
|------|------|------|
|
||||
| 员工门户 | 5137 | 员工前端访问地址 |
|
||||
| 经销商门户 | 5138 | 经销商前端访问地址 |
|
||||
| 管理后台 | 5666 | 管理后台访问地址 |
|
||||
| 后端服务 | 6039 | 后端 API 服务 |
|
||||
|
||||
## 本地开发
|
||||
|
||||
```bash
|
||||
# 安装依赖
|
||||
pnpm install
|
||||
|
||||
# 启动开发服务器
|
||||
pnpm dev
|
||||
|
||||
# 类型检查
|
||||
pnpm build
|
||||
|
||||
# 代码检查
|
||||
pnpm lint
|
||||
```
|
||||
|
||||
## 环境变量配置
|
||||
|
||||
项目根目录 `.env.development` 文件:
|
||||
|
||||
```bash
|
||||
VITE_API_URL= # 后端API地址(默认通过Vite代理)
|
||||
VITE_CLIENT_ID=e5cd7e4891bf95d1d19206ce24a7b32e
|
||||
VITE_WEB_TITLE=Employee Portal 企业员工门户
|
||||
```
|
||||
|
||||
## 登录超时配置
|
||||
|
||||
员工门户默认登录超时时间为30分钟,可通过修改数据库配置调整为1小时或其他时长。
|
||||
|
||||
**详细配置说明**: [TIMEOUT_CONFIG.md](./TIMEOUT_CONFIG.md)
|
||||
|
||||
**快速修改**:
|
||||
```bash
|
||||
# 执行SQL更新超时时间为1小时
|
||||
mysql -u root -phzhub123 hzhub < /tmp/update_timeout_1hour.sql
|
||||
```
|
||||
|
||||
**注意**: 修改后需要重新登录才能生效。
|
||||
|
||||
## 多租户支持
|
||||
|
||||
系统支持多租户架构,不同公司实体(集团总部、汇亚公司、恒福公司、玛缇公司)通过登录时选择不同租户进行数据隔离。
|
||||
|
||||
### 登录方式
|
||||
|
||||
1. **URL参数方式**: `http://localhost:5137/login?tenant=000002`
|
||||
2. **手动选择方式**: 在登录界面选择公司
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
hzhub-portal-employee/
|
||||
├── src/
|
||||
│ ├── api/ # API模块
|
||||
│ ├── components/ # 可复用组件
|
||||
│ ├── layouts/ # 布局组件
|
||||
│ ├── pages/ # 页面组件
|
||||
│ ├── routers/ # 路由配置
|
||||
│ ├── stores/ # Pinia状态管理
|
||||
│ ├── styles/ # 样式文件
|
||||
│ └── utils/ # 工具函数
|
||||
├── public/ # 静态资源
|
||||
├── .env.development # 开发环境配置
|
||||
├── Dockerfile # Docker构建文件
|
||||
├── nginx.conf # Nginx配置
|
||||
└── package.json # 项目配置
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
**Q: 如何切换不同公司登录?**
|
||||
|
||||
A: 通过URL参数 `?tenant=租户ID` 或在登录页面手动选择公司。
|
||||
|
||||
**Q: 如何连接后端服务?**
|
||||
|
||||
A: 开发环境通过Vite代理,生产环境通过Nginx代理配置 `UPSTREAM_URL`。
|
||||
|
||||
## 开源协议
|
||||
|
||||
本项目采用 **MIT 开源协议**。
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
|
||||
*用 ❤️ 打造,由 HZHub 团队维护*
|
||||
|
||||
</div>
|
||||
150
hzhub-portal-employee/TIMEOUT_CONFIG.md
Normal file
@@ -0,0 +1,150 @@
|
||||
# 员工门户登录超时配置说明
|
||||
|
||||
## 问题需求
|
||||
|
||||
修改员工门户(hzhub-portal-employee)的登录超时时间为1小时,超时后自动退出。
|
||||
|
||||
## 配置原理
|
||||
|
||||
### Token超时机制
|
||||
|
||||
HZHub使用Sa-Token进行认证管理,token超时时间由 `sys_client` 表的 `active_timeout` 字段控制:
|
||||
|
||||
| 字段 | 说明 | 单位 | 默认值 |
|
||||
|------|------|------|--------|
|
||||
| `active_timeout` | token活跃超时时间 | 秒 | 1800 (30分钟) |
|
||||
| `timeout` | token固定超时时间 | 秒 | 604800 (7天) |
|
||||
|
||||
**区别说明:**
|
||||
- **active_timeout**: 用户活跃超时,用户在指定时间内无操作则token失效
|
||||
- **timeout**: token绝对超时,从登录开始计时,无论是否活跃都将在该时间后失效
|
||||
|
||||
### 客户端配置
|
||||
|
||||
员工门户使用的客户端ID: `e5cd7e4891bf95d1d19206ce24a7b32e`(来自 .env.development)
|
||||
|
||||
## 修改方案
|
||||
|
||||
### 方法1:执行SQL更新(推荐)
|
||||
|
||||
执行以下SQL脚本将员工门户的超时时间修改为1小时:
|
||||
|
||||
```bash
|
||||
# 连接数据库
|
||||
mysql -u root -phzhub123
|
||||
|
||||
# 执行更新SQL
|
||||
source /tmp/update_employee_timeout.sql
|
||||
```
|
||||
|
||||
或手动执行SQL:
|
||||
|
||||
```sql
|
||||
-- 查询当前配置
|
||||
SELECT id, client_id, client_key, active_timeout, timeout, status
|
||||
FROM sys_client
|
||||
WHERE client_id = 'e5cd7e4891bf95d1d19206ce24a7b32e';
|
||||
|
||||
-- 更新为1小时超时
|
||||
UPDATE sys_client
|
||||
SET active_timeout = 3600
|
||||
WHERE client_id = 'e5cd7e4891bf95d1d19206ce24a7b32e';
|
||||
|
||||
-- 验证结果
|
||||
SELECT id, client_id, client_key, active_timeout, timeout, status
|
||||
FROM sys_client
|
||||
WHERE client_id = 'e5cd7e4891bf95d1d19206ce24a7b32e';
|
||||
```
|
||||
|
||||
### 方法2:通过管理后台修改
|
||||
|
||||
1. 登录管理后台: http://localhost:5666
|
||||
2. 进入 "系统管理" -> "客户端管理"
|
||||
3. 找到 `client_id = e5cd7e4891bf95d1d19206ce24a7b32e` 的记录
|
||||
4. 编辑该记录,将 `活跃超时时间` 修改为 `3600`
|
||||
5. 保存
|
||||
|
||||
## 超时时间建议
|
||||
|
||||
根据不同场景,建议的超时时间配置:
|
||||
|
||||
| 场景 | active_timeout | timeout | 说明 |
|
||||
|------|----------------|---------|------|
|
||||
| 内部员工 | 3600 (1小时) | 604800 (7天) | 平衡安全性和便利性 |
|
||||
| 移动端APP | 7200 (2小时) | 604800 (7天) | 移动端使用频率高 |
|
||||
| 公共终端 | 1800 (30分钟) | 86400 (1天) | 安全性优先 |
|
||||
| 超级管理员 | 1800 (30分钟) | 86400 (1天) | 高安全性要求 |
|
||||
|
||||
## 验证配置生效
|
||||
|
||||
修改后需要**重新登录**才能生效:
|
||||
|
||||
1. 清除浏览器缓存或使用隐身模式
|
||||
2. 访问员工门户登录页面
|
||||
3. 登录后等待1小时不操作
|
||||
4. 再次访问应自动跳转到登录页面
|
||||
|
||||
## 技术实现
|
||||
|
||||
### 后端实现
|
||||
|
||||
登录时通过 `SaLoginParameter.setActiveTimeout()` 设置:
|
||||
|
||||
```java
|
||||
// PasswordAuthStrategy.java
|
||||
SaLoginParameter model = new SaLoginParameter();
|
||||
model.setActiveTimeout(client.getActiveTimeout()); // 从sys_client表读取
|
||||
LoginHelper.login(loginUser, model);
|
||||
```
|
||||
|
||||
### 前端响应
|
||||
|
||||
前端通过以下方式检测token过期:
|
||||
|
||||
1. **API响应401**: 拦截器检测到401状态码,自动跳转登录页
|
||||
2. **路由守卫**: 访问页面时检查token是否有效
|
||||
3. **本地存储**: Pinia持久化存储token,过期后自动清除
|
||||
|
||||
## 相关配置文件
|
||||
|
||||
- **后端配置**: `/data/hzhub/hzhub-ai/hzhub-common/hzhub-common-satoken/src/main/resources/common-satoken.yml`
|
||||
- `dynamic-active-timeout: true` - 允许动态设置token有效期
|
||||
|
||||
- **前端配置**: `/data/hzhub/hzhub-portal-employee/.env.development`
|
||||
- `VITE_CLIENT_ID=e5cd7e4891bf95d1d19206ce24a7b32e` - 客户端ID
|
||||
|
||||
- **数据库表**: `sys_client`
|
||||
- `active_timeout` - 活跃超时时间(秒)
|
||||
- `timeout` - 固定超时时间(秒)
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q: 修改后没有生效?
|
||||
|
||||
A: 需要**重新登录**。已登录的token不会立即失效,只有新登录的token才会使用新的超时时间。
|
||||
|
||||
### Q: 如何测试超时是否正确?
|
||||
|
||||
A:
|
||||
1. 登录系统
|
||||
2. 等待设定的超时时间(如1小时)
|
||||
3. 不进行任何操作
|
||||
4. 刷新页面或访问API,应自动跳转到登录页
|
||||
|
||||
### Q: 可以设置为永不过期吗?
|
||||
|
||||
A: 不建议。为了安全考虑,应该设置合理的超时时间。如需延长,可以设置为更大的值:
|
||||
|
||||
```sql
|
||||
-- 设置为8小时
|
||||
UPDATE sys_client SET active_timeout = 28800 WHERE client_id = 'e5cd7e4891bf95d1d19206ce24a7b32e';
|
||||
|
||||
-- 设置为12小时
|
||||
UPDATE sys_client SET active_timeout = 43200 WHERE client_id = 'e5cd7e4891bf95d1d19206ce24a7b32e';
|
||||
```
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [Sa-Token官方文档](https://sa-token.cc/)
|
||||
- [HZHub认证流程](../docs/architecture/README.md)
|
||||
- [服务管理脚本](./SERVICE_MANAGEMENT.md)
|
||||
181
hzhub-portal-employee/docker-compose-all.yaml
Normal file
@@ -0,0 +1,181 @@
|
||||
# HZHub-AI 一键启动全部服务
|
||||
# 使用方式: docker-compose up -d
|
||||
#
|
||||
# 包含服务:
|
||||
# - MySQL 8.0 (数据库,包含初始化SQL)
|
||||
# - Redis 6.2 (缓存)
|
||||
# - Weaviate (向量数据库)
|
||||
# - MinIO (对象存储)
|
||||
# - HZHub-Backend (后端服务)
|
||||
# - HZHub-Admin (管理端前端)
|
||||
# - HZHub-Web (用户端前端)
|
||||
#
|
||||
# 镜像仓库地址: crpi-31mraxd99y2gqdgr.cn-beijing.personal.cr.aliyuncs.com/ruoyi_ai
|
||||
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
# ==================== MySQL 数据库 ====================
|
||||
mysql:
|
||||
# 阿里云镜像地址(包含初始化SQL)
|
||||
image: crpi-31mraxd99y2gqdgr.cn-beijing.personal.cr.aliyuncs.com/ruoyi_ai/mysql:v3
|
||||
container_name: hzhub-ai-mysql
|
||||
restart: always
|
||||
ports:
|
||||
- "23306:3306"
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
MYSQL_DATABASE: hzhub-ai-agent
|
||||
TZ: Asia/Shanghai
|
||||
volumes:
|
||||
- mysql-data:/var/lib/mysql
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-proot"]
|
||||
interval: 15s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
start_period: 60s
|
||||
networks:
|
||||
- ruoyi-net
|
||||
|
||||
# ==================== Redis 缓存 ====================
|
||||
redis:
|
||||
image: crpi-31mraxd99y2gqdgr.cn-beijing.personal.cr.aliyuncs.com/ruoyi_ai/redis:6.2
|
||||
container_name: hzhub-ai-redis
|
||||
restart: always
|
||||
ports:
|
||||
- "26379:6379"
|
||||
volumes:
|
||||
- redis-data:/data
|
||||
command: redis-server --appendonly yes
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
networks:
|
||||
- ruoyi-net
|
||||
|
||||
# ==================== Weaviate 向量数据库 ====================
|
||||
weaviate:
|
||||
image: crpi-31mraxd99y2gqdgr.cn-beijing.personal.cr.aliyuncs.com/ruoyi_ai/weaviate:1.30.0
|
||||
container_name: hzhub-ai-weaviate
|
||||
restart: always
|
||||
ports:
|
||||
- "28080:8080"
|
||||
environment:
|
||||
QUERY_DEFAULTS_LIMIT: 25
|
||||
AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: true
|
||||
PERSISTENCE_DATA_PATH: /var/lib/weaviate
|
||||
DEFAULT_VECTORIZER_MODULE: none
|
||||
ENABLE_MODULES: text2vec-cohere,text2vec-huggingface,text2vec-palm,text2vec-openai,generative-openai,generative-cohere,generative-palm,ref2vec-centroid,reranker-cohere,qna-openai
|
||||
CLUSTER_HOSTNAME: node1
|
||||
volumes:
|
||||
- weaviate-data:/var/lib/weaviate
|
||||
networks:
|
||||
- ruoyi-net
|
||||
|
||||
# ==================== MinIO 对象存储 ====================
|
||||
minio:
|
||||
image: crpi-31mraxd99y2gqdgr.cn-beijing.personal.cr.aliyuncs.com/ruoyi_ai/minio:latest
|
||||
container_name: hzhub-ai-minio
|
||||
restart: always
|
||||
ports:
|
||||
- "29000:9000"
|
||||
- "29090:9090"
|
||||
environment:
|
||||
MINIO_ROOT_USER: ruoyi
|
||||
MINIO_ROOT_PASSWORD: ruoyi123
|
||||
volumes:
|
||||
- minio-data:/data
|
||||
command: server /data --console-address ":9090"
|
||||
networks:
|
||||
- ruoyi-net
|
||||
|
||||
# ==================== HZHub-AI 后端服务 ====================
|
||||
backend:
|
||||
image: crpi-31mraxd99y2gqdgr.cn-beijing.personal.cr.aliyuncs.com/ruoyi_ai/hzhub-ai-backend:latest
|
||||
container_name: hzhub-ai-backend
|
||||
restart: always
|
||||
ports:
|
||||
- "26039:6039"
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
# MySQL 配置
|
||||
SPRING_DATASOURCE_DYNAMIC_PRIMARY: master
|
||||
SPRING_DATASOURCE_DYNAMIC_DATASOURCE_MASTER_DRIVERCLASSNAME: com.mysql.cj.jdbc.Driver
|
||||
SPRING_DATASOURCE_DYNAMIC_DATASOURCE_MASTER_URL: jdbc:mysql://mysql:3306/hzhub-ai-agent?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
|
||||
SPRING_DATASOURCE_DYNAMIC_DATASOURCE_MASTER_USERNAME: root
|
||||
SPRING_DATASOURCE_DYNAMIC_DATASOURCE_MASTER_PASSWORD: root
|
||||
# Redis 配置
|
||||
SPRING_DATA_REDIS_HOST: redis
|
||||
SPRING_DATA_REDIS_PORT: 6379
|
||||
SPRING_DATA_REDIS_DATABASE: 0
|
||||
# 日志配置
|
||||
LOGGING_LEVEL_ORG_RUOYI: info
|
||||
LOGGING_LEVEL_ORG_SPRINGFRAMEWORK: warn
|
||||
SYS_UPLOAD_PATH: /ruoyi/upload
|
||||
volumes:
|
||||
- logs-data:/ruoyi/server/logs
|
||||
- upload-data:/ruoyi/upload
|
||||
depends_on:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_started
|
||||
networks:
|
||||
- ruoyi-net
|
||||
|
||||
# ==================== HZHub-AI 管理端前端 ====================
|
||||
admin-frontend:
|
||||
image: crpi-31mraxd99y2gqdgr.cn-beijing.personal.cr.aliyuncs.com/ruoyi_ai/hzhub-ai-admin:latest
|
||||
container_name: hzhub-ai-admin
|
||||
restart: always
|
||||
ports:
|
||||
- "25666:5666"
|
||||
environment:
|
||||
# 后端 API 地址 - 运行时动态配置(无需重新构建镜像)
|
||||
# 在完整 docker-compose 中应设置为: http://backend:6039
|
||||
# 独立运行时可设置为实际后端地址,如: 192.168.1.100:6039
|
||||
UPSTREAM_HOST: backend:6039
|
||||
# 资源限制 - 防止 CPU 和内存耗尽
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2'
|
||||
memory: 3G
|
||||
reservations:
|
||||
cpus: '1'
|
||||
memory: 1G
|
||||
depends_on:
|
||||
- backend
|
||||
networks:
|
||||
- ruoyi-net
|
||||
|
||||
# ==================== HZHub-AI 用户端前端 ====================
|
||||
web-frontend:
|
||||
image: crpi-31mraxd99y2gqdgr.cn-beijing.personal.cr.aliyuncs.com/ruoyi_ai/hzhub-ai-web:latest
|
||||
container_name: hzhub-ai-web
|
||||
restart: always
|
||||
ports:
|
||||
- "25137:5137"
|
||||
environment:
|
||||
UPSTREAM_URL: http://backend:6039
|
||||
depends_on:
|
||||
- backend
|
||||
networks:
|
||||
- ruoyi-net
|
||||
|
||||
# ==================== 网络配置 ====================
|
||||
networks:
|
||||
ruoyi-net:
|
||||
driver: bridge
|
||||
|
||||
# ==================== 数据卷配置 ====================
|
||||
volumes:
|
||||
mysql-data:
|
||||
redis-data:
|
||||
weaviate-data:
|
||||
minio-data:
|
||||
logs-data:
|
||||
upload-data:
|
||||
24
hzhub-portal-employee/docker-compose.yml
Normal file
@@ -0,0 +1,24 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
# ==================== HZHub-AI 前端服务 ====================
|
||||
frontend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.frontend
|
||||
container_name: hzhub-ai-web
|
||||
restart: always
|
||||
ports:
|
||||
- "5137:5137"
|
||||
environment:
|
||||
# 后端 API 地址 - 运行时通过 nginx 代理
|
||||
# 在完整 docker-compose 中应设置为: http://backend:6039
|
||||
# 独立运行时可设置为实际后端地址
|
||||
UPSTREAM_URL: http://host.docker.internal:26039
|
||||
networks:
|
||||
- ruoyi-net
|
||||
|
||||
# ==================== 网络配置 ====================
|
||||
networks:
|
||||
ruoyi-net:
|
||||
driver: bridge
|
||||
169
hzhub-portal-employee/docs/COMPANY_NAME_CONFIG.md
Normal file
@@ -0,0 +1,169 @@
|
||||
# 动态公司名称配置说明
|
||||
|
||||
## 修改内容
|
||||
|
||||
已将员工门户的左上角标题从固定的"HZHub 企业员工门户"改为动态显示"登录用户对应公司名称 + 企业员工门户"。
|
||||
|
||||
## 修改文件
|
||||
|
||||
### 1. Aside侧边栏组件
|
||||
|
||||
**文件**: `src/layouts/components/Aside/index.vue`
|
||||
|
||||
**修改内容**:
|
||||
- 引入 `useUserStore` 获取用户信息
|
||||
- 创建 `tenantNameMap` 映射租户ID到公司名称
|
||||
- 使用 `computed` 计算属性动态获取公司名称
|
||||
- 将固定的 "HZHub" 改为动态的 `{{ companyName }}`
|
||||
|
||||
**关键代码**:
|
||||
```typescript
|
||||
import { useUserStore } from '@/stores';
|
||||
import { computed } from 'vue';
|
||||
|
||||
const userStore = useUserStore();
|
||||
|
||||
// 租户ID到公司名称的映射
|
||||
const tenantNameMap: Record<string, string> = {
|
||||
'000001': '集团总部',
|
||||
'000002': '汇亚公司',
|
||||
'000003': '恒福公司',
|
||||
'000004': '玛缇公司',
|
||||
};
|
||||
|
||||
// 计算当前用户的公司名称
|
||||
const companyName = computed(() => {
|
||||
const tenantId = userStore.userInfo?.tenantId;
|
||||
return tenantId ? tenantNameMap[tenantId] || 'HZHub' : 'HZHub';
|
||||
});
|
||||
```
|
||||
|
||||
### 2. 登录页面
|
||||
|
||||
**文件**: `src/pages/login/index.vue`
|
||||
|
||||
**修改内容**:
|
||||
- 从URL参数获取 `tenant` 参数
|
||||
- 使用相同的 `tenantNameMap` 映射
|
||||
- 根据URL参数动态显示公司名称
|
||||
|
||||
**关键代码**:
|
||||
```typescript
|
||||
import { useRoute } from 'vue-router';
|
||||
import { computed } from 'vue';
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 从URL参数获取租户ID,并映射到公司名称
|
||||
const companyName = computed(() => {
|
||||
const tenantId = route.query.tenant as string;
|
||||
return tenantId ? tenantNameMap[tenantId] || 'HZHub' : 'HZHub';
|
||||
});
|
||||
```
|
||||
|
||||
## 公司名称映射关系
|
||||
|
||||
| 租户ID | 公司名称 | 登录URL示例 |
|
||||
|--------|----------|-------------|
|
||||
| 000001 | 集团总部 | http://localhost:5137/login?tenant=000001 |
|
||||
| 000002 | 汇亚公司 | http://localhost:5137/login?tenant=000002 |
|
||||
| 000003 | 恒福公司 | http://localhost:5137/login?tenant=000003 |
|
||||
| 000004 | 玛缇公司 | http://localhost:5137/login?tenant=000004 |
|
||||
|
||||
## 显示效果
|
||||
|
||||
### 登录页面
|
||||
- **集团总部用户**: 左上角显示 "集团总部 企业员工门户"
|
||||
- **汇亚公司用户**: 左上角显示 "汇亚公司 企业员工门户"
|
||||
- **恒福公司用户**: 左上角显示 "恒福公司 企业员工门户"
|
||||
- **玛缇公司用户**: 左上角显示 "玛缇公司 企业员工门户"
|
||||
|
||||
### 主界面侧边栏
|
||||
登录后,侧边栏顶部会显示对应公司的名称:
|
||||
```
|
||||
[公司名称]
|
||||
企业员工门户
|
||||
```
|
||||
|
||||
## 扩展性
|
||||
|
||||
### 添加新公司
|
||||
|
||||
如果需要添加新的公司,只需在两个组件的 `tenantNameMap` 中添加映射:
|
||||
|
||||
```typescript
|
||||
const tenantNameMap: Record<string, string> = {
|
||||
'000001': '集团总部',
|
||||
'000002': '汇亚公司',
|
||||
'000003': '恒福公司',
|
||||
'000004': '玛缇公司',
|
||||
'000005': '新公司名称', // 添加新公司
|
||||
};
|
||||
```
|
||||
|
||||
### 从后端获取公司名称
|
||||
|
||||
如果需要从后端动态获取公司名称,可以:
|
||||
|
||||
1. **修改 LoginUser 类型**,添加 `companyName` 字段:
|
||||
```typescript
|
||||
export interface LoginUser {
|
||||
// ... 现有字段
|
||||
companyName?: string; // 新增公司名称字段
|
||||
}
|
||||
```
|
||||
|
||||
2. **修改 Aside 组件**,直接使用 `companyName`:
|
||||
```typescript
|
||||
const companyName = computed(() => {
|
||||
return userStore.userInfo?.companyName || 'HZHub';
|
||||
});
|
||||
```
|
||||
|
||||
3. **后端接口**:在登录接口返回用户信息时,包含 `companyName` 字段
|
||||
|
||||
## 测试验证
|
||||
|
||||
### 测试步骤
|
||||
|
||||
1. **登录页面测试**:
|
||||
- 访问 `http://localhost:5137/login?tenant=000002`
|
||||
- 检查登录页面左上角是否显示 "汇亚公司 企业员工门户"
|
||||
|
||||
2. **主界面测试**:
|
||||
- 使用不同公司的账号登录
|
||||
- 检查侧边栏顶部是否显示对应公司名称
|
||||
|
||||
3. **切换租户测试**:
|
||||
- 登录汇亚公司账号
|
||||
- 退出登录
|
||||
- 用恒福公司账号登录
|
||||
- 检查标题是否正确切换
|
||||
|
||||
### 预期结果
|
||||
|
||||
- ✅ 登录页面根据URL参数显示对应公司名称
|
||||
- ✅ 主界面侧边栏显示登录用户的公司名称
|
||||
- ✅ 切换不同公司登录时,标题正确更新
|
||||
- ✅ 未识别的租户ID显示为默认值 "HZHub"
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **租户ID一致性**:确保 `tenantId` 与数据库 `sys_tenant` 表中的 `tenant_id` 字段一致
|
||||
2. **映射完整性**:所有可能的租户ID都应该在 `tenantNameMap` 中有对应映射
|
||||
3. **默认值**:未识别的租户ID会显示默认值 "HZHub"
|
||||
4. **性能优化**:使用 `computed` 计算属性,避免不必要的重复计算
|
||||
|
||||
## 相关文件
|
||||
|
||||
- `src/layouts/components/Aside/index.vue` - 侧边栏组件
|
||||
- `src/pages/login/index.vue` - 登录页面
|
||||
- `src/stores/modules/user.ts` - 用户状态管理
|
||||
- `src/api/auth/types.ts` - 用户类型定义
|
||||
|
||||
## 后续优化建议
|
||||
|
||||
1. **统一管理映射关系**:将 `tenantNameMap` 提取到统一的配置文件
|
||||
2. **支持多语言**:添加国际化支持,显示不同语言的公司名称
|
||||
3. **动态获取**:从后端API动态获取公司名称,避免硬编码
|
||||
4. **Logo定制**:不同公司使用不同的Logo图标
|
||||
198
hzhub-portal-employee/docs/PROFILE_FEATURE.md
Normal file
@@ -0,0 +1,198 @@
|
||||
# 个人中心功能说明
|
||||
|
||||
## 功能概述
|
||||
|
||||
已将 `hzhub-admin` 中的个人中心功能移植到 `hzhub-portal-employee`,并进行了以下修改:
|
||||
|
||||
1. **移除收藏夹菜单项**
|
||||
2. **将"设置"改为"个人中心"**
|
||||
3. **移植个人中心页面和功能**
|
||||
|
||||
## 主要功能
|
||||
|
||||
### 1. 个人信息展示
|
||||
|
||||
- 用户头像(支持上传更换)
|
||||
- 用户昵称、账号
|
||||
- 手机号码、邮箱
|
||||
- 所属部门和岗位
|
||||
- 角色信息
|
||||
- 上次登录时间
|
||||
|
||||
### 2. 基本设置
|
||||
|
||||
- 修改昵称
|
||||
- 修改邮箱
|
||||
- 修改性别
|
||||
- 修改手机号
|
||||
|
||||
### 3. 安全设置
|
||||
|
||||
- 修改密码
|
||||
- 密码强度验证
|
||||
- 确认密码校验
|
||||
|
||||
## 技术实现
|
||||
|
||||
### UI框架适配
|
||||
|
||||
由于 `hzhub-admin` 使用 **Ant Design Vue**,而 `hzhub-portal-employee` 使用 **Element Plus**,进行了以下适配:
|
||||
|
||||
| Ant Design Vue | Element Plus | 说明 |
|
||||
|----------------|--------------|------|
|
||||
| `<a-card>` | `<el-card>` | 卡片容器 |
|
||||
| `<a-descriptions>` | `<el-descriptions>` | 描述列表 |
|
||||
| `<a-tabs>` | `<el-tabs>` | 标签页 |
|
||||
| `<a-form>` | `<el-form>` | 表单 |
|
||||
| `<a-upload>` | `<el-upload>` | 文件上传 |
|
||||
|
||||
### 文件结构
|
||||
|
||||
```
|
||||
src/
|
||||
├── api/
|
||||
│ └── profile/
|
||||
│ ├── index.ts # API接口
|
||||
│ └── types.ts # 类型定义
|
||||
├── pages/
|
||||
│ └── profile/
|
||||
│ ├── index.vue # 个人中心主页面
|
||||
│ └── components/
|
||||
│ ├── BasicSetting.vue # 基本设置组件
|
||||
│ └── SecuritySetting.vue # 安全设置组件
|
||||
└── layouts/
|
||||
└── components/
|
||||
└── Header/
|
||||
└── UserDropdown.vue # 用户下拉菜单(已修改)
|
||||
```
|
||||
|
||||
## 使用说明
|
||||
|
||||
### 访问个人中心
|
||||
|
||||
1. **通过下拉菜单**:点击右上角用户头像 → 选择"个人中心"
|
||||
2. **直接访问**:访问 `/profile` 路由
|
||||
|
||||
### 修改个人信息
|
||||
|
||||
1. 进入个人中心页面
|
||||
2. 在"基本设置"标签页修改信息
|
||||
3. 点击"保存修改"按钮
|
||||
|
||||
### 修改密码
|
||||
|
||||
1. 进入个人中心页面
|
||||
2. 切换到"安全设置"标签页
|
||||
3. 输入当前密码和新密码
|
||||
4. 点击"修改密码"按钮
|
||||
5. 修改成功后需要重新登录
|
||||
|
||||
### 更换头像
|
||||
|
||||
1. 点击头像区域的"更换头像"
|
||||
2. 选择图片文件(支持 JPG、PNG 格式,最大 2MB)
|
||||
3. 上传成功后自动更新显示
|
||||
|
||||
## API接口
|
||||
|
||||
### 获取用户信息
|
||||
```typescript
|
||||
GET /system/user/profile
|
||||
Response: UserProfile
|
||||
```
|
||||
|
||||
### 更新用户信息
|
||||
```typescript
|
||||
PUT /system/user/profile
|
||||
Request: {
|
||||
userId: number
|
||||
nickName: string
|
||||
email: string
|
||||
phonenumber: string
|
||||
sex: string
|
||||
}
|
||||
```
|
||||
|
||||
### 修改密码
|
||||
```typescript
|
||||
PUT /system/user/profile/updatePwd
|
||||
Request: {
|
||||
oldPassword: string
|
||||
newPassword: string
|
||||
}
|
||||
```
|
||||
|
||||
### 上传头像
|
||||
```typescript
|
||||
POST /system/user/profile/avatar
|
||||
Request: FormData (avatarfile: File)
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **头像上传限制**:
|
||||
- 仅支持图片格式(JPG、PNG等)
|
||||
- 文件大小不超过 2MB
|
||||
|
||||
2. **密码修改**:
|
||||
- 新密码长度 6-20 个字符
|
||||
- 需要输入确认密码
|
||||
- 修改成功后需要重新登录
|
||||
|
||||
3. **表单验证**:
|
||||
- 邮箱格式验证
|
||||
- 手机号格式验证(中国大陆手机号)
|
||||
- 必填项验证
|
||||
|
||||
4. **实时更新**:
|
||||
- 修改个人信息后,右上角用户信息会同步更新
|
||||
- 头像更新后会立即刷新显示
|
||||
|
||||
## 与 hzhub-admin 的差异
|
||||
|
||||
### 保留的功能
|
||||
|
||||
- ✅ 个人信息展示
|
||||
- ✅ 基本设置(修改昵称、邮箱、性别、手机号)
|
||||
- ✅ 安全设置(修改密码)
|
||||
- ✅ 头像上传
|
||||
|
||||
### 简化的功能
|
||||
|
||||
- ❌ 移除"账号绑定"功能(暂不需要)
|
||||
- ❌ 移除"在线设备"功能(暂不需要)
|
||||
- ⚠️ 简化了表单验证(使用Element Plus原生验证)
|
||||
|
||||
## 后续优化建议
|
||||
|
||||
1. **头像裁剪**:添加图片裁剪功能
|
||||
2. **更多安全选项**:
|
||||
- 两步验证
|
||||
- 登录日志查看
|
||||
3. **个性化设置**:
|
||||
- 主题色切换
|
||||
- 字体大小调整
|
||||
4. **国际化**:支持多语言显示
|
||||
|
||||
## 测试建议
|
||||
|
||||
### 功能测试
|
||||
|
||||
1. **个人信息修改**:
|
||||
- 修改昵称并保存
|
||||
- 验证右上角显示是否更新
|
||||
|
||||
2. **头像上传**:
|
||||
- 上传符合要求的图片
|
||||
- 上传超大文件(应失败)
|
||||
- 上传非图片文件(应失败)
|
||||
|
||||
3. **密码修改**:
|
||||
- 输入错误旧密码(应失败)
|
||||
- 两次新密码不一致(应失败)
|
||||
- 正确修改密码并验证重新登录
|
||||
|
||||
### 兼容性测试
|
||||
|
||||
- Chrome、Firefox、Safari 浏览器测试
|
||||
- 响应式布局测试(桌面端、平板、移动端)
|
||||
358
hzhub-portal-employee/docs/TABBAR_FEATURE.md
Normal file
@@ -0,0 +1,358 @@
|
||||
# 标签页功能说明
|
||||
|
||||
## 功能概述
|
||||
|
||||
已将 `hzhub-admin` 中的标签页导航功能移植到 `hzhub-portal-employee`,使用 Element Plus 组件重新实现。
|
||||
|
||||
## 主要功能
|
||||
|
||||
### 1. 标签页管理
|
||||
|
||||
- 自动添加标签页:路由切换时自动创建对应标签页
|
||||
- 关闭标签页:点击关闭按钮关闭当前标签页
|
||||
- 固定标签页:固定后的标签页不可关闭,以不同颜色标识
|
||||
- 标签页持久化:标签页状态自动保存到本地存储
|
||||
|
||||
### 2. 工具栏功能
|
||||
|
||||
- 更多操作菜单:
|
||||
- 关闭其他标签页
|
||||
- 关闭右侧标签页
|
||||
- 关闭所有标签页
|
||||
- 全屏切换:切换浏览器全屏模式
|
||||
|
||||
### 3. 视觉效果
|
||||
|
||||
- 当前激活标签页高亮显示(主题色)
|
||||
- 固定标签页以警告色标识
|
||||
- 悬停显示关闭按钮
|
||||
- 平滑过渡动画
|
||||
- 标签页图标支持(可选)
|
||||
|
||||
## 技术实现
|
||||
|
||||
### UI 框架适配
|
||||
|
||||
从 `hzhub-admin` (Ant Design Vue) 到 `hzhub-portal-employee` (Element Plus) 的适配:
|
||||
|
||||
| 功能 | hzhub-admin | hzhub-portal-employee |
|
||||
|------|-------------|----------------------|
|
||||
| UI 框架 | Ant Design Vue + Vben | Element Plus |
|
||||
| 图标组件 | `<VbenIcon>` | `<el-icon>` |
|
||||
| 下拉菜单 | `<VbenDropdownMenu>` | `<el-dropdown>` |
|
||||
| CSS 系统 | TailwindCSS | SCSS + Element Plus 变量 |
|
||||
| 图标库 | Vben Icons | Element Plus Icons |
|
||||
|
||||
### 文件结构
|
||||
|
||||
```
|
||||
src/
|
||||
├── stores/
|
||||
│ ├── modules/
|
||||
│ │ └── tabbar.ts # 标签页状态管理
|
||||
│ └── index.ts # 导出 tabbar store
|
||||
├── hooks/
|
||||
│ └── useTabbar.ts # 标签页操作 Hook
|
||||
├── layouts/
|
||||
│ ├── components/
|
||||
│ │ └── TabsView/
|
||||
│ │ └── index.vue # 标签页视图组件
|
||||
│ └── LayoutVertical/
|
||||
│ └── index.vue # 集成标签页的布局
|
||||
└── routers/
|
||||
└ modules/
|
||||
└── staticRouter.ts # 路由配置
|
||||
```
|
||||
|
||||
### 核心代码
|
||||
|
||||
#### Tabbar Store
|
||||
|
||||
```typescript
|
||||
export interface TabItem {
|
||||
path: string; // 路由路径
|
||||
name: string; // 路由名称
|
||||
title: string; // 标签页标题
|
||||
icon?: string; // 图标
|
||||
affix?: boolean; // 是否固定
|
||||
query?: any; // 路由参数
|
||||
}
|
||||
|
||||
export const useTabbarStore = defineStore('tabbar', {
|
||||
state: (): TabbarState => ({
|
||||
tabs: [], // 标签页列表
|
||||
activeTab: '/', // 当前激活的标签页
|
||||
cachedTabs: new Set(), // 缓存的组件名称
|
||||
}),
|
||||
|
||||
actions: {
|
||||
addTab(route), // 添加标签页
|
||||
closeTab(path), // 关闭标签页
|
||||
closeOtherTabs(path), // 关闭其他标签页
|
||||
closeAllTabs(), // 关闭所有标签页
|
||||
closeRightTabs(path), // 关闭右侧标签页
|
||||
toggleAffixTab(path), // 固定/取消固定
|
||||
initAffixTabs(routes), // 初始化固定标签页
|
||||
},
|
||||
|
||||
persist: {
|
||||
key: 'hzhub-employee-tabs',
|
||||
paths: ['tabs', 'activeTab'],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
#### useTabbar Hook
|
||||
|
||||
```typescript
|
||||
export function useTabbar() {
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const tabbarStore = useTabbarStore();
|
||||
|
||||
// 监听路由变化,自动添加标签页
|
||||
watch(() => route.path, () => {
|
||||
if (route.name) {
|
||||
tabbarStore.addTab(route);
|
||||
}
|
||||
}, { immediate: true });
|
||||
|
||||
// 初始化固定标签页
|
||||
watch(() => router.getRoutes(), () => {
|
||||
tabbarStore.initAffixTabs(router.getRoutes());
|
||||
}, { immediate: true });
|
||||
|
||||
return {
|
||||
currentActive, // 当前激活的标签页
|
||||
currentTabs, // 标签页列表
|
||||
handleClick, // 点击标签页
|
||||
handleClose, // 关闭标签页
|
||||
handleUnpin, // 固定/取消固定
|
||||
closeOtherTabs, // 关闭其他标签页
|
||||
closeAllTabs, // 关闭所有标签页
|
||||
closeRightTabs, // 关闭右侧标签页
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
#### TabsView 组件
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useTabbarStore } from '@/stores';
|
||||
|
||||
const tabbarStore = useTabbarStore();
|
||||
const activeTabPath = computed(() => tabbarStore.activeTab);
|
||||
const tabs = computed(() => tabbarStore.tabs);
|
||||
const isFullscreen = ref(false);
|
||||
|
||||
// 点击标签页
|
||||
const handleTabClick = (tab: TabItem) => {
|
||||
tabbarStore.setActiveTab(tab.path);
|
||||
router.push(tab.path);
|
||||
};
|
||||
|
||||
// 关闭标签页
|
||||
const handleTabClose = (path: string) => {
|
||||
tabbarStore.closeTab(path);
|
||||
};
|
||||
|
||||
// 切换全屏
|
||||
const toggleFullscreen = () => {
|
||||
isFullscreen.value = !isFullscreen.value;
|
||||
if (isFullscreen.value) {
|
||||
document.documentElement.requestFullscreen();
|
||||
} else {
|
||||
document.exitFullscreen();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
#### 布局集成
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import TabsView from '@/layouts/components/TabsView/index.vue';
|
||||
import { useTabbar } from '@/hooks/useTabbar';
|
||||
|
||||
const { handleClose, handleUnpin } = useTabbar();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="layout">
|
||||
<Aside />
|
||||
<div class="main-area">
|
||||
<Header />
|
||||
<TabsView
|
||||
:show-icon="true"
|
||||
@close="handleClose"
|
||||
@unpin="handleUnpin"
|
||||
/>
|
||||
<main class="page-content">
|
||||
<router-view />
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
## 使用说明
|
||||
|
||||
### 固定标签页
|
||||
|
||||
在路由配置中添加 `meta.affix` 属性:
|
||||
|
||||
```typescript
|
||||
{
|
||||
path: '/dashboard',
|
||||
name: 'dashboard',
|
||||
meta: {
|
||||
title: '工作台',
|
||||
icon: 'Odometer',
|
||||
affix: true, // 固定标签页
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### 隐藏标签页
|
||||
|
||||
在路由配置中添加 `meta.isHide` 属性:
|
||||
|
||||
```typescript
|
||||
{
|
||||
path: '/profile',
|
||||
name: 'profile',
|
||||
meta: {
|
||||
title: '个人中心',
|
||||
isHide: '1', // 不显示在标签页中
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### 禁用缓存
|
||||
|
||||
在路由配置中添加 `meta.isKeepAlive` 属性:
|
||||
|
||||
```typescript
|
||||
{
|
||||
path: '/example',
|
||||
name: 'example',
|
||||
meta: {
|
||||
title: '示例页面',
|
||||
isKeepAlive: '0', // 不缓存组件
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## 样式定制
|
||||
|
||||
标签页样式使用 Element Plus CSS 变量系统:
|
||||
|
||||
- **激活状态**: `--el-color-primary` (主题色)
|
||||
- **固定状态**: `--el-color-warning` (警告色)
|
||||
- **悬停状态**: `--el-color-primary-light-9`
|
||||
- **背景色**: `--el-bg-color-page`
|
||||
- **边框色**: `--el-border-color-light`
|
||||
|
||||
可在 `src/styles/var.scss` 中自定义这些变量值。
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **标签页限制**:
|
||||
- 固定的标签页不能关闭
|
||||
- 至少保留一个标签页
|
||||
- 标签页标题过长会自动截断
|
||||
|
||||
2. **路由要求**:
|
||||
- 路由必须设置 `name` 属性
|
||||
- 路由必须设置 `meta.title` 属性
|
||||
- 隐藏路由(`isHide='1'`)不会添加到标签页
|
||||
|
||||
3. **持久化配置**:
|
||||
- 使用 `pinia-plugin-persistedstate` 自动保存
|
||||
- 保存路径:`['tabs', 'activeTab']`
|
||||
- 不保存 `cachedTabs` (组件缓存)
|
||||
|
||||
4. **性能优化**:
|
||||
- 使用 `keep-alive` 缓存组件
|
||||
- 标签页列表使用 `Set` 优化查找
|
||||
- 懒加载图标组件
|
||||
|
||||
## 与 hzhub-admin 的差异
|
||||
|
||||
### 保留的功能
|
||||
|
||||
- ✅ 标签页自动添加/关闭
|
||||
- ✅ 标签页固定/取消固定
|
||||
- ✅ 批量关闭操作
|
||||
- ✅ 标签页持久化
|
||||
- ✅ 全屏切换
|
||||
- ✅ 标签页图标显示
|
||||
|
||||
### 简化的功能
|
||||
|
||||
- ❌ 拖拽排序(暂未实现)
|
||||
- ❌ 滚轮滚动(暂未实现)
|
||||
- ❌ 中键关闭(暂未实现)
|
||||
- ❌ 右键菜单(暂未实现)
|
||||
- ❌ 多种标签页样式(plain/brisk/card)
|
||||
- ❌ 国际化标题切换
|
||||
|
||||
### 新增的功能
|
||||
|
||||
- ✅ 简化的工具栏设计
|
||||
- ✅ Element Plus 原生样式
|
||||
- ✅ 更简洁的代码结构
|
||||
|
||||
## 后续优化建议
|
||||
|
||||
1. **交互增强**:
|
||||
- 添加右键菜单功能
|
||||
- 支持拖拽排序标签页
|
||||
- 支持鼠标滚轮横向滚动
|
||||
- 支持中键点击关闭
|
||||
|
||||
2. **样式定制**:
|
||||
- 提供多种标签页样式(chrome/card/plain)
|
||||
- 支持自定义标签页主题色
|
||||
- 标签页宽度自适应
|
||||
|
||||
3. **功能扩展**:
|
||||
- 标签页分组功能
|
||||
- 标签页搜索功能
|
||||
- 最近访问的标签页历史
|
||||
- 标签页快捷键操作
|
||||
|
||||
4. **性能优化**:
|
||||
- 虚拟滚动优化大量标签页
|
||||
- 标签页懒加载优化
|
||||
- 组件缓存策略优化
|
||||
|
||||
## 测试建议
|
||||
|
||||
### 功能测试
|
||||
|
||||
1. **标签页管理**:
|
||||
- 切换路由自动添加标签页
|
||||
- 关闭标签页后自动切换到相邻标签页
|
||||
- 固定标签页不可关闭
|
||||
- 刷新后标签页状态保持
|
||||
|
||||
2. **批量操作**:
|
||||
- 关闭其他标签页
|
||||
- 关闭右侧标签页
|
||||
- 关闭所有标签页
|
||||
|
||||
3. **全屏功能**:
|
||||
- 点击全屏按钮进入全屏模式
|
||||
- 再次点击退出全屏模式
|
||||
|
||||
### 兼容性测试
|
||||
|
||||
- Chrome、Firefox、Safari 浏览器测试
|
||||
- 响应式布局测试(桌面端、平板)
|
||||
- 标签页滚动测试(超过 10 个标签页)
|
||||
- 固定标签页与普通标签页混排测试
|
||||
38
hzhub-portal-employee/eslint.config.js
Normal file
@@ -0,0 +1,38 @@
|
||||
import antfu from "@antfu/eslint-config";
|
||||
|
||||
// 使用 antfu 配置
|
||||
export default antfu({
|
||||
vue: {
|
||||
"vue/block-order": [
|
||||
"error",
|
||||
{
|
||||
// 块顺序
|
||||
order: ["script", "template", "style"],
|
||||
},
|
||||
],
|
||||
},
|
||||
typescript: true,
|
||||
stylistic: {
|
||||
indent: 2, // 缩进
|
||||
semi: true, // 语句分号
|
||||
quotes: "single", // 单引号
|
||||
},
|
||||
rules: {
|
||||
"new-cap": ["off", { newIsCap: true, capIsNew: false }],
|
||||
"no-console": "off", // 忽略console
|
||||
},
|
||||
ignores: [
|
||||
"**/dist/**",
|
||||
"**/node_modules/**",
|
||||
"**/build/**",
|
||||
"**/lib/**",
|
||||
"**/es/**",
|
||||
"**/types/**",
|
||||
"**/public/**",
|
||||
"**/vite.config.ts",
|
||||
"**/eslint.config.js",
|
||||
"./*.cjs",
|
||||
"./*.js",
|
||||
"./package.json",
|
||||
],
|
||||
});
|
||||
16
hzhub-portal-employee/index.html
Normal file
@@ -0,0 +1,16 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>%VITE_WEB_TITLE%</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
21
hzhub-portal-employee/license
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 hzhub
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
16
hzhub-portal-employee/logs.sh
Executable file
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
# HZHub Portal Employee 前端项目日志查看脚本
|
||||
# 功能:查看服务运行日志
|
||||
|
||||
PROJECT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
LOG_FILE="$PROJECT_DIR/logs/dev.log"
|
||||
|
||||
if [ ! -f "$LOG_FILE" ]; then
|
||||
echo "⚠️ 日志文件不存在: $LOG_FILE"
|
||||
echo "服务可能未运行或未产生日志"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "查看 hzhub-portal-employee 日志 (Ctrl+C 退出)"
|
||||
echo "========================================="
|
||||
tail -f "$LOG_FILE"
|
||||
75
hzhub-portal-employee/nginx.conf
Normal file
@@ -0,0 +1,75 @@
|
||||
server {
|
||||
listen 5137;
|
||||
server_name localhost;
|
||||
|
||||
# gzip 压缩
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1k;
|
||||
gzip_comp_level 6;
|
||||
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml;
|
||||
gzip_disable "MSIE [1-6]\.";
|
||||
|
||||
# 静态文件目录
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# 访问日志
|
||||
access_log /var/log/nginx/access.log;
|
||||
error_log /var/log/nginx/error.log;
|
||||
|
||||
# 前端路由 fallback
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# API 代理 - 代理所有后端 API 请求
|
||||
# 匹配常见的后端 API 路径前缀
|
||||
location ~ ^/(system|chat|auth|resource|agent|knowledge|workflow)/ {
|
||||
proxy_pass ${UPSTREAM_URL};
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# 超时设置
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
|
||||
# 支持 WebSocket
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
|
||||
# 兼容 /api 前缀的请求
|
||||
location /api {
|
||||
proxy_pass ${UPSTREAM_URL};
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# 超时设置
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
|
||||
# 支持 WebSocket
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
|
||||
# 静态资源缓存
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
expires 30d;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# 禁止访问隐藏文件
|
||||
location ~ /\. {
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
17297
hzhub-portal-employee/package-lock.json
generated
Normal file
88
hzhub-portal-employee/package.json
Normal file
@@ -0,0 +1,88 @@
|
||||
{
|
||||
"name": "hzhub-portal-employee",
|
||||
"type": "module",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"description": "HZHub Employee Portal - 企业员工门户系统",
|
||||
"author": {
|
||||
"name": "HZHub Team",
|
||||
"email": "hzhub@example.com",
|
||||
"url": "https://github.com/hzhub"
|
||||
},
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/hzhub/hzhub-portal-employee",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:hzhub/hzhub-portal-employee.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/hzhub/hzhub-portal-employee/issues"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc -b && vite build",
|
||||
"preview": "vite preview",
|
||||
"prepare": "husky",
|
||||
"lint": "eslint .",
|
||||
"lint:stylelint": "stylelint --cache --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
|
||||
"fix": "eslint . --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"@floating-ui/core": "^1.7.2",
|
||||
"@floating-ui/dom": "^1.7.2",
|
||||
"@floating-ui/vue": "^1.1.7",
|
||||
"@jsonlee_12138/enum": "^1.0.4",
|
||||
"@vueuse/core": "^13.5.0",
|
||||
"@vueuse/integrations": "^13.5.0",
|
||||
"echarts": "^6.0.0",
|
||||
"element-plus": "^2.10.4",
|
||||
"hook-fetch": "2.0.4-beta.1",
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^3.0.3",
|
||||
"pinia-plugin-persistedstate": "^4.4.1",
|
||||
"qrcode": "^1.5.4",
|
||||
"radash": "^12.1.1",
|
||||
"reset-css": "^5.0.2",
|
||||
"vue": "^3.5.17",
|
||||
"vue-element-plus-x": "1.3.0",
|
||||
"vue-router": "4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^4.16.2",
|
||||
"@changesets/cli": "^2.29.5",
|
||||
"@commitlint/config-conventional": "^19.8.1",
|
||||
"@vitejs/plugin-vue": "^6.0.0",
|
||||
"@vue/tsconfig": "^0.7.0",
|
||||
"commitlint": "^19.8.1",
|
||||
"cz-git": "^1.12.0",
|
||||
"eslint": "^9.31.0",
|
||||
"husky": "^9.1.7",
|
||||
"lint-staged": "^16.1.2",
|
||||
"postcss": "8.4.31",
|
||||
"postcss-html": "1.5.0",
|
||||
"prettier": "^3.6.2",
|
||||
"sass-embedded": "^1.89.2",
|
||||
"stylelint": "^16.21.1",
|
||||
"stylelint-config-html": "^1.1.0",
|
||||
"stylelint-config-recess-order": "^7.1.0",
|
||||
"stylelint-config-recommended-scss": "^15.0.1",
|
||||
"stylelint-config-recommended-vue": "^1.6.1",
|
||||
"stylelint-config-standard": "^38.0.0",
|
||||
"stylelint-config-standard-scss": "^15.0.1",
|
||||
"typescript": "~5.8.3",
|
||||
"typescript-api-pro": "^0.0.7",
|
||||
"unocss": "66.3.3",
|
||||
"unplugin-auto-import": "^19.3.0",
|
||||
"unplugin-vue-components": "^28.8.0",
|
||||
"vite": "6.0.0",
|
||||
"vite-plugin-env-typed": "^0.0.2",
|
||||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
"vue-tsc": "^3.0.1"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "node_modules/cz-git"
|
||||
}
|
||||
}
|
||||
}
|
||||
9935
hzhub-portal-employee/pnpm-lock.yaml
generated
Normal file
BIN
hzhub-portal-employee/public/favicon.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
35
hzhub-portal-employee/public/favicon.svg
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="32px" height="32px" viewBox="0 0 32 32" enable-background="new 0 0 32 32" xml:space="preserve"> <image id="image0" width="32" height="32" x="0" y="0"
|
||||
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAIGNIUk0AAHomAACAhAAA+gAAAIDo
|
||||
AAB1MAAA6mAAADqYAAAXcJy6UTwAAAAGYktHRAD/AP8A/6C9p5MAAAW1SURBVFjD7ZZdjF1VGYbf
|
||||
b6299t7n/8ycOZ1OO+m0FSkFKVjpBSogTY20EocwiT+ICojaqMC0kaBoMI0RApW2JNUE00TShGoI
|
||||
NcGCFrCmMaMQbYq2QinD37Sd/86cnjPn7N+11ue1sad2vOFmntu1kvfJu76sfMACC3zA0IUOpXTx
|
||||
9I+O4Ivb1uDlR6fgCBdWWTS5iYIq4Z8TRzCjInSvuhE6mAFlizCSEMgEe5/9FgY2bMPjm9chTcL/
|
||||
T+Dgw6dQ13VcmrsMk8nZjO9kSkZxPDp35Fyl+zqObBOzrgCrPMYmXqTysuvLRgovRFiPuqphNHkC
|
||||
5OexvX9Z2wznQgJj6Rl8quNaTMSNG8tO+QEjsFQIhD2dn3gjsno/rPkTMwDW67uXbhhIiC63knzj
|
||||
qnGOgsc6Fq8+VBt9DUQSzOb8LbcL33z7z7B+yWcwl4bLPOnvZYHxFGanFvaoFlhuHHl36nqfto77
|
||||
eSPEF1jgfQ3z61Ty7xNXrDau/HKkWy+IfKWu2MX7xw7PrwFrLTzhwcKuBUTBUPygA2c4EYzTyZnn
|
||||
ym7vrpTE3Y3mNITn7ZGuvU/45SBRjBDRSZD/knGctUTylEnT+T+BAIGIQEwZMCfamlYiDZo26VyU
|
||||
6X3YCOcma8ItJIVSufJgKq3WHDzI7NQE25YGYkOUISIYMNrntGFlXx+MsLCCR1hQTgqnr8XNUkbm
|
||||
dzsQm4yJ7kHg7zp9zmzXNr7XEN1s3dxuN4xKLFUfiHIMPsWwuHLtx9oKtJ2BidEYm9b1w8I0Jan+
|
||||
hHWVhL9RSrUhQfLNxaXi8zPxOFTBotDXfWJurjVsPPXdxJUrEpiPsFIFpmQnsY2e+ckP0Jp5e34N
|
||||
HH/vdxhunUROlGqazW+0tXfNBbWbwXpwSab4h+EzJ6BbLYQmxtg7w9DV0vMp0q2pFLdEc/U706Cx
|
||||
z5wNaq3xf2Hq5MH5P8GOwRewprgOb82+iTONUyNBGijPzb472Rr/40h9Er7XgeNvD+H144eRFkto
|
||||
1iZRb4y9NHdu4l2ZL3qWzYjJSODKa3HH06/OX+DD3atRS2fQUahUFxWXDqacDCsvt6pY7Nk6lYxK
|
||||
60hcc00/Vn2yH2AGTZ6WfkfvoMwWVljot2S5vFXkVVXVGhA9vfMXmNCnUXUqyIjsVxyhPlTMl+80
|
||||
0uyA49xX7Vh1z2z9DREKhjKEeOSocHtX3wsptzh+bhf58q5EYmXq+7dzqYjM1PT8hvD+b+zBxxet
|
||||
R2Siqiv9xy3xy9cvzj/5j3D6CEm3yzjOFlHsYZMRf0+CuuMtveJ77LgPGTK/itPaTzNXL3pvrpGu
|
||||
SJX8XKyb+9POzqCMIkaOHvqvrPP+A77nwyMPTPYqYiyzMN9/ZTyEyGbDIKr9kPKVxAh6oDY7fYkq
|
||||
V6HJHWCT/pKjuR9n/Hw4PhoBsAeslLdZqdZYIQ6pTOa8DZxXoFKoQAgJA14CQBpOa4mQcLQAZavN
|
||||
uq4/EsR6uaNyX9daI2hMP4esfMR0drYaMgIZAy24ZgmSSfRACJRXLL94gaDZhKkYWPAYCVLS8b7d
|
||||
4pAZglknH/WcwgB5Yp1A+ueJ1hhUd88Nadbdp23yLMi+5sQWJud9hwW5sHacLWDj5OIFUp0ioghs
|
||||
zSueEHtJyK95yN3Kkq0VAAkcZ5j7dVz/baHShUTJAUt0BztqOykFVhAQUDD6KUTBq0JImDYCbfeB
|
||||
v+4MoMmgxc2cJ/PXpSQuiyXCWJjXI4qP2XxXoxlNo5kjcLEL59LpYpL11ySevCJRlEkde0InrSGb
|
||||
zbZYAPuuKlx8AwBwbOpvuLTnakjjtATJg0qog5oYBAZA+OptwC+eOItZ30FpYxfoADWIxBBJd4gc
|
||||
ARYxWEoQA3R4qF3MhTciAHjmsZMwVmNl3+WYDUNs+/kt2HTDZjy049b/uPelR/fjnT1P4rN/eRFO
|
||||
FRg78CZIKezeeMn/ilhggQU+WP4Ne/S05BHkAUAAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjYtMDMt
|
||||
MTlUMDk6MDA6NDArMDA6MDCEOXtUAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDI2LTAzLTE5VDA5OjAw
|
||||
OjQwKzAwOjAw9WTD6AAAACh0RVh0ZGF0ZTp0aW1lc3RhbXAAMjAyNi0wMy0xOVQwOTowMDo0MCsw
|
||||
MDowMKJx4jcAAAAASUVORK5CYII=" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
18
hzhub-portal-employee/restart.sh
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
# HZHub Portal Employee 前端项目重启脚本
|
||||
# 功能:重启后台运行的员工门户前端开发服务器
|
||||
|
||||
PROJECT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
echo "========================================="
|
||||
echo "重启 hzhub-portal-employee 开发服务器"
|
||||
echo "========================================="
|
||||
|
||||
# 停止服务
|
||||
"$PROJECT_DIR/stop.sh"
|
||||
|
||||
# 等待一秒
|
||||
sleep 1
|
||||
|
||||
# 启动服务
|
||||
"$PROJECT_DIR/start.sh"
|
||||
7
hzhub-portal-employee/src/App.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<router-view />
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
10
hzhub-portal-employee/src/api/auth/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { EmailCodeDTO, LoginDTO, LoginVO, RegisterDTO } from './types';
|
||||
import { post } from '@/utils/request';
|
||||
|
||||
export const login = (data: LoginDTO) => post<LoginVO>('/auth/login', data).json();
|
||||
|
||||
// 邮箱验证码
|
||||
export const emailCode = (data: EmailCodeDTO) => post('/resource/email/code', data).json();
|
||||
|
||||
// 注册账号
|
||||
export const register = (data: RegisterDTO) => post('/auth/register', data).json();
|
||||
150
hzhub-portal-employee/src/api/auth/types.ts
Normal file
@@ -0,0 +1,150 @@
|
||||
export interface LoginDTO {
|
||||
username: string;
|
||||
password: string;
|
||||
code?: string;
|
||||
clientId?: string;
|
||||
grantType?: string;
|
||||
tenantId?: string;
|
||||
uuid?: string;
|
||||
// 二次确认密码
|
||||
confirmPassword?: string;
|
||||
}
|
||||
|
||||
export interface LoginVO {
|
||||
access_token?: string;
|
||||
token?: string;
|
||||
userInfo?: LoginUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* LoginUser,登录用户身份权限
|
||||
*/
|
||||
export interface LoginUser {
|
||||
/**
|
||||
* 微信头像
|
||||
*/
|
||||
avatar?: string;
|
||||
/**
|
||||
* 浏览器类型
|
||||
*/
|
||||
browser?: string;
|
||||
/**
|
||||
* 部门ID
|
||||
*/
|
||||
deptId?: number;
|
||||
/**
|
||||
* 部门名
|
||||
*/
|
||||
deptName?: string;
|
||||
/**
|
||||
* 过期时间
|
||||
*/
|
||||
expireTime?: number;
|
||||
/**
|
||||
* 登录IP地址
|
||||
*/
|
||||
ipaddr?: string;
|
||||
/**
|
||||
* 获取登录id
|
||||
*/
|
||||
loginId?: string;
|
||||
/**
|
||||
* 登录地点
|
||||
*/
|
||||
loginLocation?: string;
|
||||
/**
|
||||
* 登录时间
|
||||
*/
|
||||
loginTime?: number;
|
||||
/**
|
||||
* 菜单权限
|
||||
*/
|
||||
menuPermission?: string[];
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
nickName?: string;
|
||||
/**
|
||||
* 操作系统
|
||||
*/
|
||||
os?: string;
|
||||
/**
|
||||
* 数据权限 当前角色ID
|
||||
*/
|
||||
roleId?: number;
|
||||
/**
|
||||
* 角色权限
|
||||
*/
|
||||
rolePermission?: string[];
|
||||
/**
|
||||
* 角色对象
|
||||
*/
|
||||
roles?: RoleDTO[];
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
tenantId?: string;
|
||||
/**
|
||||
* 用户唯一标识
|
||||
*/
|
||||
token?: string;
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
userId?: number;
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
username?: string;
|
||||
/**
|
||||
* 用户类型
|
||||
*/
|
||||
userType?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* RoleDTO,角色
|
||||
*/
|
||||
export interface RoleDTO {
|
||||
/**
|
||||
* 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限)
|
||||
*/
|
||||
dataScope?: string;
|
||||
/**
|
||||
* 角色ID
|
||||
*/
|
||||
roleId?: number;
|
||||
/**
|
||||
* 角色权限
|
||||
*/
|
||||
roleKey?: string;
|
||||
/**
|
||||
* 角色名称
|
||||
*/
|
||||
roleName?: string;
|
||||
}
|
||||
|
||||
// 邮箱验证码
|
||||
export interface EmailCodeDTO {
|
||||
username?: string;
|
||||
}
|
||||
|
||||
// 邮箱注册
|
||||
export interface RegisterDTO {
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
username: string;
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
password: string;
|
||||
/**
|
||||
* 验证码
|
||||
*/
|
||||
code: string;
|
||||
/**
|
||||
* 确认密码
|
||||
*/
|
||||
confirmPassword?: string;
|
||||
}
|
||||
30
hzhub-portal-employee/src/api/chat/index.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import type { ChatMessageVo, GetChatListParams, SendDTO, workflowVo } from './types';
|
||||
import { get, post } from '@/utils/request';
|
||||
|
||||
// 发送消息
|
||||
export const send = (data: SendDTO) => post('/chat/send', data);
|
||||
|
||||
// 新增对应会话聊天记录
|
||||
export function addChat(data: ChatMessageVo) {
|
||||
return post('/system/message', data).json();
|
||||
}
|
||||
|
||||
// 获取当前会话的聊天记录
|
||||
export function getChatList(params: GetChatListParams) {
|
||||
return get<ChatMessageVo[]>('/system/message/list', params).json();
|
||||
}
|
||||
|
||||
// 获取知识库列表
|
||||
export function getKnowledgeList() {
|
||||
return get('/system/info/list').json();
|
||||
}
|
||||
|
||||
// 获取工作流列表
|
||||
export function getWorkflowList(params: workflowVo) {
|
||||
// 将参数转换为查询字符串
|
||||
const queryString = new URLSearchParams(params as Record<string, string>).toString();
|
||||
// 拼接 URL
|
||||
const url = `/admin/workflow/search?${queryString}`;
|
||||
// 发送 POST 请求
|
||||
return post<workflowVo[]>(url, {}).json();
|
||||
}
|
||||
283
hzhub-portal-employee/src/api/chat/types.ts
Normal file
@@ -0,0 +1,283 @@
|
||||
/**
|
||||
* 工作流请求体
|
||||
*/
|
||||
export interface WorkFlowRunner {
|
||||
workflowId?: string;
|
||||
inputs?: {
|
||||
name: string;
|
||||
type: string;
|
||||
content: {
|
||||
value: string;
|
||||
type: string;
|
||||
};
|
||||
}[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 人机交互信息体
|
||||
*/
|
||||
export interface ReSumeRunner {
|
||||
runtimeUuid?: string;
|
||||
feedbackContent?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* SSE 事件类型
|
||||
*/
|
||||
export type SseEventType = 'content' | 'reasoning' | 'done' | 'error' | 'message';
|
||||
|
||||
/**
|
||||
* SSE 事件数据
|
||||
*/
|
||||
export interface SseEventData {
|
||||
/**
|
||||
* 事件类型
|
||||
*/
|
||||
event: SseEventType;
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
content?: string;
|
||||
/**
|
||||
* 推理内容(深度思考模式)
|
||||
*/
|
||||
reasoningContent?: string;
|
||||
/**
|
||||
* 错误信息
|
||||
*/
|
||||
error?: string;
|
||||
/**
|
||||
* 是否完成
|
||||
*/
|
||||
done?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* ChatRequest,描述:对话请求对象
|
||||
*/
|
||||
export interface SendDTO {
|
||||
/**
|
||||
* 传入的模型(必填)
|
||||
*/
|
||||
model: string;
|
||||
/**
|
||||
* 对话消息(必填)
|
||||
*/
|
||||
content: string;
|
||||
/**
|
||||
* 工作流请求体
|
||||
*/
|
||||
workFlowRunner?: WorkFlowRunner;
|
||||
/**
|
||||
* 人机交互信息体
|
||||
*/
|
||||
reSumeRunner?: ReSumeRunner;
|
||||
/**
|
||||
* 是否为人机交互用户继续输入
|
||||
*/
|
||||
isResume?: boolean;
|
||||
/**
|
||||
* 是否启用工作流
|
||||
*/
|
||||
enableWorkFlow?: boolean;
|
||||
/**
|
||||
* 会话id
|
||||
*/
|
||||
sessionId?: string;
|
||||
/**
|
||||
* 知识库id
|
||||
*/
|
||||
knowledgeId?: string;
|
||||
/**
|
||||
* 应用ID
|
||||
*/
|
||||
appId?: string;
|
||||
/**
|
||||
* 对话id(每个聊天窗口都不一样)
|
||||
*/
|
||||
uuid?: number;
|
||||
/**
|
||||
* 是否启用深度思考
|
||||
*/
|
||||
enableThinking?: boolean;
|
||||
/**
|
||||
* 是否支持联网
|
||||
*/
|
||||
enableInternet?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Message,描述:
|
||||
*/
|
||||
export interface Message {
|
||||
content?: string;
|
||||
name?: string;
|
||||
reasoning_content?: string;
|
||||
/**
|
||||
* 目前支持四个中角色参考官网,进行情景输入:
|
||||
* https://platform.openai.com/docs/guides/chat/introduction
|
||||
*/
|
||||
role?: 'system' | 'user' | 'assistant' | 'function' | 'tool';
|
||||
tool_call_id?: string;
|
||||
/**
|
||||
* The tool calls generated by the model, such as function calls.
|
||||
*/
|
||||
tool_calls?: ToolCalls[];
|
||||
}
|
||||
|
||||
/**
|
||||
* ToolCalls,The tool calls generated by the model, such as function calls.
|
||||
*/
|
||||
export interface ToolCalls {
|
||||
function?: ToolCallFunction;
|
||||
/**
|
||||
* The ID of the tool call.
|
||||
*/
|
||||
id?: string;
|
||||
/**
|
||||
* The type of the tool. Currently, only function is supported.
|
||||
*/
|
||||
type?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* ToolCallFunction,ToolCall 的 Function参数
|
||||
* The function that the model called.
|
||||
*/
|
||||
export interface ToolCallFunction {
|
||||
/**
|
||||
* 方法参数
|
||||
*/
|
||||
arguments?: string;
|
||||
/**
|
||||
* 方法名
|
||||
*/
|
||||
name?: string;
|
||||
}
|
||||
|
||||
export interface GetChatListParams {
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
content?: string;
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
createBy?: number;
|
||||
/**
|
||||
* 创建部门
|
||||
*/
|
||||
createDept?: number;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
createTime?: Date;
|
||||
/**
|
||||
* 扣除金额
|
||||
*/
|
||||
deductCost?: number;
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
* 排序的方向desc或者asc
|
||||
*/
|
||||
isAsc?: string;
|
||||
/**
|
||||
* 模型名称
|
||||
*/
|
||||
modelName?: string;
|
||||
/**
|
||||
* 排序列
|
||||
*/
|
||||
orderByColumn?: string;
|
||||
/**
|
||||
* 当前页数
|
||||
*/
|
||||
pageNum?: number;
|
||||
/**
|
||||
* 分页大小
|
||||
*/
|
||||
pageSize?: number;
|
||||
/**
|
||||
* 请求参数
|
||||
*/
|
||||
params?: { [key: string]: { [key: string]: any } };
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
/**
|
||||
* 对话角色
|
||||
*/
|
||||
role?: string;
|
||||
/**
|
||||
* 会话id
|
||||
*/
|
||||
sessionId?: string;
|
||||
/**
|
||||
* 累计 Tokens
|
||||
*/
|
||||
totalTokens?: number;
|
||||
/**
|
||||
* 更新者
|
||||
*/
|
||||
updateBy?: number;
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
updateTime?: Date;
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
userId?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* ChatMessageVo,聊天消息视图对象 chat_message
|
||||
*/
|
||||
export interface ChatMessageVo {
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
content?: string;
|
||||
/**
|
||||
* 扣除金额
|
||||
*/
|
||||
deductCost?: number;
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
* 模型名称
|
||||
*/
|
||||
modelName?: string;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
/**
|
||||
* 对话角色
|
||||
*/
|
||||
role?: string;
|
||||
/**
|
||||
* 会话id
|
||||
*/
|
||||
sessionId?: string;
|
||||
/**
|
||||
* 累计 Tokens
|
||||
*/
|
||||
totalTokens?: number;
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
userId?: number;
|
||||
}
|
||||
|
||||
export interface workflowVo {
|
||||
currentPage?: number;
|
||||
|
||||
pageSize?: number;
|
||||
}
|
||||
4
hzhub-portal-employee/src/api/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from './auth';
|
||||
export * from './chat';
|
||||
export * from './model';
|
||||
export * from './session';
|
||||
7
hzhub-portal-employee/src/api/model/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import type { GetSessionListVO } from './types';
|
||||
import { get } from '@/utils/request';
|
||||
|
||||
// 获取当前用户的模型列表
|
||||
export function getModelList() {
|
||||
return get<GetSessionListVO[]>('/system/model/modelList').json();
|
||||
}
|
||||
14
hzhub-portal-employee/src/api/model/types.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
// 查询用户模型列表返回的数据结构
|
||||
export interface GetSessionListVO {
|
||||
id?: number;
|
||||
category?: string;
|
||||
modelName?: string;
|
||||
modelDescribe?: string;
|
||||
modelPrice?: number;
|
||||
modelType?: string;
|
||||
modelShow?: string;
|
||||
systemPrompt?: string;
|
||||
apiHost?: string;
|
||||
apiKey?: string;
|
||||
remark?: string;
|
||||
}
|
||||
40
hzhub-portal-employee/src/api/profile/index.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import type { UserProfile, UpdatePasswordParam } from './types';
|
||||
|
||||
import { get, put, post } from '@/utils/request';
|
||||
|
||||
/**
|
||||
* 用户个人主页信息
|
||||
* @returns userInformation
|
||||
*/
|
||||
export function userProfile() {
|
||||
return get<UserProfile>('/system/user/profile').json();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户个人主页信息
|
||||
* @param data
|
||||
* @returns void
|
||||
*/
|
||||
export function userProfileUpdate(data: any) {
|
||||
return put<void>('/system/user/profile', { body: data }).json();
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户修改密码
|
||||
* @param data
|
||||
* @returns void
|
||||
*/
|
||||
export function userUpdatePassword(data: UpdatePasswordParam) {
|
||||
return put<void>('/system/user/profile/updatePwd', { body: data }).json();
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户更新个人头像
|
||||
* @param file 文件
|
||||
* @returns void
|
||||
*/
|
||||
export function userUpdateAvatar(file: File) {
|
||||
const formData = new FormData();
|
||||
formData.append('avatarfile', file);
|
||||
return post<void>('/system/user/profile/avatar', { body: formData }).json();
|
||||
}
|
||||
69
hzhub-portal-employee/src/api/profile/types.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
export interface Dept {
|
||||
deptId: number;
|
||||
parentId: number;
|
||||
parentName?: any;
|
||||
ancestors: string;
|
||||
deptName: string;
|
||||
orderNum: number;
|
||||
leader: string;
|
||||
phone?: any;
|
||||
email: string;
|
||||
status: string;
|
||||
createTime?: any;
|
||||
}
|
||||
|
||||
export interface Role {
|
||||
roleId: number;
|
||||
roleName: string;
|
||||
roleKey: string;
|
||||
roleSort: number;
|
||||
dataScope: string;
|
||||
menuCheckStrictly?: any;
|
||||
deptCheckStrictly?: any;
|
||||
status: string;
|
||||
remark: string;
|
||||
createTime?: any;
|
||||
flag: boolean;
|
||||
superAdmin: boolean;
|
||||
}
|
||||
|
||||
export interface User {
|
||||
userId: number;
|
||||
tenantId: string;
|
||||
deptId: number;
|
||||
userName: string;
|
||||
nickName: string;
|
||||
userType: string;
|
||||
email: string;
|
||||
phonenumber: string;
|
||||
sex: string;
|
||||
avatar: string;
|
||||
status: string;
|
||||
loginIp: string;
|
||||
loginDate: string;
|
||||
remark: string;
|
||||
createTime: string;
|
||||
dept: Dept;
|
||||
roles: Role[];
|
||||
roleIds?: string[];
|
||||
postIds?: string[];
|
||||
roleId: number;
|
||||
deptName: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 用户个人主页信息
|
||||
* @param user 用户信息
|
||||
* @param roleGroup 角色名称
|
||||
* @param postGroup 岗位名称
|
||||
*/
|
||||
export interface UserProfile {
|
||||
user: User;
|
||||
roleGroup: string;
|
||||
postGroup: string;
|
||||
}
|
||||
|
||||
export interface UpdatePasswordParam {
|
||||
oldPassword: string;
|
||||
newPassword: string;
|
||||
}
|
||||
27
hzhub-portal-employee/src/api/session/index.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import type {
|
||||
ChatSessionVo,
|
||||
CreateSessionDTO,
|
||||
// CreateSessionVO,
|
||||
GetSessionListParams,
|
||||
} from './types';
|
||||
import { del, get, post, put } from '@/utils/request';
|
||||
|
||||
export function get_session_list(params: GetSessionListParams) {
|
||||
return get<ChatSessionVo[]>('/system/session/list', params).json();
|
||||
}
|
||||
|
||||
export function create_session(data: CreateSessionDTO) {
|
||||
return post('/system/session', data).json();
|
||||
}
|
||||
|
||||
export function update_session(data: ChatSessionVo) {
|
||||
return put('/system/session', data).json();
|
||||
}
|
||||
|
||||
export function get_session(id: string) {
|
||||
return get<ChatSessionVo>(`/system/session/${id}`).json();
|
||||
}
|
||||
|
||||
export function delete_session(ids: string[]) {
|
||||
return del(`/system/session/${ids}`).json();
|
||||
}
|
||||
154
hzhub-portal-employee/src/api/session/types.ts
Normal file
@@ -0,0 +1,154 @@
|
||||
import type { Component } from 'vue';
|
||||
|
||||
export interface GetSessionListParams {
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
createBy?: number;
|
||||
/**
|
||||
* 创建部门
|
||||
*/
|
||||
createDept?: number;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
createTime?: Date;
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
* 排序的方向desc或者asc
|
||||
*/
|
||||
isAsc?: string;
|
||||
/**
|
||||
* 排序列
|
||||
*/
|
||||
orderByColumn?: string;
|
||||
/**
|
||||
* 当前页数
|
||||
*/
|
||||
pageNum?: number;
|
||||
/**
|
||||
* 分页大小
|
||||
*/
|
||||
pageSize?: number;
|
||||
/**
|
||||
* 请求参数
|
||||
*/
|
||||
params?: { [key: string]: { [key: string]: any } };
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
/**
|
||||
* 会话内容
|
||||
*/
|
||||
sessionContent?: string;
|
||||
/**
|
||||
* 会话标题
|
||||
*/
|
||||
sessionTitle?: string;
|
||||
/**
|
||||
* 更新者
|
||||
*/
|
||||
updateBy?: number;
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
updateTime?: Date;
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
userId: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* ChatSessionVo,会话管理视图对象 chat_session
|
||||
*/
|
||||
export interface ChatSessionVo {
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
// id?: number
|
||||
id?: string;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
/**
|
||||
* 会话内容
|
||||
*/
|
||||
sessionContent?: string;
|
||||
/**
|
||||
* 会话标题
|
||||
*/
|
||||
sessionTitle?: string;
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
userId?: number;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
createTime?: Date;
|
||||
/**
|
||||
* 自定义的消息前缀图标字段
|
||||
*/
|
||||
prefixIcon?: Component;
|
||||
}
|
||||
|
||||
/**
|
||||
* ChatSessionBo,会话管理业务对象 chat_session
|
||||
*/
|
||||
export interface CreateSessionDTO {
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
createBy?: number;
|
||||
/**
|
||||
* 创建部门
|
||||
*/
|
||||
createDept?: number;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
createTime?: Date;
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
// id?: number;
|
||||
id?: string;
|
||||
/**
|
||||
* 请求参数
|
||||
*/
|
||||
params?: { [key: string]: { [key: string]: any } };
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
/**
|
||||
* 会话内容
|
||||
*/
|
||||
sessionContent?: string;
|
||||
/**
|
||||
* 会话标题
|
||||
*/
|
||||
sessionTitle: string;
|
||||
/**
|
||||
* 更新者
|
||||
*/
|
||||
updateBy?: number;
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
updateTime?: Date;
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
userId: number;
|
||||
}
|
||||
|
||||
// export interface CreateSessionVO {
|
||||
// id: number;
|
||||
// }
|
||||
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M18.901 10a2.999 2.999 0 0 0 4.075 1.113 3.5 3.5 0 0 1-1.975 3.55L21 21h-6v-2a3 3 0 0 0-5.995-.176L9 19v2H3v-6.336a3.5 3.5 0 0 1-1.979-3.553A2.999 2.999 0 0 0 5.098 10h13.803zm-1.865-7a3.5 3.5 0 0 0 4.446 2.86 3.5 3.5 0 0 1-3.29 3.135L18 9H6a3.5 3.5 0 0 1-3.482-3.14A3.5 3.5 0 0 0 6.964 3h10.072z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 448 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path fill-rule="nonzero" d="M18.901 10a2.999 2.999 0 0 0 4.075 1.113 3.5 3.5 0 0 1-1.975 3.55L21 21h-7v-2a2 2 0 0 0-1.85-1.995L12 17a2 2 0 0 0-1.995 1.85L10 19v2H3v-6.336a3.5 3.5 0 0 1-1.979-3.553A2.999 2.999 0 0 0 5.098 10h13.803zm-.971 2H6.069l-.076.079c-.431.42-.935.76-1.486 1.002l-.096.039.589.28-.001 5.6 3.002-.001v-.072l.01-.223c.149-2.016 1.78-3.599 3.854-3.698l.208-.005.223.01a4 4 0 0 1 3.699 3.787l.004.201L19 19l.001-5.6.587-.28-.095-.04a5.002 5.002 0 0 1-1.486-1.001L17.93 12zm-.894-9a3.5 3.5 0 0 0 4.446 2.86 3.5 3.5 0 0 1-3.29 3.135L18 9H6a3.5 3.5 0 0 1-3.482-3.14A3.5 3.5 0 0 0 6.964 3h10.072zM15.6 5H8.399a5.507 5.507 0 0 1-1.49 1.816L6.661 7h10.677l-.012-.008a5.518 5.518 0 0 1-1.579-1.722L15.6 5z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 860 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M12.513 2.001a9.004 9.004 0 0 0 9.97 5.877A4.501 4.501 0 0 1 19 11.888V19l2 .001v2H3v-2h2v-7.113a4.503 4.503 0 0 1-3.484-4.01 9.004 9.004 0 0 0 9.972-5.876h1.025zM17 12H7V19h10v-7z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 332 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path fill-rule="nonzero" d="M12.513 2.001a9.004 9.004 0 0 0 9.97 5.877A4.501 4.501 0 0 1 19 11.888V19l2 .001v2H3v-2h2v-7.113a4.503 4.503 0 0 1-3.484-4.01 9.004 9.004 0 0 0 9.972-5.876h1.025zM17 12H7V19h10v-7zm-5-6.673l-.11.155A11.012 11.012 0 0 1 5.4 9.736l-.358.073.673.19h12.573l.668-.19-.011-.002a11.01 11.01 0 0 1-6.836-4.326L12 5.326z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 483 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M2 20h20v2H2v-2zm2-8h2v7H4v-7zm5 0h2v7H9v-7zm4 0h2v7h-2v-7zm5 0h2v7h-2v-7zM2 7l10-5 10 5v4H2V7zm10 1a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 281 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M2 20h20v2H2v-2zm2-8h2v7H4v-7zm5 0h2v7H9v-7zm4 0h2v7h-2v-7zm5 0h2v7h-2v-7zM2 7l10-5 10 5v4H2V7zm2 1.236V9h16v-.764l-8-4-8 4zM12 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 310 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M12 19h2V6l6.394 2.74a1 1 0 0 1 .606.92V19h2v2H1v-2h2V5.65a1 1 0 0 1 .594-.914l7.703-3.424A.5.5 0 0 1 12 1.77V19z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 265 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M3 19V5.7a1 1 0 0 1 .658-.94l9.671-3.516a.5.5 0 0 1 .671.47v4.953l6.316 2.105a1 1 0 0 1 .684.949V19h2v2H1v-2h2zm2 0h7V3.855L5 6.401V19zm14 0v-8.558l-5-1.667V19h5z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 314 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M10 10.111V1l11 6v14H3V7z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 177 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M10 10.111V1l11 6v14H3V7l7 3.111zm2-5.742v8.82l-7-3.111V19h14V8.187L12 4.37z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 228 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M21 20h2v2H1v-2h2V3a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1v17zM8 11v2h3v-2H8zm0-4v2h3V7H8zm0 8v2h3v-2H8zm5 0v2h3v-2h-3zm0-4v2h3v-2h-3zm0-4v2h3V7h-3z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 292 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M21 20h2v2H1v-2h2V3a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1v17zm-2 0V4H5v16h14zM8 11h3v2H8v-2zm0-4h3v2H8V7zm0 8h3v2H8v-2zm5 0h3v2h-3v-2zm0-4h3v2h-3v-2zm0-4h3v2h-3V7z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 308 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M21 19h2v2H1v-2h2V4a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v15h2V9h3a1 1 0 0 1 1 1v9zM7 11v2h4v-2H7zm0-4v2h4V7H7z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 256 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M21 19h2v2H1v-2h2V4a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v15h4v-8h-2V9h3a1 1 0 0 1 1 1v9zM5 5v14h8V5H5zm2 6h4v2H7v-2zm0-4h4v2H7V7z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 275 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M9 19h3v-6.058L8 9.454l-4 3.488V19h3v-4h2v4zm12 2H3a1 1 0 0 1-1-1v-7.513a1 1 0 0 1 .343-.754L6 8.544V4a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1zm-5-10v2h2v-2h-2zm0 4v2h2v-2h-2zm0-8v2h2V7h-2zm-4 0v2h2V7h-2z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 364 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M21 21H3a1 1 0 0 1-1-1v-7.513a1 1 0 0 1 .343-.754L6 8.544V4a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1zM9 19h3v-6.058L8 9.454l-4 3.488V19h3v-4h2v4zm5 0h6V5H8v2.127c.234 0 .469.082.657.247l5 4.359a1 1 0 0 1 .343.754V19zm2-8h2v2h-2v-2zm0 4h2v2h-2v-2zm0-8h2v2h-2V7zm-4 0h2v2h-2V7z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 434 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M2 19V8H1V6h3V4a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v2h3v2h-1v11h1v2H1v-2h1zm11 0v-7h-2v7h2zm-5 0v-7H6v7h2zm10 0v-7h-2v7h2zM6 5v1h12V5H6z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 283 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M20 6h3v2h-1v11h1v2H1v-2h1V8H1V6h3V4a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v2zm0 2H4v11h3v-7h2v7h2v-7h2v7h2v-7h2v7h3V8zM6 5v1h12V5H6z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 277 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M20 20a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-9H1l10.327-9.388a1 1 0 0 1 1.346 0L23 11h-3v9z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 236 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M19 21H5a1 1 0 0 1-1-1v-9H1l10.327-9.388a1 1 0 0 1 1.346 0L23 11h-3v9a1 1 0 0 1-1 1zM6 19h12V9.157l-6-5.454-6 5.454V19z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 271 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M20 20a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-9H1l10.327-9.388a1 1 0 0 1 1.346 0L23 11h-3v9zM8 15v2h8v-2H8z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 251 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M19 21H5a1 1 0 0 1-1-1v-9H1l10.327-9.388a1 1 0 0 1 1.346 0L23 11h-3v9a1 1 0 0 1-1 1zM6 19h12V9.157l-6-5.454-6 5.454V19zm2-4h8v2H8v-2z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 285 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M20 20a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-9H1l10.327-9.388a1 1 0 0 1 1.346 0L23 11h-3v9zm-9-7v6h2v-6h-2z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 252 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M19 21H5a1 1 0 0 1-1-1v-9H1l10.327-9.388a1 1 0 0 1 1.346 0L23 11h-3v9a1 1 0 0 1-1 1zm-6-2h5V9.157l-6-5.454-6 5.454V19h5v-6h2v6z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 279 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M21 20a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V9.49a1 1 0 0 1 .386-.79l8-6.222a1 1 0 0 1 1.228 0l8 6.222a1 1 0 0 1 .386.79V20zm-10-7v6h2v-6h-2z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 285 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M13 19h6V9.978l-7-5.444-7 5.444V19h6v-6h2v6zm8 1a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V9.49a1 1 0 0 1 .386-.79l8-6.222a1 1 0 0 1 1.228 0l8 6.222a1 1 0 0 1 .386.79V20z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 310 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M21 20a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V9.49a1 1 0 0 1 .386-.79l8-6.222a1 1 0 0 1 1.228 0l8 6.222a1 1 0 0 1 .386.79V20zM7 15v2h10v-2H7z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 284 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M21 20a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V9.49a1 1 0 0 1 .386-.79l8-6.222a1 1 0 0 1 1.228 0l8 6.222a1 1 0 0 1 .386.79V20zm-2-1V9.978l-7-5.444-7 5.444V19h14zM7 15h10v2H7v-2z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 319 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M19 20a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-9H0l10.327-9.388a1 1 0 0 1 1.346 0L22 11h-3v9zm-8-5a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 278 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M19 21H5a1 1 0 0 1-1-1v-9H1l10.327-9.388a1 1 0 0 1 1.346 0L23 11h-3v9a1 1 0 0 1-1 1zM6 19h12V9.157l-6-5.454-6 5.454V19zm6-4a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 312 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M20 20a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-9H1l10.327-9.388a1 1 0 0 1 1.346 0L23 11h-3v9zM9 10v6h6v-6H9zm2 2h2v2h-2v-2z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 266 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M19 21H5a1 1 0 0 1-1-1v-9H1l10.327-9.388a1 1 0 0 1 1.346 0L23 11h-3v9a1 1 0 0 1-1 1zM6 19h12V9.157l-6-5.454-6 5.454V19zm3-9h6v6H9v-6zm2 2v2h2v-2h-2z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 300 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M21 20a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V9.49a1 1 0 0 1 .386-.79l8-6.222a1 1 0 0 1 1.228 0l8 6.222a1 1 0 0 1 .386.79V20z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 268 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M20 20a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-9H1l10.327-9.388a1 1 0 0 1 1.346 0L23 11h-3v9zM8.592 13.808l-.991.572 1 1.733.993-.573a3.5 3.5 0 0 0 1.405.811v1.145h2.002V16.35a3.5 3.5 0 0 0 1.405-.81l.992.572L16.4 14.38l-.991-.572a3.504 3.504 0 0 0 0-1.62l.991-.573-1-1.733-.993.573A3.5 3.5 0 0 0 13 9.645V8.5h-2.002v1.144a3.5 3.5 0 0 0-1.405.811l-.992-.573L7.6 11.616l.991.572a3.504 3.504 0 0 0 0 1.62zm3.408.69a1.5 1.5 0 1 1-.002-3.001 1.5 1.5 0 0 1 .002 3z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 602 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M19 21H5a1 1 0 0 1-1-1v-9H1l10.327-9.388a1 1 0 0 1 1.346 0L23 11h-3v9a1 1 0 0 1-1 1zM6 19h12V9.157l-6-5.454-6 5.454V19zm2.591-5.191a3.508 3.508 0 0 1 0-1.622l-.991-.572 1-1.732.991.573a3.495 3.495 0 0 1 1.404-.812V8.5h2v1.144c.532.159 1.01.44 1.404.812l.991-.573 1 1.731-.991.573a3.508 3.508 0 0 1 0 1.622l.991.572-1 1.731-.991-.572a3.495 3.495 0 0 1-1.404.811v1.145h-2V16.35a3.495 3.495 0 0 1-1.404-.811l-.991.572-1-1.73.991-.573zm3.404.688a1.5 1.5 0 1 0 0-2.998 1.5 1.5 0 0 0 0 2.998z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 638 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M20 20a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-9H1l10.327-9.388a1 1 0 0 1 1.346 0L23 11h-3v9zm-8-3l3.359-3.359a2.25 2.25 0 1 0-3.182-3.182l-.177.177-.177-.177a2.25 2.25 0 1 0-3.182 3.182L12 17z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 336 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path fill-rule="nonzero" d="M20 20a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-9H1l10.327-9.388a1 1 0 0 1 1.346 0L23 11h-3v9zm-2-1V9.157l-6-5.454-6 5.454V19h12zm-6-2l-3.359-3.359a2.25 2.25 0 1 1 3.182-3.182l.177.177.177-.177a2.25 2.25 0 1 1 3.182 3.182L12 17z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 390 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M21 20a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V9.49a1 1 0 0 1 .386-.79l8-6.222a1 1 0 0 1 1.228 0l8 6.222a1 1 0 0 1 .386.79V20zm-2-1V9.978l-7-5.444-7 5.444V19h14z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 303 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M21 20a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V9.314a1 1 0 0 1 .38-.785l8-6.311a1 1 0 0 1 1.24 0l8 6.31a1 1 0 0 1 .38.786V20zM7 12a5 5 0 0 0 10 0h-2a3 3 0 0 1-6 0H7z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 307 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M19 19V9.799l-7-5.522-7 5.522V19h14zm2 1a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V9.314a1 1 0 0 1 .38-.785l8-6.311a1 1 0 0 1 1.24 0l8 6.31a1 1 0 0 1 .38.786V20zM7 12h2a3 3 0 0 0 6 0h2a5 5 0 0 1-10 0z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 340 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M20 20a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-9H1l10.327-9.388a1 1 0 0 1 1.346 0L23 11h-3v9zM7.5 13a4.5 4.5 0 1 0 9 0h-2a2.5 2.5 0 1 1-5 0h-2z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 286 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M6 19h12V9.157l-6-5.454-6 5.454V19zm13 2H5a1 1 0 0 1-1-1v-9H1l10.327-9.388a1 1 0 0 1 1.346 0L23 11h-3v9a1 1 0 0 1-1 1zM7.5 13h2a2.5 2.5 0 1 0 5 0h2a4.5 4.5 0 1 1-9 0z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 318 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M20 20a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-9H1l10.327-9.388a1 1 0 0 1 1.346 0L23 11h-3v9zM7 11v2a5 5 0 0 1 5 5h2a7 7 0 0 0-7-7zm0 4v3h3a3 3 0 0 0-3-3z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 297 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M6 19h12V9.157l-6-5.454-6 5.454V19zm13 2H5a1 1 0 0 1-1-1v-9H1l10.327-9.388a1 1 0 0 1 1.346 0L23 11h-3v9a1 1 0 0 1-1 1zM8 10a7 7 0 0 1 7 7h-2a5 5 0 0 0-5-5v-2zm0 4a3 3 0 0 1 3 3H8v-3z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 334 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M21 20h2v2H1v-2h2V3a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1v17zM11 8H9v2h2v2h2v-2h2V8h-2V6h-2v2zm3 12h2v-6H8v6h2v-4h4v4z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 263 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path fill-rule="nonzero" d="M8 20v-6h8v6h3V4H5v16h3zm2 0h4v-4h-4v4zm11 0h2v2H1v-2h2V3a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1v17zM11 8V6h2v2h2v2h-2v2h-2v-2H9V8h2z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 297 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M17 19h2v-8h-6v8h2v-6h2v6zM3 19V4a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v5h2v10h1v2H2v-2h1zm4-8v2h2v-2H7zm0 4v2h2v-2H7zm0-8v2h2V7H7z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 276 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M22 21H2v-2h1V4a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v5h2v10h1v2zm-5-2h2v-8h-6v8h2v-6h2v6zm0-10V5H5v14h6V9h6zM7 11h2v2H7v-2zm0 4h2v2H7v-2zm0-8h2v2H7V7z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 296 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M22 20v2H2v-2h1v-6.758A4.496 4.496 0 0 1 1 9.5c0-.827.224-1.624.633-2.303L4.345 2.5a1 1 0 0 1 .866-.5H18.79a1 1 0 0 1 .866.5l2.702 4.682A4.496 4.496 0 0 1 21 13.242V20h1zM5.789 4L3.356 8.213a2.5 2.5 0 0 0 4.466 2.216c.335-.837 1.52-.837 1.856 0a2.5 2.5 0 0 0 4.644 0c.335-.837 1.52-.837 1.856 0a2.5 2.5 0 1 0 4.457-2.232L18.21 4H5.79z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 486 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M21 13.242V20h1v2H2v-2h1v-6.758A4.496 4.496 0 0 1 1 9.5c0-.827.224-1.624.633-2.303L4.345 2.5a1 1 0 0 1 .866-.5H18.79a1 1 0 0 1 .866.5l2.702 4.682A4.496 4.496 0 0 1 21 13.242zm-2 .73a4.496 4.496 0 0 1-3.75-1.36A4.496 4.496 0 0 1 12 14.001a4.496 4.496 0 0 1-3.25-1.387A4.496 4.496 0 0 1 5 13.973V20h14v-6.027zM5.789 4L3.356 8.213a2.5 2.5 0 0 0 4.466 2.216c.335-.837 1.52-.837 1.856 0a2.5 2.5 0 0 0 4.644 0c.335-.837 1.52-.837 1.856 0a2.5 2.5 0 1 0 4.457-2.232L18.21 4H5.79z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 623 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M21 13v7a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-7H2v-2l1-5h18l1 5v2h-1zM5 13v6h14v-6H5zm1 1h8v3H6v-3zM3 3h18v2H3V3z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 259 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M21 13v7a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-7H2v-2l1-5h18l1 5v2h-1zM5 13v6h14v-6H5zm-.96-2h15.92l-.6-3H4.64l-.6 3zM6 14h8v3H6v-3zM3 3h18v2H3V3z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 291 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M21 11.646V21a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-9.354A3.985 3.985 0 0 1 2 9V3a1 1 0 0 1 1-1h18a1 1 0 0 1 1 1v6c0 1.014-.378 1.94-1 2.646zM14 9a1 1 0 0 1 2 0 2 2 0 1 0 4 0V4H4v5a2 2 0 1 0 4 0 1 1 0 1 1 2 0 2 2 0 1 0 4 0z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 368 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M21 11.646V21a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-9.354A3.985 3.985 0 0 1 2 9V3a1 1 0 0 1 1-1h18a1 1 0 0 1 1 1v6c0 1.014-.378 1.94-1 2.646zm-2 1.228a4.007 4.007 0 0 1-4-1.228A3.99 3.99 0 0 1 12 13a3.99 3.99 0 0 1-3-1.354 3.99 3.99 0 0 1-4 1.228V20h14v-7.126zM14 9a1 1 0 0 1 2 0 2 2 0 1 0 4 0V4H4v5a2 2 0 1 0 4 0 1 1 0 1 1 2 0 2 2 0 1 0 4 0z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 487 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M21 3a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h18zM9.399 8h-2l-3.2 8h2.154l.4-1h3.29l.4 1h2.155L9.399 8zM19 8h-2v2h-1a3 3 0 0 0-.176 5.995L16 16h3V8zm-2 4v2h-1l-.117-.007a1 1 0 0 1 0-1.986L16 12h1zm-8.601-1.115L9.244 13H7.552l.847-2.115z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 411 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path fill-rule="nonzero" d="M21 3a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h18zm-1 2H4v14h16V5zM9.399 8l3.199 8h-2.155l-.4-1h-3.29l-.4 1H4.199l3.2-8h2zM19 8v8h-3a3 3 0 0 1 0-6h.999L17 8h2zm-2 4h-1a1 1 0 0 0-.117 1.993L16 14h1v-2zm-8.601-1.115L7.552 13h1.692l-.845-2.115z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 435 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M3 13h18v8.002c0 .551-.445.998-.993.998H3.993A.995.995 0 0 1 3 21.002V13zM3 2.998C3 2.447 3.445 2 3.993 2h16.014c.548 0 .993.446.993.998V11H3V2.998zM9 5v2h6V5H9zm0 11v2h6v-2H9z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 328 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M3 2.992C3 2.444 3.445 2 3.993 2h16.014a1 1 0 0 1 .993.992v18.016a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 21.008V2.992zM19 11V4H5v7h14zm0 2H5v7h14v-7zM9 6h6v2H9V6zm0 9h6v2H9v-2z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 331 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M3 10h18v10.004c0 .55-.445.996-.993.996H3.993A.994.994 0 0 1 3 20.004V10zm6 2v2h6v-2H9zM2 4c0-.552.455-1 .992-1h18.016c.548 0 .992.444.992 1v4H2V4z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 299 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M3 10H2V4.003C2 3.449 2.455 3 2.992 3h18.016A.99.99 0 0 1 22 4.003V10h-1v10.001a.996.996 0 0 1-.993.999H3.993A.996.996 0 0 1 3 20.001V10zm16 0H5v9h14v-9zM4 5v3h16V5H4zm5 7h6v2H9v-2z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 333 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm8-10a8 8 0 1 0-3.968 6.911l-1.008-1.727A6 6 0 1 1 18 12v1a1 1 0 0 1-2 0V9h-1.354a4 4 0 1 0 .066 5.94A3 3 0 0 0 20 13v-1zm-8-2a2 2 0 1 1 0 4 2 2 0 0 1 0-4z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 380 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M20 12a8 8 0 1 0-3.562 6.657l1.11 1.664A9.953 9.953 0 0 1 12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10v1.5a3.5 3.5 0 0 1-6.396 1.966A5 5 0 1 1 15 8H17v5.5a1.5 1.5 0 0 0 3 0V12zm-8-3a3 3 0 1 0 0 6 3 3 0 0 0 0-6z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 374 B |