update admin

This commit is contained in:
全栈小学生 2023-10-26 09:54:58 +08:00
parent bffc51a8f9
commit 5795d31b81
29 changed files with 400 additions and 451 deletions

View File

@ -39,7 +39,7 @@ export function installAddon(params: Record<string, any>) {
* @returns * @returns
*/ */
export function cloudInstallAddon(params: Record<string, any>) { export function cloudInstallAddon(params: Record<string, any>) {
return request.post(`addon/cloudinstall/${params.addon}`, params) return request.post(`addon/cloudinstall/${params.addon}`, params, { timeout: 60 * 1000 })
} }
/** /**

View File

@ -37,4 +37,10 @@ export function getLoginConfig() {
*/ */
export function getAuthaddon() { export function getAuthaddon() {
return request.get(`auth/authaddon`) return request.get(`auth/authaddon`)
} }
/**
*
*/
export function getVersions() {
return request.get(`sys/info`)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,8 +1,10 @@
{ {
"app": "应用管理", "app": "应用中心",
"descriptionLeft": "暂未安装任何应用,请到", "descriptionLeft": "请点击",
"link": "插件管理", "link": "安装应用",
"descriptionRight": "进行安装使用", "descriptionRight": "安装使用",
"niucloud": "Niucloud官网", "niucloud": "Niucloud官网",
"appStore": "插件管理" "appStore": "安装应用",
"versionInfo":"版本信息:",
"currentVersion":"当前版本"
} }

View File

@ -1,4 +1,10 @@
{ {
"search":"搜索应用名称",
"appName":"应用名/版本信息",
"introduction":"简介",
"type":"类型",
"app":"应用",
"addon":"插件",
"noPlug":"暂无应用", "noPlug":"暂无应用",
"install":"安装", "install":"安装",
"unload":"卸载", "unload":"卸载",
@ -6,9 +12,10 @@
"uninstalledLabel":"未安装", "uninstalledLabel":"未安装",
"version":"版本", "version":"版本",
"title":"名称", "title":"名称",
"desc":"描述", "desc":"简介",
"plugDetail": "插件信息", "plugDetail": "插件信息",
"author": "作者", "author": "作者",
"detail":"详情",
"addonInstall": "插件安装", "addonInstall": "插件安装",
"dirPermission": "目录读写权限", "dirPermission": "目录读写权限",
"path": "路径", "path": "路径",
@ -56,5 +63,6 @@
"updateCode": "重新绑定", "updateCode": "重新绑定",
"notHaveAuth": "还没有授权?去购买", "notHaveAuth": "还没有授权?去购买",
"authInfoTips": "授权码和授权秘钥可在Niucloud官网我的授权 授权详情中查看", "authInfoTips": "授权码和授权秘钥可在Niucloud官网我的授权 授权详情中查看",
"addonUninstall": "插件卸载" "addonUninstall": "插件卸载",
"noAddon":"暂无插件"
} }

View File

@ -9,7 +9,7 @@
</div> </div>
<div class="mt-[30px] flex items-center text-[14px] text-[#797979]"> <div class="mt-[30px] flex items-center text-[14px] text-[#797979]">
<span>当前版本</span> <span>当前版本</span>
<span class="text-[26px] ml-[15px] mr-[10px] text-[#656668]">1.0.35</span> <span class="text-[26px] ml-[15px] mr-[10px] text-[#656668]">{{versions}}</span>
<em class="text-[12px]">(当前已是最新版本)</em> <em class="text-[12px]">(当前已是最新版本)</em>
</div> </div>
</div> </div>
@ -40,7 +40,7 @@
<div class="flex flex-1 flex-wrap justify-end relative"> <div class="flex flex-1 flex-wrap justify-end relative">
<el-button class="w-[154px] !h-[48px] mt-[8px]" type="primary" <el-button class="w-[154px] !h-[48px] mt-[8px]" type="primary"
@click="authCodeApproveFn">授权码认证</el-button> @click="authCodeApproveFn">授权码认证</el-button>
<el-popover ref="getAuthCodeDialog" placement="bottom" :width="478" trigger="click" <el-popover ref="getAuthCodeDialog" placement="bottom-start" :width="478" trigger="click"
class="mt-[8px]"> class="mt-[8px]">
<div class="px-[18px] py-[8px]"> <div class="px-[18px] py-[8px]">
<p class="leading-[32px] text-[14px]">您在官方应用市场购买任意一款应用即可获得授权码输入正确授权码认证通过后即可支持在线升级和其它相关服务 <p class="leading-[32px] text-[14px]">您在官方应用市场购买任意一款应用即可获得授权码输入正确授权码认证通过后即可支持在线升级和其它相关服务
@ -95,6 +95,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { reactive, ref } from 'vue' import { reactive, ref } from 'vue'
import { t } from '@/lang' import { t } from '@/lang'
import { getVersions } from '@/app/api/auth'
import { getAuthinfo, setAuthinfo, getAdminAuthinfo } from '@/app/api/module' import { getAuthinfo, setAuthinfo, getAdminAuthinfo } from '@/app/api/module'
import { FormInstance, FormRules } from 'element-plus' import { FormInstance, FormRules } from 'element-plus'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
@ -165,7 +166,6 @@ const save = async (formEl: FormInstance | undefined) => {
}) })
.catch(() => { .catch(() => {
saveLoading.value = false saveLoading.value = false
authCodeApproveDialog.value = false
}) })
} }
}) })
@ -174,6 +174,14 @@ const save = async (formEl: FormInstance | undefined) => {
const market = () => { const market = () => {
window.open('https://www.niucloud.com/product') window.open('https://www.niucloud.com/product')
} }
const versions = ref('')
const getVersionsInfo = () =>{
getVersions().then(res =>{
versions.value = res.data.version.version
})
}
getVersionsInfo()
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -1,18 +1,21 @@
<template> <template>
<div class="h-[480px] box-border pt-[20px] px-[20px]" v-loading="loading"> <div class="box-border pt-[68px] px-[76px] overview-top" v-loading="loading">
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
<div class="font-600 text-[20px] text-[#222]">{{ t('app') }}</div> <div>
<el-button @click="toAppStore"> <div class="font-[600] text-[26px] text-[#222] leading-[37px]">{{ t('app') }}</div>
<el-icon class="mr-[2px]"><Download /></el-icon> <div class="font-[500] text-[14px] text-[#222] leading-[20px] mt-[12px]">{{ t('versionInfo') }}&nbsp;{{ t('currentVersion') }}&nbsp;{{ versions }}</div>
<span>{{t('appStore')}}</span> </div>
<el-button @click="toAppStore" class="px-[15px]">
<div class="mr-[9px] text-[#3F3F3F] iconfont iconxiazai01"></div>
<span class="font-[600] text-[14px] text-[#222] leading-[20px]">{{t('appStore')}}</span>
</el-button> </el-button>
</div> </div>
<div class="flex flex-wrap mt-[28px]"> <div class="flex flex-wrap mt-[40px]">
<template v-for="(item, index) in detail.appList" :key="index"> <template v-for="(item, index) in detail.appList" :key="index">
<div class="app-item w-[284px] box-border p-[18px] pb-[24px] bg-[#fff] rounded-[8px] cursor-pointer mr-[24px] mb-[24px]" <div class="app-item w-[280px] box-border py-[42px] px-[32px] bg-[#fff] rounded-[8px] cursor-pointer mr-[20px] mb-[20px] "
@click="itemPath(item)"> @click="itemPath(item)">
<div class="flex items-center"> <div class="flex items-center">
<el-image class="w-[40px] h-[40px] rounded-[8px]" :src="img(item.icon)" fit="contain"> <el-image class="w-[44px] h-[44px] rounded-[8px]" :src="img(item.icon)" fit="contain">
<template #error> <template #error>
<div class="image-slot"> <div class="image-slot">
<img class="w-[40px] h-[40px] rounded-[8px]" <img class="w-[40px] h-[40px] rounded-[8px]"
@ -20,9 +23,11 @@
</div> </div>
</template> </template>
</el-image> </el-image>
<div class="flex-1 font-600 text-[14px] text-[#222] ml-[12px]">{{ item.title }}</div> <div class="ml-[12px] flex-1">
<div class="font-[600] text-[14px] text-[#222] leading-[20px]">{{ item.title }}</div>
<div class="font-[500] text-[13px] text-[#6D7278] leading-[18px] mt-[6px] w-[160px] truncate">{{ item.desc }}</div>
</div>
</div> </div>
<div class="font-500 text-[13px] text-[#6D7278] mt-[14px]">{{ item.desc }}</div>
</div> </div>
</template> </template>
@ -47,10 +52,10 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import {reactive, ref, onMounted, computed} from 'vue' import { reactive, ref, onMounted , computed} from 'vue'
import {t} from '@/lang' import { t } from '@/lang'
import {getAuthaddon} from '@/app/api/auth' import { getAuthaddon, getVersions} from '@/app/api/auth'
import {img} from '@/utils/common' import { img} from '@/utils/common'
import {useRouter} from 'vue-router' import {useRouter} from 'vue-router'
import storage from '@/utils/storage' import storage from '@/utils/storage'
import {findFirstValidRoute} from '@/router/routers' import {findFirstValidRoute} from '@/router/routers'
@ -126,6 +131,13 @@
userStore.logout(); userStore.logout();
} }
const versions = ref('')
const getVersionsInfo = () =>{
getVersions().then(res =>{
versions.value = res.data.version.version
})
}
getVersionsInfo()
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -134,12 +146,13 @@
min-height: calc(100vh - 64px); min-height: calc(100vh - 64px);
} }
.overview-top { .overview-top{
background-image: url('@/app/assets/images/index/overview.png'); background-image: url('@/app/assets/images/index/overview.png');
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: cover; background-size: cover;
height: calc(100vh - 120px);
} }
.app-item { .app-item {
box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.18); box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.18);
} }

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="main-container w-full p-5 bg-white" v-loading="loading"> <div class="main-container w-full p-[64px] bg-white" v-loading="loading">
<div class="flex justify-between items-center h-[32px] mb-4"> <div class="flex justify-between items-center h-[32px] mb-4">
<span class="text-[20px]">{{ t('personal') }}</span> <span class="text-[20px]">{{ t('personal') }}</span>
<span class="text-[14px] text-[#999] cursor-pointer" @click="toEditPersonal">{{ t('editPersonal') }}</span> <span class="text-[14px] text-[#999] cursor-pointer" @click="toEditPersonal">{{ t('editPersonal') }}</span>
@ -7,7 +7,13 @@
<el-card class="box-card !border-none" shadow="never"> <el-card class="box-card !border-none" shadow="never">
<el-form :model="saveInfo" label-width="90px" ref="formRef" class="page-form"> <el-form :model="saveInfo" label-width="90px" ref="formRef" class="page-form">
<el-form-item :label="t('headImg')"> <el-form-item :label="t('headImg')">
<el-image class="w-[70px] h-[70px]" :src="img(saveInfo.head_img)" fit="contain" /> <el-image class="w-[70px] h-[70px] rounded-full" :src="img(saveInfo.head_img)" fit="contain">
<template #error>
<div class="image-slot w-[70px] h-[70px] bg-[#c0c4cc] flex items-center justify-center">
<el-icon class="text-[45px] text-[#fff]"><UserFilled /></el-icon>
</div>
</template>
</el-image>
</el-form-item> </el-form-item>
<el-form-item :label="t('userName')"> <el-form-item :label="t('userName')">
<div>{{saveInfo.username}}</div> <div>{{saveInfo.username}}</div>
@ -21,6 +27,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { UserFilled } from '@element-plus/icons-vue'
import { reactive, ref } from 'vue' import { reactive, ref } from 'vue'
import { t } from '@/lang' import { t } from '@/lang'
import type { FormInstance, FormRules, ElNotification } from 'element-plus' import type { FormInstance, FormRules, ElNotification } from 'element-plus'

