define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) { var Controller = { index: function () { Table.api.init({ extend: { index_url: 'split.link/index' + location.search, add_url: 'split.link/add', edit_url: 'split.link/edit', del_url: 'split.link/del', multi_url: 'split.link/multi', table: 'split_link', } }); var table = $("#table"); table.bootstrapTable({ url: $.fn.bootstrapTable.defaults.extend.index_url, pk: 'id', sortName: 'id', sortOrder: 'desc', fixedColumns: true, fixedRightNumber: 1, columns: [ [ {checkbox: true}, {field: 'countries_text', title: __('Countries'), operate: false, formatter: Table.api.formatter.content}, { field: 'link_code', title: __('Link_code'), operate: 'LIKE', formatter: Controller.api.formatter.linkCode }, {field: 'description', title: __('Description'), operate: 'LIKE', class: 'autocontent', formatter: Table.api.formatter.content}, { field: 'auto_reply_text', title: __('Reply statements column'), operate: false, formatter: Controller.api.formatter.autoReplyText }, {field: 'ip_protect', title: __('Ip_protect'), searchList: Config.ipProtectList, formatter: Table.api.formatter.status}, {field: 'random_shuffle', title: __('Random_shuffle'), searchList: Config.randomShuffleList, formatter: Table.api.formatter.status}, {field: 'status', title: __('Status'), searchList: Config.statusList, formatter: Table.api.formatter.status}, {field: 'createtime', title: __('Createtime'), operate: 'RANGE', addclass: 'datetimerange', autocomplete: false, formatter: Table.api.formatter.datetime, sortable: true}, { field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, buttons: [ { name: 'autoreply', text: __('Auto reply'), title: __('Auto reply'), icon: 'fa fa-commenting-o', classname: 'btn btn-warning btn-xs btn-split-autoreply', url: 'javascript:;' } ], formatter: Table.api.formatter.operate } ] ] }); table.on('click', '.btn-copy-split-link', function (e) { e.preventDefault(); e.stopPropagation(); var linkCode = $.trim($(this).data('link-code') || ''); if (!linkCode) { return false; } Controller.api.openCopyModal(linkCode); }); table.on('click', '.btn-copy-link-code-inline', function (e) { e.preventDefault(); e.stopPropagation(); var linkCode = $.trim($(this).data('link-code') || ''); if (linkCode) { Controller.api.copyText(linkCode); } }); table.on('click', '.btn-split-autoreply', function (e) { e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation(); var rowIndex = $(this).data('row-index'); var row = Table.api.getrowbyindex(table, rowIndex); if (!row || !row.id) { return false; } Controller.api.openAutoReplyModal(row); }); Controller.api.bindAutoReplyPreviewTips(table); Table.api.bindevent(table); }, add: function () { Controller.api.bindevent(); Controller.api.bindLinkCodeInput(); }, edit: function () { Controller.api.bindevent(); }, api: { /** 弹窗样式(仅注入一次) */ modalStyleInjected: false, injectModalStyles: function () { if (Controller.api.modalStyleInjected) { return; } Controller.api.modalStyleInjected = true; var css = [ '.split-link-copy-modal{padding:16px 20px;box-sizing:border-box;}', '.split-link-copy-modal .form-group{margin-bottom:16px;}', '.split-link-copy-modal .control-label{display:block;font-weight:600;color:#444;margin-bottom:8px;}', '.split-link-copy-modal .split-link-code-row{display:flex;align-items:center;flex-wrap:wrap;gap:8px;}', '.split-link-copy-modal .split-link-code-value{font-size:18px;font-weight:600;color:#337ab7;text-decoration:underline;word-break:break-all;}', '.split-link-copy-modal .split-domain-tabs{margin-bottom:0;border-bottom:1px solid #ddd;}', '.split-link-copy-modal .split-domain-tab-box{border:1px solid #ddd;border-top:none;border-radius:0 0 4px 4px;background:#fafafa;overflow:hidden;}', '.split-link-copy-modal .split-domain-tab-box .tab-pane{max-height:280px;overflow-y:auto;overflow-x:hidden;padding:10px 12px;margin:0;}', '.split-link-copy-modal .split-domain-list{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;align-items:stretch;}', '.split-link-copy-modal .split-domain-item{border:1px solid #e8e8e8;border-radius:4px;background:#fff;transition:border-color .2s,box-shadow .2s;height:100%;min-width:0;}', '.split-link-copy-modal .split-domain-item:hover{border-color:#b8d4f0;}', '.split-link-copy-modal .split-domain-item.is-checked{border-color:#337ab7;box-shadow:0 0 0 1px rgba(51,122,183,.15);}', '.split-link-copy-modal .split-domain-item-label{display:flex;align-items:flex-start;gap:8px;margin:0;padding:8px 10px;cursor:pointer;font-weight:normal;width:100%;height:100%;box-sizing:border-box;}', '.split-link-copy-modal .split-domain-item-label input[type=radio]{margin:3px 0 0;flex-shrink:0;}', '.split-link-copy-modal .split-domain-item-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:5px;}', '.split-link-copy-modal .split-domain-name{font-size:13px;font-weight:600;color:#333;word-break:break-all;line-height:1.4;}', '.split-link-copy-modal .split-domain-status{display:flex;flex-direction:column;align-items:flex-start;gap:4px;}', '.split-link-copy-modal .split-domain-status .label{font-size:10px;font-weight:normal;padding:2px 6px;line-height:1.3;white-space:nowrap;margin:0;display:inline-block;max-width:100%;overflow:hidden;text-overflow:ellipsis;}', '.split-link-copy-modal .split-domain-empty{margin:0;padding:20px 12px;color:#999;text-align:center;line-height:1.6;grid-column:1/-1;}', '.split-link-copy-modal .split-tip-box{margin:12px 0;padding:10px 12px;background:#f8f9fa;border-left:3px solid #337ab7;border-radius:0 4px 4px 0;line-height:1.6;color:#666;}', '.split-link-copy-modal .split-generated-url{font-size:12px;}', '.split-link-copy-modal .split-modal-footer{margin-top:16px;padding-top:12px;border-top:1px solid #eee;}' ].join(''); $('').text(css).appendTo('head'); }, formatter: { /** * 回复语:列表最多 50 字 + ...,悬停保留换行显示全文 */ autoReplyText: function (value, row, index) { var full = (row.auto_reply != null && row.auto_reply !== '') ? String(row.auto_reply) : ''; if (full === '') { return '-'; } var preview = value != null && value !== '' ? String(value) : full; var safePreview = Fast.api.escape(preview); var encFull = encodeURIComponent(full); return '' + safePreview + ''; }, /** * 分流链接列:链接样式 + COPY 图标,点击链接打开弹窗 */ linkCode: function (value) { value = value == null ? '' : value.toString(); if (value === '') { return '-'; } var safe = Fast.api.escape(value); return '' + '' + safe + '' + ' ' + ''; } }, openCopyModal: function (linkCode) { Controller.api.loadCopyModalData(function (data) { Controller.api.renderCopyModal(linkCode, data); }); }, /** 回复语列悬停提示(保留换行) */ autoReplyTipIndex: null, bindAutoReplyPreviewTips: function (table) { table.off('mouseenter.splitAutoReply mouseleave.splitAutoReply') .on('mouseenter.splitAutoReply', '.split-auto-reply-preview', function () { var enc = $(this).attr('data-full'); if (!enc) { return; } var full = ''; try { full = decodeURIComponent(enc); } catch (e) { return; } var html = '
' + Controller.api.escapeHtmlWithNewlines(full) + '
'; Controller.api.autoReplyTipIndex = Layer.tips(html, this, { tips: [1, ''], time: 0, maxWidth: 450 }); }) .on('mouseleave.splitAutoReply', '.split-auto-reply-preview', function () { if (Controller.api.autoReplyTipIndex != null) { Layer.close(Controller.api.autoReplyTipIndex); Controller.api.autoReplyTipIndex = null; } }); }, escapeHtmlWithNewlines: function (text) { return $('
').text(text).html().replace(/\n/g, '
'); }, /** * 打开自动回复弹窗 * * @param {object} row 列表行数据 */ openAutoReplyModal: function (row) { Fast.api.ajax({ url: 'split.link/autoreply/ids/' + row.id, type: 'get' }, function (data, ret) { var info = ret.data || data || {}; Controller.api.renderAutoReplyModal(row.id, info); return false; }); }, renderAutoReplyModal: function (linkId, info) { var linkCode = Fast.api.escape(info.link_code || ''); var autoReply = info.auto_reply || ''; var html = [ '
', '
', ' ', '

' + linkCode + '

', '
', '
', ' ', '

' + __('Reply statements tip') + '

', ' ', '
', '
' ].join(''); Layer.open({ type: 1, title: __('Auto reply'), area: ['520px', 'auto'], shadeClose: false, content: html, btn: [__('OK'), __('Close')], yes: function (index, layero) { var text = layero.find('#split-auto-reply-text').val(); Fast.api.ajax({ url: 'split.link/autoreply/ids/' + linkId, type: 'post', data: {auto_reply: text} }, function () { Layer.close(index); Toastr.success(__('Auto reply saved')); }); return false; } }); }, loadCopyModalData: function (callback) { Fast.api.ajax({ url: 'split.link/copyinfo', type: 'get' }, function (data, ret) { callback(ret.data || data || {}); return false; }); }, renderCopyModal: function (linkCode, data) { Controller.api.injectModalStyles(); var platformDomains = $.isArray(data.platform_domains) ? data.platform_domains : []; if (!platformDomains.length && data.platform_domain) { var rawSingle = $.trim(data.platform_domain); platformDomains = rawSingle.split(/[\r\n,]+/).map(function (d) { return $.trim(d); }).filter(function (d) { return d.length > 0; }); } var myDomains = $.isArray(data.my_domains) ? data.my_domains : []; var domainIndexUrl = data.domain_index_url || 'domain/index'; var configIndexUrl = data.config_index_url || 'general/config/index'; var defaultType = platformDomains.length ? 'platform' : 'my'; var defaultDomain = platformDomains.length ? platformDomains[0] : (myDomains.length ? myDomains[0].domain : ''); var activeTab = defaultType === 'platform' ? 'platform' : 'my'; var platformHtml = ''; if (platformDomains.length) { var platformItems = []; $.each(platformDomains, function (i, domain) { platformItems.push(Controller.api.buildDomainRadio({ domain: domain, type: 'platform', checked: defaultType === 'platform' && domain === defaultDomain, showStatus: false })); }); platformHtml = '
' + platformItems.join('') + '
'; } else { platformHtml = '

' + __('Split platform domain empty') + ' ' + __('Go system config') + '' + '

'; } var myDomainsHtml = ''; if (myDomains.length) { var items = []; $.each(myDomains, function (i, item) { items.push(Controller.api.buildDomainRadio({ domain: item.domain, type: 'my', checked: defaultType === 'my' && item.domain === defaultDomain, nsText: item.ns_status_text || '', dnsText: item.dns_status_text || '', nsStatus: item.ns_status || '', dnsStatus: item.dns_status || '', showStatus: true })); }); myDomainsHtml = '
' + items.join('') + '
'; } else { myDomainsHtml = '

' + __('Split my domain empty') + '

'; } var initialUrl = Controller.api.buildSplitUrl(defaultDomain, linkCode); var safeLinkCode = Fast.api.escape(linkCode); var html = [ '' ].join(''); Layer.open({ type: 1, title: __('Copy split link'), area: ['780px', 'auto'], maxmin: false, resize: false, shadeClose: true, content: html, success: function (layero, index) { var $box = layero.find('.split-link-copy-modal'); var refreshGeneratedUrl = function () { var $checked = $box.find('input[name="split_main_domain"]:checked'); var domain = $.trim($checked.val() || ''); var url = Controller.api.buildSplitUrl(domain, linkCode); $box.find('.split-generated-url').val(url); $box.find('.btn-open-test').prop('disabled', url === ''); $box.find('.split-domain-item').removeClass('is-checked'); $checked.closest('.split-domain-item').addClass('is-checked'); }; var ensureTabSelection = function ($pane) { var $radios = $pane.find('input[name="split_main_domain"]'); if ($radios.length && !$radios.filter(':checked').length) { $radios.first().prop('checked', true); } }; $box.on('change', 'input[name="split_main_domain"]', refreshGeneratedUrl); $box.on('shown.bs.tab', 'a[data-toggle="tab"]', function (e) { var target = $(e.target).attr('href'); if (target) { ensureTabSelection($box.find(target)); refreshGeneratedUrl(); } }); $box.on('click', '.btn-copy-link-code', function (e) { e.preventDefault(); Controller.api.copyText(linkCode); }); $box.on('click', '.btn-manage-domain, .btn-go-add-domain', function (e) { e.preventDefault(); Layer.close(index); Backend.api.addtabs(Fast.api.fixurl(domainIndexUrl), __('Manage my domains'), 'fa fa-globe'); }); $box.on('click', '.btn-goto-config', function (e) { e.preventDefault(); Layer.close(index); Backend.api.addtabs(Fast.api.fixurl(configIndexUrl), __('System config'), 'fa fa-cogs'); }); $box.on('click', '.btn-copy-generated-url', function (e) { e.preventDefault(); var url = $.trim($box.find('.split-generated-url').val()); if (!url) { Toastr.error(__('Split url empty')); return; } Controller.api.copyText(url); }); $box.on('click', '.btn-open-test', function (e) { e.preventDefault(); var url = $.trim($box.find('.split-generated-url').val()); if (url) { window.open(url, '_blank'); } }); $box.on('click', '.btn-close-copy-modal', function (e) { e.preventDefault(); Layer.close(index); }); ensureTabSelection($box.find('.tab-pane.active')); refreshGeneratedUrl(); } }); }, buildDomainRadio: function (options) { var domain = Fast.api.escape(options.domain || ''); var type = options.type || 'my'; var checked = options.checked ? ' checked' : ''; var checkedClass = options.checked ? ' is-checked' : ''; var statusHtml = ''; if (options.showStatus) { statusHtml = '' + '' + __('NS') + ':' + Fast.api.escape(options.nsText || '-') + '' + '' + __('DNS') + ':' + Fast.api.escape(options.dnsText || '-') + '' + ''; } return '
' + '' + '
'; }, statusLabelClass: function (status, isDns) { if (isDns) { if (status === 'created') { return 'label-success'; } if (status === 'failed') { return 'label-danger'; } return 'label-warning'; } if (status === 'verified') { return 'label-success'; } if (status === 'failed') { return 'label-danger'; } return 'label-warning'; }, buildSplitUrl: function (domain, linkCode) { domain = $.trim(domain || '').replace(/^https?:\/\//i, '').replace(/\/+$/, ''); linkCode = $.trim(linkCode || ''); if (!domain || !linkCode) { return ''; } return 'https://' + domain + '/s/' + linkCode; }, copyText: function (text) { if (navigator.clipboard && window.isSecureContext) { navigator.clipboard.writeText(text).then(function () { Toastr.success(__('Copy success')); }).catch(function () { Controller.api.copyTextFallback(text); }); return; } Controller.api.copyTextFallback(text); }, copyTextFallback: function (text) { var $temp = $('