This commit is contained in:
Lark-Base
2024-07-10 12:55:22 +00:00
parent 995face0e9
commit 7d437178b2
18 changed files with 2826 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
/dist
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

31
.replit Normal file
View File

@ -0,0 +1,31 @@
run = "npm run dev"
entrypoint = "src/App.jsx"
hidden = [".config", "tsconfig.json", "tsconfig.node.json", "vite.config.js", ".gitignore"]
[nix]
channel = "stable-22_11"
[env]
PATH = "/home/runner/$REPL_SLUG/.config/npm/node_global/bin:/home/runner/$REPL_SLUG/node_modules/.bin"
npm_config_prefix = "/home/runner/$REPL_SLUG/.config/npm/node_global"
[gitHubImport]
requiredFiles = [".replit", "replit.nix", ".config"]
[packager]
language = "nodejs"
[packager.features]
packageSearch = true
guessImports = true
enabledForHosting = false
[languages]
[languages.javascript]
pattern = "**/{*.js,*.jsx,*.ts,*.tsx}"
[languages.javascript.languageServer]
start = "typescript-language-server --stdio"
[deployment]
build = ["sh", "-c", "npm run build"]
run = ["sh", "-c", "npm run preview"]

13
index.html Normal file
View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Base Script</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/index.tsx"></script>
</body>
</html>

2486
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

32
package.json Normal file
View File

@ -0,0 +1,32 @@
{
"name": "react-emplate",
"version": "1.0.0",
"type": "module",
"description": "React TypeScript on Replit, using Vite bundler",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/react": "^18.0.15",
"@types/react-dom": "^18.0.6",
"@vitejs/plugin-react": "^2.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"typescript": "^4.7.4",
"vite": "^3.0.4"
},
"dependencies": {
"@douyinfe/semi-foundation": "^2.38.0",
"@douyinfe/semi-ui": "^2.36.0",
"@lark-base-open/js-sdk": "^0.3.0",
"i18next": "^23.5.1",
"i18next-browser-languagedetector": "^7.1.0",
"react-i18next": "^13.2.2",
"reset-css": "^5.0.1"
}
}

15
public/favicon.svg Normal file
View File

