Merge remote-tracking branch 'websoft/master'

This commit is contained in:
2026-08-25 16:14:02 +08:00
16 changed files with 262 additions and 276 deletions
+2
View File
@@ -16,6 +16,7 @@ import { getScreen, findColumn } from './utils/util';
import { setupPaginationEnhancer } from './utils/pagination';
import { setupDialogFormLabelAlign } from './utils/dialog-form-label';
import { setupTableColumnDrag } from './utils/table-column-drag';
import { setupCrudDialogTitle } from './utils/crud-dialog-title';
import './permission';
import error from './error';
import App from './App.vue';
@@ -109,6 +110,7 @@ app.use(VueClipboard, {
appendToBody: true, // 这可以帮助解决一些更复杂的使用场景下的问题
});
setupTableColumnDrag(app);
setupCrudDialogTitle(app);
app.mount('#app');
setupPaginationEnhancer();
setupDialogFormLabelAlign();
@@ -142,11 +142,10 @@ export const option = {
rules: [{ max: 50, message: '最多 50 个字符', trigger: 'blur' }],
},
{
label: '费用',
label: '费用(元)',
prop: 'fee',
type: 'input',
formslot: true,
append: '元',
minWidth: 100,
span: 12,
placeholder: '请输入',
+3
View File
@@ -1049,3 +1049,6 @@
display: none;
}
}
.el-select__tags-text {
font-size: var(--el-form-label-font-size);
}
+41
View File
@@ -0,0 +1,41 @@
/**
* 全站 Avue CRUD 新增/编辑/查看弹窗标题自动注入
*
* 默认情况下 Avue 弹窗标题走 i18n,只显示「新增 / 编辑 / 查看」。
* 本 mixin 在组件创建时自动根据当前菜单中文标题拼出完整标题,例如:
* 「新增空港机场主数据」/「编辑空港机场主数据」/「查看空港机场主数据」
*
* 标题来源优先级:
* 1. 页面自定义方法 getCrudPageTitle()(若存在)
* 2. 当前路由中文菜单标题(this.$router.$avueRouter.generateTitle
*
* 若页面已在 option 中手动配置了 addTitle / editTitle / viewTitle,则不覆盖。
*/
export function setupCrudDialogTitle(app) {
app.mixin({
created() {
if (!this.option || typeof this.option !== 'object') return;
if (!Array.isArray(this.option.column)) return;
if (this.option._dialogTitleInjected) return;
const getTitle =
typeof this.getCrudPageTitle === 'function'
? this.getCrudPageTitle
: () => {
const router = this.$router;
if (router && router.$avueRouter) {
return router.$avueRouter.generateTitle(this.$route, { label: 'name' });
}
return (this.$route && this.$route.meta && this.$route.meta.title) || '';
};
const title = getTitle();
if (!title) return;
this.option.addTitle = this.option.addTitle || `新增${title}`;
this.option.editTitle = this.option.editTitle || `编辑${title}`;
this.option.viewTitle = this.option.viewTitle || `查看${title}`;
this.option._dialogTitleInjected = true;
},
});
}
@@ -2165,6 +2165,7 @@
show-close
:class="[
'business-crud-page__detail-dialog',
$attrs.option?.dialogCustomClass,
{ 'is-transport-plan-detail': isTransportPlanDetailLayout },
]"
>
@@ -4997,6 +4998,7 @@
@closed="cargoImportRows = []"
>
<div class="business-crud-page__cargo-import">
<div class="business-crud-page__cargo-import-card">
<el-upload
action="#"
accept=".xls,.xlsx"
@@ -5011,6 +5013,7 @@
</template>
</el-upload>
<el-button type="primary" @click="handleCargoImportTemplate">点击下载模板</el-button>
</div>
</div>
<template #footer>
<el-button :disabled="cargoImportLoading" @click="closeCargoImportDialog">关闭</el-button>
@@ -13248,6 +13251,18 @@ export default {
}
}
&__cargo-import {
padding: 0 !important;
}
&__cargo-import-card {
background: #fff;
border-radius: 6px;
padding: 24px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
width: 90%;
}
&__cargo-table {
width: 100%;
min-width: 0;
+2 -1
View File
@@ -106,6 +106,7 @@
</template>
<template v-else>
<div class="archive-page-form__title">{{ formPageTitle }}</div>
<el-form
ref="contractForm"
:model="form"
@@ -2132,7 +2133,7 @@ export default {
}
.contract-manage-form {
padding: 12px 16px 24px;
//padding: 12px 16px 24px;
background: #f5f6fa;
&__section {
+1 -1
View File
@@ -1072,7 +1072,7 @@ export default {
editBtn: false,
delBtn: false,
// 操作列收窄:本项目操作按钮较少,320px 过宽,调整为 220px(链接会自动换行兜底)
menuWidth: 180,
menuWidth: 200,
column: (option.column || []).map(column => ({
...column,
display: false,
@@ -666,4 +666,8 @@ export default {
white-space: normal;
line-height: 1.2;
}
.temporary-credit-limit-dialog .business-crud-page__detail-content .el-descriptions__label {
width: 180px !important;
}
</style>
+9 -3
View File
@@ -1,6 +1,6 @@
<template>
<basic-container class="claim-record-page">
<section class="claim-record-page__search">
<section-card class="claim-record-page__search">
<el-form :model="query" label-position="right" label-width="160px" @submit.prevent>
<div class="claim-record-page__search-grid">
<el-form-item label="认领通知单">
@@ -77,7 +77,7 @@
</el-tooltip>
</div>
</el-form>
</section>
</section-card>
<section class="claim-record-page__table-panel">
<el-table v-loading="loading" :data="rows" border>
@@ -93,7 +93,11 @@
<el-link v-if="column.link" type="primary" @click="openDetail(row)">
{{ displayValue(row[column.prop]) }}
</el-link>
<el-tag v-else-if="column.status" :type="statusType(row.claimStatus)" class="status-text">
<el-tag
v-else-if="column.status"
:type="statusType(row.claimStatus)"
class="status-text"
>
{{ displayValue(row[column.prop]) }}
</el-tag>
<span v-else-if="column.money">{{ formatMoney(row[column.prop]) }}</span>
@@ -133,6 +137,7 @@ import { ArrowDown, ArrowUp } from '@element-plus/icons-vue';
import { mapGetters } from 'vuex';
import * as api from '@/api/payment/receiptClaimRecord';
import { receiptClaimRecordTableColumns } from '@/option/payment/receiptClaimRecord';
import SectionCard from '@/components/section-card/main.vue';
const emptyQuery = () => ({
receiptNoticeNo: '',
@@ -150,6 +155,7 @@ const emptyQuery = () => ({
export default {
name: 'ReceiptClaimRecord',
components: { SectionCard },
data() {
return {
ArrowDown,
+63 -170
View File
@@ -46,149 +46,41 @@
v-model="activeSettlementType"
@tab-change="handleSettlementTypeChange"
>
<el-tab-pane label="应付" name="payable" />
<el-tab-pane label="应收" name="receivable" />
<el-tab-pane label="应付" name="payable">
<formal-settlement-table-panel
:rows="rows"
:columns="columns"
:loading="loading"
:page="page"
:has-permission="hasPermissionFn"
@create="openCreate"
@payment="openPaymentDialog"
@sync="handleSync"
@export="handleExport"
@refresh="loadTable"
@page-change="onPageChange"
@action="onAction"
@update:selection="selection = $event"
/>
</el-tab-pane>
<el-tab-pane label="应收" name="receivable">
<formal-settlement-table-panel
:rows="rows"
:columns="columns"
:loading="loading"
:page="page"
:has-permission="hasPermissionFn"
@create="openCreate"
@payment="openPaymentDialog"
@sync="handleSync"
@export="handleExport"
@refresh="loadTable"
@page-change="onPageChange"
@action="onAction"
@update:selection="selection = $event"
/>
</el-tab-pane>
</el-tabs>
<div class="formal-page__toolbar">
<div>
<el-button
v-if="hasPermission('formal_settlement_add')"
type="primary"
@click="openCreate"
>新增</el-button
>
<el-button
v-if="hasPermission('formal_settlement_payment')"
type="primary"
plain
disabled
@click="openPaymentDialog"
>付款申请</el-button
>
<el-button
v-if="hasPermission('formal_settlement_sync')"
type="primary"
plain
disabled
@click="handleSync"
>同步金蝶</el-button
>
<el-button
v-if="hasPermission('formal_settlement_print')"
type="primary"
plain
@click="handlePrint"
>打印结算单</el-button
>
<el-button
v-if="hasPermission('formal_settlement_export')"
type="primary"
plain
@click="handleExport"
>导出</el-button
>
</div>
<div class="formal-page__toolbar-right">
<el-tooltip content="刷新" placement="top">
<el-button :icon="Refresh" text @click="loadTable" />
</el-tooltip>
</div>
</div>
<el-table v-loading="loading" :data="rows" border @selection-change="selection = $event">
<el-table-column type="selection" width="52" fixed="left" align="center" />
<el-table-column type="index" label="序号" width="64" fixed="left" align="center" />
<el-table-column
v-for="column in columns"
:key="column.prop"
v-bind="column"
align="center"
show-overflow-tooltip
>
<template #default="{ row }">
<el-link v-if="column.link && row[column.prop]" type="primary" @click="openView(row)">{{
row[column.prop]
}}</el-link>
<el-tag v-else-if="column.status" :type="statusType(row.approvalStatus)" class="status-text">{{
displayValue(row[column.prop])
}}</el-tag>
<span v-else-if="column.money">{{ formatMoney(row[column.prop], row.currency) }}</span>
<span v-else-if="column.prop === 'invoiceStatusName'">{{
invoiceName(row.invoiceStatus)
}}</span>
<span v-else-if="column.prop === 'paymentStatusName'">{{
paymentName(row.paymentStatus)
}}</span>
<span v-else>{{ displayValue(row[column.prop]) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="180" fixed="right" align="center">
<template #default="{ row }"
><div class="formal-page__links">
<el-link
v-if="hasPermission('formal_settlement_view')"
type="primary"
@click="openView(row)"
>查看</el-link
>
<el-link
v-if="hasPermission('formal_settlement_edit') && isEditable(row)"
type="primary"
@click="openEdit(row)"
>编辑</el-link
>
<el-link
v-if="hasPermission('formal_settlement_delete') && row.approvalStatus === 'draft'"
type="danger"
@click="handleDelete(row)"
>删除</el-link
>
<el-link
v-if="hasPermission('formal_settlement_submit') && isEditable(row)"
type="primary"
@click="handleSubmit(row)"
>提交</el-link
>
<el-link
v-if="
hasPermission('formal_settlement_approve') && row.approvalStatus === 'reviewing'
"
type="primary"
@click="handleApprove(row)"
>通过</el-link
>
<el-link
v-if="
hasPermission('formal_settlement_approve') && row.approvalStatus === 'reviewing'
"
type="danger"
@click="handleReturn(row)"
>驳回</el-link
>
<el-link
v-if="
hasPermission('formal_settlement_void') &&
row.approvalStatus === 'approved' &&
row.kingdeeSyncStatus !== 'synced'
"
type="danger"
@click="handleVoid(row)"
>作废</el-link
>
</div></template
>
</el-table-column>
</el-table>
<div class="formal-page__pagination">
<el-pagination
v-model:current-page="page.current"
v-model:page-size="page.size"
:total="page.total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
@current-change="loadTable"
@size-change="handleSizeChange"
/>
</div>
</section>
<formal-settlement-editor
v-model="editor.visible"
@@ -239,7 +131,7 @@
</template>
<script>
import { ArrowDown, ArrowUp, Refresh } from '@element-plus/icons-vue';
import { ArrowDown, ArrowUp } from '@element-plus/icons-vue';
import { mapGetters } from 'vuex';
import * as XLSX from 'xlsx';
import * as api from '@/api/settlement/formalSettlement';
@@ -250,6 +142,7 @@ import {
} from '@/option/settlement/formalSettlementSearch';
import { formalSettlementTableColumns } from '@/option/settlement/formalSettlementTable';
import FormalSettlementEditor from './components/formal-settlement-editor.vue';
import FormalSettlementTablePanel from './components/formal-settlement-table-panel.vue';
const emptyQuery = () => ({
formalSettlementNo: '',
@@ -268,12 +161,11 @@ const emptyQuery = () => ({
export default {
name: 'FormalSettlement',
components: { FormalSettlementEditor },
components: { FormalSettlementEditor, FormalSettlementTablePanel },
data() {
return {
ArrowDown,
ArrowUp,
Refresh,
query: emptyQuery(),
searchExpanded: false,
activeSettlementType: 'payable',
@@ -294,6 +186,9 @@ export default {
},
computed: {
...mapGetters(['permission']),
hasPermissionFn() {
return code => this.permission?.[code] !== false;
},
visibleSearchFields() {
return this.searchExpanded ? this.searchFields : this.searchFields.slice(0, 4);
},
@@ -339,6 +234,24 @@ export default {
this.page.current = 1;
this.loadTable();
},
onPageChange({ current, size }) {
this.page.current = current;
this.page.size = size;
this.loadTable();
},
onAction({ type, row }) {
const handlers = {
view: () => this.openView(row),
edit: () => this.openEdit(row),
delete: () => this.handleDelete(row),
submit: () => this.handleSubmit(row),
approve: () => this.handleApprove(row),
return: () => this.handleReturn(row),
void: () => this.handleVoid(row),
print: () => this.handlePrint(row),
};
(handlers[type] || (() => {}))();
},
openCreate() {
this.$router.push({
path: '/settlement/formal-settlement/form',
@@ -436,9 +349,11 @@ export default {
this.paymentDialog.loading = false;
}
},
handlePrint() {
const row = this.selectedOne('打印');
if (!row) return;
handlePrint(row) {
if (!row) {
row = this.selectedOne('打印');
if (!row) return;
}
const win = window.open('', '_blank');
if (!win) return this.$message.warning('浏览器阻止了打印窗口,请允许弹窗后重试');
win.document.write(
@@ -578,28 +493,6 @@ export default {
.formal-page__table-panel {
margin-top: 8px;
}
.formal-page__toolbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
}
.formal-page__toolbar-right {
display: flex;
align-items: center;
gap: 8px;
}
.formal-page__links {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 8px;
}
.formal-page__pagination {
display: flex;
justify-content: flex-end;
margin-top: 12px;
}
.formal-page :deep(.el-table) {
--el-table-border-color: #eff1f7;
}
+22 -34
View File
@@ -143,7 +143,7 @@
>
<section-card title="基础船舶信息">
<el-row :gutter="18">
<el-col :span="6">
<el-col :span="8">
<el-form-item label="船舶所属组织" prop="organizationName">
<el-select
v-model="shipForm.organizationName"
@@ -160,12 +160,12 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-col :span="8">
<el-form-item label="船名" prop="shipName">
<el-input v-model="shipForm.shipName" maxlength="20" show-word-limit />
</el-form-item>
</el-col>
<el-col :span="6">
<el-col :span="8">
<el-form-item label="安放龙骨日期/建造完工日期" prop="keelLayingDate">
<div class="date-range-inline">
<el-date-picker
@@ -186,71 +186,59 @@
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="6">
<el-form-item label="总长" prop="totalLength">
<el-col :span="8">
<el-form-item label="总长m" prop="totalLength">
<el-input
v-model="shipForm.totalLength"
@input="normalizeNonNegativeDecimal('totalLength')"
>
<template #suffix>m</template>
</el-input>
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="船宽" prop="shipWidth">
<el-col :span="8">
<el-form-item label="船宽m" prop="shipWidth">
<el-input
v-model="shipForm.shipWidth"
@input="normalizeNonNegativeDecimal('shipWidth')"
>
<template #suffix>m</template>
</el-input>
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="型深" prop="moldedDepth">
<el-col :span="8">
<el-form-item label="型深m" prop="moldedDepth">
<el-input
v-model="shipForm.moldedDepth"
@input="normalizeNonNegativeDecimal('moldedDepth')"
>
<template #suffix>m</template>
</el-input>
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="6">
<el-form-item label="最大船高" prop="maxShipHeight">
<el-col :span="8">
<el-form-item label="最大船高m" prop="maxShipHeight">
<el-input
v-model="shipForm.maxShipHeight"
@input="normalizeNonNegativeDecimal('maxShipHeight')"
>
<template #suffix>m</template>
</el-input>
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="空载吃水" prop="lightDraft">
<el-col :span="8">
<el-form-item label="空载吃水t" prop="lightDraft">
<el-input
v-model="shipForm.lightDraft"
@input="normalizeNonNegativeDecimal('lightDraft')"
>
<template #suffix>t</template>
</el-input>
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="满载吃水" prop="fullLoadDraft">
<el-col :span="8">
<el-form-item label="满载吃水t" prop="fullLoadDraft">
<el-input
v-model="shipForm.fullLoadDraft"
@input="normalizeNonNegativeDecimal('fullLoadDraft')"
>
<template #suffix>t</template>
</el-input>
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="6">
<el-col :span="8">
<el-form-item label="航区" prop="navigationArea">
<div class="navigation-area-field">
<el-select v-model="shipForm.navigationArea" placeholder="请选择">
@@ -77,9 +77,7 @@
inputmode="decimal"
placeholder="请输入"
@input="value => normalizeDecimalInput('fee', value)"
>
<template #append></template>
</el-input>
/>
</template>
<template #attachments="{ row }">
<span>{{ formatAttachments(row.attachments) }}</span>
@@ -79,7 +79,7 @@
:title="readonly ? '查看评分量化表' : configForm.id ? '编辑评分量化表' : '新增评分量化表'"
append-to-body
v-model="configBox"
width="88%"
width="65%"
class="score-dialog"
@closed="resetConfig"
>
@@ -98,6 +98,7 @@
class="score-config__name-input"
maxlength="30"
show-word-limit
style="width: 500px"
/>
</el-form-item>
<el-form-item label="备注">
@@ -164,7 +165,7 @@
title="评分类目详情"
append-to-body
v-model="categoryBox"
width="82%"
width="61%"
class="score-dialog category-dialog"
:close-on-press-escape="false"
>
@@ -214,7 +215,7 @@
title="评分项目详情"
append-to-body
v-model="itemBox"
width="86%"
width="58%"
class="score-item-dialog"
>
<section-card title="评分项目详情">
@@ -343,7 +344,7 @@
title="评估标准设置"
append-to-body
v-model="standardBox"
width="76%"
width="50%"
:close-on-press-escape="false"
>
<section-card title="评估标准设置">
@@ -355,7 +356,7 @@
class="standard-form archive-form"
>
<el-row :gutter="20">
<el-col :span="8">
<el-col :span="12">
<el-form-item label="信用等级" required>
<el-select
v-model="standardForm.creditLevel"
@@ -372,7 +373,7 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="12">
<el-form-item label="得分率(%" required>
<div class="range-input standard-range-input score-rate-range">
<el-input
@@ -391,7 +392,7 @@
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-col :span="12">
<el-form-item label="得分率每增加" required>
<el-input
v-model="standardForm.scoreRateIncrease"
@@ -402,7 +403,7 @@
</el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="12">
<el-form-item label="额度增加(万元)" required>
<el-input
v-model="standardForm.creditLimitIncrease"
@@ -1391,8 +1392,8 @@ export default {
.option-row {
display: grid;
grid-template-columns: minmax(420px, 1fr) 315px 74px;
gap: 54px;
grid-template-columns: minmax(560px, 2fr) 200px 74px;
gap: 24px;
align-items: center;
margin-bottom: 14px;
}
@@ -1506,6 +1507,10 @@ export default {
overflow: auto;
}
:deep(.score-config__name-input) {
width: 500px;
}
:deep(.score-item-dialog) {
.el-dialog__header {
padding: 20px 24px 10px;
+3 -5
View File
@@ -97,9 +97,7 @@
inputmode="decimal"
placeholder="请输入"
@input="value => normalizeDecimalInput('mileage', value)"
>
<template #append>{{ form.mileageUnit || '公里' }}</template>
</el-input>
/>
</template>
<template #mileage="{ row }">
<span>{{ formatMileage(row.mileage, row.mileageUnit) }}</span>
@@ -331,7 +329,7 @@ export default {
placeholder: '日期时间选择器',
},
{
label: '里程/航程数',
label: '里程/航程数(公里)',
prop: 'mileage',
type: 'input',
minWidth: 130,
@@ -520,7 +518,7 @@ export default {
vehicleNoColumn.placeholder =
vehicleType === '船舶' ? '输入船号模糊查询选择' : '输入车牌号模糊查询选择';
vehicleTypeColumn.disabled = Boolean(this.form.id);
mileageColumn.append = mileageUnit;
mileageColumn.label = `里程/航程数(${mileageUnit}`;
mileageUnitColumn.value = mileageUnit;
this.form.mileageUnit = mileageUnit;
if (!this.isDetailLoading && oldValue && vehicleType !== oldValue && this.form.vehicleNo) {