create
This commit is contained in:
4
app.json
Normal file
4
app.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"appId": "cli_a7359bac85b9d01c",
|
||||||
|
"appName": "blk_inventory"
|
||||||
|
}
|
2
blk_inventory/.gitignore
vendored
Normal file
2
blk_inventory/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
dist
|
||||||
|
pnpm-lock.yaml
|
44
blk_inventory/README.md
Normal file
44
blk_inventory/README.md
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# demo
|
||||||
|
|
||||||
|
## 文件目录说明
|
||||||
|
|
||||||
|
```ts
|
||||||
|
.
|
||||||
|
├── config // webpack 配置文件目录
|
||||||
|
│ └── webpack.config.ts // webpack 配置文件
|
||||||
|
├── package.json
|
||||||
|
├── public
|
||||||
|
│ └── index.html
|
||||||
|
├── src
|
||||||
|
│ ├── App.tsx // 主要组件
|
||||||
|
│ ├── bitableApp.ts // 实例化 sdk
|
||||||
|
│ ├── index.tsx // 入口文件
|
||||||
|
│ └── utils.ts // 工具方法
|
||||||
|
├── block.json // 小组件元信息
|
||||||
|
├── README.md // 说明文件
|
||||||
|
└── tsconfig.json // ts config
|
||||||
|
```
|
||||||
|
|
||||||
|
## 安装依赖
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm / yarn / pnpm install
|
||||||
|
```
|
||||||
|
|
||||||
|
## 启动
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## 打包
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
## 发布
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run upload
|
||||||
|
```
|
5
blk_inventory/block.json
Normal file
5
blk_inventory/block.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"manifestVersion": 1,
|
||||||
|
"blockTypeID": "blk_67bf27b12481001387d8f3b9",
|
||||||
|
"projectName": "任务管理小应用"
|
||||||
|
}
|
130
blk_inventory/config/webpack.config.js
Normal file
130
blk_inventory/config/webpack.config.js
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
|
||||||
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
|
const { ESBuildMinifyPlugin } = require('esbuild-loader');
|
||||||
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
|
const WebpackBar = require('webpackbar');
|
||||||
|
const {
|
||||||
|
BitableAppWebpackPlugin,
|
||||||
|
opdevMiddleware
|
||||||
|
} = require('@lark-opdev/block-bitable-webpack-utils');
|
||||||
|
|
||||||
|
const cwd = process.cwd();
|
||||||
|
const isDevelopment = process.env.NODE_ENV === 'development';
|
||||||
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
entry: './src/index.tsx',
|
||||||
|
devtool: isProduction ? false : 'inline-source-map',
|
||||||
|
mode: isDevelopment ? 'development' : 'production',
|
||||||
|
stats: 'errors-only',
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, '../dist'),
|
||||||
|
clean: true,
|
||||||
|
publicPath: isDevelopment ? '/block/' : './',
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.js$/,
|
||||||
|
include: [/node_modules\/@lark-open/],
|
||||||
|
use: ['source-map-loader'],
|
||||||
|
enforce: 'pre',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oneOf: [
|
||||||
|
{
|
||||||
|
test: /\.[jt]sx?$/,
|
||||||
|
include: [path.join(cwd, 'src')],
|
||||||
|
exclude: /node_modules/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: require.resolve('esbuild-loader'),
|
||||||
|
options: {
|
||||||
|
loader: 'tsx',
|
||||||
|
target: 'es2015',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
use: [
|
||||||
|
isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader,
|
||||||
|
'css-loader',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.less$/,
|
||||||
|
use: [
|
||||||
|
isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader,
|
||||||
|
'css-loader',
|
||||||
|
'less-loader',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(png|jpg|jpeg|gif|ico|svg)$/,
|
||||||
|
type: 'asset/resource',
|
||||||
|
generator: {
|
||||||
|
filename: 'assets/[name][ext][query]',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
...(isDevelopment
|
||||||
|
? [new ReactRefreshWebpackPlugin(), new WebpackBar()]
|
||||||
|
: [new MiniCssExtractPlugin()]),
|
||||||
|
new BitableAppWebpackPlugin({
|
||||||
|
// open: true, // 控制是否自动打开多维表格
|
||||||
|
}),
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
filename: 'index.html',
|
||||||
|
template: './public/index.html',
|
||||||
|
publicPath: isDevelopment ? '/block/' : './',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.ts', '.tsx', '.js', '.jsx'],
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
minimize: isProduction,
|
||||||
|
minimizer: [new ESBuildMinifyPlugin({ target: 'es2015', css: true })],
|
||||||
|
moduleIds: 'deterministic',
|
||||||
|
runtimeChunk: true,
|
||||||
|
splitChunks: {
|
||||||
|
chunks: 'all',
|
||||||
|
cacheGroups: {
|
||||||
|
vendor: {
|
||||||
|
name: 'vendor',
|
||||||
|
test: /[\\/]node_modules[\\/]/,
|
||||||
|
chunks: 'all',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
devServer: isProduction
|
||||||
|
? undefined
|
||||||
|
: {
|
||||||
|
hot: true,
|
||||||
|
client: {
|
||||||
|
logging: 'error',
|
||||||
|
},
|
||||||
|
setupMiddlewares: (middlewares, devServer) => {
|
||||||
|
if (!devServer || !devServer.app) {
|
||||||
|
throw new Error('webpack-dev-server is not defined');
|
||||||
|
}
|
||||||
|
middlewares.push(opdevMiddleware(devServer))
|
||||||
|
return middlewares;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
cache: {
|
||||||
|
type: 'filesystem',
|
||||||
|
buildDependencies: {
|
||||||
|
config: [__filename],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
module.exports = config;
|
13730
blk_inventory/package-lock.json
generated
Normal file
13730
blk_inventory/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
43
blk_inventory/package.json
Normal file
43
blk_inventory/package.json
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"name": "@bitable/pack-demo",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "cross-env NODE_ENV=development webpack-dev-server --mode development --config ./config/webpack.config.js",
|
||||||
|
"start": "npm run dev",
|
||||||
|
"build": "cross-env NODE_ENV=production webpack --mode production --config ./config/webpack.config.js",
|
||||||
|
"upload": "npm run build && opdev upload ./dist"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@lark-opdev/block-bitable-api": "^0.1.0",
|
||||||
|
"@douyinfe/semi-ui": "^2.22.3",
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-async-hook": "^4.0.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"react-use": "^17.4.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@lark-opdev/block-bitable-webpack-utils": "^0.1.0",
|
||||||
|
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.7",
|
||||||
|
"@types/react": "^18.0.15",
|
||||||
|
"@types/react-dom": "^18.0.6",
|
||||||
|
"@types/webpack": "^5.28.0",
|
||||||
|
"cross-env": "^7.0.3",
|
||||||
|
"css-loader": "^6.7.1",
|
||||||
|
"esbuild-loader": "^2.20.0",
|
||||||
|
"html-webpack-plugin": "^5.5.0",
|
||||||
|
"less": "^4.1.3",
|
||||||
|
"less-loader": "^11.0.0",
|
||||||
|
"mini-css-extract-plugin": "^2.6.1",
|
||||||
|
"react-refresh": "^0.14.0",
|
||||||
|
"simple-progress-webpack-plugin": "^2.0.0",
|
||||||
|
"source-map-loader": "^4.0.0",
|
||||||
|
"style-loader": "^3.3.1",
|
||||||
|
"typescript": "^4.6.4",
|
||||||
|
"webpack": "^5.74.0",
|
||||||
|
"webpack-cli": "^4.10.0",
|
||||||
|
"webpack-dev-server": "^4.10.0",
|
||||||
|
"webpackbar": "^5.0.2"
|
||||||
|
},
|
||||||
|
"readme": "这是一个脚手架基础项目\n"
|
||||||
|
}
|
11
blk_inventory/public/index.html
Normal file
11
blk_inventory/public/index.html
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
96
blk_inventory/src/App.tsx
Normal file
96
blk_inventory/src/App.tsx
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
import { bitable } from "@lark-opdev/block-bitable-api";
|
||||||
|
import { FC, useEffect } from "react";
|
||||||
|
import { useAsync } from "react-async-hook";
|
||||||
|
import { getCurrentTask, setCompleted } from "./utils";
|
||||||
|
import {
|
||||||
|
Typography,
|
||||||
|
Tag,
|
||||||
|
Button,
|
||||||
|
Divider,
|
||||||
|
Space,
|
||||||
|
Toast,
|
||||||
|
} from "@douyinfe/semi-ui";
|
||||||
|
|
||||||
|
const { Title, Text } = Typography;
|
||||||
|
|
||||||
|
const defaultTask = {
|
||||||
|
description: "",
|
||||||
|
userName: "",
|
||||||
|
completed: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const App = () => {
|
||||||
|
const task = useAsync(getCurrentTask, []);
|
||||||
|
const { description, userName, completed } = task.result ?? defaultTask;
|
||||||
|
|
||||||
|
// 切换上下一条记录时,触发 SelectionChange
|
||||||
|
useEffect(() => {
|
||||||
|
return bitable.base.onSelectionChange(({ data }) =>
|
||||||
|
task.execute()
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const toggleCompleted = () => {
|
||||||
|
setCompleted(!completed)
|
||||||
|
.then(() => task.execute())
|
||||||
|
.then(() => Toast.success("更新任务状态成功"))
|
||||||
|
.catch(() => Toast.error("更新任务状态失败"));
|
||||||
|
};
|
||||||
|
|
||||||
|
if (task.loading) return <div>loading</div>;
|
||||||
|
if (task.error) return <div>error: {task.error.message}</div>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PureTaskComponment
|
||||||
|
description={description}
|
||||||
|
userName={userName}
|
||||||
|
completed={completed}
|
||||||
|
toggleCompleted={toggleCompleted}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface PureTaskComponmentProps {
|
||||||
|
description: string;
|
||||||
|
userName: string;
|
||||||
|
completed: boolean;
|
||||||
|
toggleCompleted: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PureTaskComponment: FC<PureTaskComponmentProps> = ({
|
||||||
|
description,
|
||||||
|
userName,
|
||||||
|
completed,
|
||||||
|
toggleCompleted,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Space vertical align="start">
|
||||||
|
<div>
|
||||||
|
<Title heading={2}>任务管理小应用</Title>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Text>描述:</Text>
|
||||||
|
<Text>{description}</Text>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Text>执行人:</Text>
|
||||||
|
<Text>{userName}</Text>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Text>完成状态:</Text>
|
||||||
|
<Tag color={completed ? "green" : "blue"}>
|
||||||
|
{completed ? "已完成" : "未完成"}
|
||||||
|
</Tag>
|
||||||
|
</div>
|
||||||
|
<Divider />
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
type={completed ? "danger" : "primary"}
|
||||||
|
onClick={toggleCompleted}
|
||||||
|
>
|
||||||
|
{completed ? "撤销完成任务" : "完成任务"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
};
|
9
blk_inventory/src/index.tsx
Normal file
9
blk_inventory/src/index.tsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import React from "react";
|
||||||
|
import ReactDOM from "react-dom/client";
|
||||||
|
import { App } from './App';
|
||||||
|
|
||||||
|
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<App />
|
||||||
|
</React.StrictMode>
|
||||||
|
);
|
85
blk_inventory/src/utils.ts
Normal file
85
blk_inventory/src/utils.ts
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import {
|
||||||
|
IOpenCheckbox,
|
||||||
|
IOpenSegment,
|
||||||
|
IOpenUser,
|
||||||
|
bitable
|
||||||
|
} from "@lark-opdev/block-bitable-api";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从模板创建的多维表格
|
||||||
|
* 可以保证字段 ID 与模板一致
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** 任务描述字段 ID */
|
||||||
|
const descriptionFieldId = "fldaxqIJ1m";
|
||||||
|
|
||||||
|
/** 任务执行人字段名称 */
|
||||||
|
const userFieldName = "任务执行人";
|
||||||
|
|
||||||
|
/** 是否完成字段 ID */
|
||||||
|
const completedFieldId = "fld9cvGzic";
|
||||||
|
|
||||||
|
/** 尝试一下:接入是否延期字段 ID */
|
||||||
|
// const exceedingFieldId = "todo"
|
||||||
|
|
||||||
|
function getUserName(userValue: IOpenUser[] | null) {
|
||||||
|
if (!userValue || userValue.length === 0) {
|
||||||
|
return "任务执行人不存在";
|
||||||
|
}
|
||||||
|
return userValue[0].name ?? "用户没有设置姓名";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDescription(descriptionValue: IOpenSegment[] | null) {
|
||||||
|
if (!descriptionValue || descriptionValue.length === 0) {
|
||||||
|
return "任务描述不存在";
|
||||||
|
}
|
||||||
|
return descriptionValue.map((segment) => segment.text).join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getCurrentTask() {
|
||||||
|
// 1. 读取选中的表和记录 //
|
||||||
|
const { tableId, recordId } = await bitable.base.getSelection();
|
||||||
|
if (!tableId || !recordId) throw new Error("选区状态读取失败");
|
||||||
|
const table = await bitable.base.getTableById(tableId);
|
||||||
|
|
||||||
|
// 2. 读取单元格 //
|
||||||
|
const completedValue = (await table.getCellValue(
|
||||||
|
completedFieldId,
|
||||||
|
recordId
|
||||||
|
)) as IOpenCheckbox;
|
||||||
|
const userField = await table.getFieldByName(userFieldName);
|
||||||
|
const userValue = (await table.getCellValue(
|
||||||
|
userField.id,
|
||||||
|
recordId
|
||||||
|
)) as IOpenUser[];
|
||||||
|
const descriptionValue = (await table.getCellValue(
|
||||||
|
descriptionFieldId,
|
||||||
|
recordId
|
||||||
|
)) as IOpenSegment[];
|
||||||
|
|
||||||
|
// 尝试一下:读取是否延期字段
|
||||||
|
// 单选的值类型为 IOpenSingleSelect
|
||||||
|
// const exceedingValue = (await table.getCellValue(exceedingFieldId, recordId)) as IOpenSingleSelect;
|
||||||
|
|
||||||
|
// 尝试一下:将 exceedingValue 转换成选中选项的字符串
|
||||||
|
// const exceedingText = doYourCustomTransform(exceedingValue)
|
||||||
|
|
||||||
|
// 3. 将单元格结构体转换成业务所需数据 //
|
||||||
|
return {
|
||||||
|
description: getDescription(descriptionValue),
|
||||||
|
userName: getUserName(userValue),
|
||||||
|
completed: completedValue,
|
||||||
|
// 尝试一下:返回是否延期信息
|
||||||
|
// exceeding: exceedingText
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setCompleted(completed: boolean) {
|
||||||
|
// 1. 读取选中的表和记录 //
|
||||||
|
const { tableId, recordId } = await bitable.base.getSelection();
|
||||||
|
if (!tableId || !recordId) throw new Error("选区状态读取失败");
|
||||||
|
const table = await bitable.base.getTableById(tableId);
|
||||||
|
|
||||||
|
// 2. 将业务数据转换成单元格结构,然后写入 //
|
||||||
|
table.setCellValue(completedFieldId, recordId, completed as IOpenCheckbox);
|
||||||
|
}
|
25
blk_inventory/tsconfig.json
Normal file
25
blk_inventory/tsconfig.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2017",
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx"
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"ts-node": {
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user