以下是「 豆包MarsCode 体验官」优秀文章,作者谦哥。
🚀 项目地址:remotejobs.justidea.cn/
🚀 项目截图:
数据处理
感谢开源项目:https://github.com/remoteintech/remote-jobs
网站信息获取:把项目里 md 文件里的网站列表抓取保存到 supabase 的数据库里(这一步可以在本地完成)
import fetch from 'node-fetch';
import { createClient } from '@supabase/supabase-js';
const SUPABASE_URL = '';
const SUPABASE_KEY = '';
const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);
async function fetchData() {
const response = await fetch('https://raw.githubusercontent.com/remoteintech/remote-jobs/main/README.md');
const text = await response.text();
// 解析 README.md 内容,提取 name, website, region
const jobs = parseJobs(text);
// 将数据存储到 Supabase
for (const job of jobs) {
await supabase
.from('remote-jobs')
.insert(job);
}
}
function parseJobs(mdText) {
const lines = mdText.split('\n');
const jobs = [];
lines.forEach(line => {
// 例子:'[Bitovi](/company-profiles/bitovi.md) | https://bitovi.com/ | Worldwide'
const match = line.match(/\[([^\]]+)\]\(([^)]+)\)\s*\|\s*(https?:\/\/[^\s|]+)\s*\|\s*(.+)/);
if (match) {
const [, name, ,website, region] = match;
jobs.push({ name, website, region });
}
});
return jobs;
}
fetchData();
网站截图: 把上一步获取到的网址用 puppeteer
循环截图,最后上传到 supabase
的 storage
里
import puppeteer from 'puppeteer'
import fs from 'fs'
import { createClient } from '@supabase/supabase-js'
const SUPABASE_URL =''
const SUPABASE_KEY =''
const supabase = createClient(SUPABASE_URL, SUPABASE_KEY)
async function captureScreenshots() {
const { data: jobs, error } = await supabase
.from('remote-jobs')
.select('id, website')
if (error) {
console.error('Error fetching jobs:', error)
return
}
for (const job of jobs) {
const { id, website } = job
const screenshotPath = `screenshots/${id}.png`
// 生成截图
try {
await generateScreenshot(website, screenshotPath)
// 上传截图到 Supabase 存储
const { data, error } = await supabase.storage
.from('remote-jobs')
.upload(screenshotPath, fs.readFileSync(screenshotPath))
// 更新数据库中的 thumb 字段
if (!error) {
const thumbUrl = data.path
await supabase
.from('remote-jobs')
.update({ thumb: thumbUrl })
.eq('id', id)
console.log(`Screenshot uploaded for job ${id}`)
}
} catch (error) {
console.log(error)
}
}
}
async function generateScreenshot(url, path) {
const browser = await puppeteer.launch()
const page = await browser.newPage()
try {
await page.goto(url, { waitUntil: 'networkidle2', timeout: 90000 })
await page.screenshot({ path })
} catch (error) {
console.error('Error generating screenshot:', error)
} finally {
await browser.close()
}
}
captureScreenshots()
Supabase 里的数据
豆包Marscode
一定要选用 marscode.com 来开发项目,不然没有部署这个选项
新建项目
安装依赖
pnpm add tailwindcss postcss autoprefixer
pnpm add @supabase/supabase-js
配置 tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
content: [
'./index.html',
'./src/**/*.{vue,js,ts,jsx,tsx}'
],
theme: {
extend: {},
},
plugins: [],
}
Vue组件JobList.vue
<template>
<div class="p-6">
<input
v-model="search"
placeholder="搜索公司名称"
class="border p-2 mb-4 w-full"
/>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<div v-for="job in paginatedJobs" :key="job.id" class="p-4 border rounded-lg shadow-md transition-transform transform hover:scale-105">
<h3 class="text-xl font-semibold">{{ job.name }}</h3>
<a :href="job.website" target="_blank" class="text-blue-500 text-xs">{{ job.website }}</a>
<p class="text-sm text-gray-500">{{ job.region }}</p>
<img v-if="job.thumb" :src="thumbUrl(job.thumb)" alt="thumbnail" class="mt-2 rounded-lg p-2" />
</div>
</div>
<div class="flex justify-between mt-4">
<button
@click="prevPage"
:disabled="currentPage === 1"
class="bg-blue-500 text-white px-4 py-2 rounded disabled:opacity-50">
上一页
</button>
<button
@click="nextPage"
:disabled="currentPage === totalPages"
class="bg-blue-500 text-white px-4 py-2 rounded disabled:opacity-50">
下一页
</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
jobs: [],
search: '',
currentPage: 1,
jobsPerPage: 20
};
},
computed: {
filteredJobs() {
return this.jobs.filter(job =>
job.name.includes(this.search) ||
job.region.includes(this.search)
);
},
totalPages() {
return Math.ceil(this.filteredJobs.length / this.jobsPerPage);
},
paginatedJobs() {
const start = (this.currentPage - 1) * this.jobsPerPage;
const end = start + this.jobsPerPage;
return this.filteredJobs.slice(start, end);
}
},
async mounted() {
const { data: jobs, error } = await this.$supabase
.from('remote-jobs')
.select('*');
if (!error) this.jobs = jobs;
},
methods: {
thumbUrl(path) {
const SUPABASE_URL = '';
//spuabase 的 storage 地址
return `${SUPABASE_URL}/storage/v1/object/public/remote-jobs/${path}`;
},
nextPage() {
if (this.currentPage < this.totalPages) {
this.currentPage++;
}
},
prevPage() {
if (this.currentPage > 1) {
this.currentPage--;
}
}
}
};
</script>
<style scoped>
input:focus {
outline: none;
border-color: #4299e1;
box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
}
</style>
部署
IDE 的这两个位置都可以部署项目,对于 vue
项目,系统会自动构建打包上传
部署过程
最后绑定自己的域名就可以了,没有并发限制,并且会自动部署 ssl
最后
IDE 可以后台直接用 AI 生成大部分的业务代码,也可以直接提问,大大缩短的一个项目上线的时间。
最重要的是豆包Marscode 和 supabase 都是免费的🤣,这个项目的目的只是测试搭建难度,希望这个想法对你有帮助。