Browse Source

租户管理

ZaiZai 1 year ago
parent
commit
3cf2a99f12

+ 1 - 1
package.json

@@ -21,7 +21,7 @@
         "dayjs": "^1.11.10",
         "echarts": "^5.4.3",
         "element-plus": "2.4.4",
-        "hc-vue3-ui": "^2.8.3",
+        "hc-vue3-ui": "^2.8.5",
         "js-base64": "^3.7.5",
         "js-fast-way": "^0.3.8",
         "js-md5": "^0.8.3",

+ 16 - 0
src/api/modules/index.js

@@ -0,0 +1,16 @@
+import { HcApi } from '../request/index'
+
+//get
+export const get = (form) => HcApi({
+    url: form.url,
+    method: 'get',
+    params: form.params,
+}, false)
+
+//post
+export const post = (form) => HcApi({
+    url: form.url,
+    method: 'post',
+    params: form.params,
+    data: form.data,
+}, false)

+ 37 - 0
src/api/modules/system/tenant-package.js

@@ -0,0 +1,37 @@
+import { HcApi } from '../../request/index'
+
+export default {
+    async page(form) {
+        return HcApi({
+            url: '/api/blade-system/tenant-package/list',
+            method: 'get',
+            params: form,
+        }, false)
+    },
+    //菜单详情
+    async detail(id) {
+        return HcApi({
+            url: '/api/blade-system/tenant-package/detail',
+            method: 'get',
+            params: {
+                id,
+            },
+        }, false)
+    },
+    async submit(form) {
+        return HcApi({
+            url: '/api/blade-system/tenant-package/submit',
+            method: 'post',
+            data: form,
+        }, false)
+    },
+    async del(ids) {
+        return HcApi({
+            url: '/api/blade-system/tenant-package/remove',
+            method: 'post',
+            params: {
+                ids,
+            },
+        }, false)
+    },
+}

+ 92 - 0
src/views/system/modules/tenant/data-source.vue

@@ -0,0 +1,92 @@
+<template>
+    <hc-new-dialog v-model="isShow" title="租户数据源配置" widths="400px" is-footer-center @close="dialogClose">
+        <el-form ref="formRef" :model="formModel" :rules="formRules" label-position="left" label-width="auto">
+            <el-form-item label="数据源:" prop="datasourceId">
+                <el-select v-model="formModel.datasourceId" placeholder="请选择数据源" filterable block>
+                    <el-option v-for="item in sourceList" :key="item.id" :label="item.name" :value="item.id" />
+                </el-select>
+            </el-form-item>
+        </el-form>
+        <template #footer>
+            <el-button hc-btn @click="dialogClose">取消</el-button>
+            <el-button hc-btn type="primary" :loading="submitLoading" @click="dialogSubmit">提交</el-button>
+        </template>
+    </hc-new-dialog>
+</template>
+
+<script setup>
+import { ref, watch } from 'vue'
+import { deepClone, getArrValue } from 'js-fast-way'
+import mainApi from '~api/system/tenant'
+import { get } from '~api'
+
+const props = defineProps({
+    info: {
+        type: Object,
+        default: () => ({}),
+    },
+})
+
+//事件
+const emit = defineEmits(['finish'])
+
+//双向绑定
+// eslint-disable-next-line no-undef
+const isShow = defineModel('modelValue', {
+    default: false,
+})
+
+//监听可否编辑
+const dataInfo = ref(props.info)
+watch(() => props.info, (data) => {
+    dataInfo.value = data
+}, { immediate: true, deep: true })
+
+//监听显示
+watch(isShow, (val) => {
+    if (val) {
+        initFormData()
+    }
+})
+
+//菜单表单
+const formRef = ref(null)
+const formModel = ref({})
+const formRules = {
+    datasourceId: [{ required: true, message: '请选择数据源', trigger: 'change' }],
+}
+
+//初始化表单
+const sourceList = ref([])
+const initFormData = async () => {
+    submitLoading.value = false
+    formModel.value = deepClone(dataInfo.value)
+    //获取数据源列表
+    const { data } = await get({
+        url: '/api/blade-develop/datasource/select',
+    })
+    sourceList.value = getArrValue(data)
+}
+
+//提交表单
+const submitLoading = ref(false)
+const dialogSubmit = async () => {
+    submitLoading.value = true
+    const { error, code, msg } = await mainApi.datasource(formModel.value)
+    if (!error && code === 200) {
+        submitLoading.value = false
+        window?.$message?.success('操作成功')
+        emit('finish')
+        dialogClose()
+    } else {
+        window?.$message?.error(msg ?? '操作失败')
+    }
+}
+
+//关闭弹窗
+const dialogClose = () => {
+    isShow.value = false
+    submitLoading.value = false
+    formModel.value = {}
+}
+</script>

