Network — 网络请求
基于 Native Bridge 的 HTTP 客户端,支持请求拦截器、自定义超时和文件上传。
交互示例
基础请求
GET / POST 请求
<script setup lang="ts">
import { ref } from 'vue'
import woo from 'mini-sdk'
const status = ref('')
const response = ref<any>(null)
const loading = ref(false)
async function doGet() {
loading.value = true
status.value = '请求中...'
response.value = null
try {
const res = await woo.get('https://api.example.com/user')
status.value = `✅ 成功 (HTTP ${res.statusCode})`
response.value = res.data
} catch (err: any) {
status.value = `❌ 失败: ${err.message}`
} finally {
loading.value = false
}
}
async function doPost() {
loading.value = true
status.value = '提交中...'
response.value = null
try {
const res = await woo.post('https://api.example.com/items', {
name: '新建项目',
category: 'tool',
})
status.value = `✅ 创建成功 (HTTP ${res.statusCode})`
response.value = res.data
} catch (err: any) {
status.value = `❌ 失败: ${err.message}`
} finally {
loading.value = false
}
}
</script>
<template>
<div class="demo-network">
<div class="btn-row">
<button class="btn btn-primary" :disabled="loading" @click="doGet">
<span v-if="loading && status.includes('请求')" class="spinner" />
GET /user
</button>
<button class="btn btn-secondary" :disabled="loading" @click="doPost">
<span v-if="loading && status.includes('提交')" class="spinner" />
POST /items
</button>
</div>
<div v-if="status" class="result-box">
<div class="status-label">{{ status }}</div>
<pre v-if="response" class="response-json">{{ JSON.stringify(response, null, 2) }}</pre>
</div>
</div>
</template>
<style scoped>
.demo-network { display: flex; flex-direction: column; gap: 16px; width: 100%; }
.btn-row { display: flex; gap: 10px; flex-wrap: wrap; }
.btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 8px 18px; border-radius: 7px; border: none;
font-size: 13.5px; font-weight: 500; cursor: pointer;
transition: all 0.15s ease; font-family: inherit;
}
.btn:disabled { opacity: 0.6; cursor: not-allowed; }
.btn-primary { background: #6366f1; color: white; }
.btn-primary:hover:not(:disabled) { background: #4f46e5; }
.btn-secondary { background: #f1f5f9; color: #334155; border: 1px solid #e2e8f0; }
.btn-secondary:hover:not(:disabled) { background: #e2e8f0; }
.spinner {
width: 12px; height: 12px; border: 2px solid currentColor;
border-top-color: transparent; border-radius: 50%;
animation: spin 0.6s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.result-box { background: var(--vp-c-bg-soft); border: 1px solid var(--vp-c-divider); border-radius: 8px; padding: 12px 16px; }
.status-label { font-size: 13px; font-weight: 500; margin-bottom: 8px; color: var(--vp-c-text-1); }
.response-json { margin: 0; font-size: 12px; color: var(--vp-c-text-2); white-space: pre-wrap; }
</style>
Attributes
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---|---|---|---|---|
url | 请求地址 | string | — | — |
method | HTTP 方法 | 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | GET / POST / PUT / DELETE / PATCH / HEAD | 'GET' |
data | 请求体数据(自动 JSON 序列化) | any | — | — |
header | 自定义请求头 | Record<string, string> | — | — |
timeout | 超时时间(ms) | number | — | 30000 |
文件上传
选择图片并上传
<script setup lang="ts">
import { ref } from 'vue'
import woo from 'mini-sdk'
const progress = ref(0)
const status = ref('')
const uploadedUrl = ref('')
const isUploading = ref(false)
async function pickAndUpload() {
isUploading.value = false
status.value = ''
uploadedUrl.value = ''
progress.value = 0
try {
// Step 1: choose image
status.value = '📂 选择图片中...'
const { tempFilePaths } = await woo.chooseImage({ count: 1, sourceType: ['album'] })
// Step 2: simulate upload progress
isUploading.value = true
status.value = '⬆️ 上传中...'
// Mock progress bar
const interval = setInterval(() => {
progress.value = Math.min(progress.value + Math.random() * 25, 95)
}, 300)
const uploadRes = await woo.uploadFile({
url: 'https://api.example.com/upload',
filePath: tempFilePaths[0],
name: 'file',
formData: { type: 'avatar' },
})
clearInterval(interval)
progress.value = 100
const serverRes = JSON.parse(uploadRes.data)
uploadedUrl.value = serverRes.url
status.value = '✅ 上传成功'
} catch (err: any) {
status.value = `❌ 失败: ${err?.message ?? '用户取消'}`
} finally {
isUploading.value = false
}
}
</script>
<template>
<div class="demo-upload">
<button class="btn" :disabled="isUploading" @click="pickAndUpload">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 16 12 12 8 16"/><line x1="12" y1="12" x2="12" y2="21"/><path d="M20.39 18.39A5 5 0 0 0 18 9h-1.26A8 8 0 1 0 3 16.3"/></svg>
选择图片并上传
</button>
<div v-if="isUploading || progress > 0" class="progress-wrap">
<div class="progress-bar">
<div class="progress-fill" :style="{ width: progress + '%' }" />
</div>
<span class="progress-text">{{ Math.round(progress) }}%</span>
</div>
<div v-if="status" class="status-row">{{ status }}</div>
<div v-if="uploadedUrl" class="url-box">
<span class="url-label">服务器地址:</span>
<code>{{ uploadedUrl }}</code>
</div>
</div>
</template>
<style scoped>
.demo-upload { display: flex; flex-direction: column; gap: 14px; width: 100%; }
.btn {
display: inline-flex; align-items: center; gap: 7px;
padding: 8px 18px; border-radius: 7px; background: #6366f1; color: white;
border: none; font-size: 13.5px; font-weight: 500; cursor: pointer;
transition: background 0.15s; font-family: inherit; width: fit-content;
}
.btn:hover:not(:disabled) { background: #4f46e5; }
.btn:disabled { opacity: 0.6; cursor: not-allowed; }
.progress-wrap { display: flex; align-items: center; gap: 10px; }
.progress-bar { flex: 1; height: 6px; background: var(--vp-c-divider); border-radius: 99px; overflow: hidden; }
.progress-fill { height: 100%; background: linear-gradient(90deg, #6366f1, #8b5cf6); border-radius: 99px; transition: width 0.3s ease; }
.progress-text { font-size: 12px; color: var(--vp-c-text-2); min-width: 32px; text-align: right; }
.status-row { font-size: 13px; color: var(--vp-c-text-2); }
.url-box { font-size: 12.5px; background: var(--vp-c-bg-soft); border: 1px solid var(--vp-c-divider); border-radius: 6px; padding: 8px 12px; }
.url-label { color: var(--vp-c-text-3); }
code { font-family: var(--vp-font-family-mono); color: #6366f1; }
</style>
Attributes
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---|---|---|---|---|
url | 上传接口地址 | string | — | — |
filePath | 文件路径(base64 DataURL 或本地路径) | string | — | — |
name | 文件字段名(multipart name) | string | — | — |
formData | 附加表单数据 | Record<string, any> | — | — |
header | 自定义请求头 | Record<string, string> | — | — |
timeout | 超时时间(ms) | number | — | 60000 |
API 参考
woo.request(options)
发起 HTTP 请求,返回 Promise<RequestResult>。ts
const res = await woo.request<{ name: string }>({
url: 'https://api.example.com/user',
method: 'GET',
header: { Authorization: 'Bearer TOKEN' },
timeout: 10000,
})
console.log(res.data.name)
console.log(res.statusCode) // 200Options
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
url | string | ✅ | — | 请求地址 |
method | 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | ❌ | 'GET' | HTTP 方法 |
data | any | ❌ | — | 请求体数据 |
header | Record<string, string> | ❌ | — | 自定义请求头 |
timeout | number | ❌ | 30000 | 超时时间(ms) |
Result
ts
interface RequestResult {
data: any // 响应体(自动 JSON.parse)
statusCode: number // HTTP 状态码
header: Record<string, string> // 响应头
}便捷方法
ts
// GET
const res = await woo.get(url, options?)
// POST
const res = await woo.post(url, data?, options?)
// PUT
const res = await woo.put(url, data?, options?)
// DELETE
const res = await woo.del(url, options?)请求拦截器
ts
// 全局注入 Authorization
const reqId = woo.request.interceptors.request.use((config) => {
return {
...config,
header: {
...config.header,
Authorization: `Bearer ${getToken()}`,
},
}
})
// 全局响应处理
const resId = woo.request.interceptors.response.use((response) => {
if (response.statusCode === 401) {
woo.reLaunch({ url: 'pages/login/index' })
throw { code: 20002, message: '登录已过期' }
}
return response
})
// 移除拦截器
woo.request.interceptors.request.eject(reqId)
woo.request.interceptors.response.eject(resId)woo.uploadFile(options)
以 multipart/form-data 方式上传文件。ts
const uploadRes = await woo.uploadFile({
url: 'https://api.example.com/upload',
filePath: tempFilePaths[0], // base64 DataURL 或本地路径
name: 'file',
formData: { type: 'avatar', userId: '123' },
header: { Authorization: 'Bearer TOKEN' },
timeout: 60000,
})
const serverRes = JSON.parse(uploadRes.data)
console.log(serverRes.url) // 服务器返回的文件 URLOptions
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
url | string | ✅ | 上传接口地址 |
filePath | string | ✅ | 文件路径(base64 DataURL 或本地路径) |
name | string | ✅ | 文件字段名 |
formData | Record<string, any> | ❌ | 附加表单数据 |
header | Record<string, string> | ❌ | 自定义请求头 |
timeout | number | ❌ | 超时时间(ms),默认 60000 |