pyt
2025-04-03 6d26a6b3397f6433fe070de72381887151950158
feat
9个文件已修改
1个文件已添加
43743 ■■■■■ 已修改文件
package-lock.json 43276 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/component/ChangePassword.vue 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/component/Header.vue 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/component/userInfo.vue 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/request.js 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/view/Home.vue 220 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/view/home.js 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/view/service.js 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vue.config.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package-lock.json
Diff too large
package.json
@@ -22,7 +22,8 @@
    "vue-quill-editor": "^3.0.6",
    "vue-router": "^3.6.5",
    "vue-ueditor-wrap": "^2.5.6",
    "vuex-persistedstate": "^4.1.0"
    "vuex-persistedstate": "^4.1.0",
    "xlsx": "^0.18.5"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "^3.11.0",
src/component/ChangePassword.vue
New file
@@ -0,0 +1,174 @@
<template>
    <el-dialog :visible.sync="show" :show-close="false" width="500px" :close-on-click-modal="false" append-to-body>
        <div class="content">
            <el-form label-position="left" label-width="120px" ref="form" :model="form" :rules="rules">
                <div class="title">修改密码 | Change Password</div>
                <el-row :gutter="20" class="form-row">
                    <el-col :span="24">
                        <el-form-item label="原密码:" prop="oldPassword">
                            <template #label>
                                <div class="form-label">
                                    <div>原密码</div>
                                    <div class="label-en">Current Password</div>
                                </div>
                            </template>
                            <el-input v-model="form.oldPassword" type="password" placeholder="请输入"></el-input>
                        </el-form-item>
                    </el-col>
                    <el-col :span="24">
                        <el-form-item label="新密码:" prop="newPassword">
                            <template #label>
                                <div class="form-label">
                                    <div>新密码</div>
                                    <div class="label-en">New Password</div>
                                </div>
                            </template>
                            <el-input v-model="form.newPassword" type="password" placeholder="请输入"></el-input>
                        </el-form-item>
                    </el-col>
                    <el-col :span="24">
                        <el-form-item label="确认密码:" prop="confirmPassword">
                            <template #label>
                                <div class="form-label">
                                    <div>确认密码</div>
                                    <div class="label-en">Confirm Password</div>
                                </div>
                            </template>
                            <el-input v-model="form.confirmPassword" type="password" placeholder="请输入"></el-input>
                        </el-form-item>
                    </el-col>
                </el-row>
            </el-form>
            <div class="btns">
                <el-button @click="$emit('close')">取消 | Cancel</el-button>
                <el-button type="primary" @click="submit">确定 | Confirm</el-button>
            </div>
        </div>
    </el-dialog>
