Compare commits

...

29 Commits
main ... carbon

Author SHA1 Message Date
caoqianming 9198a35d69 feat: 增加贷后页面 2025-11-28 13:47:06 +08:00
caoqianming 2884d8d4f4 feat: 界面优化 2025-11-13 09:25:02 +08:00
caoqianming 039f780bc5 feat: 修改主页展示 2025-11-12 16:24:28 +08:00
caoqianming db3dc079aa feat: base 修改基础样式 2025-11-12 16:24:13 +08:00
caoqianming 5b6c29b606 feat: base 调整t-card__header最小高度 2025-11-07 09:07:26 +08:00
caoqianming 9d8f274c75 feat: base 取消axios timeout 2025-11-07 08:24:59 +08:00
caoqianming 06d8786a41 feat: 页面优化 2025-11-06 16:10:16 +08:00
caoqianming 0658c9e5fb feat: base 跳转前check 2025-11-06 16:09:20 +08:00
caoqianming ac5ca4ba81 feat: base 优化xtChart 2025-11-06 16:08:56 +08:00
caoqianming 75c9087f25 feat: 界面调整 2025-11-05 09:54:02 +08:00
caoqianming 28a4318348 feat: 样式调整 2025-11-05 09:28:03 +08:00
caoqianming e1681c0135 feat: dq 页面调整 2025-11-04 15:13:18 +08:00
caoqianming 10e833c6c9 feat: 优化导航页 2025-11-03 13:48:10 +08:00
caoqianming 27d6fb9e2f feat: base login页添加获取系统信息 2025-11-03 13:47:49 +08:00
caoqianming 5ef2098852 feat: base 添加xtChart组件 2025-11-03 13:47:21 +08:00
caoqianming bd5aec2f43 feat: base 添加echarts依赖 2025-11-03 13:43:59 +08:00
caoqianming 602d016eb3 feat: dq 修改min-height 2025-11-03 11:28:24 +08:00
caoqianming fedad4613a feat: 优化dq页面 2025-10-31 09:59:05 +08:00
caoqianming 64bd545be9 feat: 优化页面 2025-10-31 09:52:46 +08:00
caoqianming 8b791fb222 fix: base 修改router相关内容 2025-10-31 09:52:30 +08:00
caoqianming a3cc4f6d74 feat: carbon 添加分值 2025-10-31 09:36:02 +08:00
caoqianming 731af1b2fc feat: base scrollable-content 样式调整 2025-10-31 09:35:44 +08:00
caoqianming 26e054c579 feat: carbon 基本完成 2025-10-30 16:56:38 +08:00
caoqianming 80b2c0bf7d feat: base request 调整 2025-10-30 16:55:08 +08:00
caoqianming 41fc6c4b14 feat: base 添加request以及一些配置项目 2025-10-30 13:58:04 +08:00
caoqianming 9a0b3f83e3 feat: 修改css引入顺序 2025-10-30 08:56:40 +08:00
caoqianming 11364180bc Merge branch 'main' into carbon 2025-10-30 08:35:38 +08:00
caoqianming 2be9f382ed Merge branch 'main' into carbon 2025-10-30 08:17:27 +08:00
caoqianming 1c90f6d571 feat: 增加部分页面 2025-10-30 08:14:33 +08:00
20 changed files with 2826 additions and 44 deletions

3
.env.development Normal file
View File

@ -0,0 +1,3 @@
VITE_API_BASEURL = http://49.232.14.174:3400/api
# VITE_API_BASEURL = http://127.0.0.1:3401/api

2
.gitignore vendored
View File