View File

@ -1,280 +1,165 @@
<template> <template>
<div class="main-container w-full p-5 bg-white" v-loading="loading"> <div class="pt-[64px] px-[90px] app-store" v-loading="loading">
<div class="flex justify-between items-center h-[32px] mb-4"> <div class="flex justify-between items-center h-[32px] mb-4">
<span class="text-[20px]">{{ t('localAppText') }}</span> <span class="text-[26px] text-[#222] font-600">{{ t('localAppText') }}</span>
<el-input class="w-[247px]" :placeholder="t('search')" v-model="search_name" @keyup.enter="query">
<template #suffix>
<el-icon class="el-input__icon cursor-pointer" size="14px" @click="query">
<search />
</el-icon>
</template>
</el-input>
</div> </div>
<div class="relative"> <div class="flex mt-[24px]">
<!-- <div class="absolute right-0 top-[2px] flex items-center cursor-pointer z-[4] border border-inherit"> <div :class="{ '!bg-[#000] !border-0 !text-[#fff]': activeName === 'installed' }"
<div class="flex item-center justify-center px-[6px] py-[4px]" class="w-[78px] h-[30rpx] text-[14px] text-[#242424] text-center rounded-[15px] leading-[30px] bg-[#F0F0F0] border-solid border-1 border-[#E0E0E0] cursor-pointer mr-[24px]"
:class="{ 'bg-slate-200': showType == 'small' }" @click="showType = 'small'"> @click="activeName = 'installed'">
<img src="@/app/assets/images/app_store/switch_icon_1.png" class=" w-[16px] h-[16px]"> {{ t('installLabel') }}
</div> </div>
<div class="flex item-center justify-center px-[6px] py-[4px]" <div :class="{ '!bg-[#000] !border-0 !text-[#fff]': activeName === 'uninstalled' }"
:class="{ 'bg-slate-200': showType == 'large' }" @click="showType = 'large'"> class="w-[78px] h-[30rpx] text-[14px] text-[#242424] text-center rounded-[15px] leading-[30px] bg-[#F0F0F0] border-solid border-1 border-[#E0E0E0] cursor-pointer mr-[24px]"
<img src="@/app/assets/images/app_store/switch_icon_2.png" class="w-[16px] h-[16px] "> @click="activeName = 'uninstalled'">
</div> {{ t('uninstalledLabel') }}
</div> --> </div>
<div :class="{ '!bg-[#000] !border-0 !text-[#fff]': activeName === 'all' }"
<el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick"> class="w-[78px] h-[30rpx] text-[14px] text-[#242424] text-center rounded-[15px] leading-[30px] bg-[#F0F0F0] border-solid border-1 border-[#E0E0E0] cursor-pointer mr-[24px]"
<el-tab-pane :label="installLabel" name="installed"> @click="activeName = 'all'">
<div class="flex flex-wrap px-2 plug-list pb-10"> {{ t('buyLabel') }}
<div v-for="(item, index) in localList.installed" :key="index + 'a'" </div>
class="flex items-center cursor-pointer w-[295px] relative plug-item mr-4 mb-4" </div>
@click="getAddonDetialFn(item)" v-if="showType == 'small'"> <div class="mt-[32px]">
<div class="p-3"> <el-table v-if="localList[activeName].length" :data="info[activeName]" size="large" class="pt-[5px]">
<img class="w-[44px] h-[44px] rounded-sm" v-if="item.icon" :src="item.icon" alt=""> <template #empty>
<img class="w-[44px] h-[44px] rounded-sm" v-else src="@/app/assets/images/icon-addon.png" <span>{{ t('noAddon') }}</span>
alt=""> </template>
</div> <el-table-column :label="t('appName')" align="left" min-width="200">
<div class="flex items-center w-[220px] border-b py-3 justify-between"> <template #default="{ row }">
<div class="flex flex-col"> <div class="flex items-center">
<span class="text-[14px] truncate w-[160px]">{{ item.title }}</span> <el-image class="w-[54px] h-[54px] flex-shrink-0" :src="row.icon" fit="contain">
<span class="text-xs text-gray-400 truncate w-[160px] mt-[4px]">{{ item.desc }}</span> <template #error>
</div> <img class="w-[54px] h-[54px]" src="@/app/assets/images/icon-addon.png" alt="">
<el-button size="small" round class="!text-primary !border-primary !bg-transparent" </template>
@click.stop="uninstallAddonFn(item.key)">{{ t('unload') </el-image>
}}</el-button> <div class="flex flex-col justify-center pl-[20px] text-[#222] font-500 text-[13px]">
<div class="multi-hidden leading-[20px]">{{ row.title }}</div>
<div class="leading-[18px] mt-[6px]">{{ row.version }}</div>
</div> </div>
</div> </div>
</template>
</el-table-column>
<el-table-column prop="" :label="t('introduction')" align="left" min-width="200">
<template #default="{ row }">
<span class="text-[#222] font-500 text-[13px] multi-hidden">{{ row.desc }}</span>
</template>
</el-table-column>
<el-table-column :label="t('type')" align="left" width="100">
<template #default="{ row }">
<span class="text-[#222] font-500 text-[13px]">{{ row.type === 'app' ? t('app') : t('addon')
}}</span>
</template>
</el-table-column>
<el-table-column prop="" :label="t('author')" align="left" min-width="200">
<template #default="{ row }">
<span class="text-[#222] font-500 text-[13px] multi-hidden">{{ row.author }}</span>
</template>
</el-table-column>
<el-table-column :label="t('operation')" align="right" width="180">
<template #default="{ row }">
<div class="flex flex-wrap plug-list pb-10 plug-large" v-if="showType == 'large'"> <el-button class="!text-[13px]" v-if="row.install_info && Object.keys(row.install_info)?.length"
<div class="app-item cursor-pointer mr-4 mt-[20px] pb-2 bg-[#f7f7f7]" type="primary" link @click="uninstallAddonFn(row.key)">{{ t('unload') }}</el-button>
v-for="(item, index) in localList.installed" :key="index + 'a'"
@click="getAddonDetialFn(item)">
<div class="flex justify-center items-center">
<img class="w-[240px] h-[120px]" v-if="item.cover" :src="item.cover" />
<img v-else class="w-[240px] h-[120px]"
src="@/app/assets/images/app_store/app_store_default.png" />
</div>
<div class="flex w-[240px] h-[46px]">
<div class="text-left mt-2 w-[190px]">
<p class="app-text text-[14px] text-[#222] pl-2">{{ item.title }}</p>
<p class="app-text text-[12px] text-[#999] pl-2">{{ item.desc }}</p>
</div>
<div class="flex items-center pr-2">
<el-button size="small" round class="!text-primary !border-primary !bg-transparent"
@click.stop="uninstallAddonFn(item.key)">{{ t('unload')
}}</el-button>
</div>
</div>
</div>
</div>
<el-empty class="mx-auto overview-empty" v-if="!localList.installed.length && !loading"> <el-button class="!text-[13px]" v-else-if="row.is_download && row.install_info <= 0" type="primary"
<template #image> link @click="installAddonFn(row.key)">{{ t('install')
<div class="w-[230px] mx-auto"> }}</el-button>
<img src="@/app/assets/images/index/apply_empty.png" class="max-w-full" alt=""> <el-button class="!text-[13px]" v-else :loading="downloading == row.key"
</div> :disabled="downloading != ''" type="primary" link @click.stop="downEvent(row)">{{
</template> t('down') }}</el-button>
<template #description> <el-button class="!text-[13px]" type="primary" link @click="getAddonDetialFn(row)">{{ t('detail')
<p class="flex items-center">{{ t('installed-empty') }}</p> }}</el-button>
</template>
</el-empty> </template>
</el-table-column>
</el-table>
<el-empty class="mx-auto overview-empty"
v-if="!localList.installed.length && !loading && activeName == 'installed'">
<template #image>
<div class="w-[230px] mx-auto">
<img src="@/app/assets/images/index/apply_empty.png" class="max-w-full" alt="">
</div> </div>
</el-tab-pane> </template>
<el-tab-pane :label="uninstalledLabel" name="uninstalled"> <template #description>
<div class="flex flex-wrap px-2 plug-list pb-10"> <p class="flex items-center">{{ t('installed-empty') }}</p>
</template>
<div v-for="(item, index) in localList.uninstalled" :key="index + 'a'" </el-empty>
class="flex items-center cursor-pointer w-[295px] relative plug-item mr-4 mb-4" <el-empty class="mx-auto overview-empty"
@click="getAddonDetialFn(item)" v-if="showType == 'small'"> v-if="!localList.uninstalled.length && !loading && activeName == 'uninstalled'">
<div class="p-3"> <template #image>
<img v-if="item.icon" class="w-[44px] h-[44px] rounded-sm" :src="item.icon" alt=""> <div class="w-[230px] mx-auto">
<img v-else class="w-[44px] h-[44px] rounded-sm" src="@/app/assets/images/icon-addon.png" <img src="@/app/assets/images/index/apply_empty.png" class="max-w-full" alt="">
alt="">
</div>
<div class="flex items-center w-[220px] border-b py-3 justify-between">
<div class="flex flex-col">
<span class="text-[14px] truncate w-[160px]">{{ item.title }}</span>
<span class="text-xs text-gray-400 truncate w-[160px] mt-[4px]">{{ item.desc }}</span>
</div>
<el-button v-if="item.is_download" size="small" round
class="!text-primary !border-primary !bg-transparent"
@click.stop="installAddonFn(item.key)">{{ t('install')
}}</el-button>
<el-button v-else size="small" :loading="downloading == item.key"
:disabled="downloading != ''" round
class="!text-primary !border-primary !bg-transparent" @click.stop="downEvent(item)">{{
t('down') }}</el-button>
</div>
</div>
<div class="flex flex-wrap plug-list pb-10 plug-large" v-if="showType == 'large'">
<div class="app-item cursor-pointer mr-4 mt-[20px] pb-2 bg-[#f7f7f7]"
v-for="(item, index) in localList.uninstalled" :key="index + 'a'"
@click="getAddonDetialFn(item)">
<div class="flex justify-center items-center">
<img v-if="item.cover && !item.is_download" class="w-[240px] h-[120px]"
:src="img(item.cover)" />
<img v-else-if="item.cover && item.is_download" class="w-[240px] h-[120px]"
:src="item.cover" />
<img v-else class="w-[240px] h-[120px]"
src="@/app/assets/images/app_store/app_store_default.png" />
</div>
<div class="flex w-[240px] h-[46px]">
<div class="text-left mt-2 w-[190px]">
<p class="app-text text-[14px] text-[#222] pl-2">{{ item.title }}</p>
<p class="app-text text-[12px] text-[#999] pl-2">{{ item.desc }}</p>
</div>
<div class="flex items-center pr-2">
<el-button v-if="item.is_download" size="small" round
class="!text-primary !border-primary !bg-transparent"
@click.stop="installAddonFn(item.key)">{{ t('install')
}}</el-button>
<el-button v-else size="small" :loading="downloading == item.key"
:disabled="downloading != ''" round
class="!text-primary !border-primary !bg-transparent"
@click.stop="downEvent(item)">{{ t('down') }}</el-button>
</div>
</div>
</div>
</div>
<el-empty class="mx-auto overview-empty" v-if="!localList.uninstalled.length && !loading">
<template #image>
<div class="w-[230px] mx-auto">
<img src="@/app/assets/images/index/apply_empty.png" class="max-w-full" alt="">
</div>
</template>
<template #description>
<p class="flex items-center">
<span>{{ t('descriptionLeft') }}</span>
<el-link type="primary" @click="goRouter" class="mx-[5px]">{{ t('link') }}</el-link>
<span>{{ t('descriptionRight') }}</span>
</p>
</template>
</el-empty>
</div> </div>
</el-tab-pane> </template>
<el-tab-pane :label="allLabel" name="buy"> <template #description>
<p class="flex items-center">
<div class="flex flex-wrap px-2 plug-list pb-10"> <span>{{ t('descriptionLeft') }}</span>
<el-link type="primary" @click="goRouter" class="mx-[5px]">{{ t('link') }}</el-link>
<template v-if="authinfo"> <span>{{ t('descriptionRight') }}</span>
<div v-for="(item, index) in localList.all" :key="index + 'a'" </p>
class="flex items-center cursor-pointer w-[295px] relative plug-item mr-4 mb-4" </template>
@click="getAddonDetialFn(item)" v-if="showType == 'small'"> </el-empty>
<div class="p-3"> <div v-if="!localList.all.length && !loading && !authinfo && activeName == 'all'"
<img v-if="item.icon" class="w-[44px] h-[44px] rounded-sm" :src="item.icon" alt=""> class="mx-auto overview-empty flex flex-col items-center pt-14 pb-6">
<img v-else class="w-[44px] h-[44px] rounded-sm" <div class="mb-[20px] text-sm text-[#888]">检测到当前账号尚未绑定授权请先绑定授权</div>
src="@/app/assets/images/icon-addon.png" alt=""> <div class="flex flex-1 flex-wrap justify-center relative">
</div> <el-button class="w-[154px] !h-[48px] mt-[8px]" type="primary"
<div class="flex items-center w-[220px] border-b py-3 justify-between"> @click="authCodeApproveFn">授权码认证</el-button>
<div class="flex flex-col"> <el-popover ref="getAuthCodeDialog" placement="bottom" :width="478" trigger="click" class="mt-[8px]">
<span class="text-[14px] truncate w-[160px]">{{ item.title }}</span> <div class="px-[18px] py-[8px]">
<span class="text-xs text-gray-400 truncate w-[160px] mt-[4px]">{{ item.desc <p class="leading-[32px] text-[14px]">
}}</span> 您在官方应用市场购买任意一款应用即可获得授权码输入正确授权码认证通过后即可支持在线升级和其它相关服务</p>
</div> <div class="flex justify-end mt-[36px]">
<el-button v-if="item.install_info && Object.keys(item.install_info)?.length" <el-button class="w-[182px] !h-[48px]" plain @click="market">去应用市场逛逛</el-button>
size="small" round class="!text-primary !border-primary !bg-transparent" <el-button class="w-[100px] !h-[48px]" plain
@click.stop="uninstallAddonFn(item.key)">{{ t('unload') @click="getAuthCodeDialog.hide()">关闭</el-button>
}}</el-button>
<el-button v-else-if="item.is_download && item.install_info <= 0" size="small" round
class="!text-primary !border-primary !bg-transparent"
@click.stop="installAddonFn(item.key)">{{ t('install')
}}</el-button>
<el-button v-else size="small" :loading="downloading == item.key"
:disabled="downloading != ''" round
class="!text-primary !border-primary !bg-transparent"
@click.stop="downEvent(item)">{{
t('down') }}</el-button>
</div>
</div>
<div class="flex flex-wrap plug-list pb-10 plug-large" v-if="showType == 'large'">
<div class="app-item cursor-pointer mr-4 mt-[20px] pb-2 bg-[#f7f7f7]"
v-for="(item, index) in localList.all" :key="index + 'a'"
@click="getAddonDetialFn(item)">
<div class="flex justify-center items-center">
<img v-if="item.icon && !item.is_download" class="w-[240px] h-[120px]"
:src="img(item.icon)" />
<img v-else-if="item.icon && item.is_download" class="w-[240px] h-[120px]"
:src="item.icon" />
<img v-else class="w-[240px] h-[120px]"
src="@/app/assets/images/app_store/app_store_default.png" />
</div>
<div class="flex w-[240px] h-[46px]">
<div class="text-left mt-2 w-[190px]">
<p class="app-text text-[14px] text-[#222] pl-2">{{ item.title }}</p>
<p class="app-text text-[12px] text-[#999] pl-2">{{ item.desc }}</p>
</div>
<div class="flex items-center pr-2">
<el-button v-if="item.install_info && Object.keys(item.install_info)?.length"
size="small" round class="!text-primary !border-primary !bg-transparent"
@click.stop="uninstallAddonFn(item.key)">{{ t('unload')
}}</el-button>
<el-button v-else-if="item.is_download && item.install_info <= 0" size="small"
round class="!text-primary !border-primary !bg-transparent"
@click.stop="installAddonFn(item.key)">{{ t('install')
}}</el-button>
<el-button v-else size="small" round :loading="downloading == item.key"
:disabled="downloading != ''"
class="!text-primary !border-primary !bg-transparent"
@click.stop="downEvent(item)">{{ t('down') }}</el-button>
</div>
</div>
</div>
</div> </div>
</div>
<template #reference>
<el-button
class="w-[154px] !h-[48px] mt-[8px] !text-[var(--el-color-primary)] hover:!text-[var(--el-color-primary)] !bg-transparent"
plain type="primary">如何获取授权码?</el-button>
</template> </template>
</el-popover>
<div v-if="!localList.all.length && !loading && !authinfo" </div>
class="mx-auto overview-empty flex flex-col items-center pt-14 pb-6"> </div>
<div class="mb-[20px] text-sm text-[#888]">检测到当前账号尚未绑定授权请先绑定授权</div>
<div class="flex flex-1 flex-wrap justify-center relative">
<el-button class="w-[154px] !h-[48px] mt-[8px]" type="primary"
@click="authCodeApproveFn">授权码认证</el-button>
<el-popover ref="getAuthCodeDialog" placement="bottom" :width="478" trigger="click"
class="mt-[8px]">
<div class="px-[18px] py-[8px]">
<p class="leading-[32px] text-[14px]">
您在官方应用市场购买任意一款应用即可获得授权码输入正确授权码认证通过后即可支持在线升级和其它相关服务</p>
<div class="flex justify-end mt-[36px]">
<el-button class="w-[182px] !h-[48px]" plain @click="market">去应用市场逛逛</el-button>
<el-button class="w-[100px] !h-[48px]" plain
@click="getAuthCodeDialog.hide()">关闭</el-button>
</div>
</div>
<template #reference>
<el-button
class="w-[154px] !h-[48px] mt-[8px] !text-[var(--el-color-primary)] hover:!text-[var(--el-color-primary)] !bg-transparent"
plain type="primary">如何获取授权码?</el-button>
</template>
</el-popover>
</div>
</div>
<el-dialog v-model="authCodeApproveDialog" title="授权码认证" width="400px">
<el-form :model="formData" label-width="0" ref="formRef" :rules="formRules" class="page-form">
<el-card class="box-card !border-none" shadow="never">
<el-form-item prop="auth_code">
<el-input v-model="formData.auth_code" :placeholder="t('authCodePlaceholder')"
class="input-width" clearable size="large" />
</el-form-item>
<div class="mt-[20px]">
<el-form-item prop="auth_secret">
<el-input v-model="formData.auth_secret" clearable
:placeholder="t('authSecretPlaceholder')" class="input-width"
size="large" />
</el-form-item>
</div>
<div class="text-sm mt-[10px] text-info">{{ t('authInfoTips') }}</div>
<div class="mt-[20px]">
<el-button type="primary" class="w-full" size="large" :loading="saveLoading"
@click="save(formRef)">{{ t('confirm') }}</el-button>
</div>
<div class="mt-[10px] text-right">
<el-button type="primary" link @click="market">{{ t('notHaveAuth') }}</el-button>
</div>
</el-card>
</el-form>
</el-dialog>
</div>
</el-tab-pane>
</el-tabs>
</div> </div>
<el-dialog v-model="authCodeApproveDialog" title="授权码认证" width="400px">
<el-form :model="formData" label-width="0" ref="formRef" :rules="formRules" class="page-form">
<el-card class="box-card !border-none" shadow="never">
<el-form-item prop="auth_code">
<el-input v-model="formData.auth_code" :placeholder="t('authCodePlaceholder')" class="input-width"
clearable size="large" />
</el-form-item>
<div class="mt-[20px]">
<el-form-item prop="auth_secret">
<el-input v-model="formData.auth_secret" clearable :placeholder="t('authSecretPlaceholder')"
class="input-width" size="large" />
</el-form-item>
</div>
<div class="text-sm mt-[10px] text-info">{{ t('authInfoTips') }}</div>
<div class="mt-[20px]">
<el-button type="primary" class="w-full" size="large" :loading="saveLoading"
@click="save(formRef)">{{ t('confirm') }}</el-button>
</div>
<div class="mt-[10px] text-right">
<el-button type="primary" link @click="market">{{ t('notHaveAuth') }}</el-button>
</div>
</el-card>
</el-form>
</el-dialog>
<!-- 详情 --> <!-- 详情 -->
<el-dialog v-model="appStoreShowDialog" :title="t('plugDetail')" width="500px" :destroy-on-close="true"> <el-dialog v-model="appStoreShowDialog" :title="t('plugDetail')" width="500px" :destroy-on-close="true">
<el-form :model="appStoreInfo" label-width="120px" ref="formRef" class="page-form"> <el-form :model="appStoreInfo" label-width="120px" ref="formRef" class="page-form">
@ -447,21 +332,18 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive, watch, computed, h } from 'vue' import { ref, reactive, watch, h } from 'vue'
import { t } from '@/lang' import { t } from '@/lang'
import { getAddonLocal, uninstallAddon, installAddon, preInstallCheck, cloudInstallAddon, getAddonInstalltask, getAddonCloudInstallLog, preUninstallCheck } from '@/app/api/addon' import { getAddonLocal, uninstallAddon, installAddon, preInstallCheck, cloudInstallAddon, getAddonInstalltask, getAddonCloudInstallLog, preUninstallCheck } from '@/app/api/addon'
import { downloadVersion, getAuthinfo, setAuthinfo } from '@/app/api/module' import { downloadVersion, getAuthinfo, setAuthinfo } from '@/app/api/module'
import { TabsPaneContext, ElMessageBox, ElNotification, FormInstance, FormRules } from 'element-plus' import { ElMessageBox, ElNotification, FormInstance, FormRules } from 'element-plus'
import { img } from '@/utils/common' import { img } from '@/utils/common'
import { Terminal, api as terminalApi } from 'vue-web-terminal' import { Terminal, api as terminalApi } from 'vue-web-terminal'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import useUserStore from '@/stores/modules/user' import useUserStore from '@/stores/modules/user'
import storage from '@/utils/storage'
const router = useRouter() const router = useRouter()
const activeName = ref('installed') const activeName = ref('installed')
const loading = ref<Boolean>(false) const loading = ref<Boolean>(false)
const showType = ref('small')
const downloading = ref('') const downloading = ref('')
const installAfterTips = ref<string[]>([]) const installAfterTips = ref<string[]>([])
const userStore = useUserStore() const userStore = useUserStore()
@ -479,24 +361,6 @@ const downEvent = (param: Record<string, any>) => {
}) })
} }
const installLabel = computed(() => {
let text = t('installLabel')
localList.value.installed.length && (text += ` (${localList.value.installed.length})`)
return text
})
const uninstalledLabel = computed(() => {
let text = t('uninstalledLabel')
localList.value.uninstalled.length && (text += ` (${localList.value.uninstalled.length})`)
return text
})
const allLabel = computed(() => {
let text = t('buyLabel')
localList.value.all.length && (text += ` (${localList.value.all.length})`)
return text
})
const authCode = ref('') const authCode = ref('')
getAuthinfo().then(res => { getAuthinfo().then(res => {
if (res.data.data && res.data.data.auth_code) { if (res.data.data && res.data.data.auth_code) {
@ -508,6 +372,27 @@ getAuthinfo().then(res => {
/** /**
* 本地下载的插件列表 * 本地下载的插件列表
*/ */
//input
const search_name = ref('')
//
const info = ref({
installed: [],
uninstalled: [],
all: [],
})
const query = () => {
if (search_name.value == '' || search_name.value == null) {
info.value.installed = localList.value.installed
info.value.uninstalled = localList.value.uninstalled
info.value.all = localList.value.all
return false
}
info.value.installed = localList.value.installed.filter((el: any) => el.title.indexOf(search_name.value) != -1)
info.value.uninstalled = localList.value.uninstalled.filter((el: any) => el.title.indexOf(search_name.value) != -1)
info.value.all = localList.value.all.filter((el: any) => el.title.indexOf(search_name.value) != -1)
}
const localList = ref({ const localList = ref({
installed: [], installed: [],
uninstalled: [], uninstalled: [],
@ -532,7 +417,7 @@ const localListFn = () => {
if (data[i].is_download == true) localList.value.uninstalled.push(data[i]) if (data[i].is_download == true) localList.value.uninstalled.push(data[i])
} }
} }
query()
loading.value = false loading.value = false
}).catch(() => { }).catch(() => {
loading.value = false loading.value = false
@ -540,17 +425,6 @@ const localListFn = () => {
} }
localListFn() localListFn()
const handleClick = (tab: TabsPaneContext, event: Event) => {
// if (tab.paneName == 'buy' && localList.value.error != '') {
// ElMessage({
// message: localList.value.error,
// grouping: true,
// type: 'error'
// })
// }
}
const currAddon = ref('') const currAddon = ref('')
// //
@ -682,7 +556,7 @@ const authElMessageBox = () => {
cancelButtonText: t('toNiucloud') cancelButtonText: t('toNiucloud')
} }
).then(() => { ).then(() => {
router.push({ path: '/app/authorize' }) authCodeApproveFn()
}).catch((action: string) => { }).catch((action: string) => {
if (action === 'cancel') { if (action === 'cancel') {
window.open('https://www.niucloud.com/product') window.open('https://www.niucloud.com/product')
@ -765,6 +639,8 @@ const installShowDialogClose = (done: () => {}) => {
).then(() => { ).then(() => {
done() done()
}).catch(() => { }) }).catch(() => { })
} else if(installStep.value == 3){
location.reload();
} else done() } else done()
} }
@ -824,7 +700,7 @@ const save = async (formEl: FormInstance | undefined) => {
.then(() => { .then(() => {
saveLoading.value = false saveLoading.value = false
setTimeout(() => { setTimeout(() => {
location.reload(); location.reload();
}, 1000); }, 1000);
}) })
.catch(() => { .catch(() => {
@ -840,6 +716,17 @@ const goRouter = () => {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
/* 多行超出隐藏 */
.multi-hidden {
white-space: normal;
word-break: break-all;
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.demo-tabs>.el-tabs__content { .demo-tabs>.el-tabs__content {
padding: 32px; padding: 32px;
color: #6b778c; color: #6b778c;
@ -893,8 +780,13 @@ html.dark .table-head-bg {
background: #141414; background: #141414;
} }
.el-alert .el-alert__title{ .el-alert .el-alert__title {
font-size: 16px; font-size: 16px;
line-height: 18px; line-height: 18px;
} }
.app-store {
height: calc(100vh - 120px);
box-sizing: border-box;
}
</style> </style>

View File

@ -43,7 +43,7 @@
<upload-image v-model="formData.front_end_logo" /> <upload-image v-model="formData.front_end_logo" />
</el-form-item> </el-form-item>
</el-card> --> </el-card> -->
<el-card class="box-card !border-none" shadow="never" v-if="app_type == 'admin' "> <el-card class="box-card !border-none" shadow="never" v-if="app_type == 'admin'">
<h3 class="panel-title !text-sm">{{ t('serviceInformation') }}</h3> <h3 class="panel-title !text-sm">{{ t('serviceInformation') }}</h3>
<el-form-item :label="t('contactsTel')"> <el-form-item :label="t('contactsTel')">
<el-input v-model="formData.tel" :placeholder="t('contactsTelPlaceholder')" class="input-width" clearable maxlength="20" /> <el-input v-model="formData.tel" :placeholder="t('contactsTelPlaceholder')" class="input-width" clearable maxlength="20" />
@ -95,9 +95,9 @@ const formData = reactive<Record<string, string>>({
front_end_name: '', front_end_name: '',
front_end_logo: '', front_end_logo: '',
icon: '', icon: '',
wechat_code: '', tel: '',
enterprise_wechat: '', wechat_code: '',
tel: '' enterprise_wechat: ''
}) })
const setFormData = async (id: number = 0) => { const setFormData = async (id: number = 0) => {

View File

@ -1,33 +1,70 @@
<template> <template>
<div class="box-border px-[30px] pt-[60px]" v-loading="loading"> <div class="box-border px-[64px] pt-[64px]">
<div class="flex justify-between items-center"> <div class="font-600 text-[22px] text-[#222] mb-[32px] pl-[14px]">工具管理</div>
<div class="font-600 text-[20px] text-[#222]">工具管理</div>
</div>
<div class="flex flex-wrap mt-[28px]"> <div class="flex flex-wrap mt-[28px]">
<template v-for="(item, index) in menus" :key="index"> <div class="w-[256px] h-[260px] tools-item-shadow mb-[24px] mx-[14px] rounded-[8px] flex flex-col cursor-pointer" @click="toLink('/tools/addon')">
<div class="app-item w-[284px] box-border p-[15px] bg-[#fff] rounded-[8px] cursor-pointer mr-[24px] mb-[24px]" @click="toLink(item)"> <div class="flex-1 py-[19px] px-[24px] flex flex-col">
<div class="flex items-center"> <span class="text-[16px] text-[#222] font-bold">插件开发</span>
<icon v-if="item.meta.icon" :name="item.meta.icon" class="!w-auto" size="40px" :title="item.meta.title" /> <p class="text-[13px] text-[#666] mt-[8px] multi-hidden">点击新建插件生成插件后系统会生成对应插</p>
<img v-else class="w-[40px] h-[40px] rounded-[8px]" src="@/app/assets/images/app_store/app_store_default.png"/>
<div class="flex-1 font-600 text-[14px] text-[#222] ml-[12px]">{{ item.meta.title }}</div>
</div>
</div> </div>
</template> <img src="@/app/assets/images/tools/addon_develop.png" class="w-[256px] h-[148px]" />
</div>
<el-empty v-if="!menus.length && !loading" class="mx-auto overview-empty"> <div class="w-[256px] h-[260px] tools-item-shadow mb-[24px] mx-[14px] rounded-[8px] flex flex-col cursor-pointer" @click="toLink('/tools/code')">
<template #image> <div class="flex-1 py-[19px] px-[24px] flex flex-col">
<div class="w-[230px] mx-auto"> <span class="text-[16px] text-[#222] font-bold">代码生成</span>
<img src="@/app/assets/images/index/apply_empty.png" class="max-w-full" alt=""> <p class="text-[13px] text-[#666] mt-[8px] multi-hidden">代码生成</p>
</div> </div>
</template> <img src="@/app/assets/images/tools/code.png" class="w-[256px] h-[148px]" />
<template #description> </div>
<p class="flex items-center"> <div class="w-[256px] h-[260px] tools-item-shadow mb-[24px] mx-[14px] rounded-[8px] flex flex-col cursor-pointer" @click="toLink('/tools/list')">
<span>{{ t('descriptionLeft') }}</span> <div class="flex-1 py-[19px] px-[24px] flex flex-col">
<el-link type="primary" @click="goRouter" class="mx-[5px]">{{ t('link') }}</el-link> <span class="text-[16px] text-[#222] font-bold">数据字典</span>
<span>{{ t('descriptionRight') }}</span> <p class="text-[13px] text-[#666] mt-[8px] multi-hidden">数据字典</p>
</p> </div>
</template> <img src="@/app/assets/images/tools/sys_dict_list.png" class="w-[256px] h-[148px]" />
</el-empty> </div>
<div class="w-[256px] h-[260px] tools-item-shadow mb-[24px] mx-[14px] rounded-[8px] flex flex-col cursor-pointer" @click="toLink('/tools/update')">
<div class="flex-1 py-[19px] px-[24px] flex flex-col">
<span class="text-[16px] text-[#222] font-bold">更新缓存</span>
<p class="text-[13px] text-[#666] mt-[8px] multi-hidden">更新缓存</p>
</div>
<img src="@/app/assets/images/tools/tools_Update_cache.png" class="w-[256px] h-[148px]" />
</div>
<div class="w-[256px] h-[260px] tools-item-shadow mb-[24px] mx-[14px] rounded-[8px] flex flex-col cursor-pointer" @click="toLink('/tools/detection')">
<div class="flex-1 py-[19px] px-[24px] flex flex-col">
<span class="text-[16px] text-[#222] font-bold">环境监测</span>
<p class="text-[13px] text-[#666] mt-[8px] multi-hidden">环境监测</p>
</div>
<img src="@/app/assets/images/tools/tools_check_environment.png" class="w-[256px] h-[148px] cursor-pointer" />
</div>
<div class="w-[256px] h-[260px] tools-item-shadow mb-[24px] mx-[14px] rounded-[8px] flex flex-col cursor-pointer" @click="toLink('/tools/schedule')">
<div class="flex-1 py-[19px] px-[24px] flex flex-col">
<span class="text-[16px] text-[#222] font-bold">计划任务</span>
<p class="text-[13px] text-[#666] mt-[8px] multi-hidden">计划任务</p>
</div>
<img src="@/app/assets/images/tools/tools_schedule.png" class="w-[256px] h-[148px]" />
</div>
<div class="w-[256px] h-[260px] tools-item-shadow mb-[24px] mx-[14px] rounded-[8px] flex flex-col cursor-pointer" @click="toLink('/tools/admin')">
<div class="flex-1 py-[19px] px-[24px] flex flex-col">
<span class="text-[16px] text-[#222] font-bold">菜单管理</span>
<p class="text-[13px] text-[#666] mt-[8px] multi-hidden">菜单管理</p>
</div>
<img src="@/app/assets/images/tools/auth_menu.png" class="w-[256px] h-[148px]" />
</div>
<div class="w-[256px] h-[260px] tools-item-shadow mb-[24px] mx-[14px] rounded-[8px] flex flex-col cursor-pointer" @click="toLink('/tools/authorize')">
<div class="flex-1 py-[19px] px-[24px] flex flex-col">
<span class="text-[16px] text-[#222] font-bold">授权信息</span>
<p class="text-[13px] text-[#666] mt-[8px] multi-hidden">查看授权信息及重新认证授权</p>
</div>
<img src="@/app/assets/images/tools/app_auth.png" class="w-[256px] h-[148px]" />
</div>
<div class="w-[256px] h-[260px] tools-item-shadow mb-[24px] mx-[14px] rounded-[8px] flex flex-col cursor-pointer" @click="goRouter">
<div class="flex-1 py-[19px] px-[24px] flex flex-col">
<span class="text-[16px] text-[#222] font-bold">官方市场</span>
<p class="text-[13px] text-[#666] mt-[8px] multi-hidden">官方市场</p>
</div>
<img src="@/app/assets/images/tools/official_market.png" class="w-[256px] h-[148px]" />
</div>
</div> </div>
</div> </div>
</template> </template>
@ -41,60 +78,29 @@
import storage from '@/utils/storage' import storage from '@/utils/storage'
import {findFirstValidRoute} from '@/router/routers' import {findFirstValidRoute} from '@/router/routers'
import {UserFilled} from '@element-plus/icons-vue' import {UserFilled} from '@element-plus/icons-vue'
const router = useRouter() const router = useRouter()
import useUserStore from '@/stores/modules/user'
const userStore = useUserStore() const toLink = (link)=>{
const loading = ref(true) router.push(link)
const detail = reactive({
appList: []
})
const appLink: any = ref({})
const menus = computed(() => {
let obj = []
loading.value = true;
userStore.routers.forEach((item, index) => {
if (item.meta.key == 'tool' && item.children && item.children.length) {
item.children.forEach((childItem,childIndex) => {
if(childItem.meta.show == 1){
obj.push(childItem);
}
});
}
})
loading.value = false;
return obj
})
const toLink = (data)=>{
router.push({ name: data.name })
} }
const goRouter = () => { const goRouter = () => {
window.open('https://www.niucloud.com/product') window.open('https://www.niucloud.com/product')
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped></style>
.main-container {
background: linear-gradient(180deg, rgba(253, 253, 253, 0.24) 0%, #FAFAFA 100%);
min-height: calc(100vh - 64px);
}
.overview-top {
background-image: url('@/app/assets/images/index/overview.png');
background-repeat: no-repeat;
background-size: cover;
}
.app-item {
box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.18);
}
</style>
<style> <style>
.overview-empty .el-empty__image { .tools-item-shadow{
width: auto !important; box-shadow: 0px 0px 6px rgba(183, 183, 175, 1);
}
/* 多行超出隐藏 */
.multi-hidden {
word-break: break-all;
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
} }
</style> </style>

View File

@ -1,11 +1,11 @@
<template> <template>
<div class="flex border-t border-b main-wrap border-color w-full" :class="scene == 'select' ? 'h-[40vh]' : 'h-full'"> <div class="flex border-t border-b main-wrap border-color w-full" :class="scene == 'select' ? 'h-[430px]' : 'h-full'">
<!-- 分组 --> <!-- 分组 -->
<div class="group-wrap w-[180px] p-[15px] h-full border-r border-color flex flex-col"> <div class="group-wrap w-[180px] p-[15px] h-full border-r border-color flex flex-col">
<el-input v-model="categoryParam.name" class="m-0" :placeholder="t('upload.attachmentCategoryPlaceholder')" clearable prefix-icon="Search" @input="getAttachmentCategoryList()"/> <el-input v-model="categoryParam.name" class="m-0" :placeholder="t('upload.attachmentCategoryPlaceholder')" clearable prefix-icon="Search" @input="getAttachmentCategoryList()"/>
<div class="group-list flex-1 my-[10px]"> <div class="group-list flex-1 my-[10px]">
<el-scrollbar> <el-scrollbar height="300px">
<div class="group-item p-[10px] leading-none text-xs rounded cursor-pointer" :class="{ active: attachmentParam.cate_id == 0 }" @click="attachmentParam.cate_id = 0"> <div class="group-item p-[10px] leading-none text-xs rounded cursor-pointer" :class="{ active: attachmentParam.cate_id == 0 }" @click="attachmentParam.cate_id = 0">
{{ t('selectPlaceholder') }} {{ t('selectPlaceholder') }}
</div> </div>

View File

@ -12,7 +12,8 @@
<span class="cursor-pointer mr-[15px]" @click="goNiucloud">{{ t('niucloud') }}</span> <span class="cursor-pointer mr-[15px]" @click="goNiucloud">{{ t('niucloud') }}</span>
<el-dropdown> <el-dropdown>
<div class="userinfo flex h-full items-center"> <div class="userinfo flex h-full items-center">
<el-avatar :size="25" :icon="UserFilled" /> <el-avatar :size="25" :icon="UserFilled" v-if="!userStore.userInfo.head_img" />
<el-avatar :size="25" v-else :src="img(userStore.userInfo.head_img)" />
<div class="user-name pl-[8px] text-[#fff]">{{ userStore.userInfo.username }}</div> <div class="user-name pl-[8px] text-[#fff]">{{ userStore.userInfo.username }}</div>
<icon name="element-ArrowDown" class="ml-[5px] !text-[#fff]" /> <icon name="element-ArrowDown" class="ml-[5px] !text-[#fff]" />
</div> </div>
@ -53,10 +54,12 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { UserFilled } from '@element-plus/icons-vue'
import { computed, reactive, ref, onMounted, watch } from 'vue' import { computed, reactive, ref, onMounted, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import type { FormInstance, FormRules, ElNotification } from 'element-plus' import type { FormInstance, FormRules, ElNotification } from 'element-plus'
import { t } from '@/lang' import { t } from '@/lang'
import { img } from '@/utils/common'
import {getEnv} from '@/app/api/sys' import {getEnv} from '@/app/api/sys'
import { setUserInfo } from '@/app/api/personal' import { setUserInfo } from '@/app/api/personal'
import useUserStore from '@/stores/modules/user' import useUserStore from '@/stores/modules/user'
@ -82,7 +85,7 @@ const goRouter = () => {
// //
const goDeveloperCenter = () => { const goDeveloperCenter = () => {
router.push('/tools/addon') router.push('/app_manage/tools')
} }
const goNiucloud = () => { const goNiucloud = () => {

View File

@ -1,11 +1,11 @@
<template> <template>
<el-container class="w-screen h-screen min-w-[1200px] flex flex-col"> <el-container class="w-screen h-screen min-w-[1200px] flex flex-col">
<layout-aside></layout-aside> <layout-aside></layout-aside>
<el-container class="overview-top"> <el-container>
<el-header class="h-[60px]"> <el-header class="h-[60px]">
<layout-header></layout-header> <layout-header></layout-header>
</el-header> </el-header>
<el-main class="w-[1200px] m-auto"> <el-main class="p-0">
<el-scrollbar class="main-height"> <el-scrollbar class="main-height">
<router-view></router-view> <router-view></router-view>
</el-scrollbar> </el-scrollbar>
@ -23,11 +23,7 @@ import layoutAside from './components/aside/index.vue'
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.overview-top {
background-image: url('@/app/assets/images/index/overview.png');
background-repeat: no-repeat;
background-size: cover;
}
.main-height{ .main-height{
height: calc(100vh - 120px); height: calc(100vh - 120px);
} }

View File

@ -2,7 +2,8 @@
<div> <div>
<el-dropdown @command="clickEvent" :tabindex="1"> <el-dropdown @command="clickEvent" :tabindex="1">
<div class="userinfo flex h-full items-center"> <div class="userinfo flex h-full items-center">
<el-avatar :size="25" :icon="UserFilled" /> <el-avatar :size="25" :icon="UserFilled" v-if="!userStore.userInfo.head_img" />
<el-avatar :size="25" v-else :src="img(userStore.userInfo.head_img)" />
<div class="user-name pl-[8px]">{{ userStore.userInfo.username }}</div> <div class="user-name pl-[8px]">{{ userStore.userInfo.username }}</div>
<icon name="element-ArrowDown" class="ml-[5px]" /> <icon name="element-ArrowDown" class="ml-[5px]" />
</div> </div>
@ -43,6 +44,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { UserFilled } from '@element-plus/icons-vue' import { UserFilled } from '@element-plus/icons-vue'
import { img } from '@/utils/common'
import { computed, reactive, ref, onMounted, watch } from 'vue' import { computed, reactive, ref, onMounted, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import type { FormInstance, FormRules, ElNotification } from 'element-plus' import type { FormInstance, FormRules, ElNotification } from 'element-plus'

View File

@ -2,7 +2,8 @@
<div> <div>
<el-dropdown @command="clickEvent" :tabindex="1"> <el-dropdown @command="clickEvent" :tabindex="1">
<div class="userinfo flex h-full items-center"> <div class="userinfo flex h-full items-center">
<el-avatar :size="25" :icon="UserFilled" /> <el-avatar :size="25" :icon="UserFilled" v-if="!userStore.userInfo.head_img" />
<el-avatar :size="25" v-else :src="img(userStore.userInfo.head_img)" />
<div class="user-name pl-[8px]">{{ userStore.userInfo.username }}</div> <div class="user-name pl-[8px]">{{ userStore.userInfo.username }}</div>
<icon name="element-ArrowDown" class="ml-[5px]" /> <icon name="element-ArrowDown" class="ml-[5px]" />
</div> </div>
@ -40,6 +41,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { img } from '@/utils/common'
import { UserFilled } from '@element-plus/icons-vue' import { UserFilled } from '@element-plus/icons-vue'
import { computed, reactive, ref, onMounted, watch } from 'vue' import { computed, reactive, ref, onMounted, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'

View File

@ -2,7 +2,8 @@
<div> <div>
<el-dropdown @command="clickEvent" :tabindex="1"> <el-dropdown @command="clickEvent" :tabindex="1">
<div class="userinfo flex h-full items-center"> <div class="userinfo flex h-full items-center">
<el-avatar :size="25" :icon="UserFilled" /> <el-avatar :size="25" :icon="UserFilled" v-if="!userStore.userInfo.head_img" />
<el-avatar :size="25" v-else :src="img(userStore.userInfo.head_img)" />
<div class="user-name pl-[8px]">{{ userStore.userInfo.username }}</div> <div class="user-name pl-[8px]">{{ userStore.userInfo.username }}</div>
<icon name="element-ArrowDown" class="ml-[5px]" /> <icon name="element-ArrowDown" class="ml-[5px]" />
</div> </div>
@ -46,6 +47,7 @@ import { computed, reactive, ref, onMounted, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import type { FormInstance, FormRules, ElNotification } from 'element-plus' import type { FormInstance, FormRules, ElNotification } from 'element-plus'
import { t } from '@/lang' import { t } from '@/lang'
import { img } from '@/utils/common'
import {getEnv} from '@/app/api/sys' import {getEnv} from '@/app/api/sys'
import { setUserInfo } from '@/app/api/personal' import { setUserInfo } from '@/app/api/personal'
import useUserStore from '@/stores/modules/user' import useUserStore from '@/stores/modules/user'

View File

@ -1,17 +1,11 @@
<template> <template>
<div class="m-[25px] w-[200px] bg-[#fff] aside-shadow app-aside-wrap"> <div class="mt-[20px] mb-[15px] mx-[10px] app-aside-wrap bg-[#fff]">
<el-menu :router="true" unique-opened="true" :default-active="String(route.name)"> <div class="flex flex-wrap items-center">
<template v-for="(item, index) in menus" :key="index"> <div v-for="(item, index) in menus" :key="index" :class="['border-[1px] border-solid my-[5px] border-[#E0E0E0] rounded-full py-[5px] px-[10px] cursor-pointer',{'mr-[20px]': index != menus.length-1},{'text-[#fff] bg-[#000] border-[#000]': item.name == route.name}]" @click="toLink(item)">
<el-menu-item v-if="item.meta.key != 'official_market'" @click="toLink(item)" :index="String(item.name)"> <icon v-if="item.meta.icon" :name="item.meta.icon" class="!w-auto mr-[4px]" size="14px" :title="item.meta.title" />
<icon v-if="item.meta.icon" :name="item.meta.icon" class="!w-auto mr-[6px]" size="16px" :title="item.meta.title" /> <span class="text-[14px]">{{ item.meta.title }}</span>
<span>{{ item.meta.title }}</span> </div>
</el-menu-item> </div>
<div class="el-menu-item" v-else @click="toLink(item)">
<icon v-if="item.meta.icon" :name="item.meta.icon" class="!w-auto mr-[6px]" size="16px" :title="item.meta.title" />
<span class="text-[14px]">{{ item.meta.title }}</span>
</div>
</template>
</el-menu>
</div> </div>
</template> </template>
@ -52,9 +46,6 @@ import useUserStore from '@/stores/modules/user'
</script> </script>
<style lang="scss"> <style lang="scss">
.aside-shadow{
box-shadow: 0 0 10px 1px rgba(0,0,0,0.1);
}
.app-aside-wrap{ .app-aside-wrap{
.el-menu-item{ .el-menu-item{
border-bottom: 1px solid #f1f1f1; border-bottom: 1px solid #f1f1f1;

View File

@ -12,7 +12,8 @@
<span class="cursor-pointer mr-[15px]" @click="goNiucloud">{{ t('niucloud') }}</span> <span class="cursor-pointer mr-[15px]" @click="goNiucloud">{{ t('niucloud') }}</span>
<el-dropdown> <el-dropdown>
<div class="userinfo flex h-full items-center"> <div class="userinfo flex h-full items-center">
<el-avatar :size="25" :icon="UserFilled" /> <el-avatar :size="25" :icon="UserFilled" v-if="!userStore.userInfo.head_img" />
<el-avatar :size="25" v-else :src="img(userStore.userInfo.head_img)" />
<div class="user-name pl-[8px] text-[#fff]">{{ userStore.userInfo.username }}</div> <div class="user-name pl-[8px] text-[#fff]">{{ userStore.userInfo.username }}</div>
<icon name="element-ArrowDown" class="ml-[5px] !text-[#fff]" /> <icon name="element-ArrowDown" class="ml-[5px] !text-[#fff]" />
</div> </div>
@ -26,7 +27,7 @@
</template> </template>
</el-dropdown> </el-dropdown>
</div> </div>
<el-dialog v-model="changePasswordDialog" title="修改密码" :before-close="handleClose"> <el-dialog v-model="changePasswordDialog" width="450px" title="修改密码" :before-close="handleClose">
<div> <div>
<el-form :model="saveInfo" label-width="90px" ref="formRef" :rules="formRules" class="page-form"> <el-form :model="saveInfo" label-width="90px" ref="formRef" :rules="formRules" class="page-form">
<el-form-item :label="t('originalPassword')" prop="original_password"> <el-form-item :label="t('originalPassword')" prop="original_password">
@ -56,6 +57,7 @@ import { computed, reactive, ref, onMounted, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import type { FormInstance, FormRules, ElNotification } from 'element-plus' import type { FormInstance, FormRules, ElNotification } from 'element-plus'
import { t } from '@/lang' import { t } from '@/lang'
import { img } from '@/utils/common'
import {getEnv} from '@/app/api/sys' import {getEnv} from '@/app/api/sys'
import useUserStore from '@/stores/modules/user' import useUserStore from '@/stores/modules/user'
import { setUserInfo } from '@/app/api/personal' import { setUserInfo } from '@/app/api/personal'
@ -80,7 +82,7 @@ const goRouter = () => {
// //
const goDeveloperCenter = () => { const goDeveloperCenter = () => {
router.push('/tools/addon') router.push('/app_manage/tools')
} }
const goNiucloud = () => { const goNiucloud = () => {

View File

@ -3,10 +3,10 @@
<el-header class="h-[60px]"> <el-header class="h-[60px]">
<layout-header></layout-header> <layout-header></layout-header>
</el-header> </el-header>
<el-container class="overview-top"> <el-container class="flex flex-col px-[64px]">
<layout-aside></layout-aside> <layout-aside></layout-aside>
<el-main class="p-0 box-shadow w-[1200px] m-[25px] ml-0 bg-[#fff]"> <el-main class="p-0 ml-0">
<el-scrollbar class="main-height"> <el-scrollbar class="main-height">
<router-view></router-view> <router-view></router-view>
</el-scrollbar> </el-scrollbar>
@ -30,9 +30,6 @@ import layoutAside from './components/aside/index.vue'
background-size: cover; background-size: cover;
} }
.main-height{ .main-height{
height: calc(100vh - 120px); height: calc(100vh - 175px);
}
.box-shadow{
box-shadow: 0 0 10px 1px rgba(151, 151, 151, 0.1);
} }
</style> </style>

View File

@ -1,8 +1,8 @@
@font-face { @font-face {
font-family: "iconfont"; /* Project id 3883393 */ font-family: "iconfont"; /* Project id 3883393 */
src: url('//at.alicdn.com/t/c/font_3883393_t7tf6zkctc.woff2?t=1697534072773') format('woff2'), src: url('//at.alicdn.com/t/c/font_3883393_zbqffn9fec.woff2?t=1698204164945') format('woff2'),
url('//at.alicdn.com/t/c/font_3883393_t7tf6zkctc.woff?t=1697534072773') format('woff'), url('//at.alicdn.com/t/c/font_3883393_zbqffn9fec.woff?t=1698204164945') format('woff'),
url('//at.alicdn.com/t/c/font_3883393_t7tf6zkctc.ttf?t=1697534072773') format('truetype'); url('//at.alicdn.com/t/c/font_3883393_zbqffn9fec.ttf?t=1698204164945') format('truetype');
} }
.iconfont { .iconfont {
@ -13,6 +13,18 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.iconxiazai01:before {
content: "\ea38";
}
.iconjingdiandingdan:before {
content: "\e6ea";
}
.iconluxiandingdan:before {
content: "\e6eb";
}
.iconfapiaoguanli:before { .iconfapiaoguanli:before {
content: "\e683"; content: "\e683";
} }