改进
This commit is contained in:
parent
834928dbe9
commit
e9e1e3e0ca
@ -1,7 +0,0 @@
|
||||
{
|
||||
"$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json",
|
||||
"spaces": 2,
|
||||
"generator-cli": {
|
||||
"version": "7.1.0"
|
||||
}
|
||||
}
|
745
api/swagger.json
745
api/swagger.json
@ -1,745 +0,0 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "This is a sample server celler server.",
|
||||
"title": "Swagger Example API",
|
||||
"contact": {},
|
||||
"version": "1.0"
|
||||
},
|
||||
"paths": {
|
||||
"/libraries": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"BearerToken": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Libraries"
|
||||
],
|
||||
"summary": "获取资料库列表",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "页码",
|
||||
"name": "Page",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponsePaginated"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponseError"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Forbidden",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponseError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerToken": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Libraries"
|
||||
],
|
||||
"summary": "新建资料库",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "资料库名称",
|
||||
"name": "Name",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/model.Library"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponseError"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Forbidden",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponseError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/library/{LibraryId}": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"BearerToken": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Libraries"
|
||||
],
|
||||
"summary": "显示资料库",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "资料库 ID",
|
||||
"name": "LibraryId",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "No Content"
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponseError"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Forbidden",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponseError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"BearerToken": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Libraries"
|
||||
],
|
||||
"summary": "删除资料库",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "资料库 ID",
|
||||
"name": "LibraryId",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "No Content"
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponseError"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Forbidden",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponseError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/library/{LibraryId}/document/{DocumentId}": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"BearerToken": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Documents"
|
||||
],
|
||||
"summary": "显示文档",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "资料库 ID",
|
||||
"name": "LibraryId",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "文档 ID",
|
||||
"name": "DocumentId",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/model.Document"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponseError"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Forbidden",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponseError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
"BearerToken": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Documents"
|
||||
],
|
||||
"summary": "更新文档",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "资料库 ID",
|
||||
"name": "LibraryId",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "文档 ID",
|
||||
"name": "DocumentId",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "标题",
|
||||
"name": "Title",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "内容",
|
||||
"name": "Content",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/model.Document"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponseError"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Forbidden",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponseError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"BearerToken": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Documents"
|
||||
],
|
||||
"summary": "删除文档",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "文档 ID",
|
||||
"name": "DocumentId",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "资料库 ID",
|
||||
"name": "LibraryId",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "No Content"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/library/{LibraryId}/document/{DocumentId}/chunks": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"BearerToken": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Documents"
|
||||
],
|
||||
"summary": "查看处理状态",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "资料库 ID",
|
||||
"name": "LibraryId",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "文档 ID",
|
||||
"name": "DocumentId",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/controller.chunkStatusResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponseError"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Forbidden",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponseError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/library/{LibraryId}/documents": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"BearerToken": []
|
||||
}
|
||||
],
|
||||
"description": "获取当前账号的文档列表",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Documents"
|
||||
],
|
||||
"summary": "获取文档列表",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "页码",
|
||||
"name": "Page",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "资料库 ID",
|
||||
"name": "LibraryId",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponsePaginated"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponseError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerToken": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Documents"
|
||||
],
|
||||
"summary": "新建文档",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "标题",
|
||||
"name": "LibraryId",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "标题",
|
||||
"name": "Title",
|
||||
"in": "formData",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "描述",
|
||||
"name": "Description",
|
||||
"in": "formData"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "文档内容",
|
||||
"name": "Content",
|
||||
"in": "formData"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/model.Document"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponseError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/library/{LibraryId}/query": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"BearerToken": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Documents"
|
||||
],
|
||||
"summary": "Server Sent Events 查询文档",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "资料库 ID",
|
||||
"name": "LibraryId",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "问题",
|
||||
"name": "Question",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponseError"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Forbidden",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponseError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/pkg.ResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"controller.chunkStatusResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"chunked": {
|
||||
"type": "integer"
|
||||
},
|
||||
"completed": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"total": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"gorm.DeletedAt": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"time": {
|
||||
"type": "string"
|
||||
},
|
||||
"valid": {
|
||||
"description": "Valid is true if Time is not NULL",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"model.Document": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"chunked": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"content": {
|
||||
"type": "string"
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"deletedAt": {
|
||||
"$ref": "#/definitions/gorm.DeletedAt"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"libraryId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"userId": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"model.Library": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"createdAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"deletedAt": {
|
||||
"$ref": "#/definitions/gorm.DeletedAt"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"userId": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"pkg.ResponseError": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"pkg.ResponsePaginated": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Data": {},
|
||||
"Limit": {
|
||||
"type": "integer"
|
||||
},
|
||||
"Page": {
|
||||
"type": "integer"
|
||||
},
|
||||
"Total": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
"BearerToken": {
|
||||
"type": "apiKey",
|
||||
"name": "Authorization",
|
||||
"in": "header"
|
||||
}
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@
|
||||
"event-source-polyfill": "^1.0.31",
|
||||
"google-protobuf": "^3.21.2",
|
||||
"grpc-web": "^1.5.0",
|
||||
"keycloak-js": "^24.0.4",
|
||||
"mammoth": "^1.6.0",
|
||||
"md-editor-v3": "^4.8.3",
|
||||
"pdfjs-dist": "^4.0.269",
|
||||
|
@ -5,60 +5,18 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { library } from "@/plugins/api";
|
||||
import { ref } from "vue";
|
||||
import { useUserStore } from "@/store/user";
|
||||
import {GrpcWebFetchTransport} from "@protobuf-ts/grpcweb-transport"
|
||||
import {LibraryServiceClient} from "@/proto/library/library.client.ts"
|
||||
import {DocumentServiceClient} from "@/proto/document/document.client.ts"
|
||||
import * as libraryProto from "@/proto/library/library.ts";
|
||||
import * as documentProto from "@/proto/document/document.ts";
|
||||
import * as libraryProto from "@/proto/library/library";
|
||||
import * as documentProto from "@/proto/document/document";
|
||||
import keycloak from "@/plugins/keycloak"
|
||||
import {libraryServiceClient} from "@/plugins/rpc"
|
||||
|
||||
let listLibrary: libraryProto.ListLibrariesRequest = {
|
||||
page: 1
|
||||
}
|
||||
|
||||
let empty: documentProto.Empty = {}
|
||||
|
||||
let transport = new GrpcWebFetchTransport({
|
||||
baseUrl: "https://test.ivampiresp.com",
|
||||
format: "text",
|
||||
meta: {
|
||||
"Authorization": "Bearer 1",
|
||||
},
|
||||
timeout: 100 * 1000,
|
||||
libraryServiceClient.listLibraries(listLibrary)
|
||||
|
||||
});
|
||||
|
||||
let client = new LibraryServiceClient(transport)
|
||||
let documentClient = new DocumentServiceClient(transport)
|
||||
|
||||
let s = documentClient.testStream(empty)
|
||||
|
||||
s.responses.onMessage(e => {
|
||||
console.log(e)
|
||||
})
|
||||
|
||||
s.then(res => {
|
||||
console.log(res)
|
||||
})
|
||||
console.log(s)
|
||||
|
||||
// let out = client.listLibraries(listLibrary, {})
|
||||
// out.then((r) => console.log(r))
|
||||
|
||||
const userStore = useUserStore();
|
||||
const token = ref("");
|
||||
|
||||
const settoken = () => {
|
||||
userStore.login("0", token.value);
|
||||
|
||||
console.log(userStore.jwt_token);
|
||||
};
|
||||
|
||||
const testrequest = () => {
|
||||
library.librariesGet(1).then((r) => console.log(r));
|
||||
};
|
||||
|
||||
// library.libraryLibraryIdDocumentsGet(1,2)
|
||||
</script>
|
||||
|
4
src/openapi/.gitignore
vendored
4
src/openapi/.gitignore
vendored
@ -1,4 +0,0 @@
|
||||
wwwroot/*.js
|
||||
node_modules
|
||||
typings
|
||||
dist
|
@ -1 +0,0 @@
|
||||
# empty npmignore to ensure all required files (e.g., in the dist folder) are published by npm
|
@ -1,23 +0,0 @@
|
||||
# OpenAPI Generator Ignore
|
||||
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
|
||||
|
||||
# Use this file to prevent files from being overwritten by the generator.
|
||||
# The patterns follow closely to .gitignore or .dockerignore.
|
||||
|
||||
# As an example, the C# client generator defines ApiClient.cs.
|
||||
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
|
||||
#ApiClient.cs
|
||||
|
||||
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
|
||||
#foo/*/qux
|
||||
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
|
||||
|
||||
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
|
||||
#foo/**/qux
|
||||
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
|
||||
|
||||
# You can also negate patterns with an exclamation (!).
|
||||
# For example, you can ignore all files in a docs folder with the file extension .md:
|
||||
#docs/*.md
|
||||
# Then explicitly reverse the ignore rule for a single file:
|
||||
#!docs/README.md
|
@ -1,8 +0,0 @@
|
||||
.gitignore
|
||||
.npmignore
|
||||
api.ts
|
||||
base.ts
|
||||
common.ts
|
||||
configuration.ts
|
||||
git_push.sh
|
||||
index.ts
|
@ -1 +0,0 @@
|
||||
7.1.0
|
1208
src/openapi/api.ts
1208
src/openapi/api.ts
File diff suppressed because it is too large
Load Diff
@ -1,86 +0,0 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Swagger Example API
|
||||
* This is a sample server celler server.
|
||||
*
|
||||
* The version of the OpenAPI document: 1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
import type { Configuration } from './configuration';
|
||||
// Some imports not used depending on template conditions
|
||||
// @ts-ignore
|
||||
import type { AxiosPromise, AxiosInstance, AxiosRequestConfig } from 'axios';
|
||||
import globalAxios from 'axios';
|
||||
|
||||
export const BASE_PATH = "http://localhost".replace(/\/+$/, "");
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const COLLECTION_FORMATS = {
|
||||
csv: ",",
|
||||
ssv: " ",
|
||||
tsv: "\t",
|
||||
pipes: "|",
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface RequestArgs
|
||||
*/
|
||||
export interface RequestArgs {
|
||||
url: string;
|
||||
options: AxiosRequestConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @class BaseAPI
|
||||
*/
|
||||
export class BaseAPI {
|
||||
protected configuration: Configuration | undefined;
|
||||
|
||||
constructor(configuration?: Configuration, protected basePath: string = BASE_PATH, protected axios: AxiosInstance = globalAxios) {
|
||||
if (configuration) {
|
||||
this.configuration = configuration;
|
||||
this.basePath = configuration.basePath ?? basePath;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @class RequiredError
|
||||
* @extends {Error}
|
||||
*/
|
||||
export class RequiredError extends Error {
|
||||
constructor(public field: string, msg?: string) {
|
||||
super(msg);
|
||||
this.name = "RequiredError"
|
||||
}
|
||||
}
|
||||
|
||||
interface ServerMap {
|
||||
[key: string]: {
|
||||
url: string,
|
||||
description: string,
|
||||
}[];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const operationServerMap: ServerMap = {
|
||||
}
|
@ -1,150 +0,0 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Swagger Example API
|
||||
* This is a sample server celler server.
|
||||
*
|
||||
* The version of the OpenAPI document: 1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
import type { Configuration } from "./configuration";
|
||||
import type { RequestArgs } from "./base";
|
||||
import type { AxiosInstance, AxiosResponse } from 'axios';
|
||||
import { RequiredError } from "./base";
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const DUMMY_BASE_URL = 'https://example.com'
|
||||
|
||||
/**
|
||||
*
|
||||
* @throws {RequiredError}
|
||||
* @export
|
||||
*/
|
||||
export const assertParamExists = function (functionName: string, paramName: string, paramValue: unknown) {
|
||||
if (paramValue === null || paramValue === undefined) {
|
||||
throw new RequiredError(paramName, `Required parameter ${paramName} was null or undefined when calling ${functionName}.`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const setApiKeyToObject = async function (object: any, keyParamName: string, configuration?: Configuration) {
|
||||
if (configuration && configuration.apiKey) {
|
||||
const localVarApiKeyValue = typeof configuration.apiKey === 'function'
|
||||
? await configuration.apiKey(keyParamName)
|
||||
: await configuration.apiKey;
|
||||
object[keyParamName] = localVarApiKeyValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const setBasicAuthToObject = function (object: any, configuration?: Configuration) {
|
||||
if (configuration && (configuration.username || configuration.password)) {
|
||||
object["auth"] = { username: configuration.username, password: configuration.password };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const setBearerAuthToObject = async function (object: any, configuration?: Configuration) {
|
||||
if (configuration && configuration.accessToken) {
|
||||
const accessToken = typeof configuration.accessToken === 'function'
|
||||
? await configuration.accessToken()
|
||||
: await configuration.accessToken;
|
||||
object["Authorization"] = "Bearer " + accessToken;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const setOAuthToObject = async function (object: any, name: string, scopes: string[], configuration?: Configuration) {
|
||||
if (configuration && configuration.accessToken) {
|
||||
const localVarAccessTokenValue = typeof configuration.accessToken === 'function'
|
||||
? await configuration.accessToken(name, scopes)
|
||||
: await configuration.accessToken;
|
||||
object["Authorization"] = "Bearer " + localVarAccessTokenValue;
|
||||
}
|
||||
}
|
||||
|
||||
function setFlattenedQueryParams(urlSearchParams: URLSearchParams, parameter: any, key: string = ""): void {
|
||||
if (parameter == null) return;
|
||||
if (typeof parameter === "object") {
|
||||
if (Array.isArray(parameter)) {
|
||||
(parameter as any[]).forEach(item => setFlattenedQueryParams(urlSearchParams, item, key));
|
||||
}
|
||||
else {
|
||||
Object.keys(parameter).forEach(currentKey =>
|
||||
setFlattenedQueryParams(urlSearchParams, parameter[currentKey], `${key}${key !== '' ? '.' : ''}${currentKey}`)
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (urlSearchParams.has(key)) {
|
||||
urlSearchParams.append(key, parameter);
|
||||
}
|
||||
else {
|
||||
urlSearchParams.set(key, parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const setSearchParams = function (url: URL, ...objects: any[]) {
|
||||
const searchParams = new URLSearchParams(url.search);
|
||||
setFlattenedQueryParams(searchParams, objects);
|
||||
url.search = searchParams.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const serializeDataIfNeeded = function (value: any, requestOptions: any, configuration?: Configuration) {
|
||||
const nonString = typeof value !== 'string';
|
||||
const needsSerialization = nonString && configuration && configuration.isJsonMime
|
||||
? configuration.isJsonMime(requestOptions.headers['Content-Type'])
|
||||
: nonString;
|
||||
return needsSerialization
|
||||
? JSON.stringify(value !== undefined ? value : {})
|
||||
: (value || "");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const toPathString = function (url: URL) {
|
||||
return url.pathname + url.search + url.hash
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const createRequestFunction = function (axiosArgs: RequestArgs, globalAxios: AxiosInstance, BASE_PATH: string, configuration?: Configuration) {
|
||||
return <T = unknown, R = AxiosResponse<T>>(axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
|
||||
const axiosRequestArgs = {...axiosArgs.options, url: (configuration?.basePath || axios.defaults.baseURL || basePath) + axiosArgs.url};
|
||||
return axios.request<T, R>(axiosRequestArgs);
|
||||
};
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Swagger Example API
|
||||
* This is a sample server celler server.
|
||||
*
|
||||
* The version of the OpenAPI document: 1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
export interface ConfigurationParameters {
|
||||
apiKey?: string | Promise<string> | ((name: string) => string) | ((name: string) => Promise<string>);
|
||||
username?: string;
|
||||
password?: string;
|
||||
accessToken?: string | Promise<string> | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise<string>);
|
||||
basePath?: string;
|
||||
serverIndex?: number;
|
||||
baseOptions?: any;
|
||||
formDataCtor?: new () => any;
|
||||
}
|
||||
|
||||
export class Configuration {
|
||||
/**
|
||||
* parameter for apiKey security
|
||||
* @param name security name
|
||||
* @memberof Configuration
|
||||
*/
|
||||
apiKey?: string | Promise<string> | ((name: string) => string) | ((name: string) => Promise<string>);
|
||||
/**
|
||||
* parameter for basic security
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof Configuration
|
||||
*/
|
||||
username?: string;
|
||||
/**
|
||||
* parameter for basic security
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof Configuration
|
||||
*/
|
||||
password?: string;
|
||||
/**
|
||||
* parameter for oauth2 security
|
||||
* @param name security name
|
||||
* @param scopes oauth2 scope
|
||||
* @memberof Configuration
|
||||
*/
|
||||
accessToken?: string | Promise<string> | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise<string>);
|
||||
/**
|
||||
* override base path
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof Configuration
|
||||
*/
|
||||
basePath?: string;
|
||||
/**
|
||||
* override server index
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof Configuration
|
||||
*/
|
||||
serverIndex?: number;
|
||||
/**
|
||||
* base options for axios calls
|
||||
*
|
||||
* @type {any}
|
||||
* @memberof Configuration
|
||||
*/
|
||||
baseOptions?: any;
|
||||
/**
|
||||
* The FormData constructor that will be used to create multipart form data
|
||||
* requests. You can inject this here so that execution environments that
|
||||
* do not support the FormData class can still run the generated client.
|
||||
*
|
||||
* @type {new () => FormData}
|
||||
*/
|
||||
formDataCtor?: new () => any;
|
||||
|
||||
constructor(param: ConfigurationParameters = {}) {
|
||||
this.apiKey = param.apiKey;
|
||||
this.username = param.username;
|
||||
this.password = param.password;
|
||||
this.accessToken = param.accessToken;
|
||||
this.basePath = param.basePath;
|
||||
this.serverIndex = param.serverIndex;
|
||||
this.baseOptions = param.baseOptions;
|
||||
this.formDataCtor = param.formDataCtor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given MIME is a JSON MIME.
|
||||
* JSON MIME examples:
|
||||
* application/json
|
||||
* application/json; charset=UTF8
|
||||
* APPLICATION/JSON
|
||||
* application/vnd.company+json
|
||||
* @param mime - MIME (Multipurpose Internet Mail Extensions)
|
||||
* @return True if the given MIME is JSON, false otherwise.
|
||||
*/
|
||||
public isJsonMime(mime: string): boolean {
|
||||
const jsonMime: RegExp = new RegExp('^(application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(;.*)?$', 'i');
|
||||
return mime !== null && (jsonMime.test(mime) || mime.toLowerCase() === 'application/json-patch+json');
|
||||
}
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
#!/bin/sh
|
||||
# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/
|
||||
#
|
||||
# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com"
|
||||
|
||||
git_user_id=$1
|
||||
git_repo_id=$2
|
||||
release_note=$3
|
||||
git_host=$4
|
||||
|
||||
if [ "$git_host" = "" ]; then
|
||||
git_host="github.com"
|
||||
echo "[INFO] No command line input provided. Set \$git_host to $git_host"
|
||||
fi
|
||||
|
||||
if [ "$git_user_id" = "" ]; then
|
||||
git_user_id="GIT_USER_ID"
|
||||
echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id"
|
||||
fi
|
||||
|
||||
if [ "$git_repo_id" = "" ]; then
|
||||
git_repo_id="GIT_REPO_ID"
|
||||
echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id"
|
||||
fi
|
||||
|
||||
if [ "$release_note" = "" ]; then
|
||||
release_note="Minor update"
|
||||
echo "[INFO] No command line input provided. Set \$release_note to $release_note"
|
||||
fi
|
||||
|
||||
# Initialize the local directory as a Git repository
|
||||
git init
|
||||
|
||||
# Adds the files in the local repository and stages them for commit.
|
||||
git add .
|
||||
|
||||
# Commits the tracked changes and prepares them to be pushed to a remote repository.
|
||||
git commit -m "$release_note"
|
||||
|
||||
# Sets the new remote
|
||||
git_remote=$(git remote)
|
||||
if [ "$git_remote" = "" ]; then # git remote not defined
|
||||
|
||||
if [ "$GIT_TOKEN" = "" ]; then
|
||||
echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment."
|
||||
git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git
|
||||
else
|
||||
git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
git pull origin master
|
||||
|
||||
# Pushes (Forces) the changes in the local repository up to the remote repository
|
||||
echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git"
|
||||
git push origin master 2>&1 | grep -v 'To https'
|
@ -1,18 +0,0 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Swagger Example API
|
||||
* This is a sample server celler server.
|
||||
*
|
||||
* The version of the OpenAPI document: 1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
export * from "./api";
|
||||
export * from "./configuration";
|
||||
|
@ -1,22 +0,0 @@
|
||||
import { DocumentsApi, Configuration, LibrariesApi } from "../openapi";
|
||||
|
||||
// import axios from "axios";
|
||||
// import router from "@/router";
|
||||
import {useUserStore} from "@/store/user";
|
||||
import { useConfigStore } from "@/store/config";
|
||||
|
||||
|
||||
const userStore = useUserStore()
|
||||
const configStore = useConfigStore()
|
||||
|
||||
const conf = new Configuration
|
||||
conf.basePath = configStore.apiServer
|
||||
|
||||
conf.apiKey = "Bearer " + userStore.jwt_token
|
||||
const document = new DocumentsApi(conf);
|
||||
const library = new LibrariesApi(conf);
|
||||
|
||||
|
||||
export {
|
||||
document, library, conf
|
||||
}
|
14
src/plugins/config.ts
Normal file
14
src/plugins/config.ts
Normal file
@ -0,0 +1,14 @@
|
||||
let staticConfig = {
|
||||
url: "https://auth.leaflow.cn/",
|
||||
realm: "UserLand",
|
||||
clientId: "Leaf Library",
|
||||
|
||||
scope: "openid profile",
|
||||
checkLoginIframe: false,
|
||||
redirectUri: window.location.origin + "/auth/login",
|
||||
|
||||
rpc_base_url: "http://127.0.0.1:18081"
|
||||
}
|
||||
|
||||
|
||||
export default staticConfig
|
41
src/plugins/keycloak.ts
Normal file
41
src/plugins/keycloak.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import Keycloak, { KeycloakConfig, KeycloakInitOptions } from "keycloak-js";
|
||||
import config from "@/plugins/config"
|
||||
|
||||
|
||||
const keycloakConfig: KeycloakConfig = {
|
||||
url: config.url,
|
||||
realm: config.realm,
|
||||
clientId: config.clientId,
|
||||
}
|
||||
|
||||
const keycloakOptions: KeycloakInitOptions = {
|
||||
scope: config.scope,
|
||||
checkLoginIframe: config.checkLoginIframe,
|
||||
redirectUri: config.redirectUri,
|
||||
// onLoad: 'login-required',
|
||||
|
||||
}
|
||||
|
||||
// kc.updateToken
|
||||
|
||||
// async function newKeycloak() {
|
||||
// return new Keycloak(configStore.getKeyCloakConfig());
|
||||
// }
|
||||
|
||||
|
||||
const kc = new Keycloak(keycloakConfig);
|
||||
|
||||
kc.init(keycloakOptions).then((auth) => {
|
||||
console.log(`User is ${auth ? 'authenticated' : 'not authenticated'}`);
|
||||
|
||||
kc.onTokenExpired = () => {
|
||||
kc.updateToken(5).then(auth => {
|
||||
console.log(auth);
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// save kc to local storage
|
||||
localStorage.setItem('keycloak', JSON.stringify(kc));
|
||||
|
||||
export default kc
|
24
src/plugins/rpc.ts
Normal file
24
src/plugins/rpc.ts
Normal file
@ -0,0 +1,24 @@
|
||||
|
||||
import { GrpcWebFetchTransport } from "@protobuf-ts/grpcweb-transport";
|
||||
import keycloak from "@/plugins/keycloak"
|
||||
import { DocumentServiceClient } from "@/proto/document/document.client";
|
||||
import { LibraryServiceClient } from "@/proto/library/library.client";
|
||||
import config from "@/plugins/config"
|
||||
|
||||
let transport = new GrpcWebFetchTransport({
|
||||
baseUrl: config.rpc_base_url,
|
||||
format: "text",
|
||||
meta: {
|
||||
"Authorization": "Bearer " + keycloak.idToken,
|
||||
},
|
||||
timeout: 100 * 1000,
|
||||
});
|
||||
|
||||
|
||||
let libraryServiceClient = new LibraryServiceClient(transport)
|
||||
let documentClient = new DocumentServiceClient(transport)
|
||||
|
||||
export {
|
||||
libraryServiceClient,
|
||||
documentClient
|
||||
}
|
@ -1,14 +1,15 @@
|
||||
// Composables
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
import { useUserStore } from "@/store/user";
|
||||
import { useConfigStore } from "@/store/config";
|
||||
import keycloak from "@/plugins/keycloak"
|
||||
import { useLoginUrlStore } from "@/store/login_url";
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: "/",
|
||||
name: "home",
|
||||
meta: {
|
||||
auth: true,
|
||||
auth: false,
|
||||
},
|
||||
// route level code-splitting
|
||||
// this generates a separate chunk (Home-[hash].js) for this route
|
||||
@ -23,6 +24,9 @@ const routes = [
|
||||
{
|
||||
path: "/libraries",
|
||||
name: "libraries",
|
||||
meta: {
|
||||
auth: true,
|
||||
},
|
||||
component: () => import("@/views/libraries/List.vue"),
|
||||
},
|
||||
{
|
||||
@ -59,12 +63,30 @@ const router = createRouter({
|
||||
});
|
||||
|
||||
router.beforeEach((to, from) => {
|
||||
|
||||
// get route name
|
||||
console.log(from.name, from.fullPath)
|
||||
|
||||
const userStore = useUserStore();
|
||||
const configStore = useConfigStore();
|
||||
const loginUrl = useLoginUrlStore();
|
||||
|
||||
if (to.name != "login") {
|
||||
console.log("last visit:" + to.fullPath)
|
||||
// 设置最后访问 url
|
||||
loginUrl.setLastVisitUrl(to.fullPath);
|
||||
}
|
||||
|
||||
// // 如果来自登录的 url,则跳转到最后访问的 url
|
||||
// if (from.name === "login") {
|
||||
// const last_visit = loginUrl.getLastVisitUrl()
|
||||
|
||||
// if (last_visit) {
|
||||
// console.log("跳转:" + last_visit)
|
||||
// // window.location.href = last_visit
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// if (to.matched.length === 0) {
|
||||
// return router.push({ name: "errors.404" });
|
||||
@ -76,9 +98,8 @@ router.beforeEach((to, from) => {
|
||||
document.title = configStore.getAppName();
|
||||
}
|
||||
|
||||
if (to.meta.auth == true) {
|
||||
// validate login state
|
||||
if (userStore.jwt_token == null) {
|
||||
if (to.meta.auth) {
|
||||
if (!keycloak.authenticated) {
|
||||
router.push({ name: "login" });
|
||||
}
|
||||
} else {
|
||||
|
@ -5,23 +5,11 @@ export const useConfigStore = defineStore('app', {
|
||||
state: () => ({
|
||||
appName: "资料库",
|
||||
description: "Leaf Library",
|
||||
accountServer: "https://oauth.leaflow.cn",
|
||||
apiServer: "https://document-api.leaflow.cn/api",
|
||||
// apiServer: "http://localhost:8080/api",
|
||||
}),
|
||||
actions: {
|
||||
getAppName(): string {
|
||||
return this.appName !== null ? this.appName : "Leaf Library"
|
||||
},
|
||||
getRefreshUrl(): string {
|
||||
return this.accountServer + "/public/auth_request/refresh"
|
||||
},
|
||||
getLoginUrl(): string {
|
||||
let url = new URL(this.accountServer + "/public/auth_request")
|
||||
url.searchParams.append("description", this.description)
|
||||
// url.searchParams.append("callback_uri", window.location.href)
|
||||
url.searchParams.append("attributes[app]", "todo")
|
||||
return url.toString()
|
||||
}
|
||||
},
|
||||
})
|
@ -1,12 +0,0 @@
|
||||
import {defineStore} from 'pinia'
|
||||
import axios from "axios";
|
||||
import {useConfigStore} from "./config";
|
||||
|
||||
export const useLibrariesStore = defineStore('libraries', {
|
||||
state: () => ({
|
||||
libraries: [{
|
||||
ID: 0,
|
||||
Name: "",
|
||||
}],
|
||||
}),
|
||||
})
|
18
src/store/login_url.ts
Normal file
18
src/store/login_url.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import {defineStore} from 'pinia'
|
||||
|
||||
export const useLoginUrlStore = defineStore('login_url', {
|
||||
persist: true,
|
||||
state: () => ({
|
||||
last_visit_url: null as null | string
|
||||
}),
|
||||
|
||||
actions: {
|
||||
setLastVisitUrl(url: string) {
|
||||
this.last_visit_url = url
|
||||
},
|
||||
getLastVisitUrl() {
|
||||
return this.last_visit_url
|
||||
}
|
||||
}
|
||||
|
||||
})
|
@ -1,68 +0,0 @@
|
||||
import { defineStore } from "pinia";
|
||||
import axios from "axios";
|
||||
import { useConfigStore } from "./config";
|
||||
|
||||
const useUserStore = defineStore("user", {
|
||||
persist: true,
|
||||
state: () => ({
|
||||
refresh_token: null as null | string,
|
||||
jwt_token: null as null | string,
|
||||
expired_at: null as null | Date,
|
||||
}),
|
||||
getters: {
|
||||
isLoggedIn(): boolean {
|
||||
return this.jwt_token !== null;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
login(refresh_token: string, jwt_token: string) {
|
||||
this.refresh_token = refresh_token;
|
||||
this.jwt_token = jwt_token;
|
||||
},
|
||||
setExpired(expires_in: number) {
|
||||
// get date(current + seconds)
|
||||
this.expired_at = new Date(Date.now() + expires_in * 1000);
|
||||
},
|
||||
logout() {
|
||||
console.log("logout");
|
||||
this.refresh_token = null;
|
||||
this.jwt_token = null;
|
||||
},
|
||||
refresh() {
|
||||
let config = useConfigStore();
|
||||
|
||||
axios
|
||||
.post(config.getRefreshUrl(), {
|
||||
refresh_token: this.refresh_token,
|
||||
})
|
||||
.then((r) => {
|
||||
this.jwt_token = r.data.token;
|
||||
this.setExpired(r.data.expires_in);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
this.logout();
|
||||
});
|
||||
},
|
||||
get_token() {
|
||||
return this.jwt_token;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
setInterval(() => {
|
||||
// check expired
|
||||
const currentDate = new Date();
|
||||
|
||||
const userStore = useUserStore();
|
||||
const expiredAt = new Date(userStore.expired_at || 0);
|
||||
|
||||
// if has expired
|
||||
if (userStore.expired_at !== null && expiredAt < currentDate) {
|
||||
if (userStore.jwt_token !== null && userStore.refresh_token !== null) {
|
||||
userStore.refresh();
|
||||
}
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
export { useUserStore };
|
@ -1,123 +1,46 @@
|
||||
<template>
|
||||
<div
|
||||
class="d-flex text-center"
|
||||
style="
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
"
|
||||
>
|
||||
<div>
|
||||
<div>
|
||||
<v-progress-circular
|
||||
indeterminate
|
||||
v-if="status == 0 || status == 3 || status == 1"
|
||||
></v-progress-circular>
|
||||
</div>
|
||||
<div class="mt-5">
|
||||
<div v-if="status == 0">
|
||||
<p>正在请求账户服务器...</p>
|
||||
</div>
|
||||
<div v-if="status == 1">
|
||||
<p>前往授权页面。</p>
|
||||
</div>
|
||||
<div v-if="status == 2">
|
||||
<v-icon color="red">mdi-alert</v-icon>
|
||||
<p>创建 Auth Requests 时出现了问题。</p>
|
||||
|
||||
<v-btn @click="restart" class="mt-3">重试</v-btn>
|
||||
</div>
|
||||
<div v-if="status == 3">
|
||||
<p>正在获取 Token</p>
|
||||
</div>
|
||||
<div v-if="status == 4">
|
||||
<p>验证失败。</p>
|
||||
|
||||
<v-btn @click="restart" class="mt-3">重试</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<p>请稍等,我们正在让您登录。</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import axios from "axios";
|
||||
import { ref } from "vue";
|
||||
import { useConfigStore } from "@/store/config";
|
||||
import { useUserStore } from "@/store/user";
|
||||
import router from "@/router";
|
||||
import keycloak from "@/plugins/keycloak";
|
||||
import { useLoginUrlStore } from "@/store/login_url";
|
||||
|
||||
const status = ref(0);
|
||||
const configStore = useConfigStore();
|
||||
const userStore = useUserStore();
|
||||
const login = async () => {
|
||||
try {
|
||||
await keycloak.login();
|
||||
|
||||
console.log(configStore.accountServer);
|
||||
back();
|
||||
} catch (error) {
|
||||
console.error("Failed to initialize adapter:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const restart = () => {
|
||||
status.value = 0;
|
||||
const urlHash = window.location.hash;
|
||||
|
||||
if (!urlHash) {
|
||||
login();
|
||||
};
|
||||
|
||||
// read the router query para
|
||||
const query = router.currentRoute.value.query;
|
||||
|
||||
const login = () => {
|
||||
// clear url
|
||||
window.history.replaceState(null, "", window.location.pathname);
|
||||
|
||||
axios
|
||||
.post(configStore.accountServer + "/public/auth_request", {
|
||||
description: configStore.appName,
|
||||
callback_uri: window.location.href,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.data.url) {
|
||||
status.value = 1;
|
||||
|
||||
window.location.href = res.data.url;
|
||||
}
|
||||
|
||||
// console.log(status.value)
|
||||
// console.log(res.data);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
status.value = 2;
|
||||
});
|
||||
};
|
||||
|
||||
const validate = () => {
|
||||
status.value = 3;
|
||||
|
||||
axios
|
||||
.get(
|
||||
configStore.accountServer + "/public/auth_request/" + query.auth_request
|
||||
)
|
||||
.then((res) => {
|
||||
if (res.data.refresh_token && res.data.token) {
|
||||
userStore.login(res.data.refresh_token, res.data.token);
|
||||
|
||||
userStore.setExpired(res.data.expires_in);
|
||||
console.log(userStore.expired_at);
|
||||
|
||||
setTimeout(() => {
|
||||
// window.location.href = "/";
|
||||
router.push("/");
|
||||
}, 100);
|
||||
}
|
||||
|
||||
console.log(res.data);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
status.value = 4;
|
||||
});
|
||||
};
|
||||
|
||||
if (query.auth_request != null) {
|
||||
validate();
|
||||
} else {
|
||||
login();
|
||||
back();
|
||||
}
|
||||
|
||||
function back() {
|
||||
const loginUrl = useLoginUrlStore();
|
||||
|
||||
let last_url = loginUrl.getLastVisitUrl();
|
||||
|
||||
if (!last_url) {
|
||||
router.push({
|
||||
path: "/",
|
||||
});
|
||||
} else {
|
||||
console.log("跳转:" + last_url);
|
||||
router.push({
|
||||
path: last_url,
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,213 +0,0 @@
|
||||
<template>
|
||||
<v-btn class="mb-3" @click="router.back()">返回</v-btn>
|
||||
|
||||
<h2>新建文档</h2>
|
||||
|
||||
<div class="form-floating mb-3">
|
||||
<v-text-field
|
||||
v-model="createData.Title"
|
||||
label="文档标题"
|
||||
required
|
||||
hide-details
|
||||
></v-text-field>
|
||||
</div>
|
||||
|
||||
<MdEditor :theme="getTheme" v-model="createData.Content" />
|
||||
|
||||
<v-btn class="mt-3" color="primary" @click="createDocument">新建文档</v-btn>
|
||||
|
||||
<v-file-input
|
||||
class="mt-3"
|
||||
:error-messages="errorMsg"
|
||||
label="加载 PDF / DOCX"
|
||||
v-model="file"
|
||||
></v-file-input>
|
||||
|
||||
<v-alert
|
||||
v-show="created"
|
||||
class="mt-3"
|
||||
type="success"
|
||||
title="创建完成"
|
||||
text="您成功创建了一篇文档,稍后我们将开始索引您的文档。在完成后您就可以使用 AI 工具了。"
|
||||
></v-alert>
|
||||
|
||||
<v-snackbar v-model="show_created_failed" vertical>
|
||||
<div class="text-subtitle-1 pb-2">创建失败</div>
|
||||
|
||||
<p>可能出现了一些问题,比如您没有完整填写,或者某一个内容超出了长度。</p>
|
||||
|
||||
<template v-slot:actions>
|
||||
<v-btn color="pink" variant="text" @click="show_created_failed = false">
|
||||
关闭
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-snackbar>
|
||||
|
||||
<v-snackbar v-model="show_title_is_not_filled">
|
||||
<p>您还没有填写标题。</p>
|
||||
|
||||
<template v-slot:actions>
|
||||
<v-btn
|
||||
color="pink"
|
||||
variant="text"
|
||||
@click="show_title_is_not_filled = false"
|
||||
>
|
||||
关闭
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-snackbar>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { MdEditor } from "md-editor-v3";
|
||||
import "md-editor-v3/lib/style.css";
|
||||
import router from "@/router/index";
|
||||
import { document } from "@/plugins/api";
|
||||
import getTheme from "@/plugins/getTheme";
|
||||
import { watch } from "vue";
|
||||
import { getDocument } from "pdfjs-dist";
|
||||
import "pdfjs-dist/build/pdf.worker.mjs";
|
||||
import { convertToHtml } from "mammoth/mammoth.browser";
|
||||
import html2markdown from "@notable/html2markdown";
|
||||
|
||||
// 错误提示
|
||||
const errorMsg = ref("");
|
||||
|
||||
const created = ref(false);
|
||||
|
||||
const libraryIdInt = parseInt(router.currentRoute.value.params.LibraryId);
|
||||
|
||||
const createData = ref({
|
||||
Title: "",
|
||||
Content: "# Markdown 支持",
|
||||
});
|
||||
|
||||
const show_created_failed = ref(false);
|
||||
const show_title_is_not_filled = ref(false);
|
||||
|
||||
function createDocument() {
|
||||
if (createData.value.Title == "") {
|
||||
show_title_is_not_filled.value = true;
|
||||
return;
|
||||
}
|
||||
|
||||
document
|
||||
.libraryLibraryIdDocumentsPost(
|
||||
libraryIdInt,
|
||||
{
|
||||
Title: createData.value.Title,
|
||||
Content: createData.value.Content,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
)
|
||||
.then((r) => {
|
||||
console.log(r);
|
||||
|
||||
created.value = true;
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
show_created_failed.value = true;
|
||||
});
|
||||
}
|
||||
|
||||
const file = ref([]);
|
||||
|
||||
watch(file, (newVal) => {
|
||||
console.log(newVal[0]);
|
||||
|
||||
if (newVal.length == 0) {
|
||||
errorMsg.value = "";
|
||||
return;
|
||||
}
|
||||
|
||||
// get file content
|
||||
const reader = new FileReader();
|
||||
reader.readAsArrayBuffer(newVal[0]);
|
||||
reader.onload = () => {
|
||||
// 如果不是 pdf 或者 docx
|
||||
if (newVal[0].type == "application/pdf") {
|
||||
const typedarray = new Uint8Array(reader.result);
|
||||
|
||||
getDocument(typedarray).promise.then(
|
||||
function (pdf) {
|
||||
// 获取PDF文档中所有页面的文本内容
|
||||
const pagesPromises = [];
|
||||
for (let i = 0; i < pdf.numPages; i++) {
|
||||
// Required to prevent that i is always the total of pages
|
||||
((pageNumber) => {
|
||||
pagesPromises.push(getPdfPageText(pageNumber, pdf));
|
||||
})(i + 1);
|
||||
}
|
||||
|
||||
Promise.all(pagesPromises).then(function (pagesText) {
|
||||
// Display text of all the pages in the console
|
||||
// console.log(pagesText); // 或者处理文本内容
|
||||
|
||||
// fill to createData
|
||||
createData.value.Content = pagesText.join("\n\n");
|
||||
});
|
||||
},
|
||||
function (error) {
|
||||
console.error(error);
|
||||
}
|
||||
);
|
||||
} else if (
|
||||
newVal[0].type ==
|
||||
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
||||
) {
|
||||
convertToHtml({ arrayBuffer: reader.result }).then(function (
|
||||
resultObject
|
||||
) {
|
||||
let html = resultObject.value; // The generated HTML
|
||||
let messages = resultObject.messages; // Any messages, such as warnings during conversion
|
||||
|
||||
createData.value.Content = html2markdown(html);
|
||||
|
||||
if (createData.value.Title == "") {
|
||||
// get first line of Content
|
||||
|
||||
const firstLine = createData.value.Content.split("\n")[0] ?? "";
|
||||
|
||||
if (firstLine) {
|
||||
createData.value.Title = firstLine;
|
||||
}
|
||||
}
|
||||
|
||||
// console.log(html);
|
||||
console.warn(messages);
|
||||
});
|
||||
} else {
|
||||
errorMsg.value = "您只能选择 DPF 或者 DOCX 文件。";
|
||||
return;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
function getPdfPageText(pageNum, PDFDocumentInstance) {
|
||||
// 返回一个Promise,该Promise解决为页面文本内容
|
||||
return new Promise(function (resolve, reject) {
|
||||
PDFDocumentInstance.getPage(pageNum).then(function (pdfPage) {
|
||||
// 返回页面文本内容
|
||||
pdfPage.getTextContent().then(function (textContent) {
|
||||
let textItems = textContent.items;
|
||||
let finalString = "";
|
||||
|
||||
// Concatenate the string of the item to the final string
|
||||
for (let i = 0; i < textItems.length; i++) {
|
||||
let item = textItems[i];
|
||||
finalString += item.str + " ";
|
||||
}
|
||||
|
||||
// Solve promise with the text content of the page
|
||||
resolve(finalString);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
@ -1,112 +0,0 @@
|
||||
<template>
|
||||
<v-btn class="mb-3" @click="router.back()">返回</v-btn>
|
||||
|
||||
<div v-if="loading">
|
||||
<LoadingComponent> </LoadingComponent>
|
||||
</div>
|
||||
<div v-else>
|
||||
<h2>编辑: {{ docu.Title }}</h2>
|
||||
|
||||
<div class="form-floating mb-3">
|
||||
<v-text-field
|
||||
v-model="docu.Title"
|
||||
label="文档标题"
|
||||
required
|
||||
hide-details
|
||||
></v-text-field>
|
||||
</div>
|
||||
|
||||
<MdEditor :theme="getTheme" v-model="docu.Content" />
|
||||
|
||||
<v-btn class="mt-3" color="primary" @click="updateDocument">更新文档</v-btn>
|
||||
</div>
|
||||
|
||||
<v-snackbar v-model="show_created_failed" vertical>
|
||||
<div class="text-subtitle-1 pb-2">更新失败</div>
|
||||
|
||||
<p>可能出现了一些问题,比如您没有完整填写,或者某一个内容超出了长度。</p>
|
||||
|
||||
<template v-slot:actions>
|
||||
<v-btn color="pink" variant="text" @click="show_created_failed = false">
|
||||
关闭
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-snackbar>
|
||||
|
||||
<v-snackbar v-model="show_title_is_not_filled">
|
||||
<p>您还没有填写标题。</p>
|
||||
|
||||
<template v-slot:actions>
|
||||
<v-btn
|
||||
color="pink"
|
||||
variant="text"
|
||||
@click="show_title_is_not_filled = false"
|
||||
>
|
||||
关闭
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-snackbar>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { MdEditor } from "md-editor-v3";
|
||||
import "md-editor-v3/lib/style.css";
|
||||
import router from "@/router/index";
|
||||
import { document } from "@/plugins/api";
|
||||
import getTheme from "@/plugins/getTheme";
|
||||
import LoadingComponent from "@/components/Loading.vue";
|
||||
|
||||
const loading = ref(true);
|
||||
const libraryIdInt = parseInt(router.currentRoute.value.params.LibraryId);
|
||||
const documentIdInt = parseInt(router.currentRoute.value.params.DocumentId);
|
||||
|
||||
const docu = ref({
|
||||
Title: "",
|
||||
Content: "",
|
||||
});
|
||||
|
||||
document
|
||||
.libraryLibraryIdDocumentDocumentIdGet(libraryIdInt, documentIdInt)
|
||||
.then((r) => {
|
||||
docu.value = r.data;
|
||||
|
||||
console.log(r.data);
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
|
||||
const show_created_failed = ref(false);
|
||||
const show_title_is_not_filled = ref(false);
|
||||
|
||||
function goto_document() {
|
||||
router.push({
|
||||
name: "library.documents.view",
|
||||
params: {
|
||||
DocumentId: documentIdInt,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function updateDocument() {
|
||||
if (docu.value.Title == "") {
|
||||
show_title_is_not_filled.value = true;
|
||||
return;
|
||||
}
|
||||
|
||||
document
|
||||
.libraryLibraryIdDocumentDocumentIdPut(
|
||||
libraryIdInt,
|
||||
documentIdInt,
|
||||
docu.value.Title,
|
||||
docu.value.Content
|
||||
)
|
||||
.then((r) => {
|
||||
goto_document()
|
||||
})
|
||||
.catch(() => {
|
||||
show_created_failed.value = true;
|
||||
});
|
||||
}
|
||||
</script>
|
@ -1,133 +0,0 @@
|
||||
<template>
|
||||
<div v-if="loading">
|
||||
<v-row>
|
||||
<v-col cols="12" md="4" xl="2" v-for="n in 6" :key="n">
|
||||
<v-skeleton-loader
|
||||
v-for="n in 2"
|
||||
:key="n"
|
||||
class="mx-auto border mt-3"
|
||||
max-width="300"
|
||||
type="article,actions"
|
||||
:elevation="24"
|
||||
></v-skeleton-loader>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="mb-3">
|
||||
<v-btn @click="goto_create_document">创建</v-btn>
|
||||
<v-btn class="ml-2" @click="goto_query_library">查询</v-btn>
|
||||
</div>
|
||||
|
||||
<v-row>
|
||||
<v-col
|
||||
cols="12"
|
||||
md="4"
|
||||
xl="2"
|
||||
v-for="document in documents.Data"
|
||||
:key="document.id"
|
||||
>
|
||||
<v-card>
|
||||
<v-card-title>{{ document.Title }}</v-card-title>
|
||||
<v-card-subtitle>{{
|
||||
document.Chunked ? "分块的文档" : "还没有处理"
|
||||
}}</v-card-subtitle>
|
||||
<v-card-subtitle>
|
||||
{{ new Date(document.CreatedAt).toLocaleString() }}
|
||||
</v-card-subtitle>
|
||||
<v-card-text>{{ document.content }}</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn text color="primary" @click="goto_document(document.ID)"
|
||||
>预览</v-btn
|
||||
>
|
||||
<v-btn text color="secondary">编辑</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
<div v-if="documents.Total == 0" class="mt-5">
|
||||
<v-alert text="看样子你还没有创建任何文档。"></v-alert>
|
||||
</div>
|
||||
|
||||
<div class="text-center">
|
||||
<v-container>
|
||||
<v-row justify="center">
|
||||
<v-col cols="8">
|
||||
<v-container class="max-width">
|
||||
<v-pagination
|
||||
v-model="page"
|
||||
class="my-4"
|
||||
:length="documents.Total"
|
||||
:disabled="loading"
|
||||
@update:model-value="load"
|
||||
></v-pagination>
|
||||
</v-container>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { document } from "@/plugins/api";
|
||||
import router from "@/router";
|
||||
import { ref } from "vue";
|
||||
|
||||
const libraryId = router.currentRoute.value.params.LibraryId;
|
||||
const page = ref(1);
|
||||
const documents = ref({});
|
||||
const loading = ref(false);
|
||||
|
||||
function load() {
|
||||
loading.value = true;
|
||||
|
||||
document
|
||||
.libraryLibraryIdDocumentsGet(page.value, libraryId)
|
||||
.then((r) => {
|
||||
documents.value = r.data;
|
||||
|
||||
|
||||
console.log(page.value)
|
||||
if (page.value == 1) {
|
||||
if (r.data.Data.length == 1) {
|
||||
goto_document(r.data.Data[0].ID);
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
load();
|
||||
console.log(libraryId);
|
||||
|
||||
function goto_create_document() {
|
||||
router.push({
|
||||
name: "library.documents.create",
|
||||
params: {
|
||||
LibraryId: libraryId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function goto_document(documentId) {
|
||||
router.push({
|
||||
name: "library.documents.view",
|
||||
params: {
|
||||
DocumentId: documentId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function goto_query_library() {
|
||||
router.push({
|
||||
name: "library.query",
|
||||
params: {
|
||||
LibraryId: libraryId,
|
||||
},
|
||||
});
|
||||
}
|
||||
</script>
|
@ -1,113 +0,0 @@
|
||||
<script setup>
|
||||
import router from "@/router";
|
||||
import { ref } from "vue";
|
||||
import { document } from "@/plugins/api";
|
||||
import { MdPreview } from "md-editor-v3";
|
||||
import LoadingComponent from "@/components/Loading.vue";
|
||||
import getTheme from "@/plugins/getTheme";
|
||||
|
||||
const libraryId = parseInt(router.currentRoute.value.params.LibraryId);
|
||||
const documentId = parseInt(router.currentRoute.value.params.DocumentId);
|
||||
const loading = ref(true);
|
||||
|
||||
const docu = ref({
|
||||
Title: "",
|
||||
Content: "",
|
||||
});
|
||||
|
||||
const status = ref({
|
||||
Total: 0,
|
||||
Chunked: 0,
|
||||
Completed: null,
|
||||
});
|
||||
|
||||
document
|
||||
.libraryLibraryIdDocumentDocumentIdGet(libraryId, documentId)
|
||||
.then((r) => {
|
||||
docu.value = r.data;
|
||||
|
||||
console.log(r.data);
|
||||
|
||||
document
|
||||
.libraryLibraryIdDocumentDocumentIdChunksGet(libraryId, documentId)
|
||||
.then((r) => {
|
||||
status.value.Total = r.data.Total;
|
||||
status.value.Chunked = r.data.Chunked;
|
||||
status.value.Completed = r.data.Completed;
|
||||
|
||||
console.log(status.value);
|
||||
|
||||
// calc percent
|
||||
if (status.value.Completed !== null) {
|
||||
if (status.value.Completed) {
|
||||
status.value.Chunked = status.value.Total;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(
|
||||
"percent: " + (status.value.Chunked / status.value.Total) * 100
|
||||
);
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
|
||||
function goto_create_document() {
|
||||
router.push({
|
||||
name: "library.documents.create",
|
||||
params: {
|
||||
LibraryId: libraryId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function goto_query_library() {
|
||||
router.push({
|
||||
name: "library.query",
|
||||
params: {
|
||||
LibraryId: libraryId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function goto_edit_document() {
|
||||
router.push({
|
||||
name: "library.documents.edit",
|
||||
params: {
|
||||
LibraryId: libraryId,
|
||||
DocumentId: documentId,
|
||||
},
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<v-btn @click="router.back(0)">返回</v-btn>
|
||||
<v-btn class="ml-2" @click="goto_create_document">新建文档</v-btn>
|
||||
<v-btn class="ml-2" @click="goto_edit_document">修改文档</v-btn>
|
||||
<v-btn class="ml-2" @click="goto_query_library">查询</v-btn>
|
||||
<v-tooltip :text="status.Completed ? '文档处理完成' : '正在处理 ' + status.Chunked + ' 于 ' + status.Total">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-progress-circular
|
||||
v-bind="props"
|
||||
class="ml-2"
|
||||
:indeterminate="status.Completed == null"
|
||||
:model-value="(status.Chunked / status.Total) * 100"
|
||||
></v-progress-circular>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
|
||||
<!-- <v-progress-circular class="ml-2" v-else indeterminate></v-progress-circular> -->
|
||||
|
||||
<div v-if="loading">
|
||||
<LoadingComponent> </LoadingComponent>
|
||||
</div>
|
||||
<div v-else>
|
||||
<h2>{{ docu.Title }}</h2>
|
||||
|
||||
<MdPreview class="mt-3" :theme="getTheme" :modelValue="docu.Content" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@ -1,125 +1,30 @@
|
||||
<template>
|
||||
<div>
|
||||
<LoadingComponent v-if="loading"> </LoadingComponent>
|
||||
<div v-else>
|
||||
<h1>资料库</h1>
|
||||
|
||||
<v-btn
|
||||
@click="selectAll"
|
||||
:color="selected_ids.length > 0 ? 'primary' : ''"
|
||||
>全选</v-btn
|
||||
>
|
||||
|
||||
<v-btn
|
||||
class="ml-2"
|
||||
:disabled="selected_ids.length === 0"
|
||||
:loading="is_deleting"
|
||||
>删除所选
|
||||
|
||||
<v-dialog v-model="show_dialog" activator="parent" width="auto">
|
||||
<v-card>
|
||||
<v-card-text
|
||||
v-text="is_deleting ? '正在为你准备新项目。' : '确认删除所选项?'"
|
||||
></v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn
|
||||
color="primary"
|
||||
@click="deleteSelected"
|
||||
:loading="is_deleting"
|
||||
>确认删除</v-btn
|
||||
>
|
||||
<v-btn @click="show_dialog = false">取消</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</v-btn>
|
||||
|
||||
<v-btn :loading="creatingLibrary" class="ml-2"
|
||||
>新建资料库
|
||||
|
||||
<v-dialog
|
||||
v-model="showCreateLibraryDialog"
|
||||
activator="parent"
|
||||
width="auto"
|
||||
>
|
||||
<v-card>
|
||||
<v-text-field
|
||||
v-model="newLibraryName"
|
||||
label="资料库名称"
|
||||
required
|
||||
hide-details
|
||||
></v-text-field>
|
||||
|
||||
<v-card-actions>
|
||||
<v-btn @click="showCreateLibraryDialog = false">取消</v-btn>
|
||||
|
||||
<v-btn
|
||||
color="primary"
|
||||
@click="newLibrary"
|
||||
:loading="creatingLibrary"
|
||||
>新建</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</v-btn>
|
||||
|
||||
<v-card class="mt-3">
|
||||
<v-list>
|
||||
<v-list-item
|
||||
v-for="item in libraries.Data"
|
||||
:key="item.ID"
|
||||
@click.stop="goto(item.ID)"
|
||||
>
|
||||
<template v-slot:prepend="{ isActive }">
|
||||
<v-list-item-action start>
|
||||
<v-checkbox-btn
|
||||
:model-value="isSelected(item.ID)"
|
||||
@click.stop="toggleSelection(item.ID)"
|
||||
></v-checkbox-btn>
|
||||
</v-list-item-action>
|
||||
</template>
|
||||
|
||||
<template v-slot:append="{ isActive }">
|
||||
<v-list-item-action end>
|
||||
<v-btn rounded="xl" @click.stop="console.log(isActive)">
|
||||
<v-icon>mdi-check</v-icon>
|
||||
</v-btn>
|
||||
</v-list-item-action>
|
||||
</template>
|
||||
|
||||
<v-list-item-title>{{ item.Name }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card>
|
||||
</div>
|
||||
|
||||
<div class="text-center">
|
||||
<v-container>
|
||||
<v-row justify="center">
|
||||
<v-col cols="8">
|
||||
<v-container class="max-width">
|
||||
<v-pagination
|
||||
v-model="page"
|
||||
class="my-4"
|
||||
:length="libraries.Total"
|
||||
:disabled="loading"
|
||||
@update:model-value="load"
|
||||
></v-pagination>
|
||||
</v-container>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</div>
|
||||
<p>资料库</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup >
|
||||
import { library } from "@/plugins/api";
|
||||
import { ref } from "vue";
|
||||
import LoadingComponent from "@/components/Loading.vue";
|
||||
import router from "@/router";
|
||||
|
||||
import * as libraryProto from "@/proto/library/library";
|
||||
import * as documentProto from "@/proto/document/document";
|
||||
import keycloak from "@/plugins/keycloak"
|
||||
import {libraryServiceClient} from "@/plugins/rpc"
|
||||
|
||||
let listLibrary = {
|
||||
page: 1
|
||||
}
|
||||
|
||||
|
||||
libraryServiceClient.listLibraries(listLibrary).then(res => {
|
||||
console.log(res)
|
||||
})
|
||||
|
||||
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
const page = ref(1);
|
||||
@ -128,121 +33,4 @@ const selected_ids = ref([]);
|
||||
const is_deleting = ref(false);
|
||||
const show_dialog = ref(false);
|
||||
|
||||
const load = (show_load = true) => {
|
||||
if (show_load) {
|
||||
loading.value = true;
|
||||
}
|
||||
|
||||
selected_ids.value = [];
|
||||
library
|
||||
.librariesGet(page.value)
|
||||
.then((r) => {
|
||||
libraries.value = r.data;
|
||||
page.value = r.data.Page;
|
||||
console.log(r.data.Data);
|
||||
})
|
||||
.finally(() => {
|
||||
if (show_load) {
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
load();
|
||||
|
||||
const goto = (libraryId) => {
|
||||
router.push({
|
||||
name: "library.documents",
|
||||
params: {
|
||||
LibraryId: libraryId,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
function isSelected(id) {
|
||||
return selected_ids.value.includes(id);
|
||||
}
|
||||
|
||||
function toggleSelection(id) {
|
||||
const index = selected_ids.value.indexOf(id);
|
||||
if (index !== -1) {
|
||||
// 如果已经选中,点击后移除
|
||||
selected_ids.value.splice(index, 1);
|
||||
} else {
|
||||
// 如果没有选中,点击后添加
|
||||
selected_ids.value.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects all items if none are selected, otherwise clears the selection.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
function selectAll() {
|
||||
if (selected_ids.value.length > 0) {
|
||||
selected_ids.value = [];
|
||||
} else {
|
||||
const data = libraries.value.Data;
|
||||
if (data.length > 0) {
|
||||
data.forEach((item) => selected_ids.value.push(item.ID));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function deleteSelected() {
|
||||
if (selected_ids.value.length > 0) {
|
||||
is_deleting.value = true;
|
||||
selected_ids.value.forEach((item) => {
|
||||
library.libraryLibraryIdDelete(item).then((r) => {
|
||||
console.log("Library deleted: " + item);
|
||||
|
||||
// remove
|
||||
libraries.value.Data = libraries.value.Data.filter(
|
||||
(i) => i.ID !== item
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
selected_ids.value = [];
|
||||
setTimeout(() => {
|
||||
if (page.value == libraries.value.Page) {
|
||||
page.value = page.value - 1;
|
||||
}
|
||||
if (page.value == 0) {
|
||||
page.value = 1;
|
||||
}
|
||||
|
||||
load(false);
|
||||
|
||||
is_deleting.value = false;
|
||||
|
||||
show_dialog.value = false;
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
const showCreateLibraryDialog = ref(false);
|
||||
const creatingLibrary = ref(false);
|
||||
const newLibraryName = ref("");
|
||||
|
||||
function newLibrary() {
|
||||
if (newLibraryName.value == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
creatingLibrary.value = true;
|
||||
|
||||
library
|
||||
.librariesPost(newLibraryName.value)
|
||||
.then(() => {})
|
||||
.finally(() => {
|
||||
showCreateLibraryDialog.value = false;
|
||||
creatingLibrary.value = false;
|
||||
|
||||
setTimeout(() => {
|
||||
load();
|
||||
}, 250);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
@ -4,7 +4,6 @@
|
||||
"target": "esnext",
|
||||
"useDefineForClassFields": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"allowImportingTsExtensions": true,
|
||||
"composite": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
|
18
yarn.lock
18
yarn.lock
@ -1855,6 +1855,11 @@ isexe@^2.0.0:
|
||||
resolved "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz"
|
||||
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
|
||||
|
||||
js-sha256@^0.11.0:
|
||||
version "0.11.0"
|
||||
resolved "https://mirrors.cloud.tencent.com/npm/js-sha256/-/js-sha256-0.11.0.tgz#256a921d9292f7fe98905face82e367abaca9576"
|
||||
integrity sha512-6xNlKayMZvds9h1Y1VWc0fQHQ82BxTXizWPEtEeGvmOUYpBRy4gbWroHLpzowe6xiQhHpelCQiE7HEdznyBL9Q==
|
||||
|
||||
js-yaml@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz"
|
||||
@ -1887,6 +1892,19 @@ jszip@^3.7.1:
|
||||
readable-stream "~2.3.6"
|
||||
setimmediate "^1.0.5"
|
||||
|
||||
jwt-decode@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://mirrors.cloud.tencent.com/npm/jwt-decode/-/jwt-decode-4.0.0.tgz#2270352425fd413785b2faf11f6e755c5151bd4b"
|
||||
integrity sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==
|
||||
|
||||
keycloak-js@^24.0.4:
|
||||
version "24.0.4"
|
||||
resolved "https://mirrors.cloud.tencent.com/npm/keycloak-js/-/keycloak-js-24.0.4.tgz#bfd78af853f0a45bb2f0b4dd24c135aef22cbab6"
|
||||
integrity sha512-eLjG7CzGGgAXh78M76QUJy1R8+QDQvIJXJf6T3bq9VJZ6RXBGZXMsXvII66b83ueYDFa1gi2JojmA31Z23HO0g==
|
||||
dependencies:
|
||||
js-sha256 "^0.11.0"
|
||||
jwt-decode "^4.0.0"
|
||||
|
||||
keyv@^4.5.3:
|
||||
version "4.5.4"
|
||||
resolved "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz"
|
||||
|
Loading…
Reference in New Issue
Block a user