+ 226 - 0
src/views/system/modules/tenant/package-data.vue

@@ -0,0 +1,226 @@
+<template>
+    <hc-new-dialog v-model="isShow" widths="800px" is-table :footer="false" :is-close="false">
+        <template #header>
+            <div class="hc-flex">
+                <div class="hc-flex flex-1">
+                    <div class="w-60">
+                        <hc-search-input v-model="searchForm.packageName" placeholder="产品包名" @search="getTableData" />
+                    </div>
+                </div>
+                <div class="hc-flex">
+                    <el-button hc-btn type="primary" @click="addClick">新增</el-button>
+                    <el-button hc-btn type="danger" @click="delClick">删除</el-button>
+                    <el-button hc-btn @click="configDialogClose">关闭</el-button>
+                </div>
+            </div>
+        </template>
+        <hc-table
+            ref="tableRef" :column="tableColumn" :datas="tableData" :loading="tableLoading"
+            is-new is-check :check-style="{ width: 29 }" :index-style="{ width: 60 }"
+            @selection-change="tableCheckChange"
+        >
+            <template #action="{ row }">
+                <el-link type="warning" @click="editRowClick(row)">修改</el-link>
+                <el-link type="danger" @click="delRowClick(row)">删除</el-link>
+            </template>
+        </hc-table>
+    </hc-new-dialog>
+
+    <!-- 新增/修改 -->
+    <hc-new-dialog v-model="isDialogShow" widths="450px" is-footer-center :title="dialogTitle" @close="dialogClose">
+        <el-form ref="formRef" :model="formModel" :rules="formRules" label-position="top" label-width="auto">
+            <el-form-item label="产品包名:" prop="packageName">
+                <el-input v-model="formModel.packageName" placeholder="请输入产品包名称" />
+            </el-form-item>
+            <el-form-item label="菜单列表:" prop="menuIds">
+                <el-tree-select
+                    v-model="formModel.menuIds" :data="menuData" :render-after-expand="false" node-key="id"
+                    :props="menuTreeProps" multiple show-checkbox clearable block placeholder="请选择菜单列表"
+                />
+            </el-form-item>
+            <el-form-item label="备注:">
+                <el-input v-model="formModel.remark" placeholder="请输入备注" />
+            </el-form-item>
+        </el-form>
+        <template #footer>
+            <el-button hc-btn @click="dialogClose">取消</el-button>
+            <el-button hc-btn type="primary" :loading="submitLoading" @click="dialogSubmit">提交</el-button>
+        </template>
+    </hc-new-dialog>
+</template>
+
+<script setup>
+import { nextTick, ref, watch } from 'vue'
+import { arrToId, formValidate, getArrValue } from 'js-fast-way'
+import { delMessage } from '~uti/tools'
+import mainApi from '~api/system/tenant-package'
+import menuApi from '~api/system/menu'
+
+//事件
+const emit = defineEmits(['close'])
+
+//双向绑定
+// eslint-disable-next-line no-undef
+const isShow = defineModel('modelValue', {
+    default: false,
+})
+
+//搜索表单
+const searchForm = ref({
+    packageName: null, current: 1, size: 200,
+})
+
+//监听显示
+watch(isShow, (val) => {
+    if (val) {
+        getTableData()
+        getMenuTreeApi()
+    }
+})
+
+//表格数据
+const tableRef = ref(null)
+const tableColumn = ref([
+    { key: 'packageName', name: '产品包名' },
+    { key: 'remark', name: '备注' },
+    { key: 'action', name: '操作', width: 100, align: 'center' },
+])
+
+//获取表格数据
+const tableLoading = ref(false)
+const tableData = ref([])
+const getTableData = async () => {
+    tableData.value = []
+    tableLoading.value = true
+    const { data } = await mainApi.page(searchForm.value)
+    tableData.value = getArrValue(data['records'])
+    tableLoading.value = false
+}
+
+//表格被选择
+const tableCheckKeys = ref([])
+const tableCheckChange = (rows) => {
+    tableCheckKeys.value = rows
+}
+
+//新增/修改 弹窗
+const isDialogShow = ref(false)
+const dialogTitle = ref('')
+
+//菜单表单
+const formRef = ref(null)
+const formModel = ref({})
+const formRules = {
+    packageName: {
+        required: true,
+        trigger: 'blur',
+        message: '请输入产品包名称',
+    },
+    menuIds: {
+        required: true,
+        trigger: 'blur',
+        message: '请选择菜单',
+    },
+}
+
+//菜单列表
+const menuData = ref([])
+const getMenuTreeApi = async () => {
+    const { data } = await menuApi.getMenuTree()
+    menuData.value = getArrValue(data)
+}
+//菜单树
+const menuTreeProps = {
+    label: 'title',
+}
+
+//新增
+const addClick = () => {
+    dialogTitle.value = '新增产品包'
+    formModel.value = {}
+    //显示表单弹窗
+    nextTick(() => {
+        isDialogShow.value = true
+    })
+}
+
+//修改菜单
+const editRowClick = (row) => {
+    formModel.value = {}
+    dialogTitle.value = '修改产品包'
+    formModel.value = { ...row }
+    formModel.value.menuIds = row?.menuId?.split(',') ?? []
+    //显示表单弹窗
+    nextTick(() => {
+        isDialogShow.value = true
+    })
+}
+
+//删除菜单
+const delRowClick = (row) => {
+    delMessage(async () => {
+        const { code, msg } = await mainApi.del(row.id)
+        if (code === 200) {
+            window.$message.success('删除成功')
+            getTableData().then()
+        } else {
+            window.$message.error(msg ?? '删除失败')
+        }
+    })
+}
+
+//批量删除菜单
+const delClick = () => {
+    const rows = tableCheckKeys.value
+    if (rows.length <= 0) {
+        window.$message.warning('请选择要删除的菜单')
+        return false
+    }
+    //确认删除菜单
+    delMessage(async () => {
+        const ids = arrToId(rows)
+        const { code, msg } = await mainApi.del(ids)
+        if (code === 200) {
+            window.$message.success('删除成功')
+            getTableData().then()
+        } else {
+            window.$message.error(msg ?? '删除失败')
+        }
+    })
+}
+
+//提交表单
+const submitLoading = ref(false)
+const dialogSubmit = async () => {
+    const formRes = await formValidate(formRef.value)
+    if (!formRes) return false
+    submitLoading.value = true
+    //处理数据
+    const form = formModel.value
+    form.menuId = form.menuIds?.join(',') ?? ''
+    //发起请求
+    const { error, code, msg } = await mainApi.submit(formModel.value)
+    if (!error && code === 200) {
+        submitLoading.value = false
+        dialogClose()
+        window?.$message?.success('操作成功')
+        getTableData().then()
+    } else {
+        window?.$message?.error(msg ?? '操作失败')
+    }
+}
+
+//关闭弹窗
+const dialogClose = () => {
+    isDialogShow.value = false
+    submitLoading.value = false
+    formModel.value = {}
+}
+
+//关闭字典配置弹窗
+const configDialogClose = () => {
+    isShow.value = false
+    tableData.value = []
+    emit('close')
+}
+</script>

