1. 【新增】售后单售后原因类型支持搜索

2. 【新增】手工创建订单折扣可输入正数

3. 【优化】盘点申请单确认

4. 【修复】采购退货单模拟出库失败问题

5. 【新增】订单金额客户实付与结算金额

6. 【优化】仓库发货统计报表物料名称显示

7. 【优化】自有仓储虚拟发货逻辑

8. 【修复】基础物料分类管理问题
This commit is contained in:
chenping
2026-04-01 11:59:17 +08:00
parent 9341122827
commit 61783b7d01
754 changed files with 46179 additions and 5700 deletions

View File

@@ -14,30 +14,776 @@
limitations under the License.
-->
<div id="system-setting" class="tableform tableform-tabs">
<{tabber}>
<{foreach from=$settingTabs item=item key=key}>
<{tab name=$item.name url=$item.url current=$item.current}>
<{include file=$item.file_name app=$item.app}>
<{/tab}>
<style>
/* 卡片布局样式 */
.import-cards-container {
padding: 20px;
}
.cards-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 20px;
margin-bottom: 20px;
}
.import-card {
border: 1px solid #ddd;
border-radius: 4px;
background: #fff;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
transition: all 0.3s ease;
cursor: pointer;
position: relative;
overflow: hidden;
height: 170px;
display: flex;
flex-direction: column;
}
.import-card:hover {
box-shadow: 0 12px 30px rgba(0,0,0,0.15);
transform: translateY(-8px);
border-color: #155ead;
background: linear-gradient(135deg, #fafbfc 0%, #ffffff 100%);
}
.import-card:hover .card-header {
background: linear-gradient(135deg, #f0f4f8 0%, #f8f9fa 100%);
}
.import-card:hover .platform-icon {
color: #0d47a1;
transform: scale(1.15);
}
.card-header {
padding: 8px 12px;
border-bottom: 1px solid #eee;
background: #f8f9fa;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
height: 30px;
}
.card-header h3 {
margin: 0;
font-size: 12px;
color: #333;
text-align: center;
font-weight: 500;
line-height: 1.2;
}
.card-body {
padding: 16px 20px;
text-align: center;
flex: 1;
display: flex;
align-items: center;
justify-content: center;
height: 110px;
}
.platform-icon {
font-size: 28px;
margin-bottom: 0;
color: #155ead;
transition: all 0.3s ease;
font-weight: 600;
letter-spacing: 0.5px;
}
.card-footer {
padding: 8px 12px;
text-align: left;
border-top: none;
background: transparent;
flex-shrink: 0;
min-height: 30px;
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
}
.download-btn {
background: none;
color: #6c757d;
border: none;
padding: 0;
cursor: pointer;
font-size: 11px;
text-decoration: none;
display: inline-block;
transition: color 0.3s ease;
font-weight: 400;
line-height: 1.2;
}
.download-btn:hover {
color: #0056b3;
text-decoration: underline;
}
.download-btn:visited {
color: #6f42c1;
}
.download-btn:active {
color: #0056b3;
}
.template-fields-info {
background: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 4px;
padding: 12px;
margin: 10px 0;
font-size: 13px;
color: #495057;
line-height: 1.4;
}
.division h4 {
white-space: nowrap;
margin: 0;
}
/* 模态框样式 - 使用系统预设样式 */
.modal {
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.5);
}
.modal-content {
background-color: #fefefe;
margin: 5% auto;
padding: 0;
border: 1px solid #888;
width: 90%;
max-width: 800px;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.modal-header {
padding: 15px 20px;
background: #fff;
border-bottom: 1px solid #dee2e6;
display: flex;
justify-content: space-between;
align-items: center;
}
.modal-header h3 {
margin: 0;
color: #333;
}
.close {
color: #aaa;
font-size: 28px;
font-weight: bold;
cursor: pointer;
line-height: 1;
}
.close:hover {
color: #000;
}
.modal-body {
padding: 20px;
}
/* 自定义文件上传组件样式 */
.custom-file-upload {
position: relative;
display: inline-block;
width: 100%;
max-width: 300px;
}
.custom-file-upload input[type="file"] {
position: absolute;
left: -9999px;
opacity: 0;
width: 0;
height: 0;
}
.custom-file-upload .file-upload-button {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 8px 16px;
background: #f8f9fa;
border: 2px dashed #dee2e6;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s ease;
font-size: 14px;
color: #495057;
text-decoration: none;
min-height: 36px;
width: 100%;
box-sizing: border-box;
}
.custom-file-upload .file-upload-button:hover {
background: #e9ecef;
border-color: #155ead;
color: #155ead;
}
.custom-file-upload .file-upload-button:active {
background: #dee2e6;
transform: translateY(1px);
}
.custom-file-upload .file-upload-icon {
margin-right: 8px;
font-size: 16px;
}
.custom-file-upload .file-upload-text {
flex: 1;
text-align: center;
}
.custom-file-upload .file-name {
margin-top: 8px;
padding: 4px 8px;
background: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 3px;
font-size: 12px;
color: #6c757d;
text-align: center;
word-break: break-all;
min-height: 20px;
display: flex;
align-items: center;
justify-content: center;
}
.custom-file-upload .file-name.has-file {
background: #d4edda;
border-color: #c3e6cb;
color: #155724;
}
.custom-file-upload .file-name.has-file::before {
content: "✓ ";
color: #28a745;
font-weight: bold;
}
/* 账期设置提示样式 */
.period-setting-notice {
background: #fff3cd;
border: 1px solid #ffeaa7;
border-radius: 4px;
padding: 12px 16px;
margin-bottom: 20px;
display: flex;
align-items: center;
font-size: 14px;
color: #856404;
}
/* 账期未设置时的卡片样式 */
.period-not-set .import-card {
cursor: pointer;
position: relative;
}
.period-not-set .import-card::after {
content: "点击设置账期";
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(220, 53, 69, 0.9);
color: white;
padding: 8px 16px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
opacity: 0;
transition: opacity 0.3s ease;
pointer-events: none;
z-index: 10;
}
.period-not-set .import-card:hover::after {
opacity: 1;
}
.period-setting-notice .notice-icon {
font-size: 16px;
margin-right: 8px;
}
.period-setting-notice .notice-text {
color: #856404;
}
.period-setting-notice .notice-link {
color: #dc3545;
text-decoration: none;
font-weight: 500;
margin: 0 4px;
padding: 2px 6px;
border-radius: 3px;
transition: all 0.3s ease;
}
.period-setting-notice .notice-link:hover {
background: #dc3545;
color: white;
text-decoration: none;
}
</style>
<!-- ===========================================
HTML结构
=========================================== -->
<div class="import-cards-container">
<!-- 账期设置提示 -->
<{if !$init_time}>
<div class="period-setting-notice">
<span class="notice-icon">⚠️</span>
<span class="notice-text">请先完成</span>
<a href="index.php?app=finance&ctl=setting_init&act=index" target="dialog::{width:550,height:500,resizeable:false,title:'账期设置'}" class="notice-link">账期设置</a>
<span class="notice-text">,然后才能进行账单导入操作</span>
</div>
<{/if}>
<div class="cards-grid <{if !$init_time}>period-not-set<{/if}>">
<{foreach from=$settingTabs item=item}>
<div class="import-card" data-type="<{$item.type}>" data-order="<{$item.order}>">
<div class="card-header">
<h3><{$item.name}></h3>
</div>
<div class="card-body">
<div class="platform-icon"><{$item.icon}></div>
</div>
<div class="card-footer">
<{if is_array($item.template_file)}>
<{foreach from=$item.template_file item=file_path key=link_text}>
<a href="/app/financebase/statics/import/<{$file_path}>" target="_blank" class="download-btn"><{$link_text}></a>
<{/foreach}>
<{else}>
<a href="/app/financebase/statics/import/<{$item.template_file}>" target="_blank" class="download-btn">下载模版</a>
<{/if}>
</div>
</div>
<{/foreach}>
<{/tabber}>
</div>
</div>
<!-- 模态框 -->
<div id="import-modal" class="modal" style="display: none;">
<div class="modal-content">
<div class="modal-header">
<h3 id="modal-title">导入账单</h3>
<span class="close">&times;</span>
</div>
<div class="modal-body">
<div id="import-form-container"></div>
</div>
</div>
</div>
<!-- 隐藏的iframe用于表单提交 -->
<div style="display:none;height:0;overflow:hidden;">
<iframe src='about.html' name='upload' id='uploadframe' class='hide'></iframe>
</div>
<script>
(function(){
var state;
$('ImportBtn').addEvent('click',function(e){
if(state||!$('ImportCSV').value.length)return false;
state=true;
this.style.cursor='not-allowed';
});
$('ImportCSV').addEvent('change',function(e){
state=false;
$('ImportBtn').style.cursor='pointer';
});
})();
</script>
<!-- tail.select 库 -->
<link rel="stylesheet" href="/app/desktop/statics/css/tail.select.css">
<script src="/app/desktop/statics/js/tail.select.js"></script>
<script type="text/javascript">
// 平台配置数据
window.platformConfigData = <{$platformConfigJs}>;
// 全局变量
var platformConfig = window.platformConfigData;
var modal = document.getElementById('import-modal');
// ===========================================
// 工具函数
// ===========================================
// 显示错误信息
function showError(message) {
var errorDiv = document.getElementById('allocation-error');
if (errorDiv) {
errorDiv.innerHTML = message;
errorDiv.style.display = 'block';
}
}
// 处理表单提交
function handleFormSubmit(event) {
event.preventDefault();
var form = event.target;
// 检查店铺选择
var shopSelect = form.querySelector('#shop-select');
if (!shopSelect || !shopSelect.value) {
showError('请选择店铺');
return false;
}
// 检查文件选择
var fileInput = form.querySelector('#import-file');
if (!fileInput || !fileInput.value) {
showError('请选择导入文件');
return false;
}
// 清除错误信息
var errorDiv = document.getElementById('allocation-error');
if (errorDiv) {
errorDiv.style.display = 'none';
}
// 创建隐藏的iframe
var iframe = document.createElement('iframe');
iframe.name = 'uploadframe';
iframe.style.display = 'none';
document.body.appendChild(iframe);
// 监听iframe加载完成
iframe.onload = function() {
try {
var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
var responseText = iframeDoc.body.innerHTML;
// 解析响应
var response = JSON.parse(responseText);
if (response.error) {
// 显示错误信息
showError(response.error);
} else {
// 成功,关闭模态框
modal.style.display = 'none';
// 可以添加成功提示
alert('导入成功!');
}
} catch (e) {
console.error('解析响应失败:', e);
}
// 清理iframe
document.body.removeChild(iframe);
};
// 设置表单的target并提交
form.target = 'uploadframe';
form.submit();
return false;
};
// ===========================================
// 核心功能函数
// ===========================================
// 生成导入表单
function generateImportForm(type, config, container) {
// 获取文件格式提示
var fileFormat = config.file_format || '.csv、 .xls、 .xlsx';
// 根据文件格式生成accept属性
var acceptTypes = [];
if (fileFormat.includes('.csv')) acceptTypes.push('.csv');
if (fileFormat.includes('.xls')) acceptTypes.push('.xls');
if (fileFormat.includes('.xlsx')) acceptTypes.push('.xlsx');
var acceptAttr = acceptTypes.length > 0 ? 'accept="' + acceptTypes.join(',') + '"' : '';
var formHtml = `
<div id="allocation-error" class="error" style="display:none;"></div>
<form action='index.php?app=financebase&ctl=admin_shop_settlement_import&act=treat' method='post' target="uploadframe" enctype="multipart/form-data" class="tableform modal-form" id="import-form" onsubmit="return handleFormSubmit(event)">
<div class="tableform" id="upload">
<!-- 模板字段说明 -->
${config.template_fields ? '<div class="division"><div class="template-fields-info">' + config.template_fields + '</div></div>' : ''}
<!-- 店铺选择区域 -->
<div class="division">
<table border="0" cellpadding="0" cellspacing="0" class="">
<tr>
<th>选择店铺:</th>
<td style="text-align:left;">
<select name="shop_id" id="shop-select" issearch="fuzzy-search">
<option value="">请选择店铺...</option>
</select>
</td>
</tr>
</table>
</div>
<!-- 文件上传区域 -->
<div class="division">
<table border="0" cellpadding="0" cellspacing="0" class="">
<tr>
<th>导入文件:</th>
<td style="text-align:left;">
<div class="custom-file-upload">
<input type="file" name="import_file" id="import-file" ${acceptAttr}>
<label for="import-file" class="file-upload-button">
<span class="file-upload-icon">[文件]</span>
<span class="file-upload-text">点击选择文件</span>
</label>
<div class="file-name" id="file-name-display">未选择任何文件</div>
</div>
</td>
<td width="">
<h4>上传文件,支持(
<span style='color:red;font-size:16px;'>${fileFormat}</span>
)格式!
</h4>
</td>
</tr>
</table>
</div>
</div>
<!-- 操作按钮 -->
<div class="table-action">
<input type='hidden' name='type' value="${type}"/>
<{if $init_time}>
<button type="submit" id="ImportBtn" class="btn-primary">导入</button>
<{else}>
<a href="index.php?app=finance&ctl=setting_init&act=index" target="dialog::{width:550,height:400,resizeable:false,title:'账期设置'}" class="btn btn-primary" style="margin-right:10px;">请先账单设置</a>
<{/if}>
<button type="button" id="stopBtn" class="btn-secondary">关闭</button>
</div>
<div class="notice-inline">
<span id="iMsg" style="color:red;"></span>
</div>
<div class="error" style="display: none;"></div>
</form>
`;
container.innerHTML = formHtml;
// 重新绑定导入表单事件
bindImportEvents();
// 绑定文件选择事件
bindFileUploadEvents(container);
loadShopList(type, container);
}
// 加载店铺列表
function loadShopList(type, container) {
var config = platformConfig[type];
if (!config) return;
var shopSelect = container.querySelector('#shop-select');
if (!shopSelect) return;
// 发送AJAX请求获取店铺列表
var xhr = new XMLHttpRequest();
var finderId = '<{$finder_id}>' || '';
xhr.open('GET', 'index.php?app=financebase&ctl=admin_shop_settlement_bill&act=getShopList&finder_id=' + finderId + '&type=' + type, true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
shopSelect.innerHTML = '<option value="">请选择店铺...</option>' + xhr.responseText;
// 初始化tail.select使用系统默认配置
if (typeof tail !== 'undefined' && tail.select) {
tail.select(shopSelect, {
search: true,
width: 150,
height: 660,
searchMinLength: 0
});
}
}
};
xhr.send();
}
// ===========================================
// 事件绑定
// ===========================================
// 绑定卡片点击事件
function bindCardEvents() {
var cards = document.querySelectorAll('.import-card');
cards.forEach(function(card) {
card.addEventListener('click', function(e) {
// 如果点击的是下载按钮,不触发卡片点击事件
if (e.target.classList.contains('download-btn') || e.target.closest('.download-btn')) {
return;
}
e.preventDefault();
// 检查账期是否已设置
var initTime = <{if $init_time}>true<{else}>false<{/if}>;
console.log('[DEBUG] 检查账期设置状态:', initTime);
if (!initTime) {
console.log('[DEBUG] 账期未设置,准备打开账期设置弹窗...');
// 账期未设置,跳转到账期设置页面
var link = document.createElement('a');
link.href = 'index.php?app=finance&ctl=setting_init&act=index';
link.target = 'dialog::{width:550,height:500,resizeable:false,title:\'账期设置\'}';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
console.log('[DEBUG] 账期设置弹窗已打开');
return;
}
// 账期已设置,正常打开导入弹窗
var type = this.getAttribute('data-type');
var config = platformConfig[type];
if (config) {
document.getElementById('modal-title').textContent = config.name;
generateImportForm(type, config, document.getElementById('import-form-container'));
modal.style.display = 'block';
} else {
console.error('未找到平台配置:', type);
}
});
});
}
// 绑定模态框事件
function bindModalEvents() {
var closeBtn = document.querySelector('.close');
if (closeBtn) {
closeBtn.addEventListener('click', function() {
closeModal();
});
}
// 点击模态框外部关闭
window.addEventListener('click', function(event) {
if (event.target === modal) {
closeModal();
}
});
}
// 关闭模态框
function closeModal() {
modal.style.display = 'none';
}
// 绑定文件上传事件
function bindFileUploadEvents(container) {
var fileInput = container.querySelector('#import-file');
var fileNameDisplay = container.querySelector('#file-name-display');
if (fileInput && fileNameDisplay) {
fileInput.addEventListener('change', function(e) {
var file = e.target.files[0];
if (file) {
// 京东钱包特殊验证
if (fileInput.closest('form').querySelector('input[name="type"]').value === 'jdwallet') {
var fileName = file.name;
var fileExt = fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase();
if (fileExt !== 'xlsx') {
alert('请选择.xlsx格式的Excel文件');
e.target.value = '';
return false;
}
// 检查文件大小限制为50MB
if (file.size > 50 * 1024 * 1024) {
alert('文件大小不能超过50MB');
e.target.value = '';
return false;
}
}
fileNameDisplay.textContent = file.name;
fileNameDisplay.classList.add('has-file');
} else {
fileNameDisplay.textContent = '未选择任何文件';
fileNameDisplay.classList.remove('has-file');
}
});
}
}
// 绑定导入表单事件
function bindImportEvents() {
var stopBtn = document.getElementById('stopBtn');
var importBtn = document.getElementById('ImportBtn');
var importFile = document.getElementById('import-file');
// 取消按钮
if (stopBtn) {
stopBtn.removeEventListener('click', stopBtn.clickHandler);
stopBtn.clickHandler = function() {
try{
var _dialogIns = this.getParent('.dialog').retrieve('instance');
}catch(e){}
if(_dialogIns){
_dialogIns.close();
} else {
closeModal();
}
};
stopBtn.addEventListener('click', stopBtn.clickHandler);
}
// 导入按钮状态管理
if (importBtn && importFile) {
var state = false;
importBtn.addEventListener('click', function(e) {
if (state || !importFile.value.length) return false;
state = true;
this.style.cursor = 'not-allowed';
});
importFile.addEventListener('change', function(e) {
state = false;
importBtn.style.cursor = 'pointer';
});
}
}
// ===========================================
// 初始化
// ===========================================
function init() {
bindCardEvents();
bindModalEvents();
}
// 页面加载完成后初始化
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
</script>