Initial commit
This commit is contained in:
225
src/views/system/appstore/components/buy.vue
Normal file
225
src/views/system/appstore/components/buy.vue
Normal file
@@ -0,0 +1,225 @@
|
||||
<!-- 用户编辑弹窗 -->
|
||||
<template>
|
||||
<ele-modal
|
||||
:width="900"
|
||||
:visible="visible"
|
||||
v-if="data"
|
||||
:confirm-loading="loading"
|
||||
:title="`购买插件(${data.appName})`"
|
||||
:maxable="true"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save"
|
||||
>
|
||||
<a-tabs type="card" v-model:activeKey="activeKey" tabPosition="top">
|
||||
<a-tab-pane key="1" tab="专业版">
|
||||
<div class="pay-box">
|
||||
<div class="qrcode">
|
||||
<ele-qr-code
|
||||
:value="text"
|
||||
:size="200"
|
||||
:margin="2"
|
||||
:image-settings="image"
|
||||
/>
|
||||
</div>
|
||||
<div class="pay-info">
|
||||
<a-alert
|
||||
message="提示:支付后请耐心等待支付结果,请勿刷新浏览器,否则将会导致购买异常。"
|
||||
banner
|
||||
closable
|
||||
style="margin-bottom: 12px"
|
||||
/>
|
||||
<a-descriptions title="" :column="{ xs: 1, sm: 1, md: 1 }">
|
||||
<a-descriptions-item label="模块名称">{{
|
||||
data.appName
|
||||
}}</a-descriptions-item>
|
||||
<a-descriptions-item label="当前账号">{{
|
||||
loginUser.nickname
|
||||
}}</a-descriptions-item>
|
||||
<a-descriptions-item label="订单金额"
|
||||
><span class="ele-text-warning"
|
||||
>¥{{ data.price }} /1年</span
|
||||
></a-descriptions-item
|
||||
>
|
||||
</a-descriptions>
|
||||
</div>
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" tab="企业版">
|
||||
<div class="pay-box">
|
||||
<div class="qrcode">
|
||||
<ele-qr-code
|
||||
:value="text"
|
||||
:size="200"
|
||||
:margin="2"
|
||||
:image-settings="image"
|
||||
/>
|
||||
</div>
|
||||
<div class="pay-info">
|
||||
<a-alert
|
||||
message="提示:支付后请耐心等待支付结果,请勿刷新浏览器,否则将会导致购买异常。"
|
||||
banner
|
||||
closable
|
||||
style="margin-bottom: 12px"
|
||||
/>
|
||||
<a-descriptions title="" :column="{ xs: 1, sm: 1, md: 1 }">
|
||||
<a-descriptions-item label="模块名称">{{
|
||||
data.appName
|
||||
}}</a-descriptions-item>
|
||||
<a-descriptions-item label="当前账号">{{
|
||||
loginUser.nickname
|
||||
}}</a-descriptions-item>
|
||||
<a-descriptions-item label="订单金额"
|
||||
><span class="ele-text-warning"
|
||||
>¥{{ data.price }} /1年</span
|
||||
></a-descriptions-item
|
||||
>
|
||||
</a-descriptions>
|
||||
</div>
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="3" tab="应用介绍">
|
||||
<div class="app-info">
|
||||
<!-- 编辑器 -->
|
||||
<tinymce-editor
|
||||
v-model:value="content"
|
||||
:disabled="true"
|
||||
:init="config"
|
||||
/>
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</ele-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch, computed } from 'vue';
|
||||
import { Form, message } from 'ant-design-vue';
|
||||
import type { App } from '@/api/dashboard/appstore/model';
|
||||
import type { ImageSettings } from 'ele-admin-pro/es/ele-qr-code/types';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { installApp } from '@/api/system/menu';
|
||||
import type { Menu } from '@/api/system/menu/model';
|
||||
|
||||
const useForm = Form.useForm;
|
||||
|
||||
const props = defineProps<{
|
||||
// 弹窗是否打开
|
||||
visible: boolean;
|
||||
// 修改回显的数据
|
||||
data?: App | null;
|
||||
showView?: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
// 表单数据
|
||||
const form = reactive<Menu>({
|
||||
menuId: undefined,
|
||||
parentId: undefined,
|
||||
title: '',
|
||||
menuType: 0,
|
||||
openType: 0,
|
||||
icon: '',
|
||||
path: '',
|
||||
component: '',
|
||||
authority: '',
|
||||
sortNumber: undefined,
|
||||
hide: 0,
|
||||
meta: '',
|
||||
appId: undefined
|
||||
});
|
||||
|
||||
const userStore = useUserStore();
|
||||
// 当前用户信息
|
||||
const loginUser = computed(() => userStore.info ?? {});
|
||||
const content = ref('');
|
||||
const activeKey = ref('1');
|
||||
const loading = ref(false);
|
||||
const text = ref('weixin://wxpay/bizpayurl?pr=r7C2C7Ozz');
|
||||
const image = reactive<ImageSettings>({
|
||||
src: 'https://cdn.eleadmin.com/20200610/logo-radius.png',
|
||||
width: 28,
|
||||
height: 28
|
||||
});
|
||||
|
||||
/* 更新visible */
|
||||
const updateVisible = (value: boolean) => {
|
||||
emit('update:visible', value);
|
||||
};
|
||||
|
||||
const config = ref({
|
||||
toolbar: false,
|
||||
menubar: false,
|
||||
height: 700
|
||||
});
|
||||
|
||||
const { resetFields, validate } = useForm(form);
|
||||
|
||||
/* 保存编辑 */
|
||||
const save = () => {
|
||||
validate()
|
||||
.then(() => {
|
||||
loading.value = true;
|
||||
const data = {
|
||||
title: props.data?.appName,
|
||||
parentId: props.data?.parentId,
|
||||
menuType: Number(props.data?.menuType),
|
||||
icon: props.data?.appIcon,
|
||||
path: props.data?.path,
|
||||
sortNumber: props.data?.sortNumber,
|
||||
component: props.data?.component,
|
||||
authority: props.data?.authority,
|
||||
meta: props.data?.meta,
|
||||
appId: props.data?.appId
|
||||
};
|
||||
installApp(data)
|
||||
.then((msg) => {
|
||||
loading.value = false;
|
||||
message.success(msg);
|
||||
updateVisible(false);
|
||||
emit('done');
|
||||
})
|
||||
.catch((e) => {
|
||||
loading.value = false;
|
||||
message.error(e.message);
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
content.value = String(props.data?.content);
|
||||
} else {
|
||||
resetFields();
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
<style lang="less">
|
||||
.tab-pane {
|
||||
min-height: 100px;
|
||||
}
|
||||
.card-head {
|
||||
display: flex;
|
||||
height: 40px;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.pay-box {
|
||||
display: flex;
|
||||
.qrcode {
|
||||
border: 1px solid #f3f3f3;
|
||||
width: 204px;
|
||||
height: auto;
|
||||
}
|
||||
.pay-info {
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,51 @@
|
||||
<!-- 角色选择下拉框 -->
|
||||
<template>
|
||||
<a-select
|
||||
optionFilterProp="label"
|
||||
:options="data"
|
||||
allow-clear
|
||||
:value="value"
|
||||
:placeholder="placeholder"
|
||||
@update:value="updateValue"
|
||||
@blur="onBlur"
|
||||
@change="onChange"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { getDictionaryOptions } from '@/utils/common';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:value', value: string): void;
|
||||
(e: 'blur'): void;
|
||||
(e: 'change'): void;
|
||||
}>();
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
}>(),
|
||||
{
|
||||
placeholder: '请选择客户跟进状态'
|
||||
}
|
||||
);
|
||||
|
||||
// 字典数据
|
||||
const data = getDictionaryOptions('customerFollowStatus');
|
||||
|
||||
/* 更新选中数据 */
|
||||
const updateValue = (value: string) => {
|
||||
emit('update:value', value);
|
||||
};
|
||||
|
||||
/* 失去焦点 */
|
||||
const onBlur = () => {
|
||||
emit('blur');
|
||||
};
|
||||
|
||||
/* 选择事件 */
|
||||
const onChange = (e) => {
|
||||
emit('change', e);
|
||||
};
|
||||
</script>
|
||||
51
src/views/system/appstore/components/dict/source-select.vue
Normal file
51
src/views/system/appstore/components/dict/source-select.vue
Normal file
@@ -0,0 +1,51 @@
|
||||
<!-- 客户来源选择下拉框 -->
|
||||
<template>
|
||||
<a-select
|
||||
optionFilterProp="label"
|
||||
:options="data"
|
||||
allow-clear
|
||||
:value="value"
|
||||
:placeholder="placeholder"
|
||||
@update:value="updateValue"
|
||||
@blur="onBlur"
|
||||
@change="onChange"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { getDictionaryOptions } from '@/utils/common';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:value', value: string): void;
|
||||
(e: 'blur'): void;
|
||||
(e: 'change'): void;
|
||||
}>();
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
}>(),
|
||||
{
|
||||
placeholder: '请选择客户来源'
|
||||
}
|
||||
);
|
||||
|
||||
// 字典数据
|
||||
const data = getDictionaryOptions('customerSource');
|
||||
|
||||
/* 更新选中数据 */
|
||||
const updateValue = (value: string) => {
|
||||
emit('update:value', value);
|
||||
};
|
||||
|
||||
/* 失去焦点 */
|
||||
const onBlur = () => {
|
||||
emit('blur');
|
||||
};
|
||||
|
||||
/* 选择事件 */
|
||||
const onChange = (e) => {
|
||||
emit('change', e);
|
||||
};
|
||||
</script>
|
||||
45
src/views/system/appstore/components/dict/status-select.vue
Normal file
45
src/views/system/appstore/components/dict/status-select.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<!-- 角色选择下拉框 -->
|
||||
<template>
|
||||
<a-select
|
||||
show-search
|
||||
optionFilterProp="label"
|
||||
:options="data"
|
||||
allow-clear
|
||||
:value="value"
|
||||
:placeholder="placeholder"
|
||||
@update:value="updateValue"
|
||||
@blur="onBlur"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { getDictionaryOptions } from '@/utils/common';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:value', value: string): void;
|
||||
(e: 'blur'): void;
|
||||
}>();
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
}>(),
|
||||
{
|
||||
placeholder: '请选择状态'
|
||||
}
|
||||
);
|
||||
|
||||
// 字典数据
|
||||
const data = getDictionaryOptions('status');
|
||||
|
||||
/* 更新选中数据 */
|
||||
const updateValue = (value: string) => {
|
||||
emit('update:value', value);
|
||||
};
|
||||
|
||||
/* 失去焦点 */
|
||||
const onBlur = () => {
|
||||
emit('blur');
|
||||
};
|
||||
</script>
|
||||
44
src/views/system/appstore/components/dict/type-select.vue
Normal file
44
src/views/system/appstore/components/dict/type-select.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<!-- 角色选择下拉框 -->
|
||||
<template>
|
||||
<a-select
|
||||
optionFilterProp="label"
|
||||
:options="data"
|
||||
allow-clear
|
||||
:value="value"
|
||||
:placeholder="placeholder"
|
||||
@update:value="updateValue"
|
||||
@blur="onBlur"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { getDictionaryOptions } from '@/utils/common';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:value', value: string): void;
|
||||
(e: 'blur'): void;
|
||||
}>();
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
}>(),
|
||||
{
|
||||
placeholder: '请选择客户类型'
|
||||
}
|
||||
);
|
||||
|
||||
// 字典数据
|
||||
const data = getDictionaryOptions('customerType');
|
||||
|
||||
/* 更新选中数据 */
|
||||
const updateValue = (value: string) => {
|
||||
emit('update:value', value);
|
||||
};
|
||||
|
||||
/* 失去焦点 */
|
||||
const onBlur = () => {
|
||||
emit('blur');
|
||||
};
|
||||
</script>
|
||||
76
src/views/system/appstore/components/dict/user-select.vue
Normal file
76
src/views/system/appstore/components/dict/user-select.vue
Normal file
@@ -0,0 +1,76 @@
|
||||
<!-- 角色选择下拉框 -->
|
||||
<template>
|
||||
<a-select
|
||||
show-search
|
||||
optionFilterProp="label"
|
||||
:options="data"
|
||||
allow-clear
|
||||
:value="value"
|
||||
:placeholder="placeholder"
|
||||
@update:value="updateValue"
|
||||
@search="onSearch"
|
||||
@blur="onBlur"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { listUsers } from '@/api/system/user';
|
||||
import type { SelectProps } from 'ant-design-vue';
|
||||
import { UserParam } from '@/api/system/user/model';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:value', value: string): void;
|
||||
(e: 'blur'): void;
|
||||
}>();
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
}>(),
|
||||
{
|
||||
placeholder: '请选择客户类型'
|
||||
}
|
||||
);
|
||||
|
||||
// 字典数据
|
||||
const data = ref<SelectProps['options']>([]);
|
||||
|
||||
/* 更新选中数据 */
|
||||
const updateValue = (value: string) => {
|
||||
emit('update:value', value);
|
||||
};
|
||||
|
||||
// 默认搜索条件
|
||||
const where = ref<UserParam>({});
|
||||
|
||||
const search = () => {
|
||||
/* 获取用户列 */
|
||||
listUsers({ ...where?.value })
|
||||
.then((result) => {
|
||||
data.value = result?.map((d) => {
|
||||
return {
|
||||
value: d.userId,
|
||||
label: d.nickname
|
||||
};
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
message.error(e.message);
|
||||
});
|
||||
};
|
||||
|
||||
const onSearch = (e) => {
|
||||
where.value.nickname = e;
|
||||
search();
|
||||
};
|
||||
|
||||
search();
|
||||
|
||||
/* 失去焦点 */
|
||||
const onBlur = () => {
|
||||
emit('blur');
|
||||
};
|
||||
</script>
|
||||
400
src/views/system/appstore/components/edit.vue
Normal file
400
src/views/system/appstore/components/edit.vue
Normal file
@@ -0,0 +1,400 @@
|
||||
<!-- 编辑弹窗 -->
|
||||
<template>
|
||||
<ele-modal
|
||||
:width="740"
|
||||
:visible="visible"
|
||||
:confirm-loading="loading"
|
||||
:title="isUpdate ? '编辑应用' : '创建应用'"
|
||||
:body-style="{ paddingBottom: '8px' }"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save"
|
||||
>
|
||||
<a-form
|
||||
:label-col="{ md: { span: 6 }, sm: { span: 4 }, xs: { span: 24 } }"
|
||||
:wrapper-col="{ md: { span: 18 }, sm: { span: 20 }, xs: { span: 24 } }"
|
||||
>
|
||||
<a-row :gutter="16">
|
||||
<a-col :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="应用分类" v-bind="validateInfos.appType">
|
||||
<a-select
|
||||
optionFilterProp="label"
|
||||
placeholder="请选择应用分类"
|
||||
allow-clear
|
||||
v-model:value="form.appType"
|
||||
>
|
||||
<template v-for="item in appTypeDict">
|
||||
<a-select-option :value="item.value">{{ item.label }}</a-select-option>
|
||||
</template>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="应用名称" v-bind="validateInfos.appName">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入应用名称"
|
||||
v-model:value="form.appName"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="应用入口" v-bind="validateInfos.component">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="/dashboard/workplace"
|
||||
v-model:value="form.component"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="应用标识" v-bind="validateInfos.appCode">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="goods"
|
||||
v-model:value="form.appCode"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="授权价格" v-bind="validateInfos.price">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="授权价格"
|
||||
v-model:value="form.price"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="排序号" v-role="'superAdmin'" v-bind="validateInfos.sortNumber">
|
||||
<a-input-number
|
||||
:min="0"
|
||||
:max="99999"
|
||||
class="ele-fluid"
|
||||
placeholder="请输入排序号"
|
||||
v-model:value="form.sortNumber"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-form-item
|
||||
label="应用简介"
|
||||
v-bind="validateInfos.comments"
|
||||
:label-col="{ md: { span: 3 }, sm: { span: 4 }, xs: { span: 24 } }"
|
||||
:wrapper-col="{ md: { span: 21 }, sm: { span: 20 }, xs: { span: 24 } }"
|
||||
>
|
||||
<a-textarea
|
||||
:rows="4"
|
||||
:maxlength="200"
|
||||
placeholder="请输入应用简介"
|
||||
v-model:value="form.comments"
|
||||
/>
|
||||
</a-form-item>
|
||||
<div style="margin-bottom: 22px">
|
||||
<a-divider />
|
||||
</div>
|
||||
<a-row :gutter="16">
|
||||
<a-col :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="应用图标" v-bind="validateInfos.appIcon">
|
||||
<ele-icon-picker
|
||||
v-model:value="form.appIcon"
|
||||
allow-clear
|
||||
placeholder="请选择图标"
|
||||
:disabled="form.appType === 1"
|
||||
:data="iconData"
|
||||
>
|
||||
<template #icon="{ icon }">
|
||||
<component :is="icon"/>
|
||||
</template>
|
||||
</ele-icon-picker>
|
||||
</a-form-item>
|
||||
<a-form-item label="下载地址" v-bind="validateInfos.downUrl">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="模块代码下载地址"
|
||||
v-model:value="form.downUrl"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="当前版本" v-bind="validateInfos.edition">
|
||||
<a-select
|
||||
optionFilterProp="label"
|
||||
placeholder="请选择版本"
|
||||
allow-clear
|
||||
v-model:value="form.edition"
|
||||
>
|
||||
<a-select-option value="正式版">正式版</a-select-option>
|
||||
<a-select-option value="开发版">开发版</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="应用状态">
|
||||
<a-switch
|
||||
checked-children="启用"
|
||||
un-checked-children="禁用"
|
||||
:checked="form.status === 0"
|
||||
@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>
|
||||
</ele-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {ref, reactive, watch} from 'vue';
|
||||
import {Form, message} from 'ant-design-vue';
|
||||
import type {RuleObject} from 'ant-design-vue/es/form';
|
||||
import iconData from 'ele-admin-pro/es/ele-icon-picker/icons';
|
||||
import {assignObject, isExternalLink} from 'ele-admin-pro';
|
||||
import {addApp, updateApp} from '@/api/dashboard/appstore';
|
||||
import type {App} from '@/api/dashboard/appstore/model';
|
||||
import {ItemType} from "ele-admin-pro/es/ele-image-upload/types";
|
||||
import {uploadFile} from "@/api/system/file";
|
||||
import {getDictionaryOptions} from "@/utils/common";
|
||||
|
||||
const useForm = Form.useForm;
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
const props = defineProps<{
|
||||
// 弹窗是否打开
|
||||
visible: boolean;
|
||||
// 修改回显的数据
|
||||
data?: App | null;
|
||||
// 上级应用id
|
||||
parentId?: number;
|
||||
}>();
|
||||
|
||||
// 是否是修改
|
||||
const isUpdate = ref(false);
|
||||
// 提交状态
|
||||
const loading = ref(false);
|
||||
const icon = ref('');
|
||||
const appTypeDict = getDictionaryOptions('appstoreType');
|
||||
// 编辑器内容,双向绑定
|
||||
const content = ref('');
|
||||
|
||||
// 表单数据
|
||||
const form = reactive<App>({
|
||||
// 应用id
|
||||
appId: undefined,
|
||||
// 应用名称
|
||||
appName: '',
|
||||
// 上级id, 0是顶级
|
||||
parentId: 0,
|
||||
// 应用编号
|
||||
appCode: '',
|
||||
// 应用图标
|
||||
appIcon: '',
|
||||
// 应用类型
|
||||
appType: undefined,
|
||||
// 应用地址
|
||||
appUrl: '',
|
||||
// 下载地址
|
||||
downUrl: '',
|
||||
// 应用包名
|
||||
packageName: '',
|
||||
// 点击次数
|
||||
clicks: '',
|
||||
// 安装次数
|
||||
installs: '',
|
||||
// 应用介绍
|
||||
content: '',
|
||||
// 开发者(个人)
|
||||
developer: '官方',
|
||||
// 页面路径
|
||||
component: undefined,
|
||||
// 软件授权价格
|
||||
price: '',
|
||||
// 评分
|
||||
score: '',
|
||||
// 星级
|
||||
star: '',
|
||||
// 排序
|
||||
sortNumber: 100,
|
||||
// 备注
|
||||
comments: '',
|
||||
// 权限标识
|
||||
authority: '',
|
||||
// 打开位置
|
||||
target: '',
|
||||
// 是否隐藏, 0否, 1是(仅注册路由不显示在左侧菜单)
|
||||
hide: undefined,
|
||||
// 菜单侧栏选中的path
|
||||
active: '',
|
||||
// 其它路由元信息
|
||||
meta: '',
|
||||
// 版本
|
||||
edition: '开发版',
|
||||
// 版本号
|
||||
version: 'v1.0',
|
||||
// 创建时间
|
||||
createTime: '',
|
||||
// 状态
|
||||
status: 1,
|
||||
// 发布者
|
||||
userId: undefined,
|
||||
// 发布者昵称
|
||||
nickname: ''
|
||||
});
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive({
|
||||
appName: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请输入应用名称',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
component: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请输入组件路径',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
appType: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请选择应用分类',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
appCode: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请输入应用标识(英文)',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
comments: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请输入应用简介',
|
||||
}
|
||||
],
|
||||
sortNumber: [
|
||||
{
|
||||
required: true,
|
||||
type: 'number',
|
||||
message: '请输入排序号',
|
||||
}
|
||||
],
|
||||
price: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入软件授权价格',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
meta: [
|
||||
{
|
||||
type: 'string',
|
||||
trigger: 'blur',
|
||||
validator: async (_rule: RuleObject, 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();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const {resetFields, validate, validateInfos} = useForm(form, rules);
|
||||
|
||||
/* 保存编辑 */
|
||||
const save = () => {
|
||||
validate()
|
||||
.then(() => {
|
||||
loading.value = true;
|
||||
const appForm = {
|
||||
...form,
|
||||
parentId: form.parentId || 0,
|
||||
content: content.value
|
||||
};
|
||||
const saveOrUpdate = isUpdate.value ? updateApp : addApp;
|
||||
saveOrUpdate(appForm)
|
||||
.then((msg) => {
|
||||
loading.value = false;
|
||||
message.success(msg);
|
||||
updateVisible(false);
|
||||
emit('done');
|
||||
})
|
||||
.catch((e) => {
|
||||
loading.value = false;
|
||||
message.error(e.message);
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
});
|
||||
};
|
||||
|
||||
const onUpload = (d: ItemType) => {
|
||||
uploadFile(<File>d.file)
|
||||
.then((result) => {
|
||||
form.appIcon = result.path;
|
||||
message.success('上传成功');
|
||||
})
|
||||
.catch((e) => {
|
||||
message.error(e.message);
|
||||
});
|
||||
};
|
||||
|
||||
/* 更新visible */
|
||||
const updateVisible = (value: boolean) => {
|
||||
emit('update:visible', value);
|
||||
};
|
||||
|
||||
const updateHideValue = (value: boolean) => {
|
||||
form.status = value ? 0 : 1;
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
if (props.data) {
|
||||
const isExternal = isExternalLink(props.data.path);
|
||||
const isInner = isExternalLink(props.data.component);
|
||||
content.value = String(props.data.content);
|
||||
form.price = props.data.price;
|
||||
assignObject(form, {
|
||||
...props.data,
|
||||
openType: isExternal ? 2 : isInner ? 1 : 0
|
||||
});
|
||||
isUpdate.value = true;
|
||||
} else {
|
||||
form.parentId = props.parentId;
|
||||
isUpdate.value = false;
|
||||
}
|
||||
} else {
|
||||
resetFields();
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
<script lang="ts">
|
||||
import * as icons from '@ant-design/icons-vue';
|
||||
|
||||
export default {
|
||||
components: icons
|
||||
};
|
||||
</script>
|
||||
96
src/views/system/appstore/components/search.vue
Normal file
96
src/views/system/appstore/components/search.vue
Normal file
@@ -0,0 +1,96 @@
|
||||
<!-- 搜索表单 -->
|
||||
<template>
|
||||
<a-space :size="10" style="flex-wrap: wrap">
|
||||
<!-- <a-button-->
|
||||
<!-- v-role="'superAdmin'"-->
|
||||
<!-- type="primary"-->
|
||||
<!-- class="ele-btn-icon"-->
|
||||
<!-- @click="add"-->
|
||||
<!-- >-->
|
||||
<!-- <template #icon>-->
|
||||
<!-- <plus-outlined />-->
|
||||
<!-- </template>-->
|
||||
<!-- <span>创建应用</span>-->
|
||||
<!-- </a-button>-->
|
||||
<a-input-search
|
||||
allow-clear
|
||||
placeholder="请输入应用名称"
|
||||
v-model:value="searchText"
|
||||
@pressEnter="search"
|
||||
@search="search"
|
||||
>
|
||||
<template #addonBefore>
|
||||
<a-select v-model:value="type" style="width: 100px; margin: -5px -12px">
|
||||
<a-select-option value="appName">应用名称</a-select-option>
|
||||
<a-select-option value="appCode">应用标识</a-select-option>
|
||||
</a-select>
|
||||
</template>
|
||||
</a-input-search>
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
PlusOutlined,
|
||||
EditOutlined,
|
||||
SearchOutlined,
|
||||
DeleteOutlined,
|
||||
UpSquareOutlined,
|
||||
DownSquareOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
import useSearch from '@/utils/use-search';
|
||||
import type { AppParam } from '@/api/dashboard/appstore/model';
|
||||
import { ref, watch } from 'vue';
|
||||
import { assignObject } from 'ele-admin-pro';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
// 选中的角色
|
||||
selection?: [];
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search', where?: AppParam): void;
|
||||
(e: 'add'): void;
|
||||
(e: 'remove'): void;
|
||||
}>();
|
||||
|
||||
// 表单数据
|
||||
const { where } = useSearch<AppParam>({
|
||||
appName: '',
|
||||
appCode: ''
|
||||
});
|
||||
// 下拉选项
|
||||
const type = ref('appName');
|
||||
// 搜索内容
|
||||
const searchText = ref('');
|
||||
|
||||
/* 搜索 */
|
||||
const search = () => {
|
||||
assignObject(where, {});
|
||||
if (type.value == 'appName') {
|
||||
where.appName = searchText.value;
|
||||
}
|
||||
if (type.value == 'appCode') {
|
||||
where.appCode = searchText.value;
|
||||
}
|
||||
emit('search', where);
|
||||
};
|
||||
|
||||
// 新增
|
||||
const add = () => {
|
||||
emit('add');
|
||||
};
|
||||
|
||||
// 批量删除
|
||||
const removeBatch = () => {
|
||||
emit('remove');
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.selection,
|
||||
() => {}
|
||||
);
|
||||
</script>
|
||||
83
src/views/system/appstore/components/setting.vue
Normal file
83
src/views/system/appstore/components/setting.vue
Normal file
@@ -0,0 +1,83 @@
|
||||
<!-- 用户编辑弹窗 -->
|
||||
<template>
|
||||
<ele-modal
|
||||
:width="800"
|
||||
:visible="visible"
|
||||
v-if="data"
|
||||
:title="`${data.appName}`"
|
||||
:maxable="true"
|
||||
@update:visible="updateVisible"
|
||||
:footer="null"
|
||||
>
|
||||
<a-tabs type="card" v-model:activeKey="activeKey" tabPosition="top">
|
||||
<a-tab-pane key="0" tab="插件介绍">
|
||||
<Basic :data="data" :appId="data.appId" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="1" tab="菜单管理">
|
||||
<Menu :data="data" :menuType="0" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" tab="按钮管理">
|
||||
<Authority :data="data" :menuType="1" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="3" tab="插件设置">
|
||||
<Setting :data="data" :appId="data.appId" />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</ele-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import type { App } from '@/api/dashboard/appstore/model';
|
||||
import Authority from './setting/authority.vue';
|
||||
import Menu from './setting/menu.vue';
|
||||
import Basic from './setting/basic.vue';
|
||||
import Setting from './setting/setting.vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
const props = defineProps<{
|
||||
// 弹窗是否打开
|
||||
visible: boolean;
|
||||
// 修改回显的数据
|
||||
data?: App | null;
|
||||
showView?: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
const activeKey = ref('0');
|
||||
// 编辑器内容,双向绑定
|
||||
const content = ref<any>('');
|
||||
|
||||
/* 更新visible */
|
||||
const updateVisible = (value: boolean) => {
|
||||
emit('update:visible', value);
|
||||
};
|
||||
|
||||
// 保存设置
|
||||
const save = () => {
|
||||
message.warn('待开发...');
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
content.value = props.data?.content;
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
<style lang="less">
|
||||
.tab-pane {
|
||||
min-height: 100px;
|
||||
}
|
||||
.card-head {
|
||||
display: flex;
|
||||
height: 40px;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
</style>
|
||||
166
src/views/system/appstore/components/setting/authority-edit.vue
Normal file
166
src/views/system/appstore/components/setting/authority-edit.vue
Normal file
@@ -0,0 +1,166 @@
|
||||
<!-- 编辑弹窗 -->
|
||||
<template>
|
||||
<ele-modal
|
||||
:width="400"
|
||||
:visible="visible"
|
||||
:confirm-loading="loading"
|
||||
:title="isUpdate ? '修改按钮' : '添加按钮'"
|
||||
:body-style="{ paddingBottom: '8px' }"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save"
|
||||
>
|
||||
<a-form
|
||||
:label-col="{ md: { span: 6 }, sm: { span: 4 }, xs: { span: 24 } }"
|
||||
:wrapper-col="{ md: { span: 18 }, sm: { span: 20 }, xs: { span: 24 } }"
|
||||
>
|
||||
<a-row :gutter="16">
|
||||
<a-col :md="23" :sm="24" :xs="24">
|
||||
<a-form-item label="按钮名称" v-bind="validateInfos.appName">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入按钮名称"
|
||||
v-model:value="form.appName"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="权限标识" v-bind="validateInfos.authority">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="sys:article:list"
|
||||
v-model:value="form.authority"
|
||||
@pressEnter="save"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</ele-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch } from 'vue';
|
||||
import { Form, message } from 'ant-design-vue';
|
||||
import { addApp, updateApp } from '@/api/dashboard/appstore';
|
||||
import type { App } from '@/api/dashboard/appstore/model';
|
||||
import { assignObject, isExternalLink } from 'ele-admin-pro';
|
||||
|
||||
const useForm = Form.useForm;
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
const props = defineProps<{
|
||||
// 弹窗是否打开
|
||||
visible: boolean;
|
||||
// 修改回显的数据
|
||||
data?: App | null;
|
||||
parentId?: number;
|
||||
}>();
|
||||
|
||||
// 提交状态
|
||||
const loading = ref(false);
|
||||
// 是否是修改
|
||||
const isUpdate = ref(false);
|
||||
|
||||
// 表单数据
|
||||
const form = reactive<App>({
|
||||
appId: undefined,
|
||||
// 菜单id
|
||||
appName: '',
|
||||
// 上级id, 0是顶级
|
||||
parentId: 0,
|
||||
// 菜单类型, 0菜单, 1按钮
|
||||
menuType: 1,
|
||||
// 排序号
|
||||
sortNumber: 100,
|
||||
// 权限标识
|
||||
authority: ''
|
||||
});
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive({
|
||||
appName: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请输入按钮名称',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
authority: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请输入权限标识',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const { resetFields, validate, validateInfos } = useForm(form, rules);
|
||||
|
||||
/* 保存编辑 */
|
||||
const save = () => {
|
||||
validate()
|
||||
.then(() => {
|
||||
loading.value = true;
|
||||
const appForm = {
|
||||
...form,
|
||||
parentId: props.parentId,
|
||||
appID: props.data?.appId,
|
||||
menuType: Number(form.menuType),
|
||||
appName: form.appName,
|
||||
authority: form.authority
|
||||
};
|
||||
const saveOrUpdate = isUpdate.value ? updateApp : addApp;
|
||||
saveOrUpdate(appForm)
|
||||
.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);
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
if (props.data?.appId) {
|
||||
const isExternal = isExternalLink(props.data.path);
|
||||
const isInner = isExternalLink(props.data.component);
|
||||
assignObject(form, {
|
||||
...props.data,
|
||||
openType: isExternal ? 2 : isInner ? 1 : 0,
|
||||
parentId: props.data?.parentId
|
||||
});
|
||||
isUpdate.value = true;
|
||||
} else {
|
||||
isUpdate.value = false;
|
||||
}
|
||||
} else {
|
||||
resetFields();
|
||||
isUpdate.value = false;
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
<script lang="ts">
|
||||
import * as icons from '@ant-design/icons-vue';
|
||||
|
||||
export default {
|
||||
components: icons
|
||||
};
|
||||
</script>
|
||||
172
src/views/system/appstore/components/setting/authority.vue
Normal file
172
src/views/system/appstore/components/setting/authority.vue
Normal file
@@ -0,0 +1,172 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-space v-role="'superAdmin'" style="margin-bottom: 10px">
|
||||
<a-button
|
||||
type="primary"
|
||||
class="ele-btn-icon"
|
||||
v-role="'superAdmin'"
|
||||
v-if="data"
|
||||
@click="openEdit"
|
||||
>
|
||||
<span>添加</span>
|
||||
</a-button>
|
||||
</a-space>
|
||||
<!-- 表格 -->
|
||||
<ele-pro-table
|
||||
ref="tableRef"
|
||||
row-key="appId"
|
||||
:columns="columns"
|
||||
class="sys-org-table"
|
||||
:toolbar="false"
|
||||
:datasource="datasource"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'menuType'">
|
||||
<a-tag v-if="record.menuType === '0'" color="blue">菜单</a-tag>
|
||||
<a-tag v-else-if="record.menuType === '1'">按钮</a-tag>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-space v-role="'superAdmin'">
|
||||
<a @click="openEdit(record)">修改</a>
|
||||
<a-divider type="vertical" />
|
||||
<a-popconfirm
|
||||
title="确定要删除此记录吗?"
|
||||
@confirm="remove(record)"
|
||||
>
|
||||
<a class="ele-text-danger">删除</a>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</ele-pro-table>
|
||||
</div>
|
||||
<!-- 编辑弹窗 -->
|
||||
<AuthorityEdit
|
||||
v-model:visible="showEdit"
|
||||
:data="current"
|
||||
:parentId="parentId"
|
||||
@done="reload"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import type {
|
||||
ColumnItem,
|
||||
DatasourceFunction
|
||||
} from 'ele-admin-pro/es/ele-pro-table/types';
|
||||
import type { EleProTable } from 'ele-admin-pro';
|
||||
import AuthorityEdit from './authority-edit.vue';
|
||||
import { App, AppParam } from '@/api/dashboard/appstore/model';
|
||||
import { listApp, removeApp } from '@/api/dashboard/appstore';
|
||||
|
||||
const props = defineProps<{
|
||||
// 菜单类型
|
||||
menuType?: number;
|
||||
// 修改回显的数据
|
||||
data?: App | null;
|
||||
showView?: boolean;
|
||||
}>();
|
||||
|
||||
// 表格实例
|
||||
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
|
||||
|
||||
// 表格列配置
|
||||
const columns = ref<ColumnItem[]>([
|
||||
{
|
||||
title: '名称',
|
||||
key: 'appName',
|
||||
dataIndex: 'appName',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '权限标识',
|
||||
dataIndex: 'authority',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
align: 'center',
|
||||
hideInSetting: true
|
||||
}
|
||||
]);
|
||||
|
||||
/* 表格数据源 */
|
||||
const datasource: DatasourceFunction = ({ page, limit, where, orders }) => {
|
||||
where.parentId = props.data?.appId;
|
||||
where.menuType = props.menuType;
|
||||
return listApp({ ...where, ...orders, page, limit });
|
||||
};
|
||||
|
||||
// 当前编辑数据
|
||||
const current = ref<App | null>(null);
|
||||
|
||||
// 是否显示编辑弹窗
|
||||
const showEdit = ref(false);
|
||||
|
||||
// 上级菜单id
|
||||
const parentId = ref<number>();
|
||||
|
||||
// 菜单数据
|
||||
// const menuData = ref<App[]>([]);
|
||||
|
||||
// 表格展开的行
|
||||
// const expandedRowKeys = ref<number[]>([]);
|
||||
|
||||
/* 表格渲染完成回调 */
|
||||
// const onDone: EleProTableDone<App> = ({ data }) => {
|
||||
// menuData.value = data;
|
||||
// };
|
||||
|
||||
/* 刷新表格 */
|
||||
const reload = (where?: AppParam) => {
|
||||
tableRef?.value?.reload({ where: where });
|
||||
};
|
||||
|
||||
/* 打开编辑弹窗 */
|
||||
const openEdit = (row?: App | null, id?: number) => {
|
||||
parentId.value = props.data?.appId;
|
||||
current.value = row ?? null;
|
||||
showEdit.value = true;
|
||||
};
|
||||
|
||||
/* 删除单个 */
|
||||
const remove = (row: App) => {
|
||||
if (row.children?.length) {
|
||||
message.error('请先删除子节点');
|
||||
return;
|
||||
}
|
||||
const hide = message.loading('请求中..', 0);
|
||||
removeApp(row.appId)
|
||||
.then((msg) => {
|
||||
hide();
|
||||
message.success(msg);
|
||||
reload();
|
||||
})
|
||||
.catch((e) => {
|
||||
hide();
|
||||
message.error(e.message);
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.data?.appId,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
reload();
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import * as MenuIcons from '@/layout/menu-icons';
|
||||
|
||||
export default {
|
||||
name: 'Authority',
|
||||
components: MenuIcons
|
||||
};
|
||||
</script>
|
||||
111
src/views/system/appstore/components/setting/basic.vue
Normal file
111
src/views/system/appstore/components/setting/basic.vue
Normal file
@@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- 编辑器 -->
|
||||
<byte-md-editor
|
||||
v-model:value="content"
|
||||
:locale="zh_Hans"
|
||||
:plugins="plugins"
|
||||
uploadImages
|
||||
height="500px"
|
||||
mode="split"
|
||||
:editorConfig="{ lineNumbers: true }"
|
||||
/>
|
||||
<a-button
|
||||
type="primary"
|
||||
class="ele-btn-icon"
|
||||
style="margin-top: 10px"
|
||||
@click="saveContent"
|
||||
>
|
||||
<span>保存</span>
|
||||
</a-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { App } from '@/api/dashboard/appstore/model';
|
||||
import { updateApp } from '@/api/dashboard/appstore';
|
||||
// 中文语言文件
|
||||
import zh_Hans from 'bytemd/locales/zh_Hans.json';
|
||||
// 链接、删除线、复选框、表格等的插件
|
||||
import gfm from '@bytemd/plugin-gfm';
|
||||
// 插件的中文语言文件
|
||||
import zh_HansGfm from '@bytemd/plugin-gfm/locales/zh_Hans.json';
|
||||
// 预览界面的样式,这里用的 github 的 markdown 主题
|
||||
import 'github-markdown-css/github-markdown-light.css';
|
||||
import highlight from '@bytemd/plugin-highlight';
|
||||
import { assignObject, isExternalLink } from 'ele-admin-pro';
|
||||
|
||||
const props = defineProps<{
|
||||
// 应用id
|
||||
appId?: number | 0;
|
||||
// 修改回显的数据
|
||||
data?: App | null;
|
||||
}>();
|
||||
|
||||
// 编辑器内容,双向绑定
|
||||
const content = ref<any>('');
|
||||
|
||||
// 插件
|
||||
const plugins = ref([
|
||||
gfm({
|
||||
locale: zh_HansGfm
|
||||
}),
|
||||
highlight()
|
||||
]);
|
||||
|
||||
// 保存应用详情
|
||||
const saveContent = () => {
|
||||
const appForm = {
|
||||
appId: props.data?.appId,
|
||||
content: content.value
|
||||
};
|
||||
updateApp(appForm)
|
||||
.then((msg) => {
|
||||
message.success('保存成功');
|
||||
})
|
||||
.catch((e) => {
|
||||
message.error(e.message);
|
||||
});
|
||||
};
|
||||
|
||||
// 加载内容
|
||||
const setContent = () => {
|
||||
content.value = props.data?.content;
|
||||
};
|
||||
|
||||
const reload = () => {
|
||||
if (props.data) {
|
||||
const isExternal = isExternalLink(props.data.path);
|
||||
const isInner = isExternalLink(props.data.component);
|
||||
content.value = String(props.data.content);
|
||||
} else {
|
||||
}
|
||||
};
|
||||
|
||||
setContent();
|
||||
|
||||
if (props.appId) {
|
||||
reload();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.data?.appId,
|
||||
(data) => {
|
||||
if (data) {
|
||||
reload();
|
||||
} else {
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import * as MenuIcons from '@/layout/menu-icons';
|
||||
|
||||
export default {
|
||||
name: 'Authority',
|
||||
components: MenuIcons
|
||||
};
|
||||
</script>
|
||||
87
src/views/system/appstore/components/setting/content.vue
Normal file
87
src/views/system/appstore/components/setting/content.vue
Normal file
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- 编辑器 -->
|
||||
<byte-md-editor
|
||||
v-model:value="content"
|
||||
:locale="zh_Hans"
|
||||
:plugins="plugins"
|
||||
uploadImages
|
||||
height="500px"
|
||||
:editorConfig="{ lineNumbers: true }"
|
||||
/>
|
||||
<a-button
|
||||
type="primary"
|
||||
class="ele-btn-icon"
|
||||
style="margin-top: 10px"
|
||||
@click="saveContent"
|
||||
>
|
||||
<span>保存</span>
|
||||
</a-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { App } from '@/api/dashboard/appstore/model';
|
||||
import { updateApp } from '@/api/dashboard/appstore';
|
||||
// 中文语言文件
|
||||
import zh_Hans from 'bytemd/locales/zh_Hans.json';
|
||||
// 链接、删除线、复选框、表格等的插件
|
||||
import gfm from '@bytemd/plugin-gfm';
|
||||
// 插件的中文语言文件
|
||||
import zh_HansGfm from '@bytemd/plugin-gfm/locales/zh_Hans.json';
|
||||
// 预览界面的样式,这里用的 github 的 markdown 主题
|
||||
import 'github-markdown-css/github-markdown-light.css';
|
||||
import highlight from '@bytemd/plugin-highlight';
|
||||
|
||||
const props = defineProps<{
|
||||
// 弹窗是否打开
|
||||
visible: boolean;
|
||||
// 修改回显的数据
|
||||
data?: App | null;
|
||||
showView?: boolean;
|
||||
}>();
|
||||
|
||||
// 编辑器内容,双向绑定
|
||||
const content = ref<any>('');
|
||||
|
||||
// 插件
|
||||
const plugins = ref([
|
||||
gfm({
|
||||
locale: zh_HansGfm
|
||||
}),
|
||||
highlight()
|
||||
]);
|
||||
|
||||
// 保存应用详情
|
||||
const saveContent = () => {
|
||||
const appForm = {
|
||||
appId: props.data?.appId,
|
||||
content: content.value
|
||||
};
|
||||
updateApp(appForm)
|
||||
.then((msg) => {
|
||||
message.success('保存成功');
|
||||
})
|
||||
.catch((e) => {
|
||||
message.error(e.message);
|
||||
});
|
||||
};
|
||||
|
||||
// 加载内容
|
||||
const setContent = () => {
|
||||
content.value = props.data?.content;
|
||||
};
|
||||
|
||||
setContent();
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import * as MenuIcons from '@/layout/menu-icons';
|
||||
|
||||
export default {
|
||||
name: 'Authority',
|
||||
components: MenuIcons
|
||||
};
|
||||
</script>
|
||||
188
src/views/system/appstore/components/setting/menu-edit.vue
Normal file
188
src/views/system/appstore/components/setting/menu-edit.vue
Normal file
@@ -0,0 +1,188 @@
|
||||
<!-- 编辑弹窗 -->
|
||||
<template>
|
||||
<ele-modal
|
||||
:width="400"
|
||||
:visible="visible"
|
||||
:confirm-loading="loading"
|
||||
:title="isUpdate ? '修改组件' : '添加组件'"
|
||||
:body-style="{ paddingBottom: '8px' }"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save"
|
||||
>
|
||||
<a-form
|
||||
:label-col="{ md: { span: 6 }, sm: { span: 4 }, xs: { span: 24 } }"
|
||||
:wrapper-col="{ md: { span: 18 }, sm: { span: 20 }, xs: { span: 24 } }"
|
||||
>
|
||||
<a-row :gutter="16">
|
||||
<a-col :md="23" :sm="24" :xs="24">
|
||||
<a-form-item label="组件名称" v-bind="validateInfos.appName">
|
||||
<a-input
|
||||
allow-clear
|
||||
:placeholder="`文章详情`"
|
||||
v-model:value="form.appName"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="组件路径" v-bind="validateInfos.path">
|
||||
<a-input
|
||||
allow-clear
|
||||
:placeholder="`/article/detail/:id`"
|
||||
v-model:value="form.path"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="组件路径" v-bind="validateInfos.component">
|
||||
<a-input
|
||||
allow-clear
|
||||
:placeholder="`/article/detail`"
|
||||
v-model:value="form.component"
|
||||
@pressEnter="save"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="路由元数据" v-bind="validateInfos.meta">
|
||||
<a-textarea
|
||||
:rows="4"
|
||||
:maxlength="200"
|
||||
placeholder="请输入JSON格式的路由元数据"
|
||||
v-model:value="form.meta"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</ele-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch } from 'vue';
|
||||
import { Form, message } from 'ant-design-vue';
|
||||
import { addApp, updateApp } from '@/api/dashboard/appstore';
|
||||
import type { App } from '@/api/dashboard/appstore/model';
|
||||
import { assignObject, isExternalLink } from 'ele-admin-pro';
|
||||
|
||||
const useForm = Form.useForm;
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
const props = defineProps<{
|
||||
// 弹窗是否打开
|
||||
visible: boolean;
|
||||
// 修改回显的数据
|
||||
data?: App | null;
|
||||
parentId?: number;
|
||||
}>();
|
||||
|
||||
// 提交状态
|
||||
const loading = ref(false);
|
||||
// 是否是修改
|
||||
const isUpdate = ref(false);
|
||||
// 表单数据
|
||||
const form = reactive<App>({
|
||||
appId: undefined,
|
||||
// 菜单id
|
||||
appName: '',
|
||||
// 上级id, 0是顶级
|
||||
parentId: 0,
|
||||
// 菜单类型, 0菜单, 1按钮
|
||||
menuType: 0,
|
||||
// 排序号
|
||||
sortNumber: 100,
|
||||
path: '',
|
||||
component: '',
|
||||
meta: ''
|
||||
});
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive({
|
||||
appName: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请输入按钮名称',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
path: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请输入路由地址',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
component: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请输组件路径',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const { resetFields, validate, validateInfos } = useForm(form, rules);
|
||||
|
||||
/* 保存编辑 */
|
||||
const save = () => {
|
||||
validate()
|
||||
.then(() => {
|
||||
loading.value = true;
|
||||
const appForm = {
|
||||
...form,
|
||||
parentId: props.parentId,
|
||||
appID: props.data?.appId,
|
||||
menuType: Number(form.menuType),
|
||||
appName: form.appName
|
||||
};
|
||||
const saveOrUpdate = isUpdate.value ? updateApp : addApp;
|
||||
saveOrUpdate(appForm)
|
||||
.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);
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
if (props.data?.appId) {
|
||||
const isExternal = isExternalLink(props.data.path);
|
||||
const isInner = isExternalLink(props.data.component);
|
||||
assignObject(form, {
|
||||
...props.data,
|
||||
openType: isExternal ? 2 : isInner ? 1 : 0,
|
||||
parentId: props.data?.parentId
|
||||
});
|
||||
isUpdate.value = true;
|
||||
} else {
|
||||
isUpdate.value = false;
|
||||
}
|
||||
} else {
|
||||
resetFields();
|
||||
isUpdate.value = false;
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
<script lang="ts">
|
||||
import * as icons from '@ant-design/icons-vue';
|
||||
|
||||
export default {
|
||||
components: icons
|
||||
};
|
||||
</script>
|
||||
174
src/views/system/appstore/components/setting/menu.vue
Normal file
174
src/views/system/appstore/components/setting/menu.vue
Normal file
@@ -0,0 +1,174 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-space style="margin-bottom: 10px">
|
||||
<a-button
|
||||
type="primary"
|
||||
class="ele-btn-icon"
|
||||
v-if="data"
|
||||
@click="openEdit"
|
||||
>
|
||||
<span>添加</span>
|
||||
</a-button>
|
||||
</a-space>
|
||||
<!-- 表格 -->
|
||||
<ele-pro-table
|
||||
ref="tableRef"
|
||||
row-key="appId"
|
||||
:columns="columns"
|
||||
class="sys-org-table"
|
||||
:toolbar="false"
|
||||
:datasource="datasource"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'menuType'">
|
||||
<a-tag v-if="record.menuType === '0'" color="blue">菜单</a-tag>
|
||||
<a-tag v-else-if="record.menuType === '1'">按钮</a-tag>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a @click="openEdit(record)">修改</a>
|
||||
<a-divider type="vertical" />
|
||||
<a-popconfirm
|
||||
title="确定要删除此记录吗?"
|
||||
@confirm="remove(record)"
|
||||
>
|
||||
<a class="ele-text-danger">删除</a>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</ele-pro-table>
|
||||
</div>
|
||||
<!-- 编辑弹窗 -->
|
||||
<MenuEdit
|
||||
v-model:visible="showEdit"
|
||||
:data="current"
|
||||
:parentId="parentId"
|
||||
@done="reload"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import type {
|
||||
ColumnItem,
|
||||
DatasourceFunction
|
||||
} from 'ele-admin-pro/es/ele-pro-table/types';
|
||||
import type { EleProTable } from 'ele-admin-pro';
|
||||
import MenuEdit from './menu-edit.vue';
|
||||
import { App, AppParam } from '@/api/dashboard/appstore/model';
|
||||
import { listApp, removeApp } from '@/api/dashboard/appstore';
|
||||
|
||||
const props = defineProps<{
|
||||
// 菜单类型
|
||||
menuType?: number;
|
||||
// 修改回显的数据
|
||||
data?: App | null;
|
||||
showView?: boolean;
|
||||
}>();
|
||||
|
||||
// 表格实例
|
||||
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
|
||||
|
||||
// 表格列配置
|
||||
const columns = ref<ColumnItem[]>([
|
||||
{
|
||||
title: '组件名称',
|
||||
key: 'appName',
|
||||
dataIndex: 'appName',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '路由地址',
|
||||
dataIndex: 'path'
|
||||
},
|
||||
{
|
||||
title: '组件路径',
|
||||
dataIndex: 'component'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
align: 'center',
|
||||
hideInSetting: true
|
||||
}
|
||||
]);
|
||||
|
||||
/* 表格数据源 */
|
||||
const datasource: DatasourceFunction = ({ page, limit, where, orders }) => {
|
||||
where.parentId = props.data?.appId;
|
||||
where.menuType = props.menuType;
|
||||
return listApp({ ...where, ...orders, page, limit });
|
||||
};
|
||||
|
||||
// 当前编辑数据
|
||||
const current = ref<App | null>(null);
|
||||
|
||||
// 是否显示编辑弹窗
|
||||
const showEdit = ref(false);
|
||||
|
||||
// 上级菜单id
|
||||
const parentId = ref<number>();
|
||||
|
||||
// 菜单数据
|
||||
// const menuData = ref<App[]>([]);
|
||||
|
||||
// 表格展开的行
|
||||
// const expandedRowKeys = ref<number[]>([]);
|
||||
|
||||
/* 表格渲染完成回调 */
|
||||
// const onDone: EleProTableDone<App> = ({ data }) => {
|
||||
// menuData.value = data;
|
||||
// };
|
||||
|
||||
/* 刷新表格 */
|
||||
const reload = (where?: AppParam) => {
|
||||
tableRef?.value?.reload({ where: where });
|
||||
};
|
||||
|
||||
/* 打开编辑弹窗 */
|
||||
const openEdit = (row?: App | null, id?: number) => {
|
||||
parentId.value = props.data?.appId;
|
||||
current.value = row ?? null;
|
||||
showEdit.value = true;
|
||||
};
|
||||
|
||||
/* 删除单个 */
|
||||
const remove = (row: App) => {
|
||||
if (row.children?.length) {
|
||||
message.error('请先删除子节点');
|
||||
return;
|
||||
}
|
||||
const hide = message.loading('请求中..', 0);
|
||||
removeApp(row.appId)
|
||||
.then((msg) => {
|
||||
hide();
|
||||
message.success(msg);
|
||||
reload();
|
||||
})
|
||||
.catch((e) => {
|
||||
hide();
|
||||
message.error(e.message);
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.data?.appId,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
reload();
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import * as MenuIcons from '@/layout/menu-icons';
|
||||
|
||||
export default {
|
||||
name: 'Authority',
|
||||
components: MenuIcons
|
||||
};
|
||||
</script>
|
||||
405
src/views/system/appstore/components/setting/setting.vue
Normal file
405
src/views/system/appstore/components/setting/setting.vue
Normal file
@@ -0,0 +1,405 @@
|
||||
<!-- 编辑弹窗 -->
|
||||
<template>
|
||||
<a-form
|
||||
:label-col="{ md: { span: 6 }, sm: { span: 4 }, xs: { span: 24 } }"
|
||||
:wrapper-col="{ md: { span: 18 }, sm: { span: 20 }, xs: { span: 24 } }"
|
||||
>
|
||||
<a-row :gutter="16">
|
||||
<a-col :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="应用分类" v-bind="validateInfos.appType">
|
||||
<a-select
|
||||
optionFilterProp="label"
|
||||
placeholder="请选择应用分类"
|
||||
allow-clear
|
||||
v-model:value="form.appType"
|
||||
>
|
||||
<template v-for="item in appTypeDict">
|
||||
<a-select-option :value="item.value">{{ item.label }}</a-select-option>
|
||||
</template>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="应用名称" v-bind="validateInfos.appName">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入应用名称"
|
||||
v-model:value="form.appName"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="应用入口" v-bind="validateInfos.component">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="/dashboard/workplace"
|
||||
v-model:value="form.component"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="应用标识" v-bind="validateInfos.appCode">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="goods"
|
||||
v-model:value="form.appCode"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="授权价格" v-bind="validateInfos.price">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="授权价格"
|
||||
v-model:value="form.price"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="排序号" v-role="'superAdmin'" v-bind="validateInfos.sortNumber">
|
||||
<a-input-number
|
||||
:min="0"
|
||||
:max="99999"
|
||||
class="ele-fluid"
|
||||
placeholder="请输入排序号"
|
||||
v-model:value="form.sortNumber"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-form-item
|
||||
label="应用简介"
|
||||
v-bind="validateInfos.comments"
|
||||
:label-col="{ md: { span: 3 }, sm: { span: 4 }, xs: { span: 24 } }"
|
||||
:wrapper-col="{ md: { span: 21 }, sm: { span: 20 }, xs: { span: 24 } }"
|
||||
>
|
||||
<a-textarea
|
||||
:rows="4"
|
||||
:maxlength="200"
|
||||
placeholder="请输入应用简介"
|
||||
v-model:value="form.comments"
|
||||
/>
|
||||
</a-form-item>
|
||||
<div style="margin-bottom: 22px">
|
||||
<a-divider />
|
||||
</div>
|
||||
<a-row :gutter="16">
|
||||
<a-col :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="应用图标" v-bind="validateInfos.appIcon">
|
||||
<ele-icon-picker
|
||||
v-model:value="form.appIcon"
|
||||
allow-clear
|
||||
placeholder="请选择图标"
|
||||
:disabled="form.appType === 1"
|
||||
:data="iconData"
|
||||
>
|
||||
<template #icon="{ icon }">
|
||||
<component :is="icon"/>
|
||||
</template>
|
||||
</ele-icon-picker>
|
||||
</a-form-item>
|
||||
<a-form-item label="下载地址" v-bind="validateInfos.downUrl">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="模块代码下载地址"
|
||||
v-model:value="form.downUrl"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="当前版本" v-bind="validateInfos.edition">
|
||||
<a-select
|
||||
optionFilterProp="label"
|
||||
placeholder="请选择版本"
|
||||
allow-clear
|
||||
v-model:value="form.edition"
|
||||
>
|
||||
<a-select-option value="正式版">正式版</a-select-option>
|
||||
<a-select-option value="开发版">开发版</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="应用状态">
|
||||
<a-switch
|
||||
checked-children="启用"
|
||||
un-checked-children="禁用"
|
||||
:checked="form.status === 0"
|
||||
@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>
|
||||
<div style="margin-bottom: 22px">
|
||||
<a-divider />
|
||||
</div>
|
||||
<a-button
|
||||
type="primary"
|
||||
class="ele-btn-icon"
|
||||
@click="save"
|
||||
>
|
||||
<span>保存</span>
|
||||
</a-button>
|
||||
</a-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {ref, reactive, watch} from 'vue';
|
||||
import {Form, message} from 'ant-design-vue';
|
||||
import type {RuleObject} from 'ant-design-vue/es/form';
|
||||
import iconData from 'ele-admin-pro/es/ele-icon-picker/icons';
|
||||
import {assignObject, isExternalLink} from 'ele-admin-pro';
|
||||
import {addApp, updateApp} from '@/api/dashboard/appstore';
|
||||
import type {App} from '@/api/dashboard/appstore/model';
|
||||
import {ItemType} from "ele-admin-pro/es/ele-image-upload/types";
|
||||
import {uploadFile} from "@/api/system/file";
|
||||
import {getDictionaryOptions} from "@/utils/common";
|
||||
|
||||
const useForm = Form.useForm;
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
const props = defineProps<{
|
||||
// 修改回显的数据
|
||||
data?: App | null;
|
||||
// 应用id
|
||||
appId?: number | 0;
|
||||
}>();
|
||||
|
||||
// 是否是修改
|
||||
const isUpdate = ref(false);
|
||||
// 提交状态
|
||||
const loading = ref(false);
|
||||
const icon = ref('');
|
||||
const appTypeDict = getDictionaryOptions('appstoreType');
|
||||
// 编辑器内容,双向绑定
|
||||
const content = ref('');
|
||||
|
||||
// 表单数据
|
||||
const form = reactive<App>({
|
||||
// 应用id
|
||||
appId: undefined,
|
||||
// 应用名称
|
||||
appName: '',
|
||||
// 上级id, 0是顶级
|
||||
parentId: 0,
|
||||
// 应用编号
|
||||
appCode: '',
|
||||
// 应用图标
|
||||
appIcon: '',
|
||||
// 应用类型
|
||||
appType: undefined,
|
||||
// 应用地址
|
||||
appUrl: '',
|
||||
// 下载地址
|
||||
downUrl: '',
|
||||
// 应用包名
|
||||
packageName: '',
|
||||
// 点击次数
|
||||
clicks: '',
|
||||
// 安装次数
|
||||
installs: '',
|
||||
// 应用介绍
|
||||
content: '',
|
||||
// 开发者(个人)
|
||||
developer: '官方',
|
||||
// 页面路径
|
||||
component: undefined,
|
||||
// 软件授权价格
|
||||
price: '',
|
||||
// 评分
|
||||
score: '',
|
||||
// 星级
|
||||
star: '',
|
||||
// 排序
|
||||
sortNumber: 100,
|
||||
// 备注
|
||||
comments: '',
|
||||
// 权限标识
|
||||
authority: '',
|
||||
// 打开位置
|
||||
target: '',
|
||||
// 是否隐藏, 0否, 1是(仅注册路由不显示在左侧菜单)
|
||||
hide: undefined,
|
||||
// 菜单侧栏选中的path
|
||||
active: '',
|
||||
// 其它路由元信息
|
||||
meta: '',
|
||||
// 版本
|
||||
edition: '开发版',
|
||||
// 版本号
|
||||
version: 'v1.0',
|
||||
// 创建时间
|
||||
createTime: '',
|
||||
// 状态
|
||||
status: 1,
|
||||
// 发布者
|
||||
userId: undefined,
|
||||
// 发布者昵称
|
||||
nickname: ''
|
||||
});
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive({
|
||||
appName: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请输入应用名称',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
component: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请输入组件路径',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
appType: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请选择应用分类',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
appCode: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请输入应用标识(英文)',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
comments: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请输入应用简介',
|
||||
}
|
||||
],
|
||||
sortNumber: [
|
||||
{
|
||||
required: true,
|
||||
type: 'number',
|
||||
message: '请输入排序号',
|
||||
}
|
||||
],
|
||||
price: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入软件授权价格',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
meta: [
|
||||
{
|
||||
type: 'string',
|
||||
trigger: 'blur',
|
||||
validator: async (_rule: RuleObject, 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();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const {resetFields, validate, validateInfos} = useForm(form, rules);
|
||||
|
||||
/* 保存编辑 */
|
||||
const save = () => {
|
||||
validate()
|
||||
.then(() => {
|
||||
loading.value = true;
|
||||
const appForm = {
|
||||
...form,
|
||||
parentId: form.parentId || 0,
|
||||
content: content.value
|
||||
};
|
||||
const saveOrUpdate = isUpdate.value ? updateApp : addApp;
|
||||
saveOrUpdate(appForm)
|
||||
.then((msg) => {
|
||||
loading.value = false;
|
||||
message.success(msg);
|
||||
updateVisible(false);
|
||||
emit('done');
|
||||
})
|
||||
.catch((e) => {
|
||||
loading.value = false;
|
||||
message.error(e.message);
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
});
|
||||
};
|
||||
|
||||
const onUpload = (d: ItemType) => {
|
||||
uploadFile(<File>d.file)
|
||||
.then((result) => {
|
||||
form.appIcon = result.path;
|
||||
message.success('上传成功');
|
||||
})
|
||||
.catch((e) => {
|
||||
message.error(e.message);
|
||||
});
|
||||
};
|
||||
|
||||
/* 更新visible */
|
||||
const updateVisible = (value: boolean) => {
|
||||
emit('update:visible', value);
|
||||
};
|
||||
|
||||
const updateHideValue = (value: boolean) => {
|
||||
form.status = value ? 0 : 1;
|
||||
};
|
||||
|
||||
const reload = () => {
|
||||
if (props.data) {
|
||||
const isExternal = isExternalLink(props.data.path);
|
||||
const isInner = isExternalLink(props.data.component);
|
||||
content.value = String(props.data.content);
|
||||
form.price = props.data.price;
|
||||
assignObject(form, {
|
||||
...props.data,
|
||||
openType: isExternal ? 2 : isInner ? 1 : 0
|
||||
});
|
||||
isUpdate.value = true;
|
||||
} else {
|
||||
isUpdate.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
if( props.appId ) {
|
||||
reload();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.data?.appId,
|
||||
(data) => {
|
||||
if (data) {
|
||||
reload();
|
||||
} else {
|
||||
resetFields();
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
<script lang="ts">
|
||||
import * as icons from '@ant-design/icons-vue';
|
||||
|
||||
export default {
|
||||
components: icons
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,268 @@
|
||||
<template>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-col="styleResponsive ? { sm: 5, xs: 24 } : { flex: '130px' }"
|
||||
:wrapper-col="styleResponsive ? { sm: 19, xs: 24 } : { flex: '1' }"
|
||||
>
|
||||
<a-form-item label="手机号码" name="phone">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="11"
|
||||
v-model:value="form.phone"
|
||||
placeholder="请输入手机号码"
|
||||
>
|
||||
<template #addonBefore> +86 </template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="验证码" name="code">
|
||||
<div class="login-input-group">
|
||||
<a-input
|
||||
placeholder="请输入验证码"
|
||||
v-model:value="form.code"
|
||||
:maxlength="6"
|
||||
allow-clear
|
||||
/>
|
||||
<a-button
|
||||
class="login-captcha"
|
||||
:disabled="!!countdownTime"
|
||||
@click="openImgCodeModal"
|
||||
>
|
||||
<span v-if="!countdownTime">发送验证码</span>
|
||||
<span v-else>已发送 {{ countdownTime }} s</span>
|
||||
</a-button>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
:wrapper-col="styleResponsive ? { sm: { offset: 5 } } : { offset: 4 }"
|
||||
style="margin-top: 24px"
|
||||
>
|
||||
<a-space size="middle">
|
||||
<a-button @click="back">上一步</a-button>
|
||||
<a-button type="primary" :loading="loading" @click="submit">
|
||||
下一步
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<!-- 编辑弹窗 -->
|
||||
<a-modal
|
||||
:width="340"
|
||||
:footer="null"
|
||||
title="发送验证码"
|
||||
v-model:visible="visible"
|
||||
>
|
||||
<div class="login-input-group" style="margin-bottom: 16px">
|
||||
<a-input
|
||||
v-model:value="imgCode"
|
||||
:maxlength="5"
|
||||
placeholder="请输入图形验证码"
|
||||
allow-clear
|
||||
@pressEnter="sendCode"
|
||||
/>
|
||||
<a-button class="login-captcha">
|
||||
<img alt="" :src="captcha" @click="changeCaptcha" />
|
||||
</a-button>
|
||||
</div>
|
||||
<a-button
|
||||
block
|
||||
size="large"
|
||||
type="primary"
|
||||
:loading="codeLoading"
|
||||
@click="sendCode"
|
||||
>
|
||||
立即发送
|
||||
</a-button>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import type { FormInstance, Rule } from 'ant-design-vue/es/form';
|
||||
import type { StepForm } from '../model';
|
||||
import { phoneReg } from 'ele-admin-pro';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { getCaptcha, sendSmsCaptcha } from '@/api/passport/login';
|
||||
import { getMobile } from '@/utils/common';
|
||||
import { addUser } from '@/api/system/user';
|
||||
import { User } from '@/api/system/user/model';
|
||||
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
const { styleResponsive } = storeToRefs(themeStore);
|
||||
|
||||
const props = defineProps<{
|
||||
// 修改回显的数据
|
||||
data?: StepForm | null;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done', data: StepForm): void;
|
||||
(e: 'back'): void;
|
||||
}>();
|
||||
|
||||
// 是否显示图形验证码弹窗
|
||||
const visible = ref(false);
|
||||
// 图形验证码
|
||||
const imgCode = ref('');
|
||||
// 发送验证码按钮loading
|
||||
const codeLoading = ref(false);
|
||||
// 验证码倒计时时间
|
||||
const countdownTime = ref(0);
|
||||
// 图形验证码地址
|
||||
const captcha = ref('');
|
||||
const text = ref('');
|
||||
// 验证码倒计时定时器
|
||||
let countdownTimer: number | null = null;
|
||||
const formRef = ref<FormInstance | null>(null);
|
||||
|
||||
/* 发送短信验证码 */
|
||||
const sendCode = () => {
|
||||
if (!imgCode.value) {
|
||||
message.error('请输入图形验证码');
|
||||
return;
|
||||
}
|
||||
if (text.value !== imgCode.value) {
|
||||
message.error('图形验证码不正确');
|
||||
return;
|
||||
}
|
||||
codeLoading.value = true;
|
||||
sendSmsCaptcha({ phone: form.phone })
|
||||
.then(() => {
|
||||
message.success('短信验证码发送成功, 请注意查收!');
|
||||
visible.value = false;
|
||||
codeLoading.value = false;
|
||||
countdownTime.value = 60;
|
||||
// 开始对按钮进行倒计时
|
||||
countdownTimer = window.setInterval(() => {
|
||||
if (countdownTime.value <= 1) {
|
||||
countdownTimer && clearInterval(countdownTimer);
|
||||
countdownTimer = null;
|
||||
}
|
||||
countdownTime.value--;
|
||||
}, 1000);
|
||||
})
|
||||
.catch((e) => {
|
||||
codeLoading.value = false;
|
||||
message.error(e.message);
|
||||
});
|
||||
};
|
||||
|
||||
// 提交状态
|
||||
const loading = ref(false);
|
||||
|
||||
// 表单数据
|
||||
const form = reactive<User>({
|
||||
phone: '',
|
||||
username: '',
|
||||
nickname: '',
|
||||
roles: [],
|
||||
organizationName: '',
|
||||
password: ''
|
||||
});
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive<Record<string, Rule[]>>({
|
||||
phone: [
|
||||
{
|
||||
pattern: phoneReg,
|
||||
message: '手机号格式不正确',
|
||||
type: 'string'
|
||||
}
|
||||
],
|
||||
code: [
|
||||
{
|
||||
required: true,
|
||||
message: '请填写短信验证码',
|
||||
type: 'string',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
/* 获取图形验证码 */
|
||||
const changeCaptcha = () => {
|
||||
// 这里演示的验证码是后端返回base64格式的形式, 如果后端地址直接是图片请参考忘记密码页面
|
||||
getCaptcha()
|
||||
.then((data) => {
|
||||
captcha.value = data.base64;
|
||||
// 实际项目后端一般会返回验证码的key而不是直接返回验证码的内容, 登录用key去验证, 你可以根据自己后端接口修改
|
||||
text.value = data.text;
|
||||
// 自动回填验证码, 实际项目去掉这个
|
||||
// form.code = data.text;
|
||||
})
|
||||
.catch((e) => {
|
||||
message.error(e.message);
|
||||
});
|
||||
};
|
||||
|
||||
/* 显示发送短信验证码弹窗 */
|
||||
const openImgCodeModal = () => {
|
||||
if (!form.phone) {
|
||||
message.error('请输入手机号码');
|
||||
return;
|
||||
}
|
||||
imgCode.value = '';
|
||||
changeCaptcha();
|
||||
visible.value = true;
|
||||
};
|
||||
|
||||
const submit = () => {
|
||||
if (!formRef.value) {
|
||||
return;
|
||||
}
|
||||
formRef.value
|
||||
?.validate()
|
||||
.then(() => {
|
||||
loading.value = true;
|
||||
const addData = {
|
||||
...form,
|
||||
username: props.data?.username,
|
||||
password: props.data?.password,
|
||||
nickname: getMobile(form.phone),
|
||||
roles: [{ roleId: 5 }]
|
||||
};
|
||||
addUser(addData)
|
||||
.then(() => {
|
||||
loading.value = false;
|
||||
message.success('注册成功');
|
||||
emit('done', form);
|
||||
})
|
||||
.catch((e) => {
|
||||
loading.value = false;
|
||||
message.error(e.message);
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
const back = () => {
|
||||
emit('back');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
/* 验证码 */
|
||||
.login-input-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
:deep(.ant-input-affix-wrapper) {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.login-captcha {
|
||||
width: 102px;
|
||||
margin-left: 10px;
|
||||
padding: 0;
|
||||
|
||||
& > img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,196 @@
|
||||
<template>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-col="styleResponsive ? { sm: 5, xs: 24 } : { flex: '130px' }"
|
||||
:wrapper-col="styleResponsive ? { sm: 19, xs: 24 } : { flex: '1' }"
|
||||
>
|
||||
<a-form-item label="账户类型" name="applyType">
|
||||
<a-radio-group v-model:value="form.applyType">
|
||||
<a-radio :value="0">个人账号</a-radio>
|
||||
<a-radio :value="1">企业账号</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="企业名称"
|
||||
name="organizationName"
|
||||
v-if="form.applyType === 1"
|
||||
>
|
||||
<a-input
|
||||
placeholder="请输入企业名称"
|
||||
v-model:value="form.organizationName"
|
||||
:maxlength="25"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<!-- <div style="margin-bottom: 22px" v-if="form.applyType === 1">-->
|
||||
<!-- <a-divider />-->
|
||||
<!-- </div>-->
|
||||
<a-form-item label="登录账号" name="username">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="16"
|
||||
v-model:value="form.username"
|
||||
placeholder="请输入登录账号"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="登录密码" name="password">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="20"
|
||||
type="password"
|
||||
v-model:value="form.password"
|
||||
placeholder="请输入密码"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="确认密码" name="password2">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="20"
|
||||
type="password"
|
||||
v-model:value="form.password2"
|
||||
placeholder="请输入确认密码"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<!-- <a-form-item label=" " name="register" v-if="form.applyType === 1">-->
|
||||
<!-- 我已阅读并同意 <a>用户协议</a> 和 <a>隐私权政策</a>-->
|
||||
<!-- </a-form-item>-->
|
||||
<a-form-item
|
||||
:wrapper-col="styleResponsive ? { sm: { offset: 5 } } : { offset: 4 }"
|
||||
>
|
||||
<a-button type="primary" :loading="loading" @click="submit">
|
||||
下一步
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import type { FormInstance, Rule } from 'ant-design-vue/es/form';
|
||||
import type { StepForm } from '../model';
|
||||
import type { RuleObject } from 'ant-design-vue/es/form';
|
||||
import { createCode } from '@/utils/common';
|
||||
import type { User } from '@/api/system/user/model';
|
||||
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
const { styleResponsive } = storeToRefs(themeStore);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done', data: StepForm): void;
|
||||
}>();
|
||||
|
||||
const formRef = ref<FormInstance | null>(null);
|
||||
|
||||
// 提交状态
|
||||
const loading = ref(false);
|
||||
|
||||
// 表单数据
|
||||
const form = reactive<User>({
|
||||
applyType: 0,
|
||||
phone: '',
|
||||
username: createCode(),
|
||||
nickname: '',
|
||||
organizationName: '',
|
||||
password: '',
|
||||
password2: ''
|
||||
});
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive<Record<string, Rule[]>>({
|
||||
applyType: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择账户类型',
|
||||
type: 'number',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
username: [
|
||||
{
|
||||
required: true,
|
||||
message: '请填写登录账号',
|
||||
type: 'string',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
password: [
|
||||
{
|
||||
required: true,
|
||||
message: '密码组合应该包含字母、数字,并且长度不少于8位',
|
||||
min: 8,
|
||||
type: 'string',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
password2: [
|
||||
{
|
||||
required: true,
|
||||
validator: async (_rule: RuleObject, value: string) => {
|
||||
if (!value) {
|
||||
return Promise.reject('请再次输入新密码');
|
||||
}
|
||||
if (value !== form.password) {
|
||||
return Promise.reject('两次输入密码不一致');
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
],
|
||||
organizationName: [
|
||||
{
|
||||
required: true,
|
||||
validator: async (_rule: RuleObject, value: string) => {
|
||||
if (!value) {
|
||||
return Promise.reject('请输入企业名称');
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
/* 步骤一提交 */
|
||||
const submit = () => {
|
||||
if (!formRef.value) {
|
||||
return;
|
||||
}
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
loading.value = true;
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
emit('done', form);
|
||||
}, 300);
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
/* 验证码 */
|
||||
.login-input-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
:deep(.ant-input-affix-wrapper) {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.login-captcha {
|
||||
width: 102px;
|
||||
margin-left: 10px;
|
||||
padding: 0;
|
||||
|
||||
& > img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-result
|
||||
title="注册成功"
|
||||
status="success"
|
||||
sub-title="请妥善保管您的账号,如忘记可通过手机短信验证码找回密码"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import type { StepForm } from '../model';
|
||||
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
const { styleResponsive } = storeToRefs(themeStore);
|
||||
|
||||
defineProps<{
|
||||
// 修改回显的数据
|
||||
data?: StepForm | null;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'back'): void;
|
||||
}>();
|
||||
|
||||
const back = () => {
|
||||
emit('back');
|
||||
};
|
||||
</script>
|
||||
112
src/views/system/appstore/components/step/index.vue
Normal file
112
src/views/system/appstore/components/step/index.vue
Normal file
@@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<ele-modal
|
||||
:width="740"
|
||||
:visible="visible"
|
||||
:confirm-loading="loading"
|
||||
:title="`安装应用`"
|
||||
:body-style="{ paddingBottom: '8px' }"
|
||||
@update:visible="updateVisible"
|
||||
:footer="null"
|
||||
@ok="save"
|
||||
>
|
||||
<a-page-header :ghost="false" :title="data.appName">
|
||||
<div class="ele-text-secondary" v-html="data.comments"> </div>
|
||||
</a-page-header>
|
||||
<div class="ele-body">
|
||||
<a-card :bordered="false">
|
||||
<div style="max-width: 700px; margin: 0 auto">
|
||||
<div style="margin: 10px 0 30px 0">
|
||||
<a-steps
|
||||
:current="active"
|
||||
direction="horizontal"
|
||||
:responsive="styleResponsive"
|
||||
>
|
||||
<a-step title="第一步" description="下载更新包" />
|
||||
<a-step title="第二步" description="数据初始化" />
|
||||
<a-step title="第三步" description="安装成功" />
|
||||
</a-steps>
|
||||
</div>
|
||||
<div class="step-next" v-if="active === 0">
|
||||
<a-button type="primary" @click="onDone()"> 下一步 </a-button>
|
||||
</div>
|
||||
<div class="step-next" v-if="active === 1">
|
||||
<a-button type="primary" @click="onNext()"> 下一步 </a-button>
|
||||
</div>
|
||||
<div class="step-next" v-if="active === 2">
|
||||
<a-button type="primary" @click="onSuccess()"> 完成 </a-button>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</div>
|
||||
</ele-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import StepEdit from './components/step-edit.vue';
|
||||
import StepConfirm from './components/step-confirm.vue';
|
||||
import StepSuccess from './components/step-success.vue';
|
||||
import type { StepForm } from './model';
|
||||
import { App } from '@/api/dashboard/appstore/model';
|
||||
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
const { styleResponsive } = storeToRefs(themeStore);
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
defineProps<{
|
||||
// 弹窗是否打开
|
||||
visible: boolean;
|
||||
// 修改回显的数据
|
||||
data?: App | null;
|
||||
// 上级应用id
|
||||
parentId?: number;
|
||||
}>();
|
||||
|
||||
// 选中步骤
|
||||
const active = ref(0);
|
||||
|
||||
//
|
||||
const form = reactive<StepForm>({});
|
||||
|
||||
//
|
||||
const onDone = (data: StepForm) => {
|
||||
Object.assign(form, data);
|
||||
active.value = 1;
|
||||
};
|
||||
|
||||
//
|
||||
const onNext = (data: StepForm) => {
|
||||
Object.assign(form, data);
|
||||
active.value = 2;
|
||||
};
|
||||
|
||||
const onSuccess = () => {};
|
||||
|
||||
//
|
||||
const onBack = () => {
|
||||
active.value = 0;
|
||||
};
|
||||
|
||||
/* 更新visible */
|
||||
const updateVisible = (value: boolean) => {
|
||||
emit('update:visible', value);
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'FormStep'
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.step-next {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
11
src/views/system/appstore/components/step/model/index.ts
Normal file
11
src/views/system/appstore/components/step/model/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export interface StepForm {
|
||||
applyType?: number;
|
||||
apply?: string;
|
||||
username?: string;
|
||||
password?: string;
|
||||
password2?: string;
|
||||
phone?: string;
|
||||
code?: string;
|
||||
nickname?: string;
|
||||
companyName?: string;
|
||||
}
|
||||
Reference in New Issue
Block a user