feat(auth): 添加用户信息获取和附件功能

- 在App.vue中添加onMounted钩子,自动检查认证状态并获取用户信息
- 在auth.ts API文件中添加getInfo方法用于获取用户信息
- 在auth store中添加getInfo方法,实现用户信息获取逻辑
- 在订单列表页面添加批量审批权限控制,根据用户角色决定是否显示批量审批按钮
- 在采购详情页面添加附件标签页,显示合同附件和其他附件列表
- 添加文件预览功能,支持附件在线预览
- 扩展类型定义,添加附件相关类型支持
- 优化批量审批流程,移除驳回选项并修复参数传递问题
master
chenhao 2025-12-23 15:57:36 +08:00
parent 8c3cf68639
commit 09513911ae
6 changed files with 159 additions and 10 deletions

View File

@ -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">

View File

@ -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')
}
/**
* 退
*/

View File

@ -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();
}
},
/**
*
*/

View File

@ -298,5 +298,8 @@ export interface PurchaseDetail {
ownerName: string // 汇智负责人
remark: string // 备注
omsPurchaseOrderItemList: PurchaseOrderItem[] // 采购产品列表
contractFileList?: AttachmentFile[] // 合同附件列表
attachmentList?: AttachmentFile[] // 其他附件列表
fileLog?: AttachmentFile // 单个附件
[key: string]: any
}

View File

@ -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>

View File

@ -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,