| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532 |
- import { currentUser as mockCurrentUser } from '@/data/mock'
- const APPLY_KEY = 'aimill_hr_applications'
- const DRAFT_KEY = 'aimill_hr_application_drafts'
- const APPROVAL_KEY = 'aimill_hr_approval_results'
- const TRAINING_KEY = 'aimill_hr_training_progress'
- const PROFILE_KEY = 'aimill_hr_profile_changes'
- const ONBOARD_MATERIALS_KEY = 'aimill_hr_onboard_materials'
- const ONBOARD_LIST_KEY = 'aimill_hr_onboardings'
- const ONBOARD_INVITATION_KEY = 'aimill_hr_onboarding_invitations'
- const ONBOARD_DRAFT_KEY = 'aimill_hr_onboarding_drafts'
- const RESIGN_KEY = 'aimill_hr_resignations'
- const AUTH_KEY = 'aimill_hr_auth'
- const REMEMBER_KEY = 'aimill_hr_remembered_account'
- const PASSWORD_MEMO_KEY = 'passwordMemo'
- const PASSWORD_INFO_KEY = 'passInfo'
- const TOKEN_KEY = 'token'
- const USER_INFO_KEY = 'userInfo'
- const TREE_KEY = 'treeList'
- const AUTHORITIES_KEY = 'authorities'
- const CURRENT_USER_KEY = 'currentUser'
- const API_INFO_KEY = 'apiInfo'
- export const DEMO_ACCOUNT = 'ZY20230128'
- export const DEMO_PASSWORD = '123456'
- export function saveLoginSession(data, account) {
- const userInfo = data && typeof data === 'object' ? data : {}
- const token = userInfo.token || `demo-token-${Date.now()}`
- const normalizedAccount = String(account || userInfo.loginName || '').trim().toUpperCase()
- uni.setStorageSync(TOKEN_KEY, token)
- uni.setStorageSync(USER_INFO_KEY, { ...userInfo, token, loginName: normalizedAccount })
- uni.setStorageSync(AUTH_KEY, { account: normalizedAccount, loggedInAt: Date.now() })
- }
- export function login(account, password, remember = true) {
- const normalizedAccount = String(account || '').trim().toUpperCase()
- if (normalizedAccount !== DEMO_ACCOUNT || String(password) !== DEMO_PASSWORD) {
- return false
- }
- saveLoginSession({
- token: `demo-token-${Date.now()}`,
- sessionId: 'demo-session',
- userId: normalizedAccount,
- userName: '陈晓雨',
- loginName: normalizedAccount,
- }, normalizedAccount)
- saveRememberedCredentials({
- account: normalizedAccount,
- password,
- rememberPassword: remember,
- })
- savePermissionData([], [])
- return true
- }
- export function logout() {
- [AUTH_KEY, TOKEN_KEY, USER_INFO_KEY, TREE_KEY, AUTHORITIES_KEY, CURRENT_USER_KEY].forEach((key) =>
- uni.removeStorageSync(key),
- )
- }
- export function isLoggedIn() {
- return Boolean(uni.getStorageSync(TOKEN_KEY) && uni.getStorageSync(USER_INFO_KEY))
- }
- // 服务器模式下需要的是真实 token(demo 登录的是 demo-token- 开头,残留时视为无效)
- export function hasServerAuth() {
- const token = uni.getStorageSync(TOKEN_KEY)
- const userInfo = uni.getStorageSync(USER_INFO_KEY)
- return Boolean(token && userInfo && !String(token).startsWith('demo-token-'))
- }
- export function getRememberedAccount() {
- return getRememberedCredentials().account
- }
- export function saveRememberedCredentials({ account, password, rememberPassword }) {
- const normalizedAccount = String(account || '').trim()
- if (!rememberPassword) {
- uni.removeStorageSync(PASSWORD_MEMO_KEY)
- uni.removeStorageSync(PASSWORD_INFO_KEY)
- uni.removeStorageSync(REMEMBER_KEY)
- return
- }
- uni.setStorageSync(PASSWORD_MEMO_KEY, '1')
- uni.setStorageSync(PASSWORD_INFO_KEY, { username: normalizedAccount, passwd: String(password || '') })
- uni.setStorageSync(REMEMBER_KEY, normalizedAccount)
- }
- export function getRememberedCredentials() {
- const remembered = uni.getStorageSync(PASSWORD_INFO_KEY) || {}
- const rememberPassword = uni.getStorageSync(PASSWORD_MEMO_KEY) === '1'
- return {
- account: remembered.username || uni.getStorageSync(REMEMBER_KEY) || '',
- password: rememberPassword ? remembered.passwd || '' : '',
- rememberPassword,
- }
- }
- export function savePermissionData(tree, authorities) {
- uni.setStorageSync(TREE_KEY, JSON.stringify(Array.isArray(tree) ? tree : []))
- uni.setStorageSync(AUTHORITIES_KEY, JSON.stringify(Array.isArray(authorities) ? authorities : []))
- }
- export function getLoginUser() {
- return uni.getStorageSync(USER_INFO_KEY) || null
- }
- export function saveCurrentUser(data) {
- if (!data) return
- uni.setStorageSync(CURRENT_USER_KEY, data)
- }
- // 把 /system/account/getLoginUser 与 /main/users/{userId}/profile 合并后的 VO 映射成页面字段
- // - 部门:vo.deptName
- // - 岗位:fetchUserDetail 已用 vo.postId 解析并写入 vo.position;这里兜底再读 postName / positionList
- // - 入职时间:profile 用的是 joinDate,getLoginUser 用 entryDate,两边都兼容
- // - 紧急联系人:secondLinkName + secondLinkPhone(关系 secondLinkRelation 可选带入)
- function mapCurrentUser(vo) {
- if (!vo || typeof vo !== 'object') return null
- const name = String(vo.name || '')
- let position = vo.position || ''
- if (!position) position = vo.postName || ''
- if (!position && Array.isArray(vo.positionList)) {
- const primary = vo.positionList.find((p) => p && p.primaryFlag === 1)
- position = primary ? String(primary.positionName || '') : ''
- }
- const emergencyName = vo.secondLinkName || ''
- const emergencyPhone = vo.secondLinkPhone || ''
- const emergencyRelation = vo.secondLinkRelation || ''
- // 拼成 "姓名(关系)· 电话" 或 "姓名 · 电话"
- const emergencyParts = []
- if (emergencyName && emergencyRelation) emergencyParts.push(`${emergencyName}(${emergencyRelation})`)
- else if (emergencyName) emergencyParts.push(emergencyName)
- if (emergencyPhone) emergencyParts.push(emergencyPhone)
- const emergencyContact = emergencyParts.join(' · ')
- const rawBankCards = Array.isArray(vo.bankCardList) ? vo.bankCardList : []
- const bankCardList = rawBankCards.map((c) => ({
- id: String(c.id ?? c.cardId ?? c.cardNumber ?? ''),
- cardNumber: String(c.cardNumber ?? c.cardNo ?? ''),
- openingBank: String(c.openingBank ?? c.bankName ?? ''),
- branch: String(c.branch ?? c.subBranch ?? ''),
- }))
- return {
- id: vo.jobNumber || vo.loginName || '',
- name,
- initials: name ? name.charAt(0) : '',
- department: vo.deptName || '',
- position,
- company: vo.groupName || '',
- phone: vo.phone || '',
- entryDate: vo.joinDate || vo.entryDate || '',
- workAge: vo.workAge || '',
- emergencyContact,
- maritalStatus: vo.maritalStatus || vo.marital || '',
- emergencyName: emergencyName,
- emergencyPhone: emergencyPhone,
- emergencyRelation: emergencyRelation,
- bankCardList,
- raw: vo,
- }
- }
- function isServerMode() {
- const info = uni.getStorageSync(API_INFO_KEY)
- return Boolean(info && info.mode === 'server')
- }
- // 演示模式 → mock 数据;服务器模式 → 接口返回的数据;服务器模式未拉到则返回 null
- export function getCurrentUser() {
- if (!isServerMode()) return mockCurrentUser
- const stored = uni.getStorageSync(CURRENT_USER_KEY)
- return mapCurrentUser(stored)
- }
- export function getAuthCredentials() {
- const userInfo = uni.getStorageSync(USER_INFO_KEY) || {}
- return {
- token: userInfo.token || '',
- sessionId: userInfo.sessionId || '',
- }
- }
- export function clearDemoStorage() {
- [APPLY_KEY, DRAFT_KEY, APPROVAL_KEY, TRAINING_KEY, PROFILE_KEY, ONBOARD_MATERIALS_KEY, ONBOARD_LIST_KEY, ONBOARD_INVITATION_KEY, ONBOARD_DRAFT_KEY, RESIGN_KEY].forEach((key) =>
- uni.removeStorageSync(key),
- )
- }
- // 入职记录持久化:mock 中的 defaultOnboardings 是只读示例,
- // 用户通过创建表单新增 / 编辑的记录存在 ONBOARD_LIST_KEY
- export function getCustomOnboardings() {
- const saved = uni.getStorageSync(ONBOARD_LIST_KEY)
- return Array.isArray(saved) ? saved : []
- }
- export function addOnboarding(staff) {
- if (!staff || !staff.id) return getCustomOnboardings()
- const list = getCustomOnboardings()
- list.unshift(staff)
- uni.setStorageSync(ONBOARD_LIST_KEY, list)
- return list
- }
- export function updateOnboarding(id, patch) {
- if (!id) return getCustomOnboardings()
- const list = getCustomOnboardings()
- const idx = list.findIndex((o) => o.id === id)
- if (idx >= 0) {
- list[idx] = { ...list[idx], ...patch }
- uni.setStorageSync(ONBOARD_LIST_KEY, list)
- }
- return list
- }
- export function removeOnboarding(id) {
- if (!id) return getCustomOnboardings()
- const list = getCustomOnboardings().filter((o) => o.id !== id)
- uni.setStorageSync(ONBOARD_LIST_KEY, list)
- return list
- }
- export function getOnboardingById(id, defaults = []) {
- const custom = getCustomOnboardings().find((o) => o.id === id)
- if (custom) return custom
- return defaults.find((o) => o.id === id) || null
- }
- // 把 mock 默认数据 + 用户新增的合并;用户数据优先
- export function mergeOnboardings(defaults = []) {
- const custom = getCustomOnboardings()
- const ids = new Set(custom.map((o) => o.id))
- return [...custom, ...defaults.filter((o) => !ids.has(o.id))]
- }
- // 入职邀请链接持久化(mock 中 defaultOnboardingInvitations 是只读示例)
- export function getOnboardingInvitations() {
- const saved = uni.getStorageSync(ONBOARD_INVITATION_KEY)
- return Array.isArray(saved) ? saved : []
- }
- export function addOnboardingInvitation(invite) {
- if (!invite || !invite.invitationId) return getOnboardingInvitations()
- const list = getOnboardingInvitations()
- list.unshift(invite)
- uni.setStorageSync(ONBOARD_INVITATION_KEY, list)
- return list
- }
- export function updateOnboardingInvitation(invitationId, patch) {
- if (!invitationId) return getOnboardingInvitations()
- const list = getOnboardingInvitations()
- const idx = list.findIndex((i) => i.invitationId === invitationId)
- if (idx >= 0) {
- list[idx] = { ...list[idx], ...patch }
- uni.setStorageSync(ONBOARD_INVITATION_KEY, list)
- }
- return list
- }
- export function getOnboardingInvitationById(invitationId) {
- return getOnboardingInvitations().find((i) => i.invitationId === invitationId) || null
- }
- // 入职创建表单草稿(按 latest-by-type 持久化一份)
- export function saveOnboardingDraft(draft) {
- if (!draft) return null
- uni.setStorageSync(ONBOARD_DRAFT_KEY, { ...draft, savedAt: Date.now() })
- return draft
- }
- export function loadOnboardingDraft() {
- return uni.getStorageSync(ONBOARD_DRAFT_KEY) || null
- }
- export function clearOnboardingDraft() {
- uni.removeStorageSync(ONBOARD_DRAFT_KEY)
- }
- // 入职材料上传:按 employeeId → materialKey → FileInfo[] 索引
- // FileInfo = { id, name, path, size, uploadedAt, sourceType }
- // 演示版只存元信息 + 本地临时路径,不上传到对象存储
- export function getOnboardMaterials(employeeId) {
- if (!employeeId) return {}
- const all = uni.getStorageSync(ONBOARD_MATERIALS_KEY) || {}
- const record = all[employeeId]
- return record && typeof record === 'object' ? record : {}
- }
- export function getOnboardMaterialList(employeeId, materialKey) {
- const record = getOnboardMaterials(employeeId)
- const list = record[materialKey]
- return Array.isArray(list) ? list : []
- }
- export function addOnboardMaterial(employeeId, materialKey, file) {
- if (!employeeId || !materialKey) return []
- const all = uni.getStorageSync(ONBOARD_MATERIALS_KEY) || {}
- const prev = all[employeeId] && typeof all[employeeId] === 'object' ? all[employeeId] : {}
- const list = Array.isArray(prev[materialKey]) ? prev[materialKey] : []
- const item = {
- id: `mat_${Date.now()}_${Math.floor(Math.random() * 1000)}`,
- name: file.name || `材料_${materialKey}`,
- path: file.path || '',
- size: Number(file.size) || 0,
- sourceType: file.sourceType || 'album',
- uploadedAt: formatNow(),
- }
- const next = [...list, item]
- all[employeeId] = { ...prev, [materialKey]: next }
- uni.setStorageSync(ONBOARD_MATERIALS_KEY, all)
- return next
- }
- export function removeOnboardMaterial(employeeId, materialKey, fileId) {
- if (!employeeId || !materialKey) return []
- const all = uni.getStorageSync(ONBOARD_MATERIALS_KEY) || {}
- const prev = all[employeeId] && typeof all[employeeId] === 'object' ? all[employeeId] : {}
- const list = Array.isArray(prev[materialKey]) ? prev[materialKey] : []
- const next = list.filter((f) => f.id !== fileId)
- all[employeeId] = { ...prev, [materialKey]: next }
- uni.setStorageSync(ONBOARD_MATERIALS_KEY, all)
- return next
- }
- export function renameOnboardMaterial(employeeId, materialKey, fileId, newName) {
- const trimmed = String(newName || '').trim()
- if (!employeeId || !materialKey || !trimmed) return []
- const all = uni.getStorageSync(ONBOARD_MATERIALS_KEY) || {}
- const prev = all[employeeId] && typeof all[employeeId] === 'object' ? all[employeeId] : {}
- const list = Array.isArray(prev[materialKey]) ? prev[materialKey] : []
- const next = list.map((f) => (f.id === fileId ? { ...f, name: trimmed } : f))
- all[employeeId] = { ...prev, [materialKey]: next }
- uni.setStorageSync(ONBOARD_MATERIALS_KEY, all)
- return next
- }
- // 把临时路径读取为 base64 dataURL,用于在 storage 中持久化预览
- // - H5:tempFilePath 通常是 blob://,用 fetch + FileReader
- // - 小程序:通过 uni.getFileSystemManager.readFile({ encoding: 'base64' })
- // - App:由原生 chooseImage 返回本地绝对路径,读不到时回退空字符串
- export async function readFileAsBase64(tempPath) {
- if (!tempPath) return ''
- // 已是 dataURL:直接返回
- if (tempPath.startsWith('data:')) return tempPath
- // #ifdef H5
- try {
- const resp = await fetch(tempPath)
- const blob = await resp.blob()
- return await new Promise((resolve) => {
- const reader = new FileReader()
- reader.onload = () => resolve(String(reader.result || ''))
- reader.onerror = () => resolve('')
- reader.readAsDataURL(blob)
- })
- } catch (e) {
- return ''
- }
- // #endif
- // #ifdef MP-WEIXIN
- return new Promise((resolve) => {
- if (!uni.getFileSystemManager) return resolve('')
- uni.getFileSystemManager().readFile({
- filePath: tempPath,
- encoding: 'base64',
- success: (res) => {
- const ext = (tempPath.match(/\.(\w+)$/)?.[1] || 'jpg').toLowerCase()
- const mime = ext === 'png' ? 'image/png' : ext === 'gif' ? 'image/gif' : 'image/jpeg'
- resolve(`data:${mime};base64,${res.data}`)
- },
- fail: () => resolve(''),
- })
- })
- // #endif
- // #ifdef APP-PLUS
- return new Promise((resolve) => {
- if (!plus || !plus.io) return resolve('')
- plus.io.resolveLocalFileSystemURL(
- tempPath,
- (entry) => entry.file(
- (file) => {
- const reader = new plus.nativeUI.FileReader()
- reader.onloadend = (e) => resolve(`data:image/jpeg;base64,${e.target.result}`)
- reader.onerror = () => resolve('')
- reader.readAsDataURL(file)
- },
- () => resolve(''),
- ),
- () => resolve(''),
- )
- })
- // #endif
- }
- // 演示版上传:模拟 0-100% 进度回调。真实接入时替换为 uni.uploadFile({ url: realEndpoint })
- // 返回 { uploaded, url },url 在演示版等于原临时路径
- export function mockUpload(tempPath, { onProgress } = {}) {
- return new Promise((resolve) => {
- let progress = 0
- const tick = () => {
- progress += Math.floor(Math.random() * 25) + 15
- if (progress > 100) progress = 100
- onProgress?.(progress)
- if (progress >= 100) resolve({ uploaded: true, url: tempPath, mock: true })
- else setTimeout(tick, 120)
- }
- setTimeout(tick, 80)
- })
- }
- export function getApplications() {
- const saved = uni.getStorageSync(APPLY_KEY)
- return Array.isArray(saved) ? saved : []
- }
- export function addApplication(item) {
- const list = getApplications()
- // 若调用方已传 id(如服务器模式返回的单据主键),保留以便与后端对齐;
- // 否则按本地时间戳生成唯一 id
- const id = item && item.id ? item.id : Date.now()
- const { id: _omit, ...rest } = item || {}
- list.unshift({ ...rest, id, status: '审批中', createdAt: formatNow() })
- uni.setStorageSync(APPLY_KEY, list)
- return list
- }
- export function saveDraft(type, form) {
- const drafts = uni.getStorageSync(DRAFT_KEY) || {}
- drafts[type] = { ...form, savedAt: formatNow() }
- uni.setStorageSync(DRAFT_KEY, drafts)
- }
- export function getDraft(type) {
- const drafts = uni.getStorageSync(DRAFT_KEY) || {}
- return drafts[type] || null
- }
- export function removeDraft(type) {
- const drafts = uni.getStorageSync(DRAFT_KEY) || {}
- delete drafts[type]
- uni.setStorageSync(DRAFT_KEY, drafts)
- }
- export function getApprovalResults() {
- const saved = uni.getStorageSync(APPROVAL_KEY)
- return saved && typeof saved === 'object' ? saved : {}
- }
- export function setApprovalResult(id, status, comment = '') {
- const results = getApprovalResults()
- results[id] = { status, comment, handledAt: formatNow() }
- uni.setStorageSync(APPROVAL_KEY, results)
- return results[id]
- }
- export function getTrainingProgress(defaults) {
- const saved = uni.getStorageSync(TRAINING_KEY) || {}
- return defaults.map((item) => ({ ...item, progress: saved[item.id] ?? item.progress }))
- }
- export function setTrainingProgress(id, progress) {
- const saved = uni.getStorageSync(TRAINING_KEY) || {}
- saved[id] = progress
- uni.setStorageSync(TRAINING_KEY, saved)
- }
- export function saveProfileChange(item) {
- const list = uni.getStorageSync(PROFILE_KEY) || []
- list.unshift({ ...item, id: Date.now(), status: '审核中', createdAt: formatNow() })
- uni.setStorageSync(PROFILE_KEY, list)
- return list
- }
- export function getResignations() {
- const saved = uni.getStorageSync(RESIGN_KEY)
- return saved && typeof saved === 'object' ? saved : {}
- }
- export function getResignDetail(id) {
- if (!id) return null
- const all = getResignations()
- return all[id] && typeof all[id] === 'object' ? all[id] : null
- }
- export function setResignStage(id, stageKey, state = 'done') {
- if (!id || !stageKey) return null
- const all = getResignations()
- const prev = all[id] && typeof all[id] === 'object' ? all[id] : {}
- const stages = prev.stages && typeof prev.stages === 'object' ? prev.stages : {}
- stages[stageKey] = state
- all[id] = { ...prev, stages, updatedAt: formatNow() }
- uni.setStorageSync(RESIGN_KEY, all)
- return all[id]
- }
- export function setResignStatus(id, status) {
- if (!id || !status) return null
- const all = getResignations()
- const prev = all[id] && typeof all[id] === 'object' ? all[id] : {}
- all[id] = { ...prev, status, updatedAt: formatNow() }
- uni.setStorageSync(RESIGN_KEY, all)
- return all[id]
- }
- export function addResignAssetReturn(id, assetName, returned = true) {
- if (!id || !assetName) return null
- const all = getResignations()
- const prev = all[id] && typeof all[id] === 'object' ? all[id] : {}
- const map = prev.assetReturnMap && typeof prev.assetReturnMap === 'object' ? prev.assetReturnMap : {}
- map[assetName] = !!returned
- all[id] = { ...prev, assetReturnMap: map, updatedAt: formatNow() }
- uni.setStorageSync(RESIGN_KEY, all)
- return all[id]
- }
- export function setResignLocalState(id, patch) {
- if (!id || !patch || typeof patch !== 'object') return null
- const all = getResignations()
- const prev = all[id] && typeof all[id] === 'object' ? all[id] : {}
- all[id] = { ...prev, ...patch, updatedAt: formatNow() }
- uni.setStorageSync(RESIGN_KEY, all)
- return all[id]
- }
- export function formatNow() {
- const d = new Date()
- const pad = (n) => String(n).padStart(2, '0')
- return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
- }
|