</template>
<script>
import { changePwd } from '@/view/service'
import CryptoJS from 'crypto-js';
export default {
    props: ['show'],
    data() {
        return {
            form: {
                oldPassword: '',
                newPassword: '',
                confirmPassword: ''
            },
            rules: {
                oldPassword: [
                    { required: true, message: '请输入原密码', trigger: 'change' }
                ],
                newPassword: [
                    { required: true, message: '请输入新密码', trigger: 'change' },
                    { min: 6, message: '密码长度不能小于6位', trigger: 'change' }
                ],
                confirmPassword: [
                    { required: true, message: '请确认新密码', trigger: 'change' },
                    { validator: this.validateConfirmPassword, trigger: 'change' }
                ]
            }
        }
    },
    methods: {
        validateConfirmPassword(rule, value, callback) {
            if (value !== this.form.newPassword) {
                callback(new Error('两次输入的密码不一致'));
            } else {
                callback();
            }
        },
        submit() {
            this.$refs.form.validate((valid) => {
                if (valid) {
                    let params = {
                        extra: localStorage.getItem('extra'),
                        oldPwd: CryptoJS.MD5(this.form.oldPassword).toString(),
                        newPwd: CryptoJS.MD5(this.form.newPassword).toString(),
                    }
                    changePwd(params).then(res => {
                        this.$message.success('密码修改成功')
                        this.$emit('close')
                    })
                }
            })
        }
    }
}
</script>
<style lang="less" scoped>
.content {
    .title {
        padding: 31px 0;
        text-align: center;
        font-size: 18px;
        font-weight: bold;
        color: #3B3F56;
        line-height: 27px;
    }
    .form-row {
        padding: 0 20px;
    }
    .btns {
        display: flex;
        justify-content: center;
        margin-top: 32px;
        padding-bottom: 33px;
        .el-button {
            width: 190px;
            height: 50px;
            font-size: 20px;
            margin: 0 10px;
        }
        .el-button--primary {
            background-color: #014099;
            border-color: #014099;
        }
    }
}
/deep/ .el-dialog {
    border-radius: 8px;
    .el-dialog__header {
        display: none !important;
    }
    .el-dialog__body {
        padding: 0 !important;
    }
}
/deep/ .el-form {
    .el-form-item__label {
        font-size: 15px;
        color: #3B3F56;
        font-weight: 500;
        display: flex;
    }
    .form-label {
        white-space: normal;
        line-height: 1.2;
        display: flex;
        flex-direction: column;
        .label-en {
            font-size: 12px;
            color: #999;
            white-space: nowrap;
        }
    }
}
</style>
src/component/Header.vue
@@ -3,32 +3,42 @@
        <div class="header_content">
            <div @click="$router.push('/home')" class="color1 fs--24 font-bold pointer">{{ userInfo.companyName }}</div>
            <div class="flex a-center j-between">
                <div @click="userInfoShow = true" class="flex a-center mr--55 shrink0 pointer">
                    <div class="mr--24 fs--20 font-bold shrink0 color2">{{ $store.state.userName }}</div>
                    <img src="../assets/img/bianji@2x.png" class="w--23 h--23" />
                <div class="flex a-center">
                    <div @click="userInfoShow = true" class="flex a-center mr--55 shrink0 pointer">
                        <div class="mr--24 fs--20 font-bold shrink0 color2">{{ $store.state.userName }}</div>
                        <img src="../assets/img/bianji@2x.png" class="w--23 h--23" />
                    </div>
                    <div @click="changePasswordShow = true" class="flex a-center mr--55 shrink0 pointer">
                        <div class="mr--24 fs--20 font-bold shrink0 color2">修改密码</div>
                        <img src="../assets/img/bianji@2x.png" class="w--23 h--23" />
                    </div>
                </div>
                <img @click="logOutShow = true" src="../assets/img/tuichu@2x.png" class="w--40 h--40 shrink0 pointer" />
            </div>
        </div>
        <LogOutComponent v-if="logOutShow" :show="logOutShow" @close="logOutShow = false" />
        <UserInfoComponent v-if="userInfoShow" :show="userInfoShow" @close="userInfoShow = false" />
        <ChangePassword v-if="changePasswordShow" :show="changePasswordShow" @close="changePasswordShow = false" />
    </div>
</template>
<script>
import UserInfoComponent from '@/component/userInfo.vue'
import LogOutComponent from '@/component/LogOut.vue'
import ChangePassword from '@/component/ChangePassword.vue'
export default {
    components: {
        UserInfoComponent,
        LogOutComponent
        LogOutComponent,
        ChangePassword
    },
    props: {},
    data() {
        return {
            userInfo: JSON.parse(localStorage.getItem('userInfo')),
            logOutShow: false,
            userInfoShow: false
            userInfoShow: false,
            changePasswordShow: false
        };
    },
    computed: {},
src/component/userInfo.vue
@@ -184,6 +184,7 @@
                        </el-form-item>
                    </el-col>
                </el-row>
            </el-form>
            <div class="btns">
                <el-button @click="$emit('close')">关闭 | Close</el-button>
@@ -229,6 +230,14 @@
        }
    },
    methods: {
        // 验证确认密码
        validateConfirmPassword(rule, value, callback) {
            if (value !== this.form.newPassword) {
                callback(new Error('两次输入的密码不一致'));
            } else {
                callback();
            }
        },
        // 提交
        submit() {
            this.$refs.form.validate((valid) => {
@@ -243,9 +252,22 @@
                        emails: this.form.otherEmails,
                        addressInfos: this.form.contactInfoList
                    }
                    // 如果有修改密码,添加密码相关参数
                    if (this.form.oldPassword && this.form.newPassword) {
                        params.password = {
                            oldPassword: this.form.oldPassword,
                            newPassword: this.form.newPassword
                        }
                    }
                    saveUserInfo(params).then(res => {
                        this.$message.success('保存成功')
                        this.$store.commit('SET_USERNAME', this.form.userName)
                        // 清空密码字段
                        this.form.oldPassword = ''
                        this.form.newPassword = ''
                        this.form.confirmPassword = ''
                    })
                }
            })
