初始化
部署生产环境 / 发布版本 (push) Successful in 17s Details
部署生产环境 / 编译发布 (push) Successful in 4m29s Details

This commit is contained in:
yj 2024-06-11 10:55:37 +08:00
parent 80d764ad9e
commit 66b6f3c030
54 changed files with 27402 additions and 1 deletions

7
.env.development Normal file
View File

@ -0,0 +1,7 @@
#基础API 绝对的
VITE_BASE_URL_API = 'http://192.168.2.9:6500'
VITE_BASE_URL_DRAW_API = 'http://192.168.2.9:6555'
#当前IP 相对的
VITE_BASE_CURRENT_API = '.'
#开发环境
VITE_ENV = 'development'

7
.env.production Normal file
View File

@ -0,0 +1,7 @@
#基础API 绝对的
VITE_BASE_URL_API = 'http://192.168.2.9:6500'
VITE_BASE_URL_DRAW_API = 'http://192.168.2.9:6555'
#当前IP 相对的
VITE_BASE_CURRENT_API = '.'
#生产环境
VITE_ENV = 'production'

7
.env.test Normal file
View File

@ -0,0 +1,7 @@
#基础API 绝对的
VITE_BASE_URL_API = 'http://192.168.2.9:6500'
VITE_BASE_URL_DRAW_API = 'http://192.168.2.9:6555'
#当前IP 相对的
VITE_BASE_CURRENT_API = '.'
#开发环境
VITE_ENV = 'test'

42
.eslintrc.js Normal file
View File

@ -0,0 +1,42 @@
module.exports = {
root: true,
env: {
browser: true,
node: true,
es6: true,
// 使用setup语法糖
'vue/setup-compiler-macros': true,
},
extends: ['airbnb-base', 'plugin:import/recommended', 'plugin:vue/vue3-essential', 'plugin:vue/vue3-recommended', 'plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'],
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
sourceType: 'module',
},
plugins: ['vue', '@typescript-eslint'],
rules: {
'import/extensions': [2, 'never', { 'web.js': 'never', json: 'never' }],
'@typescript-eslint/no-explicit-any': ['off'],
'import/no-extraneous-dependencies': [2, { devDependencies: true }],
'import/no-unresolved': 'off',
'@typescript-eslint/no-var-requires': 'off',
'prettier/prettier': 'off',
singleQuote:false,
semi:'never',
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
eqeqeq: 'off', // 要求使用 === 和 !==
indent: ['error', 2, { SwitchCase: 1 }], // 缩进风格
'newline-after-var': 'error', // 变量声明后是否需要空一行
'no-eq-null': 'error', // 禁止对null使用==或!=运算符
'no-eval': 'warn', // 禁止使用eval
'no-var': 'error',
'no-multi-spaces': 'warn', // 不能用多余的空格
'no-trailing-spaces': 'warn', // 一行结束后面不要有空格
'no-undef': 'warn', // 不能有未定义的变量
'semi-spacing': 'error', // 强制分号后面有空格
'@typescript-eslint/semi': 'error',
quotes: ['error', 'single'],
'arrow-spacing': 'error', // 要求箭头函数的=>前后有空格
},
};

View File