@ -11,6 +11,7 @@ node_modules
dist
dist-ssr
*.local
package-lock.json
# Editor directories and files
.vscode/*
@ -22,3 +23,4 @@ dist-ssr
*.njsproj
*.sln
*.sw?

View File

@ -4,10 +4,10 @@
<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>XT_WEB_TD</title>
<title>双碳金融系统</title>
</head>
<body>
<div id="app" style="height: 100vh;"></div>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

283
package-lock.json generated
View File

@ -8,6 +8,7 @@
"name": "xt_web_td",
"version": "0.1.0",
"dependencies": {
"axios": "^1.13.1",
"tdesign-icons-vue-next": "^0.4.1",
"tdesign-vue-next": "^1.17.1",
"vue": "^3.5.22",
@ -1009,6 +1010,48 @@
"integrity": "sha512-F4yc6palwq3TT0u+FYf0Ns4Tfl9GRFURDN2gWG7L1ecIaS/4fCIuFOjMTnCyjsu/OK6vaDKLCrGAa+KvvH+h4w==",
"license": "MIT"
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
"node_modules/axios": {
"version": "1.13.1",
"resolved": "https://registry.npmmirror.com/axios/-/axios-1.13.1.tgz",
"integrity": "sha512-hU4EGxxt+j7TQijx1oYdAjw4xuIp1wRQSsbMFwSthCWeBQur1eF+qJ5iQ5sN3Tw8YRzQNKb8jszgBdMDVqwJcw==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.4",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
@ -1021,6 +1064,29 @@
"integrity": "sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==",
"license": "MIT"
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz",
@ -1033,6 +1099,51 @@
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-set-tostringtag": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.6",
"has-tostringtag": "^1.0.2",
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/esbuild": {
"version": "0.25.11",
"resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.11.tgz",
@ -1099,6 +1210,42 @@
}
}
},
"node_modules/follow-redirects": {
"version": "1.15.11",
"resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz",
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/form-data": {
"version": "4.0.4",
"resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.4.tgz",
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz",
@ -1114,6 +1261,103 @@
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-tostringtag": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz",
@ -1129,6 +1373,36 @@
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mitt": {
"version": "3.0.1",
"resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz",
@ -1165,7 +1439,6 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@ -1201,6 +1474,12 @@
"node": "^10 || ^12 || >=14"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
},
"node_modules/rollup": {
"version": "4.52.5",
"resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.52.5.tgz",
@ -1335,7 +1614,6 @@
"integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.5.0",
@ -1410,7 +1688,6 @@
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.22.tgz",
"integrity": "sha512-toaZjQ3a/G/mYaLSbV+QsQhIdMo9x5rrqIpYRObsJ6T/J+RyCSFwN2LHNVH9v8uIcljDNa3QzPVdv3Y6b9hAJQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@vue/compiler-dom": "3.5.22",
"@vue/compiler-sfc": "3.5.22",

View File

@ -9,6 +9,8 @@
"preview": "vite preview"
},
"dependencies": {
"axios": "^1.13.1",
"echarts": "^6.0.0",
"tdesign-icons-vue-next": "^0.4.1",
"tdesign-vue-next": "^1.17.1",
"vue": "^3.5.22",

View File

@ -0,0 +1 @@
import { http } from "./request.js"

77
src/api/request.js Normal file
View File

@ -0,0 +1,77 @@
import axios from "axios";
import { authToken } from "@/store/index.js"
import { MessagePlugin } from "tdesign-vue-next"
import router from "@/router/index.js"
function get_api_url() {
if (import.meta.env.MODE === 'development' || (import.meta.env.MODE === 'production' && window.location.host.indexOf('localhost') > -1)) {
return import.meta.env.VITE_API_BASEURL
}
return 'http://' + window.location.host + '/api'
}
const instance = axios.create({
baseURL: get_api_url(),
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
})
instance.interceptors.request.use(
config => {
const token = authToken.access
if (token) {
config.headers['Authorization'] = `Bearer ${token}`
}
config._err_i = config._err_i !== false; // 默认 true
return config;
},
error => {
MessagePlugin.error(error.message)
return Promise.reject(error);
}
)
instance.interceptors.response.use(
response => {
return response.data
},
error => {
let err_msg = "请求错误";
console.log(error)
if (error.response) {
const status = error.response.status;
const url = error.config?.url || '';
switch (status) {
case 401:
if (authToken.access) { authToken.access = null }
if (authToken.refresh) { authToken.refresh = null }
if (!url.includes('/login')) {
// 避免重复跳转
if (!window.location.pathname.includes('/login')) {
router.replace('/login');
}
}
break;
default:
err_msg = error.response?.data?.err_msg || "内部错误"
}
} else if (error.request) {
err_msg = '网络连接失败,请检查网络';
}
if (error.config._err_i) {
MessagePlugin.error(err_msg);
}
return Promise.reject(error)
}
)
const http = {
get: (url, params={}, err_i=true) => instance.get(url, { params, err_i }),
post: (url, data={}, err_i=true) => instance.post(url, data, err_i),
put: (url, data={}, err_i=true) => instance.put(url, data, err_i),
delete: (url, err_i=true) => instance.delete(url, err_i),
postForm: (url, data, onProgress=null, err_i=true) => instance.post(url, data, { headers: { 'Content-Type': 'multipart/form-data' }, onUploadProgress: onProgress, err_i})
};
export default http;

View File

@ -0,0 +1,397 @@
{
"essos": {
"color": [
"#893448",
"#d95850",
"#eb8146",
"#ffb248",
"#f2d643",
"#ebdba4"
],
"backgroundColor": "rgba(242,234,191,0.15)",
"textStyle": { },
"title": {
"textStyle": {
"color": "#893448"
},
"subtextStyle": {
"color": "#d95850"
}
},
"line": {
"itemStyle": {
"borderWidth": "2"
},
"lineStyle": {
"width": "2"
},
"symbolSize": "6",
"symbol": "emptyCircle",
"smooth": true
},
"radar": {
"itemStyle": {
"borderWidth": "2"
},
"lineStyle": {
"width": "2"
},
"symbolSize": "6",
"symbol": "emptyCircle",
"smooth": true
},
"bar": {
"itemStyle": {
"barBorderWidth": 0,
"barBorderColor": "#ccc"
}
},
"pie": {
"itemStyle": {
"borderWidth": 0,
"borderColor": "#ccc"
}
},
"scatter": {
"itemStyle": {
"borderWidth": 0,
"borderColor": "#ccc"
}
},
"boxplot": {
"itemStyle": {
"borderWidth": 0,
"borderColor": "#ccc"
}
},
"parallel": {
"itemStyle": {
"borderWidth": 0,
"borderColor": "#ccc"
}
},
"sankey": {
"itemStyle": {
"borderWidth": 0,
"borderColor": "#ccc"
}
},
"funnel": {
"itemStyle": {
"borderWidth": 0,
"borderColor": "#ccc"
}
},
"gauge": {
"itemStyle": {
"borderWidth": 0,
"borderColor": "#ccc"
}
},
"candlestick": {
"itemStyle": {
"color": "#eb8146",
"color0": "transparent",
"borderColor": "#d95850",
"borderColor0": "#58c470",
"borderWidth": "2"
}
},
"graph": {
"itemStyle": {
"borderWidth": 0,
"borderColor": "#ccc"
},
"lineStyle": {
"width": 1,
"color": "#aaaaaa"
},
"symbolSize": "6",
"symbol": "emptyCircle",
"smooth": true,
"color": [
"#893448",
"#d95850",
"#eb8146",
"#ffb248",
"#f2d643",
"#ebdba4"
],
"label": {
"color": "#ffffff"
}
},
"map": {
"itemStyle": {
"areaColor": "#f3f3f3",
"borderColor": "#999999",
"borderWidth": 0.5
},
"label": {
"color": "#893448"
},
"emphasis": {
"itemStyle": {
"areaColor": "#ffb248",
"borderColor": "#eb8146",
"borderWidth": 1
},
"label": {
"color": "#893448"
}
}
},
"geo": {
"itemStyle": {
"areaColor": "#f3f3f3",
"borderColor": "#999999",
"borderWidth": 0.5
},
"label": {
"color": "#893448"
},
"emphasis": {
"itemStyle": {
"areaColor": "#ffb248",
"borderColor": "#eb8146",
"borderWidth": 1
},
"label": {
"color": "#893448"
}
}
},
"categoryAxis": {
"axisLine": {
"show": true,
"lineStyle": {
"color": "#aaaaaa"
}
},
"axisTick": {
"show": false,
"lineStyle": {
"color": "#333"
}
},
"axisLabel": {
"show": true,
"color": "#999999"
},
"splitLine": {
"show": true,
"lineStyle": {
"color": [
"#e6e6e6"
]
}
},
"splitArea": {
"show": false,
"areaStyle": {
"color": [
"rgba(250,250,250,0.05)",
"rgba(200,200,200,0.02)"
]
}
}
},
"valueAxis": {
"axisLine": {
"show": true,
"lineStyle": {
"color": "#aaaaaa"
}
},
"axisTick": {
"show": false,
"lineStyle": {
"color": "#333"
}
},
"axisLabel": {
"show": true,
"color": "#999999"
},
"splitLine": {
"show": true,
"lineStyle": {
"color": [
"#e6e6e6"
]
}
},
"splitArea": {
"show": false,
"areaStyle": {
"color": [
"rgba(250,250,250,0.05)",
"rgba(200,200,200,0.02)"
]
}
}
},
"logAxis": {
"axisLine": {
"show": true,
"lineStyle": {
"color": "#aaaaaa"
}
},
"axisTick": {
"show": false,
"lineStyle": {
"color": "#333"
}
},
"axisLabel": {
"show": true,
"color": "#999999"
},
"splitLine": {
"show": true,
"lineStyle": {
"color": [
"#e6e6e6"
]
}
},
"splitArea": {
"show": false,
"areaStyle": {
"color": [
"rgba(250,250,250,0.05)",
"rgba(200,200,200,0.02)"
]
}
}
},
"timeAxis": {
"axisLine": {
"show": true,
"lineStyle": {
"color": "#aaaaaa"
}
},
"axisTick": {
"show": false,
"lineStyle": {
"color": "#333"
}
},
"axisLabel": {
"show": true,
"color": "#999999"
},
"splitLine": {
"show": true,
"lineStyle": {
"color": [
"#e6e6e6"
]
}
},
"splitArea": {
"show": false,
"areaStyle": {
"color": [
"rgba(250,250,250,0.05)",
"rgba(200,200,200,0.02)"
]
}
}
},
"toolbox": {
"iconStyle": {
"borderColor": "#999999"
},
"emphasis": {
"iconStyle": {
"borderColor": "#666666"
}
}
},
"legend": {
"textStyle": {
"color": "#999999"
}
},
"tooltip": {
"axisPointer": {
"lineStyle": {
"color": "#cccccc",
"width": 1
},
"crossStyle": {
"color": "#cccccc",
"width": 1
}
}
},
"timeline": {
"lineStyle": {
"color": "#893448",
"width": 1
},
"itemStyle": {
"color": "#893448",
"borderWidth": 1
},
"controlStyle": {
"color": "#893448",
"borderColor": "#893448",
"borderWidth": 0.5
},
"checkpointStyle": {
"color": "#eb8146",
"borderColor": "#ffb248"
},
"label": {
"color": "#893448"
},
"emphasis": {
"itemStyle": {
"color": "#ffb248"
},
"controlStyle": {
"color": "#893448",
"borderColor": "#893448",
"borderWidth": 0.5
},
"label": {
"color": "#893448"
}
}
},
"visualMap": {
"color": [
"#893448",
"#d95850",
"#eb8146",
"#ffb248",
"#f2d643",
"rgb(247,238,173)"
]
},
"dataZoom": {
"backgroundColor": "rgba(255,255,255,0)",
"dataBackgroundColor": "rgba(255,178,72,0.5)",
"fillerColor": "rgba(255,178,72,0.15)",
"handleColor": "#ffb248",
"handleSize": "100%",
"textStyle": {
"color": "#333"
}
},
"markPoint": {
"label": {
"color": "#ffffff"
},
"emphasis": {
"label": {
"color": "#ffffff"
}
}
}
}
}

View File

@ -1,24 +1,23 @@
<template>
<t-layout style="height:100%">
<t-header>
<t-head-menu theme="light">
<t-layout class="layout-container">
<t-header class="header-fixed">
<t-head-menu>
<template #logo>
<img height="28" src="https://tdesign.gtimg.com/site/baseLogo-light.png" alt="logo" />
<!-- <span style="font-size: 18px; font-weight: 600;">XT_WEB_TD</span> -->
<!-- <img height="28" src="https://tdesign.gtimg.com/site/baseLogo-light.png" alt="logo" /> -->
<span style="font-size: 26px; font-weight: 600;color:var(--td-brand-color)">{{systemInfo.base_name}}</span>
</template>
<template #operations>
<t-button variant="outline" :icon="user">{{ userInfo.name }}
<t-button variant="outline">{{ userInfo.name }}
</t-button>
<t-button variant="text" shape="square" @click="handleQuit" style="margin-left: 2px;">
<template #icon><t-icon name="logout" style="color: #ff0000;"/></template>
<template #icon><t-icon name="logout" style="color: #ff0000;" /></template>
</t-button>
</template>
</t-head-menu>
</t-header>
<t-layout>
<t-aside style="border-top: 1px solid #e5e5e5;" width="160px">
<t-menu theme="light" default-value="item1" @change="handleMenuChange"
v-model="activeMenu" width="160px">
<t-layout class="main-layout">
<t-aside class="scrollable-aside" width="160px">
<t-menu @change="handleMenuChange" v-model="activeMenu" width="160px">
<t-menu-item v-for="route in menuRoutes" :key="route.name" :value="route.name"
:disabled="route.meta?.disabled">
{{ route.name }}
@ -33,7 +32,7 @@
</template> -->
</t-menu>
</t-aside>
<t-content style="border-top: 1px solid #e5e5e5; padding: 2px">
<t-content class="scrollable-content">
<router-view />
</t-content>
</t-layout>
@ -41,12 +40,11 @@
</template>
<script setup>
import { ref, computed, watch } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import { authToken, userInfo } from '@/store/index.js'
const router = useRouter();
const route = useRoute();
import router from '@/router'
import { useRoute } from 'vue-router'
import { authToken, userInfo, systemInfo } from '@/store/index.js'
const route = useRoute()
const menuRoutes = computed(() => {
const layoutRoute = router.getRoutes().find(r => r.name === 'Layout')
return layoutRoute?.children[0]?.children || []
@ -72,3 +70,40 @@ const handleQuit = () => {
router.replace('/login')
}
</script>
<style scoped>
.layout-container {
height: 100vh;
display: flex;
flex-direction: column;
position: relative;
}
.header-fixed {
position: sticky;
top: 0;
z-index: 100;
flex-shrink: 0;
}
.main-layout {
flex: 1;
display: flex;
overflow: hidden;
/* 关键:防止外部滚动 */
}
.scrollable-aside {
border-top: 1px solid #e5e5e5;
overflow-y: auto;
height: 100%;
}
.scrollable-content {
border-top: 1px solid #e5e5e5;
padding: 2px;
overflow-y: auto;
overflow-x: auto;
height: 100%;
flex: 1;
}
</style>

View File

@ -0,0 +1,152 @@
<template>
<div ref="myChart" :style="{ width: width, height: height }"></div>
</template>
<script setup>
import { onMounted, ref, defineProps, defineEmits, watch, onUnmounted } from 'vue'
import * as echarts from "echarts";
import echartTheme from "@/assets/echarts_theme.json";
//
const emit = defineEmits([
'chart-click',
'chart-dblclick',
'chart-mouseover',
'chart-mouseout',
'chart-legendselectchanged',
'chart-datazoom',
'chart-dataviewchanged',
'chart-restore',
'chart-finished'
])
const props = defineProps({
option: { type: Object, default: () => ({}) },
theme: { type: String, default: "essos" },
width: { type: String, default: "100%" },
height: { type: String, default: "300px" },
//
events: {
type: Array,
default: () => ['click']
},
//
autoResize: { type: Boolean, default: true }
})
const myChart = ref(null)
let myChartInstance = null;
// - ECharts emit
const eventMap = {
'click': 'chart-click',
'dblclick': 'chart-dblclick',
'mouseover': 'chart-mouseover',
'mouseout': 'chart-mouseout',
'legendselectchanged': 'chart-legendselectchanged',
'datazoom': 'chart-datazoom',
'dataviewchanged': 'chart-dataviewchanged',
'restore': 'chart-restore'
}
//
const initChart = () => {
if (props.theme in echartTheme) {
echarts.registerTheme(props.theme, echartTheme[props.theme]);
} else {
console.warn("echarts未找到该主题:", props.theme)
}
myChartInstance = echarts.init(myChart.value, props.theme);
myChartInstance.setOption(props.option);
//
registerEvents();
//
if (props.autoResize) {
window.addEventListener("resize", handleResize);
}
}
//
const registerEvents = () => {
props.events.forEach(eventName => {
if (eventMap[eventName]) {
myChartInstance.on(eventName, (params) => {
emit(eventMap[eventName], params);
});
} else {
console.warn(`不支持的事件类型: ${eventName}`);
}
});
}
//
const handleResize = () => {
myChartInstance?.resize();
}
//
const cleanup = () => {
if (myChartInstance) {
//
props.events.forEach(eventName => {
myChartInstance.off(eventName);
});
if (props.autoResize) {
window.removeEventListener("resize", handleResize);
}
myChartInstance.dispose();
myChartInstance = null;
}
}
onMounted(initChart)
// option
watch(() => props.option, (newOption) => {
if (myChartInstance) {
myChartInstance.setOption(newOption);
}
}, { deep: true })
// events
watch(() => props.events, (newEvents, oldEvents) => {
if (myChartInstance) {
//
oldEvents.forEach(eventName => {
myChartInstance.off(eventName);
});
//
newEvents.forEach(eventName => {
if (eventMap[eventName]) {
myChartInstance.on(eventName, (params) => {
emit(eventMap[eventName], params);
});
}
});
}
}, { deep: true })
// theme
watch(() => props.theme, (newTheme) => {
if (myChartInstance) {
cleanup();
initChart();
}
})
onUnmounted(() => {
cleanup();
})
//
defineExpose({
getInstance: () => myChartInstance,
resize: handleResize,
dispose: cleanup
})
</script>

