Файловый менеджер - Редактировать - /var/www/axolotl/data/www/axolotls.ru/www/bitrix/js/salescenter/app/src/bx-salescenter-app-add-payment-product.js
Назад
import {config} from "./config"; import {Vue} from "ui.vue"; import {PopupMenuWindow} from 'main.popup'; import "ui.dropdown"; import "ui.common"; import "ui.alerts"; import {MixinTemplatesType} from "./components/templates-type-mixin"; import {type BaseEvent, EventEmitter} from 'main.core.events'; Vue.component(config.templateAddPaymentProductName, { /** * @emits 'changeBasketItem' {index: number, fields: object} * @emits 'refreshBasket' {timeout: number} * @emits 'removeItem' {index: number} */ props: ['basketItem', 'basketItemIndex', 'countItems', 'selectedProductIds'], mixins:[MixinTemplatesType], data() { return { timer: null, productSelector: null, isNeedRebindSearch: false, }; }, created() { this.currencySymbol = this.$root.$app.options.currencySymbol; this.defaultMeasure = { name: '', id: null, }; this.measures = this.$root.$app.options.measures || []; if (BX.type.isArray(this.measures) && this.measures) { this.measures.map((measure) => { if (measure['IS_DEFAULT'] === 'Y') { this.defaultMeasure.name = measure.SYMBOL; this.defaultMeasure.code = measure.CODE; if (!this.basketItem.measureName && !this.basketItem.measureName) { this.changeData({ measureCode: this.defaultMeasure.code, measureName: this.defaultMeasure.name }); } } }); } EventEmitter.subscribe('onUploaderIsInited', this.onUploaderIsInitedHandler.bind(this)); }, mounted() { this.productSelector = new BX.UI.Dropdown( { searchAction: "salescenter.api.order.searchProduct", searchOptions: { restrictedSearchIds: this.selectedProductIds }, enableCreation: true, searchResultRenderer: null, targetElement: this.$refs.searchProductLine, items: [{ title: '', subTitle: this.localize.SALESCENTER_PRODUCT_BEFORE_SEARCH_TITLE }], messages: { creationLegend: this.localize.SALESCENTER_PRODUCT_CREATE, notFound: this.localize.SALESCENTER_PRODUCT_NOT_FOUND, }, events: { onSelect: this.selectCatalogItem.bind(this), onAdd: this.showCreationForm.bind(this), onReset: this.resetSearchForm(this) } } ); }, directives: { 'bx-search-product': { inserted(element, binding) { if (binding.value.selector instanceof BX.UI.Dropdown) { const restrictedSearchIds = binding.value.restrictedIds; binding.value.selector.targetElement = element; if (BX.type.isArray(restrictedSearchIds)) { binding.value.selector.searchOptions = {restrictedSearchIds}; binding.value.selector.items = binding.value.selector.items.filter( item => !restrictedSearchIds.includes(item.id) ); } binding.value.selector.init() } } } }, methods: { onUploaderIsInitedHandler(event: BaseEvent) { const [, uploader] = event.getCompatData(); EventEmitter.subscribe(uploader, 'onFileIsUploaded', this.onFileIsUploadedHandler.bind(this)); EventEmitter.subscribe(uploader, 'onFileIsDeleted', this.onFileIsDeleteHandler.bind(this)); }, onFileIsUploadedHandler(event: BaseEvent) { const [fileId, , params] = event.getCompatData(); let images = this.basketItem.image, file = params && params['file'] && params['file']['files'] && params['file']['files']['default'] ? params['file']['files']['default'] : false; if (file) { images.push({ id: fileId, data: { name: file.name, type: file.type, tmp_name: file.path, size: file.size, error: null, } }); let fields = { image: images, }; this.changeData(fields); } }, onFileIsDeleteHandler(event: BaseEvent) { const [fileId] = event.getCompatData(); let images = this.basketItem.image; images.forEach(function (item, index, object) { if (item.id === fileId) { object.splice(index, 1); } }); let fields = { image: images, }; this.changeData(fields); }, getQuantityTitle(string) { let result = string.split(' '); let html = ''; for (var i = 0; i < result.length; i++) { if(result.length - 1 !== i) { html += result[i] + ' '; } else { html += '<span>' + result[i] + '</span>' } } return html }, toggleDiscount(value) { this.changeData( {showDiscount: value} ); value === 'Y' ? setTimeout(() => this.$refs.discountInput.focus()) : null; }, changeData(fields) { this.$emit('changeBasketItem', { index: this.basketItemIndex, fields: fields }); }, isNeedRefreshAfterChanges() { if (this.isCreationMode) { return this.basketItem.name.length > 0 && this.basketItem.quantity > 0 && this.basketItem.basePrice > 0 } return true; }, refreshBasket() { if (this.isNeedRefreshAfterChanges()) this.$emit('refreshBasket'); }, debouncedRefresh(delay) { if (this.timer) { clearTimeout(this.timer); } this.timer = setTimeout(() => { this.refreshBasket(); this.timer = null; }, delay); }, changeQuantity(event) { event.target.value = event.target.value.replace(/[^.,\d]/g,''); let newQuantity = parseFloat(event.target.value); let lastSymbol = event.target.value.substr(-1); if (lastSymbol === ',') { event.target.value = event.target.value.replace(',', "."); } if (!newQuantity || lastSymbol === '.' || lastSymbol === ',') { return; } let fields = this.basketItem; fields.quantity = newQuantity; this.changeData(fields); this.debouncedRefresh(300); }, changeName(event) { let newName = event.target.value; let fields = this.basketItem; fields.name = newName; this.changeData(fields); this.refreshBasket(); }, changeBasePrice(event) { event.target.value = event.target.value.replace(/[^.,\d]/g,''); if (event.target.value === '') { event.target.value = 0; } let lastSymbol = event.target.value.substr(-1); if (lastSymbol === ',') { event.target.value = event.target.value.replace(',', "."); } let newPrice = parseFloat(event.target.value); if (newPrice < 0|| lastSymbol === '.' || lastSymbol === ',') { return; } let fields = this.basketItem; fields.basePrice = newPrice; if (fields.module !== 'catalog') { fields.catalogPrice = newPrice; } fields.isCustomPrice = 'Y'; this.changeData(fields); this.debouncedRefresh(300); }, /** * * @param discountType {string} */ changeDiscountType(discountType) { let type = (discountType === 'currency') ? 'currency' : 'percent'; let fields = this.basketItem; fields.discountType = type; fields.isCustomPrice = 'Y'; this.changeData(fields); if (parseFloat(this.basketItem.discount) > 0) { this.refreshBasket(); } }, changeDiscount(event) { let discountValue = parseFloat(event.target.value) || 0; if (discountValue === parseFloat(this.basketItem.discount)) { return; } let fields = this.basketItem; fields.discount = discountValue; fields.isCustomPrice = 'Y'; this.changeData(fields); this.debouncedRefresh(300); }, changeMeasureValue(event) { let measureCode = parseInt(event.target.value); let measureName = ''; this.measures.forEach ((measure) => { if (parseInt(measure.CODE) === measureCode) { measureName = measure.SYMBOL; } }); this.changeData({ measureCode: measureCode, measureName: measureName }); }, showCreationForm() { if (!(this.productSelector instanceof BX.UI.Dropdown)) return true; const value = this.productSelector.targetElement.value; this.changeData({ productId: 0, quantity: 1, module: null, sort: this.basketItemIndex, isCreatedProduct: 'Y', name: value, encodedFields: null, isCustomPrice: 'Y', discountInfos: [] }); BX.ajax.runAction( "salescenter.api.order.getDefaultFilePropertyEditHtml" ).then((result) => { let data = BX.prop.getObject(result, "data", {'filePropertyEditHtml': ''}); if (data.filePropertyEditHtml) { BX.html(this.$refs.filePropertyEditHtml, data.filePropertyEditHtml); } }); this.productSelector.destroyPopupWindow(); }, resetSearchForm() { if (!(this.productSelector instanceof BX.UI.Dropdown)) return true; this.productSelector.targetElement.value = ''; this.productSelector.items = [{ title: '', subTitle: this.localize.SALESCENTER_PRODUCT_BEFORE_SEARCH_TITLE }]; this.changeData({ productId: 0, name: '', encodedFields: null, quantity: 0, basePrice: 0, formattedPrice: 0, catalogPrice: 0, formattedCatalogPrice: null, discount: 0, discountInfos: [], image:[], errors:[], }); BX.html(this.$refs.filePropertyEditHtml, ''); this.productSelector.destroyPopupWindow(); }, hideCreationForm() { if (!(this.productSelector instanceof BX.UI.Dropdown)) return true; this.changeData({ isCreatedProduct: 'N', productId: 0, name: '', encodedFields: null, quantity: 0, basePrice: 0, formattedPrice: 0, catalogPrice: 0, formattedCatalogPrice: null, discount: 0, discountInfos: [], image:[], errors:[], }); this.refreshBasket(); BX.html(this.$refs.filePropertyEditHtml, ''); this.isNeedRebindSearch = true; }, removeItem() { this.$emit('removeItem', { index: this.basketItemIndex }); }, selectCatalogItem(sender, item) { if (!sender instanceof BX.UI.Dropdown) return true; if (item.id === undefined || parseInt(item.id) <= 0) return true; let fields = { name: item.title, productId: item.id, sort: this.basketItemIndex, module: 'catalog', quantity: this.basketItem.quantity > 0 ? this.basketItem.quantity : 1, }; if (this.basketItemIndex.productId !== item.id) { fields.encodedFields = null; fields.discount = 0; fields.isCustomPrice = 'N'; } BX.ajax.runAction( "salescenter.api.order.getProductData", { data: { productId: item.id } } ).then((result) => { let data = BX.prop.getObject(result, "data", {}); fields.basePrice = data.price; fields.catalogPrice = data.price; this.changeData(fields); this.$emit('refreshBasket', 0); if (data.filePropertyEditHtml) { let filePropertyEditHtml = BX.processHTML(data.filePropertyEditHtml); BX.html(this.$refs.filePropertyEditHtml, filePropertyEditHtml['HTML']); } }); sender.destroyPopupWindow(); }, openDiscountEditor(e, url) { if(!(window.top.BX.SidePanel && window.top.BX.SidePanel.Instance)) { return; } window.top.BX.SidePanel.Instance.open ( BX.util.add_url_param( url, { "IFRAME": "Y", "IFRAME_TYPE": "SIDE_SLIDER", "publicSidePanel": "Y" } ), { allowChangeHistory: false } ); e.preventDefault ? e.preventDefault() : (e.returnValue = false); }, isEmptyProductName() { return (this.basketItem.name.length === 0); }, incrementQuantity() { if(this.productSelector.targetElement.value === '' && this.isEmptyProductName()) { return; } if(this.basketItem.quantity === 0) { setTimeout(() => { this.basketItem.quantity = 1; this.changeData(this.basketItem); }, 5); return; } this.basketItem.quantity++; this.changeData(this.basketItem); }, decrementQuantity() { if(this.basketItem.quantity > 0) { this.basketItem.quantity = this.basketItem.quantity - 1; this.changeData(this.basketItem); } }, showPopupMenu(target, array, type) { let menuItems = []; let setItem = (ev, param) => { target.innerHTML = ev.target.innerHTML; if(type === 'discount') { this.changeDiscountType(param.options.type); } this.popupMenu.close(); }; if(type === 'discount') { array = []; array.percent = '%'; array.currency = this.currencySymbol; } if(array) { for(let item in array) { let text = array[item]; if(type === 'measures') { text = array[item].SYMBOL; } menuItems.push({ text: text, onclick: setItem.bind({ value: 'settswguy' }), type: type === 'discount' ? item : null }) } } this.popupMenu = new PopupMenuWindow({ bindElement: target, items: menuItems }); this.popupMenu.show(); } }, computed: { localize() { return Vue.getFilteredPhrases('SALESCENTER_PRODUCT_'); }, showDiscount() { return this.basketItem.showDiscount === 'Y'; }, showCatalogPrice() { return this.basketItem.discount > 0 || parseFloat(this.basketItem.basePrice) !== parseFloat(this.basketItem.catalogPrice); }, getMeasureName() { return this.basketItem.measureName || this.defaultMeasure.name; }, getMeasureCode() { return this.basketItem.measureCode || this.defaultMeasure.code; }, getBasketFilePropertyEditHtml() { let filePropertyEditHtml = this.basketItem.filePropertyEditHtml, html = ''; if (filePropertyEditHtml) { let data = BX.processHTML(filePropertyEditHtml); html = data['HTML']; } return html; }, restrictedSearchIds() { let restrictedSearchIds = this.selectedProductIds; if (this.basketItem.module === 'catalog') { restrictedSearchIds = restrictedSearchIds.filter(id => id !== this.basketItem.productId); } return restrictedSearchIds; }, isCreationMode() { return this.basketItem.isCreatedProduct === 'Y'; }, isNotEnoughQuantity() { return this.basketItem.errors.includes('SALE_BASKET_AVAILABLE_QUANTITY'); }, hasPriceError() { return this.basketItem.errors.includes('SALE_BASKET_ITEM_WRONG_PRICE'); } }, template: ` <div> <!--counters anr remover--> <div class="salescenter-app-counter">{{basketItemIndex + 1}}</div> <div class="salescenter-app-remove" @click="removeItem" v-if="countItems > 1 && editable"></div> <!--counters anr remover end--> <!--if isCreationMode--> <div class="salescenter-app-form-container" v-if="!isCreationMode"> <div class="salescenter-app-form-row"> <!--col 1--> <div class="salescenter-app-form-col salescenter-app-form-col-prod" style="flex:8"> <div> <label class="salescenter-app-ctl-label-text ui-ctl-label-text">{{localize.SALESCENTER_PRODUCT_NAME}}</label> <div class="ui-ctl ui-ctl-md ui-ctl-after-icon"> <button class="ui-ctl-after ui-ctl-icon-clear" @click="resetSearchForm" v-if="basketItem.name.length > 0 && editable"/> <input type="text" ref="searchProductLine" class="ui-ctl-element ui-ctl-textbox salescenter-app-product-search" :value="basketItem.name" v-bx-search-product="{selector: productSelector, restrictedIds: restrictedSearchIds}" :disabled="!editable"> </div> </div> <div v-if="getBasketFilePropertyEditHtml" class="salescenter-app-form-col-img"> <div v-html="getBasketFilePropertyEditHtml"></div> </div> <div v-else class="salescenter-app-form-col-img"> <div ref="filePropertyEditHtml"></div> </div> </div> <!--col 1 end--> <!--col 2--> <div class="salescenter-app-form-col salescenter-app-form-col-sm" style="flex:2"> <label class="salescenter-app-ctl-label-text salescenter-app-ctl-label-text-link ui-ctl-label-text"> {{localize.SALESCENTER_PRODUCT_QUANTITY.replace('#MEASURE_NAME#', ' ')}} <span @click="showPopupMenu($event.target, measures, 'measures')">{{ getMeasureName }}</span> </label> <div class="ui-ctl ui-ctl-md ui-ctl-w100" :class="isNotEnoughQuantity ? 'ui-ctl-danger' : ''"> <input type="text" class="ui-ctl-element ui-ctl-textbox" :value="basketItem.quantity" @input="changeQuantity" @change="refreshBasket" :disabled="!editable"> <div class="salescenter-app-input-counter" v-if="editable"> <div class="salescenter-app-input-counter-up" @click="incrementQuantity"></div> <div class="salescenter-app-input-counter-down" @click="decrementQuantity"></div> </div> </div> <div class="salescenter-form-error" v-if="isNotEnoughQuantity">{{localize.SALESCENTER_PRODUCT_IS_NOT_AVAILABLE}}</div> </div> <!--col 2 end--> <!--col 3--> <div class="salescenter-app-form-col salescenter-app-form-col-sm" style="flex:2"> <label class="salescenter-app-ctl-label-text ui-ctl-label-text">{{localize.SALESCENTER_PRODUCT_PRICE_2}}</label> <div class="ui-ctl ui-ctl-md ui-ctl-w100 salescenter-app-col-currency" :class="hasPriceError ? 'ui-ctl-danger' : ''"> <input type="text" class="ui-ctl-element ui-ctl-textbox" :value="basketItem.basePrice" @input="changeBasePrice" @change="refreshBasket" :disabled="!editable"> <div class="salescenter-app-col-currency-symbol" v-html="currencySymbol"></div> </div> </div> <!--col 3 end--> </div> <!--show discount link--> <div class="salescenter-app-form-row" v-if="editable || (!editable && showCatalogPrice)"> <div style="flex: 8;"></div> <div class="salescenter-app-form-col salescenter-app-form-col-sm" style="flex: 2;"> <div v-if="showDiscount" class="salescenter-app-collapse-link-pointer-event">{{localize.SALESCENTER_PRODUCT_DISCOUNT_PRICE_TITLE}}</div> </div> <div class="salescenter-app-form-col salescenter-app-form-col-sm" style="flex:2" v-if="showDiscount"> <div class="salescenter-app-collapse-link-hide" @click="toggleDiscount('N')">{{localize.SALESCENTER_PRODUCT_DISCOUNT_TITLE}}</div> </div> <div class="salescenter-app-form-col salescenter-app-form-col-sm" style="flex:2" v-else> <div class="salescenter-app-collapse-link-show" @click="toggleDiscount('Y')">{{localize.SALESCENTER_PRODUCT_DISCOUNT_TITLE}}</div> </div> </div> <!--show discount link end--> <!--dicount controller--> <div class="salescenter-app-form-row" style="margin-bottom: 7px" v-if="showDiscount"> <div class="salescenter-app-form-collapse-container"> <div class="salescenter-app-form-row"> <div class="salescenter-app-form-col" style="flex: 8"></div> <div class="salescenter-app-form-col salescenter-app-form-col-sm" style="flex:2; overflow: hidden;"> <div class="ui-ctl ui-ctl-md ui-ctl-w100 salescenter-app-col-currency"> <div class="ui-ctl-element ui-ctl-textbox salescenter-ui-ctl-element" v-html="basketItem.formattedPrice" disabled="true"></div> <div class="salescenter-app-col-currency-symbol" v-html="currencySymbol"></div> </div> </div> <div class="salescenter-app-form-col salescenter-app-form-col-sm" style="flex:2; overflow: hidden;"> <div class="ui-ctl ui-ctl-after-icon ui-ctl-w100 ui-ctl-dropdown salescenter-app-col-currency"> <input type="text" class="ui-ctl-element ui-ctl-textbox" ref="discountInput" :value="basketItem.discount" @input="changeDiscount" @change="refreshBasket" :disabled="!editable"> <div class="salescenter-app-col-currency-symbol salescenter-app-col-currency-symbol-link" @click="showPopupMenu($event.target.firstChild, null, 'discount')"><span v-html="basketItem.discountType === 'percent' ? '%' : currencySymbol"></span></div> </div> </div> </div> <div class="salescenter-app-form-row" style="margin-bottom: 0;" v-if="editable"> <div class="salescenter-app-form-col" v-for="discount in basketItem.discountInfos""> <span class="ui-text-4 ui-color-light"> {{discount.name}}<a :href="discount.editPageUrl" @click="openDiscountEditor(event, discount.editPageUrl)">{{localize.SALESCENTER_PRODUCT_DISCOUNT_EDIT_PAGE_URL_TITLE}}</a></span> </div> </div> </div> </div> <!--dicount controller end--> </div> <!--endif isCreationMode--> <!--else isCreationMode--> <div class="salescenter-app-form-container" v-else> <div class="salescenter-app-form-row"> <!--col 1--> <div class="salescenter-app-form-col salescenter-app-form-col-prod" style="flex:8"> <div class="salescenter-app-form-col-input"> <label class="salescenter-app-ctl-label-text ui-ctl-label-text">{{localize.SALESCENTER_PRODUCT_TITLE}}</label> <div class="ui-ctl ui-ctl-md ui-ctl-after-icon"> <button class="ui-ctl-after ui-ctl-icon-clear" @click="hideCreationForm"> </button> <input type="text" class="ui-ctl-element ui-ctl-textbox" @change="changeName" :value="basketItem.name"> <div class="ui-ctl-tag">{{localize.SALESCENTER_PRODUCT_NEW_LABEL}}</div> </div> </div> <div class="salescenter-app-form-col-img"> <div ref="filePropertyEditHtml"></div> </div> </div> <!--col 1 end--> <!--col 2--> <div class="salescenter-app-form-col salescenter-app-form-col-sm" style="flex:2"> <label class="salescenter-app-ctl-label-text salescenter-app-ctl-label-text-link ui-ctl-label-text"> {{localize.SALESCENTER_PRODUCT_QUANTITY.replace('#MEASURE_NAME#', ' ')}} <span @click="showPopupMenu($event.target, measures, 'measures')">{{ getMeasureName }}</span> </label> <div class="ui-ctl ui-ctl-md ui-ctl-w100"> <input type="text" class="ui-ctl-element ui-ctl-textbox" :value="basketItem.quantity" @input="changeQuantity" @change="refreshBasket"> <div class="salescenter-app-input-counter" v-if="editable"> <div class="salescenter-app-input-counter-up" @click="incrementQuantity"></div> <div class="salescenter-app-input-counter-down" @click="decrementQuantity"></div> </div> </div> </div> <!--col 2 end--> <!--col 3--> <div class="salescenter-app-form-col salescenter-app-form-col-sm" style="flex:2"> <label class="salescenter-app-ctl-label-text ui-ctl-label-text">{{localize.SALESCENTER_PRODUCT_PRICE_2}}</label> <div class="ui-ctl ui-ctl-md ui-ctl-w100 salescenter-app-col-currency" :class="hasPriceError ? 'ui-ctl-danger' : ''"> <input type="text" class="ui-ctl-element ui-ctl-textbox" :value="basketItem.basePrice" @input="changeBasePrice" @change="refreshBasket" :disabled="!editable"> <div class="salescenter-app-col-currency-symbol" v-html="currencySymbol"></div> </div> </div> <!--col 3 end--> </div> <!--show discount link--> <div class="salescenter-app-form-row" v-if="editable || (!editable && showCatalogPrice)"> <div style="flex: 8;"></div> <div class="salescenter-app-form-col salescenter-app-form-col-sm" style="flex: 2;"> <div v-if="showDiscount" class="salescenter-app-collapse-link-pointer-event">{{localize.SALESCENTER_PRODUCT_DISCOUNT_PRICE_TITLE}}</div> </div> <div class="salescenter-app-form-col salescenter-app-form-col-sm" style="flex:2" v-if="showDiscount"> <div class="salescenter-app-collapse-link-hide" @click="toggleDiscount('N')">{{localize.SALESCENTER_PRODUCT_DISCOUNT_TITLE}}</div> </div> <div class="salescenter-app-form-col salescenter-app-form-col-sm" style="flex:2" v-else> <div class="salescenter-app-collapse-link-show" @click="toggleDiscount('Y')">{{localize.SALESCENTER_PRODUCT_DISCOUNT_TITLE}}</div> </div> </div> <!--show discount link end--> <!--dicount controller--> <div class="salescenter-app-form-row" style="margin-bottom: 7px" v-if="showDiscount"> <div class="salescenter-app-form-collapse-container"> <div class="salescenter-app-form-row"> <div class="salescenter-app-form-col" style="flex: 8"></div> <div class="salescenter-app-form-col salescenter-app-form-col-sm" style="flex:2; overflow: hidden;"> <div class="ui-ctl ui-ctl-md ui-ctl-w100 salescenter-app-col-currency"> <div class="ui-ctl-element ui-ctl-textbox salescenter-ui-ctl-element" v-html="basketItem.formattedPrice" disabled="true"></div> <div class="salescenter-app-col-currency-symbol" v-html="currencySymbol"></div> </div> </div> <div class="salescenter-app-form-col salescenter-app-form-col-sm" style="flex:2; overflow: hidden;"> <div class="ui-ctl ui-ctl-after-icon ui-ctl-w100 ui-ctl-dropdown salescenter-app-col-currency"> <input type="text" class="ui-ctl-element ui-ctl-textbox" ref="discountInput" :value="basketItem.discount" @input="changeDiscount" @change="refreshBasket" :disabled="!editable"> <div class="salescenter-app-col-currency-symbol salescenter-app-col-currency-symbol-link" @click="showPopupMenu($event.target.firstChild, null, 'discount')"><span v-html="basketItem.discountType === 'percent' ? '%' : currencySymbol"></span></div> </div> </div> </div> <div class="salescenter-app-form-row" style="margin-bottom: 0;" v-if="editable"> <div class="salescenter-app-form-col" v-for="discount in basketItem.discountInfos""> <span class="ui-text-4 ui-color-light"> {{discount.name}} <a :href="discount.editPageUrl" @click="openDiscountEditor(event, discount.editPageUrl)">{{localize.SALESCENTER_PRODUCT_DISCOUNT_EDIT_PAGE_URL_TITLE}}</a></span> </div> </div> </div> </div> <!--dicount controller end--> </div> <!--endelse isCreationMode--> </div> ` });
| ver. 1.4 |
Github
|
.
| PHP 7.4.8 | Генерация страницы: 0 |
proxy
|
phpinfo
|
Настройка