mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-10-09 11:12:02 +00:00
fix(web): 修复代码格式和文件清理问题
清理无用文件和资源,调整代码格式统一双引号,修复部分组件路径引用问题 删除大量无用图片资源和组件文件,统一代码风格使用双引号,修复国际化文件路径引用,调整部分组件逻辑,优化项目结构
This commit is contained in:
@@ -19,7 +19,7 @@
|
||||
<el-icon :size="14">
|
||||
<Switch />
|
||||
</el-icon>
|
||||
<span>{{ treeExpanded ? '收起' : '展开' }}</span>
|
||||
<span>{{ treeExpanded ? "收起" : "展开" }}</span>
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
@@ -42,136 +42,136 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Switch } from '@element-plus/icons-vue';
|
||||
import DeptAPI, { DeptPageQuery } from '@/api/module_system/dept';
|
||||
import { formatTree } from '@/utils/common';
|
||||
import type { FilterNodeMethodFunction, TreeInstance } from 'element-plus';
|
||||
import { Switch } from "@element-plus/icons-vue";
|
||||
import DeptAPI, { DeptPageQuery } from "@/api/module_system/dept";
|
||||
import { formatTree } from "@/utils/common";
|
||||
import type { FilterNodeMethodFunction, TreeInstance } from "element-plus";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: [String, Number],
|
||||
default: undefined,
|
||||
},
|
||||
});
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: [String, Number],
|
||||
default: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
const deptOptions = ref<OptionType[]>([]); // 部门列表
|
||||
const deptTreeRef = ref<TreeInstance>(); // 部门树
|
||||
const deptName = ref(); // 部门名称
|
||||
/** 与「全部展开」状态同步,用于按钮提示(手动展开单节点后可能不完全一致) */
|
||||
const treeExpanded = ref(true);
|
||||
const deptOptions = ref<OptionType[]>([]); // 部门列表
|
||||
const deptTreeRef = ref<TreeInstance>(); // 部门树
|
||||
const deptName = ref(); // 部门名称
|
||||
/** 与「全部展开」状态同步,用于按钮提示(手动展开单节点后可能不完全一致) */
|
||||
const treeExpanded = ref(true);
|
||||
|
||||
const emits = defineEmits(['node-click', 'update:modelValue']);
|
||||
const emits = defineEmits(["node-click", "update:modelValue"]);
|
||||
|
||||
const deptId = useVModel(props, 'modelValue', emits);
|
||||
const deptId = useVModel(props, "modelValue", emits);
|
||||
|
||||
watch(deptName, (val) => {
|
||||
deptTreeRef.value?.filter(val);
|
||||
});
|
||||
watch(deptName, (val) => {
|
||||
deptTreeRef.value?.filter(val);
|
||||
});
|
||||
|
||||
type TreeStoreNode = {
|
||||
childNodes?: TreeStoreNode[];
|
||||
expand: () => void;
|
||||
collapse: () => void;
|
||||
};
|
||||
type TreeStoreNode = {
|
||||
childNodes?: TreeStoreNode[];
|
||||
expand: () => void;
|
||||
collapse: () => void;
|
||||
};
|
||||
|
||||
function getTreeRoot() {
|
||||
const tree = deptTreeRef.value as TreeInstance & { store?: { root: TreeStoreNode } };
|
||||
return tree?.store?.root;
|
||||
}
|
||||
function getTreeRoot() {
|
||||
const tree = deptTreeRef.value as TreeInstance & { store?: { root: TreeStoreNode } };
|
||||
return tree?.store?.root;
|
||||
}
|
||||
|
||||
/** 展开整棵树 */
|
||||
function expandAllTreeNodes() {
|
||||
const root = getTreeRoot();
|
||||
if (!root) return;
|
||||
const walk = (node: TreeStoreNode) => {
|
||||
node.childNodes?.forEach((child) => {
|
||||
if (child.childNodes?.length) {
|
||||
child.expand();
|
||||
walk(child);
|
||||
}
|
||||
});
|
||||
};
|
||||
walk(root);
|
||||
}
|
||||
|
||||
/** 收起整棵树 */
|
||||
function collapseAllTreeNodes() {
|
||||
const root = getTreeRoot();
|
||||
if (!root) return;
|
||||
const walk = (node: TreeStoreNode) => {
|
||||
node.childNodes?.forEach((child) => {
|
||||
/** 展开整棵树 */
|
||||
function expandAllTreeNodes() {
|
||||
const root = getTreeRoot();
|
||||
if (!root) return;
|
||||
const walk = (node: TreeStoreNode) => {
|
||||
node.childNodes?.forEach((child) => {
|
||||
if (child.childNodes?.length) {
|
||||
child.expand();
|
||||
walk(child);
|
||||
child.collapse();
|
||||
});
|
||||
};
|
||||
walk(root);
|
||||
}
|
||||
|
||||
function toggleTreeExpandAll() {
|
||||
if (treeExpanded.value) {
|
||||
collapseAllTreeNodes();
|
||||
treeExpanded.value = false;
|
||||
} else {
|
||||
expandAllTreeNodes();
|
||||
treeExpanded.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
interface Tree {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* 部门筛选
|
||||
*/
|
||||
const handleFilter: FilterNodeMethodFunction = (value: string, data: Tree) => {
|
||||
if (!value) return true;
|
||||
return data.label.includes(value);
|
||||
}
|
||||
});
|
||||
};
|
||||
walk(root);
|
||||
}
|
||||
|
||||
/** 部门树节点 Click */
|
||||
function handleNodeClick(data: { [key: string]: any }) {
|
||||
deptId.value = data.value;
|
||||
emits('node-click');
|
||||
/** 收起整棵树 */
|
||||
function collapseAllTreeNodes() {
|
||||
const root = getTreeRoot();
|
||||
if (!root) return;
|
||||
const walk = (node: TreeStoreNode) => {
|
||||
node.childNodes?.forEach((child) => {
|
||||
walk(child);
|
||||
child.collapse();
|
||||
});
|
||||
};
|
||||
walk(root);
|
||||
}
|
||||
|
||||
function toggleTreeExpandAll() {
|
||||
if (treeExpanded.value) {
|
||||
collapseAllTreeNodes();
|
||||
treeExpanded.value = false;
|
||||
} else {
|
||||
expandAllTreeNodes();
|
||||
treeExpanded.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
const queryFormData = reactive<DeptPageQuery>({
|
||||
name: undefined,
|
||||
status: undefined,
|
||||
created_time: undefined,
|
||||
});
|
||||
interface Tree {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
const loading = ref(true);
|
||||
/**
|
||||
* 部门筛选
|
||||
*/
|
||||
const handleFilter: FilterNodeMethodFunction = (value: string, data: Tree) => {
|
||||
if (!value) return true;
|
||||
return data.label.includes(value);
|
||||
};
|
||||
|
||||
onBeforeMount(() => {
|
||||
loading.value = true;
|
||||
DeptAPI.listDept(queryFormData)
|
||||
.then((response) => {
|
||||
deptOptions.value = formatTree(response.data.data);
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
});
|
||||
/** 部门树节点 Click */
|
||||
function handleNodeClick(data: { [key: string]: any }) {
|
||||
deptId.value = data.value;
|
||||
emits("node-click");
|
||||
}
|
||||
|
||||
const queryFormData = reactive<DeptPageQuery>({
|
||||
name: undefined,
|
||||
status: undefined,
|
||||
created_time: undefined,
|
||||
});
|
||||
|
||||
const loading = ref(true);
|
||||
|
||||
onBeforeMount(() => {
|
||||
loading.value = true;
|
||||
DeptAPI.listDept(queryFormData)
|
||||
.then((response) => {
|
||||
deptOptions.value = formatTree(response.data.data);
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.dept-tree-toolbar {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
.dept-tree-toolbar {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
|
||||
.dept-tree-search {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.dept-tree-expand-btn {
|
||||
flex-shrink: 0;
|
||||
height: auto;
|
||||
min-height: unset;
|
||||
padding: 0 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.dept-tree-search {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.dept-tree-expand-btn {
|
||||
flex-shrink: 0;
|
||||
height: auto;
|
||||
min-height: unset;
|
||||
padding: 0 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -8,125 +8,125 @@
|
||||
>
|
||||
<template #status="scope">
|
||||
<el-tag :type="scope.row[scope.prop] === '0' ? 'success' : 'danger'">
|
||||
{{ scope.row[scope.prop] === '0' ? '启用' : '停用' }}
|
||||
{{ scope.row[scope.prop] === "0" ? "启用" : "停用" }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</table-select>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { ISelectConfig } from '@/components/TableSelect/index.vue';
|
||||
import UserAPI from '@/api/module_system/user';
|
||||
import type { ISelectConfig } from "@/components/TableSelect/index.vue";
|
||||
import UserAPI from "@/api/module_system/user";
|
||||
|
||||
// 父组件双向绑定的值(选中用户ID)
|
||||
const props = defineProps<{ modelValue?: number }>();
|
||||
// 父组件双向绑定的值(选中用户ID)
|
||||
const props = defineProps<{ modelValue?: number }>();
|
||||
|
||||
// 事件向上派发,支持父组件监听和 v-model(仅回传选中用户ID)
|
||||
const emit = defineEmits<{
|
||||
confirmClick: [data: IUser[]];
|
||||
'update:modelValue': [val: number | undefined];
|
||||
}>();
|
||||
// 事件向上派发,支持父组件监听和 v-model(仅回传选中用户ID)
|
||||
const emit = defineEmits<{
|
||||
confirmClick: [data: IUser[]];
|
||||
"update:modelValue": [val: number | undefined];
|
||||
}>();
|
||||
|
||||
const selectConfig: ISelectConfig = {
|
||||
pk: 'id',
|
||||
width: '167.5px', // 与搜索表单其他输入宽度一致
|
||||
placeholder: '请选择用户',
|
||||
popover: {
|
||||
width: 720, // 弹出层宽度,提升可用性(约 720px)
|
||||
},
|
||||
formItems: [
|
||||
{
|
||||
type: 'select',
|
||||
label: '状态',
|
||||
prop: 'status',
|
||||
initialValue: '0',
|
||||
attrs: {
|
||||
placeholder: '全部',
|
||||
clearable: true,
|
||||
style: {
|
||||
width: '140px',
|
||||
},
|
||||
const selectConfig: ISelectConfig = {
|
||||
pk: "id",
|
||||
width: "167.5px", // 与搜索表单其他输入宽度一致
|
||||
placeholder: "请选择用户",
|
||||
popover: {
|
||||
width: 720, // 弹出层宽度,提升可用性(约 720px)
|
||||
},
|
||||
formItems: [
|
||||
{
|
||||
type: "select",
|
||||
label: "状态",
|
||||
prop: "status",
|
||||
initialValue: "0",
|
||||
attrs: {
|
||||
placeholder: "全部",
|
||||
clearable: true,
|
||||
style: {
|
||||
width: "140px",
|
||||
},
|
||||
options: [
|
||||
{ label: '启用', value: '0' },
|
||||
{ label: '停用', value: '1' },
|
||||
],
|
||||
},
|
||||
],
|
||||
indexAction(params) {
|
||||
// 映射查询参数到后端接口
|
||||
const query: any = { ...params };
|
||||
// 清理空字符串/空值,避免后端 422
|
||||
Object.keys(query).forEach((k) => {
|
||||
const v = query[k];
|
||||
if (v === '' || v === null || v === undefined) {
|
||||
delete query[k];
|
||||
}
|
||||
});
|
||||
// 规范化状态为布尔值
|
||||
if (typeof query.status === 'string') {
|
||||
if (query.status === 'true') query.status = true;
|
||||
else if (query.status === 'false') query.status = false;
|
||||
}
|
||||
// 请求用户分页列表并适配 TableSelect 需要的结构
|
||||
return UserAPI.listUser(query).then((res: any) => {
|
||||
return {
|
||||
total: res.data.data.total,
|
||||
list: res.data.data.items,
|
||||
};
|
||||
});
|
||||
options: [
|
||||
{ label: "启用", value: "0" },
|
||||
{ label: "停用", value: "1" },
|
||||
],
|
||||
},
|
||||
tableColumns: [
|
||||
{ type: 'selection', width: 50, align: 'center' },
|
||||
{ label: '编号', align: 'center', prop: 'id', width: 100 },
|
||||
{ label: '账号', align: 'center', prop: 'username' },
|
||||
{ label: '用户名', align: 'center', prop: 'name', width: 120 },
|
||||
{
|
||||
label: '状态',
|
||||
align: 'center',
|
||||
prop: 'status',
|
||||
templet: 'custom',
|
||||
slotName: 'status',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
interface IUser {
|
||||
id: number;
|
||||
username: string;
|
||||
name: string;
|
||||
mobile?: string;
|
||||
gender?: string;
|
||||
avatar?: string;
|
||||
email?: string | null;
|
||||
status?: string;
|
||||
dept_name?: string;
|
||||
role_names?: string[];
|
||||
created_time?: string;
|
||||
}
|
||||
const selectedUser = ref<IUser>();
|
||||
function handleConfirm(data: IUser[]) {
|
||||
selectedUser.value = data[0];
|
||||
const id = selectedUser.value?.id;
|
||||
emit('update:modelValue', id);
|
||||
emit('confirmClick', data);
|
||||
}
|
||||
function handleClearSelection() {
|
||||
selectedUser.value = undefined;
|
||||
emit('update:modelValue', undefined);
|
||||
}
|
||||
|
||||
// 当父组件重置 v-model(modelValue)为 undefined 时,清空本地显示
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
if (val === undefined || val === null) {
|
||||
selectedUser.value = undefined;
|
||||
],
|
||||
indexAction(params) {
|
||||
// 映射查询参数到后端接口
|
||||
const query: any = { ...params };
|
||||
// 清理空字符串/空值,避免后端 422
|
||||
Object.keys(query).forEach((k) => {
|
||||
const v = query[k];
|
||||
if (v === "" || v === null || v === undefined) {
|
||||
delete query[k];
|
||||
}
|
||||
});
|
||||
// 规范化状态为布尔值
|
||||
if (typeof query.status === "string") {
|
||||
if (query.status === "true") query.status = true;
|
||||
else if (query.status === "false") query.status = false;
|
||||
}
|
||||
);
|
||||
// 请求用户分页列表并适配 TableSelect 需要的结构
|
||||
return UserAPI.listUser(query).then((res: any) => {
|
||||
return {
|
||||
total: res.data.data.total,
|
||||
list: res.data.data.items,
|
||||
};
|
||||
});
|
||||
},
|
||||
tableColumns: [
|
||||
{ type: "selection", width: 50, align: "center" },
|
||||
{ label: "编号", align: "center", prop: "id", width: 100 },
|
||||
{ label: "账号", align: "center", prop: "username" },
|
||||
{ label: "用户名", align: "center", prop: "name", width: 120 },
|
||||
{
|
||||
label: "状态",
|
||||
align: "center",
|
||||
prop: "status",
|
||||
templet: "custom",
|
||||
slotName: "status",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const text = computed(() => {
|
||||
return selectedUser.value ? `${selectedUser.value.username} - ${selectedUser.value.name}` : '';
|
||||
});
|
||||
interface IUser {
|
||||
id: number;
|
||||
username: string;
|
||||
name: string;
|
||||
mobile?: string;
|
||||
gender?: string;
|
||||
avatar?: string;
|
||||
email?: string | null;
|
||||
status?: string;
|
||||
dept_name?: string;
|
||||
role_names?: string[];
|
||||
created_time?: string;
|
||||
}
|
||||
const selectedUser = ref<IUser>();
|
||||
function handleConfirm(data: IUser[]) {
|
||||
selectedUser.value = data[0];
|
||||
const id = selectedUser.value?.id;
|
||||
emit("update:modelValue", id);
|
||||
emit("confirmClick", data);
|
||||
}
|
||||
function handleClearSelection() {
|
||||
selectedUser.value = undefined;
|
||||
emit("update:modelValue", undefined);
|
||||
}
|
||||
|
||||
// 当父组件重置 v-model(modelValue)为 undefined 时,清空本地显示
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
if (val === undefined || val === null) {
|
||||
selectedUser.value = undefined;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const text = computed(() => {
|
||||
return selectedUser.value ? `${selectedUser.value.username} - ${selectedUser.value.name}` : "";
|
||||
});
|
||||
</script>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user