123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809 |
- /*---------------------------------------------参数配置---------------------------------------*/
- const CONSTANTS = {
- //审批列表
- KEY_APPROVAL_TASK: 'approval_task',
- // 余额统计
- KEY_CHANNEL_NAMES: 'channel_names', //上游的名字,通道分组用
- KEY_MERCHANT_NAMES: 'merchant_names', //下游的名字,机构分组用
- KEY_CHANNEL_FAST: 'channel_fast', //上下游余额统计:统计的快充通道
- KEY_CHANNEL_NORMAL: 'channel_normal', //上下游余额统计:统计的普充通道
- KEY_MERCHANT: 'merchant', //上下游余额统计:统计的机构
- KEY_DAILY_CHANNEL: 'daily_channel', //日对账单:统计的通道
- KEY_DAILY_MERCHANT: 'daily_merchant', //日对账单:统计的机构
- KEY_MONTHLY_CHANNEL: 'monthly_channel', //月对账单:统计的通道
- KEY_MONTHLY_MERCHANT: 'monthly_merchant', //月对账单:统计的机构
- //带票额度统计
- KEY_QUOTA_COMPANY: 'quota_company', //带票额度统计:主体
- KEY_QUOTA_MERCHANT: 'quota_merchant', //带票额度统计:机构
- KEY_QUOTA_CHANNEL: 'quota_channel', //带票额度统计:通道
- KEY_QUOTA_SUMMARY: 'quota_summary', //带票额度统计:综合
- KEY_QUOTA_CONDITION: 'quota_condition', //带票额度统计:条件
- };
- /*---------------------------------------------公共方法---------------------------------------*/
- function formatDecimals(number, digit= 2) {//格式化金额
- if (typeof number === 'string') {
- number = Number(number);
- }
- if (number === 0) {
- return '0';
- }
- return parseFloat(number).toFixed(digit);
- }
- function cnMoneyFormat(money) {
- let number_data = money;
- number_data = parseInt(number_data);
- let yi = 0; // 亿
- let wan = 0; // 万
- let qian = 0; // 千
- const qian_s = 1000; // 千
- const wan_s = 10000; // 万
- const yi_s = 100000000; // 亿
- // 取整
- function qz(data) {
- return Math.floor(data);
- }
- // 为 0 判断输出
- function data_if(data, amount) {
- return data === 0 ? '' : data + amount;
- }
- // 亿
- function yi_f(data) {
- yi = qz(data / yi_s);
- data = data - (yi * yi_s);
- return {
- data1: data_if(yi, '亿'),
- data2: data,
- };
- }
- // 万
- function wan_f(data) {
- wan = qz(data / wan_s);
- data = data - (wan * wan_s);
- return {
- data1: data_if(wan, '万'),
- data2: data,
- };
- }
- // 千
- function qian_f(data) {
- qian = qz(data / qian_s);
- data = data - (qian * qian_s);
- return {
- data1: data_if(qian, '千'),
- data2: data,
- };
- }
- yi = yi_f(number_data);
- number_data = yi.data2;
- wan = wan_f(number_data);
- number_data = wan.data2;
- qian = qian_f(number_data);
- number_data = qian.data2;
- let result = yi.data1 + wan.data1 + qian.data1 + number_data;
- let resultValue = result.substr(0, 1);
- if (resultValue === '0') {
- result = result.substr(1, result.length);
- }
- let resultVali = result.substr(0, 3);
- if (resultVali === 'NaN') {
- return '';
- } else {
- return result;
- }
- }
- function isValidKey(obj, key) {
- if (obj.hasOwnProperty(key)) {
- if (obj[key] !== null && obj[key] !== undefined && obj[key] !== '') {
- return true;
- }
- }
- return false;
- }
- function deepCloneData(obj) {//数据深拷贝
- if (obj === null || typeof obj !== 'object') {
- return obj;
- }
- let clone = Array.isArray(obj) ? [] : {};
- for (let key in obj) {
- if (obj.hasOwnProperty(key)) {
- clone[key] = deepCloneData(obj[key]);
- }
- }
- return clone;
- }
- function setEmptyFieldsToZero(obj, fields) {
- fields.forEach(field => {
- if (obj.hasOwnProperty(field) && obj[field] === '') {
- obj[field] = '0';
- }
- });
- }
- function handleInputd2(event) {//限制金额输入框
- let element = event.target;
- let value = element.textContent;
- let selection = window.getSelection();
- let range = selection.getRangeAt(0);
- let startOffset = range.startOffset;
- value = value.replace(/[^\d.-]/g, '');
- const parts = value.split('.');
- if (parts.length > 1) {
- parts[1] = parts[1].slice(0, 2);
- value = parts.join('.');
- }
- element.textContent = value;
- let newNode = element.childNodes[0] || element;
- range.setStart(newNode, Math.min(startOffset, value.length));
- range.setEnd(newNode, Math.min(startOffset, value.length));
- selection.removeAllRanges();
- selection.addRange(range);
- }
- function handleInputd4(event) {
- let element = event.target;
- let value = element.textContent;
- let selection = window.getSelection();
- let range = selection.getRangeAt(0);
- let startOffset = range.startOffset;
- value = value.replace(/[^\d.-]/g, '');
- const parts = value.split('.');
- if (parts.length > 1) {
- parts[1] = parts[1].slice(0, 4);
- value = parts.join('.');
- }
- element.textContent = value;
- let newNode = element.childNodes[0] || element;
- range.setStart(newNode, Math.min(startOffset, value.length));
- range.setEnd(newNode, Math.min(startOffset, value.length));
- selection.removeAllRanges();
- selection.addRange(range);
- }
- function handleInputNormal2(event) {
- let element = event.target;
- let value = element.value;
- let cursorPos = element.selectionStart;
- value = value.replace(/[^\d.-]/g, '');
- const parts = value.split('.');
- if (parts.length > 1) {
- parts[1] = parts[1].slice(0, 2);
- value = parts.join('.');
- }
- element.value = value;
- element.setSelectionRange(cursorPos, cursorPos);
- }
- function normalizeAmount(amount) {
- return amount.replace(/^0+(\d+)/, '$1').replace(/^0+$/, '0');
- }
- function formatDateTime(dateString) {
- const date = new Date(dateString);
- const year = date.getFullYear();
- const month = String(date.getMonth() + 1).padStart(2, '0');
- const day = String(date.getDate()).padStart(2, '0');
- const hours = String(date.getHours()).padStart(2, '0');
- const minutes = String(date.getMinutes()).padStart(2, '0');
- const seconds = String(date.getSeconds()).padStart(2, '0');
- return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
- }
- function isValidDate(dateString) { //校验日期是否合法
- const regex = /^\d{4}-\d{2}-\d{2}$/;
- if (!regex.test(dateString)) {
- return false;
- }
- const date = new Date(dateString);
- if (Number.isNaN(date.getTime())) {
- return false;
- }
- const [year, month, day] = dateString.split('-').map(Number);
- return date.getFullYear() === year && date.getMonth() + 1 === month && date.getDate() === day;
- }
- function getLastMonth() {
- const date = new Date();
- date.setMonth(date.getMonth() - 1);
- const year = date.getFullYear();
- const month = (date.getMonth() + 1).toString().padStart(2, '0');
- return `${year}-${month}`;
- }
- function getCurrentMonth() {
- const currentDate = new Date();
- const year = currentDate.getFullYear();
- let month = currentDate.getMonth() + 1;
- month = month < 10 ? '0' + month : month;
- return year + '-' + month;
- }
- function isValidMonth(month) {
- const regex = /^\d{4}-(0[1-9]|1[0-2])$/;
- return regex.test(month);
- }
- function getNextMonth(baseMonth = '') {
- const date = new Date();
- if (baseMonth !== '') {
- const [year, monthStr] = baseMonth.split('-').map(Number);
- date.setFullYear(year, monthStr - 1, 1);
- }
- date.setMonth(date.getMonth() + 1);
- const year = date.getFullYear();
- const month = (date.getMonth() + 1).toString().padStart(2, '0');
- return `${year}-${month}`;
- }
- function getNextMonthFirstDay(month) {
- const [year, monthStr] = month.split('-').map(Number);
- const nextMonth = monthStr === 12 ? 1 : monthStr + 1;
- const nextYear = monthStr === 12 ? year + 1 : year;
- const nextMonthStr = nextMonth.toString().padStart(2, '0');
- return `${nextYear}-${nextMonthStr}-01`;
- }
- function getLastDateOfMonth(yearMonth) {
- const [year, month] = yearMonth.split('-').map(Number);
- const date = new Date(year, month, 0);
- const yearStr = date.getFullYear();
- const monthStr = date.getMonth() + 1;
- const dayStr = date.getDate();
- return `${yearStr}年${monthStr}月${dayStr}日`;
- }
- function chineseYearMonth(dateStr) {
- const parts = dateStr.split('-');
- if (parts.length < 2) {
- return dateStr;
- }
- const year = parts[0];
- const month = parseInt(parts[1], 10);
- return year + '年' + month + '月';
- }
- /*
- * 过滤json数组,仅保留需要的字段
- */
- function filterJsonFields(jsonArray, fieldsToKeep) {
- return jsonArray.map(item => {
- return fieldsToKeep.reduce((filteredItem, field) => {
- if (field in item) {
- filteredItem[field] = item[field];
- }
- return filteredItem;
- }, {});
- });
- }
- /**
- * 按指定字段进行排序,非字典序,将间隔开的元素排在一起
- * @param list
- * @param key
- * @returns {*[]}
- */
- function sortByName(list, key) {
- const dataMap = new Map();
- const result = [...list];
- for (let i = 0; i < result.length; i++) {
- const dataName = result[i][key];
- const sortKey = dataName.slice(0, 2);
- if (dataMap.has(sortKey)) {
- const firstIndex = dataMap.get(sortKey);
- if (i > firstIndex) {
- const [item] = result.splice(i, 1);
- result.splice(firstIndex + 1, 0, item);
- dataMap.set(sortKey, firstIndex + 1);
- i--;
- }
- } else {
- dataMap.set(sortKey, i);
- }
- }
- return result;
- }
- function isObjectEmpty(obj) {
- return Object.keys(obj).length === 0;
- }
- function showErr(error) {
- layer.alert(error, {
- icon: 2,
- title: '错误'
- });
- }
- function showWarning(error, callback) {
- layer.alert(error, {
- icon: 0,
- title: '注意',
- yes: function(index) {
- if (typeof callback === 'function') {
- callback();
- }
- layer.close(index);
- }
- });
- }
- function showSuccess(msg) {
- layer.msg(msg, {
- icon: 1,
- time: 2000
- });
- }
- function isNumeric(value) {
- return /^-?\d+(\.\d+)?$/.test(value);
- }
- /*---------------------------------------------请求---------------------------------------*/
- class BaseService {
- constructor(baseUrl) {
- this.baseUrl = baseUrl;
- }
- request(endpoint, method, data) {
- return new Promise((resolve, reject) => {
- $.ajax({
- url: this.baseUrl + endpoint,
- type: method,
- data: data,
- dataType: 'json',
- success: (response) => {
- try {
- const parsedResponse = JSON.parse(JSON.stringify(response));
- resolve(parsedResponse);
- } catch (e) {
- reject(new Error('请求解析失败: ' + response));
- }
- },
- error: (jqXHR, textStatus, errorThrown) => {
- reject(new Error('请求失败: ' + textStatus + ', ' + errorThrown));
- },
- });
- });
- }
- }
- /*------------------------------------------------------UI组件-------------------------------------------*/
- class ComponentBase {
- constructor(options) {
- this.el = document.querySelector(options.el);
- this.components = options.components || {};
- this.componentData = options.componentData || {};
- this.events = {};
- this.componentInstances = {};
- this.render();
- }
- render() {
- this.el.innerHTML = this.compile(this.el.innerHTML);
- }
- compile(template) {
- const componentNames = Object.keys(this.components);
- componentNames.forEach(name => {
- const regex = new RegExp(`<${name}([^>]*)>(.*?)<\/${name}>`, 'g');
- template = template.replace(regex, (match, attrs) => {
- const props = {};
- attrs.replace(/(\w+)="([^"]*)"/g, (match, key, value) => {
- if (CONSTANTS[value] !== undefined) {
- value = CONSTANTS[value];
- }
- props[key] = value;
- });
- const component = new this.components[name]({
- props: props,
- data: this.componentData[name] || {},
- parent: this
- });
- this.componentInstances[name] = component;
- return component.render();
- });
- });
- return template;
- }
- on(event, callback) {
- if (!this.events[event]) {
- this.events[event] = [];
- }
- this.events[event].push(callback);
- }
- emit(event, ...args) {
- if (this.events[event]) {
- this.events[event].forEach(callback => callback(...args));
- }
- }
- getComponent(name) {
- return this.componentInstances[name];
- }
- }
- class Component {
- constructor(options) {
- this.data = options.data || {};
- this.props = options.props || {};
- this.template = options.template || '';
- this.id = 'component-' + Math.random().toString(36).substr(2, 9);
- this.props['comp-id'] = this.id;
- }
- render() {
- let template = this.template;
- for (const key in this.props) {
- template = template.replace(new RegExp(`{{${key}}}`, 'g'), this.props[key]);
- }
- setTimeout(() => {
- this.bindEvents();
- this.mounted();
- }, 0);
- return template;
- }
- bindEvents() {
- const rootElement = document.querySelector(`#${this.id}`);
- if (!rootElement) {
- return;
- }
- this.unbindEvents();
- const supportedEvents = ['click', 'input', 'blur', 'change'];
- supportedEvents.forEach(eventType => {
- const elements = rootElement.querySelectorAll(`[v-on\\:${eventType}]`);
- elements.forEach(element => {
- const method = element.getAttribute(`v-on:${eventType}`);
- if (method && typeof this[method] === 'function') {
- const listener = this[method].bind(this);
- element.addEventListener(eventType, listener);
- if (!this.eventListeners[eventType]) {
- this.eventListeners[eventType] = [];
- }
- this.eventListeners[eventType].push({ element, listener });
- }
- });
- });
- }
- unbindEvents() {
- for (const eventType in this.eventListeners) {
- this.eventListeners[eventType].forEach(({ element, listener }) => {
- element.removeEventListener(eventType, listener);
- });
- }
- this.eventListeners = {};
- }
- mounted() {
- }
- }
- class LayuiTableBase {
- constructor(id) {
- this.tableId = id;
- }
- /*
- * 表格中可编辑列的处理
- */
- editEvent(editColumns = [], callback) {
- const tableItem = $('#' + this.tableId);
- const trs = tableItem.next('.layui-table-view').find('.layui-table-body tbody tr');
- trs.each(function (i, tr) {
- for (let col of editColumns) {
- const td = $(tr).find('td[data-field="' + col + '"]').not('.layui-hide');
- if (td.length > 0) {//合并隐藏的不处理
- const valDiv = td.find('div');
- valDiv.attr('contenteditable', true).css('background-color', 'lightblue');
- valDiv.off('input', handleInputd4)
- .off('keypress')
- .off('blur')
- .on('input', handleInputd4)
- .on('keypress', function (e) {
- if (e.keyCode === 13) {
- e.preventDefault();
- $(this).blur();
- }
- })
- .on('blur', function () {
- const val = valDiv.text().trim();
- if (!isNumeric(val)) {
- valDiv.html('0');
- }
- if (callback !== null && typeof callback === 'function') {
- const index = $(this).parent().parent().data('index');
- callback(index);
- }
- });
- }
- }
- });
- }
- switchEvent(switchColumns = [], callback) {
- const tableItem = $('#' + this.tableId);
- const trs = tableItem.next('.layui-table-view').find('.layui-table-body tbody tr');
- trs.each(function (i, tr) {
- for (let clo of switchColumns) {
- const td = $(tr).find('td[data-field="' + clo + '"]').not('.layui-hide');
- if (td.length > 0) {
- const buttons = td.find('.layui-btn');
- buttons.off('click').on('click', function (e) {
- e.preventDefault();
- e.stopPropagation();
- const currTr = $(this).closest('tr');
- const index = currTr.data('index');
- buttons.removeClass('selected').addClass('unselected');
- $(this).removeClass('unselected').addClass('selected');
- if (callback !== null && typeof callback === 'function') {
- callback(index);
- }
- });
- }
- }
- });
- }
- /*
- * 表格中金额字段样式,小于0的数值设置为红色
- */
- setValueClass(redColumns, boldColumns = []) {
- const trs = $('#' + this.tableId).next('.layui-table-view').find('.layui-table-body tbody tr');
- trs.each(function(i, tr) {
- for(let col of redColumns) {
- const td = $(tr).find('td[data-field="' + col + '"]').not('.layui-hide');
- if (td.length > 0) {//合并隐藏的不处理
- const valDiv = td.find('div');
- const val = valDiv.text().trim();
- if (parseFloat(val) < 0) {
- valDiv.addClass('negative-value');
- } else {
- valDiv.removeClass('negative-value');
- }
- }
- }
- for (let boldCol of boldColumns) {
- const boldTd = $(tr).find('td[data-field="' + boldCol + '"]').not('.layui-hide');
- if (boldTd.length > 0) {
- const boldDiv = boldTd.find('div');
- boldDiv.css('font-weight', 'bold');
- }
- }
- });
- }
- }
- /*------------------------------------------------------审批任务-------------------------------------------*/
- class TaskManager {
- constructor() {
- this.globalTaskList = [];
- this.currentTaskList = [];
- }
- async initializeGlobalTaskList() {
- this.globalTaskList = await this._fetchTasksFromBackend();
- this._updateCurrentTaskList();
- }
- async _fetchTasksFromBackend() {
- const response = await statsApi.getStatsSettings([CONSTANTS.KEY_APPROVAL_TASK]);
- let data = [];
- if (response.state === true) {
- data = response[CONSTANTS.KEY_APPROVAL_TASK];
- }
- return data.map(task => this._normalizeTask(task));
- }
- async _syncTaskWithBackend(task) {
- const response = await statsApi.setStatsSettings(CONSTANTS.KEY_APPROVAL_TASK, task);
- return response.state === true;
- }
- async _syncTaskListWithBackend() {
- try {
- this.globalTaskList = await this._fetchTasksFromBackend();
- this._updateCurrentTaskList();
- } catch (error) {
- console.error("Failed to sync tasks with backend:", error);
- }
- }
- _mergeTasks(taskList, newTask) {
- const taskId = newTask.id;
- const taskIndex = taskList.findIndex(task => task.id === taskId);
- if (taskIndex !== -1) {
- taskList[taskIndex] = { ...taskList[taskIndex], ...newTask };
- } else {
- taskList.push(newTask);
- }
- return taskList;
- }
- _normalizeTask(task) {
- return {
- id: task.id || this._generateUniqueId(),
- type: task.type || this.getTaskType(),
- time: task.time || new Date().toISOString(),
- data: task.data || {},
- status: task.status || 'pending',
- initiator: task.initiator || '',
- approver: task.approver || ''
- };
- }
- _generateUniqueId() {
- return 'xxxx-xxxx-4xxx-yxxx-xxxxxx'.replace(/[xy]/g, function(c) {
- let r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
- return v.toString(16);
- });
- }
- getTaskType() {
- throw new Error("Subclasses must implement getTaskType()");
- }
- _updateCurrentTaskList() {
- const field = this.getTaskType();
- this.currentTaskList = this.globalTaskList.filter(task => task.type === field);
- this._sortCurrentTaskListByTime();
- }
- _sortCurrentTaskListByTime() {
- this.currentTaskList.sort((a, b) => new Date(a.time) - new Date(b.time));
- }
- async addTask(data = {}, initiator = '', approver = '') {
- const newTask = this._normalizeTask({ data, initiator, approver });
- const taskTmp = deepCloneData(this.globalTaskList);
- const updateTask = this._mergeTasks(taskTmp, newTask);
- if (await this._syncTaskWithBackend(updateTask)) {
- this.globalTaskList.push(newTask);
- this._updateCurrentTaskList();
- return true;
- }
- return false;
- }
- getPendingTasks() {
- return this.currentTaskList;
- }
- async executeTask(taskId, approvalHandler) {
- await this._syncTaskListWithBackend();
- const taskIndex = this.currentTaskList.findIndex(task => task.id === taskId);
- if (taskIndex === -1) {
- console.warn("Task already processed or not found.");
- return false;
- }
- const task = this.currentTaskList[taskIndex];
- await approvalHandler(task);
- this.currentTaskList.splice(taskIndex, 1);
- const globalIndex = this.globalTaskList.findIndex(t => t.id === taskId);
- if (globalIndex !== -1) {
- this.globalTaskList.splice(globalIndex, 1);
- }
- await this._syncTaskWithBackend(this.globalTaskList);
- return true;
- }
- async updateTask(taskId, updatedData) {
- const taskIndex = this.globalTaskList.findIndex(task => task.id === taskId);
- if (taskIndex === -1) {
- console.warn(`Task with ID ${taskId} not found.`);
- return false;
- }
- if (Object.keys(updatedData).length === 0) {
- this.globalTaskList.splice(taskIndex, 1);
- } else {
- this.globalTaskList[taskIndex].data = updatedData;
- }
- const updateSuccess = await this._syncTaskWithBackend(this.globalTaskList);
- if (updateSuccess) {
- this._updateCurrentTaskList();
- return true;
- } else {
- console.error(`Failed to update task ${taskId} in the backend.`);
- return false;
- }
- }
- }
|