OpenAppList.vue 6.9 KB
<template>
  <div>
    <a-card title="开放平台应用管理" :bordered="false">
      <template #extra>
        <a-button type="primary" @click="openAdd">创建应用</a-button>
      </template>
      <a-table :dataSource="list" :columns="columns" :loading="loading" rowKey="id" :pagination="false">
        <template #bodyCell="{ column, record }">
          <template v-if="column.key === 'status'">
            <a-tag :color="record.status === 1 ? 'green' : 'red'">
              {{ record.status === 1 ? '正常' : '禁用' }}
            </a-tag>
          </template>
          <template v-if="column.key === 'appKey'">
            <a-typography-text copyable>{{ record.appKey }}</a-typography-text>
          </template>
          <template v-if="column.key === 'action'">
            <a-space>
              <a @click="handleResetSecret(record)">重置密钥</a>
              <a @click="openWebhook(record)">Webhook</a>
              <a @click="openLogs(record)">推送日志</a>
              <a-popconfirm
                :title="record.status === 1 ? '确认禁用?' : '确认启用?'"
                @confirm="toggleStatus(record)">
                <a :style="record.status === 1 ? 'color:red' : ''">
                  {{ record.status === 1 ? '禁用' : '启用' }}
                </a>
              </a-popconfirm>
            </a-space>
          </template>
        </template>
      </a-table>
    </a-card>

    <!-- 创建应用 -->
    <a-modal v-model:open="addVisible" title="创建应用" @ok="handleCreate" :confirmLoading="saving">
      <a-form :model="addForm" layout="vertical">
        <a-form-item label="应用名称"><a-input v-model:value="addForm.appName" /></a-form-item>
        <a-form-item label="关联城市/租户(必填)">
          <a-select v-model:value="addForm.cityId" placeholder="选择城市" style="width:100%">
            <a-select-option v-for="c in cityList" :key="c.id" :value="c.id">{{ c.name }}</a-select-option>
          </a-select>
        </a-form-item>
        <a-form-item label="备注"><a-input v-model:value="addForm.remark" /></a-form-item>
      </a-form>
    </a-modal>

    <!-- Webhook配置 -->
    <a-modal v-model:open="webhookVisible" title="Webhook配置" @ok="handleWebhookSave" :confirmLoading="saving">
      <a-form layout="vertical">
        <a-form-item label="回调地址">
          <a-input v-model:value="webhookForm.webhookUrl" placeholder="https://your-server.com/webhook" />
        </a-form-item>
        <a-form-item label="订阅事件(JSON数组)">
          <a-textarea v-model:value="webhookForm.webhookEvents"
            placeholder='["order.paid","order.completed","order.cancelled"]' :rows="4" />
        </a-form-item>
        <a-alert message="支持事件:order.paid / order.accepted / order.completed / order.cancelled / order.refund" type="info" show-icon />
      </a-form>
    </a-modal>

    <!-- 推送日志 -->
    <a-modal v-model:open="logsVisible" title="Webhook推送日志" :footer="null" width="800px">
      <a-table :dataSource="logs" :columns="logColumns" rowKey="id" size="small" :pagination="false">
        <template #bodyCell="{ column, record }">
          <template v-if="column.key === 'status'">
            <a-tag :color="record.status === 1 ? 'green' : 'red'">
              {{ record.status === 1 ? '成功' : '失败' }}
            </a-tag>
          </template>
          <template v-if="column.key === 'action'">
            <a v-if="record.status === 0" @click="retryLog(record.id)">重试</a>
          </template>
        </template>
      </a-table>
    </a-modal>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { message, Modal } from 'ant-design-vue'
import { openApi, cityApi } from '@/api'

const loading = ref(false)
const saving = ref(false)
const list = ref<any[]>([])
const logs = ref<any[]>([])
const cityList = ref<any[]>([])
const addVisible = ref(false)
const webhookVisible = ref(false)
const logsVisible = ref(false)
const currentAppId = ref(0)
const addForm = reactive({ appName: '', cityId: undefined as number | undefined, remark: '' })
const webhookForm = reactive({ webhookUrl: '', webhookEvents: '' })

const columns = [
  { title: 'ID', dataIndex: 'id', width: 80 },
  { title: '应用名称', dataIndex: 'appName' },
  { title: 'AppKey', key: 'appKey' },
  { title: '城市/租户', dataIndex: 'cityId' },
  { title: '状态', key: 'status' },
  { title: '回调地址', dataIndex: 'webhookUrl', ellipsis: true },
  { title: '操作', key: 'action' },
]

const logColumns = [
  { title: 'ID', dataIndex: 'id', width: 80 },
  { title: '事件', dataIndex: 'event' },
  { title: '业务ID', dataIndex: 'bizId' },
  { title: '响应码', dataIndex: 'responseCode' },
  { title: '状态', key: 'status' },
  { title: '重试', dataIndex: 'retryCount' },
  { title: '操作', key: 'action' },
]

async function loadList() {
  loading.value = true
  try {
    const res: any = await openApi.list()
    list.value = res.data
  } finally { loading.value = false }
}

async function loadCities() {
  const res: any = await cityApi.openList()
  cityList.value = res.data
}

function openAdd() {
  Object.assign(addForm, { appName: '', cityId: undefined, remark: '' })
  addVisible.value = true
}

async function handleCreate() {
  if (!addForm.cityId) { message.error('请选择城市/租户'); return }
  saving.value = true
  try {
    await openApi.create(addForm)
    message.success('创建成功')
    addVisible.value = false
    loadList()
  } finally { saving.value = false }
}

async function handleResetSecret(record: any) {
  Modal.confirm({
    title: '确认重置 AppSecret?',
    content: '重置后旧密钥立即失效,请及时更新接入方配置',
    onOk: async () => {
      const res: any = await openApi.resetSecret(record.id)
      Modal.info({ title: '新 AppSecret', content: res.data })
    }
  })
}

async function toggleStatus(record: any) {
  await openApi.setStatus(record.id, record.status === 1 ? 0 : 1)
  message.success('操作成功')
  loadList()
}

function openWebhook(record: any) {
  currentAppId.value = record.id
  webhookForm.webhookUrl = record.webhookUrl || ''
  webhookForm.webhookEvents = record.webhookEvents || ''
  webhookVisible.value = true
}

async function handleWebhookSave() {
  saving.value = true
  try {
    await openApi.updateWebhook(currentAppId.value, webhookForm.webhookUrl, webhookForm.webhookEvents)
    message.success('保存成功')
    webhookVisible.value = false
    loadList()
  } finally { saving.value = false }
}

async function openLogs(record: any) {
  currentAppId.value = record.id
  const res: any = await openApi.webhookLogs(record.id)
  logs.value = res.data
  logsVisible.value = true
}

async function retryLog(logId: number) {
  await openApi.retryWebhook(logId)
  message.success('重试已触发')
  const res: any = await openApi.webhookLogs(currentAppId.value)
  logs.value = res.data
}

onMounted(() => { loadList(); loadCities() })
</script>