master #1

Merged
wangmingwei merged 2 commits from master into main 2026-05-08 15:09:16 +08:00
1822 changed files with 288292 additions and 0 deletions
Showing only changes of commit 7f9e424a5c - Show all commits

19
.editorconfig Normal file
View File

@@ -0,0 +1,19 @@
root = true
[*]
charset=utf-8
end_of_line=lf
insert_final_newline=true
indent_style=space
indent_size=2
max_line_length = 100
[*.{yml,yaml,json}]
indent_style = space
indent_size = 2
[*.md]
trim_trailing_whitespace = false
[Makefile]
indent_style = tab

8
.env Normal file
View File

@@ -0,0 +1,8 @@
# 端口号
VITE_PORT = 3100
# 网站标题
VITE_GLOB_APP_TITLE = 加载中...
# 简称,用于配置文件名字 不要出现空格、数字开头等特殊字符
VITE_GLOB_APP_SHORT_NAME = yunzhu

23
.env.development Normal file
View File

@@ -0,0 +1,23 @@
# 资源公共路径,需要以 /开头和结尾
VITE_PUBLIC_PATH = /
# 本地开发代理,可以解决跨域及多地址代理
# 如果接口地址匹配到则会转发到http://localhost:30000防止本地出现跨域问题
# 可以有多个,注意多个不能换行,否则代理将会失效
VITE_PROXY = [["/dev","http://localhost:30000"], ["/reportDev","http://localhost:32000"]]
# 是否删除Console.log
VITE_DROP_CONSOLE = false
# 接口地址
# 如果没有跨域问题,直接在这里配置即可
VITE_GLOB_API_URL=/dev
# 报表接口
VITE_GLOB_REPORT_API_URL=/reportDev
# WebSocket基础地址
VITE_GLOB_WEBSOCKET_URL='ws://localhost:30000'
# 接口地址前缀,有些系统所有接口地址都有前缀,可以在这里统一加,方便切换
VITE_GLOB_API_URL_PREFIX=

31
.env.production Normal file
View File

@@ -0,0 +1,31 @@
# 资源公共路径,需要以 / 开头和结尾
VITE_PUBLIC_PATH = /
# 是否删除Console.log
VITE_DROP_CONSOLE = true
# 打包是否输出gzbr文件
# 可选: gzip | brotli | none
# 也可以有多个, 例如 'gzip'|'brotli',这样会同时生成.gz和.br文件
VITE_BUILD_COMPRESS = 'gzip'
# 使用压缩时是否删除原始文件默认为false
VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE = false
# 是否在打包时使用cdn替换本地库内网环境请设置为false
VITE_CDN = false
# 接口地址 可以由nginx做转发或者直接写实际地址
VITE_GLOB_API_URL=
# 报表接口地址 可以由nginx做转发或者直接写实际地址
VITE_GLOB_REPORT_API_URL=
# WebSocket基础地址 (为空时默认取当前url路径若需要自定义请输入)
VITE_GLOB_WEBSOCKET_URL=
# 接口地址前缀
VITE_GLOB_API_URL_PREFIX=
# 打包是否开启pwa功能
VITE_USE_PWA = false

34
.env.test Normal file
View File

@@ -0,0 +1,34 @@
NODE_ENV=production
# 资源公共路径,需要以 / 开头和结尾
VITE_PUBLIC_PATH = /
# 是否删除Console.log
VITE_DROP_CONSOLE = true
# 打包是否输出gzbr文件
# 可选: gzip | brotli | none
# 也可以有多个, 例如 'gzip'|'brotli',这样会同时生成.gz和.br文件
VITE_BUILD_COMPRESS = 'gzip'
# 使用压缩时是否删除原始文件默认为false
VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE = false
# 是否在打包时使用cdn替换本地库内网环境请设置为false
VITE_CDN = false
# 接口地址 可以由nginx做转发或者直接写实际地址
VITE_GLOB_API_URL=
# 报表接口地址 可以由nginx做转发或者直接写实际地址
VITE_GLOB_REPORT_API_URL=
# WebSocket基础地址 (为空时默认取当前url路径若需要自定义请输入)
VITE_GLOB_WEBSOCKET_URL=
# 接口地址前缀
VITE_GLOB_API_URL_PREFIX=
# 打包是否开启pwa功能
VITE_USE_PWA = false

16
.eslintignore Normal file
View File

@@ -0,0 +1,16 @@
*.sh
node_modules
*.md
*.woff
*.ttf
.vscode
.idea
dist
/public
/docs
.husky
.local
/bin
Dockerfile
/src

76
.eslintrc.js Normal file
View File

@@ -0,0 +1,76 @@
module.exports = {
root: true,
env: {
browser: true,
node: true,
es6: true,
},
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
ecmaVersion: 2020,
sourceType: 'module',
jsxPragma: 'React',
ecmaFeatures: {
jsx: true,
},
},
extends: [
'plugin:vue/vue3-recommended',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
rules: {
'vue/script-setup-uses-vars': 'error',
'@typescript-eslint/ban-ts-ignore': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-empty-function': 'off',
'vue/custom-event-name-casing': 'off',
'no-use-before-define': 'off',
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/ban-types': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
},
],
'no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
},
],
'space-before-function-paren': 'off',
'vue/attributes-order': 'off',
'vue/one-component-per-file': 'off',
'vue/html-closing-bracket-newline': 'off',
'vue/max-attributes-per-line': 'off',
'vue/multiline-html-element-content-newline': 'off',
'vue/singleline-html-element-content-newline': 'off',
'vue/attribute-hyphenation': 'off',
'vue/require-default-prop': 'off',
'vue/require-explicit-emits': 'off',
'vue/html-self-closing': [
'error',
{
html: {
void: 'always',
normal: 'never',
component: 'always',
},
svg: 'always',
math: 'always',
},
],
'vue/multi-word-component-names': 'off',
},
};

30
.gitignore vendored Normal file
View File

@@ -0,0 +1,30 @@
node_modules
.DS_Store
dist
.cache
tests/server/static
tests/server/static/upload
.local
# local env files
.env.local
.env.*.local
.eslintcache
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
# .vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.vscode

6
.gitpod.yml Normal file
View File

@@ -0,0 +1,6 @@
ports:
- port: 3344
onOpen: open-preview
tasks:
- init: pnpm install
command: pnpm run dev

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
registry = "https://registry.npmmirror.com"

9
.prettierignore Normal file
View File

