diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 08c4429dba4e72d97d6ff75ea86ef48cb6bca582..871c81bd43bd6913caf00b3cf31a1e56546c56ee 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.16.0 +current_version = 0.17.0 commit = False tag = False diff --git a/.env b/.env index b23f359a1b7ecb140179431fee47ba64f435877b..4c5bd0f4495fdb63129c8a0e60c7bb2072415ac3 100644 --- a/.env +++ b/.env @@ -1,102 +1,104 @@ +IMAGE_PULL_POLICY=always IMAGE_REGISTRY=docker.io -IMAGE_POLL_POLICY=always # init +IMAGE_INIT_APPDATA=apitable/init-appdata:latest IMAGE_INIT_DB=apitable/init-db:latest -IMAGE_INIT_DATA_MYSQL=apitable/init-template-mysql:latest -IMAGE_INIT_DATA_MINIO=apitable/init-template-minio:latest # server -IMAGE_WEB_SERVER=apitable/web-server:latest IMAGE_BACKEND_SERVER=apitable/backend-server:latest +IMAGE_IMAGEPROXY_SERVER=apitable/imageproxy-server:latest IMAGE_ROOM_SERVER=apitable/room-server:latest IMAGE_SOCKET_SERVER=apitable/socket-server:latest -IMAGE_IMAGEPROXY_SERVER=apitable/imageproxy-server:latest +IMAGE_WEB_SERVER=apitable/web-server:latest -IMAGE_MINIO=minio/minio:RELEASE.2021-03-17T02-33-02Z -IMAGE_REDIS=redis:5 -IMAGE_RABBITMQ=rabbitmq:3-management -IMAGE_MYSQL=mysql:8.0.29 -IMAGE_GATEWAY=openresty/openresty:1.21.4.1-3-buster-fat +IMAGE_GATEWAY=openresty/openresty:1.21.4.1-4-buster-fat +IMAGE_MINIO=minio/minio:RELEASE.2023-01-25T00-19-54Z +IMAGE_MYSQL=mysql:8.0.32 +IMAGE_RABBITMQ=rabbitmq:3.11.7-management +IMAGE_REDIS=redis:7.0.8 ### SERVER BACKEND_BASE_URL=http://backend-server:8081/api/v1/ -SOCKET_GRPC_URL=socket-server:3007 NEST_GRPC_URL=room-server:3334 +SOCKET_GRPC_URL=socket-server:3007 ### NEST CONST -OSS_HOST=/assets -OSS_TYPE=QNY1 OSS_CACHE_TYPE=minio +OSS_HOST=http://minio:9000 +OSS_TYPE=QNY1 ### MINIO MINIO_ACCESS_KEY=apitable MINIO_SECRET_KEY=apitable@com -MINIO_ENDPOINT=http://minio:9000 -MINIO_BUCKET=assets ### MYSQL +DATABASE_TABLE_PREFIX=apitable_ +MYSQL_DATABASE=apitable MYSQL_HOST=mysql -MYSQL_PORT=3306 -MYSQL_USERNAME=root MYSQL_PASSWORD=apitable@com +MYSQL_PORT=3306 MYSQL_ROOT_PASSWORD=apitable@com -MYSQL_DATABASE=apitable -DATABASE_TABLE_PREFIX=apitable_ +MYSQL_USERNAME=root ### REDIS +REDIS_DB=0 REDIS_HOST=redis -REDIS_PORT=6379 REDIS_PASSWORD=apitable@com -REDIS_DB=0 +REDIS_PORT=6379 ### RabbitMQ RABBITMQ_HOST=rabbitmq +RABBITMQ_PASSWORD=apitable@com RABBITMQ_PORT=5672 -RABBITMQ_USERNAME=apitable-user -RABBITMQ_PASSWORD=fkJifev64y^sbxRl +RABBITMQ_USERNAME=apitable RABBITMQ_VHOST=/ -RABBITMQ_DEFAULT_USER=apitable-user -RABBITMQ_DEFAULT_PASS=fkJifev64y^sbxRl TIMEZONE=Asia/Singapore ENV=apitable -PUBLIC_URL="" -TEMPLATE_PATH="./static/web_build/index.html" -BACKEND_INFO_URL="http://backend-server:8081/api/v1/client/info" -API_PROXY="http://backend-server:8081" + +API_PROXY=http://backend-server:8081 +BACKEND_INFO_URL=http://backend-server:8081/api/v1/client/info +PUBLIC_URL= +TEMPLATE_PATH=./static/web_build/index.html USE_CUSTOM_PUBLIC_FILES=true NEST_GRPC_ADDRESS=static://room-server:3334 ### apitable const -SERVER_DOMAIN='' CALLBACK_DOMAIN= -CORS_ORIGINS='*' +CORS_ORIGINS=* +SERVER_DOMAIN= SOCKET_DOMAIN=http://socket-server:3001/socket TEMPLATE_SPACE=spcNTxlv8Drra ### apitable starter -SOCKET_URL=http://socket-server:3002 SOCKET_RECONNECTION_ATTEMPTS=10 SOCKET_RECONNECTION_DELAY=500 SOCKET_TIMEOUT=5000 +SOCKET_URL=http://socket-server:3002 SMS_ENABLED=false MAIL_ENABLED=false MAIL_HOST= -MAIL_USERNAME= MAIL_PASSWORD= +MAIL_PORT= +MAIL_SSL_ENABLE=true +MAIL_USERNAME= +OSS_CLIENT_TYPE=aws OSS_ENABLED=true -AWS_ACCESS_KEY="apitable" -AWS_ACCESS_SECRET="apitable@com" -AWS_ENDPOINT="http://minio:9000" -AWS_REGION="us-east-1" -VK_ASSETS_LTD_URL=assets +AWS_ACCESS_KEY=apitable +AWS_ACCESS_SECRET=apitable@com +AWS_ENDPOINT=http://minio:9000 +AWS_REGION=us-east-1 + +OSS_BUCKET_NAME=assets +VK_ASSETS_LTD_BUCKET=assets +VK_ASSETS_LTD_URL=http://minio:9000 DATA_PATH=. diff --git a/.env.devenv b/.env.devenv new file mode 100644 index 0000000000000000000000000000000000000000..7807bb3f1d5ffce2dad887b380149790d8d6c4eb --- /dev/null +++ b/.env.devenv @@ -0,0 +1,18 @@ +AWS_ENDPOINT=http://127.0.0.1:9000 +MYSQL_HOST=127.0.0.1 +RABBITMQ_HOST=127.0.0.1 +REDIS_HOST=127.0.0.1 + +BACKEND_BASE_URL=http://127.0.0.1:8081/api/v1/ +BACKEND_INFO_URL=http://127.0.0.1:8081/api/v1/client/info +NEST_GRPC_ADDRESS=static://127.0.0.1:3334 +NEST_GRPC_URL=127.0.0.1:3334 +SOCKET_DOMAIN=http://127.0.0.1:3001/socket +SOCKET_GRPC_URL=127.0.0.1:3007 +SOCKET_URL=http://127.0.0.1:3002 + +API_PROXY= + +QNY1=http://127.0.0.1:9000/assets/ +QNY2=http://127.0.0.1:9000/assets/ +QNY3=http://127.0.0.1:9000/assets/ diff --git a/.eslintignore b/.eslintignore index 6148aecf14c90d3dad749a1329a9dbec0bbea654..200604987486deac397fc3487b2c3013c6870df6 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,4 @@ packages/socket-server/src/grpc/generated/* packages/room-server/src/grpc/generated/* packages/datasheet/src/static/lang/* +packages/core/src/config/*.interface.ts \ No newline at end of file diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000000000000000000000000000000000000..997825ecb09a3fa260a5d2ab0c3f15b7e22dbe13 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,13 @@ +# These are supported funding model platforms + +github: [ apitable ] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea7824f11be1eeda22377549cbc1aec7f980..a863627c2000a97cbeae9a2a7bf896ed2e1e73be 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,8 +1,8 @@ --- name: Bug report -about: Create a report to help us improve -title: '' -labels: '' +about: Create a report to help APITable improve +title: 'fix: BUG...' +labels: 'bug' assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000000000000000000000000000000000..79c2cc0b95d0dfab86853bc19c9e8978867c529d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: true +contact_links: + - name: Discord Community + url: https://discord.gg/TwNb9nfdBU + about: Discuss ideas + - name: Help Center + url: https://help.apitable.com + about: Help Center of APITable, includes tutorial, manual, FAQ. diff --git a/.github/ISSUE_TEMPLATE/docs.md b/.github/ISSUE_TEMPLATE/docs.md new file mode 100644 index 0000000000000000000000000000000000000000..63f5481f93e2417d5a4210398a98940fa2cc0961 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/docs.md @@ -0,0 +1,10 @@ +--- +name: Documentation Requirement +about: Create a documentation requirement for us. +title: 'docs: DOCS...' +labels: 'documentation' +assignees: '' + +--- + +## What documentation do you want? \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index aebc2357cfd9cf85d49e418e68c2a93b526602f2..56f9371bf4822c95baed43d86ba8326a000bbd05 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,28 +1,28 @@ --- name: Feature request -about: Suggest an idea for this project -title: '' -labels: '' +about: Suggest an idea for APITable +title: 'feat: FEATURE...' +labels: 'enhancement' assignees: '' --- I have an idea for this project. -# Why? +## Why? -# What? +## What? -# How? +## How? @@ -18,6 +13,14 @@ TypeScript Language, NestJS Framework Java Language, Spring Framework + + + + + + + + @@ -35,16 +38,44 @@ + + + + +

+ +

+ English + | + Français + | + Español + | + Deutsch + | + 简体中文 + | + 繁體中文 + | + 日本語

## ✨ Quick Start +> APITable is currently a `Work In Progress`. +> +> We will publish the first release in late February 2023. +> +> Join [Discord](https://discord.gg/TwNb9nfdBU) or [Twitter](https://twitter.com/apitable_com) to keep in touch. + If you just want to try out APITable[^info], click here for [⚡️Gitpod Online Demo](https://gitpod.io/#https://github.com/apitable/apitable). If you want to install APITable in your local or cloud computing environment, see [💾 Installation](#installation) +If you want to set up your local development environment, read our [🧑‍💻 Developer Guide](./docs/contribute/developer-guide.md) + ## 🔥 Features @@ -56,9 +87,9 @@ If you want to install APITable in your local or cloud computing environment, se - - - + + + - - + + - + - + - - -
Automatic Form
@@ -79,8 +110,8 @@ If you want to install APITable in your local or cloud computing environment, se Unlimited cross-table links
@@ -93,7 +124,7 @@ If you want to install APITable in your local or cloud computing environment, se
Powerful Rows/Columns Permissions @@ -101,7 +132,7 @@ If you want to install APITable in your local or cloud computing environment, se Embed
@@ -115,56 +146,53 @@ If you want to install APITable in your local or cloud computing environment, se
- + APITable provides a range of amazing features, from the personal to the enterprise. - Advanced technology stack and open-source - - `Realtime collaboration` allows multiple users to edit together in real time, or simultaneously with the `Operational Transformation (OT)` Algorithm. - - Extremely smooth, user-friendly, super-fast database-spreadsheet interface in ` Rendering Engine`. - - Database native architecture: Changeset / Operation / Action / Snapshot and so on. - - **100k+** data rows with real-time collaboration. - - Full-stack API access, from `Data` to `Metadata`. - - One-direction / Bi-direction Table Link and `Infinite Cross Links` - - Community-friendly programming languages and framework, TypeScript ([NextJS](https://nextjs.org/) + [NestJS](https://nestjs.com/)) and Java ([Spring Boot](https://spring.io/projects/spring-boot)) + - `Realtime collaboration` allows multiple users to edit together in real time, or simultaneously with the `Operational Transformation (OT)` Algorithm. + - Extremely smooth, user-friendly, super-fast database-spreadsheet interface in ` Rendering Engine`. + - Database native architecture: Changeset / Operation / Action / Snapshot and so on. + - **100k+** data rows with real-time collaboration. + - Full-stack API access, from `Data` to `Metadata`. + - One-direction / Bi-direction Table Link and `Infinite Cross Links` + - Community-friendly programming languages and framework, TypeScript ([NextJS](https://nextjs.org/) + [NestJS](https://nestjs.com/)) and Java ([Spring Boot](https://spring.io/projects/spring-boot)) - Beautiful and Rich Database-Spreadsheet UI - - `CRUD`: Create, Read, Update, Delete the Tables, Columns, and Rows - - `Fields Operations`: sort, filter, grouping, hide/unhide, height setting. - - `Space based`: Use separated workspaces in place of App/Base-based structure, make unlimited tables link together possible. - - `Dark mode` and theme customization available. - - `7 View Types`: Grid View (Datasheet) / Gallery View / Mindmap View / Kanban View / Full-Feature Gantt View / Calendar View - - One-click API Panel + - `CRUD`: Create, Read, Update, Delete the Tables, Columns, and Rows + - `Fields Operations`: sort, filter, grouping, hide/unhide, height setting. + - `Space based`: Use separated workspaces in place of App/Base-based structure, make unlimited tables link together possible. + - `Dark mode` and theme customization available. + - `7 View Types`: Grid View (Datasheet) / Gallery View / Mindmap View / Kanban View / Full-Feature Gantt View / Calendar View + - One-click API Panel - Batteries included - - Built-in 10+ official templates. - - Robot Automation and customization available. - - BI dashboard - - One-click auto-generated form - - Shareable and embeddable page. - - Multi-language support. - - Integration with n8n.io / Zapier / Appsmith... and more. + - Built-in 10+ official templates. + - Robot Automation and customization available. + - BI dashboard + - One-click auto-generated form + - Shareable and embeddable page. + - Multi-language support. + - Integration with n8n.io / Zapier / Appsmith... and more. - Excellent extensibility - - Extensible `Widget System` with over 20 officials open-source widgets. - - Customizable Graph & Chart & Dashboard - - Customizable Data Column Types - - Customizable Formulas - - Customizable Automation Robot Actions. + - Extensible `Widget System` with over 20 officials open-source widgets. + - Customizable Graph & Chart & Dashboard + - Customizable Data Column Types + - Customizable Formulas + - Customizable Automation Robot Actions. - Enterprise-grade permissions - - `Mirror`, turn a View into a mirror to implement Row Permission. - - Activate `Column Permission` through a very simple operation. - - Folders / Sub-Folders / Files Permission. - - Tree structure folders and customizable node (file); - - Team Management & Organization Structure. + - `Mirror`, turn a View into a mirror to implement Row Permission. + - Activate `Column Permission` through a very simple operation. + - Folders / Sub-Folders / Files Permission. + - Tree structure folders and customizable node (file); + - Team Management & Organization Structure. - Enterprise features: - - SAML - - Single-Sign-On (SSO) - - Audit - - Database Auto Backup - - Data Exporter - - Watermark + - SAML + - Single-Sign-On (SSO) + - Audit + - Database Auto Backup + - Data Exporter + - Watermark - .... With extensible widgets and plugins, you can add more features. @@ -174,21 +202,21 @@ With extensible widgets and plugins, you can add more features. Why you must know APITable for your next software? - As super management software - - Flexible Project Management & Tasks / Issues Management. - - Marketing Lead Management. - - Most flexible and connectable CRM. - - Flexible Business Intelligence (BI). - - People-Friendly Forms and Surveys - - Flexible ERP. - - Low-code and no-code platform. - - ...and more, APITable puts 1000 softwares in your pocket. + - Flexible Project Management & Tasks / Issues Management. + - Marketing Lead Management. + - Most flexible and connectable CRM. + - Flexible Business Intelligence (BI). + - People-Friendly Forms and Surveys + - Flexible ERP. + - Low-code and no-code platform. + - ...and more, APITable puts 1000 softwares in your pocket. - As a visual database infrastructure - - **Embed** APITable into your own software UIs. - - Visual Database with REST API. - - Admin dashboard. - - Central configuration management. - - All-in-one enterprise database that **connect all** your software. - - ...and more, APITable connects everything. + - **Embed** APITable into your own software UIs. + - Visual Database with REST API. + - Admin dashboard. + - Central configuration management. + - All-in-one enterprise database that **connect all** your software. + - ...and more, APITable connects everything. - Also, it is open source and extensible ## 💞 API-oriented @@ -204,6 +232,7 @@ APITable will provides a Datasheet Query Language (DQL) to query your database-s ## 💝 Embed-friendly #### Share and Embed + Share your datasheet table or folder. Embed them by copying and pasting HTML scripts. @@ -211,10 +240,6 @@ Embed them by copying and pasting HTML scripts. [APITable.com](https://apitable.com) provides more Enterprise-ready Embedding features for securities. - - - - ## Installation Make sure you have `docker` & `curl` installed locally. @@ -227,14 +252,13 @@ curl https://apitable.github.io/install.sh | bash Then open [https://localhost:80](https://localhost:80) in your browser to visit it. (default username `admin@apitable.com` and password `Apitable2022`) -If you want to set up your local development environment, read our [Developer Guide](./docs/contribute/developer-guide.md) - +If you want to set up your local development environment, read our [🧑‍💻 Developer Guide](./docs/contribute/developer-guide.md) ## 🧑‍💻 Contributing Welcome, and thank you for your interest in contributing to APITable! -There are many ways in which you can contribute, beyond writing code. +There are many ways in which you can contribute, beyond writing code. You can read this repository’s [Contributing Guidelines](./CONTRIBUTING.md) to learn how to contribute. @@ -255,7 +279,6 @@ Here's a general APITable git workflow: 5. Publish the branch (`git push origin my-new-feature`) 6. Create a new Pull Request -> [Create pull request across forks](https://github.com/apitable/apitable/compare) - ### Work conventions APITable use these common conventions: @@ -265,20 +288,28 @@ APITable use these common conventions: - How to write good commit message? [Conventional Commits](https://www.conventionalcommits.org/) - What's our changelog format? [Keep Changelog](https://keepachangelog.com/en/1.0.0/) - How to versioning and tagging? [Semantic Versioning](https://semver.org/) -- What is the Java Coding Guideline? [Java Coding Guideline](https://github.com/alibaba/Alibaba-Java-Coding-Guidelines) | [Intellij IDEA Plugin](https://plugins.jetbrains.com/plugin/10046-alibaba-java-coding-guidelines) +- What is the Java Coding Guideline? [Java Coding Guideline](https://google.github.io/styleguide/javaguide.html) | [Intellij IDEA Plugin](https://plugins.jetbrains.com/plugin/8527) - What is the TypeScript Coding Guideline? -> [TypeScript Style Guide](https://google.github.io/styleguide/tsguide.html) | [ESLint](https://www.npmjs.com/package/@typescript-eslint/eslint-plugin) +### Documentations + +- [Help Center](https://help.apitable.com/) +- [👩‍💻 Developer Center](https://developers.apitable.com/) + - [🪡 REST API Docs](https://developers.apitable.com/api/introduction/) + - Widget SDK (Coming soon...) + - Scripting (Coming soon...) + ## 🛣 Roadmap ### Future Features + - Heavy-code Interface Builder -- Embbedable 3rd party documentation components +- Embbedable 3rd party documentation components - SQL-like Domain-Specific Languages - As an IdP - Web 3 features - ... - ### Hosted and Enterprise versions offer advanced features - As an IdP; @@ -288,14 +319,25 @@ APITable use these common conventions: - Database Backup - Watermark -For more information, please contact us at . +For more information, please contact us at . -## 🌏 Why we create APITable and open-source? +## 👫 Get Involved + +### 🌏 Why we create APITable and open-source? - We believe that `Database is the cornerstone` of all the software. - We believe that making a `Visual Database with rich and easy user interface for everyone` can reduce the difficulty of software industry and increase the world's digitalization adoption. - We believe that open-sourcing `APITable` work can `Push Human Beings Forward`. +### We are hiring remotely! + +We always search for good talents for APITable: + +- **Full-stack developer**: You have experience with React, NestJS, TypeScript, Spring Boot, Java, Terraform. And you like to write high quality code with clear documentation and unit tests. +- **Back-end developer**: You have experience with NestJS, TypeScript, Spring Boot, Java, SQL, Kubernetes, Terraform. And you like to write high quality code with clear documentation and unit tests. +- **Front-end developer**: You have experience with React, NextJS, TypeScript, WebPack. And you like to write high quality code with clear documentation and unit tests. + +Regardless of time and conditions, if you want to get involved to the team of APITable, do not hesitate and send your CV to . ## 📺 Screenshot @@ -330,18 +372,16 @@ For more information, please contact us at . APITable Screenshot Image

- ## 🥰 License > This repository contains the source code for the Open Source edition of APITable, released under the AGPL. -> +> > If you'd like to run your own copy of APITable or contribute to development then this is the place for you. > -> See [LICENCING](./LICENCING.md) for details. +> See [LICENSING](./LICENSING.md) for details. > > If you want to use APITable online then you don't need to run this code, we offer a hosted version of the app at [APITable.com](https://apitable.com) which optimized for global accelerator. -
[^info]: Licensed with AGPL-3.0. Designed by [APITable Ltd](https://apitable.com). diff --git a/backend-server/.version b/backend-server/.version index d183d4ace05b92877cec0b45daa9e26b09182d7d..07feb8234926c75235d2687e1f26614d2baf0c6c 100644 --- a/backend-server/.version +++ b/backend-server/.version @@ -1 +1 @@ -0.16.0 \ No newline at end of file +0.17.0 \ No newline at end of file diff --git a/backend-server/application/build.gradle b/backend-server/application/build.gradle index ecda5ed0c2941a7e283cb8bcb096feda8d86f37f..6045f35aa78e9e59c023b7b1b79599003cc7f895 100644 --- a/backend-server/application/build.gradle +++ b/backend-server/application/build.gradle @@ -1,6 +1,5 @@ import java.nio.charset.StandardCharsets import java.time.LocalDateTime -import java.time.format.DateTimeFormatter plugins { id 'org.springframework.boot' @@ -11,16 +10,16 @@ plugins { dependencies { implementation project(':shared:core') - + implementation project(':shared:starters:sms') implementation project(':shared:starters:mail') implementation project(':shared:starters:amqp') implementation project(':shared:starters:oss') implementation project(':shared:starters:socketio') implementation project(':shared:starters:beetl') - + annotationProcessor rootProject.ext.dependencies['configuration-processor'] - + implementation rootProject.ext.dependencies['spring-web-starter'] implementation rootProject.ext.dependencies['spring-thymeleaf'] implementation rootProject.ext.dependencies['spring-mail-starter'] @@ -51,34 +50,37 @@ dependencies { implementation rootProject.ext.dependencies['grpc-server-spring-boot'] implementation rootProject.ext.dependencies['pdf'] implementation rootProject.ext.dependencies['sentry'] - implementation rootProject.ext.dependencies['spring-cloud-starter-sleuth'] - implementation rootProject.ext.dependencies['spring-cloud-sleuth-zipkin'] + implementation (rootProject.ext.dependencies['spring-cloud-starter-sleuth']){ + exclude group: 'org.springframework.cloud', module: 'spring-cloud-sleuth-brave' + } + implementation rootProject.ext.dependencies['spring-cloud-sleuth-otel'] + implementation rootProject.ext.dependencies['opentelemetry-exporter-jaeger'] implementation rootProject.ext.dependencies['okhttp3'] implementation rootProject.ext.dependencies['jsoup'] implementation rootProject.ext.dependencies['protobuf-java'] implementation rootProject.ext.dependencies['io-grpc'] - + runtimeOnly rootProject.ext.dependencies['mysql'] - + annotationProcessor rootProject.ext.dependencies['lombok'] compileOnly rootProject.ext.dependencies['lombok'] - + testCompileOnly rootProject.ext.dependencies["lombok"] testAnnotationProcessor rootProject.ext.dependencies["lombok"] - + testImplementation rootProject.ext.dependencies['spring-test-starter'] testImplementation rootProject.ext.dependencies['mybatis-plus-starter-test'] testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' testImplementation("org.json:json:${json_java_version}") - + // for clock module testImplementation rootProject.ext.dependencies['assertj'] testImplementation rootProject.ext.dependencies['awaitility'] - + // for enterprise implementation rootProject.ext.dependencies['spring-plugin-core'] implementation rootProject.ext.dependencies['spring-plugin-metadata'] - + // for v-ee implementation(rootProject.ext.dependencies['social-dingtalk']) implementation(rootProject.ext.dependencies['social-feishu']) @@ -94,7 +96,7 @@ dependencies { implementation(rootProject.ext.dependencies['wx-open']) implementation(rootProject.ext.dependencies['v-client']) implementation(rootProject.ext.dependencies['sensors']) - + // for apitable-ee implementation(rootProject.ext.dependencies['auth0']) } @@ -123,7 +125,7 @@ bootJar { attributes 'Build-OS': System.getProperty("os.name") attributes 'Built-By': System.getProperty("user.name") attributes 'Build-Jdk': System.getProperty("java.version") - attributes 'Build-Timestamp': LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + attributes 'Build-Timestamp': LocalDateTime.now().toString() } } @@ -143,22 +145,22 @@ springBoot { test { // useJUnitPlatform() - + // listen to events in the test execution lifecycle // beforeTest { descriptor -> // logger.lifecycle("Running test: {}", descriptor) // } - + // doFirst { // forkEvery = 5 // minHeapSize = "256m" // maxHeapSize = "2048m" // jvmArgs "-XX:MetaspaceSize=64m", "-XX:MaxMetaspaceSize=256m" // } - + // Stops test execution after the first failed test. failFast = true - + // listen to standard out and standard error of the test JVM(s) // onOutput { descriptor, event -> // if (event.destination == TestOutputEvent.Destination.StdErr) { @@ -180,7 +182,7 @@ protobuf { artifact = "io.grpc:protoc-gen-grpc-java:${io_grpc_version}" } } - + generateProtoTasks { all()*.plugins { grpc {} diff --git a/backend-server/application/src/main/java/com/apitable/asset/controller/AssetCallbackController.java b/backend-server/application/src/main/java/com/apitable/asset/controller/AssetCallbackController.java index fd0354928c29b3eec36cb690dd860663c46ea213..1dde305c2dddc491b36ce3bf1b9f44d84241f821 100644 --- a/backend-server/application/src/main/java/com/apitable/asset/controller/AssetCallbackController.java +++ b/backend-server/application/src/main/java/com/apitable/asset/controller/AssetCallbackController.java @@ -19,80 +19,64 @@ package com.apitable.asset.controller; import java.util.List; -import java.util.Optional; import javax.annotation.Resource; -import cn.hutool.core.util.StrUtil; -import cn.hutool.json.JSONUtil; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; - -import com.apitable.core.support.ResponseData; -import com.apitable.starter.oss.autoconfigure.OssProperties; -import com.apitable.starter.oss.autoconfigure.OssProperties.Callback; -import com.apitable.starter.oss.autoconfigure.OssProperties.Qiniu; -import com.apitable.starter.oss.core.qiniu.QiniuTemporaryClientTemplate; import com.apitable.asset.enums.AssetType; -import com.apitable.asset.ro.AssetQiniuUploadCallbackBody; import com.apitable.asset.ro.AssetUploadNotifyRO; import com.apitable.asset.service.IAssetCallbackService; import com.apitable.asset.vo.AssetUploadResult; import com.apitable.base.model.WidgetUploadNotifyRO; +import com.apitable.core.support.ResponseData; import com.apitable.shared.component.scanner.annotation.ApiResource; import com.apitable.shared.component.scanner.annotation.PostResource; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RestController; -import static com.apitable.core.constants.ResponseExceptionConstants.DEFAULT_ERROR_CODE; - /** - * Attachment callback interface + * Attachment callback interface. + * + * @author Chambers */ @RestController @Api(tags = "Basic module - accessory callback interface") @ApiResource(path = "/asset") public class AssetCallbackController { + /** */ @Resource private IAssetCallbackService iAssetCallbackService; - @Autowired(required = false) - private QiniuTemporaryClientTemplate qiniuTemporaryClientTemplate; - - @Autowired(required = false) - private OssProperties ossProperties; - - @Deprecated - @PostResource(name = "Qiniu cloud upload callback", path = "/qiniu/uploadCallback", requiredLogin = false, requiredPermission = false) - @ApiOperation(value = "Qiniu cloud upload callback", hidden = true) - public ResponseData qiniuCallback(@RequestHeader("Authorization") String authorization, @RequestBody String body) { - Qiniu qiniu = ossProperties.getQiniu(); - if (null == ossProperties.getQiniu()) { - return ResponseData.status(false, DEFAULT_ERROR_CODE, "not enabled qiniu callback").data(null); - } - Callback callback = Optional.ofNullable(qiniu.getCallback()).orElseGet(Callback::new); - boolean validCallback = qiniuTemporaryClientTemplate.isValidCallback(authorization, callback.getUrl(), StrUtil.bytes(body), callback.getBodyType()); - if (!validCallback) { - return ResponseData.status(false, DEFAULT_ERROR_CODE, "invalid content").data(null); - } - - AssetQiniuUploadCallbackBody callbackBody = JSONUtil.toBean(body, AssetQiniuUploadCallbackBody.class); - return ResponseData.success(iAssetCallbackService.qiniuCallback(callbackBody)); - } - - @PostResource(name = "Resource upload completion notification callback", path = "/upload/callback", requiredLogin = false) - @ApiOperation(value = "Resource upload completion notification callback", notes = "After S3 completes the client upload, it actively reaches the notification server") - public ResponseData> notifyCallback(@RequestBody AssetUploadNotifyRO body) { - return ResponseData.success(iAssetCallbackService.loadAssetUploadResult(AssetType.of(body.getType()), body.getResourceKeys())); + /** + * * + * @param body AssetUploadNotifyRO + * @return ResponseData> + */ + @PostResource(name = "Resource upload completion notification callback", + path = "/upload/callback", requiredLogin = false) + @ApiOperation(value = "Resource upload completion notification callback", + notes = "After S3 completes the client upload, " + + "it actively reaches the notification server") + public ResponseData> notifyCallback( + @RequestBody final AssetUploadNotifyRO body) { + return ResponseData.success( + iAssetCallbackService.loadAssetUploadResult( + AssetType.of(body.getType()), body.getResourceKeys())); } - @PostResource(name = "widget upload callback", path = "/widget/uploadCallback", requiredLogin = false) + /** + * * + * @param body WidgetUploadNotifyRO + * @return ResponseData + */ + @PostResource(name = "widget upload callback", + path = "/widget/uploadCallback", requiredLogin = false) @ApiOperation(value = "widget upload callback") - public ResponseData widgetCallback(@RequestBody WidgetUploadNotifyRO body) { + public ResponseData widgetCallback( + @RequestBody final WidgetUploadNotifyRO body) { iAssetCallbackService.widgetCallback(body.getResourceKeys()); return ResponseData.success(); } diff --git a/backend-server/application/src/main/java/com/apitable/shared/constants/TimeZoneConstants.java b/backend-server/application/src/main/java/com/apitable/asset/controller/package-info.java similarity index 77% rename from backend-server/application/src/main/java/com/apitable/shared/constants/TimeZoneConstants.java rename to backend-server/application/src/main/java/com/apitable/asset/controller/package-info.java index 15dd4a7423621203c6be3bb269eff4a7a8cacefd..4856973855f7563970c05bbb8ebcb6d08d4b8fca 100644 --- a/backend-server/application/src/main/java/com/apitable/shared/constants/TimeZoneConstants.java +++ b/backend-server/application/src/main/java/com/apitable/asset/controller/package-info.java @@ -16,15 +16,8 @@ * along with this program. If not, see . */ -package com.apitable.shared.constants; - -import java.time.ZoneOffset; - /** - * time zone constants - * @author Shawn Deng + * + * @author Chambers */ -public class TimeZoneConstants { - - public static final ZoneOffset DEFAULT_TIME_ZONE = ZoneOffset.ofHours(8); -} +package com.apitable.asset.controller; diff --git a/backend-server/application/src/main/java/com/apitable/asset/ro/AssetQiniuUploadCallbackBody.java b/backend-server/application/src/main/java/com/apitable/asset/ro/AssetUploadCallbackBody.java similarity index 66% rename from backend-server/application/src/main/java/com/apitable/asset/ro/AssetQiniuUploadCallbackBody.java rename to backend-server/application/src/main/java/com/apitable/asset/ro/AssetUploadCallbackBody.java index 7c5ffe6cf88147deedc85cb73f0e453985718774..e500fb4386b6ffa20aa19ec03f41a9803731c38c 100644 --- a/backend-server/application/src/main/java/com/apitable/asset/ro/AssetQiniuUploadCallbackBody.java +++ b/backend-server/application/src/main/java/com/apitable/asset/ro/AssetUploadCallbackBody.java @@ -23,16 +23,13 @@ import lombok.Data; /** - *

- * Qiniu Cloud upload callback Body - * Reference documentation:https://developer.qiniu.com/kodo/1235/vars#magicvar-fname - *

+ * Asset upload callback Body. * * @author Pengap */ @Data -@ApiModel("Qiniu Cloud upload callback Body") -public class AssetQiniuUploadCallbackBody { +@ApiModel("Asset upload callback Body") +public class AssetUploadCallbackBody { /** * Get the resource name of the file saved in the space. @@ -40,17 +37,19 @@ public class AssetQiniuUploadCallbackBody { private String key; /** - * The HTTPETag after the file is uploaded successfully. If the resource ID is not specified when uploading, the Etag will be used as the resource ID. + * The HTTPETag after the file is uploaded successfully. + * * If the resource ID is not specified when uploading, + * * the Etag will be used as the resource ID. */ private String hash; /** - * Get the upload target space name。 + * Get the upload target space name. */ private String bucket; /** - * Uploaded original filename。 + * Uploaded original filename. */ private String fname; @@ -59,45 +58,34 @@ public class AssetQiniuUploadCallbackBody { */ private Long fsize; - /** - * The suffix of the uploaded resource - */ - private String ext; - /** * Resource type, for example, the resource type of a JPG image is imagejpg. */ private String mimeType; /** - * The suffix of the uploaded resource - */ - private String suffix; - - /** - * the width of the image + * the width of the image. */ private Integer imageWidth; /** - * the height of the picture + * the height of the picture. */ private Integer imageHeight; + /** */ private Long uploadAssetId; - private Long uploadDeveloperAssetId; - - private Integer uploadSource; - - private Long uploadUserId; - + /** */ private String spaceId; + /** */ private String nodeId; + /** */ private String bucketType; + /** */ private Integer assetType; } diff --git a/backend-server/application/src/main/java/com/apitable/asset/ro/package-info.java b/backend-server/application/src/main/java/com/apitable/asset/ro/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..255db8fcffa04511881a9edee9eef225f5916a26 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/asset/ro/package-info.java @@ -0,0 +1,23 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/** + * + * @author Chambers + */ +package com.apitable.asset.ro; diff --git a/backend-server/application/src/main/java/com/apitable/asset/service/IAssetCallbackService.java b/backend-server/application/src/main/java/com/apitable/asset/service/IAssetCallbackService.java index 4d39901ab4bce7cb4a58f46e4149cb8c4c3a64ae..a34562168aba54c7b411c5af59a2d0669186b24f 100644 --- a/backend-server/application/src/main/java/com/apitable/asset/service/IAssetCallbackService.java +++ b/backend-server/application/src/main/java/com/apitable/asset/service/IAssetCallbackService.java @@ -21,43 +21,28 @@ package com.apitable.asset.service; import java.util.List; import com.apitable.asset.enums.AssetType; -import com.apitable.asset.ro.AssetQiniuUploadCallbackBody; import com.apitable.asset.vo.AssetUploadResult; /** - *

- * Asset Upload Callback Service - *

+ * Asset Upload Callback Service. * * @author Pengap - * @date 2022/4/6 16:44:50 */ public interface IAssetCallbackService { /** - * qiniu cloud upload callback - * - * @param body callback body - * @return AssetUploadResult - * @author Pengap - * @date 2022/4/7 15:02:24 - */ - @Deprecated - AssetUploadResult qiniuCallback(AssetQiniuUploadCallbackBody body); - - /** - * asset callback notify after complete upload + * asset callback notify after complete upload. * * @param assetType assert type * @param resourceKeys resource key list * @return AssetUploadResults * @author Chambers - * @date 2022/8/8 */ - List loadAssetUploadResult(AssetType assetType, List resourceKeys); + List loadAssetUploadResult(AssetType assetType, + List resourceKeys); /** - * widget upload callback + * widget upload callback. * * @param resourceKeys file urls */ diff --git a/backend-server/application/src/main/java/com/apitable/asset/service/impl/AssetCallbackServiceImpl.java b/backend-server/application/src/main/java/com/apitable/asset/service/impl/AssetCallbackServiceImpl.java index 9ec4509943889b1a30b3d23c2b73d82112c53f17..ece48b3de87790c76ec88ba5ef1ae38360c27545 100644 --- a/backend-server/application/src/main/java/com/apitable/asset/service/impl/AssetCallbackServiceImpl.java +++ b/backend-server/application/src/main/java/com/apitable/asset/service/impl/AssetCallbackServiceImpl.java @@ -35,23 +35,21 @@ import javax.imageio.ImageIO; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; -import com.apitable.starter.oss.core.OssClientTemplate; -import com.apitable.starter.oss.core.OssStatObject; -import com.baomidou.mybatisplus.extension.toolkit.SqlHelper; -import lombok.extern.slf4j.Slf4j; - import com.apitable.asset.entity.AssetEntity; import com.apitable.asset.enums.AssetType; -import com.apitable.asset.enums.AssetUploadSource; import com.apitable.asset.mapper.AssetMapper; -import com.apitable.asset.mapper.DeveloperAssetMapper; -import com.apitable.asset.ro.AssetQiniuUploadCallbackBody; +import com.apitable.asset.ro.AssetUploadCallbackBody; import com.apitable.asset.service.IAssetAuditService; import com.apitable.asset.service.IAssetCallbackService; import com.apitable.asset.vo.AssetUploadResult; import com.apitable.base.enums.ActionException; import com.apitable.base.enums.DatabaseException; import com.apitable.base.enums.ParameterException; +import com.apitable.core.exception.BusinessException; +import com.apitable.core.util.DigestUtil; +import com.apitable.core.util.ExceptionUtil; +import com.apitable.core.util.InputStreamCache; +import com.apitable.core.util.MimeTypeMapping; import com.apitable.shared.cache.service.AssetCacheService; import com.apitable.shared.config.properties.ConstProperties; import com.apitable.shared.util.PdfToImageUtil; @@ -59,11 +57,10 @@ import com.apitable.shared.util.StringUtil; import com.apitable.space.dto.SpaceAssetDTO; import com.apitable.space.mapper.SpaceAssetMapper; import com.apitable.space.service.ISpaceAssetService; -import com.apitable.core.exception.BusinessException; -import com.apitable.core.util.DigestUtil; -import com.apitable.core.util.ExceptionUtil; -import com.apitable.core.util.InputStreamCache; -import com.apitable.core.util.MimeTypeMapping; +import com.apitable.starter.oss.core.OssClientTemplate; +import com.apitable.starter.oss.core.OssStatObject; +import com.baomidou.mybatisplus.extension.toolkit.SqlHelper; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; @@ -76,89 +73,91 @@ import static com.apitable.shared.constants.AssetsPublicConstants.SPACE_PREFIX; import static com.apitable.shared.constants.WidgetAssetConstants.TOKEN_MAX; /** - *

- * Asset Upload Callback Service Implement Class - *

+ * Asset Upload Callback Service Implement Class. * * @author Pengap - * @date 2022/4/6 16:46:25 */ @Slf4j @Service public class AssetCallbackServiceImpl implements IAssetCallbackService { + /** */ @Resource private AssetMapper assetMapper; - @Resource - private DeveloperAssetMapper developerAssetMapper; - + /** */ @Resource private IAssetAuditService iAssetAuditService; + /** */ @Resource private ISpaceAssetService iSpaceAssetService; + /** */ @Resource private SpaceAssetMapper spaceAssetMapper; + /** */ @Autowired(required = false) private OssClientTemplate ossTemplate; + /** */ @Resource private RedisLockRegistry redisLockRegistry; + /** */ @Resource private AssetCacheService assetCacheService; + /** */ @Resource private ConstProperties constProperties; - @Deprecated + /** + * * + * @param assetType assert type + * @param resourceKeys resource key list + * @return List + */ @Override @Transactional(rollbackFor = Exception.class) - public AssetUploadResult qiniuCallback(AssetQiniuUploadCallbackBody body) { - AssetUploadSource uploadSource = AssetUploadSource.of(body.getUploadSource()); - switch (uploadSource) { - case WIDGET_STATIC: - this.completeWidgetStaticUpload(body); - break; - case SPACE_ASSET: - return this.completeSpaceAssetUpload(body); - } - return null; - } - - @Override - @Transactional(rollbackFor = Exception.class) - public List loadAssetUploadResult(AssetType assetType, List resourceKeys) { + public List loadAssetUploadResult( + final AssetType assetType, final List resourceKeys) { // No space resources, only the file access path is used if (!AssetType.isSpaceAsset(assetType)) { - return resourceKeys.stream().map(AssetUploadResult::new).collect(Collectors.toList()); + return resourceKeys.stream() + .map(AssetUploadResult::new).collect(Collectors.toList()); } // Get attachments that already exist at file_url in db - List assetEntities = assetMapper.selectByFileUrl(resourceKeys); + List assetEntities = + assetMapper.selectByFileUrl(resourceKeys); if (assetEntities.size() != resourceKeys.size()) { throw new BusinessException("resource don't exist"); } List results = new ArrayList<>(resourceKeys.size()); for (AssetEntity asset : assetEntities) { // Determine whether md5 already exists. - // If it exists, it means that the resource record information in the database is complete, and return it directly; - // if it does not exist, request to load OSS and complete the resource record information. + // If it exists, it means that the resource record information + // in the database is complete, and return it directly; + // If it does not exist, request to load OSS + // and complete the resource record information. if (asset.getChecksum() != null) { - AssetUploadResult result = BeanUtil.copyProperties(asset, AssetUploadResult.class); + AssetUploadResult result = + BeanUtil.copyProperties(asset, AssetUploadResult.class); result.setToken(asset.getFileUrl()); result.setSize(asset.getFileSize().longValue()); results.add(result); continue; } // Get file attributes - OssStatObject statObject = ossTemplate.getStatObject(asset.getBucketName(), asset.getFileUrl()); + OssStatObject statObject = ossTemplate.getStatObject( + asset.getBucketName(), asset.getFileUrl()); // Check for restricted file types - this.checkFileType(statObject.getMimeType(), asset.getId(), asset.getBucketName(), asset.getFileUrl()); + this.checkFileType(statObject.getMimeType(), asset.getId(), + asset.getBucketName(), asset.getFileUrl()); // Build callback information - AssetQiniuUploadCallbackBody body = new AssetQiniuUploadCallbackBody(); + AssetUploadCallbackBody body = + new AssetUploadCallbackBody(); body.setUploadAssetId(asset.getId()); body.setBucket(asset.getBucketName()); body.setBucketType(asset.getBucket()); @@ -169,7 +168,8 @@ public class AssetCallbackServiceImpl implements IAssetCallbackService { body.setAssetType(assetType.getValue()); if (assetType != AssetType.DATASHEET) { // Load cached data - com.apitable.shared.cache.bean.SpaceAssetDTO spaceAssetDTO = assetCacheService.getSpaceAssetDTO(asset.getFileUrl()); + com.apitable.shared.cache.bean.SpaceAssetDTO spaceAssetDTO = + assetCacheService.getSpaceAssetDTO(asset.getFileUrl()); BeanUtil.copyProperties(spaceAssetDTO, body); } // Resource upload processing @@ -179,16 +179,25 @@ public class AssetCallbackServiceImpl implements IAssetCallbackService { return results; } + /** + * * + * @param resourceKeys file urls + */ @Override @Transactional(rollbackFor = Exception.class) - public void widgetCallback(List resourceKeys) { + public void widgetCallback(final List resourceKeys) { log.info("widget callback."); - ExceptionUtil.isFalse(resourceKeys.size() > TOKEN_MAX, ParameterException.INCORRECT_ARG); + ExceptionUtil.isFalse(resourceKeys.size() > TOKEN_MAX, + ParameterException.INCORRECT_ARG); // Get attachments that already exist at file_url in db - List assetEntities = assetMapper.selectByFileUrl(resourceKeys); + List assetEntities = + assetMapper.selectByFileUrl(resourceKeys); for (AssetEntity asset : assetEntities) { - // Get file attributes. there may be throw exceptions, such as no exist file in bucket. - OssStatObject statObject = ossTemplate.getStatObject(asset.getBucketName(), asset.getFileUrl()); + // Get file attributes. there may be throw exceptions, + // such as no exist file in bucket. + OssStatObject statObject = + ossTemplate.getStatObject(asset.getBucketName(), + asset.getFileUrl()); int fileSize = new Long(statObject.getFileSize()).intValue(); AssetEntity updatedAssetEntity = AssetEntity.builder() .id(asset.getId()) @@ -200,7 +209,8 @@ public class AssetCallbackServiceImpl implements IAssetCallbackService { } } - private void checkFileType(String mimeType, Long id, String bucketName, String key) { + private void checkFileType(final String mimeType, final Long id, + final String bucketName, final String key) { // Unrestricted type, end return if (!MediaType.TEXT_HTML_VALUE.equals(mimeType)) { return; @@ -211,23 +221,19 @@ public class AssetCallbackServiceImpl implements IAssetCallbackService { throw new BusinessException(ActionException.FILE_NOT_SUPPORT_HTML); } - @Transactional(rollbackFor = Throwable.class) - public AssetUploadResult completeSpaceAssetUpload(AssetQiniuUploadCallbackBody body) { - // Resource upload processing - AssetUploadResult result = this.dealWithAssetUpload(body); - result.setName(body.getFname()); - return result; - } - - private AssetUploadResult dealWithAssetUpload(AssetQiniuUploadCallbackBody body) { + private AssetUploadResult dealWithAssetUpload( + final AssetUploadCallbackBody body) { AssetUploadResult result = new AssetUploadResult(); - // Lock the hash to prevent concurrent uploads of the same new attachments + // Lock the hash to prevent concurrent uploads + // of the same new attachments Lock lock = redisLockRegistry.obtain(body.getHash()); try { if (lock.tryLock(1, TimeUnit.MINUTES)) { AssetType assetType = AssetType.of(body.getAssetType()); - AssetEntity assetEntity = assetMapper.selectByChecksum(body.getHash()); - // The same attachment does not exist, update and complete the resource data uploaded this time + AssetEntity assetEntity = + assetMapper.selectByChecksum(body.getHash()); + // The same attachment does not exist, + // update and complete the resource data uploaded this time if (ObjectUtil.isNull(assetEntity)) { AssetEntity entity = this.supplementAssetEntity(body); BeanUtil.copyProperties(entity, result); @@ -235,57 +241,78 @@ public class AssetCallbackServiceImpl implements IAssetCallbackService { result.setSize(body.getFsize()); result.setToken(body.getKey()); // If it is a picture, you need to create an audit record - if (body.getMimeType().startsWith(IMAGE_PREFIX) && constProperties.isOssImageAuditCreatable()) { - iAssetAuditService.create(body.getUploadAssetId(), body.getHash(), body.getKey()); + if (body.getMimeType().startsWith(IMAGE_PREFIX) + && constProperties.isOssImageAuditCreatable()) { + iAssetAuditService.create(body.getUploadAssetId(), + body.getHash(), body.getKey()); } // The reference of the data table is updated in the op, - // there is no need to update the reference data when uploading + // there is no need to update the reference data when upload if (assetType != AssetType.DATASHEET) { - iSpaceAssetService.saveAssetInSpace(body.getSpaceId(), body.getNodeId(), body.getUploadAssetId(), body.getHash(), assetType, StrUtil.nullToEmpty(body.getFname()), body.getFsize()); + iSpaceAssetService.saveAssetInSpace(body.getSpaceId(), + body.getNodeId(), body.getUploadAssetId(), + body.getHash(), assetType, + StrUtil.nullToEmpty(body.getFname()), + body.getFsize()); } - } - else { - // If the same attachment exists, use the previous data to clear the data uploaded this time + } else { + // If the same attachment exists, use the previous data + // and clear the data uploaded this time BeanUtil.copyProperties(assetEntity, result); result.setSize(assetEntity.getFileSize().longValue()); result.setToken(assetEntity.getFileUrl()); // Repeat the callback, return directly to the end - if (Objects.equals(assetEntity.getId(), body.getUploadAssetId())) { + if (Objects.equals(assetEntity.getId(), + body.getUploadAssetId())) { return result; } ossTemplate.delete(body.getBucket(), body.getKey()); assetMapper.deleteById(body.getUploadAssetId()); if (assetType != AssetType.DATASHEET) { - // Determine whether the file has been referenced on the node, if so, add a reference count, otherwise add a space attachment record - SpaceAssetDTO assetDto = spaceAssetMapper.selectDto(body.getSpaceId(), body.getNodeId(), assetEntity.getId()); + // Determine whether the file has been referenced + // on the node, if so, add a reference count, + // otherwise add a space attachment record + SpaceAssetDTO assetDto = + spaceAssetMapper.selectDto(body.getSpaceId(), + body.getNodeId(), assetEntity.getId()); if (ObjectUtil.isNotNull(assetDto)) { - // Once used as a cover image, the space resource record is rigidly recorded as cover image, which is convenient to obtain all used cover images. - boolean flag = !assetDto.getType().equals(AssetType.COVER.getValue()) && assetType.equals(AssetType.COVER); - Integer type = flag ? AssetType.COVER.getValue() : null; - iSpaceAssetService.edit(assetDto.getId(), assetDto.getCite() + 1, type); - } - else { - iSpaceAssetService.saveAssetInSpace(body.getSpaceId(), body.getNodeId(), assetEntity.getId(), body.getHash(), assetType, StrUtil.nullToEmpty(body.getFname()), body.getFsize()); + // Once used as a cover image, + // the space resource record is rigidly recorded + // as cover image, which is convenient + // to obtain all used cover images. + boolean flag = !assetDto.getType() + .equals(AssetType.COVER.getValue()) + && assetType.equals(AssetType.COVER); + Integer type = flag ? AssetType.COVER.getValue() + : null; + iSpaceAssetService.edit(assetDto.getId(), + assetDto.getCite() + 1, type); + } else { + iSpaceAssetService.saveAssetInSpace( + body.getSpaceId(), body.getNodeId(), + assetEntity.getId(), body.getHash(), + assetType, StrUtil.nullToEmpty(body.getFname()), + body.getFsize()); } } } + } else { + log.error("Upload operation is too frequent, " + + "please try again later. Hash:{}", body.getHash()); + throw new BusinessException("Upload operation is too frequent, " + + "please try again later."); } - else { - log.error("Upload operation is too frequent, please try again later. Hash:{}", body.getHash()); - throw new BusinessException("Upload operation is too frequent, please try again later."); - } - } - catch (InterruptedException e) { + } catch (InterruptedException e) { e.printStackTrace(); - } - finally { + } finally { lock.unlock(); } return result; } - private AssetEntity supplementAssetEntity(AssetQiniuUploadCallbackBody body) { + private AssetEntity supplementAssetEntity( + final AssetUploadCallbackBody body) { String mimeType = body.getMimeType(); AssetEntity entity = new AssetEntity(); entity.setId(body.getUploadAssetId()); @@ -297,10 +324,11 @@ public class AssetCallbackServiceImpl implements IAssetCallbackService { entity.setExtensionName(MimeTypeMapping.mimeTypeToExtension(mimeType)); // If it is a PDF type resource, generate a preview image and upload it if (MediaType.APPLICATION_PDF_VALUE.equals(mimeType)) { - String pdfImgUploadPath = this.uploadAndSavePdfImg(body.getBucket(), body.getKey()); + String pdfImgUploadPath = + this.uploadAndSavePdfImg(body.getBucket(), body.getKey()); entity.setPreview(pdfImgUploadPath); - } - else if (body.getImageHeight() == null && mimeType.startsWith(IMAGE_PREFIX)) { + } else if (body.getImageHeight() == null + && mimeType.startsWith(IMAGE_PREFIX)) { // If it is a picture, parse the height and width of the picture this.appendImageInfo(body.getBucket(), body.getKey(), entity); } @@ -309,7 +337,8 @@ public class AssetCallbackServiceImpl implements IAssetCallbackService { return entity; } - private String uploadAndSavePdfImg(String bucketName, String key) { + private String uploadAndSavePdfImg(final String bucketName, + final String key) { AtomicReference pdfImgUploadPath = new AtomicReference<>(); ossTemplate.executeStreamFunction(bucketName, key, in -> { @@ -317,19 +346,27 @@ public class AssetCallbackServiceImpl implements IAssetCallbackService { if (imageIn == null) { return; } - try (InputStreamCache pdfImgStreamCache = new InputStreamCache(imageIn, imageIn.available())) { - pdfImgUploadPath.set(StringUtil.buildPath(SPACE_PREFIX)); - String pdfImgChecksum = DigestUtil.md5Hex(pdfImgStreamCache.getInputStream()); - ossTemplate.upload(bucketName, pdfImgStreamCache.getInputStream(), pdfImgUploadPath.get(), MediaType.IMAGE_JPEG_VALUE, pdfImgChecksum); - } - catch (IOException e) { + try (InputStreamCache pdfImgStreamCache = + new InputStreamCache(imageIn, + imageIn.available())) { + pdfImgUploadPath.set( + StringUtil.buildPath(SPACE_PREFIX)); + String pdfImgChecksum = + DigestUtil.md5Hex( + pdfImgStreamCache.getInputStream()); + ossTemplate.upload(bucketName, + pdfImgStreamCache.getInputStream(), + pdfImgUploadPath.get(), + MediaType.IMAGE_JPEG_VALUE, pdfImgChecksum); + } catch (IOException e) { log.error("Failed to upload PDF preview resource", e); } }); return pdfImgUploadPath.get(); } - private void appendImageInfo(String bucketName, String key, AssetEntity entity) { + private void appendImageInfo(final String bucketName, final String key, + final AssetEntity entity) { // If it is a picture, parse the height and width of the picture ossTemplate.executeStreamFunction(bucketName, key, in -> { @@ -339,21 +376,11 @@ public class AssetCallbackServiceImpl implements IAssetCallbackService { entity.setHeight(bi.getHeight()); entity.setWidth(bi.getWidth()); } - } - catch (IOException e) { - log.error("Error reading image {}, error message: {}", key, e.getMessage()); + } catch (IOException e) { + log.error("Error reading image {}, error message: {}", + key, e.getMessage()); } }); } - private void completeWidgetStaticUpload(AssetQiniuUploadCallbackBody body) { - Long assetId = body.getUploadAssetId(); - Long developerAssetId = body.getUploadDeveloperAssetId(); - Long fsize = body.getFsize(); - - boolean flag = SqlHelper.retBool(assetMapper.updateFileSizeById(assetId, fsize)); - flag &= SqlHelper.retBool(developerAssetMapper.updateFileSizeById(developerAssetId, fsize)); - ExceptionUtil.isTrue(flag, DatabaseException.EDIT_ERROR); - } - } diff --git a/backend-server/application/src/main/java/com/apitable/asset/service/impl/package-info.java b/backend-server/application/src/main/java/com/apitable/asset/service/impl/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..0970458e0550c0aded05a98f8ddad8bf5f6c9d80 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/asset/service/impl/package-info.java @@ -0,0 +1,23 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/** + * + * @author Chambers + */ +package com.apitable.asset.service.impl; diff --git a/backend-server/application/src/main/java/com/apitable/asset/service/package-info.java b/backend-server/application/src/main/java/com/apitable/asset/service/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..7daa1c79a1e237740d7aa097730c234a70ddd41c --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/asset/service/package-info.java @@ -0,0 +1,23 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/** + * + * @author Chambers + */ +package com.apitable.asset.service; diff --git a/backend-server/application/src/main/java/com/apitable/auth/service/impl/AuthServiceImpl.java b/backend-server/application/src/main/java/com/apitable/auth/service/impl/AuthServiceImpl.java index 66246721d84ebe39519ac61805002687e11499eb..1fa776ffbed772977eff93b5edb8d99bd7499e82 100644 --- a/backend-server/application/src/main/java/com/apitable/auth/service/impl/AuthServiceImpl.java +++ b/backend-server/application/src/main/java/com/apitable/auth/service/impl/AuthServiceImpl.java @@ -18,54 +18,40 @@ package com.apitable.auth.service.impl; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -import javax.annotation.Resource; - import cn.hutool.core.lang.Validator; import cn.hutool.core.util.BooleanUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; -import lombok.extern.slf4j.Slf4j; - import com.apitable.auth.dto.UserLoginDTO; import com.apitable.auth.ro.LoginRo; import com.apitable.auth.service.IAuthService; import com.apitable.base.enums.ActionException; -import com.apitable.interfaces.billing.facade.EntitlementServiceFacade; -import com.apitable.interfaces.billing.model.EntitlementRemark; +import com.apitable.core.exception.BusinessException; +import com.apitable.core.util.ExceptionUtil; import com.apitable.interfaces.user.facade.UserLinkServiceFacade; import com.apitable.interfaces.user.model.UserLinkRequest; import com.apitable.organization.dto.MemberDTO; import com.apitable.organization.service.IMemberService; import com.apitable.shared.cache.bean.SocialAuthInfo; import com.apitable.shared.cache.service.SocialAuthInfoCacheService; -import com.apitable.shared.captcha.CodeValidateScope; -import com.apitable.shared.captcha.ValidateCodeProcessor; -import com.apitable.shared.captcha.ValidateCodeProcessorManage; -import com.apitable.shared.captcha.ValidateCodeType; -import com.apitable.shared.captcha.ValidateTarget; +import com.apitable.shared.captcha.*; import com.apitable.shared.security.PasswordService; import com.apitable.user.entity.UserEntity; import com.apitable.user.service.IUserService; -import com.apitable.core.exception.BusinessException; -import com.apitable.core.util.ExceptionUtil; - +import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.BoundValueOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import static com.apitable.user.enums.UserException.LOGIN_OFTEN; -import static com.apitable.user.enums.UserException.MOBILE_EMPTY; -import static com.apitable.user.enums.UserException.REGISTER_EMAIL_ERROR; -import static com.apitable.user.enums.UserException.USERNAME_OR_PASSWORD_ERROR; -import static com.apitable.core.constants.RedisConstants.ERROR_PWD_NUM_DIR; -import static com.apitable.core.constants.RedisConstants.USER_AUTH_INFO_TOKEN; -import static com.apitable.core.constants.RedisConstants.getUserInvitedJoinSpaceKey; +import javax.annotation.Resource; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static com.apitable.core.constants.RedisConstants.*; +import static com.apitable.user.enums.UserException.*; /** * Authorization-related service interface implementation @@ -83,9 +69,6 @@ public class AuthServiceImpl implements IAuthService { @Resource private IMemberService iMemberService; - @Resource - private EntitlementServiceFacade entitlementServiceFacade; - @Resource private SocialAuthInfoCacheService socialAuthInfoCacheService; @@ -156,8 +139,7 @@ public class AuthServiceImpl implements IAuthService { // Query whether there is a space member corresponding to a mobile phone number List inactiveMembers = iMemberService.getInactiveMemberDtoByMobile(mobile); iMemberService.activeIfExistInvitationSpace(userId, inactiveMembers.stream().map(MemberDTO::getId).collect(Collectors.toList())); - } - else { + } else { // registered a new user String nickName = socialLogin ? authInfo.getNickName() : null; String avatar = socialLogin ? authInfo.getAvatar() : null; @@ -200,10 +182,9 @@ public class AuthServiceImpl implements IAuthService { // Query whether there is a space member corresponding to the mailbox, only new registration will have this operation List inactiveMembers = iMemberService.getInactiveMemberDtoByEmail(email); iMemberService.activeIfExistInvitationSpace(userId, inactiveMembers.stream().map(MemberDTO::getId).collect(Collectors.toList())); - } - else { + } else { // Email automatic registration users do not provide third-party scan code login binding - userId = registerUserUsingEmail(email, loginRo.getSpaceId()); + userId = registerUserUsingEmail(email); if (StrUtil.isNotEmpty(loginRo.getSpaceId())) { // Cache, used to invite users to give away attachment capacity this.handleCache(userId, loginRo.getSpaceId()); @@ -225,15 +206,11 @@ public class AuthServiceImpl implements IAuthService { return user.getId(); } - public Long registerUserUsingEmail(String email, String spaceId) { + public Long registerUserUsingEmail(String email) { // Create a new user based on the mailbox and activate the corresponding member UserEntity user = iUserService.createUserByEmail(email); // Query whether there is a space member corresponding to the mailbox, only new registration will have this operation List inactiveMembers = iMemberService.getInactiveMemberDtoByEmail(email); - // Invite new users to join the space station to reward attachment capacity, asynchronous operation - if (spaceId != null) { - entitlementServiceFacade.rewardGiftCapacity(spaceId, new EntitlementRemark(user.getId(), user.getNickName())); - } createOrActiveSpace(user, inactiveMembers.stream().map(MemberDTO::getId).collect(Collectors.toList())); return user.getId(); } @@ -241,8 +218,7 @@ public class AuthServiceImpl implements IAuthService { private void createOrActiveSpace(UserEntity user, List memberIds) { if (memberIds.isEmpty()) { iUserService.initialDefaultSpaceForUser(user); - } - else { + } else { iMemberService.activeIfExistInvitationSpace(user.getId(), memberIds); } } diff --git a/backend-server/application/src/main/java/com/apitable/base/model/WidgetAssetUploadCertificateRO.java b/backend-server/application/src/main/java/com/apitable/base/model/WidgetAssetUploadCertificateRO.java index d6fb2d90fa1ba37b6555509b63ce6e5ee40f69bf..01e0ec5044e08b8b165271b4040dddc5f48e9bab 100644 --- a/backend-server/application/src/main/java/com/apitable/base/model/WidgetAssetUploadCertificateRO.java +++ b/backend-server/application/src/main/java/com/apitable/base/model/WidgetAssetUploadCertificateRO.java @@ -50,4 +50,7 @@ public class WidgetAssetUploadCertificateRO { @ApiModelProperty(value = "the package's version. when fileType package, it need", position = 4) private String version; + @ApiModelProperty(value = "the file extend name. when fileType package, it optional, such as: .js", position = 5) + private List fileExtName; + } diff --git a/backend-server/application/src/main/java/com/apitable/base/service/impl/SystemConfigServiceImpl.java b/backend-server/application/src/main/java/com/apitable/base/service/impl/SystemConfigServiceImpl.java index b706b462ae7c9132eec556bdbac208d5843b98b0..1926791fdcd67026ec8b856dc70b4633b808ef59 100644 --- a/backend-server/application/src/main/java/com/apitable/base/service/impl/SystemConfigServiceImpl.java +++ b/backend-server/application/src/main/java/com/apitable/base/service/impl/SystemConfigServiceImpl.java @@ -18,19 +18,16 @@ package com.apitable.base.service.impl; -import java.util.concurrent.TimeUnit; - import javax.annotation.Resource; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; -import com.baomidou.mybatisplus.core.toolkit.IdWorker; -import lombok.extern.slf4j.Slf4j; - import com.apitable.base.entity.SystemConfigEntity; import com.apitable.base.enums.SystemConfigType; import com.apitable.base.mapper.SystemConfigMapper; import com.apitable.base.service.ISystemConfigService; +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; @@ -57,12 +54,8 @@ public class SystemConfigServiceImpl implements ISystemConfigService { if (cacheVal != null) { return cacheVal; } - // Query the database, if it exists, set it as a cache - String dbVal = findConfig(SystemConfigType.WIZARD_CONFIG, lang); - if (dbVal != null) { - redisTemplate.opsForValue().set(key, dbVal, 7, TimeUnit.DAYS); - } - return dbVal; + // Query the database + return findConfig(SystemConfigType.WIZARD_CONFIG, lang); } @Override diff --git a/backend-server/application/src/main/java/com/apitable/interfaces/billing/model/DefaultSubscriptionFeature.java b/backend-server/application/src/main/java/com/apitable/interfaces/billing/model/DefaultSubscriptionFeature.java index 31714e1d2dda6fe23532a9cefae62aacfc1fbfca..c999f93ed621d36679976362f6efac126b791711 100644 --- a/backend-server/application/src/main/java/com/apitable/interfaces/billing/model/DefaultSubscriptionFeature.java +++ b/backend-server/application/src/main/java/com/apitable/interfaces/billing/model/DefaultSubscriptionFeature.java @@ -18,37 +18,12 @@ package com.apitable.interfaces.billing.model; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.AdminNums; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.ApiCallNums; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.CalendarViews; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.CapacitySize; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.FieldPermissionNums; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.FormViews; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.GalleryViews; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.GanttViews; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.KanbanViews; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.MirrorNums; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.NodePermissionNums; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.RowNums; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.RowsPerSheet; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.Seat; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.SheetNums; +import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.*; import com.apitable.interfaces.billing.model.SubscriptionFeatures.SolidFeatures.AuditQueryDays; import com.apitable.interfaces.billing.model.SubscriptionFeatures.SolidFeatures.RemainRecordActivityDays; import com.apitable.interfaces.billing.model.SubscriptionFeatures.SolidFeatures.RemainTimeMachineDays; import com.apitable.interfaces.billing.model.SubscriptionFeatures.SolidFeatures.RemainTrashDays; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.AllowApplyJoin; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.AllowCopyData; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.AllowDownload; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.AllowExport; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.AllowInvitation; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.AllowShare; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.ContactIsolation; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.ForbidCreateOnCatalog; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.RainbowLabel; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.ShowMobileNumber; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.SocialConnect; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.Watermark; +import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.*; public class DefaultSubscriptionFeature implements SubscriptionFeature { @@ -172,6 +147,11 @@ public class DefaultSubscriptionFeature implements SubscriptionFeature { return new AllowCopyData(false); } + @Override + public AllowEmbed getAllowEmbed() { + return new AllowEmbed(false); + } + @Override public ShowMobileNumber getShowMobileNumber() { return new ShowMobileNumber(false); diff --git a/backend-server/application/src/main/java/com/apitable/interfaces/billing/model/SubscriptionFeature.java b/backend-server/application/src/main/java/com/apitable/interfaces/billing/model/SubscriptionFeature.java index 682550d96b1baecc79fa4662446f919798dcfa32..e42f5bd62dff7404059222e7fa270492f138a008 100644 --- a/backend-server/application/src/main/java/com/apitable/interfaces/billing/model/SubscriptionFeature.java +++ b/backend-server/application/src/main/java/com/apitable/interfaces/billing/model/SubscriptionFeature.java @@ -18,37 +18,12 @@ package com.apitable.interfaces.billing.model; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.AdminNums; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.ApiCallNums; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.CalendarViews; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.CapacitySize; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.FieldPermissionNums; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.FormViews; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.GalleryViews; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.GanttViews; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.KanbanViews; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.MirrorNums; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.NodePermissionNums; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.RowNums; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.RowsPerSheet; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.Seat; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.SheetNums; +import com.apitable.interfaces.billing.model.SubscriptionFeatures.ConsumeFeatures.*; import com.apitable.interfaces.billing.model.SubscriptionFeatures.SolidFeatures.AuditQueryDays; import com.apitable.interfaces.billing.model.SubscriptionFeatures.SolidFeatures.RemainRecordActivityDays; import com.apitable.interfaces.billing.model.SubscriptionFeatures.SolidFeatures.RemainTimeMachineDays; import com.apitable.interfaces.billing.model.SubscriptionFeatures.SolidFeatures.RemainTrashDays; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.AllowApplyJoin; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.AllowCopyData; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.AllowDownload; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.AllowExport; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.AllowInvitation; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.AllowShare; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.ContactIsolation; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.ForbidCreateOnCatalog; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.RainbowLabel; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.ShowMobileNumber; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.SocialConnect; -import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.Watermark; +import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.*; public interface SubscriptionFeature { @@ -100,6 +75,8 @@ public interface SubscriptionFeature { AllowCopyData getAllowCopyData(); + AllowEmbed getAllowEmbed(); + ShowMobileNumber getShowMobileNumber(); ContactIsolation getContactIsolation(); diff --git a/backend-server/application/src/main/java/com/apitable/interfaces/billing/model/SubscriptionFeatures.java b/backend-server/application/src/main/java/com/apitable/interfaces/billing/model/SubscriptionFeatures.java index d33a75b59f741a439004334deb71f52d1ff8b264..a3fe51120a1837ecee0950b5fe714a42d07bbd53 100644 --- a/backend-server/application/src/main/java/com/apitable/interfaces/billing/model/SubscriptionFeatures.java +++ b/backend-server/application/src/main/java/com/apitable/interfaces/billing/model/SubscriptionFeatures.java @@ -216,6 +216,13 @@ public class SubscriptionFeatures { super(value); } } + + public static class AllowEmbed extends AbstractBooleanPlanFeature { + + public AllowEmbed(boolean value) { + super(value); + } + } } public static class SolidFeatures { diff --git a/backend-server/application/src/main/java/com/apitable/interfaces/notification/NotificationContextConfig.java b/backend-server/application/src/main/java/com/apitable/interfaces/notification/NotificationContextConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..1d16de20cc61d79958204687ccd8ff2e0108f648 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/interfaces/notification/NotificationContextConfig.java @@ -0,0 +1,46 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.interfaces.notification; + +import com.apitable.interfaces.notification.facade.DefaultMailFacadeImpl; +import com.apitable.interfaces.notification.facade.MailFacade; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Notification Context Config. + * + * @author Chambers + */ +@Configuration(proxyBeanMethods = false) +public class NotificationContextConfig { + + /** + * Inject Default Mail Facade. + * + * @return MailFacade + */ + @Bean + @ConditionalOnMissingBean + public MailFacade defaultMailFacadeImpl() { + return new DefaultMailFacadeImpl(); + } +} diff --git a/backend-server/application/src/main/java/com/apitable/interfaces/notification/facade/DefaultMailFacadeImpl.java b/backend-server/application/src/main/java/com/apitable/interfaces/notification/facade/DefaultMailFacadeImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..21eab43e964ac0165e23443a31e693d3a59fb1c8 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/interfaces/notification/facade/DefaultMailFacadeImpl.java @@ -0,0 +1,39 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.interfaces.notification.facade; + +/** + * Default Mail Facade Implement Class. + * + * @author Chambers + */ +public class DefaultMailFacadeImpl implements MailFacade { + + /** + * * Get Cloud Mail Template Id. + * + * @param lang language + * @param subject mail subject + * @return template id about cloud mail + */ + @Override + public Long getCloudMailTemplateId(final String lang, final String subject) { + return DefaultMailTemplateLoader.getTemplateId(subject); + } +} diff --git a/backend-server/shared/starters/oss/src/main/java/com/apitable/starter/oss/core/qiniu/QiniuTemporaryClientTemplate.java b/backend-server/application/src/main/java/com/apitable/interfaces/notification/facade/DefaultMailTemplateLoader.java similarity index 31% rename from backend-server/shared/starters/oss/src/main/java/com/apitable/starter/oss/core/qiniu/QiniuTemporaryClientTemplate.java rename to backend-server/application/src/main/java/com/apitable/interfaces/notification/facade/DefaultMailTemplateLoader.java index 44efbe5d804fdd3a796781b198118cb214210507..989f22579fc65fd98d0e630bfe3c9495269a8821 100644 --- a/backend-server/shared/starters/oss/src/main/java/com/apitable/starter/oss/core/qiniu/QiniuTemporaryClientTemplate.java +++ b/backend-server/application/src/main/java/com/apitable/interfaces/notification/facade/DefaultMailTemplateLoader.java @@ -16,35 +16,60 @@ * along with this program. If not, see . */ -package com.apitable.starter.oss.core.qiniu; +package com.apitable.interfaces.notification.facade; -import com.apitable.starter.oss.core.OssClientRequest; -import com.apitable.starter.oss.core.OssUploadAuth; -import com.apitable.starter.oss.core.OssUploadPolicy; +import java.util.HashMap; +import java.util.Map; -public class QiniuTemporaryClientTemplate { +import static com.apitable.shared.constants.MailPropConstants.SUBJECT_CHANGE_ADMIN; +import static com.apitable.shared.constants.MailPropConstants.SUBJECT_DATASHEET_REMIND; +import static com.apitable.shared.constants.MailPropConstants.SUBJECT_INVITE_NOTIFY; +import static com.apitable.shared.constants.MailPropConstants.SUBJECT_RECORD_COMMENT; +import static com.apitable.shared.constants.MailPropConstants.SUBJECT_REGISTER; +import static com.apitable.shared.constants.MailPropConstants.SUBJECT_REMOVE_MEMBER; +import static com.apitable.shared.constants.MailPropConstants.SUBJECT_SPACE_APPLY; +import static com.apitable.shared.constants.MailPropConstants.SUBJECT_VERIFY_CODE; - private QiniuOssClientRequestFactory ossClientRequestFactory; +/** + * Default Mail Template Loader. + * + * @author Chambers + */ +public final class DefaultMailTemplateLoader { - public QiniuTemporaryClientTemplate() { - } + private DefaultMailTemplateLoader() { + } - public QiniuTemporaryClientTemplate(QiniuOssClientRequestFactory ossClientRequestFactory) { - this.ossClientRequestFactory = ossClientRequestFactory; - } + /** + * * Get Template ID. + * + * @param subject mail subject + * @return template id + */ + public static Long getTemplateId(final String subject) { + return Singleton.INSTANCE.getTemplateId(subject); + } - public QiniuOssClientRequestFactory getOssClientRequestFactory() { - return ossClientRequestFactory; - } + private enum Singleton { + /** instance. */ + INSTANCE; - public OssUploadAuth uploadToken(String bucket, String key, long expires, OssUploadPolicy uploadPolicy) { - OssClientRequest request = getOssClientRequestFactory().createClient(); - return request.uploadToken(bucket, key, expires, uploadPolicy); - } + /** subject to template id map. */ + private final Map singleton = new HashMap<>(); - public boolean isValidCallback(String originAuthorization, String url, byte[] body, String contentType) { - OssClientRequest request = getOssClientRequestFactory().createClient(); - return request.isValidCallback(originAuthorization, url, body, contentType); + Singleton() { + singleton.put(SUBJECT_CHANGE_ADMIN, 0L); + singleton.put(SUBJECT_INVITE_NOTIFY, 0L); + singleton.put(SUBJECT_REGISTER, 0L); + singleton.put(SUBJECT_RECORD_COMMENT, 0L); + singleton.put(SUBJECT_DATASHEET_REMIND, 0L); + singleton.put(SUBJECT_REMOVE_MEMBER, 0L); + singleton.put(SUBJECT_SPACE_APPLY, 0L); + singleton.put(SUBJECT_VERIFY_CODE, 0L); } + public Long getTemplateId(final String subject) { + return singleton.get(subject); + } + } } diff --git a/backend-server/application/src/main/java/com/apitable/interfaces/notification/facade/MailFacade.java b/backend-server/application/src/main/java/com/apitable/interfaces/notification/facade/MailFacade.java new file mode 100644 index 0000000000000000000000000000000000000000..28df50f55938c2d56ef8c4014a14a450c37559c4 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/interfaces/notification/facade/MailFacade.java @@ -0,0 +1,36 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.interfaces.notification.facade; + +/** + * MailFacade. + * + * @author Chambers + */ +public interface MailFacade { + + /** + * * Get Cloud Mail Template Id. + * + * @param lang language + * @param subject mail subject + * @return template id about cloud mail + */ + Long getCloudMailTemplateId(String lang, String subject); +} diff --git a/backend-server/application/src/main/java/com/apitable/interfaces/notification/facade/package-info.java b/backend-server/application/src/main/java/com/apitable/interfaces/notification/facade/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..f55549d48397b2878aca3a9b23333b5141b1113d --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/interfaces/notification/facade/package-info.java @@ -0,0 +1,23 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/** + * + * @author Chambers + */ +package com.apitable.interfaces.notification.facade; diff --git a/backend-server/application/src/main/java/com/apitable/interfaces/notification/package-info.java b/backend-server/application/src/main/java/com/apitable/interfaces/notification/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..1de0845f73be39b9ae1c4e78138f270a36bcdd8b --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/interfaces/notification/package-info.java @@ -0,0 +1,23 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/** + * + * @author Chambers + */ +package com.apitable.interfaces.notification; diff --git a/backend-server/application/src/main/java/com/apitable/interfaces/widget/WidgetContextConfig.java b/backend-server/application/src/main/java/com/apitable/interfaces/widget/WidgetContextConfig.java index 4ce58d49fbbac2481b27c41668737d7330c40e28..7d3134a9cc2db10bda034568f3eb1ccd5f4b8fcb 100644 --- a/backend-server/application/src/main/java/com/apitable/interfaces/widget/WidgetContextConfig.java +++ b/backend-server/application/src/main/java/com/apitable/interfaces/widget/WidgetContextConfig.java @@ -18,8 +18,8 @@ package com.apitable.interfaces.widget; -import com.apitable.interfaces.widget.facade.DefaultWidgetServiceFacadeImpl; -import com.apitable.interfaces.widget.facade.WidgetServiceFacade; +import com.apitable.interfaces.widget.facade.DefaultWidgetServiceAuditFacadeImpl; +import com.apitable.interfaces.widget.facade.WidgetServiceAuditFacade; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; @@ -30,7 +30,7 @@ public class WidgetContextConfig { @Bean @ConditionalOnMissingBean - public WidgetServiceFacade defaultWidgetServiceFacade() { - return new DefaultWidgetServiceFacadeImpl(); + public WidgetServiceAuditFacade defaultWidgetServiceFacade() { + return new DefaultWidgetServiceAuditFacadeImpl(); } } diff --git a/backend-server/application/src/main/java/com/apitable/interfaces/widget/facade/DefaultWidgetServiceFacadeImpl.java b/backend-server/application/src/main/java/com/apitable/interfaces/widget/facade/DefaultWidgetServiceAuditFacadeImpl.java similarity index 68% rename from backend-server/application/src/main/java/com/apitable/interfaces/widget/facade/DefaultWidgetServiceFacadeImpl.java rename to backend-server/application/src/main/java/com/apitable/interfaces/widget/facade/DefaultWidgetServiceAuditFacadeImpl.java index b27b57c80b1e0f72c24ab0bcadfca42168aaa2d5..e8c689ac32ff58ed71ff43802e38107e83e0e53a 100644 --- a/backend-server/application/src/main/java/com/apitable/interfaces/widget/facade/DefaultWidgetServiceFacadeImpl.java +++ b/backend-server/application/src/main/java/com/apitable/interfaces/widget/facade/DefaultWidgetServiceAuditFacadeImpl.java @@ -18,23 +18,16 @@ package com.apitable.interfaces.widget.facade; +import java.util.ArrayList; import java.util.List; -import com.apitable.interfaces.widget.model.WidgetCopyOption; +import com.apitable.widget.ro.WidgetStoreListRo; +import com.apitable.widget.vo.WidgetStoreListInfo; -public class DefaultWidgetServiceFacadeImpl implements WidgetServiceFacade { - @Override - public String getSpaceIdByWidgetId(String widgetId) { - return null; - } +public class DefaultWidgetServiceAuditFacadeImpl implements WidgetServiceAuditFacade { @Override - public void checkWidgetReference(List childrenNodeIds, List widgetIds) { - - } - - @Override - public void copyWidget(WidgetCopyOption copyOption) { - + public List getWaitReviewWidgetList(WidgetStoreListRo body) { + return new ArrayList<>(); } } diff --git a/backend-server/application/src/main/java/com/apitable/interfaces/widget/facade/WidgetServiceFacade.java b/backend-server/application/src/main/java/com/apitable/interfaces/widget/facade/WidgetServiceAuditFacade.java similarity index 80% rename from backend-server/application/src/main/java/com/apitable/interfaces/widget/facade/WidgetServiceFacade.java rename to backend-server/application/src/main/java/com/apitable/interfaces/widget/facade/WidgetServiceAuditFacade.java index 3154cb4583b61796c7fcbc657bded5978e19ab7f..3764b2314303802ed16821fa66a0b59c7d329c49 100644 --- a/backend-server/application/src/main/java/com/apitable/interfaces/widget/facade/WidgetServiceFacade.java +++ b/backend-server/application/src/main/java/com/apitable/interfaces/widget/facade/WidgetServiceAuditFacade.java @@ -21,12 +21,10 @@ package com.apitable.interfaces.widget.facade; import java.util.List; import com.apitable.interfaces.widget.model.WidgetCopyOption; +import com.apitable.widget.ro.WidgetStoreListRo; +import com.apitable.widget.vo.WidgetStoreListInfo; -public interface WidgetServiceFacade { +public interface WidgetServiceAuditFacade { - String getSpaceIdByWidgetId(String widgetId); - - void checkWidgetReference(List childrenNodeIds, List widgetIds); - - void copyWidget(WidgetCopyOption copyOption); + List getWaitReviewWidgetList(WidgetStoreListRo body); } diff --git a/backend-server/application/src/main/java/com/apitable/internal/assembler/BillingAssembler.java b/backend-server/application/src/main/java/com/apitable/internal/assembler/BillingAssembler.java index a089d9413b39f5274adb77ae8ce1269f5d1d862b..001c30a28e65515102407b9aa10d7ff21ca7b3d3 100644 --- a/backend-server/application/src/main/java/com/apitable/internal/assembler/BillingAssembler.java +++ b/backend-server/application/src/main/java/com/apitable/internal/assembler/BillingAssembler.java @@ -18,17 +18,12 @@ package com.apitable.internal.assembler; -import java.util.Arrays; -import java.util.List; - import com.apitable.interfaces.billing.model.SubscriptionFeature; import com.apitable.interfaces.billing.model.SubscriptionInfo; import com.apitable.internal.vo.InternalSpaceApiUsageVo; import com.apitable.internal.vo.InternalSpaceSubscriptionVo; public class BillingAssembler { - protected static final List ENTERPRISE_MARK = Arrays.asList("Dingtalk_Enterprise", "Enterprise", - "Feishu_Enterprise", "Wecom_Enterprise", "Private_Cloud"); public InternalSpaceSubscriptionVo toVo(SubscriptionInfo subscriptionInfo) { SubscriptionFeature billingPlanFeature = subscriptionInfo.getFeature(); @@ -39,9 +34,7 @@ public class BillingAssembler { subscriptionVo.setMaxKanbanViewsInSpace(billingPlanFeature.getKanbanViews().getValue()); subscriptionVo.setMaxRowsInSpace(billingPlanFeature.getRowNums().getValue()); subscriptionVo.setMaxRowsPerSheet(billingPlanFeature.getRowsPerSheet().getValue()); - if (ENTERPRISE_MARK.contains(subscriptionInfo.getProduct())) { - subscriptionVo.setCanCallEnterpriseApi(true); - } + subscriptionVo.setAllowEmbed(billingPlanFeature.getAllowEmbed().getValue()); return subscriptionVo; } diff --git a/backend-server/application/src/main/java/com/apitable/internal/controller/InternalUserController.java b/backend-server/application/src/main/java/com/apitable/internal/controller/InternalUserController.java index 0bda09deee2ed9f52955cc7f2556c6ec76cb7608..6835b878af3da036d416b59ce024d1881123014a 100644 --- a/backend-server/application/src/main/java/com/apitable/internal/controller/InternalUserController.java +++ b/backend-server/application/src/main/java/com/apitable/internal/controller/InternalUserController.java @@ -18,44 +18,42 @@ package com.apitable.internal.controller; -import java.time.LocalDateTime; -import java.util.List; -import java.util.stream.Collectors; - -import javax.annotation.Resource; -import javax.servlet.http.HttpSession; - -import com.google.common.collect.Lists; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; - +import com.apitable.core.support.ResponseData; +import com.apitable.core.util.ExceptionUtil; +import com.apitable.core.util.HttpContextUtil; +import com.apitable.internal.ro.PausedUserHistoryRo; import com.apitable.shared.cache.bean.LoginUserDto; +import com.apitable.shared.clock.spring.ClockManager; import com.apitable.shared.component.scanner.annotation.ApiResource; import com.apitable.shared.component.scanner.annotation.GetResource; import com.apitable.shared.component.scanner.annotation.PostResource; import com.apitable.shared.constants.SessionAttrConstants; import com.apitable.shared.context.LoginContext; import com.apitable.shared.context.SessionContext; +import com.apitable.user.dto.PausedUserHistoryDto; +import com.apitable.user.dto.UserInPausedDto; import com.apitable.user.entity.UserEntity; import com.apitable.user.enums.UserClosingException; import com.apitable.user.enums.UserException; import com.apitable.user.enums.UserOperationType; -import com.apitable.user.dto.PausedUserHistoryDto; -import com.apitable.internal.ro.PausedUserHistoryRo; -import com.apitable.user.dto.UserInPausedDto; import com.apitable.user.service.IUserHistoryService; import com.apitable.user.service.IUserService; import com.apitable.user.vo.UserBaseInfoVo; -import com.apitable.core.support.ResponseData; -import com.apitable.core.util.ExceptionUtil; -import com.apitable.core.util.HttpContextUtil; - +import com.google.common.collect.Lists; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; import org.springframework.http.MediaType; import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +import javax.annotation.Resource; +import javax.servlet.http.HttpSession; +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; + /** * Internal Service - User Interface */ @@ -100,7 +98,7 @@ public class InternalUserController { @PostResource(name = "get the cooling-off period user operation record", path = "/getUserHistories", requiredPermission = false, requiredLogin = false) @ApiOperation(value = "get the cooling-off period user operation record", notes = "get the cooling-off period user operation record", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseData> getUserHistoryDtos(@RequestBody PausedUserHistoryRo userHistoryRo) { - LocalDateTime now = LocalDateTime.now(); + LocalDateTime now = ClockManager.me().getLocalDateTimeNow(); LocalDateTime createdBefore = now.minusDays(30 + userHistoryRo.getLimitDays()); // LocalDateTime createdAfter = now.minusDays(lastDays); // After obtaining the specified cooling-off period, there has been an operation to cancel the application within 30 days before. diff --git a/backend-server/application/src/main/java/com/apitable/internal/vo/InternalSpaceSubscriptionVo.java b/backend-server/application/src/main/java/com/apitable/internal/vo/InternalSpaceSubscriptionVo.java index bb3e82e1c62190c09d79eb9dc68d668a22fd72df..5cd718ff2f9539c2b7d855aeb48de53c3c654831 100644 --- a/backend-server/application/src/main/java/com/apitable/internal/vo/InternalSpaceSubscriptionVo.java +++ b/backend-server/application/src/main/java/com/apitable/internal/vo/InternalSpaceSubscriptionVo.java @@ -57,7 +57,7 @@ public class InternalSpaceSubscriptionVo { @JsonSerialize(nullsUsing = NullNumberSerializer.class) private Long maxCalendarViewsInSpace; - @ApiModelProperty(value = "Is it possible to call enterprise-level APIs", example = "true", position = 7) + @ApiModelProperty(value = "allow use embed", example = "true", position = 7) @JsonSerialize(nullsUsing = NullBooleanSerializer.class) - private Boolean canCallEnterpriseApi; + private Boolean allowEmbed; } diff --git a/backend-server/application/src/main/java/com/apitable/player/dto/PlayerBaseDTO.java b/backend-server/application/src/main/java/com/apitable/player/dto/PlayerBaseDTO.java index c6e199ea2555e43299a6ce37cd0dafae24ef8ded..5c19f3cd8aeaabfb8f83337a4f61846c86e713b9 100644 --- a/backend-server/application/src/main/java/com/apitable/player/dto/PlayerBaseDTO.java +++ b/backend-server/application/src/main/java/com/apitable/player/dto/PlayerBaseDTO.java @@ -32,6 +32,10 @@ public class PlayerBaseDTO { private String avatar; + private Integer color; + + private String nickName; + private String team; private Boolean isNickNameModified; diff --git a/backend-server/application/src/main/java/com/apitable/player/service/impl/PlayerNotificationServiceImpl.java b/backend-server/application/src/main/java/com/apitable/player/service/impl/PlayerNotificationServiceImpl.java index 49662031e92a58f09d3a894fdd944986b7526487..c79a06daf19bd1fe446815c8d20be722b4497218 100644 --- a/backend-server/application/src/main/java/com/apitable/player/service/impl/PlayerNotificationServiceImpl.java +++ b/backend-server/application/src/main/java/com/apitable/player/service/impl/PlayerNotificationServiceImpl.java @@ -18,22 +18,6 @@ package com.apitable.player.service.impl; -import java.io.IOException; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -import javax.annotation.Resource; - import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.ListUtil; @@ -45,20 +29,14 @@ import cn.hutool.core.lang.Dict; import cn.hutool.core.map.MapUtil; import cn.hutool.core.net.url.UrlPath; import cn.hutool.core.net.url.UrlQuery; -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.CharsetUtil; -import cn.hutool.core.util.NumberUtil; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.*; import cn.hutool.crypto.digest.DigestUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; -import com.baomidou.mybatisplus.core.toolkit.IdWorker; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.baomidou.mybatisplus.extension.toolkit.SqlHelper; -import lombok.extern.slf4j.Slf4j; -import org.apache.ibatis.cursor.Cursor; - +import com.apitable.core.constants.RedisConstants; +import com.apitable.core.exception.BusinessException; +import com.apitable.core.util.ExceptionUtil; +import com.apitable.core.util.SpringContextHolder; import com.apitable.organization.service.IMemberService; import com.apitable.organization.service.IUnitService; import com.apitable.player.dto.NotificationModelDTO; @@ -76,12 +54,7 @@ import com.apitable.player.vo.PlayerBaseVo; import com.apitable.shared.cache.bean.LoginUserDto; import com.apitable.shared.component.ClientEntryTemplateConfig; import com.apitable.shared.component.TaskManager; -import com.apitable.shared.component.notification.INotificationFactory; -import com.apitable.shared.component.notification.NotificationHelper; -import com.apitable.shared.component.notification.NotificationRenderMap; -import com.apitable.shared.component.notification.NotificationTemplateId; -import com.apitable.shared.component.notification.NotificationToTag; -import com.apitable.shared.component.notification.NotifyMailFactory; +import com.apitable.shared.component.notification.*; import com.apitable.shared.component.notification.NotifyMailFactory.MailWithLang; import com.apitable.shared.config.properties.ConstProperties; import com.apitable.shared.constants.NotificationConstants; @@ -96,29 +69,28 @@ import com.apitable.user.dto.UserLangDTO; import com.apitable.user.mapper.UserMapper; import com.apitable.user.service.IUserService; import com.apitable.workspace.service.INodeService; -import com.apitable.core.constants.RedisConstants; -import com.apitable.core.exception.BusinessException; -import com.apitable.core.util.ExceptionUtil; -import com.apitable.core.util.SpringContextHolder; - +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.baomidou.mybatisplus.extension.toolkit.SqlHelper; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.cursor.Cursor; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import javax.annotation.Resource; +import java.io.IOException; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + import static cn.hutool.core.date.DatePattern.NORM_DATETIME_MINUTE_PATTERN; -import static com.apitable.shared.constants.NotificationConstants.EMAIL_CONTACT_URL; -import static com.apitable.shared.constants.NotificationConstants.EMAIL_CREATED_AT; -import static com.apitable.shared.constants.NotificationConstants.EMAIL_DATASHEET_URL; -import static com.apitable.shared.constants.NotificationConstants.EMAIL_MEMBER_NAME; -import static com.apitable.shared.constants.NotificationConstants.EMAIL_NODE_NAME; -import static com.apitable.shared.constants.NotificationConstants.EMAIL_RECORD_ID; -import static com.apitable.shared.constants.NotificationConstants.EMAIL_RECORD_URL; -import static com.apitable.shared.constants.NotificationConstants.EMAIL_SPACE_NAME; -import static com.apitable.shared.constants.NotificationConstants.EMAIL_URL; -import static com.apitable.shared.constants.NotificationConstants.EMAIL_VIEW_ID; -import static com.apitable.shared.constants.NotificationConstants.EXPIRE_AT; -import static com.apitable.shared.constants.NotificationConstants.INVOLVE_RECORD_IDS; +import static com.apitable.shared.constants.NotificationConstants.*; /** *

diff --git a/backend-server/application/src/main/java/com/apitable/player/vo/PlayerBaseVo.java b/backend-server/application/src/main/java/com/apitable/player/vo/PlayerBaseVo.java index 3b0d8d67ba0b44632660bbdba786f649ed5fa67a..e95308691b99dc8ba1556f0d559a73baa9e2b109 100644 --- a/backend-server/application/src/main/java/com/apitable/player/vo/PlayerBaseVo.java +++ b/backend-server/application/src/main/java/com/apitable/player/vo/PlayerBaseVo.java @@ -18,6 +18,9 @@ package com.apitable.player.vo; +import com.apitable.shared.support.serializer.ImageSerializer; +import com.apitable.shared.support.serializer.NullBooleanSerializer; +import com.apitable.shared.support.serializer.NullStringSerializer; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import io.swagger.annotations.ApiModel; @@ -25,10 +28,6 @@ import io.swagger.annotations.ApiModelProperty; import lombok.Builder; import lombok.Data; -import com.apitable.shared.support.serializer.ImageSerializer; -import com.apitable.shared.support.serializer.NullBooleanSerializer; -import com.apitable.shared.support.serializer.NullStringSerializer; - /** *

* Basic user information @@ -80,4 +79,12 @@ public class PlayerBaseVo { @ApiModelProperty(value = "User player type 1: members in the space have not been removed, 2 members outside the space have been removed, and 3 visitors (non space registered users)", example = "1") private Integer playerType; + + @ApiModelProperty(value = "default avatar color number", example = "1") + @JsonSerialize(nullsUsing = NullStringSerializer.class) + private Integer avatarColor; + + @ApiModelProperty(value = "Nick Name", example = "Zhang San") + @JsonSerialize(nullsUsing = NullStringSerializer.class) + private String nickName; } diff --git a/backend-server/application/src/main/java/com/apitable/shared/captcha/email/TencentMailTemplate.java b/backend-server/application/src/main/java/com/apitable/shared/captcha/email/TencentMailTemplate.java deleted file mode 100644 index 58661e4f1f6234a54291aae5eb39a7ec13108345..0000000000000000000000000000000000000000 --- a/backend-server/application/src/main/java/com/apitable/shared/captcha/email/TencentMailTemplate.java +++ /dev/null @@ -1,390 +0,0 @@ -/* - * APITable - * Copyright (C) 2022 APITable Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.apitable.shared.captcha.email; - -import java.util.Locale; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_ADD_RECORD_LIMITED; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_ADD_RECORD_SOON_LIMITED; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_CAPACITY_FULL; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_CHANGE_ADMIN; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_DATASHEET_REMIND; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_INVITE_NOTIFY; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_PAI_SUCCESS; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_RECORD_COMMENT; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_REGISTER; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_REMOVE_MEMBER; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_SPACE_APPLY; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_SUBSCRIBED_ADMIN_LIMIT; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_SUBSCRIBED_API_LIMIT; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_SUBSCRIBED_CALENDAR_LIMIT; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_SUBSCRIBED_CAPACITY_LIMIT; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_SUBSCRIBED_DATASHEET_LIMIT; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_SUBSCRIBED_DATASHEET_RECORD_LIMIT; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_SUBSCRIBED_FIELD_PERMISSION_LIMIT; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_SUBSCRIBED_FILE_PERMISSION_LIMIT; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_SUBSCRIBED_FORM_LIMIT; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_SUBSCRIBED_GANNT_LIMIT; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_SUBSCRIBED_MIRROR_LIMIT; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_SUBSCRIBED_RECORD_CELL_UPDATED; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_SUBSCRIBED_RECORD_COMMENTED; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_SUBSCRIBED_RECORD_LIMIT; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_SUBSCRIBED_SEATS_LIMIT; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_TASK_REMINDER; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_VERIFY_CODE; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_WARN_NOTIFY; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_WIDGET_QUALIFICATION_AUTH_FAIL; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_WIDGET_QUALIFICATION_AUTH_SUCCESS; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_WIDGET_SUBMIT_FAIL; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_WIDGET_SUBMIT_SUCCESS; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_WIDGET_TRANSFER_NOTIFY; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_WIDGET_UNPUBLISH_GLOBAL_NOTIFY; -import static com.apitable.shared.constants.MailPropConstants.SUBJECT_WIDGET_UNPUBLISH_NOTIFY; - -/** - *

- * tencent cloud email template - *

- * - * @author Chambers - */ -public class TencentMailTemplate { - - public static Long getTemplateId(String lang, String subject) { - if (Locale.US.toLanguageTag().equals(lang)) { - return USMailTemplate.getTemplateIdBySubject(subject); - } - return ChineseMailTemplate.getTemplateIdBySubject(subject); - } - - - @AllArgsConstructor - @Getter - public enum ChineseMailTemplate { - - /** - * email verification code - */ - VERIFY_CODE(SUBJECT_VERIFY_CODE, 27395L), - - /** - * primary admin transfer notification - */ - CHANGE_ADMIN(SUBJECT_CHANGE_ADMIN, 26507L), - - /** - * payment successful - */ - PAI_SUCCESS(SUBJECT_PAI_SUCCESS, 23462L), - - /** - * registration verification code - */ - REGISTER(SUBJECT_REGISTER, 27395L), - - /** - * space invitation notice - */ - INVITE_NOTIFY(SUBJECT_INVITE_NOTIFY, 26497L), - - /** - * Notice that the space capacity has reached the upper limit - */ - CAPACITY_FULL(SUBJECT_CAPACITY_FULL, 23465L), - - /** - * log comment mentions - */ - RECORD_COMMENT(SUBJECT_RECORD_COMMENT, 23466L), - - /** - * member field notification - */ - DATASHEET_REMIND(SUBJECT_DATASHEET_REMIND, 23467L), - - /** - * transfer of mini program publishing rights - */ - WIDGET_TRANSFER_NOTIFY(SUBJECT_WIDGET_TRANSFER_NOTIFY, 23468L), - - /** - * small program takedown notice - */ - WIDGET_UNPUBLISH_NOTIFY(SUBJECT_WIDGET_UNPUBLISH_NOTIFY, 23469L), - - /** - * space station join application - */ - SPACE_APPLY(SUBJECT_SPACE_APPLY, 26509L), - - /** - * move out of space station notification - */ - REMOVE_MEMBER(SUBJECT_REMOVE_MEMBER, 23471L), - - /** - * alert email - */ - WARN_NOTIFY(SUBJECT_WARN_NOTIFY, 23533L), - - /** - * adding records is about to exceed the limit - */ - ADD_RECORD_SOON_LIMITED(SUBJECT_ADD_RECORD_SOON_LIMITED, 23956L), - - /** - * add record overrun - */ - ADD_RECORD_LIMITED(SUBJECT_ADD_RECORD_LIMITED, 24007L), - - /** - * the applet was successfully launched - */ - WIDGET_SUBMIT_SUCCESS(SUBJECT_WIDGET_SUBMIT_SUCCESS, 24533L), - - /** - * failed to launch the applet - */ - WIDGET_SUBMIT_FAIL(SUBJECT_WIDGET_SUBMIT_FAIL, 24534L), - - /** - * Mini Program Developer Qualification Certification Successfully - */ - WIDGET_QUALIFICATION_AUTH_SUCCESS(SUBJECT_WIDGET_QUALIFICATION_AUTH_SUCCESS, 24532L), - - /** - * The qualification authentication of the Mini Program developer failed - */ - WIDGET_QUALIFICATION_AUTH_FAIL(SUBJECT_WIDGET_QUALIFICATION_AUTH_FAIL, 24531L), - - /** - * notification of global removal of applet - */ - WIDGET_UNPUBLISH_GLOBAL_NOTIFY(SUBJECT_WIDGET_UNPUBLISH_GLOBAL_NOTIFY, 25076L), - - /** - * task reminder - */ - TASK_REMINDER(SUBJECT_TASK_REMINDER, 25038L), - - /** - * follow record change notifications - */ - SUBSCRIBED_RECORD_CELL_UPDATED(SUBJECT_SUBSCRIBED_RECORD_CELL_UPDATED, 25336L), - - /** - * follow records are notified by comments - */ - SUBSCRIBED_RECORD_COMMENTED(SUBJECT_SUBSCRIBED_RECORD_COMMENTED, 25334L), - - SUBSCRIBED_DATASHEET_LIMIT(SUBJECT_SUBSCRIBED_DATASHEET_LIMIT, 52372L), - - SUBSCRIBED_DATASHEET_RECORD_LIMIT(SUBJECT_SUBSCRIBED_DATASHEET_RECORD_LIMIT, 52375L), - - SUBSCRIBED_CAPACITY_LIMIT(SUBJECT_SUBSCRIBED_CAPACITY_LIMIT, 52378L), - - SUBSCRIBED_SEATS_LIMIT(SUBJECT_SUBSCRIBED_SEATS_LIMIT, 52379L), - - SUBSCRIBED_RECORD_LIMIT(SUBJECT_SUBSCRIBED_RECORD_LIMIT, 52382L), - - SUBSCRIBED_API_LIMIT(SUBJECT_SUBSCRIBED_API_LIMIT, 52383L), - - SUBSCRIBED_CALENDAR_LIMIT(SUBJECT_SUBSCRIBED_CALENDAR_LIMIT, 52386L), - - SUBSCRIBED_FORM_LIMIT(SUBJECT_SUBSCRIBED_FORM_LIMIT, 52387L), - - SUBSCRIBED_MIRROR_LIMIT(SUBJECT_SUBSCRIBED_MIRROR_LIMIT, 52390L), - - SUBSCRIBED_GANNT_LIMIT(SUBJECT_SUBSCRIBED_GANNT_LIMIT, 52391L), - - SUBSCRIBED_FIELD_PERMISSION_LIMIT(SUBJECT_SUBSCRIBED_FIELD_PERMISSION_LIMIT, 52394L), - - SUBSCRIBED_FILE_PERMISSION_LIMIT(SUBJECT_SUBSCRIBED_FILE_PERMISSION_LIMIT, 52395L), - - SUBSCRIBED_ADMIN_LIMIT(SUBJECT_SUBSCRIBED_ADMIN_LIMIT, 52398L); - - private final String subject; - - private final Long templateId; - - public static Long getTemplateIdBySubject(String subject) { - Long templateId = null; - for (ChineseMailTemplate ele : ChineseMailTemplate.values()) { - if (subject.equals(ele.getSubject())) { - templateId = ele.getTemplateId(); - break; - } - } - return templateId; - } - - } - - - @AllArgsConstructor - @Getter - public enum USMailTemplate { - - /** - * email verification code - */ - VERIFY_CODE(SUBJECT_VERIFY_CODE, 23612L), - - /** - * primary admin transfer notification - */ - CHANGE_ADMIN(SUBJECT_CHANGE_ADMIN, 26508L), - - /** - * The payment is successful (there is no English version template. 23462 is a Chinese template) - */ - PAI_SUCCESS(SUBJECT_PAI_SUCCESS, 23462L), - - /** - * registration verification code - */ - REGISTER(SUBJECT_REGISTER, 23612L), - - /** - * space invitation notice - */ - INVITE_NOTIFY(SUBJECT_INVITE_NOTIFY, 26498L), - - /** - * Notice that the space capacity has reached the upper limit - */ - CAPACITY_FULL(SUBJECT_CAPACITY_FULL, 23616L), - - /** - * record comment mentions - */ - RECORD_COMMENT(SUBJECT_RECORD_COMMENT, 23617L), - - /** - * member field notification - */ - DATASHEET_REMIND(SUBJECT_DATASHEET_REMIND, 23618L), - - /** - * transfer of mini program publishing rights - */ - TRANSFER_WIDGET_NOTIFY(SUBJECT_WIDGET_TRANSFER_NOTIFY, 23619L), - - /** - * small program takedown notice - */ - UNPUBLISH_WIDGET_NOTIFY(SUBJECT_WIDGET_UNPUBLISH_NOTIFY, 23620L), - - /** - * space station join application - */ - SPACE_APPLY(SUBJECT_SPACE_APPLY, 26510L), - - /** - * move out of space station notification - */ - REMOVE_MEMBER(SUBJECT_REMOVE_MEMBER, 23622L), - - /** - * adding records is about to exceed the limit - */ - ADD_RECORD_SOON_LIMITED(SUBJECT_ADD_RECORD_SOON_LIMITED, 23957L), - - /** - * add record overrun - */ - ADD_RECORD_LIMITED(SUBJECT_ADD_RECORD_LIMITED, 24008L), - - /** - * the applet was successfully launched - */ - WIDGET_SUBMIT_SUCCESS(SUBJECT_WIDGET_SUBMIT_SUCCESS, 25083L), - - /** - * failed to launch the applet - */ - WIDGET_SUBMIT_FAIL(SUBJECT_WIDGET_SUBMIT_FAIL, 25084L), - - /** - * notification of global removal of applet - */ - WIDGET_UNPUBLISH_GLOBAL_NOTIFY(SUBJECT_WIDGET_UNPUBLISH_GLOBAL_NOTIFY, 25082L), - - /** - * task reminder - */ - TASK_REMINDER(SUBJECT_TASK_REMINDER, 25039L), - - /** - * follow record change notifications - */ - SUBSCRIBED_RECORD_CELL_UPDATED(SUBJECT_SUBSCRIBED_RECORD_CELL_UPDATED, 25337L), - - /** - * follow records are notified by comments - */ - SUBSCRIBED_RECORD_COMMENTED(SUBJECT_SUBSCRIBED_RECORD_COMMENTED, 25335L), - - SUBSCRIBED_DATASHEET_LIMIT(SUBJECT_SUBSCRIBED_DATASHEET_LIMIT, 52373L), - - SUBSCRIBED_DATASHEET_RECORD_LIMIT(SUBJECT_SUBSCRIBED_DATASHEET_RECORD_LIMIT, 52376L), - - SUBSCRIBED_CAPACITY_LIMIT(SUBJECT_SUBSCRIBED_CAPACITY_LIMIT, 52377L), - - SUBSCRIBED_SEATS_LIMIT(SUBJECT_SUBSCRIBED_SEATS_LIMIT, 52380L), - - SUBSCRIBED_RECORD_LIMIT(SUBJECT_SUBSCRIBED_RECORD_LIMIT, 52381L), - - SUBSCRIBED_API_LIMIT(SUBJECT_SUBSCRIBED_API_LIMIT, 52384L), - - SUBSCRIBED_CALENDAR_LIMIT(SUBJECT_SUBSCRIBED_CALENDAR_LIMIT, 52385L), - - SUBSCRIBED_FORM_LIMIT(SUBJECT_SUBSCRIBED_FORM_LIMIT, 52388L), - - SUBSCRIBED_MIRROR_LIMIT(SUBJECT_SUBSCRIBED_MIRROR_LIMIT, 52389L), - - SUBSCRIBED_GANNT_LIMIT(SUBJECT_SUBSCRIBED_GANNT_LIMIT, 52392L), - - SUBSCRIBED_FIELD_PERMISSION_LIMIT(SUBJECT_SUBSCRIBED_FIELD_PERMISSION_LIMIT, 52393L), - - SUBSCRIBED_FILE_PERMISSION_LIMIT(SUBJECT_SUBSCRIBED_FILE_PERMISSION_LIMIT, 52396L), - - SUBSCRIBED_ADMIN_LIMIT(SUBJECT_SUBSCRIBED_ADMIN_LIMIT, 52397L); - - private final String subject; - - private final Long templateId; - - public static Long getTemplateIdBySubject(String subject) { - Long templateId = null; - for (USMailTemplate ele : USMailTemplate.values()) { - if (subject.equals(ele.getSubject())) { - templateId = ele.getTemplateId(); - break; - } - } - return templateId; - } - - } - -} diff --git a/backend-server/application/src/main/java/com/apitable/shared/clock/spring/ClockManager.java b/backend-server/application/src/main/java/com/apitable/shared/clock/spring/ClockManager.java index 24746cc6b2c15753aa6179a908e3e492f4f64b02..7a4ddb9c001714c973466352c457c33e709f86c0 100644 --- a/backend-server/application/src/main/java/com/apitable/shared/clock/spring/ClockManager.java +++ b/backend-server/application/src/main/java/com/apitable/shared/clock/spring/ClockManager.java @@ -18,22 +18,19 @@ package com.apitable.shared.clock.spring; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.OffsetDateTime; - -import lombok.extern.slf4j.Slf4j; - import com.apitable.core.util.SpringContextHolder; import com.apitable.shared.clock.Clock; import com.apitable.shared.clock.DefaultClock; import com.apitable.shared.clock.MockClock; import com.apitable.shared.component.SystemEnvironmentVariable; - +import com.apitable.shared.config.ServerConfig; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Component; -import static com.apitable.shared.constants.TimeZoneConstants.DEFAULT_TIME_ZONE; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; @Component @Slf4j @@ -41,9 +38,12 @@ public class ClockManager implements InitializingBean { private Clock clock; + private final ServerConfig serverConfig; + private final SystemEnvironmentVariable environmentVariable; - public ClockManager(SystemEnvironmentVariable environmentVariable) { + public ClockManager(ServerConfig serverConfig, SystemEnvironmentVariable environmentVariable) { + this.serverConfig = serverConfig; this.environmentVariable = environmentVariable; } @@ -72,21 +72,20 @@ public class ClockManager implements InitializingBean { public LocalDate getLocalDateNow() { OffsetDateTime utcNow = getUTCNow(); log.info("utc now: {}", utcNow); - return utcNow.withOffsetSameInstant(DEFAULT_TIME_ZONE).toLocalDate(); + return utcNow.withOffsetSameInstant(serverConfig.getTimeZone()).toLocalDate(); } public LocalDateTime getLocalDateTimeNow() { OffsetDateTime utcNow = getUTCNow(); log.info("utc now: {}", utcNow); - return utcNow.withOffsetSameInstant(DEFAULT_TIME_ZONE).toLocalDateTime(); + return utcNow.withOffsetSameInstant(serverConfig.getTimeZone()).toLocalDateTime(); } @Override - public void afterPropertiesSet() throws Exception { + public void afterPropertiesSet() { if (environmentVariable.isTestEnabled()) { clock = new MockClock(); - } - else { + } else { clock = new DefaultClock(); } } diff --git a/backend-server/application/src/main/java/com/apitable/shared/component/notification/NotificationFactory.java b/backend-server/application/src/main/java/com/apitable/shared/component/notification/NotificationFactory.java index 0d3df3be89f618a08985b91c6d62e6bb913f825b..e3ffa00d4bf1e45b5f8051e21776e8cc71c702d8 100644 --- a/backend-server/application/src/main/java/com/apitable/shared/component/notification/NotificationFactory.java +++ b/backend-server/application/src/main/java/com/apitable/shared/component/notification/NotificationFactory.java @@ -264,7 +264,7 @@ public class NotificationFactory implements INotificationFactory { .builder() .playerType(a.getIsMemberDeleted() ? PlayerType.MEMBER_DELETED.getType() : PlayerType.MEMBER.getType()) .userName(a.getUserName()).uuid(a.getUuid()).avatar(a.getAvatar()).email(a.getEmail()) - .memberId(a.getMemberId()).memberName(a.getMemberName()).team(a.getTeam()) + .memberId(a.getMemberId()).memberName(a.getMemberName()).team(a.getTeam()).avatarColor(a.getColor()).nickName(a.getNickName()) .isNickNameModified(a.getIsNickNameModified()) .isMemberNameModified(a.getIsMemberNameModified()) .isDeleted(a.getIsMemberDeleted()).build(), (k1, k2) -> k1))); diff --git a/backend-server/application/src/main/java/com/apitable/shared/component/notification/NotificationManager.java b/backend-server/application/src/main/java/com/apitable/shared/component/notification/NotificationManager.java index c361c4ed210fe59a46119c620b9685e1f0f502bf..40a92d58dba33328d5ff41120fb978c3b8d9cb4f 100644 --- a/backend-server/application/src/main/java/com/apitable/shared/component/notification/NotificationManager.java +++ b/backend-server/application/src/main/java/com/apitable/shared/component/notification/NotificationManager.java @@ -106,11 +106,7 @@ public class NotificationManager { public void spaceNotify(NotificationTemplateId templateId, Long userId, String spaceId, Object result) { HttpServletRequest request = HttpContextUtil.getRequest(); - ContentCachingRequestWrapper requestWrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class); - if (requestWrapper == null) { - log.error("Request Wrapper is null"); - return; - } + ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request); Object nodeId = NotificationHelper.resolveNodeId(requestWrapper, result); if (ObjectUtil.isNotNull(nodeId)) { String nodeIdStr = nodeId.toString(); diff --git a/backend-server/application/src/main/java/com/apitable/shared/component/notification/NotifyMailFactory.java b/backend-server/application/src/main/java/com/apitable/shared/component/notification/NotifyMailFactory.java index ad52ea21e7de6d48b3735afcc996216e51952717..bf83a57c5459f9873dfaa8e2ea1d5bb78ffa2c03 100644 --- a/backend-server/application/src/main/java/com/apitable/shared/component/notification/NotifyMailFactory.java +++ b/backend-server/application/src/main/java/com/apitable/shared/component/notification/NotifyMailFactory.java @@ -37,20 +37,19 @@ import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; -import lombok.Data; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.extern.slf4j.Slf4j; - +import com.apitable.core.exception.BusinessException; +import com.apitable.core.util.SpringContextHolder; +import com.apitable.interfaces.notification.facade.MailFacade; +import com.apitable.shared.config.properties.EmailSendProperties; import com.apitable.starter.beetl.autoconfigure.BeetlTemplate; import com.apitable.starter.mail.autoconfigure.EmailMessage; import com.apitable.starter.mail.autoconfigure.MailTemplate; import com.apitable.starter.mail.core.CloudEmailMessage; import com.apitable.starter.mail.core.CloudMailSender; -import com.apitable.shared.captcha.email.TencentMailTemplate; -import com.apitable.shared.config.properties.EmailSendProperties; -import com.apitable.core.exception.BusinessException; -import com.apitable.core.util.SpringContextHolder; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.ClassPathResource; @@ -95,9 +94,7 @@ import static com.apitable.shared.constants.MailPropConstants.SUBJECT_WIDGET_UNP import static java.util.stream.Collectors.toList; /** - *

- * Notification Mail Factory - *

+ * Notification Mail Factory. * * @author Shawn Deng */ @@ -105,362 +102,468 @@ import static java.util.stream.Collectors.toList; @Component public class NotifyMailFactory { - @Resource - private BeetlTemplate beetlTemplate; - - @Resource - private EmailSendProperties emailSendProperties; - - @Autowired(required = false) - private CloudMailSender cloudMailSender; - - @Autowired(required = false) - private MailTemplate mailTemplate; - - public static NotifyMailFactory me() { - return SpringContextHolder.getBean(NotifyMailFactory.class); + /** */ + @Resource private BeetlTemplate beetlTemplate; + + /** */ + @Resource private EmailSendProperties emailSendProperties; + + /** */ + @Resource private MailFacade mailFacade; + + /** */ + @Autowired(required = false) + private CloudMailSender cloudMailSender; + + /** */ + @Autowired(required = false) + private MailTemplate mailTemplate; + + /** + * @return NotifyMailFactory + */ + public static NotifyMailFactory me() { + return SpringContextHolder.getBean(NotifyMailFactory.class); + } + + /** + * @param subjectType subjectType + * @param subjectDict subjectDict + * @param dict dict + * @param tos tos + */ + public void sendMail( + final String subjectType, + final Dict subjectDict, + final Dict dict, + final List tos) { + if (ObjectUtil.isNotNull(tos)) { + Map> tosGroupByLang = + tos.stream() + .peek( + to -> { + if (ObjectUtil.isNull(to.getLocale())) { + to.setLocale(StrUtil.EMPTY); + } + }) + .collect(Collectors.groupingBy(MailWithLang::getLocale)); + tosGroupByLang.forEach( + (lang, mailWithLanguages) -> { + final List emails = + mailWithLanguages.stream() + .map(MailWithLang::getTo).collect(Collectors.toList()); + sendMail(lang, subjectType, subjectDict, dict, emails); + }); } - - public void sendMail(String subjectType, Dict subjectDict, Dict dict, List tos) { - if (ObjectUtil.isNotNull(tos)) { - Map> tosGroupByLang = tos.stream() - .peek(to -> { - if (ObjectUtil.isNull(to.getLocale())) { - to.setLocale(StrUtil.EMPTY); - } - }) - .collect(Collectors.groupingBy(MailWithLang::getLocale)); - tosGroupByLang.forEach((lang, mailWithLanguages) -> { - final List emails = mailWithLanguages.stream() - .map(MailWithLang::getTo) - .collect(Collectors.toList()); - sendMail(lang, subjectType, subjectDict, dict, emails); - }); - } + } + + /** + * @param lang lang + * @param subjectType subjectType + * @param dict dict + * @param to to + */ + public void sendMail( + final String lang, + final String subjectType, + final Dict dict, + final List to + ) { + this.sendMail(lang, subjectType, null, dict, to); + } + + /** + * @param language language + * @param subjectType subjectType + * @param subjectDict subjectDict + * @param dict dict + * @param to to + */ + public void sendMail( + final String language, + final String subjectType, + final Dict subjectDict, + final Dict dict, + final List to) { + MailText mailText = new MailText(subjectType, subjectDict).getTemplate(); + + String htmlTemplateName = mailText.getHtmlTemplateName(); + String textTemplateName = mailText.getTextTemplateName(); + String lang = + StrUtil.isNotBlank(language) ? language : Locale.US.toLanguageTag(); + // load subject.properties + Properties properties = loadSubjectProperties(lang); + String subject = + StrUtil.format(properties.getProperty(subjectType), subjectDict); + + if (StrUtil.hasBlank(htmlTemplateName, textTemplateName, subject)) { + log.warn("Lost parameters," + + "please check param(htmlBtl, textBtl, subject)."); + return; } - - public void sendMail(String lang, String subjectType, Dict dict, List to) { - this.sendMail(lang, subjectType, null, dict, to); + if (cloudMailSender != null) { + cloudMailSend(subject, lang, subjectType, dict, to); + return; } - - public void sendMail(String lang, String subjectType, Dict subjectDict, Dict dict, List to) { - MailText mailText = new MailText(subjectType, subjectDict).getTemplate(); - - String htmlTemplateName = mailText.getHtmlTemplateName(); - String textTemplateName = mailText.getTextTemplateName(); - if (StrUtil.isBlank(lang)) { - lang = Locale.US.toLanguageTag(); - } - // load subject.properties - Properties properties = loadSubjectProperties(lang); - String subject = StrUtil.format(properties.getProperty(subjectType), subjectDict); - - if (StrUtil.hasBlank(htmlTemplateName, textTemplateName, subject)) { - log.warn("Lost parameters,please check param(htmlBtl, textBtl, subject)."); - return; - } - if (cloudMailSender != null) { - cloudMailSend(subject, lang, subjectType, dict, to); - return; - } - if (mailTemplate == null) { - log.warn("Mail service not configured"); - return; - } - String htmlTemplatePath = loadTemplateResourcePath(lang, htmlTemplateName); - String textTemplatePath = loadTemplateResourcePath(lang, textTemplateName); - primevalMailSend(subject, beetlTemplate.render(htmlTemplatePath, dict), beetlTemplate.render(textTemplatePath, dict), to); + if (mailTemplate == null) { + log.warn("Mail service not configured"); + return; } - - private Properties loadSubjectProperties(String locale) { - final Properties properties = new Properties(); - String path = StrUtil.format("templates/notification/enterprise/{}/subject.properties", locale); - ClassPathResource resource = new ClassPathResource(path); - if (resource.exists()) { - try (InputStream in = resource.getInputStream()) { - properties.load(in); - } - catch (IOException e) { - log.error("load subject error", e); - throw new BusinessException("Fail to Send Email"); - } - } - else { - ClassPathResource defaultResource = new ClassPathResource("templates/notification/subject.properties"); - try (InputStream in = defaultResource.getInputStream()) { - properties.load(in); - } - catch (IOException e) { - log.error("load subject error", e); - throw new BusinessException("Fail to Send Email"); - } - } - return properties; + String htmlTemplatePath = loadTemplateResourcePath(lang, htmlTemplateName); + String textTemplatePath = loadTemplateResourcePath(lang, textTemplateName); + primevalMailSend( + subject, + beetlTemplate.render(htmlTemplatePath, dict), + beetlTemplate.render(textTemplatePath, dict), + to); + } + + private Properties loadSubjectProperties(final String locale) { + final Properties properties = new Properties(); + String path = StrUtil.format( + "templates/notification/enterprise/{}/subject.properties", locale); + ClassPathResource resource = new ClassPathResource(path); + if (resource.exists()) { + try (InputStream in = resource.getInputStream()) { + properties.load(in); + } catch (IOException e) { + log.error("load subject error", e); + throw new BusinessException("Fail to Send Email"); + } + } else { + ClassPathResource defaultResource = + new ClassPathResource("templates/notification/subject.properties"); + try (InputStream in = defaultResource.getInputStream()) { + properties.load(in); + } catch (IOException e) { + log.error("load subject error", e); + throw new BusinessException("Fail to Send Email"); + } } - - private String loadTemplateResourcePath(String locale, String templateName) { - String templatePath = StrUtil.format("templates/notification/enterprise/{}/{}", locale, templateName); - ClassPathResource resource = new ClassPathResource(templatePath); - if (resource.exists()) { - // load locale priority - return StrUtil.format("notification/enterprise/{}/{}", locale, templateName); - } - else { - return StrUtil.format("notification/{}", templateName); - } + return properties; + } + + private String loadTemplateResourcePath( + final String locale, final String templateName) { + String templatePath = + StrUtil.format("templates/notification/enterprise/{}/{}", + locale, templateName); + ClassPathResource resource = new ClassPathResource(templatePath); + if (resource.exists()) { + // load locale priority + return StrUtil.format("notification/enterprise/{}/{}", + locale, templateName); + } else { + return StrUtil.format("notification/{}", templateName); } - - public void notify(String subject, String textBtl) { - this.notify(emailSendProperties.getPersonal(), subject, null, null, textBtl, Collections.singletonList("devops@apitable.com")); + } + + /** + * @param subject subject + * @param textBtl textBtl + */ + public void notify(final String subject, final String textBtl) { + this.notify( + emailSendProperties.getPersonal(), + subject, + null, + null, + textBtl, + Collections.singletonList("devops@apitable.com")); + } + + /** + * * + * + * @param personal personal + * @param subject subject + * @param subjectType subjectType + * @param dict dict + * @param textBtl textBtl + * @param to to + */ + public void notify( + final String personal, + final String subject, + final String subjectType, + final Dict dict, + final String textBtl, + final List to) { + if (cloudMailSender != null) { + CloudEmailMessage message = new CloudEmailMessage(); + message.setSubject(subject); + message.setPersonal(personal); + message.setTo(to); + JSONObject obj = JSONUtil.createObj(); + if (subjectType != null) { + message.setTemplateId( + mailFacade.getCloudMailTemplateId(null, subjectType)); + obj.putAll(dict); + } else { + message.setTemplateId( + mailFacade.getCloudMailTemplateId(null, SUBJECT_WARN_NOTIFY)); + obj.putOpt("content", textBtl); + } + message.setTemplateData(obj.toString()); + cloudMailSender.send(message); + return; } - - public void notify(String personal, String subject, String subjectType, Dict dict, String textBtl, List to) { - if (cloudMailSender != null) { - CloudEmailMessage message = new CloudEmailMessage(); - message.setSubject(subject); - message.setPersonal(personal); - message.setTo(to); - JSONObject obj = JSONUtil.createObj(); - if (subjectType != null) { - message.setTemplateId(TencentMailTemplate.getTemplateId(null, subjectType)); - obj.putAll(dict); - } - else { - message.setTemplateId(TencentMailTemplate.getTemplateId(null, SUBJECT_WARN_NOTIFY)); - obj.putOpt("content", textBtl); - } - message.setTemplateData(obj.toString()); - cloudMailSender.send(message); - return; - } - if (mailTemplate == null) { - log.info("Mail service not configured"); - return; - } - EmailMessage emailMessage = new EmailMessage(); - emailMessage.setPersonal(personal); - emailMessage.setSubject(subject); - emailMessage.setTo(to); - if (subjectType != null) { - return; - } - if (textBtl != null) { - emailMessage.setPlainText(textBtl); - } - mailTemplate.send(emailMessage); + if (mailTemplate == null) { + log.info("Mail service not configured"); + return; } - - private void cloudMailSend(String subject, String lang, String subjectType, Dict dict, List to) { - CloudEmailMessage message = new CloudEmailMessage(); - message.setSubject(subject); - message.setPersonal(emailSendProperties.getPersonal()); - message.setTo(to); - message.setTemplateId(TencentMailTemplate.getTemplateId(lang, subjectType)); - JSONObject obj = JSONUtil.createObj(); - obj.putAll(dict); - message.setTemplateData(obj.toString()); - cloudMailSender.send(message); + EmailMessage emailMessage = new EmailMessage(); + emailMessage.setPersonal(personal); + emailMessage.setSubject(subject); + emailMessage.setTo(to); + if (subjectType != null) { + return; + } + if (textBtl != null) { + emailMessage.setPlainText(textBtl); + } + mailTemplate.send(emailMessage); + } + + private void cloudMailSend( + final String subject, + final String lang, + final String subjectType, + final Dict dict, + final List to) { + CloudEmailMessage message = new CloudEmailMessage(); + message.setSubject(subject); + message.setPersonal(emailSendProperties.getPersonal()); + message.setTo(to); + message.setTemplateId(mailFacade.getCloudMailTemplateId(lang, subjectType)); + JSONObject obj = JSONUtil.createObj(); + obj.putAll(dict); + message.setTemplateData(obj.toString()); + cloudMailSender.send(message); + } + + private void primevalMailSend( + final String subject, + final String htmlBody, + final String plainText, + final List to + ) { + EmailMessage[] messages = new EmailMessage[to.size()]; + for (int i = 0; i < to.size(); i++) { + EmailMessage emailMessage = new EmailMessage(); + emailMessage.setPersonal(emailSendProperties.getPersonal()); + emailMessage.setSubject(subject); + emailMessage.setTo(Collections.singletonList(to.get(i))); + emailMessage.setPlainText(plainText); + emailMessage.setHtmlText(htmlBody); + messages[i] = emailMessage; + } + mailTemplate.send(messages); + } + + @Data + @NoArgsConstructor + public static class MailWithLang { + /** * */ + private String locale; + + /** * */ + private String to; + + /** + * + * @param targetLocale targetLocale + * @param email email + */ + public MailWithLang(final String targetLocale, final String email) { + this.locale = targetLocale; + this.to = email; } - private void primevalMailSend(String subject, String htmlBody, String plainText, List to) { - EmailMessage[] messages = new EmailMessage[to.size()]; - for (int i = 0; i < to.size(); i++) { - EmailMessage emailMessage = new EmailMessage(); - emailMessage.setPersonal(emailSendProperties.getPersonal()); - emailMessage.setSubject(subject); - emailMessage.setTo(Collections.singletonList(to.get(i))); - emailMessage.setPlainText(plainText); - emailMessage.setHtmlText(htmlBody); - messages[i] = emailMessage; - } - mailTemplate.send(messages); + /** + * * + * + * @param data data + * @param mapper mapper + * @param T + * @return List + */ + public static List convert( + final List data, + final Function mapper + ) { + return Optional.ofNullable(data).orElseGet(ArrayList::new).stream() + .map(mapper) + .collect(toList()); } + } + + static final class MailText { - @Data - @NoArgsConstructor - public static class MailWithLang { - private String locale; + /** * */ + @Getter private String htmlTemplateName; - private String to; + /** * */ + @Getter private String textTemplateName; - public MailWithLang(String locale, String to) { - this.locale = locale; - this.to = to; - } + /** * */ + @Getter private final String subjectType; - public static List convert(List data, Function mapper) { - return Optional.ofNullable(data).orElseGet(ArrayList::new).stream().map(mapper).collect(toList()); - } + /** * */ + @Getter private final Dict subjectDict; + MailText(final String type, final Dict dict) { + this.subjectType = type; + this.subjectDict = dict; } - static final class MailText { - - @Getter - String htmlTemplateName; - - @Getter - String textTemplateName; - - @Getter - String subject; - - String subjectType; - - Dict subjectDict; - - public MailText(String subjectType, Dict subjectDict) { - this.subjectType = subjectType; - this.subjectDict = subjectDict; - } - - public MailText getTemplate() { - // switch email template - switch (this.subjectType) { - case SUBJECT_INVITE_NOTIFY: - this.htmlTemplateName = "invite-email-html.btl"; - this.textTemplateName = "invite-email-text.btl"; - break; - case SUBJECT_REMOVE_MEMBER: - this.htmlTemplateName = "remove-member-html.btl"; - this.textTemplateName = "remove-member-text.btl"; - break; - case SUBJECT_DATASHEET_REMIND: - this.htmlTemplateName = "remind-member-html.btl"; - this.textTemplateName = "remind-member-text.btl"; - break; - case SUBJECT_SPACE_APPLY: - this.htmlTemplateName = "space-apply-html.btl"; - this.textTemplateName = "space-apply-text.btl"; - break; - case SUBJECT_RECORD_COMMENT: - this.htmlTemplateName = "remind-comment-html.btl"; - this.textTemplateName = "remind-comment-text.btl"; - break; - case SUBJECT_WIDGET_UNPUBLISH_NOTIFY: - this.htmlTemplateName = "widget-unpublish-notify-html.btl"; - this.textTemplateName = "widget-unpublish-notify-text.btl"; - break; - case SUBJECT_WIDGET_UNPUBLISH_GLOBAL_NOTIFY: - this.htmlTemplateName = "widget-unpublish-global-notify-html.btl"; - this.textTemplateName = "widget-unpublish-global-notify-text.btl"; - break; - case SUBJECT_WIDGET_TRANSFER_NOTIFY: - this.htmlTemplateName = "widget-transfer-notify-html.btl"; - this.textTemplateName = "widget-transfer-notify-text.btl"; - break; - case SUBJECT_CHANGE_ADMIN: - this.htmlTemplateName = "admin-notify-html.btl"; - this.textTemplateName = "admin-notify-text.btl"; - break; - case SUBJECT_VERIFY_CODE: - this.htmlTemplateName = "verification-code-html.btl"; - this.textTemplateName = "verification-code-text.btl"; - break; - case SUBJECT_REGISTER: - this.htmlTemplateName = "register-email-html.btl"; - this.textTemplateName = "register-email-text.btl"; - break; - case SUBJECT_CAPACITY_FULL: - this.htmlTemplateName = "capacity-full-html.btl"; - this.textTemplateName = "capacity-full-text.btl"; - break; - case SUBJECT_PAI_SUCCESS: - this.htmlTemplateName = "pay-success-html.btl"; - this.textTemplateName = "pay-success-text.btl"; - break; - case SUBJECT_ADD_RECORD_SOON_LIMITED: - this.htmlTemplateName = "add-record-reaching-limited-html.btl"; - this.textTemplateName = "add-record-reaching-limited-text.btl"; - break; - case SUBJECT_ADD_RECORD_LIMITED: - this.htmlTemplateName = "add-record-reached-limited-html.btl"; - this.textTemplateName = "add-record-reached-limited-text.btl"; - break; - case SUBJECT_WIDGET_SUBMIT_SUCCESS: - this.htmlTemplateName = "widget-submit-success-html.btl"; - this.textTemplateName = "widget-submit-success-text.btl"; - break; - case SUBJECT_WIDGET_SUBMIT_FAIL: - this.htmlTemplateName = "widget-submit-fail-html.btl"; - this.textTemplateName = "widget-submit-fail-text.btl"; - break; - case SUBJECT_WIDGET_QUALIFICATION_AUTH_SUCCESS: - this.htmlTemplateName = "widget-qualification-auth-success-html.btl"; - this.textTemplateName = "widget-qualification-auth-success-text.btl"; - break; - case SUBJECT_WIDGET_QUALIFICATION_AUTH_FAIL: - this.htmlTemplateName = "widget-qualification-auth-fail-html.btl"; - this.textTemplateName = "widget-qualification-auth-fail-text.btl"; - break; - case SUBJECT_TASK_REMINDER: - htmlTemplateName = "task-reminder-html.btl"; - textTemplateName = "task-reminder-text.btl"; - break; - case SUBJECT_SUBSCRIBED_RECORD_CELL_UPDATED: - htmlTemplateName = "subscribed-record-cell-updated-html.btl"; - textTemplateName = "subscribed-record-cell-updated-text.btl"; - break; - case SUBJECT_SUBSCRIBED_RECORD_COMMENTED: - htmlTemplateName = "subscribed-record-commented-html.btl"; - textTemplateName = "subscribed-record-commented-text.btl"; - break; - case SUBJECT_SUBSCRIBED_DATASHEET_LIMIT: - htmlTemplateName = "subscribed-datasheet-limit-html.btl"; - textTemplateName = "subscribed-datasheet-limit-text.btl"; - break; - case SUBJECT_SUBSCRIBED_DATASHEET_RECORD_LIMIT: - htmlTemplateName = "subscribed-datasheet-record-html.btl"; - textTemplateName = "subscribed-datasheet-record-text.btl"; - break; - case SUBJECT_SUBSCRIBED_CAPACITY_LIMIT: - htmlTemplateName = "subscribed-capacity-html.btl"; - textTemplateName = "subscribed-capacity-text.btl"; - break; - case SUBJECT_SUBSCRIBED_SEATS_LIMIT: - htmlTemplateName = "subscribed-seats-limit-html.btl"; - textTemplateName = "subscribed-seats-limit-text.btl"; - break; - case SUBJECT_SUBSCRIBED_RECORD_LIMIT: - htmlTemplateName = "subscribed-record-limit-html.btl"; - textTemplateName = "subscribed-record-limit-text.btl"; - break; - case SUBJECT_SUBSCRIBED_API_LIMIT: - htmlTemplateName = "subscribed-api-limit-html.btl"; - textTemplateName = "subscribed-api-limit-text.btl"; - break; - case SUBJECT_SUBSCRIBED_CALENDAR_LIMIT: - htmlTemplateName = "subscribed-calendar-limit-html.btl"; - textTemplateName = "subscribed-calendar-limit-text.btl"; - break; - case SUBJECT_SUBSCRIBED_FORM_LIMIT: - htmlTemplateName = "subscribed-form-limit-html.btl"; - textTemplateName = "subscribed-form-limit-text.btl"; - break; - case SUBJECT_SUBSCRIBED_MIRROR_LIMIT: - htmlTemplateName = "subscribed-mirror-limit-html.btl"; - textTemplateName = "subscribed-mirror-limit-text.btl"; - break; - case SUBJECT_SUBSCRIBED_GANNT_LIMIT: - htmlTemplateName = "subscribed-gannt-limit-html.btl"; - textTemplateName = "subscribed-gannt-limit-text.btl"; - break; - case SUBJECT_SUBSCRIBED_FIELD_PERMISSION_LIMIT: - htmlTemplateName = "subscribed-field-permission-limit-html.btl"; - textTemplateName = "subscribed-field-permission-limit-text.btl"; - break; - case SUBJECT_SUBSCRIBED_FILE_PERMISSION_LIMIT: - htmlTemplateName = "subscribed-file-permission-limit-html.btl"; - textTemplateName = "subscribed-file-permission-limit-text.btl"; - break; - case SUBJECT_SUBSCRIBED_ADMIN_LIMIT: - htmlTemplateName = "subscribed-admin-limit-html.btl"; - textTemplateName = "subscribed-admin-limit-text.btl"; - break; - default: - break; - } - return this; - } + public MailText getTemplate() { + // switch email template + switch (this.subjectType) { + case SUBJECT_INVITE_NOTIFY: + this.htmlTemplateName = "invite-email-html.btl"; + this.textTemplateName = "invite-email-text.btl"; + break; + case SUBJECT_REMOVE_MEMBER: + this.htmlTemplateName = "remove-member-html.btl"; + this.textTemplateName = "remove-member-text.btl"; + break; + case SUBJECT_DATASHEET_REMIND: + this.htmlTemplateName = "remind-member-html.btl"; + this.textTemplateName = "remind-member-text.btl"; + break; + case SUBJECT_SPACE_APPLY: + this.htmlTemplateName = "space-apply-html.btl"; + this.textTemplateName = "space-apply-text.btl"; + break; + case SUBJECT_RECORD_COMMENT: + this.htmlTemplateName = "remind-comment-html.btl"; + this.textTemplateName = "remind-comment-text.btl"; + break; + case SUBJECT_WIDGET_UNPUBLISH_NOTIFY: + this.htmlTemplateName = "widget-unpublish-notify-html.btl"; + this.textTemplateName = "widget-unpublish-notify-text.btl"; + break; + case SUBJECT_WIDGET_UNPUBLISH_GLOBAL_NOTIFY: + this.htmlTemplateName = "widget-unpublish-global-notify-html.btl"; + this.textTemplateName = "widget-unpublish-global-notify-text.btl"; + break; + case SUBJECT_WIDGET_TRANSFER_NOTIFY: + this.htmlTemplateName = "widget-transfer-notify-html.btl"; + this.textTemplateName = "widget-transfer-notify-text.btl"; + break; + case SUBJECT_CHANGE_ADMIN: + this.htmlTemplateName = "admin-notify-html.btl"; + this.textTemplateName = "admin-notify-text.btl"; + break; + case SUBJECT_VERIFY_CODE: + this.htmlTemplateName = "verification-code-html.btl"; + this.textTemplateName = "verification-code-text.btl"; + break; + case SUBJECT_REGISTER: + this.htmlTemplateName = "register-email-html.btl"; + this.textTemplateName = "register-email-text.btl"; + break; + case SUBJECT_CAPACITY_FULL: + this.htmlTemplateName = "capacity-full-html.btl"; + this.textTemplateName = "capacity-full-text.btl"; + break; + case SUBJECT_PAI_SUCCESS: + this.htmlTemplateName = "pay-success-html.btl"; + this.textTemplateName = "pay-success-text.btl"; + break; + case SUBJECT_ADD_RECORD_SOON_LIMITED: + this.htmlTemplateName = "add-record-reaching-limited-html.btl"; + this.textTemplateName = "add-record-reaching-limited-text.btl"; + break; + case SUBJECT_ADD_RECORD_LIMITED: + this.htmlTemplateName = "add-record-reached-limited-html.btl"; + this.textTemplateName = "add-record-reached-limited-text.btl"; + break; + case SUBJECT_WIDGET_SUBMIT_SUCCESS: + this.htmlTemplateName = "widget-submit-success-html.btl"; + this.textTemplateName = "widget-submit-success-text.btl"; + break; + case SUBJECT_WIDGET_SUBMIT_FAIL: + this.htmlTemplateName = "widget-submit-fail-html.btl"; + this.textTemplateName = "widget-submit-fail-text.btl"; + break; + case SUBJECT_WIDGET_QUALIFICATION_AUTH_SUCCESS: + this.htmlTemplateName = "widget-qualification-auth-success-html.btl"; + this.textTemplateName = "widget-qualification-auth-success-text.btl"; + break; + case SUBJECT_WIDGET_QUALIFICATION_AUTH_FAIL: + this.htmlTemplateName = "widget-qualification-auth-fail-html.btl"; + this.textTemplateName = "widget-qualification-auth-fail-text.btl"; + break; + case SUBJECT_TASK_REMINDER: + htmlTemplateName = "task-reminder-html.btl"; + textTemplateName = "task-reminder-text.btl"; + break; + case SUBJECT_SUBSCRIBED_RECORD_CELL_UPDATED: + htmlTemplateName = "subscribed-record-cell-updated-html.btl"; + textTemplateName = "subscribed-record-cell-updated-text.btl"; + break; + case SUBJECT_SUBSCRIBED_RECORD_COMMENTED: + htmlTemplateName = "subscribed-record-commented-html.btl"; + textTemplateName = "subscribed-record-commented-text.btl"; + break; + case SUBJECT_SUBSCRIBED_DATASHEET_LIMIT: + htmlTemplateName = "subscribed-datasheet-limit-html.btl"; + textTemplateName = "subscribed-datasheet-limit-text.btl"; + break; + case SUBJECT_SUBSCRIBED_DATASHEET_RECORD_LIMIT: + htmlTemplateName = "subscribed-datasheet-record-html.btl"; + textTemplateName = "subscribed-datasheet-record-text.btl"; + break; + case SUBJECT_SUBSCRIBED_CAPACITY_LIMIT: + htmlTemplateName = "subscribed-capacity-html.btl"; + textTemplateName = "subscribed-capacity-text.btl"; + break; + case SUBJECT_SUBSCRIBED_SEATS_LIMIT: + htmlTemplateName = "subscribed-seats-limit-html.btl"; + textTemplateName = "subscribed-seats-limit-text.btl"; + break; + case SUBJECT_SUBSCRIBED_RECORD_LIMIT: + htmlTemplateName = "subscribed-record-limit-html.btl"; + textTemplateName = "subscribed-record-limit-text.btl"; + break; + case SUBJECT_SUBSCRIBED_API_LIMIT: + htmlTemplateName = "subscribed-api-limit-html.btl"; + textTemplateName = "subscribed-api-limit-text.btl"; + break; + case SUBJECT_SUBSCRIBED_CALENDAR_LIMIT: + htmlTemplateName = "subscribed-calendar-limit-html.btl"; + textTemplateName = "subscribed-calendar-limit-text.btl"; + break; + case SUBJECT_SUBSCRIBED_FORM_LIMIT: + htmlTemplateName = "subscribed-form-limit-html.btl"; + textTemplateName = "subscribed-form-limit-text.btl"; + break; + case SUBJECT_SUBSCRIBED_MIRROR_LIMIT: + htmlTemplateName = "subscribed-mirror-limit-html.btl"; + textTemplateName = "subscribed-mirror-limit-text.btl"; + break; + case SUBJECT_SUBSCRIBED_GANNT_LIMIT: + htmlTemplateName = "subscribed-gannt-limit-html.btl"; + textTemplateName = "subscribed-gannt-limit-text.btl"; + break; + case SUBJECT_SUBSCRIBED_FIELD_PERMISSION_LIMIT: + htmlTemplateName = "subscribed-field-permission-limit-html.btl"; + textTemplateName = "subscribed-field-permission-limit-text.btl"; + break; + case SUBJECT_SUBSCRIBED_FILE_PERMISSION_LIMIT: + htmlTemplateName = "subscribed-file-permission-limit-html.btl"; + textTemplateName = "subscribed-file-permission-limit-text.btl"; + break; + case SUBJECT_SUBSCRIBED_ADMIN_LIMIT: + htmlTemplateName = "subscribed-admin-limit-html.btl"; + textTemplateName = "subscribed-admin-limit-text.btl"; + break; + default: + break; + } + return this; } + } } diff --git a/backend-server/application/src/main/java/com/apitable/shared/component/notification/observer/AbstractNotifyObserver.java b/backend-server/application/src/main/java/com/apitable/shared/component/notification/observer/AbstractNotifyObserver.java index ab9e640ac68bc4b0a3930e69fb2d8a84d64b0c4c..527b106e05bd8fa865df3ae91e03805bc88331d2 100644 --- a/backend-server/application/src/main/java/com/apitable/shared/component/notification/observer/AbstractNotifyObserver.java +++ b/backend-server/application/src/main/java/com/apitable/shared/component/notification/observer/AbstractNotifyObserver.java @@ -18,33 +18,29 @@ package com.apitable.shared.component.notification.observer; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -import javax.annotation.Resource; - import cn.hutool.core.date.DatePattern; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; - -import com.apitable.shared.component.notification.NotificationHelper; -import com.apitable.player.ro.NotificationCreateRo; import com.apitable.organization.service.IMemberService; +import com.apitable.player.ro.NotificationCreateRo; +import com.apitable.shared.component.notification.NotificationHelper; +import com.apitable.shared.sysconfig.i18n.I18nStringsUtil; import com.apitable.space.service.ISpaceService; import com.apitable.workspace.service.INodeService; -import com.apitable.shared.sysconfig.i18n.I18nStringsUtil; + +import javax.annotation.Resource; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; import static cn.hutool.core.date.DatePattern.NORM_DATETIME_MINUTE_PATTERN; -import static com.apitable.shared.constants.NotificationConstants.EMAIL_MEMBER_NAME; -import static com.apitable.shared.constants.NotificationConstants.EMAIL_RECORD_ID; -import static com.apitable.shared.constants.NotificationConstants.INVOLVE_RECORD_IDS; +import static com.apitable.shared.constants.NotificationConstants.*; /** *

diff --git a/backend-server/application/src/main/java/com/apitable/shared/component/notification/package-info.java b/backend-server/application/src/main/java/com/apitable/shared/component/notification/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..ef7bebba0e21b7f5567348b7d2db46970ddf1caf --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/shared/component/notification/package-info.java @@ -0,0 +1,23 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/** + * + * @author Chambers + */ +package com.apitable.shared.component.notification; diff --git a/backend-server/application/src/main/java/com/apitable/shared/component/scanner/ApiResourceScanner.java b/backend-server/application/src/main/java/com/apitable/shared/component/scanner/ApiResourceScanner.java index 99541db9397552d42555a2c6b868dd84f69ebdd2..d065b40d9d816df42e19680603fc9d00285824da 100644 --- a/backend-server/application/src/main/java/com/apitable/shared/component/scanner/ApiResourceScanner.java +++ b/backend-server/application/src/main/java/com/apitable/shared/component/scanner/ApiResourceScanner.java @@ -25,16 +25,18 @@ import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; +import javax.annotation.Resource; + import cn.hutool.core.net.NetUtil; import cn.hutool.core.util.StrUtil; -import lombok.extern.slf4j.Slf4j; - +import com.apitable.shared.component.ResourceDefinition; +import com.apitable.shared.component.SystemEnvironmentVariable; import com.apitable.shared.component.scanner.annotation.ApiResource; import com.apitable.shared.component.scanner.annotation.GetResource; import com.apitable.shared.component.scanner.annotation.PostResource; -import com.apitable.shared.component.ResourceDefinition; -import com.apitable.shared.util.IgnorePathHelper; import com.apitable.shared.util.AopTargetUtils; +import com.apitable.shared.util.IgnorePathHelper; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; @@ -47,228 +49,278 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; /** - *

- * api source scanner - *

+ * api source scanner. * * @author Shawn Deng */ @Slf4j @Component -public class ApiResourceScanner implements BeanPostProcessor, Ordered, ApplicationContextAware { - - /** - * string connector - */ - private static final String LINK_SYMBOL = "$"; - - private ApplicationContext applicationContext; - - private ApiResourceFactory apiResourceFactory; - - public ApiResourceFactory getApiResourceFactory() { - if (apiResourceFactory == null) { - apiResourceFactory = applicationContext.getBean(ApiResourceFactory.class); - } - return apiResourceFactory; - } - - @Override - public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException { - return bean; - } - - @Override - public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException { - Object aopTarget = AopTargetUtils.getTarget(bean); +public class ApiResourceScanner + implements BeanPostProcessor, Ordered, ApplicationContextAware { - if (aopTarget == null) { - aopTarget = bean; - } + /** string connector. */ + private static final String LINK_SYMBOL = "$"; - final Class clazz = aopTarget.getClass(); + /** minuend. */ + private static final int MINUEND = 11; - final boolean controllerFlag = checkControllerFlag(clazz); - if (!controllerFlag) { - return bean; - } + /** */ + private ApplicationContext applicationContext; - final List apiResourceDefinitions = doScan(clazz); + /** */ + private ApiResourceFactory apiResourceFactory; - persistApiResources(apiResourceDefinitions); + /** */ + @Resource private SystemEnvironmentVariable systemEnvironmentVariable; - return bean; + /** + * GetApiResourceFactory. + * + * @return ApiResourceFactory + */ + public ApiResourceFactory getApiResourceFactory() { + if (apiResourceFactory == null) { + apiResourceFactory = + applicationContext.getBean(ApiResourceFactory.class); } - - @Override - public int getOrder() { - return Ordered.LOWEST_PRECEDENCE - 11; + return apiResourceFactory; + } + + /** * */ + @Override + public Object postProcessBeforeInitialization( + final Object bean, final String beanName + ) throws BeansException { + return bean; + } + + /** * */ + @Override + public Object postProcessAfterInitialization( + final Object bean, final String beanName + ) throws BeansException { + if (systemEnvironmentVariable.isTestEnabled()) { + return bean; } + Object aopTarget = AopTargetUtils.getTarget(bean); - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - this.applicationContext = applicationContext; + if (aopTarget == null) { + aopTarget = bean; } - /** - * whether class is controller - * @param clazz class - * @return true | false - */ - private boolean checkControllerFlag(final Class clazz) { - final Annotation[] annotations = clazz.getAnnotations(); - - for (final Annotation annotation : annotations) { - if (RestController.class.equals(annotation.annotationType()) - || Controller.class.equals(annotation.annotationType())) { - return true; - } - } - return false; - } + final Class clazz = aopTarget.getClass(); - /** - * save api resource - * @param apiResources api resource list - */ - private void persistApiResources(final List apiResources) { - getApiResourceFactory().registerDefinition(apiResources); + final boolean controllerFlag = checkControllerFlag(clazz); + if (!controllerFlag) { + return bean; } - /** - * scan bean - * @param clazz class - * @return resource list - */ - private List doScan(final Class clazz) { - final ArrayList apiResources = new ArrayList<>(); - final ApiResource classApiAnnotation = clazz.getAnnotation(ApiResource.class); - if (classApiAnnotation != null && !classApiAnnotation.ignore()) { - final Method[] declaredMethods = clazz.getDeclaredMethods(); - if (declaredMethods.length > 0) { - for (final Method declaredMethod : declaredMethods) { - final Annotation annotation = this.getOnMethod(declaredMethod); - if (annotation != null) { - final ResourceDefinition definition = createDefinition(clazz, declaredMethod, annotation); - apiResources.add(definition); - } - } - } - } - - return apiResources; + final List apiResourceDefinitions = doScan(clazz); + + persistApiResources(apiResourceDefinitions); + + return bean; + } + + /** + * * + * + * @return order + */ + @Override + public int getOrder() { + return Ordered.LOWEST_PRECEDENCE - MINUEND; + } + + /** + * * + * + * @param context applicationContext + */ + @Override + public void setApplicationContext(final ApplicationContext context) + throws BeansException { + this.applicationContext = context; + } + + /** + * whether class is controller. + * + * @param clazz class + * @return true | false + */ + private boolean checkControllerFlag(final Class clazz) { + final Annotation[] annotations = clazz.getAnnotations(); + + for (final Annotation annotation : annotations) { + if (RestController.class.equals(annotation.annotationType()) + || Controller.class.equals(annotation.annotationType())) { + return true; + } } - - private ResourceDefinition createDefinition(final Class clazz, final Method method, - final Annotation apiResource) { - final ResourceDefinition resourceDefinition = new ResourceDefinition(); - resourceDefinition.setClassName(clazz.getSimpleName()); - resourceDefinition.setMethodName(method.getName()); - - String modularCode; - final ApiResource classApiAnnotation = clazz.getAnnotation(ApiResource.class); - if (StrUtil.isEmpty(classApiAnnotation.code())) { - final String className = clazz.getSimpleName(); - modularCode = getControllerClassPrefix(className); - } - else { - modularCode = classApiAnnotation.code(); - } - resourceDefinition.setModularCode(modularCode); - resourceDefinition.setModularName(classApiAnnotation.name()); - - final String resourceCode = invokeAnnotationMethod(apiResource, "code"); - if (StrUtil.isEmpty(resourceCode)) { - final String definitionCode = StrUtil.join(LINK_SYMBOL, StrUtil.toUnderlineCase(modularCode), - StrUtil.toUnderlineCase(method.getName())); - resourceDefinition.setResourceCode(definitionCode); - } - else { - resourceDefinition.setResourceCode(StrUtil.join(LINK_SYMBOL, modularCode, resourceCode)); - } - - final String name = invokeAnnotationMethod(apiResource, "name"); - resourceDefinition.setResourceName(name); - final String[] path = invokeAnnotationMethod(apiResource, "path"); - resourceDefinition.setResourceUrl(getControllerClassRequestPath(clazz) + path[0]); - final Boolean requiredLogin = invokeAnnotationMethod(apiResource, "requiredLogin"); - resourceDefinition.setRequiredLogin(requiredLogin); - if (!requiredLogin) { - IgnorePathHelper.getInstant().add(resourceDefinition.getResourceUrl()); + return false; + } + + /** + * save api resource. + * + * @param apiResources api resource list + */ + private void persistApiResources( + final List apiResources) { + getApiResourceFactory().registerDefinition(apiResources); + } + + /** + * scan bean. + * + * @param clazz class + * @return resource list + */ + private List doScan(final Class clazz) { + final ArrayList apiResources = new ArrayList<>(); + final ApiResource classApiAnnotation = + clazz.getAnnotation(ApiResource.class); + if (classApiAnnotation != null && !classApiAnnotation.ignore()) { + final Method[] declaredMethods = clazz.getDeclaredMethods(); + if (declaredMethods.length > 0) { + for (final Method declaredMethod : declaredMethods) { + final Annotation annotation = this.getOnMethod(declaredMethod); + if (annotation != null) { + final ResourceDefinition definition = + createDefinition(clazz, declaredMethod, annotation); + apiResources.add(definition); + } } - final Boolean requiredPermission = invokeAnnotationMethod(apiResource, "requiredPermission"); - resourceDefinition.setRequiredPermission(requiredPermission); - final String[] tags = invokeAnnotationMethod(apiResource, "tags"); - resourceDefinition.setTags(tags); - final Boolean requiredAccessDomain = invokeAnnotationMethod(apiResource, "requiredAccessDomain"); - resourceDefinition.setRequiredAccessDomain(requiredAccessDomain); - - final RequestMethod[] requestMethods = invokeAnnotationMethod(apiResource, "method"); - final List methodNames = new ArrayList<>(); - for (final RequestMethod requestMethod : requestMethods) { - methodNames.add(requestMethod.name()); - } - resourceDefinition.setHttpMethod(StrUtil.join(",", methodNames)); - - final String localMacAddress = NetUtil.getLocalhostStr(); - resourceDefinition.setIpAddress(localMacAddress == null ? "" : localMacAddress); - resourceDefinition.setCreateTime(LocalDateTime.now()); - return resourceDefinition; + } } - @SuppressWarnings("unchecked") - private T invokeAnnotationMethod(final Annotation apiResource, final String methodName) { - try { - final Class annotationType = apiResource.annotationType(); - final Method method = annotationType.getMethod(methodName); - return (T) method.invoke(apiResource); - } - catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - log.error("fail to scan api resources!", e); - throw new RuntimeException("fail to scan api resources!", e); - } + return apiResources; + } + + private ResourceDefinition createDefinition( + final Class clazz, final Method method, final Annotation apiResource + ) { + final ResourceDefinition resourceDefinition = new ResourceDefinition(); + resourceDefinition.setClassName(clazz.getSimpleName()); + resourceDefinition.setMethodName(method.getName()); + + String modularCode; + final ApiResource classApiAnnotation = + clazz.getAnnotation(ApiResource.class); + if (StrUtil.isEmpty(classApiAnnotation.code())) { + final String className = clazz.getSimpleName(); + modularCode = getControllerClassPrefix(className); + } else { + modularCode = classApiAnnotation.code(); } - - private String getControllerClassRequestPath(final Class clazz) { - String result = ""; - - final ApiResource controllerRequestMapping = clazz.getDeclaredAnnotation(ApiResource.class); - if (controllerRequestMapping != null) { - final String[] paths = controllerRequestMapping.path(); - if (paths.length > 0) { - result = paths[0]; - } - else { - result = ""; - } - } - - return result; + resourceDefinition.setModularCode(modularCode); + resourceDefinition.setModularName(classApiAnnotation.name()); + + final String resourceCode = invokeAnnotationMethod(apiResource, "code"); + if (StrUtil.isEmpty(resourceCode)) { + final String definitionCode = + StrUtil.join( + LINK_SYMBOL, + StrUtil.toUnderlineCase(modularCode), + StrUtil.toUnderlineCase(method.getName())); + resourceDefinition.setResourceCode(definitionCode); + } else { + resourceDefinition.setResourceCode( + StrUtil.join(LINK_SYMBOL, modularCode, resourceCode)); } - private String getControllerClassPrefix(final String className) { - final int controllerIndex = className.indexOf("Controller"); - if (controllerIndex == -1) { - throw new IllegalArgumentException("Controller naming error, should ends with Controller!"); - } - return className.substring(0, controllerIndex); + final String name = invokeAnnotationMethod(apiResource, "name"); + resourceDefinition.setResourceName(name); + final String[] path = invokeAnnotationMethod(apiResource, "path"); + resourceDefinition.setResourceUrl( + getControllerClassRequestPath(clazz) + path[0]); + final Boolean requiredLogin = + invokeAnnotationMethod(apiResource, "requiredLogin"); + resourceDefinition.setRequiredLogin(requiredLogin); + if (!requiredLogin) { + IgnorePathHelper.getInstant().add(resourceDefinition.getResourceUrl()); + } + final Boolean requiredPermission = + invokeAnnotationMethod(apiResource, "requiredPermission"); + resourceDefinition.setRequiredPermission(requiredPermission); + final String[] tags = invokeAnnotationMethod(apiResource, "tags"); + resourceDefinition.setTags(tags); + final Boolean requiredAccessDomain = + invokeAnnotationMethod(apiResource, "requiredAccessDomain"); + resourceDefinition.setRequiredAccessDomain(requiredAccessDomain); + + final RequestMethod[] requestMethods = + invokeAnnotationMethod(apiResource, "method"); + final List methodNames = new ArrayList<>(); + for (final RequestMethod requestMethod : requestMethods) { + methodNames.add(requestMethod.name()); + } + resourceDefinition.setHttpMethod(StrUtil.join(",", methodNames)); + + final String localMacAddress = NetUtil.getLocalhostStr(); + resourceDefinition.setIpAddress( + localMacAddress == null ? "" : localMacAddress); + resourceDefinition.setCreateTime(LocalDateTime.now()); + return resourceDefinition; + } + + @SuppressWarnings("unchecked") + private T invokeAnnotationMethod( + final Annotation apiResource, final String methodName) { + try { + final Class annotationType = + apiResource.annotationType(); + final Method method = annotationType.getMethod(methodName); + return (T) method.invoke(apiResource); + } catch (NoSuchMethodException | IllegalAccessException + | InvocationTargetException e) { + log.error("fail to scan api resources!", e); + throw new RuntimeException("fail to scan api resources!", e); + } + } + + private String getControllerClassRequestPath(final Class clazz) { + String result = ""; + + final ApiResource controllerRequestMapping = + clazz.getDeclaredAnnotation(ApiResource.class); + if (controllerRequestMapping != null) { + final String[] paths = controllerRequestMapping.path(); + if (paths.length > 0) { + result = paths[0]; + } else { + result = ""; + } } - private Annotation getOnMethod(final Method method) { - Annotation annotation = null; + return result; + } - final GetResource getResource = method.getAnnotation(GetResource.class); + private String getControllerClassPrefix(final String className) { + final int controllerIndex = className.indexOf("Controller"); + if (controllerIndex == -1) { + throw new IllegalArgumentException( + "Controller naming error, " + "should ends with Controller!"); + } + return className.substring(0, controllerIndex); + } - if (getResource != null && !getResource.ignore()) { - annotation = getResource; - } + private Annotation getOnMethod(final Method method) { + Annotation annotation = null; - final PostResource postResource = method.getAnnotation(PostResource.class); + final GetResource getResource = method.getAnnotation(GetResource.class); - if (postResource != null && !postResource.ignore()) { - annotation = postResource; - } + if (getResource != null && !getResource.ignore()) { + annotation = getResource; + } - return annotation; + final PostResource postResource = method.getAnnotation(PostResource.class); + + if (postResource != null && !postResource.ignore()) { + annotation = postResource; } + + return annotation; + } } diff --git a/backend-server/application/src/main/java/com/apitable/shared/component/scanner/package-info.java b/backend-server/application/src/main/java/com/apitable/shared/component/scanner/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..763f59938fef26533c69f19f04d32f789cd16bbd --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/shared/component/scanner/package-info.java @@ -0,0 +1,23 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/** + * + * @author Chambers + */ +package com.apitable.shared.component.scanner; diff --git a/backend-server/application/src/main/java/com/apitable/shared/config/RedisConfig.java b/backend-server/application/src/main/java/com/apitable/shared/config/RedisConfig.java index b5c46c276d687a0bf15eb1f557e0196b6bff1952..47d94927605b0a64a230962b9dd01a7bd0d55cb5 100644 --- a/backend-server/application/src/main/java/com/apitable/shared/config/RedisConfig.java +++ b/backend-server/application/src/main/java/com/apitable/shared/config/RedisConfig.java @@ -18,11 +18,6 @@ package com.apitable.shared.config; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; - -import cn.hutool.core.lang.Dict; -import cn.hutool.core.util.StrUtil; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.PropertyAccessor; @@ -30,16 +25,13 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; import lombok.extern.slf4j.Slf4j; -import com.apitable.core.constants.RedisConstants; - import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; -import org.springframework.data.redis.serializer.StringRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.integration.redis.util.RedisLockRegistry; -import org.springframework.lang.Nullable; /** *

@@ -65,7 +57,7 @@ public class RedisConfig { public RedisTemplate redisTemplate(RedisConnectionFactory factory) { RedisTemplate template = new RedisTemplate<>(); template.setConnectionFactory(factory); - template.setKeySerializer(new MagicalKeyStringRedisSerializer(StandardCharsets.UTF_8)); + template.setKeySerializer(RedisSerializer.string()); template.setValueSerializer(json()); template.afterPropertiesSet(); return template; @@ -75,30 +67,4 @@ public class RedisConfig { public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) { return new RedisLockRegistry(redisConnectionFactory, "apitable:concurrent"); } - - private static class MagicalKeyStringRedisSerializer extends StringRedisSerializer { - - public MagicalKeyStringRedisSerializer(Charset charset) { - super(charset); - } - - @Override - public String deserialize(@Nullable byte[] bytes) { - return super.deserialize(bytes); - } - - @Override - public byte[] serialize(@Nullable String string) { - // replace magic value - string = StrUtil.format(string, this.getAllMagicalValue()); - return super.serialize(string); - } - - private Dict getAllMagicalValue() { - String env = System.getenv("ENV"); - log.info("Magical Key String Redis Serializer Load Environment: {}", env); - return Dict.create().set(RedisConstants.REDIS_ENV, env); - } - - } } diff --git a/backend-server/application/src/main/java/com/apitable/shared/config/ServerConfig.java b/backend-server/application/src/main/java/com/apitable/shared/config/ServerConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..e94275aa85c9aef60fc4abbb168247fbbd4f88ef --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/shared/config/ServerConfig.java @@ -0,0 +1,43 @@ +package com.apitable.shared.config; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.util.Arrays; +import java.util.TimeZone; + +/** + * server config + * + * @author Shawn Deng + */ +@Component +@Slf4j +public class ServerConfig implements InitializingBean { + + @Value("${DEFAULT_TIME_ZONE:UTC}") + private String timeZone; + + public ZoneId getTimeZoneId() { + return ZoneId.of(timeZone); + } + + public ZoneOffset getTimeZone() { + return getTimeZoneId().getRules().getOffset(Instant.now()); + } + + @Override + public void afterPropertiesSet() { + String[] availableIDs = TimeZone.getAvailableIDs(); + boolean validate = Arrays.stream(availableIDs).anyMatch(zoneId -> zoneId.equals(timeZone)); + if (!validate) { + throw new IllegalArgumentException("Invalid TimeZone Input : " + timeZone); + } + log.info("Server Default Region TimeZone: " + timeZone); + } +} diff --git a/backend-server/application/src/main/java/com/apitable/shared/config/SleuthConfig.java b/backend-server/application/src/main/java/com/apitable/shared/config/SleuthConfig.java index 47d4b4ebb6839d8219c40132227b867bd4f6f101..d20aa677b33479ccbb977ea95e4c8ce0a7954cce 100644 --- a/backend-server/application/src/main/java/com/apitable/shared/config/SleuthConfig.java +++ b/backend-server/application/src/main/java/com/apitable/shared/config/SleuthConfig.java @@ -18,8 +18,7 @@ package com.apitable.shared.config; -import brave.http.HttpRequestParser; - +import org.springframework.cloud.sleuth.http.HttpRequestParser; import org.springframework.cloud.sleuth.instrument.web.HttpClientRequestParser; import org.springframework.cloud.sleuth.instrument.web.HttpServerRequestParser; import org.springframework.context.annotation.Bean; @@ -32,11 +31,10 @@ import org.springframework.context.annotation.Configuration; @Configuration(proxyBeanMethods = false) public class SleuthConfig { - @Bean(name = { HttpClientRequestParser.NAME, HttpServerRequestParser.NAME}) + @Bean(name = { HttpClientRequestParser.NAME, HttpServerRequestParser.NAME }) HttpRequestParser sleuthHttpServerRequestParser() { - return (req, context, span) -> { - HttpRequestParser.DEFAULT.parse(req, context, span); - String url = req.url(); + return (request, context, span) -> { + String url = request.url(); if (url != null) { span.tag("http.url", url); } diff --git a/backend-server/application/src/main/java/com/apitable/shared/config/properties/ConstProperties.java b/backend-server/application/src/main/java/com/apitable/shared/config/properties/ConstProperties.java index dd128adc4a2770167e844513e426ed06c59e31aa..430179e9b03a884c9928cacce8361c1ffa068861 100644 --- a/backend-server/application/src/main/java/com/apitable/shared/config/properties/ConstProperties.java +++ b/backend-server/application/src/main/java/com/apitable/shared/config/properties/ConstProperties.java @@ -31,9 +31,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import static com.apitable.shared.config.properties.ConstProperties.PREFIX_CONST; /** - *

- * server constants properties - *

+ * server constants properties. * * @author Chambers */ @@ -41,100 +39,83 @@ import static com.apitable.shared.config.properties.ConstProperties.PREFIX_CONST @ConfigurationProperties(prefix = PREFIX_CONST) public class ConstProperties { + /** */ public static final String PREFIX_CONST = "const"; + /** */ private String languageTag = "zh-CN"; + /** */ private String serverDomain; + /** */ private String callbackDomain; + /** */ private String workbenchUrl = "/workbench"; /** - * Whether to create a picture audit record + * Whether to create a picture audit record. */ private boolean ossImageAuditCreatable = false; /** - * OSS bucket configuration + * OSS bucket configuration. */ private Map ossBuckets; /** - * api document avoid validate token - */ - private String loginToken = "BornForFuture"; - - /** - * Official default avatar list - * @deprecated open-source - */ - @Deprecated - private String defaultAvatarList; - - /** - * Template space, the templates created in this space will become official templates, - * and there is no upper limit for the number of templates + * Template space, the templates created in this space + * * will become official templates, + * and there is no upper limit for the number of templates. */ private String templateSpace = ""; /** - * Template ID referenced by new space by default + * Template ID referenced by new space by default. */ private String quoteTemplateId = "tpll8mltwrZMT"; /** - * English template ID referenced by default in new space + * English template ID referenced by default in new space. */ private String quoteEnTemplateId = "tpll8mltwrZMT"; /** - * wechat-mp reply template id - * @deprecated open-source - */ - @Deprecated - private String qrCodeReplyId; - - /** - * dingtalk subscription information table id + * dingtalk subscription information table id. */ private String dingTalkOrderDatasheet; /** - * Do DingTalk self-built apps need to restore the directory tree? + * * + * @return OssBucketInfo */ - private Boolean dingTalkContactWithTree = false; - public OssBucketInfo getOssBucketByAsset() { - return Optional.ofNullable(ossBuckets).orElseGet(HashMap::new).getOrDefault(BucketKey.VK_ASSETS_LTD, new OssBucketInfo()); + return Optional.ofNullable(ossBuckets).orElseGet(HashMap::new) + .getOrDefault(BucketKey.VK_ASSETS_LTD, new OssBucketInfo()); } - public OssBucketInfo getOssBucketByPublicAsset() { - return Optional.ofNullable(ossBuckets).orElseGet(HashMap::new).getOrDefault(BucketKey.VK_PUBLIC_ASSETS_LTD, new OssBucketInfo()); + /** + * * + * @return String + */ + public String defaultServerDomain() { + return ReUtil.replaceAll(serverDomain, "http://|https://", + StrUtil.EMPTY); } public enum BucketKey { - // old resource bucket - // It will be temporarily reserved during the transformation of the front-end direct transmission, and will be invalid when the transformation is completed. - @Deprecated + /** */ VK_ASSETS_LTD, - // new open bucket - VK_PUBLIC_ASSETS_LTD, } @Data public static class OssBucketInfo { - + /** */ private String resourceUrl = ""; - + /** */ private String bucketName; - + /** */ private String type; } - - - public String defaultServerDomain() { - return ReUtil.replaceAll(serverDomain, "http://|https://", StrUtil.EMPTY); - } } diff --git a/backend-server/application/src/main/java/com/apitable/shared/config/properties/package-info.java b/backend-server/application/src/main/java/com/apitable/shared/config/properties/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..ab3c6a8d60988d20e85922038bafe977626c75fd --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/shared/config/properties/package-info.java @@ -0,0 +1,23 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/** + * + * @author Chambers + */ +package com.apitable.shared.config.properties; diff --git a/backend-server/application/src/main/java/com/apitable/shared/listener/ApplicationReadyEventListener.java b/backend-server/application/src/main/java/com/apitable/shared/listener/ApplicationReadyEventListener.java index a4bb8beef7c643bcc156f958dfbf33f4b4669dd4..c6edeb6e8d7ca0e06a1378129ba186ddcf24fe40 100644 --- a/backend-server/application/src/main/java/com/apitable/shared/listener/ApplicationReadyEventListener.java +++ b/backend-server/application/src/main/java/com/apitable/shared/listener/ApplicationReadyEventListener.java @@ -18,18 +18,15 @@ package com.apitable.shared.listener; -import java.util.Locale; -import java.util.TimeZone; - -import lombok.extern.slf4j.Slf4j; - import com.apitable.shared.component.LanguageManager; - +import lombok.extern.slf4j.Slf4j; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; -import static com.apitable.shared.constants.TimeZoneConstants.DEFAULT_TIME_ZONE; +import java.time.ZoneOffset; +import java.util.Locale; +import java.util.TimeZone; /** *

@@ -44,7 +41,7 @@ public class ApplicationReadyEventListener implements ApplicationListener * notification creation listener diff --git a/backend-server/application/src/main/java/com/apitable/shared/util/DateHelper.java b/backend-server/application/src/main/java/com/apitable/shared/util/DateHelper.java index 54b746fb55a1eca1475d230dfffd109410322a95..5406f01517f4c5c726cb611c1058ae24b1c675eb 100644 --- a/backend-server/application/src/main/java/com/apitable/shared/util/DateHelper.java +++ b/backend-server/application/src/main/java/com/apitable/shared/util/DateHelper.java @@ -18,7 +18,6 @@ package com.apitable.shared.util; -import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; @@ -40,11 +39,6 @@ public class DateHelper { return ChronoUnit.SECONDS.between(LocalDateTime.now(), midnight); } - public static LocalDateTime getStartTimeOfMonth() { - LocalDate date = LocalDate.now(); - return LocalDateTime.of(date.getYear(), date.getMonth(), 1, 0, 0); - } - /** * format the time according to the incoming format * diff --git a/backend-server/application/src/main/java/com/apitable/space/assembler/SpaceAssembler.java b/backend-server/application/src/main/java/com/apitable/space/assembler/SpaceAssembler.java new file mode 100644 index 0000000000000000000000000000000000000000..c08ee36e0ffd7331375b7140b5fae74fe1fd4286 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/space/assembler/SpaceAssembler.java @@ -0,0 +1,33 @@ +package com.apitable.space.assembler; + +import cn.hutool.core.util.ObjectUtil; +import com.apitable.interfaces.social.model.SocialConnectInfo; +import com.apitable.space.dto.SpaceDTO; +import com.apitable.space.vo.SpaceSocialConfig; +import com.apitable.space.vo.SpaceVO; + +public class SpaceAssembler { + + public static SpaceVO toVO(SpaceDTO spaceDTO) { + SpaceVO spaceVO = new SpaceVO(); + spaceVO.setSpaceId(spaceDTO.getSpaceId()); + spaceVO.setName(spaceDTO.getName()); + spaceVO.setLogo(spaceDTO.getLogo()); + spaceVO.setPoint(spaceDTO.getPoint()); + spaceVO.setAdmin(spaceDTO.getAdmin()); + spaceVO.setPreDeleted(spaceDTO.getPreDeleted()); + return spaceVO; + } + + public static SpaceSocialConfig toSocialConfig(SocialConnectInfo socialConnectInfo) { + SpaceSocialConfig socialConfig = new SpaceSocialConfig(); + if (ObjectUtil.isNotNull(socialConnectInfo)) { + socialConfig.setEnabled(socialConnectInfo.isEnabled()); + socialConfig.setPlatform(socialConnectInfo.getPlatform()); + socialConfig.setAppType(socialConnectInfo.getAppType()); + socialConfig.setAuthMode(socialConnectInfo.getAuthMode()); + socialConfig.setContactSyncing(socialConnectInfo.contactSyncing()); + } + return socialConfig; + } +} diff --git a/backend-server/application/src/main/java/com/apitable/space/dto/SpaceDTO.java b/backend-server/application/src/main/java/com/apitable/space/dto/SpaceDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..4259e4d387d7acd7d9ff2eb20d345afb09a29abf --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/space/dto/SpaceDTO.java @@ -0,0 +1,19 @@ +package com.apitable.space.dto; + +import lombok.Data; + +@Data +public class SpaceDTO { + + private String spaceId; + + private String name; + + private String logo; + + private Boolean point; + + private Boolean admin; + + private Boolean preDeleted; +} diff --git a/backend-server/application/src/main/java/com/apitable/space/mapper/SpaceMapper.java b/backend-server/application/src/main/java/com/apitable/space/mapper/SpaceMapper.java index e211229b2430385710c82eb28e033ab4def08015..ba7e96afc08523a3c93bbf1281d66f61feff6046 100644 --- a/backend-server/application/src/main/java/com/apitable/space/mapper/SpaceMapper.java +++ b/backend-server/application/src/main/java/com/apitable/space/mapper/SpaceMapper.java @@ -18,13 +18,13 @@ package com.apitable.space.mapper; -import com.baomidou.mybatisplus.annotation.InterceptorIgnore; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.apitable.space.dto.MapDTO; import com.apitable.space.dto.BaseSpaceInfoDTO; +import com.apitable.space.dto.MapDTO; import com.apitable.space.dto.SpaceAdminInfoDTO; -import com.apitable.space.vo.SpaceVO; +import com.apitable.space.dto.SpaceDTO; import com.apitable.space.entity.SpaceEntity; +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Param; import java.time.LocalDateTime; @@ -61,7 +61,7 @@ public interface SpaceMapper extends BaseMapper { * @return the user's space infos. */ @InterceptorIgnore(illegalSql = "true") - List selectListByUserId(@Param("userId") Long userId); + List selectListByUserId(@Param("userId") Long userId); /** * @param userId user id diff --git a/backend-server/application/src/main/java/com/apitable/space/service/impl/SpaceServiceImpl.java b/backend-server/application/src/main/java/com/apitable/space/service/impl/SpaceServiceImpl.java index 07e06d43417a70c33d3565c6a36987ceeb255b3e..09507e45a56274022ed80a8355da6343f26f7dd6 100644 --- a/backend-server/application/src/main/java/com/apitable/space/service/impl/SpaceServiceImpl.java +++ b/backend-server/application/src/main/java/com/apitable/space/service/impl/SpaceServiceImpl.java @@ -18,19 +18,6 @@ package com.apitable.space.service.impl; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.function.Consumer; -import java.util.stream.Collectors; - -import javax.annotation.Resource; - import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.ListUtil; import cn.hutool.core.lang.Dict; @@ -39,12 +26,11 @@ import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.baomidou.mybatisplus.extension.toolkit.SqlHelper; -import lombok.extern.slf4j.Slf4j; - import com.apitable.asset.service.IAssetService; import com.apitable.base.enums.DatabaseException; +import com.apitable.core.util.ExceptionUtil; +import com.apitable.core.util.SpringContextHolder; +import com.apitable.core.util.SqlTool; import com.apitable.interfaces.billing.facade.EntitlementServiceFacade; import com.apitable.interfaces.billing.model.SubscriptionFeature; import com.apitable.interfaces.billing.model.SubscriptionInfo; @@ -83,14 +69,9 @@ import com.apitable.shared.holder.NotificationRenderFieldHolder; import com.apitable.shared.listener.event.AuditSpaceEvent; import com.apitable.shared.listener.event.AuditSpaceEvent.AuditSpaceArg; import com.apitable.shared.util.IdUtil; +import com.apitable.space.assembler.SpaceAssembler; import com.apitable.space.assembler.SubscribeAssembler; -import com.apitable.space.dto.ControlStaticsDTO; -import com.apitable.space.dto.DatasheetStaticsDTO; -import com.apitable.space.dto.GetSpaceListFilterCondition; -import com.apitable.space.dto.MapDTO; -import com.apitable.space.dto.NodeTypeStaticsDTO; -import com.apitable.space.dto.SpaceAdminInfoDTO; -import com.apitable.space.dto.SpaceCapacityUsedInfo; +import com.apitable.space.dto.*; import com.apitable.space.entity.SpaceEntity; import com.apitable.space.enums.AuditSpaceAction; import com.apitable.space.enums.SpaceException; @@ -98,17 +79,8 @@ import com.apitable.space.enums.SpaceResourceGroupCode; import com.apitable.space.mapper.SpaceMapper; import com.apitable.space.mapper.SpaceMemberRoleRelMapper; import com.apitable.space.ro.SpaceUpdateOpRo; -import com.apitable.space.service.IInvitationService; -import com.apitable.space.service.ISpaceInviteLinkService; -import com.apitable.space.service.ISpaceRoleService; -import com.apitable.space.service.ISpaceService; -import com.apitable.space.service.IStaticsService; -import com.apitable.space.vo.SpaceGlobalFeature; -import com.apitable.space.vo.SpaceInfoVO; -import com.apitable.space.vo.SpaceSocialConfig; -import com.apitable.space.vo.SpaceSubscribeVo; -import com.apitable.space.vo.SpaceVO; -import com.apitable.space.vo.UserSpaceVo; +import com.apitable.space.service.*; +import com.apitable.space.vo.*; import com.apitable.template.service.ITemplateService; import com.apitable.user.entity.UserEntity; import com.apitable.user.service.IUserService; @@ -118,25 +90,27 @@ import com.apitable.workspace.enums.IdRulePrefixEnum; import com.apitable.workspace.enums.NodeType; import com.apitable.workspace.service.INodeService; import com.apitable.workspace.service.INodeShareSettingService; -import com.apitable.core.util.ExceptionUtil; -import com.apitable.core.util.SpringContextHolder; -import com.apitable.core.util.SqlTool; - +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.baomidou.mybatisplus.extension.toolkit.SqlHelper; +import lombok.extern.slf4j.Slf4j; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import javax.annotation.Resource; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.*; +import java.util.Map.Entry; +import java.util.function.Consumer; +import java.util.stream.Collectors; + import static com.apitable.organization.enums.OrganizationException.CREATE_MEMBER_ERROR; import static com.apitable.organization.enums.OrganizationException.NOT_EXIST_MEMBER; import static com.apitable.shared.constants.NotificationConstants.NEW_SPACE_NAME; import static com.apitable.shared.constants.NotificationConstants.OLD_SPACE_NAME; -import static com.apitable.space.enums.SpaceException.NO_ALLOW_OPERATE; -import static com.apitable.space.enums.SpaceException.SPACE_NOT_EXIST; -import static com.apitable.space.enums.SpaceException.SPACE_QUIT_FAILURE; -import static com.apitable.workspace.enums.PermissionException.CAN_OP_MAIN_ADMIN; -import static com.apitable.workspace.enums.PermissionException.MEMBER_NOT_IN_SPACE; -import static com.apitable.workspace.enums.PermissionException.SET_MAIN_ADMIN_FAIL; -import static com.apitable.workspace.enums.PermissionException.TRANSFER_SELF; +import static com.apitable.space.enums.SpaceException.*; +import static com.apitable.workspace.enums.PermissionException.*; @Service @Slf4j @@ -411,30 +385,39 @@ public class SpaceServiceImpl extends ServiceImpl impl @Override public List getSpaceListByUserId(Long userId, GetSpaceListFilterCondition condition) { - List list = baseMapper.selectListByUserId(userId); - if (CollUtil.isEmpty(list)) { - return list; + List spaceDTOList = baseMapper.selectListByUserId(userId); + if (CollUtil.isEmpty(spaceDTOList)) { + return Collections.emptyList(); + } + if (condition != null && BooleanUtil.isTrue(condition.getManageable())) { + spaceDTOList = spaceDTOList.stream() + .filter(SpaceDTO::getAdmin) + .collect(Collectors.toList()); } - Map spaceMaps = list.stream().collect(Collectors.toMap(SpaceVO::getSpaceId, v -> v, (k1, k2) -> k1)); + if (CollUtil.isEmpty(spaceDTOList)) { + return Collections.emptyList(); + } + Map spaceMaps = spaceDTOList.stream().collect(Collectors.toMap(SpaceDTO::getSpaceId, v -> v, (k1, k2) -> k1)); List spaceIds = new ArrayList<>(spaceMaps.keySet()); // get space domains Map spaceDomains = socialServiceFacade.getDomainNameMap(spaceIds); // batch query subscriptions Map spacePlanFeatureMap = entitlementServiceFacade.getSpaceSubscriptions(spaceIds); // setting information - spaceMaps.forEach((spaceId, spaceVO) -> { - SubscriptionFeature planFeature = spacePlanFeatureMap.get(spaceId); - spaceVO.setMaxSeat(planFeature.getSeat().getValue()); - spaceVO.setSpaceDomain(spaceDomains.get(spaceId)); - }); - if (condition != null) { - if (BooleanUtil.isTrue(condition.getManageable())) { - return list.stream() - .filter(v -> v.getAdmin().equals(condition.getManageable())) - .collect(Collectors.toList()); + List resultList = new ArrayList<>(); + spaceMaps.forEach((spaceId, spaceDTO) -> { + SpaceVO spaceVO = SpaceAssembler.toVO(spaceDTO); + SocialConnectInfo socialConnectInfo = socialServiceFacade.getConnectInfo(spaceId); + SpaceSocialConfig socialConfig = SpaceAssembler.toSocialConfig(socialConnectInfo); + spaceVO.setSocial(socialConfig); + if (spacePlanFeatureMap.containsKey(spaceId)) { + SubscriptionFeature planFeature = spacePlanFeatureMap.get(spaceId); + spaceVO.setMaxSeat(planFeature.getSeat().getValue()); + spaceVO.setSpaceDomain(spaceDomains.get(spaceId)); } - } - return list; + resultList.add(spaceVO); + }); + return resultList; } @Override @@ -505,8 +488,7 @@ public class SpaceServiceImpl extends ServiceImpl impl vo.setCreatorName(ownerMember.getMemberName()); vo.setCreatorAvatar(ownerMember.getAvatar()); vo.setIsCreatorNameModified(ownerMember.getIsSocialNameModified() > 0); - } - else { + } else { MemberDTO creatorMember = memberMapper.selectDtoByMemberId(entity.getCreator()); if (creatorMember != null) { vo.setCreatorName(creatorMember.getMemberName()); @@ -550,8 +532,7 @@ public class SpaceServiceImpl extends ServiceImpl impl spaceCapacityUsedInfo.setCurrentBundleCapacityUsedSizes(capacityUsedSize); // Because the package capacity is preferred, the complimentary attachment capacity has been used to be 0. spaceCapacityUsedInfo.setGiftCapacityUsedSizes(0L); - } - else { + } else { spaceCapacityUsedInfo.setCurrentBundleCapacityUsedSizes(planCapacity); // complimentary attachment capacity Long giftCapacity = subscriptionInfo.getGiftCapacity().getValue(); @@ -559,8 +540,7 @@ public class SpaceServiceImpl extends ServiceImpl impl // the used complimentary attachment capacity is equal to the size of the complimentary assert capacity. if (capacityUsedSize > subscriptionInfo.getTotalCapacity().getValue()) { spaceCapacityUsedInfo.setGiftCapacityUsedSizes(giftCapacity); - } - else { + } else { // gift capacity used left spaceCapacityUsedInfo.setGiftCapacityUsedSizes(capacityUsedSize - planCapacity); } @@ -620,8 +600,7 @@ public class SpaceServiceImpl extends ServiceImpl impl // check whether the mobile phone verification code is passed ValidateTarget target = ValidateTarget.create(dto.getMobile(), dto.getAreaCode()); ValidateCodeProcessorManage.me().findValidateCodeProcessor(ValidateCodeType.SMS).verifyIsPass(target.getRealTarget()); - } - else if (dto.getEmail() != null) { + } else if (dto.getEmail() != null) { // check whether the sms verification code is passed ValidateTarget target = ValidateTarget.create(dto.getEmail()); ValidateCodeProcessorManage.me().findValidateCodeProcessor(ValidateCodeType.EMAIL).verifyIsPass(target.getRealTarget()); @@ -759,8 +738,7 @@ public class SpaceServiceImpl extends ServiceImpl impl if (linkId.startsWith(IdRulePrefixEnum.SHARE.getIdRulePrefixEnum())) { // sharing node return iNodeShareSettingService.getSpaceId(linkId); - } - else { + } else { // template return iTemplateService.getSpaceId(linkId); } diff --git a/backend-server/application/src/main/java/com/apitable/space/service/impl/StaticsServiceImpl.java b/backend-server/application/src/main/java/com/apitable/space/service/impl/StaticsServiceImpl.java index 12d629d885d26d98d46309a5251b1d4589ca576c..deb3f1e04996fc624db9c9f02f91dbd598ea7634 100644 --- a/backend-server/application/src/main/java/com/apitable/space/service/impl/StaticsServiceImpl.java +++ b/backend-server/application/src/main/java/com/apitable/space/service/impl/StaticsServiceImpl.java @@ -18,41 +18,39 @@ package com.apitable.space.service.impl; -import java.text.SimpleDateFormat; -import java.time.LocalDateTime; -import java.util.*; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -import javax.annotation.Resource; - import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.text.StrSpliter; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; -import lombok.extern.slf4j.Slf4j; - import com.apitable.control.infrastructure.ControlIdBuilder; import com.apitable.control.infrastructure.ControlType; import com.apitable.control.mapper.ControlMapper; import com.apitable.control.model.ControlTypeDTO; -import com.apitable.space.mapper.StaticsMapper; +import com.apitable.core.util.SqlTool; +import com.apitable.shared.util.DateHelper; import com.apitable.space.dto.ControlStaticsDTO; import com.apitable.space.dto.DatasheetStaticsDTO; import com.apitable.space.dto.NodeStaticsDTO; import com.apitable.space.dto.NodeTypeStaticsDTO; +import com.apitable.space.mapper.StaticsMapper; import com.apitable.space.service.IStaticsService; import com.apitable.workspace.mapper.NodeMapper; -import com.apitable.shared.util.DateHelper; -import com.apitable.core.util.SqlTool; - +import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; -import static com.apitable.shared.constants.DateFormatConstants.YEARS_MONTH_PATTERN; +import javax.annotation.Resource; +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + import static com.apitable.core.constants.RedisConstants.GENERAL_STATICS; +import static com.apitable.shared.constants.DateFormatConstants.YEARS_MONTH_PATTERN; +import static java.time.temporal.TemporalAdjusters.firstDayOfMonth; /** *

@@ -113,7 +111,7 @@ public class StaticsServiceImpl implements IStaticsService { @Override public Long getCurrentMonthApiUsageUntilYesterday(String spaceId) { // If it is the first day of this month, 0 will be returned directly - if (ObjectUtil.equals(DateUtil.date(new Date()).getDate(), 1)) { + if (ObjectUtil.equals(LocalDateTime.now().getDayOfMonth(), 1)) { return 0L; } else { // Get the API usage cache of this month before today @@ -152,7 +150,8 @@ public class StaticsServiceImpl implements IStaticsService { id = staticsMapper.selectMaxId(); } else { - id = staticsMapper.selectApiUsageMinIdByCreatedAt(lastMonthMinId, DateHelper.getStartTimeOfMonth()); + LocalDateTime startDayOfMonth = now.with(firstDayOfMonth()); + id = staticsMapper.selectApiUsageMinIdByCreatedAt(lastMonthMinId, startDayOfMonth); } // Keep the cache of the month redisTemplate.opsForValue().set(key, id, 33, TimeUnit.DAYS); diff --git a/backend-server/application/src/main/java/com/apitable/space/vo/SpaceVO.java b/backend-server/application/src/main/java/com/apitable/space/vo/SpaceVO.java index 3874cdbe89b83dbfe1734279018bb6d25187f797..afb3e011b05417b8868753ee88c3e832feb76ea7 100644 --- a/backend-server/application/src/main/java/com/apitable/space/vo/SpaceVO.java +++ b/backend-server/application/src/main/java/com/apitable/space/vo/SpaceVO.java @@ -18,6 +18,7 @@ package com.apitable.space.vo; +import com.apitable.shared.support.serializer.NullNumberSerializer; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @@ -58,21 +59,17 @@ public class SpaceVO { @JsonSerialize(nullsUsing = NullBooleanSerializer.class) private Boolean admin; - @ApiModelProperty(value = "Whether it is a paid space", example = "false", position = 6) - @JsonSerialize(nullsUsing = NullBooleanSerializer.class) - private Boolean charge; - @ApiModelProperty(value = "Whether it is in pre deletion status", example = "false", position = 7) @JsonSerialize(nullsUsing = NullBooleanSerializer.class) private Boolean preDeleted; @ApiModelProperty(value = "Maximum total number of subscription plan members", position = 8) + @JsonSerialize(nullsUsing = NullNumberSerializer.class) private Long maxSeat; @ApiModelProperty(value = "Space domain name", position = 9) private String spaceDomain; - @Deprecated @ApiModelProperty(value = "Third party integration binding information", position = 10) private SpaceSocialConfig social; } diff --git a/backend-server/application/src/main/java/com/apitable/template/mapper/TemplatePropertyRelMapper.java b/backend-server/application/src/main/java/com/apitable/template/mapper/TemplatePropertyRelMapper.java index 715280a6b12ad76d663c59e8fc931571f6ef2bd2..53ade84ed62695a124d544ce733177de49ac28eb 100644 --- a/backend-server/application/src/main/java/com/apitable/template/mapper/TemplatePropertyRelMapper.java +++ b/backend-server/application/src/main/java/com/apitable/template/mapper/TemplatePropertyRelMapper.java @@ -20,12 +20,11 @@ package com.apitable.template.mapper; import java.util.List; +import com.apitable.template.entity.TemplatePropertyRelEntity; +import com.apitable.template.model.TemplatePropertyRelDto; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Param; -import com.apitable.template.model.TemplatePropertyRelDto; -import com.apitable.template.entity.TemplatePropertyRelEntity; - /** *

* Template Property Rel Mapper @@ -44,14 +43,9 @@ public interface TemplatePropertyRelMapper extends BaseMapper entities); /** - * Query template id list by property id - */ - List selectTemplateIdsByPropertyId(@Param("propertyId") Long propertyId); - - /** - * Batch delete by id list + * Query template id list by property code */ - int deleteBatchIn(@Param("propertyIds") List propertyIds); + List selectTemplateIdsByPropertyCode(@Param("propertyCode") String propertyCode); /** * Query template ids by property id list diff --git a/backend-server/application/src/main/java/com/apitable/template/service/ITemplatePropertyService.java b/backend-server/application/src/main/java/com/apitable/template/service/ITemplatePropertyService.java index 4edda1f2159a6a20fa406816baeee67a62181296..0f0df07851b38486dc11bf28bbc284fc76eedabc 100644 --- a/backend-server/application/src/main/java/com/apitable/template/service/ITemplatePropertyService.java +++ b/backend-server/application/src/main/java/com/apitable/template/service/ITemplatePropertyService.java @@ -22,13 +22,12 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import com.baomidou.mybatisplus.extension.service.IService; - import com.apitable.shared.cache.bean.CategoryDto; +import com.apitable.template.entity.TemplatePropertyEntity; import com.apitable.template.enums.TemplatePropertyType; import com.apitable.template.model.TemplatePropertyDto; import com.apitable.template.model.TemplatePropertyRelDto; -import com.apitable.template.entity.TemplatePropertyEntity; +import com.baomidou.mybatisplus.extension.service.IService; /** *

@@ -42,11 +41,6 @@ public interface ITemplatePropertyService extends IService getTemplatePropertiesWithLangAndOrder(TemplatePropertyType type, String lang); - /** - * Get property id by property code and type - */ - Long getIdByCodeAndType(String code, TemplatePropertyType type); - /** * Get template id list by property code and type */ diff --git a/backend-server/application/src/main/java/com/apitable/template/service/impl/TemplatePropertyServiceImpl.java b/backend-server/application/src/main/java/com/apitable/template/service/impl/TemplatePropertyServiceImpl.java index 0c4faacf3870535a66c8cbc447c5cc918d9d0b89..cdf46ca99cbebe27196753e5cec15cc15911fa94 100644 --- a/backend-server/application/src/main/java/com/apitable/template/service/impl/TemplatePropertyServiceImpl.java +++ b/backend-server/application/src/main/java/com/apitable/template/service/impl/TemplatePropertyServiceImpl.java @@ -19,7 +19,6 @@ package com.apitable.template.service.impl; import java.util.ArrayList; -import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -28,9 +27,6 @@ import java.util.stream.Collectors; import javax.annotation.Resource; import cn.hutool.core.collection.CollUtil; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import lombok.extern.slf4j.Slf4j; - import com.apitable.shared.cache.bean.CategoryDto; import com.apitable.shared.component.LanguageManager; import com.apitable.template.entity.TemplatePropertyEntity; @@ -41,6 +37,8 @@ import com.apitable.template.model.TemplateKeyWordSearchDto; import com.apitable.template.model.TemplatePropertyDto; import com.apitable.template.model.TemplatePropertyRelDto; import com.apitable.template.service.ITemplatePropertyService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -65,18 +63,9 @@ public class TemplatePropertyServiceImpl extends ServiceImpl getTemplateIdsByPropertyCodeAndType(String code, TemplatePropertyType type) { - Long propertyId = getIdByCodeAndType(code, type); - if (propertyId != null) { - return propertyRelMapper.selectTemplateIdsByPropertyId(propertyId); - } - return Collections.emptyList(); + return propertyRelMapper.selectTemplateIdsByPropertyCode(code); } @Override diff --git a/backend-server/application/src/main/java/com/apitable/template/service/impl/TemplateServiceImpl.java b/backend-server/application/src/main/java/com/apitable/template/service/impl/TemplateServiceImpl.java index e1da2007a6da4fcd6b1fb0c01ea9c78a871e085a..deac86327094674c2d229594e1c1f421848ebc1f 100644 --- a/backend-server/application/src/main/java/com/apitable/template/service/impl/TemplateServiceImpl.java +++ b/backend-server/application/src/main/java/com/apitable/template/service/impl/TemplateServiceImpl.java @@ -37,13 +37,13 @@ import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.BooleanUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; +import com.apitable.widget.service.IWidgetService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import lombok.extern.slf4j.Slf4j; import com.apitable.base.enums.DatabaseException; import com.apitable.control.infrastructure.role.ControlRoleManager; import com.apitable.control.infrastructure.role.RoleConstants.Node; -import com.apitable.interfaces.widget.facade.WidgetServiceFacade; import com.apitable.shared.cache.bean.CategoryDto; import com.apitable.shared.cache.bean.RecommendConfig; import com.apitable.shared.cache.bean.RecommendConfig.AlbumGroup; @@ -142,7 +142,7 @@ public class TemplateServiceImpl extends ServiceImpl update(@RequestBody @Valid UserOpRo param) { - ExceptionUtil.isTrue(StrUtil.isNotBlank(param.getAvatar()) || StrUtil.isNotBlank(param.getNickName()) + ExceptionUtil.isTrue(StrUtil.isNotBlank(param.getAvatar()) || StrUtil.isNotBlank(param.getNickName()) || ObjectUtil.isNotNull(param.getAvatarColor()) || StrUtil.isNotBlank(param.getLocale()), ParameterException.NO_ARG); Long userId = SessionContext.getUserId(); iUserService.edit(userId, param); diff --git a/backend-server/application/src/main/java/com/apitable/user/entity/UserEntity.java b/backend-server/application/src/main/java/com/apitable/user/entity/UserEntity.java index 296ce5c151a2fa5546595b51ecef0494985f9935..0fbe9fb2cd2fd6cfd0d1bcd116869b40c2f3eeb5 100644 --- a/backend-server/application/src/main/java/com/apitable/user/entity/UserEntity.java +++ b/backend-server/application/src/main/java/com/apitable/user/entity/UserEntity.java @@ -83,13 +83,11 @@ public class UserEntity implements Serializable { */ private String password; - @TableField(updateStrategy = FieldStrategy.IGNORED) /** * Avatar */ private String avatar; - @TableField(updateStrategy = FieldStrategy.IGNORED) /** * default avatar color number */ diff --git a/backend-server/application/src/main/java/com/apitable/user/mapper/UserMapper.java b/backend-server/application/src/main/java/com/apitable/user/mapper/UserMapper.java index 95dbbbe23ae7b409dfe28aa09d3e646773ef2ef4..50ab880a8ce5413f21836451cfd0fefbbfeaa8d7 100644 --- a/backend-server/application/src/main/java/com/apitable/user/mapper/UserMapper.java +++ b/backend-server/application/src/main/java/com/apitable/user/mapper/UserMapper.java @@ -185,6 +185,15 @@ public interface UserMapper extends BaseMapper { * */ int resetUserById(@Param("userId") Long userId); + /** + * update user avatar information + * + * @param userId User ID + * @param avatar User avatar + * @param color User default avatar color number + */ + int updateUserAvatarInfo(@Param("userId") Long userId, @Param("avatar") String avatar, @Param("color") Integer color); + /** * Batch query member email * diff --git a/backend-server/application/src/main/java/com/apitable/user/service/impl/UserServiceImpl.java b/backend-server/application/src/main/java/com/apitable/user/service/impl/UserServiceImpl.java index a234627783de7397eb0ebd26d24dcb53ad2a78eb..4cabb9402685416e3cc3be1054b54df60e8e4283 100644 --- a/backend-server/application/src/main/java/com/apitable/user/service/impl/UserServiceImpl.java +++ b/backend-server/application/src/main/java/com/apitable/user/service/impl/UserServiceImpl.java @@ -41,13 +41,12 @@ import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.baomidou.mybatisplus.extension.toolkit.SqlHelper; -import com.google.common.collect.Lists; -import lombok.extern.slf4j.Slf4j; - import com.apitable.asset.service.IAssetService; import com.apitable.base.enums.DatabaseException; +import com.apitable.core.exception.BusinessException; +import com.apitable.core.util.ExceptionUtil; +import com.apitable.core.util.HttpContextUtil; +import com.apitable.core.util.SqlTool; import com.apitable.interfaces.social.enums.SocialNameModified; import com.apitable.interfaces.social.facade.SocialServiceFacade; import com.apitable.interfaces.social.model.SocialUserBind; @@ -74,13 +73,11 @@ import com.apitable.shared.component.TaskManager; import com.apitable.shared.component.notification.INotificationFactory; import com.apitable.shared.component.notification.NotificationManager; import com.apitable.shared.component.notification.NotificationTemplateId; -import com.apitable.shared.config.properties.ConstProperties; import com.apitable.shared.constants.LanguageConstants; import com.apitable.shared.constants.NotificationConstants; import com.apitable.shared.context.LoginContext; import com.apitable.shared.security.PasswordService; import com.apitable.shared.sysconfig.notification.NotificationTemplate; -import com.apitable.shared.util.RandomExtendUtil; import com.apitable.space.entity.SpaceEntity; import com.apitable.space.mapper.SpaceMapper; import com.apitable.space.ro.SpaceUpdateOpRo; @@ -100,12 +97,12 @@ import com.apitable.user.service.IUserService; import com.apitable.user.vo.UserInfoVo; import com.apitable.user.vo.UserLinkVo; import com.apitable.workspace.service.INodeShareService; -import com.apitable.core.exception.BusinessException; -import com.apitable.core.util.ExceptionUtil; -import com.apitable.core.util.HttpContextUtil; -import com.apitable.core.util.SqlTool; - +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.baomidou.mybatisplus.extension.toolkit.SqlHelper; +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; + import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.session.FindByIndexNameSessionRepository; import org.springframework.session.Session; @@ -129,148 +126,237 @@ import static com.apitable.user.enums.UserException.USER_LANGUAGE_SET_UN_SUPPORT import static com.apitable.user.enums.UserException.USER_NOT_EXIST; import static com.apitable.user.enums.UserOperationType.COMPLETE_CLOSING; -/** - *

- * User table service implementation class - *

- */ +/** User table service implementation class. */ @Service @Slf4j -public class UserServiceImpl extends ServiceImpl implements IUserService { +public class UserServiceImpl extends ServiceImpl + implements IUserService { + + /** */ + private static final int USER_AVATAR_COLOR_MAX_VALUE = 10; + /** */ + private static final int USER_IS_PAUSED_CLOSE_DAY = 30; + + /** */ private static final int QUERY_LOCALE_IN_EMAILS_LIMIT = 200; + /** */ @Resource private LoginUserCacheService loginUserCacheService; + /** */ @Resource private IAssetService iAssetService; + /** */ @Resource private ISpaceService iSpaceService; + /** */ @Resource private IPlayerActivityService iPlayerActivityService; + /** */ @Resource private UserActiveSpaceCacheService userActiveSpaceCacheService; + /** */ @Resource private UserSpaceCacheService userSpaceCacheService; + /** */ @Resource private UserSpaceOpenedSheetCacheService userSpaceOpenedSheetCacheService; + /** */ @Resource private INodeShareService nodeShareService; + /** */ @Resource private ISpaceInviteLinkService spaceInviteLinkService; + /** */ @Resource private IPlayerNotificationService notificationService; + /** */ @Resource private MemberMapper memberMapper; + /** */ @Resource private SpaceMapper spaceMapper; - @Resource - private ConstProperties constProperties; - + /** */ @Resource private FindByIndexNameSessionRepository sessions; + /** */ @Resource private IUserHistoryService iUserHistoryService; + /** */ @Resource private UserMapper userMapper; + /** */ @Resource private SocialServiceFacade socialServiceFacade; + /** */ @Resource private IMemberService iMemberService; + /** */ @Resource private INotificationFactory notificationFactory; + /** */ @Resource private UserServiceFacade userServiceFacade; + /** */ @Resource private UserLinkServiceFacade userLinkServiceFacade; + /** */ @Resource private PasswordService passwordService; + /** */ @Resource private IDeveloperService iDeveloperService; + /** + * * + * @param mobile cell-phone number + * @return user id + */ @Override - public Long getUserIdByMobile(String mobile) { + public Long getUserIdByMobile(final String mobile) { return baseMapper.selectIdByMobile(mobile); } + /** + * * + * @param email email + * @return user id + */ @Override - public Long getUserIdByEmail(String email) { + public Long getUserIdByEmail(final String email) { return baseMapper.selectIdByEmail(email); } + /** + * * + * @param code Area code + * @param mobile Phone number + * @return boolean + */ @Override - public boolean checkByCodeAndMobile(String code, String mobile) { - code = StrUtil.prependIfMissing(code, "+"); + public boolean checkByCodeAndMobile(final String code, + final String mobile) { + String areaCode = StrUtil.prependIfMissing(code, "+"); UserEntity userEntity = baseMapper.selectByMobile(mobile); if (userEntity == null) { return false; } - return StrUtil.isNotBlank(userEntity.getCode()) && userEntity.getCode().equals(code); + return StrUtil.isNotBlank(userEntity.getCode()) + && userEntity.getCode().equals(areaCode); } + /** + * * + * @param email email + * @return boolean + */ @Override - public boolean checkByEmail(String email) { + public boolean checkByEmail(final String email) { return SqlTool.retCount(baseMapper.selectCountByEmail(email)) > 0; } + /** + * * + * @param code Area code + * @param mobilePhone Phone number + * @return UserEntity + */ @Override - public UserEntity getByCodeAndMobilePhone(String code, String mobilePhone) { - code = StrUtil.prependIfMissing(code, "+"); + public UserEntity getByCodeAndMobilePhone(final String code, + final String mobilePhone) { + String areaCode = StrUtil.prependIfMissing(code, "+"); UserEntity userEntity = baseMapper.selectByMobile(mobilePhone); if (userEntity == null) { return null; } - if (StrUtil.isNotBlank(userEntity.getCode()) && userEntity.getCode().equals(code)) { + if (StrUtil.isNotBlank(userEntity.getCode()) + && userEntity.getCode().equals(areaCode)) { return userEntity; } return null; } + /** + * * + * @param code Area code + * @param mobilePhones Mobile number list + * @return List + */ @Override - public List getByCodeAndMobilePhones(String code, Collection mobilePhones) { - List userEntities = baseMapper.selectByMobilePhoneIn(mobilePhones); + public List getByCodeAndMobilePhones( + final String code, final Collection mobilePhones) { + List userEntities = + baseMapper.selectByMobilePhoneIn(mobilePhones); if (userEntities.isEmpty()) { return userEntities; } String finalCode = StrUtil.prependIfMissing(code, "+"); - return userEntities.stream().filter(user -> StrUtil.isNotBlank(user.getCode()) && user.getCode().equals(finalCode)) - .collect(Collectors.toList()); + return userEntities.stream() + .filter(user -> StrUtil.isNotBlank(user.getCode()) + && user.getCode().equals(finalCode)) + .collect(Collectors.toList()); } + /** + * * + * @param email email + * @return UserEntity + */ @Override - public UserEntity getByEmail(String email) { + public UserEntity getByEmail(final String email) { return baseMapper.selectByEmail(email); } + /** + * * + * @param emails email list + * @return List + */ @Override - public List getByEmails(Collection emails) { + public List getByEmails(final Collection emails) { return baseMapper.selectByEmails(emails); } + /** + * * + * @param externalId External System ID + * @param nickName Nickname + * @param avatar Avatar + * @param email email + * @param remark Remarks + * @return user id + */ @Override @Transactional(rollbackFor = Exception.class) - public Long createByExternalSystem(String externalId, String nickName, String avatar, String email, String remark) { + public Long createByExternalSystem( + final String externalId, + final String nickName, + final String avatar, + final String email, + final String remark + ) { if (StrUtil.isNotBlank(email)) { // Query whether the existing mail exists UserEntity selectUser = baseMapper.selectByEmail(email); @@ -285,16 +371,21 @@ public class UserServiceImpl extends ServiceImpl impleme if (!flag) { throw new BusinessException(SIGN_IN_ERROR); } - socialServiceFacade.createSocialUser(new SocialUserBind(selectUser.getId(), externalId)); + socialServiceFacade.createSocialUser( + new SocialUserBind(selectUser.getId(), externalId)); return selectUser.getId(); } } log.info("Create Account"); UserEntity user = new UserEntity(); user.setUuid(IdUtil.fastSimpleUUID()); - user.setNickName(StrUtil.isNotBlank(nickName) ? nickName : StringUtils.substringBefore(email, "@")); + String name = StrUtil.isNotBlank(nickName) ? nickName + : StringUtils.substringBefore(email, "@"); + user.setNickName(name); user.setAvatar(StrUtil.isNotBlank(avatar) ? avatar : null); - user.setColor(StrUtil.isNotBlank(avatar) ? null : RandomUtil.randomInt(1, 12)); + Integer color = StrUtil.isNotBlank(avatar) ? null + : RandomUtil.randomInt(0, USER_AVATAR_COLOR_MAX_VALUE); + user.setColor(color); user.setEmail(email); user.setLastLoginTime(LocalDateTime.now()); user.setRemark(remark); @@ -303,13 +394,16 @@ public class UserServiceImpl extends ServiceImpl impleme throw new BusinessException(SIGN_IN_ERROR); } if (StrUtil.isNotBlank(email)) { - // If the mail has been invited and has not been bound to other accounts, activate the space members of the invited mail - List inactiveMembers = iMemberService.getInactiveMemberByEmails(email); + // If the mail has been invited + // and has not been bound to other accounts, + // activate the space members of the invited mail + List inactiveMembers = + iMemberService.getInactiveMemberByEmails(email); inactiveMemberProcess(user.getId(), inactiveMembers); - } - else { + } else { String spaceName = user.getNickName(); - if (LocaleContextHolder.getLocale().equals(LanguageManager.me().getDefaultLanguage())) { + if (LocaleContextHolder.getLocale() + .equals(LanguageManager.me().getDefaultLanguage())) { spaceName += SPACE_NAME_DEFAULT_SUFFIX; } iSpaceService.createSpace(user, spaceName); @@ -319,44 +413,76 @@ public class UserServiceImpl extends ServiceImpl impleme // Create personal invitation code userServiceFacade.createInvitationCode(user.getId()); // Create Associated User - socialServiceFacade.createSocialUser(new SocialUserBind(user.getId(), externalId)); + socialServiceFacade.createSocialUser( + new SocialUserBind(user.getId(), externalId)); return user.getId(); } + /** + * * + * @param user user entity + * @return boolean + */ @Override - public boolean saveUser(UserEntity user) { + public boolean saveUser(final UserEntity user) { boolean flag = save(user); TaskManager.me().execute(() -> { // jump to third site NotificationTemplate template = - notificationFactory.getTemplateById(NotificationTemplateId.NEW_USER_WELCOME_NOTIFY.getValue()); + notificationFactory.getTemplateById(NotificationTemplateId + .NEW_USER_WELCOME_NOTIFY.getValue()); Dict extras = Dict.create(); - if (StrUtil.isNotBlank(template.getUrl()) && template.getUrl().startsWith("http")) { + if (StrUtil.isNotBlank(template.getUrl()) + && template.getUrl().startsWith("http")) { Dict toast = Dict.create(); toast.put(EXTRA_TOAST_URL, template.getUrl()); - toast.put("onClose", ListUtil.toList("mark_cur_notice_to_read()")); - toast.put("onBtnClick", ListUtil.toList("window_open_url()")); + toast.put("onClose", + ListUtil.toList("mark_cur_notice_to_read()")); + toast.put("onBtnClick", + ListUtil.toList("window_open_url()")); toast.put("duration", 0); toast.put("closable", true); extras.put(EXTRA_TOAST, toast); } - NotificationManager.me().playerNotify(NotificationTemplateId.NEW_USER_WELCOME_NOTIFY, + NotificationManager.me() + .playerNotify(NotificationTemplateId.NEW_USER_WELCOME_NOTIFY, Collections.singletonList(user.getId()), 0L, null, extras); }); return flag; } + /** + * * + * @param areaCode Area code + * @param mobile Phone number + * @param nickName Third party user nickname + * @param avatar Third party user avatar + * @param email email + * @param spaceName Name of the new space + * @return user id + */ @Override @Transactional(rollbackFor = Exception.class) - public Long create(String areaCode, String mobile, String nickName, String avatar, String email, String spaceName) { + public Long create( + final String areaCode, + final String mobile, + final String nickName, + final String avatar, + final String email, + final String spaceName + ) { + Integer color = nullToDefaultAvatar(avatar) != null ? null + : RandomUtil.randomInt(0, USER_AVATAR_COLOR_MAX_VALUE); + String name = nullToDefaultNickName(nickName, mobile != null + ? mobile : StringUtils.substringBefore(email, "@")); // Create user with mobile number UserEntity entity = UserEntity.builder() .uuid(IdUtil.fastSimpleUUID()) .code(areaCode) .mobilePhone(mobile) - .nickName(nullToDefaultNickName(nickName, mobile != null ? mobile : StringUtils.substringBefore(email, "@"))) + .nickName(name) .avatar(nullToDefaultAvatar(avatar)) - .color(nullToDefaultAvatar(avatar) != null ? null : RandomUtil.randomInt(1,12)) + .color(color) .email(email) .lastLoginTime(LocalDateTime.now()) .build(); @@ -364,13 +490,18 @@ public class UserServiceImpl extends ServiceImpl impleme ExceptionUtil.isTrue(flag, REGISTER_FAIL); boolean hasSpace = false; if (email != null) { - // If the mailbox has been invited and has not been bound to other accounts, activate the space members of the invited mailbox - List inactiveMembers = iMemberService.getInactiveMemberByEmails(email); - hasSpace = this.inactiveMemberProcess(entity.getId(), inactiveMembers); + // If the mailbox has been invited + // and has not been bound to other accounts, + // activate the space members of the invited mailbox + List inactiveMembers = + iMemberService.getInactiveMemberByEmails(email); + hasSpace = this.inactiveMemberProcess(entity.getId(), + inactiveMembers); } // Activate imported members if (mobile != null) { - int count = memberMapper.updateUserIdByMobile(entity.getId(), mobile); + int count = + memberMapper.updateUserIdByMobile(entity.getId(), mobile); hasSpace = hasSpace || count > 0; } // No space to create a space automatically @@ -378,10 +509,10 @@ public class UserServiceImpl extends ServiceImpl impleme String newSpaceName; if (StrUtil.isNotBlank(spaceName)) { newSpaceName = spaceName; - } - else { + } else { newSpaceName = entity.getNickName(); - if (LocaleContextHolder.getLocale().equals(LanguageManager.me().getDefaultLanguage())) { + if (LocaleContextHolder.getLocale() + .equals(LanguageManager.me().getDefaultLanguage())) { newSpaceName += SPACE_NAME_DEFAULT_SUFFIX; } } @@ -394,18 +525,33 @@ public class UserServiceImpl extends ServiceImpl impleme return entity.getId(); } + /** + * * + * @param areaCode Area code + * @param mobile Phone number + * @param nickName Third party user nickname + * @param avatar Third party user avatar + * @return UserEntity + */ @Override @Transactional(rollbackFor = Exception.class) - public UserEntity createUserByMobilePhone(String areaCode, String mobile, String nickName, String avatar) { + public UserEntity createUserByMobilePhone( + final String areaCode, + final String mobile, + final String nickName, + final String avatar + ) { + Integer color = nullToDefaultAvatar(avatar) != null ? null + : RandomUtil.randomInt(0, USER_AVATAR_COLOR_MAX_VALUE); UserEntity entity = UserEntity.builder() - .uuid(IdUtil.fastSimpleUUID()) - .code(areaCode) - .mobilePhone(mobile) - .nickName(nullToDefaultNickName(nickName, mobile)) - .avatar(nullToDefaultAvatar(avatar)) - .color(nullToDefaultAvatar(avatar) != null ? null : RandomUtil.randomInt(1,12)) - .lastLoginTime(LocalDateTime.now()) - .build(); + .uuid(IdUtil.fastSimpleUUID()) + .code(areaCode) + .mobilePhone(mobile) + .nickName(nullToDefaultNickName(nickName, mobile)) + .avatar(nullToDefaultAvatar(avatar)) + .color(color) + .lastLoginTime(LocalDateTime.now()) + .build(); boolean flag = saveUser(entity); ExceptionUtil.isTrue(flag, REGISTER_FAIL); // Create user activity record @@ -415,16 +561,21 @@ public class UserServiceImpl extends ServiceImpl impleme return entity; } + /** + * * + * @param email email + * @return UserEntity + */ @Override @Transactional(rollbackFor = Exception.class) - public UserEntity createUserByEmail(String email) { + public UserEntity createUserByEmail(final String email) { UserEntity entity = UserEntity.builder() - .uuid(IdUtil.fastSimpleUUID()) - .email(email) - .nickName(StringUtils.substringBefore(email, "@")) - .color(RandomUtil.randomInt(1, 12)) - .lastLoginTime(LocalDateTime.now()) - .build(); + .uuid(IdUtil.fastSimpleUUID()) + .email(email) + .nickName(StringUtils.substringBefore(email, "@")) + .color(RandomUtil.randomInt(0, USER_AVATAR_COLOR_MAX_VALUE)) + .lastLoginTime(LocalDateTime.now()) + .build(); boolean flag = saveUser(entity); ExceptionUtil.isTrue(flag, REGISTER_FAIL); // Create user activity record @@ -434,45 +585,69 @@ public class UserServiceImpl extends ServiceImpl impleme return entity; } - + /** + * * + * @param user user entity + */ @Override @Transactional(rollbackFor = Exception.class) - public void initialDefaultSpaceForUser(UserEntity user) { + public void initialDefaultSpaceForUser(final UserEntity user) { // initial default space for new come user String spaceName = user.getNickName(); - if (LocaleContextHolder.getLocale().equals(LanguageManager.me().getDefaultLanguage())) { + if (LocaleContextHolder.getLocale() + .equals(LanguageManager.me().getDefaultLanguage())) { spaceName += SPACE_NAME_DEFAULT_SUFFIX; } iSpaceService.createSpace(user, spaceName); } + /** + * * + * @param userId User ID + * @return boolean + */ @Override - public boolean checkUserHasBindEmail(Long userId) { + public boolean checkUserHasBindEmail(final Long userId) { log.info("Query whether users bind email"); UserEntity user = getById(userId); ExceptionUtil.isNotNull(user, USER_NOT_EXIST); return user.getEmail() != null; } + /** + * * + * @param userId User ID + * @param spaceId Space ID + * @param email email + */ @Override @Transactional(rollbackFor = Exception.class) - public void bindMemberByEmail(Long userId, String spaceId, String email) { + public void bindMemberByEmail(final Long userId, final String spaceId, + final String email) { log.info("Bind member email"); // Determine whether the email is unbound and invited - MemberEntity member = iMemberService.getBySpaceIdAndEmail(spaceId, email); + MemberEntity member = + iMemberService.getBySpaceIdAndEmail(spaceId, email); ExceptionUtil.isNotNull(member, INVITE_EMAIL_NOT_EXIT); ExceptionUtil.isNull(member.getUserId(), INVITE_EMAIL_HAS_LINK); - // Judge whether the requesting user's mailbox is bound to another email, and the user's email must be empty + // Judge whether the requesting user's mailbox is bound + // to another email, and the user's email must be empty String userEmail = baseMapper.selectEmailById(userId); ExceptionUtil.isBlank(userEmail, LINK_EMAIL_ERROR); - // Bind as user email, and the email will be activated by invited space members together + // Bind as user email, and the email + // will be activated by invited space members together updateEmailByUserId(userId, email); } + /** + * * + * @param userId User ID + * @param email email + */ @Override @Transactional(rollbackFor = Exception.class) - public void updateEmailByUserId(Long userId, String email) { + public void updateEmailByUserId(final Long userId, final String email) { log.info("Modify User [{}] email [{}]", userId, email); UserEntity updateUser = new UserEntity(); updateUser.setId(userId); @@ -481,20 +656,29 @@ public class UserServiceImpl extends ServiceImpl impleme ExceptionUtil.isTrue(flag, LINK_EMAIL_ERROR); // Synchronize member information email iMemberService.updateEmailByUserId(userId, email); - // If the email has been invited and has not been bound to other accounts, activate the space members of the invited email - List inactiveMembers = iMemberService.getInactiveMemberByEmails(email); + // If the email has been invited + // and has not been bound to other accounts, + // activate the space members of the invited email + List inactiveMembers = + iMemberService.getInactiveMemberByEmails(email); this.inactiveMemberProcess(userId, inactiveMembers); // Delete Cache loginUserCacheService.delete(userId); } + /** + * * + * @param userId User ID + */ @Override @Transactional(rollbackFor = Exception.class) - public void unbindEmailByUserId(Long userId) { - // The user needs to bind at least one contact (mobile phone number, email) to unbind the email + public void unbindEmailByUserId(final Long userId) { + // The user needs to bind at least + // one contact (mobile phone number, email) to unbind the email LoginUserDto userDto = loginUserCacheService.getLoginUser(userId); ExceptionUtil.isNotBlank(userDto.getMobile(), MUST_BIND_MOBILE); - boolean flag = SqlHelper.retBool(baseMapper.resetEmailByUserId(userId)); + boolean flag = + SqlHelper.retBool(baseMapper.resetEmailByUserId(userId)); ExceptionUtil.isTrue(flag, DatabaseException.EDIT_ERROR); // Synchronize unbound member information email iMemberService.resetEmailByUserId(userId); @@ -502,9 +686,16 @@ public class UserServiceImpl extends ServiceImpl impleme loginUserCacheService.delete(userId); } + /** + * * + * @param userId User ID + * @param code Area code + * @param mobile Phone number + */ @Override @Transactional(rollbackFor = Exception.class) - public void updateMobileByUserId(Long userId, String code, String mobile) { + public void updateMobileByUserId(final Long userId, final String code, + final String mobile) { LoginUserDto userDto = loginUserCacheService.getLoginUser(userId); UserEntity updateUser = new UserEntity(); updateUser.setId(userId); @@ -514,27 +705,37 @@ public class UserServiceImpl extends ServiceImpl impleme ExceptionUtil.isTrue(flag, DatabaseException.EDIT_ERROR); // Synchronize the mobile number of member information iMemberService.updateMobileByUserId(userId, mobile); - // If the mobile phone number has been invited and no other account has been bound, activate the invited space member - List inactiveMembers = iMemberService.getInactiveMemberByEmails(mobile); + // If the mobile phone number has been invited + // and no other account has been bound, + // activate the invited space member + List inactiveMembers = + iMemberService.getInactiveMemberByEmails(mobile); this.inactiveMemberProcess(userId, inactiveMembers); // Delete Cache loginUserCacheService.delete(userId); - // Email registration is bound to mobile phones for the first time, and additional invitation rewards are given + // Email registration is bound to mobile phones for the first time, + // and additional invitation rewards are given if (userDto.getMobile() == null) { - TaskManager.me().execute(() -> { - userServiceFacade.rewardUserInfoUpdateAction(new RewardedUser(userId, userDto.getNickName())); - }); + TaskManager.me() .execute( + () -> userServiceFacade.rewardUserInfoUpdateAction( + new RewardedUser(userId, userDto.getNickName()))); } } + /** + * * + * @param userId User Id + */ @Override @Transactional(rollbackFor = Exception.class) - public void unbindMobileByUserId(Long userId) { - // The user needs to bind at least one contact (phone number, email) to unbind the mobile phone number + public void unbindMobileByUserId(final Long userId) { + // The user needs to bind at least one contact (phone number, email) + // to unbind the mobile phone number LoginUserDto userDto = loginUserCacheService.getLoginUser(userId); ExceptionUtil.isNotBlank(userDto.getEmail(), MUST_BIND_EAMIL); - boolean flag = SqlHelper.retBool(baseMapper.resetMobileByUserId(userId)); + boolean flag = + SqlHelper.retBool(baseMapper.resetMobileByUserId(userId)); ExceptionUtil.isTrue(flag, DatabaseException.EDIT_ERROR); // Synchronize the mobile phone number of unbinding member information iMemberService.resetMobileByUserId(userId); @@ -542,8 +743,12 @@ public class UserServiceImpl extends ServiceImpl impleme loginUserCacheService.delete(userId); } + /** + * * + * @param userId User ID + */ @Override - public void updateLoginTime(Long userId) { + public void updateLoginTime(final Long userId) { // Update the last login time UserEntity update = new UserEntity(); update.setId(userId); @@ -552,45 +757,62 @@ public class UserServiceImpl extends ServiceImpl impleme ExceptionUtil.isTrue(flag, SIGN_IN_ERROR); } + /** + * * + * @param userId User ID + * @param param Operate parameters + */ @Override @Transactional(rollbackFor = Exception.class) - public void edit(Long userId, UserOpRo param) { + public void edit(final Long userId, final UserOpRo param) { log.info("Edit user information"); UserEntity userEntity = getById(userId); ExceptionUtil.isNotNull(userEntity, USER_NOT_EXIST); UserEntity user = UserEntity.builder().id(userId).build(); - String waitDeleteOldAvatar = userEntity.getAvatar(); + String waitDeleteOldAvatar = null; if (StrUtil.isNotBlank(param.getAvatar())) { - user.setAvatar(param.getAvatar()); - user.setColor(null); + waitDeleteOldAvatar = userEntity.getAvatar(); + userMapper.updateUserAvatarInfo(userId, param.getAvatar(), null); } - else { - user.setAvatar(null); - user.setColor(param.getAvatarColor()); + if (ObjectUtil.isNotNull(param.getAvatarColor())) { + userMapper.updateUserAvatarInfo(userId, null, + param.getAvatarColor()); } if (StrUtil.isNotBlank(param.getLocale())) { - ExceptionUtil.isTrue(LanguageConstants.isLanguagesSupported(param.getLocale()), USER_LANGUAGE_SET_UN_SUPPORTED); + ExceptionUtil.isTrue( + LanguageConstants.isLanguagesSupported(param.getLocale()), + USER_LANGUAGE_SET_UN_SUPPORTED); user.setLocale(param.getLocale()); } if (StrUtil.isNotBlank(param.getNickName())) { - // Initialize the nickname. If there is a space "space of *** planet residents" registered and automatically created, synchronously modify the space name + // Initialize the nickname. + // If there is a space "space of *** planet residents" registered + // and automatically created, synchronously modify the space name if (BooleanUtil.isTrue(param.getInit())) { - String spaceId = spaceMapper.selectSpaceIdByUserIdAndName(userId, userEntity.getNickName()); + String spaceId = + spaceMapper.selectSpaceIdByUserIdAndName(userId, + userEntity.getNickName()); if (StrUtil.isNotBlank(spaceId)) { String spaceName = param.getNickName(); - if (LocaleContextHolder.getLocale().equals(LanguageManager.me().getDefaultLanguage())) { + if (LocaleContextHolder.getLocale() + .equals(LanguageManager.me().getDefaultLanguage())) { spaceName += SPACE_NAME_DEFAULT_SUFFIX; } - iSpaceService.updateSpace(userId, spaceId, SpaceUpdateOpRo.builder().name(spaceName).build()); + iSpaceService.updateSpace(userId, spaceId, + SpaceUpdateOpRo.builder().name(spaceName).build()); } } - // Synchronize personal nickname to member name that has not been modified - iMemberService.updateMemberNameByUserId(userId, param.getNickName()); + // Synchronize personal nickname to + // member name that has not been modifie + iMemberService.updateMemberNameByUserId(userId, + param.getNickName()); // Synchronously modify member 'Social Name Modified' field status memberMapper.updateSocialNameModifiedByUserId(userId); // Delete the space cache with modified member names TaskManager.me().execute(() -> { - List spaceIds = iMemberService.getSpaceIdWithoutNameModifiedByUserId(userId); + List spaceIds = + iMemberService.getSpaceIdWithoutNameModifiedByUserId( + userId); for (String spcId : spaceIds) { userSpaceCacheService.delete(userId, spcId); } @@ -598,22 +820,31 @@ public class UserServiceImpl extends ServiceImpl impleme user.setNickName(param.getNickName()) .setIsSocialNameModified(SocialNameModified.YES.getValue()); if (BooleanUtil.isTrue(param.getInit())) { - userServiceFacade.onUserChangeNicknameAction(userId, param.getNickName()); + userServiceFacade.onUserChangeNicknameAction(userId, + param.getNickName()); } + } else { + user.setNickName(userEntity.getNickName()); } boolean flag = updateById(user); ExceptionUtil.isTrue(flag, DatabaseException.EDIT_ERROR); // Delete Cache loginUserCacheService.delete(userId); - if (StrUtil.isNotBlank(waitDeleteOldAvatar) && StrUtil.startWith(waitDeleteOldAvatar, PUBLIC_PREFIX)) { + if (StrUtil.isNotBlank(waitDeleteOldAvatar) + && StrUtil.startWith(waitDeleteOldAvatar, PUBLIC_PREFIX)) { // Delete original cloud files iAssetService.delete(waitDeleteOldAvatar); } } + /** + * * + * @param id user id + * @param password New password set + */ @Override @Transactional(rollbackFor = Exception.class) - public void updatePwd(Long id, String password) { + public void updatePwd(final Long id, final String password) { log.info("Change Password"); UserEntity user = UserEntity.builder() .id(id) @@ -626,64 +857,87 @@ public class UserServiceImpl extends ServiceImpl impleme ExceptionUtil.isTrue(flag, MODIFY_PASSWORD_ERROR); } + /** + * * + * @param userId User ID + * @param spaceId Space ID + * @param filter Whether to filter space related information + * @return UserInfoVo + */ @Override - public UserInfoVo getCurrentUserInfo(Long userId, String spaceId, Boolean filter) { + public UserInfoVo getCurrentUserInfo(final Long userId, + final String spaceId, final Boolean filter) { log.info("Get user information and space content"); // Query the user's basic information // Whether the invitation code has been used for rewards - boolean usedInviteReward = userServiceFacade.getInvitationReward(userId); - UserInfoVo userInfo = UserInfoVo.builder().sendSubscriptionNotify(true) - .usedInviteReward(usedInviteReward) - .build(); + boolean usedInviteReward = + userServiceFacade.getInvitationReward(userId); + UserInfoVo userInfo = UserInfoVo.builder() + .sendSubscriptionNotify(true) + .usedInviteReward(usedInviteReward) + .build(); LoginUserDto loginUserDto = LoginContext.me().getLoginUser(); userInfo.transferDataFromLoginUserDto(loginUserDto); - UserLinkInfo userLinkInfo = userLinkServiceFacade.getUserLinkInfo(userId); + UserLinkInfo userLinkInfo = + userLinkServiceFacade.getUserLinkInfo(userId); if (userLinkInfo != null) { // Copy third-party account associated information - List userLinkVos = new ArrayList<>(userLinkInfo.getAccountLinkList().size()); + List userLinkVos = + new ArrayList<>(userLinkInfo.getAccountLinkList().size()); for (int i = 0; i < userLinkInfo.getAccountLinkList().size(); i++) { UserLinkVo linkVo = new UserLinkVo(); - BeanUtil.copyProperties(userLinkInfo.getAccountLinkList().get(i), linkVo); + BeanUtil.copyProperties( + userLinkInfo.getAccountLinkList().get(i), linkVo); userLinkVos.add(linkVo); } userInfo.transferDataFromDto(userLinkInfo, userLinkVos); - } - else { + } else { userInfo.setApiKey(iDeveloperService.getApiKeyByUserId(userId)); } - if (userInfo.getIsPaused()) { // Cancel the account during the calm period, and calculate the official cancellation time - UserHistoryEntity userHistory = iUserHistoryService - .getLatestUserHistoryEntity(userId, UserOperationType.APPLY_FOR_CLOSING); - ExceptionUtil.isNotNull(userHistory, UserClosingException.USER_HISTORY_RECORD_ISSUE); - userInfo.setCloseAt(userHistory.getCreatedAt().plusDays(30).withHour(0).withMinute(0).withSecond(0)); + // Cancel the account during the calm period, + // and calculate the official cancellation time + if (userInfo.getIsPaused()) { + UserHistoryEntity userHistory = + iUserHistoryService.getLatestUserHistoryEntity(userId, + UserOperationType.APPLY_FOR_CLOSING); + ExceptionUtil.isNotNull(userHistory, + UserClosingException.USER_HISTORY_RECORD_ISSUE); + userInfo.setCloseAt(userHistory.getCreatedAt() + .plusDays(USER_IS_PAUSED_CLOSE_DAY) + .withHour(0) + .withMinute(0) + .withSecond(0)); } boolean noSpace = StrUtil.isBlank(spaceId); + String spcId = spaceId; // Selectively filter spatial related information if (BooleanUtil.isTrue(filter)) { if (noSpace) { return userInfo; } - Long memberId = iMemberService.getMemberIdByUserIdAndSpaceId(userId, spaceId); + Long memberId = + iMemberService.getMemberIdByUserIdAndSpaceId(userId, spcId); if (ObjectUtil.isNull(memberId)) { return userInfo; } - } - else if (noSpace) { - // When the space ID is not transferred, obtain the space ID of the user's recent work - String activeSpaceId = userActiveSpaceCacheService.getLastActiveSpace(userId); + } else if (noSpace) { + // When the space ID is not transferred, + // obtain the space ID of the user's recent work + String activeSpaceId = + userActiveSpaceCacheService.getLastActiveSpace(userId); if (StrUtil.isBlank(activeSpaceId)) { return userInfo; } - spaceId = activeSpaceId; - } - else { + spcId = activeSpaceId; + } else { // Prevent access to not join spaces - userSpaceCacheService.getMemberId(userId, spaceId); + userSpaceCacheService.getMemberId(userId, spcId); } userInfo.setNeedCreate(false); // Cache session - UserSpaceDto userSpace = userSpaceCacheService.getUserSpace(userId, spaceId); + UserSpaceDto userSpace = + userSpaceCacheService.getUserSpace(userId, spcId); userInfo.setSpaceId(userSpace.getSpaceId()); userInfo.setSpaceName(userSpace.getSpaceName()); userInfo.setSpaceLogo(userSpace.getSpaceLogo()); @@ -693,12 +947,15 @@ public class UserServiceImpl extends ServiceImpl impleme userInfo.setIsAdmin(userSpace.isAdmin() || userSpace.isMainAdmin()); userInfo.setIsMainAdmin(userSpace.isMainAdmin()); userInfo.setIsDelSpace(userSpace.isDel()); - userInfo.setIsNewComer(!iMemberService.checkUserHasModifyNameInSpace(userId)); + userInfo.setIsNewComer( + !iMemberService.checkUserHasModifyNameInSpace(userId)); userInfo.setIsMemberNameModified(userSpace.getIsMemberNameModified()); // Get the last opened data table information - OpenedSheet openedSheet = userSpaceOpenedSheetCacheService.getOpenedSheet(userId, spaceId); - if (ObjectUtil.isNotNull(openedSheet) && ObjectUtil.isNotNull(openedSheet.getNodeId())) { + OpenedSheet openedSheet = + userSpaceOpenedSheetCacheService.getOpenedSheet(userId, spcId); + if (ObjectUtil.isNotNull(openedSheet) + && ObjectUtil.isNotNull(openedSheet.getNodeId())) { userInfo.setActiveNodeId(openedSheet.getNodeId()); userInfo.setActiveViewId(openedSheet.getViewId()); userInfo.setActiveNodePos(openedSheet.getPosition()); @@ -707,11 +964,18 @@ public class UserServiceImpl extends ServiceImpl impleme return userInfo; } + /** + * * + * @param userId User ID + * @param isRetain Whether to keep the current session + */ @Override - public void closeMultiSession(Long userId, boolean isRetain) { - Collection usersSessions = this.sessions.findByPrincipalName(userId.toString()).values(); + public void closeMultiSession(final Long userId, final boolean isRetain) { + Collection usersSessions = + this.sessions.findByPrincipalName(userId.toString()).values(); if (CollUtil.isNotEmpty(usersSessions)) { - List idList = usersSessions.stream().map(Session::getId).collect(Collectors.toList()); + List idList = usersSessions.stream() + .map(Session::getId).collect(Collectors.toList()); if (isRetain) { HttpSession httpSession = HttpContextUtil.getSession(false); if (httpSession != null) { @@ -724,19 +988,33 @@ public class UserServiceImpl extends ServiceImpl impleme } } + /** + * * + * @param userId User ID + * @return uuid + */ @Override - public String getUuidByUserId(Long userId) { + public String getUuidByUserId(final Long userId) { return baseMapper.selectUuidById(userId); } + /** + * * + * @param userId user id + * @return Nickname + */ @Override - public String getNicknameByUserId(Long userId) { + public String getNicknameByUserId(final Long userId) { return baseMapper.selectNickNameById(userId); } + /** + * * + * @param user User + */ @Override @Transactional(rollbackFor = Exception.class) - public void applyForClosingAccount(UserEntity user) { + public void applyForClosingAccount(final UserEntity user) { // Update the user logoff cool down period status to Yes updateIsPaused(user.getId(), true); // Add User Operation Record @@ -751,29 +1029,35 @@ public class UserServiceImpl extends ServiceImpl impleme if (members.size() == 0) { return; } - List memberIds = members.stream().map(MemberEntity::getId).collect(Collectors.toList()); + List memberIds = members.stream() + .map(MemberEntity::getId).collect(Collectors.toList()); spaceInviteLinkService.deleteByMemberIds(memberIds); // Logical delete member information iMemberService.preDelByMemberIds(memberIds); - // notify the main admin about this member is going to close his account. - List spaceIds = members.stream().map(MemberEntity::getSpaceId).collect(Collectors.toList()); + // notify the main admin about + // this member is going to close his account. + List spaceIds = members.stream() + .map(MemberEntity::getSpaceId).collect(Collectors.toList()); List spaces = iSpaceService.getBySpaceIds(spaceIds); if (spaces.size() == 0) { return; } - List notificationCreateRos = genNotificationCreateRos(user, spaces); + List notificationCreateRos = + genNotificationCreateRos(user, spaces); notificationService.batchCreateNotify(notificationCreateRos); } /** - * Encapsulate Notification to notify the master administrator that the member has applied for logoff + * Encapsulate Notification to notify the master administrator + * * that the member has applied for logoff. * - * @param user User + * @param user User * @param spaces Space List * @return NotificationCreateRo List */ - private List genNotificationCreateRos(UserEntity user, List spaces) { + private List genNotificationCreateRos( + final UserEntity user, final List spaces) { List ros = Lists.newArrayList(); spaces.forEach(spaceEntity -> { NotificationCreateRo notifyRo = new NotificationCreateRo(); @@ -782,30 +1066,38 @@ public class UserServiceImpl extends ServiceImpl impleme notifyRo.setToMemberId(Lists.newArrayList(memberId)); notifyRo.setFromUserId(String.valueOf(user.getId())); Dict extras = Dict.create().set("nickName", user.getNickName()); - JSONObject data = JSONUtil.createObj().putOnce(NotificationConstants.BODY_EXTRAS, extras) - .set("nickName", user.getNickName()); + JSONObject data = JSONUtil.createObj() + .putOnce(NotificationConstants.BODY_EXTRAS, extras) + .set("nickName", user.getNickName()); notifyRo.setBody(data); - notifyRo.setTemplateId(NotificationTemplateId.MEMBER_APPLIED_TO_CLOSE_ACCOUNT.getValue()); + notifyRo.setTemplateId(NotificationTemplateId + .MEMBER_APPLIED_TO_CLOSE_ACCOUNT.getValue()); ros.add(notifyRo); }); return ros; } - private void updateIsPaused(Long userId, boolean isPaused) { + private void updateIsPaused(final Long userId, final boolean isPaused) { UserEntity userPaused = UserEntity.builder() .id(userId).isPaused(isPaused).build(); baseMapper.updateById(userPaused); } + /** + * * + * @param user UserEntity + */ @Override @Transactional(rollbackFor = Exception.class) - public void cancelClosingAccount(UserEntity user) { + public void cancelClosingAccount(final UserEntity user) { // Update the user to log off the cool down period status to No updateIsPaused(user.getId(), false); - // Get the member information that has not been logically deleted and the exceptions caused by compatible address book synchronization - List unexpectedMembers = iMemberService.getByUserId(user.getId()); - List unexpectedMemberIds = unexpectedMembers.stream().map(MemberEntity::getId) - .collect(Collectors.toList()); + // Get the member information that has not been logically deleted + // and the exceptions caused by compatible address book synchronization + List unexpectedMembers = + iMemberService.getByUserId(user.getId()); + List unexpectedMemberIds = unexpectedMembers.stream() + .map(MemberEntity::getId).collect(Collectors.toList()); // Logical deletion of abnormal member information if (unexpectedMemberIds.size() > 0) { memberMapper.deleteBatchByIds(unexpectedMemberIds); @@ -816,23 +1108,31 @@ public class UserServiceImpl extends ServiceImpl impleme loginUserCacheService.delete(user.getId()); userActiveSpaceCacheService.delete(user.getId()); // Add User Operation Record - UserHistoryEntity userHistory = UserHistoryEntity.builder().userId(user.getId()) - .userStatus(UserOperationType.CANCEL_CLOSING.getStatusCode()) - .uuid(user.getUuid()) - .build(); + UserHistoryEntity userHistory = UserHistoryEntity.builder() + .userId(user.getId()) + .userStatus(UserOperationType.CANCEL_CLOSING.getStatusCode()) + .uuid(user.getUuid()) + .build(); iUserHistoryService.create(userHistory); } + /** + * * + * @param user UserEntity + */ @Override @Transactional(rollbackFor = Exception.class) - public void closeAccount(UserEntity user) { - // Clear the user's nickname, area code, mobile phone and email information + public void closeAccount(final UserEntity user) { + // Clear the user's nickname, area code, + // mobile phone and email information userMapper.resetUserById(user.getId()); // Clear the user's information in the member table memberMapper.clearMemberInfoByUserId(user.getId()); - // Physically delete the user's third-party association binding information + // Physically delete + // the user's third-party association binding information socialServiceFacade.deleteUser(user.getId()); - // Write the "Logout Completed" record to the history table. 0 represents the system user + // Write the "Logout Completed" record to the history table. + // 0 represents the system user UserHistoryEntity userHistory = UserHistoryEntity.builder() .userId(user.getId()) .uuid(user.getUuid()) @@ -843,12 +1143,18 @@ public class UserServiceImpl extends ServiceImpl impleme iUserHistoryService.create(userHistory); } + /** + * * + * @param userIds User ID List + * @return List + */ @Override - public List getPausedUserDtos(List userIds) { + public List getPausedUserDtos(final List userIds) { return userMapper.selectPausedUsers(userIds); } - private boolean inactiveMemberProcess(Long userId, List inactiveMembers) { + private boolean inactiveMemberProcess(final Long userId, + final List inactiveMembers) { if (CollUtil.isEmpty(inactiveMembers)) { return false; } @@ -858,15 +1164,16 @@ public class UserServiceImpl extends ServiceImpl impleme List spaceIds = iMemberService.getSpaceIdByUserId(userId); inactiveMembers.forEach(member -> { if (spaceIds.contains(member.getSpaceId())) { - // An inactive member already exists in the user space. Delete the inactive member + // An inactive member already exists in the user space. + // Delete the inactive member delMember.add(member.getId()); - } - else { + } else { activateMember.add(member.getId()); } }); if (CollUtil.isNotEmpty(activateMember)) { - // Activate members of the invited space and synchronize user information + // Activate members of the invited space + // and synchronize user information UserEntity entity = getById(userId); if (entity != null) { List updateMembers = new ArrayList<>(); @@ -890,32 +1197,33 @@ public class UserServiceImpl extends ServiceImpl impleme return true; } - private String getRandomAvatar() { - String defaultAvatarList = constProperties.getDefaultAvatarList(); - if (StrUtil.isBlank(defaultAvatarList)) { - return null; - } - String[] splits = defaultAvatarList.split(","); - if (splits.length == 0) { - return null; - } - return splits[RandomUtil.randomInt(0, splits.length)]; - } - + /** + * * + * @param expectedLang The user did not set the system language. + * @param emails Email list + * @return List + */ @Override - public List getLangByEmails(String expectedLang, List emails) { + public List getLangByEmails(final String expectedLang, + final List emails) { // Maybe have performance problems, the segmented query is used. List userLangs = new ArrayList<>(emails.size()); - int page = PageUtil.totalPage(emails.size(), QUERY_LOCALE_IN_EMAILS_LIMIT); + int page = + PageUtil.totalPage(emails.size(), QUERY_LOCALE_IN_EMAILS_LIMIT); for (int i = 0; i < page; i++) { - List subEmails = CollUtil.page(i, QUERY_LOCALE_IN_EMAILS_LIMIT, emails); - userLangs.addAll(userMapper.selectLocaleInEmailsWithDefaultLocale(expectedLang, subEmails)); + List subEmails = + CollUtil.page(i, QUERY_LOCALE_IN_EMAILS_LIMIT, emails); + userLangs.addAll( + userMapper.selectLocaleInEmailsWithDefaultLocale(expectedLang, + subEmails)); } // Add an email that is not in the database if (userLangs.size() != emails.size()) { // Generally, they will not enter this judgment - List existEmails = userLangs.stream().map(UserLangDTO::getEmail).collect(Collectors.toList()); - List nonExistEmails = CollUtil.subtractToList(emails, existEmails); + List existEmails = userLangs.stream() + .map(UserLangDTO::getEmail).collect(Collectors.toList()); + List nonExistEmails = + CollUtil.subtractToList(emails, existEmails); nonExistEmails.forEach(email -> { UserLangDTO userLangDTO = new UserLangDTO(); userLangDTO.setLocale(expectedLang); @@ -926,17 +1234,33 @@ public class UserServiceImpl extends ServiceImpl impleme return userLangs; } + /** + * * + * @param expectedLang The user did not set the system language. + * @param email Email + * @return The email sending language + */ @Override - public String getLangByEmail(String expectedLang, String email) { - UserLangDTO userLangDTO = userMapper.selectLocaleByEmailWithDefaultLocale(expectedLang, email); + public String getLangByEmail( + final String expectedLang, final String email) { + UserLangDTO userLangDTO = + userMapper.selectLocaleByEmailWithDefaultLocale(expectedLang, + email); if (ObjectUtil.isNotNull(userLangDTO)) { return userLangDTO.getLocale(); } return expectedLang; } + /** + * * + * @param userIds User ID + * @param defaultLocale Default Language + * @return List + */ @Override - public List getLangAndEmailByIds(List userIds, String defaultLocale) { + public List getLangAndEmailByIds( + final List userIds, final String defaultLocale) { List dtos = userMapper.selectLocaleAndEmailByIds(userIds); return dtos.stream().peek(v -> { if (StrUtil.isBlank(v.getLocale())) { @@ -945,21 +1269,28 @@ public class UserServiceImpl extends ServiceImpl impleme }).collect(Collectors.toList()); } + /** + * * + * @param uuid User uuid + * @return user id + */ @Override - public Long getUserIdByUuid(String uuid) { + public Long getUserIdByUuid(final String uuid) { return userMapper.selectIdByUuid(uuid); } - private String nullToDefaultNickName(String nickName, String mobileOrEmailPrefix) { + private String nullToDefaultNickName( + final String nickName, final String mobileOrEmailPrefix + ) { return StrUtil.blankToDefault(nickName, mobileOrEmailPrefix); } - private String nullToDefaultAvatar(String avatar) { + private String nullToDefaultAvatar(final String avatar) { return StrUtil.blankToDefault(avatar, null); } /** - * Query users by username + * Query users by username. * User's name can be email or area code+mobile phone number * * @param areaCode Area code @@ -967,21 +1298,22 @@ public class UserServiceImpl extends ServiceImpl impleme * @return UserEntity */ @Override - public UserEntity getByUsername(String areaCode, String username) { + public UserEntity getByUsername( + final String areaCode, final String username + ) { if (Validator.isEmail(username)) { // Judge whether it exists UserEntity user = this.getByEmail(username); ExceptionUtil.isNotNull(user, USERNAME_OR_PASSWORD_ERROR); return user; - } - else if (StrUtil.isNotBlank(areaCode)) { - ExceptionUtil.isTrue(Validator.isNumber(username), USERNAME_OR_PASSWORD_ERROR); + } else if (StrUtil.isNotBlank(areaCode)) { + ExceptionUtil.isTrue(Validator.isNumber(username), + USERNAME_OR_PASSWORD_ERROR); // Judge whether it exists UserEntity user = this.getByCodeAndMobilePhone(areaCode, username); ExceptionUtil.isNotNull(user, USERNAME_OR_PASSWORD_ERROR); return user; - } - else { + } else { // User name format error throw new BusinessException(USERNAME_OR_PASSWORD_ERROR); } diff --git a/backend-server/application/src/main/java/com/apitable/user/service/impl/package-info.java b/backend-server/application/src/main/java/com/apitable/user/service/impl/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..2c04bcae2370089c93d247dc65586102e1010c0b --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/user/service/impl/package-info.java @@ -0,0 +1,23 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/** + * + * @author Chambers + */ +package com.apitable.user.service.impl; diff --git a/backend-server/application/src/main/java/com/apitable/widget/controller/WidgetController.java b/backend-server/application/src/main/java/com/apitable/widget/controller/WidgetController.java new file mode 100644 index 0000000000000000000000000000000000000000..ec4f9ab4e244b4c96e1d66208dff997b6b6d2604 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/controller/WidgetController.java @@ -0,0 +1,225 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.controller; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +import javax.annotation.Resource; +import javax.validation.Valid; + +import cn.hutool.core.util.StrUtil; +import com.apitable.base.enums.ParameterException; +import com.apitable.control.infrastructure.ControlTemplate; +import com.apitable.control.infrastructure.permission.NodePermission; +import com.apitable.core.support.ResponseData; +import com.apitable.core.util.ExceptionUtil; +import com.apitable.widget.ro.WidgetStoreListRo; +import com.apitable.widget.vo.WidgetStoreListInfo; +import com.apitable.shared.cache.service.UserSpaceCacheService; +import com.apitable.shared.component.scanner.annotation.ApiResource; +import com.apitable.shared.component.scanner.annotation.GetResource; +import com.apitable.shared.component.scanner.annotation.PostResource; +import com.apitable.shared.constants.ParamsConstants; +import com.apitable.shared.context.LoginContext; +import com.apitable.shared.context.SessionContext; +import com.apitable.space.service.ISpaceService; +import com.apitable.widget.enums.WidgetException; +import com.apitable.widget.mapper.WidgetMapper; +import com.apitable.widget.mapper.WidgetPackageMapper; +import com.apitable.widget.ro.WidgetCopyRo; +import com.apitable.widget.ro.WidgetCreateRo; +import com.apitable.widget.service.IWidgetService; +import com.apitable.widget.vo.WidgetInfo; +import com.apitable.widget.vo.WidgetPack; +import com.apitable.widget.vo.WidgetTemplatePackageInfo; +import com.apitable.workspace.service.INodeService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; + +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import static com.apitable.workspace.enums.PermissionException.NODE_OPERATION_DENIED; + +@RestController +@Api(tags = "Widget SDK - Widget Api") +@ApiResource(path = "/") +public class WidgetController { + + @Resource + private ISpaceService iSpaceService; + + @Resource + private INodeService iNodeService; + + @Resource + private ControlTemplate controlTemplate; + + @Resource + private UserSpaceCacheService userSpaceCacheService; + + @Resource + private IWidgetService iWidgetService; + + @Resource + private WidgetPackageMapper widgetPackageMapper; + + @Resource + private WidgetMapper widgetMapper; + + @PostResource(path = "/widget/package/store/list", requiredPermission = false) + @ApiOperation(value = "Get widget store") + @ApiImplicitParam(name = ParamsConstants.SPACE_ID, value = "space id", required = true, dataTypeClass = String.class, paramType = "header", example = "spczJrh2i3tLW") + public ResponseData> widgetStoreList(@RequestBody @Valid WidgetStoreListRo storeListRo) { + Long userId = SessionContext.getUserId(); + String spaceId = LoginContext.me().getSpaceId(); + String userLocale = LocaleContextHolder.getLocale().toLanguageTag(); + storeListRo.setLanguage(Optional.ofNullable(storeListRo.getLanguage()).orElse(userLocale)); + List infos = iWidgetService.widgetStoreList(userId, spaceId, storeListRo); + return ResponseData.success(infos); + } + + @GetResource(path = "/space/{spaceId}/widget", requiredPermission = false) + @ApiOperation(value = "Get the space widgets", notes = "get the widgets under the entire space") + @ApiImplicitParams({ + @ApiImplicitParam(name = "spaceId", value = "space id", required = true, dataTypeClass = String.class, paramType = "path", example = "spczJrh2i3tLW"), + @ApiImplicitParam(name = "count", value = "load quantity", dataTypeClass = Integer.class, paramType = "query", example = "10") + }) + public ResponseData> findWidgetInfoBySpaceId(@PathVariable("spaceId") String spaceId, + @RequestParam(value = "count", required = false, defaultValue = "10") Integer count) { + Long userId = SessionContext.getUserId(); + Long memberId = userSpaceCacheService.getMemberId(userId, spaceId); + List infos = iWidgetService.getWidgetInfoList(spaceId, memberId, count); + return ResponseData.success(infos); + } + + @GetResource(path = "/node/{nodeId}/widget", requiredPermission = false) + @ApiOperation(value = "get the widget information of the node") + @ApiImplicitParam(name = "nodeId", value = "node id", required = true, dataTypeClass = String.class, paramType = "path", example = "dstJ2oRZxsh2yld4MA") + public ResponseData> findWidgetInfoByNodeId(@PathVariable("nodeId") String nodeId) { + Long userId = SessionContext.getUserId(); + // Determine whether the node does not exist or cross-spatial access + String spaceId = iNodeService.getSpaceIdByNodeId(nodeId); + Long memberId = userSpaceCacheService.getMemberId(userId, spaceId); + // check permission + controlTemplate.checkNodePermission(memberId, nodeId, NodePermission.READ_NODE, + status -> ExceptionUtil.isTrue(status, NODE_OPERATION_DENIED)); + List infos = widgetMapper.selectInfoByNodeId(nodeId); + return ResponseData.success(infos); + } + + @GetResource(path = "/node/{nodeId}/widgetPack", requiredLogin = false) + @ApiOperation(value = "Get the node widget package", notes = "Node types are limited to dashboards and datasheet") + @ApiImplicitParams({ + @ApiImplicitParam(name = ParamsConstants.SPACE_ID, value = "space id", dataTypeClass = String.class, paramType = "header", example = "spczJrh2i3tLW"), + @ApiImplicitParam(name = "nodeId", value = "node id", required = true, dataTypeClass = String.class, paramType = "path", example = "dstJ2oRZxsh2yld4MA"), + @ApiImplicitParam(name = "linkId", value = "association id:node share id、template id", dataTypeClass = String.class, paramType = "query", example = "shr8T8vAfehg3yj3McmDG") + }) + public ResponseData> findWidgetPackByNodeId(@PathVariable("nodeId") String nodeId, + @RequestParam(value = "linkId", required = false) String linkId) { + // Determine whether the node does not exist or cross-spatial access + String nodeSpaceId = iNodeService.checkNodeIfExist(null, nodeId); + if (StrUtil.isBlank(linkId)) { + // check permission + Long userId = SessionContext.getUserId(); + Long memberId = userSpaceCacheService.getMemberId(userId, nodeSpaceId); + controlTemplate.checkNodePermission(memberId, nodeId, NodePermission.READ_NODE, + status -> ExceptionUtil.isTrue(status, NODE_OPERATION_DENIED)); + } + else { + // out of station access + String spaceId = iSpaceService.getSpaceIdByLinkId(linkId); + ExceptionUtil.isTrue(nodeSpaceId.equals(spaceId), WidgetException.WIDGET_SPACE_ERROR); + } + // get all components under the node + List widgetIds = widgetMapper.selectWidgetIdsByNodeId(nodeId); + return ResponseData.success(iWidgetService.getWidgetPackList(widgetIds)); + } + + @GetResource(path = "/widget/get", requiredLogin = false) + @ApiOperation(value = "Get widget info", notes = "get widget info by widget id") + @ApiImplicitParams({ + @ApiImplicitParam(name = "widgetIds", value = "widget ids", required = true, dataTypeClass = String.class, paramType = "query", example = "wdtlMDweJzTsbSJAFY,wdt923ZpvvRhD8kVLs"), + @ApiImplicitParam(name = "linkId", value = "Association ID: node sharing ID and template ID", dataTypeClass = String.class, paramType = "query", example = "shr8T8vAfehg3yj3McmDG") + }) + public ResponseData> findWidgetPackByWidgetIds(@RequestParam("widgetIds") List widgetIds, + @RequestParam(value = "linkId", required = false) String linkId) { + ExceptionUtil.isNotEmpty(widgetIds, ParameterException.INCORRECT_ARG); + String widgetSpaceId = iWidgetService.checkByWidgetIds(widgetIds); + if (StrUtil.isBlank(linkId)) { + Long userId = SessionContext.getUserId(); + // prevent access to unadded spaces + userSpaceCacheService.getMemberId(userId, widgetSpaceId); + } + else { + // out of station access + String spaceId = iSpaceService.getSpaceIdByLinkId(linkId); + ExceptionUtil.isTrue(widgetSpaceId.equals(spaceId), WidgetException.WIDGET_SPACE_ERROR); + } + return ResponseData.success(iWidgetService.getWidgetPackList(widgetIds)); + } + + @PostResource(path = "/widget/create", requiredPermission = false) + @ApiOperation(value = "Create widget", notes = "Scenario:1、dashboard new applet 2、datasheet widget panel new widget") + public ResponseData createWidget(@RequestBody @Valid WidgetCreateRo widget) { + Long userId = SessionContext.getUserId(); + // The method includes determining whether a node exists. + String spaceId = iNodeService.getSpaceIdByNodeId(widget.getNodeId()); + // The method includes determining whether the user is in this space. + Long memberId = LoginContext.me().getMemberId(userId, spaceId); + // check permission + controlTemplate.checkNodePermission(memberId, widget.getNodeId(), NodePermission.MANAGE_NODE, + status -> ExceptionUtil.isTrue(status, NODE_OPERATION_DENIED)); + // create widget + String widgetId = iWidgetService.create(userId, spaceId, widget); + return ResponseData.success(iWidgetService.getWidgetPack(widgetId)); + } + + @PostResource(path = "/widget/copy", requiredPermission = false) + @ApiOperation(value = "Copy widget", notes = "Scenario: 1、dashboard import widget 2、the widget panel sends applets to the dashboard") + public ResponseData> copyWidget(@RequestBody @Valid WidgetCopyRo widgetRo) { + Long userId = SessionContext.getUserId(); + // The method includes determining whether a node exists. + String spaceId = iNodeService.getSpaceIdByNodeId(widgetRo.getDashboardId()); + // The method includes determining whether the user is in this space. + Long memberId = LoginContext.me().getMemberId(userId, spaceId); + // check permission + controlTemplate.checkNodePermission(memberId, widgetRo.getDashboardId(), NodePermission.MANAGE_NODE, + status -> ExceptionUtil.isTrue(status, NODE_OPERATION_DENIED)); + // copy widget + Collection widgetIds = iWidgetService.copyToDashboard(userId, spaceId, widgetRo.getDashboardId(), widgetRo.getWidgetIds()); + return ResponseData.success(iWidgetService.getWidgetPackList(widgetIds)); + } + + @GetResource(path = "/widget/template/package/list", requiredPermission = false) + @ApiOperation(value = "Get package teamplates") + public ResponseData> findTemplatePackageList() { + String userLocale = LocaleContextHolder.getLocale().toLanguageTag(); + List data = widgetPackageMapper.selectWidgetTemplatePackageList(userLocale); + return ResponseData.success(data); + } + +} diff --git a/backend-server/application/src/main/java/com/apitable/widget/dto/NodeWidgetDto.java b/backend-server/application/src/main/java/com/apitable/widget/dto/NodeWidgetDto.java new file mode 100644 index 0000000000000000000000000000000000000000..5cbc235c5582e478a330e211ada4bb92502c3727 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/dto/NodeWidgetDto.java @@ -0,0 +1,40 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.dto; + +import lombok.Data; + +@Data +public class NodeWidgetDto { + + /** + * widget name + */ + private String widgetName; + + /** + * widget reference datasheet + */ + private String dstId; + + /** + * node id + */ + private String nodeId; +} diff --git a/backend-server/application/src/main/java/com/apitable/widget/dto/WidgetBaseInfo.java b/backend-server/application/src/main/java/com/apitable/widget/dto/WidgetBaseInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..699973182854db54d1e0266730b2586d44586189 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/dto/WidgetBaseInfo.java @@ -0,0 +1,37 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.dto; + +import lombok.Data; + +@Data +public class WidgetBaseInfo { + + private String nodeId; + + private String widgetPackageId; + + private String widgetId; + + private String name; + + private String storage; + + private Long revision; +} diff --git a/backend-server/application/src/main/java/com/apitable/widget/dto/WidgetDTO.java b/backend-server/application/src/main/java/com/apitable/widget/dto/WidgetDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..f1175e474ee4adc572b446e3587e2c9b65769411 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/dto/WidgetDTO.java @@ -0,0 +1,31 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.dto; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import com.apitable.workspace.dto.DatasheetWidgetDTO; + +@Data +@EqualsAndHashCode(callSuper = true) +public class WidgetDTO extends DatasheetWidgetDTO { + + private String nodeId; +} diff --git a/backend-server/application/src/main/java/com/apitable/widget/dto/WidgetPackageDTO.java b/backend-server/application/src/main/java/com/apitable/widget/dto/WidgetPackageDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..39838b37e3c56b9b925ce147c7b0edb41247f5e9 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/dto/WidgetPackageDTO.java @@ -0,0 +1,65 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.dto; + +import lombok.Data; + +@Data +public class WidgetPackageDTO { + + private Long id; + + private String packageId; + + private String name; + + private String description; + + private String icon; + + private String cover; + + private Integer status; + + private Integer installedNum; + + private String authorName; + + private String authorIcon; + + private String authorEmail; + + private String authorLink; + + private Integer packageType; + + private Integer releaseType; + + private String version; + + private String releaseCodeBundle; + + private Boolean sandbox; + + private String fatherWidgetId; + + private String installEnvCode; + + private String runtimeEnvCode; +} diff --git a/backend-server/application/src/main/java/com/apitable/widget/dto/WidgetSpaceByDTO.java b/backend-server/application/src/main/java/com/apitable/widget/dto/WidgetSpaceByDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..3572629eb9dd922217a6d8653a24555e90359562 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/dto/WidgetSpaceByDTO.java @@ -0,0 +1,37 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.dto; + +import lombok.Data; + +@Data +public class WidgetSpaceByDTO { + + private String spaceId; + + private String authorName; + + private String authorIcon; + + private String ownerUuid; + + private String owner; + + private String ownerMemberId; +} diff --git a/backend-server/application/src/main/java/com/apitable/widget/entity/WidgetEntity.java b/backend-server/application/src/main/java/com/apitable/widget/entity/WidgetEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..7704a0866789926273bd72de9b4fa827c1a40ece --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/entity/WidgetEntity.java @@ -0,0 +1,125 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.entity; + +import java.io.Serializable; +import java.time.LocalDateTime; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + *

+ * Workbench - Widget Table + *

+ * + * @author Mybatis Generator Tool + */ +@Data +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@EqualsAndHashCode +@TableName(keepGlobalPrefix = true, value = "widget") +public class WidgetEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * Primary Key + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + + /** + * Space ID + */ + private String spaceId; + + /** + * Node ID + */ + private String nodeId; + + /** + * Widget ID(link#xxxx_widget_package#package_id) + */ + private String packageId; + + /** + * Customized Widget ID + */ + private String widgetId; + + /** + * Name + */ + private String name; + + /** + * Storage configuration + */ + private String storage; + + /** + * Version + */ + private Long revision; + + /** + * Delete Tag(0: No, 1: Yes) + */ + @TableLogic + private Integer isDeleted; + + /** + * Creator + */ + @TableField(fill = FieldFill.INSERT) + private Long createdBy; + + /** + * Last Update By + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Long updatedBy; + + /** + * Create Time + */ + private LocalDateTime createdAt; + + /** + * Update Time + */ + private LocalDateTime updatedAt; + + +} diff --git a/backend-server/application/src/main/java/com/apitable/widget/entity/WidgetPackageAuthSpaceEntity.java b/backend-server/application/src/main/java/com/apitable/widget/entity/WidgetPackageAuthSpaceEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..21499534cb8492655de4a59b10c0ff05aaaaa056 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/entity/WidgetPackageAuthSpaceEntity.java @@ -0,0 +1,110 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.entity; + +import java.io.Serializable; +import java.time.LocalDateTime; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + *

+ * Workbench - Widget Package Auth Space Table + *

+ * + * @author Mybatis Generator Tool + */ +@Data +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@EqualsAndHashCode +@TableName(keepGlobalPrefix = true, value = "widget_package_auth_space") +public class WidgetPackageAuthSpaceEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * Primary Key + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + + /** + * Package ID + */ + private String packageId; + + /** + * Space ID(link#xxxx_space#space_id) + */ + private String spaceId; + + /** + * Widget package authorization type (0: binding space - cannot be deleted, the same widget package can be jointly managed by the "development permission" administrator of the space; global widgets can also be used for upgrading and other needs; 1: authorized space - only space station widgets can be used for authorizing other spaces) + */ + private Integer type; + + /** + * Sequence number, space station components start from 10000 + */ + private Integer widgetSort; + + /** + * Delete Tag(0: No, 1: Yes) + */ + @TableLogic + private Boolean isDeleted; + + /** + * Creator + */ + @TableField(fill = FieldFill.INSERT) + private Long createdBy; + + /** + * Last Update By + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Long updatedBy; + + /** + * Create Time + */ + private LocalDateTime createdAt; + + /** + * Update Time + */ + private LocalDateTime updatedAt; + + +} diff --git a/backend-server/application/src/main/java/com/apitable/widget/entity/WidgetPackageEntity.java b/backend-server/application/src/main/java/com/apitable/widget/entity/WidgetPackageEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..cbd7bb21c995bb002d176030f5a47528cbcdb84f --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/entity/WidgetPackageEntity.java @@ -0,0 +1,215 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.entity; + +import java.io.Serializable; +import java.time.LocalDateTime; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + *

+ * Workbench - Widget Package Table + *

+ * + * @author Mybatis Generator Tool + */ +@Data +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@EqualsAndHashCode +@TableName(keepGlobalPrefix = true, value = "widget_package") +public class WidgetPackageEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * Primary Key + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + + /** + * Widget ID + */ + private String packageId; + + /** + * Internationalized widget name + */ + private String i18nName; + + /** + * Internationalization Widget Description + */ + private String i18nDescription; + + /** + * Icon + */ + private String icon; + + /** + * Cover draw TOKEN + */ + private String cover; + + /** + * Status (0: under development, 1: banned, 2: to be published, 3: published, 4: off the shelf - global temporarily closed) 3, 4 + */ + private Integer status; + + /** + * Number of installations + */ + private Integer installedNum; + + /** + * Name - 【Discard Delete】 + */ + private String name; + + /** + * English name - 【Discard Delete】 + */ + private String nameEn; + + /** + * Version - 【Discard Delete】 + */ + private String version; + + /** + * Description - 【Discard Delete】 + */ + private String description; + + /** + * Author Name + */ + private String authorName; + + /** + * Author email + */ + private String authorEmail; + + /** + * Author icon TOKEN + */ + private String authorIcon; + + /** + * Author website address + */ + private String authorLink; + + /** + * Widget package type (0: third party, 1: official) + */ + private Integer packageType; + + /** + * 0: Publish to the component store in the space station, 1: Publish to the global application store (only allowed if the package_type is 0) + */ + private Integer releaseType; + + /** + * Widget package extension information + */ + private String widgetBody; + + /** + * Whether the sandbox runs (0: No, 1: Yes) + */ + private Boolean sandbox; + + /** + * The release version ID, the currently active version, can be empty. When it is empty, it is only displayed to Creator in the build store + */ + private Long releaseId; + + /** + * Is template (0: No, 1: Yes) + */ + private Boolean isTemplate; + + /** + * Enable or not, only for global widgets (0: not enabled, 1: enabled) + */ + private Boolean isEnabled; + + /** + * Delete Tag(0: No, 1: Yes) + */ + @TableLogic + private Boolean isDeleted; + + /** + * Owner Id(link#xxxx_user#id) + */ + private Long owner; + + /** + * Creator + */ + @TableField(fill = FieldFill.INSERT) + private Long createdBy; + + /** + * Last Update By + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Long updatedBy; + + /** + * Create Time + */ + private LocalDateTime createdAt; + + /** + * Update Time + */ + private LocalDateTime updatedAt; + + /** + * Installation environment code + */ + private String installEnvCode; + + /** + * Operate environment code + */ + private String runtimeEnvCode; + + +} diff --git a/backend-server/application/src/main/java/com/apitable/widget/entity/WidgetPackageReleaseEntity.java b/backend-server/application/src/main/java/com/apitable/widget/entity/WidgetPackageReleaseEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..2e51af561e9814ed4684c55f060911e3f3c7d90c --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/entity/WidgetPackageReleaseEntity.java @@ -0,0 +1,145 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.entity; + +import java.io.Serializable; +import java.time.LocalDateTime; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + *

+ * Workbench-Widget Package Release Table + *

+ * + * @author Mybatis Generator Tool + */ +@Data +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@EqualsAndHashCode +@TableName(keepGlobalPrefix = true, value = "widget_package_release") +public class WidgetPackageReleaseEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * Primary Key + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + + /** + * Version Summary Unique ID(id+package_id+version generate) + */ + private String releaseSha; + + /** + * Version number, unique under package id + */ + private String version; + + /** + * Widget Package ID + */ + private String packageId; + + /** + * User ID(link#xxxx_user#id) + */ + private Long releaseUserId; + + /** + * Release Code Bundle + */ + private String releaseCodeBundle; + + /** + * Source Code Bundle + */ + private String sourceCodeBundle; + + /** + * Source code encryption key + */ + private String secretKey; + + /** + * Status (0: to be approved, 1: approved, 2: rejected) + */ + private Integer status; + + /** + * Release Version Description + */ + private String releaseNote; + + /** + * Delete Tag(0: No, 1: Yes) + */ + @TableLogic + private Boolean isDeleted; + + /** + * Creator + */ + @TableField(fill = FieldFill.INSERT) + private Long createdBy; + + /** + * Last Update By + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Long updatedBy; + + /** + * Create Time + */ + private LocalDateTime createdAt; + + /** + * Update Time + */ + private LocalDateTime updatedAt; + + /** + * Installation environment code + */ + private String installEnvCode; + + /** + * Operate environment code + */ + private String runtimeEnvCode; + + +} diff --git a/backend-server/application/src/main/java/com/apitable/widget/enums/InstallEnvType.java b/backend-server/application/src/main/java/com/apitable/widget/enums/InstallEnvType.java new file mode 100644 index 0000000000000000000000000000000000000000..3eca02243134094b37a7e0238f0609fb36da85bd --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/enums/InstallEnvType.java @@ -0,0 +1,93 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.enums; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import cn.hutool.core.util.StrUtil; +import com.apitable.core.exception.BusinessException; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + *

+ * Mini Program Installation Environment Type + *

+ * + * @author liuzijing + */ +@Getter +@AllArgsConstructor +public enum InstallEnvType { + + DASHBOARD("dashboard", "01"), + + PANEL("panel", "02"), + + ; + + @JsonValue + private final String value; + + private final String code; + + public static InstallEnvType toType(String value) { + for (InstallEnvType type : InstallEnvType.values()) { + if (type.getValue().equals(value)) { + return type; + } + } + throw new BusinessException("Unknown installation environment type type"); + } + + public static List toValueList(String codes) { + List installEnvTypes = InstallEnvType.toTypeList(codes); + List list = new ArrayList<>(); + for (InstallEnvType installEnvType : installEnvTypes) { + list.add(installEnvType.value); + } + return list; + } + + public static List toTypeList(String codes) { + List installEnvTypes = new ArrayList<>(); + String[] split = StrUtil.split(codes, 2); + for (String s : split) { + for (InstallEnvType type : InstallEnvType.values()) { + if (type.getCode().equals(s)) { + installEnvTypes.add(type); + } + } + } + return installEnvTypes.stream().distinct().collect(Collectors.toList()); + } + + public static String getInstallEnvCode(List installEnv) { + StringBuilder installEnvsCodes = new StringBuilder(); + if (installEnv != null) { + for (String installenv : installEnv) { + installEnvsCodes.append(InstallEnvType.toType(installenv).getCode()); + } + } + return installEnvsCodes.toString(); + } +} diff --git a/backend-server/application/src/main/java/com/apitable/widget/enums/RuntimeEnvType.java b/backend-server/application/src/main/java/com/apitable/widget/enums/RuntimeEnvType.java new file mode 100644 index 0000000000000000000000000000000000000000..1fac7c399e7e4efb1ca7cad122141a3871a13c98 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/enums/RuntimeEnvType.java @@ -0,0 +1,93 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.enums; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import cn.hutool.core.util.StrUtil; +import com.apitable.core.exception.BusinessException; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + *

+ * Mini Program Running Environment Type + *

+ * + * @author liuzijing + */ +@Getter +@AllArgsConstructor +public enum RuntimeEnvType { + + MOBILE("mobile", "01"), + + DESKTOP("desktop", "02"), + + ; + + @JsonValue + private final String value; + + private final String code; + + public static RuntimeEnvType toType(String value) { + for (RuntimeEnvType type : RuntimeEnvType.values()) { + if (type.getValue().equals(value)) { + return type; + } + } + throw new BusinessException("Unknown runtime environment type"); + } + + public static List toValueList(String codes) { + List runtimeEnvTypes = RuntimeEnvType.toTypeList(codes); + List list = new ArrayList<>(); + for (RuntimeEnvType runtimeEnvType : runtimeEnvTypes) { + list.add(runtimeEnvType.value); + } + return list; + } + + public static List toTypeList(String codes) { + List runtimeEnvTypes = new ArrayList<>(); + String[] split = StrUtil.split(codes, 2); + for (String s : split) { + for (RuntimeEnvType type : RuntimeEnvType.values()) { + if (type.getCode().equals(s)) { + runtimeEnvTypes.add(type); + } + } + } + return runtimeEnvTypes.stream().distinct().collect(Collectors.toList()); + } + + public static String getRuntimeEnvCode(List runtimeEnv) { + StringBuilder runtimeEnvsCodes = new StringBuilder(); + if (runtimeEnv != null) { + for (String runtimeenv : runtimeEnv) { + runtimeEnvsCodes.append(RuntimeEnvType.toType(runtimeenv).getCode()); + } + } + return runtimeEnvsCodes.toString(); + } +} diff --git a/backend-server/application/src/main/java/com/apitable/widget/enums/WidgetException.java b/backend-server/application/src/main/java/com/apitable/widget/enums/WidgetException.java new file mode 100644 index 0000000000000000000000000000000000000000..567e920768d90b2b2734f33f86ddcc4ece835495 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/enums/WidgetException.java @@ -0,0 +1,85 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.enums; + +import com.apitable.core.exception.BaseException; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * status code range(460-) + * + * @author Chambers + */ +@Getter +@AllArgsConstructor +public enum WidgetException implements BaseException { + + WIDGET_PACKAGE_NOT_EXIST(460, "The widget package does not exist or is not online"), + + WIDGET_NOT_EXIST(461, "Widget does not exist"), + + WIDGET_SPACE_ERROR(462, "The space where the widget is located is inconsistent"), + + WIDGET_DATASHEET_NOT_EXIST(462, "The dimensional table referenced by the widget does not exist, the copy failed"), + + WIDGET_NUMBER_LIMIT(462, "The number of widget has reached the upper limit, and the creation failed"), + + CREATE_FAIL_CUSTOM_PACKAGEID_REPEAT(466, "The applet creation failed, the package Id is duplicated"), + + WIDGET_BANNED(467, "Operation failed, widget banned"), + + RELEASES_FAIL_WIDGET_DISABLED(468, "Failed to publish the applet, the widget has been disabled, please contact GM"), + + RELEASES_FAIL_VERSION_NUM_ERROR(469, "Failed to publish the applet, the version number does not conform to the specification"), + + RELEASES_FAIL_VERSION_NUM_REPEAT(470, "Failed to publish the applet, the version number is duplicated"), + + ROLLBACK_FAIL_VERSION_NUM_ERROR(471, "The applet fails to roll back, and the version number does not conform to the specification"), + + ROLLBACK_FAIL_SELECT_VERSION_ERROR(472, "The applet fails to roll back, the rollback version number is wrong or fails to pass the audit"), + + RELEASES_FAIL_INCOMPLETE_PARAME(473, "Failed to publish the applet, the publishing parameters are incomplete"), + + EN_US_REQUIRED(474, "en-US is required"), + + SUBMIT_FAIL_INCOMPLETE_PARAME(475, "The applet submission failed, the submission parameters are incomplete"), + + SUBMIT_FAIL_VERSION_NUM_ERROR(476, "The applet submission failed, the version number does not meet the specification"), + + SUBMIT_FAIL_VERSION_NUM_REPEAT(477, "The applet submission failed, the version number is duplicated"), + + SUBMIT_FAIL_NO_SUBMIT_METHOD(478, "Space applet, unable to execute submit"), + + ISSUED_GLOBAL_ID_FAIL(479, "Failed to apply for global applet ID, please try again later"), + + WIDGET_AUTH_DATA_AUDIT_FAIL(480, "Applet certification data cannot be reviewed repeatedly"), + + AUDIT_REASON_NOT_EMPTY(481, "Review comments cannot be empty"), + + WIDGET_VERSION_DATA_AUDIT_FAIL(482, "Mini Program version data cannot be reviewed repeatedly"), + + AUDIT_SUBMIT_VERSION_NOT_EXIST(483, "Submit Version Not Exist"), + + ; + + private final Integer code; + + private final String message; +} diff --git a/backend-server/application/src/main/java/com/apitable/widget/enums/WidgetPackageStatus.java b/backend-server/application/src/main/java/com/apitable/widget/enums/WidgetPackageStatus.java new file mode 100644 index 0000000000000000000000000000000000000000..20ab31090062f74a24a88c0515a0b0f80008c6ff --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/enums/WidgetPackageStatus.java @@ -0,0 +1,47 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.enums; + +import com.apitable.core.support.serializer.IBaseEnum; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum WidgetPackageStatus implements IBaseEnum { + + DEVELOP(0), + + BANNED(1), + + UNPUBLISHED(2), + + ONLINE(3), + + UNPUBLISH(4), + + ; + + private final int value; + + @Override + public Integer getValue() { + return this.value; + } +} diff --git a/backend-server/application/src/main/java/com/apitable/asset/enums/AssetUploadScope.java b/backend-server/application/src/main/java/com/apitable/widget/enums/WidgetReleaseType.java similarity index 59% rename from backend-server/application/src/main/java/com/apitable/asset/enums/AssetUploadScope.java rename to backend-server/application/src/main/java/com/apitable/widget/enums/WidgetReleaseType.java index 5fc0b08ad16120c11716c70c8e90bccb1f562cfc..ede5ab911639b1bcf94e8137c8a863fba17a4bc5 100644 --- a/backend-server/application/src/main/java/com/apitable/asset/enums/AssetUploadScope.java +++ b/backend-server/application/src/main/java/com/apitable/widget/enums/WidgetReleaseType.java @@ -16,45 +16,36 @@ * along with this program. If not, see . */ -package com.apitable.asset.enums; - -import lombok.AllArgsConstructor; -import lombok.Getter; +package com.apitable.widget.enums; import com.apitable.core.exception.BusinessException; import com.apitable.core.support.serializer.IBaseEnum; +import lombok.AllArgsConstructor; +import lombok.Getter; -/** - * asset upload scope - * - * @author Pengap - */ -@Deprecated @Getter @AllArgsConstructor -public enum AssetUploadScope implements IBaseEnum { +public enum WidgetReleaseType implements IBaseEnum { + + SPACE(0), + + GLOBAL(1), - /** - * single file - * bucket:key,Indicates that only users are allowed to upload files with the specified key - */ - SINGLE(0), + WAIT_REVIEW(10), - /** - * multiple files - * bucket:keyPrefix,Indicates that users are allowed to upload files prefixed with the scope's keyPrefix. - */ - MULTIPLE(1); + ; private final Integer value; - public static AssetUploadScope of(Integer value) { - for (AssetUploadScope type : AssetUploadScope.values()) { - if (type.getValue().equals(value)) { - return type; + public static WidgetReleaseType toEnum(Integer type) { + if (null != type) { + for (WidgetReleaseType e : WidgetReleaseType.values()) { + if (e.getValue().equals(type)) { + return e; + } } } - throw new BusinessException("Unknown Upload Scope!"); + throw new BusinessException("Applet Publishing Type Error"); } } diff --git a/backend-server/application/src/main/java/com/apitable/widget/mapper/WidgetMapper.java b/backend-server/application/src/main/java/com/apitable/widget/mapper/WidgetMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..ff24c7cf3d7326e952f8cfe802ba603c169af6e6 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/mapper/WidgetMapper.java @@ -0,0 +1,107 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.mapper; + +import java.util.Collection; +import java.util.List; + +import org.apache.ibatis.annotations.Param; + +import com.apitable.widget.dto.NodeWidgetDto; +import com.apitable.widget.dto.WidgetBaseInfo; +import com.apitable.widget.dto.WidgetDTO; +import com.apitable.widget.entity.WidgetEntity; +import com.apitable.widget.vo.WidgetInfo; + +public interface WidgetMapper { + + /** + * query the number of components + * + * @param spaceId space id + * @param widgetIds widget ids + * @return count + */ + Integer selectCountBySpaceIdAndWidgetIds(@Param("spaceId") String spaceId, @Param("list") List widgetIds); + + /** + * @param widgetIds widget ids + * @return WidgetBaseInfos + */ + List selectWidgetBaseInfoByWidgetIds(@Param("list") Collection widgetIds); + + /** + * @param spaceId space id + * @param nodeType node type + * @return WidgetInfo + */ + List selectInfoBySpaceIdAndNodeType(@Param("spaceId") String spaceId, @Param("nodeType") Integer nodeType); + + /** + * @param nodeId node id + * @return WidgetBaseInfos + */ + List selectInfoByNodeId(@Param("nodeId") String nodeId); + + /** + * @param nodeIds node ids + * @return NodeWidgetDto + */ + List selectNodeWidgetDtoByNodeIds(@Param("nodeIds") List nodeIds); + + /** + * query component and data source datasheet information + * + * @param widgetIds widget ids + * @return WidgetDTO + */ + List selectWidgetDtoByWidgetIds(@Param("list") Collection widgetIds); + + /** + * @param widgetIds widget ids + * @return spaceIds + */ + List selectSpaceIdByWidgetIds(@Param("list") Collection widgetIds); + + /** + * @param nodeId node id + * @return WidgetIds + */ + List selectWidgetIdsByNodeId(@Param("nodeId") String nodeId); + + /** + * Query the components under the specified node, and the data source number table set referenced. + * + * @param nodeIds node ids + * @return datasheet ids + */ + List selectDataSourceDstIdsByNodeIds(@Param("nodeIds") List nodeIds); + + /** + * @param entities widgets + * @return affected rows + */ + int insertBatch(@Param("entities") List entities); + + /** + * @param widgetId widget id + * @return space id + */ + String selectSpaceIdByWidgetIdIncludeDeleted(@Param("widgetId") String widgetId); +} diff --git a/backend-server/application/src/main/java/com/apitable/widget/mapper/WidgetPackageMapper.java b/backend-server/application/src/main/java/com/apitable/widget/mapper/WidgetPackageMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..9838c28e11b614f84a00ccdf4e4c1a61c256479f --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/mapper/WidgetPackageMapper.java @@ -0,0 +1,170 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.mapper; + +import java.util.Collection; +import java.util.List; + +import com.apitable.widget.dto.WidgetPackageDTO; +import com.apitable.widget.dto.WidgetSpaceByDTO; +import com.apitable.widget.entity.WidgetPackageEntity; +import com.apitable.widget.ro.WidgetStoreListRo; +import com.apitable.widget.vo.GlobalWidgetInfo; +import com.apitable.widget.vo.WidgetPackageInfoVo; +import com.apitable.widget.vo.WidgetStoreListInfo; +import com.apitable.widget.vo.WidgetTemplatePackageInfo; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Param; + +public interface WidgetPackageMapper extends BaseMapper { + + /** + * @param userId user id + * @param spaceId space id + * @param storeListRo request parameters + * @return widget store list information + */ + List selectWidgetStoreList(@Param("userId") Long userId, @Param("spaceId") String spaceId, @Param("storeListRo") WidgetStoreListRo storeListRo); + + /** + * @param widgetPackageIds widgetPackageIds + * @param language language + * @return WidgetPackageDTOs + */ + List selectByPackageIdsIncludeDelete(@Param("list") Collection widgetPackageIds, @Param("language") String language); + + /** + * @param widgetPackageId widgetPackageId + * @return installation package status + */ + Integer selectStatusByPackageId(@Param("widgetPackageId") String widgetPackageId); + + /** + * @param entities WidgetPackage + * @return affected rows + */ + int insertBatch(@Param("entities") Collection entities); + + /** + * Cumulative number of component package installations + * + * @param widgetPackageId widgetPackageId + * @param times accumulation times + * @return affected rows + */ + int updateInstalledNumByPackageId(@Param("widgetPackageId") String widgetPackageId, @Param("times") Integer times); + + /** + * query whether the widget package id exists + * + * @param packageId packageId + * @return exists or not + */ + boolean countNumByPackageId(@Param("packageId") String packageId); + + /** + * @param packageId widget package id + * @return WidgetPackageEntity + */ + WidgetPackageEntity selectWidgetByPackageId(@Param("packageId") String packageId); + + /** + * @param status modify status + * @param releaseId release id + * @param packageId widget package id + * @param userId user id + * @return affected rows + */ + int updateStatusAndReleaseIdByPackageId(@Param("status") Integer status, @Param("releaseId") Long releaseId, @Param("packageId") String packageId, @Param("userId") Long userId); + + /** + * @param packageId widget id + * @param spaceId space id + * @param language specify return language + * @return WidgetPackageInfoVo + */ + List selectWidgetPackageInfoByPackageIdOrSpaceId(@Param("packageId") String packageId, @Param("spaceId") String spaceId, @Param("language") String language); + + /** + * @param language specify return language + * @return WidgetTemplatePackageInfo + */ + List selectWidgetTemplatePackageList(@Param("language") String language); + + /** + * @param packageId widget id + * @return WidgetSpaceByDTO + */ + WidgetSpaceByDTO selectWidgetSpaceBy(@Param("packageId") String packageId); + + /** + * query the latest widget order + * + * @param releaseType releaseType + * @param spaceId space id + * @return the latest order + */ + int selectMaxWidgetSort(@Param("releaseType") Integer releaseType, @Param("spaceId") String spaceId); + + /** + * check the order of official or template widgets + * + * @param packageId widgetId + * @return the latest order + */ + Integer selectGlobalWidgetSort(@Param("packageId") String packageId); + + /** + * Batch update global widget and widget template configuration + * + * @param globalWidgetInfo global widget info + * @return affected rows + */ + int singleUpdateGlobalAndTemplateConfig(@Param("globalWidgetInfo") GlobalWidgetInfo globalWidgetInfo); + + /** + * According to the issuance of global Id archive data statistics + * + * @return int + */ + int countByIssuedIdArchive(@Param("dstId") String dstId, @Param("recordId") String recordId); + + /** + * archive statistics based on audit results + * + * @return int + */ + int countByAuditSubmitResultArchive(@Param("dstId") String dstId, @Param("recotdId") String recotdId); + + /** + * @param fatherWidgetId parent widget id + * @param version version + * @return widget to be approved + */ + WidgetPackageEntity selectByFatherWidgetIdAndVersion(@Param("fatherWidgetId") String fatherWidgetId, @Param("version") String version); + + /** + * update extend info + * + * @param id the widget's id + * @param widgetBody the widget's extend info + * @return updated rows' number + */ + int updateWidgetBodyById(@Param("id") Long id, @Param("widgetBody") String widgetBody); +} diff --git a/backend-server/application/src/main/java/com/apitable/asset/vo/AssetUploadTokenVo.java b/backend-server/application/src/main/java/com/apitable/widget/ro/WidgetCopyRo.java similarity index 58% rename from backend-server/application/src/main/java/com/apitable/asset/vo/AssetUploadTokenVo.java rename to backend-server/application/src/main/java/com/apitable/widget/ro/WidgetCopyRo.java index a44eb1fd67cfb4f768ce501ca96907982d3031c0..6ec9ad95b4ab6ff5f93427542e4351b76ec83990 100644 --- a/backend-server/application/src/main/java/com/apitable/asset/vo/AssetUploadTokenVo.java +++ b/backend-server/application/src/main/java/com/apitable/widget/ro/WidgetCopyRo.java @@ -16,32 +16,31 @@ * along with this program. If not, see . */ -package com.apitable.asset.vo; +package com.apitable.widget.ro; + +import java.util.List; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; - /** *

- * Resource Direct Transfer Token Result View + * Widget Copy Request Parameters *

*/ @Data -@ApiModel("Resource Direct Transfer Token Result View") -public class AssetUploadTokenVo { - - @ApiModelProperty(value = "Upload voucher", position = 1) - private String uploadToken; - - @ApiModelProperty(value = "Resource name", position = 2) - private String resourceKey; - - @ApiModelProperty(value = "Upload type (QINIU: Qiniu Cloud)", position = 3) - private String uploadType; +@ApiModel("Widget Copy Request Parameters") +public class WidgetCopyRo { - @ApiModelProperty(value = "Endpoint", position = 4) - private String endpoint; + @ApiModelProperty(value = "Dashboard ID", required = true, example = "dsb11", position = 1) + @NotBlank(message = "Dashboard ID cannot be empty") + private String dashboardId; + @ApiModelProperty(value = "Widget ID List", required = true, example = "[\"wdtiJjVmNFcFmNtQFA\", \"wdtSbp8TkH7gTGAYR1\"]", position = 2) + @NotEmpty(message = "Widget ID list cannot be empty") + private List widgetIds; } diff --git a/backend-server/application/src/main/java/com/apitable/widget/ro/WidgetCreateRo.java b/backend-server/application/src/main/java/com/apitable/widget/ro/WidgetCreateRo.java new file mode 100644 index 0000000000000000000000000000000000000000..799ed870d3caf9f8732f028bbc3fdc7eb46c535a --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/ro/WidgetCreateRo.java @@ -0,0 +1,46 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.ro; + +import javax.validation.constraints.NotBlank; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + *

+ * Request parameters for widget creation + *

+ */ +@Data +@ApiModel("Request parameters for widget creation") +public class WidgetCreateRo { + + @ApiModelProperty(value = "Node ID", required = true, example = "dstAAA/dsbBBB", position = 1) + @NotBlank(message = "Node ID cannot be empty") + private String nodeId; + + @ApiModelProperty(value = "Widget Package ID", required = true, example = "wpkBBB", position = 2) + @NotBlank(message = "The Widget package ID cannot be empty") + private String widgetPackageId; + + @ApiModelProperty(value = "Widget name", example = "This is a widget", position = 3) + private String name = "New Widget"; +} diff --git a/backend-server/application/src/main/java/com/apitable/widget/ro/WidgetStoreListRo.java b/backend-server/application/src/main/java/com/apitable/widget/ro/WidgetStoreListRo.java new file mode 100644 index 0000000000000000000000000000000000000000..d3cda7f8b857b9a8bb5d22752bd18eff3636cb76 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/ro/WidgetStoreListRo.java @@ -0,0 +1,49 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.ro; + +import javax.validation.constraints.NotNull; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + *

+ * Get the widget store list parameter + *

+ */ +@Data +@ApiModel("Widget Store List Please Parameter") +public class WidgetStoreListRo { + + @ApiModelProperty(value = "Whether to filter unpublished widget (true: filter, false: not filter)", example = "false", position = 1) + private Boolean filter; + + @NotNull + @ApiModelProperty(value = "Get widget type (0: space station, 1: global, 10: to be approved)", example = "1", position = 2) + private Integer type; + + @ApiModelProperty(value = "Specify the return language", example = "en-US", position = 3, hidden = true) + private String language; + + @ApiModelProperty(value = "Global widget search keywords to be audited", position = 4) + private String previewSearchKeyword; + +} diff --git a/backend-server/application/src/main/java/com/apitable/widget/service/IWidgetService.java b/backend-server/application/src/main/java/com/apitable/widget/service/IWidgetService.java new file mode 100644 index 0000000000000000000000000000000000000000..42ea8566349840b32112a747fb381f8a65aaf8e4 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/service/IWidgetService.java @@ -0,0 +1,101 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import com.apitable.widget.ro.WidgetCreateRo; +import com.apitable.widget.ro.WidgetStoreListRo; +import com.apitable.widget.vo.WidgetInfo; +import com.apitable.widget.vo.WidgetPack; +import com.apitable.widget.vo.WidgetStoreListInfo; +import com.apitable.workspace.dto.DatasheetWidgetDTO; + +public interface IWidgetService { + + /** + * @param userId user id + * @param spaceId space id + * @param storeListRo request parameters + * @return WidgetPackageInfos + */ + List widgetStoreList(Long userId, String spaceId, WidgetStoreListRo storeListRo); + + /** + * Gets the component information in the specified space. + * + * @param spaceId space id + * @param memberId member id + * @param count load quantity + * @return WidgetInfos + */ + List getWidgetInfoList(String spaceId, Long memberId, Integer count); + + /** + * @param userId user id + * @param spaceId space id + * @param widget widget + * @return widgetId + */ + String create(Long userId, String spaceId, WidgetCreateRo widget); + + /** + * copy widget to dashboard + * + * @param userId user id + * @param spaceId space id + * @param dashboardId dashboardId + * @param widgetIds widgetIds + * @return WidgetPack + */ + Collection copyToDashboard(Long userId, String spaceId, String dashboardId, List widgetIds); + + /** + * @param userId user id + * @param destSpaceId destSpaceId + * @param newNodeMap Original node ID-newly created node ID MAP + * @param newWidgetIdMap Original component ID-newly created component ID MAP + * @param newWidgetIdToDstMap Newly Created Component ID-Data Source Table ID MAP + */ + void copyBatch(Long userId, String destSpaceId, Map newNodeMap, Map newWidgetIdMap, Map newWidgetIdToDstMap); + + /** + * @param widgetIds widgetIds + * @return spaceId + */ + String checkByWidgetIds(List widgetIds); + + /** + * @param widgetId widget id + * @return WidgetPack + */ + WidgetPack getWidgetPack(String widgetId); + + /** + * @param widgetIds widgetIds + * @return WidgetPacks + */ + List getWidgetPackList(Collection widgetIds); + + String getSpaceIdByWidgetId(String widgetId); + + void checkWidgetReference(List subNodeIds, List widgetIds); +} diff --git a/backend-server/application/src/main/java/com/apitable/widget/service/impl/WidgetServiceImpl.java b/backend-server/application/src/main/java/com/apitable/widget/service/impl/WidgetServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..540a3eee7d0d7e165e67268a6b9c15d1c96f26e3 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/service/impl/WidgetServiceImpl.java @@ -0,0 +1,441 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.service.impl; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.annotation.Resource; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.json.JSONUtil; +import com.apitable.base.enums.DatabaseException; +import com.apitable.control.infrastructure.ControlRoleDict; +import com.apitable.control.infrastructure.ControlTemplate; +import com.apitable.core.exception.BusinessException; +import com.apitable.core.util.ExceptionUtil; +import com.apitable.core.util.SqlTool; +import com.apitable.interfaces.widget.facade.WidgetServiceAuditFacade; +import com.apitable.shared.component.TaskManager; +import com.apitable.shared.config.properties.LimitProperties; +import com.apitable.shared.context.LoginContext; +import com.apitable.shared.util.IdUtil; +import com.apitable.template.enums.TemplateException; +import com.apitable.widget.dto.NodeWidgetDto; +import com.apitable.widget.dto.WidgetBaseInfo; +import com.apitable.widget.dto.WidgetDTO; +import com.apitable.widget.dto.WidgetPackageDTO; +import com.apitable.widget.dto.WidgetSpaceByDTO; +import com.apitable.widget.entity.WidgetEntity; +import com.apitable.widget.enums.InstallEnvType; +import com.apitable.widget.enums.RuntimeEnvType; +import com.apitable.widget.enums.WidgetException; +import com.apitable.widget.enums.WidgetPackageStatus; +import com.apitable.widget.enums.WidgetReleaseType; +import com.apitable.widget.mapper.WidgetMapper; +import com.apitable.widget.mapper.WidgetPackageMapper; +import com.apitable.widget.ro.WidgetCreateRo; +import com.apitable.widget.ro.WidgetStoreListRo; +import com.apitable.widget.service.IWidgetService; +import com.apitable.widget.vo.WidgetInfo; +import com.apitable.widget.vo.WidgetPack; +import com.apitable.widget.vo.WidgetSnapshot; +import com.apitable.widget.vo.WidgetStoreListInfo; +import com.apitable.workspace.dto.DatasheetWidgetDTO; +import com.apitable.workspace.entity.DatasheetWidgetEntity; +import com.apitable.workspace.entity.NodeRelEntity; +import com.apitable.workspace.enums.NodeType; +import com.apitable.workspace.enums.PermissionException; +import com.apitable.workspace.mapper.DatasheetWidgetMapper; +import com.apitable.workspace.mapper.ResourceMetaMapper; +import com.apitable.workspace.service.IDatasheetWidgetService; +import com.apitable.workspace.service.INodeRelService; +import com.apitable.workspace.service.INodeService; +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import com.baomidou.mybatisplus.extension.toolkit.SqlHelper; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import static com.apitable.workspace.enums.NodeException.UNKNOWN_NODE_TYPE; + +@Slf4j +@Service +public class WidgetServiceImpl implements IWidgetService { + + @Resource + private WidgetMapper widgetMapper; + + @Resource + private WidgetPackageMapper widgetPackageMapper; + + @Resource + private WidgetServiceAuditFacade widgetServiceAuditFacade; + + @Resource + private INodeService iNodeService; + + @Resource + private INodeRelService iNodeRelService; + + @Resource + private ControlTemplate controlTemplate; + + @Resource + private ResourceMetaMapper resourceMetaMapper; + + @Resource + private IDatasheetWidgetService iDatasheetWidgetService; + + @Resource + private DatasheetWidgetMapper datasheetWidgetMapper; + + @Resource + private LimitProperties limitProperties; + + @Resource + private ObjectMapper objectMapper; + + @Override + public List widgetStoreList(Long userId, String spaceId, WidgetStoreListRo storeListRo) { + if (null != storeListRo && WidgetReleaseType.WAIT_REVIEW.getValue().equals(storeListRo.getType())) { + // show a list of global widgets to be reviewed + return widgetServiceAuditFacade.getWaitReviewWidgetList(storeListRo); + } + List datas = widgetPackageMapper.selectWidgetStoreList(userId, spaceId, storeListRo); + for (WidgetStoreListInfo widgetInfo : datas) { + widgetInfo.setInstallEnv(InstallEnvType.toValueList(widgetInfo.getInstallEnvCode())); + widgetInfo.setRuntimeEnv(RuntimeEnvType.toValueList(widgetInfo.getRuntimeEnvCode())); + // replace space station widget author name + if (WidgetReleaseType.SPACE.getValue().equals(widgetInfo.getReleaseType())) { + WidgetSpaceByDTO byDTO = widgetPackageMapper.selectWidgetSpaceBy(widgetInfo.getWidgetPackageId()); + if (null != byDTO) { + widgetInfo.setAuthorName(byDTO.getAuthorName()); + widgetInfo.setAuthorIcon(byDTO.getAuthorIcon()); + widgetInfo.setOwnerUuid(byDTO.getOwnerUuid()); + widgetInfo.setOwnerMemberId(byDTO.getOwnerMemberId()); + } + } + } + return datas; + } + + @Override + public List getWidgetInfoList(String spaceId, Long memberId, Integer count) { + log.info("Gets the component information in the specified space."); + // load only the components in the datasheet + List widgetInfos = widgetMapper.selectInfoBySpaceIdAndNodeType(spaceId, NodeType.DATASHEET.getNodeType()); + if (CollUtil.isEmpty(widgetInfos)) { + return new ArrayList<>(); + } + // filter source datasheet permissions + List datasheetIds = widgetInfos.stream().map(WidgetInfo::getDatasheetId).collect(Collectors.toList()); + ControlRoleDict roleDict = controlTemplate.fetchNodeRole(memberId, datasheetIds); + if (CollUtil.isEmpty(roleDict)) { + return new ArrayList<>(); + } + List infos = new ArrayList<>(count); + for (int i = 0, number = 0; i < widgetInfos.size() && number < count; i++) { + WidgetInfo info = widgetInfos.get(i); + if (!roleDict.containsKey(info.getDatasheetId())) { + continue; + } + infos.add(info); + number++; + } + return infos; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public String create(Long userId, String spaceId, WidgetCreateRo widget) { + log.info("Create Widget"); + // to determine whether the component package exists, only under development, pending release, and released status can be added + List checkStatus = CollUtil.newArrayList(WidgetPackageStatus.DEVELOP.getValue(), WidgetPackageStatus.UNPUBLISHED.getValue(), WidgetPackageStatus.ONLINE.getValue()); + this.checkWidgetPackIfExist(widget.getWidgetPackageId(), checkStatus); + String widgetId = IdUtil.createWidgetId(); + // if it is a widget in the data datasheet, create a widget in the data datasheet. if it is a widget in the dashboard, check the maximum number + NodeType nodeType = iNodeService.getTypeByNodeId(widget.getNodeId()); + switch (nodeType) { + case DATASHEET: + // create associations between tables and components + iDatasheetWidgetService.create(spaceId, widget.getNodeId(), widgetId, widget.getNodeId()); + break; + case DASHBOARD: + // Note: The number of statistics uses the number of Dashboard layout to ensure the accuracy, and the widget instance may have dirty data + int count = SqlTool.retCount(resourceMetaMapper.countDashboardWidgetNumber(widget.getNodeId())); + ExceptionUtil.isTrue(count < limitProperties.getDsbWidgetMaxCount(), WidgetException.WIDGET_NUMBER_LIMIT); + break; + case MIRROR: + // create an association between a mirror source table and a component + NodeRelEntity nodeRel = iNodeRelService.getByRelNodeId(widget.getNodeId()); + ExceptionUtil.isNotNull(nodeRel, PermissionException.NODE_NOT_EXIST); + iDatasheetWidgetService.create(spaceId, nodeRel.getMainNodeId(), widgetId, widget.getNodeId()); + break; + default: + throw new BusinessException(UNKNOWN_NODE_TYPE); + } + // new components + WidgetEntity widgetEntity = WidgetEntity.builder() + .id(IdWorker.getId()) + .spaceId(spaceId) + .nodeId(widget.getNodeId()) + .packageId(widget.getWidgetPackageId()) + .widgetId(widgetId) + .name(widget.getName()) + .storage(JSONUtil.createObj().toString()) + .createdBy(userId) + .updatedBy(userId) + .build(); + boolean flag = SqlHelper.retBool(widgetMapper.insertBatch(Collections.singletonList(widgetEntity))); + ExceptionUtil.isTrue(flag, DatabaseException.INSERT_ERROR); + // the cumulative number of installations of the component package + TaskManager.me().execute(() -> widgetPackageMapper.updateInstalledNumByPackageId(widget.getWidgetPackageId(), 1)); + return widgetId; + } + + private void checkWidgetPackIfExist(String widgetPackageId, List status) { + log.info("check if the component installation package exists, widgetPackageId:{},status:{}", widgetPackageId, status); + // determine if a component package exists + Integer packageStatus = widgetPackageMapper.selectStatusByPackageId(widgetPackageId); + ExceptionUtil.isTrue(packageStatus != null && status.contains(packageStatus), + WidgetException.WIDGET_PACKAGE_NOT_EXIST); + } + + @Override + public Collection copyToDashboard(Long userId, String spaceId, String dashboardId, List widgetIds) { + log.info("copy widgets to dashboard"); + // verify the maximum number of components for a dashboard + int count = SqlTool.retCount(resourceMetaMapper.countDashboardWidgetNumber(dashboardId)); + ExceptionUtil.isTrue(count + widgetIds.size() <= limitProperties.getDsbWidgetMaxCount(), WidgetException.WIDGET_NUMBER_LIMIT); + // check if components exist + int widgetCount = SqlTool.retCount(widgetMapper.selectCountBySpaceIdAndWidgetIds(spaceId, widgetIds)); + ExceptionUtil.isTrue(widgetCount == widgetIds.size(), WidgetException.WIDGET_NOT_EXIST); + // verify that component data sources all exist + List widgetDTOList = widgetMapper.selectWidgetDtoByWidgetIds(widgetIds); + ExceptionUtil.isTrue(CollUtil.isNotEmpty(widgetDTOList) && widgetDTOList.size() == widgetIds.size(), WidgetException.WIDGET_DATASHEET_NOT_EXIST); + // Build: Old widgetId and new widgetId mapping, new widgetId and data source datasheetId mapping + Map newNodeMap = new HashMap<>(widgetIds.size()); + Map newWidgetIdMap = new HashMap<>(widgetIds.size()); + Map newWidgetIdToDstMap = new HashMap<>(widgetIds.size()); + for (WidgetDTO dto : widgetDTOList) { + newNodeMap.put(dto.getNodeId(), dashboardId); + String newWidgetId = IdUtil.createWidgetId(); + newWidgetIdMap.put(dto.getWidgetId(), newWidgetId); + DatasheetWidgetDTO datasheetWidgetDTO = new DatasheetWidgetDTO(); + BeanUtil.copyProperties(dto, datasheetWidgetDTO); + newWidgetIdToDstMap.put(newWidgetId, datasheetWidgetDTO); + } + // copy widgets + this.copyBatch(userId, spaceId, newNodeMap, newWidgetIdMap, newWidgetIdToDstMap); + return newWidgetIdMap.values(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void copyBatch(Long userId, String destSpaceId, Map newNodeMap, Map newWidgetIdMap, Map newWidgetIdToDstMap) { + // batch generation of new widgets + List widgetBaseInfos = widgetMapper.selectWidgetBaseInfoByWidgetIds(newWidgetIdMap.keySet()); + if (widgetBaseInfos.isEmpty()) { + return; + } + List entities = new ArrayList<>(widgetBaseInfos.size()); + List datasheetWidgets = new ArrayList<>(newWidgetIdToDstMap.size()); + for (WidgetBaseInfo widgetInfo : widgetBaseInfos) { + String widgetId = newWidgetIdMap.get(widgetInfo.getWidgetId()); + // Reset the widget storage configuration when there is no data source. + boolean hasDataSource = newWidgetIdToDstMap.containsKey(widgetId); + String storage = hasDataSource ? widgetInfo.getStorage() : JSONUtil.createObj().toString(); + // build widget entity + WidgetEntity widgetEntity = WidgetEntity.builder() + .id(IdWorker.getId()) + .spaceId(destSpaceId) + .nodeId(newNodeMap.get(widgetInfo.getNodeId())) + .packageId(widgetInfo.getWidgetPackageId()) + .widgetId(widgetId) + .name(widgetInfo.getName()) + .storage(storage) + .createdBy(userId) + .updatedBy(userId) + .build(); + entities.add(widgetEntity); + if (!hasDataSource) { + continue; + } + DatasheetWidgetDTO datasheetWidgetDTO = newWidgetIdToDstMap.get(widgetId); + // Build the associated entity of the datasheet and the widget. + DatasheetWidgetEntity datasheetWidget = DatasheetWidgetEntity.builder() + .id(IdWorker.getId()) + .spaceId(destSpaceId) + .dstId(datasheetWidgetDTO.getDstId()) + .sourceId(datasheetWidgetDTO.getSourceId()) + .widgetId(widgetId) + .build(); + datasheetWidgets.add(datasheetWidget); + } + boolean flag = SqlHelper.retBool(widgetMapper.insertBatch(entities)); + ExceptionUtil.isTrue(flag, DatabaseException.INSERT_ERROR); + if (CollUtil.isNotEmpty(datasheetWidgets)) { + // Batch Create Association between datasheet and widget + flag = SqlHelper.retBool(datasheetWidgetMapper.insertBatch(datasheetWidgets)); + ExceptionUtil.isTrue(flag, DatabaseException.INSERT_ERROR); + } + // cumulative installation times of widget package + TaskManager.me().execute(() -> { + Map> packageIdToInfosMap = widgetBaseInfos.stream() + .collect(Collectors.groupingBy(WidgetBaseInfo::getWidgetPackageId)); + packageIdToInfosMap.forEach((widgetPackageId, infos) -> + widgetPackageMapper.updateInstalledNumByPackageId(widgetPackageId, infos.size())); + }); + } + + @Override + public String checkByWidgetIds(List widgetIds) { + // widget space verification, all must be in the same space + List spaceIds = widgetMapper.selectSpaceIdByWidgetIds(widgetIds); + ExceptionUtil.isTrue(CollUtil.isNotEmpty(spaceIds) && spaceIds.size() == 1, WidgetException.WIDGET_SPACE_ERROR); + return spaceIds.get(0); + } + + @Override + public WidgetPack getWidgetPack(String widgetId) { + log.info("get widget package information,widgetId:{}", widgetId); + List widgetPackList = this.getWidgetPackList(Collections.singletonList(widgetId)); + if (CollUtil.isEmpty(widgetPackList)) { + return new WidgetPack(); + } + return widgetPackList.get(0); + } + + @Override + public List getWidgetPackList(Collection widgetIds) { + log.info("get the widget package information collection"); + if (CollUtil.isEmpty(widgetIds)) { + return new ArrayList<>(); + } + List widgetBaseInfos = widgetMapper.selectWidgetBaseInfoByWidgetIds(widgetIds); + if (CollUtil.isEmpty(widgetBaseInfos)) { + return new ArrayList<>(); + } + // unified query data source datasheet + List datasheetWidgetDTOList = datasheetWidgetMapper.selectDtoByWidgetIds(widgetIds); + Map widgetIdToDstMap = datasheetWidgetDTOList.stream() + .collect(Collectors.toMap(DatasheetWidgetDTO::getWidgetId, dto -> dto)); + + // Unified query widget installation package information content + Set packageIds = widgetBaseInfos.stream().map(WidgetBaseInfo::getWidgetPackageId).collect(Collectors.toSet()); + List packageEntities = widgetPackageMapper.selectByPackageIdsIncludeDelete(packageIds, LoginContext.me().getLocaleStr()); + Map widgetPackageMap = packageEntities.stream() + .collect(Collectors.toMap(WidgetPackageDTO::getPackageId, widgetPackage -> widgetPackage)); + + // build widget package information + List widgetPacks = new ArrayList<>(widgetBaseInfos.size()); + + widgetBaseInfos.forEach(widget -> { + HashMap snapshotStorage = new HashMap<>(); + try { + snapshotStorage = objectMapper.readValue(widget.getStorage(), new TypeReference>() {}); + } + catch (JsonProcessingException ignored) { + } + // assembly widget snapshot information + WidgetSnapshot snapshot = WidgetSnapshot.builder() + .widgetName(widget.getName()) + .storage(snapshotStorage) + .build(); + if (widgetIdToDstMap.containsKey(widget.getWidgetId())) { + DatasheetWidgetDTO datasheetWidgetDTO = widgetIdToDstMap.get(widget.getWidgetId()); + snapshot.setDatasheetId(datasheetWidgetDTO.getDstId()); + snapshot.setSourceId(datasheetWidgetDTO.getSourceId()); + } + WidgetPackageDTO widgetPackage = widgetPackageMap.get(widget.getWidgetPackageId()); + WidgetPack widgetPack = WidgetPack.builder() + .id(widget.getWidgetId()) + .revision(widget.getRevision()) + .widgetPackageId(widgetPackage.getPackageId()) + .widgetPackageName(widgetPackage.getName()) + .widgetPackageIcon(widgetPackage.getIcon()) + .widgetPackageVersion(widgetPackage.getVersion()) + .snapshot(snapshot) + .status(widgetPackage.getStatus()) + .authorEmail(widgetPackage.getAuthorEmail()) + .authorLink(widgetPackage.getAuthorLink()) + .packageType(widgetPackage.getPackageType()) + .releaseType(widgetPackage.getReleaseType()) + .releaseCodeBundle(widgetPackage.getReleaseCodeBundle()) + .sandbox(widgetPackage.getSandbox()) + .installEnv(InstallEnvType.toValueList(widgetPackage.getInstallEnvCode())) + .runtimeEnv(RuntimeEnvType.toValueList(widgetPackage.getRuntimeEnvCode())) + .build(); + // Replace the name of the author of the small widget of the space station. + if (WidgetReleaseType.SPACE.getValue().equals(widgetPack.getReleaseType())) { + WidgetSpaceByDTO byDTO = widgetPackageMapper.selectWidgetSpaceBy(widgetPack.getWidgetPackageId()); + if (null != byDTO) { + widgetPack.setAuthorName(byDTO.getAuthorName()); + widgetPack.setAuthorIcon(byDTO.getAuthorIcon()); + } + } + else { + widgetPack.setAuthorName(widgetPackage.getAuthorName()); + widgetPack.setAuthorIcon(widgetPackage.getAuthorIcon()); + // to be audited widget rendering parent widget id + if (WidgetReleaseType.WAIT_REVIEW.getValue().equals(widgetPack.getReleaseType())) { + widgetPack.setFatherWidgetPackageId(widgetPackage.getFatherWidgetId()); + } + } + widgetPacks.add(widgetPack); + }); + return widgetPacks; + } + + @Override + public String getSpaceIdByWidgetId(String widgetId) { + return widgetMapper.selectSpaceIdByWidgetIdIncludeDeleted(widgetId); + } + + @Override + public void checkWidgetReference(List subNodeIds, List widgetIds) { + // If there is a dashboard, verify whether the data source of the component references an external data table + List widgetInfos = widgetMapper.selectNodeWidgetDtoByNodeIds(widgetIds); + // Group by dashboard nodeId + Map> dashboardNodeMap = widgetInfos.stream().collect(Collectors.groupingBy(NodeWidgetDto::getNodeId)); + for (String dashboardNodeId : dashboardNodeMap.keySet()) { + for (NodeWidgetDto widgetInfo : dashboardNodeMap.get(dashboardNodeId)) { + // Throws an exception if the widget is associated with an external table + if (!subNodeIds.contains(widgetInfo.getDstId())) { + Map foreignMap = new HashMap<>(); + foreignMap.put("NODE_NAME", iNodeService.getNodeNameByNodeId(dashboardNodeId)); + foreignMap.put("FOREIGN_WIDGET_NAME", widgetInfo.getWidgetName()); + throw new BusinessException(TemplateException.FOLDER_DASHBOARD_LINK_FOREIGN_NODE, foreignMap); + } + } + } + } +} diff --git a/backend-server/application/src/main/java/com/apitable/widget/vo/GlobalWidgetInfo.java b/backend-server/application/src/main/java/com/apitable/widget/vo/GlobalWidgetInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..b3969a658f555e241cb202cbb3b9a6c1e70432ed --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/vo/GlobalWidgetInfo.java @@ -0,0 +1,120 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.vo; + +/** + *

+ * Global widget config + *

+ * + */ +public class GlobalWidgetInfo { + + private String packageId; + + private String packageName; + + private Boolean isEnabled; + + private Boolean isTemplate; + + private String version; + + private Integer widgetSort; + + /* Widget Extension Fields, widget_body */ + private String openSourceAddress; + + private String templateCover; + + private String website; + /* Widget Extension Fields, widget_body */ + + public String getPackageName() { + return packageName; + } + + public void setPackageName(String packageName) { + this.packageName = packageName; + } + + public String getPackageId() { + return packageId; + } + + public void setPackageId(String packageId) { + this.packageId = packageId; + } + + public Boolean getIsEnabled() { + return isEnabled; + } + + public void setIsEnabled(Boolean enabled) { + this.isEnabled = enabled; + } + + public Boolean getIsTemplate() { + return isTemplate; + } + + public void setIsTemplate(Boolean template) { + this.isTemplate = template; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public Integer getWidgetSort() { + return widgetSort; + } + + public void setWidgetSort(Integer widgetSort) { + this.widgetSort = widgetSort; + } + + public String getOpenSourceAddress() { + return openSourceAddress; + } + + public void setOpenSourceAddress(String openSourceAddress) { + this.openSourceAddress = openSourceAddress; + } + + public String getTemplateCover() { + return templateCover; + } + + public void setTemplateCover(String templateCover) { + this.templateCover = templateCover; + } + + public String getWebsite() { + return website; + } + + public void setWebsite(String website) { + this.website = website; + } +} diff --git a/backend-server/application/src/main/java/com/apitable/widget/vo/WidgetInfo.java b/backend-server/application/src/main/java/com/apitable/widget/vo/WidgetInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..75144d94fb8836db8611ab77bdfa4c8b7403b94a --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/vo/WidgetInfo.java @@ -0,0 +1,65 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.vo; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import com.apitable.shared.support.serializer.ImageSerializer; +import com.apitable.shared.support.serializer.NullStringSerializer; + +/** + *

+ * Widget Information View + *

+ */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder(toBuilder = true) +@ApiModel("Widget Information View") +public class WidgetInfo { + + @ApiModelProperty(value = "Widget ID", example = "wdt123", position = 1) + private String widgetId; + + @ApiModelProperty(value = "Widget Name", example = "Widget instance name", position = 2) + private String widgetName; + + @ApiModelProperty(value = "Cover drawing of component package", example = "https://apitable.com/space/2020/12/23/aqa", position = 3) + @JsonSerialize(nullsUsing = NullStringSerializer.class, using = ImageSerializer.class) + private String widgetPackageCover; + + @ApiModelProperty(value = "Data source table ID", example = "dst123", position = 4) + @JsonSerialize(nullsUsing = NullStringSerializer.class) + private String datasheetId; + + @ApiModelProperty(value = "Data source data table name", example = "wpkABC", position = 5) + @JsonSerialize(nullsUsing = NullStringSerializer.class) + private String datasheetName; + + @ApiModelProperty(value = "Package Icon", example = "https://apitable.com/space/2020/12/23/aqa", position = 6) + @JsonSerialize(nullsUsing = NullStringSerializer.class, using = ImageSerializer.class) + private String widgetPackageIcon; +} diff --git a/backend-server/application/src/main/java/com/apitable/widget/vo/WidgetPack.java b/backend-server/application/src/main/java/com/apitable/widget/vo/WidgetPack.java new file mode 100644 index 0000000000000000000000000000000000000000..61681839d4201ea2341c53fbf8e2a9c1c04f4a87 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/vo/WidgetPack.java @@ -0,0 +1,110 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.vo; + +import java.util.List; + +import com.apitable.shared.support.serializer.ImageSerializer; +import com.apitable.shared.support.serializer.NullNumberSerializer; +import com.apitable.shared.support.serializer.NullStringSerializer; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + *

+ * Widget package information (alignment with front-end structure requirements) + *

+ */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder(toBuilder = true) +@ApiModel("Widget package information") +public class WidgetPack { + + @ApiModelProperty(value = "Widget ID", example = "wdt123", position = 1) + private String id; + + @ApiModelProperty(value = "Widget version number", example = "0", position = 2) + @JsonSerialize(nullsUsing = NullNumberSerializer.class) + private Long revision; + + @ApiModelProperty(value = "Package ID", example = "wpkABC", position = 3) + private String widgetPackageId; + + @ApiModelProperty(value = "Widget package name", example = "Chart", position = 4) + private String widgetPackageName; + + @ApiModelProperty(value = "Widget package icon", example = "https://apitable.com/space/2020/12/23/aqa", position = 5) + @JsonSerialize(nullsUsing = NullStringSerializer.class, using = ImageSerializer.class) + private String widgetPackageIcon; + + @ApiModelProperty(value = "Widget package version number", example = "v1.0.0", position = 6) + private String widgetPackageVersion; + + @ApiModelProperty(value = "Widget snapshot information", position = 7) + private WidgetSnapshot snapshot; + + @ApiModelProperty(value = "Widget status (0: under development; 1: banned; 2: to be published; 3: published; 4: off the shelf)", position = 8) + private Integer status; + + @ApiModelProperty(value = "Widget Author Name", position = 9) + private String authorName; + + @ApiModelProperty(value = "Widget author Email", position = 10) + private String authorEmail; + + @ApiModelProperty(value = "Widget Author Icon", position = 11) + @JsonSerialize(using = ImageSerializer.class) + private String authorIcon; + + @ApiModelProperty(value = "Widget Author Web Address", position = 12) + private String authorLink; + + @ApiModelProperty(value = "Widget package type (0: third party, 1: official)", position = 13) + private Integer packageType; + + @ApiModelProperty(value = "Widget publishing type (0: space station, 1: global)", position = 14) + private Integer releaseType; + + @ApiModelProperty(value = "Widget code address", example = "https://apitable.com/code/2020/12/23/aqa", position = 15) + @JsonSerialize(using = ImageSerializer.class) + private String releaseCodeBundle; + + @ApiModelProperty(value = "Sandbox or not", position = 16) + private Boolean sandbox; + + @JsonInclude(Include.NON_EMPTY) + @ApiModelProperty(value = "Audit Applet Parent Applet Id", notes = "Dynamic key", position = 17) + private String fatherWidgetPackageId; + + @ApiModelProperty(value = "Installation environment type", example = "dashboard", position = 18) + private List installEnv; + + @ApiModelProperty(value = "Operating environment type", example = "mobile", position = 19) + private List runtimeEnv; + +} diff --git a/backend-server/application/src/main/java/com/apitable/widget/vo/WidgetPackageInfo.java b/backend-server/application/src/main/java/com/apitable/widget/vo/WidgetPackageInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..63ff97fc44fa56835961db3d3ef696a1cbabae7e --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/vo/WidgetPackageInfo.java @@ -0,0 +1,84 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.vo; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import com.apitable.shared.support.serializer.ImageSerializer; +import com.apitable.shared.support.serializer.NullStringSerializer; + +/** + *

+ * Widget package information view + *

+ */ +@Data +@ApiModel("Widget package information view") +public class WidgetPackageInfo { + + @ApiModelProperty(value = "Package ID", example = "wpkABC", position = 1) + private String widgetPackageId; + + @ApiModelProperty(value = "Widget package name", example = "CHART", position = 2) + private String name; + + @Deprecated + @ApiModelProperty(value = "English name", example = "chart", position = 2) + private String nameEn; + + @ApiModelProperty(value = "Widget package icon", example = "https://apitable.com/space/2020/12/23/aqa", position = 3) + @JsonSerialize(nullsUsing = NullStringSerializer.class, using = ImageSerializer.class) + private String icon; + + @ApiModelProperty(value = "Cover drawing of component package", example = "https://apitable.com/space/2020/12/23/aqa", position = 3) + @JsonSerialize(nullsUsing = NullStringSerializer.class, using = ImageSerializer.class) + private String cover; + + @ApiModelProperty(value = "DESCRIBE", example = "This is the description of a chart applet", position = 4) + private String description; + + @ApiModelProperty(value = "Widget package version number", example = "1.0.0", position = 5) + private String version; + + @ApiModelProperty(value = "Author Name", position = 6) + private String authorName; + + @ApiModelProperty(value = "Author icon", position = 7) + @JsonSerialize(using = ImageSerializer.class) + private String authorIcon; + + @ApiModelProperty(value = "Author Email", position = 8) + private String authorEmail; + + @ApiModelProperty(value = "Author website address", position = 9) + private String authorLink; + + @ApiModelProperty(value = "Widget package type (0: third party, 1: official)", position = 10) + private Integer packageType; + + @ApiModelProperty(value = "0: Publish to the component store in the space station, 1: Publish to the global app store", position = 11) + private Integer releaseType; + + @ApiModelProperty(value = "Widget package status (0: to be approved; 1: not passed; 2: to be released; 3: online; 4: offline)", example = "3", position = 12) + private Integer status; + +} diff --git a/backend-server/application/src/main/java/com/apitable/widget/vo/WidgetPackageInfoVo.java b/backend-server/application/src/main/java/com/apitable/widget/vo/WidgetPackageInfoVo.java new file mode 100644 index 0000000000000000000000000000000000000000..225445807b282bd21ece7283864803b6fc166477 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/vo/WidgetPackageInfoVo.java @@ -0,0 +1,79 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.vo; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import com.apitable.shared.support.serializer.ImageSerializer; + +/** + *

+ * Applet Package Information View + *

+ */ +@Data +@ApiModel("Applet Package Information View") +public class WidgetPackageInfoVo { + + @ApiModelProperty(value = "Package ID", example = "wpkABC", position = 1) + private String packageId; + + @ApiModelProperty(value = "Widget name - returned according to the request Accept Language. Default:zh-CN,Current Support List:「en-US/zh-CN」", example = "Chart", position = 2) + private String name; + + @ApiModelProperty(value = "Widget package icon", example = "https://apitable.com/space/2020/12/23/aqa", position = 3) + @JsonSerialize(using = ImageSerializer.class) + private String icon; + + @ApiModelProperty(value = "Cover drawing of component package", example = "https://apitable.com/space/2020/12/23/aqa", position = 4) + @JsonSerialize(using = ImageSerializer.class) + private String cover; + + @ApiModelProperty(value = "Widget description - returned according to the request Accept Language, default: zh CN, current support list:「en-US/zh-CN」", example = "This is the description of a chart applet", position = 5) + private String description; + + @ApiModelProperty(value = "Widget package version number", example = "1.0.0", position = 6) + private String version; + + @ApiModelProperty(value = "Widget package status (0: under development; 1: banned; 2: to be published; 3: published; 4: off the shelf)", example = "3", position = 7) + private Integer status; + + @ApiModelProperty(value = "Author Name", position = 8) + private String authorName; + + @ApiModelProperty(value = "Author icon", position = 9) + @JsonSerialize(using = ImageSerializer.class) + private String authorIcon; + + @ApiModelProperty(value = "Author Email", position = 10) + private String authorEmail; + + @ApiModelProperty(value = "Author website address", position = 11) + private String authorLink; + + @ApiModelProperty(value = "Widget package type (0: third party, 1: official)", position = 12) + private Integer packageType; + + @ApiModelProperty(value = "0: Publish to the component store in the space station, 1: Publish to the global app store", position = 13) + private Integer releaseType; + +} diff --git a/backend-server/application/src/main/java/com/apitable/widget/vo/WidgetSnapshot.java b/backend-server/application/src/main/java/com/apitable/widget/vo/WidgetSnapshot.java new file mode 100644 index 0000000000000000000000000000000000000000..c45f898f7957a97250299a85e61431564ef4203b --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/vo/WidgetSnapshot.java @@ -0,0 +1,58 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.vo; + +import java.util.HashMap; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import com.apitable.shared.support.serializer.NullStringSerializer; + +/** + *

+ * Widget snapshot information (alignment with front-end structure requirements) + *

+ */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel("Widget snapshot information") +@Builder(toBuilder = true) +public class WidgetSnapshot { + + @ApiModelProperty(value = "Widget Name", example = "Widget instance name", position = 1) + private String widgetName; + + @ApiModelProperty(value = "Data source table ID", example = "dst123", position = 2) + @JsonSerialize(nullsUsing = NullStringSerializer.class) + private String datasheetId; + + @ApiModelProperty(value = "Storage configuration", position = 3) + private HashMap storage; + + @ApiModelProperty(value = "Data source reference source ID", example = "mir123", position = 4) + @JsonSerialize(nullsUsing = NullStringSerializer.class) + private String sourceId; +} diff --git a/backend-server/application/src/main/java/com/apitable/asset/ro/AssetUploadTokenRo.java b/backend-server/application/src/main/java/com/apitable/widget/vo/WidgetStoreListExtraInfo.java similarity index 65% rename from backend-server/application/src/main/java/com/apitable/asset/ro/AssetUploadTokenRo.java rename to backend-server/application/src/main/java/com/apitable/widget/vo/WidgetStoreListExtraInfo.java index b29987a6c2d60c092636a61d11d79701cf14c91e..0bb28b10685057a3c90580bf8ff7a465cedbcf9a 100644 --- a/backend-server/application/src/main/java/com/apitable/asset/ro/AssetUploadTokenRo.java +++ b/backend-server/application/src/main/java/com/apitable/widget/vo/WidgetStoreListExtraInfo.java @@ -16,24 +16,22 @@ * along with this program. If not, see . */ -package com.apitable.asset.ro; +package com.apitable.widget.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; +/** + *

+ * Widget Store List Extended Information View + *

+ */ @Data -@ApiModel("Token request parameters for direct resource transfer") -public class AssetUploadTokenRo { - - @Deprecated - @ApiModelProperty(value = "upload prefix scope(0:single-default;1: multi)", position = 1) - private Integer prefixalScope; - - @ApiModelProperty(value = "required when uploading a single file", position = 2) - private String assetsKey; +@ApiModel("Widget Store List Extended Information View") +public class WidgetStoreListExtraInfo { - @ApiModelProperty(value = "space id", position = 3) - private String spaceId; + @ApiModelProperty(value = "Widget official website address", position = 1) + private String website; } diff --git a/backend-server/application/src/main/java/com/apitable/widget/vo/WidgetStoreListInfo.java b/backend-server/application/src/main/java/com/apitable/widget/vo/WidgetStoreListInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..0a41a3971b939b9b55165a41c7ded73ae304adf2 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/vo/WidgetStoreListInfo.java @@ -0,0 +1,115 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.vo; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import com.apitable.shared.support.serializer.ImageSerializer; +import com.apitable.shared.support.serializer.NullArraySerializer; +import com.apitable.shared.support.serializer.NullStringSerializer; + +/** + *

+ * Widget Store List Information View + *

+ */ +@Data +@ApiModel("Widget Store List Information View") +public class WidgetStoreListInfo { + + @ApiModelProperty(value = "Package ID", example = "wpkABC", position = 1) + private String widgetPackageId; + + @ApiModelProperty(value = "Widget package name", example = "Chart", position = 2) + private String name; + + @ApiModelProperty(value = "Widget package icon", example = "https://apitable.com/space/2020/12/23/aqa", position = 3) + @JsonSerialize(nullsUsing = NullStringSerializer.class, using = ImageSerializer.class) + private String icon; + + @ApiModelProperty(value = "Cover drawing of component package", example = "https://apitable.com/space/2020/12/23/aqa", position = 3) + @JsonSerialize(nullsUsing = NullStringSerializer.class, using = ImageSerializer.class) + private String cover; + + @ApiModelProperty(value = "DESCRIBE", example = "This is the description of a chart applet", position = 4) + private String description; + + @ApiModelProperty(value = "Widget package version number", example = "1.0.0", position = 5) + private String version; + + @ApiModelProperty(value = "Author Name", position = 6) + private String authorName; + + @ApiModelProperty(value = "Author icon", position = 7) + @JsonSerialize(using = ImageSerializer.class) + private String authorIcon; + + @ApiModelProperty(value = "Author Email", position = 8) + private String authorEmail; + + @ApiModelProperty(value = "Author website address", position = 9) + private String authorLink; + + @ApiModelProperty(value = "Widget package type (0: third party, 1: official)", position = 10) + private Integer packageType; + + @ApiModelProperty(value = "0: Publish to the component store in the space station, 1: Publish to the global app store", position = 11) + private Integer releaseType; + + @ApiModelProperty(value = "Widget package status (0: to be approved; 1: not passed; 2: to be released; 3: online; 4: offline)", example = "3", position = 12) + private Integer status; + + @ApiModelProperty(value = "Whether the applet is authorized by others (false: no, true: yes)", example = "false", position = 13) + private Boolean isEmpower; + + @ApiModelProperty(value = "Widget Owner UUID", position = 14) + private String ownerUuid; + + @ApiModelProperty(value = "Widget Owner Member Id", position = 15) + private String ownerMemberId; + + @ApiModelProperty(value = "Widget Store List Extension Information", position = 16) + private WidgetStoreListExtraInfo extras; + + @JsonIgnore + @ApiModelProperty(value = "Installation environment code", example = "01", position = 17) + private String installEnvCode; + + @JsonIgnore + @ApiModelProperty(value = "Operating environment code", example = "01", position = 18) + private String runtimeEnvCode; + + @ApiModelProperty(value = "Installation environment", example = "panel", position = 19) + private List installEnv; + + @ApiModelProperty(value = "Operating environment", example = "mobile", position = 20) + private List runtimeEnv; + + @Deprecated + @ApiModelProperty(value = "Permission - Obsolete, unified to resource for judgment", example = "[\"UNPUBLISH_WIDGET\",\"TRANSFER_WIDGET\"]", hidden = true) + @JsonSerialize(using = NullArraySerializer.class, nullsUsing = NullArraySerializer.class) + private List permissions; + +} diff --git a/backend-server/application/src/main/java/com/apitable/widget/vo/WidgetTemplatePackageExtraInfo.java b/backend-server/application/src/main/java/com/apitable/widget/vo/WidgetTemplatePackageExtraInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..2c9ad48578918a4e626e41137d3f3ff7b30c7157 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/vo/WidgetTemplatePackageExtraInfo.java @@ -0,0 +1,45 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.vo; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import com.apitable.shared.support.serializer.ImageSerializer; + +/** + *

+ * Template Widget Package Information View + *

+ */ +@Data +@ApiModel("Template Widget Package Extension Information View") +public class WidgetTemplatePackageExtraInfo { + + @ApiModelProperty(value = "Open source address", example = "https://apitable.com/code/2020/12/23/aqa", position = 1) + @JsonSerialize(using = ImageSerializer.class) + private String widgetOpenSource; + + @ApiModelProperty(value = "Template Extension Cover", example = "https://apitable.com/code/2020/12/23/aqa", position = 2) + @JsonSerialize(using = ImageSerializer.class) + private String templateCover; + +} diff --git a/backend-server/application/src/main/java/com/apitable/widget/vo/WidgetTemplatePackageInfo.java b/backend-server/application/src/main/java/com/apitable/widget/vo/WidgetTemplatePackageInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..6c0168a2c42b40915fffc32ccd8185d5a417fcd6 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/widget/vo/WidgetTemplatePackageInfo.java @@ -0,0 +1,69 @@ +/* + * APITable + * Copyright (C) 2022 APITable Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.apitable.widget.vo; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import com.apitable.shared.support.serializer.ImageSerializer; +import com.apitable.shared.support.serializer.NullStringSerializer; + +/** + *

+ * Template Widget Package Information View + *

+ */ +@Data +@ApiModel("Template Widget Package Information View") +public class WidgetTemplatePackageInfo { + + @ApiModelProperty(value = "Widget Package ID", example = "wpkABC", position = 1) + private String widgetPackageId; + + @ApiModelProperty(value = "Widget package name", example = "Chart", position = 2) + private String name; + + @ApiModelProperty(value = "Widget package icon", example = "https://apitable.com/space/2020/12/23/aqa", position = 3) + @JsonSerialize(nullsUsing = NullStringSerializer.class, using = ImageSerializer.class) + private String icon; + + @ApiModelProperty(value = "Widget Package Cover", example = "https://apitable.com/space/2020/12/23/aqa", position = 4) + @JsonSerialize(nullsUsing = NullStringSerializer.class, using = ImageSerializer.class) + private String cover; + + @ApiModelProperty(value = "Describe", example = "This is the description of a chart applet", position = 5) + private String description; + + @ApiModelProperty(value = "Widget package version number", example = "1.0.0", position = 6) + private String version; + + @ApiModelProperty(value = "Code Address", example = "https://apitable.com/code/2020/12/23/aqa", position = 7) + @JsonSerialize(using = ImageSerializer.class) + private String releaseCodeBundle; + + @ApiModelProperty(value = "Source code address", example = "https://apitable.com/code/2020/12/23/aqa", position = 8) + @JsonSerialize(using = ImageSerializer.class) + private String sourceCodeBundle; + + @ApiModelProperty(value = "Widget Package Extension Information", position = 9) + private WidgetTemplatePackageExtraInfo extras; + +} diff --git a/backend-server/application/src/main/java/com/apitable/workspace/service/impl/DatasheetServiceImpl.java b/backend-server/application/src/main/java/com/apitable/workspace/service/impl/DatasheetServiceImpl.java index 466665893f6c182cd7fa15fc8d09d7e58c1cd519..94140f07e7ce17d38f3b294f18a636480839d682 100644 --- a/backend-server/application/src/main/java/com/apitable/workspace/service/impl/DatasheetServiceImpl.java +++ b/backend-server/application/src/main/java/com/apitable/workspace/service/impl/DatasheetServiceImpl.java @@ -50,19 +50,15 @@ import cn.hutool.http.HtmlUtil; import cn.hutool.json.JSONArray; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.baomidou.mybatisplus.extension.toolkit.SqlHelper; -import lombok.extern.slf4j.Slf4j; - -import com.apitable.starter.beetl.autoconfigure.BeetlTemplate; import com.apitable.base.enums.DatabaseException; import com.apitable.base.enums.ParameterException; import com.apitable.control.infrastructure.ControlTemplate; import com.apitable.control.infrastructure.permission.NodePermission; +import com.apitable.core.exception.BusinessException; +import com.apitable.core.util.ExceptionUtil; +import com.apitable.interfaces.social.event.NotificationEvent; import com.apitable.interfaces.social.facade.SocialServiceFacade; import com.apitable.interfaces.social.model.SocialConnectInfo; -import com.apitable.interfaces.widget.facade.WidgetServiceFacade; -import com.apitable.interfaces.widget.model.WidgetCopyOption; import com.apitable.internal.dto.SimpleDatasheetMetaDTO; import com.apitable.organization.entity.TeamMemberRelEntity; import com.apitable.organization.entity.UnitEntity; @@ -78,7 +74,9 @@ import com.apitable.shared.component.notification.NotificationTemplateId; import com.apitable.shared.config.properties.LimitProperties; import com.apitable.shared.sysconfig.i18n.I18nStringsUtil; import com.apitable.shared.util.IdUtil; +import com.apitable.starter.beetl.autoconfigure.BeetlTemplate; import com.apitable.user.mapper.UserMapper; +import com.apitable.widget.service.IWidgetService; import com.apitable.workspace.dto.DataSheetRecordDTO; import com.apitable.workspace.dto.DatasheetMetaDTO; import com.apitable.workspace.dto.DatasheetWidgetDTO; @@ -97,7 +95,6 @@ import com.apitable.workspace.mapper.DatasheetMapper; import com.apitable.workspace.mapper.DatasheetMetaMapper; import com.apitable.workspace.mapper.DatasheetRecordMapper; import com.apitable.workspace.mapper.NodeMapper; -import com.apitable.workspace.observer.DatasheetObserver; import com.apitable.workspace.observer.DatasheetRemindObserver; import com.apitable.workspace.observer.RemindMemberOpSubject; import com.apitable.workspace.observer.remind.MailRemind; @@ -117,8 +114,9 @@ import com.apitable.workspace.service.IDatasheetRecordService; import com.apitable.workspace.service.IDatasheetService; import com.apitable.workspace.vo.DatasheetRecordMapVo; import com.apitable.workspace.vo.DatasheetRecordVo; -import com.apitable.core.exception.BusinessException; -import com.apitable.core.util.ExceptionUtil; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.baomidou.mybatisplus.extension.toolkit.SqlHelper; +import lombok.extern.slf4j.Slf4j; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.stereotype.Service; @@ -186,7 +184,7 @@ public class DatasheetServiceImpl extends ServiceImpl entities) { @@ -563,7 +561,7 @@ public class DatasheetServiceImpl extends ServiceImpl(); } endTime = rubbishUpdatedAt; diff --git a/backend-server/application/src/main/java/com/apitable/workspace/service/impl/NodeServiceImpl.java b/backend-server/application/src/main/java/com/apitable/workspace/service/impl/NodeServiceImpl.java index e9195ea8198a6c744d692eca963bd38228e6f4ea..1ca9b7281d1e70432813ead51e611aa0e8ac72bf 100644 --- a/backend-server/application/src/main/java/com/apitable/workspace/service/impl/NodeServiceImpl.java +++ b/backend-server/application/src/main/java/com/apitable/workspace/service/impl/NodeServiceImpl.java @@ -53,6 +53,7 @@ import com.alibaba.excel.ExcelReader; import com.alibaba.excel.read.builder.ExcelReaderBuilder; import com.alibaba.excel.read.metadata.ReadSheet; import com.alibaba.excel.support.ExcelTypeEnum; +import com.apitable.widget.service.IWidgetService; import com.baomidou.mybatisplus.core.toolkit.IdWorker; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.toolkit.SqlHelper; @@ -70,7 +71,6 @@ import com.apitable.control.infrastructure.permission.NodePermission; import com.apitable.control.infrastructure.role.ControlRole; import com.apitable.interfaces.social.facade.SocialServiceFacade; import com.apitable.interfaces.social.model.SocialConnectInfo; -import com.apitable.interfaces.widget.facade.WidgetServiceFacade; import com.apitable.organization.dto.MemberDTO; import com.apitable.organization.mapper.MemberMapper; import com.apitable.organization.service.IMemberService; @@ -257,7 +257,7 @@ public class NodeServiceImpl extends ServiceImpl impleme private IMemberService iMemberService; @Resource - private WidgetServiceFacade widgetServiceFacade; + private IWidgetService iWidgetService; @Override public String getRootNodeIdBySpaceId(String spaceId) { @@ -1779,7 +1779,7 @@ public class NodeServiceImpl extends ServiceImpl impleme } else if (StrUtil.startWithIgnoreEquals(nodeId, IdRulePrefixEnum.WIDGET.getIdRulePrefixEnum())) { // widget id - result.setSpaceId(widgetServiceFacade.getSpaceIdByWidgetId(nodeId)); + result.setSpaceId(iWidgetService.getSpaceIdByWidgetId(nodeId)); } else { // all other condition query node id diff --git a/backend-server/application/src/main/java/com/apitable/workspace/service/impl/ResourceMetaServiceImpl.java b/backend-server/application/src/main/java/com/apitable/workspace/service/impl/ResourceMetaServiceImpl.java index 860d69bbc01dbb5d6feb8620416719157eb99be0..25ff8519b4eb19d58b3073cf301a4f348c0eaeed 100644 --- a/backend-server/application/src/main/java/com/apitable/workspace/service/impl/ResourceMetaServiceImpl.java +++ b/backend-server/application/src/main/java/com/apitable/workspace/service/impl/ResourceMetaServiceImpl.java @@ -34,26 +34,24 @@ import cn.hutool.core.util.StrUtil; import cn.hutool.http.HtmlUtil; import cn.hutool.json.JSONArray; import cn.hutool.json.JSONUtil; -import com.baomidou.mybatisplus.core.toolkit.IdWorker; -import com.baomidou.mybatisplus.extension.toolkit.SqlHelper; -import lombok.extern.slf4j.Slf4j; - import com.apitable.base.enums.DatabaseException; -import com.apitable.interfaces.widget.facade.WidgetServiceFacade; -import com.apitable.interfaces.widget.model.WidgetCopyOption; +import com.apitable.core.exception.BusinessException; +import com.apitable.core.util.ExceptionUtil; import com.apitable.shared.constants.NodeDescConstants; import com.apitable.shared.util.IdUtil; +import com.apitable.widget.service.IWidgetService; import com.apitable.workspace.dto.DashboardMeta; import com.apitable.workspace.dto.DatasheetWidgetDTO; import com.apitable.workspace.dto.NodeDescParseDTO; +import com.apitable.workspace.entity.ResourceMetaEntity; import com.apitable.workspace.enums.ResourceType; import com.apitable.workspace.mapper.DatasheetWidgetMapper; import com.apitable.workspace.mapper.ResourceMetaMapper; import com.apitable.workspace.service.IDatasheetService; import com.apitable.workspace.service.IResourceMetaService; -import com.apitable.core.exception.BusinessException; -import com.apitable.core.util.ExceptionUtil; -import com.apitable.workspace.entity.ResourceMetaEntity; +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import com.baomidou.mybatisplus.extension.toolkit.SqlHelper; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -77,7 +75,7 @@ public class ResourceMetaServiceImpl implements IResourceMetaService { private DatasheetWidgetMapper datasheetWidgetMapper; @Resource - private WidgetServiceFacade widgetServiceFacade; + private IWidgetService iWidgetService; @Override @Transactional(rollbackFor = Exception.class) @@ -140,7 +138,7 @@ public class ResourceMetaServiceImpl implements IResourceMetaService { // batch generation of new components Map newNodeMap = new HashMap<>(1); newNodeMap.put(originRscId, destRscId); - widgetServiceFacade.copyWidget(new WidgetCopyOption(userId, spaceId, newNodeMap, newWidgetIdMap, newWidgetIdToDstIdMap)); + iWidgetService.copyBatch(userId, spaceId, newNodeMap, newWidgetIdMap, newWidgetIdToDstIdMap); } @Override @@ -183,7 +181,7 @@ public class ResourceMetaServiceImpl implements IResourceMetaService { })); // batch generation of new components - widgetServiceFacade.copyWidget(new WidgetCopyOption(userId, spaceId, newNodeMap, newWidgetIdMap, newWidgetIdToDstMap)); + iWidgetService.copyBatch(userId, spaceId, newNodeMap, newWidgetIdMap, newWidgetIdToDstMap); } @Override diff --git a/backend-server/application/src/main/java/com/apitable/workspace/vo/RubbishNodeVo.java b/backend-server/application/src/main/java/com/apitable/workspace/vo/RubbishNodeVo.java index 1aa0bc71f1829578ea72347574a77063a59f18af..825e18c1f87509cb0c9e4cc089c683de92826520 100644 --- a/backend-server/application/src/main/java/com/apitable/workspace/vo/RubbishNodeVo.java +++ b/backend-server/application/src/main/java/com/apitable/workspace/vo/RubbishNodeVo.java @@ -86,6 +86,12 @@ public class RubbishNodeVo extends BaseNodeInfo { @ApiModelProperty(value = "Retention days", hidden = true) private Integer retainDay; + @ApiModelProperty(value = "default avatar color number", example = "1") + private Integer avatarColor; + + @ApiModelProperty(value = "Nick Name", example = "Zhang San") + private String nickName; + public Integer getRemainDay() { return retainDay - (int) (LocalDate.now(ZoneId.of("+8")).toEpochDay() - deletedAt.toLocalDate().toEpochDay()); } diff --git a/backend-server/application/src/main/proto/serving/RoomServingService.proto b/backend-server/application/src/main/proto/serving/RoomServingService.proto index 473ea91c9f08104cc0f9234c99503dbeb7e6c7aa..e58adda16ba10c5e8d890bf0a94b8d2ed6654ae3 100644 --- a/backend-server/application/src/main/proto/serving/RoomServingService.proto +++ b/backend-server/application/src/main/proto/serving/RoomServingService.proto @@ -42,7 +42,6 @@ service RoomServingService { rpc CopyNodeEffectOt (NodeCopyRo) returns (grpc.common.BasicResult); // delete datasheet effect ot rpc DeleteNodeEffectOt (NodeDeleteRo) returns (grpc.common.BasicResult); - } message WatchRoomRo { @@ -75,6 +74,8 @@ message WatchRoomVo { optional string share_id = 7; optional uint32 create_time = 8; optional ActiveCell active_cell = 9; + optional string nick_name = 10; + optional int32 avatarColor = 11; } message Data { repeated ResourceRevision resource_revisions = 1; @@ -104,6 +105,8 @@ message GetActiveCollaboratorsVo { optional string share_id = 7; optional uint32 create_time = 8; optional ActiveCell active_cell = 9; + optional string nick_name = 10; + optional int32 avatarColor = 11; } message Data { repeated Collaborator collaborators = 1; diff --git a/backend-server/application/src/main/resources/application.yml b/backend-server/application/src/main/resources/application.yml new file mode 100644 index 0000000000000000000000000000000000000000..c7cad017f8d3652c07153465f4e56871995594a2 --- /dev/null +++ b/backend-server/application/src/main/resources/application.yml @@ -0,0 +1,165 @@ +server: + max-http-header-size: ${MAX_HTTP_HEADER_SIZE:32KB} +spring: + main: + allow-circular-references: true + application: + name: service-api + servlet: + multipart: + max-file-size: ${MAX_FILE_SIZE:1GB} + max-request-size: ${MAX_REQUEST_SIZE:1GB} + session: + redis: + namespace: ${SESSION_NAMESPACE:apitable:session} + timeout: ${SESSION_TIMEOUT:30d} + datasource: + url: jdbc:mysql://${MYSQL_HOST:127.0.0.1}:${MYSQL_PORT:3306}/${MYSQL_DATABASE:apitable}?characterEncoding=utf8&autoReconnect=true + username: ${MYSQL_USERNAME:apitable} + password: ${MYSQL_PASSWORD:apitable@com} + # Redis + redis: + host: ${REDIS_HOST:127.0.0.1} + username: ${REDIS_USERNAME:} + password: ${REDIS_PASSWORD:} + port: ${REDIS_PORT:6379} + database: ${REDIS_DB:0} + timeout: 30000 + rabbitmq: + addresses: amqp://${RABBITMQ_HOST:127.0.0.1}:${RABBITMQ_PORT:5672} + username: ${RABBITMQ_USERNAME:apitable} + password: ${RABBITMQ_PASSWORD:apitable@com} + publisher-returns: true + publisher-confirm-type: correlated + listener: + simple: + acknowledge-mode: manual + prefetch: 5 + virtual-host: ${RABBITMQ_VHOST:/} + mail: + host: ${MAIL_HOST:smtp.apitable.com} + username: ${MAIL_USERNAME:support@apitable.com} + password: ${MAIL_PASSWORD:apitable} + port: ${MAIL_PORT:465} + protocol: smtp + properties: + mail.debug: true + mail.smtp.auth: true + mail.smtp.starttls.enable: true + mail.smtp.starttls.required: true + mail.smtp.ssl.enable: ${MAIL_SSL_ENABLE:true} + mail.smtp.socketFactory.class: javax.net.ssl.SSLSocketFactory + sleuth: + otel: + exporter: + jaeger: + enabled: false + config: + trace-id-ratio-based: 0.1 + +cookie: + domain-name: ${DOMAIN_NAME:} +const: + language-tag: ${DEFAULT_LOCALE:en-US} + server-domain: ${SERVER_DOMAIN:} + callback-domain: ${CALLBACK_DOMAIN:} + oss-image-audit-creatable: ${OSS_IMAGE_AUDIT_CREATABLE:false} + ossBuckets: + vk_assets_ltd: + resource-url: ${VK_ASSETS_LTD_URL:} + bucket-name: ${VK_ASSETS_LTD_BUCKET:} + type: ${VK_ASSETS_LTD_TYPE:QNY1} + template-space: ${TEMPLATE_SPACE:spcNTxlv8Drra} + quoteTemplateId: ${QUOTE_TEMPLATE_ID:tpll8mltwrZMT} + quoteEnTemplateId: ${QUOTE_EN_TEMPLATE_ID:spcNA5eN3Sj6Q} + ding-talk-order-datasheet: ${DING_TALK_ORDER_DATASHEET:dstbPAnKDwnPpliqg4} +limit: + space-max-count: ${SPACE_MAX_COUNT:10} + max-file-size: ${EXPORT_MAX_FILE_SIZE:20971520} + max-column-count: ${MAX_COLUMN_COUNT:200} + max-row-count: ${MAX_ROW_COUNT:50000} + template-max-count: ${TEMPLATE_MAX_COUNT:20} + dsb-widget-max-count: ${DSB_WIDGET_MAX_COUNT:30} + dst-robot-max-count: ${DST_ROBOT_MAX_COUNT:10} +socket: + domain: ${SOCKET_DOMAIN:http://127.0.0.1:3001/socket} + +email: + personal: ${EMAIL_PERSONAL:APITable} + +starter: + socketio: + client: + url: ${SOCKET_URL:http://127.0.0.1:3002} + path: ${SOCKET_PATH:/notification} + reconnection-attempts: ${SOCKET_RECONNECTION_ATTEMPTS:2} + reconnection-delay: ${SOCKET_RECONNECTION_DELAY:1000} + timeout: ${SOCKET_TIMEOUT:1000} + oss: + enabled: ${OSS_ENABLED:true} + type: ${OSS_CLIENT_TYPE:aws} + aws: + access-key-id: ${AWS_ACCESS_KEY:} + access-key-secret: ${AWS_ACCESS_SECRET:} + endpoint: ${AWS_ENDPOINT:s3-cn-south-1.qiniucs.com} + region: ${AWS_REGION:cn-south-1} + qiniu: + access-key: ${QINIU_ACCESS_KEY:' '} + secret-key: ${QINIU_SECRET_KEY:' '} + region: ${QINIU_REGION:z2} + download-domain: ${QINIU_DOWNLOAD_DOMAIN:s1.vika.cn} + upload-url: ${QINIU_UPLOAD_URL:https://up-z2.qiniup.com} + callback: + url: ${QINIU_CALLBACK_URL:http://fktcca.natappfree.cc/api/v1/asset/qiniu/uploadCallback} + body-type: ${QINIU_CALLBACK_BODY_TYPE:application/json} + minio: + endpoint: ${MINIO_ENDPOINT:http://minio:9000} + access-key: ${MINIO_ACCESS_KEY:apitable} + secret-key: ${MINIO_SECRET_KEY:apitable@com} + bucket-policy: ${MINIO_BUCKET_POLICY:'{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetBucketLocation"],"Resource":["arn:aws:s3:::vk-assets-ltd"]},{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:ListBucket"],"Resource":["arn:aws:s3:::vk-assets-ltd"],"Condition":{"StringEquals":{"s3:prefix":["*.*"]}}},{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetObject"],"Resource":["arn:aws:s3:::vk-assets-ltd/*.**"]}]}'} + sms: + enabled: ${SMS_ENABLED:false} + local-area-code: ${SMS_LOCAL_AREA_CODE:+86} + local: + type: ${SMS_LOCAL_TYPE:tencent} + tencent: + app-id: ${SMS_TENCENT_APP_ID:} + app-key: ${SMS_TENCENT_APP_KEY:} + sign: ${SMS_TENCENT_SIGN:Cloud} + outland: + type: ${SMS_OUTLAND_TYPE:yunpian} + yunpian: + apikey: ${SMS_YUNPIAN_APIKEY:} + mail: + enabled: ${MAIL_ENABLED:false} + type: ${MAIL_TYPE:} + region: ${MAIL_REGION:} + from: ${MAIL_FROM:} + reply: ${MAIL_REPLY:} + tencent: + secret-id: ${MAIL_TENCENT_SECRET_ID:} + secret-key: ${MAIL_TENCENT_SECRET_KEY:} + + +sentry: + dsn: ${SENTRY_DSN:} + send-default-pii: true + environment: ${ENV} + use-git-commit-id-as-release: false + max-breadcrumbs: 150 + traces-sample-rate: 1.0 + logging: + minimum-breadcrumb-level: debug + minimum-event-level: info + +grpc: + server: + port: ${BACKEND_GRPC_PORT:0} + client: + nest-grpc-server: + address: ${NEST_GRPC_ADDRESS:static://localhost:3334} + dingtalk-grpc-server: + address: ${DINGTALK_GRPC_ADDRESS:} + security: + clientAuthEnabled: false + authorityOverride: ${DINGTALK_GRPC_AUTHORITY:} diff --git a/backend-server/application/src/main/resources/default.properties b/backend-server/application/src/main/resources/default.properties index 8a2b578f0f51d86da999126cb9527ec45e0ee2cf..1b1fb2774b2ead8825b0dcf9c1e40f2e2e849cce 100644 --- a/backend-server/application/src/main/resources/default.properties +++ b/backend-server/application/src/main/resources/default.properties @@ -19,14 +19,6 @@ spring.redis.lettuce.pool.max-idle=8 spring.redis.lettuce.pool.min-idle=0 spring.redis.lettuce.pool.max-active=8 spring.redis.lettuce.pool.max-wait=-1ms -# mail Developers use regular port 25, production uses encrypted port 465 -spring.mail.properties.mail.smtp.auth=true -spring.mail.properties.mail.smtp.host=465 -spring.mail.properties.mail.smtp.socketFactory.port=465 -spring.mail.properties.mail.smtp.starttls.enable=true -spring.mail.properties.mail.smtp.starttls.required=true -spring.mail.properties.mail.smtp.ssl.enable=true -spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory # actuator management.health.mail.enabled=false management.endpoint.health.enabled=true diff --git a/backend-server/application/src/main/resources/excel/contact_example.xlsx b/backend-server/application/src/main/resources/excel/contact_example.xlsx index 4a6925adbb347bf1f8baaf24d79f7d5ea6ef1755..3c91df8292b5242707d8abbe75cab48b42135dc8 100644 Binary files a/backend-server/application/src/main/resources/excel/contact_example.xlsx and b/backend-server/application/src/main/resources/excel/contact_example.xlsx differ diff --git a/backend-server/application/src/main/resources/logback-spring.xml b/backend-server/application/src/main/resources/logback-spring.xml index 4844d1ee5eb6534a53a7e564a9ca49b5c1be5934..da0031d3180f87b3b72a7a67d156bb8a493d5146 100755 --- a/backend-server/application/src/main/resources/logback-spring.xml +++ b/backend-server/application/src/main/resources/logback-spring.xml @@ -18,10 +18,14 @@ --> + + + value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/> + + logs/${LOG_FILE_NAME}_error.log @@ -34,7 +38,7 @@ true - Log >>> %d{ISO8601} %-5level [%t] %C{1.}: %msg%n%throwable + ${FILE_LOG_PATTERN} utf-8 @@ -61,7 +65,7 @@ true - Log >>> %d{ISO8601} %-5level [%t] %C{1.}: %msg%n%throwable + ${FILE_LOG_PATTERN} utf-8 @@ -74,18 +78,18 @@ - 「Log」>>> ${CONSOLE_LOG_PATTERN} + ${CONSOLE_LOG_PATTERN} utf-8 - + - + diff --git a/backend-server/application/src/main/resources/mapper/organization/MemberMapper.xml b/backend-server/application/src/main/resources/mapper/organization/MemberMapper.xml index 1fa4ffabc4b8ca19fe31b328298d59b7393b2095..e31273da4e79083e75768d40dcda2c1a5662e4ad 100644 --- a/backend-server/application/src/main/resources/mapper/organization/MemberMapper.xml +++ b/backend-server/application/src/main/resources/mapper/organization/MemberMapper.xml @@ -377,7 +377,7 @@ select id, is_deleted from ${tablePrefix}unit_member where user_id = #{userId} and space_id = #{spaceId} - ORDER BY id desc limit 1 + ORDER BY is_deleted asc, id desc limit 1 - + - - - - SELECT vs.space_id, vs.name, vs.logo, - vst.app_type, if(vs.pre_deletion_time IS NULL, 0, 1) pre_deleted, IF(vs.owner = m.id, 1, 0) admin, - IF(m.`is_point` = 1, 1, 0) point, - IF(vstb.id IS NOT NULL, 1, 0) enabled, - IF(vst.id IS NOT NULL, vst.platform, 0) AS platform + IF(m.`is_point` = 1, 1, 0) point FROM ${tablePrefix}space vs JOIN ${tablePrefix}unit_member m ON vs.space_id = m.space_id AND m.is_deleted = 0 AND m.user_id = #{userId} - LEFT JOIN ${tablePrefix}social_tenant_bind vstb ON vstb.space_id = vs.space_id - LEFT JOIN ${tablePrefix}social_tenant vst ON vst.tenant_id = vstb.tenant_id AND vst.`status` = 1 AND ((vstb.app_id is - NULL ) OR (vst.app_id = vstb.app_id)) WHERE vs.is_deleted = 0 GROUP BY m.updated_at, m.`status`, vs.space_id ORDER BY m.updated_at DESC, m.`status` DESC diff --git a/backend-server/application/src/main/resources/mapper/template/TemplatePropertyRelMapper.xml b/backend-server/application/src/main/resources/mapper/template/TemplatePropertyRelMapper.xml index f78e904e8511b1f85940689226770f93ce1de31b..0700df264bf6e4d3d567671ce675a09b5c75dbda 100644 --- a/backend-server/application/src/main/resources/mapper/template/TemplatePropertyRelMapper.xml +++ b/backend-server/application/src/main/resources/mapper/template/TemplatePropertyRelMapper.xml @@ -37,21 +37,12 @@ - SELECT template_id from ${tablePrefix}template_property_rel - where property_id = #{propertyId} + where property_code = #{propertyCode} - - DELETE - from ${tablePrefix}template_property_rel where property_id in - - #{item} - - - SELECT vu.email FROM ${tablePrefix}user vu diff --git a/backend-server/application/src/main/resources/mapper/widget/WidgetMapper.xml b/backend-server/application/src/main/resources/mapper/widget/WidgetMapper.xml new file mode 100644 index 0000000000000000000000000000000000000000..6a1b6f638538d29df2b5e2e692d98842d1a3eb12 --- /dev/null +++ b/backend-server/application/src/main/resources/mapper/widget/WidgetMapper.xml @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + INSERT INTO ${tablePrefix}widget(id, space_id, node_id, package_id, widget_id, + name, storage, created_by, updated_by) + VALUES + + + #{entity.id}, #{entity.spaceId}, #{entity.nodeId}, #{entity.packageId}, + #{entity.widgetId}, + #{entity.name}, #{entity.storage}, #{entity.createdBy}, + #{entity.updatedBy} + + + + + + diff --git a/backend-server/application/src/main/resources/mapper/widget/WidgetPackageMapper.xml b/backend-server/application/src/main/resources/mapper/widget/WidgetPackageMapper.xml new file mode 100644 index 0000000000000000000000000000000000000000..d50d7c1ed2ba55135ab5823b832b4738b24b4ddb --- /dev/null +++ b/backend-server/application/src/main/resources/mapper/widget/WidgetPackageMapper.xml @@ -0,0 +1,369 @@ + + + + + + + + + + + + INSERT INTO ${tablePrefix}widget_package(id, package_id, name, name_en, icon, cover, description, version, + status, created_by) + VALUE + + + #{item.id}, #{item.packageId}, #{item.name}, #{item.nameEn}, #{item.icon}, #{item.cover}, + #{item.description}, #{item.version}, #{item.status}, #{item.createdBy} + + + + + + UPDATE ${tablePrefix}widget_package + SET installed_num = installed_num + #{times} + WHERE package_id = #{widgetPackageId} + + + + + + + + UPDATE ${tablePrefix}widget_package + + status = #{status} + + ,release_id = #{releaseId} + + ,updated_by = #{userId} + + WHERE package_id = #{packageId} + AND is_deleted = 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + UPDATE ${tablePrefix}widget_package vwp ,${tablePrefix}widget_package_auth_space vwpas + + + vwp.is_template = #{globalWidgetInfo.isTemplate}, + + + vwp.is_enabled = #{globalWidgetInfo.isEnabled}, + + + vwp.widget_body = JSON_SET(IFNULL(widget_body, '{}'), '$.widgetOpenSource', + #{globalWidgetInfo.openSourceAddres}), + + + vwp.widget_body = JSON_SET(IFNULL(widget_body, '{}'), '$.templateCover', + #{globalWidgetInfo.templateCover}), + + + vwp.widget_body = JSON_SET(IFNULL(widget_body, '{}'), '$.website', #{globalWidgetInfo.website}), + + + vwpas.widget_sort = #{globalWidgetInfo.widgetSort}, + + + WHERE vwp.package_id = vwpas.package_id + AND vwp.release_type = 1 + AND vwpas.type = 0 + AND vwp.status = 3 + AND vwp.package_id = #{globalWidgetInfo.packageId} + + + + + + + + + + + + UPDATE ${tablePrefix}widget_package + SET widget_body = #{widgetBody} + WHERE id = #{id} + + diff --git a/backend-server/application/src/main/resources/mapper/workspace/NodeMapper.xml b/backend-server/application/src/main/resources/mapper/workspace/NodeMapper.xml index 265fb760badcdbecf1952adf0007bc4b089a2a92..42a0ea497e6a660737fda2d51afa5887f078d8bb 100644 --- a/backend-server/application/src/main/resources/mapper/workspace/NodeMapper.xml +++ b/backend-server/application/src/main/resources/mapper/workspace/NodeMapper.xml @@ -694,8 +694,10 @@ SELECT vn.node_id, vn.space_id, vn.node_name, vn.type, vn.icon, vn.updated_at AS deletedAt, vn.deleted_path AS delPath, - vu.uuid, vu.avatar, vum.member_name, #{retainDay} AS retainDay, + vu.uuid, vu.avatar, vu.color AS avatarColor, vu.nick_name AS nickName, vum.member_name, #{retainDay} AS retainDay, IFNULL(vu.is_social_name_modified, 2) > 0 AS isNickNameModified, IFNULL(vum.is_social_name_modified, 2) > 0 AS isMemberNameModified FROM ${tablePrefix}node vn diff --git a/backend-server/application/src/main/resources/sysconfig/notification.json b/backend-server/application/src/main/resources/sysconfig/notification.json index 59d4d203059dcffcb3e5e8cb67dec2fd6b4c8f4d..f1e1b1038767853f1146aa5fdf2fb8c2cceb1fbf 100644 --- a/backend-server/application/src/main/resources/sysconfig/notification.json +++ b/backend-server/application/src/main/resources/sysconfig/notification.json @@ -1,5 +1,15 @@ { "types": { + "system": { + "format_string": "notify_type_system", + "id": "system", + "tag": "system" + }, + "space": { + "format_string": "notify_type_space", + "id": "space", + "tag": "space" + }, "member": { "format_string": "notify_type_member", "id": "member", @@ -9,16 +19,6 @@ "format_string": "notify_type_datasheet", "id": "record", "tag": "record" - }, - "space": { - "format_string": "notify_type_space", - "id": "space", - "tag": "space" - }, - "system": { - "format_string": "notify_type_system", - "id": "system", - "tag": "system" } }, "templates": { @@ -26,9 +26,6 @@ "can_jump": true, "to_tag": "members", "notifications_type": "member", - "formatString": [ - "space_add_sub_admin" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -41,9 +38,6 @@ "can_jump": true, "to_tag": "members", "notifications_type": "member", - "formatString": [ - "space_assigned_to_group" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -56,9 +50,6 @@ "can_jump": true, "to_tag": "members", "notifications_type": "member", - "formatString": [ - "space_assigned_to_role" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -67,13 +58,25 @@ "url": "/management", "is_component": true }, + "capacity_limit": { + "can_jump": true, + "to_tag": "space_admins", + "notifications_type": "space", + "is_notification": true, + "is_mail": true, + "is_browser": true, + "mail_template_subject": "subject.subscribed.capacity.limit", + "billing_notify": "max_capacity_size_in_bytes", + "format_string": "capacity_limit", + "id": "capacity_limit", + "url": "/management", + "frequency": 1, + "is_component": true + }, "changed_ordinary_user": { "can_jump": true, "to_tag": "members", "notifications_type": "member", - "formatString": [ - "space_changed_ordinary_user" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -81,13 +84,46 @@ "id": "changed_ordinary_user", "is_component": true }, + "comment_mentioned": { + "can_jump": true, + "to_tag": "members", + "notifications_type": "record", + "is_notification": true, + "is_mobile": true, + "is_mail": true, + "is_browser": true, + "format_string": "comment_mentioned", + "id": "comment_mentioned", + "url": "/workbench", + "is_component": true + }, + "datasheet_limit": { + "can_jump": true, + "to_tag": "space_admins", + "notifications_type": "space", + "is_notification": true, + "is_mail": true, + "is_browser": true, + "mail_template_subject": "subject.subscribed.datasheet.limit", + "billing_notify": "max_sheet_nums", + "format_string": "datasheet_limit", + "id": "datasheet_limit", + "url": "/management", + "frequency": 1, + "is_component": true + }, + "integral_income_notify": { + "to_tag": "users", + "notifications_type": "system", + "is_notification": true, + "format_string": "integral_income_notify", + "id": "integral_income_notify", + "is_component": true + }, "invite_member_toadmin": { "can_jump": true, "to_tag": "space_member_admins", "notifications_type": "member", - "formatString": [ - "invite_member_toadmin" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -100,9 +136,6 @@ "can_jump": true, "to_tag": "myself", "notifications_type": "member", - "formatString": [ - "invite_member_tomyself" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -114,9 +147,6 @@ "can_jump": true, "to_tag": "members", "notifications_type": "member", - "formatString": [ - "invite_member_touser" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -125,25 +155,10 @@ "url": "/workbench", "is_component": true }, - "member_applied_to_close_account": { - "to_tag": "space_member_admins", - "notifications_type": "member", - "formatString": [ - "member_applied_to_close_account" - ], - "is_notification": true, - "is_browser": true, - "format_string": "member_applied_to_close_account", - "id": "member_applied_to_close_account", - "is_component": true - }, "quit_space": { "can_jump": true, "to_tag": "space_member_admins", "notifications_type": "member", - "formatString": [ - "member_quit_space" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -156,9 +171,6 @@ "can_jump": true, "to_tag": "members", "notifications_type": "member", - "formatString": [ - "remove_from_group" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -171,9 +183,6 @@ "can_jump": true, "to_tag": "members", "notifications_type": "member", - "formatString": [ - "remove_from_role" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -186,9 +195,6 @@ "can_jump": true, "to_tag": "space_member_admins", "notifications_type": "member", - "formatString": [ - "user_removed_by_space_toadmin" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -199,9 +205,6 @@ "removed_from_space_touser": { "to_tag": "members", "notifications_type": "member", - "formatString": [ - "user_removed_by_space_touser" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -214,9 +217,6 @@ "can_jump": true, "to_tag": "myself", "notifications_type": "member", - "formatString": [ - "removed_member_tomyself" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -224,13 +224,47 @@ "id": "removed_member_tomyself", "is_component": true }, + "server_pre_publish": { + "to_tag": "all_users", + "notifications_type": "system", + "is_notification": true, + "is_mobile": true, + "is_browser": true, + "format_string": "server_pre_publish", + "id": "server_pre_publish", + "is_component": true + }, + "single_record_comment_mentioned": { + "can_jump": true, + "to_tag": "members", + "notifications_type": "record", + "is_notification": true, + "is_mobile": true, + "is_mail": true, + "is_browser": true, + "format_string": "single_record_comment_mentioned", + "id": "single_record_comment_mentioned", + "url": "/workbench", + "is_component": true + }, + "single_record_member_mention": { + "can_jump": true, + "to_tag": "members", + "notifications_type": "record", + "is_notification": true, + "is_mobile": true, + "is_mail": true, + "is_browser": true, + "mail_template_subject": "subject.datasheet.remind", + "format_string": "single_record_member_mention", + "id": "single_record_member_mention", + "url": "/workbench", + "is_component": true + }, "space_add_primary_admin": { "can_jump": true, "to_tag": "members", "notifications_type": "member", - "formatString": [ - "space_add_primary_admin" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -238,12 +272,18 @@ "id": "space_add_primary_admin", "is_component": true }, + "space_deleted": { + "to_tag": "all_members", + "notifications_type": "space", + "is_notification": true, + "is_browser": true, + "format_string": "space_has_been_deleted", + "id": "space_deleted", + "is_component": true + }, "space_join_apply": { "to_tag": "space_member_admins", "notifications_type": "member", - "formatString": [ - "space_join_apply" - ], "is_notification": true, "is_mail": true, "format_string": "space_join_apply", @@ -254,9 +294,6 @@ "can_jump": true, "to_tag": "users", "notifications_type": "member", - "formatString": [ - "space_join_apply_approved" - ], "is_notification": true, "format_string": "space_join_apply_approved", "id": "space_join_apply_approved", @@ -266,60 +303,63 @@ "space_join_apply_refused": { "to_tag": "users", "notifications_type": "member", - "formatString": [ - "space_join_apply_refused" - ], "is_notification": true, "format_string": "space_join_apply_refused", "id": "space_join_apply_refused", "is_component": true }, - "comment_mentioned": { + "member_applied_to_close_account": { + "to_tag": "space_member_admins", + "notifications_type": "member", + "is_notification": true, + "is_browser": true, + "format_string": "member_applied_to_close_account", + "id": "member_applied_to_close_account", + "is_component": true + }, + "space_name_change": { "can_jump": true, - "to_tag": "members", - "notifications_type": "record", - "formatString": [ - "comment_mentioned" - ], + "to_tag": "all_members", + "notifications_type": "space", "is_notification": true, - "is_mobile": true, - "is_mail": true, "is_browser": true, - "format_string": "comment_mentioned", - "id": "comment_mentioned", + "format_string": "notification_space_name_changed", + "id": "space_name_change", "url": "/workbench", "is_component": true }, - "single_record_comment_mentioned": { + "space_recover": { "can_jump": true, - "to_tag": "members", - "notifications_type": "record", - "formatString": [ - "single_record_comment_mentioned" - ], + "to_tag": "all_members", + "notifications_type": "space", + "is_notification": true, + "is_browser": true, + "format_string": "space_has_been_recover", + "id": "space_recover", + "is_component": true + }, + "space_members_limit": { + "can_jump": true, + "to_tag": "space_admins", + "notifications_type": "space", "is_notification": true, - "is_mobile": true, "is_mail": true, "is_browser": true, - "format_string": "single_record_comment_mentioned", - "id": "single_record_comment_mentioned", - "url": "/workbench", + "format_string": "space_members_limit", + "id": "space_members_limit", + "url": "/management", "is_component": true }, - "single_record_member_mention": { + "user_field": { "can_jump": true, "to_tag": "members", "notifications_type": "record", - "formatString": [ - "single_record_member_mention" - ], "is_notification": true, "is_mobile": true, "is_mail": true, "is_browser": true, - "mail_template_subject": "subject.datasheet.remind", - "format_string": "single_record_member_mention", - "id": "single_record_member_mention", + "format_string": "field_set_you_by_user", + "id": "user_field", "url": "/workbench", "is_component": true }, @@ -327,9 +367,6 @@ "can_jump": true, "to_tag": "members", "notifications_type": "record", - "formatString": [ - "subscribed_record_cell_updated" - ], "is_notification": true, "is_mobile": true, "is_mail": true, @@ -343,9 +380,6 @@ "can_jump": true, "to_tag": "members", "notifications_type": "record", - "formatString": [ - "subscribed_record_commented" - ], "is_notification": true, "is_mobile": true, "is_mail": true, @@ -355,112 +389,91 @@ "id": "subscribed_record_commented", "url": "/workbench" }, - "user_field": { + "task_reminder": { "can_jump": true, "to_tag": "members", - "notifications_type": "record", - "formatString": [ - "field_set_you_by_user" - ], + "notifications_type": "space", "is_notification": true, "is_mobile": true, "is_mail": true, "is_browser": true, - "format_string": "field_set_you_by_user", - "id": "user_field", + "mail_template_subject": "subject.task.reminder", + "format_string": "task_reminder", + "id": "task_reminder", "url": "/workbench", "is_component": true }, - "apply_space_beta_feature_success_notify_all": { - "can_jump": true, - "to_tag": "all_members", - "notifications_type": "space", - "formatString": [ - "apply_space_beta_feature_success_notify_all" - ], - "is_notification": true, - "format_string": "apply_space_beta_feature_success_notify_all", - "id": "apply_space_beta_feature_success_notify_all" + "web_publish": { + "to_tag": "all_users", + "notifications_type": "system", + "is_mobile": true, + "format_string": "web_publish", + "id": "web_publish", + "is_component": true }, - "apply_space_beta_feature_success_notify_me": { + "datasheet_record_limit": { "can_jump": true, - "to_tag": "myself", + "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "apply_space_beta_feature_success_notify_me" - ], "is_notification": true, - "format_string": "apply_space_beta_feature_success_notify_me", - "id": "apply_space_beta_feature_success_notify_me" + "is_mail": true, + "is_browser": true, + "mail_template_subject": "subject.subscribed.datasheet.record.limit", + "billing_notify": "max_rows_per_sheet", + "format_string": "datasheet_record_limit", + "id": "datasheet_record_limit", + "url": "/management", + "frequency": 1, + "is_component": true }, - "capacity_limit": { + "space_subscription_notify": { "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "capacity_limit" - ], "is_notification": true, - "is_mail": true, "is_browser": true, - "mail_template_subject": "subject.subscribed.capacity.limit", - "billing_notify": [], - "format_string": "capacity_limit", - "id": "capacity_limit", + "format_string": "space_subscription_notify", + "id": "space_subscription_notify", "url": "/management", - "frequency": 1, "is_component": true }, - "datasheet_limit": { + "space_subscription_end_notify": { "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "datasheet_limit" - ], "is_notification": true, "is_mail": true, "is_browser": true, - "mail_template_subject": "subject.subscribed.datasheet.limit", - "billing_notify": [], - "format_string": "datasheet_limit", - "id": "datasheet_limit", + "id": "space_subscription_end_notify", "url": "/management", - "frequency": 1, "is_component": true }, - "datasheet_record_limit": { + "space_seats_limit": { "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "datasheet_record_limit" - ], "is_notification": true, "is_mail": true, "is_browser": true, - "mail_template_subject": "subject.subscribed.datasheet.record.limit", - "billing_notify": [], - "format_string": "datasheet_record_limit", - "id": "datasheet_record_limit", + "mail_template_subject": "subject.subscribed.seats.limit", + "billing_notify": "max_seats", + "format_string": "space_seats_limit", + "id": "space_seats_limit", "url": "/management", "frequency": 1, "is_component": true }, - "space_admin_limit": { + "space_record_limit": { "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_admin_limit" - ], "is_notification": true, "is_mail": true, "is_browser": true, - "mail_template_subject": "subject.subscribed.admin.limit", - "billing_notify": [], - "format_string": "space_admin_limit", - "id": "space_admin_limit", + "mail_template_subject": "subject.subscribed.record.limit", + "billing_notify": "max_rows_in_space", + "format_string": "space_record_limit", + "id": "space_record_limit", "url": "/management", "frequency": 1, "is_component": true @@ -469,14 +482,11 @@ "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_api_limit" - ], "is_notification": true, "is_mail": true, "is_browser": true, "mail_template_subject": "subject.subscribed.api.limit", - "billing_notify": [], + "billing_notify": "max_api_call", "format_string": "space_api_limit", "id": "space_api_limit", "url": "/management", @@ -487,329 +497,101 @@ "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_calendar_limit" - ], "is_notification": true, "is_mail": true, "is_browser": true, "mail_template_subject": "subject.subscribed.calendar.limit", - "billing_notify": [], + "billing_notify": "max_calendar_views_in_space", "format_string": "space_calendar_limit", "id": "space_calendar_limit", "url": "/management", "frequency": 1, "is_component": true }, - "space_certification_fail_notify": { + "space_form_limit": { "can_jump": true, - "to_tag": "users", + "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_certification_fail_notify" - ], "is_notification": true, + "is_mail": true, "is_browser": true, - "format_string": "space_certification_fail_notify", - "id": "space_certification_fail_notify", + "mail_template_subject": "subject.subscribed.form.limit", + "billing_notify": "max_form_views_in_space", + "format_string": "space_form_limit", + "id": "space_form_limit", "url": "/management", - "is_component": true - }, - "space_certification_notify": { - "can_jump": true, - "to_tag": "users", - "notifications_type": "space", - "formatString": [ - "space_certification_notify" - ], - "is_notification": true, - "is_browser": true, - "format_string": "space_certification_notify", - "id": "space_certification_notify", - "url": "/management", - "is_component": true - }, - "space_deleted": { - "times": 1, - "to_tag": "all_members", - "notifications_type": "space", - "formatString": [ - "space_has_been_deleted" - ], - "is_notification": true, - "is_browser": true, - "format_string": "space_has_been_deleted", - "id": "space_deleted", - "is_component": true - }, - "space_dingtalk_notify": { - "can_jump": true, - "times": 1, - "to_tag": "space_admins", - "notifications_type": "space", - "formatString": [ - "space_dingtalk_notify" - ], - "is_notification": true, - "is_mail": true, - "is_browser": true, - "format_string": "space_dingtalk_notify", - "id": "space_dingtalk_notify", - "url": "/management", - "is_component": true - }, - "space_field_permission_limit": { - "can_jump": true, - "to_tag": "space_admins", - "notifications_type": "space", - "formatString": [ - "space_field_permission_limit" - ], - "is_notification": true, - "is_mail": true, - "is_browser": true, - "mail_template_subject": "subject.subscribed.field.permission.limit", - "billing_notify": [], - "format_string": "space_field_permission_limit", - "id": "space_field_permission_limit", - "url": "/management", - "frequency": 1, - "is_component": true - }, - "space_file_permission_limit": { - "can_jump": true, - "to_tag": "space_admins", - "notifications_type": "space", - "formatString": [ - "space_file_permission_limit" - ], - "is_notification": true, - "is_mail": true, - "is_browser": true, - "mail_template_subject": "subject.subscribed.file.permission.limit", - "billing_notify": [], - "format_string": "space_file_permission_limit", - "id": "space_file_permission_limit", - "url": "/management", - "frequency": 1, - "is_component": true - }, - "space_form_limit": { - "can_jump": true, - "to_tag": "space_admins", - "notifications_type": "space", - "formatString": [ - "space_form_limit" - ], - "is_notification": true, - "is_mail": true, - "is_browser": true, - "mail_template_subject": "subject.subscribed.form.limit", - "billing_notify": [], - "format_string": "space_form_limit", - "id": "space_form_limit", - "url": "/management", - "frequency": 1, + "frequency": 1, "is_component": true }, "space_gantt_limit": { "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_gantt_limit" - ], "is_notification": true, "is_mail": true, "is_browser": true, "mail_template_subject": "subject.subscribed.gannt.limit", - "billing_notify": [], + "billing_notify": "max_gantt_views_in_space", "format_string": "space_gantt_limit", "id": "space_gantt_limit", "url": "/management", "frequency": 1, "is_component": true }, - "space_lark_notify": { - "can_jump": true, - "times": 1, - "to_tag": "space_admins", - "notifications_type": "space", - "formatString": [ - "space_lark_notify" - ], - "is_notification": true, - "is_mail": true, - "is_browser": true, - "format_string": "space_lark_notify", - "id": "space_lark_notify", - "url": "/management", - "is_component": true - }, - "space_members_limit": { + "space_field_permission_limit": { "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_members_limit" - ], "is_notification": true, "is_mail": true, "is_browser": true, - "format_string": "space_members_limit", - "id": "space_members_limit", + "mail_template_subject": "subject.subscribed.field.permission.limit", + "billing_notify": "field_permission_nums", + "format_string": "space_field_permission_limit", + "id": "space_field_permission_limit", "url": "/management", - "systemControl": true, + "frequency": 1, "is_component": true }, - "space_mirror_limit": { + "space_admin_limit": { "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_mirror_limit" - ], "is_notification": true, "is_mail": true, "is_browser": true, - "mail_template_subject": "subject.subscribed.mirror.limit", - "billing_notify": [], - "format_string": "space_mirror_limit", - "id": "space_mirror_limit", + "mail_template_subject": "subject.subscribed.admin.limit", + "billing_notify": "max_admin_nums", + "format_string": "space_admin_limit", + "id": "space_admin_limit", "url": "/management", "frequency": 1, "is_component": true }, - "space_name_change": { - "can_jump": true, - "to_tag": "all_members", - "notifications_type": "space", - "formatString": [ - "notification_space_name_changed" - ], - "is_notification": true, - "is_browser": true, - "format_string": "notification_space_name_changed", - "id": "space_name_change", - "url": "/workbench", - "is_component": true - }, - "space_paid_notify": { - "to_tag": "users", - "notifications_type": "space", - "formatString": [ - "space_paid_notify" - ], - "is_notification": true, - "format_string": "space_paid_notify", - "id": "space_paid_notify", - "is_component": true - }, "space_rainbow_label_limit": { "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_rainbow_label_limit" - ], "is_notification": true, "is_mail": true, "is_browser": true, "mail_template_subject": "subject.subscribed.rainbow.label.limit", - "billing_notify": [], + "billing_notify": "rainbow_label", "format_string": "space_rainbow_label_limit", "id": "space_rainbow_label_limit", "url": "/management", "frequency": 1, "is_component": true }, - "space_record_limit": { - "can_jump": true, - "to_tag": "space_admins", - "notifications_type": "space", - "formatString": [ - "space_record_limit" - ], - "is_notification": true, - "is_mail": true, - "is_browser": true, - "mail_template_subject": "subject.subscribed.record.limit", - "billing_notify": [], - "format_string": "space_record_limit", - "id": "space_record_limit", - "url": "/management", - "frequency": 1, - "is_component": true - }, - "space_recover": { - "can_jump": true, - "to_tag": "all_members", - "notifications_type": "space", - "formatString": [ - "space_has_been_recover" - ], - "is_notification": true, - "is_browser": true, - "format_string": "space_has_been_recover", - "id": "space_recover", - "is_component": true - }, - "space_seats_limit": { - "can_jump": true, - "to_tag": "space_admins", - "notifications_type": "space", - "formatString": [ - "space_seats_limit" - ], - "is_notification": true, - "is_mail": true, - "is_browser": true, - "mail_template_subject": "subject.subscribed.seats.limit", - "billing_notify": [], - "format_string": "space_seats_limit", - "id": "space_seats_limit", - "url": "/management", - "frequency": 1, - "is_component": true - }, - "space_subscription_end_notify": { - "can_jump": true, - "times": 1, - "to_tag": "space_admins", - "notifications_type": "space", - "is_notification": true, - "is_mail": true, - "is_browser": true, - "id": "space_subscription_end_notify", - "url": "/management", - "is_component": true - }, - "space_subscription_notify": { - "can_jump": true, - "to_tag": "space_admins", - "notifications_type": "space", - "formatString": [ - "space_subscription_notify" - ], - "is_notification": true, - "is_browser": true, - "format_string": "space_subscription_notify", - "id": "space_subscription_notify", - "url": "/management", - "is_component": true - }, "space_time_machine_limit": { "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_time_machine_limit" - ], "is_notification": true, "is_mail": true, "is_browser": true, "mail_template_subject": "subject.subscribed.time.machine.limit", - "billing_notify": [], + "billing_notify": "max_remain_timemachine_days", "format_string": "space_time_machine_limit", "id": "space_time_machine_limit", "url": "/management", @@ -820,120 +602,97 @@ "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_trash_limit" - ], "is_notification": true, "is_mail": true, "is_browser": true, "mail_template_subject": "subject.subscribed.trash.limit", - "billing_notify": [], + "billing_notify": "max_remain_trash_days", "format_string": "space_trash_limit", "id": "space_trash_limit", "url": "/management", "frequency": 1, "is_component": true }, - "space_trial": { + "space_watermark_notify": { "can_jump": true, - "times": 1, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_trial" - ], "is_notification": true, "is_mail": true, "is_browser": true, - "format_string": "space_trial", - "id": "space_trial", + "billing_notify": "watermark", + "format_string": "space_watermark_notify", + "id": "space_watermark_notify", "url": "/management", "is_component": true }, - "space_vika_paid_notify": { + "space_yozooffice_notify": { "can_jump": true, - "to_tag": "users", + "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_vika_paid_notify" - ], "is_notification": true, - "format_string": "space_vika_paid_notify", - "id": "space_vika_paid_notify", + "is_mail": true, + "is_browser": true, + "billing_notify": "integration_yozo_office", + "format_string": "space_yozooffice_notify", + "id": "space_yozooffice_notify", "url": "/management", "is_component": true }, - "space_watermark_notify": { + "space_lark_notify": { "can_jump": true, - "times": 1, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_watermark_notify" - ], "is_notification": true, "is_mail": true, "is_browser": true, - "format_string": "space_watermark_notify", - "id": "space_watermark_notify", + "billing_notify": "integration_feishu", + "format_string": "space_lark_notify", + "id": "space_lark_notify", "url": "/management", "is_component": true }, - "space_wecom_notify": { + "space_dingtalk_notify": { "can_jump": true, - "times": 1, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_wecom_notify" - ], "is_notification": true, "is_mail": true, "is_browser": true, - "format_string": "space_wecom_notify", - "id": "space_wecom_notify", + "billing_notify": "integration_dingtalk", + "format_string": "space_dingtalk_notify", + "id": "space_dingtalk_notify", "url": "/management", "is_component": true }, - "space_yozooffice_notify": { + "space_wecom_notify": { "can_jump": true, - "times": 1, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_yozooffice_notify" - ], "is_notification": true, "is_mail": true, "is_browser": true, - "format_string": "space_yozooffice_notify", - "id": "space_yozooffice_notify", + "billing_notify": "integration_we_com", + "format_string": "space_wecom_notify", + "id": "space_wecom_notify", "url": "/management", "is_component": true }, - "task_reminder": { + "space_trial": { "can_jump": true, - "to_tag": "members", + "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "task_reminder" - ], "is_notification": true, - "is_mobile": true, "is_mail": true, "is_browser": true, - "mail_template_subject": "subject.task.reminder", - "format_string": "task_reminder", - "id": "task_reminder", - "url": "/workbench", + "format_string": "space_trial", + "id": "space_trial", + "url": "/management", "is_component": true }, "activity_integral_income_notify": { "to_tag": "users", "notifications_type": "system", - "formatString": [ - "activity_integral_income_notify" - ], "is_notification": true, "format_string": "activity_integral_income_notify", "id": "activity_integral_income_notify", @@ -942,54 +701,24 @@ "activity_integral_income_toadmin": { "to_tag": "space_main_admin", "notifications_type": "system", - "formatString": [ - "activity_integral_income_toadmin" - ], "is_notification": true, "format_string": "activity_integral_income_toadmin", "id": "activity_integral_income_toadmin", "is_component": true }, - "add_record_out_of_limit": { - "can_jump": true, - "times": 1, - "to_tag": "users", - "notifications_type": "system", - "formatString": [ - "add_record_out_of_limit_by_api_notify" - ], - "is_notification": true, - "is_mail": true, - "mail_template_subject": "subject.add.record.limited", - "format_string": "add_record_out_of_limit_by_api_notify", - "id": "add_record_out_of_limit", - "url": "/workbench", - "frequency": 1, - "is_component": true - }, - "add_record_soon_to_be_limit": { - "can_jump": true, - "times": 1, + "admin_unpublish_space_widget_notify": { "to_tag": "users", "notifications_type": "system", - "formatString": [ - "add_record_soon_to_be_limit_by_api_notify" - ], "is_notification": true, "is_mail": true, - "mail_template_subject": "subject.add.record.soon.limited", - "format_string": "add_record_soon_to_be_limit_by_api_notify", - "id": "add_record_soon_to_be_limit", - "url": "/workbench", - "frequency": 1, + "is_browser": true, + "format_string": "admin_unpublish_space_widget_notify", + "id": "admin_unpublish_space_widget_notify", "is_component": true }, "admin_transfer_space_widget_notify": { "to_tag": "users", "notifications_type": "system", - "formatString": [ - "admin_transfer_space_widget_notify" - ], "is_notification": true, "is_mail": true, "is_browser": true, @@ -997,25 +726,18 @@ "id": "admin_transfer_space_widget_notify", "is_component": true }, - "admin_unpublish_space_widget_notify": { + "new_space_widget_notify": { "to_tag": "users", "notifications_type": "system", - "formatString": [ - "admin_unpublish_space_widget_notify" - ], "is_notification": true, - "is_mail": true, "is_browser": true, - "format_string": "admin_unpublish_space_widget_notify", - "id": "admin_unpublish_space_widget_notify", + "format_string": "new_space_widget_notify", + "id": "new_space_widget_notify", "is_component": true }, "common_system_notify": { "to_tag": "users", "notifications_type": "system", - "formatString": [ - "common_system_notify" - ], "is_notification": true, "is_browser": true, "format_string": "common_system_notify", @@ -1025,71 +747,137 @@ "common_system_notify_web": { "to_tag": "users", "notifications_type": "system", - "formatString": [ - "common_system_notify_web" - ], "format_string": "common_system_notify_web", "id": "common_system_notify_web", "is_component": true }, - "integral_income_notify": { + "add_record_soon_to_be_limit": { + "can_jump": true, "to_tag": "users", "notifications_type": "system", - "formatString": [ - "integral_income_notify" - ], "is_notification": true, - "format_string": "integral_income_notify", - "id": "integral_income_notify", + "is_mail": true, + "mail_template_subject": "subject.add.record.soon.limited", + "format_string": "add_record_soon_to_be_limit_by_api_notify", + "id": "add_record_soon_to_be_limit", + "url": "/workbench", + "frequency": 1, "is_component": true }, - "new_space_widget_notify": { + "add_record_out_of_limit": { + "can_jump": true, "to_tag": "users", "notifications_type": "system", - "formatString": [ - "new_space_widget_notify" - ], "is_notification": true, - "is_browser": true, - "format_string": "new_space_widget_notify", - "id": "new_space_widget_notify", + "is_mail": true, + "mail_template_subject": "subject.add.record.limited", + "format_string": "add_record_out_of_limit_by_api_notify", + "id": "add_record_out_of_limit", + "url": "/workbench", + "frequency": 1, "is_component": true }, "new_user_welcome_notify": { "can_jump": true, "to_tag": "users", "notifications_type": "system", - "formatString": [ - "new_user_welcome_notify" - ], "is_notification": true, - "format_string": "new_user_welcome_notify", + "format_string": "new_user_welcom_notify", "id": "new_user_welcome_notify", "url": "https://u.vika.cn/vhw8y", "is_component": true }, - "server_pre_publish": { - "to_tag": "all_users", - "notifications_type": "system", - "formatString": [ - "server_pre_publish" - ], + "apply_space_beta_feature_success_notify_all": { + "can_jump": true, + "to_tag": "all_members", + "notifications_type": "space", + "is_notification": true, + "format_string": "apply_space_beta_feature_success_notify_all", + "id": "apply_space_beta_feature_success_notify_all" + }, + "apply_space_beta_feature_success_notify_me": { + "can_jump": true, + "to_tag": "myself", + "notifications_type": "space", + "is_notification": true, + "format_string": "apply_space_beta_feature_success_notify_me", + "id": "apply_space_beta_feature_success_notify_me" + }, + "space_paid_notify": { + "to_tag": "users", + "notifications_type": "space", + "is_notification": true, + "format_string": "space_paid_notify", + "id": "space_paid_notify", + "is_component": true + }, + "space_vika_paid_notify": { + "can_jump": true, + "to_tag": "users", + "notifications_type": "space", + "is_notification": true, + "id": "space_vika_paid_notify", + "url": "/management", + "is_component": true + }, + "space_certification_notify": { + "can_jump": true, + "to_tag": "users", + "notifications_type": "space", "is_notification": true, - "is_mobile": true, "is_browser": true, - "format_string": "server_pre_publish", - "id": "server_pre_publish", + "format_string": "space_certification_notify", + "id": "space_certification_notify", + "url": "/management", "is_component": true }, - "web_publish": { - "to_tag": "all_users", - "notifications_type": "system", - "formatString": [ - "web_publish" - ], - "is_mobile": true, - "format_string": "web_publish", - "id": "web_publish", + "space_certification_fail_notify": { + "can_jump": true, + "to_tag": "users", + "notifications_type": "space", + "is_notification": true, + "is_browser": true, + "format_string": "space_certification_fail_notify", + "id": "space_certification_fail_notify", + "url": "/management", + "is_component": true + }, + "space_wecom_api_trial_end": { + "to_tag": "all_members", + "notifications_type": "space", + "is_notification": true, + "is_browser": true, + "format_string": "space_wecom_api_trial_end", + "id": "space_wecom_api_trial_end" + }, + "space_file_permission_limit": { + "can_jump": true, + "to_tag": "space_admins", + "notifications_type": "space", + "is_notification": true, + "is_mail": true, + "is_browser": true, + "mail_template_subject": "subject.subscribed.file.permission.limit", + "billing_notify": "file_permission_nums", + "format_string": "space_file_permission_limit", + "id": "space_file_permission_limit", + "url": "/management", + "frequency": 1, + "is_component": true + }, + "space_mirror_limit": { + "can_jump": true, + "to_tag": "space_admins", + "notifications_type": "space", + "is_notification": true, + "is_mail": true, + "is_browser": true, + "mail_template_subject": "subject.subscribed.mirror.limit", + "billing_notify": "max_mirror_views_in_space", + "format_string": "space_mirror_limit", + "id": "space_mirror_limit", + "url": "/management", + "frequency": 1, "is_component": true } }, @@ -1103,24 +891,6 @@ "id": "wecom_single_record_member_mention", "template_string": "social_wecom_single_record_member_mention" }, - "wecom_subscribed_record_cell_updated": { - "notification_template_id": "subscribed_record_cell_updated", - "message_type": "textcard", - "url": "workbench/{nodeId}/{viewId}/{recordId}", - "platform": "wecom", - "title": "wecom_subscribed_record_cell_updated_title", - "id": "wecom_subscribed_record_cell_updated", - "template_string": "social_wecom_subscribed_record_cell_updated" - }, - "wecom_subscribed_record_commented": { - "notification_template_id": "subscribed_record_commented", - "message_type": "textcard", - "url": "workbench/{nodeId}/{viewId}/{recordId}", - "platform": "wecom", - "title": "wecom_subscribed_record_commented_title", - "id": "wecom_subscribed_record_commented", - "template_string": "social_wecom_subscribed_record_commented" - }, "wecom_task_reminder": { "notification_template_id": "task_reminder", "message_type": "textcard", @@ -1130,45 +900,14 @@ "id": "wecom_task_reminder", "template_string": "social_wecom_task_reminder" }, - "dingtalk_single_record_member_mention": { - "notification_template_id": "single_record_member_mention", - "url_title": "social_notification_url_title", - "message_type": "action_card", - "url": "workbench/{nodeId}/{viewId}/{recordId}", - "platform": "dingtalk", - "title": "dingtalk_single_record_member_mention_title", - "id": "dingtalk_single_record_member_mention", - "template_string": "social_dingtalk_single_record_member_mention" - }, - "dingtalk_subscribed_record_cell_updated": { - "notification_template_id": "subscribed_record_cell_updated", - "url_title": "social_notification_url_title", - "message_type": "action_card", - "url": "workbench/{nodeId}/{viewId}/{recordId}", - "platform": "dingtalk", - "title": "social_dingtalk_subscribed_record_cell_updated_title", - "id": "dingtalk_subscribed_record_cell_updated", - "template_string": "social_dingtalk_subscribed_record_cell_updated" - }, - "dingtalk_subscribed_record_commented": { - "notification_template_id": "subscribed_record_commented", - "url_title": "social_notification_url_title", - "message_type": "action_card", - "url": "workbench/{nodeId}/{viewId}/{recordId}", - "platform": "dingtalk", - "title": "social_dingtalk_subscribed_record_commented_title", - "id": "dingtalk_subscribed_record_commented", - "template_string": "social_dingtalk_subscribed_record_commented" - }, - "dingtalk_task_reminder": { - "notification_template_id": "task_reminder", - "url_title": "social_notification_url_title", - "message_type": "action_card", + "wecom_single_record_comment_mentioned": { + "notification_template_id": "single_record_comment_mentioned", + "message_type": "textcard", "url": "workbench/{nodeId}/{viewId}/{recordId}", - "platform": "dingtalk", - "title": "social_task_reminder_title", - "id": "dingtalk_task_reminder", - "template_string": "social_dingtalk_task_reminder" + "platform": "wecom", + "title": "wecom_single_record_comment_mentioned_title", + "id": "wecom_single_record_comment_mentioned", + "template_string": "wecom_single_record_comment_mentioned" }, "lark_single_record_member_mention": { "notification_template_id": "single_record_member_mention", @@ -1180,41 +919,12 @@ "id": "lark_single_record_member_mention", "template_string": "lark_single_record_member_mention" }, - "lark_subscribed_record_cell_updated": { - "notification_template_id": "subscribed_record_cell_updated", - "url_title": "social_notification_url_title", - "message_type": "textcard", - "url": "workbench/{nodeId}/{viewId}/{recordId}", - "platform": "lark", - "title": "lark_subscribed_record_cell_updated_title", - "id": "lark_subscribed_record_cell_updated", - "template_string": "lark_subscribed_record_cell_updated" - }, - "lark_subscribed_record_commented": { - "notification_template_id": "subscribed_record_commented", - "url_title": "social_notification_url_title", - "message_type": "textcard", - "url": "workbench/{nodeId}/{viewId}/{recordId}", - "platform": "lark", - "title": "lark_subscribed_record_commented_title", - "id": "lark_subscribed_record_commented", - "template_string": "lark_subscribed_record_commented" - }, - "lark_task_reminder": { - "notification_template_id": "task_reminder", - "url_title": "social_notification_url_title", - "message_type": "card", - "url": "workbench/{nodeId}/{viewId}/{recordId}", - "platform": "lark", - "title": "social_lark_task_reminder_title", - "id": "lark_task_reminder", - "template_string": "social_lark_task_reminder" - }, "dingtalk_isv_test_single_record_member_mention": { "notification_template_id": "single_record_member_mention", "message_type": "oa", "url": "workbench/{nodeId}/{viewId}/{recordId}?notifyId={notifyId}", "platform": "dingtalk_isv", + "app_id": "19716004", "id": "dingtalk_isv_test_single_record_member_mention", "template_string": "dingtalk_isv_test_single_record_member_mention" }, @@ -1223,14 +933,60 @@ "message_type": "oa", "url": "workbench/{nodeId}/{viewId}/{recordId}?notifyId={notifyId}", "platform": "dingtalk_isv", + "app_id": "20303001", "id": "dingtalk_isv_integration_single_record_member_mention", "template_string": "dingtalk_isv_integration_single_record_member_mention" }, + "dingtalk_isv_test_task_reminder": { + "notification_template_id": "task_reminder", + "message_type": "oa", + "url": "workbench/{nodeId}/{viewId}/{recordId}", + "platform": "dingtalk_isv", + "app_id": "19716004", + "id": "dingtalk_isv_test_task_reminder", + "template_string": "dingtalk_isv_test_social_task_reminder" + }, + "dingtalk_isv_integration_task_reminder": { + "notification_template_id": "task_reminder", + "message_type": "oa", + "url": "workbench/{nodeId}/{viewId}/{recordId}", + "platform": "dingtalk_isv", + "app_id": "20303001", + "id": "dingtalk_isv_integration_task_reminder", + "template_string": "dingtalk_isv_integration_social_task_reminder" + }, + "dingtalk_isv_staging_task_reminder": { + "notification_template_id": "task_reminder", + "message_type": "oa", + "url": "workbench/{nodeId}/{viewId}/{recordId}", + "platform": "dingtalk_isv", + "app_id": "19758003", + "id": "dingtalk_isv_staging_task_reminder", + "template_string": "dingtalk_isv_staging_task_reminder" + }, + "dingtalk_isv_production_task_reminder": { + "notification_template_id": "task_reminder", + "message_type": "oa", + "url": "workbench/{nodeId}/{viewId}/{recordId}", + "platform": "dingtalk_isv", + "app_id": "19704002", + "id": "dingtalk_isv_production_task_reminder", + "template_string": "dingtalk_isv_production_task_reminder" + }, + "dingtalk_isv_production_single_record_comment_mentioned": { + "notification_template_id": "single_record_comment_mentioned", + "url": "workbench/{nodeId}/{viewId}/{recordId}?notifyId={notifyId}", + "platform": "dingtalk_isv", + "app_id": "19704002", + "id": "dingtalk_isv_production_single_record_comment_mentioned", + "template_string": "dingtalk_isv_production_single_record_comment_mentioned" + }, "dingtalk_isv_staging_single_record_member_mention": { "notification_template_id": "single_record_member_mention", "message_type": "oa", "url": "workbench/{nodeId}/{viewId}/{recordId}", "platform": "dingtalk_isv", + "app_id": "19758003", "id": "dingtalk_isv_staging_single_record_member_mention", "template_string": "dingtalk_isv_staging_single_record_member_mention" }, @@ -1239,14 +995,55 @@ "message_type": "oa", "url": "workbench/{nodeId}/{viewId}/{recordId}", "platform": "dingtalk_isv", + "app_id": "19704002", "id": "dingtalk_isv_production_single_record_member_mention", "template_string": "dingtalk_isv_production_single_record_member_mention" }, + "dingtalk_single_record_member_mention": { + "notification_template_id": "single_record_member_mention", + "url_title": "social_notification_url_title", + "message_type": "action_card", + "url": "workbench/{nodeId}/{viewId}/{recordId}", + "platform": "dingtalk", + "title": "dingtalk_single_record_member_mention_title", + "id": "dingtalk_single_record_member_mention", + "template_string": "social_dingtalk_single_record_member_mention" + }, + "dingtalk_task_reminder": { + "notification_template_id": "task_reminder", + "url_title": "social_notification_url_title", + "message_type": "action_card", + "url": "workbench/{nodeId}/{viewId}/{recordId}", + "platform": "dingtalk", + "title": "social_task_reminder_title", + "id": "dingtalk_task_reminder", + "template_string": "social_dingtalk_task_reminder" + }, + "lark_task_reminder": { + "notification_template_id": "task_reminder", + "url_title": "social_notification_url_title", + "message_type": "card", + "url": "workbench/{nodeId}/{viewId}/{recordId}", + "platform": "lark", + "title": "social_lark_task_reminder_title", + "id": "lark_task_reminder", + "template_string": "social_lark_task_reminder" + }, + "lark_single_record_comment_mentioned": { + "notification_template_id": "single_record_comment_mentioned", + "url_title": "social_notification_url_title", + "url": "workbench/{nodeId}/{viewId}/{recordId}", + "platform": "lark", + "title": "lark_single_record_comment_mentioned_title", + "id": "lark_single_record_comment_mentioned", + "template_string": "lark_single_record_comment_mentioned" + }, "dingtalk_isv_test_subscribed_record_cell_updated": { "notification_template_id": "subscribed_record_cell_updated", "message_type": "oa", "url": "workbench/{nodeId}/{viewId}/{recordId}", "platform": "dingtalk_isv", + "app_id": "19716004", "id": "dingtalk_isv_test_subscribed_record_cell_updated", "template_string": "dingtalk_isv_test_subscribed_record_cell_updated" }, @@ -1255,6 +1052,7 @@ "message_type": "oa", "url": "workbench/{nodeId}/{viewId}/{recordId}", "platform": "dingtalk_isv", + "app_id": "20303001", "id": "dingtalk_isv_integration_subscribed_record_cell_updated", "template_string": "dingtalk_isv_integration_subscribed_record_cell_updated" }, @@ -1263,6 +1061,7 @@ "message_type": "oa", "url": "workbench/{nodeId}/{viewId}/{recordId}", "platform": "dingtalk_isv", + "app_id": "19704002", "id": "dingtalk_isv_production_subscribed_record_cell_updated", "template_string": "dingtalk_isv_production_subscribed_record_cell_updated" }, @@ -1271,6 +1070,7 @@ "message_type": "oa", "url": "workbench/{nodeId}/{viewId}/{recordId}", "platform": "dingtalk_isv", + "app_id": "19758003", "id": "dingtalk_isv_staging_subscribed_record_cell_updated", "template_string": "dingtalk_isv_staging_subscribed_record_cell_updated" }, @@ -1279,6 +1079,7 @@ "message_type": "oa", "url": "workbench/{nodeId}/{viewId}/{recordId}", "platform": "dingtalk_isv", + "app_id": "19758003", "id": "dingtalk_isv_staging_subscribed_record_commented", "template_string": "dingtalk_isv_staging_subscribed_record_commented" }, @@ -1287,6 +1088,7 @@ "message_type": "oa", "url": "workbench/{nodeId}/{viewId}/{recordId}", "platform": "dingtalk_isv", + "app_id": "20303001", "id": "dingtalk_isv_integration_subscribed_record_commented", "template_string": "dingtalk_isv_integration_subscribed_record_commented" }, @@ -1295,6 +1097,7 @@ "message_type": "oa", "url": "workbench/{nodeId}/{viewId}/{recordId}", "platform": "dingtalk_isv", + "app_id": "19716004", "id": "dingtalk_isv_test_subscribed_record_commented", "template_string": "dingtalk_isv_test_subscribed_record_commented" }, @@ -1303,40 +1106,95 @@ "message_type": "oa", "url": "workbench/{nodeId}/{viewId}/{recordId}", "platform": "dingtalk_isv", + "app_id": "19704002", "id": "dingtalk_isv_production_subscribed_record_commented", "template_string": "dingtalk_isv_production_subscribed_record_commented" }, - "dingtalk_isv_test_task_reminder": { - "notification_template_id": "task_reminder", - "message_type": "oa", + "dingtalk_subscribed_record_cell_updated": { + "notification_template_id": "subscribed_record_cell_updated", + "url_title": "social_notification_url_title", + "message_type": "action_card", "url": "workbench/{nodeId}/{viewId}/{recordId}", - "platform": "dingtalk_isv", - "id": "dingtalk_isv_test_task_reminder", - "template_string": "dingtalk_isv_test_social_task_reminder" + "platform": "dingtalk", + "title": "social_dingtalk_subscribed_record_cell_updated_title", + "id": "dingtalk_subscribed_record_cell_updated", + "template_string": "social_dingtalk_subscribed_record_cell_updated" }, - "dingtalk_isv_integration_task_reminder": { - "notification_template_id": "task_reminder", - "message_type": "oa", + "dingtalk_subscribed_record_commented": { + "notification_template_id": "subscribed_record_commented", + "url_title": "social_notification_url_title", + "message_type": "action_card", "url": "workbench/{nodeId}/{viewId}/{recordId}", - "platform": "dingtalk_isv", - "id": "dingtalk_isv_integration_task_reminder", - "template_string": "dingtalk_isv_integration_social_task_reminder" + "platform": "dingtalk", + "title": "social_dingtalk_subscribed_record_commented_title", + "id": "dingtalk_subscribed_record_commented", + "template_string": "social_dingtalk_subscribed_record_commented" }, - "dingtalk_isv_staging_task_reminder": { - "notification_template_id": "task_reminder", + "lark_subscribed_record_cell_updated": { + "notification_template_id": "subscribed_record_cell_updated", + "url_title": "social_notification_url_title", + "message_type": "textcard", + "url": "workbench/{nodeId}/{viewId}/{recordId}", + "platform": "lark", + "title": "lark_subscribed_record_cell_updated_title", + "id": "lark_subscribed_record_cell_updated", + "template_string": "lark_subscribed_record_cell_updated" + }, + "lark_subscribed_record_commented": { + "notification_template_id": "subscribed_record_commented", + "url_title": "social_notification_url_title", + "message_type": "textcard", + "url": "workbench/{nodeId}/{viewId}/{recordId}", + "platform": "lark", + "title": "lark_subscribed_record_commented_title", + "id": "lark_subscribed_record_commented", + "template_string": "lark_subscribed_record_commented" + }, + "wecom_subscribed_record_cell_updated": { + "notification_template_id": "subscribed_record_cell_updated", + "message_type": "textcard", + "url": "workbench/{nodeId}/{viewId}/{recordId}", + "platform": "wecom", + "title": "wecom_subscribed_record_cell_updated_title", + "id": "wecom_subscribed_record_cell_updated", + "template_string": "social_wecom_subscribed_record_cell_updated" + }, + "wecom_subscribed_record_commented": { + "notification_template_id": "subscribed_record_commented", + "message_type": "textcard", + "url": "workbench/{nodeId}/{viewId}/{recordId}", + "platform": "wecom", + "title": "wecom_subscribed_record_commented_title", + "id": "wecom_subscribed_record_commented", + "template_string": "social_wecom_subscribed_record_commented" + }, + "dingtalk_isv_integration_single_record_comment_mentioned": { + "notification_template_id": "single_record_comment_mentioned", "message_type": "oa", "url": "workbench/{nodeId}/{viewId}/{recordId}", "platform": "dingtalk_isv", - "id": "dingtalk_isv_staging_task_reminder", - "template_string": "dingtalk_isv_staging_task_reminder" + "app_id": "20303001", + "id": "dingtalk_isv_integration_single_record_comment_mentioned", + "template_string": "dingtalk_isv_integration_single_record_comment_mentioned" }, - "dingtalk_isv_production_task_reminder": { - "notification_template_id": "task_reminder", + "dingtalk_isv_staging_single_record_comment_mentioned": { + "notification_template_id": "single_record_comment_mentioned", "message_type": "oa", "url": "workbench/{nodeId}/{viewId}/{recordId}", "platform": "dingtalk_isv", - "id": "dingtalk_isv_production_task_reminder", - "template_string": "dingtalk_isv_production_task_reminder" + "app_id": "19758003", + "id": "dingtalk_isv_staging_single_record_comment_mentioned", + "template_string": "dingtalk_isv_staging_single_record_comment_mentioned" + }, + "dingtalk_single_record_comment_mentioned": { + "notification_template_id": "single_record_comment_mentioned", + "url_title": "social_notification_url_title", + "message_type": "action_card", + "url": "workbench/{nodeId}/{viewId}/{recordId}", + "platform": "dingtalk", + "title": "dingtalk_single_record_member_comment_title", + "id": "dingtalk_single_record_comment_mentioned", + "template_string": "social_dingtalk_single_record_comment_mention" } } -} \ No newline at end of file +} diff --git a/backend-server/application/src/main/resources/sysconfig/strings.auto.json b/backend-server/application/src/main/resources/sysconfig/strings.auto.json index 068c9668cb2e7cb5a1badebc742636e5203e7622..d753a5ffbdcdc9bdf9ee7d28fac9b5a23c9acafa 100644 --- a/backend-server/application/src/main/resources/sysconfig/strings.auto.json +++ b/backend-server/application/src/main/resources/sysconfig/strings.auto.json @@ -173,9 +173,9 @@ "zh_HK": "新建儀錶盤" }, "add_datasheet_editor": { - "zh_CN": "在「只可更新」基础上,还可以增删视图和记录", - "en_US": "In addition to \"Update-only\", can also add or delete views and records", - "zh_HK": "在「只可更新」基礎上,還可以增刪視圖和記錄" + "zh_CN": "在「只可更新」基础上,还可以删除记录和增删视图", + "en_US": "In addition to \"Update-only\", can also add or delete views and delete records", + "zh_HK": "在「只可更新」基礎上,還可以刪除記錄和增刪視圖" }, "add_datasheet_manager": { "zh_CN": "拥有该表格的所有操作权限", @@ -188,9 +188,9 @@ "zh_HK": "可查看該表格內容或進行評論" }, "add_datasheet_updater": { - "zh_CN": "在「只可阅读」基础上,还可以更新单元格内容", - "en_US": "Can read, can also update the cell content", - "zh_HK": "在「只可閱讀」基礎上,還可以更新單元格內容" + "zh_CN": "在「只可阅读」基础上,还可以新增和编辑记录", + "en_US": "Can read, add and edit records except deleting records", + "zh_HK": "在「只可閱讀」基礎上,還可以新增和編輯記錄" }, "add_editor": { "zh_CN": "可以编辑", @@ -233,9 +233,9 @@ "zh_HK": "只可查看該文件夾下的內容" }, "add_folder_updater": { - "zh_CN": "在「只可阅读」基础上,还可以更新表格的单元格", - "en_US": "Can read, can also update the cell content", - "zh_HK": "在「只可閱讀」基礎上,還可以更新表格的單元格" + "zh_CN": "在「只可阅读」基础上,还可以新增和编辑记录", + "en_US": "Can read, add and edit records except deleting records", + "zh_HK": "在「只可閱讀」基礎上,還可以新增和編輯記錄" }, "add_form": { "zh_CN": "新建神奇表单", @@ -309,7 +309,7 @@ }, "add_member_or_group": { "zh_CN": "添加成员/小组", - "en_US": "Add member/team", + "en_US": "Add member / team", "zh_HK": "添加成員/小組" }, "add_member_or_unit": { @@ -427,9 +427,9 @@ "zh_HK": "添加子管理員" }, "add_sub_admin_contacts_configuration": { - "zh_CN": "「小组管理」可以增删改小组;「成员管理」可以增删改成员", - "en_US": "Managing teams means that the sub-admins can add, delete, or modify teams; managing members means that the sub-admins can add, delete, or modify members.", - "zh_HK": "「小組管理」可以增刪改小組;「成員管理」可以增刪改成員" + "zh_CN": "「小组管理」可以增删改小组;「成员管理」可以增删改成员;「角色管理」可以增删改角色", + "en_US": "Managing teams means that the sub-admins can add, delete, or modify teams; Managing members means that the sub-admins can add, delete, or modify members;Managing roles means that the sub-admins can add, delete, or modify roles.", + "zh_HK": "「小組管理」可以增刪改小組;「成員管理」可以增刪改成員;「角色管理」可以增刪改角色" }, "add_sub_admin_fail": { "zh_CN": "添加子管理员失败", @@ -438,7 +438,7 @@ }, "add_sub_admin_permissions_configuration": { "zh_CN": "子管理员可以在「安全设置」页面进行配置", - "en_US": "You can edit members' privileges on the \"Permission Management\" page. For example, you can allow members to invite friends.", + "en_US": "Sub-admin can edit members' permissions on the \"Security page\". For example, you can disable creating public link", "zh_HK": "子管理員可以在「安全設置」頁面進行配置" }, "add_sub_admin_success": { @@ -463,7 +463,7 @@ }, "add_sub_admin_workbench_configuration": { "zh_CN": "子管理员拥有「工作台」所有文件的最高权限,可以对文件进行增、删、改和设置权限的操作", - "en_US": "The sub-admin can set permissions for folder(s) on the workbench, and also edit the workbench from the Workbench Settings page.", + "en_US": "The sub-admins have the highest authority of all files in \"Workbench\", and can add, delete, modify and set permissions on files", "zh_HK": "子管理員擁有「工作台」所有文件的最高權限,可以對文件進行增、刪、改和設置權限的操作" }, "add_summry_describle": { @@ -543,7 +543,7 @@ }, "admins_per_space": { "zh_CN": "管理员数量", - "en_US": "Admins per Space", + "en_US": "Admins per space", "zh_HK": "管理員數量" }, "advanced": { @@ -626,15 +626,23 @@ "en_US": "Albania", "zh_HK": "阿爾巴尼亞" }, + "album": { + "zh_CN": "专题", + "en_US": "Album" + }, + "album_publisher": { + "zh_CN": "发布者", + "en_US": "album publisher" + }, "algeria": { "zh_CN": "阿尔及利亚", "en_US": "Algeria", "zh_HK": "阿爾及利亞" }, "alien": { - "zh_CN": "外星人", - "en_US": "Alien", - "zh_HK": "外星人" + "zh_CN": "匿名用户", + "en_US": "Anonymous", + "zh_HK": "匿名用戶" }, "alien_tip_in_user_card": { "zh_CN": "该用户不属于当前空间站", @@ -699,7 +707,6 @@ "and": { "zh_CN": "和", "en_US": "and", - "终端": "APP", "zh_HK": "和" }, "andorra": { @@ -749,7 +756,7 @@ }, "api_datasheet_not_exist": { "zh_CN": "找不到指定的维格表", - "en_US": "Can't find the specified datasheet", + "en_US": "the specified datasheet does not exist", "zh_HK": "找不到指定的維格表" }, "api_datasheet_not_visible": { @@ -772,6 +779,22 @@ "en_US": "Failed to delete record because the linked datasheet was deleted", "zh_HK": "刪除記錄失敗,因為關聯表已被刪除" }, + "api_embed_link_id_not_exist": { + "zh_CN": "指定的嵌入链接不存在,请调整 linkId 后再试", + "en_US": "the specified embed link does not exist, please adjust the linkId and try again" + }, + "api_embed_link_instance_limit": { + "zh_CN": "抱歉, 暂不支持为表格类以外的其他节点创建嵌入链接", + "en_US": "embedded links are not supported for nodes other than datasheet type" + }, + "api_embed_link_limit": { + "zh_CN": "创建的嵌入链接数量已达上限,单个维格表的嵌入链接数量不能超过{value}个", + "en_US": "the maximum number of embedded links has been reached, only {value} links can be created" + }, + "api_enterprise_limit": { + "zh_CN": "抱歉,此接口仅可在黄金级以上的空间站中调用", + "en_US": "sorry, it can only be invoked in golden-level space" + }, "api_example_request": { "zh_CN": "示例请求", "en_US": "Sample request", @@ -787,6 +810,10 @@ "en_US": "API overused", "zh_HK": "API 用量超量使用" }, + "api_fieldid_empty_error": { + "zh_CN": "fieldId 的值不能为空", + "en_US": "fieldId should not be empty" + }, "api_fields": { "zh_CN": "Fields 字段", "en_US": "Fields", @@ -858,14 +885,14 @@ "zh_HK": "初始化 SDK" }, "api_panel_md_curl_example": { - "zh_CN": "\n\n\n## 获取记录\n\n```shell\ncurl \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/records?viewId={{ viewId }}&fieldKey={{ fieldKey }}\" \\\n -H \"Authorization: Bearer {{ apiToken }}\"\n\n```\n\n### 示例返回值\n\n```json\n\n{{ response }}\n\n```\n\n### 其他参数或提示\n```js\n/**\n * 注意:每张维格表获取数据最大并发量限制为 1 秒钟 5 次\n * 全部可配置的参数,可通过 URL Query Params 发送,\n */\n{\n /**\n * (选填)视图ID。默认为维格表中第一个视图。请求会返回视图中经过视图中筛选/排序后的结果,可以搭配使用fields参数过滤不需要的字段数据\n */\n viewId: 'viewId1',\n /**\n * (选填)指定分页的页码,默认为 1,与参数pageSize配合使用。\n */\n pageNum: 1,\n /**\n * (选填)指定每页返回的记录总数,默认为100。此参数只接受1-1000的整数。\n */\n pageSize: 100,\n /**\n * (选填)对返回的记录进行排序。sort 是由多个排序对象 (sort object) 组成的数组。单个排序对象的结构为 {\"order\":\"asc 或 desc\", \"field\":\"字段名称或字段 ID\"}。查询示例:sort[][field]=客户名称&sort[][order]=asc,即按照「客户名称」列的字母升序来排列返回的记录。如果 sort 与 viewId 同时使用,则 sort 指定的排序条件将会覆盖视图里的排序条件。\n */\n sort: [{ field: 'field1', order: 'desc' }],\n /**\n * (选填)recordIds 数组。如果附带此参数,则返回参数中指定的records数组。 返回值按照传入数组的顺序排序。此时无视筛选、排序。无分页,每次最多查询 1000 条\n */\n recordIds: ['recordId1', 'recordId2'],\n /**\n * (选填)指定要返回的字段(默认为字段名, 也可以通过 fieldKey 指定为字段 Id)。如果附带此参数,则返回的记录合集将会被过滤,只有指定的字段会返回。\n */\n fields: ['标题', '详情', '引用次数'],\n /**\n * (选填)使用公式作为筛选条件,返回匹配的记录,访问 https://vika.cn/help/tutorial-getting-started-with-formulas/ 了解公式使用方式\n */\n filterByFormula: '{引用次数} > 0',\n /**\n * (选填)限制返回记录的总数量。如果该值小于表中实际的记录总数,则返回的记录总数会被限制为该值。\n */\n maxRecords: 5000,\n /**\n * (选填)单元格值类型,默认为 'json',指定为 'string' 时所有值都将被自动转换为 string 格式。\n */\n cellFormat: 'json',\n /**\n * (选填)指定 field 的查询和返回的 key。默认使用列名 'name' 。指定为 'id' 时将以 fieldId 作为查询和返回方式(使用 id 可以避免列名的修改导致代码失效问题)\n */\n fieldKey: 'name',\n}\n```\n\n\n\n## 新增记录\n```shell\ncurl -X POST \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/records?viewId={{ viewId }}&fieldKey={{ fieldKey }}\" \\\n -H \"Authorization: Bearer {{ apiToken }}\" \\\n -H \"Content-Type: application/json\" \\\n --data '{\n \"records\": {{ records }},\n \"fieldKey\": \"{{ fieldKey }}\"\n}'\n\n```\n\n### 示例返回值\n\n```json\n{{ response }}\n```\n\n### 其他参数或提示\n\nadd 接口接收一个数组值,可以同时创建多条 record,单次请求可最多创建10条 record\n\n\n\n## 更新记录\n```shell\ncurl -X PATCH \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/records?viewId={{ viewId }}&fieldKey={{ fieldKey }}\" \\\n -H \"Authorization: Bearer {{ apiToken }}\" \\\n -H \"Content-Type: application/json\" \\\n --data '{\n \"records\": {{ records }},\n \"fieldKey\": \"{{ fieldKey }}\"\n}'\n```\n### 示例返回值\n\n```json\n{{ response }}\n```\n\n\n### 其他参数或提示\n\nupdate 接口接收一个数组值,可以同时更新多条 record,单次请求可最多更新10条 record\n特别注意: update 只会更新你传入的 fields 下的数据,未传入的不会影响,如果需要清空值,请显式传入 null\n\n\n\n\n## 删除记录\n```shell\ncurl -X DELETE \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/records?{{ deleteParams }}\" \\\n -H \"Authorization: Bearer {{ apiToken }}\"\n\n```\n\n### 示例返回值\n```json\n{\n \"code\": 200,\n \"success\": true,\n \"message\": \"请求成功\"\n}\n```\n\n### 其他参数或提示\n\ndelete 接口接收一个数组值,可以同时删除多条 record,单次请求可最多删除10条 record\n\n\n## 上传文件\n\n```shell\ncurl -X POST \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/attachments\" \\\n -H \"Authorization: Bearer {{ apiToken }}\" \\\n -H \"Content-Type: multipart/form-data\" \\\n -F \"file=@{你的 文件路径};\"\n```\n### 示例返回值\n\n*Tips:上传完毕后,请尽快写入data 中的数据到附件单元格里,否则附件链接可能失效*\n\n```json\n{\n \"code\": 200,\n \"success\": true,\n \"message\": \"请求成功\",\n \"data\": {\n \"id\": \"atcPtxnvqti5M\",\n \"name\": \"6.gif\",\n \"size\": 33914,\n \"mimeType\": \"image/gif\",\n \"token\": \"space/2020/09/22/01ee7202922d48688f61e34f12da5abc\",\n \"width\": 240,\n \"height\": 240,\n \"url\": \"https://s1.vika.cn/space/2020/09/22/01ee7202922d48688f61e34f12da5abc\"\n }\n}\n```", - "en_US": "\n\n\n## Get records\n\n```shell\ncurl \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/records?viewId={{ viewId }}&fieldKey={{ fieldKey }}\" \\\n -H \"Authorization: Bearer {{ apiToken }}\"\n\n```\n\n### Returned data example\n\n```json\n\n{{ response }}\n\n```\n\n### Other parameters and tips\n```js\n/**\n * 注意:每张维格表获取数据最大并发量限制为 1 秒钟 5 次\n * 全部可配置的参数,可通过 URL Query Params 发送,\n */\n{\n /**\n * (选填)视图ID。默认为维格表中第一个视图。请求会返回视图中经过视图中筛选/排序后的结果,可以搭配使用fields参数过滤不需要的字段数据\n */\n viewId: 'viewId1',\n /**\n * (选填)指定分页的页码,默认为 1,与参数pageSize配合使用。\n */\n pageNum: 1,\n /**\n * (选填)指定每页返回的记录总数,默认为100。此参数只接受1-1000的整数。\n */\n pageSize: 100,\n /**\n * (选填)对指定维格表的记录进行排序。由多个“排序对象”组成的数组。支持顺序:'asc' 和 逆序:'desc'。注:此参数指定的排序条件将会覆盖视图里的排序条件。\n */\n sort: [{ field: 'field1', order: 'asc' }],\n /**\n * (选填)recordIds 数组。如果附带此参数,则返回参数中指定的records数组。 返回值按照传入数组的顺序排序。此时无视筛选、排序。无分页,每次最多查询 1000 条\n */\n recordIds: ['recordId1', 'recordId2'],\n /**\n * (选填)指定要返回的字段(默认为字段名, 也可以通过 fieldKey 指定为字段 Id)。如果附带此参数,则返回的记录合集将会被过滤,只有指定的字段会返回。\n */\n fields: ['标题', '详情', '引用次数'],\n /**\n * (选填)使用公式作为筛选条件,返回匹配的记录,访问 https://vika.cn/help/tutorial-getting-started-with-formulas/ 了解公式使用方式\n */\n filterByFormula: '{引用次数} > 0',\n /**\n * (选填)限制返回记录的总数量。如果该值小于表中实际的记录总数,则返回的记录总数会被限制为该值。\n */\n maxRecords: 5000,\n /**\n * (选填)单元格值类型,默认为 'json',指定为 'string' 时所有值都将被自动转换为 string 格式。\n */\n cellFormat: 'json',\n /**\n * (选填)指定 field 的查询和返回的 key。默认使用列名 'name' 。指定为 'id' 时将以 fieldId 作为查询和返回方式(使用 id 可以避免列名的修改导致代码失效问题)\n */\n fieldKey: 'name',\n}\n```\n\n\n\n## Add records\n```shell\ncurl -X POST \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/records?viewId={{ viewId }}&fieldKey={{ fieldKey }}\" \\\n -H \"Authorization: Bearer {{ apiToken }}\" \\\n -H \"Content-Type: application/json\" \\\n --data '{\n \"records\": {{ records }},\n \"fieldKey\": \"{{ fieldKey }}\"\n}'\n\n```\n\n### Returned data example\n\n```json\n{{ response }}\n```\n\n### Other parameters and tips\n\nadd 接口接收一个数组值,可以同时创建多条 record,单次请求可最多创建10条 record\n\n\n\n## Update records\n```shell\ncurl -X PATCH \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/records?viewId={{ viewId }}&fieldKey={{ fieldKey }}\" \\\n -H \"Authorization: Bearer {{ apiToken }}\" \\\n -H \"Content-Type: application/json\" \\\n --data '{\n \"records\": {{ records }},\n \"fieldKey\": \"{{ fieldKey }}\"\n}'\n```\n### Returned data example\n\n```json\n{{ response }}\n```\n\n\n### Other parameters and tips\n\nupdate 接口接收一个数组值,可以同时更新多条 record,单次请求可最多更新10条 record\n特别注意: update 只会更新你传入的 fields 下的数据,未传入的不会影响,如果需要清空值,请显式传入 null\n\n\n\n\n## Delete records\n```shell\ncurl -X DELETE \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/records?{{ deleteParams }}\" \\\n -H \"Authorization: Bearer {{ apiToken }}\"\n\n```\n\n### Returned data example\n```json\n{\n \"code\": 200,\n \"success\": true,\n \"message\": \"请求成功\"\n}\n```\n\n### Other parameters and tips\n\ndelete 接口接收一个数组值,可以同时删除多条 record,单次请求可最多删除10条 record\n\n\n## Upload attachments\n\n```shell\ncurl -X POST \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/attachments\" \\\n -H \"Authorization: Bearer {{ apiToken }}\" \\\n -H \"Content-Type: multipart/form-data\" \\\n -F \"file=@{你的 文件路径};\"\n```\n### Returned data example\n\n*Tips:上传完毕后,请尽快写入data 中的数据到附件单元格里,否则附件链接可能失效*\n\n```json\n{\n \"code\": 200,\n \"success\": true,\n \"message\": \"请求成功\",\n \"data\": {\n \"id\": \"atcPtxnvqti5M\",\n \"name\": \"6.gif\",\n \"size\": 33914,\n \"mimeType\": \"image/gif\",\n \"token\": \"space/2020/09/22/01ee7202922d48688f61e34f12da5abc\",\n \"width\": 240,\n \"height\": 240,\n \"url\": \"https://s1.vika.cn/space/2020/09/22/01ee7202922d48688f61e34f12da5abc\"\n }\n}\n```", - "zh_HK": "\n\n\n## 獲取記錄\n\n```shell\ncurl \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/records?viewId={{ viewId }}&fieldKey={{ fieldKey }}\" \\\n -H \"Authorization: Bearer {{ apiToken }}\"\n\n```\n\n### 示例返回值\n\n```json\n\n{{ response }}\n\n```\n\n### 其他參數或提示\n```js\n/**\n * 注意:每張維格表獲取數據最大並發量限制為 1 秒鐘 5 次\n * 全部可配置的參數,可通過 URL Query Params 發送,\n */\n{\n /**\n * (選填)視圖ID。默認為維格表中第一個視圖。請求會返回視圖中經過視圖中篩選/排序後的結果,可以搭配使用fields參數過濾不需要的字段數據\n */\n viewId: 'viewId1',\n /**\n * (選填)指定分頁的頁碼,默認為 1,與參數pageSize配合使用。\n */\n pageNum: 1,\n /**\n * (選填)指定每頁返回的記錄總數,默認為100。此參數只接受1-1000的整數。\n */\n pageSize: 100,\n /**\n * (選填)對返回的記錄進行排序。 sort 是由多個排序對象 (sort object) 組成的數組。單個排序對象的結構為 {\"order\":\"asc 或 desc\", \"field\":\"字段名稱或字段 ID\"}。查詢示例:sort[][field]=客戶名稱&sort[][order]=asc,即按照「客戶名稱」列的字母升序來排列返回的記錄。如果 sort 與 viewId 同時使用,則 sort 指定的排序條件將會覆蓋視圖裡的排序條件。\n */\n sort: [{ field: 'field1', order: 'desc' }],\n /**\n * (選填)recordIds 數組。如果附帶此參數,則返回參數中指定的records數組。返回值按照傳入數組的順序排序。此時無視篩選、排序。無分頁,每次最多查詢 1000 條\n */\n recordIds: ['recordId1', 'recordId2'],\n /**\n * (選填)指定要返回的字段(默認為字段名, 也可以通過 fieldKey 指定為字段 Id)。如果附帶此參數,則返回的記錄合集將會被過濾,只有指定的字段會返回。\n */\n fields: ['標題', '詳情', '引用次數'],\n /**\n * (選填)使用公式作為篩選條件,返回匹配的記錄,訪問 https://vika.cn/help/tutorial-getting-started-with-formulas/ 了解公式使用方式\n */\n filterByFormula: '{引用次數} > 0',\n /**\n * (選填)限制返回記錄的總數量。如果該值小於表中實際的記錄總數,則返回的記錄總數會被限制為該值。\n */\n maxRecords: 5000,\n /**\n * (選填)單元格值類型,默認為 'json',指定為 'string' 時所有值都將被自動轉換為 string 格式。\n */\n cellFormat: 'json',\n /**\n * (選填)指定 field 的查詢和返回的 key。默認使用列名 'name' 。指定為 'id' 時將以 fieldId 作為查詢和返回方式(使用 id 可以避免列名的修改導致代碼失效問題)\n */\n fieldKey: 'name',\n}\n```\n\n\n\n## 新增記錄\n```shell\ncurl -X POST \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/records?viewId={{ viewId }}&fieldKey={{ fieldKey }}\" \\\n -H \"Authorization: Bearer {{ apiToken }}\" \\\n -H \"Content-Type: application/json\" \\\n --data '{\n \"records\": {{ records }},\n \"fieldKey\": \"{{ fieldKey }}\"\n}'\n\n```\n\n### 示例返回值\n\n```json\n{{ response }}\n```\n\n### 其他參數或提示\n\nadd 接口接收一個數組值,可以同時創建多條 record,單次請求可最多創建10條 record\n\n\n\n## 更新記錄\n```shell\ncurl -X PATCH \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/records?viewId={{ viewId }}&fieldKey={{ fieldKey }}\" \\\n -H \"Authorization: Bearer {{ apiToken }}\" \\\n -H \"Content-Type: application/json\" \\\n --data '{\n \"records\": {{ records }},\n \"fieldKey\": \"{{ fieldKey }}\"\n}'\n```\n### 示例返回值\n\n```json\n{{ response }}\n```\n\n\n### 其他參數或提示\n\nupdate 接口接收一個數組值,可以同時更新多條 record,單次請求可最多更新10條 record\n特別注意: update 只會更新你傳入的 fields 下的數據,未傳入的不會影響,如果需要清空值,請顯式傳入 null\n\n\n\n\n## 刪除記錄\n```shell\ncurl -X DELETE \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/records?{{ deleteParams }}\" \\\n -H \"Authorization: Bearer {{ apiToken }}\"\n\n```\n\n### 示例返回值\n```json\n{\n \"code\": 200,\n \"success\": true,\n \"message\": \"請求成功\"\n}\n```\n\n### 其他參數或提示\n\ndelete 接口接收一個數組值,可以同時刪除多條 record,單次請求可最多刪除10條 record\n\n\n## 上傳文件\n\n```shell\ncurl -X POST \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/attachments\" \\\n -H \"Authorization: Bearer {{ apiToken }}\" \\\n -H \"Content-Type: multipart/form-data\" \\\n -F \"file=@{你的 文件路徑};\"\n```\n### 示例返回值\n\n*Tips:上傳完畢後,請盡快寫入data 中的數據到附件單元格里,否則附件鏈接可能失效*\n\n```json\n{\n \"code\": 200,\n \"success\": true,\n \"message\": \"請求成功\",\n \"data\": {\n \"id\": \"atcPtxnvqti5M\",\n \"name\": \"6.gif\",\n \"size\": 33914,\n \"mimeType\": \"image/gif\",\n \"token\": \"space/2020/09/22/01ee7202922d48688f61e34f12da5abc\",\n \"width\": 240,\n \"height\": 240,\n \"url\": \"https://s1.vika.cn/space/2020/09/22/01ee7202922d48688f61e34f12da5abc\"\n }\n}\n```" + "zh_CN": "\n\n\n## 获取记录\n\n```shell\ncurl \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/records?viewId={{ viewId }}&fieldKey={{ fieldKey }}\" \\\n -H \"Authorization: Bearer {{ apiToken }}\"\n\n```\n\n### 示例返回值\n\n```json\n\n{{ response }}\n\n```\n\n### 其他参数或提示\n```js\n/**\n * 注意:每张维格表获取数据最大并发量限制为 1 秒钟 5 次\n * 全部可配置的参数,可通过 URL Query Params 发送,\n */\n{\n /**\n * (选填)视图ID。默认为维格表中第一个视图。请求会返回视图中经过视图中筛选/排序后的结果,可以搭配使用fields参数过滤不需要的字段数据\n */\n viewId: 'viewId1',\n /**\n * (选填)指定分页的页码,默认为 1,与参数pageSize配合使用。\n */\n pageNum: 1,\n /**\n * (选填)指定每页返回的记录总数,默认为100。此参数只接受1-1000的整数。\n */\n pageSize: 100,\n /**\n * (选填)对返回的记录进行排序。sort 是由多个排序对象 (sort object) 组成的数组。单个排序对象的结构为 {\"order\":\"asc 或 desc\", \"field\":\"字段名称或字段 ID\"}。查询示例:sort[][field]=客户名称&sort[][order]=asc,即按照「客户名称」列的字母升序来排列返回的记录。如果 sort 与 viewId 同时使用,则 sort 指定的排序条件将会覆盖视图里的排序条件。\n */\n sort: [{ field: 'field1', order: 'desc' }],\n /**\n * (选填)recordIds 数组。如果附带此参数,则返回参数中指定的records数组。 返回值按照传入数组的顺序排序。此时无视筛选、排序。无分页,每次最多查询 1000 条\n */\n recordIds: ['recordId1', 'recordId2'],\n /**\n * (选填)指定要返回的字段(默认为字段名, 也可以通过 fieldKey 指定为字段 Id)。如果附带此参数,则返回的记录合集将会被过滤,只有指定的字段会返回。\n */\n fields: ['标题', '详情', '引用次数'],\n /**\n * (选填)使用公式作为筛选条件,返回匹配的记录,访问 https://help.vika.cn/docs/guide/tutorial-getting-started-with-formulas 了解公式使用方式\n */\n filterByFormula: '{引用次数} > 0',\n /**\n * (选填)限制返回记录的总数量。如果该值小于表中实际的记录总数,则返回的记录总数会被限制为该值。\n */\n maxRecords: 5000,\n /**\n * (选填)单元格值类型,默认为 'json',指定为 'string' 时所有值都将被自动转换为 string 格式。\n */\n cellFormat: 'json',\n /**\n * (选填)指定 field 的查询和返回的 key。默认使用列名 'name' 。指定为 'id' 时将以 fieldId 作为查询和返回方式(使用 id 可以避免列名的修改导致代码失效问题)\n */\n fieldKey: 'name',\n}\n```\n\n\n\n## 新增记录\n```shell\ncurl -X POST \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/records?viewId={{ viewId }}&fieldKey={{ fieldKey }}\" \\\n -H \"Authorization: Bearer {{ apiToken }}\" \\\n -H \"Content-Type: application/json\" \\\n --data '{\n \"records\": {{ records }},\n \"fieldKey\": \"{{ fieldKey }}\"\n}'\n\n```\n\n### 示例返回值\n\n```json\n{{ response }}\n```\n\n### 其他参数或提示\n\nadd 接口接收一个数组值,可以同时创建多条 record,单次请求可最多创建10条 record\n\n\n\n## 更新记录\n```shell\ncurl -X PATCH \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/records?viewId={{ viewId }}&fieldKey={{ fieldKey }}\" \\\n -H \"Authorization: Bearer {{ apiToken }}\" \\\n -H \"Content-Type: application/json\" \\\n --data '{\n \"records\": {{ records }},\n \"fieldKey\": \"{{ fieldKey }}\"\n}'\n```\n### 示例返回值\n\n```json\n{{ response }}\n```\n\n\n### 其他参数或提示\n\nupdate 接口接收一个数组值,可以同时更新多条 record,单次请求可最多更新10条 record\n特别注意: update 只会更新你传入的 fields 下的数据,未传入的不会影响,如果需要清空值,请显式传入 null\n\n\n\n\n## 删除记录\n```shell\ncurl -X DELETE \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/records?{{ deleteParams }}\" \\\n -H \"Authorization: Bearer {{ apiToken }}\"\n\n```\n\n### 示例返回值\n```json\n{\n \"code\": 200,\n \"success\": true,\n \"message\": \"请求成功\"\n}\n```\n\n### 其他参数或提示\n\ndelete 接口接收一个数组值,可以同时删除多条 record,单次请求可最多删除10条 record\n\n\n## 上传文件\n\n```shell\ncurl -X POST \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/attachments\" \\\n -H \"Authorization: Bearer {{ apiToken }}\" \\\n -H \"Content-Type: multipart/form-data\" \\\n -F \"file=@{你的 文件路径};\"\n```\n### 示例返回值\n\n*Tips:上传完毕后,请尽快写入data 中的数据到附件单元格里,否则附件链接可能失效*\n\n```json\n{\n \"code\": 200,\n \"success\": true,\n \"message\": \"请求成功\",\n \"data\": {\n \"id\": \"atcPtxnvqti5M\",\n \"name\": \"6.gif\",\n \"size\": 33914,\n \"mimeType\": \"image/gif\",\n \"token\": \"space/2020/09/22/01ee7202922d48688f61e34f12da5abc\",\n \"width\": 240,\n \"height\": 240,\n \"url\": \"https://s1.vika.cn/space/2020/09/22/01ee7202922d48688f61e34f12da5abc\"\n }\n}\n```", + "en_US": "\n\n\n## Get records\n\n```shell\ncurl \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/records?viewId={{ viewId }}&fieldKey={{ fieldKey }}\" \\\n -H \"Authorization: Bearer {{ apiToken }}\"\n\n```\n\n### Returned data example\n\n```json\n\n{{ response }}\n\n```\n\n### Other parameters and tips\n```js\n/**\n * 注意:每张维格表获取数据最大并发量限制为 1 秒钟 5 次\n * 全部可配置的参数,可通过 URL Query Params 发送,\n */\n{\n /**\n * (选填)视图ID。默认为维格表中第一个视图。请求会返回视图中经过视图中筛选/排序后的结果,可以搭配使用fields参数过滤不需要的字段数据\n */\n viewId: 'viewId1',\n /**\n * (选填)指定分页的页码,默认为 1,与参数pageSize配合使用。\n */\n pageNum: 1,\n /**\n * (选填)指定每页返回的记录总数,默认为100。此参数只接受1-1000的整数。\n */\n pageSize: 100,\n /**\n * (选填)对指定维格表的记录进行排序。由多个“排序对象”组成的数组。支持顺序:'asc' 和 逆序:'desc'。注:此参数指定的排序条件将会覆盖视图里的排序条件。\n */\n sort: [{ field: 'field1', order: 'asc' }],\n /**\n * (选填)recordIds 数组。如果附带此参数,则返回参数中指定的records数组。 返回值按照传入数组的顺序排序。此时无视筛选、排序。无分页,每次最多查询 1000 条\n */\n recordIds: ['recordId1', 'recordId2'],\n /**\n * (选填)指定要返回的字段(默认为字段名, 也可以通过 fieldKey 指定为字段 Id)。如果附带此参数,则返回的记录合集将会被过滤,只有指定的字段会返回。\n */\n fields: ['标题', '详情', '引用次数'],\n /**\n * (选填)使用公式作为筛选条件,返回匹配的记录,访问 https://help.vika.cn/docs/guide/tutorial-getting-started-with-formulas 了解公式使用方式\n */\n filterByFormula: '{引用次数} > 0',\n /**\n * (选填)限制返回记录的总数量。如果该值小于表中实际的记录总数,则返回的记录总数会被限制为该值。\n */\n maxRecords: 5000,\n /**\n * (选填)单元格值类型,默认为 'json',指定为 'string' 时所有值都将被自动转换为 string 格式。\n */\n cellFormat: 'json',\n /**\n * (选填)指定 field 的查询和返回的 key。默认使用列名 'name' 。指定为 'id' 时将以 fieldId 作为查询和返回方式(使用 id 可以避免列名的修改导致代码失效问题)\n */\n fieldKey: 'name',\n}\n```\n\n\n\n## Add records\n```shell\ncurl -X POST \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/records?viewId={{ viewId }}&fieldKey={{ fieldKey }}\" \\\n -H \"Authorization: Bearer {{ apiToken }}\" \\\n -H \"Content-Type: application/json\" \\\n --data '{\n \"records\": {{ records }},\n \"fieldKey\": \"{{ fieldKey }}\"\n}'\n\n```\n\n### Returned data example\n\n```json\n{{ response }}\n```\n\n### Other parameters and tips\n\nadd 接口接收一个数组值,可以同时创建多条 record,单次请求可最多创建10条 record\n\n\n\n## Update records\n```shell\ncurl -X PATCH \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/records?viewId={{ viewId }}&fieldKey={{ fieldKey }}\" \\\n -H \"Authorization: Bearer {{ apiToken }}\" \\\n -H \"Content-Type: application/json\" \\\n --data '{\n \"records\": {{ records }},\n \"fieldKey\": \"{{ fieldKey }}\"\n}'\n```\n### Returned data example\n\n```json\n{{ response }}\n```\n\n\n### Other parameters and tips\n\nupdate 接口接收一个数组值,可以同时更新多条 record,单次请求可最多更新10条 record\n特别注意: update 只会更新你传入的 fields 下的数据,未传入的不会影响,如果需要清空值,请显式传入 null\n\n\n\n\n## Delete records\n```shell\ncurl -X DELETE \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/records?{{ deleteParams }}\" \\\n -H \"Authorization: Bearer {{ apiToken }}\"\n\n```\n\n### Returned data example\n```json\n{\n \"code\": 200,\n \"success\": true,\n \"message\": \"请求成功\"\n}\n```\n\n### Other parameters and tips\n\ndelete 接口接收一个数组值,可以同时删除多条 record,单次请求可最多删除10条 record\n\n\n## Upload attachments\n\n```shell\ncurl -X POST \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/attachments\" \\\n -H \"Authorization: Bearer {{ apiToken }}\" \\\n -H \"Content-Type: multipart/form-data\" \\\n -F \"file=@{你的 文件路径};\"\n```\n### Returned data example\n\n*Tips:上传完毕后,请尽快写入data 中的数据到附件单元格里,否则附件链接可能失效*\n\n```json\n{\n \"code\": 200,\n \"success\": true,\n \"message\": \"请求成功\",\n \"data\": {\n \"id\": \"atcPtxnvqti5M\",\n \"name\": \"6.gif\",\n \"size\": 33914,\n \"mimeType\": \"image/gif\",\n \"token\": \"space/2020/09/22/01ee7202922d48688f61e34f12da5abc\",\n \"width\": 240,\n \"height\": 240,\n \"url\": \"https://s1.vika.cn/space/2020/09/22/01ee7202922d48688f61e34f12da5abc\"\n }\n}\n```", + "zh_HK": "\n\n\n## 獲取記錄\n\n```shell\ncurl \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/records?viewId={{ viewId }}&fieldKey={{ fieldKey }}\" \\\n -H \"Authorization: Bearer {{ apiToken }}\"\n\n```\n\n### 示例返回值\n\n```json\n\n{{ response }}\n\n```\n\n### 其他參數或提示\n```js\n/**\n * 注意:每張維格表獲取數據最大並發量限制為 1 秒鐘 5 次\n * 全部可配置的參數,可通過 URL Query Params 發送,\n */\n{\n /**\n * (選填)視圖ID。默認為維格表中第一個視圖。請求會返回視圖中經過視圖中篩選/排序後的結果,可以搭配使用fields參數過濾不需要的字段數據\n */\n viewId: 'viewId1',\n /**\n * (選填)指定分頁的頁碼,默認為 1,與參數pageSize配合使用。\n */\n pageNum: 1,\n /**\n * (選填)指定每頁返回的記錄總數,默認為100。此參數只接受1-1000的整數。\n */\n pageSize: 100,\n /**\n * (選填)對返回的記錄進行排序。 sort 是由多個排序對象 (sort object) 組成的數組。單個排序對象的結構為 {\"order\":\"asc 或 desc\", \"field\":\"字段名稱或字段 ID\"}。查詢示例:sort[][field]=客戶名稱&sort[][order]=asc,即按照「客戶名稱」列的字母升序來排列返回的記錄。如果 sort 與 viewId 同時使用,則 sort 指定的排序條件將會覆蓋視圖裡的排序條件。\n */\n sort: [{ field: 'field1', order: 'desc' }],\n /**\n * (選填)recordIds 數組。如果附帶此參數,則返回參數中指定的records數組。返回值按照傳入數組的順序排序。此時無視篩選、排序。無分頁,每次最多查詢 1000 條\n */\n recordIds: ['recordId1', 'recordId2'],\n /**\n * (選填)指定要返回的字段(默認為字段名, 也可以通過 fieldKey 指定為字段 Id)。如果附帶此參數,則返回的記錄合集將會被過濾,只有指定的字段會返回。\n */\n fields: ['標題', '詳情', '引用次數'],\n /**\n * (選填)使用公式作為篩選條件,返回匹配的記錄,訪問 https://help.vika.cn/docs/guide/tutorial-getting-started-with-formulas 了解公式使用方式\n */\n filterByFormula: '{引用次數} > 0',\n /**\n * (選填)限制返回記錄的總數量。如果該值小於表中實際的記錄總數,則返回的記錄總數會被限制為該值。\n */\n maxRecords: 5000,\n /**\n * (選填)單元格值類型,默認為 'json',指定為 'string' 時所有值都將被自動轉換為 string 格式。\n */\n cellFormat: 'json',\n /**\n * (選填)指定 field 的查詢和返回的 key。默認使用列名 'name' 。指定為 'id' 時將以 fieldId 作為查詢和返回方式(使用 id 可以避免列名的修改導致代碼失效問題)\n */\n fieldKey: 'name',\n}\n```\n\n\n\n## 新增記錄\n```shell\ncurl -X POST \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/records?viewId={{ viewId }}&fieldKey={{ fieldKey }}\" \\\n -H \"Authorization: Bearer {{ apiToken }}\" \\\n -H \"Content-Type: application/json\" \\\n --data '{\n \"records\": {{ records }},\n \"fieldKey\": \"{{ fieldKey }}\"\n}'\n\n```\n\n### 示例返回值\n\n```json\n{{ response }}\n```\n\n### 其他參數或提示\n\nadd 接口接收一個數組值,可以同時創建多條 record,單次請求可最多創建10條 record\n\n\n\n## 更新記錄\n```shell\ncurl -X PATCH \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/records?viewId={{ viewId }}&fieldKey={{ fieldKey }}\" \\\n -H \"Authorization: Bearer {{ apiToken }}\" \\\n -H \"Content-Type: application/json\" \\\n --data '{\n \"records\": {{ records }},\n \"fieldKey\": \"{{ fieldKey }}\"\n}'\n```\n### 示例返回值\n\n```json\n{{ response }}\n```\n\n\n### 其他參數或提示\n\nupdate 接口接收一個數組值,可以同時更新多條 record,單次請求可最多更新10條 record\n特別注意: update 只會更新你傳入的 fields 下的數據,未傳入的不會影響,如果需要清空值,請顯式傳入 null\n\n\n\n\n## 刪除記錄\n```shell\ncurl -X DELETE \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/records?{{ deleteParams }}\" \\\n -H \"Authorization: Bearer {{ apiToken }}\"\n\n```\n\n### 示例返回值\n```json\n{\n \"code\": 200,\n \"success\": true,\n \"message\": \"請求成功\"\n}\n```\n\n### 其他參數或提示\n\ndelete 接口接收一個數組值,可以同時刪除多條 record,單次請求可最多刪除10條 record\n\n\n## 上傳文件\n\n```shell\ncurl -X POST \"{{ apiBase }}/fusion/v1/datasheets/{{ datasheetId }}/attachments\" \\\n -H \"Authorization: Bearer {{ apiToken }}\" \\\n -H \"Content-Type: multipart/form-data\" \\\n -F \"file=@{你的 文件路徑};\"\n```\n### 示例返回值\n\n*Tips:上傳完畢後,請盡快寫入data 中的數據到附件單元格里,否則附件鏈接可能失效*\n\n```json\n{\n \"code\": 200,\n \"success\": true,\n \"message\": \"請求成功\",\n \"data\": {\n \"id\": \"atcPtxnvqti5M\",\n \"name\": \"6.gif\",\n \"size\": 33914,\n \"mimeType\": \"image/gif\",\n \"token\": \"space/2020/09/22/01ee7202922d48688f61e34f12da5abc\",\n \"width\": 240,\n \"height\": 240,\n \"url\": \"https://s1.vika.cn/space/2020/09/22/01ee7202922d48688f61e34f12da5abc\"\n }\n}\n```" }, "api_panel_md_js_example": { - "zh_CN": "## 初始化 SDK [![github]({{ githubIcon }})](https://github.com/vikadata/vika.js)\n\n```js\n// 如果不能使用 es6 import,可用 const Vika = require('@vikadata/vika').default; 代替\nimport { Vika } from \"@vikadata/vika\";\n\nconst vika = new Vika({ token: \"{{ apiToken }}\", fieldKey: \"{{ fieldKey }}\" });\n// 通过 datasheetId 来指定要从哪张维格表操作数据。\nconst datasheet = vika.datasheet(\"{{ datasheetId }}\");\n```\n\n\n\n## 获取记录\n\n```js\n\n// 获取 {{ viewId }} 视图下的记录, 默认返回第一页。\ndatasheet.records.query({ viewId: \"{{ viewId }}\"}).then(response => {\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n});\n\n```\n\n### 示例返回值\n\n```json\n{{ response }}\n```\n\n### 其他参数或提示\n```js\n/**\n * 全局参数配置\n */\nnew Vika({\n /**\n * (必填) string 你的 API Token,用于鉴权\n */\n token: 'YOUR_API_TOKEN',\n /**\n * (选填)全局指定 field 的查询和返回的 key。默认使用列名 'name' 。指定为 'id' 时将以 fieldId 作为查询和返回方式(使用 id 可以避免列名的修改导致代码失效问题)\n */\n fieldKey: 'name', // 默认值\n /**\n * (选填)请求失效时间\n */\n requestTimeout: 60000, // 默认 60000ms (10 秒)\n /**\n * (选填)目标服务器地址\n */\n host: 'https://api.vika.cn/fusion/v1', // 默认值\n});\n\n/*******************************/\n\n// 获取记录\ndatasheet.record.query({\n /**\n * (选填)视图ID。默认为维格表中第一个视图。请求会返回视图中经过视图中筛选/排序后的结果,可以搭配使用fields参数过滤不需要的字段数据\n */\n viewId: 'viewId1',\n /**\n *(选填)对返回的记录进行排序。sort 是由多个排序对象 (sort object) 组成的数组。单个排序对象的结构为 {\"order\":\"asc 或 desc\", \"field\":\"字段名称或字段 ID\"}。查询示例:sort[][field]=客户名称&sort[][order]=asc,即按照「客户名称」列的字母升序来排列返回的记录。如果 sort 与 viewId 同时使用,则 sort 指定的排序条件将会覆盖视图里的排序条件。\n */\n sort: [{ field: 'field1': order: 'desc' }],\n /**\n * (选填)recordIds 数组。如果附带此参数,则返回参数中指定的records数组。 返回值按照传入数组的顺序排序。此时无视筛选、排序。无分页,每次最多查询 1000 条\n */\n recordIds: ['recordId1', 'recordId2'],\n /**\n * (选填)指定要返回的字段(默认为字段名, 也可以通过 fieldKey 指定为字段 Id)。如果附带此参数,则返回的记录合集将会被过滤,只有指定的字段会返回。\n */\n fields: ['标题', '详情', '引用次数'],\n /**\n * (选填)使用公式作为筛选条件,返回匹配的记录,访问 https://vika.cn/help/tutorial-getting-started-with-formulas/ 了解公式使用方式\n */\n filterByFormula: '{引用次数} > 0',\n /**\n * (选填)限制返回记录的总数量。如果该值小于表中实际的记录总数,则返回的记录总数会被限制为该值。\n */\n maxRecords: 5000,\n /**\n * (选填)单元格值类型,默认为 'json',指定为 'string' 时所有值都将被自动转换为 string 格式。\n */\n cellFormat: 'json',\n /**\n * (选填)指定 field 的查询和返回的 key。默认使用列名 'name' 。指定为 'id' 时将以 fieldId 作为查询和返回方式(使用 id 可以避免列名的修改导致代码失效问题)\n */\n fieldKey: 'name',\n});\n\n/*******************************/\n\n/**\n * 和 query 获取记录参数相同,自动处理分页。直到获取到所有数据为止。\n */\nconst allRecordsIter = datasheet.records.queryAll()\nfor await (const eachPageRecords of allRecordsIter) {\n console.log(eachPageRecords)\n}\n```\n\n\n\n## 新增记录\n```js\n// add 方法接收一个数组值,可以同时创建多条 record,单次请求可最多创建10条 record\ndatasheet.records.create({{ records }}).then(response => {\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n})\n```\n\n### 示例返回值\n\n```json\n{{ response }}\n```\n\n\n### 其他参数或提示\n\nadd 接口接收一个数组值,可以同时创建多条 record,单次请求可最多创建10条 record\n\n\n\n## 更新记录\n```js\n/**\n * update 接收一个数组值,可以同时更新多条 record,单次请求可最多更新10条 record\n * 特别注意: update 只会更新你传入的 fields 下的数据,未传入的不会影响,如果需要清空值,请显式传入 null\n */\ndatasheet.records.update({{ records }}).then(response => {\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n})\n```\n### 示例返回值\n```json\n{{ response }}\n```\n\n\n### 其他参数或提示\n\nupdate 接口接收一个数组值,可以同时更新多条 record,单次请求可最多更新10条 record\n特别注意: update 只会更新你传入的 fields 下的数据,未传入的不会影响,如果需要清空值,请显式传入 null\n \n\n\n## 删除记录\n```js\n// del 方法接收一个数组值,可以同时删除多条 record,单次请求可最多删除10条 record\ndatasheet.records.delete({{ recordIds }}).then(response => {\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n})\n```\n### 示例返回值\n```json\n{\n \"code\": 200,\n \"success\": true,\n \"message\": \"请求成功\"\n}\n```\n### 其他参数或提示\ndelete 接口接收一个数组值,可以同时删除多条 record,单次请求可最多删除10条 record\n\n\n\n## 上传文件\n\n```js\n/*\n * 从 file input 中获取文件\n * \n * 或者在 NodeJs 中使用流读取文件\n * const file = fs.createReadStream(/your/file/path)\n *\n * 下方以浏览器环境作为示例\n */\nconst input = document.getElementById('input');\n\ninput.onchange = function () {\n const file = this.files[0];\n datasheet.upload(file).then(response => {\n /**\n * response 数据包括\n * success: boolean\n * code: number\n * message: string\n * data: IAttachment\n */\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n });\n};\n\n```\n### 示例返回值\n\n*Tips:上传完毕后,请尽快写入data 中的数据到附件单元格里,否则附件链接可能失效*\n\n```json\n{\n \"code\": 200,\n \"success\": true,\n \"message\": \"请求成功\",\n \"data\": {\n \"id\": \"atcPtxnvqti5M\",\n \"name\": \"6.gif\",\n \"size\": 33914,\n \"mimeType\": \"image/gif\",\n \"token\": \"space/2020/09/22/01ee7202922d48688f61e34f12da5abc\",\n \"width\": 240,\n \"height\": 240,\n \"url\": \"https://s1.vika.cn/space/2020/09/22/01ee7202922d48688f61e34f12da5abc\"\n }\n}\n```\n\n### 其他参数或提示\ndelete 接口接收一个数组值,可以同时删除多条 record,单次请求可最多删除10条 record", - "en_US": "## Initialize SDK [![github]({{ githubIcon }})](https://github.com/vikadata/vika.js)\n\n```js\n// If es6 import does not work,use const Vika = require('@vikadata/vika').default; to replace import { Vika } from \"@vikadata/vika\";\n\nconst vika = new Vika({ token: \"{{ apiToken }}\", fieldKey: \"{{ fieldKey }}\" });\nconst datasheet = vika.datasheet(\"{{ datasheetId }}\");\n```\n\n\n\n## Get records\n\n```js\n\n// Get the records from a specific view. Returns the first page of records by default\ndatasheet.records.query({ viewId: \"{{ viewId }}\"}).then(response => {\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n});\n\n```\n\n### Returned data example\n\n```json\n{{ response }}\n```\n\n### Other parameters and tips\n```js\n/**\n * 全局参数配置\n */\nnew Vika({\n /**\n * (必填) string 你的 API Token,用于鉴权\n */\n token: 'YOUR_API_TOKEN',\n /**\n * (选填)全局指定 field 的查询和返回的 key。默认使用列名 'name' 。指定为 'id' 时将以 fieldId 作为查询和返回方式(使用 id 可以避免列名的修改导致代码失效问题)\n */\n fieldKey: 'name', // 默认值\n /**\n * (选填)请求失效时间\n */\n requestTimeout: 60000, // 默认 60000ms (10 秒)\n /**\n * (选填)目标服务器地址\n */\n host: 'https://api.vika.cn/fusion/v1', // 默认值\n});\n\n/*******************************/\n\n// 获取记录\ndatasheet.record.query({\n /**\n * (选填)视图ID。默认为维格表中第一个视图。请求会返回视图中经过视图中筛选/排序后的结果,可以搭配使用fields参数过滤不需要的字段数据\n */\n viewId: 'viewId1',\n /**\n * (选填)对指定维格表的记录进行排序。由多个“排序对象”组成的数组。支持顺序:'asc' 和 逆序:'desc'。注:此参数指定的排序条件将会覆盖视图里的排序条件。\n */\n sort: [{ field: 'field1': order: 'asc' }],\n /**\n * (选填)recordIds 数组。如果附带此参数,则返回参数中指定的records数组。 返回值按照传入数组的顺序排序。此时无视筛选、排序。无分页,每次最多查询 1000 条\n */\n recordIds: ['recordId1', 'recordId2'],\n /**\n * (选填)指定要返回的字段(默认为字段名, 也可以通过 fieldKey 指定为字段 Id)。如果附带此参数,则返回的记录合集将会被过滤,只有指定的字段会返回。\n */\n fields: ['标题', '详情', '引用次数'],\n /**\n * (选填)使用公式作为筛选条件,返回匹配的记录,访问 https://vika.cn/help/tutorial-getting-started-with-formulas/ 了解公式使用方式\n */\n filterByFormula: '{引用次数} > 0',\n /**\n * (选填)限制返回记录的总数量。如果该值小于表中实际的记录总数,则返回的记录总数会被限制为该值。\n */\n maxRecords: 5000,\n /**\n * (选填)单元格值类型,默认为 'json',指定为 'string' 时所有值都将被自动转换为 string 格式。\n */\n cellFormat: 'json',\n /**\n * (选填)指定 field 的查询和返回的 key。默认使用列名 'name' 。指定为 'id' 时将以 fieldId 作为查询和返回方式(使用 id 可以避免列名的修改导致代码失效问题)\n */\n fieldKey: 'name',\n});\n\n/*******************************/\n\n/**\n * 和 query 获取记录参数相同,自动处理分页。直到获取到所有数据为止。\n */\nconst allRecordsIter = datasheet.records.queryAll()\nfor await (const eachPageRecords of allRecordsIter) {\n console.log(eachPageRecords)\n}\n```\n\n\n\n## Add records\n```js\n// add 方法接收一个数组值,可以同时创建多条 record,单次请求可最多创建10条 record\ndatasheet.records.create({{ records }}).then(response => {\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n})\n```\n\n### Returned data example\n\n```json\n{{ response }}\n```\n\n\n### Other parameters and tips\n\nadd 接口接收一个数组值,可以同时创建多条 record,单次请求可最多创建10条 record\n\n\n\n## Update records\n```js\n/**\n * update 接收一个数组值,可以同时更新多条 record,单次请求可最多更新10条 record\n * 特别注意: update 只会更新你传入的 fields 下的数据,未传入的不会影响,如果需要清空值,请显式传入 null\n */\ndatasheet.records.update({{ records }}).then(response => {\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n})\n```\n### Returned data example\n```json\n{{ response }}\n```\n\n\n### Other parameters and tips\n\nupdate 接口接收一个数组值,可以同时更新多条 record,单次请求可最多更新10条 record\n特别注意: update 只会更新你传入的 fields 下的数据,未传入的不会影响,如果需要清空值,请显式传入 null\n \n\n\n## Delete records\n```js\n// del 方法接收一个数组值,可以同时删除多条 record,单次请求可最多删除10条 record\ndatasheet.records.delete({{ recordIds }}).then(response => {\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n})\n```\n### Returned data example\n```json\n{\n \"code\": 200,\n \"success\": true,\n \"message\": \"请求成功\"\n}\n```\n### Other parameters and tips\ndelete 接口接收一个数组值,可以同时删除多条 record,单次请求可最多删除10条 record\n\n\n\n## Upload attachments\n\n```js\n/*\n * 从 file input 中获取文件\n * \n * 或者在 NodeJs 中使用流读取文件\n * const file = fs.createReadStream(/your/file/path)\n *\n * 下方以浏览器环境作为示例\n */\nconst input = document.getElementById('input');\n\ninput.onchange = function () {\n const file = this.files[0];\n datasheet.upload(file).then(response => {\n /**\n * response 数据包括\n * success: boolean\n * code: number\n * message: string\n * data: IAttachment\n */\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n });\n};\n\n```\n### Returned data example\n\n*Tips:上传完毕后,请尽快写入data 中的数据到附件单元格里,否则附件链接可能失效*\n\n```json\n{\n \"code\": 200,\n \"success\": true,\n \"message\": \"请求成功\",\n \"data\": {\n \"id\": \"atcPtxnvqti5M\",\n \"name\": \"6.gif\",\n \"size\": 33914,\n \"mimeType\": \"image/gif\",\n \"token\": \"space/2020/09/22/01ee7202922d48688f61e34f12da5abc\",\n \"width\": 240,\n \"height\": 240,\n \"url\": \"https://s1.vika.cn/space/2020/09/22/01ee7202922d48688f61e34f12da5abc\"\n }\n}\n```\n\n### Other parameters and tips\ndelete 接口接收一个数组值,可以同时删除多条 record,单次请求可最多删除10条 record", - "zh_HK": "## 初始化 SDK [![github]({{ githubIcon }})](https://github.com/vikadata/vika.js)\n\n```js\n// 如果不能使用 es6 import,可用 const Vika = require('@vikadata/vika').default; 代替\nimport { Vika } from \"@vikadata/vika\";\n\nconst vika = new Vika({ token: \"{{ apiToken }}\", fieldKey: \"{{ fieldKey }}\" });\n// 通過 datasheetId 來指定要從哪張維格表操作數據。\nconst datasheet = vika.datasheet(\"{{ datasheetId }}\");\n```\n\n\n\n## 獲取記錄\n\n```js\n\n// 獲取 {{ viewId }} 視圖下的記錄, 默認返回第一頁。\ndatasheet.records.query({ viewId: \"{{ viewId }}\"}).then(response => {\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n});\n\n```\n\n### 示例返回值\n\n```json\n{{ response }}\n```\n\n### 其他參數或提示\n```js\n/**\n * 全局參數配置\n */\nnew Vika({\n /**\n * (必填) string 你的 API Token,用於鑑權\n */\n token: 'YOUR_API_TOKEN',\n /**\n * (選填)全局指定 field 的查詢和返回的 key。默認使用列名 'name' 。指定為 'id' 時將以 fieldId 作為查詢和返回方式(使用 id 可以避免列名的修改導致代碼失效問題)\n */\n fieldKey: 'name', // 默認值\n /**\n * (選填)請求失效時間\n */\n requestTimeout: 60000, // 默認 60000ms (10 秒)\n /**\n * (選填)目標服務器地址\n */\n host: 'https://api.vika.cn/fusion/v1', // 默認值\n});\n\n/*******************************/\n\n// 獲取記錄\ndatasheet.record.query({\n /**\n * (選填)視圖ID。默認為維格表中第一個視圖。請求會返回視圖中經過視圖中篩選/排序後的結果,可以搭配使用fields參數過濾不需要的字段數據\n */\n viewId: 'viewId1',\n /**\n *(選填)對返回的記錄進行排序。 sort 是由多個排序對象 (sort object) 組成的數組。單個排序對象的結構為 {\"order\":\"asc 或 desc\", \"field\":\"字段名稱或字段 ID\"}。查詢示例:sort[][field]=客戶名稱&sort[][order]=asc,即按照「客戶名稱」列的字母升序來排列返回的記錄。如果 sort 與 viewId 同時使用,則 sort 指定的排序條件將會覆蓋視圖裡的排序條件。\n */\n sort: [{ field: 'field1': order: 'desc' }],\n /**\n * (選填)recordIds 數組。如果附帶此參數,則返回參數中指定的records數組。返回值按照傳入數組的順序排序。此時無視篩選、排序。無分頁,每次最多查詢 1000 條\n */\n recordIds: ['recordId1', 'recordId2'],\n /**\n * (選填)指定要返回的字段(默認為字段名, 也可以通過 fieldKey 指定為字段 Id)。如果附帶此參數,則返回的記錄合集將會被過濾,只有指定的字段會返回。\n */\n fields: ['標題', '詳情', '引用次數'],\n /**\n * (選填)使用公式作為篩選條件,返回匹配的記錄,訪問 https://vika.cn/help/tutorial-getting-started-with-formulas/ 了解公式使用方式\n */\n filterByFormula: '{引用次數} > 0',\n /**\n * (選填)限制返回記錄的總數量。如果該值小於表中實際的記錄總數,則返回的記錄總數會被限制為該值。\n */\n maxRecords: 5000,\n /**\n * (選填)單元格值類型,默認為 'json',指定為 'string' 時所有值都將被自動轉換為 string 格式。\n */\n cellFormat: 'json',\n /**\n * (選填)指定 field 的查詢和返回的 key。默認使用列名 'name' 。指定為 'id' 時將以 fieldId 作為查詢和返回方式(使用 id 可以避免列名的修改導致代碼失效問題)\n */\n fieldKey: 'name',\n});\n\n/*******************************/\n\n/**\n * 和 query 獲取記錄參數相同,自動處理分頁。直到獲取到所有數據為止。\n */\nconst allRecordsIter = datasheet.records.queryAll()\nfor await (const eachPageRecords of allRecordsIter) {\n console.log(eachPageRecords)\n}\n```\n\n\n\n## 新增記錄\n```js\n// add 方法接收一個數組值,可以同時創建多條 record,單次請求可最多創建10條 record\ndatasheet.records.create({{ records }}).then(response => {\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n})\n```\n\n### 示例返回值\n\n```json\n{{ response }}\n```\n\n\n### 其他參數或提示\n\nadd 接口接收一個數組值,可以同時創建多條 record,單次請求可最多創建10條 record\n\n\n\n## 更新記錄\n```js\n/**\n * update 接收一個數組值,可以同時更新多條 record,單次請求可最多更新10條 record\n * 特別注意: update 只會更新你傳入的 fields 下的數據,未傳入的不會影響,如果需要清空值,請顯式傳入 null\n */\ndatasheet.records.update({{ records }}).then(response => {\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n})\n```\n### 示例返回值\n```json\n{{ response }}\n```\n\n\n### 其他參數或提示\n\nupdate 接口接收一個數組值,可以同時更新多條 record,單次請求可最多更新10條 record\n特別注意: update 只會更新你傳入的 fields 下的數據,未傳入的不會影響,如果需要清空值,請顯式傳入 null\n \n\n\n## 刪除記錄\n```js\n// del 方法接收一個數組值,可以同時刪除多條 record,單次請求可最多刪除10條 record\ndatasheet.records.delete({{ recordIds }}).then(response => {\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n})\n```\n### 示例返回值\n```json\n{\n \"code\": 200,\n \"success\": true,\n \"message\": \"請求成功\"\n}\n```\n### 其他參數或提示\ndelete 接口接收一個數組值,可以同時刪除多條 record,單次請求可最多刪除10條 record\n\n\n\n## 上傳文件\n\n```js\n/*\n * 從 file input 中獲取文件\n * \n * 或者在 NodeJs 中使用流讀取文件\n * const file = fs.createReadStream(/your/file/path)\n *\n * 下方以瀏覽器環境作為示例\n */\nconst input = document.getElementById('input');\n\ninput.onchange = function () {\n const file = this.files[0];\n datasheet.upload(file).then(response => {\n /**\n * response 數據包括\n * success: boolean\n * code: number\n * message: string\n * data: IAttachment\n */\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n });\n};\n\n```\n### 示例返回值\n\n*Tips:上傳完畢後,請盡快寫入data 中的數據到附件單元格里,否則附件鏈接可能失效*\n\n```json\n{\n \"code\": 200,\n \"success\": true,\n \"message\": \"請求成功\",\n \"data\": {\n \"id\": \"atcPtxnvqti5M\",\n \"name\": \"6.gif\",\n \"size\": 33914,\n \"mimeType\": \"image/gif\",\n \"token\": \"space/2020/09/22/01ee7202922d48688f61e34f12da5abc\",\n \"width\": 240,\n \"height\": 240,\n \"url\": \"https://s1.vika.cn/space/2020/09/22/01ee7202922d48688f61e34f12da5abc\"\n }\n}\n```\n\n### 其他參數或提示\ndelete 接口接收一個數組值,可以同時刪除多條 record,單次請求可最多刪除10條 record" + "zh_CN": "## 初始化 SDK [![github]({{ githubIcon }})](https://github.com/vikadata/vika.js)\n\n```js\n// 如果不能使用 es6 import,可用 const Vika = require('@vikadata/vika').default; 代替\nimport { Vika } from \"@vikadata/vika\";\n\nconst vika = new Vika({ token: \"{{ apiToken }}\", fieldKey: \"{{ fieldKey }}\" });\n// 通过 datasheetId 来指定要从哪张维格表操作数据。\nconst datasheet = vika.datasheet(\"{{ datasheetId }}\");\n```\n\n\n\n## 获取记录\n\n```js\n\n// 获取 {{ viewId }} 视图下的记录, 默认返回第一页。\ndatasheet.records.query({ viewId: \"{{ viewId }}\"}).then(response => {\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n});\n\n```\n\n### 示例返回值\n\n```json\n{{ response }}\n```\n\n### 其他参数或提示\n```js\n/**\n * 全局参数配置\n */\nnew Vika({\n /**\n * (必填) string 你的 API Token,用于鉴权\n */\n token: 'YOUR_API_TOKEN',\n /**\n * (选填)全局指定 field 的查询和返回的 key。默认使用列名 'name' 。指定为 'id' 时将以 fieldId 作为查询和返回方式(使用 id 可以避免列名的修改导致代码失效问题)\n */\n fieldKey: 'name', // 默认值\n /**\n * (选填)请求失效时间\n */\n requestTimeout: 60000, // 默认 60000ms (10 秒)\n /**\n * (选填)目标服务器地址\n */\n host: 'https://api.vika.cn/fusion/v1', // 默认值\n});\n\n/*******************************/\n\n// 获取记录\ndatasheet.record.query({\n /**\n * (选填)视图ID。默认为维格表中第一个视图。请求会返回视图中经过视图中筛选/排序后的结果,可以搭配使用fields参数过滤不需要的字段数据\n */\n viewId: 'viewId1',\n /**\n *(选填)对返回的记录进行排序。sort 是由多个排序对象 (sort object) 组成的数组。单个排序对象的结构为 {\"order\":\"asc 或 desc\", \"field\":\"字段名称或字段 ID\"}。查询示例:sort[][field]=客户名称&sort[][order]=asc,即按照「客户名称」列的字母升序来排列返回的记录。如果 sort 与 viewId 同时使用,则 sort 指定的排序条件将会覆盖视图里的排序条件。\n */\n sort: [{ field: 'field1': order: 'desc' }],\n /**\n * (选填)recordIds 数组。如果附带此参数,则返回参数中指定的records数组。 返回值按照传入数组的顺序排序。此时无视筛选、排序。无分页,每次最多查询 1000 条\n */\n recordIds: ['recordId1', 'recordId2'],\n /**\n * (选填)指定要返回的字段(默认为字段名, 也可以通过 fieldKey 指定为字段 Id)。如果附带此参数,则返回的记录合集将会被过滤,只有指定的字段会返回。\n */\n fields: ['标题', '详情', '引用次数'],\n /**\n * (选填)使用公式作为筛选条件,返回匹配的记录,访问 https://help.vika.cn/docs/guide/tutorial-getting-started-with-formulas 了解公式使用方式\n */\n filterByFormula: '{引用次数} > 0',\n /**\n * (选填)限制返回记录的总数量。如果该值小于表中实际的记录总数,则返回的记录总数会被限制为该值。\n */\n maxRecords: 5000,\n /**\n * (选填)单元格值类型,默认为 'json',指定为 'string' 时所有值都将被自动转换为 string 格式。\n */\n cellFormat: 'json',\n /**\n * (选填)指定 field 的查询和返回的 key。默认使用列名 'name' 。指定为 'id' 时将以 fieldId 作为查询和返回方式(使用 id 可以避免列名的修改导致代码失效问题)\n */\n fieldKey: 'name',\n});\n\n/*******************************/\n\n/**\n * 和 query 获取记录参数相同,自动处理分页。直到获取到所有数据为止。\n */\nconst allRecordsIter = datasheet.records.queryAll()\nfor await (const eachPageRecords of allRecordsIter) {\n console.log(eachPageRecords)\n}\n```\n\n\n\n## 新增记录\n```js\n// add 方法接收一个数组值,可以同时创建多条 record,单次请求可最多创建10条 record\ndatasheet.records.create({{ records }}).then(response => {\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n})\n```\n\n### 示例返回值\n\n```json\n{{ response }}\n```\n\n\n### 其他参数或提示\n\nadd 接口接收一个数组值,可以同时创建多条 record,单次请求可最多创建10条 record\n\n\n\n## 更新记录\n```js\n/**\n * update 接收一个数组值,可以同时更新多条 record,单次请求可最多更新10条 record\n * 特别注意: update 只会更新你传入的 fields 下的数据,未传入的不会影响,如果需要清空值,请显式传入 null\n */\ndatasheet.records.update({{ records }}).then(response => {\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n})\n```\n### 示例返回值\n```json\n{{ response }}\n```\n\n\n### 其他参数或提示\n\nupdate 接口接收一个数组值,可以同时更新多条 record,单次请求可最多更新10条 record\n特别注意: update 只会更新你传入的 fields 下的数据,未传入的不会影响,如果需要清空值,请显式传入 null\n \n\n\n## 删除记录\n```js\n// del 方法接收一个数组值,可以同时删除多条 record,单次请求可最多删除10条 record\ndatasheet.records.delete({{ recordIds }}).then(response => {\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n})\n```\n### 示例返回值\n```json\n{\n \"code\": 200,\n \"success\": true,\n \"message\": \"请求成功\"\n}\n```\n### 其他参数或提示\ndelete 接口接收一个数组值,可以同时删除多条 record,单次请求可最多删除10条 record\n\n\n\n## 上传文件\n\n```js\n/*\n * 从 file input 中获取文件\n * \n * 或者在 NodeJs 中使用流读取文件\n * const file = fs.createReadStream(/your/file/path)\n *\n * 下方以浏览器环境作为示例\n */\nconst input = document.getElementById('input');\n\ninput.onchange = function () {\n const file = this.files[0];\n datasheet.upload(file).then(response => {\n /**\n * response 数据包括\n * success: boolean\n * code: number\n * message: string\n * data: IAttachment\n */\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n });\n};\n\n```\n### 示例返回值\n\n*Tips:上传完毕后,请尽快写入data 中的数据到附件单元格里,否则附件链接可能失效*\n\n```json\n{\n \"code\": 200,\n \"success\": true,\n \"message\": \"请求成功\",\n \"data\": {\n \"id\": \"atcPtxnvqti5M\",\n \"name\": \"6.gif\",\n \"size\": 33914,\n \"mimeType\": \"image/gif\",\n \"token\": \"space/2020/09/22/01ee7202922d48688f61e34f12da5abc\",\n \"width\": 240,\n \"height\": 240,\n \"url\": \"https://s1.vika.cn/space/2020/09/22/01ee7202922d48688f61e34f12da5abc\"\n }\n}\n```\n\n### 其他参数或提示\ndelete 接口接收一个数组值,可以同时删除多条 record,单次请求可最多删除10条 record", + "en_US": "## Initialize SDK [![github]({{ githubIcon }})](https://github.com/vikadata/vika.js)\n\n```js\n// If es6 import does not work,use const Vika = require('@vikadata/vika').default; to replace import { Vika } from \"@vikadata/vika\";\n\nconst vika = new Vika({ token: \"{{ apiToken }}\", fieldKey: \"{{ fieldKey }}\" });\nconst datasheet = vika.datasheet(\"{{ datasheetId }}\");\n```\n\n\n\n## Get records\n\n```js\n\n// Get the records from a specific view. Returns the first page of records by default\ndatasheet.records.query({ viewId: \"{{ viewId }}\"}).then(response => {\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n});\n\n```\n\n### Returned data example\n\n```json\n{{ response }}\n```\n\n### Other parameters and tips\n```js\n/**\n * 全局参数配置\n */\nnew Vika({\n /**\n * (必填) string 你的 API Token,用于鉴权\n */\n token: 'YOUR_API_TOKEN',\n /**\n * (选填)全局指定 field 的查询和返回的 key。默认使用列名 'name' 。指定为 'id' 时将以 fieldId 作为查询和返回方式(使用 id 可以避免列名的修改导致代码失效问题)\n */\n fieldKey: 'name', // 默认值\n /**\n * (选填)请求失效时间\n */\n requestTimeout: 60000, // 默认 60000ms (10 秒)\n /**\n * (选填)目标服务器地址\n */\n host: 'https://api.vika.cn/fusion/v1', // 默认值\n});\n\n/*******************************/\n\n// 获取记录\ndatasheet.record.query({\n /**\n * (选填)视图ID。默认为维格表中第一个视图。请求会返回视图中经过视图中筛选/排序后的结果,可以搭配使用fields参数过滤不需要的字段数据\n */\n viewId: 'viewId1',\n /**\n * (选填)对指定维格表的记录进行排序。由多个“排序对象”组成的数组。支持顺序:'asc' 和 逆序:'desc'。注:此参数指定的排序条件将会覆盖视图里的排序条件。\n */\n sort: [{ field: 'field1': order: 'asc' }],\n /**\n * (选填)recordIds 数组。如果附带此参数,则返回参数中指定的records数组。 返回值按照传入数组的顺序排序。此时无视筛选、排序。无分页,每次最多查询 1000 条\n */\n recordIds: ['recordId1', 'recordId2'],\n /**\n * (选填)指定要返回的字段(默认为字段名, 也可以通过 fieldKey 指定为字段 Id)。如果附带此参数,则返回的记录合集将会被过滤,只有指定的字段会返回。\n */\n fields: ['标题', '详情', '引用次数'],\n /**\n * (选填)使用公式作为筛选条件,返回匹配的记录,访问 https://help.vika.cn/docs/guide/tutorial-getting-started-with-formulas 了解公式使用方式\n */\n filterByFormula: '{引用次数} > 0',\n /**\n * (选填)限制返回记录的总数量。如果该值小于表中实际的记录总数,则返回的记录总数会被限制为该值。\n */\n maxRecords: 5000,\n /**\n * (选填)单元格值类型,默认为 'json',指定为 'string' 时所有值都将被自动转换为 string 格式。\n */\n cellFormat: 'json',\n /**\n * (选填)指定 field 的查询和返回的 key。默认使用列名 'name' 。指定为 'id' 时将以 fieldId 作为查询和返回方式(使用 id 可以避免列名的修改导致代码失效问题)\n */\n fieldKey: 'name',\n});\n\n/*******************************/\n\n/**\n * 和 query 获取记录参数相同,自动处理分页。直到获取到所有数据为止。\n */\nconst allRecordsIter = datasheet.records.queryAll()\nfor await (const eachPageRecords of allRecordsIter) {\n console.log(eachPageRecords)\n}\n```\n\n\n\n## Add records\n```js\n// add 方法接收一个数组值,可以同时创建多条 record,单次请求可最多创建10条 record\ndatasheet.records.create({{ records }}).then(response => {\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n})\n```\n\n### Returned data example\n\n```json\n{{ response }}\n```\n\n\n### Other parameters and tips\n\nadd 接口接收一个数组值,可以同时创建多条 record,单次请求可最多创建10条 record\n\n\n\n## Update records\n```js\n/**\n * update 接收一个数组值,可以同时更新多条 record,单次请求可最多更新10条 record\n * 特别注意: update 只会更新你传入的 fields 下的数据,未传入的不会影响,如果需要清空值,请显式传入 null\n */\ndatasheet.records.update({{ records }}).then(response => {\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n})\n```\n### Returned data example\n```json\n{{ response }}\n```\n\n\n### Other parameters and tips\n\nupdate 接口接收一个数组值,可以同时更新多条 record,单次请求可最多更新10条 record\n特别注意: update 只会更新你传入的 fields 下的数据,未传入的不会影响,如果需要清空值,请显式传入 null\n \n\n\n## Delete records\n```js\n// del 方法接收一个数组值,可以同时删除多条 record,单次请求可最多删除10条 record\ndatasheet.records.delete({{ recordIds }}).then(response => {\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n})\n```\n### Returned data example\n```json\n{\n \"code\": 200,\n \"success\": true,\n \"message\": \"请求成功\"\n}\n```\n### Other parameters and tips\ndelete 接口接收一个数组值,可以同时删除多条 record,单次请求可最多删除10条 record\n\n\n\n## Upload attachments\n\n```js\n/*\n * 从 file input 中获取文件\n * \n * 或者在 NodeJs 中使用流读取文件\n * const file = fs.createReadStream(/your/file/path)\n *\n * 下方以浏览器环境作为示例\n */\nconst input = document.getElementById('input');\n\ninput.onchange = function () {\n const file = this.files[0];\n datasheet.upload(file).then(response => {\n /**\n * response 数据包括\n * success: boolean\n * code: number\n * message: string\n * data: IAttachment\n */\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n });\n};\n\n```\n### Returned data example\n\n*Tips:上传完毕后,请尽快写入data 中的数据到附件单元格里,否则附件链接可能失效*\n\n```json\n{\n \"code\": 200,\n \"success\": true,\n \"message\": \"请求成功\",\n \"data\": {\n \"id\": \"atcPtxnvqti5M\",\n \"name\": \"6.gif\",\n \"size\": 33914,\n \"mimeType\": \"image/gif\",\n \"token\": \"space/2020/09/22/01ee7202922d48688f61e34f12da5abc\",\n \"width\": 240,\n \"height\": 240,\n \"url\": \"https://s1.vika.cn/space/2020/09/22/01ee7202922d48688f61e34f12da5abc\"\n }\n}\n```\n\n### Other parameters and tips\ndelete 接口接收一个数组值,可以同时删除多条 record,单次请求可最多删除10条 record", + "zh_HK": "## 初始化 SDK [![github]({{ githubIcon }})](https://github.com/vikadata/vika.js)\n\n```js\n// 如果不能使用 es6 import,可用 const Vika = require('@vikadata/vika').default; 代替\nimport { Vika } from \"@vikadata/vika\";\n\nconst vika = new Vika({ token: \"{{ apiToken }}\", fieldKey: \"{{ fieldKey }}\" });\n// 通過 datasheetId 來指定要從哪張維格表操作數據。\nconst datasheet = vika.datasheet(\"{{ datasheetId }}\");\n```\n\n\n\n## 獲取記錄\n\n```js\n\n// 獲取 {{ viewId }} 視圖下的記錄, 默認返回第一頁。\ndatasheet.records.query({ viewId: \"{{ viewId }}\"}).then(response => {\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n});\n\n```\n\n### 示例返回值\n\n```json\n{{ response }}\n```\n\n### 其他參數或提示\n```js\n/**\n * 全局參數配置\n */\nnew Vika({\n /**\n * (必填) string 你的 API Token,用於鑑權\n */\n token: 'YOUR_API_TOKEN',\n /**\n * (選填)全局指定 field 的查詢和返回的 key。默認使用列名 'name' 。指定為 'id' 時將以 fieldId 作為查詢和返回方式(使用 id 可以避免列名的修改導致代碼失效問題)\n */\n fieldKey: 'name', // 默認值\n /**\n * (選填)請求失效時間\n */\n requestTimeout: 60000, // 默認 60000ms (10 秒)\n /**\n * (選填)目標服務器地址\n */\n host: 'https://api.vika.cn/fusion/v1', // 默認值\n});\n\n/*******************************/\n\n// 獲取記錄\ndatasheet.record.query({\n /**\n * (選填)視圖ID。默認為維格表中第一個視圖。請求會返回視圖中經過視圖中篩選/排序後的結果,可以搭配使用fields參數過濾不需要的字段數據\n */\n viewId: 'viewId1',\n /**\n *(選填)對返回的記錄進行排序。 sort 是由多個排序對象 (sort object) 組成的數組。單個排序對象的結構為 {\"order\":\"asc 或 desc\", \"field\":\"字段名稱或字段 ID\"}。查詢示例:sort[][field]=客戶名稱&sort[][order]=asc,即按照「客戶名稱」列的字母升序來排列返回的記錄。如果 sort 與 viewId 同時使用,則 sort 指定的排序條件將會覆蓋視圖裡的排序條件。\n */\n sort: [{ field: 'field1': order: 'desc' }],\n /**\n * (選填)recordIds 數組。如果附帶此參數,則返回參數中指定的records數組。返回值按照傳入數組的順序排序。此時無視篩選、排序。無分頁,每次最多查詢 1000 條\n */\n recordIds: ['recordId1', 'recordId2'],\n /**\n * (選填)指定要返回的字段(默認為字段名, 也可以通過 fieldKey 指定為字段 Id)。如果附帶此參數,則返回的記錄合集將會被過濾,只有指定的字段會返回。\n */\n fields: ['標題', '詳情', '引用次數'],\n /**\n * (選填)使用公式作為篩選條件,返回匹配的記錄,訪問 https://help.vika.cn/docs/guide/tutorial-getting-started-with-formulas 了解公式使用方式\n */\n filterByFormula: '{引用次數} > 0',\n /**\n * (選填)限制返回記錄的總數量。如果該值小於表中實際的記錄總數,則返回的記錄總數會被限制為該值。\n */\n maxRecords: 5000,\n /**\n * (選填)單元格值類型,默認為 'json',指定為 'string' 時所有值都將被自動轉換為 string 格式。\n */\n cellFormat: 'json',\n /**\n * (選填)指定 field 的查詢和返回的 key。默認使用列名 'name' 。指定為 'id' 時將以 fieldId 作為查詢和返回方式(使用 id 可以避免列名的修改導致代碼失效問題)\n */\n fieldKey: 'name',\n});\n\n/*******************************/\n\n/**\n * 和 query 獲取記錄參數相同,自動處理分頁。直到獲取到所有數據為止。\n */\nconst allRecordsIter = datasheet.records.queryAll()\nfor await (const eachPageRecords of allRecordsIter) {\n console.log(eachPageRecords)\n}\n```\n\n\n\n## 新增記錄\n```js\n// add 方法接收一個數組值,可以同時創建多條 record,單次請求可最多創建10條 record\ndatasheet.records.create({{ records }}).then(response => {\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n})\n```\n\n### 示例返回值\n\n```json\n{{ response }}\n```\n\n\n### 其他參數或提示\n\nadd 接口接收一個數組值,可以同時創建多條 record,單次請求可最多創建10條 record\n\n\n\n## 更新記錄\n```js\n/**\n * update 接收一個數組值,可以同時更新多條 record,單次請求可最多更新10條 record\n * 特別注意: update 只會更新你傳入的 fields 下的數據,未傳入的不會影響,如果需要清空值,請顯式傳入 null\n */\ndatasheet.records.update({{ records }}).then(response => {\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n})\n```\n### 示例返回值\n```json\n{{ response }}\n```\n\n\n### 其他參數或提示\n\nupdate 接口接收一個數組值,可以同時更新多條 record,單次請求可最多更新10條 record\n特別注意: update 只會更新你傳入的 fields 下的數據,未傳入的不會影響,如果需要清空值,請顯式傳入 null\n \n\n\n## 刪除記錄\n```js\n// del 方法接收一個數組值,可以同時刪除多條 record,單次請求可最多刪除10條 record\ndatasheet.records.delete({{ recordIds }}).then(response => {\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n})\n```\n### 示例返回值\n```json\n{\n \"code\": 200,\n \"success\": true,\n \"message\": \"請求成功\"\n}\n```\n### 其他參數或提示\ndelete 接口接收一個數組值,可以同時刪除多條 record,單次請求可最多刪除10條 record\n\n\n\n## 上傳文件\n\n```js\n/*\n * 從 file input 中獲取文件\n * \n * 或者在 NodeJs 中使用流讀取文件\n * const file = fs.createReadStream(/your/file/path)\n *\n * 下方以瀏覽器環境作為示例\n */\nconst input = document.getElementById('input');\n\ninput.onchange = function () {\n const file = this.files[0];\n datasheet.upload(file).then(response => {\n /**\n * response 數據包括\n * success: boolean\n * code: number\n * message: string\n * data: IAttachment\n */\n if (response.success) {\n console.log(response.data);\n } else {\n console.error(response);\n }\n });\n};\n\n```\n### 示例返回值\n\n*Tips:上傳完畢後,請盡快寫入data 中的數據到附件單元格里,否則附件鏈接可能失效*\n\n```json\n{\n \"code\": 200,\n \"success\": true,\n \"message\": \"請求成功\",\n \"data\": {\n \"id\": \"atcPtxnvqti5M\",\n \"name\": \"6.gif\",\n \"size\": 33914,\n \"mimeType\": \"image/gif\",\n \"token\": \"space/2020/09/22/01ee7202922d48688f61e34f12da5abc\",\n \"width\": 240,\n \"height\": 240,\n \"url\": \"https://s1.vika.cn/space/2020/09/22/01ee7202922d48688f61e34f12da5abc\"\n }\n}\n```\n\n### 其他參數或提示\ndelete 接口接收一個數組值,可以同時刪除多條 record,單次請求可最多刪除10條 record" }, "api_panel_md_python_example": { "zh_CN": "## 初始化 SDK [![github]({{ githubIcon }})](https://github.com/vikadata/vika.py)\n\n```python\nfrom vika import Vika\nvika = Vika(\"{{ apiToken }}\")\n# 通过 datasheetId 来指定要从哪张维格表操作数据。\ndatasheet = vika.datasheet(\"{{ datasheetId }}\", field_key=\"{{ fieldKey }}\")\n```\n\n{{ fieldNameTips }}\n\n\n## 获取记录\n\n```python\n# 返回所有记录的集合\nrecords = datasheet.records.all()\nfor record in records:\n print(record.json())\n\n# 指定视图id,只返回该视图的记录\nrecords_via_view = datasheet.records.all(viewId=\"{{ viewId }}\")\n# 查找并返回一条记录(如果存在多条,则返回第一条),一般通过唯一标识获取单条记录。\none_record = datasheet.records.get({{ pyGetParams }})\n# 查询并返回符合条件的记录数组,不加条件效果和 all 相同\nrecords = datasheet.records.filter({{ pyGetParams }})\n```\n\n\n## 新增记录\n```python\n# 创建单条记录\nrow = datasheet.records.create({{ addParams }})\n# 创建多条记录\nrecords = datasheet.records.bulk_create({{ bulkAddParams }})\n\n```\n\n\n## 更新记录\n```python\nrow = datasheet.records.get({{ pyGetParams }})\n\n# 更新单个字段\nrow.{{ oneFieldKey }} = {{ oneFieldValue }}\n## row.save()\n\n# 更新多个字段\nrow.update({{ updateParams }})\n\n```\n\n\n## 删除记录\n```python\nrecord = datasheet.records.get({{ pyGetParams }})\n# 删除单条记录\nrecord.delete()\n# 删除符合查询条件的一批记录\ndatasheet.records.filter({{ pyGetParams }}).delete()\n# 通过 recordId 删除记录\ndatasheet.delete_records({{ recordIds }})\n```\n\n\n## 上传文件\n\n```python\n_file = datasheet.upload_file(<本地/网络文件路径>)\nrecord.{{ attachFieldName }} = [_file]\n```", @@ -1019,7 +1046,7 @@ }, "api_panel_type_desc_created_by": { "zh_CN": "创建此记录的成员(unit),以数组形式返回 「组织单元」是维格表中描述“空间站”与“成员”之间的关系的一个抽象概念。成员(member)、小组(team)都是一种组织单元。 *创建人必须为成员(member) unitId (string) : 组织单元的ID unitType (number) : 组织单元的类型,1是小组,3是成员 unitName (string) : 组织单元的名称,如果unitType是1,此值为小组名称;如果unitType是3,此值为成员站内昵称", - "en_US": "Returns an array that contains one unit object. The unit is the member that creates the record. (A unit describes the roles in a Space such as a member or a team. \"unitType\" has two values: 1 stands for team; 3 stands for member.)", + "en_US": "Returns an array that contains one unit object. The unit is the member that creates the record.", "zh_HK": "創建此記錄的成員(unit),以數組形式返回 「組織單元」是維格表中描述“空間站”與“成員”之間的關係的一個抽象概念。成員(member)、小組(team)都是一種組織單元。 *創建人必須為成員(member) unitId (string) : 組織單元的ID unitType (number) : 組織單元的類型,1是小組,3是成員 unitName (string) : 組織單元的名稱,如果unitType是1,此值為小組名稱;如果unitType是3,此值為成員站內暱稱" }, "api_panel_type_desc_created_time": { @@ -1049,7 +1076,7 @@ }, "api_panel_type_desc_last_modified_by": { "zh_CN": "最近一次编辑记录/指定字段的成员(unit),以数组形式返回 「组织单元」是维格表中描述“空间站”与“成员”之间的关系的一个抽象概念。成员(member)、小组(team)都是一种组织单元。 *修改人必须为成员(member) unitId (string) : 组织单元的ID unitType (number) : 组织单元的类型,1是小组,3是成员 unitName (string) : 组织单元的名称,如果unitType是1,此值为小组名称;如果unitType是3,此值为成员站内昵称", - "en_US": "Returns an array that contains one unit object. The unit is the last unit that modifies the record or the specified field(s). (A unit describes the roles in a Space such as a member or a team. \"unitType\" has two values: 1 stands for team; 3 stands for member.)", + "en_US": "Returns an array that contains one unit object. The unit is the last unit that modifies the record or the specified field(s). ", "zh_HK": "最近一次編輯記錄/指定字段的成員(unit),以數組形式返回 「組織單元」是維格表中描述“空間站”與“成員”之間的關係的一個抽象概念。成員(member)、小組(team)都是一種組織單元。 *修改人必須為成員(member) unitId (string) : 組織單元的ID unitType (number) : 組織單元的類型,1是小組,3是成員 unitName (string) : 組織單元的名稱,如果unitType是1,此值為小組名稱;如果unitType是3,此值為成員站內暱稱" }, "api_panel_type_desc_last_modified_time": { @@ -1059,17 +1086,17 @@ }, "api_panel_type_desc_link": { "zh_CN": "由多条已关联记录的ID组成的数组 ", - "en_US": "Returns an array of strings. Each string is the ID of a record that is added into the Link field.", + "en_US": "Returns an array of strings. Each string is the ID of a record that is added into the Magic Link field.", "zh_HK": "由多條已關聯記錄的ID組成的數組 " }, "api_panel_type_desc_look_up": { "zh_CN": "A表与B表通过神奇关联字段进行表关联后,可使用此字段对B表的任意字段进行引用,视乎引用方式的不同,而返回不同数据类型的运算值。 如果引用方式选择了「原样引用」,则运算结果的数据类型保持与B表源字段一致; 其他引用方式皆返回数字类型的运算值", - "en_US": "Returns a number, string, or array. The Lookup result is calculated by the system. You can't write into the Formula field by API. ", + "en_US": "Returns a number, string, or array. The Lookup result is calculated by the system. You can't write into the Lookup field by API. ", "zh_HK": "A表與B表通過神奇關聯字段進行表關聯後,可使用此字段對B表的任意字段進行引用,視乎引用方式的不同,而返回不同數據類型的運算值。如果引用方式選擇了「原樣引用」,則運算結果的數據類型保持與B表源字段一致; 其他引用方式皆返回數字類型的運算值" }, "api_panel_type_desc_member": { "zh_CN": "由若干「组织单元(unit)」组成的数组 「组织单元」是维格表中描述“空间站”与“成员”之间的关系的一个抽象概念。成员(member)、小组(team)都是一种组织单元。 id (string) : 组织单元的ID type (number) : 组织单元的类型,1是小组,3是成员 name (string) : 组织单元的名称,如果 type 是1,此值为小组名称;如果 type 是3,此值为成员站内昵称", - "en_US": "Returns an array of unit objects. A unit describes the roles in a Space such as a member or a team. \"type\" has two values: 1 stands for team; 3 stands for member.", + "en_US": "Returns an array of unit objects. A unit describes the roles in a Space such as a member or a team. \"type\" has two values: Team or Member.", "zh_HK": "由若干「組織單元(unit)」組成的數組 「組織單元」是維格表中描述“空間站”與“成員”之間的關係的一個抽象概念。成員(member)、小組(team)都是一種組織單元。 id (string) : 組織單元的ID type (number) : 組織單元的類型,1是小組,3是成員 name (string) : 組織單元的名稱,如果 type 是1,此值為小組名稱;如果 type 是3,此值為成員站內暱稱" }, "api_panel_type_desc_multi_select": { @@ -1094,7 +1121,7 @@ }, "api_panel_type_desc_rating": { "zh_CN": "评分值是 1-9 之间的一个正整数 如果单元格为空或者撤销评分,则记录中不返回此字段!", - "en_US": "Returns a positive number between 1 and 9. The return result will not include this field if the value is empty.", + "en_US": "Returns a positive number between 1 and 10. The return result will not include this field if the value is empty.", "zh_HK": "評分值是 1-9 之間的一個正整數 如果單元格為空或者撤銷評分,則記錄中不返回此字段!" }, "api_panel_type_desc_single_select": { @@ -1114,7 +1141,7 @@ }, "api_panel_type_desc_url": { "zh_CN": "URL 地址(字符串)", - "en_US": "URL address (string)", + "en_US": "Returns URL address (string)", "zh_HK": "URL 地址(字符串)" }, "api_panel_update_records": { @@ -1127,6 +1154,10 @@ "en_US": "Upload attachments", "zh_HK": "上傳文件" }, + "api_param_api_btn_type_error": { + "zh_CN": "参数 apiBtn 只接受 boolean 类型的值", + "en_US": "the parameter apiBtn must be a boolean type" + }, "api_param_attachment_array_type_error": { "zh_CN": "附件类型必须是数组", "en_US": "Attachment type must be array", @@ -1147,11 +1178,19 @@ "en_US": "Attachment token must be string", "zh_HK": "附件 token 必須是字符串" }, + "api_param_basic_tools_type_error": { + "zh_CN": "参数 basicTools 只接受 boolean 类型的值", + "en_US": "the parameter bannerLogo must be a boolean type" + }, "api_param_checkbox_field_type_error": { "zh_CN": "field:{field} 勾选字段必须是布尔值", "en_US": "field:{field} Checkbox field value must be boolean", "zh_HK": "field:{field} 勾選字段必須是布爾值" }, + "api_param_collapsed_type_error": { + "zh_CN": "参数 collapsed 只接受 boolean 类型的值", + "en_US": "the parameter collapsed must be a boolean type" + }, "api_param_currency_field_type_error": { "zh_CN": "field:{field} 货币字段的值必须是数字", "en_US": "field:{field} Currency field value must be a number", @@ -1172,11 +1211,31 @@ "en_US": "field:{field} Email field value must be string", "zh_HK": "field:{field} 郵件字段必須是字符串" }, + "api_param_embed_link_id_not_empty": { + "zh_CN": "参数 linkId 为必填项", + "en_US": "the parameter linkId is required" + }, + "api_param_embed_permission_type_error": { + "zh_CN": "参数 permissionType 必须是以下值之一: readOnly, publicEdit, privateEdit", + "en_US": "the parameter permissionType must be one of the following values: readOnly, publicEdit, privateEdit" + }, + "api_param_form_btn_type_error": { + "zh_CN": "参数 formBtn 只接受 boolean 类型的值", + "en_US": "the parameter formBtn must be a boolean type" + }, "api_param_formula_error": { "zh_CN": "{value}", "en_US": "{value}", "zh_HK": "{value}" }, + "api_param_history_btn_type_error": { + "zh_CN": "参数 historyBtn 只接受 boolean 类型的值", + "en_US": "the parameter historyBtn must be a boolean type" + }, + "api_param_invailid_datasheet_name": { + "zh_CN": "数表 name 不能为空", + "en_US": "datasheet name cannot be empty\n" + }, "api_param_invalid_rating_field": { "zh_CN": "评分字段的值不能小于 0(值只能为 0 到 5 的任一数字)", "en_US": "Rating field value can't be smaller than 0 (only supports 0, 1, 2, 3, 4, or 5)", @@ -1217,11 +1276,23 @@ "en_US": "field:{field} Multiple Select field value must be string", "zh_HK": "field:{field} 多選字段的選項必須是字符串" }, + "api_param_node_id_not_empty_key": { + "zh_CN": "参数 nodeId 为必填项", + "en_US": "the parameter nodeId is required" + }, "api_param_number_field_type_error": { "zh_CN": "field:{field} 数字字段的值必须是数字", "en_US": "field:{field} Number field value must be a number", "zh_HK": "field:{field} 數字字段的值必須是數字" }, + "api_param_payload_banner_logo_type_error": { + "zh_CN": "参数 bannerLogo 只接受 boolean 类型的值", + "en_US": "the parameter bannerLogo must be a boolean type" + }, + "api_param_payload_editable_type_error": { + "zh_CN": "参数 editable 只接受 boolean 类型的值", + "en_US": "the parameter editable must be a boolean type" + }, "api_param_percent_field_type_error": { "zh_CN": "field:{field} 百分比字段的值必须是数字", "en_US": "field:{field} Percent field value must be a number", @@ -1247,11 +1318,19 @@ "en_US": "The record specified by the recordId does not exist", "zh_HK": "recordId 指定的記錄不存在" }, + "api_param_robot_btn_type_error": { + "zh_CN": "参数 robotBtn 只接受 boolean 类型的值", + "en_US": "the parameter robotBtn must be a boolean type" + }, "api_param_select_field_value_type_error": { "zh_CN": "field:{field} 单选字段必须是字符串", "en_US": "field:{field} Single Select field value must be string", "zh_HK": "field:{field} 單選字段必須是字符串" }, + "api_param_share_btn_type_error": { + "zh_CN": "参数 shareBtn 只接受 boolean 类型的值", + "en_US": "the parameter shareBtn must be a boolean type" + }, "api_param_singletext_field_type_error": { "zh_CN": "field:{field} 单行文本字段必须是字符串", "en_US": "field:{field} Single Select field value must be string", @@ -1262,11 +1341,23 @@ "en_US": "The sorted field does not exist", "zh_HK": "sort 參數指定的字段不存在" }, + "api_param_tabbar_type_error": { + "zh_CN": "参数 tabBar 只接受 boolean 类型的值", + "en_US": "the parameter tabBar must be a boolean type" + }, "api_param_text_field_type_error": { "zh_CN": "field:{field} 文本字段必须是字符串", "en_US": "field:{field} Text field value must be string", "zh_HK": "field:{field} 文本字段必須是字符串" }, + "api_param_theme_type_error": { + "zh_CN": "参数 theme 必须是以下值之一: dark, light", + "en_US": "the parameter theme must be one of the following values: dark, light" + }, + "api_param_toolbar_type_error": { + "zh_CN": "参数 toolBar 只接受 boolean 类型的值", + "en_US": "the parameter toolBar must be a boolean type" + }, "api_param_type_error": { "zh_CN": "[{property}]必须是「{value}」类型", "en_US": "{property} must be the \"{value}\" type", @@ -1288,10 +1379,26 @@ "zh_HK": "field:{field} 網址字段必須是字符串" }, "api_param_view_not_exists": { - "zh_CN": "viewId 参数指定的视图不存在", + "zh_CN": "找不到参数 viewID 对应的视图", "en_US": "The specified view does not exist", "zh_HK": "viewId 參數指定的視圖不存在" }, + "api_param_viewid_empty_error": { + "zh_CN": "参数 viewId 的值不能为空", + "en_US": "the parameter viewId should not be empty" + }, + "api_param_viewid_type_error": { + "zh_CN": "参数 viewId 只接受 string 类型的值", + "en_US": "the parameter toolBar must be a string type" + }, + "api_param_viewids_empty_error": { + "zh_CN": "viewIds 不能为空数组", + "en_US": "viewIds should not be empty" + }, + "api_param_widget_btn_type_error": { + "zh_CN": "参数 widgetBtn 只接受 boolean 类型的值", + "en_US": "the parameter widgetBtn must be a boolean type" + }, "api_params_automumber_can_not_operate": { "zh_CN": "自增数字列的值由系统自动生成,不允许编辑", "en_US": "Autonumber field can't be edited", @@ -1368,8 +1475,8 @@ "zh_HK": "sort 參數不能為空" }, "api_params_instance_space_id_error": { - "zh_CN": "spaceId 不能为空", - "en_US": "spaceId can't be null", + "zh_CN": "参数 spaceId 为必填项", + "en_US": "the parameter nodeId is required", "zh_HK": "spaceId 不能為空" }, "api_params_invalid_field_key": { @@ -1417,7 +1524,7 @@ }, "api_params_link_field_records_max_count_error": { "zh_CN": "field:{field} 只能传入一个 recordId,因为神奇关联列已设置不允许多选", - "en_US": "field:{field} The Link field was set to receive one recordId only", + "en_US": "field:{field} The Magic Link field was set to receive one recordId only", "zh_HK": "field:{field} 只能傳入一個 recordId,因為神奇關聯列已設置不允許多選" }, "api_params_lookup_can_not_operate": { @@ -1520,6 +1627,9 @@ "en_US": "Last Edited By field can't be edited", "zh_HK": "修改人列的值由系統自動生成,不允許編輯" }, + "api_params_views_max_count_error": { + "zh_CN": "单次请求删除的视图不能超过 {count} 条" + }, "api_query_params_invalid_fields": { "zh_CN": "传入的 fields 不存在:{fields}", "en_US": "Invalid fields", @@ -1623,18 +1733,87 @@ "api_usage": { "zh_CN": "API 用量", "en_US": "API usage", - "zh_HK": "API 用量" + "zh_HK": "API 用量", + "billing": { + "products": [ + "rec27NiqinItQ" + ] + } }, "api_usage_info": { - "zh_CN": "不同空间站订阅等级拥有不同的 API 用量限制,未购买付费 API 用量包不允许超量,累计用量每月账单日清零", - "en_US": "Spaces with different subscription plans have different times of API calls per month. If you don't purchase the API package, you can't call API excessively. The usage amount is cleared up on the billing day per month.", - "zh_HK": "不同空間站訂閱等級擁有不同的 API 用量限制,未購買付費 API 用量包不允許超量,累計用量每月賬單日清零" + "zh_CN": "不同空间站订阅等级拥有不同的 API 用量限制,当月累计用量下月清零", + "en_US": "Spaces with different subscription plans have different times of API calls per month. If you don't purchase the API package, you can't call API excessively. The usage amount is cleared up on the next monthly.", + "zh_HK": "不同空間站訂閱等級擁有不同的 API 用量限制,当月累計用量下月清零" + }, + "api_view_fieldid_not_exist": { + "zh_CN": "conditions fieldId对应的字段不存在", + "en_US": "the field corresponding to fieldId does not exist" + }, + "api_view_filter_conditions_empty_error": { + "zh_CN": "conditions 不能为空数组", + "en_US": "conditions should not be empty" + }, + "api_view_filter_operator_not_support": { + "zh_CN": "operator 的值和字段不匹配", + "en_US": "the value of operator does not match the field" + }, + "api_view_filter_operator_value_error": { + "zh_CN": "operator 的值必须是以下值之一: is, isNot, contains, doesNotContain, isEmpty, isNotEmpty, isGreater, isGreaterEqual, isLess, isLessEqual, isRepeat", + "en_US": "operator must be one of the following values: is, isNot, contains, doesNotContain, isEmpty, isNotEmpty, isGreater, isGreaterEqual, isLess, isLessEqual, isRepeat" + }, + "api_view_rules_empty_error": { + "zh_CN": "rules 的值不能为空数组", + "en_US": "rules should not be empty" + }, + "api_view_type_error": { + "zh_CN": "type 必须是以下值之一: Grid, Gallery, Kanban, Gantt, Calendar, Architecture", + "en_US": "type must be one of the following values: Grid, Gallery, Gantt, Kanban, Calendar, Architecture" }, "api_your_token": { "zh_CN": "_替换成你的API_Token_", "en_US": "_Paste_Your_API_Token_", "zh_HK": "_替換成你的API_Token_" }, + "apitable_choose_basic": { + "zh_CN": "Start Now", + "en_US": "Start Now" + }, + "apitable_choose_community": { + "zh_CN": "Download", + "en_US": "Download" + }, + "apitable_choose_custom": { + "zh_CN": "Request Trial", + "en_US": "Request Trial" + }, + "apitable_choose_enterprise": { + "zh_CN": "Contact Us", + "en_US": "Contact Us" + }, + "apitable_choose_plus": { + "zh_CN": "Choose Plus", + "en_US": "Choose Plus" + }, + "apitable_choose_pro": { + "zh_CN": "Choose Pro", + "en_US": "Choose Pro" + }, + "apitable_origin_price_by_month": { + "zh_CN": "{\n \"silver\": \"$8\",\n \"gold\": \"$10\"\n}", + "en_US": "{\n \"plus\": \"$18\",\n \"pro\": \"$30\"\n}" + }, + "apitable_origin_price_by_year": { + "zh_CN": "{\n \"silver\": \"$8\",\n \"gold\": \"$10\"\n}", + "en_US": "{\n \"plus\": \"$11\",\n \"pro\": \"$20\"\n}" + }, + "apitable_privatized_deployment_desc": { + "zh_CN": "Manage it yourself", + "en_US": "Manage it yourself" + }, + "apitable_public_cloud_desc": { + "zh_CN": "APITable as a service", + "en_US": "APITable as a service" + }, "app_closed": { "zh_CN": "未开启的应用", "en_US": "Recommended integrations", @@ -1643,84 +1822,71 @@ "app_launch_guide_text_1": { "zh_CN": "千人同时操作一张维格表,高效实时协同", "en_US": "Thousands of members operate one datasheet at the same time, efficient real-time collaboration", - "终端": "APP", "zh_HK": "千人同時操作一張維格表,高效實時協同" }, "app_launch_guide_text_2": { "zh_CN": "动态数据仪表盘,实时关注重点数据", "en_US": "Dynamic data dashboard, focus on key data in real time", - "终端": "APP", "zh_HK": "動態數據儀錶盤,實時關注重點數據" }, "app_launch_guide_text_3": { "zh_CN": "随需而变,全面适配不同场景需求", "en_US": "Change as needed, fully adapt to the needs of different scenarios", - "终端": "APP", "zh_HK": "隨需而變,全面適配不同場景需求" }, "app_launch_guide_text_4": { "zh_CN": "多维视图任意变换,展示个性化数据", "en_US": "Switch multi-dimensional views at your will to display personalized data", - "终端": "APP", "zh_HK": "多維視圖任意變換,展示個性化數據" }, "app_launch_guide_title_1": { "zh_CN": "团队协作", "en_US": "Teamwork", - "终端": "APP", "zh_HK": "團隊協作" }, "app_launch_guide_title_2": { "zh_CN": "数据可视化", "en_US": "Data visualization", - "终端": "APP", "zh_HK": "數據可視化" }, "app_launch_guide_title_3": { "zh_CN": "海量模板", "en_US": "Massive templates", - "终端": "APP", "zh_HK": "海量模板" }, "app_launch_guide_title_4": { "zh_CN": "丰富视图", "en_US": "Multiple Views", - "终端": "APP", "zh_HK": "豐富視圖" }, "app_load_failed": { "zh_CN": "加载失败,请刷新后再试", "en_US": "Loading failed, please refresh and try again", - "终端": "APP", "zh_HK": "加載失敗,請刷新後再試" }, "app_modal_content_policy": { "zh_CN": "感谢你使用维格表的服务,以下政策将帮助你了解维格表收集、使用、保存你的个人信息的情况,以及你所享有的相关权利。\\n请你查阅", "en_US": "Thank you for using our services, the following policies will help you understand the collection, usage, and storage of your personal information, and your related rights. \\nPlease check", - "终端": "APP", "zh_HK": "感謝你使用維格表的服務,以下政策將幫助你了解維格表收集、使用、保存你的個人信息的情況,以及你所享有的相關權利。 \\n請你查閱" }, "app_modal_content_policy_suffix": { "zh_CN": ",点击同意开始接受我们的服务。", "en_US": "Click agree to start receiving our service.", - "终端": "APP", "zh_HK": ",點擊同意開始接受我們的服務。" }, "app_opening": { "zh_CN": "已开启的应用", - "en_US": "Opened application", + "en_US": "Installed application", "zh_HK": "已開啟的應用" }, "app_reload": { "zh_CN": "重新加载", "en_US": "Try loading again", - "终端": "APP", "zh_HK": "重新加載" }, "app_timeout_to_refresh": { "zh_CN": "网络连接超时,请刷新后再试", "en_US": "The network connection timed out, please refresh and try again", - "终端": "APP", "zh_HK": "網絡連接超時,請刷新後再試" }, "application_integration_information": { @@ -1768,11 +1934,6 @@ "en_US": "Use template", "zh_HK": "使用模板" }, - "appoint_permission_desc": { - "zh_CN": "为协作成员配置该维格表的权限,无权限的成员对该表不可见", - "en_US": "Authoize permission of the datasheet to the collaborative members. In that case, members without permission cannot access to it.", - "zh_HK": "為協作成員配置該維格表的權限,無權限的成員對該表不可見" - }, "appoint_permission_tip": { "zh_CN": "只有以下成员可以访问此文件", "en_US": "Only members below have access to this file", @@ -1823,6 +1984,71 @@ "en_US": "Descending", "zh_HK": "選項倒序" }, + "assistant": { + "zh_CN": "维格小助手", + "en_US": "Vikaby assistant", + "zh_HK": "維格小助手" + }, + "assistant_activity_train_camp": { + "zh_CN": "限时福利", + "en_US": "Time-limited welfare", + "zh_HK": "限時福利" + }, + "assistant_beginner_task": { + "zh_CN": "新手任务", + "en_US": "Beginner tasks", + "zh_HK": "新手任務" + }, + "assistant_beginner_task_1_what_is_datasheet": { + "zh_CN": "什么是维格表", + "en_US": "What is vikadata?", + "zh_HK": "什麼是維格表" + }, + "assistant_beginner_task_2_quick_start": { + "zh_CN": "一分钟快速入门", + "en_US": "Vikadata's hierarchy", + "zh_HK": "一分鐘快速入門" + }, + "assistant_beginner_task_3_how_to_use_datasheet": { + "zh_CN": "玩转一张维格表", + "en_US": "How to use a datasheet", + "zh_HK": "玩轉一張維格表" + }, + "assistant_beginner_task_4_share_and_invite": { + "zh_CN": "分享和邀请成员", + "en_US": "Share and invite friends", + "zh_HK": "分享和邀請成員" + }, + "assistant_beginner_task_5_onboarding": { + "zh_CN": "智能引导", + "en_US": "Smart onboarding", + "zh_HK": "智能引導" + }, + "assistant_beginner_task_6_bind_email": { + "zh_CN": "邮箱绑定", + "en_US": "Bind email", + "zh_HK": "郵箱綁定" + }, + "assistant_beginner_task_title1": { + "zh_CN": "欢迎来到维格表", + "en_US": "Welcome to vikadata", + "zh_HK": "歡迎來到維格表" + }, + "assistant_beginner_task_title2": { + "zh_CN": "跟着指引开始你的维格之旅吧~", + "en_US": "Follow the guide and start your journey in vikadata~", + "zh_HK": "跟著指引開始你的維格之旅吧~" + }, + "assistant_hide": { + "zh_CN": "取消悬浮", + "en_US": "Hide Vikaby", + "zh_HK": "取消懸浮" + }, + "assistant_release_history": { + "zh_CN": "历史更新", + "en_US": "Update history", + "zh_HK": "歷史更新" + }, "associated_element": { "zh_CN": "关联元素", "en_US": "Associated element", @@ -1848,6 +2074,21 @@ "en_US": "Select at least one member field", "zh_HK": "至少要選擇一個成員字段" }, + "atlas": { + "zh_CN": "云版本", + "en_US": "Atlas", + "zh_HK": "雲版本", + "billing": { + "products": [ + "recfebaxiTFLu" + ] + } + }, + "atlas_grade_desc": { + "zh_CN": "云版本内的各空间站权益一致,空间站成员享有所处版本的相关权益。", + "en_US": "Consistent benefits across spaces within Atlas,Members in the Spaces of different Atlas enjoy different benefits.", + "zh_HK": "雲版本內的各空間站權益一致,空間站成員享有所處版本的相關權益。" + }, "attachment_capacity_details_entry": { "zh_CN": "查看明细", "en_US": "Details" @@ -1882,7 +2123,7 @@ }, "attachment_capacity_gift_capacity": { "zh_CN": "赠送容量", - "en_US": "Gift capacity" + "en_US": "Gift storage" }, "attachment_capacity_gift_capacity_access_portal": { "zh_CN": "邀请成员获取", @@ -1890,12 +2131,7 @@ }, "attachment_capacity_subscription_capacity": { "zh_CN": "当前套餐容量", - "en_US": "Subscription Capacity" - }, - "atlas": { - "zh_CN": "云版本", - "en_US": "Atlas", - "zh_HK": "雲版本" + "en_US": "Subscription storage" }, "attachment_data": { "zh_CN": "附件数据", @@ -1934,7 +2170,7 @@ }, "audit_add_node_role_detail": { "zh_CN": "添加文件节点权限,将「${unitNames}」设置为「${currentNodeName}」的「${role}」角色", - "en_US": "Add node permission,set「${unitNames}」to「${role}」of 「${currentNodeName}」", + "en_US": "Add file permission,set「${unitNames}」to「${role}」of 「${currentNodeName}」", "zh_HK": "添加文件節點權限,將「${unitNames}」設置為「${currentNodeName}」的「${role}」角色" }, "audit_admin_permission_change_event": { @@ -1969,12 +2205,12 @@ }, "audit_delete_node_role": { "zh_CN": "删除文件节点权限", - "en_US": "Delete node permission", + "en_US": "Delete file permission", "zh_HK": "刪除文件節點權限" }, "audit_delete_node_role_detail": { "zh_CN": "删除文件节点权限,将「${unitNames}」在「${currentNodeName}」的「${role}」角色删除", - "en_US": "Delete node permission,delete「${unitNames}」`s 「${role}」role of 「${currentNodeName}」", + "en_US": "Delete file permission,delete「${unitNames}」`s 「${role}」role of 「${currentNodeName}」", "zh_HK": "刪除文件節點權限,將「${unitNames}」在「${currentNodeName}」的「${role}」角色刪除" }, "audit_delete_template": { @@ -2009,7 +2245,7 @@ }, "audit_disable_node_share": { "zh_CN": "关闭文件节点公开链接", - "en_US": "Close node public link", + "en_US": "Close file public link", "zh_HK": "關閉文件節點公開鏈接" }, "audit_disable_node_share_detail": { @@ -2039,7 +2275,7 @@ }, "audit_enable_node_share": { "zh_CN": "开启文件节点公开链接", - "en_US": "Open node public link", + "en_US": "Open file public link", "zh_HK": "開啟文件節點公開鏈接" }, "audit_enable_node_share_detail": { @@ -2084,7 +2320,7 @@ }, "audit_space_change_event": { "zh_CN": "空间站信息变更", - "en_US": "english", + "en_US": "Space information Changed", "zh_HK": "空間站信息變更" }, "audit_space_complete_delete": { @@ -2124,7 +2360,7 @@ }, "audit_space_entry_workbench_detail": { "zh_CN": "成员${member_name} 进入空间站 ${space_name}", - "en_US": "english", + "en_US": "Member ${member_name} joined the space \n${space_name}", "zh_HK": "成員${member_name} 進入空間站 ${space_name}" }, "audit_space_invite_user": { @@ -2139,17 +2375,17 @@ }, "audit_space_node_copy": { "zh_CN": "复制文件节点", - "en_US": "Duplicate node", + "en_US": "Duplicate file", "zh_HK": "複製文件節點" }, "audit_space_node_copy_detail": { "zh_CN": "复制${nodeType}「${sourceNodeName}」,新文件节点名称为「${currentNodeName}」", - "en_US": "Duplicate ${nodeType} 「${sourceNodeName}」, the new node name is 「${currentNodeName}」", + "en_US": "Duplicate ${nodeType} 「${sourceNodeName}」, the new file's name is 「${currentNodeName}」", "zh_HK": "複製${nodeType}「${sourceNodeName}」,新文件節點名稱為「${currentNodeName}」" }, "audit_space_node_create": { "zh_CN": "创建文件节点", - "en_US": "create node", + "en_US": "Create file", "zh_HK": "創建文件節點" }, "audit_space_node_create_detail": { @@ -2159,7 +2395,7 @@ }, "audit_space_node_delete": { "zh_CN": "删除文件节点", - "en_US": "Delete node", + "en_US": "Delete file", "zh_HK": "刪除文件節點" }, "audit_space_node_delete_detail": { @@ -2169,27 +2405,27 @@ }, "audit_space_node_export": { "zh_CN": "导出数表", - "en_US": "english", + "en_US": "Export datasheet", "zh_HK": "導出數表" }, "audit_space_node_export_detail": { "zh_CN": "成员 ${member_name} 导出表 ${node_name}", - "en_US": "english", + "en_US": "Member ${member_name} exported datsheet ${node_name}", "zh_HK": "成員 ${member_name} 導出表 ${node_name}" }, "audit_space_node_import": { "zh_CN": "导入文件节点", - "en_US": "Import node", + "en_US": "Import file", "zh_HK": "導入文件節點" }, "audit_space_node_import_detail": { "zh_CN": "导入文件节点,名称为 「${nodeName}」", - "en_US": "Import a node named 「${nodeName}」", + "en_US": "Import a file named 「${nodeName}」", "zh_HK": "導入文件節點,名稱為 「${nodeName}」" }, "audit_space_node_move": { "zh_CN": "移动文件节点", - "en_US": "Move node", + "en_US": "Move file", "zh_HK": "移動文件節點" }, "audit_space_node_move_detail": { @@ -2199,7 +2435,7 @@ }, "audit_space_node_rename": { "zh_CN": "重命名文件节点", - "en_US": "Rename node", + "en_US": "Rename file", "zh_HK": "重命名文件節點" }, "audit_space_node_rename_detail": { @@ -2269,12 +2505,12 @@ }, "audit_space_rubbish_node_recover": { "zh_CN": "恢复回收舱节点", - "en_US": "Audit", + "en_US": "Restore files", "zh_HK": "恢復回收艙節點" }, "audit_space_rubbish_node_recover_detail": { "zh_CN": "从回收舱恢复${nodeType},名称为「${currentNodeName}」", - "en_US": "Recover ${nodeType} 「${currentNodeName}」 form the trash", + "en_US": "Restore ${nodeType} 「${currentNodeName}」 form the trash", "zh_HK": "從回收艙恢復${nodeType},名稱為「${currentNodeName}」" }, "audit_space_template_event": { @@ -2294,12 +2530,12 @@ }, "audit_store_share_node": { "zh_CN": "转存文件节点", - "en_US": "Audit", + "en_US": "Save shared file", "zh_HK": "轉存文件節點" }, "audit_store_share_node_detail": { "zh_CN": "将${nodeType}转存到本空间站,名称为 「${nodeName}」", - "en_US": "Restore ${nodeType} to the space,name is 「${nodeName}」", + "en_US": "Restore ${nodeType} to the space, name is 「${nodeName}」", "zh_HK": "將${nodeType}轉存到本空間站,名稱為 「${nodeName}」" }, "audit_update_field_role": { @@ -2314,17 +2550,17 @@ }, "audit_update_node_role": { "zh_CN": "修改文件节点权限", - "en_US": "Modify node permission", + "en_US": "Modify file permission", "zh_HK": "修改文件節點權限" }, "audit_update_node_role_detail": { "zh_CN": "修改文件节点权限,将「${unitNames}」修改为「${currentNodeName}」的「${role}」角色", - "en_US": "Modify node permission,modify「${unitNames}」`s role to「${role}」of 「${currentNodeName}」", + "en_US": "Modify file permission,modify「${unitNames}」`s role to「${role}」of 「${currentNodeName}」", "zh_HK": "修改文件節點權限,將「${unitNames}」修改為「${currentNodeName}」的「${role}」角色" }, "audit_update_node_share_setting": { "zh_CN": "修改文件节点公开链接", - "en_US": "Modify node public link settings", + "en_US": "Modify file public link settings", "zh_HK": "修改文件節點公開鏈接" }, "audit_update_node_share_setting_detail": { @@ -2377,7 +2613,7 @@ }, "audit_work_catalog_share_event": { "zh_CN": "工作目录节点分享事件", - "en_US": "Sharing events of nodes in Explorer", + "en_US": "Sharing events of files in Explorer", "zh_HK": "工作目錄節點分享事件" }, "augmented_views": { @@ -2395,6 +2631,11 @@ "en_US": "Austria", "zh_HK": "奧地利" }, + "auth_server_extensions_login_description_content": { + "zh_CN": "维格统一登录页", + "en_US": "Vikadata Unified Login Page", + "zh_HK": "維格統一登錄頁" + }, "authorize": { "zh_CN": "授权", "en_US": "authorize", @@ -2448,7 +2689,6 @@ "back": { "zh_CN": "返回", "en_US": "Return", - "终端": "APP", "zh_HK": "返回" }, "back_login": { @@ -2523,7 +2763,7 @@ }, "batch_import": { "zh_CN": "批量导入", - "en_US": "Bulk import", + "en_US": "Batch Import", "zh_HK": "批量導入" }, "batch_remove": { @@ -2708,15 +2948,22 @@ }, "bronze_btn_text": { "zh_CN": "立即体验", + "en_US": "Start now", "zh_HK": "立即體驗" }, "bronze_grade": { "zh_CN": "青铜级", "en_US": "Bronze", - "zh_HK": "青銅級" + "zh_HK": "青銅級", + "billing": { + "products": [ + "recXH9UsmC5ZA" + ] + } }, "bronze_grade_desc": { "zh_CN": "适用于刚上手维格表的个人或团队", + "en_US": "For individuals or teams who are new to APITable\n", "zh_HK": "適用於剛上手維格表的個人或團隊" }, "bronze_img": { @@ -2789,6 +3036,11 @@ "en_US": "Submit", "zh_HK": "提交" }, + "button_submit_anonymous": { + "zh_CN": "提交", + "en_US": "Submit", + "zh_HK": "提交" + }, "by_field_id": { "zh_CN": "使用 Field ID", "en_US": "Use field ID", @@ -2805,7 +3057,8 @@ "zh_HK": "按月" }, "by_the_year": { - "zh_CN": "按年" + "zh_CN": "按年", + "en_US": "Yearly" }, "calendar_add_date_time_field": { "zh_CN": "创建日期", @@ -2918,9 +3171,9 @@ "zh_HK": "記錄" }, "calendar_setting": { - "zh_CN": "日历视图设置", - "en_US": "Calendar setting", - "zh_HK": "日曆視圖設置" + "zh_CN": "视图设置", + "en_US": "Settings", + "zh_HK": "視圖設置" }, "calendar_setting_clear_end_time": { "zh_CN": "不使用结束日期", @@ -2959,7 +3212,7 @@ }, "calendar_view_desc": { "zh_CN": "更方便地查看项目进度,安排任务时间", - "en_US": "Use a calendar to arrange tasks and view project schedule", + "en_US": "Use a calendar to arrange tasks and view the project schedule", "zh_HK": "更方便地查看項目進度,安排任務時間" }, "cambodia": { @@ -2979,12 +3232,12 @@ }, "can_duplicate": { "zh_CN": "可另存为副本", - "en_US": "can duplicate", + "en_US": "can save as copy", "zh_HK": "可另存為副本" }, "can_edit": { "zh_CN": "可以编辑", - "en_US": "Editor", + "en_US": "can edit", "zh_HK": "可以編輯" }, "can_manage": { @@ -3114,9 +3367,9 @@ "zh_HK": "空間站容量已超出上限,升級可獲得用量" }, "capacity_limit_email_title": { - "zh_CN": "「{SPACE_NAME}」空间站的附件容量包已超出上限({USAGE}GB/{SPECIFICATION}GB)", - "en_US": "Usage reminders for the number of attachment capacities in the \"${SPACE_NAME}\" space ", - "zh_HK": "「{SPACE_NAME}」空間站的附件容量包已超出上限({USAGE}GB/{SPECIFICATION}GB)" + "zh_CN": "「${SPACE_NAME}」空间站的附件容量的用量提醒", + "en_US": "Usage reminders for the number of attachment capacities in the \"${SPACE_NAME}\" space", + "zh_HK": "「${SPACE_NAME}」空間站的附件容量的用量提醒" }, "capacity_reach_limit": { "zh_CN": "空间站「」容量已达上限", @@ -3453,9 +3706,9 @@ "zh_HK": "配置教程" }, "chart_widget_setting_help_url": { - "zh_CN": "/help/intro-widget-chart/", - "en_US": "/help/intro-widget-chart/", - "zh_HK": "/help/intro-widget-chart/" + "zh_CN": "https://help.vika.cn/docs/guide/intro-widget-chart", + "en_US": "https://help.vika.cn/docs/guide/intro-widget-chart", + "zh_HK": "https://help.vika.cn/docs/guide/intro-widget-chart" }, "check_detail": { "zh_CN": "查看详情", @@ -3478,7 +3731,9 @@ "zh_HK": "請選擇要關聯的維格表" }, "check_more_privileges": { - "zh_CN": "查看更多特权" + "zh_CN": "查看更多特权", + "en_US": "See more privileges", + "zh_HK": "查看更多特權" }, "check_network_status": { "zh_CN": "请检查网络连接", @@ -3613,9 +3868,12 @@ "click_to_agree": { "zh_CN": "点击同意", "en_US": "Click to agree ", - "终端": "APP", "zh_HK": "點擊同意" }, + "click_to_compare_with_detail": { + "zh_CN": "点击查看详细对比", + "en_US": "Detail" + }, "click_to_view": { "zh_CN": "点击查看", "en_US": "Click to view", @@ -3731,7 +3989,7 @@ }, "close_permission_warning_content": { "zh_CN": "所有成员和小组都将会看到该文件,已设置的权限会被清除", - "en_US": "all members and groups will see the file, and the set permissions will be closed at the same time", + "en_US": "all members and teams will see the file, and the set permissions will be closed at the same time", "zh_HK": "所有成员和小组都会看到该文件,已设置的权限会同时关闭" }, "close_public_link_success": { @@ -4036,7 +4294,8 @@ "zh_HK": "交流群" }, "community": { - "zh_CN": "社区版" + "zh_CN": "社区版(COMMING SOON)", + "en_US": "Community" }, "community_and_local_interest": { "zh_CN": "社区与本地兴趣", @@ -4046,10 +4305,16 @@ "community_edition": { "zh_CN": "开源社区版", "en_US": "Community", - "zh_HK": "開源社區版" + "zh_HK": "開源社區版", + "billing": { + "products": [ + "recsqdteTuLPb" + ] + } }, "community_grade_desc": { - "zh_CN": "免费且开源,可以自助安装" + "zh_CN": "免费且开源,可以自助安装(即将开放,敬请期待)", + "en_US": "Free and open source, you can install yourself" }, "comoros": { "zh_CN": "科摩罗", @@ -4102,9 +4367,9 @@ "zh_HK": "配置可使用範圍" }, "confirm": { - "zh_CN": "确认", + "zh_CN": "确定", "en_US": "Confirm", - "zh_HK": "確認" + "zh_HK": "確定" }, "confirm_activate_space_tips": { "zh_CN": "激活「 ${spaceName} 」后即可访问该空间站", @@ -4119,7 +4384,6 @@ "confirm_and_continue": { "zh_CN": "同意并继续", "en_US": "OK", - "终端": "APP", "zh_HK": "同意並繼續" }, "confirm_cancel": { @@ -4248,9 +4512,9 @@ "zh_HK": "聯繫你的專屬客服" }, "contact_us": { - "zh_CN": "联系我们", + "zh_CN": "联系客服", "en_US": "Contact us", - "zh_HK": "聯繫我們" + "zh_HK": "聯繫客服" }, "contact_us_qr_code_desc": { "zh_CN": "使用过程中遇到问题,可以随时获得服务和解答", @@ -4258,7 +4522,9 @@ "zh_HK": "使用過程中遇到問題,可以隨時獲得服務和解答" }, "contact_us_to_join_company_support": { - "zh_CN": "扫码添加我们的客服,申请创业公司扶持计划" + "zh_CN": "扫码添加我们的客服,申请创业公司扶持计划", + "en_US": "Scan the code to add our customer service and apply for the startup support program.", + "zh_HK": "掃碼添加我們的客服,申請創業公司扶持計劃" }, "contacts": { "zh_CN": "通讯录", @@ -4270,6 +4536,11 @@ "en_US": "Contacts", "zh_HK": "通訊錄配置" }, + "contacts_invite_link_template": { + "zh_CN": "【维格表】- ${nickName}邀请你加入「${spaceName}」空间站,为了更好的体验,建议通过电脑浏览器访问。", + "en_US": "From vikadata: ${nickName} invited you to join \"${spaceName}\" Space. Open it via a computer browser for a better experience.", + "zh_HK": "【維格表】- ${nickName}邀請你加入「${spaceName}」空間站,為了更好的體驗,建議通過電腦瀏覽器訪問。" + }, "contacts_management": { "zh_CN": "通讯录管理", "en_US": "Contacts", @@ -4550,6 +4821,11 @@ "en_US": "See documentation", "zh_HK": "查看文檔" }, + "create_widget_success": { + "zh_CN": "创建小程序成功", + "en_US": "Create widget success", + "zh_HK": "創建小程式成功" + }, "create_workspace": { "zh_CN": "创建空间站", "en_US": "Create a Space", @@ -4731,7 +5007,8 @@ "zh_HK": "定制功能開發" }, "custom_grade_desc": { - "zh_CN": "提供代理部署,私有化安装,您可以获得帮助支持和定制化专业服务" + "zh_CN": "提供原厂私有化安装部署,您可以获得企业级咨询服务、专家技术支持和定制化专业服务", + "en_US": "Provides agent deployment, private installation, assistance support and customized professional services" }, "custom_picture": { "zh_CN": "自定义图片", @@ -4750,7 +5027,7 @@ }, "custom_upload_tip": { "zh_CN": "推荐使用 1:1 的方形图片以达到更好的视觉体验", - "en_US": "1:1 square size image is recommended for better visual experience", + "en_US": "A 1:1 square size image is recommended for the better visual experience", "zh_HK": "推薦使用 1:1 的方形圖片以達到更好的視覺體驗" }, "cut_cell_data": { @@ -4778,6 +5055,11 @@ "en_US": "Dashboard", "zh_HK": "儀錶盤" }, + "dashboard_access_denied_help_link": { + "zh_CN": "https://help.vika.cn/docs/guide/intro-dashboard#access-denied", + "en_US": "https://help.vika.cn/docs/guide/intro-dashboard#access-denied", + "zh_HK": "https://help.vika.cn/docs/guide/intro-dashboard#access-denied" + }, "dashboard_editor_label": { "zh_CN": "在「只可阅读」基础上,还可以编辑小程序和分享仪表盘", "en_US": "In addition to \"Read-only\", can also edit widget and share dashboard", @@ -4823,15 +5105,20 @@ "en_US": "Your datasheet has the maximum number of records", "zh_HK": "抱歉,你的維格表超過了 1000 行的限制" }, + "datasheet_choose_field_type": { + "zh_CN": "选择维格列类型", + "en_US": "Select field type", + "zh_HK": "選擇維格列類型" + }, "datasheet_count": { "zh_CN": "文件节点数量", "en_US": "Total Files", "zh_HK": "文件節點數量" }, "datasheet_editor_label": { - "zh_CN": "在「只可更新」基础上,还可以增删视图和记录", - "en_US": "In addition to \"Update-only\", can also add or delete views and records", - "zh_HK": "在「只可更新」基礎上,還可以增刪視圖和記錄" + "zh_CN": "在「只可更新」基础上,还可以删除记录和增删视图", + "en_US": "In addition to \"Update-only\", can also add or delete views and delete records", + "zh_HK": "在「只可更新」基礎上,還可以刪除記錄和增刪視圖" }, "datasheet_exist_widget": { "zh_CN": "此表包含的小程序:", @@ -4849,9 +5136,9 @@ "zh_HK": "你管理的「」空間站檔案節點數上限為 個,當前已創建 個,升級可獲得更高用量。" }, "datasheet_limit_email_title": { - "zh_CN": "「{SPACE_NAME}」空间站的维格表数已超出上限({USAGE}/{SPECIFICATION})", - "en_US": "Usage reminders for the number of nodes in the \"${SPACE_NAME}\" space ", - "zh_HK": "「{SPACE_NAME}」空間站的維格表數已超出上限({USAGE}/{SPECIFICATION})" + "zh_CN": "「${SPACE_NAME}」空间站的文件节点数的用量提醒", + "en_US": "Usage reminders for the number of nodes in the \"${SPACE_NAME}\" space", + "zh_HK": "「${SPACE_NAME}」空間站的檔案節點數的用量提醒" }, "datasheet_manager_label": { "zh_CN": "在「可以编辑」基础上,还可以配置字段和表格", @@ -4874,9 +5161,9 @@ "zh_HK": "空間站的「」維格表記錄數已超出上限(/),升級可獲得更高用量" }, "datasheet_record_limit_email_title": { - "zh_CN": "「{SPACE_NAME}」空间站的「{NODE_NAME}」维格表记录数已超出上限({USAGE}/{SPECIFICATION})", - "en_US": "Usage reminders for the number of records in the \"${NODE_NAME}\" datasheet ", - "zh_HK": "「{SPACE_NAME}」空間站的「{NODE_NAME}」維格表記錄數已超出上限({USAGE}/{SPECIFICATION})" + "zh_CN": "「${NODE_NAME}」维格表记录数的用量提醒", + "en_US": "Usage reminders for the number of records in the \"${NODE_NAME}\" datasheet", + "zh_HK": "「${NODE_NAME}」維格錶記錄數的用量提醒" }, "date_after_or_equal": { "zh_CN": "晚于等于", @@ -5029,9 +5316,9 @@ "zh_HK": "各獲得 1000 V 幣" }, "default_link_join_tip": { - "zh_CN": "通过此邀请方式加入空间站将会默认获得该表的「可编辑」权限", + "zh_CN": "通过此邀请方式加入空间站将会默认获得该表的「只可更新」权限", "en_US": "Members who join the space through the invite link will be granted \"update-only\" access to the node", - "zh_HK": "通過此邀請方式加入空間站將會默認獲得該表的「可編輯」權限" + "zh_HK": "通過此邀請方式加入空間站將會默認獲得該表的「只可更新」權限" }, "default_picture": { "zh_CN": "默认图片", @@ -5193,12 +5480,12 @@ "en_US": "After confirmation, the member will be removed from the current role" }, "delete_role_member_success": { - "zh_CN": "移除成功", + "zh_CN": "移出成功", "en_US": "Removed successfully" }, "delete_role_member_title": { - "zh_CN": "移除角色", - "en_US": "Remove role" + "zh_CN": "移出该角色", + "en_US": "Remove" }, "delete_role_success_message": { "zh_CN": "角色删除成功", @@ -5206,7 +5493,7 @@ }, "delete_role_warning_content": { "zh_CN": "你需要将该角色内的成员都移出组后,才能进行删除操作", - "en_US": "You need to move all members in this role out of the group before deleting" + "en_US": "You need to move all members in this role out of the teams before deleting" }, "delete_role_warning_title": { "zh_CN": "无法删除", @@ -5366,6 +5653,26 @@ "en_US": "Click \"+\" to generate the token ", "zh_HK": "請點擊“+”手動生成 Token" }, + "devtool_apply_backup_data": { + "zh_CN": "备份数据恢复", + "en_US": "Apply Backup Data" + }, + "devtool_batch_delete_node": { + "zh_CN": "批量删除目录树", + "en_US": "Batch Delete Node" + }, + "devtool_more": { + "zh_CN": "更多", + "en_US": "more" + }, + "devtool_open_eruda": { + "zh_CN": "开启eruda调试工具", + "en_US": "Open Eruda" + }, + "devtool_test_functions": { + "zh_CN": "测试功能体验", + "en_US": "Test Functions" + }, "dingding_bind": { "zh_CN": "请使用钉钉扫码绑定", "en_US": "Please use the DINGTALK scanning to bind", @@ -5403,7 +5710,7 @@ }, "dingtalk_app_desc": { "zh_CN": "

成功创建自建应用后,请完成空间站绑定,绑定后你的企业可以在钉钉上使用维格表。

\n\n

在钉钉上使用维格表自建应用,你可以

\n\n
  • 免登录,直接在钉钉工作台进入维格表空间站,访问空间站数据
  • 通过钉钉,收取空间站里的通知
  • 在钉钉设置应用可使用范围设置,并自动同步钉钉的通讯录
", - "en_US": "

After opening the app, please complete the space station bundle, and after bundling your team can use Vikadata on DingTalk.

\n\n

With Vikadata Assistant on DingTalk, you can:

\n\n
  • Access Vikadata's space station and access space station data directly on DingTalk without logging in;
  • Collect notifications from the space station through DingTalk;
  • Set app visibility in DingTalk and automatically sync DingTalk organization structure;
", + "en_US": "

After installing the integration, please complete the space bundle, and after bundling, your team can use Vikadata on DingTalk.

\n\n

With Vikadata Assistant on DingTalk, you can:

\n\n
  • Access Vikadata's space and access space data directly on DingTalk without logging in;
  • Receive notifications from the space through DingTalk;
  • Set app visibility on DingTalk and automatically sync DingTalk's organization structure;
", "zh_HK": "

成功創建自建應用後,請完成空間站綁定,綁定後你的企業可以在釘釘上使用維格表。

\n\n

在釘釘上使用維格表自建應用,你可以

\n\n
  • 免登錄,直接在釘釘工作台進入維格表空間站,訪問空間站數據
  • 通過釘釘,收取空間站裡的通知
  • 在釘釘設置應用可使用範圍設置,並自動同步釘釘的通訊錄
" }, "dingtalk_app_intro": { @@ -5419,7 +5726,12 @@ "dingtalk_base": { "zh_CN": "钉钉基础版", "en_US": "Basic Plan with DingTalk", - "zh_HK": "釘釘基礎版" + "zh_HK": "釘釘基礎版", + "billing": { + "products": [ + "recOH9K5WwU8h" + ] + } }, "dingtalk_basic": { "zh_CN": "钉钉基础版", @@ -5433,7 +5745,7 @@ }, "dingtalk_bind_space_tips": { "zh_CN": "操作会先移除空间站「 ${spaceName} 」现存的所有成员和小组,然后再拉取钉钉企业的组织架构,请管理员知悉并做好准备工作。", - "en_US": "The operation will firstly remove all existing members and groups of the \"${spaceName}\" Space, and then get the information from the organizational structure of the DingTalk enterprise. Please inform the admin and be prepared.", + "en_US": "The operation will firstly remove all existing members and teams of the \"${spaceName}\" Space, and then get the information from the organizational structure of the DingTalk enterprise. Please inform the admin and be prepared.", "zh_HK": "操作會先移除空間站「 ${spaceName} 」現存的所有成員和小組,然後再拉取釘釘企業的組織架構,請管理員知悉並做好準備工作。" }, "dingtalk_change_admin_reject_msg": { @@ -5442,9 +5754,9 @@ "zh_HK": "正通過釘釘集成管理通訊錄,暫不支持在空間站內更換主管理員" }, "dingtalk_change_admin_reject_tips": { - "zh_CN": "该成员不在应用可见范围内,请调整后再进行更换操作 如何调整?", - "en_US": "This member is out of the available range of the application. After modifying it, you can transfer the Space. How to modify it?", - "zh_HK": "該成員不在應用可見範圍內,請調整後再進行更換操作 如何調整? " + "zh_CN": "该成员不在应用可见范围内,请调整后再进行更换操作 如何调整?", + "en_US": "This member is out of the available range of the application. After modifying it, you can transfer the Space. How to modify it?", + "zh_HK": "該成員不在應用可見範圍內,請調整後再進行更換操作 如何調整? " }, "dingtalk_da": { "zh_CN": "钉钉搭", @@ -5459,15 +5771,26 @@ "dingtalk_enterprise": { "zh_CN": "钉钉旗舰版", "en_US": "Ultimate Plan with DingTalk", - "zh_HK": "釘釘旗艦版" + "zh_HK": "釘釘旗艦版", + "billing": { + "products": [ + "rec9D00DpJlS6" + ] + } }, "dingtalk_grade_desc": { - "zh_CN": "空间站等级分为钉钉基础版(永久免费)、钉钉标准版和钉钉企业版。不同等级的空间站享有不同的权益,空间站成员享有所处等级空间站的相关权益", - "en_US": "Spaces have three kinds of billing plans: Basic Plan with DingTalk(permanently free), Standard Plan with DingTalk and Enterprise Plan with DingTalk. Members in the Spaces of different billing plans enjoy different rights and benefits.", + "zh_CN": "空间站等级分为钉钉基础版(永久免费)、钉钉标准版、钉钉专业版和钉钉企业版。不同等级的空间站享有不同的权益,空间站成员享有所处等级空间站的相关权益", + "en_US": "Spaces have three kinds of billing plans: Basic Plan with DingTalk(permanently free), Standard Plan with DingTalk \n,Profession Plan with DingTalk and Enterprise Plan with DingTalk. Members in the Spaces of different billing plans enjoy different rights and benefits.", "zh_HK": "空間站等級分為釘釘基礎版(永久免費)、釘釘標準版和釘釘企業版。不同等級的空間站享有不同的權益,空間站成員享有所處等級空間站的相關權益" }, + "dingtalk_isv_integration_single_record_comment_mentioned": { + "zh_CN": "b4ac28623fd5493dbc5f995bb1cc17c7", + "en_US": "b4ac28623fd5493dbc5f995bb1cc17c7", + "zh_HK": "b4ac28623fd5493dbc5f995bb1cc17c7" + }, "dingtalk_isv_integration_single_record_member_mention": { "zh_CN": "5994e97c8766482bb971e6253ad7f6f1", + "en_US": "5994e97c8766482bb971e6253ad7f6f1", "zh_HK": "5994e97c8766482bb971e6253ad7f6f1" }, "dingtalk_isv_integration_social_task_reminder": { @@ -5477,12 +5800,19 @@ }, "dingtalk_isv_integration_subscribed_record_cell_updated": { "zh_CN": "f33cf06b321a4348ba5063355b125184", + "en_US": "f33cf06b321a4348ba5063355b125184", "zh_HK": "f33cf06b321a4348ba5063355b125184" }, "dingtalk_isv_integration_subscribed_record_commented": { "zh_CN": "596ad27391d74630b7df029f940bb887", + "en_US": "596ad27391d74630b7df029f940bb887", "zh_HK": "596ad27391d74630b7df029f940bb887" }, + "dingtalk_isv_production_single_record_comment_mentioned": { + "zh_CN": "8f41773b6eb54d6bba9d82392956dea1", + "en_US": "8f41773b6eb54d6bba9d82392956dea1", + "zh_HK": "8f41773b6eb54d6bba9d82392956dea1" + }, "dingtalk_isv_production_single_record_member_mention": { "zh_CN": "dc79d4fb0f554ff0af08af4c1f04caa6", "en_US": "dc79d4fb0f554ff0af08af4c1f04caa6", @@ -5501,6 +5831,11 @@ "en_US": "383ba579062a4f25a5d933201f466c48", "zh_HK": "383ba579062a4f25a5d933201f466c48" }, + "dingtalk_isv_staging_single_record_comment_mentioned": { + "zh_CN": "d81ea943488b41ebb42fdeacfb42d0ef", + "en_US": "d81ea943488b41ebb42fdeacfb42d0ef", + "zh_HK": "d81ea943488b41ebb42fdeacfb42d0ef" + }, "dingtalk_isv_staging_single_record_member_mention": { "zh_CN": "76a69635e123402db90f7f2cfbb496b9", "en_US": "76a69635e123402db90f7f2cfbb496b9", @@ -5511,8 +5846,9 @@ "zh_HK": "7516cb26bca94deaa80e0e7acb8aee72" }, "dingtalk_isv_staging_subscribed_record_commented": { - "zh_CN": "f7f02e969f634f87aac940b7d0ff780e", - "zh_HK": "f7f02e969f634f87aac940b7d0ff780e" + "zh_CN": "d81ea943488b41ebb42fdeacfb42d0ef", + "en_US": "d81ea943488b41ebb42fdeacfb42d0ef", + "zh_HK": "d81ea943488b41ebb42fdeacfb42d0ef" }, "dingtalk_isv_staging_task_reminder": { "zh_CN": "0f8deadd7e6f4b0d9129fdd45ca1a8f9", @@ -5545,24 +5881,27 @@ "zh_HK": "你的企業人數較多,正在同步通訊錄,
\n同步成功後,你將收到使用通知,屆時可登錄使用維格表" }, "dingtalk_org_manage_reject_msg": { - "zh_CN": "正通过钉钉集成管理通讯录,如需邀请成员请前往钉钉管理后台 如何邀请?", - "en_US": "This Space's contacts are managed by the DingTalk integration. Please go to the DingTalk admin backend to invite members. How to invite?", - "zh_HK": "正通過釘釘集成管理通訊錄,如需邀請成員請前往釘釘管理後台 如何邀請? " + "zh_CN": "正通过钉钉集成管理通讯录,如需邀请成员请前往钉钉管理后台 如何邀请?", + "en_US": "This Space's contacts are managed by the DingTalk integration. Please go to the DingTalk admin backend to invite members. How to invite?", + "zh_HK": "正通過釘釘集成管理通訊錄,如需邀請成員請前往釘釘管理後台 如何邀請? " }, "dingtalk_profession": { "zh_CN": "钉钉专业版", "en_US": "Pro Plan with DingTalk", - "zh_HK": "釘釘專業版" + "zh_HK": "釘釘專業版", + "billing": { + "products": [ + "recQCcAQAUyRh" + ] + } + }, + "dingtalk_single_record_member_comment_title": { + "zh_CN": "评论通知" }, "dingtalk_single_record_member_mention_title": { "zh_CN": "🔔成员通知", "en_US": "🔔 You're mentioned in a record", - "zh_HK": "🔔成員通知", - "notifications": { - "social_templates copy": [ - "rec8zOy6kD7Xr" - ] - } + "zh_HK": "🔔成員通知" }, "dingtalk_social_deactivate_tip": { "zh_CN": "如需停用应用,请前往「钉钉企业管理后台 > 工作台 > 维格表应用」进行操作", @@ -5577,7 +5916,12 @@ "dingtalk_standard": { "zh_CN": "钉钉标准版", "en_US": "Standard Plan with DingTalk", - "zh_HK": "釘釘標準版" + "zh_HK": "釘釘標準版", + "billing": { + "products": [ + "recbK25vn1TV4" + ] + } }, "dingtalk_sync_address_modal_content": { "zh_CN": "手动同步通讯录后,需重新为成员和小组指定相关权限,请谨慎操作", @@ -5646,7 +5990,6 @@ "disagree_and_exit": { "zh_CN": "不同意并退出应用", "en_US": "Cancel & Exit", - "终端": "APP", "zh_HK": "不同意並退出應用" }, "disconnect_from_the_server": { @@ -5734,6 +6077,10 @@ "en_US": "Move to the cell below", "zh_HK": "向下移動一個單元格" }, + "downgrade": { + "zh_CN": "Downgrade", + "en_US": "Downgrade" + }, "download": { "zh_CN": "下载", "en_US": "Download", @@ -5932,9 +6279,23 @@ "email_placeholder": { "zh_CN": "请输入邮箱", "en_US": "Enter email address", - "终端": "APP", "zh_HK": "請輸入郵箱" }, + "embed_error_page_help": { + "zh_CN": "点击了解失效原因", + "en_US": "Learn more", + "zh_HK": "點擊了解失效原因" + }, + "embed_fail_og_description_content": { + "zh_CN": "该嵌入的公开链接已被关闭,暂时无法访问", + "en_US": "The public link for this embed has been disabled and is temporarily unavailable", + "zh_HK": "該嵌入的公開鏈接已被關閉,暫時無法訪問" + }, + "embed_failed": { + "zh_CN": "嵌入链接已失效, ", + "en_US": "Embed link is unavailable, ", + "zh_HK": "嵌入鏈接已失效," + }, "emoji_activity": { "zh_CN": "活动和事件", "en_US": "Activities", @@ -6011,9 +6372,9 @@ "zh_HK": "暫無儀錶盤,${action}" }, "empty_data": { - "zh_CN": "空白数据", - "en_US": "No data", - "zh_HK": "空白數據" + "zh_CN": "空数据", + "en_US": "Empty data", + "zh_HK": "空數據" }, "empty_datasheet": { "zh_CN": "新建空白维格表", @@ -6121,9 +6482,9 @@ "zh_HK": "兌換成功,獲得 ${amount} 個 V 幣" }, "entered_a_valid_redemption_code_info": { - "zh_CN": "特别提示:V 币为平台积分,用于兑换平台服务和内容,点击查看更多介绍。", - "en_US": "Note: V coins are users' points in the platform, used to purchase services and contents. Learn more", - "zh_HK": "特別提示:V 幣為平台積分,用於兌換平台服務和內容,點擊查看更多介紹。" + "zh_CN": "特别提示:V 币为平台积分,用于兑换平台服务和内容,点击查看更多介绍。", + "en_US": "Note: V coins are users' points in the platform, used to purchase services and contents. Learn more", + "zh_HK": "特別提示:V 幣為平台積分,用於兌換平台服務和內容,點擊查看更多介紹。" }, "entered_the_wrong_redemption_code": { "zh_CN": "请输入 8 位兑换码", @@ -6133,11 +6494,16 @@ "enterprise": { "zh_CN": "企业级", "en_US": "Enterprise", - "zh_HK": "企業級" + "zh_HK": "企業級", + "billing": { + "products": [ + "recdMa9qkW2zC" + ] + } }, "enterprise_edition": { "zh_CN": "企业版", - "en_US": "Enterprise Edition", + "en_US": "Enterprise", "zh_HK": "企業版" }, "enterprise_third_app": { @@ -6242,7 +6608,7 @@ }, "error_email_empty": { "zh_CN": "邮箱不能为空", - "en_US": "Empty email address", + "en_US": "Enter email address", "zh_HK": "郵箱不能為空" }, "error_field_not_exist": { @@ -6515,6 +6881,9 @@ "en_US": "Exclusive V+ consultant", "zh_HK": "專屬 V+ 顧問" }, + "exist_experience": { + "zh_CN": "退出体验" + }, "exits_space": { "zh_CN": "退出空间", "en_US": "Exit space", @@ -6522,7 +6891,7 @@ }, "expand": { "zh_CN": "展开", - "en_US": "Unhide", + "en_US": "Expand", "zh_HK": "展開" }, "expand_activity": { @@ -6532,7 +6901,7 @@ }, "expand_all_field_desc": { "zh_CN": "显示本表的列描述", - "en_US": "Show the field description", + "en_US": "Show field description", "zh_HK": "顯示本表的列描述" }, "expand_all_group": { @@ -6600,6 +6969,9 @@ "en_US": "Expand group/subgroup", "zh_HK": "展開該組的子分組" }, + "experience_test_function": { + "zh_CN": "正在体验 ${testFunctions}" + }, "expiration": { "zh_CN": "到期时间:${date}", "en_US": " Expiration date: ${date}", @@ -6640,6 +7012,9 @@ "en_US": "Powered by", "zh_HK": "提供技術支持" }, + "export_current_preview_view_data": { + "zh_CN": "导出当前视图数据" + }, "export_gantt_button_tips": { "zh_CN": "导出为图片", "en_US": "Export to a .png file", @@ -6766,7 +7141,12 @@ "feishu_base": { "zh_CN": "飞书基础版", "en_US": "Basic Plan with Lark", - "zh_HK": "飛書基礎版" + "zh_HK": "飛書基礎版", + "billing": { + "products": [ + "recpkHHQv2EXY" + ] + } }, "feishu_bind_space_btn": { "zh_CN": "绑定空间站", @@ -6784,9 +7164,9 @@ "zh_HK": "配置可用性狀態" }, "feishu_bind_space_err": { - "zh_CN": "当前空间站成员数上限为 ${count} 人,如想提高上限, 请联系我们", - "en_US": "Your Space can have a maximum of ${count} members. Contact us if you want to invite more. ", - "zh_HK": "當前空間站成員數上限為 ${count} 人,如想提高上限, 請聯繫我們" + "zh_CN": "当前空间站成员数上限为 ${count} 人,如想提高上限, 请联系我们", + "en_US": "Your Space can have a maximum of ${count} members. Contact us if you want to invite more. ", + "zh_HK": "當前空間站成員數上限為 ${count} 人,如想提高上限, 請聯繫我們" }, "feishu_bind_space_need_upgrade": { "zh_CN": "选择的空间站成员数上限为${maxSeat}人,无法同步${maxCount}人的飞书企业,提升空间站人数上限请提交需求。", @@ -6800,7 +7180,7 @@ }, "feishu_bind_space_tips": { "zh_CN": "操作会先移除空间站「 ${spaceName} 」现存的所有成员和小组,然后再拉取飞书企业的组织架构,请管理员知悉并做好准备工作。", - "en_US": "The operation will firstly remove all existing members and groups of the \"${spaceName}\" Space, and then get the information from the organizational structure of the Lark enterprise. Please inform the admin and be prepared.", + "en_US": "The operation will firstly remove all existing members and teams of the \"${spaceName}\" Space, and then get the information from the organizational structure of the Lark enterprise. Please inform the admin and be prepared.", "zh_HK": "操作會先移除空間站「 ${spaceName} 」現存的所有成員和小組,然後再拉取飛書企業的組織架構,請管理員知悉並做好準備工作。" }, "feishu_bind_user_subTitle": { @@ -6856,12 +7236,17 @@ "feishu_enterprise": { "zh_CN": "飞书旗舰版", "en_US": "Ultimate Plan with Lark", - "zh_HK": "飛書旗艦版" + "zh_HK": "飛書旗艦版", + "billing": { + "products": [ + "rec6lS5Wwzhvo" + ] + } }, "feishu_grade_desc": { - "zh_CN": "空间站等级分为飞书基础版(永久免费)、飞书标准版和飞书旗舰版。不同等级的空间站享有不同的权益,空间站成员享有所处等级空间站的相关权益", - "en_US": "Spaces have three kinds of billing plans: Basic Plan with Lark(permanently free), Standard Plan with Lark and Ultimate Plan with Lark. Members in the Spaces of different billing plans enjoy different rights and benefits.", - "zh_HK": "空間站等級分為飛書基礎版(永久免費)、飛書標準版和飛書旗艦版。不同等級的空間站享有不同的權益,空間站成員享有所處等級空間站的相關權益" + "zh_CN": "空间站等级分为飞书基础版(永久免费)、飞书标准版、飞书专业版和飞书旗舰版。不同等级的空间站享有不同的权益,空间站成员享有所处等级空间站的相关权益", + "en_US": "Spaces have three kinds of billing plans: Basic Plan with Lark(permanently free), Standard Plan with Lark ,Profession Plan with Lark and Ultimate Plan with Lark. Members in the Spaces of different billing plans enjoy different rights and benefits.", + "zh_HK": "空間站等級分為飛書基礎版(永久免費)、飛書標準版、飛書專業版和飛書旗艦版。不同等級的空間站享有不同的權益,空間站成員享有所處等級空間站的相關權益" }, "feishu_manage_address_reject_msg": { "zh_CN": "成员列表会实时跟飞书通讯录的组织架构保持同步,请在飞书侧进行此操作(企业管理 > 添加团队成员)", @@ -6896,7 +7281,12 @@ "feishu_profession": { "zh_CN": "飞书专业版", "en_US": "Pro Plan with Lark", - "zh_HK": "飛書專業版" + "zh_HK": "飛書專業版", + "billing": { + "products": [ + "recSdXiEapc6R" + ] + } }, "feishu_space_list_item_tag_info": { "zh_CN": "飞书企业绑定中", @@ -6906,7 +7296,12 @@ "feishu_standard": { "zh_CN": "飞书标准版", "en_US": "Standard Plan with Lark", - "zh_HK": "飛書標準版" + "zh_HK": "飛書標準版", + "billing": { + "products": [ + "rec99ArYTJ3sp" + ] + } }, "feishu_upgrade_guidance": { "zh_CN": "请联系你的飞书管理员在飞书应用后台找到「vika维格表」进行付费升级", @@ -6920,12 +7315,12 @@ }, "field_circular_err": { "zh_CN": "选择的引用列会导致循环引用,请修改。", - "en_US": "The selected column will cause a circular reference. Please correct.", + "en_US": "The selected field will cause a circular reference. Please correct.", "zh_HK": "選擇的引用列會導致循環引用,請修改。" }, "field_configuration_err": { "zh_CN": "本列有配置错误,请检查公式、引用的列或筛选条件。", - "en_US": "There is a configuration error in this column. Please check the formula or the configuration used by the column.", + "en_US": "There is a configuration error in this field. Please check the formula or the configuration used by the field.", "zh_HK": "本列有配置錯誤,請檢查公式、引用的列或篩選條件。" }, "field_configuration_numerical_value_format": { @@ -7084,114 +7479,114 @@ "zh_HK": "列頭配置" }, "field_help_attachment": { - "zh_CN": "/help/manual-field-attachment/", - "en_US": "/help/manual-field-attachment/", - "zh_HK": "/help/manual-field-attachment/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-field-attachment", + "en_US": "https://help.vika.cn/docs/guide/manual-field-attachment", + "zh_HK": "https://help.vika.cn/docs/guide/manual-field-attachment" }, "field_help_autonumber": { - "zh_CN": "/help/manual-autonumber/", - "en_US": "/help/manual-autonumber/", - "zh_HK": "/help/manual-autonumber/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-autonumber", + "en_US": "https://help.vika.cn/docs/guide/manual-autonumber", + "zh_HK": "https://help.vika.cn/docs/guide/manual-autonumber" }, "field_help_checkbox": { - "zh_CN": "/help/manual-field-checkbox/", - "en_US": "/help/manual-field-checkbox/", - "zh_HK": "/help/manual-field-checkbox/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-field-checkbox", + "en_US": "https://help.vika.cn/docs/guide/manual-field-checkbox", + "zh_HK": "https://help.vika.cn/docs/guide/manual-field-checkbox" }, "field_help_created_by": { - "zh_CN": "/help/manual-createdby/", - "en_US": "/help/manual-createdby/", - "zh_HK": "/help/manual-createdby/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-createdby", + "en_US": "https://help.vika.cn/docs/guide/manual-createdby", + "zh_HK": "https://help.vika.cn/docs/guide/manual-createdby" }, "field_help_created_time": { - "zh_CN": "/help/manual-createdtime/", - "en_US": "/help/manual-createdtime/", - "zh_HK": "/help/manual-createdtime/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-createdtime", + "en_US": "https://help.vika.cn/docs/guide/manual-createdtime", + "zh_HK": "https://help.vika.cn/docs/guide/manual-createdtime" }, "field_help_currency": { - "zh_CN": "/help/manual-field-currency/", - "en_US": "/help/manual-field-currency/", - "zh_HK": "/help/manual-field-currency/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-field-currency", + "en_US": "https://help.vika.cn/docs/guide/manual-field-currency", + "zh_HK": "https://help.vika.cn/docs/guide/manual-field-currency" }, "field_help_datetime": { - "zh_CN": "/help/manual-field-datetime/", - "en_US": "/help/manual-field-datetime/", - "zh_HK": "/help/manual-field-datetime/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-field-datetime", + "en_US": "https://help.vika.cn/docs/guide/manual-field-datetime", + "zh_HK": "https://help.vika.cn/docs/guide/manual-field-datetime" }, "field_help_email": { - "zh_CN": "/help/manual-field-email/", - "en_US": "/help/manual-field-email/", - "zh_HK": "/help/manual-field-email/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-field-email", + "en_US": "https://help.vika.cn/docs/guide/manual-field-email", + "zh_HK": "https://help.vika.cn/docs/guide/manual-field-email" }, "field_help_formula": { - "zh_CN": "/help/manual-formula-field-overview/", - "en_US": "/help/manual-formula-field-overview/", - "zh_HK": "/help/manual-formula-field-overview/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-formula-field-overview", + "en_US": "https://help.vika.cn/docs/guide/manual-formula-field-overview", + "zh_HK": "https://help.vika.cn/docs/guide/manual-formula-field-overview" }, "field_help_last_modified_by": { - "zh_CN": "/help/manual-lastmodifiedby/", - "en_US": "/help/manual-lastmodifiedby/", - "zh_HK": "/help/manual-lastmodifiedby/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-lastmodifiedby", + "en_US": "https://help.vika.cn/docs/guide/manual-lastmodifiedby", + "zh_HK": "https://help.vika.cn/docs/guide/manual-lastmodifiedby" }, "field_help_last_modified_time": { - "zh_CN": "/help/manual-lastmodifiedtime/", - "en_US": "/help/manual-lastmodifiedtime/", - "zh_HK": "/help/manual-lastmodifiedtime/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-lastmodifiedtime", + "en_US": "https://help.vika.cn/docs/guide/manual-lastmodifiedtime", + "zh_HK": "https://help.vika.cn/docs/guide/manual-lastmodifiedtime" }, "field_help_link": { - "zh_CN": "/help/manual-field-link/", - "en_US": "/help/manual-field-link/", - "zh_HK": "/help/manual-field-link/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-field-link", + "en_US": "https://help.vika.cn/docs/guide/manual-field-link", + "zh_HK": "https://help.vika.cn/docs/guide/manual-field-link" }, "field_help_lookup": { - "zh_CN": "/help/manual-field-lookup/", - "en_US": "/help/manual-field-lookup/", - "zh_HK": "/help/manual-field-lookup/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-field-lookup", + "en_US": "https://help.vika.cn/docs/guide/manual-field-lookup", + "zh_HK": "https://help.vika.cn/docs/guide/manual-field-lookup" }, "field_help_member": { - "zh_CN": "/help/manual-field-member/", - "en_US": "/help/manual-field-member/", - "zh_HK": "/help/manual-field-member/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-filed-member", + "en_US": "https://help.vika.cn/docs/guide/manual-filed-member", + "zh_HK": "https://help.vika.cn/docs/guide/manual-filed-member" }, "field_help_multi_select": { - "zh_CN": "/help/manual-field-select/", - "en_US": "/help/manual-field-select/", - "zh_HK": "/help/manual-field-select/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-field-select", + "en_US": "https://help.vika.cn/docs/guide/manual-field-select", + "zh_HK": "https://help.vika.cn/docs/guide/manual-field-select" }, "field_help_number": { - "zh_CN": "/help/manual-field-number/", - "en_US": "/help/manual-field-number/", - "zh_HK": "/help/manual-field-number/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-field-number", + "en_US": "https://help.vika.cn/docs/guide/manual-field-number", + "zh_HK": "https://help.vika.cn/docs/guide/manual-field-number" }, "field_help_percent": { - "zh_CN": "/help/manual-field-percent/", - "en_US": "/help/manual-field-percent/", - "zh_HK": "/help/manual-field-percent/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-field-percent", + "en_US": "https://help.vika.cn/docs/guide/manual-field-percent", + "zh_HK": "https://help.vika.cn/docs/guide/manual-field-percent" }, "field_help_phone": { - "zh_CN": "/help/manual-field-phone/", - "en_US": "/help/manual-field-phone/", - "zh_HK": "/help/manual-field-phone/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-field-phone", + "en_US": "https://help.vika.cn/docs/guide/manual-field-phone", + "zh_HK": "https://help.vika.cn/docs/guide/manual-field-phone" }, "field_help_rating": { - "zh_CN": "/help/manual-field-rating/", - "en_US": "/help/manual-field-rating/", - "zh_HK": "/help/manual-field-rating/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-field-rating", + "en_US": "https://help.vika.cn/docs/guide/manual-field-rating", + "zh_HK": "https://help.vika.cn/docs/guide/manual-field-rating" }, "field_help_single_select": { - "zh_CN": "/help/manual-field-select/", - "en_US": "/help/manual-field-select/", - "zh_HK": "/help/manual-field-select/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-field-select", + "en_US": "https://help.vika.cn/docs/guide/manual-field-select", + "zh_HK": "https://help.vika.cn/docs/guide/manual-field-select" }, "field_help_single_text": { - "zh_CN": "/help/single-line-text/", - "en_US": "/help/single-line-text/", - "zh_HK": "/help/single-line-text/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-field-single-line-text", + "en_US": "https://help.vika.cn/docs/guide/manual-field-single-line-text", + "zh_HK": "https://help.vika.cn/docs/guide/manual-field-single-line-text" }, "field_help_text": { - "zh_CN": "/help/manual-field-text/", - "en_US": "/help/manual-field-text/", - "zh_HK": "/help/manual-field-text/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-field-text", + "en_US": "https://help.vika.cn/docs/guide/manual-field-text", + "zh_HK": "https://help.vika.cn/docs/guide/manual-field-text" }, "field_help_tree_select": { "zh_CN": "这里是多级联动的帮助页面", @@ -7199,9 +7594,9 @@ "zh_HK": "這裡是多級聯動的幫助頁面" }, "field_help_url": { - "zh_CN": "/help/manual-field-url/", - "en_US": "/help/manual-field-url/", - "zh_HK": "/help/manual-field-url/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-field-url", + "en_US": "https://help.vika.cn/docs/guide/manual-field-url", + "zh_HK": "https://help.vika.cn/docs/guide/manual-field-url" }, "field_member_property_multi": { "zh_CN": "允许添加多个成员", @@ -7264,13 +7659,13 @@ "zh_HK": "幫助文檔" }, "field_permission_help_url": { - "zh_CN": "/help/manual-field-permission/", - "en_US": "/help/manual-field-permission/", - "zh_HK": "/help/manual-field-permission/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-field-permission", + "en_US": "https://help.vika.cn/docs/guide/manual-field-permission", + "zh_HK": "https://help.vika.cn/docs/guide/manual-field-permission" }, "field_permission_lock_tips": { "zh_CN": "该列已设置权限,部分成员无法查看或编辑该列", - "en_US": "Permission is set for this column and some members cannot view this column", + "en_US": "Permission is set for this field and some members cannot view this field", "zh_HK": "該列已設置權限,部分成員無法查看或編輯該列" }, "field_permission_manager_lock_tips": { @@ -7334,7 +7729,7 @@ }, "field_permission_view_lock_tips": { "zh_CN": "该列已设置权限,筛选条件对于无权限访问该列的成员会失效", - "en_US": "This column is already set with permissions. Filters for members that do not have permission to access this column will be invalidated", + "en_US": "This field is already set with permissions. Filters for members that do not have permission to access this field will be invalidated", "zh_HK": "該列已設置權限,篩選條件對於無權限訪問該列的成員會失效" }, "field_permisson_close_tip": { @@ -7573,9 +7968,9 @@ "zh_HK": "按「${field_name}」篩選" }, "filter_help_url": { - "zh_CN": "/help/manual-filter/", - "en_US": "/help/manual-filter/", - "zh_HK": "/help/manual-filter/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-filter", + "en_US": "https://help.vika.cn/docs/guide/manual-filter", + "zh_HK": "https://help.vika.cn/docs/guide/manual-filter" }, "filter_link_data": { "zh_CN": "筛选引用的数据", @@ -7860,9 +8255,9 @@ "zh_HK": "幫助文檔" }, "form_help_link": { - "zh_CN": "/help/form/", - "en_US": "/help/form/", - "zh_HK": "/help/form/" + "zh_CN": "https://help.vika.cn/docs/guide/magic-form", + "en_US": "https://help.vika.cn/docs/guide/magic-form", + "zh_HK": "https://help.vika.cn/docs/guide/magic-form" }, "form_index_visible": { "zh_CN": "显示序号", @@ -7921,7 +8316,7 @@ }, "form_share_closed_popconfirm_title": { "zh_CN": "关闭公开链接", - "en_US": "Disable public form link", + "en_US": "Disable the public form link", "zh_HK": "關閉公開鏈接" }, "form_share_opened_desc": { @@ -7931,7 +8326,7 @@ }, "form_share_title": { "zh_CN": "通过公开链接收集表单数据", - "en_US": "Collect data via form link", + "en_US": "Collect data via the form", "zh_HK": "通過公開鏈接收集表單數據" }, "form_source_text": { @@ -7971,12 +8366,12 @@ }, "form_submit_once": { "zh_CN": "只允许提交一次", - "en_US": "Summit once only", + "en_US": "Submit once only", "zh_HK": "只允許提交一次" }, "form_submit_success": { "zh_CN": "提交成功", - "en_US": "Sumitted", + "en_US": "Submitted", "zh_HK": "提交成功" }, "form_submit_times_limit": { @@ -8020,9 +8415,9 @@ "zh_HK": "使用教程" }, "form_tour_link": { - "zh_CN": "/help/magic-form/", - "en_US": "/help/magic-form/", - "zh_HK": "/help/magic-form/" + "zh_CN": "https://help.vika.cn/docs/guide/magic-form", + "en_US": "https://help.vika.cn/docs/guide/magic-form", + "zh_HK": "https://help.vika.cn/docs/guide/magic-form" }, "form_updater_label": { "zh_CN": "在「只可阅读」基础上,还可以提交神奇表单", @@ -8080,9 +8475,9 @@ "zh_HK": "學習更多公式使用技巧" }, "formula_learn_more_url": { - "zh_CN": "/help/manual-summary-of-formula-functions-and-operators/", - "en_US": "/help/manual-summary-of-formula-functions-and-operators/", - "zh_HK": "/help/manual-summary-of-formula-functions-and-operators/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-summary-of-formula-functions-and-operators", + "en_US": "https://help.vika.cn/docs/guide/manual-summary-of-formula-functions-and-operators", + "zh_HK": "https://help.vika.cn/docs/guide/manual-summary-of-formula-functions-and-operators" }, "france": { "zh_CN": "法国", @@ -8097,7 +8492,12 @@ "free_edition": { "zh_CN": "免费版", "en_US": "Free", - "zh_HK": "免費版" + "zh_HK": "免費版", + "billing": { + "products": [ + "recEvZJqtqWRI" + ] + } }, "free_subscription": { "zh_CN": "免费订阅", @@ -8121,7 +8521,7 @@ }, "freeze_current_column": { "zh_CN": "冻结至此列", - "en_US": "Freeze to currtent column", + "en_US": "Freeze to the currtent column", "zh_HK": "凍結至此列" }, "freeze_line_tips": { @@ -8169,11 +8569,6 @@ "en_US": "Friend", "zh_HK": "好友" }, - "from": { - "zh_CN": "从", - "en_US": "from", - "zh_HK": "從" - }, "from_datasheet_associated": { "zh_CN": "来自「${datasheetName}」", "en_US": "Record from \"${datasheetName}\"", @@ -8311,7 +8706,7 @@ }, "function_concatenate_summary": { "zh_CN": "将多个文本值串联成单个文本值。(其效果等同于 &)\n\n【text1..】是要串联的多个值,可以输入文本、数字、日期参数或者引用列数据。\n\n请用双引号将你要串联的文本值引起来,数字和引用列除外。\n特例:如果要串联双引号,你需要使用反斜杠(\\)作为转义字符。", - "en_US": "Joins together the text arguments into a single text value.To concatenate static text, surround it with double quotation marks. To concatenate double quotation marks, you need to use a backslash (\\) as an escape character.\nEquivalent to use of the & operator.", + "en_US": "Joins together the text arguments into a single text value. To concatenate static text, surround it with double quotation marks. To concatenate double quotation marks, you need to use a backslash (\\) as an escape character.\nEquivalent to using the & operator.", "zh_HK": "將多個文本值串聯成單個文本值。 (其效果等同於 &)\n\n【text1..】是要串聯的多個值,可以輸入文本、數字、日期參數或者引用列數據。\n\n請用雙引號將你要串聯的文本值引起來,數字和引用列除外。\n特例:如果要串聯雙引號,你需要使用反斜杠(\\)作為轉義字符。" }, "function_content_empty": { @@ -8395,9 +8790,9 @@ "zh_HK": "簡介\n為指定的日期增加固定的時間間隔。\n\n參數說明\ndate:是你指定的日期。本函數將在該日期基礎上增加一定的時間間隔。\ncount:是時間間隔,支持輸入帶正負號的數字。如果為正數,即表示增加幾天(可自定義計時單位),見例子一;如果為負數,即表示減少幾天,見例子二;\nunits:是計時單位,即增加時間間隔的單位。比如按 “天” 計算也可以轉換為按 “年” 計算。\n\n計時單位包括以下符號,兩種格式都可以使用:「單位說明符 」→ 「縮寫」\n毫秒:「milliseconds」 → 「ms」\n秒:「seconds」 → 「s」\n分鐘:「minutes」 → 「m」\n小時:“hours” → “h”\n天:“days” → “d”\n週:“weeks” → “w”\n月:“months” → “M”\n季度:“quarters” → “Q”\n年:“years” → “y”\n\n點擊下方鏈接可查看全部計時單位。" }, "function_dateadd_url": { - "zh_CN": "/help/manual-supported-unit-specifiers-for-date-function/", - "en_US": "/help/manual-supported-unit-specifiers-for-date-function/", - "zh_HK": "/help/manual-supported-unit-specifiers-for-date-function/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-supported-unit-specifiers-for-date-function", + "en_US": "https://help.vika.cn/docs/guide/manual-supported-unit-specifiers-for-date-function", + "zh_HK": "https://help.vika.cn/docs/guide/manual-supported-unit-specifiers-for-date-function" }, "function_datestr_example": { "zh_CN": "DATESTR(\"2020/10/01\")\n=> 2020-10-01\n\nDATESTR({启动时间})\n=> 2020-06-10", @@ -8416,13 +8811,13 @@ }, "function_datetime_diff_summary": { "zh_CN": "返回两个日期之间的差值(有正负),即日期1减去日期2。\n\n【date1】日期1\n【date2】日期2\n【units】计时单位,日期1与日期2差值的计算单位。比如按“天”计算也可以转换为按“年”计算。\n\n计时单位包括以下符号,两种格式都可以使用:「单位说明符 」→ 「缩写」\n毫秒:\"milliseconds\" → \"ms\"\n秒:\"seconds\" → \"s\"\n分钟:\"minutes\" → \"m\"\n小时:\"hours\" → \"h\"\n天:\"days\" → \"d\"\n周:\"weeks\" → \"w\"\n月:\"months\" → \"M\"\n季度:\"quarters\" → \"Q\"\n年:\"years\" → \"y\"\n\n点击下方链接可查看全部计时单位。", - "en_US": "Returns the difference between datetimes in specified units. Default units are seconds. (See list of unit specifiers here.)\nThe difference between datetimes is determined by subtracting [date2] from [date1]. This means that if [date2] is later than [date1], the resulting value will be negative.", + "en_US": "Returns the difference between datetimes in specified units. Default units are seconds.\nThe difference between datetimes is determined by subtracting [date2] from [date1]. This means that if [date2] is later than [date1], the resulting value will be negative.", "zh_HK": "返回兩個日期之間的差值(有正負),即日期1減去日期2。\n\n【date1】日期1\n【date2】日期2\n【units】計時單位,日期1與日期2差值的計算單位。比如按“天”計算也可以轉換為按“年”計算。\n\n計時單位包括以下符號,兩種格式都可以使用:「單位說明符 」→ 「縮寫」\n毫秒:\"milliseconds\" → \"ms\"\n秒:\"seconds\" → \"s\"\n分鐘:\"minutes\" → \"m\"\n小時:\"hours\" → \"h\"\n天:\"days\" → \"d\"\n週:\"weeks\" → \"w\"\n月:\"months\" → \"M\"\n季度:\"quarters\" → \"Q\"\n年:\"years\" → \"y\"\n\n點擊下方鏈接可查看全部計時單位。" }, "function_datetime_diff_url": { - "zh_CN": "/help/manual-supported-unit-specifiers-for-date-function/", - "en_US": "/help/manual-supported-unit-specifiers-for-date-function/", - "zh_HK": "/help/manual-supported-unit-specifiers-for-date-function/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-supported-unit-specifiers-for-date-function", + "en_US": "https://help.vika.cn/docs/guide/manual-supported-unit-specifiers-for-date-function", + "zh_HK": "https://help.vika.cn/docs/guide/manual-supported-unit-specifiers-for-date-function" }, "function_datetime_format_example": { "zh_CN": "DATETIME_FORMAT(\"2020-10-01\", \"DD-MM-YYYY\")\n=> 01-10-2020\n\nDATETIME_FORMAT(\"2020-10-01\", \"YYYY / MM / DD\")\n=>2020/10/01\n\nDATETIME_FORMAT(\"2020-10-01\", \"MM.DD\")\n=>10.01\n\nDATETIME_FORMAT(TODAY(), \"DD-MM-YYYY\")\n=> 01-10-2020", @@ -8430,14 +8825,14 @@ "zh_HK": "DATETIME_FORMAT(\"2020-10-01\", \"DD-MM-YYYY\")\n=> 01-10-2020\n\nDATETIME_FORMAT(\"2020-10-01\", \"YYYY / MM / DD\")\n=>2020/10/01\n\nDATETIME_FORMAT(\"2020-10-01\", \"MM.DD\")\n=>10.01\n\nDATETIME_FORMAT(TODAY(), \"DD-MM-YYYY\")\n=> 01-10-2020" }, "function_datetime_format_summary": { - "zh_CN": "将日期以自定义的形式格式化为文本。\n\n【date】是需要被格式化的日期。\n【specified_output_format】是选择的格式化说明符。比如说明符可以是:\n\"DD-MM-YYYY\"表示\"日-月-年\",见例子一;\n\" YYYY / MM / DD\"表示\"年/月/日\",见例子二;\n\"MM.DD\"表示\"月.日\",见例子三。\n\n日期经过格式化后,将变为一串文本。\n\n本函数支持的日期格式化说明符请看下方链接。", - "en_US": "Formats a datetime into a specified string. For an explanation of how to use this function with date fields, click here. For a list of supported format specifiers, please click here.", + "zh_CN": "将日期以自定义的形式格式化为文本。\n\n【date】是需要被格式化的日期。\n【output_specifier】是选择的格式化说明符。比如说明符可以是:\n\"DD-MM-YYYY\"表示\"日-月-年\",见例子一;\n\" YYYY / MM / DD\"表示\"年/月/日\",见例子二;\n\"MM.DD\"表示\"月.日\",见例子三。\n\n日期经过格式化后,将变为一串文本。\n\n本函数支持的日期格式化说明符请看下方链接。", + "en_US": "Formats a datetime into a specified string. For a list of supported format specifiers, please click here.", "zh_HK": "將日期以自定義的形式格式化為文本。\n\n【date】是需要被格式化的日期。\n【specified_output_format】是選擇的格式化說明符。比如說明符可以是:\n\"DD-MM-YYYY\"表示\"日-月-年\",見例子一;\n\" YYYY / MM / DD\"表示\"年/月/日\",見例子二;\n\"MM.DD\"表示\"月.日\",見例子三。\n\n日期經過格式化後,將變為一串文本。\n\n本函數支持的日期格式化說明符請看下方鏈接。" }, "function_datetime_format_url": { - "zh_CN": "/help/datetime-format-specified-output-format/", - "en_US": "/help/datetime-format-specified-output-format/", - "zh_HK": "/help/datetime-format-specified-output-format/" + "zh_CN": "https://help.vika.cn/docs/guide/datetime-format-specified-output-format", + "en_US": "https://help.vika.cn/docs/guide/datetime-format-specified-output-format", + "zh_HK": "https://help.vika.cn/docs/guide/datetime-format-specified-output-format" }, "function_datetime_parse_example": { "zh_CN": "DATETIME_PARSE(\"20201001\")\n=> \"2020/10/01\"\n\nDATETIME_PARSE(\"01 10 2020 18:00\", \"DD MM YYYY HH:mm\")\n=> \"2020/10/01 18:00\"\n\nDATETIME_PARSE(\"01号10月2020年18:00时\", \"DD号MM月YYYY年HH:mm时\")\n=> \"2020/10/01 18:00\"", @@ -8450,9 +8845,9 @@ "zh_HK": "將文本轉換為結構化日期類型。\n\n【date】是要被格式化為日期的文本。\n【input_format】非必填,本參數是日期格式化說明符。對於系統無法識別的文本日期內容,你可以自己解釋為結構化的日期。見例子二。\n\n本函數支持的日期格式化說明符和語言環境請查看下方鏈接。" }, "function_datetime_parse_url": { - "zh_CN": "/help/set-locale-specified-output-format/", - "en_US": "/help/set-locale-specified-output-format/", - "zh_HK": "/help/set-locale-specified-output-format/" + "zh_CN": "https://help.vika.cn/docs/guide/datetime-format-specified-output-format/", + "en_US": "https://help.vika.cn/docs/guide/datetime-format-specified-output-format/", + "zh_HK": "https://help.vika.cn/docs/guide/datetime-format-specified-output-format/" }, "function_day_example": { "zh_CN": "DAY(\"2020.10.01\")\n=>1\n\nDAY({完成日期})\n=>5", @@ -8620,9 +9015,9 @@ "zh_HK": "返回當前日期和指定日期之間的差值(無正負)。\n\n【date】是指定日期,即用指定日期減去當前日期,計算兩個日期相差的多少天(自定義計時單位),無正負。\n【units】計時單位,即指定日期與當前日期差值的計算單位,比如按”天“計算也可以轉換為按”年“計算。\n\n計時單位包括以下符號,兩種格式都可以使用:\n「單位說明符 」→ 「縮寫」\n毫秒:\"milliseconds\" → \"ms\"\n秒:\"seconds\" → \"s\"\n分鐘:\"minutes\" → \"m\"\n小時:\"hours\" → \"h\"\n天:\"days\" → \"d\"\n週:\"weeks\" → \"w\"\n月:\"months\" → \"M\"\n季度:\"quarters\" → \"Q\"\n年:\"years\" → \"y\"\n點擊下方鏈接可查看全部計時單位。" }, "function_fromnow_url": { - "zh_CN": "/help/manual-supported-unit-specifiers-for-date-function/", - "en_US": "/help/manual-supported-unit-specifiers-for-date-function/", - "zh_HK": "/help/manual-supported-unit-specifiers-for-date-function/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-supported-unit-specifiers-for-date-function", + "en_US": "https://help.vika.cn/docs/guide/manual-supported-unit-specifiers-for-date-function", + "zh_HK": "https://help.vika.cn/docs/guide/manual-supported-unit-specifiers-for-date-function" }, "function_guidance": { "zh_CN": "智能引导", @@ -8700,9 +9095,9 @@ "zh_HK": "確定日期1是否等於日期2,如果等於則返回真(true),否則返回假(false)。\n\n【date1】是日期1。\n【date2】是日期2。\n【units】非必填, 是比較的時間單位。比如比較兩個日期是否相等,一直對比到分鐘單位。\n\n日期可以是輸入的參數,見用例一;\n日期也可以是引用日期類型的維格列,見用例四。\n在單元格內真和假用\"已勾選\"和\"未勾選\"表示。\n\n點擊下方鏈接可查看全部計時單位。" }, "function_is_same_url": { - "zh_CN": "/help/manual-supported-unit-specifiers-for-date-function/", - "en_US": "/help/manual-supported-unit-specifiers-for-date-function/", - "zh_HK": "/help/manual-supported-unit-specifiers-for-date-function/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-supported-unit-specifiers-for-date-function", + "en_US": "https://help.vika.cn/docs/guide/manual-supported-unit-specifiers-for-date-function", + "zh_HK": "https://help.vika.cn/docs/guide/manual-supported-unit-specifiers-for-date-function" }, "function_iserror_example": { "zh_CN": "ISERROR(2/0)\n=> TRUE\n\nISERROR(\"哈哈\"*2)\n=> TRUE", @@ -8741,7 +9136,7 @@ }, "function_len_summary": { "zh_CN": "统计一段文本的字符长度。\n\n【string】是要计算长度的文本;标点符号、空格等也会占一个字符。", - "en_US": "Extract howMany characters from the beginning of the string.", + "en_US": "Counts the character length of a text.\n\n[string] is the text to calculate the length; Punctuation, spaces, etc. also account for one character.", "zh_HK": "統計一段文本的字符長度。\n\n【string】是要計算長度的文本;標點符號、空格等也會佔一個字符。" }, "function_log_example": { @@ -8851,7 +9246,7 @@ }, "function_odd_summary": { "zh_CN": "返回沿绝对值增大方向最接近的奇数。\n\n【value】是要取奇的数值。\n【绝对值增大】即它返回值是远离0(零)方向。", - "en_US": "Rounds positive value up the the nearest odd number and negative value down to the nearest odd number.", + "en_US": "Rounds positive value up to the nearest odd number and negative value down to the nearest odd number.", "zh_HK": "返回沿絕對值增大方向最接近的奇數。\n\n【value】是要取奇的數值。\n【絕對值增大】即它返回值是遠離0(零)方向。" }, "function_or_example": { @@ -8961,7 +9356,7 @@ }, "function_search_summary": { "zh_CN": "搜索特定的文本在内容中第一次出现的位置。\n\n【stringToFind】是要搜索到的特定文本。\n【whereToSearch】指定从哪段内容搜索文本。可以输入文本参数或者引用维格列。\n【startFromPosition】非必填,指定从内容的哪个位置开始搜索(用数字表示第几个字符)。\n\n本函数可以在一大段内容中快速搜索特定文本出现的位置。 \n如果返回数字3,表示文本出现在该内容的第3个字符。\n如果未找到匹配的文本,则结果将为空值。\n\n其效果与FIND()类似,但是未找到匹配项时,FIND()返回值为0而不是空值。", - "en_US": "Searches for an occurrence of stringToFind in whereToSearch string starting from an optional startFromPosition. (startFromPosition is 0 by default.) If no occurrence of stringToFind is found, the result will be empty.\nSimilar to FIND(), though FIND() returns 0 rather than empty if no occurrence of stringToFind is found.", + "en_US": "Searches for an occurrence of stringToFind in whereToSearch string starting from an optional startFromPosition. (startFromPosition is 0 by default.) If no occurrence of stringToFind is found, the result will be empty.\nSimilar to FIND(), FIND() returns 0 rather than empty if no occurrence of stringToFind is found.", "zh_HK": "搜索特定的文本在內容中第一次出現的位置。\n\n【stringToFind】是要搜索到的特定文本。\n【whereToSearch】指定從哪段內容搜索文本。可以輸入文本參數或者引用維格列。\n【startFromPosition】非必填,指定從內容的哪個位置開始搜索(用數字表示第幾個字符)。\n\n本函數可以在一大段內容中快速搜索特定文本出現的位置。\n如果返回數字3,表示文本出現在該內容的第3個字符。\n如果未找到匹配的文本,則結果將為空值。\n\n其效果與FIND()類似,但是未找到匹配項時,FIND()返回值為0而不是空值。" }, "function_second_example": { @@ -8985,9 +9380,9 @@ "zh_HK": "為指定日期時間設置特定的語言環境。\n\n【date】是指定日期。\n【ocale_modifier】是語言環境說明符。\n\n本函數必須與DATETIME_FORMAT結合使用。可以點擊下方鏈接查看支持的語言環境說明符。" }, "function_set_locale_url": { - "zh_CN": "/help/set-locale-specified-output-format/", - "en_US": "/help/set-locale-specified-output-format/", - "zh_HK": "/help/set-locale-specified-output-format/" + "zh_CN": "https://help.vika.cn/docs/guide/set-locale-specified-output-format", + "en_US": "https://help.vika.cn/docs/guide/set-locale-specified-output-format", + "zh_HK": "https://help.vika.cn/docs/guide/set-locale-specified-output-format" }, "function_set_timezone_example": { "zh_CN": "DATETIME_FORMAT(SET_TIMEZONE(NOW(), -8), \"M/D/YYYY h:mm\")\n=> 9/20/2021 2:30", @@ -9066,7 +9461,7 @@ }, "function_today_summary": { "zh_CN": "返回今天的日期(年月日),但不会精确到时分秒(默认为00:00:00)。如果想要精确到时分秒,请使用函数NOW。\n\n可以直接使用此函数返回年月日,见例子一;\n也可以和DATEADD或DATETIME_DIFF等函数一起使用,比如用{截止时间}减去当前时间,来显示项目的倒计时,见例子二。\n\n注意:仅当重新刷新计算公式或刷新表格时,这个函数返回的结果才会更新。", - "en_US": "Returns the current date and time.", + "en_US": "Returns today's date (year, month, and day), but is not precise to hours, minutes, and seconds (default is 00:00:00). If you want to be precise to the hours, minutes, and seconds, use the function NOW.\n\nNote: The results returned by this function are updated only when the calculation formula is refreshed or the datasheet is refreshed.", "zh_HK": "返回今天的日期(年月日),但不會精確到時分秒(默認為00:00:00)。如果想要精確到時分秒,請使用函數NOW。\n\n可以直接使用此函數返回年月日,見例子一;\n也可以和DATEADD或DATETIME_DIFF等函數一起使用,比如用{截止時間}減去當前時間,來顯示項目的倒計時,見例子二。\n\n注意:僅當重新刷新計算公式或刷新表格時,這個函數返回的結果才會更新。" }, "function_tonow_example": { @@ -9080,9 +9475,9 @@ "zh_HK": "返回當前日期和指定日期之間的差值(無正負)。\n\n【date】是指定日期,即用指定日期減去當前日期,計算兩個日期相差的多少天(自定義計時單位),無正負。\n【units】計時單位,即指定日期與當前日期差值的計算單位,比如按”天“計算也可以轉換為按”年“計算。\n\n計時單位包括以下符號,兩種格式都可以使用:\n「單位說明符 」→ 「縮寫」\n毫秒:\"milliseconds\" → \"ms\"\n秒:\"seconds\" → \"s\"\n分鐘:\"minutes\" → \"m\"\n小時:\"hours\" → \"h\"\n天:\"days\" → \"d\"\n週:\"weeks\" → \"w\"\n月:\"months\" → \"M\"\n季度:\"quarters\" → \"Q\"\n年:\"years\" → \"y\"\n點擊下方鏈接可查看全部計時單位。" }, "function_tonow_url": { - "zh_CN": "/help/manual-supported-unit-specifiers-for-date-function/", - "en_US": "/help/manual-supported-unit-specifiers-for-date-function/", - "zh_HK": "/help/manual-supported-unit-specifiers-for-date-function/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-supported-unit-specifiers-for-date-function", + "en_US": "https://help.vika.cn/docs/guide/manual-supported-unit-specifiers-for-date-function", + "zh_HK": "https://help.vika.cn/docs/guide/manual-supported-unit-specifiers-for-date-function" }, "function_trim_example": { "zh_CN": "TRIM(\" 两边空格会被清除! \")\n=>两边空格会被清除!", @@ -9091,7 +9486,7 @@ }, "function_trim_summary": { "zh_CN": "清除文本开头和结尾的空格。\n\n【value】是需要被处理的文本。", - "en_US": "Removes whitespace at the beginning and end of string.", + "en_US": "Removes whitespace at the beginning and end of the string.", "zh_HK": "清除文本開頭和結尾的空格。\n\n【value】是需要被處理的文本。" }, "function_true_example": { @@ -9176,7 +9571,7 @@ }, "function_workday_summary": { "zh_CN": "返回起始日期若干个工作日之后的日期。\n\n【startDate】是你指定的起始日期。\n【numDays】是你指定的起始日期之后的工作日天数,用正数表示。比如,数字“1”代表起始日期一个工作日之后的日期,见例子一;\n【holidays】非必填。是要从日历中去除的特定日期,例如节假日。其输入格式为「yyyy-mm-dd」,多个日期以逗号分隔的,见例子三。\n\n本函数的工作日不包括周末和你指定的特定日期。", - "en_US": "Returns the number of working days between startDate and endDate. Working days exclude weekends and an optional list of holidays, formatted as a comma-separated string of ISO-formatted dates.", + "en_US": "Returns a date several workdays after the start date.\n\n[startDate] is the start date you specify.\n[numDays] is the number of workdays after the start date you specify, expressed as a positive number. For example, the number \"1\" represents a date one workday after the start date;\n[holidays] Is not required. It is specific dates that you want to remove from your calendar, such as a holiday. The input format is \"yyyy-mm-dd\", and multiple dates are separated by commas.\n\nThe workdays of this function do not include weekends and specific days that you specify.", "zh_HK": "返回起始日期若干個工作日之後的日期。\n\n【startDate】是你指定的起始日期。\n【numDays】是你指定的起始日期之後的工作日天數,用正數表示。比如,數字“1”代表起始日期一個工作日之後的日期,見例子一;\n【holidays】非必填。是要從日曆中去除的特定日期,例如節假日。其輸入格式為「yyyy-mm-dd」,多個日期以逗號分隔的,見例子三。\n\n本函數的工作日不包括週末和你指定的特定日期。" }, "function_xor_example": { @@ -9219,9 +9614,9 @@ "zh_HK": "排列模式" }, "gallery_group_hlep_url": { - "zh_CN": "/help/manual-gallery-view/", - "en_US": "/help/manual-gallery-view/", - "zh_HK": "/help/manual-gallery-view/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-gallery-view", + "en_US": "https://help.vika.cn/docs/guide/manual-gallery-view", + "zh_HK": "https://help.vika.cn/docs/guide/manual-gallery-view" }, "gallery_guide_desc": { "zh_CN": "更直观的展示图片,管理图片数据更简单", @@ -9234,9 +9629,9 @@ "zh_HK": "拉伸" }, "gallery_style_setting_url": { - "zh_CN": "/help/manual-gallery-view/#2-toc-title", - "en_US": "/help/manual-gallery-view/#2-toc-title", - "zh_HK": "/help/manual-gallery-view/#2-toc-title" + "zh_CN": "https://help.vika.cn/docs/guide/manual-gallery-view#set-style", + "en_US": "https://help.vika.cn/docs/guide/manual-gallery-view#set-style", + "zh_HK": "https://help.vika.cn/docs/guide/manual-gallery-view#set-style" }, "gallery_view": { "zh_CN": "相册视图", @@ -9378,7 +9773,7 @@ }, "gantt_config_only_count_workdays": { "zh_CN": "任务天数统计只计算工作日", - "en_US": "The task days count counts only workdays", + "en_US": "Duration only counts workdays.", "zh_HK": "任務天數統計只計算工作日" }, "gantt_config_saturday": { @@ -9512,7 +9907,7 @@ }, "gantt_guide_desc": { "zh_CN": "更方便的管理项目进度,调整任务时间", - "en_US": "Use a Gantt chart to arrange tasks and view project schedule", + "en_US": "Use a Gantt chart to arrange tasks and view the project schedule", "zh_HK": "更方便的管理項目進度,調整任務時間" }, "gantt_historical_data_warning": { @@ -9619,9 +10014,9 @@ "zh_HK": "請選擇依賴關係字段" }, "gantt_setting": { - "zh_CN": "甘特图设置", - "en_US": "Edit chart", - "zh_HK": "甘特圖設置" + "zh_CN": "视图设置", + "en_US": "Settings", + "zh_HK": "視圖設置" }, "gantt_setting_help_tips": { "zh_CN": "配置教程", @@ -9629,9 +10024,9 @@ "zh_HK": "配置教程" }, "gantt_setting_help_url": { - "zh_CN": "/help/manual-gantt-view/", - "en_US": "/help/manual-gantt-view/ ", - "zh_HK": "/help/manual-gantt-view/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-gantt-view", + "en_US": "https://help.vika.cn/docs/guide/manual-gantt-view", + "zh_HK": "https://help.vika.cn/docs/guide/manual-gantt-view" }, "gantt_start_field_name": { "zh_CN": "开始时间", @@ -9645,7 +10040,7 @@ }, "gantt_task_group_tooltip": { "zh_CN": "共计 ${count} 个任务", - "en_US": "${count} tasks", + "en_US": "${count} task(s)", "zh_HK": "共計 ${count} 個任務" }, "gantt_task_total_date": { @@ -9660,7 +10055,7 @@ }, "gantt_view": { "zh_CN": "甘特视图", - "en_US": "Gantt chart", + "en_US": "Gantt", "zh_HK": "甘特視圖" }, "gantt_week": { @@ -9806,7 +10201,6 @@ "go_login": { "zh_CN": "去登录", "en_US": "Log in ", - "终端": "APP", "zh_HK": "去登錄" }, "go_to": { @@ -9831,7 +10225,7 @@ }, "gold_grade": { "zh_CN": "黄金级", - "en_US": "Gold", + "en_US": "Team", "zh_HK": "黃金級" }, "gold_grade_desc": { @@ -9839,10 +10233,26 @@ "en_US": "For teams or organizations with complex business procedures", "zh_HK": "適用於有復雜業務流程的團隊或組織" }, + "gold_seat_200_desc": { + "zh_CN": "200人", + "en_US": "200", + "billing": { + "prices": [ + "rec5TWb5jQxlV", + "recB97v0XiHcH", + "recvZGVHzEhyW" + ] + } + }, "golden_grade": { "zh_CN": "黄金级", "en_US": "Gold", - "zh_HK": "黃金級" + "zh_HK": "黃金級", + "billing": { + "products": [ + "recXo7DTGZByy" + ] + } }, "got_it": { "zh_CN": "知道了", @@ -9875,24 +10285,24 @@ "zh_HK": "{\"bronze\":\"免費\",\"silver\":\"1 元\",\"gold\":\"1.5 元\",\"enterprise\":\"按需定制\"}" }, "grade_price_by_month": { - "zh_CN": "{\n \"bronze\": \"免费\",\n \"silver\": \"约 36 元 人/月\",\n \"gold\": \"约 43.8 元 人/月\",\n \"enterprise\": \"按需定制\",\n \"community\": \"免费\",\n \"custom\": \"按需定制\"\n}", - "en_US": "{\"bronze\":\"free\",\"silver\":\"about 36 yuan per person/month\",\"gold\":\"about 43.8 yuan per person/month\",\"enterprise\":\"customized on demand\"}", + "zh_CN": "{\n \"bronze\": \"免费\",\n \"silver\": \" 680元 /月\",\n \"gold\": \" 2,800元 /月\",\n \"enterprise\": \"按需定制\",\n \"community\": \"免费\",\n \"custom\": \"按需定制\"\n}", + "en_US": "{\n \"bronze\": \"Free\",\n \"silver\": \"$10 $25 /month\",\n \"gold\": \" $125 /month\",\n \"enterprise\": \"Customized\",\n \"community\": \"Free\",\n \"custom\": \"Customize\",\n \"free\":\"Free\",\n \"plus\": \"$10 $25 /month\",\n \"pro\": \" $125 /month\"\n}", "zh_HK": "{\"bronze\":\"免費\",\"silver\":\"約 36 元 人/月\",\"gold\":\"約 43.8 元 人/月\",\"enterprise\":\"按需定制\"}" }, "grade_price_by_month_origin": { - "zh_CN": "{\"bronze\":\"\",\"silver\":\"\",\"gold\":\"\",\"enterprise\":\"\"}", - "en_US": "{\"bronze\":\"\",\"silver\":\"\",\"gold\":\"\",\"enterprise\":\"\"}", + "zh_CN": "{\n \"bronze\": \"\",\n \"silver\": \"\",\n \"gold\": \"\",\n \"enterprise\": \"\",\n \"community\": \"\",\n \"custom\": \"\"\n}", + "en_US": "{\n \"bronze\":\"\",\n \"silver\": \"Incl. 10 users, then $2/user/month\",\n \"gold\": \"Incl. 10 users, then $2/user/month\",\n \"enterprise\":\"\",\n \"free\":\"Free\",\n \"plus\": \"Incl. 10 users, then ${originPrice} $2/user/month\",\n \"pro\": \"Incl. 10 users, then ${originPrice} $2/user/month\"\n}", "zh_HK": "{\"bronze\":\"\",\"silver\":\"\",\"gold\":\"\",\"enterprise\":\"\"}" }, "grade_price_by_year": { - "zh_CN": "{\n \"bronze\": \"免费\",\n \"silver\": \"约 368 元 人/年\",\n \"gold\": \"约 538.8 元 人/年\",\n \"enterprise\": \"按需定制\",\n \"community\": \"免费\",\n \"custom\": \"按需定制\"\n}", - "en_US": "{\"bronze\":\"免费\",\"silver\":\"368 元 人/年\",\"gold\":\"538.8 元 人/年\",\"enterprise\":\"按需定制\"}", + "zh_CN": "{\n \"bronze\": \"免费\",\n \"silver\": \" 6,800元 /年\",\n \"gold\": \" 26,800元 /年\",\n \"enterprise\": \"按需定制\",\n \"community\": \"免费\",\n \"custom\": \"按需定制\"\n}", + "en_US": "{\n \"bronze\": \"Free\",\n \"silver\": \"$15 $25 /year\",\n \"gold\": \" $399 /year\",\n \"enterprise\": \"Customize\",\n \"community\": \"Free\",\n \"custom\": \"Customize\",\n \"free\":\"Free\",\n \"plus\": \"$15 $25 /year\",\n \"pro\": \" $399 /year\"\n}\n ", "zh_HK": "{\"bronze\":\"免費\",\"silver\":\"約 368 元 人/年\",\"gold\":\"約 538.8 元 人/年\",\"enterprise\":\"按需定制\"}" }, "grade_price_by_year_origin": { - "zh_CN": "{\"bronze\":\"\",\"silver\":\"\",\"gold\":\"\",\"enterprise\":\"\"}", - "en_US": "{\"bronze\":\"\",\"silver\":\"\",\"gold\":\"\",\"enterprise\":\"\"}", - "zh_HK": "{\"bronze\":\"\",\"silver\":\"\",\"gold\":\"\",\"enterprise\":\"\"}" + "zh_CN": "{\n \"bronze\": \"\",\n \"silver\": \"\",\n \"gold\": \"\",\n \"enterprise\": \"\",\n \"community\": \"\",\n \"custom\": \"\"\n}", + "en_US": "{\n \"bronze\": \"\",\n \"silver\": \"Incl. 10 users, then ${originPrice} $2/user/year\",\n \"gold\": \"Incl. 20 users, then ${originPrice} $8/user/year\",\n \"enterprise\": \"\",\n \"community\": \"\",\n \"custom\": \"\",\n \"free\": \"\",\n \"plus\": \"Incl. 10 users, then ${originPrice} $2/user/year\",\n \"pro\": \"Incl. 20 users, then ${originPrice} $8/user/year\"\n}", + "zh_HK": "{\n \"bronze\": \"\",\n \"silver\": \"约 368 元 人/年\",\n \"gold\": \"约 538.8 元 人/年\",\n \"enterprise\": \"\",\n \"community\": \"\",\n \"custom\": \"\"\n}" }, "grades_restriction_prompt": { "zh_CN": "你的功能使用量已经超出上限,升级可获得更多功能用量。", @@ -9960,9 +10370,9 @@ "zh_HK": "按「${field_name}」分組" }, "group_help_url": { - "zh_CN": "/help/manual-group/", - "en_US": "/help/manual-group/", - "zh_HK": "/help/manual-group/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-group", + "en_US": "https://help.vika.cn/docs/guide/manual-group", + "zh_HK": "https://help.vika.cn/docs/guide/manual-group" }, "groups_clubs_hobbies": { "zh_CN": "兴趣社群", @@ -10133,11 +10543,41 @@ "en_US": "Help center", "zh_HK": "幫助中心" }, + "help_help_center_url": { + "zh_CN": "https://help.vika.cn/", + "en_US": "https://help.vika.cn/", + "zh_HK": "https://help.vika.cn/" + }, + "help_product_manual_url": { + "zh_CN": "https://help.vika.cn/docs/guide/manual-1-what-is-vikadata/", + "en_US": "https://help.vika.cn/docs/guide/manual-1-what-is-vikadata/", + "zh_HK": "https://help.vika.cn/docs/guide/manual-1-what-is-vikadata/" + }, + "help_questions_url": { + "zh_CN": "https://help.vika.cn/docs/guide/questions", + "en_US": "https://help.vika.cn/docs/guide/questions", + "zh_HK": "https://help.vika.cn/docs/guide/questions" + }, + "help_quick_start_url": { + "zh_CN": "https://help.vika.cn/docs/guide/tutorial-1-quick-start/", + "en_US": "https://help.vika.cn/docs/guide/tutorial-1-quick-start/", + "zh_HK": "https://help.vika.cn/docs/guide/tutorial-1-quick-start/" + }, "help_resources": { "zh_CN": "畅游帮助中心", "en_US": "Help Resources", "zh_HK": "暢遊幫助中心" }, + "help_user_community": { + "zh_CN": "维格社区", + "en_US": "Vika Community", + "zh_HK": "維格社區" + }, + "help_video_tutorials": { + "zh_CN": "维格课堂", + "en_US": "Video tutorials", + "zh_HK": "維格課堂" + }, "hidden": { "zh_CN": "折叠", "en_US": "Hide", @@ -10170,7 +10610,7 @@ }, "hidden_groups_by_count": { "zh_CN": "${count} 分组隐藏", - "en_US": "${count} Group Hidden", + "en_US": "${count} Group(s) Hidden", "zh_HK": "${count} 分組隱藏" }, "hidden_n_fields": { @@ -10205,7 +10645,7 @@ }, "hide_kanban_grouping": { "zh_CN": "隐藏分组", - "en_US": "Hide the grouping", + "en_US": "Hide group", "zh_HK": "隱藏分組" }, "hide_one_field": { @@ -10245,7 +10685,7 @@ }, "history_view_tip": { "zh_CN": "可查看 ${day} 天的修改历史,", - "en_US": "You can view record activity in ${day} days", + "en_US": "You can view record activity in ${day} days. ", "zh_HK": "可查看 ${day} 天的修改歷史," }, "honduras": { @@ -10259,14 +10699,14 @@ "zh_HK": "中國香港" }, "how_contact_service": { - "zh_CN": "/help/how-contact-service/", - "en_US": "/help/how-contact-service/", - "zh_HK": "/help/how-contact-service/" + "zh_CN": "https://help.vika.cn/docs/guide/how-contact-service", + "en_US": "https://help.vika.cn/docs/guide/how-contact-service", + "zh_HK": "https://help.vika.cn/docs/guide/how-contact-service" }, "how_create_template": { - "zh_CN": "目前还没有空间站模板 如何创建", - "en_US": "This Space has no custom templates yet. How to create", - "zh_HK": "目前還沒有空間站模板 如何創建" + "zh_CN": "目前还没有空间站模板 如何创建", + "en_US": "This Space has no custom templates yet. How to create", + "zh_HK": "目前還沒有空間站模板 如何創建" }, "how_many_seconds": { "zh_CN": "${seconds} 秒", @@ -10279,9 +10719,9 @@ "zh_HK": "如何獲得 V 幣" }, "how_to_report_issues": { - "zh_CN": "/help/how-to-report-issues/", - "en_US": "/help/how-to-report-issues/", - "zh_HK": "/help/how-to-report-issues/" + "zh_CN": "https://help.vika.cn/docs/guide/how-to-report-issues", + "en_US": "https://help.vika.cn/docs/guide/how-to-report-issues", + "zh_HK": "https://help.vika.cn/docs/guide/how-to-report-issues" }, "hr_and_recruiting": { "zh_CN": "人力资源", @@ -10434,6 +10874,7 @@ }, "inclusion_plan_desc": { "zh_CN": "{\"first_row\":\"vika维格表是一款有温度的产品,我们支持创业企业与教育组织群体,\",\"second_row\":\"提供低至 3 折的普惠定价方案,\",\"third_row\":\"愿为创业企业与教育组织群体提供更多帮助。\"}", + "en_US": "{\"first_row\":\"vika维格表是一款有温度的产品,我们支持创业企业与教育组织群体,\",\"second_row\":\"提供低至 3 折的普惠定价方案,\",\"third_row\":\"愿为创业企业与教育组织群体提供更多帮助。\"}", "zh_HK": "{\"first_row\":\"vika維格表是一款有溫度的產品,我們支持創業企業與教育組織群體,\",\"second_row\":\"提供低至 3 折的普惠定價方案,\",\"third_row\":\"願為創業企業與教育組織群體提供更多幫助。\"}" }, "inclusion_plan_title": { @@ -10462,16 +10903,17 @@ }, "inherit_field_permission_tip": { "zh_CN": "下方的所有成员或小组的都可以访问该列", - "en_US": "This column is accessible to all members or groups below" + "en_US": "This field is accessible to all members or teams below", + "zh_HK": "下方的所有成員或小組的都可以訪問該列" }, "inherit_permission_tip": { "zh_CN": "下方成员或小组的访问权来自上级文件夹", - "en_US": "Access is inherited from the parent folder", + "en_US": "Permissons are inherited from the parent folder", "zh_HK": "下方成員或小組的訪問權來自上級文件夾" }, "inherit_permission_tip_root": { "zh_CN": "下方的所有成员或小组的都可以访问该文件", - "en_US": "All members or groups below can access this file" + "en_US": "All members or teams below can access this file" }, "inhert_permission_desc": { "zh_CN": "成员和小组继承上级文件夹「${nodeName}」的权限", @@ -10489,7 +10931,7 @@ }, "initialization_failed_message": { "zh_CN": "页面在加载的过程中有未初始化成功的数据,请刷新重试,如有疑问请扫描右方的二维码添加客服,让我们来帮助你解决", - "en_US": "The page has not initialized successfully in the process of loading data, please refresh and retry, if you have questions, please scan the QR code on the right to add customer service, let us help you.", + "en_US": "An error occurred while initializing data, please refresh the page, if you have questions, please scan the QR code on the right to add customer service, let us help you.", "zh_HK": "頁面在加載的過程中有未初始化成功的數據,請刷新重試,如有疑問請掃描右方的二維碼添加客服,讓我們來幫助你解決" }, "inline_code": { @@ -10500,7 +10942,6 @@ "input_confirmation_password": { "zh_CN": "确认密码", "en_US": "Confirm new password", - "终端": "APP", "zh_HK": "確認密碼" }, "input_formula": { @@ -10511,7 +10952,6 @@ "input_new_password": { "zh_CN": "新密码", "en_US": "New password", - "终端": "APP", "zh_HK": "新密碼" }, "insert_above": { @@ -10579,6 +11019,10 @@ "en_US": "Install", "zh_HK": "安裝" }, + "installation": { + "zh_CN": "安装", + "en_US": "installation" + }, "instruction_of_node_permission": { "zh_CN": "权限说明", "en_US": "Permissions overview", @@ -10655,9 +11099,9 @@ "zh_HK": "
  • 維格表支持和企業微信的集成,集成後可在維格表 web 端使用企業微信掃碼登錄,在企業微信中免登錄使用維格表,能接收到來自維格表的消息提醒
  • 在企業微信中通過自建應用的方式集成維格表後,維格表會展示在工作台中
" }, "integration_app_wecom_form1_desc": { - "zh_CN": "请在企业微信内创建自建应用,并将应用信息填写至下方 如何填写", - "en_US": "Please create a self built application in wecom and fill in the application information below how to fill in ", - "zh_HK": "請在企業微信內創建自建應用,並將應用信息填寫至下方 如何填寫" + "zh_CN": "请在企业微信内创建自建应用,并将应用信息填写至下方 如何填写", + "en_US": "Please create a self built application in wecom and fill in the application information below how to fill in ", + "zh_HK": "請在企業微信內創建自建應用,並將應用信息填寫至下方 如何填寫" }, "integration_app_wecom_form1_item1_label": { "zh_CN": "企业 ID", @@ -10680,9 +11124,9 @@ "zh_HK": "企業微信自建應用信息" }, "integration_app_wecom_form2_desc": { - "zh_CN": "请将下方信息填写至企业微信自建应用内,实现企业微信内免密登录维格表 如何填写", - "en_US": "Please fill in the connection information of datasheet into the self built application of wecom to realize the secret free login of datasheet in wecom 如何填写", - "zh_HK": "請將下方信息填寫至企業微信自建應用內,實現企業微信內免密登錄維格表 如何填寫" + "zh_CN": "请将下方信息填写至企业微信自建应用内,实现企业微信内免密登录维格表 如何填写", + "en_US": "Please fill in the connection information of datasheet into the self built application of wecom to realize the secret free login of datasheet in wecom 如何填写", + "zh_HK": "請將下方信息填寫至企業微信自建應用內,實現企業微信內免密登錄維格表 如何填寫" }, "integration_app_wecom_form2_item1_label": { "zh_CN": "应用主页", @@ -10765,9 +11209,9 @@ "zh_HK": "遇到問題聯繫客服" }, "integration_wecom_disable_tips_message": { - "zh_CN": "
    \n
  • \n 因企业微信服务商运营规则变更,维格表的集成方式需要进行调整,因此停用后短时间内将无法再次采用自建应用模式集成企业微信。不便之处,敬请谅解\n
  • \n
  • \n 你可以采用“企业微信第三方应用”模式快速创建一个已集成的新空间站 点击查看第三方应用简介\n
  • \n
", - "en_US": "
    \n
  • \n Due to the change in the operation rules for sevice provider of WeCom, the integration mode of the vikadata table needs to be adjusted. therefore, it will be impossible to integrate WeCom with self-built application mode shortly after you disable the application. . We apologize for the inconvenience.\n
  • \n
  • \n You can use the \"WeCom third-party application\" to quickly create an integrated new space click to view the introduction of third-party application \n
  • \n
", - "zh_HK": "
    \n
  • \n 因企業微信服務商運營規則變更,維格表的集成方式需要進行調整,因此停用後短時間內將無法再次採用自建應用模式集成企業微信。不便之處,敬請諒解\n
  • \n
  • \n 你可以採用“企業微信第三方應用”模式快速創建一個已集成的新空間站 點擊查看第三方應用簡介\n
  • \n
" + "zh_CN": "
    \n
  • \n 因企业微信服务商运营规则变更,维格表的集成方式需要进行调整,因此停用后短时间内将无法再次采用自建应用模式集成企业微信。不便之处,敬请谅解\n
  • \n
  • \n 你可以采用“企业微信第三方应用”模式快速创建一个已集成的新空间站 点击查看第三方应用简介\n
  • \n
", + "en_US": "
    \n
  • \n Due to the change in the operation rules for sevice provider of WeCom, the integration mode of the vikadata table needs to be adjusted. therefore, it will be impossible to integrate WeCom with self-built application mode shortly after you disable the application. . We apologize for the inconvenience.\n
  • \n
  • \n You can use the \"WeCom third-party application\" to quickly create an integrated new space click to view the introduction of third-party application \n
  • \n
", + "zh_HK": "
    \n
  • \n 因企業微信服務商運營規則變更,維格表的集成方式需要進行調整,因此停用後短時間內將無法再次採用自建應用模式集成企業微信。不便之處,敬請諒解\n
  • \n
  • \n 你可以採用“企業微信第三方應用”模式快速創建一個已集成的新空間站 點擊查看第三方應用簡介\n
  • \n
" }, "integration_wecom_disable_tips_title": { "zh_CN": "停用须知", @@ -10795,14 +11239,14 @@ "zh_HK": "我受邀的空間站" }, "intro_dashboard": { - "zh_CN": "/help/intro-dashboard/#6-toc-title", - "en_US": "/help/intro-dashboard/", - "zh_HK": "/help/intro-dashboard/#6-toc-title" + "zh_CN": "https://help.vika.cn/docs/guide/intro-dashboard", + "en_US": "https://help.vika.cn/docs/guide/intro-dashboard", + "zh_HK": "https://help.vika.cn/docs/guide/intro-dashboard" }, "intro_widget": { - "zh_CN": "/help/intro-widget/", - "en_US": "/help/intro-widget-panel/", - "zh_HK": "/help/intro-widget/" + "zh_CN": "https://help.vika.cn/docs/guide/intro-widget", + "en_US": "https://help.vika.cn/docs/guide/intro-widget", + "zh_HK": "https://help.vika.cn/docs/guide/intro-widget" }, "intro_widget_tips": { "zh_CN": "什么是小程序?", @@ -10846,7 +11290,7 @@ }, "invitation_link_old": { "zh_CN": "已创建的邀请链接", - "en_US": "Created invitation links", + "en_US": "Created invitation link(s)", "zh_HK": "已創建的邀請鏈接" }, "invitation_reward": { @@ -10876,7 +11320,7 @@ }, "invite_by_qr_code": { "zh_CN": "二维码邀请", - "en_US": "Invite via QR Code", + "en_US": "Invite QR Code", "zh_HK": "二維碼邀請" }, "invite_code": { @@ -11085,12 +11529,12 @@ }, "invite_via_link": { "zh_CN": "链接邀请", - "en_US": "Invite via link", + "en_US": "Invite link", "zh_HK": "鏈接邀請" }, "invite_your_join": { "zh_CN": "邀请您加入", - "en_US": "Welcome to join", + "en_US": " invites you to join", "zh_HK": "邀請您加入" }, "invitee": { @@ -11131,12 +11575,6 @@ "en_US": "No custom widgets", "zh_HK": "當前空間站還沒有自建小程序" }, - "is_empty_widget_panel": { - "zh_CN": "小程序面板暂未添加小程序,请前往 PC 端添加", - "废弃时间": 1660820089876, - "en_US": "No widgets, please add widgets on the PC", - "zh_HK": "小程序面板暫未添加小程序,請前往 PC 端添加" - }, "is_empty_widget_panel_mobile": { "zh_CN": "小程序面板暂未添加小程序,请前往 PC 端添加", "en_US": "No widgets, please add widgets on the PC", @@ -11202,6 +11640,11 @@ "en_US": "Submit Application", "zh_HK": "提交申請" }, + "join_discord_community": { + "zh_CN": "加入社区获取帮助", + "en_US": "Join community for help", + "zh_HK": "加入社區獲取幫助" + }, "join_the_community": { "zh_CN": "加入社群", "en_US": "Join community", @@ -11259,7 +11702,7 @@ }, "kanban_group_tip": { "zh_CN": "按${kanban_field_id}分组", - "en_US": "Group by ${kanban_field_id}", + "en_US": "Grouped by ${kanban_field_id}", "zh_HK": "按${kanban_field_id}分組" }, "kanban_guide_desc": { @@ -11332,9 +11775,9 @@ "zh_HK": "選擇或創建一列作為分組條件" }, "kanban_style_setting_url": { - "zh_CN": "/help/manual-kanban-view", - "en_US": "/help/manual-kanban-view", - "zh_HK": "/help/manual-kanban-view" + "zh_CN": "https://help.vika.cn/docs/guide/manual-kanban-view", + "en_US": "https://help.vika.cn/docs/guide/manual-kanban-view", + "zh_HK": "https://help.vika.cn/docs/guide/manual-kanban-view" }, "kanban_view": { "zh_CN": "看板视图", @@ -11513,7 +11956,6 @@ }, "lark": { "zh_CN": "飞书", - "废弃时间": 1657618477851, "en_US": "Lark", "zh_HK": "飛書" }, @@ -11529,7 +11971,7 @@ }, "lark_app_desc": { "zh_CN": "

开通应用后,请完成空间站绑定,绑定后你的团队可以在飞书上使用维格表。

\n\n

在飞书上使用维格表助手,你可以:

\n\n
  • 免登录,直接在飞书进入维格表空间站,访问空间站数据
  • 通过飞书,收取空间站里的通知
  • 在飞书设置应用可见范围,并自动同步飞书的组织架构
", - "en_US": "

After opening the app, please complete the space station bundle, and after bundling your team can use Vikadata on Lark.

\n\n

With Vikadata Assistant on Lark, you can:

\n\n
  • Access Vikadata's space station and access space station data directly on Lark without logging in;
  • Collect notifications from the space station through Lark;
  • Set app visibility in Lark and automatically sync Lark's organization structure;
", + "en_US": "

After installing the integration, please complete the space bundle, and after bundling, your team can use Vikadata on Lark.

\n\n

With Vikadata Assistant on Lark, you can:

\n\n
  • Access Vikadata's space and access space data directly on Lark without logging in;
  • Receive notifications from the space through Lark;
  • Set app visibility on Lark and automatically sync Lark's organization structure;
\n", "zh_HK": "

開通應用後,請完成空間站綁定,綁定後你的團隊可以在飛書上使用維格表。

\n\n

在飛書上使用維格表助手,你可以:

\n\n
  • 免登錄,直接在飛書進入維格表空間站,訪問空間站數據
  • 通過飛書,收取空間站裡的通知
  • 在飛書設置應用可見範圍,並自動同步飛書的組織架構
" }, "lark_app_intro": { @@ -11588,9 +12030,9 @@ "zh_HK": "溫馨提示" }, "lark_integration_step1_tips_content": { - "zh_CN": "
    \n
  • \n 除了自建应用的方式,我们还提供飞书商店应用版本,该版本无需配置与绑定空间站即可开箱即用 有什么不同?\n
  • \n
", - "en_US": "
    \n
  • \n In addition to the build-your-own app approach, we also offer a version of the Lark Store app that is ready to use right out of the box without configuration and binding to the space What's different?\n
  • \n
", - "zh_HK": "
    \n
  • \n 除了自建應用的方式,我們還提供飛書商店應用版本,該版本無需配置與綁定空間站即可開箱即用 有什麼不同? \n
  • \n
" + "zh_CN": "
    \n
  • \n 除了自建应用的方式,我们还提供飞书商店应用版本,该版本无需配置与绑定空间站即可开箱即用 有什么不同?\n
  • \n
", + "en_US": "
    \n
  • \n In addition to the build-your-own app approach, we also offer a version of the Lark Store app that is ready to use right out of the box without configuration and binding to the space What's different?\n
  • \n
", + "zh_HK": "
    \n
  • \n 除了自建應用的方式,我們還提供飛書商店應用版本,該版本無需配置與綁定空間站即可開箱即用 有什麼不同? \n
  • \n
" }, "lark_integration_step1_warning": { "zh_CN": "
    \n
  • \n

    \n 操作者需同时具有维格表空间站和飞书的管理权限\n

    \n

    操作者需要是空间站的主管理员,并同时具备在飞书管理后台创建自建应用的权限

    \n
  • \n
  • \n

    \n 空间站原有成员将会被移出空间站,然后从飞书侧单向同步成员信息和部门信息\n

    \n

    启用飞书集成后,成员和部门信息以飞书侧为准,因此无法保留原有的成员/小组信息,相关权限信息(子管理员权限、文件权限、列权限等)会丢失,请管理员知悉并做好准备工作

    \n
  • \n
", @@ -11613,9 +12055,9 @@ "zh_HK": "App Secret" }, "lark_integration_step2_content": { - "zh_CN": "请在飞书开发者后台找到应用凭证并填至下方 如何填写?", - "en_US": "Please find the app credentials in the Lark developer backend and fill in the form below How to fill in?", - "zh_HK": "請在飛書開發者後台找到應用憑證並填至下方 如何填寫? " + "zh_CN": "请在飞书开发者后台找到应用凭证并填至下方 如何填写?", + "en_US": "Please find the app credentials in the Lark developer backend and fill in the form below How to fill in?", + "zh_HK": "請在飛書開發者後台找到應用憑證並填至下方 如何填寫? " }, "lark_integration_step2_next": { "zh_CN": "生成路由地址", @@ -11638,9 +12080,9 @@ "zh_HK": "我已在飛書側完成上述地址的配置" }, "lark_integration_step3_content": { - "zh_CN": "请将下方路由地址填写至飞书应用里,实现飞书内免密登录维格表等相关功能 如何填写?", - "en_US": "Please fill in the following routing address into the Lark application to achieve the function of password-free login to vikadata and other related functions in Lark How to fill in?", - "zh_HK": "請將下方路由地址填寫至飛書應用裡,實現飛書內免密登錄維格表等相關功能 如何填寫? " + "zh_CN": "请将下方路由地址填写至飞书应用里,实现飞书内免密登录维格表等相关功能 如何填写?", + "en_US": "Please fill in the following routing address into the Lark application to achieve the function of password-free login to vikadata and other related functions in Lark How to fill in?", + "zh_HK": "請將下方路由地址填寫至飛書應用裡,實現飛書內免密登錄維格表等相關功能 如何填寫? " }, "lark_integration_step3_desktop": { "zh_CN": "桌面端主页地址", @@ -11673,9 +12115,9 @@ "zh_HK": "事件訂閱" }, "lark_integration_step4_content": { - "zh_CN": "请在飞书开发者后台找到事件订阅的凭证填至下方 如何填写?", - "en_US": "Please find the app credentials in the Lark developer backend and fill in the form below How to fill in?", - "zh_HK": "請在飛書開發者後台找到事件訂閱的憑證填至下方 如何填寫? " + "zh_CN": "请在飞书开发者后台找到事件订阅的凭证填至下方 如何填写?", + "en_US": "Please find the app credentials in the Lark developer backend and fill in the form below How to fill in?", + "zh_HK": "請在飛書開發者後台找到事件訂閱的憑證填至下方 如何填寫? " }, "lark_integration_step4_encryptkey": { "zh_CN": "Encrypt Key", @@ -11703,9 +12145,9 @@ "zh_HK": "事件驗證" }, "lark_integration_step5_content": { - "zh_CN": "请将下方信息填写至飞书自建应用内,实现订阅维格表的事件 如何填写?", - "en_US": "Please fill in the information below to subscribe to vikatdata's events in Lark's self-built application How to fill in?", - "zh_HK": "請將下方信息填寫至飛書自建應用內,實現訂閱維格表的事件 如何填寫? " + "zh_CN": "请将下方信息填写至飞书自建应用内,实现订阅维格表的事件 如何填写?", + "en_US": "Please fill in the information below to subscribe to vikatdata's events in Lark's self-built application How to fill in?", + "zh_HK": "請將下方信息填寫至飛書自建應用內,實現訂閱維格表的事件 如何填寫? " }, "lark_integration_step5_error_content": { "zh_CN": "
    \n
  • 1、请求网址是否粘贴错误;
  • \n
  • 2、步骤4「事件订阅」是否正确输入Encrypt Key 或 Verification Token
  • \n
", @@ -11782,6 +12224,12 @@ "en_US": "This Space's contacts are managed by the Lark integration. Please go to the Lark admin backend to edit contacts. ", "zh_HK": "成員列表會實時跟飛書通訊錄的組織架構保持同步,請在飛書側進行此操作(企業管理 > 添加團隊成員)" }, + "lark_single_record_comment_mentioned": { + "zh_CN": "**维格表: **{nodeName}\n**记录: **{recordTitle}\n\n\"{commentContent}\"\n" + }, + "lark_single_record_comment_mentioned_title": { + "zh_CN": "**有人在评论中提及你**" + }, "lark_single_record_member_mention": { "zh_CN": "**记录: **{recordTitle}\n**提及人: **{memberName}\n**维格表: **{nodeName}", "en_US": "**Record: **{recordTitle}\n**Mentioned by: **{memberName}\n**Datasheet: **{nodeName}", @@ -11790,12 +12238,7 @@ "lark_single_record_member_mention_title": { "zh_CN": "**有人在记录中提及你**", "en_US": "** You're mentioned in a record**", - "zh_HK": "**有人在記錄中提及你**", - "notifications": { - "social_templates copy": [ - "recy1zceaxmVN" - ] - } + "zh_HK": "**有人在記錄中提及你**" }, "lark_subscribed_record_cell_updated": { "zh_CN": "**记录:** {recordTitle}\n**原内容:** {oldDisplayValue}\n**修改后的内容:** {newDisplayValue}\n**修改人:** {memberName}\n**维格表:** {nodeName}", @@ -11805,12 +12248,7 @@ "lark_subscribed_record_cell_updated_title": { "zh_CN": "**关注的记录被修改**", "en_US": "**Someone has changed the record you are watching in**", - "zh_HK": "**關注的記錄被修改**", - "notifications": { - "social_templates copy": [ - "recPg3T3IameG" - ] - } + "zh_HK": "**關注的記錄被修改**" }, "lark_subscribed_record_commented": { "zh_CN": "**记录:** {recordTitle}\n**评论的内容:** {content}\n**评论人:** {memberName}\n**维格表:** {nodeName}", @@ -11820,12 +12258,7 @@ "lark_subscribed_record_commented_title": { "zh_CN": "**关注的记录有一条新评论**", "en_US": "**The record you are watching received a new comment**", - "zh_HK": "**關注的記錄有一條新評論**", - "notifications": { - "social_templates copy": [ - "recbhTobcvgFG" - ] - } + "zh_HK": "**關注的記錄有一條新評論**" }, "lark_version_enterprise": { "zh_CN": "飞书企业版", @@ -12040,7 +12473,6 @@ "loading": { "zh_CN": "加载中", "en_US": "Loading...", - "终端": "APP", "zh_HK": "加載中" }, "loading_file": { @@ -12114,7 +12546,6 @@ "login": { "zh_CN": "登录", "en_US": "Log in", - "终端": "APP", "zh_HK": "登錄" }, "login_failed": { @@ -12140,13 +12571,11 @@ "login_logo_slogan": { "zh_CN": "多维表格首创者,面向API的易用低代码平台", "en_US": "Low-code platform with API-first Database-Spreadsheet", - "终端": "APP", "zh_HK": "多維表格首創者,面向API的易用低代碼平台" }, "login_logo_slogan_mobile": { "zh_CN": "多维表格首创者", "en_US": "API-first Database-Spreadsheet", - "终端": "APP", "zh_HK": "多維表格首創者" }, "login_mobile_format_err": { @@ -12159,16 +12588,19 @@ "en_US": "Empty phone number", "zh_HK": "手機號不能為空" }, + "login_privacy_policy": { + "zh_CN": "《维格隐私政策》", + "en_US": "", + "zh_HK": "《維格隱私政策》" + }, "login_register": { "zh_CN": "登录/注册", "en_US": "Log in/Sign up", - "终端": "APP", "zh_HK": "登錄/註冊" }, "login_slogan": { "zh_CN": "面向API的易用低代码平台", "en_US": "API-first Database-Spreadsheet", - "终端": "APP", "zh_HK": "面嚮API的易用低代碼平臺" }, "login_status_expired_button": { @@ -12213,7 +12645,7 @@ }, "logout_warning": { "zh_CN": "由于你的账号管理了多人(大于或等于2人)的空间站,请在「空间站设置」页面将主管理员更换至他人后再进行注销操作", - "en_US": "Since your account is managed by more than one person (greater than or equal to 2 people), please change the main administrator to someone else on the \"Station Settings\" page before deleting.", + "en_US": "Since your account managed spaces with multiple members(more than or equal to 2 members), please change the main admin to someone else on the \"Space Settings\" page before deleting the account.", "zh_HK": "由於你的賬號管理了多人(大於或等於2人)的空間站,請在「空間站設置」頁面將主管理員更換至他人後再進行註銷操作" }, "long_time_no_operate": { @@ -12388,18 +12820,18 @@ }, "lookup_filter_condition_tip": { "zh_CN": "列类型被转换过,请重新配置筛选条件", - "en_US": "This column type has been converted. Please confirm the filter criteria", + "en_US": "This field type has been converted. Please confirm the filter criteria", "zh_HK": "列類型被轉換過,請重新配置篩選條件" }, "lookup_filter_waring": { "zh_CN": "本列的筛选列发生了类型转换,请检查。", - "en_US": "The filter column of this column has type conversion, please check.", + "en_US": "The filter column of this field has type conversion, please check.", "zh_HK": "本列的篩選列發生了類型轉換,請檢查。" }, "lookup_help": { - "zh_CN": "/help/manual-field-lookup/", - "en_US": "/help/manual-field-lookup/", - "zh_HK": "/help/manual-field-lookup/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-field-lookup", + "en_US": "https://help.vika.cn/docs/guide/manual-field-lookup", + "zh_HK": "https://help.vika.cn/docs/guide/manual-field-lookup" }, "lookup_link": { "zh_CN": "选择本表的神奇关联列", @@ -12533,7 +12965,6 @@ "mail": { "zh_CN": "邮箱", "en_US": "Email", - "终端": "APP", "zh_HK": "郵箱" }, "mail_invite_fail": { @@ -12553,9 +12984,13 @@ }, "main_admin_page_desc": { "zh_CN": "拥有空间站的最高管理权限,可以分配子管理员或转让空间站", - "en_US": "Admins have full access to the Space, such as assigning sub-admins and transferring ownership of the Space", + "en_US": "Admin have full access to the Space, such as assigning sub-admins and transferring ownership of the Space", "zh_HK": "擁有空間站的最高管理權限,可以分配子管理員或轉讓空間站" }, + "main_contain": { + "zh_CN": "主要包含内容", + "en_US": "Main contents" + }, "malawi": { "zh_CN": "马拉维", "en_US": "Malawi", @@ -12607,19 +13042,19 @@ }, "manage_role_empty_desc1": { "zh_CN": "你可以自由创建角色,并将任意部门/成员添加到一个或多个角色内,可以实现更灵活地权限控制,详情请查看帮助说明", - "en_US": "You can freely create roles and add any department / member to one or more roles to achieve more flexible permission control. For details, please see the help" + "en_US": "You can freely create roles and add any teams / members to one or more roles to achieve more flexible permission control. For details, please see the Learn more" }, "manage_role_empty_desc2": { "zh_CN": "例如:你可以创建一个“项目经理”的角色,把组织架构中项目经理岗位的成员都添加进去,这样就可以实现在成员列或者在记录的评论中直接选择“项目经理”这个角色并通知到他们了", - "en_US": "For example, you can create a role of \"project manager\" and add all the members of the project manager position in the organizational structure. In this way, you can directly select the role of \"project manager\" in the member column or in the recorded comments and notify them" + "en_US": "For example, you can create a role named \"project manager\" and add all project managers in your organization to this role. In this way, you can directly select the role \"project manager\" in the member field or in the record comments and notify them." }, "manage_role_empty_title": { "zh_CN": "欢迎使用角色", - "en_US": "Welcome to roles" + "en_US": "Welcome to the Role feature" }, "manage_role_header_desc": { "zh_CN": "你可以自由创建角色,并将任意部门/成员添加到一个或多个角色内,可以实现更灵活地权限控制。帮助文档", - "en_US": "You can freely create roles and add any department / member to one or more roles to achieve more flexible permission control help document" + "en_US": "You can freely create roles and add any teams / members to one or more roles to achieve more flexible permission control Learn more" }, "manage_team": { "zh_CN": "小组设置", @@ -12658,7 +13093,7 @@ }, "map_select_data": { "zh_CN": "选择一个视图来读取地理位置", - "en_US": "Select a view as data source", + "en_US": "Select a view as the data source", "zh_HK": "選擇一個視圖來讀取地理位置" }, "map_select_label_field": { @@ -12712,7 +13147,7 @@ }, "marketing_sub_title": { "zh_CN": "由维格表官方集成的第三方应用", - "en_US": "Vikadata brings your work tools together by integrating third-party applications. Integrations help connect your data and make vikadata even more useful.", + "en_US": "Brings your work tools together by integrating third-party applications. Integrations help connect your data.", "zh_HK": "由維格表官方集成的第三方應用" }, "marketplace_app_disable_succeed": { @@ -12727,27 +13162,27 @@ }, "marketplace_integration_app_dec_dingtalk": { "zh_CN": "

成功创建自建应用后,请完成空间站绑定,绑定后你的企业可以在钉钉上使用维格表。

\n\n

在钉钉上使用维格表自建应用,你可以

\n\n
  • 免登录,直接在钉钉工作台进入维格表空间站,访问空间站数据
  • 通过钉钉,收取空间站里的通知
  • 在钉钉设置应用可使用范围设置,并自动同步钉钉的通讯录
", - "en_US": "

After opening the app, please complete the space station bundle, and after bundling your team can use Vikadata on DingTalk.

\n\n

With Vikadata Assistant on DingTalk, you can:

\n\n
  • Access Vikadata's space station and access space station data directly on DingTalk without logging in;
  • Collect notifications from the space station through DingTalk;
  • Set app visibility in DingTalk and automatically sync DingTalk organization structure;
", + "en_US": "

After installing the integration, please complete the space bundle, and after bundling, your team can use Vikadata on DingTalk.

\n\n

With Vikadata Assistant on DingTalk, you can:

\n\n
  • Access Vikadata's space and access space data directly on DingTalk without logging in;
  • Receive notifications from the space through DingTalk;
  • Set app visibility on DingTalk and automatically sync DingTalk's organization structure;
", "zh_HK": "

成功創建自建應用後,請完成空間站綁定,綁定後你的企業可以在釘釘上使用維格表。

\n\n

在釘釘上使用維格表自建應用,你可以

\n\n
  • 免登錄,直接在釘釘工作台進入維格表空間站,訪問空間站數據
  • 通過釘釘,收取空間站裡的通知
  • 在釘釘設置應用可使用範圍設置,並自動同步釘釘的通訊錄
" }, "marketplace_integration_app_dec_feishu": { "zh_CN": "

开通应用后,请完成空间站绑定,绑定后你的团队可以在飞书上使用维格表。

\n\n

在飞书上使用维格表助手,你可以:

\n\n
  • 免登录,直接在飞书进入维格表空间站,访问空间站数据
  • 通过飞书,收取空间站里的通知
  • 在飞书设置应用可见范围,并自动同步飞书的组织架构
", - "en_US": "

After opening the app, please complete the space station bundle, and after bundling your team can use Vikadata on Lark.

\n\n

With Vikadata Assistant on Lark, you can:

\n\n
  • Access Vikadata's space station and access space station data directly on Lark without logging in;
  • Collect notifications from the space station through Lark;
  • Set app visibility in Lark and automatically sync Lark's organization structure;
", + "en_US": "

After installing the integration, please complete the space bundle, and after bundling, your team can use Vikadata on Lark.

\n\n

With Vikadata Assistant on Lark, you can:

\n\n
  • Access Vikadata's space and access space data directly on Lark without logging in;
  • Receive notifications from the space through Lark;
  • Set app visibility on Lark and automatically sync Lark's organization structure;
\n", "zh_HK": "

開通應用後,請完成空間站綁定,綁定後你的團隊可以在飛書上使用維格表。

\n\n

在飛書上使用維格表助手,你可以:

\n\n
  • 免登錄,直接在飛書進入維格表空間站,訪問空間站數據
  • 通過飛書,收取空間站裡的通知
  • 在飛書設置應用可見範圍,並自動同步飛書的組織架構
" }, "marketplace_integration_app_dec_feishu_html_str": { "zh_CN": "

开通应用后,请完成空间站绑定,绑定后你的团队可以在飞书上使用维格表。

\n\n

在飞书上使用维格表助手,你可以:

\n\n
  • 免登录,直接在飞书进入维格表空间站,访问空间站数据;
  • 通过飞书,收取空间站里的通知;
  • 在飞书设置应用可见范围,并自动同步飞书的组织架构;
", - "en_US": "

After opening the app, please complete the space station bundle, and after bundling your team can use Vikadata on Lark.

\n\n

With Vikadata Assistant on Lark, you can:

\n\n
  • Access Vikadata's space station and access space station data directly on Lark without logging in;
  • Collect notifications from the space station through Lark;
  • Set app visibility in Lark and automatically sync Lark's organization structure;
", + "en_US": "

After installing the integration, please complete the space bundle, and after bundling, your team can use Vikadata on Lark.

\n\n

With Vikadata Assistant on Lark, you can:

\n\n
  • Access Vikadata's space and access space data directly on Lark without logging in;
  • Receive notifications from the space through Lark;
  • Set app visibility on Lark and automatically sync Lark's organization structure;
\n", "zh_HK": "

開通應用後,請完成空間站綁定,綁定後你的團隊可以在飛書上使用維格表。

\n\n

在飛書上使用維格表助手,你可以:

\n\n
  • 免登錄,直接在飛書進入維格表空間站,訪問空間站數據;
  • 通過飛書,收取空間站裡的通知;
  • 在飛書設置應用可見範圍,並自動同步飛書的組織架構;
" }, "marketplace_integration_app_dec_office_preview": { "zh_CN": "

在维格表里提供无缝的 office 文件在线预览能力, 让你可以随时随地使用 PC 或手机查看 Excel、Word、PPT 等常见的 office 文件

\n\n
  • 支持在线预览 .doc、.docx、.xls、.xlsx、.ppt、.pptx 和 .pdf 格式的 office 文件
  • 桌面端和移动端同步支持对上述格式的文件预览\n
\n\n

补充说明:

\n
  • 该功能由「永中云转换」提供技术支持,维格表官方进行集成
  • 点击下方 “授权” 按钮,表示启用本应用,并同意「永中云转换」读取你将要预览的 office 文件
  • 如不再需要 office 预览功能,管理员可再次访问此页面进行停用操作
", - "en_US": "

Seamless online preview of office files in Vigor Table, allowing you to view common office files such as Excel, Word, PPT, etc. on your PC or cell phone anywhere, anytime

  • Online preview of .doc, .docx, .xls, .xlsx, .ppt, .pptx, and .pdf formatted office files
  • Desktop and mobile simultaneous support for file preview of the above formats\n
  • \n\n

    Additional notes:

  • This feature is powered by \"Yoncentric Cloud Conversion\" and officially integrated by Vigilometer
  • Click the \"Authorize\" button below to enable this app and agree that \"Yoncentric Cloud Conversion\" reads the office files you want to preview
  • If you no longer need the office preview feature, the administrator can visit this page again to disable it
  • ", + "en_US": "

    Smooth online preview of office files on datasheet, allowing you to view common office files such as Excel, Word, PPT, etc. on your desktop or mobile anywhere, anytime

    \n\n
      \n
    • Online preview of .doc, .docx, .xls, .xlsx, .ppt, .pptx, and .pdf formatted office files
    • \n
    • Desktop and mobile both support file previews of the above formats
    • \n
    \n\n

    Additional notes:

    \n
      \n
    • This feature is powered by \"Yoncentric Cloud Conversion\" and officially integrated
    • \n
    • Click the \"Enable\" button below to enable this integration and agree that \"Yoncentric Cloud Conversion\" reads the office files you want to preview
    • \n
    • If you no longer need the office files preview feature, the space admin can disable it on this page
    • \n
    ", "zh_HK": "

    在維格表裡提供無縫的 office 文件在線預覽能力, 讓你可以隨時隨地使用 PC 或手機查看 Excel、Word、PPT 等常見的 office 文件

    \n\n
    • 支持在線預覽 .doc、.docx、.xls、.xlsx、.ppt、.pptx 和 .pdf 格式的 office 文件
    • 桌面端和移動端同步支持對上述格式的文件預覽\n
    \n\n

    補充說明:

    \n
    • 該功能由「永中云轉換」提供技術支持,維格表官方進行集成
    • 點擊下方 “授權” 按鈕,表示啟用本應用,並同意「永中云轉換」讀取你將要預覽的 office 文件
    • 如不再需要 office 預覽功能,管理員可再次訪問此頁面進行停用操作
    " }, "marketplace_integration_app_dec_wechatcp": { "zh_CN": "

    在企业微信上使用维格表应用,你可以

    \n\n
    • 免登录,直接在企业微信进入维格表空间站,访问空间站数据
    • 通过企业微信,收取空间站里的通知
    • 在企业微信设置应用可见范围,并同步企业微信的通讯录
    ", - "en_US": "Coming soon", + "en_US": "

    Using Vikadata on WeCom, you can

    \n\n
    • Access Vikadata's space and access space data directly on WeCom without logging in;
    • Receive notifications from the space through WeCom;
    • Set app visibility on WeCom and automatically sync WeCom's organization structure;
    ", "zh_HK": "

    在企業微信上使用維格表應用,你可以

    \n\n
    • 免登錄,直接在企業微信進入維格表空間站,訪問空間站數據
    • 通過企業微信,收取空間站裡的通知
    • 在企業微信設置應用可見範圍,並同步企業微信的通訊錄
    " }, "marketplace_integration_app_info_dingtalk": { @@ -12762,7 +13197,7 @@ }, "marketplace_integration_app_info_office_preview": { "zh_CN": "为当前空间站的「附件」类型维格列提供在线预览 office 文件的能力", - "en_US": "With this integration, you can easily preview Microsoft Office files in \"Attachment\" columns", + "en_US": "With this integration, you can easily preview Microsoft Office files in \"Attachment\" fields", "zh_HK": "為當前空間站的「附件」類型維格列提供在線預覽 office 文件的能力" }, "marketplace_integration_app_info_wecahtcp": { @@ -12827,7 +13262,7 @@ }, "marketplace_integration_btncard_btntext_open": { "zh_CN": "开通使用", - "en_US": "Install", + "en_US": "Enable", "zh_HK": "開通使用" }, "martinique": { @@ -12865,6 +13300,11 @@ "en_US": "The API usage of this space has reached the limit( ${usage}/${specification} ), and you can upgrade to get higher usage.", "zh_HK": "空間站的API用量已超出上限( ${usage}/${specification} ),升級可獲得更高用量。" }, + "max_audit_query_days": { + "zh_CN": "你正在使用${grade}空间站专属功能,升级后可以解锁该功能", + "en_US": "You are experiencing the ${grade} space exclusive feature, which will be unlocked when you upgrade", + "zh_HK": "你正在體驗${grade}空間站專屬功能,升級後可以解鎖該功能" + }, "max_calendar_views_in_space": { "zh_CN": "空间站的日历视图数上限为 ${specification} 个,当前已创建 ${usage} 个,升级可获得更高用量。", "en_US": "The maximum number of calendar views to be used in space is ${specification}. So far, ${usage} has been used, and you can upgrade to get higher usage.", @@ -12900,6 +13340,16 @@ "en_US": "The maximum number of Kanban views to be used in space is ${specification}. So far, ${usage} has been used, and you can upgrade to get higher usage.", "zh_HK": "空間站的看板視圖數上限為 ${specification} 個,當前已創建 ${usage} 個,升級可獲得更高用量。" }, + "max_mirror_nums": { + "zh_CN": "空间站的镜像数上限为 ${specification} 个,当前已创建 ${usage} 个,升级可获得更高用量。", + "en_US": "The maximum number of mirror to be used in space is ${specification}. So far, ${usage} has been used, and you can upgrade to get higher usage.", + "zh_HK": "空間站的神奇表單數上限為 ${specification} 個,當前已創建 ${usage} 個,升級可獲得更高用量。" + }, + "max_record_num_per_dst": { + "zh_CN": "当前支持的单表上限为 5w 行", + "en_US": "The current limit for a single datasheet is 5w rows", + "zh_HK": "當前支持的單表上限為 5w 行" + }, "max_records": { "zh_CN": "记录数上限:${count}条", "en_US": "Maximum: ${count}", @@ -12907,7 +13357,7 @@ }, "max_remain_record_activity_days": { "zh_CN": "空间站仅显示 ${specification} 天内的修改历史,升级后可查看更久的历史数据。", - "en_US": "空间站仅显示 ${specification} 天内的修改历史,升级后可查看更久的历史数据。", + "en_US": "This space supports viewing record activity within ${specification} days, upgrade to view more ", "zh_HK": "空間站僅顯示 ${specification} 天內的修改歷史,升級後可查看更久的歷史數據。" }, "max_remain_timemachine_days": { @@ -12921,7 +13371,7 @@ "zh_HK": "本空間站支持查看 ${specification} 天內的歷史文件,升級後可查看更久的歷史文件。" }, "max_rows_in_space": { - "zh_CN": "空间站记录数上限为 ${specification} 行,当前已创建 ${usage} 行,升级可获得更高用量。", + "zh_CN": "空间站总记录数上限为 ${specification} 行,当前已创建 ${usage} 行,升级可获得更高用量。", "en_US": "The maximum number of records in space is ${specification}. So far, ${usage} records have been created, and you can upgrade to get higher usage.", "zh_HK": "空間站記錄數上限為 ${specification} 行,當前已創建 ${usage} 行,升級可獲得更高用量。" }, @@ -12987,7 +13437,7 @@ }, "member_data_desc_of_field_number": { "zh_CN": "工作目录中所有文件(表格、仪表盘、神奇表单、镜像)的数量统计", - "en_US": "Quantity statistics of all files in the working catalog", + "en_US": "Numbers of all files in the catalog", "zh_HK": "工作目錄中所有文件(表格、儀錶盤、神奇表單、鏡像)的數量統計" }, "member_data_desc_of_member_number": { @@ -12997,7 +13447,7 @@ }, "member_data_desc_of_record_number": { "zh_CN": "工作目录中所有维格表中记录的数量统计,包括空白记录", - "en_US": "Quantity statistic in all datasheets of working catalog, including the blank record(s)", + "en_US": "Numbers of all records in the catalog,including blank record(s)", "zh_HK": "工作目錄中所有維格表中記錄的數量統計,包括空白記錄" }, "member_err": { @@ -13035,11 +13485,6 @@ "en_US": "Member does not exist", "zh_HK": "成員不存在" }, - "member_number": { - "zh_CN": "工号", - "en_US": "Member Number", - "zh_HK": "工號" - }, "member_per_space": { "zh_CN": "空间站成员", "en_US": "Space members", @@ -13148,7 +13593,6 @@ "message_code": { "zh_CN": "获取验证码", "en_US": "Send", - "终端": "APP", "zh_HK": "獲取驗證碼" }, "message_coping": { @@ -13183,7 +13627,7 @@ }, "message_display_count": { "zh_CN": "${count}条新消息", - "en_US": "${count} new messages", + "en_US": "${count} new message(s)", "zh_HK": "${count}條新消息" }, "message_exit_space_failed": { @@ -13302,9 +13746,9 @@ "zh_HK": "鏡像" }, "mirror_editor_label": { - "zh_CN": "在「只可更新」基础上,还可以增删记录", - "en_US": "In addition to \"Update-only\", can also add or delete records", - "zh_HK": "在「只可更新」基礎上,還可以增刪記錄" + "zh_CN": "在「只可更新」基础上,还可以删除记录和增删视图", + "en_US": "In addition to \"Update-only\", can also add or delete views and delete records", + "zh_HK": "在「只可更新」基礎上,還可以刪除記錄和增刪視圖" }, "mirror_from": { "zh_CN": "镜像来自:", @@ -13312,9 +13756,9 @@ "zh_HK": "鏡像來自:" }, "mirror_help_url": { - "zh_CN": "/help/manual-mirror/", - "en_US": "/help/manual-mirror/", - "zh_HK": "/help/manual-mirror/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-mirror", + "en_US": "https://help.vika.cn/docs/guide/manual-mirror", + "zh_HK": "https://help.vika.cn/docs/guide/manual-mirror" }, "mirror_manager_label": { "zh_CN": "在「可以编辑」基础上,还可以删除和配置镜像", @@ -13337,9 +13781,9 @@ "zh_HK": "生成該鏡像的視圖已被刪除,無法訪問鏡像" }, "mirror_uploader_label": { - "zh_CN": "在「只可阅读」基础上,还可以更新单元格内容", - "en_US": "Can read, can also update the cell content", - "zh_HK": "在「只可閱讀」基礎上,還可以更新單元格內容" + "zh_CN": "在「只可阅读」基础上,还可以新增和编辑记录", + "en_US": "Can read, add and edit records except deleting records", + "zh_HK": "在「只可閱讀」基礎上,還可以新增和編輯記錄" }, "missing_instruction_op_error": { "zh_CN": "提交数据时发生错误,请刷新重试,如有疑问请扫描右方的二维码添加客服,让我们来帮助你解决", @@ -13351,6 +13795,11 @@ "en_US": "Log in with password", "zh_HK": "帳號密碼登錄" }, + "mobile_function_over_limit_tip": { + "zh_CN": "须前往PC端升级后才可解锁该高级功能", + "en_US": "Available after upgrade", + "zh_HK": "須升級後解鎖功能和提高用量" + }, "mobile_show_allow": { "zh_CN": "显示成员手机号", "en_US": "Show phone numbers", @@ -13366,6 +13815,11 @@ "en_US": "For more \"Space Management\" settings, please access the web app on a computer", "zh_HK": "更多「空間站管理」的功能請前往電腦網頁端操作" }, + "mobile_usage_over_limit_tip": { + "zh_CN": "功能用量已超限,请前往PC端升级后可完成此操作", + "en_US": "Usage over limits. Please go to the PC to upgrade.", + "zh_HK": "功能用量已超限,請前往PC端升級後可完成此操作" + }, "modal_content_confirm_revoke_logout": { "zh_CN": "确认要恢复账号吗?", "en_US": "Confirm you want to recovery your account?", @@ -13576,6 +14030,10 @@ "en_US": "Morocco", "zh_HK": "摩洛哥" }, + "move": { + "zh_CN": "移动", + "en_US": "Move" + }, "move_favorite_node_fail": { "zh_CN": "移动节点失败,系统将会自动更新列表", "en_US": "Node moving failed. The system will update the list automatically. ", @@ -13583,7 +14041,24 @@ }, "move_node_modal_content": { "zh_CN": "移动后,文件的可见性可能会受到上级文件夹的影响,你可以查看 ${nodeSet} 状态了解详情", - "en_US": "After moving, the visibility of the file may be affected by the parent folder. You can check the ${nodeset} status for details" + "en_US": "After moving, the visibility of the file may be affected by the parent folder. You can check the ${nodeSet} status for details" + }, + "move_to": { + "zh_CN": "移动至", + "en_US": "Move to" + }, + "move_to_error_equal_parent": { + "zh_CN": "文件已在当前文件夹下方,请选择其他文件夹", + "en_US": "The file is under the current folder. Please select another folder" + }, + "move_to_modal_title": { + "zh_CN": "将「${name}」移动至当前文件夹下", + "en_US": "Move [${name}] to", + "zh_HK": "將【${name}】移動至" + }, + "move_to_success": { + "zh_CN": "移动成功", + "en_US": "Move succeeded" }, "mozambique": { "zh_CN": "莫桑比克", @@ -13770,10 +14245,7 @@ "en_US": "If you have not signed up yet, click \"sign up\" and bind the account", "zh_HK": "若還不是維格表用戶,點擊註冊帳號完成註冊再進行綁定" }, - "new_user_welcome_notify": { - "zh_CN": "欢迎加入维格表,我们为你准备了一份免费学习维格表的见面礼,点击这条消息即可跳转了解详情。" - }, - "new_user_welcome_notify": { + "new_user_welcom_notify": { "zh_CN": "欢迎加入维格表,我们为你准备了一份免费学习维格表的见面礼,点击这条消息即可跳转了解详情。" }, "new_view": { @@ -13809,7 +14281,6 @@ "next_step": { "zh_CN": "下一步", "en_US": "Next", - "终端": "APP", "zh_HK": "下一步" }, "nicaragua": { @@ -14174,7 +14645,7 @@ }, "node_permission_extend_desc": { "zh_CN": "权限继承上级文件夹", - "en_US": "Permissions inherit from parent folder", + "en_US": "Permissions inherit from the parent folder", "zh_HK": "權限繼承上級文件夾" }, "node_permission_has_been_changed": { @@ -14184,27 +14655,27 @@ }, "node_permission_item_tips_admin_he": { "zh_CN": "他是空间站管理员,可以管理此文件", - "en_US": "He is the administrator of the space and can manage this file", + "en_US": "The administrator of the space who can manage this file", "zh_HK": "他是空间站管理员,可以管理此文件" }, "node_permission_item_tips_admin_you": { "zh_CN": "你是空间站管理员,拥有此文件的可管理权限", - "en_US": "You are the administrator of the space station and have manageable rights to this file", + "en_US": "You are the administrator of the space and can manage this file", "zh_HK": "你是空間站管理員,擁有此文件的可管理權限" }, "node_permission_item_tips_file_he": { "zh_CN": "他是文件管理员,可以管理此文件", - "en_US": "He is the file administrator and can manage this file", + "en_US": "The file manager who can manage this file", "zh_HK": "他是文件管理员,可以管理此文件" }, "node_permission_item_tips_file_you": { "zh_CN": "你是文件管理员,拥有此文件的可管理权限", - "en_US": "You are a file administrator and have manageable permissions for this file", + "en_US": "You are the file manager and can manage this file", "zh_HK": "你是文件管理員,擁有此文件的可管理權限" }, "node_permission_item_tips_other_he": { "zh_CN": "他${role}数据", - "en_US": "He is ${role}", + "en_US": "The ${role} of this file", "zh_HK": "他${role}该文件" }, "node_permission_item_tips_other_he_edit": { @@ -14214,7 +14685,7 @@ }, "node_permission_item_tips_other_you": { "zh_CN": "你${role}数据", - "en_US": "You is ${role}", + "en_US": "You are the ${role} of this file", "zh_HK": "你${role}该文件" }, "node_permission_label_space_and_file": { @@ -14222,6 +14693,10 @@ "en_US": "Space & File Manager", "zh_HK": "空間站&文件管理員" }, + "node_permission_nums": { + "zh_CN": "空间站的文件权限使用数上限为 ${specification} 个,目前已开启 ${usage} 个,升级可获得更高用量。", + "en_US": "The maximum number of node permissions to be used in space is ${specification}. So far, ${usage} has been used, and you can upgrade to get higher usage." + }, "node_with_link_share_reminder": { "zh_CN": "当前分享的维格表存在关联其他维格表的内容,继续开启分享?", "en_US": "The current datasheet is associated with the contents of other datasheet. Do you want to continue sharing?", @@ -14413,9 +14888,9 @@ "zh_HK": "自定義單位" }, "number_field_format": { - "zh_CN": "格式", - "en_US": "Number format", - "zh_HK": "格式" + "zh_CN": "精度", + "en_US": "Precision", + "zh_HK": "精度" }, "number_field_symbol_placeholder": { "zh_CN": "请输入单位名称", @@ -14483,10 +14958,12 @@ "zh_HK": "未獲取驗證碼或已過期,請重新獲取" }, "offical_website_footer_nav_data": { - "zh_CN": "[{\n \"title\": \"产品\",\n \"lists\": [{\n \"name\": \"快速入门\",\n \"url\": \"/help/tutorial-1-quick-start/\"\n }, {\n \"name\": \"产品指南\",\n \"url\": \"/help/manual-1-what-is-vikadata/\"\n }, {\n \"name\": \"产品价格\",\n \"url\": \"/pricing/\"\n }, {\n \"name\": \"产品路线图\",\n \"url\": \"https://vika.cn/share/shrvp76H9lQBFLaRG1wSY/dstB1RtGAw5qMoqnnY/viw9giKo0IcT5\"\n }]\n}, {\n \"title\": \"关于我们\",\n \"lists\": [{\n \"name\": \"公司介绍\",\n \"url\": \"/company/\"\n }, {\n \"name\": \"加入我们\",\n \"url\": \"/join-us/\"\n }, {\n \"name\": \"媒体报道\",\n \"url\": \"/press/\"\n }, {\n \"name\": \"vika维格课堂\",\n \"url\": \"https://u.vika.cn/vikaxueyuan\"\n }, {\n \"name\": \"维格合伙人\",\n \"url\": \"/partners/\"\n }]\n}, {\n \"title\": \"解决方案\",\n \"lists\": [{\n \"name\": \"PMO项目群管理\",\n \"url\": \"/business-pmo/\"\n }, {\n \"name\": \"智慧电商运营\",\n \"url\": \"/ecommerce/\"\n }, {\n \"name\": \"CRM客户管理\",\n \"url\": \"/crm/\"\n }, {\n \"name\": \"HR人力资源管理\",\n \"url\": \"/hr/\"\n }, {\n \"name\": \"Scrum敏捷开发管理\",\n \"url\": \"/scrum/\"\n }, {\n \"name\": \"营销策划与市场运营\",\n \"url\": \"/marketing/\"\n }, {\n \"name\": \"OKR目标管理\",\n \"url\": \"/okr/\"\n }, {\n \"name\": \"教育培训管理\",\n \"url\": \"/education/\"\n }, {\n \"name\": \"智慧门店管理\",\n \"url\": \"/shop/\"\n }]\n}, {\n \"title\": \"支持\",\n \"lists\": [{\n \"name\": \"意见反馈\",\n \"url\": \"https://vika.cn/share/shrzw0miJVmSkJ7eBBa9k/fomx5qPX0JGPFvfEEP\"\n }, {\n \"name\": \"帮助中心\",\n \"url\": \"/help/\"\n }, {\n \"name\": \"开发者中心\",\n \"url\": \"/developers/\"\n }, {\n \"name\": \"服务条款\",\n \"url\": \"/service-agreement/\"\n }, {\n \"name\": \"隐私协议\",\n \"url\": \"/service-agreement/\"\n }, {\n \"name\": \"安全与合规\",\n \"url\": \"/security/\"\n }]\n}, {\n \"title\": \"服务\",\n \"lists\": [{\n \"name\": \"加入社群\",\n \"url\": \"/chatgroup/\"\n }, {\n \"name\": \"Github开源\",\n \"url\": \"https://github.com/vikadata\"\n }, {\n \"name\": \"预约演示\",\n \"url\": \"https://vika.cn/share/shrlRw3YWmqZ4BMl0B7qZ/fomUMtKMblNchCG7Ef\"\n }, {\n \"name\": \"专有云部署\",\n \"url\": \"https://vika.cn/share/shrVrGPclBql6w9ysUHzR/fomed5397fFJfdcRvL\"\n }, {\n \"name\": \"应用连接\",\n \"url\": \"/connections/\"\n }]\n}, {\n \"title\": \"联系我们\",\n \"lists\": [{\n \"name\": \"地址:深圳市南山区国信投资大厦1710\",\n \"url\": \"https://ditu.amap.com/place/B0FFJ14BJ7\"\n }, {\n \"name\": \"售前咨询:点击联系商务\",\n \"url\": \"https://vika.cn/share/shrlRw3YWmqZ4BMl0B7qZ/fomUMtKMblNchCG7Ef\"\n }, {\n \"name\": \"合作:bd@vikadata.com\",\n \"url\": \"mailto:bd@vikadata.com\"\n }, {\n \"name\": \"媒体:pr@vikadata.com\",\n \"url\": \"mailto:pr@vikadata.com\"\n }, {\n \"name\": \"招聘:hr@vikadata.com\",\n \"url\": \"mailto:hr@vikadata.com\"\n }]\n}]" + "zh_CN": "[{\n \"title\": \"产品\",\n \"lists\": [{\n \"name\": \"快速入门\",\n \"url\": \"https://help.vika.cn/docs/guide/tutorial-1-quick-start\"\n }, {\n \"name\": \"产品指南\",\n \"url\": \"https://help.vika.cn/docs/guide/manual-1-what-is-vikadata\"\n }, {\n \"name\": \"产品价格\",\n \"url\": \"/pricing/\"\n }, {\n \"name\": \"产品路线图\",\n \"url\": \"https://vika.cn/share/shrvp76H9lQBFLaRG1wSY/dstB1RtGAw5qMoqnnY/viw9giKo0IcT5\"\n }]\n}, {\n \"title\": \"关于我们\",\n \"lists\": [{\n \"name\": \"公司介绍\",\n \"url\": \"/company/\"\n }, {\n \"name\": \"加入我们\",\n \"url\": \"/join-us/\"\n }, {\n \"name\": \"媒体报道\",\n \"url\": \"/press/\"\n }, {\n \"name\": \"vika维格课堂\",\n \"url\": \"https://u.vika.cn/vikaxueyuan\"\n }, {\n \"name\": \"维格合伙人\",\n \"url\": \"/partners/\"\n }]\n}, {\n \"title\": \"解决方案\",\n \"lists\": [{\n \"name\": \"PMO项目群管理\",\n \"url\": \"/business-pmo/\"\n }, {\n \"name\": \"智慧电商运营\",\n \"url\": \"/ecommerce/\"\n }, {\n \"name\": \"CRM客户管理\",\n \"url\": \"/crm/\"\n }, {\n \"name\": \"HR人力资源管理\",\n \"url\": \"/hr/\"\n }, {\n \"name\": \"Scrum敏捷开发管理\",\n \"url\": \"/scrum/\"\n }, {\n \"name\": \"营销策划与市场运营\",\n \"url\": \"/marketing/\"\n }, {\n \"name\": \"OKR目标管理\",\n \"url\": \"/okr/\"\n }, {\n \"name\": \"教育培训管理\",\n \"url\": \"/education/\"\n }, {\n \"name\": \"智慧门店管理\",\n \"url\": \"/shop/\"\n }]\n}, {\n \"title\": \"支持\",\n \"lists\": [{\n \"name\": \"意见反馈\",\n \"url\": \"https://vika.cn/share/shrzw0miJVmSkJ7eBBa9k/fomx5qPX0JGPFvfEEP\"\n }, {\n \"name\": \"帮助中心\",\n \"url\": \"https://help.vika.cn/\"\n }, {\n \"name\": \"开发者中心\",\n \"url\": \"/developers/\"\n }, {\n \"name\": \"服务条款\",\n \"url\": \"/service-agreement/\"\n }, {\n \"name\": \"隐私协议\",\n \"url\": \"/service-agreement/\"\n }, {\n \"name\": \"安全与合规\",\n \"url\": \"/security/\"\n }]\n}, {\n \"title\": \"服务\",\n \"lists\": [{\n \"name\": \"加入社群\",\n \"url\": \"/chatgroup/\"\n }, {\n \"name\": \"Github开源\",\n \"url\": \"https://github.com/vikadata\"\n }, {\n \"name\": \"预约演示\",\n \"url\": \"https://vika.cn/share/shrlRw3YWmqZ4BMl0B7qZ/fomUMtKMblNchCG7Ef\"\n }, {\n \"name\": \"专有云部署\",\n \"url\": \"https://vika.cn/share/shrVrGPclBql6w9ysUHzR/fomed5397fFJfdcRvL\"\n }, {\n \"name\": \"应用连接\",\n \"url\": \"/connections/\"\n }]\n}, {\n \"title\": \"联系我们\",\n \"lists\": [{\n \"name\": \"地址:深圳市南山区国信投资大厦1710\",\n \"url\": \"https://ditu.amap.com/place/B0FFJ14BJ7\"\n }, {\n \"name\": \"售前咨询:点击联系商务\",\n \"url\": \"https://vika.cn/share/shrlRw3YWmqZ4BMl0B7qZ/fomUMtKMblNchCG7Ef\"\n }, {\n \"name\": \"合作:bd@vikadata.com\",\n \"url\": \"mailto:bd@vikadata.com\"\n }, {\n \"name\": \"媒体:pr@vikadata.com\",\n \"url\": \"mailto:pr@vikadata.com\"\n }, {\n \"name\": \"招聘:hr@vikadata.com\",\n \"url\": \"mailto:hr@vikadata.com\"\n }]\n}]", + "en_US": "[{\n \"title\": \"产品\",\n \"lists\": [{\n \"name\": \"快速入门\",\n \"url\": \"https://help.vika.cn/docs/guide/tutorial-1-quick-start\"\n }, {\n \"name\": \"产品指南\",\n \"url\": \"https://help.vika.cn/docs/guide/manual-1-what-is-vikadata\"\n }, {\n \"name\": \"产品价格\",\n \"url\": \"/pricing/\"\n }, {\n \"name\": \"产品路线图\",\n \"url\": \"https://vika.cn/share/shrvp76H9lQBFLaRG1wSY/dstB1RtGAw5qMoqnnY/viw9giKo0IcT5\"\n }]\n}, {\n \"title\": \"关于我们\",\n \"lists\": [{\n \"name\": \"公司介绍\",\n \"url\": \"/company/\"\n }, {\n \"name\": \"加入我们\",\n \"url\": \"/join-us/\"\n }, {\n \"name\": \"媒体报道\",\n \"url\": \"/press/\"\n }, {\n \"name\": \"vika维格课堂\",\n \"url\": \"https://u.vika.cn/vikaxueyuan\"\n }, {\n \"name\": \"维格合伙人\",\n \"url\": \"/partners/\"\n }]\n}, {\n \"title\": \"解决方案\",\n \"lists\": [{\n \"name\": \"PMO项目群管理\",\n \"url\": \"/business-pmo/\"\n }, {\n \"name\": \"智慧电商运营\",\n \"url\": \"/ecommerce/\"\n }, {\n \"name\": \"CRM客户管理\",\n \"url\": \"/crm/\"\n }, {\n \"name\": \"HR人力资源管理\",\n \"url\": \"/hr/\"\n }, {\n \"name\": \"Scrum敏捷开发管理\",\n \"url\": \"/scrum/\"\n }, {\n \"name\": \"营销策划与市场运营\",\n \"url\": \"/marketing/\"\n }, {\n \"name\": \"OKR目标管理\",\n \"url\": \"/okr/\"\n }, {\n \"name\": \"教育培训管理\",\n \"url\": \"/education/\"\n }, {\n \"name\": \"智慧门店管理\",\n \"url\": \"/shop/\"\n }]\n}, {\n \"title\": \"支持\",\n \"lists\": [{\n \"name\": \"意见反馈\",\n \"url\": \"https://vika.cn/share/shrzw0miJVmSkJ7eBBa9k/fomx5qPX0JGPFvfEEP\"\n }, {\n \"name\": \"帮助中心\",\n \"url\": \"https://help.vika.cn/\"\n }, {\n \"name\": \"开发者中心\",\n \"url\": \"/developers/\"\n }, {\n \"name\": \"服务条款\",\n \"url\": \"/service-agreement/\"\n }, {\n \"name\": \"隐私协议\",\n \"url\": \"/service-agreement/\"\n }, {\n \"name\": \"安全与合规\",\n \"url\": \"/security/\"\n }]\n}, {\n \"title\": \"服务\",\n \"lists\": [{\n \"name\": \"加入社群\",\n \"url\": \"/chatgroup/\"\n }, {\n \"name\": \"Github开源\",\n \"url\": \"https://github.com/vikadata\"\n }, {\n \"name\": \"预约演示\",\n \"url\": \"https://vika.cn/share/shrlRw3YWmqZ4BMl0B7qZ/fomUMtKMblNchCG7Ef\"\n }, {\n \"name\": \"专有云部署\",\n \"url\": \"https://vika.cn/share/shrVrGPclBql6w9ysUHzR/fomed5397fFJfdcRvL\"\n }, {\n \"name\": \"应用连接\",\n \"url\": \"/connections/\"\n }]\n}, {\n \"title\": \"联系我们\",\n \"lists\": [{\n \"name\": \"地址:深圳市南山区国信投资大厦1710\",\n \"url\": \"https://ditu.amap.com/place/B0FFJ14BJ7\"\n }, {\n \"name\": \"售前咨询:点击联系商务\",\n \"url\": \"https://vika.cn/share/shrlRw3YWmqZ4BMl0B7qZ/fomUMtKMblNchCG7Ef\"\n }, {\n \"name\": \"合作:bd@vikadata.com\",\n \"url\": \"mailto:bd@vikadata.com\"\n }, {\n \"name\": \"媒体:pr@vikadata.com\",\n \"url\": \"mailto:pr@vikadata.com\"\n }, {\n \"name\": \"招聘:hr@vikadata.com\",\n \"url\": \"mailto:hr@vikadata.com\"\n }]\n}]" }, "offical_website_nav_data": { - "zh_CN": "[{\n \"name\": \"产品\",\n \"href\": \"/login\",\n \"children\": [\n {\n \"name\": \"vika维格表 Overview\",\n \"href\": \"/login/\"\n },\n {\n \"name\": \"应用连接 Integration\",\n \"href\": \"/connections/\"\n },\n {\n \"name\": \"客户端(Beta) Download\",\n \"href\": \"/download/\"\n }\n ]\n}, {\n \"name\": \"解决方案\",\n \"href\": \"/\",\n \"children\": [{\n \"name\": \"PMO项目群管理\",\n \"href\": \"/business-pmo/\"\n }, {\n \"name\": \"智慧电商运营\",\n \"href\": \"/ecommerce/\"\n }, {\n \"name\": \"CRM客户管理\",\n \"href\": \"/crm/\"\n }, {\n \"name\": \"HR人力资源管理\",\n \"href\": \"/hr/\"\n }, {\n \"name\": \"Scrum敏捷开发管理\",\n \"href\": \"/scrum/\"\n }, {\n \"name\": \"营销策划与市场运营\",\n \"href\": \"/marketing/\"\n }, {\n \"name\": \"OKR目标管理\",\n \"href\": \"/okr/\"\n }, {\n \"name\": \"教育培训管理\",\n \"href\": \"/education/\"\n }, {\n \"name\": \"智慧门店管理\",\n \"href\": \"/shop/\"\n }, {\n \"name\": \"更多解决方案 >\",\n \"href\": \"/solutions/\"\n }]\n}, {\n \"name\": \"模板/案例\",\n \"href\": \"/template\",\n \"children\": [\n {\n \"name\": \"模板中心 Tempaltes\",\n \"href\": \"/template\"\n },\n {\n \"name\": \"客户案例 Customers\",\n \"href\": \"/cases/\"\n }\n ]\n},\n {\n \"name\": \"首页\",\n \"href\": \"/?home=1\"\n },\n {\n \"name\": \"学习资源\",\n \"href\": \"https://edu.vika.cn/index\",\n \"children\": [\n {\n \"name\": \"快速入门 Tutorials\",\n \"href\": \"/help/tutorial/\"\n },\n {\n \"name\": \"产品手册 Manual\",\n \"href\": \"/help/manual/\"\n },\n {\n \"name\": \"维格课堂 Vika Education\",\n \"href\": \"https://edu.vika.cn/\"\n },\n {\n \"name\": \"维格社区 Vika BBS\",\n \"href\": \"https://bbs.vika.cn/\"\n },\n {\n \"name\": \"第三方连接 Integration\",\n \"href\": \"/help/connection/\"\n },\n {\n \"name\": \"开发者中心 Developer Center\",\n \"href\": \"/developers/\"\n },\n {\n \"name\": \"常见问题 FAQ\",\n \"href\": \"/help/questions/\"\n }\n ]\n }]" + "zh_CN": "[{\n \"name\": \"产品\",\n \"href\": \"/login\",\n \"children\": [\n {\n \"name\": \"vika维格表 Overview\",\n \"href\": \"/login/\"\n },\n {\n \"name\": \"应用连接 Integration\",\n \"href\": \"/connections/\"\n },\n {\n \"name\": \"客户端(Beta) Download\",\n \"href\": \"/download/\"\n }\n ]\n}, {\n \"name\": \"解决方案\",\n \"href\": \"/\",\n \"children\": [{\n \"name\": \"PMO项目群管理\",\n \"href\": \"/business-pmo/\"\n }, {\n \"name\": \"智慧电商运营\",\n \"href\": \"/ecommerce/\"\n }, {\n \"name\": \"CRM客户管理\",\n \"href\": \"/crm/\"\n }, {\n \"name\": \"HR人力资源管理\",\n \"href\": \"/hr/\"\n }, {\n \"name\": \"Scrum敏捷开发管理\",\n \"href\": \"/scrum/\"\n }, {\n \"name\": \"营销策划与市场运营\",\n \"href\": \"/marketing/\"\n }, {\n \"name\": \"OKR目标管理\",\n \"href\": \"/okr/\"\n }, {\n \"name\": \"教育培训管理\",\n \"href\": \"/education/\"\n }, {\n \"name\": \"智慧门店管理\",\n \"href\": \"/shop/\"\n }, {\n \"name\": \"更多解决方案 >\",\n \"href\": \"/solutions/\"\n }]\n}, {\n \"name\": \"模板/案例\",\n \"href\": \"/template\",\n \"children\": [\n {\n \"name\": \"模板中心 Tempaltes\",\n \"href\": \"/template\"\n },\n {\n \"name\": \"客户案例 Customers\",\n \"href\": \"/cases/\"\n }\n ]\n},\n {\n \"name\": \"首页\",\n \"href\": \"/?home=1\"\n },\n {\n \"name\": \"学习资源\",\n \"href\": \"https://edu.vika.cn/index\",\n \"children\": [\n {\n \"name\": \"快速入门 Tutorials\",\n \"href\": \"https://help.vika.cn/docs/guide/tutorial\"\n },\n {\n \"name\": \"产品手册 Manual\",\n \"href\": \"https://help.vika.cn/docs/guide/manual\"\n },\n {\n \"name\": \"维格课堂 Vika Education\",\n \"href\": \"https://edu.vika.cn/\"\n },\n {\n \"name\": \"维格社区 Vika BBS\",\n \"href\": \"https://bbs.vika.cn/\"\n },\n {\n \"name\": \"第三方连接 Integration\",\n \"href\": \"https://help.vika.cn/docs/guide/connection\"\n },\n {\n \"name\": \"开发者中心 Developer Center\",\n \"href\": \"/developers/\"\n },\n {\n \"name\": \"常见问题 FAQ\",\n \"href\": \"https://help.vika.cn/docs/guide/questions\"\n }\n ]\n }]", + "en_US": "[{\n \"name\": \"产品\",\n \"href\": \"/login\",\n \"children\": [\n {\n \"name\": \"vika维格表 Overview\",\n \"href\": \"/login/\"\n },\n {\n \"name\": \"应用连接 Integration\",\n \"href\": \"/connections/\"\n },\n {\n \"name\": \"客户端(Beta) Download\",\n \"href\": \"/download/\"\n }\n ]\n}, {\n \"name\": \"解决方案\",\n \"href\": \"/\",\n \"children\": [{\n \"name\": \"PMO项目群管理\",\n \"href\": \"/business-pmo/\"\n }, {\n \"name\": \"智慧电商运营\",\n \"href\": \"/ecommerce/\"\n }, {\n \"name\": \"CRM客户管理\",\n \"href\": \"/crm/\"\n }, {\n \"name\": \"HR人力资源管理\",\n \"href\": \"/hr/\"\n }, {\n \"name\": \"Scrum敏捷开发管理\",\n \"href\": \"/scrum/\"\n }, {\n \"name\": \"营销策划与市场运营\",\n \"href\": \"/marketing/\"\n }, {\n \"name\": \"OKR目标管理\",\n \"href\": \"/okr/\"\n }, {\n \"name\": \"教育培训管理\",\n \"href\": \"/education/\"\n }, {\n \"name\": \"智慧门店管理\",\n \"href\": \"/shop/\"\n }, {\n \"name\": \"更多解决方案 >\",\n \"href\": \"/solutions/\"\n }]\n}, {\n \"name\": \"模板/案例\",\n \"href\": \"/template\",\n \"children\": [\n {\n \"name\": \"模板中心 Tempaltes\",\n \"href\": \"/template\"\n },\n {\n \"name\": \"客户案例 Customers\",\n \"href\": \"/cases/\"\n }\n ]\n},\n {\n \"name\": \"首页\",\n \"href\": \"/?home=1\"\n },\n {\n \"name\": \"学习资源\",\n \"href\": \"https://edu.vika.cn/index\",\n \"children\": [\n {\n \"name\": \"快速入门 Tutorials\",\n \"href\": \"https://help.vika.cn/docs/guide/tutorial\"\n },\n {\n \"name\": \"产品手册 Manual\",\n \"href\": \"https://help.vika.cn/docs/guide/manual\"\n },\n {\n \"name\": \"维格课堂 Vika Education\",\n \"href\": \"https://edu.vika.cn/\"\n },\n {\n \"name\": \"维格社区 Vika BBS\",\n \"href\": \"https://bbs.vika.cn/\"\n },\n {\n \"name\": \"第三方连接 Integration\",\n \"href\": \"https://help.vika.cn/docs/guide/connection\"\n },\n {\n \"name\": \"开发者中心 Developer Center\",\n \"href\": \"/developers/\"\n },\n {\n \"name\": \"常见问题 FAQ\",\n \"href\": \"https://help.vika.cn/docs/guide/questions\"\n }\n ]\n }]" }, "office_preview": { "zh_CN": "Office 文件预览", @@ -14495,12 +14972,12 @@ }, "office_preview_app_desc": { "zh_CN": "

    在维格表里提供无缝的 office 文件在线预览能力, 让你可以随时随地使用 PC 或手机查看 Excel、Word、PPT 等常见的 office 文件

    \n\n
    • 支持在线预览 .doc、.docx、.xls、.xlsx、.ppt、.pptx 和 .pdf 格式的 office 文件
    • 桌面端和移动端同步支持对上述格式的文件预览\n
    \n\n

    补充说明:

    \n
    • 该功能由「永中云转换」提供技术支持,维格表官方进行集成
    • 点击下方 “授权” 按钮,表示启用本应用,并同意「永中云转换」读取你将要预览的 office 文件
    • 如不再需要 office 预览功能,管理员可再次访问此页面进行停用操作
    ", - "en_US": "

    Seamless online preview of office files in Vigor Table, allowing you to view common office files such as Excel, Word, PPT, etc. on your PC or cell phone anywhere, anytime

  • Online preview of .doc, .docx, .xls, .xlsx, .ppt, .pptx, and .pdf formatted office files
  • Desktop and mobile simultaneous support for file preview of the above formats\n
  • \n\n

    Additional notes:

  • This feature is powered by \"Yoncentric Cloud Conversion\" and officially integrated by Vigilometer
  • Click the \"Authorize\" button below to enable this app and agree that \"Yoncentric Cloud Conversion\" reads the office files you want to preview
  • If you no longer need the office preview feature, the administrator can visit this page again to disable it
  • ", + "en_US": "

    Smooth online preview of office files on datasheet, allowing you to view common office files such as Excel, Word, PPT, etc. on your desktop or mobile anywhere, anytime

    \n\n
      \n
    • Online preview of .doc, .docx, .xls, .xlsx, .ppt, .pptx, and .pdf formatted office files
    • \n
    • Desktop and mobile both support file previews of the above formats
    • \n
    \n\n

    Additional notes:

    \n
      \n
    • This feature is powered by \"Yoncentric Cloud Conversion\" and officially integrated
    • \n
    • Click the \"Enable\" button below to enable this integration and agree that \"Yoncentric Cloud Conversion\" reads the office files you want to preview
    • \n
    • If you no longer need the office files preview feature, the space admin can disable it on this page
    • \n
    ", "zh_HK": "

    在維格表裡提供無縫的 office 文件在線預覽能力, 讓你可以隨時隨地使用 PC 或手機查看 Excel、Word、PPT 等常見的 office 文件

    \n\n
    • 支持在線預覽 .doc、.docx、.xls、.xlsx、.ppt、.pptx 和 .pdf 格式的 office 文件
    • 桌面端和移動端同步支持對上述格式的文件預覽\n
    \n\n

    補充說明:

    \n
    • 該功能由「永中云轉換」提供技術支持,維格表官方進行集成
    • 點擊下方 “授權” 按鈕,表示啟用本應用,並同意「永中云轉換」讀取你將要預覽的 office 文件
    • 如不再需要 office 預覽功能,管理員可再次訪問此頁面進行停用操作
    " }, "office_preview_app_intro": { "zh_CN": "为当前空间站的「附件」类型维格列提供在线预览 office 文件的能力", - "en_US": "With this integration, you can easily preview Microsoft Office files in \"Attachment\" columns", + "en_US": "With this integration, you can easily preview Microsoft Office files in \"Attachment\" fields", "zh_HK": "為當前空間站的「附件」類型維格列提供在線預覽 office 文件的能力" }, "office_preview_app_notice": { @@ -14574,13 +15051,33 @@ "zh_HK": "官網" }, "official_website_partners": { - "zh_CN": "[{\n \"name\": \"五源资本\",\n \"url\": \"https://www.5ycap.com/\"\n}, {\n \"name\": \"IDG资本\",\n \"url\": \"https://cn.idgcapital.com/\"\n}, {\n \"name\": \"天图资本\",\n \"url\": \"http://www.tiantu.com.cn/cn/index.aspx\"\n}, {\n \"name\": \"软族科技\",\n \"url\": \"https://ruanzu.com/\"\n}, {\n \"name\": \"惟客数据\",\n \"url\": \"https://wakedata.com/\"\n}, {\n \"name\": \"才云科技\",\n \"url\": \"https://caicloud.io/\"\n}, {\n \"name\": \"方云智能\",\n \"url\": \"https://www.ironz.com/\"\n}, {\n \"name\": \"SegmentFault思否\",\n \"url\": \"https://segmentfault.com/\"\n}, {\n \"name\": \"字符科技\",\n \"url\": \"http://www.alphabets.cn/\"\n}, {\n \"name\": \"ones.ai\",\n \"url\": \"https://ones.ai/\"\n}, {\n \"name\": \"集简云\",\n \"url\": \"https://jijyun.cn/\"\n}, {\n \"name\": \"粤湾商盟\",\n \"url\": \"http://www.gbabusiness.cn/\"\n},{\n \"name\": \"语鹦企服\",\n \"url\": \"https://crm.bytell.cn/\"\n}, {\n \"name\": \"ShowMeBug\",\n \"url\": \"https://www.showmebug.com/\"\n}, {\n \"name\": \"ucbug软件站\",\n \"url\": \"http://www.ucbug.com/\"\n}, {\n \"name\": \"flomo 浮墨笔记\",\n \"url\": \"https://flomoapp.com/\"\n}, {\n \"name\": \"高瓴资本\",\n \"url\": \"https://www.hillhousecap.com/zh-hans/\"\n}, {\n \"name\": \"靖亚资本\",\n \"url\": \"http://www.emventures.cn/\"\n}, {\n \"name\": \"AdsPower指纹浏览器\",\n \"url\": \"https://www.adspower.net/\"\n}, {\n \"name\": \"微伴助手\",\n \"url\": \"https://weibanzhushou.com/\"\n}, {\n \"name\": \"Tapdata\",\n \"url\": \"https://tapdata.net/\"\n}, {\n \"name\": \"CodeFun\",\n \"url\": \"https://code.fun/\"\n}]" + "zh_CN": "[{\n \"name\": \"五源资本\",\n \"url\": \"https://www.5ycap.com/\"\n}, {\n \"name\": \"IDG资本\",\n \"url\": \"https://cn.idgcapital.com/\"\n}, {\n \"name\": \"天图资本\",\n \"url\": \"http://www.tiantu.com.cn/\"\n}, {\n \"name\": \"惟客数据\",\n \"url\": \"https://wakedata.com/\"\n}, {\n \"name\": \"方云智能\",\n \"url\": \"https://www.farcloud.com/\"\n}, {\n \"name\": \"SegmentFault思否\",\n \"url\": \"https://segmentfault.com/\"\n}, {\n \"name\": \"字符科技\",\n \"url\": \"http://www.alphabets.cn/\"\n}, {\n \"name\": \"ones.ai\",\n \"url\": \"https://ones.ai/\"\n}, {\n \"name\": \"集简云\",\n \"url\": \"https://jijyun.cn/\"\n}, {\n \"name\": \"粤湾商盟\",\n \"url\": \"http://www.gbabusiness.cn/\"\n},{\n \"name\": \"语鹦企服\",\n \"url\": \"https://crm.bytell.cn/\"\n}, {\n \"name\": \"ShowMeBug\",\n \"url\": \"https://www.showmebug.com/\"\n}, {\n \"name\": \"ucbug软件站\",\n \"url\": \"http://www.ucbug.com/\"\n}, {\n \"name\": \"flomo 浮墨笔记\",\n \"url\": \"https://flomoapp.com/\"\n}, {\n \"name\": \"高瓴资本\",\n \"url\": \"https://www.hillhousecap.com/zh-hans/\"\n}, {\n \"name\": \"靖亚资本\",\n \"url\": \"http://www.emventures.cn/\"\n}, {\n \"name\": \"AdsPower指纹浏览器\",\n \"url\": \"https://www.adspower.net/\"\n}, {\n \"name\": \"微伴助手\",\n \"url\": \"https://weibanzhushou.com/\"\n}, {\n \"name\": \"Tapdata\",\n \"url\": \"https://tapdata.net/\"\n}, {\n \"name\": \"CodeFun\",\n \"url\": \"https://code.fun/\"\n}]", + "en_US": "[{\n \"name\": \"五源资本\",\n \"url\": \"https://www.5ycap.com/\"\n}, {\n \"name\": \"IDG资本\",\n \"url\": \"https://cn.idgcapital.com/\"\n}, {\n \"name\": \"天图资本\",\n \"url\": \"http://www.tiantu.com.cn/\"\n}, {\n \"name\": \"惟客数据\",\n \"url\": \"https://wakedata.com/\"\n}, {\n \"name\": \"方云智能\",\n \"url\": \"https://www.farcloud.com/\"\n}, {\n \"name\": \"SegmentFault思否\",\n \"url\": \"https://segmentfault.com/\"\n}, {\n \"name\": \"字符科技\",\n \"url\": \"http://www.alphabets.cn/\"\n}, {\n \"name\": \"ones.ai\",\n \"url\": \"https://ones.ai/\"\n}, {\n \"name\": \"集简云\",\n \"url\": \"https://jijyun.cn/\"\n}, {\n \"name\": \"粤湾商盟\",\n \"url\": \"http://www.gbabusiness.cn/\"\n},{\n \"name\": \"语鹦企服\",\n \"url\": \"https://crm.bytell.cn/\"\n}, {\n \"name\": \"ShowMeBug\",\n \"url\": \"https://www.showmebug.com/\"\n}, {\n \"name\": \"ucbug软件站\",\n \"url\": \"http://www.ucbug.com/\"\n}, {\n \"name\": \"flomo 浮墨笔记\",\n \"url\": \"https://flomoapp.com/\"\n}, {\n \"name\": \"高瓴资本\",\n \"url\": \"https://www.hillhousecap.com/zh-hans/\"\n}, {\n \"name\": \"靖亚资本\",\n \"url\": \"http://www.emventures.cn/\"\n}, {\n \"name\": \"AdsPower指纹浏览器\",\n \"url\": \"https://www.adspower.net/\"\n}, {\n \"name\": \"微伴助手\",\n \"url\": \"https://weibanzhushou.com/\"\n}, {\n \"name\": \"Tapdata\",\n \"url\": \"https://tapdata.net/\"\n}, {\n \"name\": \"CodeFun\",\n \"url\": \"https://code.fun/\"\n}]" }, "official_website_without_abbr": { "zh_CN": "官方网站", "en_US": "Official website", "zh_HK": "官方網站" }, + "og_keywords_content": { + "zh_CN": "维格表,vika,vikadata,维格智数,大数据,数字化,数字化转型,数据中台,业务中台,数据资产,数字化智能办公,远程办公,数据工作台,区块链,人工智能,多维表格,aPaaS,hpaPaaS,RAD,数据库应用,快速开发工具", + "en_US": "Vikadata, vika, vikadata, big data, digitization, digital transformation, data center, business center, data assets, digital smart office, remote office, data workbench, blockchain, artificial intelligence, multidimensional tables, aPaaS, hpaPaaS, RAD, database applications, rapid development tools", + "zh_HK": "維格表,vika,vikadata,維格智數,大數據,數字化,數字化轉型,數據中台,業務中台,數據資產,數字化智能辦公,遠程辦公,數據工作台,區塊鏈,人工智能,多維表格,aPaaS,hpaPaaS,RAD,數據庫應用,快速開發工具" + }, + "og_page_title": { + "zh_CN": "维格表", + "en_US": "vikadata" + }, + "og_product_description_content": { + "zh_CN": "维格表, 积木式多媒体数据表格, 维格表技术首创者, 数据整理神器, 让人人都是数据设计师", + "en_US": "Vikadata, building block multimedia datasheet, pioneer of multidimensional tables technology, data sorting artifact, making everyone a data designer", + "zh_HK": "維格表, 積木式多媒體數據表格, 維格表技術首創者, 數據整理神器, 讓人人都是數據設計師" + }, + "og_site_name_content": { + "zh_CN": "维格表", + "en_US": "Vikadata", + "zh_HK": "維格表" + }, "okay": { "zh_CN": "好的", "en_US": "OK", @@ -14642,7 +15139,7 @@ }, "open_auto_save_warn_content": { "zh_CN": "所有成员修改当前视图配置会自动保存并同步给其他成员。(视图配置包括:筛选、分组、排序、隐藏列、布局、样式等)", - "en_US": "All members under this view are automatically saved and synchronized to other members.", + "en_US": "All changes under this view are automatically saved and synchronized with other members.", "zh_HK": "所有成員修改當前視圖配置會自動保存並同步給其他成員。 (視圖配置包括:篩選、分組、排序、隱藏列、佈局、樣式等)" }, "open_auto_save_warn_title": { @@ -15055,9 +15552,9 @@ "zh_HK": "待處理記錄" }, "org_chart_setting": { - "zh_CN": "设置", + "zh_CN": "视图设置", "en_US": "Settings", - "zh_HK": "設置" + "zh_HK": "視圖設置" }, "org_chart_setting_field_deleted": { "zh_CN": "关联列被删除", @@ -15086,13 +15583,13 @@ }, "org_guide_desc": { "zh_CN": "更方便地搭建组织和业务架构,快速调整更方便", - "en_US": "Easier to build organization and business structure, and easier to adjust quickly", + "en_US": "Easier to build organizations and business structure, and easier to adjust quickly", "zh_HK": "更方便地搭建組織和業務架構,快速調整更方便" }, "organization_and_role": { - "zh_CN": "组织架构与角色", - "en_US": "Organization & Role", - "zh_HK": "組織架構與角色" + "zh_CN": "组织架构", + "en_US": "Organization", + "zh_HK": "組織架構" }, "orgin_plan_discount": { "zh_CN": "原方案抵扣", @@ -15111,7 +15608,7 @@ }, "other_equitys_desc": { "zh_CN": "根据不同的空间站订阅等级会有不同的其他权益限制", - "en_US": "Depending on the subscription level of the space station there will be different restrictions on other benefits", + "en_US": "Depending on the subscription plan of the space.There will be different restrictions on other benefits", "zh_HK": "根據不同的空間站訂閱等級會有不同的其他權益限制" }, "other_invitation_rule": { @@ -15126,7 +15623,7 @@ }, "other_view_desc": { "zh_CN": "根据不同的空间站订阅等级会有不同的高级视图创建限制", - "en_US": "Depending on the subscription level of the space station there are different restrictions on the creation of advanced views", + "en_US": "Depending on the subscription plan of the space.There are different restrictions on the creation of advanced views", "zh_HK": "根據不同的空間站訂閱等級會有不同的高級視圖創建限制" }, "other_views": { @@ -15212,7 +15709,6 @@ "password": { "zh_CN": "密码", "en_US": "Password", - "终端": "APP", "zh_HK": "密碼" }, "password_length_err": { @@ -15223,7 +15719,6 @@ "password_login": { "zh_CN": "密码登录", "en_US": "Log in with password", - "终端": "APP", "zh_HK": "密碼登錄" }, "password_not_identical_err": { @@ -15431,8 +15926,8 @@ "zh_HK": "權限更改成功" }, "permission_config_in_workbench_page": { - "zh_CN": "[{\"key\":0,\"title\":\"对表格的操作权限\",\"detail\":[{\"title\":\"编辑视图列表\",\"permissions\":[0,1,2]},{\"title\":\"编辑视图工具栏\",\"permissions\":[0,1,2]},{\"title\":\"导出视图数据\",\"permissions\":[0,1]},{\"title\":\"增删和编辑字段(列)\",\"permissions\":[0,1]},{\"title\":\"编辑维格列样式(列宽/统计栏/顺序)\",\"permissions\":[0,1,2]},{\"title\":\"编辑记录(增删/拖动)\",\"permissions\":[0,1,2]},{\"title\":\"编辑单元格(增删改数据)\",\"permissions\":[0,1,2,3]},{\"title\":\"发布评论\",\"permissions\":[0,1,2,3,4]},{\"title\":\" 撤销,重做\",\"permissions\":[0,1,2,3]}]},{\"key\":1,\"title\":\"对文件(夹)的操作权限\",\"detail\":[{\"title\":\"设置文件(夹)权限\",\"permissions\":[0,1]},{\"title\":\"新建文件(夹)\",\"permissions\":[0,1]},{\"title\":\"导入文件\",\"permissions\":[0,1]},{\"title\":\"导出文件\",\"permissions\":[0,1]},{\"title\":\"复制文件(当前文件&上级文件夹权限)\",\"permissions\":[0,1]},{\"title\":\"移动文件(当前文件&目标文件夹权限)\",\"permissions\":[0,1]},{\"title\":\"重命名文件(夹)\",\"permissions\":[0,1]},{\"title\":\"删除文件(夹)\",\"permissions\":[0,1]},{\"title\":\"分享文件(夹)\",\"permissions\":[0,1,2]},{\"title\":\"设置文件(夹)描述\",\"permissions\":[0,1]},{\"title\":\"保存为模板\",\"permissions\":[0,1]}]}]", - "en_US": "[{\"key\":0,\"title\":\"About Datasheets\",\"detail\":[{\"title\":\"Edit view settings\",\"permissions\":[0,1,2]},{\"title\":\"Edit view list\",\"permissions\":[0,1,2]},{\"title\":\"Export view data\",\"permissions\":[0,1]},{\"title\":\"Add/Delete/Configured fields\",\"permissions\":[0,1]},{\"title\":\"Edit field layout (width/statistics/order)\",\"permissions\":[0,1,2]},{\"title\":\"Edit records (add/delete/drag)\",\"permissions\":[0,1,2]},{\"title\":\"Edit cells (add/delete/update)\",\"permissions\":[0,1,2,3]},{\"title\":\"Undo/Redo actions\",\"permissions\":[0,1,2,3]},{\"title\":\"Post a comment\",\"permissions\":[0,1,2,3,4]}]},{\"key\":1,\"title\":\"About Files\",\"detail\":[{\"title\":\"Edit file permissions\",\"permissions\":[0,1]},{\"title\":\"Create files\",\"permissions\":[0,1]},{\"title\":\"Import files\",\"permissions\":[0,1]},{\"title\":\"Export files\",\"permissions\":[0,1]},{\"title\":\"Duplicate files (Need full access to the file and its folder)\",\"permissions\":[0,1]},{\"title\":\"Move files (Need full access to the file and its folder)\",\"permissions\":[0,1]},{\"title\":\"Rename files\",\"permissions\":[0,1]},{\"title\":\"Delete files\",\"permissions\":[0,1]},{\"title\":\"Share files\",\"permissions\":[0,1,2]},{\"title\":\"Add files description\",\"permissions\":[0,1]},{\"title\":\"Save as template\",\"permissions\":[0,1]}]}]", + "zh_CN": "[{\"key\":0,\"title\":\"对表格的操作权限\",\"detail\":[{\"title\":\"编辑视图列表\",\"permissions\":[0,1,2]},{\"title\":\"编辑视图工具栏\",\"permissions\":[0,1,2]},{\"title\":\"导出视图数据\",\"permissions\":[0,1]},{\"title\":\"增删和编辑字段(列)\",\"permissions\":[0,1]},{\"title\":\"编辑维格列样式(列宽/统计栏/顺序)\",\"permissions\":[0,1,2]},{\"title\":\"编辑记录(新增和修改)\",\"permissions\":[0,1,2,3]},{\"title\":\"删除记录\",\"permissions\":[0,1,2]},{\"title\":\"发布评论\",\"permissions\":[0,1,2,3,4]},{\"title\":\" 撤销,重做\",\"permissions\":[0,1,2,3]}]},{\"key\":1,\"title\":\"对文件(夹)的操作权限\",\"detail\":[{\"title\":\"设置文件(夹)权限\",\"permissions\":[0,1]},{\"title\":\"新建文件(夹)\",\"permissions\":[0,1]},{\"title\":\"导入文件\",\"permissions\":[0,1]},{\"title\":\"导出文件\",\"permissions\":[0,1]},{\"title\":\"复制文件(当前文件&上级文件夹权限)\",\"permissions\":[0,1]},{\"title\":\"移动文件(当前文件&目标文件夹权限)\",\"permissions\":[0,1]},{\"title\":\"重命名文件(夹)\",\"permissions\":[0,1]},{\"title\":\"删除文件(夹)\",\"permissions\":[0,1]},{\"title\":\"分享文件(夹)\",\"permissions\":[0,1,2]},{\"title\":\"设置文件(夹)描述\",\"permissions\":[0,1]},{\"title\":\"保存为模板\",\"permissions\":[0,1]}]}]", + "en_US": "[{\"key\":0,\"title\":\"About Datasheets\",\"detail\":[{\"title\":\"Edit view settings\",\"permissions\":[0,1,2]},{\"title\":\"Edit view list\",\"permissions\":[0,1,2]},{\"title\":\"Export view data\",\"permissions\":[0,1]},{\"title\":\"Add/Delete/Configure fields\",\"permissions\":[0,1]},{\"title\":\"Edit field layout (width/statistics/order)\",\"permissions\":[0,1,2]},{\"title\":\"Edit records (add/edit records)\",\"permissions\":[0,1,2,3]},{\"title\":\"Delete records\",\"permissions\":[0,1,2]},{\"title\":\"Undo/Redo actions\",\"permissions\":[0,1,2,3]},{\"title\":\"Post a comment\",\"permissions\":[0,1,2,3,4]}]},{\"key\":1,\"title\":\"About Files\",\"detail\":[{\"title\":\"Edit file permissions\",\"permissions\":[0,1]},{\"title\":\"Create files\",\"permissions\":[0,1]},{\"title\":\"Import files\",\"permissions\":[0,1]},{\"title\":\"Export files\",\"permissions\":[0,1]},{\"title\":\"Duplicate files (Need full access to the file and its folder)\",\"permissions\":[0,1]},{\"title\":\"Move files (Need full access to the file and its folder)\",\"permissions\":[0,1]},{\"title\":\"Rename files\",\"permissions\":[0,1]},{\"title\":\"Delete files\",\"permissions\":[0,1]},{\"title\":\"Share files\",\"permissions\":[0,1,2]},{\"title\":\"Add files description\",\"permissions\":[0,1]},{\"title\":\"Save as template\",\"permissions\":[0,1]}]}]", "zh_HK": "[{\"key\":0,\"title\":\"對錶格的操作權限\",\"detail\":[{\"title\":\"編輯視圖列表\",\"permissions\":[0,1,2]},{\"title\":\"編輯視圖工具欄\",\"permissions\":[0,1,2]},{\"title\":\"導出視圖數據\",\"permissions\":[0,1]},{\"title\":\"增刪和編輯字段(列)\",\"permissions\":[0,1]},{\"title\":\"編輯維格列樣式(列寬/統計欄/順序)\",\"permissions\":[0,1,2]},{\"title\":\"編輯記錄(增刪/拖動)\",\"permissions\":[0,1,2]},{\"title\":\"編輯單元格(增刪改數據)\",\"permissions\":[0,1,2,3]},{\"title\":\"發布評論\",\"permissions\":[0,1,2,3,4]},{\"title\":\" 撤銷,重做\",\"permissions\":[0,1,2,3]}]},{\"key\":1,\"title\":\"對文件(夾)的操作權限\",\"detail\":[{\"title\":\"設置文件(夾)權限\",\"permissions\":[0,1]},{\"title\":\"新建文件(夾)\",\"permissions\":[0,1]},{\"title\":\"導入文件\",\"permissions\":[0,1]},{\"title\":\"導出文件\",\"permissions\":[0,1]},{\"title\":\"複製文件(當前文件&上級文件夾權限)\",\"permissions\":[0,1]},{\"title\":\"移動文件(當前文件&目標文件夾權限)\",\"permissions\":[0,1]},{\"title\":\"重命名文件(夾)\",\"permissions\":[0,1]},{\"title\":\"刪除文件(夾)\",\"permissions\":[0,1]},{\"title\":\"分享文件(夾)\",\"permissions\":[0,1,2]},{\"title\":\"設置文件(夾)描述\",\"permissions\":[0,1]},{\"title\":\"保存為模板\",\"permissions\":[0,1]}]}]" }, "permission_delete_failed": { @@ -15606,7 +16101,7 @@ }, "personalized_setting_tip": { "zh_CN": "请根据个人喜好选择合适的设置项,以下设置仅对自己的帐号生效。", - "en_US": "Configure your preferences, include languages, appearance and so on", + "en_US": "Configure your preferences, including language, appearance, and so on. The following settings only take effect on your account.", "zh_HK": "請根據個人喜好選擇合適的設置項,以下設置僅對自己的帳號生效。" }, "peru": { @@ -15637,7 +16132,6 @@ "phone_number": { "zh_CN": "手机号", "en_US": "Phone", - "终端": "APP", "zh_HK": "手機號" }, "pick_field_or_function": { @@ -15662,7 +16156,7 @@ }, "placeholder_choose_group": { "zh_CN": "请选择小组", - "en_US": "Select a team or the Space", + "en_US": "Select a team", "zh_HK": "請選擇小組" }, "placeholder_email_code": { @@ -15683,7 +16177,6 @@ "placeholder_enter_your_verification_code": { "zh_CN": "请输入验证码", "en_US": "Enter verification code", - "终端": "APP", "zh_HK": "請輸入驗證碼" }, "placeholder_input": { @@ -15724,7 +16217,6 @@ "placeholder_input_mobile": { "zh_CN": "请输入手机号", "en_US": "Enter your phone number", - "终端": "APP", "zh_HK": "請輸入手機號" }, "placeholder_input_new_nickname": { @@ -15735,13 +16227,11 @@ "placeholder_input_new_password_again": { "zh_CN": "请再次输入新密码", "en_US": "Enter your new password again", - "终端": "APP", "zh_HK": "請再次輸入新密碼" }, "placeholder_input_new_password_with_given_rules": { "zh_CN": "请输入 8-24 位字母加数字组合的新密码", "en_US": "Enter a new password with 8-24 letters and numbers", - "终端": "APP", "zh_HK": "請輸入 8-24 位字母加數字組合的新密碼" }, "placeholder_input_new_phone": { @@ -15757,7 +16247,6 @@ "placeholder_input_password": { "zh_CN": "请输入密码", "en_US": "Enter a password", - "终端": "APP", "zh_HK": "請輸入密碼" }, "placeholder_input_password_again": { @@ -15921,7 +16410,7 @@ }, "player_step_ui_config_105": { "zh_CN": "{\n \"element\": \".sc-jlRMkV:nth-last-of-type(2)\",\n\"placement\": \"rightCenter\",\n \"title\": \"智能引导\", \n\"description\": \"模板中心提供了 1000+ 业务自动化解决方案,可免费安装应用\", \"children\":\"\" \n}", - "en_US": "{\n \"element\": \".sc-jlRMkV:nth-last-of-type(2)\",\n\"placement\": \"rightCenter\",\n \"title\": \"How to use a datasheet\", \n\"description\": \"The templates center offers more than 1000 business automation solutions and is free for download.\", \"children\":\"\" \n}", + "en_US": "{\n \"element\": \".sc-jlRMkV:nth-last-of-type(2)\",\n\"placement\": \"rightCenter\",\n \"title\": \"Tips\", \n\"description\": \"The templates center offers more than 1000 business automation solutions and is free for download.\", \"children\":\"\" \n}", "zh_HK": "{\n \"element\": \".sc-eFehXo:first-child .sc-fmBDoT:nth-last-of-type(2)\",\n\"placement\": \"rightCenter\",\n \"title\": \"智能引導\", \n\"description\": \"模板中心提供了 1000+ 業務自動化解決方案,可免費安裝應用\", \"children\":\"\" \n}" }, "player_step_ui_config_106": { @@ -15931,7 +16420,7 @@ }, "player_step_ui_config_107": { "zh_CN": "{ \n\"element\": \"#WORKBENCH_SIDE_ADD_NODE_BTN\", \"placement\": \"bottom\", \"title\": \"智能引导\", \"description\": \"还没想好怎么搭建业务场景?那就先从模板开始吧~\", \"children\":\"\" }", - "en_US": "{\n \"element\": \"#WORKBENCH_SIDE_ADD_NODE_BTN\", \n\"placement\": \"bottom\",\n \"title\": \"How to use a datasheet\", \n\"description\": \"Still trying to figure out your business scenarios? Give these templates a shot~\", \"children\":\"\" \n}", + "en_US": "{\n \"element\": \"#WORKBENCH_SIDE_ADD_NODE_BTN\", \n\"placement\": \"bottom\",\n \"title\": \"Tips\", \n\"description\": \"Still trying to figure out your business scenarios? Give these templates a shot~\", \"children\":\"\" \n}", "zh_HK": "{ \n\"element\": \"#WORKBENCH_SIDE_ADD_NODE_BTN\", \"placement\": \"bottom\", \"title\": \"智能引導\", \"description\": \"還沒想好怎麼搭建業務場景?那就先從模板開始吧~\", \"children\":\"\" }" }, "player_step_ui_config_108": { @@ -16111,6 +16600,15 @@ "player_step_ui_config_161": { "zh_CN": "{\n \"headerImg\": \"https://s1.vika.cn/space/2022/09/02/3ec8ee88a8c64ee2875cc24e3650a0b3\",\n \"readMoreUrl\": \"https://bbs.vika.cn/article/242\",\n \"children\": \"

    🚀 本次更新内容

    \\n
    • 甘特图新增任务依赖和自动编排功能
    • 简化权限设置界面,提升操作体验
    • 右键菜单可批量插入新行,助力效率提升
    • 模板中心新增中秋专题、开学季两大板块
    • 邀请好友注册并加入空间站,可获赠更多附件容量
    \"\n}" }, + "player_step_ui_config_162": { + "zh_CN": "{\n \"headerImg\": \"https://s1.vika.cn/space/2022/09/15/6eab2470b79a4ab1a6ced0fd555342a3?attname=Update_cover%402x.png\",\n \"readMoreUrl\": \"https://bbs.vika.cn/article/244\",\n \"children\": \"

    🚀 本次更新内容

    \\n
    • 「只可更新」权限范围调整,协作者也能新增记录
    • 空间站「安全设置」结束免费体验,正式按空间站等级提供服务
    • 5 个模板上新,覆盖财务预算、翻译项目、汽车零部件管理等场景
    \"\n}" + }, + "player_step_ui_config_163": { + "zh_CN": "{\n \"headerImg\": \"https://s1.vika.cn/space/2022/09/30/74b29d31dbc44eb48949acd6dff3bd65?attname=Update_cover%402x%203.png\",\n \"readMoreUrl\": \"https://bbs.vika.cn/article/246\",\n \"children\": \"

    🚀 本次更新内容

    \\n
    • 新增「角色」功能,权限分配与任务指派更灵活
    • 「成员」选择框增加悬浮式信息卡片
    • 右键菜单新增「移动至」选项,文件分类归纳更快捷
    • 模板中心新增假期旅游攻略、自卷指南和复盘规划三大专题
    \"\n}" + }, + "player_step_ui_config_164": { + "zh_CN": "{\n \"headerImg\": \"https://s1.vika.cn/space/2022/10/20/1dc32d8aa47b4863a97ea84ce11ecc60?attname=Update_cover%402x.png\",\n \"readMoreUrl\": \"https://bbs.vika.cn/article/252\",\n \"children\": \"

    🚀 本次更新内容

    \\n
    • 模板中心新增「专题」板块,更多元化的模板介绍
    • 全新帮助中心上线,提供更丰富的内容
    • 部分功能结束免费体验,正式按空间站等级提供服务
    \"\n}" + }, "player_step_ui_config_17": { "zh_CN": "{\n \"element\": \"#TEMPLATE_CENTER_USE_TEMPLATE_BTN>button\", \n\"placement\": \"rightBottom\",\n\"offsetY\": 20,\n \"title\": \"使用模板教程\", \n\"description\": \"点击左侧按钮使用模板\", \"children\":\"\" \n}", "en_US": "{\n \"element\": \"#TEMPLATE_CENTER_USE_TEMPLATE_BTN>button\", \n\"placement\": \"rightBottom\",\n\"offsetY\": 20,\n \"title\": \"How to use a template\", \n\"description\": \"Click the button on the left to use the template\", \"children\":\"\" \n}", @@ -16128,7 +16626,7 @@ }, "player_step_ui_config_2": { "zh_CN": "{\n \"config\": [\n {\n \"key\": 1,\n \"name\": \"answer1\",\n \"title\": \"您希望通过维格表解决哪些问题?\",\n \"type\": \"checkbox\",\n \"answers\": [\n \"工作规划\",\n \"客户服务\",\n \"项目管理\",\n \"采购供应\",\n \"内容生产\",\n \"电商运营\",\n \"活动策划\",\n \"人力资源\",\n \"行政管理\",\n \"财务管理\",\n \"网络直播\",\n \"高校管理\",\n \"其它\"\n ],\n \"lastAllowInput\": true\n },\n {\n \"key\": 2,\n \"name\": \"answer2\",\n \"title\": \"您的工作岗位是?\",\n \"type\": \"radio\",\n \"answers\": [\n \"管理者\",\n \"项目经理\",\n \"产品经理\",\n \"设计师\",\n \"研发、工程师\",\n \"运营、编辑\",\n \"销售、客服\",\n \"人事、行政\",\n \"财务、会计\",\n \"律师、法务\",\n \"市场\",\n \"教师\",\n \"学生\",\n \"其它\"\n ],\n \"lastAllowInput\": true\n },\n {\n \"key\": 3,\n \"name\": \"answer3\",\n \"title\": \"您的公司名称是?\",\n \"type\": \"input\",\n \"submit\": false\n },\n {\n \"key\": 4,\n \"name\": \"answer4\",\n \"title\": \"请留下你的邮箱/手机/微信号,以便我们及时提供帮助。\",\n \"type\": \"input\",\n \"submit\": true\n },\n {\n \"key\": 5,\n \"title\": \"感谢你的填写,请加一下客服号以备不时之需\",\n \"platform\": {\n \"website\": \"https://s1.vika.cn/space/2022/02/15/696b6ba95dd743f1a6668daeeef2c4f7\",\n \"dingtalk\": \"https://s1.vika.cn/space/2022/02/09/5dfcfc63236d48c78e938969bceb2d06\",\n \"wecom\": \"https://s1.vika.cn/space/2022/02/09/b9cab8d995a146a3ac7c0fcdaee1a919\",\n \"feishu\": \"https://u.vika.cn/z9ygm\"\n },\n \"type\": \"contactUs\",\n \"next\": true\n }\n ]\n}\n", - "en_US": "{\n \"config\": [\n {\n \"key\": 1,\n \"name\": \"answer1\",\n \"title\": \"What kind of issues are you looking forward to solved by vikadata?\",\n \"type\": \"checkbox\",\n \"answers\": [\n \"Work Planning\",\n \"Customer Service\",\n \"Project Management\",\n \"Sourcing Supply\",\n \"Content Production\",\n \"E-commerce operation\",\n \"Event Planning\",\n \"Human Resources\",\n \"Administration\",\n \"Financial Management\",\n \"Webcast\",\n \"Educational institutes Management\",\n \"Other\"\n ],\n \"lastAllowInput\": true\n },\n {\n \"key\": 2,\n \"name\": \"answer2\",\n \"title\": \"Your job title is______\",\n \"type\": \"radio\",\n \"answers\": [\n \"General manager \",\n \"Project manager\",\n \"Product manager\",\n \"Designer\",\n \"R&D engineer\",\n \"Operator, editor\",\n \"Sales, customer service\",\n \"Human resource, administration \",\n \"Finance, accountant\",\n \"Lawyer, legal affairs\",\n \"Marketing\",\n \"Teacher\",\n \"Student\",\n \"Other\"\n ],\n \"lastAllowInput\": true\n },\n {\n \"key\": 3,\n \"name\": \"answer3\",\n \"title\": \"What is the name of your company?\",\n \"type\": \"input\",\n \"submit\": false\n },\n {\n \"key\": 4,\n \"name\": \"answer4\",\n \"title\": \"Please leave your phone email address/ phone number/ Wechat account below so we can reach you in time when you need help.\",\n \"type\": \"input\",\n \"submit\": true\n },\n {\n \"key\": 5,\n \"title\": \"Thank you for filling out the form, you can add our customer service in case of need\",\n \"platform\": {\n \"website\": \"https://s1.vika.cn/space/2022/02/15/696b6ba95dd743f1a6668daeeef2c4f7\",\n \"dingtalk\": \"https://s1.vika.cn/space/2022/02/09/5dfcfc63236d48c78e938969bceb2d06\",\n \"wecom\": \"https://s1.vika.cn/space/2022/02/09/b9cab8d995a146a3ac7c0fcdaee1a919\",\n \"feishu\": \"https://u.vika.cn/z9ygm\"\n },\n \"type\": \"contactUs\",\n \"next\": true\n }\n ]\n}\n", + "en_US": "{\n \"config\": [\n {\n \"key\": 1,\n \"name\": \"answer1\",\n \"title\": \"What kind of issues are you looking forward to solved by vikadata?\",\n \"type\": \"checkbox\",\n \"answers\": [\n \"Work Planning\",\n \"Customer Service\",\n \"Project Management\",\n \"Sourcing Supply\",\n \"Content Production\",\n \"E-commerce operation\",\n \"Event Planning\",\n \"Human Resources\",\n \"Administration\",\n \"Financial Management\",\n \"Webcast\",\n \"Educational institutes Management\",\n \"Other\"\n ],\n \"lastAllowInput\": true\n },\n {\n \"key\": 2,\n \"name\": \"answer2\",\n \"title\": \"Your job title is______\",\n \"type\": \"radio\",\n \"answers\": [\n \"General manager \",\n \"Project manager\",\n \"Product manager\",\n \"Designer\",\n \"R&D engineer\",\n \"Operator, editor\",\n \"Sales, customer service\",\n \"Human resource, administration \",\n \"Finance, accountant\",\n \"Lawyer, legal affairs\",\n \"Marketing\",\n \"Teacher\",\n \"Student\",\n \"Other\"\n ],\n \"lastAllowInput\": true\n },\n {\n \"key\": 3,\n \"name\": \"answer3\",\n \"title\": \"What is the name of your company?\",\n \"type\": \"input\",\n \"submit\": false\n },\n {\n \"key\": 4,\n \"name\": \"answer4\",\n \"title\": \"Please leave your email address/ phone number/ Wechat account below so we can reach you in time when you need help.\",\n \"type\": \"input\",\n \"submit\": true\n },\n {\n \"key\": 5,\n \"title\": \"Thank you for filling out the form, you can add our customer service in case of need\",\n \"platform\": {\n \"website\": \"https://s1.vika.cn/space/2022/02/15/696b6ba95dd743f1a6668daeeef2c4f7\",\n \"dingtalk\": \"https://s1.vika.cn/space/2022/02/09/5dfcfc63236d48c78e938969bceb2d06\",\n \"wecom\": \"https://s1.vika.cn/space/2022/02/09/b9cab8d995a146a3ac7c0fcdaee1a919\",\n \"feishu\": \"https://u.vika.cn/z9ygm\"\n },\n \"type\": \"contactUs\",\n \"next\": true\n }\n ]\n}\n", "zh_HK": "{\n \"config\": [\n {\n \"key\": 1,\n \"name\": \"answer1\",\n \"title\": \"您希望通過維格表解決哪些問題?\",\n \"type\": \"checkbox\",\n \"answers\": [\n \"工作規劃\",\n \"客戶服務\",\n \"項目管理\",\n \"採購供應\",\n \"內容生產\",\n \"電商運營\",\n \"活動策劃\",\n \"人力資源\",\n \"行政管理\",\n \"財務管理\",\n \"網絡直播\",\n \"高校管理\",\n \"其它\"\n ],\n \"lastAllowInput\": true\n },\n {\n \"key\": 2,\n \"name\": \"answer2\",\n \"title\": \"您的工作崗位是?\",\n \"type\": \"radio\",\n \"answers\": [\n \"管理者\",\n \"項目經理\",\n \"產品經理\",\n \"設計師\",\n \"研發、工程師\",\n \"運營、編輯\",\n \"銷售、客服\",\n \"人事、行政\",\n \"財務、會計\",\n \"律師、法務\",\n \"市場\",\n \"教師\",\n \"學生\",\n \"其它\"\n ],\n \"lastAllowInput\": true\n },\n {\n \"key\": 3,\n \"name\": \"answer3\",\n \"title\": \"您的公司名稱是?\",\n \"type\": \"input\",\n \"submit\": false\n },\n {\n \"key\": 4,\n \"name\": \"answer4\",\n \"title\": \"請留下你的郵箱/手機/微信號,以便我們及時提供幫助。\",\n \"type\": \"input\",\n \"submit\": true\n },\n {\n \"key\": 5,\n \"title\": \"感謝你的填寫,請加一下客服號以備不時之需\",\n \"platform\": {\n \"website\": \"https://s1.vika.cn/space/2022/02/15/696b6ba95dd743f1a6668daeeef2c4f7\",\n \"dingtalk\": \"https://s1.vika.cn/space/2022/02/09/5dfcfc63236d48c78e938969bceb2d06\",\n \"wecom\": \"https://s1.vika.cn/space/2022/02/09/b9cab8d995a146a3ac7c0fcdaee1a919\",\n \"feishu\": \"https://u.vika.cn/z9ygm\"\n },\n \"type\": \"contactUs\",\n \"next\": true\n }\n ]\n}\n" }, "player_step_ui_config_20": { @@ -16162,7 +16660,7 @@ }, "player_step_ui_config_26": { "zh_CN": "{\n \"element\": \"#WORKBENCH_SIDE_ADD_NODE_BTN\", \n\"placement\": \"bottom\",\n \"title\": \"智能引导\", \n\"description\": \"让我们新建一张空白的维格表试一试\", \"children\":\"\" \n}", - "en_US": "{\n \"element\": \"#WORKBENCH_SIDE_ADD_NODE_BTN\", \n\"placement\": \"bottom\",\n \"title\": \"How to use a datasheet\", \n\"description\": \"First, Let's start by creating a blank datasheet\", \"children\":\"\" \n}", + "en_US": "{\n \"element\": \"#WORKBENCH_SIDE_ADD_NODE_BTN\", \n\"placement\": \"bottom\",\n \"title\": \"Tips\", \n\"description\": \"First, Let's start by creating a blank datasheet\", \"children\":\"\" \n}", "zh_HK": "{\n \"element\": \"#WORKBENCH_SIDE_ADD_NODE_BTN\", \n\"placement\": \"bottom\",\n \"title\": \"智能引導\", \n\"description\": \"讓我們新建一張空白的維格表試一試\", \"children\":\"\" \n}" }, "player_step_ui_config_27": { @@ -16172,7 +16670,7 @@ }, "player_step_ui_config_28": { "zh_CN": "{\n \"element\": \"#NODE_CONTEXT_MENU_ID .react-contexify__item:nth-of-type(1)\", \n\"placement\": \"rightCenter\",\n \"title\": \"智能引导\", \n\"description\": \"接着,选择“新建空白维格表”\", \"children\":\"\" \n}", - "en_US": "{\n \"element\": \"#NODE_CONTEXT_MENU_ID .react-contexify__item:nth-of-type(1)\", \n\"placement\": \"rightCenter\",\n \"title\": \"How to use a datasheet\", \n\"description\": \"Then, click 'New datasheet'\", \"children\":\"\" \n}", + "en_US": "{\n \"element\": \"#NODE_CONTEXT_MENU_ID .react-contexify__item:nth-of-type(1)\", \n\"placement\": \"rightCenter\",\n \"title\": \"Tips\", \n\"description\": \"Then, click 'New datasheet'\", \"children\":\"\" \n}", "zh_HK": "{\n \"element\": \"#NODE_CONTEXT_MENU_ID .react-contexify__item:nth-of-type(1)\", \n\"placement\": \"rightCenter\",\n \"title\": \"智能引導\", \n\"description\": \"接著,選擇“新建空白維格表”\", \"children\":\"\" \n}" }, "player_step_ui_config_29": { @@ -16186,7 +16684,7 @@ }, "player_step_ui_config_30": { "zh_CN": "{\n \"element\": \"#DATASHEET_ADD_COLUMN_BTN\", \n\"placement\": \"leftTop\",\n \"title\": \"智能引导\", \n\"description\": \"一张空白的维格表创建好啦,接下来我们来尝试一下新建一个维格列吧\", \"children\":\"\" \n}", - "en_US": "{\n \"element\": \"#DATASHEET_ADD_COLUMN_BTN\", \n\"placement\": \"leftTop\",\n \"title\": \"How to use a datasheet\", \n\"description\": \"Next, let's create a new field\", \"children\":\"\" \n}", + "en_US": "{\n \"element\": \"#DATASHEET_ADD_COLUMN_BTN\", \n\"placement\": \"leftTop\",\n \"title\": \"Tips\", \n\"description\": \"Next, let's create a new field\", \"children\":\"\" \n}", "zh_HK": "{\n \"element\": \"#DATASHEET_ADD_COLUMN_BTN\", \n\"placement\": \"leftTop\",\n \"title\": \"智能引導\", \n\"description\": \"一張空白的維格表創建好啦,接下來我們來嘗試一下新建一個維格列吧\", \"children\":\"\" \n}" }, "player_step_ui_config_31": { @@ -16195,12 +16693,12 @@ }, "player_step_ui_config_32": { "zh_CN": "{\n \"element\": \"#DATASHEET_GRID_CUR_COLUMN_TYPE\", \n\"placement\": \"bottom\",\n \"title\": \"智能引导\", \n\"description\": \"维格表提供了丰富的维格列类型以匹配各种使用场景,鼠标悬浮在这里即可查看\", \"children\":\"\" \n}", - "en_US": "{\n \"element\": \"#DATASHEET_GRID_CUR_COLUMN_TYPE\", \n\"placement\": \"bottom\",\n \"title\": \"How to use a datasheet\", \n\"description\": \"Vikadata provides various types of fields. Hover on one field type to view the details.\", \"children\":\"\" \n}", + "en_US": "{\n \"element\": \"#DATASHEET_GRID_CUR_COLUMN_TYPE\", \n\"placement\": \"bottom\",\n \"title\": \"Tips\", \n\"description\": \"Vikadata provides various types of fields. Hover on one field type to view the details.\", \"children\":\"\" \n}", "zh_HK": "{\n \"element\": \"#DATASHEET_GRID_CUR_COLUMN_TYPE\", \n\"placement\": \"bottom\",\n \"title\": \"智能引導\", \n\"description\": \"維格表提供了豐富的維格列類型以匹配各種使用場景,鼠標懸浮在這裡即可查看\", \"children\":\"\" \n}" }, "player_step_ui_config_33": { "zh_CN": "{\n \"element\": \"#DATASHEET_VIEW_TAB_BAR .style_viewBarWrapper__AJlc-\", \n\"placement\": \"bottom\",\n \"title\": \"智能引导\", \n\"description\": \"同一张维格表可提供多种视图模式,通过“分组、筛选、排序”等功能来自定义视图的展示数据。但是要注意,一张维格表下所有的视图用的都是同一份数据源,只是展示的样式各有不同,所以不要把视图当作excel的工作簿用哦!\", \"children\":\"\" \n}", - "en_US": "{\n \"element\": \"#DATASHEET_VIEW_TAB_BAR .style_viewBarWrapper__AJlc-\", \n\"placement\": \"bottom\",\n \"title\": \"How to use a datasheet\", \n\"description\": \"A datasheet provides a variety of views, in which grouping, filtering, sorting and other features help you customize the data layout. All views of a datasheet use the same data source, so do not use the view as an Excel sheet!\", \"children\":\"\" \n}", + "en_US": "{\n \"element\": \"#DATASHEET_VIEW_TAB_BAR .style_viewBarWrapper__AJlc-\", \n\"placement\": \"bottom\",\n \"title\": \"Tips\", \n\"description\": \"A datasheet provides a variety of views, in which grouping, filtering, sorting and other features help you customize the data layout. All views of a datasheet use the same data source, so do not use the view as an Excel sheet!\", \"children\":\"\" \n}", "zh_HK": "{\n \"element\": \"#DATASHEET_VIEW_TAB_BAR .style_viewBarWrapper__AJlc-\", \n\"placement\": \"bottom\",\n \"title\": \"智能引導\", \n\"description\": \"同一張維格表可提供多種視圖模式,通過“分組、篩選、排序”等功能來自定義視圖的展示數據。但是要注意,一張維格表下所有的視圖用的都是同一份數據源,只是展示的樣式各有不同,所以不要把視圖當作excel的工作簿用哦!\", \"children\":\"\" \n}" }, "player_step_ui_config_34": { @@ -16209,7 +16707,7 @@ }, "player_step_ui_config_35": { "zh_CN": "{\n \"element\": \"#DATASHEET_ADD_VIEW_BTN\", \n\"placement\": \"bottom\",\n \"title\": \"智能引导\", \n\"description\": \"维格表除了标准表格形式的“维格视图”外,还支持变换“相册视图”“看板视图””甘特视图“”日历视图“”架构视图“,分别对应管理丰富的图像化数据和任务化数据,让你事半功倍,还是得要提醒一次,视图只是展示的样式不同,但是数据源是同一份哦!\", \"children\":\"\" \n}", - "en_US": "{\n \"element\": \"#DATASHEET_ADD_VIEW_BTN\", \n\"placement\": \"bottom\",\n \"title\": \"How to use a datasheet\", \n\"description\": \"Vikadata supports a variety of views such as Grid, Gallery, Kanban, Gantt, Calendar, and Architecture. Different views correspond to different data visualizations and workflows. Again, views are only displayed differently, but they come from the same data source!\", \"children\":\"\" \n}", + "en_US": "{\n \"element\": \"#DATASHEET_ADD_VIEW_BTN\", \n\"placement\": \"bottom\",\n \"title\": \"Tips\", \n\"description\": \"Vikadata supports a variety of views such as Grid, Gallery, Kanban, Gantt, Calendar, and Architecture. Different views correspond to different data visualizations and workflows. Again, views are only displayed differently, but they come from the same data source!\", \"children\":\"\" \n}", "zh_HK": "{\n \"element\": \"#DATASHEET_ADD_VIEW_BTN\", \n\"placement\": \"bottom\",\n \"title\": \"智能引導\", \n\"description\": \"維格表除了標準表格形式的“維格視圖”外,還支持變換“相冊視圖”“看板視圖””甘特視圖“”日曆視圖“”架構視圖“,分別對應管理豐富的圖像化數據和任務化數據,讓你事半功倍,還是得要提醒一次,視圖只是展示的樣式不同,但是數據源是同一份哦!\", \"children\":\"\" \n}" }, "player_step_ui_config_36": { @@ -16218,7 +16716,7 @@ }, "player_step_ui_config_37": { "zh_CN": "{\n \"element\": \"#DATASHEET_TOOL_BAR .style_toolbarMiddle__2kxTf>button:nth-of-type(7)\", \n\"placement\": \"bottom\",\n \"title\": \"智能引导\", \n\"description\": \"如果要将内容分享给空间站外的人员,你可以通过这个功能创建一条链接分享出去\", \"children\":\"\" \n}", - "en_US": "{\n \"element\": \"#DATASHEET_TOOL_BAR .style_toolbarMiddle__2kxTf>button:nth-of-type(7)\", \n\"placement\": \"bottom\",\n \"title\": \"How to use a datasheet\", \n\"description\": \"To share content outside the Space, use this feature to create a public link and share it\", \"children\":\"\" \n}", + "en_US": "{\n \"element\": \"#DATASHEET_TOOL_BAR .style_toolbarMiddle__2kxTf>button:nth-of-type(7)\", \n\"placement\": \"bottom\",\n \"title\": \"Tips\", \n\"description\": \"To share content outside the Space, use this feature to create a public link and share it\", \"children\":\"\" \n}", "zh_HK": "{\n \"element\": \"#DATASHEET_TOOL_BAR .style_toolbarMiddle__2kxTf>button:nth-of-type(7)\", \n\"placement\": \"bottom\",\n \"title\": \"智能引導\", \n\"description\": \"如果要將內容分享給空間站外的人員,你可以通過這個功能創建一條鏈接分享出去\", \"children\":\"\" \n}" }, "player_step_ui_config_38": { @@ -16312,12 +16810,12 @@ }, "player_step_ui_config_55": { "zh_CN": "{\n \"placement\": \"bottomLeft\",\n \"description\": \"仪表盘作为维格表的一种文件类型,添加/导入小程序后,可视化能力提升。你可以通过数据统计和图表分析,更好地进行判断和决策\"\n}", - "en_US": "{\n \"placement\": \"bottomLeft\",\n \"description\": \"Using/adding widgets on dashboard can optimizes its data visualization ability, so that you can make a judgement or decision more clearly based on the extra information offered by statistics and graphical analysis.\"\n}", + "en_US": "{\n \"placement\": \"bottomLeft\",\n \"description\": \"Using/adding widgets on the dashboard can optimize its data visualization ability so that you can make a judgment or decision more clearly based on the extra information offered by statistics and graphical analysis.\"\n}", "zh_HK": "{\n \"placement\": \"bottomLeft\",\n \"description\": \"儀錶盤作為維格表的一種文件類型,添加/導入小程序後,可視化能力提升。你可以通過數據統計和圖表分析,更好地進行判斷和決策\"\n}" }, "player_step_ui_config_56": { "zh_CN": "{\n \"element\": \"#DASHBOARD_PANEL_ID .style_tabRight__4YAkM button:nth-of-type(1)\",\n\"placement\": \"bottom\",\n \"title\": \"添加小程序\", \n\"description\": \"在这里有两种方法添加小程序,一种是从小程序中心添加,一种是从已有的小程序中导入进去\", \"children\":\"\" \n}", - "en_US": "{\n \"element\": \"#DASHBOARD_PANEL_ID .style_tabRight__4YAkM button:nth-of-type(1)\",\n\"placement\": \"bottom\",\n \"title\": \"Add widget\", \n\"description\": \"There are two ways to add an widget here, either from the Widget Center or by importing from existing widget boards\", \"children\":\"\" \n}", + "en_US": "{\n \"element\": \"#DASHBOARD_PANEL_ID .style_tabRight__4YAkM button:nth-of-type(1)\",\n\"placement\": \"bottom\",\n \"title\": \"Add widget\", \n\"description\": \"There are two ways to add a widget here, either from the Widget Center or by importing from existing widget boards\", \"children\":\"\" \n}", "zh_HK": "{\n \"element\": \"#DASHBOARD_PANEL_ID .style_tabRight__4YAkM button:nth-of-type(1)\",\n\"placement\": \"bottom\",\n \"title\": \"添加小程序\", \n\"description\": \"在這裡有兩種方法添加小程序,一種是從小程序中心添加,一種是從已有的小程序中導入進去\", \"children\":\"\" \n}" }, "player_step_ui_config_57": { @@ -16326,7 +16824,7 @@ }, "player_step_ui_config_58": { "zh_CN": "{\n \"element\": \".style_addWidgetMenu__29bIe .style_menuItem__3Ugjz:nth-of-type(1)\",\n\"placement\": \"bottom\",\n \"title\": \"添加小程序\", \n\"description\": \"让我们来尝试一下,添加一个小程序到仪表盘吧\", \"children\":\"\" \n}", - "en_US": "{\n \"element\": \".style_addWidgetMenu__29bIe .style_menuItem__3Ugjz:nth-of-type(1)\",\n\"placement\": \"bottom\",\n \"title\": \"Add widget\", \n\"description\": \"Let's add an widget to the dashboard\", \"children\":\"\" \n}", + "en_US": "{\n \"element\": \".style_addWidgetMenu__29bIe .style_menuItem__3Ugjz:nth-of-type(1)\",\n\"placement\": \"bottom\",\n \"title\": \"Add widget\", \n\"description\": \"Let's add a widget to the dashboard\", \"children\":\"\" \n}", "zh_HK": "{\n \"element\": \".style_addWidgetMenu__29bIe .style_menuItem__3Ugjz:nth-of-type(1)\",\n\"placement\": \"bottom\",\n \"title\": \"添加小程序\", \n\"description\": \"讓我們來嘗試一下,添加一個小程序到儀錶盤吧\", \"children\":\"\" \n}" }, "player_step_ui_config_59": { @@ -16513,7 +17011,7 @@ }, "player_step_ui_config_94": { "zh_CN": "{\n \"title\": \"选择任务,开启探索之旅吧\",\n \"description\": \"移动鼠标来选择任务,跟随页面提示开启旅程吧~\",\n \"data\": [\n {\n \"text\": \"重命名文件,目录索引更直观\",\n \"stopEvents\": [\n \"onMouseDown\"\n ],\n \"actions\": [\n {\n \"uiType\": \"element\",\n \"uiConfig\": {\n \"element\": \"#FOLDER_SHOWCASE_TITLE\",\n \"emitEvent\": \"click\",\n \"nextActions\": [\n {\n \"uiType\": \"popover\",\n \"uiConfig\": \"{\\\"element\\\":\\\"#FOLDER_SHOWCASE_TITLE_INPUT\\\",\\\"placement\\\":\\\"bottom\\\",\\\"title\\\":\\\"智能引导\\\",\\\"description\\\":\\\"点击这里可以修改名称 👆\\\"}\",\n \"backdrop\": \"around_mask\"\n },\n {\n \"uiType\": \"element\",\n \"uiConfig\": {\n \"element\": \"#FOLDER_SHOWCASE_TITLE_INPUT\",\n \"emitEvent\": \"focus\",\n \"finishTodoWhen\": [\n \"blur\"\n ]\n }\n }\n ]\n }\n }\n ]\n },\n {\n \"text\": \"查看/修改文件夹说明,以便其他协作者理解\",\n \"actions\": [\n {\n \"uiType\": \"breath\",\n \"uiConfig\": \"{\\\"element\\\":\\\"#FOLDER_SHOWCASE_DESCRIPTION\\\"}\",\n \"backdrop\": \"around_mask\"\n },\n {\n \"uiType\": \"popover\",\n \"uiConfig\": \"{\\\"element\\\":\\\"#FOLDER_SHOWCASE_DESCRIPTION\\\",\\\"placement\\\":\\\"left\\\",\\\"title\\\":\\\"智能引导\\\",\\\"description\\\":\\\"点击这里查看/修改说明\\\"}\"\n },\n {\n \"uiType\": \"element\",\n \"uiConfig\": {\n \"element\": \"#FOLDER_SHOWCASE_DESCRIPTION\",\n \"finishTodoWhen\": [\n \"click\"\n ]\n }\n }\n ]\n },\n {\n \"text\": \"为避免数据泄露或误删,设置文件访问权限\",\n \"actions\": [\n {\n \"uiType\": \"breath\",\n \"uiConfig\": \"{\\\"element\\\":\\\"#FOLDER_SHOWCASE_BTN_MORE\\\"}\"\n },\n {\n \"uiType\": \"element\",\n \"uiConfig\": {\n \"element\": \"#FOLDER_SHOWCASE_BTN_MORE\",\n \"nextActions\": [\n {\n \"uiType\": \"breath\",\n \"uiConfig\": \"{\\\"element\\\":\\\".sc-eFehXo div:nth-of-type(1)\\\",\\\"shadowDirection\\\":\\\"inset\\\"}\"\n },\n {\n \"uiType\": \"element\",\n \"uiConfig\": {\n \"element\": \".sc-eFehXo div:nth-of-type(1)\",\n \"finishTodoWhen\": [\n \"click\"\n ]\n }\n }\n ]\n }\n }\n ]\n },\n {\n \"text\": \"点击任一文件节点,开始进行数据协作吧\",\n \"actions\": [\n {\n \"uiType\": \"breath\",\n \"uiConfig\": \"{\\\"element\\\":\\\"#FOLDER_SHOWCASE_FIRST_NODE\\\"}\"\n },\n {\n \"uiType\": \"element\",\n \"uiConfig\": {\n \"element\": \"#FOLDER_SHOWCASE_NODES_CONTAINER > div\",\n \"finishTodoWhen\": [\n \"click\"\n ]\n }\n }\n ]\n }\n ]\n}", - "en_US": "{\n \"title\": \"Let's go start your project!\",\n \"description\": \"Welcome to Vika Planet! Let's take up a task and our journey will start from here.\",\n \"data\": [\n {\n \"text\": \"Rename a file to make the index clearer\",\n \"stopEvents\": [\n \"onMouseDown\"\n ],\n \"actions\": [\n {\n \"uiType\": \"element\",\n \"uiConfig\": {\n \"element\": \"#FOLDER_SHOWCASE_TITLE\",\n \"emitEvent\": \"click\",\n \"nextActions\": [\n {\n \"uiType\": \"popover\",\n \"uiConfig\": \"{\\\"element\\\":\\\"#FOLDER_SHOWCASE_TITLE_INPUT\\\",\\\"placement\\\":\\\"bottom\\\",\\\"title\\\":\\\"How to use a datasheet\\\",\\\"description\\\":\\\"Click here to rename the file 👆\\\"}\",\n \"backdrop\": \"around_mask\"\n },\n {\n \"uiType\": \"element\",\n \"uiConfig\": {\n \"element\": \"#FOLDER_SHOWCASE_TITLE_INPUT\",\n \"emitEvent\": \"focus\",\n \"finishTodoWhen\": [\n \"blur\"\n ]\n }\n }\n ]\n }\n }\n ]\n },\n {\n \"text\": \"Modify a file's description to help others understand better\",\n \"actions\": [\n {\n \"uiType\": \"breath\",\n \"uiConfig\": \"{\\\"element\\\":\\\"#FOLDER_SHOWCASE_DESCRIPTION\\\"}\",\n \"backdrop\": \"around_mask\"\n },\n {\n \"uiType\": \"popover\",\n \"uiConfig\": \"{\\\"element\\\":\\\"#FOLDER_SHOWCASE_DESCRIPTION\\\",\\\"placement\\\":\\\"left\\\",\\\"title\\\":\\\"How to use a datasheet\\\",\\\"description\\\":\\\"Click here to view or modify description\\\"}\"\n },\n {\n \"uiType\": \"element\",\n \"uiConfig\": {\n \"element\": \"#FOLDER_SHOWCASE_DESCRIPTION\",\n \"finishTodoWhen\": [\n \"click\"\n ]\n }\n }\n ]\n },\n {\n \"text\": \"Set access permission on a file to prevent data leakage or misoperation\",\n \"actions\": [\n {\n \"uiType\": \"breath\",\n \"uiConfig\": \"{\\\"element\\\":\\\"#FOLDER_SHOWCASE_BTN_MORE\\\"}\"\n },\n {\n \"uiType\": \"element\",\n \"uiConfig\": {\n \"element\": \"#FOLDER_SHOWCASE_BTN_MORE\",\n \"nextActions\": [\n {\n \"uiType\": \"breath\",\n \"uiConfig\": \"{\\\"element\\\":\\\".sc-eFehXo div:nth-of-type(1)\\\",\\\"shadowDirection\\\":\\\"inset\\\"}\"\n },\n {\n \"uiType\": \"element\",\n \"uiConfig\": {\n \"element\": \".sc-eFehXo div:nth-of-type(1)\",\n \"finishTodoWhen\": [\n \"click\"\n ]\n }\n }\n ]\n }\n }\n ]\n },\n {\n \"text\": \"Pick and enter a node to start collaborating with others\",\n \"actions\": [\n {\n \"uiType\": \"breath\",\n \"uiConfig\": \"{\\\"element\\\":\\\"#FOLDER_SHOWCASE_FIRST_NODE\\\"}\"\n },\n {\n \"uiType\": \"element\",\n \"uiConfig\": {\n \"element\": \"#FOLDER_SHOWCASE_NODES_CONTAINER > div\",\n \"finishTodoWhen\": [\n \"click\"\n ]\n }\n }\n ]\n }\n ]\n}", + "en_US": "{\n \"title\": \"Let's go start your project!\",\n \"description\": \"Welcome to Vika Planet! Let's take up a task and our journey will start from here.\",\n \"data\": [\n {\n \"text\": \"Rename a file to make the index clearer\",\n \"stopEvents\": [\n \"onMouseDown\"\n ],\n \"actions\": [\n {\n \"uiType\": \"element\",\n \"uiConfig\": {\n \"element\": \"#FOLDER_SHOWCASE_TITLE\",\n \"emitEvent\": \"click\",\n \"nextActions\": [\n {\n \"uiType\": \"popover\",\n \"uiConfig\": \"{\\\"element\\\":\\\"#FOLDER_SHOWCASE_TITLE_INPUT\\\",\\\"placement\\\":\\\"bottom\\\",\\\"title\\\":\\\"How to use a datasheet\\\",\\\"description\\\":\\\"Click here to rename the file 👆\\\"}\",\n \"backdrop\": \"around_mask\"\n },\n {\n \"uiType\": \"element\",\n \"uiConfig\": {\n \"element\": \"#FOLDER_SHOWCASE_TITLE_INPUT\",\n \"emitEvent\": \"focus\",\n \"finishTodoWhen\": [\n \"blur\"\n ]\n }\n }\n ]\n }\n }\n ]\n },\n {\n \"text\": \"Modify a file's description to help others understand it better\",\n \"actions\": [\n {\n \"uiType\": \"breath\",\n \"uiConfig\": \"{\\\"element\\\":\\\"#FOLDER_SHOWCASE_DESCRIPTION\\\"}\",\n \"backdrop\": \"around_mask\"\n },\n {\n \"uiType\": \"popover\",\n \"uiConfig\": \"{\\\"element\\\":\\\"#FOLDER_SHOWCASE_DESCRIPTION\\\",\\\"placement\\\":\\\"left\\\",\\\"title\\\":\\\"How to use a datasheet\\\",\\\"description\\\":\\\"Click here to view or modify description\\\"}\"\n },\n {\n \"uiType\": \"element\",\n \"uiConfig\": {\n \"element\": \"#FOLDER_SHOWCASE_DESCRIPTION\",\n \"finishTodoWhen\": [\n \"click\"\n ]\n }\n }\n ]\n },\n {\n \"text\": \"Set access permission on a file to prevent data leakage or misoperation\",\n \"actions\": [\n {\n \"uiType\": \"breath\",\n \"uiConfig\": \"{\\\"element\\\":\\\"#FOLDER_SHOWCASE_BTN_MORE\\\"}\"\n },\n {\n \"uiType\": \"element\",\n \"uiConfig\": {\n \"element\": \"#FOLDER_SHOWCASE_BTN_MORE\",\n \"nextActions\": [\n {\n \"uiType\": \"breath\",\n \"uiConfig\": \"{\\\"element\\\":\\\".sc-eFehXo div:nth-of-type(1)\\\",\\\"shadowDirection\\\":\\\"inset\\\"}\"\n },\n {\n \"uiType\": \"element\",\n \"uiConfig\": {\n \"element\": \".sc-eFehXo div:nth-of-type(1)\",\n \"finishTodoWhen\": [\n \"click\"\n ]\n }\n }\n ]\n }\n }\n ]\n },\n {\n \"text\": \"Pick and enter a node to start collaborating with others\",\n \"actions\": [\n {\n \"uiType\": \"breath\",\n \"uiConfig\": \"{\\\"element\\\":\\\"#FOLDER_SHOWCASE_FIRST_NODE\\\"}\"\n },\n {\n \"uiType\": \"element\",\n \"uiConfig\": {\n \"element\": \"#FOLDER_SHOWCASE_NODES_CONTAINER > div\",\n \"finishTodoWhen\": [\n \"click\"\n ]\n }\n }\n ]\n }\n ]\n}", "zh_HK": "{\n \"title\": \"選擇任務,開啟探索之旅吧\",\n \"description\": \"移動鼠標來選擇任務,跟隨頁面提示開啟旅程吧~\",\n \"data\": [\n {\n \"text\": \"重命名文件,目錄索引更直觀\",\n \"stopEvents\": [\n \"onMouseDown\"\n ],\n \"actions\": [\n {\n \"uiType\": \"element\",\n \"uiConfig\": {\n \"element\": \"#FOLDER_SHOWCASE_TITLE\",\n \"emitEvent\": \"click\",\n \"nextActions\": [\n {\n \"uiType\": \"popover\",\n \"uiConfig\": \"{\\\"element\\\":\\\"#FOLDER_SHOWCASE_TITLE_INPUT\\\",\\\"placement\\\":\\\"bottom\\\",\\\"title\\\":\\\"智能引導\\\",\\\"description\\\":\\\"點擊這裡可以修改名稱 👆\\\"}\",\n \"backdrop\": \"around_mask\"\n },\n {\n \"uiType\": \"element\",\n \"uiConfig\": {\n \"element\": \"#FOLDER_SHOWCASE_TITLE_INPUT\",\n \"emitEvent\": \"focus\",\n \"finishTodoWhen\": [\n \"blur\"\n ]\n }\n }\n ]\n }\n }\n ]\n },\n {\n \"text\": \"查看/修改文件夾說明,以便其他協作者理解\",\n \"actions\": [\n {\n \"uiType\": \"breath\",\n \"uiConfig\": \"{\\\"element\\\":\\\"#FOLDER_SHOWCASE_DESCRIPTION\\\"}\",\n \"backdrop\": \"around_mask\"\n },\n {\n \"uiType\": \"popover\",\n \"uiConfig\": \"{\\\"element\\\":\\\"#FOLDER_SHOWCASE_DESCRIPTION\\\",\\\"placement\\\":\\\"left\\\",\\\"title\\\":\\\"智能引導\\\",\\\"description\\\":\\\"點擊這裡查看/修改說明\\\"}\"\n },\n {\n \"uiType\": \"element\",\n \"uiConfig\": {\n \"element\": \"#FOLDER_SHOWCASE_DESCRIPTION\",\n \"finishTodoWhen\": [\n \"click\"\n ]\n }\n }\n ]\n },\n {\n \"text\": \"為避免數據洩露或誤刪,設置文件訪問權限\",\n \"actions\": [\n {\n \"uiType\": \"breath\",\n \"uiConfig\": \"{\\\"element\\\":\\\"#FOLDER_SHOWCASE_BTN_MORE\\\"}\"\n },\n {\n \"uiType\": \"element\",\n \"uiConfig\": {\n \"element\": \"#FOLDER_SHOWCASE_BTN_MORE\",\n \"nextActions\": [\n {\n \"uiType\": \"breath\",\n \"uiConfig\": \"{\\\"element\\\":\\\".sc-eFehXo div:nth-of-type(1)\\\",\\\"shadowDirection\\\":\\\"inset\\\"}\"\n },\n {\n \"uiType\": \"element\",\n \"uiConfig\": {\n \"element\": \".sc-eFehXo div:nth-of-type(1)\",\n \"finishTodoWhen\": [\n \"click\"\n ]\n }\n }\n ]\n }\n }\n ]\n },\n {\n \"text\": \"點擊任一文件節點,開始進行數據協作吧\",\n \"actions\": [\n {\n \"uiType\": \"breath\",\n \"uiConfig\": \"{\\\"element\\\":\\\"#FOLDER_SHOWCASE_FIRST_NODE\\\"}\"\n },\n {\n \"uiType\": \"element\",\n \"uiConfig\": {\n \"element\": \"#FOLDER_SHOWCASE_NODES_CONTAINER > div\",\n \"finishTodoWhen\": [\n \"click\"\n ]\n }\n }\n ]\n }\n ]\n}" }, "player_step_ui_config_95": { @@ -16561,7 +17059,6 @@ "please_note": { "zh_CN": "温馨提示", "en_US": "Note", - "终端": "APP", "zh_HK": "溫馨提示" }, "please_read_carefully": { @@ -16574,6 +17071,16 @@ "en_US": "Select a team", "zh_HK": "請選擇小組" }, + "plus_edition": { + "zh_CN": "Plus", + "en_US": "Plus", + "zh_HK": "Plus", + "billing": { + "products": [ + "reccLfrUNVaem" + ] + } + }, "png": { "zh_CN": "PNG 图片", "en_US": "As .png format", @@ -16612,7 +17119,6 @@ "press_again_to_exit": { "zh_CN": "再次点击退出!", "en_US": "Press again to exit!", - "终端": "APP", "zh_HK": "再次點擊退出!" }, "preview": { @@ -16704,6 +17210,11 @@ "en_US": "The file doesn't support reviewing", "zh_HK": "當前格式圖片暫不支持預覽" }, + "preview_time_machine": { + "zh_CN": "正在预览,版本 ${version}", + "en_US": "Previewing, version ${version}", + "zh_HK": "正在預覽,版本 ${version}" + }, "preview_tip_contact_main_admin": { "zh_CN": "可联系空间站的主管理员前往「空间站设置 - 第三方应用集成」启用", "en_US": "Ask the admin to enable it from the Integrations page of \"Space Management\"", @@ -16760,15 +17271,17 @@ }, "price_question_title": { "zh_CN": "产品价格问答", + "en_US": "Product Price Q&A", "zh_HK": "產品價格問答" }, "price_questions": { "zh_CN": "[{\n\t\"question\": \"什么是空间站等级?\",\n\t\"answer\": \"空间站是组织或团队所有成员的共享工作区,在「维格表」里点击左上角空间站图标即可查看自己管理或受邀的空间站。空间站等级分为青铜级(永久免费)、白银级、黄金级和企业级。不同等级的空间站享有不同的权益,空间站成员享有所处等级空间站的相关权益。\"\n}, {\n\t\"question\": \"如何查看空间站的当前等级和用量情况?\",\n\t\"answer\": \"在「空间站管理 > 空间站驾驶舱」中可查看。\"\n}, {\n\t\"question\": \"白银级/黄金级/企业级付费时长结束后,维格表数量/行数/附件等超出用量限制,会被删除吗?\",\n\t\"answer\": \"空间站到期后等级自动降级为青铜级,享受青铜级的权益。超出青铜级用量上限部分的数据不会被删除,可以正常使用。当你再新增数据时,如果超出了青铜级上限,则会触发上限提醒。\"\n}, {\n\t\"question\": \"文件节点数量是什么?\",\n\t\"answer\": \"文件节点数量包含工作目录中所有文件(表格、仪表盘、神奇表单、镜像)的数量统计。被删除的文件节点不计入统计。\"\n}, {\n\t\"question\": \"席位数是什么?\",\n\t\"answer\": \"席位数是空间站内席位成员的名额上限,席位成员属于正式成员,可以作为协作成员对文件进行编辑。\"\n}, {\n\t\"question\": \"空间站内的V币是什么?\",\n\t\"answer\": \"V币是维格表赠送的平台积分,与人民币的兑换比例为 100:1,可用于兑换维格表内的服务。如白银级空间站每人每月需要 30 元,即可用 3000 V币进行兑换。\"\n}, {\n\t\"question\": \"如何获取V币?\",\n\t\"answer\": \"V币的获取方式包括但不限于:完成新手任务、邀请好友、参与维格表相关活动等。\"\n}, {\n\t\"question\": \"为什么有的功能名称左侧标了星号「*」?\",\n\t\"answer\": \"标星号「*」表示目前该功能未上线,在未来会推出。\"\n}, {\n\t\"question\": \"有的功能是限时免费的,截止日期是多少?\",\n\t\"answer\": \"某些维格表新功能上线后,会有一段不定周期的时间给用户进行免费体验。功能正式收费后,免费体验期间创建的数据不会受到影响,请大家放心使用。\"\n}, {\n\t\"question\": \"企业级的定制小程序怎么收费?\",\n\t\"answer\": \"我们将根据企业级客户的实际业务需求,提供付费定制开发小程序的服务。如果想要了解定制费用详情,请点击右下角的「联系客服」进行咨询;移动端用户请点击底部的「联系客服」。\"\n}, {\n\t\"question\": \"如何了解具体的收费计划和使用规则?\",\n\t\"answer\": \"如果想要了解企业级的费用详情,请点击右下角的「联系客服」进行咨询;移动端用户请点击底部的「联系客服」。\"\n}, {\n\t\"question\": \"数据安全如何得到保障?\",\n\t\"answer\": \"数据安全是维格表的生命线。数据通过 HTTPS/SSL/TLS 协议配置 CA 证书进行加密传输,并存储于云服务商,由云厂商进行安全运维保障,数据实时备份不会丢失。此外,维格表中所有应用的数据授权,均通过 Vault 密钥钱包进行安全验证管理。考虑到企业更高的数据安全需求,维格表提供私有化部署服务,建立全面、稳定的技术防御。\"\n}]", + "en_US": "[\n {\n \"question\": \"What is the Space?\",\n \"answer\": \"The space is a shared work area for all members of the organization or team. Click the space icon in the upper left corner of the APITable workbench to view your own managed or invited spaces.\"\n }, \n {\n\t\"question\": \"What is the grade of the space?\",\n\t\"answer\": \"APITable space grade include Free ,Plus, Pro and Enterprise. Different grade of space enjoy different rights and interests, and space members enjoy the relevant rights and interests in the same space\"\n}, {\n\t\"question\": \"How to check the current grade and usage of the space?\",\n\t\"answer\": \"You can view it in the Space settings > Overview.\"\n}, {\n\t\"question\": \"How does APITable's pricing work?\",\n\t\"answer\": \"APITable plans are charged flat rate and per-seat.For example, if you buy a plus plan of the space, you have to pay a fixed rate. Then,the plus plan contains 10 free seats. If you have more than 10 members on the space , you need to buy seats for the excess members.\"\n},{\n\t\"question\": \"How do I upgrade?\",\n\t\"answer\": \"you can go to the Space settings > upgrade and choose a paid plan.Only a fews steps,you can subscribe to a plan.If your space have been a paid plan,You can still choose a higher plan in this way.\"\n}, {\n\t\"question\": \"How does adding and removing members work?\",\n\t\"answer\": \" If you added members, your account will be charged a prorated amount based on the percentage of the billing cycle left at the time each member was added. If you removed members, your account will be credited in the same way.\"\n}, {\n\t\"question\": \"How do I downgrade or unsubscribe?\",\n\t\"answer\": \"Please contact us,you can click the small purple icon in the lower right corner and send us a message.\"\n}, {\n\t\"question\": \"How do I switch billing cycle\",\n\t\"answer\": \"Please contact us,you can click the small purple icon in the lower right corner and send us a message.\"\n}]", "zh_HK": "[{\n\t\"question\": \"什麼是空間站等級?\",\n\t\"answer\": \"空間站是組織或團隊所有成員的共享工作區,在「維格表」裡點擊左上角空間站圖標即可查看自己管理或受邀的空間站。空間站等級分為青銅級(永久免費)、白銀級、黃金級和企業級。不同等級的空間站享有不同的權益,空間站成員享有所處等級空間站的相關權益。 \"\n}, {\n\t\"question\": \"如何查看空間站的當前等級和用量情況?\",\n\t\"answer\": \"在「空間站管理 > 空間站駕駛艙」中可查看。 \"\n}, {\n\t\"question\": \"白銀級/黃金級/企業級付費時長結束後,維格表數量/行數/附件等超出用量限制,會被刪除嗎?\",\n\t\"answer\": \"空間站到期後等級自動降級為青銅級,享受青銅級的權益。超出青銅級用量上限部分的數據不會被刪除,可以正常使用。當你再新增數據時,如果超出了青銅級上限,則會觸發上限提醒。\"\n}, {\n\t\"question\": \"文件節點數量是什麼?\",\n\t\"answer\": \"文件節點數量包含工作目錄中所有文件(表格、儀錶盤、神奇表單、鏡像)的數量統計。被刪除的文件節點不計入統計。\"\n}, {\n\t\"question\": \"席位數是什麼?\",\n\t\"answer\": \"席位數是空間站內席位成員的名額上限,席位成員屬於正式成員,可以作為協作成員對文件進行編輯。\"\n}, {\n\t\"question\": \"空間站內的V幣是什麼?\",\n\t\"answer\": \"V幣是維格表贈送的平台積分,與人民幣的兌換比例為 100:1,可用於兌換維格表內的服務。如白銀級空間站每人每月需要 30 元,即可用 3000 V幣進行兌換。\"\n}, {\n\t\"question\": \"如何獲取V幣?\",\n\t\"answer\": \"V幣的獲取方式包括但不限於:完成新手任務、邀請好友、參與維格表相關活動等。\"\n}, {\n\t\"question\": \"為什麼有的功能名稱左側標了星號「*」? \",\n\t\"answer\": \"標星號「*」表示目前該功能未上線,在未來會推出。 \"\n}, {\n\t\"question\": \"有的功能是限時免費的,截止日期是多少?\",\n\t\"answer\": \"某些維格表新功能上線後,會有一段不定週期的時間給用戶進行免費體驗。功能正式收費後,免費體驗期間創建的數據不會受到影響,請大家放心使用。\"\n}, {\n\t\"question\": \"企業級的定制小程序怎麼收費?\",\n\t\"answer\": \"我們將根據企業級客戶的實際業務需求,提供付費定制開發小程序的服務。如果想要了解定制費用詳情,請點擊右下角的「聯繫客服」進行諮詢;移動端用戶請點擊底部的「聯繫客服」。 \"\n}, {\n\t\"question\": \"如何了解具體的收費計劃和使用規則?\",\n\t\"answer\": \"如果想要了解企業級的費用詳情,請點擊右下角的「聯繫客服」進行諮詢;移動端用戶請點擊底部的「聯繫客服」。 \"\n}, {\n\t\"question\": \"數據安全如何得到保障?\",\n\t\"answer\": \"數據安全是維格表的生命線。數據通過 HTTPS/SSL/TLS 協議配置 CA 證書進行加密傳輸,並存儲於雲服務商,由雲廠商進行安全運維保障,數據實時備份不會丟失。此外,維格表中所有應用的數據授權,均通過 Vault 密鑰錢包進行安全驗證管理。考慮到企業更高的數據安全需求,維格表提供私有化部署服務,建立全面、穩定的技術防禦。\"\n}]" }, "price_sub_title": { "zh_CN": "1 元/人/天 = 100+ 个办公工作模板", - "en_US": "1 yuan / person / day = 100 + office work templates", + "en_US": "$1 / person/day = 100+ office work templates", "zh_HK": "1 元/人/天 = 100+ 個辦公工作模板" }, "price_title1": { @@ -16823,7 +17336,6 @@ "privacy_policy": { "zh_CN": "《隐私协议》", "en_US": "", - "终端": "APP", "zh_HK": "《隱私協議》" }, "privacy_policy_pure_string": { @@ -16831,6 +17343,10 @@ "en_US": "Privacy Policy", "zh_HK": "隱私協議" }, + "privacy_policy_title": { + "zh_CN": "维格隐私政策", + "en_US": "Privacy policy" + }, "privacy_protection": { "zh_CN": "《隐私保护》", "en_US": "\"Privacy Protection\"", @@ -16839,7 +17355,12 @@ "private_cloud": { "zh_CN": "专有云旗舰版", "en_US": "Private Cloud", - "zh_HK": "專有云旗艦版" + "zh_HK": "專有云旗艦版", + "billing": { + "products": [ + "recbhvo5J4ET2" + ] + } }, "private_external_person_only": { "zh_CN": "外部人员专用", @@ -16852,19 +17373,32 @@ "zh_HK": "內部人員專用" }, "private_product_point": { - "zh_CN": "一键拥有自己的维格表平台" + "zh_CN": "一键拥有自己的维格表平台", + "en_US": "Get your own APITable immediately" }, "privatized_deployment": { - "zh_CN": "私有化部署" + "zh_CN": "私有化部署", + "en_US": "Slef-Hosted" }, "privatized_deployment_desc": { - "zh_CN": "将产品部署到你选择的公有云 或者企业自有云" + "zh_CN": "将产品部署到您自有的公有云、私有云或IDC机房", + "en_US": "Host your own instance on-premise or in the cloud" }, "privilege_list_of_sliver": { "zh_CN": "{\"title\":\"白银级空间站特权\",\"privilegeList\":[\"独享更换主题色\",\"独享更换主题色\",\"独享更换主题色\",\"独享更换主题色\"],\"helper\":\"了解用户服务联系客服\"}", "en_US": "{\"title\": \"Silver Space Station privilege\", \"privilegeList\": [\"exclusive change theme color\", \"exclusive change theme color\"], \"helper\": \"understand user service and contact customer service\"}", "zh_HK": "{\"title\":\"白銀級空間站特權\",\"privilegeList\":[\"獨享更換主題色\",\"獨享更換主題色\",\"獨享更換主題色\",\"獨享更換主題色\"],\"helper\":\"了解用戶服務聯繫客服\"}" }, + "pro_edition": { + "zh_CN": "Pro", + "en_US": "Pro", + "zh_HK": "Pro", + "billing": { + "products": [ + "recmskxfdjT51" + ] + } + }, "process": { "zh_CN": "处理", "en_US": "Process", @@ -16916,10 +17450,12 @@ "zh_HK": "公測期" }, "public_cloud": { - "zh_CN": "公有云" + "zh_CN": "公有云", + "en_US": "CLOUD" }, "public_cloud_desc": { - "zh_CN": "通过互联网可以便捷访问产品,数据存储到 vika.cn" + "zh_CN": "通过互联网可以便捷访问产品,数据存储到 vika.cn", + "en_US": "Saas offering on the APITable website" }, "public_link": { "zh_CN": "公开链接", @@ -16938,7 +17474,7 @@ }, "publish_share_link_with_anyone": { "zh_CN": "通过公开链接分享内容给他人", - "en_US": "Publish and share link with anyone", + "en_US": "Publish and share the link with anyone", "zh_HK": "通過公開鏈接分享內容給他人" }, "publish_to_dingtalk_workbench": { @@ -17093,7 +17629,7 @@ }, "rebuild_token_value": { "zh_CN": "重新生成令牌", - "en_US": "Reset", + "en_US": "Generate New Token", "zh_HK": "重新生成令牌" }, "receive_new_folder": { @@ -17111,11 +17647,19 @@ "en_US": "Recently installed widgets (${count})", "zh_HK": "最近添加的小程序(${count} 個)" }, + "recently_used_files": { + "zh_CN": "最近访问的文件夹", + "en_US": "Recently visited folders" + }, "recommend": { "zh_CN": "推荐", "en_US": "Hot", "zh_HK": "推薦" }, + "recommend_album": { + "zh_CN": "推荐专题", + "en_US": "recommend album" + }, "reconciled_data": { "zh_CN": "正在核对数据……", "en_US": "Data is being reconciled", @@ -17127,7 +17671,7 @@ "zh_HK": "記錄" }, "record_activity_experience_tips": { - "zh_CN": "高级空间站特权体验中,可查看 ${day} 天的修改历史", + "zh_CN": "可查看 ${day} 天的修改历史", "en_US": "You are experiencing advanced space privileges, you can view record activity of ${day} days", "zh_HK": "高級空間站特權體驗中,可查看 ${day} 天的修改歷史" }, @@ -17162,9 +17706,9 @@ "zh_HK": "僅修改歷史" }, "record_history_help_url": { - "zh_CN": "/help/manual-record-history/", - "en_US": "/help/manual-record-history/", - "zh_HK": "/help/manual-record-history/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-record-history", + "en_US": "https://help.vika.cn/docs/guide/manual-record-history", + "zh_HK": "https://help.vika.cn/docs/guide/manual-record-history" }, "record_history_title": { "zh_CN": "修改历史", @@ -17249,7 +17793,6 @@ "refresh": { "zh_CN": "刷新", "en_US": "Refresh", - "终端": "APP", "zh_HK": "刷新" }, "refresh_manually": { @@ -17387,7 +17930,7 @@ }, "remove_from_team_confirm_tip": { "zh_CN": "确认是否将该成员移出此小组", - "en_US": "Confirm to remove the member from this group", + "en_US": "Confirm to remove the member from this team", "zh_HK": "確認是否將該成員移出此小組" }, "remove_from_the_team": { @@ -17447,7 +17990,7 @@ }, "remove_permissions_desc": { "zh_CN": "移除权限后,该成员/小组可能无法查看该文件", - "en_US": "After removing permissions, th is member/team may not be able to view the file", + "en_US": "After removing permissions, the member/team may not be able to view the file", "zh_HK": "移除權限後,該成員/小組可能無法查看該文件" }, "remove_role": { @@ -17513,6 +18056,16 @@ "en_US": "renewal", "zh_HK": "續費" }, + "renewal_prompt": { + "zh_CN": "续费提示", + "en_US": "Renewal prompt", + "zh_HK": "續費提示" + }, + "renewal_prompt_description": { + "zh_CN": "你的原方案已经下架,不支持续费。你可以先升级到其他方案后再续费。", + "en_US": "Your original plan has been removed from the shelves and does not support renewal. You can upgrade to another solution before you renew it.", + "zh_HK": "你的原方案已經下架,不支持續費。你可以先升級到其他方案後再續費。" + }, "renewal_seat_warning": { "zh_CN": "续费不支持等级和人数的变更,如需提高等级或人数,${link}", "en_US": "If you need to upgrade or increase the number of members, ${link}", @@ -17523,6 +18076,16 @@ "en_US": "Reopen it", "zh_HK": "重新打開" }, + "report_issues": { + "zh_CN": "反馈问题", + "en_US": "Report issues", + "zh_HK": "反饋問題" + }, + "report_issues_github_url": { + "zh_CN": "https://github.com/apitable/apitable/issues", + "en_US": "https://github.com/apitable/apitable/issues", + "zh_HK": "https://github.com/apitable/apitable/issues" + }, "report_reason_1": { "zh_CN": "违反国家法规定", "en_US": "Violation of national law", @@ -17606,7 +18169,6 @@ "reset_password": { "zh_CN": "重置密码", "en_US": "Reset password", - "终端": "APP", "zh_HK": "重置密碼" }, "reset_password_need_message_verify_code_tip": { @@ -17626,7 +18188,7 @@ }, "reset_permission_content": { "zh_CN": "成员和小组将恢复为继承上级文件夹的权限,已设置的权限会被清除", - "en_US": "Members and groups will restore inherited permissions to the parent folder, and the set permissions will be closed at the same time", + "en_US": "Members and teams will restore inherited permissions to the parent folder, and the set permissions will be closed at the same time", "zh_HK": "成員和小組將恢復为繼承對上級文件夾的權限,且已設置的權限會同時關閉" }, "reset_permission_default": { @@ -17636,7 +18198,7 @@ }, "reset_permission_desc": { "zh_CN": "已限制权限,不再继承上级文件夹的权限", - "en_US": "Permission has been restricted, and no longer inherit from parent folders", + "en_US": "Permission has been restricted, and no longer inherit from the parent folder", "zh_HK": "已限制權限,不再繼承上級文件夾的權限" }, "reset_permission_desc_root": { @@ -17692,7 +18254,6 @@ "retrieve_password": { "zh_CN": "忘记密码", "en_US": "Forgot password", - "终端": "APP", "zh_HK": "忘記密碼" }, "reunion_island": { @@ -17746,9 +18307,9 @@ "zh_HK": "釘釘機器人 webhook 地址" }, "robot_action_send_dingtalk_config_1_desc": { - "zh_CN": "指定一个钉钉机器人,向它所在的群聊发送消息 [如何获取 webhook](https://vika.cn/help/manual-vika-robot/#9-toc-title)", - "en_US": "Specify a DingTalk robot to send a message to the chat group it's in [How to get webhook](https://vika.cn/help/manual-vika-robot/#9-toc-title)", - "zh_HK": "指定一個釘釘機器人,向它所在的群聊發送消息 [如何獲取 webhook](https://vika.cn/help/manual-vika-robot/#9-toc-title)" + "zh_CN": "指定一个钉钉机器人,向它所在的群聊发送消息 [如何获取 webhook](https://help.vika.cn/docs/guide/manual-automation-robot#send-to-dingtalk-groups)", + "en_US": "Specify a DingTalk robot to send a message to the chat group it's in [How to get webhook](https://help.vika.cn/docs/guide/manual-automation-robot#send-to-dingtalk-groups)", + "zh_HK": "指定一個釘釘機器人,向它所在的群聊發送消息 [如何獲取 webhook](https://help.vika.cn/docs/guide/manual-automation-robot#send-to-dingtalk-groups)" }, "robot_action_send_dingtalk_config_2": { "zh_CN": "消息类型", @@ -17806,9 +18367,9 @@ "zh_HK": "飛書機器人 webhook 地址" }, "robot_action_send_lark_config_1_desc": { - "zh_CN": "指定一个飞书机器人,向它所在的群聊发送消息 [如何获取 webhook](https://vika.cn/help/manual-vika-robot/#8-toc-title)", - "en_US": "Specify a Lark robot to send a message to the chat group it's in [How to get webhook](https://vika.cn/help/manual-vika-robot/#8-toc-title)", - "zh_HK": "指定一個飛書機器人,向它所在的群聊發送消息 [如何獲取 webhook](https://vika.cn/help/manual-vika-robot/#8-toc-title)" + "zh_CN": "指定一个飞书机器人,向它所在的群聊发送消息 [如何获取 webhook](https://help.vika.cn/docs/guide/manual-automation-robot#send-to-lark-groups)", + "en_US": "Specify a Lark robot to send a message to the chat group it's in [How to get webhook](https://help.vika.cn/docs/guide/manual-automation-robot#send-to-lark-groups)", + "zh_HK": "指定一個飛書機器人,向它所在的群聊發送消息 [如何獲取 webhook](https://help.vika.cn/docs/guide/manual-automation-robot#send-to-lark-groups)" }, "robot_action_send_lark_config_2": { "zh_CN": "消息类型", @@ -17966,9 +18527,9 @@ "zh_HK": "企業微信機器人 webhook 地址" }, "robot_action_send_wework_config_1_desc": { - "zh_CN": "指定一个企业微信机器人,向它所在的群聊发送消息 [如何获取 webhook](https://vika.cn/help/manual-vika-robot/#10-toc-title)", - "en_US": "Specify a WeCom robot to send a message to the chat group it's in [How to get webhook](https://vika.cn/help/manual-vika-robot/#10-toc-title)", - "zh_HK": "指定一個企業微信機器人,向它所在的群聊發送消息 [如何獲取 webhook](https://vika.cn/help/manual-vika-robot/#10-toc-title)" + "zh_CN": "指定一个企业微信机器人,向它所在的群聊发送消息 [如何获取 webhook](https://help.vika.cn/docs/guide/manual-automation-robot#send-to-wecom-groups)", + "en_US": "Specify a WeCom robot to send a message to the chat group it's in [How to get webhook](https://help.vika.cn/docs/guide/manual-automation-robot#send-to-wecom-groups)", + "zh_HK": "指定一個企業微信機器人,向它所在的群聊發送消息 [如何獲取 webhook](https://help.vika.cn/docs/guide/manual-automation-robot#send-to-wecom-groups)" }, "robot_action_send_wework_config_2": { "zh_CN": "消息类型", @@ -18050,6 +18611,11 @@ "en_US": "Please fill in the required field", "zh_HK": "請填寫必填項" }, + "robot_config_help_url": { + "zh_CN": "https://help.vika.cn/docs/guide/manual-automation-robot/#how-to-config-robot", + "en_US": "https://help.vika.cn/docs/guide/manual-automation-robot/#how-to-config-robot", + "zh_HK": "https://help.vika.cn/docs/guide/manual-automation-robot/#how-to-config-robot" + }, "robot_config_incomplete_tooltip": { "zh_CN": "请先完成配置", "en_US": "Please complete the configuration", @@ -18135,11 +18701,6 @@ "en_US": "Delete robot", "zh_HK": "刪除機器人" }, - "robot_delete_success": { - "zh_CN": "机器人删除成功", - "en_US": "Robot deleted", - "zh_HK": "機器人刪除成功" - }, "robot_disable_create_tooltip": { "zh_CN": "请先申请开通机器人功能", "en_US": "Please apply to enable the Robot feature first", @@ -18190,6 +18751,11 @@ "en_US": "Robot", "zh_HK": "機器人" }, + "robot_help_url": { + "zh_CN": "https://help.vika.cn/docs/guide/manual-automation-robot", + "en_US": "https://help.vika.cn/docs/guide/manual-automation-robot", + "zh_HK": "https://help.vika.cn/docs/guide/manual-automation-robot" + }, "robot_inserted_variable_invalid": { "zh_CN": "该变量已失效", "en_US": "Invalid variable", @@ -18212,7 +18778,7 @@ }, "robot_new_action_tooltip": { "zh_CN": "添加操作", - "en_US": "Add action", + "en_US": "Add an action", "zh_HK": "添加操作" }, "robot_no_step_config_1": { @@ -18271,9 +18837,9 @@ "zh_HK": "沒有更多了" }, "robot_run_history_desc": { - "zh_CN": "公测期间,你可以看到该机器人所有的运行历史 如何排查问题", - "en_US": "You have access to all run history during the public beta Trouble shooting", - "zh_HK": "公測期間,你可以看到該機器人所有的運行歷史 如何排查問題" + "zh_CN": "公测期间,你可以看到该机器人所有的运行历史 如何排查问题", + "en_US": "You have access to all run history during the public beta Trouble shooting", + "zh_HK": "公測期間,你可以看到該機器人所有的運行歷史 如何排查問題" }, "robot_run_history_error": { "zh_CN": "错误", @@ -18466,9 +19032,9 @@ "zh_HK": "選擇匹配條件" }, "robot_trigger_record_matches_condition_config_2_desc": { - "zh_CN": "注意:不支持在匹配条件中添加日期列或者包含日期函数的公式列来实现定时触发或到期触发 [FAQ 参考](https://vika.cn/help/manual-vika-robot/#26-toc-title)", - "en_US": "Note: Adding Date/Formula field to trigger at scheduled/due time is not supported [FAQ](https://vika.cn/help/manual-vika-robot/#26-toc-title)", - "zh_HK": "注意:不支持在匹配條件中添加日期列或者包含日期函數的公式列來實現定時觸發或到期觸發 [FAQ 參考](https://vika.cn/help/manual-vika-robot/#26-toc-title)" + "zh_CN": "注意:不支持在匹配条件中添加日期列或者包含日期函数的公式列来实现定时触发或到期触发 [FAQ 参考](https://help.vika.cn/docs/guide/manual-automation-robot#robot-scene-related-faq)", + "en_US": "Note: Adding Date/Formula field to trigger at scheduled/due time is not supported [FAQ](https://help.vika.cn/docs/guide/manual-automation-robot#robot-scene-related-faq)", + "zh_HK": "注意:不支持在匹配條件中添加日期列或者包含日期函數的公式列來實現定時觸發或到期觸發 [FAQ 參考](https://help.vika.cn/docs/guide/manual-automation-robot#robot-scene-related-faq)" }, "robot_trigger_record_matches_condition_desc": { "zh_CN": "当表中有记录满足指定条件时,机器人会开始运行", @@ -18797,24 +19363,32 @@ }, "role_context_item_delete": { "zh_CN": "删除", - "en_US": "delete" + "en_US": "Delete" }, "role_context_item_rename": { "zh_CN": "重命名", - "en_US": "rename" + "en_US": "Rename" + }, + "role_item": { + "zh_CN": "${count} 人 / 组", + "en_US": "${count} item(s)" }, "role_member_table_empty": { "zh_CN": "该角色暂无成员,你可以", - "en_US": "There are no members in this role. You can" + "en_US": "There are no members in this role. You can " }, "role_member_table_header_name": { - "zh_CN": "成员/团队", + "zh_CN": "成员/小组", "en_US": "Member / Team" }, "role_member_table_header_team": { - "zh_CN": "团队", + "zh_CN": "小组", "en_US": "Team" }, + "role_name_input_placeholder": { + "zh_CN": "请输入角色的名称", + "en_US": "Please enter the name of the role" + }, "role_permission_manage_integration": { "zh_CN": "管理第三方应用集成", "en_US": "Manage integrations", @@ -18836,8 +19410,9 @@ "zh_HK": "普通成員設置" }, "role_permission_manage_role": { - "zh_CN": "角色管理", - "en_US": "Role management" + "zh_CN": "管理角色", + "en_US": "Manage roles", + "zh_HK": "管理角色" }, "role_permission_manage_security": { "zh_CN": "安全设置管理", @@ -19016,7 +19591,7 @@ }, "row": { "zh_CN": "行", - "en_US": "Row", + "en_US": " Row(s)", "zh_HK": "行" }, "row_height": { @@ -19370,14 +19945,18 @@ "zh_HK": "什麼是維格表?" }, "security_address_list_isolation": { - "zh_CN": "禁止通讯录显示所有成员和小组", - "en_US": "The contacts shows all members and teams", - "zh_HK": "通訊錄顯示所有成員和小組" + "zh_CN": "通讯录隐藏其他小组和成员", + "en_US": "Hide other teams and members in Contacts" }, "security_address_list_isolation_describe": { - "zh_CN": "所有人可以在通讯录查看其它成员和小组。关闭该功能后,普通成员在「通讯录」中仅能查看自己所在的小组和成员(包含下级小组)", - "en_US": "Everyone can view all teams and members in the contacts.\nWhen this function is turned off, members can only view their teams and members (including lower teams) in the contacts.", - "zh_HK": "所有人可以在通訊錄查看其它成員和小組。關閉該功能後,普通成員在「通訊錄」中僅能查看自己所在的小組和成員(包含下級小組)" + "zh_CN": "普通成员在「通讯录」中仅能查看自己所在的小组和成员(包含下级小组)", + "en_US": "Members can only view their teams and members(including sub-teams) in Contacts", + "zh_HK": "普通成員在「通訊錄」中僅能查看自己所在的小組和成員(包含下級小組)" + }, + "security_address_list_isolation_description": { + "zh_CN": "普通成员在「通讯录」中仅能查看自己所在的小组和成员(包含下级小组)", + "en_US": "Members can only view their teams and members(including sub-teams) in Contacts", + "zh_HK": "普通成員在「通訊錄」中僅能查看自己所在的小組和成員(包含下級小組)" }, "security_advanced_tip": { "zh_CN": "高级空间站专享功能", @@ -19386,12 +19965,12 @@ }, "security_disabled_apply_join_space": { "zh_CN": "禁止站外用户申请加入空间站", - "en_US": "Prevent users from applying to joining the Space in the sharing page ", + "en_US": "Prevent users from applying to join the Space in the sharing page ", "zh_HK": "禁止站外用戶申請加入空間站" }, "security_disabled_apply_join_space_describle": { "zh_CN": "分享页面不再显示「申请加入空间站」的入口,外部用户无法提交申请", - "en_US": "The \"Apply to join this Space\" entry is no longer displayed on the sharing page, and users cannot request for joining", + "en_US": "The \"Apply to join this Space\" entry is no longer displayed on the sharing page, and users cannot apply to join", "zh_HK": "分享頁面不再顯示「申請加入空間站」的入口,外部用戶無法提交申請" }, "security_disabled_apply_join_space_modal_describle": { @@ -19535,12 +20114,12 @@ "zh_HK": "權限安全" }, "security_setting_address_list_isolation": { - "zh_CN": "你正在体验${grade}空间站专属功能,升级后可以解锁该功能", + "zh_CN": "你正在使用${grade}空间站专属功能,升级后可以解锁该功能", "en_US": "You are experiencing the ${grade} space exclusive feature, which will be unlocked when you upgrade", "zh_HK": "你正在體驗${grade}空間站專屬功能,升級後可以解鎖該功能" }, "security_setting_apply_join_space": { - "zh_CN": "你正在体验${grade}空间站专属功能,升级后可以解锁该功能", + "zh_CN": "你正在使用${grade}空间站专属功能,升级后可以解锁该功能", "en_US": "You are experiencing the ${grade} space exclusive feature, which will be unlocked when you upgrade", "zh_HK": "你正在體驗${grade}空間站專屬功能,升級後可以解鎖該功能" }, @@ -19549,13 +20128,18 @@ "en_US": "After the function is disabled,the \"Apply to join this Space\" entry is no longer displayed on the sharing page, and users cannot request for joining", "zh_HK": "分享頁面會顯示「申請加入空間站」的入口,站外用戶可申請。關閉該功能後,該入口不再顯示。" }, + "security_setting_apply_join_space_description": { + "zh_CN": "分享页面不再显示「申请加入空间站」的入口,外部用户无法提交申请", + "en_US": "The \"Apply to join this Space\" entry is no longer displayed on the sharing page, and users cannot request for joining", + "zh_HK": "分享頁面會顯示「申請加入空間站」的入口,站外用戶可申請。關閉該功能後,該入口不再顯示。" + }, "security_setting_apply_join_space_title": { - "zh_CN": "禁止站外用户可申请加入空间站", - "en_US": "Allow requests to join the Space", + "zh_CN": "禁止站外用户申请加入空间站", + "en_US": "Prevent users from applying to join the Space in the sharing page ", "zh_HK": "站外用戶可申請加入空間站" }, "security_setting_catalog_management": { - "zh_CN": "你正在体验${grade}空间站专属功能,升级后可以解锁该功能", + "zh_CN": "你正在使用${grade}空间站专属功能,升级后可以解锁该功能", "en_US": "You are experiencing the ${grade} space exclusive feature, which will be unlocked when you upgrade", "zh_HK": "你正在體驗${grade}空間站專屬功能,升級後可以解鎖該功能" }, @@ -19564,13 +20148,18 @@ "en_US": "Members can create new nodes to the root of the Working catalog. After the function is disabled, only Space admin can create.", "zh_HK": "普通成員可以在“工作目錄”的根目錄新增、刪除文件節點。關閉後,僅空間站管理員可進行此操作" }, + "security_setting_catalog_management_description": { + "zh_CN": "禁止普通成员可以在“工作目录”的根目录新增、删除、移动文件节点。开启后,仅空间站管理员可进行此操作", + "en_US": "Members can't create new files at the root of the catalog. After toggling this on, only space admins can create new files at the root of the catalog.", + "zh_HK": "普通成員可以在“工作目錄”的根目錄新增、刪除文件節點。關閉後,僅空間站管理員可進行此操作" + }, "security_setting_catalog_management_title": { - "zh_CN": "禁止成员可在根目录增删文件", - "en_US": "Members can create new nodes to the root of the Working catalog", + "zh_CN": "禁止成员在根目录增删文件", + "en_US": "Prevent members from creating new files at the root of the catalog", "zh_HK": "成員可在根目錄增刪文件" }, "security_setting_copy_cell_data": { - "zh_CN": "你正在体验${grade}空间站专属功能,升级后可以解锁该功能", + "zh_CN": "你正在使用${grade}空间站专属功能,升级后可以解锁该功能", "en_US": "You are experiencing the ${grade} space exclusive feature, which will be unlocked when you upgrade", "zh_HK": "你正在體驗${grade}空間站專屬功能,升級後可以解鎖該功能" }, @@ -19579,54 +20168,69 @@ "en_US": "After the function is disabled,users with \"read-Only\" permission cannot copy cell data, both on and off the Space", "zh_HK": "在站內和站外,擁有「只可閱讀」權限的用戶也可以復製表格內的數據。關閉該功能後,將無法複製數據" }, + "security_setting_copy_cell_data_description": { + "zh_CN": "在站内和站外,拥有「只可阅读」权限的用户无法复制表格内的数据", + "en_US": "Users with \"Read-only\" permission cannot copy data in cells", + "zh_HK": "在站內和站外,擁有「只可閱讀」權限的用戶也可以復製表格內的數據。關閉該功能後,將無法複製數據" + }, "security_setting_copy_cell_data_title": { - "zh_CN": "禁止「只可阅读」用户可复制数据", - "en_US": "Allow read-only users to copy cell data", + "zh_CN": "禁止「只可阅读」用户复制数据", + "en_US": "Prevent \"Read-only\" users from copying data", "zh_HK": "「只可閱讀」用戶可複制數據" }, "security_setting_download_file": { - "zh_CN": "你正在体验${grade}空间站专属功能,升级后可以解锁该功能", + "zh_CN": "你正在使用${grade}空间站专属功能,升级后可以解锁该功能", "en_US": "You are experiencing the ${grade} space exclusive feature, which will be unlocked when you upgrade", "zh_HK": "你正在體驗${grade}空間站專屬功能,升級後可以解鎖該功能" }, "security_setting_download_file_describle": { - "zh_CN": "在站内和站外,拥有「只可阅读」权限的用户也可以下载单元格内的附件。关闭该功能后,下载入口将屏蔽", - "en_US": "After the function is disabled,users with \"read-Only\" permission cannot download attachments in cells, both on and off the Space", + "zh_CN": "在站内和站外,拥有「只可阅读」权限的用户无法下载单元格内的附件", + "en_US": "Users with \"Read-only\" permission cannot download attachments in cells", "zh_HK": "在站內和站外,擁有「只可閱讀」權限的用戶也可以下載單元格內的附件。關閉該功能後,下載入口將屏蔽" }, + "security_setting_download_file_description": { + "zh_CN": "在站内和站外,拥有「只可阅读」权限的用户无法下载单元格内的附件", + "en_US": "Users with \"Read-only\" permission cannot download attachments in cells", + "zh_HK": "在站内和站外,拥有「只可阅读」权限的用户无法下载单元格内的附件" + }, "security_setting_download_file_title": { - "zh_CN": "禁止「只可阅读」用户可下载附件", - "en_US": "Allow read-only users to download attachments", + "zh_CN": "禁止「只可阅读」用户下载附件", + "en_US": "Prevent \"Read-only\" users from downloading attachments", "zh_HK": "「只可閱讀」用戶可下載附件" }, "security_setting_export": { - "zh_CN": "你正在体验${grade}空间站专属功能,升级后可以解锁该功能", + "zh_CN": "你正在使用${grade}空间站专属功能,升级后可以解锁该功能", "en_US": "You are experiencing the ${grade} space exclusive feature, which will be unlocked when you upgrade", "zh_HK": "你正在體驗${grade}空間站專屬功能,升級後可以解鎖該功能" }, "security_setting_export_data_describle": { - "zh_CN": "对应角色的成员可以将工作目录中的维格表或者视图数据导出到本地。关闭该功能后,导出入口将屏蔽。", - "en_US": "After the function is disabled,all members cannot export datasheet or view data locally", + "zh_CN": "指定对应权限的成员可以导出维格表和视图数据到本地。未开启时,仅空间站管理员可导出数据", + "en_US": "Specify members to export data.If the function is disabled,only space andmin can export data.", + "zh_HK": "對應角色的成員可以將工作目錄中的維格表或者視圖數據導出到本地。關閉該功能後,導出入口將屏蔽。" + }, + "security_setting_export_data_description": { + "zh_CN": "指定对应权限的成员可以导出维格表和视图数据到本地。未开启时,仅空间站管理员可导出数据", + "en_US": "Specify members to export data.If the function is disabled,only space andmin can export data.", "zh_HK": "對應角色的成員可以將工作目錄中的維格表或者視圖數據導出到本地。關閉該功能後,導出入口將屏蔽。" }, "security_setting_export_data_editable": { "zh_CN": "可以编辑及以上", - "en_US": "above “Editor”", + "en_US": "above \"Editor\"", "zh_HK": "可以編輯及以上" }, "security_setting_export_data_manageable": { "zh_CN": "可以管理及以上", - "en_US": "above “Manageable”", + "en_US": "above \"Manager\"", "zh_HK": "可以管理及以上" }, "security_setting_export_data_read_only": { "zh_CN": "只可阅读及以上", - "en_US": "above “Read-only”", + "en_US": "above \"Read-only\"", "zh_HK": "只可閱讀及以上" }, "security_setting_export_data_title": { - "zh_CN": "禁止成员可导出维格表和视图", - "en_US": "Allow to export files", + "zh_CN": "指定成员可导出维格表和视图", + "en_US": "Specify members to export datasheet and views", "zh_HK": "成員可導出維格表和視圖" }, "security_setting_export_data_tooltips": { @@ -19640,7 +20244,7 @@ "zh_HK": "只更新及以上" }, "security_setting_invite_member": { - "zh_CN": "你正在体验${grade}空间站专属功能,升级后可以解锁该功能", + "zh_CN": "你正在使用${grade}空间站专属功能,升级后可以解锁该功能", "en_US": "You are experiencing the ${grade} space exclusive feature, which will be unlocked when you upgrade", "zh_HK": "你正在體驗${grade}空間站專屬功能,升級後可以解鎖該功能" }, @@ -19649,29 +20253,39 @@ "en_US": "After the function is disabled,no one can invite users to join the Space except Space admin,and the generated invitation link will be invalid", "zh_HK": "普通成員可以邀請用戶加入空間站。關閉功能後僅空間站管理員可邀請成員加入空間站,普通成員創建的邀請鏈接會失效" }, + "security_setting_invite_member_description": { + "zh_CN": "仅空间站管理员可邀请成员加入空间站,普通成员创建的邀请链接以及分享下的快速邀请方式都会失效。", + "en_US": "No one can invite users to join the Space except Space admin,and the generated invitation link will be invalid", + "zh_HK": "普通成員可以邀請用戶加入空間站。關閉功能後僅空間站管理員可邀請成員加入空間站,普通成員創建的邀請鏈接會失效" + }, "security_setting_invite_member_title": { - "zh_CN": "禁止成员可邀请站外用户加入空间站", - "en_US": "Members can Invite off-space users to join the space", + "zh_CN": "禁止成员邀请站外用户加入空间站", + "en_US": "Prevent members from inviting user", "zh_HK": "成員可邀請站外用戶加入空間站" }, "security_setting_mobile": { - "zh_CN": "你正在体验${grade}空间站专属功能,升级后可以解锁该功能", + "zh_CN": "你正在使用${grade}空间站专属功能,升级后可以解锁该功能", "en_US": "You are experiencing the ${grade} space exclusive feature, which will be unlocked when you upgrade", "zh_HK": "你正在體驗${grade}空間站專屬功能,升級後可以解鎖該功能" }, "security_setting_share": { - "zh_CN": "你正在体验${grade}空间站专属功能,升级后可以解锁该功能", + "zh_CN": "你正在使用${grade}空间站专属功能,升级后可以解锁该功能", "en_US": "You are experiencing the ${grade} space exclusive feature, which will be unlocked when you upgrade", "zh_HK": "你正在體驗${grade}空間站專屬功能,升級後可以解鎖該功能" }, "security_setting_share_describle": { - "zh_CN": "成员可以将工作目录的文件节点创建公开链接分享给外部用户。关闭该功能后,已生成的公开分享链接将暂时失效,包括管理员在内所有人无法创建公开链接。", - "en_US": "After the function is disabled,all members cannot creat the public link of the file, and the generated public link becomes invalid", + "zh_CN": "工作目录内所有已生成的公开分享链接将暂时失效,包括管理员在内所有人无法创建空开链接", + "en_US": "All members cannot creat the public link of the file, and the generated public link becomes invalid", + "zh_HK": "成員可以將工作目錄的文件節點創建公開鏈接分享給外部用戶。關閉該功能後,已生成的公開分享鏈接將暫時失效,包括管理員在內所有人無法創建公開鏈接。" + }, + "security_setting_share_description": { + "zh_CN": "工作目录内所有已生成的公开分享链接将暂时失效,包括管理员在内所有人无法创建公开链接", + "en_US": "All members cannot creat the public link of the file, and the generated public link becomes invalid", "zh_HK": "成員可以將工作目錄的文件節點創建公開鏈接分享給外部用戶。關閉該功能後,已生成的公開分享鏈接將暫時失效,包括管理員在內所有人無法創建公開鏈接。" }, "security_setting_share_title": { - "zh_CN": "禁止成员可创建文件节点的公开链接", - "en_US": "Allow to create public link", + "zh_CN": "禁止成员创建文件节点的公开链接", + "en_US": "Prevent members from creating public links", "zh_HK": "成員可創建文件節點的公開鏈接" }, "security_show_mobile": { @@ -19684,6 +20298,11 @@ "en_US": "Show all members' phone numbers in contacts", "zh_HK": "在「通訊錄」中完整顯示所有成員的手機號信息" }, + "security_show_mobile_description": { + "zh_CN": "在「通讯录」中完整显示所有成员的手机号信息", + "en_US": "Show all members' phone numbers in contacts", + "zh_HK": "在「通訊錄」中完整顯示所有成員的手機號信息" + }, "security_show_mobile_modal_describle": { "zh_CN": "在「通讯录」中所有成员无法查看手机号", "en_US": "The mobile phone number is not displayed in the contacts", @@ -19704,6 +20323,11 @@ "en_US": "The workbench and the contacts support watermarks to prevent members from leaking enterprise information. A watermark includes the name and phone number suffix/email prefix.", "zh_HK": "為了保證企業信息安全,工作台和通訊錄支持顯示全局水印。水印內容為當前訪問成員的姓名+手機號後綴或者郵箱前綴,防止截屏洩密。" }, + "security_show_watermark_description": { + "zh_CN": "为了保证企业信息安全,工作台和通讯录支持显示全局水印。水印内容为当前访问成员的姓名+手机号后缀或者邮箱前缀,防止截屏泄密。", + "en_US": "The workbench and the contacts support watermarks to prevent members from leaking enterprise information. A watermark includes the name and phone number suffix/email prefix.", + "zh_HK": "為了保證企業信息安全,工作台和通訊錄支持顯示全局水印。水印內容為當前訪問成員的姓名+手機號後綴或者郵箱前綴,防止截屏洩密。" + }, "security_show_watermark_modal_describle": { "zh_CN": "为了保证企业信息安全,工作台和通讯录支持显示全局水印。水印内容为当前访问成员的姓名+手机号后缀或者邮箱前缀,防止截屏泄密。", "en_US": "The watermarks is not displayed in the contacts", @@ -19817,7 +20441,6 @@ "select_phone_code": { "zh_CN": "选择国家和地区", "en_US": "Select country and region", - "终端": "APP", "zh_HK": "選擇國家和地區" }, "select_sort_rule": { @@ -19842,7 +20465,7 @@ }, "select_view": { "zh_CN": "从下方视图读取数据", - "en_US": "Select a view as data source", + "en_US": "Select a view as the data source", "zh_HK": "從下方視圖讀取數據" }, "select_wdget_Import_widget": { @@ -20015,7 +20638,7 @@ }, "set_field": { "zh_CN": "设置列", - "en_US": "Field settings", + "en_US": "Field setting", "zh_HK": "設置列" }, "set_field_permission_modal_title": { @@ -20030,7 +20653,7 @@ }, "set_field_required": { "zh_CN": "设置为神奇表单必填项", - "en_US": "Set as required in forms", + "en_US": "Set as required on forms", "zh_HK": "設置為神奇表單必填項" }, "set_field_required_tip_1": { @@ -20071,7 +20694,6 @@ "set_new_password": { "zh_CN": "设置新密码", "en_US": "Set a new password", - "终端": "APP", "zh_HK": "設置新密碼" }, "set_nickname": { @@ -20115,9 +20737,9 @@ "zh_HK": "添加${node_role}成員" }, "set_permission_modal_help": { - "zh_CN": "/help/faq-permission-settings/", - "en_US": "/help/faq-permission-settings/", - "zh_HK": "/help/faq-permission-settings/" + "zh_CN": "https://help.vika.cn/docs/guide/faq-permission-settings", + "en_US": "https://help.vika.cn/docs/guide/faq-permission-settings", + "zh_HK": "https://help.vika.cn/docs/guide/faq-permission-settings" }, "set_permission_modal_radio_1": { "zh_CN": "继承上级权限", @@ -20184,6 +20806,10 @@ "en_US": "Share", "zh_HK": "分享" }, + "share_and_collaboration": { + "zh_CN": "分享与协作", + "en_US": "Sharing and collaboration" + }, "share_and_editable_desc": { "zh_CN": "他人可以在分享页面下协作编辑表格的内容", "en_US": "Others can edit the content of the datasheet within the shared page", @@ -20274,6 +20900,11 @@ "en_US": "You're sharing a datasheet that contains ${content}. Others can view it via the sharing page. Please make sure you don't disclose any sensitive information. Linked datasheets are:\n", "zh_HK": "當前的分享存在${content},他人可以在分享頁面看到關聯數據,請確認數據的保密性,以下是關聯的表格名字:\n" }, + "share_fail_og_description_content": { + "zh_CN": "该分享的公开链接已被关闭,暂时无法访问", + "en_US": "The shared public link has been closed and is temporarily unavailable", + "zh_HK": "該分享的公開鏈接已被關閉,暫時無法訪問" + }, "share_failed": { "zh_CN": "分享失败", "en_US": "Sharing failed", @@ -20415,7 +21046,7 @@ }, "share_qr_code_tips": { "zh_CN": "通过二维码分享", - "en_US": "Via QR code", + "en_US": "Share via QR code", "zh_HK": "通過二維碼分享" }, "share_reader": { @@ -20558,7 +21189,7 @@ }, "show_smooth_line": { "zh_CN": "开启平滑曲线", - "en_US": "Show smooth line", + "en_US": "Show smooth curve", "zh_HK": "開啟平滑曲線" }, "sierra_leone": { @@ -20571,6 +21202,11 @@ "en_US": "Sign up", "zh_HK": "註冊" }, + "signin_idaas_official_account": { + "zh_CN": "维格星球", + "en_US": "Vika Planet", + "zh_HK": "維格星球" + }, "silver": { "zh_CN": "白银级", "en_US": "Silver", @@ -20579,7 +21215,12 @@ "silver_grade": { "zh_CN": "白银级", "en_US": "Silver", - "zh_HK": "白銀級" + "zh_HK": "白銀級", + "billing": { + "products": [ + "recNCNkhWw4GR" + ] + } }, "silver_grade_6months_time_machine": { "zh_CN": "升级到白银PRO空间站,查看6个月历史修订记录", @@ -20588,6 +21229,7 @@ }, "silver_grade_desc": { "zh_CN": "适用于业务快速发展的团队或组织", + "en_US": "For teams or organizations with rapid business growth", "zh_HK": "適用於業務快速發展的團隊或組織" }, "silver_grade_unlimited": { @@ -20600,6 +21242,28 @@ "en_US": "URL", "zh_HK": "URL" }, + "silver_seat_100_desc": { + "zh_CN": "100人", + "en_US": "100", + "billing": { + "prices": [ + "recNwufda6OZq", + "recA3gw13IvAp", + "recbfADEFPBqX" + ] + } + }, + "silver_seat_2_desc": { + "zh_CN": "2人(买一送一)", + "en_US": "2(50% OFF)", + "billing": { + "prices": [ + "recH8KkKScyN8", + "recAaT4PnqpCW", + "recI4j8mPN58r" + ] + } + }, "singapore": { "zh_CN": "新加坡", "en_US": "Singapore", @@ -20655,6 +21319,9 @@ "en_US": "Slovenia", "zh_HK": "斯洛文尼亞" }, + "social_dingtalk_single_record_comment_mention": { + "zh_CN": "### 🔔有人在评论中提及你\n\n**所在表:** {nodeName}\n\n**记录:** {recordTitle}\n\n“${commentContent}”" + }, "social_dingtalk_single_record_member_mention": { "zh_CN": "### 🔔有人在记录中提及你\n\n**记录:** {recordTitle}\n\n**提及人:** {memberName}\n\n**维格表:** {nodeName}\n\n**操作时间:** {createdAt}", "en_US": "### 🔔 You're mentioned in a record\n\n** record: ** {recordTitle}\n\n** Mentioned by: ** {memberName}\n\n** Datasheet: ** {nodeName}\n\n** Operation time: ** {createdAt}", @@ -20668,12 +21335,7 @@ "social_dingtalk_subscribed_record_cell_updated_title": { "zh_CN": "关注的记录被修改", "en_US": "Records modified by others", - "zh_HK": "關注的記錄被修改", - "notifications": { - "social_templates copy": [ - "recSLnaXHbcIr" - ] - } + "zh_HK": "關注的記錄被修改" }, "social_dingtalk_subscribed_record_commented": { "zh_CN": "### 关注的记录有一条新评论\n\n**记录:** {recordTitle}\n\n**评论的内容:** {content}\n\n**评论人:** {memberName}\n\n**维格表:** {nodeName}", @@ -20683,12 +21345,7 @@ "social_dingtalk_subscribed_record_commented_title": { "zh_CN": "关注的记录有一条新评论", "en_US": "Record received a new comment", - "zh_HK": "關注的記錄有一條新評論", - "notifications": { - "social_templates copy": [ - "recMdXxGr1JIE" - ] - } + "zh_HK": "關注的記錄有一條新評論" }, "social_dingtalk_task_reminder": { "zh_CN": "### 🔔表中的记录已到提醒时间\n\n**记录标题:** {recordTitle}\n\n**到期时间:** {taskExpireAt}\n\n**所在表:** {nodeName}", @@ -20703,12 +21360,7 @@ "social_lark_task_reminder_title": { "zh_CN": "**表中的记录已到提醒时间**", "en_US": "** The record in the datasheet has reached the reminder time * *", - "zh_HK": "**表中的記錄已到提醒時間**", - "notifications": { - "social_templates copy": [ - "recwyPz9I1OVg" - ] - } + "zh_HK": "**表中的記錄已到提醒時間**" }, "social_media": { "zh_CN": "新媒体运营", @@ -20731,9 +21383,9 @@ "zh_HK": "該空間已綁定了其他第三方應用" }, "social_plat_bind_space_seats_err": { - "zh_CN": "当前空间站成员数上限为${count}人,如想提高上限, 请联系我们", - "en_US": "Your Space can have a maximum of ${count} members. Contact us if you want to invite more. ", - "zh_HK": "當前空間站成員數上限為${count}人,如想提高上限, 請聯繫我們" + "zh_CN": "当前空间站成员数上限为${count}人,如想提高上限, 请联系我们", + "en_US": "Your Space can have a maximum of ${count} members. Contact us if you want to invite more. ", + "zh_HK": "當前空間站成員數上限為${count}人,如想提高上限, 請聯繫我們" }, "social_plat_space_list_item_seats_msg": { "zh_CN": "(最多${max}人)", @@ -20743,13 +21395,7 @@ "social_task_reminder_title": { "zh_CN": "🔔 表中的记录已到提醒时间", "en_US": "🔔 Reminder", - "zh_HK": "🔔 表中的記錄已到提醒時間", - "notifications": { - "social_templates copy": [ - "recRkZamjppbd", - "reczGXSYPC6y1" - ] - } + "zh_HK": "🔔 表中的記錄已到提醒時間" }, "social_wecom_single_record_member_mention": { "zh_CN": "记录:{recordTitle}\n提及人:{memberName}\n维格表:{nodeName}", @@ -20833,7 +21479,7 @@ }, "sort_apply": { "zh_CN": "应用", - "en_US": "Application", + "en_US": "Apply", "zh_HK": "應用" }, "sort_by_option_order": { @@ -20857,9 +21503,9 @@ "zh_HK": "按 ${from} → ${to} 排列" }, "sort_help_url": { - "zh_CN": "/help/manual-sort/", - "en_US": "/help/manual-sort/", - "zh_HK": "/help/manual-sort/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-sort", + "en_US": "https://help.vika.cn/docs/guide/manual-sort", + "zh_HK": "https://help.vika.cn/docs/guide/manual-sort" }, "sort_link_data": { "zh_CN": "排序引用的数据", @@ -20908,7 +21554,7 @@ }, "space_admin_info": { "zh_CN": "添加子管理员协助你管理空间站", - "en_US": "With your Space's subscription plan, you can assign ${count} sub-admins.", + "en_US": "With your Space's subscription plan, you can assign ${count} sub-admins", "zh_HK": "添加子管理員協助你管理空間站" }, "space_admin_level": { @@ -20922,9 +21568,9 @@ "zh_HK": "空間站管理員數已超出上限(/),升級可獲得更高用量" }, "space_admin_limit_email_title": { - "zh_CN": "「{SPACE_NAME}」空间站的管理员数已超出上限({USAGE}/{SPECIFICATION})", - "en_US": "Usage reminders for the number of sub-admins in the \"${SPACE_NAME}\" space ", - "zh_HK": "「{SPACE_NAME}」空間站的管理員數已超出上限({USAGE}/{SPECIFICATION})" + "zh_CN": "「${SPACE_NAME}」空间站的子管理员数的用量提醒", + "en_US": "Usage reminders for the number of sub-admins in the \"${SPACE_NAME}\" space", + "zh_HK": "「${SPACE_NAME}」空間站的子管理員數的用量提醒" }, "space_admins_3_up": { "zh_CN": "哟,你的空间站管理员已经达到 3 人上限", @@ -20942,9 +21588,9 @@ "zh_HK": "空間站 API 用量已超出上限(/),升級可獲得更高用量" }, "space_api_limit_email_title": { - "zh_CN": "「{SPACE_NAME}」空间站的 API 用量已超出上限({USAGE}/{SPECIFICATION})", - "en_US": "Usage reminders for the number of api usages per month in the \"${SPACE_NAME}\" space ", - "zh_HK": "「{SPACE_NAME}」空間站的 API 用量已超出上限({USAGE}/{SPECIFICATION})" + "zh_CN": "「${SPACE_NAME}」空间站的每月API调用数的用量提醒", + "en_US": "Usage reminders for the number of api usages per month in the \"${SPACE_NAME}\" space", + "zh_HK": "「${SPACE_NAME}」空間站的每月API調用數的用量提醒" }, "space_assigned_to_group": { "zh_CN": "将你分配到空间站「」的小组「」", @@ -20962,14 +21608,19 @@ "zh_HK": "空間站日曆視圖數已超出上限(/),升級可獲得更高用量" }, "space_calendar_limit_email_title": { - "zh_CN": "「{SPACE_NAME}」空间站的日历视图使用数已超出上限({USAGE}/{SPECIFICATION})", - "en_US": "The Space \"{SPACE_NAME}\" has the maximum number of Calendar views ({USAGE}/{SPECIFICATION})", - "zh_HK": "「{SPACE_NAME}」空間站的日曆視圖使用數已超出上限({USAGE}/{SPECIFICATION})" + "zh_CN": "「${SPACE_NAME}」空间站的日历视图数的用量提醒", + "en_US": "Usage reminders for the number of calendar views in the \"${SPACE_NAME}\" space", + "zh_HK": "「${SPACE_NAME}」空間站的日曆視圖數的用量提醒" }, "space_capacity": { "zh_CN": "附件容量", "en_US": "Attachments storage", - "zh_HK": "附件容量" + "zh_HK": "附件容量", + "billing": { + "products": [ + "rec3WFmkM3seX" + ] + } }, "space_capacity_1g_limit_tips": { "zh_CN": "已超过附件空间大小,升级为高级空间后可享受超大空间", @@ -20983,7 +21634,7 @@ }, "space_certification_notify": { "zh_CN": " 恭喜你完成维格空间站认证,获得GB 附件容量,详情请前往「空间站设置-空间站驾驶舱-附件容量」查看", - "en_US": "Congratulation. You have completed the certification of vika space station. Go through 「Settings-Overview-Attachments storage」 to receive your GB capacity package.", + "en_US": "Congratulation. You have completed the certification of space. Go through 「Settings-Overview-Attachments storage」 to receive your GB capacity package.", "zh_HK": " 恭喜你完成維格空間站認證,獲得GB 附件容量,詳情請前往「空間站設置-空間站駕駛艙-附件容量」查看" }, "space_changed_ordinary_user": { @@ -21008,7 +21659,7 @@ }, "space_corp_uncertified_tooltip": { "zh_CN": "完善空间站信息,获得 5G 容量包", - "en_US": "Complete space information to get 5G capacity package", + "en_US": "Complete space information to get a free 5G attachment storage package", "zh_HK": "完善空間站信息,獲得 5G 容量包" }, "space_dashboard_contact": { @@ -21047,9 +21698,9 @@ "zh_HK": "空間站列權限數已超出上限(/),升級可獲得更高用量" }, "space_field_permission_limit_email_title": { - "zh_CN": "「{SPACE_NAME}」空间站的列权限使用数已超出上限({USAGE}/{SPECIFICATION})", + "zh_CN": "「${SPACE_NAME}」空间站的列权限数的用量提醒", "en_US": "Usage reminders for the number of field permissions in the \"${SPACE_NAME}\" space", - "zh_HK": "「{SPACE_NAME}」空間站的列權限使用數已超出上限({USAGE}/{SPECIFICATION})" + "zh_HK": "「${SPACE_NAME}」空間站的列許可權數的用量提醒" }, "space_file_permission_limit": { "zh_CN": "你管理的「」空间站文件权限数上限为 个,当前已使用 个,升级可获得更高用量。", @@ -21066,9 +21717,9 @@ "zh_HK": "空間站神奇表單數已超出上限(/),升級可獲得更高用量" }, "space_form_limit_email_title": { - "zh_CN": "「{SPACE_NAME}」空间站的神奇表单使用数已超出上限({USAGE}/{SPECIFICATION})", - "en_US": "Usage reminders for the number of forms in the \"${SPACE_NAME}\" space ", - "zh_HK": "「{SPACE_NAME}」空間站的神奇表單使用數已超出上限({USAGE}/{SPECIFICATION})" + "zh_CN": "「${SPACE_NAME}」空间站的神奇表单数的用量提醒", + "en_US": "Usage reminders for the number of forms in the \"${SPACE_NAME}\" space", + "zh_HK": "「${SPACE_NAME}」空間站的神奇表單數的用量提醒" }, "space_free_capacity_expansion": { "zh_CN": "免费扩容", @@ -21081,9 +21732,9 @@ "zh_HK": "空間站甘特視圖數已超出上限(/),升級可獲得更高用量" }, "space_gantt_limit_email_title": { - "zh_CN": "「{SPACE_NAME}」空间站的甘特视图使用数已超出上限({USAGE}/{SPECIFICATION})", - "en_US": "Usage reminders for the number of gannt views in the \"${SPACE_NAME}\" space ", - "zh_HK": "「{SPACE_NAME}」空間站的甘特視圖使用數已超出上限({USAGE}/{SPECIFICATION})" + "zh_CN": "「${SPACE_NAME}」空间站的甘特视图数的用量提醒", + "en_US": "Usage reminders for the number of gannt views in the \"${SPACE_NAME}\" space", + "zh_HK": "「${SPACE_NAME}」空間站的甘特視圖數的用量提醒" }, "space_guide_step_one_desc": { "zh_CN": "空间站马上就要建好了🔨
    \n在这之前,需要重新更换一下空间站头像和名称吗?", @@ -21197,7 +21848,7 @@ }, "space_log_file_name": { "zh_CN": "文件名称", - "en_US": "Node name", + "en_US": "File name", "zh_HK": "文件名稱" }, "space_log_operator": { @@ -21211,9 +21862,9 @@ "zh_HK": "空間站操作日誌" }, "space_log_trial_button": { - "zh_CN": "立即试用", + "zh_CN": "了解详情", "en_US": "Try now", - "zh_HK": "立即試用" + "zh_HK": "了解详情" }, "space_log_trial_desc1": { "zh_CN": "你可以在当前页面查看 ${days} 天内的操作日志", @@ -21221,14 +21872,14 @@ "zh_HK": "你可以在當前頁面查看 ${days} 天內的操作日誌" }, "space_log_trial_desc2": { - "zh_CN": "你可以在当前页面查看 ${days} 天内的操作日志,升级「企业级」后可以查看更多。", - "en_US": "You are now on a trial of the advanced feature to view the space logs within ${days} days", - "zh_HK": "你可以在當前頁面查看 ${days} 天內的操作日誌。" + "zh_CN": "点击下方按钮可以升级或者添加我们客服了解功能详情", + "en_US": "Click the button below to upgrade or add our customer service to learn more about the features", + "zh_HK": "點擊下方按鈕可以升級或者添加我們客服了解功能詳情" }, "space_log_trial_desc3": { - "zh_CN": "该功能是「企业级」空间站专属功能,限时开放试用", - "en_US": "Now free trial of exclusive feature only for enterprise spaces", - "zh_HK": "該功能是「企業級」空間站專屬功能,限時開放試用" + "zh_CN": "该功能是「企业级」空间站专属功能,升级后可以解锁该功能", + "en_US": "An exclusive feature only for enterprise spaces, unlock after upgrading the space", + "zh_HK": "該功能是「企業級」空間站專屬功能,升級後可以解鎖該功能" }, "space_logo": { "zh_CN": "空间站LOGO图标", @@ -21343,9 +21994,9 @@ "zh_HK": "空間站總記錄數已超出上限(/),升級可獲得更高用量" }, "space_record_limit_email_title": { - "zh_CN": "「{SPACE_NAME}」空间站的记录数已超出上限({USAGE}/{SPECIFICATION})", + "zh_CN": "「${SPACE_NAME}」空间站的总记录数的用量提醒", "en_US": "Usage reminders for the number of total records in the \"${SPACE_NAME}\" space", - "zh_HK": "「{SPACE_NAME}」空間站的記錄數已超出上限({USAGE}/{SPECIFICATION})" + "zh_HK": "「${SPACE_NAME}」空間站的總記錄數的用量提醒" }, "space_seat_info": { "zh_CN": "空间站人数:${num} 人", @@ -21358,9 +22009,9 @@ "zh_HK": "空間站現有付費成員數已超出上限(/),升級可獲得更高用量" }, "space_seats_limit_email_title": { - "zh_CN": "「{SPACE_NAME}」空间站的付费席位数已超出上限({USAGE}/{SPECIFICATION})", - "en_US": "Usage reminders for the number of seats in the \"${SPACE_NAME}\" space ", - "zh_HK": "「{SPACE_NAME}」空間站的付費席位數已超出上限({USAGE}/{SPECIFICATION})" + "zh_CN": "「${SPACE_NAME}」空间站的付费成员数的用量提醒", + "en_US": "Usage reminders for the number of seats in the \"${SPACE_NAME}\" space", + "zh_HK": "「${SPACE_NAME}」空間站的付費成員數的用量提醒" }, "space_setting": { "zh_CN": "空间站管理", @@ -21373,7 +22024,7 @@ }, "space_setting_social_ad_decs": { "zh_CN": "完成空间站认证,将可以获得免费的 5G 附件容量包", - "en_US": "Certified current space will get a free 5G attachment capacity package" + "en_US": "Certified current space will get a free 5G attachment storage package" }, "space_subscription_notify": { "zh_CN": "恭喜!你已成功订阅「」,到期时间为 ", @@ -21408,11 +22059,6 @@ "en_US": "Congrats! Your \"\" Space has upgraded to the (on trial). The plan will expire at .", "zh_HK": "恭喜你的空間站「」已成功訂閱(試用),有效期至" }, - "space_vika_paid_notify": { - "zh_CN": "恭喜你,空间站升级成功,可前往「空间站驾驶舱」 查看最新权益", - "en_US": "Congratulations on your successful upgrade, you can go to the \"space dashboard\" to see the latest benefits", - "zh_HK": "恭喜你,空間站升級成功,可前往「空間站駕駛艙」 查看最新權益" - }, "space_watermark_notify": { "zh_CN": "空间站正在使用企业级空间站专属功能「全局水印」,请升级以继续使用", "en_US": "The Space is using the enterprise-level feature \"Global Watermarks\". Please upgrade to continue using it.", @@ -21549,7 +22195,9 @@ "zh_HK": "創業" }, "startup_company_support_program": { - "zh_CN": "创业公司扶持计划" + "zh_CN": "创业公司扶持计划", + "en_US": "Startup support Program", + "zh_HK": "創業公司扶持計劃" }, "stat_average": { "zh_CN": "平均值", @@ -21608,7 +22256,7 @@ }, "stat_none": { "zh_CN": "不展示", - "en_US": "Don't show", + "en_US": "None", "zh_HK": "不展示" }, "stat_percent_checked": { @@ -21658,7 +22306,7 @@ }, "statistics": { "zh_CN": "统计", - "en_US": "Statistics", + "en_US": "Summary", "zh_HK": "統計" }, "status_code_inviter_space_member_limit": { @@ -21855,6 +22503,16 @@ "en_US": "Advanced space features", "zh_HK": "${grade}以上空間站專享功能" }, + "subscribe_new_choose_member": { + "zh_CN": "最高支持 ${member_num} 人", + "en_US": "Supports up to ${member_num} members", + "zh_HK": "最高支持 {member_num} 人" + }, + "subscribe_new_choose_member_tips": { + "zh_CN": "本方案支持 1~${member_num} 名成员进入空间站使用", + "en_US": "This plan supports 1~${member_num} members to enter the space", + "zh_HK": "本方案支持 1~${member_num} 名成員進入空間站使用" + }, "subscribe_success_desc": { "zh_CN": "恭喜!你已成功订阅,可前往空间站驾驶舱查看相关权益", "en_US": "Congrats! Your subscription is activated and starts from today. Go to Overview to check your benefits.", @@ -21865,6 +22523,16 @@ "en_US": "Payment successful", "zh_HK": "支付成功" }, + "subscribe_upgrade_choose_member": { + "zh_CN": "${old_member_num} 人升级为 ${new_member_num} 人", + "en_US": "${old_member_num} members are upgraded to ${new_member_num} members", + "zh_HK": "${old_member_num} 人升級為 ${new_member_num} 人" + }, + "subscribe_upgrade_choose_member_tips": { + "zh_CN": "你的原方案最高支持 ${old_member_num} 名成员,本次升级将扩容到 ${new_member_num} 名成员", + "en_US": "Your original plan supports a maximum of ${old_member_num} members, but this upgrade will be expanded to ${new_member_num} members.", + "zh_HK": "你的原方案最高支持 ${old_member_num} 名成員,本次升級將擴容到 ${new_member_num} 名成員" + }, "subscribe_welcome_tip": { "zh_CN": "特别提示:公测阶段,你可以超量邀请成员(最多100人)和创建维格表(最多1000个)。", "en_US": "Notice: During the public beta, you can invite 100 members and create 1,000 datasheets at most.", @@ -21891,13 +22559,20 @@ "zh_HK": "訂閱費用:${fee}" }, "subscription_grades_checklist": { - "zh_CN": "{\n \"用量\": [\n {\n \"group\": \"用量\",\n \"id\": \"datasheet_number\",\n \"title\": \"文件节点数量\",\n \"bronze\": \"30 个\",\n \"silver\": \"300 个\",\n \"gold\": \"1000 个\",\n \"enterprise\": \"1 万个\",\n \"community\":\"1 个\",\n \"custom\":\"1 个\"\n },\n {\n \"group\": \"用量\",\n \"id\": \"datasheet_rows\",\n \"title\": \"单维格表行数\",\n \"bronze\": \"5,000 行\",\n \"silver\": \"1 万行\",\n \"gold\": \"2 万行\",\n \"enterprise\": \"5 万行\"\n },\n {\n \"group\": \"用量\",\n \"id\": \"space_rows\",\n \"title\": \"空间站总行数\",\n \"bronze\": \"2 万行\",\n \"silver\": \"300 万行\",\n \"gold\": \"2000 万行\",\n \"enterprise\": \"5 亿行\"\n },\n {\n \"group\": \"用量\",\n \"id\": \"space_capacity\",\n \"title\": \"附件容量\",\n \"bronze\": \"1GB 默认\",\n \"silver\": \"席位数 * 5GB\",\n \"gold\": \"席位数 * 7GB\",\n \"enterprise\": \"席位数 * 10GB\"\n }\n ],\n \"功能\": [\n {\n \"group\": \"功能\",\n \"id\": \"star_marker\",\n \"title\": \"星标\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true\n },\n {\n \"group\": \"功能\",\n \"id\": \"album_view\",\n \"title\": \"相册视图\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true\n },\n {\n \"group\": \"功能\",\n \"id\": \"kanban_view\",\n \"title\": \"看板视图\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true\n },\n {\n \"group\": \"功能\",\n \"id\": \"org_chart_view\",\n \"title\": \"架构视图\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true\n },\n {\n \"group\": \"功能\",\n \"id\": \"gantt_view\",\n \"title\": \"甘特视图\",\n \"bronze\": \"单空间站 10 个\",\n \"silver\": \"单空间站 50 个\",\n \"gold\": \"单空间站 200 个\",\n \"enterprise\": \"不限\"\n },\n {\n \"group\": \"功能\",\n \"id\": \"calendar_view\",\n \"title\": \"日历视图\",\n \"bronze\": \"单空间站 5 个\",\n \"silver\": \"单空间站 50 个\",\n \"gold\": \"单空间站 200 个\",\n \"enterprise\": \"不限\"\n },\n {\n \"group\": \"功能\",\n \"id\": \"collection_table\",\n \"title\": \"神奇表单\",\n \"bronze\": \"单空间站 20 张\",\n \"silver\": \"单空间站 100 张\",\n \"gold\": \"单空间站 300 张\",\n \"enterprise\": \"不限\"\n },\n {\n \"group\": \"功能\",\n \"id\": \"mirror\",\n \"title\": \"镜像(行权限)\",\n \"bronze\": \"单空间站 5 个\",\n \"silver\": \"单空间站 50 个\",\n \"gold\": \"单空间站 100 个\",\n \"enterprise\": \"不限\"\n },\n {\n \"group\": \"功能\",\n \"id\": \"widget\",\n \"title\": \"小程序\",\n \"bronze\": \"不限,基础 + 自建\",\n \"silver\": \"不限,基础 + 自建\",\n \"gold\": \"不限,基础 + 自建\",\n \"enterprise\": \"不限,基础 + 自建 + 定制\"\n },\n {\n \"group\": \"功能\",\n \"id\": \"dashboard\",\n \"title\": \"仪表盘\",\n \"bronze\": \"单空间站 5 个\",\n \"silver\": \"单空间站 50 个\",\n \"gold\": \"单空间站 100 个\",\n \"enterprise\": \"不限\"\n },\n {\n \"group\": \"功能\",\n \"id\": \"rainbow_tags\",\n \"title\": \"彩虹标签\",\n \"bronze\": \"基础色系(20 个)\",\n \"silver\": \"彩虹色系(50 个)\",\n \"gold\": \"彩虹色系(50 个)\",\n \"enterprise\": \"彩虹色系(50 个)\"\n },\n {\n \"group\": \"功能\",\n \"id\": \"full_platform_client_support\",\n \"title\": \"全平台客户端支持\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true\n },\n {\n \"group\": \"功能\",\n \"id\": \"space_capacity\",\n \"title\": \"时光机(历史记录)\",\n \"bronze\": \"追溯 14 天\",\n \"silver\": \"追溯 3 个月\",\n \"gold\": \"追溯 6 个月\",\n \"enterprise\": \"追溯 24 个月\"\n },\n {\n \"group\": \"功能\",\n \"id\": \"recycle_bin\",\n \"title\": \"回收舱(历史文件)\",\n \"bronze\": \"追溯 14 天\",\n \"silver\": \"追溯 3 个月\",\n \"gold\": \"追溯 6 个月\",\n \"enterprise\": \"追溯 24 个月\"\n },\n {\n \"group\": \"功能\",\n \"id\": \"api_limits\",\n \"title\": \"API 用量\",\n \"bronze\": \"1 万次请求/月\",\n \"silver\": \"10 万次请求/月\",\n \"gold\": \"50 万次请求/月\",\n \"enterprise\": \"100 万次请求/月\"\n },\n {\n \"group\": \"功能\",\n \"id\": \"robot\",\n \"title\": \"机器人\",\n \"bronze\": \"限时免费\",\n \"silver\": \"限时免费\",\n \"gold\": \"限时免费\",\n \"enterprise\": \"限时免费\"\n }\n ],\n \"管理和安全\": [\n {\n \"group\": \"管理和安全\",\n \"id\": \"column_permission\",\n \"title\": \"列权限\",\n \"bronze\": \"单空间站 10 列\",\n \"silver\": \"单空间站 50 列\",\n \"gold\": \"单空间站 200 列\",\n \"enterprise\": \"不限\"\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"folder_permission\",\n \"title\": \"文件权限\",\n \"bronze\": \"单空间站 10 个\",\n \"silver\": \"单空间站 50 个\",\n \"gold\": \"单空间站 200 个\",\n \"enterprise\": \"不限\"\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"admin_counts\",\n \"title\": \"管理员数量\",\n \"bronze\": \"3 个\",\n \"silver\": \"5 个\",\n \"gold\": \"10 个\",\n \"enterprise\": \"不限\"\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"marketplace_integration_app_name_officepreview\",\n \"title\": \"Office 文件预览\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"feishu_integration\",\n \"title\": \"飞书集成\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"dingtalk_integration\",\n \"title\": \"钉钉集成\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"wecom_integration\",\n \"title\": \"企业微信集成\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"watermark\",\n \"title\": \"全局水印\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"security_disabled_export_data\",\n \"title\": \"禁止导出数据\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": true,\n \"enterprise\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"security_disabled_share\",\n \"title\": \"禁止创建公开链接\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"security_disabled_copy\",\n \"title\": \"禁止复制数据\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"security_disabled_download_file\",\n \"title\": \"禁止下载附件\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"security_disabled_invite_members\",\n \"title\": \"禁止邀请站外用户\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"security_show_mobile\",\n \"title\": \"通讯录显示手机号\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": true,\n \"enterprise\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"single_sign_on\",\n \"title\": \"*单点登录(SSO)\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true\n }\n ],\n \"支持服务\": [\n {\n \"group\": \"支持服务\",\n \"id\": \"online_server\",\n \"title\": \"在线支持\",\n \"bronze\": \"基础服务\",\n \"silver\": \"高级服务\",\n \"gold\": \"专业顾问 24 小时服务\",\n \"enterprise\": \"VIP 顾问 24 小时服务\"\n },\n {\n \"group\": \"支持服务\",\n \"id\": \"customer_support_group\",\n \"title\": \"客户支持群\",\n \"bronze\": \"用户社群\",\n \"silver\": \"高级客户群\",\n \"gold\": \"专业客户群\",\n \"enterprise\": \"VIP 客户群\"\n },\n {\n \"group\": \"支持服务\",\n \"id\": \"muti_language_email\",\n \"title\": \"中英文邮件支持\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true\n },\n {\n \"group\": \"支持服务\",\n \"id\": \"free_online_video_train\",\n \"title\": \"免费在线视频培训\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true\n },\n {\n \"group\": \"支持服务\",\n \"id\": \"exclusive_v_consultant\",\n \"title\": \"专属 V+ 顾问\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true\n },\n {\n \"group\": \"支持服务\",\n \"id\": \"customized_features\",\n \"title\": \"定制功能开发(费用另计)\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true\n },\n {\n \"group\": \"支持服务\",\n \"id\": \"enterprise_consulting_training\",\n \"title\": \"企业级咨询培训\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true\n }\n ]\n}", - "zh_HK": "[\n {\n \"group\": \"用量\",\n \"id\": \"datasheet_number\",\n \"title\": \"文件節點數量\",\n \"bronze\": \"30 個\",\n \"silver\": \"300 個\",\n \"gold\": \"1000 個\",\n \"enterprise\": \"1 萬個\"\n },\n {\n \"group\": \"用量\",\n \"id\": \"datasheet_rows\",\n \"title\": \"單維格表行數\",\n \"bronze\": \"5,000 行\",\n \"silver\": \"1 萬行\",\n \"gold\": \"2 萬行\",\n \"enterprise\": \"5 萬行\"\n },\n {\n \"group\": \"用量\",\n \"id\": \"space_rows\",\n \"title\": \"空間站總行數\",\n \"bronze\": \"2 萬行\",\n \"silver\": \"300 萬行\",\n \"gold\": \"2000 萬行\",\n \"enterprise\": \"5 億行\"\n },\n {\n \"group\": \"用量\",\n \"id\": \"space_capacity\",\n \"title\": \"附件容量\",\n \"bronze\": \"1GB 默認\",\n \"silver\": \"席位數 * 5GB\",\n \"gold\": \"席位數 * 7GB\",\n \"enterprise\": \"席位數 * 10GB\"\n },\n {\n \"group\": \"功能\",\n \"id\": \"star_marker\",\n \"title\": \"星標\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true\n },\n {\n \"group\": \"功能\",\n \"id\": \"album_view\",\n \"title\": \"相冊視圖\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true\n },\n {\n \"group\": \"功能\",\n \"id\": \"kanban_view\",\n \"title\": \"看板視圖\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true\n },\n {\n \"group\": \"功能\",\n \"id\": \"org_chart_view\",\n \"title\": \"架構視圖\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true\n },\n {\n \"group\": \"功能\",\n \"id\": \"gantt_view\",\n \"title\": \"甘特視圖\",\n \"bronze\": \"單空間站 10 個\",\n \"silver\": \"單空間站 50 個\",\n \"gold\": \"單空間站 200 個\",\n \"enterprise\": \"不限\"\n },\n {\n \"group\": \"功能\",\n \"id\": \"calendar_view\",\n \"title\": \"日曆視圖\",\n \"bronze\": \"單空間站 5 個\",\n \"silver\": \"單空間站 50 個\",\n \"gold\": \"單空間站 200 個\",\n \"enterprise\": \"不限\"\n },\n {\n \"group\": \"功能\",\n \"id\": \"collection_table\",\n \"title\": \"神奇表單\",\n \"bronze\": \"單空間站 20 張\",\n \"silver\": \"單空間站 100 張\",\n \"gold\": \"單空間站 300 張\",\n \"enterprise\": \"不限\"\n },\n {\n \"group\": \"功能\",\n \"id\": \"mirror\",\n \"title\": \"鏡像(行權限)\",\n \"bronze\": \"單空間站 5 個\",\n \"silver\": \"單空間站 50 個\",\n \"gold\": \"單空間站 100 個\",\n \"enterprise\": \"不限\"\n },\n {\n \"group\": \"功能\",\n \"id\": \"widget\",\n \"title\": \"小程序\",\n \"bronze\": \"不限,基礎 + 自建\",\n \"silver\": \"不限,基礎 + 自建\",\n \"gold\": \"不限,基礎 + 自建\",\n \"enterprise\": \"不限,基礎 + 自建 + 定制\"\n },\n {\n \"group\": \"功能\",\n \"id\": \"dashboard\",\n \"title\": \"儀錶盤\",\n \"bronze\": \"單空間站 5 個\",\n \"silver\": \"單空間站 50 個\",\n \"gold\": \"單空間站 100 個\",\n \"enterprise\": \"不限\"\n },\n {\n \"group\": \"功能\",\n \"id\": \"rainbow_tags\",\n \"title\": \"彩虹標籤\",\n \"bronze\": \"基礎色系(20 個)\",\n \"silver\": \"彩虹色系(50 個)\",\n \"gold\": \"彩虹色系(50 個)\",\n \"enterprise\": \"彩虹色系(50 個)\"\n },\n {\n \"group\": \"功能\",\n \"id\": \"full_platform_client_support\",\n \"title\": \"全平台客戶端支持\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true\n },\n {\n \"group\": \"功能\",\n \"id\": \"space_capacity\",\n \"title\": \"時光機(歷史記錄)\",\n \"bronze\": \"追溯 14 天\",\n \"silver\": \"追溯 3 個月\",\n \"gold\": \"追溯 6 個月\",\n \"enterprise\": \"追溯 24 個月\"\n },\n {\n \"group\": \"功能\",\n \"id\": \"recycle_bin\",\n \"title\": \"回收艙(歷史文件)\",\n \"bronze\": \"追溯 14 天\",\n \"silver\": \"追溯 3 個月\",\n \"gold\": \"追溯 6 個月\",\n \"enterprise\": \"追溯 24 個月\"\n },\n {\n \"group\": \"功能\",\n \"id\": \"api_limits\",\n \"title\": \"API 用量\",\n \"bronze\": \"1 萬次請求/月\",\n \"silver\": \"10 萬次請求/月\",\n \"gold\": \"50 萬次請求/月\",\n \"enterprise\": \"100 萬次請求/月\"\n },\n {\n \"group\": \"功能\",\n \"id\": \"robot\",\n \"title\": \"機器人\",\n \"bronze\": \"限時免費\",\n \"silver\": \"限時免費\",\n \"gold\": \"限時免費\",\n \"enterprise\": \"限時免費\"\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"column_permission\",\n \"title\": \"列權限\",\n \"bronze\": \"單空間站 10 列\",\n \"silver\": \"單空間站 50 列\",\n \"gold\": \"單空間站 200 列\",\n \"enterprise\": \"不限\"\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"folder_permission\",\n \"title\": \"文件權限\",\n \"bronze\": \"單空間站 10 個\",\n \"silver\": \"單空間站 50 個\",\n \"gold\": \"單空間站 200 個\",\n \"enterprise\": \"不限\"\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"admin_counts\",\n \"title\": \"管理員數量\",\n \"bronze\": \"3 個\",\n \"silver\": \"5 個\",\n \"gold\": \"10 個\",\n \"enterprise\": \"不限\"\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"marketplace_integration_app_name_officepreview\",\n \"title\": \"Office 文件預覽\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"feishu_integration\",\n \"title\": \"飛書集成\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"dingtalk_integration\",\n \"title\": \"釘釘集成\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"wecom_integration\",\n \"title\": \"企業微信集成\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"watermark\",\n \"title\": \"全局水印\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"security_disabled_export_data\",\n \"title\": \"禁止導出數據\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": true,\n \"enterprise\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"security_disabled_share\",\n \"title\": \"禁止創建公開鏈接\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"security_disabled_copy\",\n \"title\": \"禁止複制數據\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"security_disabled_download_file\",\n \"title\": \"禁止下載附件\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"security_disabled_invite_members\",\n \"title\": \"禁止邀請站外用戶\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"security_show_mobile\",\n \"title\": \"通訊錄顯示手機號\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": true,\n \"enterprise\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"single_sign_on\",\n \"title\": \"*單點登錄(SSO)\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true\n },\n {\n \"group\": \"支持服務\",\n \"id\": \"online_server\",\n \"title\": \"在線支持\",\n \"bronze\": \"基礎服務\",\n \"silver\": \"高級服務\",\n \"gold\": \"專業顧問 24 小時服務\",\n \"enterprise\": \"VIP 顧問 24 小時服務\"\n },\n {\n \"group\": \"支持服務\",\n \"id\": \"customer_support_group\",\n \"title\": \"客戶支持群\",\n \"bronze\": \"用戶社群\",\n \"silver\": \"高級客戶群\",\n \"gold\": \"專業客戶群\",\n \"enterprise\": \"VIP 客戶群\"\n },\n {\n \"group\": \"支持服務\",\n \"id\": \"muti_language_email\",\n \"title\": \"中英文郵件支持\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true\n },\n {\n \"group\": \"支持服務\",\n \"id\": \"free_online_video_train\",\n \"title\": \"免費在線視頻培訓\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true\n },\n {\n \"group\": \"支持服務\",\n \"id\": \"exclusive_v_consultant\",\n \"title\": \"專屬 V+ 顧問\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true\n },\n {\n \"group\": \"支持服務\",\n \"id\": \"customized_features\",\n \"title\": \"定制功能開發(費用另計)\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true\n },\n {\n \"group\": \"支持服務\",\n \"id\": \"enterprise_consulting_training\",\n \"title\": \"企業級諮詢培訓\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true\n }\n]" + "zh_CN": "{\n \"用量\": [\n {\n \"group\": \"用量\",\n \"id\": \"member_num\",\n \"title\": \"成员数量\",\n \"bronze\": \"1~10 人\",\n \"silver\": \"1~100 人\",\n \"gold\": \"1~200 人\",\n \"enterprise\": \"定制\",\n \"community\":\"不限\",\n \"custom\":\"不限\"\n },\n {\n \"group\": \"用量\",\n \"id\": \"datasheet_number\",\n \"title\": \"文件节点数量\",\n \"bronze\": \"30 个\",\n \"silver\": \"300 个\",\n \"gold\": \"1000 个\",\n \"enterprise\": \"1 万个\",\n \"community\":\"不限\",\n \"custom\":\"不限\"\n },\n {\n \"group\": \"用量\",\n \"id\": \"datasheet_rows\",\n \"title\": \"单维格表行数\",\n \"bronze\": \"5,000 行\",\n \"silver\": \"1 万行\",\n \"gold\": \"2 万行\",\n \"enterprise\": \"5 万行\",\n \"community\":\"不限\",\n \"custom\":\"不限\"\n },\n {\n \"group\": \"用量\",\n \"id\": \"space_rows\",\n \"title\": \"空间站总行数\",\n \"bronze\": \"2 万行\",\n \"silver\": \"300 万行\",\n \"gold\": \"2000 万行\",\n \"enterprise\": \"5 亿行\",\n \"community\":\"不限\",\n \"custom\":\"不限\"\n },\n {\n \"group\": \"用量\",\n \"id\": \"space_capacity\",\n \"title\": \"附件容量\",\n \"bronze\": \"1GB 默认\",\n \"silver\": \"席位数 * 5GB\",\n \"gold\": \"席位数 * 7GB\",\n \"enterprise\": \"席位数 * 10GB\",\n \"community\":\"不限\",\n \"custom\":\"不限\"\n }\n ],\n \"功能\": [\n {\n \"group\": \"功能\",\n \"id\": \"star_marker\",\n \"title\": \"星标\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true,\n \"community\":true,\n \"custom\":true\n },\n {\n \"group\": \"功能\",\n \"id\": \"album_view\",\n \"title\": \"相册视图\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true,\n \"community\":true,\n \"custom\":true\n },\n {\n \"group\": \"功能\",\n \"id\": \"kanban_view\",\n \"title\": \"看板视图\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true,\n \"community\":true,\n \"custom\":true\n },\n {\n \"group\": \"功能\",\n \"id\": \"org_chart_view\",\n \"title\": \"架构视图\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true,\n \"community\":true,\n \"custom\":true\n },\n {\n \"group\": \"功能\",\n \"id\": \"gantt_view\",\n \"title\": \"甘特视图\",\n \"bronze\": \"单空间站 10 个\",\n \"silver\": \"单空间站 50 个\",\n \"gold\": \"单空间站 200 个\",\n \"enterprise\": \"不限\",\n \"community\":true,\n \"custom\":true\n },\n {\n \"group\": \"功能\",\n \"id\": \"calendar_view\",\n \"title\": \"日历视图\",\n \"bronze\": \"单空间站 5 个\",\n \"silver\": \"单空间站 50 个\",\n \"gold\": \"单空间站 200 个\",\n \"enterprise\": \"不限\",\n \"community\":true,\n \"custom\":true\n },\n {\n \"group\": \"功能\",\n \"id\": \"collection_table\",\n \"title\": \"神奇表单\",\n \"bronze\": \"单空间站 20 张\",\n \"silver\": \"单空间站 100 张\",\n \"gold\": \"单空间站 300 张\",\n \"enterprise\": \"不限\",\n \"community\":true,\n \"custom\":true\n },\n {\n \"group\": \"功能\",\n \"id\": \"mirror\",\n \"title\": \"镜像(行权限)\",\n \"bronze\": \"单空间站 5 个\",\n \"silver\": \"单空间站 50 个\",\n \"gold\": \"单空间站 100 个\",\n \"enterprise\": \"不限\",\n \"community\":true,\n \"custom\":true\n },\n {\n \"group\": \"功能\",\n \"id\": \"widget\",\n \"title\": \"小程序\",\n \"bronze\": \"不限,基础 + 自建\",\n \"silver\": \"不限,基础 + 自建\",\n \"gold\": \"不限,基础 + 自建\",\n \"enterprise\": \"不限,基础 + 自建 + 定制\",\n \"community\":\"不限,基础 + 自建\",\n \"custom\":\"不限,基础 + 自建\"\n },\n {\n \"group\": \"功能\",\n \"id\": \"dashboard\",\n \"title\": \"仪表盘\",\n \"bronze\": \"单空间站 5 个\",\n \"silver\": \"单空间站 50 个\",\n \"gold\": \"单空间站 100 个\",\n \"enterprise\": \"不限\",\n \"community\": true,\n \"custom\":\"true\"\n },\n {\n \"group\": \"功能\",\n \"id\": \"custom_embedding\",\n \"title\": \"自定义嵌入\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": true,\n \"enterprise\": true,\n \"community\":false,\n \"custom\":true\n },\n {\n \"group\": \"功能\",\n \"id\": \"rainbow_tags\",\n \"title\": \"彩虹标签\",\n \"bronze\": \"基础色系(20 个)\",\n \"silver\": \"彩虹色系(50 个)\",\n \"gold\": \"彩虹色系(50 个)\",\n \"enterprise\": \"彩虹色系(50 个)\",\n \"community\":\"基础色系(20 个)\",\n \"custom\":\"彩虹色系(50 个)\"\n },\n {\n \"group\": \"功能\",\n \"id\": \"full_platform_client_support\",\n \"title\": \"全平台客户端支持\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true,\n \"community\": false,\n \"custom\":true\n },\n {\n \"group\": \"功能\",\n \"id\": \"space_capacity\",\n \"title\": \"时光机(历史记录)\",\n \"bronze\": \"追溯 14 天\",\n \"silver\": \"追溯 3 个月\",\n \"gold\": \"追溯 6 个月\",\n \"enterprise\": \"追溯 24 个月\",\n \"community\": true,\n \"custom\": true\n },\n {\n \"group\": \"功能\",\n \"id\": \"recycle_bin\",\n \"title\": \"回收舱(历史文件)\",\n \"bronze\": \"追溯 14 天\",\n \"silver\": \"追溯 3 个月\",\n \"gold\": \"追溯 6 个月\",\n \"enterprise\": \"追溯 24 个月\",\n \"community\": true,\n \"custom\": true\n },\n {\n \"group\": \"功能\",\n \"id\": \"api_limits\",\n \"title\": \"API 用量\",\n \"bronze\": \"1 万次请求/月\",\n \"silver\": \"10 万次请求/月\",\n \"gold\": \"50 万次请求/月\",\n \"enterprise\": \"100 万次请求/月\",\n \"community\": true,\n \"custom\": true\n },\n {\n \"group\": \"功能\",\n \"id\": \"robot\",\n \"title\": \"机器人\",\n \"bronze\": \"限时免费\",\n \"silver\": \"限时免费\",\n \"gold\": \"限时免费\",\n \"enterprise\": \"限时免费\",\n \"community\": true,\n \"custom\": true\n }\n ],\n \"管理和安全\": [\n {\n \"group\": \"管理和安全\",\n \"id\": \"column_permission\",\n \"title\": \"列权限\",\n \"bronze\": \"单空间站 10 列\",\n \"silver\": \"单空间站 50 列\",\n \"gold\": \"单空间站 200 列\",\n \"enterprise\": \"不限\",\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"folder_permission\",\n \"title\": \"文件权限\",\n \"bronze\": \"单空间站 10 个\",\n \"silver\": \"单空间站 50 个\",\n \"gold\": \"单空间站 200 个\",\n \"enterprise\": \"不限\",\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"admin_counts\",\n \"title\": \"管理员数量\",\n \"bronze\": \"3 个\",\n \"silver\": \"5 个\",\n \"gold\": \"10 个\",\n \"enterprise\": \"不限\",\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"marketplace_integration_app_name_officepreview\",\n \"title\": \"Office 文件预览\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"feishu_integration\",\n \"title\": \"飞书集成\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"dingtalk_integration\",\n \"title\": \"钉钉集成\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"wecom_integration\",\n \"title\": \"企业微信集成\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"watermark\",\n \"title\": \"全局水印\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"security_disabled_export_data\",\n \"title\": \"禁止导出数据\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": true,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"security_disabled_share\",\n \"title\": \"禁止创建公开链接\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"security_disabled_copy\",\n \"title\": \"禁止复制数据\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"security_disabled_download_file\",\n \"title\": \"禁止下载附件\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"security_disabled_invite_members\",\n \"title\": \"禁止邀请站外用户\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"security_show_mobile\",\n \"title\": \"通讯录显示手机号\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": true,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"single_sign_on\",\n \"title\": \"*单点登录(SSO)\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n }\n ],\n \"支持服务\": [\n {\n \"group\": \"支持服务\",\n \"id\": \"online_server\",\n \"title\": \"在线支持\",\n \"bronze\": \"基础服务\",\n \"silver\": \"高级服务\",\n \"gold\": \"专业顾问 24 小时服务\",\n \"enterprise\": \"VIP 顾问 24 小时服务\",\n \"community\": \"基础服务\",\n \"custom\": \"VIP 顾问 24 小时服务\"\n },\n {\n \"group\": \"支持服务\",\n \"id\": \"customer_support_group\",\n \"title\": \"客户支持群\",\n \"bronze\": \"用户社群\",\n \"silver\": \"高级客户群\",\n \"gold\": \"专业客户群\",\n \"enterprise\": \"VIP 客户群\",\n \"community\": \"用户社群\",\n \"custom\": \"VIP 客户群\"\n },\n {\n \"group\": \"支持服务\",\n \"id\": \"muti_language_email\",\n \"title\": \"中英文邮件支持\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true,\n \"community\": true,\n \"custom\": true\n },\n {\n \"group\": \"支持服务\",\n \"id\": \"free_online_video_train\",\n \"title\": \"免费在线视频培训\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true,\n \"community\": true,\n \"custom\": true\n },\n {\n \"group\": \"支持服务\",\n \"id\": \"exclusive_v_consultant\",\n \"title\": \"专属 V+ 顾问\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"支持服务\",\n \"id\": \"customized_features\",\n \"title\": \"定制功能开发(费用另计)\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"支持服务\",\n \"id\": \"enterprise_consulting_training\",\n \"title\": \"企业级咨询培训\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n }\n ]\n}", + "en_US": "{\n \"Usages\": [\n {\n \"group\": \"Usages\",\n \"id\": \"member_num\",\n \"title\": \"Seats\",\n \"bronze\": \"Incl. 10 seats\",\n \"silver\": \"Incl. 10 seats\",\n \"gold\": \"Incl. 20 seats\",\n \"enterprise\": \"Custom\",\n \"community\":\"unlimited\",\n \"custom\":\"unlimited\"\n },\n {\n \"group\": \"Usages\",\n \"id\": \"datasheet_number\",\n \"title\": \"file nodes \",\n \"bronze\": \"30\",\n \"silver\": \"300\",\n \"gold\": \"1,000\",\n \"enterprise\": \"10,000\",\n \"community\":\"unlimited\",\n \"custom\":\"unlimited\"\n },\n {\n \"group\": \"Usages\",\n \"id\": \"datasheet_rows\",\n \"title\": \"Records per datasheet\",\n \"bronze\": \"5,000 \",\n \"silver\": \"10,000\",\n \"gold\": \"20,000\",\n \"enterprise\": \"50,000\",\n \"community\":\"50,000\",\n \"custom\":\"50,000\"\n },\n {\n \"group\": \"Usages\",\n \"id\": \"space_rows\",\n \"title\": \"Records per space\",\n \"bronze\": \"20,000\",\n \"silver\": \"3,000,000\",\n \"gold\": \"20,000,000\",\n \"enterprise\": \"500,000,000\",\n \"community\":\"unlimited\",\n \"custom\":\"unlimited\"\n },\n {\n \"group\": \"Usages\",\n \"id\": \"space_capacity\",\n \"title\": \"Attachments storage per space\",\n \"bronze\": \"1GB \",\n \"silver\": \"seats * 5GB\",\n \"gold\": \"seats * 7GB\",\n \"enterprise\": \"seats * 10GB\",\n \"community\":\"unlimited\",\n \"custom\":\"unlimited\"\n }\n ],\n \"Functions\": [\n {\n \"group\": \"Functions\",\n \"id\": \"star_marker\",\n \"title\": \"Pin\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true,\n \"community\":true,\n \"custom\":true\n },\n {\n \"group\": \"Functions\",\n \"id\": \"album_view\",\n \"title\": \"Gallery view\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true,\n \"community\":true,\n \"custom\":true\n },\n {\n \"group\": \"Functions\",\n \"id\": \"kanban_view\",\n \"title\": \"Kanban view\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true,\n \"community\":true,\n \"custom\":true\n },\n {\n \"group\": \"Functions\",\n \"id\": \"org_chart_view\",\n \"title\": \"Architecture view\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true,\n \"community\":true,\n \"custom\":true\n },\n {\n \"group\": \"Functions\",\n \"id\": \"gantt_view\",\n \"title\": \"Gantt view\",\n \"bronze\": \"10 per space\",\n \"silver\": \"50 per space\",\n \"gold\": \"200 per space\",\n \"enterprise\": \"unlimited\",\n \"community\":true,\n \"custom\":true\n },\n {\n \"group\": \"Functions\",\n \"id\": \"calendar_view\",\n \"title\": \"Calendar view\",\n \"bronze\": \"5 per space\",\n \"silver\": \"50 per space\",\n \"gold\": \"200 per space\",\n \"enterprise\": \"unlimited\",\n \"community\":true,\n \"custom\":true\n },\n {\n \"group\": \"Functions\",\n \"id\": \"collection_table\",\n \"title\": \"Form\",\n \"bronze\": \"20 per space\",\n \"silver\": \"50 per space\",\n \"gold\": \"300 per space\",\n \"enterprise\": \"unlimited\",\n \"community\":true,\n \"custom\":true\n },\n {\n \"group\": \"Functions\",\n \"id\": \"mirror\",\n \"title\": \"Mirror(records permission)\",\n \"bronze\": \"5 per space\",\n \"silver\": \"10 per space\",\n \"gold\": \"100 per space\",\n \"enterprise\": \"unlimited\",\n \"community\":true,\n \"custom\":true\n },\n {\n \"group\": \"Functions\",\n \"id\": \"widget\",\n \"title\": \"Widget\",\n \"bronze\": \"free for a limited time\",\n \"silver\": \"free for a limited time\",\n \"gold\": \"free for a limited time\",\n \"enterprise\": \"free for a limited time\",\n \"community\":\"free for a limited time\",\n \"custom\":\"free for a limited time\"\n },\n {\n \"group\": \"Functions\",\n \"id\": \"dashboard\",\n \"title\": \"Dashboard\",\n \"bronze\": \"5 per space\",\n \"silver\": \"50 per space\",\n \"gold\": \"100 per space\",\n \"enterprise\": \"unlimited\",\n \"community\":true,\n \"custom\":true\n },\n {\n \"group\": \"Functions\",\n \"id\": \"custom_embedding\",\n \"title\": \"Custom embedding\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": true,\n \"enterprise\": true,\n \"community\":false,\n \"custom\":true\n },\n {\n \"group\": \"Functions\",\n \"id\": \"rainbow_tags\",\n \"title\": \"Rainbow tags\",\n \"bronze\": \"basic color\",\n \"silver\": \"advanced color\",\n \"gold\": \"advanced color\",\n \"enterprise\": \"advanced color\",\n \"community\":\"basic color\",\n \"custom\":\"advanced color\"\n },\n {\n \"group\": \"Functions\",\n \"id\": \"space_capacity\",\n \"title\": \"Record activity\",\n \"bronze\": \"14 days\",\n \"silver\": \"3 months\",\n \"gold\": \"6 months\",\n \"enterprise\": \"24 months\",\n \"community\": true,\n \"custom\": true\n },\n {\n \"group\": \"Functions\",\n \"id\": \"recycle_bin\",\n \"title\": \"Trash(Historical node files)\",\n \"bronze\": \"14 days\",\n \"silver\": \"3 months\",\n \"gold\": \"6 months\",\n \"enterprise\": \"24 months\",\n \"community\": true,\n \"custom\": true\n },\n {\n \"group\": \"Functions\",\n \"id\": \"api_limits\",\n \"title\": \"API\",\n \"bronze\": \"free for a limited time\",\n \"silver\": \"free for a limited time\",\n \"gold\": \"free for a limited time\",\n \"enterprise\": \"free for a limited time\",\n \"community\": true,\n \"custom\": true\n },\n {\n \"group\": \"Functions\",\n \"id\": \"robot\",\n \"title\": \"Robot\",\n \"bronze\": \"free for a limited time\",\n \"silver\": \"free for a limited time\",\n \"gold\": \"free for a limited time\",\n \"enterprise\": \"free for a limited time\",\n \"community\": true,\n \"custom\": true\n }\n ],\n \"Management and security\": [\n {\n \"group\": \"Management and security\",\n \"id\": \"column_permission\",\n \"title\": \"Field permission\",\n \"bronze\": \"10 per space\",\n \"silver\": \"50 per space\",\n \"gold\": \"200 per space\",\n \"enterprise\": \"unlimited\",\n \"community\":false,\n \"custom\":true\n },\n {\n \"group\": \"Management and security\",\n \"id\": \"folder_permission\",\n \"title\": \"File permission\",\n \"bronze\": \"10 per space\",\n \"silver\": \"50 per space\",\n \"gold\": \"200 per space\",\n \"enterprise\": \"unlimited\",\n \"community\":false,\n \"custom\":true\n },\n {\n \"group\": \"Management and security\",\n \"id\": \"admin_counts\",\n \"title\": \"Space admin\",\n \"bronze\": \"3\",\n \"silver\": \"5\",\n \"gold\": \"10\",\n \"enterprise\": \"unlimited\",\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"Management and security\",\n \"id\": \"watermark\",\n \"title\": \"Watermark\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"Management and security\",\n \"id\": \"security_disabled_export_data\",\n \"title\": \"Prohibit data export\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": true,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"Management and security\",\n \"id\": \"security_disabled_share\",\n \"title\": \"Prohibit opening public links\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"Management and security\",\n \"id\": \"security_disabled_copy\",\n \"title\": \"Prohibit copying datas\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"Management and security\",\n \"id\": \"security_disabled_download_file\",\n \"title\": \"Prohibit downloadimg files\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"Management and security\",\n \"id\": \"security_disabled_invite_members\",\n \"title\": \"Prohibit inviting off-space user\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"Management and security\",\n \"id\": \"single_sign_on\",\n \"title\": \"Single sign-on(SSO)\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n }\n ],\n \"Support service\": [\n {\n \"group\": \"Support service\",\n \"id\": \"online_server\",\n \"title\": \"Online server\",\n \"bronze\": \"community service\",\n \"silver\": \"community service\",\n \"gold\": \"community service\",\n \"enterprise\": \"consultancy service\",\n \"community\": \"community service\",\n \"custom\": \"consultancy service\"\n },\n {\n \"group\": \"Support service\",\n \"id\": \"customized_features\",\n \"title\": \"Custom function development (additional cost)\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"Support service\",\n \"id\": \"enterprise_consulting_training\",\n \"title\": \"Enterprise-level consulting training\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n }\n ]\n}", + "zh_HK": "{\n \"用量\": [\n {\n \"group\": \"用量\",\n \"id\": \"datasheet_number\",\n \"title\": \"文件节点数量\",\n \"bronze\": \"30 个\",\n \"silver\": \"300 个\",\n \"gold\": \"1000 个\",\n \"enterprise\": \"1 万个\",\n \"community\":\"不限\",\n \"custom\":\"不限\"\n },\n {\n \"group\": \"用量\",\n \"id\": \"datasheet_rows\",\n \"title\": \"单维格表行数\",\n \"bronze\": \"5,000 行\",\n \"silver\": \"1 万行\",\n \"gold\": \"2 万行\",\n \"enterprise\": \"5 万行\",\n \"community\":\"不限\",\n \"custom\":\"不限\"\n },\n {\n \"group\": \"用量\",\n \"id\": \"space_rows\",\n \"title\": \"空间站总行数\",\n \"bronze\": \"2 万行\",\n \"silver\": \"300 万行\",\n \"gold\": \"2000 万行\",\n \"enterprise\": \"5 亿行\",\n \"community\":\"不限\",\n \"custom\":\"不限\"\n },\n {\n \"group\": \"用量\",\n \"id\": \"space_capacity\",\n \"title\": \"附件容量\",\n \"bronze\": \"1GB 默认\",\n \"silver\": \"席位数 * 5GB\",\n \"gold\": \"席位数 * 7GB\",\n \"enterprise\": \"席位数 * 10GB\",\n \"community\":\"不限\",\n \"custom\":\"不限\"\n }\n ],\n \"功能\": [\n {\n \"group\": \"功能\",\n \"id\": \"star_marker\",\n \"title\": \"星标\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true,\n \"community\":true,\n \"custom\":true\n },\n {\n \"group\": \"功能\",\n \"id\": \"album_view\",\n \"title\": \"相册视图\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true,\n \"community\":true,\n \"custom\":true\n },\n {\n \"group\": \"功能\",\n \"id\": \"kanban_view\",\n \"title\": \"看板视图\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true,\n \"community\":true,\n \"custom\":true\n },\n {\n \"group\": \"功能\",\n \"id\": \"org_chart_view\",\n \"title\": \"架构视图\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true,\n \"community\":true,\n \"custom\":true\n },\n {\n \"group\": \"功能\",\n \"id\": \"gantt_view\",\n \"title\": \"甘特视图\",\n \"bronze\": \"单空间站 10 个\",\n \"silver\": \"单空间站 50 个\",\n \"gold\": \"单空间站 200 个\",\n \"enterprise\": \"不限\",\n \"community\":true,\n \"custom\":true\n },\n {\n \"group\": \"功能\",\n \"id\": \"calendar_view\",\n \"title\": \"日历视图\",\n \"bronze\": \"单空间站 5 个\",\n \"silver\": \"单空间站 50 个\",\n \"gold\": \"单空间站 200 个\",\n \"enterprise\": \"不限\",\n \"community\":true,\n \"custom\":true\n },\n {\n \"group\": \"功能\",\n \"id\": \"collection_table\",\n \"title\": \"神奇表单\",\n \"bronze\": \"单空间站 20 张\",\n \"silver\": \"单空间站 100 张\",\n \"gold\": \"单空间站 300 张\",\n \"enterprise\": \"不限\",\n \"community\":true,\n \"custom\":true\n },\n {\n \"group\": \"功能\",\n \"id\": \"mirror\",\n \"title\": \"镜像(行权限)\",\n \"bronze\": \"单空间站 5 个\",\n \"silver\": \"单空间站 50 个\",\n \"gold\": \"单空间站 100 个\",\n \"enterprise\": \"不限\",\n \"community\":true,\n \"custom\":true\n },\n {\n \"group\": \"功能\",\n \"id\": \"widget\",\n \"title\": \"小程序\",\n \"bronze\": \"不限,基础 + 自建\",\n \"silver\": \"不限,基础 + 自建\",\n \"gold\": \"不限,基础 + 自建\",\n \"enterprise\": \"不限,基础 + 自建 + 定制\",\n \"community\":\"不限,基础 + 自建\",\n \"custom\":\"不限,基础 + 自建\"\n },\n {\n \"group\": \"功能\",\n \"id\": \"dashboard\",\n \"title\": \"仪表盘\",\n \"bronze\": \"单空间站 5 个\",\n \"silver\": \"单空间站 50 个\",\n \"gold\": \"单空间站 100 个\",\n \"enterprise\": \"不限\",\n \"community\": true,\n \"custom\":\"不限\"\n },\n {\n \"group\": \"功能\",\n \"id\": \"rainbow_tags\",\n \"title\": \"彩虹标签\",\n \"bronze\": \"基础色系(20 个)\",\n \"silver\": \"彩虹色系(50 个)\",\n \"gold\": \"彩虹色系(50 个)\",\n \"enterprise\": \"彩虹色系(50 个)\",\n \"community\":\"基础色系(20 个)\",\n \"custom\":\"彩虹色系(50 个)\"\n },\n {\n \"group\": \"功能\",\n \"id\": \"full_platform_client_support\",\n \"title\": \"全平台客户端支持\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true,\n \"community\": false,\n \"custom\":true\n },\n {\n \"group\": \"功能\",\n \"id\": \"space_capacity\",\n \"title\": \"时光机(历史记录)\",\n \"bronze\": \"追溯 14 天\",\n \"silver\": \"追溯 3 个月\",\n \"gold\": \"追溯 6 个月\",\n \"enterprise\": \"追溯 24 个月\",\n \"community\": true,\n \"custom\": true\n },\n {\n \"group\": \"功能\",\n \"id\": \"recycle_bin\",\n \"title\": \"回收舱(历史文件)\",\n \"bronze\": \"追溯 14 天\",\n \"silver\": \"追溯 3 个月\",\n \"gold\": \"追溯 6 个月\",\n \"enterprise\": \"追溯 24 个月\",\n \"community\": true,\n \"custom\": true\n },\n {\n \"group\": \"功能\",\n \"id\": \"api_limits\",\n \"title\": \"API 用量\",\n \"bronze\": \"1 万次请求/月\",\n \"silver\": \"10 万次请求/月\",\n \"gold\": \"50 万次请求/月\",\n \"enterprise\": \"100 万次请求/月\",\n \"community\": true,\n \"custom\": true\n },\n {\n \"group\": \"功能\",\n \"id\": \"robot\",\n \"title\": \"机器人\",\n \"bronze\": \"限时免费\",\n \"silver\": \"限时免费\",\n \"gold\": \"限时免费\",\n \"enterprise\": \"限时免费\",\n \"community\": true,\n \"custom\": true\n }\n ],\n \"管理和安全\": [\n {\n \"group\": \"管理和安全\",\n \"id\": \"column_permission\",\n \"title\": \"列权限\",\n \"bronze\": \"单空间站 10 列\",\n \"silver\": \"单空间站 50 列\",\n \"gold\": \"单空间站 200 列\",\n \"enterprise\": \"不限\",\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"folder_permission\",\n \"title\": \"文件权限\",\n \"bronze\": \"单空间站 10 个\",\n \"silver\": \"单空间站 50 个\",\n \"gold\": \"单空间站 200 个\",\n \"enterprise\": \"不限\",\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"admin_counts\",\n \"title\": \"管理员数量\",\n \"bronze\": \"3 个\",\n \"silver\": \"5 个\",\n \"gold\": \"10 个\",\n \"enterprise\": \"不限\",\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"marketplace_integration_app_name_officepreview\",\n \"title\": \"Office 文件预览\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"feishu_integration\",\n \"title\": \"飞书集成\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"dingtalk_integration\",\n \"title\": \"钉钉集成\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"wecom_integration\",\n \"title\": \"企业微信集成\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"watermark\",\n \"title\": \"全局水印\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"security_disabled_export_data\",\n \"title\": \"禁止导出数据\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": true,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"security_disabled_share\",\n \"title\": \"禁止创建公开链接\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"security_disabled_copy\",\n \"title\": \"禁止复制数据\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"security_disabled_download_file\",\n \"title\": \"禁止下载附件\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"security_disabled_invite_members\",\n \"title\": \"禁止邀请站外用户\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"security_show_mobile\",\n \"title\": \"通讯录显示手机号\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": true,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"管理和安全\",\n \"id\": \"single_sign_on\",\n \"title\": \"*单点登录(SSO)\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n }\n ],\n \"支持服务\": [\n {\n \"group\": \"支持服务\",\n \"id\": \"online_server\",\n \"title\": \"在线支持\",\n \"bronze\": \"基础服务\",\n \"silver\": \"高级服务\",\n \"gold\": \"专业顾问 24 小时服务\",\n \"enterprise\": \"VIP 顾问 24 小时服务\",\n \"community\": \"基础服务\",\n \"custom\": \"VIP 顾问 24 小时服务\"\n },\n {\n \"group\": \"支持服务\",\n \"id\": \"customer_support_group\",\n \"title\": \"客户支持群\",\n \"bronze\": \"用户社群\",\n \"silver\": \"高级客户群\",\n \"gold\": \"专业客户群\",\n \"enterprise\": \"VIP 客户群\",\n \"community\": \"用户社群\",\n \"custom\": \"VIP 客户群\"\n },\n {\n \"group\": \"支持服务\",\n \"id\": \"muti_language_email\",\n \"title\": \"中英文邮件支持\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true,\n \"community\": true,\n \"custom\": true\n },\n {\n \"group\": \"支持服务\",\n \"id\": \"free_online_video_train\",\n \"title\": \"免费在线视频培训\",\n \"bronze\": true,\n \"silver\": true,\n \"gold\": true,\n \"enterprise\": true,\n \"community\": true,\n \"custom\": true\n },\n {\n \"group\": \"支持服务\",\n \"id\": \"exclusive_v_consultant\",\n \"title\": \"专属 V+ 顾问\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"支持服务\",\n \"id\": \"customized_features\",\n \"title\": \"定制功能开发(费用另计)\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n },\n {\n \"group\": \"支持服务\",\n \"id\": \"enterprise_consulting_training\",\n \"title\": \"企业级咨询培训\",\n \"bronze\": false,\n \"silver\": false,\n \"gold\": false,\n \"enterprise\": true,\n \"community\": false,\n \"custom\": true\n }\n ]\n}" }, - "subscription_grades_checklist_mobile": { + "subscription_grades_checklist_mobile_saas": { "zh_CN": "[{\n\t\t\"grade\": \"bronze\",\n\t\t\"btnText\": \"立即体验\",\n\t\t\"btnHref\": \"/login\",\n\t\t\"primaryContent\": [\n\t\t\t\"支持创建 30 个文件节点\",\n\t\t\t\"单表可创建 5,000 行记录\",\n\t\t\t\"支持相册、看板、架构、甘特、日历视图与神奇表单\"\n\t\t],\n\t\t\"details\": [{\n\t\t\t\t\"name\": \"用量\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"支持创建 30 个文件节点\",\n\t\t\t\t\t\"单表可创建 5,000 行记录\",\n\t\t\t\t\t\"空间站可创建 2 万行记录\",\n\t\t\t\t\t\"附件容量 1GB(默认)\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\": \"功能\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"支持星标\",\n\t\t\t\t\t\"支持相册视图、看板视图、架构视图\",\n\t\t\t\t\t\"单空间站可创建 10 个甘特视图\",\n\t\t\t\t\t\"单空间站可创建 5 个日历视图\",\n\t\t\t\t\t\"单空间站可创建 20 张神奇表单\",\n\t\t\t\t\t\"单空间站可创建 5 个镜像文件(行权限)\",\n\t\t\t\t\t\"支持基础和自建小程序,不限量\",\n\t\t\t\t\t\"单空间站可创建 5 个仪表盘\",\n\t\t\t\t\t\"支持基础色系的彩虹标签(20 个)\",\n\t\t\t\t\t\"支持全平台客户端\",\n\t\t\t\t\t\"时光机(历史记录)可追溯 14 天\",\n\t\t\t\t\t\"回收舱(历史文件)可追溯 14 天\",\n\t\t\t\t\t\"每月支持 1 万次 API 请求\",\n\t\t\t\t\t\"支持创建和使用机器人(限时免费)\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\": \"管理和安全\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"单空间站支持配置 10 个列权限\",\n\t\t\t\t\t\"单空间站支持配置 10 个文件权限\",\n\t\t\t\t\t\"支持配置 3 个管理员\",\n\t\t\t\t\t\"支持 Office 文件预览\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\": \"支持服务\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"基础在线支持\",\n\t\t\t\t\t\"用户社群支持\",\n\t\t\t\t\t\"中英文邮件支持\",\n\t\t\t\t\t\"免费在线视频培训\"\n\t\t\t\t]\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\t\"grade\": \"silver\",\n\t\t\"btnText\": \"立即体验\",\n\t\t\"btnHref\": \"/login\",\n\t\t\"primaryContent\": [\n\t\t\t\"支持创建 300 个文件节点\",\n\t\t\t\"单表可创建 1 万行记录\",\n\t\t\t\"附件容量 5 GB/席位,全空间站共享\",\n\t\t\t\"支持相册、看板、架构、甘特、日历视图与神奇表单\"\n\t\t],\n\t\t\"details\": [{\n\t\t\t\t\"name\": \"用量\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"支持创建 300 个文件节点\",\n\t\t\t\t\t\"单表可创建 1 万行记录\",\n\t\t\t\t\t\"空间站可创建 300 万行记录\",\n\t\t\t\t\t\"附件容量 5 GB/席位,全空间站共享\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\": \"功能\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"支持星标\",\n\t\t\t\t\t\"支持相册视图、看板视图、架构视图\",\n\t\t\t\t\t\"单空间站可创建 50 个甘特视图\",\n\t\t\t\t\t\"单空间站可创建 50 个日历视图\",\n\t\t\t\t\t\"单空间站可创建 100 张神奇表单\",\n\t\t\t\t\t\"单空间站可创建 50 个镜像文件(行权限)\",\n\t\t\t\t\t\"支持基础和自建小程序,不限量\",\n\t\t\t\t\t\"单空间站可创建 50 个仪表盘\",\n\t\t\t\t\t\"支持彩虹色系的标签(50 个)\",\n\t\t\t\t\t\"支持全平台客户端\",\n\t\t\t\t\t\"时光机(历史记录)可追溯 3 个月\",\n\t\t\t\t\t\"回收舱(历史文件)可追溯 3 个月\",\n\t\t\t\t\t\"每月支持 10 万次 API 请求\",\n\t\t\t\t\t\"支持创建和使用机器人(限时免费)\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\": \"管理和安全\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"单空间站支持配置 50 个列权限\",\n\t\t\t\t\t\"单空间站支持配置 50 个文件权限\",\n\t\t\t\t\t\"支持配置 5 个管理员\",\n\t\t\t\t\t\"支持 Office 文件预览\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\": \"支持服务\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"高级在线支持\",\n\t\t\t\t\t\"高级客户群支持\",\n\t\t\t\t\t\"中英文邮件支持\",\n\t\t\t\t\t\"免费在线视频培训\"\n\t\t\t\t]\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\t\"grade\": \"gold\",\n\t\t\"btnText\": \"立即体验\",\n\t\t\"btnHref\": \"/login\",\n\t\t\"primaryContent\": [\n\t\t\t\"支持创建 1000 个文件节点\",\n\t\t\t\"单表可创建 2 万行记录\",\n\t\t\t\"附件容量 7 GB/席位,全空间站共享\",\n\t\t\t\"支持相册、看板、架构、甘特、日历视图与神奇表单\"\n\t\t],\n\t\t\"details\": [{\n\t\t\t\t\"name\": \"用量\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"支持创建 1000 个文件节点\",\n\t\t\t\t\t\"单表可创建 2 万行记录\",\n\t\t\t\t\t\"空间站可创建 2000 万行记录\",\n\t\t\t\t\t\"附件容量 7 GB/席位,全空间站共享\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\": \"功能\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"支持星标\",\n\t\t\t\t\t\"支持相册视图、看板视图、架构视图\",\n\t\t\t\t\t\"单空间站可创建 200 个甘特视图\",\n\t\t\t\t\t\"单空间站可创建 200 个日历视图\",\n\t\t\t\t\t\"单空间站可创建 300 张神奇表单\",\n\t\t\t\t\t\"单空间站可创建 100 个镜像文件(行权限)\",\n\t\t\t\t\t\"支持基础和自建小程序,不限量\",\n\t\t\t\t\t\"单空间站可创建 100 个仪表盘\",\n\t\t\t\t\t\"支持彩虹色系的标签(50 个)\",\n\t\t\t\t\t\"支持全平台客户端\",\n\t\t\t\t\t\"时光机(历史记录)可追溯 6 个月\",\n\t\t\t\t\t\"回收舱(历史文件)可追溯 6 个月\",\n\t\t\t\t\t\"每月支持 50 万次 API 请求\",\n\t\t\t\t\t\"支持创建和使用机器人(限时免费)\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\": \"管理和安全\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"单空间站支持配置 200 个列权限\",\n\t\t\t\t\t\"单空间站支持配置 200 个文件权限\",\n\t\t\t\t\t\"支持配置 10 个管理员\",\n\t\t\t\t\t\"支持 Office 文件预览\",\n\t\t\t\t\t\"禁止导出数据\",\n\t\t\t\t\t\"通讯录显示手机号\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\": \"支持服务\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"专业顾问 24 小时在线支持\",\n\t\t\t\t\t\"高级客户群支持\",\n\t\t\t\t\t\"中英文邮件支持\",\n\t\t\t\t\t\"免费在线视频培训\"\n\t\t\t\t]\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\t\"grade\": \"enterprise\",\n\t\t\"btnText\": \"联系我们\",\n\t\t\"btnHref\": \"https://vika.cn/share/shrlRw3YWmqZ4BMl0B7qZ/fomUMtKMblNchCG7Ef\",\n\t\t\"primaryContent\": [\n\t\t\t\"支持创建 1 万个文件节点\",\n\t\t\t\"单表可创建 5 万行记录\",\n\t\t\t\"附件容量 10 GB/席位,全空间站共享\",\n\t\t\t\"支持全部视图、神奇表单和列权限等全部高级功能\",\n\t\t\t\"支持第三方 IM 集成、全局水印等安全管理功能\",\n\t\t\t\"专属 V+ 顾问提供全程支持服务\"\n\t\t],\n\t\t\"details\": [{\n\t\t\t\t\"name\": \"用量\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"支持创建 1 万个文件节点\",\n\t\t\t\t\t\"单表可创建 5 万行记录\",\n\t\t\t\t\t\"空间站可创建 5 亿行记录\",\n\t\t\t\t\t\"附件容量 10 GB/席位,全空间站共享\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\": \"功能\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"支持星标\",\n\t\t\t\t\t\"支持相册视图、看板视图、架构视图\",\n\t\t\t\t\t\"甘特视图不限量\",\n\t\t\t\t\t\"日历视图不限量\",\n\t\t\t\t\t\"神奇表单不限量\",\n\t\t\t\t\t\"镜像文件(行权限)不限量\",\n\t\t\t\t\t\"支持基础、自建和定制小程序,不限量\",\n\t\t\t\t\t\"仪表盘不限量\",\n\t\t\t\t\t\"支持彩虹色系的标签(50 个)\",\n\t\t\t\t\t\"支持全平台客户端\",\n\t\t\t\t\t\"时光机(历史记录)可追溯 24 个月\",\n\t\t\t\t\t\"回收舱(历史文件)可追溯 24 个月\",\n\t\t\t\t\t\"每月支持 100 万次 API 请求\",\n\t\t\t\t\t\"支持创建和使用机器人(限时免费)\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\": \"管理和安全\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"列权限不限量\",\n\t\t\t\t\t\"文件权限不限量\",\n\t\t\t\t\t\"管理员不限量\",\n\t\t\t\t\t\"支持 Office 文件预览\",\n\t\t\t\t\t\"支持飞书、钉钉、企业微信集成\",\n\t\t\t\t\t\"支持全局水印\",\n\t\t\t\t\t\"禁止导出数据\",\n\t\t\t\t\t\"禁止创建公开链接\",\n\t\t\t\t\t\"禁止复制数据\",\n\t\t\t\t\t\"禁止下载附件\",\n\t\t\t\t\t\"禁止邀请站外用户\",\n\t\t\t\t\t\"通讯录显示手机号\",\n\t\t\t\t\t\"*支持单点登录(SSO)\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\": \"支持服务\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"VIP 顾问 24 小时在线支持\",\n\t\t\t\t\t\"VIP 客户群支持\",\n\t\t\t\t\t\"中英文邮件支持\",\n\t\t\t\t\t\"免费在线视频培训\",\n\t\t\t\t\t\"专属 V+ 顾问\",\n\t\t\t\t\t\"定制功能开发(费用另计)\",\n\t\t\t\t\t\"企业级咨询培训\"\n\t\t\t\t]\n\t\t\t}\n\t\t]\n\t}\n]", + "en_US": "[{\n \"grade\": \"free\",\n \"btnText\": \"Start now\",\n \"btnHref\": \"/login\",\n \"primaryContent\": [\n \"30 file nodes\",\n \"5,000 records per datasheet\",\n \"1G attachments storage in space\"\n ],\n \"details\": [{\n \"name\": \"Usage\",\n \"rules\": [\n \"30 file nodes\",\n \"5,000 records per datasheet\",\n \"20,000 records in space\",\n \"1G attachments storage in space\"\n ]\n },\n {\n \"name\": \"Function\",\n \"rules\": [\n \"Pin file nodes\",\n \"Gallery view,Kanban view and Architecture view are unlimited\",\n \"10 gantt views in space\",\n \"5 calendar views in space\",\n \"20 forms in space\",\n \"5 mirrors in space\",\n \"5 dashbords in space\",\n \"Widget is free for a limited time\",\n \"Basic colors for rainbow tags\",\n \"Record activity goes back 14 days\",\n \"Trash goes back 14 days\",\n \"API is free for a limited time\",\n \"Robot is free for a limited time\"\n ]\n },\n {\n \"name\": \"Management and security\",\n \"rules\": [\n \"10 field permissions in space\",\n \"10 field permissions in space\",\n \"3 admins in space\"\n ]\n },\n {\n \"name\": \"Support service\",\n \"rules\": [\n \"community service\"\n ]\n }\n ]\n },\n {\n \"grade\": \"plus\",\n \"btnText\": \"Choose Plus\",\n \"btnHref\": \"/login\",\n \"primaryContent\": [\n \"300 file nodes\",\n \"10,000 records per datasheet\",\n \"5G/seats attachments storage in space\"\n ],\n \"details\": [{\n \"name\": \"Usage\",\n \"rules\": [\n \"300 file nodes\",\n \"10,000 records per datasheet\",\n \"3,0000,000 records in space\",\n \"5G/seats attachments storage in space\"\n ]\n },\n {\n \"name\": \"Function\",\n \"rules\": [\n \"Pin file nodes\",\n \"Gallery view,Kanban view and Architecture view are unlimited\",\n \"50 gantt views in space\",\n \"50 calendar views in space\",\n \"100 forms in space\",\n \"50 mirrors in space\",\n \"50 dashbords in space\",\n \"Widget is free for a limited time\",\n \"Advanced colors for rainbow tags\",\n \"Record activity goes back 3 months\",\n \"Trash goes back 3 months\",\n \"API is free for a limited time\",\n \"Robot is free for a limited time\"\n ]\n },\n {\n \"name\": \"Management and security\",\n \"rules\": [\n \"50 field permissions in space\",\n \"50 field permissions in space\",\n \"5 admins in space\"\n ]\n },\n {\n \"name\": \"Support service\",\n \"rules\": [\n \"community service\"\n ]\n }\n ]\n },\n {\n \"grade\": \"pro\",\n \"btnText\": \"Choose Pro\",\n \"btnHref\": \"/login\",\n \"primaryContent\": [\n \"1,000 file nodes\",\n \"20,000 records per datasheet\",\n \"7G/seats attachments storage in space\",\n \"Custom embedding\"\n ],\n \"details\": [{\n \"name\": \"Usage\",\n \"rules\": [\n \"1,000 file nodes\",\n \"20,000 records per datasheet\",\n \"20,000,000 records in space\", \n \"7G/seats attachments storage in space\"\n ]\n },\n {\n \"name\": \"Function\",\n \"rules\": [\n \"Pin file nodes\",\n \"Gallery view,Kanban view and Architecture view are unlimited\",\n \"200 gantt views in space\",\n \"300 calendar views in space\",\n \"300 forms in space\",\n \"100 mirrors in space\",\n \"100 dashbords in space\",\n \"Widget is free for a limited time\",\n \"Advanced colors for rainbow tags\",\n \"Custom embedding\",\n \"Record activity goes back 6 months\",\n \"Trash goes back 6 months\",\n \"API is free for a limited time\",\n \"Robot is free for a limited time\"\n ]\n },\n {\n \"name\": \"Management and security\",\n \"rules\": [\n \"200 field permissions in space\",\n \"200 field permissions in space\",\n \"10 admins in space\",\n \"Prohibit data export\"\n ]\n },\n {\n \"name\": \"Support service\",\n \"rules\": [\n \"community service\"\n ]\n }\n ]\n },\n {\n \"grade\": \"enterprise\",\n \"btnText\": \"Contacts us\",\n \"btnHref\": \"\",\n \"primaryContent\": [\n \"10,000 file nodes\",\n \"50,000 records per datasheet\",\n \"10G/seats attachments storage in space\",\n \"Unlimited permission and security settings\",\n \"Consultancy service\"\n ],\n \"details\": [{\n \"name\": \"Usage\",\n \"rules\": [\n \"10,000 file nodes\",\n \"50,000 records per datasheet\",\n \"50,0000,000 records in space\",\n \"10G/seats attachments storage in space\"\n ]\n },\n {\n \"name\": \"Function\",\n \"rules\": [\n \"Pin file nodes\",\n \"Gallery view,Kanban view and Architecture view are unlimited\",\n \"Gantt views are unlimited\",\n \"Calendar views are unlimited\",\n \"Forms are unlimited\",\n \"Mirrors are unlimited\",\n \"Dashbords are unlimited\",\n \"Widget is free for a limited time\",\n \"Advanced colors for rainbow tags\",\n \"Custom embedding\",\n \"Record activity goes back 24 months\",\n \"Trash goes back 24 months\",\n \"API is free for a limited time\",\n \"Robot is free for a limited time\"\n ]\n },\n {\n \"name\": \"Management and security\",\n \"rules\": [\n \"Permissions are unlimited\",\n \"Permissions are unlimited\",\n \"Admins are unlimited\",\n \"Watermark\",\n \"Prohibit data export\",\n \"Prohibit opening public links\",\n \"Prohibit copying datas\",\n \"Prohibit downloadimg files\",\n \"Prohibit inviting off-space user\",\n \"Single sign-on(SSO)\"\n ]\n },\n {\n \"name\": \"Support service\",\n \"rules\": [\n \"Consultancy service\",\n \"Custom function development (additional cost)\",\n \"Enterprise-level consulting training\"\n ]\n }\n ]\n }\n ]", "zh_HK": "[{\n\t\t\"grade\": \"bronze\",\n\t\t\"btnText\": \"立即體驗\",\n\t\t\"btnHref\": \"/login\",\n\t\t\"primaryContent\": [\n\t\t\t\"支持創建 30 個文件節點\",\n\t\t\t\"單表可創建 5,000 行記錄\",\n\t\t\t\"支持相冊、看板、架構、甘特、日曆視圖與神奇表單\"\n\t\t],\n\t\t\"details\": [{\n\t\t\t\t\"name\": \"用量\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"支持創建 30 個文件節點\",\n\t\t\t\t\t\"單表可創建 5,000 行記錄\",\n\t\t\t\t\t\"空間站可創建 2 萬行記錄\",\n\t\t\t\t\t\"附件容量 1GB(默認)\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\": \"功能\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"支持星標\",\n\t\t\t\t\t\"支持相冊視圖、看板視圖、架構視圖\",\n\t\t\t\t\t\"單空間站可創建 10 個甘特視圖\",\n\t\t\t\t\t\"單空間站可創建 5 個日曆視圖\",\n\t\t\t\t\t\"單空間站可創建 20 張神奇表單\",\n\t\t\t\t\t\"單空間站可創建 5 個鏡像文件(行權限)\",\n\t\t\t\t\t\"支持基礎和自建小程序,不限量\",\n\t\t\t\t\t\"單空間站可創建 5 個儀錶盤\",\n\t\t\t\t\t\"支持基礎色系的彩虹標籤(20 個)\",\n\t\t\t\t\t\"支持全平台客戶端\",\n\t\t\t\t\t\"時光機(歷史記錄)可追溯 14 天\",\n\t\t\t\t\t\"回收艙(歷史文件)可追溯 14 天\",\n\t\t\t\t\t\"每月支持 1 萬次 API 請求\",\n\t\t\t\t\t\"支持創建和使用機器人(限時免費)\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\": \"管理和安全\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"單空間站支持配置 10 個列權限\",\n\t\t\t\t\t\"單空間站支持配置 10 個文件權限\",\n\t\t\t\t\t\"支持配置 3 個管理員\",\n\t\t\t\t\t\"支持 Office 文件預覽\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\": \"支持服務\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"基礎在線支持\",\n\t\t\t\t\t\"用戶社群支持\",\n\t\t\t\t\t\"中英文郵件支持\",\n\t\t\t\t\t\"免費在線視頻培訓\"\n\t\t\t\t]\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\t\"grade\": \"silver\",\n\t\t\"btnText\": \"立即體驗\",\n\t\t\"btnHref\": \"/login\",\n\t\t\"primaryContent\": [\n\t\t\t\"支持創建 300 個文件節點\",\n\t\t\t\"單表可創建 1 萬行記錄\",\n\t\t\t\"附件容量 5 GB/席位,全空間站共享\",\n\t\t\t\"支持相冊、看板、架構、甘特、日曆視圖與神奇表單\"\n\t\t],\n\t\t\"details\": [{\n\t\t\t\t\"name\": \"用量\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"支持創建 300 個文件節點\",\n\t\t\t\t\t\"單表可創建 1 萬行記錄\",\n\t\t\t\t\t\"空間站可創建 300 萬行記錄\",\n\t\t\t\t\t\"附件容量 5 GB/席位,全空間站共享\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\": \"功能\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"支持星標\",\n\t\t\t\t\t\"支持相冊視圖、看板視圖、架構視圖\",\n\t\t\t\t\t\"單空間站可創建 50 個甘特視圖\",\n\t\t\t\t\t\"單空間站可創建 50 個日曆視圖\",\n\t\t\t\t\t\"單空間站可創建 100 張神奇表單\",\n\t\t\t\t\t\"單空間站可創建 50 個鏡像文件(行權限)\",\n\t\t\t\t\t\"支持基礎和自建小程序,不限量\",\n\t\t\t\t\t\"單空間站可創建 50 個儀錶盤\",\n\t\t\t\t\t\"支持彩虹色系的標籤(50 個)\",\n\t\t\t\t\t\"支持全平台客戶端\",\n\t\t\t\t\t\"時光機(歷史記錄)可追溯 3 個月\",\n\t\t\t\t\t\"回收艙(歷史文件)可追溯 3 個月\",\n\t\t\t\t\t\"每月支持 10 萬次 API 請求\",\n\t\t\t\t\t\"支持創建和使用機器人(限時免費)\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\": \"管理和安全\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"單空間站支持配置 50 個列權限\",\n\t\t\t\t\t\"單空間站支持配置 50 個文件權限\",\n\t\t\t\t\t\"支持配置 5 個管理員\",\n\t\t\t\t\t\"支持 Office 文件預覽\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\": \"支持服務\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"高級在線支持\",\n\t\t\t\t\t\"高級客戶群支持\",\n\t\t\t\t\t\"中英文郵件支持\",\n\t\t\t\t\t\"免費在線視頻培訓\"\n\t\t\t\t]\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\t\"grade\": \"gold\",\n\t\t\"btnText\": \"立即體驗\",\n\t\t\"btnHref\": \"/login\",\n\t\t\"primaryContent\": [\n\t\t\t\"支持創建 1000 個文件節點\",\n\t\t\t\"單表可創建 2 萬行記錄\",\n\t\t\t\"附件容量 7 GB/席位,全空間站共享\",\n\t\t\t\"支持相冊、看板、架構、甘特、日曆視圖與神奇表單\"\n\t\t],\n\t\t\"details\": [{\n\t\t\t\t\"name\": \"用量\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"支持創建 1000 個文件節點\",\n\t\t\t\t\t\"單表可創建 2 萬行記錄\",\n\t\t\t\t\t\"空間站可創建 2000 萬行記錄\",\n\t\t\t\t\t\"附件容量 7 GB/席位,全空間站共享\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\": \"功能\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"支持星標\",\n\t\t\t\t\t\"支持相冊視圖、看板視圖、架構視圖\",\n\t\t\t\t\t\"單空間站可創建 200 個甘特視圖\",\n\t\t\t\t\t\"單空間站可創建 200 個日曆視圖\",\n\t\t\t\t\t\"單空間站可創建 300 張神奇表單\",\n\t\t\t\t\t\"單空間站可創建 100 個鏡像文件(行權限)\",\n\t\t\t\t\t\"支持基礎和自建小程序,不限量\",\n\t\t\t\t\t\"單空間站可創建 100 個儀錶盤\",\n\t\t\t\t\t\"支持彩虹色系的標籤(50 個)\",\n\t\t\t\t\t\"支持全平台客戶端\",\n\t\t\t\t\t\"時光機(歷史記錄)可追溯 6 個月\",\n\t\t\t\t\t\"回收艙(歷史文件)可追溯 6 個月\",\n\t\t\t\t\t\"每月支持 50 萬次 API 請求\",\n\t\t\t\t\t\"支持創建和使用機器人(限時免費)\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\": \"管理和安全\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"單空間站支持配置 200 個列權限\",\n\t\t\t\t\t\"單空間站支持配置 200 個文件權限\",\n\t\t\t\t\t\"支持配置 10 個管理員\",\n\t\t\t\t\t\"支持 Office 文件預覽\",\n\t\t\t\t\t\"禁止導出數據\",\n\t\t\t\t\t\"通訊錄顯示手機號\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\": \"支持服務\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"專業顧問 24 小時在線支持\",\n\t\t\t\t\t\"高級客戶群支持\",\n\t\t\t\t\t\"中英文郵件支持\",\n\t\t\t\t\t\"免費在線視頻培訓\"\n\t\t\t\t]\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\t\"grade\": \"enterprise\",\n\t\t\"btnText\": \"聯繫我們\",\n\t\t\"btnHref\": \"https://vika.cn/share/shrlRw3YWmqZ4BMl0B7qZ/fomUMtKMblNchCG7Ef\",\n\t\t\"primaryContent\": [\n\t\t\t\"支持創建 1 萬個文件節點\",\n\t\t\t\"單表可創建 5 萬行記錄\",\n\t\t\t\"附件容量 10 GB/席位,全空間站共享\",\n\t\t\t\"支持全部視圖、神奇表單和列權限等全部高級功能\",\n\t\t\t\"支持第三方 IM 集成、全局水印等安全管理功能\",\n\t\t\t\"專屬 V+ 顧問提供全程支持服務\"\n\t\t],\n\t\t\"details\": [{\n\t\t\t\t\"name\": \"用量\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"支持創建 1 萬個文件節點\",\n\t\t\t\t\t\"單表可創建 5 萬行記錄\",\n\t\t\t\t\t\"空間站可創建 5 億行記錄\",\n\t\t\t\t\t\"附件容量 10 GB/席位,全空間站共享\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\": \"功能\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"支持星標\",\n\t\t\t\t\t\"支持相冊視圖、看板視圖、架構視圖\",\n\t\t\t\t\t\"甘特視圖不限量\",\n\t\t\t\t\t\"日曆視圖不限量\",\n\t\t\t\t\t\"神奇表單不限量\",\n\t\t\t\t\t\"鏡像文件(行權限)不限量\",\n\t\t\t\t\t\"支持基礎、自建和定制小程序,不限量\",\n\t\t\t\t\t\"儀錶盤不限量\",\n\t\t\t\t\t\"支持彩虹色系的標籤(50 個)\",\n\t\t\t\t\t\"支持全平台客戶端\",\n\t\t\t\t\t\"時光機(歷史記錄)可追溯 24 個月\",\n\t\t\t\t\t\"回收艙(歷史文件)可追溯 24 個月\",\n\t\t\t\t\t\"每月支持 100 萬次 API 請求\",\n\t\t\t\t\t\"支持創建和使用機器人(限時免費)\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\": \"管理和安全\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"列權限不限量\",\n\t\t\t\t\t\"文件權限不限量\",\n\t\t\t\t\t\"管理員不限量\",\n\t\t\t\t\t\"支持 Office 文件預覽\",\n\t\t\t\t\t\"支持飛書、釘釘、企業微信集成\",\n\t\t\t\t\t\"支持全局水印\",\n\t\t\t\t\t\"禁止導出數據\",\n\t\t\t\t\t\"禁止創建公開鏈接\",\n\t\t\t\t\t\"禁止複制數據\",\n\t\t\t\t\t\"禁止下載附件\",\n\t\t\t\t\t\"禁止邀請站外用戶\",\n\t\t\t\t\t\"通訊錄顯示手機號\",\n\t\t\t\t\t\"*支持單點登錄(SSO)\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\": \"支持服務\",\n\t\t\t\t\"rules\": [\n\t\t\t\t\t\"VIP 顧問 24 小時在線支持\",\n\t\t\t\t\t\"VIP 客戶群支持\",\n\t\t\t\t\t\"中英文郵件支持\",\n\t\t\t\t\t\"免費在線視頻培訓\",\n\t\t\t\t\t\"專屬 V+ 顧問\",\n\t\t\t\t\t\"定制功能開發(費用另計)\",\n\t\t\t\t\t\"企業級諮詢培訓\"\n\t\t\t\t]\n\t\t\t}\n\t\t]\n\t}\n]" }, + "subscription_grades_checklist_mobile_selfhost": { + "zh_CN": "[\n {\n \"grade\": \"community\",\n \"btnText\": \"联系客服\",\n \"primaryContent\": [\n \"文件节点数不限\",\n \"单表行记录数不限\",\n \"支持相册、看板、架构、甘特、日历视图与神奇表单\"\n ],\n \"details\": [\n {\n \"name\": \"用量\",\n \"rules\": [\n \"文件节点数不限\",\n \"单表行记录数不限\",\n \"空间站总记录数不限\",\n \"附件容量不限\"\n ]\n },\n {\n \"name\": \"功能\",\n \"rules\": [\n \"支持星标\",\n \"支持相册视图、看板视图、架构视图\",\n \"甘特视图不限量\",\n \"日历视图不限量\",\n \"神奇表单不限量\",\n \"镜像文件(行权限)不限量\",\n \"支持基础、自建和定制小程序,不限量\",\n \"仪表盘不限量\",\n \"支持彩虹色系的标签(30 个)\",\n \"支持时光机(历史记录)\",\n \"支持回收舱(历史文件)\",\n \"API 请求不限\",\n \"支持创建和使用机器人(限时免费)\"\n ]\n },\n {\n \"name\": \"管理和安全\",\n \"rules\": [\n \"无\"\n ]\n },\n {\n \"name\": \"支持服务\",\n \"rules\": [\n \"基础在线支持\",\n \"用户社群支持\",\n \"中英文邮件支持\",\n \"免费在线视频培训\"\n ]\n }\n ]\n },\n {\n \"grade\": \"custom\",\n \"btnText\": \"联系客服\",\n \"primaryContent\": [\n \"支持全部视图、神奇表单和列权限等全部高级功能\",\n \"支持第三方 IM 集成、全局水印等安全管理功能\",\n \"专属 V+ 顾问提供全程支持服务\"\n ],\n \"details\": [\n {\n \"name\": \"用量\",\n \"rules\": [\n \"文件节点数不限\",\n \"单表行记录数不限\",\n \"空间站总记录数不限\",\n \"附件容量不限\"\n ]\n },\n {\n \"name\": \"功能\",\n \"rules\": [\n \"支持星标\",\n \"支持相册视图、看板视图、架构视图\",\n \"甘特视图不限量\",\n \"日历视图不限量\",\n \"神奇表单不限量\",\n \"镜像文件(行权限)不限量\",\n \"支持基础、自建和定制小程序,不限量\",\n \"仪表盘不限量\",\n \"支持彩虹色系的标签(50 个)\",\n \"支持全平台客户端\",\n \"支持时光机(历史记录)\",\n \"支持回收舱(历史文件)\",\n \"API 请求不限\",\n \"支持创建和使用机器人(限时免费)\"\n ]\n },\n {\n \"name\": \"管理和安全\",\n \"rules\": [\n\t\t\t\t\t\"列权限不限量\",\n\t\t\t\t\t\"文件权限不限量\",\n\t\t\t\t\t\"管理员不限量\",\n\t\t\t\t\t\"支持 Office 文件预览\",\n\t\t\t\t\t\"支持飞书、钉钉、企业微信集成\",\n\t\t\t\t\t\"支持全局水印\",\n\t\t\t\t\t\"禁止导出数据\",\n\t\t\t\t\t\"禁止创建公开链接\",\n\t\t\t\t\t\"禁止复制数据\",\n\t\t\t\t\t\"禁止下载附件\",\n\t\t\t\t\t\"禁止邀请站外用户\",\n\t\t\t\t\t\"通讯录显示手机号\",\n\t\t\t\t\t\"*支持单点登录(SSO)\"\n ]\n },\n {\n \"name\": \"支持服务\",\n \"rules\": [\n\t\t\t\t\t\"VIP 顾问 24 小时在线支持\",\n\t\t\t\t\t\"VIP 客户群支持\",\n\t\t\t\t\t\"中英文邮件支持\",\n\t\t\t\t\t\"免费在线视频培训\",\n\t\t\t\t\t\"专属 V+ 顾问\",\n\t\t\t\t\t\"定制功能开发(费用另计)\",\n\t\t\t\t\t\"企业级咨询培训\"\n ]\n }\n ]\n }\n]", + "en_US": "[\n {\n \"grade\": \"community\",\n \"btnText\": \"Dowload\",\n \"primaryContent\": [\n \"10,000 file nodes\",\n \"50,000 records per datasheet\",\n \"Unlimitied attachments storage in space\"\n ],\n \"details\": [\n {\n \"name\": \"用量\",\n \"rules\": [\n \"10,000 file nodes\",\n \"50,000 records per datasheet\",\n \"50,0000,000 records in space\",\n \"Unlimitied attachments storage in space\"\n ]\n },\n {\n \"name\": \"功能\",\n \"rules\": [\n \"Pin file nodes\",\n \"Gallery view,Kanban view and Architecture view are unlimited\",\n \"Gantt views are unlimited\",\n \"Calendar views are unlimited\",\n \"Forms are unlimited\",\n \"Mirrors are unlimited\",\n \"Dashbords are unlimited\",\n \"Widget is free for a limited time\",\n \"Basic colors for rainbow tags\",\n \"Record activity goes back 24 months\",\n \"Trash goes back 24 months\",\n \"API is free for a limited time\",\n \"Robot is free for a limited time\"\n ]\n },\n {\n \"name\": \"Management and security\",\n \"rules\": [\n \"Not supported\"\n ]\n },\n {\n \"name\": \"Support service\",\n \"rules\": [\n \"community service\"\n ]\n }\n ]\n },\n {\n \"grade\": \"custom\",\n \"btnText\": \"Require Trial\",\n \"primaryContent\": [\n \"10,000 file nodes\",\n \"50,000 records per datasheet\",\n \"10G/seats attachments storage in space\",\n \"Unlimited permission and security settings\",\n \"Consultancy service\"\n ],\n \"details\": [\n {\n \"name\": \"Usage\",\n \"rules\": [\n \"10,000 file nodes\",\n \"50,000 records per datasheet\",\n \"50,0000,000 records in space\",\n \"Unlimited attachments storage in space\"\n ]\n },\n {\n \"name\": \"Function\",\n \"rules\": [\n \"Pin file nodes\",\n \"Gallery view,Kanban view and Architecture view are unlimited\",\n \"Gantt views are unlimited\",\n \"Calendar views are unlimited\",\n \"Forms are unlimited\",\n \"Mirrors are unlimited\",\n \"Dashbords are unlimited\",\n \"Widget is free for a limited time\",\n \"Advanced colors for rainbow tags\",\n \"Custom embedding\",\n \"Record activity goes back 24 months\",\n \"Trash goes back 24 months\",\n \"API is free for a limited time\",\n \"Robot is free for a limited time\"\n ]\n },\n {\n \"name\": \"Management and security\",\n \"rules\": [\n \"Permissions are unlimited\",\n \"Permissions are unlimited\",\n \"Admins are unlimited\",\n \"Watermark\",\n \"Prohibit data export\",\n \"Prohibit opening public links\",\n \"Prohibit copying datas\",\n \"Prohibit downloadimg files\",\n \"Prohibit inviting off-space user\",\n \"Single sign-on(SSO)\"\n ]\n },\n {\n \"name\": \"Support service\",\n \"rules\": [\n \"Consultancy service\",\n \"Custom function development (additional cost)\",\n \"Enterprise-level consulting training\"\n ]\n }\n ]\n }\n ]", + "zh_HK": "[\n {\n \"grade\": \"community\",\n \"btnText\": \"联系客服\",\n \"primaryContent\": [\n \"文件节点数不限\",\n \"单表行记录数不限\",\n \"支持相册、看板、架构、甘特、日历视图与神奇表单\"\n ],\n \"details\": [\n {\n \"name\": \"用量\",\n \"rules\": [\n \"文件节点数不限\",\n \"单表行记录数不限\",\n \"空间站总记录数不限\",\n \"附件容量不限\"\n ]\n },\n {\n \"name\": \"功能\",\n \"rules\": [\n \"支持星标\",\n \"支持相册视图、看板视图、架构视图\",\n \"甘特视图不限量\",\n \"日历视图不限量\",\n \"神奇表单不限量\",\n \"镜像文件(行权限)不限量\",\n \"支持基础、自建和定制小程序,不限量\",\n \"仪表盘不限量\",\n \"支持彩虹色系的标签(30 个)\",\n \"支持时光机(历史记录)\",\n \"支持回收舱(历史文件)\",\n \"API 请求不限\",\n \"支持创建和使用机器人(限时免费)\"\n ]\n },\n {\n \"name\": \"管理和安全\",\n \"rules\": [\n \"无\"\n ]\n },\n {\n \"name\": \"支持服务\",\n \"rules\": [\n \"基础在线支持\",\n \"用户社群支持\",\n \"中英文邮件支持\",\n \"免费在线视频培训\"\n ]\n }\n ]\n },\n {\n \"grade\": \"custom\",\n \"btnText\": \"联系客服\",\n \"primaryContent\": [\n \"支持全部视图、神奇表单和列权限等全部高级功能\",\n \"支持第三方 IM 集成、全局水印等安全管理功能\",\n \"专属 V+ 顾问提供全程支持服务\"\n ],\n \"details\": [\n {\n \"name\": \"用量\",\n \"rules\": [\n \"文件节点数不限\",\n \"单表行记录数不限\",\n \"空间站总记录数不限\",\n \"附件容量不限\"\n ]\n },\n {\n \"name\": \"功能\",\n \"rules\": [\n \"支持星标\",\n \"支持相册视图、看板视图、架构视图\",\n \"甘特视图不限量\",\n \"日历视图不限量\",\n \"神奇表单不限量\",\n \"镜像文件(行权限)不限量\",\n \"支持基础、自建和定制小程序,不限量\",\n \"仪表盘不限量\",\n \"支持彩虹色系的标签(50 个)\",\n \"支持全平台客户端\",\n \"支持时光机(历史记录)\",\n \"支持回收舱(历史文件)\",\n \"API 请求不限\",\n \"支持创建和使用机器人(限时免费)\"\n ]\n },\n {\n \"name\": \"管理和安全\",\n \"rules\": [\n\t\t\t\t\t\"列权限不限量\",\n\t\t\t\t\t\"文件权限不限量\",\n\t\t\t\t\t\"管理员不限量\",\n\t\t\t\t\t\"支持 Office 文件预览\",\n\t\t\t\t\t\"支持飞书、钉钉、企业微信集成\",\n\t\t\t\t\t\"支持全局水印\",\n\t\t\t\t\t\"禁止导出数据\",\n\t\t\t\t\t\"禁止创建公开链接\",\n\t\t\t\t\t\"禁止复制数据\",\n\t\t\t\t\t\"禁止下载附件\",\n\t\t\t\t\t\"禁止邀请站外用户\",\n\t\t\t\t\t\"通讯录显示手机号\",\n\t\t\t\t\t\"*支持单点登录(SSO)\"\n ]\n },\n {\n \"name\": \"支持服务\",\n \"rules\": [\n\t\t\t\t\t\"VIP 顾问 24 小时在线支持\",\n\t\t\t\t\t\"VIP 客户群支持\",\n\t\t\t\t\t\"中英文邮件支持\",\n\t\t\t\t\t\"免费在线视频培训\",\n\t\t\t\t\t\"专属 V+ 顾问\",\n\t\t\t\t\t\"定制功能开发(费用另计)\",\n\t\t\t\t\t\"企业级咨询培训\"\n ]\n }\n ]\n }\n]" + }, "subscription_information": { "zh_CN": "订阅信息", "en_US": "Subscription plan", @@ -21952,13 +22627,13 @@ "zh_HK": "添加目標值" }, "summary_widget_select_field": { - "zh_CN": "选择一个字段进行统计", - "en_US": "Select a field for summary", - "zh_HK": "選擇一個字段進行統計" + "zh_CN": "选择一种统计方式", + "en_US": "Select a way for summarizing", + "zh_HK": "選擇一種統計方式" }, "summary_widget_select_view": { "zh_CN": "从下方视图读取数据", - "en_US": "Select a view as data source", + "en_US": "Select a view as the data source", "zh_HK": "從下方視圖讀取數據" }, "summary_widget_setting": { @@ -21972,9 +22647,9 @@ "zh_HK": "配置教程" }, "summary_widget_setting_help_url": { - "zh_CN": "/help/intro-widget-summary/ ", - "en_US": "/help/intro-widget-summary", - "zh_HK": "/help/intro-widget-summary/ " + "zh_CN": "https://help.vika.cn/docs/guide/intro-widget-summary", + "en_US": "https://help.vika.cn/docs/guide/intro-widget-summary", + "zh_HK": "https://help.vika.cn/docs/guide/intro-widget-summary" }, "superior_team": { "zh_CN": "上级小组", @@ -22076,6 +22751,26 @@ "en_US": "Syria", "zh_HK": "敘利亞" }, + "system_configuration_company_copyright": { + "zh_CN": "copyright @ 2019-2021 深圳维格云科技有限公司.All rights reserved.", + "en_US": "copyright @ 2019-2021 深圳维格云科技有限公司.All rights reserved.", + "zh_HK": "copyright @ 2019-2021 深圳維格雲科技有限公司.All rights reserved." + }, + "system_configuration_company_name_short": { + "zh_CN": "vika", + "en_US": "vika", + "zh_HK": "vika" + }, + "system_configuration_company_official_account": { + "zh_CN": "官方公众号", + "en_US": "Official account", + "zh_HK": "官方公眾號" + }, + "system_configuration_product_name": { + "zh_CN": "维格表", + "en_US": "vikadata", + "zh_HK": "維格表" + }, "system_message": { "zh_CN": "系统消息", "en_US": "System message", @@ -22093,7 +22788,7 @@ }, "tab_org": { "zh_CN": "组织架构", - "en_US": "Organizational structure" + "en_US": "Organization" }, "tab_role": { "zh_CN": "角色", @@ -22295,9 +22990,9 @@ "zh_HK": "成員字段不存在" }, "team": { - "zh_CN": "团队", + "zh_CN": "小组", "en_US": "Team", - "zh_HK": "團隊" + "zh_HK": "小組" }, "team_is_exist_err": { "zh_CN": "同层级的小组已存在该名称,请使用其他名称", @@ -22330,9 +23025,21 @@ "zh_HK": "正在與 ${number} 人協作中 " }, "template": { - "zh_CN": "空间站模板", - "en_US": "Templates", - "zh_HK": "空間站模板" + "zh_CN": "模板", + "en_US": "Template" + }, + "template_advise_tip": { + "zh_CN": "没有想要的模板?欢迎向我们提出建议和吐槽!", + "en_US": "Don't have the template you want? Feel free to send us suggestions !" + }, + "template_album_share_success": { + "zh_CN": "专题分享链接已复制成功", + "en_US": "The sharing link has been copied successfully" + }, + "template_center_use_to_create_datasheets": { + "zh_CN": "使用模板创建维格表", + "en_US": "Use this template to create datasheet(s)", + "zh_HK": "使用模板創建維格表" }, "template_centre": { "zh_CN": "维格模板中心", @@ -22341,7 +23048,7 @@ }, "template_centre_create_vika_used_by_template": { "zh_CN": "使用模板创建维格表", - "en_US": "Use template to create datasheet", + "en_US": "Use this template to create datasheet(s)", "zh_HK": "使用模板創建維格表" }, "template_centre_using_template_data": { @@ -22377,6 +23084,10 @@ "template_experience": { "zh_HK": "undefined" }, + "template_feedback": { + "zh_CN": "点击反馈", + "en_US": "Feedback" + }, "template_go_back": { "zh_CN": "模板中心 - ${category}", "en_US": "Templates - ${category}", @@ -22440,7 +23151,6 @@ "terms_of_service": { "zh_CN": "《服务条款》", "en_US": "", - "终端": "APP", "zh_HK": "《服務條款》" }, "terms_of_service_pure_string": { @@ -22448,6 +23158,10 @@ "en_US": "Terms of service", "zh_HK": " 服務條款" }, + "terms_of_service_title": { + "zh_CN": "维格服务协议", + "en_US": "Service Agreement" + }, "test": { "zh_CN": "测试", "en_US": "test", @@ -22475,7 +23189,7 @@ }, "test_function_card_info_async_compute": { "zh_CN": "在数据量较大时提升表格内增删改操作的流畅性,减少卡顿", - "en_US": "Async computing gives you a smoothier adding, editing, and deleting experience in a data-heavy datasheet", + "en_US": "Async computing gives you a smoother editing experience in a data-heavy datasheet", "zh_HK": "在數據量較大時提升表格內增刪改操作的流暢性,減少卡頓" }, "test_function_card_info_render_normal": { @@ -22490,17 +23204,17 @@ }, "test_function_card_info_robot": { "zh_CN": "使用机器人,将维格表中的重复性操作自动化,解放员工生产力", - "en_US": "Vika Robots help you automate repetitive actions in the datasheets and free up your team's productivity", + "en_US": "Vika Robots help you automate repetitive actions in the datasheets and free your team's productivity", "zh_HK": "使用機器人,將維格表中的重複性操作自動化,解放員工生產力" }, "test_function_card_info_view_manual_save": { "zh_CN": "成员修改视图配置后不会同步给其他成员,仅对自己临时生效", - "en_US": "View configurations modified by a member are not synchronized to other members but only take effect temporarily for himself", + "en_US": "View configurations modified by members only take effect temporarily for themselves", "zh_HK": "成員修改視圖配置後不會同步給其他成員,僅對自己臨時生效" }, "test_function_card_info_widget": { "zh_CN": "使用官方 SDK 自定义开发小程序,支持更丰富的数据可视化和数据传输方式,满足更多的业务场景", - "en_US": "Use widget SDK to develop custom widgets that support richer data visualization and data transfer and meet more business needs", + "en_US": "Use widget SDK to develop custom widgets to meet more business needs", "zh_HK": "使用官方 SDK 自定義開發小程序,支持更豐富的數據可視化和數據傳輸方式,滿足更多的業務場景" }, "test_function_desc": { @@ -22525,7 +23239,7 @@ }, "test_function_modal_info_async_compute": { "zh_CN": "在数据量较大情况下,启用此功能可以提升表格内新增、编辑、删除等操作的流畅性,减少卡顿的情况。", - "en_US": "After the async computing mode is enabled, adding, editing, and deleting operations in a datasheet with a large amount of data can be smoothier (the latency will be lower)", + "en_US": "After the async computing mode is enabled, adding, editing, and deleting operations in a datasheet with a large amount of data can be smoother (the latency will be lower)", "zh_HK": "在數據量較大情況下,啟用此功能可以提升表格內新增、編輯、刪除等操作的流暢性,減少卡頓的情況。" }, "test_function_modal_info_render_normal": { @@ -22539,13 +23253,13 @@ "zh_HK": "在數據量較大情況下,啟用此功能可以提升表格內滾動查看數據的流暢性,減少卡頓的情況。" }, "test_function_modal_info_robot": { - "zh_CN": "

    机器人是维格表的效率守护者,将重复性工作都交给机器人吧。有创造性的人类,值得去做更有价值的事情!查看文档

    \n\n

    机器人示例

    \n
    • 小钉机器人:当有新的记录创建时,机器人会自动发消息到钉钉群
    • \n
    • 小飞机器人:当有新的表单提交时,机器人会自动发消息到飞书群
    • \n
    • 小钩机器人:当有记录符合条件时,机器人会自动发送网络请求
    ", - "en_US": "

    Vika Robots are the guardians for your work efficiency on the Vika Planet. Human beings are meant to do more creative things! < a href=\"https://vika.cn/help/manual-vika-robot\" target =\"_blank\"> View documentation

    \n\n

    Example Robots:

    \n\n
    • Robot Alpha: When a record is created, Alpha will automatically send a message to the Slack channel
    • \n\n
    • Robot Beta: When a specific form is submitted, Beta will automatically send a message to the Lark chat group
    • \n\n
    • Robot Delta: when a record meets a specific condition, Delta will automatically update another record in another datasheet
    ", - "zh_HK": "

    機器人是維格表的效率守護者,將重複性工作都交給機器人吧。有創造性的人類,值得去做更有價值的事情! 查看文檔

    \n\n

    機器人示例

    \n
    • 小釘機器人:當有新的記錄創建時,機器人會自動發消息到釘釘群
    • \n
    • 小飛機器人:當有新的表單提交時,機器人會自動發消息到飛書群
    • \n
    • 小鉤機器人:當有記錄符合條件時,機器人會自動發送網絡請求
    " + "zh_CN": "

    机器人是维格表的效率守护者,将重复性工作都交给机器人吧。有创造性的人类,值得去做更有价值的事情!查看文档

    \n\n

    机器人示例

    \n
    • 小钉机器人:当有新的记录创建时,机器人会自动发消息到钉钉群
    • \n
    • 小飞机器人:当有新的表单提交时,机器人会自动发消息到飞书群
    • \n
    • 小钩机器人:当有记录符合条件时,机器人会自动发送网络请求
    ", + "en_US": "

    Vika Robots are the guardians for your work efficiency on the Vika Planet. Human beings are meant to do more creative things! View documentation

    \n\n

    Example Robots:

    \n\n
    • Robot Alpha: When a record is created, Alpha will automatically send a message to the Slack channel
    • \n\n
    • Robot Beta: When a specific form is submitted, Beta will automatically send a message to the Lark chat group
    • \n\n
    • Robot Delta: when a record meets a specific condition, Delta will automatically update another record in another datasheet
    ", + "zh_HK": "

    機器人是維格表的效率守護者,將重複性工作都交給機器人吧。有創造性的人類,值得去做更有價值的事情! 查看文檔

    \n\n

    機器人示例

    \n
    • 小釘機器人:當有新的記錄創建時,機器人會自動發消息到釘釘群
    • \n
    • 小飛機器人:當有新的表單提交時,機器人會自動發消息到飛書群
    • \n
    • 小鉤機器人:當有記錄符合條件時,機器人會自動發送網絡請求
    " }, "test_function_modal_info_view_manual_save": { "zh_CN": "

    当多个成员在同一个视图下修改筛选、分组或排序时经常会产生冲突,因为每个人都想根据自己的需求进行配置。开启此功能后「视图」和「镜像」内支持临时的视图配置,减少冲突场景

    \n\n

    该功能开启后影响:

    \n
      \n
    • 「视图」内成员修改视图配置后仅对自己临时生效,刷新后恢复默认配置。成员可选择手动保存视图配置并同步给其他成员
    • \n
    • 「镜像」内也支持临时的视图配置。特例:无法在镜像内修改源表视图下的筛选条件
    • \n
    • 「只读」权限的用户也允许临时修改视图配置。
    • \n
    • 视图配置包括:筛选、分组、排序、隐藏列、样式、布局、列顺序、统计栏等
    • \n
    ", - "en_US": "

    When multiple members modify filter, group, or sorting in the same view, conflicts often arise, because everyone wants to configure them according to their own needs. After enabling this function, temporary view configuration is supported in \"Datasheet\" and \"Mirror\" to reduce conflicts

    \n\n

    Impacts after this function is turned on:

    \n
      \n
    • View configurations modified by a member are not synchronized to other members but only take effect temporarily for himself
    • \n
    • \"Mirror\" also supports temporary view configuration. Special case: the filters under the source datasheet view cannot be modified in the mirror
    • \n
    • \n
    • View configuration includes: filter, group, sort, hidden columns, style, layout, column order, summary bar, etc.
    • \n
    ", + "en_US": "

    Conflicts often arise when members modify filtering, grouping, or sorting in the same view, as each wants to configure it to suit their needs. After enabling this feature, temporary view configuration is supported in \"Datasheet\" and \"Mirror\" to reduce conflicts.

    \n\n

    After this feature is enabled, it affects:

    \n
      \n
    • After members in \"View\" modify the view configuration, it will only take effect temporarily for themselves. After refreshing, the default configuration will be restored. Members can choose to manually save the view configuration and sync it to other members
    • \n
    • Temporary view configurations are also supported on Mirror. Special case: The filter conditions of the source datasheet view cannot be modified in the mirror
    • \n
    • Users with \"read-only\" permission are also allowed to temporarily modify the view configuration.
    • \n
    • View configuration includes: filter, group, sort, hide fields, style, layout, field order, statistics, etc.
    • \n
    ", "zh_HK": "

    當多個成員在同一個視圖下修改篩選、分組或排序時經常會產生衝突,因為每個人都想根據自己的需求進行配置。開啟此功能後「視圖」和「鏡像」內支持臨時的視圖配置,減少衝突場景

    \n\n

    該功能開啟後影響:

    \n
      \n
    • 「視圖」內成員修改視圖配置後僅對自己臨時生效,刷新後恢復默認配置。成員可選擇手動保存視圖配置並同步給其他成員
    • \n
    • 「鏡像」內也支持臨時的視圖配置。特例:無法在鏡像內修改源表視圖下的篩選條件
    • \n
    • 「只讀」權限的用戶也允許臨時修改視圖配置。
    • \n
    • 視圖配置包括:篩選、分組、排序、隱藏列、樣式、佈局、列順序、統計欄等
    • \n
    " }, "test_function_modal_info_widget": { @@ -22595,7 +23309,7 @@ }, "test_function_space_level_desc": { "zh_CN": "启用空间站级别的实验性功能后,会对空间站的所有成员生效", - "en_US": "After you enable an experimental feature at the Space level, it will take effect for all members in the Space", + "en_US": "After you enable an experimental feature at the Space level, it will take effect on all members in the Space", "zh_HK": "啟用空間站級別的實驗性功能後,會對空間站的所有成員生效" }, "test_function_space_level_title": { @@ -22605,7 +23319,7 @@ }, "test_function_user_level_desc": { "zh_CN": "启用成员级别的实验性功能后,仅会对你自己生效", - "en_US": "After you enable an experimental feature at the member level, it will only take effect for yourself", + "en_US": "After you enable an experimental feature at the member level, it will only take effect on yourself", "zh_HK": "啟用成員級別的實驗性功能後,僅會對你自己生效" }, "test_function_user_level_title": { @@ -22864,14 +23578,19 @@ "en_US": "Unlimited Time Machine Histories", "zh_HK": "歷史記錄無限期保存" }, + "timemachine_help_url": { + "zh_CN": "https://help.vika.cn/docs/guide/manual-timemachine", + "en_US": "https://help.vika.cn/docs/guide/manual-timemachine", + "zh_HK": "https://help.vika.cn/docs/guide/manual-timemachine" + }, "times_per_month_unit": { "zh_CN": "次/月", - "en_US": "calls/month", + "en_US": "call(s)/month", "zh_HK": "次/月" }, "times_unit": { "zh_CN": "次", - "en_US": "calls", + "en_US": " call(s)", "zh_HK": "次" }, "timor_leste": { @@ -22931,7 +23650,7 @@ }, "to_new_main_admin_tip_after_change": { "zh_CN": "拥有空间站的最高管理权限,可以分配子管理员或转让空间站", - "en_US": "Admins have full access to the Space, such as assigning sub-admins and transferring ownership of the Space", + "en_US": "Admin have full access to the Space, such as assigning sub-admins and transferring ownership of the Space", "zh_HK": "擁有空間站的最高管理權限,可以分配子管理員或轉讓空間站" }, "to_old_main_admin_tip_after_change": { @@ -22941,7 +23660,7 @@ }, "to_select_tip": { "zh_CN": "此列的值将会转换成以下的选项", - "en_US": "The value of this column will be converted into the following options.", + "en_US": "The value of this field will be converted into the following options.", "zh_HK": "此列的值將會轉換成以下的選項" }, "to_view_dashboard": { @@ -23016,7 +23735,7 @@ }, "toggle_catalog_panel": { "zh_CN": "展开折叠目录面板", - "en_US": "Unhide Workbench", + "en_US": "Expand Workbench", "zh_HK": "展開折疊目錄面板" }, "toggle_comment_pane": { @@ -23136,7 +23855,7 @@ }, "trash_tip": { "zh_CN": "回收舱会显示最近 ${day} 天内删除的文件,你仅可查看具有「可以管理」权限的文件", - "en_US": "You can restore files deleted in the past ${day} days from the Trash. Expired files will be deleted permanently.", + "en_US": "You can restore files deleted in the past ${day} days from the Trash. You can only view files with \"manager\" permissions", "zh_HK": "回收艙會顯示最近 ${day} 天內刪除的文件,你僅可查看具有「可以管理」權限的文件" }, "travel_and_outdoors": { @@ -23254,6 +23973,11 @@ "en_US": "You do not have sufficient permissions to perform this operation", "zh_HK": "您的權限不足,不能進行此操作" }, + "unavailable_og_title_content": { + "zh_CN": "暂时无法访问", + "en_US": "Temporarily unavailable", + "zh_HK": "暫時無法訪問" + }, "unbind": { "zh_CN": "解绑", "en_US": "Unbind", @@ -23345,9 +24069,9 @@ "zh_HK": "升級到白銀PRO空間站,永久解鎖該功能" }, "unnamed": { - "zh_CN": "未命名", - "en_US": "Unnamed", - "zh_HK": "未命名" + "zh_CN": "匿名用户", + "en_US": "Anonymous", + "zh_HK": "匿名用戶" }, "unordered_list": { "zh_CN": "无序列表", @@ -23421,7 +24145,7 @@ }, "upgrade": { "zh_CN": "升级", - "en_US": "upgrade", + "en_US": "Upgrade", "zh_HK": "升級" }, "upgrade_expire_time_warning": { @@ -23550,7 +24274,9 @@ "zh_HK": "烏拉圭" }, "usage_overlimit_alert_title": { - "zh_CN": "功能超限提示" + "zh_CN": "功能超限提示", + "en_US": "Over limits", + "zh_HK": "功能超限提示" }, "use_the_template": { "zh_CN": "使用该模板", @@ -23585,7 +24311,6 @@ "user_feedback": { "zh_CN": "反馈问题", "en_US": "Give feedback", - "终端": "APP", "zh_HK": "反饋問題" }, "user_log_out": { @@ -23704,7 +24429,6 @@ "verification_code": { "zh_CN": "验证码", "en_US": "Verification code", - "终端": "APP", "zh_HK": "驗證碼" }, "verification_code_error_message": { @@ -23715,7 +24439,6 @@ "verification_code_login": { "zh_CN": "验证码登录/注册", "en_US": "Log in with verification code", - "终端": "APP", "zh_HK": "驗證碼登錄/註冊" }, "verify_account_title": { @@ -23783,11 +24506,21 @@ "en_US": "Export view", "zh_HK": "導出視圖數據" }, + "view_field": { + "zh_CN": "维格列", + "en_US": "Field", + "zh_HK": "維格列" + }, "view_field_permission": { "zh_CN": "查看列权限 (Beta)", "en_US": "View field permissions (Beta)", "zh_HK": "查看列權限 (Beta)" }, + "view_field_search_not_found_tip": { + "zh_CN": "未找到名称为“${value}”的维格列", + "en_US": "Column named \"${value}\" unfound", + "zh_HK": "未找到名稱為“${value}”的維格列" + }, "view_find": { "zh_CN": "查找一个视图", "en_US": "Find a view", @@ -23808,6 +24541,21 @@ "en_US": "Want to populate data to your datasheet in a uniform way? Form is the best choice", "zh_HK": "想要規範化錄入數據?使用神奇表單試試" }, + "view_form": { + "zh_CN": "神奇表单", + "en_US": "Form", + "zh_HK": "神奇表單" + }, + "view_form_field_changed_tip": { + "zh_CN": "当前表单有字段新增/类型变更", + "en_US": "The form has a new field or type change", + "zh_HK": "當前表單有字段新增/類型變更" + }, + "view_full_catalog": { + "zh_CN": "查看完整工作目录", + "en_US": "View the full catalog", + "zh_HK": "查看完整工作目錄" + }, "view_has_locked_not_deletes": { "zh_CN": "该视图被锁定,无法被删除", "en_US": "The view is locked and can't be deleted", @@ -23820,7 +24568,7 @@ }, "view_lock": { "zh_CN": "视图锁", - "en_US": "View lock", + "en_US": "Lock view", "zh_HK": "視圖鎖" }, "view_lock_command_error": { @@ -23919,9 +24667,9 @@ "zh_HK": "該字段暫不支持設置為分組/排序" }, "view_sort_help": { - "zh_CN": "/help/manual-sort/", - "en_US": "/help/manual-sort/", - "zh_HK": "/help/manual-sort/" + "zh_CN": "https://help.vika.cn/docs/guide/manual-sort", + "en_US": "https://help.vika.cn/docs/guide/manual-sort", + "zh_HK": "https://help.vika.cn/docs/guide/manual-sort" }, "view_sync_property_close_tip": { "zh_CN": "视图配置协同已关闭,配置不会同步给其他成员,刷新后清除", @@ -23968,11 +24716,6 @@ "en_US": "vika", "zh_HK": "vika" }, - "vika_cloud": { - "zh_CN": "维格云", - "en_US": "Vika Enterprise", - "zh_HK": "維格雲" - }, "vika_column": { "zh_CN": "维格列", "en_US": "Field", @@ -24013,11 +24756,6 @@ "en_US": "", "zh_HK": "《維格隱私政策》" }, - "vika_select_placeholder": { - "zh_CN": "请选择一个选项", - "en_US": "Pick a field", - "zh_HK": "請選擇一個選項" - }, "vika_share_link_template": { "zh_CN": "【维格表】- ${nickName}给你分享了《${nodeName}》,为了更好的体验,建议通过电脑浏览器访问", "en_US": "From vikadata: ${nickName} shared the \"${nodeName}\" file with you. Open it via a computer browser for a better experience.", @@ -24038,21 +24776,11 @@ "en_US": "Time-limited welfare", "zh_HK": "限時福利" }, - "vikaby_first_menu1": { - "zh_CN": "新手任务", - "en_US": "Beginner task", - "zh_HK": "新手任務" - }, "vikaby_helper": { "zh_CN": "维格小助手", "en_US": "Vikaby assistant", "zh_HK": "維格小助手" }, - "vikaby_menu_activity_center": { - "zh_CN": "模板征集", - "en_US": "Template collection", - "zh_HK": "模板徵集" - }, "vikaby_menu_beginner_task": { "zh_CN": "新手任务", "en_US": "Beginner tasks", @@ -24285,13 +25013,9 @@ "zh_CN": "因为企业微信应用市场修改了运营规则,维格表已根据指引进行了收费模式的调整,企业客户安装维格表后,接口许可免费试用期过期后,企业微信接口将无法正常调用。\n\n因此,2022年7月1日起您将无法进入应用、无法使用同步通讯录和收取应用消息等功能,为了不影响您的正常使用,请联系您的专属客服,客服将为您提供协助。", "zh_HK": "因為企業微信應用市場修改了運營規則,維格表已根據指引進行了收費模式的調整,企業客戶安裝維格表後,接口許可免費試用期過期後,企業微信接口將無法正常調用。\n\n因此,2022年7月1日起您將無法進入應用、無法使用同步通訊錄和收取應用消息等功能,為了不影響您的正常使用,請聯繫您的專屬客服,客服將為您提供協助。" }, - "wecom_api_intercept_notification_title": { - "zh_CN": "重要通知", - "zh_HK": "重要通知" - }, "wecom_app_desc": { "zh_CN": "

    在企业微信上使用维格表应用,你可以

    \n\n
    • 免登录,直接在企业微信进入维格表空间站,访问空间站数据
    • 通过企业微信,收取空间站里的通知
    • 在企业微信设置应用可见范围,并同步企业微信的通讯录
    ", - "en_US": "Coming soon", + "en_US": "

    Using Vikadata on WeCom, you can

    \n\n
    • Access Vikadata's space and access space data directly on WeCom without logging in;
    • Receive notifications from the space through WeCom;
    • Set app visibility on WeCom and automatically sync WeCom's organization structure;
    ", "zh_HK": "

    在企業微信上使用維格表應用,你可以

    \n\n
    • 免登錄,直接在企業微信進入維格表空間站,訪問空間站數據
    • 通過企業微信,收取空間站裡的通知
    • 在企業微信設置應用可見範圍,並同步企業微信的通訊錄
    " }, "wecom_app_intro": { @@ -24307,17 +25031,27 @@ "wecom_base": { "zh_CN": "企业微信基础版", "en_US": "Basic Plan with WeCom", - "zh_HK": "企業微信基礎版" + "zh_HK": "企業微信基礎版", + "billing": { + "products": [ + "recx3LO0cofxE" + ] + } }, "wecom_enterprise": { "zh_CN": "企业微信旗舰版", "en_US": "Ultimate Plan with WeCom", - "zh_HK": "企業微信旗艦版" + "zh_HK": "企業微信旗艦版", + "billing": { + "products": [ + "recpmABohtEb2" + ] + } }, "wecom_grade_desc": { - "zh_CN": "空间站等级分为企业微信基础版(永久免费)、企业微信标准版和企业微信企业版。不同等级的空间站享有不同的权益,空间站成员享有所处等级空间站的相关权益", - "en_US": "Spaces have three kinds of billing plans: Basic Plan with WeCom(permanently free), Standard Plan with WeCom and Enterprise Plan with WeCom. Members in the Spaces of different billing plans enjoy different rights and benefits.", - "zh_HK": "空間站等級分為企業微信基礎版(永久免費)、企業微信標準版和企業微信企業版。不同等級的空間站享有不同的權益,空間站成員享有所處等級空間站的相關權益" + "zh_CN": "空间站等级分为企业微信基础版(永久免费)、企业微信标准版、企微标准版和企业微信企业版。不同等级的空间站享有不同的权益,空间站成员享有所处等级空间站的相关权益", + "en_US": "Spaces have three kinds of billing plans: Basic Plan with WeCom(permanently free), Standard Plan with WeCom ,Profession Plan with WeCom and Enterprise Plan with WeCom. Members in the Spaces of different billing plans enjoy different rights and benefits.", + "zh_HK": "空間站等級分為企業微信基礎版(永久免費)、企業微信標準版、企微標準版和企業微信企業版。不同等級的空間站享有不同的權益,空間站成員享有所處等級空間站的相關權益" }, "wecom_integration_desc_check": { "zh_CN": "我已知悉所有成员会被移除空间站,相关权限信息会被清除", @@ -24407,17 +25141,26 @@ "wecom_profession": { "zh_CN": "企业微信专业版", "en_US": "Pro Plan with WeCom", - "zh_HK": "企業微信專業版" + "zh_HK": "企業微信專業版", + "billing": { + "products": [ + "recjJ2DkMsdg8" + ] + } + }, + "wecom_single_record_comment_mentioned": { + "zh_CN": "维格表:{nodeName}\n记录:{recordTitle}\n\n\"{commentContent}\"", + "en_US": "Datasheet:{nodeName}\nRecord:{recordTitle}\n\n\"{commentContent}\"\n", + "zh_HK": "維格表:{nodeName}\n記錄:{recordTitle}\n\n\"{commentContent}\"" + }, + "wecom_single_record_comment_mentioned_title": { + "zh_CN": "有人在评论中提及你", + "en_US": " You're mentioned in a record comment" }, "wecom_single_record_member_mention_title": { "zh_CN": "有人在记录中提及你", "en_US": " You're mentioned in a record", - "zh_HK": "有人在記錄中提及你", - "notifications": { - "social_templates copy": [ - "rec9aOlARGasl" - ] - } + "zh_HK": "有人在記錄中提及你" }, "wecom_social_deactivate_tip": { "zh_CN": "如需停用应用,请前往「企业微信管理后台 > 应用管理 > 维格表 > 删除应用」", @@ -24432,27 +25175,22 @@ "wecom_standard": { "zh_CN": "企业微信标准版", "en_US": "Standard Plan with WeCom", - "zh_HK": "企業微信標準版" + "zh_HK": "企業微信標準版", + "billing": { + "products": [ + "recc2g7sQa7WE" + ] + } }, "wecom_subscribed_record_cell_updated_title": { "zh_CN": "关注的记录被修改", "en_US": "Someone has changed the record you are watching in", - "zh_HK": "關注的記錄被修改", - "notifications": { - "social_templates copy": [ - "rec5qcIuCsYkz" - ] - } + "zh_HK": "關注的記錄被修改" }, "wecom_subscribed_record_commented_title": { "zh_CN": "关注的记录有一条新评论", "en_US": "The record you are watching received a new comment", - "zh_HK": "關注的記錄有一條新評論", - "notifications": { - "social_templates copy": [ - "recvTOkeCYFKO" - ] - } + "zh_HK": "關注的記錄有一條新評論" }, "wecom_sync_address_error": { "zh_CN": "你已被移出企业微信应用可见范围,同步通讯录将使空间站缺失主管理员,请更换主管理员后再同步通讯录", @@ -25304,7 +26042,7 @@ }, "without_day": { "zh_CN": "无期限", - "en_US": "No expiration date", + "en_US": "unlimited", "zh_HK": "無期限" }, "wizard_14_success_message": { @@ -25392,6 +26130,11 @@ "en_US": "Show watermarks", "zh_HK": "顯示全局水印" }, + "workbench_share_link_template": { + "zh_CN": "【维格表】- ${nickName}给你分享了《${nodeName}》,为了更好的体验,建议通过电脑浏览器访问", + "en_US": "From vikadata: ${nickName} shared the \"${nodeName}\" file with you. Open it via a computer browser for a better experience.", + "zh_HK": "【維格表】- ${nickName}給你分享了《${nodeName}》,為了更好的體驗,建議通過電腦瀏覽器訪問" + }, "workbench_side_space_template": { "zh_CN": "空间站模版", "en_US": "Custom templates", diff --git a/backend-server/application/src/main/resources/templates/email_verify.html b/backend-server/application/src/main/resources/templates/email_verify.html index 33fe7ad2d8a1d98efd753b938ea38ff9a2cfa2e8..1cda32d2de814aa494605a7a715f4dad999e6673 100644 --- a/backend-server/application/src/main/resources/templates/email_verify.html +++ b/backend-server/application/src/main/resources/templates/email_verify.html @@ -18,50 +18,414 @@ --> - - - + + + Email Verify Warning - - - -
    -

    We has already sent email, Please verify your email before logging in.

    -
    - - + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    Please confirm your email

    +

    A message with a confirmation link has been sent to your email address. Please follow the link to activate your account.

    +
    + \ No newline at end of file diff --git a/backend-server/application/src/main/resources/templates/notification/admin-notify-html.btl b/backend-server/application/src/main/resources/templates/notification/admin-notify-html.btl index c6dc8b7ac94d4269faa9de02b79a74df438456d5..d4776a71d19291bc72f2bea021c8d09be07b82c2 100644 --- a/backend-server/application/src/main/resources/templates/notification/admin-notify-html.btl +++ b/backend-server/application/src/main/resources/templates/notification/admin-notify-html.btl @@ -55,7 +55,7 @@ -

    - ${USER_NAME} assigned you as the admin of the "${SPACE_NAME}" Space

    + {{USER_NAME}} assigned you as the admin of the "{{SPACE_NAME}}" Space @@ -160,7 +160,7 @@ - Enter Space @@ -206,7 +206,7 @@

    - copyright © 2022-${YEARS} apitable. All rights reserved.

    + copyright © 2022-{{YEARS}} apitable. All rights reserved.

    diff --git a/backend-server/application/src/main/resources/templates/notification/admin-notify-text.btl b/backend-server/application/src/main/resources/templates/notification/admin-notify-text.btl index 43ef637ce58def0b91e1b2b82e6880fd1777b238..0a30c1d544afbcd6793c67e1da5e37f898f1af67 100644 --- a/backend-server/application/src/main/resources/templates/notification/admin-notify-text.btl +++ b/backend-server/application/src/main/resources/templates/notification/admin-notify-text.btl @@ -1,11 +1,11 @@ -${USER_NAME} assigned you as the admin of the "${SPACE_NAME}" Space +{{USER_NAME}} assigned you as the admin of the "{{SPACE_NAME}}" Space -------------------------------------------------------------------------------- Click the button below to enter the Space -${URL} +{{URL}} -copyright © 2022-${YEARS} apitable. All rights reserved. +copyright © 2022-{{YEARS}} apitable. All rights reserved. Contact us (https://apitable.com/) \ No newline at end of file diff --git a/backend-server/application/src/main/resources/templates/notification/invite-email-html.btl b/backend-server/application/src/main/resources/templates/notification/invite-email-html.btl index d29aea6731fec735488684f1df13345b091702df..1fec098cd96f11c89aa33df5e525bce950bd6fd5 100644 --- a/backend-server/application/src/main/resources/templates/notification/invite-email-html.btl +++ b/backend-server/application/src/main/resources/templates/notification/invite-email-html.btl @@ -45,7 +45,7 @@ -

    - ${USER_NAME} invited you to join Space

    + {{USER_NAME}} invited you to join Space

    - "${SPACE_NAME}"

    + "{{SPACE_NAME}}" @@ -153,7 +153,7 @@ - Enter Space @@ -199,7 +199,7 @@

    - copyright © 2022-${YEARS} apitable. All rights reserved.

    + copyright © 2022-{{YEARS}} apitable. All rights reserved.

    diff --git a/backend-server/application/src/main/resources/templates/notification/invite-email-text.btl b/backend-server/application/src/main/resources/templates/notification/invite-email-text.btl index 80cf71043179ac99b19be06f4fa62d82eb46960a..d8ef172626e178bed313ecbe6fc058847c9d18ca 100644 --- a/backend-server/application/src/main/resources/templates/notification/invite-email-text.btl +++ b/backend-server/application/src/main/resources/templates/notification/invite-email-text.btl @@ -1,12 +1,12 @@ -${USER_NAME} invited you to join the "${SPACE_NAME}" Space +{{USER_NAME}} invited you to join the "{{SPACE_NAME}}" Space -------------------------------------------------------------------------------- -Click on the link to enter the Space: ${INVITE_URL} +Click on the link to enter the Space: {{INVITE_URL}} -copyright © 2022-${YEARS} apitable. All rights reserved. +copyright © 2022-{{YEARS}} apitable. All rights reserved. Contact us (https://apitable.com/) \ No newline at end of file diff --git a/backend-server/application/src/main/resources/templates/notification/register-email-html.btl b/backend-server/application/src/main/resources/templates/notification/register-email-html.btl index e5fd955d6018849d0ad3a5f14faf9292216d488a..5896abd34a1a6f82b6427af10b8ebf4d6ae88300 100644 --- a/backend-server/application/src/main/resources/templates/notification/register-email-html.btl +++ b/backend-server/application/src/main/resources/templates/notification/register-email-html.btl @@ -46,7 +46,7 @@ -

    - ${VERIFICATION_CODE}

    + {{VERIFICATION_CODE}} @@ -227,7 +227,7 @@

    - ©2022-${YEARS} apitable. All rights reserved.

    + ©2022-{{YEARS}} apitable. All rights reserved.

    diff --git a/backend-server/application/src/main/resources/templates/notification/register-email-text.btl b/backend-server/application/src/main/resources/templates/notification/register-email-text.btl index ffdc2b5256d7c3061640b4b9d525055fa4e1f5cc..5f5d9b328996a4057348682c7725439e59ea4ced 100644 --- a/backend-server/application/src/main/resources/templates/notification/register-email-text.btl +++ b/backend-server/application/src/main/resources/templates/notification/register-email-text.btl @@ -4,11 +4,11 @@ New Account Email Verification Welcome! Your verification code is as follows: -${VERIFICATION_CODE} +{{VERIFICATION_CODE}} The code will become invalid after 15 minutes. If you didn't recently attempt to verify your email address, you can ignore this email. -copyright © 2022-${YEARS} apitable. All rights reserved. +copyright © 2022-{{YEARS}} apitable. All rights reserved. Contact us (https://apitable.com/) \ No newline at end of file diff --git a/backend-server/application/src/main/resources/templates/notification/remind-comment-html.btl b/backend-server/application/src/main/resources/templates/notification/remind-comment-html.btl index 47a7122d0a7f87b9448f3957ec71fbd9b441d6c1..e725c845e8cefb75ab09e3e58e51918e3dafb526 100644 --- a/backend-server/application/src/main/resources/templates/notification/remind-comment-html.btl +++ b/backend-server/application/src/main/resources/templates/notification/remind-comment-html.btl @@ -11,26 +11,26 @@ - + - + @@ -196,7 +196,7 @@
    - logo + logo
    ${MEMBER_NAME} mentioned you in the "${NODE_NAME}" datasheet{{MEMBER_NAME}} mentioned you in the "{{NODE_NAME}}" datasheet
    - + @@ -40,7 +40,7 @@
    - From ${MEMBER_NAME} + From {{MEMBER_NAME}} ${CREATED_AT}{{CREATED_AT}}
    - +
    Record: ${RECORD_TITLE}

    "${CONTENT}"
    Record: {{RECORD_TITLE}}

    "{{CONTENT}}"
    - View record + View record
    @@ -50,7 +50,7 @@
    copyright © 2022-${YEARS} apitable. All rights reserved.copyright © 2022-{{YEARS}} apitable. All rights reserved.
    diff --git a/backend-server/application/src/main/resources/templates/notification/remind-comment-text.btl b/backend-server/application/src/main/resources/templates/notification/remind-comment-text.btl index 4df58076c635019c8127512283a8844f36fc5eb6..bd96bb19a2e5b2edfaae5b4b5579787943df60e0 100644 --- a/backend-server/application/src/main/resources/templates/notification/remind-comment-text.btl +++ b/backend-server/application/src/main/resources/templates/notification/remind-comment-text.btl @@ -1,18 +1,18 @@ -${MEMBER_NAME} mentioned you in the "${NODE_NAME}" datasheet +{{MEMBER_NAME}} mentioned you in the "{{NODE_NAME}}" datasheet -------------------------------------------------------------------------------- -From ${MEMBER_NAME} -${CREATED_AT} +From {{MEMBER_NAME}} +{{CREATED_AT}} -Record: ${RECORD_TITLE} +Record: {{RECORD_TITLE}} -${CONTENT} +{{CONTENT}} View record -${URL} +{{URL}} -copyright © 2022-${YEARS} apitable. All rights reserved. +copyright © 2022-{{YEARS}} apitable. All rights reserved. Contact us (https://apitable.com/) \ No newline at end of file diff --git a/backend-server/application/src/main/resources/templates/notification/remind-member-html.btl b/backend-server/application/src/main/resources/templates/notification/remind-member-html.btl index 566884e9deca728becd336e4d54a297a7dec8ce1..4f53c7e155a02476e8c758809c2838e5ac699b81 100644 --- a/backend-server/application/src/main/resources/templates/notification/remind-member-html.btl +++ b/backend-server/application/src/main/resources/templates/notification/remind-member-html.btl @@ -11,26 +11,26 @@ - + - +
    - logo + logo
    ${MEMBER_NAME} mentioned you from the "${NODE_NAME}" datasheet in the "${SPACE_NAME}" Space{{MEMBER_NAME}} mentioned you from the "{{NODE_NAME}}" datasheet in the "{{SPACE_NAME}}" Space
    - + @@ -40,7 +40,7 @@
    - From ${MEMBER_NAME} + From {{MEMBER_NAME}} ${CREATED_AT}{{CREATED_AT}}
    - +
    Record: ${RECORD_TITLE}

    Mentioned you in field: ${FIELD_NAME}

    Record: {{RECORD_TITLE}}

    Mentioned you in field: {{FIELD_NAME}}

    - Enter Space + Enter Space
    @@ -50,7 +50,7 @@
    copyright © 2022-${YEARS} apitable. All rights reserved.copyright © 2022-{{YEARS}} apitable. All rights reserved.
    diff --git a/backend-server/application/src/main/resources/templates/notification/remind-member-text.btl b/backend-server/application/src/main/resources/templates/notification/remind-member-text.btl index ec4b210113ddb0ca144f56dcd1c983aaea45ce9f..215a2691520d6f5bcbf6472936d04cd4f7217ac0 100644 --- a/backend-server/application/src/main/resources/templates/notification/remind-member-text.btl +++ b/backend-server/application/src/main/resources/templates/notification/remind-member-text.btl @@ -1,4 +1,4 @@ -${MEMBER_NAME} mentioned you in ${ROW_NUM} comments from the "${NODE_NAME}" datasheet +{{MEMBER_NAME}} mentioned you in {{ROW_NUM}} comments from the "{{NODE_NAME}}" datasheet -------------------------------------------------------------------------------- @@ -6,6 +6,6 @@ Click on the link to enter the Space: https://apitable.com/ -copyright © 2022-${YEARS} apitable. All rights reserved. +copyright © 2022-{{YEARS}} apitable. All rights reserved. Contact us (https://apitable.com/) \ No newline at end of file diff --git a/backend-server/application/src/main/resources/templates/notification/remove-member-html.btl b/backend-server/application/src/main/resources/templates/notification/remove-member-html.btl index 3fbe3373b3e0b72ab4ef942972f533b9fec9beeb..1341a29530729659786ad9d3c37cdc135f74ad46 100644 --- a/backend-server/application/src/main/resources/templates/notification/remove-member-html.btl +++ b/backend-server/application/src/main/resources/templates/notification/remove-member-html.btl @@ -45,7 +45,7 @@
    -

    - You left the "${SPACE_NAME}" Space

    + You left the "{{SPACE_NAME}}" Space

    - ©2022-${YEARS} apitable. All rights reserved.

    + ©2022-{{YEARS}} apitable. All rights reserved.

    diff --git a/backend-server/application/src/main/resources/templates/notification/remove-member-text.btl b/backend-server/application/src/main/resources/templates/notification/remove-member-text.btl index 5a6a0af94d5b8ac269db10075a99515fd23cc0d6..c0ed6f50e74e8dc910391563ee762cff990862b9 100644 --- a/backend-server/application/src/main/resources/templates/notification/remove-member-text.btl +++ b/backend-server/application/src/main/resources/templates/notification/remove-member-text.btl @@ -1,11 +1,11 @@ -You left the "${SPACE_NAME}" Space. +You left the "{{SPACE_NAME}}" Space. -------------------------------------------------------------------------------- -The admin removed you from the "${SPACE_NAME}" Space. +The admin removed you from the "{{SPACE_NAME}}" Space. -copyright © 2022-${YEARS} apitable. All rights reserved. +copyright © 2022-{{YEARS}} apitable. All rights reserved. Contact us (https://apitable.com/) \ No newline at end of file diff --git a/backend-server/application/src/main/resources/templates/notification/space-apply-html.btl b/backend-server/application/src/main/resources/templates/notification/space-apply-html.btl index 2d97c692ce50b57d0afbf5933765fbdb4fee4f6a..ccd9f529125081d46e0d912e3a461d7fdc881139 100644 --- a/backend-server/application/src/main/resources/templates/notification/space-apply-html.btl +++ b/backend-server/application/src/main/resources/templates/notification/space-apply-html.btl @@ -45,7 +45,7 @@

    -

    - ${USER_NAME} is applying to join the "${SPACE_NAME}" Space

    + {{USER_NAME}} is applying to join the "{{SPACE_NAME}}" Space
    - View application

    - copyright © 2022-${YEARS} apitable. All rights reserved.

    + copyright © 2022-{{YEARS}} apitable. All rights reserved.

    diff --git a/backend-server/application/src/main/resources/templates/notification/space-apply-text.btl b/backend-server/application/src/main/resources/templates/notification/space-apply-text.btl index 39394d99bcba096cc84a1f55f5c70636dfc7c66f..0e24f215ebf874a6d1e71a2e3da4c3d74eb93de3 100644 --- a/backend-server/application/src/main/resources/templates/notification/space-apply-text.btl +++ b/backend-server/application/src/main/resources/templates/notification/space-apply-text.btl @@ -1,4 +1,4 @@ -${USER_NAME} is applying to join the "${SPACE_NAME}" Space +{{USER_NAME}} is applying to join the "{{SPACE_NAME}}" Space -------------------------------------------------------------------------------- @@ -8,6 +8,6 @@ Click the button below to view the application View application -copyright © 2022-${YEARS} apitable. All rights reserved. +copyright © 2022-{{YEARS}} apitable. All rights reserved. Contact us (https://apitable.com/) \ No newline at end of file diff --git a/backend-server/application/src/main/resources/templates/notification/verification-code-html.btl b/backend-server/application/src/main/resources/templates/notification/verification-code-html.btl index eed82fb9393a56ba9d481d44f009e189d1a8008e..d3853b8185391fc5a0b95956aea42452c7e3d475 100644 --- a/backend-server/application/src/main/resources/templates/notification/verification-code-html.btl +++ b/backend-server/application/src/main/resources/templates/notification/verification-code-html.btl @@ -46,7 +46,7 @@ -

    - ${VERIFICATION_CODE}

    + {{VERIFICATION_CODE}} @@ -227,7 +227,7 @@

    - ©2022-${YEARS} apitable. All rights reserved.

    + ©2022-{{YEARS}} apitable. All rights reserved.

    diff --git a/backend-server/application/src/main/resources/templates/notification/verification-code-text.btl b/backend-server/application/src/main/resources/templates/notification/verification-code-text.btl index d05722270f706cc5d4c5e0d3663590e440993374..abdf17c13c1d240e34bef29fb10c2774da363e73 100644 --- a/backend-server/application/src/main/resources/templates/notification/verification-code-text.btl +++ b/backend-server/application/src/main/resources/templates/notification/verification-code-text.btl @@ -4,11 +4,11 @@ Account Email Verification You're verifying your email address to log into APITtable. The verification code is as follows: -${VERIFICATION_CODE} +{{VERIFICATION_CODE}} The code will become invalid after 15 minutes. If you didn't recently attempt to verify your email address, you can ignore this email. -copyright © 2022-${YEARS} apitable. All rights reserved. +copyright © 2022-{{YEARS}} apitable. All rights reserved. Contact us (https://apitable.com/) \ No newline at end of file diff --git a/backend-server/application/src/test/java/com/apitable/AbstractIntegrationTest.java b/backend-server/application/src/test/java/com/apitable/AbstractIntegrationTest.java index 61008d5f221bad3b3ad3b50a22e30f39e3a394dd..d0fbf138891ac53efb1d301b489d571826c3a076 100644 --- a/backend-server/application/src/test/java/com/apitable/AbstractIntegrationTest.java +++ b/backend-server/application/src/test/java/com/apitable/AbstractIntegrationTest.java @@ -43,6 +43,7 @@ import com.apitable.organization.service.ITeamMemberRelService; import com.apitable.organization.service.ITeamService; import com.apitable.shared.clock.MockClock; import com.apitable.shared.clock.spring.ClockManager; +import com.apitable.shared.config.ServerConfig; import com.apitable.shared.config.initializers.EnterpriseEnvironmentInitializers; import com.apitable.shared.holder.UserHolder; import com.apitable.shared.util.IdUtil; @@ -74,11 +75,6 @@ import org.springframework.test.context.TestPropertySource; }, properties = { "TEST_ENABLED=true" }) public abstract class AbstractIntegrationTest extends TestSuiteWithDB { - /** - * using east 8 timezone for testing - */ - protected static final ZoneOffset testTimeZone = ZoneOffset.ofHours(8); - @Autowired protected JdbcTemplate jdbcTemplate; @@ -88,6 +84,9 @@ public abstract class AbstractIntegrationTest extends TestSuiteWithDB { @Autowired protected RedisTemplate redisTemplate; + @Autowired + protected ServerConfig serverConfig; + @Autowired protected IAuthService iAuthService; @@ -171,6 +170,10 @@ public abstract class AbstractIntegrationTest extends TestSuiteWithDB { return ClockManager.me().getMockClock(); } + protected ZoneOffset getTestTimeZone() { + return serverConfig.getTimeZone(); + } + protected UserEntity createUserRandom() { return createUserWithEmail(IdWorker.getIdStr() + "@apitable.com"); } diff --git a/backend-server/application/src/test/java/com/apitable/auth/service/impl/AuthServiceImplTest.java b/backend-server/application/src/test/java/com/apitable/auth/service/impl/AuthServiceImplTest.java index 241752c4572b39ba03a73b147175c2aab8bc0720..af41471cc3973674c135f920e24e3ab739fd8e21 100644 --- a/backend-server/application/src/test/java/com/apitable/auth/service/impl/AuthServiceImplTest.java +++ b/backend-server/application/src/test/java/com/apitable/auth/service/impl/AuthServiceImplTest.java @@ -18,11 +18,7 @@ package com.apitable.auth.service.impl; -import java.util.concurrent.atomic.AtomicReference; - import cn.hutool.core.util.IdUtil; -import org.junit.jupiter.api.Test; - import com.apitable.AbstractIntegrationTest; import com.apitable.auth.enums.LoginType; import com.apitable.auth.ro.LoginRo; @@ -36,12 +32,12 @@ import com.apitable.shared.captcha.sms.SmsValidateCodeProcessor; import com.apitable.shared.context.SessionContext; import com.apitable.user.entity.UserEntity; import com.apitable.user.vo.UserInfoVo; - +import org.junit.jupiter.api.Test; import org.springframework.boot.test.mock.mockito.MockBean; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.assertj.core.api.Assertions.assertThatNoException; +import java.util.concurrent.atomic.AtomicReference; + +import static org.assertj.core.api.Assertions.*; import static org.mockito.BDDMockito.given; public class AuthServiceImplTest extends AbstractIntegrationTest { @@ -59,7 +55,7 @@ public class AuthServiceImplTest extends AbstractIntegrationTest { // The mobile phone number does not exist, try to log in, do not automatically register LoginRo loginRo = new LoginRo(); loginRo.setAreaCode("+86"); - loginRo.setUsername("13631619061"); + loginRo.setUsername("13633333333"); loginRo.setType(LoginType.PASSWORD); loginRo.setCredential("qwer1234"); assertThatCode(() -> iAuthService.loginUsingPassword(loginRo)).isInstanceOf(BusinessException.class); @@ -70,7 +66,7 @@ public class AuthServiceImplTest extends AbstractIntegrationTest { // The phone number but the password is wrong, try to log in, not automatically registered LoginRo loginRo = new LoginRo(); loginRo.setAreaCode("+86"); - loginRo.setUsername("13631619061"); + loginRo.setUsername("13633333333"); loginRo.setType(LoginType.PASSWORD); loginRo.setCredential("qwer1234"); assertThatCode(() -> iAuthService.loginUsingPassword(loginRo)).isInstanceOf(BusinessException.class); @@ -133,7 +129,7 @@ public class AuthServiceImplTest extends AbstractIntegrationTest { @Test public void testLoginUsingSmsCodeWithMobilePhoneNotExistAutoRegister() { String areaCode = "+86"; - String mobile = "13631619061"; + String mobile = "13633333333"; // Prepare verification code String validCode = sendLoginSmsCode(areaCode, mobile); @@ -156,11 +152,11 @@ public class AuthServiceImplTest extends AbstractIntegrationTest { UserEntity user = new UserEntity(); user.setUuid(IdUtil.fastSimpleUUID()); user.setCode("+86"); - user.setMobilePhone("13631619061"); + user.setMobilePhone("13633333333"); iUserService.save(user); String areaCode = "+86"; - String mobile = "13631619061"; + String mobile = "13633333333"; // Prepare verification code String validCode = sendLoginSmsCode(areaCode, mobile); diff --git a/backend-server/application/src/test/java/com/apitable/organization/service/impl/MemberServiceImplTest.java b/backend-server/application/src/test/java/com/apitable/organization/service/impl/MemberServiceImplTest.java index e99b8fa77d6ed7d84b3bd61316b098d138911f2e..d01b76f9dcb9def2dbb6b69b19e01cf84ce87595 100644 --- a/backend-server/application/src/test/java/com/apitable/organization/service/impl/MemberServiceImplTest.java +++ b/backend-server/application/src/test/java/com/apitable/organization/service/impl/MemberServiceImplTest.java @@ -18,16 +18,15 @@ package com.apitable.organization.service.impl; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Test; - import com.apitable.AbstractIntegrationTest; -import com.apitable.organization.enums.UserSpaceStatus; import com.apitable.mock.bean.MockUserSpace; import com.apitable.organization.entity.MemberEntity; +import com.apitable.organization.enums.UserSpaceStatus; import com.apitable.user.entity.UserEntity; +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.List; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.util.Lists.list; @@ -42,11 +41,11 @@ public class MemberServiceImplTest extends AbstractIntegrationTest { public void testInvitationWithoutExistUser() { MockUserSpace mockUserSpace = createSingleUserAndSpace(); - List emails = list("shawndgh@163.com"); + List emails = list("test@apitable.com"); iMemberService.emailInvitation(mockUserSpace.getUserId(), mockUserSpace.getSpaceId(), emails); // check this member should join this space again - MemberEntity member = iMemberService.getBySpaceIdAndEmail(mockUserSpace.getSpaceId(), "shawndgh@163.com"); + MemberEntity member = iMemberService.getBySpaceIdAndEmail(mockUserSpace.getSpaceId(), "test@apitable.com"); assertThat(member).isNotNull(); assertThat(member.getIsActive()).isNotNull().isFalse(); assertThat(member.getIsPoint()).isNotNull().isTrue(); diff --git a/backend-server/application/src/test/java/com/apitable/shared/clock/TestClockUtil.java b/backend-server/application/src/test/java/com/apitable/shared/clock/TestClockUtil.java index 7d16406d1eb80c6c738c85bda65b42d0400397da..2bbce095504146fb5386e387563263b27045b4e8 100644 --- a/backend-server/application/src/test/java/com/apitable/shared/clock/TestClockUtil.java +++ b/backend-server/application/src/test/java/com/apitable/shared/clock/TestClockUtil.java @@ -18,13 +18,13 @@ package com.apitable.shared.clock; +import org.junit.jupiter.api.Test; + import java.time.LocalDate; import java.time.LocalTime; import java.time.OffsetDateTime; import java.time.ZoneOffset; -import org.junit.jupiter.api.Test; - import static java.time.ZoneOffset.UTC; import static org.assertj.core.api.Assertions.assertThat; diff --git a/backend-server/application/src/test/java/com/apitable/shared/clock/spring/ClockManagerTest.java b/backend-server/application/src/test/java/com/apitable/shared/clock/spring/ClockManagerTest.java index 143e17c4016d63cb7df74f0decd9bc6f6fcf235e..7a0f34c44ccc46ec2abb414e37c0036998526fd8 100644 --- a/backend-server/application/src/test/java/com/apitable/shared/clock/spring/ClockManagerTest.java +++ b/backend-server/application/src/test/java/com/apitable/shared/clock/spring/ClockManagerTest.java @@ -27,9 +27,12 @@ import org.junit.jupiter.api.Test; import com.apitable.AbstractIntegrationTest; +import org.springframework.test.context.TestPropertySource; + import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatNoException; +@TestPropertySource(properties = { "DEFAULT_TIME_ZONE=UTC" }) public class ClockManagerTest extends AbstractIntegrationTest { @Test @@ -49,21 +52,29 @@ public class ClockManagerTest extends AbstractIntegrationTest { @Test public void testGetLocalDateNow() { - final OffsetDateTime initialCreateDate = OffsetDateTime.of(2022, 2, 1, 19, 10, 30, 0, testTimeZone); + final OffsetDateTime initialCreateDate = OffsetDateTime.of(2022, 2, 1, 19, 10, 30, 0, getTestTimeZone()); getClock().setTime(initialCreateDate); LocalDate date = ClockManager.me().getLocalDateNow(); - assertThat(date).isAfterOrEqualTo(LocalDate.of(2022, 2, 1)); + LocalDate expectTime = LocalDate.of(2022, 2, 1); + + assertThat(date).isAfterOrEqualTo(expectTime); } @Test public void testGetLocalDateTimeNow() { - final OffsetDateTime initialCreateDate = OffsetDateTime.of(2022, 2, 1, 19, 10, 30, 0, testTimeZone); + final OffsetDateTime initialCreateDate = OffsetDateTime.of(2022, 2, 1, 19, 10, 30, 0, getTestTimeZone()); getClock().setTime(initialCreateDate); LocalDateTime dateTime = ClockManager.me().getLocalDateTimeNow(); - assertThat(dateTime).isEqualToIgnoringSeconds(LocalDateTime.of(2022, 2, 1, 19, 10, 30)); + System.out.println(dateTime); + + LocalDateTime expectTime = LocalDateTime.of(2022, 2, 1, 19, 10, 30, 0); + + System.out.println(expectTime); + + assertThat(dateTime).isEqualToIgnoringSeconds(expectTime); } } diff --git a/backend-server/application/src/test/java/com/apitable/shared/config/ServerConfigTest.java b/backend-server/application/src/test/java/com/apitable/shared/config/ServerConfigTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3cd7852e5569cccfb32b4fb385e2181e9c18c80e --- /dev/null +++ b/backend-server/application/src/test/java/com/apitable/shared/config/ServerConfigTest.java @@ -0,0 +1,24 @@ +package com.apitable.shared.config; + +import com.apitable.AbstractIntegrationTest; +import org.junit.jupiter.api.Test; + +import java.time.ZoneId; +import java.time.ZoneOffset; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ServerConfigTest extends AbstractIntegrationTest { + + @Test + void testDefaultTimeZoneId() { + ZoneId zoneId = serverConfig.getTimeZoneId(); + assertThat(zoneId).isEqualTo(ZoneId.of("UTC")); + } + + @Test + void testDefaultTimeZone() { + ZoneOffset timeZone = serverConfig.getTimeZone(); + assertThat(timeZone).isEqualTo(ZoneOffset.UTC); + } +} diff --git a/backend-server/application/src/test/java/com/apitable/space/mapper/SpaceMapperTest.java b/backend-server/application/src/test/java/com/apitable/space/mapper/SpaceMapperTest.java index 1e721b7b70bbf5b92fb214fda1bc3faa9aeeadb6..bdf0e1446f095e473a0672840ce58fc4492e2945 100644 --- a/backend-server/application/src/test/java/com/apitable/space/mapper/SpaceMapperTest.java +++ b/backend-server/application/src/test/java/com/apitable/space/mapper/SpaceMapperTest.java @@ -18,21 +18,19 @@ package com.apitable.space.mapper; -import java.util.List; - import cn.hutool.core.collection.CollUtil; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - import com.apitable.AbstractMyBatisMapperTest; import com.apitable.space.dto.BaseSpaceInfoDTO; import com.apitable.space.dto.SpaceAdminInfoDTO; -import com.apitable.space.vo.SpaceVO; +import com.apitable.space.dto.SpaceDTO; import com.apitable.space.entity.SpaceEntity; - +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.jdbc.Sql; +import java.util.List; + import static org.assertj.core.api.Assertions.assertThat; @Disabled @@ -63,21 +61,21 @@ public class SpaceMapperTest extends AbstractMyBatisMapperTest { } @Test - @Sql({ "/sql/space-data.sql", "/sql/unit-member-data.sql" }) + @Sql({"/sql/space-data.sql", "/sql/unit-member-data.sql"}) void testSelectListByUserId() { - List entities = spaceMapper.selectListByUserId(41L); + List entities = spaceMapper.selectListByUserId(41L); assertThat(entities).isNotEmpty(); } @Test - @Sql({ "/sql/space-data.sql", "/sql/unit-member-data.sql" }) + @Sql({"/sql/space-data.sql", "/sql/unit-member-data.sql"}) void testGetAdminSpaceCount() { Integer count = spaceMapper.getAdminSpaceCount(41L); assertThat(count).isEqualTo(1); } @Test - @Sql({ "/sql/space-data.sql", "/sql/unit-member-data.sql", "/sql/user-data.sql"}) + @Sql({"/sql/space-data.sql", "/sql/unit-member-data.sql", "/sql/user-data.sql"}) void testSelectAdminInfoDto() { SpaceAdminInfoDTO entity = spaceMapper.selectAdminInfoDto("spc41"); assertThat(entity).isNotNull(); @@ -105,7 +103,7 @@ public class SpaceMapperTest extends AbstractMyBatisMapperTest { } @Test - @Sql({ "/sql/space-data.sql", "/sql/unit-member-data.sql" }) + @Sql({"/sql/space-data.sql", "/sql/unit-member-data.sql"}) void testSelectSpaceIdByUserIdAndName() { String id = spaceMapper.selectSpaceIdByUserIdAndName(41L, "41"); assertThat(id).isEqualTo("spc41"); @@ -119,7 +117,7 @@ public class SpaceMapperTest extends AbstractMyBatisMapperTest { } @Test - @Sql({ "/sql/space-data.sql", "/sql/unit-member-data.sql" }) + @Sql({"/sql/space-data.sql", "/sql/unit-member-data.sql"}) void testSelectByUserId() { List entities = spaceMapper.selectByUserId(41L); assertThat(entities).isNotEmpty(); diff --git a/backend-server/application/src/test/java/com/apitable/space/service/impl/SpaceServiceImplTest.java b/backend-server/application/src/test/java/com/apitable/space/service/impl/SpaceServiceImplTest.java index f68f3f85242578d3582a0d0655fef061ec1c2aca..61bd3b4cacf5696422255c46cd810949b15e9b34 100644 --- a/backend-server/application/src/test/java/com/apitable/space/service/impl/SpaceServiceImplTest.java +++ b/backend-server/application/src/test/java/com/apitable/space/service/impl/SpaceServiceImplTest.java @@ -21,6 +21,7 @@ package com.apitable.space.service.impl; import java.time.LocalDate; import java.util.List; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import com.apitable.AbstractIntegrationTest; @@ -48,6 +49,7 @@ import com.apitable.interfaces.billing.model.SubscriptionFeatures.SolidFeatures. import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.AllowApplyJoin; import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.AllowCopyData; import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.AllowDownload; +import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.AllowEmbed; import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.AllowExport; import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.AllowInvitation; import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.AllowShare; @@ -59,7 +61,9 @@ import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatu import com.apitable.interfaces.billing.model.SubscriptionFeatures.SubscribeFeatures.Watermark; import com.apitable.interfaces.billing.model.SubscriptionInfo; import com.apitable.mock.bean.MockUserSpace; +import com.apitable.space.dto.GetSpaceListFilterCondition; import com.apitable.space.dto.SpaceCapacityUsedInfo; +import com.apitable.space.vo.SpaceVO; import com.apitable.user.entity.UserEntity; import org.springframework.boot.test.mock.mockito.MockBean; @@ -67,11 +71,30 @@ import org.springframework.boot.test.mock.mockito.MockBean; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; +@Disabled public class SpaceServiceImplTest extends AbstractIntegrationTest { @MockBean private EntitlementServiceFacade entitlementServiceFacade; + @Test + void getSpaceListWithAll() { + MockUserSpace userSpace = createSingleUserAndSpace(); + GetSpaceListFilterCondition condition = new GetSpaceListFilterCondition(); + condition.setManageable(false); + List spaceVOList = iSpaceService.getSpaceListByUserId(userSpace.getUserId(), condition); + assertThat(spaceVOList).isNotEmpty().hasSize(1); + } + + @Test + void getSpaceListWithAdmin() { + MockUserSpace userSpace = createSingleUserAndSpace(); + GetSpaceListFilterCondition condition = new GetSpaceListFilterCondition(); + condition.setManageable(true); + List spaceVOList = iSpaceService.getSpaceListByUserId(userSpace.getUserId(), condition); + assertThat(spaceVOList).isNotEmpty().hasSize(1); + } + @Test void givenExitMemberWhenCheckUserInSpaceWhenSuccess() { MockUserSpace userSpace = createSingleUserAndSpace(); @@ -314,6 +337,11 @@ public class SpaceServiceImplTest extends AbstractIntegrationTest { return null; } + @Override + public AllowEmbed getAllowEmbed() { + return null; + } + @Override public ShowMobileNumber getShowMobileNumber() { return null; diff --git a/backend-server/application/src/test/java/com/apitable/user/mapper/UserHistoryMapperTest.java b/backend-server/application/src/test/java/com/apitable/user/mapper/UserHistoryMapperTest.java index 4a10496e8e65c7470fb54ff70fab4b56bc6df64c..e4cf1937c7933157bd47e13941b971daacd441aa 100644 --- a/backend-server/application/src/test/java/com/apitable/user/mapper/UserHistoryMapperTest.java +++ b/backend-server/application/src/test/java/com/apitable/user/mapper/UserHistoryMapperTest.java @@ -52,7 +52,8 @@ public class UserHistoryMapperTest extends AbstractMyBatisMapperTest { @Test @Sql("/sql/user-history-data.sql") void testSelectUserHistoryDtos() { - List entities = userHistoryMapper.selectUserHistoryDtos(LocalDateTime.of(2020, 1, 1, 0, 0), LocalDateTime.now(), 1); + List entities = userHistoryMapper.selectUserHistoryDtos(LocalDateTime.of(2020, 10, 1, 0, 0), + LocalDateTime.of(2022, 10, 30, 23, 59), 1); assertThat(entities).isNotEmpty(); } diff --git a/backend-server/application/src/test/java/com/apitable/user/service/impl/UserServiceImplTest.java b/backend-server/application/src/test/java/com/apitable/user/service/impl/UserServiceImplTest.java index 3ce2372be3b0a6ee22521c86f8fab20b7c21fed2..246caf0829bdd3a3ba89aa20b38c561e65cc0131 100644 --- a/backend-server/application/src/test/java/com/apitable/user/service/impl/UserServiceImplTest.java +++ b/backend-server/application/src/test/java/com/apitable/user/service/impl/UserServiceImplTest.java @@ -18,9 +18,60 @@ package com.apitable.user.service.impl; +import cn.hutool.core.collection.CollectionUtil; import com.apitable.AbstractIntegrationTest; +import com.apitable.mock.bean.MockUserSpace; +import com.apitable.user.entity.UserEntity; +import com.apitable.user.mapper.UserMapper; +import com.apitable.user.ro.UserOpRo; +import org.junit.jupiter.api.Test; + +import javax.annotation.Resource; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; public class UserServiceImplTest extends AbstractIntegrationTest { + @Resource + private UserMapper userMapper; + + @Test + public void testEditUserAvatar(){ + MockUserSpace userSpace = createSingleUserAndSpace(); + + UserOpRo param = new UserOpRo(); + param.setAvatar("public/2023/01/04/11c74fbfc96541b3a2ffd3ee8217dcc0"); + param.setAvatarColor(null); + + iUserService.edit(userSpace.getUserId(), param); + List users = userMapper.selectByIds(CollectionUtil.newArrayList(userSpace.getUserId())); + assertThat(users.get(0).getAvatar()).isEqualTo("public/2023/01/04/11c74fbfc96541b3a2ffd3ee8217dcc0"); + } + + @Test + public void testEditUserAvtarColor(){ + MockUserSpace userSpace = createSingleUserAndSpace(); + + UserOpRo param = new UserOpRo(); + param.setAvatar(null); + param.setAvatarColor(5); + + iUserService.edit(userSpace.getUserId(), param); + List users = userMapper.selectByIds(CollectionUtil.newArrayList(userSpace.getUserId())); + assertThat(users.get(0).getAvatar()).isNull(); + } + + @Test + public void testEditUserNickName(){ + MockUserSpace userSpace = createSingleUserAndSpace(); + + UserOpRo param = new UserOpRo(); + param.setInit(false); + param.setNickName("testName"); + iUserService.edit(userSpace.getUserId(), param); + List users = userMapper.selectByIds(CollectionUtil.newArrayList(userSpace.getUserId())); + assertThat(users.get(0).getNickName()).isEqualTo("testName"); + } } diff --git a/backend-server/application/src/test/resources/sql/user-history-data.sql b/backend-server/application/src/test/resources/sql/user-history-data.sql index 6fa16a8d3703126ab70845e40eb52953f9dc9fe9..9b9211d55abecf3d357ec4bb6f5beb062e42ae5c 100644 --- a/backend-server/application/src/test/resources/sql/user-history-data.sql +++ b/backend-server/application/src/test/resources/sql/user-history-data.sql @@ -15,5 +15,5 @@ -- along with this program. If not, see . INSERT INTO `user_history` (`id`, `user_id`, `uuid`, `nick_name`, `user_status`, - `created_by`, `updated_by`) -VALUES (41, 41, 'uuid-41', 'apitable body', 1, 41, 41); \ No newline at end of file + `created_by`, `updated_by`, `created_at`, `updated_at`) +VALUES (41, 41, 'uuid-41', 'apitable body', 1, 41, 41, '2022-10-01', '2022-10-30'); \ No newline at end of file diff --git a/backend-server/build.gradle b/backend-server/build.gradle index 6c1e9e45964d45063d8a35de69db2bc9021f356f..23764653cf7620238fa51a0e04f5b9525c95ad6b 100644 --- a/backend-server/build.gradle +++ b/backend-server/build.gradle @@ -29,6 +29,7 @@ subprojects { mavenBom("com.amazonaws:aws-java-sdk-bom:${aws_java_sdk_version}") mavenBom("io.grpc:grpc-bom:${io_grpc_version}") mavenBom("com.google.protobuf:protobuf-bom:${protobuf_java_version}") + mavenBom("org.springframework.cloud:spring-cloud-sleuth-otel-dependencies:${spring_cloud_sleuth_otel_version}") } } diff --git a/backend-server/gradle.properties b/backend-server/gradle.properties index 02ec48ba0d0fac0559b08e253a6f26bfc747eab9..b84d4776ab091d1990602b9023444fa0f95fdb68 100644 --- a/backend-server/gradle.properties +++ b/backend-server/gradle.properties @@ -1,14 +1,16 @@ org.gradle.caching=false +org.gradle.daemon=true org.gradle.parallel=true -org.gradle.jvmargs=-Xmx5120m -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +org.gradle.jvmargs=-Xmx6144m -XX:MaxPermSize=4096m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 org.gradle.warning.mode=all org.gradle.configureondemand=true org.gradle.console=rich # Custom Properties -revision=0.16.0 +revision=0.17.0 # Dependency versions spring_boot_version=2.7.6 spring_cloud_version=2021.0.5 +spring_cloud_sleuth_otel_version=1.1.0 aws_java_sdk_version=1.12.285 mybatis_plus_version=3.5.1 spring_boot_admin_version=2.5.4 diff --git a/backend-server/lib.gradle b/backend-server/lib.gradle index 22d02c2d01b77418e502bdbc2c1127eb6746ae30..aad37bf43ac2654c33167f5c34571609726ae5d7 100644 --- a/backend-server/lib.gradle +++ b/backend-server/lib.gradle @@ -25,7 +25,9 @@ ext { 'spring-test-starter' : 'org.springframework.boot:spring-boot-starter-test', // Spring Cloud Dependencies 'spring-cloud-starter-sleuth' : "org.springframework.cloud:spring-cloud-starter-sleuth", - 'spring-cloud-sleuth-zipkin' : "org.springframework.cloud:spring-cloud-sleuth-zipkin", + // Spring Cloud Sleuth OpenTelemetry + 'spring-cloud-sleuth-otel' : "org.springframework.cloud:spring-cloud-sleuth-otel-autoconfigure", + 'opentelemetry-exporter-jaeger' : "io.opentelemetry:opentelemetry-exporter-jaeger", // Spring Session Dependencies 'spring-session-redis' : 'org.springframework.session:spring-session-data-redis', // Spring Data Dependencies diff --git a/backend-server/shared/core/src/main/java/com/apitable/core/constants/RedisConstants.java b/backend-server/shared/core/src/main/java/com/apitable/core/constants/RedisConstants.java index b6d89551673f624dded108e0a8b54bfe370054e1..2b73333623da1687dbb786f28d80eab893f38889 100644 --- a/backend-server/shared/core/src/main/java/com/apitable/core/constants/RedisConstants.java +++ b/backend-server/shared/core/src/main/java/com/apitable/core/constants/RedisConstants.java @@ -29,8 +29,6 @@ import cn.hutool.core.util.StrUtil; */ public class RedisConstants { - public static final String REDIS_ENV = "redis_env"; - /** * login user information */ diff --git a/backend-server/shared/starters/oss/src/main/java/com/apitable/starter/oss/autoconfigure/QiniuTemporaryAutoConfiguration.java b/backend-server/shared/starters/oss/src/main/java/com/apitable/starter/oss/autoconfigure/QiniuTemporaryAutoConfiguration.java deleted file mode 100644 index 2e21a48498aa112fc2502e2be1091aa2ad2ed153..0000000000000000000000000000000000000000 --- a/backend-server/shared/starters/oss/src/main/java/com/apitable/starter/oss/autoconfigure/QiniuTemporaryAutoConfiguration.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * APITable - * Copyright (C) 2022 APITable Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.apitable.starter.oss.autoconfigure; - -import java.util.Optional; - -import com.qiniu.util.Auth; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.apitable.starter.oss.autoconfigure.OssProperties.Callback; -import com.apitable.starter.oss.autoconfigure.OssProperties.Qiniu; -import com.apitable.starter.oss.core.qiniu.QiniuOssClientRequestFactory; -import com.apitable.starter.oss.core.qiniu.QiniuTemporaryClientTemplate; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - *

    - * Qiniu cloud temporary object storage automatic configuration - * (temporary use, widget-cli update cancels the use of multi-file Key upload and then off the shelf) - *

    - * - * @author Chambers - */ -@Configuration(proxyBeanMethods = false) -@EnableConfigurationProperties(OssProperties.class) -@ConditionalOnClass(QiniuTemporaryClientTemplate.class) -@ConditionalOnProperty(prefix = "starter.oss.qiniu", name = "access-key") -public class QiniuTemporaryAutoConfiguration extends OssConnectionConfiguration { - - private static final Logger LOGGER = LoggerFactory.getLogger(QiniuTemporaryAutoConfiguration.class); - - QiniuTemporaryAutoConfiguration(OssProperties properties) { - super(properties); - } - - @Bean - @ConditionalOnMissingBean - public QiniuTemporaryClientTemplate qiniuTemporaryClientTemplate() { - LOGGER.info("Qiniu cloud temporary object storage automatic configuration."); - Qiniu qiniu = getProperties().getQiniu(); - Auth auth = Auth.create(qiniu.getAccessKey(), qiniu.getSecretKey()); - Callback callback = Optional.ofNullable(qiniu.getCallback()).orElseGet(Callback::new); - QiniuOssClientRequestFactory factory = new QiniuOssClientRequestFactory(auth, qiniu.getRegion(), qiniu.getDownloadDomain(), callback.getUrl(), callback.getBodyType(), qiniu.getUploadUrl()); - return new QiniuTemporaryClientTemplate(factory); - } -} diff --git a/backend-server/shared/starters/oss/src/main/resources/META-INF/spring.factories b/backend-server/shared/starters/oss/src/main/resources/META-INF/spring.factories index eb882c4f954296453339ac2714c178b7b2c12bf4..8f4b37f523a61d741fb440b9966f98662b456fa9 100644 --- a/backend-server/shared/starters/oss/src/main/resources/META-INF/spring.factories +++ b/backend-server/shared/starters/oss/src/main/resources/META-INF/spring.factories @@ -1,3 +1,2 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -com.apitable.starter.oss.autoconfigure.OssAutoConfiguration,\ -com.apitable.starter.oss.autoconfigure.QiniuTemporaryAutoConfiguration \ No newline at end of file +com.apitable.starter.oss.autoconfigure.OssAutoConfiguration \ No newline at end of file diff --git a/crowdin.yml b/crowdin.yml new file mode 100644 index 0000000000000000000000000000000000000000..9c63595c91244d05f0f4aa5b4621d5b7f6d55959 --- /dev/null +++ b/crowdin.yml @@ -0,0 +1,5 @@ +files: + - source: /README.md + translation: /docs/readme/%locale%/%original_file_name% + - source: /docs/contribute/* + translation: /docs/readme/%locale%/docs/contribute/%original_file_name% diff --git a/docker-compose.build.yaml b/docker-compose.build.yaml index 43688b4f15ffce7960af2404eac7443fe8b77bfb..42e72c8081e0af9b9a7dfbbb8b80ace0766c5815 100644 --- a/docker-compose.build.yaml +++ b/docker-compose.build.yaml @@ -1,3 +1,5 @@ +version: '3' + services: init-db: image: ${DISTRO:-apitable}/init-db:${TAG:-latest} @@ -26,4 +28,4 @@ services: image: ${DISTRO:-apitable}/socket-server:${TAG:-latest} build: context: . - dockerfile: packaging/Dockerfile.socket-server \ No newline at end of file + dockerfile: packaging/Dockerfile.socket-server diff --git a/docker-compose.dataenv.yaml b/docker-compose.dataenv.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d263f9c44625b33ebaf2e61098840a69efc81f9d --- /dev/null +++ b/docker-compose.dataenv.yaml @@ -0,0 +1,20 @@ +version: '3' + +services: + minio: + ports: + - "9000:9000" + + redis: + ports: + - "6379:6379" + + mysql: + ports: + - "3306:3306" + + rabbitmq: + ports: + - "5671:5671" + - "5672:5672" + - "15672:15672" diff --git a/docker-compose.devenv.yaml b/docker-compose.devenv.yaml index 03f0b18b17cf9faa294264e4e51d7f8d388df0e5..79da88f125868938098e2f78c4879615c2deb794 100644 --- a/docker-compose.devenv.yaml +++ b/docker-compose.devenv.yaml @@ -1,3 +1,5 @@ +version: '3' + services: backend-server: @@ -14,7 +16,6 @@ services: - ./backend-server:/devenv environment: - HOME=/tmp - - OSS_TYPE=aws networks: - apitable command: sh -c "./gradlew build -x test && java -jar application/build/libs/application.jar" diff --git a/docker-compose.unit-test.yml b/docker-compose.unit-test.yaml similarity index 83% rename from docker-compose.unit-test.yml rename to docker-compose.unit-test.yaml index 0557573d2dbc9cfccb4a34d9f4d464c9e5c430bd..de16d258d26d67e9471e596497b6655301e7cb49 100644 --- a/docker-compose.unit-test.yml +++ b/docker-compose.unit-test.yaml @@ -1,10 +1,11 @@ -version: '3.9' +version: '3.4' + services: test-mysql: container_name: test-mysql image: mysql:8.0.28-oracle ports: - - '23306:3306' + - '3306:3306' networks: - unit-test environment: @@ -25,7 +26,7 @@ services: container_name: test-redis image: redis:5.0.7-alpine ports: - - '26379:6379' + - '6379:6379' networks: - unit-test environment: @@ -41,7 +42,7 @@ services: container_name: test-rabbitmq image: rabbitmq:3-management ports: - - '25672:5672' + - '5672:5672' networks: - unit-test environment: @@ -59,23 +60,12 @@ services: test-initdb: container_name: test-initdb - image: ${INITDB_IMAGE:-docker.vika.ltd/vikadata/vika/init-db:latest-alpha} - networks: - - unit-test - environment: - - TZ=Asia/Singapore - - ACTION=update - - DB_HOST=test-mysql - - DB_PORT=3306 - - DB_NAME=apitable_test - - DB_USERNAME=apitable - - DB_PASSWORD=password - - test-initdb-enterprise: - container_name: test-initdb - image: ${INITDB_IMAGE:-docker.vika.ltd/vikadata/vika/init-db-enterprise:latest-alpha} + image: gradle:jdk8 networks: - unit-test + working_dir: /data + volumes: + - ./init-db:/data environment: - TZ=Asia/Singapore - ACTION=update @@ -84,6 +74,8 @@ services: - DB_NAME=apitable_test - DB_USERNAME=apitable - DB_PASSWORD=password + - DATABASE_TABLE_PREFIX=apitable_ + entrypoint: ./gradlew update unit-test-room: container_name: unit-test-room @@ -117,7 +109,6 @@ services: - NODE_OPTIONS=--max-old-space-size=4096 - OSS_HOST=https://s1.vika.cn - OSS_BUCKET=QNY1 - # remove default command,use run unit-test-room (yarn test:ut:room:cov | yarn test:ut:room) unit-test-backend: container_name: unit-test-backend @@ -146,4 +137,4 @@ services: networks: unit-test: - driver: bridge + name: apitable-unit-test diff --git a/docker-compose.ut-backend.yaml b/docker-compose.ut-backend.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8783f40c6fabed16ce6c2e988203a68fdfc2defc --- /dev/null +++ b/docker-compose.ut-backend.yaml @@ -0,0 +1,72 @@ +version: '3' + +services: + test-mysql: + container_name: test-mysql + image: mysql:8.0.28-oracle + networks: + - apitable + ports: + - '3306:3306' + environment: + - MYSQL_DATABASE=apitable_test + - MYSQL_USER=apitable + - MYSQL_PASSWORD=password + - MYSQL_ROOT_PASSWORD=password + command: ['mysqld', '--sql_mode=IGNORE_SPACE,NO_ENGINE_SUBSTITUTION', '--character-set-server=utf8mb4', '--collation-server=utf8mb4_unicode_ci'] + healthcheck: + test: "mysql $$MYSQL_DATABASE -u$$MYSQL_USER -p$$MYSQL_PASSWORD -e 'SELECT 1;'" + interval: 30s + timeout: 10s + retries: 5 + + test-init-db: + container_name: test-init-db + image: gradle:jdk8 + networks: + - apitable + working_dir: /data + volumes: + - ./init-db:/data + environment: + - DB_HOST=test-mysql + - DB_PORT=3306 + - DB_NAME=apitable_test + - DB_USERNAME=apitable + - DB_PASSWORD=password + - DATABASE_TABLE_PREFIX=apitable_ + entrypoint: ./gradlew update + + test-redis: + container_name: test-redis + image: redis:5.0.7-alpine + networks: + - apitable + ports: + - '6379:6379' + healthcheck: + test: ['CMD', 'redis-cli', '--raw', 'incr', 'ping'] + interval: 30s + timeout: 10s + retries: 5 + + test-rabbitmq: + container_name: test-rabbitmq + image: rabbitmq:3-management + networks: + - apitable + ports: + - '5672:5672' + - '15672:15672' + environment: + - RABBITMQ_DEFAULT_USER=apitable + - RABBITMQ_DEFAULT_PASS=password + healthcheck: + test: rabbitmq-diagnostics -q ping + interval: 30s + timeout: 10s + retries: 5 + +networks: + apitable: + driver: bridge \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index e6db7513d293f3922dc0c0a20b5e86f6d0bd0681..18c7ffc99ef0ab8a16b19a738f865855f556f35b 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,64 +1,55 @@ +version: '3' + services: web-server: image: ${IMAGE_REGISTRY}/${IMAGE_WEB_SERVER} - pull_policy: ${IMAGE_POLL_POLICY:-missing} + pull_policy: ${IMAGE_PULL_POLICY:-if_not_present} restart: always expose: - - 8080 + - "8080" env_file: - "${ENV_FILE:-.env}" networks: - apitable - depends_on: - backend-server: - condition: service_healthy imageproxy-server: image: ${IMAGE_REGISTRY}/${IMAGE_IMAGEPROXY_SERVER} - pull_policy: ${IMAGE_POLL_POLICY:-missing} + pull_policy: ${IMAGE_PULL_POLICY:-if_not_present} restart: always expose: - "8080" environment: - TZ=${TIMEZONE} - - BASEURL=${MINIO_ENDPOINT} + - BASEURL=${AWS_ENDPOINT} networks: - apitable backend-server: image: ${IMAGE_REGISTRY}/${IMAGE_BACKEND_SERVER} - pull_policy: ${IMAGE_POLL_POLICY:-missing} + pull_policy: ${IMAGE_PULL_POLICY:-if_not_present} restart: always - ports: - - "8081:8081" env_file: - "${ENV_FILE:-.env}" expose: - "8081" environment: - TZ=${TIMEZONE} - - OSS_TYPE=aws networks: - apitable depends_on: - - mysql - - redis - - socket-server - - minio - - rabbitmq + init-db: + condition: service_completed_successfully healthcheck: test: ["CMD-SHELL", "curl -sS 'http://localhost:8081' || exit 1"] - interval: 30s - timeout: 10s - retries: 10 + interval: 5s + timeout: 5s + start_period: 30s + retries: 60 room-server: image: ${IMAGE_REGISTRY}/${IMAGE_ROOM_SERVER} - pull_policy: ${IMAGE_POLL_POLICY:-missing} + pull_policy: ${IMAGE_PULL_POLICY:-if_not_present} restart: always - #ports: - # - 3333:3333 - # - 3334:3334 expose: - "3333" - "3334" @@ -72,43 +63,13 @@ services: networks: - apitable depends_on: - - redis - - mysql - - scheduler-server: - image: ${IMAGE_REGISTRY}/${IMAGE_ROOM_SERVER} - pull_policy: ${IMAGE_POLL_POLICY:-missing} - restart: always - #ports: - # - 3333:3333 - # - 3334:3334 - expose: - - "3333" - - "3334" - env_file: - - "${ENV_FILE:-.env}" - environment: - - NODE_ENV=${ENV} - - NODE_OPTIONS=--max-old-space-size=2048 --max-http-header-size=80000 - - API_MAX_MODIFY_RECORD_COUNTS=${API_MAX_MODIFY_RECORD_COUNTS:-30} - - INSTANCE_MAX_MEMORY=4096M - - APPLICATION_NAME=SCHEDULE_SERVER - # only one server opened - - ENABLE_SCHED=true - networks: - - apitable - depends_on: - - redis - - mysql + mysql: + condition: service_healthy socket-server: image: ${IMAGE_REGISTRY}/${IMAGE_SOCKET_SERVER} - pull_policy: ${IMAGE_POLL_POLICY:-missing} + pull_policy: ${IMAGE_PULL_POLICY:-if_not_present} restart: always - # ports: - # - 3001:3001 - # - 3002:3002 - # - 3005:3005 expose: - "3001" - "3002" @@ -124,14 +85,11 @@ services: - "${ENV_FILE:-.env}" networks: - apitable - depends_on: - - redis - - rabbitmq - ### Third Party Dockers + # Third Party Dockers gateway: image: ${IMAGE_GATEWAY} - pull_policy: ${IMAGE_POLL_POLICY:-missing} + pull_policy: ${IMAGE_PULL_POLICY:-if_not_present} restart: always ports: - ${NGINX_HTTP_PORT:-80}:80 @@ -143,22 +101,24 @@ services: networks: - apitable depends_on: - - mysql - - minio - - backend-server - - room-server - - socket-server - - imageproxy-server - - web-server + web-server: + condition: service_started + imageproxy-server: + condition: service_started + backend-server: + condition: service_healthy + room-server: + condition: service_started + socket-server: + condition: service_started + init-appdata: + condition: service_completed_successfully minio: image: ${IMAGE_MINIO} - pull_policy: ${IMAGE_POLL_POLICY:-missing} + pull_policy: ${IMAGE_PULL_POLICY:-if_not_present} restart: always - user: "${UID}:${GID}" command: server /data - ports: - - "9000:9000" expose: - "9000" volumes: @@ -172,15 +132,14 @@ services: - apitable healthcheck: test: ["CMD-SHELL", "curl -sS 'http://localhost:9000' || exit 1"] - interval: 30s - timeout: 10s - retries: 5 + interval: 5s + timeout: 5s + retries: 30 redis: image: ${IMAGE_REDIS} - pull_policy: ${IMAGE_POLL_POLICY:-missing} + pull_policy: ${IMAGE_PULL_POLICY:-if_not_present} restart: always - user: "${UID}:${GID}" command: [ "redis-server", @@ -189,8 +148,6 @@ services: "--requirepass", "${REDIS_PASSWORD}", ] - ports: - - "6379:6379" expose: - "6379" volumes: @@ -202,13 +159,10 @@ services: mysql: image: ${IMAGE_MYSQL} - pull_policy: ${IMAGE_POLL_POLICY:-missing} + pull_policy: ${IMAGE_PULL_POLICY:-if_not_present} restart: always - user: "${UID}:${GID}" volumes: - ${DATA_PATH}/.data/mysql:/var/lib/mysql - ports: - - 3306:3306 expose: - "3306" environment: @@ -222,39 +176,33 @@ services: --lower_case_table_names=2 healthcheck: test: "mysql ${MYSQL_DATABASE} -u${MYSQL_USERNAME} -p${MYSQL_PASSWORD} -e 'SELECT 1;'" - interval: 30s - timeout: 10s - retries: 10 + interval: 5s + timeout: 5s + start_period: 30s + retries: 60 networks: - apitable rabbitmq: image: ${IMAGE_RABBITMQ} - pull_policy: ${IMAGE_POLL_POLICY:-missing} + pull_policy: ${IMAGE_PULL_POLICY:-if_not_present} restart: always - user: "${UID}:${GID}" volumes: - ${DATA_PATH}/.data/rabbitmq:/var/lib/rabbitmq - ports: - - "5671:5671" - - "5672:5672" - - "15672:15672" expose: - "5671" - "5672" - "15672" environment: - - RABBITMQ_USERNAME=${RABBITMQ_USERNAME} - - RABBITMQ_PASSWORD=${RABBITMQ_PASSWORD} - - RABBITMQ_DEFAULT_USER=${RABBITMQ_DEFAULT_USER} - - RABBITMQ_DEFAULT_PASS=${RABBITMQ_DEFAULT_PASS} + - RABBITMQ_DEFAULT_USER=${RABBITMQ_USERNAME} + - RABBITMQ_DEFAULT_PASS=${RABBITMQ_PASSWORD} networks: - apitable - # #### init data - init-db: + # init data + pre-init-db: image: ${IMAGE_REGISTRY}/${IMAGE_INIT_DB} - pull_policy: ${IMAGE_POLL_POLICY:-missing} + pull_policy: ${IMAGE_PULL_POLICY:-if_not_present} environment: - TZ=${TIMEZONE} - DB_HOST=${MYSQL_HOST} @@ -263,50 +211,44 @@ services: - DB_USERNAME=${MYSQL_USERNAME} - DB_PASSWORD=${MYSQL_PASSWORD} - DATABASE_TABLE_PREFIX=${DATABASE_TABLE_PREFIX} - - ACTION=update + - ACTION=releaseLocks networks: - apitable depends_on: mysql: condition: service_healthy - - init-data-mysql: - image: ${IMAGE_REGISTRY}/${IMAGE_INIT_DATA_MYSQL} - pull_policy: ${IMAGE_POLL_POLICY:-missing} + + init-db: + image: ${IMAGE_REGISTRY}/${IMAGE_INIT_DB} + pull_policy: ${IMAGE_PULL_POLICY:-if_not_present} environment: - TZ=${TIMEZONE} - - MYSQL_HOST=${MYSQL_HOST} - - MYSQL_PORT=${MYSQL_PORT} - - MYSQL_USERNAME=${MYSQL_USERNAME} - - MYSQL_PASSWORD=${MYSQL_PASSWORD} - - MYSQL_DATABASE=${MYSQL_DATABASE} - env_file: - - "${ENV_FILE:-.env}" + - DB_HOST=${MYSQL_HOST} + - DB_PORT=${MYSQL_PORT} + - DB_NAME=${MYSQL_DATABASE} + - DB_USERNAME=${MYSQL_USERNAME} + - DB_PASSWORD=${MYSQL_PASSWORD} + - DATABASE_TABLE_PREFIX=${DATABASE_TABLE_PREFIX} + - ACTION=update networks: - apitable depends_on: - mysql: - condition: service_healthy - init-db: + pre-init-db: condition: service_completed_successfully - command: - - /bin/bash - - -c - - | - sh run.sh - init-data-minio: - image: ${IMAGE_REGISTRY}/${IMAGE_INIT_DATA_MINIO} - pull_policy: ${IMAGE_POLL_POLICY:-missing} - environment: - - TZ=${TIMEZONE} + # init-appdata + init-appdata: + image: ${IMAGE_REGISTRY}/${IMAGE_INIT_APPDATA} + pull_policy: ${IMAGE_PULL_POLICY:-if_not_present} env_file: - "${ENV_FILE:-.env}" networks: - apitable depends_on: - minio: + mysql: condition: service_healthy + init-db: + condition: service_completed_successfully networks: apitable: diff --git a/docs/contribute/developer-guide.md b/docs/contribute/developer-guide.md index 4d990b8aa0c1addc3a74eb54c5bb730ef409c5ba..1da4113ebfa4217b3ca9555b7fae204b38c77fe5 100644 --- a/docs/contribute/developer-guide.md +++ b/docs/contribute/developer-guide.md @@ -8,13 +8,13 @@ Make sure you have the following dependencies and programming languages installe - `git` - [docker](https://docs.docker.com/engine/install/) -- `docker-compose v2` +- [docker-compose v2](https://docs.docker.com/engine/install/) - `make` - [sdkman](https://sdkman.io/): for install `java`, Java SDK 8 - [nvm](https://github.com/nvm-sh/nvm): for install `node`, NodeJS v16.15.0 -## Programming Language +### Programming Language If you are using macOS or Linux. We recommend install programming language with SDK manager `sdkman` and `nvm`. @@ -25,12 +25,12 @@ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install.sh | bash # quick install sdkman curl -s "https://get.sdkman.io" | bash # install nodejs -nvm install 16.15.0 && nvm use 16.15.0 +nvm install 16.15.0 && nvm use 16.15.0 && corepack enable # install java development kit sdk install java 8.0.342-amzn && sdk use java 8.0.342-amzn ``` -## macOS +### macOS We recommend using [Homebrew](https://brew.sh/) for installing any missing dependencies: @@ -41,14 +41,13 @@ brew install --cask docker brew install make ``` -## Linux +### Linux On CentOS / RHEL or other Linux distribution with `yum` ```bash sudo yum install git sudo yum install make -sudo yum install docker ``` On Ubuntu / Debian or other Linux distribution with `apt` @@ -57,11 +56,10 @@ On Ubuntu / Debian or other Linux distribution with `apt` sudo apt update sudo apt install git sudo apt install make -sudo apt install docker ``` -## Windows +### Windows If you are running APITable on Windows 10/11, we recommend installing [Docker Desktop on Windows](https://docs.docker.com/desktop/install/windows-install/), [Ubuntu on WSL](https://ubuntu.com/wsl) and [Windows Terminal](https://aka.ms/terminal), You can learn more about Windows Subsystem for Linux (WSL) in [the official site](https://learn.microsoft.com/en-us/windows/wsl). @@ -72,7 +70,6 @@ Install missing dependencies on Ubuntu using `apt`: sudo apt update sudo apt install git sudo apt install make -sudo apt install docker ``` @@ -90,6 +87,44 @@ make +## Start Development Environment + +APITable consists of 4 processes: + +1. backend-server +2. room-server +3. socket-server +4. web-server + +To start the development environment locally, run these commands: + +```bash +# start databases in dockers +make dataenv + +# install dependencies +make install + +#start backend-server +make run # enter 1 + +# and then switch to a new terminal +# start room-server +make run # enter 2 + +# and then switch to a new terminal +# start socket-server +make run # enter 3 + +# and then switch to a new terminal +# start web-server +make run # enter 4 + +``` + + + + ## IDE We recommend you use `Visual Studio Code` or `Intellij IDEA` for your IDE. diff --git a/docs/readme/ar-SA/README.md b/docs/readme/ar-SA/README.md new file mode 100644 index 0000000000000000000000000000000000000000..afa2f0fd5fc4db835d2fc8d2a017641f8b7b273d --- /dev/null +++ b/docs/readme/ar-SA/README.md @@ -0,0 +1,391 @@ +

    + + صورة غلاف APITable + +

    + +

    + + + بيئة تطوير Gitpod قابلة للتطبيق + + + لغة TypeScript، إطار NestJS + + لغة جافا، إطار الربيع + + + + + + + + + + + + +
    + + + شارة ترخيص التطبيق AGPL + + + + + + + + + + + + + +

    + +

    + English + | + Français + | + Español + | + Deutsch + | + 简体中文 + | + 繁體中文 + | + 日本語 +

    + +## ✨ بداية سريعة + +> APITable حاليا هو `العمل قيد التقدم`. +> +> سنقوم بنشر الإصدار الأول في أواخر يناير 2023. +> +> انضم إلى [ديسكورد](https://discord.gg/TwNb9nfdBU) أو [تويتر](https://twitter.com/apitable_com) للإبقاء على اتصال.إذا كنت ترغب فقط في تجربة APITable[^info]، انقر هنا مقابل [⚡Gitpod Online التجريبي](https://gitpod.io/#https://github.com/apitable/apitable). + +إذا كنت ترغب في تثبيت APITable في بيئة الحوسبة المحلية أو السحابية، انظر [💾 Ininstallation](#installation) + +إذا كنت ترغب في إعداد بيئة التنمية المحلية الخاصة بك، اقرأ دليل المطور [🧑 :laptop_computer](./docs/contribute/developer-guide.md) + +## ميزات 🔥 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + التعاون في الوقت الحقيقي + + النموذج التلقائي +
    + + + + + + + +
    + لوحة API-أولا + + روابط غير محدودة متداخلة +
    + + + + + + + +
    + أذونات الصفوف القوية / الأضافات + + تضمين +
    + + + + + + + +
    + +APITable يوفر مجموعة من الميزات المذهلة، من الشخصية إلى المؤسسة. + +- تكدس التكنولوجيا المتقدمة ومفتوح المصدر + - `التعاون في الوقت الحقيقي` يسمح للمستخدمين المتعددين بالتحرير معا في الوقت الحقيقي، أو في وقت واحد مع `خوارزمية التحول التشغيلي`. + - بسلاسة شديدة، وسهولة الاستخدام وسرعة فائقة على واجهة جدول بيانات في ` محرك تقديم`. + - قاعدة البيانات المعمارية الأصلية: تغيير/تشغيل / إجراء / لقطة و هكذا. + - **100k+** صفوف البيانات مع التعاون في الوقت الحقيقي. + - الوصول إلى واجهة برمجة التطبيقات الكاملة، من `بيانات` إلى `بيانات التعريف`. + - اتجاه واحد / رابط جدول الاتجاه الثنائي و `روابط متداخلة لا نهائية` + - لغات البرمجة الملائمة للمجتمع و إطار, TypeScript ([NextJS](https://nextjs.org/) + [NestJS](https://nestjs.com/)) و Java ([حذاء الربيع](https://spring.io/projects/spring-boot)) +- واجهة المستخدم لقاعدة البيانات الغنية الجميلة + - `CRUD`: إنشاء، قراءة، تحديث، حذف الجداول، الأعمدة، والصفوف + - `عمليات الحقول`: الفرز، الفلتر، التجميع، الإخفاء/الفحص، إعداد الطول. + - `المساحة المبنية على`: استخدام مساحات العمل المنفصلة بدلا من البنية القائمة على التطبيق/القاعدة، يجعل ربط الجداول غير محدودة معا ممكنة. + - `الوضع المظلم` وتخصيص الموضوع متوفر. + - `7 أنواع العرض`: عرض الشبكة (Datasheet) / معرض الصور / عرض Mindmap / عرض Kanban / عرض كامل الميزات / عرض التقويم + - لوحة API بنقرة واحدة +- تشمل البطاريات + - قوالب رسمية مدمجة في 10+. + - الروبوت الآلي والتخصيص المتاح. + - لوحة تحكم BI + - نموذج تم إنشاؤه تلقائياً بنقرة واحدة + - صفحة قابلة للمشاركة وقابلة للدمج. + - دعم متعدد اللغات. + - التكامل مع n8n.io / Zapier / Appsmith... وأكثر من ذلك. +- الامتداد الممتاز + - موسع `نظام القطعة` مع أكثر من 20 أداة مفتوحة المصدر. + - الرسم البياني & الرسم البياني & لوحة التحكم + - أنواع أعمدة البيانات القابلة للتخصيص + - صيغ قابلة للتخصيص + - إجراءات الروبوت الآلي القابلة للتخصيص. +- أذونات درجة المؤسسة + - `مرآة`، تحويل العرض إلى مرآة لتنفيذ أذونات الصف. + - تنشيط إذن العمود `` من خلال عملية بسيطة جدا. + - المجلدات / المجلدات الفرعية / أذونات الملفات. + - مجلدات بنية الشجرة و العقدة القابلة للتخصيص (ملف)؛ + - إدارة الفريق & هيكل المؤسسة. +- ميزات المؤسسة: + - SAML + - تسجيل دخول أحادي (SSO) + - مراجعة + - النسخ الاحتياطي التلقائي لقاعدة البيانات + - مصدر البيانات + - العلامة المائية +- .... + +مع الأدوات والإضافات القابلة للتمديد، يمكنك إضافة المزيد من الميزات. + +## 💥 استخدام الحالات + +لماذا يجب أن تعرف APITable لبرنامجك القادم؟ + +- كبرنامج رائع للإدارة + - إدارة المشاريع المرنة & المهام / المشاكل. + - إدارة قيادة التسويق. + - إدارة العلاقة مع الزبائن الأكثر مرونة وقابلية للتواصل. + - • الاستخبارات التجارية المرنة. + - استمارات واستقصاءات ملائمة للسكان + - نظام مرن لتخطيط الموارد. + - رمز منخفض ومنصة بدون رموز. + - ...وأكثر من ذلك، APITable يضع 1000 برنامج في جيبك. +- كبنية أساسية مرئية لقواعد البيانات + - **تضمين** APITable في واجهة المستخدم الخاصة بك. + - قاعدة بيانات مرئية مع REST API. + - لوحة تحكم المدير. + - إدارة التكوين المركزي. + - قاعدة بيانات المؤسسة التي **توصيل كل** برنامجك. + - ...وأكثر من ذلك، APITable يربط كل شيء. +- كما أنه مفتوح المصدر وقابل للتوسع + +## :revolving_قلوب: APIالموجه + +#### API UI Panel + +النقر على زر `API` في الزاوية اليمنى سيظهر لوحة API + +#### استعلام مثل SQL + +سيوفر APITable لغة استعلام لوحة البيانات (DQL) للاستعلام عن محتويات جدول البيانات الخاص بك. + +## 💝 مضمن صديق + +#### شارك و تضمّن + +شارك جدول أو مجلد ورقة البيانات الخاصة بك. أدمجهم عن طريق نسخ ولصق البرامج النصية HTML. + +#### مشروع مدمج جاهز + +[APITable.com](https://apitable.com) يوفر المزيد من ميزات Enterprise-ready Embedding للأوراق المالية. + +## تثبيت + +تأكد من أن لديك `مرفأ` & `` مثبت محلياً. + +إذا قمت بتثبيت جهاز الكمبيوتر جهاز Docker ، قم بفتح المحطة الطرفية الخاصة بك وتشغيل هذا: + +``` +حظر https://apitable.github.io/install.sh bash +``` + +ثم افتح [https://localhost:80](https://localhost:80) في المتصفح الخاص بك لزيارته. (اسم المستخدم الافتراضي `admin@apitable.com` وكلمة المرور `Apitable2022`) + +إذا كنت ترغب في إعداد بيئة التنمية المحلية الخاصة بك، اقرأ دليل المطور [🧑 :laptop_computer](./docs/contribute/developer-guide.md) + +## 💻 المساهمة + +مرحبا، وشكرا لاهتمامك بالمساهمة في APITable! + +هناك العديد من الطرق التي يمكن أن تساهم بها، بما يتجاوز كتابة الكود. + +يمكنك قراءة هذا المستودع [إرشادات المساهمة](./CONTRIBUTING.md) لتعلم كيفية المساهمة. + +إليك دليل سريع لمساعدتك في المساهمة في APITable + +### البيئة الإنمائية + +تعلم كيفية إعداد بيئتك المحلية، في [دليل المطور](./docs/contribute/developer-guide.md) لدينا. + +### سير عمل Git الأساسي + +إليك سير عمل بوابة عامة: + +1. إنشاء مشكلة ووصف الميزات التي تريدها -> [مشاكل APITable](https://github.com/apitable/apitable/issues) +2. اشترك في هذا المشروع -> [مشروع Fork APITable](https://github.com/apitable/apitable/fork) +3. إنشاء فرع الميزة الخاص بك (`git Checout -b my-new-feat`) +4. قم بالتغييرات الخاصة بك (`git obligation -am 'إضافة بعض الميزات'`) +5. نشر الفرع (`git push الأصل my-new-feat`) +6. إنشاء طلب سحب جديد -> [إنشاء طلب سحب عبر الشوكات](https://github.com/apitable/apitable/compare) + +### اتفاقيات العمل + +يستخدم APITable هذه الاتفاقيات المشتركة: + +- ما هو نموذج فروع Git لدينا؟ [Gitflow](https://nvie.com/posts/a-successful-git-branching-model/) +- كيف يمكن التعاون على مشاريع الشوك؟ [Github Flow](https://docs.github.com/en/get-started/quickstart/github-flow) +- كيفية كتابة رسالة الالتزام الجيدة؟ [القوات التقليدية](https://www.conventionalcommits.org/) +- ما هو تنسيق سجل التغيير؟ [احتفظ بالتغييرات](https://keepachangelog.com/en/1.0.0/) +- كيفية الإصدار و الوسم؟ [الإصدار السامي](https://semver.org/) +- ما هو دليل برمجة جافا؟ [Java Coding Guidelin](https://github.com/alibaba/Alibaba-Java-Coding-Guidelines) +- ما هو دليل برمجة TypeScript Ccolining؟ -> [دليل نمط النص النصي](https://google.github.io/styleguide/tsguide.html) + +### الوثائق + +- [مركز المساعدة](https://help.apitable.com/) +- [👩‍💻 مركز المطور](https://developers.apitable.com/) + - [ مستندات REST API](https://developers.apitable.com/api/introduction/) + - أداة SDK (قريباً...) + - سكريبت (قريبًا...) + +## 🛣 خارطة الطريق + +### ميزات المستقبل + +- منشئ الواجهة برموز ثقيلة +- مكونات توثيق الطرف الثالث القابلة للنسر +- لغات شبيهة بنطاق محدد +- كهوية شخصية +- ميزات ويب 3 +- ... + +### الإصدارات المستضافة والمؤسسة تقدم ميزات متقدمة + +- كهوية شخصية؛ +- SAML +- تسجيل دخول أحادي +- مراجعة +- النسخ الاحتياطي لقاعدة البيانات +- العلامة المائية + +لمزيد من المعلومات، يرجى الاتصال بنا على . + +## 👫 اشترك + +### :globe_showing_Asi-Australia: لماذا نقوم بإنشاء APITable ومفتوح المصدر؟ + +- نحن نعتقد أن قاعدة البيانات `هي حجر الزاوية` لكل البرمجيات. +- نعتقد أن إنشاء `قاعدة بيانات مرئية مع واجهة مستخدم غنية وسهلة للجميع` يمكن أن يقلل من صعوبة صناعة البرمجيات ويزيد من اعتماد رقمنة العالم. +- نحن نعتقد أن استخدام المصدر المفتوح `APITable` يمكن `دفع البشر إلى الأمام`. + +### نحن نستخدم عن بعد! + +نحن دائما نبحث عن مواهب جيدة لتطبيق APITable: + +- **المطور الخلفي**: لديك تجربة مع NestJS, TypeScript, Spring Boot, Java, SQL, Kubernetes, Terraform. وتحب كتابة كود عالي الجودة مع وثائق واضحة واختبارات وحدة +- **مطور المكدس الكامل**: لديك تجربة مع React. NestJS, TypeScript, Spring Boot, Java, Terraform. وتحب كتابة كود عالي الجودة مع وثائق واضحة واختبارات وحدة +- **مطور النهاية الأمامية**: لديك تجربة مع React. NextJS, TypeScript, WebPack. وتحب كتابة كود عالي الجودة مع وثائق واضحة واختبارات وحدة + +بغض النظر عن الوقت والشروط، إذا كنت ترغب في المشاركة في فريق APITable، لا تتردد في إرسال السيرة الذاتية الخاصة بك إلى + +talent@apitable .

    + + + +## :التلفزيون: لقطة الشاشة + +

    + صورة لقطة شاشة APITable +

    +

    + صورة لقطة شاشة APITable +

    +

    + صورة لقطة شاشة APITable +

    +

    + صورة لقطة شاشة APITable +

    +

    + صورة لقطة شاشة APITable +

    +

    + صورة لقطة شاشة APITable +

    +

    + صورة لقطة شاشة APITable +

    +

    + صورة لقطة شاشة APITable +

    +

    + صورة لقطة شاشة APITable +

    +

    + صورة لقطة شاشة APITable +

    + +## 🥰 ترخيص + + + +> يحتوي هذا المستودع على رمز مصدر الطبعة المفتوحة المصدر من APITabl، التي صدرت تحت AGPL. +> +> إذا كنت ترغب في تشغيل نسختك الخاصة من APITable أو المساهمة في التطوير، فهذا هو المكان بالنسبة لك. +> +> راجع [LICENSING](./LICENSING.md) للحصول على التفاصيل. +> +> إذا كنت ترغب في استخدام APITable عبر الإنترنت فأنت لست بحاجة إلى تشغيل هذا الكود، نحن نقدم نسخة مستضافة من التطبيق في [APITabl. م](https://apitable.com) الذي تم تحسينه للمسرع العالمي. + +
    + + + +[^info]: + مرخص بواسطة AGPL-3.0. مصممة بواسطة [APITable Ltd](https://apitable.com). diff --git a/docs/readme/ar-SA/docs/contribute/developer-guide.md b/docs/readme/ar-SA/docs/contribute/developer-guide.md new file mode 100644 index 0000000000000000000000000000000000000000..92a928e09cd8ad4cb0aa6266d5c63ddea5769d69 --- /dev/null +++ b/docs/readme/ar-SA/docs/contribute/developer-guide.md @@ -0,0 +1,132 @@ +# دليل المطور + +هذا الدليل يساعدك على البدء في تطوير APITable. + +## التبعيات + +تأكد من أن لديك التبعيات التالية ولغات البرمجة مثبتة قبل إعداد بيئة المطور الخاص بك: + +- `git` +- [مخزن](https://docs.docker.com/engine/install/) +- [المرفأ - تكوين v2](https://docs.docker.com/engine/install/) +- `اصنع` +- [sdkman](https://sdkman.io/): لتثبيت `جافا`، جافا SDK 8 +- [nvm](https://github.com/nvm-sh/nvm): لتثبيت `عقدة`, NodeJS v16.15.0 + + +### لغة البرمجة + +إذا كنت تستخدم macOS أو Linux. نوصي بتثبيت لغة البرمجة مع مدير SDK `sdkman` و `nvm`. + +```bash +# تثبيت سريع nvm +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install. (ح) bash +# تثبيت سريع sdkman +curl -s "https://get.sdkman.io" bash +# تثبيت nodejs +nvm تثبيت 16. 5.0 && nvm يستخدم 16.15. && تمكين الحزمة +# تثبيت مجموعة تطوير جافا +sdk تثبيت جافا 8. .342-amzn && sdk استخدم java 8.0.342-amzn +``` + +### macOS + +نوصي باستخدام [Homebrew](https://brew.sh/) لتثبيت أي تبعيات مفقودة: + +```bash +## ضروري مطلوب +التثبيت git +التثبيت --كاسك دكر +التثبيت +``` + +### Linux + +في CentOS / RHEL أو أي توزيع آخر لـ Linux مع `yum` + +```bash +sudo yum تثبيت git +sudo yum التثبيت +``` + +على Ubuntu / Debian أو أي توزيع آخر لـ Linux مع `Apt` + +```bash +sudo apt update +sudo apt install git +sudo apt install make +``` + + +### ويندوز + +إذا كنت تقوم بتشغيل APITable على Windows 10/11، نوصي بتثبيت [Docker سطح المكتب على Windows](https://docs.docker.com/desktop/install/windows-install/)، [أوبونتو على WSL](https://ubuntu.com/wsl) و [محطة ويندوز الطرفية](https://aka.ms/terminal)، يمكنك معرفة المزيد عن نظام Windows الفرعي لـ Linux (WSL) في [الموقع الرسمي](https://learn.microsoft.com/en-us/windows/wsl). + +تثبيت الإعتمادات المفقودة على أوبونتو باستخدام `apt`: + +```bash +sudo apt update +sudo apt install git +sudo apt install make +``` + + +## أداة البناء + +نحن نستخدم `صنع` كإدخال لأداة البناء المركزي لدينا التي تقود أداة بناء أخرى مثل `صف` / `npm` / `yarn`. + +لذا يمكنك فقط إدخال `صنع أمر` ومشاهدة جميع أوامر الإنشاء: + +```bash +اصنع +``` + +![اصنع لقطة للأوامر](../static/make.png) + + + +## بدء بيئة التطوير + +APITable يتألف من 4 عمليات: + +1. الخادم الخلفي +2. الغرفة-الخادم +3. مقطعة-خادم +4. خادم ويب + +لبدء بيئة التطوير محلياً، قم بتشغيل هذه الأوامر: + +```bash +# بدء قواعد البيانات في قاعدة البيانات +جعل البياناتينيف + +# تثبيت الإعتمادات +جعل التثبيت + +#start backend server +جعل تشغيل # ادخل 1 + +# ثم قم بالتبديل الى محطة طرفية جديدة +# بدء غرفة الخادم +جعل تشغيل # ادخل 2 + +# ثم قم بالتبديل الى محطة طرفية جديدة +# ابدأ Socket-server +جعل تشغيل # ادخل 3 + +# ثم قم بالتبديل الى محطة طرفية جديدة +# ابدأ web-server +جعل التشغيل # ادخل 4 + +``` + + + + +## IDE + +ننصحك باستخدام `Visual Studio Code` أو `Intellij IDEA` من أجل IDE الخاص بك. + +لقد قام APITable بإعداد تكوينين لتصحيح أخطاء الـ IDE. + +فقط قم بفتح دليل APITabL الجذري باستخدام IDE. diff --git a/docs/readme/ar-SA/docs/static/cover.png b/docs/readme/ar-SA/docs/static/cover.png new file mode 100644 index 0000000000000000000000000000000000000000..729256d74f3d38cd93db4f786f0c5c57367d6507 Binary files /dev/null and b/docs/readme/ar-SA/docs/static/cover.png differ diff --git a/docs/readme/ar-SA/docs/static/feature-api-first-panel.gif b/docs/readme/ar-SA/docs/static/feature-api-first-panel.gif new file mode 100644 index 0000000000000000000000000000000000000000..0dceee8ccd1f3b07ac64a5cffd6ea546b6d87d55 Binary files /dev/null and b/docs/readme/ar-SA/docs/static/feature-api-first-panel.gif differ diff --git a/docs/readme/ar-SA/docs/static/feature-apipanel.gif b/docs/readme/ar-SA/docs/static/feature-apipanel.gif new file mode 100644 index 0000000000000000000000000000000000000000..cc829499a82ee5019c981b01a4aa15441f280302 Binary files /dev/null and b/docs/readme/ar-SA/docs/static/feature-apipanel.gif differ diff --git a/docs/readme/ar-SA/docs/static/feature-embed.gif b/docs/readme/ar-SA/docs/static/feature-embed.gif new file mode 100644 index 0000000000000000000000000000000000000000..9edd44049941d2345804f71f61b34a1327df4ed2 Binary files /dev/null and b/docs/readme/ar-SA/docs/static/feature-embed.gif differ diff --git a/docs/readme/ar-SA/docs/static/feature-form.gif b/docs/readme/ar-SA/docs/static/feature-form.gif new file mode 100644 index 0000000000000000000000000000000000000000..bea7e8a35f27ba0d88f57e55c0e96f5ed4cc30fc Binary files /dev/null and b/docs/readme/ar-SA/docs/static/feature-form.gif differ diff --git a/docs/readme/ar-SA/docs/static/feature-permissions.gif b/docs/readme/ar-SA/docs/static/feature-permissions.gif new file mode 100644 index 0000000000000000000000000000000000000000..456b3c588ab1026933846729d29ae02c942eb725 Binary files /dev/null and b/docs/readme/ar-SA/docs/static/feature-permissions.gif differ diff --git a/docs/readme/ar-SA/docs/static/feature-realtime.gif b/docs/readme/ar-SA/docs/static/feature-realtime.gif new file mode 100644 index 0000000000000000000000000000000000000000..a2fc86b1e50ca971c0b9751088bf14e360ede30b Binary files /dev/null and b/docs/readme/ar-SA/docs/static/feature-realtime.gif differ diff --git a/docs/readme/ar-SA/docs/static/feature-unlimited-cross-table-links.gif b/docs/readme/ar-SA/docs/static/feature-unlimited-cross-table-links.gif new file mode 100644 index 0000000000000000000000000000000000000000..b2d08fe1766ac832299a2db4c4be13526153b91c Binary files /dev/null and b/docs/readme/ar-SA/docs/static/feature-unlimited-cross-table-links.gif differ diff --git a/docs/readme/ar-SA/docs/static/logo.svg b/docs/readme/ar-SA/docs/static/logo.svg new file mode 100644 index 0000000000000000000000000000000000000000..33c93bb8dbf0fc3430cb0749da1a75ef0f283774 --- /dev/null +++ b/docs/readme/ar-SA/docs/static/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/readme/ar-SA/docs/static/make.png b/docs/readme/ar-SA/docs/static/make.png new file mode 100644 index 0000000000000000000000000000000000000000..c997c1c7b453bde0940451f6ef239fc7c42c2b43 Binary files /dev/null and b/docs/readme/ar-SA/docs/static/make.png differ diff --git a/docs/readme/ar-SA/docs/static/screenshot-api-panel.png b/docs/readme/ar-SA/docs/static/screenshot-api-panel.png new file mode 100644 index 0000000000000000000000000000000000000000..622ceadd5e3f27c8a632c461f687bd6ca9e9467a Binary files /dev/null and b/docs/readme/ar-SA/docs/static/screenshot-api-panel.png differ diff --git a/docs/readme/ar-SA/docs/static/screenshot-auto-form.png b/docs/readme/ar-SA/docs/static/screenshot-auto-form.png new file mode 100644 index 0000000000000000000000000000000000000000..232d98bdfa209f60d347ad89cd28a4db93568710 Binary files /dev/null and b/docs/readme/ar-SA/docs/static/screenshot-auto-form.png differ diff --git a/docs/readme/ar-SA/docs/static/screenshot-automation.png b/docs/readme/ar-SA/docs/static/screenshot-automation.png new file mode 100644 index 0000000000000000000000000000000000000000..2356614a073f24c8ac2a7ccf7a7fbcfeee3ccf89 Binary files /dev/null and b/docs/readme/ar-SA/docs/static/screenshot-automation.png differ diff --git a/docs/readme/ar-SA/docs/static/screenshot-extensible.png b/docs/readme/ar-SA/docs/static/screenshot-extensible.png new file mode 100644 index 0000000000000000000000000000000000000000..334a29e54f82c027c3a4af3815850bc93e2248f6 Binary files /dev/null and b/docs/readme/ar-SA/docs/static/screenshot-extensible.png differ diff --git a/docs/readme/ar-SA/docs/static/screenshot-hr.png b/docs/readme/ar-SA/docs/static/screenshot-hr.png new file mode 100644 index 0000000000000000000000000000000000000000..3ec97c6cf99a69bc3a3aefcfc77a1c124508e53b Binary files /dev/null and b/docs/readme/ar-SA/docs/static/screenshot-hr.png differ diff --git a/docs/readme/ar-SA/docs/static/screenshot-it.png b/docs/readme/ar-SA/docs/static/screenshot-it.png new file mode 100644 index 0000000000000000000000000000000000000000..f0592b3a8880f1f7551bc59cd184eb804bb7a27e Binary files /dev/null and b/docs/readme/ar-SA/docs/static/screenshot-it.png differ diff --git a/docs/readme/ar-SA/docs/static/screenshot-marketing.png b/docs/readme/ar-SA/docs/static/screenshot-marketing.png new file mode 100644 index 0000000000000000000000000000000000000000..5ff160f4f6f3fbed223b22fd7aefb45a00469ddc Binary files /dev/null and b/docs/readme/ar-SA/docs/static/screenshot-marketing.png differ diff --git a/docs/readme/ar-SA/docs/static/screenshot-permissions.png b/docs/readme/ar-SA/docs/static/screenshot-permissions.png new file mode 100644 index 0000000000000000000000000000000000000000..9e3d88ec98de008ce2fef8a4bd48e8d971b9bedf Binary files /dev/null and b/docs/readme/ar-SA/docs/static/screenshot-permissions.png differ diff --git a/docs/readme/ar-SA/docs/static/screenshot-realtime.png b/docs/readme/ar-SA/docs/static/screenshot-realtime.png new file mode 100644 index 0000000000000000000000000000000000000000..40f22a47a180fae4ba21ca6fbd7f3247ecb0c4ac Binary files /dev/null and b/docs/readme/ar-SA/docs/static/screenshot-realtime.png differ diff --git a/docs/readme/ar-SA/docs/static/screenshot-sales.png b/docs/readme/ar-SA/docs/static/screenshot-sales.png new file mode 100644 index 0000000000000000000000000000000000000000..efd35700dd39d27e1f89b357e1f0ffde373944d4 Binary files /dev/null and b/docs/readme/ar-SA/docs/static/screenshot-sales.png differ diff --git a/docs/readme/de-DE/README.md b/docs/readme/de-DE/README.md new file mode 100644 index 0000000000000000000000000000000000000000..a8759115a45362cd589939df63d8fe5bc362ad19 --- /dev/null +++ b/docs/readme/de-DE/README.md @@ -0,0 +1,382 @@ +

    + + APITable Cover-Bild + +

    + +

    + + + APITable Gitpod Entwicklungsumgebung + + + TypeScript Sprache, NestJS Framework + + Java-Sprache, Spring Framework + + + + + + + + + + + + +
    + + + APITable License Badge AGPL + + + + + + + + + + + + + +

    + +

    + English + | + Français + | + Español + | + Deutsch + | + 简体中文 + | + 繁體中文 + | + 日本語 +

    + +## ✨ Schnellstart + +> APITable ist derzeit eine `Arbeit in Bearbeitung`. +> +> Wir werden die erste Veröffentlichung Ende Januar 2023 veröffentlichen. +> +> Trete [Discord](https://discord.gg/TwNb9nfdBU) oder [Twitter](https://twitter.com/apitable_com) bei, um in Kontakt zu bleiben.Wenn Sie nur APITable[^info]ausprobieren möchten, klicken Sie hier für [⚡Gitpod Online Demo](https://gitpod.io/#https://github.com/apitable/apitable). + +Wenn Sie APITable in Ihrer lokalen oder Cloud-Rechenumgebung installieren möchten, lesen Sie [💾 Installation](#installation) + +Wenn Sie Ihre lokale Entwicklungsumgebung einrichten möchten, lesen Sie unseren [🧑 💻 Developer Guide](./docs/contribute/developer-guide.md) + +## 🔥 Funktionen + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Echtzeit-Zusammenarbeit + + Automatisches Formular +
    + + + + + + + +
    + API-First-Panel + + Unbegrenzte Kreuz-Tabellen-Links +
    + + + + + + + +
    + Mächtige Zeilen/Spaltenberechtigungen + + Einbetten +
    + + + + + + + +
    + +APITable bietet eine Reihe von erstaunlichen Funktionen, von der persönlichen bis zum Unternehmen. + +- Erweiterter Technologie-Stack und Open-Source + - `Echtzeit-Zusammenarbeit` ermöglicht es mehreren Benutzern, in Echtzeit oder gleichzeitig mit dem `Operational Transformation (OT)` Algorithmus zu bearbeiten. + - Äußerst glatte, benutzerfreundliche, superschnelle Datenbank-Tabellenkalkulationsschnittstelle in ` Rendering Engine`. + - Datenbank native Architektur: Changeset / Operation / Aktion / Schnappschuss und so weiter. + - **100k+** Datenzeilen mit Echtzeit-Kollaboration. + - Vollständiger API-Zugriff, von `Daten` bis `Metadaten`. + - Ein-Richtung / Bi-Richtung Tabellenlink und `Infinite Cross Links` + - Community-freundliche Programmiersprachen und -Framework, TypeScript ([NextJS](https://nextjs.org/) + [NestJS](https://nestjs.com/)) und Java ([Spring Boot](https://spring.io/projects/spring-boot)) +- Schöne und reiche Datenbank-Tabellenkalkulation + - `CRUD`: Erstellen, lesen, aktualisieren, die Tabellen, Spalten und Zeilen löschen + - `Feldoperationen`: Sortieren, filtern, gruppieren, verstecken/unverstecken, Höheneinstellung einstellen. + - `Leerzeichen basierend`: Verwenden Sie getrennte Arbeitsbereiche anstelle von App/Base-basierter Struktur, um unbegrenzte Tabellen miteinander zu verknüpfen. + - `Dunkler Modus` und Theme-Anpassung verfügbar. + - `7 Ansichtsarten`: Rasteransicht (Datenblatt) / Gallerieansicht / Mindmap-Ansicht / Kanban-Ansicht / Gantt-Ansicht voll / Kalenderansicht + - Ein-Klick-API-Panel +- Batterien enthalten + - Integrierte 10+ offizielle Vorlagen. + - Roboterautomatisierung und -anpassung verfügbar. + - BI-Dashboard + - Ein-Klick-automatisch generiertes Formular + - Teilbare und einbettbare Seite. + - Mehrsprachige Unterstützung. + - Integration mit n8n.io / Zapier / Appsmith... und mehr. +- Exzellente Erweiterbarkeit + - Erweiterbares `Widget-System` mit über 20 offiziellen Open-Source-Widgets. + - Anpassbares Diagramm & Diagramm & Dashboard + - Anpassbare Datentypen + - Anpassbare Formeln + - Anpassbare Roboteraktionen für Automatisierung. +- Enterprise-Grade-Berechtigungen + - `Spiegeln`, verwandeln Sie einen View in einen Spiegel, um Zeilenrechte zu implementieren. + - `Spaltenberechtigung` durch eine sehr einfache Operation aktivieren. + - Ordner / Unterordner / Dateiberechtigung. + - Baumstrukturordner und anpassbare Knoten (Datei); + - Team Management & Organisation Struktur. +- Enterprise-Funktionen: + - SAML + - Einmal-Sign-On (SSO) + - Audit + - Datenbank Auto-Sicherung + - Datenexporteur + - Wasserzeichen +- .... + +Mit erweiterbaren Widgets und Plugins können Sie weitere Funktionen hinzufügen. + +## 💥 Fälle verwenden + +Warum müssen Sie APITable für Ihre nächste Software kennen? + +- Als Super-Management-Software + - Flexibles Projektmanagement & Aufgaben / Problemmanagement. + - Marketing Lead Management. + - Die meisten flexiblen und verbindbaren CRM. + - Flexible Business Intelligence (BI). + - Personen-freundliche Formulare und Umfragen + - Flexible ERP. + - Low-Code und No-Code-Plattform. + - ...und mehr, APITable stellt 1000 Software in Ihre Tasche. +- Als visuelle Datenbank-Infrastruktur + - **Einbetten** APITable in Ihre eigene Software UIs. + - Visuelle Datenbank mit REST-API. + - Admin-Dashboard. + - Zentrale Konfigurationsverwaltung. + - All-in-one enterprise database that **connect all** your software. + - ...und mehr, APITable verbindet alles. +- Auch ist es Open Source und erweiterbar + +## 💞 API-orientiert + +#### API UI Panel + +Wenn Sie auf die `API` Taste in der rechten Ecke klicken, wird das API Panel angezeigt + +#### SQL-ähnliche Abfrage + +APITable stellt eine Datasheet Query Language (DQL) zur Verfügung, um die Inhalte Ihrer Datenbanktabellenkalkulation abzufragen. + +## 💝 Einbett-freundlich + +#### Teilen und einbinden + +Teilen Sie Ihre Tabelle oder Ihren Ordner. Einbetten durch Kopieren und Einfügen von HTML-Skripten. + +#### unternehmensfertige Einbettung + +[APITable.com](https://apitable.com) bietet weitere unternehmensfertige Einbettungsfunktionen für Wertpapiere. + +## Installation + +Stellen Sie sicher, dass Sie den `Docker` & `curl` lokal installiert haben. + +Wenn Sie Docker Machine installiert haben, öffnen Sie Ihr Terminal und führen Sie es aus: + +``` +curl https://apitable.github.io/install.sh | bash +``` + +Öffne dann [https://localhost:80](https://localhost:80) in deinem Browser, um ihn zu besuchen. (Standardname `admin@apitable.com` und Passwort `Apitable2022`) + +Wenn Sie Ihre lokale Entwicklungsumgebung einrichten möchten, lesen Sie unseren [🧑 💻 Developer Guide](./docs/contribute/developer-guide.md) + +## 💻 Mitwirken + +Herzlich willkommen und vielen Dank für Ihr Interesse an APITable! + +Es gibt viele Möglichkeiten, einen Beitrag zu leisten, abgesehen vom Schreiben von Code. + +You can read this repository’s [Contributing Guidelines](./CONTRIBUTING.md) to learn how to contribute. + +Hier ist eine kurze Anleitung, die Ihnen hilft, zu APITable beizutragen. + +### Entwicklungsumgebung + +Erfahren Sie, wie Sie Ihre lokale Umgebung einrichten, in unserem [Entwicklerhandbuch](./docs/contribute/developer-guide.md). + +### Git Workflow einfach + +Hier ist ein allgemeiner APITable Git Workflow: + +1. Erstellen Sie ein Problem und beschreiben Sie die gewünschten Funktionen -> [APITable Issues](https://github.com/apitable/apitable/issues) +2. Dieses Projekt forken -> [APITable Projekt Fork](https://github.com/apitable/apitable/fork) +3. Erstelle deinen Feature-Branch (`git checkout -b my-new-feature`) +4. Commit your changes (`git commit -am 'Add some features'`) +5. Veröffentlichen des Zweiges (`git Push-Ursprung my-new-Feature`) +6. Neuen Pull-Request erstellen -> [Pull-Request über Gabeln erstellen](https://github.com/apitable/apitable/compare) + +### Arbeitskonventionen + +APITable use these common convention: + +- Was ist unser Git-Branching-Modell? [Gitflow](https://nvie.com/posts/a-successful-git-branching-model/) +- Wie kann ich an Ihren Fork-Projekten arbeiten? [Github Flow](https://docs.github.com/en/get-started/quickstart/github-flow) +- Wie kann ich eine gute Commit-Nachricht schreiben? [Herkömmliche Commits](https://www.conventionalcommits.org/) +- Was ist unser Changelog-Format? [Changelog beibehalten](https://keepachangelog.com/en/1.0.0/) +- Wie kann ich versionieren und taggingen? [Semantische Versionierung](https://semver.org/) +- Was ist die Java Coding Guideline? [Java Coding Guideline](https://github.com/alibaba/Alibaba-Java-Coding-Guidelines) | [Intellij IDEA Plugin](https://plugins.jetbrains.com/plugin/10046-alibaba-java-coding-guidelines) +- Was ist die TypeScript Coding Guideline? -> [TypeScript Style Guide](https://google.github.io/styleguide/tsguide.html) | [ESLint](https://www.npmjs.com/package/@typescript-eslint/eslint-plugin) + +### Dokumentationen + +- [Hilfe-Center](https://help.apitable.com/) +- [👩‍💻 Entwicklerzentrum](https://developers.apitable.com/) + - [REST API Docs](https://developers.apitable.com/api/introduction/) + - Widget SDK (Kommt bald...) + - Skripting (Kommt bald...) + +## :Autobahn: Fahrplan + +### Zukünftige Funktionen + +- Heavy-Code Interface Builder +- Embbedbare Dokumentations-Komponenten von Drittanbietern +- SQL-ähnliche Domain-spezifische Sprachen +- Als IdP +- Web 3 Funktionen +- ... + +### Gehostete und Enterprise-Versionen bieten erweiterte Funktionen + +- Als IdP; +- SAML +- Einmal-Sign-An +- Audit +- Datenbanksicherung +- Wasserzeichen + +Für weitere Informationen kontaktieren Sie uns bitte unter . + +## 👫 Beteiligt werden + +### 🌏 Warum erstellen wir APITable und Open-Source? + +- Wir glauben, dass die `-Datenbank der Eckpfeiler` aller Software ist. +- Wir glauben, dass die Erstellung einer `Visuellen Datenbank mit einer reichen und einfachen Benutzeroberfläche für jeden` die Schwierigkeiten der Softwareindustrie verringern und die weltweite Digitalisierung erhöhen kann. +- Wir glauben, dass Open-Sourcing `APITable` Arbeit `Push Human Seinen Weiterleitung` ermöglichen kann. + +### Wir stellen aus der Ferne! + +Wir suchen immer nach guten Talenten für APITable: + +- **Vollstack-Entwickler**: Sie haben Erfahrung mit React, NestJS, TypeScript, Spring Boot, Java, Terraform. Und Sie schreiben gerne qualitativ hochwertigen Code mit klaren Dokumentationen und Unit-Tests. +- **Back-End-Entwickler**: Sie haben Erfahrung mit NestJS, TypeScript, Spring Boot, Java, SQL, Kubernetes, Terraform. Und Sie schreiben gerne qualitativ hochwertigen Code mit klaren Dokumentationen und Unit-Tests. +- **Front-End-Entwickler**: Sie haben Erfahrung mit React, NextJS, TypeScript, WebPack. Und Sie schreiben gerne qualitativ hochwertigen Code mit klaren Dokumentationen und Unit-Tests. + +Unabhängig von Zeit und Bedingungen, wenn Sie sich an das APITable-Team beteiligen möchten zögern Sie nicht und senden Sie Ihren Lebenslauf an [talent@apitable. om](mailto:talent@apitable.com). + +## 📺 Screenshot + +

    + APITable Screenshot-Bild +

    +

    + APITable Screenshot-Bild +

    +

    + APITable Screenshot-Bild +

    +

    + APITable Screenshot-Bild +

    +

    + APITable Screenshot-Bild +

    +

    + APITable Screenshot-Bild +

    +

    + APITable Screenshot-Bild +

    +

    + APITable Screenshot-Bild +

    +

    + APITable Screenshot-Bild +

    +

    + APITable Screenshot-Bild +

    + +## 🥰 Lizenz + +> Dieses Repository enthält den Quellcode für die Open Source Edition von APITable, veröffentlicht unter der AGPL. +> +> Wenn Sie Ihre eigene Version von APITable betreiben oder zur Entwicklung beitragen möchten, dann ist dies der richtige Ort für Sie. +> +> Siehe [LICENSING](./LICENSING.md) für Details. +> +> Wenn Sie APITable online verwenden möchten, müssen Sie diesen Code nicht ausführen wir bieten eine gehostete Version der App unter [APITable an. om](https://apitable.com) die für den globalen Beschleuniger optimiert wurde. + +
    + +[^info]: Lizenziert mit AGPL-3.0. Entworfen von [APITable Ltd](https://apitable.com). diff --git a/docs/readme/de-DE/docs/contribute/developer-guide.md b/docs/readme/de-DE/docs/contribute/developer-guide.md new file mode 100644 index 0000000000000000000000000000000000000000..e6f5ab539267d9e21e68b47ad5991468f5eef101 --- /dev/null +++ b/docs/readme/de-DE/docs/contribute/developer-guide.md @@ -0,0 +1,132 @@ +# Entwicklerhandbuch + +Diese Anleitung hilft Ihnen bei der Entwicklung von APITable. + +## Abhängigkeiten + +Stellen Sie sicher, dass Sie die folgenden Abhängigkeiten und Programmiersprachen installiert haben, bevor Sie Ihre Entwicklerumgebung einrichten: + +- `git` +- [docker](https://docs.docker.com/engine/install/) +- [docker-compose v2](https://docs.docker.com/engine/install/) +- `machen` +- [sdkman](https://sdkman.io/): Installation `Java`, Java SDK 8 +- [nvm](https://github.com/nvm-sh/nvm): für `Knoten`, NodeJS v16.15.0 + + +### Programmiersprache + +Wenn Sie macOS oder Linux verwenden. Wir empfehlen die Installation der Programmiersprache mit dem SDK-Manager `sdkman` und `nvm`. + +```bash +# Schnellinstallation nvm +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install. h | bash +# quick install sdkman +curl -s "https://get.sdkman.io" | bash +# install nodejs +nvm install 16. 5.0 && nvm verwenden 16.15. && corepack aktivieren +# install Java Development Kit +sdk install java 8. .342-amzn && sdk verwenden Sie java 8.0.342-amzn +``` + +### macOS + +Wir empfehlen [Homebrew](https://brew.sh/) zur Installation fehlender Abhängigkeiten zu verwenden: + +```bash +## erforderlich +braut Installation git +braut Installation --cask docker +braut install make +``` + +### Linux + +Auf CentOS / RHEL oder anderen Linux-Distributionen mit `yum` + +```bash +sudo yum install git +sudo yum install make +``` + +Auf Ubuntu / Debian oder andere Linux-Distribution mit `apt` + +```bash +sudo apt update +sudo apt install git +sudo apt install make +``` + + +### Fenster + +Wenn Sie APITable unter Windows 10/11 ausführen, empfehlen wir die Installation von [Docker Desktop unter Windows](https://docs.docker.com/desktop/install/windows-install/), [Ubuntu auf WSL](https://ubuntu.com/wsl) und [Windows Terminal](https://aka.ms/terminal), Mehr über Windows Subsystem für Linux (WSL) erfahren Sie unter [der offiziellen Seite](https://learn.microsoft.com/en-us/windows/wsl). + +Fehlende Abhängigkeiten auf Ubuntu mit `apt` installieren: + +```bash +sudo apt update +sudo apt install git +sudo apt install make +``` + + +## Erstelle Werkzeug + +Wir verwenden `make` als unseren zentrischen Buildwerkzeug, der andere Buildwerkzeuge wie `Gradle` / `npm` / `Garn` antreibt. + +So können Sie einfach `make` Befehl eingeben und alle Build-Befehle sehen: + +```bash +machen +``` + +![erstelle einen Screenshot](../static/make.png) + + + +## Entwicklungsumgebung starten + +APITable besteht aus 4 Prozessen: + +1. backend-Server +2. room-Server +3. socket-Server +4. web-server + +Um die Entwicklungsumgebung lokal zu starten, führen Sie diese Befehle aus: + +```bash +# Datenbanken in Dockern starten +make dataenv + +# # Installationsabhängigkeiten +install + +#Start backend-server +make run # 1 + +# und dann zu einem neuen Terminal wechseln +# Starte Raum-Server +make run # 2 + +# und wechseln Sie dann zu einem neuen Terminal +# Starte Socket-Server +make run # geben Sie 3 + +# ein und wechseln Sie zu einem neuen Terminal +# Start Webserver +make run # geben Sie 4 ein # + +``` + + + + +## IDE + +Wir empfehlen Ihnen, `Visual Studio Code` oder `Intellij IDEA` für Ihre IDE zu verwenden. + +APITable haben diese beiden IDE Debug-Konfigurationen vorbereitet. + +Öffnen Sie einfach das Hauptverzeichnis von APITable mit IDE. diff --git a/docs/readme/de-DE/docs/static/cover.png b/docs/readme/de-DE/docs/static/cover.png new file mode 100644 index 0000000000000000000000000000000000000000..729256d74f3d38cd93db4f786f0c5c57367d6507 Binary files /dev/null and b/docs/readme/de-DE/docs/static/cover.png differ diff --git a/docs/readme/de-DE/docs/static/feature-api-first-panel.gif b/docs/readme/de-DE/docs/static/feature-api-first-panel.gif new file mode 100644 index 0000000000000000000000000000000000000000..0dceee8ccd1f3b07ac64a5cffd6ea546b6d87d55 Binary files /dev/null and b/docs/readme/de-DE/docs/static/feature-api-first-panel.gif differ diff --git a/docs/readme/de-DE/docs/static/feature-apipanel.gif b/docs/readme/de-DE/docs/static/feature-apipanel.gif new file mode 100644 index 0000000000000000000000000000000000000000..cc829499a82ee5019c981b01a4aa15441f280302 Binary files /dev/null and b/docs/readme/de-DE/docs/static/feature-apipanel.gif differ diff --git a/docs/readme/de-DE/docs/static/feature-embed.gif b/docs/readme/de-DE/docs/static/feature-embed.gif new file mode 100644 index 0000000000000000000000000000000000000000..9edd44049941d2345804f71f61b34a1327df4ed2 Binary files /dev/null and b/docs/readme/de-DE/docs/static/feature-embed.gif differ diff --git a/docs/readme/de-DE/docs/static/feature-form.gif b/docs/readme/de-DE/docs/static/feature-form.gif new file mode 100644 index 0000000000000000000000000000000000000000..bea7e8a35f27ba0d88f57e55c0e96f5ed4cc30fc Binary files /dev/null and b/docs/readme/de-DE/docs/static/feature-form.gif differ diff --git a/docs/readme/de-DE/docs/static/feature-permissions.gif b/docs/readme/de-DE/docs/static/feature-permissions.gif new file mode 100644 index 0000000000000000000000000000000000000000..456b3c588ab1026933846729d29ae02c942eb725 Binary files /dev/null and b/docs/readme/de-DE/docs/static/feature-permissions.gif differ diff --git a/docs/readme/de-DE/docs/static/feature-realtime.gif b/docs/readme/de-DE/docs/static/feature-realtime.gif new file mode 100644 index 0000000000000000000000000000000000000000..a2fc86b1e50ca971c0b9751088bf14e360ede30b Binary files /dev/null and b/docs/readme/de-DE/docs/static/feature-realtime.gif differ diff --git a/docs/readme/de-DE/docs/static/feature-unlimited-cross-table-links.gif b/docs/readme/de-DE/docs/static/feature-unlimited-cross-table-links.gif new file mode 100644 index 0000000000000000000000000000000000000000..b2d08fe1766ac832299a2db4c4be13526153b91c Binary files /dev/null and b/docs/readme/de-DE/docs/static/feature-unlimited-cross-table-links.gif differ diff --git a/docs/readme/de-DE/docs/static/logo.svg b/docs/readme/de-DE/docs/static/logo.svg new file mode 100644 index 0000000000000000000000000000000000000000..33c93bb8dbf0fc3430cb0749da1a75ef0f283774 --- /dev/null +++ b/docs/readme/de-DE/docs/static/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/readme/de-DE/docs/static/make.png b/docs/readme/de-DE/docs/static/make.png new file mode 100644 index 0000000000000000000000000000000000000000..c997c1c7b453bde0940451f6ef239fc7c42c2b43 Binary files /dev/null and b/docs/readme/de-DE/docs/static/make.png differ diff --git a/docs/readme/de-DE/docs/static/screenshot-api-panel.png b/docs/readme/de-DE/docs/static/screenshot-api-panel.png new file mode 100644 index 0000000000000000000000000000000000000000..622ceadd5e3f27c8a632c461f687bd6ca9e9467a Binary files /dev/null and b/docs/readme/de-DE/docs/static/screenshot-api-panel.png differ diff --git a/docs/readme/de-DE/docs/static/screenshot-auto-form.png b/docs/readme/de-DE/docs/static/screenshot-auto-form.png new file mode 100644 index 0000000000000000000000000000000000000000..232d98bdfa209f60d347ad89cd28a4db93568710 Binary files /dev/null and b/docs/readme/de-DE/docs/static/screenshot-auto-form.png differ diff --git a/docs/readme/de-DE/docs/static/screenshot-automation.png b/docs/readme/de-DE/docs/static/screenshot-automation.png new file mode 100644 index 0000000000000000000000000000000000000000..2356614a073f24c8ac2a7ccf7a7fbcfeee3ccf89 Binary files /dev/null and b/docs/readme/de-DE/docs/static/screenshot-automation.png differ diff --git a/docs/readme/de-DE/docs/static/screenshot-extensible.png b/docs/readme/de-DE/docs/static/screenshot-extensible.png new file mode 100644 index 0000000000000000000000000000000000000000..334a29e54f82c027c3a4af3815850bc93e2248f6 Binary files /dev/null and b/docs/readme/de-DE/docs/static/screenshot-extensible.png differ diff --git a/docs/readme/de-DE/docs/static/screenshot-hr.png b/docs/readme/de-DE/docs/static/screenshot-hr.png new file mode 100644 index 0000000000000000000000000000000000000000..3ec97c6cf99a69bc3a3aefcfc77a1c124508e53b Binary files /dev/null and b/docs/readme/de-DE/docs/static/screenshot-hr.png differ diff --git a/docs/readme/de-DE/docs/static/screenshot-it.png b/docs/readme/de-DE/docs/static/screenshot-it.png new file mode 100644 index 0000000000000000000000000000000000000000..f0592b3a8880f1f7551bc59cd184eb804bb7a27e Binary files /dev/null and b/docs/readme/de-DE/docs/static/screenshot-it.png differ diff --git a/docs/readme/de-DE/docs/static/screenshot-marketing.png b/docs/readme/de-DE/docs/static/screenshot-marketing.png new file mode 100644 index 0000000000000000000000000000000000000000..5ff160f4f6f3fbed223b22fd7aefb45a00469ddc Binary files /dev/null and b/docs/readme/de-DE/docs/static/screenshot-marketing.png differ diff --git a/docs/readme/de-DE/docs/static/screenshot-permissions.png b/docs/readme/de-DE/docs/static/screenshot-permissions.png new file mode 100644 index 0000000000000000000000000000000000000000..9e3d88ec98de008ce2fef8a4bd48e8d971b9bedf Binary files /dev/null and b/docs/readme/de-DE/docs/static/screenshot-permissions.png differ diff --git a/docs/readme/de-DE/docs/static/screenshot-realtime.png b/docs/readme/de-DE/docs/static/screenshot-realtime.png new file mode 100644 index 0000000000000000000000000000000000000000..40f22a47a180fae4ba21ca6fbd7f3247ecb0c4ac Binary files /dev/null and b/docs/readme/de-DE/docs/static/screenshot-realtime.png differ diff --git a/docs/readme/de-DE/docs/static/screenshot-sales.png b/docs/readme/de-DE/docs/static/screenshot-sales.png new file mode 100644 index 0000000000000000000000000000000000000000..efd35700dd39d27e1f89b357e1f0ffde373944d4 Binary files /dev/null and b/docs/readme/de-DE/docs/static/screenshot-sales.png differ diff --git a/docs/readme/es-ES/README.md b/docs/readme/es-ES/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e3dbb312ec8c2766d80d96b3abb5797a0d7fc6ac --- /dev/null +++ b/docs/readme/es-ES/README.md @@ -0,0 +1,382 @@ +

    + + Imagen de portada APITable + +

    + +

    + + + Entorno de desarrollo de Gitpod APITable + + + Idioma de TypeScript, NestJS Framework + + Idioma de Java, Spring Framework + + + + + + + + + + + + +
    + + + Insignia de licencia APITable AGPL + + + + + + + + + + + + + +

    + +

    + English + | + Français + | + Español + | + Deutsch + | + 简体中文 + | + 繁體中文 + | + 日本語 +

    + +## ✨ Inicio rápido + +> APITable es actualmente un `Trabajo en progreso`. +> +> Publicaremos la primera versión a finales de enero de 2023. +> +> Únete a [Discord](https://discord.gg/TwNb9nfdBU) o [Twitter](https://twitter.com/apitable_com) para mantenerte en contacto.Si solo quieres probar APITable[^info], haz clic aquí para [⚡Gitpod Online Demo](https://gitpod.io/#https://github.com/apitable/apitable). + +Si desea instalar APITable en su entorno de computación local o en la nube, consulte [💾 Instalación](#installation) + +Si quieres configurar tu entorno de desarrollo local, lee nuestra [🧑● 💻 Developer Guide](./docs/contribute/developer-guide.md) + +## 🔥 Características + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Colaboración en tiempo real + + Formulario automático +
    + + + + + + + +
    + Primer Panel de API + + Enlaces ilimitados de tabla cruzada +
    + + + + + + + +
    + Permisos de poderosas filas/columnas + + Incrustar +
    + + + + + + + +
    + +APITable ofrece una gama de características asombrosas, desde lo personal hasta lo personal. + +- Pila de tecnología avanzada y código abierto + - `La colaboración en tiempo real` permite a varios usuarios editar juntos en tiempo real, o simultáneamente con el algoritmo `Transformación operacional (OT)`. + - Interfaz de hoja de cálculo de base de datos extremadamente suave y fácil de usar en ` Motor de renderizado`. + - Arquitectura nativa de base de datos: Cambios / Operación / Acción / Instantánea y así sucesivamente. + - **100k+** filas de datos con colaboración en tiempo real. + - Acceso completo a la API, desde `Datos` hasta `Metadatos`. + - Vínculo de tabla de una dirección / Bi-dirección y `Enlaces cruzados infinitos` + - Idiomas de programación y marco amigables con la comunidad, TypeScript ([NextJS](https://nextjs.org/) + [NestJS](https://nestjs.com/)) y Java ([Spring Boot](https://spring.io/projects/spring-boot)) +- Bonito y rico Database-Hoja de cálculo UI + - `CRUD`: Crear, leer, actualizar, eliminar las tablas, columnas y filas + - `Operaciones de campos`: ordenar, filtro, agrupar, ocultar/desocultar, ajuste de altura. + - `Espacio basado en`: Utilice espacios de trabajo separados en lugar de la estructura basada en la aplicación/Base, haga posible el enlace de tablas ilimitadas. + - `Modo oscuro` y personalización del tema disponible. + - `7 tipos de vista`: vista en cuadrícula (hoja de datos) / Vista de galería / Mapa mental / vista Kanban / Vista completa de Gantt / Vista de calendario + - Panel de API en un clic +- Baterías incluidas + - Plantillas oficiales incorporadas en 10 o más. + - Automatización de robot y personalización disponibles. + - Panel de BI + - Formulario generado por un solo clic + - Página compartible e incrustable. + - Soporte múltiple. + - Integración con n8n.io / Zapier / Appsmith... y más. +- Excelente extensibilidad + - Extensible `Widget System` con más de 20 oficiales widgets de código abierto. + - Gráfico personalizable & Gráfico & Panel de control + - Tipos de columnas de datos personalizables + - Fórmulas personalizables + - Acciones de Robot de Automatización personalizables. +- Permisos de grado empresarial + - `Mirror`, convierte una vista en una réplica para implementar el permiso de fila. + - Activa `Permiso de Columna` a través de una operación muy simple. + - Carpetas / Subcarpetas / Permiso de archivos. + - Carpetas de estructura de árbol y nodo personalizable (archivo); + - Gestión de equipos & Estructura de la organización. +- Características de la empresa: + - SAML + - Inicio de sesión único (SST) + - Auditoría + - Copia de seguridad automática de la base de datos + - Exportador de datos + - Marca de agua +- .... + +Con widgets y plugins extensibles, puedes añadir más características. + +## 💥 Usar Casos + +¿Por qué debe conocer APITable para su próximo software? + +- Como software de supergestión + - Gestión de proyectos flexible & Tareas / Gestión de incidencias. + - Gestión de Líder de Mercadeo. + - CRM más flexible y conectable. + - Inteligencia de Negocio Flexible (BI). + - Formularios y encuestas amigables con las personas + - ERP flexible. + - Plataforma de código bajo y sin código. + - ...y más, APITable pone 1000 programas en su bolsillo. +- Como infraestructura de base de datos visual + - **Inserte** APITable en sus propias interfaces de software. + - Base de datos visual con REST API. + - Panel de administración. + - Gestión central de la configuración. + - Base de datos todo en uno que **conecte todo** su software. + - ...y más, APITable conecta todo. +- También, es de código abierto y extensible + +## :✫ ving_hearts: orientado a API + +#### API UI Panel + +Al hacer clic en el botón `API` en la esquina derecha se mostrará el Panel de la API + +#### Consulta similar a SQL + +APITable proporcionará un lenguaje de consulta (DQL) en la hoja de datos para consultar el contenido de su hoja de cálculo. + +## 💝 amigable con el incrustado + +#### Compartir e integrar + +Comparte tu tabla o carpeta de la hoja de datos. Los inserte copiando y pegando scripts HTML. + +#### Incrustación lista para empresas + +[APITable.com](https://apitable.com) proporciona más características de Incrustación listas para empresas para valores. + +## Instalación + +Asegúrate de que tienes `docker` & `curl` instalado localmente. + +Si tu ordenador ha instalado Docker Machine, abre tu terminal y ejecuta esto: + +``` +curl https://apitable.github.io/install.sh | bash +``` + +Luego abre [https://localhost:80](https://localhost:80) en tu navegador para visitarlo. (nombre de usuario predeterminado `admin@apitable.com` y contraseña `Apitable2022`) + +Si quieres configurar tu entorno de desarrollo local, lee nuestra [🧑● 💻 Developer Guide](./docs/contribute/developer-guide.md) + +## 🧑 💻 Contribuyendo + +Bienvenido, y gracias por su interés en contribuir a APITable! + +Hay muchas maneras en las que puedes contribuir, más allá de escribir código. + +Puede leer las [Directrices de contribución](./CONTRIBUTING.md) de este repositorio para aprender a contribuir. + +Aquí hay una guía rápida para ayudarte a contribuir a APITable. + +### Medio ambiente de desarrollo + +Aprende cómo configurar tu entorno local, en nuestra [Guía para desarrolladores](./docs/contribute/developer-guide.md). + +### Flujo de trabajo Git básico + +Aquí hay un flujo de trabajo general APITable git: + +1. Cree un problema y describa las características que desea -> [problemas APITables](https://github.com/apitable/apitable/issues) +2. Fork este proyecto -> [Fork proyecto APITable](https://github.com/apitable/apitable/fork) +3. Crea tu rama de características (`git checkout -b my-new-feature`) +4. Comprueba tus cambios (`git commit -am 'Añadir algunas características'`) +5. Publica la rama (`git push origin my-new-feature`) +6. Crear una nueva Pull Request -> [Crear pull request a través de bifurcaciones](https://github.com/apitable/apitable/compare) + +### Convenciones de trabajo + +Utilizar estas convenciones comunes: + +- ¿Cuál es nuestro modelo de sucursal Git? ¿Cuál es nuestro modelo de sucursal Git? [Gitflow](https://nvie.com/posts/a-successful-git-branching-model/) +- ¿Cómo colaborar en sus proyectos de bifurcación? ¿Cómo colaborar en sus proyectos de bifurcación? [Github Flow](https://docs.github.com/en/get-started/quickstart/github-flow) +- ¿Cómo escribir un buen mensaje de confirmación? ¿Cómo escribir un buen mensaje de confirmación? [Compromisos convencionales](https://www.conventionalcommits.org/) +- ¿Cuál es nuestro formato de registro de cambios? ¿Cuál es nuestro formato de registro de cambios? [Mantener el registro de cambios](https://keepachangelog.com/en/1.0.0/) +- ¿Cómo versionar y etiquetar? [Versionado semántico](https://semver.org/) [Versionado semántico](https://semver.org/) +- ¿Qué es la guía de código de Java? [Java Coding Guideline](https://google.github.io/styleguide/javaguide.html) | [Intellij IDEA Plugin](https://plugins.jetbrains.com/plugin/8527) +- ¿Qué es la guía de código de TypeScript? ¿Qué es la guía de código de TypeScript? -> [Guía de estilo de TypeScript](https://google.github.io/styleguide/tsguide.html) | [ESLint](https://www.npmjs.com/package/@typescript-eslint/eslint-plugin) + +### Documentaciones + +- [Centro de ayuda](https://help.apitable.com/) +- [👩‍💻 Centro de Desarrolladores](https://developers.apitable.com/) + - [► Documentos de API REST](https://developers.apitable.com/api/introduction/) + - SDK Widget (Próximamente...) + - Scripting (Próximamente...) + +## :autovía: Hoja de ruta + +### Características futuras + +- Constructor de Interfaz de código pesado +- Componentes de documentación de terceros incrustables +- Idiomas específicos del dominio de SQL +- Como IdP +- Características Web 3 +- ... + +### Las versiones Hosted y Enterprise ofrecen características avanzadas + +- Como identificador; +- SAML +- Inicio de sesión simple +- Auditoría +- Copia de seguridad de base de datos +- Marca de agua + +Para más información, por favor contáctenos en . + +## 👫 Participar + +### ¿Qué es la guía de código de Java? [Guía de código Java](https://github.com/alibaba/Alibaba-Java-Coding-Guidelines) | [Plugin Intellij IDEA](https://plugins.jetbrains.com/plugin/10046-alibaba-java-coding-guidelines) + +- Creemos que `la base de datos es la piedra angular` de todo el software. +- Creemos que crear una base de datos visual `con una interfaz de usuario rica y fácil para todos` puede reducir la dificultad de la industria de software e incrementar la adopción de digitalización en el mundo. +- Creemos que el trabajo `APITable` puede `Empujar Seres Humanos hacia adelante`. + +### ¡Estamos contratando de forma remota! + +Siempre buscamos buenos talentos para APITable: + +- **Desarrollador Full-stack**: Tienes experiencia con React, NestJS, TypeScript, Spring Boot, Java, Terraform. Y le gusta escribir código de alta calidad con documentación clara y pruebas unitarias. +- **Desarrollador back-end**: Tiene experiencia con NestJS, TypeScript, Spring Boot, Java, SQL, Kubernetes, Terraform. Y le gusta escribir código de alta calidad con documentación clara y pruebas unitarias. +- **Desarrollador front-end**: Tiene experiencia con React, NextJS, TypeScript, WebPack. Y le gusta escribir código de alta calidad con documentación clara y pruebas unitarias. + +Independientemente del tiempo y condiciones, si desea involucrarse en el equipo de APITable, no dudes en enviar tu CV a [talent@apitable. om](mailto:talent@apitable.com). + +## 📺 Captura de pantalla + +

    + Imagen de captura de pantalla APITable +

    +

    + Imagen de captura de pantalla APITable +

    +

    + Imagen de captura de pantalla APITable +

    +

    + Imagen de captura de pantalla APITable +

    +

    + Imagen de captura de pantalla APITable +

    +

    + Imagen de captura de pantalla APITable +

    +

    + Imagen de captura de pantalla APITable +

    +

    + Imagen de captura de pantalla APITable +

    +

    + Imagen de captura de pantalla APITable +

    +

    + Imagen de captura de pantalla APITable +

    + +## 🥰 Licencia + +> Este repositorio contiene el código fuente para la edición de código abierto de APITable, publicado bajo el AGPL. +> +> Si quieres ejecutar tu propia copia de APITable o contribuir al desarrollo, entonces este es el lugar para ti. +> +> Vea [LICENSACIÓN](./LICENSING.md) para más detalles. +> +> Si quieres usar APITable en línea, entonces no necesitas ejecutar este código, ofrecemos una versión alojada de la aplicación en [APITable. om](https://apitable.com) que optimizó para el acelerador global. + +
    + +[^info]: Licenciado con AGPL-3.0. Diseñado por [APITable Ltd](https://apitable.com). diff --git a/docs/readme/es-ES/docs/contribute/developer-guide.md b/docs/readme/es-ES/docs/contribute/developer-guide.md new file mode 100644 index 0000000000000000000000000000000000000000..d7a48f8ffed9459842fa05594d38604885581b38 --- /dev/null +++ b/docs/readme/es-ES/docs/contribute/developer-guide.md @@ -0,0 +1,132 @@ +# Guía de Desarrollador + +Esta guía te ayuda a empezar a desarrollar APITable. + +## Dependencias + +Asegúrese de que tiene instaladas las siguientes dependencias y lenguajes de programación antes de configurar su entorno de desarrollador: + +- `git` +- [acoplador](https://docs.docker.com/engine/install/) +- [docker-compose v2](https://docs.docker.com/engine/install/) +- `hacer` +- [sdkman](https://sdkman.io/): para instalar `java`, Java SDK 8 +- [nvm](https://github.com/nvm-sh/nvm): para instalar `node`, NodeJS v16.15.0 + + +### Lenguaje de programación + +Si está usando macOS o Linux. Recomendamos instalar el lenguaje de programación con el administrador de SDK `sdkman` y `nvm`. + +```bash +# rápido install nvm +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install. h | bash +# rápido install sdkman +curl -s "https://get.sdkman.io" | bash +# install nodejs +nvm install 16. 5.0 && nvm usar 16.15. && corepack habilitar +# instalar java development kit +sdk install java 8. .342-amzn && sdk usar java 8.0.342-amzn +``` + +### macOS + +Recomendamos usar [Homebrew](https://brew.sh/) para instalar cualquier dependencia faltante: + +```bash +## necesario +brew install git +brew install --cask docker +brew install make +``` + +### Linux + +En CentOS / RHEL u otra distribución Linux con `yum` + +```bash +sudo yum install git +sudo yum install make +``` + +En Ubuntu / Debian u otra distribución Linux con `apt` + +```bash +sudo apt update +sudo apt install git +sudo apt install make +``` + + +### Ventanas + +Si está ejecutando APITable en Windows 10/11, le recomendamos instalar [Docker Desktop en Windows](https://docs.docker.com/desktop/install/windows-install/), [Ubuntu en WSL](https://ubuntu.com/wsl) y [Terminal de Windows](https://aka.ms/terminal), Puede obtener más información sobre el Subsistema de Windows para Linux (WSL) en [el sitio oficial](https://learn.microsoft.com/en-us/windows/wsl). + +Instalar dependencias faltantes en Ubuntu usando `apt`: + +```bash +sudo apt update +sudo apt install git +sudo apt install make +``` + + +## Construir herramienta + +Utilizamos `make` como nuestra entrada central de herramientas de construcción que impulsa otras herramientas de construcción como `gradle` / `npm` / `yarn`. + +Así que solo puedes introducir `make` comando y ver todos los comandos de construcción: + +```bash +hacer +``` + +![hacer captura de pantalla de comandos](../static/make.png) + + + +## Iniciar entorno de desarrollo + +APITable consta de 4 procesos: + +1. servidor-backend +2. sala-servidor +3. servidor-socket +4. servidor web + +Para iniciar el entorno de desarrollo localmente, ejecute estos comandos: + +```bash +# iniciar bases de datos en dockers +make dataenv + +# install dependencies +make install + +#start backend-server +make run # enter 1 + +# y luego cambiar a un nuevo terminal +# start room-server +make run # enter 2 + +# y luego cambiar a un nuevo terminal +# start socket-server +make run # enter 3 + +# y luego cambiar a un nuevo terminal +# start web-server +make run # enter 4 + +``` + + + + +## IDE + +Le recomendamos que utilice `Visual Studio Code` o `Intellij IDEA` para su IDE. + +APITable ha preparado estas dos configuraciones de depuración del IDE. + +Simplemente abra el directorio raíz de APITable's con IDE. diff --git a/docs/readme/es-ES/docs/static/cover.png b/docs/readme/es-ES/docs/static/cover.png new file mode 100644 index 0000000000000000000000000000000000000000..729256d74f3d38cd93db4f786f0c5c57367d6507 Binary files /dev/null and b/docs/readme/es-ES/docs/static/cover.png differ diff --git a/docs/readme/es-ES/docs/static/feature-api-first-panel.gif b/docs/readme/es-ES/docs/static/feature-api-first-panel.gif new file mode 100644 index 0000000000000000000000000000000000000000..0dceee8ccd1f3b07ac64a5cffd6ea546b6d87d55 Binary files /dev/null and b/docs/readme/es-ES/docs/static/feature-api-first-panel.gif differ diff --git a/docs/readme/es-ES/docs/static/feature-apipanel.gif b/docs/readme/es-ES/docs/static/feature-apipanel.gif new file mode 100644 index 0000000000000000000000000000000000000000..cc829499a82ee5019c981b01a4aa15441f280302 Binary files /dev/null and b/docs/readme/es-ES/docs/static/feature-apipanel.gif differ diff --git a/docs/readme/es-ES/docs/static/feature-embed.gif b/docs/readme/es-ES/docs/static/feature-embed.gif new file mode 100644 index 0000000000000000000000000000000000000000..9edd44049941d2345804f71f61b34a1327df4ed2 Binary files /dev/null and b/docs/readme/es-ES/docs/static/feature-embed.gif differ diff --git a/docs/readme/es-ES/docs/static/feature-form.gif b/docs/readme/es-ES/docs/static/feature-form.gif new file mode 100644 index 0000000000000000000000000000000000000000..bea7e8a35f27ba0d88f57e55c0e96f5ed4cc30fc Binary files /dev/null and b/docs/readme/es-ES/docs/static/feature-form.gif differ diff --git a/docs/readme/es-ES/docs/static/feature-permissions.gif b/docs/readme/es-ES/docs/static/feature-permissions.gif new file mode 100644 index 0000000000000000000000000000000000000000..456b3c588ab1026933846729d29ae02c942eb725 Binary files /dev/null and b/docs/readme/es-ES/docs/static/feature-permissions.gif differ diff --git a/docs/readme/es-ES/docs/static/feature-realtime.gif b/docs/readme/es-ES/docs/static/feature-realtime.gif new file mode 100644 index 0000000000000000000000000000000000000000..a2fc86b1e50ca971c0b9751088bf14e360ede30b Binary files /dev/null and b/docs/readme/es-ES/docs/static/feature-realtime.gif differ diff --git a/docs/readme/es-ES/docs/static/feature-unlimited-cross-table-links.gif b/docs/readme/es-ES/docs/static/feature-unlimited-cross-table-links.gif new file mode 100644 index 0000000000000000000000000000000000000000..b2d08fe1766ac832299a2db4c4be13526153b91c Binary files /dev/null and b/docs/readme/es-ES/docs/static/feature-unlimited-cross-table-links.gif differ diff --git a/docs/readme/es-ES/docs/static/logo.svg b/docs/readme/es-ES/docs/static/logo.svg new file mode 100644 index 0000000000000000000000000000000000000000..33c93bb8dbf0fc3430cb0749da1a75ef0f283774 --- /dev/null +++ b/docs/readme/es-ES/docs/static/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/readme/es-ES/docs/static/make.png b/docs/readme/es-ES/docs/static/make.png new file mode 100644 index 0000000000000000000000000000000000000000..c997c1c7b453bde0940451f6ef239fc7c42c2b43 Binary files /dev/null and b/docs/readme/es-ES/docs/static/make.png differ diff --git a/docs/readme/es-ES/docs/static/screenshot-api-panel.png b/docs/readme/es-ES/docs/static/screenshot-api-panel.png new file mode 100644 index 0000000000000000000000000000000000000000..622ceadd5e3f27c8a632c461f687bd6ca9e9467a Binary files /dev/null and b/docs/readme/es-ES/docs/static/screenshot-api-panel.png differ diff --git a/docs/readme/es-ES/docs/static/screenshot-auto-form.png b/docs/readme/es-ES/docs/static/screenshot-auto-form.png new file mode 100644 index 0000000000000000000000000000000000000000..232d98bdfa209f60d347ad89cd28a4db93568710 Binary files /dev/null and b/docs/readme/es-ES/docs/static/screenshot-auto-form.png differ diff --git a/docs/readme/es-ES/docs/static/screenshot-automation.png b/docs/readme/es-ES/docs/static/screenshot-automation.png new file mode 100644 index 0000000000000000000000000000000000000000..2356614a073f24c8ac2a7ccf7a7fbcfeee3ccf89 Binary files /dev/null and b/docs/readme/es-ES/docs/static/screenshot-automation.png differ diff --git a/docs/readme/es-ES/docs/static/screenshot-extensible.png b/docs/readme/es-ES/docs/static/screenshot-extensible.png new file mode 100644 index 0000000000000000000000000000000000000000..334a29e54f82c027c3a4af3815850bc93e2248f6 Binary files /dev/null and b/docs/readme/es-ES/docs/static/screenshot-extensible.png differ diff --git a/docs/readme/es-ES/docs/static/screenshot-hr.png b/docs/readme/es-ES/docs/static/screenshot-hr.png new file mode 100644 index 0000000000000000000000000000000000000000..3ec97c6cf99a69bc3a3aefcfc77a1c124508e53b Binary files /dev/null and b/docs/readme/es-ES/docs/static/screenshot-hr.png differ diff --git a/docs/readme/es-ES/docs/static/screenshot-it.png b/docs/readme/es-ES/docs/static/screenshot-it.png new file mode 100644 index 0000000000000000000000000000000000000000..f0592b3a8880f1f7551bc59cd184eb804bb7a27e Binary files /dev/null and b/docs/readme/es-ES/docs/static/screenshot-it.png differ diff --git a/docs/readme/es-ES/docs/static/screenshot-marketing.png b/docs/readme/es-ES/docs/static/screenshot-marketing.png new file mode 100644 index 0000000000000000000000000000000000000000..5ff160f4f6f3fbed223b22fd7aefb45a00469ddc Binary files /dev/null and b/docs/readme/es-ES/docs/static/screenshot-marketing.png differ diff --git a/docs/readme/es-ES/docs/static/screenshot-permissions.png b/docs/readme/es-ES/docs/static/screenshot-permissions.png new file mode 100644 index 0000000000000000000000000000000000000000..9e3d88ec98de008ce2fef8a4bd48e8d971b9bedf Binary files /dev/null and b/docs/readme/es-ES/docs/static/screenshot-permissions.png differ diff --git a/docs/readme/es-ES/docs/static/screenshot-realtime.png b/docs/readme/es-ES/docs/static/screenshot-realtime.png new file mode 100644 index 0000000000000000000000000000000000000000..40f22a47a180fae4ba21ca6fbd7f3247ecb0c4ac Binary files /dev/null and b/docs/readme/es-ES/docs/static/screenshot-realtime.png differ diff --git a/docs/readme/es-ES/docs/static/screenshot-sales.png b/docs/readme/es-ES/docs/static/screenshot-sales.png new file mode 100644 index 0000000000000000000000000000000000000000..efd35700dd39d27e1f89b357e1f0ffde373944d4 Binary files /dev/null and b/docs/readme/es-ES/docs/static/screenshot-sales.png differ diff --git a/docs/readme/fr-FR/README.md b/docs/readme/fr-FR/README.md new file mode 100644 index 0000000000000000000000000000000000000000..62d877378b9b61efd4ff5d4c0655ace3b3263cdb --- /dev/null +++ b/docs/readme/fr-FR/README.md @@ -0,0 +1,382 @@ +

    + + Image de couverture APITable + +

    + +

    + + + Environnement de développement de Gitpod APITable + + + TypeScript Language, Framework NestJS + + Langue Java, Framework Spring + + + + + + + + + + + + +
    + + + Badge de Licence APITable AGPL + + + + + + + + + + + + + +

    + +

    + English + | + Français + | + Español + | + Deutsch + | + 简体中文 + | + 繁體中文 + | + 日本語 +

    + +## ✨ Démarrage rapide + +> APITable est actuellement un `travail en cours`. +> +> Nous publierons la première version fin janvier 2023. +> +> Rejoignez [Discord](https://discord.gg/TwNb9nfdBU) ou [Twitter](https://twitter.com/apitable_com) pour rester en contact.Si vous voulez juste essayer APITable[^info], cliquez ici pour [⚡Gitpod démo en ligne](https://gitpod.io/#https://github.com/apitable/apitable). + +Si vous voulez installer APITable dans votre environnement de calcul local ou cloud, voir [💾 Installation](#installation) + +Si vous voulez configurer votre environnement de développement local, lisez notre [🧑 💻 Developer Guide](./docs/contribute/developer-guide.md) + +## 🔥 Fonctionnalités + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Collaboration en temps réel + + Formulaire automatique +
    + + + + + + + +
    + Premier panneau d'API + + Liens de table croisée illimités +
    + + + + + + + +
    + Autorisations puissantes/colonnes + + Intégrer +
    + + + + + + + +
    + +APITable offre une gamme de fonctionnalités étonnantes, du personnel à l'entreprise. + +- Pile de technologie avancée et open-source + - `La collaboration en temps réel` permet à plusieurs utilisateurs d'éditer ensemble en temps réel, ou simultanément avec l'Algorithme `Transformation Opérationnelle (OT)`. + - Interface de feuille de calcul de base de données extrêmement lisse, conviviale et super rapide dans ` Moteur de rendu`. + - Architecture native de la base de données: Changeset / Opération / Action / Snapshot et ainsi de suite. + - **100k+** lignes de données avec collaboration en temps réel. + - Accès à l'API Full-stack, de `Data` à `Métadonnées`. + - Lien de table direction / Bi-direction et `Liens Croisés infinis` + - langages de programmation et cadre conviviaux, TypeScript ([NextJS](https://nextjs.org/) + [NestJS](https://nestjs.com/)) et Java ([Spring Boot](https://spring.io/projects/spring-boot)) +- Feuille de calcul de la base de données magnifique et riche + - `CRUD`: créer, lire, mettre à jour, supprimer les tableaux, colonnes et lignes + - `Opérations de champs`: tri, filtre, regroupement, masquer/démasquer, réglage de hauteur. + - `Basé sur l'espace`: Utilisez des espaces de travail séparés à la place de la structure basée sur l'application/Base, rendant possible la liaison illimitée de tableaux. + - `Mode sombre` et la personnalisation du thème disponible. + - `7 Types de vues`: Vue en grille (Datasheet) / Galerie Vue / Vue Mindmap / Vue Kanban / Vue en pleine fonctionnalité Gantt Vue / Vue calendrier + - Panneau API en un clic +- Batteries incluses + - Modèles officiels intégrés à plus de 10 modèles. + - Robot Automation et personnalisation disponibles. + - Tableau de bord BI + - Formulaire généré automatiquement en un clic + - Page partageable et intégrable. + - Prise en charge multi-langues. + - Intégration avec n8n.io / Zapier / Appsmith... et plus. +- Excellente extensibilité + - Extensible `Widget System` avec plus de 20 fonctionnaires widgets open-source. + - Graphique personnalisable & Graphique & Tableau de bord + - Types de colonnes de données personnalisables + - Formulaires personnalisables + - Actions personnalisables des robots d'automatisation. +- Autorisations pour les entreprises + - `Miroir`, transformer une vue en miroir pour implémenter la permission de la ligne. + - Activez `Autorisation de Colonnes` grâce à une opération très simple. + - Dossiers / Sous-dossiers / Autorisations de fichiers. + - Dossiers de structure d'arborescence et noeud personnalisable (fichier); + - Gestion d'équipe & Structure d'organisation. +- Fonctionnalités de l'entreprise: + - SAML + - Single Sign-On (SSO) + - Audit + - Sauvegarde automatique de la base de données + - Exportateur de données + - Filigrane +- .... + +Avec des widgets extensibles et des plugins, vous pouvez ajouter plus de fonctionnalités. + +## 💥 Cas d'utilisation + +Pourquoi vous devez connaître APITable pour votre prochain logiciel ? + +- En tant que super logiciel de gestion + - Gestion de projet flexible & Tâches / Gestion des problèmes. + - Gestion des Responsables Marketing. + - CRM le plus flexible et connectable. + - Flexible Business Intelligence (BI). + - Enquêtes et formulaires conviviaux + - ERP flexible. + - Plateforme de code bas et sans code. + - ...et plus, APITable met 1000 logiciels dans votre poche. +- En tant qu'infrastructure de base de données visuelle + - **Intégrez** APITable à vos propres interfaces utilisateur logicielles. + - Base de données visuelle avec API REST. + - Tableau de bord de l'administration. + - Gestion de la configuration centrale. + - Base de données d'entreprise tout-en-un qui **connecte tous vos logiciels**. + - ...et plus, APITable relie tout. +- En outre, il est open source et extensible + +## 💞 orienté API + +#### API UI Panel + +Cliquer sur le bouton `API` dans le coin droit affichera le panneau API + +#### Requête similaire à SQL + +APITable fournira un langage de requête de feuilles de données (DQL) pour interroger le contenu de votre feuille de calcul de la base de données. + +## 💝 amical + +#### Partagez et intégrez + +Partagez votre table ou dossier de fiches techniques. Intégrez-les en copiant et collant des scripts HTML. + +#### Embedding prêt pour les entreprises + +[APITable.com](https://apitable.com) fournit plus de fonctionnalités d'Embedding pour les valeurs mobilières prêtes à l'entreprise. + +## Installation + +Assurez-vous que vous avez `docker` & `curl` installé localement. + +Si vous avez installé Docker Machine, ouvrez votre terminal et lancez ceci : + +``` +curl https://apitable.github.io/install.sh | bash +``` + +Puis ouvrez [https://localhost:80](https://localhost:80) dans votre navigateur pour le visiter. (nom d'utilisateur par défaut `admin@apitable.com` et mot de passe `Apitable2022`) + +Si vous voulez configurer votre environnement de développement local, lisez notre [🧑 💻 Developer Guide](./docs/contribute/developer-guide.md) + +## 💻 Contribution + +Bienvenue, et merci de votre intérêt à contribuer à APITable! + +Il y a de nombreuses façons de contribuer, au-delà de l'écriture de code. + +Vous pouvez lire les [directives de contribution de ce dépôt](./CONTRIBUTING.md) pour apprendre comment contribuer. + +Voici un guide rapide pour vous aider à contribuer à APITable. + +### Environnement de développement + +Apprenez comment configurer votre environnement local, dans notre [Guide de Développement](./docs/contribute/developer-guide.md). + +### Git workflow basique + +Voici un flux de travail général APITable git : + +1. Créez un problème et décrivez les fonctionnalités que vous souhaitez -> [Problèmes APITables](https://github.com/apitable/apitable/issues) +2. Fork this project -> [Fork APITable project](https://github.com/apitable/apitable/fork) +3. Créez votre branche de fonctionnalité (`git checkout -b mon-nouvelle fonctionnalité`) +4. Commettre vos modifications (`git commit -am 'Ajouter quelques fonctionnalités'`) +5. Publier la branche (`git push origine mon-nouveau-fonctionnalité`) +6. Créer une nouvelle Pull Request -> [Créer une pull request à travers les forks](https://github.com/apitable/apitable/compare) + +### Conventions professionnelles + +Utilisez ces conventions communes APITables : + +- Quel est notre modèle de branchement Git ? [Gitflow](https://nvie.com/posts/a-successful-git-branching-model/) +- Comment collaborer à vos projets de fork ? [Github Flow](https://docs.github.com/en/get-started/quickstart/github-flow) +- Comment écrire un bon message de commit ? [Commits conventionnels](https://www.conventionalcommits.org/) +- Quel est notre format de changelog ? [Garder le changelog](https://keepachangelog.com/en/1.0.0/) +- Comment versionner et étiqueter ? [Versioning sémantique](https://semver.org/) +- Qu'est-ce que la directive de codage Java ? [Règles de codage Java](https://github.com/alibaba/Alibaba-Java-Coding-Guidelines) | [Plugin Intellij IDEA](https://plugins.jetbrains.com/plugin/10046-alibaba-java-coding-guidelines) +- Qu'est-ce que les règles de codage TypeScript ? -> [Guide de style TypeScript](https://google.github.io/styleguide/tsguide.html) | [ESLint](https://www.npmjs.com/package/@typescript-eslint/eslint-plugin) + +### Documentations + +- [Centre d'aide](https://help.apitable.com/) +- [👩‍💻 Centre de développement](https://developers.apitable.com/) + - [ Docs REST API](https://developers.apitable.com/api/introduction/) + - Widget SDK (à venir...) + - Scripting (à venir...) + +## 🛣 Feuille de route + +### Fonctionnalités futures + +- Constructeur d'interface de code lourd +- Composants de documentation de tiers incorporables +- Langues spécifiques au domaine de type SQL +- En tant qu'IdP +- Fonctionnalités Web 3 +- ... + +### Les versions hébergées et Entreprise offrent des fonctionnalités avancées + +- En tant qu'IdP ; +- SAML +- Authentification unique +- Audit +- Sauvegarde de la base de données +- Filigrane + +Pour plus d'informations, veuillez nous contacter à . + +## 👫 Get Involved + +### 🌏 Pourquoi créer APITable et open-source? + +- Nous croyons que la `base de données est la pierre angulaire` de tout le logiciel. +- Nous pensons que faire une base de données visuelle `avec une interface utilisateur riche et facile pour tout le monde` peut réduire la difficulté de l'industrie du logiciel et augmenter l'adoption de la numérisation dans le monde. +- Nous pensons que le travail open-sourcing `APITable` peut `pousser les êtres humains vers l'avant`. + +### Nous embauchons à distance ! + +Nous recherchons toujours de bons talents pour APITables : + +- **Développeur Full-stack**: Vous avez de l'expérience avec React, NestJS, TypeScript, Spring Boot, Java, Terraform. Et vous aimez écrire du code de haute qualité avec une documentation claire et des tests unitaires. +- **Développeur Back-end**: Vous avez de l'expérience avec NestJS, TypeScript, Spring Boot, Java, SQL, Kubernetes, Terraform. Et vous aimez écrire du code de haute qualité avec une documentation claire et des tests unitaires. +- **Développeur Front-end**: Vous avez de l'expérience avec React, NextJS, TypeScript, WebPack. Et vous aimez écrire du code de haute qualité avec une documentation claire et des tests unitaires. + +Indépendamment du temps et des conditions, si vous voulez vous impliquer dans l'équipe d'APITable, n'hésitez pas et envoyez votre CV à [talent@apitable. om](mailto:talent@apitable.com). + +## 📺 Capture d'écran + +

    + Image de capture d'écran APITable +

    +

    + Image de capture d'écran APITable +

    +

    + Image de capture d'écran APITable +

    +

    + Image de capture d'écran APITable +

    +

    + Image de capture d'écran APITable +

    +

    + Image de capture d'écran APITable +

    +

    + Image de capture d'écran APITable +

    +

    + Image de capture d'écran APITable +

    +

    + Image de capture d'écran APITable +

    +

    + Image de capture d'écran APITable +

    + +## 🥰 Licence + +> Ce dépôt contient le code source de l'édition Open Source d'APITable, publiée sous l'AGPL. +> +> Si vous voulez faire tourner votre propre copie d'APITable ou contribuer au développement, alors c'est l'endroit pour vous. +> +> Voir [LICENCE](./LICENSING.md) pour plus de détails. +> +> Si vous voulez utiliser APITable en ligne alors vous n'avez pas besoin d'exécuter ce code, nous offrons une version hébergée de l'application à [APITable. om](https://apitable.com) qui a optimisé pour l'accélération globale. + +
    + +[^info]: Licencié avec AGPL-3.0. Conçu par [APITable Ltd](https://apitable.com). diff --git a/docs/readme/fr-FR/docs/contribute/developer-guide.md b/docs/readme/fr-FR/docs/contribute/developer-guide.md new file mode 100644 index 0000000000000000000000000000000000000000..ae09807cf9a0632e3f9e2ad113ba50618eaf2f74 --- /dev/null +++ b/docs/readme/fr-FR/docs/contribute/developer-guide.md @@ -0,0 +1,132 @@ +# Guide du développeur + +Ce guide vous aide à commencer à développer APITable. + +## Dépendances + +Assurez-vous d'avoir les dépendances suivantes et les langages de programmation installés avant de configurer votre environnement de développeur: + +- `git` +- [docker](https://docs.docker.com/engine/install/) +- [docker-compose v2](https://docs.docker.com/engine/install/) +- `faire` +- [sdkman](https://sdkman.io/): pour installer `java`, Java SDK 8 +- [nvm](https://github.com/nvm-sh/nvm): pour installer `noeud`, NodeJS v16.15.0 + + +### Langue de programmation + +Si vous utilisez macOS ou Linux. Nous recommandons d'installer le langage de programmation avec le gestionnaire SDK `sdkman` et `nvm`. + +```bash +# quick install nvm +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install. h | bash +# installation rapide sdkman +curl -s "https://get.sdkman.io" | bash +# installer nodejs +nvm install 16. 5.0 && nvm utilise 16.15. && corepack active +# installer le kit de développement java +sdk install java 8. .342-amzn && sdk utilise java 8.0.342-amzn +``` + +### macOS + +Nous recommandons d'utiliser [Homebrew](https://brew.sh/) pour installer les dépendances manquantes : + +```bash +## nécessaire +brew install git +brew install --cask docker +brew install make make +``` + +### Linux + +Sur CentOS / RHEL ou toute autre distribution Linux avec `yum` + +```bash +sudo yum install git +sudo yum install make make +``` + +Sur Ubuntu / Debian ou toute autre distribution Linux avec `apt` + +```bash +sudo apt update +sudo apt install git +sudo apt install make +``` + + +### Fenêtres + +Si vous exécutez APITable sous Windows 10/11, nous vous recommandons d'installer [Docker Desktop sous Windows](https://docs.docker.com/desktop/install/windows-install/), [Ubuntu sur WSL](https://ubuntu.com/wsl) et [Terminal Windows](https://aka.ms/terminal), Vous pouvez en savoir plus sur Windows Subsystem pour Linux (WSL) dans [le site officiel](https://learn.microsoft.com/en-us/windows/wsl). + +Installer les dépendances manquantes sur Ubuntu en utilisant `apt`: + +```bash +sudo apt update +sudo apt install git +sudo apt install make +``` + + +## Outil de construction + +Nous utilisons `make` comme entrée d'outil de construction centrée qui conduit d'autres outils de construction comme `gradle` / `npm` / `yarn`. + +Donc vous pouvez simplement entrer la commande `make` et voir toutes les commandes de build : + +```bash +faire +``` + +![faire une capture d'écran de commande](../static/make.png) + + + +## Démarrer l'environnement de développement + +APITable se compose de 4 processus : + +1. serveur backend +2. room-server +3. socket-server +4. serveur web + +Pour démarrer l'environnement de développement localement, exécutez ces commandes : + +```bash +# démarrer les bases de données dans dockers +make dataenv + +# install dependencies +make install + +#start backend-server +make run # enter 1 + +# puis basculer vers un nouveau terminal +# start room-server +make run # enter 2 + +# and then switch to a new terminal +# start socket-server +make run # enter 3 + +# and then switch to a new terminal +# start web-server +make run # enter 4 + +``` + + + + +## IDE + +Nous vous recommandons d'utiliser `Visual Studio Code` ou `Intellij IDEA` pour votre IDE. + +APITable a préparé ces deux configurations de débogage d'IDE. + +Ouvrez simplement le répertoire racine d'APITable avec IDE. diff --git a/docs/readme/fr-FR/docs/static/cover.png b/docs/readme/fr-FR/docs/static/cover.png new file mode 100644 index 0000000000000000000000000000000000000000..729256d74f3d38cd93db4f786f0c5c57367d6507 Binary files /dev/null and b/docs/readme/fr-FR/docs/static/cover.png differ diff --git a/docs/readme/fr-FR/docs/static/feature-api-first-panel.gif b/docs/readme/fr-FR/docs/static/feature-api-first-panel.gif new file mode 100644 index 0000000000000000000000000000000000000000..0dceee8ccd1f3b07ac64a5cffd6ea546b6d87d55 Binary files /dev/null and b/docs/readme/fr-FR/docs/static/feature-api-first-panel.gif differ diff --git a/docs/readme/fr-FR/docs/static/feature-apipanel.gif b/docs/readme/fr-FR/docs/static/feature-apipanel.gif new file mode 100644 index 0000000000000000000000000000000000000000..cc829499a82ee5019c981b01a4aa15441f280302 Binary files /dev/null and b/docs/readme/fr-FR/docs/static/feature-apipanel.gif differ diff --git a/docs/readme/fr-FR/docs/static/feature-embed.gif b/docs/readme/fr-FR/docs/static/feature-embed.gif new file mode 100644 index 0000000000000000000000000000000000000000..9edd44049941d2345804f71f61b34a1327df4ed2 Binary files /dev/null and b/docs/readme/fr-FR/docs/static/feature-embed.gif differ diff --git a/docs/readme/fr-FR/docs/static/feature-form.gif b/docs/readme/fr-FR/docs/static/feature-form.gif new file mode 100644 index 0000000000000000000000000000000000000000..bea7e8a35f27ba0d88f57e55c0e96f5ed4cc30fc Binary files /dev/null and b/docs/readme/fr-FR/docs/static/feature-form.gif differ diff --git a/docs/readme/fr-FR/docs/static/feature-permissions.gif b/docs/readme/fr-FR/docs/static/feature-permissions.gif new file mode 100644 index 0000000000000000000000000000000000000000..456b3c588ab1026933846729d29ae02c942eb725 Binary files /dev/null and b/docs/readme/fr-FR/docs/static/feature-permissions.gif differ diff --git a/docs/readme/fr-FR/docs/static/feature-realtime.gif b/docs/readme/fr-FR/docs/static/feature-realtime.gif new file mode 100644 index 0000000000000000000000000000000000000000..a2fc86b1e50ca971c0b9751088bf14e360ede30b Binary files /dev/null and b/docs/readme/fr-FR/docs/static/feature-realtime.gif differ diff --git a/docs/readme/fr-FR/docs/static/feature-unlimited-cross-table-links.gif b/docs/readme/fr-FR/docs/static/feature-unlimited-cross-table-links.gif new file mode 100644 index 0000000000000000000000000000000000000000..b2d08fe1766ac832299a2db4c4be13526153b91c Binary files /dev/null and b/docs/readme/fr-FR/docs/static/feature-unlimited-cross-table-links.gif differ diff --git a/docs/readme/fr-FR/docs/static/logo.svg b/docs/readme/fr-FR/docs/static/logo.svg new file mode 100644 index 0000000000000000000000000000000000000000..33c93bb8dbf0fc3430cb0749da1a75ef0f283774 --- /dev/null +++ b/docs/readme/fr-FR/docs/static/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/readme/fr-FR/docs/static/make.png b/docs/readme/fr-FR/docs/static/make.png new file mode 100644 index 0000000000000000000000000000000000000000..c997c1c7b453bde0940451f6ef239fc7c42c2b43 Binary files /dev/null and b/docs/readme/fr-FR/docs/static/make.png differ diff --git a/docs/readme/fr-FR/docs/static/screenshot-api-panel.png b/docs/readme/fr-FR/docs/static/screenshot-api-panel.png new file mode 100644 index 0000000000000000000000000000000000000000..622ceadd5e3f27c8a632c461f687bd6ca9e9467a Binary files /dev/null and b/docs/readme/fr-FR/docs/static/screenshot-api-panel.png differ diff --git a/docs/readme/fr-FR/docs/static/screenshot-auto-form.png b/docs/readme/fr-FR/docs/static/screenshot-auto-form.png new file mode 100644 index 0000000000000000000000000000000000000000..232d98bdfa209f60d347ad89cd28a4db93568710 Binary files /dev/null and b/docs/readme/fr-FR/docs/static/screenshot-auto-form.png differ diff --git a/docs/readme/fr-FR/docs/static/screenshot-automation.png b/docs/readme/fr-FR/docs/static/screenshot-automation.png new file mode 100644 index 0000000000000000000000000000000000000000..2356614a073f24c8ac2a7ccf7a7fbcfeee3ccf89 Binary files /dev/null and b/docs/readme/fr-FR/docs/static/screenshot-automation.png differ diff --git a/docs/readme/fr-FR/docs/static/screenshot-extensible.png b/docs/readme/fr-FR/docs/static/screenshot-extensible.png new file mode 100644 index 0000000000000000000000000000000000000000..334a29e54f82c027c3a4af3815850bc93e2248f6 Binary files /dev/null and b/docs/readme/fr-FR/docs/static/screenshot-extensible.png differ diff --git a/docs/readme/fr-FR/docs/static/screenshot-hr.png b/docs/readme/fr-FR/docs/static/screenshot-hr.png new file mode 100644 index 0000000000000000000000000000000000000000..3ec97c6cf99a69bc3a3aefcfc77a1c124508e53b Binary files /dev/null and b/docs/readme/fr-FR/docs/static/screenshot-hr.png differ diff --git a/docs/readme/fr-FR/docs/static/screenshot-it.png b/docs/readme/fr-FR/docs/static/screenshot-it.png new file mode 100644 index 0000000000000000000000000000000000000000..f0592b3a8880f1f7551bc59cd184eb804bb7a27e Binary files /dev/null and b/docs/readme/fr-FR/docs/static/screenshot-it.png differ diff --git a/docs/readme/fr-FR/docs/static/screenshot-marketing.png b/docs/readme/fr-FR/docs/static/screenshot-marketing.png new file mode 100644 index 0000000000000000000000000000000000000000..5ff160f4f6f3fbed223b22fd7aefb45a00469ddc Binary files /dev/null and b/docs/readme/fr-FR/docs/static/screenshot-marketing.png differ diff --git a/docs/readme/fr-FR/docs/static/screenshot-permissions.png b/docs/readme/fr-FR/docs/static/screenshot-permissions.png new file mode 100644 index 0000000000000000000000000000000000000000..9e3d88ec98de008ce2fef8a4bd48e8d971b9bedf Binary files /dev/null and b/docs/readme/fr-FR/docs/static/screenshot-permissions.png differ diff --git a/docs/readme/fr-FR/docs/static/screenshot-realtime.png b/docs/readme/fr-FR/docs/static/screenshot-realtime.png new file mode 100644 index 0000000000000000000000000000000000000000..40f22a47a180fae4ba21ca6fbd7f3247ecb0c4ac Binary files /dev/null and b/docs/readme/fr-FR/docs/static/screenshot-realtime.png differ diff --git a/docs/readme/fr-FR/docs/static/screenshot-sales.png b/docs/readme/fr-FR/docs/static/screenshot-sales.png new file mode 100644 index 0000000000000000000000000000000000000000..efd35700dd39d27e1f89b357e1f0ffde373944d4 Binary files /dev/null and b/docs/readme/fr-FR/docs/static/screenshot-sales.png differ diff --git a/docs/readme/ja-JP/README.md b/docs/readme/ja-JP/README.md new file mode 100644 index 0000000000000000000000000000000000000000..6d327d86de0a4ced87c623df63e38f033c2fb35b --- /dev/null +++ b/docs/readme/ja-JP/README.md @@ -0,0 +1,382 @@ +

    + + 適用可能なカバー画像 + +

    + +

    + + + APITable Gitpod開発環境 + + + TypeScript Language, NestJS Framework + + Java言語、Spring Framework + + + + + + + + + + + + +
    + + + APITable License Badge AGPL + + + + + + + + + + + + + +

    + +

    + English + | + Français + | + Español + | + Deutsch + | + 简体中文 + | + 繁體中文 + | + 日本語 +

    + +## ✨ クイックスタート + +> APITableは現在 `作業中`です。 +> +> 最初のリリースは2023年1月下旬に公開されます。 +> +> [Discord](https://discord.gg/TwNb9nfdBU) や [Twitter](https://twitter.com/apitable_com) に参加して連絡を取り合いましょう。APITable[^info]を試してみたい場合は、 [⚡Gitpod Online Demo](https://gitpod.io/#https://github.com/apitable/apitable) をクリックしてください。 + +ローカルまたはクラウドコンピューティング環境にAPITableをインストールしたい場合は、 [💾 インストール](#installation) を参照してください。 + +ローカルの開発環境を設定したい場合は、 [🧑 💻 開発者ガイド](./docs/contribute/developer-guide.md) をご覧ください。 + +## 🔥 機能 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + リアルタイムでのコラボレーション + + 自動フォーム +
    + + + + + + + +
    + API第1パネル + + 無制限のクロステーブルリンク +
    + + + + + + + +
    + 強力な行/列の権限 + + 埋め込み +
    + + + + + + + +
    + +APITableは、個人から企業まで、さまざまな素晴らしい機能を提供します。 + +- 高度なテクノロジースタックとオープンソース + - `リアルタイムコラボレーション` では、複数のユーザーがリアルタイムで、または `運用変換(OT)` アルゴリズムと同時に編集することができます。 + - ` レンダリングエンジン` で、非常にスムーズで使いやすく、超高速なデータベース表計算インターフェイス。 + - データベースネイティブアーキテクチャ:Changeset / Operation / Action / Snapshotなど。 + - **リアルタイムのコラボレーションによる100k+** データ行。 + - `データ` から `メタデータ` まで、フルスタック API アクセス。 + - 一方向/双方向テーブルリンクと `無限クロスリンク` + - コミュニティに優しいプログラミング言語とフレームワーク, TypeScript ([NextJS](https://nextjs.org/) + [NestJS](https://nestjs.com/)) and Java ([Spring Boot](https://spring.io/projects/spring-boot)) +- 美しくリッチなデータベース表計算用UI + - `CRUD`: 作成、読み込み、更新、テーブル、列、行の削除 + - `フィールド操作`: ソート、フィルター、グループ化、非表示/非表示、高さ設定。 + - `Space based`: アプリ/ベース構造の代わりに、分離されたワークスペースを使用し、テーブルを無制限にリンクできるようにします。 + - `ダークモード` とテーマのカスタマイズが可能です。 + - `7 種類のビュータイプ`: グリッドビュー (データシート) / ギャラリービュー / マインドマップビュー / カンバンビュー / フル機能 ガントビュー / カレンダー ビュー + - ワンクリックAPIパネル +- バッテリーが付属しています + - 10以上の公式テンプレートを内蔵しています。 + - ロボットの自動化と利用可能なカスタマイズ。 + - BI ダッシュボード + - ワンクリック自動生成フォーム + - 共有可能で埋め込み可能なページ + - 多言語対応。 + - n8n.io / Zapier / Appsmith...などとの統合。 +- 優れた拡張 + - 20名以上の職員のオープンソースウィジェットを備えた拡張可能な `ウィジェット システム`。 + - カスタマイズ可能なグラフ & チャート & ダッシュボード + - カスタマイズ可能なデータ列のタイプ + - カスタマイズ可能な数式 + - カスタマイズ可能な自動化ロボットアクション。 +- エンタープライズグレードの権限 + - `ミラー`, ビューをミラーにして、Row Permissionを実装する。 + - 非常に簡単な操作で `列権限` を有効にします。 + - フォルダ/サブフォルダー / ファイルのパーミッション。 + - ツリー構造フォルダとカスタマイズ可能なノード (ファイル); + - チーム管理 & 組織構造 +- エンタープライズ機能: + - SAML + - シングルサインオン(SSO) + - Audit + - データベースの自動バックアップ + - データエクスポート + - ウォーターマーク +- .... + +拡張可能なウィジェットとプラグインを使用すると、さらに機能を追加できます。 + +## 💥 ユース・ケース + +次のソフトウェアでAPITableを知る必要があるのはなぜですか? + +- スーパーマネージメントソフトウェアとして + - 柔軟なプロジェクト管理 & タスク/課題管理 + - マーケティングリード管理 + - 最も柔軟で接続可能なCRM。 + - 柔軟なビジネスインテリジェンス(BI)。 + - 人気のフォームと調査 + - 柔軟なERP。 + - ローコードとノーコードプラットフォーム。 + - ...その他、APITable はあなたのポケットに 1000 ソフトウェアを入れています。 +- 視覚データベースのインフラストラクチャとして + - **ご自身のソフトウェアUIに** APITableを埋め込む。 + - REST API を使用したビジュアルデータベース。 + - 管理ダッシュボード + - 中央の構成管理 + - すべてのソフトウェアを **** 接続するオールインワンエンタープライズデータベース + - ...その他、APITableはすべてを接続します。 +- また、オープンソースで拡張可能 + +## 💞 API 指定 + +#### API UI Panel + +右隅にある `API` ボタンをクリックすると、API パネル が表示されます。 + +#### SQL-like query + +APITableは、データベースのスプレッドシートの内容を照会するためのDatasheet Query Language(DQL)を提供します。 + +## 💝 フレンドリーな + +#### 共有して埋め込み + +データシートのテーブルまたはフォルダを共有します。 HTMLスクリプトをコピーして貼り付けることで埋め込みます。 + +#### エンタープライズ対応の埋め込み + +[APITable.com](https://apitable.com) は、より多くのエンタープライズ対応の組み込み機能をセキュリティに提供します。 + +## インストール + +ローカルに `docker` & `curl` がインストールされていることを確認してください。 + +Docker Machineがインストールされている場合は、ターミナルを開いて以下を実行してください。 + +``` +curl https://apitable.github.io/install.sh | bash +``` + +ブラウザで [https://localhost:80](https://localhost:80) を開きます。 (デフォルトのユーザー名 `admin@apitable.com` とパスワード `Apitable2022`) + +ローカルの開発環境を設定したい場合は、 [🧑 💻 開発者ガイド](./docs/contribute/developer-guide.md) をご覧ください。 + +## 🧑 💻 コントリビュート + +APITableに貢献していただき、ありがとうございます! + +コードを書く以外にも、貢献できる方法はたくさんあります。 + +このリポジトリの [コントリビューションガイドライン](./CONTRIBUTING.md) を読んで、コントリビューションの方法を学ぶことができます。 + +APITableに貢献するためのクイックガイドはこちらです。 + +### 開発環境 + +ローカル環境の設定方法については、 [開発者ガイド](./docs/contribute/developer-guide.md) をご覧ください。 + +### Git ワークフロー 基本 + +一般的な APITable git ワークフローは次のとおりです。 + +1. 課題を作成し、必要な機能を記述してください -> [APITableの問題](https://github.com/apitable/apitable/issues) +2. Fork this project -> [Fork APITable project](https://github.com/apitable/apitable/fork) +3. フィーチャーブランチを作成する (`git checkout -b my-new-feature`) +4. 変更をコミットする (`git commit -am '機能を追加'`) +5. ブランチを公開する (`git push origin my-new-feature`) +6. 新しいプルリクエストを作成する -> [フォーク間でプルリクエストを作成します](https://github.com/apitable/apitable/compare) + +### 作業条件 + +これらの一般的な規約を使用できます。 + +- Gitのブランチモデルは何ですか? [Gitflow](https://nvie.com/posts/a-successful-git-branching-model/) +- フォークプロジェクトでコラボレーションする方法は? [Github Flow](https://docs.github.com/en/get-started/quickstart/github-flow) +- 良いコミットメッセージを書く方法は? [従来のコミット](https://www.conventionalcommits.org/) +- 更新履歴のフォーマットは何ですか? [変更履歴を保持](https://keepachangelog.com/en/1.0.0/) +- バージョン管理とタグ付け方法は? [セマンティックバージョン](https://semver.org/) +- Javaコーディングガイドラインとは何ですか? [Java Coding Guideline](https://github.com/alibaba/Alibaba-Java-Coding-Guidelines) | [Intellij IDEA プラグイン](https://plugins.jetbrains.com/plugin/10046-alibaba-java-coding-guidelines) +- TypeScript コーディングガイドラインとは何ですか? -> [TypeScript Style Guide](https://google.github.io/styleguide/tsguide.html) | [ESLint](https://www.npmjs.com/package/@typescript-eslint/eslint-plugin) + +### 文書 + +- [ヘルプセンター](https://help.apitable.com/) +- [👩‍💻 開発者センター](https://developers.apitable.com/) + - [ REST API ドキュメント](https://developers.apitable.com/api/introduction/) + - ウィジェット SDK (近日公開予定...) + - スクリプト (近日公開...) + +## 🛣 Roadmap + +### 今後の機能 + +- ヘビーコードインターフェースビルダー +- 埋め込み可能なサードパーティ製ドキュメントコンポーネント +- SQLのようなドメイン固有の言語 +- IdPとして +- Web 3の機能 +- ... + +### Hosted and Enterpriseのバージョンは高度な機能を提供します + +- IDPとして; +- SAML +- シングルサインオン +- Audit +- データベースバックアップ +- ウォーターマーク + +詳細については、 までご連絡ください。 + +## 👫 参加する + +### 🌏 なぜAPITableでオープンソースを作るのですか? + +- 我々は、 `データベースがすべてのソフトウェアの礎石` であると信じています。 +- 我々は、`誰もが簡単に使えるリッチなユーザーインターフェイスのビジュアルデータベース`を作ることが、ソフトウェア産業の難しさを軽減し、世界のデジタル化の普及を促進することができると考えています。 +- 私たちは、オープンソーシング `APITable` が `Push Human Beings Forward`を行うことができると信じています。 + +### 私たちはリモートで雇用しています! + +私たちは常にAPITableのために良い才能を探しています: + +- **Full-stack 開発者**: React, NestJS, TypeScript, Spring Boot, Java, Terraformの経験があります。 そして、明確なドキュメントと単体テストで高品質のコードを書くのが好きです。 +- **バックエンド開発者**: NestJS, TypeScript, Spring Boot, Java, SQL, Kubernetes, Terraformの経験があります。 そして、明確なドキュメントと単体テストで高品質のコードを書くのが好きです。 +- **フロントエンド開発者**: React, NextJS, TypeScript, WebPackの経験があります。 そして、明確なドキュメントと単体テストで高品質のコードを書くのが好きです。 + +時間や条件に関係なく、APITableのチームに参加したい方は、遠慮なくまでCVをお送りください。 + +## 📺 スクリーンショット + +

    + APITableスクリーンショット画像 +

    +

    + APITableスクリーンショット画像 +

    +

    + APITableスクリーンショット画像 +

    +

    + APITableスクリーンショット画像 +

    +

    + APITableスクリーンショット画像 +

    +

    + APITableスクリーンショット画像 +

    +

    + APITableスクリーンショット画像 +

    +

    + APITableスクリーンショット画像 +

    +

    + APITableスクリーンショット画像 +

    +

    + APITableスクリーンショット画像 +

    + +## 🥰 ライセンス + +> このリポジトリには、AGPL に基づいてリリースされた APITable の Open Source エディションのソースコードが含まれています。 +> +> あなた自身のAPITableのコピーを実行したい場合、または開発に貢献したい場合は、これはあなたのための場所です。 +> +> 詳細は [ライセンス](./LICENSING.md) を参照してください。 +> +> APITableをオンラインで使用したい場合は、このコードを実行する必要はありません。我々は、グローバルアクセラレータに最適化されたアプリのホスティング版を[APITable.com](https://apitable.com)で提供しています。 + +
    + +[^info]: AGPL-3.0 でライセンスされます。 [APITable Ltd](https://apitable.com). diff --git a/docs/readme/ja-JP/docs/contribute/developer-guide.md b/docs/readme/ja-JP/docs/contribute/developer-guide.md new file mode 100644 index 0000000000000000000000000000000000000000..7daa66c9a2b2839ababdb86a07dc46f14730acb2 --- /dev/null +++ b/docs/readme/ja-JP/docs/contribute/developer-guide.md @@ -0,0 +1,132 @@ +# 開発者ガイド + +このガイドは、APITableの開発を開始するのに役立ちます。 + +## 依存関係 + +開発者環境を設定する前に、以下の依存関係とプログラミング言語がインストールされていることを確認してください。 + +- `git` +- [docker](https://docs.docker.com/engine/install/) +- [docker-compose v2](https://docs.docker.com/engine/install/) +- `作る` +- [sdkman](https://sdkman.io/): `java`, Java SDK 8 をインストールする +- [nvm](https://github.com/nvm-sh/nvm): インストール用 `ノード`, NodeJS v16.15.0 + + +### プログラミング言語 + +macOS または Linux を使用している場合。 SDKマネージャー `sdkman` と `nvm` を使用してプログラミング言語をインストールすることをお勧めします。 + +```bash +# quick install nvm +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install.sh | bash +# quick install sdkman +curl -s "https://get.sdkman.io" | bash +# install nodejs +nvm install 16.15.0 && nvm use 16.15.0 && corepack enable +# install java development kit +sdk install java 8.0.342-amzn && sdk use java 8.0.342-amzn +``` + +### macOS + +不足している依存関係をインストールするには、 [Homebrew](https://brew.sh/) を使用することをお勧めします: + +```bash +## necessary required +brew install git +brew install --cask docker +brew install make +``` + +### Linux + +CentOS / RHEL または `yum` を含む Linux ディストリビューションでは + +```bash +sudo yum install git +sudo yum install make +``` + +Ubuntu / Debian または他の Linux ディストリビューションで `apt` + +```bash +sudo apt update +sudo apt install git +sudo apt install make +``` + + +### Windows + +If you are running APITable on Windows 10/11, we recommend installing [Docker Desktop on Windows](https://docs.docker.com/desktop/install/windows-install/), [Ubuntu on WSL](https://ubuntu.com/wsl) and [Windows Terminal](https://aka.ms/terminal), You can learn more about Windows Subsystem for Linux (WSL) in [the official site](https://learn.microsoft.com/en-us/windows/wsl). + +`apt` を使用してUbuntuに不足している依存関係をインストールする : + +```bash +sudo apt update +sudo apt install git +sudo apt install make +``` + + +## ビルドツール + +`make` を中心としたビルドツールエントリとして `gradle` / `npm` / `yarn` のような他のビルドツールを駆動します。 + +ですから、 `make` コマンドを入力するだけで、すべてのビルドコマンドを確認できます。 + +```bash +作る +``` + +![コマンドのスクリーンショット作成](../static/make.png) + + + +## 開発環境を開始 + +APITableは以下の4つのプロセスで構成されています。 + +1. バックエンドサーバー +2. room-server +3. ソケットサーバー +4. web-server + +開発環境をローカルで起動するには、以下のコマンドを実行します。 + +```bash +# start databases in dockers +make dataenv + +# install dependencies +make install + +#start backend-server +make run # enter 1 + +# and then switch to a new terminal +# start room-server +make run # enter 2 + +# and then switch to a new terminal +# start socket-server +make run # enter 3 + +# and then switch to a new terminal +# start web-server +make run # enter 4 + +``` + + + + +## IDE + +ご使用の IDE に `Visual Studio Code` または `Intellij IDEA` を使用することをお勧めします。 + +APITableは、これらの2つのIDEのデバッグ設定を用意しています。 + +IDEでAPITableのルートディレクトリを開くだけです。 diff --git a/docs/readme/ja-JP/docs/static/cover.png b/docs/readme/ja-JP/docs/static/cover.png new file mode 100644 index 0000000000000000000000000000000000000000..729256d74f3d38cd93db4f786f0c5c57367d6507 Binary files /dev/null and b/docs/readme/ja-JP/docs/static/cover.png differ diff --git a/docs/readme/ja-JP/docs/static/feature-api-first-panel.gif b/docs/readme/ja-JP/docs/static/feature-api-first-panel.gif new file mode 100644 index 0000000000000000000000000000000000000000..0dceee8ccd1f3b07ac64a5cffd6ea546b6d87d55 Binary files /dev/null and b/docs/readme/ja-JP/docs/static/feature-api-first-panel.gif differ diff --git a/docs/readme/ja-JP/docs/static/feature-apipanel.gif b/docs/readme/ja-JP/docs/static/feature-apipanel.gif new file mode 100644 index 0000000000000000000000000000000000000000..cc829499a82ee5019c981b01a4aa15441f280302 Binary files /dev/null and b/docs/readme/ja-JP/docs/static/feature-apipanel.gif differ diff --git a/docs/readme/ja-JP/docs/static/feature-embed.gif b/docs/readme/ja-JP/docs/static/feature-embed.gif new file mode 100644 index 0000000000000000000000000000000000000000..9edd44049941d2345804f71f61b34a1327df4ed2 Binary files /dev/null and b/docs/readme/ja-JP/docs/static/feature-embed.gif differ diff --git a/docs/readme/ja-JP/docs/static/feature-form.gif b/docs/readme/ja-JP/docs/static/feature-form.gif new file mode 100644 index 0000000000000000000000000000000000000000..bea7e8a35f27ba0d88f57e55c0e96f5ed4cc30fc Binary files /dev/null and b/docs/readme/ja-JP/docs/static/feature-form.gif differ diff --git a/docs/readme/ja-JP/docs/static/feature-permissions.gif b/docs/readme/ja-JP/docs/static/feature-permissions.gif new file mode 100644 index 0000000000000000000000000000000000000000..456b3c588ab1026933846729d29ae02c942eb725 Binary files /dev/null and b/docs/readme/ja-JP/docs/static/feature-permissions.gif differ diff --git a/docs/readme/ja-JP/docs/static/feature-realtime.gif b/docs/readme/ja-JP/docs/static/feature-realtime.gif new file mode 100644 index 0000000000000000000000000000000000000000..a2fc86b1e50ca971c0b9751088bf14e360ede30b Binary files /dev/null and b/docs/readme/ja-JP/docs/static/feature-realtime.gif differ diff --git a/docs/readme/ja-JP/docs/static/feature-unlimited-cross-table-links.gif b/docs/readme/ja-JP/docs/static/feature-unlimited-cross-table-links.gif new file mode 100644 index 0000000000000000000000000000000000000000..b2d08fe1766ac832299a2db4c4be13526153b91c Binary files /dev/null and b/docs/readme/ja-JP/docs/static/feature-unlimited-cross-table-links.gif differ diff --git a/docs/readme/ja-JP/docs/static/logo.svg b/docs/readme/ja-JP/docs/static/logo.svg new file mode 100644 index 0000000000000000000000000000000000000000..33c93bb8dbf0fc3430cb0749da1a75ef0f283774 --- /dev/null +++ b/docs/readme/ja-JP/docs/static/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/readme/ja-JP/docs/static/make.png b/docs/readme/ja-JP/docs/static/make.png new file mode 100644 index 0000000000000000000000000000000000000000..c997c1c7b453bde0940451f6ef239fc7c42c2b43 Binary files /dev/null and b/docs/readme/ja-JP/docs/static/make.png differ diff --git a/docs/readme/ja-JP/docs/static/screenshot-api-panel.png b/docs/readme/ja-JP/docs/static/screenshot-api-panel.png new file mode 100644 index 0000000000000000000000000000000000000000..622ceadd5e3f27c8a632c461f687bd6ca9e9467a Binary files /dev/null and b/docs/readme/ja-JP/docs/static/screenshot-api-panel.png differ diff --git a/docs/readme/ja-JP/docs/static/screenshot-auto-form.png b/docs/readme/ja-JP/docs/static/screenshot-auto-form.png new file mode 100644 index 0000000000000000000000000000000000000000..232d98bdfa209f60d347ad89cd28a4db93568710 Binary files /dev/null and b/docs/readme/ja-JP/docs/static/screenshot-auto-form.png differ diff --git a/docs/readme/ja-JP/docs/static/screenshot-automation.png b/docs/readme/ja-JP/docs/static/screenshot-automation.png new file mode 100644 index 0000000000000000000000000000000000000000..2356614a073f24c8ac2a7ccf7a7fbcfeee3ccf89 Binary files /dev/null and b/docs/readme/ja-JP/docs/static/screenshot-automation.png differ diff --git a/docs/readme/ja-JP/docs/static/screenshot-extensible.png b/docs/readme/ja-JP/docs/static/screenshot-extensible.png new file mode 100644 index 0000000000000000000000000000000000000000..334a29e54f82c027c3a4af3815850bc93e2248f6 Binary files /dev/null and b/docs/readme/ja-JP/docs/static/screenshot-extensible.png differ diff --git a/docs/readme/ja-JP/docs/static/screenshot-hr.png b/docs/readme/ja-JP/docs/static/screenshot-hr.png new file mode 100644 index 0000000000000000000000000000000000000000..3ec97c6cf99a69bc3a3aefcfc77a1c124508e53b Binary files /dev/null and b/docs/readme/ja-JP/docs/static/screenshot-hr.png differ diff --git a/docs/readme/ja-JP/docs/static/screenshot-it.png b/docs/readme/ja-JP/docs/static/screenshot-it.png new file mode 100644 index 0000000000000000000000000000000000000000..f0592b3a8880f1f7551bc59cd184eb804bb7a27e Binary files /dev/null and b/docs/readme/ja-JP/docs/static/screenshot-it.png differ diff --git a/docs/readme/ja-JP/docs/static/screenshot-marketing.png b/docs/readme/ja-JP/docs/static/screenshot-marketing.png new file mode 100644 index 0000000000000000000000000000000000000000..5ff160f4f6f3fbed223b22fd7aefb45a00469ddc Binary files /dev/null and b/docs/readme/ja-JP/docs/static/screenshot-marketing.png differ diff --git a/docs/readme/ja-JP/docs/static/screenshot-permissions.png b/docs/readme/ja-JP/docs/static/screenshot-permissions.png new file mode 100644 index 0000000000000000000000000000000000000000..9e3d88ec98de008ce2fef8a4bd48e8d971b9bedf Binary files /dev/null and b/docs/readme/ja-JP/docs/static/screenshot-permissions.png differ diff --git a/docs/readme/ja-JP/docs/static/screenshot-realtime.png b/docs/readme/ja-JP/docs/static/screenshot-realtime.png new file mode 100644 index 0000000000000000000000000000000000000000..40f22a47a180fae4ba21ca6fbd7f3247ecb0c4ac Binary files /dev/null and b/docs/readme/ja-JP/docs/static/screenshot-realtime.png differ diff --git a/docs/readme/ja-JP/docs/static/screenshot-sales.png b/docs/readme/ja-JP/docs/static/screenshot-sales.png new file mode 100644 index 0000000000000000000000000000000000000000..efd35700dd39d27e1f89b357e1f0ffde373944d4 Binary files /dev/null and b/docs/readme/ja-JP/docs/static/screenshot-sales.png differ diff --git a/docs/readme/zh-CN/README.md b/docs/readme/zh-CN/README.md new file mode 100644 index 0000000000000000000000000000000000000000..b3ae11f49e1bf7b303efd0add7084c056f6effbf --- /dev/null +++ b/docs/readme/zh-CN/README.md @@ -0,0 +1,387 @@ +

    + + APITable封面图像 + +

    + +

    + + + APITable Gitpod 开发环境 + + + TypeScript 语言,NestJS 框架 + + Java 语言,Spring 框架 + + + + + + + + + + + + +
    + + + APITable License Badge AGPL + + + + + + + + + + + + + +

    + +

    + English + | + Français + | + Español + | + Deutsch + | + 简体中文 + | + 繁體中文 + | + 日本語 +

    + +## ✨ 快速开始 + +> APITable 目前正在 `积极开发中`。 +> +> 我们将在 2023 年 2 月下旬公布第一份发行版本。 +> +> 加入 [Discord](https://discord.gg/TwNb9nfdBU) 或 [Twitter](https://twitter.com/apitable_com) 保持联系。如果您只是想尝试APITable[^info], 点击这里 [⚡️Gitpod 在线 Demo](https://gitpod.io/#https://github.com/apitable/apitable). + +如果您想要在本地或云端计算环境中安装 APITable ,请参阅 [💾 安装](#installation) + +如果你想要设置你的本地开发环境,请阅读我们的 [🧑‍💻 开发者指南 ](./docs/contribute/developer-guide.md) + +## 🔥 功能特性 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + 实时协作 + + 神奇表单 +
    + + + + + + + +
    + API 优先的面板 + + 无限跨表关联 +
    + + + + + + + +
    + 强大的行/列权限 + + 内嵌 +
    + + + + + + + +
    + +APITable 提供了一系列令人惊奇的功能,从个人到企业。 + +- 先进的技术栈并开源 + - `实时协作` 允许多个用户实时或与 `操作转换(OT)` 算法同时进行编辑。 + - 使用 `canvas` 渲染的极其流畅、用户友好、极其快速的数据库-电子表格界面。 + - 数据库本地架构:变更集/操作/动作/快照等等。 + - 实时协作 **100k+** 数据行 + - 从 `数据(Data)` 到 `元数据(MetaData)` 的全栈式API访问。 + - 单向/双向表链接和 `无限交叉链接` + - 社区友好的编程语言和框架,TypeScript ([NextJS](https://nextjs.org/) + [NestJS](https://nestjs.com/)) 和 Java ([Spring Boot](https://spring.io/projects/spring-boot)) +- 美观和功能齐全的多维表格UI界面 + - `CRUD`: 创建、浏览、更新、删除表、列和行 + - `字段操作`: 排序、过滤、分组、隐藏/取消隐藏、高度设置。 + - `基于空间(Space)`:使用分离的工作空间来代替基于 App/Base 的结构,使无限的表格连接成为可能。 + - `暗色模式` 和主题定制。 + - `7 种视图类型`: 网格视图(Datasheet) / 图库视图 / 脑图视图/ 看板视图 / 全功能网格视图 / 日历视图 + - 一键式 API 面板 +- 装备齐全 + - 内置的 10+ 个官方模板 + - 机器人自动化和自定义配置 + - 商业化智能(BI)仪表板 + - 一键式自动生成表单 + - 可共享和嵌入的页面 + - 多语言支持 + - 集成 n8n.io / Zapier / Appsmith... 及更多 +- 卓越的扩展性 + - 超过 20 个官方开源组件,以及可扩展的 `组件系统` + - 可自定义的图表 & 仪表板 + - 可自定义的数据列类型 + - 可自定义的公式 + - 可自定义的自动化机器人行为 +- 企业级权限 + - `镜像`, 将视图变成镜像以实现行权限 + - 通过非常简单的操作激活 `列权限` + - 文件夹/子文件夹/文件权限 + - 树结构文件夹和可自定义的节点(文件) + - 团队管理 & 组织架构 +- 企业级应用功能 + - SAML + - 单点登录(SSO) + - 审计 + - 数据库自动备份 + - 数据导出 + - 水印 +- .... + +使用可扩展的组件和插件,您可以添加更多功能。 + +## 💥 应用场景 + +为什么你必须知道 APITable 并作为你的下一个软件? + +- 作为超级管理软件 + - 灵活的项目管理 & 任务/问题管理 + - 营销潜在客户管理。 + - 最灵活和可连接的 CRM + - 灵活的 BI 图表系统 + - 便于人们使用的表格和调查表 + - 灵活的 ERP + - 低代码和无代码平台。 + - ...及更多, APITable 将 1000 个软件放入您的口袋中。 +- 作为一个可视化数据库基础设施 + - 将 APITable **嵌入** 到你自己的软件 UI 中 + - 带有 REST API 的可视化数据库 + - 管理员仪表板 + - 集中管理配置 + - 多合一的企业数据库,**连接你的所有** 软件 + - ...及更多, APITable 连接一切 +- 此外,它是开源和可扩展的 + +## 💞 面向 API + +#### API 面板 + +点击右角的 `API` 按钮将显示 API 面板 + +#### SQL式查询 + +APITable 将提供一个数据表查询语言(DQL)来查询您的数据库电子表格内容。 + +## 💝 嵌入友好 + +#### 分享和嵌入 + +分享您的数据表或文件夹。通过复制和粘贴 HTML 脚本嵌入它们。 + +#### 企业可用的嵌入 + +[APITable.com](https://apitable.com) 为证券业提供更多企业可用的嵌入功能。 + +## 安装 + +请确保您已在本地安装 `docker` & `curl` 。 + +如果您的计算机安装了 Docker ,打开您的终端并这样运行: + +``` +curl https://apitable.github.io/install.sh | bash +``` + +然后在您的浏览器中打开 [https://localhost:80](https://localhost:80) 访问它。(默认用户名 `admin@apitable.com` 和密码 `Apitable2022`) + +如果你想要设置你的本地开发环境,请阅读我们的 [🧑‍💻 开发者指南 ](./docs/contribute/developer-guide.md) + +## 🧑‍💻 贡献 + +欢迎并感谢您有兴趣为 APITable 作出贡献! + +除了编写代码,您还有许多方法可以做出贡献。 + +你可以阅读这个仓库的 [贡献指南](./CONTRIBUTING.md) 来学习如何贡献. + +这是一个帮助您为 APITable 作出贡献的快速指南。 + +### 开发环境 + +在我们的 [开发者指南](./docs/contribute/developer-guide.md) 中学习如何设置您的本地开发环境。 + +### Git 工作流基础 + +下面是通用的 APITable 工作流: + +1. 创建一个问题并描述您想要的功能 -> [APITable 问题](https://github.com/apitable/apitable/issues) +2. 派生此项目 -> [Fork APITable 项目](https://github.com/apitable/apitable/fork) +3. 创建您的功能分支(`git checkout -b my-new-feature`) +4. 提交您的更改(`git commit -am 'Add some features'`) +5. 推送分支 (`git push origin my-new-feature`) +6. 创建新的拉取请求 -> [通过派生创建拉取请求](https://github.com/apitable/apitable/compare) + +### 工作约定 + +APITable 可适用这些通用的约定: + +- 我们的 Git 分支模型是什么? [Gitflow](https://nvie.com/posts/a-successful-git-branching-model/) +- 如何在派生项目上进行合作? [Github Flow](https://docs.github.com/en/get-started/quickstart/github-flow) +- 如何写入好的提交消息? [Conventional Commits](https://www.conventionalcommits.org/) +- 我们的更新日志格式是什么? [Keep Changelog](https://keepachangelog.com/en/1.0.0/) +- 如何进行版本控制和标记? [语义化版本控制](https://semver.org/) +- Java 编码准则是什么? [Java 编码准则](https://github.com/alibaba/Alibaba-Java-Coding-Guidelines) | [Intellij IDEA 插件](https://plugins.jetbrains.com/plugin/10046-alibaba-java-coding-guidelines) +- TypeScript 编码准则是什么? -> [TypeScript 风格指南](https://google.github.io/styleguide/tsguide.html) | [ESLint](https://www.npmjs.com/package/@typescript-eslint/eslint-plugin) + +### 文档 + +- [帮助中心](https://help.apitable.com/) +- [👩‍💻 开发者中心](https://developers.apitable.com/) + - [REST API 文档](https://developers.apitable.com/api/introduction/) + - 小部件 SDK (即将到来...) + - 脚本(即将到来...) + +## 🛣 规划路线 + +### 未来的功能 + +- 重代码界面生成器 +- 可嵌入的第三方文档组件 +- 类似 SQL 的 DSL +- 作为一个 IdP +- Web 3 功能 +- ... + +### 托管服务版和企业版提供高级功能 + +- 作为一个 IdP +- SAML +- 单点登录 +- 审计 +- 数据库备份 +- 水印 + +欲了解更多信息,请通过 联系我们。 + +## 👫 参与进来 + +### 🌏 我们为什么要创建 APITable 并开源? + +- 我们认为 `数据库是所有软件的基石`。 +- 我们相信,为每个人制作一个具有丰富和简单用户界面的`可视化数据库`,可以降低软件行业的难度,提高世界数字化的应用。 +- 我们相信开源 `APITable` 可以 `推动人类前进` + +### 我们正在招聘远程工作的开发者! + +我们一直在为 APITable 寻找优秀人才: + +- **全栈开发者**: 有 React、NextJS、TypeScript、Spring Boot、Java、Terraform 的工作经验。并喜欢写高质量的代码,带有清晰的文档和单元测试。 +- **后端开发者**: 有 NestJS、TypeScript、Spring Boot、Java、SQL、Kubernetes、Terraform 的工作经验。并喜欢写高质量的代码,带有清晰的文档和单元测试。 +- **前端开发者**: 有 React、NextJS、TypeScript、WebPack 的工作经验。并喜欢写高质量的代码,带有清晰的文档和单元测试。 + +无论时间和条件如何,如果你想要加入 APITable 团队,请不要犹豫,将您的简历发送到 talent@apitable + +## 📺 截图 + +

    + 可用屏幕截图图像 +

    +

    + 可用屏幕截图图像 +

    +

    + 可用屏幕截图图像 +

    +

    + 可用屏幕截图图像 +

    +

    + 可用屏幕截图图像 +

    +

    + 可用屏幕截图图像 +

    +

    + 可用屏幕截图图像 +

    +

    + 可用屏幕截图图像 +

    +

    + 可用屏幕截图图像 +

    +

    + 可用屏幕截图图像 +

    + +## 🥰 许可协议 + + + +> 此仓库包含在 AGPL 下发布的 Open Source 版本的 APITable源代码。 +> +> 如果你想运行你自己的 APITable 副本或对发展作出贡献,那这里就是你的地方。 +> +> 详情请参阅 [LICENSING](./LICENSING.md) +> +> 如果您想要在线使用 APITable ,那么您不需要运行此代码。 我们在 [APITable 上提供一个托管的应用版本](https://apitable.com) 适合全球加速。 + +
    + + + +[^info]: + 使用AGPL-3.0授权。 由 [APITable Ltd](https://apitable.com)设计。 diff --git a/docs/readme/zh-CN/docs/contribute/developer-guide.md b/docs/readme/zh-CN/docs/contribute/developer-guide.md new file mode 100644 index 0000000000000000000000000000000000000000..626510b7eed2b310e2f74822a5fc19e7d14bfa21 --- /dev/null +++ b/docs/readme/zh-CN/docs/contribute/developer-guide.md @@ -0,0 +1,126 @@ +# 开发者指南 + +本指南帮助您开始开发 APITable 。 + +## 依赖 + +在设置你的开发者环境之前,请确保你已经安装了以下依赖和编程语言: + +- `git` +- [docker](https://docs.docker.com/engine/install/) +- [docker-compose v2](https://docs.docker.com/engine/install/) +- `make` +- [sdkman](https://sdkman.io/): 用于安装 `java`, Java SDK 8 +- [nvm](https://github.com/nvm-sh/nvm): 用于安装 `node`, NodeJS v16.15.0 + + +### 编程语言 + +如果您使用 macOS 或 Linux。 我们建议使用 SDK 管理器 `sdkman` 和 `nvm` 安装编程语言。 + +```bash +# 快速安装 nvm +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install.sh | bash +# 快速安装 sdkman +curl -s "https://get.sdkman.io" | bash +# 安装 Node.js +nvm install 16.15.0 && nvm use 16.15.0 && corepack enable +# 安装 java 开发包 +sdk install java 8.0.342-amzn && sdk use java 8.0.342-amzn +``` + +### macOS + +我们建议使用 [Homebrew](https://brew.sh/) 来安装任何缺失的依赖包: + +```bash +## 必要 +brew install git +brew install --cask docker +brew install make +``` + +### Linux + +在 CentOS / RHEL或其他 Linux 发行版使用 `yum` + +```bash +sudo yum install git +sudo yum install make +``` + +在 Ubuntu / Debian 或其他 Linux 发行版使用 `apt` + +```bash +sudo apt update +sudo apt install git +sudo apt install make +``` + + +### Windows + +如果您在 Windows 10/11 上运行 APITable,我们建议在 Windows 上安装 [Docker Desktop on Windows](https://docs.docker.com/desktop/install/windows-install/),[Ubuntu on WSL](https://ubuntu.com/wsl) 和 [Windows Terminal](https://aka.ms/terminal), 您可以在 [官方网站](https://learn.microsoft.com/en-us/windows/wsl) 了解更多关于 Windows WSL 的信息。

    + +然后就可以在 WSL 中使用 `apt` 在 Ubuntu 上安装缺少的依赖: + + +```bash +sudo apt update +sudo apt install git +sudo apt install make +``` + +## 构建工具 + +我们使用 `make` 作为我们的中央构建工具,来驱动其他构建工具,如 `gradle` / `npm` / `yarn` + +所以您可以只输入 `make` 命令并看到所有构建命令: + +```bash +make +``` + +![命令截图](../static/make.png) + +## 启动开发环境 + +APITable 由 4 个进程组成: + +1. backend-server +2. room-server +3. socket-server +4. web-server + +要启动本地开发环境,请运行这些命令: + +```bash +# 在 Docker 中启动数据库 +make dataenv + +# 安装依赖关系 +make install + +# 启动 backend-server +make run # 输入1 + +# 然后切换到新的终端 +# 启动 room-server +make run # 输入2 + +# 然后切换到新的终端 +# 启动 socket-server +make run # 输入3 + +# 然后切换到新的终端 +# 启动 web-server +make run # 输入4 +``` + +## 集成开发环境(IDE) + +我们建议您使用 `Visual Studio Code` 或 `Intellij IDEA` 作为您的 IDE。 + +APITable 已准备好这两个 IDE 的调试配置。 + +只需在 IDE 中打开 APITable 的根目录即可。 diff --git a/docs/readme/zh-CN/docs/static/cover.png b/docs/readme/zh-CN/docs/static/cover.png new file mode 100644 index 0000000000000000000000000000000000000000..729256d74f3d38cd93db4f786f0c5c57367d6507 Binary files /dev/null and b/docs/readme/zh-CN/docs/static/cover.png differ diff --git a/docs/readme/zh-CN/docs/static/feature-api-first-panel.gif b/docs/readme/zh-CN/docs/static/feature-api-first-panel.gif new file mode 100644 index 0000000000000000000000000000000000000000..0dceee8ccd1f3b07ac64a5cffd6ea546b6d87d55 Binary files /dev/null and b/docs/readme/zh-CN/docs/static/feature-api-first-panel.gif differ diff --git a/docs/readme/zh-CN/docs/static/feature-apipanel.gif b/docs/readme/zh-CN/docs/static/feature-apipanel.gif new file mode 100644 index 0000000000000000000000000000000000000000..cc829499a82ee5019c981b01a4aa15441f280302 Binary files /dev/null and b/docs/readme/zh-CN/docs/static/feature-apipanel.gif differ diff --git a/docs/readme/zh-CN/docs/static/feature-embed.gif b/docs/readme/zh-CN/docs/static/feature-embed.gif new file mode 100644 index 0000000000000000000000000000000000000000..9edd44049941d2345804f71f61b34a1327df4ed2 Binary files /dev/null and b/docs/readme/zh-CN/docs/static/feature-embed.gif differ diff --git a/docs/readme/zh-CN/docs/static/feature-form.gif b/docs/readme/zh-CN/docs/static/feature-form.gif new file mode 100644 index 0000000000000000000000000000000000000000..bea7e8a35f27ba0d88f57e55c0e96f5ed4cc30fc Binary files /dev/null and b/docs/readme/zh-CN/docs/static/feature-form.gif differ diff --git a/docs/readme/zh-CN/docs/static/feature-permissions.gif b/docs/readme/zh-CN/docs/static/feature-permissions.gif new file mode 100644 index 0000000000000000000000000000000000000000..456b3c588ab1026933846729d29ae02c942eb725 Binary files /dev/null and b/docs/readme/zh-CN/docs/static/feature-permissions.gif differ diff --git a/docs/readme/zh-CN/docs/static/feature-realtime.gif b/docs/readme/zh-CN/docs/static/feature-realtime.gif new file mode 100644 index 0000000000000000000000000000000000000000..a2fc86b1e50ca971c0b9751088bf14e360ede30b Binary files /dev/null and b/docs/readme/zh-CN/docs/static/feature-realtime.gif differ diff --git a/docs/readme/zh-CN/docs/static/feature-unlimited-cross-table-links.gif b/docs/readme/zh-CN/docs/static/feature-unlimited-cross-table-links.gif new file mode 100644 index 0000000000000000000000000000000000000000..b2d08fe1766ac832299a2db4c4be13526153b91c Binary files /dev/null and b/docs/readme/zh-CN/docs/static/feature-unlimited-cross-table-links.gif differ diff --git a/docs/readme/zh-CN/docs/static/logo.svg b/docs/readme/zh-CN/docs/static/logo.svg new file mode 100644 index 0000000000000000000000000000000000000000..33c93bb8dbf0fc3430cb0749da1a75ef0f283774 --- /dev/null +++ b/docs/readme/zh-CN/docs/static/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/readme/zh-CN/docs/static/make.png b/docs/readme/zh-CN/docs/static/make.png new file mode 100644 index 0000000000000000000000000000000000000000..c997c1c7b453bde0940451f6ef239fc7c42c2b43 Binary files /dev/null and b/docs/readme/zh-CN/docs/static/make.png differ diff --git a/docs/readme/zh-CN/docs/static/screenshot-api-panel.png b/docs/readme/zh-CN/docs/static/screenshot-api-panel.png new file mode 100644 index 0000000000000000000000000000000000000000..622ceadd5e3f27c8a632c461f687bd6ca9e9467a Binary files /dev/null and b/docs/readme/zh-CN/docs/static/screenshot-api-panel.png differ diff --git a/docs/readme/zh-CN/docs/static/screenshot-auto-form.png b/docs/readme/zh-CN/docs/static/screenshot-auto-form.png new file mode 100644 index 0000000000000000000000000000000000000000..232d98bdfa209f60d347ad89cd28a4db93568710 Binary files /dev/null and b/docs/readme/zh-CN/docs/static/screenshot-auto-form.png differ diff --git a/docs/readme/zh-CN/docs/static/screenshot-automation.png b/docs/readme/zh-CN/docs/static/screenshot-automation.png new file mode 100644 index 0000000000000000000000000000000000000000..2356614a073f24c8ac2a7ccf7a7fbcfeee3ccf89 Binary files /dev/null and b/docs/readme/zh-CN/docs/static/screenshot-automation.png differ diff --git a/docs/readme/zh-CN/docs/static/screenshot-extensible.png b/docs/readme/zh-CN/docs/static/screenshot-extensible.png new file mode 100644 index 0000000000000000000000000000000000000000..334a29e54f82c027c3a4af3815850bc93e2248f6 Binary files /dev/null and b/docs/readme/zh-CN/docs/static/screenshot-extensible.png differ diff --git a/docs/readme/zh-CN/docs/static/screenshot-hr.png b/docs/readme/zh-CN/docs/static/screenshot-hr.png new file mode 100644 index 0000000000000000000000000000000000000000..3ec97c6cf99a69bc3a3aefcfc77a1c124508e53b Binary files /dev/null and b/docs/readme/zh-CN/docs/static/screenshot-hr.png differ diff --git a/docs/readme/zh-CN/docs/static/screenshot-it.png b/docs/readme/zh-CN/docs/static/screenshot-it.png new file mode 100644 index 0000000000000000000000000000000000000000..f0592b3a8880f1f7551bc59cd184eb804bb7a27e Binary files /dev/null and b/docs/readme/zh-CN/docs/static/screenshot-it.png differ diff --git a/docs/readme/zh-CN/docs/static/screenshot-marketing.png b/docs/readme/zh-CN/docs/static/screenshot-marketing.png new file mode 100644 index 0000000000000000000000000000000000000000..5ff160f4f6f3fbed223b22fd7aefb45a00469ddc Binary files /dev/null and b/docs/readme/zh-CN/docs/static/screenshot-marketing.png differ diff --git a/docs/readme/zh-CN/docs/static/screenshot-permissions.png b/docs/readme/zh-CN/docs/static/screenshot-permissions.png new file mode 100644 index 0000000000000000000000000000000000000000..9e3d88ec98de008ce2fef8a4bd48e8d971b9bedf Binary files /dev/null and b/docs/readme/zh-CN/docs/static/screenshot-permissions.png differ diff --git a/docs/readme/zh-CN/docs/static/screenshot-realtime.png b/docs/readme/zh-CN/docs/static/screenshot-realtime.png new file mode 100644 index 0000000000000000000000000000000000000000..40f22a47a180fae4ba21ca6fbd7f3247ecb0c4ac Binary files /dev/null and b/docs/readme/zh-CN/docs/static/screenshot-realtime.png differ diff --git a/docs/readme/zh-CN/docs/static/screenshot-sales.png b/docs/readme/zh-CN/docs/static/screenshot-sales.png new file mode 100644 index 0000000000000000000000000000000000000000..efd35700dd39d27e1f89b357e1f0ffde373944d4 Binary files /dev/null and b/docs/readme/zh-CN/docs/static/screenshot-sales.png differ diff --git a/docs/readme/zh-HK/README.md b/docs/readme/zh-HK/README.md new file mode 100644 index 0000000000000000000000000000000000000000..68c081257feae4e5f68c2f5df6214d026ecebb2f --- /dev/null +++ b/docs/readme/zh-HK/README.md @@ -0,0 +1,382 @@ +

    + + APITable封面圖像 + +

    + +

    + + + APITable Gitpod 開發環境 + + + TypeScript 語言,NestJS 框架 + + Java 語言,Spring 框架 + + + + + + + + + + + + +
    + + + APITable License Badge AGPL + + + + + + + + + + + + + +

    + +

    + English + | + Français + | + Español + | + Deutsch + | + 简体中文 + | + 繁體中文 + | + 日本語 +

    + +## ✨ 快速啟動 + +> APITable 目前是 `正在進行中的工作`。 +> +> We will publish the first release in late February 2023. +> +> 加入 [Discord](https://discord.gg/TwNb9nfdBU) 或 [Twitter](https://twitter.com/apitable_com) 保持聯繫。如果您只是想嘗試APITable[^info], 點擊這裡 [⚡️Gitpod 在線 Demo](https://gitpod.io/#https://github.com/apitable/apitable). + +如果您想要在本地或雲端計算環境中安裝 APITable ,請參閱 [💾 安裝](#installation) + +如果你想要設置你的本地開發環境,請閱讀我們的 [🧑stiptop_compute: 開發者指南](./docs/contribute/developer-guide.md) + +## 🔥 功能特性 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + 實時協同 + + Automatic Form +
    + + + + + + + +
    + API-第一面板 + + 無限跨表關聯 +
    + + + + + + + +
    + 強大的行/列權限 + + Embed +
    + + + + + + + +
    + +APITable 提供了一系列令人驚奇的功能,從個人到企業。 + +- Advanced technology stack and open-source + - `實時合作` 允許多個用戶實時或與 `操作轉換(OT)` 算法同時進行編輯。 + - 在 `中極其順暢、方便用戶、超快的數據庫電子表格接口 渲染引擎` + - Database native architecture: Changeset / Operation / Action / Snapshot and so on. + - **100k+** 數據行與實時合作。 + - Full-stack API access, from `Data` to `Metadata`. + - 單向/雙向錶鏈接和 `無限交叉鏈接` + - 社區友好的編程語言和框架,TypeScript ([下一步JS](https://nextjs.org/) + [NestJS](https://nestjs.com/)) 和 Java ([Spring 啟動](https://spring.io/projects/spring-boot)) +- 美觀和齊全的多維表格UI界面 + - `CRUD`: 創建、閱讀、更新、刪除表、列和行 + - `字段操作`: 排序、過濾、分組、隱藏/取消隱藏、高度設置。 + - `基於`的空格:使用分開的工作區取代基於App/Base-的結構,使無限制的表格鏈接成為可能。 + - `可用的暗色模式` 和主題定製. + - `7 種視圖類型`: 網格視圖(Dataseet) / 圖庫視圖/ Mindmap 視圖/ Kanban 視圖/全功能網格視圖/日曆視圖 + - 單擊API面板 +- Batteries included + - 內置的 10 + 官方模板。 + - 機器人自動化和自定義可用. + - BI 儀表板 + - One-click auto-generated form + - 可共享和嵌入的頁面。 + - 多語言支持 + - 與 n8n.io / Zapier / Appsmith... 及更多。 +- 卓越的擴展性 + - 可擴展的 `部件系統` 有超過 20 個官員開源部件。 + - Customizable Graph & Chart & Dashboard + - 可自定義數據列類型 + - Customizable Formulas + - 可自定義自動機器人操作。 +- 企業級權限 + - `鏡像`, 將視圖變成鏡像以實現行權限。 + - 通過非常簡單的操作激活 `列權限`。 + - 文件夾/子文件夾/文件權限 + - 樹結構文件夾和可自定義的節點(文件); + - 團隊管理 & 組織結構 +- Enterprise features: + - SAML + - 單點登錄(SSO) + - 審計 + - 數據庫自動備份 + - Data Exporter + - 水標 +- .... + +使用可擴展的部件和插件,您可以添加更多功能。 + +## 💥 Use Cases + +為什麼你必須知道APITable並作為你的下一個軟件? + +- 作為超級管理軟件 + - Flexible Project Management & Tasks / Issues Management. + - Marketing Lead Management. + - 最靈活和可連接的CRM。 + - Flexible Business Intelligence (BI). + - 有利於人民的形式和調查 + - Flexible ERP. + - Low-code and no-code platform. + - ...及更多, APITable 將 1000 個軟件放入您的口袋中。 +- As a visual database infrastructure + - **嵌入** 個應用到您自己的軟件界面。 + - Visual Database with REST API. + - 管理儀表板 + - 中央配置管理。 + - **連接您所有的** 個軟件的全部企業數據庫。 + - ...及更多, APITable 連接到一切。 +- 此外,它是開源和可擴展的 + +## 💞 API-oriented + +#### API 面板 + +點擊右角的 `API` 按鈕將顯示 API 面板 + +#### SQL式查詢 + +APITable 將提供一個數據表查詢語言(DQL)來查詢您的數據庫電子表格內容。 + +## 💝 Embed-friendly + +#### 分享和嵌入 + +分享您的數據表或文件夾。 通過複製和粘貼HTML腳本嵌入它們。 + +#### 企業準備嵌入 + +[APITable.com](https://apitable.com) 為證券提供更多準備好企業嵌入功能。 + +## 安裝 + +請確保您已在本地安裝 `docker` & `curl` 。 + +如果您的計算機安裝了 Docker ,打開您的終端並這樣運行: + +``` +curl https://apitable.github.io/install.sh | bash +``` + +然後在您的瀏覽器中打開 [https://localhost:80](https://localhost:80) 訪問它。 (默認用戶名 `admin@apitable.com` 和密碼 `Apitable2022`) + +如果你想要設置你的本地開發環境,請閱讀我們的 [🧑‍💻 開發者指南 ](./docs/contribute/developer-guide.md) + +## 🧑‍💻 Contributing + +歡迎並感謝您有興趣為APITable作出貢獻! + +除了編寫代碼,您還有許多方法可以做出貢獻。 + +你可以閱讀這個倉庫的 [貢獻指南](./CONTRIBUTING.md) 來學習如何貢獻. + +這是一個快速指南來幫助您為API做出貢獻。 + +### 發展環境 + +在我們的 [開發者指南](./docs/contribute/developer-guide.md) 中學習如何設置您的本地環境。 + +### Git 工作流基礎 + +Here's a general APITable git workflow: + +1. 創建一個問題並描述您想要的功能 -> [APITable 問題](https://github.com/apitable/apitable/issues) +2. 派生此項目 -> [Fork APIable 項目](https://github.com/apitable/apitable/fork) +3. 創建您的功能分支(`git 結帳-b 我-新功能`) +4. 提交您的更改(`git commit-am '添加一些功能'`) +5. 發佈分支 (`git 推送源自我的新功能`) +6. 創建新的拉取請求 -> [跨叉創建拉取請求](https://github.com/apitable/apitable/compare) + +### 工作公約 + +APITable use these common conventions: + +- 我們的 Git 分支模型是什麼? [Gitflow](https://nvie.com/posts/a-successful-git-branching-model/) +- 如何在派生項目上進行合作? [Github Flow](https://docs.github.com/en/get-started/quickstart/github-flow) +- 如何寫入好的提交消息? [常規承諾](https://www.conventionalcommits.org/) +- 我們的更新日誌格式是什麼? [保留更新日誌](https://keepachangelog.com/en/1.0.0/) +- 如何進行版本控制和標記? [語義版](https://semver.org/) +- Java 編碼準則是什麼? [Java 編碼準則](https://github.com/alibaba/Alibaba-Java-Coding-Guidelines) | [Intellij IDEA 插件](https://plugins.jetbrains.com/plugin/10046-alibaba-java-coding-guidelines) +- 什麼是 TypeScript 編碼準則? -> [TypeScript 樣式指南](https://google.github.io/styleguide/tsguide.html) | [ESLint](https://www.npmjs.com/package/@typescript-eslint/eslint-plugin) + +### 文件 + +- [幫助中心](https://help.apitable.com/) +- [👩‍💻 開發者中心](https://developers.apitable.com/) + - [🪡 REST API Docs](https://developers.apitable.com/api/introduction/) + - 小部件 SDK (即將到來...) + - Scripting (Coming soon...) + +## 🛣 Roadmap + +### 未來的功能 + +- 重代碼接口構建器 +- 可嵌入第三方文件部分 +- SQL類域特定語言 +- IdP +- Web 3 功能 +- ... + +### 託管版本和企業版本提供高級功能 + +- IdP; +- SAML +- Single-Sign-On +- 審計 +- 數據庫備份 +- 水標 + +欲瞭解更多信息,請通過 聯繫我們。 + +## 👫 獲得參與 + +### :glube_showing_Asia-Australia:我們為什麼要創建 APITable 和 open-source? + +- 我們認為 `數據庫是所有軟件` 的基石。 +- We believe that making a `Visual Database with rich and easy user interface for everyone` can reduce the difficulty of software industry and increase the world's digitalization adoption. +- 我們認為開放源碼 `APITable` 工作可以 `將人類推向前進` + +### 我們正在遠程僱用! + +我們總是為APITable尋找優秀人才: + +- **前端開發者**: 你有React, NextJS, TypeScript, WebPack的體驗。 你想要寫高質量的代碼,帶有清晰的文檔和單元測試。 +- **後端開發者**: 你有經驗使用 NestJS, TypeScript, Spring Boot, Java, SQL, Kubernetes, Terraform. 你想要寫高質量的代碼,帶有清晰的文檔和單元測試。 And you like to write high quality code with clear documentation and unit tests. +- **Front-end developer**: You have experience with React, NextJS, TypeScript, WebPack. And you like to write high quality code with clear documentation and unit tests. + +無論時間和條件如何,如果你想要加入APITable團隊, 請毫不猶豫地將您的 CV 發送到 [talent@apitable。 om](mailto:talent@apitable.com)。 + +## 📺 截圖 + +

    + APITable Screenshot Image +

    +

    + APITable Screenshot Image +

    +

    + APITable Screenshot Image +

    +

    + APITable Screenshot Image +

    +

    + APITable Screenshot Image +

    +

    + APITable Screenshot Image +

    +

    + APITable Screenshot Image +

    +

    + APITable Screenshot Image +

    +

    + APITable Screenshot Image +

    +

    + APITable Screenshot Image +

    + +## 🥰 許可協議 + +> 此倉庫包含在 AGPL 下發布的 Open Source 版本的 APITable源代碼。 +> +> 如果你想運行你自己的 APITable 副本或對發展作出貢獻,那就是你的地方。 +> +> 詳情請參閱 [LICENSING](./LICENSING.md) +> +> 如果您想要在線使用 APITable ,那麼您不需要運行此代碼。 我們在 [APITable 上提供一個託管的應用版本。 om](https://apitable.com) 適合全局加速器。 + +
    + +[^info]: 使用AGPL-3.0授權。 由 [APITable Ltd](https://apitable.com)設計。 diff --git a/docs/readme/zh-HK/docs/contribute/developer-guide.md b/docs/readme/zh-HK/docs/contribute/developer-guide.md new file mode 100644 index 0000000000000000000000000000000000000000..4fb8603799ae31be6636625751f0a82a2dfb3c0d --- /dev/null +++ b/docs/readme/zh-HK/docs/contribute/developer-guide.md @@ -0,0 +1,132 @@ +# Developer Guide + +This guide helps you get started developing APITable. + +## Dependencies + +Make sure you have the following dependencies and programming languages installed before setting up your developer environment: + +- `git` +- [docker](https://docs.docker.com/engine/install/) +- [docker-compose v2](https://docs.docker.com/engine/install/) +- `make` +- [sdkman](https://sdkman.io/): for install `java`, Java SDK 8 +- [nvm](https://github.com/nvm-sh/nvm): for install `node`, NodeJS v16.15.0 + + +### Programming Language + +If you are using macOS or Linux. We recommend install programming language with SDK manager `sdkman` and `nvm`. + +```bash +# quick install nvm +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install.sh | bash +# quick install sdkman +curl -s "https://get.sdkman.io" | bash +# install nodejs +nvm install 16.15.0 && nvm use 16.15.0 && corepack enable +# install java development kit +sdk install java 8.0.342-amzn && sdk use java 8.0.342-amzn +``` + +### macOS + +We recommend using [Homebrew](https://brew.sh/) for installing any missing dependencies: + +```bash +## necessary required +brew install git +brew install --cask docker +brew install make +``` + +### Linux + +On CentOS / RHEL or other Linux distribution with `yum` + +```bash +sudo yum install git +sudo yum install make +``` + +On Ubuntu / Debian or other Linux distribution with `apt` + +```bash +sudo apt update +sudo apt install git +sudo apt install make +``` + + +### Windows + +If you are running APITable on Windows 10/11, we recommend installing [Docker Desktop on Windows](https://docs.docker.com/desktop/install/windows-install/), [Ubuntu on WSL](https://ubuntu.com/wsl) and [Windows Terminal](https://aka.ms/terminal), You can learn more about Windows Subsystem for Linux (WSL) in [the official site](https://learn.microsoft.com/en-us/windows/wsl). + +Install missing dependencies on Ubuntu using `apt`: + +```bash +sudo apt update +sudo apt install git +sudo apt install make +``` + + +## Build Tool + +We use `make` as our centric build tool entry that drives other build tool like `gradle` / `npm` / `yarn`. + +So you can just input `make` command and see all build commands: + +```bash +make +``` + +![make command screenshot](../static/make.png) + + + +## Start Development Environment + +APITable consists of 4 processes: + +1. backend-server +2. room-server +3. socket-server +4. web-server + +To start the development environment locally, run these commands: + +```bash +# start databases in dockers +make dataenv + +# install dependencies +make install + +#start backend-server +make run # enter 1 + +# and then switch to a new terminal +# start room-server +make run # enter 2 + +# and then switch to a new terminal +# start socket-server +make run # enter 3 + +# and then switch to a new terminal +# start web-server +make run # enter 4 + +``` + + + + +## IDE + +We recommend you use `Visual Studio Code` or `Intellij IDEA` for your IDE. + +APITable have prepared these two IDE's debug configs. + +Just open APITable's root directory with IDE. diff --git a/docs/readme/zh-HK/docs/static/cover.png b/docs/readme/zh-HK/docs/static/cover.png new file mode 100644 index 0000000000000000000000000000000000000000..729256d74f3d38cd93db4f786f0c5c57367d6507 Binary files /dev/null and b/docs/readme/zh-HK/docs/static/cover.png differ diff --git a/docs/readme/zh-HK/docs/static/feature-api-first-panel.gif b/docs/readme/zh-HK/docs/static/feature-api-first-panel.gif new file mode 100644 index 0000000000000000000000000000000000000000..0dceee8ccd1f3b07ac64a5cffd6ea546b6d87d55 Binary files /dev/null and b/docs/readme/zh-HK/docs/static/feature-api-first-panel.gif differ diff --git a/docs/readme/zh-HK/docs/static/feature-apipanel.gif b/docs/readme/zh-HK/docs/static/feature-apipanel.gif new file mode 100644 index 0000000000000000000000000000000000000000..cc829499a82ee5019c981b01a4aa15441f280302 Binary files /dev/null and b/docs/readme/zh-HK/docs/static/feature-apipanel.gif differ diff --git a/docs/readme/zh-HK/docs/static/feature-embed.gif b/docs/readme/zh-HK/docs/static/feature-embed.gif new file mode 100644 index 0000000000000000000000000000000000000000..9edd44049941d2345804f71f61b34a1327df4ed2 Binary files /dev/null and b/docs/readme/zh-HK/docs/static/feature-embed.gif differ diff --git a/docs/readme/zh-HK/docs/static/feature-form.gif b/docs/readme/zh-HK/docs/static/feature-form.gif new file mode 100644 index 0000000000000000000000000000000000000000..bea7e8a35f27ba0d88f57e55c0e96f5ed4cc30fc Binary files /dev/null and b/docs/readme/zh-HK/docs/static/feature-form.gif differ diff --git a/docs/readme/zh-HK/docs/static/feature-permissions.gif b/docs/readme/zh-HK/docs/static/feature-permissions.gif new file mode 100644 index 0000000000000000000000000000000000000000..456b3c588ab1026933846729d29ae02c942eb725 Binary files /dev/null and b/docs/readme/zh-HK/docs/static/feature-permissions.gif differ diff --git a/docs/readme/zh-HK/docs/static/feature-realtime.gif b/docs/readme/zh-HK/docs/static/feature-realtime.gif new file mode 100644 index 0000000000000000000000000000000000000000..a2fc86b1e50ca971c0b9751088bf14e360ede30b Binary files /dev/null and b/docs/readme/zh-HK/docs/static/feature-realtime.gif differ diff --git a/docs/readme/zh-HK/docs/static/feature-unlimited-cross-table-links.gif b/docs/readme/zh-HK/docs/static/feature-unlimited-cross-table-links.gif new file mode 100644 index 0000000000000000000000000000000000000000..b2d08fe1766ac832299a2db4c4be13526153b91c Binary files /dev/null and b/docs/readme/zh-HK/docs/static/feature-unlimited-cross-table-links.gif differ diff --git a/docs/readme/zh-HK/docs/static/logo.svg b/docs/readme/zh-HK/docs/static/logo.svg new file mode 100644 index 0000000000000000000000000000000000000000..33c93bb8dbf0fc3430cb0749da1a75ef0f283774 --- /dev/null +++ b/docs/readme/zh-HK/docs/static/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/readme/zh-HK/docs/static/make.png b/docs/readme/zh-HK/docs/static/make.png new file mode 100644 index 0000000000000000000000000000000000000000..c997c1c7b453bde0940451f6ef239fc7c42c2b43 Binary files /dev/null and b/docs/readme/zh-HK/docs/static/make.png differ diff --git a/docs/readme/zh-HK/docs/static/screenshot-api-panel.png b/docs/readme/zh-HK/docs/static/screenshot-api-panel.png new file mode 100644 index 0000000000000000000000000000000000000000..622ceadd5e3f27c8a632c461f687bd6ca9e9467a Binary files /dev/null and b/docs/readme/zh-HK/docs/static/screenshot-api-panel.png differ diff --git a/docs/readme/zh-HK/docs/static/screenshot-auto-form.png b/docs/readme/zh-HK/docs/static/screenshot-auto-form.png new file mode 100644 index 0000000000000000000000000000000000000000..232d98bdfa209f60d347ad89cd28a4db93568710 Binary files /dev/null and b/docs/readme/zh-HK/docs/static/screenshot-auto-form.png differ diff --git a/docs/readme/zh-HK/docs/static/screenshot-automation.png b/docs/readme/zh-HK/docs/static/screenshot-automation.png new file mode 100644 index 0000000000000000000000000000000000000000..2356614a073f24c8ac2a7ccf7a7fbcfeee3ccf89 Binary files /dev/null and b/docs/readme/zh-HK/docs/static/screenshot-automation.png differ diff --git a/docs/readme/zh-HK/docs/static/screenshot-extensible.png b/docs/readme/zh-HK/docs/static/screenshot-extensible.png new file mode 100644 index 0000000000000000000000000000000000000000..334a29e54f82c027c3a4af3815850bc93e2248f6 Binary files /dev/null and b/docs/readme/zh-HK/docs/static/screenshot-extensible.png differ diff --git a/docs/readme/zh-HK/docs/static/screenshot-hr.png b/docs/readme/zh-HK/docs/static/screenshot-hr.png new file mode 100644 index 0000000000000000000000000000000000000000..3ec97c6cf99a69bc3a3aefcfc77a1c124508e53b Binary files /dev/null and b/docs/readme/zh-HK/docs/static/screenshot-hr.png differ diff --git a/docs/readme/zh-HK/docs/static/screenshot-it.png b/docs/readme/zh-HK/docs/static/screenshot-it.png new file mode 100644 index 0000000000000000000000000000000000000000..f0592b3a8880f1f7551bc59cd184eb804bb7a27e Binary files /dev/null and b/docs/readme/zh-HK/docs/static/screenshot-it.png differ diff --git a/docs/readme/zh-HK/docs/static/screenshot-marketing.png b/docs/readme/zh-HK/docs/static/screenshot-marketing.png new file mode 100644 index 0000000000000000000000000000000000000000..5ff160f4f6f3fbed223b22fd7aefb45a00469ddc Binary files /dev/null and b/docs/readme/zh-HK/docs/static/screenshot-marketing.png differ diff --git a/docs/readme/zh-HK/docs/static/screenshot-permissions.png b/docs/readme/zh-HK/docs/static/screenshot-permissions.png new file mode 100644 index 0000000000000000000000000000000000000000..9e3d88ec98de008ce2fef8a4bd48e8d971b9bedf Binary files /dev/null and b/docs/readme/zh-HK/docs/static/screenshot-permissions.png differ diff --git a/docs/readme/zh-HK/docs/static/screenshot-realtime.png b/docs/readme/zh-HK/docs/static/screenshot-realtime.png new file mode 100644 index 0000000000000000000000000000000000000000..40f22a47a180fae4ba21ca6fbd7f3247ecb0c4ac Binary files /dev/null and b/docs/readme/zh-HK/docs/static/screenshot-realtime.png differ diff --git a/docs/readme/zh-HK/docs/static/screenshot-sales.png b/docs/readme/zh-HK/docs/static/screenshot-sales.png new file mode 100644 index 0000000000000000000000000000000000000000..efd35700dd39d27e1f89b357e1f0ffde373944d4 Binary files /dev/null and b/docs/readme/zh-HK/docs/static/screenshot-sales.png differ diff --git a/docs/static/cover.png b/docs/static/cover.png index ec271103ed65727037709454047d0f132d67da48..729256d74f3d38cd93db4f786f0c5c57367d6507 100644 Binary files a/docs/static/cover.png and b/docs/static/cover.png differ diff --git a/docs/static/feature-permissions.gif b/docs/static/feature-permissions.gif index a517d5a57ec63b497816bc52e2ba553970a74f67..456b3c588ab1026933846729d29ae02c942eb725 100644 Binary files a/docs/static/feature-permissions.gif and b/docs/static/feature-permissions.gif differ diff --git a/docs/static/feature-unlimited-cross-table-links.gif b/docs/static/feature-unlimited-cross-table-links.gif index 053c32e0946ba8c397efdd099ed3de3473f4414d..b2d08fe1766ac832299a2db4c4be13526153b91c 100644 Binary files a/docs/static/feature-unlimited-cross-table-links.gif and b/docs/static/feature-unlimited-cross-table-links.gif differ diff --git a/docs/static/logo.svg b/docs/static/logo.svg index 72feee24f3e5adb91cfa163e9376d4ade3dc258d..33c93bb8dbf0fc3430cb0749da1a75ef0f283774 100644 --- a/docs/static/logo.svg +++ b/docs/static/logo.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/static/make.png b/docs/static/make.png index baa24e351bf983fbe1638853e0219f9d440e009a..c997c1c7b453bde0940451f6ef239fc7c42c2b43 100644 Binary files a/docs/static/make.png and b/docs/static/make.png differ diff --git a/docs/static/screenshot-api-panel.png b/docs/static/screenshot-api-panel.png index 7e7636ee115fb4f3b105649af279d268689015e1..622ceadd5e3f27c8a632c461f687bd6ca9e9467a 100644 Binary files a/docs/static/screenshot-api-panel.png and b/docs/static/screenshot-api-panel.png differ diff --git a/docs/static/screenshot-automation.png b/docs/static/screenshot-automation.png index cd6fad5be7d278dc686bbf5abe54ef3aaebe226f..2356614a073f24c8ac2a7ccf7a7fbcfeee3ccf89 100644 Binary files a/docs/static/screenshot-automation.png and b/docs/static/screenshot-automation.png differ diff --git a/docs/static/screenshot-extensible.png b/docs/static/screenshot-extensible.png index c9255dcd4aed40327b5a7fcd7f941c8a92815d63..334a29e54f82c027c3a4af3815850bc93e2248f6 100644 Binary files a/docs/static/screenshot-extensible.png and b/docs/static/screenshot-extensible.png differ diff --git a/docs/static/screenshot-hr.png b/docs/static/screenshot-hr.png index 921b9725d8e4a0fcbff7050161ea6422b0b5f5bd..3ec97c6cf99a69bc3a3aefcfc77a1c124508e53b 100644 Binary files a/docs/static/screenshot-hr.png and b/docs/static/screenshot-hr.png differ diff --git a/docs/static/screenshot-it.png b/docs/static/screenshot-it.png index e6c19e5a1f733bb0d3d3e39800cf23bc9e856d9c..f0592b3a8880f1f7551bc59cd184eb804bb7a27e 100644 Binary files a/docs/static/screenshot-it.png and b/docs/static/screenshot-it.png differ diff --git a/docs/static/screenshot-marketing.png b/docs/static/screenshot-marketing.png index a3f2ac8abdc2c3722676902394c9b498575096ec..5ff160f4f6f3fbed223b22fd7aefb45a00469ddc 100644 Binary files a/docs/static/screenshot-marketing.png and b/docs/static/screenshot-marketing.png differ diff --git a/docs/static/screenshot-permissions.png b/docs/static/screenshot-permissions.png index da452e96e6c1e0fdabd140ceb09bc242104d0e1c..9e3d88ec98de008ce2fef8a4bd48e8d971b9bedf 100644 Binary files a/docs/static/screenshot-permissions.png and b/docs/static/screenshot-permissions.png differ diff --git a/docs/static/screenshot-realtime.png b/docs/static/screenshot-realtime.png index 1107c34b7dbca4e2e701d2a3af5f7612eba7ee25..40f22a47a180fae4ba21ca6fbd7f3247ecb0c4ac 100644 Binary files a/docs/static/screenshot-realtime.png and b/docs/static/screenshot-realtime.png differ diff --git a/docs/static/screenshot-sales.png b/docs/static/screenshot-sales.png index ae30a5fe90ee98daf557f52618b114ce680bd2fd..efd35700dd39d27e1f89b357e1f0ffde373944d4 100644 Binary files a/docs/static/screenshot-sales.png and b/docs/static/screenshot-sales.png differ diff --git a/init-db/.version b/init-db/.version index d183d4ace05b92877cec0b45daa9e26b09182d7d..07feb8234926c75235d2687e1f26614d2baf0c6c 100644 --- a/init-db/.version +++ b/init-db/.version @@ -1 +1 @@ -0.16.0 \ No newline at end of file +0.17.0 \ No newline at end of file diff --git a/backend-server/application/src/test/resources/logback-test.xml b/init-db/src/main/resources/db/changelog/0.10/20220217_changeset.xml similarity index 51% rename from backend-server/application/src/test/resources/logback-test.xml rename to init-db/src/main/resources/db/changelog/0.10/20220217_changeset.xml index ffb07507f705499ad0312bc9048d70c2ef7ec85a..079f496bd198daeee84b0873688c4e4c6ca4f1e6 100644 --- a/backend-server/application/src/test/resources/logback-test.xml +++ b/init-db/src/main/resources/db/changelog/0.10/20220217_changeset.xml @@ -1,3 +1,4 @@ + - - - - - 「Log」>>> ${CONSOLE_LOG_PATTERN} - utf-8 - - - - - - - \ No newline at end of file + + + Widget Package Table add sandbox field + + ALTER TABLE `${table.prefix}widget_package` + ADD COLUMN `sandbox` tinyint(1) NULL COMMENT 'Whether the sandbox runs (0: No, 1: Yes)' AFTER `widget_body`; + + + diff --git a/init-db/src/main/resources/db/changelog/0.10/master.xml b/init-db/src/main/resources/db/changelog/0.10/master.xml index a3c318691ae28074866f49f38618b89000338f83..4188574039128ff305e04e9fcee3054cab245c6e 100644 --- a/init-db/src/main/resources/db/changelog/0.10/master.xml +++ b/init-db/src/main/resources/db/changelog/0.10/master.xml @@ -26,4 +26,5 @@ + diff --git a/init-db/src/main/resources/db/changelog/0.12/20220520_changeset.xml b/init-db/src/main/resources/db/changelog/0.12/20220520_changeset.xml new file mode 100644 index 0000000000000000000000000000000000000000..9eb705d13ba1a031a9e9a2b0b9dbe10455a102f6 --- /dev/null +++ b/init-db/src/main/resources/db/changelog/0.12/20220520_changeset.xml @@ -0,0 +1,42 @@ + + + + + + + Automation Trigger Table add robot_id index + + ALTER TABLE `${table.prefix}automation_trigger` + ADD INDEX `idx_robot_id`(`robot_id`) USING BTREE; + + + + + Automation Run History Table add robot_id index + + ALTER TABLE `${table.prefix}automation_run_history` + ADD INDEX `idx_robot_id`(`robot_id`) USING BTREE; + + + + diff --git a/init-db/src/main/resources/db/changelog/0.12/master.xml b/init-db/src/main/resources/db/changelog/0.12/master.xml index d07f198fcb401dd638a53905f67db96ea91ac4eb..6d0c78078ed8d37c67eb633ad24b5d67e21fb6d7 100644 --- a/init-db/src/main/resources/db/changelog/0.12/master.xml +++ b/init-db/src/main/resources/db/changelog/0.12/master.xml @@ -32,6 +32,7 @@ + diff --git a/init-db/src/main/resources/db/changelog/0.13/20220624_changeset.xml b/init-db/src/main/resources/db/changelog/0.13/20220624_changeset.xml new file mode 100644 index 0000000000000000000000000000000000000000..30ba2ea020533b348f1bb8c605037df0334e43d3 --- /dev/null +++ b/init-db/src/main/resources/db/changelog/0.13/20220624_changeset.xml @@ -0,0 +1,57 @@ + + + + + + + Workbench - Widget Package Table add install_env_code field + + ALTER TABLE `${table.prefix}widget_package` + ADD COLUMN `install_env_code` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Installation environment code' AFTER `updated_at`; + + + + + Workbench - Widget Package Table add runtime_env_code field + + ALTER TABLE `${table.prefix}widget_package` + ADD COLUMN `runtime_env_code` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Operate environment code' AFTER `install_env_code`; + + + + + Workbench - Widget Package Release Table add install_env_code field + + ALTER TABLE `${table.prefix}widget_package_release` + ADD COLUMN `install_env_code` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Installation environment code' AFTER `updated_at`; + + + + + Workbench - Widget Package Release Table add runtime_env_code field + + ALTER TABLE `${table.prefix}widget_package_release` + ADD COLUMN `runtime_env_code` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Operate environment code' AFTER `install_env_code`; + + + + diff --git a/init-db/src/main/resources/db/changelog/0.13/master.xml b/init-db/src/main/resources/db/changelog/0.13/master.xml index 3f091a6d4d1c7756bca00b99f6a8bcfc41283160..7b33be9a7cb5f95ba15589c68769874241a3cf9f 100644 --- a/init-db/src/main/resources/db/changelog/0.13/master.xml +++ b/init-db/src/main/resources/db/changelog/0.13/master.xml @@ -23,6 +23,7 @@ xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.8.xsd"> + diff --git a/init-db/src/main/resources/db/changelog/0.6/20201214_changeset.xml b/init-db/src/main/resources/db/changelog/0.6/20201214_changeset.xml new file mode 100644 index 0000000000000000000000000000000000000000..628a4a211c24eafb102f0d73cc7a1c463b920f89 --- /dev/null +++ b/init-db/src/main/resources/db/changelog/0.6/20201214_changeset.xml @@ -0,0 +1,33 @@ + + + + + + Create Workbench - Widget Package Table + + + + Create Workbench - Widget Table + + + diff --git a/init-db/src/main/resources/db/changelog/0.6/20201214_changeset_001.sql b/init-db/src/main/resources/db/changelog/0.6/20201214_changeset_001.sql new file mode 100644 index 0000000000000000000000000000000000000000..a5cb102e1d02881ae8263e0230859e3446855b9e --- /dev/null +++ b/init-db/src/main/resources/db/changelog/0.6/20201214_changeset_001.sql @@ -0,0 +1,37 @@ +-- APITable +-- Copyright (C) 2022 APITable Ltd. +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU Affero General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU Affero General Public License for more details. +-- +-- You should have received a copy of the GNU Affero General Public License +-- along with this program. If not, see . + +CREATE TABLE `${table.prefix}widget_package` +( + `id` bigint(20) unsigned NOT NULL COMMENT 'Primary Key', + `package_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Widget ID', + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Name', + `name_en` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'English Name', + `icon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Icon', + `description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT 'Description', + `status` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT 'Status (0: to be approved; 1: not passed; 2: to be released; 3: online; 4: offline)', + `version` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Version', + `installed_num` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Number of installations', + `is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT 'Delete Tag(0: No, 1: Yes)', + `created_by` bigint(20) DEFAULT NULL COMMENT 'Creator', + `updated_by` bigint(20) DEFAULT NULL COMMENT 'Last Update By', + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Create Time', + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Update Time', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `uk_package_id` (`package_id`) USING BTREE +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_unicode_ci COMMENT ='Workbench - Widget Package Table'; diff --git a/init-db/src/main/resources/db/changelog/0.6/20201214_changeset_002.sql b/init-db/src/main/resources/db/changelog/0.6/20201214_changeset_002.sql new file mode 100644 index 0000000000000000000000000000000000000000..4f588722b131f8464d880e82cd4aaec197ad9ff7 --- /dev/null +++ b/init-db/src/main/resources/db/changelog/0.6/20201214_changeset_002.sql @@ -0,0 +1,35 @@ +-- APITable +-- Copyright (C) 2022 APITable Ltd. +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU Affero General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU Affero General Public License for more details. +-- +-- You should have received a copy of the GNU Affero General Public License +-- along with this program. If not, see . + +CREATE TABLE `${table.prefix}widget` +( + `id` bigint(20) unsigned NOT NULL COMMENT 'Primary Key', + `dst_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Datasheet ID(link#xxxx_datasheet#dst_id)', + `package_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Widget ID(link#xxxx_widget_package#package_id)', + `widget_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Customized Widget ID', + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Name', + `storage` json DEFAULT NULL COMMENT 'Storage configuration', + `revision` bigint(20) unsigned DEFAULT '0' COMMENT 'Version', + `is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT 'Delete Tag(0: No, 1: Yes)', + `created_by` bigint(20) DEFAULT NULL COMMENT 'Creator', + `updated_by` bigint(20) DEFAULT NULL COMMENT 'Last Update By', + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Create Time', + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Update Time', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `uk_widget_id` (`widget_id`) USING BTREE +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_unicode_ci COMMENT ='Workbench - Widget Table'; diff --git a/init-db/src/main/resources/db/changelog/0.6/20201222_changeset.xml b/init-db/src/main/resources/db/changelog/0.6/20201222_changeset.xml new file mode 100644 index 0000000000000000000000000000000000000000..3deb87d2b0bb6f64b46e14e644d1d869ddbf131c --- /dev/null +++ b/init-db/src/main/resources/db/changelog/0.6/20201222_changeset.xml @@ -0,0 +1,39 @@ + + + + + + Widget Package Table, add cover field + + ALTER TABLE `${table.prefix}widget_package` + ADD COLUMN `cover` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'Cover draw TOKEN' AFTER `icon`; + + + + Widget Table,revision field is set to non null + + ALTER TABLE `${table.prefix}widget` + MODIFY COLUMN `revision` bigint(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Version' AFTER `storage`; + + + diff --git a/init-db/src/main/resources/db/changelog/0.6/20201225_changeset.xml b/init-db/src/main/resources/db/changelog/0.6/20201225_changeset.xml new file mode 100644 index 0000000000000000000000000000000000000000..690eb63645c057b26808281c4bff9d2812ad06ec --- /dev/null +++ b/init-db/src/main/resources/db/changelog/0.6/20201225_changeset.xml @@ -0,0 +1,40 @@ + + + + + + Widget Table,dst_id field is unset as non empty + + ALTER TABLE `${table.prefix}widget` + MODIFY COLUMN `dst_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL + COMMENT 'Datasheet ID(link#xxxx_datasheet#dst_id)' AFTER `id`; + + + + Widget Table,add index idx_dst_id + + ALTER TABLE `${table.prefix}widget` + ADD INDEX `idx_dst_id`(`dst_id`) USING BTREE; + + + diff --git a/init-db/src/main/resources/db/changelog/0.6/20210109_changeset.xml b/init-db/src/main/resources/db/changelog/0.6/20210109_changeset.xml index 998fc6d7a435daa2e7d6b883d93dc94d834ed0e7..531bdfa95eed6b4923764973d09ead45b27d534f 100644 --- a/init-db/src/main/resources/db/changelog/0.6/20210109_changeset.xml +++ b/init-db/src/main/resources/db/changelog/0.6/20210109_changeset.xml @@ -26,4 +26,16 @@ Create Datasheet Widget Table + + Delete Widget Table index + + + + Widget Table add Space ID + + + + Widget Table add ndoe id records component ownership + + diff --git a/init-db/src/main/resources/db/changelog/0.6/20210109_changeset_002.sql b/init-db/src/main/resources/db/changelog/0.6/20210109_changeset_002.sql new file mode 100644 index 0000000000000000000000000000000000000000..dd278deb1271e653f184d16f9eef80f7eacd0136 --- /dev/null +++ b/init-db/src/main/resources/db/changelog/0.6/20210109_changeset_002.sql @@ -0,0 +1,17 @@ +-- APITable +-- Copyright (C) 2022 APITable Ltd. +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU Affero General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU Affero General Public License for more details. +-- +-- You should have received a copy of the GNU Affero General Public License +-- along with this program. If not, see . + +ALTER table `${table.prefix}widget` DROP INDEX `idx_dst_id`; diff --git a/init-db/src/main/resources/db/changelog/0.6/20210109_changeset_003.sql b/init-db/src/main/resources/db/changelog/0.6/20210109_changeset_003.sql new file mode 100644 index 0000000000000000000000000000000000000000..0f99060ac791dc55b9662d65c4c26b938dd45d20 --- /dev/null +++ b/init-db/src/main/resources/db/changelog/0.6/20210109_changeset_003.sql @@ -0,0 +1,19 @@ +-- APITable +-- Copyright (C) 2022 APITable Ltd. +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU Affero General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU Affero General Public License for more details. +-- +-- You should have received a copy of the GNU Affero General Public License +-- along with this program. If not, see . + +ALTER table `${table.prefix}widget` + ADD COLUMN `space_id` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Space ID' AFTER `id`, + ADD INDEX `k_space_id` (`space_id`) USING BTREE; diff --git a/init-db/src/main/resources/db/changelog/0.6/20210109_changeset_004.sql b/init-db/src/main/resources/db/changelog/0.6/20210109_changeset_004.sql new file mode 100644 index 0000000000000000000000000000000000000000..a982bb6af28727325bf4c1e76872867f730d3551 --- /dev/null +++ b/init-db/src/main/resources/db/changelog/0.6/20210109_changeset_004.sql @@ -0,0 +1,18 @@ +-- APITable +-- Copyright (C) 2022 APITable Ltd. +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU Affero General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU Affero General Public License for more details. +-- +-- You should have received a copy of the GNU Affero General Public License +-- along with this program. If not, see . + +ALTER TABLE `${table.prefix}widget` + ADD COLUMN `node_id` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Node ID' AFTER `space_id`; diff --git a/init-db/src/main/resources/db/changelog/0.6/20210111_changeset.xml b/init-db/src/main/resources/db/changelog/0.6/20210111_changeset.xml new file mode 100644 index 0000000000000000000000000000000000000000..c49edc10516d5eb7fad2fa188721a85fc51df430 --- /dev/null +++ b/init-db/src/main/resources/db/changelog/0.6/20210111_changeset.xml @@ -0,0 +1,37 @@ + + + + + + Widget Table delete Datasheet ID field + + ALTER TABLE `${table.prefix}widget` DROP COLUMN `dst_id`; + + + + Widget Table add Node ID index + + ALTER TABLE `${table.prefix}widget` ADD INDEX `k_node_id`(`node_id`) USING BTREE; + + + diff --git a/init-db/src/main/resources/db/changelog/0.6/master.xml b/init-db/src/main/resources/db/changelog/0.6/master.xml index 46da4bc16f33cdce49843ff4cd5d5c4d239e05dc..8a3504459e5d429b43bacd8841e29306e6de0cdf 100644 --- a/init-db/src/main/resources/db/changelog/0.6/master.xml +++ b/init-db/src/main/resources/db/changelog/0.6/master.xml @@ -30,10 +30,14 @@ + + + + diff --git a/init-db/src/main/resources/db/changelog/0.7/20210707_changeset.xml b/init-db/src/main/resources/db/changelog/0.7/20210707_changeset.xml new file mode 100644 index 0000000000000000000000000000000000000000..75798943105bd212cf8495e3008a84235a2ccbb4 --- /dev/null +++ b/init-db/src/main/resources/db/changelog/0.7/20210707_changeset.xml @@ -0,0 +1,149 @@ + + + + + + Widget Package Table add i18n_name file + + ALTER TABLE `${table.prefix}widget_package` + ADD COLUMN `i18n_name` json NULL COMMENT 'Internationalized widget name' AFTER `package_id`; + + + + + Widget Package Table add i18n_description field + + ALTER TABLE `${table.prefix}widget_package` + ADD COLUMN `i18n_description` json NULL COMMENT 'Internationalization Widget Description' AFTER `i18n_name`; + + + + + Widget Package Table add author_name field + + ALTER TABLE `${table.prefix}widget_package` + ADD COLUMN `author_name` varchar(50) NULL COMMENT 'Author Name' AFTER `cover`; + + + + + Widget Package Table add author_email field + + ALTER TABLE `${table.prefix}widget_package` + ADD COLUMN `author_email` varchar(100) NULL COMMENT 'Author email' AFTER `author_name`; + + + + + Widget Package Table add author_link field + + ALTER TABLE `${table.prefix}widget_package` + ADD COLUMN `author_link` varchar(255) NULL COMMENT 'Author website address' AFTER `author_email`; + + + + + Widget Package Table add package_type field + + ALTER TABLE `${table.prefix}widget_package` + ADD COLUMN `package_type` tinyint(2) NOT NULL COMMENT 'Widget package type (0: third party, 1: official)' AFTER `author_link`; + + + + + Widget Package Table add release_type field + + ALTER TABLE `${table.prefix}widget_package` + ADD COLUMN `release_type` tinyint(2) NOT NULL COMMENT '0: Publish to the component store in the space station, 1: Publish to the global application store (only allowed if the package_type is 0)' AFTER `package_type`; + + + + + Widget Package Table add release_id field + + ALTER TABLE `${table.prefix}widget_package` + ADD COLUMN `release_id` bigint NULL COMMENT 'The release version ID, the currently active version, can be empty. When it is empty, it is only displayed to Creator in the build store' AFTER `status`; + + + + + Widget Package Table modify notes + + ALTER TABLE `${table.prefix}widget_package` + MODIFY COLUMN `status` tinyint(2) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Status (0: under development, 1: banned, 2: to be published, 3: published, 4: off the shelf - global temporarily closed) 3, 4' AFTER `cover`, + MODIFY COLUMN `cover` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'Cover draw TOKEN' AFTER `icon`, + MODIFY COLUMN `installed_num` int UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Number of installations' AFTER `status`, + MODIFY COLUMN `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'Name - 【Discard Delete】' AFTER `installed_num`, + MODIFY COLUMN `name_en` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'English name - 【Discard Delete】' AFTER `name`, + MODIFY COLUMN `version` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'Version - 【Discard Delete】' AFTER `name_en`, + MODIFY COLUMN `description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT 'Description - 【Discard Delete】' AFTER `version`; + + + + + Create Widget Package Release Table + + CREATE TABLE `${table.prefix}widget_package_release` ( + `id` bigint UNSIGNED NOT NULL COMMENT 'Primary Key', + `release_sha` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Version Summary Unique ID(id+package_id+version generate)', + `version` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'Version number, unique under package id', + `package_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Widget Package ID', + `release_user_id` bigint NULL COMMENT 'User ID(link#xxxx_user#id)', + `release_code_bundle` varchar(255) NOT NULL COMMENT 'Release Code Bundle', + `source_code_bundle` varchar(255) NULL COMMENT 'Source Code Bundle', + `secrect_key` varchar(64) NULL COMMENT 'Source Code Secret Key', + `status` tinyint(2) NULL COMMENT 'Status (0: to be approved, 1: approved, 2: rejected)', + `release_note` varchar(255) NULL COMMENT 'Release Version Description', + `is_deleted` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Delete Tag(0: No, 1: Yes)', + `created_by` bigint(20) DEFAULT NULL COMMENT 'Creator', + `updated_by` bigint(20) DEFAULT NULL COMMENT 'Last Update By', + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Create Time', + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Update Time', + PRIMARY KEY (`id`) + ) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_unicode_ci COMMENT = 'Workbench-Widget Package Release Table'; + + + + + Create Widget Package Auth Space Table + + CREATE TABLE `${table.prefix}widget_package_auth_space` ( + `id` bigint UNSIGNED NOT NULL COMMENT 'Primary Key', + `package_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Package ID', + `space_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Space ID(link#xxxx_space#space_id)', + `type` tinyint(2) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Widget package authorization type (0: binding space - cannot be deleted, the same widget package can be jointly managed by the "development permission" administrator of the space; global widgets can also be used for upgrading and other needs; 1: authorized space - only space station widgets can be used for authorizing other spaces)', + `is_deleted` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Delete Tag(0: No, 1: Yes)', + `created_by` bigint(20) DEFAULT NULL COMMENT 'Creator', + `updated_by` bigint(20) DEFAULT NULL COMMENT 'Last Update By', + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Create Time', + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Update Time', + PRIMARY KEY (`id`) + ) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_unicode_ci COMMENT = 'Workbench - Widget Package Auth Space Table'; + + + + diff --git a/init-db/src/main/resources/db/changelog/0.7/20210712_changeset.xml b/init-db/src/main/resources/db/changelog/0.7/20210712_changeset.xml new file mode 100644 index 0000000000000000000000000000000000000000..af74a1d7291ff9e0381579f2ce7a8d0d51ca66aa --- /dev/null +++ b/init-db/src/main/resources/db/changelog/0.7/20210712_changeset.xml @@ -0,0 +1,62 @@ + + + + + + + Widget Package Table add author_icon field + + ALTER TABLE `${table.prefix}widget_package` + ADD COLUMN `author_icon` varchar(255) NULL COMMENT 'Author icon TOKEN' AFTER `author_email`; + + + + + Widget Package Table modify notes + + ALTER TABLE `${table.prefix}widget_package` + MODIFY COLUMN `status` tinyint(2) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Status (0: under development, 1: banned, 2: to be published, 3: published, 4: off the shelf - global temporarily closed) 3, 4' AFTER `cover`, + MODIFY COLUMN `cover` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'Cover draw TOKEN' AFTER `icon`, + MODIFY COLUMN `installed_num` int UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Number of installations' AFTER `status`, + MODIFY COLUMN `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'Name - 【Discard Delete】' AFTER `installed_num`, + MODIFY COLUMN `name_en` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'English name - 【Discard Delete】' AFTER `name`, + MODIFY COLUMN `version` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'Version - 【Discard Delete】' AFTER `name_en`, + MODIFY COLUMN `description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT 'Description - 【Discard Delete】' AFTER `version`; + + + + + Add widget package internationalization field compatible data + + UPDATE `${table.prefix}widget_package` t1, + (SELECT id, + JSON_SET( IFNULL( i18n_name, '{}' ), '$."zh-CN"', `name`, '$."en-US"', name_en ) AS i18n_name, + JSON_SET( IFNULL( i18n_description, '{}' ), '$."zh-CN"', `description` ) AS i18n_description + FROM `${table.prefix}widget_package` + ) t2 + SET t1.i18n_name = t2.i18n_name, t1.i18n_description = t2.i18n_description + WHERE t1.id = t2.id + + + + diff --git a/init-db/src/main/resources/db/changelog/0.7/20210721_changeset.xml b/init-db/src/main/resources/db/changelog/0.7/20210721_changeset.xml index 3ed7e118c31667db2ca2ad792b43bfb5389aa524..3b345bf9cbdf771801d3c14b320f1553c270fa39 100644 --- a/init-db/src/main/resources/db/changelog/0.7/20210721_changeset.xml +++ b/init-db/src/main/resources/db/changelog/0.7/20210721_changeset.xml @@ -23,6 +23,13 @@ xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.8.xsd"> + + modify widget_package_release table secrect_key field + + ALTER TABLE `${table.prefix}widget_package_release` + CHANGE COLUMN `secrect_key` `secret_key` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'Source code encryption key' AFTER `source_code_bundle`; + + Create Workbench - Develop Asset Table diff --git a/init-db/src/main/resources/db/changelog/0.7/20210730_changeset.xml b/init-db/src/main/resources/db/changelog/0.7/20210730_changeset.xml new file mode 100644 index 0000000000000000000000000000000000000000..0de50799e1fbfa194640c1a0dd5edce637c5f894 --- /dev/null +++ b/init-db/src/main/resources/db/changelog/0.7/20210730_changeset.xml @@ -0,0 +1,60 @@ + + + + + + + Compatible with widget authorization history data + + INSERT INTO `${table.prefix}widget_package_auth_space` (`id`, `package_id`, `space_id`, `created_by`) VALUES (1414470381007380481, 'wpkCKtqGTjzM7', '', 1250042558817435650); + INSERT INTO `${table.prefix}widget_package_auth_space` (`id`, `package_id`, `space_id`, `created_by`) VALUES (1414475166011990018, 'wpkY6DKgb3iVk', '', 1250042558817435650); + + + + + Compatible with the historical data of widget version release + + INSERT INTO `${table.prefix}widget_package_release` (`id`, `release_sha`, `version`, `package_id`, `release_user_id`, `release_code_bundle`, `source_code_bundle`, `secret_key`, `status`, `release_note`, `created_by`) VALUES (1414495717623459841, '0929ad13308eec94c68bb4c01adb9eb5d356fe9b', '1.0.0', 'wpkCKtqGTjzM7', 1250042558817435650, '', NULL, NULL, 1, NULL, 1250042558817435650); + INSERT INTO `${table.prefix}widget_package_release` (`id`, `release_sha`, `version`, `package_id`, `release_user_id`, `release_code_bundle`, `source_code_bundle`, `secret_key`, `status`, `release_note`, `created_by`) VALUES (1414496066941874178, 'fed531cc7451b6dbbbd999eda71d3074cf1cec01', '1.0.0', 'wpkY6DKgb3iVk', 1250042558817435650, '', NULL, NULL, 1, NULL, 1250042558817435650); + + + + + Update widget associated version data + + UPDATE ${table.prefix}widget_package + SET release_id = 1414495717623459841, + package_type = 1, + release_type = 1, + created_at = updated_at + WHERE package_id = 'wpkCKtqGTjzM7'; + UPDATE ${table.prefix}widget_package + SET release_id = 1414496066941874178, + package_type = 1, + release_type = 1, + created_at = updated_at + WHERE package_id = 'wpkY6DKgb3iVk'; + + + + diff --git a/init-db/src/main/resources/db/changelog/0.7/20210819_changeset.xml b/init-db/src/main/resources/db/changelog/0.7/20210819_changeset.xml new file mode 100644 index 0000000000000000000000000000000000000000..f825232e3e711e64a4c29a9abffc71791c388305 --- /dev/null +++ b/init-db/src/main/resources/db/changelog/0.7/20210819_changeset.xml @@ -0,0 +1,50 @@ + + + + + + + Modify the official widget description (icon component) + + UPDATE ${table.prefix}widget_package + SET i18n_description = '{ + "en-US": "Display data as a bar chart, bar chart, pie chart, line chart or scatter chart to help you get an overview of the data and facilitate decision-making and meeting reports", + "zh-CN": "将数据展示为柱状图、条形图、饼状图、折线图或散点图,帮助你概览数据的全貌,方便决策和会议汇报" + }' + WHERE package_id = 'wpkCKtqGTjzM7'; + + + + + Modify the official widget description (statistics and indicator components) + + UPDATE ${table.prefix}widget_package + SET i18n_description = '{ + "en-US": "Highlight the statistical value and target value of the specified Grid column as key indicators, such as sales, number of orders, number of registered users, number of bugs, etc.", + "zh-CN": "突出展示指定维格列的统计值和目标值,作为关键指标,比如销售额、订单数、注册用户数、Bug 数等" + }' + WHERE package_id = 'wpkY6DKgb3iVk'; + + + + diff --git a/init-db/src/main/resources/db/changelog/0.7/master.xml b/init-db/src/main/resources/db/changelog/0.7/master.xml index a009810bac14815f637e491691ae8e184be4802d..dccb81342ef4907918b77ad976cc5c3cd20cd767 100644 --- a/init-db/src/main/resources/db/changelog/0.7/master.xml +++ b/init-db/src/main/resources/db/changelog/0.7/master.xml @@ -34,9 +34,13 @@ + + + + diff --git a/init-db/src/main/resources/db/changelog/0.8/20210823_changeset.xml b/init-db/src/main/resources/db/changelog/0.8/20210823_changeset.xml new file mode 100644 index 0000000000000000000000000000000000000000..fd826ecdbff84df4a4ab8cc4ab5c14809e72b2e7 --- /dev/null +++ b/init-db/src/main/resources/db/changelog/0.8/20210823_changeset.xml @@ -0,0 +1,169 @@ + + + + + + + Create Automation Robot Table + + CREATE TABLE `${table.prefix}automation_robot` + ( + `id` bigint(20) UNSIGNED NOT NULL COMMENT 'Primary Key', + `resource_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Resource ID(link#xxxx_node#node_id)', + `robot_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Custom Robot ID', + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Name', + `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Description', + `version` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Version', + `is_active` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT 'Is it active', + `is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT 'Delete Tag(0: No, 1: Yes)', + `created_by` bigint(20) DEFAULT NULL COMMENT 'Creator', + `updated_by` bigint(20) DEFAULT NULL COMMENT 'Last Update By', + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Create Time', + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Update Time', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_robot_id` (`robot_id`) USING BTREE COMMENT 'Robot unique code', + INDEX `idx_resource_id` (`resource_id`) COMMENT 'Resource ID Index' + ) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_unicode_ci COMMENT = 'Automation - Robot Table'; + + + + Create Automation Trigger Table + + CREATE TABLE `${table.prefix}automation_trigger` + ( + `id` bigint(20) UNSIGNED NOT NULL COMMENT 'Primary Key', + `robot_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Robot ID (link#xxxx_automation_robot#robot_id)', + `trigger_type_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Trigger Type ID (link#xxxx_automation_trigger_type#trigger_type_id)', + `trigger_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Custom Trigger ID', + `input` json DEFAULT NULL COMMENT 'Trigger Input data of the instance', + `is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT 'Delete Tag(0: No, 1: Yes)', + `created_by` bigint(20) DEFAULT NULL COMMENT 'Creator', + `updated_by` bigint(20) DEFAULT NULL COMMENT 'Last Update By', + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Create Time', + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Update Time', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_trigger_id` (`trigger_id`) USING BTREE COMMENT 'Trigger unique code' + ) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_unicode_ci COMMENT = 'Automation - Trigger Table'; + + + + Create Automation Action Table + + CREATE TABLE `${table.prefix}automation_action` + ( + `id` bigint(20) UNSIGNED NOT NULL COMMENT 'Primary Key', + `robot_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Robot ID (link#xxxx_automation_robot#robot_id)', + `action_type_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Action Type ID (link#xxxx_automation_action_type#action_type_id)', + `action_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Custom Action ID', + `prev_action_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Previous Action ID', + `input` json DEFAULT NULL COMMENT 'Action Input data of the instance', + `is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT 'Delete Tag(0: No, 1: Yes)', + `created_by` bigint(20) DEFAULT NULL COMMENT 'Creator', + `updated_by` bigint(20) DEFAULT NULL COMMENT 'Last Update By', + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Create Time', + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Update Time', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_action_id` (`action_id`) USING BTREE COMMENT 'Action unique code' + ) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_unicode_ci COMMENT = 'Automation - Action Table'; + + + + Create Automation Service Table + + CREATE TABLE `${table.prefix}automation_service` + ( + `id` bigint(20) UNSIGNED NOT NULL COMMENT 'Primary Key', + `service_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Custom Service ID', + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'service name', + `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Description', + `logo` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Logo Address', + `base_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Base URL Address', + `is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT 'Delete Tag(0: No, 1: Yes)', + `created_by` bigint(20) DEFAULT NULL COMMENT 'Creator', + `updated_by` bigint(20) DEFAULT NULL COMMENT 'Last Update By', + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Create Time', + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Update Time', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_service_id` (`service_id`) USING BTREE COMMENT 'Service unique code' + ) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_unicode_ci COMMENT = 'Automation - Service Table'; + + + + Create Automation Trigger Type Table + + CREATE TABLE `${table.prefix}automation_trigger_type` + ( + `id` bigint(20) UNSIGNED NOT NULL COMMENT 'Primary Key', + `service_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Service ID (link#xxxx_automation_service#service_id)', + `trigger_type_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Custom Trigger Prototype ID', + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Name', + `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Description', + `input_json_schema` json DEFAULT NULL COMMENT 'Input JSON normal form', + `output_json_schema` json DEFAULT NULL COMMENT 'Output JSON normal form', + `is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT 'Delete Tag(0: No, 1: Yes)', + `created_by` bigint(20) DEFAULT NULL COMMENT 'Creator', + `updated_by` bigint(20) DEFAULT NULL COMMENT 'Last Update By', + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Create Time', + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Update Time', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_trigger_type_id` (`trigger_type_id`) USING BTREE COMMENT 'Unique code of trigger prototype' + ) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_unicode_ci COMMENT = 'Automation - Trigger Type Table'; + + + + Create Automation Action Type Table + + CREATE TABLE `${table.prefix}automation_action_type` + ( + `id` bigint(20) UNSIGNED NOT NULL COMMENT 'Primary Key', + `service_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Service ID (link#xxxx_automation_service#service_id)', + `action_type_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Custom action prototype ID', + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Name', + `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Description', + `input_json_schema` json DEFAULT NULL COMMENT 'Input JSON normal form', + `output_json_schema` json DEFAULT NULL COMMENT 'Output JSON normal form', + `endpoint` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Call interface', + `is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT 'Delete Tag(0: No, 1: Yes)', + `created_by` bigint(20) DEFAULT NULL COMMENT 'Creator', + `updated_by` bigint(20) DEFAULT NULL COMMENT 'Last Update By', + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Create Time', + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Update Time', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_action_type_id` (`action_type_id`) USING BTREE COMMENT 'Unique code of action prototype' + ) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_unicode_ci COMMENT = 'Automation - Action Type Table'; + + + + diff --git a/init-db/src/main/resources/db/changelog/0.8/20210831_changeset.xml b/init-db/src/main/resources/db/changelog/0.8/20210831_changeset.xml new file mode 100644 index 0000000000000000000000000000000000000000..ee388c4d9f4dc5141318fe886d77fd85c5e4c986 --- /dev/null +++ b/init-db/src/main/resources/db/changelog/0.8/20210831_changeset.xml @@ -0,0 +1,50 @@ + + + + + + + service add slug field + + ALTER TABLE `${table.prefix}automation_service` + ADD COLUMN `slug` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Service readable unique identifier' AFTER `service_id`; + + + + + service add slug field unique index + + ALTER TABLE `${table.prefix}automation_service` + ADD UNIQUE INDEX `uk_service_slug` (`slug`) USING BTREE; + + + + + trigger type add endpoint field + + ALTER TABLE `${table.prefix}automation_trigger_type` + ADD COLUMN `endpoint` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Trigger prototype endpoint' AFTER `output_json_schema`; + + + + diff --git a/init-db/src/main/resources/db/changelog/0.8/20210926_changeset.xml b/init-db/src/main/resources/db/changelog/0.8/20210926_changeset.xml new file mode 100644 index 0000000000000000000000000000000000000000..e34c85a463423b6f413ee6cab202d267f2005955 --- /dev/null +++ b/init-db/src/main/resources/db/changelog/0.8/20210926_changeset.xml @@ -0,0 +1,51 @@ + + + + + + + Widget Package Table add is_template、is_enabled、owner field + + ALTER TABLE `${table.prefix}widget_package` + ADD COLUMN `is_template` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Is template (0: Yes, 1: No)' AFTER `release_id`, + ADD COLUMN `is_enabled` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT 'Enable or not, only for global widgets (0: off, 1: on)' AFTER `is_template`, + ADD COLUMN `owner` bigint NULL DEFAULT NULL COMMENT 'Owner Id(link#xxxx_user#id)' AFTER `is_deleted`; + + + + + Widget Package Auth Table add sequence field + + ALTER TABLE `${table.prefix}widget_package_auth_space` + ADD COLUMN `widget_sort` int UNSIGNED NULL DEFAULT 10000 COMMENT 'Sequence number, space station components start from 10000' AFTER `type`; + + + + + Initialize migration widget package owner field value + + UPDATE `${table.prefix}widget_package` SET `owner` = created_by; + + + + diff --git a/init-db/src/main/resources/db/changelog/0.8/20211020_changeset.xml b/init-db/src/main/resources/db/changelog/0.8/20211020_changeset.xml new file mode 100644 index 0000000000000000000000000000000000000000..1d3f44a9921ba737be379cb74108153a52b09908 --- /dev/null +++ b/init-db/src/main/resources/db/changelog/0.8/20211020_changeset.xml @@ -0,0 +1,46 @@ + + + + + + + Create Automation Robot Run History Table + + CREATE TABLE `${table.prefix}automation_run_history` + ( + `id` bigint(20) UNSIGNED NOT NULL COMMENT 'Primary Key', + `task_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Custom Run Task ID', + `robot_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Robot ID', + `space_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '[Redundancy]ID of the space to which the current task robot belongs', + `status` tinyint(2) unsigned DEFAULT '0' COMMENT 'Running status (0: Running, 1: Success, 2: Failure)', + `data` json DEFAULT NULL COMMENT 'Run Context Details', + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Create Time', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_task_id` (`task_id`) USING BTREE COMMENT 'Unique code of running task', + INDEX `idx_space_id` (`space_id`) COMMENT 'Space ID' + ) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_unicode_ci COMMENT = 'Automation - Robot Run History Table'; + + + diff --git a/init-db/src/main/resources/db/changelog/0.8/20211027_changeset.xml b/init-db/src/main/resources/db/changelog/0.8/20211027_changeset.xml index 895e9dc2265bcfc00b2196af0874b94bd55a32a9..e77d8991edec1a54dbfbe8b69a669b5bb8fdb3b5 100644 --- a/init-db/src/main/resources/db/changelog/0.8/20211027_changeset.xml +++ b/init-db/src/main/resources/db/changelog/0.8/20211027_changeset.xml @@ -30,4 +30,22 @@ ADD INDEX `idx_space_id`(`space_id`) USING BTREE; + + + Robot Table add x_service_token、seq_id and uk_seq_resource_id + + ALTER TABLE `${table.prefix}automation_robot` + ADD COLUMN `seq_id` varchar(64) NULL COMMENT 'Request source request number' AFTER `updated_at`, + ADD COLUMN `x_service_token` varchar(128) NULL COMMENT 'Service Provider Certification Token' AFTER `seq_id`, + ADD UNIQUE INDEX `uk_seq_resource_id`(`seq_id`, `resource_id`) USING BTREE COMMENT 'Unique number of single table robot creation request'; + + + + + Automation Action Table add robot id index + + ALTER TABLE `${table.prefix}automation_action` + ADD INDEX `idx_robot_id`(`robot_id`) USING BTREE COMMENT 'Robot ID Index'; + + diff --git a/init-db/src/main/resources/db/changelog/0.8/20211104_changeset.xml b/init-db/src/main/resources/db/changelog/0.8/20211104_changeset.xml new file mode 100644 index 0000000000000000000000000000000000000000..9db7e6894c9f977027faf2e56834ea73315b72c3 --- /dev/null +++ b/init-db/src/main/resources/db/changelog/0.8/20211104_changeset.xml @@ -0,0 +1,35 @@ + + + + + + + Widget Package Table add widget_body field,modify field description + + ALTER TABLE `${table.prefix}widget_package` + ADD COLUMN `widget_body` json NULL COMMENT 'Widget package extension information' AFTER `release_type`, + MODIFY COLUMN `is_template` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Is template (0: No, 1: Yes)' AFTER `release_id`, + MODIFY COLUMN `is_enabled` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT 'Enable or not, only for global widgets (0: not enabled, 1: enabled)' AFTER `is_template`; + + + diff --git a/init-db/src/main/resources/db/changelog/0.8/master.xml b/init-db/src/main/resources/db/changelog/0.8/master.xml index f7cb09da5f488bcc54e5748c8e014a199b2b9fcf..448ca434e99d53c0efb7996daa8fafae570883e9 100644 --- a/init-db/src/main/resources/db/changelog/0.8/master.xml +++ b/init-db/src/main/resources/db/changelog/0.8/master.xml @@ -22,8 +22,12 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.8.xsd"> + + + + @@ -31,4 +35,5 @@ + diff --git a/init-db/src/main/resources/db/changelog/0.9/20211207_changeset.xml b/init-db/src/main/resources/db/changelog/0.9/20211207_changeset.xml new file mode 100644 index 0000000000000000000000000000000000000000..b1acf902bc69a8c2542e621316ae78d3a4b18a5f --- /dev/null +++ b/init-db/src/main/resources/db/changelog/0.9/20211207_changeset.xml @@ -0,0 +1,45 @@ + + + + + + + Automation Service Table add i18n field + + ALTER TABLE `${table.prefix}automation_service` + ADD COLUMN `i18n` json NULL COMMENT 'Internationalized Language Pack' AFTER `base_url`; + + + + Automation Trigger Type Table add i18n field + + ALTER TABLE `${table.prefix}automation_trigger_type` + ADD COLUMN `i18n` json NULL COMMENT 'Internationalized Language Pack' AFTER `endpoint`; + + + + Automation Action Type add i18n field + + ALTER TABLE `${table.prefix}automation_action_type` + ADD COLUMN `i18n` json NULL COMMENT 'Internationalized Language Pack' AFTER `endpoint`; + + + \ No newline at end of file diff --git a/init-db/src/main/resources/db/changelog/0.9/master.xml b/init-db/src/main/resources/db/changelog/0.9/master.xml index 367ae3a28ba616b9baeeb0d93aaf1f6b189d0b8d..830e9b0723772f7e0ffc24e7b7d9eaf0110c4c5f 100644 --- a/init-db/src/main/resources/db/changelog/0.9/master.xml +++ b/init-db/src/main/resources/db/changelog/0.9/master.xml @@ -24,4 +24,5 @@ + diff --git a/lerna.json b/lerna.json deleted file mode 100644 index 4127566bfb51eac9f906352358897c9719dde711..0000000000000000000000000000000000000000 --- a/lerna.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "packages": [ - "packages/*" - ], - "version": "independent", - "npmClient": "yarn", - "useWorkspaces": true -} \ No newline at end of file diff --git a/package.json b/package.json index 2454bbc63f556da638996c2b90c1eb3bdaf8f6a5..776a1c5957c00f6d03870744e59952694ffa6e22 100644 --- a/package.json +++ b/package.json @@ -1,22 +1,22 @@ { "name": "root", - "version": "0.16.0", + "version": "0.17.0", "private": true, "workspaces": [ "packages/*" ], "scripts": { - "publish": "lerna publish", "sc": "yarn start:core", - "sd": "yarn start:datasheet dev", + "sd": "yarn start:datasheet", "sd:private": "REACT_APP_DEPLOYMENT_MODELS=PRIVATE yarn dev:datasheet", "sr": "yarn start:room-server", - "sss": "yarn workspace @apitable/components dev", + "sss": "yarn start:components", "start:core": "yarn workspace @apitable/core start", "start:i18n": "yarn workspace @apitable/i18n-lang start", "start:datasheet": "yarn workspace @apitable/datasheet dev", "start:widget-sdk": "yarn workspace @apitable/widget-sdk start", "start:room-server": "yarn workspace @apitable/room-server start:dev", + "start:room-server:perf:flame": "yarn workspace @apitable/room-server start:perf:flame", "start:components": "yarn workspace @apitable/components start", "build:core": "yarn workspace @apitable/core build", "build:i18n": "yarn workspace @apitable/i18n-lang build", @@ -44,10 +44,11 @@ "link:widget": "yarn workspace @apitable/core link && yarn workspace @apitable/widget-sdk link && yarn workspace @apitable/components link && yarn workspace @apitable/icons", "postinstall": "cd .. && husky install apitable/.husky", "stylelint:datasheet": "yarn workspace @apitable/datasheet stylelint", - "lint": "eslint --fix --ext packages/*/src/**/!(*system_config.interface|strings.interface).{js,ts,tsx}", + "lint": "eslint --config .eslintrc --ext packages/*/src/**/*.{js,ts,tsx} --format @microsoft/eslint-formatter-sarif --output-file eslint-results.sarif", "cy:open": "yarn workspace @apitable/cypress cy:open", "cy:run": "yarn workspace @apitable/cypress cy:run", - "check": "eslint --print-config packages/*/src/**/!(*system_config.interface|strings.interface).{js,ts,tsx}" + "lint:fix": "eslint --config .eslintrc --fix --ext packages/*/src/**/*.{js,ts,tsx}", + "lint:check": "eslint --config .eslintrc --ext packages/*/src/**/*.{js,ts,tsx}" }, "lint-staged": { "packages/datasheet/src/**/*.{css,less,scss}": "yarn stylelint:datasheet", @@ -63,6 +64,7 @@ "@babel/core": "7.11.6", "@commitlint/cli": "8.1.0", "@commitlint/config-conventional": "8.1.0", + "@microsoft/eslint-formatter-sarif": "^3.0.0", "@types/dot-object": "^2.1.2", "@types/lru-cache": "^5.1.0", "@types/node": "12.7.3", @@ -85,9 +87,8 @@ "eslint-plugin-react-hooks": "^4.3.0", "eslint-plugin-vika": "0.0.1", "husky": "8.0.1", - "lerna": "^3.22.1", "lint-staged": "13.0.3", - "lodash": "^4.17.20", + "lodash": "^4.17.21", "pretty-quick": "^3.1.3", "quicktype": "^15.0.212", "svgo": "^2.3.0", diff --git a/packages/components/.gitignore b/packages/components/.gitignore index 1aa6f8d71376e611eb8f6997c0e0fdb68ae17993..f56c85800315732d07c1f42f20aef105546b1853 100644 --- a/packages/components/.gitignore +++ b/packages/components/.gitignore @@ -25,5 +25,3 @@ yarn-error.log* *.log **/storybook-static - -/sonar-project.properties diff --git a/packages/components/package.json b/packages/components/package.json index 0cb30efb8ffbae31dc7e776d0b9e4b1c6d17ddbe..cdfb1348dd1c5101a04e9ab09a2b906ab155e324 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,9 +1,9 @@ { "name": "@apitable/components", - "version": "0.16.0", + "version": "0.17.0", "main": "dist/index.js", "typings": "dist/index.d.ts", - "author": "APITable PTE. LTD.", + "author": "APITable Ltd. ", "license": "AGPL-3.0", "publishConfig": { "registry": "https://registry.npmjs.org/", @@ -13,13 +13,12 @@ "@apitable/icons": "*", "@apitable/react-contexify": "^5.0.7", "@rjsf/core": "^4.2.3", - "@types/react-window": "1.8.1", "ahooks": "^3.5.0", "antd": "4.23.5", "classnames": "2.2.6", "color": "^3.1.3", "date-fns": "^2.22.1", - "lodash": "4.17.20", + "lodash": "4.17.21", "rc-trigger": "^5.3.3", "rc-util": "^5.24.4", "react-color": "^2.19.3", @@ -58,6 +57,7 @@ "@types/react-dom": "17.0.11", "@types/react-highlight-words": "^0.16.1", "@types/react-is": "^17.0.0", + "@types/react-window": "1.8.1", "@types/resize-observer-browser": "^0.1.5", "@types/styled-components": "^5.1.26", "@types/styled-system": "^5.1.11", @@ -66,6 +66,7 @@ "concurrently": "^7.2.2", "jest": "26.6.0", "jest-environment-jsdom": "26.6.0", + "react-dom": "18.2.0", "storybook-addon-designs": "^6.1.0", "styled-components": "5.3.6", "ts-jest": "26.4.4", @@ -75,19 +76,17 @@ }, "peerDependencies": { "react": "18.2.0", - "react-dom": "18.2.0", "styled-components": "5.3.6" }, "scripts": { - "build": "rm -rf ./dist && yarn tsc && tsc-alias", + "build": "rm -rf ./dist && tsc && tsc-alias", "start": "concurrently \"tsc -w\" \"tsc-alias -w\"", "test": "jest", "test:cov": "jest --coverage", "storybook": "start-storybook -p 6006", "storybook-docs": "start-storybook --docs --no-manager-cache", "build-storybook": "build-storybook", - "test-dev": "npx majestic", - "chromatic": "npx chromatic --project-token=0852f335f2ca vika/components" + "test-dev": "npx majestic" }, "browserslist": { "production": [ diff --git a/packages/components/sonar-project.properties b/packages/components/sonar-project.properties new file mode 100644 index 0000000000000000000000000000000000000000..788726bae04d1a33e66b8ea853aba8820c83c072 --- /dev/null +++ b/packages/components/sonar-project.properties @@ -0,0 +1,15 @@ +sonar.projectKey=apitable_packages_components +sonar.organization=apitable + +# This is the name and version displayed in the SonarCloud UI. +#sonar.projectName=packages_datasheet +#sonar.projectVersion=1.0 + +# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. +#sonar.sources=. + +# Encoding of the source code. Default is default system encoding +sonar.sourceEncoding=UTF-8 + +# See: https://community.sonarsource.com/t/github-actions-sonarcloud-github-action-permissions-issue/41609/9 +sonar.working.directory=/tmp/sonar \ No newline at end of file diff --git a/packages/components/src/colors/base/blackBlue.ts b/packages/components/src/colors/base/blackBlue.ts index 597b903b03903d862c25788a02979775c189bec0..463275a5ed4b37b35a910392f57156d74e5cea17 100644 --- a/packages/components/src/colors/base/blackBlue.ts +++ b/packages/components/src/colors/base/blackBlue.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - export const blackBlue = { 50: '#FDFDFF', 100: '#F9F9FF', diff --git a/packages/components/src/colors/base/blue.ts b/packages/components/src/colors/base/blue.ts index d8e72133776f560474c6cb28f3297340e5920293..849849cc6b4b0d3179ed3310a3bac4e972d9eaad 100644 --- a/packages/components/src/colors/base/blue.ts +++ b/packages/components/src/colors/base/blue.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - export const blue = { 50: '#E4F7FF', 100: '#C9EFFF', diff --git a/packages/components/src/colors/base/brown.ts b/packages/components/src/colors/base/brown.ts index 09b273869010b1e14c571b568424cd086648bc20..4dbc70b22e6c0fb8b7e2c103bf75c4ea72baa18d 100644 --- a/packages/components/src/colors/base/brown.ts +++ b/packages/components/src/colors/base/brown.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - export const brown = { 50: '#E8DFDD', 100: '#D1BFBC', diff --git a/packages/components/src/colors/base/deepPurple.ts b/packages/components/src/colors/base/deepPurple.ts index 81ed3fdc6cb8c56dc04f6bbd8efd3b1b940588e4..7321751953314d83e73ae7308c9e6473ce3ca9e7 100644 --- a/packages/components/src/colors/base/deepPurple.ts +++ b/packages/components/src/colors/base/deepPurple.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - export const deepPurple = { 50: '#EDEAFF', 100: '#DCD6FF', diff --git a/packages/components/src/colors/base/green.ts b/packages/components/src/colors/base/green.ts index 751a1aaa409c1cb8d0eef1a9abbcb38ec43c2ea6..8378d5c6d507d3b11846064ffce5469369fdd8fc 100644 --- a/packages/components/src/colors/base/green.ts +++ b/packages/components/src/colors/base/green.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - export const green = { 50: '#E3F5DA', 100: '#C7F5B1', diff --git a/packages/components/src/colors/base/indigo.ts b/packages/components/src/colors/base/indigo.ts index 57afe9695fbadc26f0c2e675a58d32beafc67f96..fe387f64b40ed8512fa276cbf7adf76bb99bd10e 100644 --- a/packages/components/src/colors/base/indigo.ts +++ b/packages/components/src/colors/base/indigo.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - export const indigo = { 50: '#E0E9FF', 100: '#C2D3FF', diff --git a/packages/components/src/colors/base/orange.ts b/packages/components/src/colors/base/orange.ts index f337bba0c6090dc94742e18e138d6a29dcda9481..511b6a5d90226a429998fb36489332f5c3e0b4e9 100644 --- a/packages/components/src/colors/base/orange.ts +++ b/packages/components/src/colors/base/orange.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - export const orange = { 50: '#FFF6E5', 100: '#FFE5B7', diff --git a/packages/components/src/colors/base/pink.ts b/packages/components/src/colors/base/pink.ts index 352eb21cae18485bd9383de8630a2afb5b7395cf..7aad1b68f532b2fa38252968f68f3c854e2c0866 100644 --- a/packages/components/src/colors/base/pink.ts +++ b/packages/components/src/colors/base/pink.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - export const pink = { 50: '#FFE8EC', 100: '#FFCED8', diff --git a/packages/components/src/colors/base/purple.ts b/packages/components/src/colors/base/purple.ts index c5569f012242d5160bbc172155a3d4174ae78f77..35bd62e8bfbc39eabf42408a1caa4bda2587056f 100644 --- a/packages/components/src/colors/base/purple.ts +++ b/packages/components/src/colors/base/purple.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - export const purple = { 50: '#F4E9FE', 100: '#E4C5FB', diff --git a/packages/components/src/colors/base/red.ts b/packages/components/src/colors/base/red.ts index 9c2d6ab12a71a57858e8365cbfe71fec770e6282..6c0b8a93c04aedc8b47125df7bd3d7618b5fdd6f 100644 --- a/packages/components/src/colors/base/red.ts +++ b/packages/components/src/colors/base/red.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - export const red = { 50: '#FBECEB', 100: '#FFD1C8', diff --git a/packages/components/src/colors/base/tangerine.ts b/packages/components/src/colors/base/tangerine.ts index 0af3b3a31576443b444df9abdedb446d5bca7445..39d1640180763fe9fc2a41d63f9232ef9cebff70 100644 --- a/packages/components/src/colors/base/tangerine.ts +++ b/packages/components/src/colors/base/tangerine.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - export const tangerine = { 50: '#FFEAD6', 100: '#FFD2A8', diff --git a/packages/components/src/colors/base/teal.ts b/packages/components/src/colors/base/teal.ts index 656f1c60233760fe408e977406a05bc743e3c5d4..30cd389b7c26aa570361b5ef14aafc615e60b48c 100644 --- a/packages/components/src/colors/base/teal.ts +++ b/packages/components/src/colors/base/teal.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - export const teal = { 50: '#E2F6EF', 100: '#BDECDA', diff --git a/packages/components/src/colors/base/yellow.ts b/packages/components/src/colors/base/yellow.ts index 18b4171c58b5841addd62b839671f4d1cd6cce67..a08076072447d38c432d0eba18c8fd0f853861ee 100644 --- a/packages/components/src/colors/base/yellow.ts +++ b/packages/components/src/colors/base/yellow.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - export const yellow = { 50: '#FFF9E3', 100: '#FFF2C2', diff --git a/packages/components/src/components/avatar/avatar.tsx b/packages/components/src/components/avatar/avatar.tsx index 4d39c80f5441fe09a83c90bdd8a496c161d25343..2143f7bafdbb817b7430e846a2f6b66307aace0e 100644 --- a/packages/components/src/components/avatar/avatar.tsx +++ b/packages/components/src/components/avatar/avatar.tsx @@ -16,25 +16,25 @@ * along with this program. If not, see . */ -import React, { FC, useRef, useLayoutEffect, useState } from "react"; -import { IAvatarProps } from "./interface"; -import { Box } from "components"; -import { AvatarWrapper, AvatarChildWrapper, AvatarSizeConfig } from "./styled"; +import React, { FC, useLayoutEffect, useRef, useState } from 'react'; +import { IAvatarProps } from './interface'; +import { Box } from 'components'; +import { AvatarChildWrapper, AvatarSizeConfig, AvatarWrapper } from './styled'; export const Avatar: FC = ({ - size = "m", + size = 'm', icon, src, alt, children, style, - shape = "circle" + shape = 'circle' }) => { const avatarNodeRef = useRef(null); const avatarChildrenRef = useRef(null); const [scale, setScale] = useState(1); let childrenToRender; - if (typeof src === "string") { + if (typeof src === 'string') { childrenToRender = ( = ({ /> ); } else if (icon) { - const finalIcon = React.isValidElement(icon) + childrenToRender = React.isValidElement(icon) ? React.cloneElement(icon, { - size: AvatarSizeConfig[size].size * 0.6, - color: (style && style.color) || "#fff", - className: "avatar-icon" - }) + size: AvatarSizeConfig[size].size * 0.6, + color: (style && style.color) || '#fff', + className: 'avatar-icon' + }) : icon; - childrenToRender = finalIcon; } else { childrenToRender = ( = ({ useLayoutEffect(() => { const scale = () => { - if (typeof src === "string" || icon) return 1; + if (typeof src === 'string' || icon) return 1; if (!avatarChildrenRef.current || !avatarNodeRef.current) return 1; const childrenWidth = avatarChildrenRef.current?.offsetWidth; const gap = AvatarSizeConfig[size].gap; diff --git a/packages/components/src/components/avatar/interface.ts b/packages/components/src/components/avatar/interface.ts index 66b52d516d0158506de623e44bff834d0526f3fd..299d9e68733b46d6a861a94dc2bb76cbb9a5352c 100644 --- a/packages/components/src/components/avatar/interface.ts +++ b/packages/components/src/components/avatar/interface.ts @@ -16,11 +16,15 @@ * along with this program. If not, see . */ +import { AvatarSizeConfig } from './styled'; + +export type IAvatarSizeConfigKey = keyof typeof AvatarSizeConfig; + export interface IAvatarProps { /** avatar shape */ shape?: 'circle' | 'square'; /** avatar size xxs(20px)、xs(24px)、s(32px)、m(40px)、l(64px)、xl(80px) */ - size?: 'xxs' | 'xs' | 's' | 'm' | 'l' | 'xl'; + size?: IAvatarSizeConfigKey; /** avatar link */ src?: React.ReactNode; /** avatar icon */ @@ -31,5 +35,4 @@ export interface IAvatarProps { className?: string; /** avatar image alt attribute */ alt?: string; - } \ No newline at end of file diff --git a/packages/components/src/components/avatar/styled.tsx b/packages/components/src/components/avatar/styled.tsx index 959975e79dc7fc8b5cb7617cfd72b44bf13ef366..bbf9a67f8bffb08b460d28a8bde901d181b363d5 100644 --- a/packages/components/src/components/avatar/styled.tsx +++ b/packages/components/src/components/avatar/styled.tsx @@ -20,7 +20,7 @@ import { IAvatarProps } from './interface'; import styled from 'styled-components'; import { get } from 'lodash'; -export const AvatarSizeConfig:{[key: string]:{ size: number, borderRadius: number, borderWidth: number, fontSize:number, gap:number }} = { +export const AvatarSizeConfig = { xxs: { size: 20, borderRadius: 2, diff --git a/packages/components/src/components/button_group/button_group.tsx b/packages/components/src/components/button_group/button_group.tsx index 738749343b48cb0309049d4ce747937393eccb5f..c742141aa6828012e723efd4ddffe742d157ea91 100644 --- a/packages/components/src/components/button_group/button_group.tsx +++ b/packages/components/src/components/button_group/button_group.tsx @@ -22,11 +22,7 @@ import { ButtonGroupBase } from './styled'; export const ButtonGroup = React.forwardRef(({ children, - withSeparate, - withBorder, - className, - ...restProps -}:IButtonGroupProps, ref: React.Ref) => { +}:IButtonGroupProps, _ref: React.Ref) => { return ( {children} diff --git a/packages/components/src/components/calendar/calendar.tsx b/packages/components/src/components/calendar/calendar.tsx index 8580e0acb12e8ebeabff39bacbc4e297b51f308f..445ab00fe4d1e9e17b83a537d9d7846bf4b91064 100644 --- a/packages/components/src/components/calendar/calendar.tsx +++ b/packages/components/src/components/calendar/calendar.tsx @@ -47,7 +47,7 @@ export const Calendar:FC = props => { const currStep = differenceInMonths(date2Month(defaultDate), date2Month(new Date())); setStep(currStep); } - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line }, [defaultDate2Month]); const { year, month } = useMemo(() => getPanelData(step), diff --git a/packages/components/src/components/calendar/drag/drop.tsx b/packages/components/src/components/calendar/drag/drop.tsx index 82bdc029b0dae5d54b861d49a1e76b109faee237..e949d2a8a8d33744cd505e178d6deed3199cad9a 100644 --- a/packages/components/src/components/calendar/drag/drop.tsx +++ b/packages/components/src/components/calendar/drag/drop.tsx @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - import React from 'react'; import { useDrop } from 'react-dnd'; import { TYPE } from '../constants'; diff --git a/packages/components/src/components/calendar/hooks/useResize.tsx b/packages/components/src/components/calendar/hooks/useResize.tsx index 7c815c19ba58f700c404d9247d886fd7bf0c5134..17701d39142d5f06e0597c0204c438a7d16d01b4 100644 --- a/packages/components/src/components/calendar/hooks/useResize.tsx +++ b/packages/components/src/components/calendar/hooks/useResize.tsx @@ -25,10 +25,10 @@ export const useResize = ({ height = [], width, update, setResizeDay, tasks } : const resizeRef = useRef(); const onMouseMove = (event: MouseEvent) => { const isRight = resizeRef.current?.direction === Direction.Right; - const clientX = isTouchEvent(event) ? event.touches[0].clientX : event.clientX; - const clientY = isTouchEvent(event) ? event.touches[0].clientY : event.clientY; - const diffY = clientY - (resizeRef.current?.clientY || 0); - const diffX = clientX - (resizeRef.current?.clientX || 0); + const clientX = isTouchEvent(event) ? event.touches[0]?.clientX : event.clientX; + const clientY = isTouchEvent(event) ? event.touches[0]?.clientY : event.clientY; + const diffY = (clientY || 0) - (resizeRef.current?.clientY || 0); + const diffX = (clientX || 0) - (resizeRef.current?.clientX || 0); const topF = (resizeRef.current?.top || 0) % height[0]!; let week = 0; let day = 0; @@ -63,8 +63,8 @@ export const useResize = ({ height = [], width, update, setResizeDay, tasks } : clientX = event.nativeEvent.clientX; clientY = event.nativeEvent.clientY; } else if (event.nativeEvent && isTouchEvent(event.nativeEvent)) { - clientX = (event.nativeEvent as TouchEvent).touches[0].clientX; - clientY = (event.nativeEvent as TouchEvent).touches[0].clientY; + clientX = (event.nativeEvent as TouchEvent).touches[0]?.clientX || 0; + clientY = (event.nativeEvent as TouchEvent).touches[0]?.clientY || 0; } resizeRef.current = { id, clientX, clientY, direction, top: parseInt(parentNode.style.top), day: 0 }; window.addEventListener('mousemove', onMouseMove); @@ -74,7 +74,7 @@ export const useResize = ({ height = [], width, update, setResizeDay, tasks } : const direction = resizeRef.current?.direction!; const day = resizeRef.current?.day; if (day && update) { - const task = tasks.filter(t => resizeRef.current?.id === t.id)[0]; + const task = tasks.filter(t => resizeRef.current?.id === t.id)[0]!; const { startDate, endDate } = task; const formatData = resizeFormat({ startDate, endDate, day, direction }); update(task.id, formatData.startDate, formatData.endDate); diff --git a/packages/components/src/components/calendar/month/month.tsx b/packages/components/src/components/calendar/month/month.tsx index 28900dd6af1f07e78a748726ad108b308dc4ae88..2cef34d17bca57ef9cf8d303457faee84e1e2093 100644 --- a/packages/components/src/components/calendar/month/month.tsx +++ b/packages/components/src/components/calendar/month/month.tsx @@ -77,7 +77,7 @@ const MonthBase:FC = props => { return () => { window.removeEventListener('resize', handleResize, false); }; - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line }, []); useEffect(() => { setWidth(calendarWidth / 7); diff --git a/packages/components/src/components/calendar/month/styled.tsx b/packages/components/src/components/calendar/month/styled.tsx index 0ae1a3ad000d0ff553c84dfdeb557ea17bf5f6fc..c1e91a783865b679d9c002c9ae3676f7701c063a 100644 --- a/packages/components/src/components/calendar/month/styled.tsx +++ b/packages/components/src/components/calendar/month/styled.tsx @@ -117,7 +117,7 @@ export const DaySpan = styled.span` font-size: 13px; font-weight: 500; - ${props => css` + ${() => css` color: #C9C9C9; `} `; diff --git a/packages/components/src/components/calendar/month/week.tsx b/packages/components/src/components/calendar/month/week.tsx index 624337277da426093999a7dcc707e85e04931850..a19491a7b123ffa7d3f354fae56ce565c399c1e0 100644 --- a/packages/components/src/components/calendar/month/week.tsx +++ b/packages/components/src/components/calendar/month/week.tsx @@ -101,7 +101,7 @@ export const WeekBase = (props: IWeek) => { if (m <= MAX_LEVEL) { return null; } - const curDay = week[mIndex]; + const curDay = week[mIndex]!; return ( { - const start = week[0]; - const end = week[week.length - 1]; + const start = week[0]!; + const end = week[week.length - 1]!; const startDate = new Date(year, start.month - 1, start.day); const endDate = new Date(year, end.month - 1, end.day); let updateTasks = tasks; @@ -202,9 +202,9 @@ export const getLevels = ({ week, year, tasks, resizeMsg }: ILevel) => { const levels: ILevelResult[][] = []; let j: number; for (let i = 0; i < rowTasks.length; i++) { - const task = rowTasks[i]; + const task = rowTasks[i]!; for (j = 0; j < levels.length; j++) { - const isOver = levels[j].some(seg => + const isOver = levels[j]!.some(seg => seg.left <= task.right && seg.right >= task.left ); if (!isOver) { @@ -216,6 +216,6 @@ export const getLevels = ({ week, year, tasks, resizeMsg }: ILevel) => { return levels; }; -export const formatDayValue = (month, day, lang: 'en' | 'zh',) => { +export const formatDayValue = (month: number, day: number, lang: 'en' | 'zh',) => { return lang === 'zh' ? `${month}月${day}日` : `${MONTHS[month - 1]} ${day}`; }; diff --git a/packages/components/src/components/checkbox/interface.ts b/packages/components/src/components/checkbox/interface.ts index 6d946dde77c32649a5d769409bb594a57d6b0f20..197d69ffb69a0b1e0708c93a2e1fa87acc539a79 100644 --- a/packages/components/src/components/checkbox/interface.ts +++ b/packages/components/src/components/checkbox/interface.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - export interface ICheckboxProps { /** * whether the checkbox should be checked diff --git a/packages/components/src/components/context_menu/context_menu.tsx b/packages/components/src/components/context_menu/context_menu.tsx index 2c97ffd705e8cff855b078a622555a613439675c..668805cfc870eab1137d933982985bd626ca4ef8 100644 --- a/packages/components/src/components/context_menu/context_menu.tsx +++ b/packages/components/src/components/context_menu/context_menu.tsx @@ -22,6 +22,7 @@ import { StyledMenuContainer, StyledSubMenu, StyledMenuItem, StyledMenuItemConte StyledMenuShadow, StyledMenuItemExtra } from './styled'; import { ICacheOverlay, IContextMenuClickState, IContextMenuItemProps, IContextMenuProps } from './interface'; import { manager } from './event_manager'; +import { IMenuConfig } from './interface'; import { EVENT_TYPE } from './consts'; import { Tooltip } from 'antd'; import { omit } from 'lodash'; @@ -52,7 +53,7 @@ const ContextMenuWrapper: FC = (props) => { const { offset, extraInfo } = contextMenuState; // Compatible with the old context menu parameter callback method - const getExtraInfo = (info) => { + const getExtraInfo = (info: any) => { return info; }; @@ -75,7 +76,7 @@ const ContextMenuWrapper: FC = (props) => { const calcCacheLevel = (list: IContextMenuItemProps[], level = 0) => { let sub = 0; for (let i = 0; i < list.length; i++) { - const item = list[i]; + const item = list[i]!; if (getHidden(item.hidden)) { sub += 1; continue; @@ -132,7 +133,7 @@ const ContextMenuWrapper: FC = (props) => { }, [menuRef, cancelContextMenu]); // menu Item click event - const handleClick = (item: IContextMenuItemProps, keyPath, e: MouseEvent) => { + const handleClick = (item: IContextMenuItemProps, keyPath: string[], e: MouseEvent) => { if ( (typeof item.disabled === 'function' && item.disabled(getExtraInfo(extraInfo))) || (typeof item.disabled === 'boolean' && item.disabled) @@ -152,7 +153,7 @@ const ContextMenuWrapper: FC = (props) => { }; // mouse enter event - const handleMouseEnter = (item, index) => { + const handleMouseEnter = (item: IContextMenuItemProps, index: number) => { const menu = menuRef.current; if (!menu) return; setPaths((source) => { @@ -167,13 +168,13 @@ const ContextMenuWrapper: FC = (props) => { }; // recursion - const dfs = (source: IContextMenuItemProps[], results, index = 0) => { + const dfs = (source: IContextMenuItemProps[], results: { key: string, label: JSX.Element }[][], index = 0) => { if (!results[index]) { results[index] = []; } const filterHiddenSource = source.filter((v) => !getHidden(v.hidden)); for (let i = 0; i < filterHiddenSource.length; i++) { - const item = filterHiddenSource[i]; + const item = filterHiddenSource[i]!; // filter hidden key const newItem = omit(item, 'hidden'); const { key, id, disabled = false, icon, label, arrow, children, groupId, extraElement, disabledTip, ...rest } = newItem; @@ -215,7 +216,7 @@ const ContextMenuWrapper: FC = (props) => { ); } - results[index].push({ + results[index]!.push({ key, label: labelElement, }); @@ -226,7 +227,8 @@ const ContextMenuWrapper: FC = (props) => { }; // Compatible with the usage of menuId and show - const handler = React.useCallback((configs: { e: React.MouseEvent, extraInfo?: any }) => { + const handler = React.useCallback((configs?: IMenuConfig) => { + if (!configs) return; const { e, extraInfo } = configs; setContextMenuState({ offset: [e.clientX, e.clientY], @@ -275,7 +277,7 @@ const ContextMenuWrapper: FC = (props) => { const { innerHeight } = window; // Calculate the top value when the screen height is exceeded - const getTop = (top, height) => { + const getTop = (top: number, height: number) => { const total = top + height; if (height > innerHeight) { return 0; @@ -307,26 +309,26 @@ const ContextMenuWrapper: FC = (props) => { const lastElement = child.lastElementChild as HTMLElement; if (!parentKey) { - let total = offset[1] + scrollHeight; - const curTop = getTop(offset[1], scrollHeight); + let total = offset[1]! + scrollHeight; + const curTop = getTop(offset[1]!, scrollHeight); total = curTop + scrollHeight; const isOver = total > innerHeight; let cssText = isOver ? `height: ${innerHeight - menuSubSpaceHeight}px; overflow: auto;` : ''; // Calculate whether there is free space. If not, calculate the difference - const endX = offset[0] + menuOffset[0] + width; + const endX = offset[0]! + menuOffset[0]! + width; const subX = endX > innerWidth ? endX - innerWidth : 0; cssText += ` - left: ${offset[0] + menuOffset[0] - subX}px; - top: ${curTop + menuOffset[1]}px; + left: ${offset[0]! + menuOffset[0]! - subX}px; + top: ${curTop + menuOffset[1]!}px; opacity: 1; width: ${width}px; `; child.style.cssText = cssText; lastElement.style.cssText = isOver ? ` - left: ${offset[0] + menuOffset[0] - subX}px; - top: ${curTop + innerHeight - (menuSubSpaceHeight + SYADOW_HEIGHT) + menuOffset[1]}px; + left: ${offset[0]! + menuOffset[0]! - subX}px; + top: ${curTop + innerHeight - (menuSubSpaceHeight + SYADOW_HEIGHT) + menuOffset[1]!}px; width: ${width}px; ` : ''; calcScroll(child); @@ -336,7 +338,7 @@ const ContextMenuWrapper: FC = (props) => { // Find the parent menu sub item of the sub menu, and get the top of the sub menu according to the top of the parent menu sub item const parentContainer = childList[i - 1] as HTMLElement; const parentList = parentContainer.childNodes; - const { index } = cacheOverlay[parentKey]; + const { index } = cacheOverlay[parentKey]!; const parentElement = parentList[index] as HTMLElement; const { top: parentTop } = parentElement.getBoundingClientRect(); const parentTopWithoutPadding = parentTop - 4; @@ -348,14 +350,14 @@ const ContextMenuWrapper: FC = (props) => { const isOverChild = childTotal > innerHeight; const preParentWidthSum = Array.from(childList) - .filter((item, k) => k < i) + .filter((_item, k) => k < i) .reduce((pre) => { pre += width; return pre; }, 0); // Calculate the left and right offsets. When the remaining space is not enough for placement, take the parent as a reference - let childStartX = (childList[0] as HTMLElement).offsetLeft + preParentWidthSum + menuOffset[0]; + let childStartX = (childList[0] as HTMLElement).offsetLeft + preParentWidthSum + menuOffset[0]!; if (childStartX + width > innerWidth) { childStartX -= (preParentWidthSum + width * i); } @@ -363,13 +365,13 @@ const ContextMenuWrapper: FC = (props) => { let childCssText = `opacity: 1; width: ${width}px;`; childCssText += ` left: ${childStartX}px; - top: ${childTop + menuOffset[1]}px; + top: ${childTop + menuOffset[1]!}px; `; childCssText += isOverChild ? `height: ${innerHeight - menuSubSpaceHeight}px; overflow: auto;` : ''; child.style.cssText = childCssText; lastElement.style.cssText = isOverChild ? ` left: ${childStartX}px; - top: ${childTop + innerHeight - (menuSubSpaceHeight + SYADOW_HEIGHT) + menuOffset[1]}px; + top: ${childTop + innerHeight - (menuSubSpaceHeight + SYADOW_HEIGHT) + menuOffset[1]!}px; width: ${width}px; ` : ''; calcScroll(child); @@ -398,8 +400,8 @@ const ContextMenuWrapper: FC = (props) => { const style: React.CSSProperties = {}; if (Boolean(children)) { if (offset) { - style.left = offset[0] + menuOffset[0]; - style.top = offset[1] + menuOffset[1]; + style.left = offset[0]! + menuOffset[0]!; + style.top = offset[1]! + menuOffset[1]!; } else { style.opacity = 0; style.transform = 'scale(0)'; diff --git a/packages/components/src/components/context_menu/event_manager.ts b/packages/components/src/components/context_menu/event_manager.ts index ed59b2497f19f6e9773d6f9d1e2c69e66b12d361..da43673c71b7c2981d732d8d6daa7f61bd404298 100644 --- a/packages/components/src/components/context_menu/event_manager.ts +++ b/packages/components/src/components/context_menu/event_manager.ts @@ -16,19 +16,19 @@ * along with this program. If not, see . */ -import { MouseEvent } from 'react'; +import { IMenuConfig, IMenuEventHandler } from './interface'; function eventManager() { - const map = new Map(); + const map = new Map(); return { - off: (id) => { + off: (id: string) => { map.delete(id); }, - on: (id, handler) => { + on: (id: string, handler: IMenuEventHandler) => { map.set(id, handler); }, - emit: (id, configs?: { e: MouseEvent, extraInfo?: any }) => { + emit: (id: string, configs?: IMenuConfig) => { const handler = map.get(id); if (handler) { handler(configs); diff --git a/packages/components/src/components/context_menu/hooks.ts b/packages/components/src/components/context_menu/hooks.ts index 5a0369777a4a187a5b96d94818dfe8b14379f83f..1eac20a57dcaaf7c54544477cd506e9e06197c57 100644 --- a/packages/components/src/components/context_menu/hooks.ts +++ b/packages/components/src/components/context_menu/hooks.ts @@ -65,6 +65,6 @@ export const contextMenuHideAll = () => { manager.emit(EVENT_TYPE.HIDE_ALL); }; -export const contextMenuShow = (e: MouseEvent, id, extraInfo?: any) => { +export const contextMenuShow = (e: MouseEvent, id: string, extraInfo?: any) => { manager.emit(id, { e, extraInfo }); }; diff --git a/packages/components/src/components/context_menu/interface.ts b/packages/components/src/components/context_menu/interface.ts index ac32fe67009a2e97627cbe91a2dfbb21e6fd0d82..73fac04c13ce1e37d4f3c69a333bb22f759a2695 100644 --- a/packages/components/src/components/context_menu/interface.ts +++ b/packages/components/src/components/context_menu/interface.ts @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import React from 'react'; +import React, { MouseEvent } from 'react'; export interface IContextMenuClickState { /** @@ -207,3 +207,10 @@ export interface IContextMenuStyleProps { */ isGroup?: boolean; } + +export interface IMenuConfig { + e: MouseEvent; + extraInfo?: any; +} + +export type IMenuEventHandler = (configs?: IMenuConfig) => void; diff --git a/packages/components/src/components/form/rjsf_ui/field/title_field.tsx b/packages/components/src/components/form/rjsf_ui/field/title_field.tsx index 8fb9106c21f15909895a55ea3c65a2c4520bcea0..28ccbf0cf6fc046649bdf81527e2c0fbeba0fa0c 100644 --- a/packages/components/src/components/form/rjsf_ui/field/title_field.tsx +++ b/packages/components/src/components/form/rjsf_ui/field/title_field.tsx @@ -105,8 +105,8 @@ export const TitleField = (props: Pick onChange?: (collapse: boolean) => void; }) => { const { title, id, help, hasCollapse, defaultCollapse = false, onChange } = props; - const [, level] = (id || '').split('-'); - const TitleComponent = titleLevel[Math.min(parseInt(level, 10) || 0, 2)]; + const [, level] = (id || '').split('-') as [string, string]; + const TitleComponent = titleLevel[Math.min(parseInt(level, 10) || 0, 2)]!; const [collapse, setCollapse] = useState(defaultCollapse); const switchCollapse = () => { diff --git a/packages/components/src/components/form/rjsf_ui/template/field_template.tsx b/packages/components/src/components/form/rjsf_ui/template/field_template.tsx index 90beafc580cd122defe3f956084d3785030b0136..85c3b4e3825a39e3a1d5af4df976ef50cc0cb7cf 100644 --- a/packages/components/src/components/form/rjsf_ui/template/field_template.tsx +++ b/packages/components/src/components/form/rjsf_ui/template/field_template.tsx @@ -36,7 +36,7 @@ export const FieldTemplate = (props: FieldTemplateProps) => { }; const paddingTop = (() => { if (showTitle()) return 16; - const [, level] = (id || '').split('-'); + const [, level] = (id || '').split('-') as [string, string]; // The secondary title is hidden if (parseInt(level, 10) === 1 && !showTitle()) { return 0; diff --git a/packages/components/src/components/form/rjsf_ui/widget/checkboxes.tsx b/packages/components/src/components/form/rjsf_ui/widget/checkboxes.tsx index 11e5e23b8f3a8cbd69e1742a80be1b3f853dd877..3aa168d0d4c84ca6985b57c8f8308d24b4ea2ef6 100644 --- a/packages/components/src/components/form/rjsf_ui/widget/checkboxes.tsx +++ b/packages/components/src/components/form/rjsf_ui/widget/checkboxes.tsx @@ -33,18 +33,10 @@ const deselectValue = (value: any, selected: any) => { }; export const CheckboxesWidget = ({ - schema, - label, id, - disabled, options, value, - autofocus, - readonly, - required, onChange, - onBlur, - onFocus, }: WidgetProps) => { const { enumOptions, enumDisabled } = options; diff --git a/packages/components/src/components/form/rjsf_ui/widget/color_picker.tsx b/packages/components/src/components/form/rjsf_ui/widget/color_picker.tsx index ad6a084896d5426748d6fd23cdd758f50172fd20..40513813f9cdbb898262c562fb6a4c27a206b484 100644 --- a/packages/components/src/components/form/rjsf_ui/widget/color_picker.tsx +++ b/packages/components/src/components/form/rjsf_ui/widget/color_picker.tsx @@ -73,7 +73,7 @@ export const ColorWidget = ({ value, onChange }: WidgetProps) => { setDisplayColorPicker(!displayColorPicker); }; - const handleChange = (color: ColorResult, event: React.ChangeEvent) => { + const handleChange = (color: ColorResult) => { setColor(color.rgb); const { r, g, b, a = 1 } = color.rgb; const newColor = Color({ r, g, b }).alpha(a).rgb().string(); diff --git a/packages/components/src/components/form/rjsf_ui/widget/toggle_button.tsx b/packages/components/src/components/form/rjsf_ui/widget/toggle_button.tsx index 35150e22885b4f633f030332d669cd6a7734c38b..c55575b9f9fb109f56a27ffe5f57cfc9eae9ca70 100644 --- a/packages/components/src/components/form/rjsf_ui/widget/toggle_button.tsx +++ b/packages/components/src/components/form/rjsf_ui/widget/toggle_button.tsx @@ -27,7 +27,7 @@ export const ToggleButtonWidget = (props: WidgetProps) => { isBtn block options={props.options.enumOptions as any} - onChange={(e, value) => props.onChange(value)} + onChange={(_e, value) => props.onChange(value)} value={props.value || props.defaultValue} /> diff --git a/packages/components/src/components/link_button/interface.ts b/packages/components/src/components/link_button/interface.ts index 5248f2b1869e61d8d7d3c2074027ac6c44620e11..916834d0c082eddf479cf440d3f3bcc35e1060cc 100644 --- a/packages/components/src/components/link_button/interface.ts +++ b/packages/components/src/components/link_button/interface.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - import React, { ElementType } from 'react'; export interface ILinkButtonProps extends React.LinkHTMLAttributes { diff --git a/packages/components/src/components/list/list.tsx b/packages/components/src/components/list/list.tsx index 6694beac0ba7141d1cc1d74dc22d0f9e13409f3e..58fd8adb4db07168bfdffc7ef618819c97b58b64 100644 --- a/packages/components/src/components/list/list.tsx +++ b/packages/components/src/components/list/list.tsx @@ -28,7 +28,7 @@ export const List = React.forwardRef(({ data, renderItem, ...resetProps -}: IListProps, ref: React.Ref) => { +}: IListProps) => { const renderInnerItem = (d: string | IListItemProps, index: number) => { return renderItem ? renderItem(d, index) : typeof d === 'string' ? {d} : ; diff --git a/packages/components/src/components/list_deprecate/list_search.ignore.tsx b/packages/components/src/components/list_deprecate/list_search.ignore.tsx index 61c10fffb80497d01a269d4c54d91a6b05df7396..17e1351d9539910650aabac845606950f0eb32a7 100644 --- a/packages/components/src/components/list_deprecate/list_search.ignore.tsx +++ b/packages/components/src/components/list_deprecate/list_search.ignore.tsx @@ -27,7 +27,7 @@ export const ListSearch = (props: any) => { onSearchChange && onSearchChange(e, e.target!.value); }; - const onPressEnter = (e: KeyboardEvent) => { + const onPressEnter = () => { onInputEnter && onInputEnter(() => { setKeyword(''); }); diff --git a/packages/components/src/components/message/components/message_ui.tsx b/packages/components/src/components/message/components/message_ui.tsx index f66c6e8a7fd48a9187c95a8a9febd2992e678b6f..48ea5676a79f7336b13fefe77fb9bec57ae5e382 100644 --- a/packages/components/src/components/message/components/message_ui.tsx +++ b/packages/components/src/components/message/components/message_ui.tsx @@ -119,7 +119,7 @@ export const MessageUI: React.FC = ({ onDestroy && onDestroy(); }, duration * 1000); } - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line }, [duration]); useEffect(()=>{ diff --git a/packages/components/src/components/message/components/use_message.tsx b/packages/components/src/components/message/components/use_message.tsx index 59c4d8bed86f377215efa80d7ead88a0791c1dd9..ece20609d8e450617d654a26fd51be22022834be 100644 --- a/packages/components/src/components/message/components/use_message.tsx +++ b/packages/components/src/components/message/components/use_message.tsx @@ -76,7 +76,7 @@ const MessageUiContainer = (props: IMessageUIProps) => { onDestroy: () => remove(key), }, }); - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line }, [props]); return ( @@ -85,7 +85,7 @@ const MessageUiContainer = (props: IMessageUIProps) => { {({ key, className: motionClassName }) => } @@ -95,7 +95,7 @@ const MessageUiContainer = (props: IMessageUIProps) => { export const createUseMessage = () => { let domWrapper: HTMLDivElement | null = null; - const useMessage = (props: IMessageUIProps) => { + return (props: IMessageUIProps) => { if (!domWrapper) { const rootDom = document.createElement('div'); document.body.appendChild(rootDom); @@ -105,7 +105,5 @@ export const createUseMessage = () => { root.render( ); }; - - return useMessage; }; diff --git a/packages/components/src/components/modal/components/modal_base.tsx b/packages/components/src/components/modal/components/modal_base.tsx index 69e7a243728bd67be7a09bee84d9eae9d0592cdc..485a0f40c524837f631d31a14b3d1d84599a15e6 100644 --- a/packages/components/src/components/modal/components/modal_base.tsx +++ b/packages/components/src/components/modal/components/modal_base.tsx @@ -95,7 +95,7 @@ export const ModalBase: React.FC = (props) => { if (displayNone && !visible) { setBodyStyle(initialBodyStyle.width, initialBodyStyle.overflow); } - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line }, [displayNone, visible]); useUnmount(() => { diff --git a/packages/components/src/components/pagination/i18n.ts b/packages/components/src/components/pagination/i18n.ts index 71821cb8ade8534ead5a1d3cbd88fe5d960530b8..f0ca1fa17c20b18b49c6756f671c2b0a71016ffe 100644 --- a/packages/components/src/components/pagination/i18n.ts +++ b/packages/components/src/components/pagination/i18n.ts @@ -23,7 +23,7 @@ interface IConfigLang { const pattern = /\{.+?\}/g; -export const t = (obj: IConfigLang, lang, values?: any[]) => { +export const t = (obj: IConfigLang, lang: string, values?: any[]) => { const str = obj[lang || 'zh'] as string; if (!values) { return str; @@ -35,7 +35,7 @@ export const t = (obj: IConfigLang, lang, values?: any[]) => { } let res = str; for (let i = 0; i < matches.length; i++) { - res = res.replace(matches[i], values[i]); + res = res.replace(matches[i]!, values[i]); } return res; }; \ No newline at end of file diff --git a/packages/components/src/components/pagination/pagination.tsx b/packages/components/src/components/pagination/pagination.tsx index 78a5a9bde534e3c5c0c9d6ec81403ec7cf5257b4..f667e7902e0930262317458227c4f8231a19cc1c 100644 --- a/packages/components/src/components/pagination/pagination.tsx +++ b/packages/components/src/components/pagination/pagination.tsx @@ -257,7 +257,7 @@ const PaginationBase: FC = (props) => { * @param start Page number drop down start position * @param end Page number drop down end position */ - const renderEllipse = (key, start, end) => { + const renderEllipse = (key: number, start: number, end: number) => { const options: IOption[] = []; for (let i = start; i <= end; i++) { options.push({ value: i, label: i.toString() }); diff --git a/packages/components/src/components/radio/group/group.tsx b/packages/components/src/components/radio/group/group.tsx index 8c3895033d5e14276de393db9322bb7d8d55490b..b69ce1518cf88c05d93f876aead0e5c82d1b0cae 100644 --- a/packages/components/src/components/radio/group/group.tsx +++ b/packages/components/src/components/radio/group/group.tsx @@ -29,9 +29,9 @@ export const RadioGroup = React.forwardRef(({ onChange, options, ...restProps -}: IRadioGroup, ref: React.Ref) => { +}: IRadioGroup) => { const [value, setValue] = React.useState(() => restProps.value); - const handleChange = (event) => { + const handleChange = (event: React.ChangeEvent) => { const targetValue = event.target.value; setValue(targetValue); if (onChange) { diff --git a/packages/components/src/components/radio/radio.tsx b/packages/components/src/components/radio/radio.tsx index 570fa79380a70e6e2a0136469bd317b63df4b907..dcf1bcecbc7ce4632c2ff98dd3d95f8e98daaf34 100644 --- a/packages/components/src/components/radio/radio.tsx +++ b/packages/components/src/components/radio/radio.tsx @@ -30,9 +30,9 @@ export const Radio = React.forwardRef(({ readOnly, value, ...restProps -}: IRadio, ref: React.Ref) => { +}: IRadio) => { const context = React.useContext(RadioGroupContext); - const handleChange = (e) => { + const handleChange = (e: React.ChangeEvent) => { onChange?.(e); context?.onChange?.(e); }; diff --git a/packages/components/src/components/select/select.tsx b/packages/components/src/components/select/select.tsx index 7e0cf356384d531220d55838164361e043ac3508..44d3dc980c94e60db2e126872e9e68509bf3e7fe 100644 --- a/packages/components/src/components/select/select.tsx +++ b/packages/components/src/components/select/select.tsx @@ -59,7 +59,7 @@ export const Select: FC & { const OFFSET = [0, 4]; const selectedOption = options.filter(item => Boolean(item)).find(item => item!.value === value); - const inputOnChange = (e: React.ChangeEvent, keyword: string) => { + const inputOnChange = (_e: React.ChangeEvent, keyword: string) => { setKeyword(keyword); }; @@ -70,7 +70,7 @@ export const Select: FC & { return; } setVisible(false); - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line }, [value, toggleVisible, defaultVisible, isInit]); useEffect(() => { @@ -82,7 +82,7 @@ export const Select: FC & { const size = (triggerRef.current.getRootDomNode() as HTMLElement).getBoundingClientRect(); setTriggerInfo({ triggerSize: size, triggerOffset: OFFSET, adjust: true }); } - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line }, [triggerRef]); useClickAway(() => { @@ -141,7 +141,7 @@ export const Select: FC & { > { dropdownRender || { + onClick={(_e, index) => { setVisible(false); onSelected && onSelected(afterFilterOptions[index]!, index); }} @@ -167,7 +167,7 @@ export const Select: FC & { ); }; - const triggerClick = (e: React.MouseEvent) => { + const triggerClick = () => { if (disabled) { return; } diff --git a/packages/components/src/components/text_button/interface.ts b/packages/components/src/components/text_button/interface.ts index 476611966c55236da50bad66a833a8b9916a050e..864a70b7e68e09f3c06256de3ea506911d1b7a07 100644 --- a/packages/components/src/components/text_button/interface.ts +++ b/packages/components/src/components/text_button/interface.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - type IButtonHTMLAttributes = Omit, 'color'>; export interface ITextButtonProps extends IButtonHTMLAttributes { diff --git a/packages/components/src/components/tree_view/tree_item/tree_item.tsx b/packages/components/src/components/tree_view/tree_item/tree_item.tsx index 161df94aacf93db61b88f832f964f7d5f3cfc604..3811298a5696f9ab1cedd950bec94a7ee010b161 100644 --- a/packages/components/src/components/tree_view/tree_item/tree_item.tsx +++ b/packages/components/src/components/tree_view/tree_item/tree_item.tsx @@ -168,11 +168,9 @@ const TreeItemBase: FC = ({ nodeId, selectable = true, isLeaf = false, - data = null, pos, parentNode, children, - className, draggable = true, }) => { const { @@ -193,7 +191,7 @@ const TreeItemBase: FC = ({ const [{ isOver }, dndDrop] = useDrop({ accept: module, - hover: (item, monitor) => { + hover: (_item, monitor) => { setHoverNodeId(nodeId); const isGap = isOverGap(monitor.getClientOffset(), nodeRef.current); setDropPosition(isGap); diff --git a/packages/components/src/components/tree_view/tree_view.tsx b/packages/components/src/components/tree_view/tree_view.tsx index 4d1c9a23e6fa07fec587594a76e7ae1731e6b611..9449eff5853ffcb8ebf5739be43ab2d1cf77e7a4 100644 --- a/packages/components/src/components/tree_view/tree_view.tsx +++ b/packages/components/src/components/tree_view/tree_view.tsx @@ -61,7 +61,6 @@ const TreeViewRoot = styled.ul` export const TreeView: FC = React.memo( ({ module, - className, switcherIcon = , switcherLoadingIcon = , expandedKeys = null, @@ -123,7 +122,7 @@ export const TreeView: FC = React.memo( } setExpandedIds(expandedKeys); setCacheExpandedId(expandedKeys); - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line }, [expandedKeys]); useEffect(() => { diff --git a/packages/components/src/helper/color_helper.ts b/packages/components/src/helper/color_helper.ts index 3d19788cb2f745ec6cbf9c75e99ab0d7a990e73a..63f121239ed1a9a49efcf796e6c747e34244d6ba 100644 --- a/packages/components/src/helper/color_helper.ts +++ b/packages/components/src/helper/color_helper.ts @@ -17,7 +17,7 @@ */ /** - * WCAG 2.0 divides the color contrast into three grades, Grade A, Grade AA and Grade AAA. + * WCAG 2.0 divides the color contrast into three grades, Grade A, Grade AA and Grade AAA. * The higher the grade, the higher the color contrast and the greater the visual pressure: * https://www.w3.org/TR/2008/REC-WCAG20-20081211/#visual-audio-contrast-contrast * Grade A (3:1 contrast is adopted, which is the lowest contrast acceptable to ordinary observers) @@ -25,16 +25,18 @@ * AAA (7:1 contrast is adopted, which is the lowest acceptable contrast for people with severe visual loss) * REF: https://github.com/mui-org/material-ui/blob/ec37e2bb3c904d9552fa819425ee1eef72914996/packages/material-ui/src/styles/createPalette.js#L104 */ -import { light, dark } from '../theme'; +import { dark, light } from '../theme'; import Color from 'color'; import * as Colors from '../colors'; -type IHueShade = [string, number]; +type IHex = keyof typeof allColors; + +type IHueShade = [IHex, number]; type IHexHueShadeMap = { [hex: string]: IHueShade }; -const allColors: { [hue: string]: Colors.IColor } = { +const allColors = { black: Colors.black, blackBlue: Colors.blackBlue, blue: Colors.blue, @@ -57,7 +59,7 @@ const getColorHexHueShadeMap = () => { const [hue, colorObj] = item; colorObj && Object.entries(colorObj).forEach(colorItem => { const [shade, value] = colorItem; - colorHexHueShadeMap[value.toUpperCase()] = [hue, parseInt(shade, 10)]; + colorHexHueShadeMap[value.toUpperCase()] = [(hue as IHex), parseInt(shade, 10)]; }); }); return colorHexHueShadeMap; @@ -81,13 +83,13 @@ export const getActionColor = (color: string) => { const currentLevel = shadeLevel.findIndex(shade => shade === currentShade); if (currentLevel > 7) { return { - hover: allColors[hue][shadeLevel[currentLevel - 2]], - active: allColors[hue][shadeLevel[currentLevel - 4]], + hover: allColors[hue][shadeLevel[currentLevel - 2]!], + active: allColors[hue][shadeLevel[currentLevel - 4]!], }; } return { - hover: allColors[hue][shadeLevel[currentLevel + 1]], - active: allColors[hue][shadeLevel[currentLevel + 2]], + hover: allColors[hue][shadeLevel[currentLevel + 1]!], + active: allColors[hue][shadeLevel[currentLevel + 2]!], }; } return { @@ -101,14 +103,14 @@ export const getActionColor = (color: string) => { * @param color Any Hex color in the color palette, such as red[500] * @param gap shade gap */ -export const getNextShadeColor = (color: string, gap: number) => { +export const getNextShadeColor = (color: string, gap: number): string => { const hueShade = colorHexHueShadeMap[color.toUpperCase()]; if (hueShade) { const [hue, currentShade] = hueShade; const currentLevel = shadeLevel.findIndex(shade => shade === currentShade); const nextLevel = currentLevel + gap; const nextShade = shadeLevel[nextLevel]; - if (nextShade) return allColors[hue][nextShade]; + if (nextShade) return allColors[hue][nextShade]!; return color; } // The original value of the color not in the color palette is returned. @@ -126,12 +128,9 @@ export function getContrastText( background: string, contrastThreshold: number, ) { - const contrastText = - getContrastRatio(background, dark.palette.text.primary) >= contrastThreshold - ? dark.palette.text.primary - : light.palette.text.primary; - - return contrastText; + return getContrastRatio(background, dark.palette.text.primary) >= contrastThreshold + ? dark.palette.text.primary + : light.palette.text.primary; } export function decomposeColor(color: string): { type: string, values: number[] } { @@ -154,9 +153,9 @@ export function convertHexToRGB(color: string, opacity?: number) { color = color.substr(1); const re = new RegExp(`.{1,${color.length / 3}}`, 'g'); - let colors = color.match(re); + let colors = color.match(re) as string[]; - if (colors && colors[0].length === 1) { + if (colors && colors[0]!.length === 1) { colors = colors.map(n => n + n); } @@ -177,13 +176,13 @@ export function getLuminance(color: string) { return val <= 0.03928 ? val / 12.92 : ((val + 0.055) / 1.055) ** 2.4; }); // Yellow requires special calculation - const isYellow = rgb[0] > 0.4 && rgb[1] > 0.25 && rgb[2] < 0.05; + const isYellow = rgb[0]! > 0.4 && rgb[1]! > 0.25 && rgb[2]! < 0.05; // Truncate at 3 digits - return (isYellow ? 0.5 : 1) * Number((0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2]).toFixed(3)); + return (isYellow ? 0.5 : 1) * Number((0.2126 * rgb[0]! + 0.7152 * rgb[1]! + 0.0722 * rgb[2]!).toFixed(3)); } // else if (decomposedColor.type.indexOf('hsl') !== -1) - return decomposedColor.values[2] / 100; + return decomposedColor.values[2]! / 100; } /** @@ -208,6 +207,7 @@ export const rgba2hex = (foregroundColor: string, backgroundColor = '#FFFFFF') = /** * Adjust the transparency of rgba colors * @param rgbaColor Rgba color + * @param opacity */ export const editRgbaOpacity = (rgbaColor: string, opacity: number) => { const rgb = rgbaColor.match(/(\d(\.\d+)?)+/g); diff --git a/packages/components/src/helper/colors.ts b/packages/components/src/helper/colors.ts index 9611648b338c17c823b0c9eb2f54278adaa50060..213e92a2db95b4114bcc876cf08fd9511a7859c8 100644 --- a/packages/components/src/helper/colors.ts +++ b/packages/components/src/helper/colors.ts @@ -31,7 +31,7 @@ export const getThemeName = (): ThemeName => { }; const colorHandler = { - get: function(obj: any, prop: keyof any) { + get: function(_obj: any, prop: keyof any) { const theme = getThemeName(); const color = theme.includes(ThemeName.Light) ? lightColors : darkColors; return color[prop]; diff --git a/packages/components/src/helper/use_listen_visual_height.ts b/packages/components/src/helper/use_listen_visual_height.ts index f5a6b5eb2584059952f8dda4ced00fb49ba2ff9b..7d00f355ee5605bee2f1d07a401279a8d0b6bdc0 100644 --- a/packages/components/src/helper/use_listen_visual_height.ts +++ b/packages/components/src/helper/use_listen_visual_height.ts @@ -125,8 +125,8 @@ export function useListenVisualHeight(props: IUseListenVisualHeightProps) { const { top: triggerTop, height: triggerSizeHeight, bottom: triggerBottom } = triggerSize; offset = triggerOffset; adjust = triggerAdjust; - actualTop = triggerTop + offset[1]; - actualBottom = triggerBottom + offset[1]; + actualTop = triggerTop + offset[1]!; + actualBottom = triggerBottom + offset[1]!; triggerHeight = triggerSizeHeight; } const restTopSpaceHeight = window.innerHeight - actualTop - triggerHeight; @@ -199,13 +199,13 @@ export function useListenVisualHeight(props: IUseListenVisualHeightProps) { scrollELe.removeEventListener('scroll', () => toggleScorllColor(scrollELe)); } }; - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line }, [listenNode, childNode, run, cancel]); useLayoutEffect(() => { if (isMobile) return; onListenResize(); - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line }, []); const style: React.CSSProperties = {}; diff --git a/packages/components/src/hooks/use-touch.ts b/packages/components/src/hooks/use-touch.ts index 26141cb2a824fb936d2e462a4aeacbc94ae3a8e2..df97830bea51e12ea1ab3ed66c85016b340eee26 100644 --- a/packages/components/src/hooks/use-touch.ts +++ b/packages/components/src/hooks/use-touch.ts @@ -44,7 +44,7 @@ export const useTouch = () => { // Listening start to scroll const start = (e: React.TouchEvent) => { - const { clientX, clientY } = e.touches[0]; + const { clientX, clientY } = e.touches[0]!; touchRef.current = { startX: clientX, startY: clientY, @@ -54,7 +54,7 @@ export const useTouch = () => { // Listening scroll const move = (e: React.TouchEvent) => { - const { clientX, clientY } = e.touches[0]; + const { clientX, clientY } = e.touches[0]!; let direction: Direction | undefined; const { startX, startY } = touchRef.current; const deltaX = clientX - startX; diff --git a/packages/components/src/hooks/use_select_index.ts b/packages/components/src/hooks/use_select_index.ts index a7c5bb0526e7a845ac44bda997fbeaeb3ddbbdac..1c94ab2c5ec32988a0cba2da300f57c78d51a80d 100644 --- a/packages/components/src/hooks/use_select_index.ts +++ b/packages/components/src/hooks/use_select_index.ts @@ -79,10 +79,10 @@ export const useSelectIndex = (props: IUseSelectProps) => { updateIndex(e, +1); }); - useKeyPress('RightArrow', e => { + useKeyPress('RightArrow', () => { onArrowRightPress && onArrowRightPress(index); }); - useKeyPress('LeftArrow', e => { + useKeyPress('LeftArrow', () => { onArrowLeftPress && onArrowLeftPress(index); }); diff --git a/packages/components/src/react-env.d.ts b/packages/components/src/react-env.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..575d0c61c12b3b202acb8a3f4780d5eff9f0d826 --- /dev/null +++ b/packages/components/src/react-env.d.ts @@ -0,0 +1,5 @@ +declare module 'react-dom/client' { + // typing module default export as `any` will allow you to access its members without compiler warning + const createRoot: any; + export { createRoot }; +} \ No newline at end of file diff --git a/packages/components/src/stories/args.tsx b/packages/components/src/stories/args.tsx index c99354825e4d1b8fd2e75a3a030fdece01b7a391..27b05e81fe1d2961dad4bd63cc0e616365dba448 100644 --- a/packages/components/src/stories/args.tsx +++ b/packages/components/src/stories/args.tsx @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - // https://storybook.js.org/docs/react/essentials/controls import React from 'react'; diff --git a/packages/components/src/stories/assets/logo.png b/packages/components/src/stories/assets/logo.png index ef621555b9a25ffcab3ee6c0850cd9fc849fb54a..4bb0c42de50cb9ddf828407e02b60578c9c53767 100644 Binary files a/packages/components/src/stories/assets/logo.png and b/packages/components/src/stories/assets/logo.png differ diff --git a/packages/components/src/theme/light.ts b/packages/components/src/theme/light.ts index 78df3d09e937dc7880932abc46a10c0f4b92cf6f..5c1e93ac780be4b22a4b3b7fde56cc7bd386381a 100644 --- a/packages/components/src/theme/light.ts +++ b/packages/components/src/theme/light.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - import { black, blackBlue, deepPurple, orange, red, teal, lightColors } from '../colors'; import { ITheme, ThemeName } from './theme.interface'; diff --git a/packages/components/tsconfig.json b/packages/components/tsconfig.json index 584fa2800741fb0147665ab22dcd99354858f0ab..68181ca06de1c8fb8d4fbfcb083a922948c3eb32 100644 --- a/packages/components/tsconfig.json +++ b/packages/components/tsconfig.json @@ -3,6 +3,18 @@ "compilerOptions": { "allowJs": true, "strict": true, + "noImplicitThis": true, + "noImplicitAny": true, + "alwaysStrict": true, + "strictNullChecks": true, + "noUnusedLocals": true, + "noImplicitReturns": true, + "noImplicitOverride": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noUnusedParameters": true, + "strictBindCallApply": true, + "strictFunctionTypes": true, "rootDir": "src", "baseUrl": "src", "target": "es2015", @@ -13,7 +25,6 @@ ], "module": "es2015", "jsx": "react", - "noFallthroughCasesInSwitch": true, "outDir": "dist", "paths": { "*": [ diff --git a/packages/core/package.json b/packages/core/package.json index 17b23ba852f7f3e314c29b52d4ae71f289e715dc..5254b24998c8b2224687c9c43524c098d67838a2 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@apitable/core", - "version": "0.16.0", + "version": "0.17.0", "description": "datasheet core", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -14,7 +14,7 @@ "test:cov": "TZ=UTC-8 jest --coverage", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand" }, - "author": "APITable PTE. LTD.", + "author": "APITable Ltd. ", "license": "AGPL-3.0", "engines": { "node": ">=8.x" @@ -25,14 +25,14 @@ "@types/socket.io-client": "^1.4.36", "apphook": "1.0.6", "attr-accept": "^2.1.0", - "axios": "0.19.0", + "axios": "0.21.2", "color": "^3.1.3", - "dayjs": "^1.9.6", + "dayjs": "^1.11.7", "fuse.js": "^6.4.1", - "immer": "7.0.9", + "immer": "9.0.16", "joi": "^17.4.2", - "json-schema": "^0.3.0", - "lodash": "4.17.20", + "json-schema": "^0.4.0", + "lodash": "4.17.21", "lru-cache": "^5.1.1", "ot-json0": "^1.1.0", "phone": "^3.1.10", diff --git a/packages/core/src/automation_manager/validate.ts b/packages/core/src/automation_manager/validate.ts index 032708ac389686aad8ca5f2dc8256601b8352a27..fadb3c4f391604acac09b199432935db694eeaef 100644 --- a/packages/core/src/automation_manager/validate.ts +++ b/packages/core/src/automation_manager/validate.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - import { JSONSchema7 } from 'json-schema'; import { getLiteralOperandValue, getObjectOperandProperty, getOperandValueType, isLiteralOperand, isOperandNullValue } from './utils'; import { Strings, t } from '../exports/i18n'; diff --git a/packages/core/src/command_manager/__tests__/undo_manager.test.ts b/packages/core/src/command_manager/__tests__/undo_manager.test.ts index f772d66f695f09999f16add8c00d1764f39b354e..f1636a6e0202380be430284b64a98029142b7199 100644 --- a/packages/core/src/command_manager/__tests__/undo_manager.test.ts +++ b/packages/core/src/command_manager/__tests__/undo_manager.test.ts @@ -41,12 +41,12 @@ describe('add undo stack', () => { }, ExecuteType.Execute); expect(undoManager.getStockLength('undo')).toBe(1); - }) + }); }); describe('add redo cmd', () => { - }) + }); it('should delete old elements if undo stack size exceeds limit', () => { - }) + }); }); diff --git a/packages/core/src/command_manager/command_manager.ts b/packages/core/src/command_manager/command_manager.ts index 2ba1656607f7fef79f31d00e562c7ef6d50237f7..66c7ef162bfb2c5dbf30f0be8b2e202b9fcb435d 100644 --- a/packages/core/src/command_manager/command_manager.ts +++ b/packages/core/src/command_manager/command_manager.ts @@ -176,7 +176,7 @@ export class CollaCommandManager { } if (ret.result === ExecuteResult.Fail) { - console.error(`Execute "${name}" Error`, { error: ret, options }); + console.error('Execute "%s" Error', name, { error: ret, options }); return ret; } diff --git a/packages/core/src/commands/datasheet/delete_record.ts b/packages/core/src/commands/datasheet/delete_record.ts index 5659e13a93937d1e4b7f1c1e9b479e0f13b91eb2..f60f65e844019733051b9ed11591e9a9e270cfeb 100644 --- a/packages/core/src/commands/datasheet/delete_record.ts +++ b/packages/core/src/commands/datasheet/delete_record.ts @@ -90,7 +90,7 @@ export const deleteRecord: ICollaCommandDef = { return; } linkField.forEach((field: ILinkField) => { - let oldValue: string[] | undefined + let oldValue: string[] | undefined; // two tables are associated if (field.property.brotherFieldId) { oldValue = record.data[field.id] as string[] | undefined; diff --git a/packages/core/src/commands/datasheet/insert_comment.ts b/packages/core/src/commands/datasheet/insert_comment.ts index fde7e056be50847b9f44b7fd2a05786b627fb57c..03899c78486edb8457c04655b2b2c9108e5a861e 100644 --- a/packages/core/src/commands/datasheet/insert_comment.ts +++ b/packages/core/src/commands/datasheet/insert_comment.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - import { getNewIds, IDPrefix } from 'utils'; import { ResourceType } from 'types'; import { CollaCommandName } from 'commands'; diff --git a/packages/core/src/commands/datasheet/set_org_chart_style.ts b/packages/core/src/commands/datasheet/set_org_chart_style.ts index 524e6c9b5a19e8d84f33069d104fb9bb2bb1e3bf..1a452261f662fe1356bd054351bc18f8126b2d1b 100644 --- a/packages/core/src/commands/datasheet/set_org_chart_style.ts +++ b/packages/core/src/commands/datasheet/set_org_chart_style.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - import { CollaCommandName } from 'commands'; import { ExecuteResult, ICollaCommandDef } from 'command_manager'; import { IJOTAction } from 'engine'; diff --git a/packages/core/src/config/api_tip_config.auto.json b/packages/core/src/config/api_tip_config.auto.json index 07a425ccb9c95f6a07f1be65319d67ff326c80eb..c8be9700974e946065700262e668ef7cc682c08d 100644 --- a/packages/core/src/config/api_tip_config.auto.json +++ b/packages/core/src/config/api_tip_config.auto.json @@ -954,7 +954,24 @@ "id": "api_param_viewid_empty_error", "isRecordTimes": true, "message": "api_param_viewid_empty_error", - "statusCode": 200 + "statusCode": 200, + "apiTypes": "fusion_api" + }, + "api_view_rules_empty_error": { + "code": 400, + "id": "api_view_rules_empty_error", + "isRecordTimes": true, + "message": "api_view_rules_empty_error", + "statusCode": 200, + "apiTypes": "fusion_api" + }, + "api_fieldid_empty_error": { + "code": 400, + "id": "api_fieldid_empty_error", + "isRecordTimes": true, + "message": "api_fieldid_empty_error", + "statusCode": 200, + "apiTypes": "fusion_api" }, "api_param_basic_tools_type_error": { "code": 400, @@ -1046,6 +1063,54 @@ "isRecordTimes": true, "message": "api_param_embed_permission_type_error", "statusCode": 200 + }, + "api_view_type_error": { + "code": 400, + "id": "api_view_type_error", + "isRecordTimes": true, + "message": "api_view_type_error", + "statusCode": 200, + "apiTypes": "fusion_api" + }, + "api_view_filter_conditions_empty_error": { + "code": 400, + "id": "api_view_filter_conditions_empty_error", + "isRecordTimes": true, + "message": "api_view_filter_conditions_empty_error", + "statusCode": 200, + "apiTypes": "fusion_api" + }, + "api_view_filter_operator_value_error": { + "code": 400, + "id": "api_view_filter_operator_value_error", + "isRecordTimes": true, + "message": "api_view_filter_operator_value_error", + "statusCode": 200, + "apiTypes": "fusion_api" + }, + "api_view_filter_operator_not_support": { + "code": 400, + "id": "api_view_filter_operator_not_support", + "isRecordTimes": true, + "message": "api_view_filter_operator_not_support", + "statusCode": 200, + "apiTypes": "fusion_api" + }, + "api_view_fieldid_not_exist": { + "code": 400, + "id": "api_view_fieldid_not_exist", + "isRecordTimes": true, + "message": "api_view_fieldid_not_exist", + "statusCode": 200, + "apiTypes": "fusion_api" + }, + "api_param_viewids_empty_error": { + "code": 400, + "id": "api_param_viewids_empty_error", + "isRecordTimes": true, + "message": "api_param_viewids_empty_error", + "statusCode": 200, + "apiTypes": "fusion_api" } } } diff --git a/packages/core/src/config/api_tip_config.interface.ts b/packages/core/src/config/api_tip_config.interface.ts index f4e4030776ac344f782f1f52deb3cf5f640bb0c4..40532ba9ea4aa7c8a69c2801a5b854ec05b31e36 100644 --- a/packages/core/src/config/api_tip_config.interface.ts +++ b/packages/core/src/config/api_tip_config.interface.ts @@ -156,4 +156,4 @@ export interface APIAddRowFailedWrongLengthOfValueClass { export enum APITypes { FusionAPI = "fusion_api", -} +} \ No newline at end of file diff --git a/packages/core/src/config/stringkeys.interface.ts b/packages/core/src/config/stringkeys.interface.ts index 0e371a388b188ee4b884f428297bba75821cc49a..561f974e5e5f160277f873e1e1313b5481cd7b0a 100644 --- a/packages/core/src/config/stringkeys.interface.ts +++ b/packages/core/src/config/stringkeys.interface.ts @@ -63,7 +63,6 @@ export type StringKeysMapType = { 'add_role_error_empty': 'add_role_error_empty', 'add_role_error_exists': 'add_role_error_exists', 'add_role_error_limit': 'add_role_error_limit', - 'add_role_success': 'add_role_success', 'add_role_success_message': 'add_role_success_message', 'add_role_title': 'add_role_title', 'add_row_button_tip': 'add_row_button_tip', @@ -124,6 +123,7 @@ export type StringKeysMapType = { 'api_example_request': 'api_example_request', 'api_example_response': 'api_example_response', 'api_excess': 'api_excess', + 'api_fieldid_empty_error': 'api_fieldid_empty_error', 'api_fields': 'api_fields', 'api_forbidden': 'api_forbidden', 'api_forbidden_because_of_not_in_space': 'api_forbidden_because_of_not_in_space', @@ -231,6 +231,7 @@ export type StringKeysMapType = { 'api_param_view_not_exists': 'api_param_view_not_exists', 'api_param_viewid_empty_error': 'api_param_viewid_empty_error', 'api_param_viewid_type_error': 'api_param_viewid_type_error', + 'api_param_viewids_empty_error': 'api_param_viewids_empty_error', 'api_param_widget_btn_type_error': 'api_param_widget_btn_type_error', 'api_params_automumber_can_not_operate': 'api_params_automumber_can_not_operate', 'api_params_can_not_operate': 'api_params_can_not_operate', @@ -277,6 +278,7 @@ export type StringKeysMapType = { 'api_params_records_max_count_error': 'api_params_records_max_count_error', 'api_params_updated_time_can_not_operate': 'api_params_updated_time_can_not_operate', 'api_params_updatedby_can_not_operate': 'api_params_updatedby_can_not_operate', + 'api_params_views_max_count_error': 'api_params_views_max_count_error', 'api_query_params_invalid_fields': 'api_query_params_invalid_fields', 'api_query_params_view_id_not_exists': 'api_query_params_view_id_not_exists', 'api_request_success': 'api_request_success', @@ -294,7 +296,24 @@ export type StringKeysMapType = { 'api_upload_invalid_file': 'api_upload_invalid_file', 'api_upload_invalid_file_name': 'api_upload_invalid_file_name', 'api_upload_tip': 'api_upload_tip', + 'api_usage': 'api_usage', + 'api_view_fieldid_not_exist': 'api_view_fieldid_not_exist', + 'api_view_filter_conditions_empty_error': 'api_view_filter_conditions_empty_error', + 'api_view_filter_operator_not_support': 'api_view_filter_operator_not_support', + 'api_view_filter_operator_value_error': 'api_view_filter_operator_value_error', + 'api_view_rules_empty_error': 'api_view_rules_empty_error', + 'api_view_type_error': 'api_view_type_error', 'api_your_token': 'api_your_token', + 'apitable_choose_basic': 'apitable_choose_basic', + 'apitable_choose_community': 'apitable_choose_community', + 'apitable_choose_custom': 'apitable_choose_custom', + 'apitable_choose_enterprise': 'apitable_choose_enterprise', + 'apitable_choose_plus': 'apitable_choose_plus', + 'apitable_choose_pro': 'apitable_choose_pro', + 'apitable_origin_price_by_month': 'apitable_origin_price_by_month', + 'apitable_origin_price_by_year': 'apitable_origin_price_by_year', + 'apitable_privatized_deployment_desc': 'apitable_privatized_deployment_desc', + 'apitable_public_cloud_desc': 'apitable_public_cloud_desc', 'app_closed': 'app_closed', 'app_load_failed': 'app_load_failed', 'app_modal_content_policy_suffix': 'app_modal_content_policy_suffix', @@ -394,6 +413,7 @@ export type StringKeysMapType = { 'button_come_on': 'button_come_on', 'button_sub_team': 'button_sub_team', 'button_submit': 'button_submit', + 'button_submit_anonymous': 'button_submit_anonymous', 'by_field_id': 'by_field_id', 'calendar_add_date_time_field': 'calendar_add_date_time_field', 'calendar_color_more': 'calendar_color_more', @@ -496,6 +516,7 @@ export type StringKeysMapType = { 'choose_share_mode': 'choose_share_mode', 'choose_your_own_space': 'choose_your_own_space', 'chose_new_primary_admin_button': 'chose_new_primary_admin_button', + 'claim_special_offer': 'claim_special_offer', 'clear': 'clear', 'clear_all_fields': 'clear_all_fields', 'clear_cell_by_count': 'clear_cell_by_count', @@ -559,7 +580,6 @@ export type StringKeysMapType = { 'command_insert_comment': 'command_insert_comment', 'command_move_row': 'command_move_row', 'command_paste_set_record': 'command_paste_set_record', - 'command_rollback': 'command_rollback', 'command_set_field_attr': 'command_set_field_attr', 'command_set_record': 'command_set_record', 'command_undo_add_record': 'command_undo_add_record', @@ -567,7 +587,6 @@ export type StringKeysMapType = { 'command_undo_delete_records': 'command_undo_delete_records', 'command_undo_move_row': 'command_undo_move_row', 'command_undo_paste_set_record': 'command_undo_paste_set_record', - 'command_undo_rollback': 'command_undo_rollback', 'command_undo_set_field_attr': 'command_undo_set_field_attr', 'command_undo_set_record': 'command_undo_set_record', 'comment_editor_default_tip': 'comment_editor_default_tip', @@ -674,6 +693,7 @@ export type StringKeysMapType = { 'creative_production': 'creative_production', 'creator': 'creator', 'croatia': 'croatia', + 'crypto_field': 'crypto_field', 'csv': 'csv', 'cuba': 'cuba', 'cumulative_consumption': 'cumulative_consumption', @@ -714,7 +734,6 @@ export type StringKeysMapType = { 'data_calculating': 'data_calculating', 'data_error': 'data_error', 'data_loading': 'data_loading', - 'datasheet': 'datasheet', 'datasheet_count': 'datasheet_count', 'datasheet_editor_label': 'datasheet_editor_label', 'datasheet_exist_widget': 'datasheet_exist_widget', @@ -760,7 +779,6 @@ export type StringKeysMapType = { 'delete_completey': 'delete_completey', 'delete_completey_fail': 'delete_completey_fail', 'delete_field': 'delete_field', - 'delete_field_tips_content': 'delete_field_tips_content', 'delete_file_message_content': 'delete_file_message_content', 'delete_kanban_group': 'delete_kanban_group', 'delete_kanban_tip_content': 'delete_kanban_tip_content', @@ -805,12 +823,15 @@ export type StringKeysMapType = { 'developer_configuration': 'developer_configuration', 'developer_token': 'developer_token', 'developer_token_placeholder': 'developer_token_placeholder', + 'dingtalk_isv_integration_single_record_comment_mentioned': 'dingtalk_isv_integration_single_record_comment_mentioned', + 'dingtalk_isv_production_single_record_comment_mentioned': 'dingtalk_isv_production_single_record_comment_mentioned', + 'dingtalk_isv_staging_single_record_comment_mentioned': 'dingtalk_isv_staging_single_record_comment_mentioned', + 'dingtalk_single_record_member_comment_title': 'dingtalk_single_record_member_comment_title', 'direction_above': 'direction_above', 'direction_below': 'direction_below', 'direction_left': 'direction_left', 'direction_right': 'direction_right', 'disable': 'disable', - 'disabled_expand_link_record': 'disabled_expand_link_record', 'disabled_file_shared': 'disabled_file_shared', 'disabled_file_shared_desc': 'disabled_file_shared_desc', 'disabled_link_subtitle': 'disabled_link_subtitle', @@ -829,6 +850,7 @@ export type StringKeysMapType = { 'dominican_republic': 'dominican_republic', 'donut_chart': 'donut_chart', 'down': 'down', + 'downgrade': 'downgrade', 'download': 'download', 'download_all': 'download_all', 'download_client': 'download_client', @@ -1003,7 +1025,6 @@ export type StringKeysMapType = { 'favorite_empty_tip1': 'favorite_empty_tip1', 'favorite_empty_tip2': 'favorite_empty_tip2', 'feedback': 'feedback', - 'field': 'field', 'field_circular_err': 'field_circular_err', 'field_configuration_err': 'field_configuration_err', 'field_configuration_numerical_value_format': 'field_configuration_numerical_value_format', @@ -1016,7 +1037,6 @@ export type StringKeysMapType = { 'field_desc_created_time': 'field_desc_created_time', 'field_desc_currency': 'field_desc_currency', 'field_desc_datetime': 'field_desc_datetime', - 'field_desc_denied': 'field_desc_denied', 'field_desc_email': 'field_desc_email', 'field_desc_formula': 'field_desc_formula', 'field_desc_last_modified_by': 'field_desc_last_modified_by', @@ -1058,15 +1078,14 @@ export type StringKeysMapType = { 'field_help_single_select': 'field_help_single_select', 'field_help_single_text': 'field_help_single_text', 'field_help_text': 'field_help_text', - 'field_help_tree_select': 'field_help_tree_select', 'field_help_url': 'field_help_url', 'field_member_property_multi': 'field_member_property_multi', 'field_member_property_notify': 'field_member_property_notify', 'field_name_formula': 'field_name_formula', 'field_name_setting': 'field_name_setting', + 'field_permission': 'field_permission', 'field_range': 'field_range', 'field_required': 'field_required', - 'field_title': 'field_title', 'field_title_attachment': 'field_title_attachment', 'field_title_autonumber': 'field_title_autonumber', 'field_title_checkbox': 'field_title_checkbox', @@ -1074,7 +1093,6 @@ export type StringKeysMapType = { 'field_title_created_time': 'field_title_created_time', 'field_title_currency': 'field_title_currency', 'field_title_datetime': 'field_title_datetime', - 'field_title_denied': 'field_title_denied', 'field_title_email': 'field_title_email', 'field_title_formula': 'field_title_formula', 'field_title_last_modified_by': 'field_title_last_modified_by', @@ -1121,10 +1139,7 @@ export type StringKeysMapType = { 'folder_contains': 'folder_contains', 'folder_content_empty': 'folder_content_empty', 'folder_desc_title_placeholder': 'folder_desc_title_placeholder', - 'folder_editor_label': 'folder_editor_label', 'folder_level_2_limit_tips': 'folder_level_2_limit_tips', - 'folder_manager_label': 'folder_manager_label', - 'folder_reader_label': 'folder_reader_label', 'folds_hidden_fields_by_count': 'folds_hidden_fields_by_count', 'food_and_drink': 'food_and_drink', 'foreign_filed': 'foreign_filed', @@ -1216,15 +1231,10 @@ export type StringKeysMapType = { 'function_abs_example': 'function_abs_example', 'function_abs_summary': 'function_abs_summary', 'function_and_example': 'function_and_example', - 'function_and_summary': 'function_and_summary', 'function_arraycompact_example': 'function_arraycompact_example', - 'function_arraycompact_summary': 'function_arraycompact_summary', 'function_arrayflatten_example': 'function_arrayflatten_example', - 'function_arrayflatten_summary': 'function_arrayflatten_summary', 'function_arrayjoin_example': 'function_arrayjoin_example', - 'function_arrayjoin_summary': 'function_arrayjoin_summary', 'function_arrayunique_example': 'function_arrayunique_example', - 'function_arrayunique_summary': 'function_arrayunique_summary', 'function_associate_sheet': 'function_associate_sheet', 'function_average_example': 'function_average_example', 'function_average_summary': 'function_average_summary', @@ -1241,7 +1251,6 @@ export type StringKeysMapType = { 'function_counta_summary': 'function_counta_summary', 'function_countall_example': 'function_countall_example', 'function_countall_summary': 'function_countall_summary', - 'function_countif_example': 'function_countif_example', 'function_countif_summary': 'function_countif_summary', 'function_created_time_example': 'function_created_time_example', 'function_created_time_summary': 'function_created_time_summary', @@ -1250,24 +1259,16 @@ export type StringKeysMapType = { 'function_date_time_before': 'function_date_time_before', 'function_dateadd_example': 'function_dateadd_example', 'function_dateadd_summary': 'function_dateadd_summary', - 'function_dateadd_url': 'function_dateadd_url', - 'function_datestr_example': 'function_datestr_example', - 'function_datestr_summary': 'function_datestr_summary', 'function_datetime_diff_example': 'function_datetime_diff_example', 'function_datetime_diff_summary': 'function_datetime_diff_summary', - 'function_datetime_diff_url': 'function_datetime_diff_url', 'function_datetime_format_example': 'function_datetime_format_example', 'function_datetime_format_summary': 'function_datetime_format_summary', - 'function_datetime_format_url': 'function_datetime_format_url', 'function_datetime_parse_example': 'function_datetime_parse_example', 'function_datetime_parse_summary': 'function_datetime_parse_summary', - 'function_datetime_parse_url': 'function_datetime_parse_url', 'function_day_example': 'function_day_example', 'function_day_summary': 'function_day_summary', - 'function_encode_url_component_example': 'function_encode_url_component_example', 'function_encode_url_component_summary': 'function_encode_url_component_summary', 'function_err_end_of_right_bracket': 'function_err_end_of_right_bracket', - 'function_err_invalid_field_name': 'function_err_invalid_field_name', 'function_err_no_left_bracket': 'function_err_no_left_bracket', 'function_err_no_ref_self_column': 'function_err_no_ref_self_column', 'function_err_not_definition': 'function_err_not_definition', @@ -1284,18 +1285,14 @@ export type StringKeysMapType = { 'function_even_summary': 'function_even_summary', 'function_example_example': 'function_example_example', 'function_example_summary': 'function_example_summary', - 'function_example_usage': 'function_example_usage', 'function_exp_example': 'function_exp_example', 'function_exp_summary': 'function_exp_summary', 'function_false_example': 'function_false_example', - 'function_false_summary': 'function_false_summary', 'function_find_example': 'function_find_example', - 'function_find_summary': 'function_find_summary', 'function_floor_example': 'function_floor_example', 'function_floor_summary': 'function_floor_summary', 'function_fromnow_example': 'function_fromnow_example', 'function_fromnow_summary': 'function_fromnow_summary', - 'function_fromnow_url': 'function_fromnow_url', 'function_guidance': 'function_guidance', 'function_hour_example': 'function_hour_example', 'function_hour_summary': 'function_hour_summary', @@ -1304,19 +1301,13 @@ export type StringKeysMapType = { 'function_int_example': 'function_int_example', 'function_int_summary': 'function_int_summary', 'function_is_after_example': 'function_is_after_example', - 'function_is_after_summary': 'function_is_after_summary', 'function_is_before_example': 'function_is_before_example', - 'function_is_before_summary': 'function_is_before_summary', 'function_is_error_example': 'function_is_error_example', 'function_is_error_summary': 'function_is_error_summary', 'function_is_same_example': 'function_is_same_example', - 'function_is_same_summary': 'function_is_same_summary', - 'function_is_same_url': 'function_is_same_url', 'function_iserror_example': 'function_iserror_example', 'function_iserror_summary': 'function_iserror_summary', 'function_last_modified_time_example': 'function_last_modified_time_example', - 'function_last_modified_time_summary': 'function_last_modified_time_summary', - 'function_left_example': 'function_left_example', 'function_left_summary': 'function_left_summary', 'function_len_example': 'function_len_example', 'function_len_summary': 'function_len_summary', @@ -1327,7 +1318,6 @@ export type StringKeysMapType = { 'function_max_example': 'function_max_example', 'function_max_summary': 'function_max_summary', 'function_mid_example': 'function_mid_example', - 'function_mid_summary': 'function_mid_summary', 'function_min_example': 'function_min_example', 'function_min_summary': 'function_min_summary', 'function_minute_example': 'function_minute_example', @@ -1343,7 +1333,6 @@ export type StringKeysMapType = { 'function_odd_example': 'function_odd_example', 'function_odd_summary': 'function_odd_summary', 'function_or_example': 'function_or_example', - 'function_or_summary': 'function_or_summary', 'function_power_example': 'function_power_example', 'function_power_summary': 'function_power_summary', 'function_quarter_example': 'function_quarter_example', @@ -1351,10 +1340,8 @@ export type StringKeysMapType = { 'function_record_id_example': 'function_record_id_example', 'function_record_id_summary': 'function_record_id_summary', 'function_replace_example': 'function_replace_example', - 'function_replace_summary': 'function_replace_summary', 'function_rept_example': 'function_rept_example', 'function_rept_summary': 'function_rept_summary', - 'function_right_example': 'function_right_example', 'function_right_summary': 'function_right_summary', 'function_round_example': 'function_round_example', 'function_round_summary': 'function_round_summary', @@ -1363,35 +1350,28 @@ export type StringKeysMapType = { 'function_roundup_example': 'function_roundup_example', 'function_roundup_summary': 'function_roundup_summary', 'function_search_example': 'function_search_example', - 'function_search_summary': 'function_search_summary', 'function_second_example': 'function_second_example', 'function_second_summary': 'function_second_summary', 'function_set_locale_example': 'function_set_locale_example', 'function_set_locale_summary': 'function_set_locale_summary', - 'function_set_locale_url': 'function_set_locale_url', 'function_set_timezone_example': 'function_set_timezone_example', 'function_set_timezone_summary': 'function_set_timezone_summary', 'function_sqrt_example': 'function_sqrt_example', 'function_sqrt_summary': 'function_sqrt_summary', 'function_substitute_example': 'function_substitute_example', - 'function_substitute_summary': 'function_substitute_summary', 'function_sum_example': 'function_sum_example', 'function_sum_summary': 'function_sum_summary', 'function_switch_example': 'function_switch_example', 'function_switch_summary': 'function_switch_summary', - 'function_t_example': 'function_t_example', - 'function_t_summary': 'function_t_summary', 'function_timestr_example': 'function_timestr_example', 'function_timestr_summary': 'function_timestr_summary', 'function_today_example': 'function_today_example', 'function_today_summary': 'function_today_summary', 'function_tonow_example': 'function_tonow_example', 'function_tonow_summary': 'function_tonow_summary', - 'function_tonow_url': 'function_tonow_url', 'function_trim_example': 'function_trim_example', 'function_trim_summary': 'function_trim_summary', 'function_true_example': 'function_true_example', - 'function_true_summary': 'function_true_summary', 'function_upper_example': 'function_upper_example', 'function_upper_summary': 'function_upper_summary', 'function_validate_params_count': 'function_validate_params_count', @@ -1408,7 +1388,6 @@ export type StringKeysMapType = { 'function_workday_example': 'function_workday_example', 'function_workday_summary': 'function_workday_summary', 'function_xor_example': 'function_xor_example', - 'function_xor_summary': 'function_xor_summary', 'function_year_example': 'function_year_example', 'function_year_summary': 'function_year_summary', 'functions': 'functions', @@ -1478,7 +1457,6 @@ export type StringKeysMapType = { 'gantt_month': 'gantt_month', 'gantt_no_dependency': 'gantt_no_dependency', 'gantt_not_allow_link_multuble_records_gantt_warning': 'gantt_not_allow_link_multuble_records_gantt_warning', - 'gantt_not_rights_to_link_warning': 'gantt_not_rights_to_link_warning', 'gantt_open_auto_schedule_switch': 'gantt_open_auto_schedule_switch', 'gantt_open_auto_schedule_warning': 'gantt_open_auto_schedule_warning', 'gantt_open_auto_schedule_warning_no': 'gantt_open_auto_schedule_warning_no', @@ -1509,7 +1487,6 @@ export type StringKeysMapType = { 'get_verification_code_err_button': 'get_verification_code_err_button', 'get_verification_code_err_title': 'get_verification_code_err_title', 'ghana': 'ghana', - 'ghost_node_no_access': 'ghost_node_no_access', 'gibraltar': 'gibraltar', 'give_up_edit': 'give_up_edit', 'global': 'global', @@ -1623,6 +1600,7 @@ export type StringKeysMapType = { 'insert_record_left': 'insert_record_left', 'insert_record_right': 'insert_record_right', 'install_widget': 'install_widget', + 'installation': 'installation', 'integer': 'integer', 'internet': 'internet', 'intrant_space_empty_tip': 'intrant_space_empty_tip', @@ -1728,6 +1706,8 @@ export type StringKeysMapType = { 'language_setting': 'language_setting', 'language_setting_tip': 'language_setting_tip', 'laos': 'laos', + 'lark_single_record_comment_mentioned': 'lark_single_record_comment_mentioned', + 'lark_single_record_comment_mentioned_title': 'lark_single_record_comment_mentioned_title', 'last_step': 'last_step', 'last_update_time': 'last_update_time', 'latvia': 'latvia', @@ -1889,6 +1869,7 @@ export type StringKeysMapType = { 'max_records': 'max_records', 'max_value': 'max_value', 'maximum_concurrent_volume': 'maximum_concurrent_volume', + 'maybe_later': 'maybe_later', 'mayotte': 'mayotte', 'mbile_manual_setting_tip': 'mbile_manual_setting_tip', 'media_element': 'media_element', @@ -1919,7 +1900,6 @@ export type StringKeysMapType = { 'menu_insert_record_below': 'menu_insert_record_below', 'message_bind_email_succeed': 'message_bind_email_succeed', 'message_call_sharing_function_with_browser': 'message_call_sharing_function_with_browser', - 'message_can_not_associated_because_of_no_editable': 'message_can_not_associated_because_of_no_editable', 'message_coping': 'message_coping', 'message_copy_failed': 'message_copy_failed', 'message_copy_failed_reasoned_by_wrong_type': 'message_copy_failed_reasoned_by_wrong_type', @@ -1977,7 +1957,6 @@ export type StringKeysMapType = { 'more_invite_ways': 'more_invite_ways', 'more_login_mode': 'more_login_mode', 'more_member_count': 'more_member_count', - 'more_setting': 'more_setting', 'more_template': 'more_template', 'more_tips': 'more_tips', 'more_widget': 'more_widget', @@ -2043,7 +2022,6 @@ export type StringKeysMapType = { 'no_comment_tip': 'no_comment_tip', 'no_cover': 'no_cover', 'no_data': 'no_data', - 'no_foreign_dst_readable': 'no_foreign_dst_readable', 'no_foreignDstId': 'no_foreignDstId', 'no_lookup_field': 'no_lookup_field', 'no_match_tip': 'no_match_tip', @@ -2070,6 +2048,7 @@ export type StringKeysMapType = { 'node_not_exist_content': 'node_not_exist_content', 'node_not_exist_title': 'node_not_exist_title', 'node_number_err_content': 'node_number_err_content', + 'node_permission': 'node_permission', 'nodes_per_space': 'nodes_per_space', 'nonprofit': 'nonprofit', 'nonprofits_and_volunteering': 'nonprofits_and_volunteering', @@ -2241,8 +2220,6 @@ export type StringKeysMapType = { 'percent_stacked_by_field': 'percent_stacked_by_field', 'percent_stacked_column_chart': 'percent_stacked_column_chart', 'percent_stacked_line_chart': 'percent_stacked_line_chart', - 'permission_fields_count_up_to_bound': 'permission_fields_count_up_to_bound', - 'permission_no_permission_access': 'permission_no_permission_access', 'permission_template_visitor': 'permission_template_visitor', 'permisson_model_field_owner': 'permisson_model_field_owner', 'person': 'person', @@ -2276,6 +2253,8 @@ export type StringKeysMapType = { 'placeholder_select_report_reason': 'placeholder_select_report_reason', 'platform_synchronization': 'platform_synchronization', 'player_contact_us_confirm_btn': 'player_contact_us_confirm_btn', + 'player_step_ui_config_166': 'player_step_ui_config_166', + 'player_step_ui_config_167': 'player_step_ui_config_167', 'please': 'please', 'please_check': 'please_check', 'please_choose': 'please_choose', @@ -2284,6 +2263,7 @@ export type StringKeysMapType = { 'please_note': 'please_note', 'please_read_carefully': 'please_read_carefully', 'please_select_org': 'please_select_org', + 'plus_edition': 'plus_edition', 'png': 'png', 'poc_sync_members': 'poc_sync_members', 'poland': 'poland', @@ -2316,6 +2296,7 @@ export type StringKeysMapType = { 'primary_admin_email': 'primary_admin_email', 'primary_admin_new_nickname': 'primary_admin_new_nickname', 'primary_admin_nickname': 'primary_admin_nickname', + 'pro_edition': 'pro_edition', 'process': 'process', 'processed': 'processed', 'product_design_and_ux': 'product_design_and_ux', @@ -2410,7 +2391,6 @@ export type StringKeysMapType = { 'remove_members_button': 'remove_members_button', 'remove_members_content': 'remove_members_content', 'remove_members_title': 'remove_members_title', - 'remove_role': 'remove_role', 'removed_member_tomyself': 'removed_member_tomyself', 'rename': 'rename', 'rename_role_success_message': 'rename_role_success_message', @@ -2424,9 +2404,6 @@ export type StringKeysMapType = { 'reopen': 'reopen', 'report_issues': 'report_issues', 'report_issues_github_url': 'report_issues_github_url', - 'report_reason_1': 'report_reason_1', - 'report_reason_3': 'report_reason_3', - 'report_reason_5': 'report_reason_5', 'report_success_tip': 'report_success_tip', 'republic_of_the_congo': 'republic_of_the_congo', 'request': 'request', @@ -2525,6 +2502,7 @@ export type StringKeysMapType = { 'robot_required_error': 'robot_required_error', 'robot_return': 'robot_return', 'robot_run_history_bottom_tip': 'robot_run_history_bottom_tip', + 'robot_run_history_desc': 'robot_run_history_desc', 'robot_run_history_error': 'robot_run_history_error', 'robot_run_history_fail': 'robot_run_history_fail', 'robot_run_history_fail_tooltip': 'robot_run_history_fail_tooltip', @@ -2833,6 +2811,7 @@ export type StringKeysMapType = { 'skip': 'skip', 'slovakia': 'slovakia', 'slovenia': 'slovenia', + 'social_dingtalk_single_record_comment_mention': 'social_dingtalk_single_record_comment_mention', 'social_media': 'social_media', 'social_notification_url_title': 'social_notification_url_title', 'social_open_card_btn_text': 'social_open_card_btn_text', @@ -3261,6 +3240,8 @@ export type StringKeysMapType = { 'way_to_create_dashboard': 'way_to_create_dashboard', 'web_publish': 'web_publish', 'web_publish_refresh': 'web_publish_refresh', + 'wecom_single_record_comment_mentioned': 'wecom_single_record_comment_mentioned', + 'wecom_single_record_comment_mentioned_title': 'wecom_single_record_comment_mentioned_title', 'WEEKS': 'WEEKS', 'welcome_interface': 'welcome_interface', 'welcome_module4': 'welcome_module4', @@ -3399,7 +3380,6 @@ export type StringKeysMapType = { 'work_data': 'work_data', 'workbench_setting': 'workbench_setting', 'workbench_setting_all': 'workbench_setting_all', - 'workbench_setting_cannot_export_datasheet_tips': 'workbench_setting_cannot_export_datasheet_tips', 'workbench_side_space_template': 'workbench_side_space_template', 'workbenck_shortcuts': 'workbenck_shortcuts', 'workspace_data': 'workspace_data', @@ -3500,6 +3480,14 @@ export type StringKeysMapType = { 'view_sort_help': 'view_sort_help', 'lookup_help': 'lookup_help', 'formula_learn_more_url': 'formula_learn_more_url', + 'function_dateadd_url': 'function_dateadd_url', + 'function_datetime_diff_url': 'function_datetime_diff_url', + 'function_datetime_format_url': 'function_datetime_format_url', + 'function_datetime_parse_url': 'function_datetime_parse_url', + 'function_fromnow_url': 'function_fromnow_url', + 'function_is_same_url': 'function_is_same_url', + 'function_set_locale_url': 'function_set_locale_url', + 'function_tonow_url': 'function_tonow_url', 'price_title1': 'price_title1', 'new_user_welcom_notify': 'new_user_welcom_notify', 'private_product_point': 'private_product_point', @@ -3521,12 +3509,21 @@ export type StringKeysMapType = { 'audit_add_field_role_detail': 'audit_add_field_role_detail', 'section6_desc': 'section6_desc', 'bronze_grade_desc': 'bronze_grade_desc', + 'function_t_summary': 'function_t_summary', + 'function_substitute_summary': 'function_substitute_summary', + 'function_search_summary': 'function_search_summary', + 'function_replace_summary': 'function_replace_summary', + 'function_mid_summary': 'function_mid_summary', + 'function_find_summary': 'function_find_summary', 'empty_datasheet': 'empty_datasheet', + 'datasheet': 'datasheet', 'current_datasheet': 'current_datasheet', 'views_per_datasheet': 'views_per_datasheet', 'template_center_use_to_create_datasheets': 'template_center_use_to_create_datasheets', 'node_with_link_share_view_reminder': 'node_with_link_share_view_reminder', 'folder_with_link_share_view_reminder': 'folder_with_link_share_view_reminder', + 'function_last_modified_time_summary': 'function_last_modified_time_summary', + 'function_false_summary': 'function_false_summary', 'columns_count_limit_tips': 'columns_count_limit_tips', 'user_space_member_limited_tips': 'user_space_member_limited_tips', 'placeholder_input_datasheet_name': 'placeholder_input_datasheet_name', @@ -3534,6 +3531,7 @@ export type StringKeysMapType = { 'activity_login_desc': 'activity_login_desc', 'unbind_third_party_accounts_desc': 'unbind_third_party_accounts_desc', 'error_page_feedback_text': 'error_page_feedback_text', + 'field_title': 'field_title', 'field_type': 'field_type', 'error_set_column_failed_duplicate_column_name': 'error_set_column_failed_duplicate_column_name', 'kanban_new_member_field': 'kanban_new_member_field', @@ -3557,9 +3555,11 @@ export type StringKeysMapType = { 'del_field_content': 'del_field_content', 'lookup_not_found_search_keyword': 'lookup_not_found_search_keyword', 'pick_field_or_function': 'pick_field_or_function', + 'field': 'field', 'uneditable_check_info': 'uneditable_check_info', 'all_editable_fields': 'all_editable_fields', 'specified_fields': 'specified_fields', + 'delete_field_tips_content': 'delete_field_tips_content', 'view_field': 'view_field', 'kanban_setting_create_member': 'kanban_setting_create_member', 'form_view_desc': 'form_view_desc', @@ -3590,6 +3590,22 @@ export type StringKeysMapType = { 'err_field_group_tip': 'err_field_group_tip', 'error_field_not_exist': 'error_field_not_exist', 'field_had_deleted': 'field_had_deleted', + 'function_arrayjoin_summary': 'function_arrayjoin_summary', + 'function_arrayflatten_summary': 'function_arrayflatten_summary', + 'function_arraycompact_summary': 'function_arraycompact_summary', + 'function_and_summary': 'function_and_summary', + 'function_arrayunique_summary': 'function_arrayunique_summary', + 'function_err_invalid_field_name': 'function_err_invalid_field_name', + 'function_example_usage': 'function_example_usage', + 'function_left_example': 'function_left_example', + 'function_is_same_summary': 'function_is_same_summary', + 'function_is_before_summary': 'function_is_before_summary', + 'function_is_after_summary': 'function_is_after_summary', + 'function_right_example': 'function_right_example', + 'function_or_summary': 'function_or_summary', + 'function_t_example': 'function_t_example', + 'function_xor_summary': 'function_xor_summary', + 'function_true_summary': 'function_true_summary', 'kanban_setting_create_option': 'kanban_setting_create_option', 'kanban_setting_tip': 'kanban_setting_tip', 'field_select_modal_title': 'field_select_modal_title', @@ -3625,6 +3641,7 @@ export type StringKeysMapType = { 'default_view': 'default_view', 'default_create_datasheet': 'default_create_datasheet', 'datasheet_reach_limit': 'datasheet_reach_limit', + 'function_countif_example': 'function_countif_example', 'robot_variables_select_columns': 'robot_variables_select_columns', 'api_panel_type_desc_member': 'api_panel_type_desc_member', 'api_panel_type_desc_last_modified_by': 'api_panel_type_desc_last_modified_by', @@ -3642,7 +3659,9 @@ export type StringKeysMapType = { 'og_product_description_content': 'og_product_description_content', 'og_keywords_content': 'og_keywords_content', 'auth_server_extensions_login_description_content': 'auth_server_extensions_login_description_content', - 'initialization_failed_message': 'initialization_failed_message' + 'initialization_failed_message': 'initialization_failed_message', + 'welcome_module5': 'welcome_module5', + 'player_contact_us': 'player_contact_us' }; export type StringKeysType = keyof StringKeysMapType; \ No newline at end of file diff --git a/packages/core/src/config/system_config.auto.json b/packages/core/src/config/system_config.auto.json index 6fe4c52a7640698e2a318df2a9fbc76663fc3737..01221304b4b5a4767732076bb4a4bf4a43753020 100644 --- a/packages/core/src/config/system_config.auto.json +++ b/packages/core/src/config/system_config.auto.json @@ -50,9 +50,6 @@ "api_apifox_upload_url": { "value": "https://www.apifox.cn/apidoc/project-613370/api-13867674-run?path[datasheetId]=${datasheetId}&environment[token]=${token}" }, - "api_panel_help_url": { - "value": "/developers/api/introduction/" - }, "api_times_per_day": { "value": "1000000" }, @@ -125,30 +122,21 @@ "delete_account_step2_mobile_icon": { "value": "space/2022/01/11/ba4572da263c46ccb94843046a2a8e6c" }, - "dingtalk_login_appid_dev": { - "value": "dingoaumy4mh7oesecamid" - }, - "dingtalk_login_appid_prod": { - "value": "dingoam9aailglcervwmzh" - }, "education_url": { "value": "https://edu.vika.cn" }, "email_icon": { "value": "space/2022/12/05/0d3881bd9d3c4be59845739090d06051" }, - "emoji_database_32": { + "emoji_apple_32": { "value": "space/2021/03/23/6972f73d8bfe4539b67a4d3e264771e0" }, - "emoji_database_64": { + "emoji_apple_64": { "value": "space/2021/03/23/c88653f7b7424d10bef058c345f6df6d" }, "experimental_features_unsynchronized_view_intro_img": { "value": "space/2021/11/30/854742d76eaa46bba848d80f358f9cdf" }, - "feishu_login_appid": { - "value": "cli_9f3930dd7d7ad00c" - }, "field_cascade": { "value": "true" }, @@ -200,7 +188,6 @@ "help_video_tutorials_url": { "value": "https://edu.vika.cn" }, - "icp1": {}, "integration_apifox_url": { "value": "https://www.apifox.cn/apidoc/project-613370/doc-806641" }, @@ -213,15 +200,6 @@ "integration": "ina9134969049653777" } }, - "integration_dingtalk_login_appid_dev": { - "value": "dingoaumy4mh7oesecamid" - }, - "integration_dingtalk_login_appid_prod": { - "value": "dingoam9aailglcervwmzh" - }, - "integration_dingtalk_login_appid_staging": { - "value": "dingoa5koclicxm2mdx5yx" - }, "integration_dingtalk_upgrade_url": { "value": "http://h5.dingtalk.com/open-purchase/mobileUrl.html?redirectUrl=https%3A%2F%2Fh5.dingtalk.com%2Fopen-market%2Fshare.html%3FshareGoodsCode%3DD34E5A30A9AC7FC6CA73DEEEDFCEC860C2F97D997C85C521B71035D4F4F2DADF5E69AE3825326C7F%26token%3D65482d6a78796151887e033769bebfd8%26shareUid%3DBCB170692B0B56DA0C22819901B68B80&dtaction=os" }, @@ -234,18 +212,6 @@ "integration": "cli_9f3930dd7d7ad00c, cli_a08120b120fad00e, cli_9f614b454434500e" } }, - "integration_feishu_login_appid": { - "value": "cli_9f3930dd7d7ad00c" - }, - "integration_feishu_login_appid_dev": { - "value": "cli_9f3930dd7d7ad00c" - }, - "integration_feishu_login_appid_prod": { - "value": "cli_9f614b454434500e" - }, - "integration_feishu_login_appid_staging": { - "value": "cli_a08120b120fad00e" - }, "integration_feishu_manage_open_url": { "value": "https://applink.feishu.cn/client/bot/open" }, @@ -345,27 +311,15 @@ "login_privacy_policy": { "value": "维格隐私政策" }, - "login_privacy_policy_url": { - "value": "/privacy-policy" - }, "login_private_deployment_form_url": { "value": "https://vika.cn/share/shrVrGPclBql6w9ysUHzR/fomed5397fFJfdcRvL" }, "login_service_agreement": { "value": "维格服务协议" }, - "login_service_agreement_url": { - "value": "/service-agreement" - }, "official_avatar": { "value": "space/2021/12/07/aaac193704834e9a9e4af27a1535826a" }, - "onboarding_customer_service_background_img_url": { - "value": "https://s1.vika.cn/space/2022/02/28/20566ca6516d477c913f5ca79e554c68" - }, - "onboarding_customer_service_qrcode_avatar_img_url": { - "value": "https://s1.vika.cn/space/2022/02/25/1730b0599510406fb7ce1b7c9ed78393" - }, "page_apply_logout": { "value": "space/2022/01/11/5bb30117e4934522af081a05eb4fd903" }, @@ -384,9 +338,6 @@ "share_iframe_brand_dark": { "value": "space/2022/11/28/94e87bd1fd25472e99556c9b5f72c62e?attname=logo-reverse.svg" }, - "space_enterprise_certification_form": { - "value": "https://vika.cn/share/shrQetZLnGRnHXoKfJvTC?fldUC8FmVjHEv={spaceId}" - }, "space_setting_integrations_dingtalk": { "value": "true" }, @@ -411,39 +362,18 @@ "space_setting_upgrade": { "value": "true" }, - "system_configuration_default_language": { - "value": "zh_CN" - }, - "system_configuration_default_theme": { - "value": "light" - }, - "system_configuration_error_msg_qrcode": { - "value": "{ \"website\": \"/space/2022/04/26/51352131c1d74332b8ab8a57483b0f3f?attname=66_8c5b355964c515a9d2ce55bc423e5a18.png\", \"wecom\": \"/space/2022/04/26/2c706fa40e3442d8b643a30ee22f5572?attname=66_8d7dce3b6ff28bc245a2db34bf4ab55a.png\", \"dingtalk\": \"/space/2022/05/17/9bb75b8710e040be9fb99d7b28a6ad0e?attname=125_f2112b2006e4b42cc4b0e7a320f87da7.png\", \"feishu\": \"https://u.vika.cn/z9ygm\" }" - }, "system_configuration_logo_with_name_white_font": { "value": "/space/2021/09/17/5c69f63932da4be7aa0965d3b0e543c4" }, "system_configuration_minmum_version_require": { "value": "0.5.0" }, - "system_configuration_official_avatar": { - "value": "space/2021/12/07/aaac193704834e9a9e4af27a1535826a" - }, - "system_configuration_official_logo": { - "value": "/space/2021/12/07/aaac193704834e9a9e4af27a1535826a" - }, "system_configuration_server_error_bg_img": { "value": "/space/2022/09/07/cbaf2ee93be24f6bbe361a85db0efba7?attname=theserverisundermaintenance.%402x.png" }, "system_configuration_version": { "value": "0.5.0" }, - "template_feedback_form_url": { - "value": "https://u.vika.cn/vb9h5" - }, - "template_space_id": { - "value": "spcNA5eN3Sj6Q" - }, "twitter_icon": { "value": "space/2022/12/05/09cc01ee1f894fb2923bd08b715226b6" }, @@ -492,27 +422,9 @@ "user_setting_account_bind_qq": { "value": "true" }, - "user_setting_account_bind_qq_web_appid_dev": { - "value": "101907145" - }, - "user_setting_account_bind_qq_web_appid_prod": { - "value": "101911260" - }, - "user_setting_account_bind_qq_web_appid_staging": { - "value": "101907413" - }, "user_setting_account_bind_wechat": { "value": "true" }, - "user_setting_account_bind_wechat_appid_dev": { - "value": "wx3ccd2f6264309a7c" - }, - "user_setting_account_bind_wechat_appid_prod": { - "value": "wx1c297da2ee48065b" - }, - "user_setting_account_bind_wechat_appid_staging": { - "value": "wxd964fb3aa5278241" - }, "user_setting_default_avatar": { "value": "space/2020/09/11/e6aa3037a38f45acb65324ea314aea58,space/2021/03/10/61a8aae11da2439ebb4df35b9075587d,space/2020/09/11/41e723917dc742d2974e41abab8cf60b,space/2020/09/11/4dce50e4ec4649b9a408a494aca28183,space/2020/09/11/e4d073b1fa674bc884a8c194e9248ecf,space/2020/09/11/31a1acb4734c4dd3ae9538299282b39e" }, @@ -540,9 +452,6 @@ "view_gallery_guide_video": { "value": "space/2020/09/15/4383ec2f8eb041599396df0f18d99f5a" }, - "view_gantt_config_color_help_url": { - "value": "https://bbs.vika.cn/article/134" - }, "view_gantt_guide_video": { "value": "space/2021/06/02/8bd6c5263ba444aabc138ed051b83c8c" }, @@ -567,39 +476,15 @@ "widget_cli_miumum_version": { "value": "0.0.12" }, - "widget_create_widget_help_url": { - "value": "/developers/widget/introduction" - }, "widget_custom_widget_empty_img": { "value": "/space/2021/10/08/18343b0891a74bdb9ca0b36b6c543e3b" }, "widget_default_cover_img": { "value": "/space/2021/11/09/f82a5c9cb6c74452b824e17b03f20f67" }, - "widget_default_template_url": { - "value": "/develop/widget/wpk2jJ7qZS0VG/0.0.11/61433a73e4b0ed4bd1e2e8e1.zip" - }, - "widget_develop_init_help_url": { - "value": "/developers/widget/quick-start#" - }, - "widget_develop_install_help_url": { - "value": "/developers/widget/quick-start#" - }, - "widget_develop_preview_help_url": { - "value": "/developers/widget/quick-start#" - }, - "widget_develop_start_help_url": { - "value": "/developers/widget/quick-start#" - }, - "widget_how_to_close_browser_restriction_help_url": { - "value": "/developers/widget/quick-start#" - }, "widget_panel_empty_img": { "value": "space/2022/05/23/c5096fdb5d674985a49725e23f72cdc7" }, - "widget_release_help_url": { - "value": "/developers/widget/quick-start#" - }, "workbench_folder_default_cover_list": { "value": "space/2021/12/29/7306be86fc6d4cac9d8de9b4a787b1fa,space/2021/12/29/58073bbfe0f64dc7bd2f5f44a123c172,space/2021/12/29/e36e93966aa049e1ba7fd53907c2265f,space/2021/12/29/ebd570b6ee3b429f8c2e51e1b1df6657,space/2021/12/29/7eb38331f61240dcb74b1fce3a90c6bc,space/2021/12/29/a8c5df5eba2e4c07a78bdca6b9613579" }, @@ -3328,9 +3213,10 @@ "rules": [ "device_IS_pc", "url_EXCLUDES_templateId", - "url_EXCLUDES_shareId" + "url_EXCLUDES_shareId", + "edition_IS_vika" ], - "id": "datasheet_add_new_view,[device_IS_pc, url_EXCLUDES_templateId, url_EXCLUDES_shareId],[open_guide_wizard(35)]", + "id": "datasheet_add_new_view,[device_IS_pc, url_EXCLUDES_templateId, url_EXCLUDES_shareId, edition_IS_vika],[open_guide_wizard(35)]", "event": [ "datasheet_add_new_view" ], @@ -3343,9 +3229,10 @@ "rules": [ "device_IS_pc", "url_EXCLUDES_templateId", - "url_EXCLUDES_shareId" + "url_EXCLUDES_shareId", + "edition_IS_vika" ], - "id": "datasheet_add_new_view,[device_IS_pc, url_EXCLUDES_templateId, url_EXCLUDES_shareId],[open_guide_wizard(38)]", + "id": "datasheet_add_new_view,[device_IS_pc, url_EXCLUDES_templateId, url_EXCLUDES_shareId, edition_IS_vika],[open_guide_wizard(38)]", "event": [ "datasheet_add_new_view" ], @@ -3358,9 +3245,10 @@ "rules": [ "device_IS_pc", "url_EXCLUDES_templateId", - "url_EXCLUDES_shareId" + "url_EXCLUDES_shareId", + "edition_IS_vika" ], - "id": "datasheet_add_new_view,[device_IS_pc, url_EXCLUDES_templateId, url_EXCLUDES_shareId],[open_guide_wizard(55)]", + "id": "datasheet_add_new_view,[device_IS_pc, url_EXCLUDES_templateId, url_EXCLUDES_shareId, edition_IS_vika],[open_guide_wizard(55)]", "event": [ "datasheet_add_new_view" ], @@ -3601,6 +3489,22 @@ "workbench_hidden_vikaby_btn_clicked" ] }, + { + "actions": [ + "open_guide_wizards([105, 104, 18])" + ], + "rules": [ + "device_IS_pc", + "sign_up_time_IS_AFTER_2022-09-29 22:00", + "url_EXCLUDES_templateId", + "url_EXCLUDES_shareId", + "edition_IS_apitable" + ], + "id": "workbench_shown,[device_IS_pc, sign_up_time_IS_AFTER_2022-09-29 22:00, url_EXCLUDES_templateId, url_EXCLUDES_shareId, edition_IS_apitable],[open_guide_wizards([105, 104, 18])]", + "event": [ + "workbench_shown" + ] + }, { "actions": [ "open_guide_wizards([1, 24])" @@ -3609,9 +3513,10 @@ "device_IS_pc", "sign_up_time_IS_AFTER_2022-09-29 22:00", "url_EXCLUDES_templateId", - "url_EXCLUDES_shareId" + "url_EXCLUDES_shareId", + "edition_IS_vika" ], - "id": "workbench_shown,[device_IS_pc, sign_up_time_IS_AFTER_2022-09-29 22:00, url_EXCLUDES_templateId, url_EXCLUDES_shareId],[open_guide_wizards([1, 24])]", + "id": "workbench_shown,[device_IS_pc, sign_up_time_IS_AFTER_2022-09-29 22:00, url_EXCLUDES_templateId, url_EXCLUDES_shareId, edition_IS_vika],[open_guide_wizards([1, 24])]", "event": [ "workbench_shown" ] @@ -3929,6 +3834,18 @@ "id": "device_IS_pc", "conditionArgs": "pc" }, + { + "operator": "IS", + "condition": "edition", + "id": "edition_IS_apitable", + "conditionArgs": "apitable" + }, + { + "operator": "IS", + "condition": "edition", + "id": "edition_IS_vika", + "conditionArgs": "vika" + }, { "operator": "ALL_OF_FALSE", "condition": "identity", @@ -4233,6 +4150,11 @@ "command": "open_guide_wizards", "commandArgs": "[1, 24]" }, + { + "id": "open_guide_wizards([105, 104, 18])", + "command": "open_guide_wizards", + "commandArgs": "[105, 104, 18]" + }, { "id": "open_guide_wizards([19])", "command": "open_guide_wizards", @@ -4432,7 +4354,8 @@ "integral_action": "wizard_reward", "player": { "action": [ - "recMKNK2u3tkA" + "recMKNK2u3tkA", + "recRyoECYFqBv" ] } }, @@ -4919,6 +4842,25 @@ "recBtbbzXDolQ" ] } + }, + "104": { + "repeat": true, + "completeIndex": -1, + "steps": "[[166]]", + "player": { + "action": [ + "recRyoECYFqBv" + ] + } + }, + "105": { + "completeIndex": -1, + "steps": "[[167]]", + "player": { + "action": [ + "recRyoECYFqBv" + ] + } } }, "step": { @@ -6441,6 +6383,35 @@ "set_wizard_completed({\"curWizard\": true})", "open_guide_next_step({\"clearAllPrevUi\":true})" ] + }, + "166": { + "skipId": "maybe_later", + "uiConfigId": "player_step_ui_config_166", + "uiType": "billingStrip", + "backdrop": "around_mask", + "onNext": [ + "open_guide_next_step({\"clearAllPrevUi\":true})" + ], + "next": "获取特殊优惠", + "nextId": "claim_special_offer", + "uiConfig": "{\n \"title\": \"Upgrade To Pro And Save Big!\",\n \"description\": \"In a few simple steps, you can upgrade to APITable Pro at the bird special discount price.\",\n \"listHeader\": \"Included Features:\",\n \"listContent\": [\n \"Up to 2,000,000 records per space\",\n \"140GB of attachmens per space\",\n \"All APIs are accessible\"\n ],\n \"listFooter\": \"More details\",\n \"url\": \"https://apitable.com/pricing/\"\n}", + "onClose": [ + "open_guide_next_step({\"clearAllPrevUi\":true})" + ] + }, + "167": { + "uiConfigId": "player_step_ui_config_167", + "uiType": "questionnaire", + "backdrop": "around_mask", + "onNext": [ + "open_guide_next_step({\"clearAllPrevUi\":true})" + ], + "next": "确定", + "nextId": "confirm", + "uiConfig": "{\n \"config\": [\n {\n \"key\": 1,\n \"name\": \"answer1\",\n \"title\": \"How do you want to use APITable?\",\n \"type\": \"multiButton\",\n \"answers\": [\n \"IT & Support\",\n \"Education\",\n \"Marketing\",\n \"PMO\",\n \"Product Management\",\n \"HR & Recruiting\",\n \"Operations\",\n \"Finance\",\n \"Sales & CRM\",\n \"Software Development\",\n \"HR & Legal\",\n \"Design & Creative\",\n \"Nonprofit\",\n \"Other Things\",\n \"Manufacture\",\n \"Business Owner\"\n ],\n \"lastAllowInput\": false\n },\n {\n \"key\": 2,\n \"name\": \"answer2\",\n \"title\": \"What best describes your current role?\",\n \"type\": \"radio\",\n \"answers\": [\n \"Business Owner\",\n \"Team Leader\",\n \"Team Member\",\n \"Freelancer\",\n \"Director\",\n \"C-level\",\n \"VP\"\n ],\n \"lastAllowInput\": false\n },\n {\n \"key\": 3,\n \"name\": \"answer3\",\n \"title\": \"How did you hear about us? \",\n \"type\": \"checkbox\",\n \"answers\": [\n \"Search Engine\",\n \"YouTube\",\n \"Product Hunt\",\n \"Github\",\n \"Twitter\",\n \"LinkedIn\"\n ],\n \"lastAllowInput\": false\n },\n {\n \"key\": 4,\n \"name\": \"answer4\",\n \"title\": \"We host a Discord channel as a place for discussion with APITable fans, come and join us!\",\n \"type\": \"joinUs\",\n \"url\": \"https://discord.gg/2UXAbDTJTX\",\n \"confirmText\": \"Join Our Community\",\n \"skipText\": \"skip\",\n \"submit\": true\n }\n ]\n}", + "onClose": [ + "open_guide_next_step({\"clearAllPrevUi\":true})" + ] } } }, @@ -6467,9 +6438,6 @@ "activity_integral_income_notify": { "to_tag": "users", "notifications_type": "system", - "formatString": [ - "activity_integral_income_notify" - ], "is_notification": true, "format_string": "activity_integral_income_notify", "is_component": true @@ -6477,9 +6445,6 @@ "activity_integral_income_toadmin": { "to_tag": "space_main_admin", "notifications_type": "system", - "formatString": [ - "activity_integral_income_toadmin" - ], "is_notification": true, "format_string": "activity_integral_income_toadmin", "is_component": true @@ -6488,9 +6453,6 @@ "can_jump": true, "to_tag": "users", "notifications_type": "system", - "formatString": [ - "add_record_out_of_limit_by_api_notify" - ], "is_notification": true, "is_mail": true, "mail_template_subject": "subject.add.record.limited", @@ -6503,9 +6465,6 @@ "can_jump": true, "to_tag": "users", "notifications_type": "system", - "formatString": [ - "add_record_soon_to_be_limit_by_api_notify" - ], "is_notification": true, "is_mail": true, "mail_template_subject": "subject.add.record.soon.limited", @@ -6518,9 +6477,6 @@ "can_jump": true, "to_tag": "members", "notifications_type": "member", - "formatString": [ - "space_add_sub_admin" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -6531,9 +6487,6 @@ "admin_transfer_space_widget_notify": { "to_tag": "users", "notifications_type": "system", - "formatString": [ - "admin_transfer_space_widget_notify" - ], "is_notification": true, "is_mail": true, "is_browser": true, @@ -6543,9 +6496,6 @@ "admin_unpublish_space_widget_notify": { "to_tag": "users", "notifications_type": "system", - "formatString": [ - "admin_unpublish_space_widget_notify" - ], "is_notification": true, "is_mail": true, "is_browser": true, @@ -6556,9 +6506,6 @@ "can_jump": true, "to_tag": "all_members", "notifications_type": "space", - "formatString": [ - "apply_space_beta_feature_success_notify_all" - ], "is_notification": true, "format_string": "apply_space_beta_feature_success_notify_all" }, @@ -6566,9 +6513,6 @@ "can_jump": true, "to_tag": "myself", "notifications_type": "space", - "formatString": [ - "apply_space_beta_feature_success_notify_me" - ], "is_notification": true, "format_string": "apply_space_beta_feature_success_notify_me" }, @@ -6576,9 +6520,6 @@ "can_jump": true, "to_tag": "members", "notifications_type": "member", - "formatString": [ - "space_assigned_to_group" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -6590,9 +6531,6 @@ "can_jump": true, "to_tag": "members", "notifications_type": "member", - "formatString": [ - "space_assigned_to_role" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -6604,9 +6542,6 @@ "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "capacity_limit" - ], "is_notification": true, "is_mail": true, "is_browser": true, @@ -6621,9 +6556,6 @@ "can_jump": true, "to_tag": "members", "notifications_type": "member", - "formatString": [ - "space_changed_ordinary_user" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -6634,9 +6566,6 @@ "can_jump": true, "to_tag": "members", "notifications_type": [], - "formatString": [ - "comment_mentioned" - ], "is_notification": true, "is_mobile": true, "is_mail": true, @@ -6648,9 +6577,6 @@ "common_system_notify": { "to_tag": "users", "notifications_type": "system", - "formatString": [ - "common_system_notify" - ], "is_notification": true, "is_browser": true, "format_string": "common_system_notify", @@ -6659,9 +6585,6 @@ "common_system_notify_web": { "to_tag": "users", "notifications_type": "system", - "formatString": [ - "common_system_notify_web" - ], "format_string": "common_system_notify_web", "is_component": true }, @@ -6669,9 +6592,6 @@ "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "datasheet_limit" - ], "is_notification": true, "is_mail": true, "is_browser": true, @@ -6686,9 +6606,6 @@ "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "datasheet_record_limit" - ], "is_notification": true, "is_mail": true, "is_browser": true, @@ -6702,9 +6619,6 @@ "integral_income_notify": { "to_tag": "users", "notifications_type": "system", - "formatString": [ - "integral_income_notify" - ], "is_notification": true, "format_string": "integral_income_notify", "is_component": true @@ -6713,9 +6627,6 @@ "can_jump": true, "to_tag": "space_member_admins", "notifications_type": "member", - "formatString": [ - "invite_member_toadmin" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -6727,9 +6638,6 @@ "can_jump": true, "to_tag": "myself", "notifications_type": "member", - "formatString": [ - "invite_member_tomyself" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -6740,9 +6648,6 @@ "can_jump": true, "to_tag": "members", "notifications_type": "member", - "formatString": [ - "invite_member_touser" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -6753,9 +6658,6 @@ "member_applied_to_close_account": { "to_tag": "space_member_admins", "notifications_type": "member", - "formatString": [ - "member_applied_to_close_account" - ], "is_notification": true, "is_browser": true, "format_string": "member_applied_to_close_account", @@ -6764,9 +6666,6 @@ "new_space_widget_notify": { "to_tag": "users", "notifications_type": "system", - "formatString": [ - "new_space_widget_notify" - ], "is_notification": true, "is_browser": true, "format_string": "new_space_widget_notify", @@ -6776,9 +6675,6 @@ "can_jump": true, "to_tag": "users", "notifications_type": "system", - "formatString": [ - "new_user_welcom_notify" - ], "is_notification": true, "format_string": "new_user_welcom_notify", "url": "https://u.vika.cn/vhw8y", @@ -6788,9 +6684,6 @@ "can_jump": true, "to_tag": "space_member_admins", "notifications_type": "member", - "formatString": [ - "member_quit_space" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -6802,9 +6695,6 @@ "can_jump": true, "to_tag": "members", "notifications_type": "member", - "formatString": [ - "remove_from_group" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -6816,9 +6706,6 @@ "can_jump": true, "to_tag": "members", "notifications_type": "member", - "formatString": [ - "remove_from_role" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -6830,9 +6717,6 @@ "can_jump": true, "to_tag": "space_member_admins", "notifications_type": "member", - "formatString": [ - "user_removed_by_space_toadmin" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -6842,9 +6726,6 @@ "removed_from_space_touser": { "to_tag": "members", "notifications_type": "member", - "formatString": [ - "user_removed_by_space_touser" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -6856,9 +6737,6 @@ "can_jump": true, "to_tag": "myself", "notifications_type": "member", - "formatString": [ - "removed_member_tomyself" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -6868,9 +6746,6 @@ "server_pre_publish": { "to_tag": "all_users", "notifications_type": "system", - "formatString": [ - "server_pre_publish" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -6881,9 +6756,6 @@ "can_jump": true, "to_tag": "members", "notifications_type": [], - "formatString": [ - "single_record_comment_mentioned" - ], "is_notification": true, "is_mobile": true, "is_mail": true, @@ -6896,9 +6768,6 @@ "can_jump": true, "to_tag": "members", "notifications_type": [], - "formatString": [ - "single_record_member_mention" - ], "is_notification": true, "is_mobile": true, "is_mail": true, @@ -6906,26 +6775,12 @@ "mail_template_subject": "subject.datasheet.remind", "format_string": "single_record_member_mention", "url": "/workbench", - "is_component": true, - "notifications": { - "social_templates copy": [ - "rec7BsX1C9YyT", - "rec8zOy6kD7Xr", - "rec9aOlARGasl", - "recRQl03ExRP7", - "reclCmiiTe9LI", - "recruWGnffygS", - "recy1zceaxmVN" - ] - } + "is_component": true }, "space_add_primary_admin": { "can_jump": true, "to_tag": "members", "notifications_type": "member", - "formatString": [ - "space_add_primary_admin" - ], "is_notification": true, "is_mobile": true, "is_browser": true, @@ -6936,9 +6791,6 @@ "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_admin_limit" - ], "is_notification": true, "is_mail": true, "is_browser": true, @@ -6953,9 +6805,6 @@ "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_api_limit" - ], "is_notification": true, "is_mail": true, "is_browser": true, @@ -6970,9 +6819,6 @@ "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_calendar_limit" - ], "is_notification": true, "is_mail": true, "is_browser": true, @@ -6987,9 +6833,6 @@ "can_jump": true, "to_tag": "users", "notifications_type": "space", - "formatString": [ - "space_certification_fail_notify" - ], "is_notification": true, "is_browser": true, "format_string": "space_certification_fail_notify", @@ -7000,9 +6843,6 @@ "can_jump": true, "to_tag": "users", "notifications_type": "space", - "formatString": [ - "space_certification_notify" - ], "is_notification": true, "is_browser": true, "format_string": "space_certification_notify", @@ -7012,9 +6852,6 @@ "space_deleted": { "to_tag": "all_members", "notifications_type": "space", - "formatString": [ - "space_has_been_deleted" - ], "is_notification": true, "is_browser": true, "format_string": "space_has_been_deleted", @@ -7024,9 +6861,6 @@ "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_dingtalk_notify" - ], "is_notification": true, "is_mail": true, "is_browser": true, @@ -7039,9 +6873,6 @@ "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_field_permission_limit" - ], "is_notification": true, "is_mail": true, "is_browser": true, @@ -7056,9 +6887,6 @@ "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_file_permission_limit" - ], "is_notification": true, "is_mail": true, "is_browser": true, @@ -7073,9 +6901,6 @@ "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_form_limit" - ], "is_notification": true, "is_mail": true, "is_browser": true, @@ -7090,9 +6915,6 @@ "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_gantt_limit" - ], "is_notification": true, "is_mail": true, "is_browser": true, @@ -7106,9 +6928,6 @@ "space_join_apply": { "to_tag": "space_member_admins", "notifications_type": "member", - "formatString": [ - "space_join_apply" - ], "is_notification": true, "is_mail": true, "format_string": "space_join_apply", @@ -7118,9 +6937,6 @@ "can_jump": true, "to_tag": "users", "notifications_type": "member", - "formatString": [ - "space_join_apply_approved" - ], "is_notification": true, "format_string": "space_join_apply_approved", "url": "/workbench", @@ -7129,9 +6945,6 @@ "space_join_apply_refused": { "to_tag": "users", "notifications_type": "member", - "formatString": [ - "space_join_apply_refused" - ], "is_notification": true, "format_string": "space_join_apply_refused", "is_component": true @@ -7140,9 +6953,6 @@ "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_lark_notify" - ], "is_notification": true, "is_mail": true, "is_browser": true, @@ -7155,9 +6965,6 @@ "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_members_limit" - ], "is_notification": true, "is_mail": true, "is_browser": true, @@ -7169,9 +6976,6 @@ "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_mirror_limit" - ], "is_notification": true, "is_mail": true, "is_browser": true, @@ -7186,9 +6990,6 @@ "can_jump": true, "to_tag": "all_members", "notifications_type": "space", - "formatString": [ - "notification_space_name_changed" - ], "is_notification": true, "is_browser": true, "format_string": "notification_space_name_changed", @@ -7198,9 +6999,6 @@ "space_paid_notify": { "to_tag": "users", "notifications_type": "space", - "formatString": [ - "space_paid_notify" - ], "is_notification": true, "format_string": "space_paid_notify", "is_component": true @@ -7209,9 +7007,6 @@ "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_rainbow_label_limit" - ], "is_notification": true, "is_mail": true, "is_browser": true, @@ -7226,9 +7021,6 @@ "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_record_limit" - ], "is_notification": true, "is_mail": true, "is_browser": true, @@ -7243,9 +7035,6 @@ "can_jump": true, "to_tag": "all_members", "notifications_type": "space", - "formatString": [ - "space_has_been_recover" - ], "is_notification": true, "is_browser": true, "format_string": "space_has_been_recover", @@ -7255,9 +7044,6 @@ "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_seats_limit" - ], "is_notification": true, "is_mail": true, "is_browser": true, @@ -7282,9 +7068,6 @@ "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_subscription_notify" - ], "is_notification": true, "is_browser": true, "format_string": "space_subscription_notify", @@ -7295,9 +7078,6 @@ "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_time_machine_limit" - ], "is_notification": true, "is_mail": true, "is_browser": true, @@ -7312,9 +7092,6 @@ "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_trash_limit" - ], "is_notification": true, "is_mail": true, "is_browser": true, @@ -7329,9 +7106,6 @@ "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_trial" - ], "is_notification": true, "is_mail": true, "is_browser": true, @@ -7351,9 +7125,6 @@ "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_watermark_notify" - ], "is_notification": true, "is_mail": true, "is_browser": true, @@ -7365,9 +7136,6 @@ "space_wecom_api_trial_end": { "to_tag": "all_members", "notifications_type": "space", - "formatString": [ - "space_wecom_api_trial_end" - ], "is_notification": true, "is_browser": true, "format_string": "space_wecom_api_trial_end" @@ -7376,9 +7144,6 @@ "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_wecom_notify" - ], "is_notification": true, "is_mail": true, "is_browser": true, @@ -7391,9 +7156,6 @@ "can_jump": true, "to_tag": "space_admins", "notifications_type": "space", - "formatString": [ - "space_yozooffice_notify" - ], "is_notification": true, "is_mail": true, "is_browser": true, @@ -7406,61 +7168,30 @@ "can_jump": true, "to_tag": "members", "notifications_type": [], - "formatString": [ - "subscribed_record_cell_updated" - ], "is_notification": true, "is_mobile": true, "is_mail": true, "is_browser": true, "mail_template_subject": "subject.subscribed.record.cell.updated", "format_string": "subscribed_record_cell_updated", - "url": "/workbench", - "notifications": { - "social_templates copy": [ - "rec0rLNvy35rK", - "rec4qNvlMI0HE", - "rec5qcIuCsYkz", - "recCKC6JLnadH", - "recPg3T3IameG", - "recSLnaXHbcIr", - "recYzOFsNdmni" - ] - } + "url": "/workbench" }, "subscribed_record_commented": { "can_jump": true, "to_tag": "members", "notifications_type": [], - "formatString": [ - "subscribed_record_commented" - ], "is_notification": true, "is_mobile": true, "is_mail": true, "is_browser": true, "mail_template_subject": "subject.subscribed.record.commented", "format_string": "subscribed_record_commented", - "url": "/workbench", - "notifications": { - "social_templates copy": [ - "rec53gHqCIqG2", - "recMdXxGr1JIE", - "recXcVE3L8Bc6", - "recbhTobcvgFG", - "recbyiZRFPtxg", - "recsBh6KyNAoL", - "recvTOkeCYFKO" - ] - } + "url": "/workbench" }, "task_reminder": { "can_jump": true, "to_tag": "members", "notifications_type": "space", - "formatString": [ - "task_reminder" - ], "is_notification": true, "is_mobile": true, "is_mail": true, @@ -7468,26 +7199,12 @@ "mail_template_subject": "subject.task.reminder", "format_string": "task_reminder", "url": "/workbench", - "is_component": true, - "notifications": { - "social_templates copy": [ - "recKllw3g3HZi", - "recRkZamjppbd", - "recXCgjNkgWc7", - "reca07LzGUJ39", - "recfqEcenGLjU", - "recwyPz9I1OVg", - "reczGXSYPC6y1" - ] - } + "is_component": true }, "user_field": { "can_jump": true, "to_tag": "members", "notifications_type": [], - "formatString": [ - "field_set_you_by_user" - ], "is_notification": true, "is_mobile": true, "is_mail": true, @@ -7499,9 +7216,6 @@ "web_publish": { "to_tag": "all_users", "notifications_type": "system", - "formatString": [ - "web_publish" - ], "is_mobile": true, "format_string": "web_publish", "is_component": true @@ -7627,484 +7341,5 @@ "action_name": "观看引导视频奖励" } } - }, - "billing": { - "products": { - "Bronze": { - "free": true, - "ch_name": "青铜级", - "plans": [], - "id": "Bronze", - "category": "BASE", - "en_name": "Bronze", - "i18nName": "bronze_grade", - "channel": "vika", - "related_features": [] - }, - "Silver": { - "ch_name": "白银级", - "prices": [], - "plans": [], - "id": "Silver", - "category": "BASE", - "en_name": "Silver", - "i18nName": "silver_grade", - "channel": "vika", - "related_features": [] - }, - "Gold": { - "ch_name": "黄金级", - "prices": [], - "plans": [], - "id": "Gold", - "category": "BASE", - "en_name": "Gold", - "i18nName": "golden_grade", - "channel": "vika", - "related_features": [] - }, - "Enterprise": { - "ch_name": "企业级", - "plans": [], - "id": "Enterprise", - "category": "BASE", - "en_name": "Enterprise", - "i18nName": "enterprise", - "channel": "vika", - "related_features": [] - }, - "ApiUsage": { - "ch_name": "API 用量", - "plans": [], - "id": "ApiUsage", - "category": "ADD_ON", - "en_name": "API usage", - "i18nName": "api_usage", - "channel": "vika", - "related_features": [] - }, - "Capacity": { - "ch_name": "附件容量", - "plans": [], - "id": "Capacity", - "category": "ADD_ON", - "en_name": "Attachments storage", - "i18nName": "space_capacity", - "channel": "vika", - "related_features": [] - }, - "Dingtalk_Base": { - "free": true, - "ch_name": "钉钉基础版", - "plans": [], - "id": "Dingtalk_Base", - "category": "BASE", - "en_name": "Basic Plan with DingTalk", - "i18nName": "dingtalk_base", - "channel": "dingtalk", - "related_features": [] - }, - "Dingtalk_Standard": { - "ch_name": "钉钉标准版", - "prices": [], - "plans": [], - "id": "Dingtalk_Standard", - "category": "BASE", - "en_name": "Standard Plan with DingTalk", - "i18nName": "dingtalk_standard", - "channel": "dingtalk", - "related_features": [] - }, - "Dingtalk_Profession": { - "ch_name": "钉钉专业版", - "prices": [], - "plans": [], - "id": "Dingtalk_Profession", - "category": "BASE", - "en_name": "Pro Plan with DingTalk", - "i18nName": "dingtalk_profession", - "channel": "dingtalk", - "related_features": [] - }, - "Dingtalk_Enterprise": { - "ch_name": "钉钉旗舰版", - "prices": [], - "plans": [], - "id": "Dingtalk_Enterprise", - "category": "BASE", - "en_name": "Ultimate Plan with DingTalk", - "i18nName": "dingtalk_enterprise", - "channel": "dingtalk", - "related_features": [] - }, - "Feishu_Base": { - "free": true, - "ch_name": "飞书基础版", - "plans": [], - "id": "Feishu_Base", - "category": "BASE", - "en_name": "Basic Plan with Lark", - "i18nName": "feishu_base", - "channel": "lark", - "related_features": [] - }, - "Feishu_Standard": { - "ch_name": "飞书标准版", - "prices": [], - "plans": [], - "id": "Feishu_Standard", - "category": "BASE", - "en_name": "Standard Plan with Lark", - "i18nName": "feishu_standard", - "channel": "lark", - "related_features": [] - }, - "Feishu_Profession": { - "ch_name": "飞书专业版", - "prices": [], - "plans": [], - "id": "Feishu_Profession", - "category": "BASE", - "en_name": "Pro Plan with Lark", - "i18nName": "feishu_profession", - "channel": "lark", - "related_features": [] - }, - "Feishu_Enterprise": { - "ch_name": "飞书旗舰版", - "prices": [], - "plans": [], - "id": "Feishu_Enterprise", - "category": "BASE", - "en_name": "Ultimate Plan with Lark", - "i18nName": "feishu_enterprise", - "channel": "lark", - "related_features": [] - }, - "Private_Cloud": { - "free": true, - "ch_name": "专有云旗舰版", - "plans": [], - "id": "Private_Cloud", - "category": "BASE", - "en_name": "Private Cloud", - "i18nName": "private_cloud", - "channel": "private", - "related_features": [] - }, - "Wecom_Base": { - "free": true, - "ch_name": "企业微信基础版", - "plans": [], - "id": "Wecom_Base", - "category": "BASE", - "en_name": "Basic Plan with WeCom", - "i18nName": "wecom_base", - "channel": "wecom", - "related_features": [] - }, - "Wecom_Standard": { - "ch_name": "企业微信标准版", - "prices": [], - "plans": [], - "id": "Wecom_Standard", - "category": "BASE", - "en_name": "Standard Plan with WeCom", - "i18nName": "wecom_standard", - "channel": "wecom", - "related_features": [] - }, - "Wecom_Profession": { - "ch_name": "企业微信专业版", - "prices": [], - "plans": [], - "id": "Wecom_Profession", - "category": "BASE", - "en_name": "Pro Plan with WeCom", - "i18nName": "wecom_profession", - "channel": "wecom", - "related_features": [] - }, - "Wecom_Enterprise": { - "ch_name": "企业微信旗舰版", - "prices": [], - "plans": [], - "id": "Wecom_Enterprise", - "category": "BASE", - "en_name": "Ultimate Plan with WeCom", - "i18nName": "wecom_enterprise", - "channel": "wecom", - "related_features": [] - }, - "Atlas": { - "free": true, - "ch_name": "云版本", - "plans": [], - "id": "Atlas", - "category": "BASE", - "en_name": "Atlas", - "i18nName": "atlas", - "channel": "aliyun", - "related_features": [] - }, - "Community_Edtion": { - "free": true, - "ch_name": "开源社区版", - "id": "Community_Edtion", - "category": "BASE", - "en_name": "Community", - "i18nName": "community_edition", - "channel": "community" - } - }, - "notify": { - "max_rows_per_sheet": { - "id": "max_rows_per_sheet", - "link_string_id": [ - "max_rows_per_sheet" - ], - "引用名称": "单张维格表记录数上限为 ${specification} 行,当前已创建 ${usage} 行,升级可获得更高用量。", - "模板名称": "单个维格表行数已达上限", - "link_notification_id": [ - "datasheet_record_limit" - ] - }, - "max_sheet_nums": { - "id": "max_sheet_nums", - "link_string_id": [ - "max_sheet_nums" - ], - "引用名称": "文件节点数上限为 ${specification} 个,当前已创建 ${usage} 个,升级可获得更高用量。", - "模板名称": "文件数量已达上限", - "link_notification_id": [ - "datasheet_limit" - ] - }, - "max_seats": { - "id": "max_seats", - "link_string_id": [ - "max_seats" - ], - "引用名称": "空间站的席位数上限为 ${specification} 个,当前已邀请 ${usage} 个,升级可获得更高用量。", - "模板名称": "付费席位数量上限", - "link_notification_id": [ - "space_seats_limit" - ] - }, - "max_gallery_views_in_space": { - "id": "max_gallery_views_in_space", - "link_string_id": [ - "max_gallery_views_in_space" - ], - "引用名称": "空间站的相册视图数上限为 ${specification} 个,当前已创建 ${usage} 个,升级可获得更高用量。", - "模板名称": "相册视图已达上限通知" - }, - "max_rows_in_space": { - "id": "max_rows_in_space", - "link_string_id": [ - "max_rows_in_space" - ], - "引用名称": "空间站总记录数上限为 ${specification} 行,当前已创建 ${usage} 行,升级可获得更高用量。", - "模板名称": "空间站记录总数", - "link_notification_id": [ - "space_record_limit" - ] - }, - "max_capacity_size_in_bytes": { - "id": "max_capacity_size_in_bytes", - "link_string_id": [ - "max_capacity_size_in_bytes" - ], - "引用名称": "空间站的附件容量上限为 ${specification} ,当前已上传 ${usage} ,升级可获得更高用量。", - "模板名称": "空间站容量包已达上限", - "link_notification_id": [ - "capacity_limit" - ] - }, - "max_kanban_views_in_space": { - "id": "max_kanban_views_in_space", - "link_string_id": [ - "max_kanban_views_in_space" - ], - "引用名称": "空间站的看板视图数上限为 ${specification} 个,当前已创建 ${usage} 个,升级可获得更高用量。", - "模板名称": "看板视图已达上限通知" - }, - "max_gantt_views_in_space": { - "id": "max_gantt_views_in_space", - "link_string_id": [ - "max_gantt_views_in_space" - ], - "引用名称": "空间站的甘特视图数上限为 ${specification} 个,当前已创建 ${usage} 个,升级可获得更高用量。", - "模板名称": "甘特视图已达上限通知", - "link_notification_id": [ - "space_gantt_limit" - ] - }, - "max_calendar_views_in_space": { - "id": "max_calendar_views_in_space", - "link_string_id": [ - "max_calendar_views_in_space" - ], - "引用名称": "空间站的日历视图数上限为 ${specification} 个,当前已创建 ${usage} 个,升级可获得更高用量。", - "模板名称": "日历视图已达上限通知", - "link_notification_id": [ - "space_calendar_limit" - ] - }, - "max_form_views_in_space": { - "id": "max_form_views_in_space", - "link_string_id": [ - "max_form_views_in_space" - ], - "引用名称": "空间站的神奇表单数上限为 ${specification} 个,当前已创建 ${usage} 个,升级可获得更高用量。", - "模板名称": "神奇表单已达上限通知", - "link_notification_id": [ - "space_form_limit" - ] - }, - "max_admin_nums": { - "id": "max_admin_nums", - "link_string_id": [ - "max_admin_nums" - ], - "引用名称": "空间站的子管理数上限为 ${specification} 个,当前已创建 ${usage} 个,升级可获得更高用量。", - "模板名称": "管理员已达上限通知", - "link_notification_id": [ - "space_admin_limit" - ] - }, - "max_remain_trash_days": { - "id": "max_remain_trash_days", - "link_string_id": [ - "max_remain_trash_days" - ], - "引用名称": "本空间站支持查看 ${specification} 天内的历史文件,升级后可查看更久的历史文件。", - "模板名称": "历史文件超限使用通知", - "link_notification_id": [ - "space_trash_limit" - ] - }, - "max_remain_timemachine_days": { - "id": "max_remain_timemachine_days", - "link_string_id": [ - "max_remain_timemachine_days" - ], - "引用名称": "空间站仅显示 ${specification} 天内的记录动态,升级后可查看更久的动态。", - "模板名称": "历史记录超限使用通知", - "link_notification_id": [ - "space_time_machine_limit" - ] - }, - "max_api_call": { - "id": "max_api_call", - "link_string_id": [ - "max_api_call" - ], - "引用名称": "空间站的API用量已超出上限( ${usage}/${specification} ),升级可获得更高用量。", - "模板名称": "API用量已达上限通知", - "link_notification_id": [ - "space_api_limit" - ] - }, - "rainbow_label": { - "id": "rainbow_label", - "link_string_id": [ - "rainbow_label" - ], - "引用名称": "你正在体验高级功能:彩虹标签,升级可解锁该功能。", - "模板名称": "彩虹标签彩色色系使用通知", - "link_notification_id": [ - "space_rainbow_label_limit" - ] - }, - "field_permission_nums": { - "id": "field_permission_nums", - "link_string_id": [ - "field_permission_nums" - ], - "引用名称": "空间站的列权限使用数上限为 ${specification} 个,目前已开启 ${usage} 个,升级可获得更高用量。", - "模板名称": "列权限已达上限通知", - "link_notification_id": [ - "space_field_permission_limit" - ] - }, - "integration_dingtalk": { - "id": "integration_dingtalk", - "link_string_id": [ - "integration_dingtalk" - ], - "引用名称": "你正在体验高级功能:钉钉集成,升级可解锁该功能。", - "模板名称": "钉钉集成使用通知", - "link_notification_id": [ - "space_dingtalk_notify" - ] - }, - "integration_feishu": { - "id": "integration_feishu", - "link_string_id": [ - "integration_feishu" - ], - "引用名称": "你正在体验高级功能:飞书集成,升级可解锁该功能。", - "模板名称": "飞书集成使用通知", - "link_notification_id": [ - "space_lark_notify" - ] - }, - "integration_we_com": { - "id": "integration_we_com", - "link_string_id": [ - "integration_we_com" - ], - "引用名称": "你正在体验高级功能:企业微信集成,升级可解锁该功能。", - "模板名称": "企业微信集成使用通知", - "link_notification_id": [ - "space_wecom_notify" - ] - }, - "integration_yozo_office": { - "id": "integration_yozo_office", - "link_string_id": [ - "integration_yozo_office" - ], - "引用名称": "你正在体验高级功能:office 文件预览,升级可解锁该功能。", - "模板名称": "永中 Office使用通知", - "link_notification_id": [ - "space_yozooffice_notify" - ] - }, - "watermark": { - "id": "watermark", - "link_string_id": [ - "watermark" - ], - "引用名称": "你正在体验${grade}空间站专属功能,升级后可以解锁该功能", - "模板名称": "全局水印使用通知", - "link_notification_id": [ - "space_watermark_notify" - ] - }, - "max_mirror_nums": { - "id": "max_mirror_nums", - "link_string_id": [ - "max_mirror_nums" - ], - "引用名称": "空间站的镜像数上限为 ${specification} 个,当前已创建 ${usage} 个,升级可获得更高用量。", - "模板名称": "最大镜像数", - "link_notification_id": [ - "space_mirror_limit" - ] - }, - "node_permission_nums": { - "id": "node_permission_nums", - "link_string_id": [ - "node_permission_nums" - ], - "引用名称": "空间站的文件权限使用数上限为 ${specification} 个,目前已开启 ${usage} 个,升级可获得更高用量。", - "模板名称": "文件权限已达上限通知", - "link_notification_id": [ - "space_file_permission_limit" - ] - } - } } } \ No newline at end of file diff --git a/packages/core/src/config/system_config.interface.ts b/packages/core/src/config/system_config.interface.ts index aee2f0be87612f8ced904afe682c9e677732b3c7..15ab1ddd24d9e40559b251042c014a548a433b2a 100644 --- a/packages/core/src/config/system_config.interface.ts +++ b/packages/core/src/config/system_config.interface.ts @@ -1,123 +1,123 @@ export interface SystemConfigInterface { - environment: Environment; - settings: Settings; - shortcut_keys: ShortcutKey[]; + environment: Environment; + settings: Settings; + shortcut_keys: ShortcutKey[]; country_code_and_phone_code: { [key: string]: CountryCodeAndPhoneCode }; - api_panel: { [key: string]: APIPanel }; - audit: Audit; - locales: Locale[]; - marketplace: SystemConfigInterfaceMarketplace; - test_function: TestFunction; - player: SystemConfigInterfacePlayer; - guide: SystemConfigInterfaceGuide; - notifications: SystemConfigInterfaceNotifications; - integral: Integral; + api_panel: { [key: string]: APIPanel }; + audit: Audit; + locales: Locale[]; + marketplace: SystemConfigInterfaceMarketplace; + test_function: TestFunction; + player: SystemConfigInterfacePlayer; + guide: SystemConfigInterfaceGuide; + notifications: SystemConfigInterfaceNotifications; + integral: Integral; } export interface APIPanel { defaultExampleId: string; - description: string; - descriptionId: string; - defaultExample: string; - valueType: string; + description: string; + descriptionId: string; + defaultExample: string; + valueType: string; } export interface Audit { - actual_delete_space: ActualDeleteSpace; - add_field_role: ActualDeleteSpace; - add_node_role: AddNodeRole; - add_sub_admin: AddTeamToMemberClass; - add_team_to_member: AddTeamToMemberClass; - agree_user_apply: AddTeamToMemberClass; - cancel_delete_space: ActualDeleteSpace; - change_main_admin: AddTeamToMemberClass; - copy_node: AddNodeRole; - create_node: AddNodeRole; - create_space: ActualDeleteSpace; - create_team: AddTeamToMemberClass; - create_template: ActualDeleteSpace; - delete_field_role: ActualDeleteSpace; - delete_node: AddNodeRole; - delete_node_role: AddNodeRole; - delete_rubbish_node: ActualDeleteSpace; - delete_space: ActualDeleteSpace; - delete_sub_admin: AddTeamToMemberClass; - delete_team: AddTeamToMemberClass; - delete_template: ActualDeleteSpace; - disable_field_role: ActualDeleteSpace; - disable_node_role: AddNodeRole; - disable_node_share: AddNodeRole; - enable_field_role: ActualDeleteSpace; - enable_node_role: AddNodeRole; - enable_node_share: AddNodeRole; - export_node: ActualDeleteSpace; - import_node: AddNodeRole; + actual_delete_space: ActualDeleteSpace; + add_field_role: ActualDeleteSpace; + add_node_role: AddNodeRole; + add_sub_admin: AddTeamToMemberClass; + add_team_to_member: AddTeamToMemberClass; + agree_user_apply: AddTeamToMemberClass; + cancel_delete_space: ActualDeleteSpace; + change_main_admin: AddTeamToMemberClass; + copy_node: AddNodeRole; + create_node: AddNodeRole; + create_space: ActualDeleteSpace; + create_team: AddTeamToMemberClass; + create_template: ActualDeleteSpace; + delete_field_role: ActualDeleteSpace; + delete_node: AddNodeRole; + delete_node_role: AddNodeRole; + delete_rubbish_node: ActualDeleteSpace; + delete_space: ActualDeleteSpace; + delete_sub_admin: AddTeamToMemberClass; + delete_team: AddTeamToMemberClass; + delete_template: ActualDeleteSpace; + disable_field_role: ActualDeleteSpace; + disable_node_role: AddNodeRole; + disable_node_share: AddNodeRole; + enable_field_role: ActualDeleteSpace; + enable_node_role: AddNodeRole; + enable_node_share: AddNodeRole; + export_node: ActualDeleteSpace; + import_node: AddNodeRole; invite_user_join_by_email: ActualDeleteSpace; - move_node: AddNodeRole; - quote_template: ActualDeleteSpace; - recover_rubbish_node: AddNodeRole; - remove_member_from_team: AddTeamToMemberClass; - remove_user: AddTeamToMemberClass; - rename_node: AddNodeRole; - rename_space: ActualDeleteSpace; - sort_node: ActualDeleteSpace; - store_share_node: AddNodeRole; - update_field_role: ActualDeleteSpace; + move_node: AddNodeRole; + quote_template: ActualDeleteSpace; + recover_rubbish_node: AddNodeRole; + remove_member_from_team: AddTeamToMemberClass; + remove_user: AddTeamToMemberClass; + rename_node: AddNodeRole; + rename_space: ActualDeleteSpace; + sort_node: ActualDeleteSpace; + store_share_node: AddNodeRole; + update_field_role: ActualDeleteSpace; update_field_role_setting: AddTeamToMemberClass; - update_member_property: AddTeamToMemberClass; - update_member_team: AddTeamToMemberClass; - update_node_cover: ActualDeleteSpace; - update_node_desc: ActualDeleteSpace; - update_node_icon: ActualDeleteSpace; - update_node_role: AddNodeRole; + update_member_property: AddTeamToMemberClass; + update_member_team: AddTeamToMemberClass; + update_node_cover: ActualDeleteSpace; + update_node_desc: ActualDeleteSpace; + update_node_icon: ActualDeleteSpace; + update_node_role: AddNodeRole; update_node_share_setting: AddNodeRole; - update_space_logo: ActualDeleteSpace; - update_sub_admin_role: AddTeamToMemberClass; - update_team_property: AddTeamToMemberClass; - user_leave_space: ActualDeleteSpace; - user_login: ActualDeleteSpace; - user_logout: ActualDeleteSpace; + update_space_logo: ActualDeleteSpace; + update_sub_admin_role: AddTeamToMemberClass; + update_team_property: AddTeamToMemberClass; + user_leave_space: ActualDeleteSpace; + user_login: ActualDeleteSpace; + user_logout: ActualDeleteSpace; } export interface ActualDeleteSpace { - content: string; - online?: boolean; - type: NotificationsTypeEnum; + content: string; + online?: boolean; + type: NotificationsTypeEnum; category: string; - name: string; + name: string; } export enum NotificationsTypeEnum { - Member = "member", - Space = "space", - System = "system", + Member = 'member', + Space = 'space', + System = 'system', } export interface AddNodeRole { - content: string; - online: boolean; - type: NotificationsTypeEnum; - sort: string; + content: string; + online: boolean; + type: NotificationsTypeEnum; + sort: string; show_in_audit_log: boolean; - category: AddNodeRoleCategory; - name: string; + category: AddNodeRoleCategory; + name: string; } export enum AddNodeRoleCategory { - WorkCatalogChangeEvent = "work_catalog_change_event", - WorkCatalogPermissionChangeEvent = "work_catalog_permission_change_event", - WorkCatalogShareEvent = "work_catalog_share_event", + WorkCatalogChangeEvent = 'work_catalog_change_event', + WorkCatalogPermissionChangeEvent = 'work_catalog_permission_change_event', + WorkCatalogShareEvent = 'work_catalog_share_event', } export interface AddTeamToMemberClass { - type: NotificationsTypeEnum; + type: NotificationsTypeEnum; category: AddSubAdminCategory; } export enum AddSubAdminCategory { - AdminPermissionChangeEvent = "admin_permission_change_event", - DatasheetFieldPermissionChangeEvent = "datasheet_field_permission_change_event", - OrganizationChangeEvent = "organization_change_event", + AdminPermissionChangeEvent = 'admin_permission_change_event', + DatasheetFieldPermissionChangeEvent = 'datasheet_field_permission_change_event', + OrganizationChangeEvent = 'organization_change_event', } export interface CountryCodeAndPhoneCode { @@ -126,8 +126,8 @@ export interface CountryCodeAndPhoneCode { export interface Environment { integration: Integration; - production: Integration; - staging: Integration; + production: Integration; + staging: Integration; } export interface Integration { @@ -136,91 +136,92 @@ export interface Integration { export interface SystemConfigInterfaceGuide { wizard: { [key: string]: Wizard }; - step: { [key: string]: Step }; + step: { [key: string]: Step }; } export interface Step { uiConfigId: string; - uiType: UIType; - prev?: string; - backdrop?: Backdrop; - onPlay?: string[]; - onNext?: On[]; - next?: Next; - onPrev?: On[]; - nextId?: NextID; - onSkip?: On[]; - uiConfig: string; - onClose?: string[]; - onTarget?: On[]; - byEvent?: string[]; - skipId?: string; - skip?: string; + uiType: UIType; + prev?: string; + backdrop?: Backdrop; + onPlay?: string[]; + onNext?: On[]; + next?: Next; + onPrev?: On[]; + nextId?: NextID; + onSkip?: On[]; + uiConfig: string; + onClose?: string[]; + onTarget?: On[]; + byEvent?: string[]; + skipId?: string; + skip?: string; } export enum Backdrop { - AroundMask = "around_mask", + AroundMask = 'around_mask', } export enum Next { - 下一步 = "下一步", - 好的 = "好的", - 已完成添加 = "已完成添加", - 我知道了 = "我知道了", - 查看更多 = "查看更多", - 查看详情 = "查看详情", - 知道啦 = "知道啦", - 确定 = "确定", + 下一步 = '下一步', + 好的 = '好的', + 已完成添加 = '已完成添加', + 我知道了 = '我知道了', + 查看更多 = '查看更多', + 查看详情 = '查看详情', + 知道啦 = '知道啦', + 确定 = '确定', } export enum NextID { - CheckDetail = "check_detail", - Confirm = "confirm", - IKnewIt = "i_knew_it", - Known = "known", - NextStep = "next_step", - Okay = "okay", - PlayerContactUsConfirmBtn = "player_contact_us_confirm_btn", - SeeMore = "see_more", + CheckDetail = 'check_detail', + Confirm = 'confirm', + IKnewIt = 'i_knew_it', + Known = 'known', + NextStep = 'next_step', + Okay = 'okay', + PlayerContactUsConfirmBtn = 'player_contact_us_confirm_btn', + SeeMore = 'see_more', } export enum On { - ClearGuideAllUI = "clear_guide_all_ui()", - ClearGuideUisPopover = "clear_guide_uis([\"popover\"])", - OpenGuideNextStep = "open_guide_next_step()", - OpenGuideNextStepClearAllPrevUITrue = "open_guide_next_step({\"clearAllPrevUi\":true})", - OpenVikabyDefaultExpandMenuTrueVisibleTrue = "open_vikaby({\"defaultExpandMenu\": true, \"visible\": true})", - SetWizardCompletedCurWizardTrue = "set_wizard_completed({\"curWizard\": true})", - SkipAllWizards = "skip_all_wizards()", - SkipCurrentWizard = "skip_current_wizard()", - SkipCurrentWizardCurWizardCompletedTrue = "skip_current_wizard({\"curWizardCompleted\": true})", + ClearGuideAllUI = 'clear_guide_all_ui()', + ClearGuideUisPopover = 'clear_guide_uis(["popover"])', + OpenGuideNextStep = 'open_guide_next_step()', + OpenGuideNextStepClearAllPrevUITrue = 'open_guide_next_step({"clearAllPrevUi":true})', + OpenVikabyDefaultExpandMenuTrueVisibleTrue = 'open_vikaby({"defaultExpandMenu": true, "visible": true})', + SetWizardCompletedCurWizardTrue = 'set_wizard_completed({"curWizard": true})', + SkipAllWizards = 'skip_all_wizards()', + SkipCurrentWizard = 'skip_current_wizard()', + SkipCurrentWizardCurWizardCompletedTrue = 'skip_current_wizard({"curWizardCompleted": true})', } export enum UIType { - AfterSignNPS = "afterSignNPS", - Breath = "breath", - ContactUs = "contactUs", - CustomQuestionnaire = "customQuestionnaire", - Modal = "modal", - Notice = "notice", - Popover = "popover", - PrivacyModal = "privacyModal", - Questionnaire = "questionnaire", - Slideout = "slideout", - TaskList = "taskList", + AfterSignNPS = 'afterSignNPS', + Breath = 'breath', + ContactUs = 'contactUs', + CustomQuestionnaire = 'customQuestionnaire', + Modal = 'modal', + Notice = 'notice', + Popover = 'popover', + PrivacyModal = 'privacyModal', + Questionnaire = 'questionnaire', + Slideout = 'slideout', + TaskList = 'taskList', + BillingStrip = 'billingStrip', } export interface Wizard { - completeIndex?: number; - steps?: string; - player?: WizardPlayer; - repeat?: boolean; - endTime?: number; - startTime?: number; - successMsg?: string; - freeVCount?: number; + completeIndex?: number; + steps?: string; + player?: WizardPlayer; + repeat?: boolean; + endTime?: number; + startTime?: number; + successMsg?: string; + freeVCount?: number; integral_action?: string; - manualActions?: string[]; + manualActions?: string[]; } export interface WizardPlayer { @@ -232,313 +233,313 @@ export interface Integral { } export interface IntegralRule { - be_invited_to_reward: BeInvitedToReward; - complete_bind_email: BeInvitedToReward; - first_bind_email: BeInvitedToReward; - first_bind_phone: BeInvitedToReward; - fission_reward: FissionReward; - invitation_reward: BeInvitedToReward; - official_adjustment: FissionReward; + be_invited_to_reward: BeInvitedToReward; + complete_bind_email: BeInvitedToReward; + first_bind_email: BeInvitedToReward; + first_bind_phone: BeInvitedToReward; + fission_reward: FissionReward; + invitation_reward: BeInvitedToReward; + official_adjustment: FissionReward; official_invitation_reward: BeInvitedToReward; - redemption_code: BeInvitedToReward; - wallet_activity_reward: FissionReward; - wizard_reward: BeInvitedToReward; - wizard_video_reward: BeInvitedToReward; + redemption_code: BeInvitedToReward; + wallet_activity_reward: FissionReward; + wizard_reward: BeInvitedToReward; + wizard_video_reward: BeInvitedToReward; } export interface BeInvitedToReward { - action_code: string; + action_code: string; day_max_integral_value: number; - display_name: any[]; - online?: boolean; - integral_value: number; - notify?: boolean; - action_name: string; + display_name: any[]; + online?: boolean; + integral_value: number; + notify?: boolean; + action_name: string; } export interface FissionReward { - action_code: string; + action_code: string; display_name: string[]; - online: boolean; - notify?: boolean; - action_name: string; + online: boolean; + notify?: boolean; + action_name: string; } export interface Locale { - currency_name: string; - currency_symbol: string; - id: string; + currency_name: string; + currency_symbol: string; + id: string; strings_language: string; - currency_code: string; - name: string; + currency_code: string; + name: string; } export interface SystemConfigInterfaceMarketplace { cli_9f3930dd7d7ad00c: CLI; cli_a08120b120fad00e: CLI; cli_9f614b454434500e: CLI; - ina5200279359980055: Ina; - ina9134969049653777: Ina; - ina5645957505507647: Ina; + ina5200279359980055: Ina; + ina9134969049653777: Ina; + ina5645957505507647: Ina; } export interface CLI { - logo: Image; - env: string[]; - disable: boolean; - app_info: string; - note: string; - app_name: string; - type: string; + logo: Image; + env: string[]; + disable: boolean; + app_info: string; + note: string; + app_name: string; + type: string; app_description: string; - id: string; - display_order: number; - image: Image; - app_id: string; - link_to_cms: string; - app_type: string; - btn_card: BtnCard; - modal: CLI9F3930Dd7D7Ad00CModal; + id: string; + display_order: number; + image: Image; + app_id: string; + link_to_cms: string; + app_type: string; + btn_card: BtnCard; + modal: CLI9F3930Dd7D7Ad00CModal; } export interface BtnCard { - btn_text: string; - btn_action?: string; - btn_type: string; + btn_text: string; + btn_action?: string; + btn_type: string; btn_close_action?: string; - apps_btn_text: string; + apps_btn_text: string; } export interface Image { - id: string; - name: string; - size: number; + id: string; + name: string; + size: number; mimeType: MIMEType; - token: string; - width: number; - height: number; - url: string; + token: string; + width: number; + height: number; + url: string; } export enum MIMEType { - ImagePNG = "image/png", - ImageSVGXML = "image/svg+xml", + ImagePNG = 'image/png', + ImageSVGXML = 'image/svg+xml', } export interface CLI9F3930Dd7D7Ad00CModal { - btn_text: string; - btn_action?: string; + btn_text: string; + btn_action?: string; app_description: string; - btn_type: string; - help_link: string; + btn_type: string; + help_link: string; } export interface Ina { - logo: Image; - env: string[]; - app_info: string; - note: string; - app_name: string; - type: string; + app_info: string; + note: string; + logo: Image; + app_name: string; + type: string; + env: string[]; app_description: string; - id: string; - display_order: number; - image: Image; - app_id: string; - link_to_cms: string; - app_type: string; - btn_card: BtnCard; - modal: CLI9F3930Dd7D7Ad00CModal; + id: string; + display_order: number; + image: Image; + link_to_cms: string; + app_type: string; + btn_card: BtnCard; + modal: CLI9F3930Dd7D7Ad00CModal; + app_id: string; } export interface SystemConfigInterfaceNotifications { - types: Types; + types: Types; templates: Templates; } export interface Templates { - activity_integral_income_notify: ActivityIntegralIncomeNotify; - activity_integral_income_toadmin: ActivityIntegralIncomeNotify; - add_record_out_of_limit: AddRecordOutOfLimit; - add_record_soon_to_be_limit: AddRecordOutOfLimit; - add_sub_admin: AssignedToGroupClass; - admin_transfer_space_widget_notify: ActivityIntegralIncomeNotify; - admin_unpublish_space_widget_notify: ActivityIntegralIncomeNotify; + activity_integral_income_notify: ActivityIntegralIncomeNotify; + activity_integral_income_toadmin: ActivityIntegralIncomeNotify; + add_record_out_of_limit: AddRecordOutOfLimit; + add_record_soon_to_be_limit: AddRecordOutOfLimit; + add_sub_admin: AssignedToGroupClass; + admin_transfer_space_widget_notify: ActivityIntegralIncomeNotify; + admin_unpublish_space_widget_notify: ActivityIntegralIncomeNotify; apply_space_beta_feature_success_notify_all: ActivityIntegralIncomeNotify; - apply_space_beta_feature_success_notify_me: ActivityIntegralIncomeNotify; - assigned_to_group: AssignedToGroupClass; - assigned_to_role: AssignedToGroupClass; - capacity_limit: AddRecordOutOfLimit; - changed_ordinary_user: ActivityIntegralIncomeNotify; - comment_mentioned: CommentMentioned; - common_system_notify: ActivityIntegralIncomeNotify; - common_system_notify_web: ActivityIntegralIncomeNotify; - datasheet_limit: AddRecordOutOfLimit; - datasheet_record_limit: AddRecordOutOfLimit; - integral_income_notify: ActivityIntegralIncomeNotify; - invite_member_toadmin: ActivityIntegralIncomeNotify; - invite_member_tomyself: ActivityIntegralIncomeNotify; - invite_member_touser: ActivityIntegralIncomeNotify; - member_applied_to_close_account: ActivityIntegralIncomeNotify; - new_space_widget_notify: ActivityIntegralIncomeNotify; - new_user_welcome_notify: ActivityIntegralIncomeNotify; - quit_space: ActivityIntegralIncomeNotify; - remove_from_group: ActivityIntegralIncomeNotify; - remove_from_role: ActivityIntegralIncomeNotify; - removed_from_space_toadmin: ActivityIntegralIncomeNotify; - removed_from_space_touser: ActivityIntegralIncomeNotify; - removed_member_tomyself: ActivityIntegralIncomeNotify; - server_pre_publish: ActivityIntegralIncomeNotify; - single_record_comment_mentioned: CommentMentioned; - single_record_member_mention: CommentMentioned; - space_add_primary_admin: AssignedToGroupClass; - space_admin_limit: AddRecordOutOfLimit; - space_api_limit: AddRecordOutOfLimit; - space_calendar_limit: AddRecordOutOfLimit; - space_certification_fail_notify: AssignedToGroupClass; - space_certification_notify: AssignedToGroupClass; - space_deleted: AssignedToGroupClass; - space_dingtalk_notify: AssignedToGroupClass; - space_field_permission_limit: AssignedToGroupClass; - space_file_permission_limit: AssignedToGroupClass; - space_form_limit: AssignedToGroupClass; - space_gantt_limit: AssignedToGroupClass; - space_join_apply: AssignedToGroupClass; - space_join_apply_approved: AssignedToGroupClass; - space_join_apply_refused: AssignedToGroupClass; - space_lark_notify: AssignedToGroupClass; - space_members_limit: AssignedToGroupClass; - space_mirror_limit: AssignedToGroupClass; - space_name_change: AssignedToGroupClass; - space_paid_notify: AssignedToGroupClass; - space_rainbow_label_limit: AssignedToGroupClass; - space_record_limit: AssignedToGroupClass; - space_recover: AssignedToGroupClass; - space_seats_limit: AssignedToGroupClass; - space_subscription_end_notify: AssignedToGroupClass; - space_subscription_notify: AssignedToGroupClass; - space_time_machine_limit: AssignedToGroupClass; - space_trash_limit: AssignedToGroupClass; - space_trial: AssignedToGroupClass; - space_vika_paid_notify: AssignedToGroupClass; - space_watermark_notify: AssignedToGroupClass; - space_wecom_api_trial_end: AssignedToGroupClass; - space_wecom_notify: AssignedToGroupClass; - space_yozooffice_notify: AssignedToGroupClass; - subscribed_record_cell_updated: CommentMentioned; - subscribed_record_commented: CommentMentioned; - task_reminder: AddRecordOutOfLimit; - user_field: CommentMentioned; - web_publish: ActivityIntegralIncomeNotify; + apply_space_beta_feature_success_notify_me: ActivityIntegralIncomeNotify; + assigned_to_group: AssignedToGroupClass; + assigned_to_role: AssignedToGroupClass; + capacity_limit: AddRecordOutOfLimit; + changed_ordinary_user: ActivityIntegralIncomeNotify; + comment_mentioned: CommentMentioned; + common_system_notify: ActivityIntegralIncomeNotify; + common_system_notify_web: ActivityIntegralIncomeNotify; + datasheet_limit: AddRecordOutOfLimit; + datasheet_record_limit: AddRecordOutOfLimit; + integral_income_notify: ActivityIntegralIncomeNotify; + invite_member_toadmin: ActivityIntegralIncomeNotify; + invite_member_tomyself: ActivityIntegralIncomeNotify; + invite_member_touser: ActivityIntegralIncomeNotify; + member_applied_to_close_account: ActivityIntegralIncomeNotify; + new_space_widget_notify: ActivityIntegralIncomeNotify; + new_user_welcome_notify: ActivityIntegralIncomeNotify; + quit_space: ActivityIntegralIncomeNotify; + remove_from_group: ActivityIntegralIncomeNotify; + remove_from_role: ActivityIntegralIncomeNotify; + removed_from_space_toadmin: ActivityIntegralIncomeNotify; + removed_from_space_touser: ActivityIntegralIncomeNotify; + removed_member_tomyself: ActivityIntegralIncomeNotify; + server_pre_publish: ActivityIntegralIncomeNotify; + single_record_comment_mentioned: CommentMentioned; + single_record_member_mention: CommentMentioned; + space_add_primary_admin: AssignedToGroupClass; + space_admin_limit: AddRecordOutOfLimit; + space_api_limit: AddRecordOutOfLimit; + space_calendar_limit: AddRecordOutOfLimit; + space_certification_fail_notify: AssignedToGroupClass; + space_certification_notify: AssignedToGroupClass; + space_deleted: AssignedToGroupClass; + space_dingtalk_notify: AssignedToGroupClass; + space_field_permission_limit: AssignedToGroupClass; + space_file_permission_limit: AssignedToGroupClass; + space_form_limit: AssignedToGroupClass; + space_gantt_limit: AssignedToGroupClass; + space_join_apply: AssignedToGroupClass; + space_join_apply_approved: AssignedToGroupClass; + space_join_apply_refused: AssignedToGroupClass; + space_lark_notify: AssignedToGroupClass; + space_members_limit: AssignedToGroupClass; + space_mirror_limit: AssignedToGroupClass; + space_name_change: AssignedToGroupClass; + space_paid_notify: AssignedToGroupClass; + space_rainbow_label_limit: AssignedToGroupClass; + space_record_limit: AssignedToGroupClass; + space_recover: AssignedToGroupClass; + space_seats_limit: AssignedToGroupClass; + space_subscription_end_notify: AssignedToGroupClass; + space_subscription_notify: AssignedToGroupClass; + space_time_machine_limit: AssignedToGroupClass; + space_trash_limit: AssignedToGroupClass; + space_trial: AssignedToGroupClass; + space_vika_paid_notify: AssignedToGroupClass; + space_watermark_notify: AssignedToGroupClass; + space_wecom_api_trial_end: AssignedToGroupClass; + space_wecom_notify: AssignedToGroupClass; + space_yozooffice_notify: AssignedToGroupClass; + subscribed_record_cell_updated: CommentMentioned; + subscribed_record_commented: CommentMentioned; + task_reminder: AddRecordOutOfLimit; + user_field: CommentMentioned; + web_publish: ActivityIntegralIncomeNotify; } export interface ActivityIntegralIncomeNotify { - to_tag: string; + to_tag: string; notifications_type: NotificationsTypeEnum; - formatString: string[]; - is_notification?: boolean; - format_string: string; - is_component?: boolean; - is_mail?: boolean; - is_browser?: boolean; - can_jump?: boolean; - is_mobile?: boolean; - url?: string; + formatString: string[]; + is_notification?: boolean; + format_string: string; + is_component?: boolean; + is_mail?: boolean; + is_browser?: boolean; + can_jump?: boolean; + is_mobile?: boolean; + url?: string; } export interface AddRecordOutOfLimit { - can_jump: boolean; - to_tag: ToTag; - notifications_type: NotificationsTypeEnum; - formatString: string[]; - is_notification: boolean; - is_mail: boolean; + can_jump: boolean; + to_tag: ToTag; + notifications_type: NotificationsTypeEnum; + formatString: string[]; + is_notification: boolean; + is_mail: boolean; mail_template_subject: string; - format_string: string; - url: URL; - frequency?: number; - is_component: boolean; - is_browser?: boolean; - billing_notify?: string; - is_mobile?: boolean; - notifications?: AddRecordOutOfLimitNotifications; + format_string: string; + url: URL; + frequency?: number; + is_component: boolean; + is_browser?: boolean; + billing_notify?: string; + is_mobile?: boolean; + notifications?: AddRecordOutOfLimitNotifications; } export interface AddRecordOutOfLimitNotifications { - "social_templates copy": string[]; + 'social_templates copy': string[]; } export enum ToTag { - AllMembers = "all_members", - Members = "members", - SpaceAdmins = "space_admins", - SpaceMemberAdmins = "space_member_admins", - Users = "users", + AllMembers = 'all_members', + Members = 'members', + SpaceAdmins = 'space_admins', + SpaceMemberAdmins = 'space_member_admins', + Users = 'users', } export enum URL { - Management = "/management", - Workbench = "/workbench", + Management = '/management', + Workbench = '/workbench', } export interface AssignedToGroupClass { - can_jump?: boolean; - to_tag: ToTag; - notifications_type: NotificationsTypeEnum; - formatString?: string[]; - is_notification: boolean; - is_mobile?: boolean; - is_browser?: boolean; - format_string?: string; - url?: URL; - is_component?: boolean; - is_mail?: boolean; - billing_notify?: string; + can_jump?: boolean; + to_tag: ToTag; + notifications_type: NotificationsTypeEnum; + formatString?: string[]; + is_notification: boolean; + is_mobile?: boolean; + is_browser?: boolean; + format_string?: string; + url?: URL; + is_component?: boolean; + is_mail?: boolean; + billing_notify?: string; mail_template_subject?: string; - frequency?: number; + frequency?: number; } export interface CommentMentioned { - can_jump: boolean; - to_tag: ToTag; - notifications_type: any[]; - formatString: any[]; - is_notification: boolean; - is_mobile: boolean; - is_mail: boolean; - is_browser: boolean; - format_string: string; - url: URL; - is_component?: boolean; + can_jump: boolean; + to_tag: ToTag; + notifications_type: any[]; + formatString: any[]; + is_notification: boolean; + is_mobile: boolean; + is_mail: boolean; + is_browser: boolean; + format_string: string; + url: URL; + is_component?: boolean; mail_template_subject?: string; - notifications?: AddRecordOutOfLimitNotifications; + notifications?: AddRecordOutOfLimitNotifications; } export interface Types { member: Member; record: Member; - space: Member; + space: Member; system: Member; } export interface Member { format_string: string; - tag: string; + tag: string; } export interface SystemConfigInterfacePlayer { trigger: Trigger[]; - events: Events; - rule: RuleElement[]; - jobs: Jobs; - action: Action[]; - tips: Tips; + events: Events; + rule: RuleElement[]; + jobs: Jobs; + action: Action[]; + tips: Tips; } export interface Action { - id: string; - command: string; - guide?: ActionGuide; + id: string; + command: string; + guide?: ActionGuide; commandArgs?: string; } @@ -547,93 +548,89 @@ export interface ActionGuide { } export interface Events { - _: Icp1; - address_shown: AddressShown; - app_error_logger: AddressShown; - app_modal_confirm: AddressShown; - app_set_user_id: AddressShown; - app_tracker: AddressShown; - datasheet_add_new_view: AddressShown; - datasheet_dashboard_panel_shown: AddressShown; - datasheet_delete_record: AddressShown; - datasheet_field_context_hidden: AddressShown; - datasheet_field_context_shown: AddressShown; - datasheet_field_setting_hidden: DatasheetFieldSettingHidden; - datasheet_field_setting_shown: AddressShown; - datasheet_gantt_view_shown: AddressShown; - datasheet_grid_view_shown: DatasheetFieldSettingHidden; - datasheet_org_has_link_field: AddressShown; - datasheet_org_view_add_first_node: AddressShown; - datasheet_org_view_drag_to_unhandled_list: AddressShown; - datasheet_org_view_right_panel_shown: AddressShown; - datasheet_search_panel_hidden: DatasheetFieldSettingHidden; - datasheet_search_panel_shown: AddressShown; - datasheet_shown: AddressShown; - datasheet_user_menu: AddressShown; - datasheet_widget_center_modal_shown: DatasheetFieldSettingHidden; - datasheet_wigdet_empty_panel_shown: AddressShown; - get_context_menu_file_more: AddressShown; - get_context_menu_folder_more: AddressShown; - get_context_menu_root_add: AddressShown; - get_nav_list: AddressShown; - invite_entrance_modal_shown: AddressShown; - questionnaire_shown: AddressShown; - questionnaire_shown_after_sign: AddressShown; - space_setting_main_admin_shown: AddressShown; - space_setting_member_manage_shown: AddressShown; - space_setting_overview_shown: AddressShown; - space_setting_sub_admin_shown: AddressShown; - space_setting_workbench_shown: AddressShown; - template_center_shown: AddressShown; - template_detail_shown: AddressShown; - template_use_confirm_modal_shown: AddressShown; - view_add_panel_shown: AddressShown; - view_convert_gallery: AddressShown; - view_notice_auto_save_true: AddressShown; - view_notice_view_auto_false: AddressShown; - viewset_manual_save_tip: AddressShown; - workbench_create_form_bth_clicked: AddressShown; - workbench_create_form_panel_shown: AddressShown; - workbench_create_form_previewer_shown: DatasheetFieldSettingHidden; - workbench_entry: AddressShown; + address_shown: AddressShown; + app_error_logger: AddressShown; + app_modal_confirm: AddressShown; + app_set_user_id: AddressShown; + app_tracker: AddressShown; + datasheet_add_new_view: AddressShown; + datasheet_dashboard_panel_shown: AddressShown; + datasheet_delete_record: AddressShown; + datasheet_field_context_hidden: AddressShown; + datasheet_field_context_shown: AddressShown; + datasheet_field_setting_hidden: DatasheetFieldSettingHidden; + datasheet_field_setting_shown: AddressShown; + datasheet_gantt_view_shown: AddressShown; + datasheet_grid_view_shown: DatasheetFieldSettingHidden; + datasheet_org_has_link_field: AddressShown; + datasheet_org_view_add_first_node: AddressShown; + datasheet_org_view_drag_to_unhandled_list: AddressShown; + datasheet_org_view_right_panel_shown: AddressShown; + datasheet_search_panel_hidden: DatasheetFieldSettingHidden; + datasheet_search_panel_shown: AddressShown; + datasheet_shown: AddressShown; + datasheet_user_menu: AddressShown; + datasheet_widget_center_modal_shown: DatasheetFieldSettingHidden; + datasheet_wigdet_empty_panel_shown: AddressShown; + get_context_menu_file_more: AddressShown; + get_context_menu_folder_more: AddressShown; + get_context_menu_root_add: AddressShown; + get_nav_list: AddressShown; + invite_entrance_modal_shown: AddressShown; + questionnaire_shown: AddressShown; + questionnaire_shown_after_sign: AddressShown; + space_setting_main_admin_shown: AddressShown; + space_setting_member_manage_shown: AddressShown; + space_setting_overview_shown: AddressShown; + space_setting_sub_admin_shown: AddressShown; + space_setting_workbench_shown: AddressShown; + template_center_shown: AddressShown; + template_detail_shown: AddressShown; + template_use_confirm_modal_shown: AddressShown; + view_add_panel_shown: AddressShown; + view_convert_gallery: AddressShown; + view_notice_auto_save_true: AddressShown; + view_notice_view_auto_false: AddressShown; + viewset_manual_save_tip: AddressShown; + workbench_create_form_bth_clicked: AddressShown; + workbench_create_form_panel_shown: AddressShown; + workbench_create_form_previewer_shown: DatasheetFieldSettingHidden; + workbench_entry: AddressShown; workbench_folder_from_template_showcase_shown: AddressShown; - workbench_folder_showcase_shown: AddressShown; - workbench_form_container_shown: AddressShown; - workbench_hidden_vikaby_btn_clicked: AddressShown; - workbench_no_emit: AddressShown; - workbench_shown: AddressShown; - workbench_space_list_shown: AddressShown; -} - -export interface Icp1 { + workbench_folder_showcase_shown: AddressShown; + workbench_form_container_shown: AddressShown; + workbench_hidden_vikaby_btn_clicked: AddressShown; + workbench_no_emit: AddressShown; + workbench_shown: AddressShown; + workbench_space_list_shown: AddressShown; } export interface AddressShown { module: string; - name: string; + name: string; } export interface DatasheetFieldSettingHidden { module: string; - name: string; - guide: ActionGuide; + name: string; + guide: ActionGuide; } export interface Jobs { - "15_days_recall": DaysRecall; - "3_days_recall": DaysRecall; - "7_days_recall": DaysRecall; + '15_days_recall': DaysRecall; + '3_days_recall': DaysRecall; + '7_days_recall': DaysRecall; } export interface DaysRecall { actions: any[]; - cron: string; + cron: string; } export interface RuleElement { - operator: string; - condition: string; - id: string; + operator: string; + condition: string; + id: string; conditionArgs: string; } @@ -643,216 +640,215 @@ export interface Tips { export interface FirstNodeTips { description: string; - title: string; - desc: string; + title: string; + desc: string; } export interface Trigger { - actions: string[]; - rules: string[]; - id: string; - event: string[]; + actions: string[]; + rules: string[]; + id: string; + event: string[]; eventState?: string; - suspended?: boolean; + suspended?: boolean; } export interface Settings { - _build_branch: BuildBranch; - _build_id: BuildBranch; - _version_type: BuildBranch; - activity_center_end_time: BuildBranch; - activity_center_url: BuildBranch; - activity_train_camp_end_time: BuildBranch; - activity_train_camp_start_time: BuildBranch; - agree_terms_of_service: BuildBranch; - api_apiffox_patch_url: BuildBranch; - api_apiffox_post_url: BuildBranch; - api_apifox_delete_url: BuildBranch; - api_apifox_get_url: BuildBranch; - api_apifox_upload_url: BuildBranch; - api_panel_help_url: BuildBranch; - api_times_per_day: BuildBranch; - api_times_per_hour: BuildBranch; - api_times_per_minute: BuildBranch; - api_times_per_second: BuildBranch; - apitable_login_logo: BuildBranch; - assistant: BuildBranch; - assistant_activity_train_camp_end_time: BuildBranch; - assistant_activity_train_camp_start_time: BuildBranch; - assistant_ai_course_url: BuildBranch; - assistant_release_history_url: BuildBranch; - automation_action_send_msg_to_dingtalk: BuildBranch; - automation_action_send_msg_to_feishu: BuildBranch; - automation_action_send_msg_to_wecom: BuildBranch; - billing_default_billing_period: BuildBranch; - billing_default_grade: BuildBranch; - billing_default_seats: BuildBranch; - billing_enterprise_qr_code: BuildBranch; - billing_pay_contact_us: BuildBranch; - billing_pay_success_qr_code: BuildBranch; - datasheet_max_view_count_per_sheet: BuildBranch; - datasheet_unlogin_user_avatar: BuildBranch; - delete_account_step1_cover: BuildBranch; - delete_account_step2_email_icon: BuildBranch; - delete_account_step2_mobile_icon: BuildBranch; - dingtalk_login_appid_dev: BuildBranch; - dingtalk_login_appid_prod: BuildBranch; - education_url: BuildBranch; - email_icon: BuildBranch; - emoji_database_32: BuildBranch; - emoji_database_64: BuildBranch; + _build_branch: BuildBranch; + _build_id: BuildBranch; + _version_type: BuildBranch; + activity_center_end_time: BuildBranch; + activity_center_url: BuildBranch; + activity_train_camp_end_time: BuildBranch; + activity_train_camp_start_time: BuildBranch; + agree_terms_of_service: BuildBranch; + api_apiffox_patch_url: BuildBranch; + api_apiffox_post_url: BuildBranch; + api_apifox_delete_url: BuildBranch; + api_apifox_get_url: BuildBranch; + api_apifox_upload_url: BuildBranch; + api_panel_help_url: BuildBranch; + api_times_per_day: BuildBranch; + api_times_per_hour: BuildBranch; + api_times_per_minute: BuildBranch; + api_times_per_second: BuildBranch; + apitable_login_logo: BuildBranch; + assistant: BuildBranch; + assistant_activity_train_camp_end_time: BuildBranch; + assistant_activity_train_camp_start_time: BuildBranch; + assistant_ai_course_url: BuildBranch; + assistant_release_history_url: BuildBranch; + automation_action_send_msg_to_dingtalk: BuildBranch; + automation_action_send_msg_to_feishu: BuildBranch; + automation_action_send_msg_to_wecom: BuildBranch; + billing_default_billing_period: BuildBranch; + billing_default_grade: BuildBranch; + billing_default_seats: BuildBranch; + billing_enterprise_qr_code: BuildBranch; + billing_pay_contact_us: BuildBranch; + billing_pay_success_qr_code: BuildBranch; + datasheet_max_view_count_per_sheet: BuildBranch; + datasheet_unlogin_user_avatar: BuildBranch; + delete_account_step1_cover: BuildBranch; + delete_account_step2_email_icon: BuildBranch; + delete_account_step2_mobile_icon: BuildBranch; + dingtalk_login_appid_dev: BuildBranch; + dingtalk_login_appid_prod: BuildBranch; + education_url: BuildBranch; + email_icon: BuildBranch; + emoji_apple_32: BuildBranch; + emoji_apple_64: BuildBranch; experimental_features_unsynchronized_view_intro_img: BuildBranch; - feishu_login_appid: BuildBranch; - field_cascade: BuildBranch; - github_icon: BuildBranch; - grades_info: BuildBranch; - help_assistant: BuildBranch; - help_contact_us_type: BuildBranch; - help_developers_center_url: BuildBranch; - help_download_app: BuildBranch; - help_join_chatgroup_url: BuildBranch; - help_official_website_url: BuildBranch; - help_product_roadmap_url: BuildBranch; - help_solution_url: BuildBranch; - help_subscribe_demonstrate_form_url: BuildBranch; - help_user_community_url: BuildBranch; - help_user_community_url_dev: BuildBranch; - help_user_community_url_prod: BuildBranch; - help_user_feedback_url: BuildBranch; - help_video_tutorials_url: BuildBranch; - icp1: Icp1; - integration_apifox_url: BuildBranch; - integration_dingtalk_da: BuildBranch; - integration_dingtalk_help_url: IntegrationHelpURL; - integration_dingtalk_login_appid_dev: BuildBranch; - integration_dingtalk_login_appid_prod: BuildBranch; - integration_dingtalk_login_appid_staging: BuildBranch; - integration_dingtalk_upgrade_url: BuildBranch; - integration_feishu_help: BuildBranch; - integration_feishu_help_url: IntegrationHelpURL; - integration_feishu_login_appid: BuildBranch; - integration_feishu_login_appid_dev: BuildBranch; - integration_feishu_login_appid_prod: BuildBranch; - integration_feishu_login_appid_staging: BuildBranch; - integration_feishu_manage_open_url: BuildBranch; - integration_feishu_seats_form_url: BuildBranch; - integration_feishu_upgrade_url: BuildBranch; - integration_feishu_upgrade_url_dev: BuildBranch; - integration_feisu_register_now_url: BuildBranch; - integration_wecom_bind_help_center: BuildBranch; - integration_wecom_bind_help_center_url: BuildBranch; - integration_wecom_bind_success_icon_img: BuildBranch; - integration_wecom_custom_subdomain_help_url: BuildBranch; - integration_wecom_help_url: IntegrationHelpURL; - integration_wecom_login_qrcode_js: BuildBranch; - integration_wecom_qrcode_css: BuildBranch; - integration_wecom_shop_cms: BuildBranch; - integration_wecom_shop_corpid_dev: BuildBranch; - integration_wecom_shop_corpid_prod: BuildBranch; - integration_wecom_shop_corpid_staging: BuildBranch; - integration_wecom_shop_corpid_test: BuildBranch; - integration_wecom_shop_suiteid_dev: BuildBranch; - integration_wecom_shop_suiteid_prod: BuildBranch; - integration_wecom_shop_suiteid_staging: BuildBranch; - integration_wecom_shop_suiteid_test: BuildBranch; - integration_wecom_upgrade_guide_url: BuildBranch; - integration_yozosoft_help_url: IntegrationHelpURL; - introduction_video: BuildBranch; - linkedin_icon: BuildBranch; - login_agree_terms_of_service: BuildBranch; - login_icp1_url: BuildBranch; - login_icp2_url: BuildBranch; - login_introduction_video: BuildBranch; - login_join_chatgroup_url: BuildBranch; - login_privacy_policy: BuildBranch; - login_privacy_policy_url: BuildBranch; - login_private_deployment_form_url: BuildBranch; - login_service_agreement: BuildBranch; - login_service_agreement_url: BuildBranch; - official_avatar: BuildBranch; - onboarding_customer_service_background_img_url: BuildBranch; - onboarding_customer_service_qrcode_avatar_img_url: BuildBranch; - page_apply_logout: BuildBranch; - page_apply_logout_bg: BuildBranch; - permission_config_in_workbench_page: BuildBranch; - server_error_page_bg: BuildBranch; - share_iframe_brand: BuildBranch; - share_iframe_brand_dark: BuildBranch; - space_enterprise_certification_form: BuildBranch; - space_setting_integrations_dingtalk: BuildBranch; - space_setting_integrations_feishu: BuildBranch; - space_setting_integrations_preview_office_file: BuildBranch; - space_setting_integrations_wecom: BuildBranch; - space_setting_invite_user_to_get_v_coins: BuildBranch; - space_setting_list_of_enable_all_lab_features: BuildBranch; - space_setting_role_empty_img: BuildBranch; - space_setting_upgrade: BuildBranch; - system_configuration_default_language: BuildBranch; - system_configuration_default_theme: BuildBranch; - system_configuration_error_msg_qrcode: BuildBranch; - system_configuration_logo_with_name_white_font: BuildBranch; - system_configuration_minmum_version_require: BuildBranch; - system_configuration_official_avatar: BuildBranch; - system_configuration_official_logo: BuildBranch; - system_configuration_server_error_bg_img: BuildBranch; - system_configuration_version: BuildBranch; - template_feedback_form_url: BuildBranch; - template_space_id: BuildBranch; - twitter_icon: BuildBranch; - user_account_deleted_bg_img: BuildBranch; - user_account_deleted_img: BuildBranch; - user_guide_welcome_developer_center_url: BuildBranch; - user_guide_welcome_introduction_video: BuildBranch; - user_guide_welcome_quick_start_video: BuildBranch; - user_guide_welcome_template1_icon: BuildBranch; - user_guide_welcome_template1_url: BuildBranch; - user_guide_welcome_template2_icon: BuildBranch; - user_guide_welcome_template2_url: BuildBranch; - user_guide_welcome_template3_icon: BuildBranch; - user_guide_welcome_template3_url: BuildBranch; - user_guide_welcome_what_is_datasheet_video: BuildBranch; - user_setting_account_bind: BuildBranch; - user_setting_account_bind_dingtalk: BuildBranch; - user_setting_account_bind_qq: BuildBranch; - user_setting_account_bind_qq_web_appid_dev: BuildBranch; - user_setting_account_bind_qq_web_appid_prod: BuildBranch; - user_setting_account_bind_qq_web_appid_staging: BuildBranch; - user_setting_account_bind_wechat: BuildBranch; - user_setting_account_bind_wechat_appid_dev: BuildBranch; - user_setting_account_bind_wechat_appid_prod: BuildBranch; - user_setting_account_bind_wechat_appid_staging: BuildBranch; - user_setting_default_avatar: BuildBranch; - view_architecture_empty_graphics_img: BuildBranch; - view_architecture_empty_record_list_img: BuildBranch; - view_architecture_guide_video: BuildBranch; - view_calendar_guide_create: BuildBranch; - view_calendar_guide_no_permission: BuildBranch; - view_calendar_guide_video: BuildBranch; - view_form_guide_video: BuildBranch; - view_gallery_guide_video: BuildBranch; - view_gantt_config_color_help_url: BuildBranch; - view_gantt_guide_video: BuildBranch; - view_grid_guide_video: BuildBranch; - view_kanban_guide_video: BuildBranch; - view_mirror_list_empty_img: BuildBranch; - widget_center_feature_not_unturned_on_img: BuildBranch; - widget_center_help_link: BuildBranch; - widget_center_space_widget_empty_img: BuildBranch; - widget_cli_miumum_version: BuildBranch; - widget_create_widget_help_url: BuildBranch; - widget_custom_widget_empty_img: BuildBranch; - widget_default_cover_img: BuildBranch; - widget_default_template_url: BuildBranch; - widget_develop_init_help_url: BuildBranch; - widget_develop_install_help_url: BuildBranch; - widget_develop_preview_help_url: BuildBranch; - widget_develop_start_help_url: BuildBranch; - widget_how_to_close_browser_restriction_help_url: BuildBranch; - widget_panel_empty_img: BuildBranch; - widget_release_help_url: BuildBranch; - workbench_folder_default_cover_list: BuildBranch; - workbench_max_node_number_show_invite_and_new_node: BuildBranch; - workbench_no_permission_img: BuildBranch; + feishu_login_appid: BuildBranch; + field_cascade: BuildBranch; + github_icon: BuildBranch; + grades_info: BuildBranch; + help_assistant: BuildBranch; + help_contact_us_type: BuildBranch; + help_developers_center_url: BuildBranch; + help_download_app: BuildBranch; + help_join_chatgroup_url: BuildBranch; + help_official_website_url: BuildBranch; + help_product_roadmap_url: BuildBranch; + help_solution_url: BuildBranch; + help_subscribe_demonstrate_form_url: BuildBranch; + help_user_community_url: BuildBranch; + help_user_community_url_dev: BuildBranch; + help_user_community_url_prod: BuildBranch; + help_user_feedback_url: BuildBranch; + help_video_tutorials_url: BuildBranch; + integration_apifox_url: BuildBranch; + integration_dingtalk_da: BuildBranch; + integration_dingtalk_help_url: IntegrationHelpURL; + integration_dingtalk_login_appid_dev: BuildBranch; + integration_dingtalk_login_appid_prod: BuildBranch; + integration_dingtalk_login_appid_staging: BuildBranch; + integration_dingtalk_upgrade_url: BuildBranch; + integration_feishu_help: BuildBranch; + integration_feishu_help_url: IntegrationHelpURL; + integration_feishu_login_appid: BuildBranch; + integration_feishu_login_appid_dev: BuildBranch; + integration_feishu_login_appid_prod: BuildBranch; + integration_feishu_login_appid_staging: BuildBranch; + integration_feishu_manage_open_url: BuildBranch; + integration_feishu_seats_form_url: BuildBranch; + integration_feishu_upgrade_url: BuildBranch; + integration_feishu_upgrade_url_dev: BuildBranch; + integration_feisu_register_now_url: BuildBranch; + integration_wecom_bind_help_center: BuildBranch; + integration_wecom_bind_help_center_url: BuildBranch; + integration_wecom_bind_success_icon_img: BuildBranch; + integration_wecom_custom_subdomain_help_url: BuildBranch; + integration_wecom_help_url: IntegrationHelpURL; + integration_wecom_login_qrcode_js: BuildBranch; + integration_wecom_qrcode_css: BuildBranch; + integration_wecom_shop_cms: BuildBranch; + integration_wecom_shop_corpid_dev: BuildBranch; + integration_wecom_shop_corpid_prod: BuildBranch; + integration_wecom_shop_corpid_staging: BuildBranch; + integration_wecom_shop_corpid_test: BuildBranch; + integration_wecom_shop_suiteid_dev: BuildBranch; + integration_wecom_shop_suiteid_prod: BuildBranch; + integration_wecom_shop_suiteid_staging: BuildBranch; + integration_wecom_shop_suiteid_test: BuildBranch; + integration_wecom_upgrade_guide_url: BuildBranch; + integration_yozosoft_help_url: IntegrationHelpURL; + introduction_video: BuildBranch; + linkedin_icon: BuildBranch; + login_agree_terms_of_service: BuildBranch; + login_icp1_url: BuildBranch; + login_icp2_url: BuildBranch; + login_introduction_video: BuildBranch; + login_join_chatgroup_url: BuildBranch; + login_privacy_policy: BuildBranch; + login_privacy_policy_url: BuildBranch; + login_private_deployment_form_url: BuildBranch; + login_service_agreement: BuildBranch; + login_service_agreement_url: BuildBranch; + official_avatar: BuildBranch; + onboarding_customer_service_background_img_url: BuildBranch; + onboarding_customer_service_qrcode_avatar_img_url: BuildBranch; + page_apply_logout: BuildBranch; + page_apply_logout_bg: BuildBranch; + permission_config_in_workbench_page: BuildBranch; + server_error_page_bg: BuildBranch; + share_iframe_brand: BuildBranch; + share_iframe_brand_dark: BuildBranch; + space_enterprise_certification_form: BuildBranch; + space_setting_integrations_dingtalk: BuildBranch; + space_setting_integrations_feishu: BuildBranch; + space_setting_integrations_preview_office_file: BuildBranch; + space_setting_integrations_wecom: BuildBranch; + space_setting_invite_user_to_get_v_coins: BuildBranch; + space_setting_list_of_enable_all_lab_features: BuildBranch; + space_setting_role_empty_img: BuildBranch; + space_setting_upgrade: BuildBranch; + system_configuration_default_language: BuildBranch; + system_configuration_default_theme: BuildBranch; + system_configuration_error_msg_qrcode: BuildBranch; + system_configuration_logo_with_name_white_font: BuildBranch; + system_configuration_minmum_version_require: BuildBranch; + system_configuration_official_avatar: BuildBranch; + system_configuration_official_logo: BuildBranch; + system_configuration_server_error_bg_img: BuildBranch; + system_configuration_version: BuildBranch; + template_feedback_form_url: BuildBranch; + template_space_id: BuildBranch; + twitter_icon: BuildBranch; + user_account_deleted_bg_img: BuildBranch; + user_account_deleted_img: BuildBranch; + user_guide_welcome_developer_center_url: BuildBranch; + user_guide_welcome_introduction_video: BuildBranch; + user_guide_welcome_quick_start_video: BuildBranch; + user_guide_welcome_template1_icon: BuildBranch; + user_guide_welcome_template1_url: BuildBranch; + user_guide_welcome_template2_icon: BuildBranch; + user_guide_welcome_template2_url: BuildBranch; + user_guide_welcome_template3_icon: BuildBranch; + user_guide_welcome_template3_url: BuildBranch; + user_guide_welcome_what_is_datasheet_video: BuildBranch; + user_setting_account_bind: BuildBranch; + user_setting_account_bind_dingtalk: BuildBranch; + user_setting_account_bind_qq: BuildBranch; + user_setting_account_bind_qq_web_appid_dev: BuildBranch; + user_setting_account_bind_qq_web_appid_prod: BuildBranch; + user_setting_account_bind_qq_web_appid_staging: BuildBranch; + user_setting_account_bind_wechat: BuildBranch; + user_setting_account_bind_wechat_appid_dev: BuildBranch; + user_setting_account_bind_wechat_appid_prod: BuildBranch; + user_setting_account_bind_wechat_appid_staging: BuildBranch; + user_setting_default_avatar: BuildBranch; + view_architecture_empty_graphics_img: BuildBranch; + view_architecture_empty_record_list_img: BuildBranch; + view_architecture_guide_video: BuildBranch; + view_calendar_guide_create: BuildBranch; + view_calendar_guide_no_permission: BuildBranch; + view_calendar_guide_video: BuildBranch; + view_form_guide_video: BuildBranch; + view_gallery_guide_video: BuildBranch; + view_gantt_config_color_help_url: BuildBranch; + view_gantt_guide_video: BuildBranch; + view_grid_guide_video: BuildBranch; + view_kanban_guide_video: BuildBranch; + view_mirror_list_empty_img: BuildBranch; + widget_center_feature_not_unturned_on_img: BuildBranch; + widget_center_help_link: BuildBranch; + widget_center_space_widget_empty_img: BuildBranch; + widget_cli_miumum_version: BuildBranch; + widget_create_widget_help_url: BuildBranch; + widget_custom_widget_empty_img: BuildBranch; + widget_default_cover_img: BuildBranch; + widget_default_template_url: BuildBranch; + widget_develop_init_help_url: BuildBranch; + widget_develop_install_help_url: BuildBranch; + widget_develop_preview_help_url: BuildBranch; + widget_develop_start_help_url: BuildBranch; + widget_how_to_close_browser_restriction_help_url: BuildBranch; + widget_panel_empty_img: BuildBranch; + widget_release_help_url: BuildBranch; + workbench_folder_default_cover_list: BuildBranch; + workbench_max_node_number_show_invite_and_new_node: BuildBranch; + workbench_no_permission_img: BuildBranch; } export interface BuildBranch { @@ -860,7 +856,7 @@ export interface BuildBranch { } export interface IntegrationHelpURL { - value: string; + value: string; marketplace: IntegrationDingtalkHelpURLMarketplace; } @@ -869,56 +865,56 @@ export interface IntegrationDingtalkHelpURLMarketplace { } export interface ShortcutKey { - show?: boolean; - key: string; - winKey: string; - name?: string[]; - when?: string; - id: string; - command: string; + show?: boolean; + key: string; + winKey: string; + name?: string[]; + when?: string; + id: string; + command: string; description?: string; - type?: TypeElement[]; + type?: TypeElement[]; } export enum TypeElement { - GalleryViewShortcuts = "gallery_view_shortcuts", - GlobalShortcuts = "global_shortcuts", - WorkbenckShortcuts = "workbenck_shortcuts", + GalleryViewShortcuts = 'gallery_view_shortcuts', + GlobalShortcuts = 'global_shortcuts', + WorkbenckShortcuts = 'workbenck_shortcuts', } export interface TestFunction { - async_compute: AsyncCompute; - render_prompt: AsyncCompute; - robot: AsyncCompute; - widget_center: AsyncCompute; - render_normal: AsyncCompute; + async_compute: AsyncCompute; + render_prompt: AsyncCompute; + robot: AsyncCompute; + widget_center: AsyncCompute; + render_normal: AsyncCompute; view_manual_save: AsyncCompute; } export interface AsyncCompute { feature_name: string; - logo: string; - id: string; - note: string; - feature_key: string; - modal: AsyncComputeModal; - card: Card; + logo: string; + id: string; + note: string; + feature_key: string; + modal: AsyncComputeModal; + card: Card; } export interface Card { - btn_open_action: string; - info: string; - info的副本: string; + btn_open_action: string; + info: string; + info的副本: string; btn_close_action: string; - btn_text: string; - btn_type: string; + btn_text: string; + btn_type: string; } export interface AsyncComputeModal { - btn_text: string; - info: string; + btn_text: string; + info: string; btn_action?: string; - btn_type: string; - info的副本: string; - info_image: string; -} + btn_type: string; + info的副本: string; + info_image: string; +} \ No newline at end of file diff --git a/packages/core/src/config/system_config.ts b/packages/core/src/config/system_config.ts index 032eef489e093ccf8c1fc5cff0a2cfdf8095696b..29b55abfc7ab19407b3fae5f06a9dab7f950a008 100644 --- a/packages/core/src/config/system_config.ts +++ b/packages/core/src/config/system_config.ts @@ -19,12 +19,12 @@ import { APITipConfigInterface, Tips } from 'config/api_tip_config.interface'; import apiTipConfigJson from './api_tip_config.auto.json'; import systemConfigJson from './system_config.auto.json'; -import { SystemConfigInterfaceNotifications, SystemConfigInterface, Templates, Types } from './system_config.interface'; +import { SystemConfigInterface, SystemConfigInterfaceNotifications, Templates, Types } from './system_config.interface'; /** - * config class, directly get the entire class of SystemConfig - */ -const SystemConfig: SystemConfigInterface = systemConfigJson as SystemConfigInterface; + * config class, directly get the entire class of SystemConfig + */ +const SystemConfig: SystemConfigInterface = systemConfigJson as unknown as SystemConfigInterface; /** * api tip config class @@ -32,9 +32,9 @@ const SystemConfig: SystemConfigInterface = systemConfigJson as SystemConfigInte const ApiTipConfig = apiTipConfigJson as APITipConfigInterface; /** - * Settings object, quickly get system_config.system table, system constant configuration - * - * pass in key + * Settings object, quickly get system_config.system table, system constant configuration + * + * pass in key * @example Conf.api_rate... */ const Settings = SystemConfig.settings; diff --git a/packages/core/src/engine/ot/index.ts b/packages/core/src/engine/ot/index.ts index 9ed400b92b7c0d8f73bf144bd85bae0d70657843..bad6274c17cc77c2bda975491576538ed4894a23 100644 --- a/packages/core/src/engine/ot/index.ts +++ b/packages/core/src/engine/ot/index.ts @@ -30,7 +30,7 @@ export const jot: IJot = { ...json0, apply(json, actions) { try { - return json0.apply(json, actions) + return json0.apply(json, actions); } catch (e) { if ((e as Error).message === 'invalid / missing instruction in op') { throw new Error(t(Strings.missing_instruction_op_error), { cause: e }); @@ -39,4 +39,4 @@ export const jot: IJot = { } } } -} +}; diff --git a/packages/core/src/engine/ot/json0.d.ts b/packages/core/src/engine/ot/json0.d.ts index 72c62feaeb2c79a74ef8587b8e98295e9d567bb9..ba02a33499b5356b44b1305bac1eb11b9c8875a1 100644 --- a/packages/core/src/engine/ot/json0.d.ts +++ b/packages/core/src/engine/ot/json0.d.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - -declare module "ot-json0/lib/json0" { - export default json0 +declare module 'ot-json0/lib/json0' { + export default json0; } \ No newline at end of file diff --git a/packages/core/src/formula_parser/errors/params_count.error.ts b/packages/core/src/formula_parser/errors/params_count.error.ts index 274376732acfa2e026554b3ea66369420480d7fe..52c1a084889d9541f8a96e76954e296f9e9154d7 100644 --- a/packages/core/src/formula_parser/errors/params_count.error.ts +++ b/packages/core/src/formula_parser/errors/params_count.error.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - import { t, Strings } from '../../exports/i18n'; /** diff --git a/packages/core/src/formula_parser/errors/unit.error.ts b/packages/core/src/formula_parser/errors/unit.error.ts index f1f05f04b4fb49fec50698c5933abd14f7d3e281..0254e715c60d103cc1a207fa53bf71640a5e40b5 100644 --- a/packages/core/src/formula_parser/errors/unit.error.ts +++ b/packages/core/src/formula_parser/errors/unit.error.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - import { t, Strings } from '../../exports/i18n'; export class UnitError extends Error { diff --git a/packages/core/src/formula_parser/evaluate.ts b/packages/core/src/formula_parser/evaluate.ts index 9df3bd1fe910bd174d48453ad4b836f0e3efd0a1..95545deba9ed21c8922bf3939bf3ed98ec8c4b8a 100644 --- a/packages/core/src/formula_parser/evaluate.ts +++ b/packages/core/src/formula_parser/evaluate.ts @@ -244,7 +244,7 @@ export function expressionTransform( return field.id; } - const name = field.name.replace(/(\{|\})/g, '\\$1'); + const name = field.name.replace(/[{}\\]/g, '\\$&'); // When name contains illegal parameters, {} wrapping is required if (/[/+\-|=*/><()()!&%'"“”‘’^`~,,\s]/.test(name)) { return `{${name}}`; @@ -264,9 +264,9 @@ export function expressionTransform( return `{${t(Strings.crypto_field)}}`; } - // Convert the variable name according to the to parameter + // Convert the variable name according to the 'to' parameter if (field) { - return to === 'id' ? `{${field.id}}` : `{${field.name.replace(/(\{|\})/g, '\\$1')}}`; + return to === 'id' ? `{${field.id}}` : `{${field.name.replace(/[{}\\]/g, '\\$&')}}`; } console.log(t(Strings.not_found_field_the_name_as, { diff --git a/packages/core/src/formula_parser/functions/array.ts b/packages/core/src/formula_parser/functions/array.ts index 78e1194fd9642146da3b2e2e65d5117163cee910..32a108a1da0b91d07f8a3423a098734fc779daac 100644 --- a/packages/core/src/formula_parser/functions/array.ts +++ b/packages/core/src/formula_parser/functions/array.ts @@ -242,7 +242,7 @@ export class CountIf extends ArrayFunc { if (range == null) return 0; if (rangeNode.valueType === BasicValueType.Array) { // @ts-ignore - declare let range: any[] + declare let range: any[]; const filterTypes = [BasicValueType.String, BasicValueType.Number]; // special convertible type switch (finalSymbol) { case SymbolType.Equal: { diff --git a/packages/core/src/model/_colors.ts b/packages/core/src/model/_colors.ts index aba0703554a55d2d46f6e7b2cdcb7fef261bd2e5..eb48fa42be1b87bae25137e34d0a21a5fd011758 100644 --- a/packages/core/src/model/_colors.ts +++ b/packages/core/src/model/_colors.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - // WARN: this color file is generated and synced by script, DO NOT modify it manually!!! export const COLOR_MAP = { black: '#8C8C8C', diff --git a/packages/core/src/model/datasheet.ts b/packages/core/src/model/datasheet.ts index 0c725bc9427a747f9aaa90b6a0c8dd75eaea33dd..0608fcd0858fae69809a16f42f7f786012bf5df3 100644 --- a/packages/core/src/model/datasheet.ts +++ b/packages/core/src/model/datasheet.ts @@ -161,7 +161,7 @@ function getDefaultNewRecordDataByFilter( */ const pass = conditionGroup.every(condition => doFilter(state, condition, field, result)); if (pass) { - // different fields of `And`, only need to assign the values ​​that have values ​​to them. + // different fields of `And`, only need to assign the values that have values to them. recordData[fieldId] = result; } } diff --git a/packages/core/src/model/field/__tests__/open/common.ts b/packages/core/src/model/field/__tests__/open/common.ts index 8eb06716d123b854a053a29aedacc681ce956c52..810933e1b8f881afd8aa3700cb3c657ff3ae26cc 100644 --- a/packages/core/src/model/field/__tests__/open/common.ts +++ b/packages/core/src/model/field/__tests__/open/common.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - import { Field } from 'model'; import { IField, IFieldProperty } from 'types/field_types'; import { IAddOpenFieldProperty, IEffectOption, IUpdateOpenFieldProperty } from 'types/open/open_field_write_types'; diff --git a/packages/core/src/model/field/date_time_base_field.ts b/packages/core/src/model/field/date_time_base_field.ts index 7a595846a133c56d8976ff2edd51b1e73b7c0c37..437f23ea412df1e8d7fe26420c09df109a7f23c8 100644 --- a/packages/core/src/model/field/date_time_base_field.ts +++ b/packages/core/src/model/field/date_time_base_field.ts @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import dayjs from 'dayjs'; +import dayjs, { PluginFunc } from 'dayjs'; import 'dayjs/locale/zh-cn'; import 'dayjs/locale/zh-hk'; import 'dayjs/locale/zh-tw'; @@ -39,9 +39,67 @@ import { ICellValue } from '../record'; import { Field } from './field'; import { StatTranslate, StatType } from './stat'; +const patchDayjsTimezone = (timezone: PluginFunc): PluginFunc => { + // The original version of the functions `getDateTimeFormat` and `tz` comes from + // https://github.com/iamkun/dayjs/blob/dev/src/plugin/timezone/index.js + // which is published under the MIT license. See https://github.com/iamkun/dayjs/blob/dev/LICENSE for more information. + + const dtfCache: { [timezone: string]: Intl.DateTimeFormat } = {}; + const getDateTimeFormat = (timezone: string, options: { timeZoneName?: Intl.DateTimeFormatOptions['timeZoneName'] } = {}) => { + const timeZoneName = options.timeZoneName || 'short'; + const key = `${timezone}|${timeZoneName}`; + let dtf = dtfCache[key]; + if (!dtf) { + dtf = new Intl.DateTimeFormat('en-US', { + // hour12: false, + hour12: true, + timeZone: timezone, + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + // timeZoneName + }); + dtfCache[key] = dtf; + } + return dtf; + }; + + const MS = 'millisecond'; + const MIN = 'minute'; + + return (o, c, d) => { + let defaultTimezone: string | undefined; + timezone(o, c, d); + // The following function integrates a performance tuning from https://github.com/iamkun/dayjs/issues/1236#issuecomment-1262907180 + c.prototype.tz = function(this: dayjs.Dayjs, timezone = defaultTimezone, keepLocalTime = undefined) { + const oldOffset = this.utcOffset(); + const date = this.toDate(); + const target = getDateTimeFormat(timezone!).format(date); + const diff = Math.round((+date - +new Date(target)) / 1000 / 60); + let ins = (d(target) as any).$set(MS, (this as any).$ms) + .utcOffset((-Math.round(date.getTimezoneOffset() / 15) * 15) - diff, true); + if (keepLocalTime) { + const newOffset = ins.utcOffset(); + ins = ins.add(oldOffset - newOffset, MIN); + } + ins.$x.$timezone = timezone; + return ins; + }; + + const setDefaultTimezone = d.tz.setDefault; + d.tz.setDefault = (timezone) => { + setDefaultTimezone(timezone); + defaultTimezone = timezone; + }; + }; +}; + // plugin before import, prevent circular import dayjs.extend(utc); -dayjs.extend(timezone); +dayjs.extend(patchDayjsTimezone(timezone)); declare const window: any; export interface IOptionalDateTimeFieldProperty { @@ -86,7 +144,8 @@ export const dateTimeFormat = ( } // server-side if (typeof window === 'undefined' && typeof global === 'object' && global.process) { - return dayjs(Number(timestamp)).tz(DEFAULT_TIMEZONE).format(format); + const date = dayjs(Number(timestamp)).tz(DEFAULT_TIMEZONE); + return date.format(format); } return dayjs(Number(timestamp)).format(format); }; diff --git a/packages/core/src/model/resource.ts b/packages/core/src/model/resource.ts index 903876620b15eadbfbb02d5e3351607c40b8529f..3c6071af63cd7f9a9955a5cb5abd16d04829da11 100644 --- a/packages/core/src/model/resource.ts +++ b/packages/core/src/model/resource.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - export class ResourceModel { // ... diff --git a/packages/core/src/modules/database/api/datasheet_api.ts b/packages/core/src/modules/database/api/datasheet_api.ts index 7e1427d79b71354db58a4cf64afed29ec26054d6..56d170e2a7526fc917e8f5fb676c84ba8d5c1957 100644 --- a/packages/core/src/modules/database/api/datasheet_api.ts +++ b/packages/core/src/modules/database/api/datasheet_api.ts @@ -99,8 +99,8 @@ export function fetchShareForeignDatasheetPack(shareId: string, resourceId: stri return axios.get(urlcat(Url.READ_SHARE_FOREIGN_DATASHEET_PACK, { shareId, resourceId, foreignDatasheetId }), { baseURL }); } -export function fetchEmbedForeignDatasheetPack(embedId: string, foreignDatasheetId: string) { - return axios.get(urlcat(Url.READ_EMBED_FOREIGN_DATASHEET_PACK, { embedId, foreignDatasheetId }), { baseURL }); +export function fetchEmbedForeignDatasheetPack(embedId: string, resourceId: string, foreignDatasheetId: string) { + return axios.get(urlcat(Url.READ_EMBED_FOREIGN_DATASHEET_PACK, { embedId, resourceId, foreignDatasheetId }), { baseURL }); } /** diff --git a/packages/core/src/modules/database/api/url.data.ts b/packages/core/src/modules/database/api/url.data.ts index 2d445a573e8c0833f3b6faf6b0e37782b0b5d84d..3fe3e0439122138fa80d65b26e6ce9a0f5c0a459 100644 --- a/packages/core/src/modules/database/api/url.data.ts +++ b/packages/core/src/modules/database/api/url.data.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - // =============== Dashboard related ======================= export const FETCH_DASHBOARD = '/dashboard/:dashboardId/dataPack'; export const FETCH_SHARE_DASHBOARD = '/share/:shareId/dashboard/:dashboardId/dataPck'; @@ -93,7 +92,7 @@ export const READ_FOREIGN_DATASHEET_PACK = '/resource/:resourceId/foreignDatashe export const READ_SHARE_FOREIGN_DATASHEET_PACK = '/share/:shareId/resource/:resourceId/foreignDatasheet/:foreignDatasheetId/dataPack'; // Get comments and history for a single record export const GET_RECORD_ACTIVITY_LIST = '/resources/:resourceId/records/:recId/activity'; -export const READ_EMBED_FOREIGN_DATASHEET_PACK = 'embedlinks/:embedId/foreignDatasheets/:foreignDatasheetId/dataPack'; +export const READ_EMBED_FOREIGN_DATASHEET_PACK = 'embedlinks/:embedId/resources/:resourceId/foreignDatasheets/:foreignDatasheetId/dataPack'; // =============== datasheet related ======================= // load table data package diff --git a/packages/core/src/modules/database/store/actions/resource/datasheet/datasheet.ts b/packages/core/src/modules/database/store/actions/resource/datasheet/datasheet.ts index 4a1fc9d7460537094aa45c1ce8180a11c470a663..1d1ee8055a06d4d2a428e4dbff5be58df336d810 100644 --- a/packages/core/src/modules/database/store/actions/resource/datasheet/datasheet.ts +++ b/packages/core/src/modules/database/store/actions/resource/datasheet/datasheet.ts @@ -254,7 +254,7 @@ export function fetchForeignDatasheet(resourceId: string, foreignDstId: string, } if(embedId) { - requestMethod = () => fetchEmbedForeignDatasheetPack(embedId, foreignDstId); + requestMethod = () => fetchEmbedForeignDatasheetPack(embedId, resourceId, foreignDstId); } if (forceFetch || !foreignDatasheet || foreignDatasheet.isPartOfData) { diff --git a/packages/core/src/modules/database/store/interfaces/resource/datasheet/datasheet.ts b/packages/core/src/modules/database/store/interfaces/resource/datasheet/datasheet.ts index 9524967caed215cae6bae628708c44fec05eba85..c68dd37fb5b91be86773ab3d4340f3b4b4ab7da2 100644 --- a/packages/core/src/modules/database/store/interfaces/resource/datasheet/datasheet.ts +++ b/packages/core/src/modules/database/store/interfaces/resource/datasheet/datasheet.ts @@ -18,35 +18,18 @@ import { IRemoteChangeset } from 'engine/ot'; import { FillDirection, ICell, ICellValue, IFieldRanges, IRange, IRecordRanges, StatType } from 'model'; +import { IField, IStandardValue } from 'types/field_types'; +import { IFilterInfo, IGroupInfo, ISortedField, ISortInfo } from 'types/view_types'; import { - CellType, - GalleryStyleKeyType, - IUnitValue, - IUserValue, - LayoutType, - RowHeightLevel, - ViewType, - WhyRecordMoveType, + CellType, GalleryStyleKeyType, IUnitValue, IUserValue, LayoutType, RowHeightLevel, ViewType, WhyRecordMoveType, } from '../../../../../../exports/store'; +import * as actions from '../../../../../shared/store/action_constants'; import { - CalendarColorType, - CalendarStyleKeyType, - GanttColorType, - GanttStyleKeyType, - OrgChartStyleKeyType, + CalendarColorType, CalendarStyleKeyType, GanttColorType, GanttStyleKeyType, OrgChartStyleKeyType, } from '../../../../../shared/store/constants'; -import { IField, IStandardValue } from 'types/field_types'; -import { IFilterInfo, IGroupInfo, ISortedField, ISortInfo } from 'types/view_types'; -import * as actions from '../../../../../shared/store/action_constants'; import { IPermissions, Role } from '../../../../../space/store/interfaces/catalog_tree'; import { - ICalendarViewStatus, - IGanttViewStatus, - IGridViewActiveFieldState, - IGridViewDragState, - IKanbanViewStatus, - IOrgChartViewStatus, - ISearchResult, + ICalendarViewStatus, IGanttViewStatus, IGridViewActiveFieldState, IGridViewDragState, IKanbanViewStatus, IOrgChartViewStatus, ISearchResult, IWidgetPanelStatus, } from './client'; @@ -351,7 +334,7 @@ export interface IDatasheetState extends INodeMeta { } export interface ILoadingRecord { - [recordId: string]: boolean; + [recordId: string]: boolean | 'error'; } export interface IActiveUpdateRowInfo { @@ -765,6 +748,8 @@ export interface ICollaborator extends ICollaboratorParams { recordId: string; time: number; }; + avatarColor?: number; + nickName?: string; } export interface IResourceRevision { diff --git a/packages/core/src/modules/database/store/reducers/preview_file.ts b/packages/core/src/modules/database/store/reducers/preview_file.ts index 61f5836b60065bbd5dbaa0cc4181cf03a2a52d1d..bc7c291ce452e87c0a82596a951b7887034253aa 100644 --- a/packages/core/src/modules/database/store/reducers/preview_file.ts +++ b/packages/core/src/modules/database/store/reducers/preview_file.ts @@ -17,8 +17,8 @@ */ import { produce } from 'immer'; -import { SET_PREVIEW_FILE, SET_PREVIEW_FILE_CELL_ACTIVE, SET_PREVIEW_DEFAULT_ACTIVE } from '../../../shared/store/action_constants'; import { IPreviewFile, ISetPreviewDefaultAction, ISetPreviewFileAction, ISetPreviewFileCellValueAction } from '../../../../exports/store/interfaces'; +import { SET_PREVIEW_DEFAULT_ACTIVE, SET_PREVIEW_FILE, SET_PREVIEW_FILE_CELL_ACTIVE } from '../../../shared/store/action_constants'; const defaultState: IPreviewFile = { datasheetId: undefined, @@ -39,12 +39,12 @@ export const previewFile = produce( case SET_PREVIEW_FILE: return { ...action.payload }; case SET_PREVIEW_FILE_CELL_ACTIVE: - state.cellValue = action.payload; + state.cellValue = action.payload; return state; case SET_PREVIEW_DEFAULT_ACTIVE: return defaultState; default: return state; } - } + }, defaultState ); diff --git a/packages/core/src/modules/database/store/reducers/record_vision_mode.ts b/packages/core/src/modules/database/store/reducers/record_vision_mode.ts index 108f6993f870ac2681d80d21f1b43180271c2c23..706fb65bcce9f93c241523c2e257b2f7e5382ca2 100644 --- a/packages/core/src/modules/database/store/reducers/record_vision_mode.ts +++ b/packages/core/src/modules/database/store/reducers/record_vision_mode.ts @@ -16,9 +16,9 @@ * along with this program. If not, see . */ -import { SET_RECORD_VISION_MODE } from '../../../shared/store/action_constants'; -import { IRecordVisionAction, RecordVision } from '../../../../exports/store/interfaces'; import produce from 'immer'; +import { IRecordVisionAction, RecordVision } from '../../../../exports/store/interfaces'; +import { SET_RECORD_VISION_MODE } from '../../../shared/store/action_constants'; export const recordVision = produce((recordVisionModeDraft: RecordVision = RecordVision.Center, action: IRecordVisionAction) => { if (action.type === SET_RECORD_VISION_MODE) { @@ -26,4 +26,4 @@ export const recordVision = produce((recordVisionModeDraft: RecordVision = Recor return action.payload; } return recordVisionModeDraft; -}); +}, RecordVision.Center); diff --git a/packages/core/src/modules/database/store/reducers/resource/datasheet/client.ts b/packages/core/src/modules/database/store/reducers/resource/datasheet/client.ts index 29d9b205bf2fcef08d039f67e70b7216490d259d..9cd011f27a1ad03c062cae60ca7ac8b1ff3480fe 100644 --- a/packages/core/src/modules/database/store/reducers/resource/datasheet/client.ts +++ b/packages/core/src/modules/database/store/reducers/resource/datasheet/client.ts @@ -20,24 +20,19 @@ import produce from 'immer'; import { combineReducers } from 'redux'; import { ISetCloseSyncViewIdAction, ISetGridViewHoverFieldIdAction } from '../../../../../../exports/store/actions'; import { - CHANGE_WIDGET_PANEL_WIDTH, SET_ACTIVE_ROW_INFO, SET_EDIT_STATUS, SET_GANTT_DATE_UNIT_TYPE, SET_GANTT_GRID_WIDTH, SET_GANTT_SETTING_PANEL_WIDTH, - SET_GRID_VIEW_HOVER_FIELD_ID, SET_GROUPING_COLLAPSE, SET_KANBAN_GROUPING_EXPAND, SET_LOADING_RECORD, - SET_NEW_RECORD_EXPECT_INDEX, SET_SEARCH_KEYWORD, - SET_CALENDAR_SETTING_PANEL_WIDTH, TOGGLE_CALENDAR_SETTING_PANEL, - SET_SEARCH_RESULT_CURSOR_INDEX, SWITCH_ACTIVE_PANEL, SET_CALENDAR_GRID_WIDTH, TOGGLE_CALENDAR_GUIDE_STATUS, - TOGGLE_GANTT_GRID, TOGGLE_GANTT_SETTING_PANEL, TOGGLE_WIDGET_PANEL, TOGGLE_CALENDAR_GRID, SET_WIDGET_PANEL_LOADING, SET_HIGHLIGHT_FIELD_ID, - CLEAR_ACTIVE_ROW_INFO, - SET_CLOSE_SYNC_VIEW_ID, SET_ROBOT_PANEL_STATUS, SET_ORG_CHART_GRID_WIDTH, SET_ORG_CHART_SETTING_PANEL_WIDTH, TOGGLE_ORG_CHART_GRID, - TOGGLE_ORG_CHART_GUIDE_STATUS, TOGGLE_ORG_CHART_SETTING_PANEL, ACTIVE_OPERATE_VIEW_ID, RESET_OPERATE_VIEW_ID, - ACTIVE_EXPORT_VIEW_ID, RESET_EXPORT_VIEW_ID, - TOGGLE_TIME_MACHINE_PANEL, - TOGGLE_KANBAN_GROUP_SETTING_VISIBLE -} from '../../../../../shared/store/action_constants'; -import { DateUnitType, WhyRecordMoveType } from '../../../../../shared/store/constants'; -import { - IActiveRowInfo, ICalendarViewStatus, IDatasheetClientState, IGanttViewStatus, IKanbanViewStatus, ILoadingRecord, ILoadingRecordAction, + IActiveRowInfo, ICalendarViewStatus, IDatasheetClientState, IGanttViewStatus, IKanbanViewStatus, ILoadingRecord, ILoadingRecordAction, IOrgChartViewStatus, IWidgetPanelStatus } from '../../../../../../exports/store/interfaces'; +import { + ACTIVE_EXPORT_VIEW_ID, ACTIVE_OPERATE_VIEW_ID, CHANGE_WIDGET_PANEL_WIDTH, CLEAR_ACTIVE_ROW_INFO, RESET_EXPORT_VIEW_ID, RESET_OPERATE_VIEW_ID, + SET_ACTIVE_ROW_INFO, SET_CALENDAR_GRID_WIDTH, SET_CALENDAR_SETTING_PANEL_WIDTH, SET_CLOSE_SYNC_VIEW_ID, SET_EDIT_STATUS, SET_GANTT_DATE_UNIT_TYPE, + SET_GANTT_GRID_WIDTH, SET_GANTT_SETTING_PANEL_WIDTH, SET_GRID_VIEW_HOVER_FIELD_ID, SET_GROUPING_COLLAPSE, SET_HIGHLIGHT_FIELD_ID, + SET_KANBAN_GROUPING_EXPAND, SET_LOADING_RECORD, SET_NEW_RECORD_EXPECT_INDEX, SET_ORG_CHART_GRID_WIDTH, SET_ORG_CHART_SETTING_PANEL_WIDTH, + SET_ROBOT_PANEL_STATUS, SET_SEARCH_KEYWORD, SET_SEARCH_RESULT_CURSOR_INDEX, SET_WIDGET_PANEL_LOADING, SWITCH_ACTIVE_PANEL, TOGGLE_CALENDAR_GRID, + TOGGLE_CALENDAR_GUIDE_STATUS, TOGGLE_CALENDAR_SETTING_PANEL, TOGGLE_GANTT_GRID, TOGGLE_GANTT_SETTING_PANEL, TOGGLE_KANBAN_GROUP_SETTING_VISIBLE, + TOGGLE_ORG_CHART_GRID, TOGGLE_ORG_CHART_GUIDE_STATUS, TOGGLE_ORG_CHART_SETTING_PANEL, TOGGLE_TIME_MACHINE_PANEL, TOGGLE_WIDGET_PANEL +} from '../../../../../shared/store/action_constants'; +import { DateUnitType, WhyRecordMoveType } from '../../../../../shared/store/constants'; import { collaborators } from './collaborators'; import { gridViewActiveFieldState } from './grid_view_active_field'; import { gridViewDragState } from './grid_view_drag'; @@ -142,7 +137,7 @@ export const client = combineReducers({ } return state; }, - loadingRecord: produce((state = {}, action: ILoadingRecordAction): ILoadingRecord => { + loadingRecord: produce((state: ILoadingRecord = {}, action: ILoadingRecordAction): ILoadingRecord => { if (action.type === SET_LOADING_RECORD) { const { recordIds, loading } = action.payload; recordIds.forEach(recordId => { @@ -150,7 +145,7 @@ export const client = combineReducers({ }); } return state; - }), + }, {}), widgetPanelStatus: (state: IWidgetPanelStatus = defaultWidgetPanelStatus, action): IWidgetPanelStatus => { switch (action.type) { case TOGGLE_WIDGET_PANEL: { diff --git a/packages/core/src/modules/database/store/reducers/resource/datasheet/datasheet.ts b/packages/core/src/modules/database/store/reducers/resource/datasheet/datasheet.ts index ae9e413b7128ce68e27007d5cd27139ca330e57f..944ba90afb1198d2f00ada88e057bf37eca73221 100644 --- a/packages/core/src/modules/database/store/reducers/resource/datasheet/datasheet.ts +++ b/packages/core/src/modules/database/store/reducers/resource/datasheet/datasheet.ts @@ -18,17 +18,17 @@ import { compensator } from 'compensator'; import { IJOTAction } from 'engine/ot'; -import { Strings, t } from '../../../../../../exports/i18n'; import produce from 'immer'; -import { Events, Player } from '../../../../../shared/player'; +import { fieldPermissionMap } from 'modules/database/store/reducers/resource/datasheet/field_permission_map'; import { AnyAction, combineReducers } from 'redux'; -import * as actions from '../../../../../shared/store/action_constants'; +import { Strings, t } from '../../../../../../exports/i18n'; import { IAddDatasheetAction, IChangeViewAction, IComputedInfo, IComputedStatus, IDatasheetMap, IDatasheetPack, IDatasheetState, IJOTActionPayload, ILoadedDataPackAction, IRecordNodeDesc, IRecordNodeShared, IRefreshSnapshotAction, IResetDatasheetAction, ISetNodeIcon, IUpdateDatasheetAction, IUpdateDatasheetNameAction, IUpdateRevision, IUpdateSnapShotAction } from '../../../../../../exports/store/interfaces'; -import { fieldPermissionMap } from 'modules/database/store/reducers/resource/datasheet/field_permission_map'; +import { Events, Player } from '../../../../../shared/player'; +import * as actions from '../../../../../shared/store/action_constants'; import { JOTApply } from '../index'; import { client } from './client'; @@ -68,7 +68,7 @@ export const datasheet = produce(( action: IDatasheetAction, ): IDatasheetState | null => { if (action.type === actions.DATAPACK_LOADED) { - // only include part of the data payload + // only include part of the data payload // can not cover the state that contains all the data if (state && action.payload.isPartOfData && !state.isPartOfData) { console.log('datasheet with part of data ignored'); @@ -137,7 +137,7 @@ export const datasheet = produce(( } } return state; -}); +}, null); export const datasheetMap = ( state: IDatasheetMap = {}, diff --git a/packages/core/src/modules/database/store/reducers/resource/datasheet/grid_view_drag.ts b/packages/core/src/modules/database/store/reducers/resource/datasheet/grid_view_drag.ts index 97940b8d88bf59d936c2557273cf3d23591b1216..614d7474e3aa16e8a030ab888c2226e8369e1499 100644 --- a/packages/core/src/modules/database/store/reducers/resource/datasheet/grid_view_drag.ts +++ b/packages/core/src/modules/database/store/reducers/resource/datasheet/grid_view_drag.ts @@ -16,13 +16,13 @@ * along with this program. If not, see . */ +import produce from 'immer'; import { - IGridViewDragState, IHoverRecordId, IDragTargetAction, ISetHoverGroupPath, ISetHoverRowOfAddRecord, + IDragTargetAction, IGridViewDragState, IHoverRecordId, ISetHoverGroupPath, ISetHoverRowOfAddRecord, } from '../../../../../../exports/store/interfaces'; import { - SET_DRAG_TARGET, SET_HOVER_RECORD_ID, SET_HOVER_GROUP_PATH, SET_HOVER_ROW_OF_ADD_RECORD, + SET_DRAG_TARGET, SET_HOVER_GROUP_PATH, SET_HOVER_RECORD_ID, SET_HOVER_ROW_OF_ADD_RECORD, } from '../../../../../shared/store/action_constants'; -import produce from 'immer'; export const gridViewDragStateDefault: IGridViewDragState = { dragTarget: {}, @@ -50,4 +50,4 @@ export const gridViewDragState = produce( default: return draft; } - }); + }, gridViewDragStateDefault); diff --git a/packages/core/src/modules/database/store/reducers/resource/theme.ts b/packages/core/src/modules/database/store/reducers/resource/theme.ts index 39f91cbb04c323a3310f22bdbaf4940d2d77b3e3..de5e20998be0941806514d104c812e4b8704ffcf 100644 --- a/packages/core/src/modules/database/store/reducers/resource/theme.ts +++ b/packages/core/src/modules/database/store/reducers/resource/theme.ts @@ -16,9 +16,9 @@ * along with this program. If not, see . */ -import { SET_THEME } from '../../../../shared/store/action_constants'; -import { IThemeAction, ThemeName } from '../../../../../exports/store/interfaces'; import produce from 'immer'; +import { IThemeAction, ThemeName } from '../../../../../exports/store/interfaces'; +import { SET_THEME } from '../../../../shared/store/action_constants'; export const theme = produce((themeDraft: ThemeName = ThemeName.Light, action: IThemeAction) => { if (action.type === SET_THEME) { @@ -26,4 +26,4 @@ export const theme = produce((themeDraft: ThemeName = ThemeName.Light, action: I return action.payload; } return themeDraft; -}); \ No newline at end of file +}, ThemeName.Light); diff --git a/packages/core/src/modules/database/store/reducers/right_pane.ts b/packages/core/src/modules/database/store/reducers/right_pane.ts index 5622aac2c89ee3de3cfe07984190b5782ffa63cc..a214338f032de8c9bff9897d8e5e7057086f922d 100644 --- a/packages/core/src/modules/database/store/reducers/right_pane.ts +++ b/packages/core/src/modules/database/store/reducers/right_pane.ts @@ -32,5 +32,5 @@ export const rightPane = produce( default: return state; } - } + }, defaultState ); diff --git a/packages/core/src/modules/database/store/reducers/subscriptions.ts b/packages/core/src/modules/database/store/reducers/subscriptions.ts index a679bd81bc3ac483a2709cee5c4db7a23d513150..1d33c3d500587b2aa6e092b378c652c8ab37a2d1 100644 --- a/packages/core/src/modules/database/store/reducers/subscriptions.ts +++ b/packages/core/src/modules/database/store/reducers/subscriptions.ts @@ -18,8 +18,8 @@ // reducers for data subscribe(follow) import produce from 'immer'; +import { ISetSubscriptionsAction, ISubscriptions } from '../../../../exports/store/interfaces'; import { SET_SUBSCRIPTIONS } from '../../../shared/store/action_constants'; -import { ISubscriptions, ISetSubscriptionsAction } from '../../../../exports/store/interfaces'; export const subscriptions = produce((subscriptions: ISubscriptions = [], action: ISetSubscriptionsAction) => { switch (action.type) { @@ -30,4 +30,4 @@ export const subscriptions = produce((subscriptions: ISubscriptions = [], action return subscriptions; } } -}); +}, []); diff --git a/packages/core/src/modules/database/store/reducers/toolbar.ts b/packages/core/src/modules/database/store/reducers/toolbar.ts index 29bab2e9be2d74544c1dae5964b4cfc1c503aecc..53d93ef9ce241a22e218e506e4b8f9657b2f60b3 100644 --- a/packages/core/src/modules/database/store/reducers/toolbar.ts +++ b/packages/core/src/modules/database/store/reducers/toolbar.ts @@ -17,8 +17,8 @@ */ import { produce } from 'immer'; -import * as actions from '../../../shared/store/action_constants'; import { ISetToolBarMenuCardStateAction, IToolBar } from '../../../org/store/interface/tool_bar'; +import * as actions from '../../../shared/store/action_constants'; import { ToolBarMenuCardOpenState } from '../../../shared/store/constants'; const defaultState: IToolBar = { @@ -33,4 +33,4 @@ export const toolbar = produce( default: return state; } - }); + }, defaultState); diff --git a/packages/core/src/modules/database/store/selectors/resource/datasheet/base.ts b/packages/core/src/modules/database/store/selectors/resource/datasheet/base.ts index 9c469210a353c8f30f77ab289000722b70c3f2b3..8443bd2cf00d7b7b16ffd1d4959fd3316d2fcb2d 100644 --- a/packages/core/src/modules/database/store/selectors/resource/datasheet/base.ts +++ b/packages/core/src/modules/database/store/selectors/resource/datasheet/base.ts @@ -277,7 +277,9 @@ export const getLinkId = (state: IReduxState) => { export const allowShowCommentPane = (state: IReduxState) => { const spaceId = state.space.activeId; const linkId = getLinkId(state); - return Boolean(spaceId && !linkId); + const embedId = state.pageParams.embedId; + + return Boolean(spaceId && !linkId) || Boolean(spaceId && embedId); }; export const getDatasheetParentId = (state: IReduxState, id?: string) => { diff --git a/packages/core/src/modules/embed/store/interfaces/embed.ts b/packages/core/src/modules/embed/store/interfaces/embed.ts index 9fff754404303a640e9722d24cce81d8d6ac89d8..2d13a0224876db1577698ffaf6c56ecabca25af0 100644 --- a/packages/core/src/modules/embed/store/interfaces/embed.ts +++ b/packages/core/src/modules/embed/store/interfaces/embed.ts @@ -34,7 +34,8 @@ export interface IEmbedInfo { primarySideBar?: boolean, bannerLogo?: boolean, spaceId?: string - permissionType?: PermissionType + permissionType?: PermissionType, + isShowEmbedToolBar?: boolean } export enum PermissionType { diff --git a/packages/core/src/modules/embed/store/reducers/embed.ts b/packages/core/src/modules/embed/store/reducers/embed.ts index b0f7d19fb49268e79515c03f8071c2c4f52e2b13..6f46b23f6f90e82e1cff8f9d15b0e0cea31d023b 100644 --- a/packages/core/src/modules/embed/store/reducers/embed.ts +++ b/packages/core/src/modules/embed/store/reducers/embed.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - import produce from 'immer'; import * as actions from '../action_constants'; import { IEmbedInfo, IEmbedInfoAction } from '../interfaces/embed'; @@ -34,4 +33,4 @@ export const embedInfo = produce((embedInfoDraft: IEmbedInfo = defaultEmbedInfo, default: return embedInfoDraft; } -}); \ No newline at end of file +}, defaultEmbedInfo); diff --git a/packages/core/src/modules/org/store/actions/invite.ts b/packages/core/src/modules/org/store/actions/invite.ts index 7140681364f94d66b3bbfe27322944350bdc56e7..29b98cf3e63975c357a419b56454ff3ea04a0d06 100644 --- a/packages/core/src/modules/org/store/actions/invite.ts +++ b/packages/core/src/modules/org/store/actions/invite.ts @@ -45,7 +45,7 @@ export const updateMailToken = (token: string) => { }; }; export const getMailLinkData = (token: string): any => { - return async (dispatch: any) => { + return async(dispatch: any) => { const { data } = await Api.inviteEmailVerify(token); dispatch(updateInviteEmailInfo(data)); if (!data.success) { @@ -78,7 +78,7 @@ export const updateLinkInviteList = (list: IInviteLink[]) => { }; }; export const getSubTeamInvite = (teamId: string): any => { - return async (dispatch: any) => { + return async(dispatch: any) => { const subTree = await Api.getSubTeams(teamId); dispatch(updateSubTeamTreeInvite(teamId, subTree.data.data)); }; @@ -86,7 +86,7 @@ export const getSubTeamInvite = (teamId: string): any => { // TODO: to be delete by yudongdong export const getLinkInviteList = (): any => { - return async (dispatch: any) => { + return async(dispatch: any) => { const { data: { success, data }} = await Api.getLinkList(); if (success) { dispatch(updateLinkInviteList(data)); @@ -113,7 +113,7 @@ export const updateLinkToken = (token: string) => { }; export const verifyLink = (token: string): any => { - return async (dispatch: any) => { + return async(dispatch: any) => { const { data } = await Api.linkValid(token); dispatch(updateInviteLinkInfo(data)); }; diff --git a/packages/core/src/modules/org/store/actions/unit_info.ts b/packages/core/src/modules/org/store/actions/unit_info.ts index b9024e3b3ba1279c97c2d109a577fd33e8bb8fb3..98241b304f2b159157abd0b1be4d7e30d6d9882c 100644 --- a/packages/core/src/modules/org/store/actions/unit_info.ts +++ b/packages/core/src/modules/org/store/actions/unit_info.ts @@ -62,7 +62,7 @@ export const resetUnitInfo = () => { * @returns */ export const loadLackUnitMap = (names: string, linkId?: string,) => { - return async (dispatch: any) => { + return async(dispatch: any) => { if (!names.length) { return; } diff --git a/packages/core/src/modules/org/store/interface/tool_bar.ts b/packages/core/src/modules/org/store/interface/tool_bar.ts index 2daba8a2dab2d65f78daa1370bfd018535823132..86c63c678fbb9aa485f95399caed1b7fdea6bcde 100644 --- a/packages/core/src/modules/org/store/interface/tool_bar.ts +++ b/packages/core/src/modules/org/store/interface/tool_bar.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - import { SET_TOOLBAR_MENU_CARD_OPEN } from '../../../shared/store/action_constants'; import { ToolBarMenuCardOpenState } from '../../../shared/store/constants'; diff --git a/packages/core/src/modules/org/store/reducers/address_list.ts b/packages/core/src/modules/org/store/reducers/address_list.ts index 557f672250443464bd792aedf22b0b915d11757f..996b0abb8599f486a17dcdcee224625cb5306074 100644 --- a/packages/core/src/modules/org/store/reducers/address_list.ts +++ b/packages/core/src/modules/org/store/reducers/address_list.ts @@ -16,17 +16,13 @@ * along with this program. If not, see . */ +import { produce } from 'immer'; import { - IAddressList, - IUpdateTeamListAction, - IUpdateMemberListAction, - IUpdateSelectedTeamInfoAction, - IUpdateMemberInfoAction, - IMemberInfoInAddressList, - IUpdateSingleMemberInMemberListAction, + IAddressList, IMemberInfoInAddressList, IUpdateMemberInfoAction, IUpdateMemberListAction, IUpdateSelectedTeamInfoAction, + IUpdateSingleMemberInMemberListAction, IUpdateTeamListAction, } from '../../../../exports/store/interfaces'; import * as actions from '../../../shared/store/action_constants'; -import { produce } from 'immer'; + const defaultState: IAddressList = { teamList: [], // selected team @@ -54,10 +50,10 @@ const updateMemberInList = (state: IMemberInfoInAddressList[], payload: Partial< if (cur.memberId === payload.memberId) { prev.push({ ...cur, ...payload }); return prev; - } + } prev.push({ ...cur }); return prev; - + }, []); }; export const addressList = produce((data: IAddressList = defaultState, action: IAddressListActions) => { @@ -85,4 +81,4 @@ export const addressList = produce((data: IAddressList = defaultState, action: I default: return data; } -}); +}, defaultState); diff --git a/packages/core/src/modules/org/store/reducers/invite.ts b/packages/core/src/modules/org/store/reducers/invite.ts index 1ef35962a6c99671a1d65c1df64e365983c3dadf..f3242725a069aadd065a56a2de5d7aa9f4e4a548 100644 --- a/packages/core/src/modules/org/store/reducers/invite.ts +++ b/packages/core/src/modules/org/store/reducers/invite.ts @@ -16,12 +16,13 @@ * along with this program. If not, see . */ -import { IInvite, IUpdateInviteEmailInfoAction, IUpdateTeamTreeInviteAction, - IUpdateSubTeamTreeInviteAction, ITeamTreeNode, IUpdateLinkListAction, IUpdateInviteLinkInfoAction, - IUpdateLinkTokenAction, IUpdateMailTokenAction, IUpdateErrCodeAction, +import { produce } from 'immer'; +import { + IInvite, ITeamTreeNode, IUpdateErrCodeAction, IUpdateInviteEmailInfoAction, IUpdateInviteLinkInfoAction, IUpdateLinkListAction, + IUpdateLinkTokenAction, IUpdateMailTokenAction, IUpdateSubTeamTreeInviteAction, IUpdateTeamTreeInviteAction, } from '../../../../exports/store/interfaces'; import * as actions from '../../../shared/store/action_constants'; -import { produce } from 'immer'; + const defaultState: IInvite = { inviteEmailInfo: null, teamTreeInvite: [], @@ -33,8 +34,8 @@ const defaultState: IInvite = { }; type IInviteActions = IUpdateInviteEmailInfoAction | IUpdateTeamTreeInviteAction | -IUpdateSubTeamTreeInviteAction | IUpdateLinkListAction | IUpdateInviteLinkInfoAction | IUpdateLinkTokenAction | -IUpdateMailTokenAction | IUpdateErrCodeAction; + IUpdateSubTeamTreeInviteAction | IUpdateLinkListAction | IUpdateInviteLinkInfoAction | IUpdateLinkTokenAction | + IUpdateMailTokenAction | IUpdateErrCodeAction; const findParent = (data: ITeamTreeNode[], id: string): null | ITeamTreeNode => { return data.reduce((preValue, item) => { @@ -96,4 +97,4 @@ export const invite = produce((data: IInvite = defaultState, action: IInviteActi default: return data; } -}); +}, defaultState); diff --git a/packages/core/src/modules/org/store/reducers/space_member_manage.ts b/packages/core/src/modules/org/store/reducers/space_member_manage.ts index cdc19f8647e35ba9d75ec2d2c8c53f91a8fb9b88..8004c43f6f0cc3fd5a9df1ca08086d1c2dcce7fe 100644 --- a/packages/core/src/modules/org/store/reducers/space_member_manage.ts +++ b/packages/core/src/modules/org/store/reducers/space_member_manage.ts @@ -16,22 +16,13 @@ * along with this program. If not, see . */ +import { produce } from 'immer'; import { - ISpaceMemberManage, - IUpdateTeamListInSpaceAction, - IUpdateMemberListInSpaceAction, - IUpdateSelectedTeamInfoInSpaceAction, - IUpdateMemberInfoInSpaceAction, - IUpdateSelectMemberListInSpaceAction, - IUpdateSubTeamListInSpaceAction, - IUpdateRightClickTeamInfoInSpaceAction, - IUpdateInviteStatusAction, - ISelectedTeamKeysAction, - ISelectedTeamRowsAction, - IUpdateSelectedRowsInSpaceAction, + ISelectedTeamKeysAction, ISelectedTeamRowsAction, ISpaceMemberManage, IUpdateInviteStatusAction, IUpdateMemberInfoInSpaceAction, + IUpdateMemberListInSpaceAction, IUpdateRightClickTeamInfoInSpaceAction, IUpdateSelectedRowsInSpaceAction, IUpdateSelectedTeamInfoInSpaceAction, + IUpdateSelectMemberListInSpaceAction, IUpdateSubTeamListInSpaceAction, IUpdateTeamListInSpaceAction, } from '../../../../exports/store/interfaces'; import * as actions from '../../../shared/store/action_constants'; -import { produce } from 'immer'; const defaultState: ISpaceMemberManage = { // selected teams @@ -115,4 +106,4 @@ export const spaceMemberManage = produce(( default: return storeData; } -}); +}, defaultState); diff --git a/packages/core/src/modules/org/store/reducers/space_permission_manage.ts b/packages/core/src/modules/org/store/reducers/space_permission_manage.ts index 99a9c2b75faf51c414b868b6c107e6bc77713be3..16548b601a8c123593073d280b3d056d19427971 100644 --- a/packages/core/src/modules/org/store/reducers/space_permission_manage.ts +++ b/packages/core/src/modules/org/store/reducers/space_permission_manage.ts @@ -16,20 +16,18 @@ * along with this program. If not, see . */ +import { produce } from 'immer'; import { - ISpacePermissionManage, - IUpdateSubAdminListDataAction, - IUpdateMainAdminInfoAction, - IUpdateSpaceResourceAction, + ISpacePermissionManage, IUpdateMainAdminInfoAction, IUpdateSpaceResourceAction, IUpdateSubAdminListDataAction, } from '../../../../exports/store/interfaces'; import * as actions from '../../../shared/store/action_constants'; -import { produce } from 'immer'; + const defaultState: ISpacePermissionManage = { subAdminListData: null, mainAdminInfo: null, spaceResource: null, }; -type ISpacePermissionManageActions = IUpdateSubAdminListDataAction +type ISpacePermissionManageActions = IUpdateSubAdminListDataAction | IUpdateMainAdminInfoAction | IUpdateSpaceResourceAction; export const spacePermissionManage = produce(( @@ -52,4 +50,4 @@ export const spacePermissionManage = produce(( default: return data; } -}); +}, defaultState); diff --git a/packages/core/src/modules/shared/api/api.ts b/packages/core/src/modules/shared/api/api.ts index 49a51474442959856684b7272aad49eb26ea70d2..1152eefc0a34409183cd7737450223e01f709ecd 100644 --- a/packages/core/src/modules/shared/api/api.ts +++ b/packages/core/src/modules/shared/api/api.ts @@ -338,8 +338,19 @@ export function loadOrSearch({ filterIds, keyword, names, unitIds, linkId, all, }); } -export function loadOrSearchEmbed(embedId: string) { - return axios.get(urlcat(Url.LOAD_OR_SEARCH_EMBED, { embedId }), { baseURL: nestBaseURL }); +export function loadOrSearchEmbed(embedId: string, { filterIds, keyword, names, unitIds, linkId, all, searchEmail }: ILoadOrSearchArg) { + return axios.get(urlcat(Url.LOAD_OR_SEARCH_EMBED, { embedId }), { + baseURL: nestBaseURL, + params: { + filterIds, + keyword, + names, + unitIds, + linkId, + all, + searchEmail, + }, + }); } /** diff --git a/packages/core/src/modules/space/api/url.appstore.ts b/packages/core/src/modules/space/api/url.appstore.ts index 1e8aa7991760d8edef9b1a761579ac46070bde10..6f253eedeefa149fa6a5b6ad213f4fee1ec9453e 100644 --- a/packages/core/src/modules/space/api/url.appstore.ts +++ b/packages/core/src/modules/space/api/url.appstore.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - /** * Third-party application integration revision */ diff --git a/packages/core/src/modules/space/api/url.node.ts b/packages/core/src/modules/space/api/url.node.ts index bfa22c89728d0e3fff50165bd2d1b3e89f074d94..250458ed93f9bd600e9c485219394f6f47908585 100644 --- a/packages/core/src/modules/space/api/url.node.ts +++ b/packages/core/src/modules/space/api/url.node.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - // =============== Node related ======================= // get the root node export const GET_ROOT_NODE = '/node/root'; diff --git a/packages/core/src/modules/space/api/url.notification.ts b/packages/core/src/modules/space/api/url.notification.ts index 3a7f1545fae9a444d8ed577b3979e026f1aa516b..d8e76437d9874dc562ac2d904337e5320401c99f 100644 --- a/packages/core/src/modules/space/api/url.notification.ts +++ b/packages/core/src/modules/space/api/url.notification.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - // Message Center export const NOTIFICATION_PAGE = '/player/notification/page'; export const NOTIFICATION_LIST = '/player/notification/list'; diff --git a/packages/core/src/modules/space/api/url.space.ts b/packages/core/src/modules/space/api/url.space.ts index 31251345639fd75a0ae1e84df9d1fc40f2d11094..3fc3c5870872866dfb3166841a3a25c253e16257 100644 --- a/packages/core/src/modules/space/api/url.space.ts +++ b/packages/core/src/modules/space/api/url.space.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - // ================ Audit ====================== /** diff --git a/packages/core/src/modules/space/api/url.template.ts b/packages/core/src/modules/space/api/url.template.ts index 628d5d020a7242bd614ed7fbb2f8fc504f590245..8501f4cadfb41216cf6f466de78f95ed4ae59c2a 100644 --- a/packages/core/src/modules/space/api/url.template.ts +++ b/packages/core/src/modules/space/api/url.template.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - // =============== Template related ======================= export const CREATE_TEMPLATE = '/template/create'; export const OFFICIAL_TEMPLATE_CATEGORY = '/template/categoryList'; diff --git a/packages/core/src/modules/space/store/reducers/catalog_tree.ts b/packages/core/src/modules/space/store/reducers/catalog_tree.ts index 42a77a7e2446e931b92dbe62d9cdebb705b03b36..37dd1c4122e69cf7d27cae9932dadc013da4a545 100644 --- a/packages/core/src/modules/space/store/reducers/catalog_tree.ts +++ b/packages/core/src/modules/space/store/reducers/catalog_tree.ts @@ -16,23 +16,20 @@ * along with this program. If not, see . */ +import { ConfigConstant } from 'config'; +import { produce } from 'immer'; +import { collectProperty, findNode, getUniqName } from 'utils'; import { - ISetRootIdAction, ICatalogTree, ITreeNode, ISetNodeNameAction, ISetDelNodeIdAction, - IDeleteNodeAction, IMoveNodeToFolderAction, ICoLayerMoveNodeAction, ISetOptNodeAction, IOptNode, - ISetErrAction, ISetEditNodeIdAction, IAddNodeToMapAction, ITreeNodesMap, ISetIsCopyAllAction, - INode, ISetExpandedKeysActions, IInitCatalogTreeAction, ISetCopyNodeIdAction, ISetAllVisibleAction, - ISetLoadedAction, IUpdateHasChildren, IMoveToAction, IUpdateTreeNodesMapAction, IClearNodeAction, - IUpdateIsPermissionAction, IUpdateSocketDataAction, IRefreshTreeAction, IAddNodeToFavoriteTreeAction, - IRemoveFavoriteNodeAction, IDeleteNodeFromFavoriteTreeAction, IMoveFavoriteNodeAction, IInitFavoriteTreeNodesAction, - ISetActiveNodeErrorAction, IUpdatePermissionModalNodeIdAction, IUpdateShareModalNodeIdAction, IUpdateSaveAsTemplateModalNodeIdAction, - IUpdateImportModalNodeIdAction, ISetTreeRootIdAction, INodesMapItem, ISetLoadedKeysAction, ISetNodeErrorTypeAction, - ISetPermissionModalMessageStatusAction , ISetPermissionCommitRemindParameterAction, ISetNoPermissionMembersAction, - IUpdateMoveToNodeIdsAction, + IAddNodeToFavoriteTreeAction, IAddNodeToMapAction, ICatalogTree, IClearNodeAction, ICoLayerMoveNodeAction, IDeleteNodeAction, + IDeleteNodeFromFavoriteTreeAction, IInitCatalogTreeAction, IInitFavoriteTreeNodesAction, IMoveFavoriteNodeAction, IMoveNodeToFolderAction, + IMoveToAction, INode, INodesMapItem, IOptNode, IRefreshTreeAction, IRemoveFavoriteNodeAction, ISetActiveNodeErrorAction, ISetAllVisibleAction, + ISetCopyNodeIdAction, ISetDelNodeIdAction, ISetEditNodeIdAction, ISetErrAction, ISetExpandedKeysActions, ISetIsCopyAllAction, ISetLoadedAction, + ISetLoadedKeysAction, ISetNodeErrorTypeAction, ISetNodeNameAction, ISetNoPermissionMembersAction, ISetOptNodeAction, + ISetPermissionCommitRemindParameterAction, ISetPermissionModalMessageStatusAction, ISetRootIdAction, ISetTreeRootIdAction, ITreeNode, ITreeNodesMap, + IUpdateHasChildren, IUpdateImportModalNodeIdAction, IUpdateIsPermissionAction, IUpdateMoveToNodeIdsAction, IUpdatePermissionModalNodeIdAction, + IUpdateSaveAsTemplateModalNodeIdAction, IUpdateShareModalNodeIdAction, IUpdateSocketDataAction, IUpdateTreeNodesMapAction, } from '../../../../exports/store/interfaces'; -import { findNode, getUniqName, collectProperty } from 'utils'; import * as actions from '../../../shared/store/action_constants'; -import { produce } from 'immer'; -import { ConfigConstant } from 'config'; const defaultState: ICatalogTree = { /** @@ -269,7 +266,9 @@ export const catalogTree = produce((draftCatalogTree: ICatalogTree = defaultStat } case actions.UPDATE_TREE_NODES_MAP: { const { nodeId, data } = action.payload; - if (!draftCatalogTree.treeNodesMap[nodeId]) { return draftCatalogTree; } + if (!draftCatalogTree.treeNodesMap[nodeId]) { + return draftCatalogTree; + } draftCatalogTree.treeNodesMap[nodeId] = { ...draftCatalogTree.treeNodesMap[nodeId]!, ...data }; return draftCatalogTree; } @@ -356,17 +355,18 @@ export const catalogTree = produce((draftCatalogTree: ICatalogTree = defaultStat default: return draftCatalogTree; } -}); +}, defaultState); /** * delete node * attention, sub nodes may be a favorite(star). * it need to update favorite trees. - * @param tree + * @param tree * @param optNode the nodes info that will deleted */ const deleteNode = (catalogTree: ICatalogTree, optNode: IOptNode) => { - const { treeNodesMap, favoriteTreeNodeIds, delNodeId, editNodeId, favoriteEditNodeId, favoriteDelNodeId, + const { + treeNodesMap, favoriteTreeNodeIds, delNodeId, editNodeId, favoriteEditNodeId, favoriteDelNodeId, permissionModalNodeId, shareModalNodeId, saveAsTemplateModalNodeId, importModalNodeId, expandedKeys, loadedKeys } = catalogTree; // the nodeIDs collection that is operating @@ -412,7 +412,7 @@ const deleteNode = (catalogTree: ICatalogTree, optNode: IOptNode) => { /** * add nodes to treeNodeMap (catalog tree data source) - * + * * @param treeNodesMap map of tree * @param data tree nodes info */ @@ -487,7 +487,9 @@ const addNodeToMap = (catalogTree: ICatalogTree, data: (Omit { const parentNode = treeNodesMap[parentId]; - if (!parentNode) { return; } + if (!parentNode) { + return; + } if (parentNode.children.length && !treeNodesMap[parentId]!.hasChildren) { treeNodesMap[parentId]!.hasChildren = true; return; @@ -515,7 +517,7 @@ const moveTo = (treeNodesMap: ITreeNodesMap, nodeId: string, targetNodeId: strin /** * move in the same level - * + * * @param treeNodes tree nodes * @param treeNodesMap the map of tree nodes * @param nodeId be moved nodes @@ -528,7 +530,9 @@ const sameLevelMove = (treeNodesMap: ITreeNodesMap, nodeId: string, targetNodeId const targetParentNode = treeNodesMap[treeNodesMap[targetNodeId]!.parentId]!; const dragNode = treeNodesMap[nodeId]; - if (!parentNode || !dragNode) { return; } + if (!parentNode || !dragNode) { + return; + } const nextNodeId = parentNode.children[parentNode.children.findIndex(id => id === nodeId) + 1]; // whether affect the preNodeId of the next node of the moved node if (nextNodeId && treeNodesMap[nextNodeId]!.preNodeId === nodeId) { @@ -549,7 +553,9 @@ const sameLevelMove = (treeNodesMap: ITreeNodesMap, nodeId: string, targetNodeId parentNode.children = parentNode.children.reduce((prevNodeIds, id) => { // delete node from original position - if (id === nodeId) { return prevNodeIds; } + if (id === nodeId) { + return prevNodeIds; + } if (id === targetNodeId) { // move the dragged node to the front of the target node pos === -1 && prevNodeIds.push(nodeId) && prevNodeIds.push(id); @@ -576,7 +582,9 @@ const crossLevelMove = (treeNodesMap: ITreeNodesMap, nodeId: string, targetNodeI const targetParentNode = treeNodesMap[targetParentNodeId]; const dragNode = treeNodesMap[nodeId]; - if (!parentNode || !targetParentNode || !dragNode) { return; } + if (!parentNode || !targetParentNode || !dragNode) { + return; + } parentNode.children = parentNode.children.filter(id => id !== nodeId); if (pos === 0) { @@ -635,11 +643,11 @@ export const mergeObj = (oldTree: ITreeNode[], newTree: ITreeNode[], treeNodesMa /** * (newest) - * attention: when you pass in multiple nodes, + * attention: when you pass in multiple nodes, * you need to note that these multiple nodes must be the children of the same parent node - * - * @param tree - * @param newNode + * + * @param tree + * @param newNode */ export const addNodeToTree = (tree: ITreeNode[], newNode: INode | INode[]) => { // single node @@ -652,10 +660,10 @@ export const addNodeToTree = (tree: ITreeNode[], newNode: INode | INode[]) => { /** * a single node to tree - * - * @param tree - * @param newNode - * @returns + * + * @param tree + * @param newNode + * @returns */ export const addSingleNodeToTree = (tree: ITreeNode[], newNode: INode) => { const { nodeId, parentId, preNodeId } = newNode; @@ -676,15 +684,17 @@ export const addSingleNodeToTree = (tree: ITreeNode[], newNode: INode) => { /** * new multi nodes - * - * @param tree - * @param newNodes - * @returns + * + * @param tree + * @param newNodes + * @returns */ export const addMultiNodeToTree = (tree: ITreeNode[], newNodes: INode[]) => { const { parentId } = newNodes[0]!; const parentNode = findNode(tree, parentId); - if (!parentNode) { return; } + if (!parentNode) { + return; + } const formatNodes = newNodes.map(item => ({ nodeId: item.nodeId, children: [] })); parentNode.children = formatNodes; }; @@ -693,7 +703,9 @@ export const moveFavoriteNode = (draftCatalogTree: ICatalogTree, data: { nodeId: const { favoriteTreeNodeIds } = draftCatalogTree; const { nodeId, preNodeId } = data; - if (!favoriteTreeNodeIds.includes(nodeId)) { return; } + if (!favoriteTreeNodeIds.includes(nodeId)) { + return; + } draftCatalogTree.favoriteTreeNodeIds = favoriteTreeNodeIds.reduce((prev, id, index) => { if (index === 0 && !preNodeId) { prev.push(nodeId); @@ -711,7 +723,7 @@ export const moveFavoriteNode = (draftCatalogTree: ICatalogTree, data: { nodeId: /** * cancel favorite star * not only remove nodes from favorite trees, but also update node's next node's info(position) - * + * * @param catalogTree * @param removeNodeId the node that cancel favorite */ diff --git a/packages/core/src/modules/space/store/reducers/labs.ts b/packages/core/src/modules/space/store/reducers/labs.ts index ed53205ea06081a08530d4ad2a2a55de37cf8e7b..e23fe3c411cea9b4b49b1e0dd055b4610aaab808 100644 --- a/packages/core/src/modules/space/store/reducers/labs.ts +++ b/packages/core/src/modules/space/store/reducers/labs.ts @@ -17,8 +17,8 @@ */ import produce from 'immer'; -import * as actions from '../../../shared/store/action_constants'; import { ILabs, ILabsAction } from '../../../../exports/store/interfaces'; +import * as actions from '../../../shared/store/action_constants'; const defaultLabs: ILabs = []; @@ -31,4 +31,4 @@ export const labs = produce((labs: ILabs = defaultLabs, action: ILabsAction) => default: return labs; } -}); +}, defaultLabs); diff --git a/packages/core/src/modules/space/store/reducers/share.ts b/packages/core/src/modules/space/store/reducers/share.ts index 0a05f92f9b06f2605f7287613c12821c31ef2539..30ca8121de4304c2bb25ff97214d38ba891af7ae 100644 --- a/packages/core/src/modules/space/store/reducers/share.ts +++ b/packages/core/src/modules/space/store/reducers/share.ts @@ -17,8 +17,8 @@ */ import produce from 'immer'; -import * as actions from '../../../shared/store/action_constants'; import { IShareInfo, IShareInfoAction } from '../../../../exports/store/interfaces'; +import * as actions from '../../../shared/store/action_constants'; const defaultShareInfo = {}; @@ -34,4 +34,4 @@ export const share = produce((shareInfoDraft: IShareInfo = defaultShareInfo, act default: return shareInfoDraft; } -}); +}, defaultShareInfo); diff --git a/packages/core/src/modules/space/store/reducers/space.ts b/packages/core/src/modules/space/store/reducers/space.ts index 9d867c505941bbfd44cb521291e6d1ecaf2b1ca0..425d3e1e7fa9deb65f02de63c8ce769d3c7b46b5 100644 --- a/packages/core/src/modules/space/store/reducers/space.ts +++ b/packages/core/src/modules/space/store/reducers/space.ts @@ -18,11 +18,10 @@ import { produce } from 'immer'; import { - ISpace, ISpaceListAction, ISetQuitSpaceIdAction, - ISetSpaceLoadingAction, ISetSpaceErrAction, ISetSpaceInfoAction, ISetShortcutKeyPanelVisibleAction, - ISetSpaceReconnectingAction, ISetSpaceConnectedAction, ISetScreenWidth, ISetSideBarVisibleAction, IToggleApiPanelAction, - ISetMarketplaceAppsAction,ISetSpaceFeaturesAction, - ISetPreviewModalVisibleAction, ISetActiveSpaceIdAction, ISetEnvs, IToggleSideRecordPanelAction, IToggleRecordFullScreen + ISetActiveSpaceIdAction, ISetEnvs, ISetMarketplaceAppsAction, ISetPreviewModalVisibleAction, ISetQuitSpaceIdAction, ISetScreenWidth, + ISetShortcutKeyPanelVisibleAction, ISetSideBarVisibleAction, ISetSpaceConnectedAction, ISetSpaceErrAction, ISetSpaceFeaturesAction, + ISetSpaceInfoAction, ISetSpaceLoadingAction, ISetSpaceReconnectingAction, ISpace, ISpaceListAction, IToggleApiPanelAction, IToggleRecordFullScreen, + IToggleSideRecordPanelAction } from '../../../../exports/store/interfaces'; import * as actions from '../../../shared/store/action_constants'; @@ -50,7 +49,7 @@ const defaultSpace = { type SpaceAction = ISpaceListAction | ISetQuitSpaceIdAction | ISetSpaceErrAction | ISetSpaceLoadingAction | ISetSpaceInfoAction | ISetSpaceReconnectingAction | ISetSpaceConnectedAction | ISetScreenWidth | ISetSideBarVisibleAction | ISetShortcutKeyPanelVisibleAction - | IToggleApiPanelAction | IToggleSideRecordPanelAction | ISetMarketplaceAppsAction | ISetPreviewModalVisibleAction | ISetActiveSpaceIdAction | + | IToggleApiPanelAction | IToggleSideRecordPanelAction | ISetMarketplaceAppsAction | ISetPreviewModalVisibleAction | ISetActiveSpaceIdAction | ISetSpaceFeaturesAction | ISetEnvs | IToggleRecordFullScreen; export const space = produce((spaceDraft: ISpace = defaultSpace, action: SpaceAction) => { @@ -133,4 +132,4 @@ export const space = produce((spaceDraft: ISpace = defaultSpace, action: SpaceAc default: return spaceDraft; } -}); +}, defaultSpace); diff --git a/packages/core/src/modules/space/store/reducers/template_centre.ts b/packages/core/src/modules/space/store/reducers/template_centre.ts index 641dd217d1f464bacb8e8da92c8bf65197ab1b01..f60754d22bcf2de08b6685d27998aeb8f2686ff4 100644 --- a/packages/core/src/modules/space/store/reducers/template_centre.ts +++ b/packages/core/src/modules/space/store/reducers/template_centre.ts @@ -40,4 +40,4 @@ export const templateCentre = produce((draftSidebar: ITemplateCentre = defaultTe default: return draftSidebar; } -}); \ No newline at end of file +}, defaultTemplateCentre); diff --git a/packages/core/src/modules/user/api/url.auth.ts b/packages/core/src/modules/user/api/url.auth.ts index 33d6b5ccb414d9b71c69687fd19bb493811e2f19..029d4bcef147bdf2a4dc2f9ef06692675631c018 100644 --- a/packages/core/src/modules/user/api/url.auth.ts +++ b/packages/core/src/modules/user/api/url.auth.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - // login signin, register export const SIGN_IN_OR_SIGN_UP = '/signIn'; diff --git a/packages/core/src/modules/user/api/url.user.ts b/packages/core/src/modules/user/api/url.user.ts index 20db9da85e1f15db0206abfd120a3cc21e5b5902..64a4683458369ddfda7ceb089aa15ba70d78ec9d 100644 --- a/packages/core/src/modules/user/api/url.user.ts +++ b/packages/core/src/modules/user/api/url.user.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - // ================ Account ====================== /** diff --git a/packages/core/src/modules/user/store/reducers/notification.ts b/packages/core/src/modules/user/store/reducers/notification.ts index 80f3c5a3ced14739e676a3a50691b35ffad2661b..da37e889d43422a5ad4e352563065de0544e30e2 100644 --- a/packages/core/src/modules/user/store/reducers/notification.ts +++ b/packages/core/src/modules/user/store/reducers/notification.ts @@ -16,18 +16,13 @@ * along with this program. If not, see . */ +import { produce } from 'immer'; import { - INotification, - IUpdateUnreadMsgCountAction, - IUpdateReadMsgCountAction, - IUpdateReadNoticeListAction, - IUpdateUnReadNoticeListAction, - IDelUnReadNoticeListAction, - IUpdateNewNoticeListFromWsAction, - IGetNewMsgFromWsAndLookAction, + IDelUnReadNoticeListAction, IGetNewMsgFromWsAndLookAction, INotification, IUpdateNewNoticeListFromWsAction, IUpdateReadMsgCountAction, + IUpdateReadNoticeListAction, IUpdateUnreadMsgCountAction, IUpdateUnReadNoticeListAction, } from '../../../../exports/store/interfaces'; import * as actions from '../../../shared/store/action_constants'; -import { produce } from 'immer'; + const defaultState: INotification = { unReadCount: 0, readCount: 0, @@ -99,7 +94,7 @@ export const notification = produce((data: INotification = defaultState, action: data.unReadNoticeList = []; data.unReadCount = 0; return data; - } + } const newUnReadNoticeList = data.unReadNoticeList.filter(item => !action.payload.idList.includes(item.id)); data.unReadNoticeList = newUnReadNoticeList; data.unReadCount = data.unReadCount - action.payload.idList.length; @@ -118,4 +113,4 @@ export const notification = produce((data: INotification = defaultState, action: default: return data; } -}); +}, defaultState); diff --git a/packages/core/src/modules/user/store/reducers/user.ts b/packages/core/src/modules/user/store/reducers/user.ts index 169d9d8b46b6da03951e1905d181a688bb68adcd..6a83997fbf3a53fb1c42633c138256dbde9a9ff5 100644 --- a/packages/core/src/modules/user/store/reducers/user.ts +++ b/packages/core/src/modules/user/store/reducers/user.ts @@ -16,16 +16,14 @@ * along with this program. If not, see . */ -import { - IUser, ISetUserMeActions, ISetIsLoginActions, - ISetLoginErrAction, ISetRegisterAction, ISetLoadingAction, ISetUserAvatarColorAction, - ISignOutAction, ISetUserAvatarAction, ISetReqStatusAction, ISetHttpErrInfoAction, - ISetNicknameAction, IUpdateUserInfoAction, IUpdateUserInfoErrAction, - IAddWizardNumberAction, ISetActiveRecordId, ISetUsedInviteReward -} from '../../../../exports/store/interfaces'; +import axios from 'axios'; import { produce } from 'immer'; import { ActionConstants } from '../../../../exports/store'; -import axios from 'axios'; +import { + IAddWizardNumberAction, ISetActiveRecordId, ISetHttpErrInfoAction, ISetIsLoginActions, ISetLoadingAction, ISetLoginErrAction, ISetNicknameAction, + ISetRegisterAction, ISetReqStatusAction, ISetUsedInviteReward, ISetUserAvatarAction, ISetUserAvatarColorAction, ISetUserMeActions, ISignOutAction, + IUpdateUserInfoAction, IUpdateUserInfoErrAction, IUser +} from '../../../../exports/store/interfaces'; type UserActions = ISetUserMeActions | ISetIsLoginActions | ISetLoginErrAction | ISetRegisterAction | ISetLoadingAction | ISetUserAvatarColorAction | @@ -57,7 +55,7 @@ if (userInfo) { axios.defaults.headers.common['X-Space-Id'] = userInfo.spaceId; } const defaultValue: IUser = { - info: { ...userInfo, avatarColor: null }, + info: userInfo, isLogin: Boolean(userInfo), isRegister: false, isCreateSpace: false, @@ -148,4 +146,4 @@ export const user = produce((userDraft: IUser = defaultValue, action: UserAction default: return userDraft; } -}); +}, defaultValue); diff --git a/packages/core/src/modules/widget/api/url.widget.ts b/packages/core/src/modules/widget/api/url.widget.ts index b51bf65aba1c1eb7cd63b4e4f5cc43726eddc574..f1740eacd0015577010b2b6c4c742db8e007e059 100644 --- a/packages/core/src/modules/widget/api/url.widget.ts +++ b/packages/core/src/modules/widget/api/url.widget.ts @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - // ================ Widget related ======================= export const INSTALLATION_WIDGETS = '/widget/get'; export const WIDGET_CENTER_LIST = '/widget/package/store/list'; diff --git a/packages/core/src/utils/file.ts b/packages/core/src/utils/file.ts index 8e5d13088492e265f61357b81b557eaf518ba300..756051b5559d088aadad29819f5bb9dfa5fb5710 100644 --- a/packages/core/src/utils/file.ts +++ b/packages/core/src/utils/file.ts @@ -127,16 +127,16 @@ export const getHostOfAttachment = (bucket: string) => { if (bucket.toUpperCase() === 'QNY1') { const QNY1 = window.__initialization_data__?.envVars.QNY1; - return QNY1.includes('https') ? QNY1 : urlcat(origin, QNY1 + ''); + return QNY1.includes('http') ? QNY1 : urlcat(origin, QNY1 + ''); } if (bucket.toUpperCase() === 'QNY2') { const QNY2 = window.__initialization_data__?.envVars.QNY2; - return QNY2.includes('https') ? QNY2 : urlcat(origin, QNY2 + ''); + return QNY2.includes('http') ? QNY2 : urlcat(origin, QNY2 + ''); } const QNY3 = window.__initialization_data__?.envVars.QNY3; - return QNY3.includes('https') ? QNY3 : urlcat(origin, QNY3 + ''); + return QNY3.includes('http') ? QNY3 : urlcat(origin, QNY3 + ''); }; export function cellValueToImageSrc( diff --git a/packages/cypress/cypress.json b/packages/cypress/cypress.json index 09f57cf682a381e6ea34ec3b15470da81fbb2ee0..57adc46e1450d15d72339450715ba582d8893c38 100644 --- a/packages/cypress/cypress.json +++ b/packages/cypress/cypress.json @@ -1,5 +1,5 @@ { - "baseUrl": "https://integration.vika.ltd/", + "baseUrl": "http://localhost:3000", "viewportWidth": 1920, "viewportHeight": 920, "projectId": "efk8au", diff --git a/packages/cypress/img.png b/packages/cypress/img.png index b99b744954f71e8811f95fca05e132c7c2747922..ae1593a2ebe2cd470cc627a123756bd5742cab0f 100644 Binary files a/packages/cypress/img.png and b/packages/cypress/img.png differ diff --git a/packages/cypress/img_2.png b/packages/cypress/img_2.png index 920134bb022950f0608a91c343ab5c41aac5a559..e8a6925006b22dc7d6ab94e59170338ab9e8052f 100644 Binary files a/packages/cypress/img_2.png and b/packages/cypress/img_2.png differ diff --git a/packages/cypress/package.json b/packages/cypress/package.json index 13073c24fbc4236942672b50d23e808eb7501f00..3fa14fef8a8e725561b84b2271068ba1f3c4b084 100644 --- a/packages/cypress/package.json +++ b/packages/cypress/package.json @@ -1,8 +1,8 @@ { "name": "@apitable/cypress", - "version": "0.16.0", + "version": "0.17.0", "description": "> TODO: description", - "author": "APITable PTE. LTD.", + "author": "APITable Ltd. ", "license": "AGPL-3.0", "homepage": "", "main": "index.js", diff --git a/packages/datasheet/.env b/packages/datasheet/.env index c7e5b22f5d3da90486564a18255af7ad0fe01fc1..3d4c961f0dc4773422d2db2ea262f77c6a703111 100644 --- a/packages/datasheet/.env +++ b/packages/datasheet/.env @@ -15,11 +15,8 @@ NEXT_ASSET_PREFIX= NEXT_PUBLIC_ASSET_PREFIX= # When running ci, the ci script appends the version number to the environment variable =>「WEB_CLIENT_VERSION」 - ERROR_PAGE_CUSTOMER_SERVICE_QRCODE_VISIBLE=false CUSTOM_WIDGET_VISIBLE= -LOGIN_INTRODUCTION_VIDEO_VISIBLE=true -LOGIN_DEFAULT_VERIFY_TYPE=identify_code_login SPACE_UPGRADE_PAGE_VISIBLE= FIELD_CASCADE_VISIBLE= SYSTEM_CONFIGURATION_DEFAULT_THEME=dark @@ -33,13 +30,7 @@ HELP_MENU_CONTACT_US_TYPE=qrcode HELP_MENU_DOWNLOAD_APP_VISIBLE= HELP_MENU_JOIN_CHATGROUP_URL= HELP_MENU_OFFICIAL_WEBSITE_URL=https://apitable.com -INTEGRATIONS_FEISHU_VISIBLE= -INTEGRATIONS_DINGTALK_VISIBLE= -INTEGRATIONS_WECOM_VISIBLE= -INTEGRATIONS_YOZOSOFT_VISIBLE= ACCOUNT_WALLET_HELP_URL= -ASSISTANT_ACTIVITY_TRAIN_CAMP_END_TIME=2022-08-31 23:59 -LOGIN_AGREE_TERMS_OF_SERVICE_WIZARD_ID=75 APIFOX_HOME_URL= APIFOX_DEBUG_PATCH_URL= APIFOX_DEBUG_POST_URL= @@ -47,36 +38,23 @@ APIFOX_DEBUG_DELETE_URL= APIFOX_DEBUG_GET_URL= APIFOX_DEBUG_UPLOAD_URL= CALENDAR_SETTING_HELP_URL=https://help.apitable.com/docs/guide/manual-calendar-view -JOIN_CHATGROUP_PAGE_URL=https://vika.cn/chatgroup/ WIDGET_CREATE_WIDGET_HELP_URL=/developers/widget/introduction -ONBOARDING_CUSTOMER_SERVICE_BACKGROUND_IMG=https://s1.vika.cn/space/2022/02/28/20566ca6516d477c913f5ca79e554c68 -ONBOARDING_CUSTOMER_SERVICE_QRCODE_AVATAR_IMG=https://s1.vika.cn/space/2022/02/25/1730b0599510406fb7ce1b7c9ed78393 WIDGET_DEFAULT_TEMPLATE_URL=/develop/widget/wpk2jJ7qZS0VG/0.0.11/61433a73e4b0ed4bd1e2e8e1.zip -HELP_MENU_DEVELOPERS_CENTER_URL= -BILLING_ENTERPRISE_CONTACT_US_QRCODE_IMG=space/2022/02/16/cf2f386d300142a19268c487b351d6bb -SYSTEM_CONFIGURATION_ERROR_MSG_QRCODE_IMG={ "website": "/space/2022/04/26/51352131c1d74332b8ab8a57483b0f3f?attname=66_8c5b355964c515a9d2ce55bc423e5a18.png", "wecom": "/space/2022/04/26/2c706fa40e3442d8b643a30ee22f5572?attname=66_8d7dce3b6ff28bc245a2db34bf4ab55a.png", "dingtalk": "/space/2022/05/17/9bb75b8710e040be9fb99d7b28a6ad0e?attname=125_f2112b2006e4b42cc4b0e7a320f87da7.png", "feishu": "https://u.vika.cn/z9ygm" } +HELP_MENU_DEVELOPERS_CENTER_URL=https://developers.apitable.com/api/introduction/ GANTT_CONFIG_COLOR_HELP_URL= GANTT_SET_TASK_RELATION_HELP_URL=https://help.apitable.com/docs/guide/manual-gantt-view/#%E8%AE%BE%E7%BD%AE%E4%BB%BB%E5%8A%A1%E4%BE%9D%E8%B5%96%E5%85%B3%E7%B3%BB -API_PANEL_HELP_URL= -LOGIN_INTRODUCTION_VIDEO= -ACCOUNT_DELELTE_HELP_URL=https://help.apitable.com/docs/guide/faq-cancel-account -PRIVACY_POLICY_URL=/privacy-policy -PRIVACY_POLICY_STRING=维格隐私政策 -SERVICE_AGREEMENT_URL=/service-agreement -SERVICE_AGREEMENT_STRING=维格服务协议 +API_PANEL_HELP_URL=https://developers.apitable.com/api/introduction/ +ACCOUNT_DELELTE_HELP_URL=https://help.vika.cn/docs/guide/faq-cancel-account SYSTEM_CONFIGURATION_OFFICIAL_AVATAR=space/2021/12/07/aaac193704834e9a9e4af27a1535826a ARCHITECTURE_SETTING_HELP_URL=https://help.apitable.com/docs/guide/manual-architecture-view -BILLING_PAYMENT_PAGE_CONTACT_US_IMG=space/2022/02/17/b32ecd3a5dcb43a1add4d320632c6d2a -BILLING_PAY_SUCCESS_QRCODE_IMG=space/2022/02/17/b7cd077fca35444aa02fba4d11f2c1ba HELP_MENU_PRODUCT_ROADMAP_URL= QNY1=/assets/ QNY2=/assets/ QNY3=/assets/ RECORD_ACTIVITY_HELP_URL=https://help.apitable.com/docs/guide/records-comments -ASSISTANT_RELEASE_CHANGELOGS_PAGE_URL=https://bbs.vika.cn/column/details/13 -SPACE_ROLE_HELP_URL=https://help.vika.cn/docs/guide/manual/use-space/role/%20readme +SPACE_ROLE_HELP_URL=https://help.apitable.com/docs/guide/role SYSTEM_CONFIGURATION_SERVER_ERROR_BG_IMG=/space/2022/09/07/cbaf2ee93be24f6bbe361a85db0efba7?attname=theserverisundermaintenance.%402x.png -WORKBENCH_NODE_SHARE_HELP_URL=https://help.vika.cn/docs/guide/manual-share +WORKBENCH_NODE_SHARE_HELP_URL=https://help.apitable.com/docs/guide/manual-share HELP_MENU_SOLUTION_URL= SPACE_ENTERPRISE_CERTIFICATION_FORM_URL= HELP_MENU_SUBSCRIBE_DEMONSTRATE_FORM_URL= @@ -86,7 +64,7 @@ TRASH_HELP_URL=https://help.apitable.com/docs/guide/manual-trash/ USER_FEEDBACK_FORM_URL=https://github.com/apitable/apitable/issues HELP_MENU_VIDEO_TUTORIALS_URL= HELP_MENU_USER_COMMUNITY_URL=https://discord.gg/2UXAbDTJTX -SYSTEM_CONFIGURATION_OFFICIAL_LOGO=/space/2022/08/30/f973ad66584a482a846c0139845bdf91 +SYSTEM_CONFIGURATION_OFFICIAL_LOGO=space/2022/12/06/55dad383ee6e4c2db87790c6da3b2eed WIDGET_CENTER_HELP_URL=https://help.apitable.com/docs/guide/intro-widget-center/ WIDGET_HOW_TO_CLOSE_BROWSER_RESTRICTION_HELP_URL=/developers/widget/quick-start# WIDGET_DEVELOP_INIT_HELP_URL=/developers/widget/quick-start# @@ -112,7 +90,7 @@ SECURITY_CONTACTS_ISOLATION_VISIBLE= SECURITY_SHOW_MEMBER_PHONE_NUMBER_VISIBLE= SPACE_LOGS_VISIBLE= WELCOME_CONFIG={ "welcome_sub_title2": { "moduleTitleKey": "welcome_sub_title2", "moreOperation": { "textKay": "more_template", "linkUrl": "/template" }, "cards": [ { "id": "welcome_module_4", "textKey": "welcome_module4", "img": "/space/2022/11/24/6257eda8f55d4409a92f3a6c9f9a6683", "color": "teal[500]", "backgroundColor": "teal[50]", "activeBackgroundColor": "teal[100]", "linkUrl": "/template/tpcK7L3Xc3XS2/tpls68R8XJQXH", "linkNewTab": "false", "video": "" }, { "id": "welcome_module_5", "textKey": "welcome_module5", "img": "/space/2022/11/24/5997969af4fc42c4939227f051121ec2", "color": "tangerine[500]", "backgroundColor": "tangerine[50]", "activeBackgroundColor": "tangerine[100]", "linkUrl": "/template/tpc5Bu6yPfpH8/tplED3FVnX8U6", "linkNewTab": "false", "video": "" }, { "id": "welcome_module_6", "textKey": "welcome_module6", "img": "/space/2022/11/24/525187bca6eb4dcda1f6b26ba8dca345", "color": "blue[500]", "backgroundColor": "blue[50]", "activeBackgroundColor": "blue[100]", "linkUrl": "/template/tpcr05vzH017k/tplr0y8Z82JCy", "linkNewTab": "false", "video": "" } ] } } -WIDGET_REPO_PREFIX=vikadata +WIDGET_REPO_PREFIX=apitable SHARE_PUBLIC_LINK_QRCODE_ASSISTANT_VISIBLE= TIME_MACHINE_VISIBLE= SYSTEM_CONFIGURATION_DEFAULT_LANGUAGE=en_US @@ -128,16 +106,21 @@ CRASH_PAGE_CONTACT_US_URL=https://apitable.com CRASH_PAGE_REPORT_ISSUES_URL=https://github.com/apitable/apitable/issues GAIN_ATTACHMENT_CAPACITY_VISIBLE= ACCOUNT_LOGOUT_VISIBLE=true -LOGIN_DEFAULT_ACCOUNT_TYPE= HELP_MENU_CONTACT_US_URL=https://apitable.com -ACCOUNT_RESET_PASSWORD_VISIBLE=true +ACCOUNT_RESET_PASSWORD_VISIBLE= ACCOUNT_WALLET_VISIBLE=true -SPACE_INTEGRATION_PAGE_VISIBLE=true +SPACE_INTEGRATION_PAGE_VISIBLE= CONTACTS_MODAL_INVITE_VIA_EMAIL_VISIBLE=true -CONTACTS_MODAL_BULK_IMPORT_VISIBLE=true -LOGIN_OFFICIAL_WEBSITE_URL=https://vika.cn?home=1 +CONTACTS_MODAL_BULK_IMPORT_VISIBLE= CANNOT_FIND_FILE_ERROR_PAGE_IMG= -EMBED_ERROR_PAGE_HELP_URL=https://help.vika.cn +EMBED_ERROR_PAGE_HELP_URL=https://help.apitable.com +USER_BIND_PHONE_VISIBLE= +DELETE_ACCOUNT_VISIBLE= +CHANGE_SPACE_ADMIN_VISIBLE= +DELETE_SPACE_VISIBLE= +REGENERATE_API_TOKEN_VISIBLE= +INVITATION_CODE_VISIBLE= +MAXIMUM_VIEW_COUNT_PER_DATASHEET=60 SENTRY_CONFIG_URL=https://sentry.apitable.com SENTRY_CONFIG_DSN=https://d486e5409b1d4439ad0f250598cde620@sentry.apitable.ltd/3 SENTRY_CONFIG_AUTH_TOKEN=8064f23ee6414768883ca149622f3941b2d8a043fc9846cb8d7a4af26feb2879 @@ -147,4 +130,3 @@ LOGO=/space/2022/09/20/3f570238c3aa4aed865e97c27233d3d4 LOGO_TEXT_LIGHT=/space/2022/12/20/ee9a513e72f6412bba7a1b24b085e1e1 LOGO_TEXT_DARK=/space/2022/09/23/8ca6c99660a24f999404b68008fba72d LOGIN_LOGO=/space/2022/12/23/c64a745ae147495b9d1ae96d822ae891 - diff --git a/packages/datasheet/.env.local b/packages/datasheet/.env.local index 11be5b8346afa6278b361883a8dfb75a183a713b..b28b04f643122b019e912540f228c8ed20be9eeb 100644 --- a/packages/datasheet/.env.local +++ b/packages/datasheet/.env.local @@ -1,5 +1,3 @@ -API_PROXY=https://integration.vika.ltd - diff --git a/packages/datasheet/.eslintrc b/packages/datasheet/.eslintrc index f7aacf9da771883424f12efd9ce82b615e9bfa55..a66c2bbcfd52e185a4a6337f123571b45dd1f4ff 100644 --- a/packages/datasheet/.eslintrc +++ b/packages/datasheet/.eslintrc @@ -6,6 +6,7 @@ ], "rules": { "react/display-name": 0, - "react/no-find-dom-node": 0 + "react/no-find-dom-node": 0, + "@next/next/no-html-link-for-pages": ["error", "packages/datasheet/pages/"] } } diff --git a/packages/datasheet/next.config.js b/packages/datasheet/next.config.js index f7a521a52d3c5044664620ed1190679f29d8fcb4..ba5835207592bd956a89990f859d5789f263675b 100644 --- a/packages/datasheet/next.config.js +++ b/packages/datasheet/next.config.js @@ -17,15 +17,15 @@ */ /** @type {import('next').NextConfig} */ -/*eslint no-undef: 0*/ -const withLess = require('next-with-less'); -const path = require('path'); -const loaderUtils = require('loader-utils'); -const withPlugins = require('next-compose-plugins'); -const withTM = require('next-transpile-modules'); -const withBundleAnalyzer = require('@next/bundle-analyzer'); -const isProd = process.env.NODE_ENV === 'production'; -const {withSentryConfig} = require("@sentry/nextjs"); +/* eslint no-undef: 0 */ +const withLess = require('next-with-less') +const path = require('path') +const loaderUtils = require('loader-utils') +const withPlugins = require('next-compose-plugins') +const withTM = require('next-transpile-modules') +const withBundleAnalyzer = require('@next/bundle-analyzer') +const isProd = process.env.NODE_ENV === 'production' +const { withSentryConfig } = require('@sentry/nextjs') const isIntranetEnv = process.env.BUILD_VERSION?.includes('test') || process.env.BUILD_VERSION?.includes('op_') @@ -40,8 +40,8 @@ const regexEqual = (x, y) => { x.global === y.global && x.ignoreCase === y.ignoreCase && x.multiline === y.multiline - ); -}; + ) +} /** * Generate context-aware class names when developing locally @@ -60,191 +60,213 @@ const localIdent = (loaderContext, localIdentName, localName, options) => { // "they cannot start with a digit, two hyphens, or a hyphen followed by a digit [sic]" // https://www.w3.org/TR/CSS21/syndata.html#characters .replace(/^(\d|--|-\d)/, '__$1') - ); -}; + ) +} // Overrides for css-loader plugin -function cssLoaderOptions(modules) { - const {getLocalIdent, ...others} = modules; +function cssLoaderOptions (modules) { + const { getLocalIdent, ...others } = modules return { ...others, getLocalIdent: getLocalIdent || localIdent, exportLocalsConvention: 'camelCaseOnly', - mode: 'local', - }; + mode: 'local' + } } const plugins = [ - [withLess, {lessLoaderOptions: {lessOptions: {paths: [path.resolve(__dirname, './src')]}}}], - [withTM(['@apitable/components', 'antd', 'rc-pagination', 'rc-util', 'rc-picker', 'rc-notification', '@ant-design/icons', 'rc-calendar'])], - [withBundleAnalyzer({enabled: process.env.ANALYZE === 'true',})], -]; + [ + withLess, { + lessLoaderOptions: { + lessOptions: { + paths: [path.resolve(__dirname, './src')] + } + } + } + ], + [ + withTM(['@apitable/components', 'antd', 'rc-pagination', 'rc-util', 'rc-picker', 'rc-notification', '@ant-design/icons', 'rc-calendar']) + ], + [ + withBundleAnalyzer({ enabled: process.env.ANALYZE === 'true' }) + ] +] // use local public folder for editions, e.g. apitable const getStaticFolder = () => { - if (process.env.USE_CUSTOM_PUBLIC_FILES === 'true') return ''; + if (process.env.USE_CUSTOM_PUBLIC_FILES === 'true') return '' - return isProd ? process.env.NEXT_PUBLIC_ASSET_PREFIX : ''; -}; - -const _withSentryConfig = isProd && !isIntranetEnv ? withSentryConfig : (nextConfig, sentryConfig) => { - return nextConfig + return isProd ? process.env.NEXT_PUBLIC_ASSET_PREFIX : '' } +const _withSentryConfig = isProd && !isIntranetEnv + ? withSentryConfig + : (nextConfig, sentryConfig) => { + return nextConfig + } + module.exports = withPlugins(plugins, _withSentryConfig({ // Use the CDN in production and localhost for development. assetPrefix: isProd ? process.env.NEXT_ASSET_PREFIX : '', images: { - domains: ['s4.vika.cn', 's1.vika.cn', 'mp.weixin.qq.com', 'localhost'], + domains: ['s4.vika.cn', 's1.vika.cn', 'mp.weixin.qq.com', 'localhost', 'legacy-s1.apitable.com'], remotePatterns: [{ protocol: 'http', hostname: '**', - pathname: '/vk-assets-ltd/**', + pathname: '/vk-assets-ltd/**' + }, { + protocol: 'https', + hostname: '**', + pathname: '/vk-assets-ltd/**' + }, { + protocol: 'http', + hostname: '**', + pathname: '/assets/**' }, { protocol: 'https', hostname: '**', - pathname: '/vk-assets-ltd/**', + pathname: '/assets/**' }] }, poweredByHeader: false, publicRuntimeConfig: { - staticFolder: getStaticFolder(), + staticFolder: getStaticFolder() }, - webpack(config, options) { + webpack (config, options) { config.resolve.symlinks = false - const originalEntry = config.entry; + const originalEntry = config.entry config.entry = async () => { /** * In ie11, there will be a shadow error. According to the discussion in the issue, the following polyfill can be used to solve it */ - const entries = await originalEntry(); + const entries = await originalEntry() - const mainJs = entries['main.js']; + const mainJs = entries['main.js'] if (mainJs && !mainJs.includes('./utils/polyfills.js')) { - mainJs.unshift('./utils/polyfills.js'); + mainJs.unshift('./utils/polyfills.js') } - return entries; - }; + return entries + } - config.resolve.alias['react'] = path.resolve(__dirname, '../../', 'node_modules', 'react'); - config.resolve.alias['react-dom'] = path.resolve(__dirname, '../../', 'node_modules', 'react-dom'); + config.resolve.alias.react = path.resolve(__dirname, '../../', 'node_modules', 'react') + config.resolve.alias['react-dom'] = path.resolve(__dirname, '../../', 'node_modules', 'react-dom') config.resolve.alias = { ...config.resolve.alias, pc: path.resolve(__dirname, './src/pc'), static: path.resolve(__dirname, './public/static'), enterprise: process.env.IS_ENTERPRISE === 'true' ? path.resolve(__dirname, './src/modules/enterprise') : path.resolve(__dirname, './src/noop') - }; - const oneOf = config.module.rules.find((rule) => typeof rule.oneOf === 'object'); + } + const oneOf = config.module.rules.find((rule) => typeof rule.oneOf === 'object') if (oneOf) { // Find the module which targets *.scss|*.sass files const moduleSassRule = oneOf.oneOf.find((rule) => { - return regexEqual(rule.test, /\.module\.less$/); - }); + return regexEqual(rule.test, /\.module\.less$/) + }) if (moduleSassRule) { // Get the config object for css-loader plugin - const cssLoader = moduleSassRule.use.find(({loader}) => loader.includes('css-loader')); - const lessLoader = moduleSassRule.use.find(({loader}) => loader.includes('less-loader')); + const cssLoader = moduleSassRule.use.find(({ loader }) => loader.includes('css-loader')) + const lessLoader = moduleSassRule.use.find(({ loader }) => loader.includes('less-loader')) if (cssLoader) { cssLoader.options = { ...cssLoader.options, - modules: cssLoaderOptions(cssLoader.options.modules), - }; + modules: cssLoaderOptions(cssLoader.options.modules) + } } if (lessLoader) { lessLoader.options = { - ...lessLoader.options, + ...lessLoader.options // paths: [path.resolve(__dirname, './src/pc')], - }; + } } } } - // patchWebpackConfig(config, options); + // patchWebpackConfig(config, options) - const fallback = config.resolve.fallback || {}; + const fallback = config.resolve.fallback || {} Object.assign(fallback, { path: require.resolve('https-browserify'), zlib: require.resolve('browserify-zlib'), http: require.resolve('stream-http'), stream: require.resolve('stream-browserify'), url: require.resolve('url/'), - util: require.resolve('util/'), - }); - config.resolve.fallback = fallback; + util: require.resolve('util/') + }) + config.resolve.fallback = fallback // if (options.isServer) { // config.externals = webpackNodeExternals({ // Uses list to add this modules for server bundle and process. // allowlist: [/design-system/], - // }); + // }) } - const {webpack} = options; + const { webpack } = options config.plugins.push( new webpack.IgnorePlugin({ resourceRegExp: /canvas|jsdom/, contextRegExp: /konva/ }), new webpack.NormalModuleReplacementPlugin(/node:/, (resource) => { - const mod = resource.request.replace(/^node:/, ''); + const mod = resource.request.replace(/^node:/, '') switch (mod) { case 'path': - resource.request = 'path-browserify'; - break; + resource.request = 'path-browserify' + break default: - throw new Error(`Not found ${mod}`); + throw new Error(`Not found ${mod}`) } - })); + })) config.module.rules.push({ test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, use: [ { - loader: 'babel-loader', + loader: 'babel-loader' }, { loader: '@svgr/webpack', options: { babel: false, - icon: true, - }, + icon: true + } }, { loader: 'svgo-loader', options: { plugins: [ - {name: 'removeNonInheritableGroupAttrs'}, - {name: 'removeXMLNS'}, - {name: 'collapseGroups'}, - {name: 'removeStyleElement'}, - {name: 'removeAttrs', params: {attrs: '(stroke|fill)'}}, - ], + { name: 'removeNonInheritableGroupAttrs' }, + { name: 'removeXMLNS' }, + { name: 'collapseGroups' }, + { name: 'removeStyleElement' }, + { name: 'removeAttrs', params: { attrs: '(stroke|fill)' } } + ] } - }, - ], - }); + } + ] + }) if (!isProd) { - const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); + const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin') config.plugins.push(new ForkTsCheckerWebpackPlugin({ typescript: { memoryLimit: 5000, mode: 'write-references' } - })); + })) } - return config; + return config }, distDir: 'web_build', output: 'standalone', experimental: { esmExternals: true, // this includes files from the monorepo base two directories up - outputFileTracingRoot: path.join(__dirname, '../../'), + outputFileTracingRoot: path.join(__dirname, '../../') }, sentry: { disableServerWebpackPlugin: true, @@ -257,4 +279,4 @@ module.exports = withPlugins(plugins, _withSentryConfig({ dsn: process.env.SENTRY_CONFIG_DSN, org: 'sentry', release: process.env.BUILD_VERSION -})); +})) diff --git a/packages/datasheet/package.json b/packages/datasheet/package.json index fcfa21a6cc65892c811430cf40478c0a0d74a738..3b4e945a117dc74c62cbfa1179c71007de270d1a 100644 --- a/packages/datasheet/package.json +++ b/packages/datasheet/package.json @@ -1,7 +1,7 @@ { "name": "@apitable/datasheet", - "version": "0.16.0", - "author": "APITable PTE. LTD.", + "version": "0.17.0", + "author": "APITable Ltd. ", "license": "AGPL-3.0", "scripts": { "next:dev": "next dev", @@ -34,34 +34,18 @@ "@sentry/nextjs": "^7.18.0", "@sentry/tracing": "^7.18.0", "@testing-library/jest-dom": "^5.11.10", - "@types/classnames": "^2.2.10", - "@types/dom-to-image": "^2.6.2", - "@types/dot-object": "^2.1.2", - "@types/exceljs": "^1.3.0", - "@types/lodash": "4.14.161", - "@types/numeral": "^0.0.28", - "@types/prismjs": "^1.16.1", - "@types/qrcode": "^1.3.5", - "@types/quill": "1.3.6", - "@types/react": "^17.0.2", - "@types/react-dom": "17.0.11", - "@types/react-dropzone": "^5.1.0", - "@types/react-grid-layout": "^1.1.1", - "@types/react-redux": "^7.1.24", - "@types/react-virtualized-auto-sizer": "1.0.0", - "@types/react-window": "1.8.1", - "@types/socket.io-client": "^1.4.36", "ahooks": "^3.5.0", "antd": "4.23.5", "antd-mobile": "5.24.0", "attr-accept": "^2.1.0", - "axios": "0.19.0", + "axios": "0.21.2", "bowser": "^2.11.0", "canvas": "2.9.1", "classnames": "2.2.6", "comlink": "^4.3.1", "cookies-next": "^2.1.1", - "dayjs": "^1.9.6", + "current-device": "^0.10.2", + "dayjs": "^1.11.7", "dingtalk-design-libs": "^0.0.20", "dingtalk-jsapi": "^2.13.71", "dom-to-image": "^2.6.0", @@ -82,18 +66,18 @@ "history": "^5.3.0", "hoist-non-react-statics": "^3.3.2", "html-react-parser": "^0.10.2", - "immer": "7.0.9", + "immer": "9.0.16", "intersection-observer": "^0.12.0", "is-hotkey": "^0.1.6", "is-url": "^1.2.4", - "jszip": "^3.6.0", + "jszip": "^3.7.0", "konva": "^8.3.13", "less-loader": "11.0.0", - "loader-utils": "^3.2.0", - "lodash": "4.17.20", + "loader-utils": "^3.2.1", + "lodash": "4.17.21", "lottie-web": "^5.6.10", "lru-cache": "^6.0.0", - "markdown-it": "^11.0.1", + "markdown-it": "^12.3.2", "md5js": "^1.0.7", "mime-types": "^2.1.29", "naming-style": "^1.0.1", @@ -105,7 +89,7 @@ "path-browserify": "^1.0.1", "pingpp-js": "^2.2.24", "polyfill-object.fromentries": "^1.0.1", - "prismjs": "^1.22.0", + "prismjs": "^1.27.0", "qr-image": "^3.2.0", "qrcode": "^1.4.4", "query-string": "^7.1.1", @@ -170,19 +154,36 @@ "devDependencies": { "@next/bundle-analyzer": "^12.2.2", "@svgr/webpack": "^6.2.1", + "@types/classnames": "^2.2.10", "@types/dagre": "^0", + "@types/dom-to-image": "^2.6.2", + "@types/dot-object": "^2.1.2", "@types/element-closest": "^3", + "@types/exceljs": "^1.3.0", "@types/express": "^4", "@types/file-saver": "^2.0.3", "@types/hbs": "^4", "@types/jest": "24.0.18", + "@types/lodash": "4.14.161", "@types/node": "12.7.3", + "@types/numeral": "^0.0.28", + "@types/prismjs": "^1.16.1", "@types/qr-image": "^3", + "@types/qrcode": "^1.3.5", + "@types/quill": "1.3.6", + "@types/react": "^17.0.2", "@types/react-beautiful-dnd": "^13.1.2", + "@types/react-dom": "17.0.11", + "@types/react-dropzone": "^5.1.0", + "@types/react-grid-layout": "^1.1.1", "@types/react-pdf": "^5", + "@types/react-redux": "^7.1.24", "@types/react-transition-group": "^4.4.4", + "@types/react-virtualized-auto-sizer": "1.0.0", + "@types/react-window": "1.8.1", "@types/semver": "^7", "@types/sharp": "^0", + "@types/socket.io-client": "^1.4.36", "@types/worker-plugin": "^5", "@webcomponents/shadydom": "^1.9.0", "babel-jest": "^26.6.0", diff --git a/packages/datasheet/pages/_app.tsx b/packages/datasheet/pages/_app.tsx index 2c40999cd5f26b590dffbdec996f94e9f6c2b775..790e8362bab24c74c3b7ee4762ebcb9197ff54cd 100644 --- a/packages/datasheet/pages/_app.tsx +++ b/packages/datasheet/pages/_app.tsx @@ -99,7 +99,7 @@ enum LoadingStatus { Complete } -function MyApp(props: AppProps) { +function MyApp(props: AppProps & { envVars: string }) { const router = useRouter(); const isWidget = router.asPath.includes('widget-stage'); if (isWidget) { @@ -108,8 +108,9 @@ function MyApp(props: AppProps) { return MyAppMain(props); } -function MyAppMain({ Component, pageProps }: AppProps) { +function MyAppMain({ Component, pageProps, envVars }: AppProps & { envVars: string }) { const router = useRouter(); + const env = JSON.parse(envVars); const [loading, setLoading] = useState(() => { if (router.asPath.includes('widget-stage')) { return LoadingStatus.Complete; @@ -168,7 +169,7 @@ function MyAppMain({ Component, pageProps }: AppProps) { router.events.off('routeChangeStart', handleStart); router.events.off('routeChangeComplete', handleComplete); }; - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line }, [loading]); useEffect(() => { @@ -389,12 +390,25 @@ function MyAppMain({ Component, pageProps }: AppProps) { { ((loading !== LoadingStatus.Complete) || userLoading) &&
    logo - vika + logo_text_dark
    } } - + { + env.GOOGLE_ANALYTICS_ID && + <> + + + } ; } diff --git a/packages/datasheet/pages/_document.tsx b/packages/datasheet/pages/_document.tsx index e1b734a8f38ab19148d211ccc8dfa438b8268a05..ac2aeb3186da726d91594d43e5721d458aa9da6c 100644 --- a/packages/datasheet/pages/_document.tsx +++ b/packages/datasheet/pages/_document.tsx @@ -51,6 +51,16 @@ class MyDocument extends Document {