@ -0,0 +1,109 @@
name: 部署开发环境
on:
push:
branches:
- develop
env:
IMAGE_NAME: ${{ gitea.repository_owner }}/usercenter.web
PUBLISH_PATH: usercenter.web
PUBLISH_HOST: 10.255.255.44
PUBLISH_PORT: 22
PUBLISH_REPLICAS: 1 #启动副本数量
jobs:
job1:
name: 编译发布
runs-on: linux
container:
image: node:16-bullseye
volumes:
#挂载缓存
- /opt/.runner/node_modules:/cache
steps:
- name: 下载源码
uses: actions/checkout@v3
- name: 使用缓存
uses: actions/cache@master
with:
mount: |
node_modules
- name: 编译项目
run: |
echo =========构建项目开始========
rm -rf package-lock.json #删除文件
npm config set registry https://registry.npmmirror.com/
npm install
npm run build:dev
echo =========构建项目结束========
- name: 打包上传镜像
uses: actions/docker@master
with:
registry: ${{ secrets.DOCKER_REGISTRY }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
image: ${{ env.IMAGE_NAME }}
context: .
tags: latest,${{ gitea.ref_name }}
- name: 生成环境变量文件
env:
env_file: |
REGISTRY=${{ secrets.DOCKER_REGISTRY }}
OWNER=${{ gitea.repository_owner }}
TAG=${{ gitea.ref_name }}
REPLICAS=${{ env.PUBLISH_REPLICAS }}
run: |
echo "$env_file">.env
- name: 复制发布脚本
uses: docker://appleboy/drone-scp
with:
host: ${{ env.PUBLISH_HOST }}
port: ${{ env.PUBLISH_PORT }}
username: ${{ secrets.PUBLISH_USER_NAME }}
password: ${{ secrets.PUBLISH_PASSWORD }}
target: ${{ env.PUBLISH_PATH }}
source: docker-swarm.yaml,.env
- name: 部署到服务器
uses: docker://appleboy/drone-ssh
with:
host: ${{ env.PUBLISH_HOST }}
port: ${{ env.PUBLISH_PORT }}
username: ${{ secrets.PUBLISH_USER_NAME }}
password: ${{ secrets.PUBLISH_PASSWORD }}
script: |
cd ${{ env.PUBLISH_PATH }}
[ -f .env ] && export $(sed '/^#/d' .env)
docker stack deploy -c docker-swarm.yaml --with-registry-auth mk
- name: 发送构建通知
if: ${{ always() }}
uses: actions/webhook@master
with:
urls: https://oapi.dingtalk.com/robot/send?access_token=6ddafcada8f44f4bad4a7314c4d9bd19a895ded0a1ba1afdaff5dd01a5af6781
content_type: application/json
template: |
{
"msgtype": "markdown",
"markdown": {
"title":"项目部署通知",
"text": "${{ job.status == 'success' && '✅' || '❌' }}**${{ gitea.repository }}**\n
>**构建结果**: ${{ job.status }}
>**构建详情**: [点击查看](${{ gitea.server_url }}/${{ gitea.repository }}/actions/runs/${{ gitea.run_number }})
>**代码分支**: ${{ gitea.ref_name }}
>**提交标识**: ${{ gitea.sha }}
>**提交发起**: ${{ gitea.actor }}
>**提交信息**: ${{ gitea.event.head_commit.message }}\n
"
}
}

View File

@ -0,0 +1,123 @@
name: 部署生产环境
on:
push:
branches:
- master
env:
IMAGE_NAME: ${{ gitea.repository_owner }}/usercenter.web
PUBLISH_PATH: usercenter.web
PUBLISH_HOST: 10.255.255.74
PUBLISH_PORT: 10022
PUBLISH_REPLICAS: 1 #启动副本数量
jobs:
release:
name: 发布版本
runs-on: linux
outputs:
version: ${{ steps.publish_version.outputs.version }}
steps:
- name: 下载源码
uses: actions/checkout@v3
- name: 发布版本
id: publish_version
uses: actions/auto-release@master
job1:
name: 编译发布
runs-on: linux
needs: release
steps:
- name: 下载源码
uses: actions/checkout@v3
- name: 下载源码
uses: actions/checkout@v3
- name: 使用缓存
uses: actions/cache@master
with:
mount: |
node_modules
- name: 编译项目
shell: bash
run: |
echo =========构建项目开始========
rm -rf package-lock.json #删除文件
npm config set registry https://registry.npmmirror.com/
npm install
npm run build
echo =========构建项目结束========
- name: 打包上传镜像
uses: actions/docker@master
with:
registry: ${{ secrets.DOCKER_REGISTRY }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
image: ${{ env.IMAGE_NAME }}
context: .
tags: latest,${{ gitea.ref_name }},${{ needs.release.outputs.version }}
- name: 生成环境变量文件
env:
env_file: |
REGISTRY=${{ secrets.DOCKER_REGISTRY }}
OWNER=${{ gitea.repository_owner }}
TAG=${{ needs.release.outputs.version }}
REPLICAS=${{ env.PUBLISH_REPLICAS }}
run: |
echo "$env_file">.env
- name: 复制发布脚本
uses: docker://appleboy/drone-scp
with:
host: ${{ env.PUBLISH_HOST }}
port: ${{ env.PUBLISH_PORT }}
username: ${{ secrets.PUBLISH_USER_NAME }}
password: ${{ secrets.PUBLISH_PASSWORD }}
target: ${{ env.PUBLISH_PATH }}
source: docker-swarm.yaml,.env
- name: 部署到服务器
uses: docker://appleboy/drone-ssh
with:
host: ${{ env.PUBLISH_HOST }}
port: ${{ env.PUBLISH_PORT }}
username: ${{ secrets.PUBLISH_USER_NAME }}
password: ${{ secrets.PUBLISH_PASSWORD }}
script: |
cd ${{ env.PUBLISH_PATH }}
[ -f .env ] && export $(sed '/^#/d' .env)
docker stack deploy -c docker-swarm.yaml --with-registry-auth mk
- name: 发送构建通知
if: ${{ always() }}
uses: actions/webhook@master
with:
urls: https://oapi.dingtalk.com/robot/send?access_token=6ddafcada8f44f4bad4a7314c4d9bd19a895ded0a1ba1afdaff5dd01a5af6781
content_type: application/json
template: |
{
"msgtype": "markdown",
"markdown": {
"title":"项目部署通知",
"text": "${{ job.status == 'success' && '✅' || '❌' }}**${{ gitea.repository }}**\n
>**构建结果**: ${{ job.status }}
>**构建详情**: [点击查看](${{ gitea.server_url }}/${{ gitea.repository }}/actions/runs/${{ gitea.run_number }})
>**代码分支**: ${{ gitea.ref_name }}
>**发布版本**: ${{ needs.release.outputs.version }}
>**提交标识**: ${{ gitea.sha }}
>**提交发起**: ${{ gitea.actor }}
>**提交信息**: ${{ gitea.event.head_commit.message }}\n
"
}
}

View File

@ -0,0 +1,110 @@
name: 部署测试环境
on:
push:
branches:
- staging
env:
IMAGE_NAME: ${{ gitea.repository_owner }}/usercenter.web
PUBLISH_PATH: usercenter.web
PUBLISH_HOST: 10.255.255.3
PUBLISH_PORT: 22
PUBLISH_REPLICAS: 1 #启动副本数量
jobs:
job1:
name: 编译发布
runs-on: linux
container:
image: node:16-bullseye
volumes:
#挂载缓存
- /opt/.runner/node_modules:/cache
steps:
- name: 下载源码
uses: actions/checkout@v3
- name: 使用缓存
uses: actions/cache@master
with:
mount: |
node_modules
- name: 编译项目
shell: bash
run: |
echo =========构建项目开始========
rm -rf package-lock.json #删除文件
npm config set registry https://registry.npmmirror.com/
npm install
npm run build
echo =========构建项目结束========
- name: 打包上传镜像
uses: actions/docker@master
with:
registry: ${{ secrets.DOCKER_REGISTRY }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
image: ${{ env.IMAGE_NAME }}
context: .
tags: latest,${{ gitea.ref_name }}
- name: 生成环境变量文件
env:
env_file: |
REGISTRY=${{ secrets.DOCKER_REGISTRY }}
OWNER=${{ gitea.repository_owner }}
TAG=${{ gitea.ref_name }}
REPLICAS=${{ env.PUBLISH_REPLICAS }}
run: |
echo "$env_file">.env
- name: 复制发布脚本
uses: docker://appleboy/drone-scp
with:
host: ${{ env.PUBLISH_HOST }}
port: ${{ env.PUBLISH_PORT }}
username: ${{ secrets.PUBLISH_USER_NAME }}
password: ${{ secrets.PUBLISH_PASSWORD }}
target: ${{ env.PUBLISH_PATH }}
source: docker-swarm.yaml,.env
- name: 部署到服务器
uses: docker://appleboy/drone-ssh
with:
host: ${{ env.PUBLISH_HOST }}
port: ${{ env.PUBLISH_PORT }}
username: ${{ secrets.PUBLISH_USER_NAME }}
password: ${{ secrets.PUBLISH_PASSWORD }}
script: |
cd ${{ env.PUBLISH_PATH }}
[ -f .env ] && export $(sed '/^#/d' .env)
docker stack deploy -c docker-swarm.yaml --with-registry-auth mk
- name: 发送构建通知
if: ${{ always() }}
uses: actions/webhook@master
with:
urls: https://oapi.dingtalk.com/robot/send?access_token=6ddafcada8f44f4bad4a7314c4d9bd19a895ded0a1ba1afdaff5dd01a5af6781
content_type: application/json
template: |
{
"msgtype": "markdown",
"markdown": {
"title":"项目部署通知",
"text": "${{ job.status == 'success' && '✅' || '❌' }}**${{ gitea.repository }}**\n
>**构建结果**: ${{ job.status }}
>**构建详情**: [点击查看](${{ gitea.server_url }}/${{ gitea.repository }}/actions/runs/${{ gitea.run_number }})
>**代码分支**: ${{ gitea.ref_name }}
>**提交标识**: ${{ gitea.sha }}
>**提交发起**: ${{ gitea.actor }}
>**提交信息**: ${{ gitea.event.head_commit.message }}\n
"
}
}

View File

@ -0,0 +1,110 @@
name: 部署生产环境
on:
release:
types:
- published
env:
IMAGE_NAME: ${{ gitea.repository_owner }}/usercenter.web
PUBLISH_PATH: usercenter.web
PUBLISH_HOST: 10.255.255.74
PUBLISH_PORT: 10022
PUBLISH_REPLICAS: 1 #启动副本数量
jobs:
job1:
name: 编译发布
runs-on: linux
container:
image: node:16-bullseye
volumes:
#挂载缓存
- /opt/.runner/node_modules:/cache
steps:
- name: 下载源码
uses: actions/checkout@v3
- name: 使用缓存
uses: actions/cache@master
with:
mount: |
node_modules
- name: 编译项目
shell: bash
run: |
echo =========构建项目开始========
rm -rf package-lock.json #删除文件
npm config set registry https://registry.npmmirror.com/
npm install
npm run build
echo =========构建项目结束========
- name: 打包上传镜像
uses: actions/docker@master
with:
registry: ${{ secrets.DOCKER_REGISTRY }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
image: ${{ env.IMAGE_NAME }}
context: .
tags: latest,${{ gitea.ref_name }}
- name: 生成环境变量文件
env:
env_file: |
REGISTRY=${{ secrets.DOCKER_REGISTRY }}
OWNER=${{ gitea.repository_owner }}
TAG=${{ gitea.ref_name }}
REPLICAS=${{ env.PUBLISH_REPLICAS }}
run: |
echo "$env_file">.env
- name: 复制发布脚本
uses: docker://appleboy/drone-scp
with:
host: ${{ env.PUBLISH_HOST }}
port: ${{ env.PUBLISH_PORT }}
username: ${{ secrets.PUBLISH_USER_NAME }}
password: ${{ secrets.PUBLISH_PASSWORD }}
target: ${{ env.PUBLISH_PATH }}
source: docker-swarm.yaml,.env
- name: 部署到服务器
uses: docker://appleboy/drone-ssh
with:
host: ${{ env.PUBLISH_HOST }}
port: ${{ env.PUBLISH_PORT }}
username: ${{ secrets.PUBLISH_USER_NAME }}
password: ${{ secrets.PUBLISH_PASSWORD }}
script: |
cd ${{ env.PUBLISH_PATH }}
[ -f .env ] && export $(sed '/^#/d' .env)
docker stack deploy -c docker-swarm.yaml --with-registry-auth mk
- name: 发送构建通知
if: ${{ always() }}
uses: actions/webhook@master
with:
urls: https://oapi.dingtalk.com/robot/send?access_token=6ddafcada8f44f4bad4a7314c4d9bd19a895ded0a1ba1afdaff5dd01a5af6781
content_type: application/json
template: |
{
"msgtype": "markdown",
"markdown": {
"title":"项目部署通知",
"text": "${{ job.status == 'success' && '✅' || '❌' }}**${{ gitea.repository }}**\n
>**构建结果**: ${{ job.status }}
>**构建详情**: [点击查看](${{ gitea.server_url }}/${{ gitea.repository }}/actions/runs/${{ gitea.run_number }})
>**代码分支**: ${{ gitea.ref_name }}
>**提交标识**: ${{ gitea.sha }}
>**提交发起**: ${{ gitea.actor }}
>**提交信息**: ${{ gitea.event.release.target_commitish }}\n
"
}
}

24
.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

1
.nvmrc Normal file
View File

@ -0,0 +1 @@
v16.15.1

7
.prettierrc Normal file
View File

@ -0,0 +1,7 @@
{
"tabWidth": 2,
"printWidth": 200,
"semi": false,
"singleQuote": true,
"bracketSpacing": true
}

9
Dockerfile Normal file
View File

@ -0,0 +1,9 @@
FROM nginx:alpine
COPY ./dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

View File

@ -1 +1,18 @@
1
# Vue 3 + TypeScript + Vite
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
## Recommended IDE Setup
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
## Type Support For `.vue` Imports in TS
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
1. Disable the built-in TypeScript Extension
1. Run `Extensions: Show Built-in Extensions` from VSCode's command palette
2. Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.

11
auto-imports.d.ts vendored Normal file
View File

@ -0,0 +1,11 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
export {}
declare global {
const ElLoading: typeof import('element-plus/es')['ElLoading']
const ElMessage: typeof import('element-plus/es')['ElMessage']
const ElMessageBox: typeof import('element-plus/es')['ElMessageBox']
}

45
components.d.ts vendored Normal file
View File

@ -0,0 +1,45 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
export {}
declare module 'vue' {
export interface GlobalComponents {
ElButton: typeof import('element-plus/es')['ElButton']
ElCascader: typeof import('element-plus/es')['ElCascader']
ElCheckTag: typeof import('element-plus/es')['ElCheckTag']
ElCollapse: typeof import('element-plus/es')['ElCollapse']
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDivider: typeof import('element-plus/es')['ElDivider']
ElDrawer: typeof import('element-plus/es')['ElDrawer']
ElDropdown: typeof import('element-plus/es')['ElDropdown']
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
ElEmpty: typeof import('element-plus/es')['ElEmpty']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopconfirm: typeof import('element-plus/es')['ElPopconfirm']
ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElResult: typeof import('element-plus/es')['ElResult']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElStep: typeof import('element-plus/es')['ElStep']
ElSteps: typeof import('element-plus/es')['ElSteps']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTag: typeof import('element-plus/es')['ElTag']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
GradeList: typeof import('./src/components/gradeList/index.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
TeacherList: typeof import('./src/components/teacherList/index.vue')['default']
}
}

13
docker-swarm.yaml Normal file
View File

@ -0,0 +1,13 @@
version: "3"
services:
usercenter-web:
image: ${REGISTRY:-harbor.w.23544.com:8843}/marking/usercenter.web:${TAG:-latest}
hostname: marking
environment:
TZ: Asia/Shanghai
ports:
- 4400:80
deploy:
mode: replicated
replicas: ${REPLICAS:-1}

16
index.html Normal file
View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<!-- <link rel="icon" type="image/svg+xml" href="/vite.svg" /> -->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>答题卡制作</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

19
nginx.conf Normal file
View File

@ -0,0 +1,19 @@
server{
# 监听的端口号
listen 80;
# 服务名称 生产环境要修改成 公网ip 47.105.134.120
server_name localhost;
# 配置根目录的地址是以 nginx 下的 html 文件夹为根目录来查找的
root /usr/share/nginx/html;
## html不缓存
location = /index.html {
add_header Cache-Control "no-cache, no-store";
}
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}
location @router {
rewrite ^.*$ /index.html last;
}
}

22445
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

55
package.json Normal file
View File

@ -0,0 +1,55 @@
{
"name": "vue3-project-template",
"private": true,
"version": "0.0.0",
"scripts": {
"dev": "vite",
"test": "vite --mode test",
"prod": "vite --mode production",
"build": "vue-tsc && vite build",
"build:dev": "vite build --mode development",
"build:test": "vite build --mode test",
"preview": "vite preview",
"lint": "eslint . --ext src/**/*.{js,jsx,vue,ts,tsx} --fix"
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"animate.css": "^4.1.1",
"axios": "^1.4.0",
"element-plus": "^2.3.7",
"fabric": "5.3.0",
"html2canvas": "^1.4.1",
"js-cookie": "^3.0.5",
"js-md5": "^0.7.3",
"pinia": "^2.1.4",
"tesseract.js": "^5.1.0",
"tesseract.js-core": "^5.1.0",
"vue": "^3.2.47",
"vue-router": "^4.2.2"
},
"devDependencies": {
"@iconify-json/ep": "^1.1.11",
"@types/node": "^20.3.1",
"@typescript-eslint/eslint-plugin": "^6.3.0",
"@typescript-eslint/parser": "^6.3.0",
"@vitejs/plugin-vue": "^4.1.0",
"@vue/cli-plugin-eslint": "^5.0.8",
"@vue/eslint-config-prettier": "^8.0.0",
"babel-eslint": "^10.1.0",
"eslint": "^8.46.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-plugin-import": "^2.28.0",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-vue": "^9.16.1",
"postcss-px-to-viewport": "^1.1.1",
"sass": "^1.63.6",
"terser": "^5.18.1",
"typescript": "^5.0.2",
"unplugin-auto-import": "^0.16.4",
"unplugin-icons": "^0.16.3",
"unplugin-vue-components": "^0.25.1",
"vite": "^4.3.9",
"vite-plugin-inspect": "^0.7.29",
"vue-tsc": "^1.4.2"
}
}

1
public/vite.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

11
src/App.vue Normal file
View File

@ -0,0 +1,11 @@
<template>
<el-config-provider :locale="zhCn">
<router-view />
</el-config-provider>
</template>
<script setup lang="ts">
import zhCn from "element-plus/dist/locale/zh-cn.mjs";
</script>
<style lang="scss" scoped></style>

25
src/api/Home/index.ts Normal file
View File

@ -0,0 +1,25 @@
import { request } from '@/utils'
export const GetViewSize = (data: any) =>
request({
url: `/draw/position?X=${data.X}&Y=${data.Y}&Width=${data.Width}&Height=${data.Height}&ImageUrl=${data.ImageUrl}&ImageWidth=${data.ImageWidth}&ImageHeight=${data.ImageHeight}`,
method: 'get'
})
export const GetOcrDetail = (mid: string) =>
request({
url: `/api/ocr/${mid}`,
method: 'get'
})
export const PostSave = (data: any) =>
request({
url: `/api/ocr/save`,
method: 'post',
data,
})
export const GetLock = (mid: string) =>
request({
url: `/api/ocr/lock?mid=${mid}`,
method: 'get',
})

BIN
src/assets/image/1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
src/assets/image/2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
src/assets/image/icon1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 B

BIN
src/assets/image/icon2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 B

BIN
src/assets/image/icon3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 830 B

BIN
src/assets/image/icon4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 901 B

BIN
src/assets/image/xx.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 550 KiB

File diff suppressed because one or more lines are too long

3
src/assets/js/worker.min.js vendored Normal file

File diff suppressed because one or more lines are too long

17
src/config/index.ts Normal file
View File

@ -0,0 +1,17 @@
// 常量配置
enum constant {
CONFIG_TITLE = 'vue3-vite-ts-pinia',
CONFIG_REQUEST_TIMEOUT_TIME = 10000, // 请求超时时间
CONFIG_TOKEN = 'TOKEN', // token
CONFIG_USERINFO = 'USERINFO', // 用户信息
CONFIG_STATUS_CODE_SUCCESS = 100, // 自定义代码 100成功、101失败
CONFIG_STATUS_CODE_ERROR = 101,
CONFIG_USERNAME_KEY = 'USERNAME', // 用户名
CONFIG_PASSWORD_KEY = 'PASSWORD', // 密码
CONFIG_IS_REMEMBER_KEY = 'REMEMBER', // 是否记住密码
CONFIG_CODE_SUCCESS = 200, // 成功码
}
// 常规配置
const config = {}
export { config, constant }

18
src/main.ts Normal file
View File

@ -0,0 +1,18 @@
import { createApp } from 'vue'
import router from './router'
import './styles/index.scss'
import App from './App.vue'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import Store from './store'
// 清除项目中的console
if (import.meta.env.VITE_ENV !== 'development') {
console.log = function () {}
}
const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.use(router).use(Store).mount('#app')

View File

@ -0,0 +1,60 @@
<template>
<div>
<span>坐标</span>
<div>
<el-button type="primary" @click="changeFabricPosition(true, 'top')">上移</el-button>
<el-button type="primary" @click="changeFabricPosition(false, 'top')">下移</el-button>
<el-button type="primary" @click="changeFabricPosition(true, 'left')">左移</el-button>
<el-button type="primary" @click="changeFabricPosition(false, 'left')">右移</el-button>
</div>
</div>
<div>
<span>宽度</span>
<el-input-number v-model="fabricPosition.width" :min="1" @change="changeFabricPosition($event, 'width')"
:max="fabricPosition.imgDomWidth" />
</div>
<div>
<span>高度</span>
<el-input-number v-model="fabricPosition.height" :min="1" @change="changeFabricPosition($event, 'height')"
:max="fabricPosition.imgDomHight" />
</div>
</template>
<script lang="ts" setup>
const props = defineProps(["change"]);
import { reactive } from "vue";
const fabricPosition = reactive<any>({
top: 0,
left: 0,
width: 0,
height: 0,
imgDomHight: 1,
imgDomWidth: 1
})
const setSize = (data: any, imgDomWidth: number, imgDomHight: number): void => {
fabricPosition.top = data.top;
fabricPosition.left = data.left;
fabricPosition.width = data.width;
fabricPosition.height = data.height;
fabricPosition.imgDomWidth = imgDomWidth;
fabricPosition.imgDomHight = imgDomHight;
}
const changeFabricPosition = (value: number | boolean, str: string): void => {
if (typeof value === 'boolean') {
if (str === 'top') {
props.change(value ? fabricPosition.top-- : fabricPosition.top++, str)
} else {
props.change(value ? fabricPosition.left-- : fabricPosition.left++, str)
}
} else {
props.change(value, str)
}
}
defineExpose({
setSize,
});
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,132 @@
<template>
<el-dialog
v-model="dialogVisible"
title=""
width="50%"
:show-close="false"
:center="true"
:align-center="true"
>
<div>
<div style="font-size: 20px; margin-bottom: 20px; font-weight: bold">
锁定模板后无法修改请确认模板信息后操作
</div>
<div style="height: 70vh; overflow-y: auto">
<el-collapse v-model="activeNames">
<el-collapse-item :title="item.title" :name="item.key" v-for="item in list">
<div v-if="item.key !== 'otherPoint'">
<div v-if="item.data.length">
<div v-for="row in item.data">
{{ row.questionBefore }}-{{ row.questionAfter }} 总分{{ row.score }}
</div>
</div>
<div v-else>
<div style="text-align: center">暂无数据</div>
</div>
</div>
<div v-else>
<div>缺考{{ item.qk.length ? "已标记" : "未标记" }}</div>
<div>屏蔽区{{ item.pbq.length ? `${item.pbq.length}` : "未标记" }}</div>
<div>反面定位点{{ item.fmdwd.length ? "已标记" : "未标记" }}</div>
</div>
</el-collapse-item>
</el-collapse>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="props.change(), changeDialogVisible(false)"
>锁定</el-button
>
<el-button @click="changeDialogVisible(false)">取消</el-button>
</div>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
const props = defineProps(["change"]);
import { ref } from "vue";
const dialogVisible = ref<boolean>(false);
const activeNames = ref([
"objectiveQuestionsPoint",
"subjectiveQuestionsPoint",
"otherPoint",
]);
const list = ref<any>([]);
const getData = (data: any, templateInfo: any): void => {
//
let msg = "";
if (!templateInfo.headList[2].point.length && !templateInfo.tailsList[2].point.length) {
msg = "考号未框选!";
}
if (!templateInfo.headList[1].point.length && !templateInfo.tailsList[1].point.length) {
msg = "学科未框选!";
}
if (templateInfo.hasPosition) {
if (templateInfo.imgSrc.length === 2) {
if (
templateInfo.headList[0].point.length !== 4 ||
templateInfo.tailsList[0].point.length !== 4
) {
msg = "定位点未框选完成!";
}
} else {
if (templateInfo.headList[0].point.length !== 4) {
msg = "定位点未框选完成!";
}
}
}
if (msg) {
ElMessage.error(msg);
return;
}
//
changeData(data);
//
changeDialogVisible(true);
};
const changeDialogVisible = (bool: boolean): void => {
dialogVisible.value = bool;
};
const changeData = (data: any): void => {
const params = [
{ title: "", key: "objectiveQuestionsPoint" },
{ title: "", key: "subjectiveQuestionsPoint" },
{ title: "", key: "otherPoint" },
] as any;
const objectiveQuestionsPoint = [], //
subjectiveQuestionsPoint = [], //
otherPoint = []; //
for (let index = 0; index < data.length; index++) {
const item = data[index];
objectiveQuestionsPoint.push(...item.from.objectiveQuestionsPoint);
subjectiveQuestionsPoint.push(...item.from.subjectiveQuestionsPoint);
otherPoint.push(...item.from.otherPoint);
}
let objectiveQuestionsPointNumber = 0;
objectiveQuestionsPoint.forEach((item: any) => {
objectiveQuestionsPointNumber += item.questionNumber;
});
let subjectiveQuestionsPointNumber = 0;
subjectiveQuestionsPoint.forEach((item: any) => {
subjectiveQuestionsPointNumber += item.questionAfter - item.questionBefore + 1;
});
params[0].title = `客观题:${objectiveQuestionsPointNumber}`;
params[0].data = objectiveQuestionsPoint;
params[1].title = `主观题:${subjectiveQuestionsPointNumber}`;
params[1].data = subjectiveQuestionsPoint;
params[2].title = `其他标记(缺考、屏蔽区、反面定位点)`;
params[2].data = otherPoint;
params[2].qk = otherPoint.filter((item) => item.type === 1);
params[2].pbq = otherPoint.filter((item) => item.type === 2);
params[2].fmdwd = otherPoint.filter((item) => item.type === 3);
list.value = params;
};
defineExpose({
getData,
});
</script>
<style lang="scss" scoped></style>

3222
src/pages/Home/index.vue Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
<template>
<el-result icon="warning" title="404提示" sub-title="你找的页面走丢了~">
<template #extra>
<el-button type="primary" @click="toNext">返回首页</el-button>
</template>
</el-result>
</template>
<script lang="ts" setup>
import { useRouter } from "vue-router";
const router = useRouter();
const toNext = (): void => {
router.replace("/Home");
};
</script>
<style lang="scss" scoped></style>

26
src/router/index.ts Normal file
View File

@ -0,0 +1,26 @@
import { RouteRecordRaw, createRouter, createWebHashHistory } from 'vue-router'
const routes: Array<RouteRecordRaw> = [
{
path: "/",
redirect: "/Home"
},
{
path: "/Home",
name: "Home",
children: [],
component: () => import("/src/pages/Home/index.vue")
},
{
path: "/:pathMatch(.*)*",
name: "NotFound",
children: [],
component: () => import("/src/pages/NotFound/index.vue")
}
]
const router = createRouter({
history: createWebHashHistory(),
routes,
})
export default router

9
src/shims-vue.d.ts vendored Normal file
View File

@ -0,0 +1,9 @@
/* eslint-disable */
declare module '*.vue' {
const component: DefineComponent<{}, {}, any>
export default component
}
declare module 'js-md5'
declare module 'fabric'
declare module 'js-cookie'
declare module 'element-plus/dist/locale/zh-cn.mjs'

5
src/store/index.ts Normal file
View File

@ -0,0 +1,5 @@
import { createPinia } from 'pinia'
const Store = createPinia()
export default Store

7
src/store/user/index.ts Normal file
View File

@ -0,0 +1,7 @@
import { defineStore } from 'pinia'
export const userStore = defineStore('USER', {
state: () => ({
isLogin: false,
}),
})

View File

@ -0,0 +1,5 @@
.el-drawer{
.el-drawer__header{
margin-bottom: 0;
}
}

2
src/styles/index.scss Normal file
View File

@ -0,0 +1,2 @@
@import './public.scss';
@import './element-ui.scss';

24
src/styles/public.scss Normal file
View File

@ -0,0 +1,24 @@
html,
body,
h1,
h2,
h3,
h4,
h5,
h6 {
padding: 0;
margin: 0;
}
html,
body,
#app {
width: 100%;
height: 100%;
}
:root {
@for $i from 1 through 40 {
--ft#{$i}: #{$i}px;
}
}

4
src/utils/index.ts Normal file
View File

@ -0,0 +1,4 @@
import storage from './package/storage'
import request from './request'
export { storage, request }

View File

@ -0,0 +1,30 @@
class LocalStorage {
private constructor() {}
private static instance: LocalStorage | null = null
static getInstance() {
if (LocalStorage.instance === null) {
LocalStorage.instance = new LocalStorage()
}
return LocalStorage.instance
}
setItem(key: string, value: any) {
localStorage.setItem(key, value)
}
getItem(key: string) {
return localStorage.getItem(key)
}
removeItem(key: string) {
localStorage.removeItem(key)
}
removeAll() {
localStorage.clear()
}
}
export default LocalStorage.getInstance()

View File

@ -0,0 +1,26 @@
import { AxiosRequestConfig, AxiosResponse } from 'axios'
import Request from './request'
import { constant } from '@/config'
// 实例化
const req = new Request({
baseURL: '',
timeout: constant.CONFIG_REQUEST_TIMEOUT_TIME as number,
interceptors: {
// 请求拦截器
requestInterceptors: (config: AxiosRequestConfig) => config,
// 响应拦截器 <T = AxiosResponse>(result: T)
responseInterceptors: <T = AxiosResponse>(result: T) => result,
},
})
const request = (config: any) => {
const { method = 'GET' } = config
if (method === 'get' || method === 'GET') {
config.params = config.data
}
return req.request<any>(config)
}
export default request

View File

@ -0,0 +1,131 @@
import axios, { AxiosInstance, AxiosResponse } from 'axios'
import { RequestConfig, RequestInterceptors } from './types'
import { userStore } from '@/store/user'
import { storage } from '@/utils'
import Store from '@/store'
import { constant } from '@/config'
import router from '@/router'
let loading = '' as any;
const store = userStore(Store)
class Request {
// axios实例
instance: AxiosInstance
// 拦截器对象
interceptorsObj?: RequestInterceptors
constructor(config: RequestConfig) {
// 创建实例
this.instance = axios.create(config)
// 类请求拦截器
this.instance.interceptors.request.use(
(req: any) => {
const token = localStorage.getItem('token') || `Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI4NGYwNDhjYy1mNGM2LTQzMTgtOWRiNi1mOTRhMmM3YjlkZjMiLCJpYXQiOiIwNS8yMS8yMDI0IDA5OjM4OjAwIiwiaWQiOiIzNCIsInVzZXIiOiIzNCIsIm5hbWUiOiJhZG1pbmZsc3EiLCJyb2xlIjoiNSIsInRlbmFudCI6IjEiLCJwb3NpdGlvbiI6IjQ4ODQ4NDYyNTc5MzA5MyIsInNjb3BlIjoiTGVhcm5BZG1pbiIsIm5iZiI6MTcxNjI1NTQ4MCwiZXhwIjoxNzI0MTc1NDgwLCJpc3MiOiJtay1hZCIsImF1ZCI6Ik1hcmtpbmdBZG1pbkNsaWVudCJ9.MGl5dhcUNpGyjb4R0X_ME-BrXBmljfPiagqhyITVF1g`
if (token) {
// 如果有token给请求头加上
req.headers.Authorization = `${token}`
req.timeout = constant.CONFIG_REQUEST_TIMEOUT_TIME
}
if (req.contentType) {
req.headers["Content-Type"] = req.contentType;
}
return req
},
(_err: any) => {
}
)
// 类响应拦截器
this.instance.interceptors.response.use(
(res: AxiosResponse) => {
const { data: resData, status, config } = res
if (status == constant.CONFIG_CODE_SUCCESS && resData && Object.prototype.toString.call(resData) == '[object Object]') {
resData.success = false
resData.code == constant.CONFIG_CODE_SUCCESS && (resData.success = true)
}
resData.headers = res.headers['content-disposition'];
if (resData.code !== 200) {
if (config.responseType === 'blob') {
const reader = new FileReader() as any;
reader.readAsText(res.data, "utf-8");
reader.onload = function () {
if (reader.result) {
try {
ElMessage.error(JSON.parse(reader.result).message)
} catch (error) {
}
}
};
} else {
ElMessage.error(resData.message)
}
}
return resData
},
(err: any) => {
function toLogin() {
ElMessage.error('登录信息已失效,请重新登录!')
storage.removeItem(constant.CONFIG_TOKEN)
store.$patch((state) => {
state.isLogin = false
})
if (localStorage.getItem('token')) {
location.href = location.origin + "/login.html";
} else {
router.push({ path: '/login' })
}
}
// 根据自己业务/接口返回做相应调整
if (err.response) {
const { status, data } = err.response
ElMessage.error(data.message || data.title)
switch (status) {
case 401:
toLogin()
break
case 403:
toLogin()
break
}
} else {
ElMessage.error(err.message)
}
}
)
}
request<T>(config: RequestConfig): Promise<T> {
return new Promise((resolve, reject) => {
loading = ElLoading.service({
lock: true,
background: 'rgba(255,255, 255, 0.3)',
body: true
})
if (config.interceptors?.requestInterceptors) {
config = config.interceptors.requestInterceptors(config)
}
this.instance
.request<any, T>(config)
.then((res: T) => {
// 如果我们为单个响应设置拦截器,这里使用单个响应的拦截器
if (config.interceptors?.responseInterceptors) {
res = config.interceptors.responseInterceptors<T>(res)
}
resolve(res || { code: 0 } as any)
})
.catch((err: any) => {
reject(err)
}).finally(() => {
if (loading) {
loading.close()
loading = ''
}
})
})
}
}
export default Request

View File

@ -0,0 +1,20 @@
import { AxiosRequestConfig, AxiosResponse } from 'axios'
// 实例拦截器
export interface RequestInterceptors {
// 请求拦截
requestInterceptors?: (config: AxiosRequestConfig) => AxiosRequestConfig
requestInterceptorsCatch?: (err: any) => any
// 响应拦截
responseInterceptors?: <T = AxiosResponse>(config: T) => T
responseInterceptorsCatch?: (err: any) => any
}
// 自定义传入的参数
export interface RequestConfig extends AxiosRequestConfig {
interceptors?: RequestInterceptors
}
export interface useRequestConfig<T> extends RequestConfig {
data?: T
}

1
src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="vite/client" />

43
tsconfig.json Normal file
View File

@ -0,0 +1,43 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": [
"ESNext",
"DOM",
"DOM.Iterable"
],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"baseUrl": ".",
"paths": {
"@/*": [
"src/*"
]
}
},
"include": [
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.tsx",
"src/**/*.vue",
"./auto-imports.d.ts"
],
"references": [
{
"path": "./tsconfig.node.json"
}
]
}

