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