江苏商贸职业学院:接入产教融合学生端(uni-app),替换原前端工程
- 移除原 RuoYi-Vue3 管理端前端全部源码(含 src/public/html/.env 等)
- 引入产教融合实践教学管理平台学生端 H5(uni-app:登录/注册/忘记密码/填报)
Co-Authored-By: Claude <noreply@anthropic.com>
已修改7个文件
已添加21个文件
已删除603个文件
| | |
| | | .DS_Store |
| | | node_modules/ |
| | | dist/ |
| | | # Logs |
| | | logs |
| | | *.log |
| | | npm-debug.log* |
| | | yarn-debug.log* |
| | | yarn-error.log* |
| | | **/*.log |
| | | pnpm-debug.log* |
| | | lerna-debug.log* |
| | | |
| | | tests/**/coverage/ |
| | | tests/e2e/reports |
| | | selenium-debug.log |
| | | node_modules |
| | | .DS_Store |
| | | dist |
| | | *.local |
| | | |
| | | # Editor directories and files |
| | | .idea |
| | | .vscode |
| | | *.suo |
| | | *.ntvs* |
| | | *.njsproj |
| | | *.sln |
| | | *.local |
| | | |
| | | package-lock.json |
| | | yarn.lock |
| | | *.sw? |
| | |
| | | <!DOCTYPE html>
|
| | | <html>
|
| | | <head>
|
| | | <meta charset="utf-8" />
|
| | | <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
| | | <meta name="renderer" content="webkit" />
|
| | | <meta
|
| | | name="viewport"
|
| | | content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
|
| | | />
|
| | | <link rel="icon" href="/DHDCico.ico" />
|
| | | <title>æ¦ç
é¼è¯ç®¡çä¿¡æ¯ç³»ç»</title>
|
| | | <!--[if lt IE 11
|
| | | ]><script>
|
| | | window.location.href = "/html/ie.html";
|
| | | </script><!
|
| | | [endif]-->
|
| | | <style>
|
| | | html,
|
| | | body,
|
| | | #app {
|
| | | height: 100%;
|
| | | margin: 0px;
|
| | | padding: 0px;
|
| | | }
|
| | |
|
| | | .chromeframe {
|
| | | margin: 0.2em 0;
|
| | | background: #ccc;
|
| | | color: #000;
|
| | | padding: 0.2em 0;
|
| | | }
|
| | |
|
| | | #loader-wrapper {
|
| | | position: fixed;
|
| | | top: 0;
|
| | | left: 0;
|
| | | width: 100%;
|
| | | height: 100%;
|
| | | z-index: 999999;
|
| | | }
|
| | |
|
| | | #loader {
|
| | | display: block;
|
| | | position: relative;
|
| | | left: 50%;
|
| | | top: 50%;
|
| | | width: 150px;
|
| | | height: 150px;
|
| | | margin: -75px 0 0 -75px;
|
| | | border-radius: 50%;
|
| | | border: 3px solid transparent;
|
| | | border-top-color: #fff;
|
| | | -webkit-animation: spin 2s linear infinite;
|
| | | -ms-animation: spin 2s linear infinite;
|
| | | -moz-animation: spin 2s linear infinite;
|
| | | -o-animation: spin 2s linear infinite;
|
| | | animation: spin 2s linear infinite;
|
| | | z-index: 1001;
|
| | | }
|
| | |
|
| | | #loader:before {
|
| | | content: "";
|
| | | position: absolute;
|
| | | top: 5px;
|
| | | left: 5px;
|
| | | right: 5px;
|
| | | bottom: 5px;
|
| | | border-radius: 50%;
|
| | | border: 3px solid transparent;
|
| | | border-top-color: #fff;
|
| | | -webkit-animation: spin 3s linear infinite;
|
| | | -moz-animation: spin 3s linear infinite;
|
| | | -o-animation: spin 3s linear infinite;
|
| | | -ms-animation: spin 3s linear infinite;
|
| | | animation: spin 3s linear infinite;
|
| | | }
|
| | |
|
| | | #loader:after {
|
| | | content: "";
|
| | | position: absolute;
|
| | | top: 15px;
|
| | | left: 15px;
|
| | | right: 15px;
|
| | | bottom: 15px;
|
| | | border-radius: 50%;
|
| | | border: 3px solid transparent;
|
| | | border-top-color: #fff;
|
| | | -moz-animation: spin 1.5s linear infinite;
|
| | | -o-animation: spin 1.5s linear infinite;
|
| | | -ms-animation: spin 1.5s linear infinite;
|
| | | -webkit-animation: spin 1.5s linear infinite;
|
| | | animation: spin 1.5s linear infinite;
|
| | | }
|
| | |
|
| | | @-webkit-keyframes spin {
|
| | | 0% {
|
| | | -webkit-transform: rotate(0deg);
|
| | | -ms-transform: rotate(0deg);
|
| | | transform: rotate(0deg);
|
| | | }
|
| | |
|
| | | 100% {
|
| | | -webkit-transform: rotate(360deg);
|
| | | -ms-transform: rotate(360deg);
|
| | | transform: rotate(360deg);
|
| | | }
|
| | | }
|
| | |
|
| | | @keyframes spin {
|
| | | 0% {
|
| | | -webkit-transform: rotate(0deg);
|
| | | -ms-transform: rotate(0deg);
|
| | | transform: rotate(0deg);
|
| | | }
|
| | |
|
| | | 100% {
|
| | | -webkit-transform: rotate(360deg);
|
| | | -ms-transform: rotate(360deg);
|
| | | transform: rotate(360deg);
|
| | | }
|
| | | }
|
| | |
|
| | | #loader-wrapper .loader-section {
|
| | | position: fixed;
|
| | | top: 0;
|
| | | width: 51%;
|
| | | height: 100%;
|
| | | background: #7171c6;
|
| | | z-index: 1000;
|
| | | -webkit-transform: translateX(0);
|
| | | -ms-transform: translateX(0);
|
| | | transform: translateX(0);
|
| | | }
|
| | |
|
| | | #loader-wrapper .loader-section.section-left {
|
| | | left: 0;
|
| | | }
|
| | |
|
| | | #loader-wrapper .loader-section.section-right {
|
| | | right: 0;
|
| | | }
|
| | |
|
| | | .loaded #loader-wrapper .loader-section.section-left {
|
| | | -webkit-transform: translateX(-100%);
|
| | | -ms-transform: translateX(-100%);
|
| | | transform: translateX(-100%);
|
| | | -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
| | | transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
| | | }
|
| | |
|
| | | .loaded #loader-wrapper .loader-section.section-right {
|
| | | -webkit-transform: translateX(100%);
|
| | | -ms-transform: translateX(100%);
|
| | | transform: translateX(100%);
|
| | | -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
| | | transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
| | | }
|
| | |
|
| | | .loaded #loader {
|
| | | opacity: 0;
|
| | | -webkit-transition: all 0.3s ease-out;
|
| | | transition: all 0.3s ease-out;
|
| | | }
|
| | |
|
| | | .loaded #loader-wrapper {
|
| | | visibility: hidden;
|
| | | -webkit-transform: translateY(-100%);
|
| | | -ms-transform: translateY(-100%);
|
| | | transform: translateY(-100%);
|
| | | -webkit-transition: all 0.3s 1s ease-out;
|
| | | transition: all 0.3s 1s ease-out;
|
| | | }
|
| | |
|
| | | .no-js #loader-wrapper {
|
| | | display: none;
|
| | | }
|
| | |
|
| | | .no-js h1 {
|
| | | color: #222222;
|
| | | }
|
| | |
|
| | | #loader-wrapper .load_title {
|
| | | font-family: "Open Sans";
|
| | | color: #fff;
|
| | | font-size: 19px;
|
| | | width: 100%;
|
| | | text-align: center;
|
| | | z-index: 9999999999999;
|
| | | position: absolute;
|
| | | top: 60%;
|
| | | opacity: 1;
|
| | | line-height: 30px;
|
| | | }
|
| | |
|
| | | #loader-wrapper .load_title span {
|
| | | font-weight: normal;
|
| | | font-style: italic;
|
| | | font-size: 13px;
|
| | | color: #fff;
|
| | | opacity: 0.5;
|
| | | }
|
| | | </style>
|
| | | </head>
|
| | |
|
| | | <body>
|
| | | <div id="app">
|
| | | <div id="loader-wrapper">
|
| | | <div id="loader"></div>
|
| | | <div class="loader-section section-left"></div>
|
| | | <div class="loader-section section-right"></div>
|
| | | <div class="load_title">æ£å¨å 载系ç»èµæºï¼è¯·èå¿çå¾
</div>
|
| | | </div>
|
| | | </div>
|
| | | <script type="module" src="/src/main.js"></script>
|
| | | </body>
|
| | | </html>
|
| | | <!DOCTYPE html> |
| | | <html lang="zh-CN"> |
| | | <head> |
| | | <meta charset="UTF-8" /> |
| | | <link rel="icon" type="image/png" href="/static/logo.png" /> |
| | | <script> |
| | | var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') || |
| | | CSS.supports('top: constant(a)')) |
| | | document.write( |
| | | '<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' + |
| | | (coverSupport ? ', viewport-fit=cover' : '') + '" />') |
| | | </script> |
| | | <title>产æèåå®è·µæå¦ç®¡çå¹³å° Â· æ±èåè´¸èä¸å¦é¢</title> |
| | | <!--preload-links--> |
| | | <!--app-context--> |
| | | </head> |
| | | <body> |
| | | <div id="app"><!--app-html--></div> |
| | | <script type="module" src="/src/main.js"></script> |
| | | </body> |
| | | </html> |
| | |
| | | { |
| | | "name": "ruoyi", |
| | | "version": "3.8.9", |
| | | "description": "æ¦ç
é¼è¯ç®¡çä¿¡æ¯ç³»ç»", |
| | | "author": "è¥ä¾", |
| | | "license": "MIT", |
| | | "type": "module", |
| | | "name": "uni-preset-vue", |
| | | "version": "0.0.0", |
| | | "scripts": { |
| | | "dev": "vite", |
| | | "build:prod": "vite build", |
| | | "build:stage": "vite build --mode staging", |
| | | "preview": "vite preview" |
| | | }, |
| | | "repository": { |
| | | "type": "git", |
| | | "url": "https://gitee.com/y_project/RuoYi-Vue.git" |
| | | "dev:custom": "uni -p", |
| | | "dev:h5": "uni", |
| | | "dev:h5:ssr": "uni --ssr", |
| | | "dev:mp-alipay": "uni -p mp-alipay", |
| | | "dev:mp-baidu": "uni -p mp-baidu", |
| | | "dev:mp-jd": "uni -p mp-jd", |
| | | "dev:mp-kuaishou": "uni -p mp-kuaishou", |
| | | "dev:mp-lark": "uni -p mp-lark", |
| | | "dev:mp-qq": "uni -p mp-qq", |
| | | "dev:mp-toutiao": "uni -p mp-toutiao", |
| | | "dev:mp-harmony": "uni -p mp-harmony", |
| | | "dev:mp-weixin": "uni -p mp-weixin", |
| | | "dev:mp-xhs": "uni -p mp-xhs", |
| | | "dev:quickapp-webview": "uni -p quickapp-webview", |
| | | "dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei", |
| | | "dev:quickapp-webview-union": "uni -p quickapp-webview-union", |
| | | "build:custom": "uni build -p", |
| | | "build:h5": "uni build", |
| | | "build:h5:ssr": "uni build --ssr", |
| | | "build:mp-alipay": "uni build -p mp-alipay", |
| | | "build:mp-baidu": "uni build -p mp-baidu", |
| | | "build:mp-jd": "uni build -p mp-jd", |
| | | "build:mp-kuaishou": "uni build -p mp-kuaishou", |
| | | "build:mp-lark": "uni build -p mp-lark", |
| | | "build:mp-qq": "uni build -p mp-qq", |
| | | "build:mp-toutiao": "uni build -p mp-toutiao", |
| | | "build:mp-harmony": "uni build -p mp-harmony", |
| | | "build:mp-weixin": "uni build -p mp-weixin", |
| | | "build:mp-xhs": "uni build -p mp-xhs", |
| | | "build:quickapp-webview": "uni build -p quickapp-webview", |
| | | "build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei", |
| | | "build:quickapp-webview-union": "uni build -p quickapp-webview-union" |
| | | }, |
| | | "dependencies": { |
| | | "@element-plus/icons-vue": "2.3.1", |
| | | "@vue-office/docx": "^1.6.3", |
| | | "@vue-office/excel": "^1.7.14", |
| | | "@vueup/vue-quill": "1.2.0", |
| | | "@vueuse/core": "10.11.0", |
| | | "axios": "0.28.1", |
| | | "clipboard": "2.0.11", |
| | | "dayjs": "^1.11.13", |
| | | "echarts": "5.5.1", |
| | | "element-plus": "2.7.7", |
| | | "file-saver": "2.0.5", |
| | | "fuse.js": "6.6.2", |
| | | "js-beautify": "1.14.11", |
| | | "js-cookie": "3.0.5", |
| | | "jsencrypt": "3.3.2", |
| | | "nprogress": "0.2.0", |
| | | "pinia": "2.1.7", |
| | | "qrcode": "^1.5.4", |
| | | "sortablejs": "^1.15.6", |
| | | "splitpanes": "3.1.5", |
| | | "vue": "3.4.31", |
| | | "vue-cropper": "1.1.1", |
| | | "vue-esign": "^1.1.4", |
| | | "vue-router": "4.4.0", |
| | | "vuedraggable": "4.1.0" |
| | | "@dcloudio/uni-app": "3.0.0-5020420260813003", |
| | | "@dcloudio/uni-app-harmony": "3.0.0-5020420260813003", |
| | | "@dcloudio/uni-app-plus": "3.0.0-5020420260813003", |
| | | "@dcloudio/uni-components": "3.0.0-5020420260813003", |
| | | "@dcloudio/uni-h5": "3.0.0-5020420260813003", |
| | | "@dcloudio/uni-mp-alipay": "3.0.0-5020420260813003", |
| | | "@dcloudio/uni-mp-baidu": "3.0.0-5020420260813003", |
| | | "@dcloudio/uni-mp-harmony": "3.0.0-5020420260813003", |
| | | "@dcloudio/uni-mp-jd": "3.0.0-5020420260813003", |
| | | "@dcloudio/uni-mp-kuaishou": "3.0.0-5020420260813003", |
| | | "@dcloudio/uni-mp-lark": "3.0.0-5020420260813003", |
| | | "@dcloudio/uni-mp-qq": "3.0.0-5020420260813003", |
| | | "@dcloudio/uni-mp-toutiao": "3.0.0-5020420260813003", |
| | | "@dcloudio/uni-mp-weixin": "3.0.0-5020420260813003", |
| | | "@dcloudio/uni-mp-xhs": "3.0.0-5020420260813003", |
| | | "@dcloudio/uni-quickapp-webview": "3.0.0-5020420260813003", |
| | | "vue": "^3.4.21", |
| | | "vue-i18n": "^9.1.9" |
| | | }, |
| | | "devDependencies": { |
| | | "@vitejs/plugin-vue": "5.0.5", |
| | | "sass": "1.77.5", |
| | | "unplugin-auto-import": "0.17.6", |
| | | "unplugin-vue-setup-extend-plus": "1.0.1", |
| | | "vite": "5.3.2", |
| | | "vite-plugin-compression": "0.5.1", |
| | | "vite-plugin-svg-icons": "2.0.1" |
| | | }, |
| | | "overrides": { |
| | | "quill": "2.0.2" |
| | | "@dcloudio/types": "^3.4.8", |
| | | "@dcloudio/uni-automator": "3.0.0-5020420260813003", |
| | | "@dcloudio/uni-cli-shared": "3.0.0-5020420260813003", |
| | | "@dcloudio/uni-stacktracey": "3.0.0-5020420260813003", |
| | | "@dcloudio/vite-plugin-uni": "3.0.0-5020420260813003", |
| | | "@vue/runtime-core": "^3.4.21", |
| | | "rollup": "4.14.3", |
| | | "vite": "5.2.8" |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | /// <reference types='@dcloudio/types' /> |
| | | import 'vue' |
| | | |
| | | declare module '@vue/runtime-core' { |
| | | type Hooks = App.AppInstance & Page.PageInstance; |
| | | |
| | | interface ComponentCustomOptions extends Hooks { |
| | | |
| | | } |
| | | } |
| | |
| | | <template>
|
| | | <router-view />
|
| | | </template>
|
| | |
|
| | | <script setup>
|
| | | import useSettingsStore from '@/store/modules/settings'
|
| | | import { handleThemeStyle } from '@/utils/theme'
|
| | |
|
| | | onMounted(() => {
|
| | | nextTick(() => {
|
| | | // åå§å䏻颿 ·å¼
|
| | | handleThemeStyle(useSettingsStore().theme)
|
| | | })
|
| | | })
|
| | | <script>
|
| | | export default {
|
| | | onLaunch: function () {
|
| | | console.log('App Launch')
|
| | | },
|
| | | onShow: function () {
|
| | | console.log('App Show')
|
| | | },
|
| | | onHide: function () {
|
| | | console.log('App Hide')
|
| | | },
|
| | | }
|
| | | </script>
|
| | |
|
| | | <style>
|
| | | /*æ¯ä¸ªé¡µé¢å
Œ
±css */
|
| | |
|
| | | /* éæ©æ»è½®/䏿弹å±é项ï¼é¿ææ¬å¼ºå¶åè¡çç¥ï¼é²æ¢æ¢è¡æä¹±æ»è½®è¡é«ã
|
| | | 弹屿å¨ç»ä»¶æ å¤ï¼å¿
é¡»ç¨å
¨å±æ ·å¼å
åºï¼è¦çæ§ç弿 ·ä¸ç¬¬ä¸æ¹æ ·å¼æ³¨å
¥åºæ¯ï¼ã */
|
| | | .uni-picker-container .uni-picker-item,
|
| | | .uni-picker-container uni-picker-item {
|
| | | white-space: nowrap !important;
|
| | | overflow: hidden !important;
|
| | | text-overflow: ellipsis !important;
|
| | | }
|
| | | </style>
|
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | /** |
| | | * å¦ç端 APIï¼è´¦å·ä½ç³»ï¼ççº§ç æ³¨å / ç»å½ / æ¾å / æ¹å¯ï¼ |
| | | */ |
| | | import r from '@/utils/request.js' |
| | | |
| | | // è·åå½åå¦çä¿¡æ¯ |
| | | export function getStudentInfo() { |
| | | return r.get('/miniapp/my-info') |
| | | } |
| | | |
| | | // ========== ç«ç¹ææ¡ï¼åå°åå
¸ cj_fill_titleï¼å¿åå¯è¯»ï¼ ========== |
| | | |
| | | // 平尿 é¢/坿 é¢ç â { platformTitle, platformSubtitle, loginSubtitle } |
| | | export function getSiteTitles() { |
| | | return r.get('/miniapp/site-titles') |
| | | } |
| | | |
| | | // ========== è´¦å·ä½ç³»ï¼æ³¨å/ç»å½/æ¾å/æ¹å¯ï¼ ========== |
| | | |
| | | // å¾å½¢éªè¯ç ï¼è¿å { captchaEnabled, uuid, img(base64) } |
| | | export function getCaptcha() { |
| | | return r.get('/miniapp/captcha') |
| | | } |
| | | |
| | | // 注å = ç级ç 建档ï¼å¦å·+å§å+ææºå·+å¯ç +éªè¯ç ï¼ï¼æåå³ç»å½ |
| | | export function accountRegister(data) { |
| | | return r.post('/miniapp/account/register', data) |
| | | } |
| | | |
| | | // è´¦å·å¯ç ç»å½ï¼å¦å·+å§å+å¯ç +éªè¯ç ï¼â { token, mustChange } |
| | | export function accountLogin(data) { |
| | | return r.post('/miniapp/account/login', data) |
| | | } |
| | | |
| | | // å¿è®°å¯ç = ä¸è¦ç´ èªå©éç½®ï¼å¦å·+å§å+æ³¨åæ¶èªå¡«ææºå·+æ°å¯ç +éªè¯ç ï¼ |
| | | export function accountReset(data) { |
| | | return r.post('/miniapp/account/reset', data) |
| | | } |
| | | |
| | | // ä¿®æ¹å¯ç ï¼ç»å½æï¼â { token }ï¼æ° tokenï¼å
¶ä»è®¾å¤ä¼è¯ä½åºï¼ |
| | | export function accountChange(data) { |
| | | return r.post('/miniapp/account/change', data) |
| | | } |
| | | |
| | | // è§£æç级注åç â { classId, className, majorName, collegeName }ï¼æ«ç è¿å
¥æ³¨å页æ¶å±ç¤ºççº§ï¼ |
| | | export function resolveBind(bindCode) { |
| | | return r.get('/miniapp/account/resolve', { bindCode }) |
| | | } |
| | | |
| | | // æ¢çï¼ç»å½æ + æ°ç级ç ï¼â { className }ï¼å·²æäº¤è¡¨åä»å½å±åç级 |
| | | export function accountRebind(bindCode) { |
| | | return r.post('/miniapp/account/rebind', { bindCode }) |
| | | } |
| | | |
| | | // å
¨é¨å¯éççº§ï¼æªæ«ç æ³¨åæ¶æå¨éæ©ï¼â [{classId, label}] |
| | | export function getRegClasses() { |
| | | return r.get('/miniapp/account/classes') |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | /** |
| | | * å¦ç端 APIï¼å¡«è¡¨ä»»å¡ï¼å¨æ schema å¡«æ¥ï¼ |
| | | */ |
| | | import r from '@/utils/request.js' |
| | | |
| | | // æå¯å¡«æ¥/已填æ¥çä»»å¡å表 |
| | | export function getTasks() { |
| | | return r.get('/miniapp/tasks') |
| | | } |
| | | |
| | | // ä»»å¡è¡¨åï¼schema + é¢å¡« + æçè®°å½ |
| | | export function getTaskForm(taskId) { |
| | | return r.get('/miniapp/task/' + taskId) |
| | | } |
| | | |
| | | // ä¿åè稿 |
| | | export function saveTaskDraft(data) { |
| | | return r.post('/miniapp/task/draft', data) |
| | | } |
| | | |
| | | // æ£å¼æäº¤ |
| | | export function submitTask(data) { |
| | | return r.post('/miniapp/task/submit', data) |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | /** |
| | | * å¦ç端å
¨å±é
ç½® |
| | | * éè¿ manifest.json ç H5 ç¯å¢åéæç´æ¥ä¿®æ¹æ¤å¤ |
| | | */ |
| | | export default { |
| | | // å端æ¥å£å°å |
| | | // å¼å H5ï¼æ¬æºå端ï¼: http://localhost:8080 |
| | | // çäº§ï¼æ£å¼åå/æå¡å¨ï¼: http://your-domain.com/dev-api æ https://... |
| | | baseUrl: '/prod-api', // ç产ï¼èµ° nginx 代çï¼åæºï¼é¿å
è·¨åï¼ |
| | | // 微信å°ç¨åº AppIDï¼é
ç½®äº manifest.json mp-weixin.appidï¼ |
| | | appName: '产æèåå®è·µæå¦ç®¡çå¹³å°', |
| | | } |
| | |
| | | import { createApp } from "vue";
|
| | |
|
| | | import Cookies from "js-cookie";
|
| | |
|
| | | import ElementPlus from "element-plus";
|
| | | import "element-plus/dist/index.css";
|
| | | import "element-plus/theme-chalk/dark/css-vars.css";
|
| | | import locale from "element-plus/es/locale/lang/zh-cn";
|
| | |
|
| | | import "@/assets/styles/index.scss"; // global css
|
| | |
|
| | | import App from "./App";
|
| | | import store from "./store";
|
| | | import router from "./router";
|
| | | import directive from "./directive"; // directive
|
| | |
|
| | | // 注åæä»¤
|
| | | import plugins from "./plugins"; // plugins
|
| | | import { download } from "@/utils/request";
|
| | |
|
| | | // svg徿
|
| | | import "virtual:svg-icons-register";
|
| | | import SvgIcon from "@/components/SvgIcon";
|
| | | import elementIcons from "@/components/SvgIcon/svgicon";
|
| | | import "./assets/fonts/font.css";
|
| | |
|
| | | import "./permission"; // permission control
|
| | |
|
| | | import { useDict } from "@/utils/dict";
|
| | | import {
|
| | | parseTime,
|
| | | resetForm,
|
| | | addDateRange,
|
| | | handleTree,
|
| | | selectDictLabel,
|
| | | selectDictLabels,
|
| | | } from "@/utils/ruoyi";
|
| | |
|
| | | // å页ç»ä»¶
|
| | | import Pagination from "@/components/Pagination";
|
| | | // èªå®ä¹è¡¨æ ¼å·¥å
·ç»ä»¶
|
| | | import RightToolbar from "@/components/RightToolbar";
|
| | | // 坿æ¬ç»ä»¶
|
| | | import Editor from "@/components/Editor";
|
| | | // æä»¶ä¸ä¼ ç»ä»¶
|
| | | import FileUpload from "@/components/FileUpload";
|
| | | // å¾çä¸ä¼ ç»ä»¶
|
| | | import ImageUpload from "@/components/ImageUpload";
|
| | | // å¾çé¢è§ç»ä»¶
|
| | | import ImagePreview from "@/components/ImagePreview";
|
| | | // åå
¸æ ç¾ç»ä»¶
|
| | | import DictTag from "@/components/DictTag";
|
| | | // è¡¨æ ¼ç»ä»¶
|
| | | import PIMTable from "@/components/PIMTable/PIMTable.vue";
|
| | |
|
| | | import { getToken } from "@/utils/auth";
|
| | | import {
|
| | | calculateTaxExclusiveTotalPrice,
|
| | | summarizeTable,
|
| | | calculateTaxIncludeTotalPrice,
|
| | | } from "@/utils/summarizeTable.js";
|
| | |
|
| | | const app = createApp(App);
|
| | |
|
| | | // å
¨å±æ¹æ³æè½½
|
| | | app.config.globalProperties.useDict = useDict;
|
| | | app.config.globalProperties.download = download;
|
| | | app.config.globalProperties.parseTime = parseTime;
|
| | | app.config.globalProperties.resetForm = resetForm;
|
| | | app.config.globalProperties.summarizeTable = summarizeTable;
|
| | | app.config.globalProperties.calculateTaxExclusiveTotalPrice =
|
| | | calculateTaxExclusiveTotalPrice;
|
| | | app.config.globalProperties.calculateTaxIncludeTotalPrice =
|
| | | calculateTaxIncludeTotalPrice;
|
| | | app.config.globalProperties.handleTree = handleTree;
|
| | | app.config.globalProperties.addDateRange = addDateRange;
|
| | | app.config.globalProperties.selectDictLabel = selectDictLabel;
|
| | | app.config.globalProperties.selectDictLabels = selectDictLabels;
|
| | | app.config.globalProperties.javaApi = "http://114.132.189.42:9033";
|
| | | app.config.globalProperties.HaveJson = (val) => {
|
| | | return JSON.parse(JSON.stringify(val));
|
| | | };
|
| | | app.config.globalProperties.uploadHeader = {
|
| | | Authorization: "Bearer " + getToken(),
|
| | | };
|
| | |
|
| | | // å
¨å±ç»ä»¶æè½½
|
| | | app.component("DictTag", DictTag);
|
| | | app.component("Pagination", Pagination);
|
| | | app.component("FileUpload", FileUpload);
|
| | | app.component("ImageUpload", ImageUpload);
|
| | | app.component("ImagePreview", ImagePreview);
|
| | | app.component("RightToolbar", RightToolbar);
|
| | | app.component("Editor", Editor);
|
| | | app.component("PIMTable", PIMTable);
|
| | |
|
| | | app.use(router);
|
| | | app.use(store);
|
| | | app.use(plugins);
|
| | | app.use(elementIcons);
|
| | | app.component("svg-icon", SvgIcon);
|
| | |
|
| | | directive(app);
|
| | |
|
| | | // 使ç¨element-plus å¹¶ä¸è®¾ç½®å
¨å±ç大å°
|
| | | app.use(ElementPlus, {
|
| | | locale: locale,
|
| | | // æ¯æ largeãdefaultãsmall
|
| | | size: Cookies.get("size") || "default",
|
| | | });
|
| | | app._context.components.ElDialog.props.closeOnClickModal.default = false;
|
| | |
|
| | | app.mount("#app");
|
| | | createSSRApp
|
| | | } from "vue";
|
| | | import App from "./App.vue";
|
| | | export function createApp() {
|
| | | const app = createSSRApp(App);
|
| | | return {
|
| | | app,
|
| | | };
|
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | { |
| | | "name" : "", |
| | | "appid" : "", |
| | | "description" : "", |
| | | "versionName" : "1.0.0", |
| | | "versionCode" : "100", |
| | | "transformPx" : false, |
| | | /* 5+Appç¹æç¸å
³ */ |
| | | "app-plus" : { |
| | | "usingComponents" : true, |
| | | "nvueStyleCompiler" : "uni-app", |
| | | "compilerVersion" : 3, |
| | | "splashscreen" : { |
| | | "alwaysShowBeforeRender" : true, |
| | | "waiting" : true, |
| | | "autoclose" : true, |
| | | "delay" : 0 |
| | | }, |
| | | /* 模åé
ç½® */ |
| | | "modules" : {}, |
| | | /* åºç¨åå¸ä¿¡æ¯ */ |
| | | "distribute" : { |
| | | /* androidæå
é
ç½® */ |
| | | "android" : { |
| | | "permissions" : [ |
| | | "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>", |
| | | "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>", |
| | | "<uses-permission android:name=\"android.permission.VIBRATE\"/>", |
| | | "<uses-permission android:name=\"android.permission.READ_LOGS\"/>", |
| | | "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>", |
| | | "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>", |
| | | "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>", |
| | | "<uses-permission android:name=\"android.permission.CAMERA\"/>", |
| | | "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>", |
| | | "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>", |
| | | "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>", |
| | | "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>", |
| | | "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>", |
| | | "<uses-feature android:name=\"android.hardware.camera\"/>", |
| | | "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>" |
| | | ] |
| | | }, |
| | | /* iosæå
é
ç½® */ |
| | | "ios" : {}, |
| | | /* SDKé
ç½® */ |
| | | "sdkConfigs" : {} |
| | | } |
| | | }, |
| | | /* å¿«åºç¨ç¹æç¸å
³ */ |
| | | "quickapp" : {}, |
| | | /* å°ç¨åºç¹æç¸å
³ */ |
| | | "mp-weixin" : { |
| | | "appid" : "wxc52c6347b57afd4b", |
| | | "setting" : { |
| | | "urlCheck" : false |
| | | }, |
| | | "usingComponents" : true |
| | | }, |
| | | "mp-alipay" : { |
| | | "usingComponents" : true |
| | | }, |
| | | "mp-baidu" : { |
| | | "usingComponents" : true |
| | | }, |
| | | "mp-toutiao" : { |
| | | "usingComponents" : true |
| | | }, |
| | | "uniStatistics": { |
| | | "enable": false |
| | | }, |
| | | "vueVersion" : "3" |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | { |
| | | "pages": [ |
| | | { |
| | | "path": "pages/index/index", |
| | | "style": { |
| | | "navigationBarTitleText": "产æèåå®è·µæå¦ç®¡çå¹³å°" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/plan/form", |
| | | "style": { |
| | | "navigationBarTitleText": "ä¿¡æ¯å¡«æ¥" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/login/login", |
| | | "style": { |
| | | "navigationBarTitleText": "ç»å½" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/register/register", |
| | | "style": { |
| | | "navigationBarTitleText": "注åè´¦å·" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/forgot/forgot", |
| | | "style": { |
| | | "navigationBarTitleText": "å¿è®°å¯ç " |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/password/change", |
| | | "style": { |
| | | "navigationBarTitleText": "ä¿®æ¹å¯ç " |
| | | } |
| | | } |
| | | ], |
| | | "globalStyle": { |
| | | "navigationBarTextStyle": "white", |
| | | "navigationBarTitleText": "产æèåå®è·µæå¦ç®¡çå¹³å°", |
| | | "navigationBarBackgroundColor": "#1F4788", |
| | | "backgroundColor": "#EEF2F8" |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="page"> |
| | | <view class="brand"> |
| | | <image class="brand-watermark" src="/static/logo-emblem-white.png" mode="aspectFit" /> |
| | | <image class="brand-logo" src="/static/logo-head-white.png" mode="aspectFit" /> |
| | | <view class="brand-title">å¿è®°å¯ç </view> |
| | | <view class="brand-sub">ç¨æ³¨åæ¶å¡«åçâå¦å· + å§å + ææºå·âèªå©éç½®ï¼ä¸ä¸è´è¯·èç³»ç主任</view> |
| | | </view> |
| | | |
| | | <view class="card"> |
| | | <view class="field" :class="{ 'field-err': errors.studentNo }"> |
| | | <text class="label">å¦å·</text> |
| | | <input class="input" v-model="form.studentNo" placeholder="请è¾å
¥å¦å·" placeholder-class="ph" @input="clearErr('studentNo')" /> |
| | | <view v-if="errors.studentNo" class="err-text">{{ errors.studentNo }}</view> |
| | | </view> |
| | | |
| | | <view class="field" :class="{ 'field-err': errors.name }"> |
| | | <text class="label">å§å</text> |
| | | <input class="input" v-model="form.name" placeholder="请è¾å
¥å§å" placeholder-class="ph" @input="clearErr('name')" /> |
| | | <view v-if="errors.name" class="err-text">{{ errors.name }}</view> |
| | | </view> |
| | | |
| | | <view class="field" :class="{ 'field-err': errors.phone }"> |
| | | <text class="label">æ³¨åææºå·</text> |
| | | <input class="input" v-model="form.phone" type="number" maxlength="11" placeholder="æ³¨åæ¶å¡«åçææºå·" placeholder-class="ph" @input="clearErr('phone')" /> |
| | | <view v-if="errors.phone" class="err-text">{{ errors.phone }}</view> |
| | | </view> |
| | | |
| | | <view class="field" :class="{ 'field-err': errors.password }"> |
| | | <text class="label">æ°å¯ç </text> |
| | | <input class="input" v-model="form.password" type="password" placeholder="8-20ä½ï¼é¡»å«åæ¯åæ°å" placeholder-class="ph" @input="clearErr('password')" /> |
| | | <view v-if="errors.password" class="err-text">{{ errors.password }}</view> |
| | | </view> |
| | | |
| | | <view class="field" :class="{ 'field-err': errors.confirm }"> |
| | | <text class="label">确认æ°å¯ç </text> |
| | | <input class="input" v-model="form.confirm" type="password" placeholder="è¯·åæ¬¡è¾å
¥æ°å¯ç " placeholder-class="ph" @input="clearErr('confirm')" /> |
| | | <view v-if="errors.confirm" class="err-text">{{ errors.confirm }}</view> |
| | | </view> |
| | | |
| | | <view class="field" v-if="captchaEnabled" :class="{ 'field-err': errors.code }"> |
| | | <text class="label">éªè¯ç </text> |
| | | <view class="captcha-row"> |
| | | <input class="input captcha-input" v-model="form.code" placeholder="请è¾å
¥éªè¯ç " placeholder-class="ph" maxlength="6" @input="clearErr('code')" /> |
| | | <image class="captcha-img" :src="captchaImg" mode="aspectFit" @click="loadCaptcha" /> |
| | | </view> |
| | | <view v-if="errors.code" class="err-text">{{ errors.code }}</view> |
| | | </view> |
| | | |
| | | <button class="btn btn-primary btn-block" :loading="submitting" :disabled="submitting" @click="doReset"> |
| | | {{ submitting ? 'æäº¤ä¸â¦' : 'éç½®å¯ç ' }} |
| | | </button> |
| | | |
| | | <view class="links"> |
| | | <text class="link" @click="go('/pages/login/login')">è¿åç»å½</text> |
| | | </view> |
| | | <view class="tip">éç½®æåååç»å½ç¶æä¼å¨ææè®¾å¤å¤±æï¼10 åéå
æå¤å°è¯ 3 次</view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import { getCaptcha, accountReset } from '@/api/auth.js' |
| | | import { clearToken } from '@/utils/request.js' |
| | | |
| | | export default { |
| | | data() { |
| | | return { |
| | | submitting: false, |
| | | form: { studentNo: '', name: '', phone: '', password: '', confirm: '', code: '', uuid: '' }, |
| | | errors: {}, |
| | | captchaEnabled: true, |
| | | captchaImg: '', |
| | | } |
| | | }, |
| | | onLoad() { this.loadCaptcha() }, |
| | | methods: { |
| | | async loadCaptcha() { |
| | | try { |
| | | const res = await getCaptcha() |
| | | this.captchaEnabled = res.captchaEnabled !== false |
| | | this.captchaImg = res.img ? 'data:image/jpg;base64,' + res.img : '' |
| | | this.form.uuid = res.uuid || '' |
| | | this.form.code = '' |
| | | } catch (e) { /* ignore */ } |
| | | }, |
| | | clearErr(k) { if (this.errors[k]) delete this.errors[k] }, |
| | | validate() { |
| | | const errs = {} |
| | | if (!this.form.studentNo.trim()) errs.studentNo = '请è¾å
¥å¦å·' |
| | | if (!this.form.name.trim()) errs.name = '请è¾å
¥å§å' |
| | | if (!/^1[3-9]\d{9}$/.test(this.form.phone)) errs.phone = '请è¾å
¥11使æºå·' |
| | | const p = this.form.password |
| | | if (!p) errs.password = '请è¾å
¥æ°å¯ç ' |
| | | else if (!/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d!@#$%^&*_.\-+=?]{8,20}$/.test(p)) errs.password = '8-20ä½ï¼é¡»åæ¶å
å«åæ¯åæ°å' |
| | | else if (p === this.form.studentNo.trim()) errs.password = 'å¯ç ä¸è½ä¸å¦å·ç¸å' |
| | | if (this.form.confirm !== p) errs.confirm = '两次è¾å
¥çå¯ç ä¸ä¸è´' |
| | | if (this.captchaEnabled && !this.form.code.trim()) errs.code = '请è¾å
¥éªè¯ç ' |
| | | this.errors = errs |
| | | return !Object.keys(errs).length |
| | | }, |
| | | async doReset() { |
| | | if (!this.validate()) return |
| | | this.submitting = true |
| | | try { |
| | | await accountReset({ |
| | | studentNo: this.form.studentNo.trim(), |
| | | name: this.form.name.trim(), |
| | | phone: this.form.phone.trim(), |
| | | newPassword: this.form.password, |
| | | code: this.form.code.trim(), |
| | | uuid: this.form.uuid, |
| | | }) |
| | | clearToken() // æ§ä¼è¯å·²è¢«æå¡ç«¯ä½åºï¼æ¬å° token ä¸å¹¶æ¸
æ |
| | | uni.showToast({ title: 'éç½®æåï¼è¯·ç»å½', icon: 'success' }) |
| | | setTimeout(() => uni.redirectTo({ url: '/pages/login/login' }), 800) |
| | | } catch (e) { |
| | | if (this.captchaEnabled) this.loadCaptcha() |
| | | } finally { |
| | | this.submitting = false |
| | | } |
| | | }, |
| | | go(url) { uni.navigateTo({ url }) }, |
| | | }, |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | button { margin: 0; padding: 0; } |
| | | button::after { border: none; } |
| | | |
| | | .page { min-height: 100vh; background: #EEF2F8; padding: 0 0 60rpx; box-sizing: border-box; } |
| | | .brand { |
| | | position: relative; |
| | | padding: 48rpx 44rpx 96rpx; |
| | | overflow: hidden; |
| | | background: |
| | | radial-gradient(420rpx 260rpx at 88% -20%, rgba(120, 165, 225, 0.38), transparent 60%), |
| | | linear-gradient(135deg, #16345e 0%, #1F4788 55%, #2a5aa8 100%); |
| | | border-radius: 0 0 30rpx 30rpx; |
| | | } |
| | | .brand-watermark { position: absolute; right: -36rpx; bottom: -60rpx; width: 280rpx; height: 280rpx; opacity: 0.12; } |
| | | .brand-logo { width: 380rpx; height: 84rpx; display: block; } |
| | | .brand-title { position: relative; margin-top: 26rpx; font-size: 40rpx; font-weight: 700; color: #fff; letter-spacing: 8rpx; } |
| | | .brand-sub { position: relative; margin-top: 10rpx; font-size: 24rpx; color: rgba(255, 255, 255, 0.72); line-height: 1.5; } |
| | | |
| | | .card { position: relative; z-index: 1; background: #fff; margin: -56rpx 24rpx 0; border-radius: 16rpx; padding: 40rpx 30rpx 44rpx; box-shadow: 0 12rpx 36rpx rgba(31, 71, 136, 0.12); } |
| | | |
| | | .field { margin-bottom: 28rpx; } |
| | | .label { display: block; font-size: 27rpx; color: #303133; margin-bottom: 12rpx; } |
| | | .field-err .label { color: #C03; } |
| | | .input { height: 88rpx; line-height: 88rpx; background: #fff; border: 1rpx solid #DCDFE6; border-radius: 8rpx; padding: 0 22rpx; font-size: 29rpx; color: #1F2D3D; } |
| | | .input.err, .field-err .input { border-color: #E5484D; background: #FDF3F3; } |
| | | .err-text { margin-top: 8rpx; color: #E5484D; font-size: 24rpx; } |
| | | .ph { color: #C0C4CC; } |
| | | |
| | | .captcha-row { display: flex; align-items: center; } |
| | | .captcha-input { flex: 1; } |
| | | .captcha-img { width: 200rpx; height: 88rpx; margin-left: 20rpx; border: 1rpx solid #DCDFE6; border-radius: 8rpx; background: #F0F2F6; } |
| | | |
| | | .btn { height: 88rpx; line-height: 88rpx; border-radius: 12rpx; font-size: 30rpx; letter-spacing: 4rpx; text-align: center; box-sizing: border-box; } |
| | | .btn-primary { background: linear-gradient(90deg, #16345e, #1F4788 55%, #2a5aa8); color: #fff; box-shadow: 0 8rpx 20rpx rgba(31, 71, 136, 0.28); } |
| | | .btn-block { width: 100%; } |
| | | .btn[disabled] { opacity: .6; } |
| | | .btn-primary[disabled] { color: #fff; } |
| | | |
| | | .links { display: flex; justify-content: center; margin-top: 26rpx; } |
| | | .link { color: #1F4788; font-size: 26rpx; } |
| | | .tip { margin-top: 20rpx; text-align: center; font-size: 23rpx; color: #B4BAC6; } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="page"> |
| | | <!-- æªç»å½ --> |
| | | <view v-if="!loading && !student"> |
| | | <view class="brand"> |
| | | <image class="brand-watermark" src="/static/logo-emblem-white.png" mode="aspectFit" /> |
| | | <image class="brand-logo" src="/static/logo-head-white.png" mode="aspectFit" /> |
| | | <view class="brand-title">{{ titles.platformTitle }}</view> |
| | | <view class="brand-sub">{{ titles.platformSubtitle }}</view> |
| | | </view> |
| | | <view class="login-card"> |
| | | <button class="btn btn-primary btn-block" @click="goLogin">ç»å½å¹¶å¡«æ¥</button> |
| | | <view class="login-links"> |
| | | <text class="link" @click="goRegister">注åè´¦å·</text> |
| | | </view> |
| | | <view class="login-tip">æ¨èæ«ç主任åçç级äºç»´ç 注åï¼èªå¨å¸¦å
¥ç级ï¼ï¼æ äºç»´ç ä¹å¯æ³¨åæ¶æå¨éç</view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- å·²ç»å½ --> |
| | | <block v-else-if="student"> |
| | | <!-- è´¦å·å¼å¯¼æ¡ --> |
| | | <view v-if="!student.hasPassword" class="notice" @click="goRegister"> |
| | | è´¦å·å®å
¨ï¼ä½ è¿æªè®¾ç½®ç»å½å¯ç ï¼ç¹å»è®¾ç½® â |
| | | </view> |
| | | <view v-else-if="student.mustChangePwd" class="notice notice-warn" @click="goChangePwd"> |
| | | å¯ç 已被管çåéç½®ï¼è¯·ç«å³ä¿®æ¹ä¸ºä½ èªå·±çå¯ç â |
| | | </view> |
| | | |
| | | <view class="profile"> |
| | | <image class="profile-badge" src="/static/logo-emblem-white.png" mode="aspectFit" /> |
| | | <view class="profile-main"> |
| | | <view class="profile-name">{{ student.name }} |
| | | <text class="profile-no">{{ student.studentNo }}</text> |
| | | </view> |
| | | <view class="profile-org">{{ student.collegeName }} · {{ student.className }}</view> |
| | | </view> |
| | | <view class="profile-ops"> |
| | | <view class="p-link" @click="goChangePwd">ä¿®æ¹å¯ç </view> |
| | | <view class="p-link p-link-plain" @click="doLogout">éåºç»å½</view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="section"> |
| | | <view class="section-title">æçå¡«æ¥ä»»å¡</view> |
| | | |
| | | <view v-if="loadingTasks" class="state">å è½½ä¸â¦</view> |
| | | <view v-else-if="!tasks.length" class="state"> |
| | | <text>ææ å¯å¡«æ¥çä»»å¡</text> |
| | | <text class="state-sub">请å
³æ³¨å¦æ ¡åå¸çå¡«æ¥éç¥</text> |
| | | </view> |
| | | <view v-else class="task-list"> |
| | | <view class="task" v-for="t in tasks" :key="t.taskId" @click="openTask(t)"> |
| | | <view class="task-top"> |
| | | <text class="task-name">{{ t.taskName }}</text> |
| | | <text class="task-status" :class="statusClass(t)">{{ statusText(t) }}</text> |
| | | </view> |
| | | <view class="task-meta">å¡«æ¥æ¶é´ï¼{{ t.fillStartDate }} ~ {{ t.fillEndDate }}</view> |
| | | <view class="task-meta" v-if="t.recordStatus === '1'">æäº¤æ¶é´ï¼{{ t.submitTime }}</view> |
| | | <view class="task-op">{{ opText(t) }}</view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="footer">妿é®é¢è¯·èç³»ç级管ç人</view> |
| | | </block> |
| | | <view v-else class="state">å è½½ä¸â¦</view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import { getStudentInfo, resolveBind, accountRebind } from '@/api/auth.js' |
| | | import { getTasks } from '@/api/task.js' |
| | | import { clearToken } from '@/utils/request.js' |
| | | import { getTitles, DEFAULT_TITLES } from '@/utils/site.js' |
| | | |
| | | const BIND_CODE_KEY = '***' |
| | | |
| | | export default { |
| | | data() { |
| | | return { loading: true, student: null, tasks: [], loadingTasks: false, rebinding: false, titles: DEFAULT_TITLES } |
| | | }, |
| | | // ç级äºç»´ç URLï¼/#/?c=注åç ââ æè·åï¼æªç»å½â注åç¨ï¼å·²ç»å½âæ¢ç确认 |
| | | onLoad(options) { |
| | | const c = options && options.c |
| | | if (c) uni.setStorageSync(BIND_CODE_KEY, String(c).trim()) |
| | | this.loadTitles() |
| | | }, |
| | | onShow() { this.checkLogin() }, |
| | | methods: { |
| | | async loadTitles() { |
| | | this.titles = await getTitles() |
| | | }, |
| | | async checkLogin() { |
| | | this.loading = true |
| | | const token = uni.getStorageSync('cj_student_token') |
| | | if (token) { |
| | | try { |
| | | const res = await getStudentInfo() |
| | | this.student = res.data |
| | | this.loadTasks() |
| | | this.maybeRebind() |
| | | } catch (e) { this.student = null } |
| | | } |
| | | this.loading = false |
| | | }, |
| | | /** å·²ç»å½å¦çæ«äºæ°ç级ç â æ¢ç确认ï¼å·²æäº¤è¡¨åä¿çåç级å½å±ï¼ */ |
| | | async maybeRebind() { |
| | | const code = uni.getStorageSync(BIND_CODE_KEY) |
| | | if (!code || !this.student || this.rebinding) return |
| | | this.rebinding = true |
| | | try { |
| | | const r = await resolveBind(code) |
| | | const cls = r.data || {} |
| | | if (cls.className && cls.className !== this.student.className) { |
| | | uni.showModal({ |
| | | title: 'ç³è¯·æ¢ç', |
| | | content: `ä½ å½åå¨"${this.student.className}"ï¼æ«ç ç级为"${cls.collegeName || ''} ${cls.majorName || ''} ${cls.className}"ã确认æ¢çï¼ï¼æ¤åå·²æäº¤ç表åä»å½å±åç级ï¼`, |
| | | confirmText: '确认æ¢ç', |
| | | cancelText: 'çå¨åç', |
| | | success: async (m) => { |
| | | if (m.confirm) { |
| | | try { |
| | | await accountRebind(code) |
| | | uni.showToast({ title: 'æ¢çæå', icon: 'success' }) |
| | | this.checkLogin() |
| | | } catch (e) { /* request.js 已弹é */ } |
| | | } |
| | | }, |
| | | }) |
| | | } |
| | | } catch (e) { /* ç æ æåå¿½ç¥ */ } |
| | | uni.removeStorageSync(BIND_CODE_KEY) |
| | | this.rebinding = false |
| | | }, |
| | | async loadTasks() { |
| | | this.loadingTasks = true |
| | | try { |
| | | const res = await getTasks() |
| | | this.tasks = (res.data && res.data.tasks) || [] |
| | | } catch (e) { this.tasks = [] } |
| | | this.loadingTasks = false |
| | | }, |
| | | goLogin() { uni.navigateTo({ url: '/pages/login/login' }) }, |
| | | goRegister() { uni.navigateTo({ url: '/pages/register/register' }) }, |
| | | goChangePwd() { uni.navigateTo({ url: '/pages/password/change' }) }, |
| | | openTask(t) { uni.navigateTo({ url: '/pages/plan/form?taskId=' + t.taskId }) }, |
| | | doLogout() { |
| | | clearToken() |
| | | // å
¼å®¹æ¸
çæ§ç H5 âå设å¤ç âç¼åï¼è¯¥æºå¶å·²ä¸çº¿ï¼ |
| | | uni.removeStorageSync('cj_h5_dev_code') |
| | | this.student = null |
| | | this.tasks = [] |
| | | uni.reLaunch({ url: '/pages/login/login' }) |
| | | }, |
| | | statusText(t) { |
| | | if (t.recordStatus === '1') return 'å·²æäº¤' |
| | | if (t.recordStatus === '0') return 'è稿' |
| | | return t.open ? 'å¾
å¡«å' : 'æªå¼æ¾' |
| | | }, |
| | | statusClass(t) { |
| | | if (t.recordStatus === '1') return 'st-ok' |
| | | if (t.recordStatus === '0') return 'st-draft' |
| | | return t.open ? 'st-todo' : 'st-off' |
| | | }, |
| | | opText(t) { |
| | | if (t.recordStatus === '1') return 'æ¥çå¡«æ¥å
容' |
| | | if (t.recordStatus === '0') return 'ç»§ç»å¡«å' |
| | | return 'ç«å³å¡«å' |
| | | }, |
| | | }, |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | /* æé®é»è®¤æ ·å¼æ¶æï¼é¿å
è¾¹æ¡/é´è·å¼å¸¸ */ |
| | | button { margin: 0; padding: 0; background: none; } |
| | | button::after { border: none; } |
| | | .btn { height: 92rpx; line-height: 92rpx; border-radius: 12rpx; font-size: 30rpx; letter-spacing: 4rpx; text-align: center; box-sizing: border-box; } |
| | | .btn-primary { background: linear-gradient(90deg, #16345e, #1F4788 55%, #2a5aa8); color: #fff; box-shadow: 0 8rpx 20rpx rgba(31, 71, 136, 0.28); } |
| | | .btn-block { width: 100%; } |
| | | .btn[disabled] { opacity: .6; color: #fff; } |
| | | |
| | | .page { min-height: 100vh; background: #EEF2F8; padding: 0 0 60rpx; box-sizing: border-box; } |
| | | |
| | | /* åç头å¹
*/ |
| | | .brand { |
| | | position: relative; |
| | | padding: 56rpx 44rpx 110rpx; |
| | | overflow: hidden; |
| | | background: |
| | | radial-gradient(420rpx 260rpx at 88% -20%, rgba(120, 165, 225, 0.38), transparent 60%), |
| | | linear-gradient(135deg, #16345e 0%, #1F4788 55%, #2a5aa8 100%); |
| | | border-radius: 0 0 30rpx 30rpx; |
| | | } |
| | | .brand-watermark { position: absolute; right: -36rpx; bottom: -60rpx; width: 300rpx; height: 300rpx; opacity: 0.12; } |
| | | .brand-logo { width: 380rpx; height: 84rpx; display: block; } |
| | | .brand-title { position: relative; margin-top: 26rpx; font-size: 38rpx; font-weight: 700; color: #fff; letter-spacing: 4rpx; } |
| | | .brand-sub { position: relative; margin-top: 10rpx; font-size: 24rpx; color: rgba(255, 255, 255, 0.72); line-height: 1.5; } |
| | | |
| | | /* æªç»å½å¡ç */ |
| | | .login-card { |
| | | position: relative; |
| | | z-index: 1; |
| | | background: #fff; |
| | | margin: -64rpx 24rpx 0; |
| | | border-radius: 16rpx; |
| | | padding: 44rpx 34rpx; |
| | | box-shadow: 0 12rpx 36rpx rgba(31, 71, 136, 0.12); |
| | | } |
| | | .login-links { margin-top: 30rpx; display: flex; justify-content: center; gap: 60rpx; } |
| | | .login-tip { margin-top: 24rpx; font-size: 24rpx; color: #B4BAC6; text-align: center; line-height: 1.5; } |
| | | |
| | | /* è´¦å·å¼å¯¼æ¡ */ |
| | | .notice { background: #E8EFF9; color: #1F4788; border: 1rpx solid #D5E2F5; border-radius: 12rpx; padding: 20rpx 24rpx; font-size: 25rpx; margin: 24rpx 24rpx 0; } |
| | | .notice-warn { background: #FBF4E2; color: #B36A00; border-color: #F2E2BC; } |
| | | |
| | | /* 个人æ¡ï¼æ·±èåçå¡ï¼ */ |
| | | .profile { |
| | | margin: 24rpx 24rpx 0; |
| | | background: |
| | | radial-gradient(320rpx 200rpx at 92% -30%, rgba(120, 165, 225, 0.4), transparent 60%), |
| | | linear-gradient(135deg, #16345e 0%, #1F4788 60%, #2a5aa8 100%); |
| | | border-radius: 16rpx; |
| | | padding: 30rpx 28rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | box-shadow: 0 10rpx 30rpx rgba(31, 71, 136, 0.25); |
| | | } |
| | | .profile-badge { width: 88rpx; height: 88rpx; border-radius: 50%; background: rgba(255, 255, 255, 0.14); padding: 12rpx; flex-shrink: 0; box-sizing: content-box; } |
| | | .profile-main { flex: 1; margin-left: 24rpx; min-width: 0; } |
| | | .profile-name { font-size: 32rpx; font-weight: 600; color: #fff; } |
| | | .profile-no { margin-left: 16rpx; font-size: 24rpx; color: rgba(255, 255, 255, 0.72); font-weight: 400; } |
| | | .profile-org { margin-top: 10rpx; font-size: 24rpx; color: rgba(255, 255, 255, 0.72); } |
| | | .profile-ops { display: flex; flex-direction: column; align-items: flex-end; gap: 14rpx; flex-shrink: 0; } |
| | | .p-link { color: #fff; font-size: 24rpx; padding: 8rpx 22rpx; border: 1rpx solid rgba(255, 255, 255, 0.55); border-radius: 999rpx; } |
| | | .p-link-plain { color: rgba(255, 255, 255, 0.78); border-color: rgba(255, 255, 255, 0.3); } |
| | | .link { color: #1F4788; font-size: 26rpx; } |
| | | .link-plain { color: #7C8698; } |
| | | |
| | | /* å表 */ |
| | | .section { margin: 28rpx 24rpx 0; } |
| | | .section-title { font-size: 30rpx; font-weight: 600; color: #1F2D3D; padding: 0 4rpx 14rpx; position: relative; padding-left: 20rpx; } |
| | | .section-title::before { content: ''; position: absolute; left: 0; top: 8rpx; bottom: 22rpx; width: 8rpx; border-radius: 4rpx; background: linear-gradient(#1F4788, #2a5aa8); } |
| | | .state { background: #fff; border-radius: 12rpx; padding: 90rpx 0; text-align: center; color: #8A94A6; font-size: 27rpx; border: 1rpx solid #EBEDF1; } |
| | | .state-sub { display: block; margin-top: 10rpx; font-size: 24rpx; color: #B4BAC6; } |
| | | .task { background: #fff; border: 1rpx solid #EBEDF1; border-radius: 16rpx; padding: 26rpx 26rpx; margin-bottom: 18rpx; box-shadow: 0 4rpx 14rpx rgba(31, 71, 136, 0.05); } |
| | | .task:active { background: #F7F9FC; } |
| | | .task-top { display: flex; justify-content: space-between; align-items: flex-start; } |
| | | .task-name { flex: 1; font-size: 29rpx; font-weight: 600; color: #1F2D3D; line-height: 1.4; } |
| | | .task-status { margin-left: 16rpx; font-size: 22rpx; padding: 2rpx 12rpx; border-radius: 6rpx; flex-shrink: 0; } |
| | | .st-ok { color: #1D9D5F; background: #EAF7F0; } |
| | | .st-draft { color: #B36A00; background: #FBF4E2; } |
| | | .st-todo { color: #1F4788; background: #E8EFF9; } |
| | | .st-off { color: #7C8698; background: #F0F2F6; } |
| | | .task-meta { margin-top: 12rpx; font-size: 24rpx; color: #7C8698; } |
| | | .task-op { margin-top: 16rpx; padding-top: 14rpx; border-top: 1rpx solid #F2F3F6; color: #1F4788; font-weight: 600; font-size: 27rpx; } |
| | | .footer { text-align: center; margin-top: 36rpx; font-size: 22rpx; color: #B4BAC6; } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="page"> |
| | | <view class="brand"> |
| | | <image class="brand-watermark" src="/static/logo-emblem-white.png" mode="aspectFit" /> |
| | | <image class="brand-logo" src="/static/logo-head-white.png" mode="aspectFit" /> |
| | | <view class="brand-title">ç» å½</view> |
| | | <view class="brand-sub">{{ titles.loginSubtitle }}</view> |
| | | </view> |
| | | |
| | | <view class="card"> |
| | | <view class="field" :class="{ 'field-err': errors.studentNo }"> |
| | | <text class="label">å¦å·</text> |
| | | <input class="input" v-model="form.studentNo" placeholder="请è¾å
¥å¦å·" placeholder-class="ph" @input="clearErr('studentNo')" /> |
| | | <view v-if="errors.studentNo" class="err-text">{{ errors.studentNo }}</view> |
| | | </view> |
| | | |
| | | <view class="field" :class="{ 'field-err': errors.name }"> |
| | | <text class="label">å§å</text> |
| | | <input class="input" v-model="form.name" placeholder="请è¾å
¥å§å" placeholder-class="ph" @input="clearErr('name')" /> |
| | | <view v-if="errors.name" class="err-text">{{ errors.name }}</view> |
| | | </view> |
| | | |
| | | <view class="field" :class="{ 'field-err': errors.password }"> |
| | | <text class="label">å¯ç </text> |
| | | <input class="input" v-model="form.password" type="password" placeholder="请è¾å
¥å¯ç " placeholder-class="ph" @input="clearErr('password')" /> |
| | | <view v-if="errors.password" class="err-text">{{ errors.password }}</view> |
| | | </view> |
| | | |
| | | <view class="field" v-if="captchaEnabled" :class="{ 'field-err': errors.code }"> |
| | | <text class="label">éªè¯ç </text> |
| | | <view class="captcha-row"> |
| | | <input class="input captcha-input" v-model="form.code" placeholder="请è¾å
¥éªè¯ç " placeholder-class="ph" maxlength="6" @input="clearErr('code')" /> |
| | | <image class="captcha-img" :src="captchaImg" mode="aspectFit" @click="loadCaptcha" /> |
| | | </view> |
| | | <view v-if="errors.code" class="err-text">{{ errors.code }}</view> |
| | | </view> |
| | | |
| | | <button class="btn btn-primary btn-block" :loading="submitting" :disabled="submitting" @click="doLogin"> |
| | | {{ submitting ? 'ç»å½ä¸â¦' : 'ç» å½' }} |
| | | </button> |
| | | |
| | | <view class="links"> |
| | | <text class="link" @click="go('/pages/register/register')">注åè´¦å·</text> |
| | | <text class="link" @click="go('/pages/forgot/forgot')">å¿è®°å¯ç ï¼</text> |
| | | </view> |
| | | <view class="tip">馿¬¡ä½¿ç¨è¯·æ«æç主任åçç级äºç»´ç 注å</view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import { getCaptcha, accountLogin } from '@/api/auth.js' |
| | | import { setToken } from '@/utils/request.js' |
| | | import { getTitles, DEFAULT_TITLES } from '@/utils/site.js' |
| | | |
| | | export default { |
| | | data() { |
| | | return { |
| | | submitting: false, |
| | | form: { studentNo: '', name: '', password: '', code: '', uuid: '' }, |
| | | errors: {}, |
| | | captchaEnabled: true, |
| | | captchaImg: '', |
| | | titles: DEFAULT_TITLES, |
| | | } |
| | | }, |
| | | onLoad() { |
| | | this.loadCaptcha() |
| | | this.loadTitles() |
| | | }, |
| | | methods: { |
| | | async loadTitles() { |
| | | this.titles = await getTitles() |
| | | }, |
| | | async loadCaptcha() { |
| | | try { |
| | | const res = await getCaptcha() |
| | | this.captchaEnabled = res.captchaEnabled !== false |
| | | this.captchaImg = res.img ? 'data:image/jpg;base64,' + res.img : '' |
| | | this.form.uuid = res.uuid || '' |
| | | this.form.code = '' |
| | | } catch (e) { /* request.js 已弹 toast */ } |
| | | }, |
| | | clearErr(k) { if (this.errors[k]) delete this.errors[k] }, |
| | | validate() { |
| | | const errs = {} |
| | | if (!this.form.studentNo.trim()) errs.studentNo = '请è¾å
¥å¦å·' |
| | | if (!this.form.name.trim()) errs.name = '请è¾å
¥å§å' |
| | | if (!this.form.password) errs.password = '请è¾å
¥å¯ç ' |
| | | if (this.captchaEnabled && !this.form.code.trim()) errs.code = '请è¾å
¥éªè¯ç ' |
| | | this.errors = errs |
| | | return !Object.keys(errs).length |
| | | }, |
| | | async doLogin() { |
| | | if (!this.validate()) return |
| | | this.submitting = true |
| | | try { |
| | | const res = await accountLogin({ |
| | | studentNo: this.form.studentNo.trim(), |
| | | name: this.form.name.trim(), |
| | | password: this.form.password, |
| | | code: this.form.code.trim(), |
| | | uuid: this.form.uuid, |
| | | }) |
| | | setToken(res.data.token) |
| | | if (res.data.mustChange) { |
| | | uni.showToast({ title: '管çåå·²éç½®ä½ çå¯ç ï¼è¯·ä¿®æ¹', icon: 'none', duration: 2000 }) |
| | | setTimeout(() => uni.redirectTo({ url: '/pages/password/change' }), 800) |
| | | } else { |
| | | uni.showToast({ title: 'ç»å½æå', icon: 'success' }) |
| | | setTimeout(() => uni.reLaunch({ url: '/pages/index/index' }), 500) |
| | | } |
| | | } catch (e) { |
| | | if (this.captchaEnabled) this.loadCaptcha() // éªè¯ç 䏿¬¡æ§ï¼å¤±è´¥åå·æ° |
| | | } finally { |
| | | this.submitting = false |
| | | } |
| | | }, |
| | | go(url) { uni.navigateTo({ url }) }, |
| | | }, |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | button { margin: 0; padding: 0; } |
| | | button::after { border: none; } |
| | | |
| | | .page { min-height: 100vh; background: #EEF2F8; padding: 0 0 60rpx; box-sizing: border-box; } |
| | | .brand { |
| | | position: relative; |
| | | padding: 48rpx 44rpx 96rpx; |
| | | overflow: hidden; |
| | | background: |
| | | radial-gradient(420rpx 260rpx at 88% -20%, rgba(120, 165, 225, 0.38), transparent 60%), |
| | | linear-gradient(135deg, #16345e 0%, #1F4788 55%, #2a5aa8 100%); |
| | | border-radius: 0 0 30rpx 30rpx; |
| | | } |
| | | .brand-watermark { position: absolute; right: -36rpx; bottom: -60rpx; width: 280rpx; height: 280rpx; opacity: 0.12; } |
| | | .brand-logo { width: 380rpx; height: 84rpx; display: block; } |
| | | .brand-title { position: relative; margin-top: 26rpx; font-size: 40rpx; font-weight: 700; color: #fff; letter-spacing: 10rpx; } |
| | | .brand-sub { position: relative; margin-top: 10rpx; font-size: 24rpx; color: rgba(255, 255, 255, 0.72); letter-spacing: 2rpx; } |
| | | |
| | | .card { position: relative; z-index: 1; background: #fff; margin: -56rpx 24rpx 0; border-radius: 16rpx; padding: 40rpx 30rpx 44rpx; box-shadow: 0 12rpx 36rpx rgba(31, 71, 136, 0.12); } |
| | | |
| | | .field { margin-bottom: 28rpx; } |
| | | .label { display: block; font-size: 27rpx; color: #303133; margin-bottom: 12rpx; } |
| | | .field-err .label { color: #C03; } |
| | | .input { height: 88rpx; line-height: 88rpx; background: #fff; border: 1rpx solid #DCDFE6; border-radius: 8rpx; padding: 0 22rpx; font-size: 29rpx; color: #1F2D3D; } |
| | | .input.err, .field-err .input { border-color: #E5484D; background: #FDF3F3; } |
| | | .err-text { margin-top: 8rpx; color: #E5484D; font-size: 24rpx; } |
| | | .ph { color: #C0C4CC; } |
| | | |
| | | .captcha-row { display: flex; align-items: center; } |
| | | .captcha-input { flex: 1; } |
| | | .captcha-img { width: 200rpx; height: 88rpx; margin-left: 20rpx; border: 1rpx solid #DCDFE6; border-radius: 8rpx; background: #F0F2F6; } |
| | | |
| | | .btn { height: 88rpx; line-height: 88rpx; border-radius: 12rpx; font-size: 30rpx; letter-spacing: 6rpx; text-align: center; box-sizing: border-box; } |
| | | .btn-primary { background: linear-gradient(90deg, #16345e, #1F4788 55%, #2a5aa8); color: #fff; box-shadow: 0 8rpx 20rpx rgba(31, 71, 136, 0.28); } |
| | | .btn-block { width: 100%; } |
| | | .btn[disabled] { opacity: .6; } |
| | | .btn-primary[disabled] { color: #fff; } |
| | | |
| | | .links { display: flex; justify-content: space-between; margin-top: 26rpx; } |
| | | .link { color: #1F4788; font-size: 26rpx; } |
| | | .tip { margin-top: 20rpx; text-align: center; font-size: 23rpx; color: #B4BAC6; } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="page"> |
| | | <view class="brand"> |
| | | <image class="brand-watermark" src="/static/logo-emblem-white.png" mode="aspectFit" /> |
| | | <image class="brand-logo" src="/static/logo-head-white.png" mode="aspectFit" /> |
| | | <view class="brand-title">ä¿®æ¹å¯ç </view> |
| | | <view class="brand-sub">ä¿®æ¹æååï¼å
¶ä»è®¾å¤ä¸çç»å½ä¼è¢«éåº</view> |
| | | </view> |
| | | |
| | | <view class="card"> |
| | | <view class="field" :class="{ 'field-err': errors.old }"> |
| | | <text class="label">åå¯ç </text> |
| | | <input class="input" v-model="form.old" type="password" placeholder="请è¾å
¥å½åå¯ç " placeholder-class="ph" @input="clearErr('old')" /> |
| | | <view v-if="errors.old" class="err-text">{{ errors.old }}</view> |
| | | </view> |
| | | |
| | | <view class="field" :class="{ 'field-err': errors.password }"> |
| | | <text class="label">æ°å¯ç </text> |
| | | <input class="input" v-model="form.password" type="password" placeholder="8-20ä½ï¼é¡»å«åæ¯åæ°å" placeholder-class="ph" @input="clearErr('password')" /> |
| | | <view v-if="errors.password" class="err-text">{{ errors.password }}</view> |
| | | </view> |
| | | |
| | | <view class="field" :class="{ 'field-err': errors.confirm }"> |
| | | <text class="label">确认æ°å¯ç </text> |
| | | <input class="input" v-model="form.confirm" type="password" placeholder="è¯·åæ¬¡è¾å
¥æ°å¯ç " placeholder-class="ph" @input="clearErr('confirm')" /> |
| | | <view v-if="errors.confirm" class="err-text">{{ errors.confirm }}</view> |
| | | </view> |
| | | |
| | | <button class="btn btn-primary btn-block" :loading="submitting" :disabled="submitting" @click="doChange"> |
| | | {{ submitting ? 'æäº¤ä¸â¦' : '确认修æ¹' }} |
| | | </button> |
| | | <view class="tip">å¿è®°åå¯ç ï¼å¯éåºåå¨ç»å½é¡µç¨âå¿è®°å¯ç âèªå©éç½®</view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import { accountChange } from '@/api/auth.js' |
| | | import { setToken } from '@/utils/request.js' |
| | | |
| | | export default { |
| | | data() { |
| | | return { submitting: false, form: { old: '', password: '', confirm: '' }, errors: {} } |
| | | }, |
| | | methods: { |
| | | clearErr(k) { if (this.errors[k]) delete this.errors[k] }, |
| | | validate() { |
| | | const errs = {} |
| | | if (!this.form.old) errs.old = '请è¾å
¥åå¯ç ' |
| | | const p = this.form.password |
| | | if (!p) errs.password = '请è¾å
¥æ°å¯ç ' |
| | | else if (!/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d!@#$%^&*_.\-+=?]{8,20}$/.test(p)) errs.password = '8-20ä½ï¼é¡»åæ¶å
å«åæ¯åæ°å' |
| | | else if (p === this.form.old) errs.password = 'æ°å¯ç ä¸è½ä¸åå¯ç ç¸å' |
| | | if (this.form.confirm !== p) errs.confirm = '两次è¾å
¥çå¯ç ä¸ä¸è´' |
| | | this.errors = errs |
| | | return !Object.keys(errs).length |
| | | }, |
| | | async doChange() { |
| | | if (!this.validate()) return |
| | | this.submitting = true |
| | | try { |
| | | const res = await accountChange({ oldPassword: this.form.old, newPassword: this.form.password }) |
| | | if (res.data && res.data.token) setToken(res.data.token) // å½å设å¤ä¿æç»å½ï¼æ¢åæ° tokenï¼ |
| | | uni.showToast({ title: 'å¯ç ä¿®æ¹æå', icon: 'success' }) |
| | | setTimeout(() => uni.reLaunch({ url: '/pages/index/index' }), 700) |
| | | } catch (e) { /* request.js 已弹 toast */ } |
| | | this.submitting = false |
| | | }, |
| | | }, |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | button { margin: 0; padding: 0; } |
| | | button::after { border: none; } |
| | | |
| | | .page { min-height: 100vh; background: #EEF2F8; padding: 0 0 60rpx; box-sizing: border-box; } |
| | | .brand { |
| | | position: relative; |
| | | padding: 48rpx 44rpx 96rpx; |
| | | overflow: hidden; |
| | | background: |
| | | radial-gradient(420rpx 260rpx at 88% -20%, rgba(120, 165, 225, 0.38), transparent 60%), |
| | | linear-gradient(135deg, #16345e 0%, #1F4788 55%, #2a5aa8 100%); |
| | | border-radius: 0 0 30rpx 30rpx; |
| | | } |
| | | .brand-watermark { position: absolute; right: -36rpx; bottom: -60rpx; width: 280rpx; height: 280rpx; opacity: 0.12; } |
| | | .brand-logo { width: 380rpx; height: 84rpx; display: block; } |
| | | .brand-title { position: relative; margin-top: 26rpx; font-size: 40rpx; font-weight: 700; color: #fff; letter-spacing: 8rpx; } |
| | | .brand-sub { position: relative; margin-top: 10rpx; font-size: 24rpx; color: rgba(255, 255, 255, 0.72); line-height: 1.5; } |
| | | |
| | | .card { position: relative; z-index: 1; background: #fff; margin: -56rpx 24rpx 0; border-radius: 16rpx; padding: 40rpx 30rpx 44rpx; box-shadow: 0 12rpx 36rpx rgba(31, 71, 136, 0.12); } |
| | | |
| | | .field { margin-bottom: 28rpx; } |
| | | .label { display: block; font-size: 27rpx; color: #303133; margin-bottom: 12rpx; } |
| | | .field-err .label { color: #C03; } |
| | | .input { height: 88rpx; line-height: 88rpx; background: #fff; border: 1rpx solid #DCDFE6; border-radius: 8rpx; padding: 0 22rpx; font-size: 29rpx; color: #1F2D3D; } |
| | | .input.err, .field-err .input { border-color: #E5484D; background: #FDF3F3; } |
| | | .err-text { margin-top: 8rpx; color: #E5484D; font-size: 24rpx; } |
| | | .ph { color: #C0C4CC; } |
| | | |
| | | .btn { height: 88rpx; line-height: 88rpx; border-radius: 12rpx; font-size: 30rpx; letter-spacing: 4rpx; text-align: center; box-sizing: border-box; } |
| | | .btn-primary { background: linear-gradient(90deg, #16345e, #1F4788 55%, #2a5aa8); color: #fff; box-shadow: 0 8rpx 20rpx rgba(31, 71, 136, 0.28); } |
| | | .btn-block { width: 100%; } |
| | | .btn[disabled] { opacity: .6; } |
| | | .btn-primary[disabled] { color: #fff; } |
| | | .tip { margin-top: 26rpx; text-align: center; font-size: 23rpx; color: #B4BAC6; } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="form-page"> |
| | | <view v-if="loading" class="state">å è½½ä¸â¦</view> |
| | | |
| | | <!-- æªç»å®/ä¸å¨å¡«æ¥èå´ï¼ä¸å±ç¤ºè¡¨å --> |
| | | <view v-else-if="errorMsg" class="err-state"> |
| | | <view class="err-icon">!</view> |
| | | <view class="err-title">ææ¶æ æ³å¡«æ¥</view> |
| | | <view class="err-msg">{{ errorMsg }}</view> |
| | | <button class="btn btn-primary err-btn" @click="goHome">å»å®æèº«ä»½ç»å® / è¿åé¦é¡µ</button> |
| | | </view> |
| | | |
| | | <block v-else> |
| | | <!-- ä»»å¡ä¿¡æ¯æ¡ --> |
| | | <view class="task-head"> |
| | | <view class="th-top"> |
| | | <text class="th-title">{{ meta.taskName || 'ä¿¡æ¯å¡«æ¥' }}</text> |
| | | <text class="th-tag" :class="meta.readonly ? 'ok' : (meta.recordStatus === '0' ? 'draft' : 'todo')">{{ statusText() }}</text> |
| | | </view> |
| | | <view class="th-meta">{{ meta.termText }}</view> |
| | | <view class="th-meta">å¡«æ¥æ¶é´ï¼{{ meta.fillStartDate }} ~ {{ meta.fillEndDate }}</view> |
| | | <view v-if="meta.readonly" class="th-note">å·²æ£å¼æäº¤ï¼å
容åªè¯»ï¼å¦éä¿®æ¹è¯·èç³»ç级管ç人éåã</view> |
| | | </view> |
| | | |
| | | <!-- æ ¡éªéè¯¯æ±æ» --> |
| | | <view v-if="Object.keys(errors).length && !meta.readonly" class="err-banner"> |
| | | è¿æ {{ Object.keys(errors).length }} 项填åæè¯¯ï¼è¯·æçº¢è²æç¤ºä¿®æ¹åæäº¤ |
| | | </view> |
| | | |
| | | <!-- åç»è¡¨å --> |
| | | <view class="group" v-for="(g, gi) in groups" :key="gi"> |
| | | <view class="group-title">{{ groupNo(gi) }}ã{{ g.groupName }}</view> |
| | | |
| | | <view class="field" v-for="f in g.fields" :key="f.code" :class="{ 'field-err': errors[f.code] }"> |
| | | <view class="f-label"><text v-if="needReq(f)" class="req">*</text>{{ f.label }}</view> |
| | | |
| | | <block v-if="isReadonly(f)"> |
| | | <view class="f-ro" v-if="form[f.code] !== undefined && form[f.code] !== ''">{{ form[f.code] }}</view> |
| | | <view class="f-ro faint" v-else>{{ autoHint(f) }}</view> |
| | | </block> |
| | | <block v-else> |
| | | <!-- 䏿 --> |
| | | <picker v-if="f.type === 'select' && optLabels(f).length" :range="optLabels(f)" :value="curIndex(f)" @change="e => onSelect(f, e)"> |
| | | <view class="f-ctrl" :class="{ err: errors[f.code] }"> |
| | | <text :class="form[f.code] ? 'on' : 'off'">{{ curLabel(f) }}</text> |
| | | <text class="caret">â¾</text> |
| | | </view> |
| | | </picker> |
| | | <!-- åé --> |
| | | <view v-else-if="f.type === 'radio' && optLabels(f).length" class="f-radios" :class="{ err: errors[f.code] }"> |
| | | <view v-for="o in f.options" :key="o.value" class="f-radio" :class="{ on: String(form[f.code]) === String(o.value) }" @click="pickRadio(f, o)">{{ o.label }}</view> |
| | | </view> |
| | | <!-- æ¥æ --> |
| | | <picker v-else-if="f.type === 'date'" mode="date" :value="form[f.code] || ''" @change="e => { form[f.code] = e.detail.value; clearErr(f.code) }"> |
| | | <view class="f-ctrl" :class="{ err: errors[f.code] }"> |
| | | <text :class="form[f.code] ? 'on' : 'off'">{{ form[f.code] || 'è¯·éæ©æ¥æ' }}</text> |
| | | <text class="caret">â¾</text> |
| | | </view> |
| | | </picker> |
| | | <!-- ç份 --> |
| | | <picker v-else-if="f.type === 'province'" :range="provinceNames" :value="provinceIndex(f)" @change="e => onProvinceChange(f, e)"> |
| | | <view class="f-ctrl" :class="{ err: errors[f.code] }"> |
| | | <text :class="form[f.code] ? 'on' : 'off'">{{ form[f.code] || 'è¯·éæ©ç份' }}</text> |
| | | <text class="caret">â¾</text> |
| | | </view> |
| | | </picker> |
| | | <!-- åå¸ï¼éç份äºçº§èå¨ï¼æªéç份åä¸å¯ç¹ --> |
| | | <picker v-else-if="f.type === 'city'" :range="cityList(f)" :value="cityIndex(f)" |
| | | :disabled="!cityList(f).length" @change="e => onCityChange(f, e)"> |
| | | <view class="f-ctrl" :class="{ err: errors[f.code], off: !cityList(f).length }"> |
| | | <text :class="form[f.code] ? 'on' : 'off'">{{ form[f.code] || (cityList(f).length ? 'è¯·éæ©åå¸' : '请å
éæ©ç份') }}</text> |
| | | <text class="caret">â¾</text> |
| | | </view> |
| | | </picker> |
| | | <!-- å°åºï¼ä¸ä¸ªå段éè¿çéçåå¸ï¼åå¹¶å为ãç/å¸ã --> |
| | | <view v-else-if="f.type === 'region'" class="f-region"> |
| | | <picker class="f-region-p" :range="provinceNames" :value="regionProvinceIndex(f)" @change="e => onRegionProvince(f, e)"> |
| | | <view class="f-ctrl" :class="{ err: errors[f.code] }"> |
| | | <text :class="regionProvince(f) ? 'on' : 'off'">{{ regionProvince(f) || 'è¯·éæ©ç份' }}</text> |
| | | <text class="caret">â¾</text> |
| | | </view> |
| | | </picker> |
| | | <picker class="f-region-p" :range="regionCityList(f)" :value="regionCityIndex(f)" :disabled="!regionCityList(f).length" |
| | | @change="e => onRegionCity(f, e)"> |
| | | <view class="f-ctrl" :class="{ err: errors[f.code], off: !regionCityList(f).length }"> |
| | | <text :class="regionCity(f) ? 'on' : 'off'">{{ regionCity(f) || (regionCityList(f).length ? 'è¯·éæ©åå¸' : '请å
éæ©ç份') }}</text> |
| | | <text class="caret">â¾</text> |
| | | </view> |
| | | </picker> |
| | | </view> |
| | | <!-- æä»¶ä¸ä¼ ï¼å¾ç/PDF/Office ææç»ä¸å
¥å£ï¼ --> |
| | | <view v-else-if="f.type === 'file'" class="f-files"> |
| | | <view class="f-up-grid" v-if="imgList(f).length"> |
| | | <view class="f-up-cell" v-for="(p, i) in imgList(f)" :key="'i' + i"> |
| | | <image class="f-up-img" :src="fullUrl(p)" mode="aspectFill" @click="previewImg(f, i)" /> |
| | | <view v-if="!meta.readonly" class="f-up-del" @click.stop="removeFileByPath(f, p)">Ã</view> |
| | | </view> |
| | | </view> |
| | | <view class="f-file-row" v-for="(p, i) in docList(f)" :key="'d' + i"> |
| | | <text class="f-file-name" @click="openFile(p)">{{ fileNameOf(p) }}</text> |
| | | <text v-if="!meta.readonly" class="f-file-del" @click="removeFileByPath(f, p)">å é¤</text> |
| | | </view> |
| | | <view v-if="!meta.readonly && fileList(f).length < maxCount(f)" class="f-file-add" :class="{ err: errors[f.code] }" @click="chooseFiles(f)"> |
| | | ï¼ ä¸ä¼ æä»¶ï¼{{ fileList(f).length }}/{{ maxCount(f) }}ï¼å个â¤{{ MAX_UPLOAD_MB }}Mï¼ |
| | | </view> |
| | | </view> |
| | | <!-- å¤è¡ --> |
| | | <textarea v-else-if="f.type === 'textarea'" v-model="form[f.code]" :placeholder="f.placeholder || '请è¾å
¥'" :maxlength="f.maxLen || 500" class="f-area" :class="{ err: errors[f.code] }" auto-height @input="clearErr(f.code)" /> |
| | | <!-- ææ¬/æ°å/çµè¯/ä¿¡ç¨ä»£ç --> |
| | | <input v-else class="f-input" :class="{ err: errors[f.code] }" :type="f.type === 'number' ? 'number' : 'text'" |
| | | v-model="form[f.code]" :placeholder="f.placeholder || '请è¾å
¥'" :maxlength="f.maxLen || 200" |
| | | @input="e => onInput(e, f)" /> |
| | | </block> |
| | | |
| | | <view v-if="errors[f.code]" class="err-text">{{ errors[f.code] }}</view> |
| | | <view v-else-if="f.help" class="f-help">{{ f.help }}</view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- åºé¨æä½ --> |
| | | <view v-if="!meta.readonly" class="bottom-bar"> |
| | | <button class="btn btn-draft" @click="doSave('draft')">æåè稿</button> |
| | | <button class="btn btn-submit" :loading="submitting" @click="doSave('submit')">æ£å¼æäº¤</button> |
| | | </view> |
| | | </block> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import { getTaskForm, saveTaskDraft, submitTask } from '@/api/task.js' |
| | | import { PROVINCES, citiesOf } from '@/utils/region.js' |
| | | import { uploadFile } from '@/utils/request.js' |
| | | import config from '@/config.js' |
| | | |
| | | export default { |
| | | data() { |
| | | // å个ä¸ä¼ æä»¶å¤§å°ä¸éï¼MBï¼ï¼ä¸æå¡ç«¯ spring.servlet.multipart.max-file-size=10MB ä¿æä¸è´ |
| | | return { MAX_UPLOAD_MB: 10, taskId: null, loading: true, submitting: false, meta: {}, groups: [], form: {}, errors: {}, errorMsg: '', provinceNames: PROVINCES } |
| | | }, |
| | | onLoad(options) { |
| | | this.taskId = options.taskId |
| | | this.load() |
| | | }, |
| | | methods: { |
| | | async load() { |
| | | this.loading = true |
| | | this.errorMsg = '' |
| | | try { |
| | | const res = await getTaskForm(this.taskId) |
| | | const d = res.data |
| | | this.meta = d |
| | | this.groups = this.normalizeGroups((d.schema && d.schema.groups) || []) |
| | | this.form = d.form || {} |
| | | if (!d.open && !d.readonly && !this.meta.recordStatus) { |
| | | this.errorMsg = '该填æ¥ä»»å¡å½åä¸å¨å¼æ¾æå
ï¼æä½ çèº«ä»½å°æªå®æç»å®/ç级ç»è®°ã' |
| | | } |
| | | } catch (e) { |
| | | this.errorMsg = (e && e.msg) || 'æ æ³æå¼è¯¥å¡«æ¥ä»»å¡ï¼è¯·å
å®æèº«ä»½ç»å®/ç级ç»è®°ã' |
| | | } |
| | | this.loading = false |
| | | }, |
| | | goHome() { |
| | | uni.reLaunch({ url: '/pages/index/index' }) |
| | | }, |
| | | statusText() { |
| | | if (this.meta.readonly) return 'å·²æäº¤' |
| | | if (this.meta.recordStatus === '0') return 'è稿' |
| | | return this.meta.open ? 'å¾
å¡«å' : 'å·²æªæ¢' |
| | | }, |
| | | groupNo(gi) { |
| | | const map = ['ä¸', 'äº', 'ä¸', 'å', 'äº', 'å
'] |
| | | return map[gi] || (gi + 1) |
| | | }, |
| | | needReq(f) { return f.required === true }, |
| | | isReadonly(f) { return f.readonly === true || !!f.auto }, |
| | | autoHint(f) { |
| | | if (f.auto === 'roster') return 'ï¼å¦çæ¡£æ¡èªå¨å¸¦åºï¼' |
| | | if (f.auto === 'task') return 'ï¼ä»»å¡èªå¨å¸¦åºï¼' |
| | | if (f.auto === 'server') return 'ï¼ç±ç³»ç»å¤å®ï¼' |
| | | return 'ï¼åªè¯»ï¼' |
| | | }, |
| | | optList(f) { return Array.isArray(f.options) ? f.options : [] }, |
| | | optLabels(f) { return this.optList(f).map(o => o.label) }, |
| | | curIndex(f) { |
| | | const idx = this.optList(f).findIndex(o => String(o.value) === String(this.form[f.code])) |
| | | return idx < 0 ? 0 : idx |
| | | }, |
| | | curLabel(f) { |
| | | const idx = this.curIndex(f) |
| | | const opts = this.optList(f) |
| | | return this.form[f.code] && opts[idx] ? opts[idx].label : 'è¯·éæ©' |
| | | }, |
| | | onSelect(f, e) { |
| | | const o = this.optList(f)[Number(e.detail.value)] |
| | | this.form[f.code] = o ? o.value : '' |
| | | // è¯¥ä¸æè¥æ£è¢«åå¸å½ä½ç份è卿ºï¼æ¢å¼ååæ ·æ¸
æè·éçåå¸ |
| | | this.allFields().forEach(x => { |
| | | if (x.type === 'city' && this.linkCodeOf(x) === f.code) this.form[x.code] = '' |
| | | }) |
| | | this.clearErr(f.code) |
| | | }, |
| | | pickRadio(f, o) { this.form[f.code] = o.value; this.clearErr(f.code) }, |
| | | |
| | | // ---- ç份 / åå¸ï¼äºçº§èå¨ï¼---- |
| | | /** |
| | | * å
¼å®¹æ©æ schema å¿«ç
§ï¼çå¸å段æ¾ç»ä¸æ 为已åºå¼ç region ç±»åã |
| | | * ä»»å¡ schema æ¯å叿¶è½åºçå¿«ç
§ï¼æ¹å¨å段类åä¸ä¼å溯æ§ä»»å¡ï¼æ
卿¤è¿åã |
| | | */ |
| | | normalizeGroups(groups) { |
| | | const all = [] |
| | | for (const g of groups) for (const f of (g.fields || [])) { |
| | | // ä»
å
ç½®åæ®µï¼ç¼ç åºå®ä¸º province / cityï¼æä» region æå¼ï¼ |
| | | // ç¨æ·æ°å¢çãå°åºãåæ®µæ¬èº«å°±æ¯ regionï¼å¿
é¡»åæ ·ä¿ç |
| | | if (f.type === 'region' && (f.code === 'province' || f.code === 'city')) { |
| | | f.type = f.code === 'city' ? 'city' : 'province' |
| | | } |
| | | // ãå¾çä¸ä¼ ã已并å
¥ãæä»¶ä¸ä¼ ãï¼èä»»å¡ schema å¿«ç
§ææ¤è¿ç§»ï¼ä¿çå 6 å¼ é»è®¤ä¸é |
| | | if (f.type === 'image') { |
| | | if (!f.maxCount) f.maxCount = 6 |
| | | f.type = 'file' |
| | | } |
| | | // åå²çæ¬æãåå¸ãåæå¸¦è¡æ¿åºåæ è®°çæ®é䏿ï¼é项æ¯å
¨éåå¸å表ï¼ã |
| | | // å级为 city ç±»åèµ°çå¸è卿¸²æï¼å¿½ç¥å
¶å¿«ç
§éçéæé项ã |
| | | if (f.type === 'select' && f.optionSource === 'region' && (f.code === 'city' || f.linkTo)) { |
| | | f.type = 'city' |
| | | } |
| | | all.push(f) |
| | | } |
| | | const first = all.find(f => this.isProvinceSource(f)) |
| | | all.forEach(f => { if (f.type === 'city' && !f.linkTo && first) f.linkTo = first.code }) |
| | | return groups |
| | | }, |
| | | /** |
| | | * åæ®µè½å¦ä½ä¸ºåå¸çç份è卿ºï¼å
ç½®ãç份ãç±»åï¼ |
| | | * æé项éåæå¯è¯å«ç份åçã䏿/åéãåæ®µï¼ç®¡ç端æå¨é
ç份䏿çåºæ¯ï¼ã |
| | | */ |
| | | isProvinceSource(f) { |
| | | if (f.type === 'province') return true |
| | | if (f.type === 'select' || f.type === 'radio') { |
| | | return this.optList(f).some(o => citiesOf(String(o.value || o.label || '')).length > 0) |
| | | } |
| | | return false |
| | | }, |
| | | allFields() { |
| | | const out = [] |
| | | for (const g of this.groups) for (const f of (g.fields || [])) out.push(f) |
| | | return out |
| | | }, |
| | | /** åå¸å段èå¨çç份忮µ codeï¼ä¼å
åé
ç½®ç linkToï¼å¦åéå表åé第ä¸ä¸ªç份æºå段 */ |
| | | linkCodeOf(f) { |
| | | const all = this.allFields() |
| | | if (f.linkTo && all.some(x => this.isProvinceSource(x) && x.code === f.linkTo)) return f.linkTo |
| | | const first = all.find(x => this.isProvinceSource(x)) |
| | | return first ? first.code : '' |
| | | }, |
| | | /** å½åç份ä¸å¯éçå°å¸ï¼ç份æªéæ¶ä¸ºç©ºæ°ç»ï¼picker éä¹ç¦ç¨ */ |
| | | cityList(f) { |
| | | return citiesOf(this.form[this.linkCodeOf(f)]) |
| | | }, |
| | | provinceIndex(f) { |
| | | const i = PROVINCES.indexOf(this.form[f.code]) |
| | | return i < 0 ? 0 : i |
| | | }, |
| | | cityIndex(f) { |
| | | const i = this.cityList(f).indexOf(this.form[f.code]) |
| | | return i < 0 ? 0 : i |
| | | }, |
| | | onProvinceChange(f, e) { |
| | | this.form[f.code] = PROVINCES[Number(e.detail.value)] || '' |
| | | // æ¢ç忏
空è·éå®çåå¸ï¼é¿å
çä¸ãæ±è + æ·±å³ãè¿ç±»çç¾ç»å |
| | | this.allFields().forEach(x => { |
| | | if (x.type === 'city' && this.linkCodeOf(x) === f.code) this.form[x.code] = '' |
| | | }) |
| | | this.clearErr(f.code) |
| | | }, |
| | | onCityChange(f, e) { |
| | | const list = this.cityList(f) |
| | | this.form[f.code] = list[Number(e.detail.value)] || '' |
| | | this.clearErr(f.code) |
| | | }, |
| | | |
| | | // ---- å°åºï¼ç+å¸åå¹¶æä¸ä¸ªå段ï¼åãç/å¸ãï¼---- |
| | | /** æåºå·²åççå¸ï¼æ å¼ææ ¼å¼å¼å¸¸æ¶è¿å空ï¼ä¸æé */ |
| | | regionParts(f) { |
| | | const v = this.form[f.code] == null ? '' : String(this.form[f.code]) |
| | | const i = v.indexOf('/') |
| | | return i < 0 ? { p: v, c: '' } : { p: v.slice(0, i), c: v.slice(i + 1) } |
| | | }, |
| | | regionProvince(f) { return this.regionParts(f).p }, |
| | | regionCity(f) { return this.regionParts(f).c }, |
| | | regionCityList(f) { return citiesOf(this.regionProvince(f)) }, |
| | | regionProvinceIndex(f) { |
| | | const i = PROVINCES.indexOf(this.regionProvince(f)) |
| | | return i < 0 ? 0 : i |
| | | }, |
| | | regionCityIndex(f) { |
| | | const i = this.regionCityList(f).indexOf(this.regionCity(f)) |
| | | return i < 0 ? 0 : i |
| | | }, |
| | | onRegionProvince(f, e) { |
| | | // æ¢ç峿¸
空已éå¸ï¼é¿å
çä¸ãæ±èç/æ·±å³å¸ãè¿ç±»çç¾å¼ |
| | | this.form[f.code] = PROVINCES[Number(e.detail.value)] || '' |
| | | this.clearErr(f.code) |
| | | }, |
| | | onRegionCity(f, e) { |
| | | const p = this.regionProvince(f) |
| | | const c = this.regionCityList(f)[Number(e.detail.value)] || '' |
| | | this.form[f.code] = c ? (p + '/' + c) : p |
| | | this.clearErr(f.code) |
| | | }, |
| | | onInput(e, f) { |
| | | let val = e.detail.value |
| | | if (f.type === 'creditCode') val = val.toUpperCase() |
| | | this.form[f.code] = val |
| | | this.clearErr(f.code) |
| | | }, |
| | | |
| | | // ---- å¾ç / æä»¶ä¸ä¼ ---- |
| | | /** ååæ®µçå·²ä¸ä¼ è·¯å¾å表ï¼å
¼å®¹ãæ°ç»ãä¸åå²ãéå·æ¼æ¥å符串ã两ç§åå¨ */ |
| | | fileList(f) { |
| | | const v = this.form[f.code] |
| | | if (Array.isArray(v)) return v.filter(Boolean) |
| | | if (typeof v === 'string' && v.trim()) return v.split(',').map(s => s.trim()).filter(Boolean) |
| | | return [] |
| | | }, |
| | | maxCount(f) { |
| | | const n = Number(f.maxCount) |
| | | if (n > 0) return Math.min(n, 9) |
| | | return 3 |
| | | }, |
| | | /** æ¯å¦å¾çæä»¶ï¼ææ©å±å夿ï¼ç¨äºç¼©ç¥å¾é¢è§ï¼ */ |
| | | isImg(p) { return /\.(bmp|gif|jpe?g|png)$/i.test(String(p || '')) }, |
| | | imgList(f) { return this.fileList(f).filter(p => this.isImg(p)) }, |
| | | docList(f) { return this.fileList(f).filter(p => !this.isImg(p)) }, |
| | | /** ç¸å¯¹è·¯å¾ â å¯è®¿é® URLï¼/profile/** å·²æ¾è¡ï¼èµ°åä¸åååç¼ï¼ */ |
| | | fullUrl(p) { |
| | | if (!p) return '' |
| | | if (/^https?:\/\//.test(p)) return p |
| | | return config.baseUrl + p |
| | | }, |
| | | fileNameOf(p) { |
| | | const s = String(p || '') |
| | | return s.slice(s.lastIndexOf('/') + 1) |
| | | }, |
| | | /** è¶
åº 10MB çæä»¶å
è¡æ¦æªï¼ç»åºæç¡®æç¤º */ |
| | | filterOversize(items, sizeOf) { |
| | | const keep = [] |
| | | let skipped = 0 |
| | | for (const it of items) { |
| | | if (sizeOf(it) > this.MAX_UPLOAD_MB * 1024 * 1024) skipped++ |
| | | else keep.push(it) |
| | | } |
| | | if (skipped) uni.showToast({ title: 'æä»¶ä¸å¾å¤§äº' + this.MAX_UPLOAD_MB + 'Mï¼å·²è·³è¿ ' + skipped + ' 个è¶
大æä»¶', icon: 'none', duration: 2600 }) |
| | | return keep |
| | | }, |
| | | chooseImages(f) { |
| | | const left = this.maxCount(f) - this.fileList(f).length |
| | | if (left <= 0) { uni.showToast({ title: 'æå¤ä¸ä¼ ' + this.maxCount(f) + ' 个æä»¶', icon: 'none' }); return } |
| | | uni.chooseImage({ |
| | | count: Math.min(left, 9), |
| | | sizeType: ['compressed'], |
| | | success: (res) => { |
| | | const paths = res.tempFilePaths || [] |
| | | const metas = res.tempFiles || [] |
| | | // é¨åå¹³å° tempFiles 带 sizeï¼å¯å端é¢å¤ï¼æ¿ä¸å°æ¶ç±æå¡ç«¯ 10MB ä¸éå
åº |
| | | if (metas.length && metas.every(m => typeof m.size === 'number')) { |
| | | const zipped = paths.map((p, i) => ({ p, size: (metas[i] || {}).size || 0 })) |
| | | this.uploadPaths(f, this.filterOversize(zipped, x => x.size).map(x => x.p)) |
| | | } else { |
| | | this.uploadPaths(f, paths) |
| | | } |
| | | } |
| | | }) |
| | | }, |
| | | chooseFiles(f) { |
| | | const left = this.maxCount(f) - this.fileList(f).length |
| | | if (left <= 0) { uni.showToast({ title: 'æå¤ä¸ä¼ ' + this.maxCount(f) + ' 个æä»¶', icon: 'none' }); return } |
| | | // #ifdef H5 |
| | | const input = document.createElement('input') |
| | | input.type = 'file' |
| | | input.multiple = true |
| | | input.accept = '.bmp,.gif,.jpg,.jpeg,.png,.pdf,.doc,.docx,.xls,.xlsx,.txt' |
| | | input.onchange = () => { |
| | | const all = Array.from(input.files || []) |
| | | const files = this.filterOversize(all, x => x.size).slice(0, left) |
| | | this.uploadFiles(f, files) |
| | | } |
| | | input.click() |
| | | // #endif |
| | | // #ifndef H5 |
| | | // å°ç¨åºç«¯æä»¥é徿¹å¼ä¸ä¼ ï¼uni.uploadFile 对æ¬å°ä¸´æ¶è·¯å¾éç¨ï¼ |
| | | this.chooseImages(f) |
| | | // #endif |
| | | }, |
| | | /** H5ï¼ç´æ¥ä¸ä¼ File 对象 */ |
| | | uploadFiles(f, files) { |
| | | this.doUpload(f, files.map(file => ({ file }))) |
| | | }, |
| | | /** å°ç¨åº/H5ï¼æä¸´æ¶è·¯å¾é个ä¸ä¼ */ |
| | | uploadPaths(f, paths) { |
| | | this.doUpload(f, paths.map(p => ({ filePath: p }))) |
| | | }, |
| | | async doUpload(f, jobs) { |
| | | if (!jobs.length) return |
| | | uni.showLoading({ title: 'ä¸ä¼ ä¸â¦', mask: true }) |
| | | const names = this.fileList(f).slice() |
| | | try { |
| | | for (const job of jobs) { |
| | | if (names.length >= this.maxCount(f)) break |
| | | const res = await uploadFile(job) |
| | | if (res && res.fileName) names.push(res.fileName) |
| | | else uni.showToast({ title: 'ä¸ä¼ ååºå¼å¸¸ï¼æªè¿åæä»¶å°å', icon: 'none' }) |
| | | } |
| | | this.form[f.code] = names |
| | | this.clearErr(f.code) |
| | | } catch (e) { /* uploadFile 已弹é */ } |
| | | uni.hideLoading() |
| | | }, |
| | | removeFile(f, i) { |
| | | const names = this.fileList(f).slice() |
| | | names.splice(i, 1) |
| | | this.form[f.code] = names |
| | | }, |
| | | removeFileByPath(f, p) { |
| | | const i = this.fileList(f).indexOf(p) |
| | | if (i >= 0) this.removeFile(f, i) |
| | | }, |
| | | previewImg(f, i) { |
| | | const urls = this.imgList(f).map(p => this.fullUrl(p)) |
| | | if (urls.length) uni.previewImage({ urls, current: i }) |
| | | }, |
| | | openFile(p) { |
| | | const url = this.fullUrl(p) |
| | | // #ifdef H5 |
| | | window.open(url) |
| | | // #endif |
| | | // #ifndef H5 |
| | | uni.downloadFile({ |
| | | url, |
| | | success: (res) => uni.openDocument({ |
| | | filePath: res.tempFilePath, |
| | | fail: () => uni.showToast({ title: 'æä¸æ¯æé¢è§è¯¥æä»¶ç±»å', icon: 'none' }) |
| | | }) |
| | | }) |
| | | // #endif |
| | | }, |
| | | clearErr(code) { |
| | | if (this.errors[code]) { |
| | | const o = {} |
| | | Object.keys(this.errors).forEach(k => { if (k !== code) o[k] = this.errors[k] }) |
| | | this.errors = o |
| | | } |
| | | }, |
| | | doSave(mode) { |
| | | if (mode === 'submit') { |
| | | const errs = this.validate() |
| | | this.errors = errs |
| | | const keys = Object.keys(errs) |
| | | if (keys.length) { |
| | | // åªå¼¹ç¬¬ä¸æ¡ï¼ä½å¸¦ä¸æ¡æ°ï¼é¿å
ç¨æ·ä»¥ä¸ºåªæè¿ä¸å¤é |
| | | const title = errs[keys[0]] + (keys.length > 1 ? `ï¼å
± ${keys.length} 项å¾
ä¿®æ¹ï¼` : '') |
| | | uni.showToast({ title, icon: 'none', duration: 2600 }) |
| | | return |
| | | } |
| | | } |
| | | this.submitting = true |
| | | const api = mode === 'submit' ? submitTask : saveTaskDraft |
| | | api({ taskId: this.taskId, form: this.form }) |
| | | .then(() => { |
| | | uni.showToast({ title: mode === 'submit' ? 'æäº¤æå' : 'è稿已ä¿å', icon: 'success' }) |
| | | setTimeout(() => uni.navigateBack(), 600) |
| | | }) |
| | | .catch(() => {}) |
| | | .finally(() => { this.submitting = false }) |
| | | }, |
| | | validate() { |
| | | const errs = {} |
| | | for (const g of this.groups) { |
| | | for (const f of g.fields || []) { |
| | | if (this.isReadonly(f)) continue |
| | | const code = f.code |
| | | // å¾ç/æä»¶åæ®µï¼æå表å¤å¿
å¡«ï¼ä¸èµ°ææ¬/æ°åç±»æ ¡éª |
| | | if (f.type === 'image' || f.type === 'file') { |
| | | if (f.required === true && this.fileList(f).length === 0) errs[code] = '请ä¸ä¼ ã' + f.label + 'ã' |
| | | continue |
| | | } |
| | | const v = (this.form[code] === undefined || this.form[code] === null) ? '' : String(this.form[code]).trim() |
| | | if (f.required === true && !v) { errs[code] = '请填åã' + f.label + 'ã'; continue } |
| | | if (!v) continue |
| | | if (f.type === 'province' && PROVINCES.indexOf(v) < 0) { errs[code] = 'è¯·éæ©ææçã' + f.label + 'ã'; continue } |
| | | if (f.type === 'city') { |
| | | if (!this.linkCodeOf(f)) { errs[code] = '请å
éæ©ç份ï¼åéã' + f.label + 'ã'; continue } |
| | | const cities = this.cityList(f) |
| | | if (cities.length && cities.indexOf(v) < 0) { errs[code] = 'ã' + f.label + 'ã䏿éç份ä¸ç¬¦ï¼è¯·éæ°éæ©'; continue } |
| | | } |
| | | if (f.type === 'region') { |
| | | const p = this.regionProvince(f), c = this.regionCity(f) |
| | | if (!p || !c) { errs[code] = 'è¯·éæ©å®æ´çã' + f.label + 'ãï¼çåå¸ï¼'; continue } |
| | | if (citiesOf(p).indexOf(c) < 0) { errs[code] = 'ã' + f.label + 'ãæéåå¸ä¸å±äºè¯¥ç份ï¼è¯·éæ°éæ©'; continue } |
| | | } |
| | | if (f.type === 'phone' && !/^1[3-9]\d{9}$/.test(v)) { errs[code] = 'ã' + f.label + 'ã请填å 11 ä½å¤§éææºå·'; continue } |
| | | if (f.type === 'creditCode' && !/^[0-9A-HJ-NPQRTUWXY]{18}$/.test(v)) { errs[code] = 'ã' + f.label + 'ã须为 18 ä½ç»ä¸ç¤¾ä¼ä¿¡ç¨ä»£ç '; continue } |
| | | if (f.type === 'number') { |
| | | const n = Number(v) |
| | | if (Number.isNaN(n)) { errs[code] = 'ã' + f.label + 'ã请è¾å
¥æ°å'; continue } |
| | | if (f.min !== undefined && f.min !== null && n < Number(f.min)) { errs[code] = 'ã' + f.label + 'ãä¸è½å°äº ' + f.min; continue } |
| | | if (f.max !== undefined && f.max !== null && n > Number(f.max)) { errs[code] = 'ã' + f.label + 'ãä¸è½å¤§äº ' + f.max; continue } |
| | | } |
| | | if (f.minLen && v.length < Number(f.minLen)) { errs[code] = 'ã' + f.label + 'ãè³å° ' + f.minLen + ' 个å'; continue } |
| | | if (f.maxLen && v.length > Number(f.maxLen)) { errs[code] = 'ã' + f.label + 'ãæå¤ ' + f.maxLen + ' 个å'; continue } |
| | | } |
| | | } |
| | | return errs |
| | | }, |
| | | }, |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | button { margin: 0; padding: 0; } |
| | | button::after { border: none; } |
| | | |
| | | .form-page { background: #EEF2F8; min-height: 100vh; padding: 24rpx 24rpx 200rpx; box-sizing: border-box; } |
| | | .state { padding-top: 200rpx; text-align: center; color: #8A94A6; } |
| | | |
| | | /* æ æ³å¡«æ¥æç¤º */ |
| | | .err-state { padding: 160rpx 60rpx 0; display: flex; flex-direction: column; align-items: center; text-align: center; } |
| | | .err-icon { width: 96rpx; height: 96rpx; line-height: 96rpx; border-radius: 50%; background: #FBF0EE; color: #E5484D; font-size: 52rpx; font-weight: 700; text-align: center; } |
| | | .err-title { margin-top: 26rpx; font-size: 34rpx; font-weight: 600; color: #1F2D3D; } |
| | | .err-msg { margin-top: 16rpx; font-size: 26rpx; color: #8A94A6; line-height: 1.6; } |
| | | .err-btn { margin-top: 60rpx; width: 100%; } |
| | | .btn { height: 88rpx; line-height: 88rpx; border-radius: 12rpx; font-size: 30rpx; letter-spacing: 4rpx; text-align: center; box-sizing: border-box; } |
| | | .btn-primary { background: linear-gradient(90deg, #16345e, #1F4788 55%, #2a5aa8); color: #fff; box-shadow: 0 8rpx 20rpx rgba(31, 71, 136, 0.28); } |
| | | |
| | | /* ä»»å¡ä¿¡æ¯ */ |
| | | .task-head { background: #fff; border: 1rpx solid #EBEDF1; border-top: 6rpx solid #1F4788; border-radius: 16rpx; padding: 26rpx 28rpx; margin-bottom: 18rpx; box-shadow: 0 4rpx 14rpx rgba(31, 71, 136, 0.05); } |
| | | .th-top { display: flex; justify-content: space-between; align-items: flex-start; } |
| | | .th-title { flex: 1; font-size: 31rpx; font-weight: 600; color: #1F2D3D; line-height: 1.4; } |
| | | .th-tag { margin-left: 16rpx; font-size: 23rpx; padding: 3rpx 14rpx; border-radius: 6rpx; flex-shrink: 0; } |
| | | .th-tag.ok { color: #1D9D5F; background: #EAF7F0; } |
| | | .th-tag.draft { color: #B36A00; background: #FBF4E2; } |
| | | .th-tag.todo { color: #1F4788; background: #E8EFF9; } |
| | | .th-meta { margin-top: 10rpx; font-size: 24rpx; color: #7C8698; } |
| | | .th-note { margin-top: 12rpx; font-size: 24rpx; color: #B36A00; } |
| | | |
| | | /* éè¯¯æ±æ» */ |
| | | .err-banner { background: #FDF3F3; border: 1rpx solid #F5C6C8; color: #C03; border-radius: 8rpx; padding: 16rpx 20rpx; font-size: 25rpx; margin-bottom: 18rpx; } |
| | | |
| | | /* åç» */ |
| | | .group { background: #fff; border: 1rpx solid #EBEDF1; border-radius: 12rpx; padding: 6rpx 28rpx 18rpx; margin-bottom: 18rpx; } |
| | | .group-title { font-size: 29rpx; font-weight: 600; color: #1F2D3D; padding: 22rpx 0 4rpx 20rpx; border-bottom: 1rpx solid #F2F3F6; margin-bottom: 4rpx; border-left: 8rpx solid #1F4788; border-top-left-radius: 4rpx; } |
| | | |
| | | /* åæ®µ */ |
| | | .field { padding: 18rpx 0 2rpx; } |
| | | .field-err .f-label { color: #C03; } |
| | | .f-label { font-size: 26rpx; color: #303133; margin-bottom: 12rpx; } |
| | | .req { color: #E5484D; margin-right: 6rpx; font-weight: 700; } |
| | | |
| | | .f-input, .f-area, .f-ctrl { background: #fff; border: 1rpx solid #DCDFE6; border-radius: 8rpx; padding: 0 20rpx; font-size: 28rpx; color: #1F2D3D; width: 100%; box-sizing: border-box; } |
| | | .f-input { height: 84rpx; line-height: 84rpx; } |
| | | .f-ctrl { height: 84rpx; line-height: 84rpx; display: flex; justify-content: space-between; align-items: center; } |
| | | .f-area { padding: 18rpx 20rpx; min-height: 120rpx; } |
| | | .f-input.err, .f-area.err, .f-ctrl.err { border-color: #E5484D; background: #FDF3F3; } |
| | | .f-radios.err { border-color: #E5484D; } |
| | | .off { color: #B4BAC6; } |
| | | .on { color: #1F2D3D; } |
| | | .caret { color: #B4BAC6; } |
| | | .f-region { display: flex; gap: 16rpx; } |
| | | .f-region-p { flex: 1; min-width: 0; } |
| | | .f-radios { display: flex; flex-wrap: wrap; gap: 18rpx; border: 1rpx solid transparent; border-radius: 8rpx; padding: 4rpx; } |
| | | .f-radio { border: 1rpx solid #DCDFE6; color: #6B7280; border-radius: 8rpx; padding: 12rpx 26rpx; font-size: 26rpx; } |
| | | .f-radio.on { border-color: #1F4788; color: #1F4788; background: #E8EFF9; font-weight: 600; } |
| | | .f-ro { font-size: 28rpx; color: #303133; padding: 16rpx 20rpx; background: #F7F8FA; border-radius: 8rpx; } |
| | | .f-ro.faint { color: #B4BAC6; } |
| | | .err-text { margin-top: 8rpx; font-size: 23rpx; color: #E5484D; } |
| | | .f-help { margin-top: 8rpx; font-size: 22rpx; color: #9AA2B1; } |
| | | |
| | | /* ä¸ä¼ åºï¼å¾ç缩ç¥å¾ */ |
| | | .f-up-grid { display: flex; flex-wrap: wrap; gap: 16rpx; } |
| | | .f-up-cell { position: relative; width: 160rpx; height: 160rpx; border-radius: 8rpx; overflow: hidden; } |
| | | .f-up-img { width: 100%; height: 100%; background: #F2F3F6; } |
| | | .f-up-del { position: absolute; top: 0; right: 0; width: 40rpx; height: 40rpx; line-height: 36rpx; text-align: center; background: rgba(0,0,0,.55); color: #fff; font-size: 28rpx; border-bottom-left-radius: 8rpx; } |
| | | |
| | | /* æä»¶ä¸ä¼ ï¼å¾ç/PDF/Office ç»ä¸ï¼ */ |
| | | .f-files { display: flex; flex-direction: column; gap: 12rpx; } |
| | | .f-file-row { display: flex; justify-content: space-between; align-items: center; background: #F7F8FA; border: 1rpx solid #EBEDF1; border-radius: 8rpx; padding: 16rpx 20rpx; } |
| | | .f-file-name { font-size: 26rpx; color: #1F4788; word-break: break-all; flex: 1; min-width: 0; } |
| | | .f-file-del { font-size: 24rpx; color: #E5484D; margin-left: 20rpx; flex-shrink: 0; } |
| | | .f-file-add { border: 1rpx dashed #C0C6D2; background: #FAFBFD; color: #7C8698; border-radius: 8rpx; padding: 20rpx; text-align: center; font-size: 26rpx; } |
| | | .f-file-add.err { border-color: #E5484D; background: #FDF3F3; color: #E5484D; } |
| | | |
| | | /* åºé¨ */ |
| | | .bottom-bar { position: fixed; left: 0; right: 0; bottom: 0; background: #fff; border-top: 1rpx solid #EBEDF1; padding: 16rpx 24rpx calc(16rpx + env(safe-area-inset-bottom)); display: flex; gap: 18rpx; } |
| | | .btn-draft { flex: 1; background: #fff; color: #1F4788; border: 1rpx solid #1F4788; } |
| | | .btn-submit { flex: 1.4; background: linear-gradient(90deg, #16345e, #1F4788 55%, #2a5aa8); color: #fff; box-shadow: 0 8rpx 20rpx rgba(31, 71, 136, 0.28); } |
| | | .btn-submit[disabled] { opacity: .6; } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="page"> |
| | | <view class="brand"> |
| | | <image class="brand-watermark" src="/static/logo-emblem-white.png" mode="aspectFit" /> |
| | | <image class="brand-logo" src="/static/logo-head-white.png" mode="aspectFit" /> |
| | | <view class="brand-title">注åè´¦å·</view> |
| | | <view class="brand-sub">æ«æç级äºç»´ç è¿å
¥ï¼å¡«åå¦å·ä¸ææºå·å¹¶è®¾ç½®å¯ç </view> |
| | | </view> |
| | | |
| | | <view class="card"> |
| | | <!-- æ«ç ç级å±ç¤º --> |
| | | <view v-if="classInfo.className" class="class-banner"> |
| | | <text class="class-banner-label">ä½ æ£å¨å å
¥</text> |
| | | <text class="class-banner-name">{{ [classInfo.collegeName, classInfo.majorName, classInfo.className].filter(Boolean).join(' · ') }}</text> |
| | | </view> |
| | | |
| | | <!-- æªæ«ç ï¼æå¨éæ©ç级 --> |
| | | <view v-else-if="!checkingCode" class="field" :class="{ 'field-err': errors.classId }"> |
| | | <text class="label">æå¨ç级</text> |
| | | <picker mode="selector" :range="classLabels" :value="classIndex" @change="onClassChange"> |
| | | <view class="picker" :class="{ ph: classIndex < 0 }"> |
| | | {{ classIndex >= 0 ? classLabels[classIndex] : 'è¯·éæ©ä½ çç级ï¼å¦é¢Â·ä¸ä¸Â·ç级ï¼' }} |
| | | </view> |
| | | </picker> |
| | | <view v-if="errors.classId" class="err-text">{{ errors.classId }}</view> |
| | | <view v-else class="class-tip">æ¿ä¸åç级æ¶ï¼è¯·å¨è¯¢çä¸»ä»»ï¼æ«ç 注åå¯èªå¨å¸¦å
¥ç级</view> |
| | | </view> |
| | | |
| | | <view class="field" :class="{ 'field-err': errors.studentNo }"> |
| | | <text class="label">å¦å·</text> |
| | | <input class="input" v-model="form.studentNo" placeholder="请è¾å
¥å¦å·" placeholder-class="ph" @input="clearErr('studentNo')" /> |
| | | <view v-if="errors.studentNo" class="err-text">{{ errors.studentNo }}</view> |
| | | </view> |
| | | |
| | | <view class="field" :class="{ 'field-err': errors.name }"> |
| | | <text class="label">å§å</text> |
| | | <input class="input" v-model="form.name" placeholder="请è¾å
¥çå®å§å" placeholder-class="ph" @input="clearErr('name')" /> |
| | | <view v-if="errors.name" class="err-text">{{ errors.name }}</view> |
| | | </view> |
| | | |
| | | <view class="field" :class="{ 'field-err': errors.phone }"> |
| | | <text class="label">ææºå·</text> |
| | | <input class="input" v-model="form.phone" type="number" maxlength="11" placeholder="æ¬äººææºå·ï¼å¿
å¡«ï¼ç¨äºæ¾åå¯ç ï¼" placeholder-class="ph" @input="clearErr('phone')" /> |
| | | <view v-if="errors.phone" class="err-text">{{ errors.phone }}</view> |
| | | </view> |
| | | |
| | | <view class="field" :class="{ 'field-err': errors.password }"> |
| | | <text class="label">设置å¯ç </text> |
| | | <input class="input" v-model="form.password" type="password" placeholder="8-20ä½ï¼é¡»å«åæ¯åæ°å" placeholder-class="ph" @input="clearErr('password')" /> |
| | | <view v-if="errors.password" class="err-text">{{ errors.password }}</view> |
| | | </view> |
| | | |
| | | <view class="field" :class="{ 'field-err': errors.confirm }"> |
| | | <text class="label">确认å¯ç </text> |
| | | <input class="input" v-model="form.confirm" type="password" placeholder="è¯·åæ¬¡è¾å
¥å¯ç " placeholder-class="ph" @input="clearErr('confirm')" /> |
| | | <view v-if="errors.confirm" class="err-text">{{ errors.confirm }}</view> |
| | | </view> |
| | | |
| | | <view class="field" v-if="captchaEnabled" :class="{ 'field-err': errors.code }"> |
| | | <text class="label">éªè¯ç </text> |
| | | <view class="captcha-row"> |
| | | <input class="input captcha-input" v-model="form.code" placeholder="请è¾å
¥éªè¯ç " placeholder-class="ph" maxlength="6" @input="clearErr('code')" /> |
| | | <image class="captcha-img" :src="captchaImg" mode="aspectFit" @click="loadCaptcha" /> |
| | | </view> |
| | | <view v-if="errors.code" class="err-text">{{ errors.code }}</view> |
| | | </view> |
| | | |
| | | <button class="btn btn-primary btn-block" :loading="submitting" :disabled="submitting" @click="doRegister"> |
| | | {{ submitting ? 'æäº¤ä¸â¦' : '注åå¹¶å¼å§å¡«æ¥' }} |
| | | </button> |
| | | |
| | | <view class="links"> |
| | | <text class="link" @click="go('/pages/login/login')">å·²æè´¦å·ï¼å»ç»å½</text> |
| | | </view> |
| | | <view class="tip">注åå³ä»£è¡¨æ¿è¯ºæå¡«ä¿¡æ¯ä¸ç级çå®åç¡®ï¼å¦å·ä¸å§åä¸å¯é夿³¨å</view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import { getCaptcha, accountRegister, resolveBind, getRegClasses } from '@/api/auth.js' |
| | | import { setToken } from '@/utils/request.js' |
| | | |
| | | const BIND_CODE_KEY = '***' |
| | | |
| | | export default { |
| | | data() { |
| | | return { |
| | | submitting: false, |
| | | checkingCode: true, |
| | | bindCode: '', |
| | | classInfo: {}, |
| | | // æªæ«ç æ¶çç级èªé |
| | | regClasses: [], |
| | | classIndex: -1, |
| | | form: { studentNo: '', name: '', phone: '', password: '', confirm: '', code: '', uuid: '' }, |
| | | errors: {}, |
| | | captchaEnabled: true, |
| | | captchaImg: '', |
| | | } |
| | | }, |
| | | computed: { |
| | | classLabels() { |
| | | return this.regClasses.map((c) => c.label) |
| | | }, |
| | | }, |
| | | async onLoad() { |
| | | this.loadCaptcha() |
| | | this.bindCode = uni.getStorageSync(BIND_CODE_KEY) || '' |
| | | if (this.bindCode) { |
| | | try { |
| | | const res = await resolveBind(this.bindCode) |
| | | this.classInfo = res.data || {} |
| | | } catch (e) { |
| | | this.bindCode = '' // ç æ æï¼é级为æå¨éç |
| | | } |
| | | } |
| | | if (!this.bindCode) { |
| | | try { |
| | | const res = await getRegClasses() |
| | | this.regClasses = res.data || [] |
| | | } catch (e) { this.regClasses = [] } |
| | | } |
| | | this.checkingCode = false |
| | | }, |
| | | methods: { |
| | | onClassChange(e) { |
| | | this.classIndex = Number(e.detail.value) |
| | | if (this.errors.classId) delete this.errors.classId |
| | | }, |
| | | async loadCaptcha() { |
| | | try { |
| | | const res = await getCaptcha() |
| | | this.captchaEnabled = res.captchaEnabled !== false |
| | | this.captchaImg = res.img ? 'data:image/jpg;base64,' + res.img : '' |
| | | this.form.uuid = res.uuid || '' |
| | | this.form.code = '' |
| | | } catch (e) { /* ignore */ } |
| | | }, |
| | | clearErr(k) { if (this.errors[k]) delete this.errors[k] }, |
| | | validate() { |
| | | const errs = {} |
| | | if (!this.bindCode && this.classIndex < 0) errs.classId = 'è¯·éæ©ä½ çç级' |
| | | if (!this.form.studentNo.trim()) errs.studentNo = '请è¾å
¥å¦å·' |
| | | if (!this.form.name.trim()) errs.name = '请è¾å
¥å§å' |
| | | if (!/^1[3-9]\d{9}$/.test(this.form.phone)) errs.phone = '请è¾å
¥11使æºå·' |
| | | const p = this.form.password |
| | | if (!p) errs.password = '请设置å¯ç ' |
| | | else if (!/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d!@#$%^&*_.\-+=?]{8,20}$/.test(p)) errs.password = '8-20ä½ï¼é¡»åæ¶å
å«åæ¯åæ°å' |
| | | else if (p === this.form.studentNo.trim()) errs.password = 'å¯ç ä¸è½ä¸å¦å·ç¸å' |
| | | if (!this.form.confirm) errs.confirm = 'è¯·åæ¬¡è¾å
¥å¯ç ' |
| | | else if (this.form.confirm !== p) errs.confirm = '两次è¾å
¥çå¯ç ä¸ä¸è´' |
| | | if (this.captchaEnabled && !this.form.code.trim()) errs.code = '请è¾å
¥éªè¯ç ' |
| | | this.errors = errs |
| | | return !Object.keys(errs).length |
| | | }, |
| | | async doRegister() { |
| | | if (!this.validate()) return |
| | | this.submitting = true |
| | | try { |
| | | const payload = { |
| | | studentNo: this.form.studentNo.trim(), |
| | | name: this.form.name.trim(), |
| | | phone: this.form.phone.trim(), |
| | | password: this.form.password, |
| | | code: this.form.code.trim(), |
| | | uuid: this.form.uuid, |
| | | } |
| | | // æ«ç ä¼å
ï¼æªæ«ç åæºå¸¦æå¨éæ©çç级 |
| | | if (this.bindCode) payload.bindCode = this.bindCode |
| | | else payload.classId = String(this.regClasses[this.classIndex].classId) |
| | | const res = await accountRegister(payload) |
| | | setToken(res.data.token) |
| | | uni.removeStorageSync(BIND_CODE_KEY) |
| | | uni.showToast({ title: '注åæå', icon: 'success' }) |
| | | setTimeout(() => uni.reLaunch({ url: '/pages/index/index' }), 600) |
| | | } catch (e) { |
| | | if (this.captchaEnabled) this.loadCaptcha() |
| | | } finally { |
| | | this.submitting = false |
| | | } |
| | | }, |
| | | go(url) { uni.navigateTo({ url }) }, |
| | | }, |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | button { margin: 0; padding: 0; } |
| | | button::after { border: none; } |
| | | |
| | | .page { min-height: 100vh; background: #EEF2F8; padding: 0 0 60rpx; box-sizing: border-box; } |
| | | .brand { |
| | | position: relative; |
| | | padding: 48rpx 44rpx 96rpx; |
| | | overflow: hidden; |
| | | background: |
| | | radial-gradient(420rpx 260rpx at 88% -20%, rgba(120, 165, 225, 0.38), transparent 60%), |
| | | linear-gradient(135deg, #16345e 0%, #1F4788 55%, #2a5aa8 100%); |
| | | border-radius: 0 0 30rpx 30rpx; |
| | | } |
| | | .brand-watermark { position: absolute; right: -36rpx; bottom: -60rpx; width: 280rpx; height: 280rpx; opacity: 0.12; } |
| | | .brand-logo { width: 380rpx; height: 84rpx; display: block; } |
| | | .brand-title { position: relative; margin-top: 26rpx; font-size: 40rpx; font-weight: 700; color: #fff; letter-spacing: 8rpx; } |
| | | .brand-sub { position: relative; margin-top: 10rpx; font-size: 24rpx; color: rgba(255, 255, 255, 0.72); line-height: 1.5; } |
| | | |
| | | .card { position: relative; z-index: 1; background: #fff; margin: -56rpx 24rpx 0; border-radius: 16rpx; padding: 40rpx 30rpx 44rpx; box-shadow: 0 12rpx 36rpx rgba(31, 71, 136, 0.12); } |
| | | |
| | | /* æ«ç ç级横å¹
*/ |
| | | .class-banner { display: flex; flex-direction: column; background: #E8EFF9; border: 1rpx solid #D5E2F5; border-radius: 12rpx; padding: 20rpx 24rpx; margin-bottom: 30rpx; } |
| | | .class-banner-label { font-size: 22rpx; color: #7C8698; } |
| | | .class-banner-name { margin-top: 6rpx; font-size: 28rpx; font-weight: 600; color: #1F4788; } |
| | | .picker { height: 88rpx; line-height: 88rpx; background: #fff; border: 1rpx solid #DCDFE6; border-radius: 8rpx; padding: 0 22rpx; font-size: 29rpx; color: #1F2D3D; box-sizing: border-box; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } |
| | | .field-err .picker { border-color: #E5484D; background: #FDF3F3; } |
| | | .class-tip { margin-top: 10rpx; font-size: 22rpx; color: #B4BAC6; } |
| | | |
| | | .field { margin-bottom: 28rpx; } |
| | | .label { display: block; font-size: 27rpx; color: #303133; margin-bottom: 12rpx; } |
| | | .field-err .label { color: #C03; } |
| | | .input { height: 88rpx; line-height: 88rpx; background: #fff; border: 1rpx solid #DCDFE6; border-radius: 8rpx; padding: 0 22rpx; font-size: 29rpx; color: #1F2D3D; } |
| | | .input.err, .field-err .input { border-color: #E5484D; background: #FDF3F3; } |
| | | .err-text { margin-top: 8rpx; color: #E5484D; font-size: 24rpx; } |
| | | .ph { color: #C0C4CC; } |
| | | |
| | | .captcha-row { display: flex; align-items: center; } |
| | | .captcha-input { flex: 1; } |
| | | .captcha-img { width: 200rpx; height: 88rpx; margin-left: 20rpx; border: 1rpx solid #DCDFE6; border-radius: 8rpx; background: #F0F2F6; } |
| | | |
| | | .btn { height: 88rpx; line-height: 88rpx; border-radius: 12rpx; font-size: 30rpx; letter-spacing: 4rpx; text-align: center; box-sizing: border-box; } |
| | | .btn-primary { background: linear-gradient(90deg, #16345e, #1F4788 55%, #2a5aa8); color: #fff; box-shadow: 0 8rpx 20rpx rgba(31, 71, 136, 0.28); } |
| | | .btn-block { width: 100%; } |
| | | .btn[disabled] { opacity: .6; } |
| | | .btn-primary[disabled] { color: #fff; } |
| | | |
| | | .links { display: flex; justify-content: center; margin-top: 26rpx; } |
| | | .link { color: #1F4788; font-size: 26rpx; } |
| | | .tip { margin-top: 20rpx; text-align: center; font-size: 23rpx; color: #B4BAC6; } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | export {}; |
| | | |
| | | declare module "vue" { |
| | | type Hooks = App.AppInstance & Page.PageInstance; |
| | | interface ComponentCustomOptions extends Hooks {} |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | /** |
| | | * è¿éæ¯uni-appå
ç½®çå¸¸ç¨æ ·å¼åé |
| | | * |
| | | * uni-app 宿¹æ©å±æä»¶åæä»¶å¸åºï¼https://ext.dcloud.net.cnï¼ä¸å¾å¤ä¸æ¹æä»¶å使ç¨äºè¿äºæ ·å¼åé |
| | | * å¦æä½ æ¯æä»¶å¼åè
ï¼å»ºè®®ä½ 使ç¨scssé¢å¤çï¼å¹¶å¨æä»¶ä»£ç ä¸ç´æ¥ä½¿ç¨è¿äºåéï¼æ é import è¿ä¸ªæä»¶ï¼ï¼æ¹ä¾¿ç¨æ·éè¿æç§¯æ¨çæ¹å¼å¼åæ´ä½é£æ ¼ä¸è´çApp |
| | | * |
| | | */ |
| | | |
| | | /** |
| | | * å¦æä½ æ¯Appå¼åè
ï¼æä»¶ä½¿ç¨è
ï¼ï¼ä½ å¯ä»¥éè¿ä¿®æ¹è¿äºå鿥å®å¶èªå·±çæä»¶ä¸»é¢ï¼å®ç°èªå®ä¹ä¸»é¢åè½ |
| | | * |
| | | * å¦æä½ ç项ç®åæ ·ä½¿ç¨äºscssé¢å¤çï¼ä½ ä¹å¯ä»¥ç´æ¥å¨ä½ ç scss 代ç ä¸ä½¿ç¨å¦ä¸åéï¼åæ¶æ é import è¿ä¸ªæä»¶ |
| | | */ |
| | | |
| | | /* é¢è²åé */ |
| | | |
| | | /* è¡ä¸ºç¸å
³é¢è² */ |
| | | $uni-color-primary: #007aff; |
| | | $uni-color-success: #4cd964; |
| | | $uni-color-warning: #f0ad4e; |
| | | $uni-color-error: #dd524d; |
| | | |
| | | /* æååºæ¬é¢è² */ |
| | | $uni-text-color: #333; // åºæ¬è² |
| | | $uni-text-color-inverse: #fff; // åè² |
| | | $uni-text-color-grey: #999; // è¾
å©ç°è²ï¼å¦å è½½æ´å¤çæç¤ºä¿¡æ¯ |
| | | $uni-text-color-placeholder: #808080; |
| | | $uni-text-color-disable: #c0c0c0; |
| | | |
| | | /* èæ¯é¢è² */ |
| | | $uni-bg-color: #fff; |
| | | $uni-bg-color-grey: #f8f8f8; |
| | | $uni-bg-color-hover: #f1f1f1; // ç¹å»ç¶æé¢è² |
| | | $uni-bg-color-mask: rgba(0, 0, 0, 0.4); // é®ç½©é¢è² |
| | | |
| | | /* è¾¹æ¡é¢è² */ |
| | | $uni-border-color: #c8c7cc; |
| | | |
| | | /* 尺寸åé */ |
| | | |
| | | /* æå尺寸 */ |
| | | $uni-font-size-sm: 12px; |
| | | $uni-font-size-base: 14px; |
| | | $uni-font-size-lg: 16; |
| | | |
| | | /* å¾ç尺寸 */ |
| | | $uni-img-size-sm: 20px; |
| | | $uni-img-size-base: 26px; |
| | | $uni-img-size-lg: 40px; |
| | | |
| | | /* Border Radius */ |
| | | $uni-border-radius-sm: 2px; |
| | | $uni-border-radius-base: 3px; |
| | | $uni-border-radius-lg: 6px; |
| | | $uni-border-radius-circle: 50%; |
| | | |
| | | /* æ°´å¹³é´è· */ |
| | | $uni-spacing-row-sm: 5px; |
| | | $uni-spacing-row-base: 10px; |
| | | $uni-spacing-row-lg: 15px; |
| | | |
| | | /* åç´é´è· */ |
| | | $uni-spacing-col-sm: 4px; |
| | | $uni-spacing-col-base: 8px; |
| | | $uni-spacing-col-lg: 12px; |
| | | |
| | | /* éæåº¦ */ |
| | | $uni-opacity-disabled: 0.3; // ç»ä»¶ç¦ç¨æçéæåº¦ |
| | | |
| | | /* æç« åºæ¯ç¸å
³ */ |
| | | $uni-color-title: #2c405a; // æç« æ é¢é¢è² |
| | | $uni-font-size-title: 20px; |
| | | $uni-color-subtitle: #555; // äºçº§æ é¢é¢è² |
| | | $uni-font-size-subtitle: 18px; |
| | | $uni-color-paragraph: #3f536e; // æç« 段è½é¢è² |
| | | $uni-font-size-paragraph: 15px; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | /** |
| | | * è¡æ¿åºåæ°æ®ï¼ç/å¸ä¸¤çº§ï¼ |
| | | * |
| | | * ç¨éï¼å¡«è¡¨é
ç½®ä¸ãç份ããåå¸ã两ç§å段类åçéé¡¹æ¥æºï¼åå¸éç份äºçº§èå¨ã |
| | | * åå¼ä¸å¾ç¨ä¸æåç§°ï¼ä¸ companyNature / industryType çåæ®µå䏿å¼ç约å®ä¸è´ï¼ï¼ |
| | | * 便äºå表ç´è¯»ä¸ Excel 导åºï¼æ éååç å¼ç¿»è¯ã |
| | | * |
| | | * æ°æ®ä¸ºéæå
ç½®ï¼çå¸ä¸¤çº§åºåæå°åå¨ï¼å¦éè°æ´ç´æ¥æ¹æ¬æä»¶å³å¯ã |
| | | * |
| | | * 注æï¼æ¬æä»¶ä¸ ruoyi-ui/src/utils/region.js å
容ä¸è´ï¼ä¸¤ä¸ªå·¥ç¨ç¬ç«æå
ï¼æ æ³å
±äº«æ¨¡åï¼ã |
| | | * ä¿®æ¹è¡æ¿åºåæ°æ®æ¶è¯·åæ¥ä¸¤è¾¹ï¼å¦ååå°é
ç½®çå¯é项ä¸å°ç¨åºç«¯ä¼å¯¹ä¸ä¸ã |
| | | */ |
| | | |
| | | /** ççº§è¡æ¿åºï¼34 个ï¼23 ç + 5 èªæ²»åº + 4 ç´è¾å¸ + 2 ç¹å«è¡æ¿åºï¼ */ |
| | | export const PROVINCES = [ |
| | | 'å京å¸', '天津å¸', 'æ²³åç', '山西ç', 'å
èå¤èªæ²»åº', |
| | | 'è¾½å®ç', 'åæç', 'é»é¾æ±ç', '䏿µ·å¸', 'æ±èç', |
| | | 'æµæ±ç', 'å®å¾½ç', 'ç¦å»ºç', 'æ±è¥¿ç', 'å±±ä¸ç', |
| | | 'æ²³åç', 'æ¹åç', 'æ¹åç', '广ä¸ç', '广西壮æèªæ²»åº', |
| | | 'æµ·åç', 'éåºå¸', 'åå·ç', 'è´µå·ç', 'äºåç', |
| | | '西èèªæ²»åº', 'é西ç', 'çèç', 'éæµ·ç', 'å®å¤åæèªæ²»åº', |
| | | 'æ°çç»´å¾å°èªæ²»åº', 'å°æ¹¾ç', '馿¸¯ç¹å«è¡æ¿åº', 'æ¾³é¨ç¹å«è¡æ¿åº' |
| | | ] |
| | | |
| | | /** ç â å°å¸ãç´è¾å¸åæè¾è¡æ¿åºã港澳ååº/å åºãå°æ¹¾åå¿å¸ï¼å£å¾ä¸é表ãçå¸åç§°ãä¸è´ï¼ */ |
| | | export const CITIES = { |
| | | å京å¸: ['ä¸ååº', '西ååº', 'æé³åº', '丰å°åº', 'ç³æ¯å±±åº', 'æµ·æ·åº', 'é¨å¤´æ²åº', 'æ¿å±±åº', 'éå·åº', '顺ä¹åº', 'æå¹³åº', '大å
´åº', 'ææåº', '平谷åº', 'å¯äºåº', 'å»¶åºåº'], |
| | | 天津å¸: ['åå¹³åº', 'æ²³ä¸åº', '河西åº', 'åå¼åº', 'æ²³ååº', '红桥åº', 'ä¸ä¸½åº', '西éåº', 'æ´¥ååº', 'åè¾°åº', 'æ¦æ¸
åº', 'å®å»åº', '滨海æ°åº', '宿²³åº', 'éæµ·åº', 'èå·åº'], |
| | | 䏿µ·å¸: ['黿µ¦åº', '徿±åº', 'é¿å®åº', 'éå®åº', 'æ®éåº', 'è¹å£åº', 'æ¨æµ¦åº', 'éµè¡åº', 'å®å±±åº', 'åå®åº', 'æµ¦ä¸æ°åº', 'éå±±åº', 'æ¾æ±åº', 'éæµ¦åº', 'å¥è´¤åº', 'å´æåº'], |
| | | éåºå¸: ['ä¸å·åº', '涪éµåº', 'æ¸ä¸åº', '大渡å£åº', 'æ±ååº', 'æ²åªååº', 'ä¹é¾å¡åº', 'å岸åº', 'åç¢åº', '綦æ±åº', '大足åº', 'æ¸ååº', 'å·´ååº', '黿±åº', 'é¿å¯¿åº', 'æ±æ´¥åº', 'åå·åº', 'æ°¸å·åº', 'åå·åº', 'ç§å±±åº', '鿢åº', 'æ½¼ååº', 'è£æåº', 'å¼å·åº', 'æ¢å¹³åº', 'æ¦éåº', 'åå£å¿', '丰é½å¿', '嫿±å¿', 'å¿ å¿', 'äºé³å¿', 'å¥èå¿', 'å·«å±±å¿', '巫溪å¿', 'ç³æ±åå®¶æèªæ²»å¿', 'ç§å±±åå®¶æèæèªæ²»å¿', 'é
é³åå®¶æèæèªæ²»å¿', '彿°´èæåå®¶æèªæ²»å¿'], |
| | | æ²³åç: ['ç³å®¶åºå¸', 'åå±±å¸', '秦çå²å¸', 'é¯é¸å¸', 'é¢å°å¸', 'ä¿å®å¸', 'å¼ å®¶å£å¸', 'æ¿å¾·å¸', 'æ²§å·å¸', 'å»åå¸', 'è¡¡æ°´å¸'], |
| | | 山西ç: ['太åå¸', '大åå¸', '鳿³å¸', 'é¿æ²»å¸', 'æåå¸', 'æå·å¸', 'æä¸å¸', 'è¿åå¸', 'å¿»å·å¸', '临汾å¸', '忢å¸'], |
| | | å
èå¤èªæ²»åº: ['å¼å浩ç¹å¸', 'å
头å¸', '乿µ·å¸', '赤峰å¸', 'éè¾½å¸', 'éå°å¤æ¯å¸', 'å¼ä¼¦è´å°å¸', '巴彦æ·å°å¸', 'ä¹å
°å¯å¸å¸', 'å
´å®ç', '顿éåç', 'é¿æåç'], |
| | | è¾½å®ç: ['æ²é³å¸', '大è¿å¸', 'éå±±å¸', 'æé¡ºå¸', 'æ¬æºªå¸', '丹ä¸å¸', 'é¦å·å¸', 'è¥å£å¸', 'éæ°å¸', 'è¾½é³å¸', 'çé¦å¸', 'éå²å¸', 'æé³å¸', 'è«è¦å²å¸'], |
| | | åæç: ['é¿æ¥å¸', 'åæå¸', 'åå¹³å¸', 'è¾½æºå¸', 'éåå¸', 'ç½å±±å¸', 'æ¾åå¸', 'ç½åå¸', 'å»¶è¾¹æé²æèªæ²»å·'], |
| | | é»é¾æ±ç: ['åå°æ»¨å¸', 'é½é½åå°å¸', '鸡西å¸', '鹤å²å¸', 'åé¸å±±å¸', '大åºå¸', '伿¥å¸', 'ä½³æ¨æ¯å¸', 'ä¸å°æ²³å¸', 'ç¡ä¸¹æ±å¸', '黿²³å¸', '绥åå¸', '大å
´å®å²å°åº'], |
| | | æ±èç: ['å京å¸', 'æ é¡å¸', 'å¾å·å¸', '常å·å¸', 'èå·å¸', 'åéå¸', 'è¿äºæ¸¯å¸', 'æ·®å®å¸', 'çåå¸', 'æ¬å·å¸', 'éæ±å¸', 'æ³°å·å¸', '宿è¿å¸'], |
| | | æµæ±ç: ['æå·å¸', '宿³¢å¸', '温å·å¸', 'åå
´å¸', 'æ¹å·å¸', 'ç»å
´å¸', 'éåå¸', 'è¡¢å·å¸', 'èå±±å¸', 'å°å·å¸', '丽水å¸'], |
| | | å®å¾½ç: ['åè¥å¸', 'èæ¹å¸', 'èå å¸', 'æ·®åå¸', '马éå±±å¸', 'æ·®åå¸', 'ééµå¸', 'å®åºå¸', 'é»å±±å¸', 'æ»å·å¸', 'éé³å¸', '宿å·å¸', 'å
å®å¸', '亳å·å¸', 'æ± å·å¸', '宣åå¸'], |
| | | ç¦å»ºç: ['ç¦å·å¸', 'å¦é¨å¸', 'èç°å¸', '䏿å¸', 'æ³å·å¸', 'æ¼³å·å¸', 'åå¹³å¸', 'é¾å²©å¸', 'å®å¾·å¸'], |
| | | æ±è¥¿ç: ['åæå¸', 'æ¯å¾·éå¸', 'è乡å¸', '乿±å¸', 'æ°ä½å¸', 'é¹°æ½å¸', 'èµ£å·å¸', 'åå®å¸', '宿¥å¸', 'æå·å¸', 'ä¸é¥¶å¸'], |
| | | å±±ä¸ç: ['æµåå¸', 'éå²å¸', 'æ·åå¸', 'æ£åºå¸', 'ä¸è¥å¸', 'çå°å¸', 'æ½åå¸', 'æµå®å¸', 'æ³°å®å¸', '卿µ·å¸', 'æ¥ç
§å¸', '临æ²å¸', 'å¾·å·å¸', 'èåå¸', '滨å·å¸', 'èæ³½å¸'], |
| | | æ²³åç: ['éå·å¸', 'å¼å°å¸', 'æ´é³å¸', '平顶山å¸', 'å®é³å¸', '鹤å£å¸', 'æ°ä¹¡å¸', 'ç¦ä½å¸', 'æ¿®é³å¸', '许æå¸', '漯河å¸', 'ä¸é¨å³¡å¸', 'åé³å¸', 'åä¸å¸', 'ä¿¡é³å¸', 'å¨å£å¸', '驻马åºå¸', 'æµæºå¸'], |
| | | æ¹åç: ['æ¦æ±å¸', 'é»ç³å¸', 'åå °å¸', '宿å¸', 'è¥é³å¸', 'éå·å¸', 'èé¨å¸', 'åæå¸', 'èå·å¸', 'é»åå¸', 'å¸å®å¸', 'éå·å¸', 'æ©æ½åå®¶æèæèªæ²»å·', '仿¡å¸', 'æ½æ±å¸', '天é¨å¸', 'ç¥åæ¶æåº'], |
| | | æ¹åç: ['é¿æ²å¸', 'æ ªæ´²å¸', 'æ¹æ½å¸', 'è¡¡é³å¸', 'éµé³å¸', 'å²³é³å¸', '常德å¸', 'å¼ å®¶çå¸', 'çé³å¸', 'é´å·å¸', 'æ°¸å·å¸', 'æåå¸', 'å¨åºå¸', 'æ¹è¥¿åå®¶æèæèªæ²»å·'], |
| | | 广ä¸ç: ['广å·å¸', 'é¶å
³å¸', 'æ·±å³å¸', 'ç æµ·å¸', 'æ±å¤´å¸', 'ä½å±±å¸', 'æ±é¨å¸', 'æ¹æ±å¸', 'èåå¸', 'èåºå¸', 'æ å·å¸', 'æ¢
å·å¸', 'æ±å°¾å¸', 'æ²³æºå¸', '鳿±å¸', 'æ¸
è¿å¸', 'ä¸èå¸', 'ä¸å±±å¸', 'æ½®å·å¸', 'æé³å¸', 'äºæµ®å¸'], |
| | | 广西壮æèªæ²»åº: ['åå®å¸', 'æ³å·å¸', 'æ¡æå¸', '梧å·å¸', 'åæµ·å¸', 'é²å港å¸', 'é¦å·å¸', '贵港å¸', 'çæå¸', 'ç¾è²å¸', 'è´ºå·å¸', 'æ²³æ± å¸', 'æ¥å®¾å¸', 'å´å·¦å¸'], |
| | | æµ·åç: ['æµ·å£å¸', 'ä¸äºå¸', '䏿²å¸', 'åå·å¸'], |
| | | åå·ç: ['æé½å¸', 'èªè´¡å¸', 'ææè±å¸', '泸å·å¸', 'å¾·é³å¸', '绵é³å¸', '广å
å¸', 'éå®å¸', 'å
æ±å¸', 'ä¹å±±å¸', 'åå
å¸', 'çå±±å¸', 'å®å®¾å¸', '广å®å¸', 'è¾¾å·å¸', 'é
å®å¸', 'å·´ä¸å¸', 'èµé³å¸', 'é¿åèæç¾æèªæ²»å·', 'çåèæèªæ²»å·', 'åå±±å½æèªæ²»å·'], |
| | | è´µå·ç: ['è´µé³å¸', 'å
çæ°´å¸', 'éµä¹å¸', 'å®é¡ºå¸', 'æ¯èå¸', 'éä»å¸', 'é»è¥¿åå¸ä¾æèæèªæ²»å·', 'é»ä¸åèæä¾æèªæ²»å·', 'é»åå¸ä¾æèæèªæ²»å·'], |
| | | äºåç: ['ææå¸', 'æ²éå¸', 'çæºªå¸', 'ä¿å±±å¸', 'æéå¸', '丽æ±å¸', 'æ®æ´±å¸', '临沧å¸', 'æ¥éå½æèªæ²»å·', '红河åå°¼æå½æèªæ²»å·', 'æå±±å£®æèæèªæ²»å·', '西åççº³å£æèªæ²»å·', '大çç½æèªæ²»å·', 'å¾·å®å£ææ¯é¢æèªæ²»å·', 'ææ±åå³æèªæ²»å·', '迪åºèæèªæ²»å·'], |
| | | 西èèªæ²»åº: ['æè¨å¸', 'æ¥ååå¸', 'æé½å¸', 'æèå¸', 'å±±åå¸', '飿²å¸', 'é¿éå°åº'], |
| | | é西ç: ['西å®å¸', 'éå·å¸', 'å®é¸¡å¸', 'å¸é³å¸', 'æ¸åå¸', 'å»¶å®å¸', 'æ±ä¸å¸', 'æ¦æå¸', 'å®åº·å¸', 'åæ´å¸'], |
| | | çèç: ['å
°å·å¸', 'å峪å
³å¸', 'éæå¸', 'ç½é¶å¸', '天水å¸', 'æ¦å¨å¸', 'å¼ æå¸', 'å¹³åå¸', 'é
æ³å¸', 'åºé³å¸', 'å®è¥¿å¸', 'éåå¸', '临å¤åæèªæ²»å·', 'çåèæèªæ²»å·'], |
| | | éæµ·ç: ['西å®å¸', 'æµ·ä¸å¸', 'æµ·åèæèªæ²»å·', 'é»åèæèªæ²»å·', 'æµ·åèæèªæ²»å·', 'ææ´èæèªæ²»å·', 'çæ èæèªæ²»å·', '海西è夿èæèªæ²»å·'], |
| | | å®å¤åæèªæ²»åº: ['é¶å·å¸', 'ç³å´å±±å¸', 'å´å¿ å¸', 'åºåå¸', 'ä¸å«å¸'], |
| | | æ°çç»´å¾å°èªæ²»åº: ['ä¹é²æ¨é½å¸', 'å
æçä¾å¸', 'åé²çªå¸', 'åå¯å¸', 'æååæèªæ²»å·', 'åå°å¡æèå¤èªæ²»å·', 'å·´é³éæ¥èå¤èªæ²»å·', 'é¿å
èå°åº', 'å
ååèæ¯å°å
åèªæ²»å·', 'åä»å°åº', 'åç°å°åº', 'ä¼çåè¨å
èªæ²»å·', 'å¡åå°åº', 'é¿åæ³°å°åº', 'ç³æ²³åå¸', 'é¿æå°å¸', '徿¨èå
å¸', 'äºå®¶æ¸ å¸', 'å屯å¸', 'éé¨å
³å¸', 'åæ²³å¸', 'å¯å
è¾¾æå¸', 'æçå¸', 'è¡æ¨æ²³å¸'], |
| | | å°æ¹¾ç: ['å°å', 'é«é', 'å°å', 'å°ä¸', 'éé¨', 'åæ', 'åºé', 'æ°ç«¹', 'åä¹', 'æ°å', 'å®å
°', 'æ¡å', 'èæ ', 'å½°å', 'äºæ', 'å±ä¸', 'å°ä¸', 'è±è²', 'æ¾æ¹', 'è¿æ±'], |
| | | 馿¸¯ç¹å«è¡æ¿åº: ['ä¸è¥¿åº', 'æ¹¾ä»åº', 'ä¸åº', 'ååº', 'æ²¹å°æºåº', '深水ååº', 'ä¹é¾ååº', 'é»å¤§ä»åº', 'è§å¡åº', 'èæ¹¾åº', '屯é¨åº', 'å
æåº', 'ååº', '大ååº', '西贡åº', 'æ²ç°åº', 'èµéåº', '离å²åº'], |
| | | æ¾³é¨ç¹å«è¡æ¿åº: ['è±å°çå åº', 'è±çå åº', 'æå¾·å åº', '大å åº', 'é£é¡ºå åº', '忍¡å åº', 'è·¯å¼å¡«æµ·åº', '壿¹æµåå åº'] |
| | | } |
| | | |
| | | /** ç䏿é项 */ |
| | | export const PROVINCE_OPTIONS = PROVINCES.map(p => ({ value: p, label: p })) |
| | | |
| | | /** åæçä¸çå°å¸ï¼çä»½ä¸ºç©ºææ æ°æ®æ¶è¿å空æ°ç»ã容éç®åï¼å¦ãæ±èãèªå¨æãæ±èçãè§£æ */ |
| | | export function citiesOf(province) { |
| | | if (!province) return [] |
| | | if (CITIES[province]) return CITIES[province] |
| | | const guess = [province + 'ç', province + 'å¸'].find(k => CITIES[k]) |
| | | return guess ? CITIES[guess] : [] |
| | | } |
| | | |
| | | /** å°å¸ä¸æé项ï¼å¯å¸¦èªå®ä¹é¦é¡¹ï¼å¦ãå
¶ä»ãï¼ */ |
| | | export function cityOptions(province) { |
| | | return citiesOf(province).map(c => ({ value: c, label: c })) |
| | | } |
| | |
| | | import axios from 'axios'
|
| | | import { ElNotification , ElMessageBox, ElMessage, ElLoading } from 'element-plus'
|
| | | import { getToken } from '@/utils/auth'
|
| | | import errorCode from '@/utils/errorCode'
|
| | | import { tansParams, blobValidate } from '@/utils/ruoyi'
|
| | | import cache from '@/plugins/cache'
|
| | | import { saveAs } from 'file-saver'
|
| | | import useUserStore from '@/store/modules/user'
|
| | |
|
| | | let downloadLoadingInstance
|
| | | // æ¯å¦æ¾ç¤ºéæ°ç»å½
|
| | | export let isRelogin = { show: false }
|
| | |
|
| | | axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
|
| | | // å建axioså®ä¾
|
| | | const service = axios.create({
|
| | | // axiosä¸è¯·æ±é
ç½®æbaseURLé项ï¼è¡¨ç¤ºè¯·æ±URLå
Œ
±é¨å
|
| | | baseURL: import.meta.env.VITE_APP_BASE_API,
|
| | | // è¶
æ¶
|
| | | timeout: 60000
|
| | | })
|
| | |
|
| | | // requestæ¦æªå¨
|
| | | service.interceptors.request.use(config => {
|
| | | // æ¯å¦éè¦è®¾ç½® token
|
| | | const isToken = (config.headers || {}).isToken === false
|
| | | // æ¯å¦éè¦é²æ¢æ°æ®éå¤æäº¤
|
| | | const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
|
| | | if (getToken() && !isToken) {
|
| | | config.headers['Authorization'] = 'Bearer ' + getToken() // 让æ¯ä¸ªè¯·æ±æºå¸¦èªå®ä¹token è¯·æ ¹æ®å®é
æ
åµèªè¡ä¿®æ¹
|
| | | }
|
| | | // getè¯·æ±æ å°paramsåæ°
|
| | | if (config.method === 'get' && config.params) {
|
| | | let url = config.url + '?' + tansParams(config.params)
|
| | | url = url.slice(0, -1)
|
| | | config.params = {}
|
| | | config.url = url
|
| | | }
|
| | | if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
|
| | | const requestObj = {
|
| | | url: config.url,
|
| | | data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
|
| | | time: new Date().getTime()
|
| | | }
|
| | | const requestSize = Object.keys(JSON.stringify(requestObj)).length // è¯·æ±æ°æ®å¤§å°
|
| | | const limitSize = 5 * 1024 * 1024 // éå¶åæ¾æ°æ®5M
|
| | | if (requestSize >= limitSize) {
|
| | | console.warn(`[${config.url}]: ` + 'è¯·æ±æ°æ®å¤§å°è¶
åºå
许ç5Méå¶ï¼æ æ³è¿è¡é²éå¤æäº¤éªè¯ã')
|
| | | return config
|
| | | }
|
| | | const sessionObj = cache.session.getJSON('sessionObj')
|
| | | if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
|
| | | cache.session.setJSON('sessionObj', requestObj)
|
| | | } else {
|
| | | const s_url = sessionObj.url // 请æ±å°å
|
| | | const s_data = sessionObj.data // è¯·æ±æ°æ®
|
| | | const s_time = sessionObj.time // è¯·æ±æ¶é´
|
| | | const interval = 1000 // é´éæ¶é´(ms)ï¼å°äºæ¤æ¶é´è§ä¸ºéå¤æäº¤
|
| | | if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
|
| | | const message = 'æ°æ®æ£å¨å¤çï¼è¯·å¿éå¤æäº¤'
|
| | | console.warn(`[${s_url}]: ` + message)
|
| | | return Promise.reject(new Error(message))
|
| | | } else {
|
| | | cache.session.setJSON('sessionObj', requestObj)
|
| | | }
|
| | | }
|
| | | }
|
| | | return config
|
| | | }, error => {
|
| | | console.log(error)
|
| | | Promise.reject(error)
|
| | | })
|
| | |
|
| | | // ååºæ¦æªå¨
|
| | | service.interceptors.response.use(res => {
|
| | | // æªè®¾ç½®ç¶æç åé»è®¤æåç¶æ
|
| | | const code = res.data.code || 200
|
| | | // è·åé误信æ¯
|
| | | const msg = errorCode[code] || res.data.msg || errorCode['default']
|
| | | // äºè¿å¶æ°æ®åç´æ¥è¿å
|
| | | if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
|
| | | return res.data
|
| | | }
|
| | | if (code === 401) {
|
| | | if (!isRelogin.show) {
|
| | | isRelogin.show = true
|
| | | ElMessageBox.confirm('ç»å½ç¶æå·²è¿æï¼æ¨å¯ä»¥ç»§ç»çå¨è¯¥é¡µé¢ï¼æè
éæ°ç»å½', 'ç³»ç»æç¤º', { confirmButtonText: 'éæ°ç»å½', cancelButtonText: 'åæ¶', type: 'warning' }).then(() => {
|
| | | isRelogin.show = false
|
| | | useUserStore().logOut().then(() => {
|
| | | location.href = '/index'
|
| | | })
|
| | | }).catch(() => {
|
| | | isRelogin.show = false
|
| | | })
|
| | | }
|
| | | return Promise.reject('æ æçä¼è¯ï¼æè
ä¼è¯å·²è¿æï¼è¯·éæ°ç»å½ã')
|
| | | } else if (code === 500) {
|
| | | ElMessage({ message: msg, type: 'error' })
|
| | | return Promise.reject(new Error(msg))
|
| | | } else if (code === 601) {
|
| | | ElMessage({ message: msg, type: 'warning' })
|
| | | return Promise.reject(new Error(msg))
|
| | | } else if (code !== 200) {
|
| | | ElNotification.error({ title: msg })
|
| | | return Promise.reject('error')
|
| | | } else {
|
| | | return Promise.resolve(res.data)
|
| | | }
|
| | | },
|
| | | error => {
|
| | | console.log('err' + error)
|
| | | let { message } = error
|
| | | if (message == "Network Error") {
|
| | | message = "å端æ¥å£è¿æ¥å¼å¸¸"
|
| | | } else if (message.includes("timeout")) {
|
| | | message = "ç³»ç»æ¥å£è¯·æ±è¶
æ¶"
|
| | | } else if (message.includes("Request failed with status code")) {
|
| | | message = "ç³»ç»æ¥å£" + message.substr(message.length - 3) + "å¼å¸¸"
|
| | | }
|
| | | ElMessage({ message: message, type: 'error', duration: 5 * 1000 })
|
| | | return Promise.reject(error)
|
| | | }
|
| | | )
|
| | |
|
| | | // éç¨ä¸è½½æ¹æ³
|
| | | export function download(url, params, filename, config) {
|
| | | downloadLoadingInstance = ElLoading.service({ text: "æ£å¨ä¸è½½æ°æ®ï¼è¯·ç¨å", background: "rgba(0, 0, 0, 0.7)", })
|
| | | return service.post(url, params, {
|
| | | transformRequest: [(params) => { return tansParams(params) }],
|
| | | headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
| | | responseType: 'blob',
|
| | | ...config
|
| | | }).then(async (data) => {
|
| | | const isBlob = blobValidate(data)
|
| | | if (isBlob) {
|
| | | const blob = new Blob([data])
|
| | | saveAs(blob, filename)
|
| | | } else {
|
| | | const resText = await data.text()
|
| | | const rspObj = JSON.parse(resText)
|
| | | const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
|
| | | ElMessage.error(errMsg)
|
| | | }
|
| | | downloadLoadingInstance.close()
|
| | | }).catch((r) => {
|
| | | console.error(r)
|
| | | ElMessage.error('ä¸è½½æä»¶åºç°é误ï¼è¯·è系管çåï¼')
|
| | | downloadLoadingInstance.close()
|
| | | })
|
| | | }
|
| | |
|
| | | export default service
|
| | | /** |
| | | * 请æ±å°è£
ï¼åºäº uni.requestï¼æ¯æ tokenãé误ç»ä¸å¤çï¼ |
| | | */ |
| | | import config from '@/config.js' |
| | | |
| | | // åå¨ token ç key |
| | | const TOKEN_KEY = 'cj_student_token' |
| | | |
| | | export function getToken() { |
| | | return uni.getStorageSync(TOKEN_KEY) |
| | | } |
| | | export function setToken(t) { |
| | | uni.setStorageSync(TOKEN_KEY, t) |
| | | } |
| | | export function clearToken() { |
| | | uni.removeStorageSync(TOKEN_KEY) |
| | | } |
| | | |
| | | function request(method, url, data = {}, options = {}) { |
| | | return new Promise((resolve, reject) => { |
| | | const token = getToken() |
| | | uni.request({ |
| | | url: config.baseUrl + url, |
| | | method, |
| | | data, |
| | | header: { |
| | | 'Content-Type': 'application/json', |
| | | ...(token ? { Authorization: 'Bearer ' + token } : {}), |
| | | ...(options.header || {}), |
| | | }, |
| | | success: (res) => { |
| | | if (res.statusCode === 401) { |
| | | clearToken() |
| | | uni.showToast({ title: 'è¯·éæ°ç»å½', icon: 'none' }) |
| | | uni.reLaunch({ url: '/pages/login/login' }) |
| | | reject(res.data) |
| | | return |
| | | } |
| | | if (res.data && res.data.code === 200) { |
| | | resolve(res.data) |
| | | } else { |
| | | // å端以 HTTP 200 + body.code 401 表达ç»å½å¤±æï¼åæ¥æ¸
tokenï¼è·³ç»å½é¡µ |
| | | if (res.data && res.data.code === 401) { |
| | | clearToken() |
| | | uni.showToast({ title: 'ç»å½å·²å¤±æï¼è¯·éæ°ç»å½', icon: 'none' }) |
| | | uni.reLaunch({ url: '/pages/login/login' }) |
| | | } else { |
| | | uni.showToast({ title: (res.data && res.data.msg) || '请æ±å¤±è´¥', icon: 'none' }) |
| | | } |
| | | reject(res.data) |
| | | } |
| | | }, |
| | | fail: (err) => { |
| | | uni.showToast({ title: 'ç½ç»å¼å¸¸ï¼è¯·æ£æ¥åç«¯è¿æ¥', icon: 'none' }) |
| | | reject(err) |
| | | }, |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * ä¸ä¼ å个æä»¶å° /miniapp/uploadã |
| | | * H5 å¯ä¼ fileï¼input[type=file] éä¸ç File 对象ï¼ï¼å°ç¨åº/App ä¼ filePathï¼ä¸´æ¶è·¯å¾ï¼ã |
| | | * å端ååºä¸º { code, msg, data: { fileName, originalName } }ï¼ |
| | | * æåæ¶ resolve å
å± dataï¼{ fileName, originalName }ï¼ï¼å
¼å®¹ä¸ªå«æ§ç»æçé¡¶å± fileNameã |
| | | */ |
| | | export function uploadFile({ filePath, file }) { |
| | | return new Promise((resolve, reject) => { |
| | | const token = getToken() |
| | | // uni-h5 ç uploadFile 缺 filePath ä¼å
reject("file error")ï¼ä½ XHR ä»ä¼ååºï¼å¯¼è´ |
| | | // ãåç«¯ä¼ æåãå端æ¥å¤±è´¥ãï¼ï¼H5 ä¼ File 对象æ¶è¡¥ä¸ä¸ª blob URL å
åºã |
| | | let fp = filePath |
| | | if (!fp && file) { |
| | | try { fp = URL.createObjectURL(file) } catch (e) { /* éæµè§å¨ç¯å¢å¿½ç¥ */ } |
| | | } |
| | | uni.uploadFile({ |
| | | url: config.baseUrl + '/miniapp/upload', |
| | | filePath: fp, |
| | | file, |
| | | name: 'file', |
| | | header: token ? { Authorization: 'Bearer ' + token } : {}, |
| | | success: (res) => { |
| | | let d = res.data |
| | | try { d = JSON.parse(res.data) } catch (e) { /* ä¿çåå§ææ¬ */ } |
| | | if (res.statusCode === 401) { |
| | | clearToken() |
| | | uni.showToast({ title: 'è¯·éæ°ç»å½', icon: 'none' }) |
| | | uni.reLaunch({ url: '/pages/login/login' }) |
| | | reject(d) |
| | | return |
| | | } |
| | | if (d && d.code === 200) { |
| | | // çæ£æç¨çæ¯ data å
å±ï¼æ§ä»£ç ç´æ¥ resolve æ´ä¸ªååºä½å¯¼è´ res.fileName æä¸º undefined |
| | | resolve(d.data || d) |
| | | } else { |
| | | uni.showToast({ title: (d && d.msg) || 'ä¸ä¼ 失败', icon: 'none' }) |
| | | reject(d) |
| | | } |
| | | }, |
| | | fail: (err) => { |
| | | uni.showToast({ title: 'ä¸ä¼ 失败ï¼è¯·æ£æ¥ç½ç»', icon: 'none' }) |
| | | reject(err) |
| | | }, |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | export default { |
| | | get: (url, data, opts) => request('GET', url, data, opts), |
| | | post: (url, data, opts) => request('POST', url, data, opts), |
| | | put: (url, data, opts) => request('PUT', url, data, opts), |
| | | delete: (url, data, opts) => request('DELETE', url, data, opts), |
| | | upload: uploadFile, |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | /** |
| | | * ç«ç¹ææ¡ï¼åå°ãåå
¸ç®¡ç â 2024级å¦ç产æèåå®è·µæå¦ä¿¡æ¯å¡«æ¥ã驱å¨ï¼ |
| | | * |
| | | * ææ¡ç± /miniapp/site-titles ä¸åï¼åå°æ¹å®å³æ¶çæï¼å端ä¸ç¨éæ°æå
ã |
| | | * æ¥å£æªé / åå
¸é¡¹è¢«å / å¼ä¸ºç©ºæ¶ï¼ä¸å¾åéä¸é¢çå
ç½®é»è®¤ï¼é¡µé¢ä¸ä¼åºç°ç©ºç½æ é¢ã |
| | | * 䏿¬¡ä¼è¯åªè¯·æ±ä¸æ¬¡ï¼å¹¶åè°ç¨å
±ç¨åä¸ä¸ª pending Promiseï¼ï¼å¤±è´¥ä¸ç¼åã䏿¬¡å¯éè¯ã |
| | | */ |
| | | import { getSiteTitles } from '@/api/auth.js' |
| | | |
| | | // å
åºææ¡ï¼é¡»ä¸å端 MiniappController#siteTitles ç fallback ä¿æä¸è´ |
| | | export const DEFAULT_TITLES = { |
| | | platformTitle: '产æèåå®è·µæå¦ç®¡çå¹³å°', |
| | | platformSubtitle: 'æ±èåè´¸èä¸å¦é¢ · å¦çå®è·µæå¦ä¿¡æ¯å¡«æ¥', |
| | | loginSubtitle: '产æèåå®è·µæå¦ç®¡çå¹³å° Â· å¦çè´¦å·', |
| | | } |
| | | |
| | | let cache = null |
| | | let pending = null |
| | | |
| | | /** åç«ç¹ææ¡ï¼å¸¦ç¼åï¼ï¼æ°¸è¿ resolveï¼ä¸ä¼ reject */ |
| | | export function getTitles() { |
| | | if (cache) return Promise.resolve(cache) |
| | | if (!pending) { |
| | | pending = getSiteTitles() |
| | | .then((res) => { |
| | | cache = { ...DEFAULT_TITLES, ...(res.data || {}) } |
| | | return cache |
| | | }) |
| | | .catch(() => DEFAULT_TITLES) |
| | | .then((v) => { |
| | | pending = null |
| | | return v |
| | | }) |
| | | } |
| | | return pending |
| | | } |
| | |
| | | import { defineConfig, loadEnv } from "vite";
|
| | | import path from "path";
|
| | | import createVitePlugins from "./vite/plugins";
|
| | |
|
| | | // https://vitejs.dev/config/
|
| | | export default defineConfig(({ mode, command }) => {
|
| | | const env = loadEnv(mode, process.cwd());
|
| | | const { VITE_APP_ENV } = env;
|
| | | const baseUrl =
|
| | | VITE_APP_ENV == "development"
|
| | | ? "http://114.132.189.42:8089" // å¼åç¯å¢å端æ¥å£
|
| | | : "http://114.132.189.42:9032"; // ç产ç¯å¢å端æ¥å£
|
| | |
|
| | | return {
|
| | | // é¨ç½²ç产ç¯å¢åå¼åç¯å¢ä¸çURLã
|
| | | // é»è®¤æ
åµä¸ï¼vite ä¼åè®¾ä½ çåºç¨æ¯è¢«é¨ç½²å¨ä¸ä¸ªååçæ ¹è·¯å¾ä¸
|
| | | // ä¾å¦ https://www.ruoyi.vip/ã妿åºç¨è¢«é¨ç½²å¨ä¸ä¸ªåè·¯å¾ä¸ï¼ä½ å°±éè¦ç¨è¿ä¸ªé项æå®è¿ä¸ªåè·¯å¾ãä¾å¦ï¼å¦æä½ çåºç¨è¢«é¨ç½²å¨ https://www.ruoyi.vip/admin/ï¼å设置 baseUrl 为 /admin/ã
|
| | | base: VITE_APP_ENV === "production" ? "/" : "/",
|
| | | plugins: createVitePlugins(env, command === "build"),
|
| | | resolve: {
|
| | | // https://cn.vitejs.dev/config/#resolve-alias
|
| | | alias: {
|
| | | // 设置路å¾
|
| | | "~": path.resolve(__dirname, "./"),
|
| | | // 设置å«å
|
| | | "@": path.resolve(__dirname, "./src"),
|
| | | },
|
| | | // https://cn.vitejs.dev/config/#resolve-extensions
|
| | | extensions: [".mjs", ".js", ".ts", ".jsx", ".tsx", ".json", ".vue"],
|
| | | },
|
| | | // æå
é
ç½®
|
| | | build: {
|
| | | // https://vite.dev/config/build-options.html
|
| | | sourcemap: command === "build" ? false : "inline",
|
| | | outDir: "dist",
|
| | | assetsDir: "assets",
|
| | | chunkSizeWarningLimit: 2000,
|
| | | rollupOptions: {
|
| | | output: {
|
| | | chunkFileNames: "static/js/[name]-[hash].js",
|
| | | entryFileNames: "static/js/[name]-[hash].js",
|
| | | assetFileNames: "static/[ext]/[name]-[hash].[ext]",
|
| | | },
|
| | | },
|
| | | },
|
| | | // vite ç¸å
³é
ç½®
|
| | | server: {
|
| | | port: 8001,
|
| | | host: true,
|
| | | open: true,
|
| | | proxy: {
|
| | | // https://cn.vitejs.dev/config/#server-proxy
|
| | | "/dev-api": {
|
| | | target: baseUrl,
|
| | | changeOrigin: true,
|
| | | rewrite: (p) => p.replace(/^\/dev-api/, ""),
|
| | | },
|
| | | // springdoc proxy
|
| | | "^/v3/api-docs/(.*)": {
|
| | | target: baseUrl,
|
| | | changeOrigin: true,
|
| | | },
|
| | | },
|
| | | },
|
| | | css: {
|
| | | postcss: {
|
| | | plugins: [
|
| | | {
|
| | | postcssPlugin: "internal:charset-removal",
|
| | | AtRule: {
|
| | | charset: (atRule) => {
|
| | | if (atRule.name === "charset") {
|
| | | atRule.remove();
|
| | | }
|
| | | },
|
| | | },
|
| | | },
|
| | | ],
|
| | | },
|
| | | },
|
| | | };
|
| | | });
|
| | | import { defineConfig } from 'vite' |
| | | import uni from '@dcloudio/vite-plugin-uni' |
| | | // https://vitejs.dev/config/ |
| | | export default defineConfig({ |
| | | plugins: [ |
| | | uni(), |
| | | ], |
| | | // ä»
å½±åå¼åæå¡å¨ï¼dev:h5ï¼ï¼ä¸å½±å npm run build 产ç©ã |
| | | // å¦ç端 baseUrl åºå®ä¸º /prod-apiï¼å¯¹åº nginx å代ï¼ï¼æ¬å°å¼å没æ nginxï¼ |
| | | // æ
卿¤æ /prod-api 转åå°æ¬æºåç«¯å¹¶å»æåç¼ã |
| | | server: { |
| | | proxy: { |
| | | '/prod-api': { |
| | | target: 'http://localhost:18080', |
| | | changeOrigin: true, |
| | | rewrite: (path) => path.replace(/^\/prod-api/, ''), |
| | | }, |
| | | }, |
| | | }, |
| | | }) |