src/utils/request.js
@@ -43,6 +43,8 @@
      config.data = {
        ...config.data,
      }
      // console.log(config.data);
    }
    return config
  },
src/view/Home.vue
@@ -112,6 +112,10 @@
            </div>
        </div>
        <div class="table-box">
            <div class="flex a-center p-3">
                <el-button type="primary" class="search" @click="exportExcel">导出 | Export</el-button>
                <el-button type="primary" class="search" @click="importExcel">导入 | Import</el-button>
            </div>
            <el-table :data="tableData" style="width: 100%" border>
                <el-table-column width="200" prop="orderId" align="center">
                    <template #header>
@@ -153,7 +157,7 @@
                        </div>
                    </template>
                </el-table-column>
                <el-table-column width="200" prop="estinatedArrival" align="center">
                <el-table-column width="200" prop="estinatedArrival" align="center" sortable>
                    <template #header>
                        <div class="custom-header">
                            <div>预计到港</div>
@@ -275,7 +279,8 @@
import UserInfo from '@/component/userInfo.vue'
import Header from '@/component/Header.vue'
import NoticeComponent from '@/component/Notice.vue'
import { getList } from './home'
import { getList, iptOrder, exportList } from './home'
import * as XLSX from 'xlsx'
export default {
    name: "Home",
    components: {
@@ -421,7 +426,9 @@
                delete params.status
            }
            getList({ ...params, }).then(res => {
                this.tableData = res.data
                this.tableData = res.data.sort((a, b) => {
                    return new Date(a.estinatedArrival) - new Date(b.estinatedArrival);
                });
            })
        },
        search() {
@@ -439,7 +446,201 @@
                extra: JSON.parse(localStorage.getItem('userInfo')).extra
            }
            this.getLists()
        }
        },
        // 导出Excel
        exportExcel() {
            exportList({ userExtra: localStorage.getItem('extra') }).then(res => {
                // 准备导出数据
                const exportData = res.data.map(item => ({
                    '集装箱号 | Container No.': item.ContainerNo,
                    '提单号 | BOL No.': item.BolNo,
                    '柜型 | Container Type': item.containerType,
                    '船司 | Carrier': item.carrier,
                    '船名航次 | Vessel Name and Voyage': item.vesselNameAndVoyage,
                    '提柜地 | Pickup Location': item.pickupLocation,
                    '预计到港 | ETA': item.eta,
                    'SOC': item.soc,
                    'DG': item.dg,
                    '是否超重 | Overweight': item.overweight,
                    '是否查验 | Exam/Inspection': item.exam,
                    'DROP/LIVE': item.dropOrLive,
                    'Hold': item.hold,
                    '客户单号 | Customer Ref. No.': item.customerRefNo,
                    '品名 | Commodity': item.commodity,
                    '数量 | QTY': item.qty,
                    '包装种类 | Packages': item.packages,
                    '毛重 | Gr. Wt': item.GrWt,
                    '毛重(磅) | Weight (LBs)': item.weightLBS,
                    '体积 | Volume': item.volume,
                    '收件人 | Consignee': item.consignee,
                    '收件人公司 | Company Name': item.companyName,
                    '邮编 | Postal Code': item.postalCode,
                    '地址 | Address': item.address,
                    '联系电话 | Tel': item.tel,
                    '邮箱 | Email': item.email,
                    '其他联系方式 | Other Contact Information': item.otherContactInformation,
                    '备注 | Remarks': item.remarks
                }));
                // 创建工作簿
                const wb = XLSX.utils.book_new();
                const ws = XLSX.utils.json_to_sheet(exportData);
                // 设置列宽
                const colWidths = [
                    { wch: 25 }, // 集装箱号
                    { wch: 25 }, // 提单号
                    { wch: 20 }, // 柜型
                    { wch: 20 }, // 船司
                    { wch: 30 }, // 船名航次
                    { wch: 25 }, // 提柜地
                    { wch: 25 }, // 预计到港
                    { wch: 15 }, // SOC
                    { wch: 15 }, // DG
                    { wch: 20 }, // 是否超重
                    { wch: 20 }, // 是否查验
                    { wch: 20 }, // DROP/LIVE
                    { wch: 25 }, // Hold
                    { wch: 25 }, // 客户单号
                    { wch: 30 }, // 品名
                    { wch: 15 }, // 数量
                    { wch: 20 }, // 包装种类
                    { wch: 15 }, // 毛重
                    { wch: 20 }, // 毛重(磅)
                    { wch: 15 }, // 体积
                    { wch: 20 }, // 收件人
                    { wch: 25 }, // 收件人公司
                    { wch: 15 }, // 邮编
                    { wch: 35 }, // 地址
                    { wch: 20 }, // 联系电话
                    { wch: 25 }, // 邮箱
                    { wch: 30 }, // 其他联系方式
                    { wch: 35 }  // 备注
                ];
                ws['!cols'] = colWidths;
                // 将工作表添加到工作簿
                XLSX.utils.book_append_sheet(wb, ws, "订单列表");
                // 导出文件
                XLSX.writeFile(wb, `订单列表_${new Date().toLocaleDateString()}.xlsx`);
            })
        },
        // 导入Excel
        importExcel() {
            // 创建文件输入元素
            const input = document.createElement('input');
            input.type = 'file';
            input.accept = '.xlsx,.xls';
            input.onchange = (e) => {
                const file = e.target.files[0];
                const reader = new FileReader();
                reader.onload = (e) => {
                    try {
                        const data = new Uint8Array(e.target.result);
                        const workbook = XLSX.read(data, { type: 'array' });
                        const firstSheetName = workbook.SheetNames[0];
                        const worksheet = workbook.Sheets[firstSheetName];
                        // 使用header选项来获取原始表头
                        const jsonData = XLSX.utils.sheet_to_json(worksheet, {
                            raw: true,
                            defval: '',
                            header: 1
                        });
                        // 获取表头并打印日志
                        const headers = jsonData[0];
                        // 验证模板格式
                        const requiredFields = [
                            '客户单号', '集装箱号', '柜型Type', '提单号', 'SOC', 'DG', 'DG级别',
                            'drop/live', 'hold', '是否查验', '是否超重', '品名', '数量', '包装种类',
                            '毛重', '体积', '船司', '船名航次', 'ETA ', '提柜地',
                            '收件人', '收件人公司', '地址', '邮编', '联系电话', '邮箱', '其他联系方式', '备注'
                        ];
                        // 检查表头是否匹配
                        const missingFields = requiredFields.filter(field => !headers.includes(field));
                        if (missingFields.length > 0) {
                            this.$message.error(`模板格式不正确,缺少以下字段:${missingFields.join(', ')}`);
                            return;
                        }
                        // 转换数据格式
                        const transformedData = jsonData.slice(1).map(row => {
                            const item = {};
                            headers.forEach((header, index) => {
                                item[header] = row[index] || '';
                            });
                            return {
                                userExtra: JSON.parse(localStorage.getItem('userInfo')).extra,
                                ContainerNo: item['集装箱号'],
                                BolNo: item['提单号'],
                                containerType: item['柜型Type'],
                                carrier: item['船司'],
                                vesselNameAndVoyage: item['船名航次'],
                                pickupLocation: item['提柜地'],
                                eta: item['ETA '],
                                soc: item['SOC'],
                                dg: item['DG'],
                                overweight: item['是否超重'],
                                exam: item['是否查验'],
                                dropOrLive: item['drop/live'],
                                hold: item['hold'],
                                customerRefNo: item['客户单号'],
                                commodity: item['品名'],
                                qty: item['数量'],
                                packages: item['包装种类'],
                                GrWt: item['毛重'],
                                weightLBS: item['毛重_1'],
                                volume: item['体积'],
                                consignee: item['收件人'],
                                companyName: item['收件人公司'],
                                postalCode: item['邮编'],
                                address: item['地址'],
                                tel: item['联系电话'],
                                email: item['邮箱'],
                                otherContactInformation: item['其他联系方式'],
                                remarks: item['备注']
                            };
                        });
                        // 去掉第一项
                        const finalData = [...headers, ...transformedData.map(Object.values)];
                        console.log('转换后的数据:', finalData);
                        // 使用post方法直接传递数组
                        iptOrder(finalData).then(res => {
                            if (res) {
                                this.$message({
                                    message: '导入成功',
                                    type: 'success'
                                });
                                this.getLists();
                            } else {
                                this.$message({
                                    message: '导入失败',
                                    type: 'warning'
                                });
                            }
                        }).catch(error => {
                            this.$message.error('导入失败,请检查文件格式是否正确');
                        });
                    } catch (error) {
                        console.error('导入失败:', error);
                        this.$message.error('导入失败,请检查文件格式是否正确');
                    }
                };
                reader.readAsArrayBuffer(file);
            };
            input.click();
        },
    }
};
</script>
@@ -449,6 +650,17 @@
}
</style>
<style scoped lang="less">
.search {
    width: 190px;
    height: 50px;
    background: #014099;
    border-radius: 4px;
    font-family: SourceHanSansCN, SourceHanSansCN;
    font-weight: 500;
    font-size: 20px;
    color: #FFFFFF;
}
.search-box {
    background-color: #fff;
src/view/home.js
@@ -4,4 +4,19 @@
//订单列表
export const getList = (data) => {
    return axios.post('/zj0FFk5wx4S05X-zksSUHaFEdk0Xv-1', data)
}
//导入订单
export const iptOrder = (data) => {
    return axios({
        method: 'post',
        url: '/zj0FFk5wx4S05X-znUvc7QkcTI0XM-1',
        data,
        headers: {
            'Content-Type': 'application/json'
        }
    })
}
//订单列表
export const exportList = (data) => {
    return axios.post('/zj0FFk5wx4S05X-znWZcjtBQbg0XT-1', data)
}
src/view/service.js
@@ -24,4 +24,9 @@
//获取用户信息
export const getUserInfo = (data) => {
    return axios.post('/zj0FFk5wx4S05X-zlIX6NdPQS80X0-1', data)
}
//修改密码
export const changePwd = (data) => {
    return axios.post('/zj0FFk5wx4S05X-zlW1SWYMtwe0Xu-1', data)
}
vue.config.js
@@ -1,11 +1,9 @@
module.exports = {
  publicPath: './',
  // publicPath: process.env.NODE_ENV === 'production' ? './' : '/',
  outputDir: '美国集装箱',
  devServer: {
    disableHostCheck: true,
  },
  lintOnSave: false,
  devServer: {
    proxy: { //跨域配置
      "/api": { //是自定义的本地请求时的名字
        // target: "http://192.168.110.64:8000",
@@ -17,7 +15,7 @@
      },
    }
  },
  lintOnSave: false,
  css: {
    loaderOptions: {
      postcss: {