2026-06-04 14:15:12 +08:00
|
|
|
|
define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) {
|
|
|
|
|
|
|
|
|
|
|
|
var Controller = {
|
|
|
|
|
|
index: function () {
|
|
|
|
|
|
Table.api.init({
|
|
|
|
|
|
extend: {
|
|
|
|
|
|
index_url: 'split.number/index' + location.search,
|
|
|
|
|
|
add_url: 'split.number/add',
|
|
|
|
|
|
edit_url: 'split.number/edit',
|
|
|
|
|
|
del_url: 'split.number/del',
|
|
|
|
|
|
multi_url: 'split.number/multi',
|
|
|
|
|
|
table: 'split_number',
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
var table = $("#table");
|
2026-06-20 04:47:34 +08:00
|
|
|
|
var searchSelect = Controller.api.searchSelectColumn;
|
|
|
|
|
|
var searchSelectMeta = Controller.api.searchSelectMeta;
|
2026-06-04 14:15:12 +08:00
|
|
|
|
|
|
|
|
|
|
table.bootstrapTable({
|
|
|
|
|
|
url: $.fn.bootstrapTable.defaults.extend.index_url,
|
|
|
|
|
|
pk: 'id',
|
|
|
|
|
|
sortName: 'id',
|
|
|
|
|
|
sortOrder: 'desc',
|
|
|
|
|
|
fixedColumns: true,
|
|
|
|
|
|
fixedRightNumber: 1,
|
2026-06-20 04:47:34 +08:00
|
|
|
|
searchFormVisible: true,
|
2026-07-05 23:40:13 +08:00
|
|
|
|
pageSize: 10,
|
|
|
|
|
|
pageList: Controller.api.streamLoad.PAGE_LIST,
|
|
|
|
|
|
queryParams: function (params) {
|
|
|
|
|
|
return Controller.api.streamLoad.wrapQueryParams(table, params);
|
|
|
|
|
|
},
|
2026-06-04 14:15:12 +08:00
|
|
|
|
columns: [
|
|
|
|
|
|
[
|
|
|
|
|
|
{checkbox: true},
|
2026-06-20 04:47:34 +08:00
|
|
|
|
searchSelect('split_link_id', __('Link_url'), Config.splitLinkFilterList || {}, true),
|
2026-06-04 14:15:12 +08:00
|
|
|
|
{field: 'id', title: __('Id'), sortable: true},
|
|
|
|
|
|
{
|
|
|
|
|
|
field: 'link_url_text',
|
|
|
|
|
|
title: __('Link_url'),
|
|
|
|
|
|
operate: false,
|
|
|
|
|
|
formatter: Table.api.formatter.content
|
|
|
|
|
|
},
|
|
|
|
|
|
{field: 'ticket_name', title: __('Ticket_name'), operate: 'LIKE'},
|
2026-07-04 22:09:39 +08:00
|
|
|
|
{
|
|
|
|
|
|
field: 'number',
|
|
|
|
|
|
title: __('Number'),
|
|
|
|
|
|
operate: 'LIKE',
|
|
|
|
|
|
formatter: Controller.api.formatter.numberWithDeferred
|
|
|
|
|
|
},
|
2026-06-20 04:47:34 +08:00
|
|
|
|
$.extend(
|
|
|
|
|
|
{field: 'number_type', title: __('Number_type'), searchList: Config.numberTypeList, formatter: Table.api.formatter.normal},
|
|
|
|
|
|
searchSelectMeta(false)
|
|
|
|
|
|
),
|
2026-06-04 14:15:12 +08:00
|
|
|
|
{field: 'visit_count', title: __('Visit_count'), operate: false, sortable: true},
|
|
|
|
|
|
{field: 'inbound_count', title: __('Inbound_count'), operate: false, sortable: true},
|
2026-06-20 04:47:34 +08:00
|
|
|
|
$.extend(
|
|
|
|
|
|
{field: 'platform_status', title: __('Platform_status'), searchList: Config.platformStatusList, formatter: Controller.api.formatter.platformStatus},
|
|
|
|
|
|
searchSelectMeta(false)
|
|
|
|
|
|
),
|
|
|
|
|
|
$.extend(
|
|
|
|
|
|
{field: 'status', title: __('Status'), searchList: Config.statusList, formatter: Table.api.formatter.toggle, yes: 'normal', no: 'hidden'},
|
|
|
|
|
|
searchSelectMeta(false)
|
|
|
|
|
|
),
|
2026-07-04 22:09:39 +08:00
|
|
|
|
$.extend(
|
|
|
|
|
|
{
|
|
|
|
|
|
field: 'ratio_deferred',
|
|
|
|
|
|
title: __('Ratio_deferred'),
|
|
|
|
|
|
visible: false,
|
|
|
|
|
|
searchable: true,
|
|
|
|
|
|
operate: '=',
|
2026-07-05 02:13:06 +08:00
|
|
|
|
searchList: Config.ratioDeferredList || {}
|
2026-07-04 22:09:39 +08:00
|
|
|
|
},
|
|
|
|
|
|
searchSelectMeta(false)
|
|
|
|
|
|
),
|
2026-06-04 14:15:12 +08:00
|
|
|
|
{
|
|
|
|
|
|
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,
|
|
|
|
|
|
formatter: Table.api.formatter.operate
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
]
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
table.on('check.bs.table uncheck.bs.table check-all.bs.table uncheck-all.bs.table', function () {
|
|
|
|
|
|
var ids = Table.api.selectedids(table);
|
|
|
|
|
|
$('.btn-batch-update-status').toggleClass('btn-disabled disabled', ids.length === 0);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
$('.btn-batch-update-status').on('click', function (e) {
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
|
if ($(this).hasClass('disabled')) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
var ids = Table.api.selectedids(table);
|
|
|
|
|
|
if (!ids.length) {
|
|
|
|
|
|
Toastr.warning(__('Please select at least one item'));
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
Controller.api.openBatchUpdateModal(ids, table);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-06-20 04:47:34 +08:00
|
|
|
|
$('.btn-batch-operate').on('click', function (e) {
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
|
Controller.api.openBatchOperateModal(table);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-07-04 22:09:39 +08:00
|
|
|
|
table.on('post-body.bs.table', function () {
|
|
|
|
|
|
table.find('[data-toggle="tooltip"]').tooltip({container: 'body'});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-06-20 04:47:34 +08:00
|
|
|
|
table.on('post-common-search.bs.table', function (e, tbl) {
|
|
|
|
|
|
Controller.api.initCommonSearchSelectpicker(tbl);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-06-04 14:15:12 +08:00
|
|
|
|
Table.api.bindevent(table);
|
2026-07-05 23:40:13 +08:00
|
|
|
|
Controller.api.streamLoad.bind(table);
|
2026-07-04 22:09:39 +08:00
|
|
|
|
Controller.api.bindDeferredFilter(table);
|
|
|
|
|
|
Controller.api.bindDeferredCleanup(table);
|
2026-06-04 14:15:12 +08:00
|
|
|
|
},
|
|
|
|
|
|
add: function () {
|
|
|
|
|
|
Controller.api.bindevent();
|
|
|
|
|
|
},
|
|
|
|
|
|
edit: function () {
|
|
|
|
|
|
Controller.api.bindevent();
|
|
|
|
|
|
},
|
|
|
|
|
|
api: {
|
2026-06-20 04:47:34 +08:00
|
|
|
|
searchSelectColumn: function (field, title, searchList, liveSearch) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
field: field,
|
|
|
|
|
|
title: title,
|
|
|
|
|
|
visible: false,
|
|
|
|
|
|
searchList: searchList || {},
|
|
|
|
|
|
operate: '=',
|
|
|
|
|
|
addclass: 'selectpicker',
|
|
|
|
|
|
extend: Controller.api.searchSelectExtend(liveSearch !== false)
|
|
|
|
|
|
};
|
|
|
|
|
|
},
|
|
|
|
|
|
searchSelectMeta: function (liveSearch) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
addclass: 'selectpicker',
|
|
|
|
|
|
extend: Controller.api.searchSelectExtend(liveSearch !== false)
|
|
|
|
|
|
};
|
|
|
|
|
|
},
|
|
|
|
|
|
searchSelectExtend: function (liveSearch) {
|
|
|
|
|
|
var text = __('Please select');
|
|
|
|
|
|
if (!text || text === 'Please select' || text === 'Please Select') {
|
|
|
|
|
|
text = '请选择';
|
|
|
|
|
|
}
|
|
|
|
|
|
var ext = 'data-none-selected-text="' + text + '" title="' + text + '"';
|
|
|
|
|
|
if (liveSearch) {
|
|
|
|
|
|
ext += ' data-live-search="true"';
|
|
|
|
|
|
}
|
|
|
|
|
|
return ext;
|
|
|
|
|
|
},
|
|
|
|
|
|
initCommonSearchSelectpicker: function (tbl) {
|
|
|
|
|
|
var $form = $('form.form-commonsearch', tbl.$container);
|
|
|
|
|
|
if ($form.length) {
|
|
|
|
|
|
Form.events.selectpicker($form);
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2026-07-05 23:40:13 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 分页选「全部」时改为分块滚动加载,避免一次渲染过多 DOM 导致卡顿
|
|
|
|
|
|
*/
|
|
|
|
|
|
streamLoad: {
|
|
|
|
|
|
CHUNK: 50,
|
|
|
|
|
|
SCROLL_THRESHOLD: 80,
|
|
|
|
|
|
LOAD_DELAY: 1500,
|
|
|
|
|
|
PAGE_LIST: [10, 30, 50, 100, 200, 300, 500, 'All'],
|
|
|
|
|
|
|
|
|
|
|
|
createState: function () {
|
|
|
|
|
|
return {
|
|
|
|
|
|
enabled: false,
|
|
|
|
|
|
loading: false,
|
|
|
|
|
|
pendingLoad: false,
|
|
|
|
|
|
loadTimer: null,
|
|
|
|
|
|
loaded: 0,
|
|
|
|
|
|
total: 0,
|
|
|
|
|
|
chunk: 50,
|
|
|
|
|
|
pendingReset: false,
|
|
|
|
|
|
pendingAllSelect: false,
|
|
|
|
|
|
appendQuery: false
|
|
|
|
|
|
};
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
getState: function (table) {
|
|
|
|
|
|
var state = table.data('splitStreamLoad');
|
|
|
|
|
|
if (!state) {
|
|
|
|
|
|
state = Controller.api.streamLoad.createState();
|
|
|
|
|
|
table.data('splitStreamLoad', state);
|
|
|
|
|
|
}
|
|
|
|
|
|
return state;
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
getAllLabel: function () {
|
|
|
|
|
|
var localeKey = (typeof Config !== 'undefined' && Config.language === 'zh-cn') ? 'zh-CN' : 'en-US';
|
|
|
|
|
|
var locale = $.fn.bootstrapTable.locales && $.fn.bootstrapTable.locales[localeKey];
|
|
|
|
|
|
if (locale && typeof locale.formatAllRows === 'function') {
|
|
|
|
|
|
return locale.formatAllRows();
|
|
|
|
|
|
}
|
|
|
|
|
|
if ($.fn.bootstrapTable.defaults && typeof $.fn.bootstrapTable.defaults.formatAllRows === 'function') {
|
|
|
|
|
|
return $.fn.bootstrapTable.defaults.formatAllRows();
|
|
|
|
|
|
}
|
|
|
|
|
|
return 'All';
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* initServer 首次请求时实例尚未写入 data('bootstrap.table'),getOptions 会退回 jQuery 对象
|
|
|
|
|
|
*/
|
|
|
|
|
|
safeGetOptions: function (table) {
|
|
|
|
|
|
if (!table || !table.length) {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
var opts = table.bootstrapTable('getOptions');
|
|
|
|
|
|
if (opts && typeof opts.formatAllRows === 'function') {
|
|
|
|
|
|
return opts;
|
|
|
|
|
|
}
|
|
|
|
|
|
var instance = table.data('bootstrap.table');
|
|
|
|
|
|
if (instance && instance.options && typeof instance.options.formatAllRows === 'function') {
|
|
|
|
|
|
return instance.options;
|
|
|
|
|
|
}
|
|
|
|
|
|
return null;
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
getDefaultPageList: function () {
|
|
|
|
|
|
return Controller.api.streamLoad.PAGE_LIST;
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
getMaxNormalPageSize: function (pageList) {
|
|
|
|
|
|
var api = Controller.api.streamLoad;
|
|
|
|
|
|
var maxNormal = 0;
|
|
|
|
|
|
$.each(pageList || api.getDefaultPageList(), function (i, value) {
|
|
|
|
|
|
if (api.isAllPageSize(value)) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
var num = parseInt(value, 10);
|
|
|
|
|
|
if (!isNaN(num) && num > maxNormal) {
|
|
|
|
|
|
maxNormal = num;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
return maxNormal;
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
isAllPageSize: function (pageSize) {
|
|
|
|
|
|
if (pageSize === undefined || pageSize === null || pageSize === '') {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return String(pageSize).toUpperCase() === String(Controller.api.streamLoad.getAllLabel()).toUpperCase();
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
isLargeStoredPageSize: function (table, params) {
|
|
|
|
|
|
var api = Controller.api.streamLoad;
|
|
|
|
|
|
var options = api.safeGetOptions(table);
|
|
|
|
|
|
var pageSize = options ? options.pageSize : null;
|
|
|
|
|
|
var limit = params ? (parseInt(params.limit, 10) || 0) : 0;
|
|
|
|
|
|
|
|
|
|
|
|
if (pageSize !== null && api.isAllPageSize(pageSize)) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
var maxNormal = api.getMaxNormalPageSize(options ? options.pageList : null);
|
|
|
|
|
|
if (typeof pageSize === 'number' && pageSize > maxNormal) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!options && limit > maxNormal) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
shouldEnable: function (table, params) {
|
|
|
|
|
|
var api = Controller.api.streamLoad;
|
|
|
|
|
|
var state = api.getState(table);
|
|
|
|
|
|
if (state.pendingAllSelect || state.enabled) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
var options = api.safeGetOptions(table);
|
|
|
|
|
|
var limit = parseInt(params.limit, 10) || 0;
|
|
|
|
|
|
|
|
|
|
|
|
if (options) {
|
|
|
|
|
|
var totalRows = parseInt(options.totalRows, 10) || 0;
|
|
|
|
|
|
if (api.isAllPageSize(options.pageSize)) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (api.isLargeStoredPageSize(table, params)) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
return totalRows > 0 && limit >= totalRows;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 表格构造阶段:All 模式 totalRows 尚未就绪时 limit 可能为 0
|
|
|
|
|
|
if (limit === 0) {
|
|
|
|
|
|
var stored = localStorage.getItem('pagesize');
|
|
|
|
|
|
if (stored && api.isAllPageSize(stored)) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return api.isLargeStoredPageSize(table, params);
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
enable: function (table) {
|
|
|
|
|
|
var state = Controller.api.streamLoad.getState(table);
|
|
|
|
|
|
state.enabled = true;
|
|
|
|
|
|
state.loaded = 0;
|
|
|
|
|
|
state.loading = false;
|
|
|
|
|
|
state.pendingReset = true;
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
disable: function (table) {
|
|
|
|
|
|
var api = Controller.api.streamLoad;
|
|
|
|
|
|
var state = api.getState(table);
|
|
|
|
|
|
api.cancelPendingLoad(table);
|
|
|
|
|
|
state.enabled = false;
|
|
|
|
|
|
state.loaded = 0;
|
|
|
|
|
|
state.total = 0;
|
|
|
|
|
|
state.loading = false;
|
|
|
|
|
|
state.pendingReset = false;
|
|
|
|
|
|
state.pendingAllSelect = false;
|
|
|
|
|
|
api.updateHint(table);
|
|
|
|
|
|
api.togglePaginationNav(table, false);
|
|
|
|
|
|
api.showBottomLoading(table, false);
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
wrapQueryParams: function (table, params) {
|
|
|
|
|
|
var api = Controller.api.streamLoad;
|
|
|
|
|
|
var state = api.getState(table);
|
|
|
|
|
|
|
|
|
|
|
|
if (api.shouldEnable(table, params)) {
|
|
|
|
|
|
state.enabled = true;
|
|
|
|
|
|
state.pendingAllSelect = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!state.enabled) {
|
|
|
|
|
|
return params;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
params.limit = state.chunk;
|
|
|
|
|
|
if (state.appendQuery) {
|
|
|
|
|
|
params.offset = parseInt(params.offset, 10) || 0;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
params.offset = 0;
|
|
|
|
|
|
state.pendingReset = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
state.appendQuery = false;
|
|
|
|
|
|
return params;
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
bind: function (table) {
|
|
|
|
|
|
var api = Controller.api.streamLoad;
|
|
|
|
|
|
var $wrapper = table.closest('.bootstrap-table');
|
|
|
|
|
|
|
|
|
|
|
|
$wrapper.on('click.splitStream', '.page-list .dropdown-menu li a', function () {
|
|
|
|
|
|
api.cancelPendingLoad(table);
|
|
|
|
|
|
var label = $.trim($(this).text());
|
|
|
|
|
|
if (api.isAllPageSize(label)) {
|
|
|
|
|
|
api.getState(table).pendingAllSelect = true;
|
|
|
|
|
|
api.enable(table);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
table.on('page-change.bs.table', function (e, pageNumber, pageSize) {
|
|
|
|
|
|
var options = api.safeGetOptions(table);
|
|
|
|
|
|
if (!options) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (api.isAllPageSize(pageSize)
|
|
|
|
|
|
|| (typeof pageSize === 'number' && pageSize === options.totalRows && options.totalRows > 0)) {
|
|
|
|
|
|
api.enable(table);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (typeof pageSize === 'number' && pageSize < options.totalRows) {
|
|
|
|
|
|
api.disable(table);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
table.on('refresh.bs.table search.bs.table common-search.bs.table', function () {
|
|
|
|
|
|
var state = api.getState(table);
|
|
|
|
|
|
api.cancelPendingLoad(table);
|
|
|
|
|
|
if (!state.enabled) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
state.loaded = 0;
|
|
|
|
|
|
state.pendingReset = true;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
table.on('sort.bs.table', function () {
|
|
|
|
|
|
var state = api.getState(table);
|
|
|
|
|
|
api.cancelPendingLoad(table);
|
|
|
|
|
|
if (!state.enabled) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
state.loaded = 0;
|
|
|
|
|
|
state.pendingReset = true;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
table.on('load-success.bs.table', function (e, data) {
|
|
|
|
|
|
var state = api.getState(table);
|
|
|
|
|
|
if (!state.enabled || !data || typeof data.total === 'undefined') {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
state.total = parseInt(data.total, 10) || 0;
|
|
|
|
|
|
if (state.pendingReset) {
|
|
|
|
|
|
state.loaded = $.isArray(data.rows) ? data.rows.length : 0;
|
|
|
|
|
|
state.pendingReset = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
api.updateHint(table);
|
|
|
|
|
|
api.togglePaginationNav(table, true);
|
|
|
|
|
|
api.bindScrollContainer(table);
|
|
|
|
|
|
api.autoFillViewport(table);
|
|
|
|
|
|
|
|
|
|
|
|
var tableOptions = api.safeGetOptions(table);
|
|
|
|
|
|
if (tableOptions && tableOptions.fixedColumns) {
|
|
|
|
|
|
table.bootstrapTable('resetView');
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
table.on('post-body.bs.table', function () {
|
|
|
|
|
|
if (!api.getState(table).enabled) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
api.updateHint(table);
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
bindScrollContainer: function (table) {
|
|
|
|
|
|
var api = Controller.api.streamLoad;
|
|
|
|
|
|
var $body = table.closest('.bootstrap-table').find('.fixed-table-body');
|
|
|
|
|
|
if (!$body.length || $body.data('splitStreamBound')) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
$body.data('splitStreamBound', true);
|
|
|
|
|
|
$body.on('scroll.splitStream', function () {
|
|
|
|
|
|
api.onScroll(table, this);
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
onScroll: function (table, el) {
|
|
|
|
|
|
var api = Controller.api.streamLoad;
|
|
|
|
|
|
var state = api.getState(table);
|
|
|
|
|
|
if (!state.enabled || state.loading || state.loaded >= state.total) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
var atBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - api.SCROLL_THRESHOLD;
|
|
|
|
|
|
if (!atBottom) {
|
|
|
|
|
|
api.cancelPendingLoad(table);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (state.pendingLoad) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
api.scheduleLoadMore(table);
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
isNearBottom: function (table) {
|
|
|
|
|
|
var api = Controller.api.streamLoad;
|
|
|
|
|
|
var $body = table.closest('.bootstrap-table').find('.fixed-table-body');
|
|
|
|
|
|
if (!$body.length) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
var el = $body[0];
|
|
|
|
|
|
return el.scrollTop + el.clientHeight >= el.scrollHeight - api.SCROLL_THRESHOLD;
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
scheduleLoadMore: function (table) {
|
|
|
|
|
|
var api = Controller.api.streamLoad;
|
|
|
|
|
|
var state = api.getState(table);
|
|
|
|
|
|
if (state.loading || state.pendingLoad || !state.enabled || state.loaded >= state.total) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
state.pendingLoad = true;
|
|
|
|
|
|
api.showBottomLoading(table, true);
|
|
|
|
|
|
api.updateHint(table, false, true);
|
|
|
|
|
|
|
|
|
|
|
|
state.loadTimer = setTimeout(function () {
|
|
|
|
|
|
state.pendingLoad = false;
|
|
|
|
|
|
state.loadTimer = null;
|
|
|
|
|
|
if (!state.enabled) {
|
|
|
|
|
|
api.showBottomLoading(table, false);
|
|
|
|
|
|
api.updateHint(table);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!api.isNearBottom(table)) {
|
|
|
|
|
|
api.showBottomLoading(table, false);
|
|
|
|
|
|
api.updateHint(table);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
api.loadMore(table);
|
|
|
|
|
|
}, api.LOAD_DELAY);
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
cancelPendingLoad: function (table) {
|
|
|
|
|
|
var api = Controller.api.streamLoad;
|
|
|
|
|
|
var state = api.getState(table);
|
|
|
|
|
|
if (state.loadTimer) {
|
|
|
|
|
|
clearTimeout(state.loadTimer);
|
|
|
|
|
|
state.loadTimer = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
state.pendingLoad = false;
|
|
|
|
|
|
if (!state.loading) {
|
|
|
|
|
|
api.showBottomLoading(table, false);
|
|
|
|
|
|
api.updateHint(table);
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
showBottomLoading: function (table, show) {
|
|
|
|
|
|
var $body = table.closest('.bootstrap-table').find('.fixed-table-body');
|
|
|
|
|
|
if (!$body.length) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
var $bar = $body.children('.split-stream-loading-bar');
|
|
|
|
|
|
if (show) {
|
|
|
|
|
|
if (!$bar.length) {
|
|
|
|
|
|
var loadingText = __('Stream loading bottom');
|
|
|
|
|
|
if (!loadingText || loadingText === 'Stream loading bottom') {
|
|
|
|
|
|
loadingText = '加载中...';
|
|
|
|
|
|
}
|
|
|
|
|
|
$bar = $('<div class="split-stream-loading-bar text-center" style="padding:14px 0;color:#999;border-top:1px solid #f0f0f0;background:#fafafa;">'
|
|
|
|
|
|
+ '<i class="fa fa-spinner fa-spin"></i> ' + Fast.api.escape(loadingText)
|
|
|
|
|
|
+ '</div>');
|
|
|
|
|
|
$body.append($bar);
|
|
|
|
|
|
}
|
|
|
|
|
|
$bar.show();
|
|
|
|
|
|
} else if ($bar.length) {
|
|
|
|
|
|
$bar.hide();
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
buildRequestParams: function (table, offset, limit) {
|
|
|
|
|
|
var options = Controller.api.streamLoad.safeGetOptions(table);
|
|
|
|
|
|
if (!options) {
|
|
|
|
|
|
return {offset: offset, limit: limit};
|
|
|
|
|
|
}
|
|
|
|
|
|
var state = Controller.api.streamLoad.getState(table);
|
|
|
|
|
|
state.appendQuery = true;
|
|
|
|
|
|
return options.queryParams({
|
|
|
|
|
|
search: options.searchText,
|
|
|
|
|
|
sort: options.sortName,
|
|
|
|
|
|
order: options.sortOrder,
|
|
|
|
|
|
offset: offset,
|
|
|
|
|
|
limit: limit
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
loadMore: function (table) {
|
|
|
|
|
|
var api = Controller.api.streamLoad;
|
|
|
|
|
|
var state = api.getState(table);
|
|
|
|
|
|
if (state.loading || !state.enabled || state.loaded >= state.total) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
state.loading = true;
|
|
|
|
|
|
api.updateHint(table, true);
|
|
|
|
|
|
|
|
|
|
|
|
var options = api.safeGetOptions(table);
|
|
|
|
|
|
if (!options) {
|
|
|
|
|
|
state.loading = false;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
$.ajax({
|
|
|
|
|
|
url: options.url,
|
|
|
|
|
|
type: options.method,
|
|
|
|
|
|
data: api.buildRequestParams(table, state.loaded, state.chunk),
|
|
|
|
|
|
dataType: 'json'
|
|
|
|
|
|
}).done(function (res) {
|
|
|
|
|
|
state.loading = false;
|
|
|
|
|
|
api.showBottomLoading(table, false);
|
|
|
|
|
|
if (!res || typeof res.rows === 'undefined') {
|
|
|
|
|
|
Toastr.error(__('Unknown data format'));
|
|
|
|
|
|
api.updateHint(table);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var rows = res.rows || [];
|
|
|
|
|
|
if (rows.length === 0) {
|
|
|
|
|
|
state.loaded = state.total;
|
|
|
|
|
|
api.updateHint(table);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
table.bootstrapTable('append', rows);
|
|
|
|
|
|
state.loaded += rows.length;
|
|
|
|
|
|
api.updateHint(table);
|
|
|
|
|
|
api.autoFillViewport(table);
|
|
|
|
|
|
|
|
|
|
|
|
if (options.fixedColumns) {
|
|
|
|
|
|
table.bootstrapTable('resetView');
|
|
|
|
|
|
}
|
|
|
|
|
|
}).fail(function () {
|
|
|
|
|
|
state.loading = false;
|
|
|
|
|
|
api.showBottomLoading(table, false);
|
|
|
|
|
|
api.updateHint(table);
|
|
|
|
|
|
Toastr.error(__('Unknown data format'));
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
autoFillViewport: function (table) {
|
|
|
|
|
|
var api = Controller.api.streamLoad;
|
|
|
|
|
|
var state = api.getState(table);
|
|
|
|
|
|
if (!state.enabled || state.loading || state.pendingLoad || state.loaded >= state.total) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
var $body = table.closest('.bootstrap-table').find('.fixed-table-body');
|
|
|
|
|
|
if (!$body.length) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
var el = $body[0];
|
|
|
|
|
|
if (el.scrollHeight <= el.clientHeight + api.SCROLL_THRESHOLD) {
|
|
|
|
|
|
api.scheduleLoadMore(table);
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
togglePaginationNav: function (table, hide) {
|
|
|
|
|
|
table.closest('.bootstrap-table').find('.fixed-table-pagination .pagination').toggle(!hide);
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
getHintText: function (loaded, total, loading, pending) {
|
|
|
|
|
|
if (pending) {
|
|
|
|
|
|
var pendingText = __('Stream loading pending');
|
|
|
|
|
|
if (!pendingText || pendingText === 'Stream loading pending') {
|
|
|
|
|
|
pendingText = '即将加载更多,可切换显示条数取消...';
|
|
|
|
|
|
}
|
|
|
|
|
|
return pendingText;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (loading) {
|
|
|
|
|
|
var loadingText = __('Stream loading');
|
|
|
|
|
|
return loadingText && loadingText !== 'Stream loading' ? loadingText : '加载中...';
|
|
|
|
|
|
}
|
|
|
|
|
|
var template = __('Stream loaded');
|
|
|
|
|
|
if (!template || template === 'Stream loaded') {
|
|
|
|
|
|
template = '已加载 %loaded% / %total%,继续滚动加载更多';
|
|
|
|
|
|
}
|
|
|
|
|
|
return template
|
|
|
|
|
|
.replace('%loaded%', String(loaded))
|
|
|
|
|
|
.replace('%total%', String(total));
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
updateHint: function (table, loading, pending) {
|
|
|
|
|
|
var api = Controller.api.streamLoad;
|
|
|
|
|
|
var state = api.getState(table);
|
|
|
|
|
|
var $wrapper = table.closest('.bootstrap-table');
|
|
|
|
|
|
var $detail = $wrapper.find('.pagination-detail');
|
|
|
|
|
|
|
|
|
|
|
|
if (!state.enabled) {
|
|
|
|
|
|
$wrapper.find('.split-stream-hint').remove();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var $hint = $wrapper.find('.split-stream-hint');
|
|
|
|
|
|
if (!$hint.length) {
|
|
|
|
|
|
$hint = $('<span class="split-stream-hint text-muted" style="margin-left:10px;font-size:12px;"></span>');
|
|
|
|
|
|
$detail.append($hint);
|
|
|
|
|
|
}
|
|
|
|
|
|
var isPending = pending !== undefined ? pending : state.pendingLoad;
|
|
|
|
|
|
var isLoading = loading !== undefined ? loading : state.loading;
|
|
|
|
|
|
$hint.text(api.getHintText(state.loaded, state.total, isLoading, isPending));
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2026-06-09 03:36:30 +08:00
|
|
|
|
formatter: {
|
|
|
|
|
|
platformStatus: function (value, row) {
|
|
|
|
|
|
var text = row.platform_status_text != null && row.platform_status_text !== ''
|
|
|
|
|
|
? String(row.platform_status_text)
|
|
|
|
|
|
: (Config.platformStatusList && Config.platformStatusList[value] ? Config.platformStatusList[value] : value);
|
|
|
|
|
|
var cls = value === 'online' ? 'success' : (value === 'offline' ? 'default' : 'warning');
|
|
|
|
|
|
return '<span class="text-' + cls + '">' + Fast.api.escape(text || '') + '</span>';
|
2026-07-04 22:09:39 +08:00
|
|
|
|
},
|
|
|
|
|
|
isRatioDeferred: function (row) {
|
|
|
|
|
|
return parseInt(row.ratio_deferred, 10) === 1 && String(row.status || '') === 'normal';
|
|
|
|
|
|
},
|
|
|
|
|
|
ratioDeferredTooltip: function (row) {
|
|
|
|
|
|
var streak = parseInt(row.no_inbound_click_streak, 10) || 0;
|
|
|
|
|
|
var template = __('Ratio deferred tooltip');
|
|
|
|
|
|
if (!template || template === 'Ratio deferred tooltip') {
|
|
|
|
|
|
template = '连续 %s 次访问无进线增长,已触线下号比率,选号已后置,等待同步裁决';
|
|
|
|
|
|
}
|
|
|
|
|
|
return template.indexOf('%s') >= 0 ? template.replace('%s', String(streak)) : template;
|
|
|
|
|
|
},
|
|
|
|
|
|
ratioDeferredBadgeHtml: function (tooltip) {
|
|
|
|
|
|
var label = __('Ratio deferred badge');
|
|
|
|
|
|
if (!label || label === 'Ratio deferred badge') {
|
|
|
|
|
|
label = '降权中';
|
|
|
|
|
|
}
|
|
|
|
|
|
return '<span class="label label-warning split-ratio-deferred-badge" style="margin-left:6px;font-size:11px;font-weight:500;vertical-align:middle;"'
|
|
|
|
|
|
+ ' data-toggle="tooltip" data-placement="top" title="' + Fast.api.escape(tooltip) + '">'
|
|
|
|
|
|
+ '<i class="fa fa-level-down"></i> ' + Fast.api.escape(label)
|
|
|
|
|
|
+ '</span>';
|
|
|
|
|
|
},
|
|
|
|
|
|
numberWithDeferred: function (value, row) {
|
|
|
|
|
|
var html = Fast.api.escape(String(value || ''));
|
|
|
|
|
|
if (Controller.api.formatter.isRatioDeferred(row)) {
|
|
|
|
|
|
html += Controller.api.formatter.ratioDeferredBadgeHtml(
|
|
|
|
|
|
Controller.api.formatter.ratioDeferredTooltip(row)
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
return html;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
refreshSearchSelect: function ($field) {
|
|
|
|
|
|
if ($field.length && $field.hasClass('selectpicker') && $field.data('selectpicker')) {
|
|
|
|
|
|
$field.selectpicker('refresh');
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
syncStatusTabs: function (table, statusValue) {
|
|
|
|
|
|
var $tabs = table.closest('.panel-intro').find('.panel-heading [data-field="status"]');
|
|
|
|
|
|
if (!$tabs.length) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
$tabs.find('li').removeClass('active');
|
|
|
|
|
|
$tabs.find('li a[data-value="' + (statusValue === null || statusValue === undefined ? '' : statusValue) + '"]')
|
|
|
|
|
|
.parent()
|
|
|
|
|
|
.addClass('active');
|
|
|
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 工具栏「降权中」快捷筛选:与列表徽章一致(ratio_deferred=1 且 status=normal)
|
|
|
|
|
|
*/
|
|
|
|
|
|
bindDeferredFilter: function (table) {
|
|
|
|
|
|
var active = false;
|
|
|
|
|
|
var savedStatus = '';
|
|
|
|
|
|
var $btn = $('.btn-filter-ratio-deferred');
|
|
|
|
|
|
if (!$btn.length) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var applyFilter = function () {
|
|
|
|
|
|
var $form = table.closest('.bootstrap-table').find('form.form-commonsearch');
|
|
|
|
|
|
if (!$form.length) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
var $ratioField = $form.find('[name="ratio_deferred"]');
|
|
|
|
|
|
var $statusField = $form.find('[name="status"]');
|
|
|
|
|
|
|
|
|
|
|
|
if (active) {
|
|
|
|
|
|
savedStatus = $statusField.length ? String($statusField.val() || '') : '';
|
|
|
|
|
|
if ($ratioField.length) {
|
|
|
|
|
|
$ratioField.val('1');
|
|
|
|
|
|
Controller.api.refreshSearchSelect($ratioField);
|
|
|
|
|
|
}
|
|
|
|
|
|
if ($statusField.length) {
|
|
|
|
|
|
$statusField.val('normal');
|
|
|
|
|
|
Controller.api.refreshSearchSelect($statusField);
|
|
|
|
|
|
}
|
|
|
|
|
|
Controller.api.syncStatusTabs(table, 'normal');
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if ($ratioField.length) {
|
|
|
|
|
|
$ratioField.val('');
|
|
|
|
|
|
Controller.api.refreshSearchSelect($ratioField);
|
|
|
|
|
|
}
|
|
|
|
|
|
if ($statusField.length) {
|
|
|
|
|
|
$statusField.val(savedStatus);
|
|
|
|
|
|
Controller.api.refreshSearchSelect($statusField);
|
|
|
|
|
|
}
|
|
|
|
|
|
Controller.api.syncStatusTabs(table, savedStatus);
|
|
|
|
|
|
savedStatus = '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
table.trigger('uncheckbox');
|
|
|
|
|
|
$form.trigger('submit');
|
|
|
|
|
|
return true;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
$btn.on('click', function (e) {
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
active = !active;
|
|
|
|
|
|
$btn.toggleClass('btn-warning', active).toggleClass('btn-default', !active);
|
|
|
|
|
|
if (!applyFilter()) {
|
|
|
|
|
|
active = !active;
|
|
|
|
|
|
$btn.toggleClass('btn-warning', active).toggleClass('btn-default', !active);
|
|
|
|
|
|
Toastr.warning('筛选表单未就绪,请刷新页面后重试');
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 清理无效降权池:无落地页访问基线却标记 ratio_deferred=1 的号码
|
|
|
|
|
|
*/
|
|
|
|
|
|
bindDeferredCleanup: function (table) {
|
|
|
|
|
|
var $btn = $('.btn-cleanup-ratio-deferred');
|
|
|
|
|
|
if (!$btn.length) {
|
|
|
|
|
|
return;
|
2026-06-09 03:36:30 +08:00
|
|
|
|
}
|
2026-07-04 22:09:39 +08:00
|
|
|
|
$btn.on('click', function (e) {
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
Fast.api.ajax({
|
|
|
|
|
|
url: 'split.number/cleanupdeferredpreview',
|
|
|
|
|
|
type: 'get'
|
|
|
|
|
|
}, function (data) {
|
|
|
|
|
|
var count = parseInt(data.count, 10) || 0;
|
|
|
|
|
|
if (count <= 0) {
|
|
|
|
|
|
Toastr.info(__('Ratio deferred cleanup none'));
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
var template = __('Ratio deferred cleanup confirm');
|
|
|
|
|
|
if (!template || template === 'Ratio deferred cleanup confirm') {
|
|
|
|
|
|
template = '将清除 %d 条无效降权记录。确定继续?';
|
|
|
|
|
|
}
|
|
|
|
|
|
var message = template.indexOf('%d') >= 0
|
|
|
|
|
|
? template.replace('%d', String(count))
|
|
|
|
|
|
: template;
|
|
|
|
|
|
Layer.confirm(message, {icon: 3, title: __('Warning')}, function (index) {
|
|
|
|
|
|
Layer.close(index);
|
|
|
|
|
|
Fast.api.ajax({
|
|
|
|
|
|
url: 'split.number/cleanupdeferred',
|
|
|
|
|
|
type: 'post'
|
|
|
|
|
|
}, function (data, ret) {
|
|
|
|
|
|
table.bootstrapTable('refresh');
|
|
|
|
|
|
Toastr.success(ret.msg || __('Ratio deferred cleanup success'));
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
return false;
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
2026-06-09 03:36:30 +08:00
|
|
|
|
},
|
2026-06-04 14:15:12 +08:00
|
|
|
|
bindevent: function () {
|
|
|
|
|
|
Form.api.bindevent($('form[role=form]'));
|
|
|
|
|
|
Controller.api.fixSelectPlaceholder();
|
|
|
|
|
|
Controller.api.bindNumberTypeToggle();
|
|
|
|
|
|
},
|
|
|
|
|
|
fixSelectPlaceholder: function () {
|
|
|
|
|
|
var text = __('Please select');
|
|
|
|
|
|
if (!text || text === 'Please select' || text === 'Please Select') {
|
|
|
|
|
|
text = '请选择';
|
|
|
|
|
|
}
|
|
|
|
|
|
$('#c-split_link_id').each(function () {
|
|
|
|
|
|
var $el = $(this);
|
|
|
|
|
|
$el.attr({'data-none-selected-text': text, 'title': text});
|
|
|
|
|
|
$el.find('option[value=""]').first().text(text);
|
|
|
|
|
|
if ($el.data('selectpicker')) {
|
|
|
|
|
|
$el.selectpicker('render');
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
bindNumberTypeToggle: function () {
|
|
|
|
|
|
var $type = $('#c-number_type');
|
|
|
|
|
|
var $wrap = $('.split-number-type-custom');
|
|
|
|
|
|
var $custom = $('#c-number_type_custom');
|
|
|
|
|
|
if (!$type.length) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
var toggle = function () {
|
|
|
|
|
|
var val = $type.val();
|
|
|
|
|
|
if (val === 'custom') {
|
|
|
|
|
|
$wrap.removeClass('hide');
|
|
|
|
|
|
$custom.attr('data-rule', 'required');
|
|
|
|
|
|
} else {
|
|
|
|
|
|
$wrap.addClass('hide');
|
|
|
|
|
|
$custom.removeAttr('data-rule');
|
|
|
|
|
|
if (!$wrap.closest('form').attr('id') || $('#edit-form').length === 0) {
|
|
|
|
|
|
$custom.val('');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
$type.on('changed.bs.select change', toggle);
|
|
|
|
|
|
toggle();
|
|
|
|
|
|
},
|
|
|
|
|
|
openBatchUpdateModal: function (ids, table) {
|
|
|
|
|
|
var statusHtml = '';
|
|
|
|
|
|
$.each(Config.statusList || {}, function (key, label) {
|
|
|
|
|
|
var checked = key === 'normal' ? ' checked' : '';
|
|
|
|
|
|
statusHtml += '<label class="radio-inline"><input type="radio" name="batch_status" value="' + key + '"' + checked + '> ' + label + '</label>';
|
|
|
|
|
|
});
|
|
|
|
|
|
var manualHtml = '';
|
|
|
|
|
|
$.each(Config.manualManageList || {}, function (key, label) {
|
|
|
|
|
|
var checked = key === '0' ? ' checked' : '';
|
|
|
|
|
|
manualHtml += '<label class="radio-inline"><input type="radio" name="batch_manual_manage" value="' + key + '"' + checked + '> ' + label + '</label>';
|
|
|
|
|
|
});
|
|
|
|
|
|
var html = [
|
|
|
|
|
|
'<div class="split-batch-update-modal" style="padding:18px 22px;">',
|
|
|
|
|
|
' <div class="form-group">',
|
|
|
|
|
|
' <label class="control-label">' + __('Batch selected count') + '</label>',
|
|
|
|
|
|
' <p class="form-control-static" style="font-size:18px;font-weight:600;margin:0;">' + ids.length + ' ' + __('Unit count') + '</p>',
|
|
|
|
|
|
' </div>',
|
|
|
|
|
|
' <div class="form-group">',
|
|
|
|
|
|
' <label class="control-label">' + __('Status') + '</label>',
|
|
|
|
|
|
' <div>' + statusHtml + '</div>',
|
|
|
|
|
|
' </div>',
|
|
|
|
|
|
' <div class="form-group" style="margin-bottom:0;">',
|
|
|
|
|
|
' <label class="control-label">' + __('Manual_manage') + '</label>',
|
|
|
|
|
|
' <div>' + manualHtml + '</div>',
|
|
|
|
|
|
' </div>',
|
|
|
|
|
|
'</div>'
|
|
|
|
|
|
].join('');
|
|
|
|
|
|
|
|
|
|
|
|
Layer.open({
|
|
|
|
|
|
type: 1,
|
|
|
|
|
|
title: __('Batch update title'),
|
|
|
|
|
|
area: ['420px', 'auto'],
|
|
|
|
|
|
shadeClose: false,
|
|
|
|
|
|
content: html,
|
|
|
|
|
|
btn: [__('OK'), __('Cancel')],
|
|
|
|
|
|
yes: function (index, layero) {
|
|
|
|
|
|
var status = layero.find('input[name="batch_status"]:checked').val();
|
|
|
|
|
|
var manualManage = layero.find('input[name="batch_manual_manage"]:checked').val();
|
|
|
|
|
|
if (typeof status === 'undefined' || typeof manualManage === 'undefined') {
|
|
|
|
|
|
Toastr.error(__('Invalid parameters'));
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
Fast.api.ajax({
|
|
|
|
|
|
url: 'split.number/batchupdate',
|
|
|
|
|
|
type: 'post',
|
|
|
|
|
|
data: {
|
|
|
|
|
|
ids: ids.join(','),
|
|
|
|
|
|
status: status,
|
|
|
|
|
|
manual_manage: manualManage
|
|
|
|
|
|
}
|
|
|
|
|
|
}, function () {
|
|
|
|
|
|
Layer.close(index);
|
|
|
|
|
|
table.bootstrapTable('refresh');
|
|
|
|
|
|
$('.btn-batch-update-status').addClass('btn-disabled disabled');
|
|
|
|
|
|
Toastr.success(__('Batch update success'));
|
|
|
|
|
|
});
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2026-06-20 04:47:34 +08:00
|
|
|
|
},
|
|
|
|
|
|
openBatchOperateModal: function (table) {
|
|
|
|
|
|
var pleaseSelect = __('Please select');
|
|
|
|
|
|
if (!pleaseSelect || pleaseSelect === 'Please select' || pleaseSelect === 'Please Select') {
|
|
|
|
|
|
pleaseSelect = '请选择';
|
|
|
|
|
|
}
|
|
|
|
|
|
var linkOptions = ['<option value="">' + Fast.api.escape(pleaseSelect) + '</option>'];
|
|
|
|
|
|
$.each(Config.splitLinkSelectList || {}, function (id, label) {
|
|
|
|
|
|
linkOptions.push('<option value="' + Fast.api.escape(String(id)) + '">' + Fast.api.escape(String(label)) + '</option>');
|
|
|
|
|
|
});
|
|
|
|
|
|
var html = [
|
|
|
|
|
|
'<div class="split-batch-operate-modal" style="padding:18px 22px;">',
|
|
|
|
|
|
' <div class="form-group">',
|
|
|
|
|
|
' <label class="control-label">' + __('Link_url') + '<span class="text-danger">*</span></label>',
|
|
|
|
|
|
' <select id="batch-operate-split-link" class="form-control selectpicker" data-live-search="true" data-none-selected-text="' + pleaseSelect + '" title="' + pleaseSelect + '">',
|
|
|
|
|
|
linkOptions.join(''),
|
|
|
|
|
|
' </select>',
|
|
|
|
|
|
' </div>',
|
|
|
|
|
|
' <div class="form-group">',
|
|
|
|
|
|
' <label class="control-label">' + __('Number') + '<span class="text-danger">*</span></label>',
|
|
|
|
|
|
' <textarea id="batch-operate-numbers" class="form-control" rows="8" placeholder="' + Fast.api.escape(__('Numbers one per line')) + '"></textarea>',
|
|
|
|
|
|
' </div>',
|
|
|
|
|
|
' <div class="form-group">',
|
|
|
|
|
|
' <label class="control-label">' + __('Batch operate action') + '<span class="text-danger">*</span></label>',
|
|
|
|
|
|
' <select id="batch-operate-action" class="form-control selectpicker" data-none-selected-text="' + pleaseSelect + '" title="' + pleaseSelect + '">',
|
|
|
|
|
|
' <option value="enable">' + Fast.api.escape(__('Batch operate enable')) + '</option>',
|
|
|
|
|
|
' <option value="disable">' + Fast.api.escape(__('Batch operate disable')) + '</option>',
|
|
|
|
|
|
' <option value="delete">' + Fast.api.escape(__('Batch operate delete')) + '</option>',
|
|
|
|
|
|
' </select>',
|
|
|
|
|
|
' </div>',
|
|
|
|
|
|
' <div class="form-group" style="margin-bottom:0;text-align:right;">',
|
|
|
|
|
|
' <button type="button" class="btn btn-primary btn-batch-operate-confirm">' + __('OK') + '</button> ',
|
|
|
|
|
|
' <button type="button" class="btn btn-default btn-batch-operate-cancel">' + __('Cancel') + '</button>',
|
|
|
|
|
|
' </div>',
|
|
|
|
|
|
'</div>'
|
|
|
|
|
|
].join('');
|
|
|
|
|
|
|
|
|
|
|
|
var layerIndex = Layer.open({
|
|
|
|
|
|
type: 1,
|
|
|
|
|
|
title: __('Batch operate title'),
|
|
|
|
|
|
area: ['520px', 'auto'],
|
|
|
|
|
|
shadeClose: false,
|
|
|
|
|
|
content: html,
|
|
|
|
|
|
success: function (layero, index) {
|
|
|
|
|
|
Form.events.selectpicker(layero);
|
|
|
|
|
|
layero.find('.btn-batch-operate-cancel').on('click', function () {
|
|
|
|
|
|
Layer.close(index);
|
|
|
|
|
|
});
|
|
|
|
|
|
layero.find('.btn-batch-operate-confirm').on('click', function () {
|
|
|
|
|
|
var splitLinkId = $.trim(layero.find('#batch-operate-split-link').val());
|
|
|
|
|
|
var numbers = $.trim(layero.find('#batch-operate-numbers').val());
|
|
|
|
|
|
var action = $.trim(layero.find('#batch-operate-action').val());
|
|
|
|
|
|
|
|
|
|
|
|
if (!splitLinkId) {
|
|
|
|
|
|
Toastr.warning(__('Please select split link'));
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!numbers) {
|
|
|
|
|
|
Toastr.warning(__('Please fill at least one number'));
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!action) {
|
|
|
|
|
|
Toastr.error(__('Invalid batch operate action'));
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var submit = function () {
|
|
|
|
|
|
Fast.api.ajax({
|
|
|
|
|
|
url: 'split.number/batchoperate',
|
|
|
|
|
|
type: 'post',
|
|
|
|
|
|
data: {
|
|
|
|
|
|
split_link_id: splitLinkId,
|
|
|
|
|
|
numbers: numbers,
|
|
|
|
|
|
action: action
|
|
|
|
|
|
}
|
|
|
|
|
|
}, function (data, ret) {
|
|
|
|
|
|
Layer.close(index);
|
|
|
|
|
|
table.bootstrapTable('refresh');
|
|
|
|
|
|
Toastr.success(ret.msg || __('Batch operate success'));
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if (action === 'delete') {
|
|
|
|
|
|
Layer.confirm(__('Batch operate delete confirm'), {icon: 3, title: __('Warning')}, function (confirmIndex) {
|
|
|
|
|
|
Layer.close(confirmIndex);
|
|
|
|
|
|
submit();
|
|
|
|
|
|
});
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
submit();
|
|
|
|
|
|
return false;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2026-06-04 14:15:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
return Controller;
|
|
|
|
|
|
});
|