如何修改HTML默认上传按钮组件样式 - Bearalis

背景

系统自带的上传按钮有点丑,并且大的样式无法修改,想了个方法用Tailwind的按钮包装了系统自带的按钮,实现了样式的统一

解决方案

建立Uploadbutton 组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

import React, { ChangeEvent,useRef } from 'react';

import { Button } from '@/components/ui/button';
import { TreeDataItem } from '@/lib/utils';

interface Question {
...
}

interface Option {
...
}

type Props = {
disabled: boolean;
onUpload: ( result: any ) => void;
};

const UploadButton = ( { disabled, onUpload }: Props ) => {
const fileInputRef = useRef<HTMLInputElement | null>(null);

const handleButtonClick = () => {
if (fileInputRef.current) {
fileInputRef.current.click();
}
};

const parseQuestions = (content: string): { questions: Question[] } => {
...
};

const generateTreeData = ( questions: Question[]) : { data: TreeDataItem[] } => {
...
};

const handleFileChange = async (e: ChangeEvent<HTMLInputElement>) => {

const file = e.target.files?.[0];
if (!file) return;

try {
const content = await file.text();

const { questions } = parseQuestions(content);
const { data } = generateTreeData(questions);

const results = {
data: data,
jsondata: questions
};

onUpload( results );
} catch (error) {
console.error('Error processing file:', error);
}
}

return (
<div className="w-full lg:w-auto">
<input
type="file"
ref={fileInputRef}
accept=".txt"
onChange={handleFileChange}
className="hidden"
/>
<Button
disabled={disabled}
onClick={handleButtonClick}
size="sm"
className="w-full lg:w-auto"
>
Import TXT
</Button>
</div>
);
};

export default UploadButton;

分解来说,

  1. 定义组件,并定义onUpload 函数作为组件参数
  2. 定义 fileInputRef ,绑定给原始上传控件
  3. 再定义 handleButtonClick 函数,函数里调用上传控件的Click方法
  4. 定义Button控件,将点击事件绑定给handleButtonClick
  5. 定义handleFileChange函数,绑定原始上传控件的onChange事件
  6. 在handleFileChange函数,用 await file.text() 得到文件内容,并处理加工,把结果通过onUpload 函数,传输给外部调用者

作者:Bearalise
出处:如何修改HTML默认上传按钮组件样式 - Bearalis
版权:本文版权归作者所有
转载:欢迎转载,但未经作者同意,必须保留此段声明,必须在文章中给出原文链接。

PostgreSQL 在Windows环境下使用 Docker 安装 - Bearalise

背景

项目需要使用PG, 尝试安装一下

Create Volume

1
docker volume create --driver local --opt device=C:\10.VM\data\pg01 --opt type=none --opt o=bind pg01

Pull Image

1
docker pull postgres

Start Container

1
2
docker run --name pgsql --privileged -e POSTGRES_PASSWORD=xxxxx -p 15333:5432 -v pg01:/var/lib/postgresql/data -d postgres

连接数据库,创建用户

1
2
create user fintest01 with password 'xxxxxx';
create database fintest01 with owner fintest01;

切换数据库连接

1
DATABASE_URL=postgresql://fintest01:fin1234@192.xx.xx.xx:15333/fintest01
More...

Mac OS 下修改 Google Chrome 显示语言的方法

背景

由于调试程序需要,希望把Chrome的语言改成英文,发现在Mac下Chrome的语言是跟随系统语言的,通过菜单无法修改。

解决方案

英文 -> 简体中文

defaults write com.google.Chrome AppleLanguages '(zh-CN)'

简体中文 -> 英文

defaults write com.google.Chrome AppleLanguages '(en-US)'

英文优先,简体中文第二。反之改一下顺序

defaults write com.google.Chrome AppleLanguages "(en-US,zh-CN)"

Build a Finance SaaS Platform -26 (Account/Date Filter)(End)

Install Package

1
npm i query-string

Add Filter

Add components/filters.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
import React from 'react'
import { AccountFilter } from '@/components/account-filter'
import { DateFilter } from '@/components/date-filter'

export const Filters = () => {
return (
<div className="flex flex-col lg:flex-row items-center gap-y-2 lg:gap-y-0 lg:gap-x-2">
<AccountFilter />
<DateFilter />
</div>
)
}

More...

Build a Finance SaaS Platform -25 (Spending Pie)

Add Spending Pie Component

Add components/spending-pie.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115

import { useState } from "react";
import { FileSearch, Loader2, PieChart, Radar, Target } from "lucide-react";

import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"

import {
Card,
CardContent,
CardHeader,
CardTitle,
} from "@/components/ui/card";

import { PieVariant } from "@/components/pie-variant";
import { RadialVariant } from "@/components/radial-variant";
import { RadarVariant } from "./radar-variant";
import { Skeleton } from "./ui/skeleton";


type Props = {
data?: {
name: string;
value: number;
}[];
};

