mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-10-10 15:36:45 +08:00
269 lines
9 KiB
Vue
269 lines
9 KiB
Vue
<template>
|
|
<LayoutContent v-loading="loading" v-if="!showDetail" :title="$t('app.app')">
|
|
<template #toolbar>
|
|
<el-row :gutter="5">
|
|
<el-col :span="20">
|
|
<el-button
|
|
class="tag-button"
|
|
:class="activeTag === 'all' ? '' : 'no-active'"
|
|
@click="changeTag('all')"
|
|
:type="activeTag === 'all' ? 'primary' : ''"
|
|
:plain="activeTag !== 'all'"
|
|
>
|
|
{{ $t('app.all') }}
|
|
</el-button>
|
|
<div v-for="item in tags" :key="item.key" style="display: inline">
|
|
<el-button
|
|
class="tag-button"
|
|
:class="activeTag === item.key ? '' : 'no-active'"
|
|
@click="changeTag(item.key)"
|
|
:type="activeTag === item.key ? 'primary' : ''"
|
|
:plain="activeTag !== item.key"
|
|
>
|
|
{{ language == 'zh' ? item.name : item.key }}
|
|
</el-button>
|
|
</div>
|
|
</el-col>
|
|
<el-col :span="4">
|
|
<div class="search-button">
|
|
<el-input
|
|
v-model="req.name"
|
|
clearable
|
|
@clear="searchByName('')"
|
|
suffix-icon="Search"
|
|
@change="searchByName(req.name)"
|
|
:placeholder="$t('commons.button.search')"
|
|
></el-input>
|
|
</div>
|
|
</el-col>
|
|
</el-row>
|
|
</template>
|
|
<template #rightButton>
|
|
<el-badge is-dot class="item" :hidden="!canUpdate">
|
|
<el-button @click="sync" type="primary" link :plain="true">{{ $t('app.syncAppList') }}</el-button>
|
|
</el-badge>
|
|
</template>
|
|
<template #main>
|
|
<el-row :gutter="5">
|
|
<el-col v-for="(app, index) in apps" :key="index" :xs="12" :sm="12" :md="8" :lg="8" :xl="8">
|
|
<div class="app-card">
|
|
<el-card class="e-card">
|
|
<el-row :gutter="24">
|
|
<el-col :xs="5" :sm="5" :md="6" :lg="6" :xl="5">
|
|
<div class="app-icon">
|
|
<el-avatar
|
|
shape="square"
|
|
:size="60"
|
|
:src="'data:image/png;base64,' + app.icon"
|
|
/>
|
|
</div>
|
|
</el-col>
|
|
<el-col :xs="19" :sm="19" :md="18" :lg="18" :xl="19">
|
|
<div class="app-content">
|
|
<div class="app-header">
|
|
<span class="app-title">{{ app.name }}</span>
|
|
<el-text type="success" style="margin-left: 10px" v-if="app.installed">
|
|
{{ $t('app.allReadyInstalled') }}
|
|
</el-text>
|
|
<el-button
|
|
class="app-button"
|
|
type="primary"
|
|
plain
|
|
round
|
|
size="small"
|
|
@click="getAppDetail(app.key)"
|
|
>
|
|
{{ $t('app.install') }}
|
|
</el-button>
|
|
</div>
|
|
<div class="app-desc">
|
|
<span class="desc">
|
|
{{ language == 'zh' ? app.shortDescZh : app.shortDescEn }}
|
|
</span>
|
|
</div>
|
|
<div class="app-tag">
|
|
<el-tag v-for="(tag, ind) in app.tags" :key="ind" style="margin-right: 5px">
|
|
<span :style="{ color: getColor(ind) }">
|
|
{{ language == 'zh' ? tag.name : tag.key }}
|
|
</span>
|
|
</el-tag>
|
|
<el-tag v-if="app.status === 'TakeDown'" style="margin-right: 5px">
|
|
<span style="color: red">{{ $t('app.takeDown') }}</span>
|
|
</el-tag>
|
|
</div>
|
|
</div>
|
|
</el-col>
|
|
</el-row>
|
|
</el-card>
|
|
</div>
|
|
</el-col>
|
|
</el-row>
|
|
</template>
|
|
</LayoutContent>
|
|
<Detail v-if="showDetail" :id="appId"></Detail>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import LayoutContent from '@/layout/layout-content.vue';
|
|
import { App } from '@/api/interface/app';
|
|
import { onMounted, reactive, ref } from 'vue';
|
|
import { GetAppListUpdate, GetAppTags, SearchApp, SyncApp } from '@/api/modules/app';
|
|
import i18n from '@/lang';
|
|
import Detail from '../detail/index.vue';
|
|
import router from '@/routers';
|
|
import { MsgSuccess } from '@/utils/message';
|
|
import { useI18n } from 'vue-i18n';
|
|
|
|
const language = useI18n().locale.value;
|
|
|
|
const req = reactive({
|
|
name: '',
|
|
tags: [],
|
|
page: 1,
|
|
pageSize: 50,
|
|
});
|
|
|
|
const apps = ref<App.AppDTO[]>([]);
|
|
const tags = ref<App.Tag[]>([]);
|
|
const colorArr = ['#005eeb', '#008B45', '#BEBEBE', '#FFF68F', '#FFFF00', '#8B0000'];
|
|
const loading = ref(false);
|
|
const activeTag = ref('all');
|
|
const showDetail = ref(false);
|
|
const appId = ref(0);
|
|
const canUpdate = ref(false);
|
|
|
|
const getColor = (index: number) => {
|
|
return colorArr[index];
|
|
};
|
|
|
|
const search = async (req: App.AppReq) => {
|
|
loading.value = true;
|
|
await SearchApp(req)
|
|
.then((res) => {
|
|
apps.value = res.data.items;
|
|
})
|
|
.finally(() => {
|
|
loading.value = false;
|
|
});
|
|
GetAppTags().then((res) => {
|
|
tags.value = res.data;
|
|
});
|
|
};
|
|
|
|
const getAppDetail = (key: string) => {
|
|
router.push({ name: 'AppDetail', params: { appKey: key } });
|
|
};
|
|
|
|
const sync = () => {
|
|
loading.value = true;
|
|
SyncApp()
|
|
.then((res) => {
|
|
if (res.message != '') {
|
|
MsgSuccess(res.message);
|
|
} else {
|
|
MsgSuccess(i18n.global.t('app.syncStart'));
|
|
}
|
|
canUpdate.value = false;
|
|
search(req);
|
|
})
|
|
.finally(() => {
|
|
loading.value = false;
|
|
});
|
|
};
|
|
|
|
const getAppListUpdate = async () => {
|
|
const res = await GetAppListUpdate();
|
|
canUpdate.value = res.data.canUpdate;
|
|
};
|
|
|
|
const changeTag = (key: string) => {
|
|
req.tags = [];
|
|
activeTag.value = key;
|
|
if (key !== 'all') {
|
|
req.tags = [key];
|
|
}
|
|
search(req);
|
|
};
|
|
|
|
const searchByName = (name: string) => {
|
|
req.name = name;
|
|
search(req);
|
|
};
|
|
|
|
onMounted(() => {
|
|
getAppListUpdate();
|
|
search(req);
|
|
});
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.header {
|
|
padding-bottom: 10px;
|
|
}
|
|
|
|
.app-card {
|
|
margin-top: 10px;
|
|
cursor: pointer;
|
|
padding: 5px;
|
|
|
|
.app-icon {
|
|
margin-top: 10px;
|
|
margin-left: 10px;
|
|
}
|
|
|
|
.app-content {
|
|
margin-top: 10px;
|
|
height: 100%;
|
|
width: 100%;
|
|
|
|
.app-header {
|
|
height: 20%;
|
|
.app-title {
|
|
font-weight: 500;
|
|
font-size: 16px;
|
|
color: var(--el-text-color-regular);
|
|
}
|
|
.app-button {
|
|
float: right;
|
|
margin-right: 20px;
|
|
}
|
|
}
|
|
|
|
.app-desc {
|
|
margin-top: 5px;
|
|
overflow: hidden;
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 2;
|
|
-webkit-box-orient: vertical;
|
|
|
|
text-overflow: ellipsis;
|
|
height: 45px;
|
|
|
|
.desc {
|
|
font-size: 14px;
|
|
color: var(--el-text-color-regular);
|
|
}
|
|
}
|
|
|
|
.app-tag {
|
|
margin-top: 5px;
|
|
}
|
|
}
|
|
|
|
.e-card {
|
|
border: var(--panel-border) !important;
|
|
&:hover {
|
|
cursor: pointer;
|
|
border: 1px solid var(--el-color-primary) !important;
|
|
}
|
|
}
|
|
}
|
|
|
|
.tag-button {
|
|
margin-right: 10px;
|
|
&.no-active {
|
|
background: none;
|
|
border: none;
|
|
}
|
|
}
|
|
</style>
|