@@ -0,0 +1,9 @@
/dist/*
.local
.output.js
/node_modules/**
**/*.svg
**/*.sh
/public/*

3
.stylelintignore Normal file
View File

@@ -0,0 +1,3 @@
/dist/*
/public/*
public/*

39
Dockerfile Normal file
View File

@@ -0,0 +1,39 @@
# 基础镜像
# nodejs请勿使用alpine版本以免出现依赖安装失败的问题
FROM node:20-alpine as build-stage
LABEL maintainer=yunzhupaas-team
# 设置时区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 指定临时工作目录
WORKDIR /temp
# 安装pnpm
RUN npm install -g pnpm@9.9.0 --registry=https://registry.npmmirror.com
# 复制项目
COPY . .
# 安装依赖
RUN pnpm install --registry https://registry.npmmirror.com
# 构建项目
RUN pnpm build
# 基础镜像
FROM nginx:stable-alpine as production-stage
# 指定运行时的工作目录
ENV WORKDIR /data/yunzhupaas/yunzhupaas-web-vue3
WORKDIR $WORKDIR
# 将构建文件拷贝到运行时目录中
COPY --from=build-stage /temp/dist ${WORKDIR}
# 复制Nginx配置
COPY deploy/default.conf /etc/nginx/conf.d/
# 指定容器内运行端口
EXPOSE 80

0
README
View File

157
README.md Normal file
View File

@@ -0,0 +1,157 @@
## 一 环境要求
### 1.1 开发环境
- 操作系统:`Windows 10/11``MacOS`
- `Node 16.15.0` 及以上版本(某些情况下可能需要安装 `Python3` 环境)
- `pnpm v8.1.0`及以上版本;
- `Visual Studio Code` (简称 VSCode)
### 1.2 运行环境
`Nginx` 建议使用 `1.18.0` 及以上版本、兼容 `OpenResty``TongHttpServer` 6.0(国产信创)
## 二 浏览器支持
> 支持现代浏览器,不支持 IE
| IE | Edge | Firefox | Chrome | Safari |
| ----------- | --------------- | --------------- | --------------- | --------------- |
| not support | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
## 三 关联项目
> 需要使用下表中的对应分支
| 项目 | 分支 | 说明 |
| ------------------------ | ------------- | ---------------------- |
| **后端**(任一后端服务) | | |
| yunzhupaas-java-boot | v5.2.x-stable | Java 单体项目源码 |
| yunzhupaas-java-cloud | v5.2.x-stable | Java 微服务项目源码 |
| yunzhupaas-dotnet | v5.2.x-stable | .NET 单体项目源码 |
| yunzhupaas-dotnet-cloud | v5.2.x-stable | .NET 微服务项目源码 |
| **前端** | | |
| yunzhupaas-web-datascreen-vue3 | v5.2.x-stable | 大屏前端项目源码(Vue3) |
| yunzhupaas-web-datareport | v5.2.x-stable | 报表前端项目源码 |
## 四 使用说明
### 4.1 开发环境
#### 4.1.1 安装 pnpm
> 推荐使用 `pnpm`
在 Windows 的 `PowerShell` 中执行如下命令
```bash
iwr https://get.pnpm.io/install.ps1 -useb | iex
```
MacOS 通过 `Homebrew` 安装 `pnpm`<br/> 若已经安装了 `Homebrew` 软件包管理器,则可以使用如下命令赖安装 pnpm
```bash
brew install pnpm
```
也可以通过 npm 安装 pnpm
```bash
npm install -g pnpm
npm install -g @pnpm/exe
```
#### 4.1.2 安装依赖
使用如下命令安装项目依赖
```bash
pnpm install --registry http://registry.npmmirror.com
```
#### 4.1.3 后端接口配置
修改项目根目录 `.env.development` 中的后端接口地址
- Java 项目本地开发默认接口地址:`http://localhost:30000`
- .NET 项目本地开发默认接口地址:`http://localhost:5000`
```bash
# 第7行后端接口
VITE_PROXY = [["/dev","http://localhost:30000"]]
# 第17行websocket地址
# 在本地开发环境,将后端默认接口地址的协议改成 ws 即可
VITE_GLOB_WEBSOCKET_URL='ws://localhost:30000'
```
#### 4.1.4 关联项目配置
打开 `/src/hooks/setting/index.ts` 配置文件,默认配置如下所示
```bash
...
const glob: Readonly<GlobConfig> = {
title: VITE_GLOB_APP_TITLE,
apiUrl: VITE_GLOB_API_URL,
shortName: VITE_GLOB_APP_SHORT_NAME,
urlPrefix: VITE_GLOB_API_URL_PREFIX,
uploadUrl: VITE_GLOB_API_URL + '/api/file/Uploader',
webSocketUrl: VITE_GLOB_WEBSOCKET_URL,
cipherKey: 'EY8WePvjM5GGwQzn', // 加密key
aMapJsKey: '26a65601349a5ec88318721884ef81b5',
aMapWebKey: '09485f01587712b3c04e5a9abf324237',
aMapSecurityJsCode: '243e837c2ba077b4143b9a9dd2893992',
// 本地文件预览
filePreviewServer: isDevMode() ? 'http://localhost:30090/FileServer' : VITE_GLOB_API_URL + '/FileServer',
// 大屏应用前端路径
dataVUrl: isDevMode() ? 'http://localhost:8100/DataV/' : prodUrlPrefix + '/DataV/',
// 数据报表接口
reportServer: isDevMode() ? 'http://localhost:30007' : VITE_GLOB_API_URL + '/ReportServer',
// 报表前端路径
report: isDevMode() ? 'http://localhost:8200' : VITE_GLOB_API_URL + '/Report',
};
...
```
#### 4.1.5 本地运行
完成上述操作后,使用如下命令运行前端项目
```bash
pnpm dev
```
### 4.2 运行环境
> 测试或生产环境
如果需要测试或生产环境发布,使用如下命令打包项目
```bash
pnpm build
```
然后将项目根目录下 `/dist/` 中所有的文件上传至服务器。
## 五 常见问题
### 5.1 修改项目基本信息
打开项目根目录 `.env` 文件,可以看到 `本地运行端口号``网站标题``简称` 等配置。
```bash
# 端口号
VITE_PORT = 3100
# 网站标题
VITE_GLOB_APP_TITLE = 云筑项目管理平台
# 简称,用于配置文件名字 不要出现空格、数字开头等特殊字符
VITE_GLOB_APP_SHORT_NAME = yunzhu
```
### 5.2 代码更新后报错
在开发或打包时报依赖缺失,可以先删除项目根目录下的 `pnpm-lock.yaml` 文件,然后重新执行 `pnpm install` 安装依赖即可解决。

View File

@@ -0,0 +1,67 @@
import { generate } from '@ant-design/colors';
export const primaryColor = '#1890ff';
export const darkMode = 'light';
type Fn = (...arg: any) => any;
type GenerateTheme = 'default' | 'dark';
export interface GenerateColorsParams {
mixLighten: Fn;
mixDarken: Fn;
tinycolor: any;
color?: string;
}
export function generateAntColors(color: string, theme: GenerateTheme = 'default') {
return generate(color, {
theme,
});
}
export function getThemeColors(color?: string) {
const tc = color || primaryColor;
const lightColors = generateAntColors(tc);
const primary = lightColors[5];
const modeColors = generateAntColors(primary, 'dark');
return [...lightColors, ...modeColors];
}
export function generateColors({ color = primaryColor, mixLighten, mixDarken, tinycolor }: GenerateColorsParams) {
const arr = new Array(19).fill(0);
const lightens = arr.map((_t, i) => {
return mixLighten(color, i / 5);
});
const darkens = arr.map((_t, i) => {
return mixDarken(color, i / 5);
});
const alphaColors = arr.map((_t, i) => {
return tinycolor(color)
.setAlpha(i / 20)
.toRgbString();
});
const shortAlphaColors = alphaColors.map(item => item.replace(/\s/g, '').replace(/0\./g, '.'));
const tinycolorLightens = arr
.map((_t, i) => {
return tinycolor(color)
.lighten(i * 5)
.toHexString();
})
.filter(item => item !== '#ffffff');
const tinycolorDarkens = arr
.map((_t, i) => {
return tinycolor(color)
.darken(i * 5)
.toHexString();
})
.filter(item => item !== '#000000');
return [...lightens, ...darkens, ...alphaColors, ...shortAlphaColors, ...tinycolorDarkens, ...tinycolorLightens].filter(item => !item.includes('-'));
}

6
build/constant.ts Normal file
View File

@@ -0,0 +1,6 @@
/**
* The name of the configuration file entered in the production environment
*/
export const GLOB_CONFIG_FILE_NAME = '_app.config.js';
export const OUTPUT_DIR = 'dist';

View File

