Full-Stack Frontend Implementation of a Publishing Platform: Pagination, CRUD, and Vue‑Element‑Plus Integration
This tutorial walks through the front‑end portion of a publishing platform series, demonstrating how to build a Vue 3 and Element‑plus UI with TailwindCSS, implement server‑side pagination via Koa, integrate Axios with proxy and interceptors, and add complete CRUD operations—including create, edit, and delete dialogs—while showcasing the essential code snippets.
The article continues a series on building a full‑stack publishing platform, focusing on the front‑end implementation using Vue , Element‑plus , TailwindCSS and a Koa back‑end.
Core Points
Front‑end and back‑end data pagination display
Full‑stack CRUD functionality
1. Building the Static UI
Using Element‑plus components such as el-card , el-form , el-table and TailwindCSS classes, a static page layout is created. The following snippet shows the initial markup:
...2. Back‑End Pagination API
The Koa route implements a paginated query. The key function getConfigList extracts query parameters, calls the service layer, and returns the result.
export async function getConfigList(ctx, next) {
try {
const { pageNo: page, pageSize, projectName } = ctx.request.query;
const pageData = await services.findJobPage(page, pageSize, { projectName });
ctx.state.apiResponse = { code: RESPONSE_CODE.SUC, data: pageData };
} catch (e) {
ctx.state.apiResponse = { code: RESPONSE_CODE.ERR, msg: '配置分页查询失败' };
}
next();
}The service layer uses Mongoose to perform find , skip , limit , and count operations:
export async function findJobPage(page, pageSize, params) {
Object.keys(params).forEach(key => {
if (!params[key]) Reflect.deleteProperty(params, key);
});
const DocumentUserList = await JobModel.find(params)
.skip((page - 1) * pageSize)
.limit(pageSize);
return DocumentUserList.map(_ => _.toObject());
}
export function countJob(params) {
Object.keys(params).forEach(key => {
if (!params[key]) Reflect.deleteProperty(params, key);
});
return JobModel.count(params);
}3. Front‑End Integration
Axios is used to request the paginated data. A proxy is configured in vite.config to forward /api calls to the back‑end.
proxy: {
'/api': {
target: 'http://localhost:3200/', // 代理到后端地址
changeOrigin: true,
rewrite: path => {
return path.replace(/^/api/, '');
}
}
}An interceptor simplifies the response format:
axios.interceptors.response.use(function (response) {
const data = response.data;
if (data.code === 0) {
return data.data; // 业务层直接拿到数据
}
data.message = data.message || data.msg;
return Promise.reject(data);
});The request function used in the component:
export async function getConfig(params) {
return axios.get('/job', { params });
}4. CRUD Implementation
Create : A button opens an el-dialog with a form. The submit handler onSubmit calls postSave and refreshes the table.
const formData = reactive({
projectName: '',
gitUrl: '',
gitBranch: '',
buildCommand: '',
uploadPath: ''
});
const onSubmit = async () => {
try {
await postSave(formData);
ElMessage.success('配置保存成功');
await initData();
dialogVisible.value = false;
} catch (e) {
ElMessage.error('配置保存失败');
}
};Edit : Clicking the edit button fills the form with the selected row and sets isEdit . The same onSubmit decides whether to call postUpdate or postSave .
const onEdit = rowData => {
isEdit.value = true;
dialogVisible.value = true;
Object.keys(formData).forEach(key => {
formData[key] = rowData[key];
});
formData.id = rowData._id;
};
const onSubmit = async () => {
try {
isEdit.value ? await postUpdate(formData) : await postSave(formData);
ElMessage.success(isEdit.value ? '配置编辑成功' : '配置保存成功');
await initData();
dialogVisible.value = false;
} catch (e) {
ElMessage.error(isEdit.value ? '配置编辑失败' : '配置保存失败');
}
};Delete : An el-popconfirm wraps a delete button. The handler onDel calls postDelete with the record id.
const onDel = async rowData => {
try {
await postDelete({ id: rowData._id });
ElMessage.success('配置删除成功');
await initData();
} catch (e) {
ElMessage.error('配置删除失败');
}
};Conclusion
The article completes the front‑end pagination display and full CRUD cycle for the publishing platform. The next installment will cover triggering builds from the front‑end and streaming Jenkins logs via WebSocket.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.