提交 188f2da5 authored 作者: lidongxu's avatar lidongxu

Merge branch 'release' into dev

......@@ -16,11 +16,11 @@ VITE_APP_REDIRECT_URL = 'http://localhost:8085'
# 积木报表服务地址
VITE_APP_REPORT_URL = 'https://sfa-qa.wxl66.cn'
# 积木报表预览地址
VITE_APP_REPORT_PREVIEW_URL = '/report/jmreport/view'
VITE_APP_REPORT_PREVIEW_URL = '#/report/jmreport/view'
# 积木报表分享预览地址
VITE_APP_REPORT_SHARE_PREVIEW_URL = '/report'
VITE_APP_REPORT_SHARE_PREVIEW_URL = '#/report'
# 积木报表编辑地址
VITE_APP_REPORT_EDIT_URL = '/report/jmreport/index'
VITE_APP_REPORT_EDIT_URL = '#/report/jmreport/index'
# 模板表格
VITE_APP_PLAN_TEMPLATE_EXCEL = 'https://link-promotion.oss-cn-shanghai.aliyuncs.com/file/%E6%96%B0%E5%A2%9E%E6%88%96%E4%BF%AE%E6%94%B9%E8%AE%A1%E5%88%92-%E6%A8%A1%E6%9D%BF4.0.xlsx'
......@@ -15,11 +15,11 @@ VITE_APP_REDIRECT_URL = 'https://sfa.wxl66.cn/link/'
# 积木报表服务地址
VITE_APP_REPORT_URL = 'https://sfa.wxl66.cn'
# 积木报表预览地址
VITE_APP_REPORT_PREVIEW_URL = '/report/jmreport/view'
VITE_APP_REPORT_PREVIEW_URL = '#/report/jmreport/view'
# 积木报表分享预览地址
VITE_APP_REPORT_SHARE_PREVIEW_URL = '/report'
VITE_APP_REPORT_SHARE_PREVIEW_URL = '#/report'
# 积木报表编辑地址
VITE_APP_REPORT_EDIT_URL = '/report/jmreport/index'
VITE_APP_REPORT_EDIT_URL = '#/report/jmreport/index'
# 模板表格
VITE_APP_PLAN_TEMPLATE_EXCEL = 'https://link-promotion.oss-cn-shanghai.aliyuncs.com/file/%E6%96%B0%E5%A2%9E%E6%88%96%E4%BF%AE%E6%94%B9%E8%AE%A1%E5%88%92-%E6%A8%A1%E6%9D%BF4.0.xlsx'
......@@ -14,11 +14,11 @@ VITE_APP_REDIRECT_URL = 'https://sfa-qa.wxl66.cn/'
# 积木报表服务地址
VITE_APP_REPORT_URL = 'https://sfa-qa.wxl66.cn'
# 积木报表预览地址
VITE_APP_REPORT_PREVIEW_URL = '/report/jmreport/view'
VITE_APP_REPORT_PREVIEW_URL = '#/report/jmreport/view'
# 积木报表分享预览地址
VITE_APP_REPORT_SHARE_PREVIEW_URL = '/report'
VITE_APP_REPORT_SHARE_PREVIEW_URL = '#/report'
# 积木报表编辑地址
VITE_APP_REPORT_EDIT_URL = '/report/jmreport/index'
VITE_APP_REPORT_EDIT_URL = '#/report/jmreport/index'
# 模板表格
VITE_APP_PLAN_TEMPLATE_EXCEL = 'https://link-promotion.oss-cn-shanghai.aliyuncs.com/file/%E6%96%B0%E5%A2%9E%E6%88%96%E4%BF%AE%E6%94%B9%E8%AE%A1%E5%88%92-%E6%A8%A1%E6%9D%BF4.0.xlsx'
<template>
<el-watermark :font="font"
:content="content"
class="wm-class">
<template v-if="!isInWhiteList">
<el-watermark :font="font"
:content="content"
class="wm-class">
<router-view />
</el-watermark>
</template>
<template v-else>
<router-view />
</el-watermark>
</template>
<back-to-up />
</template>
......@@ -11,6 +16,10 @@
import useSettingsStore from '@/store/modules/settings'
import { handleThemeStyle } from '@/utils/theme'
import useUserStore from '@/store/modules/user'
import { isWhiteList } from '@/permission'
const route = useRoute()
const isInWhiteList = computed(() => isWhiteList(route.path))
const content = computed(() => {
return useUserStore().userInfo.nickName + ' ' + useUserStore().userInfo.phone
......@@ -19,14 +28,14 @@ const isDark = computed(() => {
return useSettingsStore().isDark
})
const font = reactive({
color: 'rgba(0, 0, 0, .15)',
color: 'rgba(0, 0, 0, .05)',
})
watch(
isDark,
() => {
font.color = isDark.value
? 'rgba(255, 255, 255, 0.15)'
: 'rgba(0, 0, 0, .15)'
? 'rgba(255, 255, 255, 0.05)'
: 'rgba(0, 0, 0, .05)'
},
{
immediate: true,
......
......@@ -73,9 +73,14 @@ export function logout() {
// 刷新 token 有效期
export function refreshToken() {
export function refreshTokenAPI(refreshToken) {
return request({
url: '/auth/refresh',
method: 'post'
url: '/auth/refreshToken',
method: 'post',
headers: {
isToken: false, // 本次请求无需设置在请求拦截器设置请求头上的 token(容易把过期的 token 覆盖到 Authorization 了,而这次 Authorization 我带的是刷新的 refreshToken 给后端)
'Authorization': `Bearer ` + refreshToken
},
})
}
......@@ -14,9 +14,9 @@ import useVersionStore from '@/store/modules/version'
NProgress.configure({ showSpinner: false })
// 登录页,物流查询页
export const whiteList = ['/login', '/logistics']
const whiteList = ['/login', '/logistics']
const isWhiteList = (path) => {
export const isWhiteList = (path) => {
return whiteList.some(pattern => isPathMatch(pattern, path))
}
......
import { login, fsLogin, getInfo, qcLogin } from '@/api'
import { getToken, setToken, removeToken } from '@/utils/auth'
import { login, fsLogin, getInfo, qcLogin, refreshTokenAPI } from '@/api'
import { getToken, setToken, removeToken, getRefreshToken, setRefreshToken, removeRefreshToken } from '@/utils/auth'
import { isHttp, isEmpty } from "@/utils/validate"
import defAva from '@/assets/images/profile.jpg'
......@@ -8,6 +9,7 @@ export default defineStore(
{
state: () => ({
token: getToken(),
refreshToken: getRefreshToken(),
id: '',
name: '',
avatar: '',
......@@ -27,7 +29,9 @@ export default defineStore(
const { username, password } = data
login({ username, password }).then(res => {
this.token = res.data.access_token
this.refreshToken = res.data.refresh_token
setToken(res.data.access_token)
setRefreshToken(res.data.refresh_token)
resolve()
}).catch(error => {
reject(error)
......@@ -92,7 +96,13 @@ export default defineStore(
// reject(error)
// })
})
}
},
// 刷新token
async refreshTokenFn() {
const res = await refreshTokenAPI(this.refreshToken)
this.token = res.data.access_token
setToken(res.data.access_token)
},
},
getters: {
/**
......@@ -109,7 +119,7 @@ export default defineStore(
empId: state.userInfo.userId, // 员工 id
empNo: state.userInfo.userName, // 员工工号
empName: state.userInfo.nickName, // 员工昵称名字
}
}
}
}
})
......
......@@ -14,8 +14,8 @@ export default defineStore(
getVersion() {
return new Promise((resolve, reject) => {
getVersionAPI().then(res => {
this.versionChange = !(this.version === res.data[0].version)
this.version = res.data[0].version
this.versionChange = !(this.version === res.data[0]?.version)
this.version = res.data[0]?.version
// 组织版本内容数据
this.versionList = res.data[0]?.changes.map(o => {
return {
......
import Cookies from 'js-cookie'
const TokenKey = 'Admin-Token'
const refreshToken = 'admin-t'
export function getToken() {
return Cookies.get(TokenKey)
......@@ -13,3 +14,17 @@ export function setToken(token) {
export function removeToken() {
return Cookies.remove(TokenKey)
}
export function getRefreshToken() {
return Cookies.get(refreshToken)
}
export function setRefreshToken(token) {
return Cookies.set(refreshToken, token)
}
export function removeRefreshToken() {
return Cookies.remove(refreshToken)
}
......@@ -6,7 +6,7 @@ import { tansParams, blobValidate } from '@/utils/ruoyi'
import cache from '@/plugins/cache'
import { saveAs } from 'file-saver'
import useUserStore from '@/store/modules/user'
import { fsClientAuth, refreshToken } from '@/api'
import { fsClientAuth } from '@/api'
let downloadLoadingInstance;
// 是否显示重新登录
......@@ -81,7 +81,6 @@ service.interceptors.request.use(config => {
// 响应拦截器
service.interceptors.response.use(async res => {
// 未设置状态码则默认成功状态
const code = res.data.code || 200;
// 获取错误信息
......@@ -102,21 +101,29 @@ service.interceptors.response.use(async res => {
return service(res.config)
}
// PC/移动端刷新 token 有效期
// await refreshToken()
// await useUserStore().refreshTokenFn()
// return service(res.config)
if (!isRelogin.show) {
isRelogin.show = true;
ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => {
isRelogin.show = false;
useUserStore().logOut().then(() => {
location.href = '#/login';
})
}).catch(() => {
isRelogin.show = false;
});
}
ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => {
isRelogin.show = false;
useUserStore().logOut().then(() => {
location.href = '#/login';
})
}).catch(() => {
isRelogin.show = false;
});
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
} else if (code === 403) {
// 如果刷新 refreshToken 接口也报错了,证明需要重新登录
// ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => {
// isRelogin.show = false;
// useUserStore().logOut().then(() => {
// location.href = '#/login';
// })
// }).catch(() => {
// isRelogin.show = false;
// });
// return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
} else if (code === 500) {
ElMessage({ message: msg, type: 'error' })
return Promise.reject(new Error(msg))
......
<template>
<div class="app-container">
<div class="container">
<!-- 查询表单 -->
<el-form :model="queryParams"
inline
label-position="left">
<el-row>
<el-form-item label="安徽/北京单据编号"
prop="sentNo">
<el-input v-model="queryParams.sentNo"
clearable
placeholder="输入要查询的安徽/北京单独单号"
@input="getLogisticsReceiptList"
style="width: 250px;" />
</el-form-item>
<el-form-item label="快递单号"
prop="expressNo">
<el-input v-model="queryParams.expressNo"
clearable
placeholder="输入要查询的快递单号"
@input="getLogisticsReceiptList" />
</el-form-item>
<el-form-item label="DD 单号查询"
prop="ddNo">
<el-input v-model="queryParams.ddNo"
clearable
placeholder="输入要查询的 DD 单号"
@input="getLogisticsReceiptList" />
</el-form-item>
<el-form-item label="物流公司编码"
prop="transport">
<el-input v-model="queryParams.transport"
clearable
placeholder="输入要查询的物流公司编码"
@input="getLogisticsReceiptList"
style="width: 220px;" />
</el-form-item>
<el-form-item label="订单状态"
prop="isOperateEnd">
<el-select v-model="queryParams.isOperateEnd"
placeholder="查询订单状态"
clearable
@change="getLogisticsReceiptList">
<el-option label="已完成"
value="true" />
<el-option label="未完成"
value="false" />
</el-select>
</el-form-item>
</el-row>
</el-form>
<!-- 数据表格 -->
<el-table :data="tableList"
border
style="width: 100%"
class="table-container">
<el-table-column v-for="item in columns"
:key="item.label"
:prop="item.prop"
:label="item.label"
:width="item.width"
:fixed="item.fixed"
:class-name="item.className">
<template #header="{ column }"
v-if="item.prop === 'receiptPhoto'">
<div class="header-container">
<span>{{ column.label }}</span>
<el-button @click="showPhoto = !showPhoto"
style="margin-left: 10px;">{{ showPhoto ? '隐藏' : '显示' }}图片</el-button>
</div>
</template>
<template #default="scope">
<template v-if="item.prop === 'receiptPhoto'">
<div class="receipt-photo-container"
v-if="showPhoto">
<span v-if="JSON.parse(scope.row[item.prop] || '[]').length === 0">暂无图片</span>
<el-image v-else
v-for="(urlStr, index) in JSON.parse(scope.row[item.prop] || '[]')"
:key="index"
:initial-index="index"
:preview-src-list="JSON.parse(scope.row[item.prop] || '[]')"
preview-teleported
:src="urlStr"
alt="图片"
class="receipt-photo-item"
fit="cover"
style="width: 80px; height: 50px; " />
</div>
</template>
<template v-else-if="item.prop === 'logisticsTrack'">
<el-button type="primary"
@click="handleLogisticsTrack(scope.row)"
text>
查看物流轨迹
</el-button>
</template>
<template v-else-if="item.prop === 'receiptPhotoCompleteFlag'">
<el-select v-model="scope.row[item.prop]"
placeholder="请选择"
style="width: 100%"
@change="handleReceiptPhotoChangeStatus(scope.row)">
<el-option v-for="item in receiptPhotoCompleteFlagOptions"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
</template>
<template v-else-if="item.prop === 'sentStatus'">
<el-select v-model="scope.row[item.prop]"
placeholder="请选择"
filterable
style="width: 100%"
@change="handleSentStatusChange(scope.row)">
<el-option v-for="item in sentStatusOptions"
:key="item.value"
:label="item.label"
:value="item.value" />
<el-row class="client-fix-height container">
<el-col>
<!-- 查询表单 -->
<el-form :model="queryParams"
inline
label-position="left">
<el-row>
<el-form-item label="安徽/北京单据编号"
prop="sentNo">
<el-input v-model="queryParams.sentNo"
clearable
placeholder="输入要查询的安徽/北京单独单号"
@input="getLogisticsReceiptList"
style="width: 250px;" />
</el-form-item>
<el-form-item label="快递单号"
prop="expressNo">
<el-input v-model="queryParams.expressNo"
clearable
placeholder="输入要查询的快递单号"
@input="getLogisticsReceiptList" />
</el-form-item>
<el-form-item label="DD 单号查询"
prop="ddNo">
<el-input v-model="queryParams.ddNo"
clearable
placeholder="输入要查询的 DD 单号"
@input="getLogisticsReceiptList" />
</el-form-item>
<el-form-item label="物流公司编码"
prop="transport">
<el-input v-model="queryParams.transport"
clearable
placeholder="输入要查询的物流公司编码"
@input="getLogisticsReceiptList"
style="width: 220px;" />
</el-form-item>
<el-form-item label="订单状态"
prop="isOperateEnd">
<el-select v-model="queryParams.isOperateEnd"
placeholder="查询订单状态"
clearable
@change="getLogisticsReceiptList">
<el-option label="已完成"
value="true" />
<el-option label="未完成"
value="false" />
</el-select>
</el-form-item>
</el-row>
</el-form>
<!-- 数据表格 -->
<el-table :data="tableList"
border
style="width: 100%"
class="table-container">
<el-table-column v-for="item in columns"
:key="item.label"
:prop="item.prop"
:label="item.label"
:width="item.width"
:fixed="item.fixed"
:class-name="item.className">
<template #header="{ column }"
v-if="item.prop === 'receiptPhoto'">
<div class="header-container">
<span>{{ column.label }}</span>
<el-button @click="showPhoto = !showPhoto"
style="margin-left: 10px;">{{ showPhoto ? '隐藏' : '显示' }}图片</el-button>
</div>
</template>
<template #default="scope">
<template v-if="item.prop === 'receiptPhoto'">
<div class="receipt-photo-container"
v-if="showPhoto">
<span v-if="JSON.parse(scope.row[item.prop] || '[]').length === 0">暂无图片</span>
<el-image v-else
v-for="(urlStr, index) in JSON.parse(scope.row[item.prop] || '[]')"
:key="index"
:initial-index="index"
:preview-src-list="JSON.parse(scope.row[item.prop] || '[]')"
preview-teleported
:src="urlStr"
alt="图片"
class="receipt-photo-item"
fit="cover"
style="width: 80px; height: 50px; " />
</div>
</template>
<template v-else-if="item.prop === 'logisticsTrack'">
<el-button type="primary"
@click="handleLogisticsTrack(scope.row)"
text>
查看物流轨迹
</el-button>
</template>
<template v-else-if="item.prop === 'receiptPhotoCompleteFlag'">
<el-select v-model="scope.row[item.prop]"
placeholder="请选择"
style="width: 100%"
@change="handleReceiptPhotoChangeStatus(scope.row)">
<el-option v-for="item in receiptPhotoCompleteFlagOptions"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
</template>
<template v-else-if="item.prop === 'sentStatus'">
<el-select v-model="scope.row[item.prop]"
placeholder="请选择"
filterable
style="width: 100%"
@change="handleSentStatusChange(scope.row)">
<el-option v-for="item in sentStatusOptions"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
</template>
<template v-else>
<!-- 其他字段显示原始值 -->
{{ formatter(scope.row, scope.column, scope.row[item.prop]) }}
<template v-else>
<!-- 其他字段显示原始值 -->
{{ formatter(scope.row, scope.column, scope.row[item.prop]) }}
</template>
</template>
</template>
</el-table-column>
<!-- <el-table-column label="操作"
</el-table-column>
<!-- <el-table-column label="操作"
width="200"
fixed="right">
<template #default="scope">
......@@ -136,13 +137,14 @@
</el-button>
</template>
</el-table-column> -->
</el-table>
<!-- 分页 -->
<pagination :total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getLogisticsReceiptList" />
</div>
</el-table>
<!-- 分页 -->
<pagination :total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getLogisticsReceiptList" />
</el-col>
</el-row>
</div>
</template>
......@@ -402,65 +404,78 @@ const handleSentStatusChange = async (row) => {
<style scoped
lang="scss">
.table-container {
margin-top: 20px;
.app-container {
>.el-row {
.el-col {
display: flex;
flex-direction: column;
height: 100%;
.header-container {
display: flex;
align-items: center;
}
.table-container {
margin-top: 20px;
::v-deep(.el-image) {
img {
width: 80px !important;
height: 50px !important;
}
}
}
.header-container {
display: flex;
align-items: center;
}
.receipt-photo-container {
padding: 0 15px;
display: flex;
flex-wrap: wrap;
gap: 10px;
min-width: 200px;
/* justify-content: center; // 内容整体居中
align-items: flex-start; // 顶部对齐 */
justify-content: flex-start; // 改为左对齐,让每行都从左边开始
align-items: flex-start; // 顶部对齐
::v-deep(.el-image) {
img {
width: 80px !important;
height: 50px !important;
}
}
}
.receipt-photo-item {
border-radius: 4px;
border: 1px solid #e4e7ed;
transition: all 0.3s;
flex: 0 0 80px; // 固定宽度,不伸缩
.receipt-photo-container {
padding: 0 15px;
display: flex;
flex-wrap: wrap;
gap: 10px;
min-width: 200px;
/* justify-content: center; // 内容整体居中
align-items: flex-start; // 顶部对齐 */
justify-content: flex-start; // 改为左对齐,让每行都从左边开始
align-items: flex-start; // 顶部对齐
&:hover {
border-color: #409eff;
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.2);
}
}
}
.receipt-photo-item {
border-radius: 4px;
border: 1px solid #e4e7ed;
transition: all 0.3s;
flex: 0 0 80px; // 固定宽度,不伸缩
// 让表格列宽自适应内容
::v-deep(.el-table) {
.el-table__body-wrapper {
.el-table__body {
.el-table__row {
.cell {
white-space: nowrap;
overflow: visible;
&:hover {
border-color: #409eff;
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.2);
}
}
}
td.el-table__cell.col-receipt-photo {
.cell {
padding-right: 0px; // 给回单照片列补充右内边距
padding-left: 0px; // 给回单照片列补充左内边距
text-align: center; // 内容居中
// 让表格列宽自适应内容
::v-deep(.el-table) {
.el-table__body-wrapper {
.el-table__body {
.el-table__row {
.cell {
white-space: nowrap;
overflow: visible;
}
}
td.el-table__cell.col-receipt-photo {
.cell {
padding-right: 0px; // 给回单照片列补充右内边距
padding-left: 0px; // 给回单照片列补充左内边距
text-align: center; // 内容居中
}
}
}
}
}
}
}
}
</style>
......@@ -39,7 +39,7 @@ export default defineConfig(({ mode, command }) => {
rewrite: (p) => p.replace(/^\/ql_local/, '')
},
'/bc_local': {
target: 'http://192.168.100.103:8180',
target: 'http://192.168.100.180:8180',
changeOrigin: true,
rewrite: (p) => p.replace(/^\/bc_local/, '')
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论