@ -0,0 +1,15 @@
<svg width="410" height="404" viewBox="0 0 410 404" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M399.641 59.5246L215.643 388.545C211.844 395.338 202.084 395.378 198.228 388.618L10.5817 59.5563C6.38087 52.1896 12.6802 43.2665 21.0281 44.7586L205.223 77.6824C206.398 77.8924 207.601 77.8904 208.776 77.6763L389.119 44.8058C397.439 43.2894 403.768 52.1434 399.641 59.5246Z" fill="url(#paint0_linear)"/>
<path d="M292.965 1.5744L156.801 28.2552C154.563 28.6937 152.906 30.5903 152.771 32.8664L144.395 174.33C144.198 177.662 147.258 180.248 150.51 179.498L188.42 170.749C191.967 169.931 195.172 173.055 194.443 176.622L183.18 231.775C182.422 235.487 185.907 238.661 189.532 237.56L212.947 230.446C216.577 229.344 220.065 232.527 219.297 236.242L201.398 322.875C200.278 328.294 207.486 331.249 210.492 326.603L212.5 323.5L323.454 102.072C325.312 98.3645 322.108 94.137 318.036 94.9228L279.014 102.454C275.347 103.161 272.227 99.746 273.262 96.1583L298.731 7.86689C299.767 4.27314 296.636 0.855181 292.965 1.5744Z" fill="url(#paint1_linear)"/>
<defs>
<linearGradient id="paint0_linear" x1="6.00017" y1="32.9999" x2="235" y2="344" gradientUnits="userSpaceOnUse">
<stop stop-color="#41D1FF"/>
<stop offset="1" stop-color="#BD34FE"/>
</linearGradient>
<linearGradient id="paint1_linear" x1="194.651" y1="8.81818" x2="236.076" y2="292.989" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFEA83"/>
<stop offset="0.0833333" stop-color="#FFDD35"/>
<stop offset="1" stop-color="#FFA800"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

8
replit.nix Normal file
View File

@ -0,0 +1,8 @@
{ pkgs }: {
deps = [
pkgs.nodejs-16_x
pkgs.nodePackages.typescript-language-server
pkgs.yarn
pkgs.replitPackages.jest
];
}

12
src/App.css Normal file
View File

@ -0,0 +1,12 @@
@import 'reset-css';
.main {
padding: 1.5rem 1rem 1rem;
}
.main h4 {
font-size: calc(1.275rem + 0.3vw);
}
.main code {
color: #d63384;
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
}

73
src/App.tsx Normal file
View File

@ -0,0 +1,73 @@
import './App.css';
import { bitable, TableMeta } from "@lark-base-open/js-sdk";
import { Button, Form } from '@douyinfe/semi-ui';
import { BaseFormApi } from '@douyinfe/semi-foundation/lib/es/form/interface';
import { useState, useEffect, useRef, useCallback } from 'react';
export default function App() {
const [tableMetaList, setTableMetaList] = useState<TableMeta[]>();
const formApi = useRef<BaseFormApi>();
const addRecord = useCallback(async ({ table: tableId }: { table: string }) => {
if (tableId) {
const table = await bitable.base.getTableById(tableId);
table.addRecord({
fields: {},
});
}
}, []);
useEffect(() => {
Promise.all([bitable.base.getTableMetaList(), bitable.base.getSelection()])
.then(([metaList, selection]) => {
setTableMetaList(metaList);
formApi.current?.setValues({ table: selection.tableId });
});
}, []);
return (
<main className="main">
<h4>
Edit <code>src/App.tsx</code> and save to reload
</h4>
<Form labelPosition='top' onSubmit={addRecord} getFormApi={(baseFormApi: BaseFormApi) => formApi.current = baseFormApi}>
<Form.Slot label="Development guide">
<div>
<a href="https://lark-technologies.larksuite.com/docx/HvCbdSzXNowzMmxWgXsuB2Ngs7d" target="_blank"
rel="noopener noreferrer">
Base Extensions Guide
</a>
<a href="https://bytedance.feishu.cn/docx/HazFdSHH9ofRGKx8424cwzLlnZc" target="_blank"
rel="noopener noreferrer">
</a>
</div>
</Form.Slot>
<Form.Slot label="API">
<div>
<a href="https://lark-technologies.larksuite.com/docx/Y6IcdywRXoTYSOxKwWvuLK09sFe" target="_blank"
rel="noopener noreferrer">
Base Extensions Front-end API
</a>
<a href="https://bytedance.feishu.cn/docx/HjCEd1sPzoVnxIxF3LrcKnepnUf" target="_blank"
rel="noopener noreferrer">
API
</a>
</div>
</Form.Slot>
<Form.Select field='table' label='Select Table' placeholder="Please select a Table" style={{ width: '100%' }}>
{
Array.isArray(tableMetaList) && tableMetaList.map(({ name, id }) => {
return (
<Form.Select.Option key={id} value={id}>
{name}
</Form.Select.Option>
);
})
}
</Form.Select>
<Button theme='solid' htmlType='submit'>Add Record</Button>
</Form>
</main>
)
}

View File

@ -0,0 +1,37 @@
import { ReactElement, useEffect, useState } from "react"
import { bitable } from "@lark-base-open/js-sdk"
import './style.css'
export default function LoadApp(props: { neverShowBanner?: boolean, children: ReactElement }): ReactElement {
const [loadErr, setLoadErr] = useState(false)
const TopBanner = <div>
<div className='errTop'>
After running the project, please get the webview address and paste it into the Base table "Extended Script" for use. See:&nbsp;
<a target='_blank' href='https://bytedance.feishu.cn/docx/HazFdSHH9ofRGKx8424cwzLlnZc'>Development Guide</a>
</div>
</div>
useEffect(() => {
if (props.neverShowBanner) return;
const timer = new Promise((resolve, reject) => {
setTimeout(() => {
reject(false)
}, 3000)
})
Promise.race([bitable.bridge.getLanguage(), timer]).then((v) => {
setLoadErr(false)
}).catch(() => {
setLoadErr(true)
})
}, [])
if (props.neverShowBanner) {
return props.children || null
}
return <div>
{loadErr && TopBanner}
{props.children}
</div>
}

View File

@ -0,0 +1,6 @@
.errTop{
padding: 14px;
background: #fffbe6;
border: 1px solid #ffe58f;
border-radius: 8px
}

8
src/index.tsx Normal file
View File

@ -0,0 +1,8 @@
import ReactDOM from 'react-dom/client'
import './App.css';
import App from './App';
import LoadApp from './components/LoadApp';
// import './locales/i18n' // 支持国际化
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(<LoadApp ><App /></LoadApp>)

3
src/locales/en.json Normal file
View File

@ -0,0 +1,3 @@
{
"title":"这是英文标题"
}

34
src/locales/i18n.ts Normal file
View File

@ -0,0 +1,34 @@
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import { bitable } from '@lark-base-open/js-sdk';
import translationEN from './en.json';
import translationZH from './zh.json';
const resources = {
zh: {
translation: translationZH,
},
en: {
translation: translationEN,
},
};
i18n
.use(LanguageDetector)
.use(initReactI18next)
.init({
resources,
fallbackLng: 'en', // 指定降级文案为英文
interpolation: {
escapeValue: false,
},
});
bitable.bridge.getLanguage().then((lng) => {
if (i18n.language !== lng) {
i18n.changeLanguage(lng);
}
});
export default i18n;

3
src/locales/zh.json Normal file
View File

@ -0,0 +1,3 @@
{
"title":"这是中文标题"
}

21
tsconfig.json Normal file
View File

@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}

9
tsconfig.node.json Normal file
View File

@ -0,0 +1,9 @@
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}

11
vite.config.js Normal file
View File

@ -0,0 +1,11 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
// https://vitejs.dev/config/
export default defineConfig({
base: './',
plugins: [react()],
server: {
host: '0.0.0.0',
}
})