10
tsconfig.node.json Normal file
View File

@ -0,0 +1,10 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}

95
vite.config.ts Normal file
View File

@ -0,0 +1,95 @@
import { resolve } from 'path'
import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import Icons from 'unplugin-icons/vite'
import IconsResolver from 'unplugin-icons/resolver'
import Inspect from 'vite-plugin-inspect'
import pxtovw from 'postcss-px-to-viewport'
const loder_pxtovw = pxtovw({
// 这里是设计稿宽度 自己修改
viewportWidth: 1920,
viewportUnit: 'vw',
})
// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd());
return {
css: {
postcss: {
// plugins: [loder_pxtovw],
},
},
server: {
port: 8080,
host: '0.0.0.0',
proxy: {
"/api": {
target: env.VITE_BASE_URL_API,
changeOrigin: true
},
"/draw": {
target: env.VITE_BASE_URL_DRAW_API,
changeOrigin: true
},
}
},
resolve: {
alias: [
{
find: '@',
replacement: resolve(__dirname, 'src'),
},
],
},
build: {
minify: 'terser',
terserOptions: {
compress: {
drop_console: true,
},
},
rollupOptions: {
output: {
chunkFileNames: 'assets/js/[name]-[hash].js',
entryFileNames: 'assets/js/[name]-[hash].js',
assetFileNames: 'assets/[ext]/[name]-[hash].[ext]',
compact: true,
manualChunks: {
}
},
cache: true,
}
},
base: './', // 这里更改打包相对绝对路径
plugins: [
vue(),
AutoImport({
resolvers: [
ElementPlusResolver(),
IconsResolver({
prefix: 'Icon',
}),
],
}),
Components({
resolvers: [
ElementPlusResolver(),
IconsResolver({
enabledCollections: ['ep'],
}),
],
}),
Icons({
autoInstall: true,
}),
Inspect(),
],
}
}
)