🧹 清理重复配置文件

- 删除根目录中重复的 NestJS 配置文件
- 删除 tsconfig.json, tsconfig.build.json, eslint.config.mjs, .prettierrc
- 保留 wwjcloud-nest/ 目录中的完整配置
- 避免配置冲突,确保项目结构清晰
This commit is contained in:
wanwu
2025-10-14 23:56:20 +08:00
parent 7a160dd04b
commit e7a1d6b4d6
3263 changed files with 356 additions and 112679 deletions

View File

@@ -0,0 +1,557 @@
<template>
<div class="site-group-container">
<Card :bordered="false" class="search-card">
<Form
:model="searchForm"
layout="inline"
@finish="handleSearch"
class="search-form"
>
<FormItem name="keywords">
<Input
v-model:value="searchForm.keywords"
placeholder="请输入分组名称"
allow-clear
/>
</FormItem>
<FormItem>
<Button type="primary" html-type="submit" :loading="loading">
搜索
</Button>
<Button @click="handleReset" style="margin-left: 8px">
重置
</Button>
</FormItem>
</Form>
<div class="action-bar">
<Button type="primary" @click="handleAdd">
添加站点分组
</Button>
</div>
</Card>
<Card :bordered="false" class="table-card">
<Table
:columns="columns"
:data-source="tableData"
:loading="loading"
:pagination="pagination"
@change="handleTableChange"
row-key="group_id"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'app_name'">
<div v-if="record.app_list && record.app_list.length > 0" class="app-list">
<Tooltip
placement="top-start"
:title="getAppTooltipContent(record.app_list)"
>
<div class="app-icons">
<div
v-for="(app, index) in record.app_list.slice(0, 4)"
:key="index"
class="app-icon"
>
<Avatar
:src="app.icon"
:size="54"
shape="square"
/>
</div>
<div
v-if="record.app_list.length > 4"
class="app-more"
>
<span>...</span>
</div>
</div>
</Tooltip>
</div>
<div v-else class="empty-apps">
暂无应用
</div>
</template>
<template v-else-if="column.key === 'addon_name'">
<div v-if="record.addon_list && record.addon_list.length > 0" class="addon-list">
<Tooltip
placement="top-start"
:title="getAddonTooltipContent(record.addon_list)"
>
<div class="addon-icons">
<div
v-for="(addon, index) in record.addon_list.slice(0, 4)"
:key="index"
class="addon-icon"
>
<Avatar
:src="addon.icon"
:size="54"
shape="square"
/>
</div>
<div
v-if="record.addon_list.length > 4"
class="addon-more"
>
<span>...</span>
</div>
</div>
</Tooltip>
</div>
<div v-else class="empty-addons">
暂无插件
</div>
</template>
<template v-else-if="column.key === 'action'">
<Space>
<Button type="link" @click="handleEdit(record)">
编辑
</Button>
<Button type="link" @click="handleDelete(record)" danger>
删除
</Button>
</Space>
</template>
</template>
</Table>
</Card>
<!-- 添加/编辑分组对话框 -->
<Modal
v-model:open="groupModalVisible"
:title="isEdit ? '编辑站点分组' : '添加站点分组'"
@ok="handleSubmit"
:confirm-loading="submitLoading"
width="600px"
>
<Form
ref="groupFormRef"
:model="groupForm"
:rules="groupRules"
layout="vertical"
>
<FormItem label="分组名称" name="group_name" required>
<Input
v-model:value="groupForm.group_name"
placeholder="请输入分组名称"
maxlength="50"
show-count
/>
</FormItem>
<FormItem label="分组描述" name="group_desc">
<TextArea
v-model:value="groupForm.group_desc"
placeholder="请输入分组描述"
:rows="3"
maxlength="255"
show-count
/>
</FormItem>
<FormItem label="应用" name="app">
<Select
v-model:value="groupForm.app"
mode="multiple"
placeholder="请选择应用"
:options="appOptions"
:filter-option="filterAppOption"
show-search
/>
</FormItem>
<FormItem label="插件" name="addon">
<Select
v-model:value="groupForm.addon"
mode="multiple"
placeholder="请选择插件"
:options="addonOptions"
:filter-option="filterAddonOption"
show-search
/>
</FormItem>
</Form>
</Modal>
<!-- 删除确认对话框 -->
<Modal
v-model:open="deleteModalVisible"
title="确认删除"
@ok="confirmDelete"
:confirm-loading="deleteLoading"
>
<p>确定要删除该站点分组吗删除后无法恢复</p>
<p v-if="currentRecord?.site_count > 0" class="text-red-500">
注意该分组下还有 {{ currentRecord.site_count }} 个站点删除分组可能会影响这些站点
</p>
</Modal>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { message } from 'ant-design-vue'
import {
Card,
Form,
FormItem,
Input,
Button,
Table,
Space,
Modal,
Avatar,
Tooltip,
Select,
TextArea
} from 'ant-design-vue'
import { useSiteApi } from '@/api/site'
// API
const siteApi = useSiteApi()
// 响应式数据
const loading = ref(false)
const submitLoading = ref(false)
const deleteLoading = ref(false)
const groupModalVisible = ref(false)
const deleteModalVisible = ref(false)
const isEdit = ref(false)
const currentRecord = ref<any>(null)
const groupFormRef = ref()
// 表单数据
const searchForm = reactive({
keywords: ''
})
const groupForm = reactive({
group_id: '',
group_name: '',
group_desc: '',
app: [],
addon: []
})
// 表格数据
const tableData = ref([])
const pagination = reactive({
current: 1,
pageSize: 10,
total: 0,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total: number) => `${total} 条记录`
})
// 选项数据
const appOptions = ref([])
const addonOptions = ref([])
// 表单验证规则
const groupRules = {
group_name: [
{ required: true, message: '请输入分组名称', trigger: 'blur' },
{ max: 50, message: '分组名称不能超过50个字符', trigger: 'blur' }
],
group_desc: [
{ max: 255, message: '分组描述不能超过255个字符', trigger: 'blur' }
]
}
// 表格列定义
const columns = [
{
title: '分组名称',
dataIndex: 'group_name',
key: 'group_name',
width: 150
},
{
title: '应用',
key: 'app_name',
width: 250
},
{
title: '插件',
key: 'addon_name',
width: 250
},
{
title: '创建时间',
dataIndex: 'create_time',
key: 'create_time',
width: 120
},
{
title: '操作',
key: 'action',
width: 120,
fixed: 'right'
}
]
// 方法
const loadGroupList = async () => {
loading.value = true
try {
const params = {
page: pagination.current,
limit: pagination.pageSize,
...searchForm
}
const response = await siteApi.getSiteGroupList(params)
tableData.value = response.data.list || []
pagination.total = response.data.total || 0
} catch (error) {
message.error('加载分组列表失败')
} finally {
loading.value = false
}
}
const handleSearch = () => {
pagination.current = 1
loadGroupList()
}
const handleReset = () => {
searchForm.keywords = ''
pagination.current = 1
loadGroupList()
}
const handleTableChange = (pag: any) => {
pagination.current = pag.current
pagination.pageSize = pag.pageSize
loadGroupList()
}
const handleAdd = () => {
isEdit.value = false
resetGroupForm()
groupModalVisible.value = true
}
const handleEdit = (record: any) => {
isEdit.value = true
currentRecord.value = record
Object.assign(groupForm, {
group_id: record.group_id,
group_name: record.group_name,
group_desc: record.group_desc,
app: record.app ? record.app.split(',').filter(Boolean) : [],
addon: record.addon ? record.addon.split(',').filter(Boolean) : []
})
groupModalVisible.value = true
}
const handleDelete = (record: any) => {
currentRecord.value = record
deleteModalVisible.value = true
}
const handleSubmit = async () => {
try {
await groupFormRef.value.validate()
submitLoading.value = true
const params = {
...groupForm,
app: groupForm.app.join(','),
addon: groupForm.addon.join(',')
}
if (isEdit.value) {
await siteApi.editSiteGroup(params)
message.success('编辑成功')
} else {
await siteApi.addSiteGroup(params)
message.success('添加成功')
}
groupModalVisible.value = false
loadGroupList()
} catch (error) {
if (error.errorFields) {
message.error('请检查表单填写')
} else {
message.error(isEdit.value ? '编辑失败' : '添加失败')
}
} finally {
submitLoading.value = false
}
}
const confirmDelete = async () => {
deleteLoading.value = true
try {
await siteApi.deleteSiteGroup(currentRecord.value.group_id)
message.success('删除成功')
deleteModalVisible.value = false
loadGroupList()
} catch (error) {
message.error('删除失败')
} finally {
deleteLoading.value = false
}
}
const resetGroupForm = () => {
Object.assign(groupForm, {
group_id: '',
group_name: '',
group_desc: '',
app: [],
addon: []
})
}
const getAppTooltipContent = (appList: any[]) => {
return (
<div class="app-tooltip">
{appList.map((app, index) => (
<div key={index} class="app-tooltip-item">
<img src={app.icon} class="app-tooltip-icon" />
<span>{app.title}</span>
</div>
))}
</div>
)
}
const getAddonTooltipContent = (addonList: any[]) => {
return (
<div class="addon-tooltip">
{addonList.map((addon, index) => (
<div key={index} class="addon-tooltip-item">
<img src={addon.icon} class="addon-tooltip-icon" />
<span>{addon.title}</span>
</div>
))}
</div>
)
}
const filterAppOption = (input: string, option: any) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
const filterAddonOption = (input: string, option: any) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
const loadOptions = async () => {
try {
const [appResponse, addonResponse] = await Promise.all([
siteApi.getShowApp(),
siteApi.getShowMarketing()
])
appOptions.value = (appResponse.data || []).map((item: any) => ({
label: item.title,
value: item.key
}))
addonOptions.value = (addonResponse.data || []).map((item: any) => ({
label: item.title,
value: item.key
}))
} catch (error) {
console.error('加载选项数据失败', error)
}
}
// 初始化
onMounted(async () => {
await Promise.all([
loadGroupList(),
loadOptions()
])
})
</script>
<style scoped>
.site-group-container {
padding: 24px;
}
.search-card {
margin-bottom: 24px;
}
.search-form {
margin-bottom: 16px;
}
.action-bar {
display: flex;
justify-content: flex-end;
}
.table-card {
margin-top: 16px;
}
.app-list,
.addon-list {
display: flex;
align-items: center;
}
.app-icons,
.addon-icons {
display: flex;
align-items: center;
gap: 8px;
overflow: hidden;
}
.app-icon,
.addon-icon {
flex-shrink: 0;
}
.app-more,
.addon-more {
display: flex;
align-items: center;
height: 54px;
color: #999;
font-size: 12px;
}
.empty-apps,
.empty-addons {
color: #999;
font-size: 12px;
}
.app-tooltip,
.addon-tooltip {
display: flex;
flex-wrap: wrap;
gap: 8px;
max-width: 315px;
}
.app-tooltip-item,
.addon-tooltip-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
}
.app-tooltip-icon,
.addon-tooltip-icon {
width: 54px;
height: 54px;
border-radius: 4px;
}
</style>

View File

@@ -0,0 +1,16 @@
<script setup lang="ts">
// 简单的测试页面
</script>
<template>
<div>
<h1>站点管理</h1>
<p>这是一个测试页面</p>
</div>
</template>
<style scoped>
h1 {
color: #1890ff;
}
</style>