Merge branch 'main' of github.com:maxkb-dev/maxkb

This commit is contained in:
shaohuzhang1 2023-12-08 18:11:21 +08:00
commit 6e31965976
10 changed files with 325 additions and 92 deletions

View File

@ -0,0 +1,63 @@
import { Result } from '@/request/Result'
import { get, post, postStream, del, put } from '@/request/index'
import { type Ref } from 'vue'
const prefix = '/application'
/**
* API_KEY列表
* @param applicaiton_id
*/
const getAPIKey: (applicaiton_id: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
applicaiton_id,
loading
) => {
return get(`${prefix}/${applicaiton_id}/api_key`, undefined, loading)
}
/**
* API_KEY
* @param applicaiton_id
*/
const postAPIKey: (applicaiton_id: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
applicaiton_id,
loading
) => {
return post(`${prefix}/${applicaiton_id}/api_key`, {}, undefined, loading)
}
/**
* API_KEY
* @param applicaiton_id api_key_id
*/
const delAPIKey: (
applicaiton_id: String,
api_key_id: String,
loading?: Ref<boolean>
) => Promise<Result<boolean>> = (applicaiton_id, api_key_id, loading) => {
return del(`${prefix}/${applicaiton_id}/api_key/${api_key_id}`, undefined, undefined, loading)
}
/**
* API_KEY
* @param applicaiton_id,api_key_id
* data {
* is_active: boolean
* }
*/
const putAPIKey: (
applicaiton_id: string,
api_key_id: String,
data: any,
loading?: Ref<boolean>
) => Promise<Result<any>> = (applicaiton_id, api_key_id, data, loading) => {
return put(`${prefix}/${applicaiton_id}/api_key/${api_key_id}`, data, undefined, loading)
}
export default {
getAPIKey,
postAPIKey,
delAPIKey,
putAPIKey
}

View File