+ 87 - 0
src/views/system/modules/tenant/setting.vue

@@ -0,0 +1,87 @@
+<template>
+    <hc-new-dialog v-model="isShow" title="租户授权配置" widths="400px" is-footer-center @close="dialogClose">
+        <el-form ref="formRef" :model="formModel" :rules="formRules" label-position="left" label-width="auto">
+            <el-form-item label="账号额度:">
+                <el-input-number v-model="formModel.accountNumber" block controls-position="right" />
+            </el-form-item>
+            <el-form-item label="过期时间:">
+                <el-date-picker
+                    v-model="formModel.expireTime" type="datetime" class="block"
+                    format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
+                />
+            </el-form-item>
+        </el-form>
+        <template #footer>
+            <el-button hc-btn @click="dialogClose">取消</el-button>
+            <el-button hc-btn type="primary" :loading="submitLoading" @click="dialogSubmit">提交</el-button>
+        </template>
+    </hc-new-dialog>
+</template>
+
+<script setup>
+import { ref, watch } from 'vue'
+import { deepClone } from 'js-fast-way'
+import mainApi from '~api/system/tenant'
+
+const props = defineProps({
+    info: {
+        type: Object,
+        default: () => ({}),
+    },
+})
+
+//事件
+const emit = defineEmits(['finish'])
+
+//双向绑定
+// eslint-disable-next-line no-undef
+const isShow = defineModel('modelValue', {
+    default: false,
+})
+
+//监听可否编辑
+const dataInfo = ref(props.info)
+watch(() => props.info, (data) => {
+    dataInfo.value = data
+}, { immediate: true, deep: true })
+
+//监听显示
+watch(isShow, (val) => {
+    if (val) {
+        initFormData()
+    }
+})
+
+//菜单表单
+const formRef = ref(null)
+const formModel = ref({})
+const formRules = {}
+
+//初始化表单
+const initFormData = () => {
+    submitLoading.value = false
+    formModel.value = deepClone(dataInfo.value)
+}
+
+//提交表单
+const submitLoading = ref(false)
+const dialogSubmit = async () => {
+    submitLoading.value = true
+    const { error, code, msg } = await mainApi.setting(formModel.value)
+    if (!error && code === 200) {
+        submitLoading.value = false
+        window?.$message?.success('操作成功')
+        emit('finish')
+        dialogClose()
+    } else {
+        window?.$message?.error(msg ?? '操作失败')
+    }
+}
+
+//关闭弹窗
+const dialogClose = () => {
+    isShow.value = false
+    submitLoading.value = false
+    formModel.value = {}
+}
+</script>

