三页面重设计与移动端适配;接入 PWA 与构建分包
CI / test (push) Successful in 47s

This commit is contained in:
2026-07-21 19:26:22 +08:00
parent ac457282ce
commit 6129173fe6
64 changed files with 6243 additions and 549 deletions
+14 -19
View File
@@ -3,7 +3,6 @@ import {
NButton,
NDataTable,
NInput,
NModal,
NRadioButton,
NRadioGroup,
type DataTableColumns,
@@ -22,6 +21,7 @@ import ConfirmPop from '@/components/ConfirmPop.vue'
import AppInputNumber from '@/components/AppInputNumber.vue'
import FootNote from '@/components/FootNote.vue'
import FormField from '@/components/FormField.vue'
import FormModal from '@/components/FormModal.vue'
import ProxyTenantsModal from '@/components/proxy/ProxyTenantsModal.vue'
import StatusBadge from '@/components/StatusBadge.vue'
import { useAsync } from '@/composables/useAsync'
@@ -279,7 +279,7 @@ const columns: DataTableColumns<ProxyInfo> = [
:row-key="(r: ProxyInfo) => r.id"
/>
<FootNote>
支持 SOCKS5 / HTTP / HTTPS;密码 AES 加密存储、不回显明文;地区与状态为经代理实测的出口探测结果(ip-api.com),创建后自动检测,探测失败即视为异常,可点「重测」复查;被租户关联的代理不可删除,请先解除关联
支持 SOCKS5 / HTTP / HTTPS;被租户关联的代理先解除关联才可删除
</FootNote>
<ProxyTenantsModal
@@ -288,11 +288,12 @@ const columns: DataTableColumns<ProxyInfo> = [
@saved="void proxies.run({ silent: true })"
/>
<NModal
<FormModal
v-model:show="showForm"
preset="card"
:title="editing ? '编辑代理' : '新增代理'"
:style="{ width: '480px', maxWidth: 'calc(100vw - 24px)' }"
submit-text="保存"
:submitting="saving"
@submit="save"
>
<FormField label="类型">
<NRadioGroup v-model:value="form.type" class="w-full" size="small">
@@ -334,7 +335,7 @@ const columns: DataTableColumns<ProxyInfo> = [
/>
</FormField>
</div>
<div class="mb-3.5 flex items-start gap-2.5 rounded-lg border border-ok/30 bg-ok/10 px-3 py-2.5">
<div class="flex items-start gap-2.5 rounded-lg border border-ok/30 bg-ok/10 px-3 py-2.5">
<svg
class="mt-0.5 h-[15px] w-[15px] flex-none text-ok"
viewBox="0 0 24 24"
@@ -352,17 +353,15 @@ const columns: DataTableColumns<ProxyInfo> = [
</div>
</div>
</div>
<div class="mt-1 flex items-center gap-2">
<NButton size="small" type="primary" :loading="saving" @click="save">保存</NButton>
<NButton size="small" quaternary @click="showForm = false">取消</NButton>
</div>
</NModal>
</FormModal>
<NModal
<FormModal
v-model:show="showImport"
preset="card"
title="批量导入代理"
:style="{ width: '560px', maxWidth: 'calc(100vw - 24px)' }"
submit-text="导入"
:submitting="importing"
:width="560"
@submit="doImport"
>
<FormField label="代理列表">
<NInput
@@ -404,10 +403,6 @@ const columns: DataTableColumns<ProxyInfo> = [
<span class="ml-auto flex-none text-[11.5px] text-ink-2">{{ f.reason }}</span>
</div>
</div>
<div class="mt-1 flex items-center gap-2">
<NButton size="small" type="primary" :loading="importing" @click="doImport">导入</NButton>
<NButton size="small" quaternary @click="showImport = false">关闭</NButton>
</div>
</NModal>
</FormModal>
</div>
</template>
+4 -1
View File
@@ -7,6 +7,7 @@ import { setProxyTenants } from '@/api/proxies'
import FormField from '@/components/FormField.vue'
import TenantPicker from '@/components/TenantPicker.vue'
import { useAsync } from '@/composables/useAsync'
import { useMobileModal } from '@/composables/useMobileModal'
import type { ProxyInfo } from '@/types/api'
import { useToast } from '@/composables/useToast'
@@ -16,6 +17,7 @@ const props = defineProps<{ show: boolean; proxy: ProxyInfo | null }>()
const emit = defineEmits<{ 'update:show': [boolean]; saved: [] }>()
const message = useToast()
const { modalStyle, modalClass } = useMobileModal(480)
const configs = useAsync(listConfigs, false)
const selected = ref<number[]>([])
const saving = ref(false)
@@ -66,7 +68,8 @@ async function save() {
:show="show"
preset="card"
:title="`关联租户 · ${proxy?.name ?? ''}`"
:style="{ width: '480px', maxWidth: 'calc(100vw - 24px)' }"
:style="modalStyle"
:class="modalClass"
@update:show="emit('update:show', $event)"
>
<FormField label="关联租户" hint="选中即关联,取消选中即解除;关联后租户全部 SDK 请求经此代理出站">