Compare commits
34 Commits
He-Chang-L
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70e5753dcd | ||
|
|
7e6921c8df | ||
|
|
99e1c9b445 | ||
|
|
aff235bbf4 | ||
|
|
0f59cc6c87 | ||
|
|
eb7312fb23 | ||
|
|
20428e2ac8 | ||
|
|
fae1999a93 | ||
|
|
64e8813f35 | ||
|
|
4b99518c16 | ||
|
|
4fdf48893c | ||
|
|
55d51efd20 | ||
|
|
fc76632d4d | ||
|
|
2a38aed1f7 | ||
|
|
11e617adc9 | ||
|
|
32051eb13f | ||
|
|
2e61e255a2 | ||
|
|
499796afbe | ||
|
|
15617f45b9 | ||
|
|
ceac848fd2 | ||
|
|
9b35ac6474 | ||
|
|
4368d0a88d | ||
|
|
d5090f66e2 | ||
|
|
24a01c9bb0 | ||
|
|
38e65e9917 | ||
|
|
d6186e73ad | ||
|
|
690331f88c | ||
|
|
5e679bdde8 | ||
|
|
2732efbefb | ||
| 6efc8b908b | |||
| 935e7f61b3 | |||
| 26a6d313e3 | |||
| dfa6146840 | |||
| c76c4329f9 |
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# 默认忽略的文件
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# 基于编辑器的 HTTP 客户端请求
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
10
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
10
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="AngularCliAddDependency" enabled="true" level="ERROR" enabled_by_default="true" editorAttributes="ERRORS_ATTRIBUTES" />
|
||||||
|
<inspection_tool class="AngularInaccessibleComponentMemberInAotMode" enabled="true" level="ERROR" enabled_by_default="true" editorAttributes="ERRORS_ATTRIBUTES" />
|
||||||
|
<inspection_tool class="AngularInsecureBindingToEvent" enabled="true" level="ERROR" enabled_by_default="true" editorAttributes="ERRORS_ATTRIBUTES" />
|
||||||
|
<inspection_tool class="AngularInvalidI18nAttribute" enabled="true" level="ERROR" enabled_by_default="true" editorAttributes="ERRORS_ATTRIBUTES" />
|
||||||
|
<inspection_tool class="AngularNgOptimizedImage" enabled="true" level="ERROR" enabled_by_default="true" editorAttributes="ERRORS_ATTRIBUTES" />
|
||||||
|
</profile>
|
||||||
|
</component>
|
||||||
6
.idea/misc.xml
generated
Normal file
6
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectRootManager">
|
||||||
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/vue-frame.iml" filepath="$PROJECT_DIR$/.idea/vue-frame.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
9
.idea/vue-frame.iml
generated
Normal file
9
.idea/vue-frame.iml
generated
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
12
.prettierrc
Normal file
12
.prettierrc
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"printWidth": 100,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": false,
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"arrowParens": "avoid",
|
||||||
|
"vueIndentScriptAndStyle": false,
|
||||||
|
"endOfLine": "lf"
|
||||||
|
}
|
||||||
@@ -1 +1 @@
|
|||||||
# Vue 3 + Typescript + Vite + ElementUI-Plus + DayJs + Pinia
|
# Vue 3 + Typescript + Vite + TDdesign + DayJs + Pinia
|
||||||
|
|||||||
3
auto-imports.d.ts
vendored
3
auto-imports.d.ts
vendored
@@ -3,7 +3,8 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
// noinspection JSUnusedGlobalSymbols
|
// noinspection JSUnusedGlobalSymbols
|
||||||
// Generated by unplugin-auto-import
|
// Generated by unplugin-auto-import
|
||||||
|
// biome-ignore lint: disable
|
||||||
export {}
|
export {}
|
||||||
declare global {
|
declare global {
|
||||||
|
const ElMessageBox: typeof import('element-plus/es')['ElMessageBox']
|
||||||
}
|
}
|
||||||
|
|||||||
3
components.d.ts
vendored
3
components.d.ts
vendored
@@ -1,10 +1,11 @@
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
/* prettier-ignore */
|
|
||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
// Generated by unplugin-vue-components
|
// Generated by unplugin-vue-components
|
||||||
// Read more: https://github.com/vuejs/core/pull/3399
|
// Read more: https://github.com/vuejs/core/pull/3399
|
||||||
|
// biome-ignore lint: disable
|
||||||
export {}
|
export {}
|
||||||
|
|
||||||
|
/* prettier-ignore */
|
||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
Foot: typeof import('./src/components/Foot.vue')['default']
|
Foot: typeof import('./src/components/Foot.vue')['default']
|
||||||
|
|||||||
1
dev-dist/registerSW.js
Normal file
1
dev-dist/registerSW.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
if('serviceWorker' in navigator) navigator.serviceWorker.register('/dev-sw.js?dev-sw', { scope: '/', type: 'classic' })
|
||||||
92
dev-dist/sw.js
Normal file
92
dev-dist/sw.js
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2018 Google Inc. All Rights Reserved.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// If the loader is already loaded, just stop.
|
||||||
|
if (!self.define) {
|
||||||
|
let registry = {};
|
||||||
|
|
||||||
|
// Used for `eval` and `importScripts` where we can't get script URL by other means.
|
||||||
|
// In both cases, it's safe to use a global var because those functions are synchronous.
|
||||||
|
let nextDefineUri;
|
||||||
|
|
||||||
|
const singleRequire = (uri, parentUri) => {
|
||||||
|
uri = new URL(uri + ".js", parentUri).href;
|
||||||
|
return registry[uri] || (
|
||||||
|
|
||||||
|
new Promise(resolve => {
|
||||||
|
if ("document" in self) {
|
||||||
|
const script = document.createElement("script");
|
||||||
|
script.src = uri;
|
||||||
|
script.onload = resolve;
|
||||||
|
document.head.appendChild(script);
|
||||||
|
} else {
|
||||||
|
nextDefineUri = uri;
|
||||||
|
importScripts(uri);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
.then(() => {
|
||||||
|
let promise = registry[uri];
|
||||||
|
if (!promise) {
|
||||||
|
throw new Error(`Module ${uri} didn’t register its module`);
|
||||||
|
}
|
||||||
|
return promise;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
self.define = (depsNames, factory) => {
|
||||||
|
const uri = nextDefineUri || ("document" in self ? document.currentScript.src : "") || location.href;
|
||||||
|
if (registry[uri]) {
|
||||||
|
// Module is already loading or loaded.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let exports = {};
|
||||||
|
const require = depUri => singleRequire(depUri, uri);
|
||||||
|
const specialDeps = {
|
||||||
|
module: { uri },
|
||||||
|
exports,
|
||||||
|
require
|
||||||
|
};
|
||||||
|
registry[uri] = Promise.all(depsNames.map(
|
||||||
|
depName => specialDeps[depName] || require(depName)
|
||||||
|
)).then(deps => {
|
||||||
|
factory(...deps);
|
||||||
|
return exports;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
define(['./workbox-86c9b217'], (function (workbox) { 'use strict';
|
||||||
|
|
||||||
|
self.skipWaiting();
|
||||||
|
workbox.clientsClaim();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The precacheAndRoute() method efficiently caches and responds to
|
||||||
|
* requests for URLs in the manifest.
|
||||||
|
* See https://goo.gl/S9QRab
|
||||||
|
*/
|
||||||
|
workbox.precacheAndRoute([{
|
||||||
|
"url": "registerSW.js",
|
||||||
|
"revision": "3ca0b8505b4bec776b69afdba2768812"
|
||||||
|
}, {
|
||||||
|
"url": "index.html",
|
||||||
|
"revision": "0.o5uo4dculfk"
|
||||||
|
}], {});
|
||||||
|
workbox.cleanupOutdatedCaches();
|
||||||
|
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
|
||||||
|
allowlist: [/^\/$/]
|
||||||
|
}));
|
||||||
|
|
||||||
|
}));
|
||||||
3391
dev-dist/workbox-86c9b217.js
Normal file
3391
dev-dist/workbox-86c9b217.js
Normal file
File diff suppressed because it is too large
Load Diff
11
index.html
11
index.html
@@ -1,10 +1,13 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="zh-CN">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/logo.png" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta
|
||||||
<title>Vite App</title>
|
name="viewport"
|
||||||
|
content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0"
|
||||||
|
/>
|
||||||
|
<title>Vue模板</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
3484
package-lock.json
generated
3484
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
35
package.json
35
package.json
@@ -1,29 +1,32 @@
|
|||||||
{
|
{
|
||||||
"name": "vue-frame",
|
"name": "vue-frame",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vue-tsc --noEmit && vite build",
|
"build": "tsx src/utils/update-version.ts && vite build --mode release",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.6.2",
|
"axios": "^1.12.2",
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.18",
|
||||||
"element-plus": "^2.4.3",
|
|
||||||
"normalize.css": "^8.0.1",
|
"normalize.css": "^8.0.1",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^3.0.4",
|
||||||
"qs": "^6.11.2",
|
"pinia-plugin-persistedstate": "^4.7.1",
|
||||||
"vue": "^3.3.10",
|
"qs": "^6.14.0",
|
||||||
"vue-router": "^4.2.5"
|
"tdesign-vue-next": "^1.17.1",
|
||||||
|
"vite-plugin-pwa": "^1.1.0",
|
||||||
|
"vue": "^3.5.26",
|
||||||
|
"vue-router": "^4.6.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^18.17.1",
|
"@types/node": "^24.7.2",
|
||||||
"@vitejs/plugin-vue": "^4.5.1",
|
"@vitejs/plugin-vue": "^6.0.1",
|
||||||
"sass": "^1.69.5",
|
"sass": "^1.93.2",
|
||||||
"typescript": "^4.9.5",
|
"typescript": "^5.1.6",
|
||||||
"unplugin-auto-import": "^0.17.2",
|
"unplugin-auto-import": "^20.2.0",
|
||||||
"unplugin-vue-components": "^0.26.0",
|
"unplugin-vue-components": "^29.1.0",
|
||||||
"vite": "^5.0.6",
|
"vite": "^7.3.0",
|
||||||
"vue-tsc": "^0.39.5"
|
"vue-tsc": "^3.2.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
6048
pnpm-lock.yaml
generated
6048
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
1
public/config.ts
Normal file
1
public/config.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
BIN
public/logo.png
Normal file
BIN
public/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 65 KiB |
5
public/version.json
Normal file
5
public/version.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"version": "2025-12-27 10:25:12",
|
||||||
|
"buildTime": "2025-12-27T02:25:12.719Z",
|
||||||
|
"environment": "production"
|
||||||
|
}
|
||||||
48
src/App.vue
48
src/App.vue
@@ -1,18 +1,46 @@
|
|||||||
<!--
|
<!-- 基于rspack打包github&gitee -->
|
||||||
* @Description: {{ByRuin}}
|
|
||||||
* @Version: 2.0
|
|
||||||
* @Author: Ruin 🍭
|
|
||||||
* @Date: 2022-01-25 16:22:24
|
|
||||||
* @LastEditors: 刘引 liu.yin.work@foxmail.com
|
|
||||||
* @LastEditTime: 2023-08-01 15:25:13
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
|
<footer class="app-version">
|
||||||
|
<span>版本:{{ versionText }}</span>
|
||||||
|
</footer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts"></script>
|
<script setup lang="ts">
|
||||||
|
import { onMounted, ref } from 'vue'
|
||||||
|
|
||||||
|
const versionText = ref('未知')
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
const res = await fetch('/version.json', { cache: 'no-store' })
|
||||||
|
if (res.ok) {
|
||||||
|
const data = await res.json()
|
||||||
|
const v = typeof data?.version === 'string' && data.version.trim() ? data.version.trim() : ''
|
||||||
|
versionText.value = v || '未知'
|
||||||
|
} else {
|
||||||
|
versionText.value = '未知'
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
versionText.value = '未知'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@import './assets/scss/index.scss';
|
@import './assets/scss/index.scss';
|
||||||
|
.app-version {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 16px 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #333;
|
||||||
|
background: rgba(255, 255, 255, 0.85);
|
||||||
|
backdrop-filter: saturate(180%) blur(6px);
|
||||||
|
border-top: 1px solid rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,81 +0,0 @@
|
|||||||
/*
|
|
||||||
* @Description: {{ByRuin}}
|
|
||||||
* @Version: 2.0
|
|
||||||
* @Author: Ruin 🍭
|
|
||||||
* @Date: 2022-01-25 17:48:13
|
|
||||||
* @LastEditors: 刘引
|
|
||||||
* @LastEditTime: 2022-01-25 17:52:39
|
|
||||||
*/
|
|
||||||
/* 1.引入文件 */
|
|
||||||
import axios from 'axios' //引入 axios库
|
|
||||||
// @ts-ignore
|
|
||||||
import qs from 'qs' //引入 node中自带的qs模块(数据格式转换)
|
|
||||||
/* 2.全局默认配置 */
|
|
||||||
let baseURL
|
|
||||||
let process: any
|
|
||||||
// 判断开发环境(一般用于本地代理)
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
|
||||||
// 开发环境
|
|
||||||
baseURL = 'http://www.liulongbin.top:3006' // 你设置的本地代理请求(跨域代理),下文会详细介绍怎么进行跨域代理
|
|
||||||
} else {
|
|
||||||
// 编译环境
|
|
||||||
if (process.env.type === 'test') {
|
|
||||||
// 测试环境
|
|
||||||
baseURL = 'http://www.liulongbin.top:3006'
|
|
||||||
} else {
|
|
||||||
// 正式环境
|
|
||||||
baseURL = 'http://www.liulongbin.top:3006'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 配置axios的属性
|
|
||||||
axios.defaults.timeout = 6000 // 请求超时时间1分钟
|
|
||||||
axios.defaults.baseURL = baseURL // 你的接口地址
|
|
||||||
axios.defaults.responseType = 'json'
|
|
||||||
axios.defaults.withCredentials = false //是否允许带cookie这些
|
|
||||||
/*你也可以创建一个实例,然后在实例中配置相关属性,此方法和上面的方法一样,写法不同,怎么用随个人
|
|
||||||
*喜好,我比较喜欢用这种方法,如下:
|
|
||||||
*/
|
|
||||||
const Axios = axios.create({
|
|
||||||
baseURL: baseURL, // 后台服务地址
|
|
||||||
timeout: 60000, // 请求超时时间1分钟
|
|
||||||
responseType: 'json',
|
|
||||||
withCredentials: false // 是否允许带cookie这些
|
|
||||||
})
|
|
||||||
/* 3.设置拦截器 */
|
|
||||||
/*如果不是用创建实例的方式配置,那么下面的Axios都要换成axios,也就是文件开头你用import引入axios
|
|
||||||
时定义的变量*/
|
|
||||||
Axios.interceptors.request.use(
|
|
||||||
(config) => {
|
|
||||||
//发送请求前进行拦截
|
|
||||||
// 可在此处配置请求头信息
|
|
||||||
// config.headers["appkey"] = "...";
|
|
||||||
// config.headers["token"] = "...";
|
|
||||||
if (config.method == 'post') {
|
|
||||||
/*数据转换: axios post方式默认是json格式提交数据,如果使用application/x-www-form-urlencoded数据格式提交,要用qs.stringify()进行转换,个人建议不在拦截器中全局配置,因为不够灵活,还有一点是,如果
|
|
||||||
设置了重新请求的配置,那么重新请求时,请求体中的config里面的传参就会被再次进行qs.stringify()转
|
|
||||||
换,会使得参数丢失,造成请求失败。*/
|
|
||||||
config.data = qs.stringify(config.data)
|
|
||||||
}
|
|
||||||
return config
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
//console.log("错误的传参", 'fail');
|
|
||||||
return Promise.reject(error)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
Axios.interceptors.response.use(
|
|
||||||
(res) => {
|
|
||||||
//请求响应后拦截
|
|
||||||
if (res.status == 200) {
|
|
||||||
// 对响应数据做些事
|
|
||||||
//alert("提交成功")
|
|
||||||
return Promise.resolve(res)
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
//alert("网络异常!") 404等问题可以在这里处理
|
|
||||||
return Promise.reject(error)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
export default Axios
|
|
||||||
@@ -3,6 +3,37 @@
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
box-sizing: border-box !important;
|
box-sizing: border-box !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 禁用移动端弹性滚动 */
|
||||||
|
html, body {
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
-webkit-overflow-scrolling: auto;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 禁用iOS橡皮筋效果 */
|
||||||
|
body {
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overscroll-behavior: none;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 防止下拉刷新和弹性滚动 */
|
||||||
|
html {
|
||||||
|
overscroll-behavior: none;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
li {
|
li {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
@@ -11,3 +42,14 @@ li {
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
$fzt: 20px;
|
$fzt: 20px;
|
||||||
|
.t-notification__detail {
|
||||||
|
// padding: 0 20px;
|
||||||
|
|
||||||
|
.t-notification__detail-item {
|
||||||
|
padding: 6px 12px;
|
||||||
|
|
||||||
|
&:nth-child(1) {
|
||||||
|
color: #0052d9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
21
src/main.ts
21
src/main.ts
@@ -10,17 +10,14 @@ import { createApp } from 'vue'
|
|||||||
import router from './router/index' //引入vue-router
|
import router from './router/index' //引入vue-router
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
import Foot from '@/components/Foot.vue'
|
import { startVersionCheck } from './utils/versionCheck'
|
||||||
import Head from '@/components/Head.vue'
|
import TDesign from 'tdesign-vue-next'
|
||||||
import ElementPlus from 'element-plus'
|
import 'tdesign-vue-next/es/style/index.css'
|
||||||
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
|
|
||||||
import 'element-plus/dist/index.css'
|
|
||||||
import 'normalize.css/normalize.css'
|
import 'normalize.css/normalize.css'
|
||||||
|
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
||||||
|
// 启动版本检测服务 (两小时检查一次)
|
||||||
|
startVersionCheck(1000 * 60 * 2)
|
||||||
|
const pinia = createPinia()
|
||||||
|
pinia.use(piniaPluginPersistedstate) // 注入插件
|
||||||
// 挂载到app上
|
// 挂载到app上
|
||||||
createApp(App)
|
createApp(App).use(router).use(pinia).use(TDesign).mount('#app')
|
||||||
.use(router)
|
|
||||||
.use(createPinia())
|
|
||||||
.use(ElementPlus)
|
|
||||||
.component('Foot', Foot)
|
|
||||||
.component('Head', Head)
|
|
||||||
.mount('#app')
|
|
||||||
|
|||||||
@@ -6,24 +6,17 @@
|
|||||||
* @LastEditors: 刘引
|
* @LastEditors: 刘引
|
||||||
* @LastEditTime: 2022-03-11 14:26:24
|
* @LastEditTime: 2022-03-11 14:26:24
|
||||||
*/
|
*/
|
||||||
import path from 'node:path/win32'
|
|
||||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
component: () => import('@/views/index.vue'),
|
component: () => import('@/views/index.vue')
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: '/',
|
|
||||||
component: import('@/views/index.vue'),
|
|
||||||
children: [{ path: '/', component: import('@/views/index.vue') }]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
// redirect: "/index",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/user',
|
path: '/excel-upload',
|
||||||
component: () => import('@/views/user/index.vue')
|
name: 'ExcelUpload',
|
||||||
|
component: () => import('@/views/excel-upload/index.vue')
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -32,4 +25,3 @@ const router = createRouter({
|
|||||||
routes
|
routes
|
||||||
})
|
})
|
||||||
export default router
|
export default router
|
||||||
// 我感觉
|
|
||||||
|
|||||||
71
src/services/excel-upload.ts
Normal file
71
src/services/excel-upload.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import Axios from '../utils/request/base-service' // 导入配置好的axios文件
|
||||||
|
import type { AxiosProgressEvent } from 'axios'
|
||||||
|
|
||||||
|
// 封装axios请求函数,并用export导出
|
||||||
|
export function uploadExcel(
|
||||||
|
file: FormData,
|
||||||
|
onUploadProgress?: (progressEvent: AxiosProgressEvent) => void
|
||||||
|
) {
|
||||||
|
return Axios({
|
||||||
|
url: '/upload',
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
},
|
||||||
|
data: file,
|
||||||
|
responseType: 'blob',
|
||||||
|
onUploadProgress
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function checkHealth() {
|
||||||
|
return Axios({
|
||||||
|
url: '/health',
|
||||||
|
method: 'get',
|
||||||
|
headers: {
|
||||||
|
'Cache-Control': 'no-cache',
|
||||||
|
Pragma: 'no-cache'
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
_t: Date.now()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function checkDataSourceStatus() {
|
||||||
|
return Axios({
|
||||||
|
url: '/data-source-status',
|
||||||
|
method: 'get',
|
||||||
|
headers: {
|
||||||
|
'Cache-Control': 'no-cache',
|
||||||
|
Pragma: 'no-cache'
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
_t: Date.now()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function testConnection() {
|
||||||
|
return Axios({
|
||||||
|
url: '/test',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
_t: Date.now()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getProcessingProgress() {
|
||||||
|
return Axios({
|
||||||
|
url: '/progress',
|
||||||
|
method: 'get',
|
||||||
|
headers: {
|
||||||
|
'Cache-Control': 'no-cache',
|
||||||
|
Pragma: 'no-cache'
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
_t: Date.now()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -7,41 +7,41 @@
|
|||||||
* @LastEditTime: 2022-01-25 18:04:29
|
* @LastEditTime: 2022-01-25 18:04:29
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Axios from "../api/base-service"; // 导入配置好的axios文件
|
import Axios from '../utils/request/base-service' // 导入配置好的axios文件
|
||||||
// 封装axios请求函数,并用export导出
|
// 封装axios请求函数,并用export导出
|
||||||
export function getInfo(datas: unknown) {
|
export function getInfo(datas: unknown) {
|
||||||
return Axios({
|
return Axios({
|
||||||
url: "/api.php?key=free&appid=0&msg=鹅鹅鹅",
|
url: '/api.php?key=free&appid=0&msg=鹅鹅鹅',
|
||||||
method: "GET",
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
"content-type": "application/json",
|
'content-type': 'application/json'
|
||||||
},
|
},
|
||||||
data: datas,
|
data: datas
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
export function getInfoA(datas: unknown) {
|
export function getInfoA(datas: unknown) {
|
||||||
return Axios({
|
return Axios({
|
||||||
url: "/api/getbooks",
|
url: '/api/getbooks',
|
||||||
method: "get",
|
method: 'get',
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/x-www-form-urlencoded", //设置请求头请求格式form
|
'Content-Type': 'application/x-www-form-urlencoded' //设置请求头请求格式form
|
||||||
},
|
},
|
||||||
data: datas,
|
data: datas
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
export function getItem(datas: unknown) {
|
export function getItem(datas: unknown) {
|
||||||
return Axios({
|
return Axios({
|
||||||
url: "/api/getItem",
|
url: '/api/getItem',
|
||||||
method: "post",
|
method: 'post',
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json", //设置请求头请求格式为json
|
'Content-Type': 'application/json' //设置请求头请求格式为json
|
||||||
},
|
},
|
||||||
data: datas,
|
data: datas
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
export function getItemInfo(datas: unknown) {
|
export function getItemInfo(datas: unknown) {
|
||||||
return Axios({
|
return Axios({
|
||||||
url: "/api/getItemInfo" + datas,
|
url: '/api/getItemInfo' + datas,
|
||||||
method: "get",
|
method: 'get'
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,29 @@
|
|||||||
/*
|
import { createPinia } from 'pinia'
|
||||||
* @Author: 刘引 liu.yin.work@foxmail.com
|
const store = createPinia()
|
||||||
* @Date: 2023-08-01 13:46:06
|
export { store }
|
||||||
* @LastEditors: 刘引 liu.yin.work@foxmail.com
|
export * from './modules/user'
|
||||||
* @LastEditTime: 2023-08-01 15:56:19
|
// export let useStore = defineStore('useStore', {
|
||||||
* @FilePath: \kthec-emss-web\src\pinia\index.js
|
// /**
|
||||||
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
// * 存储全局状态
|
||||||
*/
|
// * 1.必须是箭头函数: 为了在服务器端渲染的时候避免交叉请求导致数据状态污染
|
||||||
import { defineStore } from 'pinia' // 定义容器
|
// * 和 TS 类型推导
|
||||||
|
// */
|
||||||
export let useMain = defineStore('useStore', {
|
// state: () => {
|
||||||
/**
|
// return {
|
||||||
* 存储全局状态
|
// count: 0,
|
||||||
* 1.必须是箭头函数: 为了在服务器端渲染的时候避免交叉请求导致数据状态污染
|
// list: [1, 2, 3, 4]
|
||||||
* 和 TS 类型推导
|
// }
|
||||||
*/
|
// },
|
||||||
state: () => {
|
// /**
|
||||||
return {
|
// * 用来封装计算属性 有缓存功能 类似于computed
|
||||||
count: 0,
|
// */
|
||||||
list: [1, 2, 3, 4]
|
// getters: {},
|
||||||
}
|
// /**
|
||||||
},
|
// * 编辑业务逻辑 类似于methods
|
||||||
/**
|
// */
|
||||||
* 用来封装计算属性 有缓存功能 类似于computed
|
// actions: {
|
||||||
*/
|
// changeData(val: number) {
|
||||||
getters: {},
|
// this.count = val + 10
|
||||||
/**
|
// }
|
||||||
* 编辑业务逻辑 类似于methods
|
// }
|
||||||
*/
|
// })
|
||||||
actions: {
|
|
||||||
changeData(val: number) {
|
|
||||||
this.count = val + 10
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|||||||
16
src/store/modules/user.ts
Normal file
16
src/store/modules/user.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { reactive } from 'vue'
|
||||||
|
|
||||||
|
export const useUserStore = defineStore(
|
||||||
|
'useUserStore',
|
||||||
|
() => {
|
||||||
|
const userInfo = reactive({
|
||||||
|
name: 'yin.liu',
|
||||||
|
age: 18
|
||||||
|
})
|
||||||
|
return { userInfo }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
persist: true
|
||||||
|
}
|
||||||
|
)
|
||||||
118
src/utils/request/base-service.ts
Normal file
118
src/utils/request/base-service.ts
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
|
||||||
|
import { DialogPlugin } from 'tdesign-vue-next'
|
||||||
|
// 获取浏览器中的url地址和端口号并拼接
|
||||||
|
/* import { config } from 'public/config.js'
|
||||||
|
const getBaseUrl = () => {
|
||||||
|
let baseUrlData = ''
|
||||||
|
if (process.env.NODE_ENV === 'development') {
|
||||||
|
baseUrlData = config.baseUrl
|
||||||
|
} else if (process.env.NODE_ENV === 'production') {
|
||||||
|
baseUrlData = 'http://10.0.0.88:3000/api'
|
||||||
|
}
|
||||||
|
return baseUrlData
|
||||||
|
} */
|
||||||
|
// 创建一个新的axios实例
|
||||||
|
const instance: AxiosInstance = axios.create({
|
||||||
|
baseURL: '/api',
|
||||||
|
timeout: 3000000,
|
||||||
|
withCredentials: false,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 请求拦截器
|
||||||
|
instance.interceptors.request.use(
|
||||||
|
(config: any) => {
|
||||||
|
// 在发送请求之前做些什么
|
||||||
|
// 例如,添加token到请求头
|
||||||
|
// const token = localStorage.getItem('token');
|
||||||
|
// if (token) {
|
||||||
|
// config.headers.Authorization = `Bearer ${token}`;
|
||||||
|
// }
|
||||||
|
return config
|
||||||
|
},
|
||||||
|
(error: any) => {
|
||||||
|
// 对请求错误做些什么
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 响应拦截器
|
||||||
|
instance.interceptors.response.use(
|
||||||
|
(response: AxiosResponse) => {
|
||||||
|
// 对响应数据做点什么
|
||||||
|
console.log(process.env.NODE_ENV, '变量')
|
||||||
|
|
||||||
|
const data = response.data
|
||||||
|
console.log('接口返回', data)
|
||||||
|
|
||||||
|
// 对于健康检查和数据源状态检查接口,直接返回数据
|
||||||
|
if (
|
||||||
|
response.config.url?.includes('/health') ||
|
||||||
|
response.config.url?.includes('/data-source-status')
|
||||||
|
) {
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断接口返回的 Message 字段是否为 Success(对于其他接口)
|
||||||
|
if (data && data.Message && !data.Message.includes('Success')) {
|
||||||
|
// 如果不是 Success,则将请求视为失败
|
||||||
|
const errorDia = DialogPlugin({
|
||||||
|
header: '错误',
|
||||||
|
body: `接口${data.config.url}报错!【${JSON.stringify(data) || '未知错误'}】`,
|
||||||
|
confirmBtn: null,
|
||||||
|
cancelBtn: null,
|
||||||
|
destroyOnClose: true,
|
||||||
|
theme: 'danger',
|
||||||
|
zIndex: 10000,
|
||||||
|
onClose: () => {
|
||||||
|
errorDia.destroy()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return Promise.reject({
|
||||||
|
response: {
|
||||||
|
status: 200,
|
||||||
|
data: data,
|
||||||
|
message: JSON.stringify(data) || '请求失败'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 如果是 Success,或者没有Message字段,则返回数据
|
||||||
|
return response
|
||||||
|
},
|
||||||
|
(error: any) => {
|
||||||
|
// 对响应错误做点什么
|
||||||
|
if (error.response) {
|
||||||
|
// 请求已发出,但服务器响应的状态码不在 2xx 范围内
|
||||||
|
|
||||||
|
console.error('请求错误:', error.response.status, error.response.data)
|
||||||
|
} else if (error.request) {
|
||||||
|
// 请求已经发出,但没有收到响应
|
||||||
|
|
||||||
|
console.error('网络错误:', error.request)
|
||||||
|
} else {
|
||||||
|
// 在设置请求时发生了一些事情,触发了一个错误
|
||||||
|
console.error('错误:', error.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error('报错', error)
|
||||||
|
const errorDia = DialogPlugin({
|
||||||
|
header: '错误',
|
||||||
|
body: `接口${error.config.url}报错!【${JSON.stringify(error.message) || '未知错误'}】`,
|
||||||
|
confirmBtn: null,
|
||||||
|
cancelBtn: null,
|
||||||
|
destroyOnClose: true,
|
||||||
|
theme: 'danger',
|
||||||
|
zIndex: 10000,
|
||||||
|
onClose: () => {
|
||||||
|
errorDia.destroy()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export default instance
|
||||||
57
src/utils/update-version.ts
Normal file
57
src/utils/update-version.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
* 更新版本信息脚本
|
||||||
|
* 在构建过程中自动更新 version.json 文件
|
||||||
|
*/
|
||||||
|
import fs from 'node:fs'
|
||||||
|
import path, { dirname } from 'node:path'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
|
||||||
|
// 获取当前文件的目录路径
|
||||||
|
const __filename = fileURLToPath(import.meta.url)
|
||||||
|
const __dirname = dirname(__filename)
|
||||||
|
|
||||||
|
// 版本信息
|
||||||
|
const now = new Date()
|
||||||
|
const dateStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(
|
||||||
|
2,
|
||||||
|
'0'
|
||||||
|
)}-${String(now.getDate()).padStart(2, '0')}`
|
||||||
|
const timeStr = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(
|
||||||
|
2,
|
||||||
|
'0'
|
||||||
|
)}:${String(now.getSeconds()).padStart(2, '0')}`
|
||||||
|
|
||||||
|
interface VersionInfo {
|
||||||
|
version: string
|
||||||
|
buildTime: string
|
||||||
|
environment: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不再需要检查构建序号,直接使用时间戳作为版本号
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(path.resolve(__dirname, '../../public/version.json'))) {
|
||||||
|
const currentVersionInfo = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../../public/version.json'), 'utf8'))
|
||||||
|
console.log(`当前版本: ${currentVersionInfo.version}, 更新为时间格式版本`)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('读取当前版本信息失败', error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置版本格式为 YYYY-MM-DD HH:MM:SS
|
||||||
|
const versionFormat = `${dateStr} ${timeStr}`
|
||||||
|
|
||||||
|
const versionInfo: VersionInfo = {
|
||||||
|
version: versionFormat,
|
||||||
|
buildTime: now.toISOString(),
|
||||||
|
environment: process.env.NODE_ENV || 'production'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 版本文件路径
|
||||||
|
// 确保路径正确解析到 pc-front/public/version.json
|
||||||
|
const versionFilePath = path.resolve(__dirname, '../../public/version.json')
|
||||||
|
|
||||||
|
// 写入版本信息
|
||||||
|
fs.writeFileSync(versionFilePath, JSON.stringify(versionInfo, null, 2), 'utf8')
|
||||||
|
|
||||||
|
console.log(`版本信息已更新: ${JSON.stringify(versionInfo)}`)
|
||||||
|
console.log(`当前版本时间戳: ${versionFormat}`)
|
||||||
154
src/utils/versionCheck.ts
Normal file
154
src/utils/versionCheck.ts
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
/**
|
||||||
|
* 版本检测服务
|
||||||
|
* 定期检查应用版本并提示用户更新
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { NotifyPlugin } from 'tdesign-vue-next'
|
||||||
|
import { h } from 'vue'
|
||||||
|
|
||||||
|
interface VersionInfo {
|
||||||
|
version: string
|
||||||
|
buildTime: string
|
||||||
|
environment: string
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentVersion: string | null = null
|
||||||
|
let checkInterval: number | null = null
|
||||||
|
let isUpdateNotificationShown = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前版本信息
|
||||||
|
*/
|
||||||
|
export const fetchVersionInfo = async (): Promise<VersionInfo> => {
|
||||||
|
try {
|
||||||
|
// 确保使用正确的路径访问version.json
|
||||||
|
const response = await fetch(`/version.json?t=${new Date().getTime()}`)
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('无法获取版本信息')
|
||||||
|
}
|
||||||
|
return await response.json()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取版本信息失败:', error)
|
||||||
|
return {
|
||||||
|
version: '0.0.0',
|
||||||
|
buildTime: new Date().toISOString(),
|
||||||
|
environment: 'unknown'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查版本更新
|
||||||
|
*/
|
||||||
|
export const checkVersion = async (): Promise<boolean> => {
|
||||||
|
try {
|
||||||
|
const versionInfo = await fetchVersionInfo()
|
||||||
|
|
||||||
|
// 首次加载,保存当前版本
|
||||||
|
if (!currentVersion) {
|
||||||
|
currentVersion = versionInfo.version
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 版本不一致,需要更新
|
||||||
|
if (currentVersion !== versionInfo.version) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
} catch (error) {
|
||||||
|
console.error('检查版本更新失败:', error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示更新提示
|
||||||
|
*/
|
||||||
|
export const showUpdateNotification = () => {
|
||||||
|
// 如果已经显示了更新提示,则不再重复显示
|
||||||
|
if (isUpdateNotificationShown) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 标记已显示更新提示
|
||||||
|
isUpdateNotificationShown = true
|
||||||
|
|
||||||
|
const notify = NotifyPlugin.warning({
|
||||||
|
title: '有新的版本需要更新',
|
||||||
|
content: '点击后将刷新页面获取最新版本!',
|
||||||
|
footer: h('div', { class: 't-notification__detail' }, [
|
||||||
|
h(
|
||||||
|
't-button',
|
||||||
|
{
|
||||||
|
class: 't-notification__detail-item',
|
||||||
|
theme: 'default',
|
||||||
|
variant: 'text',
|
||||||
|
onClick: () => window.location.reload()
|
||||||
|
},
|
||||||
|
'立即刷新'
|
||||||
|
),
|
||||||
|
h(
|
||||||
|
't-button',
|
||||||
|
{
|
||||||
|
class: 't-notification__detail-item',
|
||||||
|
theme: 'primary',
|
||||||
|
variant: 'text',
|
||||||
|
onClick: () => {
|
||||||
|
isUpdateNotificationShown = false
|
||||||
|
NotifyPlugin.close(notify)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'稍后提醒我'
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
duration: 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动版本检测服务
|
||||||
|
* @param intervalTime 检测间隔时间(毫秒),默认5分钟
|
||||||
|
*/
|
||||||
|
export const startVersionCheck = (intervalTime = 1000) => {
|
||||||
|
// 重置通知标志
|
||||||
|
isUpdateNotificationShown = false
|
||||||
|
|
||||||
|
// 初始化获取当前版本
|
||||||
|
fetchVersionInfo().then(info => {
|
||||||
|
currentVersion = info.version
|
||||||
|
console.log('当前应用版本:', currentVersion)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 清除可能存在的旧定时器
|
||||||
|
if (checkInterval) {
|
||||||
|
window.clearInterval(checkInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置定期检查
|
||||||
|
checkInterval = window.setInterval(async () => {
|
||||||
|
const needUpdate = await checkVersion()
|
||||||
|
if (needUpdate) {
|
||||||
|
showUpdateNotification()
|
||||||
|
}
|
||||||
|
}, intervalTime)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (checkInterval) {
|
||||||
|
window.clearInterval(checkInterval)
|
||||||
|
checkInterval = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止版本检测服务
|
||||||
|
*/
|
||||||
|
export const stopVersionCheck = () => {
|
||||||
|
if (checkInterval) {
|
||||||
|
window.clearInterval(checkInterval)
|
||||||
|
checkInterval = null
|
||||||
|
}
|
||||||
|
// 重置通知标志
|
||||||
|
isUpdateNotificationShown = false
|
||||||
|
}
|
||||||
437
src/views/excel-upload/index.vue
Normal file
437
src/views/excel-upload/index.vue
Normal file
@@ -0,0 +1,437 @@
|
|||||||
|
<template>
|
||||||
|
<div class="excel-upload-container">
|
||||||
|
<t-card title="Excel 地址信息补充" class="upload-card">
|
||||||
|
<!-- 服务状态检查 -->
|
||||||
|
<div class="status-section">
|
||||||
|
<t-space>
|
||||||
|
<t-tag variant="outline" :theme="serviceStatus === 'ok' ? 'success' : 'danger'">
|
||||||
|
服务器状态:
|
||||||
|
{{ serviceStatus === 'ok' ? '正常' : serviceStatus === 'error' ? '异常' : '检查中' }}
|
||||||
|
</t-tag>
|
||||||
|
<t-tag :theme="dataSourceStatus.both_exist ? 'success' : 'danger'" variant="outline">
|
||||||
|
数据源: {{ dataSourceStatus.both_exist ? '完整' : '缺失' }}
|
||||||
|
</t-tag>
|
||||||
|
</t-space>
|
||||||
|
</div>
|
||||||
|
<t-divider />
|
||||||
|
<!-- 文件上传区域 -->
|
||||||
|
<div class="upload-section" @click="openFileSelect($event)">
|
||||||
|
<t-upload
|
||||||
|
v-model="fileList"
|
||||||
|
:before-upload="beforeUpload"
|
||||||
|
accept=".xlsx,.xls"
|
||||||
|
ref="uploadRef"
|
||||||
|
:cancelUploadButton="null"
|
||||||
|
:max="2"
|
||||||
|
upload-all-files-in-one-request
|
||||||
|
:disabled="uploading"
|
||||||
|
:request-method="customUpload"
|
||||||
|
:showUploadProgress="false"
|
||||||
|
:auto-upload="false"
|
||||||
|
theme="file-flow"
|
||||||
|
multiple
|
||||||
|
placeholder="最多支持2个Excel文件"
|
||||||
|
>
|
||||||
|
</t-upload>
|
||||||
|
|
||||||
|
<!-- 上传进度 -->
|
||||||
|
|
||||||
|
<div v-if="uploading" class="upload-progress">
|
||||||
|
<p class="progress-text">{{ uploadStatus }}</p>
|
||||||
|
<t-progress :percentage="uploadProgress" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<t-divider />
|
||||||
|
|
||||||
|
<!-- 说明信息 -->
|
||||||
|
<div class="info-section">
|
||||||
|
<t-alert theme="info" message="使用说明" :close="false">
|
||||||
|
<template #default>
|
||||||
|
<li>1、上传需要处理的Excel文件(包含身份证号列)</li>
|
||||||
|
<li>2、系统将自动根据数据源补充乡镇街道和村社区信息</li>
|
||||||
|
<li>3、处理完成后会自动下载结果文件</li>
|
||||||
|
</template>
|
||||||
|
</t-alert>
|
||||||
|
</div>
|
||||||
|
</t-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, onMounted, onUnmounted } from 'vue'
|
||||||
|
import { MessagePlugin, DialogPlugin } from 'tdesign-vue-next'
|
||||||
|
import {
|
||||||
|
uploadExcel,
|
||||||
|
checkHealth,
|
||||||
|
checkDataSourceStatus,
|
||||||
|
getProcessingProgress
|
||||||
|
} from '@/services/excel-upload'
|
||||||
|
|
||||||
|
// 响应式数据
|
||||||
|
const fileList = ref([])
|
||||||
|
const uploadRef = ref(null)
|
||||||
|
const uploading = ref(false)
|
||||||
|
const uploadProgress = ref(0)
|
||||||
|
const uploadStatus = ref('')
|
||||||
|
const serviceStatus = ref('checking')
|
||||||
|
const dataSourceStatus = reactive({
|
||||||
|
urban_rural_exists: false,
|
||||||
|
worker_exists: false,
|
||||||
|
both_exist: false
|
||||||
|
})
|
||||||
|
const processingProgress = reactive({
|
||||||
|
status: 'idle',
|
||||||
|
current_step: '',
|
||||||
|
progress: 0,
|
||||||
|
total_files: 0,
|
||||||
|
processed_files: 0,
|
||||||
|
current_file: '',
|
||||||
|
error_message: ''
|
||||||
|
})
|
||||||
|
const progressTimer = ref<NodeJS.Timeout | null>(null)
|
||||||
|
|
||||||
|
// 文件上传前检查
|
||||||
|
const beforeUpload = (file: File) => {
|
||||||
|
// 检查文件类型
|
||||||
|
const allowedTypes = [
|
||||||
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // .xlsx
|
||||||
|
'application/vnd.ms-excel' // .xls
|
||||||
|
]
|
||||||
|
|
||||||
|
if (!allowedTypes.includes(file.type)) {
|
||||||
|
MessagePlugin.error('只支持Excel文件格式(.xlsx, .xls)')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查文件大小(50MB)
|
||||||
|
/* const maxSize = 50 * 1024 * 1024
|
||||||
|
if (file.size > maxSize) {
|
||||||
|
MessagePlugin.error('文件大小不能超过50MB')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取处理进度
|
||||||
|
const fetchProcessingProgress = async () => {
|
||||||
|
try {
|
||||||
|
const response = await getProcessingProgress()
|
||||||
|
if (response.data.success && response.data.data) {
|
||||||
|
Object.assign(processingProgress, response.data.data)
|
||||||
|
|
||||||
|
// 更新进度显示
|
||||||
|
if (processingProgress.status !== 'idle') {
|
||||||
|
uploadProgress.value = processingProgress.progress
|
||||||
|
// 直接从接口获取状态文本,增加更友好的状态显示
|
||||||
|
const step = processingProgress.current_step || '处理中...'
|
||||||
|
|
||||||
|
// 根据不同的处理状态提供更详细的提示
|
||||||
|
uploadStatus.value = `🚀 ${step}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果处理完成或出错,停止轮询
|
||||||
|
if (processingProgress.status === 'completed' || processingProgress.status === 'error') {
|
||||||
|
if (progressTimer.value) {
|
||||||
|
clearInterval(progressTimer.value)
|
||||||
|
progressTimer.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (processingProgress.status === 'error') {
|
||||||
|
throw new Error(processingProgress.error_message || '处理失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log('进度显示', uploadProgress.value)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取进度失败:', error)
|
||||||
|
if (progressTimer.value) {
|
||||||
|
clearInterval(progressTimer.value)
|
||||||
|
progressTimer.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始进度轮询
|
||||||
|
const startProgressPolling = () => {
|
||||||
|
// 重置进度状态
|
||||||
|
Object.assign(processingProgress, {
|
||||||
|
status: 'idle',
|
||||||
|
current_step: '',
|
||||||
|
progress: 0,
|
||||||
|
total_files: 0,
|
||||||
|
processed_files: 0,
|
||||||
|
current_file: '',
|
||||||
|
error_message: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 立即获取一次进度
|
||||||
|
fetchProcessingProgress()
|
||||||
|
|
||||||
|
// 开始轮询
|
||||||
|
progressTimer.value = setInterval(() => {
|
||||||
|
fetchProcessingProgress()
|
||||||
|
}, 2000) // 每1秒查询一次
|
||||||
|
}
|
||||||
|
|
||||||
|
// 停止进度轮询
|
||||||
|
const stopProgressPolling = () => {
|
||||||
|
if (progressTimer.value) {
|
||||||
|
clearInterval(progressTimer.value)
|
||||||
|
progressTimer.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自定义上传方法
|
||||||
|
const customUpload = async (options: any) => {
|
||||||
|
const files = options.map((item: any) => item.raw)
|
||||||
|
console.log('文件', files)
|
||||||
|
|
||||||
|
// 开始进度轮询
|
||||||
|
startProgressPolling()
|
||||||
|
|
||||||
|
try {
|
||||||
|
uploading.value = true
|
||||||
|
// 创建FormData并添加文件
|
||||||
|
const formData = new FormData()
|
||||||
|
// 逐个添加文件到 FormData,添加索引避免覆盖
|
||||||
|
files.forEach((file: File, index: number) => {
|
||||||
|
formData.append(`file_${index}`, file)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 调用上传API,传递formData
|
||||||
|
const response = await uploadExcel(formData)
|
||||||
|
|
||||||
|
// 停止轮询,因为后端处理已完成
|
||||||
|
stopProgressPolling()
|
||||||
|
|
||||||
|
uploadStatus.value = '处理完成!'
|
||||||
|
uploadProgress.value = 100
|
||||||
|
// 处理响应,下载文件
|
||||||
|
if (response.data instanceof Blob) {
|
||||||
|
// 生成带时间戳的文件名
|
||||||
|
const now = new Date()
|
||||||
|
const year = now.getFullYear()
|
||||||
|
const month = String(now.getMonth() + 1).padStart(2, '0')
|
||||||
|
const day = String(now.getDate()).padStart(2, '0')
|
||||||
|
const hours = String(now.getHours()).padStart(2, '0')
|
||||||
|
const minutes = String(now.getMinutes()).padStart(2, '0')
|
||||||
|
const seconds = String(now.getSeconds()).padStart(2, '0')
|
||||||
|
const fileName = `已添加地址信息_${year}-${month}-${day} ${hours}:${minutes}:${seconds}.zip`
|
||||||
|
|
||||||
|
// 创建下载链接
|
||||||
|
const url = window.URL.createObjectURL(new Blob([response.data]))
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = url
|
||||||
|
link.setAttribute('download', fileName)
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
|
||||||
|
// 清理
|
||||||
|
link.remove()
|
||||||
|
window.URL.revokeObjectURL(url)
|
||||||
|
|
||||||
|
MessagePlugin.success('文件处理完成,已自动下载结果')
|
||||||
|
|
||||||
|
// 显示成功对话框
|
||||||
|
const tipDialog = DialogPlugin({
|
||||||
|
header: '处理完成',
|
||||||
|
theme: 'success',
|
||||||
|
body: 'Excel文件处理完成,已自动下载处理结果文件。',
|
||||||
|
confirmBtn: '确定',
|
||||||
|
cancelBtn: null,
|
||||||
|
onConfirm: () => {
|
||||||
|
fileList.value = []
|
||||||
|
uploadProgress.value = 0
|
||||||
|
uploading.value = false
|
||||||
|
tipDialog.destroy()
|
||||||
|
},
|
||||||
|
onClose: () => {
|
||||||
|
fileList.value = []
|
||||||
|
uploadProgress.value = 0
|
||||||
|
uploading.value = false
|
||||||
|
tipDialog.destroy()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('处理失败:', error)
|
||||||
|
|
||||||
|
// 如果响应是blob但包含错误信息,尝试读取
|
||||||
|
if (error.response && error.response.data instanceof Blob) {
|
||||||
|
try {
|
||||||
|
const errorText = await error.response.data.text()
|
||||||
|
const errorData = JSON.parse(errorText)
|
||||||
|
console.error('服务器错误详情:', errorData)
|
||||||
|
MessagePlugin.error(errorData.error || '处理失败,请重试')
|
||||||
|
} catch (parseError) {
|
||||||
|
console.error('解析错误响应失败:', parseError)
|
||||||
|
MessagePlugin.error('处理失败,请重试')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
MessagePlugin.error(error.message || '处理失败,请重试')
|
||||||
|
}
|
||||||
|
|
||||||
|
stopProgressPolling()
|
||||||
|
uploading.value = false
|
||||||
|
uploadProgress.value = 0
|
||||||
|
uploadStatus.value = '处理失败'
|
||||||
|
fileList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查服务状态
|
||||||
|
const checkServiceStatus = async () => {
|
||||||
|
try {
|
||||||
|
const response = await checkHealth()
|
||||||
|
console.log('健康检查响应:', response.data)
|
||||||
|
if (response.data.success && response.data.data.status === 'ok') {
|
||||||
|
serviceStatus.value = 'ok'
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
serviceStatus.value = 'error'
|
||||||
|
console.error('服务状态检查失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查数据源状态
|
||||||
|
const checkDataSourceStatusLocal = async () => {
|
||||||
|
try {
|
||||||
|
const response = await checkDataSourceStatus()
|
||||||
|
console.log('数据源状态响应:', response.data)
|
||||||
|
if (response.data.success && response.data.data) {
|
||||||
|
Object.assign(dataSourceStatus, response.data.data)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('数据源状态检查失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const openFileSelect = (event: Event) => {
|
||||||
|
const target = event.target as HTMLElement
|
||||||
|
if (
|
||||||
|
target &&
|
||||||
|
uploadRef.value &&
|
||||||
|
['t-upload__flow-empty', 't-upload__flow-card-area'].includes(target.className)
|
||||||
|
) {
|
||||||
|
;(uploadRef.value as any).triggerUpload()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 组件挂载时检查状态
|
||||||
|
onMounted(() => {
|
||||||
|
checkServiceStatus()
|
||||||
|
checkDataSourceStatusLocal()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 组件卸载时清理定时器
|
||||||
|
onUnmounted(() => {
|
||||||
|
stopProgressPolling()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.excel-upload-container {
|
||||||
|
padding: 24px;
|
||||||
|
max-width: 800px;
|
||||||
|
|
||||||
|
margin: 0 auto;
|
||||||
|
:deep(.t-upload__flow-table) {
|
||||||
|
th {
|
||||||
|
&:nth-child(2) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
&:nth-child(3) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
&:nth-child(3) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
&:nth-child(2) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.t-card__title) {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
:deep(.t-alert__content) {
|
||||||
|
// margin-left: 0 !important;
|
||||||
|
}
|
||||||
|
.upload-card {
|
||||||
|
margin-bottom: 50px;
|
||||||
|
.status-section {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-section {
|
||||||
|
margin: 24px 0;
|
||||||
|
cursor: pointer;
|
||||||
|
:deep(.t-upload__flow-empty) {
|
||||||
|
color: var(--td-brand-color);
|
||||||
|
}
|
||||||
|
:deep(.t-upload__flow) {
|
||||||
|
width: 100% !important;
|
||||||
|
min-width: 100px !important;
|
||||||
|
max-width: none !important;
|
||||||
|
}
|
||||||
|
:deep(.t-upload__flow-card-area) {
|
||||||
|
border: 1px dashed var(--td-brand-color) !important;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--td-bg-color-container-hover);
|
||||||
|
border-color: var(--td-brand-color-hover) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.drag-content {
|
||||||
|
text-align: center;
|
||||||
|
// padding: 40px 20px;
|
||||||
|
|
||||||
|
.t-icon {
|
||||||
|
color: var(--td-brand-color);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-tips {
|
||||||
|
color: var(--td-text-color-secondary);
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-progress {
|
||||||
|
.progress-text {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
color: var(--td-text-color-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-section {
|
||||||
|
margin-top: 24px;
|
||||||
|
|
||||||
|
ol {
|
||||||
|
margin: 8px 0 0 0;
|
||||||
|
padding-left: 20px;
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.excel-upload-container {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,18 +1,8 @@
|
|||||||
<!--
|
|
||||||
* @Description: {{ByRuin}}
|
|
||||||
* @Version: 2.0
|
|
||||||
* @Author: Ruin 🍭
|
|
||||||
* @Date: 2022-03-10 10:11:16
|
|
||||||
* @LastEditors: 刘引
|
|
||||||
* @LastEditTime: 2022-07-26 14:42:11
|
|
||||||
-->
|
|
||||||
<template>
|
<template>
|
||||||
<div class="root">我是home组件的子组件news</div>
|
<div class="root">我是home组件的子组件news</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup></script>
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.content {
|
.content {
|
||||||
|
|||||||
@@ -1,22 +1,13 @@
|
|||||||
<!--
|
|
||||||
* @Description: {{ByRuin}}
|
|
||||||
* @Version: 2.0
|
|
||||||
* @Author: Ruin 🍭
|
|
||||||
* @Date: 2022-03-10 10:11:06
|
|
||||||
* @LastEditors: 刘引
|
|
||||||
* @LastEditTime: 2022-07-26 14:42:06
|
|
||||||
-->
|
|
||||||
<template>
|
<template>
|
||||||
<div class="root-home">
|
<div class="root-home">
|
||||||
<p>我是home组件</p>
|
<p>我是home组件</p>
|
||||||
<el-button>按钮</el-button>
|
|
||||||
<news></news>
|
<news></news>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, watch, onUpdated, ref, reactive } from 'vue';
|
import { computed, watch, onUpdated, ref, reactive } from 'vue'
|
||||||
import news from './components/news.vue'
|
import news from './components/news.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped></style>
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<Head></Head>
|
<Head></Head>
|
||||||
<div>根组件{{ store.count }}</div>
|
<p>{{ userStore.userInfo.age }}</p>
|
||||||
|
<t-button @click="userStore.userInfo.age++">测试</t-button>
|
||||||
<Foot></Foot>
|
<Foot></Foot>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, onMounted, watch } from 'vue'
|
import { reactive, onMounted, watch } from 'vue'
|
||||||
import { useMain } from '@/store'
|
import { useUserStore } from '@/store'
|
||||||
const store = useMain()
|
const userStore = useUserStore()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="root-home">
|
<div class="root-home">
|
||||||
<news></news>
|
<news></news>
|
||||||
<el-button @click="changeData()">更改值了</el-button>
|
|
||||||
<h1>写一点demo玩一玩</h1>
|
<h1>写一点demo玩一玩</h1>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"lib": ["esnext", "dom"],
|
"lib": ["esnext", "dom"],
|
||||||
"types": ["element-plus/global"]
|
"types": ["vue", "element-plus/global", "node"]
|
||||||
},
|
},
|
||||||
|
|
||||||
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
|
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
|
||||||
|
|||||||
@@ -1,39 +1,81 @@
|
|||||||
/*
|
import { defineConfig } from 'vite'
|
||||||
* @Description: {{ByRuin}}
|
import vue from '@vitejs/plugin-vue'
|
||||||
* @Version: 2.0
|
import { fileURLToPath } from 'url'
|
||||||
* @Author: Ruin 🍭
|
import { dirname, resolve } from 'path'
|
||||||
* @Date: 2022-01-25 16:22:24
|
import AutoImport from 'unplugin-auto-import/vite'
|
||||||
* @LastEditors: 刘引
|
import Components from 'unplugin-vue-components/vite'
|
||||||
* @LastEditTime: 2022-03-10 16:34:39
|
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
|
||||||
*/
|
import { VitePWA } from 'vite-plugin-pwa'
|
||||||
import { defineConfig } from "vite";
|
// ESM 中获取 __dirname 的替代方案
|
||||||
import vue from "@vitejs/plugin-vue";
|
const __filename = fileURLToPath(import.meta.url)
|
||||||
// ui组件自动导入
|
const __dirname = dirname(__filename)
|
||||||
import AutoImport from "unplugin-auto-import/vite";
|
|
||||||
import Components from "unplugin-vue-components/vite";
|
|
||||||
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
|
|
||||||
import { resolve } from "path";
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
vue(),
|
vue(),
|
||||||
|
VitePWA({
|
||||||
|
injectRegister: 'auto',
|
||||||
|
registerType: 'autoUpdate',
|
||||||
|
devOptions: {
|
||||||
|
enabled: true // 是否本地localhost调试pwa
|
||||||
|
},
|
||||||
|
|
||||||
|
workbox: {
|
||||||
|
// 设置需要缓存的文件类型
|
||||||
|
globPatterns: ['**/*.{js,css,html,svg,jpg,ico}']
|
||||||
|
},
|
||||||
|
manifest: {
|
||||||
|
name: 'Excel处理工具',
|
||||||
|
short_name: '处理工具',
|
||||||
|
theme_color: '#fff', // 浏览器状态栏主题
|
||||||
|
start_url: './',
|
||||||
|
display: 'standalone',
|
||||||
|
background_color: '#fff',
|
||||||
|
icons: [
|
||||||
|
{
|
||||||
|
src: 'logo.png',
|
||||||
|
sizes: '144x144',
|
||||||
|
type: 'image/png',
|
||||||
|
purpose: 'any'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: 'logo.png',
|
||||||
|
sizes: '512x512',
|
||||||
|
type: 'image/png',
|
||||||
|
purpose: 'any'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}),
|
||||||
AutoImport({
|
AutoImport({
|
||||||
resolvers: [ElementPlusResolver()],
|
resolvers: [ElementPlusResolver()]
|
||||||
}),
|
}),
|
||||||
Components({
|
Components({
|
||||||
resolvers: [ElementPlusResolver()],
|
resolvers: [ElementPlusResolver()]
|
||||||
}),
|
})
|
||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
"@": resolve(__dirname, "src"), // 路径别名
|
'@': resolve(__dirname, 'src')
|
||||||
},
|
}
|
||||||
extensions: [".js", ".json", ".ts"], // 使用路径别名时想要省略的后缀名,可以自己 增减
|
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
host: "0.0.0.0",
|
host: '0.0.0.0',
|
||||||
port: 8888,
|
port: 6677,
|
||||||
// 是否开启 https
|
open: true,
|
||||||
https: false,
|
proxy: {
|
||||||
},
|
'/api': {
|
||||||
});
|
target: 'http://10.2.0.32:5000',
|
||||||
|
changeOrigin: true,
|
||||||
|
rewrite: p => p.replace(/^\/api/, ''),
|
||||||
|
bypass: (req, res, options) => {
|
||||||
|
const proxyURL = options.target + options.rewrite(req.url)
|
||||||
|
// console.log('proxyURL', proxyURL)
|
||||||
|
req.headers['x-req-proxyURL'] = proxyURL // 设置未生效
|
||||||
|
res.setHeader('x-req-proxyURL', proxyURL) // 设置响应头可以看到
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user