3 Commits

Author SHA1 Message Date
gxwebsoft 42e4925fd6 style(contract): 优化附件上传按钮样式及必填提示
- 为“上传附件”和“上传变更材料”按钮添加图标及主色调样式
- 在变更原因标题及输入框添加“必填”提示文字

style(customer-archive): 调整客户档案页面细节排版和格式
- 修复联系人列表相关代码缩进及注释格式
-
2026-09-03 16:06:43 +08:00
gxwebsoft 397490a165 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	src/views/business/contract-manage.vue
2026-09-03 15:45:29 +08:00
gxwebsoft 9e14f31604 fix(business): 修复下拉框定位及输入框宽度问题
- 统一 business-crud-page.vue 表单控件宽度,包含 el-autocomplete 及其内部元素
- 调整调度弹窗收发货地址行网格,使地址输入框宽度自适应避免溢出
- contract-manage.vue 中所有 el-select 加 :teleported="false" 和 :fit-input-width="true"
- 强制 contract-manage.vue 下拉框在输入框正下方显示,防止弹出位置漂移
- 禁用 contract-manage.vue 下拉弹窗自动翻转,确保下拉框始终在下方显示
2026-09-03 15:45:01 +08:00
5 changed files with 94 additions and 81 deletions
+17
View File
@@ -0,0 +1,17 @@
## 合同管理独立表单页下拉框定位修复
- `src/views/business/contract-manage.vue` 全部 12 处 `el-select``:teleported="false"` + `:fit-input-width="true"`:下拉保持在输入框正下方、宽度与输入框一致(原 teleport 到 body 导致下拉漂移且按内容撑宽)。
- 若后续新增独立表单页出现同样问题,照加这两个属性即可。
- 追加:teleported=false 后 popper 因下方空间不足自动上翻,再给 12 处 el-select 加 `placement="bottom-start"` + `:popper-options="{ modifiers: [{ name: 'flip', enabled: false }] }"` 强制始终在下方展示。
## 调度弹窗司机输入框宽度不一致修复
- 现象:「编辑调度信息(精简录入)」弹窗中,司机(el-autocomplete)输入框比同行 250px 的其他输入框宽。
- 根因:`business-crud-page.vue` 约 13434 行的「表单控件统一 250px」规则漏了 `.el-autocomplete`autocomplete 根节点是包装层,内部才是 .el-input)。
- 修复:在该规则选择器列表追加 `> .el-autocomplete``> .el-autocomplete > .el-input`,与运单独立表单页(13658 行 business-crud-page--form-page 先例)一致。
- 经验:凡「控件统一 250px」类规则,选择器必须包含 `.el-autocomplete` 包装层。
## 调度弹窗收发货地址行溢出修复
- 现象:「编辑调度信息」弹窗收发货信息行,地址输入框过宽,右侧「联系方式」列被挤出弹窗。
- 根因:`__dispatch-shipping-form __route-address-row` 网格地址列为 `minmax(280px, 1fr)`,各列最小宽度合计 1030px,弹窗不够宽时 grid 不换行直接溢出。
- 修复:地址列改 `minmax(0, 1fr)`(宽屏仍 1fr 拉伸,窄窗自动收窄,总最小宽降到 750px)。
- 备注:基础规则 `__route-address-row`(13750 行,运单独立表单页等在用,7 列含 40px 按钮列)仍是 minmax(280px,1fr),同样窄窗有溢出风险,暂未动(用户未反馈该页问题)。
+7 -7
View File
@@ -55,13 +55,13 @@
<el-button type="primary" @click.prevent="handleLogin" class="login-submit" <el-button type="primary" @click.prevent="handleLogin" class="login-submit"
>{{ $t('login.submit') }} >{{ $t('login.submit') }}
</el-button> </el-button>
<el-button <!-- <el-button-->
v-if="this.registerMode" <!-- v-if="this.registerMode"-->
type="danger" <!-- type="danger"-->
@click.prevent="handleRegister" <!-- @click.prevent="handleRegister"-->
class="register-submit" <!-- class="register-submit"-->
>{{ $t('login.register') }} <!-- >{{ $t('login.register') }}-->
</el-button> <!-- </el-button>-->
</el-form-item> </el-form-item>
<behavior-captcha ref="behaviorCaptcha" @success="handleBehaviorSuccess"></behavior-captcha> <behavior-captcha ref="behaviorCaptcha" @success="handleBehaviorSuccess"></behavior-captcha>
</el-form> </el-form>
@@ -13568,7 +13568,9 @@ export default {
:deep(.el-form-item__content > .el-select), :deep(.el-form-item__content > .el-select),
:deep(.el-form-item__content > .el-cascader), :deep(.el-form-item__content > .el-cascader),
:deep(.el-form-item__content > .el-input-number), :deep(.el-form-item__content > .el-input-number),
:deep(.el-form-item__content > .el-date-editor) { :deep(.el-form-item__content > .el-date-editor),
:deep(.el-form-item__content > .el-autocomplete),
:deep(.el-form-item__content > .el-autocomplete > .el-input) {
width: 250px; width: 250px;
max-width: 100%; max-width: 100%;
} }
@@ -13639,7 +13641,7 @@ export default {
&__shipping-title, &__shipping-title,
&__goods-title, &__goods-title,
&__task-title { &__task-title {
min-width: 100%; //min-width: 100%;
} }
&__shipping-route-action { &__shipping-route-action {
@@ -13886,7 +13888,7 @@ export default {
); );
gap: 8px; gap: 8px;
align-items: center; align-items: center;
width: 100%; width:98%;
:deep(.el-input), :deep(.el-input),
:deep(.el-select), :deep(.el-select),
@@ -13940,7 +13942,9 @@ export default {
} }
&__dispatch-shipping-form &__route-address-row { &__dispatch-shipping-form &__route-address-row {
grid-template-columns: 260px minmax(280px, 1fr) 58px minmax(160px, 220px) 72px minmax( // minmax(0, 1fr)
//
grid-template-columns: 260px minmax(0, 1fr) 58px minmax(160px, 220px) 72px minmax(
160px, 160px,
220px 220px
); );
@@ -79,10 +79,10 @@
<section class="change-section attachment-section"> <section class="change-section attachment-section">
<div class="section-head"><div class="dialog-section-title">其它附件</div><el-button type="primary" plain>批量下载</el-button></div> <div class="section-head"><div class="dialog-section-title">其它附件</div><el-button type="primary" plain>批量下载</el-button></div>
<el-table :data="attachments" border class="change-table"><el-table-column type="index" label="序号" width="70" /><el-table-column label="文件名" min-width="240"><template #default="{ row }"><el-link type="primary" @click="previewAttachment(row, attachments)">{{ row.originalName || row.name }}</el-link></template></el-table-column><el-table-column prop="description" label="附件描述" min-width="240" /><el-table-column prop="size" label="文件大小" width="120" /><el-table-column prop="userName" label="上传人" width="140" /><el-table-column prop="uploadTime" label="上传时间" width="180" /><el-table-column label="操作" width="100"><template #default="{ $index }"><el-link type="danger" @click="attachments.splice($index, 1)">删除</el-link></template></el-table-column></el-table> <el-table :data="attachments" border class="change-table"><el-table-column type="index" label="序号" width="70" /><el-table-column label="文件名" min-width="240"><template #default="{ row }"><el-link type="primary" @click="previewAttachment(row, attachments)">{{ row.originalName || row.name }}</el-link></template></el-table-column><el-table-column prop="description" label="附件描述" min-width="240" /><el-table-column prop="size" label="文件大小" width="120" /><el-table-column prop="userName" label="上传人" width="140" /><el-table-column prop="uploadTime" label="上传时间" width="180" /><el-table-column label="操作" width="100"><template #default="{ $index }"><el-link type="danger" @click="attachments.splice($index, 1)">删除</el-link></template></el-table-column></el-table>
<el-upload action="#" :auto-upload="false" multiple :show-file-list="false" @change="handleAttachment"><el-button plain>上传附件</el-button></el-upload> <el-upload action="#" :auto-upload="false" multiple :show-file-list="false" @change="handleAttachment"><el-button type="primary" plain icon="el-icon-upload">上传附件</el-button></el-upload>
</section> </section>
<section class="change-section change-reason-section"><div class="dialog-section-title">变更原因</div><el-form-item prop="changeReason"><el-input v-model="form.changeReason" type="textarea" :rows="2" maxlength="2000" show-word-limit placeholder="请输入变更原因" /></el-form-item><div class="dialog-section-title">变更材料</div><el-upload action="#" :auto-upload="false" multiple :show-file-list="false" @change="handleChangeMaterial"><el-button plain>上传变更材料</el-button></el-upload></section> <section class="change-section change-reason-section"><div class="dialog-section-title">变更原因必填</div><el-form-item prop="changeReason"><el-input v-model="form.changeReason" type="textarea" :rows="2" maxlength="2000" show-word-limit placeholder="请输入变更原因(必填)" /></el-form-item><div class="dialog-section-title">变更材料</div><el-upload action="#" :auto-upload="false" multiple :show-file-list="false" @change="handleChangeMaterial"><el-button type="primary" plain icon="el-icon-upload">上传变更材料</el-button></el-upload></section>
<div class="page-footer"> <div class="page-footer">
<el-button @click="$router.back()">取消</el-button> <el-button @click="$router.back()">取消</el-button>
<el-button type="primary" @click="submit">提交</el-button> <el-button type="primary" @click="submit">提交</el-button>
+60 -68
View File
@@ -497,38 +497,37 @@
>新增联系人</el-button >新增联系人</el-button
> >
</div> </div>
<el-table :data="contactTableData" border class="contact-table"> <el-table :data="contactTableData" border class="contact-table">
<el-table-column label="序号" prop="__index" width="90" align="center" /> <el-table-column label="序号" prop="__index" width="90" align="center" />
<el-table-column label="联系人姓名" prop="contactName" min-width="150" /> <el-table-column label="联系人姓名" prop="contactName" min-width="150" />
<el-table-column label="联系人电话" prop="contactPhone" min-width="150" /> <el-table-column label="联系人电话" prop="contactPhone" min-width="150" />
<el-table-column label="邮箱" prop="email" min-width="180" /> <el-table-column label="邮箱" prop="email" min-width="180" />
<el-table-column label="联系地址" min-width="240"> <el-table-column label="联系地址" min-width="240">
<template #default="{ row }"> <template #default="{ row }">
<span>{{ formatContactAddress(row) || '-' }}</span> <span>{{ formatContactAddress(row) || '-' }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="备注" prop="remark" min-width="180" /> <el-table-column label="备注" prop="remark" min-width="180" />
<el-table-column label="所属分公司/事业部" min-width="170"> <el-table-column label="所属分公司/事业部" min-width="170">
<template #default="{ row }"> <template #default="{ row }">
<span>{{ row.branchName || archiveForm.deptName || '-' }}</span> <span>{{ row.branchName || archiveForm.deptName || '-' }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width="130" align="center" v-if="!readonly"> <el-table-column label="操作" width="130" align="center" v-if="!readonly">
<template #default="{ row }"> <template #default="{ row }">
<el-link type="primary" @click="openContact(row.__raw, row.__rawIndex)"> <el-link type="primary" @click="openContact(row.__raw, row.__rawIndex)">
修改 修改
</el-link> </el-link>
<el-link type="danger" @click="deleteContact(row.__rawIndex)">删除</el-link> <el-link type="danger" @click="deleteContact(row.__rawIndex)">删除</el-link>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<empty-pagination <empty-pagination
:page="contactPaginationPage" :page="contactPaginationPage"
always-show always-show
@size-change="size => handleDetailSizeChange('contact', size)" @size-change="size => handleDetailSizeChange('contact', size)"
@current-change="page => handleDetailCurrentChange('contact', page)" @current-change="page => handleDetailCurrentChange('contact', page)"
/> />
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="收款信息" name="receiptAccounts"> <el-tab-pane label="收款信息" name="receiptAccounts">
<div class="tab-table-toolbar" v-if="!readonly"> <div class="tab-table-toolbar" v-if="!readonly">
@@ -607,7 +606,9 @@
</template> </template>
<template #extra> <template #extra>
<div class="section-actions"> <div class="section-actions">
<el-button icon="el-icon-download" @click="downloadAttachments">批量下载</el-button> <el-button type="primary" plain icon="el-icon-download" @click="downloadAttachments"
>批量下载</el-button
>
<vehicle-attachment-upload <vehicle-attachment-upload
v-if="!readonly" v-if="!readonly"
v-model="ocrQualificationUploadFiles" v-model="ocrQualificationUploadFiles"
@@ -817,9 +818,7 @@
<div class="archive-form__footer"> <div class="archive-form__footer">
<!-- 次要:独立页返回 / 只读关闭 / 弹窗取消 --> <!-- 次要:独立页返回 / 只读关闭 / 弹窗取消 -->
<el-button @click="closeArchive">{{ <el-button @click="closeArchive">{{ readonly ? '关闭' : '取消' }}</el-button>
readonly ? '关闭' : '取消'
}}</el-button>
<el-button type="primary" plain v-if="!readonly" @click="saveArchive">保存</el-button> <el-button type="primary" plain v-if="!readonly" @click="saveArchive">保存</el-button>
<el-button type="primary" v-if="!readonly" @click="saveAndSubmitArchive">提交</el-button> <el-button type="primary" v-if="!readonly" @click="saveAndSubmitArchive">提交</el-button>
</div> </div>
@@ -1145,7 +1144,7 @@
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<el-form-item label="最大资金使用额度"> <el-form-item label="最大资金使用额度">
<el-input :model-value="formatScoreValue(currentScore.maxCreditLimit)" disabled > <el-input :model-value="formatScoreValue(currentScore.maxCreditLimit)" disabled>
<template #suffix>万元</template> <template #suffix>万元</template>
</el-input> </el-input>
</el-form-item> </el-form-item>
@@ -1245,7 +1244,7 @@
</el-input> </el-input>
</template> </template>
<template v-else> <template v-else>
<!-- <div class="score-standard-cell__label">请选择:</div>--> <!-- <div class="score-standard-cell__label">请选择:</div>-->
<el-select <el-select
v-model="detail.selectedOption" v-model="detail.selectedOption"
filterable filterable
@@ -1419,12 +1418,7 @@ import { normalizeSearchRangeParams } from '@/utils/search-range';
import { ArrowRight, Location } from '@element-plus/icons-vue'; import { ArrowRight, Location } from '@element-plus/icons-vue';
import { ElImageViewer, ElLoading } from 'element-plus'; import { ElImageViewer, ElLoading } from 'element-plus';
import { OpenFileViewer } from '@open-file-viewer/vue'; import { OpenFileViewer } from '@open-file-viewer/vue';
import { import { fallbackPlugin, imagePlugin, officePlugin, textPlugin } from '@open-file-viewer/core';
fallbackPlugin,
imagePlugin,
officePlugin,
textPlugin,
} from '@open-file-viewer/core';
import '@open-file-viewer/core/style.css'; import '@open-file-viewer/core/style.css';
import pdfWorkerSrc from 'pdfjs-dist/build/pdf.worker.mjs?url'; import pdfWorkerSrc from 'pdfjs-dist/build/pdf.worker.mjs?url';
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
@@ -1482,7 +1476,9 @@ export default {
} }
}; };
const validateUnifiedCreditCode = (rule, value, callback) => { const validateUnifiedCreditCode = (rule, value, callback) => {
const code = String(value || '').trim().toUpperCase(); const code = String(value || '')
.trim()
.toUpperCase();
const charset = '0123456789ABCDEFGHJKLMNPQRTUWXY'; const charset = '0123456789ABCDEFGHJKLMNPQRTUWXY';
const weights = [1, 3, 9, 27, 19, 26, 16, 17, 20, 29, 25, 13, 8, 24, 10, 30, 28]; const weights = [1, 3, 9, 27, 19, 26, 16, 17, 20, 29, 25, 13, 8, 24, 10, 30, 28];
if (!code) { if (!code) {
@@ -1865,10 +1861,7 @@ export default {
this.initScoreQuantificationOptions(); this.initScoreQuantificationOptions();
if (this.isArchivePage) { if (this.isArchivePage) {
const readonly = this.$route.query.view === '1' || this.$route.query.view === 'true'; const readonly = this.$route.query.view === '1' || this.$route.query.view === 'true';
this.openArchive( this.openArchive(this.$route.query.id ? { id: this.$route.query.id } : null, readonly);
this.$route.query.id ? { id: this.$route.query.id } : null,
readonly
);
} }
}, },
computed: { computed: {
@@ -1917,8 +1910,8 @@ export default {
const ids = Array.isArray(this.archiveForm.deptId) const ids = Array.isArray(this.archiveForm.deptId)
? this.archiveForm.deptId ? this.archiveForm.deptId
: this.archiveForm.deptId : this.archiveForm.deptId
? [this.archiveForm.deptId] ? [this.archiveForm.deptId]
: []; : [];
const currentId = ids.length ? String(ids[ids.length - 1]) : ''; const currentId = ids.length ? String(ids[ids.length - 1]) : '';
if (!currentId) return []; if (!currentId) return [];
return this.findDeptPath(currentId) || [currentId]; return this.findDeptPath(currentId) || [currentId];
@@ -2596,15 +2589,10 @@ export default {
}); });
}, },
initBusinessDictionaries() { initBusinessDictionaries() {
this.loadDictOptions( this.loadDictOptions('nature_of_client', 'customerNatureOptions', [], options => {
'nature_of_client', const customerNatureColumn = this.findColumn(this.option.column, 'customerNature');
'customerNatureOptions', customerNatureColumn.dicData = [{ label: '全部', value: '' }, ...options];
[], });
options => {
const customerNatureColumn = this.findColumn(this.option.column, 'customerNature');
customerNatureColumn.dicData = [{ label: '全部', value: '' }, ...options];
}
);
this.loadDictOptions('customer_type', 'customerTypeOptions', ['客户', '承运商'], options => { this.loadDictOptions('customer_type', 'customerTypeOptions', ['客户', '承运商'], options => {
const customerTypeColumn = this.findColumn(this.option.column, 'customerType'); const customerTypeColumn = this.findColumn(this.option.column, 'customerType');
customerTypeColumn.dicData = [{ label: '全部', value: '' }, ...options]; customerTypeColumn.dicData = [{ label: '全部', value: '' }, ...options];
@@ -3605,7 +3593,11 @@ export default {
: this.splitValue(customerType); : this.splitValue(customerType);
const requiredTypes = ['营业执照', '法人身份证']; const requiredTypes = ['营业执照', '法人身份证'];
const includesCarrier = customerTypes.some(item => const includesCarrier = customerTypes.some(item =>
['承运商', 'carrier'].includes(String(item || '').trim().toLowerCase()) ['承运商', 'carrier'].includes(
String(item || '')
.trim()
.toLowerCase()
)
); );
if (includesCarrier) requiredTypes.push('运输许可证'); if (includesCarrier) requiredTypes.push('运输许可证');
return requiredTypes; return requiredTypes;
@@ -3656,10 +3648,7 @@ export default {
} }
files.forEach((item, index) => { files.forEach((item, index) => {
window.setTimeout(() => { window.setTimeout(() => {
downloadFileByUrl( downloadFileByUrl(item.url, item.originalName || item.name || `客商材料${index + 1}`);
item.url,
item.originalName || item.name || `客商材料${index + 1}`
);
}, index * 300); }, index * 300);
}); });
}, },
@@ -4155,7 +4144,9 @@ export default {
const changeLabel = item.changeType === 'decrease' ? '每减少' : '每增加'; const changeLabel = item.changeType === 'decrease' ? '每减少' : '每增加';
const scoreLabel = item.scoreType === 'subtract' ? '减' : '加'; const scoreLabel = item.scoreType === 'subtract' ? '减' : '加';
const generatedLabel = isScoreOption const generatedLabel = isScoreOption
? `${changeLabel}${item.changeValue || ''}${item.changeUnit || ''}${scoreLabel}${item.score || ''}分` ? `${changeLabel}${item.changeValue || ''}${item.changeUnit || ''}${scoreLabel}${
item.score || ''
}分`
: ''; : '';
const label = item.label || item.optionName || item.value || generatedLabel; const label = item.label || item.optionName || item.value || generatedLabel;
return { return {
@@ -4359,7 +4350,8 @@ export default {
calculateScore(score) { calculateScore(score) {
const details = score.details || []; const details = score.details || [];
details.forEach(detail => { details.forEach(detail => {
if (!this.isScoreTypeDetail(detail) || String(detail.scoreInput ?? '').trim() === '') return; if (!this.isScoreTypeDetail(detail) || String(detail.scoreInput ?? '').trim() === '')
return;
const calculatedScore = this.getScoreTypeCalculatedScore(detail); const calculatedScore = this.getScoreTypeCalculatedScore(detail);
if (calculatedScore !== null) detail.selfScore = calculatedScore; if (calculatedScore !== null) detail.selfScore = calculatedScore;
}); });
@@ -5316,7 +5308,7 @@ export default {
max-height: 72vh; max-height: 72vh;
overflow: auto; overflow: auto;
} }
.el-col{ .el-col {
margin-bottom: 0; margin-bottom: 0;
} }
.el-form-item { .el-form-item {