@ks-dbdev/template-react-db-record
Advanced tools
| module.exports = { | ||
| root: true, | ||
| env: { browser: true, es2020: true }, | ||
| extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:react-hooks/recommended"], | ||
| ignorePatterns: ["dist", ".eslintrc.cjs"], | ||
| parser: "@typescript-eslint/parser", | ||
| plugins: ["react-refresh"], | ||
| rules: { | ||
| "react-refresh/only-export-components": ["warn", { allowConstantExport: true }], | ||
| }, | ||
| } |
+28
| <!doctype html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8" /> | ||
| <link rel="icon" type="image/svg+xml" href="/vite.svg" /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| <title>Vite + React + TS</title> | ||
| </head> | ||
| <body> | ||
| <div id="root"></div> | ||
| <script type="module" src="/src/main.tsx"></script> | ||
| <script> | ||
| if (!window.parent) { | ||
| console.error("must be in iframe") | ||
| } else { | ||
| function _t_ss(){ | ||
| var url = new URL(window.location.href) | ||
| var searchParams = url.searchParams | ||
| window.appId = searchParams.get("app_id") | ||
| if (!window.appId) { | ||
| console.error("appId is required") | ||
| } | ||
| } | ||
| _t_ss() | ||
| } | ||
| </script> | ||
| </body> | ||
| </html> |
| <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg> |
+94
| import {IField } from "./interface" | ||
| export default class Application { | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| app: any = null | ||
| sheetId: string = '' | ||
| setApp(app: any) { | ||
| this.app = app | ||
| window.addEventListener('beforeunload', this.beforeDestroy) | ||
| } | ||
| async getCurrentSheetId() { | ||
| const sheetId = await this.app.ActiveSheet.Id | ||
| this.sheetId = sheetId | ||
| return sheetId | ||
| } | ||
| async getSheets(){ | ||
| const sheets = await this.app.Sheet.GetSheets() | ||
| return sheets | ||
| } | ||
| async getCurrentFields(): Promise<IField[]> { | ||
| const sheet = await this.app.ActiveSheet | ||
| const res: IField[] = [] | ||
| const fields = await sheet.FieldDescriptors | ||
| const count = await fields.Count | ||
| for (let i = 0; i < count; i++) { | ||
| const field = await fields.Item(i + 1) | ||
| const name = await field.Name | ||
| const type = await field.Type | ||
| const id = await field.Id | ||
| res.push({ id, name, type }) | ||
| } | ||
| return res | ||
| } | ||
| async getCurrentSelected():Promise<any>{ | ||
| const selectedField = await this.app.Selection.GetSelectionRecords() | ||
| if(selectedField && selectedField[0]?.length === 1 && selectedField.length === 1){ | ||
| const selected= selectedField[0][0] | ||
| return selected | ||
| }else{ | ||
| return null | ||
| } | ||
| } | ||
| async getCurrentView(){ | ||
| const selectedView = await this.app.Selection.GetActiveView() | ||
| return selectedView | ||
| } | ||
| async getCurentViews(){ | ||
| const sheetId = this.sheetId | ||
| const currentViews = await this.app.View.GetViews({SheetId:sheetId}) | ||
| return currentViews | ||
| } | ||
| async onRefresh(callbackA: () => void,callbackB:()=>void) { | ||
| this.app.Sub.ViewDataUpdate = callbackA | ||
| this.app.Sub.SelectionChange = callbackB | ||
| } | ||
| async getCurrentTaskSelect(recordId:string,descriptionFieldId:string,complateFieldId:string,userFieldId:string):Promise<any>{ | ||
| const selectedField = await this.app.ActiveView.RecordRange(recordId, [descriptionFieldId,complateFieldId,userFieldId]).Value | ||
| if(selectedField){ | ||
| return selectedField | ||
| } | ||
| else{ | ||
| return [] | ||
| } | ||
| } | ||
| async updateTask(recordId:string,completed:boolean,taskFieldId:string){ | ||
| if (!recordId) { | ||
| throw new Error("recordId cannot be empty.") | ||
| } | ||
| const updateRecord = await this.app.ActiveView.RecordRange( | ||
| recordId, | ||
| [taskFieldId] | ||
| ) | ||
| if (typeof await updateRecord.Value !== 'boolean') { | ||
| throw new Error("Expected a boolean value for updateRecord.Value.") | ||
| } | ||
| updateRecord.Value = completed | ||
| return updateRecord.Value | ||
| } | ||
| beforeDestroy() { | ||
| this.app.Sub.ViewDataUpdate = null | ||
| this.app.Sub.SelectionChange = null | ||
| } | ||
| } |
+60
| body{ | ||
| background-color: #f0f0f0; | ||
| width: 100vw; | ||
| height: 100vh; | ||
| } | ||
| #root { | ||
| width: 100%; | ||
| height: 100%; | ||
| display: flex; | ||
| flex-direction: column; | ||
| } | ||
| .main{ | ||
| min-width: 700px; | ||
| display: flex; | ||
| flex-direction:row; | ||
| flex: 1; | ||
| height: 100%; | ||
| width: 100%; | ||
| } | ||
| .header{ | ||
| width: 150px; | ||
| z-index: 10; | ||
| flex: 0 0 150px; | ||
| background-color: #f0f0f0; | ||
| padding: 10px 20px; | ||
| display: flex; | ||
| flex-direction: column; | ||
| button{ | ||
| padding: 10px 20px; | ||
| margin-top:20px ; | ||
| cursor: pointer; | ||
| outline: none; | ||
| border: 1px solid #ccc; | ||
| background-color: #fff; | ||
| transition: background-color 0.3s; | ||
| color: #666; | ||
| } | ||
| button:hover{ | ||
| background-color: #e6e6e6; | ||
| color: #333; | ||
| } | ||
| .active{ | ||
| background-color: #e6e6e6; | ||
| outline: 4px auto -webkit-focus-ring-color; | ||
| color: #333; | ||
| } | ||
| } | ||
| .card { | ||
| flex: 1; | ||
| overflow: auto; | ||
| background-color: #ffffff; | ||
| padding: 20px; | ||
| } | ||
| table{ | ||
| margin-top: 10px; | ||
| border: 2px solid #ccc; | ||
| border-collapse: collapse; | ||
| } | ||
| tr{ | ||
| border-bottom: 1px solid #ccc; | ||
| } | ||
| th, td { | ||
| padding: 18px; | ||
| text-align: left; | ||
| color: #333; | ||
| } | ||
| td{ | ||
| max-width: 600px; | ||
| } | ||
| pre { | ||
| white-space: pre-wrap; | ||
| word-break: break-word; | ||
| font-size: 15px; | ||
| max-width: 100%; | ||
| } |
+102
| import {useEffect, useMemo, useState} from 'react' | ||
| import type { CurrentActiveView, CurrentSheetView} from "./interface" | ||
| import './BaseInfo.css' | ||
| interface Props{ | ||
| selectedInfo?:CurrentSheetView | ||
| viewInfo?:CurrentActiveView | ||
| } | ||
| interface labelItem{ | ||
| name:string; | ||
| data:string | JSX.Element | ||
| } | ||
| const BaseInfoContent :React.FC< Props> = ({selectedInfo,viewInfo})=>{ | ||
| const appID = window.appId | ||
| const [attachmentLink,setAttachmentLink] = useState('') | ||
| useEffect(()=>{ | ||
| if(selectedInfo?.fields[0]?.type === 'Attachment'){ | ||
| const parts = selectedInfo.fields[0].value.split('|') | ||
| switch (parts[1]) { | ||
| case 'cloud': | ||
| setAttachmentLink(parts[5]); | ||
| break; | ||
| case 'upload_ks3': | ||
| setAttachmentLink('该附件为本地文件'); | ||
| break; | ||
| default: | ||
| setAttachmentLink(''); | ||
| } | ||
| } | ||
| else{ | ||
| setAttachmentLink('') | ||
| } | ||
| },[selectedInfo,viewInfo]) | ||
| const labelArray:labelItem[] = useMemo(() => [ | ||
| { | ||
| name: 'Base ID(appId)', | ||
| data: `${appID}` | ||
| }, | ||
| { | ||
| name: 'Sheet(数据表)ID', | ||
| data: viewInfo?.sheetId || '数据异常' | ||
| }, | ||
| { | ||
| name: 'View(视图)ID', | ||
| data: viewInfo?.viewId || '数据异常' | ||
| }, | ||
| { | ||
| name: 'Field(列) ID', | ||
| data: selectedInfo?.fields?.length === 1 && selectedInfo.fields[0]?.id || '请选择单元格' | ||
| }, | ||
| { | ||
| name: 'Record(行) ID', | ||
| data: selectedInfo?.id || '请选择单元格或单元行' | ||
| }, | ||
| { | ||
| name: '当前视图链接', | ||
| data: `` | ||
| }, | ||
| { | ||
| name: '当前记录链接', | ||
| data: '' | ||
| }, | ||
| { | ||
| name: '单元格值', | ||
| data: selectedInfo?.fields?.length === 1 ? selectedInfo.fields[0].value : '请选择单元格' | ||
| }, | ||
| { | ||
| name: '列元数据', | ||
| data: selectedInfo?.fields?.length === 1 ? <pre>{JSON.stringify(selectedInfo.fields[0], null, 2)}</pre> : '请选择单元格' | ||
| }, | ||
| { | ||
| name: '附件链接', | ||
| data: attachmentLink || "" | ||
| } | ||
| ], [appID, viewInfo, selectedInfo, attachmentLink]); | ||
| return( | ||
| <div> | ||
| <table> | ||
| <tbody> | ||
| { | ||
| labelArray && labelArray.map((label,index)=>( | ||
| <tr key={index}> | ||
| <th>{label.name}</th> | ||
| <td>{label.data}</td> | ||
| </tr> | ||
| )) | ||
| } | ||
| </tbody> | ||
| </table> | ||
| </div> | ||
| ) | ||
| } | ||
| export default BaseInfoContent |
| div{ | ||
| text-align: center; | ||
| } | ||
| .taskCard{ | ||
| background-color: #c7c7c7; | ||
| width: 300px; | ||
| height: 330px; | ||
| margin: 10px auto; | ||
| padding: 10px; | ||
| background: #fdfdfd; | ||
| box-shadow: 0 4px 8px rgba(0,0,0,0.1); | ||
| border-radius: 8px; | ||
| overflow: hidden; | ||
| } | ||
| .cardHeader { | ||
| border-bottom: 1px solid #e1e1e1; | ||
| } | ||
| .cardHeader h1{ | ||
| color: rgb(0, 0, 0); | ||
| font-size: 25px; | ||
| } | ||
| .row{ | ||
| width: 300px; | ||
| height: 50px; | ||
| margin-top: 5px ; | ||
| display:flex; | ||
| align-items: center; | ||
| white-space: nowrap; | ||
| } | ||
| .row span{ | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| white-space: nowrap; | ||
| } | ||
| .taskButton:hover{ | ||
| border-color: white; | ||
| } |
| import './index.less' | ||
| import { FC } from "react" | ||
| import { Tag,Button,Divider,Tooltip } from "@kdocs/kdesign" | ||
| import React from 'react' | ||
| interface PureTaskComponentProps { | ||
| description: string | ||
| userName: string | ||
| completed: boolean | ||
| toggleCompleted: () => void | ||
| } | ||
| const PureTaskComponent: FC<PureTaskComponentProps> = React.memo(({ | ||
| description, | ||
| userName, | ||
| completed, | ||
| toggleCompleted, | ||
| }) => { | ||
| return ( | ||
| <div className="taskCard"> | ||
| <div className="cardHeader"> | ||
| <h1>任务管理小应用</h1> | ||
| </div> | ||
| <div className="cardBody"> | ||
| <div className="row"> | ||
| <h3 className="des">描述:</h3> | ||
| <Tooltip title={description}>{description?description:'————'}</Tooltip> | ||
| </div> | ||
| <div className="row"> | ||
| <h3>执行人:</h3> | ||
| <span>{userName?userName:'————'}</span> | ||
| </div> | ||
| <div className="row"> | ||
| <h3>完成状态:</h3> | ||
| {/* @ts-ignore */} | ||
| <Tag color={completed? "rgb(0, 187, 187)" : "rgb(80, 129, 194)" } size="large"> | ||
| <span style={{color:'white'}}>{ completed ? "已完成" : "未完成"}</span> | ||
| </Tag> | ||
| </div> | ||
| </div> | ||
| <Divider /> | ||
| <Button | ||
| type={'primary'} | ||
| danger = {completed ?true:false} | ||
| onClick={toggleCompleted} | ||
| className="taskButton" | ||
| > | ||
| {completed ? "撤销完成任务" : "完成任务"} | ||
| </Button> | ||
| </div> | ||
| ) | ||
| }) | ||
| export default PureTaskComponent |
| interface Window { | ||
| appId: string | ||
| } |
| export interface IField { | ||
| id: string | ||
| name: string | ||
| type: string | ||
| } | ||
| export interface SheetsInfo { | ||
| id:string, | ||
| name:string, | ||
| recordsCount:string, | ||
| sheetType:string, | ||
| syncType:string | ||
| } | ||
| export interface CurrentFields{ | ||
| id:string, | ||
| name:string, | ||
| type:string, | ||
| value:string, | ||
| recordsCount:string, | ||
| linkUrl:string, | ||
| source:string | ||
| } | ||
| export interface CurrentSheetView{ | ||
| fields: CurrentFields[], | ||
| id:string | ||
| } | ||
| export interface CurrentActiveView{ | ||
| name:string, | ||
| sheetId:string, | ||
| type:string, | ||
| viewId:string | ||
| } |
| import React from 'react' | ||
| import {SheetsInfo} from './interface' | ||
| interface Props { | ||
| sheetList :SheetsInfo[] | ||
| } | ||
| const SheetList:React.FC<Props>= ({sheetList})=>{ | ||
| const transformSheetList = sheetList.map(item=>( | ||
| { | ||
| id:item.id, | ||
| name:item.name, | ||
| recordsCount:item.recordsCount, | ||
| sheetType:item.sheetType, | ||
| syncType:item.syncType | ||
| })) | ||
| return( | ||
| <div style={{ textAlign:'left'}}> | ||
| <pre style={{fontSize:'15px', color:'#666'}}> | ||
| {JSON.stringify(transformSheetList,null,2)} | ||
| </pre> | ||
| </div> | ||
| ) | ||
| } | ||
| export default SheetList |
| import React from 'react' | ||
| import {CurrentFields} from './interface' | ||
| interface Props { | ||
| currentSheetList :CurrentFields[] | ||
| } | ||
| const SheetViewList:React.FC<Props>= ({currentSheetList})=>{ | ||
| const transformSheetList = currentSheetList.map(item=>( | ||
| { | ||
| id:item.id, | ||
| name:item.name, | ||
| recordsCount:item.recordsCount, | ||
| type:item.type | ||
| })) | ||
| return( | ||
| <div style={{ textAlign:'left'}}> | ||
| <pre style={{fontSize:'15px', color:'#666'}}> | ||
| {JSON.stringify(transformSheetList,null,2)} | ||
| </pre> | ||
| </div> | ||
| ) | ||
| } | ||
| export default SheetViewList |
| import type {IField} from './interface' | ||
| interface Props { | ||
| fieldList: IField[] | ||
| } | ||
| const TabelData: React.FC<Props> = ({ fieldList }) =>{ | ||
| return( | ||
| <div style={{ textAlign:'left'}}> | ||
| <pre style={{fontSize:'15px', color:'#666'}}> | ||
| {JSON.stringify(fieldList,null,2)} | ||
| </pre> | ||
| </div> | ||
| ) | ||
| } | ||
| export default TabelData |
| { | ||
| "compilerOptions": { | ||
| "composite": true, | ||
| "skipLibCheck": true, | ||
| "module": "ESNext", | ||
| "moduleResolution": "bundler", | ||
| "allowSyntheticDefaultImports": true, | ||
| "strict": true | ||
| }, | ||
| "include": ["vite.config.ts"] | ||
| } |
| import { defineConfig } from "vitest/config" | ||
| export default defineConfig({ | ||
| test: { | ||
| coverage: { | ||
| provider: "v8", // or 'v8' | ||
| reporter: ["text", "json", "html"], | ||
| }, | ||
| environment: "jsdom", | ||
| }, | ||
| }) |
+12
-15
| { | ||
| "name": "@ks-dbdev/template-react-db-record", | ||
| "version": "0.0.1", | ||
| "type": "module", | ||
| "version": "0.1.0", | ||
| "publishConfig": { | ||
| "access": "public" | ||
| }, | ||
| "type": "module", | ||
| "scripts": { | ||
| "dev": "vite", | ||
| "build": "tsc && vite build", | ||
| "test-build":"vite build", | ||
| "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", | ||
| "preview": "vite preview" | ||
| "preview": "vite preview", | ||
| "test": "echo 'no test'" | ||
| }, | ||
| "files": [ | ||
| "src", | ||
| "README.md", | ||
| "vite.config.ts", | ||
| "package.json", | ||
| "tsconfig.json", | ||
| "eslintrc.cjs" | ||
| ], | ||
| "dependencies": { | ||
| "@ks-dbdev/sdk": "latest", | ||
| "@kdocs/kdesign": "^2.1.4", | ||
| "@kdocs/kdesign-theme": "^2.3.5", | ||
| "react": "^18.2.0", | ||
| "react-dom": "^18.2.0", | ||
| "@dbdev/sdk": "file:./dbdev-sdk-0.0.1.tgz" | ||
| "react-dom": "^18.2.0" | ||
| }, | ||
@@ -33,9 +29,10 @@ "devDependencies": { | ||
| "@vitejs/plugin-react-swc": "^3.5.0", | ||
| "autoprefixer": "^10.4.19", | ||
| "eslint": "^8.56.0", | ||
| "eslint-plugin-react-hooks": "^4.6.0", | ||
| "eslint-plugin-react-refresh": "^0.4.5", | ||
| "less": "^4.2.0", | ||
| "typescript": "^5.2.2", | ||
| "vite": "^5.1.4", | ||
| "autoprefixer": "^10.4.19" | ||
| "vite": "^5.1.4" | ||
| } | ||
| } |
+11
-45
@@ -1,48 +0,14 @@ | ||
| # React + TypeScript + Vite | ||
| ## 主要功能 | ||
| This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. | ||
| Currently, two official plugins are available: | ||
| - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh | ||
| - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh | ||
| ## Expanding the ESLint configuration | ||
| If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: | ||
| - Configure the top-level `parserOptions` property like this: | ||
| ```js | ||
| export default { | ||
| // other rules... | ||
| parserOptions: { | ||
| ecmaVersion: 'latest', | ||
| sourceType: 'module', | ||
| project: ['./tsconfig.json', './tsconfig.node.json'], | ||
| tsconfigRootDir: __dirname, | ||
| }, | ||
| } | ||
| 1. 点击第一个Base Info按钮,可以查找单元格的基础信息 | ||
| 2. 点击第二个Current Sheet Fields Meta按钮,可以查找当前sheet下的字段列表 | ||
| 3. 点击第三个Sheets Meta List按钮,可以查所有sheet的信息 | ||
| 4. 点击第四个按钮Current Sheet View Meta,可以查当前sheet下的view列表信息 | ||
| 5. 第五个按钮为一个demo组件,绑定了三个字段,当点击某一行的单元格,可以对该行的任务进行管理。**注意**,这里的字段必须与代码中要求的字段一致,否则不生效,代码中的字段要求如下: | ||
| ``` | ||
| - Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` | ||
| - Optionally add `plugin:@typescript-eslint/stylistic-type-checked` | ||
| - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list | ||
| ### vite configuration | ||
| If you are developing a production application, we require configuring the Vite configuration | ||
| //任务管理器绑定三个字段,任务描述,任务执行人,任务状态 | ||
| const taskDescription = 'B' //绑定任务描述字段 | ||
| const taskUserFieldId = 'O' //绑定执行人 | ||
| const taskFieldId = 'F' //绑定是否完成字段 | ||
| ``` | ||
| build: { | ||
| rollupOptions: { | ||
| output: { | ||
| entryFileNames: '[name]-[hash].js', | ||
| chunkFileNames: '[name]-[hash].js', | ||
| assetFileNames: '[name]-[hash].[ext]', | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| This ensures that resources can be loaded correctly | ||
| 可参考链接https://www.kdocs.cn/l/celFWiCFKQOX?R=L1MvMQ== |
+118
-40
@@ -1,51 +0,129 @@ | ||
| import { useEffect, useState, memo } from "react" | ||
| import reactLogo from "./assets/react.svg" | ||
| import viteLogo from "/vite.svg" | ||
| import sdk from "@dbdev/sdk" | ||
| import "./App.css" | ||
| import { useEffect, useState, memo, useRef, useCallback } from "react" | ||
| import sdk from "@ks-dbdev/sdk" | ||
| import "./App.less" | ||
| import '@kdocs/kdesign-theme/default.css' | ||
| import Application from "./api" | ||
| import { CurrentActiveView, CurrentFields, CurrentSheetView, IField ,SheetsInfo} from "./interface" | ||
| import BaseInfoContent from './BaseInfo' | ||
| import TabelData from "./TabelList" | ||
| import SheetList from "./SheetsList" | ||
| import SheetViewList from "./SheetViewList" | ||
| import PureTaskComponent from "./componment/PurTaskComponent" | ||
| import { Message, Loading} from "@kdocs/kdesign" | ||
| async function testGetData() { | ||
| const Application = await sdk.ready() | ||
| const sheet = await Application.Sheets(1) | ||
| const fds = await sheet.FieldDescriptors | ||
| const count = await fds.Count | ||
| //任务管理器绑定三个字段,任务描述,任务执行人,任务状态 | ||
| const taskDescription = 'B' //绑定任务描述字段 | ||
| const taskUserFieldId = 'O' //绑定执行人 | ||
| const taskFieldId = 'F' //绑定是否完成字段 | ||
| console.time("post-message-test") | ||
| for (let i = 0; i < 1000; i++) { | ||
| const fd = await fds.Item((i % count) + 1) | ||
| const name = await fd.Name | ||
| const id = await fd.Id | ||
| const type = await fd.Type | ||
| const nf = await fd.NumberFormat | ||
| const df = await fd.DefaultValue | ||
| console.log(id, name, type, nf, df) | ||
| function App() { | ||
| const appRef = useRef(new Application()) | ||
| const [loading, setLoading] = useState(true) | ||
| const [content, setContent] = useState(0) | ||
| const[viewInfo,setViewInfo] = useState<CurrentActiveView>() | ||
| const [activeButton, setActiveButton] = useState(0) | ||
| const [fields,setFields] = useState<IField[]>([]) | ||
| const [recordId,setRecordId] = useState<string>('') | ||
| const[sheets,setSheets]=useState<SheetsInfo[]>([]) | ||
| const[viewList,setViewList] = useState<CurrentFields[]>([]) | ||
| const[currentSelect,setCurrentSelect] = useState<CurrentSheetView>() | ||
| const[description,setDescription] = useState('') | ||
| const[user,setUser] = useState('') | ||
| const[completed,setCompleted]=useState<boolean>(false) | ||
| const toggleContent = (value:number) => { | ||
| setContent(value) | ||
| setActiveButton(value) | ||
| setLoading(true) | ||
| run() | ||
| } | ||
| console.timeEnd("post-message-test") | ||
| } | ||
| function App() { | ||
| const [count, setCount] = useState(0) | ||
| const toggleCompleted = useCallback(async () => { | ||
| await appRef.current.updateTask(recordId, !completed ,taskFieldId).then( | ||
| result=>{ | ||
| setCompleted(result) | ||
| Message.success('更新任务状态成功') | ||
| } | ||
| ).catch( ()=> { | ||
| Message.error('更新任务状态失败') | ||
| }) | ||
| }, [recordId,completed]) | ||
| const run = useCallback(async()=>{ | ||
| await appRef.current.getCurrentSheetId() | ||
| const currentView = await appRef.current.getCurentViews() | ||
| setViewList(currentView) | ||
| const sheets = await appRef.current.getSheets() | ||
| setSheets(sheets) | ||
| const fieldIds = await appRef.current.getCurrentFields() | ||
| setFields(fieldIds) | ||
| const currentActiveView = await appRef.current.getCurrentView() | ||
| const currentSelect = await appRef.current.getCurrentSelected() | ||
| setViewInfo(currentActiveView) | ||
| if(currentSelect){ | ||
| setCurrentSelect(currentSelect) | ||
| setRecordId(currentSelect.id) | ||
| const record = await appRef.current.getCurrentTaskSelect(currentSelect.id, taskDescription,taskFieldId,taskUserFieldId) | ||
| if(record.length>2){ | ||
| setDescription(record[0]) | ||
| setCompleted(record[1]) | ||
| if(record[2]?.value){ | ||
| record[2].value[0] ? setUser(record[2].value[0].nickname) : setUser('') | ||
| }} | ||
| } | ||
| else{ | ||
| setCurrentSelect({id:'',fields:[]}) | ||
| setRecordId('') | ||
| setDescription('') | ||
| setCompleted(false) | ||
| setUser('') | ||
| } | ||
| setLoading(false) | ||
| },[]) | ||
| useEffect(() => { | ||
| testGetData() | ||
| }, []) | ||
| async function initApp() { | ||
| const application = await sdk.ready() | ||
| if (appRef.current.app === null) { | ||
| appRef.current.setApp(application) | ||
| appRef.current.onRefresh( | ||
| run, | ||
| ()=>{ | ||
| setLoading(true) | ||
| run() | ||
| }) | ||
| } | ||
| run() | ||
| } | ||
| initApp() | ||
| }, [run]) | ||
| const renderContent = () => ( | ||
| loading ? <Loading /> : { | ||
| 0: <BaseInfoContent selectedInfo={currentSelect} viewInfo={viewInfo} />, | ||
| 1: <TabelData fieldList={fields} />, | ||
| 2: <SheetList sheetList={sheets} />, | ||
| 3: <SheetViewList currentSheetList={viewList} />, | ||
| 4: <PureTaskComponent description={description} userName={user} completed={completed} toggleCompleted={toggleCompleted} /> | ||
| }[content] | ||
| ) | ||
| return ( | ||
| <> | ||
| <div> | ||
| <a href="https://vitejs.dev" target="_blank"> | ||
| <img src={viteLogo} className="logo" alt="Vite logo" /> | ||
| </a> | ||
| <a href="https://react.dev" target="_blank"> | ||
| <img src={reactLogo} className="logo react" alt="React logo" /> | ||
| </a> | ||
| <div className="main"> | ||
| <div className="header"> | ||
| {[0, 1, 2, 3, 4].map(key => ( | ||
| <button | ||
| key={key} | ||
| onClick={() => toggleContent(key)} | ||
| className={activeButton === key ? 'active':''} | ||
| > | ||
| {['Base Info', 'Current Sheet Fields Meta', 'Sheets Meta List', 'Current Sheet View Meta', 'Demo Component'][key]} | ||
| </button> | ||
| ))} | ||
| </div> | ||
| <div className="card"> | ||
| {renderContent()} | ||
| </div> | ||
| </div> | ||
| <h1>Vite + React</h1> | ||
| <div className="card"> | ||
| <button onClick={() => setCount((count) => count + 1)}>count is {count}</button> | ||
| <p> | ||
| Edit <code>src/App.tsx</code> and save to test HMR | ||
| </p> | ||
| </div> | ||
| <p className="read-the-docs">Click on the Vite and React logos to learn more</p> | ||
| </> | ||
@@ -52,0 +130,0 @@ ) |
+0
-6
@@ -28,3 +28,2 @@ :root { | ||
| display: flex; | ||
| place-items: center; | ||
| min-width: 320px; | ||
@@ -53,7 +52,2 @@ min-height: 100vh; | ||
| } | ||
| button:focus, | ||
| button:focus-visible { | ||
| outline: 4px auto -webkit-focus-ring-color; | ||
| } | ||
| @media (prefers-color-scheme: light) { | ||
@@ -60,0 +54,0 @@ :root { |
+0
-2
@@ -1,2 +0,1 @@ | ||
| // import React from "react" | ||
| import ReactDOM from "react-dom/client" | ||
@@ -6,4 +5,3 @@ import App from "./App.tsx" | ||
| // TODO: add StrictMode to release | ||
| ReactDOM.createRoot(document.getElementById("root")!).render(<App />) |
| import { expect, test, } from 'vitest' | ||
| import { render } from "@testing-library/react"; | ||
| import React from 'react'; | ||
| import App from "../App"; | ||
| test('demo', () => { | ||
| expect(true).toBe(true) | ||
| }) | ||
| test('toUpperCase', () => { | ||
| const result = ('foobar') | ||
| expect(result).toMatchInlineSnapshot(`"foobar"`) | ||
| }) | ||
| test('render app', () => { | ||
| const app = render(<App />) | ||
| expect(app).toMatchFileSnapshot('app.test.snapshot') | ||
| }) |
Sorry, the diff of this file is not supported yet
| import { expect, test } from 'vitest' | ||
| import { sum } from '../sum' | ||
| test('adds 1 + 2 to equal 3', () => { | ||
| expect(sum(1, 2)).toBe(3) | ||
| }) |
-42
| #root { | ||
| max-width: 1280px; | ||
| margin: 0 auto; | ||
| padding: 2rem; | ||
| text-align: center; | ||
| } | ||
| .logo { | ||
| height: 6em; | ||
| padding: 1.5em; | ||
| will-change: filter; | ||
| transition: filter 300ms; | ||
| } | ||
| .logo:hover { | ||
| filter: drop-shadow(0 0 2em #646cffaa); | ||
| } | ||
| .logo.react:hover { | ||
| filter: drop-shadow(0 0 2em #61dafbaa); | ||
| } | ||
| @keyframes logo-spin { | ||
| from { | ||
| transform: rotate(0deg); | ||
| } | ||
| to { | ||
| transform: rotate(360deg); | ||
| } | ||
| } | ||
| @media (prefers-reduced-motion: no-preference) { | ||
| a:nth-of-type(2) .logo { | ||
| animation: logo-spin infinite 20s linear; | ||
| } | ||
| } | ||
| .card { | ||
| padding: 2em; | ||
| } | ||
| .read-the-docs { | ||
| color: #888; | ||
| } |
| export function sum(a: number, b: number) { | ||
| return a + b | ||
| } |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
26944
73.44%25
78.57%595
166.82%1
-50%5
66.67%12
9.09%15
-68.75%1
Infinity%+ Added
+ Added
+ Added
+ Added