@ -111,17 +111,6 @@ const getApplicationDataset: (
return get(`${prefix}/${applicaiton_id}/list_dataset`, undefined, loading)
}
/**
* API_KEY列表
* @param applicaiton_id
*/
const getAPIKey: (applicaiton_id: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
applicaiton_id,
loading
) => {
return get(`${prefix}/${applicaiton_id}/api_key`, undefined, loading)
}
/**
* AccessToken
* @param applicaiton_id
@ -133,6 +122,21 @@ const getAccessToken: (applicaiton_id: string, loading?: Ref<boolean>) => Promis
return get(`${prefix}/${applicaiton_id}/access_token`, undefined, loading)
}
/**
* AccessToken
* @param applicaiton_id
* data {
* "is_active": true
* }
*/
const putAccessToken: (
applicaiton_id: string,
data: any,
loading?: Ref<boolean>
) => Promise<Result<any>> = (applicaiton_id, data, loading) => {
return put(`${prefix}/${applicaiton_id}/access_token`, data, undefined, loading)
}
/**
*
* @param
@ -235,8 +239,8 @@ export default {
delApplication,
getApplicationDetail,
getApplicationDataset,
getAPIKey,
getAccessToken,
putAccessToken,
postAppAuthentication,
getProfile,
putChatVote

View File

@ -141,8 +141,8 @@ export const post: (
*/
export const put: (
url: string,
params?: unknown,
data?: unknown,
params?: unknown,
loading?: NProgress | Ref<boolean>
) => Promise<Result<any>> = (url, data, params, loading) => {
return promise(request({ url: url, method: 'put', data, params }), loading)

View File

@ -35,7 +35,7 @@ const applicationRouter = {
parentPath: '/application/:id',
parentName: 'ApplicationDetail'
},
component: () => import('@/views/application/AppOverview.vue')
component: () => import('@/views/applicaiton-overview/index.vue')
},
{
path: 'setting',

View File

@ -0,0 +1,124 @@
<template>
<el-dialog title="API Key" v-model="dialogVisible" width="800">
<el-button type="primary" class="mb-16" @click="createApiKey"> 创建 </el-button>
<el-table :data="apiKey" class="mb-16" :loading="loading">
<el-table-column prop="secret_key" label="API Key">
<template #default="{ row }">
<span class="vertical-middle lighter break-all">
{{ row.secret_key }}
</span>
<el-button type="primary" text @click="copyClick(row.secret_key)">
<AppIcon iconName="app-copy"></AppIcon>
</el-button>
</template>
</el-table-column>
<el-table-column label="状态" width="100">
<template #default="{ row }">
<div @click.stop>
<el-switch size="small" v-model="row.is_active" @change="changeState($event, row)" />
</div>
</template>
</el-table-column>
<el-table-column prop="name" label="创建日期" width="170">
<template #default="{ row }">
{{ datetimeFormat(row.create_time) }}
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="80">
<template #default="{ row }">
<el-tooltip effect="dark" content="删除" placement="top">
<el-button type="primary" text @click="deleteApiKey(row)">
<el-icon><Delete /></el-icon>
</el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
import { useRoute } from 'vue-router'
import { copyClick } from '@/utils/clipboard'
import overviewApi from '@/api/application-overview'
import { datetimeFormat } from '@/utils/time'
import { MsgSuccess, MsgConfirm } from '@/utils/message'
import useStore from '@/stores'
const route = useRoute()
const {
params: { id }
} = route
const { application } = useStore()
const emit = defineEmits(['addData'])
const dialogVisible = ref<boolean>(false)
const loading = ref(false)
const apiKey = ref<any>(null)
watch(dialogVisible, (bool) => {
if (!bool) {
apiKey.value = null
}
})
function deleteApiKey(row: any) {
MsgConfirm(
`是否删除API Key${row.secret_key} ?`,
`删除后无法使用该 API Key 调用接口,请谨慎操作`,
{
confirmButtonText: '删除',
confirmButtonClass: 'danger'
}
)
.then(() => {
overviewApi.delAPIKey(id as string, row.id, loading).then(() => {
MsgSuccess('删除成功')
getApiKeyList()
})
})
.catch(() => {})
}
function changeState(bool: Boolean, row: any) {
const obj = {
is_active: bool
}
const str = bool ? '启用成功' : '禁用成功'
overviewApi.putAPIKey(id as string, row.id, obj, loading).then((res) => {
MsgSuccess(str)
getApiKeyList()
})
}
function createApiKey() {
overviewApi.postAPIKey(id as string, loading).then((res) => {
getApiKeyList()
})
}
const open = () => {
getApiKeyList()
dialogVisible.value = true
}
function getApiKeyList() {
overviewApi.getAPIKey(id as string, loading).then((res) => {
apiKey.value = res.data
})
}
defineExpose({ open })
</script>
<style lang="scss" scope>
.embed-dialog {
.code {
color: var(--app-text-color) !important;
background: var(--app-layout-bg-color);
font-weight: 400;
font-size: 13px;
white-space: pre;
height: 180px;
}
}
</style>

View File

@ -2,7 +2,7 @@
<LayoutContainer header="概览">
<div class="main-calc-height p-24">
<h4 class="title-decoration-1 mb-16">应用信息</h4>
<el-card shadow="never" class="overview-card">
<el-card shadow="never" class="overview-card" v-loading="loading">
<div class="title flex align-center">
<AppAvatar
v-if="detail?.name"
@ -20,15 +20,17 @@
<div class="flex">
<el-text type="info">公开访问链接</el-text>
<el-switch
v-model="accessToken.is_active"
class="ml-8"
size="small"
inline-prompt
active-text="开"
inactive-text="关"
@change="changeState"
/>
</div>
<div class="mt-4">
<div class="mt-4 url-height">
<span class="vertical-middle lighter break-all">
{{ shareUrl }}
</span>
@ -36,40 +38,46 @@
<el-button type="primary" text @click="copyClick(shareUrl)">
<AppIcon iconName="app-copy"></AppIcon>
</el-button>
<el-button @click="getAccessToken" type="primary" text style="margin-left: 1px">
<el-icon><RefreshRight /></el-icon>
</el-button>
</div>
<div class="mt-16">
<el-button type="primary"><a :href="shareUrl" target="_blank">演示</a></el-button>
<el-button @click="openDialog"> 嵌入第三方 </el-button>
<div>
<el-button :disabled="!accessToken?.is_active" type="primary"><a :href="shareUrl" target="_blank">演示</a></el-button>
<el-button :disabled="!accessToken?.is_active" @click="openDialog"> 嵌入第三方 </el-button>
</div>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" class="mt-16">
<div class="flex">
<el-text type="info">API访问凭据</el-text>
</div>
<div class="mt-4">
<div class="mt-4 url-height">
<span class="vertical-middle lighter break-all">
API Key: OGZmZThlZjYyYzU2MWE1OTlkYTVjZTBi
{{ apiUrl }}
</span>
<el-button type="primary" text>
<el-button type="primary" text @click="copyClick(apiUrl)">
<AppIcon iconName="app-copy"></AppIcon>
</el-button>
</div>
<div class="mt-16">
<el-button @click="openDialog"> 获取密钥 </el-button>
<div>
<el-button @click="openAPIKeyDialog"> API Key </el-button>
</div>
</el-col>
</el-row>
</el-card>
</div>
<EmbedDialog ref="EmbedDialogRef" />
<APIKeyDialog ref="APIKeyDialogRef" />
</LayoutContainer>
</template>
<script setup lang="ts">
import { reactive, ref, watch, onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import EmbedDialog from './component/EmbedDialog.vue'
import APIKeyDialog from './component/APIKeyDialog.vue'
import applicationApi from '@/api/application'
import EmbedDialog from './components/EmbedDialog.vue'
import { MsgSuccess, MsgConfirm } from '@/utils/message'
import { copyClick } from '@/utils/clipboard'
import useStore from '@/stores'
const { application } = useStore()
@ -79,27 +87,37 @@ const {
params: { id }
} = route as any
const apiUrl = window.location.origin + '/doc'
const APIKeyDialogRef = ref()
const EmbedDialogRef = ref()
const shareUrl = ref('')
const accessToken = ref('')
const accessToken = ref<any>({})
const detail = ref<any>(null)
const apiKey = ref<any>(null)
const loading = ref(false)
function openDialog() {
EmbedDialogRef.value.open(accessToken.value)
}
function getAccessToken() {
application.asyncGetAccessToken(id, loading).then((res: any) => {
accessToken.value = res?.data?.access_token
shareUrl.value = application.location + res?.data?.access_token
function changeState(bool: Boolean) {
const obj = {
is_active: bool
}
const str = bool ? '启用成功' : '禁用成功'
applicationApi.putAccessToken(id as string, obj, loading).then((res) => {
MsgSuccess(str)
getDetail()
})
}
function getApiKey() {
applicationApi.getAPIKey(id, loading).then((res) => {
apiKey.value = res.data
function openAPIKeyDialog() {
APIKeyDialogRef.value.open()
}
function openDialog() {
EmbedDialogRef.value.open(accessToken.value?.access_token)
}
function getAccessToken() {
application.asyncGetAccessToken(id, loading).then((res: any) => {
accessToken.value = res?.data
shareUrl.value = application.location + res?.data?.access_token
})
}
@ -110,7 +128,6 @@ function getDetail() {
}
onMounted(() => {
getDetail()
getApiKey()
getAccessToken()
})
</script>
@ -122,5 +139,8 @@ onMounted(() => {
right: 16px;
top: 21px;
}
.url-height {
min-height: 50px;
}
}
</style>

View File

@ -57,7 +57,7 @@
</el-text>
</template>
</el-table-column>
<el-table-column prop="name" label="启用状态">
<el-table-column label="启用状态">
<template #default="{ row }">
<div @click.stop>
<el-switch
@ -78,7 +78,7 @@
{{ datetimeFormat(row.update_time) }}
</template>
</el-table-column>
<el-table-column prop="name" label="操作" align="center">
<el-table-column label="操作" align="center">
<template #default="{ row }">
<span v-if="row.status === 2">
<el-tooltip effect="dark" content="刷新" placement="top">

View File

@ -1,50 +1,56 @@
<template>
<el-input v-model="filterText" placeholder="搜索" prefix-icon="Search" class="mb-16" />
<el-table :data="data" :max-height="tableHeight">
<el-table-column prop="name" :label="isApplication ? '应用名称' : '数据集名称'">
<template #default="{ row }">
<div class="flex align-center">
<AppAvatar
v-if="isApplication"
:name="row.name"
pinyinColor
class="mr-12"
shape="square"
:size="24"
/>
<AppAvatar v-else-if="isDataset" class="mr-12" shape="square" :size="24">
<img src="@/assets/icon_document.svg" style="width: 58%" alt="" />
</AppAvatar>
<span class="ellipsis-1"> {{ row?.name }}</span>
</div>
</template>
</el-table-column>
<el-table-column label="管理" align="center" width="60">
<!-- <template #header>
<el-input
v-model="filterText"
placeholder="搜索"
prefix-icon="Search"
class="p-24 pt-0 pb-0 mb-16 mt-4"
/>
<div class="p-24 pt-0">
<el-table :data="data" :max-height="tableHeight">
<el-table-column prop="name" :label="isApplication ? '应用名称' : '数据集名称'">
<template #default="{ row }">
<div class="flex align-center">
<AppAvatar
v-if="isApplication"
:name="row.name"
pinyinColor
class="mr-12"
shape="square"
:size="24"
/>
<AppAvatar v-else-if="isDataset" class="mr-12" shape="square" :size="24">
<img src="@/assets/icon_document.svg" style="width: 58%" alt="" />
</AppAvatar>
<span class="ellipsis-1"> {{ row?.name }}</span>
</div>
</template>
</el-table-column>
<el-table-column label="管理" align="center" width="60">
<!-- <template #header>
<el-checkbox
v-model="allChecked[MANAGE]"
label="管理"
@change="handleCheckAllChange($event, MANAGE)"
/>
</template> -->
<template #default="{ row }">
<el-checkbox v-model="row.operate[MANAGE]" @change="checkedOperateChange(MANAGE, row)" />
</template>
</el-table-column>
<el-table-column label="使用" align="center" width="60">
<!-- <template #header>
<template #default="{ row }">
<el-checkbox v-model="row.operate[MANAGE]" @change="checkedOperateChange(MANAGE, row)" />
</template>
</el-table-column>
<el-table-column label="使用" align="center" width="60">
<!-- <template #header>
<el-checkbox
v-model="allChecked[USE]"
label="使用"
@change="handleCheckAllChange($event, USE)"
/>
</template> -->
<template #default="{ row }">
<el-checkbox v-model="row.operate[USE]" @change="checkedOperateChange(USE, row)" />
</template>
</el-table-column>
</el-table>
<template #default="{ row }">
<el-checkbox v-model="row.operate[USE]" @change="checkedOperateChange(USE, row)" />
</template>
</el-table-column>
</el-table>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, watch, computed } from 'vue'
@ -56,7 +62,8 @@ const props = defineProps({
default: () => []
},
id: String,
type: String
type: String,
tableHeight: Number
})
const isDataset = computed(() => props.type === DATASET)
@ -68,8 +75,6 @@ const allChecked: any = ref({
[USE]: false
})
const tableHeight = ref(0)
const filterText = ref('')
watch(
@ -113,12 +118,6 @@ function compare(attrs: string | number) {
}
onMounted(() => {
tableHeight.value = window.innerHeight - 300
window.onresize = () => {
return (() => {
tableHeight.value = window.innerHeight - 300
})()
}
Object.keys(allChecked.value).map((item) => {
allChecked.value[item] = compare(item)
})

View File

@ -40,21 +40,26 @@
</common-list>
</div>
<div class="permission-setting flex" v-loading="rLoading">
<div class="team-manage__table p-24">
<h4>权限设置</h4>
<div class="team-manage__table">
<h4 class="p-24 pb-0 mb-4">权限设置</h4>
<el-tabs v-model="activeName" class="team-manage__tabs">
<el-tab-pane
v-for="item in settingTags"
v-for="(item, index) in settingTags"
:key="item.value"
:label="item.label"
:name="item.value"
>
<PermissionSetting :data="item.data" :type="item.value"></PermissionSetting>
<PermissionSetting
:key="index"
:data="item.data"
:type="item.value"
:tableHeight="tableHeight"
></PermissionSetting>
</el-tab-pane>
</el-tabs>
</div>
<div class="team-manage__footer border-t p-16 flex">
<div class="submit-button">
<el-button type="primary" @click="submitPermissions">保存</el-button>
</div>
</div>
@ -81,6 +86,7 @@ const currentUser = ref<String>('')
const filterText = ref('')
const activeName = ref(DATASET)
const tableHeight = ref(0)
const settingTags = reactive([
{
@ -201,6 +207,12 @@ function refresh() {
}
onMounted(() => {
tableHeight.value = window.innerHeight - 330
window.onresize = () => {
return (() => {
tableHeight.value = window.innerHeight - 330
})()
}
getMember()
})
</script>
@ -223,17 +235,28 @@ onMounted(() => {
box-sizing: border-box;
width: calc(100% - var(--setting-left-width));
flex-direction: column;
position: relative;
.submit-button {
position: absolute;
top: 54px;
right: 24px;
}
}
&__tabs {
margin-top: 10px;
:deep(.el-tabs__nav-wrap::after) {
height: 1px;
}
:deep(.el-tabs__nav-scroll) {
padding: 0 24px;
}
:deep(.el-tabs__active-bar) {
height: 3px;
}
}
&__table {
flex: 1;
}
&__footer {
flex: 0 0 auto;
justify-content: right;
}
}
</style>