mirror of
https://github.com/keven1024/015.git
synced 2026-05-26 07:08:02 +00:00
Merge branch 'main' into dev
This commit is contained in:
37
README-zh.md
37
README-zh.md
@@ -39,25 +39,36 @@
|
||||
📷 **图片处理** - 图片压缩、格式转换等处理功能
|
||||
🏷️ **下载控制** - 基于 JWT 的下载令牌管理系统
|
||||
|
||||
## 截图
|
||||
## 📸 截图预览
|
||||
|
||||
选择文件上传页面
|
||||

|
||||
| 选择文件上传页面 | 输入文本上传页面 |
|
||||
|---------------------------------------|-----------------------------------------------|
|
||||
|  |  |
|
||||
|
||||
输入文本上传页面
|
||||

|
||||
| 多选文件上传 | 文件上传进度热力图 |
|
||||
|------------------------------------------------|---------------------------------------------------|
|
||||
|  |  |
|
||||
|
||||
选择文件上传页面 - 支持多选文件上传
|
||||

|
||||
| 文件上传进度条 | 文件上传成功页面 |
|
||||
|------------------------------------------------|-------------------------------------------------|
|
||||
|  |  |
|
||||
|
||||
文件正在上传页面 - 类似Github的展示上传进度的文件热力图
|
||||

|
||||
## 🚀 快速开始
|
||||
|
||||
文件正在上传页面 - 类似 QbitTorrent 的展示文件上传进度的进度条
|
||||

|
||||
### Docker
|
||||
|
||||
1. 下载文件
|
||||
- config.example.yaml
|
||||
- docker-compose.yml
|
||||
|
||||
2. 把config.example.yaml配置完成后改为config.yaml
|
||||
|
||||
|
||||
3. 启动
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
文件上传成功页面
|
||||

|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
|
||||
24
README.md
24
README.md
@@ -41,23 +41,17 @@ English | [中文](README-zh.md)
|
||||
|
||||
## 📸 Screenshots
|
||||
|
||||
File selection upload page
|
||||

|
||||
| File Selection Upload Page | Text Input Upload Page |
|
||||
|---------------------------------------|-----------------------------------------------|
|
||||
|  |  |
|
||||
|
||||
Text input upload page
|
||||

|
||||
| Multiple File Upload | Upload Progress Heatmap |
|
||||
|------------------------------------------------|---------------------------------------------------|
|
||||
|  |  |
|
||||
|
||||
File selection upload page - supports multiple file uploads
|
||||

|
||||
|
||||
File uploading page - similar to GitHub's file heatmap showing upload progress
|
||||

|
||||
|
||||
File uploading page - similar to qBittorrent's progress bar showing file upload progress
|
||||

|
||||
|
||||
File upload success page
|
||||