export const SpendingPie = ( { data = []} : Props) => {
const [chartType, setChartType] = useState("pie");

const onTypeChange = ( type: string) => {
// TODO: Add paywall
setChartType(type);
}
return(
<Card className="border-none drop-shadow-sm">
<CardHeader className="flex space-y-2 lg:space-y-0 lg:flex-row lg:items-center justify-between">
<CardTitle className="text-xl line-clamp-1">
Categories
</CardTitle>
<Select
defaultValue={chartType}
onValueChange={onTypeChange}
>
<SelectTrigger className="lg:w-auto h-9 rounded-md px-3">
<SelectValue placeholder="Chart type" />
</SelectTrigger>
<SelectContent>
<SelectItem value="pie">
<div className="flex items-center">
<PieChart className="size-4 mr-2 shrink-0" />
<p className="line-clamp-1">
Pie chart
</p>
</div>
</SelectItem>
<SelectItem value="radar">
<div className="flex items-center">
<Radar className="size-4 mr-2 shrink-0" />
<p className="line-clamp-1">
Radar chart
</p>
</div>
</SelectItem>
<SelectItem value="radial">
<div className="flex items-center">
<Target className="size-4 mr-2 shrink-0" />
<p className="line-clamp-1">
Radial chart
</p>
</div>
</SelectItem>
</SelectContent>
</Select>
</CardHeader>
<CardContent>
{data.length === 0 ? (
<div className="felx flex-col gap-y-4 items-center justify-center h-[350px] w-full">
<FileSearch className="size-6 text-muted-foreground" />
<p className="text-muted-foreground text-sm">
No data for this period
</p>
</div>
) : (
<>
{ chartType === "pie" && <PieVariant data={data} /> }
{ chartType === "radar" && <RadarVariant data={data} /> }
{ chartType === "radial" && <RadialVariant data={data} /> }
</>
)}
</CardContent>
</Card>
)
}

export const SpendingPieLoading =() => {
return (
<Card className="border-none drop-shadow-sm">
<CardHeader className="flex space-y-2 lg:space-y-0 lg:flex-row lg:items-center justify-between">
<Skeleton className="h-8 w-48" />
<Skeleton className="h-8 lg:w-[120px] w-full" />
</CardHeader>
<CardContent>
<div className="h-[350px] w-full flex items-center justify-center">
<Loader2 className="h-6 w-6 text-slate-300 animate-spin"/>
</div>
</CardContent>
</Card>
);
};
More...

Build a Finance SaaS Platform -22 (Seed Function)

Add Seed Function

Add scripts/seed.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import { config } from "dotenv";
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import { eachDayOfInterval,format, subDays } from "date-fns";

import { categories,accounts,transactions } from "@/db/schema";
import { convertAmountToMilliunits } from "@/lib/utils";

config({ path: ".env.local"});

const sql = postgres(process.env.DATABASE_URL!);
const db = drizzle(sql);

const SEED_USER_ID = "user_2krYMtZimxyJTWl7HDsA1FrAwYC";
const SEED_CATEGORIES = [
{ id: "category_1", name: "Food", userId: SEED_USER_ID, plaidId: null },
{ id: "category_2", name: "Transportation", userId: SEED_USER_ID, plaidId: null },
{ id: "category_3", name: "Utilities", userId: SEED_USER_ID, plaidId: null },
{ id: "category_4", name: "Entertainment", userId: SEED_USER_ID, plaidId: null },
{ id: "category_5", name: "Clothing", userId: SEED_USER_ID, plaidId: null },
{ id: "category_6", name: "Miscellaneous", userId: SEED_USER_ID, plaidId: null },
{ id: "category_7", name: "Rent", userId: SEED_USER_ID, plaidId: null },
];
const SEED_TRANSACTIONS: typeof transactions.$inferInsert[] = [];
const SEED_ACCOUNTS = [
{ id: "account_1", name: "Cash", userId: SEED_USER_ID, plaidId: null},
{ id: "account_2", name: "Saving", userId: SEED_USER_ID, plaidId: null},
];

const defaultTo = new Date();
const defaultFrom = subDays(defaultTo, 90);

const generateRandomAmount = (category: typeof categories.$inferInsert) => {
switch( category.name){
case "Transportation":
case "Miscellaneous":
return Math.random()* 50 + 15;
case "Utilities":
return Math.random()* 200 + 10;
case "Food":
return Math.random()* 30 + 10;
case "Entertainment":
case "Clothing":
return Math.random()* 100 + 10;
case "Rent":
return Math.random()* 400 + 90;
default:
return Math.random()* 50 + 10;
}
}

const generateTransactionsForDay = (day: Date) => {
const numTransactions = Math.floor(Math.random()*9) + 1 // 1 to 9 transactions per day

for(let i = 0; i < numTransactions; i++) {
const category = SEED_CATEGORIES[Math.floor(Math.random()* SEED_CATEGORIES.length)];
const isExpense = Math.random() > 0.6; // 60% chance of being an expense
const amount = generateRandomAmount(category);
const formattedAmount = convertAmountToMilliunits(isExpense ? -amount : amount);

//console.log(i + " " + formattedAmount);

SEED_TRANSACTIONS.push({
id: `transaction_${format(day,"yyyy-MM-dd")}_${i}`,
accountId: SEED_ACCOUNTS[0].id, //Always use first Account
categoryId: category.id,
date: day,
amount: formattedAmount,
payee: "Merchant",
notes: "Random transaction",
})
}
};

const generateTransactions = () => {
const days = eachDayOfInterval( { start: defaultFrom, end: defaultTo });
days.forEach( day => generateTransactionsForDay(day));
}

generateTransactions();

const main = async () =>{
try {
// Reset database
await db.delete(transactions).execute();
await db.delete(accounts).execute();
await db.delete(categories).execute();
// Seed categories
await db.insert(categories).values(SEED_CATEGORIES).execute();
// Seed accounts
await db.insert(accounts).values(SEED_ACCOUNTS).execute();
// Seed transactions
await db.insert(transactions).values(SEED_TRANSACTIONS).execute();

} catch (error) {
console.error("Error during seed:", error);
process.exit(1)
};
};

main();

Add Seed Command:

Modify package.json

1
2
3
4
5
"scripts": {
...
"db:seed": "tsx ./scripts/seed.ts",
...
}

Run Seed

1
npm run db:seed

请我喝杯咖啡吧~

支付宝
微信