@@ -0,0 +1,53 @@
import { primaryColor } from '../config/themeConfig';
import { resolve } from 'path';
import { generate } from '@ant-design/colors';
import { theme } from 'ant-design-vue/lib';
import convertLegacyToken from 'ant-design-vue/lib/theme/convertLegacyToken';
const { defaultAlgorithm, defaultSeed } = theme;
function generateAntColors(color: string, theme: 'default' | 'dark' = 'default') {
return generate(color, {
theme,
});
}
/**
* less global variable
*/
export function generateModifyVars() {
const palettes = generateAntColors(primaryColor);
const primary = palettes[5];
const primaryColorObj: Record<string, string> = {};
for (let index = 0; index < 10; index++) {
primaryColorObj[`primary-${index + 1}`] = palettes[index];
}
const mapToken = defaultAlgorithm(defaultSeed);
const v3Token = convertLegacyToken(mapToken);
return {
...v3Token,
// Used for global import to avoid the need to import each style file separately
// reference: Avoid repeated references
hack: `true; @import (reference) "${resolve('src/design/config.less')}";`,
'primary-color': primary,
...primaryColorObj,
'info-color': primary,
'processing-color': primary,
'success-color': '#55D187', // Success color
'error-color': '#ED6F6F', // False color
'warning-color': '#EFBD47', // Warning color
'btn-info-color': '#909399',
'text-color-secondary': 'rgba(0, 0, 0, 0.45)',
'border-color-base1': '#f0f0f0',
'font-size-base': '14px', // Main font size
'border-radius-base': '2px', // Component/float fillet
'link-color': primary, // Link color
'app-base-background': '#eaecf0',
'app-content-background': '#F1F4F8', // Link color
'app-main-background': '#ebeef5',
'selected-hover-bg': '#f5f5f5',
'hover-background': '#f5f7fa',
};
}

View File

@@ -0,0 +1,72 @@
import path from 'path';
import fs from 'fs-extra';
import inquirer from 'inquirer';
import colors from 'picocolors';
import pkg from '../../../package.json';
async function generateIcon() {
const dir = path.resolve(process.cwd(), 'node_modules/@iconify/json');
const raw = await fs.readJSON(path.join(dir, 'collections.json'));
const collections = Object.entries(raw).map(([id, v]) => ({
...(v as any),
id,
}));
const choices = collections.map((item) => ({ key: item.id, value: item.id, name: item.name }));
inquirer
.prompt([
{
type: 'list',
name: 'useType',
choices: [
{ key: 'local', value: 'local', name: 'Local' },
{ key: 'onLine', value: 'onLine', name: 'OnLine' },
],
message: 'How to use icons?',
},
{
type: 'list',
name: 'iconSet',
choices: choices,
message: 'Select the icon set that needs to be generated?',
},
{
type: 'input',
name: 'output',
message: 'Select the icon set that needs to be generated?',
default: 'src/components/Icon/data',
},
])
.then(async (answers) => {
const { iconSet, output, useType } = answers;
const outputDir = path.resolve(process.cwd(), output);
fs.ensureDir(outputDir);
const genCollections = collections.filter((item) => [iconSet].includes(item.id));
const prefixSet: string[] = [];
for (const info of genCollections) {
const data = await fs.readJSON(path.join(dir, 'json', `${info.id}.json`));
if (data) {
const { prefix } = data;
const isLocal = useType === 'local';
const icons = Object.keys(data.icons).map(
(item) => `${isLocal ? prefix + ':' : ''}${item}`,
);
await fs.writeFileSync(
path.join(output, `icons.data.ts`),
`export default ${isLocal ? JSON.stringify(icons) : JSON.stringify({ prefix, icons })}`,
);
prefixSet.push(prefix);
}
}
fs.emptyDir(path.join(process.cwd(), 'node_modules/.vite'));
console.log(
`${colors.cyan(`[${pkg.name}]`)}` + ' - Icon generated successfully:' + `[${prefixSet}]`,
);
});
}
generateIcon();

View File

@@ -0,0 +1,7 @@
/**
* Get the configuration file variable name
* @param env
*/
export const getConfigFileName = (env: Record<string, any>) => {
return `__PRODUCTION__${env.VITE_GLOB_APP_SHORT_NAME || '__APP'}__CONF__`.toUpperCase().replace(/\s/g, '');
};

47
build/script/buildConf.ts Normal file
View File

@@ -0,0 +1,47 @@
/**
* Generate additional configuration files when used for packaging. The file can be configured with some global variables, so that it can be changed directly externally without repackaging
*/
import { GLOB_CONFIG_FILE_NAME, OUTPUT_DIR } from '../constant';
import fs, { writeFileSync } from 'fs-extra';
import colors from 'picocolors';
import { getEnvConfig, getRootPath } from '../utils';
import { getConfigFileName } from '../getConfigFileName';
import pkg from '../../package.json';
interface CreateConfigParams {
configName: string;
config: any;
configFileName?: string;
}
function createConfig(params: CreateConfigParams) {
const { configName, config, configFileName } = params;
try {
const windowConf = `window.${configName}`;
// Ensure that the variable will not be modified
let configStr = `${windowConf}=${JSON.stringify(config)};`;
configStr += `
Object.freeze(${windowConf});
Object.defineProperty(window, "${configName}", {
configurable: false,
writable: false,
});
`.replace(/\s/g, '');
fs.mkdirp(getRootPath(OUTPUT_DIR));
writeFileSync(getRootPath(`${OUTPUT_DIR}/${configFileName}`), configStr);
console.log(colors.cyan(`✨ [${pkg.name}]`) + ` - configuration file is build successfully:`);
console.log(colors.gray(OUTPUT_DIR + '/' + colors.green(configFileName)) + '\n');
} catch (error) {
console.log(colors.red('configuration file configuration file failed to package:\n' + error));
}
}
export function runBuildConfig() {
const config = getEnvConfig();
const configFileName = getConfigFileName(config);
createConfig({ config, configName: configFileName, configFileName: GLOB_CONFIG_FILE_NAME });
}

23
build/script/postBuild.ts Normal file
View File

@@ -0,0 +1,23 @@
// #!/usr/bin/env node
import { runBuildConfig } from './buildConf';
import colors from 'picocolors';
import pkg from '../../package.json';
export const runBuild = async () => {
try {
const argvList = process.argv.splice(2);
// Generate configuration file
if (!argvList.includes('disabled-config')) {
runBuildConfig();
}
console.log(`${colors.cyan(`[${pkg.name}]`)}` + ' - build successfully!');
} catch (error) {
console.log(colors.red('vite build error:\n' + error));
process.exit(1);
}
};
runBuild();

88
build/utils.ts Normal file
View File