|
||||
| Upload Progress Bar | Upload Success Page |
|
||||
|------------------------------------------------|-------------------------------------------------|
|
||||
|  |  |
|
||||
|
||||
## 🏗️ Technical Architecture
|
||||
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import VeeForm from '../VeeForm.vue'
|
||||
import FormButton from '@/components/Field/FormButton.vue'
|
||||
import InputField from '@/components/Field/InputField.vue'
|
||||
import { LucideCheckCheck, LucideLanguages, LucideSparkle, LucideWandSparkles } from 'lucide-vue-next'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import asyncWait from '~/lib/asyncWait'
|
||||
|
||||
const props = defineProps<{
|
||||
hide: () => void
|
||||
data: { html: string }
|
||||
}>()
|
||||
const formRef = ref<InstanceType<typeof VeeForm>>()
|
||||
|
||||
const actions = [
|
||||
{
|
||||
icon: LucideWandSparkles,
|
||||
label: '总结',
|
||||
onClick: () => {
|
||||
formRef.value?.form?.setFieldValue('input', '总结这段话')
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: LucideCheckCheck,
|
||||
label: '修正拼写和语法错误',
|
||||
onClick: () => {
|
||||
formRef.value?.form?.setFieldValue('input', '修正这段话的拼写和语法错误')
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: LucideLanguages,
|
||||
label: '翻译成',
|
||||
children: [
|
||||
{
|
||||
label: '英语',
|
||||
onClick: () => {
|
||||
formRef.value?.form?.setFieldValue('input', '把这段话翻译成英语')
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '韩文',
|
||||
onClick: () => {
|
||||
formRef.value?.form?.setFieldValue('input', '把这段话翻译成韩文')
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '中文',
|
||||
onClick: () => {
|
||||
formRef.value?.form?.setFieldValue('input', '把这段话翻译成中文')
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col gap-5">
|
||||
<div class="text-xl font-bold">询问AI</div>
|
||||
<div class="overflow-y-auto max-h-[160px] p-3 rounded-md bg-primary/5 border border-primary/10 text-black/80 text-sm">
|
||||
<div v-html="data.html" />
|
||||
</div>
|
||||
<VeeForm ref="formRef">
|
||||
<InputField name="input" placeholder="您想如何处理该文本" rules="required" />
|
||||
<div class="flex flex-row gap-3">
|
||||
<template v-for="action in actions">
|
||||
<Button variant="outline" v-if="!action?.children" @click="action.onClick">
|
||||
<component :is="action.icon" /> {{ action.label }}
|
||||
</Button>
|
||||
<DropdownMenu v-else>
|
||||
<DropdownMenuTrigger>
|
||||
<Button variant="outline"> <component :is="action.icon" /> {{ action.label }} </Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem v-for="item in action?.children" @click="item.onClick">{{ item?.label }}</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<FormButton
|
||||
@click="
|
||||
async () => {
|
||||
await asyncWait(3000)
|
||||
}
|
||||
"
|
||||
>
|
||||
<LucideSparkle />
|
||||
生成
|
||||
</FormButton>
|
||||
</VeeForm>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,106 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { Editor } from '@tiptap/vue-3'
|
||||
import { BubbleMenu } from '@tiptap/vue-3/menus'
|
||||
import { LucideAArrowUp, LucideBold, LucideCode, LucideItalic, LucideSparkles, LucideStrikethrough } from 'lucide-vue-next'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
import TextInlineAIDrawer from '@/components/Drawer/TextInlineAIDrawer.vue'
|
||||
import { cx } from 'class-variance-authority'
|
||||
|
||||
import { getHTMLFromFragment } from '@tiptap/vue-3'
|
||||
import showDrawer from '~/lib/showDrawer'
|
||||
|
||||
const props = defineProps<{
|
||||
editor: Editor
|
||||
}>()
|
||||
|
||||
const show = ref(false)
|
||||
|
||||
const menus = [
|
||||
{
|
||||
type: 'button',
|
||||
label: '询问AI',
|
||||
icon: LucideSparkles,
|
||||
onClick: () => {
|
||||
show.value = false
|
||||
const { from, to } = props.editor.state.selection || {}
|
||||
showDrawer({
|
||||
render: ({ ...rest }) =>
|
||||
h(TextInlineAIDrawer, {
|
||||
...rest,
|
||||
data: { html: getHTMLFromFragment(props.editor.state.doc.slice(from, to).content, props.editor.schema) },
|
||||
}),
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'icon',
|
||||
label: '加粗',
|
||||
icon: LucideBold,
|
||||
onClick: () => {
|
||||
props.editor?.chain().focus().toggleBold().run()
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'icon',
|
||||
label: '斜体',
|
||||
icon: LucideItalic,
|
||||
onClick: () => {
|
||||
props.editor?.chain().focus().toggleItalic().run()
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'icon',
|
||||
label: '删除线',
|
||||
icon: LucideStrikethrough,
|
||||
onClick: () => {
|
||||
props.editor?.chain().focus().toggleStrike().run()
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'icon',
|
||||
label: '代码',
|
||||
icon: LucideCode,
|
||||
onClick: () => {
|
||||
props.editor?.chain().focus().toggleCode().run()
|
||||
},
|
||||
},
|
||||
]
|
||||
</script>
|
||||
<template>
|
||||
<bubble-menu
|
||||
v-if="editor"
|
||||
:editor="editor as any"
|
||||
:options="{
|
||||
placement: 'bottom',
|
||||
offset: 8,
|
||||
onShow: () => {
|
||||
show = true
|
||||
},
|
||||
onHide: () => {
|
||||
show = false
|
||||
},
|
||||
}"
|
||||
>
|
||||
<div :class="cx('bg-white rounded-md overflow-hidden', show ? 'block' : 'hidden')">
|
||||
<div class="border border-black/10 bg-primary/30 text-primary p-1 flex flex-row gap-0.5 shadow-md">
|
||||
<template v-for="menu in menus" :key="menu.label">
|
||||
<Button v-if="menu.type === 'button'" variant="ghost" size="sm" @click="menu.onClick">
|
||||
<component :is="menu.icon" /> {{ menu.label }}
|
||||
</Button>
|
||||
<TooltipProvider v-if="menu.type === 'icon'">
|
||||
<Tooltip>
|
||||
<TooltipTrigger as-child>
|
||||
<Button variant="ghost" class="!size-8" @click="menu.onClick">
|
||||
<component :is="menu.icon" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
{{ menu.label }}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</bubble-menu>
|
||||
</template>
|
||||
@@ -3,7 +3,6 @@ import { Editor, EditorContent } from '@tiptap/vue-3'
|
||||
import StarterKit from '@tiptap/starter-kit'
|
||||
import { Markdown } from 'tiptap-markdown'
|
||||
import Placeholder from '@tiptap/extension-placeholder'
|
||||
import BubbleMenuView from './BubbleMenu/BubbleMenuView.vue'
|
||||
import { cx } from 'class-variance-authority'
|
||||
|
||||
const props = defineProps<{
|
||||
|
||||
Reference in New Issue
Block a user