mirror of
				https://github.com/MHSanaei/3x-ui.git
				synced 2025-10-30 11:52:51 +00:00 
			
		
		
		
	 bea19a263d
			
		
	
	
		bea19a263d
		
			
		
	
	
	
	
		
			
			* refactor: use vue inline styles in entire application * refactor: setting row in dashboard page * refactor: use blob for download file in text modal * refactor: move all html templates in `web/html` folder * refactor: `DeviceUtils` -> `MediaQueryMixin` The transition to mixins has been made, as they can update themselves. * chore: pretty right buttons in `outbounds` tab in xray settings * refactor: add translations for system status * refactor: adjust gutter spacing in setting list item * refactor: use native `a-input-password` for password field * chore: return old system status with new translations * chore: add missing translation
		
			
				
	
	
		
			246 lines
		
	
	
	
		
			9.6 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			246 lines
		
	
	
	
		
			9.6 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
| {{define "modals/ruleModal"}}
 | |
| <a-modal id="rule-modal" v-model="ruleModal.visible" :title="ruleModal.title" @ok="ruleModal.ok" :confirm-loading="ruleModal.confirmLoading" :closable="true" :mask-closable="false" :ok-text="ruleModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
 | |
|   <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
 | |
|     <a-form-item label='Domain Matcher'>
 | |
|       <a-select v-model="ruleModal.rule.domainMatcher" :dropdown-class-name="themeSwitcher.currentTheme">
 | |
|         <a-select-option v-for="dm in ['','hybrid','linear']" :value="dm">[[ dm ]]</a-select-option>
 | |
|       </a-select>
 | |
|     </a-form-item>
 | |
|     <a-form-item>
 | |
|       <template slot="label">
 | |
|         <a-tooltip>
 | |
|           <template slot="title">
 | |
|             <span>{{ i18n "pages.xray.rules.useComma" }}</span>
 | |
|           </template> Source IPs <a-icon type="question-circle"></a-icon>
 | |
|         </a-tooltip>
 | |
|       </template>
 | |
|       <a-input v-model.trim="ruleModal.rule.source"></a-input>
 | |
|     </a-form-item>
 | |
|     <a-form-item>
 | |
|       <template slot="label">
 | |
|         <a-tooltip>
 | |
|           <template slot="title">
 | |
|             <span>{{ i18n "pages.xray.rules.useComma" }}</span>
 | |
|           </template> Source Port <a-icon type="question-circle"></a-icon>
 | |
|         </a-tooltip>
 | |
|       </template>
 | |
|       <a-input v-model.trim="ruleModal.rule.sourcePort"></a-input>
 | |
|     </a-form-item>
 | |
|     <a-form-item label='Network'>
 | |
|       <a-select v-model="ruleModal.rule.network" :dropdown-class-name="themeSwitcher.currentTheme">
 | |
|         <a-select-option v-for="x in ['','TCP','UDP','TCP,UDP']" :value="x">[[ x ]]</a-select-option>
 | |
|       </a-select>
 | |
|     </a-form-item>
 | |
|     <a-form-item label='Protocol'>
 | |
|       <a-select v-model="ruleModal.rule.protocol" mode="multiple" :dropdown-class-name="themeSwitcher.currentTheme">
 | |
|         <a-select-option v-for="x in ['http','tls','bittorrent','quic']" :value="x">[[ x ]]</a-select-option>
 | |
|       </a-select>
 | |
|     </a-form-item>
 | |
|     <a-form-item label='Attributes'>
 | |
|       <a-button icon="plus" size="small" :style="{ marginLeft: '10px' }" @click="ruleModal.rule.attrs.push(['', ''])"></a-button>
 | |
|     </a-form-item>
 | |
|     <a-form-item :wrapper-col="{span: 24}">
 | |
|       <a-input-group compact v-for="(attr,index) in ruleModal.rule.attrs">
 | |
|         <a-input :style="{ width: '50%' }" v-model="attr[0]" placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
 | |
|           <template slot="addonBefore" :style="{ margin: '0' }">[[ index+1 ]]</template>
 | |
|         </a-input>
 | |
|         <a-input :style="{ width: '50%' }" v-model="attr[1]" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
 | |
|           <a-button icon="minus" slot="addonAfter" size="small" @click="ruleModal.rule.attrs.splice(index,1)"></a-button>
 | |
|         </a-input>
 | |
|       </a-input-group>
 | |
|     </a-form-item>
 | |
|     <a-form-item>
 | |
|       <template slot="label">
 | |
|         <a-tooltip>
 | |
|           <template slot="title">
 | |
|             <span>{{ i18n "pages.xray.rules.useComma" }}</span>
 | |
|           </template> IP <a-icon type="question-circle"></a-icon>
 | |
|         </a-tooltip>
 | |
|       </template>
 | |