View File

@ -1,9 +1,9 @@
import { createApp } from 'vue'
import '@/style/index.css';
import App from './App.vue'
import TDesign from 'tdesign-vue-next';
import router from './router';
import '@/style/index.css';
const app = createApp(App);
app.use(TDesign);

375
src/pages/cal/dh.vue Normal file
View File

@ -0,0 +1,375 @@
<template>
<t-row style="height: 100%;">
<t-col :flex="5" style="height: 100%;">
<t-card header-bordered style="height: 100%;overflow-y: auto;">
<template #header>
<t-button @click="handleCal" :loading="calLoading">开始计算</t-button>
</template>
<template #content>
<t-form label-align="left" label-width="280px" style="overflow-y: auto;">
<t-form-item style="min-height: 80px;" label="企业技术说明及改造方案">
<t-upload v-model="file6" :autoUpload="false" :max="1" accept=".docx,.pdf,.txt,.doc"/>
</t-form-item>
<t-form-item style="min-height: 80px;" label="数字化与智能控制系统技术方案">
<t-upload v-model="file5" :autoUpload="false" :max="1" accept=".docx,.pdf,.txt,.doc"/>
</t-form-item>
<t-form-item style="min-height: 80px;" label="企业低碳转型战略与总体规划">
<t-upload v-model="file1" :autoUpload="false" :max="1" accept=".docx,.pdf,.txt,.doc" />
</t-form-item>
<t-form-item style="min-height: 80px;" label="碳排放数据监测、核查与信息披露报告">
<t-upload v-model="file2" :autoUpload="false" :max="1" accept=".docx,.pdf,.txt,.doc"/>
</t-form-item>
<t-form-item style="min-height: 80px;" label="环境、社会与治理ESG尽职调查报告" >
<t-upload v-model="file3" :autoUpload="false" :max="1" accept=".docx,.pdf,.txt,.doc"/>
</t-form-item>
<t-form-item style="min-height: 80px;" label="项目融资与可行性研究方案">
<t-upload v-model="file4" :autoUpload="false" :max="1" accept=".docx,.pdf,.txt,.doc"/>
</t-form-item>
</t-form>
</template>
</t-card>
</t-col>
<t-col :flex="7" style="height: 100%;">
<t-card header-bordered style="height: 100%;">
<template #header>
<span v-if="score === null">暂无评分数据请上传相关文件后计算</span>
<span v-else>
贵企业的双碳贷后得分为
<span class="score-display">{{ score }}</span> ;
等级: <span class="level-display" :style="levelStyle">
{{ level }}
</span>
</span>
</template>
<template #content>
<!-- 动画容器 -->
<div class="animation-container" v-show="showAnimation">
<div class="particles-container">
<div class="particle" v-for="n in 30" :key="n" :style="particleStyle(n)"></div>
</div>
<div class="loading-circle">
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
</div>
<div class="loading-text" :style="{color: loadingTextColor}">{{ loadingText }}</div>
</div>
<!-- 结果展示 -->
<div class="result-container" v-show="showResult">
<div class="score-circle" :style="scoreCircleStyle">
<div class="score-value">{{ score }}</div>
<!-- <div class="score-label">总分</div> -->
</div>
<!-- <div class="level-badge" :style="levelStyle">
{{ level }}
</div> -->
<div class="result-description">
<p>根据您提交的资料我们已完成双碳贷后评估</p>
<p>您的企业评级为 <strong :style="{color: levelColor,fontSize: '24px'}">{{ level }}</strong> </p>
</div>
</div>
</template>
</t-card>
</t-col>
</t-row>
</template>
<script setup>
import { ref, onMounted, watch, computed } from 'vue';
import http from "@/api/request.js";
import { MessagePlugin } from "tdesign-vue-next"
onMounted(() => {
//
})
const file1 = ref([]);
const file2 = ref([]);
const file3 = ref([]);
const file4 = ref([]);
const file5 = ref([]);
const file6 = ref([]);
const calLoading = ref(false);
const score = ref(null);
const level = ref("较差");
const levelColor = ref('#000');
const showAnimation = ref(false);
const showResult = ref(false);
const loadingText = ref("正在计算评分...");
const loadingTextColor = ref("#ee2416");
//
const levelStyle = computed(() => {
return {
color: levelColor.value,
background: `${levelColor.value}15`,
border: `1px solid ${levelColor.value}30`
};
});
//
const scoreCircleStyle = computed(() => {
const percentage = score.value ? (score.value / 100) * 360 : 0;
return {
background: `conic-gradient(${levelColor.value} ${percentage}deg, #f0f0f0 ${percentage}deg 360deg)`
};
});
//
const particleStyle = (n) => {
const size = Math.random() * 8 + 4;
const left = Math.random() * 100;
const animationDelay = Math.random() * 2;
const animationDuration = Math.random() * 3 + 2;
return {
width: `${size}px`,
height: `${size}px`,
left: `${left}%`,
animationDelay: `${animationDelay}s`,
animationDuration: `${animationDuration}s`,
background: '#ee2416'
};
};
watch(score, (newScore) => {
if (newScore >= 80) {
level.value = '领先'
levelColor.value = '#4caf50'
} else if (newScore >= 60) {
level.value = '良好'
levelColor.value = '#2196f3'
} else if (newScore >= 30) {
level.value = '一般'
levelColor.value = '#ff9800'
} else {
level.value = '较差'
levelColor.value = '#f44336'
}
}, { immediate: true });
const handleCal = () => {
//
const files = [file1.value, file2.value, file3.value, file4.value, file5.value, file6.value];
const hasFiles = files.some(fileArray => fileArray && fileArray.length > 0);
if (!hasFiles) {
MessagePlugin.error('请至少上传一个文件后再进行计算');
return;
}
let formData = new FormData();
formData.append('file1', file1.value?.[0]?.raw);
formData.append('file2', file2.value?.[0]?.raw);
formData.append('file3', file3.value?.[0]?.raw);
formData.append('file4', file4.value?.[0]?.raw);
formData.append('file5', file5.value?.[0]?.raw);
formData.append('file6', file6.value?.[0]?.raw);
calLoading.value = true;
showAnimation.value = true;
showResult.value = false;
//
const loadingTexts = [
"正在分析企业低碳转型战略...",
"正在评估碳排放数据...",
"正在审核ESG尽职调查报告...",
"正在研究项目融资方案...",
"正在分析数字化技术方案...",
"正在评估企业技术说明...",
"正在生成最终评分..."
];
let textIndex = 0;
const minLoadingTime = 5000; // 5
const startTime = Date.now(); //
const textInterval = setInterval(() => {
loadingText.value = loadingTexts[textIndex];
textIndex = (textIndex + 1) % loadingTexts.length;
}, 800);
http.postForm("/cal_dh/", formData).then(res => {
const elapsedTime = Date.now() - startTime; //
const remainingTime = Math.max(0, minLoadingTime - elapsedTime); //
setTimeout(() => {
clearInterval(textInterval);
calLoading.value = false;
score.value = res.total_score;
//
setTimeout(() => {
showAnimation.value = false;
showResult.value = true;
}, 500);
}, remainingTime);
}).catch(e => {
const elapsedTime = Date.now() - startTime;
const remainingTime = Math.max(0, minLoadingTime - elapsedTime);
setTimeout(() => {
clearInterval(textInterval);
calLoading.value = false;
showAnimation.value = false;
}, remainingTime);
});
}
</script>
<style scoped>
.animation-container {
margin-top: 80px;
position: relative;
height: 300px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.particles-container {
position: absolute;
width: 100%;
height: 100%;
overflow: hidden;
}
.particle {
position: absolute;
border-radius: 50%;
opacity: 0.7;
animation: floatUp linear infinite;
}
@keyframes floatUp {
0% {
transform: translateY(100px) scale(0);
opacity: 0;
}
10% {
opacity: 0.7;
}
90% {
opacity: 0.7;
}
100% {
transform: translateY(-100px) scale(1);
opacity: 0;
}
}
.loading-circle {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.circle {
width: 15px;
height: 15px;
border-radius: 50%;
background: var(--td-brand-color);
animation: pulse 1.5s ease-in-out infinite;
}
.circle:nth-child(2) {
animation-delay: 0.2s;
}
.circle:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes pulse {
0%, 100% {
transform: scale(0.8);
opacity: 0.5;
}
50% {
transform: scale(1.2);
opacity: 1;
}
}
.loading-text {
font-size: 18px;
font-weight: 500;
transition: all 0.3s ease;
}
.result-container {
margin-top: 80px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 400px;
gap: 20px;
}
.score-circle {
width: 200px;
height: 200px;
border-radius: 50%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: relative;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
}
.score-circle::before {
content: '';
position: absolute;
width: 170px;
height: 170px;
border-radius: 50%;
background: white;
}
.score-value {
font-size: 40px;
font-weight: bold;
z-index: 1;
color: #333;
}
.score-label {
font-size: 14px;
color: #666;
z-index: 1;
}
.level-badge {
padding: 8px 20px;
border-radius: 20px;
font-size: 18px;
font-weight: bold;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.result-description {
text-align: center;
max-width: 80%;
line-height: 1.6;
font-size: 18px;
}
.score-display {
font-weight: bold;
font-size: 32px;
color: #2d8cf0;
}
.level-display {
font-weight: bold;
font-size: 32px;
padding: 4px 12px;
border-radius: 8px;
}
</style>

375
src/pages/cal/dq.vue Normal file
View File

@ -0,0 +1,375 @@
<template>
<t-row style="height: 100%;">
<t-col :flex="5" style="height: 100%;">
<t-card header-bordered style="height: 100%;overflow-y: auto;">
<template #header>
<t-button @click="handleCal" :loading="calLoading">开始计算</t-button>
</template>
<template #content>
<t-form label-align="left" label-width="280px" style="overflow-y: auto;">
<t-form-item style="min-height: 80px;" label="企业技术说明及改造方案">
<t-upload v-model="file6" :autoUpload="false" :max="1" accept=".docx,.pdf,.txt,.doc"/>
</t-form-item>
<t-form-item style="min-height: 80px;" label="企业低碳转型战略与总体规划">
<t-upload v-model="file1" :autoUpload="false" :max="1" accept=".docx,.pdf,.txt,.doc" />
</t-form-item>
<t-form-item style="min-height: 80px;" label="碳排放数据监测、核查与信息披露报告">
<t-upload v-model="file2" :autoUpload="false" :max="1" accept=".docx,.pdf,.txt,.doc"/>
</t-form-item>
<t-form-item style="min-height: 80px;" label="数字化与智能控制系统技术方案">
<t-upload v-model="file5" :autoUpload="false" :max="1" accept=".docx,.pdf,.txt,.doc"/>
</t-form-item>
<t-form-item style="min-height: 80px;" label="环境、社会与治理ESG尽职调查报告" >
<t-upload v-model="file3" :autoUpload="false" :max="1" accept=".docx,.pdf,.txt,.doc"/>
</t-form-item>
<t-form-item style="min-height: 80px;" label="项目融资与可行性研究方案">
<t-upload v-model="file4" :autoUpload="false" :max="1" accept=".docx,.pdf,.txt,.doc"/>
</t-form-item>
</t-form>
</template>
</t-card>
</t-col>
<t-col :flex="7" style="height: 100%;">
<t-card header-bordered style="height: 100%;">
<template #header>
<span v-if="score === null">暂无评分数据请上传相关文件后计算</span>
<span v-else>
贵企业的双碳贷前得分为
<span class="score-display">{{ score }}</span> ;
等级: <span class="level-display" :style="levelStyle">
{{ level }}
</span>
</span>
</template>
<template #content>
<!-- 动画容器 -->
<div class="animation-container" v-show="showAnimation">
<div class="particles-container">
<div class="particle" v-for="n in 30" :key="n" :style="particleStyle(n)"></div>
</div>
<div class="loading-circle">
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
</div>
<div class="loading-text" :style="{color: loadingTextColor}">{{ loadingText }}</div>
</div>
<!-- 结果展示 -->
<div class="result-container" v-show="showResult">
<div class="score-circle" :style="scoreCircleStyle">
<div class="score-value">{{ score }}</div>
<!-- <div class="score-label">总分</div> -->
</div>
<!-- <div class="level-badge" :style="levelStyle">
{{ level }}
</div> -->
<div class="result-description">
<p>根据您提交的资料我们已完成双碳贷前评估</p>
<p>您的企业评级为 <strong :style="{color: levelColor,fontSize: '24px'}">{{ level }}</strong> </p>
</div>
</div>
</template>
</t-card>
</t-col>
</t-row>
</template>
<script setup>
import { ref, onMounted, watch, computed } from 'vue';
import http from "@/api/request.js";
import { MessagePlugin } from "tdesign-vue-next"
onMounted(() => {
//
})
const file1 = ref([]);
const file2 = ref([]);
const file3 = ref([]);
const file4 = ref([]);
const file5 = ref([]);
const file6 = ref([]);
const calLoading = ref(false);
const score = ref(null);
const level = ref("较差");
const levelColor = ref('#000');
const showAnimation = ref(false);
const showResult = ref(false);
const loadingText = ref("正在计算评分...");
const loadingTextColor = ref("#ee2416");
//
const levelStyle = computed(() => {
return {
color: levelColor.value,
background: `${levelColor.value}15`,
border: `1px solid ${levelColor.value}30`
};
});
//
const scoreCircleStyle = computed(() => {
const percentage = score.value ? (score.value / 100) * 360 : 0;
return {
background: `conic-gradient(${levelColor.value} ${percentage}deg, #f0f0f0 ${percentage}deg 360deg)`
};
});
//
const particleStyle = (n) => {
const size = Math.random() * 8 + 4;
const left = Math.random() * 100;
const animationDelay = Math.random() * 2;
const animationDuration = Math.random() * 3 + 2;
return {
width: `${size}px`,
height: `${size}px`,
left: `${left}%`,
animationDelay: `${animationDelay}s`,
animationDuration: `${animationDuration}s`,
background: '#ee2416'
};
};
watch(score, (newScore) => {
if (newScore >= 80) {
level.value = '领先'
levelColor.value = '#4caf50'
} else if (newScore >= 60) {
level.value = '良好'
levelColor.value = '#2196f3'
} else if (newScore >= 30) {
level.value = '一般'
levelColor.value = '#ff9800'
} else {
level.value = '较差'
levelColor.value = '#f44336'
}
}, { immediate: true });
const handleCal = () => {
//
const files = [file1.value, file2.value, file3.value, file4.value, file5.value, file6.value];
const hasFiles = files.some(fileArray => fileArray && fileArray.length > 0);
if (!hasFiles) {
MessagePlugin.error('请至少上传一个文件后再进行计算');
return;
}
let formData = new FormData();
formData.append('file1', file1.value?.[0]?.raw);
formData.append('file2', file2.value?.[0]?.raw);
formData.append('file3', file3.value?.[0]?.raw);
formData.append('file4', file4.value?.[0]?.raw);
formData.append('file5', file5.value?.[0]?.raw);
formData.append('file6', file6.value?.[0]?.raw);
calLoading.value = true;
showAnimation.value = true;
showResult.value = false;
//
const loadingTexts = [
"正在分析企业低碳转型战略...",
"正在评估碳排放数据...",
"正在审核ESG尽职调查报告...",
"正在研究项目融资方案...",
"正在分析数字化技术方案...",
"正在评估企业技术说明...",
"正在生成最终评分..."
];
let textIndex = 0;
const minLoadingTime = 5000; // 5
const startTime = Date.now(); //
const textInterval = setInterval(() => {
loadingText.value = loadingTexts[textIndex];
textIndex = (textIndex + 1) % loadingTexts.length;
}, 800);
http.postForm("/cal/", formData).then(res => {
const elapsedTime = Date.now() - startTime; //
const remainingTime = Math.max(0, minLoadingTime - elapsedTime); //
setTimeout(() => {
clearInterval(textInterval);
calLoading.value = false;
score.value = res.total_score;
//
setTimeout(() => {
showAnimation.value = false;
showResult.value = true;
}, 500);
}, remainingTime);
}).catch(e => {
const elapsedTime = Date.now() - startTime;
const remainingTime = Math.max(0, minLoadingTime - elapsedTime);
setTimeout(() => {
clearInterval(textInterval);
calLoading.value = false;
showAnimation.value = false;
}, remainingTime);
});
}
</script>
<style scoped>
.animation-container {
margin-top: 80px;
position: relative;
height: 300px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.particles-container {
position: absolute;
width: 100%;
height: 100%;
overflow: hidden;
}
.particle {
position: absolute;
border-radius: 50%;
opacity: 0.7;
animation: floatUp linear infinite;
}
@keyframes floatUp {
0% {
transform: translateY(100px) scale(0);
opacity: 0;
}
10% {
opacity: 0.7;
}
90% {
opacity: 0.7;
}
100% {
transform: translateY(-100px) scale(1);
opacity: 0;
}
}
.loading-circle {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.circle {
width: 15px;
height: 15px;
border-radius: 50%;
background: var(--td-brand-color);
animation: pulse 1.5s ease-in-out infinite;
}
.circle:nth-child(2) {
animation-delay: 0.2s;
}
.circle:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes pulse {
0%, 100% {
transform: scale(0.8);
opacity: 0.5;
}
50% {
transform: scale(1.2);
opacity: 1;
}
}
.loading-text {
font-size: 18px;
font-weight: 500;
transition: all 0.3s ease;
}
.result-container {
margin-top: 80px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 400px;
gap: 20px;
}
.score-circle {
width: 200px;
height: 200px;
border-radius: 50%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: relative;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
}
.score-circle::before {
content: '';
position: absolute;
width: 170px;
height: 170px;
border-radius: 50%;
background: white;
}
.score-value {
font-size: 40px;
font-weight: bold;
z-index: 1;
color: #333;
}
.score-label {
font-size: 14px;
color: #666;
z-index: 1;
}
.level-badge {
padding: 8px 20px;
border-radius: 20px;
font-size: 18px;
font-weight: bold;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.result-description {
text-align: center;
max-width: 80%;
line-height: 1.6;
font-size: 18px;
}
.score-display {
font-weight: bold;
font-size: 32px;
color: #2d8cf0;
}
.level-display {
font-weight: bold;
font-size: 32px;
padding: 4px 12px;
border-radius: 8px;
}
</style>

167
src/pages/cal/dq_old.vue Normal file
View File

@ -0,0 +1,167 @@
<template>
<t-row style="height: 100%;">
<t-col :flex="2" style="height: 100%;min-height: 700px;">
<t-card style="height: 100%;">
<t-form label-align="left" label-width="0">
<t-form-item style="min-height: 70px;">
<t-upload v-model="file1" :autoUpload="false" :max="1" accept=".docx,.pdf,.txt"
tips="企业低碳转型战略与总体规划" />
</t-form-item>
<t-form-item style="min-height: 70px;">
<t-upload v-model="file2" :autoUpload="false" :max="1" accept=".docx,.pdf,.txt"
tips="碳排放数据监测、核查与信息披露报告" />
</t-form-item>
<t-form-item style="min-height: 70px;">
<t-upload v-model="file3" :autoUpload="false" :max="1" accept=".docx,.pdf,.txt"
tips="环境、社会与治理ESG尽职调查报告" />
</t-form-item>
<t-form-item style="min-height: 70px;">
<t-upload v-model="file4" :autoUpload="false" :max="1" accept=".docx,.pdf,.txt"
tips="项目融资与可行性研究方案" />
</t-form-item>
<t-form-item style="min-height: 70px;">
<t-upload v-model="file5" :autoUpload="false" :max="1" accept=".docx,.pdf,.txt"
tips="数字化与智能控制系统技术方案" />
</t-form-item>
<t-form-item style="min-height: 70px;">
<t-upload v-model="file6" :autoUpload="false" :max="1" accept=".docx,.pdf,.txt"
tips="企业技术说明及改造方案" />
</t-form-item>
<t-form-item>
<t-button @click="handleCal" :loading="calLoading">开始计算</t-button>
</t-form-item>
</t-form>
</t-card>
</t-col>
<t-col :flex="10" style="height: 100%;">
<t-card style="font-size: 24px;height: 56px;">
<span v-if="score === null">暂无评分数据请上传相关文件后计算</span>
<span v-else>
贵企业的双碳贷前得分为
<span :style="{
color: levelColor,
fontWeight: 'bold',
fontSize: '32px',
}">{{ score }}</span> ;
等级: <span :style="{
color: levelColor,
fontWeight: 'bold',
fontSize: '32px',
}">
{{ level }}
</span>
</span>
</t-card>
<t-card>
<t-table :data="tableData" :columns="columns" row-key="id" :hover="true" size="small"
:rowspan-and-colspan="colSpan">
<template #scoringCriteria="{ row }">
<span v-for="(option, optIndex) in row.scoringCriteria" :key="optIndex"
style="margin-bottom: 4px;">
{{ option.选项 }} ({{ option.得分 }})
</span>
</template>
</t-table>
</t-card>
</t-col>
</t-row>
</template>
<script setup>
import { ref, onMounted, watch } from 'vue';
import http from "@/api/request.js";
onMounted(() => {
getStandard();
})
const getStandard = async () => {
tableData.value = await http.get("/standard/");
}
const tableData = ref([]);
const columns = ref([
{
title: '一级指标',
colKey: 'firstLevel',
},
{
title: '二级指标',
colKey: 'secondLevel',
},
{
title: '三级指标',
colKey: 'thirdLevel',
},
{
title: '选项及分值',
colKey: 'scoringCriteria',
width: 300
},
{
title: '结果',
colKey: 'result',
},
{
title: '得分',
colKey: 'score',
}
]);
const colSpan = ({ row, column, rowIndex, columnIndex }) => {
// if (column.colKey === 'firstLevel') {
// return {
// rowspan: getFirstLevelRowspan(rowIndex),
// colspan: 1
// };
// }
// if (column.colKey === 'secondLevel') {
// return {
// rowspan: getSecondLevelRowspan(rowIndex),
// colspan: 1
// };
// }
return {
rowspan: 1,
colspan: 1
};
};
const file1 = ref([]);
const file2 = ref([]);
const file3 = ref([]);
const file4 = ref([]);
const file5 = ref([]);
const file6 = ref([]);
const calLoading = ref(false);
const score = ref(null);
const level = ref("较差");
const levelColor = ref('#000')
watch(score, (newScore) => {
if (newScore >= 80) {
level.value = '领先'
levelColor.value = '#4caf50'
} else if (newScore >= 60) {
level.value = '良好'
levelColor.value = '#2196f3'
} else if (newScore >= 30) {
level.value = '一般'
levelColor.value = '#ff9800'
} else {
level.value = '较差'
levelColor.value = '#f44336'
}
}, { immediate: true })
const handleCal = () => {
let formData = new FormData();
formData.append('file1', file1.value?.[0]?.raw);
formData.append('file2', file2.value?.[0]?.raw);
formData.append('file3', file3.value?.[0]?.raw);
formData.append('file4', file4.value?.[0]?.raw);
formData.append('file5', file5.value?.[0]?.raw);
formData.append('file6', file6.value?.[0]?.raw);
calLoading.value = true;
http.postForm("/cal/", formData).then(res => {
calLoading.value = false;
tableData.value = res.data;
score.value = res.total_score;
}).catch(e=>{calLoading.value = false;})
console.log(formData);
}
</script>

519
src/pages/cal/index.vue Normal file
View File

@ -0,0 +1,519 @@
<template>
<t-row>
<t-col :sm="12" :md="6">
<t-card hoverShadow header-bordered>
<template #header>
<div class="redTitle" @click="goDq">转型金融贷前计算器</div>
<div>
<t-button theme="default" style="margin-right:2px" @click="showDq">指标说明</t-button>
<t-button @click="goDq">前往测算</t-button>
</div>
</template>
<div style="height:400px" v-if="!chartShow"></div>
<xt-chart :option="option" height="400px" v-else></xt-chart>
<!-- 原生表格实现 -->
<div class="table-container">
<table class="score-table">
<thead>
<tr>
<th>
<div>项目碳排放量</div>
<div style="font-size: 12px;">与基准线相比</div>
</th>
<th>企业分值区间</th>
<th>项目/企业等级</th>
</tr>
</thead>
<tbody>
<tr
v-for="(item, index) in tableData"
:key="index"
:class="['score-row', { 'current-row': item.isCurrent }]"
>
<td :style="{ color: item.color, fontWeight: 'bold' }">{{ item.pf }}</td>
<td :style="{ color: item.color, fontWeight: 'bold' }">{{ item.score }}</td>
<td :style="{ color: item.color, fontWeight: 'bold' }">{{ item.level }}</td>
</tr>
</tbody>
</table>
</div>
</t-card>
</t-col>
<t-col :sm="12" :md="6">
<t-card hoverShadow header-bordered>
<template #header>
<div class="redTitle" @click="goDh">转型金融贷后计算器</div>
<div>
<t-button theme="default" style="margin-right:2px" @click="showDh">指标说明</t-button>
<t-button @click="goDh">前往测算</t-button>
</div>
</template>
<div style="height:400px" v-if="!chartShow"></div>
<xt-chart :option="option_dh" height="400px" v-else></xt-chart>
<div class="table-container">
<table class="score-table">
<thead>
<tr>
<th>
<div>项目碳排放量</div>
<div style="font-size: 12px;">与基准线相比</div>
</th>
<th>企业分值区间</th>
<th>项目/企业等级</th>
</tr>
</thead>
<tbody>
<tr
v-for="(item, index) in tableData_dh"
:key="index"
:class="['score-row', { 'current-row': item.isCurrent }]"
>
<td :style="{ color: item.color, fontWeight: 'bold' }">{{ item.pf }}</td>
<td :style="{ color: item.color, fontWeight: 'bold' }">{{ item.score }}</td>
<td :style="{ color: item.color, fontWeight: 'bold' }">{{ item.level }}</td>
</tr>
</tbody>
</table>
</div>
</t-card>
</t-col>
</t-row>
<t-dialog
:closeBtn="false"
closeOnEscKeydown
closeOnOverlayClick
:footer="false"
:header="false"
v-model:visible="dialogVisible"
>
<div v-for="(item, key, index) in descriptions">
<div style="font-size:16px; font-weight: bold;">{{ key }}</div>
<div >{{ item }}</div>
</div>
</t-dialog>
</template>
<script setup>
import router from '@/router'
import { ref, onMounted, onUnmounted, computed } from 'vue'
import xtChart from "@/components/xtChart/index.vue";
const goDq = () => {
router.push('/dq')
}
const goDh = () => {
router.push('/dh')
}
//
const currentData = ref([35, 20, 10, 10, 10, 8, 7]);
const currentData_dh = ref([35, 25, 13, 9, 9, 5, 2, 2]);
const dialogVisible = ref(false);
//
const descriptions = ref({});
const showDq = () => {
dialogVisible.value = true;
descriptions.value = {
'技术路径': '评估企业采用的技术方案是否先进、可行,是否符合低碳转型要求。包括技术创新性、成熟度、实施路径清晰度等维度。',
'碳排放': '衡量企业碳排放水平和减排潜力。包括碳排放总量、碳强度、减排目标达成率等关键指标。',
'企业碳治理': '评估企业碳管理体系和治理能力。包括碳管理制度、管理机构、信息披露、碳资产管理等方面。',
'融资计划': '分析转型项目融资方案的合理性和可行性。包括资金需求、融资结构、成本控制、风险防范等要素。',
'社会与治理协同效益': '评估项目对社会发展和公司治理的协同促进作用。包括就业创造、社区发展、治理结构优化等。',
'数字化与智能化水平': '衡量企业数字化转型和智能化应用程度。包括数字化基础设施、数据应用、智能化改造等方面。',
'环境协同效益': '评估项目对环境保护和生态改善的协同效应。包括污染物减排、资源循环利用、生态保护等。'
}
}
const showDh = () => {
dialogVisible.value = true;
descriptions.value = {
"减碳量": "核心指标,直接体现转型成效",
"技术成熟度": "决定技术可行性与推广价值",
"企业碳治理": "反映企业长期减碳意愿与能力",
"融资计划": "体现项目落地与资金保障能力",
"环境协同效益": "综合评价项目的社会与环境价值",
"社会与治理协同效益": "体现企业全面可持续发展的能力",
"创新与可持续竞争力": "体现企业是否具备持续引领行业绿色转型的能力",
"数字化与智能化水平": "体现企业是否具备持续引领行业绿色转型的能力"
}
}
//
const currentScore = computed(() => {
let score = currentData.value.reduce((sum, value) => sum + value, 0);
if (score>=80) {
option.value.series[0].data[0].areaStyle.color = 'rgba(76, 175, 80, 0.3)';
}else if (score>=60) {
option.value.series[0].data[0].areaStyle.color = 'rgba(33, 150, 243, 0.3)';
} else if (score>=30) {
option.value.series[0].data[0].areaStyle.color = 'rgba(255, 152, 0, 0.3)';
} else {
option.value.series[0].data[0].areaStyle.color = 'rgba(244, 67, 54, 0.3)';
}
return score;
});
const currentScore_dh = computed(() => {
let score = currentData_dh.value.reduce((sum, value) => sum + value, 0);
if (score>=80) {
option_dh.value.series[0].data[0].areaStyle.color = 'rgba(76, 175, 80, 0.3)';
}else if (score>=60) {
option_dh.value.series[0].data[0].areaStyle.color = 'rgba(33, 150, 243, 0.3)';
} else if (score>=30) {
option_dh.value.series[0].data[0].areaStyle.color = 'rgba(255, 152, 0, 0.3)';
} else {
option_dh.value.series[0].data[0].areaStyle.color = 'rgba(244, 67, 54, 0.3)';
}
return score;
})
const tableData = computed(() => [
{
pf: "低于10%及以上",
score: "80~100",
level: "领先",
color: "rgba(76, 175, 80, 0.7)",
isCurrent: currentScore.value >= 80
},
{
pf: "低于5%(含)~0%",
score: "60~79",
level: "良好",
color: "rgba(33, 150, 243, 0.7)",
isCurrent: currentScore.value >= 60 && currentScore.value < 80
},
{
pf: "低于0~5%",
score: "30~59",
level: "一般",
color: "rgba(255, 152, 0, 0.7)",
isCurrent: currentScore.value >= 30 && currentScore.value < 60
},
{
pf: "高于基准线",
score: "0~29",
level: "较差",
color: "rgba(244, 67, 54, 0.7)",
isCurrent: currentScore.value < 30
}
]);
const tableData_dh = computed(() => [
{
pf: "低于10%及以上",
score: "80~100",
level: "领先",
color: "rgba(76, 175, 80, 0.7)",
isCurrent: currentScore_dh.value >= 80
},
{
pf: "低于5%(含)~0%",
score: "60~79",
level: "良好",
color: "rgba(33, 150, 243, 0.7)",
isCurrent: currentScore_dh.value >= 60 && currentScore_dh.value < 80
},
{
pf: "低于0~5%",
score: "30~59",
level: "一般",
color: "rgba(255, 152, 0, 0.7)",
isCurrent: currentScore_dh.value >= 30 && currentScore_dh.value < 60
},
{
pf: "高于基准线",
score: "0~29",
level: "较差",
color: "rgba(244, 67, 54, 0.7)",
isCurrent: currentScore_dh.value < 30
}
]);
//
const option = ref({
title: {
text: '核算雷达图',
textStyle: {
color: '#c23531'
}
},
// tooltip: {
// },
radar: {
center: ['50%', '55%'],
radius: '70%',
indicator: [
{ name: '技术路径', max: 35 },
{ name: '碳排放', max: 20 },
{ name: '企业碳治理', max: 10 },
{ name: '融资计划', max: 10 },
{ name: '社会与治理协同效益', max: 10 },
{ name: '数字化与智能化水平', max: 8 },
{ name: '环境协同效益', max: 7 }
],
axisName: {
color: '#c23531',
// fontWeight: 'bold',
fontSize: 16
},
splitLine: {
lineStyle: {
color: ['rgba(194, 53, 49, 0.3)']
}
},
splitArea: {
areaStyle: {
color: ['rgba(194, 53, 49, 0.05)', 'rgba(194, 53, 49, 0.1)']
}
},
axisLine: {
lineStyle: {
color: 'rgba(194, 53, 49, 0.5)'
}
}
},
series: [
{
type: 'radar',
data: [
{
value: currentData.value,
name: '企业评估',
areaStyle: {
color: 'rgba(194, 53, 49, 0.4)'
},
lineStyle: {
color: '#c23531',
width: 2
},
itemStyle: {
color: '#c23531'
}
}
]
}
]
});
const option_dh = ref({
title: {
text: '核算雷达图',
textStyle: {
color: '#c23531'
}
},
// tooltip: {
// },
radar: {
center: ['50%', '55%'],
radius: '70%',
indicator: [
{ name: '减碳量', max: 35 },
{ name: '技术成熟度', max: 25 },
{ name: '企业碳治理', max: 13 },
{ name: '融资计划', max: 9 },
{ name: '环境协同效益', max: 9 },
{ name: '社会与治理协同效益', max: 5 },
{ name: '创新与可持续竞争力', max: 2 },
{ name: '数字化与智能化水平', max: 2 }
],
axisName: {
color: '#c23531',
// fontWeight: 'bold',
fontSize: 16
},
splitLine: {
lineStyle: {
color: ['rgba(194, 53, 49, 0.3)']
}
},
splitArea: {
areaStyle: {
color: ['rgba(194, 53, 49, 0.05)', 'rgba(194, 53, 49, 0.1)']
}
},
axisLine: {
lineStyle: {
color: 'rgba(194, 53, 49, 0.5)'
}
}
},
series: [
{
type: 'radar',
data: [
{
value: currentData_dh.value,
name: '企业评估',
areaStyle: {
color: 'rgba(194, 53, 49, 0.4)'
},
lineStyle: {
color: '#c23531',
width: 2
},
itemStyle: {
color: '#c23531'
}
}
]
}
]
});
//
const updateRadarData = () => {
//
const newData = currentData.value.map((value, index) => {
const maxValue = option.value.radar.indicator[index].max;
const minValue = option.value.radar.indicator[index].max * 0.2; //
const fluctuationRange = maxValue * 0.5; // 10%
const fluctuation = (Math.random() - 0.5) * 2 * fluctuationRange;
let newValue = value + fluctuation;
//
newValue = Math.min(newValue, maxValue);
// 0
newValue = Math.max(newValue, minValue);
return Math.round(newValue * 10) / 10; //
});
currentData.value = newData;
//
option.value.series[0].data[0].value = newData;
};
//
const updateRadarData_dh = () => {
//
const newData = currentData_dh.value.map((value, index) => {
const maxValue = option_dh.value.radar.indicator[index].max;
const minValue = option_dh.value.radar.indicator[index].max * 0.2; //
const fluctuationRange = maxValue * 0.5; // 10%
const fluctuation = (Math.random() - 0.5) * 2 * fluctuationRange;
let newValue = value + fluctuation;
//
newValue = Math.min(newValue, maxValue);
// 0
newValue = Math.max(newValue, minValue);
return Math.round(newValue * 10) / 10; //
});
currentData_dh.value = newData;
//
option_dh.value.series[0].data[0].value = newData;
};
//
let timer = null;
let timer_dh = null;
const chartShow = ref(false);
onMounted(() => {
// 4
setTimeout(() =>{ chartShow.value = true;}, 200);
timer = setInterval(updateRadarData, 4000);
setTimeout(()=>{
timer_dh = setInterval(updateRadarData_dh, 4000);
}, 2000);
});
onUnmounted(() => {
//
if (timer) {
clearInterval(timer);
}
if (timer_dh) {
clearInterval(timer_dh);
}
});
</script>
<style scoped>
.bigNav {
font-size: 20px;
font-weight: 500;
color: var(--td-brand-color);
height: 180px;
display: flex;
align-items: center;
justify-content: center;
}
.redTitle {
color: var(--td-brand-color);
font-size: 20px;
font-weight: 500;
cursor: pointer;
}
/* 表格容器样式 */
.table-container {
margin-top: 20px;
background-color: #f5f5f5;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
/* 原生表格样式 */
.score-table {
width: 100%;
border-collapse: collapse;
font-size: 14px;
}
/* 表头样式 */
.score-table thead {
background-color: #f7f6f6;
}
.score-table th {
/* color: #fff; */
color: black;
font-weight: bold;
padding: 12px 16px;
text-align: center;
border-bottom: 2px solid #555;
}
/* 表格行样式 */
.score-table tbody tr {
transition: all 0.3s ease;
background-color: white;
}
.score-table td {
padding: 12px 16px;
text-align: center;
border-bottom: 1px solid #e0e0e0;
color: #333;
font-weight: 500;
}
/* 当前行高亮样式 */
.current-row {
transform: scale(1.02);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
position: relative;
z-index: 1;
}
.current-row td {
font-weight: bold;
color: #000;
}
/* 鼠标悬停效果 */
.score-table tbody tr:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
z-index: 2;
}
</style>

View File

@ -2,7 +2,7 @@
<div class="login-wrapper">
<div class="login-container">
<div class="title-container">
<h1>XT_WEB_TD</h1>
<h1 style="color: var(--td-brand-color)">{{ systemInfo.base_name }}</h1>
</div>
<t-form class="item-container" label-width="0" @submit="handleLogin">
<t-form-item>
@ -28,22 +28,33 @@
</div>
</template>
<script setup>
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import { authToken } from '@/store/index.js'
const router = useRouter();
import { ref, onMounted } from 'vue';
import router from '@/router';
import { authToken, systemInfo, userInfo } from '@/store/index.js'
import { MessagePlugin } from "tdesign-vue-next"
import http from "@/api/request.js";
const formData = ref({
username: "",
password: "",
});
const submitLoading = ref(false);
onMounted(() => {
http.get("/system_info/").then(res=>{
Object.assign(systemInfo, res)
})
})
const handleLogin = () => {
submitLoading.value = true;
setTimeout(() => {
authToken.access = "123456";
http.post("/login/", formData.value).then(res=>{
MessagePlugin.success("登录成功");
authToken.access = res.access;
Object.assign(userInfo, res.userInfo);
submitLoading.value = false;
router.replace({ path: "/home" });
}, 300);
}).catch(e=>{
submitLoading.value = false;
})
}
</script>
<style scoped>

View File

@ -1,5 +1,6 @@
import { createRouter, createWebHistory} from 'vue-router';
import { authToken } from '@/store/index.js'
import http from "@/api/request.js";
const routes = [
{ path: "/", redirect: "/login" },
{
@ -17,17 +18,30 @@ const routes = [
name: "工作台",
meta: { icon: "file"},
children: [
// {
// path: "home",
// name: "主页",
// component: () => import("@/pages/home/index.vue"),
// },
// {
// path: "user",
// name: "用户管理",
// component: () => import("@/pages/system/user.vue"),
// },
{
path: "home",
name: "主页",
component: () => import("@/pages/home/index.vue"),
meta: { icon: "app"},
name: "快捷导航",
component: () => import("@/pages/cal/index.vue"),
},
{
path: "user",
name: "用户管理",
component: () => import("@/pages/system/user.vue"),
meta: { icon: "app"},
path: "dq",
name: "贷前计算",
component: () => import("@/pages/cal/dq.vue"),
},
{
path: "dh",
name: "贷后计算",
component: () => import("@/pages/cal/dh.vue"),
},
],
},
@ -41,7 +55,7 @@ const router = createRouter({
routes,
});
router.beforeEach((to, from, next) => {
router.beforeEach(async (to, from, next) => {
if (to.path === "/login") {
if (authToken.access) {
next("/home");
@ -50,7 +64,14 @@ router.beforeEach((to, from, next) => {
}
} else {
if (authToken.access) {
next();
try{
await http.get("/check_token/")
next();
} catch(e){
authToken.access = null;
authToken.refresh = null;
next("/login");
}
} else {
next("/login");
}

View File

@ -1,5 +1,14 @@
import { reactive, watch } from "vue";
export const systemInfo = reactive(JSON.parse(localStorage.getItem("systemInfo")) || {
// base_name: "系统名称"
base_name: "双碳金融系统"
})
watch(systemInfo, (newVal) => {
localStorage.setItem("systemInfo", JSON.stringify(newVal));
}, { deep: true, immediate: true})
export const userInfo = reactive(JSON.parse(localStorage.getItem("userInfo")) || {
name: "张三",
username: "zhangsan"
@ -8,7 +17,10 @@ watch(userInfo, (newVal) => {
localStorage.setItem("userInfo", JSON.stringify(newVal));
}, { deep: true, immediate: true})
export const authToken = reactive(JSON.parse(localStorage.getItem("authToken")) ||{
/**
* @type {{access: string|null, refresh: string|null}}
*/
export const authToken = reactive(JSON.parse(localStorage.getItem("authToken")) || {
access: null,
refresh: null
})

View File

@ -9,3 +9,7 @@ body {
font-weight: normal;
font-style: normal;
}
.t-card__header {
min-height: 36px;
}

View File

@ -1,3 +1,355 @@
body {
margin: 0;
:root, :root[theme-mode="light"] {
--td-brand-color-1: #fff0ec;
--td-brand-color-2: #ffd8d0;
--td-brand-color-3: #ffb9ac;
--td-brand-color-4: #ff927f;
--td-brand-color-5: #ff5f48;
--td-brand-color-6: #ee2416;
--td-brand-color-7: #c30000;
--td-brand-color-8: #950000;
--td-brand-color-9: #6b0000;
--td-brand-color-10: #480000;
--td-brand-color-light: var(--td-brand-color-1);
--td-brand-color-focus: var(--td-brand-color-2);
--td-brand-color-disabled: var(--td-brand-color-3);
--td-brand-color-hover: var(--td-brand-color-5);
--td-brand-color: var(--td-brand-color-6);
--td-brand-color-active: var(--td-brand-color-7);
--td-warning-color-1: #fef3e6;
--td-warning-color-2: #f9e0c7;
--td-warning-color-3: #f7c797;
--td-warning-color-4: #f2995f;
--td-warning-color-5: #ed7b2f;
--td-warning-color-6: #d35a21;
--td-warning-color-7: #ba431b;
--td-warning-color-8: #9e3610;
--td-warning-color-9: #842b0b;
--td-warning-color-10: #5a1907;
--td-warning-color: var(--td-warning-color-5);
--td-warning-color-hover: var(--td-warning-color-4);
--td-warning-color-focus: var(--td-warning-color-2);
--td-warning-color-active: var(--td-warning-color-6);
--td-warning-color-disabled: var(--td-warning-color-3);
--td-warning-color-light: var(--td-warning-color-1);
--td-error-color-1: #fdecee;
--td-error-color-2: #f9d7d9;
--td-error-color-3: #f8b9be;
--td-error-color-4: #f78d94;
--td-error-color-5: #f36d78;
--td-error-color-6: #e34d59;
--td-error-color-7: #c9353f;
--td-error-color-8: #b11f26;
--td-error-color-9: #951114;
--td-error-color-10: #680506;
--td-error-color: var(--td-error-color-6);
--td-error-color-hover: var(--td-error-color-5);
--td-error-color-focus: var(--td-error-color-2);
--td-error-color-active: var(--td-error-color-7);
--td-error-color-disabled: var(--td-error-color-3);
--td-error-color-light: var(--td-error-color-1);
--td-success-color-1: #e8f8f2;
--td-success-color-2: #bcebdc;
--td-success-color-3: #85dbbe;
--td-success-color-4: #48c79c;
--td-success-color-5: #00a870;
--td-success-color-6: #078d5c;
--td-success-color-7: #067945;
--td-success-color-8: #056334;
--td-success-color-9: #044f2a;
--td-success-color-10: #033017;
--td-success-color: var(--td-success-color-5);
--td-success-color-hover: var(--td-success-color-4);
--td-success-color-focus: var(--td-success-color-2);
--td-success-color-active: var(--td-success-color-6);
--td-success-color-disabled: var(--td-success-color-3);
--td-success-color-light: var(--td-success-color-1);
--td-gray-color-1: #f3f3f3;
--td-gray-color-2: #eee;
--td-gray-color-3: #e7e7e7;
--td-gray-color-4: #dcdcdc;
--td-gray-color-5: #c5c5c5;
--td-gray-color-6: #a6a6a6;
--td-gray-color-7: #8b8b8b;
--td-gray-color-8: #777;
--td-gray-color-9: #5e5e5e;
--td-gray-color-10: #4b4b4b;
--td-gray-color-11: #383838;
--td-gray-color-12: #2c2c2c;
--td-gray-color-13: #242424;
--td-gray-color-14: #181818;
--td-bg-color-container: #fff;
--td-bg-color-container-select: #fff;
--td-bg-color-page: var(--td-gray-color-2);
--td-bg-color-container-hover: var(--td-gray-color-1);
--td-bg-color-container-active: var(--td-gray-color-3);
--td-bg-color-secondarycontainer: var(--td-gray-color-1);
--td-bg-color-secondarycontainer-hover: var(--td-gray-color-2);
--td-bg-color-secondarycontainer-active: var(--td-gray-color-4);
--td-bg-color-component: var(--td-gray-color-3);
--td-bg-color-component-hover: var(--td-gray-color-4);
--td-bg-color-component-active: var(--td-gray-color-6);
--td-bg-color-component-disabled: var(--td-gray-color-2);
--td-component-stroke: var(--td-gray-color-3);
--td-component-border: var(--td-gray-color-4);
--td-font-white-1: #ffffff;
--td-font-white-2: rgba(255, 255, 255, 0.55);
--td-font-white-3: rgba(255, 255, 255, 0.35);
--td-font-white-4: rgba(255, 255, 255, 0.22);
--td-font-gray-1: rgba(0, 0, 0, 0.9);
--td-font-gray-2: rgba(0, 0, 0, 0.6);
--td-font-gray-3: rgba(0, 0, 0, 0.4);
--td-font-gray-4: rgba(0, 0, 0, 0.26);
--td-text-color-primary: var(--td-font-gray-1);
--td-text-color-secondary: var(--td-font-gray-2);
--td-text-color-placeholder: var(--td-font-gray-3);
--td-text-color-disabled: var(--td-font-gray-4);
--td-text-color-anti: #fff;
--td-text-color-brand: var(--td-brand-color);
--td-text-color-link: var(--td-brand-color);
--td-brand-color-light-hover: var(--td-brand-color-2);
--td-warning-color-light-hover: var(--td-warning-color-2);
--td-error-color-light-hover: var(--td-error-color-2);
--td-success-color-light-hover: var(--td-success-color-2);
--td-bg-color-secondarycomponent: var(--td-gray-color-4);
--td-bg-color-secondarycomponent-hover: var(--td-gray-color-5);
--td-bg-color-secondarycomponent-active: var(--td-gray-color-6);
--td-table-shadow-color: rgba(0, 0, 0, 8%);
--td-scrollbar-color: rgba(0, 0, 0, 10%);
--td-scrollbar-hover-color: rgba(0, 0, 0, 30%);
--td-scroll-track-color: #fff;
--td-bg-color-specialcomponent: #fff;
--td-border-level-1-color: var(--td-gray-color-3);
--td-border-level-2-color: var(--td-gray-color-4);
--td-shadow-1: 0 1px 10px rgba(0, 0, 0, 5%), 0 4px 5px rgba(0, 0, 0, 8%), 0 2px 4px -1px rgba(0, 0, 0, 12%);
--td-shadow-2: 0 3px 14px 2px rgba(0, 0, 0, 5%), 0 8px 10px 1px rgba(0, 0, 0, 6%), 0 5px 5px -3px rgba(0, 0, 0, 10%);
--td-shadow-3: 0 6px 30px 5px rgba(0, 0, 0, 5%), 0 16px 24px 2px rgba(0, 0, 0, 4%), 0 8px 10px -5px rgba(0, 0, 0, 8%);
--td-shadow-inset-top: inset 0 0.5px 0 #dcdcdc;
--td-shadow-inset-right: inset 0.5px 0 0 #dcdcdc;
--td-shadow-inset-bottom: inset 0 -0.5px 0 #dcdcdc;
--td-shadow-inset-left: inset -0.5px 0 0 #dcdcdc;
--td-mask-active: rgba(0, 0, 0, 0.6);
--td-mask-disabled: rgba(255, 255, 255, 0.6);
}
:root[theme-mode="dark"] {
--td-brand-color-1: #ff5f4820;
--td-brand-color-2: #6b0000;
--td-brand-color-3: #950000;
--td-brand-color-4: #c30000;
--td-brand-color-5: #ff0000;
--td-brand-color-6: #ff5f48;
--td-brand-color-7: #ff927f;
--td-brand-color-8: #ffb9ac;
--td-brand-color-9: #ffd8d0;
--td-brand-color-10: #fff0ec;
--td-brand-color-light: var(--td-brand-color-1);
--td-brand-color-focus: var(--td-brand-color-2);
--td-brand-color-disabled: var(--td-brand-color-3);
--td-brand-color-hover: var(--td-brand-color-5);
--td-brand-color: var(--td-brand-color-6);
--td-brand-color-active: var(--td-brand-color-7);
--td-warning-color-1: #4f2a1d;
--td-warning-color-2: #582f21;
--td-warning-color-3: #733c23;
--td-warning-color-4: #a75d2b;
--td-warning-color-5: #cf6e2d;
--td-warning-color-6: #dc7633;
--td-warning-color-7: #e8935c;
--td-warning-color-8: #ecbf91;
--td-warning-color-9: #eed7bf;
--td-warning-color-10: #f3e9dc;
--td-error-color-1: #472324;
--td-error-color-2: #5e2a2d;
--td-error-color-3: #703439;
--td-error-color-4: #83383e;
--td-error-color-5: #a03f46;
--td-error-color-6: #c64751;
--td-error-color-7: #de6670;
--td-error-color-8: #ec888e;
--td-error-color-9: #edb1b6;
--td-error-color-10: #eeced0;
--td-success-color-1: #193a2a;
--td-success-color-2: #1a4230;
--td-success-color-3: #17533d;
--td-success-color-4: #0d7a55;
--td-success-color-5: #059465;
--td-success-color-6: #43af8a;
--td-success-color-7: #46bf96;
--td-success-color-8: #80d2b6;
--td-success-color-9: #b4e1d3;
--td-success-color-10: #deede8;
--td-gray-color-1: #f3f3f3;
--td-gray-color-2: #eee;
--td-gray-color-3: #e7e7e7;
--td-gray-color-4: #dcdcdc;
--td-gray-color-5: #c5c5c5;
--td-gray-color-6: #a6a6a6;
--td-gray-color-7: #8b8b8b;
--td-gray-color-8: #777;
--td-gray-color-9: #5e5e5e;
--td-gray-color-10: #4b4b4b;
--td-gray-color-11: #383838;
--td-gray-color-12: #2c2c2c;
--td-gray-color-13: #242424;
--td-gray-color-14: #181818;
--td-bg-color-page: var(--td-gray-color-14);
--td-bg-color-container: var(--td-gray-color-13);
--td-bg-color-container-hover: var(--td-gray-color-12);
--td-bg-color-container-active: var(--td-gray-color-10);
--td-bg-color-container-select: var(--td-gray-color-9);
--td-bg-color-secondarycontainer: var(--td-gray-color-12);
--td-bg-color-secondarycontainer-hover: var(--td-gray-color-11);
--td-bg-color-secondarycontainer-active: var(--td-gray-color-9);
--td-bg-color-component: var(--td-gray-color-11);
--td-bg-color-component-hover: var(--td-gray-color-10);
--td-bg-color-component-active: var(--td-gray-color-9);
--td-bg-color-component-disabled: var(--td-gray-color-12);
--td-component-stroke: var(--td-gray-color-11);
--td-component-border: var(--td-gray-color-9);
--td-font-white-1: rgba(255, 255, 255, 0.9);
--td-font-white-2: rgba(255, 255, 255, 0.55);
--td-font-white-3: rgba(255, 255, 255, 0.35);
--td-font-white-4: rgba(255, 255, 255, 0.22);
--td-font-gray-1: rgba(0, 0, 0, 0.9);
--td-font-gray-2: rgba(0, 0, 0, 0.6);
--td-font-gray-3: rgba(0, 0, 0, 0.4);
--td-font-gray-4: rgba(0, 0, 0, 0.26);
--td-text-color-primary: var(--td-font-white-1);
--td-text-color-secondary: var(--td-font-white-2);
--td-text-color-placeholder: var(--td-font-white-3);
--td-text-color-disabled: var(--td-font-white-4);
--td-text-color-anti: #fff;
--td-text-color-brand: var(--td-brand-color);
--td-text-color-link: var(--td-brand-color);
--td-shadow-1: 0 4px 6px rgba(0, 0, 0, 0.06), 0 1px 10px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.12);
--td-shadow-2: 0 8px 10px rgba(0, 0, 0, 0.12), 0 3px 14px rgba(0, 0, 0, 0.10), 0 5px 5px rgba(0, 0, 0, 0.16);
--td-shadow-3: 0 16px 24px rgba(0, 0, 0, 0.14), 0 6px 30px rgba(0, 0, 0, 0.12), 0 8px 10px rgba(0, 0, 0, 0.20);
--td-shadow-inset-top: inset 0 0.5px 0 #5e5e5e;
--td-shadow-inset-right: inset 0.5px 0 0 #5e5e5e;
--td-shadow-inset-bottom: inset 0 -0.5px 0 #5e5e5e;
--td-shadow-inset-left: inset -0.5px 0 0 #5e5e5e;
--td-table-shadow-color: rgba(0, 0, 0, 55%);
--td-scrollbar-color: rgba(255, 255, 255, 10%);
--td-scrollbar-hover-color: rgba(255, 255, 255, 30%);
--td-scroll-track-color: #333;
--td-bg-color-specialcomponent: transparent;
--td-border-level-1-color: var(--td-gray-color-11);
--td-border-level-2-color: var(--td-gray-color-9);
--td-mask-active: rgba(0, 0, 0, 0.4);
--td-mask-disabled: rgba(0, 0, 0, 0.6);
}
:root {
--td-font-family: pingfang sc, microsoft yahei, arial regular;
--td-font-family-medium: pingfang sc, microsoft yahei, arial medium;
--td-font-size-link-small: 12px;
--td-font-size-link-medium: 14px;
--td-font-size-link-large: 16px;
--td-font-size-mark-small: 12px;
--td-font-size-mark-medium: 14px;
--td-font-size-body-small: 12px;
--td-font-size-body-medium: 14px;
--td-font-size-body-large: 16px;
--td-font-size-title-small: 14px;
--td-font-size-title-medium: 16px;
--td-font-size-title-large: 20px;
--td-font-size-headline-small: 24px;
--td-font-size-headline-medium: 28px;
--td-font-size-headline-large: 36px;
--td-font-size-display-medium: 48px;
--td-font-size-display-large: 64px;
--td-line-height-link-small: 20px;
--td-line-height-link-medium: 22px;
--td-line-height-link-large: 24px;
--td-line-height-mark-small: 20px;
--td-line-height-mark-medium: 22px;
--td-line-height-body-small: 20px;
--td-line-height-body-medium: 22px;
--td-line-height-body-large: 24px;
--td-line-height-title-small: 22px;
--td-line-height-title-medium: 24px;
--td-line-height-title-large: 28px;
--td-line-height-headline-small: 32px;
--td-line-height-headline-medium: 36px;
--td-line-height-headline-large: 44px;
--td-line-height-display-medium: 56px;
--td-line-height-display-large: 72px;
--td-font-link-small: var(--td-font-size-link-small) / var(--td-line-height-link-small) var(--td-font-family);
--td-font-link-medium: var(--td-font-size-link-medium) / var(--td-line-height-link-medium) var(--td-font-family);
--td-font-link-large: var(--td-font-size-link-large) / var(--td-line-height-link-large) var(--td-font-family);
--td-font-mark-small: 600 var(--td-font-size-mark-small) / var(--td-line-height-mark-small) var(--td-font-family);
--td-font-mark-medium: 600 var(--td-font-size-mark-medium) / var(--td-line-height-mark-medium) var(--td-font-family);
--td-font-body-small: var(--td-font-size-body-small) / var(--td-line-height-body-small) var(--td-font-family);
--td-font-body-medium: var(--td-font-size-body-medium) / var(--td-line-height-body-medium) var(--td-font-family);
--td-font-body-large: var(--td-font-size-body-large) / var(--td-line-height-body-large) var(--td-font-family);
--td-font-title-small: 600 var(--td-font-size-title-small) / var(--td-line-height-title-small) var(--td-font-family);
--td-font-title-medium: 600 var(--td-font-size-title-medium) / var(--td-line-height-title-medium) var(--td-font-family);
--td-font-title-large: 600 var(--td-font-size-title-large) / var(--td-line-height-title-large) var(--td-font-family);
--td-font-headline-small: 600 var(--td-font-size-headline-small) / var(--td-line-height-headline-small) var(--td-font-family);
--td-font-headline-medium: 600 var(--td-font-size-headline-medium) / var(--td-line-height-headline-medium) var(--td-font-family);
--td-font-headline-large: 600 var(--td-font-size-headline-large) / var(--td-line-height-headline-large) var(--td-font-family);
--td-font-display-medium: 600 var(--td-font-size-display-medium) / var(--td-line-height-display-medium) var(--td-font-family);
--td-font-display-large: 600 var(--td-font-size-display-large) / var(--td-line-height-display-large) var(--td-font-family);
--td-radius-small: 1px;
--td-radius-default: 2px;
--td-radius-medium: 4px;
--td-radius-large: 6px;
--td-radius-extraLarge: 8px;
--td-radius-round: 999px;
--td-radius-circle: 50%;
--td-size-1: 2px;
--td-size-2: 4px;
--td-size-3: 6px;
--td-size-4: 8px;
--td-size-5: 12px;
--td-size-6: 16px;
--td-size-7: 20px;
--td-size-8: 24px;
--td-size-9: 28px;
--td-size-10: 32px;
--td-size-11: 36px;
--td-size-12: 40px;
--td-size-13: 48px;
--td-size-14: 56px;
--td-size-15: 64px;
--td-size-16: 72px;
--td-comp-size-xxxs: var(--td-size-6);
--td-comp-size-xxs: var(--td-size-7);
--td-comp-size-xs: var(--td-size-8);
--td-comp-size-s: var(--td-size-9);
--td-comp-size-m: var(--td-size-10);
--td-comp-size-l: var(--td-size-11);
--td-comp-size-xl: var(--td-size-12);
--td-comp-size-xxl: var(--td-size-13);
--td-comp-size-xxxl: var(--td-size-14);
--td-comp-size-xxxxl: var(--td-size-15);
--td-comp-size-xxxxxl: var(--td-size-16);
--td-pop-padding-s: var(--td-size-2);
--td-pop-padding-m: var(--td-size-3);
--td-pop-padding-l: var(--td-size-4);
--td-pop-padding-xl: var(--td-size-5);
--td-pop-padding-xxl: var(--td-size-6);
--td-comp-paddingLR-xxs: var(--td-size-1);
--td-comp-paddingLR-xs: var(--td-size-2);
--td-comp-paddingLR-s: var(--td-size-4);
--td-comp-paddingLR-m: var(--td-size-5);
--td-comp-paddingLR-l: var(--td-size-6);
--td-comp-paddingLR-xl: var(--td-size-8);
--td-comp-paddingLR-xxl: var(--td-size-10);
--td-comp-paddingTB-xxs: var(--td-size-1);
--td-comp-paddingTB-xs: var(--td-size-2);
--td-comp-paddingTB-s: var(--td-size-4);
--td-comp-paddingTB-m: var(--td-size-5);
--td-comp-paddingTB-l: var(--td-size-6);
--td-comp-paddingTB-xl: var(--td-size-8);
--td-comp-paddingTB-xxl: var(--td-size-10);
--td-comp-margin-xxs: var(--td-size-1);
--td-comp-margin-xs: var(--td-size-2);
--td-comp-margin-s: var(--td-size-4);
--td-comp-margin-m: var(--td-size-5);
--td-comp-margin-l: var(--td-size-6);
--td-comp-margin-xl: var(--td-size-7);
--td-comp-margin-xxl: var(--td-size-8);
--td-comp-margin-xxxl: var(--td-size-10);
--td-comp-margin-xxxxl: var(--td-size-12);
}