From da05dcffeabcb3c460e45666a4804f4ea5a4145a Mon Sep 17 00:00:00 2001
From: zhou zhou <3272660260@qq.com>
Date: 星期三, 25 二月 2026 17:02:17 +0800
Subject: [PATCH] #
---
pages/home/home.vue | 579 ++++++++++++++
.gemini/skills/wms-dev-standards/SKILL.md | 62 +
pages/login/login.vue | 934 ++++++++++++++--------
locale/zh-Hans.json | 16
pages.json | 4
.trae/skills/nvue/SKILL.md | 63 +
config/request.js | 86 ++
locale/en.json | 14
pages/login/api.js | 7
/dev/null | 41 -
pages/login/index.nvue | 559 +++++++++++++
main.js | 16
pages/home/api.js | 4
pages/index/index.vue | 33
14 files changed, 2,004 insertions(+), 414 deletions(-)
diff --git a/.gemini/skills/wms-dev-standards/SKILL.md b/.gemini/skills/wms-dev-standards/SKILL.md
new file mode 100644
index 0000000..7be4f2c
--- /dev/null
+++ b/.gemini/skills/wms-dev-standards/SKILL.md
@@ -0,0 +1,62 @@
+---
+name: wms-dev-standards
+description: Guidance for developing the WMS uniapp project. Use this when writing new API requests or developing UI components to ensure consistency with uView UI and existing API request patterns.
+---
+
+# WMS Project Development Standards
+
+This skill outlines the core development standards and patterns to follow when contributing to the WMS uniapp project.
+
+## API Request Pattern
+
+When adding or modifying API requests, always follow the established pattern in `config/api.js`.
+
+### Core Rules
+
+1. **HTTP Client:** Always use `uni.$u.http` for requests.
+2. **Export Format:** Export each API call as a named arrow function.
+3. **GET Parameters:** When sending parameters in a `GET` request, they **must** be wrapped in a configuration object with a `params` key.
+
+### Examples
+
+**Correct GET Request (with parameters):**
+```javascript
+import { http } from '@/config/api.js' // or standard uniapp import
+
+const http = uni.$u.http
+
+// 鐢ㄦ埛鐧诲綍 - Wrap parameters in { params }
+export const login = (params) => http.get('/login.action', { params })
+```
+
+**Correct POST Request:**
+```javascript
+// POST requests pass data directly as the second argument
+export const submitData = (data) => http.post('/submit.action', data)
+```
+
+## UI Component Standards
+
+The project relies exclusively on the **uView UI** library for its components.
+
+### Core Rules
+
+1. **Use uView Components:** Always prefer uView components (e.g., `<u-button>`, `<u-input>`, `<u-form>`) over native components (`<button>`, `<input>`) or writing custom CSS when a uView equivalent exists.
+2. **Consult Available Components:** The project already has `uview-ui` installed. Check the existing component library capabilities before writing custom layouts.
+3. **Styling:** Follow the existing project styling classes and `uni.scss` variables where appropriate.
+
+### Examples
+
+**Correct UI implementation:**
+```vue
+<template>
+ <view class="container">
+ <u--form>
+ <u-form-item label="Username">
+ <u--input v-model="username" placeholder="Enter your username"></u--input>
+ </u-form-item>
+ <u-button type="primary" @click="handleLogin">Login</u-button>
+ </u--form>
+ </view>
+</template>
+```
diff --git a/.trae/skills/nvue/SKILL.md b/.trae/skills/nvue/SKILL.md
new file mode 100644
index 0000000..c470b88
--- /dev/null
+++ b/.trae/skills/nvue/SKILL.md
@@ -0,0 +1,63 @@
+---
+name: "nvue"
+description: "Expert assistant for uni-app nvue (native rendering) development. Invoke when working with .nvue files, native rendering, or performance optimization in uni-app."
+---
+
+# Uni-app nvue Development Skill
+
+You are an expert in uni-app nvue (native vue) development. nvue uses a native rendering engine based on Weex, distinct from the WebView-based Vue pages.
+
+## Core Principles
+1. **Environment**: Assumes latest HBuilderX version.
+2. **Native Rendering**: nvue maps to native components. It is NOT a webview.
+3. **Strict Layout**: ONLY Flexbox is supported.
+4. **Text Rendering**: All text MUST be wrapped in `<text>` components.
+5. **Compilation Mode**: In `uni-app` mode (default), styles from `App.vue` are compiled into every `.nvue` file.
+
+## When to Use nvue
+Recommend nvue for:
+- **High Performance Lists**: `list`, `recycle-list`, `waterfall`.
+- **Complex Interactions**: `BindingX` for high-performance gestures/animations.
+- **Complex Layouts**: Left/right draggable lists, sticky headers + swiper.
+- **Native Component Coverage**: Overcoming z-index issues with `map`, `video`, `live-pusher`.
+- **Fast Startup**: nvue pages load faster than WebView pages.
+
+## CSS Limitations & Rules
+- **Layout**: `display: flex` is default and only option. No `grid`, `block`, `inline-block`.
+- **Box Model**: Defaults to `border-box`.
+- **Text**:
+ - Must use `<text>Content</text>`.
+ - Only `<text>` supports `font-size`, `color`, `lines` (truncation).
+ - `<text>` cannot contain other components.
+- **Styling**:
+ - **Supported Shorthands**: `border`, `border-top` (etc), `border-radius`, `flex-flow`, `background`.
+ - **Supported Selectors**: Class selectors, descendant, child, adjacent sibling, and general sibling selectors.
+ - **Class Binding**: Only array syntax `:class="['a', 'b']"` is supported. Object syntax is NOT supported.
+ - **Units**: No `%` (mostly). Use `px` (logic pixels) or `rpx`.
+ - **Background**: `background-image` is NOT supported in CSS. Use `<image>` component with `position: absolute`.
+ - **Transparency**: Android components are transparent by default. Set `background-color` to avoid ghosting.
+ - **Overflow**: Android only supports `hidden`. iOS supports `hidden` and `visible`.
+
+## API Usage
+- **DOM Module**:
+ - `dom.addRule('fontFace', { fontFamily: '...', src: "url('...')" })`
+ - `dom.scrollToElement(ref, { offset: 0 })`
+ - `dom.getComponentRect(ref, callback)` (Use ref `'viewport'` for screen dims).
+- **Native Plugins**:
+ - `const plugin = uni.requireNativePlugin('PluginName')`
+ - **BindingX**: Built-in for high-performance expression binding.
+ - **Animation**: `uni.requireNativePlugin('animation')`.
+- **Transition**: Supports `width`, `height`, `backgroundColor`, `opacity`, `transform`.
+
+## Event Handling
+- **Events**: `click`, `longpress`, `appear` (view becomes visible), `disappear`.
+ - `appear`/`disappear` provide `direction` (up/down).
+- **Event Penetration (iOS)**: `<view eventPenetrationEnabled="true">` allows clicks to pass through to underlying layers.
+- **User Interaction (iOS)**: `<view userInteractionEnabled="false">` disables all interaction.
+
+## Code Generation Guidelines
+1. Always verify if the file is `.nvue`.
+2. If user asks for CSS that is invalid in nvue (e.g., `background-image`, `display: grid`), STOP and explain the limitation, then provide the nvue-compatible workaround.
+3. Use `<list>` or `<waterfall>` for long lists instead of `<scroll-view>` for better performance.
+4. Ensure all text nodes are strictly inside `<text>`.
+5. Prefer `BindingX` for complex touch interactions.
diff --git a/common/request.js b/common/request.js
deleted file mode 100644
index b3caf91..0000000
--- a/common/request.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
- * @param {string} url url鍦板潃
- * @param {any} postData 鍙傛暟
- * @param {string} method 璇锋眰鏂瑰紡
- * @param {boolean} hideLoading 鏄惁load
- * @description: 鏍煎紡鍖栨椂闂�
- */
-export function request(url, postData, method = 'POST', hideLoading = 'false') {
- if (!hideLoading) {
- uni.showLoading({
- title: '璇风◢鍊�...',
- mask: true
- })
- }
- return new Promise((resolve, reject) => {
- const token = uni.getStorageSync('token');
- // const URL = 'http://47.76.147.249:8080/rsf-server/pda' + url;
- // const URL = 'http://test.zoneyung.net:8080/rsf-server/pda' + url;
- const URL = 'http://127.0.0.1:8080/jshdasrs/' + url;
- uni.request({
- url: URL,
- data: postData,
- header: {
- 'content-type': 'application/json',
- 'token': token
- },
- method: method, //'GET','POST'
- dataType: 'json',
- success: (res) => {
- !hideLoading && uni.hideLoading()
- resolve(res.data)
-
- },
- fail: (res) => {
- // !hideLoading && toast("缃戠粶涓嶇粰鍔涳紝璇风◢鍚庡啀璇晘")
- //wx.hideLoading()
- reject(res)
- }
- })
- })
-}
\ No newline at end of file
diff --git a/config/request.js b/config/request.js
new file mode 100644
index 0000000..9a8beab
--- /dev/null
+++ b/config/request.js
@@ -0,0 +1,86 @@
+// 姝m鍙傛暟涓洪〉闈㈢殑瀹炰緥锛屽彲浠ラ�氳繃瀹冨紩鐢╲uex涓殑鍙橀噺
+module.exports = (vm) => {
+ // 鍒濆鍖栬姹傞厤缃�
+ uni.$u.http.setConfig((config) => {
+ /* config 涓洪粯璁ゅ叏灞�閰嶇疆*/
+ // 鍔ㄦ�佽缃� baseURL
+ let settings = uni.getStorageSync('app_settings');
+ if (!settings) {
+ settings = {
+ ip: '127.0.0.1',
+ port: '8080',
+ project: 'jshdasrs'
+ };
+ // uni.setStorageSync('app_settings', settings);
+ }
+ config.baseURL = `http://${settings.ip}:${settings.port}/${settings.project}`;
+ config.header = {
+ 'content-type': 'application/json'
+ };
+ return config;
+ })
+
+ // 璇锋眰鎷︽埅
+ uni.$u.http.interceptors.request.use((config) => { // 鍙娇鐢╝sync await 鍋氬紓姝ユ搷浣�
+ // 鍒濆鍖栬姹傛嫤鎴櫒鏃讹紝浼氭墽琛屾鏂规硶锛屾鏃禿ata涓簎ndefined锛岃祴浜堥粯璁}
+ config.data = config.data || {}
+
+ // 鎻愮ず鍔犺浇妗嗛�昏緫锛堟牴鎹� custom.hideLoading 鍐冲畾锛�
+ const hideLoading = config.custom?.hideLoading;
+ if (hideLoading === false || hideLoading === undefined) {
+ uni.showLoading({
+ title: '璇风◢鍊�...',
+ mask: true
+ });
+ }
+
+ // 鏍规嵁custom鍙傛暟涓厤缃殑鏄惁闇�瑕乼oken锛屾坊鍔犲搴旂殑璇锋眰澶�
+ // 榛樿鎴栨樉寮� auth 涓� true 鏃舵坊鍔� token
+ if (config?.custom?.auth !== false) {
+ const token = uni.getStorageSync('token');
+ if (token) {
+ config.header.token = token;
+ }
+ }
+ return config
+ }, config => { // 鍙娇鐢╝sync await 鍋氬紓姝ユ搷浣�
+ return Promise.reject(config)
+ })
+
+ // 鍝嶅簲鎷︽埅
+ uni.$u.http.interceptors.response.use((response) => {
+ /* 瀵瑰搷搴旀垚鍔熷仛鐐逛粈涔� 鍙娇鐢╝sync await 鍋氬紓姝ユ搷浣�*/
+ const hideLoading = response.config?.custom?.hideLoading;
+ if (hideLoading === false || hideLoading === undefined) {
+ uni.hideLoading();
+ }
+
+ const data = response.data
+ // 鑷畾涔夊弬鏁�
+ const custom = response.config?.custom || {}
+
+ if (data.code !== 200) {
+ // 濡傛灉娌℃湁鏄惧紡瀹氫箟custom鐨則oast鍙傛暟涓篺alse鐨勮瘽锛岄粯璁ゅ鎶ラ敊杩涜toast寮瑰嚭鎻愮ず
+ if (custom.toast !== false) {
+ uni.$u.toast(data.msg || data.message || '璇锋眰澶辫触')
+ }
+
+ // 濡傛灉闇�瑕乧atch杩斿洖锛屽垯杩涜reject
+ if (custom?.catch) {
+ return Promise.reject(data)
+ } else {
+ // 鍚﹀垯杩斿洖涓�涓猵ending涓殑promise锛岃姹備笉浼氳繘鍏atch涓�
+ return new Promise(() => {})
+ }
+ }
+ // 鏍规嵁绀轰緥锛岃繑鍥炰笟鍔℃暟鎹�
+ return data === undefined ? {} : data
+ }, (response) => {
+ // 瀵瑰搷搴旈敊璇仛鐐逛粈涔� 锛坰tatusCode !== 200锛�
+ const hideLoading = response.config?.custom?.hideLoading;
+ if (hideLoading === false || hideLoading === undefined) {
+ uni.hideLoading();
+ }
+ return Promise.reject(response)
+ })
+}
\ No newline at end of file
diff --git a/locale/en.json b/locale/en.json
index a960e47..0499039 100644
--- a/locale/en.json
+++ b/locale/en.json
@@ -41,5 +41,19 @@
},
"other":{
+ },
+ "settings": {
+ "title": "Settings",
+ "ip": "IP Address",
+ "port": "Port",
+ "project": "Project Name",
+ "saved": "Settings Saved",
+ "authTitle": "Authentication",
+ "inputAuthPwd": "Enter Admin Password",
+ "authError": "Incorrect Password"
+ },
+ "common": {
+ "confirm": "Confirm",
+ "cancel": "Cancel"
}
}
diff --git a/locale/zh-Hans.json b/locale/zh-Hans.json
index ff7223c..05d1cd3 100644
--- a/locale/zh-Hans.json
+++ b/locale/zh-Hans.json
@@ -43,7 +43,19 @@
"other":{
"asnNo":"ASN鍗曞彿"
+ },
+ "settings": {
+ "title": "璁剧疆",
+ "ip": "IP鍦板潃",
+ "port": "绔彛",
+ "project": "椤圭洰鍚�",
+ "saved": "璁剧疆宸蹭繚瀛�",
+ "authTitle": "韬唤楠岃瘉",
+ "inputAuthPwd": "璇疯緭鍏ョ鐞嗗憳瀵嗙爜",
+ "authError": "瀵嗙爜閿欒"
+ },
+ "common": {
+ "confirm": "纭",
+ "cancel": "鍙栨秷"
}
-
-
}
diff --git a/main.js b/main.js
index 6e0bb1c..c04e6b7 100644
--- a/main.js
+++ b/main.js
@@ -11,27 +11,35 @@
import './uni.promisify.adaptor'
Vue.config.productionTip = false
App.mpType = 'app'
+
import VueI18n from 'vue-i18n'
Vue.use(VueI18n)
+
+import uView from '@/uni_modules/uview-ui'
+Vue.use(uView)
+
const i18n = new VueI18n(i18nConfig)
const app = new Vue({
...App,
i18n
})
+
+// 寮曞叆璇锋眰灏佽锛屽皢app鍙傛暟浼犻�掑埌閰嶇疆涓�
+require('./config/request.js')(app)
+
app.$mount()
// #endif
-
-import uView from '@/uni_modules/uview-ui'
-Vue.use(uView)
// #ifdef VUE3
import {
createSSRApp
} from 'vue'
+import uView from '@/uni_modules/uview-ui'
export function createApp() {
const app = createSSRApp(App)
+ app.use(uView)
return {
app
}
}
-// #endif
\ No newline at end of file
+// #endif
diff --git a/pages.json b/pages.json
index 7a73fbd..68e489c 100644
--- a/pages.json
+++ b/pages.json
@@ -1,6 +1,5 @@
{
"pages": [ //pages鏁扮粍涓涓�椤硅〃绀哄簲鐢ㄥ惎鍔ㄩ〉锛屽弬鑰冿細https://uniapp.dcloud.io/collocation/pages
-
{
"path": "pages/login/login",
"style": {
@@ -9,10 +8,11 @@
}
},
{
- "path": "pages/index/index",
+ "path": "pages/home/home",
"style": {
"navigationBarTitleText": "棣栭〉",
"navigationStyle": "custom"
+
}
},
{
diff --git a/pages/home/api.js b/pages/home/api.js
new file mode 100644
index 0000000..a4584cc
--- /dev/null
+++ b/pages/home/api.js
@@ -0,0 +1,4 @@
+const http = uni.$u.http
+
+// 鑾峰彇鍔ㄦ�佽彍鍗�
+export const getAuthMenu = (data, config = {}) => http.post('/menu/pda/auth', data, config)
diff --git a/pages/home/home.vue b/pages/home/home.vue
new file mode 100644
index 0000000..4d8ad18
--- /dev/null
+++ b/pages/home/home.vue
@@ -0,0 +1,579 @@
+<template>
+ <view class="page-container">
+ <!-- 澶撮儴瀵艰埅 -->
+ <u-navbar
+ title="Zoneyung 宸ヤ綔鍙�"
+ :fixed="true"
+ :placeholder="true"
+ bgColor="#ffffff"
+ titleStyle="font-weight: 600; color: #303133; font-size: 32rpx;"
+ leftIcon=""
+ ></u-navbar>
+
+ <!-- 娆㈣繋淇℃伅 / 鐢ㄦ埛淇℃伅 -->
+ <view class="welcome-box">
+ <view class="welcome-info">
+ <text class="greeting">娆㈣繋鍥炴潵</text>
+ <text class="date-text">涓壃绔嬪簱 WMS 浠撳偍绠$悊绯荤粺</text>
+ </view>
+ <view class="welcome-avatar">
+ <!-- 鍙浛鎹负鐪熷疄澶村儚 -->
+ <u-avatar
+ icon="account-fill"
+ fontSize="24"
+ randomBgColor
+ size="45"
+ ></u-avatar>
+ </view>
+ </view>
+
+ <!-- 鍔熻兘鑿滃崟 -->
+ <view class="menu-section">
+ <!-- 鍒嗙被妯″紡 -->
+ <template v-if="useCategoryMode">
+ <view
+ class="category-panel"
+ v-for="(category, catIndex) in categorizedMenus"
+ :key="category.key"
+ >
+ <view
+ class="category-header"
+ @click="toggleCategory(category.key)"
+ >
+ <view class="category-title-wrapper">
+ <view class="title-indicator"></view>
+ <text class="category-title">
+ {{ category.name }}
+ </text>
+ </view>
+ <view class="category-action">
+ <uni-icons
+ :type="
+ expandedCategories.includes(category.key)
+ ? 'up'
+ : 'down'
+ "
+ size="16"
+ color="#909399"
+ ></uni-icons>
+ </view>
+ </view>
+
+ <view
+ class="category-body"
+ v-show="expandedCategories.includes(category.key)"
+ >
+ <u-grid
+ :col="4"
+ :border="false"
+ >
+ <u-grid-item
+ v-for="(item, index) in category.items"
+ :key="index"
+ @click="navigateTo(item)"
+ customStyle="padding-top: 15px; padding-bottom: 15px;"
+ >
+ <view
+ class="grid-icon-box"
+ :style="{
+ backgroundColor: getBgColor(item.color)
+ }"
+ >
+ <uni-icons
+ :type="getIconType(item)"
+ size="24"
+ color="#ffffff"
+ ></uni-icons>
+ </view>
+ <text class="grid-text">{{ item.title }}</text>
+ </u-grid-item>
+ </u-grid>
+ </view>
+ </view>
+
+ <!-- 鏈垎绫昏彍鍗� -->
+ <view
+ class="category-panel"
+ v-if="uncategorizedMenus.length > 0"
+ >
+ <view class="category-header">
+ <view class="category-title-wrapper">
+ <view class="title-indicator"></view>
+ <text class="category-title">鍏朵粬鎿嶄綔</text>
+ </view>
+ </view>
+ <view class="category-body">
+ <u-grid
+ :col="4"
+ :border="false"
+ >
+ <u-grid-item
+ v-for="(item, index) in uncategorizedMenus"
+ :key="index"
+ @click="navigateTo(item)"
+ customStyle="padding-top: 15px; padding-bottom: 15px;"
+ >
+ <view
+ class="grid-icon-box"
+ :style="{
+ backgroundColor: getBgColor(item.color)
+ }"
+ >
+ <uni-icons
+ :type="getIconType(item)"
+ size="24"
+ color="#ffffff"
+ ></uni-icons>
+ </view>
+ <text class="grid-text">{{ item.title }}</text>
+ </u-grid-item>
+ </u-grid>
+ </view>
+ </view>
+ </template>
+
+ <!-- 骞抽摵妯″紡 -->
+ <template v-else>
+ <view class="category-panel">
+ <view class="category-body">
+ <u-grid
+ :col="4"
+ :border="false"
+ >
+ <u-grid-item
+ v-for="(item, index) in elements"
+ :key="index"
+ @click="navigateTo(item)"
+ customStyle="padding-top: 15px; padding-bottom: 15px;"
+ >
+ <view
+ class="grid-icon-box"
+ :style="{
+ backgroundColor: getBgColor(item.color)
+ }"
+ >
+ <uni-icons
+ :type="getIconType(item)"
+ size="24"
+ color="#ffffff"
+ ></uni-icons>
+ </view>
+ <text class="grid-text">{{ item.title }}</text>
+ </u-grid-item>
+ </u-grid>
+ </view>
+ </view>
+ </template>
+ </view>
+
+ <!-- 搴曢儴鐗堟潈 -->
+ <view class="footer">
+ <text class="footer-text">
+ copyright 漏 2022 娴欐睙涓壃绔嬪簱鏈夐檺鍏徃 all rights reserved.
+ </text>
+ </view>
+ </view>
+</template>
+
+<script>
+import { getAuthMenu } from './api.js'
+
+export default {
+ data() {
+ return {
+ baseUrl: '',
+ token: '',
+ icon: '',
+ elements: [],
+ // 鏄惁浣跨敤鍒嗙被妯″紡锛坱rue: 涓婁笅灞傛姌鍙犺彍鍗曟ā寮�, false: 骞抽摵鑿滃崟妯″紡锛�
+ useCategoryMode: true,
+ // 榛樿鑿滃崟鏁版嵁锛堢敤浜庡悗绔棤杩斿洖鏃讹級
+ elements2: [
+ {
+ title: '缁勬墭',
+ name: 'pakin',
+ color: 'cyan',
+ cuIcon: 'pullup',
+ url: '/pakin/pakin'
+ },
+ {
+ title: '璁㈠崟鍏ュ簱',
+ name: 'orderPakin',
+ color: 'purple',
+ cuIcon: '',
+ url: '/order/orderList'
+ },
+ {
+ title: 'AGV瀹瑰櫒鍏ュ簱',
+ name: 'agv_start',
+ color: 'blue',
+ cuIcon: '',
+ url: '/AGV/agv_start'
+ },
+ {
+ title: 'AGV瀹瑰櫒鍥炲簱',
+ name: 'agv_back',
+ color: 'orange',
+ cuIcon: '',
+ url: '/AGV/agv_back'
+ },
+ {
+ title: '搴撳瓨鏌ヨ',
+ name: 'stockQuery',
+ color: 'green',
+ cuIcon: '',
+ url: '/stock/stockQuery'
+ },
+ {
+ title: '閫�鍑虹櫥褰�',
+ name: 'logOut',
+ color: 'grey',
+ cuIcon: 'exit',
+ url: '/login/logOut'
+ }
+ ],
+ colorList: [
+ 'cyan',
+ 'purple',
+ 'blue',
+ 'pink',
+ 'orange',
+ 'green',
+ 'mauve',
+ 'brown',
+ 'olive',
+ 'red',
+ 'yellow',
+ 'grey'
+ ],
+ // 鍥炬爣鏄犲皠
+ iconMap: {
+ pakin: 'upload',
+ orderPakin: 'list',
+ stockQuery: 'search',
+ logOut: 'redo',
+ orderPutOn: 'top',
+ orderPutDown: 'bottom',
+ restock: 'refresh',
+ stockCheck: 'checkbox',
+ agv_start: 'navigate',
+ agv_back: 'refresh'
+ },
+ // 鑿滃崟鍒嗙被閰嶇疆锛堜粎鍒嗙被妯″紡浣跨敤锛�
+ // key: 鍒嗙被鍞竴鏍囪瘑
+ // name: 鍒嗙被鏄剧ず鍚嶇О
+ // color: 鍒嗙被鍥炬爣棰滆壊
+ // icon: 鍒嗙被鍥炬爣
+ // menuNames: 璇ュ垎绫诲寘鍚殑鑿滃崟name鍒楄〃
+ categoryConfig: [
+ {
+ key: 'pakin_category',
+ name: '鍏ュ簱绠$悊',
+ color: 'cyan',
+ icon: 'upload',
+ menuNames: ['pakin', 'orderPakin']
+ },
+ {
+ key: 'agv_category',
+ name: 'AGV绠$悊',
+ color: 'blue',
+ icon: 'navigate',
+ menuNames: ['agv_start', 'agv_back']
+ },
+ {
+ key: 'stock_category',
+ name: '搴撳瓨绠$悊',
+ color: 'green',
+ icon: 'search',
+ menuNames: ['stockQuery']
+ }
+ ],
+ // 涓嶅弬涓庡垎绫荤殑鑿滃崟椤癸紙濡傞��鍑虹櫥褰曪紝濮嬬粓鍗曠嫭鏄剧ず锛�
+ excludeFromCategory: ['logOut'],
+ // 褰撳墠灞曞紑鐨勫垎绫诲垪琛�
+ expandedCategories: [
+ 'pakin_category',
+ 'agv_category',
+ 'stock_category'
+ ]
+ }
+ },
+ computed: {
+ // 鍒嗙被鍚庣殑鑿滃崟鏁版嵁锛堣繃婊ゆ帀绌哄垎绫伙級
+ categorizedMenus() {
+ return this.categoryConfig
+ .map((category) => {
+ const items = this.elements.filter((item) =>
+ category.menuNames.includes(item.name)
+ )
+ return {
+ ...category,
+ items: items
+ }
+ })
+ .filter((category) => category.items.length > 0)
+ },
+ // 鏈垎绫荤殑鑿滃崟锛堜笉鍦ㄤ换浣曞垎绫讳腑鎴栧湪鎺掗櫎鍒楄〃涓級
+ uncategorizedMenus() {
+ const allCategorizedNames = this.categoryConfig.reduce(
+ (acc, c) => acc.concat(c.menuNames || []),
+ []
+ )
+ return this.elements.filter(
+ (item) =>
+ !allCategorizedNames.includes(item.name) ||
+ this.excludeFromCategory.includes(item.name)
+ )
+ }
+ },
+ onShow() {
+ this.baseUrl = uni.getStorageSync('baseUrl')
+ this.token = uni.getStorageSync('token')
+ this.getAuth()
+ },
+ methods: {
+ // 鍒囨崲鍒嗙被灞曞紑/鏀惰捣
+ toggleCategory(categoryKey) {
+ const index = this.expandedCategories.indexOf(categoryKey)
+ if (index > -1) {
+ this.expandedCategories.splice(index, 1)
+ } else {
+ this.expandedCategories.push(categoryKey)
+ }
+ },
+ // 鑾峰彇鍥炬爣绫诲瀷
+ getIconType(item) {
+ return this.iconMap[item.name] || 'circle'
+ },
+ // 棰滆壊鏄犲皠鍒板浘鐗囦笂鐨勪紒涓氳壊
+ getBgColor(colorStr) {
+ const map = {
+ cyan: '#00ced1',
+ purple: '#8a2be2',
+ blue: '#409eff',
+ pink: '#ff49db',
+ orange: '#e6a23c',
+ green: '#67c23a',
+ mauve: '#b070cc',
+ brown: '#874d28',
+ olive: '#8e9e30',
+ red: '#f56c6c',
+ yellow: '#e6a23c',
+ grey: '#909399'
+ }
+ return map[colorStr] || '#409eff'
+ },
+ // 璺宠浆椤甸潰
+ navigateTo(item) {
+ if (item.name === 'logOut') {
+ uni.reLaunch({
+ url: '/pages/login/login'
+ })
+ return
+ }
+ uni.navigateTo({
+ url: '/pages' + item.url
+ })
+ },
+ async getAuth() {
+ try {
+ // custom.catch=true ensures errors throw into the catch block
+ // custom.toast=false disables the interceptor's default toast so we can handle it manually
+ const res = await getAuthMenu(
+ {},
+ { custom: { catch: true, toast: false } }
+ )
+ this.elements = []
+
+ if (
+ res.data == undefined ||
+ res.data == null ||
+ res.data === ''
+ ) {
+ this.elements = this.elements2
+ return
+ }
+
+ for (let i = 0; i < res.data.length; i++) {
+ this.getIcon(res.data[i].title)
+ this.elements.unshift({
+ title: res.data[i].name,
+ name: res.data[i].title,
+ color: this.colorList[i % this.colorList.length],
+ cuIcon: this.icon,
+ url: res.data[i].action
+ })
+ }
+
+ this.elements.push({
+ title: '閫�鍑虹櫥褰�',
+ name: 'logOut',
+ color: 'grey',
+ cuIcon: 'exit',
+ url: '/login/logOut'
+ })
+ } catch (err) {
+ console.log('getAuth error:', err)
+ this.elements = this.elements2 // Fallback to default menus
+
+ const errCode = err.code || err.statusCode
+ const errMsg =
+ err.msg ||
+ err.message ||
+ (err.data && (err.data.msg || err.data.message))
+
+ if (errCode === 403) {
+ uni.showToast({
+ title: errMsg || '鏃犳潈闄愭垨鐧诲綍杩囨湡',
+ icon: 'none',
+ position: 'top'
+ })
+ setTimeout(() => {
+ uni.reLaunch({
+ url: '../login/login'
+ })
+ }, 1000)
+ } else {
+ if (errMsg) {
+ uni.showToast({
+ title: errMsg,
+ icon: 'none',
+ position: 'top'
+ })
+ }
+ }
+ }
+ },
+ getIcon(e) {
+ const ways = ['pakin', 'orderPakin', 'orderPutOn']
+ if (ways.includes(e)) {
+ this.icon = 'pullup'
+ }
+ }
+ }
+}
+</script>
+
+<style>
+page {
+ background: #f0f2f5; /* 绫讳技鍥剧墖涓祬鐏扮櫧鐨勫伐浣滃尯鑳屾櫙 */
+}
+
+.page-container {
+ min-height: 100vh;
+ background-color: #f0f2f5;
+}
+
+/* 娆㈣繋鍖哄煙 */
+.welcome-box {
+ background-color: #ffffff;
+ padding: 40rpx 40rpx;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 24rpx;
+ border-bottom: 1px solid #ebeef5; /* 杞诲井杈规 */
+}
+
+.welcome-info {
+ display: flex;
+ flex-direction: column;
+}
+
+.greeting {
+ font-size: 36rpx;
+ color: #303133;
+ font-weight: 600;
+ margin-bottom: 8rpx;
+}
+
+.date-text {
+ font-size: 26rpx;
+ color: #909399;
+}
+
+/* 鑿滃崟鍖哄煙 */
+.menu-section {
+ padding: 0 24rpx;
+}
+
+/* 鍒嗙被闈㈡澘椋庢牸锛氭磥鐧斤紝杞婚噺闃村奖/杈规 */
+.category-panel {
+ background-color: #ffffff;
+ border-radius: 12rpx;
+ margin-bottom: 24rpx;
+ overflow: hidden;
+ box-shadow: 0 2rpx 12rpx 0 rgba(0, 0, 0, 0.05); /* Element UI 甯哥敤娴呴槾褰� */
+}
+
+.category-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 30rpx 24rpx;
+ border-bottom: 1px solid #ebeef5;
+ background-color: #fafafa; /* 杞诲井鑳屾櫙鍖哄垎 */
+}
+
+.category-title-wrapper {
+ display: flex;
+ align-items: center;
+}
+
+/* 鏍囬宸︿晶鍨傜洿钃濊壊鎸囩ず绾� */
+.title-indicator {
+ width: 6rpx;
+ height: 30rpx;
+ background-color: #409eff;
+ border-radius: 4rpx;
+ margin-right: 16rpx;
+}
+
+.category-title {
+ font-size: 30rpx;
+ color: #303133;
+ font-weight: 600;
+}
+
+.category-action {
+ display: flex;
+ align-items: center;
+}
+
+.category-body {
+ padding: 10rpx 0;
+}
+
+/* 鑿滃崟涔濆鏍奸」 */
+.grid-icon-box {
+ width: 88rpx;
+ height: 88rpx;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-bottom: 16rpx;
+ transition: transform 0.2s;
+}
+
+.grid-icon-box:active {
+ transform: scale(0.9);
+}
+
+.grid-text {
+ font-size: 26rpx;
+ color: #606266;
+ margin-top: 10rpx;
+}
+
+/* 搴曢儴 */
+.footer {
+ padding: 40rpx 0;
+ text-align: center;
+}
+
+.footer-text {
+ font-size: 24rpx;
+ color: #c0c4cc;
+}
+</style>
diff --git a/pages/index/index.vue b/pages/index/index.vue
index 729371f..4027174 100644
--- a/pages/index/index.vue
+++ b/pages/index/index.vue
@@ -1,24 +1,25 @@
<template>
<div>
- <uni-nav-bar :right-text="userName" title="棣栭〉" />
+ <uni-nav-bar
+ :right-text="userName"
+ title="棣栭〉"
+ />
</div>
</template>
<script>
- export default {
- data() {
- return {
- userName: '123'
- };
- },
- onLoad(obj) {
- console.log(obj.name)
- this.userName = uni.getStorageSync('userData')
- },
- methods: {}
- }
+export default {
+ data() {
+ return {
+ userName: '123'
+ }
+ },
+ onLoad(obj) {
+ console.log(obj.name)
+ this.userName = uni.getStorageSync('userData')
+ },
+ methods: {}
+}
</script>
-<style>
-
-</style>
\ No newline at end of file
+<style></style>
diff --git a/pages/login/api.js b/pages/login/api.js
new file mode 100644
index 0000000..7fe5bc1
--- /dev/null
+++ b/pages/login/api.js
@@ -0,0 +1,7 @@
+const http = uni.$u.http
+
+// 鐢ㄦ埛鐧诲綍
+export const login = (params) => http.get('/login.action', { params })
+
+// 杩欓噷鍙互缁х画娣诲姞鍏朵粬鎺ュ彛
+// export const getInfo = (data) => http.get('/user/info', data)
\ No newline at end of file
diff --git a/pages/login/index.nvue b/pages/login/index.nvue
new file mode 100644
index 0000000..01709d2
--- /dev/null
+++ b/pages/login/index.nvue
@@ -0,0 +1,559 @@
+<template>
+ <view class="bodyView">
+ <view>
+ <uni-nav-bar :fixed="true" :statusBar="true" :title="$t('page.login')" right-icon="gear" @clickRight="openSettings"></uni-nav-bar>
+ </view>
+ <!-- <image class="bgImage" src="/static/img/login_backg.png" mode="aspectFill"></image> -->
+ <view class="topView">
+ <image src="/static/img/login_top.png" mode="aspectFill">
+ </image>
+ </view>
+ <view class="logoView">
+ <image src="/static/img/newLogo.png" mode="aspectFit">
+ </image>
+ </view>
+ <view class="bottomView">
+ <view class="itemView" style="margin-bottom: 20px;">
+ <text class="helloText">{{$t('index.hello')}}</text>
+ <text class="introText">{{$t('index.intro')}}</text>
+ </view>
+ <view class="itemView">
+ <text class="textType3">{{$t('login.user')}}:</text>
+ <u-input clearable class="" v-model="user.userName" focus :placeholder="$t('login.inputUser')" />
+ </view>
+ <view class="itemView">
+ <text class="textType3">{{$t('login.pwd')}}:</text>
+
+ <u-input :password="showPassword" v-model="user.password" :placeholder="$t('login.inputPwd')"
+ suffixIcon="map-fill" suffixIconStyle="color: #909399">
+ <template slot="suffix">
+ <u-icon :name="pwdIcon" @click="changePassword"></u-icon>
+ </template>
+ </u-input>
+
+
+ </view>
+ <view class="langAndRemView">
+ <view class="check">
+ <view>
+ <text>{{$t('login.remPwd')}}</text>
+ </view>
+ <view>
+ <u-switch space="2" size="20" v-model="remberPassword" activeColor="#f9ae3d"
+ inactiveColor="rgb(230, 230, 230)">
+ </u-switch>
+ <!-- <switch :checked='remberPassword' color="#FFCC33" style="transform:scale(0.7)"
+ @change="remberChange" /> -->
+ </view>
+ </view>
+ <view class="langView">
+ <!-- 璇█閫夋嫨涓嬫媺鑿滃崟 -->
+ <view class="language-dropdown">
+ <view class="selected-language" @click="toggleLanguageDropdown">
+ <text>{{getCurrentLanguageText()}}</text>
+ <u-icon name="list" size="14" color="#707070"></u-icon>
+ </view>
+ <view class="language-options" v-if="showLanguageDropdown">
+ <view class="language-option" v-for="(item, index) in locales" :key="index"
+ @click="onLocaleChange(item)">
+ <text>{{item.text}}</text>
+ <u-icon name="checkbox-mark" color="#007AFF"
+ v-if="item.code == applicationLocale"></u-icon>
+ </view>
+ </view>
+ </view>
+ </view>
+ </view>
+ <view class="itemView">
+ <u-button class="loadingButton" @click="onLogin()" :loading="loading">{{btnText}}</u-button>
+ </view>
+ </view>
+ <u-popup :show="showAuth" @close="showAuth = false" mode="center" :round="14" :customStyle="{width: '500rpx'}">
+ <view class="settings-popup">
+ <view class="settings-title">
+ <text class="settings-title-text">{{$t('settings.authTitle') || '韬唤楠岃瘉'}}</text>
+ </view>
+ <view class="settings-item">
+ <u-input v-model="authPassword" type="password" border="surround" :placeholder="$t('settings.inputAuthPwd') || '璇疯緭鍏ョ鐞嗗憳瀵嗙爜'" />
+ </view>
+ <view class="settings-buttons">
+ <u-button size="medium" @click="showAuth = false">{{$t('common.cancel') || '鍙栨秷'}}</u-button>
+ <view style="width: 20px;"></view>
+ <u-button size="medium" type="primary" @click="checkAuth">{{$t('common.confirm') || '纭'}}</u-button>
+ </view>
+ </view>
+ </u-popup>
+ <u-popup :show="showSettings" @close="showSettings = false" mode="center" :round="14" :closeOnClickOverlay="false" :customStyle="{width: '600rpx'}">
+ <view class="settings-popup">
+ <view class="settings-title">
+ <text class="settings-title-text">{{$t('settings.title') || '璁剧疆'}}</text>
+ </view>
+ <view class="settings-item">
+ <text class="settings-label">{{$t('settings.ip') || 'IP鍦板潃'}}:</text>
+ <u-input v-model="settings.ip" border="surround" />
+ </view>
+ <view class="settings-item">
+ <text class="settings-label">{{$t('settings.port') || '绔彛'}}:</text>
+ <u-input v-model="settings.port" border="surround" />
+ </view>
+ <view class="settings-item">
+ <text class="settings-label">{{$t('settings.project') || '椤圭洰鍚�'}}:</text>
+ <u-input v-model="settings.project" border="surround" />
+ </view>
+ <view class="settings-buttons">
+ <u-button size="medium" @click="showSettings = false">{{$t('common.cancel') || '鍙栨秷'}}</u-button>
+ <view style="width: 20px;"></view>
+ <u-button size="medium" type="primary" @click="saveSettings">{{$t('common.confirm') || '纭'}}</u-button>
+ </view>
+ </view>
+ </u-popup>
+ <u-toast ref="uToast"></u-toast>
+ </view>
+</template>
+
+<script>
+ import md5 from '../../static/js/md5.js'
+ import { login } from './api.js'
+
+ export default {
+ data() {
+ return {
+ showPassword: true,
+ loading: false,
+ showLanguageDropdown: false,
+ loginButton: 'login.login',
+ systemLocale: '',
+ applicationLocale: '',
+ remberPassword: true,
+ user: {
+ userName: '',
+ password: '',
+ },
+ passwordIcon: 'eye-off',
+ showSettings: false,
+ settings: {
+ ip: '',
+ port: '',
+ project: ''
+ },
+ showAuth: false,
+ authPassword: ''
+ }
+ },
+ computed: {
+ locales() {
+ return [{
+ text: this.$t('locale.auto'),
+ code: 'auto'
+ }, {
+ text: this.$t('locale.en'),
+ code: 'en'
+ },
+ {
+ text: this.$t('locale.zh-hans'),
+ code: 'zh-Hans'
+ },
+ {
+ text: this.$t('locale.zh-hant'),
+ code: 'zh-Hant'
+ },
+ {
+ text: this.$t('locale.ja'),
+ code: 'ja'
+ }
+ ]
+ },
+ btnText() {
+ return this.$t(this.loginButton);
+ },
+ pwdIcon() {
+ return this.passwordIcon;
+ }
+ },
+ onLoad() {
+ let systemInfo = uni.getSystemInfoSync();
+ this.systemLocale = systemInfo.language;
+ this.applicationLocale = uni.getLocale();
+ this.isAndroid = systemInfo.platform.toLowerCase() === 'android';
+ uni.onLocaleChange((e) => {
+ this.applicationLocale = e.locale;
+ })
+
+ this.user = uni.getStorageSync('user')
+ if (!this.user) {
+ this.user = {
+ userName: '',
+ password: ''
+ }
+ }
+ },
+ methods: {
+ openSettings() {
+ this.showAuth = true;
+ this.authPassword = '';
+ },
+ checkAuth() {
+ // Default password: admin or 123456. Ideally from config.
+ if (this.authPassword === 'admin' || this.authPassword === '123456') {
+ this.showAuth = false;
+ this.loadSettings();
+ } else {
+ this.$refs.uToast.show({
+ type: 'error',
+ message: this.$t('settings.authError') || '瀵嗙爜閿欒',
+ });
+ }
+ },
+ loadSettings() {
+ this.showSettings = true;
+ let settings = uni.getStorageSync('app_settings');
+ if (!settings) {
+ settings = {
+ ip: '127.0.0.1',
+ port: '8080',
+ project: 'jshdasrs'
+ };
+ }
+ this.settings = settings;
+ },
+ saveSettings() {
+ uni.setStorageSync('app_settings', this.settings);
+ this.showSettings = false;
+ this.$refs.uToast.show({
+ type: 'success',
+ message: this.$t('settings.saved') || '璁剧疆宸蹭繚瀛�',
+ });
+ },
+ async onLogin() {
+ try {
+ const res = await login({
+ username: this.user.userName,
+ password: md5.hex_md5(this.user.password)
+ }, {
+ custom: {
+ catch: true
+ }
+ })
+
+ this.loading = true;
+ this.loginButton = 'login.loging';
+ uni.setStorageSync('token', res.data.accessToken);
+ uni.setStorageSync('userData', res.data.username);
+ if (this.remberPassword) {
+ uni.setStorageSync('user', this.user);
+ } else {
+ uni.removeStorageSync('user');
+ }
+ this.goHome()
+ } catch (e) {
+ // 鎷︽埅鍣ㄥ凡澶勭悊 toast
+ }
+ },
+ goHome() {
+ setTimeout(() => {
+ this.$refs.uToast.show({
+ type: 'success',
+ message: "鐧诲綍鎴愬姛",
+ position: 'top'
+
+ });
+ setTimeout(() => {
+ uni.$u.route({
+ url: 'pages/index/index',
+ params: {
+ name: 'lisa'
+ }
+ })
+
+ }, 300)
+ }, 700)
+ },
+ remberChange(e) {
+ this.remberPassword = !this.remberPassword
+ },
+ // 鏄剧ず/闅愯棌瀵嗙爜
+ changePassword() {
+ this.passwordIcon = !this.showPassword ? 'eye-off' : 'eye'
+ this.showPassword = !this.showPassword;
+ },
+ localChange() {
+ console.log(this.local)
+ if (this.isAndroid) {
+ uni.showModal({
+ content: this.$t('index.language-change-confirm'),
+ success: (res) => {
+ if (res.confirm) {
+ uni.setLocale(this.local.value);
+ }
+ }
+ })
+ } else {
+ uni.setLocale(this.local.value);
+ this.$i18n.locale = this.local.value;
+ }
+ },
+ // 鍒囨崲璇█涓嬫媺鑿滃崟鏄剧ず鐘舵��
+ toggleLanguageDropdown() {
+ this.showLanguageDropdown = !this.showLanguageDropdown;
+ },
+
+ // 鑾峰彇褰撳墠閫夋嫨鐨勮瑷�鏂囨湰
+ getCurrentLanguageText() {
+ const currentLocale = this.locales.find(item => item.code === this.applicationLocale);
+ return currentLocale ? currentLocale.text : this.$t('locale.auto');
+ },
+
+ // 璇█閫夋嫨鏀瑰彉
+ onLocaleChange(e) {
+ if (this.isAndroid) {
+ uni.showModal({
+ content: this.$t('index.language-change-confirm'),
+ success: (res) => {
+ if (res.confirm) {
+ uni.setLocale(e.code);
+ this.showLanguageDropdown = false;
+ }
+ }
+ })
+ } else {
+ uni.setLocale(e.code);
+ this.$i18n.locale = e.code;
+ this.showLanguageDropdown = false;
+ }
+ },
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ .helloText {
+ font-family: a2;
+ font-size: 20px;
+ margin-top: 10px;
+ margin-bottom: 5px;
+ }
+
+ .introText {
+ font-family: a3;
+ font-size: 15px;
+ color: #ababab;
+ }
+
+ .textType3 {
+ font-family: a4;
+ }
+
+ .bodyView {
+ flex: 1;
+ flex-direction: column;
+ width: 750rpx;
+ }
+
+ .bgImage {
+ position: fixed;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+ }
+
+ .topView {
+ flex: 7;
+ }
+
+ .topView image {
+ width: 100%;
+ }
+
+ .logoView {
+ flex: 1;
+ display: flex;
+ justify-content: flex-end;
+ align-items: flex-end;
+
+ }
+
+ .logoView image {
+ width: 33%;
+ height: 50px;
+ margin-right: 20px;
+ }
+
+ .bottomView {
+ flex: 15;
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+ align-items: center;
+ }
+
+ .itemView {
+ width: 90%;
+ margin-bottom: 5px;
+ }
+
+ .langAndRemView {
+ width: 90%;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 5px;
+ }
+
+ .langView {
+ width: 30%;
+ }
+
+ .textImage {
+ width: 60%;
+ height: 42px;
+ object-fit: cover;
+ margin-top: 20px;
+ margin-bottom: 30px;
+ }
+
+ .input-wrapper {
+ /* #ifndef APP-NVUE */
+ display: flex;
+ /* #endif */
+ padding: 8px 13px;
+ flex-direction: row;
+ flex-wrap: nowrap;
+ background-color: #FFFFFF;
+ border-radius: 10px;
+ height: 45px;
+ align-items: center;
+ margin-top: 5px;
+ }
+
+ .uni-input {
+ height: 28px;
+ line-height: 28px;
+ font-size: 15px;
+ padding: 0px;
+ flex: 1;
+ background-color: #FFFFFF;
+
+ }
+
+ .uni-icon {
+ font-family: uniicons;
+ font-size: 24px;
+ font-weight: normal;
+ font-style: normal;
+ width: 24px;
+ height: 24px;
+ line-height: 24px;
+ color: #999999;
+ }
+
+ .uni-eye-active {
+ color: #007AFF;
+ }
+
+ .eye-icon {
+ width: 20px;
+ height: 13px;
+ margin-left: 5px;
+ }
+
+ .loadingButton {
+ background-color: #ffda1e;
+ font-family: a1;
+ }
+
+ .check {
+
+ height: 100%;
+ display: flex;
+ flex-direction: row;
+ font-size: 18px;
+ color: #606266;
+ justify-content: flex-start;
+ align-items: center;
+
+ }
+
+ /* 璇█閫夋嫨涓嬫媺鑿滃崟 */
+ .language-dropdown {
+ position: relative;
+ margin-bottom: 20rpx;
+ z-index: 10;
+ }
+
+ .selected-language {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ padding: 15rpx 20rpx;
+ background-color: #f8f8f8;
+ border-radius: 8rpx;
+ border: 1px solid #e0e0e0;
+ }
+
+ .language-options {
+ position: absolute;
+ bottom: 100%;
+ left: 0;
+ right: 0;
+ background-color: #ffffff;
+ border-radius: 8rpx;
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+ border: 1px solid #e0e0e0;
+ margin-bottom: 5rpx;
+ }
+
+ .language-option {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ padding: 15rpx 20rpx;
+ border-bottom: 1px solid #f0f0f0;
+ }
+
+ .language-option:last-child {
+ border-bottom: none;
+ }
+
+ .language-option:active {
+ background-color: #f5f5f5;
+ }
+
+ .settings-popup {
+ padding: 20px;
+ background-color: #ffffff;
+ border-radius: 10px;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .settings-title {
+ margin-bottom: 20px;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .settings-title-text {
+ font-size: 18px;
+ font-weight: bold;
+ }
+
+ .settings-item {
+ margin-bottom: 15px;
+ }
+
+ .settings-label {
+ margin-bottom: 5px;
+ font-size: 14px;
+ color: #606266;
+ }
+
+ .settings-buttons {
+ flex-direction: row;
+ justify-content: center;
+ margin-top: 10px;
+ }
+</style>
\ No newline at end of file
diff --git a/pages/login/login.vue b/pages/login/login.vue
index a0d860e..f015b1e 100644
--- a/pages/login/login.vue
+++ b/pages/login/login.vue
@@ -1,437 +1,673 @@
<template>
<view class="bodyView">
+ <view>
+ <u-navbar
+ :fixed="true"
+ :title="$t('page.login')"
+ rightIcon="setting"
+ left-icon=""
+ :placeholder="true"
+ @rightClick="openSettings"
+ ></u-navbar>
+ </view>
<view class="topView">
- <image src="/static/img/login_top.png" mode="aspectFill">
- </image>
+ <u--image
+ width="100%"
+ src="/static/img/login_top.png"
+ mode="aspectFill"
+ ></u--image>
</view>
<view class="logoView">
- <image src="/static/img/newLogo.png" mode="aspectFit">
- </image>
+ <image
+ src="/static/img/newLogo.png"
+ mode="aspectFit"
+ ></image>
</view>
<view class="bottomView">
- <view class="itemView" style="margin-bottom: 40px;">
- <view class="helloText">{{$t('index.hello')}}</view>
- <view class="introText">{{$t('index.intro')}}</view>
+ <view
+ class="itemView"
+ style="margin-bottom: 20px"
+ >
+ <view class="helloText">{{ $t('index.hello') }}</view>
+ <view class="introText">{{ $t('index.intro') }}</view>
</view>
<view class="itemView">
- <view class="textType3">{{$t('login.user')}}:</view>
- <u-input clearable class="" v-model="user.userName" focus :placeholder="$t('login.inputUser')" />
+ <text class="textType3">{{ $t('login.user') }}:</text>
+ <u-input
+ clearable
+ class=""
+ v-model="user.userName"
+ focus
+ :placeholder="$t('login.inputUser')"
+ />
</view>
<view class="itemView">
- <view class="textType3">{{$t('login.pwd')}}:</view>
+ <text class="textType3">{{ $t('login.pwd') }}:</text>
- <u-input :password="showPassword" v-model="user.password" :placeholder="$t('login.inputPwd')"
- suffixIcon="map-fill" suffixIconStyle="color: #909399">
+ <u-input
+ :password="showPassword"
+ v-model="user.password"
+ :placeholder="$t('login.inputPwd')"
+ suffixIcon="map-fill"
+ suffixIconStyle="color: #909399"
+ >
<template slot="suffix">
- <u-icon :name="pwdIcon" @click="changePassword"></u-icon>
+ <u-icon
+ :name="pwdIcon"
+ @click="changePassword"
+ ></u-icon>
</template>
</u-input>
-
-
</view>
<view class="langAndRemView">
<view class="check">
<view>
- <view>{{$t('login.remPwd')}}</view>
+ <text>{{ $t('login.remPwd') }}</text>
</view>
<view>
- <u-switch space="2" size="20" v-model="remberPassword" activeColor="#f9ae3d"
- inactiveColor="rgb(230, 230, 230)">
- </u-switch>
- <!-- <switch :checked='remberPassword' color="#FFCC33" style="transform:scale(0.7)"
- @change="remberChange" /> -->
+ <u-switch
+ space="2"
+ size="20"
+ v-model="remberPassword"
+ activeColor="#f9ae3d"
+ inactiveColor="rgb(230, 230, 230)"
+ ></u-switch>
</view>
</view>
<view class="langView">
<!-- 璇█閫夋嫨涓嬫媺鑿滃崟 -->
<view class="language-dropdown">
- <view class="selected-language" @click="toggleLanguageDropdown">
- <text>{{getCurrentLanguageText()}}</text>
- <u-icon name="list" size="14" color="#707070"></u-icon>
+ <view
+ class="selected-language"
+ @click="toggleLanguageDropdown"
+ >
+ <text>{{ getCurrentLanguageText() }}</text>
+ <u-icon
+ name="list"
+ size="14"
+ color="#707070"
+ ></u-icon>
</view>
- <view class="language-options" v-if="showLanguageDropdown">
- <view class="language-option" v-for="(item, index) in locales" :key="index"
- @click="onLocaleChange(item)">
- <text>{{item.text}}</text>
- <u-icon name="checkbox-mark" color="#007AFF"
- v-if="item.code == applicationLocale"></u-icon>
+ <view
+ class="language-options"
+ v-if="showLanguageDropdown"
+ >
+ <view
+ class="language-option"
+ v-for="(item, index) in locales"
+ :key="index"
+ @click="onLocaleChange(item)"
+ >
+ <text>{{ item.text }}</text>
+ <u-icon
+ name="checkbox-mark"
+ color="#007AFF"
+ v-if="item.code == applicationLocale"
+ ></u-icon>
</view>
</view>
</view>
</view>
</view>
<view class="itemView">
- <u-button class="loadingButton" @click="onLogin()" :loading="loading">{{btnText}}</u-button>
+ <u-button
+ class="loadingButton"
+ @click="onLogin()"
+ :loading="loading"
+ >
+ {{ btnText }}
+ </u-button>
</view>
</view>
+ <u-popup
+ :show="showAuth"
+ @close="showAuth = false"
+ mode="center"
+ :round="14"
+ :customStyle="{ width: '500rpx' }"
+ >
+ <view class="settings-popup">
+ <view class="settings-title">
+ <text class="settings-title-text">
+ {{ $t('settings.authTitle') || '韬唤楠岃瘉' }}
+ </text>
+ </view>
+ <view class="settings-item">
+ <u-input
+ v-model="authPassword"
+ type="password"
+ border="surround"
+ :placeholder="
+ $t('settings.inputAuthPwd') || '璇疯緭鍏ョ鐞嗗憳瀵嗙爜'
+ "
+ />
+ </view>
+ <view class="settings-buttons">
+ <u-button
+ size="medium"
+ @click="showAuth = false"
+ >
+ {{ $t('common.cancel') || '鍙栨秷' }}
+ </u-button>
+ <view style="width: 20px"></view>
+ <u-button
+ size="medium"
+ type="primary"
+ @click="checkAuth"
+ >
+ {{ $t('common.confirm') || '纭' }}
+ </u-button>
+ </view>
+ </view>
+ </u-popup>
+ <u-popup
+ :show="showSettings"
+ @close="showSettings = false"
+ mode="center"
+ :round="14"
+ :closeOnClickOverlay="false"
+ :customStyle="{ width: '600rpx' }"
+ >
+ <view class="settings-popup">
+ <view class="settings-title">
+ <text class="settings-title-text">
+ {{ $t('settings.title') || '璁剧疆' }}
+ </text>
+ </view>
+ <view class="settings-item">
+ <text class="settings-label">
+ {{ $t('settings.ip') || 'IP鍦板潃' }}:
+ </text>
+ <u-input
+ v-model="settings.ip"
+ border="surround"
+ />
+ </view>
+ <view class="settings-item">
+ <text class="settings-label">
+ {{ $t('settings.port') || '绔彛' }}:
+ </text>
+ <u-input
+ v-model="settings.port"
+ border="surround"
+ />
+ </view>
+ <view class="settings-item">
+ <text class="settings-label">
+ {{ $t('settings.project') || '椤圭洰鍚�' }}:
+ </text>
+ <u-input
+ v-model="settings.project"
+ border="surround"
+ />
+ </view>
+ <view class="settings-buttons">
+ <u-button
+ size="medium"
+ @click="showSettings = false"
+ >
+ {{ $t('common.cancel') || '鍙栨秷' }}
+ </u-button>
+ <view style="width: 20px"></view>
+ <u-button
+ size="medium"
+ type="primary"
+ @click="saveSettings"
+ >
+ {{ $t('common.confirm') || '纭' }}
+ </u-button>
+ </view>
+ </view>
+ </u-popup>
<u-toast ref="uToast"></u-toast>
</view>
</template>
<script>
- import md5 from '../../static/js/md5.js'
- import {
- request
- } from '../../common/request.js'
- export default {
- data() {
- return {
- showPassword: true,
- loading: false,
- showLanguageDropdown: false,
- loginButton: 'login.login',
- systemLocale: '',
- applicationLocale: '',
- remberPassword: true,
- user: {
- userName: '',
- password: '',
+import md5 from '../../static/js/md5.js'
+import { login } from './api.js'
+export default {
+ data() {
+ return {
+ showPassword: true,
+ loading: false,
+ showLanguageDropdown: false,
+ loginButton: 'login.login',
+ systemLocale: '',
+ applicationLocale: '',
+ remberPassword: true,
+ user: {
+ userName: '',
+ password: ''
+ },
+ passwordIcon: 'eye-off',
+ showSettings: false,
+ settings: {
+ ip: '',
+ port: '',
+ project: ''
+ },
+ showAuth: false,
+ authPassword: ''
+ }
+ },
+ computed: {
+ locales() {
+ return [
+ {
+ text: this.$t('locale.auto'),
+ code: 'auto'
},
- passwordIcon: 'eye-off'
+ {
+ text: this.$t('locale.en'),
+ code: 'en'
+ },
+ {
+ text: this.$t('locale.zh-hans'),
+ code: 'zh-Hans'
+ },
+ {
+ text: this.$t('locale.zh-hant'),
+ code: 'zh-Hant'
+ },
+ {
+ text: this.$t('locale.ja'),
+ code: 'ja'
+ }
+ ]
+ },
+ btnText() {
+ return this.$t(this.loginButton)
+ },
+ pwdIcon() {
+ return this.passwordIcon
+ }
+ },
+ onLoad() {
+ let systemInfo = uni.getSystemInfoSync()
+ this.systemLocale = systemInfo.language
+ this.applicationLocale = uni.getLocale()
+ this.isAndroid = systemInfo.platform.toLowerCase() === 'android'
+ uni.onLocaleChange((e) => {
+ this.applicationLocale = e.locale
+ })
+
+ this.user = uni.getStorageSync('user')
+ if (!this.user) {
+ this.user = {
+ userName: '',
+ password: ''
+ }
+ }
+ },
+ methods: {
+ openSettings() {
+ this.showAuth = true
+ this.authPassword = ''
+ },
+ checkAuth() {
+ // Default password: admin or 123456. Ideally from config.
+ if (
+ this.authPassword === 'admin' ||
+ this.authPassword === '123456'
+ ) {
+ this.showAuth = false
+ this.loadSettings()
+ } else {
+ this.$refs.uToast.show({
+ type: 'error',
+ message: this.$t('settings.authError') || '瀵嗙爜閿欒'
+ })
}
},
- computed: {
- locales() {
- return [{
- text: this.$t('locale.auto'),
- code: 'auto'
- }, {
- text: this.$t('locale.en'),
- code: 'en'
- },
- {
- text: this.$t('locale.zh-hans'),
- code: 'zh-Hans'
- },
- {
- text: this.$t('locale.zh-hant'),
- code: 'zh-Hant'
- },
- {
- text: this.$t('locale.ja'),
- code: 'ja'
- }
- ]
- },
- btnText() {
- return this.$t(this.loginButton);
- },
- pwdIcon() {
- return this.passwordIcon;
+ loadSettings() {
+ this.showSettings = true
+ let settings = uni.getStorageSync('app_settings')
+ if (!settings) {
+ settings = {
+ ip: '127.0.0.1',
+ port: '8080',
+ project: 'jshdasrs'
+ }
}
+ this.settings = settings
},
- onLoad() {
- let systemInfo = uni.getSystemInfoSync();
- this.systemLocale = systemInfo.language;
- this.applicationLocale = uni.getLocale();
- this.isAndroid = systemInfo.platform.toLowerCase() === 'android';
- uni.onLocaleChange((e) => {
- this.applicationLocale = e.locale;
+ saveSettings() {
+ uni.setStorageSync('app_settings', this.settings)
+ console.log(this.settings)
+ this.showSettings = false
+ this.$refs.uToast.show({
+ type: 'success',
+ message: this.$t('settings.saved') || '璁剧疆宸蹭繚瀛�'
})
+ },
+ async onLogin() {
+ try {
+ const res = await login(
+ {
+ username: this.user.userName,
+ password: md5.hex_md5(this.user.password)
+ },
+ { custom: { catch: true } }
+ )
- this.user = uni.getStorageSync('user')
- if (!this.user) {
- this.user = {
- userName: '',
- password: ''
+ this.loading = true
+ this.loginButton = 'login.loging'
+ uni.setStorageSync('token', res.data.accessToken)
+ uni.setStorageSync('userData', res.data.username)
+ if (this.remberPassword) {
+ uni.setStorageSync('user', this.user)
+ } else {
+ uni.removeStorageSync('user')
}
+ this.goHome()
+ } catch (e) {
+ // 鎷︽埅鍣ㄥ凡澶勭悊 toast
}
},
- methods: {
- async onLogin() {
- const {
- code,
- data,
- msg
- } = await request('/login.action', {
- username: this.user.userName,
- password: md5.hex_md5(this.user.password),
-
- }, 'GET')
- if (code === 200) {
- this.loading = true;
- this.loginButton = 'login.loging';
- uni.setStorageSync('token', data.accessToken);
- uni.setStorageSync('userData', data.username);
- if (this.remberPassword) {
- uni.setStorageSync('user', this.user);
- } else {
- uni.removeStorageSync('user');
- }
- this.goHome()
- } else {
- this.$refs.uToast.show({
- type: 'error',
- message: "璇锋鏌ユ帴鍙h繛鎺�",
- position: 'top'
-
- });
- }
-
- },
- goHome() {
+ goHome() {
+ setTimeout(() => {
+ this.$refs.uToast.show({
+ type: 'success',
+ message: '鐧诲綍鎴愬姛',
+ position: 'top'
+ })
setTimeout(() => {
- this.$refs.uToast.show({
- type: 'success',
- message: "鐧诲綍鎴愬姛",
- position: 'top'
-
- });
- setTimeout(() => {
- uni.$u.route({
- url: 'pages/index/index',
- params: {
- name: 'lisa'
- }
- })
-
- }, 300)
- }, 700)
- },
- remberChange(e) {
- this.remberPassword = !this.remberPassword
- },
- // 鏄剧ず/闅愯棌瀵嗙爜
- changePassword() {
- this.passwordIcon = !this.showPassword ? 'eye-off' : 'eye'
- this.showPassword = !this.showPassword;
- },
- localChange() {
- console.log(this.local)
- if (this.isAndroid) {
- uni.showModal({
- content: this.$t('index.language-change-confirm'),
- success: (res) => {
- if (res.confirm) {
- uni.setLocale(this.local.value);
- }
+ uni.$u.route({
+ type: 'reLaunch',
+ url: 'pages/home/home',
+ params: {
+ name: 'lisa'
}
})
- } else {
- uni.setLocale(this.local.value);
- this.$i18n.locale = this.local.value;
- }
- },
- // 鍒囨崲璇█涓嬫媺鑿滃崟鏄剧ず鐘舵��
- toggleLanguageDropdown() {
- this.showLanguageDropdown = !this.showLanguageDropdown;
- },
-
- // 鑾峰彇褰撳墠閫夋嫨鐨勮瑷�鏂囨湰
- getCurrentLanguageText() {
- const currentLocale = this.locales.find(item => item.code === this.applicationLocale);
- return currentLocale ? currentLocale.text : this.$t('locale.auto');
- },
-
- // 璇█閫夋嫨鏀瑰彉
- onLocaleChange(e) {
- if (this.isAndroid) {
- uni.showModal({
- content: this.$t('index.language-change-confirm'),
- success: (res) => {
- if (res.confirm) {
- uni.setLocale(e.code);
- this.showLanguageDropdown = false;
- }
+ }, 300)
+ }, 700)
+ },
+ remberChange(e) {
+ this.remberPassword = !this.remberPassword
+ },
+ // 鏄剧ず/闅愯棌瀵嗙爜
+ changePassword() {
+ this.passwordIcon = !this.showPassword ? 'eye-off' : 'eye'
+ this.showPassword = !this.showPassword
+ },
+ localChange() {
+ console.log(this.local)
+ if (this.isAndroid) {
+ uni.showModal({
+ content: this.$t('index.language-change-confirm'),
+ success: (res) => {
+ if (res.confirm) {
+ uni.setLocale(this.local.value)
}
- })
- } else {
- uni.setLocale(e.code);
- this.$i18n.locale = e.code;
- this.showLanguageDropdown = false;
- }
- },
+ }
+ })
+ } else {
+ uni.setLocale(this.local.value)
+ this.$i18n.locale = this.local.value
+ }
+ },
+ // 鍒囨崲璇█涓嬫媺鑿滃崟鏄剧ず鐘舵��
+ toggleLanguageDropdown() {
+ this.showLanguageDropdown = !this.showLanguageDropdown
+ },
+
+ // 鑾峰彇褰撳墠閫夋嫨鐨勮瑷�鏂囨湰
+ getCurrentLanguageText() {
+ const currentLocale = this.locales.find(
+ (item) => item.code === this.applicationLocale
+ )
+ return currentLocale ? currentLocale.text : this.$t('locale.auto')
+ },
+
+ // 璇█閫夋嫨鏀瑰彉
+ onLocaleChange(e) {
+ if (this.isAndroid) {
+ uni.showModal({
+ content: this.$t('index.language-change-confirm'),
+ success: (res) => {
+ if (res.confirm) {
+ uni.setLocale(e.code)
+ this.showLanguageDropdown = false
+ }
+ }
+ })
+ } else {
+ uni.setLocale(e.code)
+ this.$i18n.locale = e.code
+ this.showLanguageDropdown = false
+ }
}
}
+}
</script>
<style lang="scss" scoped>
- .helloText {
- font-family: a2;
- font-size: 20px;
- margin-top: 10px;
- margin-bottom: 5px;
- }
+.helloText {
+ font-family: a2;
+ font-size: 20px;
+ margin-top: 10px;
+ margin-bottom: 5px;
+}
- .introText {
- font-family: a3;
- font-size: 15px;
- color: #ababab;
- }
+.introText {
+ font-family: a3;
+ font-size: 15px;
+ color: #ababab;
+}
- .textType3 {
- font-family: a4;
- }
+.textType3 {
+ font-family: a4;
+}
- .bodyView {
- display: flex;
- flex-direction: column;
- background-image: url("/static/img/login_backg.png");
- background-repeat: no-repeat;
- background-size: cover;
- background-position: center;
- height: 100vh;
- width: 100%;
- }
+.bodyView {
+ flex: 1;
+ flex-direction: column;
+ width: 750rpx;
+}
- .topView {
- flex: 7;
- }
+.bgImage {
+ position: fixed;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+}
- .topView image {
- width: 100%;
- }
+.topView {
+ flex: 7;
+}
- .logoView {
- flex: 1;
- display: flex;
- justify-content: flex-end;
- align-items: center;
+.topView image {
+ width: 100%;
+}
- }
+.logoView {
+ flex: 1;
+ display: flex;
+ justify-content: flex-end;
+ align-items: flex-end;
+}
- .logoView image {
- width: 33%;
- height: 50px;
- margin-right: 20px;
- }
+.logoView image {
+ width: 33%;
+ height: 50px;
+ margin-right: 20px;
+}
- .bottomView {
- flex: 15;
- display: flex;
- flex-direction: column;
- justify-content: flex-start;
- align-items: center;
- }
+.bottomView {
+ flex: 15;
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+ align-items: center;
+}
- .itemView {
- width: 90%;
- height: 50px;
- margin-bottom: 30px;
- }
+.itemView {
+ width: 90%;
+ margin-bottom: 5px;
+}
- .langAndRemView {
- width: 90%;
- display: flex;
- justify-content: space-between;
- align-items: center;
- }
+.langAndRemView {
+ width: 90%;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 5px;
+}
- .langView {
- width: 30%;
- }
+.langView {
+ width: 30%;
+}
- .textImage {
- width: 60%;
- height: 42px;
- object-fit: cover;
- margin-top: 20px;
- margin-bottom: 30px;
- }
+.textImage {
+ width: 60%;
+ height: 42px;
+ object-fit: cover;
+ margin-top: 20px;
+ margin-bottom: 30px;
+}
- .input-wrapper {
- /* #ifndef APP-NVUE */
- display: flex;
- /* #endif */
- padding: 8px 13px;
- flex-direction: row;
- flex-wrap: nowrap;
- background-color: #FFFFFF;
- border-radius: 10px;
- height: 45px;
- align-items: center;
- margin-top: 5px;
- }
+.input-wrapper {
+ /* #ifndef APP-NVUE */
+ display: flex;
+ /* #endif */
+ padding: 8px 13px;
+ flex-direction: row;
+ flex-wrap: nowrap;
+ background-color: #ffffff;
+ border-radius: 10px;
+ height: 45px;
+ align-items: center;
+ margin-top: 5px;
+}
- .uni-input {
- height: 28px;
- line-height: 28px;
- font-size: 15px;
- padding: 0px;
- flex: 1;
- background-color: #FFFFFF;
+.uni-input {
+ height: 28px;
+ line-height: 28px;
+ font-size: 15px;
+ padding: 0px;
+ flex: 1;
+ background-color: #ffffff;
+}
- }
+.uni-icon {
+ font-family: uniicons;
+ font-size: 24px;
+ font-weight: normal;
+ font-style: normal;
+ width: 24px;
+ height: 24px;
+ line-height: 24px;
+ color: #999999;
+}
- .uni-icon {
- font-family: uniicons;
- font-size: 24px;
- font-weight: normal;
- font-style: normal;
- width: 24px;
- height: 24px;
- line-height: 24px;
- color: #999999;
- }
+.uni-eye-active {
+ color: #007aff;
+}
- .uni-eye-active {
- color: #007AFF;
- }
+.eye-icon {
+ width: 20px;
+ height: 13px;
+ margin-left: 5px;
+}
- .eye-icon {
- width: 20px;
- height: 13px;
- margin-left: 5px;
- }
+.loadingButton {
+ background-color: #ffda1e;
+ font-family: a1;
+}
- .loadingButton {
- background-color: #ffda1e;
- font-family: a1;
- }
+.check {
+ height: 100%;
+ display: flex;
+ flex-direction: row;
+ font-size: 18px;
+ color: #606266;
+ justify-content: flex-start;
+ align-items: center;
+}
- .check {
+/* 璇█閫夋嫨涓嬫媺鑿滃崟 */
+.language-dropdown {
+ position: relative;
+ margin-bottom: 20rpx;
+ z-index: 10;
+}
- height: 100%;
- display: flex;
- font-size: 18px;
- color: #606266;
- justify-content: flex-start;
- align-items: center;
+.selected-language {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ padding: 15rpx 20rpx;
+ background-color: #f8f8f8;
+ border-radius: 8rpx;
+ border: 1px solid #e0e0e0;
+}
- }
+.language-options {
+ position: absolute;
+ bottom: 100%;
+ left: 0;
+ right: 0;
+ background-color: #ffffff;
+ border-radius: 8rpx;
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+ border: 1px solid #e0e0e0;
+ margin-bottom: 5rpx;
+}
- /* 璇█閫夋嫨涓嬫媺鑿滃崟 */
- .language-dropdown {
- position: relative;
- margin-bottom: 20rpx;
- z-index: 10;
- }
+.language-option {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ padding: 15rpx 20rpx;
+ border-bottom: 1px solid #f0f0f0;
+}
- .selected-language {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 15rpx 20rpx;
- background-color: #f8f8f8;
- border-radius: 8rpx;
- border: 1px solid #e0e0e0;
- }
+.language-option:last-child {
+ border-bottom: none;
+}
- .language-options {
- position: absolute;
- bottom: 100%;
- left: 0;
- right: 0;
- background-color: #ffffff;
- border-radius: 8rpx;
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
- border: 1px solid #e0e0e0;
- margin-bottom: 5rpx;
- }
+.language-option:active {
+ background-color: #f5f5f5;
+}
- .language-option {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 15rpx 20rpx;
- border-bottom: 1px solid #f0f0f0;
- }
+.settings-popup {
+ padding: 20px;
+ background-color: #ffffff;
+ border-radius: 10px;
+ display: flex;
+ flex-direction: column;
+}
- .language-option:last-child {
- border-bottom: none;
- }
+.settings-title {
+ margin-bottom: 20px;
+ align-items: center;
+ justify-content: center;
+}
- .language-option:active {
- background-color: #f5f5f5;
- }
-</style>
\ No newline at end of file
+.settings-title-text {
+ font-size: 18px;
+ font-weight: bold;
+}
+
+.settings-item {
+ margin-bottom: 15px;
+}
+
+.settings-label {
+ margin-bottom: 5px;
+ font-size: 14px;
+ color: #606266;
+}
+
+.settings-buttons {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ margin-top: 10px;
+}
+</style>
--
Gitblit v1.9.1