This commit is contained in:
ivamp 2024-05-25 23:12:01 +08:00
parent 834928dbe9
commit e9e1e3e0ca
32 changed files with 202 additions and 3500 deletions

View File

@ -1,7 +0,0 @@
{
"$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json",
"spaces": 2,
"generator-cli": {
"version": "7.1.0"
}
}

View File

@ -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"
}
}
}

View File

@ -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",

View File

@ -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>

View File

@ -1,4 +0,0 @@
wwwroot/*.js
node_modules
typings
dist

View File

@ -1 +0,0 @@
# empty npmignore to ensure all required files (e.g., in the dist folder) are published by npm

View File

@ -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

View File

@ -1,8 +0,0 @@
.gitignore
.npmignore
api.ts
base.ts
common.ts
configuration.ts
git_push.sh
index.ts

View File

@ -1 +0,0 @@
7.1.0

File diff suppressed because it is too large Load Diff

View File

@ -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 = {
}

View File

@ -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);
};
}

View File

@ -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');
}
}

View File

@ -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'

View File

@ -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";

View File

@ -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
View 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
View 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
View 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
}

View File

@ -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 {

View File

@ -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()
}
},
})

View File

@ -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
View 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
}
}
})

View File

@ -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 };

View File

@ -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>

View File

@ -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) {
// PromisePromise
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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -4,7 +4,6 @@
"target": "esnext",
"useDefineForClassFields": true,
"allowSyntheticDefaultImports": true,
"allowImportingTsExtensions": true,
"composite": true,
"module": "esnext",
"moduleResolution": "node",

View File

@ -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"