feat(auth): 添加用户信息获取和附件功能
- 在App.vue中添加onMounted钩子,自动检查认证状态并获取用户信息 - 在auth.ts API文件中添加getInfo方法用于获取用户信息 - 在auth store中添加getInfo方法,实现用户信息获取逻辑 - 在订单列表页面添加批量审批权限控制,根据用户角色决定是否显示批量审批按钮 - 在采购详情页面添加附件标签页,显示合同附件和其他附件列表 - 添加文件预览功能,支持附件在线预览 - 扩展类型定义,添加附件相关类型支持 - 优化批量审批流程,移除驳回选项并修复参数传递问题master
parent
8c3cf68639
commit
09513911ae
11
src/App.vue
11
src/App.vue
|
|
@ -5,7 +5,16 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// App根组件
|
||||
import { onMounted } from 'vue'
|
||||
import { useAuthStore } from '@/store/auth'
|
||||
|
||||
onMounted(() => {
|
||||
const authStore = useAuthStore()
|
||||
// 检查本地存储的认证状态,如果已认证,则获取用户信息
|
||||
if (authStore.isAuthenticated) {
|
||||
authStore.getInfo()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
|
|
|||
|
|
@ -19,6 +19,13 @@ export const login = (params: LoginParams): Promise<AxiosResponse<ApiResponse<{
|
|||
return http.post('/login', formData)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
*/
|
||||
export const getInfo = (): Promise<AxiosResponse<ApiResponse<any>>> => {
|
||||
return http.get('/getInfo')
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户退出登录
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { login, logout as logoutApi } from '@/api/auth'
|
||||
import { login, getInfo, logout as logoutApi } from '@/api/auth'
|
||||
import type { LoginParams } from '@/types'
|
||||
|
||||
interface AuthState {
|
||||
|
|
@ -52,6 +52,26 @@ export const useAuthStore = defineStore('auth', {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
*/
|
||||
async getInfo() {
|
||||
if (this.userInfo) return; // 如果已有用户信息,则不再获取
|
||||
try {
|
||||
const response = await getInfo();
|
||||
if (response.data.code === 0) {
|
||||
const user = response.data.data.user;
|
||||
this.userInfo = user;
|
||||
} else {
|
||||
throw new Error(response.data.msg || '获取用户信息失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取用户信息失败:', error);
|
||||
// 这里可以选择是否要登出
|
||||
// await this.logout();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 用户登出
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -298,5 +298,8 @@ export interface PurchaseDetail {
|
|||
ownerName: string // 汇智负责人
|
||||
remark: string // 备注
|
||||
omsPurchaseOrderItemList: PurchaseOrderItem[] // 采购产品列表
|
||||
contractFileList?: AttachmentFile[] // 合同附件列表
|
||||
attachmentList?: AttachmentFile[] // 其他附件列表
|
||||
fileLog?: AttachmentFile // 单个附件
|
||||
[key: string]: any
|
||||
}
|
||||
|
|
@ -116,7 +116,7 @@
|
|||
</van-tabs>
|
||||
|
||||
<!-- 一键审批操作悬浮框 -->
|
||||
<div v-if="currentTab === 'pending' && orderList.length > 0" class="batch-actions-footer">
|
||||
<div v-if="canBatchApprove && currentTab === 'pending' && orderList.length > 0" class="batch-actions-footer">
|
||||
<div class="footer-content">
|
||||
<van-button
|
||||
type="primary"
|
||||
|
|
@ -199,16 +199,21 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useOrderStore } from '@/store/order'
|
||||
import { useAuthStore } from '@/store/auth'
|
||||
import { formatAmount, formatDate } from '@/utils'
|
||||
import type { ApprovalStatus, ApproveBtn, BatchApprovalParams } from '@/types'
|
||||
import { showToast, showSuccessToast } from 'vant'
|
||||
|
||||
const router = useRouter()
|
||||
const orderStore = useOrderStore()
|
||||
const authStore = useAuthStore()
|
||||
|
||||
// 用户信息
|
||||
const { userInfo } = storeToRefs(authStore)
|
||||
|
||||
// 待审批列表相关状态
|
||||
const { orderList, loading, finished } = storeToRefs(orderStore)
|
||||
|
|
@ -230,6 +235,15 @@ const batchApprovalOpinion = ref('')
|
|||
const batchSubmitting = ref(false)
|
||||
const selectedBatchTag = ref('')
|
||||
|
||||
// 计算是否可以显示批量审批按钮
|
||||
const canBatchApprove = computed(() => {
|
||||
if (!userInfo.value || !Array.isArray(userInfo.value.roles)) {
|
||||
return true // 如果没有用户信息或角色信息,默认显示
|
||||
}
|
||||
// 如果角色名包含“商务”,则不显示
|
||||
return !userInfo.value.roles.some(role => role.roleName && role.roleName.includes('商务'))
|
||||
})
|
||||
|
||||
// 加载数据
|
||||
const onLoad = async () => {
|
||||
try {
|
||||
|
|
@ -341,8 +355,7 @@ const showBatchApprovalDialog = () => {
|
|||
// 获取默认通过意见标签
|
||||
const getBatchOpinionTags = () => {
|
||||
return [
|
||||
'所有信息已阅,审核通过',
|
||||
'经审查有问题,驳回'
|
||||
'所有信息已阅,审核通过'
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -369,7 +382,7 @@ const submitBatchApproval = async (approveStatus: ApproveBtn) => {
|
|||
try {
|
||||
const params: BatchApprovalParams = {
|
||||
variables: {
|
||||
approveBtn: approveStatus+'',
|
||||
approveBtn: approveStatus,
|
||||
comment: opinion || (approveStatus === 1 ? '同意' : '')
|
||||
}
|
||||
}
|
||||
|
|
@ -387,9 +400,13 @@ const submitBatchApproval = async (approveStatus: ApproveBtn) => {
|
|||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
onMounted(async () => {
|
||||
orderStore.resetListState()
|
||||
orderStore.resetCompletedListState()
|
||||
// 如果已经登录,则确保获取了用户信息
|
||||
if (authStore.isAuthenticated && !authStore.userInfo) {
|
||||
await authStore.getInfo()
|
||||
}
|
||||
onLoad()
|
||||
})
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -132,6 +132,32 @@
|
|||
</div>
|
||||
</van-tab>
|
||||
|
||||
<!-- 附件 -->
|
||||
<van-tab title="附件" name="attachment">
|
||||
<div class="tab-content">
|
||||
<div class="card" v-if="attachmentList.length">
|
||||
<div class="card-body">
|
||||
<div class="file-list">
|
||||
<div v-for="file in attachmentList" :key="file.id" class="file-item"
|
||||
@click="previewFile(file)">
|
||||
<van-icon name="description"/>
|
||||
<div class="file-info">
|
||||
<div class="file-name">{{ file.fileName }}</div>
|
||||
<div class="file-meta"> {{ formatDate(file.uploadTime) }}</div>
|
||||
</div>
|
||||
<van-icon name="arrow"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 无附件时的提示 -->
|
||||
<div v-else class="empty-state">
|
||||
<van-empty description="暂无附件"/>
|
||||
</div>
|
||||
</div>
|
||||
</van-tab>
|
||||
|
||||
<!-- 审批历史 -->
|
||||
<van-tab title="审批历史" name="approval">
|
||||
<div class="tab-content">
|
||||
|
|
@ -260,8 +286,8 @@ import { storeToRefs } from 'pinia'
|
|||
import { showToast, showSuccessToast } from 'vant'
|
||||
import { usePurchaseStore } from '@/store/purchase'
|
||||
import { getPurchaseApprovalHistory, submitPurchaseApproval } from '@/api/purchase'
|
||||
import { formatAmount, formatDate, getApprovalStatusColor } from '@/utils'
|
||||
import type {ApprovalStatus, ApproveBtn} from '@/types'
|
||||
import { formatAmount, formatDate, getApprovalStatusColor, getFilePreviewUrl } from '@/utils'
|
||||
import type {ApprovalStatus, ApproveBtn, AttachmentFile, FileType} from '@/types'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
|
@ -281,6 +307,16 @@ const currentApprovalStatus = ref<ApproveBtn>(3)
|
|||
const submitting = ref(false)
|
||||
const selectedTag = ref('')
|
||||
|
||||
// 获取附件列表
|
||||
const attachmentList = computed(() => {
|
||||
if (!currentPurchase.value) return []
|
||||
|
||||
// 如果有fileLog且不为空,则将其包装为数组返回
|
||||
if ((currentPurchase.value as any).fileLog) {
|
||||
return [(currentPurchase.value as any).fileLog]
|
||||
}
|
||||
})
|
||||
|
||||
// 是否显示审批按钮
|
||||
const showApprovalButtons = computed(() => {
|
||||
// 如果是只读模式(来自已审批列表),则不显示审批按钮
|
||||
|
|
@ -302,6 +338,18 @@ const getStepIcon = (status?: ApprovalStatus) => {
|
|||
return iconMap[status] || 'clock'
|
||||
}
|
||||
|
||||
|
||||
// 预览文件
|
||||
const previewFile = (file: AttachmentFile) => {
|
||||
if (!file.filePath) {
|
||||
showToast('文件路径不存在')
|
||||
return
|
||||
}
|
||||
|
||||
const url = getFilePreviewUrl(file.filePath)
|
||||
window.open(url, '_blank')
|
||||
}
|
||||
|
||||
// 计算总金额
|
||||
const getTotalAmount = () => {
|
||||
if (!purchaseItems.value || purchaseItems.value.length === 0) return 0
|
||||
|
|
@ -674,6 +722,51 @@ onMounted(async () => {
|
|||
}
|
||||
}
|
||||
|
||||
// 附件列表样式
|
||||
.file-list {
|
||||
.file-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: var(--spacing-md);
|
||||
background: var(--background-color-tertiary);
|
||||
border-radius: var(--border-radius-sm);
|
||||
margin-bottom: var(--spacing-sm);
|
||||
cursor: pointer;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: var(--border-color);
|
||||
}
|
||||
|
||||
.van-icon:first-child {
|
||||
color: var(--primary-color);
|
||||
margin-right: var(--spacing-md);
|
||||
}
|
||||
|
||||
.file-info {
|
||||
flex: 1;
|
||||
|
||||
.file-name {
|
||||
font-size: 14px;
|
||||
color: var(--text-color-primary);
|
||||
margin-bottom: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.file-meta {
|
||||
font-size: 12px;
|
||||
color: var(--text-color-tertiary);
|
||||
}
|
||||
}
|
||||
|
||||
.van-icon:last-child {
|
||||
color: var(--text-color-tertiary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 审批历史样式
|
||||
.approval-item {
|
||||
.approval-user,
|
||||
|
|
|
|||
Loading…
Reference in New Issue