563909fafa
- 新增全局弹窗表单标准类 .dialog-form--carded,实现卡片白底分组风格 - 规范表单标签宽度为 calc(6em + 24px),右对齐且垂直居中 - 统一 input/select/cascader/tree-select/date-editor/textarea 等控件宽度为 250px - 保持 radio/checkbox 组宽度自适应 - 应用样式改造多个弹窗组件:流程变更、流程删除、参数编辑、锁定用户、解锁确认、用户编辑与修改密码、新建导入 - 港口码头编辑弹窗内 input 宽度统一为 250px,调整详细地址为单行输入框并放宽宽度 - 调整备注字段文本域高度,优化整体弹窗表单布局与风格统一
1382 lines
43 KiB
Vue
1382 lines
43 KiB
Vue
<template>
|
|
<basic-container class="port-terminal-page">
|
|
<avue-crud
|
|
:option="option"
|
|
:table-loading="loading"
|
|
:data="data"
|
|
v-model:page="page"
|
|
v-model="form"
|
|
ref="crud"
|
|
:permission="permissionList"
|
|
:before-open="beforeOpen"
|
|
@row-save="rowSave"
|
|
@row-update="rowUpdate"
|
|
@row-del="rowDel"
|
|
@search-change="searchChange"
|
|
@search-reset="searchReset"
|
|
@selection-change="selectionChange"
|
|
@current-change="currentChange"
|
|
@size-change="sizeChange"
|
|
@refresh-change="refreshChange"
|
|
@on-load="onLoad"
|
|
>
|
|
<template #menu-left>
|
|
<el-button
|
|
type="primary"
|
|
icon="el-icon-upload"
|
|
plain
|
|
v-if="hasPermission('port_terminal_import')"
|
|
@click="handleImport"
|
|
>批量导入
|
|
</el-button>
|
|
<el-button
|
|
type="primary"
|
|
icon="el-icon-download"
|
|
plain
|
|
v-if="hasPermission('port_terminal_template')"
|
|
@click="handleTemplate"
|
|
>下载模板
|
|
</el-button>
|
|
<el-button
|
|
type="primary"
|
|
icon="el-icon-download"
|
|
plain
|
|
v-if="hasPermission('port_terminal_export')"
|
|
@click="handleExport"
|
|
>批量导出
|
|
</el-button>
|
|
<el-button
|
|
type="danger"
|
|
icon="el-icon-delete"
|
|
plain
|
|
v-if="hasPermission('port_terminal_delete')"
|
|
@click="handleDelete"
|
|
>批量删除
|
|
</el-button>
|
|
</template>
|
|
<template #parentName="{ row }">
|
|
<span>{{ row.category === '港口' ? '-' : row.parentName }}</span>
|
|
</template>
|
|
<template #parentCode="{ row }">
|
|
<span>{{ row.category === '港口' ? '-' : row.parentCode }}</span>
|
|
</template>
|
|
<template #name-header>
|
|
<span>港口/码头</span>
|
|
</template>
|
|
<template #status="{ row }">
|
|
<el-tag :type="row.status === 1 ? 'primary' : 'info'">
|
|
{{ row.status === 1 ? '启用' : '停用' }}
|
|
</el-tag>
|
|
</template>
|
|
<template #country-form>
|
|
<el-select
|
|
v-model="form.countryCode"
|
|
clearable
|
|
filterable
|
|
:disabled="isParentRegionLocked"
|
|
:placeholder="isParentRegionLocked ? '自动带出' : '请选择国家'"
|
|
style="width: 100%"
|
|
@change="handleCountryChange"
|
|
>
|
|
<el-option
|
|
v-for="item in countryOptions"
|
|
:key="item.id"
|
|
:label="item.title"
|
|
:value="item.id"
|
|
/>
|
|
</el-select>
|
|
</template>
|
|
<template #category-form>
|
|
<el-radio-group v-model="form.category" @change="handleCategoryChange">
|
|
<el-radio label="港口">港口</el-radio>
|
|
<el-radio label="码头">码头</el-radio>
|
|
</el-radio-group>
|
|
</template>
|
|
<template #code-form>
|
|
<el-input
|
|
v-if="form.category !== '码头'"
|
|
v-model="form.code"
|
|
clearable
|
|
maxlength="5"
|
|
placeholder="请输入5位大写字母"
|
|
@input="handleCodeChange"
|
|
@blur="handleCodeChange(form.code)"
|
|
/>
|
|
<div v-else>
|
|
<el-input
|
|
v-model="form.terminalCode"
|
|
clearable
|
|
maxlength="24"
|
|
placeholder="请输入码头标识"
|
|
@input="handleTerminalCodeChange"
|
|
/>
|
|
</div>
|
|
</template>
|
|
<template #menu="{ row, index }">
|
|
<el-link
|
|
type="primary"
|
|
v-if="hasPermission('port_terminal_edit')"
|
|
@click="$refs.crud.rowEdit(row, index)"
|
|
>
|
|
编辑
|
|
</el-link>
|
|
<el-link
|
|
type="primary"
|
|
v-if="hasPermission('port_terminal_status')"
|
|
@click="handleStatus(row)"
|
|
>
|
|
{{ row.status === 1 ? '停用' : '启用' }}
|
|
</el-link>
|
|
</template>
|
|
<template #cityCode-form>
|
|
<el-select
|
|
v-model="form.cityCode"
|
|
clearable
|
|
filterable
|
|
:loading="cityLoading"
|
|
:disabled="isCityDisabled"
|
|
:placeholder="isParentRegionLocked ? '自动带出' : '请先选择国家'"
|
|
style="width: 100%"
|
|
@change="handleCityChange"
|
|
>
|
|
<el-option
|
|
v-for="item in cityOptions"
|
|
:key="item.id"
|
|
:label="item.title"
|
|
:value="item.id"
|
|
/>
|
|
</el-select>
|
|
</template>
|
|
<template #districtCode-form>
|
|
<el-select
|
|
v-model="form.districtCode"
|
|
clearable
|
|
filterable
|
|
:loading="districtLoading"
|
|
:disabled="isDistrictDisabled"
|
|
:placeholder="
|
|
isParentRegionLocked ? '自动带出' : form.cityCode ? '请选择区县' : '请先选择城市'
|
|
"
|
|
style="width: 100%"
|
|
@change="handleDistrictChange"
|
|
>
|
|
<el-option
|
|
v-for="item in districtOptions"
|
|
:key="item.id"
|
|
:label="item.title"
|
|
:value="item.id"
|
|
/>
|
|
</el-select>
|
|
</template>
|
|
<template #detailAddress="{ row }">
|
|
<span class="port-terminal-page__detail-address">{{ row.detailAddress || '-' }}</span>
|
|
</template>
|
|
<template #detailAddress-form>
|
|
<el-input
|
|
v-model="form.detailAddress"
|
|
class="detail-address-input"
|
|
clearable
|
|
maxlength="255"
|
|
show-word-limit
|
|
placeholder="请输入"
|
|
/>
|
|
</template>
|
|
<template #remark-form>
|
|
<el-input
|
|
v-model="form.remark"
|
|
class="remark-input"
|
|
type="textarea"
|
|
:rows="2"
|
|
maxlength="200"
|
|
show-word-limit
|
|
placeholder="请输入"
|
|
/>
|
|
</template>
|
|
<template #longitude-form>
|
|
<el-input
|
|
v-model="form.longitude"
|
|
clearable
|
|
placeholder="请输入经度"
|
|
@input="handleCoordinateInput('longitude', $event)"
|
|
/>
|
|
</template>
|
|
<template #latitude-form>
|
|
<el-input
|
|
v-model="form.latitude"
|
|
clearable
|
|
placeholder="请输入纬度"
|
|
@input="handleCoordinateInput('latitude', $event)"
|
|
/>
|
|
</template>
|
|
</avue-crud>
|
|
<empty-pagination
|
|
:page="page"
|
|
@size-change="sizeChange"
|
|
@current-change="currentChange"
|
|
@load="onLoad(page, query)"
|
|
/>
|
|
|
|
<el-dialog title="港口码头主数据导入" append-to-body v-model="excelBox" width="500px">
|
|
<avue-form :option="excelOption" v-model="excelForm">
|
|
<template #excelTemplate>
|
|
<el-button type="primary" @click="handleTemplate">
|
|
点击下载<i class="el-icon-download el-icon--right"></i>
|
|
</el-button>
|
|
</template>
|
|
</avue-form>
|
|
</el-dialog>
|
|
</basic-container>
|
|
</template>
|
|
|
|
<script>
|
|
import {
|
|
getList,
|
|
getDetail,
|
|
submit,
|
|
remove,
|
|
changeStatus,
|
|
getPortSelect,
|
|
} from '@/api/base/port-terminal';
|
|
import { getLazyTree } from '@/api/base/region';
|
|
import { exportBlob } from '@/api/common';
|
|
import { mapGetters } from 'vuex';
|
|
import { downloadXls } from '@/utils/util';
|
|
import { openImportDialog } from '@/utils/import-excel';
|
|
import { formatUpdateUserName } from '@/utils/audit';
|
|
import { getToken } from '@/utils/auth';
|
|
import { getCoordinateValidationMessage, normalizeCoordinateInput } from '@/utils/coordinate';
|
|
import NProgress from 'nprogress';
|
|
import 'nprogress/nprogress.css';
|
|
|
|
const DEFAULT_COUNTRY_CODE = '+86';
|
|
const EXCLUDED_EXCEL_HEADERS = ['数据来源', '启停状态'];
|
|
const REQUIRED_EXPORT_HEADERS = [
|
|
'港口码头名称',
|
|
'港口/码头名称',
|
|
'类型',
|
|
'国家',
|
|
'城市',
|
|
'经度',
|
|
'纬度',
|
|
];
|
|
|
|
const getExcelCellText = value => {
|
|
if (value === undefined || value === null) return '';
|
|
if (typeof value === 'object') {
|
|
if (Array.isArray(value.richText)) {
|
|
return value.richText.map(item => item.text || '').join('');
|
|
}
|
|
if (value.text) return value.text;
|
|
if (value.result) return value.result;
|
|
}
|
|
return String(value);
|
|
};
|
|
|
|
const removeWorkbookColumnsByHeaders = workbook => {
|
|
workbook.eachSheet(worksheet => {
|
|
const maxHeaderRow = Math.min(worksheet.rowCount || 0, 10);
|
|
for (let rowIndex = 1; rowIndex <= maxHeaderRow; rowIndex += 1) {
|
|
const row = worksheet.getRow(rowIndex);
|
|
const indexes = [];
|
|
row.eachCell((cell, colNumber) => {
|
|
const text = getExcelCellText(cell.value).trim();
|
|
if (EXCLUDED_EXCEL_HEADERS.includes(text)) {
|
|
indexes.push(colNumber);
|
|
}
|
|
});
|
|
if (indexes.length) {
|
|
indexes.sort((a, b) => b - a).forEach(colNumber => worksheet.spliceColumns(colNumber, 1));
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
const addWorkbookRequiredHeaderMarks = workbook => {
|
|
workbook.eachSheet(worksheet => {
|
|
const maxHeaderRow = Math.min(worksheet.rowCount || 0, 10);
|
|
for (let rowIndex = 1; rowIndex <= maxHeaderRow; rowIndex += 1) {
|
|
const row = worksheet.getRow(rowIndex);
|
|
let hasRequiredHeader = false;
|
|
row.eachCell(cell => {
|
|
const text = getExcelCellText(cell.value).trim();
|
|
const header = text.replace(/\*+$/g, '').trim();
|
|
if (REQUIRED_EXPORT_HEADERS.includes(header)) {
|
|
cell.value = `${header}*`;
|
|
hasRequiredHeader = true;
|
|
}
|
|
});
|
|
if (hasRequiredHeader) break;
|
|
}
|
|
});
|
|
};
|
|
|
|
const removeExcelColumnsByHeaders = async (blob, options = {}) => {
|
|
const { markRequiredHeaders = false } = options;
|
|
const ExcelJS = await import('exceljs');
|
|
const workbook = new ExcelJS.Workbook();
|
|
await workbook.xlsx.load(await blob.arrayBuffer());
|
|
removeWorkbookColumnsByHeaders(workbook);
|
|
if (markRequiredHeaders) {
|
|
addWorkbookRequiredHeaderMarks(workbook);
|
|
}
|
|
const buffer = await workbook.xlsx.writeBuffer();
|
|
return new Blob([buffer], { type: 'application/vnd.ms-excel' });
|
|
};
|
|
|
|
export default {
|
|
data() {
|
|
const validateCode = (rule, value, callback) => {
|
|
const code = String(value || '').toUpperCase();
|
|
if (this.form.category === '港口') {
|
|
if (!/^[A-Z]{5}$/.test(code)) {
|
|
callback(new Error('港口编码为5位大写字母,如:CNSHG'));
|
|
} else {
|
|
callback();
|
|
}
|
|
return;
|
|
}
|
|
if (this.form.category === '码头') {
|
|
if (!/^[A-Z]{5}-[A-Z0-9]+$/.test(code)) {
|
|
callback(new Error('码头编码格式为“港口编码-码头标识”,如:CNSHG-CT1'));
|
|
} else if (this.form.parentCode && !code.startsWith(`${this.form.parentCode}-`)) {
|
|
callback(new Error(`码头编码须以所选上级港口编码 ${this.form.parentCode}- 开头`));
|
|
} else {
|
|
callback();
|
|
}
|
|
return;
|
|
}
|
|
callback();
|
|
};
|
|
const validateLongitude = (rule, value, callback) => {
|
|
const message = getCoordinateValidationMessage(value, 'longitude');
|
|
if (message) {
|
|
callback(new Error(message));
|
|
} else {
|
|
callback();
|
|
}
|
|
};
|
|
const validateLatitude = (rule, value, callback) => {
|
|
const message = getCoordinateValidationMessage(value, 'latitude');
|
|
if (message) {
|
|
callback(new Error(message));
|
|
} else {
|
|
callback();
|
|
}
|
|
};
|
|
return {
|
|
form: {},
|
|
query: {},
|
|
loading: true,
|
|
data: [],
|
|
excelBox: false,
|
|
excelForm: {},
|
|
portOptions: [],
|
|
countryOptions: [],
|
|
cityOptions: [],
|
|
cityLoading: false,
|
|
cityOptionCache: {},
|
|
districtOptions: [],
|
|
districtLoading: false,
|
|
districtOptionCache: {},
|
|
page: {
|
|
pageSize: 10,
|
|
pageSizes: [10, 20, 50, 100],
|
|
currentPage: 1,
|
|
total: 0,
|
|
},
|
|
selectionList: [],
|
|
option: {
|
|
height: 'auto',
|
|
calcHeight: 32,
|
|
dialogWidth: 980,
|
|
dialogCustomClass: 'port-terminal-edit-dialog',
|
|
labelPosition: 'right',
|
|
labelWidth: 'auto',
|
|
tip: false,
|
|
searchShow: true,
|
|
searchLabelWidth: 160,
|
|
searchIcon: true,
|
|
searchIndex: 4,
|
|
searchMenuSpan: 24,
|
|
searchMenuPosition: 'right',
|
|
border: true,
|
|
index: true,
|
|
indexLabel: '序号',
|
|
indexWidth: 70,
|
|
viewBtn: false,
|
|
delBtn: false,
|
|
editBtn: false,
|
|
selection: true,
|
|
dialogClickModal: false,
|
|
menuWidth: 180,
|
|
column: [
|
|
{
|
|
label: '编码',
|
|
prop: 'code',
|
|
search: true,
|
|
formslot: true,
|
|
width: 140,
|
|
span: 12,
|
|
order: 30,
|
|
placeholder: '请输入编码',
|
|
rules: [
|
|
{ required: true, message: '请输入编码', trigger: 'blur' },
|
|
{ validator: validateCode, trigger: 'blur' },
|
|
],
|
|
change: ({ value }) => this.handleCodeChange(value),
|
|
blur: ({ value }) => this.handleCodeChange(value),
|
|
},
|
|
{
|
|
label: '港口/码头名称',
|
|
prop: 'name',
|
|
minWidth: 140,
|
|
search: true,
|
|
searchOrder: 7,
|
|
span: 12,
|
|
order: 20,
|
|
placeholder: '请输入',
|
|
rules: [{ required: true, message: '请输入港口/码头名称', trigger: 'blur' }],
|
|
},
|
|
{
|
|
label: '类型',
|
|
prop: 'category',
|
|
type: 'select',
|
|
search: true,
|
|
searchOrder: 6,
|
|
formslot: true,
|
|
span: 24,
|
|
order: 10,
|
|
dicData: [
|
|
{ label: '全部', value: '' },
|
|
{ label: '港口', value: '港口' },
|
|
{ label: '码头', value: '码头' },
|
|
],
|
|
rules: [{ required: true, message: '请选择类型', trigger: 'change' }],
|
|
change: ({ value }) => this.handleCategoryChange(value),
|
|
},
|
|
{
|
|
label: '上级港口',
|
|
prop: 'parentId',
|
|
type: 'select',
|
|
props: {
|
|
label: 'name',
|
|
value: 'id',
|
|
},
|
|
dicData: [],
|
|
hide: true,
|
|
span: 12,
|
|
order: 100,
|
|
placeholder: '请选择',
|
|
rules: [{ required: false, message: '请选择上级港口', trigger: 'change' }],
|
|
change: ({ value }) => this.handleParentChange(value),
|
|
},
|
|
{
|
|
label: '上级港口',
|
|
prop: 'parentName',
|
|
minWidth: 140,
|
|
slot: true,
|
|
addDisplay: false,
|
|
editDisplay: false,
|
|
},
|
|
{
|
|
label: '上级港口编码',
|
|
prop: 'parentCode',
|
|
minWidth: 120,
|
|
slot: true,
|
|
span: 12,
|
|
addDisplay: false,
|
|
editDisplay: false,
|
|
placeholder: '自动带出',
|
|
rules: [{ required: false, message: '请选择上级港口', trigger: 'change' }],
|
|
},
|
|
{
|
|
label: '国家',
|
|
prop: 'country',
|
|
type: 'select',
|
|
search: true,
|
|
searchOrder: 5,
|
|
formslot: true,
|
|
filterable: true,
|
|
dicData: [],
|
|
span: 12,
|
|
order: 40,
|
|
minWidth: 140,
|
|
overHidden: false,
|
|
placeholder: '自动带出',
|
|
searchPlaceholder: '请选择 国家',
|
|
change: ({ value }) => this.handleCountryNameChange(value),
|
|
rules: [{ required: true, message: '请选择国家', trigger: 'change' }],
|
|
},
|
|
{
|
|
label: '城市',
|
|
prop: 'cityCode',
|
|
formslot: true,
|
|
hide: true,
|
|
span: 12,
|
|
order: 50,
|
|
placeholder: '自动带出',
|
|
rules: [{ required: true, message: '请选择所属城市', trigger: 'change' }],
|
|
},
|
|
{
|
|
label: '城市',
|
|
prop: 'city',
|
|
type: 'select',
|
|
search: true,
|
|
searchOrder: 4,
|
|
filterable: true,
|
|
dicData: [],
|
|
addDisplay: false,
|
|
editDisplay: false,
|
|
minWidth: 100,
|
|
change: ({ value }) => this.handleCityNameChange(value),
|
|
},
|
|
{
|
|
label: '区县',
|
|
prop: 'districtCode',
|
|
type: 'select',
|
|
formslot: true,
|
|
hide: true,
|
|
order: 60,
|
|
props: {
|
|
label: 'title',
|
|
value: 'id',
|
|
},
|
|
dicData: [],
|
|
span: 12,
|
|
placeholder: '选择城市后再选择区县',
|
|
rules: [{ required: true, message: '请选择区县', trigger: 'change' }],
|
|
},
|
|
{
|
|
label: '区县',
|
|
prop: 'districtName',
|
|
type: 'select',
|
|
search: true,
|
|
searchOrder: 3,
|
|
filterable: true,
|
|
dicData: [],
|
|
minWidth: 100,
|
|
hide: true,
|
|
addDisplay: false,
|
|
editDisplay: false,
|
|
},
|
|
{
|
|
label: '经度',
|
|
prop: 'longitude',
|
|
formslot: true,
|
|
minWidth: 130,
|
|
span: 12,
|
|
order: 80,
|
|
placeholder: '请输入',
|
|
overHidden: false,
|
|
rules: [
|
|
{ required: true, message: '请输入经度', trigger: 'blur' },
|
|
{ validator: validateLongitude, trigger: 'blur' },
|
|
],
|
|
},
|
|
{
|
|
label: '纬度',
|
|
prop: 'latitude',
|
|
formslot: true,
|
|
minWidth: 130,
|
|
span: 12,
|
|
order: 90,
|
|
placeholder: '请输入',
|
|
overHidden: false,
|
|
rules: [
|
|
{ required: true, message: '请输入纬度', trigger: 'blur' },
|
|
{ validator: validateLatitude, trigger: 'blur' },
|
|
],
|
|
},
|
|
{
|
|
label: '行政区划编码',
|
|
prop: 'regionCode',
|
|
minWidth: 140,
|
|
span: 12,
|
|
placeholder: '选择区县后自动填写',
|
|
addDisplay: false,
|
|
editDisplay: false,
|
|
rules: [{ max: 32, message: '行政区划编码不能超过32字', trigger: 'blur' }],
|
|
},
|
|
{
|
|
label: '详细地址',
|
|
prop: 'detailAddress',
|
|
slot: true,
|
|
formslot: true,
|
|
span: 24,
|
|
minWidth: 220,
|
|
overHidden: false,
|
|
order: 70,
|
|
placeholder: '请输入',
|
|
maxlength: 255,
|
|
showWordLimit: true,
|
|
rules: [{ max: 255, message: '详细地址不能超过255字', trigger: 'blur' }],
|
|
},
|
|
{
|
|
label: '数据来源',
|
|
prop: 'dataSource',
|
|
type: 'select',
|
|
search: true,
|
|
searchOrder: 2,
|
|
minWidth: 120,
|
|
overHidden: false,
|
|
addDisplay: false,
|
|
editDisplay: false,
|
|
dicData: [
|
|
{ label: '全部', value: '' },
|
|
{ label: '初始化导入', value: '初始化导入' },
|
|
{ label: '批量导入', value: '批量导入' },
|
|
{ label: '手工导入', value: '手工导入' },
|
|
],
|
|
},
|
|
{
|
|
label: '备注',
|
|
prop: 'remark',
|
|
type: 'textarea',
|
|
minRows: 2,
|
|
span: 24,
|
|
formslot: true,
|
|
hide: true,
|
|
maxlength: 200,
|
|
showWordLimit: true,
|
|
rules: [{ max: 200, message: '备注不能超过200个字', trigger: 'blur' }],
|
|
},
|
|
{
|
|
label: '更新人',
|
|
prop: 'updateUserName',
|
|
formatter: formatUpdateUserName,
|
|
addDisplay: false,
|
|
editDisplay: false,
|
|
minWidth: 120,
|
|
},
|
|
{
|
|
label: '更新时间',
|
|
prop: 'updateTime',
|
|
sortable: true,
|
|
type: 'datetime',
|
|
format: 'YYYY-MM-DD HH:mm:ss',
|
|
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
|
addDisplay: false,
|
|
editDisplay: false,
|
|
minWidth: 160,
|
|
},
|
|
{
|
|
label: '创建时间',
|
|
prop: 'createTime',
|
|
sortable: true,
|
|
type: 'datetime',
|
|
format: 'YYYY-MM-DD HH:mm:ss',
|
|
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
|
addDisplay: false,
|
|
editDisplay: false,
|
|
minWidth: 160,
|
|
},
|
|
{
|
|
label: '状态',
|
|
prop: 'status',
|
|
type: 'select',
|
|
search: true,
|
|
searchOrder: 1,
|
|
slot: true,
|
|
dataType: 'number',
|
|
minWidth: 110,
|
|
overHidden: false,
|
|
addDisplay: false,
|
|
editDisplay: false,
|
|
dicData: [
|
|
{ label: '全部', value: '' },
|
|
{ label: '启用', value: 1 },
|
|
{ label: '停用', value: 2 },
|
|
],
|
|
},
|
|
],
|
|
},
|
|
excelOption: {
|
|
labelPosition: 'right',
|
|
submitBtn: false,
|
|
emptyBtn: false,
|
|
column: [
|
|
{
|
|
label: '模板上传',
|
|
prop: 'excelFile',
|
|
type: 'upload',
|
|
drag: true,
|
|
loadText: '模板上传中,请稍等',
|
|
span: 24,
|
|
propsHttp: {
|
|
res: 'data',
|
|
},
|
|
tip: '请上传 .xls,.xlsx 标准格式文件',
|
|
accept: '.xls,.xlsx',
|
|
action: '/blade-system/port-terminal/import-port-terminal',
|
|
},
|
|
{
|
|
label: '模板下载',
|
|
prop: 'excelTemplate',
|
|
formslot: true,
|
|
span: 24,
|
|
},
|
|
],
|
|
},
|
|
};
|
|
},
|
|
computed: {
|
|
...mapGetters(['permission', 'userInfo']),
|
|
permissionList() {
|
|
return {
|
|
addBtn: this.hasPermission('port_terminal_add'),
|
|
};
|
|
},
|
|
isAdmin() {
|
|
const authority = this.userInfo && this.userInfo.authority;
|
|
return Array.isArray(authority)
|
|
? authority.includes('admin')
|
|
: String(authority || '').includes('admin');
|
|
},
|
|
isParentRegionLocked() {
|
|
return this.form.category === '码头';
|
|
},
|
|
isCityDisabled() {
|
|
return this.isParentRegionLocked || !this.form.countryCode;
|
|
},
|
|
isDistrictDisabled() {
|
|
return this.isParentRegionLocked || !this.form.cityCode;
|
|
},
|
|
ids() {
|
|
let ids = [];
|
|
this.selectionList.forEach(ele => {
|
|
ids.push(ele.id);
|
|
});
|
|
return ids.join(',');
|
|
},
|
|
},
|
|
created() {
|
|
this.loadCountryOptions();
|
|
this.loadPortOptions();
|
|
},
|
|
methods: {
|
|
hasPermission(code) {
|
|
return this.isAdmin || this.validData(this.permission && this.permission[code], false);
|
|
},
|
|
handleCodeChange(value) {
|
|
const code = String(value || '').toUpperCase();
|
|
if (this.form.category === '港口') {
|
|
this.form.code = code.replace(/[^A-Z]/g, '').slice(0, 5);
|
|
return;
|
|
}
|
|
if (this.form.category === '码头') {
|
|
const terminalCode = this.getTerminalCodePart(code).replace(/[^A-Z0-9]/g, '');
|
|
if (this.form.parentCode) {
|
|
this.handleTerminalCodeChange(terminalCode);
|
|
} else {
|
|
this.form.terminalCode = terminalCode;
|
|
this.form.code = code.replace(/[^A-Z0-9-]/g, '');
|
|
}
|
|
}
|
|
},
|
|
handleTerminalCodeChange(value) {
|
|
const terminalCode = String(value || '')
|
|
.toUpperCase()
|
|
.replace(/[^A-Z0-9]/g, '');
|
|
this.form.terminalCode = terminalCode;
|
|
this.form.code =
|
|
this.form.parentCode && terminalCode ? `${this.form.parentCode}-${terminalCode}` : '';
|
|
},
|
|
getTerminalCodePart(code) {
|
|
const value = String(code || '').toUpperCase();
|
|
if (this.form.parentCode && value.startsWith(`${this.form.parentCode}-`)) {
|
|
return value.slice(this.form.parentCode.length + 1);
|
|
}
|
|
return value.split('-').slice(1).join('-');
|
|
},
|
|
syncTerminalCode() {
|
|
if (this.form.category !== '码头') return;
|
|
this.form.terminalCode = this.getTerminalCodePart(this.form.code);
|
|
this.handleTerminalCodeChange(this.form.terminalCode);
|
|
},
|
|
setColumnDisplay(prop, display) {
|
|
const column = this.findColumn(this.option.column, prop);
|
|
if (!column) return;
|
|
column.display = display;
|
|
column.addDisplay = display;
|
|
column.editDisplay = display;
|
|
column.viewDisplay = display;
|
|
},
|
|
loadCountryOptions() {
|
|
return getLazyTree('0').then(res => {
|
|
this.countryOptions = (res.data.data || []).map(this.normalizeRegionOption);
|
|
const countryColumn = this.findColumn(this.option.column, 'country');
|
|
countryColumn.dicData = this.countryOptions.map(country => ({
|
|
label: country.title,
|
|
value: country.title,
|
|
id: country.id,
|
|
}));
|
|
});
|
|
},
|
|
loadPortOptions() {
|
|
getPortSelect().then(res => {
|
|
this.portOptions = res.data.data;
|
|
this.findColumn(this.option.column, 'parentId').dicData = this.portOptions;
|
|
});
|
|
},
|
|
clearParentPortFields() {
|
|
this.form.parentId = undefined;
|
|
this.form.parentCode = '';
|
|
this.form.parentName = '';
|
|
this.form.countryCode = '';
|
|
this.form.country = '';
|
|
this.clearCityValue();
|
|
},
|
|
handleCategoryChange(value, reset = true) {
|
|
const parentColumn = this.findColumn(this.option.column, 'parentId');
|
|
parentColumn.rules[0].required = value === '码头';
|
|
this.setColumnDisplay('parentId', value === '码头');
|
|
if (value === '港口') {
|
|
this.form.parentId = undefined;
|
|
this.form.parentCode = '';
|
|
this.form.parentName = '';
|
|
this.form.terminalCode = '';
|
|
this.form.code = reset ? '' : String(this.form.code || '').split('-')[0];
|
|
} else if (reset) {
|
|
this.clearParentPortFields();
|
|
}
|
|
this.handleCodeChange(this.form.code);
|
|
},
|
|
handleCountryChange(value) {
|
|
const country = this.countryOptions.find(item => item.id === value);
|
|
this.form.country = country ? country.title : '';
|
|
this.clearCityValue();
|
|
this.loadCityOptions(value);
|
|
},
|
|
handleCountryNameChange(value) {
|
|
const country = this.countryOptions.find(item => item.title === value);
|
|
this.setDistrictOptions([]);
|
|
this.loadCityOptions(country ? country.id : '');
|
|
},
|
|
syncCountryName(countryCode) {
|
|
const country = this.countryOptions.find(item => item.id === countryCode);
|
|
if (country) {
|
|
this.form.country = country.title;
|
|
}
|
|
},
|
|
normalizeRegionOption(region) {
|
|
return {
|
|
...region,
|
|
id: region.id === undefined || region.id === null ? region.id : String(region.id),
|
|
parentCode:
|
|
region.parentCode === undefined || region.parentCode === null
|
|
? region.parentCode
|
|
: String(region.parentCode),
|
|
};
|
|
},
|
|
setCityOptions(cityOptions) {
|
|
this.cityOptions = cityOptions;
|
|
const cityCodeColumn = this.findColumn(this.option.column, 'cityCode');
|
|
if (cityCodeColumn) {
|
|
cityCodeColumn.dicData = cityOptions;
|
|
}
|
|
const cityColumn = this.findColumn(this.option.column, 'city');
|
|
if (cityColumn) {
|
|
cityColumn.dicData = cityOptions.map(city => ({
|
|
label: city.title,
|
|
value: city.title,
|
|
id: city.id,
|
|
}));
|
|
}
|
|
},
|
|
clearCityValue() {
|
|
this.form.cityCode = undefined;
|
|
this.form.city = '';
|
|
this.clearDistrictValue();
|
|
},
|
|
clearDistrictValue() {
|
|
this.form.districtCode = undefined;
|
|
this.form.districtName = '';
|
|
this.form.regionCode = '';
|
|
},
|
|
loadCityOptions(countryCode) {
|
|
if (!countryCode) {
|
|
this.setCityOptions([]);
|
|
return Promise.resolve([]);
|
|
}
|
|
if (this.cityOptionCache[countryCode]) {
|
|
this.setCityOptions(this.cityOptionCache[countryCode]);
|
|
return Promise.resolve(this.cityOptionCache[countryCode]);
|
|
}
|
|
this.cityLoading = true;
|
|
return getLazyTree(countryCode)
|
|
.then(res => {
|
|
const firstLevelList = (res.data.data || []).map(this.normalizeRegionOption);
|
|
return Promise.all(
|
|
firstLevelList.map(parent => {
|
|
return getLazyTree(parent.id).then(childRes => {
|
|
const childList = (childRes.data.data || []).map(this.normalizeRegionOption);
|
|
return childList.map(child => ({
|
|
...child,
|
|
parentCode: child.parentCode || parent.id,
|
|
}));
|
|
});
|
|
})
|
|
).then(cityGroups => {
|
|
const cityList = cityGroups.flat();
|
|
const options = cityList.length > 0 ? cityList : firstLevelList;
|
|
this.cityOptionCache[countryCode] = options;
|
|
this.setCityOptions(options);
|
|
return options;
|
|
});
|
|
})
|
|
.finally(() => {
|
|
this.cityLoading = false;
|
|
});
|
|
},
|
|
resolveCityCode(cityName, regionCode) {
|
|
const city = this.cityOptions.find(item => item.title === cityName || item.id === regionCode);
|
|
return city ? city.id : '';
|
|
},
|
|
setDistrictOptions(districtOptions) {
|
|
this.districtOptions = districtOptions;
|
|
const districtCodeColumn = this.findColumn(this.option.column, 'districtCode');
|
|
if (districtCodeColumn) {
|
|
districtCodeColumn.dicData = districtOptions;
|
|
}
|
|
const districtNameColumn = this.findColumn(this.option.column, 'districtName');
|
|
if (districtNameColumn) {
|
|
districtNameColumn.dicData = districtOptions.map(district => ({
|
|
label: district.title,
|
|
value: district.title,
|
|
id: district.id,
|
|
}));
|
|
}
|
|
},
|
|
loadDistrictOptions(cityCode) {
|
|
if (!cityCode) {
|
|
this.setDistrictOptions([]);
|
|
return Promise.resolve([]);
|
|
}
|
|
if (this.districtOptionCache[cityCode]) {
|
|
this.setDistrictOptions(this.districtOptionCache[cityCode]);
|
|
return Promise.resolve(this.districtOptionCache[cityCode]);
|
|
}
|
|
this.districtLoading = true;
|
|
return getLazyTree(cityCode)
|
|
.then(res => {
|
|
const options = (res.data.data || []).map(this.normalizeRegionOption);
|
|
this.districtOptionCache[cityCode] = options;
|
|
this.setDistrictOptions(options);
|
|
return options;
|
|
})
|
|
.finally(() => {
|
|
this.districtLoading = false;
|
|
});
|
|
},
|
|
resolveDistrictCode(districtName, regionCode) {
|
|
const district = this.districtOptions.find(
|
|
item => item.id === regionCode || item.title === districtName
|
|
);
|
|
return district ? district.id : regionCode || '';
|
|
},
|
|
handleParentChange(value) {
|
|
const parent = this.portOptions.find(item => item.id === value);
|
|
if (!parent) {
|
|
this.clearParentPortFields();
|
|
this.syncTerminalCode();
|
|
return;
|
|
}
|
|
const terminalCode =
|
|
this.form.terminalCode ||
|
|
String(this.form.code || '')
|
|
.split('-')
|
|
.slice(1)
|
|
.join('-');
|
|
this.form.parentCode = parent.code;
|
|
this.form.parentName = parent.name;
|
|
this.form.terminalCode = terminalCode;
|
|
this.handleTerminalCodeChange(terminalCode);
|
|
this.form.country = parent.country;
|
|
this.form.city = parent.city;
|
|
this.form.districtCode = parent.districtCode;
|
|
this.form.districtName = parent.districtName;
|
|
this.form.regionCode = parent.regionCode;
|
|
this.resolveCountryCode(parent.country)
|
|
.then(countryCode => {
|
|
this.form.countryCode = countryCode;
|
|
this.syncCountryName(countryCode);
|
|
return this.loadCityOptions(countryCode);
|
|
})
|
|
.then(() => {
|
|
this.form.cityCode = this.resolveCityCode(parent.city, parent.regionCode);
|
|
return this.loadDistrictOptions(this.form.cityCode);
|
|
})
|
|
.then(() => {
|
|
this.form.districtCode = this.resolveDistrictCode(parent.districtName, parent.regionCode);
|
|
this.handleDistrictChange(this.form.districtCode);
|
|
});
|
|
},
|
|
handleCityChange(value) {
|
|
const city = this.cityOptions.find(item => item.id === value);
|
|
this.form.city = city ? city.title : '';
|
|
this.clearDistrictValue();
|
|
this.loadDistrictOptions(value);
|
|
},
|
|
handleCityNameChange(value) {
|
|
const city = this.cityOptions.find(item => item.title === value);
|
|
this.setDistrictOptions([]);
|
|
this.loadDistrictOptions(city ? city.id : '');
|
|
},
|
|
handleDistrictChange(value) {
|
|
const district = this.districtOptions.find(item => item.id === value);
|
|
this.form.districtName = district ? district.title : '';
|
|
this.form.regionCode = value || '';
|
|
},
|
|
handleCoordinateInput(prop, value) {
|
|
this.form[prop] = normalizeCoordinateInput(value);
|
|
},
|
|
resolveCountryCode(country) {
|
|
if (!country) {
|
|
return Promise.resolve('');
|
|
}
|
|
const countryOption = this.countryOptions.find(
|
|
item => item.title === country || item.id === country
|
|
);
|
|
if (countryOption) {
|
|
return Promise.resolve(countryOption.id);
|
|
}
|
|
return this.loadCountryOptions().then(() => {
|
|
const option = this.countryOptions.find(
|
|
item => item.title === country || item.id === country
|
|
);
|
|
return option ? option.id : DEFAULT_COUNTRY_CODE;
|
|
});
|
|
},
|
|
normalizeRow(row) {
|
|
const submitRow = { ...row };
|
|
submitRow.code = String(submitRow.code || '').toUpperCase();
|
|
submitRow.regionCode = String(submitRow.regionCode || '').trim();
|
|
submitRow.districtCode = String(submitRow.districtCode || '').trim();
|
|
submitRow.districtName = String(submitRow.districtName || '').trim();
|
|
submitRow.detailAddress = String(submitRow.detailAddress || '').trim();
|
|
submitRow.longitude = normalizeCoordinateInput(submitRow.longitude);
|
|
submitRow.latitude = normalizeCoordinateInput(submitRow.latitude);
|
|
if (submitRow.category === '港口') {
|
|
submitRow.parentId = undefined;
|
|
submitRow.parentCode = undefined;
|
|
submitRow.parentName = undefined;
|
|
}
|
|
if (!submitRow.dataSource) submitRow.dataSource = '手工导入';
|
|
if (!submitRow.status) submitRow.status = 1;
|
|
delete submitRow.terminalCode;
|
|
delete submitRow.cityCode;
|
|
delete submitRow.countryCode;
|
|
return submitRow;
|
|
},
|
|
validateRequiredFields(row) {
|
|
const isBlank = value => value === undefined || value === null || String(value).trim() === '';
|
|
if (isBlank(row.code)) {
|
|
this.$message.warning('请输入编码');
|
|
return false;
|
|
}
|
|
if (isBlank(row.name)) {
|
|
this.$message.warning('请输入港口/码头名称');
|
|
return false;
|
|
}
|
|
if (isBlank(row.category)) {
|
|
this.$message.warning('请选择类型');
|
|
return false;
|
|
}
|
|
if (row.category === '码头' && isBlank(row.parentId)) {
|
|
this.$message.warning('请选择上级港口');
|
|
return false;
|
|
}
|
|
if (isBlank(row.regionCode)) {
|
|
this.$message.warning('请选择区县');
|
|
return false;
|
|
}
|
|
if (isBlank(row.longitude)) {
|
|
this.$message.warning('请输入经度');
|
|
return false;
|
|
}
|
|
const longitudeMessage = getCoordinateValidationMessage(row.longitude, 'longitude');
|
|
if (longitudeMessage) {
|
|
this.$message.warning(longitudeMessage);
|
|
return false;
|
|
}
|
|
if (isBlank(row.latitude)) {
|
|
this.$message.warning('请输入纬度');
|
|
return false;
|
|
}
|
|
const latitudeMessage = getCoordinateValidationMessage(row.latitude, 'latitude');
|
|
if (latitudeMessage) {
|
|
this.$message.warning(latitudeMessage);
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
stopSubmitLoading(loading) {
|
|
if (typeof loading === 'function') {
|
|
loading();
|
|
}
|
|
},
|
|
rowSave(row, done, loading) {
|
|
const submitRow = this.normalizeRow(row);
|
|
if (!this.validateRequiredFields(submitRow)) {
|
|
this.stopSubmitLoading(loading);
|
|
return;
|
|
}
|
|
submit(submitRow).then(
|
|
() => {
|
|
this.onLoad(this.page);
|
|
this.loadPortOptions();
|
|
this.$message({ type: 'success', message: '操作成功!' });
|
|
done();
|
|
},
|
|
error => {
|
|
window.console.log(error);
|
|
loading();
|
|
}
|
|
);
|
|
},
|
|
rowUpdate(row, index, done, loading) {
|
|
const submitRow = this.normalizeRow(row);
|
|
if (!this.validateRequiredFields(submitRow)) {
|
|
this.stopSubmitLoading(loading);
|
|
return;
|
|
}
|
|
submit(submitRow).then(
|
|
() => {
|
|
this.onLoad(this.page);
|
|
this.loadPortOptions();
|
|
this.$message({ type: 'success', message: '操作成功!' });
|
|
done();
|
|
},
|
|
error => {
|
|
window.console.log(error);
|
|
loading();
|
|
}
|
|
);
|
|
},
|
|
rowDel(row) {
|
|
this.$confirm('确定将选择数据删除?', {
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning',
|
|
})
|
|
.then(() => remove(row.id))
|
|
.then(() => {
|
|
this.onLoad(this.page);
|
|
this.loadPortOptions();
|
|
this.$message({ type: 'success', message: '操作成功!' });
|
|
});
|
|
},
|
|
handleDelete() {
|
|
if (this.selectionList.length === 0) {
|
|
this.$message.warning('请选择至少一条数据');
|
|
return;
|
|
}
|
|
this.$confirm('确定将选择数据删除?', {
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning',
|
|
})
|
|
.then(() => remove(this.ids))
|
|
.then(() => {
|
|
this.onLoad(this.page);
|
|
this.loadPortOptions();
|
|
this.$message({ type: 'success', message: '操作成功!' });
|
|
this.$refs.crud.toggleSelection();
|
|
});
|
|
},
|
|
handleStatus(row) {
|
|
const nextStatus = row.status === 1 ? 2 : 1;
|
|
const actionName = nextStatus === 1 ? '启用' : '停用';
|
|
this.$confirm(`是否确认${actionName}该条数据?`, {
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning',
|
|
})
|
|
.then(() => changeStatus(row.id, nextStatus))
|
|
.then(() => {
|
|
this.onLoad(this.page);
|
|
this.loadPortOptions();
|
|
this.$message({ type: 'success', message: '操作成功!' });
|
|
});
|
|
},
|
|
beforeOpen(done, type) {
|
|
if (type === 'add') {
|
|
this.form.category = this.form.category || '港口';
|
|
this.form.dataSource = this.form.dataSource || '手工导入';
|
|
this.form.status = this.form.status || 1;
|
|
}
|
|
this.handleCategoryChange(this.form.category || '港口');
|
|
if (['edit', 'view'].includes(type)) {
|
|
getDetail(this.form.id).then(res => {
|
|
this.form = res.data.data;
|
|
this.form.terminalCode = this.getTerminalCodePart(this.form.code);
|
|
this.resolveCountryCode(this.form.country)
|
|
.then(countryCode => {
|
|
this.form.countryCode = countryCode;
|
|
this.syncCountryName(countryCode);
|
|
return this.loadCityOptions(countryCode);
|
|
})
|
|
.then(() => {
|
|
this.form.cityCode = this.resolveCityCode(this.form.city, this.form.regionCode);
|
|
return this.loadDistrictOptions(this.form.cityCode);
|
|
})
|
|
.then(() => {
|
|
this.form.districtCode = this.resolveDistrictCode(
|
|
this.form.districtName,
|
|
this.form.regionCode
|
|
);
|
|
if (!this.form.regionCode) {
|
|
this.handleDistrictChange(this.form.districtCode);
|
|
}
|
|
this.handleCategoryChange(this.form.category, false);
|
|
this.syncTerminalCode();
|
|
done();
|
|
});
|
|
});
|
|
return;
|
|
}
|
|
this.resolveCountryCode(this.form.country)
|
|
.then(countryCode => {
|
|
this.form.countryCode = countryCode;
|
|
this.syncCountryName(countryCode);
|
|
return this.loadCityOptions(countryCode);
|
|
})
|
|
.then(() => {
|
|
this.form.cityCode = this.resolveCityCode(this.form.city, this.form.regionCode);
|
|
return this.loadDistrictOptions(this.form.cityCode);
|
|
})
|
|
.then(() => {
|
|
this.form.districtCode = this.resolveDistrictCode(
|
|
this.form.districtName,
|
|
this.form.regionCode
|
|
);
|
|
if (!this.form.regionCode) {
|
|
this.handleDistrictChange(this.form.districtCode);
|
|
}
|
|
done();
|
|
});
|
|
},
|
|
searchReset() {
|
|
this.query = {};
|
|
this.setCityOptions([]);
|
|
this.setDistrictOptions([]);
|
|
this.onLoad(this.page);
|
|
},
|
|
searchChange(params, done) {
|
|
this.query = params;
|
|
this.page.currentPage = 1;
|
|
this.onLoad(this.page, params);
|
|
done();
|
|
},
|
|
selectionChange(list) {
|
|
this.selectionList = list;
|
|
},
|
|
selectionClear() {
|
|
this.selectionList = [];
|
|
this.$refs.crud.toggleSelection();
|
|
},
|
|
currentChange(currentPage) {
|
|
this.page.currentPage = currentPage;
|
|
},
|
|
sizeChange(pageSize) {
|
|
this.page.pageSize = pageSize;
|
|
},
|
|
refreshChange() {
|
|
this.onLoad(this.page, this.query);
|
|
},
|
|
onLoad(page, params = {}) {
|
|
this.loading = true;
|
|
getList(page.currentPage, page.pageSize, { ...params, ...this.query })
|
|
.then(res => {
|
|
const data = res.data.data;
|
|
this.page.total = data.total;
|
|
this.data = data.records;
|
|
this.selectionClear();
|
|
})
|
|
.finally(() => {
|
|
this.loading = false;
|
|
});
|
|
},
|
|
handleImport() {
|
|
openImportDialog(
|
|
this,
|
|
'港口码头主数据',
|
|
() => {
|
|
this.loadPortOptions();
|
|
},
|
|
{
|
|
failDetailDecorator: removeWorkbookColumnsByHeaders,
|
|
}
|
|
);
|
|
},
|
|
handleExport() {
|
|
this.$confirm('是否导出港口码头主数据?', '提示', {
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning',
|
|
}).then(() => {
|
|
NProgress.start();
|
|
exportBlob('/blade-system/port-terminal/export-port-terminal', this.buildExportParams(), {
|
|
feedback: true,
|
|
})
|
|
.then(async res => {
|
|
const blob = await removeExcelColumnsByHeaders(res.data, {
|
|
markRequiredHeaders: true,
|
|
});
|
|
downloadXls(blob, `港口码头主数据${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`);
|
|
})
|
|
.finally(() => {
|
|
NProgress.done();
|
|
});
|
|
});
|
|
},
|
|
buildExportParams() {
|
|
return {
|
|
...this.query,
|
|
ids: this.ids,
|
|
[this.website.tokenHeader]: getToken(),
|
|
};
|
|
},
|
|
handleTemplate() {
|
|
exportBlob(
|
|
`/blade-system/port-terminal/export-template?${this.website.tokenHeader}=${getToken()}`,
|
|
undefined,
|
|
{ feedback: true }
|
|
).then(async res => {
|
|
const blob = await removeExcelColumnsByHeaders(res.data);
|
|
downloadXls(blob, '港口码头主数据模板.xlsx');
|
|
});
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.port-terminal-page__detail-address {
|
|
display: block;
|
|
white-space: normal;
|
|
word-break: break-all;
|
|
line-height: 20px;
|
|
padding: 4px 0;
|
|
}
|
|
</style>
|
|
|
|
<style lang="scss">
|
|
/* 港口码头编辑/新增弹窗:所有 input 文本框、select 下拉控件统一宽度 250px */
|
|
.port-terminal-edit-dialog {
|
|
.el-input,
|
|
.el-select {
|
|
width: 250px !important;
|
|
}
|
|
/* 详细地址为整行字段,放宽到占满整行 */
|
|
.detail-address-input {
|
|
width: 81% !important;
|
|
}
|
|
/* 备注宽度 80% */
|
|
.remark-input {
|
|
width: 80% !important;
|
|
}
|
|
}
|
|
</style>
|