feat: Support for Quick Website Name Editing (#10479)

This commit is contained in:
CityFun 2025-09-25 15:25:23 +08:00 committed by GitHub
parent d93f8c5e6e
commit 6b24ee23e6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 143 additions and 98 deletions

View file

@ -0,0 +1,130 @@
<template>
<div class="name-row">
<div>
<el-input
v-if="isEditing"
v-model="editValue"
@keyup.enter="saveEdit"
@blur="saveEdit"
@keyup.esc="cancelEdit"
class="domain-input"
ref="inputRef"
/>
<el-text v-else type="primary" class="cursor-pointer" @click="openConfig(row.id)">
{{ row.primaryDomain }}
</el-text>
<el-popover placement="right" trigger="hover" :width="300" @before-enter="searchDomains(row.id)">
<template #reference>
<el-button link icon="Promotion" class="ml-2.5"></el-button>
</template>
<table>
<tbody>
<tr v-for="(domain, index) in domains" :key="index">
<td>
<el-button type="primary" link @click="openUrl(getUrl(domain, row))">
{{ getUrl(domain, row) }}
</el-button>
</td>
<td>
<CopyButton :content="getUrl(domain, row)" />
</td>
</tr>
</tbody>
</table>
</el-popover>
<el-button link icon="edit" @click="startEdit" v-if="!isEditing"></el-button>
</div>
<div>
<el-tooltip effect="dark" :content="$t('website.cancelFavorite')" placement="top-start" v-if="row.favorite">
<el-button link size="large" icon="StarFilled" type="warning" @click="favoriteWebsite(row)"></el-button>
</el-tooltip>
<el-tooltip
effect="dark"
:content="$t('website.favorite')"
placement="top-start"
v-if="!row.favorite && isHovered"
>
<el-button link icon="Star" type="info" @click="favoriteWebsite(row)"></el-button>
</el-tooltip>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { listDomains } from '@/api/modules/website';
import { Website } from '@/api/interface/website';
import { routerToNameWithParams } from '@/utils/router';
interface Props {
row: Website.Website;
isHovered: boolean;
}
const props = defineProps<Props>();
const emit = defineEmits(['favoriteChange', 'domainEdit']);
const inputRef = ref();
const isEditing = ref(false);
const editValue = ref('');
const domains = ref<Website.Domain[]>([]);
const startEdit = () => {
editValue.value = props.row.primaryDomain;
isEditing.value = true;
nextTick(() => {
inputRef.value?.focus();
inputRef.value?.select();
});
};
const saveEdit = () => {
if (editValue.value.trim() && editValue.value !== props.row.primaryDomain) {
emit('domainEdit', props.row, editValue.value.trim());
}
isEditing.value = false;
};
const cancelEdit = () => {
editValue.value = props.row.primaryDomain;
isEditing.value = false;
};
const openConfig = (id: number) => {
routerToNameWithParams('WebsiteConfig', { id: id, tab: 'basic' });
};
const searchDomains = (id: number) => {
listDomains(id).then((res) => {
domains.value = res.data;
});
};
const openUrl = (url: string) => {
window.open(url);
};
const getUrl = (domain: Website.Domain, website: Website.Website): string => {
const protocol = website.protocol.toLowerCase();
let url = protocol + '://' + domain.domain;
if (protocol == 'http' && domain.port != 80) {
url = url + ':' + domain.port;
}
if (protocol == 'https' && domain.ssl) {
url = url + ':' + domain.port;
}
return url;
};
const favoriteWebsite = (row: Website.Website) => {
emit('favoriteChange', row);
};
</script>
<style lang="css" scoped>
.name-row {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
}
</style>

View file

@ -89,71 +89,12 @@
show-overflow-tooltip
>
<template #default="{ row, $index }">
<div class="name-row">
<div>
<el-text type="primary" class="cursor-pointer" @click="openConfig(row.id)">
{{ row.primaryDomain }}
</el-text>
<el-popover
placement="right"
trigger="hover"
:width="300"
@before-enter="searchDomains(row.id)"
>
<template #reference>
<el-button link icon="Promotion" class="ml-2.5"></el-button>
</template>
<table>
<tbody>
<tr v-for="(domain, index) in domains" :key="index">
<td>
<el-button
type="primary"
link
@click="openUrl(getUrl(domain, row))"
>
{{ getUrl(domain, row) }}
</el-button>
</td>
<td>
<CopyButton :content="getUrl(domain, row)" />
</td>
</tr>
</tbody>
</table>
</el-popover>
</div>
<div>
<el-tooltip
effect="dark"
:content="$t('website.cancelFavorite')"
placement="top-start"
v-if="row.favorite"
>
<el-button
link
size="large"
icon="StarFilled"
type="warning"
@click="favoriteWebsite(row)"
></el-button>
</el-tooltip>
<el-tooltip
effect="dark"
:content="$t('website.favorite')"
placement="top-start"
v-if="!row.favorite && hoveredRowIndex === $index"
>
<el-button
link
icon="Star"
type="info"
@click="favoriteWebsite(row)"
></el-button>
</el-tooltip>
</div>
</div>
<Domain
:row="row"
:is-hovered="hoveredRowIndex === $index"
@favorite-change="favoriteWebsite"
@domain-edit="handleDomainEdit"
/>
</template>
</el-table-column>
<el-table-column
@ -333,9 +274,10 @@ import NginxConfig from '@/views/website/website/nginx/index.vue';
import GroupDialog from '@/components/agent-group/index.vue';
import AppStatus from '@/components/app-status/index.vue';
import TaskLog from '@/components/log/task/index.vue';
import Domain from '@/views/website/website/domain/index.vue';
import i18n from '@/lang';
import { onMounted, reactive, ref, computed } from 'vue';
import { batchOpreate, listDomains, opWebsite, searchWebsites, updateWebsite } from '@/api/modules/website';
import { batchOpreate, opWebsite, searchWebsites, updateWebsite } from '@/api/modules/website';
import { Website } from '@/api/interface/website';
import { App } from '@/api/interface/app';
import { ElMessageBox } from 'element-plus';
@ -383,7 +325,6 @@ const defaultRef = ref();
const data = ref();
let groups = ref<Group.GroupInfo[]>([]);
const dataRef = ref();
const domains = ref<Website.Domain[]>([]);
const columns = ref([]);
const hoveredRowIndex = ref(-1);
const websiteDir = ref();
@ -431,6 +372,11 @@ const favoriteWebsite = (row: Website.Website) => {
updateWebsitConfig(row);
};
const handleDomainEdit = (row: Website.Website, domain: string) => {
row.primaryDomain = domain;
updateWebsitConfig(row);
};
const disabledConfig = computed(() => {
return nginxStatus.value != 'Running';
});
@ -647,28 +593,6 @@ const operateWebsite = (op: string, id: number) => {
});
};
const searchDomains = (id: number) => {
listDomains(id).then((res) => {
domains.value = res.data;
});
};
const openUrl = (url: string) => {
window.open(url);
};
const getUrl = (domain: Website.Domain, website: Website.Website): string => {
const protocol = website.protocol.toLowerCase();
let url = protocol + '://' + domain.domain;
if (protocol == 'http' && domain.port != 80) {
url = url + ':' + domain.port;
}
if (protocol == 'https' && domain.ssl) {
url = url + ':' + domain.port;
}
return url;
};
const updateRemark = (row: Website.Website, bulr: Function) => {
bulr();
if (row.remark && row.remark.length > 128) {
@ -702,12 +626,3 @@ onMounted(() => {
listGroup();
});
</script>
<style lang="css" scoped>
.name-row {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
}
</style>