chore(frontend): introduce prettier + eslint and prune unused code
- Add Prettier 3 with prettier-plugin-organize-imports (sorts/removes unused imports) - Add ESLint 10 flat config with typescript-eslint + eslint-plugin-vue + eslint-config-prettier - Add root scripts: format, format:check, lint, lint:fix - Reformat 257 files across admin-web, ops-web, desktop-client, packages - Remove unused locals/exports flagged by --noUnusedLocals/--noUnusedParameters - Fix duplicate localTabLabel key, surrogate-pair regex u-flag, NBSP literal in regex - Skip server/ (Go) and apps/browser-extension/ (deprecated per ADR) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -70,15 +70,26 @@
|
||||
{{ formatDate(record.updated_at) }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'actions'">
|
||||
<div class="table-actions-row" style="justify-content: flex-end;">
|
||||
<div class="table-actions-row" style="justify-content: flex-end">
|
||||
<a-tooltip title="编辑">
|
||||
<a-button type="text" shape="circle" size="small" class="action-btn action-edit" @click="openEdit(record)">
|
||||
<a-button
|
||||
type="text"
|
||||
shape="circle"
|
||||
size="small"
|
||||
class="action-btn action-edit"
|
||||
@click="openEdit(record)"
|
||||
>
|
||||
<EditOutlined />
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip title="删除">
|
||||
<a-popconfirm title="确认删除该映射?" @confirm="deleteMapping(record)">
|
||||
<a-button type="text" shape="circle" size="small" class="action-btn action-delete">
|
||||
<a-button
|
||||
type="text"
|
||||
shape="circle"
|
||||
size="small"
|
||||
class="action-btn action-delete"
|
||||
>
|
||||
<DeleteOutlined />
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
@@ -107,7 +118,11 @@
|
||||
<a-input v-model:value="form.site_name" placeholder="知乎" />
|
||||
</a-form-item>
|
||||
<a-form-item label="状态">
|
||||
<a-switch v-model:checked="form.is_active" checked-children="启用" un-checked-children="停用" />
|
||||
<a-switch
|
||||
v-model:checked="form.is_active"
|
||||
checked-children="启用"
|
||||
un-checked-children="停用"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
@@ -122,224 +137,219 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
DeleteOutlined,
|
||||
EditOutlined,
|
||||
PlusOutlined,
|
||||
ReloadOutlined,
|
||||
} from "@ant-design/icons-vue";
|
||||
import type { TablePaginationConfig } from "ant-design-vue";
|
||||
import { message } from "ant-design-vue";
|
||||
import dayjs from "dayjs";
|
||||
import { computed, onMounted, reactive, ref } from "vue";
|
||||
import { DeleteOutlined, EditOutlined, PlusOutlined, ReloadOutlined } from '@ant-design/icons-vue'
|
||||
import type { TablePaginationConfig } from 'ant-design-vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import dayjs from 'dayjs'
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
|
||||
import { OpsApiError, http } from "@/lib/http";
|
||||
import { OpsApiError, http } from '@/lib/http'
|
||||
|
||||
interface SiteDomainMappingRow {
|
||||
id: number;
|
||||
registrable_domain: string;
|
||||
site_key: string;
|
||||
site_name: string;
|
||||
is_active: boolean;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
id: number
|
||||
registrable_domain: string
|
||||
site_key: string
|
||||
site_name: string
|
||||
is_active: boolean
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
interface ListResult {
|
||||
items: SiteDomainMappingRow[];
|
||||
total: number;
|
||||
page: number;
|
||||
size: number;
|
||||
items: SiteDomainMappingRow[]
|
||||
total: number
|
||||
page: number
|
||||
size: number
|
||||
}
|
||||
|
||||
const filter = reactive({
|
||||
keyword: "",
|
||||
keyword: '',
|
||||
status: undefined as string | undefined,
|
||||
});
|
||||
})
|
||||
|
||||
const statusOptions = [
|
||||
{ label: "启用", value: "true" },
|
||||
{ label: "停用", value: "false" },
|
||||
];
|
||||
{ label: '启用', value: 'true' },
|
||||
{ label: '停用', value: 'false' },
|
||||
]
|
||||
|
||||
const columns = [
|
||||
{ title: "域名 / Key", key: "registrable_domain", dataIndex: "registrable_domain", width: "34%" },
|
||||
{ title: "中文名", key: "site_name", dataIndex: "site_name", width: "22%" },
|
||||
{ title: "状态", key: "is_active", width: 120 },
|
||||
{ title: "更新时间", key: "updated_at", width: 180 },
|
||||
{ title: "操作", key: "actions", width: 120, align: "right" as const },
|
||||
];
|
||||
{ title: '域名 / Key', key: 'registrable_domain', dataIndex: 'registrable_domain', width: '34%' },
|
||||
{ title: '中文名', key: 'site_name', dataIndex: 'site_name', width: '22%' },
|
||||
{ title: '状态', key: 'is_active', width: 120 },
|
||||
{ title: '更新时间', key: 'updated_at', width: 180 },
|
||||
{ title: '操作', key: 'actions', width: 120, align: 'right' as const },
|
||||
]
|
||||
|
||||
const rows = ref<SiteDomainMappingRow[]>([]);
|
||||
const loading = ref(false);
|
||||
const page = ref(1);
|
||||
const size = ref(20);
|
||||
const total = ref(0);
|
||||
const statusLoadingId = ref<number | null>(null);
|
||||
const rows = ref<SiteDomainMappingRow[]>([])
|
||||
const loading = ref(false)
|
||||
const page = ref(1)
|
||||
const size = ref(20)
|
||||
const total = ref(0)
|
||||
const statusLoadingId = ref<number | null>(null)
|
||||
|
||||
const activeRows = computed(() => rows.value.filter((row) => row.is_active).length);
|
||||
const inactiveRows = computed(() => rows.value.length - activeRows.value);
|
||||
const activeRows = computed(() => rows.value.filter((row) => row.is_active).length)
|
||||
const inactiveRows = computed(() => rows.value.length - activeRows.value)
|
||||
|
||||
const pagination = computed<TablePaginationConfig>(() => ({
|
||||
current: page.value,
|
||||
pageSize: size.value,
|
||||
total: total.value,
|
||||
showSizeChanger: true,
|
||||
pageSizeOptions: ["10", "20", "50", "100"],
|
||||
}));
|
||||
pageSizeOptions: ['10', '20', '50', '100'],
|
||||
}))
|
||||
|
||||
let keywordTimer: number | null = null;
|
||||
let keywordTimer: number | null = null
|
||||
|
||||
function onKeywordChange() {
|
||||
if (keywordTimer) window.clearTimeout(keywordTimer);
|
||||
if (keywordTimer) window.clearTimeout(keywordTimer)
|
||||
keywordTimer = window.setTimeout(() => {
|
||||
page.value = 1;
|
||||
void reload();
|
||||
}, 300);
|
||||
page.value = 1
|
||||
void reload()
|
||||
}, 300)
|
||||
}
|
||||
|
||||
function onStatusChange() {
|
||||
page.value = 1;
|
||||
void reload();
|
||||
page.value = 1
|
||||
void reload()
|
||||
}
|
||||
|
||||
async function reload() {
|
||||
loading.value = true;
|
||||
loading.value = true
|
||||
try {
|
||||
const result = await http.get<ListResult>("/site-domain-mappings", {
|
||||
const result = await http.get<ListResult>('/site-domain-mappings', {
|
||||
keyword: filter.keyword || undefined,
|
||||
is_active: filter.status || undefined,
|
||||
page: page.value,
|
||||
size: size.value,
|
||||
});
|
||||
rows.value = result.items;
|
||||
total.value = result.total;
|
||||
})
|
||||
rows.value = result.items
|
||||
total.value = result.total
|
||||
} catch (error) {
|
||||
handleError(error);
|
||||
handleError(error)
|
||||
} finally {
|
||||
loading.value = false;
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function onTableChange(pag: TablePaginationConfig) {
|
||||
page.value = pag.current ?? 1;
|
||||
size.value = pag.pageSize ?? 20;
|
||||
void reload();
|
||||
page.value = pag.current ?? 1
|
||||
size.value = pag.pageSize ?? 20
|
||||
void reload()
|
||||
}
|
||||
|
||||
const drawerOpen = ref(false);
|
||||
const saving = ref(false);
|
||||
const editingRow = ref<SiteDomainMappingRow | null>(null);
|
||||
const drawerOpen = ref(false)
|
||||
const saving = ref(false)
|
||||
const editingRow = ref<SiteDomainMappingRow | null>(null)
|
||||
const form = reactive({
|
||||
registrable_domain: "",
|
||||
site_key: "",
|
||||
site_name: "",
|
||||
registrable_domain: '',
|
||||
site_key: '',
|
||||
site_name: '',
|
||||
is_active: true,
|
||||
});
|
||||
})
|
||||
|
||||
function resetForm() {
|
||||
form.registrable_domain = "";
|
||||
form.site_key = "";
|
||||
form.site_name = "";
|
||||
form.is_active = true;
|
||||
editingRow.value = null;
|
||||
form.registrable_domain = ''
|
||||
form.site_key = ''
|
||||
form.site_name = ''
|
||||
form.is_active = true
|
||||
editingRow.value = null
|
||||
}
|
||||
|
||||
function openCreate() {
|
||||
resetForm();
|
||||
drawerOpen.value = true;
|
||||
resetForm()
|
||||
drawerOpen.value = true
|
||||
}
|
||||
|
||||
function openEdit(row: SiteDomainMappingRow) {
|
||||
editingRow.value = row;
|
||||
form.registrable_domain = row.registrable_domain;
|
||||
form.site_key = row.site_key;
|
||||
form.site_name = row.site_name;
|
||||
form.is_active = row.is_active;
|
||||
drawerOpen.value = true;
|
||||
editingRow.value = row
|
||||
form.registrable_domain = row.registrable_domain
|
||||
form.site_key = row.site_key
|
||||
form.site_name = row.site_name
|
||||
form.is_active = row.is_active
|
||||
drawerOpen.value = true
|
||||
}
|
||||
|
||||
function closeDrawer() {
|
||||
drawerOpen.value = false;
|
||||
drawerOpen.value = false
|
||||
}
|
||||
|
||||
async function submitForm() {
|
||||
const domain = form.registrable_domain.trim();
|
||||
const siteName = form.site_name.trim();
|
||||
const domain = form.registrable_domain.trim()
|
||||
const siteName = form.site_name.trim()
|
||||
if (!domain) {
|
||||
message.warning("请输入匹配域名");
|
||||
return;
|
||||
message.warning('请输入匹配域名')
|
||||
return
|
||||
}
|
||||
if (!siteName) {
|
||||
message.warning("请输入中文名");
|
||||
return;
|
||||
message.warning('请输入中文名')
|
||||
return
|
||||
}
|
||||
|
||||
saving.value = true;
|
||||
saving.value = true
|
||||
try {
|
||||
const payload = {
|
||||
registrable_domain: domain,
|
||||
site_key: form.site_key.trim(),
|
||||
site_name: siteName,
|
||||
is_active: form.is_active,
|
||||
};
|
||||
if (editingRow.value) {
|
||||
await http.patch(`/site-domain-mappings/${editingRow.value.id}`, payload);
|
||||
message.success("已保存");
|
||||
} else {
|
||||
await http.post("/site-domain-mappings", payload);
|
||||
message.success("已新增");
|
||||
}
|
||||
drawerOpen.value = false;
|
||||
resetForm();
|
||||
void reload();
|
||||
if (editingRow.value) {
|
||||
await http.patch(`/site-domain-mappings/${editingRow.value.id}`, payload)
|
||||
message.success('已保存')
|
||||
} else {
|
||||
await http.post('/site-domain-mappings', payload)
|
||||
message.success('已新增')
|
||||
}
|
||||
drawerOpen.value = false
|
||||
resetForm()
|
||||
void reload()
|
||||
} catch (error) {
|
||||
handleError(error);
|
||||
handleError(error)
|
||||
} finally {
|
||||
saving.value = false;
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function setActive(row: SiteDomainMappingRow, active: boolean) {
|
||||
statusLoadingId.value = row.id;
|
||||
statusLoadingId.value = row.id
|
||||
try {
|
||||
await http.post(`/site-domain-mappings/${row.id}/active`, { is_active: active });
|
||||
message.success(active ? "已启用" : "已停用");
|
||||
row.is_active = active;
|
||||
await http.post(`/site-domain-mappings/${row.id}/active`, { is_active: active })
|
||||
message.success(active ? '已启用' : '已停用')
|
||||
row.is_active = active
|
||||
} catch (error) {
|
||||
handleError(error);
|
||||
handleError(error)
|
||||
} finally {
|
||||
statusLoadingId.value = null;
|
||||
statusLoadingId.value = null
|
||||
}
|
||||
}
|
||||
|
||||
function onActiveSwitchChange(row: SiteDomainMappingRow, checked: unknown) {
|
||||
void setActive(row, Boolean(checked));
|
||||
void setActive(row, Boolean(checked))
|
||||
}
|
||||
|
||||
async function deleteMapping(row: SiteDomainMappingRow) {
|
||||
try {
|
||||
await http.delete(`/site-domain-mappings/${row.id}`);
|
||||
message.success("已删除");
|
||||
void reload();
|
||||
await http.delete(`/site-domain-mappings/${row.id}`)
|
||||
message.success('已删除')
|
||||
void reload()
|
||||
} catch (error) {
|
||||
handleError(error);
|
||||
handleError(error)
|
||||
}
|
||||
}
|
||||
|
||||
function formatDate(value: string | null | undefined): string {
|
||||
return value ? dayjs(value).format("YYYY-MM-DD HH:mm:ss") : "—";
|
||||
return value ? dayjs(value).format('YYYY-MM-DD HH:mm:ss') : '—'
|
||||
}
|
||||
|
||||
function handleError(error: unknown) {
|
||||
if (error instanceof OpsApiError) {
|
||||
message.error(error.detail || error.message);
|
||||
message.error(error.detail || error.message)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
void reload();
|
||||
});
|
||||
void reload()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
Reference in New Issue
Block a user