196 lines
4.6 KiB
Vue
196 lines
4.6 KiB
Vue
<template>
|
||
<Dialog
|
||
v-model="show"
|
||
:options="{
|
||
title: '在您的站点上安装应用',
|
||
size: '4xl'
|
||
}"
|
||
>
|
||
<template #body-content>
|
||
<ObjectList :options="listOptions" />
|
||
</template>
|
||
</Dialog>
|
||
</template>
|
||
|
||
<script>
|
||
import { getCachedDocumentResource, createResource } from 'jingrow-ui';
|
||
import { defineAsyncComponent, h } from 'vue';
|
||
import { toast } from 'vue-sonner';
|
||
import { renderDialog } from '../../utils/components';
|
||
import router from '../../router';
|
||
import ObjectList from '../ObjectList.vue';
|
||
import { getToastErrorMessage } from '../../utils/toast';
|
||
|
||
export default {
|
||
props: {
|
||
site: {
|
||
type: String,
|
||
required: true
|
||
}
|
||
},
|
||
emits: ['installed'],
|
||
components: {
|
||
ObjectList
|
||
},
|
||
data() {
|
||
return {
|
||
show: true,
|
||
installPending: false
|
||
};
|
||
},
|
||
computed: {
|
||
$site() {
|
||
return getCachedDocumentResource('Site', this.site);
|
||
},
|
||
listOptions() {
|
||
const handleInstall = async row => {
|
||
// 防止重复点击或正在处理中
|
||
if (this.$site.installApp.loading || this.installPending) return;
|
||
|
||
// 确保有有效的app
|
||
if (!row || !row.app) {
|
||
toast.error('无效的应用信息');
|
||
return;
|
||
}
|
||
|
||
// 标记为处理中,防止重复点击
|
||
this.installPending = true;
|
||
|
||
try {
|
||
// 直接创建一个资源进行应用安装权限检查
|
||
const checkResource = createResource({
|
||
url: 'jcloud.api.site.check_app_installable',
|
||
params: {
|
||
name: this.site,
|
||
app: row.app
|
||
},
|
||
auto: true
|
||
});
|
||
|
||
// 等待资源加载完成
|
||
await new Promise(resolve => {
|
||
const unwatch = this.$watch(
|
||
() => !checkResource.loading,
|
||
isNotLoading => {
|
||
if (isNotLoading) {
|
||
unwatch();
|
||
resolve();
|
||
}
|
||
},
|
||
{ immediate: true }
|
||
);
|
||
});
|
||
|
||
// 获取检查结果
|
||
const checkResult = checkResource.data;
|
||
|
||
// 如果API返回错误或结果无效,取消安装
|
||
if (!checkResult) {
|
||
toast.error('应用安装权限验证失败,安装已取消');
|
||
this.installPending = false;
|
||
return;
|
||
}
|
||
|
||
// 如果应用不可安装,显示对应提示
|
||
if (!checkResult.installable) {
|
||
const subscriptionTypeMap = {
|
||
2: 'Pro',
|
||
3: 'Business',
|
||
4: 'Enterprise',
|
||
5: 'Ultimate'
|
||
};
|
||
|
||
const requiredPlan = subscriptionTypeMap[checkResult.required_plan_level] ||
|
||
`Level ${checkResult.required_plan_level}`;
|
||
|
||
toast.error(`无法安装此应用,需要${requiredPlan}或更高级别的站点计划。请先升级您的站点计划。`);
|
||
this.installPending = false;
|
||
return;
|
||
}
|
||
|
||
// 应用可安装,执行安装流程
|
||
await toast.promise(
|
||
this.$site.installApp.submit({ app: row.app })
|
||
.then(jobId => {
|
||
this.$emit('installed');
|
||
this.show = false;
|
||
return jobId;
|
||
}),
|
||
{
|
||
loading: '正在创建安装任务...',
|
||
success: () => '应用安装任务已创建',
|
||
error: e => {
|
||
// 尝试从错误对象中提取消息
|
||
const errorMessage = e?.message || e?.messages?.[0] || e?.exc_info?.exception || '安装任务创建失败';
|
||
|
||
// 返回用户友好的错误消息
|
||
return typeof errorMessage === 'string' ? errorMessage : '安装任务创建失败,请稍后重试';
|
||
}
|
||
}
|
||
);
|
||
|
||
} catch (error) {
|
||
toast.error('无法验证安装权限,安装已取消');
|
||
} finally {
|
||
// 无论成功失败,都清除处理标记
|
||
this.installPending = false;
|
||
}
|
||
};
|
||
|
||
return {
|
||
label: '应用',
|
||
fieldname: 'app',
|
||
fieldtype: 'ListSelection',
|
||
emptyStateMessage:
|
||
'未找到应用' +
|
||
(!this.$site.pg?.group_public
|
||
? '。请从您的工作台添加它们。'
|
||
: ''),
|
||
columns: [
|
||
{
|
||
label: '标题',
|
||
fieldname: 'title',
|
||
class: 'font-medium',
|
||
width: 2,
|
||
format: (value, row) => value || row.app_title
|
||
},
|
||
{
|
||
label: '仓库',
|
||
fieldname: 'repository_owner',
|
||
class: 'text-gray-600',
|
||
width: '10rem'
|
||
},
|
||
{
|
||
label: '版本',
|
||
fieldname: 'branch',
|
||
class: 'text-gray-600',
|
||
width: '20rem'
|
||
},
|
||
{
|
||
label: '',
|
||
fieldname: '',
|
||
align: 'right',
|
||
type: 'Button',
|
||
width: '5rem',
|
||
Button: function({ row }) {
|
||
return {
|
||
label: '安装',
|
||
onClick: () => handleInstall(row)
|
||
};
|
||
}
|
||
}
|
||
],
|
||
resource: () => {
|
||
return {
|
||
url: 'jcloud.api.site.available_apps',
|
||
params: {
|
||
name: this.site
|
||
},
|
||
auto: true
|
||
};
|
||
}
|
||
};
|
||
}
|
||
}
|
||
};
|
||
</script> |