Улучшение фронта
This commit is contained in:
@@ -44,7 +44,7 @@ code {
|
||||
font-family: var(--font-mono);
|
||||
background: rgba(var(--accent-rgb), 0.1);
|
||||
padding: 3px 8px;
|
||||
border-radius: var(--radius-sm);
|
||||
border-radius: var(--radius);
|
||||
font-size: var(--text-sm);
|
||||
color: var(--accent-purple-light);
|
||||
border: 1px solid var(--glass-border);
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
.form-section {
|
||||
padding: var(--space-xl);
|
||||
background: rgba(var(--accent-rgb), 0.02);
|
||||
border-radius: var(--radius-xl);
|
||||
border-radius: var(--radius);
|
||||
border: 1px solid var(--glass-border);
|
||||
transition: all var(--transition-base);
|
||||
}
|
||||
@@ -83,7 +83,7 @@
|
||||
padding: 10px var(--space-md);
|
||||
background: rgba(var(--muted-rgb), 0.1);
|
||||
border: 1px solid rgba(var(--muted-rgb), 0.3);
|
||||
border-radius: var(--radius-md);
|
||||
border-radius: var(--radius);
|
||||
color: var(--text-muted);
|
||||
font-size: var(--text-base);
|
||||
font-weight: var(--font-semibold);
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
background: var(--glass-bg-light);
|
||||
backdrop-filter: var(--backdrop-blur);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--radius-xl);
|
||||
border-radius: var(--radius);
|
||||
overflow: hidden;
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
@@ -121,7 +121,7 @@
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
margin-right: 8px;
|
||||
border-radius: var(--radius-sm);
|
||||
border-radius: var(--radius);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@
|
||||
justify-content: center;
|
||||
background: var(--btn-icon-bg);
|
||||
border: 1px solid var(--btn-icon-border);
|
||||
border-radius: var(--radius-md);
|
||||
border-radius: var(--radius);
|
||||
color: var(--btn-icon-color);
|
||||
font-size: var(--text-md);
|
||||
cursor: pointer;
|
||||
|
||||
@@ -58,9 +58,9 @@
|
||||
--table-border: rgba(255, 255, 255, 0.05);
|
||||
|
||||
/* Компоненты */
|
||||
--card-hover-shadow: 0 8px 32px rgba(139, 92, 246, 0.3);
|
||||
--card-border-hover: rgba(139, 92, 246, 0.3);
|
||||
--service-card-gradient: linear-gradient(90deg, #8b5cf6, #a78bfa, #06b6d4);
|
||||
--card-hover-shadow: 0 4px 16px rgba(139, 92, 246, 0.12);
|
||||
--card-border-hover: rgba(139, 92, 246, 0.2);
|
||||
--service-card-gradient: linear-gradient(90deg, rgba(139, 92, 246, 0.6), rgba(167, 139, 250, 0.6), rgba(6, 182, 212, 0.6));
|
||||
--btn-icon-bg: rgba(139, 92, 246, 0.1);
|
||||
--btn-icon-border: rgba(139, 92, 246, 0.3);
|
||||
--btn-icon-color: #a78bfa;
|
||||
|
||||
@@ -13,10 +13,7 @@
|
||||
--space-3xl: 60px;
|
||||
|
||||
/* Border Radius */
|
||||
--radius-sm: 6px;
|
||||
--radius-md: 8px;
|
||||
--radius-lg: 12px;
|
||||
--radius-xl: 16px;
|
||||
--radius: 8px;
|
||||
--radius-full: 50%;
|
||||
|
||||
/* Transitions */
|
||||
|
||||
@@ -39,7 +39,7 @@ const goBack = () => {
|
||||
margin-bottom: var(--space-md);
|
||||
padding: var(--space-md) 20px;
|
||||
background: rgba(var(--accent-rgb), 0.05);
|
||||
border-radius: var(--radius-lg);
|
||||
border-radius: var(--radius);
|
||||
border: 1px solid var(--glass-border);
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ const goBack = () => {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: var(--space-sm) var(--space-lg);
|
||||
border-radius: var(--radius-md);
|
||||
border-radius: var(--radius);
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-base);
|
||||
display: flex;
|
||||
|
||||
@@ -29,7 +29,7 @@ defineProps({
|
||||
margin-bottom: var(--space-md);
|
||||
padding: var(--space-lg);
|
||||
background: rgba(var(--accent-rgb), 0.03);
|
||||
border-radius: var(--radius-xl);
|
||||
border-radius: var(--radius);
|
||||
border: 1px solid var(--glass-border);
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ const navigate = (item) => {
|
||||
justify-content: center;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: var(--radius-lg);
|
||||
border-radius: var(--radius);
|
||||
color: var(--nav-color);
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
|
||||
@@ -131,7 +131,7 @@ const windowClose = () => { if (isWails) window.runtime.Quit() }
|
||||
align-items: center;
|
||||
gap: var(--space-sm);
|
||||
padding: var(--space-xs) var(--space-md);
|
||||
border-radius: var(--radius-lg);
|
||||
border-radius: var(--radius);
|
||||
background: var(--glass-bg);
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ const windowClose = () => { if (isWails) window.runtime.Quit() }
|
||||
gap: var(--space-sm);
|
||||
padding: var(--space-xs) var(--space-md);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--radius-md);
|
||||
border-radius: var(--radius);
|
||||
background: var(--glass-bg);
|
||||
color: var(--text-primary);
|
||||
cursor: pointer;
|
||||
@@ -194,7 +194,7 @@ const windowClose = () => { if (isWails) window.runtime.Quit() }
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: none;
|
||||
border-radius: var(--radius-md);
|
||||
border-radius: var(--radius);
|
||||
background: transparent;
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
@@ -208,7 +208,7 @@ const windowClose = () => { if (isWails) window.runtime.Quit() }
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: none;
|
||||
border-radius: var(--radius-md);
|
||||
border-radius: var(--radius);
|
||||
background: transparent;
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
|
||||
@@ -82,7 +82,7 @@ const findCertForDomain = (domain) => {
|
||||
</VBadge>
|
||||
</td>
|
||||
<td>
|
||||
<button class="icon-btn" :title="t('sites.editVaccess')" @click="router.push(`/vaccess/${proxy.ExternalDomain}`)">
|
||||
<button class="icon-btn" :title="t('sites.editVaccess')" @click="router.push(`/vaccess/${proxy.ExternalDomain}?proxy=true`)">
|
||||
<i class="fas fa-user-lock"></i>
|
||||
</button>
|
||||
<button class="icon-btn" :title="t('certs.title')" @click="router.push(`/certs/${proxy.ExternalDomain}`)">
|
||||
|
||||
@@ -51,7 +51,7 @@ const serviceInfoLabel = {
|
||||
background: var(--glass-bg-light);
|
||||
backdrop-filter: var(--backdrop-blur);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--radius-xl);
|
||||
border-radius: var(--radius);
|
||||
padding: var(--space-lg);
|
||||
transition: all var(--transition-bounce);
|
||||
position: relative;
|
||||
|
||||
@@ -29,7 +29,7 @@ defineEmits(['click'])
|
||||
gap: var(--space-sm);
|
||||
padding: var(--space-sm) var(--space-md);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--radius-md);
|
||||
border-radius: var(--radius);
|
||||
background: var(--glass-bg);
|
||||
color: var(--text-primary);
|
||||
cursor: pointer;
|
||||
|
||||
@@ -26,7 +26,7 @@ defineProps({
|
||||
.v-card {
|
||||
background: var(--glass-bg);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--radius-lg);
|
||||
border-radius: var(--radius);
|
||||
backdrop-filter: var(--backdrop-blur);
|
||||
transition: all var(--transition-base);
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ const onFileSelect = (event) => {
|
||||
padding: var(--space-sm) var(--space-md);
|
||||
background: var(--glass-bg);
|
||||
border: 1px dashed var(--glass-border);
|
||||
border-radius: var(--radius-md);
|
||||
border-radius: var(--radius);
|
||||
color: var(--text-secondary);
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ defineEmits(['update:modelValue'])
|
||||
padding: 10px 14px;
|
||||
background: var(--glass-bg-dark);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--radius-md);
|
||||
border-radius: var(--radius);
|
||||
color: var(--text-primary);
|
||||
font-size: var(--text-base);
|
||||
transition: all var(--transition-fast);
|
||||
|
||||
@@ -49,7 +49,7 @@ const modal = useModal()
|
||||
.v-modal-window {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--radius-xl);
|
||||
border-radius: var(--radius);
|
||||
width: 90%;
|
||||
max-width: 700px;
|
||||
max-height: 85vh;
|
||||
@@ -79,7 +79,7 @@ const modal = useModal()
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
border-radius: var(--radius-md);
|
||||
border-radius: var(--radius);
|
||||
background: transparent;
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
|
||||
@@ -42,7 +42,7 @@ const { notifications, remove } = useNotification()
|
||||
align-items: center;
|
||||
gap: var(--space-sm);
|
||||
padding: var(--space-sm) var(--space-md);
|
||||
border-radius: var(--radius-md);
|
||||
border-radius: var(--radius);
|
||||
font-size: var(--text-md);
|
||||
cursor: pointer;
|
||||
backdrop-filter: var(--backdrop-blur);
|
||||
|
||||
@@ -88,7 +88,7 @@ onUnmounted(() => document.removeEventListener('click', onDocClick))
|
||||
padding: 10px 14px;
|
||||
background: var(--glass-bg-dark);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--radius-md);
|
||||
border-radius: var(--radius);
|
||||
color: var(--text-primary);
|
||||
font-size: var(--text-base);
|
||||
cursor: pointer;
|
||||
@@ -129,7 +129,7 @@ onUnmounted(() => document.removeEventListener('click', onDocClick))
|
||||
right: 0;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid rgba(var(--accent-rgb), 0.3);
|
||||
border-radius: var(--radius-md);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--shadow-lg);
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
|
||||
@@ -30,7 +30,7 @@ defineProps({
|
||||
<style scoped>
|
||||
.v-table-container {
|
||||
overflow-x: auto;
|
||||
border-radius: var(--radius-lg);
|
||||
border-radius: var(--radius);
|
||||
border: 1px solid var(--glass-border);
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ const show = ref(false)
|
||||
padding: 10px 14px;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid rgba(var(--accent-rgb), 0.3);
|
||||
border-radius: var(--radius-md);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--shadow-lg);
|
||||
z-index: 1000;
|
||||
font-size: var(--text-sm);
|
||||
|
||||
@@ -1 +1,161 @@
|
||||
<script setup>`nconst { t } = useI18n()`n</script>`n<template><div>Field Editor</div></template>
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
modelValue: { type: Array, default: () => [] },
|
||||
placeholder: { type: String, default: '' },
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const input = ref('')
|
||||
const editing = ref(false)
|
||||
|
||||
const addItem = () => {
|
||||
const raw = input.value.trim()
|
||||
if (!raw) return
|
||||
const items = raw.split(',').map(s => s.trim()).filter(s => s && !props.modelValue.includes(s))
|
||||
if (items.length) {
|
||||
emit('update:modelValue', [...props.modelValue, ...items])
|
||||
}
|
||||
input.value = ''
|
||||
}
|
||||
|
||||
const removeItem = (index) => {
|
||||
const updated = [...props.modelValue]
|
||||
updated.splice(index, 1)
|
||||
emit('update:modelValue', updated)
|
||||
}
|
||||
|
||||
const onKeydown = (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault()
|
||||
addItem()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="field-editor" @click="editing = true">
|
||||
<div v-if="!editing && modelValue.length === 0" class="empty-field" @click="editing = true">—</div>
|
||||
<div v-else-if="!editing" class="mini-tags" @click="editing = true">
|
||||
<code v-for="(item, i) in modelValue" :key="i">{{ item }}</code>
|
||||
<span class="edit-hint"><i class="fas fa-pen"></i></span>
|
||||
</div>
|
||||
<div v-else class="field-editor-active" @click.stop>
|
||||
<div class="field-input-row">
|
||||
<input
|
||||
v-model="input"
|
||||
class="field-input"
|
||||
:placeholder="placeholder"
|
||||
@keydown="onKeydown"
|
||||
@blur="input || (editing = false)"
|
||||
>
|
||||
<button v-if="input" class="field-add-btn" @click="addItem"><i class="fas fa-plus"></i></button>
|
||||
</div>
|
||||
<div v-if="modelValue.length" class="field-tags">
|
||||
<span v-for="(item, i) in modelValue" :key="i" class="field-tag">
|
||||
{{ item }}
|
||||
<button class="field-tag-remove" @click="removeItem(i)"><i class="fas fa-times"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.field-editor {
|
||||
cursor: pointer;
|
||||
min-height: 28px;
|
||||
}
|
||||
|
||||
.edit-hint {
|
||||
opacity: 0;
|
||||
font-size: 10px;
|
||||
color: var(--text-muted);
|
||||
transition: opacity var(--transition-fast);
|
||||
}
|
||||
|
||||
.field-editor:hover .edit-hint {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.field-editor-active {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.field-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.field-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 2px 8px;
|
||||
background: rgba(var(--accent-rgb), 0.2);
|
||||
border: 1px solid rgba(var(--accent-rgb), 0.4);
|
||||
border-radius: 12px;
|
||||
font-size: 11px;
|
||||
color: var(--text-primary);
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
.field-tag-remove {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--accent-red);
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
font-size: 9px;
|
||||
opacity: 0.6;
|
||||
transition: opacity var(--transition-fast);
|
||||
}
|
||||
|
||||
.field-tag-remove:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.field-input-row {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.field-input {
|
||||
flex: 1;
|
||||
padding: 4px 8px;
|
||||
background: var(--glass-bg-dark);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--radius);
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-family: var(--font-mono);
|
||||
outline: none;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.field-input:focus {
|
||||
border-color: rgba(var(--accent-rgb), 0.5);
|
||||
}
|
||||
|
||||
.field-input::placeholder {
|
||||
color: var(--text-muted);
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.field-add-btn {
|
||||
padding: 4px 8px;
|
||||
background: rgba(var(--accent-rgb), 0.15);
|
||||
border: 1px solid rgba(var(--accent-rgb), 0.3);
|
||||
border-radius: var(--radius);
|
||||
color: var(--accent-purple-light);
|
||||
cursor: pointer;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.field-add-btn:hover {
|
||||
background: rgba(var(--accent-rgb), 0.25);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -142,7 +142,7 @@ const deleteCert = async (domain) => {
|
||||
.cert-card {
|
||||
background: rgba(var(--accent-rgb), 0.03);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--radius-xl);
|
||||
border-radius: var(--radius);
|
||||
padding: var(--space-lg);
|
||||
transition: all var(--transition-base);
|
||||
}
|
||||
@@ -196,7 +196,7 @@ const deleteCert = async (domain) => {
|
||||
.cert-info-item {
|
||||
padding: var(--space-md);
|
||||
background: var(--subtle-overlay);
|
||||
border-radius: var(--radius-md);
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
|
||||
.cert-info-label {
|
||||
@@ -229,7 +229,7 @@ const deleteCert = async (domain) => {
|
||||
.cert-domain-tag {
|
||||
padding: 4px 12px;
|
||||
background: rgba(var(--accent-rgb), 0.15);
|
||||
border-radius: var(--radius-md);
|
||||
border-radius: var(--radius);
|
||||
font-size: var(--text-sm);
|
||||
color: var(--accent-purple-light);
|
||||
font-family: var(--font-mono);
|
||||
|
||||
@@ -130,7 +130,7 @@ const toggleAcme = async () => {
|
||||
background: var(--glass-bg-light);
|
||||
backdrop-filter: var(--backdrop-blur);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--radius-xl);
|
||||
border-radius: var(--radius);
|
||||
padding: var(--space-lg);
|
||||
}
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ const confirmDelete = () => {
|
||||
padding: 10px 14px;
|
||||
background: var(--glass-bg-dark);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--radius-md);
|
||||
border-radius: var(--radius);
|
||||
color: var(--text-primary);
|
||||
font-size: var(--text-base);
|
||||
outline: none;
|
||||
@@ -197,7 +197,7 @@ const confirmDelete = () => {
|
||||
padding: var(--space-sm) var(--space-md);
|
||||
background: rgba(var(--accent-rgb), 0.15);
|
||||
border: 1px solid rgba(var(--accent-rgb), 0.3);
|
||||
border-radius: var(--radius-md);
|
||||
border-radius: var(--radius);
|
||||
color: var(--accent-purple-light);
|
||||
font-size: var(--text-base);
|
||||
font-weight: var(--font-semibold);
|
||||
@@ -222,7 +222,7 @@ const confirmDelete = () => {
|
||||
padding: 12px;
|
||||
background: var(--glass-bg-dark);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--radius-md);
|
||||
border-radius: var(--radius);
|
||||
min-height: 48px;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ const props = defineProps({
|
||||
host: { type: String, required: true },
|
||||
})
|
||||
|
||||
const isProxy = computed(() => route.query.proxy === 'true')
|
||||
|
||||
const activeTab = ref('rules')
|
||||
const rules = ref([])
|
||||
const loading = ref(true)
|
||||
@@ -17,13 +19,13 @@ const loading = ref(true)
|
||||
const { dragIndex, dragOverIndex, onDragStart, onDragOver, onDragEnter, onDragLeave, onDrop, onDragEnd } = useDraggable(rules)
|
||||
|
||||
onMounted(async () => {
|
||||
const data = await api.getVAccessRules(props.host, false)
|
||||
const data = await api.getVAccessRules(props.host, isProxy.value)
|
||||
rules.value = data?.rules || []
|
||||
loading.value = false
|
||||
})
|
||||
|
||||
const saveRules = async () => {
|
||||
await api.saveVAccessRules(props.host, false, JSON.stringify({ rules: rules.value }))
|
||||
await api.saveVAccessRules(props.host, isProxy.value, JSON.stringify({ rules: rules.value }))
|
||||
success(t('notify.changesSaved'))
|
||||
}
|
||||
|
||||
@@ -84,12 +86,12 @@ const formatList = (arr) => {
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-drag"></th>
|
||||
<th class="col-type">{{ t('vaccess.type') }}</th>
|
||||
<th class="col-files">{{ t('vaccess.files') }}</th>
|
||||
<th class="col-paths">{{ t('vaccess.paths') }}</th>
|
||||
<th class="col-ips">{{ t('vaccess.ips') }}</th>
|
||||
<th class="col-exceptions">{{ t('vaccess.exceptions') }}</th>
|
||||
<th class="col-error">{{ t('vaccess.error') }}</th>
|
||||
<th class="col-type"><span class="th-with-info">{{ t('vaccess.type') }} <VTooltip text="Allow — разрешить доступ, Disable — запретить. Клик для переключения" /></span></th>
|
||||
<th class="col-files"><span class="th-with-info">{{ t('vaccess.files') }} <VTooltip text="Расширения файлов через запятую" :items="['*.php', '*.exe', '*.sh', 'no_extension']" /></span></th>
|
||||
<th class="col-paths"><span class="th-with-info">{{ t('vaccess.paths') }} <VTooltip text="Пути доступа через запятую" :items="['/admin/*', '/api/*', '/uploads/*']" /></span></th>
|
||||
<th class="col-ips"><span class="th-with-info">{{ t('vaccess.ips') }} <VTooltip text="IP адреса через запятую" :items="['192.168.1.1', '10.0.0.0/24', '127.0.0.1']" /></span></th>
|
||||
<th class="col-exceptions"><span class="th-with-info">{{ t('vaccess.exceptions') }} <VTooltip text="Пути-исключения: правило НЕ применяется к ним" :items="['/public/*', '/bot/*', '/api/open/*']" /></span></th>
|
||||
<th class="col-error"><span class="th-with-info">{{ t('vaccess.error') }} <VTooltip text="Куда перенаправить при блокировке" :items="['404', 'https://site.com', '/error.html']" /></span></th>
|
||||
<th class="col-actions"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -108,33 +110,25 @@ const formatList = (arr) => {
|
||||
>
|
||||
<td class="drag-handle"><i class="fas fa-grip-vertical"></i></td>
|
||||
<td>
|
||||
<VBadge :variant="rule.type === 'Allow' ? 'yes' : 'no'">{{ rule.type }}</VBadge>
|
||||
<VBadge class="type-toggle" :variant="rule.type === 'Allow' ? 'yes' : 'no'" @click="rule.type = rule.type === 'Allow' ? 'Disable' : 'Allow'">
|
||||
{{ rule.type }}
|
||||
</VBadge>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="rule.type_file?.length" class="mini-tags">
|
||||
<code v-for="f in rule.type_file" :key="f">{{ f }}</code>
|
||||
</span>
|
||||
<span v-else class="empty-field">—</span>
|
||||
<FieldEditor v-model="rule.type_file" placeholder="*.php" />
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="rule.path_access?.length" class="mini-tags">
|
||||
<code v-for="p in rule.path_access" :key="p">{{ p }}</code>
|
||||
</span>
|
||||
<span v-else class="empty-field">—</span>
|
||||
<FieldEditor v-model="rule.path_access" placeholder="/admin/*" />
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="rule.ip_list?.length" class="mini-tags">
|
||||
<code v-for="ip in rule.ip_list" :key="ip">{{ ip }}</code>
|
||||
</span>
|
||||
<span v-else class="empty-field">—</span>
|
||||
<FieldEditor v-model="rule.ip_list" placeholder="192.168.1.1" />
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="rule.exceptions_dir?.length" class="mini-tags">
|
||||
<code v-for="e in rule.exceptions_dir" :key="e">{{ e }}</code>
|
||||
</span>
|
||||
<span v-else class="empty-field">—</span>
|
||||
<FieldEditor v-model="rule.exceptions_dir" placeholder="/public/*" />
|
||||
</td>
|
||||
<td>
|
||||
<input v-model="rule.url_error" class="inline-input" placeholder="404" />
|
||||
</td>
|
||||
<td><code>{{ rule.url_error || '—' }}</code></td>
|
||||
<td class="col-actions-cell">
|
||||
<button class="icon-btn-small" @click="removeRule(index)"><i class="fas fa-trash"></i></button>
|
||||
</td>
|
||||
@@ -235,7 +229,7 @@ const formatList = (arr) => {
|
||||
padding: 10px 18px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: var(--radius-md);
|
||||
border-radius: var(--radius);
|
||||
color: var(--text-muted);
|
||||
font-size: var(--text-base);
|
||||
font-weight: var(--font-medium);
|
||||
@@ -302,6 +296,12 @@ const formatList = (arr) => {
|
||||
border-bottom: 1px solid rgba(var(--accent-rgb), 0.05);
|
||||
}
|
||||
|
||||
.th-with-info {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.col-drag { width: 3%; min-width: 40px; text-align: center; }
|
||||
.col-type { width: 8%; min-width: 80px; }
|
||||
.col-files { width: 15%; min-width: 120px; }
|
||||
@@ -345,7 +345,7 @@ const formatList = (arr) => {
|
||||
.mini-tags code {
|
||||
padding: 2px 6px;
|
||||
background: rgba(var(--accent-rgb), 0.15);
|
||||
border-radius: var(--radius-sm);
|
||||
border-radius: var(--radius);
|
||||
font-size: var(--text-sm);
|
||||
color: var(--accent-purple-light);
|
||||
}
|
||||
@@ -356,6 +356,36 @@ const formatList = (arr) => {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.type-toggle {
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
.type-toggle:hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.inline-input {
|
||||
padding: 4px 8px;
|
||||
background: var(--glass-bg-dark);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--radius);
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-family: var(--font-mono);
|
||||
outline: none;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.inline-input:focus {
|
||||
border-color: rgba(var(--accent-rgb), 0.5);
|
||||
}
|
||||
|
||||
.inline-input::placeholder {
|
||||
color: var(--text-muted);
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.col-actions-cell {
|
||||
text-align: center;
|
||||
}
|
||||
@@ -368,7 +398,7 @@ const formatList = (arr) => {
|
||||
justify-content: center;
|
||||
background: rgba(var(--danger-rgb), 0.1);
|
||||
border: 1px solid rgba(var(--danger-rgb), 0.3);
|
||||
border-radius: var(--radius-sm);
|
||||
border-radius: var(--radius);
|
||||
color: var(--accent-red);
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
@@ -413,7 +443,7 @@ const formatList = (arr) => {
|
||||
|
||||
.help-card {
|
||||
background: var(--subtle-overlay);
|
||||
border-radius: var(--radius-xl);
|
||||
border-radius: var(--radius);
|
||||
padding: var(--space-xl);
|
||||
border: 1px solid var(--glass-border);
|
||||
transition: all var(--transition-slow);
|
||||
@@ -459,7 +489,7 @@ const formatList = (arr) => {
|
||||
.help-param {
|
||||
padding: 20px;
|
||||
background: rgba(var(--accent-rgb), 0.03);
|
||||
border-radius: var(--radius-lg);
|
||||
border-radius: var(--radius);
|
||||
border-left: 3px solid var(--accent-purple);
|
||||
}
|
||||
|
||||
@@ -479,7 +509,7 @@ const formatList = (arr) => {
|
||||
.help-param code {
|
||||
padding: 3px 8px;
|
||||
background: rgba(var(--accent-rgb), 0.15);
|
||||
border-radius: var(--radius-sm);
|
||||
border-radius: var(--radius);
|
||||
font-size: var(--text-base);
|
||||
color: var(--accent-purple-light);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user