2
0
Files
fe.wfc/src/views/billing/ratelimit/index.vue
2025-02-26 16:22:10 +08:00

369 lines
11 KiB
Vue

<template>
<SimpleScrollbar>
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
<ACard
:title="t('page.ratelimit.title')"
:bordered="false"
:body-style="{ flex: 1, overflow: 'hidden' }"
class="flex-col-stretch sm:flex-1-hidden card-wrapper"
>
<template #extra>
<div class="flex items-center gap-2">
<TableHeaderOperation
v-model:columns="columnChecks"
:loading="loading"
:show-delete="false"
:show-add="false"
@refresh="getData"
@add="handleAdd"
/>
</div>
</template>
<ATable
ref="wrapperEl"
:columns="columns"
:data-source="data"
:loading="loading"
row-key="rateLimitName"
size="small"
:pagination="mobilePagination"
:scroll="scrollConfig"
class="h-full"
/>
</ACard>
<!-- 新增限速配置弹窗 -->
<AModal
v-model:visible="showModal"
:title="isEdit ? t('page.ratelimit.change') : t('page.ratelimit.add')"
@ok="handleOk"
@cancel="handleCancel"
>
<AForm
ref="formRef"
:model="formState"
:rules="rules"
:label-col="{ span: 9 }"
:wrapper-col="{ span: 13 }"
>
<AFormItem :label="t('page.ratelimit.name')" name="rateLimitName">
<AInput v-model:value="formState.rateLimitName" :placeholder="t('page.ratelimit.plename')" />
</AFormItem>
<AFormItem :label="t('page.ratelimit.upratelimit')" name="upLimitEnable">
<ACheckbox v-model:checked="formState.upLimitEnable">{{ t('page.ratelimit.useuplimit') }}</ACheckbox>
</AFormItem>
<AFormItem
v-if="formState.upLimitEnable"
:label="t('page.ratelimit.uplimit')"
name="upLimit"
>
<div class="flex gap-2">
<AFormItem name="upLimit" class="mb-0 flex-1">
<AInputNumber
v-model:value="formState.upLimit"
:placeholder="t('page.ratelimit.pleuplimit')"
:min="1"
:max="1024"
:precision="0"
style="width: 100%"
/>
</AFormItem>
<AFormItem name="upLimitUnit" class="mb-0">
<ASelect
v-model:value="formState.upLimitUnit"
:options="bandwidthUnits.map(unit => ({ label: unit, value: unit }))"
style="width: 100px"
/>
</AFormItem>
</div>
</AFormItem>
<AFormItem :label="t('page.ratelimit.downratelimit')" name="downLimitEnable">
<ACheckbox v-model:checked="formState.downLimitEnable">{{ t('page.ratelimit.usedownlimit') }}</ACheckbox>
</AFormItem>
<AFormItem
v-if="formState.downLimitEnable"
:label="t('page.ratelimit.downlimit')"
name="downLimit"
>
<div class="flex gap-2">
<AFormItem name="downLimit" class="mb-0 flex-1">
<AInputNumber
v-model:value="formState.downLimit"
:placeholder="t('page.ratelimit.pledownlimit')"
:min="1"
:max="1024"
:precision="0"
style="width: 100%"
/>
</AFormItem>
<AFormItem name="downLimitUnit" class="mb-0">
<ASelect
v-model:value="formState.downLimitUnit"
:options="bandwidthUnits.map(unit => ({ label: unit, value: unit }))"
style="width: 100px"
/>
</AFormItem>
</div>
</AFormItem>
</AForm>
</AModal>
</div>
</SimpleScrollbar>
</template>
<script setup lang="tsx">
import { useTable } from '@/hooks/common/table';
import { SimpleScrollbar } from '~/packages/materials/src';
import { computed, shallowRef, ref } from 'vue';
import { useElementSize } from '@vueuse/core';
import { fetchRateLimitList, addRateLimit, editRateLimit, removeRateLimit } from '@/service/api/auth';
import { message, Modal, Button } from 'ant-design-vue';
import type { Rule } from 'ant-design-vue/es/form';
import { PlusOutlined, ExclamationCircleOutlined } from '@ant-design/icons-vue';
import { bandwidthUnits, convertBandwidth, formatBandwidth, type BandwidthUnit } from '@/utils/units';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
interface RateLimitForm extends Omit<Api.Auth.RateLimitAdd, 'upLimit' | 'downLimit'> {
upLimit: number;
downLimit: number;
upLimitUnit: BandwidthUnit;
downLimitUnit: BandwidthUnit;
}
const wrapperEl = shallowRef<HTMLElement | null>(null);
const { height: wrapperElHeight } = useElementSize(wrapperEl);
const scrollConfig = computed(() => {
return {
y: wrapperElHeight.value - 72,
x: 1000
};
});
const showModal = ref(false);
const formRef = ref();
const formState = ref<RateLimitForm>({
rateLimitName: '',
upLimitEnable: false,
downLimitEnable: false,
upLimit: 0,
downLimit: 0,
upLimitUnit: 'Mbps',
downLimitUnit: 'Mbps'
});
const rules: Record<string, Rule[]> = {
rateLimitName: [{ required: true, message: t('page.ratelimit.plename'), trigger: 'blur' }],
upLimit: [
{
required: true,
message: t('page.ratelimit.pleuplimit'),
trigger: 'blur',
validator: (_rule: Rule, value: number) => {
if (!formState.value.upLimitEnable) return Promise.resolve();
if (!value || value <= 0) return Promise.reject(t('page.ratelimit.reject'));
return Promise.resolve();
}
}
],
downLimit: [
{
required: true,
message: t('page.ratelimit.pledownlimit'),
trigger: 'blur',
validator: (_rule: Rule, value: number) => {
if (!formState.value.downLimitEnable) return Promise.resolve();
if (!value || value <= 0) return Promise.reject(t('page.ratelimit.reject'));
return Promise.resolve();
}
}
]
};
const isEdit = ref(false);
const editId = ref<number>();
const handleAdd = () => {
isEdit.value = false;
editId.value = undefined;
formState.value = {
rateLimitName: '',
upLimitEnable: false,
downLimitEnable: false,
upLimit: 0,
downLimit: 0,
upLimitUnit: 'Mbps',
downLimitUnit: 'Mbps'
};
showModal.value = true;
};
const handleEdit = (record: Api.Auth.RateLimit) => {
isEdit.value = true;
editId.value = record.id;
formState.value = {
rateLimitName: record.rateLimitName,
upLimitEnable: record.upLimitEnable,
downLimitEnable: record.downLimitEnable,
upLimit: convertBandwidth(record.upLimit, 'Kbps', 'Mbps'),
downLimit: convertBandwidth(record.downLimit, 'Kbps', 'Mbps'),
upLimitUnit: 'Mbps',
downLimitUnit: 'Mbps'
};
showModal.value = true;
};
const handleDelete = (record: Api.Auth.RateLimit) => {
Modal.confirm({
title: t('page.ratelimit.confirmdelete'),
icon: () => <ExclamationCircleOutlined />,
content: `${t('page.ratelimit.deletecontant')} "${record.rateLimitName}"?`,
async onOk() {
try {
await removeRateLimit(record.id);
message.success(t('page.ratelimit.deletesuc'));
getData();
} catch (error) {
// console.error('error:', error);
}
}
});
};
const handleCancel = () => {
showModal.value = false;
formState.value = {
rateLimitName: '',
upLimitEnable: false,
downLimitEnable: false,
upLimit: 0,
downLimit: 0,
upLimitUnit: 'Mbps',
downLimitUnit: 'Mbps'
};
};
const handleOk = async () => {
try {
await formRef.value?.validate();
const submitData = {
...formState.value,
upLimit: formState.value.upLimitEnable
? Math.round(convertBandwidth(formState.value.upLimit, formState.value.upLimitUnit, 'Kbps'))
: 0,
downLimit: formState.value.downLimitEnable
? Math.round(convertBandwidth(formState.value.downLimit, formState.value.downLimitUnit, 'Kbps'))
: 0
};
if (isEdit.value) {
if (!editId.value) {
message.error(t('page.ratelimit.idnull'));
return;
}
await editRateLimit({
id: editId.value,
rateLimitName: submitData.rateLimitName,
upLimitEnable: submitData.upLimitEnable,
downLimitEnable: submitData.downLimitEnable,
upLimit: submitData.upLimit,
downLimit: submitData.downLimit
});
message.success(t('page.ratelimit.changesuc'));
} else {
await addRateLimit(submitData);
message.success(t('page.ratelimit.addsuc'));
}
handleCancel();
getData();
} catch (error) {
// message.error(isEdit.value ? 'change error' : 'add error');
}
};
const {
columns,
columnChecks,
data,
loading,
getData,
mobilePagination
} = useTable({
apiFn: async () => {
try {
const response = await fetchRateLimitList();
return {
data: {
rows: response.data || [],
total: response.data?.length || 0
}
};
} catch (err) {
return {
data: {
rows: [],
total: 0
}
};
}
},
immediate: true,
rowKey: 'id',
columns: (): AntDesign.TableColumn<Api.Auth.RateLimit>[] => [
{
key: 'rateLimitName',
dataIndex: 'rateLimitName',
title: t('page.ratelimit.name'),
align: 'center'
},
{
key: 'upLimit',
dataIndex: 'upLimit',
title: t('page.ratelimit.upratelimit'),
align: 'center',
customRender: ({ text, record }) => {
if (!record.upLimitEnable) return t('page.ratelimit.unlimit');
const { value, unit } = formatBandwidth(text);
return `${value} ${unit}`;
}
},
{
key: 'downLimit',
dataIndex: 'downLimit',
title: t('page.ratelimit.downratelimit'),
align: 'center',
customRender: ({ text, record }) => {
if (!record.downLimitEnable) return t('page.ratelimit.unlimit');
const { value, unit } = formatBandwidth(text);
return `${value} ${unit}`;
}
},
{
title: t('page.ratelimit.operate'),
key: 'operate',
align: 'center',
width: 200,
fixed: 'right',
customRender: ({ record }) => (
<div class="flex justify-center gap-2">
<Button type="link" onClick={() => handleEdit(record)}>
{ t('page.ratelimit.changed') }
</Button>
<Button type="link" danger onClick={() => handleDelete(record)}>
{t('page.ratelimit.delete')}
</Button>
</div>
)
}
]
});
</script>
<style scoped></style>