增加前端格式检查配置

This commit is contained in:
yml2213
2026-06-08 06:40:33 +08:00
parent 79ea3a476c
commit 500f511185
150 changed files with 7254 additions and 4013 deletions
@@ -3,7 +3,12 @@ import { Search } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { computed, reactive, ref } from 'vue'
import { fetchAdminListings, type AdminListingPage, type AdminListingQuery, type Listing } from '@/features/listings'
import {
fetchAdminListings,
type AdminListingPage,
type AdminListingQuery,
type Listing,
} from '@/features/listings'
import { useAdminTable } from '@/features/admin/composables/useAdminTable'
import { useMoney } from '@/shared/composables/useMoney'
import {
@@ -29,7 +34,11 @@ const filters = reactive<AdminListingQuery>({
const pageSize = ref(10)
const currentPage = ref(1)
const { loading, data: listingPage, load: loadListings } = useAdminTable<AdminListingPage>({
const {
loading,
data: listingPage,
load: loadListings,
} = useAdminTable<AdminListingPage>({
fetchFn: () =>
fetchAdminListings({
...filters,
@@ -46,9 +55,13 @@ const { loading, data: listingPage, load: loadListings } = useAdminTable<AdminLi
const listings = computed(() => listingPage.value.items)
const totalListings = computed(() => listingPage.value.total)
const publishedCount = computed(() => listings.value.filter((item) => item.status === 'published').length)
const rentedCount = computed(() => listings.value.filter((item) => item.status === 'rented').length)
const pendingCount = computed(() => listings.value.filter((item) => item.review_status === 'pending').length)
const publishedCount = computed(
() => listings.value.filter(item => item.status === 'published').length
)
const rentedCount = computed(() => listings.value.filter(item => item.status === 'rented').length)
const pendingCount = computed(
() => listings.value.filter(item => item.review_status === 'pending').length
)
const totalPages = computed(() => Math.max(1, Math.ceil(totalListings.value / pageSize.value)))
const screenshotColumns = [
@@ -65,10 +78,18 @@ const screenshotColumns = [
{ label: '六头', width: 50, read: (row: Listing) => resourceText(row, 'helmet6') },
{ label: '六甲', width: 50, read: (row: Listing) => resourceText(row, 'armor6') },
{ label: '特殊刀皮', width: 110, read: (row: Listing) => skinGroupText(row, 'melee') },
{ label: '人物红皮/人物金皮/武器皮肤', width: 420, read: (row: Listing) => characterAndWeaponSkinText(row) },
{
label: '人物红皮/人物金皮/武器皮肤',
width: 420,
read: (row: Listing) => characterAndWeaponSkinText(row),
},
{ label: '租金/押金', width: 96, read: (row: Listing) => rentAndDepositText(row) },
{ label: '比例', width: 64, read: (row: Listing) => formatRatio(row) },
{ label: '租期', width: 92, read: (row: Listing) => `${formatEstimatedRentalDuration(row)}\n日耗 ${getDailyLoss(row)}` },
{
label: '租期',
width: 92,
read: (row: Listing) => `${formatEstimatedRentalDuration(row)}\n日耗 ${getDailyLoss(row)}`,
},
] as const
async function queryListings() {
@@ -90,7 +111,9 @@ function setStatusFilter(status: string) {
}
function setReviewStatusFilter(reviewStatus: string) {
filters.review_status = (filters.review_status === reviewStatus ? '' : reviewStatus) as AdminListingQuery['review_status']
filters.review_status = (
filters.review_status === reviewStatus ? '' : reviewStatus
) as AdminListingQuery['review_status']
filters.status = ''
void queryListings()
}
@@ -143,7 +166,7 @@ async function downloadTableScreenshot() {
async function createTableScreenshotBlob() {
if (!listings.value.length) throw new Error('当前页暂无可截图数据')
const canvas = renderTableScreenshotCanvas(listings.value)
const blob = await new Promise<Blob | null>((resolve) => canvas.toBlob(resolve, 'image/png'))
const blob = await new Promise<Blob | null>(resolve => canvas.toBlob(resolve, 'image/png'))
if (!blob) throw new Error('截图生成失败')
return blob
}
@@ -165,14 +188,17 @@ function renderTableScreenshotCanvas(rows: Listing[]) {
if (!measureCtx) throw new Error('截图画布初始化失败')
measureCtx.font = '13px Arial, "Microsoft YaHei", sans-serif'
const rowLines = rows.map((row) =>
screenshotColumns.map((column) => wrapCanvasText(measureCtx, column.read(row), column.width - cellPaddingX * 2)),
const rowLines = rows.map(row =>
screenshotColumns.map(column =>
wrapCanvasText(measureCtx, column.read(row), column.width - cellPaddingX * 2)
)
)
const rowHeights = rowLines.map((lineGroups) => {
const maxLines = Math.max(...lineGroups.map((lines) => lines.length), 1)
const rowHeights = rowLines.map(lineGroups => {
const maxLines = Math.max(...lineGroups.map(lines => lines.length), 1)
return Math.max(44, maxLines * lineHeight + cellPaddingY * 2)
})
const canvasHeight = paddingY * 2 + titleHeight + headerHeight + rowHeights.reduce((sum, height) => sum + height, 0)
const canvasHeight =
paddingY * 2 + titleHeight + headerHeight + rowHeights.reduce((sum, height) => sum + height, 0)
const canvas = document.createElement('canvas')
canvas.width = Math.round(canvasWidth * ratio)
canvas.height = Math.round(canvasHeight * ratio)
@@ -189,7 +215,11 @@ function renderTableScreenshotCanvas(rows: Listing[]) {
ctx.fillText('商品管理', paddingX, paddingY + 20)
ctx.fillStyle = '#8f9bba'
ctx.font = '12px Arial, "Microsoft YaHei", sans-serif'
ctx.fillText(`当前页 ${rows.length} 条 / 查询结果 ${totalListings.value}`, paddingX, paddingY + 40)
ctx.fillText(
`当前页 ${rows.length} 条 / 查询结果 ${totalListings.value}`,
paddingX,
paddingY + 40
)
let y = paddingY + titleHeight
drawRect(ctx, paddingX, y, tableWidth, headerHeight, '#f8f9fe')
@@ -211,7 +241,10 @@ function renderTableScreenshotCanvas(rows: Listing[]) {
for (const [columnIndex, column] of screenshotColumns.entries()) {
const lines = rowLines[rowIndex]?.[columnIndex] ?? ['-']
ctx.fillStyle = columnIndex === 0 ? '#4b5563' : '#3f4654'
ctx.font = columnIndex === 0 ? '700 13px Arial, "Microsoft YaHei", sans-serif' : '13px Arial, "Microsoft YaHei", sans-serif'
ctx.font =
columnIndex === 0
? '700 13px Arial, "Microsoft YaHei", sans-serif'
: '13px Arial, "Microsoft YaHei", sans-serif'
drawCellText(ctx, lines, x + cellPaddingX, y + cellPaddingY + 14, lineHeight)
x += column.width
}
@@ -240,18 +273,37 @@ function wrapCanvasText(ctx: CanvasRenderingContext2D, text: string, maxWidth: n
return lines
}
function drawCellText(ctx: CanvasRenderingContext2D, lines: string[], x: number, y: number, lineHeight: number) {
function drawCellText(
ctx: CanvasRenderingContext2D,
lines: string[],
x: number,
y: number,
lineHeight: number
) {
lines.forEach((line, index) => {
ctx.fillText(line, x, y + index * lineHeight)
})
}
function drawRect(ctx: CanvasRenderingContext2D, x: number, y: number, width: number, height: number, color: string) {
function drawRect(
ctx: CanvasRenderingContext2D,
x: number,
y: number,
width: number,
height: number,
color: string
) {
ctx.fillStyle = color
ctx.fillRect(x, y, width, height)
}
function drawLine(ctx: CanvasRenderingContext2D, startX: number, startY: number, endX: number, endY: number) {
function drawLine(
ctx: CanvasRenderingContext2D,
startX: number,
startY: number,
endX: number,
endY: number
) {
ctx.strokeStyle = '#e6eaf2'
ctx.lineWidth = 1
ctx.beginPath()
@@ -311,7 +363,7 @@ function characterAndWeaponSkinText(row: Listing) {
{ label: '武器', key: 'weapon' },
]
const parts = groups
.map((group) => {
.map(group => {
const names = getSkinGroup(row, group.key)
return names.length ? `${group.label}${names.join('、')}` : ''
})
@@ -421,9 +473,13 @@ function formatQuantity(value: number) {
</el-form>
<div class="listing-action-strip">
<el-button @click="resetFilters">重置</el-button>
<el-button type="primary" :icon="Search" :loading="loading" @click="queryListings">查询</el-button>
<el-button type="primary" :icon="Search" :loading="loading" @click="queryListings"
>查询</el-button
>
<el-button :disabled="!listings.length" @click="copyTableScreenshot">复制截图</el-button>
<el-button :disabled="!listings.length" @click="downloadTableScreenshot">下载截图</el-button>
<el-button :disabled="!listings.length" @click="downloadTableScreenshot"
>下载截图</el-button
>
</div>
</div>
</div>
@@ -504,7 +560,9 @@ function formatQuantity(value: number) {
</el-table>
<div class="table-pagination">
<span class="pagination-summary">当前页 {{ listings.length }} {{ totalListings }} </span>
<span class="pagination-summary"
>当前页 {{ listings.length }} {{ totalListings }} </span
>
<div class="pagination-controls">
<span class="pagination-size-label">每页</span>
<el-select
@@ -520,7 +578,9 @@ function formatQuantity(value: number) {
</el-select>
<span class="pagination-page">{{ currentPage }}/{{ totalPages }}</span>
<el-button size="small" :disabled="currentPage <= 1" @click="prevPage">上页</el-button>
<el-button size="small" :disabled="currentPage >= totalPages" @click="nextPage">下页</el-button>
<el-button size="small" :disabled="currentPage >= totalPages" @click="nextPage"
>下页</el-button
>
</div>
</div>
</section>