|       <a-input v-model.trim="ruleModal.rule.ip"></a-input>
 | |
|     </a-form-item>
 | |
|     <a-form-item>
 | |
|       <template slot="label">
 | |
|         <a-tooltip>
 | |
|           <template slot="title">
 | |
|             <span>{{ i18n "pages.xray.rules.useComma" }}</span>
 | |
|           </template> Domain <a-icon type="question-circle"></a-icon>
 | |
|         </a-tooltip>
 | |
|       </template>
 | |
|       <a-input v-model.trim="ruleModal.rule.domain"></a-input>
 | |
|     </a-form-item>
 | |
|     <a-form-item>
 | |
|       <template slot="label">
 | |
|         <a-tooltip>
 | |
|           <template slot="title">
 | |
|             <span>{{ i18n "pages.xray.rules.useComma" }}</span>
 | |
|           </template> User <a-icon type="question-circle"></a-icon>
 | |
|         </a-tooltip>
 | |
|       </template>
 | |
|       <a-input v-model.trim="ruleModal.rule.user"></a-input>
 | |
|     </a-form-item>
 | |
|     <a-form-item>
 | |
|       <template slot="label">
 | |
|         <a-tooltip>
 | |
|           <template slot="title">
 | |
|             <span>{{ i18n "pages.xray.rules.useComma" }}</span>
 | |
|           </template> Port <a-icon type="question-circle"></a-icon>
 | |
|         </a-tooltip>
 | |
|       </template>
 | |
|       <a-input v-model.trim="ruleModal.rule.port"></a-input>
 | |
|     </a-form-item>
 | |
|     <a-form-item label='Inbound Tags'>
 | |
|       <a-select v-model="ruleModal.rule.inboundTag" mode="multiple" :dropdown-class-name="themeSwitcher.currentTheme">
 | |
|         <a-select-option v-for="tag in ruleModal.inboundTags" :value="tag">[[ tag ]]</a-select-option>
 | |
|       </a-select>
 | |
|     </a-form-item>
 | |
|     <a-form-item label='Outbound Tag'>
 | |
|       <a-select v-model="ruleModal.rule.outboundTag" :dropdown-class-name="themeSwitcher.currentTheme">
 | |
|         <a-select-option v-for="tag in ruleModal.outboundTags" :value="tag">[[ tag ]]</a-select-option>
 | |
|       </a-select>
 | |
|     </a-form-item>
 | |
|     <a-form-item>
 | |
|       <template slot="label">
 | |
|         <a-tooltip>
 | |
|           <template slot="title">
 | |
|             <span>{{ i18n "pages.xray.balancer.balancerDesc" }}</span>
 | |
|           </template> Balancer Tag <a-icon type="question-circle"></a-icon>
 | |
|         </a-tooltip>
 | |
|       </template>
 | |
|       <a-select v-model="ruleModal.rule.balancerTag" :dropdown-class-name="themeSwitcher.currentTheme">
 | |
|         <a-select-option v-for="tag in ruleModal.balancerTags" :value="tag">[[ tag ]]</a-select-option>
 | |
|       </a-select>
 | |
|     </a-form-item>
 | |
|   </a-form>
 | |
| </a-modal>
 | |
| <script>
 | |
