mirror of https://github.com/1Panel-dev/1Panel
Browse Source
Refs https://github.com/1Panel-dev/1Panel/issues/1654 Refs https://github.com/1Panel-dev/1Panel/issues/1387 Refs https://github.com/1Panel-dev/1Panel/issues/986pull/1744/head
zhengkunwang
1 year ago
committed by
GitHub
18 changed files with 1537 additions and 3 deletions
@ -0,0 +1,198 @@
|
||||
<template> |
||||
<el-drawer v-model="open" :close-on-click-modal="false" size="40%" :before-close="handleClose"> |
||||
<template #header> |
||||
<DrawerHeader |
||||
:header="$t('commons.button.' + redirect.operate) + $t('website.redirect')" |
||||
:back="handleClose" |
||||
/> |
||||
</template> |
||||
<el-row v-loading="loading"> |
||||
<el-col :span="22" :offset="1"> |
||||
<el-form ref="redirectForm" label-position="top" :model="redirect" :rules="rules"> |
||||
<el-form-item :label="$t('commons.table.name')" prop="name"> |
||||
<el-input |
||||
v-model.trim="redirect.name" |
||||
:disabled="redirect.operate === 'edit' || redirect.type == '404'" |
||||
></el-input> |
||||
</el-form-item> |
||||
<el-form-item :label="$t('commons.table.type')" prop="type"> |
||||
<el-select |
||||
v-model="redirect.type" |
||||
@change="changeType(redirect.type)" |
||||
:disabled="redirect.operate === 'edit'" |
||||
> |
||||
<el-option :label="$t('website.domain')" :value="'domain'"></el-option> |
||||
<el-option :label="$t('website.path')" :value="'path'"></el-option> |
||||
<el-option :label="'404'" :value="'404'"></el-option> |
||||
</el-select> |
||||
</el-form-item> |
||||
<el-form-item :label="$t('website.redirectWay')" prop="redirect"> |
||||
<el-select v-model="redirect.redirect"> |
||||
<el-option :label="'301'" :value="'301'"></el-option> |
||||
<el-option :label="'302'" :value="'302'"></el-option> |
||||
</el-select> |
||||
<span class="input-help"> |
||||
{{ $t('website.redirectHelper') }} |
||||
</span> |
||||
</el-form-item> |
||||
<el-form-item :label="$t('website.path')" prop="path" v-if="redirect.type == 'path'"> |
||||
<el-input v-model.trim="redirect.path"></el-input> |
||||
</el-form-item> |
||||
<el-form-item :label="$t('website.domain')" prop="domains" v-if="redirect.type == 'domain'"> |
||||
<el-select v-model="redirect.domains" multiple> |
||||
<el-option |
||||
v-for="(item, index) in domains" |
||||
:key="index" |
||||
:value="item.domain" |
||||
:label="item.domain" |
||||
/> |
||||
</el-select> |
||||
</el-form-item> |
||||
<el-form-item :label="$t('website.redirectRoot')" prop="redirectRoot" v-if="redirect.type == '404'"> |
||||
<el-switch v-model="redirect.redirectRoot"></el-switch> |
||||
</el-form-item> |
||||
<div v-if="!redirect.redirectRoot"> |
||||
<el-form-item :label="$t('website.targetURL')" prop="target"> |
||||
<el-input v-model.trim="redirect.target"></el-input> |
||||
</el-form-item> |
||||
<el-form-item :label="$t('website.keepPath')" prop="keepPath"> |
||||
<el-switch v-model="redirect.keepPath"></el-switch> |
||||
</el-form-item> |
||||
</div> |
||||
</el-form> |
||||
</el-col> |
||||
</el-row> |
||||
<template #footer> |
||||
<span class="dialog-footer"> |
||||
<el-button @click="handleClose" :disabled="loading">{{ $t('commons.button.cancel') }}</el-button> |
||||
<el-button type="primary" @click="submit(redirectForm)" :disabled="loading"> |
||||
{{ $t('commons.button.confirm') }} |
||||
</el-button> |
||||
</span> |
||||
</template> |
||||
</el-drawer> |
||||
</template> |
||||
|
||||
<script lang="ts" setup> |
||||
import DrawerHeader from '@/components/drawer-header/index.vue'; |
||||
import { ListDomains, OperateRedirectConfig, GetRedirectConfig } from '@/api/modules/website'; |
||||
import { Rules } from '@/global/form-rules'; |
||||
import i18n from '@/lang'; |
||||
import { FormInstance } from 'element-plus'; |
||||
import { ref } from 'vue'; |
||||
import { MsgSuccess } from '@/utils/message'; |
||||
import { Website } from '@/api/interface/website'; |
||||
|
||||
const redirectForm = ref<FormInstance>(); |
||||
const rules = ref({ |
||||
name: [Rules.requiredInput, Rules.appName], |
||||
type: [Rules.requiredSelect], |
||||
redirect: [Rules.requiredSelect], |
||||
domains: [Rules.requiredSelect], |
||||
target: [Rules.requiredInput], |
||||
path: [Rules.requiredInput], |
||||
}); |
||||
const open = ref(false); |
||||
const loading = ref(false); |
||||
|
||||
const initData = (): Website.RedirectConfig => ({ |
||||
websiteID: 0, |
||||
operate: 'create', |
||||
enable: true, |
||||
name: '', |
||||
domains: [], |
||||
keepPath: true, |
||||
type: 'domain', |
||||
redirect: '301', |
||||
target: 'http://', |
||||
redirectRoot: false, |
||||
}); |
||||
let redirect = ref(initData()); |
||||
const em = defineEmits(['close']); |
||||
const handleClose = () => { |
||||
redirectForm.value?.resetFields(); |
||||
open.value = false; |
||||
em('close', false); |
||||
}; |
||||
const domains = ref([]); |
||||
|
||||
const acceptParams = (redirectParam: Website.RedirectConfig) => { |
||||
if (redirectParam.operate == 'edit') { |
||||
redirect.value = redirectParam; |
||||
} else { |
||||
redirect.value = initData(); |
||||
redirect.value.websiteID = redirectParam.websiteID; |
||||
} |
||||
domains.value = []; |
||||
getDomains(); |
||||
open.value = true; |
||||
}; |
||||
|
||||
const changeType = (type: string) => { |
||||
if (type != '404') { |
||||
redirect.value.redirectRoot = false; |
||||
} else { |
||||
redirect.value.name = '404'; |
||||
} |
||||
}; |
||||
|
||||
const submit = async (formEl: FormInstance | undefined) => { |
||||
if (!formEl) return; |
||||
await formEl.validate((valid) => { |
||||
if (!valid) { |
||||
return; |
||||
} |
||||
loading.value = true; |
||||
OperateRedirectConfig(redirect.value) |
||||
.then(() => { |
||||
if (redirect.value.operate == 'create') { |
||||
MsgSuccess(i18n.global.t('commons.msg.createSuccess')); |
||||
} else { |
||||
MsgSuccess(i18n.global.t('commons.msg.updateSuccess')); |
||||
} |
||||
handleClose(); |
||||
}) |
||||
.finally(() => { |
||||
loading.value = false; |
||||
}); |
||||
}); |
||||
}; |
||||
|
||||
const getDomains = async () => { |
||||
try { |
||||
loading.value = true; |
||||
const res = await GetRedirectConfig({ websiteID: redirect.value.websiteID }); |
||||
let oldDomains = []; |
||||
if (res.data) { |
||||
for (const old of res.data) { |
||||
if (old.type == 'domain') { |
||||
oldDomains = oldDomains.concat(old.domains); |
||||
} |
||||
} |
||||
} |
||||
ListDomains(redirect.value.websiteID) |
||||
.then((domainRes) => { |
||||
if (domainRes.data) { |
||||
if (oldDomains.length > 0) { |
||||
for (const data of domainRes.data) { |
||||
if (oldDomains.indexOf(data.domain) > -1) { |
||||
continue; |
||||
} |
||||
domains.value.push(data); |
||||
} |
||||
} else { |
||||
domains.value = domainRes.data || []; |
||||
} |
||||
} |
||||
}) |
||||
.finally(() => {}); |
||||
} catch (error) { |
||||
} finally { |
||||
loading.value = false; |
||||
} |
||||
}; |
||||
|
||||
defineExpose({ |
||||
acceptParams, |
||||
}); |
||||
</script> |
@ -0,0 +1,93 @@
|
||||
<template> |
||||
<el-drawer v-model="open" :close-on-click-modal="false" size="40%" :before-close="handleClose"> |
||||
<template #header> |
||||
<DrawerHeader :header="$t('website.proxyFile')" :back="handleClose" /> |
||||
</template> |
||||
<el-row v-loading="loading"> |
||||
<el-col :span="22" :offset="1"> |
||||
<div class="redirect-editor"> |
||||
<codemirror |
||||
:autofocus="true" |
||||
placeholder="" |
||||
:indent-with-tab="true" |
||||
:tabSize="4" |
||||
:lineWrapping="true" |
||||
:matchBrackets="true" |
||||
style="height: 600px" |
||||
theme="cobalt" |
||||
:styleActiveLine="true" |
||||
:extensions="extensions" |
||||
v-model="req.content" |
||||
/> |
||||
</div> |
||||
</el-col> |
||||
</el-row> |
||||
<template #footer> |
||||
<span class="dialog-footer"> |
||||
<el-button @click="handleClose" :disabled="loading">{{ $t('commons.button.cancel') }}</el-button> |
||||
<el-button type="primary" @click="submit()" :disabled="loading"> |
||||
{{ $t('commons.button.confirm') }} |
||||
</el-button> |
||||
</span> |
||||
</template> |
||||
</el-drawer> |
||||
</template> |
||||
|
||||
<script lang="ts" setup> |
||||
import DrawerHeader from '@/components/drawer-header/index.vue'; |
||||
import i18n from '@/lang'; |
||||
import { FormInstance } from 'element-plus'; |
||||
import { reactive, ref } from 'vue'; |
||||
import { MsgSuccess } from '@/utils/message'; |
||||
import { Codemirror } from 'vue-codemirror'; |
||||
import { UpdateRedirectConfigFile } from '@/api/modules/website'; |
||||
import { StreamLanguage } from '@codemirror/language'; |
||||
import { nginx } from '@codemirror/legacy-modes/mode/nginx'; |
||||
import { oneDark } from '@codemirror/theme-one-dark'; |
||||
|
||||
const extensions = [StreamLanguage.define(nginx), oneDark]; |
||||
const proxyForm = ref<FormInstance>(); |
||||
const open = ref(false); |
||||
const loading = ref(false); |
||||
const em = defineEmits(['close']); |
||||
const handleClose = () => { |
||||
proxyForm.value?.resetFields(); |
||||
open.value = false; |
||||
em('close', false); |
||||
}; |
||||
const req = reactive({ |
||||
name: '', |
||||
websiteID: 0, |
||||
content: '', |
||||
}); |
||||
|
||||
const acceptParams = async (proxyreq: any) => { |
||||
req.name = proxyreq.name; |
||||
req.websiteID = proxyreq.websiteID; |
||||
req.content = proxyreq.content; |
||||
open.value = true; |
||||
}; |
||||
|
||||
const submit = async () => { |
||||
loading.value = true; |
||||
UpdateRedirectConfigFile(req) |
||||
.then(() => { |
||||
MsgSuccess(i18n.global.t('commons.msg.updateSuccess')); |
||||
handleClose(); |
||||
}) |
||||
.finally(() => { |
||||
loading.value = false; |
||||
}); |
||||
}; |
||||
|
||||
defineExpose({ |
||||
acceptParams, |
||||
}); |
||||
</script> |
||||
|
||||
<style scoped> |
||||
.redirect-editor { |
||||
margin-top: 10px; |
||||
width: 100%; |
||||
} |
||||
</style> |
@ -0,0 +1,194 @@
|
||||
<template> |
||||
<ComplexTable :data="data" @search="search" v-loading="loading"> |
||||
<template #toolbar> |
||||
<el-button type="primary" plain @click="openCreate"> |
||||
{{ $t('commons.button.create') + $t('website.redirect') }} |
||||
</el-button> |
||||
</template> |
||||
<el-table-column :label="$t('commons.table.name')" prop="name"></el-table-column> |
||||
<el-table-column :label="$t('website.sourceDomain')" prop="domain" min-width="150px"> |
||||
<template #default="{ row }"> |
||||
<span v-if="row.type === 'domain'">{{ row.domains.join(',') }}</span> |
||||
<span v-else>{{ row.path }}</span> |
||||
</template> |
||||
</el-table-column> |
||||
<el-table-column :label="$t('commons.table.type')" prop="type" width="80px"> |
||||
<template #default="{ row }"> |
||||
<span v-if="row.type != 404">{{ $t('website.' + row.type) }}</span> |
||||
<span v-else>{{ 404 }}</span> |
||||
</template> |
||||
</el-table-column> |
||||
<el-table-column :label="$t('website.redirectWay')" prop="redirect" width="80px"></el-table-column> |
||||
<el-table-column :label="$t('website.targetURL')" prop="target"></el-table-column> |
||||
<el-table-column :label="$t('website.keepPath')" prop="keepPath"> |
||||
<template #default="{ row }"> |
||||
<span v-if="row.type != '404'">{{ row.keepPath ? $t('website.keep') : $t('website.notKeep') }}</span> |
||||
<span v-else></span> |
||||
</template> |
||||
</el-table-column> |
||||
<el-table-column :label="$t('commons.table.status')" prop="enable"> |
||||
<template #default="{ row }"> |
||||
<el-button v-if="row.enable" link type="success" :icon="VideoPlay" @click="opProxy(row)"> |
||||
{{ $t('commons.status.running') }} |
||||
</el-button> |
||||
<el-button v-else link type="danger" :icon="VideoPause" @click="opProxy(row)"> |
||||
{{ $t('commons.status.stopped') }} |
||||
</el-button> |
||||
</template> |
||||
</el-table-column> |
||||
<fu-table-operations |
||||
:ellipsis="10" |
||||
width="260px" |
||||
:buttons="buttons" |
||||
:label="$t('commons.table.operate')" |
||||
:fixed="mobile ? false : 'right'" |
||||
fix |
||||
/> |
||||
</ComplexTable> |
||||
<Create ref="createRef" @close="search()" /> |
||||
<File ref="fileRef" @close="search()" /> |
||||
</template> |
||||
|
||||
<script lang="ts" setup name="proxy"> |
||||
import { Website } from '@/api/interface/website'; |
||||
import { OperateRedirectConfig, GetRedirectConfig } from '@/api/modules/website'; |
||||
import { computed, onMounted, ref } from 'vue'; |
||||
import Create from './create/index.vue'; |
||||
import File from './file/index.vue'; |
||||
import { VideoPlay, VideoPause } from '@element-plus/icons-vue'; |
||||
import i18n from '@/lang'; |
||||
import { MsgSuccess } from '@/utils/message'; |
||||
import { useDeleteData } from '@/hooks/use-delete-data'; |
||||
import { ElMessageBox } from 'element-plus'; |
||||
import { GlobalStore } from '@/store'; |
||||
const globalStore = GlobalStore(); |
||||
|
||||
const props = defineProps({ |
||||
id: { |
||||
type: Number, |
||||
default: 0, |
||||
}, |
||||
}); |
||||
|
||||
const mobile = computed(() => { |
||||
return globalStore.isMobile(); |
||||
}); |
||||
const id = computed(() => { |
||||
return props.id; |
||||
}); |
||||
const loading = ref(false); |
||||
const data = ref(); |
||||
const createRef = ref(); |
||||
const fileRef = ref(); |
||||
|
||||
const buttons = [ |
||||
{ |
||||
label: i18n.global.t('website.proxyFile'), |
||||
click: function (row: Website.RedirectConfig) { |
||||
openEditFile(row); |
||||
}, |
||||
disabled: (row: Website.RedirectConfig) => { |
||||
return !row.enable; |
||||
}, |
||||
}, |
||||
{ |
||||
label: i18n.global.t('commons.button.edit'), |
||||
click: function (row: Website.RedirectConfig) { |
||||
openEdit(row); |
||||
}, |
||||
disabled: (row: Website.ProxyConfig) => { |
||||
return !row.enable; |
||||
}, |
||||
}, |
||||
{ |
||||
label: i18n.global.t('commons.button.delete'), |
||||
click: function (row: Website.RedirectConfig) { |
||||
deleteProxy(row); |
||||
}, |
||||
}, |
||||
]; |
||||
|
||||
const initData = (id: number): Website.RedirectConfig => ({ |
||||
websiteID: id, |
||||
operate: 'create', |
||||
enable: true, |
||||
name: '', |
||||
domains: [], |
||||
keepPath: true, |
||||
type: '', |
||||
redirect: '', |
||||
target: '', |
||||
}); |
||||
|
||||
const openCreate = () => { |
||||
createRef.value.acceptParams(initData(id.value)); |
||||
}; |
||||
|
||||
const openEdit = (proxyConfig: Website.RedirectConfig) => { |
||||
let proxy = JSON.parse(JSON.stringify(proxyConfig)); |
||||
proxy.operate = 'edit'; |
||||
createRef.value.acceptParams(proxy); |
||||
}; |
||||
|
||||
const openEditFile = (proxyConfig: Website.RedirectConfig) => { |
||||
fileRef.value.acceptParams({ |
||||
name: proxyConfig.name, |
||||
content: proxyConfig.content, |
||||
websiteID: proxyConfig.websiteID, |
||||
}); |
||||
}; |
||||
|
||||
const deleteProxy = async (redirectConfig: Website.RedirectConfig) => { |
||||
redirectConfig.operate = 'delete'; |
||||
await useDeleteData(OperateRedirectConfig, redirectConfig, 'commons.msg.delete'); |
||||
search(); |
||||
}; |
||||
|
||||
const submit = async (redirectConfig: Website.RedirectConfig) => { |
||||
loading.value = true; |
||||
await OperateRedirectConfig(redirectConfig) |
||||
.then(() => { |
||||
MsgSuccess(i18n.global.t('commons.msg.updateSuccess')); |
||||
search(); |
||||
}) |
||||
.finally(() => { |
||||
loading.value = false; |
||||
}); |
||||
}; |
||||
|
||||
const opProxy = (redirectConfig: Website.RedirectConfig) => { |
||||
let proxy = JSON.parse(JSON.stringify(redirectConfig)); |
||||
proxy.enable = !redirectConfig.enable; |
||||
let message = ''; |
||||
if (proxy.enable) { |
||||
proxy.operate = 'enable'; |
||||
message = i18n.global.t('commons.button.start') + i18n.global.t('website.redirect'); |
||||
} else { |
||||
proxy.operate = 'disable'; |
||||
message = i18n.global.t('commons.button.stop') + i18n.global.t('website.redirect'); |
||||
} |
||||
ElMessageBox.confirm(message, i18n.global.t('cronjob.changeStatus'), { |
||||
confirmButtonText: i18n.global.t('commons.button.confirm'), |
||||
cancelButtonText: i18n.global.t('commons.button.cancel'), |
||||
}) |
||||
.then(async () => { |
||||
await submit(proxy); |
||||
}) |
||||
.catch(() => {}); |
||||
}; |
||||
|
||||
const search = async () => { |
||||
try { |
||||
loading.value = true; |
||||
const res = await GetRedirectConfig({ websiteID: id.value }); |
||||
data.value = res.data || []; |
||||
} catch (error) { |
||||
} finally { |
||||
loading.value = false; |
||||
} |
||||
}; |
||||
|
||||
onMounted(() => { |
||||
search(); |
||||
}); |
||||
</script> |
Loading…
Reference in new issue