diff --git a/src/actions/app.js b/src/actions/app.js index b0ad342..6b6ea99 100644 --- a/src/actions/app.js +++ b/src/actions/app.js @@ -39,10 +39,26 @@ export function login({ username, password }) { return async (dispatch, getState) => { const res = await getData('authentication/login', { UserName: username, Password: password }, true); if (!isReqSuccess(res)) { - dispatch(logout()); + // dispatch(logout()); return res; } const userData = firstCharToLowerCase(res.data); + + const isSuperAdmin = userData.backgroundPermission === 3; + if(isSuperAdmin) { + return { + code: 'err', + msg: '该账号没有访问权限', + } + } + + const companyInfoRes = await getData('company/queryFrontDeskCompanyById', { id: userData.companyId }); + if (!isReqSuccess(companyInfoRes)) { + return companyInfoRes; + } + + + const { data: { company: { SoftwareVersion } = {} } = {} } = companyInfoRes; // 角色判断 // const { app: appStore } = getState(); @@ -56,8 +72,10 @@ export function login({ username, password }) { accountName: userData.cnName, customerId: userData.companyId, avatorUrl: userData.headImgUrl, + isMini: SoftwareVersion === 'Mini', }; storage.set('accountId', payload.accountId); + storage.set('isMini', payload.isMini) dispatch({ type: 'app/login', payload, diff --git a/src/components/FileExplorer/fileExplorer.module.scss b/src/components/FileExplorer/fileExplorer.module.scss new file mode 100644 index 0000000..ffd965e --- /dev/null +++ b/src/components/FileExplorer/fileExplorer.module.scss @@ -0,0 +1,127 @@ +.list { + background-color: transparent; + &:after { + content: none; + } +} +.listItem { + padding: 0 20px * 2 0 8px * 2; + &:after { + left: 72px * 2; + } + :global { + .at-list__item-thumb { + width: 64px * 2; + height: 64px * 2; + margin-right: 0; + padding: 5px * 2; + box-sizing: border-box; + } + .at-icon-chevron-right { + color: #7850FF; + } + .item-extra__image { + margin-right: 0; + } + } +} + +.listItem2 { + $itemHeight: 64px * 2; + position: relative; + overflow: hidden; + height: $itemHeight; + .content { + position: absolute; + left: 0; + right: 0; + top: 0; + height: 100%; + display: flex; + align-items: center; + z-index: 2; + padding: 0 20px * 2 0 8px * 2; + transition: right 0.3s linear; + background-color: rgba(#f6f6f6, 1); + &.dragging { + transition: none; + } + .img { + width: 100%; + height: 100%; + } + .itemThumb { + width: $itemHeight; + height: $itemHeight; + padding: 10px; + margin-right: 0; + box-sizing: border-box; + } + + .itemContent { + flex: 1; + overflow: hidden; + margin-right: 40px; + &_info { + &_title { + color: #32323c; + font-size: 16Px; + line-height: 1.5; + white-space: nowrap; + text-overflow: ellipsis; + } + &_note { + color: rgba(0, 0, 0, 0.56);; + font-size: 12Px; + line-height: 1.5; + } + } + } + + .itemExtra { + position: relative; + max-width: 235px; + text-align: right; + &Thumb { + display: inline-block; + width: 56px; + height: 56px; + vertical-align: middle; + margin-right: 0; + } + &Icon { + position: relative; + right: -12px; + } + } + } + .actions { + position: absolute; + right: 0; + z-index: 1; + .deleteBtn { + background-color: #D6243A; + color: #fff; + text-align: center; + line-height: $itemHeight; + } + } + + &:after { + content: ""; + position: absolute; + transform-origin: center; + box-sizing: border-box; + pointer-events: none; + top: auto; + left: 144rpx; + right: 0; + bottom: 0; + z-index: 3; + transform: scaleY(0.5); + border-bottom: 1PX solid #d6e4ef; + border-bottom-width: 1px; + border-bottom-style: solid; + border-bottom-color: rgb(214, 228, 239); + } +} \ No newline at end of file diff --git a/src/components/FileExplorer/index.jsx b/src/components/FileExplorer/index.jsx new file mode 100644 index 0000000..040bb54 --- /dev/null +++ b/src/components/FileExplorer/index.jsx @@ -0,0 +1,135 @@ +import { getFileUrl } from '@root/service/oss'; +import { ScrollView, View, MovableArea, MovableView, Image } from '@tarojs/components'; +import React, { useCallback, useState } from 'react'; +import { AtIcon, AtList, AtListItem } from 'taro-ui'; +import styles from './fileExplorer.module.scss'; + +export default function FileExplorer(props) { + const { className, node, nodeHash, gotoNode } = props; + return ( + + + { + node && node.mapChildrenIds(nodeId => { + const fileNode = nodeHash[nodeId]; + if (!fileNode) return null; + return ( + fileNode.type === 'folder' + ? ( + gotoNode(fileNode)} + thumb={getFileUrl('oss://assets/file-icon/folder.png')} + /> + ) + : ( + + ) + ) + }) + } + + + ) +} + + +const deleteBtnWidth = 64; +function ListItem(props) { + const { className, enableDelete = false, title, note, arrow, thumb, extraThumb, onClick } = props; + const [initX, setInitX] = useState(); + const [swipeWidth, setSwipeWidth] = useState(0); + + const onTouchStart = useCallback((e) => { + setInitX(e.touches[0].pageX); + }, [enableDelete]); + + const onTouchMove = useCallback((e) => { + const currentX = e.touches[0].pageX; + const diffx = currentX - initX; + setSwipeWidth(prevValue => { + return diffx >= 0 + ? prevValue ? Math.max(deleteBtnWidth - diffx, 0) : 0 // 右滑 + : prevValue !== deleteBtnWidth ? Math.min(deleteBtnWidth, Math.abs(diffx)) : deleteBtnWidth; // 左滑 + }); + + }, [initX, enableDelete]); + + const onTouchEnd = useCallback(() => { + setSwipeWidth(prevWidth => prevWidth > deleteBtnWidth / 2 ? deleteBtnWidth : 0); + setInitX(); + }, [enableDelete]) + + + return ( + + + { + thumb ? ( + + + + ) + : null + } + + + {title} + { + note + ? ( + {note} + ) + : null + } + + + + + { + extraThumb + ? ( + + + + ) + : null + } + { + arrow + ? ( + + ) + : null + } + + + + 删除 + + + ) +} \ No newline at end of file diff --git a/src/pages/recycle/index.jsx b/src/pages/recycle/index.jsx index 8c8deb2..fd2486b 100644 --- a/src/pages/recycle/index.jsx +++ b/src/pages/recycle/index.jsx @@ -1,10 +1,77 @@ -import { Text, View } from '@tarojs/components'; -import React from 'react'; +import React, { useCallback, useEffect, useLayoutEffect, useState } from 'react'; +import Page from '@root/components/Page'; +import { AtSearchBar } from 'taro-ui'; +import styles from './index.module.scss'; +import FileExplorer from '@root/components/FileExplorer'; +import { fetchProjectList, fetchProjectRemovedFiles } from './service'; export default function RecycleView() { + const [tempKeywords, setTempKeywords] = useState(''); + const [effectKeywords, setEffectKeywords] = useState(''); + const [currentNode, setCurrentNode] = useState(null); + const [nodeHash, setNodeHash] = useState({}); + + useLayoutEffect(() => { + fetchProjectList() + .then(([rootNode, nextNodeHash]) => { + setCurrentNode(rootNode); + setNodeHash(nextNodeHash); + }) + }, []); + + const gotoNode = useCallback((nextFileNode) => { + if(nextFileNode.isRoot) { + fetchProjectList() + .then(([rootNode, nextNodeHash]) => { setCurrentNode(rootNode); setNodeHash(nextNodeHash); }) + } else if(nextFileNode.isProject) { + fetchProjectRemovedFiles(nextFileNode) + .then(([nextNodeHash]) => { + setCurrentNode(nextFileNode); + setNodeHash(prevHash => ({ ...prevHash, ...nextNodeHash })); + }) + } else { + setCurrentNode(nextFileNode); + } + }, []) + return ( - - 回收站页 - + { + setCurrentNode(prevNode => nodeHash[prevNode.parentId]) + }} + > + setEffectKeywords(tempKeywords)} + onClear={() => {setEffectKeywords(''); setTempKeywords('');}} + /> + { + currentNode + ? ( + + ) + : null + } + + ) } diff --git a/src/pages/recycle/index.module.scss b/src/pages/recycle/index.module.scss new file mode 100644 index 0000000..8441c2d --- /dev/null +++ b/src/pages/recycle/index.module.scss @@ -0,0 +1,25 @@ +$searchBoxHeight: 82px; +.page { + background-color: #f6f6f6; +} +.search { + height: $searchBoxHeight; + background-color: #ebebeb; + border-bottom: 1px solid rgba(0, 0, 0, 0.2); + padding: 12px 16px * 2; + :global(.at-search-bar__input-cnt) { + border-radius: 8Px; + background-color: rgba(0, 0, 0, 0.05); + + } + :global(.at-search-bar__input) { + color: rgba(0, 0, 0, 0.56); + } + :global(.at-search-bar__action) { + background-color: transparent; + color: #7850FF; + } +} +.list { + height: calc(100% - #{$searchBoxHeight}); +} \ No newline at end of file diff --git a/src/pages/recycle/service.js b/src/pages/recycle/service.js new file mode 100644 index 0000000..cf72005 --- /dev/null +++ b/src/pages/recycle/service.js @@ -0,0 +1,98 @@ +import getData from '@root/utils/request'; +import * as storage from '@root/utils/storage'; +import { FileNode, firstCharToLowerCase } from '@root/utils/tool'; +import { hideLoading, showLoading } from '@tarojs/taro'; + +export async function fetchProjectList() { + const accountId = storage.get('accountId'); + showLoading(); + const res = await getData('project/queryAllProjectListByUserId', { userId: accountId }); + hideLoading(); + const nodeHash = {}; + const rootNode = new FileNode('root', '', null, 'folder'); + rootNode.isRoot = true; + nodeHash[rootNode.id] = rootNode; + (res.data || []).forEach((upperProject) => { + if(!upperProject.HasFile) { return; } + // const project = firstCharToLowerCase(upperProject); + const projectFolder = new FileNode(upperProject.Id, upperProject.ProjName, rootNode.id, 'folder'); + projectFolder.isProject = true; + nodeHash[projectFolder.id] = projectFolder; + // if (upperProject.NodeFolder) { + // upperProject.NodeFolder + // .forEach((upperNodeFolder) => { + // if(upperNodeFolder.FirstFolderId === upperNodeFolder.Id || + // upperNodeFolder.NodeId === '0') { return; } + // const folderNode = new FileNode(upperNodeFolder.Id, upperNodeFolder.FolderName, projectFolder.id, 'folder'); + // nodeHash[folderNode.id] = folderNode; + // projectFolder.append(folderNode.id); + // }); + // ; + // } + rootNode.append(projectFolder.id) + }, []); + return [rootNode, nodeHash]; +} + +export async function fetchProjectRemovedFiles(projectNode) { + showLoading(); + const hash = {}; + const isMini = storage.get('isMini'); + const res = await getData('file/queryFilesFromRecycleBinByProjectId', { projectId: projectNode.id }); + hideLoading(); + projectNode.removeAllChildrenIds(); + (res.data || []).forEach((topNode) => { + if(!topNode.ProjArchives) { return ; } + let parentNode = projectNode; + if(!isMini) { + const node = new FileNode(topNode.Id, topNode.FolderName, projectNode.id, 'folder'); + hash[node.id] = node; + projectNode.append(node.id); + parentNode = node; + } + topNode.ProjArchives.forEach((upperFileItem) => { + const fileItem = firstCharToLowerCase(upperFileItem); + const lastFolder = generateFolder(hash, `${topNode.FolderName}${fileItem.relativePath ? `/${fileItem.relativePath}` : ''}`, parentNode); + const file = new FileNode(fileItem.id, fileItem.archName, parentNode.id, 'file', fileItem); + lastFolder.append(file.id); + // parentNode.append(file); + hash[file.id] = file; + }); + }); + console.log('fetchProjectRemovedFiles:', res); + return [hash]; +} + + +function resolveFileListToTree(upperFileList, hash) { + const headListIdList = []; + +} + + +function generateFolder(folderMap, filePath, topNode) { + const paths = filePath.split("/"); + let lastFolder = topNode; + paths.forEach((_, idx) => { + if(idx === 0) { // 第一位为节点文件夹的名称,已在前文创建,不在重复 + return; + } + const fullRelativePath = `${paths.slice(0, idx + 1).join("/")}`; + let folder; + if (folderMap[fullRelativePath]) { + folder = folderMap[fullRelativePath]; + } else { + const folderName = paths[idx]; + const parentNodeId = idx === 1 ? topNode.id : paths.slice(0, idx).join('/'); + folder = new FileNode(fullRelativePath, folderName, parentNodeId, 'folder'); + folderMap[fullRelativePath] = folder; + } + + // const parentRelativePath = folder.parentFolderPath; + + (folderMap[folder.parentId] || topNode) + .append(folder.id); + lastFolder = folder; + }); + return lastFolder; +} \ No newline at end of file diff --git a/src/reducers/app.js b/src/reducers/app.js index 27a801d..ff73ea9 100644 --- a/src/reducers/app.js +++ b/src/reducers/app.js @@ -6,6 +6,7 @@ const initState = { accountName: '', companyId: '', avatorUrl: '', + isMini: false, } @@ -30,6 +31,7 @@ export default (state = initState, { type, payload }) => { accountName: '', companyId: '', avatorUrl: '', + isMini: false, } default: return state; diff --git a/src/utils/tool.js b/src/utils/tool.js index 95e4197..838ee87 100644 --- a/src/utils/tool.js +++ b/src/utils/tool.js @@ -46,4 +46,35 @@ class RequestHandler { } } -export const handleRequest = res => new RequestHandler(res); \ No newline at end of file +export const handleRequest = res => new RequestHandler(res); + + +export class FileNode { + // type: 'folder' | 'file' + constructor(id, label, parentId, type, data = {}) { + Object.assign(this, { id, label, parentId, type, data }); + } + append(childNodeId) { + if(!this.childrenIds) { this.childrenIds = []; } + this.childrenIds.push(childNodeId); + } + // sortChildren(f = sortFunc) { + // if(!this.hasChildren) { return this; } + // this.childrenIds.sort(f); + // return this; + // } + mapChildrenIds(f) { + if(!this.hasChildren) { return []; } + return this.childrenIds.map(f); + } + // everyChildren(f) { + // if(!this.hasChildren) { return false; } + // return this.childrenIds.every(f); + // } + get hasChildren() { + return this.childrenIds && !!this.childrenIds.length; + } + removeAllChildrenIds() { + return this.childrenIds = []; + } +} \ No newline at end of file