|   const ruleModal = {
 | |
|     title: '',
 | |
|     visible: false,
 | |
|     confirmLoading: false,
 | |
|     okText: '{{ i18n "sure" }}',
 | |
|     isEdit: false,
 | |
|     confirm: null,
 | |
|     rule: {
 | |
|       type: "field",
 | |
|       domainMatcher: "",
 | |
|       domain: "",
 | |
|       ip: "",
 | |
|       port: "",
 | |
|       sourcePort: "",
 | |
|       network: "",
 | |
|       source: "",
 | |
|       user: "",
 | |
|       inboundTag: [],
 | |
|       protocol: [],
 | |
|       attrs: [],
 | |
|       outboundTag: "",
 | |
|       balancerTag: "",
 | |
|     },
 | |
|     inboundTags: [],
 | |
|     outboundTags: [],
 | |
|     users: [],
 | |
|     balancerTags: [],
 | |
|     ok() {
 | |
|       newRule = ruleModal.getResult();
 | |
|       ObjectUtil.execute(ruleModal.confirm, newRule);
 | |
|     },
 | |
|     show({
 | |
|       title = '',
 | |
|       okText = '{{ i18n "sure" }}',
 | |
|       rule,
 | |
|       confirm = (rule) => {},
 | |
|       isEdit = false
 | |
|     }) {
 | |
|       this.title = title;
 | |
|       this.okText = okText;
 | |
|       this.confirm = confirm;
 | |
|       this.visible = true;
 | |
|       if (isEdit) {
 | |
|         this.rule.domainMatcher = rule.domainMatcher;
 | |
|         this.rule.domain = rule.domain ? rule.domain.join(',') : [];
 | |
|         this.rule.ip = rule.ip ? rule.ip.join(',') : [];
 | |
|         this.rule.port = rule.port;
 | |
|         this.rule.sourcePort = rule.sourcePort;
 | |
|         this.rule.network = rule.network;
 | |
|         this.rule.source = rule.source ? rule.source.join(',') : [];
 | |
|         this.rule.user = rule.user ? rule.user.join(',') : [];
 | |
|         this.rule.inboundTag = rule.inboundTag;
 | |
|         this.rule.protocol = rule.protocol;
 | |
|         this.rule.attrs = rule.attrs ? Object.entries(rule.attrs) : [];
 | |
|         this.rule.outboundTag = rule.outboundTag;
 | |
|         this.rule.balancerTag = rule.balancerTag ? rule.balancerTag : "";
 | |
|       } else {
 | |
|         this.rule = {
 | |
|           domainMatcher: "",
 | |
|           domain: "",
 | |
|           ip: "",
 | |
|           port: "",
 | |
|           sourcePort: "",
 | |
|           network: "",
 | |
|           source: "",
 | |
|           user: "",
 | |
|           inboundTag: [],
 | |
|           protocol: [],
 | |
|           attrs: [],
 | |
|           outboundTag: "",
 | |
|           balancerTag: "",
 | |
|         }
 | |
|       }
 | |
|       this.isEdit = isEdit;
 | |
|       this.inboundTags = app.templateSettings.inbounds.filter((i) => !ObjectUtil.isEmpty(i.tag)).map(obj => obj.tag);
 | |
|       this.inboundTags.push(...app.inboundTags);
 | |
|       if (app.enableDNS && !ObjectUtil.isEmpty(app.dnsTag)) this.inboundTags.push(app.dnsTag)
 | |
|       this.outboundTags = ["", ...app.templateSettings.outbounds.filter((o) => !ObjectUtil.isEmpty(o.tag)).map(obj => obj.tag)];
 | |
|       if (app.templateSettings.reverse) {
 | |
|         if (app.templateSettings.reverse.bridges) {
 | |
|           this.inboundTags.push(...app.templateSettings.reverse.bridges.map(b => b.tag));
 | |
|         }
 | |
|         if (app.templateSettings.reverse.portals) this.outboundTags.push(...app.templateSettings.reverse.portals.map(b => b.tag));
 | |
|       }
 | |
|       if (app.templateSettings.routing && app.templateSettings.routing.balancers) {
 | |
|         this.balancerTags = ["", ...app.templateSettings.routing.balancers.filter((o) => !ObjectUtil.isEmpty(o.tag)).map(obj => obj.tag)];
 | |
|       }
 | |
|     },
 | |
|     close() {
 | |
|       ruleModal.visible = false;
 | |
|       ruleModal.loading(false);
 | |
|     },
 | |
|     loading(loading = true) {
 | |
|       ruleModal.confirmLoading = loading;
 | |
|     },
 | |
|     getResult() {
 | |
|       value = ruleModal.rule;
 | |
|       rule = {};
 | |
|       newRule = {};
 | |
|       rule.type = "field";
 | |
|       rule.domainMatcher = value.domainMatcher;
 | |
|       rule.domain = value.domain.length > 0 ? value.domain.split(',') : [];
 | |
|       rule.ip = value.ip.length > 0 ? value.ip.split(',') : [];
 | |
|       rule.port = value.port;
 | |
|       rule.sourcePort = value.sourcePort;
 | |
|       rule.network = value.network;
 | |
|       rule.source = value.source.length > 0 ? value.source.split(',') : [];
 | |
|       rule.user = value.user.length > 0 ? value.user.split(',') : [];
 | |
|       rule.inboundTag = value.inboundTag;
 | |
|       rule.protocol = value.protocol;
 | |
|       rule.attrs = Object.fromEntries(value.attrs);
 | |
|       rule.outboundTag = value.outboundTag == "" ? undefined : value.outboundTag;
 | |
|       rule.balancerTag = value.balancerTag == "" ? undefined : value.balancerTag;
 | |
|       for (const [key, value] of Object.entries(rule)) {
 | |
|         if (value !== null && value !== undefined && !(Array.isArray(value) && value.length === 0) && !(typeof value === 'object' && Object.keys(value).length === 0) && value !== '') {
 | |
|           newRule[key] = value;
 | |
|         }
 | |
|       }
 | |
|       return newRule;
 | |
|     }
 | |
|   };
 | |
|   new Vue({
 | |
|     delimiters: ['[[', ']]'],
 | |
|     el: '#rule-modal',
 | |
|     data: {
 | |
|       ruleModal: ruleModal,
 | |
|     }
 | |
|   });
 | |
| </script>
 | |
| {{end}}
 |