feat(add): 新增多页面新增和编辑表单功能

- 添加编辑和新增收货地址页面,支持表单数据加载和提交
- 新增应用密钥凭证、新增应用操作动态、新增应用成员、新增应用版本页面配置
- 实现文章新增及编辑页面,包含图片上传及多种文章属性配置
- 增加注册会员页面,支持头像上传、手机号获取和邀请人关系处理
- 引入统一表单提交成功和失败处理,支持编辑模式数据回显
- 配置统一eslint和editorconfig规则,增强代码规范和编辑体验
- 新增.gitignore规则,屏蔽无关文件和目录,优化版本管理
This commit is contained in:
2026-04-11 12:22:29 +08:00
commit 07f5c92f4b
627 changed files with 85725 additions and 0 deletions
+101
View File
@@ -0,0 +1,101 @@
import request from '@/utils/request';
import type { ApiResult, PageResult } from '@/api/index';
import type { AppCredential, AppCredentialParam } from './model';
/**
* 分页查询应用密钥凭证
*/
export async function pageAppCredential(params: AppCredentialParam) {
const res = await request.get<ApiResult<PageResult<AppCredential>>>(
'/app/app-credential/page',
params
);
if (res.code === 0) {
return res.data;
}
return Promise.reject(new Error(res.message));
}
/**
* 查询应用密钥凭证列表
*/
export async function listAppCredential(params?: AppCredentialParam) {
const res = await request.get<ApiResult<AppCredential[]>>(
'/app/app-credential',
params
);
if (res.code === 0 && res.data) {
return res.data;
}
return Promise.reject(new Error(res.message));
}
/**
* 添加应用密钥凭证
*/
export async function addAppCredential(data: AppCredential) {
const res = await request.post<ApiResult<unknown>>(
'/app/app-credential',
data
);
if (res.code === 0) {
return res.message;
}
return Promise.reject(new Error(res.message));
}
/**
* 修改应用密钥凭证
*/
export async function updateAppCredential(data: AppCredential) {
const res = await request.put<ApiResult<unknown>>(
'/app/app-credential',
data
);
if (res.code === 0) {
return res.message;
}
return Promise.reject(new Error(res.message));
}
/**
* 删除应用密钥凭证
*/
export async function removeAppCredential(id?: number) {
const res = await request.del<ApiResult<unknown>>(
'/app/app-credential/' + id
);
if (res.code === 0) {
return res.message;
}
return Promise.reject(new Error(res.message));
}
/**
* 批量删除应用密钥凭证
*/
export async function removeBatchAppCredential(data: (number | undefined)[]) {
const res = await request.del<ApiResult<unknown>>(
'/app/app-credential/batch',
{
data
}
);
if (res.code === 0) {
return res.message;
}
return Promise.reject(new Error(res.message));
}
/**
* 根据id查询应用密钥凭证
*/
export async function getAppCredential(id: number) {
const res = await request.get<ApiResult<AppCredential>>(
'/app/app-credential/' + id
);
if (res.code === 0 && res.data) {
return res.data;
}
return Promise.reject(new Error(res.message));
}
+49
View File
@@ -0,0 +1,49 @@
import type { PageParam } from '@/api/index';
/**
* 应用密钥凭证
*/
export interface AppCredential {
// 自增ID
id?: string;
// 关联应用ID
websiteId?: string;
// 凭证名称,如生产环境密钥
name?: string;
// App ID(公开)
appId?: string;
// App Secret(加密存储)
appSecret?: string;
// 凭证类型: server/client/webhook
type?: string;
// 权限范围,空格分隔
scopes?: string;
// 到期时间,NULL=永不过期
expireTime?: string;
// 最后使用时间
lastUsedAt?: string;
//
remark?: string;
// 排序(数字越小越靠前)
sortNumber?: number;
// 状态, 0正常, 1冻结
status?: number;
// 是否删除, 0否, 1是
deleted?: number;
// 用户ID
userId?: number;
// 租户id
tenantId?: number;
// 创建时间
createTime?: string;
// 修改时间
updateTime?: string;
}
/**
* 应用密钥凭证搜索条件
*/
export interface AppCredentialParam extends PageParam {
id?: number;
keywords?: string;
}
+101
View File
@@ -0,0 +1,101 @@
import request from '@/utils/request';
import type { ApiResult, PageResult } from '@/api/index';
import type { AppEvent, AppEventParam } from './model';
/**
* 分页查询应用操作动态
*/
export async function pageAppEvent(params: AppEventParam) {
const res = await request.get<ApiResult<PageResult<AppEvent>>>(
'/app/app-event/page',
params
);
if (res.code === 0) {
return res.data;
}
return Promise.reject(new Error(res.message));
}
/**
* 查询应用操作动态列表
*/
export async function listAppEvent(params?: AppEventParam) {
const res = await request.get<ApiResult<AppEvent[]>>(
'/app/app-event',
params
);
if (res.code === 0 && res.data) {
return res.data;
}
return Promise.reject(new Error(res.message));
}
/**
* 添加应用操作动态
*/
export async function addAppEvent(data: AppEvent) {
const res = await request.post<ApiResult<unknown>>(
'/app/app-event',
data
);
if (res.code === 0) {
return res.message;
}
return Promise.reject(new Error(res.message));
}
/**
* 修改应用操作动态
*/
export async function updateAppEvent(data: AppEvent) {
const res = await request.put<ApiResult<unknown>>(
'/app/app-event',
data
);
if (res.code === 0) {
return res.message;
}
return Promise.reject(new Error(res.message));
}
/**
* 删除应用操作动态
*/
export async function removeAppEvent(id?: number) {
const res = await request.del<ApiResult<unknown>>(
'/app/app-event/' + id
);
if (res.code === 0) {
return res.message;
}
return Promise.reject(new Error(res.message));
}
/**
* 批量删除应用操作动态
*/
export async function removeBatchAppEvent(data: (number | undefined)[]) {
const res = await request.del<ApiResult<unknown>>(
'/app/app-event/batch',
{
data
}
);
if (res.code === 0) {
return res.message;
}
return Promise.reject(new Error(res.message));
}
/**
* 根据id查询应用操作动态
*/
export async function getAppEvent(id: number) {
const res = await request.get<ApiResult<AppEvent>>(
'/app/app-event/' + id
);
if (res.code === 0 && res.data) {
return res.data;
}
return Promise.reject(new Error(res.message));
}
+47
View File
@@ -0,0 +1,47 @@
import type { PageParam } from '@/api/index';
/**
* 应用操作动态
*/
export interface AppEvent {
// 自增ID
id?: string;
// 关联应用ID
websiteId?: string;
// 事件类型: created/published/updated/domain_bound/member_added/status_changed
eventType?: string;
// 事件标题,如已发布
title?: string;
// 详细描述
content?: string;
// 操作人用户ID
operatorId?: string;
// 操作人名称(冗余)
operator?: string;
// 关联ID,如版本ID
refId?: string;
// 关联类型
refType?: string;
// 扩展数据
extra?: string;
// 排序(数字越小越靠前)
sortNumber?: number;
// 状态, 0正常, 1冻结
status?: number;
// 用户ID
userId?: number;
// 租户id
tenantId?: number;
// 创建时间
createTime?: string;
// 修改时间
updateTime?: string;
}
/**
* 应用操作动态搜索条件
*/
export interface AppEventParam extends PageParam {
id?: number;
keywords?: string;
}
+19
View File
@@ -0,0 +1,19 @@
import request from '@/utils/request';
import type { ApiResult, PageResult } from '@/api';
import type { AppProduct } from './model';
const BASE = '/app/product';
/** 分页查询我的应用 */
export async function pageMyApps(params?: { current?: number; size?: number }) {
const res = await request.get<ApiResult<PageResult<AppProduct>>>(BASE + '/my/page', { params });
if (res.code === 0) return res.data;
return Promise.reject(new Error(res.message));
}
/** 获取我参与的应用列表 */
export async function listJoinedApps(params?: { page?: number; limit?: number }) {
const res = await request.get<ApiResult<PageResult<AppProduct>>>(BASE + '/page', { params });
if (res.code === 0) return res.data;
return Promise.reject(new Error(res.message));
}
+51
View File
@@ -0,0 +1,51 @@
export interface AppProduct {
productId?: number
productName?: string
productCode?: string
appType?: number
icon?: string
qrcode?: string
description?: string
domain?: string
homeUrl?: string
adminUrl?: string
version?: string
status?: number
statusText?: string
running?: number
expirationTime?: string
createTime?: string
updateTime?: string
publishStatus?: string
}
export const APP_TYPE_NAME: Record<number, string> = {
10: '网站',
20: '微信小程序',
30: '抖音小程序',
40: '百度小程序',
50: '支付宝小程序',
60: 'Android APP',
70: 'iOS APP',
80: 'macOS 应用',
90: 'Windows 应用',
100: '插件',
}
export const STATUS_NAME: Record<number, string> = {
0: '未开通',
1: '运行中',
2: '维护中',
3: '已关闭',
4: '已欠费',
5: '违规停机',
}
export const STATUS_COLOR: Record<number, string> = {
0: '#6b7280',
1: '#10b981',
2: '#f59e0b',
3: '#ef4444',
4: '#ef4444',
5: '#ef4444',
}
+101
View File
@@ -0,0 +1,101 @@
import request from '@/utils/request';
import type { ApiResult, PageResult } from '@/api/index';
import type { AppUser, AppUserParam } from './model';
/**
* 分页查询应用成员
*/
export async function pageAppUser(params: AppUserParam) {
const res = await request.get<ApiResult<PageResult<AppUser>>>(
'/app/app-user/page',
params
);
if (res.code === 0) {
return res.data;
}
return Promise.reject(new Error(res.message));
}
/**
* 查询应用成员列表
*/
export async function listAppUser(params?: AppUserParam) {
const res = await request.get<ApiResult<AppUser[]>>(
'/app/app-user',
params
);
if (res.code === 0 && res.data) {
return res.data;
}
return Promise.reject(new Error(res.message));
}
/**
* 添加应用成员
*/
export async function addAppUser(data: AppUser) {
const res = await request.post<ApiResult<unknown>>(
'/app/app-user',
data
);
if (res.code === 0) {
return res.message;
}
return Promise.reject(new Error(res.message));
}
/**
* 修改应用成员
*/
export async function updateAppUser(data: AppUser) {
const res = await request.put<ApiResult<unknown>>(
'/app/app-user',
data
);
if (res.code === 0) {
return res.message;
}
return Promise.reject(new Error(res.message));
}
/**
* 删除应用成员
*/
export async function removeAppUser(id?: number) {
const res = await request.del<ApiResult<unknown>>(
'/app/app-user/' + id
);
if (res.code === 0) {
return res.message;
}
return Promise.reject(new Error(res.message));
}
/**
* 批量删除应用成员
*/
export async function removeBatchAppUser(data: (number | undefined)[]) {
const res = await request.del<ApiResult<unknown>>(
'/app/app-user/batch',
{
data
}
);
if (res.code === 0) {
return res.message;
}
return Promise.reject(new Error(res.message));
}
/**
* 根据id查询应用成员
*/
export async function getAppUser(id: number) {
const res = await request.get<ApiResult<AppUser>>(
'/app/app-user/' + id
);
if (res.code === 0 && res.data) {
return res.data;
}
return Promise.reject(new Error(res.message));
}
+41
View File
@@ -0,0 +1,41 @@
import type { PageParam } from '@/api/index';
/**
* 应用成员
*/
export interface AppUser {
// 自增ID
id?: string;
// 关联应用ID
websiteId?: string;
// 用户ID
userId?: string;
// 用户名(冗余)
username?: string;
// 头像(冗余)
avatar?: string;
// 角色: owner/admin/developer/viewer
role?: string;
// 邀请人用户ID
inviteBy?: string;
// 加入时间
inviteTime?: string;
// 排序(数字越小越靠前)
sortNumber?: number;
// 状态, 0正常, 1冻结
status?: number;
// 租户id
tenantId?: number;
// 创建时间
createTime?: string;
// 修改时间
updateTime?: string;
}
/**
* 应用成员搜索条件
*/
export interface AppUserParam extends PageParam {
id?: number;
keywords?: string;
}
+101
View File
@@ -0,0 +1,101 @@
import request from '@/utils/request';
import type { ApiResult, PageResult } from '@/api/index';
import type { AppVersion, AppVersionParam } from './model';
/**
* 分页查询应用版本发布记录
*/
export async function pageAppVersion(params: AppVersionParam) {
const res = await request.get<ApiResult<PageResult<AppVersion>>>(
'/app/app-version/page',
params
);
if (res.code === 0) {
return res.data;
}
return Promise.reject(new Error(res.message));
}
/**
* 查询应用版本发布记录列表
*/
export async function listAppVersion(params?: AppVersionParam) {
const res = await request.get<ApiResult<AppVersion[]>>(
'/app/app-version',
params
);
if (res.code === 0 && res.data) {
return res.data;
}
return Promise.reject(new Error(res.message));
}
/**
* 添加应用版本发布记录
*/
export async function addAppVersion(data: AppVersion) {
const res = await request.post<ApiResult<unknown>>(
'/app/app-version',
data
);
if (res.code === 0) {
return res.message;
}
return Promise.reject(new Error(res.message));
}
/**
* 修改应用版本发布记录
*/
export async function updateAppVersion(data: AppVersion) {
const res = await request.put<ApiResult<unknown>>(
'/app/app-version',
data
);
if (res.code === 0) {
return res.message;
}
return Promise.reject(new Error(res.message));
}
/**
* 删除应用版本发布记录
*/
export async function removeAppVersion(id?: number) {
const res = await request.del<ApiResult<unknown>>(
'/app/app-version/' + id
);
if (res.code === 0) {
return res.message;
}
return Promise.reject(new Error(res.message));
}
/**
* 批量删除应用版本发布记录
*/
export async function removeBatchAppVersion(data: (number | undefined)[]) {
const res = await request.del<ApiResult<unknown>>(
'/app/app-version/batch',
{
data
}
);
if (res.code === 0) {
return res.message;
}
return Promise.reject(new Error(res.message));
}
/**
* 根据id查询应用版本发布记录
*/
export async function getAppVersion(id: number) {
const res = await request.get<ApiResult<AppVersion>>(
'/app/app-version/' + id
);
if (res.code === 0 && res.data) {
return res.data;
}
return Promise.reject(new Error(res.message));
}
+53
View File
@@ -0,0 +1,53 @@
import type { PageParam } from '@/api/index';
/**
* 应用版本发布记录
*/
export interface AppVersion {
// 自增ID
id?: string;
// 关联应用ID
websiteId?: string;
// 版本号,如 1.0.0
versionNo?: string;
// 版本名称
versionName?: string;
// 版本更新说明
changelog?: string;
// 安装包地址
packageUrl?: string;
// 包大小(字节)
packageSize?: string;
// 包MD5/SHA256
packageHash?: string;
// 环境: development/staging/production
env?: string;
// 状态 0=构建中 1=已发布 2=已回滚 3=构建失败
status?: number;
// 是否为当前版本
isCurrent?: string;
// 发布人用户ID
publishBy?: string;
// 发布时间
publishTime?: string;
// 备注
remark?: string;
// 排序(数字越小越靠前)
sortNumber?: number;
// 用户ID
userId?: number;
// 租户id
tenantId?: number;
// 创建时间
createTime?: string;
// 修改时间
updateTime?: string;
}
/**
* 应用版本发布记录搜索条件
*/
export interface AppVersionParam extends PageParam {
id?: number;
keywords?: string;
}
+47
View File
@@ -0,0 +1,47 @@
import request from '@/utils/request';
import type { ApiResult, PageResult } from '@/api';
import type { Notification, NotificationParam, UnreadCountResult } from './model';
const BASE = '/app/notification';
/** 分页查询通知列表 */
export async function pageNotification(params: NotificationParam) {
const res = await request.get<ApiResult<PageResult<Notification>>>(BASE + '/page', { params });
if (res.code === 0) return res.data;
return Promise.reject(new Error(res.message));
}
/** 查询最近通知 */
export async function listRecentNotification(params?: { type?: string; limit?: number }) {
const res = await request.get<ApiResult<Notification[]>>(BASE + '/recent', { params });
if (res.code === 0 && res.data) return res.data;
return Promise.reject(new Error(res.message));
}
/** 获取未读数量统计 */
export async function getUnreadCount() {
const res = await request.get<ApiResult<UnreadCountResult>>(BASE + '/unread-count');
if (res.code === 0 && res.data) return res.data;
return Promise.reject(new Error(res.message));
}
/** 标记单条通知为已读 */
export async function markNotificationRead(id: number) {
const res = await request.put<ApiResult<unknown>>(BASE + `/read/${id}`);
if (res.code === 0) return res.message;
return Promise.reject(new Error(res.message));
}
/** 标记全部已读 */
export async function markAllNotificationRead(data?: { type?: string }) {
const res = await request.put<ApiResult<unknown>>(BASE + '/read-all', data);
if (res.code === 0) return res.message;
return Promise.reject(new Error(res.message));
}
/** 删除单条通知 */
export async function removeNotification(id: number) {
const res = await request.del<ApiResult<unknown>>(BASE + '/' + id);
if (res.code === 0) return res.message;
return Promise.reject(new Error(res.message));
}
+41
View File
@@ -0,0 +1,41 @@
import type { PageParam } from '@/api/index';
/** 通知类型 */
export type NotificationType = 'ticket' | 'review' | 'system' | 'resource' | 'permission' | 'member' | 'payment';
/** 通知实体 */
export interface Notification {
id?: number;
userId?: number;
type?: NotificationType;
title?: string;
content?: string;
isRead?: number;
refId?: number;
refType?: string;
linkUrl?: string;
senderId?: number;
senderName?: string;
senderAvatar?: string;
tenantId?: number;
createTime?: string;
updateTime?: string;
}
/** 通知查询参数 */
export interface NotificationParam extends PageParam {
type?: NotificationType | '';
isRead?: number;
}
/** 未读统计 */
export interface UnreadCountResult {
total?: number;
ticket?: number;
review?: number;
system?: number;
resource?: number;
permission?: number;
member?: number;
payment?: number;
}
+54
View File
@@ -0,0 +1,54 @@
import request from '@/utils/request';
import type { ApiResult, PageResult } from '@/api';
import type { Ticket, TicketReply, TicketSubmitForm, TicketQueryParams, TicketStats } from './model';
const BASE = '/app/ticket';
/** 查询我的工单(分页) */
export async function myTickets(params: TicketQueryParams) {
const res = await request.get<ApiResult<PageResult<Ticket>>>(BASE + '/my', { params });
if (res.code === 0) return res.data;
return Promise.reject(new Error(res.message));
}
/** 提交工单 */
export async function submitTicket(data: TicketSubmitForm) {
const res = await request.post<ApiResult<Ticket>>(BASE + '/submit', data);
if (res.code === 0) return res.data;
return Promise.reject(new Error(res.message));
}
/** 获取工单详情 */
export async function getTicket(ticketId: number) {
const res = await request.get<ApiResult<Ticket>>(BASE + '/' + ticketId);
if (res.code === 0 && res.data) return res.data;
return Promise.reject(new Error(res.message));
}
/** 关闭工单 */
export async function closeTicket(ticketId: number) {
const res = await request.put<ApiResult<unknown>>(BASE + `/${ticketId}/close`);
if (res.code === 0) return res.message;
return Promise.reject(new Error(res.message));
}
/** 获取工单回复列表 */
export async function getTicketReplies(ticketId: number) {
const res = await request.get<ApiResult<TicketReply[]>>(BASE + `/${ticketId}/replies`);
if (res.code === 0 && res.data) return res.data;
return Promise.reject(new Error(res.message));
}
/** 提交工单回复 */
export async function replyTicket(data: { ticketId: number; content: string }) {
const res = await request.post<ApiResult<TicketReply>>(BASE + '/reply', data);
if (res.code === 0) return res.data;
return Promise.reject(new Error(res.message));
}
/** 获取工单统计 */
export async function getTicketStats() {
const res = await request.get<ApiResult<TicketStats>>(BASE + '/stats');
if (res.code === 0 && res.data) return res.data;
return Promise.reject(new Error(res.message));
}
+64
View File
@@ -0,0 +1,64 @@
export type TicketPriority = 'low' | 'normal' | 'high' | 'urgent'
export type TicketStatus = 'pending' | 'assigned' | 'processing' | 'resolved' | 'closed' | 'rejected'
export type TicketCategory = 'bug' | 'feature' | 'consultation' | 'complaint' | 'other'
export interface Ticket {
ticketId?: number
ticketNo?: string
title?: string
content?: string
productId?: number
productName?: string
category?: TicketCategory
priority?: TicketPriority
status?: TicketStatus
attachments?: string[]
submitUserId?: number
submitUserName?: string
submitUserAvatar?: string
assigneeId?: number
assigneeName?: string
assigneeAvatar?: string
createTime?: string
updateTime?: string
resolvedTime?: string
closedTime?: string
replyCount?: number
hasUnread?: boolean
}
export interface TicketReply {
replyId?: number
ticketId?: number
content?: string
attachments?: string[]
userId?: number
userName?: string
userAvatar?: string
isStaff?: boolean
createTime?: string
}
export interface TicketSubmitForm {
title: string
content: string
productId?: number
category: TicketCategory
priority: TicketPriority
}
export interface TicketQueryParams {
status?: TicketStatus
category?: TicketCategory
keywords?: string
page?: number
limit?: number
}
export interface TicketStats {
total?: number
pending?: number
processing?: number
resolved?: number
closed?: number
}