|
@@ -1,572 +0,0 @@
|
|
|
-<template>
|
|
|
|
|
- <div class="lepao-proxy-page">
|
|
|
|
|
- <Breadcrumb />
|
|
|
|
|
-
|
|
|
|
|
- <a-skeleton animation :loading="pageLoading" class="lepao-proxy-skeleton">
|
|
|
|
|
- <a-skeleton-line :rows="10" />
|
|
|
|
|
- <template #content>
|
|
|
|
|
- <a-card class="hero-card" :bordered="false">
|
|
|
|
|
- <div class="hero-inner">
|
|
|
|
|
- <div class="hero-title">
|
|
|
|
|
- <span class="t1">乐跑出站代理</span>
|
|
|
|
|
- <a-tag color="arcoblue" size="small">通道提取</a-tag>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </a-card>
|
|
|
|
|
-
|
|
|
|
|
- <a-row :gutter="16" style="margin-top: 16px">
|
|
|
|
|
- <a-col :span="24">
|
|
|
|
|
- <a-card title="当前状态" :bordered="false" class="panel">
|
|
|
|
|
- <a-descriptions :column="isMobile ? 1 : 2" bordered size="small">
|
|
|
|
|
- <a-descriptions-item label="extractKey">
|
|
|
|
|
- <a-tag :color="status?.extract_key_configured ? 'green' : 'orangered'">
|
|
|
|
|
- {{ status?.extract_key_configured ? '已配置' : '未配置' }}
|
|
|
|
|
- </a-tag>
|
|
|
|
|
- </a-descriptions-item>
|
|
|
|
|
- <a-descriptions-item label="代理账密">
|
|
|
|
|
- <a-tag :color="status?.proxy_auth_configured ? 'cyan' : 'gray'">
|
|
|
|
|
- {{ status?.proxy_auth_configured ? '已配置' : '可选(白名单可不填)' }}
|
|
|
|
|
- </a-tag>
|
|
|
|
|
- </a-descriptions-item>
|
|
|
|
|
- <a-descriptions-item label="代理节点 server">
|
|
|
|
|
- <span class="mono">{{ status?.current_proxy?.server ?? '—' }}</span>
|
|
|
|
|
- </a-descriptions-item>
|
|
|
|
|
- <a-descriptions-item label="节点 IP 属地">
|
|
|
|
|
- <a-tag color="purple">{{ status?.current_proxy?.node_region ?? '—' }}</a-tag>
|
|
|
|
|
- </a-descriptions-item>
|
|
|
|
|
- <a-descriptions-item label="出口 IP proxy_ip">
|
|
|
|
|
- <span class="mono">{{ status?.current_proxy?.proxy_ip ?? '—' }}</span>
|
|
|
|
|
- </a-descriptions-item>
|
|
|
|
|
- <a-descriptions-item label="出口 IP 属地">
|
|
|
|
|
- <a-tag color="gold">{{ status?.current_proxy?.proxy_ip_region ?? '—' }}</a-tag>
|
|
|
|
|
- </a-descriptions-item>
|
|
|
|
|
- <a-descriptions-item label="deadline" :span="2">
|
|
|
|
|
- {{ status?.current_proxy?.deadline ?? '—' }}
|
|
|
|
|
- <a-tag
|
|
|
|
|
- v-if="status?.current_proxy"
|
|
|
|
|
- size="small"
|
|
|
|
|
- :color="status?.current_proxy?.stale ? 'orangered' : 'green'"
|
|
|
|
|
- style="margin-left: 8px"
|
|
|
|
|
- >
|
|
|
|
|
- {{ status?.current_proxy?.stale ? '临近/已过期' : '有效期内' }}
|
|
|
|
|
- </a-tag>
|
|
|
|
|
- </a-descriptions-item>
|
|
|
|
|
- </a-descriptions>
|
|
|
|
|
- </a-card>
|
|
|
|
|
- </a-col>
|
|
|
|
|
- </a-row>
|
|
|
|
|
-
|
|
|
|
|
- <a-row :gutter="16" style="margin-top: 16px">
|
|
|
|
|
- <a-col :xs="24" :lg="24">
|
|
|
|
|
- <a-card title="筛选与路由" :bordered="false" class="panel">
|
|
|
|
|
- <a-form :model="form" layout="vertical">
|
|
|
|
|
- <a-form-item label="启用青果出站">
|
|
|
|
|
- <a-switch v-model="form.proxy_enabled" />
|
|
|
|
|
- </a-form-item>
|
|
|
|
|
-
|
|
|
|
|
- <a-form-item label="从可选资源勾选地区">
|
|
|
|
|
- <a-space direction="vertical" fill style="width: 100%">
|
|
|
|
|
- <a-space wrap>
|
|
|
|
|
- <a-button size="mini" type="outline" @click="loadResources" :loading="resLoading">
|
|
|
|
|
- 刷新资源列表
|
|
|
|
|
- </a-button>
|
|
|
|
|
- <a-checkbox v-model="onlyAvailableRes">仅显示「可提取」</a-checkbox>
|
|
|
|
|
- </a-space>
|
|
|
|
|
- <a-select
|
|
|
|
|
- v-model="selectedAreaCodes"
|
|
|
|
|
- multiple
|
|
|
|
|
- allow-search
|
|
|
|
|
- allow-clear
|
|
|
|
|
- :loading="resLoading"
|
|
|
|
|
- :options="filteredResourceOptions"
|
|
|
|
|
- :placeholder="resourcePlaceholder"
|
|
|
|
|
- :filter-option="filterResourceOption"
|
|
|
|
|
- style="width: 100%"
|
|
|
|
|
- />
|
|
|
|
|
- <div class="hint-muted">
|
|
|
|
|
- 将写入后端 area 字段(逗号分隔)。也可在下方微调编码。
|
|
|
|
|
- </div>
|
|
|
|
|
- <a-input
|
|
|
|
|
- v-model="form.area"
|
|
|
|
|
- allow-clear
|
|
|
|
|
- placeholder="area 编码,逗号分隔(与上方选择同步,失焦后与多选对齐)"
|
|
|
|
|
- @blur="syncSelectedFromArea"
|
|
|
|
|
- />
|
|
|
|
|
- </a-space>
|
|
|
|
|
- </a-form-item>
|
|
|
|
|
-
|
|
|
|
|
- <a-form-item label="排除地区 area_ex">
|
|
|
|
|
- <a-input v-model="form.area_ex" allow-clear placeholder="可选,逗号分隔" />
|
|
|
|
|
- </a-form-item>
|
|
|
|
|
- <a-form-item label="运营商 isp">
|
|
|
|
|
- <a-select v-model="form.isp" allow-clear placeholder="不筛选">
|
|
|
|
|
- <a-option :value="undefined">不筛选</a-option>
|
|
|
|
|
- <a-option :value="1">电信</a-option>
|
|
|
|
|
- <a-option :value="2">移动</a-option>
|
|
|
|
|
- <a-option :value="3">联通</a-option>
|
|
|
|
|
- </a-select>
|
|
|
|
|
- </a-form-item>
|
|
|
|
|
- <a-form-item label="distinct 去重提取">
|
|
|
|
|
- <a-switch v-model="form.distinct_extract" />
|
|
|
|
|
- </a-form-item>
|
|
|
|
|
- <a-form-item label="保存时清空 Redis 当前 IP">
|
|
|
|
|
- <a-checkbox v-model="form.invalidate_cache">清空(下次强制重新提取)</a-checkbox>
|
|
|
|
|
- </a-form-item>
|
|
|
|
|
- <a-space>
|
|
|
|
|
- <a-button type="primary" @click="saveConfig" :loading="saving">保存配置</a-button>
|
|
|
|
|
- <a-button @click="loadStatus">刷新状态</a-button>
|
|
|
|
|
- </a-space>
|
|
|
|
|
- </a-form>
|
|
|
|
|
- </a-card>
|
|
|
|
|
- </a-col>
|
|
|
|
|
- </a-row>
|
|
|
|
|
-
|
|
|
|
|
- <a-card title="出站与切换摘要" class="panel log-card" style="margin-top: 16px" :bordered="false">
|
|
|
|
|
- <template #extra>
|
|
|
|
|
- <a-space>
|
|
|
|
|
- <a-button
|
|
|
|
|
- type="primary"
|
|
|
|
|
- status="danger"
|
|
|
|
|
- :disabled="!selectedLogKeys.length"
|
|
|
|
|
- :loading="logDeleting"
|
|
|
|
|
- @click="deleteSelectedLogs"
|
|
|
|
|
- >
|
|
|
|
|
- 删除所选({{ selectedLogKeys.length }})
|
|
|
|
|
- </a-button>
|
|
|
|
|
- <a-button type="outline" status="danger" :loading="logDeleting" @click="confirmDeleteAllLogs">
|
|
|
|
|
- 清空全部
|
|
|
|
|
- </a-button>
|
|
|
|
|
- </a-space>
|
|
|
|
|
- </template>
|
|
|
|
|
- <div class="table-wrap">
|
|
|
|
|
- <a-table
|
|
|
|
|
- v-model:selected-keys="selectedLogKeys"
|
|
|
|
|
- row-key="id"
|
|
|
|
|
- class="table"
|
|
|
|
|
- :row-selection="{ type: 'checkbox', showCheckedAll: true }"
|
|
|
|
|
- :data="logData"
|
|
|
|
|
- :loading="logLoading"
|
|
|
|
|
- :bordered="false"
|
|
|
|
|
- :scroll="{ x: TABLE_SCROLL_X }"
|
|
|
|
|
- :pagination="{
|
|
|
|
|
- showPageSize: true,
|
|
|
|
|
- showJumper: true,
|
|
|
|
|
- showTotal: true,
|
|
|
|
|
- pageSize: pagination.pagesize,
|
|
|
|
|
- current: pagination.current,
|
|
|
|
|
- total: pagination.total
|
|
|
|
|
- }"
|
|
|
|
|
- @page-change="handlePageChange"
|
|
|
|
|
- @page-size-change="handlePageSizeChange"
|
|
|
|
|
- >
|
|
|
|
|
- <template #columns>
|
|
|
|
|
- <a-table-column title="时间" :width="178">
|
|
|
|
|
- <template #cell="{ record }">{{ strTime(record.created_at) }}</template>
|
|
|
|
|
- </a-table-column>
|
|
|
|
|
- <a-table-column title="类型" :width="120">
|
|
|
|
|
- <template #cell="{ record }">
|
|
|
|
|
- <a-tag size="small" :color="record.event_color">{{ record.event_label }}</a-tag>
|
|
|
|
|
- </template>
|
|
|
|
|
- </a-table-column>
|
|
|
|
|
- <a-table-column title="节点 server" data-index="server" :width="170" ellipsis tooltip />
|
|
|
|
|
- <a-table-column title="出口 IP" :width="150">
|
|
|
|
|
- <template #cell="{ record }">
|
|
|
|
|
- <span class="mono">{{ record.egress_ip ?? '—' }}</span>
|
|
|
|
|
- </template>
|
|
|
|
|
- </a-table-column>
|
|
|
|
|
- <a-table-column title="出口属地" :width="190">
|
|
|
|
|
- <template #cell="{ record }">
|
|
|
|
|
- <span v-if="!record.egress_ip" class="hint-muted">—</span>
|
|
|
|
|
- <a-tag v-else color="cyan" size="small">{{ shortenRegion(record.egress_region || '未知') }}</a-tag>
|
|
|
|
|
- </template>
|
|
|
|
|
- </a-table-column>
|
|
|
|
|
- <a-table-column title="摘要">
|
|
|
|
|
- <template #cell="{ record }">
|
|
|
|
|
- <div class="summary-text">{{ record.summary }}</div>
|
|
|
|
|
- </template>
|
|
|
|
|
- </a-table-column>
|
|
|
|
|
- <a-table-column title="操作" :fixed="tableFixed('right')" :width="88">
|
|
|
|
|
- <template #cell="{ record }">
|
|
|
|
|
- <a-popconfirm content="删除该条日志?" @ok="deleteOneLog(record.id)">
|
|
|
|
|
- <a-button type="text" size="mini" status="danger">删除</a-button>
|
|
|
|
|
- </a-popconfirm>
|
|
|
|
|
- </template>
|
|
|
|
|
- </a-table-column>
|
|
|
|
|
- </template>
|
|
|
|
|
- </a-table>
|
|
|
|
|
- </div>
|
|
|
|
|
- </a-card>
|
|
|
|
|
- </template>
|
|
|
|
|
- </a-skeleton>
|
|
|
|
|
- </div>
|
|
|
|
|
-</template>
|
|
|
|
|
-
|
|
|
|
|
-<script setup>
|
|
|
|
|
-import { reactive, ref, computed, watch, onMounted } from 'vue'
|
|
|
|
|
-import { Notification, Modal } from '@arco-design/web-vue'
|
|
|
|
|
-import { useResponsiveTable } from '@/hooks/useResponsiveTable'
|
|
|
|
|
-import {
|
|
|
|
|
- getAdminLepaoProxyStatus,
|
|
|
|
|
- postAdminLepaoProxyConfig,
|
|
|
|
|
- getAdminLepaoProxyLogs,
|
|
|
|
|
- postAdminLepaoProxyLogsDelete,
|
|
|
|
|
- getAdminLepaoProxyResources
|
|
|
|
|
-} from '@/api/lepao'
|
|
|
|
|
-
|
|
|
|
|
-const { isMobile, tableFixed } = useResponsiveTable()
|
|
|
|
|
-
|
|
|
|
|
-/** 列宽合计(含勾选列与摘要列最小宽度),保证小屏可横向滚动 */
|
|
|
|
|
-const TABLE_SCROLL_X = 1200
|
|
|
|
|
-
|
|
|
|
|
-const pageLoading = ref(false)
|
|
|
|
|
-const logLoading = ref(false)
|
|
|
|
|
-const logDeleting = ref(false)
|
|
|
|
|
-const selectedLogKeys = ref([])
|
|
|
|
|
-const saving = ref(false)
|
|
|
|
|
-const status = ref(null)
|
|
|
|
|
-const logData = ref([])
|
|
|
|
|
-const resourcesRaw = ref([])
|
|
|
|
|
-const resLoading = ref(false)
|
|
|
|
|
-const onlyAvailableRes = ref(true)
|
|
|
|
|
-const selectedAreaCodes = ref([])
|
|
|
|
|
-
|
|
|
|
|
-const pagination = reactive({
|
|
|
|
|
- total: 0,
|
|
|
|
|
- current: 1,
|
|
|
|
|
- pagesize: 20
|
|
|
|
|
-})
|
|
|
|
|
-
|
|
|
|
|
-const form = reactive({
|
|
|
|
|
- proxy_enabled: false,
|
|
|
|
|
- area: '',
|
|
|
|
|
- area_ex: '',
|
|
|
|
|
- isp: undefined,
|
|
|
|
|
- distinct_extract: true,
|
|
|
|
|
- invalidate_cache: false
|
|
|
|
|
-})
|
|
|
|
|
-
|
|
|
|
|
-watch(
|
|
|
|
|
- selectedAreaCodes,
|
|
|
|
|
- (v) => {
|
|
|
|
|
- form.area = Array.isArray(v) && v.length ? v.join(',') : ''
|
|
|
|
|
- },
|
|
|
|
|
- { deep: true }
|
|
|
|
|
-)
|
|
|
|
|
-
|
|
|
|
|
-const filteredResourceOptions = computed(() => {
|
|
|
|
|
- const rows = resourcesRaw.value || []
|
|
|
|
|
- let list = onlyAvailableRes.value ? rows.filter((r) => r.available === true) : [...rows]
|
|
|
|
|
- return list.map((r) => {
|
|
|
|
|
- const code = String(r.area_code ?? '')
|
|
|
|
|
- const isp = r.isp ?? ''
|
|
|
|
|
- const ok = r.available ? '可提取' : '暂不可用'
|
|
|
|
|
- return {
|
|
|
|
|
- label: `[${code}] ${r.area ?? ''} · ${isp} · ${ok}`,
|
|
|
|
|
- value: code
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
-})
|
|
|
|
|
-
|
|
|
|
|
-const resourcePlaceholder = computed(() =>
|
|
|
|
|
- resourcesRaw.value.length ? '搜索城市 / 运营商 / 编码…' : '请先点击「刷新资源列表」'
|
|
|
|
|
-)
|
|
|
|
|
-
|
|
|
|
|
-function filterResourceOption(input, option) {
|
|
|
|
|
- return String(option?.label ?? '')
|
|
|
|
|
- .toLowerCase()
|
|
|
|
|
- .includes(String(input || '').toLowerCase())
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function shortenRegion(r) {
|
|
|
|
|
- if (!r || r === '未知') return '未知'
|
|
|
|
|
- const parts = String(r).split(' · ')
|
|
|
|
|
- return parts.slice(-2).join(' · ') || r
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const strTime = (t) =>
|
|
|
|
|
- t == null
|
|
|
|
|
- ? '—'
|
|
|
|
|
- : new Date(Number(t)).toLocaleString('zh-CN', {
|
|
|
|
|
- year: 'numeric',
|
|
|
|
|
- month: '2-digit',
|
|
|
|
|
- day: '2-digit',
|
|
|
|
|
- hour: '2-digit',
|
|
|
|
|
- minute: '2-digit',
|
|
|
|
|
- second: '2-digit'
|
|
|
|
|
- })
|
|
|
|
|
-
|
|
|
|
|
-const syncSelectedFromArea = () => {
|
|
|
|
|
- selectedAreaCodes.value = form.area
|
|
|
|
|
- ? String(form.area)
|
|
|
|
|
- .split(',')
|
|
|
|
|
- .map((s) => s.trim())
|
|
|
|
|
- .filter(Boolean)
|
|
|
|
|
- .map(String)
|
|
|
|
|
- : []
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const applyStatus = (data) => {
|
|
|
|
|
- status.value = data
|
|
|
|
|
- form.proxy_enabled = !!data?.proxy_enabled
|
|
|
|
|
- form.area = data?.area ?? ''
|
|
|
|
|
- form.area_ex = data?.area_ex ?? ''
|
|
|
|
|
- form.isp = data?.isp === null || data?.isp === '' ? undefined : Number(data.isp)
|
|
|
|
|
- form.distinct_extract = data?.distinct_extract !== false
|
|
|
|
|
- syncSelectedFromArea()
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const loadStatus = async () => {
|
|
|
|
|
- pageLoading.value = true
|
|
|
|
|
- try {
|
|
|
|
|
- const res = await getAdminLepaoProxyStatus({})
|
|
|
|
|
- if (!res || res.code !== 0) {
|
|
|
|
|
- Notification.error({ title: '读取状态失败', content: res?.msg ?? '请稍后再试' })
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- applyStatus(res.data)
|
|
|
|
|
- } finally {
|
|
|
|
|
- pageLoading.value = false
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const loadResources = async () => {
|
|
|
|
|
- resLoading.value = true
|
|
|
|
|
- try {
|
|
|
|
|
- const res = await getAdminLepaoProxyResources({})
|
|
|
|
|
- if (!res || res.code !== 0) {
|
|
|
|
|
- Notification.error({ title: '资源列表获取失败', content: res?.msg ?? '请检查 extractKey 与网络' })
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- resourcesRaw.value = res.data || []
|
|
|
|
|
- Notification.success({ title: '资源列表已更新', content: `共 ${resourcesRaw.value.length} 条` })
|
|
|
|
|
- } finally {
|
|
|
|
|
- resLoading.value = false
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const loadLogs = async () => {
|
|
|
|
|
- logLoading.value = true
|
|
|
|
|
- try {
|
|
|
|
|
- const res = await getAdminLepaoProxyLogs({
|
|
|
|
|
- current: pagination.current,
|
|
|
|
|
- pagesize: pagination.pagesize
|
|
|
|
|
- })
|
|
|
|
|
- if (!res || res.code !== 0) {
|
|
|
|
|
- Notification.error({ title: '读取日志失败', content: res?.msg ?? '请稍后再试' })
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- logData.value = res.data || []
|
|
|
|
|
- pagination.total = res.pagination?.total || 0
|
|
|
|
|
- selectedLogKeys.value = []
|
|
|
|
|
- } finally {
|
|
|
|
|
- logLoading.value = false
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const deleteOneLog = async (id) => {
|
|
|
|
|
- logDeleting.value = true
|
|
|
|
|
- try {
|
|
|
|
|
- const res = await postAdminLepaoProxyLogsDelete({ ids: [id] })
|
|
|
|
|
- if (!res || res.code !== 0) {
|
|
|
|
|
- Notification.error({ title: '删除失败', content: res?.msg ?? '请稍后再试' })
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- Notification.success({ title: '已删除', content: '' })
|
|
|
|
|
- await loadLogs()
|
|
|
|
|
- } finally {
|
|
|
|
|
- logDeleting.value = false
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const deleteSelectedLogs = async () => {
|
|
|
|
|
- if (!selectedLogKeys.value.length) return
|
|
|
|
|
- logDeleting.value = true
|
|
|
|
|
- try {
|
|
|
|
|
- const res = await postAdminLepaoProxyLogsDelete({ ids: selectedLogKeys.value.map(Number) })
|
|
|
|
|
- if (!res || res.code !== 0) {
|
|
|
|
|
- Notification.error({ title: '删除失败', content: res?.msg ?? '请稍后再试' })
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- Notification.success({ title: `已删除 ${res.data?.deleted ?? selectedLogKeys.value.length} 条`, content: '' })
|
|
|
|
|
- pagination.current = 1
|
|
|
|
|
- await loadLogs()
|
|
|
|
|
- } finally {
|
|
|
|
|
- logDeleting.value = false
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const confirmDeleteAllLogs = () => {
|
|
|
|
|
- Modal.confirm({
|
|
|
|
|
- title: '清空全部代理日志',
|
|
|
|
|
- content: '将删除表中全部 lepao_proxy_log 记录,不可恢复。',
|
|
|
|
|
- okText: '确认清空',
|
|
|
|
|
- modalStyle: { maxWidth: '420px' },
|
|
|
|
|
- okButtonProps: { status: 'danger' },
|
|
|
|
|
- onOk: async () => {
|
|
|
|
|
- logDeleting.value = true
|
|
|
|
|
- try {
|
|
|
|
|
- const res = await postAdminLepaoProxyLogsDelete({ purge_all: 1 })
|
|
|
|
|
- if (!res || res.code !== 0) {
|
|
|
|
|
- Notification.error({ title: '清空失败', content: res?.msg ?? '请稍后再试' })
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- Notification.success({ title: '已清空日志', content: '' })
|
|
|
|
|
- pagination.current = 1
|
|
|
|
|
- await loadLogs()
|
|
|
|
|
- } finally {
|
|
|
|
|
- logDeleting.value = false
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const saveConfig = async () => {
|
|
|
|
|
- saving.value = true
|
|
|
|
|
- try {
|
|
|
|
|
- const res = await postAdminLepaoProxyConfig({
|
|
|
|
|
- proxy_enabled: form.proxy_enabled ? 1 : 0,
|
|
|
|
|
- area: form.area,
|
|
|
|
|
- area_ex: form.area_ex,
|
|
|
|
|
- isp: form.isp === undefined ? '' : form.isp,
|
|
|
|
|
- distinct_extract: form.distinct_extract ? 1 : 0,
|
|
|
|
|
- invalidate_cache: form.invalidate_cache ? 1 : 0
|
|
|
|
|
- })
|
|
|
|
|
- if (!res || res.code !== 0) {
|
|
|
|
|
- Notification.error({ title: '保存失败', content: res?.msg ?? '请稍后再试' })
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- Notification.success({ title: '已保存', content: '' })
|
|
|
|
|
- form.invalidate_cache = false
|
|
|
|
|
- await loadStatus()
|
|
|
|
|
- await loadLogs()
|
|
|
|
|
- } finally {
|
|
|
|
|
- saving.value = false
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const handlePageChange = (page) => {
|
|
|
|
|
- pagination.current = page
|
|
|
|
|
- loadLogs()
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const handlePageSizeChange = (size) => {
|
|
|
|
|
- pagination.pagesize = size
|
|
|
|
|
- pagination.current = 1
|
|
|
|
|
- loadLogs()
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-onMounted(async () => {
|
|
|
|
|
- await loadStatus()
|
|
|
|
|
- await loadLogs()
|
|
|
|
|
-})
|
|
|
|
|
-</script>
|
|
|
|
|
-
|
|
|
|
|
-<style scoped lang="less">
|
|
|
|
|
-.lepao-proxy-page {
|
|
|
|
|
- padding: 16px 20px 32px;
|
|
|
|
|
- max-width: 1280px;
|
|
|
|
|
- margin: 0 auto;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.lepao-proxy-skeleton {
|
|
|
|
|
- width: 100%;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.hero-card {
|
|
|
|
|
- background: linear-gradient(135deg, var(--color-bg-2), var(--color-fill-2));
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.hero-inner {
|
|
|
|
|
- padding: 4px 0;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.hero-title {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 12px;
|
|
|
|
|
-
|
|
|
|
|
- .t1 {
|
|
|
|
|
- font-size: 20px;
|
|
|
|
|
- font-weight: 600;
|
|
|
|
|
- color: var(--color-text-1);
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.hero-desc {
|
|
|
|
|
- margin-top: 10px;
|
|
|
|
|
- color: var(--color-text-2);
|
|
|
|
|
- font-size: 13px;
|
|
|
|
|
- line-height: 1.6;
|
|
|
|
|
-
|
|
|
|
|
- code {
|
|
|
|
|
- padding: 0 6px;
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
- background: var(--color-fill-3);
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.panel {
|
|
|
|
|
- border-radius: 12px;
|
|
|
|
|
-
|
|
|
|
|
- &.log-card {
|
|
|
|
|
- box-shadow: 0 1px 4px rgb(0 0 0 / 4%);
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.mono {
|
|
|
|
|
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
|
|
|
|
|
- font-size: 13px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.side-tips .tips-list {
|
|
|
|
|
- margin: 0;
|
|
|
|
|
- padding-left: 18px;
|
|
|
|
|
- color: var(--color-text-2);
|
|
|
|
|
- font-size: 13px;
|
|
|
|
|
- line-height: 1.85;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.summary-text {
|
|
|
|
|
- font-size: 13px;
|
|
|
|
|
- line-height: 1.65;
|
|
|
|
|
- color: var(--color-text-2);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.hint-muted {
|
|
|
|
|
- color: var(--color-text-3);
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.table-wrap {
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- overflow-x: auto;
|
|
|
|
|
- -webkit-overflow-scrolling: touch;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.table {
|
|
|
|
|
- min-width: 100%;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-@media (max-width: 768px) {
|
|
|
|
|
- .lepao-proxy-page {
|
|
|
|
|
- padding: 12px 12px 24px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .log-card :deep(.arco-card-header) {
|
|
|
|
|
- flex-wrap: wrap;
|
|
|
|
|
- gap: 8px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .log-card :deep(.arco-card-header-extra) {
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- margin-left: 0 !important;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .log-card :deep(.arco-card-header-extra .arco-space) {
|
|
|
|
|
- flex-wrap: wrap;
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-</style>
|
|
|