新增 菜单界面[待完善]
This commit is contained in:
parent
ac2a6caa1e
commit
78d85e8aac
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { http } from "@/utils/http";
|
||||||
|
import type { Res } from "@/utils/http/types";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取枚举下拉
|
||||||
|
* @param {string} type 枚举类型 type=StatusEnum
|
||||||
|
* @return {object}
|
||||||
|
*/
|
||||||
|
export function getenum(type) {
|
||||||
|
return http.request<Res<any>>("get", `public/enum/${type}`);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @description 获取枚举对象
|
||||||
|
* @param {string} type 枚举类型 type=StatusEnum
|
||||||
|
* @return {object}
|
||||||
|
*/
|
||||||
|
export function getenumDic(type) {
|
||||||
|
return http.request<Res<any>>("get", `public/enum/${type}/Dic`);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { http } from "@/utils/http";
|
||||||
|
import type { Res } from "@/utils/http/types";
|
||||||
|
|
||||||
|
export class hTableAPI {
|
||||||
|
url = "";
|
||||||
|
/** 构造函数 */
|
||||||
|
constructor(url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
PageList(data = {}) {
|
||||||
|
return http.request<Res<any>>("post", `${this.url}/PageList`, { data });
|
||||||
|
}
|
||||||
|
Info(id, tag = {}) {
|
||||||
|
const pUrl = `${this.url}/${id}`;
|
||||||
|
let getUrl = pUrl;
|
||||||
|
for (const key in tag) {
|
||||||
|
const el = tag[key];
|
||||||
|
getUrl += (getUrl === pUrl ? "?" : "&") + key + "=" + el;
|
||||||
|
}
|
||||||
|
return http.request<Res<any>>("get", getUrl);
|
||||||
|
}
|
||||||
|
edit(data) {
|
||||||
|
return http.request<Res<any>>("post", `${this.url}/Edit`, { data });
|
||||||
|
}
|
||||||
|
delete(data) {
|
||||||
|
return http.request<Res<any>>("post", `${this.url}/Del`, { data });
|
||||||
|
}
|
||||||
|
querycombo(data) {
|
||||||
|
return http.request<Res<any>>("post", `${this.url}/QueryCombo`, { data });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { http } from "@/utils/http";
|
||||||
|
import type { Res } from "@/utils/http/types";
|
||||||
|
|
||||||
|
// 定义菜单项接口
|
||||||
|
export interface MenuItem {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
path?: string;
|
||||||
|
isButton: boolean;
|
||||||
|
title: string;
|
||||||
|
icon?: string;
|
||||||
|
auths?: string;
|
||||||
|
rank: number;
|
||||||
|
showLink: boolean;
|
||||||
|
parentId: number;
|
||||||
|
children?: MenuItem[];
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @description 获取所有的菜单
|
||||||
|
* @return {object}
|
||||||
|
*/
|
||||||
|
export function MenuAll() {
|
||||||
|
return http.request<Res<MenuItem[]>>("get", `Menu/All`);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,182 @@
|
||||||
|
export interface Dialog {
|
||||||
|
/* 对话框是否可见 */
|
||||||
|
visible: boolean;
|
||||||
|
/* 是否显示关闭按钮 */
|
||||||
|
close: boolean;
|
||||||
|
/* 对话框标题 */
|
||||||
|
title: string;
|
||||||
|
/* 对话框宽度 */
|
||||||
|
width: string;
|
||||||
|
/**自定义弹窗数据 */
|
||||||
|
custom: {
|
||||||
|
/* 自定义对话框高度 */
|
||||||
|
height: string;
|
||||||
|
/* 自定义对话框数据 */
|
||||||
|
data: any[];
|
||||||
|
/* 自定义组件路径 */
|
||||||
|
src?: string;
|
||||||
|
/* 自定义配置项 */
|
||||||
|
custom: Record<string, any>;
|
||||||
|
/* 自定义对话框是否可见 */
|
||||||
|
visible: boolean;
|
||||||
|
/* 异步加载组件 */
|
||||||
|
component: any;
|
||||||
|
};
|
||||||
|
edit: {
|
||||||
|
/* 编辑项ID */
|
||||||
|
id: number;
|
||||||
|
/* 编辑对话框标题 */
|
||||||
|
title: string;
|
||||||
|
/* 编辑对话框是否可见 */
|
||||||
|
visible: boolean;
|
||||||
|
row?: any;
|
||||||
|
tagData?: any;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按钮自定义配置 */
|
||||||
|
export interface ButtonCustomConfig {
|
||||||
|
/* 弹出框标题 */
|
||||||
|
title: string;
|
||||||
|
/* 组件路径 */
|
||||||
|
src: string;
|
||||||
|
/* 弹框宽度 */
|
||||||
|
width: string;
|
||||||
|
/* 弹框高度 */
|
||||||
|
height: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 操作按钮配置 */
|
||||||
|
export interface OperationButton {
|
||||||
|
/* 是否为头部按钮 */
|
||||||
|
topBtn: boolean;
|
||||||
|
/* 是否显示 */
|
||||||
|
show?: boolean;
|
||||||
|
/* 按钮文本 */
|
||||||
|
label: string;
|
||||||
|
/* 按钮类型 */
|
||||||
|
btnType: "add" | "edit" | "del" | "custom";
|
||||||
|
/* 按钮样式 */
|
||||||
|
btnStyle?: "success" | "danger";
|
||||||
|
/* 自定义按钮配置 */
|
||||||
|
custom?: ButtonCustomConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 字段设置项 */
|
||||||
|
export interface FieldSetting {
|
||||||
|
/**map 时Value的取值的属性 */
|
||||||
|
mapValue?: string;
|
||||||
|
/**map 时label的取值的属性 */
|
||||||
|
maplabel?: string;
|
||||||
|
/** 数据源 请求方式 */
|
||||||
|
datasourceStr?: string;
|
||||||
|
/**
|
||||||
|
* 图片地址 获取方式
|
||||||
|
* @param value 当前值
|
||||||
|
* @param row 当前行值
|
||||||
|
* @returns 预期返回有效图片地址url
|
||||||
|
*/
|
||||||
|
imgUrl?: (value: any, row: any) => string;
|
||||||
|
/* 数据源 */
|
||||||
|
datasource?: Array<{
|
||||||
|
Value: any;
|
||||||
|
Text: string;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
///* 表格列配置 */
|
||||||
|
//export interface TableEditColumn {}
|
||||||
|
|
||||||
|
/* 表格列配置 */
|
||||||
|
export interface TableColumn {
|
||||||
|
/* 显示标签 */
|
||||||
|
label: string;
|
||||||
|
/* 是否可搜索 */
|
||||||
|
search: boolean;
|
||||||
|
/* 搜索类型 */
|
||||||
|
searchType?:
|
||||||
|
| "Equal"
|
||||||
|
| "NoEqual"
|
||||||
|
| "Like"
|
||||||
|
| "GreaterThan"
|
||||||
|
| "LessThan"
|
||||||
|
| "NoLike";
|
||||||
|
/* 是否允许添加 */
|
||||||
|
add: boolean;
|
||||||
|
/* 是否允许修改 */
|
||||||
|
edit: boolean;
|
||||||
|
/* 列宽度 */
|
||||||
|
width?: string;
|
||||||
|
/* 字段类型 */
|
||||||
|
type?: string;
|
||||||
|
/** 是否多选 */
|
||||||
|
multiple?: boolean;
|
||||||
|
/** 编辑时显示列 */
|
||||||
|
editShow?: boolean;
|
||||||
|
rules?: any | Array<any>;
|
||||||
|
/** 显示列 */
|
||||||
|
show?: boolean;
|
||||||
|
/* 字段设置 */
|
||||||
|
setting?: FieldSetting;
|
||||||
|
/* 修改时的编辑值 */
|
||||||
|
valueE?: Array<string> | string;
|
||||||
|
/* 查询值 */
|
||||||
|
value?: Array<string> | string;
|
||||||
|
/** textarea编辑时的行数 */
|
||||||
|
editRows?: number;
|
||||||
|
/**编辑时值发生变化 */
|
||||||
|
change?: () => void;
|
||||||
|
/**列值初始化时 如何获取默认取对应列*/
|
||||||
|
custom?: (row: any) => string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 分页数据 */
|
||||||
|
export interface PageData {
|
||||||
|
/* 总条数 */
|
||||||
|
total: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 搜索条件 */
|
||||||
|
export interface SearchConditions {
|
||||||
|
/* 是否显示搜索 */
|
||||||
|
show: boolean;
|
||||||
|
/* 当前页码 */
|
||||||
|
PageIndex: number;
|
||||||
|
/* 每页大小 */
|
||||||
|
PageSize: number;
|
||||||
|
/* 排序字段 */
|
||||||
|
OrderBy: string;
|
||||||
|
/* 默认查询条件 */
|
||||||
|
defaultConditions: any[];
|
||||||
|
/* 查询条件 */
|
||||||
|
Conditions: any[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表格配置 */
|
||||||
|
export interface TableConfig {
|
||||||
|
/* 搜索回调函数 */
|
||||||
|
searchCallback?: (s: SearchConditions) => void;
|
||||||
|
/* 新增/修改回调函数 */
|
||||||
|
editCallback?: (from: any) => void;
|
||||||
|
/* API地址 */
|
||||||
|
apiUrl: string;
|
||||||
|
/* 是否显示选择列 */
|
||||||
|
selectColumn: boolean;
|
||||||
|
/* 搜索配置 */
|
||||||
|
search: SearchConditions;
|
||||||
|
/* 是否显示操作列 */
|
||||||
|
operationColumn: boolean;
|
||||||
|
/* 操作按钮配置 */
|
||||||
|
operationColumnData: OperationButton[];
|
||||||
|
/* 列配置 */
|
||||||
|
column: Record<string, TableColumn>;
|
||||||
|
/* 表格数据 */
|
||||||
|
data: any[];
|
||||||
|
/**显示头部操作按钮 */
|
||||||
|
operationTop?: boolean;
|
||||||
|
/* 分页数据 */
|
||||||
|
pageData: PageData;
|
||||||
|
/* 选中行 */
|
||||||
|
selectRows: any[];
|
||||||
|
/* 是否显示边框 */
|
||||||
|
border: boolean;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,218 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, PropType, ref } from "vue";
|
||||||
|
import { hTableAPI } from "@/api/hTable";
|
||||||
|
import { TableConfig, TableColumn } from "./hTable";
|
||||||
|
import { FormInstance } from "element-plus";
|
||||||
|
import { ElMessage, ElMessageBox } from "element-plus";
|
||||||
|
const props = defineProps({
|
||||||
|
//** 传入的表单数据 */
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
default: -1
|
||||||
|
},
|
||||||
|
tableData: {
|
||||||
|
type: Object as PropType<TableConfig>,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
row: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
tagData: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const emit = defineEmits(["handlePagedCallback"]);
|
||||||
|
const editFormRef = ref<FormInstance>();
|
||||||
|
const column: Record<string, TableColumn> = {};
|
||||||
|
const editData = ref({
|
||||||
|
frorm: {},
|
||||||
|
isedit: props.id !== -1,
|
||||||
|
table: props.tableData,
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: "不能为空",
|
||||||
|
trigger: "blur"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
formLabelWidth: "120px",
|
||||||
|
size: "small",
|
||||||
|
loading: false
|
||||||
|
});
|
||||||
|
const Api = new hTableAPI(editData.value.table.apiUrl);
|
||||||
|
onMounted(() => {
|
||||||
|
intiColumn();
|
||||||
|
fetchInitData();
|
||||||
|
fetchFormData();
|
||||||
|
});
|
||||||
|
|
||||||
|
function execute(obj, btn) {
|
||||||
|
return eval(obj);
|
||||||
|
}
|
||||||
|
function intiColumn() {
|
||||||
|
for (const key in editData.value.table.column) {
|
||||||
|
const element = editData.value.table.column[key];
|
||||||
|
if (editData.value.isedit) {
|
||||||
|
if (element.edit) {
|
||||||
|
column.value[key] = element;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (element.add) {
|
||||||
|
column.value[key] = element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function handlePagedCallback() {
|
||||||
|
emit("handlePagedCallback"); // 传参给父组件
|
||||||
|
}
|
||||||
|
function handleSubmitForm() {
|
||||||
|
editFormRef.value.validate(valid => {
|
||||||
|
if (!valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
editData.value.loading = true;
|
||||||
|
let form = {};
|
||||||
|
if (editData.value.isedit) {
|
||||||
|
form = props.row;
|
||||||
|
}
|
||||||
|
for (const key in column.value) {
|
||||||
|
const element = column.value[key];
|
||||||
|
if (element.valueE !== null && element.valueE !== "") {
|
||||||
|
form[key] = element.valueE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (editData.value.table.editCallback) {
|
||||||
|
editData.value.table.editCallback(form);
|
||||||
|
}
|
||||||
|
Api.edit(form).then(res => {
|
||||||
|
editData.value.loading = false;
|
||||||
|
if (res.code === 200) {
|
||||||
|
ElMessage.success("操作成功");
|
||||||
|
setTimeout(handlePagedCallback, 0);
|
||||||
|
} else {
|
||||||
|
ElMessage.error(res.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function handleResetForm() {
|
||||||
|
for (const key in column.value) {
|
||||||
|
let item = column.value[key];
|
||||||
|
if (Array.isArray(item.valueE)) {
|
||||||
|
item.valueE = [];
|
||||||
|
} else if (typeof item.valueE === "number") {
|
||||||
|
item.valueE = 0;
|
||||||
|
} else if (typeof item.valueE === "boolean") {
|
||||||
|
item.valueE = false;
|
||||||
|
} else {
|
||||||
|
item.valueE = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function fetchInitData() {}
|
||||||
|
function fetchFormData() {
|
||||||
|
editData.value.loading = false;
|
||||||
|
handleResetForm();
|
||||||
|
if (editData.value.isedit) {
|
||||||
|
Api.Info(props.id).then(res => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
editData.value.frorm = res.data;
|
||||||
|
for (const key in column.value) {
|
||||||
|
const element = column.value[key];
|
||||||
|
element.valueE = res.data[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
editData.value.loading = true;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
editData.value.loading = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-form
|
||||||
|
v-if="editData.loading"
|
||||||
|
ref="editFormRef"
|
||||||
|
:model="editData.table.column"
|
||||||
|
:label-width="editData.formLabelWidth"
|
||||||
|
size="small"
|
||||||
|
clearable
|
||||||
|
>
|
||||||
|
<el-form-item
|
||||||
|
v-for="(o, k, i) in column"
|
||||||
|
v-show="execute(o['editShow'], o)"
|
||||||
|
:key="i"
|
||||||
|
:rules="o.rules"
|
||||||
|
:prop="'' + k + '.valueE'"
|
||||||
|
:label="o.label"
|
||||||
|
>
|
||||||
|
<div v-if="o.type.trim() == 'datetime'">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="o.valueE"
|
||||||
|
format="yyyy-MM-dd HH:mm:ss"
|
||||||
|
value-format="yyyy-MM-dd HH:mm:ss"
|
||||||
|
type="datetime"
|
||||||
|
:placeholder="o.label"
|
||||||
|
style="width: 100%"
|
||||||
|
@change="o.change"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="o.type.trim() == 'dropdown'">
|
||||||
|
<el-select
|
||||||
|
v-model="o.valueE"
|
||||||
|
:multiple="o.multiple"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
:placeholder="o.label"
|
||||||
|
style="width: 100%"
|
||||||
|
@change="o.change"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in o.setting.datasource"
|
||||||
|
:key="item.Value"
|
||||||
|
autocomplete="off"
|
||||||
|
:label="item[o.setting.maplabel]"
|
||||||
|
:value="item[o.setting.mapValue]"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="o.type.trim() == 'textarea'">
|
||||||
|
<el-input
|
||||||
|
v-model="o.valueE as string"
|
||||||
|
:rows="o.editRows || 4"
|
||||||
|
type="textarea"
|
||||||
|
:placeholder="o.label"
|
||||||
|
@change="o.change"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="o.type.trim() == 'switch'">
|
||||||
|
<el-switch
|
||||||
|
v-model="o.valueE as string"
|
||||||
|
active-text="启用"
|
||||||
|
inactive-text="禁用"
|
||||||
|
@change="o.change"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<el-input v-model="o.valueE as string" :placeholder="o.label" />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
:loading="!editData.loading"
|
||||||
|
@click="handleSubmitForm()"
|
||||||
|
>立即提交</el-button
|
||||||
|
>
|
||||||
|
<el-button @click="handleResetForm()">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,597 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
ref,
|
||||||
|
unref,
|
||||||
|
watch,
|
||||||
|
reactive,
|
||||||
|
computed,
|
||||||
|
onMounted,
|
||||||
|
nextTick,
|
||||||
|
onUnmounted,
|
||||||
|
getCurrentInstance,
|
||||||
|
onBeforeMount,
|
||||||
|
PropType
|
||||||
|
} from "vue";
|
||||||
|
|
||||||
|
import { Search } from "@element-plus/icons-vue";
|
||||||
|
import { ElMessage, ElMessageBox } from "element-plus";
|
||||||
|
import { defineAsyncComponent, AsyncComponentLoader } from "vue";
|
||||||
|
import { Dialog, TableConfig } from "./hTable";
|
||||||
|
import hTableEdit from "./hTableEdit.vue";
|
||||||
|
import { hTableAPI } from "@/api/hTable";
|
||||||
|
import { getenum } from "@/api/enum";
|
||||||
|
const props = defineProps({
|
||||||
|
//** 传入的表单数据 */
|
||||||
|
Row: {
|
||||||
|
type: Object as PropType<any>,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
tableConfig: {
|
||||||
|
type: Object as PropType<TableConfig>,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const table = ref<TableConfig>(props.tableConfig);
|
||||||
|
onBeforeMount(() => {
|
||||||
|
/* 初始化系统配置 */
|
||||||
|
nextTick(async () => {
|
||||||
|
intdata();
|
||||||
|
Api = new hTableAPI(table.value.apiUrl);
|
||||||
|
init.value = true;
|
||||||
|
tableShow.value = true;
|
||||||
|
await fetchInitData();
|
||||||
|
fetchPagedData();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
onMounted(() => {
|
||||||
|
nextTick(async () => {
|
||||||
|
// appStyle();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
onUnmounted(() => {});
|
||||||
|
// defineExpose({
|
||||||
|
// initTable
|
||||||
|
// });
|
||||||
|
const tableShow = ref(false);
|
||||||
|
let Api: hTableAPI = null;
|
||||||
|
const instance = getCurrentInstance();
|
||||||
|
|
||||||
|
const init = ref(false);
|
||||||
|
const tableHeight = ref(0);
|
||||||
|
|
||||||
|
const dialog = ref<Dialog>({
|
||||||
|
visible: false,
|
||||||
|
close: true,
|
||||||
|
title: "",
|
||||||
|
width: "500px", // 弹出层宽度
|
||||||
|
custom: {
|
||||||
|
height: "85vh",
|
||||||
|
data: [],
|
||||||
|
custom: {},
|
||||||
|
component: null, //自定义弹窗的路径
|
||||||
|
visible: false // 弹出层是否显示
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
id: -1,
|
||||||
|
title: "", // 弹出层title
|
||||||
|
visible: false // 弹出层是否显示
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const appB = ref<HTMLElement>(null);
|
||||||
|
const appB_S = ref<HTMLElement>(null);
|
||||||
|
function appStyle() {
|
||||||
|
if (tableHeight.value !== 0) return;
|
||||||
|
tableHeight.value =
|
||||||
|
appB.value.parentElement.parentElement.offsetHeight -
|
||||||
|
145 -
|
||||||
|
appB_S.value.offsetHeight;
|
||||||
|
return tableHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
function intdata() {
|
||||||
|
if (!table.value.data) table.value.data = [];
|
||||||
|
if (!table.value.selectRows) table.value.selectRows = [];
|
||||||
|
if (table.value.border == null) table.value.border = true;
|
||||||
|
if (!table.value.pageData) table.value.pageData = { total: 0 };
|
||||||
|
if (table.value.operationTop === undefined) table.value.operationTop = true;
|
||||||
|
|
||||||
|
// 处理 column 的属性
|
||||||
|
for (const key in table.value.column) {
|
||||||
|
const element = table.value.column[key];
|
||||||
|
|
||||||
|
// Vue 3 中直接赋值即可,不需要 $set
|
||||||
|
if (element.valueE === undefined)
|
||||||
|
element.valueE = element.multiple ? [] : "";
|
||||||
|
if (element.value === undefined) element.value = element.multiple ? [] : "";
|
||||||
|
if (!element.type) element.type = "string";
|
||||||
|
if (element.show === undefined) element.show = true;
|
||||||
|
if (element.editShow === undefined) element.editShow = true;
|
||||||
|
if (!element.setting)
|
||||||
|
element.setting = { datasource: [], mapValue: "Value", maplabel: "Text" };
|
||||||
|
if (!element.setting.mapValue) element.setting.mapValue = "Value";
|
||||||
|
if (!element.setting.maplabel) element.setting.maplabel = "Text";
|
||||||
|
|
||||||
|
if (!element.change) element.change = () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理 operationColumnData 的属性
|
||||||
|
for (const key in table.value.operationColumnData) {
|
||||||
|
const element = table.value.operationColumnData[key];
|
||||||
|
if (element.show === undefined) element.show = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function rowKeyFun(row) {
|
||||||
|
return row.customId;
|
||||||
|
}
|
||||||
|
function execute(obj, scope, btn) {
|
||||||
|
if (Object.prototype.toString.call(obj) === "[object Function]")
|
||||||
|
return obj(scope, btn);
|
||||||
|
return eval(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOperationColumnWidth() {
|
||||||
|
let hFontSize = getComputedStyle(window.document.documentElement)[
|
||||||
|
"font-size"
|
||||||
|
].replace("px", "");
|
||||||
|
let defWidth = 10 + 2 + 30;
|
||||||
|
|
||||||
|
let width = eval(
|
||||||
|
table.value.operationColumnData
|
||||||
|
.filter(s => !s.topBtn && s.show)
|
||||||
|
.map(s => defWidth + s.label.length * (hFontSize || 16))
|
||||||
|
.join("+")
|
||||||
|
);
|
||||||
|
width = width < 100 ? 100 : width;
|
||||||
|
width = width > 320 ? 320 : width;
|
||||||
|
return width + "px";
|
||||||
|
}
|
||||||
|
function getbtnClick(obj, row) {
|
||||||
|
row = row ? [row] : table.value.selectRows;
|
||||||
|
if (obj.btnType === "add") {
|
||||||
|
return handleAdd();
|
||||||
|
} else if (obj.btnType === "edit") {
|
||||||
|
return handleEdit(obj, row);
|
||||||
|
} else if (obj.btnType === "del") {
|
||||||
|
return handleDelete(obj, row);
|
||||||
|
} else if (obj.btnType === "custom") {
|
||||||
|
return handleCustom(obj, row, obj.custom);
|
||||||
|
} else {
|
||||||
|
return obj.click(obj, row, handleReloadPaged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function handleAdd() {
|
||||||
|
dialog.value.edit.id = -1;
|
||||||
|
dialog.value.title = "添加";
|
||||||
|
dialog.value.custom.visible = false;
|
||||||
|
dialog.value.edit.visible = true;
|
||||||
|
dialog.value.visible = true;
|
||||||
|
dialog.value.width = "500px";
|
||||||
|
}
|
||||||
|
function handleEdit(obj, row) {
|
||||||
|
dialog.value.edit.id = row[0].Id;
|
||||||
|
dialog.value.edit.row = row[0];
|
||||||
|
dialog.value.edit.tagData = obj.tagData;
|
||||||
|
dialog.value.title = obj.label || "修改";
|
||||||
|
dialog.value.edit.visible = true;
|
||||||
|
dialog.value.custom.visible = false;
|
||||||
|
dialog.value.visible = true;
|
||||||
|
dialog.value.width = "500px";
|
||||||
|
}
|
||||||
|
function handleCustom(obj, row, custom) {
|
||||||
|
dialog.value.custom.data = row || [];
|
||||||
|
dialog.value.custom.custom = custom || [];
|
||||||
|
// 异步加载组件
|
||||||
|
dialog.value.custom.component = defineAsyncComponent({
|
||||||
|
loader: () => import(/* @vite-ignore */ `../${custom.src}.vue`)
|
||||||
|
});
|
||||||
|
dialog.value.width = custom.width;
|
||||||
|
dialog.value.title = custom.title;
|
||||||
|
dialog.value.edit.visible = false;
|
||||||
|
dialog.value.custom.visible = true;
|
||||||
|
if (custom.height !== undefined) {
|
||||||
|
dialog.value.custom.height = custom.height;
|
||||||
|
} else {
|
||||||
|
dialog.value.custom.height = "600px";
|
||||||
|
}
|
||||||
|
dialog.value.visible = true;
|
||||||
|
}
|
||||||
|
// 删除
|
||||||
|
function handleDelete(obj, row) {
|
||||||
|
if (row.length === 0) {
|
||||||
|
ElMessage.warning("未勾选记录");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ids: any[] = [];
|
||||||
|
row.forEach(it => {
|
||||||
|
ids.push(it.Id);
|
||||||
|
});
|
||||||
|
ElMessageBox.confirm("此操作将永久删除勾选记录, 是否继续?").then(() => {
|
||||||
|
Api.delete(ids).then(res => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
handleReloadPaged();
|
||||||
|
ElMessage.success("删除成功");
|
||||||
|
} else {
|
||||||
|
ElMessage.error(res.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 页面完成回调
|
||||||
|
function handleAddCallback() {
|
||||||
|
Api.url = table.value.apiUrl;
|
||||||
|
dialog.value.visible = false;
|
||||||
|
dialog.value.edit.visible = false;
|
||||||
|
dialog.value.custom.visible = false;
|
||||||
|
handleReloadPaged();
|
||||||
|
}
|
||||||
|
function tableClose() {
|
||||||
|
handleAddCallback();
|
||||||
|
}
|
||||||
|
function pageSizeChange(o) {
|
||||||
|
table.value.search.PageSize = o;
|
||||||
|
fetchPagedData();
|
||||||
|
}
|
||||||
|
function pageIndexChange(o) {
|
||||||
|
table.value.search.PageIndex = o - 1;
|
||||||
|
fetchPagedData();
|
||||||
|
}
|
||||||
|
// 勾选
|
||||||
|
function handleSelectionChange(selection) {
|
||||||
|
console.log("selection-change", selection);
|
||||||
|
|
||||||
|
table.value.selectRows = selection;
|
||||||
|
}
|
||||||
|
// 查询
|
||||||
|
function handleReloadPaged(reload = true) {
|
||||||
|
if (
|
||||||
|
table.value.search === undefined ||
|
||||||
|
table.value.search.PageIndex === undefined
|
||||||
|
) {
|
||||||
|
table.value.search.PageIndex = 0;
|
||||||
|
table.value.search.PageSize = 20;
|
||||||
|
}
|
||||||
|
table.value.search.Conditions = [];
|
||||||
|
for (let name in table.value.column) {
|
||||||
|
if (
|
||||||
|
!table.value.column[name].search ||
|
||||||
|
table.value.column[name].value === undefined ||
|
||||||
|
table.value.column[name].value === null ||
|
||||||
|
table.value.column[name].value === ""
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let data: any = { ConditionalType: 0 };
|
||||||
|
if (table.value.column[name].type === "datetime") {
|
||||||
|
// data.CSharpTypeName = 'DateTime'
|
||||||
|
data.ConditionalType = 8; // '2023-10-07%'
|
||||||
|
} else if (table.value.column[name].type === "switch") {
|
||||||
|
data.CSharpTypeName = "Boolean";
|
||||||
|
} else if (table.value.column[name].type === "string") {
|
||||||
|
data.ConditionalType = "Like";
|
||||||
|
}
|
||||||
|
data.FieldName = name.charAt(0).toUpperCase() + name.slice(1);
|
||||||
|
data.FieldValue = table.value.column[name].value;
|
||||||
|
|
||||||
|
if (table.value.column[name].searchType != undefined) {
|
||||||
|
data.ConditionalType = table.value.column[name].searchType || 0;
|
||||||
|
}
|
||||||
|
table.value.search.Conditions.push(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!reload && table.value.search.Conditions.length > 0) {
|
||||||
|
table.value.search.PageIndex = 0;
|
||||||
|
}
|
||||||
|
if (table.value.searchCallback) {
|
||||||
|
table.value.searchCallback(table.value.search);
|
||||||
|
}
|
||||||
|
fetchPagedData();
|
||||||
|
}
|
||||||
|
// 加载前置数据(如查询条件的下拉选择数据)
|
||||||
|
async function fetchInitData() {
|
||||||
|
for (const key in table.value.column) {
|
||||||
|
const element = table.value.column[key];
|
||||||
|
if (element.type === "dropdown") {
|
||||||
|
if (!element.setting.datasource) {
|
||||||
|
// 存在值就不处理
|
||||||
|
|
||||||
|
let rdata = await eval(element.setting.datasourceStr);
|
||||||
|
element.setting.datasource = rdata.data;
|
||||||
|
console.log(key + " " + element.setting.datasourceStr, rdata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
element.custom == undefined &&
|
||||||
|
(element.type === "switch" ||
|
||||||
|
element.type === "dropdown" ||
|
||||||
|
element.type === "string" ||
|
||||||
|
element.type === undefined)
|
||||||
|
) {
|
||||||
|
element.custom = row => {
|
||||||
|
let sc = element.setting.datasource.find(
|
||||||
|
s => s[element.setting.mapValue] + "" == row[key] + ""
|
||||||
|
);
|
||||||
|
return !sc ? row[key] : sc[element.setting.maplabel];
|
||||||
|
};
|
||||||
|
} else if (element.custom == undefined) {
|
||||||
|
element.custom = row => row[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
init.value = true;
|
||||||
|
appStyle();
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
function showTips(item, value) {
|
||||||
|
if (item.width == undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
getByteLen(item.custom(value) + "") * 16 < item.width.replace("px", "")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
function getByteLen(str) {
|
||||||
|
let len = 0;
|
||||||
|
for (let i = 0; i < str.length; i++) {
|
||||||
|
str.charCodeAt(i) < 256 ? (len += 1) : (len += 2);
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
// 加载分页数据
|
||||||
|
function fetchPagedData() {
|
||||||
|
for (const iterator of table.value.search.defaultConditions) {
|
||||||
|
if (!iterator) continue;
|
||||||
|
if (!table.value.search.Conditions.find(s => s == iterator)) {
|
||||||
|
table.value.search.Conditions.push(iterator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Api.PageList(table.value.search).then(res => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
table.value.data = res.data.data.map((s, i) => {
|
||||||
|
return { ...s, customId: i };
|
||||||
|
});
|
||||||
|
table.value.pageData = res.data;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="tableShow" ref="appB" class="app-container container-h">
|
||||||
|
<div ref="appB_S" class="search-container1">
|
||||||
|
<!-- 搜索项目 -->
|
||||||
|
<el-form v-if="table.search.show" :inline="true" :model="table.search">
|
||||||
|
<el-form-item
|
||||||
|
v-for="(o, n, i) in table.column"
|
||||||
|
v-show="o.search"
|
||||||
|
:key="i"
|
||||||
|
>
|
||||||
|
<el-date-picker
|
||||||
|
v-if="o.type.trim() == 'datetime'"
|
||||||
|
v-model="o.value"
|
||||||
|
format="yyyy-MM-dd"
|
||||||
|
value-format="yyyy-MM-dd"
|
||||||
|
type="date"
|
||||||
|
:placeholder="o.label"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
<el-select
|
||||||
|
v-else-if="o.type.trim() == 'dropdown'"
|
||||||
|
v-model="o.value"
|
||||||
|
:multiple="o.multiple"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
:placeholder="o.label"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in o.setting.datasource"
|
||||||
|
:key="item.Value"
|
||||||
|
autocomplete="off"
|
||||||
|
:label="item[o.setting.maplabel]"
|
||||||
|
:value="item[o.setting.mapValue]"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
<el-select
|
||||||
|
v-else-if="o.type.trim() == 'switch'"
|
||||||
|
v-model="o.value"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
:placeholder="o.label"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-option autocomplete="off" :label="'启用'" :value="true" />
|
||||||
|
<el-option autocomplete="off" :label="'禁用'" :value="false" />
|
||||||
|
</el-select>
|
||||||
|
<div v-else-if="o.type.trim() == 'img'" v-show="false" />
|
||||||
|
<el-input v-else v-model="o.value as string" :placeholder="o.label" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
:icon="Search"
|
||||||
|
@click="handleReloadPaged(false)"
|
||||||
|
>查询</el-button
|
||||||
|
>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="table.operationTop" class="toolbar-container">
|
||||||
|
<!-- 头部按钮组 -->
|
||||||
|
<el-button
|
||||||
|
v-for="(e, i) in table.operationColumnData.filter(s => s.topBtn)"
|
||||||
|
v-show="execute(e['show'], {}, e)"
|
||||||
|
:key="i"
|
||||||
|
:type="e.btnStyle || 'info'"
|
||||||
|
@click="getbtnClick(e, null)"
|
||||||
|
>{{ e.label }}</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table
|
||||||
|
v-if="init"
|
||||||
|
fit
|
||||||
|
:height="tableHeight"
|
||||||
|
:data="table.data"
|
||||||
|
:border="table.border"
|
||||||
|
:highlight-current-row="true"
|
||||||
|
style="width: 100%"
|
||||||
|
:row-key="rowKeyFun"
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
>
|
||||||
|
<el-table-column v-if="table.selectColumn" type="selection" width="40" />
|
||||||
|
<el-table-column
|
||||||
|
v-if="
|
||||||
|
table.operationColumn &&
|
||||||
|
table.operationColumnData.filter(s => !s.topBtn).length > 0
|
||||||
|
"
|
||||||
|
label="操作"
|
||||||
|
:width="getOperationColumnWidth()"
|
||||||
|
>
|
||||||
|
<template v-slot="scope">
|
||||||
|
<div class="columnTemplate">
|
||||||
|
<el-button
|
||||||
|
v-for="(e, i) in table.operationColumnData.filter(s => !s.topBtn)"
|
||||||
|
v-show="execute(e['show'], scope, e)"
|
||||||
|
:key="i"
|
||||||
|
class="btn"
|
||||||
|
text
|
||||||
|
:type="e.btnStyle || 'primary'"
|
||||||
|
@click="getbtnClick(e, scope.row)"
|
||||||
|
>
|
||||||
|
{{ e.label }}</el-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<!-- 行内按钮组 -->
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
v-for="(item, name, i) of table.column"
|
||||||
|
v-show="item.show"
|
||||||
|
:key="i"
|
||||||
|
:prop="name"
|
||||||
|
:label="item.label"
|
||||||
|
:width="item.width"
|
||||||
|
>
|
||||||
|
<template v-slot="scope">
|
||||||
|
<div class="columnTemplate">
|
||||||
|
<!-- 暂未实现 -->
|
||||||
|
<div v-if="item.type.trim() == 'math'">
|
||||||
|
{{ item.custom(scope.row) }}
|
||||||
|
</div>
|
||||||
|
<!-- <ShowMathJax
|
||||||
|
v-if="item.type.trim() == 'math'"
|
||||||
|
style="zoom: 0.8"
|
||||||
|
:str="item.custom(scope.row)"
|
||||||
|
:divId="'MathJax_' + scope.row.Id"
|
||||||
|
/> -->
|
||||||
|
<el-image
|
||||||
|
v-else-if="item.type.trim() == 'img'"
|
||||||
|
style="width: 300px; height: 100px"
|
||||||
|
fit="fill"
|
||||||
|
:src="item.setting.imgUrl(scope.row[name], scope.row)[0]"
|
||||||
|
:preview-src-list="
|
||||||
|
Array.from(item.setting.imgUrl(scope.row[name], scope.row))
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<template v-slot:error>
|
||||||
|
<div class="image-slot">
|
||||||
|
{{ item.setting.imgUrl(scope.row[name], scope.row) }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-image>
|
||||||
|
<el-tooltip
|
||||||
|
v-else-if="item.type.trim() != 'img'"
|
||||||
|
popper-class="maxWidth600px"
|
||||||
|
:disabled="showTips(item, scope.row)"
|
||||||
|
:content="item.custom(scope.row) + ''"
|
||||||
|
placement="top"
|
||||||
|
effect="light"
|
||||||
|
>
|
||||||
|
<div class="tab_tip" :style="{ width: item.width }">
|
||||||
|
{{ item.custom(scope.row) }}
|
||||||
|
</div>
|
||||||
|
</el-tooltip>
|
||||||
|
<!-- <p ></p> -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div style="display: flex; align-items: center; justify-content: center">
|
||||||
|
<el-pagination
|
||||||
|
:current-page="table.search.PageIndex + 1"
|
||||||
|
:page-sizes="[20, 40, 80, 100]"
|
||||||
|
:page-size="table.search.PageSize"
|
||||||
|
layout="prev, pager, next,sizes, total"
|
||||||
|
:total="table.pageData.total"
|
||||||
|
@size-change="pageSizeChange"
|
||||||
|
@current-change="pageIndexChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="dialog-container">
|
||||||
|
<el-dialog
|
||||||
|
v-if="dialog.visible"
|
||||||
|
ref="elDialog"
|
||||||
|
v-model:visible="dialog.visible"
|
||||||
|
:class="dialog.title ? '' : 'noHeader'"
|
||||||
|
:title="dialog.title"
|
||||||
|
:width="dialog.width"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:close-on-press-escape="dialog.close"
|
||||||
|
:before-close="tableClose"
|
||||||
|
append-to-body
|
||||||
|
>
|
||||||
|
<hTableEdit
|
||||||
|
v-if="dialog.edit.visible"
|
||||||
|
:id="dialog.edit.id"
|
||||||
|
:data="table"
|
||||||
|
:tagData="dialog.edit.tagData"
|
||||||
|
:row="dialog.edit.row"
|
||||||
|
@handlePagedCallback="handleAddCallback"
|
||||||
|
/>
|
||||||
|
/>
|
||||||
|
<component
|
||||||
|
:is="dialog.custom.component"
|
||||||
|
v-if="dialog.custom.visible"
|
||||||
|
ref="custom"
|
||||||
|
:style="{ height: 'calc( ' + dialog.custom.height + ' - 84px )' }"
|
||||||
|
:iscomponent="true"
|
||||||
|
:custom="dialog.custom.custom"
|
||||||
|
:CancelCallback="handleAddCallback"
|
||||||
|
:data="dialog.custom.data"
|
||||||
|
@handlePagedCallback="handleAddCallback"
|
||||||
|
/>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.columnTemplate .btn {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
}
|
||||||
|
.columnTemplate {
|
||||||
|
display: flex;
|
||||||
|
gap: 5px;
|
||||||
|
-webkit-box-orient: horizontal;
|
||||||
|
-webkit-box-direction: normal;
|
||||||
|
-ms-flex-direction: row;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-content: flex-start;
|
||||||
|
}
|
||||||
|
.sty .tab_tip {
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.toolbar-container {
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
.maxWidth600px {
|
||||||
|
max-width: 600px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -20,6 +20,24 @@ export default {
|
||||||
title: "首页",
|
title: "首页",
|
||||||
showLink: VITE_HIDE_HOME === "true" ? false : true
|
showLink: VITE_HIDE_HOME === "true" ? false : true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/school",
|
||||||
|
name: "school",
|
||||||
|
component: () => import("@/views/school/index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "学校",
|
||||||
|
showLink: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/menu",
|
||||||
|
name: "Menu",
|
||||||
|
component: () => import("@/views/menu/index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "菜单",
|
||||||
|
showLink: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
} satisfies RouteConfigsTable;
|
} satisfies RouteConfigsTable;
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ export const usePermissionStore = defineStore("pure-permission", {
|
||||||
actions: {
|
actions: {
|
||||||
/** 组装整体路由生成的菜单 */
|
/** 组装整体路由生成的菜单 */
|
||||||
handleWholeMenus(routes: any[]) {
|
handleWholeMenus(routes: any[]) {
|
||||||
debugger;
|
|
||||||
this.wholeMenus = filterNoPermissionTree(
|
this.wholeMenus = filterNoPermissionTree(
|
||||||
filterTree(ascending(this.constantMenus.concat(routes)))
|
filterTree(ascending(this.constantMenus.concat(routes)))
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,236 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div style="padding-bottom: 5px;">
|
||||||
|
<el-button type="primary"> 分配权限 </el-button>
|
||||||
|
</div>
|
||||||
|
<el-tree
|
||||||
|
:data="treeData"
|
||||||
|
:props="treeProps"
|
||||||
|
node-key="id"
|
||||||
|
:default-expand-all="true"
|
||||||
|
:highlight-current="true"
|
||||||
|
show-checkbox
|
||||||
|
class="menu-tree"
|
||||||
|
>
|
||||||
|
<template #default="{ node, data }">
|
||||||
|
<div class="menu-node">
|
||||||
|
<i v-if="data.icon" :class="data.icon" class="menu-icon"></i>
|
||||||
|
<span class="menu-title">{{ data.title }}</span>
|
||||||
|
<span class="menu-path" v-if="data.path">{{ data.path }}</span>
|
||||||
|
</div>
|
||||||
|
<div style="display: flex; gap: 6px">
|
||||||
|
<el-button type="success" link @click="() => {}"> 添加 </el-button>
|
||||||
|
<el-button type="primary" link @click="() => {}"> 编辑 </el-button>
|
||||||
|
<el-button type="danger" link @click="() => {}"> 删除 </el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-tree>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { MenuAll, MenuItem } from "@/api/menu";
|
||||||
|
import { ElMessage } from "element-plus";
|
||||||
|
import { ref, computed, onMounted } from "vue";
|
||||||
|
defineOptions({
|
||||||
|
name: "Menu"
|
||||||
|
});
|
||||||
|
|
||||||
|
// 模拟从API获取的菜单数据
|
||||||
|
const mockMenuData: MenuItem[] = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: "dashboard",
|
||||||
|
path: "/dashboard",
|
||||||
|
isButton: false,
|
||||||
|
title: "控制台",
|
||||||
|
icon: "el-icon-monitor",
|
||||||
|
rank: 1,
|
||||||
|
showLink: true,
|
||||||
|
parentId: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: "system",
|
||||||
|
path: "/system",
|
||||||
|
isButton: false,
|
||||||
|
title: "系统管理",
|
||||||
|
icon: "el-icon-setting",
|
||||||
|
rank: 2,
|
||||||
|
showLink: true,
|
||||||
|
parentId: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: "user",
|
||||||
|
path: "/system/user",
|
||||||
|
isButton: false,
|
||||||
|
title: "用户管理",
|
||||||
|
icon: "el-icon-user",
|
||||||
|
rank: 1,
|
||||||
|
showLink: true,
|
||||||
|
parentId: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
name: "role",
|
||||||
|
path: "/system/role",
|
||||||
|
isButton: false,
|
||||||
|
title: "角色管理",
|
||||||
|
icon: "el-icon-s-custom",
|
||||||
|
rank: 2,
|
||||||
|
showLink: true,
|
||||||
|
parentId: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
name: "createUser",
|
||||||
|
isButton: true,
|
||||||
|
title: "创建用户",
|
||||||
|
rank: 1,
|
||||||
|
showLink: true,
|
||||||
|
parentId: 3
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// 将扁平数据转换为树形结构
|
||||||
|
const convertToTree = (menus: MenuItem[]): MenuItem[] => {
|
||||||
|
const menuMap = new Map<number, MenuItem>();
|
||||||
|
const tree: MenuItem[] = [];
|
||||||
|
|
||||||
|
// 创建映射并初始化children
|
||||||
|
menus.forEach(menu => {
|
||||||
|
menuMap.set(menu.id, { ...menu, children: [] });
|
||||||
|
});
|
||||||
|
|
||||||
|
// 构建树形结构
|
||||||
|
menuMap.forEach(menu => {
|
||||||
|
if (menu.parentId === 0) {
|
||||||
|
tree.push(menu);
|
||||||
|
} else {
|
||||||
|
const parent = menuMap.get(menu.parentId);
|
||||||
|
if (parent) {
|
||||||
|
parent.children!.push(menu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 对同级菜单排序
|
||||||
|
const sortChildren = (nodes: MenuItem[]) => {
|
||||||
|
nodes.sort((a, b) => a.rank - b.rank);
|
||||||
|
nodes.forEach(node => {
|
||||||
|
if (node.children && node.children.length > 0) {
|
||||||
|
sortChildren(node.children);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
sortChildren(tree);
|
||||||
|
return tree;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 树形结构数据
|
||||||
|
const treeData = ref<MenuItem[]>([]);
|
||||||
|
|
||||||
|
// 树组件配置
|
||||||
|
const treeProps = {
|
||||||
|
label: "title",
|
||||||
|
children: "children"
|
||||||
|
};
|
||||||
|
|
||||||
|
// 模拟API请求
|
||||||
|
const fetchMenuData = async (): Promise<MenuItem[]> => {
|
||||||
|
// 实际项目中替换为API调用:
|
||||||
|
// const response = await fetch('/api/menus')
|
||||||
|
// return await response.json()
|
||||||
|
|
||||||
|
// 这里使用模拟数据
|
||||||
|
return new Promise(resolve => {
|
||||||
|
setTimeout(() => resolve(mockMenuData), 300);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const flatData = await MenuAll();
|
||||||
|
if (flatData.code === 200) {
|
||||||
|
treeData.value = convertToTree(flatData.data);
|
||||||
|
} else {
|
||||||
|
ElMessage.error(flatData.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.menu-tree {
|
||||||
|
padding: 15px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-node {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
padding: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-title {
|
||||||
|
font-size: 14px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-path {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
margin-right: 10px;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
.menu-tree {
|
||||||
|
padding: 20px; /* 增加内边距 */
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 16px; /* 增大整体字体 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 使用深度选择器修改树节点样式 */
|
||||||
|
:deep(.el-tree-node) {
|
||||||
|
margin-bottom: 12px; /* 增加节点间距 */
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tree-node__content) {
|
||||||
|
height: 48px; /* 增加节点高度 */
|
||||||
|
padding: 10px 0; /* 增加垂直内边距 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-node {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px 0; /* 增加内边距 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-icon {
|
||||||
|
margin-right: 12px; /* 增加图标右边距 */
|
||||||
|
font-size: 20px; /* 增大图标 */
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-title {
|
||||||
|
font-size: 18px; /* 增大标题字体 */
|
||||||
|
margin-right: 15px; /* 增加右边距 */
|
||||||
|
font-weight: 500; /* 加粗字体 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-path {
|
||||||
|
font-size: 16px; /* 增大路径字体 */
|
||||||
|
color: #909399;
|
||||||
|
margin-right: 15px; /* 增加右边距 */
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,120 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import ahTable from "@/components/hTable/index.vue";
|
||||||
|
import { TableConfig } from "@/components/hTable/hTable";
|
||||||
|
import { onMounted, ref } from "vue";
|
||||||
|
defineOptions({
|
||||||
|
name: "School"
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {});
|
||||||
|
function searchCallback(data) {
|
||||||
|
let c = data.Conditions.find(s => s.FieldName === "Enable");
|
||||||
|
if (c) {
|
||||||
|
if (c.FieldValue == "true") {
|
||||||
|
c.FieldValue = 1;
|
||||||
|
} else {
|
||||||
|
c.FieldValue = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const table = ref<{ initTable: (config: TableConfig) => void }>(null);
|
||||||
|
const tableData: TableConfig = {
|
||||||
|
apiUrl: "School",
|
||||||
|
selectColumn: false, // 列表选择
|
||||||
|
border: false, // 是否显示表格边框
|
||||||
|
searchCallback: searchCallback,
|
||||||
|
search: {
|
||||||
|
// 查询条件
|
||||||
|
show: true,
|
||||||
|
PageIndex: 0,
|
||||||
|
PageSize: 20,
|
||||||
|
OrderBy: "CreateTime", // 排序
|
||||||
|
defaultConditions: [], // 默认查询条件
|
||||||
|
Conditions: []
|
||||||
|
},
|
||||||
|
operationColumn: true, // 显示操作按钮
|
||||||
|
operationColumnData: [
|
||||||
|
{
|
||||||
|
// 操作按钮
|
||||||
|
topBtn: false, // 是头部按钮
|
||||||
|
label: "修改",
|
||||||
|
btnType: "edit" // 按钮类型 add edit del custom
|
||||||
|
},
|
||||||
|
{
|
||||||
|
topBtn: true, // 头部按钮
|
||||||
|
show: true,
|
||||||
|
label: "新增",
|
||||||
|
btnType: "add", // 按钮类型 add edit del custom
|
||||||
|
btnStyle: "success" // topBtn: true才生效 success danger
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// topBtn: true, // 头部按钮
|
||||||
|
// label: "新增学校",
|
||||||
|
// btnType: "custom", // 按钮类型 add edit del custom
|
||||||
|
// btnStyle: "success", // topBtn: true才生效 success danger
|
||||||
|
// custom: {
|
||||||
|
// // 按钮类型 custom 专用
|
||||||
|
// title: "新增学校", // 弹出框title
|
||||||
|
// src: "school/SchoolEdit", // 组件路径
|
||||||
|
// width: "550px", // 弹框宽度
|
||||||
|
// height: "300px" // 弹框高度
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
topBtn: false, // 头部按钮
|
||||||
|
show: true,
|
||||||
|
label: "删除",
|
||||||
|
btnType: "del", // 按钮类型 add edit del 不设置则 自定义按钮
|
||||||
|
btnStyle: "danger" // topBtn: true才生效 success danger
|
||||||
|
}
|
||||||
|
],
|
||||||
|
column: {
|
||||||
|
// 行数据
|
||||||
|
id: {
|
||||||
|
label: "编号",
|
||||||
|
search: true,
|
||||||
|
add: true, // 字段允许添加
|
||||||
|
edit: true, // 字段允许修改
|
||||||
|
width: "150px"
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
label: "学校名称",
|
||||||
|
width: "300px",
|
||||||
|
search: true,
|
||||||
|
searchType: "Like",
|
||||||
|
add: true, // 字段允许添加
|
||||||
|
edit: true // 字段允许修改
|
||||||
|
},
|
||||||
|
loc: {
|
||||||
|
label: "地区",
|
||||||
|
width: "300px",
|
||||||
|
search: true,
|
||||||
|
custom: row => `${row.pname}-${row.cname}-${row.rname}`,
|
||||||
|
add: true, // 字段允许添加
|
||||||
|
edit: true // 字段允许修改
|
||||||
|
},
|
||||||
|
enable: {
|
||||||
|
label: "启用",
|
||||||
|
type: "dropdown",
|
||||||
|
search: true,
|
||||||
|
add: true, // 字段允许添加
|
||||||
|
edit: true, // 字段允许修改
|
||||||
|
setting: {
|
||||||
|
datasource: [
|
||||||
|
{ Value: "true", Text: "√" },
|
||||||
|
{ Value: "false", Text: "X" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: [],
|
||||||
|
pageData: {
|
||||||
|
total: 0
|
||||||
|
},
|
||||||
|
selectRows: []
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div><ahTable ref="table" :tableConfig="tableData" /></div>
|
||||||
|
</template>
|
||||||
Loading…
Reference in New Issue