Skip to content

Commit 50ece72

Browse files
committed
feat: Support selecting multiple rule sets
1 parent aff55d3 commit 50ece72

File tree

4 files changed

+75
-19
lines changed

4 files changed

+75
-19
lines changed

frontend/src/assets/main.less

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,10 @@ a {
138138
color: #fff;
139139
}
140140

141+
.text-nowrap {
142+
text-wrap: nowrap;
143+
}
144+
141145
@keyframes rotate {
142146
from {
143147
transform: rotate(0deg);

frontend/src/utils/generator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const _generateRule = (rule: IRule, rule_set: IRuleSet[], inbounds: IInbound[])
1717
if (rule.type === RuleType.Inline) {
1818
deepAssign(extra, JSON.parse(rule.payload))
1919
} else if (rule.type === RuleType.RuleSet) {
20-
extra[rule.type] = getRuleset(rule.payload)
20+
extra[rule.type] = rule.payload.split(',').map((id) => getRuleset(id))
2121
} else if (rule.type === RuleType.Inbound) {
2222
extra[rule.type] = getInbound(rule.payload)
2323
} else if (rule.type === RuleType.IpIsPrivate) {

frontend/src/views/ProfilesView/components/DnsRulesConfig.vue

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,19 @@ const handleDeleteRule = (index: number) => {
6565
}
6666
6767
const handleUse = (ruleset: any) => {
68-
fields.value.payload = ruleset.id
68+
const ids = fields.value.payload.split(',').filter((v) => v)
69+
const idx = ids.findIndex((v) => v === ruleset.id)
70+
if (idx === -1) {
71+
ids.push(ruleset.id)
72+
} else {
73+
ids.splice(idx, 1)
74+
}
75+
fields.value.payload = ids.join(',')
76+
}
77+
78+
const handleClearRuleset = (ruleset: any) => {
79+
const ids = fields.value.payload.split(',').filter((id) => props.ruleSet.find((v) => v.id === id))
80+
ruleset.payload = ids.join(',')
6981
}
7082
7183
const showLost = () => message.warn('kernel.route.rules.invalid')
@@ -93,7 +105,10 @@ const hasLost = (rule: IDNSRule) => {
93105
return rule.payload !== 'any' && !props.outboundOptions.find((v) => v.value === rule.payload)
94106
}
95107
if (rule.type === RuleType.RuleSet) {
96-
return !props.ruleSet.find((v) => v.id === rule.payload)
108+
const hasMissingRuleset = rule.payload
109+
.split(',')
110+
.some((id) => !props.ruleSet.find((v) => v.id === id))
111+
return hasMissingRuleset
97112
}
98113
if (rule.type === RuleType.Inline) {
99114
return !isValidJson(rule.payload)
@@ -140,7 +155,14 @@ const renderRule = (rule: IDNSRule) => {
140155
<span v-if="hasLost(rule)" @click="showLost" class="warn"> [ ! ] </span>
141156
{{ renderRule(rule) }}
142157
</div>
143-
<div class="ml-auto">
158+
<div class="flex text-nowrap ml-auto">
159+
<Button
160+
v-if="rule.type === RuleType.RuleSet && rule.payload && hasLost(rule)"
161+
@click="handleClearRuleset(rule)"
162+
type="text"
163+
>
164+
{{ t('common.clear') }}
165+
</Button>
144166
<Button @click="handleEdit(index)" icon="edit" type="text" size="small" />
145167
<Button @click="handleDeleteRule(index)" icon="delete" type="text" size="small" />
146168
</div>
@@ -234,7 +256,7 @@ const renderRule = (rule: IDNSRule) => {
234256
:key="ruleset.tag"
235257
:title="ruleset.tag"
236258
@click="handleUse(ruleset)"
237-
:selected="fields.payload === ruleset.id"
259+
:selected="fields.payload.includes(ruleset.id)"
238260
v-tips="ruleset.type"
239261
class="ruleset"
240262
>

frontend/src/views/ProfilesView/components/RouteRulesConfig.vue

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,19 @@ const handleEdit = (index: number) => {
5555
}
5656
5757
const handleUse = (ruleset: any) => {
58-
fields.value.payload = ruleset.id
58+
const ids = fields.value.payload.split(',').filter((v) => v)
59+
const idx = ids.findIndex((v) => v === ruleset.id)
60+
if (idx === -1) {
61+
ids.push(ruleset.id)
62+
} else {
63+
ids.splice(idx, 1)
64+
}
65+
fields.value.payload = ids.join(',')
66+
}
67+
68+
const handleClearRuleset = (ruleset: any) => {
69+
const ids = fields.value.payload.split(',').filter((id) => props.ruleSet.find((v) => v.id === id))
70+
ruleset.payload = ids.join(',')
5971
}
6072
6173
const handleDelete = (index: number) => {
@@ -69,20 +81,31 @@ const isSupportPayload = computed(() => {
6981
})
7082
7183
const hasLost = (rule: IRule) => {
72-
if (rule.type === RuleType.Protocol) {
73-
return false
84+
const rulesValidationFlags: boolean[] = []
85+
const hasMissingInbound = !props.inboundOptions.find((v) => v.value === rule.payload)
86+
const hasMissingOutbound = !props.outboundOptions.find((v) => v.value === rule.outbound)
87+
const hasMissingRuleset = rule.payload
88+
.split(',')
89+
.some((id) => !props.ruleSet.find((v) => v.id === id))
90+
if (rule.action === RuleAction.Route) {
91+
rulesValidationFlags.push(hasMissingOutbound)
92+
} else if (rule.action === RuleAction.RouteOptions) {
93+
let isValid = true
94+
try {
95+
JSON.parse(rule.outbound)
96+
} catch {
97+
isValid = false
98+
}
99+
rulesValidationFlags.push(!isValid)
74100
}
75101
if (rule.type === RuleType.Inbound) {
76-
return !props.inboundOptions.find((v) => v.value === rule.payload)
77-
}
78-
if (rule.action !== RuleAction.Route) {
79-
return false
80-
}
81-
const outboundLost = !props.outboundOptions.find((v) => v.value === rule.outbound)
82-
if (rule.type === RuleType.RuleSet) {
83-
return !props.ruleSet.find((v) => v.id === rule.payload) || outboundLost
102+
rulesValidationFlags.push(hasMissingInbound)
103+
} else if (rule.type === RuleType.IpIsPrivate) {
104+
rulesValidationFlags.push(!['true', 'false'].includes(rule.payload))
105+
} else if (rule.type === RuleType.RuleSet) {
106+
rulesValidationFlags.push(hasMissingRuleset)
84107
}
85-
return outboundLost
108+
return rulesValidationFlags.some((v) => v) || !rule.payload
86109
}
87110
88111
const renderRule = (rule: IRule) => {
@@ -121,7 +144,14 @@ const renderRule = (rule: IRule) => {
121144
<span v-if="hasLost(rule)" @click="showLost" class="warn"> [ ! ] </span>
122145
{{ renderRule(rule) }}
123146
</div>
124-
<div class="ml-auto">
147+
<div class="flex text-nowrap ml-auto">
148+
<Button
149+
v-if="rule.type === RuleType.RuleSet && rule.payload && hasLost(rule)"
150+
@click="handleClearRuleset(rule)"
151+
type="text"
152+
>
153+
{{ t('common.clear') }}
154+
</Button>
125155
<Button @click="handleEdit(index)" icon="edit" type="text" size="small" />
126156
<Button @click="handleDelete(index)" icon="delete" type="text" size="small" />
127157
</div>
@@ -227,7 +257,7 @@ const renderRule = (rule: IRule) => {
227257
:key="ruleset.tag"
228258
:title="ruleset.tag"
229259
@click="handleUse(ruleset)"
230-
:selected="fields.payload === ruleset.id"
260+
:selected="fields.payload.includes(ruleset.id)"
231261
v-tips="ruleset.type"
232262
class="ruleset"
233263
>

0 commit comments

Comments
 (0)