@@ -0,0 +1,88 @@
import fs from 'fs';
import path from 'path';
import dotenv from 'dotenv';
export function isDevFn(mode: string): boolean {
return mode === 'development';
}
export function isProdFn(mode: string): boolean {
return mode === 'production';
}
/**
* Whether to generate package preview
*/
export function isReportMode(): boolean {
return process.env.REPORT === 'true';
}
// Read all environment variable configuration files to process.env
export function wrapperEnv(envConf: Recordable): ViteEnv {
const ret: any = {};
for (const envName of Object.keys(envConf)) {
let realName = envConf[envName].replace(/\\n/g, '\n');
realName = realName === 'true' ? true : realName === 'false' ? false : realName;
if (envName === 'VITE_PROXY' && realName) {
try {
realName = JSON.parse(realName.replace(/'/g, '"'));
} catch (error) {
realName = '';
}
}
ret[envName] = realName;
// if (typeof realName === 'string') {
// process.env[envName] = realName;
// } else if (typeof realName === 'object') {
// process.env[envName] = JSON.stringify(realName);
// }
}
return ret;
}
/**
* 获取当前环境下生效的配置文件名
*/
function getConfFiles() {
const script = process.env.npm_lifecycle_script;
const reg = new RegExp('--mode ([a-z_\\d]+)');
const result = reg.exec(script as string) as any;
if (result) {
const mode = result[1] as string;
return ['.env', `.env.${mode}`];
}
return ['.env', '.env.production'];
}
/**
* Get the environment variables starting with the specified prefix
* @param match prefix
* @param confFiles ext
*/
export function getEnvConfig(match = 'VITE_GLOB_', confFiles = getConfFiles()) {
let envConfig = {};
confFiles.forEach(item => {
try {
const env = dotenv.parse(fs.readFileSync(path.resolve(process.cwd(), item)));
envConfig = { ...envConfig, ...env };
} catch (e) {
console.error(`Error in parsing ${item}`, e);
}
});
const reg = new RegExp(`^(${match})`);
Object.keys(envConfig).forEach(key => {
if (!reg.test(key)) {
Reflect.deleteProperty(envConfig, key);
}
});
return envConfig;
}
/**
* Get user root directory
* @param dir file path
*/
export function getRootPath(...dir: string[]) {
return path.resolve(process.cwd(), ...dir);
}

49
build/vite/plugin/cdn.ts Normal file
View File

@@ -0,0 +1,49 @@
import { Plugin as importToCDN } from 'vite-plugin-cdn-import';
/**
* @description 打包时采用`cdn`模式仅限外网使用默认不采用如果需要采用cdn模式请在 .env.production 文件,将 VITE_CDN 设置成true
* 平台采用国内cdnhttps://www.bootcdn.cn当然你也可以选择 https://unpkg.com 或者 https://www.jsdelivr.com
* 注意上面提到的仅限外网使用也不是完全肯定的如果你们公司内网部署的有相关js、css文件也可以将下面配置对应改一下整一套内网版cdn
*/
export const configCdnPlugin = importToCDN({
//prodUrl解释 name: 对应下面modules的nameversion: 自动读取本地package.json中dependencies依赖中对应包的版本号path: 对应下面modules的path当然也可写完整路径会替换prodUrl
prodUrl: 'https://cdn.bootcdn.net/ajax/libs/{name}/{version}/{path}',
modules: [
{
name: 'vue',
var: 'Vue',
path: 'vue.global.prod.min.js',
},
{
name: 'vue-router',
var: 'VueRouter',
path: 'vue-router.global.min.js',
},
// 项目中没有直接安装vue-demi但是pinia用到了所以需要在引入pinia前引入vue-demihttps://github.com/vuejs/pinia/blob/v2/packages/pinia/package.json#L77
{
name: 'vue-demi',
var: 'VueDemi',
path: 'https://cdn.bootcdn.net/ajax/libs/vue-demi/0.14.5/index.iife.min.js',
},
{
name: 'pinia',
var: 'Pinia',
path: 'pinia.iife.min.js',
},
{
name: 'axios',
var: 'axios',
path: 'axios.min.js',
},
{
name: 'dayjs',
var: 'dayjs',
path: 'dayjs.min.js',
},
{
name: 'echarts',
var: 'echarts',
path: 'echarts.min.js',
},
],
});

View File

@@ -0,0 +1,35 @@
/**
* Used to package and output gzip. Note that this does not work properly in Vite, the specific reason is still being investigated
* https://github.com/anncwb/vite-plugin-compression
*/
import type { PluginOption } from 'vite';
import compressPlugin from 'vite-plugin-compression';
export function configCompressPlugin(
compress: 'gzip' | 'brotli' | 'none',
deleteOriginFile = false,
): PluginOption | PluginOption[] {
const compressList = compress.split(',');
const plugins: PluginOption[] = [];
if (compressList.includes('gzip')) {
plugins.push(
compressPlugin({
ext: '.gz',
deleteOriginFile,
}),
);
}
if (compressList.includes('brotli')) {
plugins.push(
compressPlugin({
ext: '.br',
algorithm: 'brotliCompress',
deleteOriginFile,
}),
);
}
return plugins;
}

40
build/vite/plugin/html.ts Normal file
View File

@@ -0,0 +1,40 @@
/**
* Plugin to minimize and use ejs template syntax in index.html.
* https://github.com/anncwb/vite-plugin-html
*/
import type { PluginOption } from 'vite';
import { createHtmlPlugin } from 'vite-plugin-html';
import pkg from '../../../package.json';
import { GLOB_CONFIG_FILE_NAME } from '../../constant';
export function configHtmlPlugin(env: ViteEnv, isBuild: boolean) {
const { VITE_GLOB_APP_TITLE, VITE_PUBLIC_PATH } = env;
const path = VITE_PUBLIC_PATH.endsWith('/') ? VITE_PUBLIC_PATH : `${VITE_PUBLIC_PATH}/`;
const getAppConfigSrc = () => {
return `${path || '/'}${GLOB_CONFIG_FILE_NAME}?v=${pkg.version}-${new Date().getTime()}`;
};
const htmlPlugin: PluginOption[] = createHtmlPlugin({
minify: isBuild,
inject: {
// Inject data into ejs template
data: {
title: VITE_GLOB_APP_TITLE,
},
// Embed the generated app.config.js file
tags: isBuild
? [
{
tag: 'script',
attrs: {
src: getAppConfigSrc(),
},
},
]
: [],
},
});
return htmlPlugin;
}

View File

@@ -0,0 +1,69 @@
import { PluginOption } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
import purgeIcons from 'vite-plugin-purge-icons';
import windiCSS from 'vite-plugin-windicss';
import VitePluginCertificate from 'vite-plugin-mkcert';
// import vueSetupExtend from 'vite-plugin-vue-setup-extend';
import { configHtmlPlugin } from './html';
import { configPwaConfig } from './pwa';
import { configCompressPlugin } from './compress';
import { configVisualizerConfig } from './visualizer';
import { configThemePlugin } from './theme';
import { configSvgIconsPlugin } from './svgSprite';
import { configCdnPlugin } from './cdn';
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
const { VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE, VITE_CDN } = viteEnv;
const vitePlugins: (PluginOption | PluginOption[])[] = [
// have to
vue({
template: {
compilerOptions: {
// 解决vue3上marquee标签报错
isCustomElement: tag => tag === 'marquee',
},
},
}),
// have to
vueJsx(),
// support name
// vueSetupExtend(),
VitePluginCertificate({
source: 'coding',
}),
];
// vite-plugin-windicss
vitePlugins.push(windiCSS());
// vite-plugin-html
vitePlugins.push(configHtmlPlugin(viteEnv, isBuild));
// vite-plugin-svg-icons
vitePlugins.push(configSvgIconsPlugin(isBuild));
// vite-plugin-purge-icons
vitePlugins.push(purgeIcons());
// rollup-plugin-visualizer
vitePlugins.push(configVisualizerConfig());
// vite-plugin-theme
vitePlugins.push(configThemePlugin(isBuild));
// The following plugins only work in the production environment
if (isBuild) {
// vite-plugin-cdn-import
if (VITE_CDN) vitePlugins.push(configCdnPlugin);
// rollup-plugin-gzip
vitePlugins.push(configCompressPlugin(VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE));
// vite-plugin-pwa
vitePlugins.push(configPwaConfig(viteEnv));
}
return vitePlugins;
}

View File

@@ -0,0 +1,5 @@
import PurgeIcons from 'vite-plugin-purge-icons';
export function configPurgeIconsPlugin() {
return PurgeIcons();
}

33
build/vite/plugin/pwa.ts Normal file
View File

@@ -0,0 +1,33 @@
/**
* Yunzhupaas-config PWA for Vite
* https://github.com/antfu/vite-plugin-pwa
*/
import { VitePWA } from 'vite-plugin-pwa';
export function configPwaConfig(env: ViteEnv) {
const { VITE_USE_PWA, VITE_GLOB_APP_TITLE, VITE_GLOB_APP_SHORT_NAME } = env;
if (VITE_USE_PWA) {
// vite-plugin-pwa
const pwaPlugin = VitePWA({
manifest: {
name: VITE_GLOB_APP_TITLE,
short_name: VITE_GLOB_APP_SHORT_NAME,
icons: [
{
src: './resource/img/pwa-192x192.png',
sizes: '192x192',
type: 'image/png',
},
{
src: './resource/img/pwa-512x512.png',
sizes: '512x512',
type: 'image/png',
},
],
},
});
return pwaPlugin;
}
return [];
}

View File

@@ -0,0 +1,82 @@
/**
* Introduces component library styles on demand.
* https://github.com/anncwb/vite-plugin-style-import
*/
import { createStyleImportPlugin } from 'vite-plugin-style-import';
export function configStyleImportPlugin(_isBuild: boolean) {
if (!_isBuild) {
return [];
}
const styleImportPlugin = createStyleImportPlugin({
libs: [
{
libraryName: 'ant-design-vue',
esModule: true,
resolveStyle: name => {
// 这里是无需额外引入样式文件的“子组件”列表
const ignoreList = [
'anchor-link',
'sub-menu',
'menu-item',
'menu-divider',
'menu-item-group',
'breadcrumb-item',
'breadcrumb-separator',
'form-item',
'step',
'select-option',
'select-opt-group',
'card-grid',
'card-meta',
'collapse-panel',
'descriptions-item',
'list-item',
'list-item-meta',
'table-column',
'table-column-group',
'tab-pane',
'tab-content',
'timeline-item',
'tree-node',
'skeleton-input',
'skeleton-avatar',
'skeleton-title',
'skeleton-paragraph',
'skeleton-image',
'skeleton-button',
];
// 这里是需要额外引入样式的子组件列表
// 单独引入子组件时需引入组件样式,否则会在打包后导致子组件样式丢失
const replaceList = {
textarea: 'input',
'typography-text': 'typography',
'typography-title': 'typography',
'typography-paragraph': 'typography',
'typography-link': 'typography',
'dropdown-button': 'dropdown',
'input-password': 'input',
'input-search': 'input',
'input-group': 'input',
'radio-group': 'radio',
'checkbox-group': 'checkbox',
'layout-sider': 'layout',
'layout-content': 'layout',
'layout-footer': 'layout',
'layout-header': 'layout',
'month-picker': 'date-picker',
'range-picker': 'date-picker',
'image-preview-group': 'image',
};
return ignoreList.includes(name)
? ''
: replaceList.hasOwnProperty(name)
? `ant-design-vue/es/${replaceList[name]}/style/index`
: `ant-design-vue/es/${name}/style/index`;
},
},
],
});
return styleImportPlugin;
}

View File

@@ -0,0 +1,10 @@
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
import path from 'path';
export function configSvgIconsPlugin(isBuild: boolean) {
return createSvgIconsPlugin({
iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
svgoOptions: isBuild,
symbolId: 'icon-[dir]-[name]',
});
}

View File

@@ -0,0 +1,17 @@
/**
* Vite Plugin for fast creating SVG sprites.
* https://github.com/anncwb/vite-plugin-svg-icons
*/
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
import path from 'path';
export function configSvgIconsPlugin(isBuild: boolean) {
const svgIconsPlugin = createSvgIconsPlugin({
iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
svgoOptions: isBuild,
// default
symbolId: 'icon-[dir]-[name]',
});
return svgIconsPlugin;
}

101
build/vite/plugin/theme.ts Normal file
View File

@@ -0,0 +1,101 @@
/**
* Vite plugin for website theme color switching
* https://github.com/anncwb/vite-plugin-theme
*/
import type { PluginOption } from 'vite';
import path from 'path';
import { viteThemePlugin, antdDarkThemePlugin, mixLighten, mixDarken, tinycolor } from '@rys-fe/vite-plugin-theme';
import { getThemeColors, generateColors } from '../../config/themeConfig';
import { generateModifyVars } from '../../generate/generateModifyVars';
export function configThemePlugin(isBuild: boolean): PluginOption[] {
const colors = generateColors({
mixDarken,
mixLighten,
tinycolor,
});
// update-begin-修复编译后主题色切换不生效黑屏的问题-----------------------
// https://github.com/vbenjs/vue-vben-admin/issues/1445
// 抽取出viteThemePlugin插件下方会根据不同环境设置enforce
const vite_theme_plugin = viteThemePlugin({
resolveSelector: s => {
s = s.trim();
switch (s) {
case '.ant-steps-item-process .ant-steps-item-icon > .ant-steps-icon':
return '.ant-steps-item-icon > .ant-steps-icon';
case '.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled)':
case '.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):hover':
case '.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):active':
return s;
case '.ant-steps-item-icon > .ant-steps-icon':
return s;
case '.ant-select-item-option-selected:not(.ant-select-item-option-disabled)':
return s;
default:
if (s.indexOf('.ant-btn') >= -1) {
// 按钮被重新定制过需要过滤掉class防止覆盖
return s;
}
}
return s.startsWith('[data-theme') ? s : `[data-theme] ${s}`;
},
colorVariables: [...getThemeColors(), ...colors],
});
vite_theme_plugin.forEach(function (item) {
//对vite:theme插件特殊配置
if ('vite:theme' === item.name) {
// 打包时去除enforce: "post"vite 2.6.x适配否则生成app-theme-style为空因为async transform(code, id) {的code没有正确获取
if (isBuild) {
delete item.enforce;
}
}
});
// update-end-修复编译后主题色切换不生效黑屏的问题-----------------------
const plugin = [
vite_theme_plugin,
antdDarkThemePlugin({
preloadFiles: [path.resolve(process.cwd(), 'src/design/index.less')],
filter: id => (isBuild ? !id.endsWith('antd.less') : true),
// extractCss: false,
darkModifyVars: {
...generateModifyVars(),
'text-color': '#c9d1d9',
'primary-1': 'rgb(255 255 255 / 8%)',
'text-color-base': '#c9d1d9',
'text-color-label': '#606266',
'component-background': '#151515',
'hover-background': '#333333',
'heading-color': 'rgb(255 255 255 / 65%)',
// black: '#0e1117',
// #8b949e
'text-color-secondary': '#8b949e',
'border-color-base': '#303030',
'border-color-base1': '#303030',
// 'border-color-split': '#30363d',
'item-active-bg': '#111b26',
'app-base-background': '#1e1e1e',
'app-content-background': '#1e1e1e',
'app-main-background': '#333333',
'tree-node-selected-bg': '#11263c',
'selected-hover-bg': 'rgba(255, 255, 255, 0.08)',
'alert-success-border-color': '#274916',
'alert-success-bg-color': '#162312',
'alert-success-icon-color': '#49aa19',
'alert-info-border-color': '#153450',
'alert-info-bg-color': '#111b26',
'alert-info-icon-color': '#177ddc',
'alert-warning-border-color': '#594214',
'alert-warning-bg-color': '#2b2111',
'alert-warning-icon-color': '#d89614',
'alert-error-border-color': '#58181c',
'alert-error-bg-color': '#2a1215',
'alert-error-icon-color': '#a61d24',
},
}),
];
return plugin as unknown as PluginOption[];
}

View File

@@ -0,0 +1,17 @@
/**
* Package file volume analysis
*/
import visualizer from 'rollup-plugin-visualizer';
import { isReportMode } from '../../utils';
export function configVisualizerConfig() {
if (isReportMode()) {
return visualizer({
filename: './node_modules/.cache/visualizer/stats.html',
open: true,
gzipSize: true,
brotliSize: true,
}) as Plugin;
}
return [];
}

View File

@@ -0,0 +1,6 @@
import type { Plugin } from 'vite';
import WindiCSS from 'vite-plugin-windicss';
export function configWindiCssPlugin(): Plugin[] {
return (WindiCSS() as Plugin[]).filter(Boolean);
}

34
build/vite/proxy.ts Normal file
View File

@@ -0,0 +1,34 @@
/**
* Used to parse the .env.development proxy configuration
*/
import type { ProxyOptions } from 'vite';
type ProxyItem = [string, string];
type ProxyList = ProxyItem[];
type ProxyTargetList = Record<string, ProxyOptions>;
const httpsRE = /^https:\/\//;
/**
* Generate proxy
* @param list
*/
export function createProxy(list: ProxyList = []) {
const ret: ProxyTargetList = {};
for (const [prefix, target] of list) {
const isHttps = httpsRE.test(target);
// https://github.com/http-party/node-http-proxy#options
ret[prefix] = {
target: target,
changeOrigin: true,
ws: true,
rewrite: path => path.replace(new RegExp(`^${prefix}`), ''),
// https is require secure=false
...(isHttps ? { secure: false } : {}),
};
}
return ret;
}

83
deploy/default.conf Normal file
View File

@@ -0,0 +1,83 @@
server {
listen 80;
server_name localhost;
root /data/yunzhupaas/yunzhupaas-web-vue3;
index index.html;
gzip on;
gzip_static on;
gzip_min_length 1k;
gzip_comp_level 4;
gzip_proxied any;
gzip_types text/plain text/xml text/css;
gzip_vary on;
gzip_http_version 1.0;
gzip_disable "MSIE [1-6]\.(?!.*SV1)";
# YUNZHUPAAS-START
# 设置上传文件的大小
client_max_body_size 100m;
# 添加头部信息
proxy_set_header Host $http_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_set_header X-NginX-Proxy true;
# This is necessary to pass the correct IP to be hashed
real_ip_header X-Real-IP;
proxy_connect_timeout 300;
# 前端主项目伪静态
location / {
try_files $uri $uri/ /index.html;
}
# 大屏伪静态
location /DataV {
try_files $uri $uri/ /DataV/index.html;
}
# 报表伪静态
location /Report/icons/{
try_files $uri $uri/ /Report/icons/;
}
# 主项目后端接口
location /api/ {
proxy_pass http://yunzhupaas-gateway-external.java-cloud-v510:30000;
}
location /websocket {
proxy_pass http://yunzhupaas-gateway-external.java-cloud-v510:30000/api/message/websocket;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 600s;
}
# 报表后端接口
location /ReportServer/ {
proxy_pass http://yunzhupaas-datareport-external.java-cloud-v510:30007/;
}
# Flowable后端接口
location /api/Flow {
proxy_pass http://yunzhupaas-flowable-external.java-cloud-v510:31000;
}
# Univer后端接口
location /api/Report {
proxy_pass http://yunzhupaas-univer-external.java-cloud-v510:32000;
}
# 文件预览
location /FileServer {
proxy_pass http://yunzhupaas-file-preview-external.java-cloud-v510:30090;
}
location ~ /FileServer/*.*\.(js|css)?$ {
proxy_pass http://yunzhupaas-file-preview-external.java-cloud-v510:30090;
}
# YUNZHUPAAS-END
}

38
index.html Normal file
View File

@@ -0,0 +1,38 @@
<!DOCTYPE html>
<html lang="en" id="htmlRoot">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<link rel="stylesheet" href="/css/index.css" />
<link rel="stylesheet" href="/css/print-lock.css" />
<link rel="stylesheet" href="/css/fonts/ym/iconfont.css" />
<link rel="stylesheet" href="/css/fonts/ym-custom/iconfont.css" />
<meta name="renderer" content="webkit" />
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0" />
<!-- <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests" /> -->
<title><%= title %></title>
<link rel="icon" href="/favicon.ico" />
</head>
<body>
<script>
(() => {
var htmlRoot = document.getElementById('htmlRoot');
var theme = window.localStorage.getItem('__APP__DARK__MODE__');
if (htmlRoot && theme) {
htmlRoot.setAttribute('data-theme', theme);
theme = htmlRoot = null;
}
})();
</script>
<div id="app">
<div class="app-loading">
<div class="app-loading-wrap">
<div class="app-loading-dots">
<span class="dot dot-spin"><i></i><i></i><i></i><i></i></span>
</div>
</div>
</div>
</div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

206
package.json Normal file
View File

@@ -0,0 +1,206 @@
{
"name": "yunzhupaas-web-vue3",
"version": "5.2.0",
"description": "云筑项目管理平台",
"author": {
"name": "深圳市乐程软件有限公司",
"email": "service@yunzhupaas.cn",
"url": "https://www.yunzhupaas.cn"
},
"homepage": "https://www.yunzhupaas.cn",
"scripts": {
"commit": "czg",
"bootstrap": "pnpm install --registry=https://registry.npmmirror.com",
"serve": "npm run dev",
"dev": "vite",
"build": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=8192 vite build && esno ./build/script/postBuild.ts",
"build:test": "cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build --mode test && esno ./build/script/postBuild.ts",
"build:no-cache": "pnpm clean:cache && npm run build",
"report": "cross-env REPORT=true npm run build",
"type:check": "vue-tsc --noEmit --skipLibCheck",
"preview": "npm run build && vite preview",
"preview:dist": "vite preview",
"log": "conventional-changelog -p angular -i CHANGELOG.md -s",
"clean:cache": "rimraf node_modules/.cache/ && rimraf node_modules/.vite",
"clean:lib": "rimraf node_modules",
"lint:eslint": "eslint --cache --max-warnings 0 \"{src,mock}/**/*.{vue,ts,tsx}\" --fix",
"lint:prettier": "prettier --write \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"",
"lint:stylelint": "stylelint --cache --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
"lint:lint-staged": "lint-staged",
"reinstall": "rimraf pnpm-lock.yaml && rimraf package.lock.json && rimraf node_modules && npm run bootstrap",
"gen:icon": "esno ./build/generate/icon/index.ts"
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"{!(package)*.json,*.code-snippets,.!(browserslist)*rc}": [
"prettier --write--parser json"
],
"package.json": [
"prettier --write"
],
"*.vue": [
"eslint --fix",
"prettier --write",
"stylelint --fix"
],
"*.{scss,less,styl,html}": [
"stylelint --fix",
"prettier --write"
],
"*.md": [
"prettier --write"
]
},
"config": {
"commitizen": {
"path": "node_modules/cz-git"
}
},
"dependencies": {
"@amap/amap-jsapi-loader": "^1.0.1",
"@ant-design/colors": "^7.1.0",
"@ant-design/icons-vue": "^7.0.1",
"@fullcalendar/core": "^6.1.14",
"@fullcalendar/daygrid": "^6.1.14",
"@fullcalendar/interaction": "^6.1.14",
"@fullcalendar/timegrid": "^6.1.14",
"@fullcalendar/vue3": "^6.1.14",
"@iconify/iconify": "^3.1.0",
"@logicflow/core": "^1.2.1",
"@logicflow/extension": "^1.2.1",
"@vue/runtime-core": "^3.4.27",
"@vue/shared": "^3.4.27",
"@vueuse/core": "^10.1.2",
"@vueuse/shared": "^10.1.2",
"@zxcvbn-ts/core": "^2.2.1",
"ant-design-vue": "^4.2.3",
"axios": "^1.4.0",
"bpmn-js": "16.3.2",
"bpmn-js-properties-panel": "5.7.0",
"camunda-bpmn-moddle": "6.1.2",
"codemirror": "^5.65.12",
"cron-parser": "^4.8.1",
"cropperjs": "^1.5.13",
"crypto-js": "^4.1.1",
"dayjs": "^1.11.7",
"diagram-js": "11.9.1",
"diagram-js-minimap": "4.1.0",
"echarts": "^5.4.2",
"echarts-stat": "^1.2.0",
"inherits-browser": "^0.1.0",
"intro.js": "^7.0.1",
"jsbarcode": "^3.11.5",
"lodash-es": "^4.17.21",
"min-dash": "^4.2.1",
"min-dom": "^4.1.0",
"monaco-editor": "^0.38.0",
"nprogress": "^0.2.0",
"path-to-regexp": "^6.2.1",
"pinia": "^2.1.3",
"print-js": "^1.6.0",
"qrcode": "^1.5.1",
"qs": "^6.11.1",
"reconnecting-websocket": "^4.4.0",
"resize-observer-polyfill": "^1.5.1",
"showdown": "^2.1.0",
"sortablejs": "^1.15.0",
"spark-md5": "^3.0.2",
"terser": "^5.14.2",
"tiny-svg": "^3.0.1",
"tinymce": "^5.10.7",
"v-code-diff": "^1.13.1",
"vditor": "^3.9.1",
"vue": "^3.4.27",
"vue-grid-layout": "^3.0.0-beta1",
"vue-i18n": "^9.13.1",
"vue-json-pretty": "^2.2.4",
"vue-plugin-hiprint": "0.0.57-beta24",
"vue-router": "^4.3.2",
"vue-simple-uploader": "1.0.0",
"vue-types": "^5.1.2",
"vue3-draggable-resizable": "^1.6.5",
"vue3-tree-org": "^4.2.2",
"vuedraggable": "^4.1.0",
"xlsx": "^0.18.5",
"zero-bpmn": "1.1.1",
"zero-univer": "1.1.2"
},
"devDependencies": {
"@commitlint/cli": "^17.8.1",
"@commitlint/config-conventional": "^17.8.1",
"@iconify/json": "^2.2.43",
"@purge-icons/generated": "^0.9.0",
"@rys-fe/vite-plugin-theme": "^0.8.6",
"@types/codemirror": "^5.60.7",
"@types/crypto-js": "^4.2.2",
"@types/fs-extra": "^11.0.1",
"@types/inquirer": "^8.2.6",
"@types/intro.js": "^5.1.1",
"@types/lodash-es": "^4.17.7",
"@types/node": "^18.15.11",
"@types/nprogress": "^0.2.0",
"@types/qrcode": "^1.5.0",
"@types/qs": "^6.9.7",
"@types/showdown": "^2.0.0",
"@types/sortablejs": "^1.15.1",
"@typescript-eslint/eslint-plugin": "^5.57.0",
"@typescript-eslint/parser": "^5.57.0",
"@vitejs/plugin-vue": "^4.2.3",
"@vitejs/plugin-vue-jsx": "^3.0.1",
"@vue/compiler-sfc": "^3.4.27",
"@vue/test-utils": "^2.4.5",
"autoprefixer": "^10.4.14",
"conventional-changelog-cli": "^2.2.2",
"cross-env": "^7.0.3",
"cz-git": "^1.6.1",
"czg": "^1.6.1",
"dotenv": "^16.0.3",
"eslint": "^8.37.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-vue": "^9.10.0",
"esno": "^0.16.3",
"fs-extra": "^11.1.1",
"inquirer": "^9.1.5",
"jquery": "^3.7.1",
"less": "^4.1.3",
"lint-staged": "13.2.0",
"npm-run-all": "^4.1.5",
"picocolors": "^1.0.0",
"postcss": "^8.4.21",
"postcss-html": "^1.5.0",
"postcss-less": "^6.0.0",
"prettier": "^2.8.8",
"rimraf": "^4.4.1",
"rollup": "^3.7.4",
"rollup-plugin-visualizer": "^5.9.0",
"stylelint": "^15.4.0",
"stylelint-config-prettier": "^9.0.3",
"stylelint-config-recommended": "^11.0.0",
"stylelint-config-recommended-vue": "^1.4.0",
"stylelint-config-standard": "^32.0.0",
"stylelint-order": "^6.0.3",
"ts-node": "^10.9.1",
"typescript": "^5.4.5",
"vite": "^4.5.3",
"vite-plugin-cdn-import": "^0.3.5",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-html": "^3.2.0",
"vite-plugin-mkcert": "^1.10.1",
"vite-plugin-purge-icons": "^0.9.2",
"vite-plugin-pwa": "^0.14.0",
"vite-plugin-style-import": "^2.0.0",
"vite-plugin-svg-icons": "^2.0.1",
"vite-plugin-vue-setup-extend": "^0.4.0",
"vite-plugin-windicss": "^1.8.10",
"vue-eslint-parser": "^9.1.1",
"vue-tsc": "^2.0.19"
},
"engines": {
"node": ">=16.15.0",
"pnpm": ">=8.1.0"
}
}

