第一次提交
This commit is contained in:
186
src/views/system/plug/components/clone.vue
Normal file
186
src/views/system/plug/components/clone.vue
Normal file
@@ -0,0 +1,186 @@
|
||||
<!-- 编辑弹窗 -->
|
||||
<template>
|
||||
<ele-modal
|
||||
:width="540"
|
||||
:visible="visible"
|
||||
:confirm-loading="loading"
|
||||
:title="`菜单克隆`"
|
||||
:body-style="{ paddingBottom: '8px' }"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-col="styleResponsive ? { md: 5, sm: 4, xs: 24 } : { flex: '90px' }"
|
||||
:wrapper-col="
|
||||
styleResponsive ? { md: 18, sm: 20, xs: 24 } : { flex: '1' }
|
||||
"
|
||||
>
|
||||
<a-form-item label="选择模板" name="tenantId">
|
||||
<a-input
|
||||
placeholder="请输入要克隆的租户ID"
|
||||
v-model:value="form.tenantId"
|
||||
@pressEnter="save"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</ele-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch } from 'vue';
|
||||
import { message } from 'ant-design-vue/es';
|
||||
import type { FormInstance, Rule } from 'ant-design-vue/es/form';
|
||||
import { isExternalLink } from 'ele-admin-pro/es';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import useFormData from '@/utils/use-form-data';
|
||||
import { clone } from '@/api/system/menu';
|
||||
import type { Menu } from '@/api/system/menu/model';
|
||||
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
const { styleResponsive } = storeToRefs(themeStore);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
const props = defineProps<{
|
||||
// 弹窗是否打开
|
||||
visible: boolean;
|
||||
// 修改回显的数据
|
||||
data?: Menu | null;
|
||||
// 上级菜单id
|
||||
parentId?: number;
|
||||
// 全部菜单数据
|
||||
menuList: Menu[];
|
||||
}>();
|
||||
|
||||
//
|
||||
const formRef = ref<FormInstance | null>(null);
|
||||
|
||||
// 是否是修改
|
||||
const isUpdate = ref(false);
|
||||
|
||||
// 提交状态
|
||||
const loading = ref(false);
|
||||
|
||||
// 表单数据
|
||||
const { form, resetFields, assignFields } = useFormData<Menu>({
|
||||
title: '',
|
||||
icon: '',
|
||||
path: '',
|
||||
component: '',
|
||||
tenantId: undefined
|
||||
});
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive<Record<string, Rule[]>>({
|
||||
tenantId: [
|
||||
{
|
||||
required: true,
|
||||
message: '克隆后原有的菜单将会抹除,请慎用!',
|
||||
type: 'string',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
/* 保存编辑 */
|
||||
const save = () => {
|
||||
if (!formRef.value) {
|
||||
return;
|
||||
}
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
loading.value = true;
|
||||
const menuForm = {
|
||||
...form
|
||||
};
|
||||
clone(menuForm)
|
||||
.then((msg) => {
|
||||
loading.value = false;
|
||||
message.success(msg);
|
||||
updateVisible(false);
|
||||
emit('done');
|
||||
})
|
||||
.catch((e) => {
|
||||
loading.value = false;
|
||||
message.error(e.message);
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
/* 更新visible */
|
||||
const updateVisible = (value: boolean) => {
|
||||
emit('update:visible', value);
|
||||
};
|
||||
|
||||
/* 判断是否是目录 */
|
||||
const isDirectory = (d: Menu) => {
|
||||
return !!d.children?.length && !d.component;
|
||||
};
|
||||
|
||||
// 查询租户列表
|
||||
// const tenantList = ref<Tenant[]>([]);
|
||||
// const reload = (tenantName?: any) => {
|
||||
// listTenant({ tenantName }).then((result) => {
|
||||
// tenantList.value = result;
|
||||
// });
|
||||
// };
|
||||
//
|
||||
// reload();
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
if (props.data) {
|
||||
const isExternal = isExternalLink(props.data.path);
|
||||
const isInner = isExternalLink(props.data.component);
|
||||
// menuType 对应的值与后端不一致在前端处理
|
||||
const menuType =
|
||||
props.data.menuType === 1 ? 2 : isDirectory(props.data) ? 0 : 1;
|
||||
assignFields({
|
||||
...props.data,
|
||||
menuType,
|
||||
openType: isExternal ? 2 : isInner ? 1 : 0,
|
||||
parentId:
|
||||
props.data.parentId === 0 ? undefined : props.data.parentId
|
||||
});
|
||||
isUpdate.value = true;
|
||||
} else {
|
||||
form.parentId = props.parentId;
|
||||
isUpdate.value = false;
|
||||
}
|
||||
} else {
|
||||
resetFields();
|
||||
formRef.value?.clearValidate();
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import * as icons from '@/layout/menu-icons';
|
||||
|
||||
export default {
|
||||
components: icons,
|
||||
data() {
|
||||
return {
|
||||
iconData: [
|
||||
{
|
||||
title: '已引入的图标',
|
||||
icons: Object.keys(icons)
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
416
src/views/system/plug/components/menu-edit.vue
Normal file
416
src/views/system/plug/components/menu-edit.vue
Normal file
@@ -0,0 +1,416 @@
|
||||
<!-- 编辑弹窗 -->
|
||||
<template>
|
||||
<ele-modal
|
||||
:width="740"
|
||||
:visible="visible"
|
||||
:confirm-loading="loading"
|
||||
:title="isUpdate ? '修改菜单' : '新建菜单'"
|
||||
:body-style="{ paddingBottom: '8px' }"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-col="styleResponsive ? { md: 6, sm: 4, xs: 24 } : { flex: '90px' }"
|
||||
:wrapper-col="
|
||||
styleResponsive ? { md: 18, sm: 20, xs: 24 } : { flex: '1' }
|
||||
"
|
||||
>
|
||||
<a-row :gutter="16">
|
||||
<a-col
|
||||
v-bind="styleResponsive ? { md: 12, sm: 24, xs: 24 } : { span: 12 }"
|
||||
>
|
||||
<a-form-item label="上级菜单" name="parentId">
|
||||
<a-tree-select
|
||||
allow-clear
|
||||
:tree-data="menuList"
|
||||
tree-default-expand-all
|
||||
placeholder="请选择上级菜单"
|
||||
:value="form.parentId || undefined"
|
||||
:dropdown-style="{ maxHeight: '360px', overflow: 'auto' }"
|
||||
@update:value="(value?: number) => (form.parentId = value)"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="菜单名称" name="title">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入菜单名称"
|
||||
v-model:value="form.title"
|
||||
@pressEnter="save"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col
|
||||
v-bind="styleResponsive ? { md: 12, sm: 24, xs: 24 } : { span: 12 }"
|
||||
>
|
||||
<a-form-item label="菜单类型" name="menuType">
|
||||
<a-radio-group
|
||||
v-model:value="form.menuType"
|
||||
@change="onMenuTypeChange"
|
||||
>
|
||||
<a-radio :value="0">目录</a-radio>
|
||||
<a-radio :value="1">菜单</a-radio>
|
||||
<a-radio :value="2">按钮</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="打开方式">
|
||||
<a-radio-group
|
||||
v-model:value="form.openType"
|
||||
:disabled="form.menuType === 0 || form.menuType === 2"
|
||||
@change="onOpenTypeChange"
|
||||
>
|
||||
<a-radio :value="0">组件</a-radio>
|
||||
<a-radio :value="1">内链</a-radio>
|
||||
<a-radio :value="2">外链</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<div style="margin-bottom: 22px">
|
||||
<a-divider />
|
||||
</div>
|
||||
<a-row :gutter="16">
|
||||
<a-col
|
||||
v-bind="styleResponsive ? { md: 12, sm: 24, xs: 24 } : { span: 12 }"
|
||||
>
|
||||
<a-form-item label="菜单图标" name="icon">
|
||||
<ele-icon-picker
|
||||
:data="iconData"
|
||||
:allow-search="false"
|
||||
v-model:value="form.icon"
|
||||
placeholder="请选择菜单图标"
|
||||
:disabled="form.menuType === 2"
|
||||
>
|
||||
<template #icon="{ icon }">
|
||||
<component :is="icon" />
|
||||
</template>
|
||||
</ele-icon-picker>
|
||||
</a-form-item>
|
||||
<a-form-item name="path">
|
||||
<template #label>
|
||||
<a-tooltip
|
||||
v-if="form.openType === 2"
|
||||
title="需要以`http://`、`https://`、`//`开头"
|
||||
>
|
||||
<question-circle-outlined
|
||||
style="vertical-align: -2px; margin-right: 4px"
|
||||
/>
|
||||
</a-tooltip>
|
||||
<span>{{ form.openType === 2 ? '外链地址' : '路由地址' }}</span>
|
||||
</template>
|
||||
<a-input
|
||||
allow-clear
|
||||
v-model:value="form.path"
|
||||
:disabled="form.menuType === 2"
|
||||
:placeholder="
|
||||
form.openType === 2 ? '请输入外链地址' : '请输入路由地址'
|
||||
"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item name="component">
|
||||
<template #label>
|
||||
<a-tooltip
|
||||
v-if="form.openType === 1"
|
||||
title="需要以`http://`、`https://`、`//`开头"
|
||||
>
|
||||
<question-circle-outlined
|
||||
style="vertical-align: -2px; margin-right: 4px"
|
||||
/>
|
||||
</a-tooltip>
|
||||
<span>{{ form.openType === 1 ? '内链地址' : '组件路径' }}</span>
|
||||
</template>
|
||||
<a-input
|
||||
allow-clear
|
||||
v-model:value="form.component"
|
||||
:disabled="
|
||||
form.menuType === 0 ||
|
||||
form.menuType === 2 ||
|
||||
form.openType === 2
|
||||
"
|
||||
:placeholder="
|
||||
form.openType === 1 ? '请输入内链地址' : '请输入组件路径'
|
||||
"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col
|
||||
v-bind="styleResponsive ? { md: 12, sm: 24, xs: 24 } : { span: 12 }"
|
||||
>
|
||||
<a-form-item label="权限标识" name="authority">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入权限标识"
|
||||
v-model:value="form.authority"
|
||||
:disabled="
|
||||
form.menuType === 0 ||
|
||||
(form.menuType === 1 && form.openType === 2)
|
||||
"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="排序号" name="sortNumber">
|
||||
<a-input-number
|
||||
:min="0"
|
||||
:max="99999"
|
||||
class="ele-fluid"
|
||||
placeholder="请输入排序号"
|
||||
v-model:value="form.sortNumber"
|
||||
@pressEnter="save"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="是否展示">
|
||||
<a-switch
|
||||
checked-children="是"
|
||||
un-checked-children="否"
|
||||
:checked="form.hide === 0"
|
||||
:disabled="form.menuType === 2"
|
||||
@update:checked="updateHideValue"
|
||||
/>
|
||||
<a-tooltip
|
||||
title="选择不展示只注册路由不展示在侧边栏, 比如添加页面应该选择不展示"
|
||||
>
|
||||
<question-circle-outlined
|
||||
style="vertical-align: -4px; margin-left: 16px"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-form-item
|
||||
label="路由元数据"
|
||||
name="meta"
|
||||
:label-col="
|
||||
styleResponsive ? { md: 3, sm: 4, xs: 24 } : { flex: '90px' }
|
||||
"
|
||||
:wrapper-col="
|
||||
styleResponsive ? { md: 21, sm: 20, xs: 24 } : { flex: '1' }
|
||||
"
|
||||
>
|
||||
<a-textarea
|
||||
:rows="4"
|
||||
:maxlength="200"
|
||||
placeholder="请输入JSON格式的路由元数据"
|
||||
v-model:value="form.meta"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</ele-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch } from 'vue';
|
||||
import { message } from 'ant-design-vue/es';
|
||||
import type { FormInstance, Rule } from 'ant-design-vue/es/form';
|
||||
import { QuestionCircleOutlined } from '@ant-design/icons-vue';
|
||||
import { isExternalLink } from 'ele-admin-pro/es';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import useFormData from '@/utils/use-form-data';
|
||||
import { addMenu, updateMenu } from '@/api/system/menu';
|
||||
import type { Menu } from '@/api/system/menu/model';
|
||||
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
const { styleResponsive } = storeToRefs(themeStore);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
const props = defineProps<{
|
||||
// 弹窗是否打开
|
||||
visible: boolean;
|
||||
// 修改回显的数据
|
||||
data?: Menu | null;
|
||||
// 上级菜单id
|
||||
parentId?: number;
|
||||
// 全部菜单数据
|
||||
menuList: Menu[];
|
||||
}>();
|
||||
|
||||
//
|
||||
const formRef = ref<FormInstance | null>(null);
|
||||
|
||||
// 是否是修改
|
||||
const isUpdate = ref(false);
|
||||
|
||||
// 提交状态
|
||||
const loading = ref(false);
|
||||
|
||||
// 表单数据
|
||||
const { form, resetFields, assignFields } = useFormData<Menu>({
|
||||
menuId: undefined,
|
||||
parentId: undefined,
|
||||
title: '',
|
||||
menuType: 0,
|
||||
openType: 0,
|
||||
icon: '',
|
||||
path: '',
|
||||
component: '',
|
||||
authority: '',
|
||||
sortNumber: undefined,
|
||||
hide: 0,
|
||||
meta: ''
|
||||
});
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive<Record<string, Rule[]>>({
|
||||
title: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入菜单名称',
|
||||
type: 'string',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
sortNumber: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入排序号',
|
||||
type: 'number',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
meta: [
|
||||
{
|
||||
type: 'string',
|
||||
validator: async (_rule: Rule, value: string) => {
|
||||
if (value) {
|
||||
const msg = '请输入正确的JSON格式';
|
||||
try {
|
||||
const obj = JSON.parse(value);
|
||||
if (typeof obj !== 'object' || obj === null) {
|
||||
return Promise.reject(msg);
|
||||
}
|
||||
} catch (_e) {
|
||||
return Promise.reject(msg);
|
||||
}
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
/* 保存编辑 */
|
||||
const save = () => {
|
||||
if (!formRef.value) {
|
||||
return;
|
||||
}
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
loading.value = true;
|
||||
const menuForm = {
|
||||
...form,
|
||||
// menuType 对应的值与后端不一致在前端处理
|
||||
menuType: form.menuType === 2 ? 1 : 0,
|
||||
parentId: form.parentId || 0
|
||||
};
|
||||
const saveOrUpdate = isUpdate.value ? updateMenu : addMenu;
|
||||
saveOrUpdate(menuForm)
|
||||
.then((msg) => {
|
||||
loading.value = false;
|
||||
message.success(msg);
|
||||
updateVisible(false);
|
||||
emit('done');
|
||||
})
|
||||
.catch((e) => {
|
||||
loading.value = false;
|
||||
message.error(e.message);
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
/* 更新visible */
|
||||
const updateVisible = (value: boolean) => {
|
||||
emit('update:visible', value);
|
||||
};
|
||||
|
||||
/* menuType选择改变 */
|
||||
const onMenuTypeChange = () => {
|
||||
if (form.menuType === 0) {
|
||||
form.authority = '';
|
||||
form.openType = 0;
|
||||
form.component = '';
|
||||
} else if (form.menuType === 1) {
|
||||
if (form.openType === 2) {
|
||||
form.authority = '';
|
||||
}
|
||||
} else {
|
||||
form.openType = 0;
|
||||
form.icon = '';
|
||||
form.path = '';
|
||||
form.component = '';
|
||||
form.hide = 0;
|
||||
}
|
||||
};
|
||||
|
||||
/* openType选择改变 */
|
||||
const onOpenTypeChange = () => {
|
||||
if (form.openType === 2) {
|
||||
form.component = '';
|
||||
form.authority = '';
|
||||
}
|
||||
};
|
||||
|
||||
const updateHideValue = (value: boolean) => {
|
||||
form.hide = value ? 0 : 1;
|
||||
};
|
||||
|
||||
/* 判断是否是目录 */
|
||||
const isDirectory = (d: Menu) => {
|
||||
return !!d.children?.length && !d.component;
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
if (props.data) {
|
||||
const isExternal = isExternalLink(props.data.path);
|
||||
const isInner = isExternalLink(props.data.component);
|
||||
// menuType 对应的值与后端不一致在前端处理
|
||||
const menuType =
|
||||
props.data.menuType === 1 ? 2 : isDirectory(props.data) ? 0 : 1;
|
||||
assignFields({
|
||||
...props.data,
|
||||
menuType,
|
||||
openType: isExternal ? 2 : isInner ? 1 : 0,
|
||||
parentId:
|
||||
props.data.parentId === 0 ? undefined : props.data.parentId
|
||||
});
|
||||
isUpdate.value = true;
|
||||
} else {
|
||||
form.parentId = props.parentId;
|
||||
isUpdate.value = false;
|
||||
}
|
||||
} else {
|
||||
resetFields();
|
||||
formRef.value?.clearValidate();
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import * as icons from '@/layout/menu-icons';
|
||||
|
||||
export default {
|
||||
components: icons,
|
||||
data() {
|
||||
return {
|
||||
iconData: [
|
||||
{
|
||||
title: '已引入的图标',
|
||||
icons: Object.keys(icons)
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
106
src/views/system/plug/components/menu-search.vue
Normal file
106
src/views/system/plug/components/menu-search.vue
Normal file
@@ -0,0 +1,106 @@
|
||||
<!-- 搜索表单 -->
|
||||
<template>
|
||||
<a-form
|
||||
:label-col="
|
||||
styleResponsive ? { xl: 7, lg: 5, md: 7, sm: 4 } : { flex: '90px' }
|
||||
"
|
||||
:wrapper-col="
|
||||
styleResponsive ? { xl: 17, lg: 19, md: 17, sm: 20 } : { flex: '1' }
|
||||
"
|
||||
>
|
||||
<a-row :gutter="8">
|
||||
<a-col
|
||||
v-bind="
|
||||
styleResponsive
|
||||
? { xl: 6, lg: 12, md: 12, sm: 24, xs: 24 }
|
||||
: { span: 6 }
|
||||
"
|
||||
>
|
||||
<a-form-item label="菜单名称">
|
||||
<a-input
|
||||
v-model:value.trim="form.title"
|
||||
placeholder="请输入"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col
|
||||
v-bind="
|
||||
styleResponsive
|
||||
? { xl: 6, lg: 12, md: 12, sm: 24, xs: 24 }
|
||||
: { span: 6 }
|
||||
"
|
||||
>
|
||||
<a-form-item label="菜单地址">
|
||||
<a-input
|
||||
v-model:value.trim="form.path"
|
||||
placeholder="请输入"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col
|
||||
v-bind="
|
||||
styleResponsive
|
||||
? { xl: 6, lg: 12, md: 12, sm: 24, xs: 24 }
|
||||
: { span: 6 }
|
||||
"
|
||||
>
|
||||
<a-form-item label="权限标识">
|
||||
<a-input
|
||||
v-model:value.trim="form.authority"
|
||||
placeholder="请输入"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col
|
||||
v-bind="
|
||||
styleResponsive
|
||||
? { xl: 6, lg: 12, md: 12, sm: 24, xs: 24 }
|
||||
: { span: 6 }
|
||||
"
|
||||
>
|
||||
<a-form-item class="ele-text-right" :wrapper-col="{ span: 24 }">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="search">查询</a-button>
|
||||
<a-button @click="reset">重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import useFormData from '@/utils/use-form-data';
|
||||
import type { MenuParam } from '@/api/system/menu/model';
|
||||
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
const { styleResponsive } = storeToRefs(themeStore);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search', where?: MenuParam): void;
|
||||
}>();
|
||||
|
||||
// 表单数据
|
||||
const { form, resetFields } = useFormData<MenuParam>({
|
||||
title: '',
|
||||
path: '',
|
||||
authority: ''
|
||||
});
|
||||
|
||||
/* 搜索 */
|
||||
const search = () => {
|
||||
emit('search', form);
|
||||
};
|
||||
|
||||
/* 重置 */
|
||||
const reset = () => {
|
||||
resetFields();
|
||||
search();
|
||||
};
|
||||
</script>
|
||||
253
src/views/system/plug/components/plug-edit.vue
Normal file
253
src/views/system/plug/components/plug-edit.vue
Normal file
@@ -0,0 +1,253 @@
|
||||
<!-- 编辑弹窗 -->
|
||||
<template>
|
||||
<ele-modal
|
||||
:width="780"
|
||||
:visible="visible"
|
||||
:confirm-loading="loading"
|
||||
title="安装插件"
|
||||
:body-style="{ paddingBottom: '8px' }"
|
||||
@update:visible="updateVisible"
|
||||
okText="安装插件"
|
||||
:footer="footer"
|
||||
@ok="save"
|
||||
>
|
||||
<template v-if="!isSuccess">
|
||||
<a-alert
|
||||
:description="`安装成功后,插件出现在左侧菜单中,请在 【系统设置->菜单管理】修改插件的名称及顺序`"
|
||||
type="success"
|
||||
closable
|
||||
show-icon
|
||||
style="margin-bottom: 20px"
|
||||
>
|
||||
<template #icon><SmileOutlined /></template>
|
||||
</a-alert>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-col="
|
||||
styleResponsive ? { md: 6, sm: 4, xs: 24 } : { flex: '90px' }
|
||||
"
|
||||
:wrapper-col="
|
||||
styleResponsive ? { md: 18, sm: 20, xs: 24 } : { flex: '1' }
|
||||
"
|
||||
>
|
||||
<a-row :gutter="16">
|
||||
<a-col
|
||||
v-bind="styleResponsive ? { md: 12, sm: 24, xs: 24 } : { span: 12 }"
|
||||
>
|
||||
<a-form-item label="插件名称" name="parentId">
|
||||
<span class="ele-text-heading">{{ data.title }}</span>
|
||||
</a-form-item>
|
||||
<a-form-item label="价格" name="comments">
|
||||
¥{{ data.price }}
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col
|
||||
v-bind="styleResponsive ? { md: 12, sm: 24, xs: 24 } : { span: 12 }"
|
||||
>
|
||||
<a-form-item label="插件ID" name="menuId">
|
||||
<span class="ele-text-secondary">{{ data.menuId }}</span>
|
||||
</a-form-item>
|
||||
<a-form-item label="开发商" name="sortName">
|
||||
<span class="ele-text-secondary">
|
||||
{{ data.tenantId === 5 ? '官方' : data.shortName }}
|
||||
</span>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col
|
||||
v-bind="styleResponsive ? { md: 12, sm: 24, xs: 24 } : { span: 24 }"
|
||||
>
|
||||
<a-form-item label="插件介绍" name="comments">
|
||||
<div class="ele-text-secondary" style="padding: 5px">
|
||||
<byte-md-viewer :value="data.comments" :plugins="plugins" />
|
||||
</div>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</template>
|
||||
<a-result
|
||||
status="success"
|
||||
title="安装成功"
|
||||
v-if="isSuccess"
|
||||
sub-title="请在 【系统设置->菜单管理】修改插件的名称及顺序"
|
||||
>
|
||||
<template #extra>
|
||||
<a-button key="console" type="primary" @click="reset(data.path)"
|
||||
>立即前往</a-button
|
||||
>
|
||||
</template>
|
||||
</a-result>
|
||||
</ele-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import { message } from 'ant-design-vue/es';
|
||||
import type { FormInstance } from 'ant-design-vue/es/form';
|
||||
import { isExternalLink } from 'ele-admin-pro/es';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import useFormData from '@/utils/use-form-data';
|
||||
import type { Menu } from '@/api/system/menu/model';
|
||||
import { Plug } from '@/api/system/plug/model';
|
||||
import { SmileOutlined } from '@ant-design/icons-vue';
|
||||
import gfm from '@bytemd/plugin-gfm';
|
||||
import highlight from '@bytemd/plugin-highlight-ssr';
|
||||
// // 插件的中文语言文件
|
||||
import zh_HansGfm from '@bytemd/plugin-gfm/locales/zh_Hans.json';
|
||||
import { installPlug } from '@/api/system/menu';
|
||||
import { reloadPageTab } from '@/utils/page-tab-util';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const { push } = useRouter();
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
const { styleResponsive } = storeToRefs(themeStore);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
const props = defineProps<{
|
||||
// 弹窗是否打开
|
||||
visible: boolean;
|
||||
// 修改回显的数据
|
||||
data?: Plug | null;
|
||||
// 上级插件id
|
||||
parentId?: number;
|
||||
// 全部插件数据
|
||||
menuList: Plug[];
|
||||
}>();
|
||||
|
||||
// 是否是修改
|
||||
const isUpdate = ref(false);
|
||||
// 提交状态
|
||||
const loading = ref(false);
|
||||
const appid = ref(undefined);
|
||||
const formRef = ref<FormInstance | null>(null);
|
||||
const isSuccess = ref(false);
|
||||
const footer = ref();
|
||||
|
||||
// 表单数据
|
||||
const { form, resetFields, assignFields } = useFormData<Plug>({
|
||||
plugId: undefined,
|
||||
menuId: undefined,
|
||||
parentId: undefined,
|
||||
title: '',
|
||||
menuType: 0,
|
||||
openType: 0,
|
||||
icon: '',
|
||||
path: '',
|
||||
component: '',
|
||||
authority: '',
|
||||
comments: '',
|
||||
sortNumber: undefined,
|
||||
hide: 0,
|
||||
meta: '',
|
||||
status: 10
|
||||
});
|
||||
|
||||
/* 保存编辑 */
|
||||
const save = () => {
|
||||
if (!formRef.value) {
|
||||
return;
|
||||
}
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
loading.value = true;
|
||||
const menuForm = {
|
||||
...form,
|
||||
// menuType 对应的值与后端不一致在前端处理
|
||||
menuType: form.menuType === 2 ? 1 : 0,
|
||||
parentId: form.parentId || 0
|
||||
};
|
||||
console.log(menuForm);
|
||||
installPlug(form.menuId)
|
||||
.then((msg) => {
|
||||
loading.value = false;
|
||||
isSuccess.value = true;
|
||||
footer.value = null;
|
||||
// message.success(msg);
|
||||
})
|
||||
.catch((e) => {
|
||||
loading.value = false;
|
||||
message.error(e.message);
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
/* 更新visible */
|
||||
const updateVisible = (value: boolean) => {
|
||||
emit('update:visible', value);
|
||||
};
|
||||
|
||||
// 插件
|
||||
const plugins = ref([
|
||||
gfm({
|
||||
locale: zh_HansGfm
|
||||
}),
|
||||
highlight()
|
||||
]);
|
||||
|
||||
const reset = (url) => {
|
||||
console.log(url);
|
||||
push(url);
|
||||
};
|
||||
|
||||
/* 判断是否是目录 */
|
||||
const isDirectory = (d: Menu) => {
|
||||
return !!d.children?.length && !d.component;
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
if (props.data) {
|
||||
const isExternal = isExternalLink(props.data.path);
|
||||
const isInner = isExternalLink(props.data.component);
|
||||
// menuType 对应的值与后端不一致在前端处理
|
||||
const menuType =
|
||||
props.data.menuType === 1 ? 2 : isDirectory(props.data) ? 0 : 1;
|
||||
assignFields({
|
||||
...props.data,
|
||||
menuType,
|
||||
openType: isExternal ? 2 : isInner ? 1 : 0,
|
||||
parentId:
|
||||
props.data.parentId === 0 ? undefined : props.data.parentId
|
||||
});
|
||||
isUpdate.value = true;
|
||||
} else {
|
||||
form.parentId = props.parentId;
|
||||
isUpdate.value = false;
|
||||
}
|
||||
} else {
|
||||
resetFields();
|
||||
formRef.value?.clearValidate();
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import * as icons from '@/layout/menu-icons';
|
||||
|
||||
export default {
|
||||
components: icons,
|
||||
data() {
|
||||
return {
|
||||
iconData: [
|
||||
{
|
||||
title: '已引入的图标',
|
||||
icons: Object.keys(icons)
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
67
src/views/system/plug/components/plug-search.vue
Normal file
67
src/views/system/plug/components/plug-search.vue
Normal file
@@ -0,0 +1,67 @@
|
||||
<!-- 搜索表单 -->
|
||||
<template>
|
||||
<a-space :size="10" style="flex-wrap: wrap">
|
||||
<!-- <a-radio-group :defaultValue="plugType" @change="handleTabs">-->
|
||||
<!-- <a-radio-button :value="0">插件市场</a-radio-button>-->
|
||||
<!-- <a-radio-button :value="10">我的插件</a-radio-button>-->
|
||||
<!-- </a-radio-group>-->
|
||||
<a-input-search
|
||||
allow-clear
|
||||
placeholder="请输入搜索关键词"
|
||||
v-model:value="searchText"
|
||||
@pressEnter="search"
|
||||
@search="search"
|
||||
/>
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import useSearch from '@/utils/use-search';
|
||||
import type { CustomerParam } from '@/api/oa/customer/model';
|
||||
import { ref, watch } from 'vue';
|
||||
import { AppParam } from '@/api/app/model';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
// 选中的角色
|
||||
selection?: [];
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search', where?: CustomerParam): void;
|
||||
(e: 'add'): void;
|
||||
(e: 'remove'): void;
|
||||
}>();
|
||||
|
||||
// 表单数据
|
||||
const { where, resetFields } = useSearch<AppParam>({
|
||||
appId: undefined,
|
||||
userId: undefined,
|
||||
keywords: undefined,
|
||||
status: 0
|
||||
});
|
||||
// const plugType = ref<number>(0);
|
||||
// 搜索内容
|
||||
const searchText = ref(null);
|
||||
|
||||
/* 搜索 */
|
||||
const search = () => {
|
||||
resetFields();
|
||||
if (searchText.value) {
|
||||
where.keywords = searchText.value;
|
||||
}
|
||||
emit('search', where);
|
||||
};
|
||||
|
||||
// const reload = () => {
|
||||
// // 刷新当前路由
|
||||
// emit('search', where);
|
||||
// };
|
||||
|
||||
watch(
|
||||
() => props.selection,
|
||||
() => {}
|
||||
);
|
||||
</script>
|
||||
186
src/views/system/plug/create/components/clone.vue
Normal file
186
src/views/system/plug/create/components/clone.vue
Normal file
@@ -0,0 +1,186 @@
|
||||
<!-- 编辑弹窗 -->
|
||||
<template>
|
||||
<ele-modal
|
||||
:width="540"
|
||||
:visible="visible"
|
||||
:confirm-loading="loading"
|
||||
:title="`菜单克隆`"
|
||||
:body-style="{ paddingBottom: '8px' }"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-col="styleResponsive ? { md: 5, sm: 4, xs: 24 } : { flex: '90px' }"
|
||||
:wrapper-col="
|
||||
styleResponsive ? { md: 18, sm: 20, xs: 24 } : { flex: '1' }
|
||||
"
|
||||
>
|
||||
<a-form-item label="选择模板" name="tenantId">
|
||||
<a-input
|
||||
placeholder="请输入要克隆的租户ID"
|
||||
v-model:value="form.tenantId"
|
||||
@pressEnter="save"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</ele-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch } from 'vue';
|
||||
import { message } from 'ant-design-vue/es';
|
||||
import type { FormInstance, Rule } from 'ant-design-vue/es/form';
|
||||
import { isExternalLink } from 'ele-admin-pro/es';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import useFormData from '@/utils/use-form-data';
|
||||
import { clone } from '@/api/system/menu';
|
||||
import type { Menu } from '@/api/system/menu/model';
|
||||
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
const { styleResponsive } = storeToRefs(themeStore);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
const props = defineProps<{
|
||||
// 弹窗是否打开
|
||||
visible: boolean;
|
||||
// 修改回显的数据
|
||||
data?: Menu | null;
|
||||
// 上级菜单id
|
||||
parentId?: number;
|
||||
// 全部菜单数据
|
||||
menuList: Menu[];
|
||||
}>();
|
||||
|
||||
//
|
||||
const formRef = ref<FormInstance | null>(null);
|
||||
|
||||
// 是否是修改
|
||||
const isUpdate = ref(false);
|
||||
|
||||
// 提交状态
|
||||
const loading = ref(false);
|
||||
|
||||
// 表单数据
|
||||
const { form, resetFields, assignFields } = useFormData<Menu>({
|
||||
title: '',
|
||||
icon: '',
|
||||
path: '',
|
||||
component: '',
|
||||
tenantId: undefined
|
||||
});
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive<Record<string, Rule[]>>({
|
||||
tenantId: [
|
||||
{
|
||||
required: true,
|
||||
message: '克隆后原有的菜单将会抹除,请慎用!',
|
||||
type: 'string',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
/* 保存编辑 */
|
||||
const save = () => {
|
||||
if (!formRef.value) {
|
||||
return;
|
||||
}
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
loading.value = true;
|
||||
const menuForm = {
|
||||
...form
|
||||
};
|
||||
clone(menuForm)
|
||||
.then((msg) => {
|
||||
loading.value = false;
|
||||
message.success(msg);
|
||||
updateVisible(false);
|
||||
emit('done');
|
||||
})
|
||||
.catch((e) => {
|
||||
loading.value = false;
|
||||
message.error(e.message);
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
/* 更新visible */
|
||||
const updateVisible = (value: boolean) => {
|
||||
emit('update:visible', value);
|
||||
};
|
||||
|
||||
/* 判断是否是目录 */
|
||||
const isDirectory = (d: Menu) => {
|
||||
return !!d.children?.length && !d.component;
|
||||
};
|
||||
|
||||
// 查询租户列表
|
||||
// const tenantList = ref<Tenant[]>([]);
|
||||
// const reload = (tenantName?: any) => {
|
||||
// listTenant({ tenantName }).then((result) => {
|
||||
// tenantList.value = result;
|
||||
// });
|
||||
// };
|
||||
//
|
||||
// reload();
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
if (props.data) {
|
||||
const isExternal = isExternalLink(props.data.path);
|
||||
const isInner = isExternalLink(props.data.component);
|
||||
// menuType 对应的值与后端不一致在前端处理
|
||||
const menuType =
|
||||
props.data.menuType === 1 ? 2 : isDirectory(props.data) ? 0 : 1;
|
||||
assignFields({
|
||||
...props.data,
|
||||
menuType,
|
||||
openType: isExternal ? 2 : isInner ? 1 : 0,
|
||||
parentId:
|
||||
props.data.parentId === 0 ? undefined : props.data.parentId
|
||||
});
|
||||
isUpdate.value = true;
|
||||
} else {
|
||||
form.parentId = props.parentId;
|
||||
isUpdate.value = false;
|
||||
}
|
||||
} else {
|
||||
resetFields();
|
||||
formRef.value?.clearValidate();
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import * as icons from '@/layout/menu-icons';
|
||||
|
||||
export default {
|
||||
components: icons,
|
||||
data() {
|
||||
return {
|
||||
iconData: [
|
||||
{
|
||||
title: '已引入的图标',
|
||||
icons: Object.keys(icons)
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
262
src/views/system/plug/create/components/plug-edit.vue
Normal file
262
src/views/system/plug/create/components/plug-edit.vue
Normal file
@@ -0,0 +1,262 @@
|
||||
<!-- 编辑弹窗 -->
|
||||
<template>
|
||||
<ele-modal
|
||||
:width="740"
|
||||
:visible="visible"
|
||||
:confirm-loading="loading"
|
||||
:title="isUpdate ? '插件管理' : '发布插件'"
|
||||
:body-style="{ paddingBottom: '8px' }"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save"
|
||||
>
|
||||
<a-alert
|
||||
:description="`审核通过后,插件将展示在插件市场,可供其他用户安装和使用后,获取销售分成。`"
|
||||
type="success"
|
||||
closable
|
||||
show-icon
|
||||
style="margin-bottom: 20px"
|
||||
>
|
||||
<template #icon><SmileOutlined /></template>
|
||||
</a-alert>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-col="styleResponsive ? { md: 6, sm: 4, xs: 24 } : { flex: '90px' }"
|
||||
:wrapper-col="
|
||||
styleResponsive ? { md: 18, sm: 20, xs: 24 } : { flex: '1' }
|
||||
"
|
||||
>
|
||||
<a-row :gutter="16">
|
||||
<a-col
|
||||
v-bind="styleResponsive ? { md: 12, sm: 24, xs: 24 } : { span: 12 }"
|
||||
>
|
||||
<a-form-item label="选择模块" name="parentId">
|
||||
<a-tree-select
|
||||
allow-clear
|
||||
:tree-data="menuList"
|
||||
tree-default-expand-all
|
||||
placeholder="请选择上级插件"
|
||||
:value="form.parentId || undefined"
|
||||
:dropdown-style="{ maxHeight: '360px', overflow: 'auto' }"
|
||||
@update:value="(value?: number) => (form.parentId = value)"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="插件名称" name="title">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入插件名称"
|
||||
v-model:value="form.title"
|
||||
/>
|
||||
</a-form-item>
|
||||
<!-- <a-form-item label="关联应用" name="appId">-->
|
||||
<!-- <SelectApp v-model:value="appId" @done="onApp" />-->
|
||||
<!-- </a-form-item>-->
|
||||
</a-col>
|
||||
<a-col
|
||||
v-bind="styleResponsive ? { md: 12, sm: 24, xs: 24 } : { span: 12 }"
|
||||
>
|
||||
<a-form-item label="插件价格" name="price">
|
||||
<a-input-number
|
||||
allow-clear
|
||||
:min="0"
|
||||
:precision="2"
|
||||
style="width: 200px"
|
||||
placeholder="请输入插件价格"
|
||||
v-model:value="form.price"
|
||||
/>
|
||||
<span class="ml-10">元</span>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<div style="margin-bottom: 22px">
|
||||
<a-divider />
|
||||
</div>
|
||||
<a-form-item
|
||||
label="插件简介"
|
||||
name="comments"
|
||||
:label-col="
|
||||
styleResponsive ? { md: 3, sm: 4, xs: 24 } : { flex: '90px' }
|
||||
"
|
||||
:wrapper-col="
|
||||
styleResponsive ? { md: 21, sm: 20, xs: 24 } : { flex: '1' }
|
||||
"
|
||||
>
|
||||
<a-textarea
|
||||
:rows="4"
|
||||
:maxlength="200"
|
||||
placeholder="请输入插件简介"
|
||||
v-model:value="form.comments"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</ele-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch } from 'vue';
|
||||
import { message } from 'ant-design-vue/es';
|
||||
import type { FormInstance, Rule } from 'ant-design-vue/es/form';
|
||||
import { isExternalLink } from 'ele-admin-pro/es';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import useFormData from '@/utils/use-form-data';
|
||||
import type { Menu } from '@/api/system/menu/model';
|
||||
import { createPlug, updatePlug } from '@/api/system/plug';
|
||||
import { Plug } from '@/api/system/plug/model';
|
||||
import { SmileOutlined } from '@ant-design/icons-vue';
|
||||
import { addDocs, updateDocs } from '@/api/cms/docs';
|
||||
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
const { styleResponsive } = storeToRefs(themeStore);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
const props = defineProps<{
|
||||
// 弹窗是否打开
|
||||
visible: boolean;
|
||||
// 修改回显的数据
|
||||
data?: Menu | null;
|
||||
// 上级插件id
|
||||
parentId?: number;
|
||||
// 全部插件数据
|
||||
menuList: Menu[];
|
||||
}>();
|
||||
|
||||
// 是否是修改
|
||||
const isUpdate = ref(false);
|
||||
// 提交状态
|
||||
const loading = ref(false);
|
||||
const formRef = ref<FormInstance | null>(null);
|
||||
|
||||
// 表单数据
|
||||
const { form, resetFields, assignFields } = useFormData<Plug>({
|
||||
plugId: undefined,
|
||||
menuId: undefined,
|
||||
parentId: undefined,
|
||||
title: '',
|
||||
price: undefined,
|
||||
comments: '',
|
||||
status: undefined
|
||||
});
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive<Record<string, Rule[]>>({
|
||||
parentId: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择模块',
|
||||
type: 'number',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
title: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入插件名称',
|
||||
type: 'string',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
/* 保存编辑 */
|
||||
const save = () => {
|
||||
if (!formRef.value) {
|
||||
return;
|
||||
}
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
loading.value = true;
|
||||
const formData = {
|
||||
...form,
|
||||
status: 10
|
||||
// content: content.value
|
||||
};
|
||||
const saveOrUpdate = isUpdate.value ? updatePlug : createPlug;
|
||||
saveOrUpdate(formData)
|
||||
.then((msg) => {
|
||||
loading.value = false;
|
||||
message.success(msg);
|
||||
updateVisible(false);
|
||||
emit('done');
|
||||
})
|
||||
.catch((e) => {
|
||||
loading.value = false;
|
||||
message.error(e.message);
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
/* 更新visible */
|
||||
const updateVisible = (value: boolean) => {
|
||||
emit('update:visible', value);
|
||||
};
|
||||
|
||||
/* 判断是否是目录 */
|
||||
const isDirectory = (d: Menu) => {
|
||||
return !!d.children?.length && !d.component;
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
if (props.data) {
|
||||
const isExternal = isExternalLink(props.data.path);
|
||||
const isInner = isExternalLink(props.data.component);
|
||||
// menuType 对应的值与后端不一致在前端处理
|
||||
const menuType =
|
||||
props.data.menuType === 1 ? 2 : isDirectory(props.data) ? 0 : 1;
|
||||
assignFields({
|
||||
...props.data,
|
||||
menuType,
|
||||
openType: isExternal ? 2 : isInner ? 1 : 0,
|
||||
parentId:
|
||||
props.data.parentId === 0 ? undefined : props.data.parentId
|
||||
});
|
||||
form.parentId = props.parentId;
|
||||
isUpdate.value = true;
|
||||
} else {
|
||||
form.parentId = props.parentId;
|
||||
isUpdate.value = false;
|
||||
}
|
||||
} else {
|
||||
resetFields();
|
||||
formRef.value?.clearValidate();
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import * as icons from '@/layout/menu-icons';
|
||||
|
||||
export default {
|
||||
components: icons,
|
||||
data() {
|
||||
return {
|
||||
iconData: [
|
||||
{
|
||||
title: '已引入的图标',
|
||||
icons: Object.keys(icons)
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="less">
|
||||
.tab-pane {
|
||||
min-height: 300px;
|
||||
}
|
||||
.ml-10 {
|
||||
margin-left: 5px;
|
||||
}
|
||||
</style>
|
||||
98
src/views/system/plug/create/components/plug-search.vue
Normal file
98
src/views/system/plug/create/components/plug-search.vue
Normal file
@@ -0,0 +1,98 @@
|
||||
<!-- 搜索表单 -->
|
||||
<template>
|
||||
<a-space :size="10" style="flex-wrap: wrap">
|
||||
<a-button type="primary" class="ele-btn-icon" @click="add">
|
||||
<template #icon>
|
||||
<PlusOutlined />
|
||||
</template>
|
||||
<span>发布插件</span>
|
||||
</a-button>
|
||||
<!-- <a-input-search-->
|
||||
<!-- allow-clear-->
|
||||
<!-- placeholder="请输入搜索关键词"-->
|
||||
<!-- v-model:value="searchText"-->
|
||||
<!-- @pressEnter="search"-->
|
||||
<!-- @search="search"-->
|
||||
<!-- />-->
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import useSearch from '@/utils/use-search';
|
||||
import type { CustomerParam } from '@/api/oa/customer/model';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { AppParam } from '@/api/app/model';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
// 选中的角色
|
||||
selection?: [];
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search', where?: CustomerParam): void;
|
||||
(e: 'add'): void;
|
||||
(e: 'remove'): void;
|
||||
}>();
|
||||
|
||||
// 表单数据
|
||||
const { where, resetFields } = useSearch<AppParam>({
|
||||
appId: undefined,
|
||||
userId: undefined,
|
||||
keywords: undefined,
|
||||
status: 0
|
||||
});
|
||||
const plugType = ref<number>(0);
|
||||
// 搜索内容
|
||||
const searchText = ref(null);
|
||||
const userId = ref(0);
|
||||
|
||||
/* 搜索 */
|
||||
const search = () => {
|
||||
resetFields();
|
||||
if (searchText.value) {
|
||||
where.keywords = searchText.value;
|
||||
}
|
||||
emit('search', where);
|
||||
};
|
||||
|
||||
// 新增
|
||||
const add = () => {
|
||||
emit('add');
|
||||
};
|
||||
|
||||
// 批量删除
|
||||
const removeBatch = () => {
|
||||
emit('remove');
|
||||
};
|
||||
|
||||
const handleTabs = (e) => {
|
||||
const index = Number(e.target.value);
|
||||
const userStore = useUserStore();
|
||||
const loginUser = computed(() => userStore.info ?? {});
|
||||
plugType.value = index;
|
||||
if (index > 0) {
|
||||
userId.value = Number(loginUser.value.userId);
|
||||
where.status = undefined;
|
||||
where.userId = Number(loginUser.value.userId);
|
||||
}
|
||||
if (index == 0) {
|
||||
where.userId = undefined;
|
||||
where.status = 20;
|
||||
}
|
||||
search();
|
||||
};
|
||||
|
||||
const reload = () => {
|
||||
// 刷新当前路由
|
||||
emit('search', where);
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.selection,
|
||||
() => {}
|
||||
);
|
||||
</script>
|
||||
296
src/views/system/plug/create/index.vue
Normal file
296
src/views/system/plug/create/index.vue
Normal file
@@ -0,0 +1,296 @@
|
||||
<template>
|
||||
<div class="ele-body">
|
||||
<a-card title="插件管理" :bordered="false">
|
||||
<!-- 表格 -->
|
||||
<ele-pro-table
|
||||
ref="tableRef"
|
||||
row-key="plugId"
|
||||
:columns="columns"
|
||||
:datasource="datasource"
|
||||
:expand-icon-column-index="1"
|
||||
:expanded-row-keys="expandedRowKeys"
|
||||
cache-key="proSystemPlugTable"
|
||||
@done="onDone"
|
||||
@expand="onExpand"
|
||||
>
|
||||
<template #toolbar>
|
||||
<PlugSearch
|
||||
@search="reload"
|
||||
:selection="selection"
|
||||
@add="openEdit"
|
||||
@remove="removeBatch"
|
||||
/>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'title'">
|
||||
<div class="app-box">
|
||||
<a-image
|
||||
:height="50"
|
||||
:width="50"
|
||||
:preview="false"
|
||||
:src="record.icon"
|
||||
fallback="https://file.wsdns.cn/20230218/550e610d43334dd2a7f66d5b20bd58eb.svg"
|
||||
/>
|
||||
<!-- <component v-if="record.icon" :is="record.icon" />-->
|
||||
<div class="app-info">
|
||||
<a class="ele-text-heading" @click="openEdit(record)">
|
||||
{{ record.title }}
|
||||
</a>
|
||||
<div class="ele-text-placeholder comments">
|
||||
{{ record.comments }}
|
||||
</div>
|
||||
<a-space size="large" class="ele-text-placeholder">
|
||||
<a
|
||||
class="ele-text-placeholder"
|
||||
:href="`${record.domain}`"
|
||||
target="_blank"
|
||||
>
|
||||
{{ record.companyName }}
|
||||
</a>
|
||||
<span>下载: {{ record.clicks ? record.clicks : 0 }}</span>
|
||||
<span>收藏: {{ record.installs ? record.installs : 0 }}</span>
|
||||
</a-space>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'comments'">
|
||||
<span class="ele-text-secondary">
|
||||
{{ record.comments }}
|
||||
</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'appType'">
|
||||
<span class="ele-text-placeholder" v-if="record.appType === 'web'">
|
||||
网站应用
|
||||
</span>
|
||||
<span
|
||||
class="ele-text-placeholder"
|
||||
v-if="record.appType === 'mp-weixin'"
|
||||
>
|
||||
小程序
|
||||
</span>
|
||||
<span
|
||||
class="ele-text-placeholder"
|
||||
v-if="record.appType === 'h5-weixin'"
|
||||
>
|
||||
公众号
|
||||
</span>
|
||||
<span
|
||||
class="ele-text-placeholder"
|
||||
v-if="record.appType === 'app-plus'"
|
||||
>
|
||||
移动应用
|
||||
</span>
|
||||
<span class="ele-text-placeholder" v-if="record.appType === 'plug'">
|
||||
插件
|
||||
</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'price'">
|
||||
<a class="ele-text-warning">¥{{ record.price }}</a>
|
||||
</template>
|
||||
<template v-if="column.key === 'shortName'">
|
||||
<span class="ele-text-success">
|
||||
{{ record.sortName }}
|
||||
</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'score'">
|
||||
<a>{{ record.score.toFixed(1) }}</a>
|
||||
</template>
|
||||
<template v-if="column.key === 'status'">
|
||||
<a-tag v-if="record.status === 0" color="green">正常</a-tag>
|
||||
<a-tag v-if="record.status === 10" color="orange">待审核</a-tag>
|
||||
<a-tag v-if="record.status === 20" color="green">已通过</a-tag>
|
||||
<a-tag v-if="record.status === 30" color="red">已驳回</a-tag>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a @click="openEdit(record)">管理</a>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</ele-pro-table>
|
||||
</a-card>
|
||||
<!-- 编辑弹窗 -->
|
||||
<PlugEdit
|
||||
v-model:visible="showEdit"
|
||||
:data="current"
|
||||
:parent-id="parentId"
|
||||
:menu-list="menuData"
|
||||
@done="reload"
|
||||
/>
|
||||
<clone v-model:visible="showClone" @done="reload" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue';
|
||||
const { push } = useRouter();
|
||||
import type {
|
||||
DatasourceFunction,
|
||||
ColumnItem
|
||||
// EleProTableDone
|
||||
} from 'ele-admin-pro/es/ele-pro-table/types';
|
||||
import PlugSearch from './components/plug-search.vue';
|
||||
import { toTreeData, toDateString } from 'ele-admin-pro/es';
|
||||
import type { EleProTable } from 'ele-admin-pro/es';
|
||||
import PlugEdit from './components/plug-edit.vue';
|
||||
import Clone from './components/clone.vue';
|
||||
import { pagePlug } from '@/api/system/plug';
|
||||
import type { Plug, PlugParam } from '@/api/system/plug/model';
|
||||
import { Menu } from '@/api/system/menu/model';
|
||||
import { listMenus } from '@/api/system/menu';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
|
||||
// 表格实例
|
||||
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
|
||||
|
||||
// 表格列配置
|
||||
const columns = ref<ColumnItem[]>([
|
||||
{
|
||||
title: '插件ID',
|
||||
dataIndex: 'menuId',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '插件名称',
|
||||
dataIndex: 'title',
|
||||
key: 'title'
|
||||
},
|
||||
{
|
||||
title: '价格',
|
||||
dataIndex: 'price',
|
||||
key: 'price',
|
||||
align: 'center',
|
||||
customRender: ({ text }) => '¥' + text
|
||||
},
|
||||
{
|
||||
title: '评分',
|
||||
dataIndex: 'score',
|
||||
align: 'center',
|
||||
key: 'score'
|
||||
},
|
||||
{
|
||||
title: '审核状态',
|
||||
dataIndex: 'status',
|
||||
align: 'center',
|
||||
key: 'status'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
align: 'center'
|
||||
}
|
||||
]);
|
||||
|
||||
// 当前编辑数据
|
||||
const current = ref<Plug | null>(null);
|
||||
// 是否显示编辑弹窗
|
||||
const showEdit = ref(false);
|
||||
const showClone = ref(false);
|
||||
// 上级菜单id
|
||||
const parentId = ref<number>();
|
||||
// 菜单数据
|
||||
const menuData = ref<Menu[]>([]);
|
||||
const userStore = useUserStore();
|
||||
const loginUser = computed(() => userStore.info ?? {});
|
||||
|
||||
// 表格展开的行
|
||||
const expandedRowKeys = ref<number[]>([]);
|
||||
|
||||
// 表格数据源
|
||||
const datasource: DatasourceFunction = ({ where }) => {
|
||||
where.userId = loginUser.value.userId;
|
||||
return pagePlug({ ...where });
|
||||
};
|
||||
|
||||
/* 表格渲染完成回调 */
|
||||
// const onDone: EleProTableDone<Plug> = ({ data }) => {
|
||||
// menuData.value = data;
|
||||
// };
|
||||
|
||||
/* 刷新表格 */
|
||||
const reload = (where?: PlugParam) => {
|
||||
tableRef?.value?.reload({ where });
|
||||
};
|
||||
|
||||
/* 打开编辑弹窗 */
|
||||
const openEdit = (row?: Plug | null, id?: number) => {
|
||||
console.log(row);
|
||||
current.value = row ?? null;
|
||||
parentId.value = row?.menuId;
|
||||
showEdit.value = true;
|
||||
};
|
||||
|
||||
/* 一键克隆 */
|
||||
const clonePlug = (row?: Plug | null, id?: number) => {
|
||||
current.value = row ?? null;
|
||||
parentId.value = id;
|
||||
showClone.value = true;
|
||||
};
|
||||
|
||||
const query = () => {
|
||||
listMenus({}).then((res) => {
|
||||
if (res) {
|
||||
menuData.value = parseData(res);
|
||||
}
|
||||
});
|
||||
};
|
||||
/* 数据转为树形结构 */
|
||||
const parseData = (data: Menu[]) => {
|
||||
return toTreeData({
|
||||
data: data
|
||||
.filter((d) => d.menuType == 0)
|
||||
.map((d) => {
|
||||
if (d.parentId != 0) {
|
||||
// d.disabled = true;
|
||||
}
|
||||
return { ...d, key: d.menuId, value: d.menuId };
|
||||
}),
|
||||
idField: 'menuId',
|
||||
parentIdField: 'parentId'
|
||||
});
|
||||
};
|
||||
query();
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import * as PlugIcons from '@/layout/menu-icons';
|
||||
|
||||
export default {
|
||||
name: 'SystemPlug',
|
||||
components: PlugIcons
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.app-box {
|
||||
display: flex;
|
||||
.app-info {
|
||||
display: flex;
|
||||
margin-left: 6px;
|
||||
margin-right: 6px;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
.cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
.sys-org-table :deep(.ant-table-body) {
|
||||
overflow: auto !important;
|
||||
overflow: overlay !important;
|
||||
}
|
||||
|
||||
.sys-org-table :deep(.ant-table-pagination.ant-pagination) {
|
||||
padding: 0 4px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.ele-text-heading {
|
||||
}
|
||||
.comments {
|
||||
width: 420px;
|
||||
padding: 5px 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
276
src/views/system/plug/index.vue
Normal file
276
src/views/system/plug/index.vue
Normal file
@@ -0,0 +1,276 @@
|
||||
<template>
|
||||
<div class="ele-body">
|
||||
<a-card title="扩展插件" :bordered="false">
|
||||
<template #extra>
|
||||
<a-button class="ele-btn-icon" @click="push('/system/plug/create')">
|
||||
<span>发布插件</span>
|
||||
</a-button>
|
||||
</template>
|
||||
<!-- 表格 -->
|
||||
<ele-pro-table
|
||||
ref="tableRef"
|
||||
row-key="plugId"
|
||||
:columns="columns"
|
||||
:datasource="datasource"
|
||||
:expand-icon-column-index="1"
|
||||
:expanded-row-keys="expandedRowKeys"
|
||||
cache-key="proSystemPlugTable"
|
||||
@done="onDone"
|
||||
@expand="onExpand"
|
||||
>
|
||||
<template #toolbar>
|
||||
<PlugSearch @search="reload" :selection="selection" @add="openEdit" />
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'title'">
|
||||
<div class="app-box">
|
||||
<a-image
|
||||
:height="50"
|
||||
:width="50"
|
||||
:preview="false"
|
||||
:src="record.icon"
|
||||
fallback="https://file.wsdns.cn/20230218/550e610d43334dd2a7f66d5b20bd58eb.svg"
|
||||
/>
|
||||
<!-- <component v-if="record.icon" :is="record.icon" />-->
|
||||
<div class="app-info">
|
||||
<a class="ele-text-heading" @click="openEdit(record)">
|
||||
{{ record.title }}
|
||||
</a>
|
||||
<div class="ele-text-placeholder comments">
|
||||
{{ record.comments }}
|
||||
</div>
|
||||
<a-space :size="50" class="ele-text-placeholder">
|
||||
<span>
|
||||
{{ record.companyName }}
|
||||
</span>
|
||||
<span>赞赏: {{ record.clicks ? record.clicks : 0 }}</span>
|
||||
<span>安装: {{ record.installs ? record.installs : 0 }}</span>
|
||||
</a-space>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'comments'">
|
||||
<span class="ele-text-secondary">
|
||||
{{ record.comments }}
|
||||
</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'appType'">
|
||||
<span class="ele-text-placeholder" v-if="record.appType === 'web'">
|
||||
网站应用
|
||||
</span>
|
||||
<span
|
||||
class="ele-text-placeholder"
|
||||
v-if="record.appType === 'mp-weixin'"
|
||||
>
|
||||
小程序
|
||||
</span>
|
||||
<span
|
||||
class="ele-text-placeholder"
|
||||
v-if="record.appType === 'h5-weixin'"
|
||||
>
|
||||
公众号
|
||||
</span>
|
||||
<span
|
||||
class="ele-text-placeholder"
|
||||
v-if="record.appType === 'app-plus'"
|
||||
>
|
||||
移动应用
|
||||
</span>
|
||||
<span class="ele-text-placeholder" v-if="record.appType === 'plug'">
|
||||
插件
|
||||
</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'shortName'">
|
||||
<span class="ele-text-placeholder">
|
||||
{{ record.shortName }}
|
||||
</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'price'">
|
||||
<span class="ele-text-success" v-if="record.price === 0">免费</span>
|
||||
<span class="ele-text-warning" v-else>¥{{ record.price }}</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'score'">
|
||||
<a>{{ record.score.toFixed(1) }}</a>
|
||||
</template>
|
||||
<template v-if="column.key === 'status'">
|
||||
<a-tag v-if="record.status === 0" color="green">正常</a-tag>
|
||||
<a-tag v-if="record.status === 10" color="orange">待审核</a-tag>
|
||||
<a-tag v-if="record.status === 20" color="green">已通过</a-tag>
|
||||
<a-tag v-if="record.status === 30" color="red">已驳回</a-tag>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a @click="openEdit(record)">安装</a>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</ele-pro-table>
|
||||
</a-card>
|
||||
<!-- 编辑弹窗 -->
|
||||
<PlugEdit
|
||||
v-model:visible="showEdit"
|
||||
:data="current"
|
||||
:parent-id="parentId"
|
||||
:menu-list="menuData"
|
||||
@done="reload"
|
||||
/>
|
||||
<clone v-model:visible="showClone" @done="reload" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
const { push } = useRouter();
|
||||
import type {
|
||||
DatasourceFunction,
|
||||
ColumnItem
|
||||
} from 'ele-admin-pro/es/ele-pro-table/types';
|
||||
import PlugSearch from './components/plug-search.vue';
|
||||
import { toTreeData } from 'ele-admin-pro/es';
|
||||
import type { EleProTable } from 'ele-admin-pro/es';
|
||||
import PlugEdit from './components/plug-edit.vue';
|
||||
import Clone from './components/clone.vue';
|
||||
import { pagePlug } from '@/api/system/plug';
|
||||
import type { Plug, PlugParam } from '@/api/system/plug/model';
|
||||
import { Menu } from '@/api/system/menu/model';
|
||||
import { listMenus } from '@/api/system/menu';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
// 表格实例
|
||||
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
|
||||
|
||||
// 表格列配置
|
||||
const columns = ref<ColumnItem[]>([
|
||||
{
|
||||
title: '插件ID',
|
||||
dataIndex: 'menuId',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '插件名称',
|
||||
dataIndex: 'title',
|
||||
key: 'title'
|
||||
},
|
||||
{
|
||||
title: '价格',
|
||||
dataIndex: 'price',
|
||||
key: 'price',
|
||||
align: 'center',
|
||||
customRender: ({ text }) => '¥' + text
|
||||
},
|
||||
{
|
||||
title: '评分',
|
||||
dataIndex: 'score',
|
||||
align: 'center',
|
||||
key: 'score'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
align: 'center'
|
||||
}
|
||||
]);
|
||||
|
||||
// 当前编辑数据
|
||||
const current = ref<Plug | null>(null);
|
||||
|
||||
// 是否显示编辑弹窗
|
||||
const showEdit = ref(false);
|
||||
|
||||
const showClone = ref(false);
|
||||
|
||||
// 上级菜单id
|
||||
const parentId = ref<number>();
|
||||
|
||||
// 菜单数据
|
||||
const menuData = ref<Menu[]>([]);
|
||||
|
||||
// 表格展开的行
|
||||
const expandedRowKeys = ref<number[]>([]);
|
||||
|
||||
// 表格数据源
|
||||
const datasource: DatasourceFunction = ({ where }) => {
|
||||
where.status = 20;
|
||||
return pagePlug({ ...where });
|
||||
};
|
||||
|
||||
/* 表格渲染完成回调 */
|
||||
// const onDone: EleProTableDone<Plug> = ({ data }) => {
|
||||
// menuData.value = data;
|
||||
// };
|
||||
|
||||
/* 刷新表格 */
|
||||
const reload = (where?: PlugParam) => {
|
||||
tableRef?.value?.reload({ where });
|
||||
};
|
||||
|
||||
/* 打开编辑弹窗 */
|
||||
const openEdit = (row?: Plug | null, id?: number) => {
|
||||
current.value = row ?? null;
|
||||
parentId.value = id;
|
||||
showEdit.value = true;
|
||||
};
|
||||
|
||||
const query = () => {
|
||||
listMenus({}).then((res) => {
|
||||
if (res) {
|
||||
menuData.value = parseData(res);
|
||||
}
|
||||
});
|
||||
};
|
||||
/* 数据转为树形结构 */
|
||||
const parseData = (data: Menu[]) => {
|
||||
return toTreeData({
|
||||
data: data
|
||||
.filter((d) => d.menuType == 0)
|
||||
.map((d) => {
|
||||
return { ...d, key: d.menuId, value: d.menuId };
|
||||
}),
|
||||
idField: 'menuId',
|
||||
parentIdField: 'parentId'
|
||||
});
|
||||
};
|
||||
query();
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import * as PlugIcons from '@/layout/menu-icons';
|
||||
|
||||
export default {
|
||||
name: 'SystemPlug',
|
||||
components: PlugIcons
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.app-box {
|
||||
display: flex;
|
||||
.app-info {
|
||||
display: flex;
|
||||
margin-left: 6px;
|
||||
margin-right: 6px;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
.cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
.sys-org-table :deep(.ant-table-body) {
|
||||
overflow: auto !important;
|
||||
overflow: overlay !important;
|
||||
}
|
||||
|
||||
.sys-org-table :deep(.ant-table-pagination.ant-pagination) {
|
||||
padding: 0 4px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.ele-text-heading {
|
||||
}
|
||||
.comments {
|
||||
width: 420px;
|
||||
padding: 5px 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user