+ 88 - 4
src/views/system/tenant.vue

@@ -1,10 +1,10 @@
 <template>
     <hc-new-card>
         <template #header>
-            <el-button hc-btn type="success">授权配置</el-button>
-            <el-button hc-btn type="success">数据源配置</el-button>
-            <el-button hc-btn type="warning">产品包配置</el-button>
-            <el-button hc-btn type="warning">产品包管理</el-button>
+            <el-button hc-btn type="success" :disabled="tableCheckKeys.length <= 0" @click="handleSetting">授权配置</el-button>
+            <el-button hc-btn type="success" :disabled="tableCheckKeys.length <= 0" @click="handleDatasource">数据源配置</el-button>
+            <el-button hc-btn type="warning" :disabled="tableCheckKeys.length <= 0" @click="handlePackage">产品包配置</el-button>
+            <el-button hc-btn type="warning" @click="handlePackageSetting">产品包管理</el-button>
         </template>
         <template #extra>
             <el-button hc-btn type="primary" @click="addClick">新增</el-button>
@@ -95,6 +95,15 @@
             </template>
         </hc-new-dialog>
 
+        <!-- 授权配置 -->
+        <HcSettingVue v-model="isSettingShow" :info="settingInfo" @finish="settingFinish" />
+
+        <!-- 数据源配置 -->
+        <HcDataSource v-model="isSourceShow" :info="sourceInfo" @finish="sourceFinish" />
+
+        <!-- 产品包管理 -->
+        <HcPackageData v-model="isPackageDataShow" />
+
         <!-- 上传文件 -->
         <hc-upload-file ref="uploadRef" :echo-params="uploadParams" :options="uploadOptions" @success="uploadSuccess" />
     </hc-new-card>
