瀏覽代碼

update

main
郑州 3 年之前
父節點
當前提交
3538497e97
共有 8 個文件被更改,包括 510 次插入7 次删除
  1. +19
    -1
      src/actions/app.js
  2. +127
    -0
      src/components/FileExplorer/fileExplorer.module.scss
  3. +135
    -0
      src/components/FileExplorer/index.jsx
  4. +72
    -5
      src/pages/recycle/index.jsx
  5. +25
    -0
      src/pages/recycle/index.module.scss
  6. +98
    -0
      src/pages/recycle/service.js
  7. +2
    -0
      src/reducers/app.js
  8. +32
    -1
      src/utils/tool.js

+ 19
- 1
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,


+ 127
- 0
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);
}
}

+ 135
- 0
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 (
<ScrollView scrollY className={className}>
<AtList className={styles.list}>
{
node && node.mapChildrenIds(nodeId => {
const fileNode = nodeHash[nodeId];
if (!fileNode) return null;
return (
fileNode.type === 'folder'
? (
<ListItem
key={fileNode.id}
className={styles.listItem}
title={fileNode.label}
arrow="right"
// note="xxx个项目"
onClick={() => gotoNode(fileNode)}
thumb={getFileUrl('oss://assets/file-icon/folder.png')}
/>
)
: (
<ListItem
key={fileNode.id}
className={[styles.listItem, styles.fileItem]}
title={fileNode.label}
note="版本: xxx"
thumb={getFileUrl('oss://assets/file-icon/default.svg')}
extraThumb={getFileUrl('oss://assets/file-icon/download.png')}
/>
)
)
})
}
</AtList>
</ScrollView>
)
}


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 (
<View
className={[styles.listItem2, className]}
>
<View
className={[styles.content, initX ? styles.dragging : '']}
onTouchStart={enableDelete ? onTouchStart : undefined}
onTouchMove={enableDelete ? onTouchMove : undefined}
onTouchEnd={enableDelete ? onTouchEnd : undefined}
style={{ right: `${swipeWidth * 2}rpx` }}
onClick={onClick}
>
{
thumb ? (
<View className={styles.itemThumb}>
<Image className={styles.img} src={thumb} />
</View>
)
: null
}
<View className={styles.itemContent}>
<View className={styles.itemContent_info}>
<View className={styles.itemContent_info_title}>{title}</View>
{
note
? (
<View className={styles.itemContent_info_note}>{note}</View>
)
: null
}
</View>
</View>

<View className={styles.itemExtra}>
{
extraThumb
? (
<View className={styles.itemExtraThumb}>
<Image className={styles.img} src={extraThumb} />
</View>
)
: null
}
{
arrow
? (
<AtIcon className={styles.itemExtraIcon} value='chevron-right' />
)
: null
}
</View>
</View>
<View className={styles.actions}>
<View
className={styles.deleteBtn}
style={{ width: `${deleteBtnWidth * 2}rpx` }}
>删除</View>
</View>
</View>
)
}

+ 72
- 5
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 (
<View>
<Text>回收站页</Text>
</View>
<Page
className={styles.page}
title={
currentNode && currentNode.id !== 'root'
? currentNode.label
: '回收站'
}
headerColor="#EBEBEB"
leftText={
currentNode && currentNode.id !== 'root'
? '返回'// nodeHash[currentNode.parentId].label || '项目列表'
: undefined
}
onClickLeftIcon={() => {
setCurrentNode(prevNode => nodeHash[prevNode.parentId])
}}
>
<AtSearchBar
className={styles.search}
value={tempKeywords}
onChange={setTempKeywords}
onActionClick={() => setEffectKeywords(tempKeywords)}
onClear={() => {setEffectKeywords(''); setTempKeywords('');}}
/>
{
currentNode
? (
<FileExplorer
className={styles.list}
node={currentNode}
gotoNode={gotoNode}
nodeHash={nodeHash}
/>
)
: null
}
</Page>
)
}

+ 25
- 0
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});
}

+ 98
- 0
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;
}

+ 2
- 0
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;


+ 32
- 1
src/utils/tool.js 查看文件

@@ -46,4 +46,35 @@ class RequestHandler {
}
}

export const handleRequest = res => new RequestHandler(res);
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 = [];
}
}

Loading…
取消
儲存