test/custom-admin-ui #3
@@ -1,15 +0,0 @@
|
|||||||
/* eslint-env node */
|
|
||||||
require('@rushstack/eslint-patch/modern-module-resolution')
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
'extends': [
|
|
||||||
'plugin:vue/vue3-essential',
|
|
||||||
'eslint:recommended',
|
|
||||||
'@vue/eslint-config-typescript',
|
|
||||||
'@vue/eslint-config-prettier/skip-formatting'
|
|
||||||
],
|
|
||||||
parserOptions: {
|
|
||||||
ecmaVersion: 'latest'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
pnpm-debug.log*
|
|
||||||
lerna-debug.log*
|
|
||||||
.vite/
|
|
||||||
node_modules
|
|
||||||
.DS_Store
|
|
||||||
dist-ssr
|
|
||||||
coverage
|
|
||||||
*.local
|
|
||||||
.git
|
|
||||||
/cypress/videos/
|
|
||||||
/cypress/screenshots/
|
|
||||||
|
|
||||||
# Editor directories and files
|
|
||||||
.vscode/*
|
|
||||||
!.vscode/extensions.json
|
|
||||||
.idea
|
|
||||||
*.suo
|
|
||||||
*.ntvs*
|
|
||||||
*.njsproj
|
|
||||||
*.sln
|
|
||||||
*.sw?
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://json.schemastore.org/prettierrc",
|
|
||||||
"semi": false,
|
|
||||||
"tabWidth": 2,
|
|
||||||
"singleQuote": true,
|
|
||||||
"printWidth": 100,
|
|
||||||
"trailingComma": "none"
|
|
||||||
}
|
|
||||||
Vendored
-9
@@ -1,9 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
/* prettier-ignore */
|
|
||||||
// @ts-nocheck
|
|
||||||
// noinspection JSUnusedGlobalSymbols
|
|
||||||
// Generated by unplugin-auto-import
|
|
||||||
export {}
|
|
||||||
declare global {
|
|
||||||
|
|
||||||
}
|
|
||||||
Vendored
-46
@@ -1,46 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
// @ts-nocheck
|
|
||||||
// Generated by unplugin-vue-components
|
|
||||||
// Read more: https://github.com/vuejs/core/pull/3399
|
|
||||||
export {}
|
|
||||||
|
|
||||||
/* prettier-ignore */
|
|
||||||
declare module 'vue' {
|
|
||||||
export interface GlobalComponents {
|
|
||||||
CardTools: typeof import('./src/components/CardTools.vue')['default']
|
|
||||||
ElButton: typeof import('element-plus/es')['ElButton']
|
|
||||||
ElCard: typeof import('element-plus/es')['ElCard']
|
|
||||||
ElCol: typeof import('element-plus/es')['ElCol']
|
|
||||||
ElContainer: typeof import('element-plus/es')['ElContainer']
|
|
||||||
ElDialog: typeof import('element-plus/es')['ElDialog']
|
|
||||||
ElDrawer: typeof import('element-plus/es')['ElDrawer']
|
|
||||||
ElEmpty: typeof import('element-plus/es')['ElEmpty']
|
|
||||||
ElForm: typeof import('element-plus/es')['ElForm']
|
|
||||||
ElFormItem: typeof import('element-plus/es')['ElFormItem']
|
|
||||||
ElHeader: typeof import('element-plus/es')['ElHeader']
|
|
||||||
ElIcon: typeof import('element-plus/es')['ElIcon']
|
|
||||||
ElInput: typeof import('element-plus/es')['ElInput']
|
|
||||||
ElMain: typeof import('element-plus/es')['ElMain']
|
|
||||||
ElMenu: typeof import('element-plus/es')['ElMenu']
|
|
||||||
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
|
|
||||||
ElOption: typeof import('element-plus/es')['ElOption']
|
|
||||||
ElPagination: typeof import('element-plus/es')['ElPagination']
|
|
||||||
ElProgress: typeof import('element-plus/es')['ElProgress']
|
|
||||||
ElRadio: typeof import('element-plus/es')['ElRadio']
|
|
||||||
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
|
|
||||||
ElRow: typeof import('element-plus/es')['ElRow']
|
|
||||||
ElSelect: typeof import('element-plus/es')['ElSelect']
|
|
||||||
ElTable: typeof import('element-plus/es')['ElTable']
|
|
||||||
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
|
||||||
ElTag: typeof import('element-plus/es')['ElTag']
|
|
||||||
ElUpload: typeof import('element-plus/es')['ElUpload']
|
|
||||||
FileBox: typeof import('./src/components/FileBox.vue')['default']
|
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
|
||||||
UploadFile: typeof import('./src/components/UploadFile.vue')['default']
|
|
||||||
UploadText: typeof import('./src/components/UploadText.vue')['default']
|
|
||||||
}
|
|
||||||
export interface ComponentCustomProperties {
|
|
||||||
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Vendored
-1
@@ -1 +0,0 @@
|
|||||||
/// <reference types="vite/client" />
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<link rel="icon" href="/assets/logo_small.png">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />
|
|
||||||
<meta name="description" content="{{description}}">
|
|
||||||
<meta name="keywords" content="{{keywords}}" />
|
|
||||||
<meta name="generator" content="FileCodeBox2.1" />
|
|
||||||
<title>{{title}}</title>
|
|
||||||
</head>
|
|
||||||
<body style="background: url('{{background}}') no-repeat center center fixed;background-size: cover;">
|
|
||||||
<div id="app" style="opacity: {{opacity}}"></div>
|
|
||||||
<script type="module" src="/src/main.ts"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "fcb-fronted",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"private": true,
|
|
||||||
"scripts": {
|
|
||||||
"dev": "vite",
|
|
||||||
"build": "run-p type-check build-only",
|
|
||||||
"preview": "vite preview",
|
|
||||||
"build-only": "vite build",
|
|
||||||
"type-check": "vue-tsc --noEmit -p tsconfig.app.json --composite false",
|
|
||||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
|
|
||||||
"format": "prettier --write src/"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@traptitech/markdown-it-katex": "^3.6.0",
|
|
||||||
"@types/markdown-it": "^14.1.2",
|
|
||||||
"@types/markdown-it-link-attributes": "^3.0.5",
|
|
||||||
"axios": "^1.7.2",
|
|
||||||
"element-plus": "^2.7.8",
|
|
||||||
"github-markdown-css": "^5.6.1",
|
|
||||||
"highlight.js": "^11.10.0",
|
|
||||||
"markdown-it": "^14.1.0",
|
|
||||||
"markdown-it-link-attributes": "^4.0.1",
|
|
||||||
"pinia": "^2.2.0",
|
|
||||||
"qrcode.vue": "^3.4.1",
|
|
||||||
"sass": "^1.77.8",
|
|
||||||
"unplugin-auto-import": "^0.18.2",
|
|
||||||
"unplugin-vue-components": "^0.27.3",
|
|
||||||
"vue": "^3.4.34",
|
|
||||||
"vue-i18n": "^9.13.1",
|
|
||||||
"vue-router": "^4.4.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@rushstack/eslint-patch": "^1.10.4",
|
|
||||||
"@tsconfig/node18": "^18.2.4",
|
|
||||||
"@types/node": "^22.0.0",
|
|
||||||
"@vitejs/plugin-vue": "^5.1.1",
|
|
||||||
"@vue/eslint-config-prettier": "^9.0.0",
|
|
||||||
"@vue/eslint-config-typescript": "^13.0.0",
|
|
||||||
"@vue/tsconfig": "^0.5.1",
|
|
||||||
"eslint": "^9.11.1",
|
|
||||||
"eslint-plugin-vue": "^9.28.0",
|
|
||||||
"npm-run-all": "^4.1.5",
|
|
||||||
"prettier": "^3.3.3",
|
|
||||||
"typescript": "~5.5.4",
|
|
||||||
"vite": "^5.3.5",
|
|
||||||
"vue-tsc": "^2.0.29"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 92 KiB |
@@ -1,33 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { onMounted } from "vue";
|
|
||||||
import { request } from "@/utils/request";
|
|
||||||
import { ElNotification } from 'element-plus'
|
|
||||||
onMounted(() => {
|
|
||||||
request({
|
|
||||||
url:'/',
|
|
||||||
method:'post'
|
|
||||||
}).then((res:any)=>{
|
|
||||||
if (res.code === 200) {
|
|
||||||
localStorage.setItem('config', JSON.stringify(res.detail));
|
|
||||||
if (res.detail.notify_title && res.detail.notify_content && localStorage.getItem('notify') !== res.detail.notify_title + res.detail.notify_content) {
|
|
||||||
localStorage.setItem('notify', res.detail.notify_title + res.detail.notify_content);
|
|
||||||
ElNotification({
|
|
||||||
title: res.detail.notify_title,
|
|
||||||
dangerouslyUseHTMLString: true,
|
|
||||||
message: res.detail.notify_content,
|
|
||||||
type: 'success'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<RouterView />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
/* color palette from <https://github.com/vuejs/theme> */
|
|
||||||
:root {
|
|
||||||
--vt-c-white: #ffffff;
|
|
||||||
--vt-c-white-soft: #f5f5f5;
|
|
||||||
--vt-c-white-mute: #f2f2f2;
|
|
||||||
--vt-c-black: #181818;
|
|
||||||
--vt-c-black-soft: #212121;
|
|
||||||
--vt-c-black-mute: #282828;
|
|
||||||
|
|
||||||
--vt-c-indigo: #2c3e50;
|
|
||||||
|
|
||||||
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
|
|
||||||
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
|
|
||||||
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
|
|
||||||
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
|
|
||||||
|
|
||||||
--vt-c-text-light-1: var(--vt-c-indigo);
|
|
||||||
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
|
|
||||||
--vt-c-text-dark-1: var(--vt-c-white);
|
|
||||||
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* semantic color variables for this project */
|
|
||||||
:root {
|
|
||||||
--color-background: var(--vt-c-white);
|
|
||||||
--color-background-soft: var(--vt-c-white-soft);
|
|
||||||
--color-background-mute: var(--vt-c-white-mute);
|
|
||||||
|
|
||||||
--color-border: var(--vt-c-divider-light-2);
|
|
||||||
--color-border-hover: var(--vt-c-divider-light-1);
|
|
||||||
|
|
||||||
--color-heading: var(--vt-c-text-light-1);
|
|
||||||
--color-text: var(--vt-c-text-light-1);
|
|
||||||
|
|
||||||
--section-gap: 160px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
:root {
|
|
||||||
--color-background: var(--vt-c-black);
|
|
||||||
--color-background-soft: var(--vt-c-black-soft);
|
|
||||||
--color-background-mute: var(--vt-c-black-mute);
|
|
||||||
|
|
||||||
--color-border: var(--vt-c-divider-dark-2);
|
|
||||||
--color-border-hover: var(--vt-c-divider-dark-1);
|
|
||||||
|
|
||||||
--color-heading: var(--vt-c-text-dark-1);
|
|
||||||
--color-text: var(--vt-c-text-dark-2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*,
|
|
||||||
*::before,
|
|
||||||
*::after {
|
|
||||||
box-sizing: border-box;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
html{
|
|
||||||
background: var(--vt-c-white-soft);
|
|
||||||
}
|
|
||||||
html.dark{
|
|
||||||
background: var(--vt-c-black-soft);
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
color: var(--color-text);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
line-height: 1.6;
|
|
||||||
font-family: Inter,
|
|
||||||
-apple-system,
|
|
||||||
BlinkMacSystemFont,
|
|
||||||
'Segoe UI',
|
|
||||||
Roboto,
|
|
||||||
Oxygen,
|
|
||||||
Ubuntu,
|
|
||||||
Cantarell,
|
|
||||||
'Fira Sans',
|
|
||||||
'Droid Sans',
|
|
||||||
'Helvetica Neue',
|
|
||||||
sans-serif;
|
|
||||||
font-size: 15px;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,206 +0,0 @@
|
|||||||
html.dark {
|
|
||||||
pre code.hljs {
|
|
||||||
display: block;
|
|
||||||
overflow-x: auto;
|
|
||||||
padding: 1em
|
|
||||||
}
|
|
||||||
|
|
||||||
code.hljs {
|
|
||||||
padding: 3px 5px
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs {
|
|
||||||
color: #abb2bf;
|
|
||||||
background: #282c34
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-keyword,
|
|
||||||
.hljs-operator,
|
|
||||||
.hljs-pattern-match {
|
|
||||||
color: #f92672
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-function,
|
|
||||||
.hljs-pattern-match .hljs-constructor {
|
|
||||||
color: #61aeee
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-function .hljs-params {
|
|
||||||
color: #a6e22e
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-function .hljs-params .hljs-typing {
|
|
||||||
color: #fd971f
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-module-access .hljs-module {
|
|
||||||
color: #7e57c2
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-constructor {
|
|
||||||
color: #e2b93d
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-constructor .hljs-string {
|
|
||||||
color: #9ccc65
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-comment,
|
|
||||||
.hljs-quote {
|
|
||||||
color: #b18eb1;
|
|
||||||
font-style: italic
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-doctag,
|
|
||||||
.hljs-formula {
|
|
||||||
color: #c678dd
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-deletion,
|
|
||||||
.hljs-name,
|
|
||||||
.hljs-section,
|
|
||||||
.hljs-selector-tag,
|
|
||||||
.hljs-subst {
|
|
||||||
color: #e06c75
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-literal {
|
|
||||||
color: #56b6c2
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-addition,
|
|
||||||
.hljs-attribute,
|
|
||||||
.hljs-meta .hljs-string,
|
|
||||||
.hljs-regexp,
|
|
||||||
.hljs-string {
|
|
||||||
color: #98c379
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-built_in,
|
|
||||||
.hljs-class .hljs-title,
|
|
||||||
.hljs-title.class_ {
|
|
||||||
color: #e6c07b
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-attr,
|
|
||||||
.hljs-number,
|
|
||||||
.hljs-selector-attr,
|
|
||||||
.hljs-selector-class,
|
|
||||||
.hljs-selector-pseudo,
|
|
||||||
.hljs-template-variable,
|
|
||||||
.hljs-type,
|
|
||||||
.hljs-variable {
|
|
||||||
color: #d19a66
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-bullet,
|
|
||||||
.hljs-link,
|
|
||||||
.hljs-meta,
|
|
||||||
.hljs-selector-id,
|
|
||||||
.hljs-symbol,
|
|
||||||
.hljs-title {
|
|
||||||
color: #61aeee
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-emphasis {
|
|
||||||
font-style: italic
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-strong {
|
|
||||||
font-weight: 700
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-link {
|
|
||||||
text-decoration: underline
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
|
||||||
pre code.hljs {
|
|
||||||
display: block;
|
|
||||||
overflow-x: auto;
|
|
||||||
padding: 1em
|
|
||||||
}
|
|
||||||
|
|
||||||
code.hljs {
|
|
||||||
padding: 3px 5px;
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
height: 4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs {
|
|
||||||
color: #383a42;
|
|
||||||
background: #fafafa
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-comment,
|
|
||||||
.hljs-quote {
|
|
||||||
color: #a0a1a7;
|
|
||||||
font-style: italic
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-doctag,
|
|
||||||
.hljs-formula,
|
|
||||||
.hljs-keyword {
|
|
||||||
color: #a626a4
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-deletion,
|
|
||||||
.hljs-name,
|
|
||||||
.hljs-section,
|
|
||||||
.hljs-selector-tag,
|
|
||||||
.hljs-subst {
|
|
||||||
color: #e45649
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-literal {
|
|
||||||
color: #0184bb
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-addition,
|
|
||||||
.hljs-attribute,
|
|
||||||
.hljs-meta .hljs-string,
|
|
||||||
.hljs-regexp,
|
|
||||||
.hljs-string {
|
|
||||||
color: #50a14f
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-attr,
|
|
||||||
.hljs-number,
|
|
||||||
.hljs-selector-attr,
|
|
||||||
.hljs-selector-class,
|
|
||||||
.hljs-selector-pseudo,
|
|
||||||
.hljs-template-variable,
|
|
||||||
.hljs-type,
|
|
||||||
.hljs-variable {
|
|
||||||
color: #986801
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-bullet,
|
|
||||||
.hljs-link,
|
|
||||||
.hljs-meta,
|
|
||||||
.hljs-selector-id,
|
|
||||||
.hljs-symbol,
|
|
||||||
.hljs-title {
|
|
||||||
color: #4078f2
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-built_in,
|
|
||||||
.hljs-class .hljs-title,
|
|
||||||
.hljs-title.class_ {
|
|
||||||
color: #c18401
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-emphasis {
|
|
||||||
font-style: italic
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-strong {
|
|
||||||
font-weight: 700
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-link {
|
|
||||||
text-decoration: underline
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
@import 'katex/dist/katex.min.css';
|
|
||||||
@import '@/assets/code/highlight.scss';
|
|
||||||
@import '@/assets/code/github-markdown.scss';
|
|
||||||
@import '@/assets/code/markdown-add.scss';
|
|
||||||
.markdown-body {
|
|
||||||
box-sizing: border-box;
|
|
||||||
background:none ;
|
|
||||||
*{
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 767px) {
|
|
||||||
.markdown-body {
|
|
||||||
padding: 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin: auto;
|
|
||||||
max-width: 1080px;
|
|
||||||
padding: 0 5px;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-messages {
|
|
||||||
flex-grow: 1;
|
|
||||||
overflow-y: auto;
|
|
||||||
border-radius: 10px 10px 0 0;
|
|
||||||
padding: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-message {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-message-bubble {
|
|
||||||
padding: 8px;
|
|
||||||
align-self: flex-start;
|
|
||||||
color: #2f4f4f;
|
|
||||||
border-radius: 8px 8px 8px 0;
|
|
||||||
background: linear-gradient(to right, #c9d6ff, #e2e2e2); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
|
|
||||||
box-shadow: 0 16px 20px 0 rgba(174, 167, 223, .06);
|
|
||||||
max-width: 80%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-toolbar-icon{
|
|
||||||
margin-left: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-message-meta {
|
|
||||||
display: flex;
|
|
||||||
position: relative;
|
|
||||||
margin-top: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.n-input-wrapper {
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-message-nickname-and-time {
|
|
||||||
color: #999;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-me {
|
|
||||||
justify-content: flex-end;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.n-input .n-input__suffix, .n-input .n-input__prefix {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.n-input .n-input-wrapper {
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.n-input .n-input__suffix .n-button {
|
|
||||||
border-radius: 8px !important;
|
|
||||||
width: 80px;
|
|
||||||
height: 30px;
|
|
||||||
position: absolute; right: 10px; bottom: 10px;
|
|
||||||
box-shadow: inset 0 1px 3px 0 rgba(0, 0, 0, 0.3);
|
|
||||||
background-image: linear-gradient(-56deg, #0773ff 5%, #797eff);
|
|
||||||
--n-border: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-message.is-me .chat-message-bubble {
|
|
||||||
background: linear-gradient(to right, #56ccf2, #2f80ed); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
|
|
||||||
border-radius: 8px 8px 0 !important;
|
|
||||||
box-shadow: 0 16px 20px 0 rgba(174, 167, 223, .06);
|
|
||||||
align-self: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
.markdown-body {
|
|
||||||
background-color: transparent;
|
|
||||||
font-size: 14px;
|
|
||||||
|
|
||||||
p {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol {
|
|
||||||
list-style-type: decimal;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
list-style-type: disc;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre code,
|
|
||||||
pre tt {
|
|
||||||
line-height: 1.65;
|
|
||||||
}
|
|
||||||
|
|
||||||
// .highlight pre,
|
|
||||||
// pre {
|
|
||||||
// background-color: #fff;
|
|
||||||
// }
|
|
||||||
|
|
||||||
code.hljs {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code-block {
|
|
||||||
&-wrapper {
|
|
||||||
position: relative;
|
|
||||||
padding-top: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-header {
|
|
||||||
position: absolute;
|
|
||||||
top: 5px;
|
|
||||||
right: 0;
|
|
||||||
width: 100%;
|
|
||||||
padding: 0 1rem;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
align-items: center;
|
|
||||||
color: #b3b3b3;
|
|
||||||
|
|
||||||
&__copy {
|
|
||||||
cursor: pointer;
|
|
||||||
margin-left: 0.5rem;
|
|
||||||
user-select: none;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #65a665;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
html.dark {
|
|
||||||
|
|
||||||
.message-reply {
|
|
||||||
.whitespace-pre-wrap {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
color: var(--n-text-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.highlight pre,
|
|
||||||
pre {
|
|
||||||
background-color: #282c34;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
@import './base.css';
|
|
||||||
|
|
||||||
#app {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
max-width: 400px;
|
|
||||||
min-width: 398px;
|
|
||||||
height: 100%;
|
|
||||||
background-color: #F8FBFE;
|
|
||||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1);
|
|
||||||
border-radius: 20px !important;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tools {
|
|
||||||
display: flex;
|
|
||||||
z-index: 999;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.box {
|
|
||||||
display: inline-block;
|
|
||||||
align-items: center;
|
|
||||||
width: 1.5rem;
|
|
||||||
height: 1.5rem;
|
|
||||||
user-select: none;
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 1px;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
.circle {
|
|
||||||
padding: 0 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.red {
|
|
||||||
padding: 3px;
|
|
||||||
background-color: #ff605c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.yellow {
|
|
||||||
padding: 3px;
|
|
||||||
background-color: #ffbd44;
|
|
||||||
}
|
|
||||||
|
|
||||||
.green {
|
|
||||||
background-color: #00ca4e;
|
|
||||||
padding: 3px;
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { useDark, useToggle } from '@vueuse/core'
|
|
||||||
import { useRoute, useRouter } from "vue-router";
|
|
||||||
const router = useRouter()
|
|
||||||
const route = useRoute()
|
|
||||||
const isDark = useDark()
|
|
||||||
const toggleDark = useToggle(isDark)
|
|
||||||
import { Moon, Sunny, Upload, Back, TakeawayBox } from "@element-plus/icons-vue";
|
|
||||||
import FileBox from "@/components/FileBox.vue";
|
|
||||||
import { useFileBoxStore } from "@/stores/fileBox";
|
|
||||||
const fileBoxStore = useFileBoxStore();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="tools">
|
|
||||||
<div class="circle">
|
|
||||||
<el-icon size="17" color="#212121" class="red box" @click="router.push({'name':route.name=='home'?'send':'home'})">
|
|
||||||
<Back v-if="route.name=='send'"></Back>
|
|
||||||
<Upload v-else></Upload>
|
|
||||||
</el-icon>
|
|
||||||
</div>
|
|
||||||
<div class="circle">
|
|
||||||
<el-icon size="17" color="#212121" class="yellow box" @click="toggleDark(!isDark)">
|
|
||||||
<Moon v-if="isDark"></Moon>
|
|
||||||
<Sunny v-else></Sunny>
|
|
||||||
</el-icon>
|
|
||||||
</div>
|
|
||||||
<div class="circle">
|
|
||||||
<el-icon size="17" color="#212121" class="green box" @click="fileBoxStore.showFileBox=true">
|
|
||||||
<TakeawayBox></TakeawayBox>
|
|
||||||
</el-icon>
|
|
||||||
</div>
|
|
||||||
<file-box></file-box>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { useFileDataStore } from "@/stores/fileData";
|
|
||||||
import { useFileBoxStore } from "@/stores/fileBox";
|
|
||||||
import hljs from 'highlight.js';
|
|
||||||
import mila from 'markdown-it-link-attributes';
|
|
||||||
import markdownIt from 'markdown-it';
|
|
||||||
import markdownItKatex from '@traptitech/markdown-it-katex';
|
|
||||||
import '@/assets/code/main.scss'
|
|
||||||
const fileStore = useFileDataStore();
|
|
||||||
const fileBoxStore = useFileBoxStore();
|
|
||||||
import QrcodeVue from "qrcode.vue";
|
|
||||||
import { useRoute } from "vue-router";
|
|
||||||
import { ref } from "vue";
|
|
||||||
|
|
||||||
import { useI18n } from 'vue-i18n'
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
import { ElMessage } from "element-plus";
|
|
||||||
const openUrl = (url: string) => {
|
|
||||||
if (url.startsWith('/')) {
|
|
||||||
url = window.location.origin + url;
|
|
||||||
}
|
|
||||||
window.open(url);
|
|
||||||
};
|
|
||||||
const route = useRoute();
|
|
||||||
|
|
||||||
const copyText = (text: any, style = 0) => {
|
|
||||||
ElMessage.success(t('fileBox.copySuccess'));
|
|
||||||
if (style === 1) {
|
|
||||||
text = window.location.origin + '/#/?code=' + text;
|
|
||||||
}
|
|
||||||
const temp: any = document.createElement('textarea');
|
|
||||||
temp.value = text;
|
|
||||||
document.body.appendChild(temp);
|
|
||||||
temp.select();
|
|
||||||
if (document.execCommand('copy')) {
|
|
||||||
document.execCommand('copy');
|
|
||||||
}
|
|
||||||
document.body.removeChild(temp);
|
|
||||||
};
|
|
||||||
const nowText = ref('');
|
|
||||||
const showTextDetail = (text: any) => {
|
|
||||||
showTextDetailVisible.value = true;
|
|
||||||
nowText.value = text;
|
|
||||||
};
|
|
||||||
|
|
||||||
const showTextDetailVisible = ref(false);
|
|
||||||
|
|
||||||
const md = new markdownIt({
|
|
||||||
html: false,
|
|
||||||
linkify: true,
|
|
||||||
highlight(code: any, language: any) {
|
|
||||||
const validLang = Boolean(language && hljs.getLanguage(language));
|
|
||||||
return `<pre class="code-block-wrapper"><code class="hljs code-block-body ${validLang}">${hljs.highlight(code, { language: language ?? '' }).value}</code></pre>`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
md.use(mila, { attrs: { target: '_blank', rel: 'noopener' } });
|
|
||||||
md.use(markdownItKatex, { blockClass: 'katexmath-block rounded-md p-[10px]', errorColor: ' #cc0000' });
|
|
||||||
function renderMarkdown(message: string) {
|
|
||||||
return md.render(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getQrCodeUrl(code: string) {
|
|
||||||
// 否是接收的文件,如果是则返回下载直链,否则拼接分享链接
|
|
||||||
if (code) {
|
|
||||||
return code.toString().startsWith('/share/download') ? window.location.origin + code : window.location.origin + '/#/?code=' + code;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<el-drawer :append-to-body="true" v-model="fileBoxStore.showFileBox" direction="btt" style="max-width: 1080px;margin: auto;"
|
|
||||||
size="400">
|
|
||||||
<template #header>
|
|
||||||
<h4 v-if="route.name=='home'">{{t('fileBox.receiveFileBox')}}</h4>
|
|
||||||
<h4 v-else>{{t('fileBox.sendFileBox')}}</h4>
|
|
||||||
</template>
|
|
||||||
<template #default>
|
|
||||||
<div v-if="route.name=='home'" style="display: flex;flex-wrap: wrap;justify-content: center">
|
|
||||||
<el-dialog
|
|
||||||
append-to-body
|
|
||||||
align-center
|
|
||||||
:title="t('fileBox.textDetail')"
|
|
||||||
width="70%"
|
|
||||||
style="height: 70%;overflow-y: scroll"
|
|
||||||
v-model="showTextDetailVisible"
|
|
||||||
>
|
|
||||||
<div style="max-width: 100%;overflow-y: scroll" v-html="renderMarkdown(nowText)"></div>
|
|
||||||
<template #footer>
|
|
||||||
<el-button type="success" @click="copyText(nowText);showTextDetailVisible = false">{{t('fileBox.copy')}}</el-button>
|
|
||||||
<el-button type="primary" @click="showTextDetailVisible = false">{{ t('fileBox.close') }}</el-button>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
<el-card v-for="(value,index) in fileStore.receiveData" :key="index" style="margin: 0.5rem">
|
|
||||||
<template #header>
|
|
||||||
<div style="display: flex;justify-content: space-between">
|
|
||||||
<h4 style="width: 6rem;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;">{{ value.name }}</h4>
|
|
||||||
<el-button size="small" type="danger" @click="fileStore.deleteReceiveData(index)">{{ t('fileBox.delete') }}</el-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div style="width: 200px;">
|
|
||||||
<div style="display: flex;justify-content: space-between">
|
|
||||||
<!-- 下载二维码白色边框 -->
|
|
||||||
<div v-if="value.name!=='Text'"
|
|
||||||
style="padding: 5px; background-color: white; border: 2px solid white; box-sizing: border-box; height: 114px;">
|
|
||||||
<qrcode-vue :value="getQrCodeUrl(value.text)" :size="100"></qrcode-vue>
|
|
||||||
</div>
|
|
||||||
<div v-else style="width: 100px;height: 100px;flex-wrap: wrap;overflow-y:scroll ">{{value.text}}</div>
|
|
||||||
<div style="display: flex;flex-direction: column;justify-content: space-around">
|
|
||||||
<el-tag size="large" style="cursor: pointer" @click="copyText(value.code)">{{ value.code }}</el-tag>
|
|
||||||
<el-tag v-if="value.name!=='Text'" size="large" type="success" style="cursor: pointer" @click="openUrl(value.text);">
|
|
||||||
{{ t('fileBox.download') }}
|
|
||||||
</el-tag>
|
|
||||||
<el-tag v-else size="large" type="success" style="cursor: pointer" @click="showTextDetail(value.text);">{{ t('fileBox.detail') }}</el-tag>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</div>
|
|
||||||
<div v-else style="display: flex;flex-wrap: wrap;justify-content: center">
|
|
||||||
<el-card v-for="(value,index) in fileStore.shareData" :key="index" style="margin: 0.5rem">
|
|
||||||
<template #header>
|
|
||||||
<div style="display: flex;justify-content: space-between">
|
|
||||||
<h4 style="width: 6rem;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;">{{ value.name }}</h4>
|
|
||||||
<el-button size="small" type="danger" @click="fileStore.deleteShareData(index)">{{ t('fileBox.delete') }}</el-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div style="width: 200px;">
|
|
||||||
<el-progress v-if="value.status!='success' && value.status!='fail'" striped :percentage="value.percentage" :text-inside="true"
|
|
||||||
:stroke-width="20"></el-progress>
|
|
||||||
<div v-else style="display: flex;justify-content: space-between">
|
|
||||||
<!-- 分享二维码白色边框 -->
|
|
||||||
<div style="padding: 5px; background-color: white; border: 2px solid white; box-sizing: border-box; height: 114px;">
|
|
||||||
<qrcode-vue :value="getQrCodeUrl(value.code)" :size="100"></qrcode-vue>
|
|
||||||
</div>
|
|
||||||
<div style="display: flex;flex-direction: column;justify-content: space-around">
|
|
||||||
<el-tag size="large" style="cursor: pointer" @click="copyText(value.code)">{{ value.code }}</el-tag>
|
|
||||||
<el-tag size="large" type="success" style="cursor: pointer" @click="copyText(value.code,1);">{{ t('fileBox.copyLink') }}
|
|
||||||
</el-tag>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-drawer>
|
|
||||||
</template>
|
|
||||||
@@ -1,183 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { UploadFilled } from '@element-plus/icons-vue'
|
|
||||||
import { ref, onMounted, onUnmounted } from 'vue'
|
|
||||||
import { request } from "@/utils/request";
|
|
||||||
import { useFileDataStore } from "@/stores/fileData";
|
|
||||||
import { useFileBoxStore } from "@/stores/fileBox";
|
|
||||||
import { useI18n } from 'vue-i18n'
|
|
||||||
import { useConfigStore } from "@/stores/config";
|
|
||||||
import { ElMessage } from "element-plus";
|
|
||||||
|
|
||||||
const { config } = useConfigStore();
|
|
||||||
const { t } = useI18n()
|
|
||||||
const fileBoxStore = useFileBoxStore();
|
|
||||||
const fileStore = useFileDataStore();
|
|
||||||
const props = defineProps({
|
|
||||||
shareData: {
|
|
||||||
type: Object,
|
|
||||||
default: () => {
|
|
||||||
return {
|
|
||||||
expire_value: 1,
|
|
||||||
expire_style: 'day',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const fileList: any = ref([])
|
|
||||||
const uploadBox: any = ref(null)
|
|
||||||
const handleOnChangeFileList = (file: any) => {
|
|
||||||
fileStore.addShareData({
|
|
||||||
'name': file.name,
|
|
||||||
'text': '',
|
|
||||||
'status': file.status,
|
|
||||||
'percentage': 0,
|
|
||||||
'size': file.size,
|
|
||||||
'type': file.raw.type,
|
|
||||||
'uid': file.uid,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleHttpRequest = (options: any) => {
|
|
||||||
fileBoxStore.showFileBox = true;
|
|
||||||
const formData = new FormData();
|
|
||||||
if (config.openUpload === 0 && localStorage.getItem('adminPassword') === null) {
|
|
||||||
fileStore.shareData.forEach((file: any) => {
|
|
||||||
if (file.uid === options.file.uid) {
|
|
||||||
ElMessage.error(t('msg.uploadClose'));
|
|
||||||
file.status = 'fail';
|
|
||||||
file.code = t('msg.fileUploadFail');
|
|
||||||
fileStore.save();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (options.file.size > config.uploadSize) {
|
|
||||||
fileStore.shareData.forEach((file: any) => {
|
|
||||||
if (file.uid === options.file.uid) {
|
|
||||||
ElMessage.error(t('msg.fileOverSize'));
|
|
||||||
file.status = 'fail';
|
|
||||||
file.code = t('msg.fileOverSize');
|
|
||||||
fileStore.save();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
formData.append('file', options.file);
|
|
||||||
formData.append('expire_value', props.shareData.expireValue);
|
|
||||||
formData.append('expire_style', props.shareData.expireStyle);
|
|
||||||
request(
|
|
||||||
{
|
|
||||||
url: "share/file/",
|
|
||||||
method: "post",
|
|
||||||
data: formData,
|
|
||||||
onUploadProgress: (event: any) => {
|
|
||||||
const percentage = Math.round((event.loaded * 100) / event.total) || 0;
|
|
||||||
fileStore.shareData.forEach((file: any) => {
|
|
||||||
if (file.uid === options.file.uid) {
|
|
||||||
file.percentage = percentage;
|
|
||||||
fileStore.save();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
).then((res: any) => {
|
|
||||||
const data = res.detail;
|
|
||||||
fileStore.shareData.forEach((file: any) => {
|
|
||||||
if (file.uid === options.file.uid) {
|
|
||||||
file.status = 'success';
|
|
||||||
file.text = data.text;
|
|
||||||
file.code = data.code;
|
|
||||||
ElMessage.success(t('msg.fileUploadSuccess'));
|
|
||||||
fileStore.save();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}).catch(() => {
|
|
||||||
fileStore.shareData.forEach((file: any) => {
|
|
||||||
if (file.uid === options.file.uid) {
|
|
||||||
file.status = 'fail';
|
|
||||||
file.code = t('msg.fileUploadFail');
|
|
||||||
ElMessage.error(t('msg.fileUploadFail'));
|
|
||||||
fileStore.save();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
function pasteLister(event: any) {
|
|
||||||
const items = event.clipboardData && event.clipboardData.items;
|
|
||||||
if (items && items.length) {
|
|
||||||
for (let i = 0; i < items.length; i++) {
|
|
||||||
if (items[i].kind === 'string') {
|
|
||||||
if (items[i].type.match(/^text\/plain/)) {
|
|
||||||
items[i].getAsString(function(str:any) {
|
|
||||||
console.log(str);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const file: any = items[i].getAsFile();
|
|
||||||
if (file) {
|
|
||||||
const uid = Date.now();
|
|
||||||
file.uid = uid;
|
|
||||||
fileStore.addShareData({
|
|
||||||
'name': file.name,
|
|
||||||
'text': '',
|
|
||||||
'status': 'ready',
|
|
||||||
'percentage': 0,
|
|
||||||
'size': file.size,
|
|
||||||
'type': file.type,
|
|
||||||
'uid': uid,
|
|
||||||
});
|
|
||||||
handleHttpRequest({
|
|
||||||
file: file,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onUnmounted(()=>{
|
|
||||||
// 清除剪切板事件
|
|
||||||
document.removeEventListener('paste', pasteLister);
|
|
||||||
})
|
|
||||||
onMounted(()=>{
|
|
||||||
document.addEventListener('paste', pasteLister);
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<el-upload
|
|
||||||
class="upload-demo"
|
|
||||||
drag
|
|
||||||
multiple
|
|
||||||
:show-file-list="false"
|
|
||||||
ref="uploadBox"
|
|
||||||
v-model:file-list="fileList"
|
|
||||||
:on-change="handleOnChangeFileList"
|
|
||||||
:http-request="handleHttpRequest"
|
|
||||||
>
|
|
||||||
<el-icon class="el-icon--upload">
|
|
||||||
<upload-filled/>
|
|
||||||
</el-icon>
|
|
||||||
<div class="el-upload__text">
|
|
||||||
{{t('send.prompt1')}}<em>{{t('send.clickUpload')}}</em>
|
|
||||||
</div>
|
|
||||||
<div class="el-upload__text" style="font-size: 10px;">{{t('send.prompt2')}}</div>
|
|
||||||
<template #tip>
|
|
||||||
<div class="el-upload__tip">
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-upload>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.el-upload {
|
|
||||||
border-radius: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-upload-dragger {
|
|
||||||
box-shadow: 3px 3px 0 0 rgba(0, 0, 0, 0.2);
|
|
||||||
border-radius: 20px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { ref } from 'vue'
|
|
||||||
import { request } from "@/utils/request";
|
|
||||||
const shareText = ref('')
|
|
||||||
import { useFileDataStore } from "@/stores/fileData";
|
|
||||||
import { useFileBoxStore } from "@/stores/fileBox";
|
|
||||||
import { ElMessage } from "element-plus";
|
|
||||||
|
|
||||||
import { useI18n } from 'vue-i18n'
|
|
||||||
import { useConfigStore } from "@/stores/config";
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
const {config} = useConfigStore();
|
|
||||||
const fileBoxStore = useFileBoxStore();
|
|
||||||
const fileStore = useFileDataStore();
|
|
||||||
const props = defineProps({
|
|
||||||
shareData: {
|
|
||||||
type: Object,
|
|
||||||
default: () => {
|
|
||||||
return {
|
|
||||||
expire_value: 1,
|
|
||||||
expire_style: 'day',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const handleSubmitShareText = ()=>{
|
|
||||||
if (shareText.value === '') {
|
|
||||||
ElMessage.warning(t('send.prompt3'));
|
|
||||||
} else if(config.openUpload === 0 && localStorage.getItem('adminPassword') === null){
|
|
||||||
ElMessage.error(t('msg.uploadClose'));
|
|
||||||
} else {
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('text', shareText.value);
|
|
||||||
formData.append('expire_value', props.shareData.expireValue);
|
|
||||||
formData.append('expire_style', props.shareData.expireStyle);
|
|
||||||
request({
|
|
||||||
'url': 'share/text/',
|
|
||||||
'method': 'post',
|
|
||||||
'data': formData,
|
|
||||||
}).then((res: any) => {
|
|
||||||
const data = res.detail;
|
|
||||||
fileBoxStore.showFileBox = true;
|
|
||||||
fileStore.addShareData({
|
|
||||||
'name': t('send.textShare'),
|
|
||||||
'text': data.text,
|
|
||||||
'code': data.code,
|
|
||||||
'status': 'success',
|
|
||||||
'percentage': 100,
|
|
||||||
'size': shareText.value.length,
|
|
||||||
'type': 'text',
|
|
||||||
'uid': Date.now(),
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div style="position: relative">
|
|
||||||
<el-input
|
|
||||||
:placeholder="t('send.prompt3')"
|
|
||||||
v-model="shareText"
|
|
||||||
type="textarea"
|
|
||||||
:rows="9"
|
|
||||||
:input-style="{'border-radius':'20px','border':'1px dashed var(--el-border-color)','box-shadow':'none'}"
|
|
||||||
>
|
|
||||||
</el-input>
|
|
||||||
<el-button @click="handleSubmitShareText" style="position: absolute;right: 0;bottom: 0;border-radius: 20px 0 20px 0;margin: 1px;background: rgba(255,255,255,0.2)" size="large">{{t('send.share')}}</el-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
export default {
|
|
||||||
send: {
|
|
||||||
disclaimers: 'Terms and Conditions',
|
|
||||||
prompt1: 'Drag and drop text or files here, or ',
|
|
||||||
prompt2: 'Days <7 or limited number of times (deleted after 24h)',
|
|
||||||
prompt3: 'Please enter the text you want to send, supports MarkDown format',
|
|
||||||
share: 'Share',
|
|
||||||
textShare: 'Text Share',
|
|
||||||
clickUpload: 'Click to Upload',
|
|
||||||
pleaseInputExpireValue: 'Please enter the validity period',
|
|
||||||
expireStyle: 'Expiration Method',
|
|
||||||
expireData: {
|
|
||||||
day: 'Days',
|
|
||||||
hour: 'Hours',
|
|
||||||
forever: 'Permanent',
|
|
||||||
minute: 'Minutes',
|
|
||||||
count: 'Times'
|
|
||||||
},
|
|
||||||
expireValue: {
|
|
||||||
day: 'Days',
|
|
||||||
hour: 'Hours',
|
|
||||||
minute: 'Minutes',
|
|
||||||
count: 'Times'
|
|
||||||
},
|
|
||||||
fileType: {
|
|
||||||
file: 'File',
|
|
||||||
text: 'Text'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
fileBox: {
|
|
||||||
copySuccess: 'Copy Successful',
|
|
||||||
inputNotEmpty: 'Please enter a five-digit retrieval code',
|
|
||||||
sendFileBox: 'Outgoing Box',
|
|
||||||
ok: 'OK',
|
|
||||||
receiveFileBox: 'Incoming Box',
|
|
||||||
textDetail: 'Text Details',
|
|
||||||
copy: 'Copy',
|
|
||||||
close: 'Close',
|
|
||||||
delete: 'Delete',
|
|
||||||
download: 'Click to Download',
|
|
||||||
detail: 'View Details',
|
|
||||||
copyLink: 'Copy Link',
|
|
||||||
},
|
|
||||||
admin: {
|
|
||||||
about: {
|
|
||||||
source1: 'This project is open-source on Github: ',
|
|
||||||
source2: 'FileCodeBox'
|
|
||||||
},
|
|
||||||
settings: {
|
|
||||||
name: 'Website Name',
|
|
||||||
description: 'Website Description',
|
|
||||||
uploadlimit: 'Upload Limit',
|
|
||||||
explain: 'Interface Instructions',
|
|
||||||
errorlimit: 'Error Limit',
|
|
||||||
keywords: 'Keywords',
|
|
||||||
themeSelect: 'Theme Selection',
|
|
||||||
notify_title: 'Notification Title',
|
|
||||||
notify_content: 'Notification Content',
|
|
||||||
background: 'Background Image',
|
|
||||||
max_save_seconds: 'Maximum Save Time',
|
|
||||||
maxSaveSecondsNote: 'Maximum save time, unit: (seconds), default is 0 which means 7 days',
|
|
||||||
admin_token: 'Admin Password',
|
|
||||||
uploadSize: 'File Size',
|
|
||||||
expireStyle: 'Expiration Method',
|
|
||||||
uploadSizeNote: 'Maximum file size, unit: (Bytes), 1mb = 1 * 1024 * 1024',
|
|
||||||
showAdmin: {
|
|
||||||
open: 'Open',
|
|
||||||
close: 'Close',
|
|
||||||
note: 'Is the backend entrance displayed at the bottom of the page?',
|
|
||||||
},
|
|
||||||
openUpload: {
|
|
||||||
title: 'Enable Upload',
|
|
||||||
open: 'Enable Guest Upload',
|
|
||||||
close: 'Disable Guest Upload',
|
|
||||||
note: 'After disabling, you need to log in to the backend to upload',
|
|
||||||
},
|
|
||||||
file_storage: {
|
|
||||||
title: 'Storage Engine',
|
|
||||||
local: 'Local Storage',
|
|
||||||
s3: 'S3 Storage',
|
|
||||||
note: 'After updating, you need to restart FileCodeBox',
|
|
||||||
},
|
|
||||||
mei: 'Every',
|
|
||||||
minute: 'Minutes',
|
|
||||||
upload: 'Upload',
|
|
||||||
files: 'Files',
|
|
||||||
allow: 'Allow',
|
|
||||||
errors: 'Errors',
|
|
||||||
save: 'Save',
|
|
||||||
saveSuccess: 'Save Successful',
|
|
||||||
},
|
|
||||||
fileView: {
|
|
||||||
code: 'Retrieval Code',
|
|
||||||
prefix: 'File Prefix',
|
|
||||||
suffix: 'File Suffix',
|
|
||||||
text: 'Text',
|
|
||||||
used_count: 'Times Used',
|
|
||||||
expired_count: 'Available Times',
|
|
||||||
size: 'File Size',
|
|
||||||
expired_at: 'Expiration Time',
|
|
||||||
file_path: 'File Path',
|
|
||||||
action: 'Action',
|
|
||||||
delete: 'Delete',
|
|
||||||
delete_success: 'Delete Successful',
|
|
||||||
forever: 'Permanent',
|
|
||||||
unlimited_count: 'Unlimited Times',
|
|
||||||
download: 'Download',
|
|
||||||
download_fail: 'File save failed, please try again later~',
|
|
||||||
},
|
|
||||||
menu: {
|
|
||||||
fileManage: 'File Management',
|
|
||||||
systemSetting: 'System Settings',
|
|
||||||
about: 'About Us',
|
|
||||||
color: 'Color Mode',
|
|
||||||
send: 'Send',
|
|
||||||
local:'Local File',
|
|
||||||
receive: 'Receive',
|
|
||||||
signout: 'Sign Out',
|
|
||||||
},
|
|
||||||
login: {
|
|
||||||
managePassword: 'Admin Password',
|
|
||||||
passwordNotEmpty: 'Password cannot be empty',
|
|
||||||
login: 'Login',
|
|
||||||
loginSuccess: 'Login Successful',
|
|
||||||
loginError: 'Login Failed',
|
|
||||||
},
|
|
||||||
local: {
|
|
||||||
Name: '文件',
|
|
||||||
Expire: '过期',
|
|
||||||
Cancel: '取消',
|
|
||||||
Confirm: '确定',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
msg: {
|
|
||||||
fileOverSize: 'File Too Large',
|
|
||||||
fileUploadFail: 'Upload Failed',
|
|
||||||
fileUploadSuccess: 'Upload Successful',
|
|
||||||
uploadClose: 'Guest upload is closed on this site',
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
export default {
|
|
||||||
send: {
|
|
||||||
disclaimers: 'Términos y condiciones',
|
|
||||||
prompt1: 'Arrastre, pegue el texto, archivos aquí, o ',
|
|
||||||
prompt2: 'Días<7 o límite de veces (elimina después de 24h)',
|
|
||||||
prompt3: 'Ingrese el texto que desea enviar, admite formato MarkDown',
|
|
||||||
share: 'Compartir',
|
|
||||||
textShare: 'Compartir texto',
|
|
||||||
clickUpload: 'Hacer clic para subir',
|
|
||||||
pleaseInputExpireValue: 'Ingrese el período de validez',
|
|
||||||
expireStyle: 'Método de caducidad',
|
|
||||||
expireData: {
|
|
||||||
day: 'Días',
|
|
||||||
hour: 'Horas',
|
|
||||||
forever: 'Para siempre',
|
|
||||||
minute: 'Minutos',
|
|
||||||
count: 'Veces'
|
|
||||||
},
|
|
||||||
expireValue: {
|
|
||||||
day: 'Días',
|
|
||||||
hour: 'Horas',
|
|
||||||
minute: 'Minutos',
|
|
||||||
count: 'Veces'
|
|
||||||
},
|
|
||||||
fileType: {
|
|
||||||
file: 'Archivo',
|
|
||||||
text: 'Texto'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
fileBox: {
|
|
||||||
copySuccess: 'Copiado exitosamente',
|
|
||||||
inputNotEmpty: 'Ingrese un código de recogida de cinco dígitos',
|
|
||||||
sendFileBox: 'Buzón de envíos',
|
|
||||||
ok: 'Aceptar',
|
|
||||||
receiveFileBox: 'Buzón de recepción',
|
|
||||||
textDetail: 'Detalles del texto',
|
|
||||||
copy: 'Copiar',
|
|
||||||
close: 'Cerrar',
|
|
||||||
delete: 'Eliminar',
|
|
||||||
download: 'Hacer clic para descargar',
|
|
||||||
detail: 'Ver detalles',
|
|
||||||
copyLink: 'Copiar enlace',
|
|
||||||
},
|
|
||||||
admin: {
|
|
||||||
about: {
|
|
||||||
source1: 'Proyecto de código abierto en Github:',
|
|
||||||
source2: 'FileCodeBox'
|
|
||||||
},
|
|
||||||
settings: {
|
|
||||||
name: 'Nombre del sitio web',
|
|
||||||
description: 'Descripción del sitio web',
|
|
||||||
uploadlimit: 'Límite de subida',
|
|
||||||
explain: 'Descripción de la interfaz',
|
|
||||||
errorlimit: 'Límite de errores',
|
|
||||||
keywords: 'Palabras clave',
|
|
||||||
themeSelect: 'Selección de tema',
|
|
||||||
notify_title: 'Título de notificación',
|
|
||||||
notify_content: 'Contenido de notificación',
|
|
||||||
background: 'Imagen de fondo',
|
|
||||||
max_save_seconds: 'Tiempo máximo de almacenamiento',
|
|
||||||
maxSaveSecondsNote: 'Tiempo máximo de almacenamiento, en segundos. El valor predeterminado es 0, que equivale a 7 días',
|
|
||||||
admin_token: 'Contraseña de administración',
|
|
||||||
uploadSize: 'Tamaño de archivo',
|
|
||||||
expireStyle: 'Método de caducidad',
|
|
||||||
uploadSizeNote: 'Tamaño máximo de archivo, en bytes. 1mb=1 * 1024 * 1024',
|
|
||||||
openUpload: {
|
|
||||||
title: 'Habilitar subida',
|
|
||||||
open: 'Habilitar subida para visitantes',
|
|
||||||
close: 'Deshabilitar subida para visitantes',
|
|
||||||
note: 'Después de deshabilitar, es necesario iniciar sesión en el backend para poder subir archivos',
|
|
||||||
},
|
|
||||||
showAdmin: {
|
|
||||||
open: 'abierto',
|
|
||||||
close: 'cierre',
|
|
||||||
note: 'si mostrar la entrada de fondo en la parte inferior de la página',
|
|
||||||
},
|
|
||||||
file_storage: {
|
|
||||||
title: 'Motor de almacenamiento',
|
|
||||||
local: 'Almacenamiento local',
|
|
||||||
s3: 'Almacenamiento S3',
|
|
||||||
note: 'Es necesario reiniciar FileCodeBox después de actualizar',
|
|
||||||
},
|
|
||||||
mei: 'Cada',
|
|
||||||
minute: 'Minuto',
|
|
||||||
upload: 'Subir',
|
|
||||||
files: 'Archivos',
|
|
||||||
allow: 'Permitir',
|
|
||||||
errors: 'Errores',
|
|
||||||
save: 'Guardar',
|
|
||||||
saveSuccess: 'Guardado exitosamente',
|
|
||||||
},
|
|
||||||
fileView: {
|
|
||||||
code: 'Código de recogida',
|
|
||||||
prefix: 'Prefijo de archivo',
|
|
||||||
suffix: 'Sufijo de archivo',
|
|
||||||
text: 'Texto',
|
|
||||||
used_count: 'Número de veces utilizado',
|
|
||||||
expired_count: 'Número de veces disponible',
|
|
||||||
size: 'Tamaño de archivo',
|
|
||||||
expired_at: 'Fecha de caducidad',
|
|
||||||
file_path: 'Ruta del archivo',
|
|
||||||
action: 'Acción',
|
|
||||||
delete: 'Eliminar',
|
|
||||||
delete_success: 'Eliminado exitosamente',
|
|
||||||
forever: 'Válido para siempre',
|
|
||||||
unlimited_count: 'Número de veces ilimitado',
|
|
||||||
download: 'Descargar',
|
|
||||||
download_fail: 'Error al guardar el archivo, intente nuevamente más tarde~',
|
|
||||||
},
|
|
||||||
menu: {
|
|
||||||
fileManage: 'Gestión de archivos',
|
|
||||||
systemSetting: 'Configuración del sistema',
|
|
||||||
about: 'Sobre nosotros',
|
|
||||||
color: 'Modo de color',
|
|
||||||
send: 'Enviar',
|
|
||||||
local:'Archivo Local',
|
|
||||||
receive: 'Recibir',
|
|
||||||
signout: 'Cerrar sesión',
|
|
||||||
},
|
|
||||||
login: {
|
|
||||||
managePassword: 'Contraseña de administración',
|
|
||||||
passwordNotEmpty: 'La contraseña no puede estar vacía',
|
|
||||||
login: 'Iniciar sesión',
|
|
||||||
loginSuccess: 'Inicio de sesión exitoso',
|
|
||||||
loginError: 'Error de inicio de sesión',
|
|
||||||
},
|
|
||||||
local: {
|
|
||||||
Name: '文件',
|
|
||||||
Expire: '过期',
|
|
||||||
Cancel: '取消',
|
|
||||||
Confirm: '确定',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
msg: {
|
|
||||||
fileOverSize: 'Archivo demasiado grande',
|
|
||||||
fileUploadFail: 'Error al subir',
|
|
||||||
fileUploadSuccess: 'Subida exitosa',
|
|
||||||
uploadClose: 'El sitio ha deshabilitado la subida para visitantes',
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import { createI18n } from 'vue-i18n' //引入vue-i18n组件
|
|
||||||
import messages from './index'
|
|
||||||
let language = (
|
|
||||||
navigator.language || 'zh_cn'
|
|
||||||
).toLowerCase().replace(/-/, '_');
|
|
||||||
if (['zh','zh_cn','en','es','zh_tw'].indexOf(language) === -1) {
|
|
||||||
language = 'zh_cn'
|
|
||||||
}
|
|
||||||
const lang = (localStorage.getItem('language') || language);
|
|
||||||
const i18n = createI18n({
|
|
||||||
silentTranslationWarn: true,
|
|
||||||
globalInjection: true,
|
|
||||||
legacy: false,
|
|
||||||
locale: lang,
|
|
||||||
messages,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default i18n
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import zh_cn from './zh_cn';
|
|
||||||
import zh from './zh';
|
|
||||||
import es from './es';
|
|
||||||
import en from './en';
|
|
||||||
import zh_tw from './zh_tw'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
zh_cn,
|
|
||||||
en,
|
|
||||||
zh_tw,
|
|
||||||
zh,
|
|
||||||
es
|
|
||||||
}
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
export default {
|
|
||||||
send: {
|
|
||||||
disclaimers:'使用须知',
|
|
||||||
prompt1: '将文字、文件拖、粘贴到此处,或 ',
|
|
||||||
prompt2: '天数<7或限制次数(24h后删除)',
|
|
||||||
prompt3: '请输入您要寄出的文本,支持MarkDown格式',
|
|
||||||
share: '分享',
|
|
||||||
textShare: '文本分享',
|
|
||||||
clickUpload: '点击上传',
|
|
||||||
pleaseInputExpireValue: '请输入有效期',
|
|
||||||
expireStyle: '过期方式',
|
|
||||||
expireData: {
|
|
||||||
day: '天数',
|
|
||||||
hour: '小时',
|
|
||||||
forever: '永久',
|
|
||||||
minute: '分钟',
|
|
||||||
count: '次数'
|
|
||||||
},
|
|
||||||
expireValue: {
|
|
||||||
day: '天',
|
|
||||||
hour: '时',
|
|
||||||
minute: '分',
|
|
||||||
count: '次'
|
|
||||||
},
|
|
||||||
fileType: {
|
|
||||||
file: '文件',
|
|
||||||
text: '文本'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
fileBox: {
|
|
||||||
copySuccess: '复制成功',
|
|
||||||
inputNotEmpty: '请输入五位取件码',
|
|
||||||
sendFileBox: '寄件箱',
|
|
||||||
ok:'确定',
|
|
||||||
receiveFileBox: '收件箱',
|
|
||||||
textDetail: '文本详情',
|
|
||||||
copy: '复 制',
|
|
||||||
close: '关 闭',
|
|
||||||
delete: '删 除',
|
|
||||||
download: '点 击 下 载',
|
|
||||||
detail: '查 看 详 情',
|
|
||||||
copyLink: '复制链接',
|
|
||||||
},
|
|
||||||
admin: {
|
|
||||||
about: {
|
|
||||||
source1: '本项目开源于Github:',
|
|
||||||
source2: 'FileCodeBox'
|
|
||||||
},
|
|
||||||
settings: {
|
|
||||||
name: '网站名称',
|
|
||||||
description: '网站描述',
|
|
||||||
uploadlimit: '上传限制',
|
|
||||||
explain:'界面说明',
|
|
||||||
errorlimit: '错误限制',
|
|
||||||
keywords: '关键词',
|
|
||||||
themeSelect: '主题选择',
|
|
||||||
notify_title: '通知标题',
|
|
||||||
notify_content: '通知内容',
|
|
||||||
background: '背景图片',
|
|
||||||
max_save_seconds: '最长保存',
|
|
||||||
maxSaveSecondsNote: '最长保存时间,单位:(秒),默认为0则为7天',
|
|
||||||
admin_token: '管理密码',
|
|
||||||
uploadSize: '文件大小',
|
|
||||||
expireStyle: '过期方式',
|
|
||||||
uploadSizeNote: '最大文件大小,单位:(Bytes),1mb=1 * 1024 * 1024',
|
|
||||||
openUpload: {
|
|
||||||
title: '开启上传',
|
|
||||||
open: '开启游客上传',
|
|
||||||
close: '关闭游客上传',
|
|
||||||
note: '关闭之后需要登录后台方可上传',
|
|
||||||
},
|
|
||||||
showAdmin:{
|
|
||||||
open: '开启',
|
|
||||||
close: '关闭',
|
|
||||||
note: '是否在页面底部显示后台入口',
|
|
||||||
},
|
|
||||||
file_storage: {
|
|
||||||
title: '存储引擎',
|
|
||||||
local: '本地存储',
|
|
||||||
s3: 'S3存储',
|
|
||||||
note: '更新后需要重启FileCodeBox',
|
|
||||||
},
|
|
||||||
mei: '每',
|
|
||||||
minute: '分钟',
|
|
||||||
upload: '上传',
|
|
||||||
files: '个文件',
|
|
||||||
allow: '允许',
|
|
||||||
errors: '次错误',
|
|
||||||
save: '保存',
|
|
||||||
saveSuccess: '保存成功',
|
|
||||||
},
|
|
||||||
fileView: {
|
|
||||||
code: '取件码',
|
|
||||||
prefix: '文件前缀',
|
|
||||||
suffix: '文件后缀',
|
|
||||||
text: '文本',
|
|
||||||
used_count: '已使用次数',
|
|
||||||
expired_count: '可用次数',
|
|
||||||
size: '文件大小',
|
|
||||||
expired_at: '过期时间',
|
|
||||||
file_path: '文件路径',
|
|
||||||
action: '操作',
|
|
||||||
delete: '删除',
|
|
||||||
delete_success: '删除成功',
|
|
||||||
forever: '永久有效',
|
|
||||||
unlimited_count: '不限次数',
|
|
||||||
download: '下载',
|
|
||||||
download_fail: '文件保存失败,请稍后再试~',
|
|
||||||
},
|
|
||||||
menu: {
|
|
||||||
fileManage: '文件管理',
|
|
||||||
systemSetting: '系统设置',
|
|
||||||
about: '关于我们',
|
|
||||||
color: '颜色模式',
|
|
||||||
local:'本地文件',
|
|
||||||
send: '寄件',
|
|
||||||
receive: '收件',
|
|
||||||
signout: '退出登录',
|
|
||||||
},
|
|
||||||
login: {
|
|
||||||
managePassword: '管理密码',
|
|
||||||
passwordNotEmpty: '密码不能为空',
|
|
||||||
login: '登 录',
|
|
||||||
loginSuccess: '登录成功',
|
|
||||||
loginError: '登录失败',
|
|
||||||
},
|
|
||||||
local: {
|
|
||||||
Name: '文件',
|
|
||||||
Expire: '过期',
|
|
||||||
Cancel: '取消',
|
|
||||||
Confirm: '确定',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
msg:{
|
|
||||||
fileOverSize: '文件过大',
|
|
||||||
fileUploadFail: '上传失败',
|
|
||||||
fileUploadSuccess: '上传成功',
|
|
||||||
uploadClose: '本站已关闭游客上传',
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
export default {
|
|
||||||
send: {
|
|
||||||
disclaimers:'使用须知',
|
|
||||||
prompt1: '将文字、文件拖、粘贴到此处,或 ',
|
|
||||||
prompt2: '天数<7或限制次数(24h后删除)',
|
|
||||||
prompt3: '请输入您要寄出的文本,支持MarkDown格式',
|
|
||||||
share: '分享',
|
|
||||||
textShare: '文本分享',
|
|
||||||
clickUpload: '点击上传',
|
|
||||||
pleaseInputExpireValue: '请输入有效期',
|
|
||||||
expireStyle: '过期方式',
|
|
||||||
expireData: {
|
|
||||||
day: '天数',
|
|
||||||
hour: '小时',
|
|
||||||
forever: '永久',
|
|
||||||
minute: '分钟',
|
|
||||||
count: '次数'
|
|
||||||
},
|
|
||||||
expireValue: {
|
|
||||||
day: '天',
|
|
||||||
hour: '时',
|
|
||||||
minute: '分',
|
|
||||||
count: '次'
|
|
||||||
},
|
|
||||||
fileType: {
|
|
||||||
file: '文件',
|
|
||||||
text: '文本'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
fileBox: {
|
|
||||||
copySuccess: '复制成功',
|
|
||||||
inputNotEmpty: '请输入五位取件码',
|
|
||||||
sendFileBox: '寄件箱',
|
|
||||||
ok:'确定',
|
|
||||||
receiveFileBox: '收件箱',
|
|
||||||
textDetail: '文本详情',
|
|
||||||
copy: '复 制',
|
|
||||||
close: '关 闭',
|
|
||||||
delete: '删 除',
|
|
||||||
download: '点 击 下 载',
|
|
||||||
detail: '查 看 详 情',
|
|
||||||
copyLink: '复制链接',
|
|
||||||
},
|
|
||||||
admin: {
|
|
||||||
about: {
|
|
||||||
source1: '本项目开源于Github:',
|
|
||||||
source2: 'FileCodeBox'
|
|
||||||
},
|
|
||||||
settings: {
|
|
||||||
name: '网站名称',
|
|
||||||
description: '网站描述',
|
|
||||||
uploadlimit: '上传限制',
|
|
||||||
explain:'界面说明',
|
|
||||||
errorlimit: '错误限制',
|
|
||||||
keywords: '关键词',
|
|
||||||
themeSelect: '主题选择',
|
|
||||||
notify_title: '通知标题',
|
|
||||||
notify_content: '通知内容',
|
|
||||||
background: '背景图片',
|
|
||||||
max_save_seconds: '最长保存',
|
|
||||||
maxSaveSecondsNote: '最长保存时间,单位:(秒),默认为0则为7天',
|
|
||||||
admin_token: '管理密码',
|
|
||||||
uploadSize: '文件大小',
|
|
||||||
expireStyle: '过期方式',
|
|
||||||
showAdminAddr: '显示后台',
|
|
||||||
showAdmin:{
|
|
||||||
open: '开启',
|
|
||||||
close: '关闭',
|
|
||||||
note: '是否在页面底部显示后台入口',
|
|
||||||
},
|
|
||||||
robotsText: 'robots.txt',
|
|
||||||
uploadSizeNote: '最大文件大小,单位:(Bytes),1mb=1 * 1024 * 1024',
|
|
||||||
openUpload: {
|
|
||||||
title: '开启上传',
|
|
||||||
open: '开启游客上传',
|
|
||||||
close: '关闭游客上传',
|
|
||||||
note: '关闭之后需要登录后台方可上传',
|
|
||||||
},
|
|
||||||
file_storage: {
|
|
||||||
title: '存储引擎',
|
|
||||||
local: '本地存储',
|
|
||||||
s3: 'S3存储',
|
|
||||||
note: '更新后需要重启FileCodeBox',
|
|
||||||
},
|
|
||||||
mei: '每',
|
|
||||||
minute: '分钟',
|
|
||||||
upload: '上传',
|
|
||||||
files: '个文件',
|
|
||||||
allow: '允许',
|
|
||||||
errors: '次错误',
|
|
||||||
save: '保存',
|
|
||||||
saveSuccess: '保存成功',
|
|
||||||
},
|
|
||||||
fileView: {
|
|
||||||
code: '取件码',
|
|
||||||
prefix: '文件前缀',
|
|
||||||
suffix: '文件后缀',
|
|
||||||
text: '文本',
|
|
||||||
used_count: '已使用次数',
|
|
||||||
expired_count: '可用次数',
|
|
||||||
size: '文件大小',
|
|
||||||
expired_at: '过期时间',
|
|
||||||
file_path: '文件路径',
|
|
||||||
action: '操作',
|
|
||||||
delete: '删除',
|
|
||||||
delete_success: '删除成功',
|
|
||||||
forever: '永久有效',
|
|
||||||
unlimited_count: '不限次数',
|
|
||||||
download: '下载',
|
|
||||||
download_fail: '文件保存失败,请稍后再试~',
|
|
||||||
},
|
|
||||||
menu: {
|
|
||||||
fileManage: '文件管理',
|
|
||||||
systemSetting: '系统设置',
|
|
||||||
about: '关于我们',
|
|
||||||
local:'本地文件',
|
|
||||||
color: '颜色模式',
|
|
||||||
send: '寄件',
|
|
||||||
receive: '收件',
|
|
||||||
signout: '退出登录',
|
|
||||||
},
|
|
||||||
login: {
|
|
||||||
managePassword: '管理密码',
|
|
||||||
passwordNotEmpty: '密码不能为空',
|
|
||||||
login: '登 录',
|
|
||||||
loginSuccess: '登录成功',
|
|
||||||
loginError: '登录失败',
|
|
||||||
},
|
|
||||||
local: {
|
|
||||||
Name: '文件',
|
|
||||||
Expire: '过期',
|
|
||||||
Cancel: '取消',
|
|
||||||
Confirm: '确定',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
msg:{
|
|
||||||
fileOverSize: '文件过大',
|
|
||||||
fileUploadFail: '上传失败',
|
|
||||||
fileUploadSuccess: '上传成功',
|
|
||||||
uploadClose: '本站已关闭游客上传',
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
export default {
|
|
||||||
send: {
|
|
||||||
disclaimers:'使用須知',
|
|
||||||
prompt1: '將文字、文件拖、貼到此處,或 ',
|
|
||||||
prompt2: '天數<7或限制次數(24h後刪除)',
|
|
||||||
prompt3: '請輸入您要寄出的文本,支持MarkDown格式',
|
|
||||||
share: '分享',
|
|
||||||
textShare: '文本分享',
|
|
||||||
clickUpload: '點擊上傳',
|
|
||||||
pleaseInputExpireValue: '請輸入有效期',
|
|
||||||
expireStyle: '過期方式',
|
|
||||||
expireData: {
|
|
||||||
day: '天數',
|
|
||||||
hour: '小時',
|
|
||||||
forever: '永久',
|
|
||||||
minute: '分鐘',
|
|
||||||
count: '次數'
|
|
||||||
},
|
|
||||||
expireValue: {
|
|
||||||
day: '天',
|
|
||||||
hour: '時',
|
|
||||||
minute: '分',
|
|
||||||
count: '次'
|
|
||||||
},
|
|
||||||
fileType: {
|
|
||||||
file: '文件',
|
|
||||||
text: '文本'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
fileBox: {
|
|
||||||
copySuccess: '複製成功',
|
|
||||||
inputNotEmpty: '請輸入五位取件碼',
|
|
||||||
sendFileBox: '寄件箱',
|
|
||||||
ok:'確定',
|
|
||||||
receiveFileBox: '收件箱',
|
|
||||||
textDetail: '文本詳情',
|
|
||||||
copy: '複 製',
|
|
||||||
close: '關 閉',
|
|
||||||
delete: '刪 除',
|
|
||||||
download: '點 擊 下 載',
|
|
||||||
detail: '查 看 詳 情',
|
|
||||||
copyLink: '複製連結',
|
|
||||||
},
|
|
||||||
admin: {
|
|
||||||
about: {
|
|
||||||
source1: '本項目開源於Github:',
|
|
||||||
source2: 'FileCodeBox'
|
|
||||||
},
|
|
||||||
settings: {
|
|
||||||
name: '網站名稱',
|
|
||||||
description: '網站描述',
|
|
||||||
uploadlimit: '上傳限制',
|
|
||||||
explain: '界面說明',
|
|
||||||
errorlimit: '錯誤限制',
|
|
||||||
keywords: '關鍵詞',
|
|
||||||
themeSelect: 'Theme選擇',
|
|
||||||
notify_title: '通知標題',
|
|
||||||
notify_content: '通知內容',
|
|
||||||
background: '背景圖片',
|
|
||||||
max_save_seconds: '最長保存',
|
|
||||||
maxSaveSecondsNote: '最長保存時間,單位:(秒),默認為0則為7天',
|
|
||||||
admin_token: '管理密碼',
|
|
||||||
uploadSize: '文件大小',
|
|
||||||
expireStyle: '過期方式',
|
|
||||||
uploadSizeNote: '最大文件大小,單位:(Bytes),1mb=1 * 1024 * 1024',
|
|
||||||
showAdmin: {
|
|
||||||
open: '開啟',
|
|
||||||
close: '關閉',
|
|
||||||
note: '是否在頁面底部顯示後臺入口',
|
|
||||||
},
|
|
||||||
openUpload: {
|
|
||||||
title: '開啟上傳',
|
|
||||||
open: '開啟遊客上傳',
|
|
||||||
close: '關閉遊客上傳',
|
|
||||||
note: '關閉之後需要登錄後臺方可上傳',
|
|
||||||
},
|
|
||||||
file_storage: {
|
|
||||||
title: '存儲引擎',
|
|
||||||
local: '本地存儲',
|
|
||||||
s3: 'S3存儲',
|
|
||||||
note: '更新後需要重啟FileCodeBox',
|
|
||||||
},
|
|
||||||
mei: '每',
|
|
||||||
minute: '分鐘',
|
|
||||||
upload: '上傳',
|
|
||||||
files: '個文件',
|
|
||||||
allow: '允許',
|
|
||||||
errors: '次錯誤',
|
|
||||||
save: '保存',
|
|
||||||
saveSuccess: '保存成功',
|
|
||||||
},
|
|
||||||
fileView: {
|
|
||||||
code: '取件碼',
|
|
||||||
prefix: '文件前綴',
|
|
||||||
suffix: '文件後綴',
|
|
||||||
text: '文本',
|
|
||||||
used_count: '已使用次數',
|
|
||||||
expired_count: '可用次數',
|
|
||||||
size: '文件大小',
|
|
||||||
expired_at: '過期時間',
|
|
||||||
file_path: '文件路徑',
|
|
||||||
action: '操作',
|
|
||||||
delete: '刪除',
|
|
||||||
delete_success: '刪除成功',
|
|
||||||
forever: '永久有效',
|
|
||||||
unlimited_count: '不限次數',
|
|
||||||
download: '下載',
|
|
||||||
download_fail: '文件保存失敗,請稍後再試~',
|
|
||||||
},
|
|
||||||
menu: {
|
|
||||||
fileManage: '文件管理',
|
|
||||||
systemSetting: '系統設置',
|
|
||||||
about: '關於我們',
|
|
||||||
color: '顏色模式',
|
|
||||||
send: '寄件',
|
|
||||||
local:'本地文件',
|
|
||||||
receive: '收件',
|
|
||||||
signout: '退出登錄',
|
|
||||||
},
|
|
||||||
login: {
|
|
||||||
managePassword: '管理密碼',
|
|
||||||
passwordNotEmpty: '密碼不能為空',
|
|
||||||
login: '登 錄',
|
|
||||||
loginSuccess: '登錄成功',
|
|
||||||
loginError: '登錄失敗',
|
|
||||||
},local: {
|
|
||||||
Name: '文件',
|
|
||||||
Expire: '過期',
|
|
||||||
Cancel: '取消',
|
|
||||||
Confirm: '確定',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
msg:{
|
|
||||||
fileOverSize: '文件過大',
|
|
||||||
fileUploadFail: '上傳失敗',
|
|
||||||
fileUploadSuccess: '上傳成功',
|
|
||||||
uploadClose: '本站已關閉遊客上傳',
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import './assets/main.css'
|
|
||||||
import 'element-plus/dist/index.css'
|
|
||||||
import 'element-plus/theme-chalk/dark/css-vars.css'
|
|
||||||
import { createApp } from 'vue'
|
|
||||||
import { createPinia } from 'pinia'
|
|
||||||
import App from './App.vue'
|
|
||||||
import router from './router'
|
|
||||||
import i18n from "./locals/i18n";
|
|
||||||
|
|
||||||
const app = createApp(App)
|
|
||||||
|
|
||||||
app.use(createPinia())
|
|
||||||
app.use(i18n)
|
|
||||||
app.use(router)
|
|
||||||
app.mount('#app')
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
import { createRouter, createWebHashHistory } from 'vue-router';
|
|
||||||
|
|
||||||
const router = createRouter({
|
|
||||||
history: createWebHashHistory(import.meta.env.BASE_URL),
|
|
||||||
routes: [
|
|
||||||
{
|
|
||||||
path: '/',
|
|
||||||
name: 'home',
|
|
||||||
component: () => import('@/views/Share/HomeView.vue'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/send',
|
|
||||||
name: 'send',
|
|
||||||
component: () => import('@/views/Share/SendView.vue'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/admin',
|
|
||||||
name: 'admin',
|
|
||||||
component: () => import('@/views/Admin/AdminView.vue'),
|
|
||||||
children:[
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
name: 'file',
|
|
||||||
component: () => import('@/views/Admin/FileView.vue'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'setting',
|
|
||||||
name: 'setting',
|
|
||||||
component: () => import('@/views/Admin/SettingView.vue'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'local',
|
|
||||||
name: 'local',
|
|
||||||
component: () => import('@/views/Admin/LocalView.vue'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'about',
|
|
||||||
name: 'about',
|
|
||||||
component: () => import('@/views/Admin/AboutView.vue'),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
export default router;
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { defineStore } from 'pinia';
|
|
||||||
import {ref} from "vue";
|
|
||||||
|
|
||||||
export const useAdminData = defineStore('adminData', () => {
|
|
||||||
const adminPassword = ref(localStorage.getItem('adminPassword') || '');
|
|
||||||
const isAdmin = ref(!!localStorage.getItem('adminPassword'));
|
|
||||||
function updateAdminPwd(pwd: string) {
|
|
||||||
adminPassword.value = pwd;
|
|
||||||
isAdmin.value = true;
|
|
||||||
localStorage.setItem('adminPassword', pwd);
|
|
||||||
}
|
|
||||||
return { adminPassword,updateAdminPwd,isAdmin };
|
|
||||||
});
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
import { defineStore } from 'pinia';
|
|
||||||
import {ref} from "vue";
|
|
||||||
|
|
||||||
export const useConfigStore = defineStore('config', () => {
|
|
||||||
const config:any = ref(JSON.parse(localStorage.getItem('config') || '{}') || {}); // 配置
|
|
||||||
return { config };
|
|
||||||
});
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
import { defineStore } from 'pinia';
|
|
||||||
import {ref} from "vue";
|
|
||||||
|
|
||||||
export const useFileBoxStore = defineStore('fileBox', () => {
|
|
||||||
const showFileBox = ref(false);
|
|
||||||
return { showFileBox };
|
|
||||||
});
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import { defineStore } from 'pinia';
|
|
||||||
import {reactive} from "vue";
|
|
||||||
|
|
||||||
export const useFileDataStore = defineStore('fileData', () => {
|
|
||||||
const receiveData = reactive(JSON.parse(localStorage.getItem('receiveData')||'[]') || []); // 接收的数据
|
|
||||||
const shareData = reactive(JSON.parse(localStorage.getItem('shareData')||'[]') || []); // 接收的数据
|
|
||||||
function save() {
|
|
||||||
localStorage.setItem('receiveData', JSON.stringify(receiveData));
|
|
||||||
localStorage.setItem('shareData', JSON.stringify(shareData));
|
|
||||||
}
|
|
||||||
function addReceiveData(data:any) {
|
|
||||||
receiveData.unshift(data);
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
|
|
||||||
function addShareData(data:any) {
|
|
||||||
shareData.unshift(data);
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteReceiveData(index: number) {
|
|
||||||
receiveData.splice(index, 1);
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteShareData(index: number) {
|
|
||||||
shareData.splice(index, 1);
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
return { receiveData, shareData, save, addShareData, addReceiveData, deleteReceiveData, deleteShareData };
|
|
||||||
});
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
// @ts-ignore
|
|
||||||
import axios from "axios";
|
|
||||||
import { ElMessage } from "element-plus";
|
|
||||||
const instance = axios.create({
|
|
||||||
baseURL: import.meta.env.DEV ? "http://localhost:12345" : "/",
|
|
||||||
timeout: 6000000,
|
|
||||||
});
|
|
||||||
instance.interceptors.request.use(
|
|
||||||
(config: any) => {
|
|
||||||
config.headers= {
|
|
||||||
'Authorization': 'Bearer '+ localStorage.getItem('adminPassword') || '',
|
|
||||||
}
|
|
||||||
return config;
|
|
||||||
});
|
|
||||||
instance.interceptors.response.use(
|
|
||||||
(response:any) => {
|
|
||||||
if (response.status === 200 && response.config.url === '/admin/file/download') {
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
if (response.data.code === 200) {
|
|
||||||
return response.data;
|
|
||||||
} else {
|
|
||||||
ElMessage.error(response.data.detail);
|
|
||||||
return Promise.reject(response.data);
|
|
||||||
}
|
|
||||||
}, (error:any) => {
|
|
||||||
localStorage.clear()
|
|
||||||
ElMessage.error(error.response.data.detail);
|
|
||||||
return Promise.reject(error);
|
|
||||||
});
|
|
||||||
|
|
||||||
export const request = instance;
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
export function formatTimestamp(timestamp: string): string {
|
|
||||||
const date = new Date(timestamp);
|
|
||||||
const year = date.getFullYear();
|
|
||||||
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
|
||||||
const day = date.getDate().toString().padStart(2, '0');
|
|
||||||
const hours = date.getHours().toString().padStart(2, '0');
|
|
||||||
const minutes = date.getMinutes().toString().padStart(2, '0');
|
|
||||||
const seconds = date.getSeconds().toString().padStart(2, '0');
|
|
||||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
<template>
|
|
||||||
<main style="text-align: center">
|
|
||||||
<span>{{ t('admin.about.source1') }}
|
|
||||||
<em>
|
|
||||||
<a style="color: #333" href="https://github.com/vastsa/FileCodeBox">{{ t('admin.about.source2') }}</a>
|
|
||||||
</em>
|
|
||||||
</span>
|
|
||||||
</main>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
|
|
||||||
import { useI18n } from 'vue-i18n'
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
</script>
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
<template>
|
|
||||||
<el-container v-if="adminData.isAdmin" style="height: 100vh;width: 100vw;position: relative;user-select: none">
|
|
||||||
<el-header>
|
|
||||||
<el-menu mode="horizontal" router :default-active="route.path">
|
|
||||||
<el-menu-item v-for="menu in menus" :index="menu.path" :key="menu.path">{{menu.name}}</el-menu-item>
|
|
||||||
<el-menu-item style="float: right" @click="toggleDark(!isDark)">{{ t('admin.menu.color') }}</el-menu-item>
|
|
||||||
<el-menu-item style="float: right" @click="logout()">{{ t('admin.menu.signout') }}</el-menu-item>
|
|
||||||
</el-menu>
|
|
||||||
</el-header>
|
|
||||||
<el-main>
|
|
||||||
<router-view/>
|
|
||||||
</el-main>
|
|
||||||
</el-container>
|
|
||||||
<el-form size="large" v-else>
|
|
||||||
<el-form-item :label="t('admin.login.managePassword')">
|
|
||||||
<el-input
|
|
||||||
v-model="adminData.adminPassword"
|
|
||||||
:placeholder="t('admin.login.passwordNotEmpty')"
|
|
||||||
type="password"
|
|
||||||
>
|
|
||||||
<template #append>
|
|
||||||
<el-button @click="refreshLoginStatus">{{ t('admin.login.login') }}</el-button>
|
|
||||||
</template>
|
|
||||||
</el-input>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { useDark, useToggle } from '@vueuse/core';
|
|
||||||
import { ref } from "vue";
|
|
||||||
const isDark = useDark()
|
|
||||||
const toggleDark = useToggle(isDark)
|
|
||||||
import { useRoute } from 'vue-router';
|
|
||||||
import { useAdminData } from "@/stores/adminData";
|
|
||||||
import { request } from "@/utils/request";
|
|
||||||
import { ElMessage } from "element-plus";
|
|
||||||
|
|
||||||
import { useI18n } from 'vue-i18n'
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
const adminData = useAdminData();
|
|
||||||
const route = useRoute();
|
|
||||||
const menus = ref([
|
|
||||||
{
|
|
||||||
name: t('admin.menu.fileManage'),
|
|
||||||
path: '/admin',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: t('admin.menu.systemSetting'),
|
|
||||||
path: '/admin/setting',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: t('admin.menu.local'),
|
|
||||||
path: '/admin/local',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: t('admin.menu.about'),
|
|
||||||
path: '/admin/about',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: t('admin.menu.send'),
|
|
||||||
path: '/#/send',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: t('admin.menu.receive'),
|
|
||||||
path: '/#/',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
const refreshLoginStatus = () => {
|
|
||||||
request({
|
|
||||||
url: '/admin/login',
|
|
||||||
method: 'post',
|
|
||||||
data: {
|
|
||||||
password: adminData.adminPassword,
|
|
||||||
},
|
|
||||||
}).then((res: any) => {
|
|
||||||
if (res.code === 200) {
|
|
||||||
adminData.updateAdminPwd(res.detail.token);
|
|
||||||
ElMessage.success(t('admin.login.loginSuccess'));
|
|
||||||
} else {
|
|
||||||
localStorage.clear();
|
|
||||||
ElMessage.error(t('admin.login.loginError'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const logout = () => {
|
|
||||||
localStorage.clear();
|
|
||||||
};
|
|
||||||
|
|
||||||
</script>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
</style>
|
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
<template>
|
|
||||||
<main>
|
|
||||||
<el-table size="large" stripe :data="tableData" style="width: 100%">
|
|
||||||
<el-table-column prop="code" :label="t('admin.fileView.code')" />
|
|
||||||
<el-table-column prop="prefix" :label="t('admin.fileView.prefix')" />
|
|
||||||
<el-table-column prop="suffix" :label="t('admin.fileView.suffix')" />
|
|
||||||
<el-table-column prop="text" :label="t('admin.fileView.text')">
|
|
||||||
<template #default="scope">
|
|
||||||
<span style="width: 6rem;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;">{{ scope.row.text }}</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="used_count" :label="t('admin.fileView.used_count')" />
|
|
||||||
<el-table-column prop="expired_count" :label="t('admin.fileView.expired_count')">
|
|
||||||
<template #default="scope">
|
|
||||||
<span>{{ scope.row.expired_count > -1 ? scope.row.expired_count : t('admin.fileView.unlimited_count') }}</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="size" :label="t('admin.fileView.size')">
|
|
||||||
<template #default="scope">
|
|
||||||
<span>{{ Math.round(scope.row.size/1024/1024*100)/100 }}MB</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="expired_at" :label="t('admin.fileView.expired_at')">
|
|
||||||
<template #default="scope">
|
|
||||||
<span>{{ scope.row.expired_at ? formatTimestamp(scope.row.expired_at) : t('admin.fileView.forever') }}</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="file_path" :label="t('admin.fileView.file_path')" />
|
|
||||||
<el-table-column>
|
|
||||||
<template #header>
|
|
||||||
{{ t('admin.fileView.action')}}
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button type="danger" size="small" @click="deleteFile(scope.row.id)">{{ t('admin.fileView.delete') }}</el-button>
|
|
||||||
<el-button type="success" size="small" @click="downloadFile(scope.row.id)" v-if="scope.row.file_path">{{ t('admin.fileView.download') }}</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<el-pagination @size-change="updatePageSize" @current-change="updateCurrentPage" background layout="prev, pager, next" :page-size="params.size" :page-count="Math.round(params.total/params.size)" :total="params.total" />
|
|
||||||
</main>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { request } from "@/utils/request";
|
|
||||||
import { formatTimestamp } from "@/utils/timestamp-format"
|
|
||||||
import { ref } from "vue";
|
|
||||||
import { ElMessage } from "element-plus";
|
|
||||||
const tableData = ref([]);
|
|
||||||
import { useI18n } from 'vue-i18n'
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
|
|
||||||
const params = ref({
|
|
||||||
page: 1,
|
|
||||||
size: 10,
|
|
||||||
total: 0,
|
|
||||||
});
|
|
||||||
const updateCurrentPage = (currentPage: number) => {
|
|
||||||
params.value.page = currentPage;
|
|
||||||
refreshData();
|
|
||||||
};
|
|
||||||
const deleteFile = (id: number) => {
|
|
||||||
request({
|
|
||||||
url: '/admin/file/delete',
|
|
||||||
method: 'delete',
|
|
||||||
data: {
|
|
||||||
id,
|
|
||||||
},
|
|
||||||
}).then(() => {
|
|
||||||
ElMessage.success(t('admin.fileView.delete_success'));
|
|
||||||
refreshData();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const downloadFile = (id: number) => {
|
|
||||||
request({
|
|
||||||
url: '/admin/file/download',
|
|
||||||
method: 'get',
|
|
||||||
params: {
|
|
||||||
id,
|
|
||||||
},
|
|
||||||
responseType: 'blob'
|
|
||||||
}).then((response: any) => {
|
|
||||||
const contentDisposition = response.headers['content-disposition'];
|
|
||||||
let filename = 'file';
|
|
||||||
// 使用正则表达式来提取文件名
|
|
||||||
const filenameMatch = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
|
|
||||||
if (filenameMatch != null && filenameMatch[1]) {
|
|
||||||
// 去除文件名周围的引号
|
|
||||||
filename = filenameMatch[1].replace(/['"]/g, '');
|
|
||||||
}
|
|
||||||
// @ts-ignore
|
|
||||||
if (window.showSaveFilePicker)
|
|
||||||
saveFileByWebApi(response.data, filename).catch(() => {
|
|
||||||
// ElMessage.error('文件保存函数不支持您的浏览器');
|
|
||||||
saveFileByElementA(response.data, filename).catch(() => {
|
|
||||||
ElMessage.error(t('admin.fileView.download_fail'));
|
|
||||||
})
|
|
||||||
})
|
|
||||||
else
|
|
||||||
saveFileByElementA(response.data, filename).catch(() => {
|
|
||||||
ElMessage.error(t('admin.fileView.download_fail'));
|
|
||||||
})
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
async function saveFileByElementA(fileBlob: Blob, filename: string) {
|
|
||||||
const downloadUrl = window.URL.createObjectURL(fileBlob);
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.href = downloadUrl;
|
|
||||||
link.download = filename; // 设置下载文件名
|
|
||||||
document.body.appendChild(link);
|
|
||||||
link.click();
|
|
||||||
// 清理并释放URL对象
|
|
||||||
window.URL.revokeObjectURL(downloadUrl);
|
|
||||||
document.body.removeChild(link);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveFileByWebApi(fileBlob: Blob, filename: string) {
|
|
||||||
// 创建一个新句柄。
|
|
||||||
// @ts-ignore
|
|
||||||
const newHandle = await window.showSaveFilePicker({
|
|
||||||
suggestedName: filename,
|
|
||||||
});
|
|
||||||
// 创建一个 FileSystemWritableFileStream 用于写入。
|
|
||||||
const writableStream = await newHandle.createWritable();
|
|
||||||
// 写入我们的文件。
|
|
||||||
await writableStream.write(fileBlob);
|
|
||||||
// 关闭文件并将内容写入磁盘。
|
|
||||||
await writableStream.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const updatePageSize=(pageSize: number) => {
|
|
||||||
params.value.size = pageSize;
|
|
||||||
refreshData();
|
|
||||||
};
|
|
||||||
const refreshData=() => {
|
|
||||||
request({
|
|
||||||
url: '/admin/file/list',
|
|
||||||
method: 'get',
|
|
||||||
params: params.value,
|
|
||||||
}).then((res:any) => {
|
|
||||||
tableData.value = res.detail.data;
|
|
||||||
params.value.total = res.detail.total;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
refreshData();
|
|
||||||
</script>
|
|
||||||
@@ -1,155 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="file-list">
|
|
||||||
<el-empty style="width: 90vw;" v-if="localFiles.length===0" description="请在/opt/filecodebox/local目录上传您需要分享的文件" />
|
|
||||||
<el-card v-for="file in localFiles" :key="file.name" class="file-card" shadow="hover">
|
|
||||||
<div class="file-info">
|
|
||||||
<div class="file-name">{{ file.file }}</div>
|
|
||||||
<div class="file-date">{{ file.ctime }}</div>
|
|
||||||
<div style="width: 100%;text-align: right">
|
|
||||||
<el-button type="primary" style="margin-top: 1rem" @click="shareLocalFile(file)" plain>分享</el-button>
|
|
||||||
<el-button type="danger" style="margin-top: 1rem" @click="deleteLocalFile(file)" plain>删除</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
<el-dialog v-model="dialogFormVisible" width="500">
|
|
||||||
<el-form :model="form">
|
|
||||||
<el-form-item :label="t('admin.local.Name')" >
|
|
||||||
<el-input v-model="form.name" readonly autocomplete="off" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item :label="t('admin.local.Expire')" >
|
|
||||||
<el-input
|
|
||||||
v-model="form.expireValue"
|
|
||||||
style="width: 200px"
|
|
||||||
:placeholder="t('send.pleaseInputExpireValue')"
|
|
||||||
>
|
|
||||||
<template #prepend>
|
|
||||||
<el-select v-model="form.expireStyle" :placeholder="t('send.expireStyle')" style="width: 75px">
|
|
||||||
<el-option v-for="item in config.expireStyle" :key="item" :label="t(`send.expireData.${item}`)" :value="item" />
|
|
||||||
</el-select>
|
|
||||||
</template>
|
|
||||||
<template #append>
|
|
||||||
<span v-if="form.expireStyle==='day'">{{t('send.expireValue.day')}}</span>
|
|
||||||
<span v-else-if="form.expireStyle==='hour'">{{t('send.expireValue.hour')}}</span>
|
|
||||||
<span v-else-if="form.expireStyle==='minute'">{{t('send.expireValue.minute')}}</span>
|
|
||||||
<span v-else-if="form.expireStyle==='forever'">👌</span>
|
|
||||||
<span v-else-if="form.expireStyle==='count'">{{t('send.expireValue.count')}}</span>
|
|
||||||
</template>
|
|
||||||
</el-input>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
<template #footer>
|
|
||||||
<div class="dialog-footer">
|
|
||||||
<el-button @click="dialogFormVisible = false">{{ t('admin.local.Cancel') }}</el-button>
|
|
||||||
<el-button type="primary" @click="toShareFile()">
|
|
||||||
{{ t('admin.local.Confirm') }}
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import {ref, reactive} from 'vue';
|
|
||||||
import {request} from "@/utils/request";
|
|
||||||
import {ElMessage} from "element-plus";
|
|
||||||
import {useConfigStore} from "@/stores/config";
|
|
||||||
import {useI18n} from "vue-i18n";
|
|
||||||
const { t } = useI18n()
|
|
||||||
|
|
||||||
const { config } = useConfigStore();
|
|
||||||
const localFiles:any = ref([]);
|
|
||||||
const loadLocalFiles = ()=>{
|
|
||||||
request({
|
|
||||||
url: '/admin/local/lists',
|
|
||||||
method: 'get',
|
|
||||||
}).then((data:any) => {
|
|
||||||
localFiles.value = data.detail;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const dialogFormVisible = ref(false);
|
|
||||||
|
|
||||||
const form = reactive({
|
|
||||||
name: '1',
|
|
||||||
expireStyle: 'day',
|
|
||||||
expireValue: 1,
|
|
||||||
})
|
|
||||||
loadLocalFiles()
|
|
||||||
const deleteLocalFile = (file: any) => {
|
|
||||||
request({
|
|
||||||
url: '/admin/local/delete',
|
|
||||||
method: 'delete',
|
|
||||||
data: {
|
|
||||||
filename: file.file
|
|
||||||
}
|
|
||||||
}).then((data:any) => {
|
|
||||||
ElMessage.success(data.detail);
|
|
||||||
loadLocalFiles()
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const shareLocalFile = (file:any) => {
|
|
||||||
form.name = file.file;
|
|
||||||
dialogFormVisible.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const toShareFile=()=>{
|
|
||||||
request({
|
|
||||||
url: '/admin/local/share',
|
|
||||||
method: 'post',
|
|
||||||
data: {
|
|
||||||
filename: form.name,
|
|
||||||
expire_style: form.expireStyle,
|
|
||||||
expire_value: form.expireValue
|
|
||||||
}
|
|
||||||
}).then((data:any) => {
|
|
||||||
dialogFormVisible.value = false;
|
|
||||||
ElMessage.success(
|
|
||||||
{
|
|
||||||
showClose: true,
|
|
||||||
message: 'Code:' + data.detail.code,
|
|
||||||
duration: 0
|
|
||||||
}
|
|
||||||
);
|
|
||||||
loadLocalFiles()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.file-list {
|
|
||||||
display: grid;
|
|
||||||
width: 100%;
|
|
||||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
||||||
gap: 16px;
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-card {
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
border-radius: 10px;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-card:hover {
|
|
||||||
box-shadow: 0px 6px 15px rgba(0, 0, 0, 0.25);
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-info {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-name {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: bold;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-date {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #a0a0b2;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@@ -1,219 +0,0 @@
|
|||||||
<template>
|
|
||||||
<el-form>
|
|
||||||
<el-form-item size="large" :label="t('admin.settings.name')">
|
|
||||||
<el-input v-model="config.name" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item size="large" :label="t('admin.settings.description')">
|
|
||||||
<el-input v-model="config.description" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item size="large" :label="t('admin.settings.keywords')" style="letter-spacing: 0.3rem">
|
|
||||||
<el-input v-model="config.keywords" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item size="large" :label="t('admin.settings.themeSelect')" >
|
|
||||||
<el-select
|
|
||||||
v-model="config.themesSelect"
|
|
||||||
style="width: 100%"
|
|
||||||
>
|
|
||||||
<el-option v-for="item in config.themesChoices" :label="item.name" :value="item.key" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item size="large" :label="t('admin.settings.notify_title')">
|
|
||||||
<el-input v-model="config.notify_title" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item size="large" :label="t('admin.settings.notify_content')">
|
|
||||||
<el-input v-model="config.notify_content" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item size="large" :label="t('admin.settings.background')">
|
|
||||||
<span style="display: flex;height: 38px;width: 45%">
|
|
||||||
<el-input v-model="config.background" placeholder="url" />
|
|
||||||
</span>
|
|
||||||
<span style="display: flex;height: 38px;margin-left: 20px">
|
|
||||||
Opacity: <el-input type="number" v-model="config.opacity" />
|
|
||||||
</span>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item size="large" :label="t('admin.settings.showAdminAddr')">
|
|
||||||
<el-select v-model="config.showAdminAddr" style="width: 80%">
|
|
||||||
<el-option :label="t('admin.settings.showAdmin.open')" :value="1" />
|
|
||||||
<el-option :label="t('admin.settings.showAdmin.close')" :value="0" />
|
|
||||||
</el-select>
|
|
||||||
<small style="margin-left: 0.4rem">{{ t('admin.settings.showAdmin.note') }}</small>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item size="large" label="robots.Text">
|
|
||||||
<el-input type="textarea" v-model="config.robotsText" placeholder="explain" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item size="large" :label="t('admin.settings.explain')">
|
|
||||||
<el-input type="textarea" v-model="config.page_explain" placeholder="explain" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item size="large" :label="t('admin.settings.admin_token')">
|
|
||||||
<el-input type="password" v-model="config.admin_token" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item size="large" :label="t('admin.settings.uploadSize')">
|
|
||||||
<el-input type="number" v-model="config.uploadSize" />
|
|
||||||
<template #append></template>
|
|
||||||
<small>Bytes:{{ t('admin.settings.uploadSizeNote') }}</small>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item size="large" :label="t('admin.settings.max_save_seconds')">
|
|
||||||
<el-input type="number" v-model="config.max_save_seconds" />
|
|
||||||
<small>Seconds:{{t('admin.settings.maxSaveSecondsNote')}}</small>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item size="large" :label="t('admin.settings.expireStyle')" >
|
|
||||||
<el-select
|
|
||||||
v-model="config.expireStyle"
|
|
||||||
multiple
|
|
||||||
style="width: 100%"
|
|
||||||
>
|
|
||||||
<el-option :label="t('send.expireData.day')" value="day" />
|
|
||||||
<el-option :label="t('send.expireData.hour')" value="hour" />
|
|
||||||
<el-option :label="t('send.expireData.minute')" value="minute" />
|
|
||||||
<el-option :label="t('send.expireData.forever')" value="forever" />
|
|
||||||
<el-option :label="t('send.expireData.count')" value="count" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item size="large" :label="t('admin.settings.openUpload.title')">
|
|
||||||
<el-select v-model="config.openUpload" style="width: 80%">
|
|
||||||
<el-option :label="t('admin.settings.openUpload.open')" :value="1" />
|
|
||||||
<el-option :label="t('admin.settings.openUpload.close')" :value="0" />
|
|
||||||
</el-select>
|
|
||||||
<small style="margin-left: 0.4rem">{{ t('admin.settings.openUpload.note') }}</small>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item size="large" :label="t('admin.settings.file_storage.title')">
|
|
||||||
<el-select v-model="config.file_storage" style="width: 80%">
|
|
||||||
<el-option :label="t('admin.settings.file_storage.local')" value="local" />
|
|
||||||
<el-option :label="t('admin.settings.file_storage.s3')" value="s3" />
|
|
||||||
</el-select>
|
|
||||||
<small style="margin-left: 0.4rem">{{ t('admin.settings.file_storage.note') }}</small>
|
|
||||||
</el-form-item>
|
|
||||||
<div v-if="config.file_storage==='s3'">
|
|
||||||
<el-form-item size="large" label="S3 AccessKeyId">
|
|
||||||
<el-input v-model="config.s3_access_key_id" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item size="large" label="S3 SecretAccessKey">
|
|
||||||
<el-input v-model="config.s3_secret_access_key" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item size="large" label="S3 BucketName">
|
|
||||||
<el-input v-model="config.s3_bucket_name" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item size="large" label="S3 EndpointUrl">
|
|
||||||
<el-input v-model="config.s3_endpoint_url" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item size="large" label="S3 hostname">
|
|
||||||
<el-input v-model="config.s3_hostname" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item size="large" label="S3 region name">
|
|
||||||
<el-input v-model="config.s3_region_name" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item size="large" label="S3 Signature Version">
|
|
||||||
<el-input v-model="config.s3_signature_version" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item size="large" label="Aws Session Token">
|
|
||||||
<el-input v-model="config.aws_session_token" />
|
|
||||||
</el-form-item>
|
|
||||||
</div>
|
|
||||||
<el-form-item size="large" :label="t('admin.settings.uploadlimit')">
|
|
||||||
<span style="display: flex;height: 38px">
|
|
||||||
<span style="margin-right: 0.4rem">{{ t('admin.settings.mei') }}</span>
|
|
||||||
<el-input type="number" v-model="config.uploadMinute" />
|
|
||||||
<span style="width: 200px;margin-left: 0.4rem">{{ t('admin.settings.minute') }}</span>
|
|
||||||
</span>
|
|
||||||
<span style="display: flex;height: 38px">
|
|
||||||
<span style="width:3rem;margin-right: 0.4rem">{{ t('admin.settings.upload') }}</span>
|
|
||||||
<el-input type="number" v-model="config.uploadCount" />
|
|
||||||
<span style="width: 200px;margin-left: 0.4rem">{{ t('admin.settings.files') }}</span>
|
|
||||||
</span>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item size="large" :label="t('admin.settings.errorlimit')">
|
|
||||||
<span style="display: flex;height: 38px">
|
|
||||||
<span style="margin-right: 0.4rem">{{ t('admin.settings.mei') }}</span>
|
|
||||||
<el-input type="number" v-model="config.errorMinute" />
|
|
||||||
<span style="width: 200px;margin-left: 0.4rem">{{ t('admin.settings.minute') }}</span>
|
|
||||||
</span>
|
|
||||||
<span style="display: flex;height: 38px">
|
|
||||||
<span style="width:3rem;margin-right: 0.4rem">{{ t('admin.settings.allow') }}</span>
|
|
||||||
<el-input type="number" v-model="config.errorCount" />
|
|
||||||
<span style="width: 200px;margin-left: 0.4rem">{{ t('admin.settings.errors') }}</span>
|
|
||||||
</span>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item>
|
|
||||||
<el-button @click="submitSave" type="primary" style="margin: auto">{{ t('admin.settings.save') }}</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import {ref} from "vue";
|
|
||||||
import { request } from "@/utils/request";
|
|
||||||
import { ElMessage } from "element-plus";
|
|
||||||
|
|
||||||
import { useI18n } from 'vue-i18n'
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
|
|
||||||
const config:any = ref({
|
|
||||||
name: '',
|
|
||||||
description: '',
|
|
||||||
file_storage: '',
|
|
||||||
expireStyle: [],
|
|
||||||
themesChoices: [],
|
|
||||||
themesSelect: '',
|
|
||||||
admin_token: '',
|
|
||||||
robotsText:'',
|
|
||||||
keywords: '',
|
|
||||||
notify_title: '',
|
|
||||||
notify_content: '',
|
|
||||||
openUpload: 1,
|
|
||||||
uploadSize: 1,
|
|
||||||
uploadMinute: 1,
|
|
||||||
max_save_seconds: 0,
|
|
||||||
opacity: 0.9,
|
|
||||||
s3_access_key_id: '',
|
|
||||||
background: '',
|
|
||||||
showAdminAddr: 0,
|
|
||||||
page_explain: '',
|
|
||||||
s3_secret_access_key: '',
|
|
||||||
aws_session_token: '',
|
|
||||||
s3_signature_version: '',
|
|
||||||
s3_region_name: '',
|
|
||||||
s3_bucket_name: '',
|
|
||||||
s3_endpoint_url: '',
|
|
||||||
s3_hostname: '',
|
|
||||||
uploadCount: 1,
|
|
||||||
errorMinute: 1,
|
|
||||||
errorCount: 1,
|
|
||||||
});
|
|
||||||
const refreshData = ()=>{
|
|
||||||
request({
|
|
||||||
url: '/admin/config/get',
|
|
||||||
method: 'get'
|
|
||||||
}).then((res: any) => {
|
|
||||||
config.value = res.detail;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
refreshData();
|
|
||||||
const submitSave = () => {
|
|
||||||
request({
|
|
||||||
url: '/admin/config/update',
|
|
||||||
method: 'patch',
|
|
||||||
data: config.value
|
|
||||||
}).then((res: any) => {
|
|
||||||
if (res.code == 200) {
|
|
||||||
ElMessage.success(t('admin.settings.saveSuccess'));
|
|
||||||
} else {
|
|
||||||
ElMessage.error(res.message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<style lang="scss">
|
|
||||||
|
|
||||||
.left-menu {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
cursor: pointer;
|
|
||||||
text-align: center;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 1rem;
|
|
||||||
letter-spacing: 0.4rem;
|
|
||||||
}
|
|
||||||
small,.el-form-item__content{
|
|
||||||
color: #909399;
|
|
||||||
margin-left: 0.4rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { Upload, TakeawayBox } from '@element-plus/icons-vue';
|
|
||||||
import { onMounted, reactive, ref, watch } from 'vue'
|
|
||||||
import { useRoute, useRouter } from "vue-router";
|
|
||||||
import CardTools from "@/components/CardTools.vue";
|
|
||||||
import { useFileBoxStore } from "@/stores/fileBox";
|
|
||||||
import { useFileDataStore } from "@/stores/fileData";
|
|
||||||
import { ElMessageBox } from 'element-plus'
|
|
||||||
|
|
||||||
import { request } from "@/utils/request";
|
|
||||||
const fileBoxStore = useFileBoxStore();
|
|
||||||
const fileStore = useFileDataStore();
|
|
||||||
const router = useRouter()
|
|
||||||
const route = useRoute()
|
|
||||||
const code = ref('')
|
|
||||||
const input_status = reactive({
|
|
||||||
'readonly': false,
|
|
||||||
'loading': false,
|
|
||||||
})
|
|
||||||
import { useI18n } from 'vue-i18n'
|
|
||||||
import { useConfigStore } from "@/stores/config";
|
|
||||||
|
|
||||||
const { config } = useConfigStore()
|
|
||||||
const noDialog = ()=>{
|
|
||||||
ElMessageBox.alert(config.explain, t('send.disclaimers'), {
|
|
||||||
confirmButtonText: t('fileBox.ok'),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const { t } = useI18n()
|
|
||||||
onMounted(() => {
|
|
||||||
const query_code = route.query.code as string;
|
|
||||||
if (query_code) {
|
|
||||||
code.value = query_code
|
|
||||||
}
|
|
||||||
})
|
|
||||||
watch(code, (newVal) => {
|
|
||||||
if (newVal.length === 5) {
|
|
||||||
input_status.readonly = true;
|
|
||||||
input_status.loading = true;
|
|
||||||
request({
|
|
||||||
'url': '/share/select/',
|
|
||||||
'method': 'POST',
|
|
||||||
'data': {
|
|
||||||
'code': newVal
|
|
||||||
}
|
|
||||||
}).then((res: any) => {
|
|
||||||
fileBoxStore.showFileBox = true;
|
|
||||||
let flag = true;
|
|
||||||
fileStore.receiveData.forEach((file: any) => {
|
|
||||||
if (file.code === res.detail.code) {
|
|
||||||
flag = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (flag) {
|
|
||||||
fileStore.addReceiveData(res.detail);
|
|
||||||
}
|
|
||||||
}).finally(() => {
|
|
||||||
input_status.readonly = false;
|
|
||||||
input_status.loading = false;
|
|
||||||
code.value = '';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const listenInput = (num: number) => {
|
|
||||||
if (code.value.length < 5) {
|
|
||||||
code.value += num
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const name = atob('RmlsZUNvZGVCb3ggVjIuMQ==');
|
|
||||||
const url = atob('aHR0cHM6Ly9naXRodWIuY29tL3Zhc3RzYS9GaWxlQ29kZUJveA==');
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<main>
|
|
||||||
<el-card class="card" style="padding-bottom: 1rem">
|
|
||||||
<CardTools/>
|
|
||||||
<el-row style="text-align: center">
|
|
||||||
<el-col :span="24">
|
|
||||||
<el-input :readonly="input_status.readonly" v-loading="input_status.loading" v-model="code" class="code-input" round autofocus clearable maxlength="5" :placeholder="t('fileBox.inputNotEmpty')"/>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span=8 v-for="i in 9" :key="i">
|
|
||||||
<el-button class="key-button" round @click="listenInput(i)">{{ i }}</el-button>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span=8>
|
|
||||||
<el-button @click="router.push({'name':'send'})" class="key-button" :icon="Upload" round>
|
|
||||||
</el-button>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span=8>
|
|
||||||
<el-button class="key-button" round @click="listenInput(0)">0</el-button>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span=8>
|
|
||||||
<el-button class="key-button" round :icon="TakeawayBox" @click="fileBoxStore.showFileBox=true">
|
|
||||||
</el-button>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</el-card>
|
|
||||||
<div style="text-align: center; margin-top: 1rem;color: #606266">
|
|
||||||
<a style="text-decoration: none;color: #606266" target="_blank" :href="url">
|
|
||||||
{{ name }}
|
|
||||||
</a>
|
|
||||||
<a @click="noDialog" style="text-decoration: none;color: #606266;margin-left: 1rem" href="javascript:void(0)">{{t('send.disclaimers')}}</a>
|
|
||||||
<a v-if="config.show_admin_address" style="color: #606266;margin-left: 1rem;text-decoration: none" href="#/admin">Admin</a>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</template>
|
|
||||||
<style lang='scss'>
|
|
||||||
.key-button{
|
|
||||||
width: 6rem;
|
|
||||||
height: 6rem;
|
|
||||||
margin: 0.2rem;
|
|
||||||
font-size: 2rem;
|
|
||||||
font-weight: bold;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code-input {
|
|
||||||
height: 100px;
|
|
||||||
font-size: 30px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin: 1rem 0;
|
|
||||||
.el-input__wrapper{
|
|
||||||
border-radius: 20px !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { ref } from 'vue'
|
|
||||||
import CardTools from "@/components/CardTools.vue";
|
|
||||||
import UploadFile from "@/components/UploadFile.vue";
|
|
||||||
import UploadText from "@/components/UploadText.vue";
|
|
||||||
import { useI18n } from 'vue-i18n'
|
|
||||||
import { useConfigStore } from "@/stores/config";
|
|
||||||
|
|
||||||
const { config } = useConfigStore();
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
const shareData = ref({
|
|
||||||
expireValue: 1,
|
|
||||||
expireStyle: config.expireStyle.length > 0 ? config.expireStyle[0] : 'day',
|
|
||||||
targetType: 'file',
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<main>
|
|
||||||
<el-card class="card" style="position: relative" :body-style="{ padding: '0' }">
|
|
||||||
<card-tools style="padding: 1rem"/>
|
|
||||||
<div style="display: flex;margin: 1rem">
|
|
||||||
<div>
|
|
||||||
<el-input
|
|
||||||
v-model="shareData.expireValue"
|
|
||||||
style="width: 200px"
|
|
||||||
:placeholder="t('send.pleaseInputExpireValue')"
|
|
||||||
>
|
|
||||||
<template #prepend>
|
|
||||||
<el-select v-model="shareData.expireStyle" :placeholder="t('send.expireStyle')" style="width: 75px">
|
|
||||||
<el-option v-for="item in config.expireStyle" :key="item" :label="t(`send.expireData.${item}`)" :value="item" />
|
|
||||||
</el-select>
|
|
||||||
</template>
|
|
||||||
<template #append>
|
|
||||||
<span v-if="shareData.expireStyle=='day'">{{t('send.expireValue.day')}}</span>
|
|
||||||
<span v-else-if="shareData.expireStyle=='hour'">{{t('send.expireValue.hour')}}</span>
|
|
||||||
<span v-else-if="shareData.expireStyle=='minute'">{{t('send.expireValue.minute')}}</span>
|
|
||||||
<span v-else-if="shareData.expireStyle=='forever'">👌</span>
|
|
||||||
<span v-else-if="shareData.expireStyle=='count'">{{t('send.expireValue.count')}}</span>
|
|
||||||
</template>
|
|
||||||
</el-input>
|
|
||||||
</div>
|
|
||||||
<el-radio-group v-model="shareData.targetType" style="margin-left: 1rem;">
|
|
||||||
<el-radio label="file">
|
|
||||||
{{t('send.fileType.file')}}
|
|
||||||
</el-radio>
|
|
||||||
<el-radio label="text">
|
|
||||||
{{t('send.fileType.text')}}
|
|
||||||
</el-radio>
|
|
||||||
</el-radio-group>
|
|
||||||
</div>
|
|
||||||
<div style="margin: 1rem">
|
|
||||||
<upload-file :shareData="shareData" v-if="shareData.targetType=='file'"/>
|
|
||||||
<upload-text :shareData="shareData" v-else-if="shareData.targetType=='text'"/>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</main>
|
|
||||||
</template>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
|
||||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
|
||||||
"exclude": ["src/**/__tests__/*"],
|
|
||||||
"compilerOptions": {
|
|
||||||
"composite": true,
|
|
||||||
"baseUrl": ".",
|
|
||||||
"paths": {
|
|
||||||
"@/*": ["./src/*"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"files": [],
|
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.node.json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.app.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "@tsconfig/node18/tsconfig.json",
|
|
||||||
"include": [
|
|
||||||
"vite.config.*",
|
|
||||||
"vitest.config.*",
|
|
||||||
"cypress.config.*",
|
|
||||||
"nightwatch.conf.*",
|
|
||||||
"playwright.config.*"
|
|
||||||
],
|
|
||||||
"compilerOptions": {
|
|
||||||
"composite": true,
|
|
||||||
"module": "ESNext",
|
|
||||||
"moduleResolution": "Bundler",
|
|
||||||
"types": ["node"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import { fileURLToPath, URL } from 'node:url'
|
|
||||||
|
|
||||||
import { defineConfig } from 'vite'
|
|
||||||
import vue from '@vitejs/plugin-vue'
|
|
||||||
import AutoImport from 'unplugin-auto-import/vite'
|
|
||||||
import Components from 'unplugin-vue-components/vite'
|
|
||||||
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [
|
|
||||||
vue(),
|
|
||||||
AutoImport({
|
|
||||||
resolvers: [ElementPlusResolver()],
|
|
||||||
}),
|
|
||||||
Components({
|
|
||||||
resolvers: [ElementPlusResolver()],
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
resolve: {
|
|
||||||
alias: {
|
|
||||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user