This commit is contained in:
xuziqiang
2024-05-15 17:29:42 +08:00
commit d0155dbe3c
7296 changed files with 1832517 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
<template>
<ns-application />
</template>
<script lang="ts" type="module">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'App',
});
</script>
<style>
#app {
width: 100%;
height: 100%;
}
</style>

View File

@@ -0,0 +1,50 @@
/***
*配置接口 格式 module:Array<resource>
*/
export const apiModule = {
pension: [
'User',
'CurrentUser',
'Gaffer',
// 'Organizational',
// 'Device',
// 'Region',
// 'Owner',
// 'AllList',
// 'VisitorRecord',
// 'Room',
// 'Device',
// 'CommunityVisitor',
// 'AccessControlGroup',
// 'AccessControlLeft',
// 'AccessControlRight',
// 'AccessControlRightIdentity',
// 'Visitor',
// 'DeviceListByDeviceCategory',
// 'Perimeter',
// 'AccessControlRightVisitorIdentity',
// 'BedChoose',
// 'Gateway',
// 'FileUpload',
// 'User',
// 'CurrentUser',
// 'Organizational',
// 'Device',
// 'Region',
// 'Owner',
// 'AllList',
// 'VisitorRecord',
// 'Room',
// 'Device',
// 'CommunityVisitor',
// 'AccessControlGroup',
// 'AccessControlLeft',
// 'AccessControlRight',
// 'AccessControlRightIdentity',
// 'Visitor',
// 'DeviceListByDeviceCategory',
// 'BedChoose',
// 'Perimeter',
// 'OrganizationConfig',
],
};

View File

@@ -0,0 +1,15 @@
import { http } from '/nerv-lib/paas';
enum Api {
USER_LOGIN = '/api/passport/objs/login', //用户登录
USER_INFO = '/api/webui/webui/objs/PassportUserInfo', //获取用户信息
}
export const userLogin = (data: RoomListModel) => http.post(Api.USER_LOGIN, data);
export const userInfo = () => http.get(Api.USER_INFO);
/**
* @description 用户登录
* @property `[fatherRegionUuid]` 父级区域唯一标识
*/
interface RoomListModel {
data: string;
}

View File

@@ -0,0 +1,6 @@
/** @format */
export const appConfig = {
projectType: 'web',
timeout: 120 * 1000,
};

View File

@@ -0,0 +1,2 @@
import { appConfig } from '/@/config/app.config';
export { appConfig };

View File

@@ -0,0 +1,16 @@
import { createApp } from 'vue';
import App from '/@/App.vue';
import { paasInit } from '/nerv-lib/paas';
import { apiModule } from '/@/api';
import { appConfig } from '/@/config';
// import '/@/theme/theme.scss';
const modules = undefined;
const app = createApp(App);
paasInit({
app,
apiModule,
appConfig,
modules,
});
app.mount('#app');

View File