@@ -103,6 +112,9 @@
 <script setup>
 import { nextTick, onActivated, ref } from 'vue'
 import { arrToId, formValidate, getArrValue } from 'js-fast-way'
+import HcSettingVue from './modules/tenant/setting.vue'
+import HcDataSource from './modules/tenant/data-source.vue'
+import HcPackageData from './modules/tenant/package-data.vue'
 import { delMessage } from '~uti/tools'
 import { getHeader } from 'hc-vue3-ui'
 import mainApi from '~api/system/tenant'
@@ -324,4 +336,76 @@ const dialogClose = () => {
     submitLoading.value = false
     formModel.value = {}
 }
+
+//授权配置
+const isSettingShow = ref(false)
+const settingInfo = ref({})
+const handleSetting = () => {
+    const rows = tableCheckKeys.value
+    if (rows.length <= 0) {
+        window.$message.warning('请选择要授权的租户')
+        return false
+    }
+    //处理数据
+    settingInfo.value = {
+        accountNumber: rows[0].accountNumber,
+        expireTime: rows[0].expireTime,
+        ids: arrToId(rows),
+    }
+    //显示表单弹窗
+    nextTick(() => {
+        isSettingShow.value = true
+    })
+}
+//授权配置完成
+const settingFinish = () => {
+    isSettingShow.value = false
+    getTableData()
+}
+
+//数据源配置
+const isSourceShow = ref(false)
+const sourceInfo = ref({})
+const handleDatasource = () => {
+    const rows = tableCheckKeys.value
+    if (rows.length <= 0) {
+        window.$message.warning('请选择要授权的租户')
+        return false
+    }
+    if (rows.length > 1) {
+        window.$message.warning('只能选择一个租户')
+        return false
+    }
+    //处理数据
+    sourceInfo.value = { id: rows[0].id }
+    //显示表单弹窗
+    nextTick(() => {
+        isSourceShow.value = true
+    })
+}
+//数据源配置完成
+const sourceFinish = () => {
+    isSourceShow.value = false
+    getTableData()
+}
+
+
+//产品包配置
+const handlePackage = () => {
+    const rows = tableCheckKeys.value
+    if (rows.length <= 0) {
+        window.$message.warning('请选择要授权的租户')
+        return false
+    }
+    if (rows.length > 1) {
+        window.$message.warning('只能选择一个租户')
+        return false
+    }
+}
+
+//产品包管理
+const isPackageDataShow = ref(false)
+const handlePackageSetting = () => {
+    isPackageDataShow.value = true
+}
 </script>

+ 4 - 4
yarn.lock

@@ -1915,10 +1915,10 @@ has-flag@^4.0.0:
   resolved "http://47.110.251.215:9000/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
   integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
 
-hc-vue3-ui@^2.8.3:
-  version "2.8.3"
-  resolved "http://47.110.251.215:9000/hc-vue3-ui/-/hc-vue3-ui-2.8.3.tgz#1ef51ebc3f06ea4a2b1f870aa7ae83f1cf5b5832"
-  integrity sha512-lSgR/uXm5LXLyct89WewXMonw81PQCCrjnhQP/4Sj0O2rdWwkdU6YOrNj5djEv2l48khUuF/798UoZnuUEEj6A==
+hc-vue3-ui@^2.8.5:
+  version "2.8.5"
+  resolved "http://47.110.251.215:9000/hc-vue3-ui/-/hc-vue3-ui-2.8.5.tgz#cf91f5d8c522ec3f57fcdc818fdddfb39e6b8a04"
+  integrity sha512-/mgo6VRBgyFPznlxq6VAY1fqDC8sosQG3a7N54gUpTahXUn1rl6gaU9AjnQXQQgF4d3tf+bbuL1XF+gJL8wSyw==
 
 human-signals@^2.1.0:
   version "2.1.0"