Merge branch 'refs/heads/dev' into search
# Conflicts: # src/components/ShowFile/ShowFile.vue
This commit is contained in:
commit
07613e7924
7
.github/release-drafter.yml
vendored
7
.github/release-drafter.yml
vendored
@ -15,7 +15,7 @@ change-template: '- $TITLE (#$NUMBER)'
|
||||
no-changes-template: '- No changes'
|
||||
template: |
|
||||
|
||||
[docker-docker.yml](https://github.com/jamebal/jmal-cloud-server/blob/master/docker-compose.example2.yml)
|
||||
[docker-docker.yml](https://github.com/jamebal/jmal-cloud-server/blob/master/docker-compose.base.yml)
|
||||
|
||||
#### 更新前备份数据库
|
||||
|
||||
@ -27,6 +27,11 @@ template: |
|
||||
docker-compose pull && docker-compose up -d
|
||||
```
|
||||
|
||||
#### 清理旧镜像
|
||||
```shell
|
||||
docker images --filter=reference='jmal/jmalcloud*' --filter "dangling=true" -q | xargs -r docker rmi
|
||||
```
|
||||
|
||||
## Changes in this release:
|
||||
|
||||
$CHANGES
|
||||
|
||||
32
.github/workflows/build-nginx-drawio.yml
vendored
Normal file
32
.github/workflows/build-nginx-drawio.yml
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
name: Build nginx with Draw.io Docker Image
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: docker/nginx-drawio/Dockerfile
|
||||
push: true
|
||||
tags: jmal/nginx-drawio:latest
|
||||
platforms: linux/amd64,linux/arm64
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
25
.github/workflows/build.yml
vendored
25
.github/workflows/build.yml
vendored
@ -1,4 +1,4 @@
|
||||
name: Build and Push Multi-Platform Docker Image
|
||||
name: Build Docker Image
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@ -12,10 +12,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node.js 16.20.2
|
||||
uses: actions/setup-node@v2
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16.20.2
|
||||
|
||||
@ -31,23 +31,30 @@ jobs:
|
||||
echo "PROJECT_VERSION=$VERSION" >> $GITHUB_ENV
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ secrets.GHCR_IO_USERNAME }}
|
||||
password: ${{ secrets.GHCR_IO_TOKEN }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
push: true
|
||||
tags: jmal/jmalcloud-nginx:${{ env.PROJECT_VERSION }},jmal/jmalcloud-nginx:latest
|
||||
tags: |
|
||||
jmal/jmalcloud-nginx:${{ env.PROJECT_VERSION }},jmal/jmalcloud-nginx:latest
|
||||
ghcr.io/${{ secrets.GHCR_IO_USERNAME }}/jmalcloud-nginx:${{ env.PROJECT_VERSION }},ghcr.io/${{ secrets.GHCR_IO_USERNAME }}/jmalcloud-nginx:latest
|
||||
platforms: linux/amd64,linux/arm64
|
||||
build-args: |
|
||||
VERSION=${{ env.PROJECT_VERSION }}
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
87
.github/workflows/pushAliyun.yml
vendored
Normal file
87
.github/workflows/pushAliyun.yml
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
name: Build and Push Docker Image to Aliyun
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [ published ]
|
||||
tags: [ v* ]
|
||||
|
||||
jobs:
|
||||
build_and_push:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
arch:
|
||||
- amd64
|
||||
- arm64
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node.js 16.20.2
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16.20.2
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install && npm install --save core-js@2
|
||||
|
||||
- name: Build
|
||||
run: npm run build:prod
|
||||
|
||||
- name: Extract project version from package.json
|
||||
run: |
|
||||
VERSION=$(node -p "require('./package.json').version")
|
||||
echo "PROJECT_VERSION=$VERSION" >> $GITHUB_ENV
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to Aliyun Container Registry guangzhou
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: registry.cn-guangzhou.aliyuncs.com
|
||||
username: ${{ secrets.ALIYUN_DOCKER_USERNAME }}
|
||||
password: ${{ secrets.ALIYUN_DOCKER_PASSWORD }}
|
||||
|
||||
- name: Log in to Aliyun Container Registry beijing
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: registry.cn-beijing.aliyuncs.com
|
||||
username: ${{ secrets.ALIYUN_DOCKER_USERNAME }}
|
||||
password: ${{ secrets.ALIYUN_DOCKER_PASSWORD }}
|
||||
|
||||
- name: Set tag for amd64
|
||||
if: matrix.arch == 'amd64'
|
||||
run: |
|
||||
echo "TAG_LATEST_0=registry.cn-guangzhou.aliyuncs.com/jmalcloud/jmalcloud-nginx:latest" >> $GITHUB_ENV
|
||||
echo "TAG_VERSION_0=registry.cn-guangzhou.aliyuncs.com/jmalcloud/jmalcloud-nginx:${{ env.PROJECT_VERSION }}" >> $GITHUB_ENV
|
||||
echo "TAG_LATEST_1=registry.cn-beijing.aliyuncs.com/jmalcloud/jmalcloud-nginx:latest" >> $GITHUB_ENV
|
||||
echo "TAG_VERSION_1=registry.cn-beijing.aliyuncs.com/jmalcloud/jmalcloud-nginx:${{ env.PROJECT_VERSION }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Set tag for non-amd64
|
||||
if: matrix.arch != 'amd64'
|
||||
run: |
|
||||
echo "TAG_LATEST_0=registry.cn-guangzhou.aliyuncs.com/jmalcloud/jmalcloud-nginx:latest-${{ matrix.arch }}" >> $GITHUB_ENV
|
||||
echo "TAG_VERSION_0=registry.cn-guangzhou.aliyuncs.com/jmalcloud/jmalcloud-nginx:${{ env.PROJECT_VERSION }}-${{ matrix.arch }}" >> $GITHUB_ENV
|
||||
echo "TAG_LATEST_1=registry.cn-beijing.aliyuncs.com/jmalcloud/jmalcloud-nginx:latest-${{ matrix.arch }}" >> $GITHUB_ENV
|
||||
echo "TAG_VERSION_1=registry.cn-beijing.aliyuncs.com/jmalcloud/jmalcloud-nginx:${{ env.PROJECT_VERSION }}-${{ matrix.arch }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: linux/${{ matrix.arch }}
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.TAG_LATEST_0 }}
|
||||
${{ env.TAG_VERSION_0 }}
|
||||
${{ env.TAG_LATEST_1 }}
|
||||
${{ env.TAG_VERSION_1 }}
|
||||
build-args: |
|
||||
VERSION=${{ env.PROJECT_VERSION }}
|
||||
2
.github/workflows/release-drafter.yml
vendored
2
.github/workflows/release-drafter.yml
vendored
@ -10,7 +10,7 @@ jobs:
|
||||
update_release_draft:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- uses: release-drafter/release-drafter@v5
|
||||
with:
|
||||
config-name: release-drafter.yml
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
name: Build and Push Multi-Platform Docker Image
|
||||
name: Build Test Docker Image
|
||||
|
||||
on:
|
||||
push:
|
||||
@ -11,10 +11,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node.js 16.20.2
|
||||
uses: actions/setup-node@v2
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16.20.2
|
||||
|
||||
@ -30,23 +30,30 @@ jobs:
|
||||
echo "PROJECT_VERSION=$VERSION" >> $GITHUB_ENV
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ secrets.GHCR_IO_USERNAME }}
|
||||
password: ${{ secrets.GHCR_IO_TOKEN }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
push: true
|
||||
tags: jmal/jmalcloud-nginx:nightly
|
||||
tags: |
|
||||
jmal/jmalcloud-nginx:test
|
||||
ghcr.io/${{ secrets.GHCR_IO_USERNAME }}/jmalcloud-nginx:test
|
||||
platforms: linux/amd64,linux/arm64
|
||||
build-args: |
|
||||
VERSION=${{ env.PROJECT_VERSION }}
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
@ -1,11 +1,11 @@
|
||||
FROM nginx:latest
|
||||
FROM jmal/nginx-drawio:latest
|
||||
|
||||
COPY nginx.conf /etc/nginx/nginx.conf
|
||||
|
||||
COPY nginx.conf /etc/nginx/nginx.conf.template
|
||||
|
||||
COPY dist /var/www/public
|
||||
|
||||
COPY config.js.template /var/www/public/config.js.template
|
||||
|
||||
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
||||
RUN chmod +x /docker-entrypoint.sh
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@ JmalCloud 是一款私有云存储网盘项目,能够简单安全管理您的
|
||||
### 部署
|
||||
- 必须安装:`Docker` 和 `Docker Compose v2.0+`
|
||||
|
||||
参考[docker-compose.yml](https://github.com/jamebal/jmal-cloud-server/blob/master/docker-compose.example2.yml)
|
||||
参考[docker-compose.yml](https://github.com/jamebal/jmal-cloud-server/blob/master/docker-compose.base.yml)
|
||||
|
||||
```shell
|
||||
docker compose up -d
|
||||
@ -44,7 +44,7 @@ docker restart jmalcloud_server
|
||||
```
|
||||
#### 恢复数据库
|
||||
```shell
|
||||
docker exec -it jmalcloud_mongodb mongorestore -d jmalcloud --dir /dump/xxx --gzip --quiet
|
||||
docker exec -it jmalcloud_mongodb mongorestore --gzip --nsInclude=jmalcloud.* --dir /dump/xxx --quiet
|
||||
```
|
||||
|
||||
### dev
|
||||
|
||||
@ -16,13 +16,13 @@ JmalCloud It's a private cloud storage project that makes it simple and secure t
|
||||
✅ supports WebDAV
|
||||
|
||||
### Online Demo:
|
||||
[View Demo](https://jmal.cc/demo) , username:demo, password:demo1234
|
||||
[Live Demo](https://jmal.cc/demo) , username:demo, password:demo1234
|
||||
|
||||
|
||||
### Deploy
|
||||
- Prerequisites:`Docker` and `Docker Compose v2.0+`
|
||||
|
||||
Refer to [docker-compose.yml](https://github.com/jamebal/jmal-cloud-server/blob/master/docker-compose.example2.yml)
|
||||
Refer to [docker-compose.yml](https://github.com/jamebal/jmal-cloud-server/blob/master/docker-compose.base.yml)
|
||||
|
||||
```shell
|
||||
docker compose up -d
|
||||
|
||||
4
config.js.template
Normal file
4
config.js.template
Normal file
@ -0,0 +1,4 @@
|
||||
window._env_ = {
|
||||
API_URL: "${API_URL}",
|
||||
API_OFFICE_URL: "${API_OFFICE_URL}"
|
||||
};
|
||||
@ -1,8 +1,4 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# 使用envsubst命令替换nginx.conf.template文件中的环境变量
|
||||
envsubst '$API_BASE_URL $API_DRAWIO_URL $API_OFFICE_URL' < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf
|
||||
|
||||
# 启动Nginx
|
||||
envsubst '${API_URL} ${API_OFFICE_URL}' < /var/www/public/config.js.template > /var/www/public/config.js
|
||||
exec "$@"
|
||||
|
||||
17
docker/nginx-drawio/Dockerfile
Normal file
17
docker/nginx-drawio/Dockerfile
Normal file
@ -0,0 +1,17 @@
|
||||
FROM nginx:latest
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y unzip wget && \
|
||||
mkdir -p /etc/nginx/servers /var/www && \
|
||||
wget https://github.com/jgraph/drawio/releases/download/v24.4.10/draw.war -O /var/www/draw.war && \
|
||||
wget https://github.com/mozilla/pdf.js/releases/download/v4.3.136/pdfjs-4.3.136-dist.zip -O /var/www/pdfjs.zip && \
|
||||
unzip /var/www/draw.war -d /var/www/draw && \
|
||||
unzip /var/www/pdfjs.zip -d /var/www/pdfjs && \
|
||||
rm -rf /var/www/pdfjs.zip /var/www/draw.war /var/www/draw/META-INF /var/www/draw/WEB-INF && \
|
||||
apt-get remove -y unzip wget && \
|
||||
apt-get autoremove -y && \
|
||||
apt-get clean
|
||||
|
||||
COPY docker/nginx-drawio/drawio.conf /etc/nginx/servers/drawio.conf
|
||||
COPY docker/nginx-drawio/pdfjs.conf /etc/nginx/servers/pdfjs.conf
|
||||
COPY docker/nginx-drawio/mime.types /etc/nginx/mime.types
|
||||
10
docker/nginx-drawio/drawio.conf
Normal file
10
docker/nginx-drawio/drawio.conf
Normal file
@ -0,0 +1,10 @@
|
||||
server {
|
||||
listen 8080;
|
||||
server_name localhost;
|
||||
root /var/www/draw;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
98
docker/nginx-drawio/mime.types
Normal file
98
docker/nginx-drawio/mime.types
Normal file
@ -0,0 +1,98 @@
|
||||
types {
|
||||
text/html html htm shtml;
|
||||
text/css css;
|
||||
text/xml xml;
|
||||
image/gif gif;
|
||||
image/jpeg jpeg jpg;
|
||||
application/javascript js mjs;
|
||||
application/atom+xml atom;
|
||||
application/rss+xml rss;
|
||||
|
||||
text/mathml mml;
|
||||
text/plain txt;
|
||||
text/vnd.sun.j2me.app-descriptor jad;
|
||||
text/vnd.wap.wml wml;
|
||||
text/x-component htc;
|
||||
|
||||
image/avif avif;
|
||||
image/png png;
|
||||
image/svg+xml svg svgz;
|
||||
image/tiff tif tiff;
|
||||
image/vnd.wap.wbmp wbmp;
|
||||
image/webp webp;
|
||||
image/x-icon ico;
|
||||
image/x-jng jng;
|
||||
image/x-ms-bmp bmp;
|
||||
|
||||
font/woff woff;
|
||||
font/woff2 woff2;
|
||||
|
||||
application/java-archive jar war ear;
|
||||
application/json json;
|
||||
application/mac-binhex40 hqx;
|
||||
application/msword doc;
|
||||
application/pdf pdf;
|
||||
application/postscript ps eps ai;
|
||||
application/rtf rtf;
|
||||
application/vnd.apple.mpegurl m3u8;
|
||||
application/vnd.google-earth.kml+xml kml;
|
||||
application/vnd.google-earth.kmz kmz;
|
||||
application/vnd.ms-excel xls;
|
||||
application/vnd.ms-fontobject eot;
|
||||
application/vnd.ms-powerpoint ppt;
|
||||
application/vnd.oasis.opendocument.graphics odg;
|
||||
application/vnd.oasis.opendocument.presentation odp;
|
||||
application/vnd.oasis.opendocument.spreadsheet ods;
|
||||
application/vnd.oasis.opendocument.text odt;
|
||||
application/vnd.openxmlformats-officedocument.presentationml.presentation
|
||||
pptx;
|
||||
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
||||
xlsx;
|
||||
application/vnd.openxmlformats-officedocument.wordprocessingml.document
|
||||
docx;
|
||||
application/vnd.wap.wmlc wmlc;
|
||||
application/wasm wasm;
|
||||
application/x-7z-compressed 7z;
|
||||
application/x-cocoa cco;
|
||||
application/x-java-archive-diff jardiff;
|
||||
application/x-java-jnlp-file jnlp;
|
||||
application/x-makeself run;
|
||||
application/x-perl pl pm;
|
||||
application/x-pilot prc pdb;
|
||||
application/x-rar-compressed rar;
|
||||
application/x-redhat-package-manager rpm;
|
||||
application/x-sea sea;
|
||||
application/x-shockwave-flash swf;
|
||||
application/x-stuffit sit;
|
||||
application/x-tcl tcl tk;
|
||||
application/x-x509-ca-cert der pem crt;
|
||||
application/x-xpinstall xpi;
|
||||
application/xhtml+xml xhtml;
|
||||
application/xspf+xml xspf;
|
||||
application/zip zip;
|
||||
|
||||
application/octet-stream bin exe dll;
|
||||
application/octet-stream deb;
|
||||
application/octet-stream dmg;
|
||||
application/octet-stream iso img;
|
||||
application/octet-stream msi msp msm;
|
||||
|
||||
audio/midi mid midi kar;
|
||||
audio/mpeg mp3;
|
||||
audio/ogg ogg;
|
||||
audio/x-m4a m4a;
|
||||
audio/x-realaudio ra;
|
||||
|
||||
video/3gpp 3gpp 3gp;
|
||||
video/mp2t ts;
|
||||
video/mp4 mp4;
|
||||
video/mpeg mpeg mpg;
|
||||
video/quicktime mov;
|
||||
video/webm webm;
|
||||
video/x-flv flv;
|
||||
video/x-m4v m4v;
|
||||
video/x-mng mng;
|
||||
video/x-ms-asf asx asf;
|
||||
video/x-ms-wmv wmv;
|
||||
video/x-msvideo avi;
|
||||
}
|
||||
5
docker/nginx-drawio/pdfjs.conf
Normal file
5
docker/nginx-drawio/pdfjs.conf
Normal file
@ -0,0 +1,5 @@
|
||||
server {
|
||||
listen 8081;
|
||||
server_name localhost;
|
||||
root /var/www/pdfjs;
|
||||
}
|
||||
52
nginx.conf
52
nginx.conf
@ -89,6 +89,36 @@ http {
|
||||
index index.html index.htm;
|
||||
}
|
||||
|
||||
location = /blog {
|
||||
proxy_pass http://jmalcloud:8088/articles;
|
||||
proxy_set_header Host $proxy_host;
|
||||
proxy_set_header X-real-ip $remote_addr;
|
||||
}
|
||||
|
||||
location = /blog/ {
|
||||
proxy_pass http://jmalcloud:8088/articles;
|
||||
proxy_set_header Host $proxy_host;
|
||||
proxy_set_header X-real-ip $remote_addr;
|
||||
}
|
||||
|
||||
location /blog/api/ {
|
||||
proxy_pass http://jmalcloud:8088/;
|
||||
proxy_set_header Host $proxy_host;
|
||||
proxy_set_header X-real-ip $remote_addr;
|
||||
}
|
||||
|
||||
location /blog/articles {
|
||||
proxy_pass http://jmalcloud:8088/articles/;
|
||||
proxy_set_header Host $proxy_host;
|
||||
proxy_set_header X-real-ip $remote_addr;
|
||||
}
|
||||
|
||||
location /blog/ {
|
||||
proxy_pass http://jmalcloud:8088/articles/;
|
||||
proxy_set_header Host $proxy_host;
|
||||
proxy_set_header X-real-ip $remote_addr;
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
proxy_pass http://jmalcloud:8088/;
|
||||
proxy_http_version 1.1;
|
||||
@ -179,11 +209,29 @@ http {
|
||||
}
|
||||
|
||||
location /drawio/webapp/ {
|
||||
proxy_pass http://drawio-webapp:8080/;
|
||||
proxy_pass http://localhost:8080/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Real-PORT $remote_port;
|
||||
proxy_set_header X-Forwarded-Host $the_host/drawio/webapp;
|
||||
proxy_set_header X-Forwarded-Host $the_host//drawio/webapp;
|
||||
proxy_set_header X-Forwarded-Proto $the_scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header Scheme $scheme;
|
||||
proxy_set_header Server-Protocol $server_protocol;
|
||||
proxy_set_header Server-Name $server_name;
|
||||
proxy_set_header Server-Addr $server_addr;
|
||||
proxy_set_header Server-Port $server_port;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
}
|
||||
|
||||
location /pdf.js/ {
|
||||
proxy_pass http://localhost:8081/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Real-PORT $remote_port;
|
||||
proxy_set_header X-Forwarded-Host $the_host//pdf.js;
|
||||
proxy_set_header X-Forwarded-Proto $the_scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Host $http_host;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jmal-cloud-view",
|
||||
"version": "2.8.3",
|
||||
"version": "2.9.2",
|
||||
"description": "jmalcloud",
|
||||
"author": "Jmal <zhushilun084@163.com>",
|
||||
"license": "MIT",
|
||||
@ -16,7 +16,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"ant-design-vue": "^1.6.5",
|
||||
"artplayer": "^5.1.1",
|
||||
"clipboard": "^2.0.6",
|
||||
"cropperjs": "^1.5.6",
|
||||
"e-vue-contextmenu": "^0.1.3",
|
||||
|
||||
8
prettier.config.js
Normal file
8
prettier.config.js
Normal file
@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
singleQuote: true,
|
||||
semi: false,
|
||||
trailingComma: 'es6',
|
||||
tabWidth: 2,
|
||||
printWidth: 80,
|
||||
endOfLine: 'lf'
|
||||
}
|
||||
4
public/config.js
Normal file
4
public/config.js
Normal file
@ -0,0 +1,4 @@
|
||||
window._env_ = {
|
||||
API_URL: "",
|
||||
API_OFFICE_URL: "",
|
||||
};
|
||||
@ -17,8 +17,8 @@
|
||||
<!--axios.js-->
|
||||
<script src="<%= htmlWebpackPlugin.options.path %>/resource/axios@0.21.1.js"></script>
|
||||
<!--vant.js-->
|
||||
<link rel="stylesheet" href="<%= htmlWebpackPlugin.options.path %>/resource/vant@2.12.css"/>
|
||||
<script src="<%= htmlWebpackPlugin.options.path %>/resource/vant@2.12.js"></script>
|
||||
<link rel="stylesheet" href="<%= htmlWebpackPlugin.options.path %>/resource/vant@2.12.54.css"/>
|
||||
<script src="<%= htmlWebpackPlugin.options.path %>/resource/vant@2.12.54.js"></script>
|
||||
<!--element-ui.js-->
|
||||
<link rel="stylesheet" href="<%= htmlWebpackPlugin.options.path %>/resource/element-ui@2.13.0.css">
|
||||
<script src="<%= htmlWebpackPlugin.options.path %>/resource/element-ui@2.13.0.js"></script>
|
||||
@ -35,7 +35,9 @@
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title><%= webpackConfig.name %></title>
|
||||
<link rel="stylesheet" href="<%= htmlWebpackPlugin.options.path %>/resource/font-awesome/free-6.4.2/css/all.min.css">
|
||||
<script src="/config.js"></script>
|
||||
|
||||
<!-- <script type="module" src="<%= htmlWebpackPlugin.options.path %>/resource/model-viewer.min.js"></script>-->
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
|
||||
8
public/resource/artplayer@5.1.6.js
Normal file
8
public/resource/artplayer@5.1.6.js
Normal file
File diff suppressed because one or more lines are too long
282
public/resource/epubjs/epub.css
Normal file
282
public/resource/epubjs/epub.css
Normal file
@ -0,0 +1,282 @@
|
||||
body {
|
||||
margin: 0;
|
||||
background: #fafafa;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
color: #333;
|
||||
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
min-height: 800px;
|
||||
}
|
||||
|
||||
#title {
|
||||
width: 900px;
|
||||
min-height: 18px;
|
||||
margin: 10px auto;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
color: #E2E2E2;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
#title:hover {
|
||||
color: #777;
|
||||
}
|
||||
|
||||
#viewer.spreads {
|
||||
width: 900px;
|
||||
height: 600px;
|
||||
box-shadow: 0 0 4px #ccc;
|
||||
border-radius: 5px;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
margin: 10px auto;
|
||||
background: white url('ajax-loader.gif') center center no-repeat;
|
||||
top: calc(50vh - 400px);
|
||||
}
|
||||
|
||||
#viewer.spreads .epub-view > iframe {
|
||||
background: white;
|
||||
}
|
||||
|
||||
#viewer.scrolled {
|
||||
overflow: hidden;
|
||||
width: 800px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
background: url('ajax-loader.gif') center center no-repeat;
|
||||
box-shadow: 0 0 4px #ccc;
|
||||
padding: 20px;
|
||||
background: white;
|
||||
}
|
||||
|
||||
#viewer.scrolled .epub-view > iframe {
|
||||
background: white;
|
||||
}
|
||||
|
||||
#prev {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
#next {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
#toc {
|
||||
display: block;
|
||||
margin: 10px auto;
|
||||
}
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
#viewer.spreads:after {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
border-right: 1px #000 solid;
|
||||
height: 90%;
|
||||
z-index: 1;
|
||||
left: 50%;
|
||||
margin-left: -1px;
|
||||
top: 5%;
|
||||
opacity: .15;
|
||||
box-shadow: -2px 0 15px rgba(0, 0, 0, 1);
|
||||
content: "";
|
||||
}
|
||||
|
||||
#viewer.spreads.single:after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#prev {
|
||||
left: 40px;
|
||||
}
|
||||
|
||||
#next {
|
||||
right: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.arrow {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
margin-top: -32px;
|
||||
font-size: 64px;
|
||||
color: #E2E2E2;
|
||||
font-family: arial, sans-serif;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.navlink {
|
||||
margin: 14px;
|
||||
display: block;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.arrow:hover, .navlink:hover {
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.arrow:active, .navlink:hover {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
#book-wrapper {
|
||||
width: 480px;
|
||||
height: 640px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #ccc;
|
||||
margin: 28px auto;
|
||||
background: #fff;
|
||||
border-radius: 0 5px 5px 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
#book-viewer {
|
||||
width: 480px;
|
||||
height: 660px;
|
||||
margin: -30px auto;
|
||||
-moz-box-shadow: inset 10px 0 20px rgba(0,0,0,.1);
|
||||
-webkit-box-shadow: inset 10px 0 20px rgba(0,0,0,.1);
|
||||
box-shadow: inset 10px 0 20px rgba(0,0,0,.1);
|
||||
}
|
||||
|
||||
#book-viewer iframe {
|
||||
padding: 40px 40px;
|
||||
}
|
||||
|
||||
#controls {
|
||||
position: absolute;
|
||||
bottom: 16px;
|
||||
left: 50%;
|
||||
width: 400px;
|
||||
margin-left: -200px;
|
||||
text-align: center;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#controls > input[type=range] {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
#navigation {
|
||||
width: 400px;
|
||||
height: 100vh;
|
||||
position: absolute;
|
||||
overflow: auto;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: #777;
|
||||
-webkit-transition: -webkit-transform .25s ease-out;
|
||||
-moz-transition: -moz-transform .25s ease-out;
|
||||
-ms-transition: -moz-transform .25s ease-out;
|
||||
transition: transform .25s ease-out;
|
||||
|
||||
}
|
||||
|
||||
#navigation.fixed {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
#navigation h1 {
|
||||
width: 200px;
|
||||
font-size: 16px;
|
||||
font-weight: normal;
|
||||
color: #fff;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
#navigation h2 {
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
color: #B0B0B0;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#navigation ul {
|
||||
padding-left: 36px;
|
||||
margin-left: 0;
|
||||
margin-top: 12px;
|
||||
margin-bottom: 12px;
|
||||
width: 340px;
|
||||
}
|
||||
|
||||
#navigation ul li {
|
||||
list-style: decimal;
|
||||
margin-bottom: 10px;
|
||||
color: #cccddd;
|
||||
font-size: 12px;
|
||||
padding-left: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
#navigation ul li a {
|
||||
color: #ccc;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#navigation ul li a:hover {
|
||||
color: #fff;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#navigation ul li a.active {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#navigation #cover {
|
||||
display: block;
|
||||
margin: 24px auto;
|
||||
}
|
||||
|
||||
#navigation #closer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: 12px;
|
||||
color: #cccddd;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
#navigation.closed {
|
||||
-webkit-transform: translate(-400px, 0);
|
||||
-moz-transform: translate(-400px, 0);
|
||||
-ms-transform: translate(-400px, 0);
|
||||
transform: translate(-400px, 0);
|
||||
}
|
||||
|
||||
svg {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.close-x {
|
||||
stroke: #cccddd;
|
||||
fill: transparent;
|
||||
stroke-linecap: round;
|
||||
stroke-width: 5;
|
||||
}
|
||||
|
||||
.close-x:hover {
|
||||
stroke: #fff;
|
||||
}
|
||||
|
||||
#opener {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
padding: 10px;
|
||||
stroke: #E2E2E2;
|
||||
fill: #E2E2E2;
|
||||
|
||||
}
|
||||
|
||||
#opener:hover {
|
||||
stroke: #777;
|
||||
fill: #777;
|
||||
}
|
||||
1
public/resource/epubjs/epubjs@0.3.88.min.js
vendored
Normal file
1
public/resource/epubjs/epubjs@0.3.88.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
13
public/resource/epubjs/jszip@3.10.1.min.js
vendored
Normal file
13
public/resource/epubjs/jszip@3.10.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
167
public/resource/epubjs/viewer.html
Normal file
167
public/resource/epubjs/viewer.html
Normal file
@ -0,0 +1,167 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>EPUB.js Spreads Example</title>
|
||||
|
||||
<script src="./jszip@3.10.1.min.js"></script>
|
||||
<script src="./epubjs@0.3.88.min.js"></script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="./epub.css">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<!-- <div id="title"></div> -->
|
||||
<select id="toc"></select>
|
||||
<div id="viewer" class="spreads"></div>
|
||||
<a id="prev" href="#prev" class="arrow">‹</a>
|
||||
<a id="next" href="#next" class="arrow">›</a>
|
||||
|
||||
<script>
|
||||
var params =
|
||||
URLSearchParams &&
|
||||
new URLSearchParams(document.location.search.substring(1))
|
||||
var url =
|
||||
params && params.get("url") && decodeURIComponent(params.get("url"))
|
||||
var currentSectionIndex =
|
||||
params && params.get("loc") ? params.get("loc") : undefined
|
||||
let blob = fetch(url).then((res) => res.blob())
|
||||
var book = ePub(
|
||||
blob.then((blob) => blob.arrayBuffer()),
|
||||
{
|
||||
restore: true,
|
||||
reload: true,
|
||||
spreads: true,
|
||||
}
|
||||
)
|
||||
var rendition = book.renderTo("viewer", {
|
||||
width: "100%",
|
||||
height: 600,
|
||||
spread: "always",
|
||||
})
|
||||
|
||||
rendition.display(currentSectionIndex);
|
||||
|
||||
book.ready.then(function() {
|
||||
|
||||
var next = document.getElementById("next");
|
||||
|
||||
next.addEventListener("click", function(e){
|
||||
book.package.metadata.direction === "rtl" ? rendition.prev() : rendition.next();
|
||||
e.preventDefault();
|
||||
}, false);
|
||||
|
||||
var prev = document.getElementById("prev");
|
||||
prev.addEventListener("click", function(e){
|
||||
book.package.metadata.direction === "rtl" ? rendition.next() : rendition.prev();
|
||||
e.preventDefault();
|
||||
}, false);
|
||||
|
||||
var keyListener = function(e){
|
||||
|
||||
// Left Key
|
||||
if ((e.keyCode || e.which) == 37) {
|
||||
book.package.metadata.direction === "rtl" ? rendition.next() : rendition.prev();
|
||||
}
|
||||
|
||||
// Right Key
|
||||
if ((e.keyCode || e.which) == 39) {
|
||||
book.package.metadata.direction === "rtl" ? rendition.prev() : rendition.next();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
rendition.on("keyup", keyListener);
|
||||
document.addEventListener("keyup", keyListener, false);
|
||||
|
||||
})
|
||||
|
||||
var title = document.getElementById("title");
|
||||
|
||||
rendition.on("rendered", function(section){
|
||||
var current = book.navigation && book.navigation.get(section.href);
|
||||
|
||||
if (current) {
|
||||
var $select = document.getElementById("toc");
|
||||
var $selected = $select.querySelector("option[selected]");
|
||||
if ($selected) {
|
||||
$selected.removeAttribute("selected");
|
||||
}
|
||||
|
||||
var $options = $select.querySelectorAll("option");
|
||||
for (var i = 0; i < $options.length; ++i) {
|
||||
let selected = $options[i].getAttribute("ref") === current.href;
|
||||
if (selected) {
|
||||
$options[i].setAttribute("selected", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
rendition.on("relocated", function(location){
|
||||
console.log(location);
|
||||
|
||||
var next = book.package.metadata.direction === "rtl" ? document.getElementById("prev") : document.getElementById("next");
|
||||
var prev = book.package.metadata.direction === "rtl" ? document.getElementById("next") : document.getElementById("prev");
|
||||
|
||||
if (location.atEnd) {
|
||||
next.style.visibility = "hidden";
|
||||
} else {
|
||||
next.style.visibility = "visible";
|
||||
}
|
||||
|
||||
if (location.atStart) {
|
||||
prev.style.visibility = "hidden";
|
||||
} else {
|
||||
prev.style.visibility = "visible";
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
rendition.on("layout", function(layout) {
|
||||
let viewer = document.getElementById("viewer");
|
||||
|
||||
if (layout.spread) {
|
||||
viewer.classList.remove('single');
|
||||
} else {
|
||||
viewer.classList.add('single');
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener("unload", function () {
|
||||
console.log("unloading");
|
||||
this.book.destroy();
|
||||
});
|
||||
|
||||
book.loaded.navigation.then(function(toc){
|
||||
var $select = document.getElementById("toc"),
|
||||
docfrag = document.createDocumentFragment();
|
||||
|
||||
toc.forEach(function(chapter) {
|
||||
var option = document.createElement("option");
|
||||
option.textContent = chapter.label;
|
||||
option.setAttribute("ref", chapter.href);
|
||||
|
||||
docfrag.appendChild(option);
|
||||
});
|
||||
|
||||
$select.appendChild(docfrag);
|
||||
|
||||
$select.onchange = function(){
|
||||
var index = $select.selectedIndex,
|
||||
url = $select.options[index].getAttribute("ref");
|
||||
rendition.display(url);
|
||||
return false;
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
1
public/resource/font_2024484_jfax4gppvg.js
Normal file
1
public/resource/font_2024484_jfax4gppvg.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1093
public/resource/model-viewer.min.js
vendored
Normal file
1093
public/resource/model-viewer.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
7
public/resource/vant@2.12.54.js
Normal file
7
public/resource/vant@2.12.54.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
17
src/App.vue
17
src/App.vue
@ -5,22 +5,21 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
const pcList = ['/','/s']
|
||||
const mobileList = ['/_m','/_m/','/s_m','/s_m/']
|
||||
const pcList = ['/', '/s']
|
||||
const mobileList = ['/_m', '/_m/']
|
||||
export default {
|
||||
name: 'App',
|
||||
components: {},
|
||||
watch: {
|
||||
$route: function(to) {
|
||||
const path = to.path
|
||||
if(pcList.includes(path) && !this.$pc){
|
||||
this.$router.push({path: path+'_m', query: to.query})
|
||||
if (pcList.includes(path) && !this.$pc) {
|
||||
this.$router.push({ path: path + '_m', query: to.query })
|
||||
}
|
||||
if(mobileList.includes(path) && this.$pc){
|
||||
this.$router.push({path: path.split('_m')[0], query: to.query})
|
||||
if (mobileList.includes(path) && this.$pc) {
|
||||
this.$router.push({ path: path.split('_m')[0], query: to.query })
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -16,11 +16,11 @@ export default {
|
||||
})
|
||||
},
|
||||
// 检查文件是否存在
|
||||
checkExist: function (params) {
|
||||
checkExist: function (data) {
|
||||
return request({
|
||||
url: "checkExist",
|
||||
method: "post",
|
||||
params
|
||||
data
|
||||
})
|
||||
},
|
||||
// 上传文件夹
|
||||
@ -100,6 +100,35 @@ export default {
|
||||
}
|
||||
})
|
||||
},
|
||||
// 返回原处
|
||||
restore: function(params) {
|
||||
return request({
|
||||
url: 'restore',
|
||||
method: 'post',
|
||||
params,
|
||||
paramsSerializer: function(params) {
|
||||
return qs.stringify(params, { arrayFormat: 'repeat' })
|
||||
}
|
||||
})
|
||||
},
|
||||
// 彻底删除
|
||||
sweep: function(params) {
|
||||
return request({
|
||||
url: 'sweep',
|
||||
method: 'delete',
|
||||
params,
|
||||
paramsSerializer: function(params) {
|
||||
return qs.stringify(params, { arrayFormat: 'repeat' })
|
||||
}
|
||||
})
|
||||
},
|
||||
// 清空回收站
|
||||
clearTrash: function() {
|
||||
return request({
|
||||
url: 'clear-trash',
|
||||
method: 'delete'
|
||||
})
|
||||
},
|
||||
// 重名名
|
||||
rename: function(params) {
|
||||
return request({
|
||||
@ -189,6 +218,14 @@ export default {
|
||||
data: data
|
||||
})
|
||||
},
|
||||
// 生成shareToken
|
||||
generateShareToken: function(params) {
|
||||
return request({
|
||||
url: '/share/generate/share-token',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
},
|
||||
// 取消分享
|
||||
cancelShareLink: function(params) {
|
||||
return request({
|
||||
@ -241,9 +278,9 @@ export default {
|
||||
})
|
||||
},
|
||||
// 获取分享信息
|
||||
getShare: function(params) {
|
||||
getShareByFileId: function(params) {
|
||||
return request({
|
||||
url: '/get/share',
|
||||
url: '/get/share/by/fileId',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
@ -338,5 +375,22 @@ export default {
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
},
|
||||
// 获取office jwt
|
||||
getOfficeJwt: function(data) {
|
||||
return request({
|
||||
url: '/office/signature',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
},
|
||||
// 获取office jwt
|
||||
getPublicOfficeJwt: function(params, data) {
|
||||
return request({
|
||||
url: '/public/office/signature',
|
||||
method: 'post',
|
||||
params,
|
||||
data
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
@ -1,13 +1,55 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export default {
|
||||
sync(params) {
|
||||
getPreviewConfig(params) {
|
||||
return request({
|
||||
url: '/cloud/setting/preview/config',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
},
|
||||
updatePreviewConfig(data) {
|
||||
return request({
|
||||
url: '/cloud/setting/preview/config',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
},
|
||||
getTranscodeConfig(params) {
|
||||
return request({
|
||||
url: '/cloud/setting/transcode/config',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
},
|
||||
setTranscodeConfig(data) {
|
||||
return request({
|
||||
url: '/cloud/setting/transcode/config',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
},
|
||||
cancelTranscodeTask(data) {
|
||||
return request({
|
||||
url: '/cloud/setting/transcode/cancel-task',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
},
|
||||
syncUser(params) {
|
||||
return request({
|
||||
url: '/user/setting/sync',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
},
|
||||
syncCloud(params) {
|
||||
return request({
|
||||
url: '/cloud/setting/sync',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
},
|
||||
isSync(params) {
|
||||
return request({
|
||||
url: '/user/setting/isSync',
|
||||
@ -62,6 +104,29 @@ export default {
|
||||
url: '/user/setting/resetMenuAndRole',
|
||||
method: 'put',
|
||||
})
|
||||
},
|
||||
// 获取office配置
|
||||
getPublicOfficeConfig: function(params) {
|
||||
return request({
|
||||
url: 'public/office/config',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
},
|
||||
// 获取office配置
|
||||
getOfficeConfig: function() {
|
||||
return request({
|
||||
url: '/office/config',
|
||||
method: 'get'
|
||||
})
|
||||
},
|
||||
// 更新office配置
|
||||
updateOfficeConfig: function(data) {
|
||||
return request({
|
||||
url: '/office/config',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,3 +161,19 @@ export function getHeartwings(params) {
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function getTaskProgress(params) {
|
||||
return request({
|
||||
url: '/cloud/task/progress',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function getTranscodeStatus(params) {
|
||||
return request({
|
||||
url: '/cloud/transcode/status',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
@ -62,14 +62,6 @@ export function getUserInfo(params) {
|
||||
})
|
||||
}
|
||||
|
||||
export function getUsername(params) {
|
||||
return request({
|
||||
url: 'user/username',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function userList(params) {
|
||||
return request({
|
||||
url: 'user/userList',
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { iconClass,suffix } from '@/utils/file-type'
|
||||
import { findIconClass,suffix } from '@/utils/file-type'
|
||||
import 'jquery.fancytree/dist/skin-win8/ui.fancytree.less';
|
||||
import 'jquery.fancytree/dist/modules/jquery.fancytree.edit';
|
||||
import fancytree from 'jquery.fancytree';
|
||||
@ -450,34 +450,7 @@
|
||||
})
|
||||
},
|
||||
icon(event,data) {
|
||||
return { html: this.loadSvg(this.findIconClass(data.node.data)) }
|
||||
},
|
||||
findIconClass(file){
|
||||
if(!file){
|
||||
return
|
||||
}
|
||||
if(file.isFolder){
|
||||
return 'folder'
|
||||
}
|
||||
if (file.contentType) {
|
||||
if(file.contentType.indexOf('video') > -1){
|
||||
return 'video'
|
||||
}
|
||||
if(file.contentType.startsWith('image')){
|
||||
return 'image'
|
||||
}
|
||||
if(file.contentType.indexOf('audio') > -1){
|
||||
return 'audio'
|
||||
}
|
||||
}
|
||||
let suffix = file.suffix;
|
||||
if(!suffix && file.name){
|
||||
suffix = file.name.substring(file.name.lastIndexOf('.') + 1)
|
||||
}
|
||||
if(iconClass.has(suffix)){
|
||||
return iconClass.get(suffix)
|
||||
}
|
||||
return 'file'
|
||||
return { html: this.loadSvg(findIconClass(data.node.data)) }
|
||||
},
|
||||
loadSvg(svgName){
|
||||
if(svgName){
|
||||
|
||||
@ -5,18 +5,17 @@
|
||||
|
||||
import { Icon } from 'ant-design-vue'
|
||||
const IconFont = Icon.createFromIconfontCN({
|
||||
scriptUrl: document.location.protocol + '/resource/font_2024484_ziwmm4cu4r.js'
|
||||
scriptUrl: document.location.protocol + '/resource/font_2024484_jfax4gppvg.js'
|
||||
})
|
||||
|
||||
// 把此段代码贴到iconfont项目页面的控制台中, 以获取图标列表
|
||||
|
||||
// const nodeArray = Array.from(document.querySelectorAll('.block-icon-list .icon-item .icon-code.icon-code-show'));
|
||||
// const iconNames = nodeArray.map(item => item.outerText.replace('icon-',''))
|
||||
// console.log(iconNames.slice(0,80))
|
||||
// console.log(iconNames.slice(80,160))
|
||||
|
||||
export const iconFonts = {
|
||||
list: ["qrcode", "arrow1_left", "wulianwang-","zuji","disk-drive", "menu-fuzhi", "iso", "zuijinhuoyue","key", "shouquan", "sort", "grid", "list", "emoji-a", "emoji-bizui", "emoji-baiyan", "emoji-aixin", "emoji-dajing", "emoji-ziya", "emoji-daxiao", "emoji-esi", "emoji-fadai", "emoji-fankun", "emoji-ganga", "emoji-fennu", "emoji-hanyan", "emoji-jingkong", "emoji-haochi", "emoji-emo", "emoji-jingsong", "emoji-jingya", "emoji-kaixin", "emoji-lengku", "emoji-danao", "emoji-liukoushui", "emoji-liulei", "emoji-mengbi", "mianwubiaoqing", "nanguo", "shuizhuo", "taoyan", "tanchi", "siliao", "tiaopi", "xiaochulei", "wuliao", "xingxingyan", "xieyan", "xiasi", "xiaolian", "ku", "shengqi", "yousiliao", "en", "bushufu", "bianbian", "fankun1", "feiwen", "ganmao", "huaixiao", "liuhan", "outu", "keshui", "renzhe", "santiaoxian", "guaiwu", "shoushang", "tianshi", "shuai", "xianwen", "xiaodiaodaya", "xiong", "yiwen", "yun", "liubixie", "shimo", "dianzan", "biti", "fendou", "huqi", "heng", "kulou", "leng", "taoyan1", "shuixing", "aini", "aixin1", "zhadan", "xinsui",
|
||||
list: ['huanyuan', 'huishouzhan', 'glb', 'gengxinjindu', 'duigou', 'bars-solid', 'sort-amount-down-solid', 'sort-amount-up-alt-solid', 'tag2', 'guazai', 'duplicate', 'play1', "qrcode", "arrow1_left", "wulianwang-","zuji","disk-drive", "menu-fuzhi", "iso", "zuijinhuoyue","key", "shouquan", "sort", "grid", "list", "emoji-a", "emoji-bizui", "emoji-baiyan", "emoji-aixin", "emoji-dajing", "emoji-ziya", "emoji-daxiao", "emoji-esi", "emoji-fadai", "emoji-fankun", "emoji-ganga", "emoji-fennu", "emoji-hanyan", "emoji-jingkong", "emoji-haochi", "emoji-emo", "emoji-jingsong", "emoji-jingya", "emoji-kaixin", "emoji-lengku", "emoji-danao", "emoji-liukoushui", "emoji-liulei", "emoji-mengbi", "mianwubiaoqing", "nanguo", "shuizhuo", "taoyan", "tanchi", "siliao", "tiaopi", "xiaochulei", "wuliao", "xingxingyan", "xieyan", "xiasi", "xiaolian", "ku", "shengqi", "yousiliao", "en", "bushufu", "bianbian", "fankun1", "feiwen", "ganmao", "huaixiao", "liuhan", "outu", "keshui", "renzhe", "santiaoxian", "guaiwu", "shoushang", "tianshi", "shuai", "xianwen", "xiaodiaodaya", "xiong", "yiwen", "yun", "liubixie", "shimo", "dianzan", "biti", "fendou", "huqi", "heng", "kulou", "leng", "taoyan1", "shuixing", "aini", "aixin1", "zhadan", "xinsui",
|
||||
"maren", "zhutou", "qie", "youling", "apple-fill", "attachment", "android-fill", "apple", "download", "arrow-up-circle", "arrow-right-circle", "arrow-down", "ashbin", "add", "file_music", "menu", "role", "jiaoseguanli", "yemianshezhi", "drop_down", "yuandian", "tag", "service-directory", "fabu", "wailian", "bianji-", "WEBSITE", "wangzhanguanli", "Up", "up", "fenlei", "guanlizhuanjiawenzhang", "pifu", "tuichudenglu", "shezhi", "wangzhan", "huaban", "yunshezhi", "UserSettings", "blogger", "biaoqian", "myself", "guidang", "quxiaoyulan1-copy", "yulan-copy", "quxiaoyulan1", "yulan"]
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<span>
|
||||
<div :style="{position: grid ? 'relative': 'unset'}">
|
||||
<div v-if="item.isFavorite && !public">
|
||||
<div v-if="pc">
|
||||
<svg-icon v-if="!grid" class="pc list icon-favorite" icon-class="Favoritestarrate"/>
|
||||
@ -57,9 +57,9 @@
|
||||
<div v-if="grid && pc" class="grid-play-icon">
|
||||
<svg-icon icon-class="play1"/>
|
||||
</div>
|
||||
<el-image lazy v-if="grid" :style="{'height':details?'110px':(gridWidth-35) + 'px'}" fit="contain"
|
||||
<el-image lazy v-if="grid" :style="{'height':details?'110px':(gridWidth-50) + 'px'}" fit="contain"
|
||||
:src="item.fileId ? (audioCoverUrl+item.fileId) : (audioCoverUrl+item.id)">
|
||||
<div slot="error" class="image-slot">
|
||||
<div slot="error" class="image-slot-error">
|
||||
<svg-icon icon-class="video"/>
|
||||
</div>
|
||||
</el-image>
|
||||
@ -75,9 +75,9 @@
|
||||
<div v-else-if="item.contentType && item.contentType.indexOf('audio') > -1">
|
||||
<div v-if="item.music !== undefined">
|
||||
<div v-if="item.music.name !== null">
|
||||
<el-image v-if="grid" :style="{'height':details?'110px':(gridWidth-35) + 'px'}" fit="contain"
|
||||
<el-image v-if="grid" :style="{'height':details?'110px':(gridWidth-50) + 'px'}" fit="contain"
|
||||
:src="item.fileId ? (audioCoverUrl+item.fileId) : (audioCoverUrl+item.id)">
|
||||
<div slot="error" class="image-slot">
|
||||
<div slot="error" class="image-slot-error">
|
||||
<svg-icon icon-class="audio"/>
|
||||
</div>
|
||||
</el-image>
|
||||
@ -94,7 +94,7 @@
|
||||
<div v-else-if="item.contentType && item.contentType.startsWith('image')">
|
||||
<el-image v-if="grid || grid === 'details'" :style="{'height':details?'110px':(gridWidth-35) + 'px'}"
|
||||
fit="contain" :src="item.fileId ? (imageUrl+item.fileId) : (imageUrl+item.id)">
|
||||
<div slot="error" class="image-slot">
|
||||
<div slot="error" class="image-slot-error">
|
||||
<svg-icon icon-class="image"/>
|
||||
</div>
|
||||
</el-image>
|
||||
@ -102,12 +102,8 @@
|
||||
<el-avatar v-if="!grid" shape="square"
|
||||
:src="item.fileId ? (imageUrl+item.fileId) : (imageUrl+item.id)"></el-avatar>
|
||||
</div>
|
||||
<!--<svg-icon v-else-if="item.contentType.indexOf('application/pdf') > -1" icon-class="file-pdf"/>-->
|
||||
<!--<svg-icon v-else-if="item.contentType.indexOf('word') > -1" icon-class="file-word"/>-->
|
||||
<!--<svg-icon v-else-if="item.contentType.indexOf('zip') > -1" icon-class="zip"/>-->
|
||||
<!--<svg-icon v-else-if="item.contentType.indexOf('excel') > -1" icon-class="file-excel"/>-->
|
||||
<svg-icon v-else :icon-class="findIconClass"/>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import {iconClass} from '@/utils/file-type'
|
||||
@ -191,6 +187,9 @@ export default {
|
||||
line-height: 35px;
|
||||
}
|
||||
|
||||
>>> .image-slot-error {
|
||||
}
|
||||
|
||||
.icon-favorite {
|
||||
position: absolute;
|
||||
color: #f5222d;
|
||||
|
||||
@ -28,23 +28,32 @@
|
||||
<el-col :span="12" v-if="shareOptionConfig.shared" class="shared-expires-text">到期时间</el-col>
|
||||
<el-col :span="1" v-if="!shareOptionConfig.shared"> </el-col>
|
||||
<el-col v-if="shareOption.expiresDateOption === 2" :span="12" class="share-expires-data">
|
||||
<el-form-item prop="expiresDate">
|
||||
<el-date-picker
|
||||
ref="expiresDatePicker"
|
||||
size="small"
|
||||
:clearable="false"
|
||||
v-model="shareOption.expiresDate"
|
||||
type="date"
|
||||
:placeholder="!shareOption.expiresDate ? '永久有效' : '选择到期时间'"
|
||||
align="right"
|
||||
format="yyyy-MM-dd HH:mm"
|
||||
value-format="timestamp"
|
||||
@change="pickerChange"
|
||||
:picker-options="pickerOptions">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
<el-date-picker
|
||||
ref="expiresDatePicker"
|
||||
size="small"
|
||||
:clearable="false"
|
||||
v-model="shareOption.expiresDate"
|
||||
type="date"
|
||||
:placeholder="!shareOption.expiresDate ? '永久有效' : '选择到期时间'"
|
||||
align="right"
|
||||
format="yyyy-MM-dd HH:mm"
|
||||
value-format="timestamp"
|
||||
@change="pickerChange"
|
||||
:picker-options="pickerOptions">
|
||||
</el-date-picker>
|
||||
</el-col>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="shareOptionConfig.shared" label="">
|
||||
<el-col :span="12" class="shared-expires-text">分享形式</el-col>
|
||||
<el-col :span="12">
|
||||
<el-select size="small" v-model="shareOption.isPrivacy" style="width: 100%" @change="shareFormChange">
|
||||
<el-option label="公开链接" :value="false"></el-option>
|
||||
<el-option label="私密链接" :value="true"></el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="!shareOptionConfig.shared" class="share-option-form" label="分享形式" width="100">
|
||||
<el-col :span="20">
|
||||
<el-select size="small" v-model="shareOption.isPrivacy" style="width: 100%">
|
||||
@ -77,7 +86,7 @@
|
||||
<el-collapse-item title="操作权限" name="1">
|
||||
<el-checkbox-group v-model="shareOption.operationPermissionList" @change="permissionActionChange">
|
||||
<el-checkbox label="UPLOAD">上传</el-checkbox>
|
||||
<el-checkbox label="PUT">修改</el-checkbox>
|
||||
<el-checkbox label="PUT">编辑</el-checkbox>
|
||||
<el-checkbox label="DELETE">删除</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-collapse-item>
|
||||
@ -89,6 +98,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button size="small" v-if="shareOptionConfig.shared" type="danger" @click="cancelShare">取消分享</el-button>
|
||||
<el-button size="small" v-if="!shareOptionConfig.shared" type="primary" @click="submitShare"
|
||||
v-loading="generateShareLinkLoading">创建分享
|
||||
</el-button>
|
||||
@ -202,7 +212,8 @@ export default {
|
||||
shared: false,
|
||||
linkLabel: "选择有效期",
|
||||
},
|
||||
filename: '',// 文件名
|
||||
filename: '',
|
||||
shareId: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -221,23 +232,17 @@ export default {
|
||||
}).then(res => {
|
||||
this.filename = res.data.path + this.file.name
|
||||
})
|
||||
|
||||
this.shareOptionConfig.shared = true
|
||||
this.shareOptionConfig.linkLabel = '分享链接'
|
||||
this.setShareLink(this.getShareLink(this.file.shareId))
|
||||
this.shareOption.isPrivacy = this.file.isPrivacy
|
||||
this.shareOption.operationPermissionList = this.file.operationPermissionList || []
|
||||
if (this.file.expireDate) {
|
||||
this.shareOption.expiresDate = moment(this.file.expireDate).format('x')
|
||||
}
|
||||
this.shareOption.expiresDateOption = 2
|
||||
this.extractionCode = this.file.extractionCode
|
||||
this.showSharedPage(this.file)
|
||||
} else {
|
||||
this.shareOptionConfig.shared = false
|
||||
this.shareOptionConfig.linkLabel = '选择有效期'
|
||||
this.shareOption.isPrivacy = false
|
||||
this.shareOption.expiresDate = null
|
||||
this.shareOption.expiresDateOption = 1
|
||||
if (this.file.shareBase) {
|
||||
api.getShareByFileId({fileId: this.file.fileId}).then(res => {
|
||||
const shareObject = res.data
|
||||
shareObject.shareId = res.data.id
|
||||
this.showSharedPage(shareObject)
|
||||
})
|
||||
} else {
|
||||
this.showNotSharedPage()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -256,6 +261,26 @@ export default {
|
||||
console.error(error)
|
||||
})
|
||||
},
|
||||
showSharedPage(shareObject) {
|
||||
this.shareOptionConfig.shared = true
|
||||
this.shareOptionConfig.linkLabel = '分享链接'
|
||||
this.setShareLink(this.getShareLink(shareObject.shareId, shareObject.shortId))
|
||||
this.shareOption.isPrivacy = shareObject.isPrivacy
|
||||
this.shareOption.operationPermissionList = shareObject.operationPermissionList || []
|
||||
if (shareObject.expireDate) {
|
||||
this.shareOption.expiresDate = moment(shareObject.expireDate).format('x')
|
||||
}
|
||||
this.shareOption.expiresDateOption = 2
|
||||
this.extractionCode = shareObject.extractionCode
|
||||
this.shareId = shareObject.shareId
|
||||
},
|
||||
showNotSharedPage() {
|
||||
this.shareOptionConfig.shared = false
|
||||
this.shareOptionConfig.linkLabel = '选择有效期'
|
||||
this.shareOption.isPrivacy = false
|
||||
this.shareOption.expiresDate = null
|
||||
this.shareOption.expiresDateOption = 1
|
||||
},
|
||||
shareDialogClose() {
|
||||
this.shareOptionConfig.shared = false
|
||||
this.$emit('update:status', this.shareDialogVisible)
|
||||
@ -272,9 +297,34 @@ export default {
|
||||
this.shareOption.expiresDate = null
|
||||
}
|
||||
if (this.shareOptionConfig.shared) {
|
||||
this.createShare(true)
|
||||
this.createShare('pickerChange')
|
||||
}
|
||||
},
|
||||
// 切换分享形式
|
||||
shareFormChange() {
|
||||
if (!this.shareOption.isPrivacy) {
|
||||
this.shareOption.operationPermissionList = []
|
||||
}
|
||||
this.createShare('shareFormChange')
|
||||
},
|
||||
// 取消分享
|
||||
cancelShare() {
|
||||
let title = '确定要取消分享 "' + this.filename + '" 吗?'
|
||||
this.$confirm(title, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
api.cancelShareLink({
|
||||
userId: this.$store.state.user.userId,
|
||||
shareId: [this.shareId]
|
||||
}).then(() => {
|
||||
this.$emit('onCancelShare')
|
||||
this.shareDialogVisible = false
|
||||
this.shareDialogClose()
|
||||
})
|
||||
})
|
||||
},
|
||||
submitShare() {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (valid) {
|
||||
@ -289,8 +339,11 @@ export default {
|
||||
timeFormat(time) {
|
||||
return moment(time).format("yyyy-MM-DD HH:mm")
|
||||
},
|
||||
getShareLink(shareId) {
|
||||
return window.location.origin + '/s?s=' + shareId
|
||||
getShareLink(shareId, shortId) {
|
||||
if (!shortId) {
|
||||
shortId = shareId
|
||||
}
|
||||
return `${window.location.origin}/s/${shortId}`
|
||||
},
|
||||
createShare(update) {
|
||||
this.generateShareLinkLoading = true
|
||||
@ -303,6 +356,9 @@ export default {
|
||||
break
|
||||
case 2:
|
||||
if (this.shareOption.expiresDate) {
|
||||
if (typeof this.shareOption.expiresDate === 'string') {
|
||||
this.shareOption.expiresDate = Number(this.shareOption.expiresDate)
|
||||
}
|
||||
expireDate = this.timeFormat(this.shareOption.expiresDate)
|
||||
}
|
||||
}
|
||||
@ -315,8 +371,9 @@ export default {
|
||||
operationPermissionList: this.shareOption.operationPermissionList
|
||||
}).then(res => {
|
||||
if (res.data) {
|
||||
let {shareId, extractionCode, operationPermissionList} = res.data
|
||||
this.setShareLink(this.getShareLink(shareId))
|
||||
let {shareId, shortId, extractionCode, operationPermissionList} = res.data
|
||||
this.setShareLink(this.getShareLink(shareId, shortId))
|
||||
this.shareId = shareId
|
||||
this.generateShareLinkLoading = false
|
||||
this.shareOptionConfig.shared = true
|
||||
this.shareOption.expiresDateOption = 2
|
||||
@ -324,23 +381,38 @@ export default {
|
||||
this.extractionCode = extractionCode
|
||||
this.shareOption.operationPermissionList = operationPermissionList || []
|
||||
this.$emit('onSuccess', shareId)
|
||||
if (update) {
|
||||
if (!expireDate) {
|
||||
expireDate = '永久有效'
|
||||
}
|
||||
this.$emit("onUpdateExpireData", shareId, expireDate);
|
||||
this.$message({
|
||||
duration: 3000,
|
||||
dangerouslyUseHTMLString: true,
|
||||
iconClass: 'el-icon-success',
|
||||
message: '<strong> 有效期已设置为: <span>' + expireDate + '</span> </strong>'
|
||||
});
|
||||
if (update === 'pickerChange') {
|
||||
this.afterPickerChange(shareId, expireDate)
|
||||
}
|
||||
if (update === 'shareFormChange') {
|
||||
this.afterShareFormChange(shareId)
|
||||
}
|
||||
}
|
||||
}).catch(() => {
|
||||
this.generateShareLinkLoading = false
|
||||
})
|
||||
},
|
||||
afterPickerChange (shareId, expireDate) {
|
||||
if (!expireDate) {
|
||||
expireDate = '永久有效'
|
||||
}
|
||||
this.$emit("onUpdateExpireData", shareId, expireDate);
|
||||
this.$message({
|
||||
duration: 3000,
|
||||
dangerouslyUseHTMLString: true,
|
||||
iconClass: 'el-icon-success',
|
||||
message: '<strong> 有效期已设置为: <span>' + expireDate + '</span> </strong>'
|
||||
});
|
||||
},
|
||||
afterShareFormChange(shareId) {
|
||||
this.$emit("onUpdateShareForm", shareId, this.shareOption.isPrivacy);
|
||||
this.$message({
|
||||
duration: 3000,
|
||||
dangerouslyUseHTMLString: true,
|
||||
iconClass: 'el-icon-success',
|
||||
message: '<strong> 链接已设置为: <span>' + (this.shareOption.isPrivacy ? '私密链接' : '公开链接') + '</span> </strong>'
|
||||
});
|
||||
},
|
||||
// 复制分享链接
|
||||
copyShareLink(msg) {
|
||||
let clipboard = new Clipboard('.tag-share-link')
|
||||
@ -370,7 +442,7 @@ export default {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
> > > .el-dialog {
|
||||
>>> .el-dialog {
|
||||
max-width: 460px;
|
||||
|
||||
.el-dialog__title {
|
||||
@ -395,7 +467,7 @@ export default {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.share-icon-font > > > .svg-icon {
|
||||
.share-icon-font >>> .svg-icon {
|
||||
font-size: 8rem;
|
||||
}
|
||||
}
|
||||
@ -403,26 +475,27 @@ export default {
|
||||
.share-filename {
|
||||
margin: 25px 0 50px 0;
|
||||
text-align: center;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.share-option {
|
||||
|
||||
> > > .el-form-item--mini.el-form-item {
|
||||
>>> .el-form-item--mini.el-form-item {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.share-expires-data {
|
||||
> > > .el-input__inner {
|
||||
>>> .el-input__inner {
|
||||
cursor: pointer;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
> > > .el-date-editor.el-input {
|
||||
>>> .el-date-editor.el-input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
> > > .el-form-item__label {
|
||||
>>> .el-form-item__label {
|
||||
text-align: left;
|
||||
line-height: 32px;
|
||||
}
|
||||
@ -434,21 +507,20 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
> > > .dialog-footer {
|
||||
>>> .dialog-footer {
|
||||
.el-loading-spinner .circular {
|
||||
width: 25px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.share-link {
|
||||
margin-top: -10px;
|
||||
margin-bottom: 25px;
|
||||
|
||||
.share-link-code {
|
||||
width: 130px;
|
||||
margin-top: 10px;
|
||||
|
||||
> > > .el-input-group__prepend {
|
||||
>>> .el-input-group__prepend {
|
||||
padding: 0 10px;
|
||||
font-weight: 500;
|
||||
}
|
||||
@ -462,7 +534,7 @@ export default {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
> > > .el-input__inner {
|
||||
>>> .el-input__inner {
|
||||
background-color: #84858d14;
|
||||
text-align: center;
|
||||
padding: 0 10px;
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
>
|
||||
<uploader-unsupport></uploader-unsupport>
|
||||
|
||||
<uploader-drop v-if="dragover && enableDragUplaod" class="uploader-drop">
|
||||
<uploader-drop v-if="dragover && enableDragUpload" class="uploader-drop">
|
||||
<span>上传文件到当前目录下</span>
|
||||
</uploader-drop>
|
||||
|
||||
@ -112,7 +112,6 @@
|
||||
|
||||
<script>
|
||||
|
||||
// import { ACCEPT_CONFIG } from '@/assets/js/config'
|
||||
import store from '@/store'
|
||||
import $ from 'jquery'
|
||||
import SparkMD5 from 'spark-md5'
|
||||
@ -129,11 +128,11 @@ export default {
|
||||
username: this.$store.state.user.name,
|
||||
options: {
|
||||
target: api.simpleUploadURL,
|
||||
chunkSize: 5 * 1024 * 1024,
|
||||
chunkSize: localStorage.getItem('uploader_chunk_size') || 1024 * 1024,
|
||||
// speedSmoothingFactor: 0.1,
|
||||
// progressCallbacksInterval: 500,
|
||||
maxChunkRetries: 3, // 最大重试次数
|
||||
simultaneousUploads: 5, // 并发上传数
|
||||
simultaneousUploads: 3, // 并发上传数
|
||||
testChunks: true, // 是否开启服务器分片校验
|
||||
// 服务器分片校验函数,秒传及断点续传基础
|
||||
checkChunkUploadedByResponse: function (chunk, message) {
|
||||
@ -190,7 +189,7 @@ export default {
|
||||
fileListScrollTop: 0,
|
||||
dragoverLoop: null,
|
||||
successMsg: null,
|
||||
enableDragUplaod: false,// 是否启用拖拽上传
|
||||
enableDragUpload: false,// 是否启用拖拽上传
|
||||
uploader: null,
|
||||
}
|
||||
},
|
||||
@ -200,12 +199,12 @@ export default {
|
||||
watch: {
|
||||
$route(route) {
|
||||
// 只有首页才启用拖拽上传
|
||||
this.enableDragUplaod = route.path === '/'
|
||||
this.enableDragUpload = route.path === '/'
|
||||
},
|
||||
message(msg) {
|
||||
switch (msg.event) {
|
||||
case 'storageTypeChange':
|
||||
this.onStorageTypeChange(msg.data)
|
||||
case 'uploaderChunkSize':
|
||||
this.onStorageTypeChange(msg.data.body)
|
||||
break
|
||||
case 'fileListScrollTop':
|
||||
this.fileListScrollTop = msg.data
|
||||
@ -234,12 +233,12 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.enableDragUplaod = this.$route.path === '/'
|
||||
this.enableDragUpload = this.$route.path === '/'
|
||||
let that = this
|
||||
let dropbox = document.body
|
||||
|
||||
document.body.ondragstart = function (e) {
|
||||
if (that.enableDragUplaod) {
|
||||
if (that.enableDragUpload) {
|
||||
if (e.target.slot === 'jmal') {
|
||||
that.isDragStart = true
|
||||
}
|
||||
@ -281,17 +280,10 @@ export default {
|
||||
destroyed() {
|
||||
},
|
||||
methods: {
|
||||
onStorageTypeChange(storageType) {
|
||||
if (storageType === 'File') {
|
||||
if (this.options.chunkSize === 1024 * 1024) {
|
||||
return
|
||||
}
|
||||
this.updateChunkSize(1024 * 1024)
|
||||
} else {
|
||||
if (this.options.chunkSize === 5 * 1024 * 1024) {
|
||||
return
|
||||
}
|
||||
this.updateChunkSize(5 * 1024 * 1024)
|
||||
onStorageTypeChange(chunkSize) {
|
||||
localStorage.setItem('uploader_chunk_size', chunkSize)
|
||||
if (this.options.chunkSize !== chunkSize && !this.panelShow) {
|
||||
this.updateChunkSize(chunkSize)
|
||||
}
|
||||
},
|
||||
initUploader() {
|
||||
@ -351,12 +343,23 @@ export default {
|
||||
}).then(() => {
|
||||
this.doUploadBefore(files)
|
||||
}).catch(() => {
|
||||
this.uploader.cancel()
|
||||
this.uploaderCancel()
|
||||
})
|
||||
} else {
|
||||
this.doUploadBefore(files)
|
||||
}
|
||||
},
|
||||
uploaderCancel() {
|
||||
this.uploader.cancel()
|
||||
this.displayPanel(false)
|
||||
const chunkSize = localStorage.getItem('uploader_chunk_size');
|
||||
if (chunkSize !== this.uploader.opts.chunkSize) {
|
||||
this.updateChunkSize(Number.parseInt(chunkSize))
|
||||
}
|
||||
},
|
||||
displayPanel(display) {
|
||||
this.panelShow = display
|
||||
},
|
||||
doUploadBefore(files) {
|
||||
this.fileListLength = this.uploader.fileList.length
|
||||
const filePaths = this.uploader.filePaths
|
||||
@ -374,15 +377,12 @@ export default {
|
||||
currentDirectory: encodeIfNeeded(this.params.currentDirectory),
|
||||
username: this.params.username,
|
||||
userId: this.params.userId
|
||||
}).then(() => {
|
||||
this.getFileList()
|
||||
}).catch(e => {
|
||||
})
|
||||
})
|
||||
}
|
||||
if (window.pc) {
|
||||
this.pc = true
|
||||
this.panelShow = true
|
||||
this.displayPanel(true)
|
||||
} else {
|
||||
this.pc = false
|
||||
this.shrink()
|
||||
@ -400,14 +400,20 @@ export default {
|
||||
this.uploader.resume()
|
||||
})
|
||||
},
|
||||
onFileProgress(rootFile, file, chunk) {
|
||||
this.netSpeed = formatNetSpeed(file.currentSpeed)
|
||||
this.process = Math.trunc(window.uploader.progress() * 100)
|
||||
setPageTitle() {
|
||||
if (this.process === -10 || this.process === 100 || this.fileListLength === 0) {
|
||||
document.title = `${this.$route.meta.title}`
|
||||
} else {
|
||||
document.title = `${this.process}% | ${this.$route.meta.title}`
|
||||
}
|
||||
},
|
||||
onFileProgress(rootFile, file, chunk) {
|
||||
this.netSpeed = formatNetSpeed(file.currentSpeed, false)
|
||||
this.process = Math.trunc(window.uploader.progress() * 100)
|
||||
this.setPageTitle()
|
||||
if (rootFile.isFolder && this.process < 100) {
|
||||
this.statusSet(rootFile.id, 'progress', formatNetSpeed(file.currentSpeed, true))
|
||||
}
|
||||
if (this.process > 0 && this.process < 100 && window.uploader.fileList.length > 0) {
|
||||
window.onbeforeunload = function () {
|
||||
return "还有文件正在上传, 确定退出吗?";
|
||||
@ -425,7 +431,6 @@ export default {
|
||||
message: res.message,
|
||||
type: 'error'
|
||||
})
|
||||
this.uploader.cancel()
|
||||
this.close()
|
||||
return;
|
||||
}
|
||||
@ -489,8 +494,7 @@ export default {
|
||||
|
||||
}
|
||||
if (this.process === -10 || this.process === 100 || this.fileListLength === 0) {
|
||||
this.uploader.cancel()
|
||||
this.panelShow = false
|
||||
this.uploaderCancel()
|
||||
}
|
||||
},
|
||||
onFileError(rootFile, file, response) {
|
||||
@ -640,16 +644,14 @@ export default {
|
||||
},
|
||||
close() {
|
||||
if (this.process === -10 || this.process === 100 || this.fileListLength === 0) {
|
||||
this.uploader.cancel()
|
||||
this.panelShow = false
|
||||
this.uploaderCancel()
|
||||
} else {
|
||||
this.$confirm('还有文件正在上传, 确定要关闭吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.uploader.cancel()
|
||||
this.panelShow = false
|
||||
this.uploaderCancel()
|
||||
})
|
||||
}
|
||||
},
|
||||
@ -659,8 +661,12 @@ export default {
|
||||
* @param id
|
||||
* @param status
|
||||
*/
|
||||
statusSet(id, status) {
|
||||
statusSet(id, status, progressText) {
|
||||
const statusMap = {
|
||||
progress: {
|
||||
text: progressText,
|
||||
bgc: '#ffffff00'
|
||||
},
|
||||
md5: {
|
||||
text: '校验MD5',
|
||||
bgc: '#fff'
|
||||
@ -683,6 +689,11 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
if (status === 'progress') {
|
||||
$('.uploader-file-status').find('span:eq(1) em').text(progressText)
|
||||
return
|
||||
}
|
||||
|
||||
this.$nextTick(() => {
|
||||
$(`<p class="myStatus_${id}"></p>`).appendTo(`.file_${id} .uploader-file-status`).css({
|
||||
'position': 'absolute',
|
||||
@ -812,7 +823,7 @@ export default {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
> li {
|
||||
>>> li {
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
@ -832,7 +843,7 @@ export default {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
> .uploader-file-icon {
|
||||
>>> .uploader-file-icon {
|
||||
&:before {
|
||||
content: "" !important;
|
||||
}
|
||||
@ -860,21 +871,21 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
> .uploader-file-actions > span {
|
||||
>>> .uploader-file-actions > span {
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
> .uploader-file-status {
|
||||
>>> .uploader-file-status {
|
||||
width: 32%;
|
||||
text-indent: 20px;
|
||||
}
|
||||
|
||||
> .uploader-file-meta {
|
||||
>>> .uploader-file-meta {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
> .uploader-file-icon {
|
||||
>>> .uploader-file-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: inline-block;
|
||||
@ -883,7 +894,7 @@ export default {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
> .uploader-file-actions > span {
|
||||
>>> .uploader-file-actions > span {
|
||||
margin-right: 6px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
75
src/components/TaskProgress/index.vue
Normal file
75
src/components/TaskProgress/index.vue
Normal file
@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<table-list
|
||||
ref="tableList"
|
||||
:less-client-height="245"
|
||||
:tableData="dataList"
|
||||
:loading="loading"
|
||||
:hasSelection="false"
|
||||
:border="false"
|
||||
:tableHeader="tableHeader"
|
||||
></table-list>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TableList from "@/components/table/TableList"
|
||||
import {mapState} from "vuex";
|
||||
|
||||
export default {
|
||||
name: 'TaskProgress',
|
||||
components: {
|
||||
TableList
|
||||
},
|
||||
props: {
|
||||
data: {
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState(['message'])
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dataList: [],
|
||||
loading: false,
|
||||
tableHeader: [
|
||||
{prop: 'type', width: 100, label: '任务类型'},
|
||||
{prop: 'name', minWidth: 100, label: '名称'},
|
||||
{prop: 'progress', width: 200, label: '进度'},
|
||||
]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
data(val) {
|
||||
this.dataList = val
|
||||
},
|
||||
message(msg) {
|
||||
if (msg.event === 'msg/taskProgress') {
|
||||
const taskProgress = msg.data.body
|
||||
const index = this.dataList.findIndex(item => item.taskId === taskProgress.taskId)
|
||||
if (index > -1) {
|
||||
if (taskProgress.progress) {
|
||||
this.dataList[index].progress = taskProgress.progress
|
||||
} else {
|
||||
this.dataList.splice(index, 1)
|
||||
}
|
||||
} else {
|
||||
if (taskProgress.progress) {
|
||||
this.dataList.push(taskProgress)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
padding: 0 !important;
|
||||
}
|
||||
</style>
|
||||
@ -84,7 +84,7 @@ export default {
|
||||
let chrome = this.readOnly ? 0 : 1
|
||||
let theme = 'kennedy'
|
||||
let title = this.file.name ? encodeURIComponent(this.file.name) : ''
|
||||
let query = `?title=${title}&chrome=${chrome}&lightbox=${lightbox}&ui=${theme}&lang=${language}&offline=1&pwa=0&embed=1&noLangIcon=1&noExitBtn=1&noSaveBtn=1&saveAndExit=0&spin=1&proto=json`
|
||||
let query = `?title=${title}&chrome=${chrome}&lightbox=${lightbox}&ui=${theme}&lang=${language}&offline=0&pwa=0&embed=1&noLangIcon=1&noExitBtn=1&noSaveBtn=1&saveAndExit=0&spin=1&proto=json`
|
||||
this.url = $J.apiUrl(`../drawio/webapp/${query}`)
|
||||
},
|
||||
mounted() {
|
||||
@ -322,7 +322,7 @@ export default {
|
||||
top: 2.5rem;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
height: calc(100% - 2.5rem);
|
||||
|
||||
>>>.dark-button {
|
||||
background: #3e3e3e;
|
||||
|
||||
76
src/components/office/ModelPreview.vue
Normal file
76
src/components/office/ModelPreview.vue
Normal file
@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="show">
|
||||
<model-viewer ref="modelViewer" class="model-viewer" alt="" :src="fileUrl" autoplay animation-name="Running" shadow-intensity="1" camera-controls touch-action="pan-y"></model-viewer>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { loadScript } from '@/utils/load-script'
|
||||
|
||||
export default {
|
||||
name: "ModelPreview",
|
||||
components: {},
|
||||
props: {
|
||||
file: {
|
||||
type: Object,
|
||||
default: function () {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
fileUrl: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
shareId: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
readOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
created() {
|
||||
},
|
||||
mounted() {
|
||||
const modelViewerJsUrl = window.location.origin + '/resource/model-viewer.min.js';
|
||||
loadScript(modelViewerJsUrl, 'module').then(() => {
|
||||
this.onRead();
|
||||
})
|
||||
},
|
||||
beforeDestroy() {
|
||||
},
|
||||
destroyed() {
|
||||
},
|
||||
watch: {
|
||||
},
|
||||
methods: {
|
||||
onRead() {
|
||||
this.$emit('onReady')
|
||||
this.show = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.modelViewer.style.width = this.width + "px"
|
||||
this.$refs.modelViewer.style.height = this.height + "px"
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.model-viewer {
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
||||
@ -22,6 +22,7 @@
|
||||
<script>
|
||||
|
||||
import api from "@/api/file-api"
|
||||
import settingApi from "@/api/setting-api"
|
||||
import fileConfig from "@/utils/file-config";
|
||||
import SparkMD5 from 'spark-md5'
|
||||
import HistoryPopover from "@/components/HistoryPopover/index.vue";
|
||||
@ -49,6 +50,10 @@ export default {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
sharer: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
file: {
|
||||
type: Object,
|
||||
default: function () {
|
||||
@ -75,7 +80,12 @@ export default {
|
||||
historyPageIndex: 1,
|
||||
historyPageSize: 50,
|
||||
viewHistory: false,
|
||||
historyOperationLoading: false
|
||||
historyOperationLoading: false,
|
||||
officeServerConfig: {
|
||||
documentServer: undefined,
|
||||
callbackServer: undefined,
|
||||
tokenEnabled: false
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
@ -93,25 +103,18 @@ export default {
|
||||
if (!id) {
|
||||
return
|
||||
}
|
||||
let officeApiUrl = fileConfig.officeApiUrl()
|
||||
$J.loadScript($J.apiUrl(officeApiUrl), (e) => {
|
||||
if (e !== null) {
|
||||
this.$emit('onClose')
|
||||
this.$store.dispatch('updateMessage', { event: 'loadFileFailed'})
|
||||
return
|
||||
}
|
||||
if (this.shareId) {
|
||||
api.getPublicFileInfoById({fileId: this.file.id, shareId: this.shareId}).then(res => {
|
||||
this.file = res.data
|
||||
this.loadFile()
|
||||
})
|
||||
} else {
|
||||
api.getFileInfoById({id: this.file.id}).then(res => {
|
||||
this.file = res.data
|
||||
this.loadFile()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
if (window.shareId) {
|
||||
settingApi.getPublicOfficeConfig({shareId: window.shareId, shareToken: this.$store.getters.shareToken}).then(res => {
|
||||
this.officeServerConfig = res.data
|
||||
this.loadOfficeApi()
|
||||
})
|
||||
} else {
|
||||
settingApi.getOfficeConfig().then(res => {
|
||||
this.officeServerConfig = res.data
|
||||
this.loadOfficeApi()
|
||||
})
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
@ -134,6 +137,27 @@ export default {
|
||||
destroyed() {
|
||||
},
|
||||
methods: {
|
||||
loadOfficeApi() {
|
||||
let officeApiUrl = fileConfig.officeApiUrl(this.officeServerConfig.documentServer)
|
||||
$J.loadScript($J.apiUrl(officeApiUrl), (e) => {
|
||||
if (e !== null) {
|
||||
this.$emit('onClose')
|
||||
this.$store.dispatch('updateMessage', { event: 'loadFileFailed'})
|
||||
return
|
||||
}
|
||||
if (this.shareId) {
|
||||
api.getPublicFileInfoById({fileId: this.file.id, shareId: this.shareId}).then(res => {
|
||||
this.file = res.data
|
||||
this.loadFile()
|
||||
})
|
||||
} else {
|
||||
api.getFileInfoById({id: this.file.id}).then(res => {
|
||||
this.file = res.data
|
||||
this.loadFile()
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
viewHistoryFile({historyInfo}) {
|
||||
const historyUrl = window.location.origin + fileConfig.previewHistoryUrl(historyInfo.id, this.$store.state.user.name, this.$store.state.user.token)
|
||||
this.onRequestHistoryData(null, historyInfo, historyUrl)
|
||||
@ -188,11 +212,15 @@ export default {
|
||||
this.docEditor = new DocsAPI.DocEditor(this.id, this.docEditorConfig)
|
||||
},
|
||||
onRequestHistoryData(event, historyInfo, url) {
|
||||
this.docEditor.setHistoryData({
|
||||
let historyConfig = {
|
||||
"fileType": this.fileType,
|
||||
"key": `${new Date(historyInfo.metadata.time).getTime()}-${SparkMD5.hash(this.file.id)}`,
|
||||
"url": url,
|
||||
"version": historyInfo.metadata.time,
|
||||
}
|
||||
api.getOfficeJwt(historyConfig).then(res => {
|
||||
historyConfig.token = res.data
|
||||
this.docEditor.setHistoryData(historyConfig)
|
||||
})
|
||||
},
|
||||
onRequestHistory() {
|
||||
@ -221,14 +249,25 @@ export default {
|
||||
this.$refs.historyPopover.loadHistoryList(this.file.id)
|
||||
})
|
||||
this.destroyEditor()
|
||||
this.fileUrl = window.location.origin + fileConfig.previewUrl(this.$store.state.user.name, this.file, this.$store.getters.token)
|
||||
if (this.readOnly && window.shareId) {
|
||||
this.fileUrl = window.location.origin + fileConfig.publicPreviewUrl(this.file, window.shareId, this.$store.getters.shareToken)
|
||||
|
||||
const file_username = this.sharer ? this.sharer : this.$store.state.user.name
|
||||
|
||||
let callbackServer = this.officeServerConfig.callbackServer
|
||||
if (callbackServer) {
|
||||
if (callbackServer.endsWith('/')) {
|
||||
// 去掉最后的/
|
||||
callbackServer = callbackServer.substring(0, this.officeServerConfig.callbackServer.length - 1)
|
||||
}
|
||||
} else {
|
||||
callbackServer = 'http://jmalcloud:8088'
|
||||
}
|
||||
this.fileKey = `${new Date(this.file.updateDate).getTime()}-${SparkMD5.hash(this.file.id)}`
|
||||
|
||||
let callbackUrl = fileConfig.officeCallBackUrl(this.$store.getters.token, this.$store.getters.name, this.file.id)
|
||||
|
||||
this.fileUrl = fileConfig.previewUrl(file_username, this.file, this.$store.getters.token, undefined, callbackServer)
|
||||
this.fileKey = `${new Date(this.file.updateDate).getTime()}-${SparkMD5.hash(this.file.id + callbackServer)}`
|
||||
if (this.readOnly && window.shareId) {
|
||||
this.fileUrl = fileConfig.publicPreviewUrl(this.file, window.shareId, this.$store.getters.shareToken, callbackServer)
|
||||
this.fileKey = `${new Date(this.file.updateDate).getTime()}-${SparkMD5.hash(window.shareId + callbackServer)}`
|
||||
}
|
||||
let callbackUrl = fileConfig.officeCallBackUrl(this.officeServerConfig.callbackServer, this.$store.getters.token, this.$store.getters.name, this.file.id)
|
||||
this.docEditorConfig = {
|
||||
document: {
|
||||
fileType: this.fileType,
|
||||
@ -241,7 +280,8 @@ export default {
|
||||
lang: "zh",
|
||||
user: {
|
||||
id: this.$store.state.user.userId,
|
||||
name: this.$store.state.user.name
|
||||
name: this.$store.state.user.name,
|
||||
image: `${window.location.origin}${process.env.VUE_APP_BASE_API}/public/s/view/thumbnail?id=${this.$store.state.user.avatar}`
|
||||
},
|
||||
customization: {
|
||||
autosave: false,
|
||||
@ -260,6 +300,28 @@ export default {
|
||||
callbackUrl: callbackUrl,
|
||||
}
|
||||
}
|
||||
|
||||
if (this.readOnly && window.shareId) {
|
||||
const shareToken = this.$store.getters.shareToken ? this.$store.getters.shareToken : 'token'
|
||||
this.docEditorConfig.editorConfig.callbackUrl = null
|
||||
api.getPublicOfficeJwt({shareId: window.shareId, shareToken: shareToken}, this.docEditorConfig).then(res => {
|
||||
this.loadOffice(res.data)
|
||||
})
|
||||
} else {
|
||||
if (this.officeServerConfig.tokenEnabled) {
|
||||
api.getOfficeJwt(this.docEditorConfig).then(res => {
|
||||
this.loadOffice(res.data)
|
||||
})
|
||||
} else {
|
||||
this.loadOffice()
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
loadOffice(token) {
|
||||
if (token) {
|
||||
this.docEditorConfig.token = token
|
||||
}
|
||||
if (!this.$pc) {
|
||||
this.docEditorConfig.type = 'mobile'
|
||||
}
|
||||
@ -276,6 +338,7 @@ export default {
|
||||
this.docEditorConfig.editorConfig.user.name = "Visitor_" + visitor
|
||||
}
|
||||
}
|
||||
|
||||
this.docEditorConfig.events = {
|
||||
"onAppReady": this.onAppReady,
|
||||
"onDocumentReady": this.onDocumentReady,
|
||||
@ -316,6 +379,7 @@ export default {
|
||||
if (this.hasHistoryVersion && !doc.getElementById('box-doc-name-history-btn')) {
|
||||
const toolbar = doc.getElementById('box-doc-name')
|
||||
toolbar.style.justifyContent = 'right'
|
||||
toolbar.style.padding = '0'
|
||||
let versionButton = document.createElement("button")
|
||||
versionButton.classList.add('btn', 'btn-header')
|
||||
versionButton.setAttribute('id', 'box-doc-name-history-btn')
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<div ref="artRef"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Artplayer from 'artplayer';
|
||||
|
||||
import { loadScript } from '@/utils/load-script'
|
||||
|
||||
export default {
|
||||
name: 'Artplayer',
|
||||
@ -19,19 +19,26 @@ export default {
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.instance = new Artplayer({
|
||||
...this.option,
|
||||
container: this.$refs.artRef,
|
||||
});
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.$emit('get-instance', this.instance);
|
||||
});
|
||||
const artPlayerJsUrl = window.location.origin + '/resource/artplayer@5.1.6.js';
|
||||
loadScript(artPlayerJsUrl, undefined).then(() => {
|
||||
this.onRead();
|
||||
})
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.instance && this.instance.destroy) {
|
||||
this.instance.destroy(false);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onRead() {
|
||||
this.instance = new Artplayer({
|
||||
...this.option,
|
||||
container: this.$refs.artRef,
|
||||
})
|
||||
this.$nextTick(() => {
|
||||
this.$emit('get-instance', this.instance);
|
||||
})
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
273
src/components/preview/FileDetails.vue
Normal file
273
src/components/preview/FileDetails.vue
Normal file
@ -0,0 +1,273 @@
|
||||
<template>
|
||||
<div>
|
||||
<!--文件详细信息-->
|
||||
<!-- <el-drawer :title="rowContextData.name" :visible.sync="visible">-->
|
||||
<!-- -->
|
||||
<!-- </el-drawer>-->
|
||||
<el-dialog :visible.sync="visible" width="420px">
|
||||
<div class="drawer-icon">
|
||||
<icon-file
|
||||
class="drawer-icon-font"
|
||||
:grid="true"
|
||||
:details="true"
|
||||
:item="rowContextData"
|
||||
:image-url="imageUrl"
|
||||
:audio-cover-url="audioCoverUrl"
|
||||
></icon-file>
|
||||
</div>
|
||||
<el-form class="details-form">
|
||||
<el-form-item label="重建索引:" class="details-scan">
|
||||
<el-popover
|
||||
placement="bottom"
|
||||
trigger="click">
|
||||
<p class="el-popconfirm__main">
|
||||
<i class="el-popconfirm__icon el-icon-question" style="color: rgb(255, 153, 0);"></i>
|
||||
是否开始扫描?
|
||||
</p>
|
||||
<div style="text-align: right; margin: 0">
|
||||
<el-button size="mini" type="text" @click="cancelScan()">取消</el-button>
|
||||
<el-button type="primary" size="mini" @click="scanDirectory(rowContextData)">确定</el-button>
|
||||
</div>
|
||||
<el-button slot="reference" id="scanDirectoryBtn-file" type="primary" size="small" :loading="syncLoading"><i class="el-icon-refresh"></i></el-button>
|
||||
</el-popover>
|
||||
<span v-show="syncPercent < 100">正在同步文件基本信息: {{ syncPercent }}%</span>
|
||||
<span v-show="indexingPercent > 0 && indexingPercent < 100">正在为文件内容创建索引: </span><span v-show="indexingPercent > 0 && indexingPercent < 100">{{ indexingPercent }}%</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-scrollbar wrap-class="scrollbar-wrapper" class="details-form-list">
|
||||
<el-form-item label="名称:">
|
||||
<span>{{ rowContextData.name }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="类型:">
|
||||
<span>{{
|
||||
rowContextData.isFolder ? '文件夹' : rowContextData.contentType
|
||||
}}</span>
|
||||
</el-form-item>
|
||||
<div v-if="rowContextData.music">
|
||||
<el-form-item label="🎵 歌手:">
|
||||
<span>{{ rowContextData.music.singer }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="🎵 专辑:">
|
||||
<span>{{ '《' + rowContextData.music.album + '》' }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="🎵 歌名:">
|
||||
<span>{{ '《' + rowContextData.music.songName + '》' }}</span>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-form-item
|
||||
v-show="rowContextData.w && rowContextData.h"
|
||||
label="尺寸:"
|
||||
class="details-resolution"
|
||||
>
|
||||
<span>{{ rowContextData.w + ' x ' + rowContextData.h }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="大小:">
|
||||
<span> {{ formatSize(rowContextData.size) }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="位置:" class="details-position">
|
||||
<a :href="'/?path=' + rowContextData.path + '&highlight=' + rowContextData.name">{{ rowContextData.path }}</a>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间:">
|
||||
<span>{{ rowContextData.uploadDate }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="修改时间:">
|
||||
<span>{{ rowContextData.updateDate }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="rowContextData.exif" label="">
|
||||
<span style="white-space: break-spaces;">{{ formatExif(rowContextData.exif) }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="rowContextData.video" label="">
|
||||
<span style="white-space: break-spaces;">{{ formatVideo(rowContextData.video) }}</span>
|
||||
</el-form-item>
|
||||
</el-scrollbar>
|
||||
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { formatSize } from '@/utils/number'
|
||||
import IconFile from '@/components/Icon/IconFile'
|
||||
import {mapState} from "vuex";
|
||||
import {formatExif,formatVideo} from "@/utils/media";
|
||||
import settingApi from "@/api/setting-api";
|
||||
|
||||
export default {
|
||||
name: 'FileDetails',
|
||||
components: {
|
||||
IconFile
|
||||
},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
rowContextData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
imageUrl: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
audioCoverUrl: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
syncLoading: false,
|
||||
clickSync: false,
|
||||
indexingPercent: 100,
|
||||
syncPercent: 100,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
computed: {
|
||||
...mapState(['message']),
|
||||
},
|
||||
watch: {
|
||||
message(msg) {
|
||||
if (msg.event === 'msg/synced') {
|
||||
this.updateSyncStatus(msg.data.body)
|
||||
}
|
||||
},
|
||||
visible(val) {
|
||||
this.$emit('update:visible', val)
|
||||
if (val) {
|
||||
this.getIsSync()
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formatSize(size) {
|
||||
return formatSize(size)
|
||||
},
|
||||
formatExif(exifInfo) {
|
||||
return formatExif(exifInfo)
|
||||
},
|
||||
formatVideo(videoInfo) {
|
||||
return formatVideo(videoInfo)
|
||||
},
|
||||
getIsSync() {
|
||||
settingApi.isSync({username: this.$store.state.user.name}).then((res) => {
|
||||
this.updateSyncStatus(res.data)
|
||||
})
|
||||
},
|
||||
updateSyncStatus(dataPercent) {
|
||||
if (this.clickSync) {
|
||||
return
|
||||
}
|
||||
const {syncPercent, indexingPercent} = dataPercent
|
||||
if (syncPercent) {
|
||||
this.syncPercent = syncPercent
|
||||
} else {
|
||||
this.syncPercent = 0
|
||||
}
|
||||
if (indexingPercent) {
|
||||
this.indexingPercent = indexingPercent
|
||||
} else {
|
||||
this.indexingPercent = 0
|
||||
}
|
||||
this.syncLoading = !((this.syncPercent === 100 && this.indexingPercent === 100) || (this.syncPercent === 0 && this.indexingPercent === 0));
|
||||
},
|
||||
cancelScan() {
|
||||
document.getElementById('scanDirectoryBtn-file').click()
|
||||
},
|
||||
scanDirectory(file) {
|
||||
this.cancelScan()
|
||||
this.syncLoading = true
|
||||
this.syncPercent = 0
|
||||
this.clickSync = true
|
||||
settingApi.syncUser({username: this.$store.state.user.name, path: decodeURIComponent(file.path + file.name)}).then(() => {
|
||||
this.clickSync = false
|
||||
}).catch(() => {
|
||||
this.clickSync = false
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
>>> .el-drawer__header {
|
||||
color: #000000;
|
||||
|
||||
span {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.details-form {
|
||||
padding: 20px 0;
|
||||
|
||||
>>> .el-scrollbar__view {
|
||||
max-height: 50vh;
|
||||
}
|
||||
|
||||
>>> .el-form-item__content {
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
word-wrap: break-word;
|
||||
line-height: 25px;
|
||||
}
|
||||
|
||||
>>> .el-form-item__label {
|
||||
line-height: 25px;
|
||||
}
|
||||
|
||||
>>> .el-form-item {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
>>> .details-scan {
|
||||
.el-form-item__label {
|
||||
line-height: 32.5px;
|
||||
}
|
||||
margin-bottom: 10px;
|
||||
.el-button--mini, .el-button--mini.is-round {
|
||||
padding: 5px 15px;
|
||||
}
|
||||
}
|
||||
|
||||
>>> .details-position {
|
||||
margin: 10px 0;
|
||||
|
||||
.el-form-item__content {
|
||||
line-height: 20px;
|
||||
color: #84a0c3;
|
||||
}
|
||||
|
||||
.el-form-item__label {
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #409eff;
|
||||
}
|
||||
}
|
||||
|
||||
.drawer-icon {
|
||||
text-align: center;
|
||||
position: relative;
|
||||
>>> .icon-favorite {
|
||||
display: none;
|
||||
}
|
||||
>>> .icon-share {
|
||||
display: none;
|
||||
}
|
||||
>>> .icon-tag {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.drawer-icon-font >>> .svg-icon {
|
||||
font-size: 8rem;
|
||||
}
|
||||
</style>
|
||||
162
src/components/preview/IframeContentPreview.vue
Normal file
162
src/components/preview/IframeContentPreview.vue
Normal file
@ -0,0 +1,162 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="top-bar">
|
||||
<div class="top-left"></div>
|
||||
<div class="top-center">{{file.name}}</div>
|
||||
<div class="top-right">
|
||||
<div class="top-right-option">
|
||||
<el-select v-if="services.length > 1" size="medium" v-model="url" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in services"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-button size="medium" type="primary" @click="openUrl"><svg-icon icon-class="wailian"/></el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="iframe-content">
|
||||
<iframe ref="iframeContent" name="Iframe Content Preview" :title="file.name" :src="url" sandbox="allow-scripts allow-forms allow-same-origin allow-popups"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: "IframeContentPreview",
|
||||
props: {
|
||||
fileUrl: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
file: {
|
||||
type: Object,
|
||||
default: function () {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
fileHandler: {
|
||||
type: Object,
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
url: undefined,
|
||||
services: []
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.iframeLoad()
|
||||
},
|
||||
methods: {
|
||||
iframeLoad() {
|
||||
if (this.fileHandler) {
|
||||
for (let service in this.fileHandler) {
|
||||
const urlTemplate = this.fileHandler[service]
|
||||
if (!urlTemplate) {
|
||||
continue
|
||||
}
|
||||
const url = decodeURI(this.fileUrl)
|
||||
if (urlTemplate.indexOf('$e_url') > 0) {
|
||||
this.url = urlTemplate.replace('$e_url', encodeURIComponent(url))
|
||||
this.services.push({label: service, value: this.url})
|
||||
}
|
||||
if (urlTemplate.indexOf('$eb_url') > 0) {
|
||||
this.url = urlTemplate.replace('$eb_url', encodeURIComponent(this.base64EncodeUnicode(url)))
|
||||
this.services.push({label: service, value: this.url})
|
||||
}
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.onReady()
|
||||
})
|
||||
} else {
|
||||
this.url = undefined
|
||||
this.services = []
|
||||
this.$emit('loadFileFailed')
|
||||
}
|
||||
},
|
||||
base64EncodeUnicode(str) {
|
||||
// 首先,将字符串编码为 UTF-8
|
||||
const utf8Bytes = new TextEncoder().encode(str);
|
||||
// 然后,将 Uint8Array 转换为字符串
|
||||
const binaryStr = String.fromCharCode(...utf8Bytes);
|
||||
// 最后,使用 btoa 编码为 Base64
|
||||
return btoa(binaryStr);
|
||||
},
|
||||
onReady() {
|
||||
this.$emit('onReady')
|
||||
// this.url = 'http://192.168.0.233:8012/onlinePreview?url='+encodeURIComponent(this.base64EncodeUnicode('https://cloud.jmal.top/api/file/jmal/tewo/海印缤缤广场/冷机智能控制系统布线图.pdf'))
|
||||
//this.url = 'https://alist-org.github.io/pdf.js/web/viewer.html?file=' + encodeURIComponent('http://localhost:9528/api/file/jmal/jmal/share/滨滨广场冷冻侧采集箱布线图_副本 (1).pdf')
|
||||
if (!this.url) {
|
||||
this.$emit('loadFileFailed')
|
||||
}
|
||||
},
|
||||
openUrl() {
|
||||
window.open(this.url)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.top-bar {
|
||||
width: 100%;
|
||||
top: 0;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
background: #fff;
|
||||
}
|
||||
.top-left {
|
||||
width: 25%;
|
||||
}
|
||||
.top-center {
|
||||
width: 50%;
|
||||
text-align: center;
|
||||
align-content: center;
|
||||
}
|
||||
.top-right {
|
||||
width: 25%;
|
||||
.svg-icon {
|
||||
font-size: 14px;
|
||||
}
|
||||
.top-right-option {
|
||||
float: right;
|
||||
margin-right: 3rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
height: 2.5rem;
|
||||
line-height: 2.5rem;
|
||||
}
|
||||
>>> .el-select {
|
||||
width: 135px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
.iframe-content {
|
||||
position: absolute;
|
||||
top: 2.5rem;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
-webkit-box-pack: center;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -16,10 +16,13 @@
|
||||
<svg-icon class="preview-close" icon-class="close"/>
|
||||
</div>
|
||||
<div class="wrapper">
|
||||
<div>{{fileType}}</div>
|
||||
<pdf-preview v-if="fileType === 'pdf'" :file="file" :shareId="shareId" :file-url="fileUrl" @onReady="onReady"></pdf-preview>
|
||||
<drawio v-else-if="fileType === 'drawio'" v-show="fileReday" :file="file" :shareId="shareId" :read-only="readOnly" @onEdit="onEdit" @onReady="onReady" @onClose="close"></drawio>
|
||||
<my-mind-editor v-else-if="fileType === 'mind'" :file="file" :shareId="shareId" :read-only="readOnly" @onEdit="onEdit" @onReady="onReady" @onClose="close"></my-mind-editor>
|
||||
<only-office-editor ref="officeEditor" v-else :file="file" :file-url="fileUrl" :shareId="shareId" :read-only="readOnly" @onEdit="onEdit" @manualSave="manualSave" @onClose="close" @onReady="onReady"></only-office-editor>
|
||||
<model-preview v-else-if="fileType === 'glTF/GLB'" :file="file" :file-url="fileUrl" :shareId="shareId" @onReady="onReady"></model-preview>
|
||||
<only-office-editor ref="officeEditor" v-else-if="fileType === 'office'" :file="file" :file-url="fileUrl" :shareId="shareId" :sharer="sharer" :read-only="readOnly" @onEdit="onEdit" @manualSave="manualSave" @onClose="close" @onReady="onReady"></only-office-editor>
|
||||
<iframe-content-preview v-else :file="file" :fileHandler="fileHandler" :file-url="fileUrl" @onReady="onReady" @loadFileFailed="loadFileFailed"></iframe-content-preview>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -28,17 +31,18 @@
|
||||
|
||||
<script>
|
||||
|
||||
import ModelPreview from '@/components/office/ModelPreview.vue'
|
||||
import OnlyOfficeEditor from "@/components/office/OnlyOfficeEditor";
|
||||
import MessageDialog from "@/components/message/MessageDialog";
|
||||
import Bus from "@/assets/js/bus";
|
||||
import IframeContentPreview from '@/components/preview/IframeContentPreview.vue'
|
||||
import fileConfig from "@/utils/file-config";
|
||||
import PdfPreview from "@/components/office/PdfPreview";
|
||||
import Drawio from "@/components/office/Drawio";
|
||||
import MyMindEditor from "@/components/Minder/minder"
|
||||
|
||||
export default {
|
||||
name: 'OfficePreview',
|
||||
components: {Drawio, PdfPreview, MessageDialog, OnlyOfficeEditor, MyMindEditor},
|
||||
name: 'IframePreview',
|
||||
components: { IframeContentPreview, ModelPreview, Drawio, PdfPreview, MessageDialog, OnlyOfficeEditor, MyMindEditor},
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
@ -52,10 +56,18 @@ export default {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
fileHandler: {
|
||||
type: Object,
|
||||
default: {}
|
||||
},
|
||||
shareId: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
sharer: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
status: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
@ -115,6 +127,12 @@ export default {
|
||||
}
|
||||
},
|
||||
getType(suffix) {
|
||||
if (Object.keys(this.fileHandler).length > 0) {
|
||||
return 'other'
|
||||
}
|
||||
if (this.file.contentType.indexOf('office') > -1) {
|
||||
return 'office'
|
||||
}
|
||||
switch (suffix) {
|
||||
case 'drawio':
|
||||
return 'drawio'
|
||||
@ -122,8 +140,14 @@ export default {
|
||||
return 'pdf'
|
||||
case 'mind':
|
||||
return 'mind'
|
||||
default:
|
||||
case 'gltf':
|
||||
case 'glb':
|
||||
return 'glTF/GLB'
|
||||
case 'office':
|
||||
case 'csv':
|
||||
return 'office'
|
||||
default:
|
||||
return 'other'
|
||||
}
|
||||
},
|
||||
/**
|
||||
@ -207,7 +231,7 @@ export default {
|
||||
this.$message.info('请手动保存')
|
||||
},
|
||||
/**
|
||||
* 检查要预览调文件是否为只读
|
||||
* 检查要预览的文件是否为只读
|
||||
* @param file userId
|
||||
*/
|
||||
checkReadOnly(file){
|
||||
@ -153,11 +153,12 @@ export default {
|
||||
this.images.forEach(image => {
|
||||
imageUrls.push(image.source)
|
||||
})
|
||||
const that = this
|
||||
this.vantImagePreview = vant.ImagePreview({
|
||||
images: imageUrls,
|
||||
startPosition: viewIndex,
|
||||
onClose() {
|
||||
this.$store.dispatch('updateMessage', { event: 'updateImageViewerStatus'})
|
||||
that.$store.dispatch('updateMessage', { event: 'updateImageViewerStatus'})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -207,6 +207,7 @@
|
||||
mountFileId: undefined,
|
||||
saveTitle: '',
|
||||
options: {
|
||||
tabSize: 2,
|
||||
fontSize: 14,
|
||||
contextmenu: true,
|
||||
codeLens: true,
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
<script>
|
||||
import fileConfig from '@/utils/file-config'
|
||||
import Artplayer from "@/components/preview/Artplayer.vue";
|
||||
import artplayerPluginVttThumbnail from './artplayer-plugin-vtt-thumbnail';
|
||||
import Hls from "hls.js";
|
||||
import FlvJs from "flv.js";
|
||||
|
||||
@ -45,6 +46,7 @@ export default {
|
||||
return {
|
||||
pc: this.$pc,
|
||||
art: null,
|
||||
switchQuality: false,
|
||||
option: {
|
||||
url: '',
|
||||
title: '', // 视频标题,目前会出现在 视频截图 和 迷你模式 下
|
||||
@ -78,7 +80,7 @@ export default {
|
||||
style: {
|
||||
marginRight: this.$pc ? '20px' : '0px',
|
||||
},
|
||||
html: `<a href="nplayer-videLink"><img style="width: 1.9rem;height: 1.9rem;line-height: 1.9rem" src="${require("@/assets/img/nplayer.webp")}"></a>`,
|
||||
html: `<a><img style="width: 1.9rem;height: 1.9rem;line-height: 1.9rem" src="${require("@/assets/img/nplayer.webp")}"></a>`,
|
||||
index: 2,
|
||||
},
|
||||
{
|
||||
@ -88,7 +90,7 @@ export default {
|
||||
style: {
|
||||
marginRight: this.$pc ? '20px' : '0px',
|
||||
},
|
||||
html: `<a href="infuse://x-callback-url/play?url=videLink"><img style="width: 1.9rem;height: 1.9rem;line-height: 1.9rem" src="${require("@/assets/img/infuse.webp")}"></a>`,
|
||||
html: `<a><img style="width: 1.9rem;height: 1.9rem;line-height: 1.9rem" src="${require("@/assets/img/infuse.webp")}"></a>`,
|
||||
index: 3,
|
||||
},
|
||||
{
|
||||
@ -101,7 +103,7 @@ export default {
|
||||
},
|
||||
click: () => this.copyToClipboard(this.videoLink),
|
||||
}
|
||||
],
|
||||
]
|
||||
},
|
||||
videoLink: '',
|
||||
style: {
|
||||
@ -124,6 +126,12 @@ export default {
|
||||
watch: {
|
||||
status: function (visible) {
|
||||
if (visible) {
|
||||
|
||||
if (!this.$pc) {
|
||||
// 去掉this.option.controls中name为 name: 'iina'
|
||||
this.option.controls = this.option.controls.filter(item => item.name === 'nplayer' || item.name === 'infuse')
|
||||
}
|
||||
|
||||
let videoUrl = fileConfig.previewUrl(this.$store.state.user.name, this.file, this.$store.getters.token)
|
||||
if (this.shareId) {
|
||||
videoUrl = fileConfig.publicPreviewUrl(this.file, window.shareId, this.$store.getters.shareToken)
|
||||
@ -137,8 +145,9 @@ export default {
|
||||
}
|
||||
}
|
||||
this.option.url = videoUrl
|
||||
|
||||
if (this.file.m3u8) {
|
||||
const supported = ['flv', 'mp4', 'ogg', 'mkv', 'webm', 'hls', 'mov']
|
||||
const supported = ['mp4', 'ogg', 'mkv', 'webm', 'hls', 'mov']
|
||||
if (supported.includes(this.file.suffix.toLowerCase())) {
|
||||
this.option.quality = [
|
||||
{
|
||||
@ -155,17 +164,30 @@ export default {
|
||||
this.option.customType = {
|
||||
m3u8: this.playM3u8
|
||||
}
|
||||
this.option.plugins = [
|
||||
artplayerPluginVttThumbnail({
|
||||
vtt: videoUrl.replace(/m3u8/g, 'vtt'),
|
||||
})
|
||||
]
|
||||
}
|
||||
|
||||
if (this.file.contentType.indexOf('flv') > -1) {
|
||||
this.option.url = originUrl
|
||||
this.option.quality = []
|
||||
this.option.customType = {
|
||||
flv: this.playFlv
|
||||
}
|
||||
}
|
||||
|
||||
this.videoLink = window.location.origin + videoUrl
|
||||
this.videoLink = window.location.origin + originUrl
|
||||
this.title = this.file.name
|
||||
this.option.id = this.file.id
|
||||
this.show = true
|
||||
this.switchQuality = true
|
||||
} else {
|
||||
this.option.url = ''
|
||||
this.videoLink = ''
|
||||
this.option.quality = []
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -197,15 +219,24 @@ export default {
|
||||
getInstance(art) {
|
||||
this.art = art
|
||||
let doc = this.art.controls.$parent
|
||||
// iina
|
||||
const iina = doc.querySelector('[data-index="1"]').querySelector('a');
|
||||
iina.href = `iina://weblink?url=${this.videoLink}`
|
||||
// nplayer
|
||||
const nplayer = doc.querySelector('[data-index="2"]').querySelector('a');
|
||||
nplayer.href = `nplayer-${this.videoLink}`
|
||||
// infuse
|
||||
const infuse = doc.querySelector('[data-index="3"]').querySelector('a');
|
||||
infuse.href = `infuse://x-callback-url/play?url=${this.videoLink}`
|
||||
if (this.$pc) {
|
||||
// iina
|
||||
const iina = doc.querySelector('[data-index="1"]').querySelector('a');
|
||||
iina.href = `iina://weblink?url=${this.videoLink}`
|
||||
// nplayer
|
||||
const nplayer = doc.querySelector('[data-index="2"]').querySelector('a');
|
||||
nplayer.href = `nplayer-${this.videoLink}`
|
||||
// infuse
|
||||
const infuse = doc.querySelector('[data-index="3"]').querySelector('a');
|
||||
infuse.href = `infuse://x-callback-url/play?url=${this.videoLink}`
|
||||
} else {
|
||||
// nplayer
|
||||
const nplayer = doc.querySelector('[data-index="2"]').querySelector('a');
|
||||
nplayer.href = `nplayer-${this.videoLink}`
|
||||
// infuse
|
||||
const infuse = doc.querySelector('[data-index="3"]').querySelector('a');
|
||||
infuse.href = `infuse://x-callback-url/play?url=${this.videoLink}`
|
||||
}
|
||||
},
|
||||
playM3u8(video, url, art) {
|
||||
if (Hls.isSupported()) {
|
||||
|
||||
@ -0,0 +1,68 @@
|
||||
function padEnd(str, targetLength, padString) {
|
||||
if (str.length > targetLength) {
|
||||
return String(str);
|
||||
} else {
|
||||
targetLength = targetLength - str.length;
|
||||
if (targetLength > padString.length) {
|
||||
padString += padString.repeat(targetLength / padString.length);
|
||||
}
|
||||
return String(str) + padString.slice(0, targetLength);
|
||||
}
|
||||
}
|
||||
|
||||
function t2d(time) {
|
||||
var arr = time.split('.');
|
||||
var left = arr[0].split(':') || [];
|
||||
var right = padEnd(arr[1] || '0', 3, '0');
|
||||
var ms = Number(right) / 1000;
|
||||
|
||||
var h = Number(left[left.length - 3] || 0) * 3600;
|
||||
var m = Number(left[left.length - 2] || 0) * 60;
|
||||
var s = Number(left[left.length - 1] || 0);
|
||||
return h + m + s + ms;
|
||||
}
|
||||
|
||||
export default async function getVttArray(vttUrl = '') {
|
||||
const vttString = await (await fetch(vttUrl)).text();
|
||||
const lines = vttString.split(/[\n\r]/gm).filter((item) => item.trim());
|
||||
const vttArray = [];
|
||||
|
||||
for (let i = 1; i < lines.length; i += 2) {
|
||||
const time = lines[i];
|
||||
const text = lines[i + 1];
|
||||
|
||||
if (!text.trim()) continue;
|
||||
|
||||
const timeReg =
|
||||
/((?:[0-9]{2}:)?(?:[0-9]{2}:)?[0-9]{2}(?:.[0-9]{3})?)(?: ?--> ?)((?:[0-9]{2}:)?(?:[0-9]{2}:)?[0-9]{2}(?:.[0-9]{3})?)/;
|
||||
const timeMatch = time.match(timeReg);
|
||||
|
||||
const textReg = /(.*)#(\w{4})=(.*)/i;
|
||||
const textMatch = text.match(textReg);
|
||||
|
||||
const start = Math.floor(t2d(timeMatch[1]));
|
||||
const end = Math.floor(t2d(timeMatch[2]));
|
||||
|
||||
let url = textMatch[1];
|
||||
const isAbsoluteUrl = /^\/|((https?|ftp|file):\/\/)/i.test(url);
|
||||
if (!isAbsoluteUrl) {
|
||||
const urlArr = vttUrl.split('/');
|
||||
urlArr.pop();
|
||||
urlArr.push(url);
|
||||
url = urlArr.join('/');
|
||||
}
|
||||
|
||||
const result = { start, end, url };
|
||||
|
||||
const keys = textMatch[2].split('');
|
||||
const values = textMatch[3].split(',');
|
||||
|
||||
for (let j = 0; j < keys.length; j++) {
|
||||
result[keys[j]] = values[j];
|
||||
}
|
||||
|
||||
vttArray.push(result);
|
||||
}
|
||||
|
||||
return vttArray;
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
import getVttArray from './getVttArray';
|
||||
|
||||
export default function artplayerPluginVttThumbnail(option) {
|
||||
return async (art) => {
|
||||
const {
|
||||
constructor: {
|
||||
utils: { setStyle, isMobile },
|
||||
},
|
||||
template: { $progress },
|
||||
} = art;
|
||||
|
||||
let timer = null;
|
||||
const thumbnails = await getVttArray(option.vtt);
|
||||
function showThumbnails($control, find, width) {
|
||||
setStyle($control, 'backgroundImage', `url(${find.url})`);
|
||||
setStyle($control, 'height', `${find.h}px`);
|
||||
setStyle($control, 'width', `${find.w}px`);
|
||||
setStyle($control, 'backgroundPosition', `-${find.x}px -${find.y}px`);
|
||||
if (width <= find.w / 2) {
|
||||
setStyle($control, 'left', 0);
|
||||
} else if (width > $progress.clientWidth - find.w / 2) {
|
||||
setStyle($control, 'left', `${$progress.clientWidth - find.w}px`);
|
||||
} else {
|
||||
setStyle($control, 'left', `${width - find.w / 2}px`);
|
||||
}
|
||||
}
|
||||
|
||||
art.controls.add({
|
||||
name: 'thumbnails',
|
||||
position: 'top',
|
||||
index: 20,
|
||||
style: option.style || {},
|
||||
mounted($control) {
|
||||
art.on('setBar', async (type, percentage, event) => {
|
||||
const isMobileDroging = type === 'played' && event && isMobile;
|
||||
|
||||
if (type === 'hover' || isMobileDroging) {
|
||||
const width = $progress.clientWidth * percentage;
|
||||
const second = percentage * art.duration;
|
||||
setStyle($control, 'display', 'flex');
|
||||
|
||||
const find = thumbnails.find((item) => second >= item.start && second <= item.end);
|
||||
if (!find) return setStyle($control, 'display', 'none');
|
||||
|
||||
if (width > 0 && width < $progress.clientWidth) {
|
||||
showThumbnails($control, find, width);
|
||||
} else {
|
||||
if (!isMobile) {
|
||||
setStyle($control, 'display', 'none');
|
||||
}
|
||||
}
|
||||
|
||||
if (isMobileDroging) {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(() => {
|
||||
setStyle($control, 'display', 'none');
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
name: 'artplayerPluginVttThumbnail',
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window['artplayerPluginVttThumbnail'] = artplayerPluginVttThumbnail;
|
||||
}
|
||||
1
src/icons/svg/file-c.svg
Normal file
1
src/icons/svg/file-c.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1716514768273" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="40143" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M923.2 245.6L536.8 23.2c-6.4-4-15.2-5.6-24.8-5.6-9.6 0-18.4 2.4-24.8 5.6l-384 223.2c-13.6 8-23.2 28-23.2 43.2v445.6c0 8.8 1.6 19.2 8 28l854.4-496c-4.8-9.6-12-16.8-19.2-21.6z" fill="#659AD3" p-id="40144"></path><path d="M85.6 762.4c4 6.4 9.6 12 15.2 15.2l385.6 223.2c6.4 4 15.2 5.6 24.8 5.6 9.6 0 18.4-2.4 24.8-5.6l384-223.2c13.6-8 23.2-28 23.2-43.2V288.8c0-7.2-0.8-15.2-4.8-22.4l-852.8 496z" fill="#03599C" p-id="40145"></path><path d="M682.4 608.8C648.8 668 584.8 708 512 708c-108 0-196-88-196-196s88-196 196-196c72.8 0 136.8 40 170.4 100l104-60c-54.4-95.2-156.8-160-274.4-160-174.4 0-316 141.6-316 316s141.6 316 316 316c116.8 0 219.2-64 273.6-158.4l-103.2-60.8z" fill="#FFFFFF" p-id="40146"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 5.8 KiB |
1
src/icons/svg/file-glb.svg
Normal file
1
src/icons/svg/file-glb.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1718450747239" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6336" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M263.68 337.9712h572.5696v562.8416H302.4896a38.8096 38.8096 0 0 1-38.8096-38.8096z" fill="#2F68CB" p-id="6337"></path><path d="M184.32 255.3856l137.6768-71.68v519.2192L184.32 774.5536z" fill="#93BBFF" p-id="6338"></path><path d="M321.9456 183.7056h417.28a38.8096 38.8096 0 0 1 38.8096 38.8096v479.5392H321.9456z" fill="#478BFF" p-id="6339"></path><path d="M482.56 877.1584c27.0336 0 44.2368-13.2096 44.2368-29.4912 0-14.4384-10.24-20.48-30.4128-20.48h-15.36c-10.9056-0.1024-14.2848-3.328-14.2848-8.448a11.2128 11.2128 0 0 1 4.608-8.9088 29.3888 29.3888 0 0 0 11.8272 2.6112 28.8768 28.8768 0 0 0 31.6416-29.0304 22.8352 22.8352 0 0 0-5.5296-15.36h16.128v-13.312h-29.952a35.2256 35.2256 0 0 0-12.288-2.1504 30.72 30.72 0 0 0-32.6144 30.3104 28.928 28.928 0 0 0 10.9568 22.528v0.6144a20.8384 20.8384 0 0 0-9.3696 15.6672 15.36 15.36 0 0 0 7.68 13.9776v0.768a22.3232 22.3232 0 0 0-12.288 18.432c0 14.7456 14.7456 22.272 35.0208 22.272z m0.6144-76.032a16.4864 16.4864 0 0 1-15.9744-18.2272 16.0768 16.0768 0 0 1 15.9744-17.9712 16.1792 16.1792 0 0 1 15.9744 17.9712 16.4864 16.4864 0 0 1-15.9744 18.2272z m1.9968 64.3584c-14.1312 0-22.5792-4.9152-22.5792-13.2096a15.36 15.36 0 0 1 7.2192-12.288 53.7088 53.7088 0 0 0 11.4688 1.2288h12.7488c10.24 0 15.8208 2.1504 15.8208 9.216s-9.9328 15.0528-24.6784 15.0528z m78.4896-24.2688a28.3648 28.3648 0 0 0 9.7792-1.3824l-2.56-13.3632a12.288 12.288 0 0 1-2.9184 0.3072c-2.1504 0-4.3008-1.6896-4.3008-6.4512V716.8h-17.5104v102.4c0 13.8752 4.7616 22.016 17.5104 22.016z m71.2192 0c19.0464 0 36.4032-16.5888 36.4032-45.6192 0-25.9584-12.1344-43.008-33.4848-43.008a40.3456 40.3456 0 0 0-25.1904 11.0592l0.4608-14.4384V716.8h-17.4592v122.4192h13.6704l1.6896-8.7552h0.4608a35.072 35.072 0 0 0 23.4496 10.752z m-3.328-14.592a28.3136 28.3136 0 0 1-18.432-7.68v-41.1648a29.3888 29.3888 0 0 1 20.1216-10.24c14.1312 0 19.8144 10.9056 19.8144 28.5696 0 19.6096-9.216 30.5152-21.504 30.5152z" fill="#FFFFFF" p-id="6340"></path><path d="M498.2784 401.3056h23.04v90.4704h-23.04z" fill="#FFFFFF" p-id="6341"></path><path d="M498.2784 401.3056h23.04v90.4704h-23.04z" fill="#FFFFFF" p-id="6342"></path><path d="M477.8496 426.9056c-5.12 0-10.9056 7.424-10.9056 17.3568s5.7856 17.3568 10.9056 17.3568 10.9568-7.424 10.9568-17.3568-5.7856-17.3568-10.9568-17.3568m0-25.6a40.1408 40.1408 0 0 1 36.5056 42.9568 40.1408 40.1408 0 0 1-36.5056 42.9568 40.1408 40.1408 0 0 1-36.5056-42.9568 40.1408 40.1408 0 0 1 36.5056-42.9568z" fill="#FFFFFF" p-id="6343"></path><path d="M665.088 499.9168a318.3104 318.3104 0 0 1-59.904 18.5344 324.608 324.608 0 0 1-63.744 5.12 47.9744 47.9744 0 0 1-15.0528 11.9296 192.256 192.256 0 0 1-28.2112 10.752 263.9872 263.9872 0 0 0 92.16-4.2496 205.5168 205.5168 0 0 0 74.752-42.0864z" fill="#FFFFFF" p-id="6344"></path><path d="M647.168 382.6176a197.0176 197.0176 0 0 0-231.2192-22.8352c-114.8416 76.8 6.656 208.0768 77.7216 171.6736a53.4528 53.4528 0 0 0 27.4432-41.7792l-16.4352-14.0288s-8.8576 32-26.3168 34.9696a56.7808 56.7808 0 0 1-49.664-24.1152 78.9504 78.9504 0 0 1-10.24-71.9872c14.2848-37.888 52.8896-49.8688 93.6448-54.784a293.2224 293.2224 0 0 1 135.0656 22.8864z" fill="#FFFFFF" p-id="6345"></path><path d="M539.2896 401.8688h23.04v92.16h-23.04z" fill="#FFFFFF" p-id="6346"></path><path d="M599.1936 401.8688h23.04v92.16h-23.04z" fill="#FFFFFF" p-id="6347"></path><path d="M659.1488 401.8688h23.04v92.16h-23.04z" fill="#FFFFFF" p-id="6348"></path><path d="M576.1536 401.8688h69.1712v23.04h-69.1712z" fill="#FFFFFF" p-id="6349"></path><path d="M659.1488 401.8688h69.1712v23.04h-69.1712z" fill="#FFFFFF" p-id="6350"></path><path d="M659.1488 436.48h58.88v23.04h-58.88z" fill="#FFFFFF" p-id="6351"></path></svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
@ -221,10 +221,6 @@ export default {
|
||||
tagClick(tag) {
|
||||
this.$router.push(`/tag?tagId=${tag.id}`);
|
||||
},
|
||||
mouseleave() {
|
||||
},
|
||||
onmousemove() {
|
||||
},
|
||||
progressFormat() {
|
||||
if (this.takeUpSpace === 0) {
|
||||
this.takeUpSpace = this.userInfo.takeUpSpace
|
||||
|
||||
@ -21,7 +21,7 @@ import ResizeMixin from './mixin/ResizeHandler'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import AudioPreview from "@/components/preview/AudioPreview";
|
||||
import globalUploader from '@/components/SimpleUploader/globalUploader.vue'
|
||||
import { connectToSSE } from '@/sse/SSEClient'
|
||||
import { initiateSSE } from '@/sse/SSEClient'
|
||||
|
||||
export default {
|
||||
name: 'Layout',
|
||||
@ -62,7 +62,7 @@ export default {
|
||||
if(getToken()){
|
||||
this.isShow = window.pc;
|
||||
// 与服务器建立sse连接
|
||||
connectToSSE(this.$store.state.user.name)
|
||||
initiateSSE(this.$store.state.user.name)
|
||||
if(this.$route.path.startsWith('/setting')){
|
||||
this.isShow = true
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import getPageTitle from '@/utils/get-page-title'
|
||||
|
||||
NProgress.configure({ showSpinner: false }) // NProgress配置
|
||||
|
||||
const whiteList = ['/login','/s','/s/','/s_m','/s_m/'] // 白名单
|
||||
const whiteList = ['/login','/s','/s/'] // 白名单
|
||||
|
||||
router.beforeEach(async(to, from, next) => {
|
||||
// 开始进度条
|
||||
@ -51,7 +51,7 @@ router.beforeEach(async(to, from, next) => {
|
||||
}
|
||||
} else {
|
||||
// 没有登录的情况
|
||||
if (whiteList.indexOf(to.path) !== -1) {
|
||||
if (hasWhiteList(to.path)) {
|
||||
// 没有token但是在白名单中,直接进入
|
||||
next()
|
||||
} else {
|
||||
@ -65,6 +65,15 @@ router.beforeEach(async(to, from, next) => {
|
||||
}
|
||||
})
|
||||
|
||||
function hasWhiteList(path) {
|
||||
for (let i = 0; i < whiteList.length; i++) {
|
||||
if (path.indexOf(whiteList[i]) !== -1) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function setMenuList(next, to){
|
||||
if(store.getters.menuList.length === 0){
|
||||
store.dispatch('user/setMenuList').then((res) => {
|
||||
|
||||
@ -47,12 +47,12 @@ export const constantRoutes = [
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: '/s',
|
||||
path: '/s/:id',
|
||||
component: Layout,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 's',
|
||||
name: 'share',
|
||||
component: () => import('@/views/public/share/shareList'),
|
||||
}
|
||||
]
|
||||
@ -75,15 +75,15 @@ export const dynamicRouters = [
|
||||
}]
|
||||
},
|
||||
{
|
||||
path: '/s_m',
|
||||
path: '/upload/index_m',
|
||||
component: Layout,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 's_m',
|
||||
component: () => import('@/views/public/share/shareList'),
|
||||
}
|
||||
]
|
||||
redirect: '/upload/index_m',
|
||||
children: [{
|
||||
path: '',
|
||||
name: 'Home_m',
|
||||
component: () => import('@/views/upload/mobile/index'),
|
||||
meta: { title: '文件传输' }
|
||||
}]
|
||||
},
|
||||
{
|
||||
path: '/tag',
|
||||
|
||||
@ -1,88 +1,113 @@
|
||||
import Bus from "@/assets/js/bus";
|
||||
import _ from "lodash";
|
||||
import store from "@/store";
|
||||
import {generateUUID} from "ant-design-vue/lib/vc-select/util";
|
||||
import { generateUUID } from "ant-design-vue/lib/vc-select/util";
|
||||
|
||||
let eventSource = null;
|
||||
let timer = null;
|
||||
let lastHeartbeat = new Date().getTime();
|
||||
|
||||
const uuid = generateUUID();
|
||||
|
||||
const throttledConnectSSE = _.throttle(username => {
|
||||
if (typeof (EventSource) !== 'undefined') {
|
||||
const url = `/api/events?username=${username}&uuid=${uuid}`;
|
||||
eventSource = new EventSource(url);
|
||||
eventSource.addEventListener('message', function (event) {
|
||||
if (event.data === 'h') {
|
||||
// 心跳包, 超过5s没有收到心跳包, 则关闭连接
|
||||
lastHeartbeat = new Date().getTime()
|
||||
} else {
|
||||
const msg = JSON.parse( event.data);
|
||||
onMessage(msg)
|
||||
}
|
||||
});
|
||||
eventSource.addEventListener('error', function (event) {
|
||||
eventSource.close()
|
||||
const throttledConnectSSE = _.throttle((username) => {
|
||||
if (typeof EventSource !== 'undefined') {
|
||||
// 判断cookie中是否有token
|
||||
if (store.getters.name) {
|
||||
connectToSSE(username);
|
||||
});
|
||||
// 清除定时器
|
||||
if (timer) {
|
||||
clearInterval(timer);
|
||||
}
|
||||
// 3s检测一次连接状态
|
||||
timer = setInterval(() => {
|
||||
if (eventSource.readyState === 1) {
|
||||
// 心跳包, 超过5s没有收到心跳包, 则关闭连接
|
||||
if (new Date().getTime() - lastHeartbeat > 5000) {
|
||||
eventSource.close()
|
||||
}
|
||||
}
|
||||
if (eventSource.readyState === 2) {
|
||||
connectToSSE(username);
|
||||
}
|
||||
}, 3000);
|
||||
} else {
|
||||
console.error('EventSource is not supported by the browser');
|
||||
}
|
||||
}, 3000);
|
||||
|
||||
export function connectToSSE(username) {
|
||||
function connectToSSE(username) {
|
||||
|
||||
if (eventSource) {
|
||||
// 移除之前的事件监听器
|
||||
eventSource.removeEventListener('message', handleMessage);
|
||||
eventSource.removeEventListener('error', handleError.bind(null, username));
|
||||
}
|
||||
|
||||
const url = `/api/events?username=${username}&uuid=${uuid}`;
|
||||
eventSource = new EventSource(url);
|
||||
|
||||
eventSource.addEventListener('message', handleMessage);
|
||||
eventSource.addEventListener('error', handleError.bind(null, username));
|
||||
}
|
||||
|
||||
function handleMessage(event) {
|
||||
const msg = JSON.parse(event.data);
|
||||
onMessage(msg);
|
||||
}
|
||||
|
||||
function handleError(username) {
|
||||
eventSource.close();
|
||||
throttledConnectSSE(username);
|
||||
}
|
||||
|
||||
function onMessage(msg) {
|
||||
const url = msg.url
|
||||
if (url === 'synced') {
|
||||
store.dispatch('updateMessage', {event: 'msg/synced', data: msg})
|
||||
} else {
|
||||
store.dispatch('updateMessage', {event: 'msg/file/change', data: msg})
|
||||
if ('operationFile' === url) {
|
||||
let doc = msg.body
|
||||
if (doc.code === 0) {
|
||||
Bus.notify({
|
||||
title: `${doc.operation}成功`,
|
||||
dangerouslyUseHTMLString: true,
|
||||
message: `
|
||||
<div>
|
||||
<p>form:</p>
|
||||
<pre style="word-break: break-all;white-space: pre-wrap;font-size: 12px;">${doc.from}</pre>
|
||||
</div>
|
||||
<div>
|
||||
<p>to:</p>
|
||||
<pre style="word-break: break-all;white-space: pre-wrap;font-size: 12px;">${doc.to}</pre>
|
||||
</div>`,
|
||||
type: 'success',
|
||||
});
|
||||
} else {
|
||||
store.dispatch('updateMessage', {event: 'msg/file/operation/fault', data: msg});
|
||||
Bus.notify({
|
||||
title: `${doc.operation}失败`,
|
||||
dangerouslyUseHTMLString: true,
|
||||
message: `<span style="font-size: 12px;">${doc.msg}</span>`,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
function clearExistingTimer() {
|
||||
if (timer) {
|
||||
clearInterval(timer);
|
||||
}
|
||||
}
|
||||
|
||||
function onMessage(msg) {
|
||||
const { url, body } = msg;
|
||||
const eventMapping = {
|
||||
synced: 'msg/synced',
|
||||
taskProgress: 'msg/taskProgress',
|
||||
transcodeStatus: 'msg/transcodeStatus',
|
||||
uploaderChunkSize: 'uploaderChunkSize',
|
||||
};
|
||||
|
||||
if (eventMapping[url]) {
|
||||
store.dispatch('updateMessage', { event: eventMapping[url], data: msg });
|
||||
} else {
|
||||
handleFileChangeMessage(msg, url, body);
|
||||
}
|
||||
}
|
||||
|
||||
function handleFileChangeMessage(msg, url, body) {
|
||||
store.dispatch('updateMessage', { event: 'msg/file/change', data: msg });
|
||||
|
||||
if (url === 'operationFile') {
|
||||
const { operation, from, to, code, msg: message } = body;
|
||||
const notificationOptions = {
|
||||
title: `${operation} ${code === 0 ? '成功' : '失败'}`,
|
||||
dangerouslyUseHTMLString: true,
|
||||
message: code === 0
|
||||
? `
|
||||
<div>
|
||||
<p>from:</p>
|
||||
<pre style="word-break: break-all;white-space: pre-wrap;font-size: 12px;">${from}</pre>
|
||||
</div>
|
||||
<div>
|
||||
<p>to:</p>
|
||||
<pre style="word-break: break-all;white-space: pre-wrap;font-size: 12px;">${to}</pre>
|
||||
</div>`
|
||||
: `<span style="font-size: 12px;">${message}</span>`,
|
||||
type: code === 0 ? 'success' : 'error',
|
||||
};
|
||||
|
||||
Bus.notify(notificationOptions);
|
||||
|
||||
if (code !== 0) {
|
||||
store.dispatch('updateMessage', { event: 'msg/file/operation/fault', data: msg });
|
||||
}
|
||||
}
|
||||
|
||||
if (url === 'operationTips') {
|
||||
const { operation, success, msg: message } = body
|
||||
const notificationOptions = {
|
||||
title: `${operation} ${success ? '成功' : '失败'}`,
|
||||
dangerouslyUseHTMLString: true,
|
||||
message: success ? '' : message ? `<span style="font-size: 12px;">${message}</span>` : '',
|
||||
type: success ? 'success' : 'error',
|
||||
}
|
||||
Bus.notify(notificationOptions)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export function initiateSSE(username) {
|
||||
throttledConnectSSE(username);
|
||||
}
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@ const getters = {
|
||||
name: state => state.user.name,
|
||||
showName: state => state.user.showName,
|
||||
newVersion: state => state.user.newVersion,
|
||||
iframePreviewConfig: state => state.user.iframePreviewConfig,
|
||||
userId: state => state.user.userId,
|
||||
userInfo: state => state.user.userInfo,
|
||||
menuList: state => state.user.menuList,
|
||||
|
||||
@ -33,7 +33,8 @@ const getDefaultState = () => {
|
||||
menuList: [],
|
||||
netdiskName: '',
|
||||
netdiskLogo: '',
|
||||
newVersion: ''
|
||||
newVersion: '',
|
||||
iframePreviewConfig: {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,6 +76,9 @@ const mutations = {
|
||||
},
|
||||
SET_NEW_VERSION:(state,newVersion) => {
|
||||
state.newVersion = newVersion;
|
||||
},
|
||||
SET_IFRAME_PREVIEW:(state, iframePreviewConfig) => {
|
||||
state.iframePreviewConfig = iframePreviewConfig;
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,7 +151,7 @@ const actions = {
|
||||
// reject('Verification failed, please Login again.')
|
||||
}
|
||||
|
||||
const { id, username, showName, avatar, netdiskName, netdiskLogo, newVersion} = data
|
||||
const { id, username, showName, avatar, netdiskName, netdiskLogo, newVersion, iframe} = data
|
||||
|
||||
commit('SET_NAME', username)
|
||||
commit('SET_SHOW_NAME', showName)
|
||||
@ -156,6 +160,11 @@ const actions = {
|
||||
commit('SET_USER_INFO', data)
|
||||
commit('SET_NETDISK_NAME', netdiskName)
|
||||
commit('SET_NETDISK_LOGO', netdiskLogo)
|
||||
if (iframe) {
|
||||
commit('SET_IFRAME_PREVIEW', JSON.parse(iframe))
|
||||
} else {
|
||||
commit('SET_IFRAME_PREVIEW', {})
|
||||
}
|
||||
if (newVersion) {
|
||||
commit('SET_NEW_VERSION', newVersion)
|
||||
} else {
|
||||
|
||||
@ -285,7 +285,7 @@
|
||||
}
|
||||
|
||||
>>> .van-grid {
|
||||
padding: 0;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
>>> .van-grid-item__content .svg-icon{
|
||||
@ -293,6 +293,21 @@
|
||||
}
|
||||
|
||||
>>> .grid-item-text {
|
||||
margin-top: 5px;
|
||||
cursor: default;
|
||||
text-align: center;
|
||||
color: #606266;
|
||||
font-size: smaller;
|
||||
//flex: 1;
|
||||
//align-items: center;
|
||||
justify-content: space-evenly;
|
||||
display: flex;
|
||||
word-break: break-word;
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
>>> .grid-item-text-input {
|
||||
cursor: default;
|
||||
text-align: center;
|
||||
color: #606266;
|
||||
@ -306,6 +321,7 @@
|
||||
padding: 0 5px 0 5px;
|
||||
bottom: 5px;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
textarea {
|
||||
overflow: hidden;
|
||||
resize:none;
|
||||
@ -330,20 +346,16 @@
|
||||
}
|
||||
>>> .grid-item {
|
||||
border-radius: 5px;
|
||||
padding: 0 8px;
|
||||
border: solid 1px #ccc0;
|
||||
.grid-item-icon{
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
//margin: 9px auto 0;
|
||||
//width: 84px;
|
||||
//height: 84px;
|
||||
//text-align: center;
|
||||
//position: absolute;
|
||||
//top: 0;
|
||||
}
|
||||
.grid-item-icon-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0px;
|
||||
top: 0;
|
||||
border-radius: 5px;
|
||||
margin: 0;
|
||||
-webkit-backdrop-filter: contrast(0.5);
|
||||
|
||||
@ -226,3 +226,16 @@ input {
|
||||
.dark-file-history-list.el-table::before {
|
||||
background-color: #3e3e3e;
|
||||
}
|
||||
kbd {
|
||||
display: inline-block;
|
||||
padding: 5px 5px;
|
||||
font: 14px var(--fontStack-monospace, ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace);
|
||||
line-height: 10px;
|
||||
color: #1f2328;
|
||||
vertical-align: middle;
|
||||
background-color: #f6f8fa;
|
||||
border: solid 1px rgba(175, 184, 193, .2);
|
||||
border-radius: 6px;
|
||||
-webkit-box-shadow: inset 0 -1px 0 rgba(175, 184, 193, .2);
|
||||
box-shadow: inset 0 -1px 0 rgba(175, 184, 193, .2);
|
||||
}
|
||||
|
||||
@ -197,4 +197,28 @@ a:hover {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.setting-title-desc {
|
||||
.setting-title-desc-text {
|
||||
padding: 5px 0 20px 0;
|
||||
}
|
||||
>>>.el-divider--horizontal {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
>>> .el-form {
|
||||
.form-instruction {
|
||||
color: #9a9a9a;
|
||||
font-size: .8125rem;
|
||||
a {
|
||||
color: #1890ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
.form-item-flex {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
>>> .el-icon-question {
|
||||
padding: 5px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,15 +12,23 @@ export default {
|
||||
return window.location.origin + `/webDAV/${username}`
|
||||
},
|
||||
// office api url
|
||||
officeApiUrl: function() {
|
||||
return `${window.location.origin}/office/web-apps/apps/api/documents/api.js`
|
||||
officeApiUrl: function(documentServer) {
|
||||
let serverUrl = documentServer || `${window.location.origin}/office`
|
||||
if (!serverUrl.endsWith('/')) {
|
||||
serverUrl = serverUrl + "/"
|
||||
}
|
||||
return serverUrl + "web-apps/apps/api/documents/api.js"
|
||||
},
|
||||
// office回调url
|
||||
officeCallBackUrl: function(token, username, fileId) {
|
||||
return `http://jmalcloud:8088/office/track?jmal-token=${token}&name=${username}&fileId=${fileId}`
|
||||
officeCallBackUrl: function(callbackServer, token, username, fileId) {
|
||||
let callbackServerUrl = callbackServer || 'http://jmalcloud:8088'
|
||||
if (!callbackServerUrl.endsWith('/')) {
|
||||
callbackServerUrl = callbackServerUrl + "/"
|
||||
}
|
||||
return `${callbackServerUrl}office/track?jmal-token=${token}&name=${username}&fileId=${fileId}`
|
||||
},
|
||||
// 预览文件的url
|
||||
previewUrl: function(username, file, token, shareToken) {
|
||||
previewUrl: function(username, file, token, shareToken, serverUrl) {
|
||||
let owner = null
|
||||
if (username !== store.getters.name || localStorage.getItem('mountFileOwner') !== null) {
|
||||
owner = localStorage.getItem('mountFileOwner')
|
||||
@ -30,8 +38,10 @@ export default {
|
||||
if (owner == null) {
|
||||
owner = store.getters.name
|
||||
}
|
||||
let fileUrl = `${this.baseUrl}/file/${owner}${encodeURI(file.path)}${encodeURI(file.name)}`
|
||||
fileUrl = fileUrl.replace(/%5C/g, '/')
|
||||
let baseUrl = serverUrl || this.baseUrl
|
||||
let fileUrl = `${baseUrl}/file/${owner}${encodeURI(file.path)}${encodeURI(file.name)}`
|
||||
fileUrl = fileUrl.replaceAll('#', '%23')
|
||||
fileUrl = fileUrl.replaceAll(/%5C/g, '/')
|
||||
if (token) {
|
||||
return `${fileUrl}?jmal-token=${token}&name=${username}`
|
||||
}
|
||||
@ -64,7 +74,8 @@ export default {
|
||||
// 打包下载文件
|
||||
packageDownload: function(fileIds, token, username) {
|
||||
fileApi.isAllowDownload().then(() => {
|
||||
window.open(`${this.baseUrl}/packageDownload?fileIds=${fileIds}&jmal-token=${token}&name=${username}`, '_self')
|
||||
fileIds = fileIds.join(',')
|
||||
window.open(`${this.baseUrl}/packageDownload?fileIds=${fileIds}`, '_self')
|
||||
})
|
||||
},
|
||||
// 共享文件下载
|
||||
@ -77,11 +88,12 @@ export default {
|
||||
window.open(url, '_blank')
|
||||
},
|
||||
// 共享文件预览Url
|
||||
publicPreviewUrl: function(file, shareId, shareToken) {
|
||||
publicPreviewUrl: function(file, shareId, shareToken, serverUrl) {
|
||||
if (!shareToken) {
|
||||
shareToken = "none"
|
||||
}
|
||||
return `${this.baseUrl}/public/s/preview/${file.name}?fileId=${file.id}&shareId=${shareId}&shareToken=${shareToken}`
|
||||
let baseUrl = serverUrl || this.baseUrl
|
||||
return `${baseUrl}/public/s/preview/${file.name}?fileId=${file.id}&shareId=${shareId}&shareToken=${shareToken}`
|
||||
},
|
||||
// 共享文件打包下载
|
||||
publicPackageDownload: function(shareId, fileIds, shareToken) {
|
||||
@ -96,6 +108,13 @@ export default {
|
||||
},
|
||||
// 共享文件打包下载Url
|
||||
publicPackageDownloadUrl: function(shareId, fileIds, shareToken) {
|
||||
return window.location.origin + `${this.baseUrl}/public/s/packageDownload?shareId=${shareId}&fileIds=${fileIds}&share-token=${shareToken}`
|
||||
const queryShareId = shareId ? `&shareId=${shareId}` : ''
|
||||
const queryShareToken = shareToken ? `&share-token=${shareToken}` : ''
|
||||
return window.location.origin + `${this.baseUrl}/public/s/packageDownload?fileIds=${fileIds}${queryShareId}${queryShareToken}`
|
||||
},
|
||||
// 文件打包下载Url
|
||||
packageDownloadUrl: function(fileId, downloadName, shareToken) {
|
||||
const queryShareToken = shareToken ? `?share-token=${shareToken}` : ''
|
||||
return window.location.origin + `${this.baseUrl}/public/s/${fileId}/packageDownload/${downloadName}${queryShareToken}`
|
||||
},
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ export const suffix = {
|
||||
'cmake','db','gradle','bat','conf','dart','plist'
|
||||
],
|
||||
compressedFile: [
|
||||
'zip','tar','jar','tar.gz','tgz','tar.bz2'
|
||||
'zip','tar','7z', 'rar', 'jar','tar.gz','tgz','tar.bz2'
|
||||
],
|
||||
}
|
||||
|
||||
@ -25,6 +25,7 @@ export const lineWrapping = [
|
||||
|
||||
export const iconClass = new Map()
|
||||
iconClass.set('php','file-php')
|
||||
iconClass.set('c','file-c')
|
||||
iconClass.set('js','file-js')
|
||||
iconClass.set('vue','file-vue')
|
||||
iconClass.set('xml','file-xml')
|
||||
@ -64,10 +65,12 @@ iconClass.set('pdf','file-pdf')
|
||||
iconClass.set('mind','file-mind')
|
||||
iconClass.set('zip','zip')
|
||||
iconClass.set('rar','zip')
|
||||
iconClass.set('7z','zip')
|
||||
iconClass.set('iml','file-idea')
|
||||
iconClass.set('key','file-key')
|
||||
iconClass.set('pem','file-pem')
|
||||
iconClass.set('gz','file-gzip')
|
||||
iconClass.set('tar.gz','file-gzip')
|
||||
iconClass.set('json','file-json')
|
||||
iconClass.set('tar','file-tar')
|
||||
iconClass.set('sh','file-shell')
|
||||
@ -83,5 +86,36 @@ iconClass.set('gradle','file-gradle')
|
||||
iconClass.set('bat','file-bat')
|
||||
iconClass.set('swift','file-swift')
|
||||
iconClass.set('yml','file-yml')
|
||||
iconClass.set('yaml','file-yml')
|
||||
iconClass.set('plist','file-plist')
|
||||
iconClass.set('iso','file-iso')
|
||||
iconClass.set('glb','file-glb')
|
||||
iconClass.set('gltf','file-glb')
|
||||
|
||||
export function findIconClass(file){
|
||||
if(!file){
|
||||
return
|
||||
}
|
||||
if(file.isFolder){
|
||||
return 'folder'
|
||||
}
|
||||
if (file.contentType) {
|
||||
if(file.contentType.indexOf('video') > -1){
|
||||
return 'video'
|
||||
}
|
||||
if(file.contentType.startsWith('image')){
|
||||
return 'image'
|
||||
}
|
||||
if(file.contentType.indexOf('audio') > -1){
|
||||
return 'audio'
|
||||
}
|
||||
}
|
||||
let suffix = file.suffix;
|
||||
if(!suffix && file.name){
|
||||
suffix = file.name.substring(file.name.lastIndexOf('.') + 1)
|
||||
}
|
||||
if(iconClass.has(suffix)){
|
||||
return iconClass.get(suffix)
|
||||
}
|
||||
return 'file'
|
||||
}
|
||||
|
||||
25
src/utils/load-script.js
Normal file
25
src/utils/load-script.js
Normal file
@ -0,0 +1,25 @@
|
||||
// loadScript.js
|
||||
export function loadScript(scriptUrl, type) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 检查是否已经加载了相同的脚本
|
||||
const existingScript = Array.from(document.head.getElementsByTagName('script')).find(
|
||||
script => script.src === scriptUrl
|
||||
)
|
||||
if (existingScript) {
|
||||
resolve()
|
||||
} else {
|
||||
const script = document.createElement('script');
|
||||
script.src = scriptUrl;
|
||||
if (type) {
|
||||
script.type = type
|
||||
}
|
||||
script.onload = () => {
|
||||
resolve()
|
||||
}
|
||||
script.onerror = () => {
|
||||
reject(new Error(`Failed to load script with URL ${scriptUrl}`));
|
||||
}
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
})
|
||||
}
|
||||
87
src/utils/media.js
Normal file
87
src/utils/media.js
Normal file
@ -0,0 +1,87 @@
|
||||
export function formatExif(exifInfo) {
|
||||
if (!exifInfo) {
|
||||
return ''
|
||||
}
|
||||
let exifStr = ''
|
||||
if (exifInfo['dateTimeOriginal']) {
|
||||
exifStr += `内容创作时间: ${exifInfo['dateTimeOriginal']}`
|
||||
}
|
||||
if (exifInfo['resolution']) {
|
||||
exifStr += `\r\n分辨率: ${exifInfo['resolution']}`
|
||||
}
|
||||
if (exifInfo['make']) {
|
||||
exifStr += `\r\n设备制造商: ${exifInfo['make']}`
|
||||
}
|
||||
if (exifInfo['model']) {
|
||||
exifStr += `\r\n设备型号: ${exifInfo['model']}`
|
||||
}
|
||||
if (exifInfo['aperture']) {
|
||||
exifStr += `\r\n光圈值: ${exifInfo['aperture']}`
|
||||
}
|
||||
if (exifInfo['exposureTime']) {
|
||||
exifStr += `\r\n曝光时间: ${exifInfo['exposureTime']}`
|
||||
}
|
||||
if (exifInfo['focalLength']) {
|
||||
exifStr += `\r\n焦距: ${exifInfo['focalLength']} 毫米`
|
||||
}
|
||||
if (exifInfo['isoEquivalent']) {
|
||||
exifStr += `\r\nISO感光度: ${exifInfo['isoEquivalent']}`
|
||||
}
|
||||
if (exifInfo['flash']) {
|
||||
exifStr += `\r\n闪光灯: ${exifInfo['flash']}`
|
||||
}
|
||||
if (exifInfo['fNumber']) {
|
||||
exifStr += `\r\n光圈数: ${exifInfo['fNumber']}`
|
||||
}
|
||||
if (exifInfo['exposureProgram']) {
|
||||
exifStr += `\r\n曝光程序: ${exifInfo['exposureProgram']}`
|
||||
}
|
||||
if (exifInfo['meteringMode']) {
|
||||
exifStr += `\r\n测光模式: ${exifInfo['meteringMode']}`
|
||||
}
|
||||
if (exifInfo['whiteBalanceMode']) {
|
||||
exifStr += `\r\n白平衡: ${exifInfo['whiteBalanceMode']}`
|
||||
}
|
||||
if (exifInfo['software']) {
|
||||
exifStr += `内容创作者: ${exifInfo['software']}`
|
||||
}
|
||||
if (exifInfo['longitude']) {
|
||||
exifStr += `\r\n经度: ${exifInfo['longitude']}`
|
||||
}
|
||||
if (exifInfo['latitude']) {
|
||||
exifStr += `\r\n纬度: ${exifInfo['latitude']}`
|
||||
}
|
||||
return exifStr
|
||||
}
|
||||
|
||||
export function formatVideo(videoInfo) {
|
||||
if (!videoInfo) {
|
||||
return ''
|
||||
}
|
||||
let videoStr = ''
|
||||
if (videoInfo['height'] && videoInfo['width']) {
|
||||
videoStr += `尺寸: ${videoInfo['width']} x ${videoInfo['height']}`
|
||||
}
|
||||
if (videoInfo['bitrate']) {
|
||||
videoStr += `\r\n码率: ${videoInfo['bitrate']}`
|
||||
}
|
||||
if (videoInfo['frameRate']) {
|
||||
videoStr += `\r\n帧率: ${videoInfo['frameRate']} fps`
|
||||
}
|
||||
if (videoInfo['format']) {
|
||||
videoStr += `\r\n格式: ${videoInfo['format']}`
|
||||
}
|
||||
if (videoInfo['duration']) {
|
||||
videoStr += `\r\n时长: ${videoInfo['duration']}`
|
||||
}
|
||||
if (videoInfo['toBitrate']) {
|
||||
videoStr += `\r\n转码后的码率: ${videoInfo['toBitrate']} kbps`
|
||||
}
|
||||
if (videoInfo['toFrameRate']) {
|
||||
videoStr += `\r\n转码后的帧率: ${videoInfo['toFrameRate']} fps`
|
||||
}
|
||||
if (videoInfo['toHeight']) {
|
||||
videoStr += `\r\n转码后的高度: ${videoInfo['toHeight']}`
|
||||
}
|
||||
return videoStr
|
||||
}
|
||||
@ -143,17 +143,18 @@ export function formatSize(size) {
|
||||
/**
|
||||
* formatNetSpeed
|
||||
* @param {*} size
|
||||
* @param space
|
||||
*/
|
||||
export function formatNetSpeed(size) {
|
||||
export function formatNetSpeed(size, space) {
|
||||
if (size === 0) {
|
||||
return '0B/s'
|
||||
return space ? '0 B / s' : '0B/s'
|
||||
} else if (size < 1024) {
|
||||
return size + 'B/s'
|
||||
return size + (space ? ' B / s' : 'B/s')
|
||||
} else if (size < 1024 * 1024) {
|
||||
return (size/1024).toFixed(2) + 'KB/s'
|
||||
return (size/1024).toFixed(2) + (space ? ' KB / s' : 'KB/s')
|
||||
} else if (size < 1024 * 1024 * 1024) {
|
||||
return (size/(1024 * 1024)).toFixed(2) + 'MB/s'
|
||||
return (size/(1024 * 1024)).toFixed(2) + (space ? ' MB / s' : 'MB/s')
|
||||
} else {
|
||||
return (size/(1024 * 1024 * 1024)).toFixed(2) + 'GB/s'
|
||||
return (size/(1024 * 1024 * 1024)).toFixed(2) + (space ? ' GB / s' : 'GB/s')
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
// import { MessageBox, Message } from 'element-ui'
|
||||
import store from '@/store'
|
||||
import _ from "lodash";
|
||||
import {getShareId, getShareToken, getToken, getUsername} from '@/utils/auth'
|
||||
|
||||
// create an axios instance
|
||||
@ -31,12 +32,24 @@ service.interceptors.request.use(
|
||||
}
|
||||
)
|
||||
|
||||
const responseErr = _.throttle((error) => {
|
||||
let msg = '哎呀!服务器出错了,请稍后再试!'
|
||||
if (error.response && error.response.status === 403) {
|
||||
msg = '您的请求被服务器拒绝!'
|
||||
}
|
||||
Vue.prototype.$message({
|
||||
message: msg,
|
||||
type: 'error',
|
||||
duration: 5 * 1000
|
||||
})
|
||||
}, 3000)
|
||||
|
||||
// response interceptor
|
||||
service.interceptors.response.use(
|
||||
/**
|
||||
* If you want to get http information such as headers or status
|
||||
* Please return response => response
|
||||
*/
|
||||
*/
|
||||
|
||||
/**
|
||||
* Determine the request status by custom code
|
||||
@ -56,7 +69,7 @@ service.interceptors.response.use(
|
||||
// to re-login
|
||||
setTimeout(function () {
|
||||
const hasToken = getToken()
|
||||
if(hasToken){
|
||||
if (hasToken) {
|
||||
Vue.prototype.$confirm('登录已失效,请重新登录', '确认登出', {
|
||||
confirmButtonText: '重新登录',
|
||||
cancelButtonText: '取消',
|
||||
@ -67,7 +80,7 @@ service.interceptors.response.use(
|
||||
})
|
||||
})
|
||||
}
|
||||
},200)
|
||||
}, 200)
|
||||
} else {
|
||||
Vue.prototype.$message({
|
||||
message: res.message || '服务器开小差了...',
|
||||
@ -78,10 +91,10 @@ service.interceptors.response.use(
|
||||
return Promise.reject()
|
||||
} else {
|
||||
let message = ''
|
||||
if(res.message){
|
||||
if (res.message) {
|
||||
message = res.message.toString()
|
||||
}
|
||||
if(message !== 'true'){
|
||||
if (message !== 'true') {
|
||||
Vue.prototype.$message({
|
||||
message: res.message,
|
||||
type: 'success',
|
||||
@ -92,15 +105,7 @@ service.interceptors.response.use(
|
||||
}
|
||||
},
|
||||
error => {
|
||||
let msg = '哎呀!服务器出错了,请稍后再试!'
|
||||
if (error.response && error.response.status === 403) {
|
||||
msg = '您的请求被服务器拒绝!'
|
||||
}
|
||||
Vue.prototype.$message({
|
||||
message: msg,
|
||||
type: 'error',
|
||||
duration: 5 * 1000
|
||||
})
|
||||
responseErr(error)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
:defaultSort="sortable"
|
||||
:queryCondition="queryCondition"
|
||||
:contextMenus="contextMenus"
|
||||
homeHidden
|
||||
>
|
||||
</show-file>
|
||||
</div>
|
||||
|
||||
@ -8,6 +8,8 @@
|
||||
:queryCondition="queryCondition"
|
||||
singleFileType="音乐"
|
||||
emptyStatus="还没有音乐呢~"
|
||||
:showUploadButton="false"
|
||||
:contextMenus="contextMenus"
|
||||
></show-file>
|
||||
</div>
|
||||
</template>
|
||||
@ -24,7 +26,36 @@ export default {
|
||||
},
|
||||
queryCondition: {
|
||||
isFolder:false
|
||||
}
|
||||
},
|
||||
contextMenus: [
|
||||
{ label: '查看', operation: 'viewMode' ,child: [
|
||||
{ iconClass: this.grid?'':'menu-point', label: '列表', operation: 'vmode-list'},
|
||||
{ iconClass: this.grid?'menu-point':'', label: '缩略图', operation: 'vmode-grid'},
|
||||
{ label: '显示文件夹大小', operation: 'show-folder-size'},
|
||||
]
|
||||
},
|
||||
{ label: '排列方式', operation: 'arrangement' ,child: [
|
||||
{ label: '名称', operation: 'orderName', orderProp: 'name'},
|
||||
{ label: '大小', operation: 'orderSize', orderProp: 'size'},
|
||||
{ label: '日期', operation: 'orderUpdateDate', orderProp: 'updateDate'},
|
||||
]
|
||||
},
|
||||
{ label: '刷新', operation: 'refresh'},
|
||||
{ divider: true, operation: 'divider' },
|
||||
{ label: '新建', operation: 'create' , homeDisable: true, child: [
|
||||
{ label: '文件夹', operation: 'createFolder', iconClass: 'folder'},
|
||||
{ divider: true, operation: 'divider' },
|
||||
{ label: '文本', operation: 'createTextFile', iconClass: 'file-txt'},
|
||||
{ label: '思维导图', operation: 'createMinderFile', iconClass: 'file-mind'},
|
||||
{ label: '流程图', operation: 'createDrawioFile', iconClass: 'file-drawio'},
|
||||
{ label: 'Word', operation: 'createWordFile', iconClass: 'file-word'},
|
||||
{ label: 'Excel', operation: 'createExcelFile', iconClass: 'file-excel'},
|
||||
{ label: 'PPT', operation: 'createPPTFile', iconClass: 'file-ppt'},
|
||||
]
|
||||
},
|
||||
{ divider: true, operation: 'divider' },
|
||||
{ label: '写文章', operation: 'createMarkdownFile'},
|
||||
]
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
:sortable="sortable"
|
||||
:queryCondition="queryCondition"
|
||||
emptyStatus="还没有文档呢~"
|
||||
:showUploadButton="false"
|
||||
:contextMenus="contextMenus"
|
||||
>
|
||||
</show-file>
|
||||
</div>
|
||||
@ -24,7 +26,36 @@ export default {
|
||||
},
|
||||
queryCondition: {
|
||||
isFolder: false
|
||||
}
|
||||
},
|
||||
contextMenus: [
|
||||
{ label: '查看', operation: 'viewMode' ,child: [
|
||||
{ iconClass: this.grid?'':'menu-point', label: '列表', operation: 'vmode-list'},
|
||||
{ iconClass: this.grid?'menu-point':'', label: '缩略图', operation: 'vmode-grid'},
|
||||
{ label: '显示文件夹大小', operation: 'show-folder-size'},
|
||||
]
|
||||
},
|
||||
{ label: '排列方式', operation: 'arrangement' ,child: [
|
||||
{ label: '名称', operation: 'orderName', orderProp: 'name'},
|
||||
{ label: '大小', operation: 'orderSize', orderProp: 'size'},
|
||||
{ label: '日期', operation: 'orderUpdateDate', orderProp: 'updateDate'},
|
||||
]
|
||||
},
|
||||
{ label: '刷新', operation: 'refresh'},
|
||||
{ divider: true, operation: 'divider' },
|
||||
{ label: '新建', operation: 'create' , homeDisable: true, child: [
|
||||
{ label: '文件夹', operation: 'createFolder', iconClass: 'folder'},
|
||||
{ divider: true, operation: 'divider' },
|
||||
{ label: '文本', operation: 'createTextFile', iconClass: 'file-txt'},
|
||||
{ label: '思维导图', operation: 'createMinderFile', iconClass: 'file-mind'},
|
||||
{ label: '流程图', operation: 'createDrawioFile', iconClass: 'file-drawio'},
|
||||
{ label: 'Word', operation: 'createWordFile', iconClass: 'file-word'},
|
||||
{ label: 'Excel', operation: 'createExcelFile', iconClass: 'file-excel'},
|
||||
{ label: 'PPT', operation: 'createPPTFile', iconClass: 'file-ppt'},
|
||||
]
|
||||
},
|
||||
{ divider: true, operation: 'divider' },
|
||||
{ label: '写文章', operation: 'createMarkdownFile'},
|
||||
]
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@ -6,6 +6,8 @@
|
||||
:sortable="sortable"
|
||||
:queryCondition="queryCondition"
|
||||
emptyStatus="还没有图片呢~"
|
||||
:showUploadButton="false"
|
||||
:contextMenus="contextMenus"
|
||||
>
|
||||
</show-file>
|
||||
</div>
|
||||
@ -23,7 +25,36 @@ export default {
|
||||
},
|
||||
queryCondition: {
|
||||
isFolder: false
|
||||
}
|
||||
},
|
||||
contextMenus: [
|
||||
{ label: '查看', operation: 'viewMode' ,child: [
|
||||
{ iconClass: this.grid?'':'menu-point', label: '列表', operation: 'vmode-list'},
|
||||
{ iconClass: this.grid?'menu-point':'', label: '缩略图', operation: 'vmode-grid'},
|
||||
{ label: '显示文件夹大小', operation: 'show-folder-size'},
|
||||
]
|
||||
},
|
||||
{ label: '排列方式', operation: 'arrangement' ,child: [
|
||||
{ label: '名称', operation: 'orderName', orderProp: 'name'},
|
||||
{ label: '大小', operation: 'orderSize', orderProp: 'size'},
|
||||
{ label: '日期', operation: 'orderUpdateDate', orderProp: 'updateDate'},
|
||||
]
|
||||
},
|
||||
{ label: '刷新', operation: 'refresh'},
|
||||
{ divider: true, operation: 'divider' },
|
||||
{ label: '新建', operation: 'create' , homeDisable: true, child: [
|
||||
{ label: '文件夹', operation: 'createFolder', iconClass: 'folder'},
|
||||
{ divider: true, operation: 'divider' },
|
||||
{ label: '文本', operation: 'createTextFile', iconClass: 'file-txt'},
|
||||
{ label: '思维导图', operation: 'createMinderFile', iconClass: 'file-mind'},
|
||||
{ label: '流程图', operation: 'createDrawioFile', iconClass: 'file-drawio'},
|
||||
{ label: 'Word', operation: 'createWordFile', iconClass: 'file-word'},
|
||||
{ label: 'Excel', operation: 'createExcelFile', iconClass: 'file-excel'},
|
||||
{ label: 'PPT', operation: 'createPPTFile', iconClass: 'file-ppt'},
|
||||
]
|
||||
},
|
||||
{ divider: true, operation: 'divider' },
|
||||
{ label: '写文章', operation: 'createMarkdownFile'},
|
||||
]
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
:sortable="sortable"
|
||||
:queryCondition="queryCondition"
|
||||
emptyStatus="还没有视频呢~"
|
||||
:showUploadButton="false"
|
||||
:contextMenus="contextMenus"
|
||||
>
|
||||
</show-file>
|
||||
</div>
|
||||
@ -24,7 +26,36 @@ export default {
|
||||
},
|
||||
queryCondition: {
|
||||
isFolder: false
|
||||
}
|
||||
},
|
||||
contextMenus: [
|
||||
{ label: '查看', operation: 'viewMode' ,child: [
|
||||
{ iconClass: this.grid?'':'menu-point', label: '列表', operation: 'vmode-list'},
|
||||
{ iconClass: this.grid?'menu-point':'', label: '缩略图', operation: 'vmode-grid'},
|
||||
{ label: '显示文件夹大小', operation: 'show-folder-size'},
|
||||
]
|
||||
},
|
||||
{ label: '排列方式', operation: 'arrangement' ,child: [
|
||||
{ label: '名称', operation: 'orderName', orderProp: 'name'},
|
||||
{ label: '大小', operation: 'orderSize', orderProp: 'size'},
|
||||
{ label: '日期', operation: 'orderUpdateDate', orderProp: 'updateDate'},
|
||||
]
|
||||
},
|
||||
{ label: '刷新', operation: 'refresh'},
|
||||
{ divider: true, operation: 'divider' },
|
||||
{ label: '新建', operation: 'create' , homeDisable: true, child: [
|
||||
{ label: '文件夹', operation: 'createFolder', iconClass: 'folder'},
|
||||
{ divider: true, operation: 'divider' },
|
||||
{ label: '文本', operation: 'createTextFile', iconClass: 'file-txt'},
|
||||
{ label: '思维导图', operation: 'createMinderFile', iconClass: 'file-mind'},
|
||||
{ label: '流程图', operation: 'createDrawioFile', iconClass: 'file-drawio'},
|
||||
{ label: 'Word', operation: 'createWordFile', iconClass: 'file-word'},
|
||||
{ label: 'Excel', operation: 'createExcelFile', iconClass: 'file-excel'},
|
||||
{ label: 'PPT', operation: 'createPPTFile', iconClass: 'file-ppt'},
|
||||
]
|
||||
},
|
||||
{ divider: true, operation: 'divider' },
|
||||
{ label: '写文章', operation: 'createMarkdownFile'},
|
||||
]
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@ -268,7 +268,7 @@
|
||||
<sim-text-preview :file.sync="textPreviewRow" :status.sync="textPreviewVisible"></sim-text-preview>
|
||||
<image-viewer :fileList="fileList" :file="imagePreviewRow" :status.sync="imagePreviewVisible"></image-viewer>
|
||||
<video-preview :file="videoPreviewRow" :status.sync="videoPreviewVisible"></video-preview>
|
||||
<office-preview :file="officePreviewRow" :status.sync="officePreviewVisible"></office-preview>
|
||||
<iframe-preview :file="officePreviewRow" :status.sync="officePreviewVisible"></iframe-preview>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
@ -283,14 +283,12 @@ import ImageViewer from "@/components/preview/ImageViewer";
|
||||
import VideoPreview from "@/components/preview/VideoPreview";
|
||||
import SimTextPreview from "@/components/preview/SimTextPreview";
|
||||
import {suffix} from "@/utils/file-type";
|
||||
import OfficePreview from "@/components/preview/OfficePreview";
|
||||
import IframePreview from "@/components/preview/IframePreview.vue";
|
||||
import Clipboard from "clipboard";
|
||||
|
||||
let pinyin = require("pinyin");
|
||||
|
||||
export default {
|
||||
components: {
|
||||
OfficePreview,
|
||||
IframePreview,
|
||||
SimTextPreview,
|
||||
VideoPreview,
|
||||
ImageViewer, IconFile,
|
||||
@ -409,11 +407,7 @@ export default {
|
||||
// 加载布局
|
||||
if (this.$route.query.vmode) {
|
||||
this.vmode = this.$route.query.vmode
|
||||
if (this.vmode === 'list') {
|
||||
this.grid = false
|
||||
} else {
|
||||
this.grid = true
|
||||
}
|
||||
this.grid = this.vmode !== 'list';
|
||||
}
|
||||
// 加载url上的path
|
||||
if (this.$route.query.path !== '/') {
|
||||
@ -676,7 +670,8 @@ export default {
|
||||
api.delete({
|
||||
currentDirectory: this.rowContextData.path,
|
||||
username: this.$store.state.user.name,
|
||||
fileIds: fileIds
|
||||
fileIds: fileIds,
|
||||
sweep: true
|
||||
}).then(() => {
|
||||
// 刷新列表
|
||||
this.getFileList()
|
||||
@ -929,7 +924,6 @@ export default {
|
||||
this.searchFile(this.searchValue, onLoad)
|
||||
}
|
||||
} else {
|
||||
this.beforeLoadData(onLoad)
|
||||
api.fileList({
|
||||
username: this.$store.state.user.name,
|
||||
userId: this.$store.state.user.userId,
|
||||
|
||||
@ -209,9 +209,11 @@ export default {
|
||||
let data = new FormData()
|
||||
data.append('username',this.loginForm.username)
|
||||
data.append('password',this.loginForm.password)
|
||||
this.loading = true
|
||||
initialization(data).then(()=>{
|
||||
this.initialize = false
|
||||
this.$message.success('创建成功')
|
||||
this.loading = false
|
||||
})
|
||||
}else{
|
||||
// 登录
|
||||
@ -330,7 +332,7 @@ $light_gray:#eee;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
height: calc(100vh - 6px);
|
||||
}
|
||||
|
||||
.box-card {
|
||||
|
||||
@ -223,7 +223,7 @@
|
||||
</van-grid>
|
||||
</van-checkbox-group>
|
||||
</div>
|
||||
<div v-if="linkFailed && !showShareCode" class="share-header">
|
||||
<div v-if="linkFailed && !showShareCode" class="share-header-prompt">
|
||||
<p v-if="prompt !== ''">温馨提示:</p>
|
||||
<p>{{ prompt }}</p>
|
||||
</div>
|
||||
@ -255,7 +255,7 @@
|
||||
<image-viewer :fileList="fileList" :shareId="shareId" :file="imagePreviewRow"
|
||||
:status.sync="imagePreviewVisible"></image-viewer>
|
||||
<video-preview :file="videoPreviewRow" :shareId="shareId" :status.sync="videoPreviewVisible"></video-preview>
|
||||
<office-preview :file="officePreviewRow" :shareId="shareId" :status.sync="officePreviewVisible"></office-preview>
|
||||
<iframe-preview :file="officePreviewRow" :shareId="shareId" :sharer="sharerUsername" :status.sync="officePreviewVisible"></iframe-preview>
|
||||
<el-divider v-if="!linkFailed && !showShareCode" class="grid-divider" content-position="center"><i
|
||||
class="el-icon-folder-opened"></i> {{ summaries }}
|
||||
</el-divider>
|
||||
@ -290,7 +290,7 @@ import VideoPreview from "@/components/preview/VideoPreview";
|
||||
import AudioPreview from "@/components/preview/AudioPreview";
|
||||
import fileConfig from "@/utils/file-config";
|
||||
import Clipboard from "clipboard";
|
||||
import OfficePreview from "@/components/preview/OfficePreview";
|
||||
import IframePreview from "@/components/preview/IframePreview.vue";
|
||||
import Logo from "@/components/Logo";
|
||||
import store from "@/store";
|
||||
import FileTree from "@/components/FileTree";
|
||||
@ -299,7 +299,7 @@ export default {
|
||||
components: {
|
||||
FileTree,
|
||||
Logo,
|
||||
OfficePreview, IconFile, BreadcrumbFilePath, AlLoading,
|
||||
IframePreview, IconFile, BreadcrumbFilePath, AlLoading,
|
||||
AudioPreview, VideoPreview, ImageViewer, SimTextPreview
|
||||
},
|
||||
data() {
|
||||
@ -401,7 +401,8 @@ export default {
|
||||
shareLink: '',
|
||||
shareFileName: '',
|
||||
generateShareLinkLoading: true,
|
||||
shareId: this.$route.query.s,
|
||||
shareId: '',
|
||||
shortId: this.$route.params.id,
|
||||
currentDirName: '',
|
||||
linkFailed: true,
|
||||
textPreviewVisible: false,
|
||||
@ -417,6 +418,7 @@ export default {
|
||||
showUpdateDateItem: this.$pc,// 列表模式下是否显示修改时间
|
||||
showSizeItem: this.$pc,// 列表模式下是否显示文件大小
|
||||
sharer: undefined,//分享者信息,
|
||||
sharerUsername: undefined,
|
||||
loginTitle: '',
|
||||
sharerAvatarUrl: '',
|
||||
netdiskName: 'JmalCloud',
|
||||
@ -584,7 +586,7 @@ export default {
|
||||
if (index === 0) {
|
||||
this.pathList.splice(this.pathList.findIndex((v, i) => i === index + 1), this.pathList.length - (index + 1))
|
||||
this.getFileList(null, true);
|
||||
this.$router.push(`/s?s=${this.shareId}&vmode=${this.vmode}`)
|
||||
this.pushRouter()
|
||||
}
|
||||
if (item && item.fileId) {
|
||||
this.accessShareOpenDir(item.fileId)
|
||||
@ -598,8 +600,7 @@ export default {
|
||||
if (this.grid) {
|
||||
this.vmode = 'grid'
|
||||
}
|
||||
let f = ''
|
||||
this.$router.push(`/s?s=${this.shareId}&vmode=${this.vmode}`)
|
||||
this.pushRouter()
|
||||
},
|
||||
accessShareOpenDir(fileId) {
|
||||
this.tableLoading = true
|
||||
@ -607,7 +608,8 @@ export default {
|
||||
share: this.shareId,
|
||||
fileId: fileId,
|
||||
pageIndex: this.pagination.pageIndex,
|
||||
pageSize: this.pagination.pageSize
|
||||
pageSize: this.pagination.pageSize,
|
||||
showFolderSize: true
|
||||
}).then(res => {
|
||||
this.isLoading = false
|
||||
this.fileList = res.data
|
||||
@ -626,9 +628,10 @@ export default {
|
||||
getFileList(pagination, overload) {
|
||||
this.tableLoading = true
|
||||
api.accessShare({
|
||||
share: this.shareId,
|
||||
share: this.shortId,
|
||||
pageIndex: this.pagination.pageIndex,
|
||||
pageSize: this.pagination.pageSize
|
||||
pageSize: this.pagination.pageSize,
|
||||
showFolderSize: true
|
||||
}).then(res => {
|
||||
this.isLoading = false
|
||||
if (Object.getPrototypeOf(res.data) === String.prototype) {
|
||||
@ -687,14 +690,16 @@ export default {
|
||||
})
|
||||
},
|
||||
getSharer() {
|
||||
api.getSharer({shareId: this.shareId}).then(res => {
|
||||
api.getSharer({shareId: this.shortId}).then(res => {
|
||||
this.sharer = res.data
|
||||
this.setLoginTitle()
|
||||
if (res.data) {
|
||||
this.shareId = res.data.shareId
|
||||
this.sharerAvatarUrl = window.location.origin + this.imageUrl + res.data.avatar
|
||||
if (this.sharer.netdiskName) {
|
||||
this.netdiskName = this.sharer.netdiskName
|
||||
}
|
||||
this.sharerUsername = this.sharer.username
|
||||
if (this.sharer.netdiskLogo) {
|
||||
this.netdiskLogo = this.sharer.netdiskLogo
|
||||
}
|
||||
@ -1044,6 +1049,9 @@ export default {
|
||||
clipboard.destroy()
|
||||
})
|
||||
},
|
||||
pushRouter() {
|
||||
this.$router.push(`/s/${this.shortId}/?vmode=${this.vmode}`)
|
||||
},
|
||||
// 点击文件或文件夹
|
||||
fileClick(row) {
|
||||
window.shareId = this.shareId
|
||||
@ -1055,7 +1063,7 @@ export default {
|
||||
item['index'] = this.pathList.length
|
||||
this.pathList.push(item)
|
||||
this.pagination.pageIndex = 1
|
||||
this.$router.push(`?s=${this.shareId}&vmode=${this.vmode}`)
|
||||
this.pushRouter()
|
||||
this.accessShareOpenDir(row.id)
|
||||
} else {
|
||||
if (row.contentType.startsWith('image')) {
|
||||
@ -1131,6 +1139,13 @@ export default {
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
.share-header-prompt {
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.share-header {
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
|
||||
@ -6,6 +6,8 @@
|
||||
:sortable="sortable"
|
||||
:queryCondition="queryCondition"
|
||||
:showNavigation="true"
|
||||
:contextMenus="contextMenus"
|
||||
:showUploadButton="false"
|
||||
></show-file>
|
||||
</div>
|
||||
</template>
|
||||
@ -22,7 +24,36 @@
|
||||
},
|
||||
queryCondition: {
|
||||
isFolder:false
|
||||
}
|
||||
},
|
||||
contextMenus: [
|
||||
{ label: '查看', operation: 'viewMode' ,child: [
|
||||
{ iconClass: this.grid?'':'menu-point', label: '列表', operation: 'vmode-list'},
|
||||
{ iconClass: this.grid?'menu-point':'', label: '缩略图', operation: 'vmode-grid'},
|
||||
{ label: '显示文件夹大小', operation: 'show-folder-size'},
|
||||
]
|
||||
},
|
||||
{ label: '排列方式', operation: 'arrangement' ,child: [
|
||||
{ label: '名称', operation: 'orderName', orderProp: 'name'},
|
||||
{ label: '大小', operation: 'orderSize', orderProp: 'size'},
|
||||
{ label: '日期', operation: 'orderUpdateDate', orderProp: 'updateDate'},
|
||||
]
|
||||
},
|
||||
{ label: '刷新', operation: 'refresh'},
|
||||
{ divider: true, operation: 'divider' },
|
||||
{ label: '新建', operation: 'create' , homeDisable: true, child: [
|
||||
{ label: '文件夹', operation: 'createFolder', iconClass: 'folder'},
|
||||
{ divider: true, operation: 'divider' },
|
||||
{ label: '文本', operation: 'createTextFile', iconClass: 'file-txt'},
|
||||
{ label: '思维导图', operation: 'createMinderFile', iconClass: 'file-mind'},
|
||||
{ label: '流程图', operation: 'createDrawioFile', iconClass: 'file-drawio'},
|
||||
{ label: 'Word', operation: 'createWordFile', iconClass: 'file-word'},
|
||||
{ label: 'Excel', operation: 'createExcelFile', iconClass: 'file-excel'},
|
||||
{ label: 'PPT', operation: 'createPPTFile', iconClass: 'file-ppt'},
|
||||
]
|
||||
},
|
||||
{ divider: true, operation: 'divider' },
|
||||
{ label: '写文章', operation: 'createMarkdownFile'},
|
||||
]
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@ -300,16 +300,11 @@ export default {
|
||||
api.delete({
|
||||
currentDirectory: '',
|
||||
username: this.$store.state.user.name,
|
||||
fileIds: fileIds
|
||||
fileIds: fileIds,
|
||||
sweep: true
|
||||
}).then(() => {
|
||||
// 移除列表
|
||||
this.getArticleList()
|
||||
}).then(() => {
|
||||
this.$notify({
|
||||
title: '删除成功',
|
||||
type: 'success',
|
||||
duration: 1000
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
@ -37,8 +37,10 @@
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="token-title-desc">您可以使用这些以生成的令牌来操作 JmalCloud API。</div>
|
||||
<el-divider></el-divider>
|
||||
<div class="setting-title-desc">
|
||||
<div class="setting-title-desc-text">您可以使用这些以生成的令牌来操作 JmalCloud API。</div>
|
||||
<el-divider></el-divider>
|
||||
</div>
|
||||
<div>
|
||||
<ul v-if="accessTokenList.length > 0">
|
||||
<li v-for="token in accessTokenList" :key="token.id">
|
||||
@ -144,9 +146,6 @@ export default {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "src/styles/setting";
|
||||
>>>.el-divider--horizontal {
|
||||
margin: 0;
|
||||
}
|
||||
>>>.el-alert {
|
||||
margin-bottom: 15px;
|
||||
.el-alert__content {
|
||||
@ -156,9 +155,6 @@ export default {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
.token-title-desc {
|
||||
padding: 5px 0 20px 0;
|
||||
}
|
||||
.token-item {
|
||||
display: flex;
|
||||
height: 80px;
|
||||
|
||||
@ -44,12 +44,11 @@
|
||||
<a href="https://baike.baidu.com/item/webp%E6%A0%BC%E5%BC%8F" target="_blank">什么是webp?</a>
|
||||
</span>
|
||||
<div class="config-itme-label">重建索引:
|
||||
<el-button class="sync-button" size="mini" :loading="syncLoading" type="primary" @click="sync()"><i
|
||||
class="el-icon-refresh"></i>
|
||||
</el-button>
|
||||
<span v-show="syncPercent < 100">{{ syncPercent }}%</span>
|
||||
<el-button class="sync-button" size="mini" :loading="syncLoading" type="primary" @click="sync()"><i class="el-icon-refresh"></i></el-button>
|
||||
<span v-show="syncPercent < 100">正在同步文件基本信息: {{ syncPercent }}%</span>
|
||||
<span v-show="indexingPercent > 0 && indexingPercent < 100">正在为文件内容创建索引: </span><span v-show="indexingPercent > 0 && indexingPercent < 100">{{ indexingPercent }}%</span>
|
||||
</div>
|
||||
<span class="instruction">一般用于初始化操作, 将文件数据同步到数据库</span>
|
||||
<span class="instruction">重建索引分为两步: 1.同步文件基本信息, 2.为文件内容创建索引</span>
|
||||
<div class="config-itme-label">重置角色、菜单:
|
||||
<el-button class="sync-button" size="mini" :loading="resetLoading" type="danger"
|
||||
@click="resetMenuAndRole()">
|
||||
@ -66,60 +65,24 @@
|
||||
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="LDAP认证" name="2" class="setting-tab-panel">
|
||||
<el-tab-pane label="OnlyOffice" name="2" class="setting-tab-panel">
|
||||
<div v-if="activeName === '2'">
|
||||
<el-form :rules="rules" ref="form" :model="ldapFormData" label-width="120px" size="small"
|
||||
style="width: 450px" autocomplete="off">
|
||||
<el-form-item label="功能状态" prop="enable">
|
||||
<el-select v-model="ldapFormData.enable">
|
||||
<el-option
|
||||
v-for="item in ldapStatusOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="LDAP 服务器" prop="ldapServer">
|
||||
<el-input placeholder="例如: ldap.test.com:389" v-model="ldapFormData.ldapServer"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="Base DN" prop="baseDN">
|
||||
<el-input placeholder="例如: dc=test,dc=com" v-model="ldapFormData.baseDN" autocomplete="off"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="账号" prop="userDN">
|
||||
<el-input placeholder="管理员账号,例如: cn=admin,dc=test,dc=com" v-model="ldapFormData.userDN" autocomplete="off"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="密码" prop="password">
|
||||
<el-input type="password" placeholder="管理员密码" v-model="ldapFormData.password" autocomplete="off"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" :disabled="ldapTestBtn" v-loading="testLdapConfigLoading" @click="doTestLdapConfig">测试链接</el-button>
|
||||
<span v-if="testLdapConfigResult === 0" class="el-icon-check" style="color: #67C23A"></span>
|
||||
<span v-if="testLdapConfigResult > 0" class="el-icon-close" style="color: #F56C6C"></span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="登录名" prop="loginName">
|
||||
<el-input placeholder="LDAP服务器中对应个人用户名的字段, 例如: uid" v-model="ldapFormData.loginName"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="默认角色" prop="defaultRoleList">
|
||||
<el-select ref="selectRole" v-model="ldapFormData.defaultRoleList" multiple placeholder="请选择角色">
|
||||
<el-option
|
||||
v-for="item in roleList"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" :disabled="ldapSaveBtn" v-loading="saveLdapConfigLoading" @click="saveLdapConfig">保存配置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<office-config></office-config>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="视频转码" name="3" class="setting-tab-panel">
|
||||
<div v-if="activeName === '3'">
|
||||
<transcode-config></transcode-config>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="预览配置" name="4" class="setting-tab-panel">
|
||||
<div v-if="activeName === '4'">
|
||||
<preview-config></preview-config>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="LDAP认证" name="5" class="setting-tab-panel">
|
||||
<div v-if="activeName === '5'">
|
||||
<ldap-config></ldap-config>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
@ -131,23 +94,28 @@
|
||||
<script>
|
||||
|
||||
import config from '@/../package.json'
|
||||
import settingApi, {getSetting} from "@/api/setting-api";
|
||||
import settingApi, { getSetting } from "@/api/setting-api";
|
||||
import Logo from "@/components/Logo";
|
||||
import getPageTitle from "@/utils/get-page-title";
|
||||
import UploadImageInput from "@/components/input/UploadImageInput.vue";
|
||||
import roleApi from "@/api/role";
|
||||
import { loadLdapConfig, testLdapConfig, updateLdapConfig} from '@/api/user'
|
||||
import PreviewConfig from '@/views/setting/cloudSetting/previewConfig.vue'
|
||||
import {mapGetters, mapState} from "vuex";
|
||||
import store from "@/store";
|
||||
import TaskProgress from "@/components/TaskProgress/index.vue";
|
||||
import LdapConfig from "@/views/setting/cloudSetting/ldapConfig.vue";
|
||||
import TranscodeConfig from "@/views/setting/cloudSetting/transcodeConfig.vue";
|
||||
import OfficeConfig from "@/views/setting/cloudSetting/officeConfig.vue";
|
||||
|
||||
export default {
|
||||
components: {UploadImageInput, Logo},
|
||||
components: { PreviewConfig, OfficeConfig, TranscodeConfig, LdapConfig, TaskProgress, UploadImageInput, Logo},
|
||||
data() {
|
||||
return {
|
||||
activeName: '1',
|
||||
uploadUrl: `${process.env.VUE_APP_BASE_API}/user/setting/upload_logo?jmal-token=${this.$store.state.user.token}&name=${this.$store.state.user.name}`,
|
||||
title: '网盘管理',
|
||||
syncLoading: false,
|
||||
clickSync: false,
|
||||
indexingPercent: 100,
|
||||
syncPercent: 100,
|
||||
resetLoading: false,
|
||||
webpEnabled: false,
|
||||
@ -156,44 +124,7 @@ export default {
|
||||
showAckBtn: false,
|
||||
inputNetdiskNameWidth: 150,
|
||||
logoFileTypeList: ['image/svg+xml', 'image/jpg', 'image/png', 'image/jpeg'],
|
||||
ldapFormData: {
|
||||
enable: true,
|
||||
ldapServer: '',
|
||||
defaultRoleList: [],
|
||||
baseDN: '',
|
||||
userDN: '',
|
||||
password: '',
|
||||
loginName: ''
|
||||
},
|
||||
currentVersion: config.version,
|
||||
roleList: [],
|
||||
ldapStatusOptions: [{
|
||||
value: true,
|
||||
label: '启用'
|
||||
}, {
|
||||
value: false,
|
||||
label: '禁用'
|
||||
}],
|
||||
rules: {
|
||||
ldapServer: [
|
||||
{required: true, message: '请填写LDAP服务器', trigger: 'submit'}
|
||||
],
|
||||
defaultRoleList: [
|
||||
{required: true, message: '请选择默认角色', trigger: 'submit'}
|
||||
],
|
||||
baseDN: [
|
||||
{required: true, message: '请填写 Base DN', trigger: 'submit'}
|
||||
],
|
||||
userDN: [
|
||||
{required: true, message: '请填写 管理员账号', trigger: 'submit'}
|
||||
],
|
||||
loginName: [
|
||||
{required: true, message: '请填写登录名', trigger: 'submit'}
|
||||
]
|
||||
},
|
||||
testLdapConfigLoading: false,
|
||||
saveLdapConfigLoading: false,
|
||||
testLdapConfigResult: -1,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@ -203,8 +134,6 @@ export default {
|
||||
if (this.$route.query.tab) {
|
||||
this.activeName = this.$route.query.tab
|
||||
if (this.activeName === '2') {
|
||||
this.getRoleList()
|
||||
this.getLdapConfig()
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -213,50 +142,17 @@ export default {
|
||||
...mapGetters([
|
||||
'newVersion'
|
||||
]),
|
||||
ldapTestBtn() {
|
||||
return !(this.ldapFormData.ldapServer.length > 0 && this.ldapFormData.baseDN.length > 0 && (this.ldapFormData.password ? this.ldapFormData.password.length > 0 : false))
|
||||
},
|
||||
ldapSaveBtn() {
|
||||
return !(!this.ldapTestBtn && this.ldapFormData.defaultRoleList.length > 0 && this.ldapFormData.loginName.length > 0)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
message(msg) {
|
||||
if (msg.event === 'msg/synced') {
|
||||
this.syncPercent = msg.data.body
|
||||
if (this.syncPercent >= 100) {
|
||||
this.syncLoading = false
|
||||
}
|
||||
this.updateSyncStatus(msg.data.body)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
doTestLdapConfig() {
|
||||
this.testLdapConfigLoading = true
|
||||
testLdapConfig(this.ldapFormData).then(() => {
|
||||
this.testLdapConfigLoading = false
|
||||
this.testLdapConfigResult = 0
|
||||
this.$message.success("连接成功")
|
||||
}).catch(() => {
|
||||
this.testLdapConfigResult = 1
|
||||
this.testLdapConfigLoading = false
|
||||
});
|
||||
},
|
||||
saveLdapConfig() {
|
||||
this.saveLdapConfigLoading = true
|
||||
updateLdapConfig(this.ldapFormData).then(() => {
|
||||
this.saveLdapConfigLoading = false
|
||||
this.$message.success("保存成功")
|
||||
}).catch(() => {
|
||||
this.saveLdapConfigLoading = false
|
||||
});
|
||||
},
|
||||
handleClick(tab) {
|
||||
this.$router.push({query: {tab: tab.name}})
|
||||
if (tab.name === '2') {
|
||||
this.getRoleList()
|
||||
this.getLdapConfig()
|
||||
}
|
||||
},
|
||||
handleAvatarSuccess(res) {
|
||||
if (res.code === 0) {
|
||||
@ -295,18 +191,6 @@ export default {
|
||||
}
|
||||
})
|
||||
},
|
||||
getRoleList() {
|
||||
roleApi.roleList().then(res => {
|
||||
this.roleList = res.data;
|
||||
})
|
||||
},
|
||||
getLdapConfig() {
|
||||
loadLdapConfig().then(res => {
|
||||
if (res.data) {
|
||||
this.ldapFormData = res.data;
|
||||
}
|
||||
})
|
||||
},
|
||||
inputNetdiskName(input) {
|
||||
if (input) {
|
||||
this.showAckBtn = true
|
||||
@ -350,21 +234,36 @@ export default {
|
||||
},
|
||||
getIsSync() {
|
||||
settingApi.isSync({username: this.$store.state.user.name}).then((res) => {
|
||||
this.syncLoading = true;
|
||||
this.syncPercent = res.data
|
||||
this.syncLoading = this.syncPercent < 100;
|
||||
this.updateSyncStatus(res.data)
|
||||
})
|
||||
},
|
||||
updateSyncStatus(dataPercent) {
|
||||
const {syncPercent, indexingPercent} = dataPercent
|
||||
if (syncPercent) {
|
||||
this.syncPercent = syncPercent
|
||||
} else {
|
||||
this.syncPercent = 0
|
||||
}
|
||||
if (indexingPercent) {
|
||||
this.indexingPercent = indexingPercent
|
||||
} else {
|
||||
this.indexingPercent = 0
|
||||
}
|
||||
this.syncLoading = !((this.syncPercent === 100 && this.indexingPercent === 100) || (this.syncPercent === 0 && this.indexingPercent === 0));
|
||||
},
|
||||
sync() {
|
||||
this.$confirm('是否开始同步? ', '提示', {
|
||||
this.$confirm('是否开始扫描? ', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.syncLoading = true
|
||||
this.syncPercent = 0
|
||||
settingApi.sync({username: this.$store.state.user.name}).then(() => {
|
||||
this.$message.success("开始同步...")
|
||||
this.clickSync = true
|
||||
settingApi.syncCloud({username: this.$store.state.user.name}).then(() => {
|
||||
this.clickSync = false
|
||||
}).catch(() => {
|
||||
this.clickSync = false
|
||||
})
|
||||
})
|
||||
},
|
||||
@ -392,7 +291,7 @@ export default {
|
||||
}).catch(() => {
|
||||
this.webpEnabled = !this.webpEnabled
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -400,7 +299,7 @@ export default {
|
||||
<style lang="scss" scoped>
|
||||
@import "src/styles/setting";
|
||||
|
||||
> > > .el-textarea {
|
||||
>>> .el-textarea {
|
||||
max-width: 1080px;
|
||||
}
|
||||
|
||||
@ -413,7 +312,7 @@ export default {
|
||||
width: 220px;
|
||||
}
|
||||
|
||||
.avatar-uploader > > > .el-upload {
|
||||
.avatar-uploader >>> .el-upload {
|
||||
display: block;
|
||||
|
||||
.avatar {
|
||||
@ -432,11 +331,11 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
> > > .el-card__body {
|
||||
>>> .el-card__body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
> > > .el-tabs__header {
|
||||
>>> .el-tabs__header {
|
||||
margin: 0;
|
||||
@media screen and (min-width: 768px) {
|
||||
.el-tabs__nav {
|
||||
@ -448,7 +347,7 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
> > > .el-form-item__content {
|
||||
>>> .el-form-item__content {
|
||||
.el-select {
|
||||
width: 100%;
|
||||
}
|
||||
@ -462,4 +361,11 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
.progress {
|
||||
margin-top: 5px;
|
||||
>>> .el-card__header {
|
||||
padding: 8px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
165
src/views/setting/cloudSetting/ldapConfig.vue
Normal file
165
src/views/setting/cloudSetting/ldapConfig.vue
Normal file
@ -0,0 +1,165 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<el-form :rules="rules" ref="form" :model="ldapFormData" label-width="120px" size="small"
|
||||
style="width: 450px" autocomplete="off">
|
||||
<el-form-item label="功能状态" prop="enable">
|
||||
<el-select v-model="ldapFormData.enable">
|
||||
<el-option
|
||||
v-for="item in ldapStatusOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="LDAP 服务器" prop="ldapServer">
|
||||
<el-input placeholder="例如: ldap.test.com:389" v-model="ldapFormData.ldapServer"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="Base DN" prop="baseDN">
|
||||
<el-input placeholder="例如: dc=test,dc=com" v-model="ldapFormData.baseDN" autocomplete="off"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="账号" prop="userDN">
|
||||
<el-input placeholder="管理员账号,例如: cn=admin,dc=test,dc=com" v-model="ldapFormData.userDN" autocomplete="off"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="密码" prop="password">
|
||||
<el-input type="password" placeholder="管理员密码" v-model="ldapFormData.password" autocomplete="off"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" :disabled="ldapTestBtn" v-loading="testLdapConfigLoading" @click="doTestLdapConfig">测试链接</el-button>
|
||||
<span v-if="testLdapConfigResult === 0" class="el-icon-check" style="color: #67C23A"></span>
|
||||
<span v-if="testLdapConfigResult > 0" class="el-icon-close" style="color: #F56C6C"></span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="登录名" prop="loginName">
|
||||
<el-input placeholder="LDAP服务器中对应个人用户名的字段, 例如: uid" v-model="ldapFormData.loginName"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="默认角色" prop="defaultRoleList">
|
||||
<el-select ref="selectRole" v-model="ldapFormData.defaultRoleList" multiple placeholder="请选择角色">
|
||||
<el-option
|
||||
v-for="item in roleList"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" :disabled="ldapSaveBtn" v-loading="saveLdapConfigLoading" @click="saveLdapConfig">保存配置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import {loadLdapConfig, testLdapConfig, updateLdapConfig} from "@/api/user";
|
||||
import roleApi from "@/api/role";
|
||||
|
||||
export default {
|
||||
name: 'LdapConfig',
|
||||
props: {
|
||||
data: {
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
ldapTestBtn() {
|
||||
return !(this.ldapFormData.ldapServer.length > 0 && this.ldapFormData.baseDN.length > 0 && (this.ldapFormData.password ? this.ldapFormData.password.length > 0 : false))
|
||||
},
|
||||
ldapSaveBtn() {
|
||||
return !(!this.ldapTestBtn && this.ldapFormData.defaultRoleList.length > 0 && this.ldapFormData.loginName.length > 0)
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
ldapFormData: {
|
||||
enable: true,
|
||||
ldapServer: '',
|
||||
defaultRoleList: [],
|
||||
baseDN: '',
|
||||
userDN: '',
|
||||
password: '',
|
||||
loginName: ''
|
||||
},
|
||||
ldapStatusOptions: [{
|
||||
value: true,
|
||||
label: '启用'
|
||||
}, {
|
||||
value: false,
|
||||
label: '禁用'
|
||||
}],
|
||||
rules: {
|
||||
ldapServer: [
|
||||
{required: true, message: '请填写LDAP服务器', trigger: 'submit'}
|
||||
],
|
||||
defaultRoleList: [
|
||||
{required: true, message: '请选择默认角色', trigger: 'submit'}
|
||||
],
|
||||
baseDN: [
|
||||
{required: true, message: '请填写 Base DN', trigger: 'submit'}
|
||||
],
|
||||
userDN: [
|
||||
{required: true, message: '请填写 管理员账号', trigger: 'submit'}
|
||||
],
|
||||
loginName: [
|
||||
{required: true, message: '请填写登录名', trigger: 'submit'}
|
||||
]
|
||||
},
|
||||
testLdapConfigLoading: false,
|
||||
saveLdapConfigLoading: false,
|
||||
testLdapConfigResult: -1,
|
||||
roleList: [],
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getRoleList()
|
||||
this.getLdapConfig()
|
||||
},
|
||||
methods: {
|
||||
doTestLdapConfig() {
|
||||
this.testLdapConfigLoading = true
|
||||
testLdapConfig(this.ldapFormData).then(() => {
|
||||
this.testLdapConfigLoading = false
|
||||
this.testLdapConfigResult = 0
|
||||
this.$message.success("连接成功")
|
||||
}).catch(() => {
|
||||
this.testLdapConfigResult = 1
|
||||
this.testLdapConfigLoading = false
|
||||
});
|
||||
},
|
||||
saveLdapConfig() {
|
||||
this.saveLdapConfigLoading = true
|
||||
updateLdapConfig(this.ldapFormData).then(() => {
|
||||
this.saveLdapConfigLoading = false
|
||||
this.$message.success("保存成功")
|
||||
}).catch(() => {
|
||||
this.saveLdapConfigLoading = false
|
||||
});
|
||||
},
|
||||
getRoleList() {
|
||||
roleApi.roleList().then(res => {
|
||||
this.roleList = res.data;
|
||||
})
|
||||
},
|
||||
getLdapConfig() {
|
||||
loadLdapConfig().then(res => {
|
||||
if (res.data) {
|
||||
this.ldapFormData = res.data;
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
padding: 0 !important;
|
||||
}
|
||||
</style>
|
||||
81
src/views/setting/cloudSetting/officeConfig.vue
Normal file
81
src/views/setting/cloudSetting/officeConfig.vue
Normal file
@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<el-form ref="officeConfigForm" :model="configFormData" label-width="150px" size="small"
|
||||
style="width: 520px" autocomplete="off">
|
||||
<el-form-item label="OnlyOffice地址" prop="documentServer">
|
||||
<el-input placeholder="OnlyOffice Server 地址, 一般不用填" v-model.number="configFormData.documentServer"></el-input>
|
||||
<span class="form-instruction">使用外部OnlyOffice服务时需要填写</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="密钥" prop="secret">
|
||||
<el-input type="password" placeholder="OnlyOffice JWT (留空为不使用密钥)" v-model="configFormData.secret"></el-input>
|
||||
<span class="form-instruction">建议使用密钥,防止office服务被白嫖。<a href="https://helpcenter.onlyoffice.com/installation/docs-configure-jwt.aspx" target="_blank">查看OnlyOffice文档</a></span>
|
||||
</el-form-item>
|
||||
<el-form-item label="回调服务地址" prop="callbackServer">
|
||||
<el-input placeholder="JmalCloud Server 地址, 一般不用填" v-model.number="configFormData.callbackServer"></el-input>
|
||||
<span class="form-instruction">使用外部OnlyOffice服务时需要填写</span>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" v-loading="saveConfigLoading" @click="saveConfig">保存配置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import settingApi from "@/api/setting-api";
|
||||
|
||||
export default {
|
||||
name: 'OfficeConfig',
|
||||
props: {
|
||||
data: {
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
saveConfigLoading: false,
|
||||
configFormData: {
|
||||
documentServer: undefined,
|
||||
secret: undefined,
|
||||
tokenEnabled: false,
|
||||
callbackServer: undefined
|
||||
},
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getOfficeConfig()
|
||||
},
|
||||
methods: {
|
||||
getOfficeConfig() {
|
||||
settingApi.getOfficeConfig().then(res => {
|
||||
this.configFormData = res.data
|
||||
})
|
||||
},
|
||||
saveConfig() {
|
||||
this.saveConfigLoading = true
|
||||
if (this.configFormData.documentServer === '') {
|
||||
this.configFormData.documentServer = undefined
|
||||
}
|
||||
if (this.configFormData.callbackServer === '') {
|
||||
this.configFormData.callbackServer = undefined
|
||||
}
|
||||
settingApi.updateOfficeConfig(this.configFormData).then(() => {
|
||||
this.saveConfigLoading = false
|
||||
this.$message.success('保存成功')
|
||||
}).catch(() => {
|
||||
this.saveConfigLoading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
padding: 0 !important;
|
||||
}
|
||||
</style>
|
||||
107
src/views/setting/cloudSetting/previewConfig.vue
Normal file
107
src/views/setting/cloudSetting/previewConfig.vue
Normal file
@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<el-form :rules="rules" ref="previewConfigForm" :model="configFormData" label-width="120px" size="small"
|
||||
style="width: 860px" autocomplete="off">
|
||||
<el-form-item label="iframe 预览" prop="iframe">
|
||||
<span class="form-instruction"><a href="https://alist.nn.ci/zh/config/preview.html#iframe-%E9%A2%84%E8%A7%88" target="_blank"><svg-icon icon-class="wailian"/>参考Alist的iframe预览 </a> </span>
|
||||
<el-input
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 2}"
|
||||
:placeholder="defaultIframeConfig"
|
||||
v-model="configFormData.iframe">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" v-loading="saveConfigLoading" @click="submitForm">保存配置</el-button>
|
||||
<el-button type="warning" @click="resetConfig">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import settingApi from '@/api/setting-api'
|
||||
|
||||
export default {
|
||||
name: 'PreviewConfig',
|
||||
props: {
|
||||
data: {
|
||||
type: Array,
|
||||
default: [],
|
||||
},
|
||||
},
|
||||
computed: {},
|
||||
data() {
|
||||
const validateIframeFormat = async (rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback()
|
||||
} else {
|
||||
try {
|
||||
this.configFormData.iframe = JSON.stringify(JSON.parse(value), null, 4)
|
||||
} catch (e) {
|
||||
callback(new Error('json格式错误'))
|
||||
}
|
||||
callback()
|
||||
}
|
||||
}
|
||||
return {
|
||||
saveConfigLoading: false,
|
||||
defaultIframeConfig: '{\n' +
|
||||
' "pdf": {\n' +
|
||||
' "PDF.js": "/pdf.js/web/viewer.html?file=$e_url"\n' +
|
||||
' },\n' +
|
||||
' "epub": {\n' +
|
||||
' "epub.js": "/resource/epubjs/viewer.html?url=$e_url"\n' +
|
||||
' }\n' +
|
||||
'}',
|
||||
configFormData: {
|
||||
iframe: '',
|
||||
},
|
||||
rules: {
|
||||
iframe: [
|
||||
{ required: false, validator: validateIframeFormat, trigger: 'submit'}
|
||||
],
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getPreviewConfig()
|
||||
},
|
||||
methods: {
|
||||
getPreviewConfig() {
|
||||
settingApi.getPreviewConfig().then(res => {
|
||||
this.configFormData = res.data
|
||||
})
|
||||
},
|
||||
submitForm() {
|
||||
this.$refs.previewConfigForm.validate((valid) => {
|
||||
if (valid) {
|
||||
this.saveConfig()
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
});
|
||||
},
|
||||
resetConfig() {
|
||||
this.configFormData.iframe = this.defaultIframeConfig
|
||||
},
|
||||
saveConfig() {
|
||||
this.saveConfigLoading = true
|
||||
settingApi.updatePreviewConfig(this.configFormData).then(() => {
|
||||
this.saveConfigLoading = false
|
||||
this.$message.success('保存成功')
|
||||
this.$store.dispatch('user/getInfo')
|
||||
}).catch(() => {
|
||||
this.saveConfigLoading = false
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
padding: 0 !important;
|
||||
}
|
||||
</style>
|
||||
240
src/views/setting/cloudSetting/transcodeConfig.vue
Normal file
240
src/views/setting/cloudSetting/transcodeConfig.vue
Normal file
@ -0,0 +1,240 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<el-form :rules="rules" ref="transcodeConfigForm" :model="configFormData" label-width="120px" size="small"
|
||||
style="width: 520px" autocomplete="off">
|
||||
<el-form-item label="功能状态" prop="enable">
|
||||
<el-switch
|
||||
v-model="configFormData.enable"
|
||||
active-text="启用"
|
||||
active-color="#13ce66"
|
||||
inactive-color="#ff4949"
|
||||
inactive-text="禁用">
|
||||
</el-switch>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="最大线程数" prop="maxThreads">
|
||||
<div class="form-item-flex">
|
||||
<el-input-number v-model="configFormData.maxThreads" :min="1" :max="8" label="最大线程数"></el-input-number>
|
||||
<div>
|
||||
<el-tooltip class="item" effect="dark" content="最多同时的转码的任务数, 默认为1(可根据具体硬件配置酌情增加)" placement="top">
|
||||
<i class="el-icon-question"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-divider content-position="left">转码条件, 以下三个条件满足其一即可</el-divider>
|
||||
|
||||
<el-form-item label="码率(kbps)" prop="bitrateCond">
|
||||
<div class="form-item-flex">
|
||||
<el-input placeholder="2500" v-model.number="configFormData.bitrateCond"></el-input>
|
||||
<div>
|
||||
<el-tooltip class="item" effect="dark" content="转码条件: 视频码率(kbps), 默认 2500 kbps, 小于该值则不转码" placement="top">
|
||||
<i class="el-icon-question"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="高度" prop="height">
|
||||
<div class="form-item-flex">
|
||||
<el-input placeholder="720" v-model.number="configFormData.heightCond"></el-input>
|
||||
<div>
|
||||
<el-tooltip class="item" effect="dark" content="转码条件: 视频高度(视频宽度, 默认随高度等比例缩放), 默认 720, 小于该值则不转码" placement="top">
|
||||
<i class="el-icon-question"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="帧率" prop="height">
|
||||
<div class="form-item-flex">
|
||||
<el-input placeholder="30" v-model.number="configFormData.frameRateCond"></el-input>
|
||||
<div>
|
||||
<el-tooltip class="item" effect="dark" content="转码条件: 视频帧率, 默认 30 fps, 小于该值则不转码" placement="top">
|
||||
<i class="el-icon-question"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-divider content-position="left">转码参数</el-divider>
|
||||
|
||||
<el-form-item label="目标码率(kbps)" prop="bitrate">
|
||||
<div class="form-item-flex">
|
||||
<el-input placeholder="2500" v-model.number="configFormData.bitrate"></el-input>
|
||||
<div>
|
||||
<el-tooltip class="item" effect="dark" content="转码后的视频码率(kbps),默认 2500 kbps" placement="top">
|
||||
<i class="el-icon-question"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="目标高度" prop="height">
|
||||
<div class="form-item-flex">
|
||||
<el-input placeholder="720" v-model.number="configFormData.height"></el-input>
|
||||
<div>
|
||||
<el-tooltip class="item" effect="dark" content="转码后的视频高度, 默认720(视频宽度则默认随高度等比例缩放)" placement="top">
|
||||
<i class="el-icon-question"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="目标帧率" prop="height">
|
||||
<div class="form-item-flex">
|
||||
<el-input placeholder="30" v-model.number="configFormData.frameRate"></el-input>
|
||||
<div>
|
||||
<el-tooltip class="item" effect="dark" content="转码后的视频帧率, 默认 30 fps" placement="top">
|
||||
<i class="el-icon-question"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="vtt缩略图数量" prop="vttThumbnailCount">
|
||||
<div class="form-item-flex">
|
||||
<el-input placeholder="60" v-model.number="configFormData.vttThumbnailCount"></el-input>
|
||||
<div>
|
||||
<el-tooltip class="item" effect="dark" content="vtt缩略图数量, 默认 60 张" placement="top">
|
||||
<i class="el-icon-question"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否重新转码" prop="isReTranscode">
|
||||
<div class="form-item-flex">
|
||||
<el-switch v-model="configFormData.isReTranscode"></el-switch>
|
||||
<div>
|
||||
<el-tooltip class="item" effect="dark" content="转码参数变化后对已经转码过的视频重新转码, 默认开启" placement="top">
|
||||
<i class="el-icon-question"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<div class="transcode-status">
|
||||
<el-button v-if="transcodeStatus.waitingTranscodingCount > 0 || transcodeStatus.transcodingCount > 0" type="warning" v-loading="cancelTaskLoading" @click="cancelTask">取消任务</el-button>
|
||||
<el-button v-else type="primary" v-loading="saveConfigLoading" @click="saveConfig">保存配置</el-button>
|
||||
<div>
|
||||
<span v-if="transcodeStatus.waitingTranscodingCount > 0"><b>{{transcodeStatus.waitingTranscodingCount}}</b>个视频等待转码 </span>
|
||||
<span v-if="transcodeStatus.transcodingCount > 0"> <b>{{transcodeStatus.transcodingCount}}</b>个视频正在转码中</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import settingApi, { getTranscodeStatus } from '@/api/setting-api'
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'TranscodeConfig',
|
||||
props: {
|
||||
data: {
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState(['message'])
|
||||
},
|
||||
watch: {
|
||||
message(msg) {
|
||||
if (msg.event === 'msg/transcodeStatus') {
|
||||
this.transcodeStatus = msg.data.body
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
saveConfigLoading: false,
|
||||
cancelTaskLoading: false,
|
||||
configFormData: {
|
||||
enable: true,
|
||||
bitrate: undefined,
|
||||
height: undefined,
|
||||
frameRate: undefined,
|
||||
bitrateCond: undefined,
|
||||
maxThreads: undefined,
|
||||
heightCond: undefined,
|
||||
frameRateCond: undefined,
|
||||
vttThumbnailCount: undefined,
|
||||
isReTranscode: undefined
|
||||
},
|
||||
transcodeStatus: {
|
||||
waitingTranscodingCount: 0,
|
||||
transcodingCount: 0
|
||||
},
|
||||
configEnableOptions: [{
|
||||
value: true,
|
||||
label: '启用'
|
||||
}, {
|
||||
value: false,
|
||||
label: '禁用'
|
||||
}],
|
||||
rules: {
|
||||
bitrate: [
|
||||
{type: 'number', message: '码率必须为数字值', trigger: 'blur'}
|
||||
],
|
||||
height: [
|
||||
{type: 'number', message: '高度必须为数字值', trigger: 'blur'}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getTranscodeConfig()
|
||||
getTranscodeStatus().then(res => {
|
||||
this.transcodeStatus = res.data
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
getTranscodeConfig() {
|
||||
settingApi.getTranscodeConfig().then(res => {
|
||||
this.configFormData = res.data
|
||||
})
|
||||
},
|
||||
saveConfig() {
|
||||
this.saveConfigLoading = true
|
||||
settingApi.setTranscodeConfig(this.configFormData).then((res) => {
|
||||
if (res.data > 0) {
|
||||
this.transcodeStatus.waitingTranscodingCount = res.data
|
||||
}
|
||||
this.saveConfigLoading = false
|
||||
this.$message.success('保存成功')
|
||||
}).catch(() => {
|
||||
this.saveConfigLoading = false
|
||||
})
|
||||
},
|
||||
cancelTask() {
|
||||
this.$confirm('确定要取消转码任务吗?可能导致转码后的视频不完整', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.doCancelTask()
|
||||
})
|
||||
},
|
||||
doCancelTask() {
|
||||
this.cancelTaskLoading = true
|
||||
settingApi.cancelTranscodeTask().then(() => {
|
||||
this.$message.success('取消成功')
|
||||
this.cancelTaskLoading = false
|
||||
}).catch(() => {
|
||||
this.cancelTaskLoading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "src/styles/setting";
|
||||
.transcode-status {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.container {
|
||||
padding: 0 !important;
|
||||
}
|
||||
</style>
|
||||
@ -123,7 +123,7 @@ export default {
|
||||
isLoginType: this.type === 'LOGIN',
|
||||
// 查询条件
|
||||
queryCondition: {
|
||||
excludeUsername: 'other',
|
||||
excludeUsername: undefined,
|
||||
username: undefined,
|
||||
ip: undefined,
|
||||
type: this.type,
|
||||
@ -191,12 +191,7 @@ export default {
|
||||
}
|
||||
this.queryLogList()
|
||||
},
|
||||
queryLogList() {
|
||||
if (this.queryCondition.username) {
|
||||
this.queryCondition.excludeUsername = null;
|
||||
} else {
|
||||
this.queryCondition.excludeUsername = 'other';
|
||||
}
|
||||
queryLogList(val) {
|
||||
this.$refs.tableList.initPageIndex()
|
||||
this.getLogList()
|
||||
},
|
||||
|
||||
82
src/views/setting/taskProgress.vue
Normal file
82
src/views/setting/taskProgress.vue
Normal file
@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-card class="box-card">
|
||||
<div slot="header">
|
||||
<div class="box-card-header">
|
||||
<div class="clearfix card-header-back">
|
||||
<span>任务进度</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="setting-title-desc">
|
||||
<div class="title-status">
|
||||
<div class="setting-title-desc-text">OCR识别任务和视频转码任务</div>
|
||||
<div>
|
||||
<span v-if="transcodeStatus.waitingTranscodingCount > 0"><b>{{transcodeStatus.waitingTranscodingCount}}</b>个视频等待转码 </span>
|
||||
<span v-if="transcodeStatus.transcodingCount > 0"> <b>{{transcodeStatus.transcodingCount}}</b>个视频正在转码中</span>
|
||||
</div>
|
||||
</div>
|
||||
<el-divider></el-divider>
|
||||
</div>
|
||||
<task-progress :data="taskProgressDataList"></task-progress>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import TaskProgress from "@/components/TaskProgress/index.vue";
|
||||
import { getTaskProgress, getTranscodeStatus } from '@/api/setting-api'
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
components: { TaskProgress },
|
||||
data() {
|
||||
return {
|
||||
taskProgressDataList: [],
|
||||
transcodeStatus: {
|
||||
waitingTranscodingCount: 0,
|
||||
transcodingCount: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
getTaskProgress().then(res => {
|
||||
this.taskProgressDataList = res.data
|
||||
})
|
||||
getTranscodeStatus().then(res => {
|
||||
this.transcodeStatus = res.data
|
||||
})
|
||||
},
|
||||
computed: {
|
||||
...mapState(['message'])
|
||||
},
|
||||
watch: {
|
||||
message(msg) {
|
||||
if (msg.event === 'msg/transcodeStatus') {
|
||||
this.transcodeStatus = msg.data.body
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "src/styles/setting";
|
||||
.title-status {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.progress {
|
||||
margin-top: 5px;
|
||||
>>> .el-card__header {
|
||||
padding: 8px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<share-dialog :file.sync="shareDialogObject" :status.sync="shareDialogVisible" @onUpdateExpireData="onUpdateExpireData"></share-dialog>
|
||||
<share-dialog :file.sync="shareDialogObject" :status.sync="shareDialogVisible" @onUpdateExpireData="onUpdateExpireData" @onUpdateShareForm="onUpdateShareForm" @onCancelShare="onCancelShare"></share-dialog>
|
||||
<!--list布局-->
|
||||
<div v-show="fileList.length > 0" :style="{'width':'100%','height': clientHeight+'px'}">
|
||||
<pl-table
|
||||
@ -138,7 +138,7 @@
|
||||
header-align="left"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<span>{{expireInfo(scope.row)}}</span>
|
||||
<span :style="{color: expireColor(scope.row)}">{{expireInfo(scope.row)}}</span>
|
||||
</template>
|
||||
</pl-table-column>
|
||||
</template>
|
||||
@ -271,6 +271,19 @@
|
||||
return '永久有效'
|
||||
}
|
||||
},
|
||||
expireColor(row) {
|
||||
if (row.expireDate) {
|
||||
let currentTime = new Date();
|
||||
let targetTime = new Date(row.expireDate);
|
||||
if (currentTime > targetTime) {
|
||||
return '#F56C6C'
|
||||
} else {
|
||||
return '#606266'
|
||||
}
|
||||
} else {
|
||||
return '#67C23A'
|
||||
}
|
||||
},
|
||||
// 请求之前的准备
|
||||
beforeLoadData(onLoad){
|
||||
if (onLoad) {
|
||||
@ -461,6 +474,9 @@
|
||||
})
|
||||
})
|
||||
},
|
||||
onCancelShare() {
|
||||
this.getFileList()
|
||||
},
|
||||
// 点击文件或文件夹
|
||||
fileClick(row) {
|
||||
this.shareDialogObject = row
|
||||
@ -474,6 +490,12 @@
|
||||
share.expireDate = expireDate
|
||||
this.fileList.splice(index, 1, share)
|
||||
},
|
||||
onUpdateShareForm(shareId, isPrivacy) {
|
||||
let index = this.fileList.findIndex(item => item.id === shareId)
|
||||
let share = this.fileList[index]
|
||||
share.isPrivacy = isPrivacy
|
||||
this.fileList.splice(index, 1, share)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
:defaultSort="sortable"
|
||||
:queryCondition="queryCondition"
|
||||
:contextMenus="contextMenus"
|
||||
homeHidden
|
||||
>
|
||||
</show-file>
|
||||
</div>
|
||||
|
||||
72
src/views/trash/index.vue
Normal file
72
src/views/trash/index.vue
Normal file
@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<div>
|
||||
<show-file
|
||||
queryFileType="trash"
|
||||
:defaultGrid="true"
|
||||
:orderCustom="true"
|
||||
:sortable="sortable"
|
||||
:queryCondition="queryCondition"
|
||||
:showNavigation="true"
|
||||
:show-search-button="false"
|
||||
:context-menus="contextMenus"
|
||||
:single-menus="singleMenus"
|
||||
:multiple-right-menus="multipleRightMenus"
|
||||
:show-upload-button="false"
|
||||
></show-file>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ShowFile from "@/components/ShowFile/ShowFile";
|
||||
|
||||
export default {
|
||||
components: {ShowFile},
|
||||
data() {
|
||||
return {
|
||||
sortable: {
|
||||
prop: 'updateDate', order: 'descending'
|
||||
},
|
||||
queryCondition: {
|
||||
isFolder: false,
|
||||
isTrash: true
|
||||
},
|
||||
contextMenus: [
|
||||
{
|
||||
label: '查看', operation: 'viewMode', child: [
|
||||
{ iconClass: this.grid ? '' : 'menu-point', label: '列表', operation: 'vmode-list' },
|
||||
{ iconClass: this.grid ? 'menu-point' : '', label: '缩略图', operation: 'vmode-grid' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '排列方式', operation: 'arrangement', child: [
|
||||
{ label: '名称', operation: 'orderName', orderProp: 'name' },
|
||||
{ label: '大小', operation: 'orderSize', orderProp: 'size' },
|
||||
{ label: '日期', operation: 'orderUpdateDate', orderProp: 'updateDate' },
|
||||
]
|
||||
},
|
||||
{ label: '刷新', operation: 'refresh' },
|
||||
{ divider: true, operation: 'divider' },
|
||||
{ label: '清空回收站', operation: 'clearTrash' },
|
||||
],
|
||||
singleMenus: [
|
||||
{iconClass: 'huanyuan', label: '放回原处', operation: 'restore'},
|
||||
{iconClass: 'menu-remove', label: '彻底删除', operation: 'sweep'}
|
||||
],
|
||||
multipleRightMenus: [
|
||||
{
|
||||
iconClass: 'menu-deselect',
|
||||
label: '取消选定',
|
||||
operation: 'deselect',
|
||||
},
|
||||
{iconClass: 'huanyuan', label: '放回原处', operation: 'restore'},
|
||||
{ iconClass: 'menu-remove', label: '彻底删除', operation: 'sweep' },
|
||||
],
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user