@@ -0,0 +1,35 @@
echo "=====================================================create====================================================="
#!/usr/bin/env bash
function create() {
if [ -d "$nervui_app_home" ];then
echo "$nervui_app_home exists!"
else
echo "start mkdir $nervui_app_home"
mkdir -p "$nervui_app_home"
fi
pkg_file_name=${pkg_url##*/}
pkg_file_path="$nervui_app_home$pkg_file_name"
echo "start download $pkg_url"
curl -L -o $pkg_file_path $pkg_url
echo "start install $pkg_file_path"
tar -xf $pkg_file_path -C $nervui_app_home
}
if [ "$pkg_url" == "" ]; then
echo {\"error\":\"pkg_url is empty\"}
exit 1
elif [ "$nervui_app_home" == "" ]; then
echo {\"error\":\"nervui_app_home is empty\"}
exit 1
else
create
fi

View File

@@ -0,0 +1 @@
echo "=====================================================delete====================================================="

View File

@@ -0,0 +1 @@
echo "=====================================================setup====================================================="

View File

@@ -0,0 +1 @@
echo "=====================================================start====================================================="

View File

@@ -0,0 +1 @@
echo "=====================================================stop====================================================="

View File

@@ -0,0 +1,30 @@
{
"name":"/nervui-notify",
"operations": [
{
"name": "Create",
"type": "shell",
"implementor": "create.sh"
},
{
"name": "Delete",
"type": "shell",
"implementor": "delete.sh"
},
{
"name": "Setup",
"type": "shell",
"implementor": "setup.sh"
},
{
"name": "Start",
"type": "shell",
"implementor": "start.sh"
},
{
"name": "Stop",
"type": "shell",
"implementor": "stop.sh"
}
]
}

View File

@@ -0,0 +1,64 @@
{
"name": "/nervui-operations",
"version": 1,
"inputs": [
{
"name": "server_ip",
"type": "string",
"required":true,
"description":"应用安装IP地址",
"inputType":"ipSelectType"
},
{
"name": "version",
"type": "string",
"required":true,
"description":"软件版本",
"inputType":"versionSelectType"
},
{
"name": "install_dir",
"type": "string",
"required": true,
"defaultValue": "/data",
"inputType": "textInputType",
"description": "安装目录"
}
],
"nodes": [
{
"name": "nervui-operations",
"type": "/nerv/nerv-orchestrator/cluster/Nervui",
"parameters": [
{
"name": "file_repository",
"value": "${nerv_file_repository}"
},
{
"name": "install_dir",
"value": "${install_dir}"
},
{
"name": "pkg_url",
"value": "/api/pkg/nerv/nervui-operations/${version}/nervui-operations-${version}.tgz"
}
],
"dependencies": [
{
"type": "contained",
"target": "host"
}
]
},
{
"name": "host",
"type": "/nerv/nerv-orchestrator/compute/Host",
"parameters": [
{
"name": "address",
"value": "${server_ip}"
}
]
}
]
}

View File

@@ -0,0 +1,11 @@
/** @format */
const RootRoute = {
path: '/root',
name: 'root',
redirect: '/operations',
meta: {
title: 'Root',
},
};
export default RootRoute;

View File

@@ -0,0 +1,169 @@
/** @format */
import { NsViewContent, NsViewDetail, NsViewSideNav } from '/nerv-lib/paas';
const SideNav = () => Promise.resolve(NsViewSideNav);
const Base = () => Promise.resolve(NsViewContent);
const operations = {
path: '/operations',
name: 'operations',
component: SideNav,
redirect: { name: 'Tenant' },
meta: {
sideMenus: {
title: '运营管理',
name: 'tenant',
menus: [
{
name: 'Tenant',
label: '租户管理',
url: '/operations/Tenant',
module: 'vm',
app: 'tenant',
},
{
name: 'Approval',
label: '审批中心',
url: '/operations/Approval',
module: 'vm',
app: 'tenant',
},
],
},
},
children: [
// 租户管理
{
path: 'Tenant',
name: 'TenantMangment',
component: Base,
// redirect: { name: 'TenantList' },
children: [
// 租户管理列表
{
path: 'list',
name: 'Tenant',
meta: { app: 'tenant' },
component: () => import('/@/view/Tenant/list.vue'),
},
{
path: 'add',
name: 'TenantAdd',
meta: { app: 'tenant' },
component: () => import('/@/view/Tenant/add.vue'),
},
{
path: ':id/edit',
name: 'TenantEdit',
meta: { app: 'tenant' },
component: () => import('/@/view/Tenant/edit.vue'),
},
],
},
// 审批中心
{
path: 'Approval',
name: 'ApprovalCenter',
component: Base,
children: [
// 租户管理列表
{
path: 'list',
name: 'Approval',
meta: { app: 'tenant' },
component: () => import('/@/view/Approval/list.vue'),
},
{
path: ':id',
name: 'ApprovalDetail',
component: NsViewDetail,
meta: { app: 'tenant' },
// component: () => import('/@/view/Approval/detail.vue'),
props: {
title: '查看',
showBack: true,
api: '/api/passport/passport/objs/ApprovalCenter/:id',
detail: [
{
title: '企业认证',
items: [
{
label: '企业名称',
name: 'companyName',
},
{
label: '营业执照注册号',
name: 'businessLicense',
},
{
label: '营业执照所在地',
name: 'licenseAddress',
},
{
label: '营业执照期限',
name: 'licenseTimeLimit',
},
{
label: '常用地址',
name: 'address',
},
{
label: '联系电话',
name: 'contactNumber',
},
{
label: '营业执照副本扫描件',
name: 'licensephoto',
type: 'image',
},
],
},
{
title: '法人认证',
items: [
{
label: '法定代表人归属地',
name: 'corporateplace',
},
{
label: '法定代表人真实姓名',
name: 'corporateRealName',
},
{
label: '法定代表人身份证号码',
name: 'corporateIDNumber',
},
{
label: '身份证类型',
name: 'IDCardType',
format: (value: any) => ['二代身份证', '临时身份证'][value],
},
{
label: '身份证正面',
name: 'IDCardFront',
type: 'image',
},
{
label: '身份证反面',
name: 'IDCardBack',
type: 'image',
},
{
label: '邮箱',
name: 'email',
},
{
label: '手机号码',
name: 'phoneNumber',
},
],
},
],
},
},
],
},
],
};
export default operations;

View File

View File

@@ -0,0 +1,2 @@
@import "variable";
@import "global";

View File

@@ -0,0 +1,10 @@
// @layout-header-background:url(/asset/image/header_background.png) no-repeat;
// // @layout-sider-background:url(/asset/image/sider_background.png) no-repeat;
// @layout-nav-background:url(/asset/image/sider_background.png) no-repeat;
// @layout-header-color: #FFFFFF;
// @layout-header-link-color: #FFFFFF;
// @layout-nav-color: #FFFFFF;
// @layout-nav-check-color: #FFFFFF;
// @primary-color: #2C68FF; // 全局主色
// @link-color: #2C68FF; // 链接色
// @layout-nav-hover:linear-gradient(90deg, #2C68FF 0%, rgba(44, 104, 255, 0.4) 100%);

View File

@@ -0,0 +1,85 @@
<!-- @format -->
<template>
<ns-view-add-form
:schemas="formSchema"
:model="data"
formLayout="vertical"
title="添加token"
api="/api/passport/passport/objs/Credential" />
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { useRouter } from 'vue-router';
export default defineComponent({
name: 'TokenEdit',
setup() {
const router = useRouter();
const { view } = router.currentRoute.value.query;
const data = ref({});
let formSchema = ref([
{
label: '凭据类型',
field: 'type',
component: 'NsInputText',
defaultValue: view,
},
{
label: '凭据名称',
field: 'name',
component: 'NsInput',
componentProps: {
placeholder: '请输入',
},
rules: [
{
required: true,
validator: async (rule, value) => {
if (!value) {
return Promise.reject('凭据名称必填');
}
if (!/^[A-Za-z\d\u4e00-\u9fa5][A-Za-z\d\u4e00-\u9fa5\._-]{0,31}$/.test(value)) {
return Promise.reject(
'支持中文/字母/数字/特殊字符:点(.)短横线(-下划线_输入长度1~32位不能以特殊字符开头不能只包含特殊字符',
);
}
},
},
],
},
{
field: 'token',
label: 'token',
component: 'NsInput',
componentProps: {
placeholder: '请输入',
},
rules: [
{
required: true,
validator: async (rule, value) => {
if (!value) {
return Promise.reject('token必填');
}
if (!/^[A-Za-z][A-Za-z\d_-]{3,31}$/.test(value)) {
return Promise.reject(
'支持字母,数字,短横线(-)和下划线(_)输入长度4-32位不能以特殊字符或数字开头不能只包含特殊字符',
);
}
},
},
],
},
]);
return {
data,
formSchema,
};
},
beforeCreate() {},
});
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,84 @@
<!-- @format -->
<template>
<ns-view-detail
title="查看详情"
:api="`/api/passport/passport/objs/ApprovalCenter/${$router.currentRoute.value.params.id}`"
:detail="detail" />
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { dateUtil } from '/nerv-lib/util/date-util';
import { useRouter } from 'vue-router';
export default defineComponent({
name: 'SoftwareVersionManageDetail',
setup() {
const router = useRouter();
const detail = [
{
title: '基本信息',
items: [
{
label: '软件名称',
name: 'appName',
},
{
label: '版本号',
name: 'versionCode',
},
{
name: 'downUrl',
label: '下载地址',
},
{
label: '安装包',
name: 'appPackageName',
ifShow: ({ platformId }) => platformId == 1,
},
{
label: '下载方式',
name: 'downSource',
ifShow: ({ platformId }) => platformId == 1,
format: (value) => ['浏览器下载', '应用商城下载', '应用内下载'][value],
},
// {
// name: 'md5',
// label: 'MD5码',
// },
// {
// name: 'uniqueCode',
// label: '唯一code',
// },
{
name: 'updateFlag',
label: '是否需要更新',
format: (value, data) => {
return ['否', '是'][value];
},
},
{
name: 'updateType',
label: '更新类型',
format: (value, data) => {
return ['建议更新', '强制更新'][value];
},
},
{
name: 'versionState',
label: '是否最新版本',
format: (value, data) => {
return ['否', '是'][value];
},
},
{
name: 'upgradeTips',
label: '更新日志',
},
],
},
];
return { detail };
},
});
</script>

View File

@@ -0,0 +1,277 @@
<!-- @format -->
<template>
<page-title title="审批中心" />
<a-tabs class="tabs" v-model:activeKey="activeKey">
<a-tab-pane v-for="(item, index) in tabBucket" :key="index" :tab="item.title">
<ns-view-list-table v-bind="item.tableConfig" ref="nsTableRef" />
</a-tab-pane>
</a-tabs>
</template>
<script lang="ts">
import { defineComponent, ref, watch } from 'vue';
import { dateUtil } from '/nerv-lib/util/date-util';
import { http } from '/nerv-lib/util';
import { NsMessage } from '/nerv-lib/component/message';
export default defineComponent({
name: 'ApproveList',
setup() {
const nsTableRef = ref();
const activeKey = ref();
// 缓存选中tab
if (sessionStorage.getItem('ACTIVE_TAB_KEY')) {
activeKey.value = Number(sessionStorage.getItem('ACTIVE_TAB_KEY'));
}
watch(
() => activeKey.value,
(val) => {
sessionStorage.setItem('ACTIVE_TAB_KEY', `${val}`);
},
);
const actionReq = (api) => {
// 指定刷新那个api列表
if (Object.prototype.toString.apply(nsTableRef.value) === '[object Array]') {
nsTableRef.value.map((item, index) => {
if (item.api == api) {
nsTableRef.value[index]?.nsTableRef.reload();
}
});
return;
}
nsTableRef.value?.nsTableRef.reload();
};
let approval_time = ref('');
let approval_select = ref('pass,unpass');
const joinFunc = (select, time) => {
approvalConfig.params.where = `${
select.indexOf(',') !== -1
? 'approval_result = ? or approval_result = ?'
: 'approval_result = ?'
}${time && ' and created_at >= ? and created_at <= ?'}`;
approvalConfig.params.values = `${select}${time}`;
};
const tableConfig = {
api: '/api/passport/passport/objs/ApprovalCenter?order=created_at desc',
params: {
where: 'approval_result = ?',
values: 'unknown',
},
formConfig: {
keySearch: false,
schemas: [
{
field: 'timeRange',
label: '申请时间',
component: 'NsRangePicker',
componentProps: {
valueFormat: 'YYYY-MM-DD HH:mm:ss',
format: 'YYYY-MM-DD HH:mm:ss',
showTime: true,
'show-time': {
hideDisabledOptions: true,
defaultValue: [
dateUtil('00:00:00', 'HH:mm:ss'),
dateUtil('23:59:59', 'HH:mm:ss'),
],
},
onChange: (_, [start, end]) => {
tableConfig.params.where = `approval_result = ?${
start && ' and created_at >= ? and created_at <= ?'
}`;
tableConfig.params.values = `unknown${start && `,${start},${end}`}`;
actionReq(tableConfig.api);
},
},
},
],
},
showTitle: false,
rowSelection: null,
scroll: { x: '100%' },
resultField: 'data',
columns: [
{
title: '企业名称',
dataIndex: 'companyName',
width: '34%',
},
{
title: '申请类型',
dataIndex: 'approvalType',
customRender: ({ value }) => ['企业认证'][value],
width: '33%',
},
{
title: '申请时间',
dataIndex: 'CreatedAt',
customRender: ({ value }: any) => dateUtil(value).format('YYYY-MM-DD HH:mm:ss'),
width: '33%',
},
],
columnActions: {
title: '操作',
actions: [
{
label: '审批',
name: 'ApprovalApproval',
route: ':ID',
},
],
},
rowKey: 'ID',
};
const approvalConfig = {
api: '/api/passport/passport/objs/ApprovalCenter',
params: {
where: 'approval_result = ? or approval_result = ?',
order: 'created_at desc',
values: 'pass,unpass',
},
showTitle: false,
rowSelection: null,
resultField: 'data',
scroll: { x: '100%' },
columns: [
{
title: '企业名称',
width: '25%',
dataIndex: 'companyName',
},
{
title: '申请类型',
width: '25%',
dataIndex: 'approvalType',
customRender: ({ value }) => ['企业认证'][value],
},
{
title: '审批结果',
dataIndex: 'approvalResult',
width: '25%',
customRender: ({ value }) => {
enum status {
pass = '审批通过',
unpass = '审批拒绝',
unknown = '未审批',
}
return status[value];
},
},
{
title: '申请时间',
dataIndex: 'CreatedAt',
width: '25%',
customRender: ({ value }: any) => dateUtil(value).format('YYYY-MM-DD HH:mm:ss'),
},
],
columnActions: {
title: '操作',
actions: [
{
label: '查看',
name: 'ApprovalDetail',
route: ':ID',
},
],
},
formConfig: {
keySearch: false,
schemas: [
{
field: 'values',
label: '类型',
component: 'NsSelect',
defaultValue: 'pass,unpass',
componentProps: {
placeholder: '请选择',
options: [
{
label: '全部',
value: 'pass,unpass',
},
{
label: '通过',
value: 'pass',
},
{
label: '拒绝',
value: 'unpass',
},
],
onChange: (value) => {
approval_select.value = value;
joinFunc(approval_select.value, approval_time.value);
actionReq(approvalConfig.api);
},
},
},
{
field: 'timeRange',
label: '申请时间',
component: 'NsRangePicker',
componentProps: {
valueFormat: 'YYYY-MM-DD HH:mm:ss',
format: 'YYYY-MM-DD HH:mm:ss',
showTime: true,
'show-time': {
hideDisabledOptions: true,
defaultValue: [
dateUtil('00:00:00', 'HH:mm:ss'),
dateUtil('23:59:59', 'HH:mm:ss'),
],
},
onChange: (_, [start, end]) => {
approval_time.value = start && `,${start},${end}`;
joinFunc(approval_select.value, approval_time.value);
actionReq(approvalConfig.api);
},
},
},
],
},
rowKey: 'ID',
};
const tabBucket = [
{
title: '待审批',
tableConfig,
},
{
title: '已审批',
tableConfig: approvalConfig,
},
];
return {
nsTableRef,
tableConfig,
tabBucket,
activeKey,
};
},
methods: {},
});
</script>
<style lang="less" scoped>
.tabs {
margin: 0 24px;
}
:deep(.ns-table .ns-table-main) {
padding: 0px;
}
:deep(.ant-tabs-nav) {
margin: 0 !important;
}
</style>

View File

@@ -0,0 +1,162 @@
<!-- @format -->
<template>
<my-add-form
:schemas="formSchema"
:model="data"
formLayout="vertical"
title="创建租户"
api="/api/passport/passport/objs/Tenant"
:confirmContent="`将发送验证邮件到${data.tenantEmail}以便激活完成租户开通激活链接将在24小时以内有效确定发送吗`"
confirmApi="/api/passport/passport/objs/PreCreateTenant" />
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { useRouter } from 'vue-router';
import myAddForm from '/@/view/components/add-form.vue';
export default defineComponent({
name: 'TenantAdd',
components: { myAddForm },
setup() {
const router = useRouter();
const { view } = router.currentRoute.value.query;
const data = ref({});
let formSchema = ref([
{
label: '项目',
field: 'projectID',
component: 'NsSelectApi',
componentProps: {
api: {
url: '/api/passport/passport/objs/Authorization/CheckAuthorization',
method: 'POST',
},
dropdownReload: true,
showSearch: true,
filterOption: (input: string, option: any) => {
return option.name.toLowerCase().indexOf(input.toLowerCase()) >= 0;
},
resultField: 'projects',
labelField: 'projectname',
valueField: 'projectid',
immediate: true,
placeholder: '请选择',
},
rules: [
{
required: true,
message: '请选择项目',
},
],
},
{
label: '租户名',
field: 'tenant',
component: 'NsInput',
componentProps: {
placeholder: '请输入',
},
rules: [
{
required: true,
message: '请输入租户名',
},
{
pattern: /^[A-Za-z][A-Za-z\d_-]{5,31}$/,
message: '以字母开头输入6-32个字符只能包含字母数字短横线(-)和下划线(_)',
},
],
},
{
label: '别名',
field: 'alias',
component: 'NsInput',
componentProps: {
placeholder: '请输入',
},
rules: [
{
pattern: /^[A-Za-z][A-Za-z\d_-]{5,31}$/,
message: '以字母开头输入6-32个字符只能包含字母数字短横线(-)和下划线(_)',
},
],
},
{
label: '域名',
field: 'cloudDomain',
component: 'NsSelectApi',
componentProps: {
api: '/api/passport/passport/objs/GetCloudDomainList?pageSize=100',
dropdownReload: true,
showSearch: true,
filterOption: (input: string, option: any) => {
return option.name.toLowerCase().indexOf(input.toLowerCase()) >= 0;
},
resultField: 'data',
labelField: 'cloudDomain',
valueField: 'cloudDomain',
immediate: true,
placeholder: '请选择',
},
rules: [
{
required: true,
message: '请选择',
},
],
},
{
label: '邮箱地址',
field: 'tenantEmail',
component: 'NsInput',
componentProps: {
placeholder: '请输入',
},
rules: [
{
required: true,
message: '请输入邮箱地址',
},
{
pattern: /^\w+@[a-z0-9]+\.[a-z]{2,4}$/,
message: '请输入正确的邮箱地址',
},
],
},
{
field: 'tenantType',
component: 'NsRadioGroup',
defaultValue: 2,
label: '租户类型',
componentProps: {
radioType: 'radio',
options: [
// {
// label: '私有云租户',
// value: 1,
// },
{
label: '公有云租户',
value: 2,
},
],
},
rules: [
{
required: true,
message: '请选择',
},
],
},
]);
return {
data,
formSchema,
};
},
beforeCreate() {},
});
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,99 @@
<!-- @format -->
<template>
<div>
<ns-view-edit-form
:schemas="formSchema"
:model="data"
formLayout="vertical"
title="编辑租户"
api="/api/passport/passport/objs/Tenant"
:getApi="`/api/passport/passport/objs/Tenant/${$router.currentRoute.value.params.id}`" />
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, ref } from 'vue';
import { useRouter } from 'vue-router';
import { NsMessage } from '/nerv-lib/component/message';
import { useNavigate } from '/nerv-lib/use/use-navigate';
import { http } from '/nerv-lib/util/http';
export default defineComponent({
name: 'TenantAdd',
setup() {
const data = ref({});
let formSchema = ref([
{
field: 'projectID',
component: 'NsInput',
show: false,
},
{
field: 'ID',
component: 'NsInput',
show: false,
},
{
label: '项目',
field: 'projectName',
component: 'NsInputText',
},
{
label: '租户名',
field: 'tenant',
component: 'NsInputText',
},
{
label: '别名',
field: 'alias',
component: 'NsInput',
componentProps: {
placeholder: '请输入',
},
rules: [
{
pattern: /^[A-Za-z][A-Za-z\d_-]{5,31}$/,
message: '以字母开头输入6-32个字符只能包含字母数字短横线(-)和下划线(_)',
},
],
},
{
label: '域名',
field: 'cloudDomain',
component: 'NsInputText',
},
{
label: '邮箱地址',
field: 'tenantEmail',
component: 'NsInputText',
},
{
field: 'tenantType',
component: 'NsRadioGroup',
defaultValue: 2,
label: '租户类型',
componentProps: {
radioType: 'radio',
options: [
// {
// label: '私有云租户',
// value: 1,
// },
{
label: '公有云租户',
value: 2,
},
],
},
},
]);
return {
data,
formSchema,
};
},
beforeCreate() {},
});
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,156 @@
<!-- @format -->
<template>
<div>
<ns-view-list-table v-bind="tableConfig" rowKey="ID" ref="nsTableRef" />
</div>
</template>
<script lang="ts">
import { defineComponent, ref, computed, unref } from 'vue';
import { dateUtil } from '/nerv-lib/util/date-util';
import { useRouter } from 'vue-router';
export default defineComponent({
name: 'TenantPage',
setup() {
const router = useRouter();
const nsTableRef = ref();
const tableConfig = {
api: '/api/passport/passport/objs/Tenant',
title: '租户列表',
params: {
order: 'created_at desc',
},
rowSelection: null,
resultField: 'data',
columns: [
{
title: '项目',
dataIndex: 'projectName',
},
{
title: '租户名',
dataIndex: 'tenant',
},
{
title: '别名',
dataIndex: 'alias',
},
{
title: '租户类型',
dataIndex: 'tenantType',
customRender: ({ value }) => ['未知', '私有云', '公有云'][value],
},
{
title: '域名',
dataIndex: 'cloudDomain',
},
{
title: '邮箱',
dataIndex: 'tenantEmail',
},
{
title: '状态',
dataIndex: 'tenantStatus',
transform: {
pipe: 'state',
statusField: ({ tenantStatus }: object) => {
enum status {
'unknown',
'success',
'error',
}
return status[tenantStatus];
},
textField: ({ tenantStatus }: object) => {
enum status {
'未激活',
'激活',
'禁用',
}
return status[tenantStatus];
},
},
},
{
title: '创建人',
dataIndex: 'CreatedBy',
},
{
title: '创建时间',
dataIndex: 'CreatedAt',
sorter: true,
customRender: ({ value }: any) => dateUtil(value).format('YYYY-MM-DD HH:mm:ss'),
},
],
columnActions: {
title: '操作',
// autoMergeAction: false,
// actionNumber: 3,
// textNumber: 5,
actions: [
{
label: '编辑',
name: 'TenantEdit',
route: ':ID/edit',
},
{
label: '删除',
name: 'TenantRemove',
confirm: {
content: '删除后不可恢复,您确定要删除吗?',
},
isReload: true,
api: { method: 'delete', url: '/api/passport/passport/objs/Tenant/:ID' },
ifShow: ({ tenantStatus }: any) => tenantStatus !== 1,
},
{
label: '禁用',
name: 'TenantRemove',
confirm: {
content: '确定要禁用该租户吗?禁用后该租户将无法登录平台!',
},
isReload: true,
dynamicParams: ['projectID', 'tenant'],
api: { method: 'post', url: '/api/passport/passport/objs/Tenant/Forbidden' },
ifShow: ({ tenantStatus }: any) => tenantStatus === 1,
},
{
label: '启用',
name: 'TenantRemove',
isReload: true,
dynamicParams: ['projectID', 'tenant'],
api: { method: 'post', url: '/api/passport/passport/objs/Tenant/Activate' },
ifShow: ({ tenantStatus }: any) => tenantStatus === 2,
},
],
},
headerActions: [
{
label: '创建租户',
type: 'primary',
name: 'TenantAdd',
route: 'add',
},
],
formConfig: {},
rowKey: 'ID',
};
return {
nsTableRef,
dateUtil,
tableConfig,
};
},
methods: {},
});
</script>
<style lang="less" scoped>
:deep(.status-field > .anticon) {
// 解决pipe 处理状态隐藏问号icon
display: none;
}
</style>

View File

@@ -0,0 +1,118 @@
<template>
<div :key="'addForm_' + $route.name">
<a-spin :spinning="isLoading">
<page-title :title="title" />
<a-page-header>
<template #extra>
<!-- todo 隐藏取消-->
<a-button @click="navigateBack">返回</a-button>
<a-button type="primary" @click="submit" :disabled="!nsFormElRef?.validateResult">
保存</a-button
>
</template>
</a-page-header>
<div class="add-form">
<ns-form ref="nsFormElRef" v-bind="getBindValue">
<template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data || {}"></slot>
</template>
</ns-form>
</div>
</a-spin>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, ref } from 'vue';
import { http } from '/nerv-lib/util/http';
import { NsMessage } from '/nerv-lib/component/message';
import { formProps } from '/nerv-lib/component/form/form/props';
import { PropTypes } from '/nerv-lib/util/type';
import { useNavigate } from '/nerv-lib/use/use-navigate';
import { useApi, HttpRequestConfig } from '/nerv-lib/use/use-api';
import { useRoute } from 'vue-router';
import { NsModal } from '/nerv-lib/component/modal';
export default defineComponent({
name: 'NsViewAddForm',
props: {
...formProps,
api: PropTypes.string,
title: PropTypes.string,
confirmContent: String,
confirmApi: String,
},
setup(props, { attrs }) {
const nsFormElRef = ref();
const isLoading = ref(false);
const { navigateBack } = useNavigate();
const { httpRequest } = useApi();
const route = useRoute();
function submit() {
const { confirmApi, confirmContent, api } = props;
fetch(confirmApi).then(() => {
NsModal.confirm({
title: '确认发送邮箱',
content: confirmContent,
okText: '确认',
cancelText: '取消',
onOk: () =>
fetch(api).then(() => {
NsMessage.success('操作成功', 1, () => {
navigateBack();
});
}),
});
});
}
function fetch(api) {
return new Promise((resolve, reject) => {
nsFormElRef.value
.triggerSubmit()
.then((data: Recordable) => {
isLoading.value = true;
// const { api } = props;
const { params } = route;
const requestConfig: HttpRequestConfig = { method: 'POST' };
httpRequest({ api, params: data, pathParams: params, requestConfig })
.then((res) => {
isLoading.value = false;
resolve(res);
})
.catch(() => {
isLoading.value = false;
});
})
.catch((err) => reject(err));
});
}
const getBindValue = computed(() => ({
...attrs,
...props,
}));
return { nsFormElRef, submit, getBindValue, isLoading, navigateBack };
},
});
</script>
<style lang="less" scoped>
.ant-page-header {
padding: 0 24px;
margin-bottom: 15px;
}
:deep(.ant-page-header-heading-extra) {
margin-right: auto !important;
margin-left: 0;
}
.add-form {
padding: 0 24px;
}
:deep(.ant-table-thead
> tr
> th:not(:last-child):not(.ant-table-selection-column):not(.ant-table-row-expand-icon-cell):not([colspan])::before) {
display: none;
}
</style>