|
|
@@ -0,0 +1,899 @@
|
|
|
+<template>
|
|
|
+ <div class="container">
|
|
|
+ <header class="header">
|
|
|
+ <div class="logo">运费金融</div>
|
|
|
+ <div class="user-info">
|
|
|
+ <span class="user-name">张三</span>
|
|
|
+ <span class="verified-icon" title="已认证">✓</span>
|
|
|
+ </div>
|
|
|
+ </header>
|
|
|
+
|
|
|
+ <section class="section">
|
|
|
+ <h2>认证货主列表</h2>
|
|
|
+ <div class="shipper-list">
|
|
|
+ <table>
|
|
|
+ <thead>
|
|
|
+ <tr>
|
|
|
+ <th>货主名称</th>
|
|
|
+ <th>认证有效期</th>
|
|
|
+ <th>贷款滚动额度(元)</th>
|
|
|
+ <th>已使用额度(元)</th>
|
|
|
+ <th>剩余额度(元)</th>
|
|
|
+ </tr>
|
|
|
+ </thead>
|
|
|
+ <tbody>
|
|
|
+ <tr v-for="shipper in shipperList" :key="shipper.id">
|
|
|
+ <td>{{ shipper.name }}</td>
|
|
|
+ <td>{{ shipper.validUntil }}</td>
|
|
|
+ <td>{{ formatCurrency(shipper.totalLimit) }}</td>
|
|
|
+ <td>{{ formatCurrency(shipper.usedLimit) }}</td>
|
|
|
+ <td>{{ formatCurrency(shipper.remainingLimit) }}</td>
|
|
|
+ </tr>
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ </div>
|
|
|
+ </section>
|
|
|
+
|
|
|
+ <section class="section">
|
|
|
+ <h2>可申请贷款航次列表</h2>
|
|
|
+ <div class="voyage-list">
|
|
|
+ <table id="available-voyages">
|
|
|
+ <thead>
|
|
|
+ <tr>
|
|
|
+ <th>选择</th>
|
|
|
+ <th>航次编号</th>
|
|
|
+ <th>货主名称</th>
|
|
|
+ <th>货种</th>
|
|
|
+ <th>装载吨位(吨)</th>
|
|
|
+ <th>运价(元/吨)</th>
|
|
|
+ <th>航次总运费(元)</th>
|
|
|
+ <th>当前状态</th>
|
|
|
+ </tr>
|
|
|
+ </thead>
|
|
|
+ <tbody>
|
|
|
+ <tr v-for="voyage in availableVoyages" :key="voyage.id">
|
|
|
+ <td>
|
|
|
+ <input
|
|
|
+ type="radio"
|
|
|
+ name="voyage"
|
|
|
+ :value="voyage.id"
|
|
|
+ v-model="selectedVoyage"
|
|
|
+ />
|
|
|
+ </td>
|
|
|
+ <td>{{ voyage.voyageNumber }}</td>
|
|
|
+ <td>{{ voyage.shipperName }}</td>
|
|
|
+ <td>{{ voyage.cargoType }}</td>
|
|
|
+ <td>{{ voyage.weight.toLocaleString() }}</td>
|
|
|
+ <td>{{ voyage.rate }}</td>
|
|
|
+ <td>{{ formatCurrency(voyage.totalFreight) }}</td>
|
|
|
+ <td>{{ voyage.status }}</td>
|
|
|
+ </tr>
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ <div class="action-buttons">
|
|
|
+ <button @click="applyLoan" class="primary-btn">申请贷款</button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </section>
|
|
|
+
|
|
|
+ <section class="section">
|
|
|
+ <h2>贷款中航次列表</h2>
|
|
|
+ <div class="loan-voyage-list">
|
|
|
+ <table>
|
|
|
+ <thead>
|
|
|
+ <tr>
|
|
|
+ <th>航次编号</th>
|
|
|
+ <th>货主名称</th>
|
|
|
+ <th>货种</th>
|
|
|
+ <th>装载吨位(吨)</th>
|
|
|
+ <th>运价(元/吨)</th>
|
|
|
+ <th>航次总运费(元)</th>
|
|
|
+ <th>贷款金额(元)</th>
|
|
|
+ <th>申请日期</th>
|
|
|
+ <th>贷款状态</th>
|
|
|
+ <th>还款状态</th>
|
|
|
+ </tr>
|
|
|
+ </thead>
|
|
|
+ <tbody>
|
|
|
+ <tr v-for="loanVoyage in loanVoyages" :key="loanVoyage.id">
|
|
|
+ <td>{{ loanVoyage.voyageNumber }}</td>
|
|
|
+ <td>{{ loanVoyage.shipperName }}</td>
|
|
|
+ <td>{{ loanVoyage.cargoType }}</td>
|
|
|
+ <td>{{ loanVoyage.weight.toLocaleString() }}</td>
|
|
|
+ <td>{{ loanVoyage.rate }}</td>
|
|
|
+ <td>{{ formatCurrency(loanVoyage.totalFreight) }}</td>
|
|
|
+ <td>{{ formatCurrency(loanVoyage.loanAmount) }}</td>
|
|
|
+ <td>{{ loanVoyage.applicationDate }}</td>
|
|
|
+ <td>{{ loanVoyage.loanStatus }}</td>
|
|
|
+ <td :class="getRepaymentStatusClass(loanVoyage.repaymentStatus)">
|
|
|
+ {{ loanVoyage.repaymentStatus }}
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ </div>
|
|
|
+ </section>
|
|
|
+
|
|
|
+ <section v-if="showVoyageDetail" class="section">
|
|
|
+ <h2>航次详情</h2>
|
|
|
+ <div class="voyage-info">
|
|
|
+ <div class="info-section">
|
|
|
+ <h3>航次基础信息</h3>
|
|
|
+ <div class="info-grid">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">航次编号:</span>
|
|
|
+ <span class="value">{{ voyageDetail.voyageNumber }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">货主公司名称:</span>
|
|
|
+ <span class="value">{{ voyageDetail.shipperName }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">航次发起日期:</span>
|
|
|
+ <span class="value">{{ voyageDetail.startDate }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">装货港:</span>
|
|
|
+ <span class="value">{{ voyageDetail.loadingPort }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">卸货港:</span>
|
|
|
+ <span class="value">{{ voyageDetail.unloadingPort }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">船名:</span>
|
|
|
+ <span class="value">{{ voyageDetail.shipName }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">船东:</span>
|
|
|
+ <span class="value">{{ voyageDetail.shipOwner }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">船舶MMSI:</span>
|
|
|
+ <span class="value">{{ voyageDetail.shipMMSI }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">货种:</span>
|
|
|
+ <span class="value">{{ voyageDetail.cargoType }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">实载吨位:</span>
|
|
|
+ <span class="value">
|
|
|
+ {{ voyageDetail.weight.toLocaleString() }}吨
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">运价:</span>
|
|
|
+ <span class="value">{{ voyageDetail.rate }}元/吨</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">航次总运费:</span>
|
|
|
+ <span class="value">
|
|
|
+ {{ formatCurrency(voyageDetail.totalFreight) }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="map-container">
|
|
|
+ <h3>航次轨迹</h3>
|
|
|
+ <div id="voyage-map" ref="voyageMap"></div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="info-section">
|
|
|
+ <h3>装货信息</h3>
|
|
|
+ <div class="info-grid">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">到达装货港时间:</span>
|
|
|
+ <span class="value">{{ voyageDetail.arrivalTime }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">装货开始时间:</span>
|
|
|
+ <span class="value">{{ voyageDetail.loadingStart }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">装货结束时间:</span>
|
|
|
+ <span class="value">{{ voyageDetail.loadingEnd }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">航次开航时间:</span>
|
|
|
+ <span class="value">{{ voyageDetail.departureTime }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="document-section">
|
|
|
+ <h3>航次文件确认</h3>
|
|
|
+ <div class="document-list">
|
|
|
+ <div
|
|
|
+ class="document-item"
|
|
|
+ v-for="doc in voyageDetail.documents"
|
|
|
+ :key="doc.id"
|
|
|
+ >
|
|
|
+ <span class="document-name">{{ doc.name }}</span>
|
|
|
+ <button class="view-btn">查看</button>
|
|
|
+ <span class="confirm-status">✓ 已确认</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="loan-application">
|
|
|
+ <h3>贷款申请</h3>
|
|
|
+ <div class="loan-amount-container">
|
|
|
+ <div class="loan-info">
|
|
|
+ <span class="label">航次总运费:</span>
|
|
|
+ <span class="value">
|
|
|
+ {{ formatCurrency(voyageDetail.totalFreight) }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <div class="loan-info">
|
|
|
+ <span class="label">最高可贷款金额(80%):</span>
|
|
|
+ <span class="value">{{ formatCurrency(maxLoanAmount) }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="loan-slider-container">
|
|
|
+ <span class="label">申请贷款金额:</span>
|
|
|
+ <input
|
|
|
+ type="range"
|
|
|
+ v-model="loanAmount"
|
|
|
+ :min="0"
|
|
|
+ :max="maxLoanAmount"
|
|
|
+ :step="10000"
|
|
|
+ class="loan-slider"
|
|
|
+ />
|
|
|
+ <span class="value">{{ formatCurrency(loanAmount) }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="action-buttons">
|
|
|
+ <button @click="submitApplication" class="primary-btn">
|
|
|
+ 提交申请
|
|
|
+ </button>
|
|
|
+ <button @click="cancelApplication" class="secondary-btn">
|
|
|
+ 取消
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </section>
|
|
|
+
|
|
|
+ <!-- 弹窗组件 -->
|
|
|
+ <div v-if="showModal" class="modal">
|
|
|
+ <div class="modal-content">
|
|
|
+ <span class="close-btn" @click="closeModal">×</span>
|
|
|
+ <h2>{{ modalTitle }}</h2>
|
|
|
+ <p>{{ modalMessage }}</p>
|
|
|
+ <div class="modal-buttons">
|
|
|
+ <button @click="confirmModal" class="primary-btn">确认</button>
|
|
|
+ <button v-if="!hideCancel" @click="closeModal" class="secondary-btn">
|
|
|
+ 取消
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import L from "leaflet";
|
|
|
+import "leaflet/dist/leaflet.css";
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: "FreightFinance",
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ selectedVoyage: null,
|
|
|
+ showVoyageDetail: false,
|
|
|
+ showModal: false,
|
|
|
+ modalTitle: "",
|
|
|
+ modalMessage: "",
|
|
|
+ hideCancel: false,
|
|
|
+ confirmCallback: null,
|
|
|
+ loanAmount: 480000,
|
|
|
+ map: null,
|
|
|
+ shipperList: [
|
|
|
+ {
|
|
|
+ id: 1,
|
|
|
+ name: "上海海运有限公司",
|
|
|
+ validUntil: "2023-12-31",
|
|
|
+ totalLimit: 5000000,
|
|
|
+ usedLimit: 2300000,
|
|
|
+ remainingLimit: 2700000,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 2,
|
|
|
+ name: "江苏货运集团",
|
|
|
+ validUntil: "2024-06-30",
|
|
|
+ totalLimit: 3000000,
|
|
|
+ usedLimit: 1200000,
|
|
|
+ remainingLimit: 1800000,
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ availableVoyages: [
|
|
|
+ {
|
|
|
+ id: "V20230001",
|
|
|
+ voyageNumber: "V20230001",
|
|
|
+ shipperName: "上海海运有限公司",
|
|
|
+ cargoType: "煤炭",
|
|
|
+ weight: 5000,
|
|
|
+ rate: 120,
|
|
|
+ totalFreight: 600000,
|
|
|
+ status: "运输中",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: "V20230002",
|
|
|
+ voyageNumber: "V20230002",
|
|
|
+ shipperName: "上海海运有限公司",
|
|
|
+ cargoType: "铁矿石",
|
|
|
+ weight: 8000,
|
|
|
+ rate: 150,
|
|
|
+ totalFreight: 1200000,
|
|
|
+ status: "运输中",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: "V20230003",
|
|
|
+ voyageNumber: "V20230003",
|
|
|
+ shipperName: "江苏货运集团",
|
|
|
+ cargoType: "粮食",
|
|
|
+ weight: 3000,
|
|
|
+ rate: 180,
|
|
|
+ totalFreight: 540000,
|
|
|
+ status: "运输中",
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ loanVoyages: [
|
|
|
+ {
|
|
|
+ id: 1,
|
|
|
+ voyageNumber: "V20230004",
|
|
|
+ shipperName: "上海海运有限公司",
|
|
|
+ cargoType: "原油",
|
|
|
+ weight: 10000,
|
|
|
+ rate: 200,
|
|
|
+ totalFreight: 2000000,
|
|
|
+ loanAmount: 1600000,
|
|
|
+ applicationDate: "2023-09-15",
|
|
|
+ loanStatus: "审核中",
|
|
|
+ repaymentStatus: "未还款",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 2,
|
|
|
+ voyageNumber: "V20230005",
|
|
|
+ shipperName: "江苏货运集团",
|
|
|
+ cargoType: "钢材",
|
|
|
+ weight: 6000,
|
|
|
+ rate: 180,
|
|
|
+ totalFreight: 1080000,
|
|
|
+ loanAmount: 800000,
|
|
|
+ applicationDate: "2023-09-10",
|
|
|
+ loanStatus: "已放款",
|
|
|
+ repaymentStatus: "部分还款",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 3,
|
|
|
+ voyageNumber: "V20230006",
|
|
|
+ shipperName: "上海海运有限公司",
|
|
|
+ cargoType: "集装箱",
|
|
|
+ weight: 4500,
|
|
|
+ rate: 220,
|
|
|
+ totalFreight: 990000,
|
|
|
+ loanAmount: 750000,
|
|
|
+ applicationDate: "2023-09-01",
|
|
|
+ loanStatus: "已放款",
|
|
|
+ repaymentStatus: "已还清",
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ voyageDetail: {
|
|
|
+ voyageNumber: "V20230001",
|
|
|
+ shipperName: "上海海运有限公司",
|
|
|
+ startDate: "2023-09-10",
|
|
|
+ loadingPort: "青岛港",
|
|
|
+ unloadingPort: "上海港",
|
|
|
+ shipName: "远洋号",
|
|
|
+ shipOwner: "中远海运",
|
|
|
+ shipMMSI: "123456789",
|
|
|
+ cargoType: "煤炭",
|
|
|
+ weight: 5000,
|
|
|
+ rate: 120,
|
|
|
+ totalFreight: 600000,
|
|
|
+ arrivalTime: "2023-09-10 08:00",
|
|
|
+ loadingStart: "2023-09-10 10:00",
|
|
|
+ loadingEnd: "2023-09-11 16:00",
|
|
|
+ departureTime: "2023-09-11 18:00",
|
|
|
+ documents: [
|
|
|
+ { id: 1, name: "当月运输合同" },
|
|
|
+ { id: 2, name: "航次装货运单" },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ };
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ maxLoanAmount() {
|
|
|
+ return Math.floor(this.voyageDetail.totalFreight * 0.8);
|
|
|
+ },
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ this.$nextTick(() => {
|
|
|
+ if (this.showVoyageDetail) {
|
|
|
+ this.initMap();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ formatCurrency(value) {
|
|
|
+ return parseFloat(value).toLocaleString("zh-CN") + "元";
|
|
|
+ },
|
|
|
+ getRepaymentStatusClass(status) {
|
|
|
+ switch (status) {
|
|
|
+ case "未还款":
|
|
|
+ return "status-unpaid";
|
|
|
+ case "部分还款":
|
|
|
+ return "status-partial";
|
|
|
+ case "已还清":
|
|
|
+ return "status-paid";
|
|
|
+ default:
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ },
|
|
|
+ applyLoan() {
|
|
|
+ if (!this.selectedVoyage) {
|
|
|
+ this.showModalDialog("提示", "请先选择一个航次", true);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.showVoyageDetail = true;
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.initMap();
|
|
|
+ // 滚动到航次详情区域
|
|
|
+ const element = document.querySelector(".section:last-of-type");
|
|
|
+ if (element) {
|
|
|
+ element.scrollIntoView({ behavior: "smooth" });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ submitApplication() {
|
|
|
+ this.showModalDialog(
|
|
|
+ "确认提交",
|
|
|
+ `您确定要申请 ${this.formatCurrency(this.loanAmount)} 的贷款吗?`,
|
|
|
+ false,
|
|
|
+ () => {
|
|
|
+ this.showModalDialog(
|
|
|
+ "提交成功",
|
|
|
+ "您的贷款申请已提交,请等待审核。",
|
|
|
+ true,
|
|
|
+ () => {
|
|
|
+ this.resetForm();
|
|
|
+ }
|
|
|
+ );
|
|
|
+ }
|
|
|
+ );
|
|
|
+ },
|
|
|
+ cancelApplication() {
|
|
|
+ this.showVoyageDetail = false;
|
|
|
+ this.selectedVoyage = null;
|
|
|
+ },
|
|
|
+ resetForm() {
|
|
|
+ this.showVoyageDetail = false;
|
|
|
+ this.selectedVoyage = null;
|
|
|
+ this.loanAmount = 480000;
|
|
|
+ },
|
|
|
+ showModalDialog(
|
|
|
+ title,
|
|
|
+ message,
|
|
|
+ hideCancel = false,
|
|
|
+ confirmCallback = null
|
|
|
+ ) {
|
|
|
+ this.modalTitle = title;
|
|
|
+ this.modalMessage = message;
|
|
|
+ this.hideCancel = hideCancel;
|
|
|
+ this.confirmCallback = confirmCallback;
|
|
|
+ this.showModal = true;
|
|
|
+ },
|
|
|
+ closeModal() {
|
|
|
+ this.showModal = false;
|
|
|
+ },
|
|
|
+ confirmModal() {
|
|
|
+ this.showModal = false;
|
|
|
+ if (this.confirmCallback) {
|
|
|
+ this.confirmCallback();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ initMap() {
|
|
|
+ if (this.map) {
|
|
|
+ this.map.remove();
|
|
|
+ }
|
|
|
+
|
|
|
+ this.$nextTick(() => {
|
|
|
+ const mapElement = this.$refs.voyageMap;
|
|
|
+ if (!mapElement) return;
|
|
|
+
|
|
|
+ // 创建地图实例
|
|
|
+ this.map = L.map(mapElement).setView([35.5, 120], 6);
|
|
|
+
|
|
|
+ // 添加地图图层
|
|
|
+ L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
|
|
|
+ attribution:
|
|
|
+ '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
|
|
+ }).addTo(this.map);
|
|
|
+
|
|
|
+ // 示例航线数据
|
|
|
+ const routePoints = [
|
|
|
+ [36.0839, 120.3676], // 青岛港
|
|
|
+ [35.8, 121.5],
|
|
|
+ [34.7, 122.8],
|
|
|
+ [33.5, 123.5],
|
|
|
+ [32.0, 123.8],
|
|
|
+ [31.2304, 121.4737], // 上海港
|
|
|
+ ];
|
|
|
+
|
|
|
+ // 添加航线
|
|
|
+ const polyline = L.polyline(routePoints, {
|
|
|
+ color: "#1e88e5",
|
|
|
+ weight: 3,
|
|
|
+ }).addTo(this.map);
|
|
|
+
|
|
|
+ // 添加起点和终点标记
|
|
|
+ const startIcon = L.divIcon({
|
|
|
+ className: "start-marker",
|
|
|
+ html: '<div style="background-color:#4CAF50;width:12px;height:12px;border-radius:50%;"></div>',
|
|
|
+ });
|
|
|
+ const endIcon = L.divIcon({
|
|
|
+ className: "end-marker",
|
|
|
+ html: '<div style="background-color:#F44336;width:12px;height:12px;border-radius:50%;"></div>',
|
|
|
+ });
|
|
|
+
|
|
|
+ L.marker(routePoints[0], { icon: startIcon })
|
|
|
+ .addTo(this.map)
|
|
|
+ .bindPopup("青岛港 - 装货港");
|
|
|
+
|
|
|
+ L.marker(routePoints[routePoints.length - 1], { icon: endIcon })
|
|
|
+ .addTo(this.map)
|
|
|
+ .bindPopup("上海港 - 卸货港");
|
|
|
+
|
|
|
+ // 添加船舶当前位置
|
|
|
+ const shipIcon = L.divIcon({
|
|
|
+ className: "ship-marker",
|
|
|
+ html: '<div style="background-color:#1e88e5;width:16px;height:16px;border-radius:50%;border:2px solid white;"></div>',
|
|
|
+ iconSize: [16, 16],
|
|
|
+ });
|
|
|
+
|
|
|
+ // 假设船舶在航线中间位置
|
|
|
+ const currentPosition = routePoints[Math.floor(routePoints.length / 2)];
|
|
|
+ L.marker(currentPosition, { icon: shipIcon })
|
|
|
+ .addTo(this.map)
|
|
|
+ .bindPopup("远洋号 - 当前位置");
|
|
|
+
|
|
|
+ // 调整地图视图以适应航线
|
|
|
+ this.map.fitBounds(polyline.getBounds(), { padding: [50, 50] });
|
|
|
+ });
|
|
|
+ },
|
|
|
+ },
|
|
|
+ beforeUnmount() {
|
|
|
+ if (this.map) {
|
|
|
+ this.map.remove();
|
|
|
+ }
|
|
|
+ },
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+/* 全局样式 */
|
|
|
+* {
|
|
|
+ margin: 0;
|
|
|
+ padding: 0;
|
|
|
+ box-sizing: border-box;
|
|
|
+ font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
|
|
|
+}
|
|
|
+
|
|
|
+.container {
|
|
|
+ max-width: 1200px;
|
|
|
+ margin: 0 auto;
|
|
|
+ padding: 20px;
|
|
|
+ color: #333;
|
|
|
+ line-height: 1.6;
|
|
|
+}
|
|
|
+
|
|
|
+/* 头部样式 */
|
|
|
+.header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ padding: 15px 0;
|
|
|
+ border-bottom: 1px solid #e0e6ed;
|
|
|
+ margin-bottom: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.logo {
|
|
|
+ font-size: 24px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #1e88e5;
|
|
|
+}
|
|
|
+
|
|
|
+.user-info {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.user-name {
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.verified-icon {
|
|
|
+ display: inline-block;
|
|
|
+ width: 20px;
|
|
|
+ height: 20px;
|
|
|
+ background-color: #4caf50;
|
|
|
+ color: white;
|
|
|
+ border-radius: 50%;
|
|
|
+ text-align: center;
|
|
|
+ line-height: 20px;
|
|
|
+ font-size: 12px;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+/* 区块样式 */
|
|
|
+.section {
|
|
|
+ background-color: #fff;
|
|
|
+ border-radius: 8px;
|
|
|
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
|
|
+ padding: 20px;
|
|
|
+ margin-bottom: 30px;
|
|
|
+}
|
|
|
+
|
|
|
+.section h2 {
|
|
|
+ font-size: 18px;
|
|
|
+ color: #333;
|
|
|
+ margin-bottom: 20px;
|
|
|
+ padding-bottom: 10px;
|
|
|
+ border-bottom: 1px solid #eee;
|
|
|
+}
|
|
|
+
|
|
|
+.section h3 {
|
|
|
+ font-size: 16px;
|
|
|
+ color: #555;
|
|
|
+ margin: 15px 0 10px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 表格样式 */
|
|
|
+table {
|
|
|
+ width: 100%;
|
|
|
+ border-collapse: collapse;
|
|
|
+ margin-bottom: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+table th,
|
|
|
+table td {
|
|
|
+ padding: 12px 15px;
|
|
|
+ text-align: left;
|
|
|
+ border-bottom: 1px solid #eee;
|
|
|
+}
|
|
|
+
|
|
|
+table th {
|
|
|
+ background-color: #f8f9fa;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #555;
|
|
|
+}
|
|
|
+
|
|
|
+table tr:hover {
|
|
|
+ background-color: #f5f7fa;
|
|
|
+}
|
|
|
+
|
|
|
+/* 还款状态样式 */
|
|
|
+.status-unpaid {
|
|
|
+ color: #f44336;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.status-partial {
|
|
|
+ color: #ff9800;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.status-paid {
|
|
|
+ color: #4caf50;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+/* 按钮样式 */
|
|
|
+.action-buttons {
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ gap: 15px;
|
|
|
+ margin-top: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.primary-btn,
|
|
|
+.secondary-btn,
|
|
|
+.view-btn {
|
|
|
+ padding: 8px 16px;
|
|
|
+ border-radius: 4px;
|
|
|
+ border: none;
|
|
|
+ cursor: pointer;
|
|
|
+ font-size: 14px;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.primary-btn {
|
|
|
+ background-color: #1e88e5;
|
|
|
+ color: white;
|
|
|
+}
|
|
|
+
|
|
|
+.primary-btn:hover {
|
|
|
+ background-color: #1976d2;
|
|
|
+}
|
|
|
+
|
|
|
+.secondary-btn {
|
|
|
+ background-color: #f5f5f5;
|
|
|
+ color: #333;
|
|
|
+ border: 1px solid #ddd;
|
|
|
+}
|
|
|
+
|
|
|
+.secondary-btn:hover {
|
|
|
+ background-color: #e0e0e0;
|
|
|
+}
|
|
|
+
|
|
|
+.view-btn {
|
|
|
+ background-color: #f0f7ff;
|
|
|
+ color: #1e88e5;
|
|
|
+ border: 1px solid #d0e3ff;
|
|
|
+}
|
|
|
+
|
|
|
+.view-btn:hover {
|
|
|
+ background-color: #e1f0ff;
|
|
|
+}
|
|
|
+
|
|
|
+/* 航次详情样式 */
|
|
|
+.voyage-info {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.info-section {
|
|
|
+ margin-bottom: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.info-grid {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
|
|
+ gap: 15px;
|
|
|
+}
|
|
|
+
|
|
|
+.info-item {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 5px;
|
|
|
+}
|
|
|
+
|
|
|
+.label {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #666;
|
|
|
+}
|
|
|
+
|
|
|
+.value {
|
|
|
+ font-size: 15px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+
|
|
|
+/* 地图容器 */
|
|
|
+.map-container {
|
|
|
+ margin: 20px 0;
|
|
|
+}
|
|
|
+
|
|
|
+#voyage-map {
|
|
|
+ height: 300px;
|
|
|
+ border-radius: 8px;
|
|
|
+ border: 1px solid #eee;
|
|
|
+}
|
|
|
+
|
|
|
+/* 文档确认区域 */
|
|
|
+.document-list {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 15px;
|
|
|
+}
|
|
|
+
|
|
|
+.document-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 15px;
|
|
|
+ padding: 10px;
|
|
|
+ background-color: #f9f9f9;
|
|
|
+ border-radius: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.document-name {
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.confirm-status {
|
|
|
+ color: #4caf50;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+/* 贷款申请区域 */
|
|
|
+.loan-application {
|
|
|
+ background-color: #f8f9fa;
|
|
|
+ padding: 20px;
|
|
|
+ border-radius: 8px;
|
|
|
+ margin-top: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.loan-amount-container {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 15px;
|
|
|
+ margin-bottom: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.loan-info {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.loan-slider-container {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 15px;
|
|
|
+ margin-top: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.loan-slider {
|
|
|
+ flex: 1;
|
|
|
+ height: 5px;
|
|
|
+ -webkit-appearance: none;
|
|
|
+ appearance: none;
|
|
|
+ background: #ddd;
|
|
|
+ outline: none;
|
|
|
+ border-radius: 5px;
|
|
|
+}
|
|
|
+
|
|
|
+.loan-slider::-webkit-slider-thumb {
|
|
|
+ -webkit-appearance: none;
|
|
|
+ appearance: none;
|
|
|
+ width: 20px;
|
|
|
+ height: 20px;
|
|
|
+ border-radius: 50%;
|
|
|
+ background: #1e88e5;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+/* 弹窗样式 */
|
|
|
+.modal {
|
|
|
+ position: fixed;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ background-color: rgba(0, 0, 0, 0.5);
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ z-index: 1000;
|
|
|
+}
|
|
|
+
|
|
|
+.modal-content {
|
|
|
+ background-color: #fff;
|
|
|
+ padding: 30px;
|
|
|
+ border-radius: 8px;
|
|
|
+ width: 90%;
|
|
|
+ max-width: 500px;
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+
|
|
|
+.close-btn {
|
|
|
+ position: absolute;
|
|
|
+ top: 15px;
|
|
|
+ right: 15px;
|
|
|
+ font-size: 24px;
|
|
|
+ cursor: pointer;
|
|
|
+ color: #999;
|
|
|
+}
|
|
|
+
|
|
|
+.modal-buttons {
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ gap: 15px;
|
|
|
+ margin-top: 20px;
|
|
|
+}
|
|
|
+</style>
|