Signed-off-by: 吴文峰 <kevin@lmve.net>
This commit is contained in:
2026-04-17 20:46:21 +08:00
parent 3bb51d0794
commit 8dce0346a7
10 changed files with 370 additions and 78 deletions
+23 -16
View File
@@ -4,21 +4,28 @@
*/ */
// 用户相关 // 用户相关
export const user = {
login: (data) => post('/api/user/login', data), export const api = {
logout: () => post('/api/user/logout'), /**
info: () => get('/api/user/info') * GET 请求(一般不需要认证)
*/
get(path) {
},
/**
* POST JSON
*/
post(path, data = {},callback) {
console.log("post")
},
/**
* POST FormData(文件上传)
*/
upload(path, file) {
},
} }
// 订单相关 export default api
export const order = {
list: (data) => get('/api/order/list', data),
detail: (id) => get('/api/order/detail', { id }),
create: (data) => post('/api/order/create', data)
}
// 消息相关
export const message = {
list: (data) => get('/api/message/list', data),
read: (id) => post('/api/message/read', { id })
}
+1 -47
View File
@@ -1,50 +1,4 @@
import { useConfigStore } from '@/stores/config' import { useConfigStore } from '@/stores/config'
/** const useConfig=useConfigStore()
* 基础请求封装
* @param {string} path - 接口路径
* @param {object} data - 请求参数
* @param {string} method - 请求方法
*/
export const request = (path, data = {}, method = 'GET') => {
const config = useConfigStore()
return new Promise((resolve, reject) => {
uni.request({
url: config.apiBaseUrl + path,
data,
method,
header: {
'Content-Type': 'application/json'
},
success: (res) => {
if (res.statusCode === 200) {
resolve(res.data)
} else {
uni.showToast({
title: '请求失败',
icon: 'none'
})
reject(res)
}
},
fail: (err) => {
uni.showToast({
title: '网络错误',
icon: 'none'
})
reject(err)
}
})
})
}
/**
* GET 请求
*/
export const get = (path, data) => request(path, data, 'GET')
/**
* POST 请求
*/
export const post = (path, data) => request(path, data, 'POST')
+9
View File
@@ -0,0 +1,9 @@
import api from ".";
export const userApi = {
login(username, password, remember) {
return api.post('/users/login', { username, password, remember })
},
}
+74
View File
@@ -0,0 +1,74 @@
<template>
<view v-if="visible" class="my-toast" :class="typeClass">
<text>{{ message }}</text>
</view>
</template>
<script setup>
import { ref, computed } from 'vue'
const visible = ref(false)
const message = ref('')
const type = ref('info') // info | success | error | warning
const typeClass = computed(() => `toast-${type.value}`)
// 暴露方法给外部调用
function show(msg, t = 'info', duration = 2000) {
message.value = msg
type.value = t
visible.value = true
setTimeout(() => {
visible.value = false
}, duration)
}
// 快捷方法
function success(msg, duration = 2000) { show(msg, 'success', duration) }
function error(msg, duration = 2000) { show(msg, 'error', duration) }
function warning(msg, duration = 2000) { show(msg, 'warning', duration) }
function info(msg, duration = 2000) { show(msg, 'info', duration) }
defineExpose({ show, success, error, warning, info })
</script>
<style scoped>
/* 固定顶部居中 */
.my-toast {
position: fixed;
top: 120rpx;
left: 50%;
transform: translateX(-50%);
padding: 20rpx 40rpx;
border-radius: 12rpx;
font-size: 28rpx;
z-index: 9999;
text-align: center;
max-width: 600rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.15);
word-break: break-all;
line-height: 1.5;
}
/* 四种类型 */
.toast-info {
background-color: #007AFF;
color: #fff;
}
.toast-success {
background-color: #34C759;
color: #fff;
}
.toast-error {
background-color: #FF3B30;
color: #fff;
}
.toast-warning {
background-color: #FF9500;
color: #fff;
}
</style>
+4 -1
View File
@@ -41,7 +41,10 @@
"navigationBarTextStyle": "black", "navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-app", "navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#F8F8F8", "navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8" "backgroundColor": "#F8F8F8",
"usingComponents": {
"my-toast": "/components/my-toast/my-toast"
}
}, },
"tabBar": { "tabBar": {
"color": "#7A7E83", "color": "#7A7E83",
+39 -1
View File
@@ -6,14 +6,21 @@
<button class="submit-btn" @click="handleLogin">登录</button> <button class="submit-btn" @click="handleLogin">登录</button>
</view> </view>
</view> </view>
<my-toast ref="toast" />
</template> </template>
<script setup> <script setup>
import { ref } from 'vue' import { ref } from 'vue'
import { useConfigStore } from '../../stores/config'
import { userApi } from '../../api/user'
const useConfig =useConfigStore()
const username = ref('') const username = ref('')
const password = ref('') const password = ref('')
const toast = ref(null)
const handleLogin = () => { const handleLogin = () => {
if (!username.value || !password.value) { if (!username.value || !password.value) {
uni.showToast({ uni.showToast({
@@ -23,7 +30,38 @@ const handleLogin = () => {
return return
} }
// TODO: 调用登录接口 // TODO: 调用登录接口
console.log('登录信息:', username.value, password.value) console.log('登录信息:', username.value, password.value,useConfig.getApiBaseUrl())
//userApi.login(username.value,password.value,true)
uni.request({
url: useConfig.getApiBaseUrl()+"/users/login",
method: 'POST',
timeout:1000,
header: {
'Content-Type': 'application/json'
},
data:{
"username": username.value,
"password": password.value,
"remember": true
},
success: (res) => {
console.log('成功', res)
switch(res.statusCode){
case 200:
toast.value.success('成功')
break;
default:
toast.value.error('服务异常')
break
}
},
fail: (err) => {
console.log('失败', err)
toast.value.error('网异常')
},
})
} }
</script> </script>
+194 -12
View File
@@ -1,23 +1,205 @@
<template> <template>
<view class="container"> <!-- 页面根容器灰色背景 -->
<text class="placeholder">设置</text> <view class="page">
</view>
<!-- ===== 分组账号设置 ===== -->
<view class="group">
<!-- 分组标题 -->
<text class="group-title">base url</text>
<!-- 列表容器白色卡片 -->
<view class="group-body">
<!-- 列表项点击跳转或弹窗编辑 -->
<view class="cell" @tap="onEditApiUrl">
<text class="cell-value">{{ useConfig.getApiBaseUrl() || '未设置' }}</text>
</view>
<view class="btn-wrapper">
<button v-if="!isTesting" class="save-btn" @tap="onTest">测试连接</button>
<button v-if="isTesting" class="save-btn" disabled>正在测试...</button>
</view>
<view v-if="isTested&&!isTesting" :class="isTestok ? 'result success' : 'result error'">
{{ isTestok ? ' 连接成功' : ' 连接失败' }}
</view>
</view>
</view>
<!-- ====================== 这里是居中的按钮 ====================== -->
</view>
</template> </template>
<script setup> <script setup>
import { ref,onMounted } from 'vue'
import { isUrl } from '@/utils/index.js'
import { useConfigStore } from '../../stores/config'
const useConfig=useConfigStore()
// ---- 响应式状态 ----
const isTested=ref(false)//是否点了测试
const isTesting=ref(false)//是否正在测试
const isTestok=ref(false)//测试是否通过
// ---- 事件处理 ----
function onTest(){
//console.log("test")
isTested.value=true
isTesting.value=true
uni.request({
url: useConfig.getApiBaseUrl(),
method: 'GET',
timeout:1000,
success: (res) => {
//console.log('成功', res.data)
if(res.data.return.isOpsApiRoot==true){
isTestok.value=true
}
},
fail: (err) => {
//console.log('失败', err)
isTestok.value=false
},
complete() {
// 这个回调不管成功失败**一定**会执行
//console.log('请求完成')
isTesting.value=false
}
})
}
// 点击 API URL → 弹出输入框(示例用 uni.showModal 简单实现)
function onEditApiUrl() {
// 实际可跳转子页面或弹出自定义弹窗
uni.showModal({
title: 'API Base URL',
editable: true,
placeholderText: 'https://example.com',
content: useConfig.getApiBaseUrl(),
success(res) {
if (res.confirm) {
console.log(res)
if (isUrl(res.content)){
useConfig.setApiBaseUrl(res.content)
console.log(res.content,":ok")
}else{
console.log(res.content,":not a url")
}
}
}
})
}
onMounted(()=>{
})
</script> </script>
<style scoped> <style scoped>
.container { /* 页面底色 */
flex: 1; .page {
display: flex; min-height: 100vh;
justify-content: center; background-color: #f2f2f7; /* iOS 系统级灰色 */
align-items: center; padding-top: 20rpx;
background-color: #f5f5f5;
} }
.placeholder { /* 分组间距 */
font-size: 32rpx; .group {
color: #999; margin-bottom: 32rpx;
}
/* 分组标题:小号灰色,左侧缩进 */
.group-title {
font-size: 24rpx;
color: #8e8e93;
padding: 0 32rpx 12rpx;
}
/* 白色卡片容器 */
.group-body {
background-color: #ffffff;
border-radius: 12rpx;
margin: 0 24rpx;
overflow: hidden; /* 保证圆角裁剪子项 */
}
/* 每一行 cell */
.cell {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 0 32rpx;
height: 96rpx;
background-color: #ffffff;
}
/* 点击高亮(uni-app 用 hover-class 实现,这里仅演示结构) */
.cell:active {
background-color: #f2f2f7;
}
/* 左侧标签 */
.cell-label {
font-size: 30rpx;
color: #1c1c1e;
}
/* 右侧容器(值 + 箭头) */
.cell-right {
display: flex;
flex-direction: row;
align-items: center;
gap: 8rpx;
}
/* 右侧当前值 */
.cell-value {
font-size: 28rpx;
color: #8e8e93;
max-width: 300rpx; /* 防止超长文本撑破布局 */
white-space: nowrap;
text-overflow: ellipsis;
}
/* 右箭头 */
.cell-arrow {
font-size: 36rpx;
color: #c7c7cc;
line-height: 1;
}
/* switch 开关右对齐 */
.cell-switch {
transform: scale(0.85); /* 微调大小 */
}
/* 分割线:左侧缩进,不跨满 */
.divider {
height: 1rpx;
background-color: #e5e5ea;
margin-left: 32rpx; /* 左缩进,右边贴边,iOS 风格 */
}
/* 危险操作行(退出登录) */
.cell-danger {
justify-content: center; /* 文字居中 */
}
.cell-label-danger {
font-size: 30rpx;
color: #ff3b30; /* iOS 红色 */
} }
</style> </style>
+1
View File
@@ -8,6 +8,7 @@
</template> </template>
<script setup> <script setup>
const goToLogin = () => { const goToLogin = () => {
uni.navigateTo({ uni.navigateTo({
url: '/pages/login/login' url: '/pages/login/login'
+12 -1
View File
@@ -3,7 +3,7 @@ import { ref } from 'vue'
export const useConfigStore = defineStore('config', () => { export const useConfigStore = defineStore('config', () => {
// API 配置 // API 配置
const apiBaseUrl = ref('http://192.168.13.105') const apiBaseUrl = ref('')
// 应用配置 // 应用配置
const appName = ref('OPS') const appName = ref('OPS')
@@ -15,8 +15,18 @@ export const useConfigStore = defineStore('config', () => {
// 设置 API 地址 // 设置 API 地址
const setApiBaseUrl = (url) => { const setApiBaseUrl = (url) => {
apiBaseUrl.value = url apiBaseUrl.value = url
uni.setStorageSync('baseUrl', url)
} }
const getApiBaseUrl=()=>{
if(apiBaseUrl.value==='')
{
apiBaseUrl.value=uni.getStorageSync('baseUrl')
}
return apiBaseUrl.value
}
// 设置主题 // 设置主题
const setTheme = (newTheme) => { const setTheme = (newTheme) => {
theme.value = newTheme theme.value = newTheme
@@ -28,6 +38,7 @@ export const useConfigStore = defineStore('config', () => {
version, version,
theme, theme,
setApiBaseUrl, setApiBaseUrl,
getApiBaseUrl,
setTheme setTheme
} }
}) })
+13
View File
@@ -0,0 +1,13 @@
/**
* 检查字符串是否是合法的 URL
* @param {string} str
* @returns {boolean}
*/
export function isUrl(str) {
if (!str || typeof str !== 'string') return false
// 必须以 http:// 或 https:// 开头(接口地址必须带)
const reg = /^https?:\/\/.+/i
return reg.test(str.trim())
}