| | |
| | | "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", |
New file |
| | |
| | | <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> |
| | |
| | | <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 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: {}, |
| | |
| | | </el-form-item>
|
| | | </el-col>
|
| | | </el-row>
|
| | | |
| | | </el-form>
|
| | | <div class="btns">
|
| | | <el-button @click="$emit('close')">关闭 | Close</el-button>
|
| | |
| | | }
|
| | | },
|
| | | methods: {
|
| | | // 验证确认密码
|
| | | validateConfirmPassword(rule, value, callback) {
|
| | | if (value !== this.form.newPassword) {
|
| | | callback(new Error('两次输入的密码不一致'));
|
| | | } else {
|
| | | callback();
|
| | | }
|
| | | },
|
| | | // 提交
|
| | | submit() {
|
| | | this.$refs.form.validate((valid) => {
|
| | |
| | | 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 = ''
|
| | | })
|
| | | }
|
| | | })
|
| | |
| | | config.data = {
|
| | | ...config.data,
|
| | | }
|
| | | // console.log(config.data);
|
| | | |
| | | }
|
| | | return config
|
| | | },
|
| | |
| | | </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> |
| | |
| | | </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> |
| | |
| | | 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: { |
| | |
| | | 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() { |
| | |
| | | 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> |
| | |
| | | } |
| | | </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; |
| | | |
| | |
| | | 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) |
| | | } |
| | |
| | | export const getUserInfo = (data) => { |
| | | return axios.post('/zj0FFk5wx4S05X-zlIX6NdPQS80X0-1', data) |
| | | } |
| | | |
| | | //修改密码 |
| | | export const changePwd = (data) => { |
| | | return axios.post('/zj0FFk5wx4S05X-zlW1SWYMtwe0Xu-1', data) |
| | | } |
| | |
| | | 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", |
| | |
| | | }, |
| | | } |
| | | }, |
| | | |
| | | lintOnSave: false, |
| | | css: { |
| | | loaderOptions: { |
| | | postcss: { |