14173
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

5
postcss.config.js Normal file
View File

@@ -0,0 +1,5 @@
module.exports = {
plugins: {
autoprefixer: {},
},
};

13
prettier.config.js Normal file
View File

@@ -0,0 +1,13 @@
module.exports = {
printWidth: 160,
semi: true,
vueIndentScriptAndStyle: true,
singleQuote: true,
trailingComma: 'all',
proseWrap: 'never',
htmlWhitespaceSensitivity: 'strict',
endOfLine: 'auto',
bracketSameLine: true,
jsxBracketSameLine: true,
arrowParens: 'avoid',
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
public/cdn/socials/qq.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

135
public/css/index.css Normal file
View File

@@ -0,0 +1,135 @@
html[data-theme='dark'] .app-loading {
background-color: #2c344a;
}
html[data-theme='dark'] .app-loading .app-loading-title {
color: rgb(255 255 255 / 85%);
}
html[data-theme="dark"] .ant-steps-navigation .ant-steps-item::after {
border-bottom: none;
border-left: none;
}
.app-loading {
display: flex;
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
flex-direction: column;
background-color: #f4f7f9;
}
.app-loading .app-loading-wrap {
position: absolute;
top: 50%;
left: 50%;
display: flex;
transform: translate3d(-50%, -50%, 0);
justify-content: center;
align-items: center;
flex-direction: column;
}
.app-loading .dots {
display: flex;
padding: 98px;
justify-content: center;
align-items: center;
}
.app-loading .app-loading-title {
display: flex;
margin-top: 30px;
font-size: 30px;
color: rgb(0 0 0 / 85%);
justify-content: center;
align-items: center;
}
.app-loading .app-loading-logo {
display: block;
width: 90px;
margin: 0 auto;
margin-bottom: 20px;
border: none;
}
.dot {
position: relative;
display: inline-block;
width: 48px;
height: 48px;
margin-top: 30px;
font-size: 32px;
transform: rotate(45deg);
box-sizing: border-box;
animation: antRotate 1.2s infinite linear;
}
.dot i {
position: absolute;
display: block;
width: 20px;
height: 20px;
background-color: #0065cc;
border-radius: 100%;
opacity: 30%;
transform: scale(0.75);
animation: antSpinMove 1s infinite linear alternate;
transform-origin: 50% 50%;
}
.dot i:nth-child(1) {
top: 0;
left: 0;
}
.dot i:nth-child(2) {
top: 0;
right: 0;
animation-delay: 0.4s;
}
.dot i:nth-child(3) {
right: 0;
bottom: 0;
animation-delay: 0.8s;
}
.dot i:nth-child(4) {
bottom: 0;
left: 0;
animation-delay: 1.2s;
}
@keyframes antRotate {
to {
transform: rotate(405deg);
}
}
@-webkit-keyframes antRotate {
to {
transform: rotate(405deg);
}
}
@keyframes antSpinMove {
to {
opacity: 100%;
}
}
@-webkit-keyframes antSpinMove {
to {
opacity: 100%;
}
}
@media (max-width: 600px) {
.app-loading .app-loading-logo,
.app-loading .app-loading-title {
display: none;
}
}

352
public/css/print-lock.css Normal file
View File

@@ -0,0 +1,352 @@
@media print {
body {
margin: 0px;
padding: 0px;
}
}
@page {
margin: 0;
}
.hiprint-printPaper * {
box-sizing: border-box;
-moz-box-sizing: border-box; /* Firefox */
-webkit-box-sizing: border-box; /* Safari */
}
.hiprint-printPaper *:focus {
outline: -webkit-focus-ring-color auto 0px;
}
.hiprint-printPaper {
position: relative;
padding: 0 0 0 0;
page-break-after: always;
-webkit-user-select: none; /* Chrome/Safari/Opera */
-moz-user-select: none; /* Firefox */
user-select: none;
overflow-x: hidden;
overflow: hidden;
}
.hiprint-printPaper .hiprint-printPaper-content {
position: relative;
}
/* 火狐浏览器打印 第一页过后 重叠问题 */
@-moz-document url-prefix() {
.hiprint-printPaper .hiprint-printPaper-content {
position: relative;
margin-top: 20px;
top: -20px
}
}
.hiprint-printPaper.design {
overflow: visible;
}
.hiprint-printTemplate .hiprint-printPanel {
page-break-after: always;
}
.hiprint-printPaper, hiprint-printPanel {
box-sizing: border-box;
border: 0px;
}
.hiprint-printPanel .hiprint-printPaper:last-child {
page-break-after: avoid;
}
.hiprint-printTemplate .hiprint-printPanel:last-child {
page-break-after: avoid;
}
.hiprint-printPaper .hideheaderLinetarget {
border-top: 0px dashed rgb(201, 190, 190) !important;
}
.hiprint-printPaper .hidefooterLinetarget {
border-top: 0px dashed rgb(201, 190, 190) !important;
}
.hiprint-printPaper.design {
border: 1px dashed rgba(170, 170, 170, 0.7);
}
.design .hiprint-printElement-table-content, .design .hiprint-printElement-longText-content {
overflow: hidden;
box-sizing: border-box;
}
.design .resize-panel {
box-sizing: border-box;
border: 1px dotted;
}
.hiprint-printElement-text {
background-color: transparent;
background-repeat: repeat;
padding: 0 0 0 0;
border: 0.75pt none rgb(0, 0, 0);
direction: ltr;
font-family: 'SimSun';
font-size: 9pt;
font-style: normal;
font-weight: normal;
padding-bottom: 0pt;
padding-left: 0pt;
padding-right: 0pt;
padding-top: 0pt;
text-align: left;
text-decoration: none;
line-height: 9.75pt;
box-sizing: border-box;
word-wrap: break-word;
word-break: break-all;
}
.design .hiprint-printElement-text-content {
border: 1px dashed rgb(206, 188, 188);
box-sizing: border-box;
}
.hiprint-printElement-longText {
background-color: transparent;
background-repeat: repeat;
border: 0.75pt none rgb(0, 0, 0);
direction: ltr;
font-family: 'SimSun';
font-size: 9pt;
font-style: normal;
font-weight: normal;
padding-bottom: 0pt;
padding-left: 0pt;
padding-right: 0pt;
padding-top: 0pt;
text-align: left;
text-decoration: none;
line-height: 9.75pt;
box-sizing: border-box;
word-wrap: break-word;
word-break: break-all;
/*white-space: pre-wrap*/
}
.hiprint-printElement-table {
background-color: transparent;
background-repeat: repeat;
color: rgb(0, 0, 0);
border-color: rgb(0, 0, 0);
border-style: none;
direction: ltr;
font-family: 'SimSun';
font-size: 9pt;
font-style: normal;
font-weight: normal;
padding-bottom: 0pt;
padding-left: 0pt;
padding-right: 0pt;
padding-top: 0pt;
text-align: left;
text-decoration: none;
padding: 0 0 0 0;
box-sizing: border-box;
line-height: 9.75pt;
}
.hiprint-printElement-table thead {
background: #e8e8e8;
font-weight: 700;
}
table.hiprint-printElement-tableTarget {
width: 100%;
}
.hiprint-printElement-tableTarget, .hiprint-printElement-tableTarget tr, .hiprint-printElement-tableTarget td {
border-color: rgb(0, 0, 0);
/*border-style: none;*/
/*border: 1px solid rgb(0, 0, 0);*/
font-weight: normal;
direction: ltr;
padding-bottom: 0pt;
padding-left: 4pt;
padding-right: 4pt;
padding-top: 0pt;
text-decoration: none;
vertical-align: middle;
box-sizing: border-box;
word-wrap: break-word;
word-break: break-all;
/*line-height: 9.75pt;
font-size: 9pt;*/
}
.hiprint-printElement-tableTarget-border-all {
border: 1px solid;
}
.hiprint-printElement-tableTarget-border-none {
border: 0px solid;
}
.hiprint-printElement-tableTarget-border-lr {
border-left: 1px solid;
border-right: 1px solid;
}
.hiprint-printElement-tableTarget-border-left {
border-left: 1px solid;
}
.hiprint-printElement-tableTarget-border-right {
border-right: 1px solid;
}
.hiprint-printElement-tableTarget-border-tb {
border-top: 1px solid;
border-bottom: 1px solid;
}
.hiprint-printElement-tableTarget-border-top {
border-top: 1px solid;
}
.hiprint-printElement-tableTarget-border-bottom {
border-bottom: 1px solid;
}
.hiprint-printElement-tableTarget-border-td-none td {
border: 0px solid;
}
.hiprint-printElement-tableTarget-border-td-all td:not(:nth-last-child(-n+2)) {
border-right: 1px solid;
}
.hiprint-printElement-tableTarget-border-td-all td:not(last-child) {
border-right: 1px solid;
}
.hiprint-printElement-tableTarget-border-td-all td:last-child {
border-left: 1px solid;
}
.hiprint-printElement-tableTarget-border-td-all td:last-child:first-child {
border-left: none;
}
/*.hiprint-printElement-tableTarget tr,*/
.hiprint-printElement-tableTarget td {
height: 18pt;
}
.hiprint-printPaper .hiprint-paperNumber {
font-size: 9pt;
}
.design .hiprint-printElement-table-handle {
position: absolute;
height: 21pt;
width: 21pt;
background: red;
z-index: 1;
}
.hiprint-printPaper .hiprint-paperNumber-disabled {
float: right !important;
right: 0 !important;
color: gainsboro !important;
}
.hiprint-printElement-vline, .hiprint-printElement-hline {
border: 0px none rgb(0, 0, 0);
}
.hiprint-printElement-vline {
border-left: 0.75pt solid #000;
border-right: 0px none rgb(0, 0, 0) !important;
border-bottom: 0px none rgb(0, 0, 0) !important;
border-top: 0px none rgb(0, 0, 0) !important;
}
.hiprint-printElement-hline {
border-top: 0.75pt solid #000;
border-right: 0px none rgb(0, 0, 0) !important;
border-bottom: 0px none rgb(0, 0, 0) !important;
border-left: 0px none rgb(0, 0, 0) !important;
}
.hiprint-printElement-oval, .hiprint-printElement-rect {
border: 0.75pt solid #000;
}
.hiprint-text-content-middle {
}
.hiprint-text-content-middle > div {
display: grid;
align-items: center;
}
.hiprint-text-content-bottom {
}
.hiprint-text-content-bottom > div {
display: grid;
align-items: flex-end;
}
.hiprint-text-content-wrap {
}
.hiprint-text-content-wrap .hiprint-text-content-wrap-nowrap {
white-space: nowrap;
}
.hiprint-text-content-wrap .hiprint-text-content-wrap-clip {
white-space: nowrap;
overflow: hidden;
text-overflow: clip;
}
.hiprint-text-content-wrap .hiprint-text-content-wrap-ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/*hi-grid-row */
.hi-grid-row {
position: relative;
height: auto;
margin-right: 0;
margin-left: 0;
zoom: 1;
display: block;
box-sizing: border-box;
}
.hi-grid-row::after, .hi-grid-row::before {
display: table;
content: '';
box-sizing: border-box;
}
.hi-grid-col {
display: block;
box-sizing: border-box;
position: relative;
float: left;
flex: 0 0 auto;
}
.table-grid-row {
margin-left: -0pt;
margin-right: -0pt;
}
.tableGridColumnsGutterRow {
padding-left: 0pt;
padding-right: 0pt;
}
.hiprint-gridColumnsFooter {
text-align: left;
clear: both;
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

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