Initial commit
This commit is contained in:
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/oa/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>
|
||||
213
src/views/system/plug/components/plug.vue
Normal file
213
src/views/system/plug/components/plug.vue
Normal file
@@ -0,0 +1,213 @@
|
||||
<template>
|
||||
<a-card
|
||||
style="background-color: transparent"
|
||||
:body-style="{ padding: '0px' }"
|
||||
>
|
||||
<div class="card-title">
|
||||
<a-typography-title :level="3">应用模块</a-typography-title>
|
||||
</div>
|
||||
<a-row :gutter="16">
|
||||
<a-col
|
||||
v-bind="
|
||||
styleResponsive
|
||||
? { xl: 6, lg: 6, md: 6, sm: 24, xs: 24 }
|
||||
: { span: 12 }
|
||||
"
|
||||
class="gutter-row"
|
||||
v-for="(item, index) in list"
|
||||
:key="index"
|
||||
:span="6"
|
||||
>
|
||||
<a-card class="gutter-box" hoverable>
|
||||
<div class="plug-item">
|
||||
<a-image
|
||||
:height="80"
|
||||
:width="80"
|
||||
:preview="false"
|
||||
:src="item.companyLogo"
|
||||
@click="openUrl('/system/plug/detail?id=' + item.companyId)"
|
||||
fallback="https://file.wsdns.cn/20230218/550e610d43334dd2a7f66d5b20bd58eb.svg"
|
||||
/>
|
||||
<div class="info">
|
||||
<a
|
||||
class="name ele-text-heading"
|
||||
@click="openUrl('/system/plug/detail?id=' + item.companyId)"
|
||||
>{{ item.tenantName }}</a
|
||||
>
|
||||
<a-rate class="rate" v-model:value="rate" disabled allow-half />
|
||||
<div class="company ele-text-placeholder">
|
||||
<a-typography-text
|
||||
type="secondary"
|
||||
:ellipsis="{ rows: 1, expandable: true, symbol: '...' }"
|
||||
>
|
||||
{{ item.companyName }}
|
||||
</a-typography-text>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="plug-desc ele-text-secondary">
|
||||
<a-typography-paragraph
|
||||
type="secondary"
|
||||
:ellipsis="{ rows: 2, expandable: true, symbol: '显示' }"
|
||||
:content="item.comments"
|
||||
/>
|
||||
</div>
|
||||
<div class="plug-bottom">
|
||||
<div class="downloads ele-text-placeholder"
|
||||
>安装 {{ item.clicks }}</div
|
||||
>
|
||||
<a-button type="primary" disabled v-if="planId === item.tenantId"
|
||||
>已安装</a-button
|
||||
>
|
||||
<a-button v-else type="primary" @click="onClone(item)"
|
||||
>安装</a-button
|
||||
>
|
||||
</div>
|
||||
</a-card>
|
||||
<!-- <a-card class="gutter-box" hoverable>-->
|
||||
<!-- <div class="flex-justify">-->
|
||||
<!-- <div class="plug-item">-->
|
||||
<!-- <a-image-->
|
||||
<!-- :height="80"-->
|
||||
<!-- :width="80"-->
|
||||
<!-- :preview="false"-->
|
||||
<!-- :src="item.companyLogo"-->
|
||||
<!-- fallback="https://file.wsdns.cn/20230218/550e610d43334dd2a7f66d5b20bd58eb.svg"-->
|
||||
<!-- />-->
|
||||
<!-- <div class="info">-->
|
||||
<!-- <span class="name ele-text-heading">{{ item.tenantName }}</span>-->
|
||||
<!-- <a-rate class="rate" v-model:value="rate" disabled allow-half />-->
|
||||
<!-- <div class="company ele-text-placeholder">-->
|
||||
<!-- <a-typography-text-->
|
||||
<!-- type="secondary"-->
|
||||
<!-- :ellipsis="{ rows: 1, expandable: true, symbol: '..' }"-->
|
||||
<!-- >-->
|
||||
<!-- {{ item.companyName }}-->
|
||||
<!-- </a-typography-text>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="plug-desc ele-text-secondary">-->
|
||||
<!-- <a-typography-paragraph-->
|
||||
<!-- type="secondary"-->
|
||||
<!-- :ellipsis="{ rows: 2, expandable: true, symbol: '显示' }"-->
|
||||
<!-- :content="item.comments"-->
|
||||
<!-- />-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="plug-desc ele-text-secondary">-->
|
||||
<!-- <a-button-->
|
||||
<!-- @click="openUrl('/system/plug/detail?id=' + item.companyId)"-->
|
||||
<!-- >去开通</a-button-->
|
||||
<!-- >-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </a-card>-->
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { openUrl } from '@/utils/common';
|
||||
import { onClone } from '@/utils/plug-uitl';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
import { Company, CompanyParam } from '@/api/system/company/model';
|
||||
import useSearch from '@/utils/use-search';
|
||||
import { message } from 'ant-design-vue/es';
|
||||
import { pageCompanyAll } from '@/api/system/company';
|
||||
|
||||
const props = defineProps<{
|
||||
// 修改回显的数据
|
||||
use: boolean;
|
||||
}>();
|
||||
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
const { styleResponsive } = storeToRefs(themeStore);
|
||||
|
||||
const searchText = ref('');
|
||||
const rate = ref(3.5);
|
||||
const list = ref<Company[]>([]);
|
||||
|
||||
// 查询条件
|
||||
const { where, resetFields } = useSearch<CompanyParam>({
|
||||
keywords: undefined,
|
||||
limit: 4,
|
||||
recommend: true,
|
||||
authoritative: 1
|
||||
});
|
||||
|
||||
const reload = () => {
|
||||
resetFields();
|
||||
if (searchText.value) {
|
||||
where.recommend = undefined;
|
||||
where.keywords = searchText.value;
|
||||
}
|
||||
if (props.use) {
|
||||
where.recommend = false;
|
||||
}
|
||||
where.sort = 'buys';
|
||||
where.order = 'desc';
|
||||
|
||||
const hide = message.loading('加载中...');
|
||||
pageCompanyAll(where)
|
||||
.then((data) => {
|
||||
if (data?.list) {
|
||||
list.value = data?.list;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
hide();
|
||||
});
|
||||
};
|
||||
reload();
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.ele-body-card {
|
||||
background-color: transparent;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.gutter-row {
|
||||
margin: 0 auto 30px auto;
|
||||
.gutter-box {
|
||||
.plug-item {
|
||||
display: flex;
|
||||
.info {
|
||||
font-size: 14px;
|
||||
margin-left: 6px;
|
||||
.name {
|
||||
font-size: 20px;
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
-webkit-box-orient: vertical;
|
||||
text-overflow: ellipsis;
|
||||
-webkit-line-clamp: 2;
|
||||
font-weight: 500;
|
||||
}
|
||||
.rate {
|
||||
font-size: 13px;
|
||||
}
|
||||
.company {
|
||||
}
|
||||
}
|
||||
}
|
||||
.plug-desc {
|
||||
padding: 10px 0;
|
||||
font-size: 16px;
|
||||
min-height: 88px;
|
||||
}
|
||||
.plug-bottom {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
254
src/views/system/plug/components/tenant.vue
Normal file
254
src/views/system/plug/components/tenant.vue
Normal file
@@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<a-card
|
||||
style="background-color: transparent"
|
||||
:bordered="false"
|
||||
:body-style="{ padding: '0px' }"
|
||||
>
|
||||
<a-alert
|
||||
message="欢迎使用"
|
||||
description="请选择需要安装的镜像"
|
||||
type="success"
|
||||
show-icon
|
||||
closable
|
||||
/>
|
||||
<div style="margin: 30px auto; display: flex; justify-content: center">
|
||||
<a-space style="flex-wrap: wrap">
|
||||
<industry-select
|
||||
v-model:value="industry"
|
||||
valueField="label"
|
||||
size="large"
|
||||
placeholder="按行业筛选"
|
||||
class="ele-fluid"
|
||||
@change="onIndustry"
|
||||
/>
|
||||
<a-input-search
|
||||
allow-clear
|
||||
size="large"
|
||||
style="width: 360px"
|
||||
placeholder="请输入搜索关键词"
|
||||
v-model:value="where.keywords"
|
||||
@pressEnter="reload"
|
||||
@search="reload"
|
||||
/>
|
||||
<a-button size="large" @click="reset">重置</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
<a-tabs v-model:active-key="where.sceneType" @change="onTabs">
|
||||
<a-tab-pane tab="热门推荐" key="recommend" />
|
||||
<a-tab-pane tab="免费热榜" key="free" />
|
||||
<a-tab-pane tab="付费热榜" key="pay" />
|
||||
<a-tab-pane tab="最新上架" key="new" />
|
||||
<a-tab-pane tab="我的收藏" key="collect" />
|
||||
</a-tabs>
|
||||
<a-row :gutter="16">
|
||||
<a-col
|
||||
v-bind="
|
||||
styleResponsive
|
||||
? { xl: 6, lg: 6, md: 6, sm: 24, xs: 24 }
|
||||
: { span: 12 }
|
||||
"
|
||||
class="gutter-row"
|
||||
v-for="(item, index) in list"
|
||||
:key="index"
|
||||
:span="6"
|
||||
>
|
||||
<a-card hoverable>
|
||||
<div class="gutter-box">
|
||||
<div class="plug-item">
|
||||
<a-image
|
||||
:height="80"
|
||||
:width="80"
|
||||
:preview="false"
|
||||
:src="item.companyLogo"
|
||||
@click="openUrl('/system/plug/detail?id=' + item.companyId)"
|
||||
fallback="https://file.wsdns.cn/20230218/550e610d43334dd2a7f66d5b20bd58eb.svg"
|
||||
/>
|
||||
<div class="info">
|
||||
<a
|
||||
class="name ele-text-heading"
|
||||
@click="openUrl('/system/plug/detail?id=' + item.companyId)"
|
||||
>{{ item.tenantName }}</a
|
||||
>
|
||||
<a-rate class="rate" v-model:value="rate" disabled allow-half />
|
||||
<div class="company ele-text-placeholder">
|
||||
<a-typography-text
|
||||
type="secondary"
|
||||
:ellipsis="{ rows: 1, expandable: true, symbol: '...' }"
|
||||
>
|
||||
{{ item.companyName }}
|
||||
</a-typography-text>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="plug-desc ele-text-secondary">
|
||||
<a-typography-paragraph
|
||||
type="secondary"
|
||||
:ellipsis="{ rows: 2, expandable: true, symbol: '显示' }"
|
||||
:content="item.comments"
|
||||
/>
|
||||
</div>
|
||||
<div class="plug-bottom">
|
||||
<div class="downloads ele-text-placeholder"
|
||||
>安装 {{ item.clicks }}</div
|
||||
>
|
||||
<a-button type="primary" disabled v-if="planId === item.tenantId"
|
||||
>已安装</a-button
|
||||
>
|
||||
<a-button v-else type="primary" @click="onClone(item)"
|
||||
>安装</a-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<div class="plug-page" v-if="list.length">
|
||||
<a-pagination
|
||||
v-model:current="where.page"
|
||||
v-model:pageSize="where.limit"
|
||||
size="large"
|
||||
:total="total"
|
||||
@change="reload"
|
||||
/>
|
||||
</div>
|
||||
<a-empty v-else />
|
||||
</a-card>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { openUrl } from '@/utils/common';
|
||||
import { onClone } from '@/utils/plug-uitl';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
import { Company, CompanyParam } from '@/api/system/company/model';
|
||||
import useSearch from '@/utils/use-search';
|
||||
import { message } from 'ant-design-vue/es';
|
||||
import { pageCompanyAll } from '@/api/system/company';
|
||||
|
||||
const props = defineProps<{
|
||||
// 修改回显的数据
|
||||
use: boolean;
|
||||
}>();
|
||||
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
const { styleResponsive } = storeToRefs(themeStore);
|
||||
|
||||
const searchText = ref('');
|
||||
const rate = ref(3.5);
|
||||
const list = ref<Company[]>([]);
|
||||
const industry = ref<any>();
|
||||
const total = ref<any>(0);
|
||||
const planId = ref<number>(Number(localStorage.getItem('PlanId')));
|
||||
|
||||
// 查询条件
|
||||
const { where, resetFields } = useSearch<CompanyParam>({
|
||||
keywords: undefined,
|
||||
industryParent: '',
|
||||
industryChild: '',
|
||||
recommend: undefined,
|
||||
authoritative: 1,
|
||||
sceneType: 'recommend',
|
||||
limit: 20,
|
||||
page: 1
|
||||
});
|
||||
|
||||
const onIndustry = (item: any) => {
|
||||
where.industryChild = item[1];
|
||||
};
|
||||
|
||||
const onTabs = (index) => {
|
||||
if (index == 'recommend') {
|
||||
where.recommend = true;
|
||||
}
|
||||
if (index == 'free') {
|
||||
where.recommend = false;
|
||||
}
|
||||
if (index == 'pay') {
|
||||
where.recommend = false;
|
||||
}
|
||||
if (index == 'new') {
|
||||
where.sceneType = 'new';
|
||||
}
|
||||
if (index == 'collect') {
|
||||
where.sceneType = 'collect';
|
||||
}
|
||||
reload();
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
resetFields();
|
||||
reload();
|
||||
};
|
||||
|
||||
const reload = () => {
|
||||
where.sort = 'buys';
|
||||
where.order = 'desc';
|
||||
pageCompanyAll(where).then((data) => {
|
||||
total.value = data?.count;
|
||||
if (data?.list) {
|
||||
list.value = data?.list;
|
||||
}
|
||||
});
|
||||
};
|
||||
reload();
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.ele-body-card {
|
||||
background-color: transparent;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.gutter-row {
|
||||
margin: 0 auto 30px auto;
|
||||
.gutter-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
min-height: 200px;
|
||||
.plug-item {
|
||||
display: flex;
|
||||
.info {
|
||||
font-size: 14px;
|
||||
margin-left: 6px;
|
||||
.name {
|
||||
font-size: 20px;
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
-webkit-box-orient: vertical;
|
||||
text-overflow: ellipsis;
|
||||
-webkit-line-clamp: 2;
|
||||
font-weight: 500;
|
||||
}
|
||||
.rate {
|
||||
font-size: 13px;
|
||||
}
|
||||
.company {
|
||||
}
|
||||
}
|
||||
}
|
||||
.plug-desc {
|
||||
padding: 10px 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
.plug-bottom {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
}
|
||||
.ele-text-heading {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.plug-page {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
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>
|
||||
261
src/views/system/plug/create/components/plug-edit.vue
Normal file
261
src/views/system/plug/create/components/plug-edit.vue
Normal file
@@ -0,0 +1,261 @@
|
||||
<!-- 编辑弹窗 -->
|
||||
<template>
|
||||
<ele-modal
|
||||
:width="740"
|
||||
:visible="visible"
|
||||
:confirm-loading="loading"
|
||||
:title="isUpdate ? '插件管理' : '发布插件'"
|
||||
:body-style="{ paddingBottom: '8px' }"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save"
|
||||
>
|
||||
<a-alert
|
||||
:description="`审核通过后,插件将展示在插件市场,可供其他用户安装和使用后,获取销售分成。`"
|
||||
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>
|
||||
400
src/views/system/plug/detail/index.vue
Normal file
400
src/views/system/plug/detail/index.vue
Normal file
@@ -0,0 +1,400 @@
|
||||
<template>
|
||||
<a-page-header :title="form.shortName" @back="push('/system/plug')">
|
||||
<a-row :gutter="16">
|
||||
<!-- 左侧区域-->
|
||||
<a-col
|
||||
v-bind="
|
||||
styleResponsive
|
||||
? { xl: 18, lg: 12, md: 12, sm: 24, xs: 24 }
|
||||
: { span: 12 }
|
||||
"
|
||||
class="gutter-row"
|
||||
:span="6"
|
||||
>
|
||||
<a-card :bordered="false" style="width: 100%; margin-bottom: 16px">
|
||||
<div class="goods-info">
|
||||
<div class="logo">
|
||||
<a-image
|
||||
:width="70"
|
||||
:height="70"
|
||||
:preview="false"
|
||||
:src="form.companyLogo"
|
||||
/>
|
||||
</div>
|
||||
<div class="info">
|
||||
<a-card :bordered="false" :body-style="{ padding: 0 }">
|
||||
<div class="goods-name ele-text-heading">{{
|
||||
form.shortName
|
||||
}}</div>
|
||||
<div class="comments ele-text-secondary">{{
|
||||
form.comments
|
||||
}}</div>
|
||||
</a-card>
|
||||
<a-card :bordered="false" class="buy-card">
|
||||
<div class="price-box">
|
||||
<div class="left">
|
||||
<div
|
||||
><span class="ele-text-secondary">价格:</span
|
||||
><span class="ele-text-danger price">¥50</span>/月</div
|
||||
>
|
||||
<div
|
||||
><span class="ele-text-secondary">续费:</span
|
||||
><span class="ele-text-heading">¥1280</span></div
|
||||
>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="sales ele-text-secondary">浏览 35</div>
|
||||
<div class="sales ele-text-secondary">评价 3</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</div>
|
||||
</div>
|
||||
<div class="goods-item">
|
||||
<div class="title"> 套餐版本 </div>
|
||||
<div class="info">
|
||||
<a-radio-group v-model:value="plan" size="large">
|
||||
<a-radio-button value="1">体验版(3用户)</a-radio-button>
|
||||
<a-radio-button value="2">基础版(10用户)</a-radio-button>
|
||||
<a-radio-button value="3">标准版(50用户)</a-radio-button>
|
||||
<a-radio-button value="4">豪华版(100用户)</a-radio-button>
|
||||
<a-radio-button value="5">旗舰版(不限)</a-radio-button>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="goods-item">
|
||||
<div class="title"> 购买时长 </div>
|
||||
<div class="info">
|
||||
<a-radio-group v-model:value="duration" size="large">
|
||||
<a-radio-button value="1">1个月</a-radio-button>
|
||||
<a-radio-button value="2">1年</a-radio-button>
|
||||
<a-radio-button value="3">2年</a-radio-button>
|
||||
<a-radio-button value="4">3年</a-radio-button>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="goods-item">
|
||||
<div class="title"></div>
|
||||
<div class="info">
|
||||
<a-button type="primary" size="large">立即购买</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
<a-card
|
||||
:bordered="false"
|
||||
:body-style="{ padding: 0 }"
|
||||
style="margin-bottom: 16px"
|
||||
>
|
||||
<div class="guarantee">
|
||||
<span class="text ele-text-danger"
|
||||
>服务保障:平台购买支持5天无理由退款,请勿线下支付!</span
|
||||
>
|
||||
</div>
|
||||
</a-card>
|
||||
<a-card :body-style="{ padding: '0 16px' }">
|
||||
<a-tabs v-model:active-key="active">
|
||||
<a-tab-pane tab="商品详情" key="detail">
|
||||
<Tenant :use="false" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane tab="商品价格" key="price">
|
||||
<Tenant :use="true" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane tab="客户案例" key="case">
|
||||
<Tenant :use="true" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane tab="使用指南" key="guide">
|
||||
<Tenant :use="true" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane tab="用户评论(8)" key="comments">
|
||||
<Tenant :use="true" />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<!-- 右侧区域 -->
|
||||
<a-col
|
||||
v-bind="
|
||||
styleResponsive
|
||||
? { xl: 6, lg: 12, md: 12, sm: 24, xs: 24 }
|
||||
: { span: 12 }
|
||||
"
|
||||
class="gutter-row"
|
||||
:span="6"
|
||||
>
|
||||
<a-card :bordered="false" class="task-card">
|
||||
<a-list :bordered="false">
|
||||
<a-list-item style="font-weight: 500">
|
||||
服务厂商:{{ form.companyName }}
|
||||
</a-list-item>
|
||||
<a-list-item @click="openUrl('http://www.' + form.domain)">
|
||||
官方网站:{{ form.domain }}
|
||||
</a-list-item>
|
||||
<a-list-item>
|
||||
联系客服:<QqOutlined :style="{ fontSize: '18px' }" /><span
|
||||
class="ele-text-secondary"
|
||||
>
|
||||
(在线时间:9:00到6:00)</span
|
||||
>
|
||||
</a-list-item>
|
||||
<a-list-item> 电话:0771-5386339 </a-list-item>
|
||||
<a-list-item> 邮箱:{{ form.email }} </a-list-item>
|
||||
<a-list-item>
|
||||
问题处理:<a-button @click="openUrl('/oa/task/add')"
|
||||
>提交工单</a-button
|
||||
></a-list-item
|
||||
>
|
||||
</a-list>
|
||||
</a-card>
|
||||
<a-card
|
||||
title="建议搭配应用"
|
||||
:bordered="false"
|
||||
class="task-card"
|
||||
:split="false"
|
||||
:body-style="{ padding: '5px' }"
|
||||
>
|
||||
<a-row :gutter="16">
|
||||
<a-col class="gutter-row" :span="12">
|
||||
<a-button
|
||||
type="link"
|
||||
class="ele-text-secondary"
|
||||
@click="openUrl('https://3x.antdv.com/components/overview-cn')"
|
||||
>Ant Design Vue</a-button
|
||||
>
|
||||
</a-col>
|
||||
<a-col class="gutter-row" :span="12">
|
||||
<a-button
|
||||
type="link"
|
||||
class="ele-text-secondary"
|
||||
@click="openUrl('https://eleadmin.com/doc/eleadminpro/')"
|
||||
>EleAdmin Pro</a-button
|
||||
>
|
||||
</a-col>
|
||||
<a-col class="gutter-row" :span="12">
|
||||
<a-button
|
||||
type="link"
|
||||
class="ele-text-secondary"
|
||||
@click="openUrl('https://eleadmin.com/doc/oauth2/')"
|
||||
>后端教程Java</a-button
|
||||
>
|
||||
</a-col>
|
||||
<a-col class="gutter-row" :span="12">
|
||||
<a-button
|
||||
type="link"
|
||||
class="ele-text-secondary"
|
||||
@click="openUrl('/system/plug')"
|
||||
>插件扩展</a-button
|
||||
>
|
||||
</a-col>
|
||||
<a-col class="gutter-row" :span="12">
|
||||
<a-button
|
||||
type="link"
|
||||
class="ele-text-secondary"
|
||||
@click="openUrl('http://git.gxwebsoft.com')"
|
||||
>git仓库</a-button
|
||||
>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-page-header>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, unref, watch } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { getFileSize, openUrl } from '@/utils/common';
|
||||
import { setPageTabTitle } from '@/utils/page-tab-util';
|
||||
import useFormData from '@/utils/use-form-data';
|
||||
import { getCompanyAll } from '@/api/system/company';
|
||||
import { Company } from '@/api/system/company/model';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { onClone } from '@/utils/plug-uitl';
|
||||
const { push } = useRouter();
|
||||
const ROUTE_PATH = '/system/plug/detail';
|
||||
import { QqOutlined } from '@ant-design/icons-vue';
|
||||
import Tenant from '@/views/system/plug/components/tenant.vue';
|
||||
import Plug from '@/views/system/plug/components/plug.vue';
|
||||
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
const { styleResponsive } = storeToRefs(themeStore);
|
||||
|
||||
const { currentRoute } = useRouter();
|
||||
const logo = ref<any>([]);
|
||||
const active = ref('detail');
|
||||
const duration = ref<any>('1');
|
||||
const plan = ref<any>('1');
|
||||
const top = ref<number>(10);
|
||||
|
||||
// 用户信息
|
||||
const { form, assignFields } = useFormData<Company>({
|
||||
companyId: undefined,
|
||||
companyName: undefined,
|
||||
companyLogo: undefined,
|
||||
shortName: undefined,
|
||||
domain: undefined,
|
||||
email: undefined,
|
||||
tenantId: undefined,
|
||||
tenantName: '',
|
||||
comments: '',
|
||||
version: undefined,
|
||||
createTime: undefined
|
||||
});
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive({
|
||||
taskType: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择工单类型',
|
||||
type: 'string',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
content: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请填写问题描述',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
sortNumber: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入排序号',
|
||||
type: 'number',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// 查询租户信息
|
||||
const query = () => {
|
||||
const { query } = unref(currentRoute);
|
||||
const id = query.id;
|
||||
if (id) {
|
||||
getCompanyAll(Number(id)).then((data) => {
|
||||
assignFields({
|
||||
...data
|
||||
});
|
||||
// 修改当前页签标题
|
||||
setPageTabTitle(`${form.tenantName}`);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
currentRoute,
|
||||
(route) => {
|
||||
const { path } = unref(route);
|
||||
if (path !== ROUTE_PATH) {
|
||||
return;
|
||||
}
|
||||
query();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'SystemPlugDetail'
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.goods-info {
|
||||
max-width: 80%;
|
||||
display: flex;
|
||||
.logo {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-right: 20px;
|
||||
width: 120px;
|
||||
}
|
||||
.info {
|
||||
.goods-name {
|
||||
font-size: 22px;
|
||||
}
|
||||
.comments {
|
||||
margin: 5px 0;
|
||||
}
|
||||
.buy-card {
|
||||
margin-top: 20px;
|
||||
width: 700px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-color: #f3faee;
|
||||
border-radius: 1px;
|
||||
.price-box {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.price {
|
||||
font-size: 28px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.goods-item {
|
||||
max-width: 80%;
|
||||
margin: 20px 0;
|
||||
display: flex;
|
||||
.title {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding-right: 20px;
|
||||
width: 120px;
|
||||
}
|
||||
}
|
||||
.task-card {
|
||||
padding: 2px !important;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.user-content {
|
||||
max-width: 100%;
|
||||
border-radius: 8px !important;
|
||||
background-color: #a2ec71;
|
||||
border: none;
|
||||
}
|
||||
.admin-content {
|
||||
border-radius: 8px !important;
|
||||
border: 3px solid #f1f1f1;
|
||||
}
|
||||
/deep/.markdown-body {
|
||||
background-color: transparent; /* 设置背景透明 */
|
||||
}
|
||||
/deep/.markdown-body img {
|
||||
}
|
||||
.files {
|
||||
margin-top: 10px;
|
||||
}
|
||||
#bottom {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.transparent-bg {
|
||||
background-color: transparent; /* 设置背景透明 */
|
||||
}
|
||||
.item-name {
|
||||
font-size: 14px;
|
||||
}
|
||||
.guarantee {
|
||||
height: 82px;
|
||||
background: url('https://oss.wsdns.cn/20231101/220819e37b2546b493f90a6de6e762f9.png?x-oss-process=image/resize,w_750/quality,Q_90');
|
||||
background-repeat: no-repeat;
|
||||
background-size: 80%;
|
||||
background-color: #fbf0e5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.text {
|
||||
padding: 0 180px;
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
33
src/views/system/plug/index.vue
Normal file
33
src/views/system/plug/index.vue
Normal file
@@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<div class="">
|
||||
<a-card
|
||||
:bordered="false"
|
||||
:style="{ backgroundColor: 'transparent' }"
|
||||
:body-style="{ padding: '16px' }"
|
||||
>
|
||||
<Tenant :use="false" />
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import Tenant from './components/tenant.vue';
|
||||
// import Plug from './components/plug.vue';
|
||||
// const active = ref('list');
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import * as PlugIcons from '@/layout/menu-icons';
|
||||
|
||||
export default {
|
||||
name: 'SystemPlug',
|
||||
components: PlugIcons
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.ele-body-card {
|
||||
background-color: transparent;
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
253
src/views/system/plug/list/index.vue
Normal file
253
src/views/system/plug/list/index.vue
Normal file
@@ -0,0 +1,253 @@
|
||||
<template>
|
||||
<a-page-header
|
||||
:title="title"
|
||||
:sub-title="subTitle"
|
||||
@back="() => $router.go(-1)"
|
||||
>
|
||||
<template #extra>
|
||||
<a-tabs v-model:activeKey="activeKey" @change="onTabs">
|
||||
<a-tab-pane key="free" tab="免费热门" />
|
||||
<a-tab-pane key="pay" tab="付费热门" />
|
||||
<a-tab-pane key="new" tab="最新上架" />
|
||||
<a-tab-pane key="collect" tab="我的收藏" />
|
||||
</a-tabs>
|
||||
</template>
|
||||
<template v-if="list.length > 0">
|
||||
<a-row :gutter="16">
|
||||
<a-col
|
||||
v-bind="
|
||||
styleResponsive
|
||||
? { xl: 6, lg: 12, md: 12, sm: 24, xs: 24 }
|
||||
: { span: 12 }
|
||||
"
|
||||
class="gutter-row"
|
||||
v-for="(item, index) in list"
|
||||
:key="index"
|
||||
:span="6"
|
||||
>
|
||||
<a-card class="gutter-box" hoverable>
|
||||
<div class="plug-item">
|
||||
<a-image
|
||||
:height="80"
|
||||
:width="80"
|
||||
:preview="false"
|
||||
:src="item.companyLogo"
|
||||
style="margin-right: 10px"
|
||||
fallback="https://file.wsdns.cn/20230218/550e610d43334dd2a7f66d5b20bd58eb.svg"
|
||||
/>
|
||||
<div class="info">
|
||||
<a
|
||||
class="name ele-text-heading"
|
||||
@click="openUrl('/system/plug/detail?id=' + item.companyId)"
|
||||
>{{ item.tenantName }}</a
|
||||
>
|
||||
<a-rate
|
||||
class="rate"
|
||||
v-model:value="value"
|
||||
disabled
|
||||
allow-half
|
||||
/>
|
||||
<div class="company ele-text-placeholder">
|
||||
<a-typography-paragraph
|
||||
type="secondary"
|
||||
:ellipsis="{ rows: 2, expandable: true, symbol: '...' }"
|
||||
>
|
||||
{{ item.companyName }}
|
||||
</a-typography-paragraph>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="plug-desc ele-text-secondary">
|
||||
<a-typography-paragraph
|
||||
type="secondary"
|
||||
:ellipsis="{ rows: 2, expandable: true, symbol: '显示' }"
|
||||
:content="item.comments"
|
||||
/>
|
||||
</div>
|
||||
<div class="plug-bottom">
|
||||
<div
|
||||
class="downloads ele-text-placeholder"
|
||||
@click="() => openNotification('success', '开发中')"
|
||||
>
|
||||
安装 {{ item.clicks }}</div
|
||||
>
|
||||
<a-button type="primary" disabled v-if="planId === item.tenantId"
|
||||
>已安装</a-button
|
||||
>
|
||||
<a-button v-else type="primary" @click="onClone(item)"
|
||||
>安装</a-button
|
||||
>
|
||||
</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
</a-page-header>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, unref, watch } from 'vue';
|
||||
import { pageCompanyAll } from '@/api/system/company';
|
||||
import { Company, CompanyParam } from '@/api/system/company/model';
|
||||
import { openUrl } from '@/utils/common';
|
||||
import { notification } from 'ant-design-vue';
|
||||
import useSearch from '@/utils/use-search';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { setPageTabTitle } from '@/utils/page-tab-util';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { onClone } from '@/utils/plug-uitl';
|
||||
import { message } from 'ant-design-vue/es';
|
||||
const ROUTE_PATH = '/system/plug/list';
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
const { styleResponsive } = storeToRefs(themeStore);
|
||||
const value = ref(3.5);
|
||||
const title = ref('Tenant');
|
||||
const subTitle = ref('租户系统');
|
||||
const { currentRoute } = useRouter();
|
||||
const list = ref<Company[]>([]);
|
||||
const searchText = ref('');
|
||||
const activeKey = ref('free');
|
||||
const planId = ref<number>(Number(localStorage.getItem('PlanId')));
|
||||
|
||||
/**
|
||||
* 通知提醒框
|
||||
*/
|
||||
const openNotification = (type: string, text: string) => {
|
||||
notification[type]({
|
||||
message: '通知提醒框',
|
||||
description: text
|
||||
});
|
||||
};
|
||||
|
||||
// 查询条件
|
||||
const { where, resetFields } = useSearch<CompanyParam>({
|
||||
keywords: undefined,
|
||||
limit: 500,
|
||||
recommend: undefined,
|
||||
authoritative: 1,
|
||||
sort: 'buys',
|
||||
order: 'desc'
|
||||
});
|
||||
|
||||
const onTabs = () => {
|
||||
resetFields();
|
||||
if (activeKey.value == 'new') {
|
||||
where.sort = 'createTime';
|
||||
where.order = 'desc';
|
||||
}
|
||||
if (activeKey.value == 'free') {
|
||||
where.sort = 'buys';
|
||||
where.order = 'desc';
|
||||
}
|
||||
if (activeKey.value == 'pay') {
|
||||
where.sort = 'likes';
|
||||
where.order = 'desc';
|
||||
}
|
||||
if (activeKey.value == 'collect') {
|
||||
where.sceneType = 'collect';
|
||||
where.limit = 0;
|
||||
}
|
||||
reload();
|
||||
};
|
||||
|
||||
const reload = () => {
|
||||
if (searchText.value) {
|
||||
where.keywords = searchText.value;
|
||||
}
|
||||
const hide = message.loading('加载中...');
|
||||
pageCompanyAll(where)
|
||||
.then((data) => {
|
||||
if (data?.list) {
|
||||
list.value = data.list;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
hide();
|
||||
});
|
||||
};
|
||||
|
||||
reload();
|
||||
|
||||
watch(
|
||||
currentRoute,
|
||||
(route) => {
|
||||
const { path } = unref(route);
|
||||
if (path !== ROUTE_PATH) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { query } = unref(currentRoute);
|
||||
const { type } = query;
|
||||
if (type == 'Tenant') {
|
||||
title.value = 'Tenant';
|
||||
subTitle.value = '租户系统';
|
||||
setPageTabTitle('租户系统');
|
||||
} else if (type == 'Vue') {
|
||||
title.value = 'Vue';
|
||||
subTitle.value = 'Vue开发的应用';
|
||||
setPageTabTitle('Vue开发的应用');
|
||||
} else if (type == 'UniApp') {
|
||||
title.value = 'UniApp';
|
||||
subTitle.value = '使用UniApp开发的移动应用';
|
||||
setPageTabTitle('使用UniApp开发的移动应用');
|
||||
} else if (type == 'WebSite') {
|
||||
title.value = 'WebSite';
|
||||
subTitle.value = '网站应用';
|
||||
setPageTabTitle('网站应用');
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import * as PlugIcons from '@/layout/menu-icons';
|
||||
|
||||
export default {
|
||||
name: 'SystemPlug',
|
||||
components: PlugIcons
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.ele-body-card {
|
||||
background-color: transparent;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.gutter-row {
|
||||
margin: 15px auto;
|
||||
.gutter-box {
|
||||
.plug-item {
|
||||
display: flex;
|
||||
.info {
|
||||
font-size: 14px;
|
||||
.name {
|
||||
font-size: 20px;
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
-webkit-box-orient: vertical;
|
||||
text-overflow: ellipsis;
|
||||
-webkit-line-clamp: 2;
|
||||
font-weight: 500;
|
||||
}
|
||||
.rate {
|
||||
font-size: 13px;
|
||||
}
|
||||
.company {
|
||||
}
|
||||
}
|
||||
}
|
||||
.plug-desc {
|
||||
padding: 10px 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
.plug-bottom {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
286
src/views/system/plug/search/index.vue
Normal file
286
src/views/system/plug/search/index.vue
Normal file
@@ -0,0 +1,286 @@
|
||||
<template>
|
||||
<a-page-header :title="title" @back="() => $router.go(-1)">
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
margin-bottom: 50px;
|
||||
flex-flow: column;
|
||||
align-items: center;
|
||||
"
|
||||
>
|
||||
<a-space style="flex-wrap: wrap">
|
||||
<a-input-search
|
||||
allow-clear
|
||||
size="large"
|
||||
style="width: 500px"
|
||||
placeholder="请输入搜索关键词"
|
||||
v-model:value="searchText"
|
||||
@pressEnter="reload"
|
||||
@search="reload"
|
||||
/>
|
||||
<a-button size="large" @click="reset">重置</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
<div :bordered="false" class="ele-body-card">
|
||||
<ele-split-layout
|
||||
width="266px"
|
||||
:right-style="{ overflow: 'hidden' }"
|
||||
:style="{ minHeight: 'calc(100vh - 15px)' }"
|
||||
>
|
||||
<div class="ele-bg-white">
|
||||
<ele-toolbar theme="default">
|
||||
<div class="toolbar">
|
||||
<span>应用分类</span>
|
||||
</div>
|
||||
</ele-toolbar>
|
||||
<div class="ele-border-split sys-category-list"> </div>
|
||||
</div>
|
||||
<template #content>
|
||||
<div v-if="list.length > 0">
|
||||
<a-row :gutter="16">
|
||||
<a-col
|
||||
v-bind="styleResponsive ? { lg: 24 } : { span: 12 }"
|
||||
class="gutter-row"
|
||||
:span="6"
|
||||
v-for="(item, index) in list"
|
||||
:key="index"
|
||||
>
|
||||
<a-card class="gutter-box" hoverable>
|
||||
<div class="plug-item">
|
||||
<a-image
|
||||
:height="80"
|
||||
:width="80"
|
||||
:preview="false"
|
||||
:src="item.companyLogo"
|
||||
@click="
|
||||
openUrl('/system/plug/detail?id=' + item.companyId)
|
||||
"
|
||||
fallback="https://file.wsdns.cn/20230218/550e610d43334dd2a7f66d5b20bd58eb.svg"
|
||||
/>
|
||||
<div class="info">
|
||||
<a
|
||||
class="name ele-text-heading"
|
||||
@click="
|
||||
openUrl('/system/plug/detail?id=' + item.companyId)
|
||||
"
|
||||
>{{ item.tenantName }}</a
|
||||
>
|
||||
<a-rate
|
||||
class="rate"
|
||||
v-model:value="value"
|
||||
disabled
|
||||
allow-half
|
||||
/>
|
||||
<div class="company ele-text-placeholder">
|
||||
<a-typography-paragraph
|
||||
type="secondary"
|
||||
:ellipsis="{
|
||||
rows: 2,
|
||||
expandable: true,
|
||||
symbol: '...'
|
||||
}"
|
||||
>
|
||||
{{ item.companyName }}
|
||||
</a-typography-paragraph>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="plug-desc ele-text-secondary">
|
||||
<a-typography-paragraph
|
||||
type="secondary"
|
||||
:ellipsis="{ rows: 2, expandable: true, symbol: '显示' }"
|
||||
:content="item.comments"
|
||||
/>
|
||||
</div>
|
||||
<div class="plug-bottom">
|
||||
<div
|
||||
class="downloads ele-text-placeholder"
|
||||
@click="() => openNotification('success', '开发中')"
|
||||
>
|
||||
安装 {{ item.clicks }}</div
|
||||
>
|
||||
<a-button
|
||||
type="primary"
|
||||
disabled
|
||||
v-if="planId === item.tenantId"
|
||||
>已安装</a-button
|
||||
>
|
||||
<a-button v-else type="primary" @click="onClone(item)"
|
||||
>安装</a-button
|
||||
>
|
||||
</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
<a-space
|
||||
v-if="count > 0"
|
||||
style="display: flex; justify-content: center"
|
||||
>
|
||||
<a-pagination
|
||||
v-model:current="current"
|
||||
:total="count"
|
||||
@change="onChange"
|
||||
/>
|
||||
</a-space>
|
||||
</template>
|
||||
</ele-split-layout>
|
||||
</div>
|
||||
</a-page-header>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, unref, watch } from 'vue';
|
||||
import { pageCompanyAll } from '@/api/system/company';
|
||||
import { Company, CompanyParam } from '@/api/system/company/model';
|
||||
import { openUrl } from '@/utils/common';
|
||||
import { message } from 'ant-design-vue/es';
|
||||
import { notification } from 'ant-design-vue';
|
||||
import useSearch from '@/utils/use-search';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { onClone } from '@/utils/plug-uitl';
|
||||
const ROUTE_PATH = '/system/plug/search';
|
||||
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
const { styleResponsive } = storeToRefs(themeStore);
|
||||
const value = ref(3.5);
|
||||
const title = ref('搜索');
|
||||
const { currentRoute } = useRouter();
|
||||
const list = ref<Company[]>([]);
|
||||
const searchText = ref('');
|
||||
const current = ref(1);
|
||||
const count = ref(0);
|
||||
const planId = ref<number>(Number(localStorage.getItem('PlanId')));
|
||||
|
||||
/**
|
||||
* 通知提醒框
|
||||
*/
|
||||
const openNotification = (type: string, text: string) => {
|
||||
notification[type]({
|
||||
message: '通知提醒框',
|
||||
description: text
|
||||
});
|
||||
};
|
||||
|
||||
// 查询条件
|
||||
const { where, resetFields } = useSearch<CompanyParam>({
|
||||
keywords: undefined,
|
||||
companyName: undefined,
|
||||
limit: 10,
|
||||
recommend: undefined,
|
||||
authoritative: 1,
|
||||
page: 1,
|
||||
sort: 'buys',
|
||||
order: 'desc'
|
||||
});
|
||||
|
||||
const onChange = (page) => {
|
||||
where.page = page;
|
||||
reload();
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
searchText.value = '';
|
||||
resetFields();
|
||||
reload();
|
||||
};
|
||||
|
||||
const reload = () => {
|
||||
if (searchText.value) {
|
||||
where.keywords = searchText.value;
|
||||
}
|
||||
pageCompanyAll(where).then((data) => {
|
||||
if (data?.list) {
|
||||
list.value = data.list;
|
||||
count.value = data.count;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
currentRoute,
|
||||
(route) => {
|
||||
const { path } = unref(route);
|
||||
if (path !== ROUTE_PATH) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { query } = unref(currentRoute);
|
||||
const { type, keywords, companyName } = query;
|
||||
if (companyName) {
|
||||
where.companyName = String(companyName);
|
||||
searchText.value = String(companyName);
|
||||
}
|
||||
if (keywords) {
|
||||
searchText.value = String(keywords);
|
||||
}
|
||||
reload();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import * as PlugIcons from '@/layout/menu-icons';
|
||||
|
||||
export default {
|
||||
name: 'SystemPlug',
|
||||
components: PlugIcons
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.ele-body-card {
|
||||
background-color: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.gutter-row {
|
||||
margin-bottom: 30px;
|
||||
.gutter-box {
|
||||
.plug-item {
|
||||
display: flex;
|
||||
.info {
|
||||
margin-left: 10px;
|
||||
font-size: 14px;
|
||||
.name {
|
||||
font-size: 20px;
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
-webkit-box-orient: vertical;
|
||||
text-overflow: ellipsis;
|
||||
-webkit-line-clamp: 2;
|
||||
font-weight: 500;
|
||||
}
|
||||
.rate {
|
||||
font-size: 13px;
|
||||
}
|
||||
.company {
|
||||
}
|
||||
}
|
||||
}
|
||||
.plug-desc {
|
||||
padding: 10px 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
.plug-bottom {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
}
|
||||
.sys-category-list {
|
||||
padding: 12px 6px;
|
||||
height: calc(100vh - 242px);
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
overflow: auto;
|
||||
}
|
||||
.toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user