feat(admin-frontend): 补齐用户节点与订单运营工作台

新增用户高级筛选、批量操作与更多行级动作,支持邮件、
CSV、封禁恢复、订单分配、邀请查看、流量记录与重置流量

增强节点管理页的分页、父子筛选、跨页勾选、批量修改与
单节点置顶,并补齐后端批量更新 host、group_ids、rate

修复订单佣金状态误判问题,新增真实佣金筛选与行级确认,
同时优化仪表盘排行悬浮详情展示

补充 admin-frontend 独立 Dockerfile、Caddy 配置与 GHCR
发布工作流,支持通过独立镜像部署管理前端
This commit is contained in:
yinjianm
2026-04-24 23:15:48 +08:00
parent e393b11b61
commit d4168720ac
65 changed files with 4114 additions and 438 deletions
@@ -0,0 +1,178 @@
<script setup lang="ts">
import { computed, reactive, watch } from 'vue'
import { ElMessage } from 'element-plus'
import type { AdminServerGroupItem } from '@/types/api'
interface NodeBatchEditPayload {
host?: string
rate?: number
group_ids?: number[]
}
const props = defineProps<{
visible: boolean
groups: AdminServerGroupItem[]
selectedCount: number
loading?: boolean
}>()
const emit = defineEmits<{
'update:visible': [value: boolean]
submit: [payload: NodeBatchEditPayload]
}>()
const form = reactive({
updateHost: false,
host: '',
updateRate: false,
rate: 1,
updateGroups: false,
groupIds: [] as number[],
})
const hasEnabledField = computed(() => form.updateHost || form.updateRate || form.updateGroups)
function resetForm() {
form.updateHost = false
form.host = ''
form.updateRate = false
form.rate = 1
form.updateGroups = false
form.groupIds = []
}
function closeDialog() {
emit('update:visible', false)
}
function handleSubmit() {
if (!hasEnabledField.value) {
ElMessage.warning('请至少开启一个需要批量修改的字段')
return
}
if (form.updateHost && !form.host.trim()) {
ElMessage.warning('请输入新的节点地址 host')
return
}
if (form.updateRate && (!Number.isFinite(Number(form.rate)) || Number(form.rate) <= 0)) {
ElMessage.warning('请输入大于 0 的倍率')
return
}
emit('submit', {
host: form.updateHost ? form.host.trim() : undefined,
rate: form.updateRate ? Number(form.rate) : undefined,
group_ids: form.updateGroups ? [...form.groupIds] : undefined,
})
}
watch(
() => props.visible,
(visible) => {
if (visible) {
resetForm()
}
},
)
</script>
<template>
<ElDialog
:model-value="props.visible"
width="min(680px, calc(100vw - 24px))"
class="node-batch-edit-dialog"
destroy-on-close
@close="closeDialog"
@update:model-value="emit('update:visible', $event)"
>
<div class="batch-shell">
<header class="batch-hero">
<div>
<h2>批量修改节点</h2>
<p>本轮仅对已勾选节点生效支持统一修改节点地址 host权限组和倍率</p>
</div>
<ElTag round effect="dark">
已选 {{ props.selectedCount }} 个节点
</ElTag>
</header>
<section class="batch-section">
<label class="batch-switch-card">
<div>
<strong>批量修改节点地址</strong>
<span>只修改 `host`不改端口适合整批切换域名或 IP</span>
</div>
<ElSwitch v-model="form.updateHost" />
</label>
<ElInput
v-model="form.host"
:disabled="!form.updateHost"
placeholder="例如 node.example.com 或 1.2.3.4"
/>
</section>
<section class="batch-section">
<label class="batch-switch-card">
<div>
<strong>批量修改权限组</strong>
<span>启用后会整体替换所选节点的权限组留空表示清空权限组</span>
</div>
<ElSwitch v-model="form.updateGroups" />
</label>
<ElSelect
v-model="form.groupIds"
multiple
collapse-tags
collapse-tags-tooltip
:disabled="!form.updateGroups"
placeholder="请选择权限组"
>
<ElOption
v-for="group in props.groups"
:key="group.id"
:label="group.name"
:value="group.id"
/>
</ElSelect>
</section>
<section class="batch-section">
<label class="batch-switch-card">
<div>
<strong>批量修改倍率</strong>
<span>适合统一调整节点倍率不会改动动态倍率规则</span>
</div>
<ElSwitch v-model="form.updateRate" />
</label>
<ElInputNumber
v-model="form.rate"
:disabled="!form.updateRate"
:min="0.01"
:step="0.01"
:precision="2"
:controls="false"
class="full-width"
/>
</section>
</div>
<template #footer>
<div class="batch-footer">
<span class="batch-footer__hint">批量修改不会影响端口协议配置与显隐状态</span>
<div class="batch-footer__actions">
<ElButton @click="closeDialog">取消</ElButton>
<ElButton type="primary" :loading="props.loading" @click="handleSubmit">
确认批量修改
</ElButton>
</div>
</div>
</template>
</ElDialog>
</template>
<style scoped lang="scss" src="./NodeBatchEditDialog.scss"></style>