Skip to content

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
methodHTTP 方法'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD'GET / POST / PUT / DELETE / PATCH / HEAD'GET'
data请求体数据(自动 JSON 序列化)any
header自定义请求头Record<string, string>
timeout超时时间(ms)number30000

文件上传

选择图片并上传
<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)number60000

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) // 200

Options

参数类型必填默认值说明
urlstring请求地址
method'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD''GET'HTTP 方法
dataany请求体数据
headerRecord<string, string>自定义请求头
timeoutnumber30000超时时间(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) // 服务器返回的文件 URL

Options

参数类型必填说明
urlstring上传接口地址
filePathstring文件路径(base64 DataURL 或本地路径)
namestring文件字段名
formDataRecord<string, any>附加表单数据
headerRecord<string, string>自定义请求头
timeoutnumber超时时间(ms),默认 60000

基于 OpenSumi IDE 构建